From c09e06a6efaa94768660636a9c58ce9666e29d05 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 22 Apr 2016 16:55:31 +0200 Subject: trafgen: proto: Add IPv6 header generation Support for generating simple IPv6 headers using the 'ip6()/ipv6()' trafgen generation function. Fields supported: ver|version Version (default: 6) tc|tclass Traffic class (default: 0) fl|flow Flow Label (default: 0) len|length Payload length (calculated by default) nh|nexthdr Type of next header (default: 0) hl|hoplimit|ttl Hop Limit, TTL (default: 0) sa|saddr Source IPv6 address (default: device address) da|daddr Destination IPv6 address (default: 0:0:0:0:0:0:0:0) Examples: { eth(), ipv6(daddr=1:2:3:4:5:6:7:8) } { ipv6(tc=2, hl=3, daddr=::1) } { eth(), ipv6(nh=58, sa=2001:db8::, da=::1), 128, 0, 0x52, 0x03, 0, 0, 0, 0 } If not explicitely specified, the lower header is initialized as Ethernet. Signed-off-by: Tobias Klauser --- trafgen.8 | 39 +++++++++++++++++++++++++- trafgen_l3.c | 58 ++++++++++++++++++++++++++++++++++++++ trafgen_l3.h | 11 ++++++++ trafgen_l4.c | 84 ++++++++++++++++++++++++++++++++++++-------------------- trafgen_lexer.l | 20 ++++++++++++-- trafgen_parser.y | 46 +++++++++++++++++++++++++++++-- trafgen_proto.c | 33 +++++++++++++++++++--- trafgen_proto.h | 3 ++ 8 files changed, 254 insertions(+), 40 deletions(-) diff --git a/trafgen.8 b/trafgen.8 index 300277b..c81fecb 100644 --- a/trafgen.8 +++ b/trafgen.8 @@ -424,7 +424,7 @@ destination MAC address is set to the broadcast address (ff:ff:ff:ff:ff:ff). - Explicit Congestion Notification (ECN) field (default: 0) .sp .B len|length -- Total length of header and data (calculated by default) +- Total length of header and payload (calculated by default) .sp .B id - IPv4 datagram identification (default: 0) @@ -461,6 +461,43 @@ By default, if the lower level header is Ethernet, its EtherType field is set to 0x0800 (IPv4). If the lower level header is IPv4, its protocol field is set to 0x4 (IP-in-IP). +.I IPv6 +: +.B ip6|ipv6(ver=, class=, flow= len=, +.B nexthdr=, hoplimit=, +.in +16 +.B da=, sa=) +.in -16 +.sp +.in +4 +.B ver|version +- Version field (default: 6) +.sp +.B tc|tclass +- Traffic class (default: 0) +.sp +.B fl|flow +- Flow label (default: 0) +.sp +.B len|length +- Payload length (calculated by default) +.sp +.B nh|nexthdr +- Type of next header, i.e. transport layer protocol number (default: 0) +.sp +.B hl|hoplimit|ttl +- Hop limit, i.e. time to live (default: 0) +.sp +.B sa|saddr +- Source IPv6 address (default: device IPv6 address) +.sp +.B da|daddr +- Destination IPv6 address (default: 0:0:0:0:0:0:0:0) +.in -4 +.sp +By default, if the lower level header is Ethernet, its EtherType field is set to +0x86DD (IPv6). + .I UDP : .B udp(sp=, dp=, len=, csum=) diff --git a/trafgen_l3.c b/trafgen_l3.c index 88766ea..94c0755 100644 --- a/trafgen_l3.c +++ b/trafgen_l3.c @@ -90,7 +90,65 @@ static struct proto_hdr ipv4_hdr = { .set_next_proto = ipv4_set_next_proto, }; +static struct proto_field ipv6_fields[] = { + { .id = IP6_VER, .len = 4, .offset = 0, .shift = 28, .mask = 0xf0000000 }, + { .id = IP6_CLASS, .len = 4, .offset = 0, .shift = 20, .mask = 0x0ff00000 }, + { .id = IP6_FLOW_LBL, .len = 4, .offset = 0, .shift = 0, .mask = 0x000fffff }, + { .id = IP6_LEN, .len = 2, .offset = 4 }, + { .id = IP6_NEXT_HDR, .len = 1, .offset = 6 }, + { .id = IP6_HOP_LIMIT, .len = 1, .offset = 7 }, + { .id = IP6_SADDR, .len = 16, .offset = 8 }, + { .id = IP6_DADDR, .len = 16, .offset = 24 }, +}; + +static void ipv6_header_init(struct proto_hdr *hdr) +{ + proto_lower_default_add(hdr, PROTO_ETH); + + proto_header_fields_add(hdr, ipv6_fields, array_size(ipv6_fields)); + + proto_field_set_default_be32(hdr, IP6_VER, 6); + proto_field_set_default_dev_ipv6(hdr, IP6_SADDR); +} + +#define IPV6_HDR_LEN 40 + +static void ipv6_packet_finish(struct proto_hdr *hdr) +{ + struct packet *pkt = current_packet(); + uint16_t total_len = pkt->len - hdr->pkt_offset - IPV6_HDR_LEN; + + proto_field_set_default_be16(hdr, IP6_LEN, total_len); +} + +static void ipv6_set_next_proto(struct proto_hdr *hdr, enum proto_id pid) +{ + uint8_t ip_proto; + + switch(pid) { + case PROTO_UDP: + ip_proto = IPPROTO_UDP; + break; + case PROTO_TCP: + ip_proto = IPPROTO_TCP; + break; + default: + bug(); + } + + proto_field_set_default_u8(hdr, IP6_NEXT_HDR, ip_proto); +} + +static struct proto_hdr ipv6_hdr = { + .id = PROTO_IP6, + .layer = PROTO_L3, + .header_init = ipv6_header_init, + .packet_finish = ipv6_packet_finish, + .set_next_proto = ipv6_set_next_proto, +}; + void protos_l3_init(void) { proto_header_register(&ipv4_hdr); + proto_header_register(&ipv6_hdr); } diff --git a/trafgen_l3.h b/trafgen_l3.h index 9427378..a1b1523 100644 --- a/trafgen_l3.h +++ b/trafgen_l3.h @@ -20,6 +20,17 @@ enum ip4_field { IP4_MF, }; +enum ip6_field { + IP6_VER, + IP6_CLASS, + IP6_FLOW_LBL, + IP6_LEN, + IP6_NEXT_HDR, + IP6_HOP_LIMIT, + IP6_SADDR, + IP6_DADDR, +}; + extern void protos_l3_init(void); #endif /* TRAFGEN_L2_H */ diff --git a/trafgen_l4.c b/trafgen_l4.c index 64aada4..a5981c4 100644 --- a/trafgen_l4.c +++ b/trafgen_l4.c @@ -15,30 +15,10 @@ #include "trafgen_proto.h" static struct proto_field udp_fields[] = { - { .id = UDP_SPORT, .len = 2, .offset = 0 }, - { .id = UDP_DPORT, .len = 2, .offset = 2 }, - { .id = UDP_LEN, .len = 2, .offset = 4 }, - { .id = UDP_CSUM, .len = 2, .offset = 6 }, -}; - -static struct proto_field tcp_fields[] = { - { .id = TCP_SPORT, .len = 2, .offset = 0 }, - { .id = TCP_DPORT, .len = 2, .offset = 2 }, - { .id = TCP_SEQ, .len = 4, .offset = 4 }, - { .id = TCP_ACK_SEQ, .len = 4, .offset = 8 }, - { .id = TCP_DOFF, .len = 2, .offset = 12, .shift = 12, .mask = 0xf000 }, - /* reserved (4 bits) */ - { .id = TCP_CWR, .len = 2, .offset = 12, .shift = 7, .mask = 0x0080 }, - { .id = TCP_ECE, .len = 2, .offset = 12, .shift = 6, .mask = 0x0040 }, - { .id = TCP_URG, .len = 2, .offset = 12, .shift = 5, .mask = 0x0020 }, - { .id = TCP_ACK, .len = 2, .offset = 12, .shift = 4, .mask = 0x0010 }, - { .id = TCP_PSH, .len = 2, .offset = 12, .shift = 3, .mask = 0x0008 }, - { .id = TCP_RST, .len = 2, .offset = 12, .shift = 2, .mask = 0x0004 }, - { .id = TCP_SYN, .len = 2, .offset = 12, .shift = 1, .mask = 0x0002 }, - { .id = TCP_FIN, .len = 2, .offset = 12, .shift = 0, .mask = 0x0001 }, - { .id = TCP_WINDOW, .len = 2, .offset = 14 }, - { .id = TCP_CSUM, .len = 2, .offset = 16 }, - { .id = TCP_URG_PTR, .len = 2, .offset = 18 }, + { .id = UDP_SPORT, .len = 2, .offset = 0 }, + { .id = UDP_DPORT, .len = 2, .offset = 2 }, + { .id = UDP_LEN, .len = 2, .offset = 4 }, + { .id = UDP_CSUM, .len = 2, .offset = 6 }, }; static void udp_header_init(struct proto_hdr *hdr) @@ -61,12 +41,24 @@ static void udp_packet_finish(struct proto_hdr *hdr) if (proto_field_is_set(hdr, UDP_CSUM)) return; - if (!lower || lower->id != PROTO_IP4) + if (!lower) return; total_len = proto_field_get_u16(hdr, UDP_LEN); - csum = p4_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr), - total_len, IPPROTO_UDP); + + switch (lower->id) { + case PROTO_IP4: + csum = p4_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr), + total_len, IPPROTO_UDP); + break; + case PROTO_IP6: + csum = p6_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr), + total_len, IPPROTO_UDP); + break; + default: + csum = 0; + break; + } proto_field_set_be16(hdr, UDP_CSUM, bswap_16(csum)); } @@ -78,6 +70,26 @@ static struct proto_hdr udp_hdr = { .packet_finish = udp_packet_finish, }; +static struct proto_field tcp_fields[] = { + { .id = TCP_SPORT, .len = 2, .offset = 0 }, + { .id = TCP_DPORT, .len = 2, .offset = 2 }, + { .id = TCP_SEQ, .len = 4, .offset = 4 }, + { .id = TCP_ACK_SEQ, .len = 4, .offset = 8 }, + { .id = TCP_DOFF, .len = 2, .offset = 12, .shift = 12, .mask = 0xf000 }, + /* reserved (4 bits) */ + { .id = TCP_CWR, .len = 2, .offset = 12, .shift = 7, .mask = 0x0080 }, + { .id = TCP_ECE, .len = 2, .offset = 12, .shift = 6, .mask = 0x0040 }, + { .id = TCP_URG, .len = 2, .offset = 12, .shift = 5, .mask = 0x0020 }, + { .id = TCP_ACK, .len = 2, .offset = 12, .shift = 4, .mask = 0x0010 }, + { .id = TCP_PSH, .len = 2, .offset = 12, .shift = 3, .mask = 0x0008 }, + { .id = TCP_RST, .len = 2, .offset = 12, .shift = 2, .mask = 0x0004 }, + { .id = TCP_SYN, .len = 2, .offset = 12, .shift = 1, .mask = 0x0002 }, + { .id = TCP_FIN, .len = 2, .offset = 12, .shift = 0, .mask = 0x0001 }, + { .id = TCP_WINDOW, .len = 2, .offset = 14 }, + { .id = TCP_CSUM, .len = 2, .offset = 16 }, + { .id = TCP_URG_PTR, .len = 2, .offset = 18 }, +}; + static void tcp_header_init(struct proto_hdr *hdr) { proto_lower_default_add(hdr, PROTO_IP4); @@ -97,12 +109,24 @@ static void tcp_packet_finish(struct proto_hdr *hdr) if (proto_field_is_set(hdr, TCP_CSUM)) return; - if (!lower || lower->id != PROTO_IP4) + if (!lower) return; total_len = pkt->len - hdr->pkt_offset; - csum = p4_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr), - total_len, IPPROTO_TCP); + + switch (lower->id) { + case PROTO_IP4: + csum = p4_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr), + total_len, IPPROTO_TCP); + break; + case PROTO_IP6: + csum = p6_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr), + total_len, IPPROTO_TCP); + break; + default: + csum = 0; + break; + } proto_field_set_be16(hdr, TCP_CSUM, bswap_16(csum)); } diff --git a/trafgen_lexer.l b/trafgen_lexer.l index 3c624f8..a093ac1 100644 --- a/trafgen_lexer.l +++ b/trafgen_lexer.l @@ -77,9 +77,13 @@ number_bin ([0]?[b][0-1]+) number_dec (([0])|([1-9][0-9]*)) number_ascii ([a-zA-Z]) -mac_hex ([a-fA-F0-9]+) -mac ({mac_hex}:{mac_hex}:{mac_hex}:{mac_hex}:{mac_hex}:{mac_hex}) +a_hex ([a-fA-F0-9]+) +mac ({a_hex}:{a_hex}:{a_hex}:{a_hex}:{a_hex}:{a_hex}) ip4_addr ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) + /* We're very permissive about IPv6 addresses the grammar accepts, as + * they can come in various different formats. In any case, + * inet_pton(AF_INET6, ...) will reject the invalid ones later on. */ +ip6_addr (({a_hex}?:)?({a_hex}?:)?({a_hex}?:)?({a_hex}?:)?({a_hex}?:)?({a_hex}?:)?({a_hex}?:)?({a_hex})?) %% @@ -106,6 +110,7 @@ ip4_addr ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) "const64"|"c64" { return K_CONST64; } "prot"[o]? { return K_PROT; } +"tc"|"tclass" { return K_TC; } /* Ethernet */ "daddr"|"da" { return K_DADDR; } @@ -123,7 +128,6 @@ ip4_addr ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) /* MPLS (Multi Protocol Label Switching) */ "lbl"|"label" { return K_LABEL; } "last" { return K_LAST; } -"tc"|"tclass" { return K_TC; } "exp" { return K_EXP; } /* ARP */ @@ -152,6 +156,11 @@ ip4_addr ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) "df" { return K_DF; } "mf" { return K_MF; } + /* IPv6 */ +"fl"|"flow" { return K_FLOW; } +"nh"|"nexthdr" { return K_NEXT_HDR; } +"hl"|"hoplimit" { return K_HOP_LIMIT; } + /* UDP */ "sp"|"sport" { return K_SPORT; } "dp"|"dport" { return K_DPORT; } @@ -176,6 +185,7 @@ ip4_addr ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) "mpls" { return K_MPLS; } "arp" { return K_ARP; } "ip4"|"ipv4" { return K_IP4; } +"ip6"|"ipv6" { return K_IP6; } "udp" { return K_UDP; } "tcp" { return K_TCP; } @@ -235,6 +245,10 @@ ip4_addr ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) panic("Failed to parse IPv4 address %s\n", yytext); return ip4_addr; }; +{ip6_addr} { if (inet_pton(AF_INET6, yytext, &yylval.ip6_addr) != 1) + panic("Failed to parse IPv6 address %s\n", yytext); + return ip6_addr; }; + "'\\x"[a-fA-F0-9]{2}"'" { yylval.number = strtol(yytext + 3, NULL, 16); return number; } diff --git a/trafgen_parser.y b/trafgen_parser.y index 0b1c0fb..d6c9e70 100644 --- a/trafgen_parser.y +++ b/trafgen_parser.y @@ -342,6 +342,7 @@ static void proto_add(enum proto_id pid) %union { struct in_addr ip4_addr; + struct in6_addr ip6_addr; long long int number; uint8_t bytes[256]; char *str; @@ -353,6 +354,7 @@ static void proto_add(enum proto_id pid) %token K_DADDR K_SADDR K_ETYPE %token K_OPER K_SHA K_SPA K_THA K_TPA K_REQUEST K_REPLY K_PTYPE K_HTYPE %token K_PROT K_TTL K_DSCP K_ECN K_TOS K_LEN K_ID K_FLAGS K_FRAG K_IHL K_VER K_CSUM K_DF K_MF +%token K_FLOW K_NEXT_HDR K_HOP_LIMIT %token K_SPORT K_DPORT %token K_SEQ K_ACK_SEQ K_DOFF K_CWR K_ECE K_URG K_ACK K_PSH K_RST K_SYN K_FIN K_WINDOW K_URG_PTR %token K_TPID K_TCI K_PCP K_DEI K_1Q K_1AD @@ -361,17 +363,18 @@ static void proto_add(enum proto_id pid) %token K_ETH %token K_VLAN K_MPLS %token K_ARP -%token K_IP4 +%token K_IP4 K_IP6 %token K_UDP K_TCP %token ',' '{' '}' '(' ')' '[' ']' ':' '-' '+' '*' '/' '%' '&' '|' '<' '>' '^' -%token number string mac ip4_addr +%token number string mac ip4_addr ip6_addr %type number expression %type string %type mac %type ip4_addr +%type ip6_addr %left '-' '+' '*' '/' '%' '&' '|' '<' '>' '^' @@ -587,6 +590,7 @@ proto | mpls_proto { } | arp_proto { } | ip4_proto { } + | ip6_proto { } | udp_proto { } | tcp_proto { } ; @@ -770,6 +774,44 @@ ip4 : K_IP4 { proto_add(PROTO_IP4); } ; +ip6_proto + : ip6 '(' ip6_param_list ')' { } + ; + +ip6_param_list + : { } + | ip6_field { } + | ip6_field delimiter ip6_param_list { } + ; + +ip6_hop_limit + : K_HOP_LIMIT { } + | K_TTL { } + ; + +ip6_field + : K_VER skip_white '=' skip_white number + { proto_field_set_be32(hdr, IP6_VER, $5); } + | K_TC skip_white '=' skip_white number + { proto_field_set_be32(hdr, IP6_CLASS, $5); } + | K_FLOW skip_white '=' skip_white number + { proto_field_set_be32(hdr, IP6_FLOW_LBL, $5); } + | K_LEN skip_white '=' skip_white number + { proto_field_set_be16(hdr, IP6_LEN, $5); } + | K_NEXT_HDR skip_white '=' skip_white number + { proto_field_set_u8(hdr, IP6_NEXT_HDR, $5); } + | ip6_hop_limit skip_white '=' skip_white number + { proto_field_set_u8(hdr, IP6_HOP_LIMIT, $5); } + | K_SADDR skip_white '=' skip_white ip6_addr + { proto_field_set_bytes(hdr, IP6_SADDR, (uint8_t *)&($5.s6_addr)); } + | K_DADDR skip_white '=' skip_white ip6_addr + { proto_field_set_bytes(hdr, IP6_DADDR, (uint8_t *)&($5.s6_addr)); } + ; + +ip6 + : K_IP6 { proto_add(PROTO_IP6); } + ; + udp_proto : udp '(' udp_param_list ')' { } ; diff --git a/trafgen_proto.c b/trafgen_proto.c index e3704d8..1babba5 100644 --- a/trafgen_proto.c +++ b/trafgen_proto.c @@ -348,7 +348,6 @@ static void __proto_field_set_dev_ipv4(struct proto_hdr *hdr, uint32_t fid, { struct sockaddr_storage ss = { }; struct sockaddr_in *ss4; - uint32_t ip_addr; int ret; if (proto_field_is_set(hdr, fid)) @@ -359,9 +358,7 @@ static void __proto_field_set_dev_ipv4(struct proto_hdr *hdr, uint32_t fid, panic("Could not get device IPv4 address\n"); ss4 = (struct sockaddr_in *) &ss; - ip_addr = ss4->sin_addr.s_addr; - - __proto_field_set_bytes(hdr, fid, (uint8_t *)&ip_addr, is_default, false); + __proto_field_set_bytes(hdr, fid, (uint8_t *)&ss4->sin_addr.s_addr, is_default, false); } void proto_field_set_dev_ipv4(struct proto_hdr *hdr, uint32_t fid) @@ -374,6 +371,34 @@ void proto_field_set_default_dev_ipv4(struct proto_hdr *hdr, uint32_t fid) __proto_field_set_dev_ipv4(hdr, fid, true); } +static void __proto_field_set_dev_ipv6(struct proto_hdr *hdr, uint32_t fid, + bool is_default) +{ + struct sockaddr_storage ss = { }; + struct sockaddr_in6 *ss6; + int ret; + + if (proto_field_is_set(hdr, fid)) + return; + + ret = device_address(hdr->ctx->dev, AF_INET6, &ss); + if (ret < 0) + panic("Could not get device IPv6 address\n"); + + ss6 = (struct sockaddr_in6 *) &ss; + __proto_field_set_bytes(hdr, fid, (uint8_t *)&ss6->sin6_addr.s6_addr, is_default, false); +} + +void proto_field_set_dev_ipv6(struct proto_hdr *hdr, uint32_t fid) +{ + __proto_field_set_dev_ipv6(hdr, fid, false); +} + +void proto_field_set_default_dev_ipv6(struct proto_hdr *hdr, uint32_t fid) +{ + __proto_field_set_dev_ipv6(hdr, fid, true); +} + void protos_init(const char *dev) { struct proto_hdr *p; diff --git a/trafgen_proto.h b/trafgen_proto.h index 31ac1c8..cdcffdc 100644 --- a/trafgen_proto.h +++ b/trafgen_proto.h @@ -105,4 +105,7 @@ extern void proto_field_set_default_dev_mac(struct proto_hdr *hdr, uint32_t fid) extern void proto_field_set_dev_ipv4(struct proto_hdr *hdr, uint32_t fid); extern void proto_field_set_default_dev_ipv4(struct proto_hdr *hdr, uint32_t fid); +extern void proto_field_set_dev_ipv6(struct proto_hdr *hdr, uint32_t fid); +extern void proto_field_set_default_dev_ipv6(struct proto_hdr *hdr, uint32_t fid); + #endif /* TRAFGEN_PROTO_H */ -- cgit v1.2.3-54-g00ecf