diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l index f0876095b503cb4d0bc239a908a57673885ab59f..ea3c647d1e2993ff2bd6dd609307448917b70694 100644 --- a/gtests/net/packetdrill/lexer.l +++ b/gtests/net/packetdrill/lexer.l @@ -320,6 +320,7 @@ nop return NOP; sack return SACK; sackOK return SACKOK; TS return TIMESTAMP; +EXP-FO return EXP_FAST_OPEN; FO return FAST_OPEN; val return VAL; win return WIN; diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index 80d421e495daf79930583917992f0bbeba200e36..db2b43f40de51fc7ce499d78f297eb7aada64cea 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -399,7 +399,7 @@ static int parse_hex_string(const char *hex, u8 *buf, int buf_len, } static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, - char **error) + char **error) { int cookie_string_len = strlen(cookie_string); if (cookie_string_len & 1) { @@ -415,10 +415,9 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, cookie_bytes, MAX_TCP_FAST_OPEN_COOKIE_BYTES); return NULL; } - u8 option_bytes = TCPOLEN_EXP_FASTOPEN_BASE + cookie_bytes; + u8 option_bytes = TCPOLEN_FASTOPEN_BASE + cookie_bytes; struct tcp_option *option; - option = tcp_option_new(TCPOPT_EXP, option_bytes); - option->data.fast_open.magic = htons(TCPOPT_FASTOPEN_MAGIC); + option = tcp_option_new(TCPOPT_FASTOPEN, option_bytes); int parsed_bytes = 0; /* Parse cookie. This should be an ASCII hex string * representing an even number of bytes (4-16 bytes). But we @@ -437,6 +436,45 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, return option; } +static struct tcp_option *new_tcp_exp_fast_open_option(const char *cookie_string, + char **error) +{ + int cookie_string_len = strlen(cookie_string); + if (cookie_string_len & 1) { + asprintf(error, + "TCP fast open cookie has an odd number of digits"); + return NULL; + } + int cookie_bytes = cookie_string_len / 2; /* 2 hex chars per byte */ + if (cookie_bytes > MAX_TCP_EXP_FAST_OPEN_COOKIE_BYTES) { + asprintf(error, "TCP fast open cookie too long"); + asprintf(error, "TCP fast open cookie of %d bytes " + "exceeds maximum cookie length of %d bytes", + cookie_bytes, MAX_TCP_EXP_FAST_OPEN_COOKIE_BYTES); + return NULL; + } + u8 option_bytes = TCPOLEN_EXP_FASTOPEN_BASE + cookie_bytes; + struct tcp_option *option; + option = tcp_option_new(TCPOPT_EXP, option_bytes); + option->data.exp_fast_open.magic = htons(TCPOPT_FASTOPEN_MAGIC); + int parsed_bytes = 0; + /* Parse cookie. This should be an ASCII hex string + * representing an even number of bytes (4-16 bytes). But we + * do not enforce this, since we want to allow test cases that + * supply invalid cookies. + */ + if (parse_hex_string(cookie_string, option->data.exp_fast_open.cookie, + sizeof(option->data.exp_fast_open.cookie), + &parsed_bytes)) { + free(option); + asprintf(error, + "TCP fast open cookie is not a valid hex string"); + return NULL; + } + assert(parsed_bytes == cookie_bytes); + return option; +} + %} %locations @@ -525,7 +563,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %token <reserved> SF_HDTR_HEADERS SF_HDTR_TRAILERS %token <reserved> FD EVENTS REVENTS ONOFF LINGER %token <reserved> ACK ECR EOL MSS NOP SACK NR_SACK SACKOK TIMESTAMP VAL WIN WSCALE PRO -%token <reserved> FAST_OPEN +%token <reserved> EXP_FAST_OPEN FAST_OPEN %token <reserved> IOV_BASE IOV_LEN %token <reserved> ECT0 ECT1 CE ECT01 NO_ECN %token <reserved> IPV4 IPV6 ICMP SCTP UDP UDPLITE GRE MTU @@ -2752,6 +2790,16 @@ tcp_option free(error); } } +| EXP_FAST_OPEN opt_tcp_fast_open_cookie { + char *error = NULL; + $$ = new_tcp_exp_fast_open_option($2, &error); + free($2); + if ($$ == NULL) { + assert(error != NULL); + semantic_error(error); + free(error); + } +} ; abs_integer diff --git a/gtests/net/packetdrill/tcp.h b/gtests/net/packetdrill/tcp.h index c07bdb8f43741af3e94d91d32b37af2dddb51084..fbc8edbd2d652dfb8cddcde7b8a816c0dcdfa4ba 100644 --- a/gtests/net/packetdrill/tcp.h +++ b/gtests/net/packetdrill/tcp.h @@ -85,6 +85,7 @@ #define TCPOPT_SACK 5 #define TCPOPT_TIMESTAMP 8 #define TCPOLEN_TIMESTAMP 10 +#define TCPOPT_FASTOPEN 34 #define TCPOPT_EXP 254 /* Experimental */ /* A portable TCP header definition (Linux and *BSD use different names). */ diff --git a/gtests/net/packetdrill/tcp_options.h b/gtests/net/packetdrill/tcp_options.h index 3fb8231c926247016173da6d3ce4e3ec6d7f5b79..34ae671c11a876a16154f67c949de7a1cbab8169 100644 --- a/gtests/net/packetdrill/tcp_options.h +++ b/gtests/net/packetdrill/tcp_options.h @@ -35,10 +35,10 @@ * option value for sharing TCP experimental options. * * For a description of experimental options, see: - * http://tools.ietf.org/html/draft-ietf-tcpm-experimental-options-00 + * https://tools.ietf.org/html/rfc6994 * * For a description of TFO, see: - * http://tools.ietf.org/html/draft-cheng-tcpm-fastopen-02 + * https://tools.ietf.org/html/rfc7413 */ #define TCPOPT_FASTOPEN_MAGIC 0xF989 @@ -46,9 +46,16 @@ #define TCPOLEN_EXP_FASTOPEN_BASE 4 /* smallest legal TFO option size */ /* The TFO option base prefix leaves this amount of space: */ -#define MAX_TCP_FAST_OPEN_COOKIE_BYTES \ +#define MAX_TCP_EXP_FAST_OPEN_COOKIE_BYTES \ (MAX_TCP_OPTION_BYTES - TCPOLEN_EXP_FASTOPEN_BASE) +/* TFO option must have: 1-byte kind, 1-byte length */ +#define TCPOLEN_FASTOPEN_BASE 2 /* smallest legal TFO option size */ + +/* The TFO option base prefix leaves this amount of space: */ +#define MAX_TCP_FAST_OPEN_COOKIE_BYTES \ + (MAX_TCP_OPTION_BYTES - TCPOLEN_FASTOPEN_BASE) + /* Represents a list of TCP options in their wire format. */ struct tcp_options { u8 data[MAX_TCP_OPTION_BYTES]; /* The options data, in wire format */ @@ -85,6 +92,14 @@ struct tcp_option { } sack; struct { u16 magic; /* must be TCPOPT_FASTOPEN_MAGIC */ + /* The fast open chookie should be 4-16 bytes + * of cookie, multiple of 2 bytes, but we + * allow for larger sizes, so we can test what + * stacks do with illegal options. + */ + u8 cookie[MAX_TCP_EXP_FAST_OPEN_COOKIE_BYTES]; + } exp_fast_open; + struct { /* The fast open chookie should be 4-16 bytes * of cookie, multiple of 2 bytes, but we * allow for larger sizes, so we can test what diff --git a/gtests/net/packetdrill/tcp_options_iterator.c b/gtests/net/packetdrill/tcp_options_iterator.c index 90257eb03b6e0d00c5a8d2e53286c3e69f188e85..1ec8dd8155a5fd4425a8ccca186c954c0b4dc144 100644 --- a/gtests/net/packetdrill/tcp_options_iterator.c +++ b/gtests/net/packetdrill/tcp_options_iterator.c @@ -62,6 +62,7 @@ static int get_expected_tcp_option_length(u8 kind, u8 *expected_length, break; case TCPOPT_SACK: + case TCPOPT_FASTOPEN: case TCPOPT_EXP: *expected_length = 0; /* variable-length option */ break; diff --git a/gtests/net/packetdrill/tcp_options_to_string.c b/gtests/net/packetdrill/tcp_options_to_string.c index d022ec2cb53b86a6b3529ebdfed0ced224bd15e6..d23ecd3060db98f7874043e0376b76014c14cd58 100644 --- a/gtests/net/packetdrill/tcp_options_to_string.c +++ b/gtests/net/packetdrill/tcp_options_to_string.c @@ -26,23 +26,45 @@ #include "tcp_options_iterator.h" +static int tcp_fast_open_option_to_string(FILE *s, struct tcp_option *option) +{ + if (option->length < TCPOLEN_FASTOPEN_BASE) { + return STATUS_ERR; + } + + fputs("FO", s); + int cookie_bytes = option->length - TCPOLEN_FASTOPEN_BASE; + assert(cookie_bytes >= 0); + assert(cookie_bytes <= MAX_TCP_FAST_OPEN_COOKIE_BYTES); + if (cookie_bytes > 0) { + fputs(" ", s); + } + int i; + for (i = 0; i < cookie_bytes; ++i) + fprintf(s, "%02x", option->data.fast_open.cookie[i]); + return STATUS_OK; +} + /* See if the given experimental option is a TFO option, and if so * then print the TFO option and return STATUS_OK. Otherwise, return * STATUS_ERR. */ -static int tcp_fast_open_option_to_string(FILE *s, struct tcp_option *option) +static int tcp_exp_fast_open_option_to_string(FILE *s, struct tcp_option *option) { if ((option->length < TCPOLEN_EXP_FASTOPEN_BASE) || - (ntohs(option->data.fast_open.magic) != TCPOPT_FASTOPEN_MAGIC)) + (ntohs(option->data.exp_fast_open.magic) != TCPOPT_FASTOPEN_MAGIC)) return STATUS_ERR; - fprintf(s, "FO "); + fputs("EXP-FO", s); int cookie_bytes = option->length - TCPOLEN_EXP_FASTOPEN_BASE; assert(cookie_bytes >= 0); - assert(cookie_bytes <= MAX_TCP_FAST_OPEN_COOKIE_BYTES); + assert(cookie_bytes <= MAX_TCP_EXP_FAST_OPEN_COOKIE_BYTES); + if (cookie_bytes > 0) { + fputs(" ", s); + } int i; for (i = 0; i < cookie_bytes; ++i) - fprintf(s, "%02x", option->data.fast_open.cookie[i]); + fprintf(s, "%02x", option->data.exp_fast_open.cookie[i]); return STATUS_OK; } @@ -106,8 +128,16 @@ int tcp_options_to_string(struct packet *packet, ntohl(option->data.time_stamp.ecr)); break; - case TCPOPT_EXP: + case TCPOPT_FASTOPEN: if (tcp_fast_open_option_to_string(s, option)) { + asprintf(error, "invalid length: %u", + option->length); + goto out; + } + break; + + case TCPOPT_EXP: + if (tcp_exp_fast_open_option_to_string(s, option)) { asprintf(error, "unknown experimental option"); goto out;