summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Klauser <tklauser@distanz.ch>2016-04-22 16:55:31 +0200
committerTobias Klauser <tklauser@distanz.ch>2016-04-25 17:56:34 +0200
commitc09e06a6efaa94768660636a9c58ce9666e29d05 (patch)
treea41455899d30c7a8de30fdc9eb917702d9769975
parent0cf15c320f9211d59b055157c895537e4b682a07 (diff)
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 <tklauser@distanz.ch>
-rw-r--r--trafgen.839
-rw-r--r--trafgen_l3.c58
-rw-r--r--trafgen_l3.h11
-rw-r--r--trafgen_l4.c84
-rw-r--r--trafgen_lexer.l20
-rw-r--r--trafgen_parser.y46
-rw-r--r--trafgen_proto.c33
-rw-r--r--trafgen_proto.h3
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=<number>, class=<number>, flow=<number> len=<number>,
+.B nexthdr=<number>, hoplimit=<number>,
+.in +16
+.B da=<ip6_addr>, sa=<ip6_addr>)
+.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=<number>, dp=<number>, len=<number>, csum=<number>)
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> number expression
%type <str> string
%type <bytes> mac
%type <ip4_addr> ip4_addr
+%type <ip6_addr> 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 */