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