Skip to content

Commit

Permalink
trafgen: proto: Add IPv6 header generation
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
tklauser committed Apr 25, 2016
1 parent 0cf15c3 commit c09e06a
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 40 deletions.
39 changes: 38 additions & 1 deletion trafgen.8
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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>)
Expand Down
58 changes: 58 additions & 0 deletions trafgen_l3.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
11 changes: 11 additions & 0 deletions trafgen_l3.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
84 changes: 54 additions & 30 deletions trafgen_l4.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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));
}
Expand All @@ -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);
Expand All @@ -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));
}
Expand Down
20 changes: 17 additions & 3 deletions trafgen_lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -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})?)

%%

Expand All @@ -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; }
Expand All @@ -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 */
Expand Down Expand Up @@ -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; }
Expand All @@ -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; }

Expand Down Expand Up @@ -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; }

Expand Down
Loading

0 comments on commit c09e06a

Please sign in to comment.