From d76839089c4ab13444a100b1aeea4d5fcf969359 Mon Sep 17 00:00:00 2001 From: Michael Tuexen <tuexen@fh-muenster.de> Date: Wed, 29 Jan 2020 23:40:41 +0100 Subject: [PATCH] Integrate https://github.com/google/packetdrill/commit/77ab15806a56de226345fbe1910139829e431fe0 --- gtests/net/packetdrill/packet.h | 1 + gtests/net/packetdrill/packet_to_string.c | 21 ++++++-- gtests/net/packetdrill/parser.y | 30 +++++++++-- gtests/net/packetdrill/run_packet.c | 28 +++++++--- gtests/net/packetdrill/tcp.h | 10 ++-- gtests/net/packetdrill/tcp_packet.c | 63 +++++++++++++++++++++-- 6 files changed, 131 insertions(+), 22 deletions(-) diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h index 6a13cd15..10b22a74 100644 --- a/gtests/net/packetdrill/packet.h +++ b/gtests/net/packetdrill/packet.h @@ -116,6 +116,7 @@ struct packet { #define FLAGS_UDP_ENCAPSULATED 0x80 /* TCP/UDP or SCTP/UDP encapsulated */ #define FLAG_IGNORE_TS_VAL 0x100 /* set to ignore processing of TS val */ #define FLAG_IGNORE_SEQ 0x200 /* set to ignore processing of sequence numbers */ +#define FLAG_PARSE_ACE 0x400 /* output parsed AccECN ACE field */ enum ip_ecn_t ecn; /* IPv4/IPv6 ECN treatment for packet */ diff --git a/gtests/net/packetdrill/packet_to_string.c b/gtests/net/packetdrill/packet_to_string.c index 1e3fb6ff..fa8cbadf 100644 --- a/gtests/net/packetdrill/packet_to_string.c +++ b/gtests/net/packetdrill/packet_to_string.c @@ -183,6 +183,7 @@ 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 */ + int ace = 0; if ((format == DUMP_FULL) || (format == DUMP_VERBOSE)) { endpoints_to_string(s, packet); @@ -202,10 +203,22 @@ static int tcp_packet_to_string(FILE *s, struct packet *packet, int i, fputc('.', s); if (packet->tcp->urg) fputc('U', s); - if (packet->tcp->ece) - fputc('E', s); /* ECN *E*cho sent (ECN) */ - if (packet->tcp->cwr) - fputc('W', s); /* Congestion *W*indow reduced (ECN) */ + if (packet->flags & FLAG_PARSE_ACE) { + if (packet->tcp->ece) + ace |= 1; + if (packet->tcp->cwr) + ace |= 2; + if (packet->tcp->ae) + ace |= 4; + fputc('0' + ace, s); + } else { + if (packet->tcp->ece) + fputc('E', s); /* ECN *E*cho sent (ECN) */ + if (packet->tcp->cwr) + fputc('W', s); /* Congestion *W*indow reduced (ECN) */ + if (packet->tcp->ae) + fputc('A', s); /* *A*ccurate ECN */ + } fprintf(s, " %u:%u(%u)", ntohl(packet->tcp->seq), diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index 47eded23..7bb69e4f 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -670,7 +670,7 @@ static struct tcp_option *new_tcp_exp_fast_open_option(const char *cookie_string %type <mpls_stack_entry> mpls_stack_entry %type <integer> opt_mpls_stack_bottom %type <integer> opt_icmp_mtu -%type <string> icmp_type opt_icmp_code flags +%type <string> icmp_type opt_icmp_code opt_ack_flag opt_word ack_and_ace flags %type <string> opt_tcp_fast_open_cookie tcp_fast_open_cookie %type <string> opt_note note word_list %type <string> option_flag option_value script @@ -2677,10 +2677,34 @@ ip_ecn | CE { $$ = ECN_CE; } ; +opt_ack_flag +: { + $$ = strdup(""); +} +| '.' { + $$ = strdup("."); +} +; + +opt_word +: { + $$ = strdup(""); +} +| WORD { + $$ = $1; +} +; + +ack_and_ace +: FLOAT { + $$ = strdup(yytext); +} +; + flags -: WORD { $$ = $1; } +: WORD opt_ack_flag { asprintf(&($$), "%s%s", $1, $2); free($1); free($2); } +| opt_word ack_and_ace { asprintf(&($$), "%s%s", $1, $2); free($1); free($2); } | '.' { $$ = strdup("."); } -| WORD '.' { asprintf(&($$), "%s.", $1); free($1); } | '-' { $$ = strdup(""); } /* no TCP flags set in segment */ ; diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c index 60c8b5ff..a61bd5bb 100644 --- a/gtests/net/packetdrill/run_packet.c +++ b/gtests/net/packetdrill/run_packet.c @@ -1387,6 +1387,14 @@ static int tcp_options_allowance(const struct packet *actual_packet, return 0; } +/* Return the AccECN ACE count associated with the given TCP header. */ +static int tcp_ace_field(const struct tcp *tcp) +{ + return (tcp->ae ? 4 : 0) | + (tcp->cwr ? 2 : 0) | + (tcp->ece ? 1 : 0); +} + /* Verify that required actual IPv4 header fields are as the script expected. */ static int verify_ipv4( const struct packet *actual_packet, @@ -2615,12 +2623,20 @@ static int verify_tcp( check_field("tcp_urg", script_tcp->urg, actual_tcp->urg, error) || - check_field("tcp_ece", - script_tcp->ece, - actual_tcp->ece, error) || - check_field("tcp_cwr", - script_tcp->cwr, - actual_tcp->cwr, error) || + ((script_packet->flags & FLAG_PARSE_ACE) && + check_field("tcp_ace", + tcp_ace_field(script_tcp), + tcp_ace_field(actual_tcp), error)) || + (!(script_packet->flags & FLAG_PARSE_ACE) && + (check_field("tcp_ece", + script_tcp->ece, + actual_tcp->ece, error) || + check_field("tcp_cwr", + script_tcp->cwr, + actual_tcp->cwr, error) || + check_field("tcp_ae", + script_tcp->ae, + actual_tcp->ae, error))) || check_field("tcp_reserved_bits", script_tcp->res1, actual_tcp->res1, error) || diff --git a/gtests/net/packetdrill/tcp.h b/gtests/net/packetdrill/tcp.h index 779077a0..0c76db5f 100644 --- a/gtests/net/packetdrill/tcp.h +++ b/gtests/net/packetdrill/tcp.h @@ -95,8 +95,8 @@ struct tcp { __be32 seq; __be32 ack_seq; # if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) - __u16 res1:2, - res2:2, + __u16 ae:1, + res1:3, doff:4, fin:1, syn:1, @@ -107,9 +107,9 @@ struct tcp { ece:1, cwr:1; # elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) - __u16 doff:4, - res2:2, - res1:2, + __u16 doff:4 + res1:3, + ae:1, cwr:1, ece:1, urg:1, diff --git a/gtests/net/packetdrill/tcp_packet.c b/gtests/net/packetdrill/tcp_packet.c index 44070de9..860fee41 100644 --- a/gtests/net/packetdrill/tcp_packet.c +++ b/gtests/net/packetdrill/tcp_packet.c @@ -27,19 +27,44 @@ #include "ip_packet.h" #include "tcp.h" -/* The full list of valid TCP bit flag characters */ -static const char valid_tcp_flags[] = "FSRP.EWC"; +/* + * The full list of valid TCP bit flag characters. + * The numeric 0..7 is used as shorthand for the ACE field. + * + * In the list of valid flags the dot, the most common flag, is placed first. + */ +static const char valid_tcp_flags[] = ".FSRPEWAU01234567"; +static const char ace_tcp_flags[] = "01234567"; +static const char ecn_tcp_flags[] = "EWA"; /* Are all the TCP flags in the given string valid? */ static bool is_tcp_flags_spec_valid(const char *flags, char **error) { const char *s; + bool has_ecn_flag = false; + bool has_ace_flag = false; for (s = flags; *s != '\0'; ++s) { if (!strchr(valid_tcp_flags, *s)) { asprintf(error, "Invalid TCP flag: '%c'", *s); return false; } + if (strchr(ecn_tcp_flags, *s)) { + if (has_ace_flag) { + asprintf(error, + "Conflicting TCP flag: '%c'", *s); + return false; + } + has_ecn_flag = true; + } + if (strchr(ace_tcp_flags, *s)) { + if (has_ecn_flag || has_ace_flag) { + asprintf(error, + "Conflicting TCP flag: '%c'", *s); + return false; + } + has_ace_flag = true; + } } return true; } @@ -50,6 +75,19 @@ static inline int is_tcp_flag_set(char flag, const char *flags) return (strchr(flags, flag) != NULL) ? 1 : 0; } +/* Find and return the first numeric flag for ACE */ +static inline int tcp_flag_ace_count(const char *flags) +{ + const char *s; + + for (s = flags; *s != '\0'; ++s) { + if (strchr(ace_tcp_flags, *s)) + return ((int)*s - (int)'0'); + } + return 0; +} + + struct packet *new_tcp_packet(int address_family, enum direction_t direction, enum ip_ecn_t ecn, @@ -77,6 +115,7 @@ struct packet *new_tcp_packet(int address_family, const int udp_header_bytes = sizeof(struct udp); const int tcp_header_bytes = sizeof(struct tcp) + tcp_option_bytes; int ip_bytes; + int ace; bool encapsulate = (udp_src_port > 0) || (udp_dst_port > 0); /* Sanity-check all the various lengths */ @@ -173,8 +212,24 @@ struct packet *new_tcp_packet(int address_family, packet->tcp->psh = is_tcp_flag_set('P', flags); packet->tcp->ack = is_tcp_flag_set('.', flags); packet->tcp->urg = 0; - packet->tcp->ece = is_tcp_flag_set('E', flags); - packet->tcp->cwr = is_tcp_flag_set('W', flags); + + ace = tcp_flag_ace_count(flags); + if (ace != 0) { + /* + * After validity check, ACE value doesn't + * coexist with ECN flags. + * Need to force a boolean check for the + * 1-bit fields to get correctly set. + */ + packet->flags |= FLAG_PARSE_ACE; + packet->tcp->ece = ((ace & 1) != 0); + packet->tcp->cwr = ((ace & 2) != 0); + packet->tcp->ae = ((ace & 4) != 0); + } else { + packet->tcp->ece = is_tcp_flag_set('E', flags); + packet->tcp->cwr = is_tcp_flag_set('W', flags); + packet->tcp->ae = is_tcp_flag_set('A', flags); + } if (tcp_options == NULL) { packet->flags |= FLAG_OPTIONS_NOCHECK; -- GitLab