diff --git a/gtests/net/packetdrill/Makefile.common b/gtests/net/packetdrill/Makefile.common index 9ff5027c931ae354cfd5fcdbbbf56634bfa4b312..e58ab8540bbe7e2b52732fb2d6ad1534c6fc8235 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 389b126dd3c2c31266899f5a3b258d819d62b2ca..e0a3e09aa824c8eb603c4a82a371850527cb96d8 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 bb62f3b69535cf41edf4db9f9d905aef2aaa9787..dbb33b177c743bdfcb7cc98b53893a34f4bd4398 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 57f8f73d01754c5ed475dbc3f121c295435eb729..06ddf02dcdd4f5f1140e757c8297e6d94967f1d9 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 1bd6d0d30365b91600a372139d2147fb0a1a3e37..6599777da5cd10475a61a8486ad165649697ef47 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 cbc9c3cea5b452ad17678c777bb2a04f7ee0fd4d..6eb6b0bd412183a65aa4ff828210f55011624c79 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 9fc5e5fb7cd69ded9ebe6d4664484291d8e5ad13..08573d39da96c9f2ea18160c4c92d23ffd147009 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 8851437a4ab38295b1b9198cc5dbb32e8112abda..0e0f0788a7a13b56d0ccc5426086fa9af0e828d1 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 11df81187dfd62645d6c7e6b31e6414b4afd0d84..207d8ee0d7069904e1b1f06eef23eecd40392ed3 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 9877c4eb263e91262014f072dc805f02c049bd4e..d0e9dcd14d9eb287a5f109ba94e474cea8d2b798 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 b831dd9c22613496f87025e709c905cc57f7a84a..ded5509ae450cdda30cbc83044fc0dc3c2fd02cf 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 0000000000000000000000000000000000000000..48b05824e3c2498a72a731f37c1ec9157a79c52b --- /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 0000000000000000000000000000000000000000..1503bbdf79c92d3ddbabf5d0fabcab1d8719df06 --- /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 0000000000000000000000000000000000000000..fc37d1622ad4b0f9ddab04e13b5bfa7479b379fc --- /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 0000000000000000000000000000000000000000..36d596b34629290687a0c95b565d17ef7fc3c24f --- /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 0b14a7e0fe52c2776d2ecc6f17111d0e49954d00..41b23c88df9507a38d411e4b84460ae0e0bb0047 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 c3b37cef43e6990e142a0ba09d53d1816227569a..6ccfbe4422bd9014a5b5c945b117ae7a3aa1a059 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