From ca0ca32d475ddfbbc2db8f027674726c4653a2c9 Mon Sep 17 00:00:00 2001 From: Michael Tuexen <tuexen@fh-muenster.de> Date: Wed, 7 Jan 2015 11:39:19 +0100 Subject: [PATCH] Add support for SCTP packet parsing and string conversion. This commit adds support for parsing SCTP packets and converting them to strings. Currently only chunks, parameters and error causes specified in RFC 4960 are supported. Support for other RFCs will be added in the future. Tested on FreeBSD (amd64 and arm) and Linux. Signed-off-by: Michael Tuexen <tuexen@fh-muenster.de> --- gtests/net/packetdrill/Makefile.common | 1 + gtests/net/packetdrill/header.h | 3 + gtests/net/packetdrill/packet.c | 2 + gtests/net/packetdrill/packet.h | 29 +- gtests/net/packetdrill/packet_parser.c | 58 +- gtests/net/packetdrill/packet_parser_test.c | 140 +- gtests/net/packetdrill/packet_to_string.c | 60 +- .../net/packetdrill/packet_to_string_test.c | 405 +++++- gtests/net/packetdrill/platforms.h | 2 +- gtests/net/packetdrill/script.c | 2 - gtests/net/packetdrill/sctp.h | 326 ++++- gtests/net/packetdrill/sctp_chunk_to_string.c | 1266 +++++++++++++++++ gtests/net/packetdrill/sctp_chunk_to_string.h | 40 + gtests/net/packetdrill/sctp_iterator.c | 200 +++ gtests/net/packetdrill/sctp_iterator.h | 102 ++ gtests/net/packetdrill/socket.h | 14 +- gtests/net/packetdrill/types.h | 5 +- 17 files changed, 2606 insertions(+), 49 deletions(-) create mode 100644 gtests/net/packetdrill/sctp_chunk_to_string.c create mode 100644 gtests/net/packetdrill/sctp_chunk_to_string.h create mode 100644 gtests/net/packetdrill/sctp_iterator.c create mode 100644 gtests/net/packetdrill/sctp_iterator.h diff --git a/gtests/net/packetdrill/Makefile.common b/gtests/net/packetdrill/Makefile.common index 9ff5027c..e58ab854 100644 --- a/gtests/net/packetdrill/Makefile.common +++ b/gtests/net/packetdrill/Makefile.common @@ -24,6 +24,7 @@ packetdrill-lib := \ mpls_packet.o \ run.o run_command.o run_packet.o run_system_call.o \ script.o socket.o system.o \ + sctp_chunk_to_string.o sctp_iterator.o \ tcp_options.o tcp_options_iterator.o tcp_options_to_string.o \ logging.o types.o lexer.o parser.o \ fmemopen.o open_memstream.o \ diff --git a/gtests/net/packetdrill/header.h b/gtests/net/packetdrill/header.h index 389b126d..e0a3e09a 100644 --- a/gtests/net/packetdrill/header.h +++ b/gtests/net/packetdrill/header.h @@ -39,6 +39,7 @@ #include "ip.h" #include "ipv6.h" #include "mpls.h" +#include "sctp.h" #include "tcp.h" #include "udp.h" #include "udplite.h" @@ -52,6 +53,7 @@ enum header_t { HEADER_IPV6, HEADER_GRE, HEADER_MPLS, + HEADER_SCTP, HEADER_TCP, HEADER_UDP, HEADER_UDPLITE, @@ -71,6 +73,7 @@ struct header { struct ipv6 *ipv6; struct gre *gre; struct mpls *mpls; + struct sctp_common_header *sctp; struct tcp *tcp; struct udp *udp; struct udplite *udplite; diff --git a/gtests/net/packetdrill/packet.c b/gtests/net/packetdrill/packet.c index bb62f3b6..dbb33b17 100644 --- a/gtests/net/packetdrill/packet.c +++ b/gtests/net/packetdrill/packet.c @@ -41,6 +41,7 @@ struct header_type_info header_types[HEADER_NUM_TYPES] = { { "IPV6", IPPROTO_IPV6, ETHERTYPE_IPV6, ipv6_header_finish }, { "GRE", IPPROTO_GRE, 0, gre_header_finish }, { "MPLS", 0, ETHERTYPE_MPLS_UC, mpls_header_finish }, + { "SCTP", IPPROTO_SCTP, 0, NULL }, { "TCP", IPPROTO_TCP, 0, NULL }, { "UDP", IPPROTO_UDP, 0, NULL }, { "UDPLITE", IPPROTO_UDPLITE, 0, NULL }, @@ -158,6 +159,7 @@ static struct packet *packet_copy_with_headroom(struct packet *old_packet, /* Set up layer 3 header pointer. */ packet->ipv4 = offset_ptr(old_base, new_base, old_packet->ipv4); packet->ipv6 = offset_ptr(old_base, new_base, old_packet->ipv6); + packet->sctp = offset_ptr(old_base, new_base, old_packet->sctp); packet->tcp = offset_ptr(old_base, new_base, old_packet->tcp); packet->udp = offset_ptr(old_base, new_base, old_packet->udp); packet->udplite = offset_ptr(old_base, new_base, old_packet->udplite); diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h index 57f8f73d..06ddf02d 100644 --- a/gtests/net/packetdrill/packet.h +++ b/gtests/net/packetdrill/packet.h @@ -36,6 +36,7 @@ #include "icmpv6.h" #include "ip.h" #include "ipv6.h" +#include "sctp.h" #include "tcp.h" #include "udp.h" #include "udplite.h" @@ -47,6 +48,7 @@ #define MAX_TCP_HEADER_BYTES (15*4) #define MAX_TCP_DATAGRAM_BYTES (64*1024) /* for sanity-checking */ +#define MAX_SCTP_DATAGRAM_BYTES (64*1024) /* for sanity-checking */ #define MAX_UDP_DATAGRAM_BYTES (64*1024) /* for sanity-checking */ #define MAX_UDPLITE_DATAGRAM_BYTES (64*1024) /* for sanity-checking */ @@ -92,6 +94,8 @@ struct packet { struct ipv6 *ipv6; /* start of IPv6 header, if present */ /* Layer 4 */ + struct sctp_common_header *sctp; + /* start of SCTP common header, if present */ struct tcp *tcp; /* start of TCP header, if present */ struct udp *udp; /* start of UDP header, if present */ struct udplite *udplite;/* start of UDPLite header, if present */ @@ -220,6 +224,8 @@ static inline int packet_ip_protocol(const struct packet *packet) /* Return the length of an optionless TCP or UDP header. */ static inline int layer4_header_len(int protocol) { + if (protocol == IPPROTO_SCTP) + return sizeof(struct sctp_common_header); if (protocol == IPPROTO_TCP) return sizeof(struct tcp); if (protocol == IPPROTO_UDP) @@ -230,6 +236,13 @@ static inline int layer4_header_len(int protocol) return 0; } +/* Return the length of the SCTP common header. */ +static inline int packet_sctp_header_len(const struct packet *packet) +{ + assert(packet->sctp); + return sizeof(struct sctp_common_header); +} + /* Return the length of the TCP header, including options. */ static inline int packet_tcp_header_len(const struct packet *packet) { @@ -288,6 +301,8 @@ static inline void packet_set_tcp_ts_ecr(struct packet *packet, u32 ts_ecr) /* Return a pointer to the TCP/UDP data payload. */ static inline u8 *packet_payload(struct packet *packet) { + if (packet->sctp) + return ((u8 *) packet->sctp) + packet_sctp_header_len(packet); if (packet->tcp) return ((u8 *) packet->tcp) + packet_tcp_header_len(packet); if (packet->udp) @@ -295,7 +310,7 @@ static inline u8 *packet_payload(struct packet *packet) if (packet->udplite) return ((u8 *) packet->udplite) + packet_udplite_header_len(packet); - assert(!"no valid payload; not TCP or UDP or UDPLite!?"); + assert(!"no valid payload; not SCTP or TCP or UDP or UDPLite!?"); return NULL; } @@ -360,7 +375,7 @@ static inline int packet_echoed_ip_protocol(struct packet *packet) return 0; } -/* Return the location of the TCP or UDP header echoed by an ICMP message. */ +/* Return the location of the transport header echoed by an ICMP message. */ static inline u8 *packet_echoed_layer4_header(struct packet *packet) { u8 *echoed_ip = packet_echoed_ip_header(packet); @@ -368,6 +383,16 @@ static inline u8 *packet_echoed_layer4_header(struct packet *packet) return echoed_ip + ip_header_len; } +/* Return the location of the SCTP common header echoed by an ICMP message. */ +static inline struct sctp_common_header * +packet_echoed_sctp_header(struct packet *packet) +{ + if (packet_echoed_ip_protocol(packet) == IPPROTO_SCTP) + return (struct sctp_common_header *) + (packet_echoed_layer4_header(packet)); + return NULL; +} + /* Return the location of the TCP header echoed by an ICMP message. */ static inline struct tcp *packet_echoed_tcp_header(struct packet *packet) { diff --git a/gtests/net/packetdrill/packet_parser.c b/gtests/net/packetdrill/packet_parser.c index 1bd6d0d3..6599777d 100644 --- a/gtests/net/packetdrill/packet_parser.c +++ b/gtests/net/packetdrill/packet_parser.c @@ -210,7 +210,14 @@ static int parse_ipv4(struct packet *packet, u8 *header_start, u8 *packet_end, } const u16 checksum = ipv4_checksum(ipv4, ip_header_bytes); if (checksum != 0) { - asprintf(error, "Bad IP checksum"); + u16 received_checksum, computed_checksum; + + received_checksum = ntohs(ipv4->check); + ipv4->check = 0; + computed_checksum = ntohs(ipv4_checksum(ipv4, ip_header_bytes)); + ipv4->check = htons(received_checksum); + asprintf(error, "Bad IP checksum 0x%04x (expected 0x%04x)", + received_checksum, computed_checksum); goto error_out; } @@ -326,6 +333,49 @@ error_out: return PACKET_BAD; } +/* Parse the SCTP header. Return a packet_parse_result_t. */ +static int parse_sctp(struct packet *packet, u8 *layer4_start, int layer4_bytes, + u8 *packet_end, char **error) +{ + u32 received_crc32c, computed_crc32c; + struct header *sctp_header = NULL; + u8 *p = layer4_start; + + assert(layer4_bytes >= 0); + if (layer4_bytes < sizeof(struct sctp_common_header)) { + asprintf(error, "Truncated SCTP common header"); + goto error_out; + } + packet->sctp = (struct sctp_common_header *) p; + + received_crc32c = ntohl(packet->sctp->crc32c); + packet->sctp->crc32c = htonl(0); + computed_crc32c = ntohl(sctp_crc32c(packet->sctp, layer4_bytes)); + packet->sctp->crc32c = htonl(received_crc32c); + if (received_crc32c != computed_crc32c) { + asprintf(error, "Bad SCTP checksum 0x%08x (expected 0x%08x)", + received_crc32c, computed_crc32c); + goto error_out; + } + const int sctp_header_len = sizeof(struct sctp_common_header); + sctp_header = packet_append_header(packet, HEADER_SCTP, + sctp_header_len); + if (sctp_header == NULL) { + asprintf(error, "Too many nested headers at SCTP header"); + goto error_out; + } + sctp_header->total_bytes = layer4_bytes; + p += layer4_bytes; + assert(p <= packet_end); + + DEBUGP("SCTP src port: %d\n", ntohs(packet->sctp->src_port)); + DEBUGP("SCTP dst port: %d\n", ntohs(packet->sctp->dst_port)); + return PACKET_OK; + +error_out: + return PACKET_BAD; +} + /* Parse the TCP header. Return a packet_parse_result_t. */ static int parse_tcp(struct packet *packet, u8 *layer4_start, int layer4_bytes, u8 *packet_end, char **error) @@ -626,7 +676,11 @@ static int parse_layer4(struct packet *packet, u8 *layer4_start, int layer4_protocol, int layer4_bytes, u8 *packet_end, bool *is_inner, char **error) { - if (layer4_protocol == IPPROTO_TCP) { + if (layer4_protocol == IPPROTO_SCTP) { + *is_inner = true; /* found inner-most layer 4 */ + return parse_sctp(packet, layer4_start, layer4_bytes, + packet_end, error); + } else if (layer4_protocol == IPPROTO_TCP) { *is_inner = true; /* found inner-most layer 4 */ return parse_tcp(packet, layer4_start, layer4_bytes, packet_end, error); diff --git a/gtests/net/packetdrill/packet_parser_test.c b/gtests/net/packetdrill/packet_parser_test.c index cbc9c3ce..6eb6b0bd 100644 --- a/gtests/net/packetdrill/packet_parser_test.c +++ b/gtests/net/packetdrill/packet_parser_test.c @@ -29,6 +29,96 @@ #include <stdlib.h> #include <string.h> +static void test_parse_sctp_ipv4_packet(void) +{ + /* A SCTP/IPv4 packet. */ + u8 data[] = { + /* 1.1.1.1:1234 > 192.168.0.1:60213 + * sctp: ABORT[] */ + 0x45, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x84, 0xf8, 0xaa, 0x01, 0x01, 0x01, 0x01, + 0xc0, 0xa8, 0x00, 0x01, 0x04, 0xd2, 0xeb, 0x35, + 0x01, 0x02, 0x03, 0x04, 0xab, 0x0c, 0xeb, 0x7a, + 0x06, 0x01, 0x00, 0x04 + }; + + struct packet *packet = packet_new(sizeof(data)); + + /* Populate and parse a packet */ + memcpy(packet->buffer, data, sizeof(data)); + char *error = NULL; + enum packet_parse_result_t result = + parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + assert(result == PACKET_OK); + assert(error == NULL); + + struct ipv4 *expected_ipv4 = (struct ipv4 *)(packet->buffer); + struct sctp_common_header *expected_sctp = + (struct sctp_common_header *)(expected_ipv4 + 1); + + assert(packet->ip_bytes == sizeof(data)); + assert(packet->ipv4 == expected_ipv4); + assert(packet->ipv6 == NULL); + assert(packet->sctp == expected_sctp); + assert(packet->tcp == NULL); + assert(packet->udp == NULL); + assert(packet->udplite == NULL); + assert(packet->icmpv4 == NULL); + assert(packet->icmpv6 == NULL); + + assert(packet->time_usecs == 0); + assert(packet->flags == 0); + assert(packet->ecn == 0); + + packet_free(packet); +} + +static void test_parse_sctp_ipv6_packet(void) +{ + /* A SCTP/IPv6 packet. */ + u8 data[] = { + /* 2001:db8::1:54242 > fd3d:fa7b:d17d::1:8080 + * sctp: ABORT[] */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0x84, 0xff, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xfd, 0x3d, 0xfa, 0x7b, 0xd1, 0x7d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xd3, 0xe2, 0x1f, 0x90, 0x01, 0x02, 0x03, 0x04, + 0xc4, 0xb0, 0x47, 0x64, 0x06, 0x01, 0x00, 0x04 + }; + + struct packet *packet = packet_new(sizeof(data)); + + /* Populate and parse a packet */ + memcpy(packet->buffer, data, sizeof(data)); + char *error = NULL; + enum packet_parse_result_t result = + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); + assert(result == PACKET_OK); + assert(error == NULL); + + struct ipv6 *expected_ipv6 = (struct ipv6 *)(packet->buffer); + struct sctp_common_header *expected_sctp = + (struct sctp_common_header *)(expected_ipv6 + 1); + + assert(packet->ip_bytes == sizeof(data)); + assert(packet->ipv4 == NULL); + assert(packet->ipv6 == expected_ipv6); + assert(packet->sctp == expected_sctp); + assert(packet->tcp == NULL); + assert(packet->udp == NULL); + assert(packet->udplite == NULL); + assert(packet->icmpv4 == NULL); + assert(packet->icmpv6 == NULL); + + assert(packet->time_usecs == 0); + assert(packet->flags == 0); + assert(packet->ecn == 0); + + packet_free(packet); +} + static void test_parse_tcp_ipv4_packet(void) { /* A TCP/IPv4 packet. */ @@ -53,8 +143,7 @@ static void test_parse_tcp_ipv4_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -64,6 +153,7 @@ static void test_parse_tcp_ipv4_packet(void) assert(packet->ip_bytes == sizeof(data)); assert(packet->ipv4 == expected_ipv4); assert(packet->ipv6 == NULL); + assert(packet->sctp == NULL); assert(packet->tcp == expected_tcp); assert(packet->udp == NULL); assert(packet->udplite == NULL); @@ -92,7 +182,7 @@ static void test_parse_tcp_ipv6_packet(void) 0xd3, 0xe2, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x80, 0x18, 0x06, 0x60, 0x00, 0x00, 0x02, 0x04, 0x03, 0xe8, - 0x04, 0x02, 0x01, 0x01, 0x01, 0x03, 0x03, 0x07, + 0x04, 0x02, 0x01, 0x01, 0x01, 0x03, 0x03, 0x07 }; struct packet *packet = packet_new(sizeof(data)); @@ -101,8 +191,7 @@ static void test_parse_tcp_ipv6_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -112,6 +201,7 @@ static void test_parse_tcp_ipv6_packet(void) assert(packet->ip_bytes == sizeof(data)); assert(packet->ipv4 == NULL); assert(packet->ipv6 == expected_ipv6); + assert(packet->sctp == NULL); assert(packet->tcp == expected_tcp); assert(packet->udp == NULL); assert(packet->udplite == NULL); @@ -133,7 +223,7 @@ static void test_parse_udp_ipv4_packet(void) 0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0xff, 0x11, 0x39, 0x22, 0xc0, 0x00, 0x02, 0x01, 0xc0, 0xa8, 0x00, 0x01, 0x1f, 0x90, 0xe1, 0xf5, - 0x00, 0x0c, 0x7b, 0xa5, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x7b, 0xa5, 0x00, 0x00, 0x00, 0x00 }; struct packet *packet = packet_new(sizeof(data)); @@ -142,8 +232,7 @@ static void test_parse_udp_ipv4_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -153,6 +242,7 @@ static void test_parse_udp_ipv4_packet(void) assert(packet->ip_bytes == sizeof(data)); assert(packet->ipv4 == expected_ipv4); assert(packet->ipv6 == NULL); + assert(packet->sctp == NULL); assert(packet->tcp == NULL); assert(packet->udp == expected_udp); assert(packet->udplite == NULL); @@ -177,7 +267,7 @@ static void test_parse_udp_ipv6_packet(void) 0xfd, 0x3d, 0xfa, 0x7b, 0xd1, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1f, 0x90, 0xc9, 0x65, 0x00, 0x0c, 0x1f, 0xee, - 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; struct packet *packet = packet_new(sizeof(data)); @@ -186,8 +276,7 @@ static void test_parse_udp_ipv6_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -197,6 +286,7 @@ static void test_parse_udp_ipv6_packet(void) assert(packet->ip_bytes == sizeof(data)); assert(packet->ipv4 == NULL); assert(packet->ipv6 == expected_ipv6); + assert(packet->sctp == NULL); assert(packet->tcp == NULL); assert(packet->udp == expected_udp); assert(packet->udplite == NULL); @@ -228,8 +318,7 @@ static void test_parse_udplite_ipv4_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -240,6 +329,7 @@ static void test_parse_udplite_ipv4_packet(void) assert(packet->ip_bytes == sizeof(data)); assert(packet->ipv4 == expected_ipv4); assert(packet->ipv6 == NULL); + assert(packet->sctp == NULL); assert(packet->tcp == NULL); assert(packet->udp == NULL); assert(packet->udplite == expected_udplite); @@ -274,8 +364,7 @@ static void test_parse_udplite_ipv6_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -286,6 +375,7 @@ static void test_parse_udplite_ipv6_packet(void) assert(packet->ip_bytes == sizeof(data)); assert(packet->ipv4 == NULL); assert(packet->ipv6 == expected_ipv6); + assert(packet->sctp == NULL); assert(packet->tcp == NULL); assert(packet->udp == NULL); assert(packet->udplite == expected_udplite); @@ -326,8 +416,7 @@ static void test_parse_ipv4_gre_ipv4_tcp_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -365,6 +454,7 @@ static void test_parse_ipv4_gre_ipv4_tcp_packet(void) assert(packet->ip_bytes == sizeof(data)); assert(packet->ipv4 == expected_inner_ipv4); assert(packet->ipv6 == NULL); + assert(packet->sctp == NULL); assert(packet->tcp == expected_tcp); assert(packet->udp == NULL); assert(packet->udplite == NULL); @@ -418,8 +508,7 @@ static void test_parse_ipv4_gre_mpls_ipv4_tcp_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -464,6 +553,7 @@ static void test_parse_ipv4_gre_mpls_ipv4_tcp_packet(void) assert(packet->ip_bytes == sizeof(data)); assert(packet->ipv4 == expected_inner_ipv4); assert(packet->ipv6 == NULL); + assert(packet->sctp == NULL); assert(packet->tcp == expected_tcp); assert(packet->udp == NULL); assert(packet->udplite == NULL); @@ -487,7 +577,7 @@ static void test_parse_icmpv4_packet(void) 0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x40, 0x00, 0x40, 0x01, 0xb6, 0xc4, 0xc0, 0xa8, 0x01, 0x65, 0xc0, 0xa8, 0x01, 0x67, 0x08, 0x00, 0xcd, 0x2e, - 0x2a, 0xd0, 0x00, 0x01, + 0x2a, 0xd0, 0x00, 0x01 }; struct packet *packet = packet_new(sizeof(data)); @@ -496,8 +586,7 @@ static void test_parse_icmpv4_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -507,6 +596,7 @@ static void test_parse_icmpv4_packet(void) assert(packet->ip_bytes == sizeof(data)); assert(packet->ipv4 == expected_ipv4); assert(packet->ipv6 == NULL); + assert(packet->sctp == NULL); assert(packet->tcp == NULL); assert(packet->udp == NULL); assert(packet->udplite == NULL); @@ -550,8 +640,7 @@ static void test_parse_icmpv6_packet(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -561,6 +650,7 @@ static void test_parse_icmpv6_packet(void) assert(packet->ip_bytes == sizeof(data)); assert(packet->ipv4 == NULL); assert(packet->ipv6 == expected_ipv6); + assert(packet->sctp == NULL); assert(packet->tcp == NULL); assert(packet->udp == NULL); assert(packet->udplite == NULL); @@ -576,6 +666,8 @@ static void test_parse_icmpv6_packet(void) int main(void) { + test_parse_sctp_ipv4_packet(); + test_parse_sctp_ipv6_packet(); test_parse_tcp_ipv4_packet(); test_parse_tcp_ipv6_packet(); test_parse_udp_ipv4_packet(); diff --git a/gtests/net/packetdrill/packet_to_string.c b/gtests/net/packetdrill/packet_to_string.c index 9fc5e5fb..08573d39 100644 --- a/gtests/net/packetdrill/packet_to_string.c +++ b/gtests/net/packetdrill/packet_to_string.c @@ -27,6 +27,8 @@ #include <stdlib.h> #include "socket.h" +#include "sctp_iterator.h" +#include "sctp_chunk_to_string.h" #include "tcp_options_to_string.h" static void endpoints_to_string(FILE *s, const struct packet *packet) @@ -90,7 +92,7 @@ static int ipv6_header_to_string(FILE *s, struct packet *packet, int layer, static int gre_header_to_string(FILE *s, struct packet *packet, int layer, enum dump_format_t format, char **error) { - fprintf(s, "gre: "); + fputs("gre: ", s); return STATUS_OK; } @@ -102,7 +104,7 @@ static int mpls_header_to_string(FILE *s, struct packet *packet, int layer, int num_entries = header->header_bytes / sizeof(struct mpls); int i = 0; - fprintf(s, "mpls"); + fputs("mpls", s); for (i = 0; i < num_entries; ++i) { const struct mpls *mpls = header->h.mpls + i; @@ -118,6 +120,46 @@ static int mpls_header_to_string(FILE *s, struct packet *packet, int layer, return STATUS_OK; } +static int sctp_packet_to_string(FILE *s, struct packet *packet, + enum dump_format_t format, char **error) +{ + struct sctp_chunks_iterator iter; + struct sctp_chunk *chunk; + u16 index; + int result = STATUS_OK; /* return value */ + + assert(*error == NULL); + if ((format == DUMP_FULL) || (format == DUMP_VERBOSE)) { + endpoints_to_string(s, packet); + fputc(' ', s); + } + + fputs("sctp:", s); + + index = 0; + for (chunk = sctp_chunks_begin(packet, &iter, error); + chunk != NULL; + chunk = sctp_chunks_next(&iter, error)) { + if (index == 0) + fputc(' ', s); + else + fputs("; ", s); + if (*error != NULL) + break; + result = sctp_chunk_to_string(s, chunk, error); + if (result != STATUS_OK) + break; + index++; + } + if (*error != NULL) + result = STATUS_ERR; + + if (format == DUMP_VERBOSE) + packet_buffer_to_string(s, packet); + + return result; +} + /* Print a string representation of the TCP packet: * direction opt_ip_info flags seq ack window tcp_options */ @@ -216,7 +258,7 @@ static int udplite_packet_to_string(FILE *s, struct packet *packet, static int icmpv4_packet_to_string(FILE *s, struct packet *packet, enum dump_format_t format, char **error) { - fprintf(s, "icmpv4"); + fputs("icmpv4", s); /* TODO(ncardwell): print type, code; use tables from icmp_packet.c */ return STATUS_OK; } @@ -224,7 +266,7 @@ static int icmpv4_packet_to_string(FILE *s, struct packet *packet, static int icmpv6_packet_to_string(FILE *s, struct packet *packet, enum dump_format_t format, char **error) { - fprintf(s, "icmpv6"); + fputs("icmpv6", s); /* TODO(ncardwell): print type, code; use tables from icmp_packet.c */ return STATUS_OK; } @@ -251,7 +293,6 @@ static int encap_header_to_string(FILE *s, struct packet *packet, int layer, return printer(s, packet, layer, format, error); } - int packet_to_string(struct packet *packet, enum dump_format_t format, char **ascii_string, char **error) @@ -272,9 +313,12 @@ int packet_to_string(struct packet *packet, } if ((packet->ipv4 == NULL) && (packet->ipv6 == NULL)) { - fprintf(s, "[NO IP HEADER]"); + fputs("[NO IP HEADER]", s); } else { - if (packet->tcp != NULL) { + if (packet->sctp != NULL) { + if (sctp_packet_to_string(s, packet, format, error)) + goto out; + } else if (packet->tcp != NULL) { if (tcp_packet_to_string(s, packet, format, error)) goto out; } else if (packet->udp != NULL) { @@ -290,7 +334,7 @@ int packet_to_string(struct packet *packet, if (icmpv6_packet_to_string(s, packet, format, error)) goto out; } else { - fprintf(s, "[No TCP or UDP or UDPLite or ICMP header]"); + fputs("[No SCTP, TCP, UDP, UDPLite or ICMP header]", s); } } diff --git a/gtests/net/packetdrill/packet_to_string_test.c b/gtests/net/packetdrill/packet_to_string_test.c index 8851437a..0e0f0788 100644 --- a/gtests/net/packetdrill/packet_to_string_test.c +++ b/gtests/net/packetdrill/packet_to_string_test.c @@ -30,6 +30,399 @@ #include "ethernet.h" #include "packet_parser.h" +static void test_sctp_ipv4_packet_to_string(void) +{ + /* An IPv4/SCTP packet. */ + u8 data[] = { + /* IPv4: */ + 0x45, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x84, 0xb5, 0x50, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x01, + /* SCTP Common Header: */ + 0x04, 0xd2, 0x1f, 0x90, 0x01, 0x02, 0x03, 0x04, + 0x3d, 0x99, 0xbf, 0xe3, + /* SCTP ABORT Chunk: */ + 0x06, 0x01, 0x00, 0x04 + }; + + struct packet *packet = packet_new(sizeof(data)); + + /* Populate and parse a packet */ + memcpy(packet->buffer, data, sizeof(data)); + char *error = NULL; + enum packet_parse_result_t result = + parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); + assert(result == PACKET_OK); + assert(error == NULL); + + int status = 0; + char *dump = NULL, *expected = NULL; + + /* Test a DUMP_SHORT dump */ + status = packet_to_string(packet, DUMP_SHORT, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "sctp: ABORT[flags=T]"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_FULL dump */ + status = packet_to_string(packet, DUMP_FULL, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "2.2.2.2:1234 > 1.1.1.1:8080 " + "sctp: ABORT[flags=T]"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_VERBOSE dump */ + status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "2.2.2.2:1234 > 1.1.1.1:8080 " + "sctp: ABORT[flags=T]" + "\n" + "0x0000: 45 00 00 24 00 00 00 00 ff 84 b5 50 02 02 02 02 " "\n" + "0x0010: 01 01 01 01 04 d2 1f 90 01 02 03 04 3d 99 bf e3 " "\n" + "0x0020: 06 01 00 04 " "\n"; + assert(strcmp(dump, expected) == 0); + free(dump); + + packet_free(packet); +} + +static void test_sctp_ipv6_packet_to_string(void) +{ + /* An IPv6/SCTP packet. */ + u8 data[] = { + /* IPv6 Base Header: */ + 0x60, 0x00, 0x00, 0x00, 0x01, 0x78, 0x84, 0xff, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, + /* SCTP Common Header: */ + 0x04, 0xd2, 0x1f, 0x90, + 0x01, 0x02, 0x03, 0x04, + 0x1b, 0x68, 0x75, 0x8e, + /* SCTP DATA Chunk */ + 0x00, 0x0f, 0x00, 0x13, + 0x01, 0x02, 0x03, 0x04, + 0x00, 0xff, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x00, + /* SCTP INIT Chunk */ + 0x01, 0x00, 0x00, 0x50, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x0f, + 0x01, 0x02, 0x03, 0x04, + 0x00, 0x05, 0x00, 0x08, + 0x01, 0x02, 0x03, 0x04, + 0x00, 0x06, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x09, 0x00, 0x08, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x06, + 0x40, 0x41, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x0a, + 0x00, 0x05, 0x00, 0x06, + 0x00, 0x0b, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x04, + /* SCTP INIT_ACK Chunk */ + 0x02, 0x00, 0x00, 0x24, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x0f, + 0x01, 0x02, 0x03, 0x04, + 0x00, 0x07, 0x00, 0x07, + 0x01, 0x02, 0x03, 0x00, + 0x00, 0x08, 0x00, 0x08, + 0x80, 0x01, 0x00, 0x04, + /* SCTP SACK Chunk */ + 0x03, 0x00, 0x00, 0x20, + 0x01, 0x02, 0x03, 0x04, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x03, + 0x00, 0x05, 0x00, 0x0f, + 0x10, 0x00, 0x10, 0x14, + 0x01, 0x02, 0x03, 0x04, + /* SCTP HEARTBEAT Chunk */ + 0x04, 0x00, 0x00, 0x06, + 0x01, 0x02, 0x00, 0x00, + /* SCTP HEARTBEAT-ACK Chunk */ + 0x05, 0x00, 0x00, 0x06, + 0x01, 0x02, 0x00, 0x00, + /* SCTP ABORT Chunk: */ + 0x06, 0x01, 0x00, 0x04, + /* SCTP ABORT Chunk: */ + 0x06, 0x00, 0x00, 0x80, + 0x00, 0x01, 0x00, 0x08, + 0x00, 0xff, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x0a, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x07, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x08, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x0c, + 0x00, 0x0b, 0x00, 0x06, + 0x40, 0x41, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x0c, + 0xfe, 0x05, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x04, + 0x00, 0x08, 0x00, 0x10, + 0x80, 0x0a, 0x00, 0x04, + 0x80, 0x0b, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x08, + 0x01, 0x02, 0x03, 0x04, + 0x00, 0x0a, 0x00, 0x04, + 0x00, 0x0b, 0x00, 0x14, + 0x00, 0x05, 0x00, 0x08, + 0x01, 0x02, 0x03, 0x04, + 0x00, 0x05, 0x00, 0x08, + 0x02, 0x03, 0x04, 0x05, + 0x00, 0x0c, 0x00, 0x07, + 0x42, 0x59, 0x45, 0x00, + 0x00, 0x0d, 0x00, 0x06, + 0x40, 0x40, 0x00, 0x00, + /* SCTP SHUTDOWN Chunk */ + 0x07, 0x00, 0x00, 0x08, + 0x01, 0x02, 0x03, 0x04, + /* SCTP SHUTDOWN_ACK Chunk */ + 0x08, 0x00, 0x00, 0x04, + /* SCTP ERROR Chunk */ + 0x09, 0x00, 0x00, 0x04, + /* SCTP COOKIE_ECHO Chunk */ + 0x0a, 0x00, 0x00, 0x05, + 0x45, 0x00, 0x00, 0x00, + /* SCTP COOKIE_ACK Chunk */ + 0x0b, 0x00, 0x00, 0x04, + /* SCTP ECNE Chunk */ + 0x0c, 0x00, 0x00, 0x08, + 0x01, 0x02, 0x03, 0x04, + /* SCTP CWR Chunk */ + 0x0d, 0x00, 0x00, 0x08, + 0x01, 0x02, 0x03, 0x04, + /* SCTP SHUTDOWN_COMPLETE Chunk */ + 0x0e, 0x01, 0x00, 0x04 + }; + + struct packet *packet = packet_new(sizeof(data)); + + /* Populate and parse a packet */ + memcpy(packet->buffer, data, sizeof(data)); + char *error = NULL; + enum packet_parse_result_t result = + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); + assert(result == PACKET_OK); + assert(error == NULL); + + int status = 0; + char *dump = NULL, *expected = NULL; + + /* Test a DUMP_SHORT dump */ + status = packet_to_string(packet, DUMP_SHORT, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "sctp: " + "DATA[flags=IUBE, tsn=16909060, sid=255, ssn=256, ppid=0, " + "payload_len=3]; " + "INIT[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, " + "IPV4_ADDRESS[1.2.3.4], " + "IPV6_ADDRESS[::1], " + "COOKIE_PRESERVATIVE[65536], " + "HOSTNAME[@A], " + "SUPPORTED_ADDRESS_TYPES[IPV4, IPV6, HOSTNAME], " + "ECN_CAPABLE[]]; " + "INIT_ACK[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, " + "STATE_COOKIE[cookie_len=3], " + "UNRECOGNIZED_PARAMETER[" + "PARAMETER[type=0x8001, value=[]]]]; " + "SACK[cum_tsn=16909060, a_rwnd=65536, " + "gaps=[1-3, 5-15, 4096-4116], dups=[16909060]]; " + "HEARTBEAT[info_len=2]; " + "HEARTBEAT_ACK[info_len=2]; " + "ABORT[flags=T]; " + "ABORT[INVALID_STREAM_IDENTIFIER[sid=255], " + "MISSING_MANDATORY_PARAMETER[STATE_COOKIE], " + "STALE_COOKIE_ERROR[staleness=65536], " + "OUT_OF_RESOURCES[], " + "UNRESOLVABLE_ADDRESS[HOSTNAME[@A]], " + "UNRECOGNIZED_CHUNK[" + "CHUNK[type=0xfe, flags=0x05, value=[0x01]]], " + "INVALID_MANDATORY_PARAMETER[], " + "UNRECOGNIZED_PARAMETERS[" + "PARAMETER[type=0x800a, value=[]], " + "PARAMETER[type=0x800b, value=[0x01]]], " + "NO_USER_DATA[tsn=16909060], " + "COOKIE_RECEIVED_WHILE_SHUTDOWN[], " + "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[1.2.3.4], " + "IPV4_ADDRESS[2.3.4.5]], " + "USER_INITIATED_ABORT[BYE], " + "PROTOCOL_VIOLATION[@@]]; " + "SHUTDOWN[tsn=16909060]; " + "SHUTDOWN_ACK[]; " + "ERROR[]; " + "COOKIE_ECHO[cookie_len=1]; " + "COOKIE_ACK[]; " + "ECNE[tsn=16909060]; " + "CWR[tsn=16909060]; " + "SHUTDOWN_COMPLETE[flags=T]"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_FULL dump */ + status = packet_to_string(packet, DUMP_FULL, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "2::2222:1234 > 1::1111:8080 " + "sctp: " + "DATA[flags=IUBE, tsn=16909060, sid=255, ssn=256, ppid=0, " + "payload_len=3]; " + "INIT[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, " + "IPV4_ADDRESS[1.2.3.4], " + "IPV6_ADDRESS[::1], " + "COOKIE_PRESERVATIVE[65536], " + "HOSTNAME[@A], " + "SUPPORTED_ADDRESS_TYPES[IPV4, IPV6, HOSTNAME], " + "ECN_CAPABLE[]]; " + "INIT_ACK[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, " + "STATE_COOKIE[cookie_len=3], " + "UNRECOGNIZED_PARAMETER[" + "PARAMETER[type=0x8001, value=[]]]]; " + "SACK[cum_tsn=16909060, a_rwnd=65536, " + "gaps=[1-3, 5-15, 4096-4116], dups=[16909060]]; " + "HEARTBEAT[info_len=2]; " + "HEARTBEAT_ACK[info_len=2]; " + "ABORT[flags=T]; " + "ABORT[INVALID_STREAM_IDENTIFIER[sid=255], " + "MISSING_MANDATORY_PARAMETER[STATE_COOKIE], " + "STALE_COOKIE_ERROR[staleness=65536], " + "OUT_OF_RESOURCES[], " + "UNRESOLVABLE_ADDRESS[HOSTNAME[@A]], " + "UNRECOGNIZED_CHUNK[" + "CHUNK[type=0xfe, flags=0x05, value=[0x01]]], " + "INVALID_MANDATORY_PARAMETER[], " + "UNRECOGNIZED_PARAMETERS[" + "PARAMETER[type=0x800a, value=[]], " + "PARAMETER[type=0x800b, value=[0x01]]], " + "NO_USER_DATA[tsn=16909060], " + "COOKIE_RECEIVED_WHILE_SHUTDOWN[], " + "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[1.2.3.4], " + "IPV4_ADDRESS[2.3.4.5]], " + "USER_INITIATED_ABORT[BYE], " + "PROTOCOL_VIOLATION[@@]]; " + "SHUTDOWN[tsn=16909060]; " + "SHUTDOWN_ACK[]; " + "ERROR[]; " + "COOKIE_ECHO[cookie_len=1]; " + "COOKIE_ACK[]; " + "ECNE[tsn=16909060]; " + "CWR[tsn=16909060]; " + "SHUTDOWN_COMPLETE[flags=T]"; + assert(strcmp(dump, expected) == 0); + free(dump); + + /* Test a DUMP_VERBOSE dump */ + status = packet_to_string(packet, DUMP_VERBOSE, &dump, &error); + assert(status == STATUS_OK); + assert(error == NULL); + printf("dump = '%s'\n", dump); + expected = + "2::2222:1234 > 1::1111:8080 " + "sctp: " + "DATA[flags=IUBE, tsn=16909060, sid=255, ssn=256, ppid=0, " + "payload_len=3]; " + "INIT[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, " + "IPV4_ADDRESS[1.2.3.4], " + "IPV6_ADDRESS[::1], " + "COOKIE_PRESERVATIVE[65536], " + "HOSTNAME[@A], " + "SUPPORTED_ADDRESS_TYPES[IPV4, IPV6, HOSTNAME], " + "ECN_CAPABLE[]]; " + "INIT_ACK[tag=1, a_rwnd=65536, os=15, is=15, tsn=16909060, " + "STATE_COOKIE[cookie_len=3], " + "UNRECOGNIZED_PARAMETER[" + "PARAMETER[type=0x8001, value=[]]]]; " + "SACK[cum_tsn=16909060, a_rwnd=65536, " + "gaps=[1-3, 5-15, 4096-4116], dups=[16909060]]; " + "HEARTBEAT[info_len=2]; " + "HEARTBEAT_ACK[info_len=2]; " + "ABORT[flags=T]; " + "ABORT[INVALID_STREAM_IDENTIFIER[sid=255], " + "MISSING_MANDATORY_PARAMETER[STATE_COOKIE], " + "STALE_COOKIE_ERROR[staleness=65536], " + "OUT_OF_RESOURCES[], " + "UNRESOLVABLE_ADDRESS[HOSTNAME[@A]], " + "UNRECOGNIZED_CHUNK[" + "CHUNK[type=0xfe, flags=0x05, value=[0x01]]], " + "INVALID_MANDATORY_PARAMETER[], " + "UNRECOGNIZED_PARAMETERS[" + "PARAMETER[type=0x800a, value=[]], " + "PARAMETER[type=0x800b, value=[0x01]]], " + "NO_USER_DATA[tsn=16909060], " + "COOKIE_RECEIVED_WHILE_SHUTDOWN[], " + "RESTART_WITH_NEW_ADDRESSES[IPV4_ADDRESS[1.2.3.4], " + "IPV4_ADDRESS[2.3.4.5]], " + "USER_INITIATED_ABORT[BYE], " + "PROTOCOL_VIOLATION[@@]]; " + "SHUTDOWN[tsn=16909060]; " + "SHUTDOWN_ACK[]; " + "ERROR[]; " + "COOKIE_ECHO[cookie_len=1]; " + "COOKIE_ACK[]; " + "ECNE[tsn=16909060]; " + "CWR[tsn=16909060]; " + "SHUTDOWN_COMPLETE[flags=T]" + "\n" + "0x0000: 60 00 00 00 01 78 84 ff 00 02 00 00 00 00 00 00 " "\n" + "0x0010: 00 00 00 00 00 00 22 22 00 01 00 00 00 00 00 00 " "\n" + "0x0020: 00 00 00 00 00 00 11 11 04 d2 1f 90 01 02 03 04 " "\n" + "0x0030: 1b 68 75 8e 00 0f 00 13 01 02 03 04 00 ff 01 00 " "\n" + "0x0040: 00 00 00 00 00 01 02 00 01 00 00 50 00 00 00 01 " "\n" + "0x0050: 00 01 00 00 00 0f 00 0f 01 02 03 04 00 05 00 08 " "\n" + "0x0060: 01 02 03 04 00 06 00 14 00 00 00 00 00 00 00 00 " "\n" + "0x0070: 00 00 00 00 00 00 00 01 00 09 00 08 00 01 00 00 " "\n" + "0x0080: 00 0b 00 06 40 41 00 00 00 0c 00 0a 00 05 00 06 " "\n" + "0x0090: 00 0b 00 00 80 00 00 04 02 00 00 24 00 00 00 01 " "\n" + "0x00a0: 00 01 00 00 00 0f 00 0f 01 02 03 04 00 07 00 07 " "\n" + "0x00b0: 01 02 03 00 00 08 00 08 80 01 00 04 03 00 00 20 " "\n" + "0x00c0: 01 02 03 04 00 01 00 00 00 03 00 01 00 01 00 03 " "\n" + "0x00d0: 00 05 00 0f 10 00 10 14 01 02 03 04 04 00 00 06 " "\n" + "0x00e0: 01 02 00 00 05 00 00 06 01 02 00 00 06 01 00 04 " "\n" + "0x00f0: 06 00 00 80 00 01 00 08 00 ff 00 00 00 02 00 0a " "\n" + "0x0100: 00 00 00 01 00 07 00 00 00 03 00 08 00 01 00 00 " "\n" + "0x0110: 00 04 00 04 00 05 00 0c 00 0b 00 06 40 41 00 00 " "\n" + "0x0120: 00 06 00 0c fe 05 00 05 01 00 00 00 00 07 00 04 " "\n" + "0x0130: 00 08 00 10 80 0a 00 04 80 0b 00 05 01 00 00 00 " "\n" + "0x0140: 00 09 00 08 01 02 03 04 00 0a 00 04 00 0b 00 14 " "\n" + "0x0150: 00 05 00 08 01 02 03 04 00 05 00 08 02 03 04 05 " "\n" + "0x0160: 00 0c 00 07 42 59 45 00 00 0d 00 06 40 40 00 00 " "\n" + "0x0170: 07 00 00 08 01 02 03 04 08 00 00 04 09 00 00 04 " "\n" + "0x0180: 0a 00 00 05 45 00 00 00 0b 00 00 04 0c 00 00 08 " "\n" + "0x0190: 01 02 03 04 0d 00 00 08 01 02 03 04 0e 01 00 04 " "\n"; + assert(strcmp(dump, expected) == 0); + free(dump); + packet_free(packet); +} + static void test_tcp_ipv4_packet_to_string(void) { /* An IPv4/GRE/IPv4/TCP packet. */ @@ -133,7 +526,7 @@ static void test_tcp_ipv6_packet_to_string(void) 0xd3, 0xe2, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x80, 0x18, 0x06, 0x60, 0x00, 0x00, 0x02, 0x04, 0x03, 0xe8, - 0x04, 0x02, 0x01, 0x01, 0x01, 0x03, 0x03, 0x07, + 0x04, 0x02, 0x01, 0x01, 0x01, 0x03, 0x03, 0x07 }; struct packet *packet = packet_new(sizeof(data)); @@ -244,6 +637,8 @@ static void test_gre_mpls_tcp_ipv4_packet_to_string(void) "<nop,nop,TS val 117573699 ecr 5>"; assert(strcmp(dump, expected) == 0); free(dump); + + packet_free(packet); } static void test_udplite_ipv4_packet_to_string(void) @@ -269,8 +664,7 @@ static void test_udplite_ipv4_packet_to_string(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IP, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IP, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -348,8 +742,7 @@ static void test_udplite_ipv6_packet_to_string(void) memcpy(packet->buffer, data, sizeof(data)); char *error = NULL; enum packet_parse_result_t result = - parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, - &error); + parse_packet(packet, sizeof(data), ETHERTYPE_IPV6, &error); assert(result == PACKET_OK); assert(error == NULL); @@ -402,6 +795,8 @@ static void test_udplite_ipv6_packet_to_string(void) int main(void) { + test_sctp_ipv4_packet_to_string(); + test_sctp_ipv6_packet_to_string(); test_tcp_ipv4_packet_to_string(); test_tcp_ipv6_packet_to_string(); test_gre_mpls_tcp_ipv4_packet_to_string(); diff --git a/gtests/net/packetdrill/platforms.h b/gtests/net/packetdrill/platforms.h index 11df8118..207d8ee0 100644 --- a/gtests/net/packetdrill/platforms.h +++ b/gtests/net/packetdrill/platforms.h @@ -30,7 +30,7 @@ #ifdef linux -+/* It seems that Linux does not provide netinet/udplite.h */ +/* It seems that Linux does not provide netinet/udplite.h */ #define SOL_UDPLITE IPPROTO_UDPLITE #define UDPLITE_SEND_CSCOV 10 #define UDPLITE_RECV_CSCOV 11 diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c index 9877c4eb..d0e9dcd1 100644 --- a/gtests/net/packetdrill/script.c +++ b/gtests/net/packetdrill/script.c @@ -104,9 +104,7 @@ struct int_symbol cross_platform_symbols[] = { { IPPROTO_IP, "IPPROTO_IP" }, { IPPROTO_IPV6, "IPPROTO_IPV6" }, { IPPROTO_ICMP, "IPPROTO_ICMP" }, -#ifdef IPPROTO_SCTP { IPPROTO_SCTP, "IPPROTO_SCTP" }, -#endif { IPPROTO_TCP, "IPPROTO_TCP" }, { IPPROTO_UDP, "IPPROTO_UDP" }, { IPPROTO_UDPLITE, "IPPROTO_UDPLITE" }, diff --git a/gtests/net/packetdrill/sctp.h b/gtests/net/packetdrill/sctp.h index b831dd9c..ded5509a 100644 --- a/gtests/net/packetdrill/sctp.h +++ b/gtests/net/packetdrill/sctp.h @@ -35,6 +35,330 @@ struct sctp_common_header { __be16 dst_port; __be32 v_tag; __be32 crc32c; -}; +} __packed; + +#define SCTP_DATA_CHUNK_TYPE 0x00 +#define SCTP_INIT_CHUNK_TYPE 0x01 +#define SCTP_INIT_ACK_CHUNK_TYPE 0x02 +#define SCTP_SACK_CHUNK_TYPE 0x03 +#define SCTP_HEARTBEAT_CHUNK_TYPE 0x04 +#define SCTP_HEARTBEAT_ACK_CHUNK_TYPE 0x05 +#define SCTP_ABORT_CHUNK_TYPE 0x06 +#define SCTP_SHUTDOWN_CHUNK_TYPE 0x07 +#define SCTP_SHUTDOWN_ACK_CHUNK_TYPE 0x08 +#define SCTP_ERROR_CHUNK_TYPE 0x09 +#define SCTP_COOKIE_ECHO_CHUNK_TYPE 0x0a +#define SCTP_COOKIE_ACK_CHUNK_TYPE 0x0b +#define SCTP_ECNE_CHUNK_TYPE 0x0c +#define SCTP_CWR_CHUNK_TYPE 0x0d +#define SCTP_SHUTDOWN_COMPLETE_CHUNK_TYPE 0x0e + +struct sctp_chunk { + __u8 type; + __u8 flags; + __be16 length; + __u8 value[]; +} __packed; + +#define SCTP_DATA_CHUNK_I_BIT 0x08 +#define SCTP_DATA_CHUNK_U_BIT 0x04 +#define SCTP_DATA_CHUNK_B_BIT 0x02 +#define SCTP_DATA_CHUNK_E_BIT 0x01 + +struct sctp_data_chunk { + __u8 type; + __u8 flags; + __be16 length; + __be32 tsn; + __be16 sid; + __be16 ssn; + __be32 ppid; + __u8 data[]; +} __packed; + +#define SCTP_INIT_CHUNK_PARAMETER_OFFSET 20 + +struct sctp_init_chunk { + __u8 type; + __u8 flags; + __be16 length; + __be32 initiate_tag; + __be32 a_rwnd; + __be16 os; + __be16 is; + __be32 initial_tsn; + __u8 parameter[]; +} __packed; + +struct sctp_init_ack_chunk { + __u8 type; + __u8 flags; + __be16 length; + __be32 initiate_tag; + __be32 a_rwnd; + __be16 os; + __be16 is; + __be32 initial_tsn; + __u8 parameter[]; +} __packed; + +union sctp_sack_block { + struct { + __be16 start; + __be16 end; + } gap; + u32 tsn; +} __packed; + +struct sctp_sack_chunk { + __u8 type; + __u8 flags; + __be16 length; + __be32 cum_tsn; + __be32 a_rwnd; + __be16 nr_gap_blocks; + __be16 nr_dup_tsns; + union sctp_sack_block block[]; +} __packed; + +struct sctp_heartbeat_chunk { + __u8 type; + __u8 flags; + __be16 length; + __u8 value[]; +} __packed; + +struct sctp_heartbeat_ack_chunk { + __u8 type; + __u8 flags; + __be16 length; + __u8 value[]; +} __packed; + +#define SCTP_ABORT_CHUNK_T_BIT 0x01 +#define SCTP_ABORT_CHUNK_CAUSE_OFFSET 4 + +struct sctp_abort_chunk { + __u8 type; + __u8 flags; + __be16 length; + __u8 cause[]; +} __packed; + +struct sctp_shutdown_chunk { + __u8 type; + __u8 flags; + __be16 length; + __be32 cum_tsn; +} __packed; + +struct sctp_shutdown_ack_chunk { + __u8 type; + __u8 flags; + __be16 length; +} __packed; + +#define SCTP_ERROR_CHUNK_CAUSE_OFFSET 4 + +struct sctp_error_chunk { + __u8 type; + __u8 flags; + __be16 length; + __u8 cause[]; +} __packed; + +struct sctp_cookie_echo_chunk { + __u8 type; + __u8 flags; + __be16 length; + __u8 cookie[]; +} __packed; + +struct sctp_cookie_ack_chunk { + __u8 type; + __u8 flags; + __be16 length; +} __packed; + +struct sctp_ecne_chunk { + __u8 type; + __u8 flags; + __be16 length; + __be32 lowest_tsn; +} __packed; + +struct sctp_cwr_chunk { + __u8 type; + __u8 flags; + __be16 length; + __be32 lowest_tsn; +} __packed; + +#define SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT 0x01 + +struct sctp_shutdown_complete_chunk { + __u8 type; + __u8 flags; + __be16 length; +} __packed; + +#define SCTP_IPV4_ADDRESS_PARAMETER_TYPE 0x0005 +#define SCTP_IPV6_ADDRESS_PARAMETER_TYPE 0x0006 +#define SCTP_STATE_COOKIE_PARAMETER_TYPE 0x0007 +#define SCTP_UNRECOGNIZED_PARAMETER_PARAMETER_TYPE 0x0008 +#define SCTP_COOKIE_PRESERVATIVE_PARAMETER_TYPE 0x0009 +#define SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE 0x000b +#define SCTP_SUPPORTED_ADDRESS_TYPES_PARAMETER_TYPE 0x000c +#define SCTP_ECN_CAPABLE_PARAMETER_TYPE 0x8000 + +struct sctp_parameter { + __be16 type; + __be16 length; + __u8 value[]; +} __packed; + +struct sctp_ipv4_address_parameter { + __be16 type; + __be16 length; + struct in_addr addr; +} __packed; + +struct sctp_ipv6_address_parameter { + __be16 type; + __be16 length; + struct in6_addr addr; +} __packed; + +struct sctp_state_cookie_parameter { + __be16 type; + __be16 length; + __u8 cookie[]; +} __packed; + +struct sctp_unrecognized_parameter_parameter { + __be16 type; + __be16 length; + __u8 value[]; +} __packed; + +struct sctp_cookie_preservative_parameter { + __be16 type; + __be16 length; + __be32 increment; +} __packed; + +struct sctp_hostname_address_parameter { + __be16 type; + __be16 length; + __u8 hostname[]; +} __packed; + +struct sctp_supported_address_types_parameter { + __be16 type; + __be16 length; + __be16 address_type[]; +} __packed; + +struct sctp_ecn_capable_parameter { + __be16 type; + __be16 length; +} __packed; + +#define SCTP_INVALID_STREAM_IDENTIFIER_CAUSE_CODE 0x0001 +#define SCTP_MISSING_MADATORY_PARAMETER_CAUSE_CODE 0x0002 +#define SCTP_STALE_COOKIE_ERROR_CAUSE_CODE 0x0003 +#define SCTP_OUT_OF_RESOURCES_CAUSE_CODE 0x0004 +#define SCTP_UNRESOLVABLE_ADDRESS_CAUSE_CODE 0x0005 +#define SCTP_UNRECOGNIZED_CHUNK_TYPE_CAUSE_CODE 0x0006 +#define SCTP_INVALID_MADATORY_PARAMETER_CAUSE_CODE 0x0007 +#define SCTP_UNRECOGNIZED_PARAMETERS_CAUSE_CODE 0x0008 +#define SCTP_NO_USER_DATA_CAUSE_CODE 0x0009 +#define SCTP_COOKIE_RECEIVED_WHILE_SHUTDOWN_CAUSE_CODE 0x000a +#define SCTP_RESTART_WITH_NEW_ADDRESSES_CAUSE_CODE 0x000b +#define SCTP_USER_INITIATED_ABORT_CAUSE_CODE 0x000c +#define SCTP_PROTOCOL_VIOLATION_CAUSE_CODE 0x000d + +struct sctp_cause { + __be16 code; + __be16 length; + __u8 information[]; +} __packed; + +struct sctp_invalid_stream_identifier_cause { + __be16 code; + __be16 length; + __be16 sid; + __be16 reserved; +} __packed; + +struct sctp_missing_mandatory_parameter_cause { + __be16 code; + __be16 length; + __be32 nr_parameters; + __be16 parameter_type[]; +} __packed; + +struct sctp_stale_cookie_error_cause { + __be16 code; + __be16 length; + __be32 staleness; +} __packed; + +struct sctp_out_of_resources_cause { + __be16 code; + __be16 length; +} __packed; + +struct sctp_unresolvable_address_cause { + __be16 code; + __be16 length; + __u8 parameter[]; +} __packed; + +struct sctp_unrecognized_chunk_type_cause { + __be16 code; + __be16 length; + __u8 chunk[]; +} __packed; + +struct sctp_invalid_mandatory_parameter_cause { + __be16 code; + __be16 length; +} __packed; + +struct sctp_unrecognized_parameters_cause { + __be16 code; + __be16 length; + __u8 parameters[]; +} __packed; + +struct sctp_no_user_data_cause { + __be16 code; + __be16 length; + __be32 tsn; +} __packed; + +struct sctp_cookie_received_while_shutdown_cause { + __be16 code; + __be16 length; +} __packed; + +struct sctp_restart_with_new_addresses_cause { + __be16 code; + __be16 length; + __u8 addresses[]; +} __packed; + +struct sctp_user_initiated_abort_cause { + __be16 code; + __be16 length; + __u8 information[]; +} __packed; + +struct sctp_protocol_violation_cause { + __be16 code; + __be16 length; + __u8 information[]; +} __packed; #endif /* __SCTP_HEADERS_H__ */ diff --git a/gtests/net/packetdrill/sctp_chunk_to_string.c b/gtests/net/packetdrill/sctp_chunk_to_string.c new file mode 100644 index 00000000..48b05824 --- /dev/null +++ b/gtests/net/packetdrill/sctp_chunk_to_string.c @@ -0,0 +1,1266 @@ +/* + * Copyright 2015 Michael Tuexen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +/* + * Author: tuexen@fh-muenster.de (Michael Tuexen) + * + * Implementation for generating human-readable representations of SCTP chunks. + */ + +#include "sctp_chunk_to_string.h" +#include "sctp_iterator.h" + +static int sctp_parameter_to_string(FILE *, struct sctp_parameter *, char **); + +static int sctp_ipv4_address_parameter_to_string( + FILE *s, + struct sctp_ipv4_address_parameter *parameter, + char **error) +{ + u16 length; + char buffer[INET_ADDRSTRLEN]; + + length = ntohs(parameter->length); + if (length != sizeof(struct sctp_ipv4_address_parameter)) { + asprintf(error, "IPV4_ADDRESS parameter illegal (length=%u)", + length); + return STATUS_ERR; + } + inet_ntop(AF_INET, ¶meter->addr, buffer, INET_ADDRSTRLEN); + fprintf(s, "IPV4_ADDRESS[%s]", buffer); + return STATUS_OK; +} + +static int sctp_ipv6_address_parameter_to_string( + FILE *s, + struct sctp_ipv6_address_parameter *parameter, + char **error) +{ + u16 length; + char buffer[INET6_ADDRSTRLEN]; + + length = ntohs(parameter->length); + if (length != sizeof(struct sctp_ipv6_address_parameter)) { + asprintf(error, "IPV6_ADDRESS parameter illegal (length=%u)", + length); + return STATUS_ERR; + } + inet_ntop(AF_INET6, ¶meter->addr, buffer, INET6_ADDRSTRLEN); + fprintf(s, "IPV6_ADDRESS[%s]", buffer); + return STATUS_OK; +} + +static int sctp_state_cookie_parameter_to_string( + FILE *s, + struct sctp_state_cookie_parameter *parameter, + char **error) +{ + u16 length, cookie_length; + + length = ntohs(parameter->length); + if (length < sizeof(struct sctp_state_cookie_parameter)) { + asprintf(error, "STATE_COOKIE parameter illegal (length=%u)", + length); + return STATUS_ERR; + } + cookie_length = length - sizeof(struct sctp_state_cookie_parameter); + fprintf(s, "STATE_COOKIE[cookie_len=%d]", cookie_length); + return STATUS_OK; +} + +static int sctp_unrecognized_parameter_parameter_to_string( + FILE *s, + struct sctp_unrecognized_parameter_parameter *parameter, + char **error) +{ + u16 length; + int result = STATUS_OK; + + length = ntohs(parameter->length); + if (length < (sizeof(struct sctp_unrecognized_parameter_parameter) + + sizeof(struct sctp_parameter))) { + asprintf(error, + "UNRECOGNIZED_PARAMETER parameter illegal (length=%u)", + length); + return STATUS_ERR; + } + fputs("UNRECOGNIZED_PARAMETER[", s); + result = sctp_parameter_to_string(s, + (struct sctp_parameter *)parameter->value, error); + fputc(']', s); + return result; +} + +static int sctp_cookie_preservative_parameter_to_string( + FILE *s, + struct sctp_cookie_preservative_parameter *parameter, + char **error) +{ + u16 length; + + length = ntohs(parameter->length); + if (length != sizeof(struct sctp_cookie_preservative_parameter)) { + asprintf(error, + "COOKIE_PRESERVATIVE parameter illegal (length=%u)", + length); + return STATUS_ERR; + } + fputs("COOKIE_PRESERVATIVE[", s); + fprintf(s, "%u", ntohl(parameter->increment)); + fputc(']', s); + return STATUS_OK; +} + +static int sctp_hostname_parameter_to_string( + FILE *s, + struct sctp_hostname_address_parameter *parameter, + char **error) +{ + u16 length; + + length = ntohs(parameter->length); + if (length < sizeof(struct sctp_hostname_address_parameter)) { + asprintf(error, "HOSTNAME parameter illegal (length=%u)", + length); + return STATUS_ERR; + } + fprintf(s, "HOSTNAME[%.*s]", + (int)(length - sizeof(struct sctp_hostname_address_parameter)), + (char *)parameter->hostname); + return STATUS_OK; +} + +static int sctp_supported_address_types_parameter_to_string( + FILE *s, + struct sctp_supported_address_types_parameter *parameter, + char **error) +{ + u16 i, length, nr_address_types; + + length = ntohs(parameter->length); + if ((length < sizeof(struct sctp_supported_address_types_parameter)) || + ((length & 0x0001) != 0)) { + asprintf(error, + "SUPPORTED_ADDRESS_TYPES parameter illegal (length=%u)", + length); + return STATUS_ERR; + } + nr_address_types = + (length - sizeof(struct sctp_supported_address_types_parameter)) + / sizeof(u16); + fputs("SUPPORTED_ADDRESS_TYPES[", s); + for (i = 0; i < nr_address_types; i++) { + if (i > 0) + fputs(", ", s); + switch (ntohs(parameter->address_type[i])) { + case SCTP_IPV4_ADDRESS_PARAMETER_TYPE: + fputs("IPV4", s); + break; + case SCTP_IPV6_ADDRESS_PARAMETER_TYPE: + fputs("IPV6", s); + break; + case SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE: + fputs("HOSTNAME", s); + break; + default: + fprintf(s, "0x%04x", ntohs(parameter->address_type[i])); + break; + } + } + fputs("]", s); + return STATUS_OK; +} + +static int sctp_ecn_capable_parameter_to_string( + FILE *s, + struct sctp_ecn_capable_parameter *parameter, + char **error) +{ + u16 length; + + length = ntohs(parameter->length); + if (length != sizeof(struct sctp_ecn_capable_parameter)) { + asprintf(error, "ECN_CAPABLE parameter illegal (length=%u)", + length); + return STATUS_ERR; + } + fputs("ECN_CAPABLE[]", s); + return STATUS_OK; +} + +static int sctp_unknown_parameter_to_string( + FILE *s, + struct sctp_parameter *parameter, + char **error) +{ + u16 i, length; + + length = ntohs(parameter->length); + if (length < sizeof(struct sctp_parameter)) { + asprintf(error, "PARAMETER too short (type=0x%04x, length=%u)", + ntohs(parameter->type), length); + return STATUS_ERR; + } + fputs("PARAMETER[", s); + fprintf(s, "type=0x%04x, ", ntohs(parameter->type)); + fputs("value=[", s); + for (i = 0; i < length - sizeof(struct sctp_parameter); i++) { + fprintf(s, "%s0x%02x", + i > 0 ? ", " : "", + parameter->value[i]); + } + fputs("]]", s); + return STATUS_OK; +} + +static int sctp_parameter_to_string(FILE *s, + struct sctp_parameter *parameter, + char **error) +{ + int result; + + switch (ntohs(parameter->type)) { + case SCTP_IPV4_ADDRESS_PARAMETER_TYPE: + result = sctp_ipv4_address_parameter_to_string(s, + (struct sctp_ipv4_address_parameter *)parameter, error); + break; + case SCTP_IPV6_ADDRESS_PARAMETER_TYPE: + result = sctp_ipv6_address_parameter_to_string(s, + (struct sctp_ipv6_address_parameter *)parameter, error); + break; + case SCTP_STATE_COOKIE_PARAMETER_TYPE: + result = sctp_state_cookie_parameter_to_string(s, + (struct sctp_state_cookie_parameter *)parameter, error); + break; + case SCTP_UNRECOGNIZED_PARAMETER_PARAMETER_TYPE: + result = sctp_unrecognized_parameter_parameter_to_string(s, + (struct sctp_unrecognized_parameter_parameter *)parameter, + error); + break; + case SCTP_COOKIE_PRESERVATIVE_PARAMETER_TYPE: + result = sctp_cookie_preservative_parameter_to_string(s, + (struct sctp_cookie_preservative_parameter *)parameter, + error); + break; + case SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE: + result = sctp_hostname_parameter_to_string(s, + (struct sctp_hostname_address_parameter *)parameter, + error); + break; + case SCTP_SUPPORTED_ADDRESS_TYPES_PARAMETER_TYPE: + result = sctp_supported_address_types_parameter_to_string(s, + (struct sctp_supported_address_types_parameter *)parameter, + error); + break; + case SCTP_ECN_CAPABLE_PARAMETER_TYPE: + result = sctp_ecn_capable_parameter_to_string(s, + (struct sctp_ecn_capable_parameter *)parameter, error); + break; + default: + result = sctp_unknown_parameter_to_string(s, parameter, error); + break; + } + return result; +} + +static int sctp_invalid_stream_identifier_cause_to_string( + FILE *s, + struct sctp_invalid_stream_identifier_cause *cause, + char **error) +{ + u16 length; + + length = ntohs(cause->length); + if (length != sizeof(struct sctp_invalid_stream_identifier_cause)) { + asprintf(error, + "INVALID_STREAM_IDENTIFIER cause invalid (length=%u)", + length); + return STATUS_ERR; + } + fprintf(s, "INVALID_STREAM_IDENTIFIER[sid=%u]", ntohs(cause->sid)); + return STATUS_OK; +} + +static int sctp_missing_mandatory_parameter_cause_to_string( + FILE *s, + struct sctp_missing_mandatory_parameter_cause *cause, + char **error) +{ + u16 length; + u32 i, nr_parameters; + + length = ntohs(cause->length); + if (length < sizeof(struct sctp_missing_mandatory_parameter_cause)) { + asprintf(error, + "MISSING_MANDATORY_PARAMETER cause too short (length=%u)", + length); + return STATUS_ERR; + } + nr_parameters = ntohl(cause->nr_parameters); + if (length != sizeof(struct sctp_missing_mandatory_parameter_cause) + + nr_parameters * sizeof(u16)) { + asprintf(error, "MISSING_MANDATORY_PARAMETER inconsistent"); + return STATUS_ERR; + } + fputs("MISSING_MANDATORY_PARAMETER[", s); + for (i = 0; i < nr_parameters; i++) { + if (i > 0) + fputs(", ", s); + switch (ntohs(cause->parameter_type[i])) { + case SCTP_IPV4_ADDRESS_PARAMETER_TYPE: + fputs("IPV4_ADDRESS", s); + break; + case SCTP_IPV6_ADDRESS_PARAMETER_TYPE: + fputs("IPV6_ADDRESS", s); + break; + case SCTP_STATE_COOKIE_PARAMETER_TYPE: + fputs("STATE_COOKIE", s); + break; + case SCTP_UNRECOGNIZED_PARAMETER_PARAMETER_TYPE: + fputs("UNRECOGNIZED_PARAMETER", s); + break; + case SCTP_COOKIE_PRESERVATIVE_PARAMETER_TYPE: + fputs("COOKIE_PRESERVATIVE", s); + break; + case SCTP_HOSTNAME_ADDRESS_PARAMETER_TYPE: + fputs("HOSTNAME_ADDRESS", s); + break; + case SCTP_SUPPORTED_ADDRESS_TYPES_PARAMETER_TYPE: + fputs("SUPPORTED_ADDRESS_TYPES", s); + break; + case SCTP_ECN_CAPABLE_PARAMETER_TYPE: + fputs("ECN_CAPABLE", s); + break; + default: + fprintf(s, "0x%04x", ntohs(cause->parameter_type[i])); + break; + } + } + fputc(']', s); + return STATUS_OK; +} + +static int sctp_stale_cookie_error_cause_to_string( + FILE *s, + struct sctp_stale_cookie_error_cause *cause, + char **error) +{ + u16 length; + + length = ntohs(cause->length); + if (length != sizeof(struct sctp_stale_cookie_error_cause)) { + asprintf(error, "STALE_COOKIE_ERROR cause invalid (length=%u)", + length); + return STATUS_ERR; + } + fprintf(s, "STALE_COOKIE_ERROR[staleness=%u]", ntohl(cause->staleness)); + return STATUS_OK; +} + +static int sctp_out_of_resources_cause_to_string( + FILE *s, + struct sctp_out_of_resources_cause *cause, + char **error) +{ + u16 length; + + length = ntohs(cause->length); + if (length != sizeof(struct sctp_out_of_resources_cause)) { + asprintf(error, "OUT_OF_RESOURCES cause invalid (length=%u)", + length); + return STATUS_ERR; + } + fputs("OUT_OF_RESOURCES[]", s); + return STATUS_OK; +} + +static int sctp_unresolvable_address_cause_to_string( + FILE *s, + struct sctp_unresolvable_address_cause *cause, + char **error) +{ + u16 cause_length, parameter_length, cause_padding, parameter_padding; + struct sctp_parameter *parameter; + int result; + + cause_length = ntohs(cause->length); + if (cause_length < sizeof(struct sctp_unresolvable_address_cause) + + sizeof(struct sctp_parameter)) { + asprintf(error, "UNRESOLVABLE_ADDRESS cause too short"); + return STATUS_ERR; + } + cause_padding = cause_length & 0x0003; + if (cause_padding != 0) + cause_padding = 4 - cause_padding; + parameter = (struct sctp_parameter *)cause->parameter; + parameter_length = ntohs(parameter->length); + parameter_padding = parameter_length & 0x0003; + if (parameter_padding != 0) + parameter_padding = 4 - parameter_padding; + if (cause_length + cause_padding != + sizeof(struct sctp_unresolvable_address_cause) + + parameter_length + parameter_padding) { + asprintf(error, "UNRESOLVABLE_ADDRESS cause inconsistent"); + return STATUS_ERR; + } + fputs("UNRESOLVABLE_ADDRESS[", s); + result = sctp_parameter_to_string(s, parameter, error); + fputc(']', s); + return result; +} + +static int sctp_unrecognized_chunk_type_cause_to_string( + FILE *s, + struct sctp_unrecognized_chunk_type_cause *cause, + char **error) +{ + u16 cause_length, chunk_length, cause_padding, chunk_padding; + struct sctp_chunk *chunk; + int result; + + cause_length = ntohs(cause->length); + if (cause_length < sizeof(struct sctp_unrecognized_chunk_type_cause) + + sizeof(struct sctp_chunk)) { + asprintf(error, "UNRECOGNIZED_CHUNK cause too short"); + return STATUS_ERR; + } + cause_padding = cause_length & 0x0003; + if (cause_padding != 0) + cause_padding = 4 - cause_padding; + chunk = (struct sctp_chunk *)cause->chunk; + chunk_length = ntohs(chunk->length); + chunk_padding = chunk_length & 0x0003; + if (chunk_padding != 0) + chunk_padding = 4 - chunk_padding; + /* XXX: Do we need to deal with padding here? */ + if (cause_length + cause_padding != + sizeof(struct sctp_unrecognized_chunk_type_cause) + + chunk_length + chunk_padding) { + asprintf(error, "UNRECOGNIZED_CHUNK cause inconsistent"); + return STATUS_ERR; + } + fputs("UNRECOGNIZED_CHUNK[", s); + result = sctp_chunk_to_string(s, chunk, error); + fputc(']', s); + return result; +} + +static int sctp_invalid_mandatory_parameter_cause_to_string( + FILE *s, + struct sctp_invalid_mandatory_parameter_cause *cause, + char **error) +{ + u16 length; + + length = ntohs(cause->length); + if (length != sizeof(struct sctp_invalid_mandatory_parameter_cause)) { + asprintf(error, + "INVALID_MANDATORY_PARAMETER cause invalid (length=%u)", + length); + return STATUS_ERR; + } + fputs("INVALID_MANDATORY_PARAMETER[]", s); + return STATUS_OK; +} + +static int sctp_unrecognized_parameters_cause_to_string( + FILE *s, + struct sctp_unrecognized_parameters_cause *cause, + char **error) +{ + u16 length, parameters_length, index; + struct sctp_parameters_iterator iter; + struct sctp_parameter *parameter; + int result = STATUS_OK; + + length = ntohs(cause->length); + if (length < sizeof(struct sctp_unrecognized_parameters_cause)) { + asprintf(error, + "UNRECOGNIZED_PARAMETERS cause too short (length=%u)", + length); + return STATUS_ERR; + } + parameters_length = length - + sizeof(struct sctp_unrecognized_parameters_cause); + fputs("UNRECOGNIZED_PARAMETERS[", s); + index = 0; + for (parameter = sctp_parameters_begin(cause->parameters, + parameters_length, + &iter, error); + parameter != NULL; + parameter = sctp_parameters_next(&iter, error)) { + if (index > 0) + fputs(", ", s); + if (*error != NULL) + break; + result = sctp_parameter_to_string(s, parameter, error); + if (result != STATUS_OK) + break; + index++; + } + fputc(']', s); + return STATUS_OK; +} + +static int sctp_no_user_data_cause_to_string( + FILE *s, + struct sctp_no_user_data_cause *cause, + char **error) +{ + u16 length; + + length = ntohs(cause->length); + if (length != sizeof(struct sctp_no_user_data_cause)) { + asprintf(error, "NO_USER_DATA cause invalid (length=%u)", + length); + return STATUS_ERR; + } + fprintf(s, "NO_USER_DATA[tsn=%u]", ntohl(cause->tsn)); + return STATUS_OK; +} + +static int sctp_cookie_received_while_shutdown_cause_to_string( + FILE *s, + struct sctp_cookie_received_while_shutdown_cause *cause, + char **error) +{ + u16 length; + + length = ntohs(cause->length); + if (length != + sizeof(struct sctp_cookie_received_while_shutdown_cause)) { + asprintf(error, + "COOKIE_RECEIVED_WHILE_SHUTDOWN cause invalid (length=%u)", + length); + return STATUS_ERR; + } + fputs("COOKIE_RECEIVED_WHILE_SHUTDOWN[]", s); + return STATUS_OK; +} + +static int sctp_restart_with_new_addresses_cause_to_string( + FILE *s, + struct sctp_restart_with_new_addresses_cause *cause, + char **error) +{ + u16 length, addressess_length, index; + struct sctp_parameters_iterator iter; + struct sctp_parameter *parameter; + int result = STATUS_OK; + + length = ntohs(cause->length); + if (length < sizeof(struct sctp_restart_with_new_addresses_cause)) { + asprintf(error, + "RESTART_WITH_NEW_ADDRESSES cause too short (length=%u)", + length); + return STATUS_ERR; + } + addressess_length = + length - sizeof(struct sctp_restart_with_new_addresses_cause); + fputs("RESTART_WITH_NEW_ADDRESSES[", s); + index = 0; + for (parameter = sctp_parameters_begin(cause->addresses, + addressess_length, + &iter, error); + parameter != NULL; + parameter = sctp_parameters_next(&iter, error)) { + if (index > 0) + fputs(", ", s); + if (*error != NULL) + break; + result = sctp_parameter_to_string(s, parameter, error); + if (result != STATUS_OK) + break; + index++; + } + fputc(']', s); + return STATUS_OK; +} + +static int sctp_user_initiated_abort_cause_to_string( + FILE *s, + struct sctp_user_initiated_abort_cause *cause, + char **error) +{ + u16 length; + + length = ntohs(cause->length); + if (length < sizeof(struct sctp_user_initiated_abort_cause)) { + asprintf(error, + "USER_INITIATED_ABORT cause illegal (length=%u)", + length); + return STATUS_ERR; + } + fprintf(s, "USER_INITIATED_ABORT[%.*s]", + (int)(length - sizeof(struct sctp_user_initiated_abort_cause)), + (char *)cause->information); + return STATUS_OK; +} + +static int sctp_protocol_violation_cause_to_string( + FILE *s, + struct sctp_protocol_violation_cause *cause, + char **error) +{ + u16 length; + + length = ntohs(cause->length); + if (length < sizeof(struct sctp_protocol_violation_cause)) { + asprintf(error, "PROTOCOL_VIOLOATION cause illegal (length=%u)", + length); + return STATUS_ERR; + } + fprintf(s, "PROTOCOL_VIOLATION[%.*s]", + (int)(length - sizeof(struct sctp_protocol_violation_cause)), + (char *)cause->information); + return STATUS_OK; +} + +static int sctp_unknown_cause_to_string(FILE *s, + struct sctp_cause *cause, + char **error) +{ + u16 i, length; + + length = ntohs(cause->length); + if (length < sizeof(struct sctp_parameter)) { + asprintf(error, "CAUSE too short (code=0x%04x, length=%u)", + ntohs(cause->code), length); + return STATUS_ERR; + } + fputs("CAUSE[", s); + fprintf(s, "code=0x%04x, ", ntohs(cause->code)); + fputs("value=[", s); + for (i = 0; i < length - sizeof(struct sctp_cause); i++) { + fprintf(s, "%s0x%02x", + i > 0 ? ", " : "", + cause->information[i]); + } + fputs("]]", s); + return STATUS_OK; +} + +static int sctp_cause_to_string(FILE *s, struct sctp_cause *cause, char **error) +{ + int result; + + switch (ntohs(cause->code)) { + case SCTP_INVALID_STREAM_IDENTIFIER_CAUSE_CODE: + result = sctp_invalid_stream_identifier_cause_to_string(s, + (struct sctp_invalid_stream_identifier_cause *)cause, + error); + break; + case SCTP_MISSING_MADATORY_PARAMETER_CAUSE_CODE: + result = sctp_missing_mandatory_parameter_cause_to_string(s, + (struct sctp_missing_mandatory_parameter_cause *)cause, + error); + break; + case SCTP_STALE_COOKIE_ERROR_CAUSE_CODE: + result = sctp_stale_cookie_error_cause_to_string(s, + (struct sctp_stale_cookie_error_cause *)cause, error); + break; + case SCTP_OUT_OF_RESOURCES_CAUSE_CODE: + result = sctp_out_of_resources_cause_to_string(s, + (struct sctp_out_of_resources_cause *)cause, error); + break; + case SCTP_UNRESOLVABLE_ADDRESS_CAUSE_CODE: + result = sctp_unresolvable_address_cause_to_string(s, + (struct sctp_unresolvable_address_cause *)cause, error); + break; + case SCTP_UNRECOGNIZED_CHUNK_TYPE_CAUSE_CODE: + result = sctp_unrecognized_chunk_type_cause_to_string(s, + (struct sctp_unrecognized_chunk_type_cause *)cause, + error); + break; + case SCTP_INVALID_MADATORY_PARAMETER_CAUSE_CODE: + result = sctp_invalid_mandatory_parameter_cause_to_string(s, + (struct sctp_invalid_mandatory_parameter_cause *)cause, + error); + break; + case SCTP_UNRECOGNIZED_PARAMETERS_CAUSE_CODE: + result = sctp_unrecognized_parameters_cause_to_string(s, + (struct sctp_unrecognized_parameters_cause *)cause, + error); + break; + case SCTP_NO_USER_DATA_CAUSE_CODE: + result = sctp_no_user_data_cause_to_string(s, + (struct sctp_no_user_data_cause *)cause, error); + break; + case SCTP_COOKIE_RECEIVED_WHILE_SHUTDOWN_CAUSE_CODE: + result = sctp_cookie_received_while_shutdown_cause_to_string(s, + (struct sctp_cookie_received_while_shutdown_cause *)cause, + error); + break; + case SCTP_RESTART_WITH_NEW_ADDRESSES_CAUSE_CODE: + result = sctp_restart_with_new_addresses_cause_to_string(s, + (struct sctp_restart_with_new_addresses_cause *)cause, + error); + break; + case SCTP_USER_INITIATED_ABORT_CAUSE_CODE: + result = sctp_user_initiated_abort_cause_to_string(s, + (struct sctp_user_initiated_abort_cause *)cause, error); + break; + case SCTP_PROTOCOL_VIOLATION_CAUSE_CODE: + result = sctp_protocol_violation_cause_to_string(s, + (struct sctp_protocol_violation_cause *)cause, error); + break; + default: + result = sctp_unknown_cause_to_string(s, cause, error); + break; + } + return result; +} + +static int sctp_data_chunk_to_string(FILE *s, + struct sctp_data_chunk *chunk, + char **error) +{ + u16 length; + u8 flags; + + flags = chunk->flags; + length = ntohs(chunk->length); + if (length < sizeof(struct sctp_data_chunk)) { + asprintf(error, "DATA chunk too short (length=%u)", length); + return STATUS_ERR; + } + fputs("DATA[", s); + if (flags != 0x00) { + fputs("flags=", s); + if (flags & ~(SCTP_DATA_CHUNK_I_BIT | + SCTP_DATA_CHUNK_U_BIT | + SCTP_DATA_CHUNK_B_BIT | + SCTP_DATA_CHUNK_E_BIT)) + fprintf(s, "0x%02x", chunk->flags); + else { + if (flags & SCTP_DATA_CHUNK_I_BIT) + fputc('I', s); + if (flags & SCTP_DATA_CHUNK_U_BIT) + fputc('U', s); + if (flags & SCTP_DATA_CHUNK_B_BIT) + fputc('B', s); + if (flags & SCTP_DATA_CHUNK_E_BIT) + fputc('E', s); + } + fputs(", ", s); + } + fprintf(s, "tsn=%u, ", ntohl(chunk->tsn)); + fprintf(s, "sid=%d, ", ntohs(chunk->sid)); + fprintf(s, "ssn=%u, ", ntohs(chunk->ssn)); + fprintf(s, "ppid=%u, ", ntohl(chunk->ppid)); + fprintf(s, "payload_len=%u]", + length - (u16)sizeof(struct sctp_data_chunk)); + return STATUS_OK; +} + +static int sctp_init_chunk_to_string(FILE *s, + struct sctp_init_chunk *chunk, + char **error) +{ + struct sctp_parameters_iterator iter; + struct sctp_parameter *parameter; + u16 length, parameters_length; + u8 flags; + int result = STATUS_OK; + + assert(*error == NULL); + flags = chunk->flags; + length = ntohs(chunk->length); + if (length < sizeof(struct sctp_init_chunk)) { + asprintf(error, "INIT chunk too short (length=%u)", length); + return STATUS_ERR; + } + parameters_length = length - sizeof(struct sctp_init_chunk); + fputs("INIT[", s); + if (flags != 0x00) + fprintf(s, "flags=0x%02x, ", chunk->flags); + fprintf(s, "tag=%u, ", ntohl(chunk->initiate_tag)); + fprintf(s, "a_rwnd=%d, ", ntohl(chunk->a_rwnd)); + fprintf(s, "os=%u, ", ntohs(chunk->os)); + fprintf(s, "is=%u, ", ntohs(chunk->is)); + fprintf(s, "tsn=%u", ntohl(chunk->initial_tsn)); + for (parameter = sctp_parameters_begin(chunk->parameter, + parameters_length, + &iter, error); + parameter != NULL; + parameter = sctp_parameters_next(&iter, error)) { + fputs(", ", s); + if (*error != NULL) + break; + result = sctp_parameter_to_string(s, parameter, error); + if (result != STATUS_OK) + break; + } + fputc(']', s); + if (*error != NULL) + result = STATUS_ERR; + return result; +} + +static int sctp_init_ack_chunk_to_string(FILE *s, + struct sctp_init_ack_chunk *chunk, + char **error) +{ + struct sctp_parameters_iterator iter; + struct sctp_parameter *parameter; + u16 length, parameters_length; + u8 flags; + int result = STATUS_OK; + + assert(*error == NULL); + flags = chunk->flags; + length = ntohs(chunk->length); + if (length < sizeof(struct sctp_init_ack_chunk)) { + asprintf(error, "INIT_ACK chunk too short (length=%u)", length); + return STATUS_ERR; + } + parameters_length = length - sizeof(struct sctp_init_ack_chunk); + fputs("INIT_ACK[", s); + if (flags != 0x00) + fprintf(s, "flags=0x%02x, ", chunk->flags); + fprintf(s, "tag=%u, ", ntohl(chunk->initiate_tag)); + fprintf(s, "a_rwnd=%d, ", ntohl(chunk->a_rwnd)); + fprintf(s, "os=%u, ", ntohs(chunk->os)); + fprintf(s, "is=%u, ", ntohs(chunk->is)); + fprintf(s, "tsn=%u", ntohl(chunk->initial_tsn)); + for (parameter = sctp_parameters_begin(chunk->parameter, + parameters_length, + &iter, error); + parameter != NULL; + parameter = sctp_parameters_next(&iter, error)) { + fputs(", ", s); + if (*error != NULL) + break; + result = sctp_parameter_to_string(s, parameter, error); + if (result != STATUS_OK) + break; + } + fputc(']', s); + if (*error != NULL) + result = STATUS_ERR; + return result; +} + +static int sctp_sack_chunk_to_string(FILE *s, + struct sctp_sack_chunk *chunk, + char **error) +{ + u16 length; + u16 nr_gaps, nr_dups; + u16 i; + u8 flags; + + flags = chunk->flags; + length = ntohs(chunk->length); + if (length < sizeof(struct sctp_sack_chunk)) { + asprintf(error, "SACK chunk too short (length=%u)", length); + return STATUS_ERR; + } + nr_gaps = ntohs(chunk->nr_gap_blocks); + nr_dups = ntohs(chunk->nr_dup_tsns); + if (length != sizeof(struct sctp_sack_chunk) + + (nr_gaps + nr_dups) * sizeof(u32)) { + asprintf(error, "SACK chunk length inconsistent"); + return STATUS_ERR; + } + fputs("SACK[", s); + if (flags != 0) + fprintf(s, "flags=0x%02x, ", flags); + fprintf(s, "cum_tsn=%u, ", ntohl(chunk->cum_tsn)); + fprintf(s, "a_rwnd=%u, ", ntohl(chunk->a_rwnd)); + fputs("gaps=[", s); + for (i = 0; i < nr_gaps; i++) + fprintf(s, "%s%u-%u", + i > 0 ? ", " : "", + ntohs(chunk->block[i].gap.start), + ntohs(chunk->block[i].gap.end)); + fputs("], dups=[", s); + for (i = 0; i < nr_dups; i++) + fprintf(s, "%s%u", + i > 0 ? ", " : "", + ntohl(chunk->block[i + nr_gaps].tsn)); + fputs("]]", s); + return STATUS_OK; +} + +static int sctp_heartbeat_chunk_to_string(FILE *s, + struct sctp_heartbeat_chunk *chunk, + char **error) +{ + u16 length; + u8 flags; + + flags = chunk->flags; + length = ntohs(chunk->length); + fputs("HEARTBEAT[", s); + if (flags != 0x00) + fprintf(s, "flags=0x%02x, ", flags); + fprintf(s, "info_len=%u", + length - (u16)sizeof(struct sctp_heartbeat_chunk)); + fputc(']', s); + return STATUS_OK; +} + +static int sctp_heartbeat_ack_chunk_to_string( + FILE *s, + struct sctp_heartbeat_ack_chunk *chunk, + char **error) +{ + u16 length; + u8 flags; + + flags = chunk->flags; + length = ntohs(chunk->length); + fputs("HEARTBEAT_ACK[", s); + if (flags != 0x00) + fprintf(s, "flags=0x%02x, ", flags); + fprintf(s, "info_len=%u", + length - (u16)sizeof(struct sctp_heartbeat_chunk)); + fputc(']', s); + return STATUS_OK; +} + +static int sctp_abort_chunk_to_string(FILE *s, + struct sctp_abort_chunk *chunk, + char **error) +{ + struct sctp_causes_iterator iter; + struct sctp_cause *cause; + u16 length, index; + u8 flags; + int result = STATUS_OK; + + flags = chunk->flags; + length = ntohs(chunk->length); + if (length < sizeof(struct sctp_abort_chunk)) { + asprintf(error, "ABORT chunk too short (length=%u)", length); + return STATUS_ERR; + } + fputs("ABORT[", s); + if (flags != 0x00) { + fputs("flags=", s); + if (flags & ~SCTP_ABORT_CHUNK_T_BIT) + fprintf(s, "0x%02x", chunk->flags); + else + if (flags & SCTP_ABORT_CHUNK_T_BIT) + fputc('T', s); + } + index = 0; + for (cause = sctp_causes_begin((struct sctp_chunk *)chunk, + SCTP_ABORT_CHUNK_CAUSE_OFFSET, + &iter, error); + cause != NULL; + cause = sctp_causes_next(&iter, error)) { + if (index > 0) + fputs(", ", s); + if (*error != NULL) + break; + result = sctp_cause_to_string(s, cause, error); + if (result != STATUS_OK) + break; + index++; + } + fputc(']', s); + if (*error != NULL) + result = STATUS_ERR; + return result; +} + +static int sctp_shutdown_chunk_to_string(FILE *s, + struct sctp_shutdown_chunk *chunk, + char **error) +{ + u8 flags; + u16 length; + + flags = chunk->flags; + length = ntohs(chunk->length); + if (length != sizeof(struct sctp_shutdown_chunk)) { + asprintf(error, "SHUTDOWN chunk illegal (length=%u)", length); + return STATUS_ERR; + } + fputs("SHUTDOWN[", s); + if (flags != 0x00) + fprintf(s, "flags=0x%02x, ", chunk->flags); + fprintf(s, "tsn=%u", ntohl(chunk->cum_tsn)); + fputc(']', s); + return STATUS_OK; +} + +static int sctp_shutdown_ack_chunk_to_string( + FILE *s, + struct sctp_shutdown_ack_chunk *chunk, + char **error) +{ + u8 flags; + u16 length; + + flags = chunk->flags; + length = ntohs(chunk->length); + if (length != sizeof(struct sctp_shutdown_ack_chunk)) { + asprintf(error, "SHUTDOWN_ACK chunk too long (length=%u)", + length); + return STATUS_ERR; + } + fputs("SHUTDOWN_ACK[", s); + if (flags != 0x00) + fprintf(s, "flags=0x%02x, ", chunk->flags); + fputc(']', s); + return STATUS_OK; +} + +static int sctp_error_chunk_to_string(FILE *s, + struct sctp_error_chunk *chunk, + char **error) +{ + struct sctp_causes_iterator iter; + struct sctp_cause *cause; + u16 length, index; + u8 flags; + int result = STATUS_OK; + + flags = chunk->flags; + length = ntohs(chunk->length); + if (length < sizeof(struct sctp_abort_chunk)) { + asprintf(error, "ERROR chunk too short (length=%u)", length); + return STATUS_ERR; + } + fputs("ERROR[", s); + if (flags != 0x00) + fprintf(s, "flags=0x%02x", chunk->flags); + index = 0; + for (cause = sctp_causes_begin((struct sctp_chunk *)chunk, + SCTP_ERROR_CHUNK_CAUSE_OFFSET, + &iter, error); + cause != NULL; + cause = sctp_causes_next(&iter, error)) { + if (index > 0) + fputs(", ", s); + if (*error != NULL) + break; + result = sctp_cause_to_string(s, cause, error); + if (result != STATUS_OK) + break; + index++; + } + fputc(']', s); + if (*error != NULL) + result = STATUS_ERR; + return STATUS_OK; +} + +static int sctp_cookie_echo_chunk_to_string( + FILE *s, + struct sctp_cookie_echo_chunk *chunk, + char **error) +{ + u16 length; + u8 flags; + + flags = chunk->flags; + length = ntohs(chunk->length); + fputs("COOKIE_ECHO[", s); + if (flags != 0x00) + fprintf(s, "flags=0x%02x, ", flags); + fprintf(s, "cookie_len=%u", + length - (u16)sizeof(struct sctp_cookie_echo_chunk)); + fputc(']', s); + return STATUS_OK; +} + +static int sctp_cookie_ack_chunk_to_string(FILE *s, + struct sctp_cookie_ack_chunk *chunk, + char **error) +{ + u8 flags; + u16 length; + + flags = chunk->flags; + length = ntohs(chunk->length); + if (length != sizeof(struct sctp_cookie_ack_chunk)) { + asprintf(error, "COOKIE_ACK chunk too long (length=%u)", + length); + return STATUS_ERR; + } + fputs("COOKIE_ACK[", s); + if (flags != 0x00) + fprintf(s, "flags=0x%02x", chunk->flags); + fputc(']', s); + return STATUS_OK; +} + +static int sctp_ecne_chunk_to_string(FILE *s, + struct sctp_ecne_chunk *chunk, + char **error) +{ + u8 flags; + u16 length; + + flags = chunk->flags; + length = ntohs(chunk->length); + if (length != sizeof(struct sctp_ecne_chunk)) { + asprintf(error, "ECNE chunk illegal (length=%u)", length); + return STATUS_ERR; + } + fputs("ECNE[", s); + if (flags != 0x00) + fprintf(s, "flags=0x%02x, ", chunk->flags); + fprintf(s, "tsn=%u", ntohl(chunk->lowest_tsn)); + fputc(']', s); + return STATUS_OK; +} + +static int sctp_cwr_chunk_to_string(FILE *s, + struct sctp_cwr_chunk *chunk, + char **error) +{ + u8 flags; + u16 length; + + flags = chunk->flags; + length = ntohs(chunk->length); + if (length != sizeof(struct sctp_cwr_chunk)) { + asprintf(error, "CWR chunk illegal (length=%u)", length); + return STATUS_ERR; + } + fputs("CWR[", s); + if (flags != 0x00) + fprintf(s, "flags=0x%02x, ", chunk->flags); + fprintf(s, "tsn=%u", ntohl(chunk->lowest_tsn)); + fputc(']', s); + return STATUS_OK; +} + +static int sctp_shutdown_complete_chunk_to_string( + FILE *s, + struct sctp_shutdown_complete_chunk *chunk, + char **error) +{ + u8 flags; + u16 length; + + flags = chunk->flags; + length = ntohs(chunk->length); + if (length != sizeof(struct sctp_shutdown_complete_chunk)) { + asprintf(error, "SHUTDOWN_COMPLETE chunk too long (length=%u)", + length); + return STATUS_ERR; + } + fputs("SHUTDOWN_COMPLETE[", s); + if (flags != 0x00) { + fputs("flags=", s); + if (flags & ~SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT) + fprintf(s, "0x%02x", chunk->flags); + else + if (flags & SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT) + fputc('T', s); + } + fputc(']', s); + return STATUS_OK; +} + +static int sctp_unknown_chunk_to_string(FILE *s, + struct sctp_chunk *chunk, + char **error) +{ + u16 i, length; + + length = ntohs(chunk->length); + fputs("CHUNK[", s); + fprintf(s, "type=0x%02x, ", chunk->type); + fprintf(s, "flags=0x%02x, ", chunk->flags); + fputs("value=[", s); + for (i = 0; i < length - sizeof(struct sctp_chunk); i++) + fprintf(s, "%s0x%02x", + i > 0 ? ", " : "", + chunk->value[i]); + fputs("]]", s); + return STATUS_OK; +} + +int sctp_chunk_to_string(FILE *s, struct sctp_chunk *chunk, char **error) +{ + int result; + + switch (chunk->type) { + case SCTP_DATA_CHUNK_TYPE: + result = sctp_data_chunk_to_string(s, + (struct sctp_data_chunk *)chunk, error); + break; + case SCTP_INIT_CHUNK_TYPE: + result = sctp_init_chunk_to_string(s, + (struct sctp_init_chunk *)chunk, error); + break; + case SCTP_INIT_ACK_CHUNK_TYPE: + result = sctp_init_ack_chunk_to_string(s, + (struct sctp_init_ack_chunk *)chunk, error); + break; + case SCTP_SACK_CHUNK_TYPE: + result = sctp_sack_chunk_to_string(s, + (struct sctp_sack_chunk *)chunk, error); + break; + case SCTP_HEARTBEAT_CHUNK_TYPE: + result = sctp_heartbeat_chunk_to_string(s, + (struct sctp_heartbeat_chunk *)chunk, error); + break; + case SCTP_HEARTBEAT_ACK_CHUNK_TYPE: + result = sctp_heartbeat_ack_chunk_to_string(s, + (struct sctp_heartbeat_ack_chunk *)chunk, error); + break; + case SCTP_ABORT_CHUNK_TYPE: + result = sctp_abort_chunk_to_string(s, + (struct sctp_abort_chunk *)chunk, error); + break; + case SCTP_SHUTDOWN_CHUNK_TYPE: + result = sctp_shutdown_chunk_to_string(s, + (struct sctp_shutdown_chunk *)chunk, error); + break; + case SCTP_SHUTDOWN_ACK_CHUNK_TYPE: + result = sctp_shutdown_ack_chunk_to_string(s, + (struct sctp_shutdown_ack_chunk *)chunk, error); + break; + case SCTP_ERROR_CHUNK_TYPE: + result = sctp_error_chunk_to_string(s, + (struct sctp_error_chunk *)chunk, error); + break; + case SCTP_COOKIE_ECHO_CHUNK_TYPE: + result = sctp_cookie_echo_chunk_to_string(s, + (struct sctp_cookie_echo_chunk *)chunk, error); + break; + case SCTP_COOKIE_ACK_CHUNK_TYPE: + result = sctp_cookie_ack_chunk_to_string(s, + (struct sctp_cookie_ack_chunk *)chunk, error); + break; + case SCTP_ECNE_CHUNK_TYPE: + result = sctp_ecne_chunk_to_string(s, + (struct sctp_ecne_chunk *)chunk, error); + break; + case SCTP_CWR_CHUNK_TYPE: + result = sctp_cwr_chunk_to_string(s, + (struct sctp_cwr_chunk *)chunk, error); + break; + case SCTP_SHUTDOWN_COMPLETE_CHUNK_TYPE: + result = sctp_shutdown_complete_chunk_to_string(s, + (struct sctp_shutdown_complete_chunk *)chunk, error); + break; + default: + result = sctp_unknown_chunk_to_string(s, chunk, error); + break; + } + return result; +} diff --git a/gtests/net/packetdrill/sctp_chunk_to_string.h b/gtests/net/packetdrill/sctp_chunk_to_string.h new file mode 100644 index 00000000..1503bbdf --- /dev/null +++ b/gtests/net/packetdrill/sctp_chunk_to_string.h @@ -0,0 +1,40 @@ +/* + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +/* + * Author: ncardwell@google.com (Neal Cardwell) + * + * Interface for generating human-readable representations of SCTP chunks. + */ + +#ifndef __SCTP_CHUNK_TO_STRING_H__ +#define __SCTP_CHUNK_TO_STRING_H__ + +#include "types.h" +#include "packet.h" +#include "sctp.h" + +/* Write to s a human-readable representation of the SCTP + * chunk. Returns STATUS_OK on success; on failure + * returns STATUS_ERR and sets error message. + */ +extern int sctp_chunk_to_string(FILE *s, + struct sctp_chunk *chunk, + char **error); + +#endif /* __SCTP_CHUNK_TO_STRING_H__ */ diff --git a/gtests/net/packetdrill/sctp_iterator.c b/gtests/net/packetdrill/sctp_iterator.c new file mode 100644 index 00000000..fc37d162 --- /dev/null +++ b/gtests/net/packetdrill/sctp_iterator.c @@ -0,0 +1,200 @@ +/* + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +/* + * Author: ncardwell@google.com (Neal Cardwell) + * + * Implementation for module to allow iteration over SCTP chunks and + * parameters in wire format. + */ + +#include "sctp_iterator.h" + +#include <stdlib.h> +#include <string.h> +#include "packet.h" + +static struct sctp_chunk *get_current_chunk(struct sctp_chunks_iterator *iter, + char **error) +{ + struct sctp_chunk *chunk; + u16 chunk_length; + + if (iter->current_chunk == iter->packet_end) + iter->current_chunk = NULL; + else if (iter->current_chunk + sizeof(struct sctp_chunk) > + iter->packet_end) { + asprintf(error, "Partial SCTP chunk not allowed"); + iter->current_chunk = NULL; + } else { + chunk = (struct sctp_chunk *)iter->current_chunk; + chunk_length = ntohs(chunk->length); + if (iter->current_chunk + chunk_length > iter->packet_end) { + asprintf(error, + "Partial SCTP chunk (type 0x%02x, length %u) not allowed", + chunk->type, chunk_length); + iter->current_chunk = NULL; + } + } + return (struct sctp_chunk *)iter->current_chunk; +} + +struct sctp_chunk *sctp_chunks_begin(struct packet *packet, + struct sctp_chunks_iterator *iter, + char **error) +{ + assert(*error == NULL); + memset(iter, 0, sizeof(*iter)); + iter->current_chunk = packet_payload(packet); + iter->packet_end = packet_end(packet); + return get_current_chunk(iter, error); +} + +struct sctp_chunk *sctp_chunks_next(struct sctp_chunks_iterator *iter, + char **error) +{ + u16 chunk_length, padding_length; + struct sctp_chunk *current_chunk; + + assert(*error == NULL); + current_chunk = (struct sctp_chunk *)iter->current_chunk; + chunk_length = ntohs(current_chunk->length); + padding_length = chunk_length & 0x0003; + if (padding_length > 0) + padding_length = 4 - padding_length; + assert(chunk_length >= sizeof(struct sctp_chunk)); + assert(padding_length < 4); + iter->current_chunk += chunk_length; + iter->current_chunk += padding_length; + return get_current_chunk(iter, error); +} + +static struct sctp_parameter * +get_current_parameter(struct sctp_parameters_iterator *iter, + char **error) +{ + struct sctp_parameter *parameter; + u16 parameter_length; + + if (iter->current_parameter == iter->end) + iter->current_parameter = NULL; + else if (iter->current_parameter + sizeof(struct sctp_parameter) > + iter->end) { + asprintf(error, "Partial SCTP parameter not allowed"); + iter->current_parameter = NULL; + } else { + parameter = (struct sctp_parameter *)iter->current_parameter; + parameter_length = ntohs(parameter->length); + if (iter->current_parameter + parameter_length > iter->end) { + asprintf(error, + "Partial SCTP parameter (type 0x%04x, length %u) not allowed", + ntohs(parameter->type), parameter_length); + iter->current_parameter = NULL; + } + } + return (struct sctp_parameter *)iter->current_parameter; +} + +struct sctp_parameter * +sctp_parameters_begin(u8 *begin, + u16 length, + struct sctp_parameters_iterator *iter, + char **error) +{ + assert(*error == NULL); + memset(iter, 0, sizeof(*iter)); + iter->current_parameter = begin; + iter->end = begin + length; + return get_current_parameter(iter, error); +} + +struct sctp_parameter * +sctp_parameters_next(struct sctp_parameters_iterator *iter, + char **error) +{ + u16 parameter_length, padding_length; + struct sctp_parameter *current_parameter; + + assert(*error == NULL); + current_parameter = (struct sctp_parameter *)iter->current_parameter; + parameter_length = ntohs(current_parameter->length); + padding_length = parameter_length & 0x0003; + if (padding_length > 0) + padding_length = 4 - padding_length; + assert(parameter_length >= sizeof(struct sctp_parameter)); + assert(padding_length < 4); + iter->current_parameter += parameter_length; + iter->current_parameter += padding_length; + return get_current_parameter(iter, error); +} + +static struct sctp_cause *get_current_cause(struct sctp_causes_iterator *iter, + char **error) +{ + struct sctp_cause *cause; + u16 cause_length; + + if (iter->current_cause == iter->chunk_end) + iter->current_cause = NULL; + else if (iter->current_cause + sizeof(struct sctp_cause) > + iter->chunk_end) { + asprintf(error, "Partial SCTP cause not allowed"); + iter->current_cause = NULL; + } else { + cause = (struct sctp_cause *)iter->current_cause; + cause_length = ntohs(cause->length); + if (iter->current_cause + cause_length > iter->chunk_end) { + asprintf(error, + "Partial SCTP cause (code 0x%04x, length %u) not allowed", + ntohs(cause->code), cause_length); + iter->current_cause = NULL; + } + } + return (struct sctp_cause *)iter->current_cause; +} + +struct sctp_cause *sctp_causes_begin(struct sctp_chunk *chunk, + u16 offset, + struct sctp_causes_iterator *iter, + char **error) +{ + assert(*error == NULL); + memset(iter, 0, sizeof(*iter)); + iter->current_cause = (u8 *)chunk + offset; + iter->chunk_end = (u8 *)chunk + ntohs(chunk->length); + return get_current_cause(iter, error); +} + +struct sctp_cause *sctp_causes_next(struct sctp_causes_iterator *iter, + char **error) +{ + u16 cause_length, padding_length; + struct sctp_cause *current_cause; + + assert(*error == NULL); + current_cause = (struct sctp_cause *)iter->current_cause; + cause_length = ntohs(current_cause->length); + padding_length = cause_length & 0x0003; + if (padding_length > 0) + padding_length = 4 - padding_length; + assert(cause_length >= sizeof(struct sctp_cause)); + assert(padding_length < 4); + iter->current_cause += cause_length; + iter->current_cause += padding_length; + return get_current_cause(iter, error); +} diff --git a/gtests/net/packetdrill/sctp_iterator.h b/gtests/net/packetdrill/sctp_iterator.h new file mode 100644 index 00000000..36d596b3 --- /dev/null +++ b/gtests/net/packetdrill/sctp_iterator.h @@ -0,0 +1,102 @@ +/* + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +/* + * Author: ncardwell@google.com (Neal Cardwell) + * + * Interface for a module to allow iteration over TCP options in wire format. + */ + +#ifndef __SCTP_ITERATOR_H__ +#define __SCTP_ITERATOR_H__ + +#include "types.h" + +#include "packet.h" + +/* Internal state for an iterator for SCTP chunks in wire format. */ +struct sctp_chunks_iterator { + u8 *current_chunk; + u8 *packet_end; +}; + +/* Internal state for an iterator for SCTP parameters in wire format. */ +struct sctp_parameters_iterator { + u8 *current_parameter; + u8 *end; +}; + +/* Internal state for an iterator for SCTP causes in wire format. */ +struct sctp_causes_iterator { + u8 *current_cause; + u8 *chunk_end; +}; + +/* Initialize the iterator to iterate over the SCTP chunks in the + * given packet. Return a pointer to the first chunk in the packet, + * or NULL if there are none. + */ +extern struct sctp_chunk *sctp_chunks_begin( + struct packet *packet, + struct sctp_chunks_iterator *iter, + char **error); + +/* Return a pointer to the next chunk in the packet, or NULL if there + * are no more. On failure returns NULL and sets error message. + */ +extern struct sctp_chunk *sctp_chunks_next( + struct sctp_chunks_iterator *iter, + char **error); + +/* Initialize the iterator to iterate over the SCTP parameters in the + * given chunk. Return a pointer to the first parameter in the chunk, + * or NULL if there are none or an error is present. This the error case + * *error is not NULL. + */ +extern struct sctp_parameter *sctp_parameters_begin( + u8 *begin, + u16 length, + struct sctp_parameters_iterator *iter, + char **error); + +/* Return a pointer to the next parameter in the chunk, or NULL if there + * are no more. On failure returns NULL and sets error message. + */ +extern struct sctp_parameter *sctp_parameters_next( + struct sctp_parameters_iterator *iter, + char **error); + +/* Initialize the iterator to iterate over the SCTP causes in the + * given chunk. Return a pointer to the first cause in the chunk, + * or NULL if there are none or an error is present. This the error case + * *error is not NULL. + */ +extern struct sctp_cause *sctp_causes_begin( + struct sctp_chunk *chunk, + u16 offset, + struct sctp_causes_iterator *iter, + char **error); + +/* Return a pointer to the next cause in the chunk, or NULL if there + * are no more. On failure returns NULL and sets error message. + */ +extern struct sctp_cause *sctp_causes_next( + struct sctp_causes_iterator *iter, + char **error); + +#endif /* __SCTP_ITERATOR_H__ */ diff --git a/gtests/net/packetdrill/socket.h b/gtests/net/packetdrill/socket.h index 0b14a7e0..41b23c88 100644 --- a/gtests/net/packetdrill/socket.h +++ b/gtests/net/packetdrill/socket.h @@ -166,7 +166,10 @@ static inline void get_packet_tuple(const struct packet *packet, } else { assert(!"bad IP version in packet"); } - if (packet->tcp != NULL) { + if (packet->sctp != NULL) { + tuple->src.port = packet->sctp->src_port; + tuple->dst.port = packet->sctp->dst_port; + } else if (packet->tcp != NULL) { tuple->src.port = packet->tcp->src_port; tuple->dst.port = packet->tcp->dst_port; } else if (packet->udp != NULL) { @@ -181,6 +184,7 @@ static inline void get_packet_tuple(const struct packet *packet, /* Set the tuple inside some TCP/IPv4 or TCP/IPv6 headers. */ static inline void set_headers_tuple(struct ipv4 *ipv4, struct ipv6 *ipv6, + struct sctp_common_header *sctp, struct tcp *tcp, struct udp *udp, struct udplite *udplite, @@ -195,7 +199,10 @@ static inline void set_headers_tuple(struct ipv4 *ipv4, } else { assert(!"bad IP version in packet"); } - if (tcp != NULL) { + if (sctp != NULL) { + sctp->src_port = tuple->src.port; + sctp->dst_port = tuple->dst.port; + } else if (tcp != NULL) { tcp->src_port = tuple->src.port; tcp->dst_port = tuple->dst.port; } else if (udp != NULL) { @@ -226,6 +233,7 @@ static inline void set_icmp_echoed_tuple(struct packet *packet, reverse_tuple(tuple, &echoed_tuple); set_headers_tuple(packet_echoed_ipv4_header(packet), packet_echoed_ipv6_header(packet), + packet_echoed_sctp_header(packet), packet_echoed_tcp_header(packet), packet_echoed_udp_header(packet), packet_echoed_udplite_header(packet), @@ -236,7 +244,7 @@ static inline void set_icmp_echoed_tuple(struct packet *packet, static inline void set_packet_tuple(struct packet *packet, const struct tuple *tuple) { - set_headers_tuple(packet->ipv4, packet->ipv6, packet->tcp, + set_headers_tuple(packet->ipv4, packet->ipv6, packet->sctp, packet->tcp, packet->udp, packet->udplite, tuple); if ((packet->icmpv4 != NULL) || (packet->icmpv6 != NULL)) set_icmp_echoed_tuple(packet, tuple); diff --git a/gtests/net/packetdrill/types.h b/gtests/net/packetdrill/types.h index c3b37cef..6ccfbe44 100644 --- a/gtests/net/packetdrill/types.h +++ b/gtests/net/packetdrill/types.h @@ -53,7 +53,10 @@ #define __aligned(x) __attribute__ ((aligned(x))) #endif -/* Make sure we have the following constant on all platforms */ +/* Make sure we have the following constants on all platforms */ +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif #ifndef IPPROTO_UDPLITE #define IPPROTO_UDPLITE 136 #endif -- GitLab