/* * netsniff-ng - the packet sniffing beast * By Daniel Borkmann * Copyright 2012 Daniel Borkmann , * Swiss federal institute of technology (ETH Zurich) * Subject to the GPL, version 2. */ /* yaac-func-prefix: yy */ %{ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "trafgen_parser.tab.h" #include "trafgen_conf.h" #include "trafgen_proto.h" #include "trafgen_l2.h" #include "trafgen_l3.h" #include "trafgen_l4.h" #include "built_in.h" #include "die.h" #include "str.h" #include "csum.h" #include "cpp.h" #define YYERROR_VERBOSE 0 #define YYDEBUG 0 #define YYENABLE_NLS 1 #define YYLTYPE_IS_TRIVIAL 1 #define ENABLE_NLS 1 extern FILE *yyin; extern int yylex(void); extern void yy_scan_string(char *); extern void yylex_destroy(); extern void yyerror(const char *); extern int yylineno; extern char *yytext; extern struct packet *packets; extern size_t plen; #define packet_last (plen - 1) #define payload_last (packets[packet_last].len - 1) extern struct packet_dyn *packet_dyn; extern size_t dlen; #define packetd_last (dlen - 1) #define packetdc_last (packet_dyn[packetd_last].clen - 1) #define packetdr_last (packet_dyn[packetd_last].rlen - 1) #define packetds_last (packet_dyn[packetd_last].slen - 1) static int our_cpu, min_cpu = -1, max_cpu = -1; enum field_expr_type_t { FIELD_EXPR_UNKNOWN, FIELD_EXPR_NUMB, FIELD_EXPR_MAC, FIELD_EXPR_IP4_ADDR, FIELD_EXPR_IP6_ADDR, FIELD_EXPR_INC, FIELD_EXPR_RND, }; struct proto_field_expr { enum field_expr_type_t type; struct proto_field *field; union { struct in_addr ip4_addr; struct in6_addr ip6_addr; long long int number; uint8_t bytes[256]; struct proto_field_func func; } val; }; static struct proto_field_expr field_expr; static struct proto_hdr *hdr; static inline int test_ignore(void) { if (min_cpu < 0 && max_cpu < 0) return 0; else if (max_cpu >= our_cpu && min_cpu <= our_cpu) return 0; else return 1; } static inline void __init_new_packet_slot(struct packet *slot) { slot->payload = NULL; slot->len = 0; } static inline void __init_new_counter_slot(struct packet_dyn *slot) { slot->cnt = NULL; slot->clen = 0; } static inline void __init_new_randomizer_slot(struct packet_dyn *slot) { slot->rnd = NULL; slot->rlen = 0; } static inline void __init_new_csum_slot(struct packet_dyn *slot) { slot->csum = NULL; slot->slen = 0; } static inline void __init_new_fields_slot(struct packet_dyn *slot) { slot->fields = NULL; slot->flen = 0; } static inline void __setup_new_counter(struct counter *c, uint8_t start, uint8_t stop, uint8_t stepping, int type) { c->min = start; c->max = stop; c->inc = stepping; c->val = (type == TYPE_INC) ? start : stop; c->off = payload_last; c->type = type; } static inline void __setup_new_randomizer(struct randomizer *r) { r->off = payload_last; } static inline void __setup_new_csum16(struct csum16 *s, off_t from, off_t to, enum csum which) { s->off = payload_last - 1; s->from = from; s->to = to; s->which = which; } static void realloc_packet(void) { if (test_ignore()) return; plen++; packets = xrealloc(packets, plen * sizeof(*packets)); __init_new_packet_slot(&packets[packet_last]); dlen++; packet_dyn = xrealloc(packet_dyn, dlen * sizeof(*packet_dyn)); __init_new_counter_slot(&packet_dyn[packetd_last]); __init_new_randomizer_slot(&packet_dyn[packetd_last]); __init_new_csum_slot(&packet_dyn[packetd_last]); __init_new_fields_slot(&packet_dyn[packetd_last]); } struct packet *current_packet(void) { return &packets[packet_last]; } uint32_t current_packet_id(void) { return packet_last; } struct packet *packet_get(uint32_t id) { return &packets[id]; } static void set_byte(uint8_t val) { struct packet *pkt = &packets[packet_last]; if (test_ignore()) return; pkt->len++; pkt->payload = xrealloc(pkt->payload, pkt->len); pkt->payload[payload_last] = val; } static void set_multi_byte(uint8_t *s, size_t len) { size_t i; for (i = 0; i < len; ++i) set_byte(s[i]); } void set_fill(uint8_t val, size_t len) { size_t i; struct packet *pkt = &packets[packet_last]; if (test_ignore()) return; pkt->len += len; pkt->payload = xrealloc(pkt->payload, pkt->len); for (i = 0; i < len; ++i) pkt->payload[payload_last - i] = val; } static void __set_csum16_dynamic(size_t from, size_t to, enum csum which) { struct packet *pkt = &packets[packet_last]; struct packet_dyn *pktd = &packet_dyn[packetd_last]; pkt->len += 2; pkt->payload = xrealloc(pkt->payload, pkt->len); pktd->slen++; pktd->csum = xrealloc(pktd->csum, pktd->slen * sizeof(struct csum16)); __setup_new_csum16(&pktd->csum[packetds_last], from, to, which); } static void __set_csum16_static(size_t from, size_t to, enum csum which __maybe_unused) { struct packet *pkt = &packets[packet_last]; uint16_t sum; uint8_t *psum; sum = htons(calc_csum(pkt->payload + from, to - from)); psum = (uint8_t *) ∑ set_byte(psum[0]); set_byte(psum[1]); } static inline bool is_dynamic_csum(enum csum which) { switch (which) { case CSUM_UDP: case CSUM_TCP: case CSUM_UDP6: case CSUM_TCP6: return true; default: return false; } } static void set_csum16(size_t from, size_t to, enum csum which) { struct packet *pkt = &packets[packet_last]; struct packet_dyn *pktd = &packet_dyn[packetd_last]; if (test_ignore()) return; if (to < from) { size_t tmp = to; to = from; from = tmp; } bug_on(!(from < to)); if (packet_dyn_has_elems(pktd) || to >= pkt->len || is_dynamic_csum(which)) __set_csum16_dynamic(from, to, which); else __set_csum16_static(from, to, which); } static void set_rnd(size_t len) { size_t i; struct packet *pkt = &packets[packet_last]; if (test_ignore()) return; pkt->len += len; pkt->payload = xrealloc(pkt->payload, pkt->len); for (i = 0; i < len; ++i) pkt->payload[payload_last - i] = (uint8_t) rand(); } static void set_sequential_inc(uint8_t start, size_t len, uint8_t stepping) { size_t i; struct packet *pkt = &packets[packet_last]; if (test_ignore()) return; pkt->len += len; pkt->payload = xrealloc(pkt->payload, pkt->len); for (i = 0; i < len; ++i) { off_t off = len - 1 - i; pkt->payload[payload_last - off] = start; start += stepping; } } static void set_sequential_dec(uint8_t start, size_t len, uint8_t stepping) { size_t i; struct packet *pkt = &packets[packet_last]; if (test_ignore()) return; pkt->len += len; pkt->payload = xrealloc(pkt->payload, pkt->len); for (i = 0; i < len; ++i) { int off = len - 1 - i; pkt->payload[payload_last - off] = start; start -= stepping; } } static void set_dynamic_rnd(void) { struct packet *pkt = &packets[packet_last]; struct packet_dyn *pktd = &packet_dyn[packetd_last]; if (test_ignore()) return; pkt->len++; pkt->payload = xrealloc(pkt->payload, pkt->len); pktd->rlen++; pktd->rnd = xrealloc(pktd->rnd, pktd->rlen * sizeof(struct randomizer)); __setup_new_randomizer(&pktd->rnd[packetdr_last]); } static void set_dynamic_incdec(uint8_t start, uint8_t stop, uint8_t stepping, int type) { struct packet *pkt = &packets[packet_last]; struct packet_dyn *pktd = &packet_dyn[packetd_last]; if (test_ignore()) return; pkt->len++; pkt->payload = xrealloc(pkt->payload, pkt->len); pktd->clen++; pktd->cnt = xrealloc(pktd->cnt, pktd->clen * sizeof(struct counter)); __setup_new_counter(&pktd->cnt[packetdc_last], start, stop, stepping, type); } static void proto_add(enum proto_id pid) { hdr = proto_header_push(pid); } static void proto_field_set(uint32_t fid) { field_expr.field = proto_field_by_id(hdr, fid); } static void proto_field_func_setup(struct proto_field *field, struct proto_field_func *func) { struct packet_dyn *pkt_dyn; proto_field_func_add(field->hdr, field->id, func); pkt_dyn = &packet_dyn[packetd_last]; pkt_dyn->flen++; pkt_dyn->fields = xrealloc(pkt_dyn->fields, pkt_dyn->flen * sizeof(struct proto_field *)); pkt_dyn->fields[pkt_dyn->flen - 1] = field; } static void proto_field_expr_eval(void) { struct proto_field *field = field_expr.field; switch (field_expr.type) { case FIELD_EXPR_NUMB: if (field->len == 1) proto_field_set_u8(hdr, field->id, field_expr.val.number); else if (field->len == 2) proto_field_set_be16(hdr, field->id, field_expr.val.number); else if (field->len == 4) proto_field_set_be32(hdr, field->id, field_expr.val.number); else bug(); break; case FIELD_EXPR_MAC: proto_field_set_bytes(hdr, field->id, field_expr.val.bytes); break; case FIELD_EXPR_IP4_ADDR: proto_field_set_u32(hdr, field->id, field_expr.val.ip4_addr.s_addr); break; case FIELD_EXPR_IP6_ADDR: proto_field_set_bytes(hdr, field->id, (uint8_t *)&field_expr.val.ip6_addr.s6_addr); break; case FIELD_EXPR_INC: case FIELD_EXPR_RND: if (field_expr.val.func.min && field_expr.val.func.min >= field_expr.val.func.max) panic("dinc(): min(%u) can't be >= max(%u)\n", field_expr.val.func.min, field_expr.val.func.max); proto_field_func_setup(field, &field_expr.val.func); break; case FIELD_EXPR_UNKNOWN: default: bug(); } memset(&field_expr, 0, sizeof(field_expr)); } %} %union { struct in_addr ip4_addr; struct in6_addr ip6_addr; long long int number; uint8_t bytes[256]; char *str; } %token K_COMMENT K_FILL K_RND K_SEQINC K_SEQDEC K_DRND K_DINC K_DDEC K_WHITE %token K_CPU K_CSUMIP K_CSUMUDP K_CSUMTCP K_CSUMUDP6 K_CSUMTCP6 K_CONST8 K_CONST16 K_CONST32 K_CONST64 %token K_DADDR K_SADDR K_ETYPE K_TYPE %token K_TIME K_PRIO %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_CODE K_ECHO_REQUEST K_ECHO_REPLY %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 %token K_LABEL K_TC K_LAST K_EXP %token K_ADDR K_MTU %token K_ETH %token K_PAUSE %token K_PFC %token K_VLAN K_MPLS %token K_ARP %token K_IP4 K_IP6 %token K_ICMP4 K_ICMP6 %token K_UDP K_TCP %token ',' '{' '}' '(' ')' '[' ']' ':' '-' '+' '*' '/' '%' '&' '|' '<' '>' '^' %token number string mac ip4_addr ip6_addr %type number expression %type string %type mac %type ip4_addr %type ip6_addr %left '-' '+' '*' '/' '%' '&' '|' '<' '>' '^' %% packets : { } | packets packet { } | packets inline_comment { } | packets K_WHITE { } ; inline_comment : K_COMMENT { } ; cpu_delim : ':' { } | '-' { } ; delimiter_nowhite : ',' { } | ',' K_WHITE { } ; noenforce_white : { } | K_WHITE { } | delimiter_nowhite { } ; skip_white : { } | K_WHITE { } ; packet : '{' noenforce_white payload noenforce_white '}' { min_cpu = max_cpu = -1; proto_packet_finish(); realloc_packet(); } | K_CPU '(' number cpu_delim number ')' ':' noenforce_white '{' noenforce_white payload noenforce_white '}' { min_cpu = $3; max_cpu = $5; if (min_cpu > max_cpu) { int tmp = min_cpu; min_cpu = max_cpu; max_cpu = tmp; } proto_packet_finish(); realloc_packet(); } | K_CPU '(' number ')' ':' noenforce_white '{' noenforce_white payload noenforce_white '}' { min_cpu = max_cpu = $3; proto_packet_finish(); realloc_packet(); } ; payload : elem { } | payload elem_delimiter { } ; delimiter : delimiter_nowhite { } | K_WHITE { } ; elem_delimiter : delimiter elem { } ; elem : number { set_byte((uint8_t) $1); } | string { set_multi_byte((uint8_t *) $1 + 1, strlen($1) - 2); } | fill { } | rnd { } | drnd { } | seqinc { } | seqdec { } | dinc { } | ddec { } | csum { } | const { } | proto { proto_header_finish(hdr); } | inline_comment { } ; expression : number { $$ = $1; } | expression '+' expression { $$ = $1 + $3; } | expression '-' expression { $$ = $1 - $3; } | expression '*' expression { $$ = $1 * $3; } | expression '/' expression { $$ = $1 / $3; } | expression '%' expression { $$ = $1 % $3; } | expression '&' expression { $$ = $1 & $3; } | expression '|' expression { $$ = $1 | $3; } | expression '^' expression { $$ = $1 ^ $3; } | expression '<' '<' expression { $$ = $1 << $4; } | expression '>' '>' expression { $$ = $1 >> $4; } | '-' expression { $$ = -1 * $2; } | '(' expression ')' { $$ = $2;} ; fill : K_FILL '(' number delimiter number ')' { set_fill($3, $5); } ; const : K_CONST8 '(' expression ')' { set_byte((uint8_t) $3); } | K_CONST16 '(' expression ')' { uint16_t __c = cpu_to_be16((uint16_t) $3); set_multi_byte((uint8_t *) &__c, sizeof(__c)); } | K_CONST32 '(' expression ')' { uint32_t __c = cpu_to_be32((uint32_t) $3); set_multi_byte((uint8_t *) &__c, sizeof(__c)); } | K_CONST64 '(' expression ')' { uint64_t __c = cpu_to_be64((uint64_t) $3); set_multi_byte((uint8_t *) &__c, sizeof(__c)); } ; rnd : K_RND '(' number ')' { set_rnd($3); } ; csum : K_CSUMIP '(' number delimiter number ')' { set_csum16($3, $5, CSUM_IP); } | K_CSUMTCP '(' number delimiter number ')' { set_csum16($3, $5, CSUM_TCP); } | K_CSUMUDP '(' number delimiter number ')' { set_csum16($3, $5, CSUM_UDP); } | K_CSUMTCP6 '(' number delimiter number ')' { set_csum16($3, $5, CSUM_TCP6); } | K_CSUMUDP6 '(' number delimiter number ')' { set_csum16($3, $5, CSUM_UDP6); } ; seqinc : K_SEQINC '(' number delimiter number ')' { set_sequential_inc($3, $5, 1); } | K_SEQINC '(' number delimiter number delimiter number ')' { set_sequential_inc($3, $5, $7); } ; seqdec : K_SEQDEC '(' number delimiter number ')' { set_sequential_dec($3, $5, 1); } | K_SEQDEC '(' number delimiter number delimiter number ')' { set_sequential_dec($3, $5, $7); } ; drnd : K_DRND '(' ')' { set_dynamic_rnd(); } | K_DRND '(' number ')' { int i, max = $3; for (i = 0; i < max; ++i) set_dynamic_rnd(); } ; dinc : K_DINC '(' number delimiter number ')' { set_dynamic_incdec($3, $5, 1, TYPE_INC); } | K_DINC '(' number delimiter number delimiter number ')' { set_dynamic_incdec($3, $5, $7, TYPE_INC); } ; ddec : K_DDEC '(' number delimiter number ')' { set_dynamic_incdec($3, $5, 1, TYPE_DEC); } | K_DDEC '(' number delimiter number delimiter number ')' { set_dynamic_incdec($3, $5, $7, TYPE_DEC); } ; proto : eth_proto { } | pause_proto { } | pfc_proto { } | vlan_proto { } | mpls_proto { } | arp_proto { } | ip4_proto { } | ip6_proto { } | icmp4_proto { } | icmpv6_proto { } | udp_proto { } | tcp_proto { } ; field_expr : number { field_expr.type = FIELD_EXPR_NUMB; field_expr.val.number = $1; } | mac { field_expr.type = FIELD_EXPR_MAC; memcpy(field_expr.val.bytes, $1, sizeof(field_expr.val.bytes)); } | ip4_addr { field_expr.type = FIELD_EXPR_IP4_ADDR; field_expr.val.ip4_addr = $1; } | ip6_addr { field_expr.type = FIELD_EXPR_IP6_ADDR; field_expr.val.ip6_addr = $1; } | K_DINC '(' ')' { field_expr.type = FIELD_EXPR_INC; field_expr.val.func.type = PROTO_FIELD_FUNC_INC; field_expr.val.func.inc = 1; } | K_DINC '(' number ')' { field_expr.type = FIELD_EXPR_INC; field_expr.val.func.type = PROTO_FIELD_FUNC_INC; field_expr.val.func.inc = $3; } | K_DINC '(' number delimiter number ')' { field_expr.type = FIELD_EXPR_INC; field_expr.val.func.type = PROTO_FIELD_FUNC_INC; field_expr.val.func.type |= PROTO_FIELD_FUNC_MIN; field_expr.val.func.min = $3; field_expr.val.func.max = $5; field_expr.val.func.inc = 1; } | K_DINC '(' number delimiter number delimiter number ')' { field_expr.type = FIELD_EXPR_INC; field_expr.val.func.type = PROTO_FIELD_FUNC_INC; field_expr.val.func.type |= PROTO_FIELD_FUNC_MIN; field_expr.val.func.min = $3; field_expr.val.func.max = $5; field_expr.val.func.inc = $7; } | K_DRND '(' ')' { field_expr.type = FIELD_EXPR_RND; field_expr.val.func.type = PROTO_FIELD_FUNC_RND; } | K_DRND '(' number delimiter number ')' { field_expr.type = FIELD_EXPR_RND; field_expr.val.func.type = PROTO_FIELD_FUNC_RND; field_expr.val.func.min = $3; field_expr.val.func.max = $5; } ; eth_proto : eth '(' eth_param_list ')' { } ; eth : K_ETH { proto_add(PROTO_ETH); } ; eth_param_list : { } | eth_expr { } | eth_expr delimiter eth_param_list { } ; eth_type : K_ETYPE { } | K_TYPE { } | K_PROT { } ; eth_field : K_DADDR { proto_field_set(ETH_DST_ADDR); } | K_SADDR { proto_field_set(ETH_SRC_ADDR); } | eth_type { proto_field_set(ETH_TYPE); } eth_expr : eth_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } ; pause_proto : pause '(' pause_param_list ')' { } ; pause : K_PAUSE { proto_add(PROTO_PAUSE); } ; pause_param_list : { } | pause_expr { } | pause_expr delimiter pause_param_list { } ; pause_field : K_CODE { proto_field_set(PAUSE_OPCODE); } | K_TIME { proto_field_set(PAUSE_TIME); } ; pause_expr : pause_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } ; pfc_proto : pfc '(' pfc_param_list ')' { } ; pfc : K_PFC { proto_add(PROTO_PFC); } ; pfc_param_list : { } | pfc_expr { } | pfc_expr delimiter pfc_param_list { } ; pfc_field : K_CODE { proto_field_set(PFC_OPCODE); } | K_PRIO { proto_field_set(PFC_PRIO); } | K_PRIO '(' number ')' { if ($3 > 7) { yyerror("pfc: Invalid prio(index) parameter"); panic("pfc: prio(0)..prio(7) is allowed only\n"); } proto_field_set(PFC_PRIO_0 + $3); } | K_TIME '(' number ')' { if ($3 > 7) { yyerror("pfc: Invalid time(index) parameter"); panic("pfc: time(0)..time(7) is allowed only\n"); } proto_field_set(PFC_TIME_0 + $3); } ; pfc_expr : pfc_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } ; vlan_proto : vlan '(' vlan_param_list ')' { } ; vlan : K_VLAN { proto_add(PROTO_VLAN); } ; vlan_param_list : { } | vlan_expr { } | vlan_expr delimiter vlan_param_list { } ; vlan_type : K_TPID { } | K_PROT ; vlan_field : vlan_type { proto_field_set(VLAN_TPID); } | K_TCI { proto_field_set(VLAN_TCI); } | K_PCP { proto_field_set(VLAN_PCP); } | K_DEI { proto_field_set(VLAN_DEI); } | K_ID { proto_field_set(VLAN_VID); } ; vlan_expr : vlan_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } | K_1Q { proto_field_set_be16(hdr, VLAN_TPID, ETH_P_8021Q); } | K_1AD { proto_field_set_be16(hdr, VLAN_TPID, ETH_P_8021AD); } ; mpls_proto : mpls '(' mpls_param_list ')' { } ; mpls : K_MPLS { proto_add(PROTO_MPLS); } ; mpls_param_list : { } | mpls_expr { } | mpls_expr delimiter mpls_param_list { } ; mpls_tc : K_TC { } | K_EXP { } ; mpls_field : K_LABEL { proto_field_set(MPLS_LABEL); } | mpls_tc { proto_field_set(MPLS_TC); } | K_LAST { proto_field_set(MPLS_LAST); } | K_TTL { proto_field_set(MPLS_TTL); } ; mpls_expr : mpls_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } arp_proto : arp '(' arp_param_list ')' { } ; arp_param_list : { } | arp_expr { } | arp_expr delimiter arp_param_list { } ; arp_field : K_HTYPE { proto_field_set(ARP_HTYPE); } | K_PTYPE { proto_field_set(ARP_PTYPE); } | K_SHA { proto_field_set(ARP_SHA); } | K_THA { proto_field_set(ARP_THA); } | K_SPA { proto_field_set(ARP_SPA); } | K_TPA { proto_field_set(ARP_TPA); } ; arp_expr : arp_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } | K_OPER skip_white '=' skip_white field_expr { proto_field_set(ARP_OPER); proto_field_expr_eval(); } | K_OPER skip_white '=' skip_white K_REQUEST { proto_field_set_be16(hdr, ARP_OPER, ARPOP_REQUEST); } | K_OPER skip_white '=' skip_white K_REPLY { proto_field_set_be16(hdr, ARP_OPER, ARPOP_REPLY); } | K_REQUEST { proto_field_set_be16(hdr, ARP_OPER, ARPOP_REQUEST); } | K_REPLY { proto_field_set_be16(hdr, ARP_OPER, ARPOP_REPLY); } ; arp : K_ARP { proto_add(PROTO_ARP); } ; ip4_proto : ip4 '(' ip4_param_list ')' { } ; ip4_param_list : { } | ip4_expr { } | ip4_expr delimiter ip4_param_list { } ; ip4_field : K_VER { proto_field_set(IP4_VER); } | K_IHL { proto_field_set(IP4_IHL); } | K_DADDR { proto_field_set(IP4_DADDR); } | K_SADDR { proto_field_set(IP4_SADDR); } | K_PROT { proto_field_set(IP4_PROTO); } | K_TTL { proto_field_set(IP4_TTL); } | K_DSCP { proto_field_set(IP4_DSCP); } | K_ECN { proto_field_set(IP4_ECN); } | K_TOS { proto_field_set(IP4_TOS); } | K_LEN { proto_field_set(IP4_LEN); } | K_ID { proto_field_set(IP4_ID); } | K_FLAGS { proto_field_set(IP4_FLAGS); } | K_FRAG { proto_field_set(IP4_FRAG_OFFS); } | K_CSUM { proto_field_set(IP4_CSUM); } ; ip4_expr : ip4_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } | K_DF { proto_field_set_be16(hdr, IP4_DF, 1); } | K_MF { proto_field_set_be16(hdr, IP4_MF, 1); } ; ip4 : K_IP4 { proto_add(PROTO_IP4); } ; ip6_proto : ip6 '(' ip6_param_list ')' { } ; ip6_param_list : { } | ip6_expr { } | ip6_expr delimiter ip6_param_list { } ; ip6_hop_limit : K_HOP_LIMIT { } | K_TTL { } ; ip6_field : K_VER { proto_field_set(IP6_VER); } | K_TC { proto_field_set(IP6_CLASS); } | K_FLOW { proto_field_set(IP6_FLOW_LBL); } | K_LEN { proto_field_set(IP6_LEN); } | K_NEXT_HDR { proto_field_set(IP6_NEXT_HDR); } | ip6_hop_limit { proto_field_set(IP6_HOP_LIMIT); } | K_SADDR { proto_field_set(IP6_SADDR); } | K_DADDR { proto_field_set(IP6_DADDR) ; } ; ip6_expr : ip6_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } ; ip6 : K_IP6 { proto_add(PROTO_IP6); } ; icmp4_proto : icmp4 '(' icmp4_param_list ')' { } ; icmp4_param_list : { } | icmp4_expr { } | icmp4_expr delimiter icmp4_param_list { } ; icmp4_field : K_TYPE { proto_field_set(ICMPV4_TYPE); } | K_CODE { proto_field_set(ICMPV4_CODE); } | K_ID { proto_field_set(ICMPV4_ID); } | K_SEQ { proto_field_set(ICMPV4_SEQ); } | K_MTU { proto_field_set(ICMPV4_MTU); } | K_ADDR { proto_field_set(ICMPV4_REDIR_ADDR); } ; icmp4_expr : icmp4_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } | 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 ')' { } icmp6_param_list : { } | icmp6_expr { } | icmp6_expr delimiter icmp6_param_list { } ; icmp6_field : K_CODE { proto_field_set(ICMPV6_CODE); } | K_CSUM { proto_field_set(ICMPV6_CSUM); } ; icmp6_expr : icmp6_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } | K_TYPE skip_white '=' skip_white field_expr { proto_field_set(ICMPV6_TYPE); proto_field_expr_eval(); } | K_TYPE skip_white '=' K_ECHO_REQUEST { proto_field_set_u8(hdr, ICMPV6_TYPE, ICMPV6_ECHO_REQUEST); } | K_ECHO_REQUEST { proto_field_set_u8(hdr, ICMPV6_TYPE, ICMPV6_ECHO_REQUEST); } | K_TYPE skip_white '=' K_ECHO_REPLY { proto_field_set_u8(hdr, ICMPV6_TYPE, ICMPV6_ECHO_REPLY); } | K_ECHO_REPLY { proto_field_set_u8(hdr, ICMPV6_TYPE, ICMPV6_ECHO_REPLY); } ; icmp6 : K_ICMP6 { proto_add(PROTO_ICMP6); } ; udp_proto : udp '(' udp_param_list ')' { } ; udp_param_list : { } | udp_expr { } | udp_expr delimiter udp_param_list { } ; udp_field : K_SPORT { proto_field_set(UDP_SPORT); } | K_DPORT { proto_field_set(UDP_DPORT); } | K_LEN { proto_field_set(UDP_LEN); } | K_CSUM { proto_field_set(UDP_CSUM); } ; udp_expr : udp_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } ; udp : K_UDP { proto_add(PROTO_UDP); } ; tcp_proto : tcp '(' tcp_param_list ')' { } ; tcp_param_list : { } | tcp_expr { } | tcp_expr delimiter tcp_param_list { } ; tcp_field : K_SPORT { proto_field_set(TCP_SPORT); } | K_DPORT { proto_field_set(TCP_DPORT); } | K_SEQ { proto_field_set(TCP_SEQ); } | K_ACK_SEQ { proto_field_set(TCP_ACK_SEQ); } | K_DOFF { proto_field_set(TCP_DOFF); } | K_WINDOW { proto_field_set(TCP_WINDOW); } | K_CSUM { proto_field_set(TCP_CSUM); } | K_URG_PTR { proto_field_set(TCP_URG_PTR); } ; tcp_expr : tcp_field skip_white '=' skip_white field_expr { proto_field_expr_eval(); } | 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); } ; tcp : K_TCP { proto_add(PROTO_TCP); } ; %% static void finalize_packet(void) { /* XXX hack ... we allocated one packet pointer too much */ plen--; dlen--; } static void dump_conf(void) { size_t i, j; for (i = 0; i < plen; ++i) { printf("[%zu] pkt\n", i); printf(" len %zu cnts %zu rnds %zu\n", packets[i].len, packet_dyn[i].clen, packet_dyn[i].rlen); printf(" payload "); for (j = 0; j < packets[i].len; ++j) printf("%02x ", packets[i].payload[j]); printf("\n"); for (j = 0; j < packet_dyn[i].clen; ++j) printf(" cnt%zu [%u,%u], inc %u, off %jd type %s\n", j, packet_dyn[i].cnt[j].min, packet_dyn[i].cnt[j].max, packet_dyn[i].cnt[j].inc, (intmax_t)packet_dyn[i].cnt[j].off, packet_dyn[i].cnt[j].type == TYPE_INC ? "inc" : "dec"); for (j = 0; j < packet_dyn[i].rlen; ++j) printf(" rnd%zu off %jd\n", j, (intmax_t)packet_dyn[i].rnd[j].off); } } void cleanup_packets(void) { size_t i, j; for (i = 0; i < plen; ++i) { struct packet *pkt = &packets[i]; if (pkt->len > 0) xfree(pkt->payload); for (j = 0; j < pkt->headers_count; j++) { struct proto_hdr *hdr = pkt->headers[j]; if (hdr->fields) xfree(hdr->fields); xfree(hdr); } } free(packets); for (i = 0; i < dlen; ++i) { free(packet_dyn[i].cnt); free(packet_dyn[i].rnd); free(packet_dyn[i].fields); } free(packet_dyn); } void compile_packets(char *file, bool verbose, unsigned int cpu, bool invoke_cpp, char *const cpp_argv[]) { char tmp_file[128]; int ret = -1; if (access(file, R_OK)) { fprintf(stderr, "Cannot access %s: %s!\n", file, strerror(errno)); die(); } memset(tmp_file, 0, sizeof(tmp_file)); our_cpu = cpu; if (invoke_cpp) { if (cpp_exec(file, tmp_file, sizeof(tmp_file), cpp_argv)) { fprintf(stderr, "Failed to invoke C preprocessor!\n"); goto err; } file = tmp_file; } if (!strncmp("-", file, strlen("-"))) yyin = stdin; else yyin = fopen(file, "r"); if (!yyin) { fprintf(stderr, "Cannot open %s: %s!\n", file, strerror(errno)); goto err; } realloc_packet(); if (yyparse() != 0) goto err; finalize_packet(); if (our_cpu == 0 && verbose) dump_conf(); ret = 0; err: if (yyin && yyin != stdin) fclose(yyin); if (invoke_cpp) unlink(tmp_file); if (ret) die(); } void compile_packets_str(char *str, bool verbose, unsigned int cpu) { int ret = 1; our_cpu = cpu; realloc_packet(); yy_scan_string(str); if (yyparse() != 0) goto err; finalize_packet(); if (our_cpu == 0 && verbose) dump_conf(); ret = 0; err: yylex_destroy(); if (ret) die(); } void yyerror(const char *err) { fprintf(stderr, "Syntax error at line %d, char '%s': %s\n", yylineno, yytext, err); }