diff --git a/gtests/net/packetdrill/Makefile.common b/gtests/net/packetdrill/Makefile.common index 0ec741f5548a41934f055a55e0b80e5430447963..9ff5027c931ae354cfd5fcdbbbf56634bfa4b312 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 b37dc00b95c1f6b754ff4cb4847055388da0b8b1..06ced251bc6ac59589a7edfe46c0830b5a1afaac 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 43681d2cdfbe83395db852d4a4fcf758b4e37e3b..d4154cd8d7b85f5973a5ae20811171f40cac6baf 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 55ac51e29d5398e491a358e57ef7ab627f16406a..72133a55917cfa40c588a57691add59e99fbe9a7 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 4e1e8b9bfd2c6ef0ca2ae489456f408de99da7a9..53d8b02b0543329760423cd7109ae29a6581ee79 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 4c4216ce81cb4b652e9eda8009bfafb57bf246e9..389b126dd3c2c31266899f5a3b258d819d62b2ca 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 0aec9f00164d6dd760ea92aa39e6a24fd60e348c..3ded8fe59cc32e5111f221fc7e5a62603ff6fd26 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 ecaa8990a56f7ba892b5b71994d411d6206c4254..ba3db691bb23668257c3c9493131aa3faeec0f38 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 09cefdcdf414e51d616393f9830b9154896e3a15..cc57de998593a6e2015746863a668ea865b01cc5 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 c6e36fcb1f5a82f7dccc2108d77114b1979e0301..4c1f1ce8038e88d87892774f3988f2e74ee3bc4d 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 c52bb14392420bb92cce00bf91595676ab23fe6d..bb62f3b69535cf41edf4db9f9d905aef2aaa9787 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 fd4a50ceb9ec1c166e72fb8bf19baaf59ab4f6ad..57f8f73d01754c5ed475dbc3f121c295435eb729 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 d5164b3433fe808a2cd2b5f77d312dfdf4c46c58..f6498f6aa285edfdfe4462e6bfd56d8c6c838da3 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 b1acd3feddc66ecf9766bbf50c7c42762fb1f37b..1bd6d0d30365b91600a372139d2147fb0a1a3e37 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 160be514094ecc2e3bfafe1ad0fb5272fb6df9e8..07805553da28179d17db7a99196b7f1cd3677b16 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 c2545e7d62726b76eae3a0148660503042ea2153..45e49e08b6e33157ddc3a76f35dcc3d1ada56aae 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 cfd503dcea22dab32406e13ea88fcde3efb80fcf..9fc5e5fb7cd69ded9ebe6d4664484291d8e5ad13 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 4d33971027e9e990bed42b70e472a1f7b6e5ec7c..31f7b27c43e17954a704abf36fbd55644e6f4115 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 6bee8a9d07f52c86a9f76eb035901b97b8e700e9..32f18e9c6f4be6c0f1b2836a6642e070564b1ffa 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 160869020876d97723f9110e66c742faa44c6e1c..6296fc9e79c1bc765773561d7f8321055d2c9bd4 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 52d08d3fdc9807eb1b24637f0cde59eb862054ff..070a184bc831cb8e3084959d9085e83c9170cc4c 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 fbf150c9f7b59a11d5c69d68cd52ec6d80f2dd5b..9877c4eb263e91262014f072dc805f02c049bd4e 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 4fd8ea8ad832fa6e5243058aa6ac462edb78a7a8..0b14a7e0fe52c2776d2ecc6f17111d0e49954d00 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 d78a73ec61bfc035309134f802cbd7413075db71..c760b465698006f6b51aab871885edba9a1b1452 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 173474361d05d8623cecc1599e337c76ba8d85b9..518c5322e9ddb48b1009be0178651a9a35132ce9 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 ffb24e001d4f384188acac1d36f71ea823be7f4c..c3b37cef43e6990e142a0ba09d53d1816227569a 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;