From 4be3d393bf3987470f60a962f5797c9e30f7b9d5 Mon Sep 17 00:00:00 2001 From: Michael Tuexen <tuexen@fh-muenster.de> Date: Tue, 23 May 2017 19:49:14 +0200 Subject: [PATCH] Add support for UDP encapsulated transport protocols. This adds support for SCTP/UDP and TCP/UDP including ICMP support. Sponsored by: Netflix, Inc. --- gtests/net/packetdrill/config.c | 14 +- gtests/net/packetdrill/config.h | 5 +- gtests/net/packetdrill/icmp_packet.c | 29 +- gtests/net/packetdrill/icmp_packet.h | 2 + gtests/net/packetdrill/netdev.c | 12 +- gtests/net/packetdrill/netdev.h | 6 +- gtests/net/packetdrill/packet.c | 1 + gtests/net/packetdrill/packet.h | 58 ++- gtests/net/packetdrill/packet_checksum.c | 93 +++- gtests/net/packetdrill/packet_checksum.h | 2 +- gtests/net/packetdrill/packet_parser.c | 120 ++--- gtests/net/packetdrill/packet_parser.h | 4 +- gtests/net/packetdrill/packet_parser_test.c | 127 +++++- gtests/net/packetdrill/packet_to_string.c | 34 +- gtests/net/packetdrill/packet_to_string.h | 2 +- .../net/packetdrill/packet_to_string_test.c | 425 ++++++++++++++++-- gtests/net/packetdrill/parser.y | 173 +++---- gtests/net/packetdrill/run_packet.c | 251 ++++++++--- gtests/net/packetdrill/sctp_packet.c | 85 +++- gtests/net/packetdrill/sctp_packet.h | 16 +- gtests/net/packetdrill/socket.h | 18 +- gtests/net/packetdrill/symbols_freebsd.c | 10 +- gtests/net/packetdrill/tcp_packet.c | 34 +- gtests/net/packetdrill/tcp_packet.h | 2 + gtests/net/packetdrill/wire_client_netdev.c | 2 +- gtests/net/packetdrill/wire_server_netdev.c | 6 +- 26 files changed, 1176 insertions(+), 355 deletions(-) diff --git a/gtests/net/packetdrill/config.c b/gtests/net/packetdrill/config.c index 4df80329..e79061c4 100644 --- a/gtests/net/packetdrill/config.c +++ b/gtests/net/packetdrill/config.c @@ -60,6 +60,7 @@ enum option_codes { OPT_DRY_RUN, OPT_VERBOSE = 'v', /* our only single-letter option */ OPT_DEBUG, + OPT_UDP_ENCAPS, }; /* Specification of command line options for getopt_long(). */ @@ -89,6 +90,7 @@ struct option options[] = { { "dry_run", .has_arg = false, NULL, OPT_DRY_RUN }, { "verbose", .has_arg = false, NULL, OPT_VERBOSE }, { "debug", .has_arg = false, NULL, OPT_DEBUG }, + { "udp_encapsulation", .has_arg = true, NULL, OPT_UDP_ENCAPS }, { NULL }, }; @@ -120,6 +122,7 @@ void show_usage(void) "\t[--dry_run]\n" "\t[--verbose|-v]\n" "\t[--debug] * requires compilation with DEBUG *\n" + "\t[--udp_encapsulation=[sctp,tcp]]\n" "\tscript_path ...\n"); } @@ -365,8 +368,7 @@ static void process_option(int opt, char *optarg, struct config *config, char *end = NULL; unsigned long speed = 0; - DEBUGP("process_option %d ('%c') = %s\n", - opt, (char)opt, optarg); + DEBUGP("process_option %d = %s\n", opt, optarg); switch (opt) { case OPT_IP_VERSION: @@ -475,6 +477,14 @@ static void process_option(int opt, char *optarg, struct config *config, #endif debug_logging = true; break; + case OPT_UDP_ENCAPS: + if (strcmp(optarg, "sctp") == 0) + config->udp_encaps = IPPROTO_SCTP; + else if (strcmp(optarg, "tcp") == 0) + config->udp_encaps = IPPROTO_TCP; + else + die("%s: bad --udp_encapsulation: %s\n", where, optarg); + break; default: show_usage(); exit(EXIT_FAILURE); diff --git a/gtests/net/packetdrill/config.h b/gtests/net/packetdrill/config.h index 55190ce1..d7898261 100644 --- a/gtests/net/packetdrill/config.h +++ b/gtests/net/packetdrill/config.h @@ -36,7 +36,7 @@ #include "script.h" #define TUN_DRIVER_SPEED_CUR 0 /* don't change current speed */ -#define TUN_DRIVER_DEFAULT_MTU 1500 /* default MTU for tun device */ +#define TUN_DRIVER_DEFAULT_MTU 1500 /* default MTU for tun device */ extern struct option options[]; @@ -81,6 +81,9 @@ struct config { bool dry_run; /* parse script but don't execute? */ bool verbose; /* print detailed debug info? */ + + u8 udp_encaps; /* Protocol encapsulated in UDP */ + char *script_path; /* pathname of script file */ /* Shell command to invoke via system(3) to run post-processing code */ diff --git a/gtests/net/packetdrill/icmp_packet.c b/gtests/net/packetdrill/icmp_packet.c index 82533e3c..89d60eb3 100644 --- a/gtests/net/packetdrill/icmp_packet.c +++ b/gtests/net/packetdrill/icmp_packet.c @@ -291,6 +291,8 @@ struct packet *new_icmp_packet(int address_family, u16 udplite_checksum_coverage, u32 sctp_verification_tag, s64 mtu, + u16 udp_src_port, + u16 udp_dst_port, char **error) { s32 type = -1; /* bad type; means "unknown so far" */ @@ -303,10 +305,13 @@ struct packet *new_icmp_packet(int address_family, * header and the first 8 bytes after that (which will * typically have the port info needed to demux the message). */ + const bool encapsulated = ((protocol != IPPROTO_UDP) && + ((udp_src_port > 0) || (udp_dst_port > 0))); const int ip_fixed_bytes = ip_header_min_len(address_family); const int ip_option_bytes = 0; const int ip_header_bytes = ip_fixed_bytes + ip_option_bytes; - const int echoed_bytes = ip_fixed_bytes + ICMP_ECHO_BYTES; + const int echoed_bytes = ip_fixed_bytes + ICMP_ECHO_BYTES + + (encapsulated ? sizeof(struct udp) : 0); const int icmp_bytes = icmp_header_len(address_family) + echoed_bytes; const int ip_bytes = ip_header_bytes + icmp_bytes; @@ -355,16 +360,30 @@ struct packet *new_icmp_packet(int address_family, */ u8 *echoed_ip = packet_echoed_ip_header(packet); const int echoed_ip_bytes = (ip_fixed_bytes + + (encapsulated ? sizeof(struct udp) : 0) + layer4_header_len(protocol) + payload_bytes); - set_ip_header(echoed_ip, address_family, echoed_ip_bytes, - ecn, protocol); + if (encapsulated) { + struct udp *udp; + + set_ip_header(echoed_ip, address_family, echoed_ip_bytes, + ecn, IPPROTO_UDP); + udp = (struct udp *)(echoed_ip + ip_fixed_bytes); + udp->src_port = htons(udp_src_port); + udp->dst_port = htons(udp_dst_port); + udp->len = htons(sizeof(struct udp) + + layer4_header_len(protocol) + + payload_bytes); + } else { + set_ip_header(echoed_ip, address_family, echoed_ip_bytes, + ecn, protocol); + } if (protocol == IPPROTO_SCTP) { - u32 *v_tag = packet_echoed_sctp_v_tag(packet); + u32 *v_tag = packet_echoed_sctp_v_tag(packet, encapsulated); *v_tag = htonl(sctp_verification_tag); } if (protocol == IPPROTO_TCP) { - u32 *seq = packet_echoed_tcp_seq(packet); + u32 *seq = packet_echoed_tcp_seq(packet, encapsulated); *seq = htonl(tcp_start_sequence); } if (protocol == IPPROTO_UDP) { diff --git a/gtests/net/packetdrill/icmp_packet.h b/gtests/net/packetdrill/icmp_packet.h index 609d3a66..6f20fc90 100644 --- a/gtests/net/packetdrill/icmp_packet.h +++ b/gtests/net/packetdrill/icmp_packet.h @@ -50,6 +50,8 @@ extern struct packet *new_icmp_packet(int address_family, u16 udplite_checksum_coverage, u32 sctp_verification_tag, s64 mtu, + u16 udp_src_port, + u16 udp_dst_port, char **error); #endif /* __ICMP_PACKET_H__ */ diff --git a/gtests/net/packetdrill/netdev.c b/gtests/net/packetdrill/netdev.c index d4ac7c80..18bdefb8 100644 --- a/gtests/net/packetdrill/netdev.c +++ b/gtests/net/packetdrill/netdev.c @@ -405,10 +405,10 @@ static void local_netdev_read_queue(struct local_netdev *netdev, else die_perror("tun read()"); } - } + } } -static int local_netdev_receive(struct netdev *a_netdev, +static int local_netdev_receive(struct netdev *a_netdev, u8 udp_encaps, struct packet **packet, char **error) { struct local_netdev *netdev = to_local_netdev(a_netdev); @@ -417,14 +417,15 @@ static int local_netdev_receive(struct netdev *a_netdev, DEBUGP("local_netdev_receive\n"); - status = netdev_receive_loop(netdev->psock, DIRECTION_OUTBOUND, packet, - &num_packets, error); + status = netdev_receive_loop(netdev->psock, DIRECTION_OUTBOUND, + udp_encaps,packet, &num_packets, error); local_netdev_read_queue(netdev, num_packets); return status; } int netdev_receive_loop(struct packet_socket *psock, enum direction_t direction, + u8 udp_encaps, struct packet **packet, int *num_packets, char **error) @@ -446,7 +447,8 @@ int netdev_receive_loop(struct packet_socket *psock, continue; ++*num_packets; - result = parse_packet(*packet, in_bytes, ether_type, error); + result = parse_packet(*packet, in_bytes, ether_type, udp_encaps, + error); if (result == PACKET_OK) return STATUS_OK; diff --git a/gtests/net/packetdrill/netdev.h b/gtests/net/packetdrill/netdev.h index 115c3453..3e479678 100644 --- a/gtests/net/packetdrill/netdev.h +++ b/gtests/net/packetdrill/netdev.h @@ -52,7 +52,7 @@ struct netdev_ops { * pointer to the newly-allocated packet. Caller must free the packet * with packet_free(). */ - int (*receive)(struct netdev *netdev, + int (*receive)(struct netdev *netdev, u8 udp_encaps, struct packet **packet, char **error); }; @@ -75,10 +75,11 @@ static inline int netdev_send(struct netdev *netdev, * with packet_free(). */ static inline int netdev_receive(struct netdev *netdev, + u8 udp_encaps, struct packet **packet, char **error) { - return netdev->ops->receive(netdev, packet, error); + return netdev->ops->receive(netdev, udp_encaps, packet, error); } @@ -88,6 +89,7 @@ static inline int netdev_receive(struct netdev *netdev, */ extern int netdev_receive_loop(struct packet_socket *psock, enum direction_t direction, + u8 udp_encaps, struct packet **packet, int *num_packets, char **error); diff --git a/gtests/net/packetdrill/packet.c b/gtests/net/packetdrill/packet.c index 406e3465..ebaebd5e 100644 --- a/gtests/net/packetdrill/packet.c +++ b/gtests/net/packetdrill/packet.c @@ -33,6 +33,7 @@ #include "ip_packet.h" #include "mpls_packet.h" #include "sctp_packet.h" +#include "udp_packet.h" /* Info for all types of header we support. */ diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h index a7510b34..db09a339 100644 --- a/gtests/net/packetdrill/packet.h +++ b/gtests/net/packetdrill/packet.h @@ -216,14 +216,18 @@ static inline int ip_header_min_len(int address_family) } /* Return the layer4 protocol of the packet. */ -static inline int packet_ip_protocol(const struct packet *packet) +static inline int packet_ip_protocol(const struct packet *packet, u8 udp_encaps) { + int protocol = 0; + + assert(packet->ipv4 != NULL || packet->ipv6 != NULL); if (packet->ipv4 != NULL) - return packet->ipv4->protocol; + protocol = packet->ipv4->protocol; if (packet->ipv6 != NULL) - return packet->ipv6->next_header; - assert(!"no valid IP header"); - return 0; + protocol = packet->ipv6->next_header; + if (protocol == IPPROTO_UDP && udp_encaps != 0) + protocol = udp_encaps; + return protocol; } /* Return the length of an optionless TCP or UDP header. */ @@ -381,27 +385,37 @@ static inline int packet_echoed_ip_protocol(struct packet *packet) } /* Return the location of the transport header echoed by an ICMP message. */ -static inline u8 *packet_echoed_layer4_header(struct packet *packet) +static inline u8 *packet_echoed_layer4_header(struct packet *packet, bool encapsulated) { u8 *echoed_ip = packet_echoed_ip_header(packet); int ip_header_len = packet_echoed_ip_header_len(packet); - return echoed_ip + ip_header_len; + if (packet_echoed_ip_protocol(packet) == IPPROTO_UDP && encapsulated == true) { + return echoed_ip + ip_header_len + sizeof(struct udp); + } else { + return echoed_ip + ip_header_len; + } } /* Return the location of the SCTP common header echoed by an ICMP message. */ static inline struct sctp_common_header * -packet_echoed_sctp_header(struct packet *packet) +packet_echoed_sctp_header(struct packet *packet, bool encapsulated) { - if (packet_echoed_ip_protocol(packet) == IPPROTO_SCTP) + int protocol; + + protocol = packet_echoed_ip_protocol(packet); + if (protocol == IPPROTO_UDP && encapsulated == true) { + protocol = IPPROTO_SCTP; + } + if (protocol == IPPROTO_SCTP) return (struct sctp_common_header *) - (packet_echoed_layer4_header(packet)); + (packet_echoed_layer4_header(packet, encapsulated)); return NULL; } /* Return the location of the SCTP verification tag echoed by an ICMP message. */ -static inline u32 *packet_echoed_sctp_v_tag(struct packet *packet) +static inline u32 *packet_echoed_sctp_v_tag(struct packet *packet, bool encapsulated) { - struct sctp_common_header *echoed_sctp = packet_echoed_sctp_header(packet); + struct sctp_common_header *echoed_sctp = packet_echoed_sctp_header(packet, encapsulated); assert(echoed_sctp); u32 *v_tag = &(echoed_sctp->v_tag); /* Check that the v_tag field is actually in the space we @@ -412,10 +426,16 @@ static inline u32 *packet_echoed_sctp_v_tag(struct packet *packet) } /* Return the location of the TCP header echoed by an ICMP message. */ -static inline struct tcp *packet_echoed_tcp_header(struct packet *packet) +static inline struct tcp *packet_echoed_tcp_header(struct packet *packet, bool encapsulated) { - if (packet_echoed_ip_protocol(packet) == IPPROTO_TCP) - return (struct tcp *)(packet_echoed_layer4_header(packet)); + int protocol; + + protocol = packet_echoed_ip_protocol(packet); + if (protocol == IPPROTO_UDP && encapsulated == true) { + protocol = IPPROTO_TCP; + } + if (protocol == IPPROTO_TCP) + return (struct tcp *)(packet_echoed_layer4_header(packet, encapsulated)); return NULL; } @@ -423,7 +443,7 @@ static inline struct tcp *packet_echoed_tcp_header(struct packet *packet) static inline struct udp *packet_echoed_udp_header(struct packet *packet) { if (packet_echoed_ip_protocol(packet) == IPPROTO_UDP) - return (struct udp *)(packet_echoed_layer4_header(packet)); + return (struct udp *)(packet_echoed_layer4_header(packet, 0)); return NULL; } @@ -432,14 +452,14 @@ 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 (struct udplite *)(packet_echoed_layer4_header(packet, 0)); 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) +static inline u32 *packet_echoed_tcp_seq(struct packet *packet, bool encapsulated) { - struct tcp *echoed_tcp = packet_echoed_tcp_header(packet); + struct tcp *echoed_tcp = packet_echoed_tcp_header(packet, encapsulated); assert(echoed_tcp); u32 *seq = &(echoed_tcp->seq); /* Check that the seq field is actually in the space we diff --git a/gtests/net/packetdrill/packet_checksum.c b/gtests/net/packetdrill/packet_checksum.c index a81a3d05..a5853e28 100644 --- a/gtests/net/packetdrill/packet_checksum.c +++ b/gtests/net/packetdrill/packet_checksum.c @@ -31,7 +31,7 @@ #include "ipv6.h" #include "tcp.h" -static void checksum_ipv4_packet(struct packet *packet) +static void checksum_ipv4_packet(struct packet *packet, u8 udp_encaps) { struct ipv4 *ipv4 = packet->ipv4; @@ -47,19 +47,52 @@ static void checksum_ipv4_packet(struct packet *packet) /* Fill in IPv4-based layer 4 checksum. */ if (packet->sctp != NULL) { struct sctp_common_header *sctp = packet->sctp; + struct udp *udp; + sctp->crc32c = htonl(0); - sctp->crc32c = sctp_crc32c(sctp, l4_bytes); + if (udp_encaps == IPPROTO_SCTP) { + sctp->crc32c = sctp_crc32c(sctp, l4_bytes - sizeof(struct udp)); + } else { + sctp->crc32c = sctp_crc32c(sctp, l4_bytes); + } if (packet->flags & FLAGS_SCTP_BAD_CRC32C) { sctp->crc32c = htonl(ntohl(sctp->crc32c) + 1); } + if (udp_encaps == IPPROTO_SCTP) { + udp = ((struct udp *)sctp) - 1; + udp->check = 0; + udp->check = tcp_udp_v4_checksum(ipv4->src_ip, + ipv4->dst_ip, + IPPROTO_UDP, udp, + l4_bytes); + } } else if (packet->tcp != NULL) { struct tcp *tcp = packet->tcp; + struct udp *udp; + tcp->check = 0; - tcp->check = tcp_udp_v4_checksum(ipv4->src_ip, - ipv4->dst_ip, - IPPROTO_TCP, tcp, l4_bytes); + if (udp_encaps == IPPROTO_TCP) { + tcp->check = tcp_udp_v4_checksum(ipv4->src_ip, + ipv4->dst_ip, + IPPROTO_TCP, tcp, + l4_bytes - sizeof(struct udp)); + } else { + tcp->check = tcp_udp_v4_checksum(ipv4->src_ip, + ipv4->dst_ip, + IPPROTO_TCP, tcp, + l4_bytes); + } + if (udp_encaps == IPPROTO_TCP) { + udp = ((struct udp *)tcp) - 1; + udp->check = 0; + udp->check = tcp_udp_v4_checksum(ipv4->src_ip, + ipv4->dst_ip, + IPPROTO_UDP, udp, + l4_bytes); + } } else if (packet->udp != NULL) { struct udp *udp = packet->udp; + udp->check = 0; udp->check = tcp_udp_v4_checksum(ipv4->src_ip, ipv4->dst_ip, @@ -79,6 +112,7 @@ static void checksum_ipv4_packet(struct packet *packet) l4_bytes, coverage); } else if (packet->icmpv4 != NULL) { struct icmpv4 *icmpv4 = packet->icmpv4; + icmpv4->checksum = 0; icmpv4->checksum = ipv4_checksum(icmpv4, l4_bytes); } else { @@ -86,7 +120,7 @@ static void checksum_ipv4_packet(struct packet *packet) } } -static void checksum_ipv6_packet(struct packet *packet) +static void checksum_ipv6_packet(struct packet *packet, u8 udp_encaps) { struct ipv6 *ipv6 = packet->ipv6; @@ -101,19 +135,52 @@ static void checksum_ipv6_packet(struct packet *packet) /* Fill in IPv6-based layer 4 checksum. */ if (packet->sctp != NULL) { struct sctp_common_header *sctp = packet->sctp; + struct udp *udp; + sctp->crc32c = htonl(0); - sctp->crc32c = sctp_crc32c(sctp, l4_bytes); + if (udp_encaps == IPPROTO_SCTP) { + sctp->crc32c = sctp_crc32c(sctp, l4_bytes - sizeof(struct udp)); + } else { + sctp->crc32c = sctp_crc32c(sctp, l4_bytes); + } if (packet->flags & FLAGS_SCTP_BAD_CRC32C) { sctp->crc32c = htonl(ntohl(sctp->crc32c) + 1); } + if (udp_encaps == IPPROTO_SCTP) { + udp = ((struct udp *)sctp) - 1; + udp->check = 0; + udp->check = tcp_udp_v6_checksum(&ipv6->src_ip, + &ipv6->dst_ip, + IPPROTO_UDP, udp, + l4_bytes); + } } else if (packet->tcp != NULL) { struct tcp *tcp = packet->tcp; + struct udp *udp; + tcp->check = 0; - tcp->check = tcp_udp_v6_checksum(&ipv6->src_ip, - &ipv6->dst_ip, - IPPROTO_TCP, tcp, l4_bytes); + if (udp_encaps == IPPROTO_TCP) { + tcp->check = tcp_udp_v6_checksum(&ipv6->src_ip, + &ipv6->dst_ip, + IPPROTO_TCP, tcp, + l4_bytes - sizeof(struct udp)); + } else { + tcp->check = tcp_udp_v6_checksum(&ipv6->src_ip, + &ipv6->dst_ip, + IPPROTO_TCP, tcp, + l4_bytes); + } + if (udp_encaps == IPPROTO_TCP) { + udp = ((struct udp *)tcp) - 1; + udp->check = 0; + udp->check = tcp_udp_v6_checksum(&ipv6->src_ip, + &ipv6->dst_ip, + IPPROTO_UDP, udp, + l4_bytes); + } } else if (packet->udp != NULL) { struct udp *udp = packet->udp; + udp->check = 0; udp->check = tcp_udp_v6_checksum(&ipv6->src_ip, &ipv6->dst_ip, @@ -144,13 +211,13 @@ static void checksum_ipv6_packet(struct packet *packet) } } -void checksum_packet(struct packet *packet) +void checksum_packet(struct packet *packet, u8 udp_encaps) { int address_family = packet_address_family(packet); if (address_family == AF_INET) - return checksum_ipv4_packet(packet); + return checksum_ipv4_packet(packet, udp_encaps); else if (address_family == AF_INET6) - return checksum_ipv6_packet(packet); + return checksum_ipv6_packet(packet, udp_encaps); else assert(!"bad ip version"); } diff --git a/gtests/net/packetdrill/packet_checksum.h b/gtests/net/packetdrill/packet_checksum.h index 2c87df3e..2eee17c3 100644 --- a/gtests/net/packetdrill/packet_checksum.h +++ b/gtests/net/packetdrill/packet_checksum.h @@ -28,6 +28,6 @@ #include "packet.h" /* Fill in layer 3 and layer 4 checksums for the given input 'packet'. */ -extern void checksum_packet(struct packet *packet); +extern void checksum_packet(struct packet *packet, u8 udp_encaps); #endif /* __PACKET_CHECKSUM_H__ */ diff --git a/gtests/net/packetdrill/packet_parser.c b/gtests/net/packetdrill/packet_parser.c index 03209545..008b01d7 100644 --- a/gtests/net/packetdrill/packet_parser.c +++ b/gtests/net/packetdrill/packet_parser.c @@ -44,22 +44,24 @@ #include "packet.h" #include "tcp.h" -static int parse_ipv4(struct packet *packet, u8 *header_start, u8 *packet_end, - char **error); -static int parse_ipv6(struct packet *packet, u8 *header_start, u8 *packet_end, - char **error); -static int parse_mpls(struct packet *packet, u8 *header_start, u8 *packet_end, - char **error); +static int parse_ipv4(struct packet *packet, u8 udp_encaps, + u8 *header_start, u8 *packet_end, char **error); +static int parse_ipv6(struct packet *packet, u8 udp_encaps, + u8 *header_start, u8 *packet_end, char **error); +static int parse_mpls(struct packet *packet, u8 udp_encaps, + u8 *header_start, u8 *packet_end, char **error); static int parse_layer3_packet_by_proto(struct packet *packet, - u16 proto, u8 *header_start, - u8 *packet_end, char **error); -static int parse_layer4(struct packet *packet, u8 *header_start, + u16 proto, u8 udp_encaps, + u8 *header_start, u8 *packet_end, + char **error); +static int parse_layer4(struct packet *packet, u8 udp_encaps, u8 *header_start, int layer4_protocol, int layer4_bytes, u8 *packet_end, bool *is_inner, char **error); static int parse_layer3_packet_by_proto(struct packet *packet, - u16 proto, u8 *header_start, - u8 *packet_end, char **error) + u16 proto, u8 udp_encaps, + u8 *header_start, u8 *packet_end, + char **error) { u8 *p = header_start; @@ -77,7 +79,8 @@ static int parse_layer3_packet_by_proto(struct packet *packet, */ ip = (struct ipv4 *)p; if (ip->version == 4) { - return parse_ipv4(packet, p, packet_end, error); + return parse_ipv4(packet, udp_encaps, p, packet_end, + error); } else { asprintf(error, "Bad IP version for ETHERTYPE_IP"); goto error_out; @@ -96,14 +99,15 @@ static int parse_layer3_packet_by_proto(struct packet *packet, */ ip = (struct ipv6 *)p; if (ip->version == 6) { - return parse_ipv6(packet, p, packet_end, error); + return parse_ipv6(packet, udp_encaps, p, packet_end, + error); } else { asprintf(error, "Bad IP version for ETHERTYPE_IPV6"); goto error_out; } } else if ((proto == ETHERTYPE_MPLS_UC) || (proto == ETHERTYPE_MPLS_MC)) { - return parse_mpls(packet, p, packet_end, error); + return parse_mpls(packet, udp_encaps, p, packet_end, error); } else { return PACKET_UNKNOWN_L4; } @@ -112,7 +116,7 @@ error_out: return PACKET_BAD; } -static int parse_layer3_packet(struct packet *packet, +static int parse_layer3_packet(struct packet *packet, u8 udp_encaps, u8 *header_start, u8 *packet_end, char **error) { @@ -131,16 +135,16 @@ static int parse_layer3_packet(struct packet *packet, */ ip = (struct ipv4 *) (p); if (ip->version == 4) - return parse_ipv4(packet, p, packet_end, error); + return parse_ipv4(packet, udp_encaps, p, packet_end, error); else if (ip->version == 6) - return parse_ipv6(packet, p, packet_end, error); + return parse_ipv6(packet, udp_encaps, p, packet_end, error); asprintf(error, "Unsupported IP version"); return PACKET_BAD; } int parse_packet(struct packet *packet, int in_bytes, - u16 ether_type, char **error) + u16 ether_type, u8 udp_encaps, char **error) { assert(in_bytes <= packet->buffer_bytes); char *message = NULL; /* human-readable error summary */ @@ -150,7 +154,7 @@ int parse_packet(struct packet *packet, int in_bytes, /* packet_end points to the byte beyond the end of packet. */ u8 *packet_end = packet->buffer + in_bytes; - result = parse_layer3_packet_by_proto(packet, ether_type, + result = parse_layer3_packet_by_proto(packet, ether_type, udp_encaps, header_start, packet_end, error); if (result != PACKET_BAD) @@ -170,8 +174,8 @@ int parse_packet(struct packet *packet, int in_bytes, * packet_parse_result_t. * Note that packet_end points to the byte beyond the end of packet. */ -static int parse_ipv4(struct packet *packet, u8 *header_start, u8 *packet_end, - char **error) +static int parse_ipv4(struct packet *packet, u8 udp_encaps, + u8 *header_start, u8 *packet_end, char **error) { struct header *ip_header = NULL; u8 *p = header_start; @@ -247,8 +251,8 @@ static int parse_ipv4(struct packet *packet, u8 *header_start, u8 *packet_end, /* Examine the L4 header. */ const int layer4_bytes = ip_total_bytes - ip_header_bytes; const int layer4_protocol = ipv4->protocol; - result = parse_layer4(packet, p, layer4_protocol, layer4_bytes, - packet_end, &is_inner, error); + result = parse_layer4(packet, udp_encaps, p, layer4_protocol, + layer4_bytes, packet_end, &is_inner, error); /* If this is the innermost IP header then this is the primary. */ if (is_inner) @@ -268,8 +272,8 @@ error_out: * protocol other than TCP. Return a packet_parse_result_t. * Note that packet_end points to the byte beyond the end of packet. */ -static int parse_ipv6(struct packet *packet, u8 *header_start, u8 *packet_end, - char **error) +static int parse_ipv6(struct packet *packet, u8 udp_encaps, + u8 *header_start, u8 *packet_end, char **error) { struct header *ip_header = NULL; u8 *p = header_start; @@ -321,8 +325,8 @@ static int parse_ipv6(struct packet *packet, u8 *header_start, u8 *packet_end, /* Examine the L4 header. */ const int layer4_bytes = ip_total_bytes - ip_header_bytes; const int layer4_protocol = ipv6->next_header; - result = parse_layer4(packet, p, layer4_protocol, layer4_bytes, - packet_end, &is_inner, error); + result = parse_layer4(packet, udp_encaps, p, layer4_protocol, + layer4_bytes, packet_end, &is_inner, error); /* If this is the innermost IP header then this is the primary. */ if (is_inner) @@ -422,19 +426,21 @@ error_out: } /* Parse the UDP header. Return a packet_parse_result_t. */ -static int parse_udp(struct packet *packet, u8 *layer4_start, int layer4_bytes, - u8 *packet_end, char **error) +static int parse_udp(struct packet *packet, u8 udp_encaps, + u8 *layer4_start, int layer4_bytes, u8 *packet_end, + char **error) { struct header *udp_header = NULL; u8 *p = layer4_start; + struct udp *udp; assert(layer4_bytes >= 0); if (layer4_bytes < sizeof(struct udp)) { asprintf(error, "Truncated UDP header"); goto error_out; } - packet->udp = (struct udp *) p; - const int udp_len = ntohs(packet->udp->len); + udp = (struct udp *) p; + const int udp_len = ntohs(udp->len); const int udp_header_len = sizeof(struct udp); if (udp_len < udp_header_len) { asprintf(error, "UDP datagram length too small for UDP header"); @@ -456,12 +462,23 @@ static int parse_udp(struct packet *packet, u8 *layer4_start, int layer4_bytes, } udp_header->total_bytes = layer4_bytes; - p += layer4_bytes; - assert(p <= packet_end); - - DEBUGP("UDP src port: %d\n", ntohs(packet->udp->src_port)); - DEBUGP("UDP dst port: %d\n", ntohs(packet->udp->dst_port)); - return PACKET_OK; + DEBUGP("UDP src port: %d\n", ntohs(udp->src_port)); + DEBUGP("UDP dst port: %d\n", ntohs(udp->dst_port)); + if (udp_encaps == IPPROTO_SCTP) + return parse_sctp(packet, p + udp_header_len, + layer4_bytes - udp_header_len, + packet_end, error); + else if (udp_encaps == IPPROTO_TCP) + return parse_tcp(packet, p + udp_header_len, + layer4_bytes - udp_header_len, + packet_end, error); + else { + assert(udp_encaps == 0); + packet->udp = udp; + p += layer4_bytes; + assert(p <= packet_end); + return PACKET_OK; + } error_out: return PACKET_BAD; @@ -585,8 +602,9 @@ error_out: } /* Parse the GRE header. Return a packet_parse_result_t. */ -static int parse_gre(struct packet *packet, u8 *layer4_start, int layer4_bytes, - u8 *packet_end, char **error) +static int parse_gre(struct packet *packet, u8 udp_encaps, + u8 *layer4_start, int layer4_bytes, u8 *packet_end, + char **error) { struct header *gre_header = NULL; u8 *p = layer4_start; @@ -629,14 +647,14 @@ static int parse_gre(struct packet *packet, u8 *layer4_start, int layer4_bytes, p += gre_header_len; assert(p <= packet_end); return parse_layer3_packet_by_proto(packet, ntohs(gre->protocol), - p, packet_end, error); + udp_encaps, p, packet_end, error); error_out: return PACKET_BAD; } -static int parse_mpls(struct packet *packet, u8 *header_start, u8 *packet_end, - char **error) +static int parse_mpls(struct packet *packet, u8 udp_encaps, + u8 *header_start, u8 *packet_end, char **error) { struct header *mpls_header = NULL; u8 *p = header_start; @@ -670,13 +688,13 @@ static int parse_mpls(struct packet *packet, u8 *header_start, u8 *packet_end, /* Move on to the header inside the MPLS label stack. */ assert(p <= packet_end); - return parse_layer3_packet(packet, p, packet_end, error); + return parse_layer3_packet(packet, udp_encaps, p, packet_end, error); error_out: return PACKET_BAD; } -static int parse_layer4(struct packet *packet, u8 *layer4_start, +static int parse_layer4(struct packet *packet, u8 udp_encaps, u8 *layer4_start, int layer4_protocol, int layer4_bytes, u8 *packet_end, bool *is_inner, char **error) { @@ -690,8 +708,8 @@ static int parse_layer4(struct packet *packet, u8 *layer4_start, error); } else if (layer4_protocol == IPPROTO_UDP) { *is_inner = true; /* found inner-most layer 4 */ - return parse_udp(packet, layer4_start, layer4_bytes, packet_end, - error); + return parse_udp(packet, udp_encaps, 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, @@ -706,14 +724,16 @@ static int parse_layer4(struct packet *packet, u8 *layer4_start, packet_end, error); } else if (layer4_protocol == IPPROTO_GRE) { *is_inner = false; - return parse_gre(packet, layer4_start, layer4_bytes, packet_end, - error); + return parse_gre(packet, udp_encaps, layer4_start, layer4_bytes, + packet_end, error); } else if (layer4_protocol == IPPROTO_IPIP) { *is_inner = false; - return parse_ipv4(packet, layer4_start, packet_end, error); + return parse_ipv4(packet, udp_encaps, layer4_start, packet_end, + error); } else if (layer4_protocol == IPPROTO_IPV6) { *is_inner = false; - return parse_ipv6(packet, layer4_start, packet_end, error); + return parse_ipv6(packet, udp_encaps, layer4_start, packet_end, + error); } return PACKET_UNKNOWN_L4; } diff --git a/gtests/net/packetdrill/packet_parser.h b/gtests/net/packetdrill/packet_parser.h index 07805553..3051d224 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 or UDPLite */ + PACKET_UNKNOWN_L4, /* not SCTP or TCP or UDP or UDPLite */ }; /* Given an input packet of length 'in_bytes' stored in the buffer @@ -42,6 +42,6 @@ enum packet_parse_result_t { * error message. */ int parse_packet(struct packet *packet, int in_bytes, - u16 ether_type, char **error); + u16 ether_type, u8 udp_encaps, char **error); #endif /* __PACKET_PARSER_H__ */ diff --git a/gtests/net/packetdrill/packet_parser_test.c b/gtests/net/packetdrill/packet_parser_test.c index 07ef9592..5b216f07 100644 --- a/gtests/net/packetdrill/packet_parser_test.c +++ b/gtests/net/packetdrill/packet_parser_test.c @@ -29,7 +29,7 @@ #include <stdlib.h> #include <string.h> -int debug_logging=0; +int debug_logging = 0; static void test_parse_sctp_ipv4_packet(void) { @@ -50,7 +50,7 @@ static void test_parse_sctp_ipv4_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -96,7 +96,7 @@ static void test_parse_sctp_ipv6_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -121,6 +121,105 @@ static void test_parse_sctp_ipv6_packet(void) packet_free(packet); } +static void test_parse_sctp_udp_ipv4_packet(void) +{ + /* A SCTP/UDP/IPv4 packet. */ + u8 data[] = { + /* 1.1.1.1:1234 > 192.168.0.1:60213 + * udp(9899 > 9899): sctp: ABORT[] */ + 0x45, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x11, 0xf9, 0x15, 0x01, 0x01, 0x01, 0x01, + 0xc0, 0xa8, 0x00, 0x01, 0x26, 0xab, 0x26, 0xab, + 0x00, 0x18, 0x00, 0x00, 0x04, 0xd2, 0xeb, 0x35, + 0x01, 0x02, 0x03, 0x04, 0xab, 0x0c, 0xeb, 0x7a, + 0x06, 0x01, 0x00, 0x04 + }; + + 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), + ETHERTYPE_IP, IPPROTO_SCTP, &error); + assert(result == PACKET_OK); + assert(error == NULL); + + struct ipv4 *expected_ipv4 = (struct ipv4 *)(packet->buffer); + struct udp *expected_udp = + (struct udp *)(expected_ipv4 + 1); + struct sctp_common_header *expected_sctp = + (struct sctp_common_header *)(expected_udp + 1); + + assert(packet->ip_bytes == sizeof(data)); + assert(packet->ipv4 == expected_ipv4); + assert(packet->ipv6 == NULL); + assert(packet->sctp == expected_sctp); + assert(packet->tcp == NULL); + assert(packet->udp == NULL); + 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_sctp_udp_ipv6_packet(void) +{ + /* A SCTP/UDP/IPv6 packet. */ + u8 data[] = { + /* 2001:db8::1:54242 > fd3d:fa7b:d17d::1:8080 + * udp(9899->9899): sctp: ABORT[] */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x11, 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, + 0x26, 0xab, 0x26, 0xab, 0x00, 0x18, 0x00, 0x00, + 0xd3, 0xe2, 0x1f, 0x90, 0x01, 0x02, 0x03, 0x04, + 0xc4, 0xb0, 0x47, 0x64, 0x06, 0x01, 0x00, 0x04 + }; + + 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), + ETHERTYPE_IPV6, IPPROTO_SCTP, &error); + assert(result == PACKET_OK); + assert(error == NULL); + + struct ipv6 *expected_ipv6 = (struct ipv6 *)(packet->buffer); + struct udp *expected_udp = + (struct udp *)(expected_ipv6 + 1); + struct sctp_common_header *expected_sctp = + (struct sctp_common_header *)(expected_udp + 1); + + assert(packet->ip_bytes == sizeof(data)); + assert(packet->ipv4 == NULL); + assert(packet->ipv6 == expected_ipv6); + assert(packet->sctp == expected_sctp); + assert(packet->tcp == NULL); + assert(packet->udp == NULL); + 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_tcp_ipv4_packet(void) { /* A TCP/IPv4 packet. */ @@ -145,7 +244,7 @@ static void test_parse_tcp_ipv4_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -193,7 +292,7 @@ static void test_parse_tcp_ipv6_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -234,7 +333,7 @@ static void test_parse_udp_ipv4_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -278,7 +377,7 @@ static void test_parse_udp_ipv6_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -320,7 +419,7 @@ static void test_parse_udplite_ipv4_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -366,7 +465,7 @@ static void test_parse_udplite_ipv6_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -418,7 +517,7 @@ static void test_parse_ipv4_gre_ipv4_tcp_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -510,7 +609,7 @@ static void test_parse_ipv4_gre_mpls_ipv4_tcp_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -588,7 +687,7 @@ static void test_parse_icmpv4_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -642,7 +741,7 @@ static void test_parse_icmpv6_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -670,6 +769,8 @@ int main(void) { test_parse_sctp_ipv4_packet(); test_parse_sctp_ipv6_packet(); + test_parse_sctp_udp_ipv4_packet(); + test_parse_sctp_udp_ipv6_packet(); test_parse_tcp_ipv4_packet(); test_parse_tcp_ipv6_packet(); test_parse_udp_ipv4_packet(); diff --git a/gtests/net/packetdrill/packet_to_string.c b/gtests/net/packetdrill/packet_to_string.c index 9b1dd53e..c74fb542 100644 --- a/gtests/net/packetdrill/packet_to_string.c +++ b/gtests/net/packetdrill/packet_to_string.c @@ -120,7 +120,7 @@ static int mpls_header_to_string(FILE *s, struct packet *packet, int layer, return STATUS_OK; } -static int sctp_packet_to_string(FILE *s, struct packet *packet, +static int sctp_packet_to_string(FILE *s, struct packet *packet, int i, enum dump_format_t format, char **error) { struct sctp_chunks_iterator iter; @@ -138,6 +138,13 @@ static int sctp_packet_to_string(FILE *s, struct packet *packet, if (packet->flags & FLAGS_SCTP_BAD_CRC32C) { fputs("(bad_crc32c)", s); } + + if (packet->headers[i + 1].type == HEADER_UDP) { + struct udp *udp = packet->headers[i + 1].h.udp; + + fprintf(s, "/udp(%u > %u)", + ntohs(udp->src_port), ntohs(udp->dst_port)); + } fputc(':', s); index = 0; @@ -167,7 +174,7 @@ static int sctp_packet_to_string(FILE *s, struct packet *packet, /* Print a string representation of the TCP packet: * direction opt_ip_info flags seq ack window tcp_options */ -static int tcp_packet_to_string(FILE *s, struct packet *packet, +static int tcp_packet_to_string(FILE *s, struct packet *packet, int i, enum dump_format_t format, char **error) { int result = STATUS_OK; /* return value */ @@ -177,7 +184,6 @@ static int tcp_packet_to_string(FILE *s, struct packet *packet, fputc(' ', s); } - /* We print flags in the same order as tcpdump 4.1.1. */ if (packet->tcp->fin) fputc('F', s); @@ -216,6 +222,13 @@ static int tcp_packet_to_string(FILE *s, struct packet *packet, free(tcp_options); } + if (packet->headers[i + 1].type == HEADER_UDP) { + struct udp *udp = packet->headers[i + 1].h.udp; + + fprintf(s, "/udp(%u > %u)", + ntohs(udp->src_port), ntohs(udp->dst_port)); + } + if (format == DUMP_VERBOSE) packet_buffer_to_string(s, packet); @@ -297,7 +310,7 @@ static int encap_header_to_string(FILE *s, struct packet *packet, int layer, return printer(s, packet, layer, format, error); } -int packet_to_string(struct packet *packet, +int packet_to_string(struct packet *packet, u8 udp_encaps, enum dump_format_t format, char **ascii_string, char **error) { @@ -307,9 +320,14 @@ int packet_to_string(struct packet *packet, FILE *s = open_memstream(ascii_string, &size); /* output string */ int i; int header_count = packet_header_count(packet); + int limit; /* Print any encapsulation headers preceding layer 3 and 4 headers. */ - for (i = 0; i < header_count - 2; ++i) { + if (udp_encaps == 0) + limit = header_count - 2; + else + limit = header_count - 3; + for (i = 0; i < limit; ++i) { if (packet->headers[i].type == HEADER_NONE) break; if (encap_header_to_string(s, packet, i, format, error)) @@ -320,10 +338,12 @@ int packet_to_string(struct packet *packet, fputs("[NO IP HEADER]", s); } else { if (packet->sctp != NULL) { - if (sctp_packet_to_string(s, packet, format, error)) + if (sctp_packet_to_string(s, packet, limit, format, + error)) goto out; } else if (packet->tcp != NULL) { - if (tcp_packet_to_string(s, packet, format, error)) + if (tcp_packet_to_string(s, packet, limit, format, + error)) goto out; } else if (packet->udp != NULL) { if (udp_packet_to_string(s, packet, format, error)) diff --git a/gtests/net/packetdrill/packet_to_string.h b/gtests/net/packetdrill/packet_to_string.h index 462a4f9e..3a4fb40d 100644 --- a/gtests/net/packetdrill/packet_to_string.h +++ b/gtests/net/packetdrill/packet_to_string.h @@ -37,7 +37,7 @@ enum dump_format_t { * packet 'packet'. Returns STATUS_OK on success; on failure returns * STATUS_ERR and sets error message. */ -extern int packet_to_string(struct packet *packet, +extern int packet_to_string(struct packet *packet, u8 udp_encaps, enum dump_format_t format, char **ascii_string, char **error); diff --git a/gtests/net/packetdrill/packet_to_string_test.c b/gtests/net/packetdrill/packet_to_string_test.c index 560aa24d..2ad71b44 100644 --- a/gtests/net/packetdrill/packet_to_string_test.c +++ b/gtests/net/packetdrill/packet_to_string_test.c @@ -31,7 +31,8 @@ #include "packet_parser.h" #include "logging.h" -int debug_logging=0; +int debug_logging = 0; +#define DEBUG_LOGGING 1 static void test_sctp_ipv4_packet_to_string(void) { @@ -54,7 +55,12 @@ static void test_sctp_ipv4_packet_to_string(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error); +#if DEBUG_LOGGING == 1 + if (result != PACKET_OK) { + printf("error was: %s\n", error); + } +#endif assert(result == PACKET_OK); assert(error == NULL); @@ -62,7 +68,7 @@ static void test_sctp_ipv4_packet_to_string(void) char *dump = NULL, *expected = NULL; /* Test a DUMP_SHORT dump */ - status = packet_to_string(packet, DUMP_SHORT, &dump, &error); + status = packet_to_string(packet, 0, DUMP_SHORT, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -72,7 +78,7 @@ static void test_sctp_ipv4_packet_to_string(void) free(dump); /* Test a DUMP_FULL dump */ - status = packet_to_string(packet, DUMP_FULL, &dump, &error); + status = packet_to_string(packet, 0, DUMP_FULL, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -83,7 +89,7 @@ static void test_sctp_ipv4_packet_to_string(void) free(dump); /* Test a DUMP_VERBOSE dump */ - status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error); + status = packet_to_string(packet, 0, DUMP_VERBOSE, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -276,7 +282,7 @@ static void test_sctp_ipv6_packet_to_string(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error); #if DEBUG_LOGGING == 1 if (result != PACKET_OK) { printf("error was: %s\n", error); @@ -289,7 +295,7 @@ static void test_sctp_ipv6_packet_to_string(void) char *dump = NULL, *expected = NULL; /* Test a DUMP_SHORT dump */ - status = packet_to_string(packet, DUMP_SHORT, &dump, &error); + status = packet_to_string(packet, 0, DUMP_SHORT, &dump, &error); #if DEBUG_LOGGING == 1 if (status != STATUS_OK) { printf("error was: %s\n", error); @@ -356,7 +362,7 @@ static void test_sctp_ipv6_packet_to_string(void) free(dump); /* Test a DUMP_FULL dump */ - status = packet_to_string(packet, DUMP_FULL, &dump, &error); + status = packet_to_string(packet, 0, DUMP_FULL, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -419,7 +425,7 @@ static void test_sctp_ipv6_packet_to_string(void) free(dump); /* Test a DUMP_VERBOSE dump */ - status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error); + status = packet_to_string(packet, 0, DUMP_VERBOSE, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -523,6 +529,366 @@ static void test_sctp_ipv6_packet_to_string(void) packet_free(packet); } +static void test_sctp_udp_ipv4_packet_to_string(void) +{ + /* An IPv4/UDP/SCTP packet. */ + u8 data[] = { + /* IPv4: */ + 0x45, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x11, 0xb5, 0xbb, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x01, + /* UDP Header */ + 0x26, 0xab, 0x26, 0xab, 0x00, 0x18, 0x00, 0x00, + /* SCTP Common Header: */ + 0x04, 0xd2, 0x1f, 0x90, + 0x01, 0x02, 0x03, 0x04, + 0x3d, 0x99, 0xbf, 0xe3, + /* SCTP ABORT Chunk: */ + 0x06, 0x01, 0x00, 0x04 + }; + + 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), ETHERTYPE_IP, IPPROTO_SCTP, + &error); +#if DEBUG_LOGGING == 1 + if (result != PACKET_OK) { + printf("error was: %s\n", error); + } +#endif + 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, IPPROTO_SCTP, DUMP_SHORT, &dump, + &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "sctp/udp(9899 > 9899): ABORT[flgs=T]"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_FULL dump */ + status = packet_to_string(packet, IPPROTO_SCTP, DUMP_FULL, &dump, + &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "2.2.2.2:1234 > 1.1.1.1:8080 " + "sctp/udp(9899 > 9899): ABORT[flgs=T]"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_VERBOSE dump */ + status = packet_to_string(packet, IPPROTO_SCTP, DUMP_VERBOSE, &dump, + &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "2.2.2.2:1234 > 1.1.1.1:8080 " + "sctp/udp(9899 > 9899): ABORT[flgs=T]" + "\n" + "0x0000: 45 00 00 2c 00 00 00 00 ff 11 b5 bb 02 02 02 02 " "\n" + "0x0010: 01 01 01 01 26 ab 26 ab 00 18 00 00 04 d2 1f 90 " "\n" + "0x0020: 01 02 03 04 3d 99 bf e3 06 01 00 04 " "\n"; + assert(strcmp(dump, expected) == 0); + free(dump); + + packet_free(packet); +} + +static void test_sctp_udp_ipv6_packet_to_string(void) +{ + /* An IPv6/UDP/SCTP packet. */ + u8 data[] = { + /* IPv6 Base Header: */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x11, 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, + /* UDP Header */ + 0x26, 0xab, 0x26, 0xab, 0x00, 0x18, 0x00, 0x00, + /* SCTP Common Header: */ + 0x04, 0xd2, 0x1f, 0x90, + 0x01, 0x02, 0x03, 0x04, + 0x3d, 0x99, 0xbf, 0xe3, + /* SCTP ABORT Chunk: */ + 0x06, 0x01, 0x00, 0x04 + }; + + 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), ETHERTYPE_IPV6, IPPROTO_SCTP, + &error); +#if DEBUG_LOGGING == 1 + if (result != PACKET_OK) { + printf("error was: %s\n", error); + } +#endif + 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, IPPROTO_SCTP, DUMP_SHORT, &dump, + &error); +#if DEBUG_LOGGING == 1 + if (status != STATUS_OK) { + printf("error was: %s\n", error); + } +#endif + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "sctp/udp(9899 > 9899): ABORT[flgs=T]"; + + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_FULL dump */ + status = packet_to_string(packet, IPPROTO_SCTP, DUMP_FULL, &dump, + &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "2::2222:1234 > 1::1111:8080 " + "sctp/udp(9899 > 9899): ABORT[flgs=T]"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_VERBOSE dump */ + status = packet_to_string(packet, IPPROTO_SCTP, DUMP_VERBOSE, &dump, + &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "2::2222:1234 > 1::1111:8080 " + "sctp/udp(9899 > 9899): ABORT[flgs=T]" + "\n" + "0x0000: 60 00 00 00 00 18 11 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 26 ab 26 ab 00 18 00 00 " "\n" + "0x0030: 04 d2 1f 90 01 02 03 04 3d 99 bf e3 06 01 00 04 " "\n"; + + printf("expected = '%s'\n", expected); + assert(strcmp(dump, expected) == 0); + free(dump); + packet_free(packet); +} + +static void test_tcp_udp_ipv4_packet_to_string(void) +{ + /* An IPv4/GRE/IPv4/UDP/TCP packet. */ + u8 data[] = { + /* IPv4: */ + 0x45, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x2f, 0xb5, 0x6d, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x01, + /* GRE: */ + 0x00, 0x00, 0x08, 0x00, + /* IPv4, UDP, TCP: */ + 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x11, 0x38, 0xfe, 0xc0, 0x00, 0x02, 0x01, + 0xc0, 0xa8, 0x00, 0x01, 0x26, 0xab, 0x26, 0xab, + 0x00, 0x30, 0x00, 0x00, 0xcf, 0x3f, 0x1f, 0x90, + 0x00, 0x00, 0x00, 0x01, 0x83, 0x4d, 0xa5, 0x5b, + 0xa0, 0x10, 0x01, 0x01, 0xdb, 0x2d, 0x00, 0x00, + 0x05, 0x0a, 0x83, 0x4d, 0xab, 0x03, 0x83, 0x4d, + 0xb0, 0xab, 0x08, 0x0a, 0x00, 0x00, 0x01, 0x2c, + 0x60, 0xc2, 0x18, 0x20 + }; + + 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), ETHERTYPE_IP, IPPROTO_TCP, + &error); +#if DEBUG_LOGGING == 1 + if (result != STATUS_OK) { + printf("error was: %s\n", error); + } +#endif + 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, IPPROTO_TCP, 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: " + ". 1:1(0) ack 2202903899 win 257 " + "<sack 2202905347:2202906795,TS val 300 ecr 1623332896>" + "/udp(9899 > 9899)"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_FULL dump */ + status = packet_to_string(packet, IPPROTO_TCP, 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: " + "192.0.2.1:53055 > 192.168.0.1:8080 " + ". 1:1(0) ack 2202903899 win 257 " + "<sack 2202905347:2202906795,TS val 300 ecr 1623332896>" + "/udp(9899 > 9899)"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_VERBOSE dump */ + status = packet_to_string(packet, IPPROTO_TCP, 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: " + "192.0.2.1:53055 > 192.168.0.1:8080 " + ". 1:1(0) ack 2202903899 win 257 " + "<sack 2202905347:2202906795,TS val 300 ecr 1623332896>" + "/udp(9899 > 9899)" + "\n" + "0x0000: 45 00 00 5c 00 00 00 00 ff 2f b5 6d 02 02 02 02 " "\n" + "0x0010: 01 01 01 01 00 00 08 00 45 00 00 44 00 00 00 00 " "\n" + "0x0020: ff 11 38 fe c0 00 02 01 c0 a8 00 01 26 ab 26 ab " "\n" + "0x0030: 00 30 00 00 cf 3f 1f 90 00 00 00 01 83 4d a5 5b " "\n" + "0x0040: a0 10 01 01 db 2d 00 00 05 0a 83 4d ab 03 83 4d " "\n" + "0x0050: b0 ab 08 0a 00 00 01 2c 60 c2 18 20 " "\n"; + assert(strcmp(dump, expected) == 0); + free(dump); + + packet_free(packet); +} + +static void test_tcp_udp_ipv6_packet_to_string(void) +{ + /* An IPv6/GRE/IPv6/UDP/TCP packet. */ + u8 data[] = { + /* IPv6: */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x54, 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, UDP, TCP: */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x28, 0x11, 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, + 0x26, 0xab, 0x26, 0xab, 0x00, 0x28, 0x00, 0x00, + 0xd3, 0xe2, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x80, 0x18, + 0x06, 0x60, 0x00, 0x00, 0x02, 0x04, 0x03, 0xe8, + 0x04, 0x02, 0x01, 0x01, 0x01, 0x03, 0x03, 0x07 + }; + + 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), ETHERTYPE_IPV6, IPPROTO_TCP, + &error); +#if DEBUG_LOGGING == 1 + if (result != STATUS_OK) { + printf("error was: %s\n", error); + } +#endif + 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, IPPROTO_TCP, DUMP_SHORT, &dump, + &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "ipv6 2::2222 > 1::1111: gre: " + "S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>" + "/udp(9899 > 9899)"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_FULL dump */ + status = packet_to_string(packet, IPPROTO_TCP, 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 " + "S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>" + "/udp(9899 > 9899)"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_VERBOSE dump */ + status = packet_to_string(packet, IPPROTO_TCP, 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 " + "S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>" + "/udp(9899 > 9899)" + "\n" + "0x0000: 60 00 00 00 00 54 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 28 11 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 26 ab 26 ab 00 28 00 00 d3 e2 1f 90 " "\n" + "0x0060: 00 00 00 00 00 00 00 00 80 02 80 18 06 60 00 00 " "\n" + "0x0070: 02 04 03 e8 04 02 01 01 01 03 03 07 " "\n"; + assert(strcmp(dump, expected) == 0); + free(dump); + + packet_free(packet); +} + static void test_tcp_ipv4_packet_to_string(void) { /* An IPv4/GRE/IPv4/TCP packet. */ @@ -550,7 +916,7 @@ static void test_tcp_ipv4_packet_to_string(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -558,7 +924,7 @@ static void test_tcp_ipv4_packet_to_string(void) char *dump = NULL, *expected = NULL; /* Test a DUMP_SHORT dump */ - status = packet_to_string(packet, DUMP_SHORT, &dump, &error); + status = packet_to_string(packet, 0, DUMP_SHORT, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -570,7 +936,7 @@ static void test_tcp_ipv4_packet_to_string(void) free(dump); /* Test a DUMP_FULL dump */ - status = packet_to_string(packet, DUMP_FULL, &dump, &error); + status = packet_to_string(packet, 0, DUMP_FULL, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -583,7 +949,7 @@ static void test_tcp_ipv4_packet_to_string(void) free(dump); /* Test a DUMP_VERBOSE dump */ - status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error); + status = packet_to_string(packet, 0, DUMP_VERBOSE, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -635,7 +1001,7 @@ static void test_tcp_ipv6_packet_to_string(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -643,7 +1009,7 @@ static void test_tcp_ipv6_packet_to_string(void) char *dump = NULL, *expected = NULL; /* Test a DUMP_SHORT dump */ - status = packet_to_string(packet, DUMP_SHORT, &dump, &error); + status = packet_to_string(packet, 0, DUMP_SHORT, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -654,7 +1020,7 @@ static void test_tcp_ipv6_packet_to_string(void) free(dump); /* Test a DUMP_FULL dump */ - status = packet_to_string(packet, DUMP_FULL, &dump, &error); + status = packet_to_string(packet, 0, DUMP_FULL, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -666,7 +1032,7 @@ static void test_tcp_ipv6_packet_to_string(void) free(dump); /* Test a DUMP_VERBOSE dump */ - status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error); + status = packet_to_string(packet, 0, DUMP_VERBOSE, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -716,7 +1082,7 @@ static void test_gre_mpls_tcp_ipv4_packet_to_string(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -724,7 +1090,7 @@ static void test_gre_mpls_tcp_ipv4_packet_to_string(void) char *dump = NULL, *expected = NULL; /* Test a DUMP_FULL dump */ - status = packet_to_string(packet, DUMP_FULL, &dump, &error); + status = packet_to_string(packet, 0, DUMP_FULL, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -764,7 +1130,7 @@ static void test_udplite_ipv4_packet_to_string(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -772,7 +1138,7 @@ static void test_udplite_ipv4_packet_to_string(void) char *dump = NULL, *expected = NULL; /* Test a DUMP_SHORT dump */ - status = packet_to_string(packet, DUMP_SHORT, &dump, &error); + status = packet_to_string(packet, 0, DUMP_SHORT, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -783,7 +1149,7 @@ static void test_udplite_ipv4_packet_to_string(void) free(dump); /* Test a DUMP_FULL dump */ - status = packet_to_string(packet, DUMP_FULL, &dump, &error); + status = packet_to_string(packet, 0, DUMP_FULL, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -795,7 +1161,7 @@ static void test_udplite_ipv4_packet_to_string(void) free(dump); /* Test a DUMP_VERBOSE dump */ - status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error); + status = packet_to_string(packet, 0, DUMP_VERBOSE, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -842,7 +1208,7 @@ static void test_udplite_ipv6_packet_to_string(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, 0, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -850,7 +1216,7 @@ static void test_udplite_ipv6_packet_to_string(void) char *dump = NULL, *expected = NULL; /* Test a DUMP_SHORT dump */ - status = packet_to_string(packet, DUMP_SHORT, &dump, &error); + status = packet_to_string(packet, 0, DUMP_SHORT, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -861,7 +1227,7 @@ static void test_udplite_ipv6_packet_to_string(void) free(dump); /* Test a DUMP_FULL dump */ - status = packet_to_string(packet, DUMP_FULL, &dump, &error); + status = packet_to_string(packet, 0, DUMP_FULL, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -873,7 +1239,7 @@ static void test_udplite_ipv6_packet_to_string(void) free(dump); /* Test a DUMP_VERBOSE dump */ - status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error); + status = packet_to_string(packet, 0, DUMP_VERBOSE, &dump, &error); assert(status == STATUS_OK); assert(error == NULL); printf("dump = '%s'\n", dump); @@ -895,10 +1261,15 @@ static void test_udplite_ipv6_packet_to_string(void) int main(void) { + test_tcp_udp_ipv4_packet_to_string(); test_sctp_ipv4_packet_to_string(); test_sctp_ipv6_packet_to_string(); + test_sctp_udp_ipv4_packet_to_string(); + test_sctp_udp_ipv6_packet_to_string(); test_tcp_ipv4_packet_to_string(); test_tcp_ipv6_packet_to_string(); + test_tcp_udp_ipv4_packet_to_string(); + test_tcp_udp_ipv6_packet_to_string(); test_gre_mpls_tcp_ipv4_packet_to_string(); test_udplite_ipv4_packet_to_string(); test_udplite_ipv6_packet_to_string(); diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index ffa9d175..399656cd 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -467,7 +467,17 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, u32 verification_tag; /* used for SCTP */ u32 start_sequence; /* used for TCP */ u16 checksum_coverage; /* used for UDPLite */ + u16 udp_src_port; + u16 udp_dst_port; } transport_info; + struct { + u16 udp_src_port; + u16 udp_dst_port; + } udp_encaps_info; + struct { + bool bad_crc32c; + s64 tag; + } sctp_header_spec; struct option_list *option; struct event *event; struct packet *packet; @@ -629,7 +639,10 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %type <expression> decimal_integer hex_integer data %type <expression> inaddr sockaddr msghdr cmsghdr cmsg_level cmsg_type cmsg_data %type <expression> sf_hdtr iovec pollfd opt_revents -%type <expression> linger l_onoff l_linger sctp_assoc_id +%type <expression> linger l_onoff l_linger +%type <udp_encaps_info> opt_udp_encaps_info +%type <sctp_header_spec> sctp_header_spec +%type <expression> sctp_assoc_id %type <expression> sctp_status sstat_state sstat_rwnd sstat_unackdata sstat_penddata %type <expression> sstat_instrms sstat_outstrms sstat_fragmentation_point sstat_primary %type <expression> sctp_initmsg sinit_num_ostreams sinit_max_instreams sinit_max_attempts @@ -910,113 +923,58 @@ packet_spec | icmp_packet_spec { $$ = $1; } ; -sctp_packet_spec -: packet_prefix opt_ip_info SCTP ':' sctp_chunk_list_spec { - char *error = NULL; - struct packet *outer = $1, *inner = NULL; - enum direction_t direction = outer->direction; - - inner = new_sctp_packet(in_config->wire_protocol, direction, $2, - -1, false, $5, &error); - if (inner == NULL) { - assert(error != NULL); - semantic_error(error); - free(error); - } - - $$ = packet_encapsulate_and_free(outer, inner); -} -| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ')' ':' sctp_chunk_list_spec { - char *error = NULL; - struct packet *outer = $1, *inner = NULL; - enum direction_t direction = outer->direction; - - inner = new_sctp_packet(in_config->wire_protocol, direction, $2, - -1, true, $8, &error); - if (inner == NULL) { - assert(error != NULL); - semantic_error(error); - free(error); - } - - $$ = packet_encapsulate_and_free(outer, inner); +opt_udp_encaps_info +: { + $$.udp_src_port = 0; + $$.udp_dst_port = 0; } -| packet_prefix opt_ip_info SCTP '(' TAG '=' INTEGER ')' ':' sctp_chunk_list_spec { - char *error = NULL; - struct packet *outer = $1, *inner = NULL; - enum direction_t direction = outer->direction; - - if (!is_valid_u32($7)) { - semantic_error("tag value out of range"); +| '/' UDP '(' INTEGER '>' INTEGER ')' { + if (!is_valid_u16($4)) { + semantic_error("UDP source port out of range"); } - inner = new_sctp_packet(in_config->wire_protocol, direction, $2, - $7, false, $10, &error); - if (inner == NULL) { - assert(error != NULL); - semantic_error(error); - free(error); + if (!is_valid_u16($6)) { + semantic_error("UDP destination port out of range"); } - - $$ = packet_encapsulate_and_free(outer, inner); + $$.udp_src_port = $4; + $$.udp_dst_port = $6; } -| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ',' TAG '=' INTEGER ')' ':' sctp_chunk_list_spec { - char *error = NULL; - struct packet *outer = $1, *inner = NULL; - enum direction_t direction = outer->direction; +; - if (!is_valid_u32($9)) { +sctp_header_spec +: SCTP { + $$.bad_crc32c = false; + $$.tag = -1; +} +| SCTP '(' BAD_CRC32C ')' { + $$.bad_crc32c = true; + $$.tag = -1; +} +| SCTP '(' TAG '=' INTEGER ')' { + if (!is_valid_u32($5)) { semantic_error("tag value out of range"); } - inner = new_sctp_packet(in_config->wire_protocol, direction, $2, - $9, true, $12, &error); - if (inner == NULL) { - assert(error != NULL); - semantic_error(error); - free(error); - } - - $$ = packet_encapsulate_and_free(outer, inner); + $$.bad_crc32c = false; + $$.tag = $5; } -| packet_prefix opt_ip_info SCTP ':' '[' byte_list ']' { - char *error = NULL; - struct packet *outer = $1, *inner = NULL; - enum direction_t direction = outer->direction; - - inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2, - -1, false, $6, &error); - if (inner == NULL) { - assert(error != NULL); - semantic_error(error); - free(error); +| SCTP '(' BAD_CRC32C ',' TAG '=' INTEGER ')' { + if (!is_valid_u32($7)) { + semantic_error("tag value out of range"); } - - $$ = packet_encapsulate_and_free(outer, inner); + $$.bad_crc32c = true; + $$.tag = $7; } -| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ')' ':' '[' byte_list ']' { - char *error = NULL; - struct packet *outer = $1, *inner = NULL; - enum direction_t direction = outer->direction; - - inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2, - -1, true, $9, &error); - if (inner == NULL) { - assert(error != NULL); - semantic_error(error); - free(error); - } +; - $$ = packet_encapsulate_and_free(outer, inner); -} -| packet_prefix opt_ip_info SCTP '(' TAG '=' INTEGER ')' ':' '[' byte_list ']' { +sctp_packet_spec +: packet_prefix opt_ip_info sctp_header_spec opt_udp_encaps_info ':' sctp_chunk_list_spec { char *error = NULL; struct packet *outer = $1, *inner = NULL; enum direction_t direction = outer->direction; - if (!is_valid_u32($7)) { - semantic_error("tag value out of range"); - } - inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2, - $7, false, $11, &error); + inner = new_sctp_packet(in_config->wire_protocol, direction, $2, + $3.tag, $3.bad_crc32c, $6, + $4.udp_src_port, $4.udp_dst_port, + &error); if (inner == NULL) { assert(error != NULL); semantic_error(error); @@ -1025,16 +983,15 @@ sctp_packet_spec $$ = packet_encapsulate_and_free(outer, inner); } -| packet_prefix opt_ip_info SCTP '(' BAD_CRC32C ',' TAG '=' INTEGER ')' ':' '[' byte_list ']' { +| packet_prefix opt_ip_info sctp_header_spec opt_udp_encaps_info ':' '[' byte_list ']' { char *error = NULL; struct packet *outer = $1, *inner = NULL; enum direction_t direction = outer->direction; - if (!is_valid_u32($9)) { - semantic_error("tag value out of range"); - } inner = new_sctp_generic_packet(in_config->wire_protocol, direction, $2, - $9, true, $13, &error); + $3.tag, $3.bad_crc32c, $7, + $4.udp_src_port, $4.udp_dst_port, + &error); if (inner == NULL) { assert(error != NULL); semantic_error(error); @@ -2362,7 +2319,7 @@ sctp_protocol_violation_cause_spec } tcp_packet_spec -: packet_prefix opt_ip_info flags seq opt_ack opt_window opt_tcp_options { +: packet_prefix opt_ip_info flags seq opt_ack opt_window opt_tcp_options opt_udp_encaps_info{ char *error = NULL; struct packet *outer = $1, *inner = NULL; enum direction_t direction = outer->direction; @@ -2378,6 +2335,7 @@ tcp_packet_spec $4.start_sequence, $4.payload_bytes, $5, $6, $7, absolute_ts_ecr, + $8.udp_src_port, $8.udp_dst_port, &error); absolute_ts_ecr = false; free($3); @@ -2447,7 +2405,8 @@ icmp_packet_spec inner = new_icmp_packet(in_config->wire_protocol, direction, $4, $5, $2.protocol, $2.payload_bytes, $2.start_sequence, $2.checksum_coverage, - $2.verification_tag, $6, &error); + $2.verification_tag, $6, + $2.udp_src_port, $2.udp_dst_port, &error); free($4); free($5); if (inner == NULL) { @@ -2559,26 +2518,36 @@ opt_icmp_echoed $$.protocol = IPPROTO_TCP; $$.payload_bytes = 0; $$.start_sequence = 0; + $$.udp_src_port = 0; + $$.udp_dst_port = 0; } -| '[' SCTP '(' INTEGER ')' ']' { +| '[' SCTP '(' INTEGER ')' opt_udp_encaps_info ']' { $$.protocol = IPPROTO_SCTP; $$.payload_bytes = 0; $$.verification_tag = $4; + $$.udp_src_port = $6.udp_src_port; + $$.udp_dst_port = $6.udp_dst_port; } | '[' UDP '(' INTEGER ')' ']' { $$.protocol = IPPROTO_UDP; $$.payload_bytes = $4; $$.start_sequence = 0; + $$.udp_src_port = 0; + $$.udp_dst_port = 0; } | '[' UDPLITE '(' INTEGER ',' INTEGER ')' ']' { $$.protocol = IPPROTO_UDPLITE; $$.payload_bytes = $4; $$.checksum_coverage = $6; + $$.udp_src_port = 0; + $$.udp_dst_port = 0; } -| '[' seq ']' { +| '[' seq opt_udp_encaps_info ']' { $$.protocol = IPPROTO_TCP; $$.payload_bytes = $2.payload_bytes; $$.start_sequence = $2.start_sequence; + $$.udp_src_port = $3.udp_src_port; + $$.udp_dst_port = $3.udp_dst_port; } ; diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c index 07ebe30a..a984ce09 100644 --- a/gtests/net/packetdrill/run_packet.c +++ b/gtests/net/packetdrill/run_packet.c @@ -101,7 +101,7 @@ static u16 next_ephemeral_port(struct state *state) * *error followed by the given type and a hex dump of the given * packet. */ -static void add_packet_dump(char **error, const char *type, +static void add_packet_dump(struct state *state, char **error, const char *type, struct packet *packet, s64 time_usecs, enum dump_format_t format) { @@ -109,7 +109,7 @@ static void add_packet_dump(char **error, const char *type, char *old_error = *error; char *dump = NULL, *dump_error = NULL; - packet_to_string(packet, format, + packet_to_string(packet, state->config->udp_encaps, format, &dump, &dump_error); asprintf(error, "%s\n%s packet: %9.6f %s%s%s", old_error, type, usecs_to_secs(time_usecs), dump, @@ -129,8 +129,8 @@ static void verbose_packet_dump(struct state *state, const char *type, if (state->config->verbose) { char *dump = NULL, *dump_error = NULL; - packet_to_string(live_packet, DUMP_SHORT, - &dump, &dump_error); + packet_to_string(live_packet, state->config->udp_encaps, + DUMP_SHORT, &dump, &dump_error); printf("%s packet: %9.6f %s%s%s\n", type, usecs_to_secs(time_usecs), dump, @@ -189,7 +189,7 @@ static struct socket *setup_new_child_socket(struct state *state, const struct p assert(socket->state == SOCKET_INIT); socket->state = SOCKET_PASSIVE_PACKET_RECEIVED; socket->address_family = packet_address_family(packet); - socket->protocol = packet_ip_protocol(packet); + socket->protocol = packet_ip_protocol(packet, config->udp_encaps); /* Set script info for this socket using script packet. */ struct tuple tuple; @@ -413,7 +413,7 @@ static struct socket *handle_connect_for_script_packet( state->socket_under_test = socket; assert(socket->state == SOCKET_INIT); socket->address_family = packet_address_family(packet); - socket->protocol = packet_ip_protocol(packet); + socket->protocol = packet_ip_protocol(packet, config->udp_encaps); socket->script.fd = -1; @@ -587,9 +587,12 @@ static int offset_sack_blocks(struct packet *packet, } static int map_inbound_icmp_sctp_packet( - struct socket *socket, struct packet *live_packet, char **error) + struct socket *socket, + struct packet *live_packet, + bool encapsulated, + char **error) { - u32 *v_tag = packet_echoed_sctp_v_tag(live_packet); + u32 *v_tag = packet_echoed_sctp_v_tag(live_packet, encapsulated); *v_tag = htonl(socket->live.remote_initiate_tag); return STATUS_OK; } @@ -598,9 +601,12 @@ static int map_inbound_icmp_sctp_packet( * The Linux TCP layer ignores ICMP messages with bogus sequence numbers. */ static int map_inbound_icmp_tcp_packet( - struct socket *socket, struct packet *live_packet, char **error) + struct socket *socket, + struct packet *live_packet, + bool encapsulated, + char **error) { - u32 *seq = packet_echoed_tcp_seq(live_packet); + u32 *seq = packet_echoed_tcp_seq(live_packet, encapsulated); /* FIXME: There is currently no way to access the TCP flags */ bool is_syn = false; u32 seq_offset = local_seq_script_to_live_offset(socket, is_syn); @@ -623,12 +629,15 @@ static int map_inbound_icmp_udplite_packet( } static int map_inbound_icmp_packet( - struct socket *socket, struct packet *live_packet, char **error) + struct socket *socket, + struct packet *live_packet, + bool encapsulated, + char **error) { if (packet_echoed_ip_protocol(live_packet) == IPPROTO_SCTP) - return map_inbound_icmp_sctp_packet(socket, live_packet, error); + return map_inbound_icmp_sctp_packet(socket, live_packet, encapsulated, error); else if (packet_echoed_ip_protocol(live_packet) == IPPROTO_TCP) - return map_inbound_icmp_tcp_packet(socket, live_packet, error); + return map_inbound_icmp_tcp_packet(socket, live_packet, encapsulated, 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) @@ -899,17 +908,18 @@ static int map_inbound_sctp_packet( * on failure returns STATUS_ERR and sets error message. */ static int map_inbound_packet( - struct socket *socket, struct packet *live_packet, char **error) + struct socket *socket, struct packet *live_packet, u8 udp_encaps, + char **error) { DEBUGP("map_inbound_packet\n"); /* Remap packet to live values. */ struct tuple live_inbound; socket_get_inbound(&socket->live, &live_inbound); - set_packet_tuple(live_packet, &live_inbound); + set_packet_tuple(live_packet, &live_inbound, udp_encaps != 0); if ((live_packet->icmpv4 != NULL) || (live_packet->icmpv6 != NULL)) - return map_inbound_icmp_packet(socket, live_packet, error); + return map_inbound_icmp_packet(socket, live_packet, udp_encaps != 0, error); if (live_packet->sctp) { return map_inbound_sctp_packet(socket, live_packet, error); @@ -1143,6 +1153,7 @@ static int map_outbound_live_packet( struct packet *live_packet, struct packet *actual_packet, struct packet *script_packet, + u8 udp_encaps, char **error) { DEBUGP("map_outbound_live_packet\n"); @@ -1156,7 +1167,7 @@ static int map_outbound_live_packet( /* Rewrite 4-tuple to be outbound script values. */ socket_get_outbound(&socket->script, &script_outbound); - set_packet_tuple(actual_packet, &script_outbound); + set_packet_tuple(actual_packet, &script_outbound, udp_encaps != 0); if (live_packet->sctp) { return map_outbound_live_sctp_packet(socket, live_packet, actual_packet, script_packet, error); @@ -1312,7 +1323,7 @@ static int tcp_options_allowance(const struct packet *actual_packet, static int verify_ipv4( const struct packet *actual_packet, const struct packet *script_packet, - int layer, char **error) + int layer, u8 udp_encaps, char **error) { const struct ipv4 *actual_ipv4 = actual_packet->headers[layer].h.ipv4; const struct ipv4 *script_ipv4 = script_packet->headers[layer].h.ipv4; @@ -1339,6 +1350,18 @@ static int verify_ipv4( ntohs(actual_ipv4->tot_len), error)) return STATUS_ERR; break; + case IPPROTO_UDP: + if (udp_encaps == IPPROTO_TCP) { + if (check_field("ipv4_total_length", + (ntohs(script_ipv4->tot_len) + + tcp_options_allowance(actual_packet, + script_packet)), + ntohs(actual_ipv4->tot_len), error)) + return STATUS_ERR; + break; + } else if (udp_encaps == IPPROTO_SCTP) { + break; + } default: if (check_field("ipv4_total_length", ntohs(script_ipv4->tot_len), @@ -1360,7 +1383,7 @@ static int verify_ipv4( static int verify_ipv6( const struct packet *actual_packet, const struct packet *script_packet, - int layer, char **error) + int layer, u8 udp_encaps, char **error) { const struct ipv6 *actual_ipv6 = actual_packet->headers[layer].h.ipv6; const struct ipv6 *script_ipv6 = script_packet->headers[layer].h.ipv6; @@ -1384,6 +1407,18 @@ static int verify_ipv6( ntohs(actual_ipv6->payload_len), error)) return STATUS_ERR; break; + case IPPROTO_UDP: + if (udp_encaps != 0) { + if (check_field("ipv6_payload_len", + (ntohs(script_ipv6->payload_len) + + tcp_options_allowance(actual_packet, + script_packet)), + ntohs(actual_ipv6->payload_len), error)) + return STATUS_ERR; + break; + } else if (udp_encaps == IPPROTO_SCTP) { + break; + } default: if (check_field("ipv6_payload_len", ntohs(script_ipv6->payload_len), @@ -1456,7 +1491,7 @@ static int verify_sctp_parameters(u8 *begin, u16 length, (flags & FLAG_RECONFIG_RESP_SN_NOCHECK ? STATUS_OK : check_field("outgoing_ssn_reset_request_parameter.resp_sn", ntohl(script_reset->respsn), - ntohl(live_reset->respsn), + ntohl(live_reset->respsn), error)) || (flags & FLAG_RECONFIG_LAST_TSN_NOCHECK ? STATUS_OK : check_field("outgoing_ssn_reset_request_parameter.last_tsn", @@ -1472,7 +1507,7 @@ static int verify_sctp_parameters(u8 *begin, u16 length, ntohs(live_reset->sids[i]), error)) { return STATUS_ERR; - } + } } break; } @@ -1496,7 +1531,7 @@ static int verify_sctp_parameters(u8 *begin, u16 length, ntohs(live_reset->sids[i]), error)) { return STATUS_ERR; - } + } } break; } @@ -2286,7 +2321,7 @@ static int verify_i_forward_tsn_chunk(struct sctp_i_forward_tsn_chunk *actual_ch static int verify_sctp( const struct packet *actual_packet, const struct packet *script_packet, - int layer, char **error) + int layer, u8 udp_encaps, char **error) { struct sctp_chunks_iterator iter; struct sctp_chunk *actual_chunk; @@ -2469,11 +2504,23 @@ static int verify_sctp( static int verify_tcp( const struct packet *actual_packet, const struct packet *script_packet, - int layer, char **error) + int layer, u8 udp_encaps, char **error) { const struct tcp *actual_tcp = actual_packet->headers[layer].h.tcp; const struct tcp *script_tcp = script_packet->headers[layer].h.tcp; - + const struct udp *actual_udp = (struct udp *)actual_tcp - 1; + const struct udp *script_udp = (struct udp *)script_tcp - 1; + + if (udp_encaps != 0) { + if (check_field("udp_src_port", + ntohs(script_udp->src_port), + ntohs(actual_udp->src_port), error) || + check_field("udp_dst_port", + ntohs(script_udp->dst_port), + ntohs(actual_udp->dst_port), error)) { + return STATUS_ERR; + } + } if (check_field("tcp_data_offset", (script_tcp->doff + tcp_options_allowance(actual_packet, @@ -2528,11 +2575,14 @@ static int verify_tcp( static int verify_udp( const struct packet *actual_packet, const struct packet *script_packet, - int layer, char **error) + int layer, u8 udp_encaps, char **error) { const struct udp *actual_udp = actual_packet->headers[layer].h.udp; const struct udp *script_udp = script_packet->headers[layer].h.udp; + if (udp_encaps != 0) { + return STATUS_OK; + } if (check_field("udp_len", ntohs(script_udp->len), ntohs(actual_udp->len), error)) @@ -2545,7 +2595,7 @@ static int verify_udp( static int verify_udplite( const struct packet *actual_packet, const struct packet *script_packet, - int layer, char **error) + int layer, u8 udp_encaps, char **error) { const struct udplite *actual_udplite = actual_packet->headers[layer].h.udplite; @@ -2562,7 +2612,7 @@ static int verify_udplite( static int verify_gre( const struct packet *actual_packet, const struct packet *script_packet, - int layer, char **error) + int layer, u8 udp_encaps, char **error) { const struct gre *actual_gre = actual_packet->headers[layer].h.gre; const struct gre *script_gre = script_packet->headers[layer].h.gre; @@ -2579,7 +2629,7 @@ static int verify_gre( static int verify_mpls( const struct packet *actual_packet, const struct packet *script_packet, - int layer, char **error) + int layer, u8 udp_encaps, char **error) { const struct header *actual_header = &actual_packet->headers[layer]; const struct header *script_header = &script_packet->headers[layer]; @@ -2608,13 +2658,13 @@ static int verify_mpls( typedef int (*verifier_func)( const struct packet *actual_packet, const struct packet *script_packet, - int layer, char **error); + int layer, u8 udp_encaps, char **error); /* Verify that required actual header fields are as the script expected. */ static int verify_header( const struct packet *actual_packet, const struct packet *script_packet, - int layer, char **error) + int layer, u8 udp_encaps, char **error) { verifier_func verifiers[HEADER_NUM_TYPES] = { [HEADER_IPV4] = verify_ipv4, @@ -2644,13 +2694,13 @@ static int verify_header( assert(type < HEADER_NUM_TYPES); verifier = verifiers[type]; assert(verifier != NULL); - return verifier(actual_packet, script_packet, layer, error); + return verifier(actual_packet, script_packet, layer, udp_encaps, error); } /* Verify that required actual header fields are as the script expected. */ static int verify_outbound_live_headers( const struct packet *actual_packet, - const struct packet *script_packet, char **error) + const struct packet *script_packet, u8 udp_encaps, char **error) { const int actual_headers = packet_header_count(actual_packet); const int script_headers = packet_header_count(script_packet); @@ -2676,7 +2726,8 @@ static int verify_outbound_live_headers( if (script_packet->headers[i].type == HEADER_NONE) break; - if (verify_header(actual_packet, script_packet, i, error)) + if (verify_header(actual_packet, script_packet, i, + udp_encaps, error)) return STATUS_ERR; } @@ -2795,11 +2846,13 @@ static int verify_outbound_live_packet( /* Map live packet values into script space for easy comparison. */ if (map_outbound_live_packet( - socket, live_packet, actual_packet, script_packet, error)) + socket, live_packet, actual_packet, script_packet, + state->config->udp_encaps, error)) goto out; /* Verify actual IP, TCP/UDP header values matched expected ones. */ - if (verify_outbound_live_headers(actual_packet, script_packet, error)) { + if (verify_outbound_live_headers(actual_packet, script_packet, + state->config->udp_encaps, error)) { non_fatal = true; goto out; } @@ -2832,11 +2885,11 @@ static int verify_outbound_live_packet( result = STATUS_OK; out: - add_packet_dump(error, "script", script_packet, script_usecs, + add_packet_dump(state, error, "script", script_packet, script_usecs, DUMP_SHORT); if (actual_packet != NULL) { - add_packet_dump(error, "actual", actual_packet, actual_usecs, - DUMP_SHORT); + add_packet_dump(state, error, "actual", actual_packet, + actual_usecs, DUMP_SHORT); packet_free(actual_packet); } if (result == STATUS_ERR && @@ -2857,7 +2910,8 @@ static int sniff_outbound_live_packet( enum direction_t direction = DIRECTION_INVALID; assert(*packet == NULL); while (1) { - if (netdev_receive(state->netdev, packet, error)) + if (netdev_receive(state->netdev, state->config->udp_encaps, + packet, error)) return STATUS_ERR; /* See if the packet matches an existing, known socket. */ socket = find_socket_for_live_packet(state, *packet, @@ -3113,8 +3167,24 @@ static int do_outbound_script_packet( state, live_packet->time_usecs)); /* Save the TCP header so we can reset the connection at the end. */ - if (live_packet->tcp) + if (live_packet->tcp) { socket->last_outbound_tcp_header = *(live_packet->tcp); + if (state->config->udp_encaps == IPPROTO_TCP) { + struct udp *udp = (struct udp *)(live_packet->tcp) - 1; + + socket->last_outbound_udp_encaps_src_port = ntohs(udp->src_port); + socket->last_outbound_udp_encaps_dst_port = ntohs(udp->dst_port); + } + } + + if (live_packet->sctp) { + if (state->config->udp_encaps == IPPROTO_SCTP) { + struct udp *udp = (struct udp *)(live_packet->sctp) - 1; + + socket->last_outbound_udp_encaps_src_port = ntohs(udp->src_port); + socket->last_outbound_udp_encaps_dst_port = ntohs(udp->dst_port); + } + } /* Verify the bits the kernel sent were what the script expected. */ result = verify_outbound_live_packet( @@ -3128,7 +3198,8 @@ out: /* Checksum the packet and inject it into the kernel under test. */ static int send_live_ip_packet(struct netdev *netdev, - struct packet *packet) + struct packet *packet, + u8 udp_encaps) { assert(packet->ip_bytes > 0); /* We do IPv4 and IPv6 */ @@ -3138,7 +3209,7 @@ static int send_live_ip_packet(struct netdev *netdev, packet->icmpv4 || packet->icmpv6); /* Fill in layer 3 and layer 4 checksums */ - checksum_packet(packet); + checksum_packet(packet, udp_encaps); return netdev_send(netdev, packet); } @@ -3211,9 +3282,21 @@ static int do_inbound_script_packet( break; } } - assert(packet->headers[i + 1].type == HEADER_SCTP); - packet->headers[i].total_bytes += temp_offset; - packet->headers[i + 1].total_bytes += temp_offset; + if (state->config->udp_encaps == IPPROTO_SCTP) { + struct udp *udp; + + assert(packet->headers[i + 1].type == HEADER_UDP); + assert(packet->headers[i + 2].type == HEADER_SCTP); + packet->headers[i].total_bytes += temp_offset; + packet->headers[i + 1].total_bytes += temp_offset; + packet->headers[i + 2].total_bytes += temp_offset; + udp = ((struct udp *)packet->sctp) - 1; + udp->len = htons(ntohs(udp->len) + temp_offset); + } else { + assert(packet->headers[i + 1].type == HEADER_SCTP); + packet->headers[i].total_bytes += temp_offset; + packet->headers[i + 1].total_bytes += temp_offset; + } offset += temp_offset; } if (((packet->flags & FLAGS_SCTP_BAD_CRC32C) == 0) && @@ -3248,9 +3331,21 @@ static int do_inbound_script_packet( break; } } - assert(packet->headers[i + 1].type == HEADER_SCTP); - packet->headers[i].total_bytes += temp_offset; - packet->headers[i + 1].total_bytes += temp_offset; + if (state->config->udp_encaps == IPPROTO_SCTP) { + struct udp *udp; + + assert(packet->headers[i + 1].type == HEADER_UDP); + assert(packet->headers[i + 2].type == HEADER_SCTP); + packet->headers[i].total_bytes += temp_offset; + packet->headers[i + 1].total_bytes += temp_offset; + packet->headers[i + 2].total_bytes += temp_offset; + udp = ((struct udp *)packet->sctp) - 1; + udp->len = htons(ntohs(udp->len) + temp_offset); + } else { + assert(packet->headers[i + 1].type == HEADER_SCTP); + packet->headers[i].total_bytes += temp_offset; + packet->headers[i + 1].total_bytes += temp_offset; + } offset += temp_offset; } break; @@ -3265,7 +3360,8 @@ static int do_inbound_script_packet( /* Start with a bit-for-bit copy of the packet from the script. */ struct packet *live_packet = packet_copy(packet); /* Map packet fields from script values to live values. */ - if (map_inbound_packet(socket, live_packet, error)) + if (map_inbound_packet(socket, live_packet, state->config->udp_encaps, + error)) goto out; verbose_packet_dump(state, "inbound injected", live_packet, @@ -3277,7 +3373,22 @@ static int do_inbound_script_packet( socket->last_injected_tcp_header = *(live_packet->tcp); socket->last_injected_tcp_payload_len = packet_payload_len(live_packet); + if (state->config->udp_encaps == IPPROTO_TCP) { + struct udp *udp = (struct udp *)(live_packet->tcp) - 1; + + socket->last_injected_udp_encaps_src_port = ntohs(udp->src_port); + socket->last_injected_udp_encaps_dst_port = ntohs(udp->dst_port); + } } + if (live_packet->sctp) { + if (state->config->udp_encaps == IPPROTO_SCTP) { + struct udp *udp = (struct udp *)(live_packet->sctp) - 1; + + socket->last_injected_udp_encaps_src_port = ntohs(udp->src_port); + socket->last_injected_udp_encaps_dst_port = ntohs(udp->dst_port); + } + } + if (((live_packet->ipv4 != NULL) && (live_packet->ipv4->src_ip.s_addr == htonl(INADDR_ANY))) || ((live_packet->ipv6 != NULL) && @@ -3288,11 +3399,11 @@ static int do_inbound_script_packet( DEBUGP("socket_under_test = %p\n", state->socket_under_test); state->socket_under_test = setup_new_child_socket(state, packet); socket_get_inbound(&state->socket_under_test->live, &live_inbound); - set_packet_tuple(live_packet, &live_inbound); + set_packet_tuple(live_packet, &live_inbound, state->config->udp_encaps != 0); } /* Inject live packet into kernel. */ - result = send_live_ip_packet(state->netdev, live_packet); + result = send_live_ip_packet(state->netdev, live_packet, state->config->udp_encaps); out: packet_free(live_packet); @@ -3360,7 +3471,17 @@ int reset_connection(struct state *state, struct socket *socket) struct packet *packet = NULL; struct tuple live_inbound; int result = STATUS_OK; + u16 udp_src_port; + u16 udp_dst_port; + if (socket->last_outbound_udp_encaps_src_port != 0 || + socket->last_outbound_udp_encaps_dst_port != 0) { + udp_src_port = socket->last_outbound_udp_encaps_dst_port; + udp_dst_port = socket->last_outbound_udp_encaps_src_port; + } else { + udp_src_port = socket->last_injected_udp_encaps_src_port; + udp_dst_port = socket->last_injected_udp_encaps_dst_port; + } /* Pick TCP header fields to be something the kernel will accept. */ if (socket->last_injected_tcp_header.ack) { /* If we've already injected something, then use a sequence @@ -3393,16 +3514,17 @@ int reset_connection(struct state *state, struct socket *socket) packet = new_tcp_packet(socket->address_family, DIRECTION_INBOUND, ECN_NONE, - "R.", seq, 0, ack_seq, window, NULL, false, &error); + "R.", seq, 0, ack_seq, window, NULL, false, + udp_src_port, udp_dst_port, &error); if (packet == NULL) die("%s", error); /* Rewrite addresses and port to match inbound live traffic. */ socket_get_inbound(&socket->live, &live_inbound); - set_packet_tuple(packet, &live_inbound); + set_packet_tuple(packet, &live_inbound, state->config->udp_encaps != 0); /* Inject live packet into kernel. */ - result = send_live_ip_packet(state->netdev, packet); + result = send_live_ip_packet(state->netdev, packet, state->config->udp_encaps); packet_free(packet); @@ -3421,8 +3543,10 @@ int abort_association(struct state *state, struct socket *socket) struct sctp_chunk_list *chunk_list; struct sctp_cause_list *cause_list; struct tuple live_inbound; - int result = STATUS_OK; + int result; s64 flgs; + u16 udp_src_port; + u16 udp_dst_port; if ((socket->live.local_initiate_tag == 0) && (socket->live.remote_initiate_tag == 0)) { @@ -3433,6 +3557,14 @@ int abort_association(struct state *state, struct socket *socket) } else { flgs = SCTP_ABORT_CHUNK_T_BIT; } + if (socket->last_outbound_udp_encaps_src_port != 0 || + socket->last_outbound_udp_encaps_dst_port != 0) { + udp_src_port = socket->last_outbound_udp_encaps_dst_port; + udp_dst_port = socket->last_outbound_udp_encaps_src_port; + } else { + udp_src_port = socket->last_injected_udp_encaps_src_port; + udp_dst_port = socket->last_injected_udp_encaps_dst_port; + } cause_list = sctp_cause_list_new(); sctp_cause_list_append(cause_list, sctp_user_initiated_abort_cause_new("packetdrill cleaning up")); @@ -3440,12 +3572,13 @@ int abort_association(struct state *state, struct socket *socket) sctp_chunk_list_append(chunk_list, sctp_abort_chunk_new(flgs, cause_list)); packet = new_sctp_packet(socket->address_family, DIRECTION_INBOUND, ECN_NONE, -1, false, - chunk_list, &error); + chunk_list, udp_src_port, udp_dst_port, + &error); if (packet == NULL) die("%s", error); /* Rewrite addresses and port to match inbound live traffic. */ socket_get_inbound(&socket->live, &live_inbound); - set_packet_tuple(packet, &live_inbound); + set_packet_tuple(packet, &live_inbound, state->config->udp_encaps != 0); /* Rewrite the verification tag in the SCTP common header */ if (socket->live.local_initiate_tag != 0) { packet->sctp->v_tag = htonl(socket->live.local_initiate_tag); @@ -3454,7 +3587,7 @@ int abort_association(struct state *state, struct socket *socket) } /* Inject live packet into kernel. */ - result = send_live_ip_packet(state->netdev, packet); + result = send_live_ip_packet(state->netdev, packet, state->config->udp_encaps); packet_free(packet); diff --git a/gtests/net/packetdrill/sctp_packet.c b/gtests/net/packetdrill/sctp_packet.c index 8be43c6e..4237d9a9 100644 --- a/gtests/net/packetdrill/sctp_packet.c +++ b/gtests/net/packetdrill/sctp_packet.c @@ -3013,10 +3013,12 @@ new_sctp_packet(int address_family, s64 tag, bool bad_crc32c, struct sctp_chunk_list *list, + u16 udp_src_port, + u16 udp_dst_port, char **error) { struct packet *packet; /* the newly-allocated result packet */ - struct header *sctp_header; /* the SCTP header info */ + struct header *sctp_header, *udp_header; struct sctp_chunk_list_item *chunk_item; struct sctp_parameter_list_item *parameter_item; struct sctp_cause_list_item *cause_item; @@ -3024,11 +3026,12 @@ new_sctp_packet(int address_family, const int ip_option_bytes = 0; const int ip_header_bytes = (ip_header_min_len(address_family) + ip_option_bytes); + const int udp_header_bytes = sizeof(struct udp); const int sctp_header_bytes = sizeof(struct sctp_common_header); const int sctp_chunk_bytes = list->length; - const int ip_bytes = - ip_header_bytes + sctp_header_bytes + sctp_chunk_bytes; + int ip_bytes; bool overbook = false; + bool encapsulate = (udp_src_port > 0) || (udp_dst_port > 0); /* Sanity-check all the various lengths */ if (ip_option_bytes & 0x3) { @@ -3039,6 +3042,11 @@ new_sctp_packet(int address_family, } assert((ip_header_bytes & 0x3) == 0); + ip_bytes = ip_header_bytes + sctp_header_bytes + sctp_chunk_bytes; + if (encapsulate) { + ip_bytes += udp_header_bytes; + } + if (ip_bytes > MAX_SCTP_DATAGRAM_BYTES) { asprintf(error, "SCTP packet too large"); return NULL; @@ -3383,7 +3391,6 @@ new_sctp_packet(int address_family, /* Allocate and zero out a packet object of the desired size */ packet = packet_new(overbook ? MAX_SCTP_DATAGRAM_BYTES : ip_bytes); memset(packet->buffer, 0, overbook ? MAX_SCTP_DATAGRAM_BYTES : ip_bytes); - packet->direction = direction; packet->flags = 0; if (bad_crc32c) { @@ -3395,14 +3402,29 @@ new_sctp_packet(int address_family, packet->ecn = ecn; /* Set IP header fields */ - set_packet_ip_header(packet, address_family, ip_bytes, ecn, - IPPROTO_SCTP); + if (encapsulate) { + set_packet_ip_header(packet, address_family, ip_bytes, ecn, + IPPROTO_UDP); + udp_header = packet_append_header(packet, HEADER_UDP, udp_header_bytes); + udp_header->total_bytes = udp_header_bytes + sctp_header_bytes + sctp_chunk_bytes; + udp_header->h.udp->src_port = htons(udp_src_port); + udp_header->h.udp->dst_port = htons(udp_dst_port); + udp_header->h.udp->len = htons(udp_header_bytes + sctp_header_bytes + sctp_chunk_bytes); + udp_header->h.udp->check = htons(0); + } else { + set_packet_ip_header(packet, address_family, ip_bytes, ecn, + IPPROTO_SCTP); + } sctp_header = packet_append_header(packet, HEADER_SCTP, sctp_header_bytes); sctp_header->total_bytes = sctp_header_bytes + sctp_chunk_bytes; /* Find the start of the SCTP common header of the packet */ - packet->sctp = (struct sctp_common_header *) (ip_start(packet) + ip_header_bytes); + if (encapsulate) { + packet->sctp = (struct sctp_common_header *) (ip_start(packet) + ip_header_bytes + udp_header_bytes); + } else { + packet->sctp = (struct sctp_common_header *) (ip_start(packet) + ip_header_bytes); + } u8 *sctp_chunk_start = (u8 *) (packet->sctp + 1); /* Set SCTP header fields */ @@ -3436,8 +3458,8 @@ new_sctp_packet(int address_family, sctp_chunk_start += chunk_item->length; } free(packet->chunk_list); + packet->ip_bytes += sctp_chunk_bytes; packet->chunk_list = list; - packet->ip_bytes = ip_bytes; return packet; } @@ -3453,24 +3475,27 @@ static void print_sctp_byte_list(struct sctp_byte_list *list) { struct packet * new_sctp_generic_packet(int address_family, - enum direction_t direction, - enum ip_ecn_t ecn, - s64 tag, - bool bad_crc32c, - struct sctp_byte_list *bytes, - char **error) { + enum direction_t direction, + enum ip_ecn_t ecn, + s64 tag, + bool bad_crc32c, + struct sctp_byte_list *bytes, + u16 udp_src_port, + u16 udp_dst_port, + char **error) { struct packet *packet; /* the newly-allocated result packet */ - struct header *sctp_header; /* the SCTP header info */ + struct header *sctp_header, *udp_header; struct sctp_byte_list_item *item = NULL; /* Calculate lengths in bytes of all sections of the packet */ const int ip_option_bytes = 0; const int ip_header_bytes = (ip_header_min_len(address_family) + ip_option_bytes); + const int udp_header_bytes = sizeof(struct udp); const int sctp_header_bytes = sizeof(struct sctp_common_header); const int sctp_chunk_bytes = bytes->nr_entries; - const int ip_bytes = - ip_header_bytes + sctp_header_bytes + sctp_chunk_bytes; + int ip_bytes; bool overbook = false; + bool encapsulate = (udp_src_port > 0) || (udp_dst_port > 0); u16 i; #ifdef DEBUG_LOGGING @@ -3492,6 +3517,11 @@ new_sctp_generic_packet(int address_family, } assert((ip_header_bytes & 0x3) == 0); + ip_bytes = ip_header_bytes + sctp_header_bytes + sctp_chunk_bytes; + if (encapsulate) { + ip_bytes += udp_header_bytes; + } + if (ip_bytes > MAX_SCTP_DATAGRAM_BYTES) { asprintf(error, "SCTP packet too large"); return NULL; @@ -3512,14 +3542,29 @@ new_sctp_generic_packet(int address_family, packet->ecn = ecn; /* Set IP header fields */ - set_packet_ip_header(packet, address_family, ip_bytes, ecn, - IPPROTO_SCTP); + if (encapsulate) { + set_packet_ip_header(packet, address_family, ip_bytes, ecn, + IPPROTO_UDP); + udp_header = packet_append_header(packet, HEADER_UDP, udp_header_bytes); + udp_header->total_bytes = udp_header_bytes + sctp_header_bytes + sctp_chunk_bytes; + udp_header->h.udp->src_port = htons(udp_src_port); + udp_header->h.udp->dst_port = htons(udp_dst_port); + udp_header->h.udp->len = htons(udp_header_bytes + sctp_header_bytes + sctp_chunk_bytes); + udp_header->h.udp->check = htons(0); + } else { + set_packet_ip_header(packet, address_family, ip_bytes, ecn, + IPPROTO_SCTP); + } sctp_header = packet_append_header(packet, HEADER_SCTP, sctp_header_bytes); sctp_header->total_bytes = sctp_header_bytes + sctp_chunk_bytes; /* Find the start of the SCTP common header of the packet */ - packet->sctp = (struct sctp_common_header *) (ip_start(packet) + ip_header_bytes); + if (encapsulate) { + packet->sctp = (struct sctp_common_header *) (ip_start(packet) + ip_header_bytes + udp_header_bytes); + } else { + packet->sctp = (struct sctp_common_header *) (ip_start(packet) + udp_header_bytes); + } u8 *sctp_chunk_start = (u8 *) (packet->sctp + 1); /* Set SCTP header fields */ diff --git a/gtests/net/packetdrill/sctp_packet.h b/gtests/net/packetdrill/sctp_packet.h index 6fece3a3..33f420e5 100644 --- a/gtests/net/packetdrill/sctp_packet.h +++ b/gtests/net/packetdrill/sctp_packet.h @@ -564,14 +564,18 @@ extern struct packet *new_sctp_packet(int address_family, s64 tag, bool bad_crc32c, struct sctp_chunk_list *chunk_list, + u16 udp_src_port, + u16 udp_dst_port, char **error); struct packet * new_sctp_generic_packet(int address_family, - enum direction_t direction, - enum ip_ecn_t ecn, - s64 tag, - bool bad_crc32c, - struct sctp_byte_list *bytes, - char **error); + enum direction_t direction, + enum ip_ecn_t ecn, + s64 tag, + bool bad_crc32c, + struct sctp_byte_list *bytes, + u16 udp_src_port, + u16 udp_dst_port, + char **error); #endif /* __SCTP_PACKET_H__ */ diff --git a/gtests/net/packetdrill/socket.h b/gtests/net/packetdrill/socket.h index 96c6f6bf..07fc1336 100644 --- a/gtests/net/packetdrill/socket.h +++ b/gtests/net/packetdrill/socket.h @@ -116,6 +116,11 @@ struct socket { struct tcp last_injected_tcp_header; u32 last_injected_tcp_payload_len; + u16 last_outbound_udp_encaps_dst_port; + u16 last_outbound_udp_encaps_src_port; + u16 last_injected_udp_encaps_src_port; + u16 last_injected_udp_encaps_dst_port; + struct sctp_cookie_echo_chunk *prepared_cookie_echo; u16 prepared_cookie_echo_length; struct sctp_heartbeat_ack_chunk *prepared_heartbeat_ack; @@ -233,14 +238,15 @@ static inline void set_headers_tuple(struct ipv4 *ipv4, /* Set the tuple for a packet header echoed inside an ICMPv4/ICMPv6 message. */ static inline void set_icmp_echoed_tuple(struct packet *packet, - const struct tuple *tuple) + const struct tuple *tuple, + bool encapsulated) { /* All currently supported ICMP message types include a copy * of the outbound IP header and the first few bytes inside, * which so far always means the first ICMP_ECHO_BYTES of * TCP header. */ - DEBUGP("set_icmp_echoed_tuple"); + DEBUGP("set_icmp_echoed_tuple encapsulated: %d\n", encapsulated); /* Flip the direction of the tuple, since the ICMP message is * flowing in the direction opposite that of the echoed TCP/IP @@ -250,8 +256,8 @@ static inline void set_icmp_echoed_tuple(struct packet *packet, reverse_tuple(tuple, &echoed_tuple); set_headers_tuple(packet_echoed_ipv4_header(packet), packet_echoed_ipv6_header(packet), - packet_echoed_sctp_header(packet), - packet_echoed_tcp_header(packet), + packet_echoed_sctp_header(packet, encapsulated), + packet_echoed_tcp_header(packet, encapsulated), packet_echoed_udp_header(packet), packet_echoed_udplite_header(packet), &echoed_tuple); @@ -259,12 +265,12 @@ static inline void set_icmp_echoed_tuple(struct packet *packet, /* Set the tuple for a packet. */ static inline void set_packet_tuple(struct packet *packet, - const struct tuple *tuple) + const struct tuple *tuple, bool encapsulated) { set_headers_tuple(packet->ipv4, packet->ipv6, packet->sctp, packet->tcp, packet->udp, packet->udplite, tuple); if ((packet->icmpv4 != NULL) || (packet->icmpv6 != NULL)) - set_icmp_echoed_tuple(packet, tuple); + set_icmp_echoed_tuple(packet, tuple, encapsulated); } diff --git a/gtests/net/packetdrill/symbols_freebsd.c b/gtests/net/packetdrill/symbols_freebsd.c index d8b0f52f..9e21d375 100644 --- a/gtests/net/packetdrill/symbols_freebsd.c +++ b/gtests/net/packetdrill/symbols_freebsd.c @@ -366,18 +366,20 @@ struct int_symbol platform_symbols_table[] = { { TCP_MD5SIG, "TCP_MD5SIG" }, { TCP_INFO, "TCP_INFO" }, { TCP_CONGESTION, "TCP_CONGESTION" }, -#if __FreeBSD_version >=1100000 +#if defined(TCP_CCALGOOPT) { TCP_CCALGOOPT, "TCP_CCALGOOPT" }, #endif { TCP_KEEPINIT, "TCP_KEEPINIT" }, { TCP_KEEPIDLE, "TCP_KEEPIDLE" }, { TCP_KEEPINTVL, "TCP_KEEPINTVL" }, { TCP_KEEPCNT, "TCP_KEEPCNT" }, -#if __FreeBSD_version >= 1003000 +#if defined(TCP_FASTOPEN) { TCP_FASTOPEN, "TCP_FASTOPEN" }, #endif - -#if __FreeBSD_version >= 1001000 +#if defined(TCP_REMOTE_UDP_ENCAPS_PORT) + { TCP_REMOTE_UDP_ENCAPS_PORT, "TCP_REMOTE_UDP_ENCAPS_PORT" }, +#endif +#if defined(UDPLITE_RECV_CSCOV) && defined(UDPLITE_SEND_CSCOV) /* /usr/include/netinet/udplite.h */ { UDPLITE_RECV_CSCOV, "UDPLITE_RECV_CSCOV" }, { UDPLITE_SEND_CSCOV, "UDPLITE_SEND_CSCOV" }, diff --git a/gtests/net/packetdrill/tcp_packet.c b/gtests/net/packetdrill/tcp_packet.c index de415f98..fd536547 100644 --- a/gtests/net/packetdrill/tcp_packet.c +++ b/gtests/net/packetdrill/tcp_packet.c @@ -60,18 +60,21 @@ struct packet *new_tcp_packet(int address_family, s32 window, const struct tcp_options *tcp_options, bool abs_ts_ecr, + u16 udp_src_port, + u16 udp_dst_port, char **error) { struct packet *packet = NULL; /* the newly-allocated result packet */ - struct header *tcp_header = NULL; /* the TCP header info */ + struct header *tcp_header, *udp_header; /* Calculate lengths in bytes of all sections of the packet */ const int ip_option_bytes = 0; const int tcp_option_bytes = tcp_options ? tcp_options->length : 0; const int ip_header_bytes = (ip_header_min_len(address_family) + ip_option_bytes); + const int udp_header_bytes = sizeof(struct udp); const int tcp_header_bytes = sizeof(struct tcp) + tcp_option_bytes; - const int ip_bytes = - ip_header_bytes + tcp_header_bytes + tcp_payload_bytes; + int ip_bytes; + bool encapsulate = (udp_src_port > 0) || (udp_dst_port > 0); /* Sanity-check all the various lengths */ if (ip_option_bytes & 0x3) { @@ -95,6 +98,10 @@ struct packet *new_tcp_packet(int address_family, return NULL; } + ip_bytes = ip_header_bytes + tcp_header_bytes + tcp_payload_bytes; + if (encapsulate) { + ip_bytes += udp_header_bytes; + } if (ip_bytes > MAX_TCP_DATAGRAM_BYTES) { asprintf(error, "TCP segment too large"); return NULL; @@ -112,14 +119,29 @@ struct packet *new_tcp_packet(int address_family, packet->ecn = ecn; /* Set IP header fields */ - set_packet_ip_header(packet, address_family, ip_bytes, ecn, - IPPROTO_TCP); + if (encapsulate) { + set_packet_ip_header(packet, address_family, ip_bytes, ecn, + IPPROTO_UDP); + udp_header = packet_append_header(packet, HEADER_UDP, udp_header_bytes); + udp_header->total_bytes = udp_header_bytes + tcp_header_bytes + tcp_payload_bytes; + udp_header->h.udp->src_port = htons(udp_src_port); + udp_header->h.udp->dst_port = htons(udp_dst_port); + udp_header->h.udp->len = htons(udp_header_bytes + tcp_header_bytes + tcp_payload_bytes); + udp_header->h.udp->check = htons(0); + } else { + set_packet_ip_header(packet, address_family, ip_bytes, ecn, + IPPROTO_TCP); + } tcp_header = packet_append_header(packet, HEADER_TCP, tcp_header_bytes); tcp_header->total_bytes = tcp_header_bytes + tcp_payload_bytes; /* Find the start of TCP sections of the packet */ - packet->tcp = (struct tcp *) (ip_start(packet) + ip_header_bytes); + if (encapsulate) { + packet->tcp = (struct tcp *) (ip_start(packet) + ip_header_bytes + udp_header_bytes); + } else { + packet->tcp = (struct tcp *) (ip_start(packet) + ip_header_bytes); + } u8 *tcp_option_start = (u8 *) (packet->tcp + 1); /* Set TCP header fields */ diff --git a/gtests/net/packetdrill/tcp_packet.h b/gtests/net/packetdrill/tcp_packet.h index 0c065c0e..415445e9 100644 --- a/gtests/net/packetdrill/tcp_packet.h +++ b/gtests/net/packetdrill/tcp_packet.h @@ -45,5 +45,7 @@ extern struct packet *new_tcp_packet(int address_family, s32 window, const struct tcp_options *tcp_options, bool abs_ts_ecr, + u16 udp_src_port, + u16 udp_dst_port, char **error); #endif /* __TCP_PACKET_H__ */ diff --git a/gtests/net/packetdrill/wire_client_netdev.c b/gtests/net/packetdrill/wire_client_netdev.c index ce4ebefc..049f9821 100644 --- a/gtests/net/packetdrill/wire_client_netdev.c +++ b/gtests/net/packetdrill/wire_client_netdev.c @@ -150,7 +150,7 @@ static int wire_client_netdev_send(struct netdev *a_netdev, return STATUS_ERR; } -static int wire_client_netdev_receive(struct netdev *a_netdev, +static int wire_client_netdev_receive(struct netdev *a_netdev, u8 udp_encaps, struct packet **packet, char **error) { DEBUGP("wire_client_netdev_receive\n"); diff --git a/gtests/net/packetdrill/wire_server_netdev.c b/gtests/net/packetdrill/wire_server_netdev.c index 8ef26f3a..02905614 100644 --- a/gtests/net/packetdrill/wire_server_netdev.c +++ b/gtests/net/packetdrill/wire_server_netdev.c @@ -184,7 +184,7 @@ static int wire_server_netdev_send(struct netdev *a_netdev, return result; } -static int wire_server_netdev_receive(struct netdev *a_netdev, +static int wire_server_netdev_receive(struct netdev *a_netdev, u8 udp_encaps, struct packet **packet, char **error) { struct wire_server_netdev *netdev = to_server_netdev(a_netdev); @@ -192,8 +192,8 @@ static int wire_server_netdev_receive(struct netdev *a_netdev, DEBUGP("wire_server_netdev_receive\n"); - return netdev_receive_loop(netdev->psock, DIRECTION_INBOUND, packet, - &num_packets, error); + return netdev_receive_loop(netdev->psock, DIRECTION_INBOUND, udp_encaps, + packet, &num_packets, error); } struct netdev_ops wire_server_netdev_ops = { -- GitLab