diff options
author | Tobias Klauser <tklauser@distanz.ch> | 2016-02-01 15:56:10 +0100 |
---|---|---|
committer | Tobias Klauser <tklauser@distanz.ch> | 2016-02-01 16:22:24 +0100 |
commit | f26175af848bfb3faaedd98b402949e8bd7ab8bc (patch) | |
tree | 3901fb68dd629e08abc02b81349ff4cb6b26f213 | |
parent | 0042946ef9e332e32af00a0fe0c6c7b6eec4b0e1 (diff) |
trafgen: parser: Add TCP header generation function
Add a function 'tcp()' to generate TCP headers from the trafgen
configuration language.
Fields supported:
sp|sport TCP source port (default 0)
dp|dport TCP destination port (default 0)
seq Sequence number (default: 0)
aseq|ackseq Acknowledgement number (default 0)
doff|hlen Header length/data offset (default: 5)
cwr Congestion Window Reduced flag (default: 0)
ece|ecn ECN-Echo flag (default: 0)
urg Urgent flag (default: 0)
ack Acknowledgement flag (default: 0)
psh Push flag (default: 0)
rst Reset flag (default: 0)
syn Synchronize flag (default: 0)
fin Finish flag (default: 0)
win|window Receive window size (default: 0)
csum Checksum field (calculated automatically)
urgptr Urgent pointer (default: 0)
Example (SYN on port 80/http):
{ tcp(dport=80, syn, window=5840) }
Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
-rw-r--r-- | trafgen.8 | 59 | ||||
-rw-r--r-- | trafgen_l4.c | 64 | ||||
-rw-r--r-- | trafgen_l4.h | 19 | ||||
-rw-r--r-- | trafgen_lexer.l | 16 | ||||
-rw-r--r-- | trafgen_parser.y | 45 |
5 files changed, 202 insertions, 1 deletions
@@ -428,6 +428,65 @@ By default, if the lower level header is Ethernet, its EtherType field is set to By default, if the lower level header is IPv4, its protocol field is set to 0x11 (UDP). +.I TCP +: +.B tcp(sp=<number>, dp=<number>, seq=<number>, aseq|ackseq=<number>, doff|hlen=<number>, +.B cwr, ece|ecn, urg, ack, psh, rst, syn, fin, win|window=<number>, csum=<number>, +.B urgptr=<number>) +.sp +.in +4 +.B sp|sport +- Source port (default: 0) +.sp +.B dp|dport +- Destination port (default: 0) +.sp +.B seq +- Sequence number (default: 0) +.sp +.B aseq|ackseq +- Acknowledgement number (default: 0) +.sp +.B doff|hlen +- Header size (data offset) in number of 32-bit words (default: 5) +.sp +.B cwr +- Congestion Window Reduced (CWR) flag (default: 0) +.sp +.B ece|ecn +- ECN-Echo (ECE) flag (default: 0) +.sp +.B urg +- Urgent flag (default: 0) +.sp +.B ack +- Acknowledgement flag (default: 0) +.sp +.B psh +- Push flag (default: 0) +.sp +.B rst +- Reset flag (default: 0) +.sp +.B syn +- Synchronize flag (default: 0) +.sp +.B fin +- Finish flag (default: 0) +.sp +.B win|window +- Receive window size (default: 0) +.sp +.B csum +- Checksum field over IPv4 pseudo header (calculated by default) +.sp +.B urgptr +- Urgent pointer (default: 0) +.sp +.in -4 +By default, if the lower level header is IPv4, its protocol field is set to +0x6 (TCP). + Simple example of a UDP Echo packet: .PP .in +5 diff --git a/trafgen_l4.c b/trafgen_l4.c index 286e54a..7f80e74 100644 --- a/trafgen_l4.c +++ b/trafgen_l4.c @@ -21,6 +21,26 @@ static struct proto_field udp_fields[] = { { .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 }, +}; + static void udp_header_init(struct proto_hdr *hdr) { struct proto_hdr *lower; @@ -65,7 +85,51 @@ static struct proto_hdr udp_hdr = { .packet_finish = udp_packet_finish, }; +static void tcp_header_init(struct proto_hdr *hdr) +{ + struct proto_hdr *lower; + + proto_lower_default_add(PROTO_IP4); + + lower = proto_current_header(); + + if (lower->id == PROTO_IP4) + proto_field_set_default_u8(lower, IP4_PROTO, IPPROTO_TCP); + + proto_header_fields_add(hdr, tcp_fields, array_size(tcp_fields)); + + proto_field_set_default_be16(hdr, TCP_DOFF, 5); +} + +static void tcp_packet_finish(struct proto_hdr *hdr) +{ + struct proto_hdr *lower = proto_lower_header(hdr); + struct packet *pkt = current_packet(); + uint16_t total_len; + uint16_t csum; + + if (proto_field_is_set(hdr, TCP_CSUM)) + return; + + if (!lower || lower->id != PROTO_IP4) + return; + + total_len = pkt->len - hdr->pkt_offset; + csum = p4_csum((void *) proto_header_ptr(lower), proto_header_ptr(hdr), + total_len, IPPROTO_TCP); + + proto_field_set_be16(hdr, TCP_CSUM, bswap_16(csum)); +} + +static struct proto_hdr tcp_hdr = { + .id = PROTO_TCP, + .layer = PROTO_L4, + .header_init = tcp_header_init, + .packet_finish = tcp_packet_finish, +}; + void protos_l4_init(void) { proto_header_register(&udp_hdr); + proto_header_register(&tcp_hdr); } diff --git a/trafgen_l4.h b/trafgen_l4.h index 4651009..da73ebc 100644 --- a/trafgen_l4.h +++ b/trafgen_l4.h @@ -8,6 +8,25 @@ enum udp_field { UDP_CSUM, }; +enum tcp_field { + TCP_SPORT, + TCP_DPORT, + TCP_SEQ, + TCP_ACK_SEQ, + TCP_DOFF, + TCP_CWR, + TCP_ECE, + TCP_URG, + TCP_ACK, + TCP_PSH, + TCP_RST, + TCP_SYN, + TCP_FIN, + TCP_WINDOW, + TCP_CSUM, + TCP_URG_PTR, +}; + extern void protos_l4_init(void); #endif /* TRAFGEN_L4_H */ diff --git a/trafgen_lexer.l b/trafgen_lexer.l index 5ab0815..83e7602 100644 --- a/trafgen_lexer.l +++ b/trafgen_lexer.l @@ -143,10 +143,26 @@ ip4_addr ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) "sp"|"sport" { return K_SPORT; } "dp"|"dport" { return K_DPORT; } + /* TCP */ +"seq" { return K_SEQ; } +"ackseq"|"aseq" { return K_ACK_SEQ; } +"doff"|hlen { return K_DOFF; } +"cwr" { return K_CWR; } +"ece"|"ecn" { return K_ECE; } +"urg" { return K_URG; } +"ack" { return K_ACK; } +"psh" { return K_PSH; } +"rst" { return K_RST; } +"syn" { return K_SYN; } +"fin" { return K_FIN; } +"win"|"window" { return K_WINDOW; } +"urgptr" { return K_URG_PTR; } + "eth" { return K_ETH; } "arp" { return K_ARP; } "ip4"|"ipv4" { return K_IP4; } "udp" { return K_UDP; } +"tcp" { return K_TCP; } [ ]*"-"[ ]* { return '-'; } [ ]*"+"[ ]* { return '+'; } diff --git a/trafgen_parser.y b/trafgen_parser.y index e512aed..7387c14 100644 --- a/trafgen_parser.y +++ b/trafgen_parser.y @@ -354,11 +354,12 @@ static void proto_add(enum proto_id pid) %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_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_ETH %token K_ARP %token K_IP4 -%token K_UDP +%token K_UDP K_TCP %token ',' '{' '}' '(' ')' '[' ']' ':' '-' '+' '*' '/' '%' '&' '|' '<' '>' '^' @@ -582,6 +583,7 @@ proto | arp_proto { } | ip4_proto { } | udp_proto { } + | tcp_proto { } ; eth_proto @@ -717,6 +719,47 @@ udp : K_UDP { proto_add(PROTO_UDP); } ; +tcp_proto + : tcp '(' tcp_param_list ')' { } + ; + +tcp_param_list + : { } + | tcp_field { } + | tcp_field delimiter tcp_param_list { } + ; + +tcp_field + : K_SPORT skip_white '=' skip_white number + { proto_field_set_be16(hdr, TCP_SPORT, $5); } + | K_DPORT skip_white '=' skip_white number + { proto_field_set_be16(hdr, TCP_DPORT, $5); } + | K_SEQ skip_white '=' skip_white number + { proto_field_set_be32(hdr, TCP_SEQ, $5); } + | K_ACK_SEQ skip_white '=' skip_white number + { proto_field_set_be32(hdr, TCP_ACK_SEQ, $5); } + | K_DOFF skip_white '=' skip_white number + { proto_field_set_be16(hdr, TCP_DOFF, $5); } + | K_CWR { proto_field_set_be16(hdr, TCP_CWR, 1); } + | K_ECE { proto_field_set_be16(hdr, TCP_ECE, 1); } + | K_URG { proto_field_set_be16(hdr, TCP_URG, 1); } + | K_ACK { proto_field_set_be16(hdr, TCP_ACK, 1); } + | K_PSH { proto_field_set_be16(hdr, TCP_PSH, 1); } + | K_RST { proto_field_set_be16(hdr, TCP_RST, 1); } + | K_SYN { proto_field_set_be16(hdr, TCP_SYN, 1); } + | K_FIN { proto_field_set_be16(hdr, TCP_FIN, 1); } + | K_WINDOW skip_white '=' skip_white number + { proto_field_set_be16(hdr, TCP_WINDOW, $5); } + | K_CSUM skip_white '=' skip_white number + { proto_field_set_be16(hdr, TCP_CSUM, $5); } + | K_URG_PTR skip_white '=' skip_white number + { proto_field_set_be16(hdr, TCP_URG_PTR, $5); } + ; + +tcp + : K_TCP { proto_add(PROTO_TCP); } + ; + %% static void finalize_packet(void) |