From 423bb070d372341aa178849a9479099b95c7d352 Mon Sep 17 00:00:00 2001 From: Vadim Kochan Date: Wed, 13 Jul 2016 01:01:53 +0300 Subject: [PATCH] trafgen: proto: Add ICMPv4 header generation Support for generating ICMPv4 headers using the 'icmp4()/icmpv4()' trafgen generation functions. Fields supported: type Set type field (default 0: Echo reply) Supported keywords: echorequest, echoreply code Set code field (default 0) csum Set checksum field (calculated by default) mtu Set mtu field for destination unreachable (default 0) seq Set sequence field (default 0) id Set identifier field (default 0) addr Set redirect address (default 0.0.0.0) Example (send ping request): { icmpv4(echorequest, seq=1, id=1326) } Signed-off-by: Vadim Kochan [tk: squash commits, consistency between functionality and docu] Signed-off-by: Tobias Klauser --- trafgen.8 | 40 ++++++++++++++++++++++++++++++++++++++++ trafgen_l3.c | 3 +++ trafgen_l4.c | 42 ++++++++++++++++++++++++++++++++++++++++++ trafgen_l4.h | 10 ++++++++++ trafgen_lexer.l | 6 ++++++ trafgen_parser.y | 41 ++++++++++++++++++++++++++++++++++++++++- trafgen_proto.h | 1 + 7 files changed, 142 insertions(+), 1 deletion(-) diff --git a/trafgen.8 b/trafgen.8 index b011276d4..c95879401 100644 --- a/trafgen.8 +++ b/trafgen.8 @@ -498,6 +498,46 @@ By default, if the lower level header is Ethernet, its EtherType field is set to By default, if the lower level header is Ethernet, its EtherType field is set to 0x86DD (IPv6). +.I ICMPv4 +: +.B icmp4|icmpv4(type=, code=, echorequest, echoreply, +.B csum=, mtu=, seq=, id=, addr=) +.sp +.in +4 +.B type +- Message type (default: 0 - Echo reply) +.sp +.B code +- Message code (default: 0) +.sp +.B echorequest +- ICMPv4 echo (ping) request (type: 8, code: 0) +.sp +.B echoreply +- ICMPv4 echo (ping) reply (type: 0, code: 0) +.sp +.B csum +- Checksum of ICMPv4 header and payload (calculated by default) +.sp +.B mtu +- Next-hop MTU field used in 'Datagram is too big' message type (default; 0) +.sp +.B seq +- Sequence number used in Echo/Timestamp/Address mask messages (default: 0) +.sp +.B id +- Identifier used in Echo/Timestamp/Address mask messages (default: 0) +.sp +.B addr +- IPv4 address used in Redirect messages (default: 0.0.0.0) +.sp +.in -4 +Example ICMP echo request (ping): +.in +4 +.sp +{ icmpv4(echorequest, seq=1, id=1326) } +.in -4 + .I ICMPv6 : .B icmp6|icmpv6(type=, echorequest, echoreply, code=, diff --git a/trafgen_l3.c b/trafgen_l3.c index cbdbe6c01..ae3f136b6 100644 --- a/trafgen_l3.c +++ b/trafgen_l3.c @@ -69,6 +69,9 @@ static void ipv4_set_next_proto(struct proto_hdr *hdr, enum proto_id pid) case PROTO_IP4: ip_proto = IPPROTO_IPIP; break; + case PROTO_ICMP4: + ip_proto = IPPROTO_ICMP; + break; case PROTO_UDP: ip_proto = IPPROTO_UDP; break; diff --git a/trafgen_l4.c b/trafgen_l4.c index 886e2b2ac..c10967596 100644 --- a/trafgen_l4.c +++ b/trafgen_l4.c @@ -138,6 +138,47 @@ static struct proto_hdr tcp_hdr = { .packet_finish = tcp_packet_finish, }; +static struct proto_field icmpv4_fields[] = { + { .id = ICMPV4_TYPE, .len = 1, .offset = 0 }, + { .id = ICMPV4_CODE, .len = 1, .offset = 1 }, + { .id = ICMPV4_CSUM, .len = 2, .offset = 2 }, + /* Echo/Ping fields */ + { .id = ICMPV4_ID, .len = 2, .offset = 4 }, + { .id = ICMPV4_SEQ, .len = 2, .offset = 6 }, + /* Redirect field */ + { .id = ICMPV4_REDIR_ADDR, .len = 4, .offset = 4 }, + /* Next-hop MTU */ + { .id = ICMPV4_MTU, .len = 2, .offset = 6 }, +}; + +static void icmpv4_header_init(struct proto_hdr *hdr) +{ + proto_lower_default_add(hdr, PROTO_IP4); + + proto_header_fields_add(hdr, icmpv4_fields, array_size(icmpv4_fields)); +} + +static void icmpv4_packet_finish(struct proto_hdr *hdr) +{ + struct packet *pkt; + uint16_t csum; + + if (proto_field_is_set(hdr, ICMPV4_CSUM)) + return; + + pkt = current_packet(); + + csum = htons(calc_csum(proto_header_ptr(hdr), pkt->len - hdr->pkt_offset)); + proto_field_set_u16(hdr, ICMPV4_CSUM, bswap_16(csum)); +} + +static struct proto_hdr icmpv4_hdr = { + .id = PROTO_ICMP4, + .layer = PROTO_L4, + .header_init = icmpv4_header_init, + .packet_finish = icmpv4_packet_finish, +}; + static struct proto_field icmpv6_fields[] = { { .id = ICMPV6_TYPE, .len = 1, .offset = 0 }, { .id = ICMPV6_CODE, .len = 1, .offset = 1 }, @@ -190,5 +231,6 @@ void protos_l4_init(void) { proto_header_register(&udp_hdr); proto_header_register(&tcp_hdr); + proto_header_register(&icmpv4_hdr); proto_header_register(&icmpv6_hdr); } diff --git a/trafgen_l4.h b/trafgen_l4.h index e94ff2385..9537cbf3f 100644 --- a/trafgen_l4.h +++ b/trafgen_l4.h @@ -27,6 +27,16 @@ enum tcp_field { TCP_URG_PTR, }; +enum icmpv4_field { + ICMPV4_TYPE, + ICMPV4_CODE, + ICMPV4_CSUM, + ICMPV4_ID, + ICMPV4_SEQ, + ICMPV4_REDIR_ADDR, + ICMPV4_MTU, +}; + enum icmpv6_field { ICMPV6_TYPE, ICMPV6_CODE, diff --git a/trafgen_lexer.l b/trafgen_lexer.l index 87db50419..5873eec82 100644 --- a/trafgen_lexer.l +++ b/trafgen_lexer.l @@ -162,6 +162,11 @@ ip6_addr (({a_hex}?:)?({a_hex}?:)?({a_hex}?:)?({a_hex}?:)?({a_hex}?:)?({a_hex}?: "nh"|"nexthdr" { return K_NEXT_HDR; } "hl"|"hoplimit" { return K_HOP_LIMIT; } + + /* ICMPv4 */ +"addr" { return K_ADDR; } +"mtu" { return K_MTU; } + /* ICMPv6 */ "code" { return K_CODE; } "echorequest" { return K_ECHO_REQUEST; } @@ -192,6 +197,7 @@ ip6_addr (({a_hex}?:)?({a_hex}?:)?({a_hex}?:)?({a_hex}?:)?({a_hex}?:)?({a_hex}?: "arp" { return K_ARP; } "ip4"|"ipv4" { return K_IP4; } "ip6"|"ipv6" { return K_IP6; } +"icmp4"|"icmpv4" { return K_ICMP4; } "icmp6"|"icmpv6" { return K_ICMP6; } "udp" { return K_UDP; } "tcp" { return K_TCP; } diff --git a/trafgen_parser.y b/trafgen_parser.y index adb7f5edf..0fe867498 100644 --- a/trafgen_parser.y +++ b/trafgen_parser.y @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -362,11 +363,13 @@ static void proto_add(enum proto_id pid) %token K_TPID K_TCI K_PCP K_DEI K_1Q K_1AD %token K_LABEL K_TC K_LAST K_EXP +%token K_ADDR K_MTU + %token K_ETH %token K_VLAN K_MPLS %token K_ARP %token K_IP4 K_IP6 -%token K_ICMP6 +%token K_ICMP4 K_ICMP6 %token K_UDP K_TCP %token ',' '{' '}' '(' ')' '[' ']' ':' '-' '+' '*' '/' '%' '&' '|' '<' '>' '^' @@ -594,6 +597,7 @@ proto | arp_proto { } | ip4_proto { } | ip6_proto { } + | icmp4_proto { } | icmpv6_proto { } | udp_proto { } | tcp_proto { } @@ -817,6 +821,41 @@ ip6 : K_IP6 { proto_add(PROTO_IP6); } ; +icmp4_proto + : icmp4 '(' icmp4_param_list ')' { } + ; + +icmp4_param_list + : { } + | icmp4_field { } + | icmp4_field delimiter icmp4_param_list { } + ; + +icmp4_field + : K_TYPE skip_white '=' skip_white number + { proto_field_set_u8(hdr, ICMPV4_TYPE, $5); } + | K_CODE skip_white '=' skip_white number + { proto_field_set_u8(hdr, ICMPV4_CODE, $5); } + | K_ID skip_white '=' skip_white number + { proto_field_set_be16(hdr, ICMPV4_ID, $5); } + | K_SEQ skip_white '=' skip_white number + { proto_field_set_be16(hdr, ICMPV4_SEQ, $5); } + | K_MTU skip_white '=' skip_white number + { proto_field_set_be16(hdr, ICMPV4_MTU, $5); } + | K_ADDR skip_white '=' skip_white ip4_addr + { proto_field_set_u32(hdr, ICMPV4_REDIR_ADDR, $5.s_addr); } + | K_ECHO_REQUEST + { proto_field_set_u8(hdr, ICMPV4_TYPE, ICMP_ECHO); + proto_field_set_u8(hdr, ICMPV4_CODE, 0); } + | K_ECHO_REPLY + { proto_field_set_u8(hdr, ICMPV4_TYPE, ICMP_ECHOREPLY); + proto_field_set_u8(hdr, ICMPV4_CODE, 0); } + ; + +icmp4 + : K_ICMP4 { proto_add(PROTO_ICMP4); } + ; + icmpv6_proto : icmp6 '(' icmp6_param_list ')' { } diff --git a/trafgen_proto.h b/trafgen_proto.h index 55287043b..822d841f3 100644 --- a/trafgen_proto.h +++ b/trafgen_proto.h @@ -16,6 +16,7 @@ enum proto_id { PROTO_ARP, PROTO_MPLS, PROTO_IP4, + PROTO_ICMP4, PROTO_IP6, PROTO_ICMP6, PROTO_UDP,