summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Klauser <tklauser@distanz.ch>2016-02-01 15:56:10 +0100
committerTobias Klauser <tklauser@distanz.ch>2016-02-01 16:22:24 +0100
commitf26175af848bfb3faaedd98b402949e8bd7ab8bc (patch)
tree3901fb68dd629e08abc02b81349ff4cb6b26f213
parent0042946ef9e332e32af00a0fe0c6c7b6eec4b0e1 (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.859
-rw-r--r--trafgen_l4.c64
-rw-r--r--trafgen_l4.h19
-rw-r--r--trafgen_lexer.l16
-rw-r--r--trafgen_parser.y45
5 files changed, 202 insertions, 1 deletions
diff --git a/trafgen.8 b/trafgen.8
index 48a252c..207cfc5 100644
--- a/trafgen.8
+++ b/trafgen.8
@@ -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)