From d88f7bf006b43f24c33feeab8aafb9e0b0b64ac1 Mon Sep 17 00:00:00 2001 From: Michael Tuexen <tuexen@fh-muenster.de> Date: Tue, 12 May 2015 13:19:04 +0200 Subject: [PATCH] Add support for injecting SCTP related ICMP messages. This fixes https://github.com/nplab/packetdrill/issues/7 --- gtests/net/packetdrill/icmp_packet.c | 5 ++++ gtests/net/packetdrill/icmp_packet.h | 1 + gtests/net/packetdrill/packet.h | 13 +++++++++++ gtests/net/packetdrill/parser.y | 8 ++++++- gtests/net/packetdrill/run_packet.c | 11 ++++++++- .../tests/bsd/sctp/sctp_icmp_active.pkt | 23 +++++++++++++++++++ .../tests/bsd/sctp/sctp_icmp_passive.pkt | 22 ++++++++++++++++++ 7 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_active.pkt create mode 100644 gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_passive.pkt diff --git a/gtests/net/packetdrill/icmp_packet.c b/gtests/net/packetdrill/icmp_packet.c index 73a890ca..36c0b9fc 100644 --- a/gtests/net/packetdrill/icmp_packet.c +++ b/gtests/net/packetdrill/icmp_packet.c @@ -283,6 +283,7 @@ struct packet *new_icmp_packet(int address_family, u16 payload_bytes, u32 tcp_start_sequence, u16 udplite_checksum_coverage, + u32 sctp_verification_tag, s64 mtu, char **error) { @@ -352,6 +353,10 @@ struct packet *new_icmp_packet(int address_family, payload_bytes); set_ip_header(echoed_ip, address_family, echoed_ip_bytes, ecn, protocol); + if (protocol == IPPROTO_SCTP) { + u32 *v_tag = packet_echoed_sctp_v_tag(packet); + *v_tag = htonl(sctp_verification_tag); + } if (protocol == IPPROTO_TCP) { u32 *seq = packet_echoed_tcp_seq(packet); *seq = htonl(tcp_start_sequence); diff --git a/gtests/net/packetdrill/icmp_packet.h b/gtests/net/packetdrill/icmp_packet.h index ba3db691..609d3a66 100644 --- a/gtests/net/packetdrill/icmp_packet.h +++ b/gtests/net/packetdrill/icmp_packet.h @@ -48,6 +48,7 @@ extern struct packet *new_icmp_packet(int address_family, u16 payload_bytes, u32 tcp_start_sequence, u16 udplite_checksum_coverage, + u32 sctp_verification_tag, s64 mtu, char **error); diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h index dfedcd8d..6348cc4e 100644 --- a/gtests/net/packetdrill/packet.h +++ b/gtests/net/packetdrill/packet.h @@ -394,6 +394,19 @@ packet_echoed_sctp_header(struct packet *packet) return NULL; } +/* Return the location of the SCTP verification tag echoed by an ICMP message. */ +static inline u32 *packet_echoed_sctp_v_tag(struct packet *packet) +{ + struct sctp_common_header *echoed_sctp = packet_echoed_sctp_header(packet); + assert(echoed_sctp); + u32 *v_tag = &(echoed_sctp->v_tag); + /* Check that the v_tag field is actually in the space we + * reserved for the echoed prefix of the SCTP common header. + */ + assert((char *) (v_tag + 1) <= (char *) echoed_sctp + ICMP_ECHO_BYTES); + return v_tag; +} + /* 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/parser.y b/gtests/net/packetdrill/parser.y index f3f8f328..ec044345 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -457,6 +457,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, struct { int protocol; u16 payload_bytes; + u32 verification_tag; /* used for SCTP */ u32 start_sequence; /* used for TCP */ u16 checksum_coverage; /* used for UDPLite */ } transport_info; @@ -1174,7 +1175,7 @@ icmp_packet_spec inner = new_icmp_packet(in_config->wire_protocol, direction, $4, $5, $2.protocol, $2.payload_bytes, $2.start_sequence, $2.checksum_coverage, - $6, &error); + $2.verification_tag, $6, &error); free($4); free($5); if (inner == NULL) { @@ -1287,6 +1288,11 @@ opt_icmp_echoed $$.payload_bytes = 0; $$.start_sequence = 0; } +| '[' SCTP '(' INTEGER ')' ']' { + $$.protocol = IPPROTO_SCTP; + $$.payload_bytes = 0; + $$.verification_tag = $4; +} | '[' UDP '(' INTEGER ')' ']' { $$.protocol = IPPROTO_UDP; $$.payload_bytes = $4; diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c index 27b3e66f..4f5e0b6a 100644 --- a/gtests/net/packetdrill/run_packet.c +++ b/gtests/net/packetdrill/run_packet.c @@ -517,6 +517,13 @@ static int offset_sack_blocks(struct packet *packet, return *error ? STATUS_ERR : STATUS_OK; } +static int map_inbound_icmp_sctp_packet( + struct socket *socket, struct packet *live_packet, char **error) +{ + u32 *v_tag = packet_echoed_sctp_v_tag(live_packet); + *v_tag = htonl(socket->live.remote_initiate_tag); + return STATUS_OK; +} /* Rewrite the TCP sequence number echoed by the ICMP packet. * The Linux TCP layer ignores ICMP messages with bogus sequence numbers. @@ -548,7 +555,9 @@ static int map_inbound_icmp_udplite_packet( static int map_inbound_icmp_packet( struct socket *socket, struct packet *live_packet, char **error) { - if (packet_echoed_ip_protocol(live_packet) == IPPROTO_TCP) + if (packet_echoed_ip_protocol(live_packet) == IPPROTO_SCTP) + return map_inbound_icmp_sctp_packet(socket, live_packet, error); + else if (packet_echoed_ip_protocol(live_packet) == IPPROTO_TCP) return map_inbound_icmp_tcp_packet(socket, live_packet, error); else if (packet_echoed_ip_protocol(live_packet) == IPPROTO_UDP) return map_inbound_icmp_udp_packet(socket, live_packet, error); diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_active.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_active.pkt new file mode 100644 index 00000000..c02a9e08 --- /dev/null +++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_active.pkt @@ -0,0 +1,23 @@ +// Create a non-blocking socket. ++0.0 socket(..., SOCK_STREAM, IPPROTO_SCTP) = 3 ++0.0 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR) ++0.0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0 +// Check the handshake with an empty(!) cookie. ++0.1 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress) ++0.0 > sctp: INIT[tag=1 tsn=0] ++0.1 < sctp: INIT_ACK[tag=2 a_rwnd=4500 os=1 is =1 tsn=3] // faked cookie ++0.0 > sctp: COOKIE_ECHO[] // faked cookie ++0.1 < sctp: COOKIE_ACK[] ++0.0 getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 +// Reduce the PMTU. ++0.5 < [sctp(2)] icmp unreachable frag_needed mtu 1500 +// Send some data and get it acknowledged. ++0.5 write(3, ..., 2000) = 2000 ++0.0 > sctp: DATA[flgs=B len=1468 tsn=0 sid=0 ssn=0 ppid=0] ++0.0 > sctp: DATA[flgs=E len=564 tsn=1 sid=0 ssn=0 ppid=0] ++0.1 < sctp: SACK[tsn=1 a_rwnd=1500 gaps=[] dups=[]] +// Tear down the association ++0.0 close(3) = 0 ++0.0 > sctp: SHUTDOWN[tsn=2] ++0.1 < sctp: SHUTDOWN_ACK[] ++0.0 > sctp: SHUTDOWN_COMPLETE[] diff --git a/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_passive.pkt b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_passive.pkt new file mode 100644 index 00000000..9daa9c7f --- /dev/null +++ b/gtests/net/packetdrill/tests/bsd/sctp/sctp_icmp_passive.pkt @@ -0,0 +1,22 @@ ++0.0 socket(..., SOCK_STREAM, IPPROTO_SCTP) = 3 +// Check the handshake with en empty(!) cookie ++0.0 bind(3, ..., ...) = 0 ++0.0 listen(3, 1) = 0 ++0.0 < sctp: INIT[tag=1 a_rwnd=4500 os=1 is=1 tsn=0] ++0.0 > sctp: INIT_ACK[tag=2 tsn=0] // faked cookie ++0.1 < sctp: COOKIE_ECHO[] ++0.0 > sctp: COOKIE_ACK[] ++0.0 accept(3, ..., ...) = 4 ++0.0 close(3) = 0 +// Reduce the PMTU. ++0.5 < [sctp(1)] icmp unreachable frag_needed mtu 1500 +// Send some data and get it acknowledged. ++0.5 write(4, ..., 2000) = 2000 ++0.0 > sctp: DATA[flgs=B len=1468 tsn=0 sid=0 ssn=0 ppid=0] ++0.0 > sctp: DATA[flgs=E len=564 tsn=1 sid=0 ssn=0 ppid=0] ++0.1 < sctp: SACK[tsn=1 a_rwnd=4500 gaps=[] dups=[]] +// Tear down the association ++0.0 close(4) = 0 ++0.0 > sctp: SHUTDOWN[tsn=2] ++0.1 < sctp: SHUTDOWN_ACK[] ++0.0 > sctp: SHUTDOWN_COMPLETE[] -- GitLab