From 8eb75b5d5e518432ec98154d6235b8a1b0621f61 Mon Sep 17 00:00:00 2001 From: Neal Cardwell <ncardwell@google.com> Date: Mon, 4 Nov 2013 15:31:11 -0500 Subject: [PATCH] net-test: packetdrill encap support: IPv6 support Add support for IPv6 in encapsulation layers, including: lexical scanning of IPv6 addresses, parsing IPv6 encap in scripts, appending and filling in IPv6 encapsulation. Change-Id: I1349cd4056c168a4114a92324eb2a321f7db7697 --- gtests/net/packetdrill/ip_packet.c | 48 ++++++++++++++++++++++++++++++ gtests/net/packetdrill/ip_packet.h | 17 ++++++++++- gtests/net/packetdrill/lexer.l | 30 +++++++++++++++++-- gtests/net/packetdrill/packet.c | 2 +- gtests/net/packetdrill/parser.y | 17 +++++++++-- 5 files changed, 107 insertions(+), 7 deletions(-) diff --git a/gtests/net/packetdrill/ip_packet.c b/gtests/net/packetdrill/ip_packet.c index 7c0aad16..147728c2 100644 --- a/gtests/net/packetdrill/ip_packet.c +++ b/gtests/net/packetdrill/ip_packet.c @@ -153,6 +153,37 @@ int ipv4_header_append(struct packet *packet, return STATUS_OK; } +int ipv6_header_append(struct packet *packet, + const char *ip_src, + const char *ip_dst, + char **error) +{ + struct header *header = NULL; + const int ipv6_bytes = sizeof(struct ipv6); + struct ipv6 *ipv6 = NULL; + + header = packet_append_header(packet, HEADER_IPV6, ipv6_bytes); + if (header == NULL) { + asprintf(error, "too many headers"); + return STATUS_ERR; + } + + ipv6 = header->h.ipv6; + set_ip_header(ipv6, AF_INET6, sizeof(struct ipv6), ECN_NONE, 0); + + if (inet_pton(AF_INET6, ip_src, &ipv6->src_ip) != 1) { + asprintf(error, "bad IPv6 src address: '%s'\n", ip_src); + return STATUS_ERR; + } + + if (inet_pton(AF_INET6, ip_dst, &ipv6->dst_ip) != 1) { + asprintf(error, "bad IPv6 dst address: '%s'\n", ip_dst); + return STATUS_ERR; + } + + return STATUS_OK; +} + int ipv4_header_finish(struct packet *packet, struct header *header, struct header *next_inner) { @@ -170,3 +201,20 @@ int ipv4_header_finish(struct packet *packet, return STATUS_OK; } + +int ipv6_header_finish(struct packet *packet, + struct header *header, struct header *next_inner) +{ + struct ipv6 *ipv6 = header->h.ipv6; + int ip_bytes = sizeof(struct ipv6) + next_inner->total_bytes; + + assert(next_inner->total_bytes <= 0xffff); + ipv6->payload_len = htons(next_inner->total_bytes); + ipv6->next_header = header_type_info(next_inner->type)->ip_proto; + + /* IPv6 has no header checksum. */ + + header->total_bytes = ip_bytes; + + return STATUS_OK; +} diff --git a/gtests/net/packetdrill/ip_packet.h b/gtests/net/packetdrill/ip_packet.h index ae39e578..7531a292 100644 --- a/gtests/net/packetdrill/ip_packet.h +++ b/gtests/net/packetdrill/ip_packet.h @@ -50,10 +50,25 @@ extern int ipv4_header_append(struct packet *packet, const char *ip_dst, char **error); -/* Finalize the IPV4 header by filling in all necessary fields that +/* Append an IPv6 header to the end of the given packet and fill in + * src/dst. On success, return STATUS_OK; on error return STATUS_ERR + * and fill in a malloc-allocated error message in *error. + */ +extern int ipv6_header_append(struct packet *packet, + const char *ip_src, + const char *ip_dst, + char **error); + +/* Finalize the IPv4 header by filling in all necessary fields that * were not filled in at parse time. */ extern int ipv4_header_finish(struct packet *packet, struct header *header, struct header *next_inner); +/* Finalize the IPv6 header by filling in all necessary fields that + * were not filled in at parse time. + */ +extern int ipv6_header_finish(struct packet *packet, + struct header *header, struct header *next_inner); + #endif /* __IP_PACKET_H__ */ diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l index 759f9bd0..398b434a 100644 --- a/gtests/net/packetdrill/lexer.l +++ b/gtests/net/packetdrill/lexer.l @@ -131,11 +131,33 @@ c_comment \/\*(([^*])|(\*[^\/]))*\*\/ */ code \%\{(([^}])|(\}[^\%]))*\}\% -/* A regular experssion for an IP address - * TODO(ncardwell): IPv6 - */ +/* IPv4: a regular experssion for an IPv4 address */ ipv4_addr [0-9]+[.][0-9]+[.][0-9]+[.][0-9]+ +/* IPv6: a regular experssion for an IPv6 address. The complexity is + * unfortunate, but we can't use a super-simple approach because TCP + * sequence number ranges like 1:1001 can look like IPv6 addresses if + * we use a naive approach. + */ +seg [0-9a-fA-F]{1,4} +v0 [:][:] +v1 ({seg}[:]){7,7}{seg} +v2 ({seg}[:]){1,7}[:] +v3 ({seg}[:]){1,6}[:]{seg} +v4 ({seg}[:]){1,5}([:]{seg}){1,2} +v5 ({seg}[:]){1,4}([:]{seg}){1,3} +v6 ({seg}[:]){1,3}([:]{seg}){1,4} +v7 ({seg}[:]){1,2}([:]{seg}){1,5} +v8 {seg}[:](([:]{seg}){1,6}) +v9 [:]([:]{seg}){1,7} +/* IPv4-mapped IPv6 address: */ +v10 [:][:]ffff[:]{ipv4_addr} +/* IPv4-translated IPv6 address: */ +v11 [:][:]ffff[:](0){1,4}[:]{ipv4_addr} +/* IPv4-embedded IPv6 addresses: */ +v12 ({seg}[:]){1,4}[:]{ipv4_addr} +ipv6_addr ({v0}|{v1}|{v2}|{v3}|{v4}|{v5}|{v6}|{v7}|{v8}|{v9}|{v10}|{v11}|{v12}) + %% sa_family return SA_FAMILY; sin_port return SIN_PORT; @@ -150,6 +172,7 @@ onoff return ONOFF; linger return LINGER; htons return _HTONS_; ipv4 return IPV4; +ipv6 return IPV6; icmp return ICMP; udp return UDP; gre return GRE; @@ -186,4 +209,5 @@ ce return CE; {c_comment} /* ignore C-style comment */; {code} yylval.string = code(yytext); return CODE; {ipv4_addr} yylval.string = strdup(yytext); return IPV4_ADDR; +{ipv6_addr} yylval.string = strdup(yytext); return IPV6_ADDR; %% diff --git a/gtests/net/packetdrill/packet.c b/gtests/net/packetdrill/packet.c index 8f814650..310738a5 100644 --- a/gtests/net/packetdrill/packet.c +++ b/gtests/net/packetdrill/packet.c @@ -37,7 +37,7 @@ struct header_type_info header_types[HEADER_NUM_TYPES] = { { "NONE", 0, 0, NULL }, { "IPV4", IPPROTO_IPIP, ETHERTYPE_IP, ipv4_header_finish }, - { "IPV6", IPPROTO_IPV6, ETHERTYPE_IPV6, NULL }, + { "IPV6", IPPROTO_IPV6, ETHERTYPE_IPV6, ipv6_header_finish }, { "GRE", IPPROTO_GRE, 0, gre_header_finish }, { "TCP", IPPROTO_TCP, 0, NULL }, { "UDP", IPPROTO_UDP, 0, NULL }, diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index 636e2e01..7c43792c 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -473,11 +473,11 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string, %token <reserved> ACK ECR EOL MSS NOP SACK SACKOK TIMESTAMP VAL WIN WSCALE PRO %token <reserved> FAST_OPEN %token <reserved> ECT0 ECT1 CE ECT01 NO_ECN -%token <reserved> IPV4 ICMP UDP GRE MTU +%token <reserved> IPV4 IPV6 ICMP UDP GRE MTU %token <reserved> OPTION %token <floating> FLOAT %token <integer> INTEGER HEX_INTEGER -%token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR +%token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR %type <direction> direction %type <ip_ecn> opt_ip_info %type <ip_ecn> ip_ecn @@ -550,7 +550,9 @@ option_value | WORD { $$ = $1; } | STRING { $$ = $1; } | IPV4_ADDR { $$ = $1; } +| IPV6_ADDR { $$ = $1; } | IPV4 { $$ = strdup("ipv4"); } +| IPV6 { $$ = strdup("ipv6"); } ; opt_init_command @@ -751,6 +753,17 @@ packet_prefix free(ip_dst); $$ = packet; } +| packet_prefix IPV6 IPV6_ADDR '>' IPV6_ADDR ':' { + char *error = NULL; + struct packet *packet = $1; + char *ip_src = $3; + char *ip_dst = $5; + if (ipv6_header_append(packet, ip_src, ip_dst, &error)) + semantic_error(error); + free(ip_src); + free(ip_dst); + $$ = packet; +} | packet_prefix GRE ':' { char *error = NULL; struct packet *packet = $1; -- GitLab