From e1205285e7d9ad4174af26e9773027aae2030a8a Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Mon, 27 Mar 2017 09:29:55 -0700 Subject: [PATCH] net-test: packetdrill: add PSP encryption Add packetdrill support for the PSP encryption protocol, both in tunnel and transport mode. For more information on PSP, see the architecture spec at https://github.com/google/psp/blob/main/doc/PSP_Arch_Spec.pdf Implementation by to Dimitris Michailidis This implementation is against the upstream draft PSP protocol as published at https://github.com/wdebruij/psp/tree/linux-v5.15-psp-v1.1 This is for demonstration purposes only: an upstream Linux ABI is expected to look significantly different. Tested: Test psp scripts with a psp-capable tuntap device in qemu # step 1: build kernel with psp support git clone https://github.com/wdebruij/psp.git cd psp git checkout origin/linux-v5.15-psp-v1.1 make defconfig make kvm_guest.config sed -i 's/#\ CONFIG_TUN\ is\ not\ set/CONFIG_TUN=y/' .config make -j $(nproc) bzImage # step 2 inside qemu, build and run packetdrill git clone github.com/google/packetdrill cd gtests/net/packetdrill make && make tests PDIR=${PWD} cd ../tcp $PDIR/in_netns.sh $PDIR/packetdrill --ip_version=ipv6 --mtu=1520 psp/psp_client_sockopt.pkt $PDIR/in_netns.sh $PDIR/packetdrill --ip_version=ipv6 --mtu=1520 psp/psp_server_sockopt.pkt Signed-off-by: Willem de Bruijn --- gtests/net/packetdrill/Makefile.common | 2 +- gtests/net/packetdrill/config.c | 9 + gtests/net/packetdrill/config.h | 1 + gtests/net/packetdrill/header.h | 3 + gtests/net/packetdrill/lexer.l | 5 + gtests/net/packetdrill/packet.c | 11 +- gtests/net/packetdrill/packet.h | 5 +- gtests/net/packetdrill/packet_checksum.c | 10 +- gtests/net/packetdrill/packet_parser.c | 65 ++++++- gtests/net/packetdrill/packet_parser_test.c | 178 ++++++++++++++++++ gtests/net/packetdrill/packet_to_string.c | 72 ++++++- .../net/packetdrill/packet_to_string_test.c | 177 +++++++++++++++++ gtests/net/packetdrill/parser.y | 94 ++++++++- gtests/net/packetdrill/platforms.h | 3 + gtests/net/packetdrill/psp.h | 86 +++++++++ gtests/net/packetdrill/psp_packet.c | 87 +++++++++ gtests/net/packetdrill/psp_packet.h | 74 ++++++++ gtests/net/packetdrill/run_packet.c | 46 ++++- gtests/net/packetdrill/run_system_call.c | 79 +++++--- gtests/net/packetdrill/script.c | 5 + gtests/net/packetdrill/script.h | 2 + gtests/net/packetdrill/socket.h | 8 +- gtests/net/packetdrill/symbols_linux.c | 4 + gtests/net/packetdrill/tcp.h | 5 + gtests/net/packetdrill/tcp_packet.c | 18 +- gtests/net/packetdrill/tcp_packet.h | 2 + .../tests/encap-injection-psp-v6.pkt | 35 ++++ .../packetdrill/tests/encap-injection-psp.pkt | 33 ++++ .../packetdrill/tests/encap-injection-v6.pkt | 60 ++++++ .../net/packetdrill/tests/encap-injection.pkt | 57 ++++++ .../packetdrill/tests/encap-syntax-psp.pkt | 37 ++++ gtests/net/packetdrill/tests/encap-syntax.pkt | 70 +++++++ gtests/net/packetdrill/udp_packet.c | 34 ++++ gtests/net/packetdrill/udp_packet.h | 8 + gtests/net/tcp/psp/psp_client_sockopt.pkt | 25 +++ gtests/net/tcp/psp/psp_server_sockopt.pkt | 36 ++++ 36 files changed, 1394 insertions(+), 52 deletions(-) create mode 100644 gtests/net/packetdrill/psp.h create mode 100644 gtests/net/packetdrill/psp_packet.c create mode 100644 gtests/net/packetdrill/psp_packet.h create mode 100644 gtests/net/packetdrill/tests/encap-injection-psp-v6.pkt create mode 100644 gtests/net/packetdrill/tests/encap-injection-psp.pkt create mode 100644 gtests/net/packetdrill/tests/encap-injection-v6.pkt create mode 100644 gtests/net/packetdrill/tests/encap-injection.pkt create mode 100644 gtests/net/packetdrill/tests/encap-syntax-psp.pkt create mode 100644 gtests/net/packetdrill/tests/encap-syntax.pkt create mode 100644 gtests/net/tcp/psp/psp_client_sockopt.pkt create mode 100644 gtests/net/tcp/psp/psp_server_sockopt.pkt diff --git a/gtests/net/packetdrill/Makefile.common b/gtests/net/packetdrill/Makefile.common index b614d085..63c05813 100644 --- a/gtests/net/packetdrill/Makefile.common +++ b/gtests/net/packetdrill/Makefile.common @@ -20,7 +20,7 @@ packetdrill-lib := \ symbols_openbsd.o \ symbols_netbsd.o \ gre_packet.o icmp_packet.o ip_packet.o tcp_packet.o udp_packet.o \ - mpls_packet.o \ + mpls_packet.o psp_packet.o \ run.o run_command.o run_packet.o run_system_call.o \ script.o socket.o system.o \ tcp_options.o tcp_options_iterator.o tcp_options_to_string.o \ diff --git a/gtests/net/packetdrill/config.c b/gtests/net/packetdrill/config.c index 9bdf1ecd..83e084a4 100644 --- a/gtests/net/packetdrill/config.c +++ b/gtests/net/packetdrill/config.c @@ -71,6 +71,7 @@ enum option_codes { OPT_DRY_RUN, OPT_IS_ANYIP, OPT_SEND_OMIT_FREE, + OPT_PSP_UDP_DPORT, OPT_DEBUG, OPT_DEFINE = 'D', /* a '-D' single-letter option */ OPT_VERBOSE = 'v', /* a '-v' single-letter option */ @@ -110,6 +111,7 @@ struct option options[] = { { "dry_run", .has_arg = false, NULL, OPT_DRY_RUN }, { "is_anyip", .has_arg = false, NULL, OPT_IS_ANYIP }, { "send_omit_free", .has_arg = false, NULL, OPT_SEND_OMIT_FREE }, + { "psp_udp_port", .has_arg = true, NULL, OPT_PSP_UDP_DPORT }, { "debug", .has_arg = false, NULL, OPT_DEBUG }, { "define", .has_arg = true, NULL, OPT_DEFINE }, { "verbose", .has_arg = false, NULL, OPT_VERBOSE }, @@ -151,6 +153,7 @@ void show_usage(void) "\t[--dry_run]\n" "\t[--is_anyip]\n" "\t[--send_omit_free]\n" + "\t[--psp_udp_port=]\n" "\t[--debug]\n" "\t[--define symbol1=val1 --define symbol2=val2 ...]\n" "\t[--verbose|-v]\n" @@ -611,6 +614,12 @@ static void process_option(int opt, char *optarg, struct config *config, case OPT_SEND_OMIT_FREE: config->send_omit_free = true; break; + case OPT_PSP_UDP_DPORT: + port = atoi(optarg); + if (port <= 0 || port > 0xffff) + die("%s: bad --psp_udp_port: %s\n", where, optarg); + config->psp_udp_port = port; + break; case OPT_DEBUG: opt_debug++; break; diff --git a/gtests/net/packetdrill/config.h b/gtests/net/packetdrill/config.h index 4a66f72a..d286cb24 100644 --- a/gtests/net/packetdrill/config.h +++ b/gtests/net/packetdrill/config.h @@ -126,6 +126,7 @@ struct config { int mtu; /* MTU of tun device */ bool strict_segments; /* check exact segmentation? */ + int psp_udp_port; /* UDP dst port for PSP encapsulation */ bool non_fatal_packet; /* treat packet asserts as non-fatal */ bool non_fatal_syscall; /* treat syscall asserts as non-fatal */ diff --git a/gtests/net/packetdrill/header.h b/gtests/net/packetdrill/header.h index bfd339f5..3d1b0620 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 "psp.h" #include "tcp.h" #include "udp.h" @@ -55,6 +56,7 @@ enum header_t { HEADER_UDP, HEADER_ICMPV4, HEADER_ICMPV6, + HEADER_PSP, HEADER_NUM_TYPES }; @@ -73,6 +75,7 @@ struct header { struct udp *udp; struct icmpv4 *icmpv4; struct icmpv6 *icmpv6; + struct psp *psp; } h; }; diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l index 0d61a3dd..8b712e00 100644 --- a/gtests/net/packetdrill/lexer.l +++ b/gtests/net/packetdrill/lexer.l @@ -240,6 +240,11 @@ none return NONE; checksum return CHECKSUM; sequence# return SEQUENCE; present return PRESENT; +encap return ENCAP; +psp return PSP; +spi return SPI; +vc return VC; +vnid return VNID; mpls return MPLS; label return LABEL; tc return TC; diff --git a/gtests/net/packetdrill/packet.c b/gtests/net/packetdrill/packet.c index d2d792a2..d898550e 100644 --- a/gtests/net/packetdrill/packet.c +++ b/gtests/net/packetdrill/packet.c @@ -33,6 +33,8 @@ #include "ip_packet.h" #include "logging.h" #include "mpls_packet.h" +#include "psp_packet.h" +#include "udp_packet.h" /* Info for all types of header we support. */ @@ -43,9 +45,10 @@ struct header_type_info header_types[HEADER_NUM_TYPES] = { { "GRE", IPPROTO_GRE, 0, gre_header_finish }, { "MPLS", 0, ETHERTYPE_MPLS_UC, mpls_header_finish }, { "TCP", IPPROTO_TCP, 0, NULL }, - { "UDP", IPPROTO_UDP, 0, NULL }, + { "UDP", IPPROTO_UDP, 0, udp_header_finish }, { "ICMPV4", IPPROTO_ICMP, 0, NULL }, { "ICMPV6", IPPROTO_ICMPV6, 0, NULL }, + { "PSP", 0, 0, psp_header_finish }, }; struct packet *packet_new(u32 buffer_bytes) @@ -173,6 +176,7 @@ static void packet_duplicate_info(struct packet *packet, packet->udp = offset_ptr(old_base, new_base, old_packet->udp); packet->icmpv4 = offset_ptr(old_base, new_base, old_packet->icmpv4); packet->icmpv6 = offset_ptr(old_base, new_base, old_packet->icmpv6); + packet->psp = offset_ptr(old_base, new_base, old_packet->psp); packet->tcp_ts_val = offset_ptr(old_base, new_base, old_packet->tcp_ts_val); @@ -254,6 +258,11 @@ struct packet *packet_encapsulate(struct packet *outer, struct packet *inner) packet_finish_encapsulation_headers(packet); + /* Both inner and outer may have PSP headers. Keep the inner. */ + if (packet->psp == NULL) + packet->psp = offset_ptr(outer->buffer, packet->buffer, + outer->psp); + packet->ip_bytes = outer->ip_bytes + inner->ip_bytes; return packet; diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h index ed9d3b7b..07b8f493 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 "psp.h" #include "tcp.h" #include "udp.h" #include "unaligned.h" @@ -98,6 +99,8 @@ struct packet { bool echoed_header; /* icmp payload is an echoed header? This is for TCP/UDP */ + /* Encapsulation */ + struct psp *psp; s64 time_usecs; /* wall time of receive/send if non-zero */ @@ -201,7 +204,7 @@ static inline u8 *packet_start(const struct packet *packet) } /* Return a pointer to the first byte of the innermost IP header. */ -static inline u8 *ip_start(struct packet *packet) +static inline u8 *ip_start(const struct packet *packet) { if (packet->ipv4 != NULL) return (u8 *)packet->ipv4; diff --git a/gtests/net/packetdrill/packet_checksum.c b/gtests/net/packetdrill/packet_checksum.c index d5164b34..0ffc9134 100644 --- a/gtests/net/packetdrill/packet_checksum.c +++ b/gtests/net/packetdrill/packet_checksum.c @@ -47,10 +47,13 @@ static void checksum_ipv4_packet(struct packet *packet) /* Fill in IPv4-based layer 4 checksum. */ if (packet->tcp != NULL) { struct tcp *tcp = packet->tcp; + const int encap_bytes = ((u8 *)tcp - (u8 *)ipv4) - + ipv4_header_len(ipv4); + const int tcp_bytes = l4_bytes - encap_bytes; tcp->check = 0; tcp->check = tcp_udp_v4_checksum(ipv4->src_ip, ipv4->dst_ip, - IPPROTO_TCP, tcp, l4_bytes); + IPPROTO_TCP, tcp, tcp_bytes); } else if (packet->udp != NULL) { struct udp *udp = packet->udp; udp->check = 0; @@ -81,10 +84,13 @@ static void checksum_ipv6_packet(struct packet *packet) /* Fill in IPv6-based layer 4 checksum. */ if (packet->tcp != NULL) { struct tcp *tcp = packet->tcp; + const int encap_bytes = ((u8 *)tcp - (u8 *)ipv6) - + sizeof(*ipv6); + const int tcp_bytes = l4_bytes - encap_bytes; tcp->check = 0; tcp->check = tcp_udp_v6_checksum(&ipv6->src_ip, &ipv6->dst_ip, - IPPROTO_TCP, tcp, l4_bytes); + IPPROTO_TCP, tcp, tcp_bytes); } else if (packet->udp != NULL) { struct udp *udp = packet->udp; udp->check = 0; diff --git a/gtests/net/packetdrill/packet_parser.c b/gtests/net/packetdrill/packet_parser.c index f593233d..b9c7afe3 100644 --- a/gtests/net/packetdrill/packet_parser.c +++ b/gtests/net/packetdrill/packet_parser.c @@ -42,6 +42,7 @@ #include "ip_address.h" #include "logging.h" #include "packet.h" +#include "psp_packet.h" #include "tcp.h" static int parse_ipv4(struct packet *packet, u8 *header_start, u8 *packet_end, @@ -394,6 +395,52 @@ static int parse_tcp(struct packet *packet, u8 *layer4_start, int layer4_bytes, return PACKET_BAD; } +static int parse_psp(struct packet *packet, u8 *psp_start, int psp_bytes, + u8 *packet_end, char **error) +{ + assert(psp_bytes >= 0); + assert(psp_start + psp_bytes <= packet_end); + if (psp_bytes < PSP_MINLEN) { + asprintf(error, "Truncated PSP header"); + goto error_out; + } + + struct psp *psp = (struct psp *)psp_start; + if (psp->version != 0) { + asprintf(error, "Unsupported PSP header version %u", + psp->version); + goto error_out; + } + if (psp->ext_len != 1 + psp->has_vc) { + asprintf(error, "Wrong PSP header length"); + goto error_out; + } + + const int psp_header_len = psp_len(psp); + if (psp_header_len > psp_bytes) { + asprintf(error, "PSP header overflows packet"); + goto error_out; + } + + DEBUGP("PSP header len: %d\n", psp_header_len); + + struct header *psp_header; + psp_header = packet_append_header(packet, HEADER_PSP, psp_header_len); + if (psp_header == NULL) { + asprintf(error, "Too many nested headers at PSP header"); + goto error_out; + } + psp_header->total_bytes = psp_bytes; + packet->psp = psp; + + u8 *next_proto_start = psp_start + psp_header_len; + return parse_layer4(packet, next_proto_start, psp->next_header, + psp_bytes - psp_header_len, packet_end, error); + +error_out: + return PACKET_BAD; +} + /* Parse the UDP header. Return a packet_parse_result_t. */ static int parse_udp(struct packet *packet, u8 *layer4_start, int layer4_bytes, u8 *packet_end, char **error) @@ -406,8 +453,9 @@ static int parse_udp(struct packet *packet, u8 *layer4_start, int layer4_bytes, asprintf(error, "Truncated UDP header"); goto error_out; } - packet->udp = (struct udp *) p; - const int udp_len = ntohs(packet->udp->len); + + struct udp *udp = (struct udp *)p; + const int udp_len = ntohs(udp->len); const int udp_header_len = sizeof(struct udp); if (udp_len < udp_header_len) { asprintf(error, "UDP datagram length too small for UDP header"); @@ -432,8 +480,17 @@ static int parse_udp(struct packet *packet, u8 *layer4_start, int layer4_bytes, p += layer4_bytes; assert(p <= packet_end); - DEBUGP("UDP src port: %d\n", ntohs(packet->udp->src_port)); - DEBUGP("UDP dst port: %d\n", ntohs(packet->udp->dst_port)); + u16 dst_port = ntohs(udp->dst_port); + DEBUGP("UDP src port: %d\n", ntohs(udp->src_port)); + DEBUGP("UDP dst port: %d\n", dst_port); + + if (is_psp_port(dst_port)) + return parse_psp(packet, layer4_start + udp_header_len, + layer4_bytes - udp_header_len, packet_end, + error); + + /* This UDP header is the innermost L4 rather than part of an encap. */ + packet->udp = udp; return PACKET_OK; error_out: diff --git a/gtests/net/packetdrill/packet_parser_test.c b/gtests/net/packetdrill/packet_parser_test.c index d0d33d9d..752b58bd 100644 --- a/gtests/net/packetdrill/packet_parser_test.c +++ b/gtests/net/packetdrill/packet_parser_test.c @@ -24,6 +24,7 @@ #include "assert.h" #include "packet_parser.h" +#include "psp_packet.h" #include #include @@ -67,6 +68,7 @@ static void test_parse_tcp_ipv4_packet(void) assert(packet->udp == NULL); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); + assert(packet->psp == NULL); assert(packet->time_usecs == 0); assert(packet->flags == 0); @@ -113,6 +115,7 @@ static void test_parse_tcp_ipv6_packet(void) assert(packet->udp == NULL); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); + assert(packet->psp == NULL); assert(packet->time_usecs == 0); assert(packet->flags == 0); @@ -152,6 +155,7 @@ static void test_parse_udp_ipv4_packet(void) assert(packet->udp == expected_udp); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); + assert(packet->psp == NULL); assert(packet->time_usecs == 0); assert(packet->flags == 0); @@ -195,6 +199,7 @@ static void test_parse_udp_ipv6_packet(void) assert(packet->udp == expected_udp); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); + assert(packet->psp == NULL); assert(packet->time_usecs == 0); assert(packet->flags == 0); @@ -272,6 +277,7 @@ static void test_parse_ipv4_gre_ipv4_tcp_packet(void) assert(packet->udp == NULL); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); + assert(packet->psp == NULL); assert(packet->time_usecs == 0); assert(packet->flags == 0); @@ -369,6 +375,174 @@ static void test_parse_ipv4_gre_mpls_ipv4_tcp_packet(void) assert(packet->udp == NULL); assert(packet->icmpv4 == NULL); assert(packet->icmpv6 == NULL); + assert(packet->psp == NULL); + + assert(packet->time_usecs == 0); + assert(packet->flags == 0); + + packet_free(packet); +} + +/* tunnel-mode PSP encapsulation */ +static void test_parse_ipv4_psp_ipv4_tcp_packet(void) +{ + u8 *p = NULL; + int i; + + /* An IPv4/UDP/PSP/IPv4/TCP packet. */ + u8 data[] = { + /* IP 2.2.2.2.42743 > 1.1.1.1.1000: UDP, length 68: + PSP spi 12 vc 0x17da01c2: + IP 192.0.2.1.47078 > 192.168.0.1.8080: + . 2:6(4) ack 1 win 123 */ + 0x45, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x11, 0xb5, 0x87, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x01, 0xa6, 0xf7, 0x03, 0xe8, + 0x00, 0x4c, 0x00, 0x00, 0x04, 0x02, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0xda, 0x01, 0xc2, 0x45, 0x00, 0x00, 0x2c, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x06, 0x39, 0x21, + 0xc0, 0x00, 0x02, 0x01, 0xc0, 0xa8, 0x00, 0x01, + 0xb7, 0xe6, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x50, 0x10, 0x00, 0x7b, + 0x55, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + 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), PACKET_LAYER_3_IP, &error); + assert(result == PACKET_OK); + assert(error == NULL); + + p = packet->buffer; + i = 0; /* outer most layer, 0 */ + + assert(packet->headers[i].type == HEADER_IPV4); + assert(packet->headers[i].h.ptr == p); + assert(packet->headers[i].header_bytes == sizeof(struct ipv4)); + p += packet->headers[i].header_bytes; + i++; + + assert(packet->headers[i].type == HEADER_UDP); + assert(packet->headers[i].h.ptr == p); + assert(packet->headers[i].header_bytes == sizeof(struct udp)); + p += packet->headers[i].header_bytes; + i++; + + struct psp *expected_psp = (struct psp *)p; + assert(packet->headers[i].type == HEADER_PSP); + assert(packet->headers[i].h.ptr == p); + assert(packet->headers[i].header_bytes == sizeof(struct psp)); + p += packet->headers[i].header_bytes; + i++; + + struct ipv4 *expected_inner_ipv4 = (struct ipv4 *)p; + assert(packet->headers[i].type == HEADER_IPV4); + assert(packet->headers[i].h.ptr == p); + assert(packet->headers[i].header_bytes == sizeof(struct ipv4)); + p += packet->headers[i].header_bytes; + i++; + + struct tcp *expected_tcp = (struct tcp *)p; + assert(packet->headers[i].type == HEADER_TCP); + assert(packet->headers[i].h.ptr == p); + assert(packet->headers[i].header_bytes == sizeof(struct tcp)); + p += packet->headers[i].header_bytes; + i++; + + assert(packet->headers[i].type == HEADER_NONE); + + assert(packet->ip_bytes == sizeof(data)); + assert(packet->ipv4 == expected_inner_ipv4); + assert(packet->ipv6 == NULL); + assert(packet->tcp == expected_tcp); + assert(packet->udp == NULL); + assert(packet->icmpv4 == NULL); + assert(packet->icmpv6 == NULL); + assert(packet->psp == expected_psp); + + assert(packet->time_usecs == 0); + assert(packet->flags == 0); + + packet_free(packet); +} + +/* transport-mode PSP encapsulation */ +static void test_parse_ipv4_psp_tcp_packet(void) +{ + u8 *p = NULL; + int i; + + /* An IPv4/UDP/PSP/TCP packet. */ + u8 data[] = { + /* IP 192.0.2.1.47078 > 192.168.0.1.8080: PSP spi 18: + . 2:6(4) ack 1 win 123 */ + 0x45, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x11, 0x38, 0xfe, 0xc0, 0x00, 0x02, 0x01, + 0xc0, 0xa8, 0x00, 0x01, 0xa6, 0xf7, 0x03, 0xe8, + 0x00, 0x30, 0x00, 0x00, 0x06, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb7, 0xe6, 0x1f, 0x90, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x10, 0x00, 0x7b, 0x55, 0x31, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + 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), PACKET_LAYER_3_IP, &error); + assert(result == PACKET_OK); + assert(error == NULL); + + p = packet->buffer; + i = 0; /* outer most layer, 0 */ + + struct ipv4 *expected_ipv4 = (struct ipv4 *)p; + assert(packet->headers[i].type == HEADER_IPV4); + assert(packet->headers[i].h.ptr == p); + assert(packet->headers[i].header_bytes == sizeof(struct ipv4)); + p += packet->headers[i].header_bytes; + i++; + + assert(packet->headers[i].type == HEADER_UDP); + assert(packet->headers[i].h.ptr == p); + assert(packet->headers[i].header_bytes == sizeof(struct udp)); + p += packet->headers[i].header_bytes; + i++; + + struct psp *expected_psp = (struct psp *)p; + assert(packet->headers[i].type == HEADER_PSP); + assert(packet->headers[i].h.ptr == p); + assert(packet->headers[i].header_bytes == PSP_MINLEN); + p += packet->headers[i].header_bytes; + i++; + + struct tcp *expected_tcp = (struct tcp *)p; + assert(packet->headers[i].type == HEADER_TCP); + assert(packet->headers[i].h.ptr == p); + assert(packet->headers[i].header_bytes == sizeof(struct tcp)); + p += packet->headers[i].header_bytes; + i++; + + assert(packet->headers[i].type == HEADER_NONE); + + assert(packet->ip_bytes == sizeof(data)); + assert(packet->ipv4 == expected_ipv4); + assert(packet->ipv6 == NULL); + assert(packet->tcp == expected_tcp); + assert(packet->udp == NULL); + assert(packet->icmpv4 == NULL); + assert(packet->icmpv6 == NULL); + assert(packet->psp == expected_psp); assert(packet->time_usecs == 0); assert(packet->flags == 0); @@ -480,5 +654,9 @@ int main(void) test_parse_icmpv4_packet(); test_parse_icmpv6_packet(); + + set_psp_port(1000); + test_parse_ipv4_psp_ipv4_tcp_packet(); + test_parse_ipv4_psp_tcp_packet(); return 0; } diff --git a/gtests/net/packetdrill/packet_to_string.c b/gtests/net/packetdrill/packet_to_string.c index cdd410d1..f5a1e2b9 100644 --- a/gtests/net/packetdrill/packet_to_string.c +++ b/gtests/net/packetdrill/packet_to_string.c @@ -27,6 +27,7 @@ #include #include "socket.h" +#include "psp_packet.h" #include "tcp_options_to_string.h" static void endpoints_to_string(FILE *s, const struct packet *packet) @@ -37,6 +38,18 @@ static void endpoints_to_string(FILE *s, const struct packet *packet) get_packet_tuple(packet, &tuple); + /* get_packet_tuple() returns the tuple of the decapsulated packet. + * Here we want the tuple of the full potentially encapsulated packet, + * see if we need to use an encapsulating header's ports. Currently + * this happens only for transport-mode PSP. + */ + if (is_psp_transport_encap(packet)) { + struct udp *udp = (struct udp *)packet->psp - 1; + + tuple.src.port = udp->src_port; + tuple.dst.port = udp->dst_port; + } + fprintf(s, "%s:%u > %s:%u", ip_to_string(&tuple.src.ip, src_string), ntohs(tuple.src.port), ip_to_string(&tuple.dst.ip, dst_string), ntohs(tuple.dst.port)); @@ -141,8 +154,33 @@ static int mpls_header_to_string(FILE *s, struct packet *packet, int layer, return STATUS_OK; } +static int psp_header_to_string(FILE *s, struct packet *packet, int layer, + enum dump_format_t format, char **error) +{ + const struct psp *psp = packet->headers[layer].h.psp; + + fprintf(s, "psp ip_proto %u crypto_offset %u spi %#x", + psp->next_header, psp->crypt_offset, ntohl(psp->spi)); + + if (psp->has_vc) + fprintf(s, " vc %#lx", be64toh(psp->vc)); + + fprintf(s, ": "); + return STATUS_OK; +} + +static int udp_header_to_string(FILE *s, struct packet *packet, int layer, + enum dump_format_t format, char **error) +{ + const struct udp *udp = packet->headers[layer].h.udp; + + fprintf(s, "udp %u > %u (%u): ", ntohs(udp->src_port), + ntohs(udp->dst_port), ntohs(udp->len)); + return STATUS_OK; +} + /* Print a string representation of the TCP packet: - * direction opt_ip_info flags seq ack window tcp_options + * direction opt_ip_info opt_encapsulation flags seq ack window tcp_options */ static int tcp_packet_to_string(FILE *s, struct packet *packet, enum dump_format_t format, char **error) @@ -155,6 +193,24 @@ static int tcp_packet_to_string(FILE *s, struct packet *packet, fputc(' ', s); } + if (is_psp_transport_encap(packet)) { + /* transport-mode PSP encapsulation */ + const struct psp *psp = packet->psp; + + if (format == DUMP_SHORT) { + fprintf(s, "psp spi %#x ", ntohl(psp->spi)); + } else { + fprintf(s, "psp ip_proto %u crypto_offset %u spi %#x ", + psp->next_header, psp->crypt_offset, + ntohl(psp->spi)); + + if (psp->has_vc) + fprintf(s, "vc %#lx ", be64toh(psp->vc)); + + fprintf(s, "%u > %u ", ntohs(packet->tcp->src_port), + ntohs(packet->tcp->dst_port)); + } + } /* We print flags in the same order as tcpdump 4.1.1. */ if (packet->tcp->fin) @@ -257,6 +313,8 @@ static int encap_header_to_string(FILE *s, struct packet *packet, int layer, [HEADER_IPV6] = ipv6_header_to_string, [HEADER_GRE] = gre_header_to_string, [HEADER_MPLS] = mpls_header_to_string, + [HEADER_UDP] = udp_header_to_string, + [HEADER_PSP] = psp_header_to_string, }; header_to_string_func printer = NULL; enum header_t type = packet->headers[layer].type; @@ -279,16 +337,18 @@ int packet_to_string(struct packet *packet, FILE *s = open_memstream(ascii_string, &size); /* output string */ int i; int header_count = packet_header_count(packet); + const u8 *inner_l3_hdr = ip_start(packet); - /* Print any encapsulation headers preceding layer 3 and 4 headers. */ - for (i = 0; i < header_count - 2; ++i) { - if (packet->headers[i].type == HEADER_NONE) + /* Print any encapsulation headers preceding the innermost L3 header. */ + for (i = 0; i < header_count; ++i) { + assert(packet->headers[i].type != HEADER_NONE); + if (inner_l3_hdr && packet->headers[i].h.ptr >= inner_l3_hdr) break; if (encap_header_to_string(s, packet, i, format, error)) goto out; } - if ((packet->ipv4 == NULL) && (packet->ipv6 == NULL)) { + if (inner_l3_hdr == NULL) { fprintf(s, "[NO IP HEADER]"); } else { if (packet->tcp != NULL) { @@ -304,7 +364,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 ICMP HEADER]"); + fprintf(s, "[NO TCP, UDP OR ICMP HEADER]"); } } diff --git a/gtests/net/packetdrill/packet_to_string_test.c b/gtests/net/packetdrill/packet_to_string_test.c index 814cddbc..4db7ea7d 100644 --- a/gtests/net/packetdrill/packet_to_string_test.c +++ b/gtests/net/packetdrill/packet_to_string_test.c @@ -28,6 +28,7 @@ #include #include "assert.h" #include "packet_parser.h" +#include "psp_packet.h" static void test_tcp_ipv4_packet_to_string(void) { @@ -291,11 +292,187 @@ static void test_tcp_md5_option_to_string(void) packet_free(packet); } +static void test_tcp_ipv4_psp_ipv4_packet_to_string(void) +{ + /* An IPv4/UDP/PSP/IPv4/TCP packet. */ + u8 data[] = { + /* IPv4: */ + 0x45, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x11, 0xb5, 0x7f, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x01, + /* UDP: */ + 0xa6, 0xf7, 0x03, 0xe8, 0x00, 0x54, 0x00, 0x00, + /* PSP: */ + 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* IPv4, TCP: */ + 0x45, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x06, 0x39, 0x11, 0xc0, 0x00, 0x02, 0x01, + 0xc0, 0xa8, 0x00, 0x01, 0xcf, 0x3f, 0x1f, 0x90, + 0x00, 0x00, 0x00, 0x01, 0x83, 0x4d, 0xa5, 0x5b, + 0xa0, 0x10, 0x01, 0x01, 0xdb, 0x2d, 0x00, 0x00, + 0x05, 0x0a, 0x83, 0x4d, 0xab, 0x03, 0x83, 0x4d, + 0xb0, 0xab, 0x08, 0x0a, 0x00, 0x00, 0x01, 0x2c, + 0x60, 0xc2, 0x18, 0x20 + }; + + 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), PACKET_LAYER_3_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 = + "ipv4 2.2.2.2 > 1.1.1.1: udp 42743 > 1000 (84): " + "psp ip_proto 4 crypto_offset 0 spi 0xf1: " + ". 1:1(0) ack 2202903899 win 257 " + ""; + 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 = + "ipv4 2.2.2.2 > 1.1.1.1: udp 42743 > 1000 (84): " + "psp ip_proto 4 crypto_offset 0 spi 0xf1: " + "192.0.2.1:53055 > 192.168.0.1:8080 " + ". 1:1(0) ack 2202903899 win 257 " + ""; + 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 = + "ipv4 2.2.2.2 > 1.1.1.1: udp 42743 > 1000 (84): " + "psp ip_proto 4 crypto_offset 0 spi 0xf1: " + "192.0.2.1:53055 > 192.168.0.1:8080 " + ". 1:1(0) ack 2202903899 win 257 " + "" + "\n" + "0x0000: 45 00 00 68 00 00 00 00 ff 11 b5 7f 02 02 02 02 " "\n" + "0x0010: 01 01 01 01 a6 f7 03 e8 00 54 00 00 04 01 00 01 " "\n" + "0x0020: 00 00 00 f1 00 00 00 00 00 00 00 00 45 00 00 3c " "\n" + "0x0030: 00 00 00 00 ff 06 39 11 c0 00 02 01 c0 a8 00 01 " "\n" + "0x0040: cf 3f 1f 90 00 00 00 01 83 4d a5 5b a0 10 01 01 " "\n" + "0x0050: db 2d 00 00 05 0a 83 4d ab 03 83 4d b0 ab 08 0a " "\n" + "0x0060: 00 00 01 2c 60 c2 18 20 " "\n"; + assert(strcmp(dump, expected) == 0); + free(dump); + + packet_free(packet); +} + +static void test_tcp_psp_ipv4_packet_to_string(void) +{ + /* An IPv4/UDP/PSP/TCP packet. */ + u8 data[] = { + /* IPv4: */ + 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x11, 0x38, 0xee, 0xc0, 0x00, 0x02, 0x01, + 0xc0, 0xa8, 0x00, 0x01, + /* UDP: */ + 0xa6, 0xf7, 0x03, 0xe8, 0x00, 0x40, 0x00, 0x00, + /* PSP: */ + 0x06, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* TCP: */ + 0xcf, 0x3f, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01, + 0x83, 0x4d, 0xa5, 0x5b, 0xa0, 0x10, 0x01, 0x01, + 0xdb, 0x2d, 0x00, 0x00, 0x05, 0x0a, 0x83, 0x4d, + 0xab, 0x03, 0x83, 0x4d, 0xb0, 0xab, 0x08, 0x0a, + 0x00, 0x00, 0x01, 0x2c, 0x60, 0xc2, 0x18, 0x20 + }; + + 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), PACKET_LAYER_3_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 = + "psp spi 0xf1 . 1:1(0) ack 2202903899 win 257 " + ""; + 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 = + "192.0.2.1:42743 > 192.168.0.1:1000 " + "psp ip_proto 6 crypto_offset 0 spi 0xf1 " + "53055 > 8080 . 1:1(0) ack 2202903899 win 257 " + ""; + 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 = + "192.0.2.1:42743 > 192.168.0.1:1000 " + "psp ip_proto 6 crypto_offset 0 spi 0xf1 " + "53055 > 8080 . 1:1(0) ack 2202903899 win 257 " + "" + "\n" + "0x0000: 45 00 00 54 00 00 00 00 ff 11 38 ee c0 00 02 01 " "\n" + "0x0010: c0 a8 00 01 a6 f7 03 e8 00 40 00 00 06 01 00 01 " "\n" + "0x0020: 00 00 00 f1 00 00 00 00 00 00 00 00 cf 3f 1f 90 " "\n" + "0x0030: 00 00 00 01 83 4d a5 5b a0 10 01 01 db 2d 00 00 " "\n" + "0x0040: 05 0a 83 4d ab 03 83 4d b0 ab 08 0a 00 00 01 2c " "\n" + "0x0050: 60 c2 18 20 " "\n"; + assert(strcmp(dump, expected) == 0); + free(dump); + + packet_free(packet); +} + int main(void) { test_tcp_ipv4_packet_to_string(); test_tcp_ipv6_packet_to_string(); test_gre_mpls_tcp_ipv4_packet_to_string(); test_tcp_md5_option_to_string(); + + set_psp_port(1000); + test_tcp_ipv4_psp_ipv4_packet_to_string(); + test_tcp_psp_ipv4_packet_to_string(); return 0; } diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index 08aff3ea..32d79db5 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -97,6 +97,7 @@ #include "logging.h" #include "mpls.h" #include "mpls_packet.h" +#include "psp_packet.h" #include "tcp_packet.h" #include "udp_packet.h" #include "parse.h" @@ -480,6 +481,15 @@ static struct packet *append_gre(struct packet *packet, struct expression *expr) return packet; } +static struct packet *append_udp_psp(struct packet *packet, struct psp *psp) +{ + char *error = NULL; + if (psp_encapsulate(packet, psp, in_config->psp_udp_port, &error)) + semantic_error(error); + free(psp); + return packet; +} + %} %locations @@ -499,6 +509,7 @@ static struct packet *append_gre(struct packet *packet, struct expression *expr) struct ip_info ip_info; struct mpls_stack *mpls_stack; struct mpls mpls_stack_entry; + struct psp *psp; u16 port; s32 window; u16 urg_ptr; @@ -523,6 +534,10 @@ static struct packet *append_gre(struct packet *packet, struct expression *expr) u16 src_port; u16 dst_port; } port_info; + struct { + bool supplied; + s64 value; + } optional_integer; } /* The specific type of the output for a symbol is given by the %type @@ -544,6 +559,7 @@ static struct packet *append_gre(struct packet *packet, struct expression *expr) %token OPTION %token SUM OFF KEY SEQ %token NONE CHECKSUM SEQUENCE PRESENT +%token PSP SPI VC VNID ENCAP %token EE_ERRNO EE_CODE EE_DATA EE_INFO EE_ORIGIN EE_TYPE %token SCM_SEC SCM_NSEC %token FLOAT @@ -569,6 +585,8 @@ static struct packet *append_gre(struct packet *packet, struct expression *expr) %type gre_sum gre_off gre_key gre_seq %type opt_icmp_echo_id %type flow_label +%type psp_spi +%type opt_psp_vc %type icmp_type opt_icmp_code opt_ack_flag opt_word ack_and_ace flags %type opt_tcp_fast_open_cookie hex_blob %type opt_note note word_list @@ -591,9 +609,11 @@ static struct packet *append_gre(struct packet *packet, struct expression *expr) %type sock_extended_err_expr %type mpls_stack_expression %type gre_header_expression +%type psp_tuple %type epollev %type opt_errno %type opt_port_info +%type psp_header_info opt_encap %% /* The grammar follows. */ @@ -784,28 +804,31 @@ packet_spec ; tcp_packet_spec -: packet_prefix opt_ip_info opt_port_info flags seq opt_ack opt_window opt_urg_ptr opt_tcp_options { +: packet_prefix opt_ip_info opt_encap opt_port_info flags seq opt_ack opt_window opt_urg_ptr opt_tcp_options { char *error = NULL; struct packet *outer = $1, *inner = NULL; enum direction_t direction = outer->direction; + struct psp *psp = $3; if (($2.tos.check == TOS_CHECK_ECN_ECT01) && (direction != DIRECTION_OUTBOUND)) { semantic_error("[ect01] can only be used with outbound packets"); } - if (($9 == NULL) && (direction != DIRECTION_OUTBOUND)) { - yylineno = @7.first_line; + if (($10 == NULL) && (direction != DIRECTION_OUTBOUND)) { + yylineno = @10.first_line; semantic_error("<...> for TCP options can only be used with " "outbound packets"); } inner = new_tcp_packet(in_config->wire_protocol, - direction, $2, $3.src_port, $3.dst_port, $4, - $5.start_sequence, $5.payload_bytes, - $6, $7, $8, $9, &error); - free($4); - free($9); + direction, $2, psp, in_config->psp_udp_port, + $4.src_port, $4.dst_port, $5, + $6.start_sequence, $6.payload_bytes, + $7, $8, $9, $10, &error); + free($3); + free($5); + free($10); if (inner == NULL) { assert(error != NULL); semantic_error(error); @@ -920,6 +943,11 @@ packet_prefix free(mpls_stack); $$ = packet; } +| packet_prefix PSP psp_header_info ':' { + struct packet *packet = $1; + struct psp *psp = $3; + $$ = append_udp_psp(packet, psp); +} ; gre_header_expression @@ -1018,6 +1046,41 @@ opt_mpls_stack_bottom } ; +psp_header_info +: psp_spi opt_psp_vc { + $$ = psp_new(); + $$->flags = 1; + $$->ext_len = 1; + $$->crypt_offset = 1; + if ($2.supplied) { + $$->has_vc = 1; + $$->vc = htobe64($2.value); + $$->ext_len++; + $$->crypt_offset += 2; + } + $$->spi = htonl($1); +} +; + +psp_spi +: SPI any_int { $$ = $2->value.num; } +; + +opt_psp_vc +: { + $$.supplied = false; + $$.value = 0; +} +| opt_comma VC any_int { + $$.supplied = true; + $$.value = $3->value.num; +} +| opt_comma VNID any_int { + $$.supplied = true; + $$.value = $3->value.num; +} +; + icmp_type : WORD { $$ = $1; } ; @@ -1183,6 +1246,11 @@ opt_ip_info | '[' ip_info ']' { $$ = $2; } ; +opt_encap +: { $$ = NULL; } +| ENCAP PSP psp_header_info { $$ = $3; } +; + seq : INTEGER ':' INTEGER '(' INTEGER ')' { if (!is_valid_u32($1)) { @@ -1464,6 +1532,9 @@ expression | epollev { $$ = $1; } +| psp_tuple { + $$ = $1; +} ; any_int @@ -1736,6 +1807,13 @@ mpls_stack_expression } ; +psp_tuple +: '{' SPI '=' any_int '}' { + $$ = new_expression(EXPR_PSP); + $$->value.psp_tuple.spi = $4->value.num; +} +; + opt_errno : { $$ = NULL; } | WORD note { diff --git a/gtests/net/packetdrill/platforms.h b/gtests/net/packetdrill/platforms.h index b61aad60..d35a9cfd 100644 --- a/gtests/net/packetdrill/platforms.h +++ b/gtests/net/packetdrill/platforms.h @@ -30,6 +30,7 @@ #ifdef linux +#include #include #include @@ -44,6 +45,8 @@ #else +#include + /* The underscore variants of the kernel-style names are defined in * linux/types.h, but we must define them explicitly for other platforms. */ typedef u8 __u8; diff --git a/gtests/net/packetdrill/psp.h b/gtests/net/packetdrill/psp.h new file mode 100644 index 00000000..bf29f753 --- /dev/null +++ b/gtests/net/packetdrill/psp.h @@ -0,0 +1,86 @@ +/* + * Copyright 2017 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: dmichail@google.com (Dimitris Michailidis) + * + * Our own PSP header declarations, so we have something that's + * portable and somewhat more readable than a typical system header + * file. + * + * We cannot include the kernel's PSP .h files because this tool tries + * to compile and work for basically any Linux/BSD kernel version. So + * we declare our own version of various PSP-related definitions here. + */ + +#ifndef __PSP_HEADERS_H__ +#define __PSP_HEADERS_H__ + +#include "types.h" + +#define PSP_MINLEN 16 + +struct psp { + __u8 next_header; + __u8 ext_len; + __u8 crypt_offset; + union { + __u8 flags; + struct { +#if __BYTE_ORDER == __LITTLE_ENDIAN + __u8 one:1, + has_vc:1, + version:4, + reserved:2; +#elif __BYTE_ORDER == __BIG_ENDIAN + __u8 reserved:2, + version:4, + has_vc:1, + one:1; +#else +# error "Please fix endianness defines" +#endif + }; + }; + __be32 spi; + __be64 iv; + __be64 vc; +}; + +/* Return the length in bytes of a PSP header. */ +static inline int psp_len(const struct psp *psp) +{ + return (psp->ext_len + 1) * 8; +} + +/* Allocate and 0-initialize a new PSP header. */ +static inline struct psp *psp_new(void) +{ + return calloc(1, sizeof(struct psp)); +} + +/* This structure is used with the PSP [gs]etsockopts to obtain/set PSP keys + * and SPIs. See include/uapi/linux/psp.h in Google kernels. + */ +struct psp_spi_tuple { + __u8 key[16]; + __u32 key_gen; + __u32 spi; +}; + +#endif /* __PSP_HEADERS_H__ */ diff --git a/gtests/net/packetdrill/psp_packet.c b/gtests/net/packetdrill/psp_packet.c new file mode 100644 index 00000000..3050bb5d --- /dev/null +++ b/gtests/net/packetdrill/psp_packet.c @@ -0,0 +1,87 @@ +/* + * Copyright 2017 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: dmichail@google.com (Dimitris Michailidis) + * + * Implementation for module for formatting PSP-encapsulated packets. + */ + +#include "psp_packet.h" +#include "udp_packet.h" + +static u16 psp_udp_port; + +void set_psp_port(u16 port) +{ + psp_udp_port = port; +} + +bool is_psp_port(u16 port) +{ + return psp_udp_port && port == psp_udp_port; +} + +static int psp_header_append(struct packet *packet, const struct psp *psp, + char **error) +{ + struct header *header; + + header = packet_append_header(packet, HEADER_PSP, psp_len(psp)); + if (header == NULL) { + asprintf(error, "too many headers"); + return STATUS_ERR; + } + + memcpy(header->h.psp, psp, header->header_bytes); + packet->psp = header->h.psp; + + return STATUS_OK; +} + +int psp_encapsulate(struct packet *packet, const struct psp *psp, u16 udp_dport, + char **error) +{ + int ret; + + if (!udp_dport) { + asprintf(error, + "attempting to use PSP without specifying its UDP port"); + return STATUS_ERR; + } + + assert(!psp_udp_port || psp_udp_port == udp_dport); + psp_udp_port = udp_dport; + + /* for now use the dport also as sport */ + ret = udp_header_append(packet, psp_udp_port, psp_udp_port, error); + if (ret == STATUS_OK) + ret = psp_header_append(packet, psp, error); + + return ret; +} + +int psp_header_finish(struct packet *packet, + struct header *header, struct header *next_inner) +{ + struct psp *psp = header->h.psp; + + psp->next_header = header_type_info(next_inner->type)->ip_proto; + header->total_bytes = header->header_bytes + next_inner->total_bytes; + return STATUS_OK; +} diff --git a/gtests/net/packetdrill/psp_packet.h b/gtests/net/packetdrill/psp_packet.h new file mode 100644 index 00000000..fb41c275 --- /dev/null +++ b/gtests/net/packetdrill/psp_packet.h @@ -0,0 +1,74 @@ +/* + * Copyright 2017 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: dmichail@google.com (Dimitris Michailidis) + * + * Interface for module for formatting PSP-encapsulated packets. + */ + +#ifndef __PSP_PACKET_H__ +#define __PSP_PACKET_H__ + +#include "packet.h" + +/* Return true if a packet has transport-mode PSP encapsulation. */ +static inline bool is_psp_transport_encap(const struct packet *packet) +{ + /* There are three possibilities: + * + * ->psp == NULL: no PSP encapsulation + * ->psp != NULL && < ip_start: tunnel-mode PSP + * ->psp > ip_start: transport-mode PSP + */ + return (u8 *)packet->psp > ip_start(packet); +} + +/* Return the packet length overhead for encapsulation with the given + * PSP parameters. + */ +static inline unsigned int psp_encap_header_bytes(const struct psp *psp) +{ + return psp == NULL ? 0 : sizeof(struct udp) + psp_len(psp); +} + +/* PSP-encapsulate the given packet by appending a UDP and a PSP header to it. + * The PSP header is provided explicitly while the UDP header is mostly + * implicit except for its destination port. + * + * On success return STATUS_OK; on error return STATUS_ERR and fill in a + * malloc-allocated error message in *error. + */ +extern int psp_encapsulate(struct packet *packet, const struct psp *psp, + u16 udp_dport, char **error); + +/* Finalize the PSP header by filling in all necessary fields that + * were not filled in at parse time. + */ +extern int psp_header_finish(struct packet *packet, + struct header *header, struct header *next_inner); + +/* Return true if the supplied port is PSP's UDP port. */ +extern bool is_psp_port(u16 port); + +/* Explicitly set PSP's UDP port. Intended for use only by packet parser tests + * that build their own packets, packetdrill itself doesn't need this. + */ +extern void set_psp_port(u16 port); + +#endif /* __PSP_PACKET_H__ */ diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c index da0e6b67..b7e24be2 100644 --- a/gtests/net/packetdrill/run_packet.c +++ b/gtests/net/packetdrill/run_packet.c @@ -185,6 +185,7 @@ static struct socket *handle_listen_for_script_packet( */ struct config *config = state->config; struct socket *socket = state->socket_under_test; /* shortcut */ + struct socket *parent = socket; bool match = (direction == DIRECTION_INBOUND); if (!match) @@ -215,7 +216,11 @@ static struct socket *handle_listen_for_script_packet( assert(socket->state == SOCKET_INIT); socket->state = SOCKET_PASSIVE_PACKET_RECEIVED; socket->address_family = packet_address_family(packet); - socket->protocol = packet_ip_protocol(packet); + socket->protocol = IPPROTO_TCP; + + /* PSP SPI offset is inherited from the parent, if it exists */ + if (parent != NULL) + socket->psp_rx_spi_offset = parent->psp_rx_spi_offset; /* Set script info for this socket using script packet. */ struct tuple tuple; @@ -609,6 +614,11 @@ static int map_inbound_packet( live_packet->mss = state->config->mss; + if (live_packet->psp != NULL) { + live_packet->psp->spi = htonl(ntohl(live_packet->psp->spi) + + socket->psp_rx_spi_offset); + } + if ((live_packet->icmpv4 != NULL) || (live_packet->icmpv6 != NULL)) return map_inbound_icmp_packet(socket, live_packet, error); @@ -1090,6 +1100,37 @@ static int verify_gre( return STATUS_OK; } +/* Verify that required actual PSP header fields are as the script expected. */ +static int verify_psp( + const struct packet *actual_packet, + const struct packet *script_packet, + int layer, bool strict, char **error) +{ + const struct psp *actual_psp = actual_packet->headers[layer].h.psp; + const struct psp *script_psp = script_packet->headers[layer].h.psp; + + if (check_field("psp_ext_len", script_psp->ext_len, actual_psp->ext_len, + error)) + return STATUS_ERR; + if (check_field("psp_next_header", script_psp->next_header, + actual_psp->next_header, error)) + return STATUS_ERR; + if (check_field("psp_crypto_offset", script_psp->crypt_offset, + actual_psp->crypt_offset, error)) + return STATUS_ERR; + if (check_field("psp_flags", script_psp->flags, actual_psp->flags, + error)) + return STATUS_ERR; + if (check_field("psp_spi", ntohl(script_psp->spi), + ntohl(actual_psp->spi), error)) + return STATUS_ERR; + if (script_psp->has_vc && script_psp->vc != actual_psp->vc) { + asprintf(error, "mismatch in PSP VC"); + return STATUS_ERR; + } + return STATUS_OK; +} + /* Verify that required actual MPLS header fields are as the script expected. */ static int verify_mpls( const struct packet *actual_packet, @@ -1180,6 +1221,7 @@ static int verify_header( [HEADER_UDP] = verify_udp, [HEADER_ICMPV4] = verify_icmpv4, [HEADER_ICMPV6] = verify_icmpv6, + [HEADER_PSP] = verify_psp, }; verifier_func verifier = NULL; const struct header *actual_header = &actual_packet->headers[layer]; @@ -2087,7 +2129,7 @@ int reset_connection(struct state *state, struct socket *socket) } packet = new_tcp_packet(socket->address_family, - DIRECTION_INBOUND, ip_info, 0, 0, + DIRECTION_INBOUND, ip_info, NULL, 0, 0, 0, "R.", seq, 0, ack_seq, window, 0, NULL, &error); if (packet == NULL) diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c index ebfff192..39b41c16 100644 --- a/gtests/net/packetdrill/run_system_call.c +++ b/gtests/net/packetdrill/run_system_call.c @@ -2555,6 +2555,41 @@ static int syscall_shutdown(struct state *state, struct syscall_spec *syscall, return STATUS_OK; } +/* Return a pointer to a socket option value held in args[index]. + * Return NULL in case of error. If args[index] is a bracketed expression the + * nested value is returned in optval_s32. + */ +static void *get_sockopt_optval(struct expression_list *args, int index, + s32 *optval_s32, char **error) +{ + struct expression *val_expression = get_arg(args, 3, error); + + if (val_expression == NULL) + return NULL; + + if (val_expression->type == EXPR_LINGER) + return &val_expression->value.linger; + if (val_expression->type == EXPR_GRE) + return &val_expression->value.gre; + if (val_expression->type == EXPR_IN6_ADDR) + return &val_expression->value.address_ipv6; + if (val_expression->type == EXPR_MPLS_STACK) + return val_expression->value.mpls_stack; + if (val_expression->type == EXPR_PSP) + return &val_expression->value.psp_tuple; + if (val_expression->type == EXPR_STRING) + return val_expression->value.buf.ptr; + if (val_expression->type == EXPR_LIST) { + if (s32_bracketed_arg(args, index, optval_s32, error)) + return NULL; + return optval_s32; + } + + asprintf(error, "unsupported [gs]etsockopt value type: %s", + expression_type_to_string(val_expression->type)); + return NULL; +} + static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall, struct expression_list *args, char **error) { @@ -2587,6 +2622,16 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall, live_optval = calloc(1, live_optlen + 1); assert(live_optval != NULL); + /* TCP_PSP_LISTENER is an unusual sockopt that also sets state */ + if (optname == TCP_PSP_LISTENER) { + void *optval; + + optval = get_sockopt_optval(args, 3, &script_optval_s32, error); + if (optval == NULL) + goto error_out; + memcpy(live_optval, optval, live_optlen); + } + begin_syscall(state, syscall); if (state->so_instance) { @@ -2695,6 +2740,15 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall, script_buf, live_buf); goto error_out; } + } else if (val_expression->type == EXPR_PSP) { + struct socket *socket; + const struct psp_spi_tuple *live_tuple = live_optval; + u32 live_rx_spi = live_tuple->spi; + u32 script_rx_spi = val_expression->value.psp_tuple.spi; + + socket = fd_to_socket(find_by_live_fd(state, live_fd)); + assert(socket != NULL); + socket->psp_rx_spi_offset = live_rx_spi - script_rx_spi; } else { asprintf(error, "unsupported getsockopt value type: %s", expression_type_to_string( @@ -2716,7 +2770,6 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall, { int script_fd, live_fd, level, optname, optval_s32, optlen, result; void *optval = NULL; - struct expression *val_expression; if (check_arg_count(args, 5, error)) return STATUS_ERR; @@ -2731,29 +2784,9 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall, if (s32_arg(args, 4, &optlen, error)) return STATUS_ERR; - val_expression = get_arg(args, 3, error); - if (val_expression == NULL) - return STATUS_ERR; - if (val_expression->type == EXPR_LINGER) { - optval = &val_expression->value.linger; - } else if (val_expression->type == EXPR_GRE) { - optval = &val_expression->value.gre; - } else if (val_expression->type == EXPR_IN6_ADDR) { - optval = &val_expression->value.address_ipv6; - } else if (val_expression->type == EXPR_MPLS_STACK) { - optval = val_expression->value.mpls_stack; - } else if (val_expression->type == EXPR_STRING) { - optval = val_expression->value.buf.ptr; - } else if (val_expression->type == EXPR_LIST) { - if (s32_bracketed_arg(args, 3, &optval_s32, error)) - return STATUS_ERR; - optval = &optval_s32; - } else { - asprintf(error, "unsupported setsockopt value type: %s", - expression_type_to_string( - val_expression->type)); + optval = get_sockopt_optval(args, 3, &optval_s32, error); + if (optval == NULL) return STATUS_ERR; - } begin_syscall(state, syscall); diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c index b7137134..55bb7709 100644 --- a/gtests/net/packetdrill/script.c +++ b/gtests/net/packetdrill/script.c @@ -72,6 +72,7 @@ struct expression_type_entry expression_type_table[] = { { EXPR_SCM_TIMESTAMPING, "scm_timestamping"}, { EXPR_SOCK_EXTENDED_ERR, "sock_extended_err"}, { EXPR_EPOLLEV, "epollev" }, + { EXPR_PSP, "psp" }, {-1, NULL} }; @@ -300,6 +301,7 @@ void free_expression(struct expression *expression) case EXPR_ELLIPSIS: case EXPR_INTEGER: case EXPR_GRE: + case EXPR_PSP: case EXPR_IN6_ADDR: case EXPR_LINGER: break; @@ -743,6 +745,9 @@ static int evaluate(struct expression *in, case EXPR_EPOLLEV: result = evaluate_epollev_expression(in, out, error); break; + case EXPR_PSP: /* copy as-is */ + out->value.psp_tuple = in->value.psp_tuple; + break; case NUM_EXPR_TYPES: break; /* missing default case so compiler catches missing cases */ diff --git a/gtests/net/packetdrill/script.h b/gtests/net/packetdrill/script.h index e2939be3..e97efeb2 100644 --- a/gtests/net/packetdrill/script.h +++ b/gtests/net/packetdrill/script.h @@ -53,6 +53,7 @@ enum expression_t { EXPR_SCM_TIMESTAMPING, /* scm_timestamping expression */ EXPR_SOCK_EXTENDED_ERR, /* scm_sock_extended_err expression */ EXPR_EPOLLEV, /* expression tree for a epoll_event struct */ + EXPR_PSP, /* PSP tuple */ NUM_EXPR_TYPES, }; /* Convert an expression type to a human-readable string */ @@ -85,6 +86,7 @@ struct expression { struct scm_timestamping_expr *scm_timestamping; struct sock_extended_err_expr *sock_extended_err; struct epollev_expr *epollev; + struct psp_spi_tuple psp_tuple; } value; const char *format; /* the printf format for printing the value */ }; diff --git a/gtests/net/packetdrill/socket.h b/gtests/net/packetdrill/socket.h index 55e43b03..3a3224bb 100644 --- a/gtests/net/packetdrill/socket.h +++ b/gtests/net/packetdrill/socket.h @@ -117,6 +117,12 @@ struct socket { /* flowlabel mapping */ struct flowlabel_map flowlabel_map; + + /* Difference between the live and script Rx SPI values for a + * PSP-encapsulated socket. We learn this value by looking at the + * PSP socket options that set Rx SPIs. + */ + u32 psp_rx_spi_offset; }; /* Convert to socket pointer if the fd is a socket, otherwise return NULL. */ @@ -173,7 +179,7 @@ static inline void reverse_tuple(const struct tuple *src_tuple, dst_tuple->dst.port = src_tuple->src.port; } -/* Get the tuple for a packet. */ +/* Get the tuple for a packet ignoring any encapsulations. */ static inline void get_packet_tuple(const struct packet *packet, struct tuple *tuple) { diff --git a/gtests/net/packetdrill/symbols_linux.c b/gtests/net/packetdrill/symbols_linux.c index 16551438..ffb6b099 100644 --- a/gtests/net/packetdrill/symbols_linux.c +++ b/gtests/net/packetdrill/symbols_linux.c @@ -197,6 +197,10 @@ struct int_symbol platform_symbols_table[] = { { TCP_CM_INQ, "TCP_CM_INQ" }, { TCP_TX_DELAY, "TCP_TX_DELAY" }, + { TCP_PSP_TX_SPI_KEY, "TCP_PSP_TX_SPI_KEY" }, + { TCP_PSP_RX_SPI_KEY, "TCP_PSP_RX_SPI_KEY" }, + { TCP_PSP_LISTENER, "TCP_PSP_LISTENER" }, + { O_RDONLY, "O_RDONLY" }, { O_WRONLY, "O_WRONLY" }, { O_RDWR, "O_RDWR" }, diff --git a/gtests/net/packetdrill/tcp.h b/gtests/net/packetdrill/tcp.h index d95ba449..a5a5e449 100644 --- a/gtests/net/packetdrill/tcp.h +++ b/gtests/net/packetdrill/tcp.h @@ -81,6 +81,11 @@ #endif /* linux */ +/* TCP PSP draft APIs. These are unlikely to become upstream ABI as is */ +#define TCP_PSP_TX_SPI_KEY 1730 /* Set PSP Tx SPI and key */ +#define TCP_PSP_RX_SPI_KEY 1731 /* Get PSP Rx SPI and key */ +#define TCP_PSP_LISTENER 1732 /* Get/set PSP Tx/Rx SPI and key */ + /* New TCP flags for sendto(2)/sendmsg(2). */ #ifndef MSG_FASTOPEN #define MSG_FASTOPEN 0x20000000 /* TCP Fast Open: data in SYN */ diff --git a/gtests/net/packetdrill/tcp_packet.c b/gtests/net/packetdrill/tcp_packet.c index d1ef65b5..28b52c3c 100644 --- a/gtests/net/packetdrill/tcp_packet.c +++ b/gtests/net/packetdrill/tcp_packet.c @@ -25,6 +25,7 @@ #include "tcp_packet.h" #include "ip_packet.h" +#include "psp_packet.h" #include "tcp.h" /* @@ -91,6 +92,8 @@ static inline int tcp_flag_ace_count(const char *flags) struct packet *new_tcp_packet(int address_family, enum direction_t direction, struct ip_info ip_info, + const struct psp *psp, + u16 psp_port, u16 src_port, u16 dst_port, const char *flags, @@ -109,9 +112,11 @@ struct packet *new_tcp_packet(int address_family, const int tcp_option_bytes = tcp_options ? tcp_options->length : 0; const int ip_header_bytes = (ip_header_min_len(address_family) + ip_option_bytes); + const unsigned int encap_header_bytes = psp_encap_header_bytes(psp); const int tcp_header_bytes = sizeof(struct tcp) + tcp_option_bytes; const int ip_bytes = - ip_header_bytes + tcp_header_bytes + tcp_payload_bytes; + ip_header_bytes + encap_header_bytes + tcp_header_bytes + + tcp_payload_bytes; int ace; /* Sanity-check all the various lengths */ @@ -155,13 +160,20 @@ struct packet *new_tcp_packet(int address_family, /* Set IP header fields */ set_packet_ip_header(packet, address_family, ip_bytes, ip_info.tos.value, ip_info.flow_label, - ip_info.ttl, IPPROTO_TCP); + ip_info.ttl, + psp == NULL ? IPPROTO_TCP : IPPROTO_UDP); + + /* For transport-mode PSP the UDP/PSP encapsulation headers sit + * between the L3 and TCP headers. + */ + if (psp != NULL && psp_encapsulate(packet, psp, psp_port, error)) + return NULL; tcp_header = packet_append_header(packet, HEADER_TCP, tcp_header_bytes); tcp_header->total_bytes = tcp_header_bytes + tcp_payload_bytes; /* Find the start of TCP sections of the packet */ - packet->tcp = (struct tcp *) (ip_start(packet) + ip_header_bytes); + packet->tcp = tcp_header->h.tcp; u8 *tcp_option_start = (u8 *) (packet->tcp + 1); /* Set TCP header fields */ diff --git a/gtests/net/packetdrill/tcp_packet.h b/gtests/net/packetdrill/tcp_packet.h index 6e2a6b08..41cf0b44 100644 --- a/gtests/net/packetdrill/tcp_packet.h +++ b/gtests/net/packetdrill/tcp_packet.h @@ -38,6 +38,8 @@ extern struct packet *new_tcp_packet(int address_family, enum direction_t direction, struct ip_info ip_info, + const struct psp *psp, + u16 psp_port, u16 src_port, u16 dst_port, const char *flags, diff --git a/gtests/net/packetdrill/tests/encap-injection-psp-v6.pkt b/gtests/net/packetdrill/tests/encap-injection-psp-v6.pkt new file mode 100644 index 00000000..840962e5 --- /dev/null +++ b/gtests/net/packetdrill/tests/encap-injection-psp-v6.pkt @@ -0,0 +1,35 @@ +// Verify that we can inject inbound encapsulated packets with PSP. +// Uses IPv6 inner packets. + +--ip_version=ipv6 +--psp_udp_port=1000 + +// Initialize connection +0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +// IPv4... + +// tunnel-mode PSP encap ++0 < ipv4 2.2.2.2 > 1.1.1.1 : psp spi 12: . 1:1001(1000) ack 1 win 123 + +// tunnel-mode PSP encap with VC ++0 < ipv4 2.2.2.2 > 1.1.1.1 : psp spi 0x684abfc1, vc 0x123456789abcdef: . 1:1001(1000) ack 1 win 123 + +// IPv6... + +// tunnel-mode PSP encap ++0 < ipv6 2::2222 > 1::1111 : psp spi 12: . 1:1001(1000) ack 1 win 123 + +// tunnel-mode PSP encap with VC ++0 < ipv6 2::2222 > 1::1111 : psp spi 0x684abfc1, vc 0x123456789abcdef: . 1:1001(1000) ack 1 win 123 + +// The following encapsulations do not add L3 headers. + +// transport-mode PSP encap ++0 < encap psp spi 12 . 1:1001(1000) ack 1 win 123 + +// transport-mode PSP encap with VC ++0 < encap psp spi 0x684abfc1, vc 0x123456789abcdef . 1:1001(1000) ack 1 win 123 diff --git a/gtests/net/packetdrill/tests/encap-injection-psp.pkt b/gtests/net/packetdrill/tests/encap-injection-psp.pkt new file mode 100644 index 00000000..cc68ed9f --- /dev/null +++ b/gtests/net/packetdrill/tests/encap-injection-psp.pkt @@ -0,0 +1,33 @@ +// Verify that we can inject inbound encapsulated packets with PSP. + +--psp_udp_port=1000 + +// Initialize connection +0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +// IPv4... + +// tunnel-mode PSP encap ++0 < ipv4 2.2.2.2 > 1.1.1.1 : psp spi 12: . 1:1001(1000) ack 1 win 123 + +// tunnel-mode PSP encap with VC ++0 < ipv4 2.2.2.2 > 1.1.1.1 : psp spi 0x684abfc1, vc 0x123456789abcdef: . 1:1001(1000) ack 1 win 123 + +// IPv6... + +// tunnel-mode PSP encap ++0 < ipv6 2::2222 > 1::1111 : psp spi 12: . 1:1001(1000) ack 1 win 123 + +// tunnel-mode PSP encap with VC ++0 < ipv6 2::2222 > 1::1111 : psp spi 0x684abfc1, vc 0x123456789abcdef: . 1:1001(1000) ack 1 win 123 + +// The following encapsulations do not add L3 headers. + +// transport-mode PSP encap ++0 < encap psp spi 12 . 1:1001(1000) ack 1 win 123 + +// transport-mode PSP encap with VC ++0 < encap psp spi 0x684abfc1, vc 0x123456789abcdef . 1:1001(1000) ack 1 win 123 diff --git a/gtests/net/packetdrill/tests/encap-injection-v6.pkt b/gtests/net/packetdrill/tests/encap-injection-v6.pkt new file mode 100644 index 00000000..a7258e27 --- /dev/null +++ b/gtests/net/packetdrill/tests/encap-injection-v6.pkt @@ -0,0 +1,60 @@ +// Verify that we can inject inbound encapsulated packets. +// Uses IPv6 inner packets. + +--ip_version=ipv6 + +// Initialize connection +0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +// IPv4... + +// GRE encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: . 1:1001(1000) ack 1 win 123 + +// GRE plus 1-entry MPLS encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: + mpls (label 123, tc 1, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// GRE plus 2-entry MPLS encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: + mpls (label 0, tc 0, ttl 0) (label 1048575, tc 7, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// double GRE encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: ipv4 2.2.2.2 > 1.1.1.1: gre: . 1:1001(1000) ack 1 win 123 + +// IPIP encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: . 1:1001(1000) ack 1 win 123 + +// double IPIP encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: ipv4 2.2.2.2 > 1.1.1.1: . 1:1001(1000) ack 1 win 123 + +// GRE encap with GRE header fields specified ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre flags 0xb000, sum 511, off 1023, key 0x80001234, seq 512: . 1:1001(1000) ack 1 win 123 + +// IPv6... + +// GRE encap ++0 < ipv6 2::2222 > 1::1111: gre: . 1:1001(1000) ack 1 win 123 + +// GRE plus 1-entry MPLS encap ++0 < ipv6 2::2222 > 1::1111: gre: + mpls (label 123, tc 1, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// GRE plus 2-entry MPLS encap ++0 < ipv6 2::2222 > 1::1111: gre: + mpls (label 0, tc 0, ttl 0) (label 1048575, tc 7, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// double GRE encap ++0 < ipv6 2::2222 > 1::1111: gre: ipv6 2::2222 > 1::1111: gre: . 1:1001(1000) ack 1 win 123 + +// IPIP encap ++0 < ipv6 2::2222 > 1::1111: . 1:1001(1000) ack 1 win 123 + +// double IPIP encap ++0 < ipv6 2::2222 > 1::1111: ipv6 2::2222 > 1::1111: . 1:1001(1000) ack 1 win 123 + +// GRE encap with GRE header fields specified ++0 < ipv6 2::2222 > 1::1111: gre flags 0xb000, sum 511, off 1023, key 0x80001234, seq 512: . 1:1001(1000) ack 1 win 123 diff --git a/gtests/net/packetdrill/tests/encap-injection.pkt b/gtests/net/packetdrill/tests/encap-injection.pkt new file mode 100644 index 00000000..bf7f9b5f --- /dev/null +++ b/gtests/net/packetdrill/tests/encap-injection.pkt @@ -0,0 +1,57 @@ +// Verify that we can inject inbound encapsulated packets. + +// Initialize connection +0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +// IPv4... + +// GRE encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: . 1:1001(1000) ack 1 win 123 + +// GRE plus 1-entry MPLS encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: + mpls (label 123, tc 1, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// GRE plus 2-entry MPLS encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: + mpls (label 0, tc 0, ttl 0) (label 1048575, tc 7, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// double GRE encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: ipv4 2.2.2.2 > 1.1.1.1: gre: . 1:1001(1000) ack 1 win 123 + +// IPIP encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: . 1:1001(1000) ack 1 win 123 + +// double IPIP encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: ipv4 2.2.2.2 > 1.1.1.1: . 1:1001(1000) ack 1 win 123 + +// GRE encap with GRE header fields specified ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre flags 0xb000, sum 511, off 1023, key 0x80001234, seq 512: . 1:1001(1000) ack 1 win 123 + +// IPv6... + +// GRE encap ++0 < ipv6 2::2222 > 1::1111: gre: . 1:1001(1000) ack 1 win 123 + +// GRE plus 1-entry MPLS encap ++0 < ipv6 2::2222 > 1::1111: gre: + mpls (label 123, tc 1, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// GRE plus 2-entry MPLS encap ++0 < ipv6 2::2222 > 1::1111: gre: + mpls (label 0, tc 0, ttl 0) (label 1048575, tc 7, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// double GRE encap ++0 < ipv6 2::2222 > 1::1111: gre: ipv6 2::2222 > 1::1111: gre: . 1:1001(1000) ack 1 win 123 + +// IPIP encap ++0 < ipv6 2::2222 > 1::1111: . 1:1001(1000) ack 1 win 123 + +// double IPIP encap ++0 < ipv6 2::2222 > 1::1111: ipv6 2::2222 > 1::1111: . 1:1001(1000) ack 1 win 123 + +// GRE encap with GRE header fields specified ++0 < ipv6 2::2222 > 1::1111: gre flags 0xb000, sum 511, off 1023, key 0x80001234, seq 512: . 1:1001(1000) ack 1 win 123 diff --git a/gtests/net/packetdrill/tests/encap-syntax-psp.pkt b/gtests/net/packetdrill/tests/encap-syntax-psp.pkt new file mode 100644 index 00000000..25accf01 --- /dev/null +++ b/gtests/net/packetdrill/tests/encap-syntax-psp.pkt @@ -0,0 +1,37 @@ +// Verify that we can parse inbound and outbound encapsulated packets. +// This script tests PSP (Google-internal). +// This is a separate file so that we can keep encap-syntax.pkt passing +// on both internal and upstream packetdrill. + +--dry_run // just parse, don't run +--psp_udp_port=1000 + +// IPv4... + +// tunnel-mode PSP encap ++0 < ipv4 2.2.2.2 > 1.1.1.1 : psp spi 12: . 1:1001(1000) ack 1 win 123 ++0 > ipv4 2.2.2.2 > 1.1.1.1 : psp spi 12: . 1:1001(1000) ack 1 win 123 + +// tunnel-mode PSP encap with VC ++0 < ipv4 2.2.2.2 > 1.1.1.1 : psp spi 0x684abfc1, vc 0x123456789abcdef: . 1:1001(1000) ack 1 win 123 ++0 > ipv4 2.2.2.2 > 1.1.1.1 : psp spi 0x684abfc1, vc 0x123456789abcdef: . 1:1001(1000) ack 1 win 123 + +// IPv6... + +// tunnel-mode PSP encap ++0 < ipv6 2::2222 > 1::1111 : psp spi 12: . 1:1001(1000) ack 1 win 123 ++0 > ipv6 2::2222 > 1::1111 : psp spi 12: . 1:1001(1000) ack 1 win 123 + +// tunnel-mode PSP encap with VC ++0 < ipv6 2::2222 > 1::1111 : psp spi 0x684abfc1, vc 0x123456789abcdef: . 1:1001(1000) ack 1 win 123 ++0 > ipv6 2::2222 > 1::1111 : psp spi 0x684abfc1, vc 0x123456789abcdef: . 1:1001(1000) ack 1 win 123 + +// The following encapsulations do not add L3 headers. + +// transport-mode PSP encap ++0 < encap psp spi 12 . 1:1001(1000) ack 1 win 123 ++0 > encap psp spi 12 . 1:1001(1000) ack 1 win 123 + +// transport-mode PSP encap with VC ++0 < encap psp spi 0x684abfc1, vc 0x123456789abcdef . 1:1001(1000) ack 1 win 123 ++0 > encap psp spi 0x684abfc1, vc 0x123456789abcdef . 1:1001(1000) ack 1 win 123 diff --git a/gtests/net/packetdrill/tests/encap-syntax.pkt b/gtests/net/packetdrill/tests/encap-syntax.pkt new file mode 100644 index 00000000..46b0292e --- /dev/null +++ b/gtests/net/packetdrill/tests/encap-syntax.pkt @@ -0,0 +1,70 @@ +// Verify that we can parse inbound and outbound encapsulated packets. +// Try to keep this script passing on all versions of packetdrill +// (upstream and local). + +--dry_run // just parse, don't run + +// IPv4... + +// GRE encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: . 1:1001(1000) ack 1 win 123 ++0 > ipv4 1.1.1.1 > 2.2.2.2: gre: . 1:1001(1000) ack 1 + +// GRE plus 1-entry MPLS encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: + mpls (label 123, tc 1, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// GRE plus 2-entry MPLS encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: + mpls (label 0, tc 0, ttl 0) (label 1048575, tc 7, [S], ttl 255): . 1:1001(1000) ack 1 win 123 ++0 > ipv4 2.2.2.2 > 1.1.1.1: gre: + mpls (label 0, tc 0, ttl 0) (label 1048575, tc 7, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// double GRE encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: gre: ipv4 2.2.2.2 > 1.1.1.1: gre: . 1:1001(1000) ack 1 win 123 ++0 > ipv4 1.1.1.1 > 2.2.2.2: gre: ipv4 1.1.1.1 > 2.2.2.2: gre: . 1:1001(1000) ack 1 + +// IPIP encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: . 1:1001(1000) ack 1 win 123 ++0 > ipv4 1.1.1.1 > 2.2.2.2: . 1:1001(1000) ack 1 + +// double IPIP encap ++0 < ipv4 2.2.2.2 > 1.1.1.1: ipv4 2.2.2.2 > 1.1.1.1: . 1:1001(1000) ack 1 win 123 ++0 > ipv4 1.1.1.1 > 2.2.2.2: ipv4 1.1.1.1 > 2.2.2.2: . 1:1001(1000) ack 1 + +// GRE encap with GRE header fields specified ++0 < ipv4 2.2.2.2 > 1.1.1.1 : gre flags 0xb000, sum 511, off 1023, key 0x80001234, seq 512: . 1:1001(1000) ack 1 win 123 + +// IPv6... + +// GRE encap ++0 < ipv6 2::2222 > 1::1111: gre: . 1:1001(1000) ack 1 win 123 ++0 > ipv6 1::1111 > 2::2222: gre: . 1:1001(1000) ack 1 + +// GRE plus 1-entry MPLS encap ++0 < ipv6 2::2222 > 1::1111: gre: + mpls (label 123, tc 1, [S], ttl 255): . 1:1001(1000) ack 1 win 123 ++0 > ipv6 2::2222 > 1::1111: gre: + mpls (label 123, tc 1, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// GRE plus 2-entry MPLS encap ++0 < ipv6 2::2222 > 1::1111: gre: + mpls (label 0, tc 0, ttl 0) (label 1048575, tc 7, [S], ttl 255): . 1:1001(1000) ack 1 win 123 ++0 > ipv6 2::2222 > 1::1111: gre: + mpls (label 0, tc 0, ttl 0) (label 1048575, tc 7, [S], ttl 255): . 1:1001(1000) ack 1 win 123 + +// double GRE encap ++0 < ipv6 2::2222 > 1::1111: gre: ipv6 2::2222 > 1::1111: gre: . 1:1001(1000) ack 1 win 123 ++0 > ipv6 1::1111 > 2::2222: gre: ipv6 1::1111 > 2::2222: gre: . 1:1001(1000) ack 1 + +// IPv6 encap ++0 < ipv6 2::2222 > 1::1111: . 1:1001(1000) ack 1 win 123 ++0 > ipv6 1::1111 > 2::2222: . 1:1001(1000) ack 1 + +// double IPv6 encap ++0 < ipv6 2::2222 > 1::1111: ipv6 2::2222 > 1::1111: . 1:1001(1000) ack 1 win 123 ++0 > ipv6 1::1111 > 2::2222: ipv6 1::1111 > 2::2222: . 1:1001(1000) ack 1 + +// GRE encap with GRE header fields specified ++0 < ipv6 2::2222 > 1::1111 : gre flags 0xb000, sum 511, off 1023, key 0x80001234, seq 512: . 1:1001(1000) ack 1 win 123 ++0 > ipv6 1::1111 > 2::2222 : gre flags 0xb000, sum 511, off 1023, key 0x80001234, seq 512: . 1:1001(1000) ack 1 diff --git a/gtests/net/packetdrill/udp_packet.c b/gtests/net/packetdrill/udp_packet.c index 81c91873..e8004c56 100644 --- a/gtests/net/packetdrill/udp_packet.c +++ b/gtests/net/packetdrill/udp_packet.c @@ -27,6 +27,40 @@ #include "ip_packet.h" #include "udp.h" +int udp_header_append(struct packet *packet, u16 src_port, u16 dst_port, + char **error) +{ + struct header *header; + + header = packet_append_header(packet, HEADER_UDP, sizeof(struct udp)); + if (header == NULL) { + asprintf(error, "too many headers"); + return STATUS_ERR; + } + + header->h.udp->src_port = htons(src_port); + header->h.udp->dst_port = htons(dst_port); + header->h.udp->len = 0; /* actual length is filled in later */ + header->h.udp->check = 0; + return STATUS_OK; +} + +int udp_header_finish(struct packet *packet, + struct header *header, struct header *next_inner) +{ + struct udp *udp = header->h.udp; + + /* A UDP header may be either part of an encapsulation or belong to the + * innermost packet. We adjust only encapsulation headers, which we + * identify through their 0 current length. + */ + if (!header->total_bytes) { + header->total_bytes = header->header_bytes + next_inner->total_bytes; + udp->len = htons(header->total_bytes); + } + return STATUS_OK; +} + struct packet *new_udp_packet(int address_family, enum direction_t direction, struct ip_info ip_info, diff --git a/gtests/net/packetdrill/udp_packet.h b/gtests/net/packetdrill/udp_packet.h index 9ace4403..e66a8f4a 100644 --- a/gtests/net/packetdrill/udp_packet.h +++ b/gtests/net/packetdrill/udp_packet.h @@ -41,4 +41,12 @@ extern struct packet *new_udp_packet(int address_family, u16 src_port, u16 dst_port, char **error); + +/* Add a UDP header that is part of an encapsulation. */ +extern int udp_header_append(struct packet *packet, u16 src_port, u16 dst_port, + char **error); + +/* Fill in the length of an encapsulating UDP header. */ +extern int udp_header_finish(struct packet *packet, + struct header *header, struct header *next_inner); #endif /* __UDP_PACKET_H__ */ diff --git a/gtests/net/tcp/psp/psp_client_sockopt.pkt b/gtests/net/tcp/psp/psp_client_sockopt.pkt new file mode 100644 index 00000000..d2f294bf --- /dev/null +++ b/gtests/net/tcp/psp/psp_client_sockopt.pkt @@ -0,0 +1,25 @@ +// Test the client PSP [gs]etsockopts + +--psp_udp_port=1000 + +`../common/defaults.sh +ethtool -K tun0 psp-ip on +echo 0 > /proc/sys/net/ipv4/psp_hide_payload_from_taps` + + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 ++0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 ++0 bind(3, ..., ...) = 0 + ++0 getsockopt(3, SOL_TCP, TCP_PSP_RX_SPI_KEY, { spi=1 }, [24]) = 0 ++0 setsockopt(3, SOL_TCP, TCP_PSP_TX_SPI_KEY, { spi=2 }, 24) = 0 + ++0...0 connect(3, ..., ...) = 0 + ++0 > encap psp spi 2 S 0:0(0) ++0 < encap psp spi 1 S. 0:0(0) ack 1 win 32792 ++0 > encap psp spi 2 . 1:1(0) ack 1 + ++0 send(3, ..., 1000, 0) = 1000 ++0 > encap psp spi 2 P. 1:1001(1000) ack 1 ++0 < encap psp spi 1 . 1:1(0) ack 1001 win 257 + diff --git a/gtests/net/tcp/psp/psp_server_sockopt.pkt b/gtests/net/tcp/psp/psp_server_sockopt.pkt new file mode 100644 index 00000000..3547f58c --- /dev/null +++ b/gtests/net/tcp/psp/psp_server_sockopt.pkt @@ -0,0 +1,36 @@ +// Test the server PSP getsockopt + +--psp_udp_port=1000 + +`../common/defaults.sh +ethtool -K tun0 psp-ip on +echo 0 > /proc/sys/net/ipv4/psp_hide_payload_from_taps` + + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 ++0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 ++0 bind(3, ..., ...) = 0 ++0 listen(3, 1) = 0 + +// Passing wrong optlen should fail with EINVAL ++0 getsockopt(3, SOL_TCP, TCP_PSP_LISTENER, { spi=1 }, [20]) = -1 EINVAL (Invalid argument) + +// This getsockopt write the TX SPI and reads the RX SPI +// For simplicity assume both sides chose the same SPI ++0 getsockopt(3, SOL_TCP, TCP_PSP_LISTENER, { spi=1 }, [24]) = 0 + ++0 < encap psp spi 1 S 0:0(0) win 32792 ++0 > encap psp spi 1 S. 0:0(0) ack 1 ++0 < encap psp spi 1 . 1:1(0) ack 1 win 257 + ++0 accept(3, ..., ...) = 4 + ++0 send(4, ..., 1000, 0) = 1000 ++0 > encap psp spi 1 P. 1:1001(1000) ack 1 ++0 < encap psp spi 1 . 1:1(0) ack 1001 win 257 + ++0.1 < encap psp spi 1 P. 1:501(500) ack 1001 win 257 ++0 > encap psp spi 1 . 1001:1001(0) ack 501 ++0 recv(4, ..., 1000, 0) = 500 + ++0 close(3) = 0 ++0 close(4) = 0