From de587f3f0251af54291be97e1caff676937701d3 Mon Sep 17 00:00:00 2001
From: Aomx <aomx@riseup.net>
Date: Tue, 12 Jul 2016 18:47:37 +0200
Subject: [PATCH] first commit regarding support of forward-tsn-chunk

---
 gtests/net/packetdrill/lexer.l       |   1 +
 gtests/net/packetdrill/parser.y      |  41 ++++++++-
 gtests/net/packetdrill/sctp.h        |  14 +++
 gtests/net/packetdrill/sctp_packet.c | 128 +++++++++++++++++++++++++++
 gtests/net/packetdrill/sctp_packet.h |  31 +++++++
 5 files changed, 214 insertions(+), 1 deletion(-)

diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index da93842b..0a33a0eb 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -555,6 +555,7 @@ SHUTDOWN_COMPLETE		return SHUTDOWN_COMPLETE;
 I-DATA				return I_DATA;
 PAD				return PAD;
 RECONFIG			return RECONFIG;
+FORWARD_TSN                     return FORWARD_TSN;
 type				return TYPE;
 flgs				return FLAGS;
 len				return LEN;
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index dc41710d..e78a2a86 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -472,6 +472,8 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 	struct sctp_u16_list_item *u16_item;
 	struct sctp_sack_block_list_item *sack_block_list_item;
 	struct sctp_sack_block_list *sack_block_list;
+	struct sctp_forward_tsn_sids_list *forward_tsn_sids_list;
+	struct sctp_forward_tsn_sids_list_item  *forward_tsn_sids_list_item;
 	struct sctp_address_type_list_item *address_type_list_item;
 	struct sctp_address_type_list *address_type_list;
 	struct sctp_parameter_type_list_item *parameter_type_list_item;
@@ -519,7 +521,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> SPINFO_MTU GAUTH_ASSOC_ID GAUTH_NUMBER_OF_CHUNKS GAUTH_CHUNKS
 %token <reserved> CHUNK DATA INIT INIT_ACK HEARTBEAT HEARTBEAT_ACK ABORT
 %token <reserved> SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECNE CWR
-%token <reserved> SHUTDOWN_COMPLETE I_DATA PAD RECONFIG
+%token <reserved> SHUTDOWN_COMPLETE I_DATA PAD RECONFIG FORWARD_TSN
 %token <reserved> TYPE FLAGS LEN
 %token <reserved> TAG A_RWND OS IS TSN SID SSN MID PPID FSN CUM_TSN GAPS DUPS
 %token <reserved> PARAMETER HEARTBEAT_INFORMATION IPV4_ADDRESS IPV6_ADDRESS
@@ -675,6 +677,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <chunk_list_item> sctp_shutdown_complete_chunk_spec
 %type <chunk_list_item> sctp_i_data_chunk_spec
 %type <chunk_list_item> sctp_pad_chunk_spec sctp_reconfig_chunk_spec
+%type <chunk_list_item> sctp_forward_tsn_spec
 %type <parameter_list> opt_parameter_list_spec sctp_parameter_list_spec
 %type <parameter_list_item> sctp_parameter_spec
 %type <parameter_list_item> sctp_generic_parameter_spec
@@ -722,6 +725,8 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <u16_item> u16_item
 %type <sack_block_list> opt_gaps gap_list opt_dups dup_list
 %type <sack_block_list_item> gap dup
+%type <forward_tsn_sids_list> opt_stream_identifier sids_list
+%type <forward_tsn_sids_list_item> sid
 %type <address_type_list> address_types_list
 %type <address_type_list_item> address_type
 %type <parameter_type_list> parameter_types_list
@@ -1053,6 +1058,7 @@ sctp_chunk_spec
 | sctp_i_data_chunk_spec            { $$ = $1; }
 | sctp_pad_chunk_spec               { $$ = $1; }
 | sctp_reconfig_chunk_spec          { $$ = $1; }
+| sctp_forward_tsn_spec             { $$ = $1; }
 ;
 
 chunk_type
@@ -1122,6 +1128,9 @@ chunk_type
 | RECONFIG {
 	$$ = SCTP_RECONFIG_CHUNK_TYPE;
 }
+| FORWARD_TSN {
+	$$ = SCTP_FORWARD_TSN_CHUNK_TYPE;
+}
 ;
 
 opt_chunk_type
@@ -1564,6 +1573,31 @@ dup
 }
 ;
 
+opt_stream_identifier
+: SIDS '=' ELLIPSIS         { $$ = NULL; }
+| SIDS '=' '[' ELLIPSIS ']' { $$ = NULL; }
+| SIDS '=' '[' sids_list ']' { $$ = $4; }
+;
+
+sids_list
+:                  { $$ = sctp_forward_tsn_sids_list_new(); }
+| sid              { $$ =sctp_forward_tsn_sids_list_new();
+                     sctp_forward_tsn_sids_list_append($$, $1); }
+| sids_list ',' sid { $$ = $1;
+                     sctp_forward_tsn_sids_list_append($1, $3); }
+;
+
+sid: INTEGER ':' INTEGER {
+	if (!is_valid_u16($1)) {
+		semantic_error("stream identifier out of range");
+	}
+	if (!is_valid_u16($3)) {
+		semantic_error("stream sequence number out of range");
+	}
+	$$ = sctp_forward_tsn_sids_list_item_new($1, $3);
+}
+;
+
 sctp_generic_chunk_spec
 : CHUNK '[' opt_chunk_type ',' opt_flags ',' opt_len ',' opt_val ']' {
 	if (($7 != -1) &&
@@ -1695,6 +1729,11 @@ sctp_pad_chunk_spec
 	$$ = sctp_pad_chunk_new($3, $5, NULL);
 }
 
+sctp_forward_tsn_spec
+: FORWARD_TSN '[' opt_cum_tsn ',' opt_stream_identifier']' {
+    $$ = sctp_forward_tsn_chunk_new($3, $5);
+}
+
 opt_req_sn
 : REQ_SN '=' INTEGER {
 	if (!is_valid_u32($3)) {
diff --git a/gtests/net/packetdrill/sctp.h b/gtests/net/packetdrill/sctp.h
index 0b95445b..7ae514f7 100644
--- a/gtests/net/packetdrill/sctp.h
+++ b/gtests/net/packetdrill/sctp.h
@@ -55,6 +55,7 @@ struct sctp_common_header {
 #define SCTP_I_DATA_CHUNK_TYPE				0x40
 #define SCTP_RECONFIG_CHUNK_TYPE			0x82
 #define SCTP_PAD_CHUNK_TYPE				0x84
+#define SCTP_FORWARD_TSN_CHUNK_TYPE                     0xc0
 
 #define MAX_SCTP_CHUNK_BYTES	0xffff
 
@@ -241,6 +242,19 @@ struct sctp_reconfig_chunk {
 	__u8 parameter[];
 } __packed;
 
+struct sctp_stream_identifier_block {
+	__u16 stream;
+        __u16 stream_sequence;
+} __packed;
+
+struct sctp_forward_tsn_chunk {
+	__u8 type;
+	__u8 flags;
+	__be16 length;
+	__be32 cum_tsn;
+	struct sctp_stream_identifier_block stream_identifier_blocks[];
+} __packed;
+
 #define SCTP_HEARTBEAT_INFORMATION_PARAMETER_TYPE	0x0001
 #define SCTP_IPV4_ADDRESS_PARAMETER_TYPE		0x0005
 #define SCTP_IPV6_ADDRESS_PARAMETER_TYPE		0x0006
diff --git a/gtests/net/packetdrill/sctp_packet.c b/gtests/net/packetdrill/sctp_packet.c
index 725d59ba..a0ee2576 100644
--- a/gtests/net/packetdrill/sctp_packet.c
+++ b/gtests/net/packetdrill/sctp_packet.c
@@ -234,6 +234,65 @@ sctp_sack_block_list_item_dup_new(u32 tsn)
 	return item;
 }
 
+struct sctp_forward_tsn_sids_list *
+sctp_forward_tsn_sids_list_new () {
+	struct sctp_forward_tsn_sids_list *list;
+
+	list = malloc(sizeof(struct sctp_forward_tsn_sids_list));
+	assert(list != NULL);
+	list->first = NULL;
+	list->last = NULL;
+	list->nr_entries = 0;
+	return list;
+}
+
+void
+sctp_forward_tsn_sids_list_append(struct sctp_forward_tsn_sids_list *list,
+			          struct sctp_forward_tsn_sids_list_item *item) {
+	assert(item->next == NULL);
+	if (list->last == NULL) {
+		assert(list->first == NULL);
+		assert(list->nr_entries == 0);
+		list->first = item;
+	} else {
+		assert(list->first != NULL);
+		list->last->next = item;
+	}
+	list->last = item;
+	list->nr_entries++;
+}
+
+void sctp_forward_tsn_sids_list_free (struct sctp_forward_tsn_sids_list *list) {
+	struct sctp_forward_tsn_sids_list_item *current_item, *next_item;
+
+	if (list == NULL) {
+		return;
+	}
+	current_item = list->first;
+	while (current_item != NULL) {
+		assert(list->nr_entries > 0);
+		next_item = current_item->next;
+		assert(next_item != NULL || current_item == list->last);
+		free(current_item);
+		current_item = next_item;
+		list->nr_entries--;
+	}
+	assert(list->nr_entries == 0);
+	free(list);
+}
+
+struct sctp_forward_tsn_sids_list_item *
+sctp_forward_tsn_sids_list_item_new(u16 stream_identifier, u16 stream_sequence_number) {
+	struct sctp_forward_tsn_sids_list_item *item;
+
+	item = malloc(sizeof(struct sctp_forward_tsn_sids_list_item));
+	assert(item != NULL);
+	item->next = NULL;
+	item->stream_identifier = stream_identifier;
+	item->stream_sequence_number= stream_sequence_number;
+	return item;
+}
+
 struct sctp_address_type_list *
 sctp_address_type_list_new(void)
 {
@@ -1283,6 +1342,62 @@ sctp_pad_chunk_new(s64 flgs, s64 len, u8* padding)
 	                                sctp_cause_list_new());
 }
 
+struct sctp_chunk_list_item *
+sctp_forward_tsn_chunk_new(u32 cum_tsn, struct sctp_forward_tsn_sids_list *sids) {
+	struct sctp_forward_tsn_chunk *chunk;
+	struct sctp_forward_tsn_sids_list_item *item;
+	
+	DEBUGP("sctp_forward_tsn_chunk_new called with cum_tsn = %d and sids_list = %p", cum_tsn, sids);
+	
+	u32 flags;
+	u32 length;
+	u16 i, nr_sids;
+
+	flags = 0;
+	length = sizeof(struct sctp_forward_tsn_chunk);
+	if (sids == NULL) {
+		nr_sids = 0;
+		flags |= FLAG_CHUNK_LENGTH_NOCHECK;
+		flags |= FLAG_FORWARD_TSN_CHUNK_SIDS_NOCHECK;
+	} else {
+		nr_sids = sids->nr_entries;
+		length += nr_sids * sizeof(struct sctp_stream_identifier_block);
+	}
+	
+	assert(is_valid_u16(length));
+	assert(length % 4 == 0);
+	chunk = malloc(length);
+	assert(chunk != NULL);
+	chunk->type = SCTP_FORWARD_TSN_CHUNK_TYPE;
+	chunk->flags = 0;
+	chunk->length = htons(length);
+	if (cum_tsn == -1) {
+		chunk->cum_tsn = htonl(0);
+		flags |= FLAG_FORWARD_TSN_CHUNK_CUM_TSN_NOCHECK;
+	} else {
+		chunk->cum_tsn = htonl((u32)cum_tsn);
+	}
+	
+	if (nr_sids == 0 || sids == NULL) {
+		flags |= FLAG_FORWARD_TSN_CHUNK_SIDS_NOCHECK;
+	}
+
+	if (sids != NULL) {
+		for (i = 0, item = sids->first;
+		     (i < nr_sids) && (item != NULL);
+		     i++, item = item->next) {
+			chunk->stream_identifier_blocks[i].stream= htons(item->stream_identifier);
+			chunk->stream_identifier_blocks[i].stream_sequence = htons(item->stream_sequence_number);
+		}
+		
+		assert((i == nr_sids) && (item == NULL));
+	}
+	return sctp_chunk_list_item_new((struct sctp_chunk *)chunk,
+	                                length, flags,
+	                                sctp_parameter_list_new(),
+	                                sctp_cause_list_new());
+}
+
 struct sctp_chunk_list_item *
 sctp_reconfig_chunk_new(s64 flgs, struct sctp_parameter_list *parameters)
 {
@@ -2966,6 +3081,19 @@ new_sctp_packet(int address_family,
 				break;
 			case SCTP_RECONFIG_CHUNK_TYPE:
 				break;
+			case SCTP_FORWARD_TSN_CHUNK_TYPE:
+				if (chunk_item->flags & FLAG_FORWARD_TSN_CHUNK_CUM_TSN_NOCHECK) {
+					asprintf(error,
+						 "cum tsn must be specified for inbound packets");
+					return NULL;
+				}
+				if (chunk_item->flags & FLAG_FORWARD_TSN_CHUNK_SIDS_NOCHECK) {
+					// TODO: is this true or are FORWARD-TSN-Chunks that only contain the new cum tsn valid?
+					asprintf(error,
+						 "at least one stream number and stream sequence number must be specified for inbound packets");
+					return NULL;
+				}
+				break;
 			default:
 				if (chunk_item->flags & FLAG_CHUNK_TYPE_NOCHECK) {
 					asprintf(error,
diff --git a/gtests/net/packetdrill/sctp_packet.h b/gtests/net/packetdrill/sctp_packet.h
index 01922281..8d34127f 100644
--- a/gtests/net/packetdrill/sctp_packet.h
+++ b/gtests/net/packetdrill/sctp_packet.h
@@ -104,6 +104,31 @@ sctp_sack_block_list_item_gap_new(u16 start, u16 end);
 struct sctp_sack_block_list_item *
 sctp_sack_block_list_item_dup_new(u32 tsn);
 
+struct sctp_forward_tsn_sids_list_item {
+	struct sctp_forward_tsn_sids_list_item *next;
+	u16 stream_identifier;
+	u16 stream_sequence_number;
+}; 
+
+struct sctp_forward_tsn_sids_list {
+	struct sctp_forward_tsn_sids_list_item *first;
+	struct sctp_forward_tsn_sids_list_item *last;
+	u16 nr_entries;
+};
+
+struct sctp_forward_tsn_sids_list *
+sctp_forward_tsn_sids_list_new ();
+
+void
+sctp_forward_tsn_sids_list_append(struct sctp_forward_tsn_sids_list *list,
+			          struct sctp_forward_tsn_sids_list_item *item);
+
+void sctp_forward_tsn_sids_list_free (struct sctp_forward_tsn_sids_list *list);
+
+struct sctp_forward_tsn_sids_list_item *
+sctp_forward_tsn_sids_list_item_new(u16 stream_identifier, u16 stream_sequence_number);
+
+
 struct sctp_address_type_list_item {
 	struct sctp_address_type_list_item *next;
 	u16 address_type;
@@ -313,6 +338,12 @@ sctp_i_data_chunk_new(s64 flgs, s64 len, s64 tsn, s64 sid, s64 res, s64 mid,
 struct sctp_chunk_list_item *
 sctp_pad_chunk_new(s64 flgs, s64 len, u8* padding);
 
+#define FLAG_FORWARD_TSN_CHUNK_CUM_TSN_NOCHECK  0x00000100
+#define FLAG_FORWARD_TSN_CHUNK_SIDS_NOCHECK     0x00000200
+
+struct sctp_chunk_list_item *
+sctp_forward_tsn_chunk_new(u32 cum_tsn, struct sctp_forward_tsn_sids_list *sids_list);
+
 struct sctp_chunk_list_item *
 sctp_reconfig_chunk_new(s64 flgs, struct sctp_parameter_list *parameters);
 
-- 
GitLab