From 5acc32898951247dd599c71a92d120fd8dd59c89 Mon Sep 17 00:00:00 2001 From: Michael Tuexen <tuexen@fh-muenster.de> Date: Sun, 28 Dec 2014 07:07:26 +0100 Subject: [PATCH] Add support for UDPLite (RFC 3828) This commit adds support for UDPLite as specified in RFC 3828 and the corresponding socket options. It is tested on FreeBSD 10.1 and Linux, the platforms supporting UDPLite. Signed-off-by: Michael Tuexen <tuexen@fh-muenster.de> --- gtests/net/packetdrill/Makefile.common | 3 +- gtests/net/packetdrill/checksum.c | 18 ++ gtests/net/packetdrill/checksum.h | 11 ++ gtests/net/packetdrill/checksum_test.c | 60 +++++++ .../net/packetdrill/contrib/packetdrill.vim | 2 +- gtests/net/packetdrill/header.h | 3 + gtests/net/packetdrill/icmp_packet.c | 14 +- gtests/net/packetdrill/icmp_packet.h | 3 +- gtests/net/packetdrill/lexer.l | 1 + gtests/net/packetdrill/netdev.c | 5 +- gtests/net/packetdrill/packet.c | 20 ++- gtests/net/packetdrill/packet.h | 70 +++++++- gtests/net/packetdrill/packet_checksum.c | 30 +++- gtests/net/packetdrill/packet_parser.c | 44 +++++ gtests/net/packetdrill/packet_parser.h | 2 +- gtests/net/packetdrill/packet_parser_test.c | 106 +++++++++++- gtests/net/packetdrill/packet_to_string.c | 24 ++- .../net/packetdrill/packet_to_string_test.c | 158 +++++++++++++++++- gtests/net/packetdrill/parser.y | 69 ++++++-- gtests/net/packetdrill/platforms.h | 12 +- gtests/net/packetdrill/run_packet.c | 58 +++++-- gtests/net/packetdrill/script.c | 1 + gtests/net/packetdrill/socket.h | 12 +- gtests/net/packetdrill/symbols_freebsd.c | 6 + gtests/net/packetdrill/symbols_linux.c | 4 + gtests/net/packetdrill/types.h | 5 + 26 files changed, 687 insertions(+), 54 deletions(-) diff --git a/gtests/net/packetdrill/Makefile.common b/gtests/net/packetdrill/Makefile.common index 0ec741f5..9ff5027c 100644 --- a/gtests/net/packetdrill/Makefile.common +++ b/gtests/net/packetdrill/Makefile.common @@ -19,7 +19,8 @@ packetdrill-lib := \ symbols_freebsd.o \ symbols_openbsd.o \ symbols_netbsd.o \ - gre_packet.o icmp_packet.o ip_packet.o tcp_packet.o udp_packet.o \ + gre_packet.o icmp_packet.o ip_packet.o \ + tcp_packet.o udp_packet.o udplite_packet.o \ mpls_packet.o \ run.o run_command.o run_packet.o run_system_call.o \ script.o socket.o system.o \ diff --git a/gtests/net/packetdrill/checksum.c b/gtests/net/packetdrill/checksum.c index b37dc00b..06ced251 100644 --- a/gtests/net/packetdrill/checksum.c +++ b/gtests/net/packetdrill/checksum.c @@ -99,6 +99,14 @@ __be16 tcp_udp_v4_checksum(struct in_addr src_ip, struct in_addr dst_ip, sum = ip_checksum_partial(payload, len, sum); return ip_checksum_fold(sum); } +__be16 udplite_v4_checksum(struct in_addr src_ip, struct in_addr dst_ip, + u8 protocol, const void *payload, u16 len, u16 cov) +{ + u64 sum = tcp_udp_v4_header_checksum_partial( + src_ip, dst_ip, protocol, len); + sum = ip_checksum_partial(payload, cov, sum); + return ip_checksum_fold(sum); +} /* Calculates and returns IPv4 header checksum. */ __be16 ipv4_checksum(void *ip_header, size_t ip_header_bytes) @@ -148,6 +156,16 @@ __be16 tcp_udp_v6_checksum(const struct in6_addr *src_ip, return ip_checksum_fold(sum); } +__be16 udplite_v6_checksum(const struct in6_addr *src_ip, + const struct in6_addr *dst_ip, + u8 protocol, const void *payload, u32 len, u16 cov) +{ + u64 sum = tcp_udp_v6_header_checksum_partial( + src_ip, dst_ip, protocol, len); + sum = ip_checksum_partial(payload, cov, sum); + return ip_checksum_fold(sum); +} + #define CRC32C(c, d) (c = (c>>8) ^ crc_c[(c^(d))&0xFF]) static u32 crc_c[256] = { diff --git a/gtests/net/packetdrill/checksum.h b/gtests/net/packetdrill/checksum.h index 43681d2c..d4154cd8 100644 --- a/gtests/net/packetdrill/checksum.h +++ b/gtests/net/packetdrill/checksum.h @@ -39,6 +39,11 @@ extern __be16 ipv4_checksum(void *ip_header, size_t ip_header_bytes); extern __be16 tcp_udp_v4_checksum(struct in_addr src_ip, struct in_addr dst_ip, u8 protocol, const void *payload, u16 len); +/* Calculates UDPLite checksum for IPv4 (in network byte order). */ +extern __be16 udplite_v4_checksum(struct in_addr src_ip, struct in_addr dst_ip, + u8 protocol, const void *payload, + u16 len, u16 cov); + /* IPv6 ... */ /* Calculates TCP, UDP, or ICMP checksum for IPv6 (in network byte order). */ @@ -46,6 +51,12 @@ extern __be16 tcp_udp_v6_checksum(const struct in6_addr *src_ip, const struct in6_addr *dst_ip, u8 protocol, const void *payload, u32 len); +/* Calculates UDPLite checksum for IPv6 (in network byte order). */ +extern __be16 udplite_v6_checksum(const struct in6_addr *src_ip, + const struct in6_addr *dst_ip, + u8 protocol, const void *payload, + u32 len, u16 cov); + /* SCTP ... */ /* Calculates the CRC32C checksum used by SCTP (in network byte order). */ diff --git a/gtests/net/packetdrill/checksum_test.c b/gtests/net/packetdrill/checksum_test.c index 55ac51e2..72133a55 100644 --- a/gtests/net/packetdrill/checksum_test.c +++ b/gtests/net/packetdrill/checksum_test.c @@ -30,6 +30,7 @@ #include "ipv6.h" #include "sctp.h" #include "tcp.h" +#include "udplite.h" static void test_tcp_udp_v4_checksum(void) { @@ -130,11 +131,70 @@ static void test_sctp_crc32c(void) assert(crc32c == 0xdad73774); } +static void test_udplite_v4_checksum(void) +{ + u8 data[] = { + 0x45, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x88, 0xf8, 0xab, 0x01, 0x01, 0x01, 0x01, + 0xc0, 0xa8, 0x00, 0x01, 0x04, 0xd2, 0xeb, 0x35, + 0x00, 0x09, 0x86, 0xaf, 0xc6, 0x45, 0x46 + }; + struct ipv4 *ip = (struct ipv4 *)data; + struct udplite *udplite; + int len = sizeof(data) - sizeof(struct ipv4); + u16 cov = 9; + u16 checksum; + + udplite = (struct udplite *)(data + sizeof(struct ipv4)); + checksum = + ntohs(udplite_v4_checksum(ip->src_ip, ip->dst_ip, + IPPROTO_UDPLITE, udplite, len, cov)); + assert(checksum == 0); + + udplite->check = 0; + checksum = + ntohs(udplite_v4_checksum(ip->src_ip, ip->dst_ip, + IPPROTO_UDPLITE, udplite, len, cov)); + assert(checksum == 0x86af); +} + +static void test_udplite_v6_checksum(void) +{ + u8 data[] = { + 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0xff, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xfd, 0x3d, 0xfa, 0x7b, 0xd1, 0x7d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xd3, 0xe2, 0x1f, 0x90, 0x00, 0x09, 0x4e, 0xfd, + 0xc6, 0x45, 0x46 + }; + struct ipv6 *ipv6 = (struct ipv6 *)data; + struct udplite *udplite; + int len = sizeof(data) - sizeof(struct ipv6); + u16 cov = 9; + u16 checksum; + + udplite = (struct udplite *)(data + sizeof(struct ipv6)); + checksum = + ntohs(udplite_v6_checksum(&ipv6->src_ip, &ipv6->dst_ip, + IPPROTO_UDPLITE, udplite, len, cov)); + assert(checksum == 0); + + udplite->check = 0; + checksum = + ntohs(udplite_v6_checksum(&ipv6->src_ip, &ipv6->dst_ip, + IPPROTO_UDPLITE, udplite, len, cov)); + assert(checksum == 0x4efd); +} + int main(void) { test_tcp_udp_v4_checksum(); test_tcp_udp_v6_checksum(); test_ipv4_checksum(); test_sctp_crc32c(); + test_udplite_v4_checksum(); + test_udplite_v6_checksum(); return 0; } diff --git a/gtests/net/packetdrill/contrib/packetdrill.vim b/gtests/net/packetdrill/contrib/packetdrill.vim index 4e1e8b9b..53d8b02b 100644 --- a/gtests/net/packetdrill/contrib/packetdrill.vim +++ b/gtests/net/packetdrill/contrib/packetdrill.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Packetdrill " Maintainer: Barath Raghavan <barath@google.com> -" Last Change: 2013 Jul 27 +" Last Change: 2014 Sep 22 " Quit when a (custom) syntax file was already loaded if exists("b:current_syntax") diff --git a/gtests/net/packetdrill/header.h b/gtests/net/packetdrill/header.h index 4c4216ce..389b126d 100644 --- a/gtests/net/packetdrill/header.h +++ b/gtests/net/packetdrill/header.h @@ -41,6 +41,7 @@ #include "mpls.h" #include "tcp.h" #include "udp.h" +#include "udplite.h" struct packet; @@ -53,6 +54,7 @@ enum header_t { HEADER_MPLS, HEADER_TCP, HEADER_UDP, + HEADER_UDPLITE, HEADER_ICMPV4, HEADER_ICMPV6, HEADER_NUM_TYPES @@ -71,6 +73,7 @@ struct header { struct mpls *mpls; struct tcp *tcp; struct udp *udp; + struct udplite *udplite; struct icmpv4 *icmpv4; struct icmpv6 *icmpv6; } h; diff --git a/gtests/net/packetdrill/icmp_packet.c b/gtests/net/packetdrill/icmp_packet.c index 0aec9f00..3ded8fe5 100644 --- a/gtests/net/packetdrill/icmp_packet.c +++ b/gtests/net/packetdrill/icmp_packet.c @@ -280,8 +280,9 @@ struct packet *new_icmp_packet(int address_family, const char *type_string, const char *code_string, int protocol, + u16 payload_bytes, u32 tcp_start_sequence, - u32 payload_bytes, + u16 udplite_checksum_coverage, s64 mtu, char **error) { @@ -355,6 +356,17 @@ struct packet *new_icmp_packet(int address_family, u32 *seq = packet_echoed_tcp_seq(packet); *seq = htonl(tcp_start_sequence); } + if (protocol == IPPROTO_UDP) { + u16 *cov = packet_echoed_udp_len(packet); + *cov = htons(payload_bytes + sizeof(struct udp)); + } + if (protocol == IPPROTO_UDPLITE) { + u16 *cov = packet_echoed_udplite_cov(packet); + u16 *checksum = packet_echoed_udplite_checksum(packet); + *cov = htons(udplite_checksum_coverage); + /* Zero checksum is not allowed, so choose some valid value */ + *checksum = htons(0xffff); + } packet->ip_bytes = ip_bytes; return packet; diff --git a/gtests/net/packetdrill/icmp_packet.h b/gtests/net/packetdrill/icmp_packet.h index ecaa8990..ba3db691 100644 --- a/gtests/net/packetdrill/icmp_packet.h +++ b/gtests/net/packetdrill/icmp_packet.h @@ -45,8 +45,9 @@ extern struct packet *new_icmp_packet(int address_family, const char *type_string, const char *code_string, int protocol, + u16 payload_bytes, u32 tcp_start_sequence, - u32 payload_bytes, + u16 udplite_checksum_coverage, s64 mtu, char **error); diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l index 09cefdcd..cc57de99 100644 --- a/gtests/net/packetdrill/lexer.l +++ b/gtests/net/packetdrill/lexer.l @@ -175,6 +175,7 @@ ipv4 return IPV4; ipv6 return IPV6; icmp return ICMP; udp return UDP; +udplite return UDPLITE; gre return GRE; mpls return MPLS; label return LABEL; diff --git a/gtests/net/packetdrill/netdev.c b/gtests/net/packetdrill/netdev.c index c6e36fcb..4c1f1ce8 100644 --- a/gtests/net/packetdrill/netdev.c +++ b/gtests/net/packetdrill/netdev.c @@ -347,8 +347,9 @@ static int local_netdev_send(struct netdev *a_netdev, assert(packet->ip_bytes > 0); /* We do IPv4 and IPv6 */ assert(packet->ipv4 || packet->ipv6); - /* We only do TCP and ICMP */ - assert(packet->tcp || packet->udp || packet->icmpv4 || packet->icmpv6); + /* We only do TCP, UDP, UDPLite and ICMP */ + assert(packet->tcp || packet->udp || packet->udplite || + packet->icmpv4 || packet->icmpv6); DEBUGP("local_netdev_send\n"); diff --git a/gtests/net/packetdrill/packet.c b/gtests/net/packetdrill/packet.c index c52bb143..bb62f3b6 100644 --- a/gtests/net/packetdrill/packet.c +++ b/gtests/net/packetdrill/packet.c @@ -36,15 +36,16 @@ /* Info for all types of header we support. */ struct header_type_info header_types[HEADER_NUM_TYPES] = { - { "NONE", 0, 0, NULL }, - { "IPV4", IPPROTO_IPIP, ETHERTYPE_IP, ipv4_header_finish }, - { "IPV6", IPPROTO_IPV6, ETHERTYPE_IPV6, ipv6_header_finish }, - { "GRE", IPPROTO_GRE, 0, gre_header_finish }, - { "MPLS", 0, ETHERTYPE_MPLS_UC, mpls_header_finish }, - { "TCP", IPPROTO_TCP, 0, NULL }, - { "UDP", IPPROTO_UDP, 0, NULL }, - { "ICMPV4", IPPROTO_ICMP, 0, NULL }, - { "ICMPV6", IPPROTO_ICMPV6, 0, NULL }, + { "NONE", 0, 0, NULL }, + { "IPV4", IPPROTO_IPIP, ETHERTYPE_IP, ipv4_header_finish }, + { "IPV6", IPPROTO_IPV6, ETHERTYPE_IPV6, ipv6_header_finish }, + { "GRE", IPPROTO_GRE, 0, gre_header_finish }, + { "MPLS", 0, ETHERTYPE_MPLS_UC, mpls_header_finish }, + { "TCP", IPPROTO_TCP, 0, NULL }, + { "UDP", IPPROTO_UDP, 0, NULL }, + { "UDPLITE", IPPROTO_UDPLITE, 0, NULL }, + { "ICMPV4", IPPROTO_ICMP, 0, NULL }, + { "ICMPV6", IPPROTO_ICMPV6, 0, NULL }, }; struct packet *packet_new(u32 buffer_bytes) @@ -159,6 +160,7 @@ static struct packet *packet_copy_with_headroom(struct packet *old_packet, packet->ipv6 = offset_ptr(old_base, new_base, old_packet->ipv6); packet->tcp = offset_ptr(old_base, new_base, old_packet->tcp); packet->udp = offset_ptr(old_base, new_base, old_packet->udp); + packet->udplite = offset_ptr(old_base, new_base, old_packet->udplite); packet->icmpv4 = offset_ptr(old_base, new_base, old_packet->icmpv4); packet->icmpv6 = offset_ptr(old_base, new_base, old_packet->icmpv6); diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h index fd4a50ce..57f8f73d 100644 --- a/gtests/net/packetdrill/packet.h +++ b/gtests/net/packetdrill/packet.h @@ -38,6 +38,7 @@ #include "ipv6.h" #include "tcp.h" #include "udp.h" +#include "udplite.h" #include "unaligned.h" /* The data offset field is 4 bits, and specifies the length of the TCP header, @@ -47,6 +48,7 @@ #define MAX_TCP_DATAGRAM_BYTES (64*1024) /* for sanity-checking */ #define MAX_UDP_DATAGRAM_BYTES (64*1024) /* for sanity-checking */ +#define MAX_UDPLITE_DATAGRAM_BYTES (64*1024) /* for sanity-checking */ /* We allow reading pretty big packets, since some interface MTUs can * be pretty big (the Linux loopback MTU, for example, is typically @@ -92,6 +94,7 @@ struct packet { /* Layer 4 */ struct tcp *tcp; /* start of TCP header, if present */ struct udp *udp; /* start of UDP header, if present */ + struct udplite *udplite;/* start of UDPLite header, if present */ struct icmpv4 *icmpv4; /* start of ICMPv4 header, if present */ struct icmpv6 *icmpv6; /* start of ICMPv6 header, if present */ @@ -221,6 +224,8 @@ static inline int layer4_header_len(int protocol) return sizeof(struct tcp); if (protocol == IPPROTO_UDP) return sizeof(struct udp); + if (protocol == IPPROTO_UDPLITE) + return sizeof(struct udplite); assert(!"bad protocol"); return 0; } @@ -239,6 +244,13 @@ static inline int packet_udp_header_len(const struct packet *packet) return sizeof(struct udp); } +/* Return the length of the UDPLite header. */ +static inline int packet_udplite_header_len(const struct packet *packet) +{ + assert(packet->udplite); + return sizeof(struct udplite); +} + /* Return the length of the TCP options. */ static inline int packet_tcp_options_len(const struct packet *packet) { @@ -280,7 +292,10 @@ static inline u8 *packet_payload(struct packet *packet) return ((u8 *) packet->tcp) + packet_tcp_header_len(packet); if (packet->udp) return ((u8 *) packet->udp) + packet_udp_header_len(packet); - assert(!"no valid payload; not TCP or UDP!?"); + if (packet->udplite) + return ((u8 *) packet->udplite) + + packet_udplite_header_len(packet); + assert(!"no valid payload; not TCP or UDP or UDPLite!?"); return NULL; } @@ -369,6 +384,15 @@ static inline struct udp *packet_echoed_udp_header(struct packet *packet) return NULL; } +/* Return the location of the UDPLITE header echoed by an ICMP message. */ +static inline struct +udplite *packet_echoed_udplite_header(struct packet *packet) +{ + if (packet_echoed_ip_protocol(packet) == IPPROTO_UDPLITE) + return (struct udplite *)(packet_echoed_layer4_header(packet)); + return NULL; +} + /* Return the location of the TCP sequence number echoed by an ICMP message. */ static inline u32 *packet_echoed_tcp_seq(struct packet *packet) { @@ -382,4 +406,48 @@ static inline u32 *packet_echoed_tcp_seq(struct packet *packet) return seq; } +/* Return the location of the UDP length echoed by an ICMP message. */ +static inline u16 *packet_echoed_udp_len(struct packet *packet) +{ + struct udp *echoed_udp = packet_echoed_udp_header(packet); + + assert(echoed_udp); + u16 *len = &(echoed_udp->len); + /* Check that the len field is actually in the space we + * reserved for the echoed prefix of the UDP header. + */ + assert((char *) (len + 1) <= (char *) echoed_udp + ICMP_ECHO_BYTES); + return len; +} + +/* Return the location of the UDPLite checksum coverage echoed by an ICMP + message. */ +static inline u16 *packet_echoed_udplite_cov(struct packet *packet) +{ + struct udplite *echoed_udplite = packet_echoed_udplite_header(packet); + + assert(echoed_udplite); + u16 *cov = &(echoed_udplite->cov); + /* Check that the len field is actually in the space we + * reserved for the echoed prefix of the UDPlite header. + */ + assert((char *) (cov + 1) <= (char *) echoed_udplite + ICMP_ECHO_BYTES); + return cov; +} + +/* Return the location of the UDPLite checksum echoed by an ICMP message. */ +static inline u16 *packet_echoed_udplite_checksum(struct packet *packet) +{ + struct udplite *echoed_udplite = packet_echoed_udplite_header(packet); + + assert(echoed_udplite); + u16 *checksum = &(echoed_udplite->check); + /* Check that the len field is actually in the space we + * reserved for the echoed prefix of the UDPlite header. + */ + assert((char *) (checksum + 1) <= + (char *) echoed_udplite + ICMP_ECHO_BYTES); + return checksum; +} + #endif /* __PACKET_H__ */ diff --git a/gtests/net/packetdrill/packet_checksum.c b/gtests/net/packetdrill/packet_checksum.c index d5164b34..f6498f6a 100644 --- a/gtests/net/packetdrill/packet_checksum.c +++ b/gtests/net/packetdrill/packet_checksum.c @@ -57,12 +57,25 @@ static void checksum_ipv4_packet(struct packet *packet) udp->check = tcp_udp_v4_checksum(ipv4->src_ip, ipv4->dst_ip, IPPROTO_UDP, udp, l4_bytes); + } else if (packet->udplite != NULL) { + struct udplite *udplite = packet->udplite; + u16 coverage; + + coverage = ntohs(udplite->cov); + if ((coverage == 0) || (coverage > l4_bytes)) + coverage = l4_bytes; + udplite->check = 0; + udplite->check = udplite_v4_checksum(ipv4->src_ip, + ipv4->dst_ip, + IPPROTO_UDPLITE, + udplite, + l4_bytes, coverage); } else if (packet->icmpv4 != NULL) { struct icmpv4 *icmpv4 = packet->icmpv4; icmpv4->checksum = 0; icmpv4->checksum = ipv4_checksum(icmpv4, l4_bytes); } else { - assert(!"not TCP or ICMP"); + assert(!"not TCP or UDP or UDPLite or ICMP"); } } @@ -91,6 +104,19 @@ static void checksum_ipv6_packet(struct packet *packet) udp->check = tcp_udp_v6_checksum(&ipv6->src_ip, &ipv6->dst_ip, IPPROTO_UDP, udp, l4_bytes); + } else if (packet->udplite != NULL) { + struct udplite *udplite = packet->udplite; + u16 coverage; + + coverage = ntohs(udplite->cov); + if ((coverage == 0) || (coverage > l4_bytes)) + coverage = l4_bytes; + udplite->check = 0; + udplite->check = udplite_v6_checksum(&ipv6->src_ip, + &ipv6->dst_ip, + IPPROTO_UDPLITE, + udplite, + l4_bytes, coverage); } else if (packet->icmpv6 != NULL) { /* IPv6 ICMP has a pseudo-header checksum, like TCP. */ struct icmpv6 *icmpv6 = packet->icmpv6; @@ -100,7 +126,7 @@ static void checksum_ipv6_packet(struct packet *packet) &ipv6->dst_ip, IPPROTO_ICMPV6, icmpv6, l4_bytes); } else { - assert(!"not TCP or ICMP"); + assert(!"not TCP or UDP or UDPLite or ICMP"); } } diff --git a/gtests/net/packetdrill/packet_parser.c b/gtests/net/packetdrill/packet_parser.c index b1acd3fe..1bd6d0d3 100644 --- a/gtests/net/packetdrill/packet_parser.c +++ b/gtests/net/packetdrill/packet_parser.c @@ -413,6 +413,46 @@ error_out: return PACKET_BAD; } +/* Parse the UDPLite header. Return a packet_parse_result_t. */ +static int parse_udplite(struct packet *packet, u8 *layer4_start, + int layer4_bytes, u8 *packet_end, char **error) +{ + struct header *udplite_header = NULL; + u8 *p = layer4_start; + + assert(layer4_bytes >= 0); + if (layer4_bytes < sizeof(struct udplite)) { + asprintf(error, "Truncated UDPLite header"); + goto error_out; + } + packet->udplite = (struct udplite *) p; + const int udplite_header_len = sizeof(struct udplite); + + if (layer4_bytes < udplite_header_len) { + asprintf(error, + "UDPLITE datagram length too small for UDPLite header"); + goto error_out; + } + + udplite_header = packet_append_header(packet, HEADER_UDPLITE, + udplite_header_len); + if (udplite_header == NULL) { + asprintf(error, "Too many nested headers at UDPLite header"); + goto error_out; + } + udplite_header->total_bytes = layer4_bytes; + + p += layer4_bytes; + assert(p <= packet_end); + + DEBUGP("UDPLite src port: %d\n", ntohs(packet->udplite->src_port)); + DEBUGP("UDPLite dst port: %d\n", ntohs(packet->udplite->dst_port)); + return PACKET_OK; + +error_out: + return PACKET_BAD; +} + /* Parse the ICMPv4 header. Return a packet_parse_result_t. */ static int parse_icmpv4(struct packet *packet, u8 *layer4_start, int layer4_bytes, u8 *packet_end, char **error) @@ -594,6 +634,10 @@ static int parse_layer4(struct packet *packet, u8 *layer4_start, *is_inner = true; /* found inner-most layer 4 */ return parse_udp(packet, layer4_start, layer4_bytes, packet_end, error); + } else if (layer4_protocol == IPPROTO_UDPLITE) { + *is_inner = true; /* found inner-most layer 4 */ + return parse_udplite(packet, layer4_start, layer4_bytes, + packet_end, error); } else if (layer4_protocol == IPPROTO_ICMP) { *is_inner = true; /* found inner-most layer 4 */ return parse_icmpv4(packet, layer4_start, layer4_bytes, diff --git a/gtests/net/packetdrill/packet_parser.h b/gtests/net/packetdrill/packet_parser.h index 160be514..07805553 100644 --- a/gtests/net/packetdrill/packet_parser.h +++ b/gtests/net/packetdrill/packet_parser.h @@ -30,7 +30,7 @@ enum packet_parse_result_t { PACKET_OK, /* no errors detected */ PACKET_BAD, /* illegal header */ - PACKET_UNKNOWN_L4, /* not TCP or UDP */ + PACKET_UNKNOWN_L4, /* not TCP or UDP or UDPLite */ }; /* Given an input packet of length 'in_bytes' stored in the buffer diff --git a/gtests/net/packetdrill/packet_parser_test.c b/gtests/net/packetdrill/packet_parser_test.c index c2545e7d..45e49e08 100644 --- a/gtests/net/packetdrill/packet_parser_test.c +++ b/gtests/net/packetdrill/packet_parser_test.c @@ -66,6 +66,7 @@ static void test_parse_tcp_ipv4_packet(void) assert(packet->ipv6 == NULL); assert(packet->tcp == expected_tcp); assert(packet->udp == NULL); + assert(packet->udplite == NULL); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); @@ -113,6 +114,7 @@ static void test_parse_tcp_ipv6_packet(void) assert(packet->ipv6 == expected_ipv6); assert(packet->tcp == expected_tcp); assert(packet->udp == NULL); + assert(packet->udplite == NULL); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); @@ -127,7 +129,7 @@ static void test_parse_udp_ipv4_packet(void) { /* A UDP/IPv4 packet. */ u8 data[] = { - /* 192.0.2.1.8080 > 192.168.0.1.57845: UDP, length 4 */ + /* 192.0.2.1:8080 > 192.168.0.1:57845: UDP, length 4 */ 0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0xff, 0x11, 0x39, 0x22, 0xc0, 0x00, 0x02, 0x01, 0xc0, 0xa8, 0x00, 0x01, 0x1f, 0x90, 0xe1, 0xf5, @@ -153,6 +155,7 @@ static void test_parse_udp_ipv4_packet(void) assert(packet->ipv6 == NULL); assert(packet->tcp == NULL); assert(packet->udp == expected_udp); + assert(packet->udplite == NULL); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); @@ -163,12 +166,11 @@ static void test_parse_udp_ipv4_packet(void) packet_free(packet); } - static void test_parse_udp_ipv6_packet(void) { /* A UDP/IPv6 packet. */ u8 data[] = { - /* 2001:db8::1.8080 > fd3d:fa7b:d17d::1.51557: UDP, length 4 */ + /* 2001:db8::1:8080 > fd3d:fa7b:d17d::1:51557: UDP, length 4 */ 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0xff, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, @@ -197,6 +199,96 @@ static void test_parse_udp_ipv6_packet(void) assert(packet->ipv6 == expected_ipv6); assert(packet->tcp == NULL); assert(packet->udp == expected_udp); + assert(packet->udplite == NULL); + assert(packet->icmpv4 == NULL); + assert(packet->icmpv6 == NULL); + + assert(packet->time_usecs == 0); + assert(packet->flags == 0); + assert(packet->ecn == 0); + + packet_free(packet); +} + +static void test_parse_udplite_ipv4_packet(void) +{ + /* A UDPLite/IPv4 packet. */ + u8 data[] = { + /* 1.1.1.1:1234 > 192.168.0.1:60213 + * UDPLite, length 3, coverage 9 */ + 0x45, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x88, 0xf8, 0xab, 0x01, 0x01, 0x01, 0x01, + 0xc0, 0xa8, 0x00, 0x01, 0x04, 0xd2, 0xeb, 0x35, + 0x00, 0x09, 0x86, 0xaf, 0xc6, 0x45, 0x46 + }; + + struct packet *packet = packet_new(sizeof(data)); + + /* Populate and parse a packet */ + memcpy(packet->buffer, data, sizeof(data)); + char *error = NULL; + enum packet_parse_result_t result = + parse_packet(packet, sizeof(data), PACKET_LAYER_3_IP, + &error); + assert(result == PACKET_OK); + assert(error == NULL); + + struct ipv4 *expected_ipv4 = (struct ipv4 *)(packet->buffer); + struct udplite *expected_udplite = + (struct udplite *)(expected_ipv4 + 1); + + assert(packet->ip_bytes == sizeof(data)); + assert(packet->ipv4 == expected_ipv4); + assert(packet->ipv6 == NULL); + assert(packet->tcp == NULL); + assert(packet->udp == NULL); + assert(packet->udplite == expected_udplite); + assert(packet->icmpv4 == NULL); + assert(packet->icmpv6 == NULL); + + assert(packet->time_usecs == 0); + assert(packet->flags == 0); + assert(packet->ecn == 0); + + packet_free(packet); +} + +static void test_parse_udplite_ipv6_packet(void) +{ + /* A UDPLite/IPv6 packet. */ + u8 data[] = { + /* 2001:db8::1:54242 > fd3d:fa7b:d17d::1:8080 + * UDPLITE, length 3, coverage 9 */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0xff, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xfd, 0x3d, 0xfa, 0x7b, 0xd1, 0x7d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xd3, 0xe2, 0x1f, 0x90, 0x00, 0x09, 0x4e, 0xfd, + 0xc6, 0x45, 0x46 + }; + + struct packet *packet = packet_new(sizeof(data)); + + /* Populate and parse a packet */ + memcpy(packet->buffer, data, sizeof(data)); + char *error = NULL; + enum packet_parse_result_t result = + parse_packet(packet, sizeof(data), PACKET_LAYER_3_IP, + &error); + assert(result == PACKET_OK); + assert(error == NULL); + + struct ipv6 *expected_ipv6 = (struct ipv6 *)(packet->buffer); + struct udplite *expected_udplite = + (struct udplite *)(expected_ipv6 + 1); + + assert(packet->ip_bytes == sizeof(data)); + assert(packet->ipv4 == NULL); + assert(packet->ipv6 == expected_ipv6); + assert(packet->tcp == NULL); + assert(packet->udp == NULL); + assert(packet->udplite == expected_udplite); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); @@ -215,7 +307,7 @@ static void test_parse_ipv4_gre_ipv4_tcp_packet(void) /* An IPv4/GRE/IPv4/TCP packet. */ u8 data[] = { /* IP 2.2.2.2 > 1.1.1.1: GREv0, length 48: - IP 192.0.2.1.47078 > 192.168.0.1.8080: + IP 192.0.2.1:47078 > 192.168.0.1:8080: . 2:6(4) ack 1 win 123 */ 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0xff, 0x2f, 0xb5, 0x85, 0x02, 0x02, 0x02, 0x02, @@ -275,6 +367,7 @@ static void test_parse_ipv4_gre_ipv4_tcp_packet(void) assert(packet->ipv6 == NULL); assert(packet->tcp == expected_tcp); assert(packet->udp == NULL); + assert(packet->udplite == NULL); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); @@ -373,6 +466,7 @@ static void test_parse_ipv4_gre_mpls_ipv4_tcp_packet(void) assert(packet->ipv6 == NULL); assert(packet->tcp == expected_tcp); assert(packet->udp == NULL); + assert(packet->udplite == NULL); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); @@ -415,6 +509,7 @@ static void test_parse_icmpv4_packet(void) assert(packet->ipv6 == NULL); assert(packet->tcp == NULL); assert(packet->udp == NULL); + assert(packet->udplite == NULL); assert(packet->icmpv4 == expected_icmpv4); assert(packet->icmpv6 == NULL); @@ -468,6 +563,7 @@ static void test_parse_icmpv6_packet(void) assert(packet->ipv6 == expected_ipv6); assert(packet->tcp == NULL); assert(packet->udp == NULL); + assert(packet->udplite == NULL); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == expected_icmpv6); @@ -484,6 +580,8 @@ int main(void) test_parse_tcp_ipv6_packet(); test_parse_udp_ipv4_packet(); test_parse_udp_ipv6_packet(); + test_parse_udplite_ipv4_packet(); + test_parse_udplite_ipv6_packet(); test_parse_ipv4_gre_ipv4_tcp_packet(); test_parse_ipv4_gre_mpls_ipv4_tcp_packet(); test_parse_icmpv4_packet(); diff --git a/gtests/net/packetdrill/packet_to_string.c b/gtests/net/packetdrill/packet_to_string.c index cfd503dc..9fc5e5fb 100644 --- a/gtests/net/packetdrill/packet_to_string.c +++ b/gtests/net/packetdrill/packet_to_string.c @@ -194,6 +194,25 @@ static int udp_packet_to_string(FILE *s, struct packet *packet, return result; } +static int udplite_packet_to_string(FILE *s, struct packet *packet, + enum dump_format_t format, char **error) +{ + int result = STATUS_OK; /* return value */ + + if ((format == DUMP_FULL) || (format == DUMP_VERBOSE)) { + endpoints_to_string(s, packet); + fputc(' ', s); + } + + fprintf(s, "udplite (%u, %u)", + packet_payload_len(packet), ntohs(packet->udplite->cov)); + + if (format == DUMP_VERBOSE) + packet_buffer_to_string(s, packet); + + return result; +} + static int icmpv4_packet_to_string(FILE *s, struct packet *packet, enum dump_format_t format, char **error) { @@ -261,6 +280,9 @@ int packet_to_string(struct packet *packet, } else if (packet->udp != NULL) { if (udp_packet_to_string(s, packet, format, error)) goto out; + } else if (packet->udplite != NULL) { + if (udplite_packet_to_string(s, packet, format, error)) + goto out; } else if (packet->icmpv4 != NULL) { if (icmpv4_packet_to_string(s, packet, format, error)) goto out; @@ -268,7 +290,7 @@ int packet_to_string(struct packet *packet, if (icmpv6_packet_to_string(s, packet, format, error)) goto out; } else { - fprintf(s, "[NO TCP OR ICMP HEADER]"); + fprintf(s, "[No TCP or UDP or UDPLite or ICMP header]"); } } diff --git a/gtests/net/packetdrill/packet_to_string_test.c b/gtests/net/packetdrill/packet_to_string_test.c index 4d339710..31f7b27c 100644 --- a/gtests/net/packetdrill/packet_to_string_test.c +++ b/gtests/net/packetdrill/packet_to_string_test.c @@ -114,7 +114,7 @@ static void test_tcp_ipv4_packet_to_string(void) static void test_tcp_ipv6_packet_to_string(void) { - /* An IPv6/GRE/TCP/IPv6 packet. */ + /* An IPv6/GRE/IPv6/TCP packet. */ u8 data[] = { /* IPv6: */ 0x60, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x2f, 0xff, @@ -246,10 +246,166 @@ static void test_gre_mpls_tcp_ipv4_packet_to_string(void) free(dump); } +static void test_udplite_ipv4_packet_to_string(void) +{ + /* An IPv4/GRE/IPv4/UDPLite packet. */ + u8 data[] = { + /* IPv4: */ + 0x45, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x2f, 0xb5, 0x92, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x01, + /* GRE: */ + 0x00, 0x00, 0x08, 0x00, + /* IPv4, UDPLite: */ + 0x45, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x88, 0xf8, 0xab, 0x01, 0x01, 0x01, 0x01, + 0xc0, 0xa8, 0x00, 0x01, 0x04, 0xd2, 0xeb, 0x35, + 0x00, 0x09, 0x86, 0xaf, 0xc6, 0x45, 0x46 + }; + + struct packet *packet = packet_new(sizeof(data)); + + /* Populate and parse a packet */ + memcpy(packet->buffer, data, sizeof(data)); + char *error = NULL; + enum packet_parse_result_t result = + parse_packet(packet, sizeof(data), PACKET_LAYER_3_IP, + &error); + assert(result == PACKET_OK); + assert(error == NULL); + + int status = 0; + char *dump = NULL, *expected = NULL; + + /* Test a DUMP_SHORT dump */ + status = packet_to_string(packet, DUMP_SHORT, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "ipv4 2.2.2.2 > 1.1.1.1: gre: " + "udplite (3, 9)"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_FULL dump */ + status = packet_to_string(packet, DUMP_FULL, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "ipv4 2.2.2.2 > 1.1.1.1: gre: " + "1.1.1.1:1234 > 192.168.0.1:60213 " + "udplite (3, 9)"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_VERBOSE dump */ + status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "ipv4 2.2.2.2 > 1.1.1.1: gre: " + "1.1.1.1:1234 > 192.168.0.1:60213 " + "udplite (3, 9)" + "\n" + "0x0000: 45 00 00 37 00 00 00 00 ff 2f b5 92 02 02 02 02 " "\n" + "0x0010: 01 01 01 01 00 00 08 00 45 00 00 1f 00 00 00 00 " "\n" + "0x0020: ff 88 f8 ab 01 01 01 01 c0 a8 00 01 04 d2 eb 35 " "\n" + "0x0030: 00 09 86 af c6 45 46 " "\n"; + assert(strcmp(dump, expected) == 0); + free(dump); + + packet_free(packet); +} + +static void test_udplite_ipv6_packet_to_string(void) +{ + /* An IPv6/GRE/IPv6/UDPLite packet. */ + u8 data[] = { + /* IPv6: */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x37, 0x2f, 0xff, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, + /* GRE: */ + 0x00, 0x00, 0x86, 0xdd, + /* IPv6, UDPLite: */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x88, 0xff, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xfd, 0x3d, 0xfa, 0x7b, 0xd1, 0x7d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xd3, 0xe2, 0x1f, 0x90, 0x00, 0x09, 0x4e, 0xfd, + 0xc6, 0x45, 0x46 + }; + + struct packet *packet = packet_new(sizeof(data)); + + /* Populate and parse a packet */ + memcpy(packet->buffer, data, sizeof(data)); + char *error = NULL; + enum packet_parse_result_t result = + parse_packet(packet, sizeof(data), PACKET_LAYER_3_IP, + &error); + assert(result == PACKET_OK); + assert(error == NULL); + + int status = 0; + char *dump = NULL, *expected = NULL; + + /* Test a DUMP_SHORT dump */ + status = packet_to_string(packet, DUMP_SHORT, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "ipv6 2::2222 > 1::1111: gre: " + "udplite (3, 9)"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_FULL dump */ + status = packet_to_string(packet, DUMP_FULL, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "ipv6 2::2222 > 1::1111: gre: " + "2001:db8::1:54242 > fd3d:fa7b:d17d::1:8080 " + "udplite (3, 9)"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_VERBOSE dump */ + status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "ipv6 2::2222 > 1::1111: gre: " + "2001:db8::1:54242 > fd3d:fa7b:d17d::1:8080 " + "udplite (3, 9)\n" + "0x0000: 60 00 00 00 00 37 2f ff 00 02 00 00 00 00 00 00 " "\n" + "0x0010: 00 00 00 00 00 00 22 22 00 01 00 00 00 00 00 00 " "\n" + "0x0020: 00 00 00 00 00 00 11 11 00 00 86 dd 60 00 00 00 " "\n" + "0x0030: 00 0b 88 ff 20 01 0d b8 00 00 00 00 00 00 00 00 " "\n" + "0x0040: 00 00 00 01 fd 3d fa 7b d1 7d 00 00 00 00 00 00 " "\n" + "0x0050: 00 00 00 01 d3 e2 1f 90 00 09 4e fd c6 45 46 " "\n"; + assert(strcmp(dump, expected) == 0); + free(dump); + + packet_free(packet); +} + int main(void) { test_tcp_ipv4_packet_to_string(); test_tcp_ipv6_packet_to_string(); test_gre_mpls_tcp_ipv4_packet_to_string(); + test_udplite_ipv4_packet_to_string(); + test_udplite_ipv6_packet_to_string(); return 0; } diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index 6bee8a9d..32f18e9c 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -99,6 +99,7 @@ #include "mpls_packet.h" #include "tcp_packet.h" #include "udp_packet.h" +#include "udplite_packet.h" #include "parse.h" #include "script.h" #include "tcp.h" @@ -449,10 +450,15 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, s32 window; u32 sequence_number; struct { - int protocol; /* IPPROTO_TCP or IPPROTO_UDP */ u32 start_sequence; u16 payload_bytes; } tcp_sequence_info; + struct { + int protocol; + u16 payload_bytes; + u32 start_sequence; /* used for TCP */ + u16 checksum_coverage; /* used for UDPLite */ + } transport_info; struct option_list *option; struct event *event; struct packet *packet; @@ -477,7 +483,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %token <reserved> ACK ECR EOL MSS NOP SACK SACKOK TIMESTAMP VAL WIN WSCALE PRO %token <reserved> FAST_OPEN %token <reserved> ECT0 ECT1 CE ECT01 NO_ECN -%token <reserved> IPV4 IPV6 ICMP UDP GRE MTU +%token <reserved> IPV4 IPV6 ICMP UDP UDPLITE GRE MTU %token <reserved> MPLS LABEL TC TTL %token <reserved> OPTION %token <reserved> SRTO_INITIAL SRTO_MAX SRTO_MIN @@ -494,7 +500,8 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %type <option> option options opt_options %type <event> event events event_time action %type <time_usecs> time opt_end_time -%type <packet> packet_spec tcp_packet_spec udp_packet_spec icmp_packet_spec +%type <packet> packet_spec tcp_packet_spec udp_packet_spec udplite_packet_spec +%type <packet> icmp_packet_spec %type <packet> packet_prefix %type <syscall> syscall_spec %type <command> command_spec @@ -509,7 +516,8 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %type <string> option_flag option_value script %type <window> opt_window %type <sequence_number> opt_ack -%type <tcp_sequence_info> seq opt_icmp_echoed +%type <tcp_sequence_info> seq +%type <transport_info> opt_icmp_echoed %type <tcp_options> opt_tcp_options tcp_option_list %type <tcp_option> tcp_option sack_block_list sack_block %type <string> function_name @@ -676,9 +684,10 @@ action ; packet_spec -: tcp_packet_spec { $$ = $1; } -| udp_packet_spec { $$ = $1; } -| icmp_packet_spec { $$ = $1; } +: tcp_packet_spec { $$ = $1; } +| udp_packet_spec { $$ = $1; } +| udplite_packet_spec { $$ = $1; } +| icmp_packet_spec { $$ = $1; } ; tcp_packet_spec @@ -730,6 +739,31 @@ udp_packet_spec } ; +udplite_packet_spec +: packet_prefix UDPLITE '(' INTEGER ',' INTEGER ')' { + char *error = NULL; + struct packet *outer = $1, *inner = NULL; + enum direction_t direction = outer->direction; + + if (!is_valid_u16($4)) { + semantic_error("UDPLite payload size out of range"); + } + if (!is_valid_u16($6)) { + semantic_error("UDPLite checksum coverage out of range"); + } + + inner = new_udplite_packet(in_config->wire_protocol, direction, $4, $6, + &error); + if (inner == NULL) { + assert(error != NULL); + semantic_error(error); + free(error); + } + + $$ = packet_encapsulate_and_free(outer, inner); +} +; + icmp_packet_spec : packet_prefix opt_icmp_echoed ICMP icmp_type opt_icmp_code opt_icmp_mtu { char *error = NULL; @@ -737,8 +771,9 @@ icmp_packet_spec enum direction_t direction = outer->direction; inner = new_icmp_packet(in_config->wire_protocol, direction, $4, $5, - $2.protocol, $2.start_sequence, - $2.payload_bytes, $6, &error); + $2.protocol, $2.payload_bytes, + $2.start_sequence, $2.checksum_coverage, + $6, &error); free($4); free($5); if (inner == NULL) { @@ -847,17 +882,24 @@ opt_icmp_code /* This specifies the relevant details about the packet echoed by ICMP. */ opt_icmp_echoed : { - $$.start_sequence = 0; - $$.payload_bytes = 0; $$.protocol = IPPROTO_TCP; + $$.payload_bytes = 0; + $$.start_sequence = 0; } | '[' UDP '(' INTEGER ')' ']' { + $$.protocol = IPPROTO_UDP; + $$.payload_bytes = $4; $$.start_sequence = 0; +} +| '[' UDPLITE '(' INTEGER ',' INTEGER ')' ']' { + $$.protocol = IPPROTO_UDPLITE; $$.payload_bytes = $4; - $$.protocol = IPPROTO_UDP; + $$.checksum_coverage = $6; } | '[' seq ']' { - $$ = $2; + $$.protocol = IPPROTO_TCP; + $$.payload_bytes = $2.payload_bytes; + $$.start_sequence = $2.start_sequence; } ; @@ -908,7 +950,6 @@ seq } $$.start_sequence = $1; $$.payload_bytes = $5; - $$.protocol = IPPROTO_TCP; } ; diff --git a/gtests/net/packetdrill/platforms.h b/gtests/net/packetdrill/platforms.h index 16086902..6296fc9e 100644 --- a/gtests/net/packetdrill/platforms.h +++ b/gtests/net/packetdrill/platforms.h @@ -46,17 +46,23 @@ #include <netinet/sctp.h> #include <sys/param.h> +#if __FreeBSD_version >= 1010000 +#include <netinet/udplite.h> +#endif #define USE_LIBPCAP 1 #define TUN_PATH "/dev/tun0" #define TUN_DEV "tun0" - #define HAVE_TCP_INFO 1 #if (__FreeBSD_version < 1000000 && __FreeBSD_version > 902000) || __FreeBSD_version > 1000028 #define HAVE_FMEMOPEN 1 +#else +#include "fmemopen.h" #endif - +#if (__FreeBSD_version > 902000) +#define HAVE_OPEN_MEMSTREAM 1 +#else #include "open_memstream.h" -#include "fmemopen.h" +#endif #endif /* __FreeBSD__ */ diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c index 52d08d3f..070a184b 100644 --- a/gtests/net/packetdrill/run_packet.c +++ b/gtests/net/packetdrill/run_packet.c @@ -331,11 +331,15 @@ static struct socket *find_connect_for_live_packet( (packet->udp && (socket->protocol == IPPROTO_UDP) && (socket->state == SOCKET_ACTIVE_CONNECTING)); + bool is_udplite_match = + (packet->udplite && + (socket->protocol == IPPROTO_UDPLITE) && + (socket->state == SOCKET_ACTIVE_CONNECTING)); bool is_tcp_match = (packet->tcp && packet->tcp->syn && !packet->tcp->ack && (socket->protocol == IPPROTO_TCP) && (socket->state == SOCKET_ACTIVE_SYN_SENT)); - if (!is_udp_match && !is_tcp_match) + if (!is_udp_match && !is_tcp_match && !is_udplite_match) return NULL; if (!is_equal_ip(&tuple.dst.ip, &socket->live.remote.ip) || @@ -456,6 +460,13 @@ static int map_inbound_icmp_udp_packet( return STATUS_OK; } +/* UDPLite headers echoed by ICMP messages need no special rewriting. */ +static int map_inbound_icmp_udplite_packet( + struct socket *socket, struct packet *live_packet, char **error) +{ + return STATUS_OK; +} + static int map_inbound_icmp_packet( struct socket *socket, struct packet *live_packet, char **error) { @@ -463,6 +474,9 @@ static int map_inbound_icmp_packet( return map_inbound_icmp_tcp_packet(socket, live_packet, error); else if (packet_echoed_ip_protocol(live_packet) == IPPROTO_UDP) return map_inbound_icmp_udp_packet(socket, live_packet, error); + else if (packet_echoed_ip_protocol(live_packet) == IPPROTO_UDPLITE) + return map_inbound_icmp_udplite_packet(socket, live_packet, + error); else assert(!"unsupported layer 4 protocol echoed in ICMP packet"); return STATUS_ERR; @@ -827,6 +841,24 @@ static int verify_udp( return STATUS_OK; } +/* Verify that required actual UDPLite header fields are as the script + expected. */ +static int verify_udplite( + const struct packet *actual_packet, + const struct packet *script_packet, + int layer, char **error) +{ + const struct udplite *actual_udplite = + actual_packet->headers[layer].h.udplite; + const struct udplite *script_udplite = + script_packet->headers[layer].h.udplite; + if (check_field("udplite_cov", + ntohs(script_udplite->cov), + ntohs(actual_udplite->cov), error)) + return STATUS_ERR; + return STATUS_OK; +} + /* Verify that required actual GRE header fields are as the script expected. */ static int verify_gre( const struct packet *actual_packet, @@ -886,12 +918,13 @@ static int verify_header( int layer, char **error) { verifier_func verifiers[HEADER_NUM_TYPES] = { - [HEADER_IPV4] = verify_ipv4, - [HEADER_IPV6] = verify_ipv6, - [HEADER_GRE] = verify_gre, - [HEADER_MPLS] = verify_mpls, - [HEADER_TCP] = verify_tcp, - [HEADER_UDP] = verify_udp, + [HEADER_IPV4] = verify_ipv4, + [HEADER_IPV6] = verify_ipv6, + [HEADER_GRE] = verify_gre, + [HEADER_MPLS] = verify_mpls, + [HEADER_TCP] = verify_tcp, + [HEADER_UDP] = verify_udp, + [HEADER_UDPLITE] = verify_udplite, }; verifier_func verifier = NULL; const struct header *actual_header = &actual_packet->headers[layer]; @@ -924,7 +957,9 @@ static int verify_outbound_live_headers( int i; assert((actual_packet->ipv4 != NULL) || (actual_packet->ipv6 != NULL)); - assert((actual_packet->tcp != NULL) || (actual_packet->udp != NULL)); + assert((actual_packet->tcp != NULL) || + (actual_packet->udp != NULL) || + (actual_packet->udplite != NULL)); if (actual_headers != script_headers) { asprintf(error, "live packet header layers: " @@ -1154,6 +1189,8 @@ static bool is_script_packet_match_for_socket( return packet->tcp || is_packet_icmp; else if (socket->protocol == IPPROTO_UDP) return packet->udp || is_packet_icmp; + else if (socket->protocol == IPPROTO_UDPLITE) + return packet->udplite || is_packet_icmp; else assert(!"unsupported layer 4 protocol in socket"); return false; @@ -1260,8 +1297,9 @@ static int send_live_ip_packet(struct netdev *netdev, assert(packet->ip_bytes > 0); /* We do IPv4 and IPv6 */ assert(packet->ipv4 || packet->ipv6); - /* We only do TCP, UDP, and ICMP */ - assert(packet->tcp || packet->udp || packet->icmpv4 || packet->icmpv6); + /* We only do TCP, UDP, UDPLite and ICMP */ + assert(packet->tcp || packet->udp || packet->udplite || + packet->icmpv4 || packet->icmpv6); /* Fill in layer 3 and layer 4 checksums */ checksum_packet(packet); diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c index fbf150c9..9877c4eb 100644 --- a/gtests/net/packetdrill/script.c +++ b/gtests/net/packetdrill/script.c @@ -109,6 +109,7 @@ struct int_symbol cross_platform_symbols[] = { #endif { IPPROTO_TCP, "IPPROTO_TCP" }, { IPPROTO_UDP, "IPPROTO_UDP" }, + { IPPROTO_UDPLITE, "IPPROTO_UDPLITE" }, { SHUT_RD, "SHUT_RD" }, { SHUT_WR, "SHUT_WR" }, diff --git a/gtests/net/packetdrill/socket.h b/gtests/net/packetdrill/socket.h index 4fd8ea8a..0b14a7e0 100644 --- a/gtests/net/packetdrill/socket.h +++ b/gtests/net/packetdrill/socket.h @@ -172,6 +172,9 @@ static inline void get_packet_tuple(const struct packet *packet, } else if (packet->udp != NULL) { tuple->src.port = packet->udp->src_port; tuple->dst.port = packet->udp->dst_port; + } else if (packet->udplite != NULL) { + tuple->src.port = packet->udplite->src_port; + tuple->dst.port = packet->udplite->dst_port; } } @@ -180,6 +183,7 @@ static inline void set_headers_tuple(struct ipv4 *ipv4, struct ipv6 *ipv6, struct tcp *tcp, struct udp *udp, + struct udplite *udplite, const struct tuple *tuple) { if (ipv4 != NULL) { @@ -197,6 +201,9 @@ static inline void set_headers_tuple(struct ipv4 *ipv4, } else if (udp != NULL) { udp->src_port = tuple->src.port; udp->dst_port = tuple->dst.port; + } else if (udplite != NULL) { + udplite->src_port = tuple->src.port; + udplite->dst_port = tuple->dst.port; } } @@ -221,6 +228,7 @@ static inline void set_icmp_echoed_tuple(struct packet *packet, packet_echoed_ipv6_header(packet), packet_echoed_tcp_header(packet), packet_echoed_udp_header(packet), + packet_echoed_udplite_header(packet), &echoed_tuple); } @@ -228,8 +236,8 @@ static inline void set_icmp_echoed_tuple(struct packet *packet, static inline void set_packet_tuple(struct packet *packet, const struct tuple *tuple) { - set_headers_tuple(packet->ipv4, packet->ipv6, packet->tcp, packet->udp, - tuple); + set_headers_tuple(packet->ipv4, packet->ipv6, packet->tcp, + packet->udp, packet->udplite, tuple); if ((packet->icmpv4 != NULL) || (packet->icmpv6 != NULL)) set_icmp_echoed_tuple(packet, tuple); } diff --git a/gtests/net/packetdrill/symbols_freebsd.c b/gtests/net/packetdrill/symbols_freebsd.c index d78a73ec..c760b465 100644 --- a/gtests/net/packetdrill/symbols_freebsd.c +++ b/gtests/net/packetdrill/symbols_freebsd.c @@ -95,6 +95,12 @@ struct int_symbol platform_symbols_table[] = { { TCP_INFO, "TCP_INFO" }, { TCP_CONGESTION, "TCP_CONGESTION" }, +#if __FreeBSD_version >= 1100000 + /* /usr/include/netinet/udplite.h */ + { UDPLITE_RECV_CSCOV, "UDPLITE_RECV_CSCOV" }, + { UDPLITE_SEND_CSCOV, "UDPLITE_SEND_CSCOV" }, +#endif + /* /usr/include/sys/fcntl.h */ { O_RDONLY, "O_RDONLY" }, { O_WRONLY, "O_WRONLY" }, diff --git a/gtests/net/packetdrill/symbols_linux.c b/gtests/net/packetdrill/symbols_linux.c index 17347436..518c5322 100644 --- a/gtests/net/packetdrill/symbols_linux.c +++ b/gtests/net/packetdrill/symbols_linux.c @@ -53,6 +53,7 @@ struct int_symbol platform_symbols_table[] = { #endif { SOL_TCP, "SOL_TCP" }, { SOL_UDP, "SOL_UDP" }, + { SOL_UDPLITE, "SOL_UDPLITE" }, { SO_ACCEPTCONN, "SO_ACCEPTCONN" }, { SO_ATTACH_FILTER, "SO_ATTACH_FILTER" }, @@ -137,6 +138,9 @@ struct int_symbol platform_symbols_table[] = { { TCP_THIN_DUPACK, "TCP_THIN_DUPACK" }, { TCP_USER_TIMEOUT, "TCP_USER_TIMEOUT" }, + { UDPLITE_RECV_CSCOV, "UDPLITE_RECV_CSCOV" }, + { UDPLITE_SEND_CSCOV, "UDPLITE_SEND_CSCOV" }, + { O_RDONLY, "O_RDONLY" }, { O_WRONLY, "O_WRONLY" }, { O_RDWR, "O_RDWR" }, diff --git a/gtests/net/packetdrill/types.h b/gtests/net/packetdrill/types.h index ffb24e00..c3b37cef 100644 --- a/gtests/net/packetdrill/types.h +++ b/gtests/net/packetdrill/types.h @@ -53,6 +53,11 @@ #define __aligned(x) __attribute__ ((aligned(x))) #endif +/* Make sure we have the following constant on all platforms */ +#ifndef IPPROTO_UDPLITE +#define IPPROTO_UDPLITE 136 +#endif + /* We use kernel-style names for standard integer types. */ typedef unsigned char u8; typedef unsigned short u16; -- GitLab