From 68f26eb394c45ca374bba48ea801c0a2990abae2 Mon Sep 17 00:00:00 2001 From: Jason Eggleston <jeggleston@llnw.com> Date: Thu, 10 Aug 2017 07:04:27 -0700 Subject: [PATCH] add support for ignoring tcp sequence number and timestamp offset changes In order to simulate delayed packets, there are occasions where responses to those packets need to be ignored insofar as they modify relative sequence numbers or timestamps. For example a retransmitted SYN or SYN-ACK might be delayed until after the TCP session ends, but a combination of bugs on the client and server might result in the session persisting as if it never ended. inbound injected packet: 0.100034 S 0:0(0) win 65535 <mss 1460,nop,wscale 6,sackOK,TS val 100 ecr 0> outbound sniffed packet: 0.100083 S. 3079308609:3079308609(0) ack 1 win 65535 <mss 1460,nop,wscale 4,sackOK,TS val 3206488061 ecr 100> inbound injected packet: 0.150127 . 1:101(100) ack 3079308610 win 1031 <nop,nop,TS val 150 ecr 3206488061> outbound sniffed packet: 0.150170 . 3079308610:3079308610(0) ack 101 win 12495 <nop,nop,TS val 3206488111 ecr 150> outbound sniffed packet: 0.150192 P. 3079308610:3079309010(400) ack 101 win 12501 <nop,nop,TS val 3206488111 ecr 150> inbound injected packet: 0.200251 . 101:101(0) ack 3079309010 win 1024 <nop,nop,TS val 200 ecr 3206488111> inbound injected packet: 0.200271 F. 101:101(0) ack 3079309010 win 1031 <nop,nop,TS val 200 ecr 3206488111> outbound sniffed packet: 0.200287 . 3079309010:3079309010(0) ack 102 win 12501 <nop,nop,TS val 3206488161 ecr 200> outbound sniffed packet: 0.200324 F. 3079309010:3079309010(0) ack 102 win 12501 <nop,nop,TS val 3206488161 ecr 200> inbound injected packet: 0.250354 . 102:102(0) ack 3079309011 win 1031 <nop,nop,TS val 250 ecr 3206488161> inbound injected packet: 0.265380 S 0:0(0) win 65535 <mss 1460,nop,wscale 6,sackOK,TS val 100 ecr 0> outbound sniffed packet: 0.265402 S. 3079308609:3079308609(0) ack 1 win 65535 <mss 1460,nop,wscale 4,sackOK,TS val 3118763918 ecr 100> inbound injected packet: 0.300437 . 1:1(0) ack 3079308610 win 1031 <nop,nop,TS val 150 ecr 3206488061> outbound sniffed packet: 0.300455 R 3079308610:3079308610(0) win 0 0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 +0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 +0.000 bind(3, ..., ...) = 0 +0.000 listen(3, 1) = 0 +0.100 < S 0:0(0) win 65535 <mss 1460,nop,wscale 6,sackOK,TS val 100 ecr 0> +0.000 > S. 0:0(0) ack 1 win 65535 <mss 1460,nop,wscale 4,sackOK,TS val 200 ecr 100> +0.050 < . 1:101(100) ack 1 win 1031 <nop, nop, TS val 150 ecr 200> +0.000 accept(3, ..., ...) = 4 +0.000 read(4, ..., 100) = 100 +0.000 write(4, ..., 400) = 400 +0.000 > . 1:1(0) ack 101 win 12495 <nop, nop, TS val 250 ecr 150> +0.000 > P. 1:401(400) ack 101 win 12501 <nop, nop, TS val 250 ecr 150> +0.050 < . 101:101(0) ack 401 win 1024 <nop, nop, TS val 200 ecr 250> +0.000 < F. 101:101(0) ack 401 win 1031 <nop, nop, TS val 200 ecr 250> +0.000 > . 401:401(0) ack 102 win 12501 <nop, nop, TS val 300 ecr 200> +0.000 close(4) = 0 +0.000 > F. 401:401(0) ack 102 win 12501 <nop, nop, TS val 300 ecr 200> +0.050 < . 102:102(0) ack 402 win 1031 <nop, nop, TS val 250 ecr 300> +0.015 < S 0:0(0) win 65535 <mss 1460,nop,wscale 6,sackOK,TS val 100 ecr 0!> +0.000 > S. ...(0) ack 1 win 65535 <mss 1460,nop,wscale 4,sackOK,TS val ... ecr 100> +0.035 < . 1:1(0) ack 1 win 1031 <nop, nop, TS val 150 ecr 200> +0.000 > . 1:1(0) ack 1 win 12501 <nop, nop, TS val 200 ecr 150> --- gtests/net/packetdrill/packet.h | 2 ++ gtests/net/packetdrill/parser.y | 27 ++++++++++++++++++++++++--- gtests/net/packetdrill/run_packet.c | 21 ++++++++++++--------- gtests/net/packetdrill/tcp_packet.c | 8 ++++++++ gtests/net/packetdrill/tcp_packet.h | 2 ++ gtests/net/packetdrill/types.h | 6 ++++++ 6 files changed, 54 insertions(+), 12 deletions(-) diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h index 3d7de771..eb1ab64c 100644 --- a/gtests/net/packetdrill/packet.h +++ b/gtests/net/packetdrill/packet.h @@ -114,6 +114,8 @@ struct packet { #define FLAGS_SCTP_EXPLICIT_TAG 0x20 /* verification tag specified */ #define FLAGS_SCTP_GENERIC_PACKET 0x40 /* set if it is a generic 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 */ enum ip_ecn_t ecn; /* IPv4/IPv6 ECN treatment for packet */ diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index a25ffb5b..794f87a3 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -152,10 +152,11 @@ static struct script *out_script = NULL; /* The test invocation to pass back to parse_and_finalize_config(). */ struct invocation *invocation; -/* Temporary variables to allow passing absolute timestamp flags to +/* Temporary variables to allow passing absolute or ignore timestamp flags to * the tcp_options struct from the timestamp options. Adding fields * to struct tcp_option, which might be cleaner, affects the on-wire format. */ +bool ignore_ts_val = false; bool absolute_ts_ecr = false; /* Copy the script contents into our single linear buffer. */ @@ -446,6 +447,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %union { s64 integer; struct abs_integer abs_integer; + struct ignore_integer ignore_integer; double floating; char *string; char *reserved; @@ -461,6 +463,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, u32 start_sequence; u16 payload_bytes; bool absolute; + bool ignore; } tcp_sequence_info; struct { int protocol; @@ -607,6 +610,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %token <integer> INTEGER HEX_INTEGER %token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR %type <abs_integer> abs_integer +%type <ignore_integer> ignore_integer %type <direction> direction %type <ip_ecn> opt_ip_info %type <ip_ecn> ip_ecn @@ -2337,10 +2341,13 @@ tcp_packet_spec direction, $2, $3, $4.start_sequence, $4.payload_bytes, $5, $6, $7, + ignore_ts_val, absolute_ts_ecr, $4.absolute, + $4.ignore, $8.udp_src_port, $8.udp_dst_port, &error); + ignore_ts_val = false; absolute_ts_ecr = false; free($3); free($7); @@ -2622,6 +2629,14 @@ seq $$.payload_bytes = $5; $$.absolute = true; } +| ELLIPSIS '(' INTEGER ')' { + if (!is_valid_u16($3)) { + semantic_error("TCP payload size out of range"); + } + $$.start_sequence = 0; + $$.payload_bytes = $3; + $$.ignore = true; +} ; opt_ack @@ -2699,11 +2714,12 @@ tcp_option | SACK sack_block_list { $$ = $2; } -| TIMESTAMP VAL INTEGER ECR abs_integer { +| TIMESTAMP VAL ignore_integer ECR abs_integer { u32 val, ecr; + ignore_ts_val = $3.ignore; absolute_ts_ecr = $5.absolute; $$ = tcp_option_new(TCPOPT_TIMESTAMP, TCPOLEN_TIMESTAMP); - val = $3; + val = $3.integer; ecr = $5.integer; if (!is_valid_u32(val)) { semantic_error("ts val out of range"); @@ -2731,6 +2747,11 @@ abs_integer | INTEGER '!' { $$.integer = $1; $$.absolute = true; } ; +ignore_integer +: INTEGER { $$.integer = $1; $$.ignore = false; } +| ELLIPSIS { $$.integer = 0; $$.ignore = true; } +; + sack_block_list : sack_block { $$ = $1; } | sack_block_list sack_block { diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c index 3debc5d7..7bb90459 100644 --- a/gtests/net/packetdrill/run_packet.c +++ b/gtests/net/packetdrill/run_packet.c @@ -1215,9 +1215,11 @@ static int map_outbound_live_packet( u32 actual_ts_val = packet_tcp_ts_val(actual_packet); /* Remember script->actual TS val mapping for later. */ - set_outbound_ts_val_mapping(socket, - script_ts_val, - actual_ts_val); + if (!(script_packet->flags & FLAG_IGNORE_TS_VAL)) { + set_outbound_ts_val_mapping(socket, + script_ts_val, + actual_ts_val); + } /* Find baseline for socket's live->script TS val mapping. */ if (!socket->found_first_tcp_ts) { @@ -2566,9 +2568,10 @@ static int verify_tcp( check_field("tcp_reserved_bits", script_tcp->res1, actual_tcp->res1, error) || - check_field("tcp_seq", - ntohl(script_tcp->seq), - ntohl(actual_tcp->seq), error) || + (script_packet->flags & FLAG_IGNORE_SEQ ? STATUS_OK : + check_field("tcp_seq", + ntohl(script_tcp->seq), + ntohl(actual_tcp->seq), error)) || check_field("tcp_ack_seq", ntohl(script_tcp->ack_seq), ntohl(actual_tcp->ack_seq), error) || @@ -3039,7 +3042,7 @@ static int do_outbound_script_packet( } if (socket->state == SOCKET_PASSIVE_PACKET_RECEIVED) { - if (packet->tcp && packet->tcp->syn && packet->tcp->ack) { + if (packet->tcp && packet->tcp->syn && packet->tcp->ack && !(packet->flags & FLAG_IGNORE_SEQ)) { /* Script says we should see an outbound server SYNACK. */ socket->script.local_isn = ntohl(packet->tcp->seq); DEBUGP("SYNACK script.local_isn: %u\n", @@ -3526,8 +3529,8 @@ 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, - false, udp_src_port, udp_dst_port, &error); + "R.", seq, 0, ack_seq, window, NULL, false, false, + false, false, udp_src_port, udp_dst_port, &error); if (packet == NULL) die("%s", error); diff --git a/gtests/net/packetdrill/tcp_packet.c b/gtests/net/packetdrill/tcp_packet.c index 2bbe13f8..62b6070a 100644 --- a/gtests/net/packetdrill/tcp_packet.c +++ b/gtests/net/packetdrill/tcp_packet.c @@ -59,8 +59,10 @@ struct packet *new_tcp_packet(int address_family, u32 ack_sequence, s32 window, const struct tcp_options *tcp_options, + bool ignore_ts_val, bool abs_ts_ecr, bool abs_seq, + bool ignore_seq, u16 udp_src_port, u16 udp_dst_port, char **error) @@ -181,12 +183,18 @@ struct packet *new_tcp_packet(int address_family, tcp_options->length); } + if (ignore_ts_val) { + packet->flags |= FLAG_IGNORE_TS_VAL; + } if (abs_ts_ecr) { packet->flags |= FLAG_ABSOLUTE_TS_ECR; } if (abs_seq) { packet->flags |= FLAG_ABSOLUTE_SEQ; } + if (ignore_seq) { + packet->flags |= FLAG_IGNORE_SEQ; + } packet->ip_bytes = ip_bytes; return packet; diff --git a/gtests/net/packetdrill/tcp_packet.h b/gtests/net/packetdrill/tcp_packet.h index deacd96f..1a9115cd 100644 --- a/gtests/net/packetdrill/tcp_packet.h +++ b/gtests/net/packetdrill/tcp_packet.h @@ -44,8 +44,10 @@ extern struct packet *new_tcp_packet(int address_family, u32 ack_sequence, s32 window, const struct tcp_options *tcp_options, + bool ignore_ts_val, bool abs_ts_ecr, bool abs_seq, + bool ignore_seq, u16 udp_src_port, u16 udp_dst_port, char **error); diff --git a/gtests/net/packetdrill/types.h b/gtests/net/packetdrill/types.h index c6cc344d..21b124ac 100644 --- a/gtests/net/packetdrill/types.h +++ b/gtests/net/packetdrill/types.h @@ -141,6 +141,12 @@ struct abs_integer { bool absolute; }; +/* Type to handle ... or <integer> for specifying an integer or to ignore one. */ +struct ignore_integer { + s64 integer; + bool ignore; +}; + /* Length of output buffer for inet_ntop, plus prefix length (e.g. "/128"). */ #define ADDR_STR_LEN ((INET_ADDRSTRLEN + INET6_ADDRSTRLEN)+5) -- GitLab