From e6c1b6ca7fd51a9b7d2b3c597cd9731b59330032 Mon Sep 17 00:00:00 2001 From: Vadim Kochan Date: Thu, 1 Jun 2017 13:12:29 +0300 Subject: trafgen: parser: Add syntax to generate DNS header Add new syntax for DNS header generation via 'dns()' proto function. The fields are supported: id - 16 bit identifier qr - message is a query(0) or response(1) op|oper - specified kind of query aanswer - authoritative answer flag trunc - message was truncated flag rdesired - recursion desired flag ravail - recursion available flag zero - reserved for future use rcode - response code qdcount - number of entries in question section ancount - number of entries in answer section nscount - number of entries in authority section arcount - number of entries in additional section Also there are functions to generate DNS sections: 'qry()' function to generate separate query entry: name - variable domain name type - type of the query class - class of the query 'ans()', 'auth()', 'add' functions to generate separate answer, authoritative, adidditional entry with the same fields layout: name - variable domain name type - resource record type class - class of the data ttl - time interval that the record may be cached len - length of data data - variable length of bytes All the DNS section entries will be automaticlly sorted by DNS proto API in the way which is required by DNS header: query entries answer entries authoritative entries additional entries 'name' field in qry/ans/auth/add functions is automatically converted to FQDN format if it was specified as "string". There are also added functions to simplify the way of filling some often used RR types for using them inside ans/auth/add functions: addr(ipv4_addr | ipv6_addr) - fills the following RR fields: len - 4 or 16 depends on IPv4 or IPv6 address was specified data - is filled with IPv4 or IPv6 address type - 1 for IPv4 address, 28 - for IPv6 ns(string) type - 2 cname(string) type - 5 ptr(string) type - 12 EXAMPLES: { dns(qr=1, auth(name="ns1", ns("ns1.org")), ans(name="www.google.com", cname("google.com")), auth(name="aa", ns("bb")), qry(name="www.google.com")) } { dns(qr=1, ans(name="www.google.com", addr(1.2.3.4))) } { dns(qr=1, ans(name="www.google.com", addr(1::))) } Signed-off-by: Vadim Kochan Signed-off-by: Tobias Klauser --- trafgen_lexer.l | 24 +++++++ trafgen_parser.y | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) diff --git a/trafgen_lexer.l b/trafgen_lexer.l index cf67c74..da072e1 100644 --- a/trafgen_lexer.l +++ b/trafgen_lexer.l @@ -239,6 +239,29 @@ ip6_addr ({v680}|{v67}|{v66}|{v65}|{v64}|{v63}|{v62}|{v61}|{v60}) "win"|"window" { return K_WINDOW; } "urgptr" { return K_URG_PTR; } + /* DNS */ +"qr" { return K_QR; } +"aa"|"aanswer" { return K_AANSWER; } +"trunc" { return K_TRUNC; } +"ravail" { return K_RAVAIL; } +"rdesired" { return K_RDESIRED; } +"zero" { return K_ZERO; } +"rc"|"rcode" { return K_RCODE; } +"qdcount" { return K_QDCOUNT; } +"ancount" { return K_ANCOUNT; } +"nscount" { return K_NSCOUNT; } +"arcount" { return K_ARCOUNT; } +"name" { return K_NAME; } +"class" { return K_CLASS; } +"data" { return K_DATA; } +"qry"|"query" { return K_QUERY; } +"ans"|"answer" { return K_ANSWER; } +"auth" { return K_AUTH; } +"add" { return K_ADD; } +"ns" { return K_NS; } +"cname" { return K_CNAME; } +"ptr" { return K_PTR; } + "eth" { return K_ETH; } "pause" { return K_PAUSE; } "pfc" { return K_PFC; } @@ -251,6 +274,7 @@ ip6_addr ({v680}|{v67}|{v66}|{v65}|{v64}|{v63}|{v62}|{v61}|{v60}) "icmp6"|"icmpv6" { return K_ICMP6; } "udp" { return K_UDP; } "tcp" { return K_TCP; } +"dns" { return K_DNS; } [ ]*"-"[ ]* { return '-'; } [ ]*"+"[ ]* { return '+'; } diff --git a/trafgen_parser.y b/trafgen_parser.y index b4eedea..656c4f6 100644 --- a/trafgen_parser.y +++ b/trafgen_parser.y @@ -31,6 +31,7 @@ #include "trafgen_l2.h" #include "trafgen_l3.h" #include "trafgen_l4.h" +#include "trafgen_l7.h" #include "built_in.h" #include "die.h" #include "str.h" @@ -80,6 +81,8 @@ enum field_expr_type_t { FIELD_EXPR_INC = 1 << 4, FIELD_EXPR_RND = 1 << 5, FIELD_EXPR_OFFSET = 1 << 6, + FIELD_EXPR_STRING = 1 << 7, + FIELD_EXPR_FQDN = 1 << 8, }; struct proto_field_expr { @@ -91,6 +94,7 @@ struct proto_field_expr { struct in6_addr ip6_addr; long long int number; uint8_t mac[256]; + char *str; struct proto_field_func func; } val; }; @@ -434,6 +438,14 @@ static void proto_field_expr_eval(void) panic("Invalid value length %zu, can be 1,2 or 4\n", field->len); } else if (field_expr.type & FIELD_EXPR_MAC) { proto_field_set_bytes(field, field_expr.val.mac, 6); + } else if (field_expr.type & FIELD_EXPR_FQDN) { + char *fqdn = str2fqdn(field_expr.val.str); + proto_field_set_bytes(field, (uint8_t *) fqdn, strlen(fqdn) + 1); + xfree(field_expr.val.str); + xfree(fqdn); + } else if (field_expr.type & FIELD_EXPR_STRING) { + proto_field_set_string(field, field_expr.val.str); + xfree(field_expr.val.str); } else if (field_expr.type & FIELD_EXPR_IP4_ADDR) { proto_field_set_u32(field, field_expr.val.ip4_addr.s_addr); } else if (field_expr.type & FIELD_EXPR_IP6_ADDR) { @@ -472,6 +484,19 @@ static void field_index_validate(struct proto_field *field, uint16_t index, size } } +static void proto_push_sub_hdr(uint32_t id) +{ + hdr = proto_hdr_push_sub_header(hdr, id); +} + +static void proto_pop_sub_hdr(void) +{ + if (hdr->ops->header_finish) + hdr->ops->header_finish(hdr); + + hdr = hdr->parent; +} + %} %union { @@ -498,6 +523,10 @@ static void field_index_validate(struct proto_field *field, uint16_t index, size %token K_ADDR K_MTU +%token K_QR K_AANSWER K_TRUNC K_RAVAIL K_RDESIRED K_ZERO K_RCODE K_QDCOUNT K_ANCOUNT K_NSCOUNT K_ARCOUNT +%token K_QUERY K_ANSWER K_AUTH K_ADD +%token K_NAME K_CLASS K_DATA K_NS K_CNAME K_PTR + %token K_ETH %token K_PAUSE %token K_PFC @@ -506,6 +535,7 @@ static void field_index_validate(struct proto_field *field, uint16_t index, size %token K_IP4 K_IP6 %token K_ICMP4 K_ICMP6 %token K_UDP K_TCP +%token K_DNS %token ',' '{' '}' '(' ')' '[' ']' ':' '-' '+' '*' '/' '%' '&' '|' '<' '>' '^' @@ -738,6 +768,7 @@ proto | icmpv6_proto { } | udp_proto { } | tcp_proto { } + | dns_proto { } ; field_expr @@ -758,6 +789,9 @@ field_value_expr field_expr.val.number = $1; } | mac { field_expr.type |= FIELD_EXPR_MAC; memcpy(field_expr.val.mac, $1, sizeof(field_expr.val.mac)); } + | string { field_expr.type |= FIELD_EXPR_STRING; + field_expr.val.str = xstrdup($1 + 1); + field_expr.val.str[strlen($1 + 1) - 1] = '\0'; } | ip4_addr { field_expr.type |= FIELD_EXPR_IP4_ADDR; field_expr.val.ip4_addr = $1; } | ip6_addr { field_expr.type |= FIELD_EXPR_IP6_ADDR; @@ -1229,6 +1263,157 @@ tcp : K_TCP { proto_add(PROTO_TCP); } ; +dns_proto + : dns '(' dns_param_list ')' { } + ; + +dns_param_list + : { } + | dns_expr { } + | dns_expr delimiter dns_param_list { } + ; + +dns_field + : K_ID { proto_field_set(DNS_ID); } + | K_QR { proto_field_set(DNS_QR); } + | K_OPER { proto_field_set(DNS_OPCODE); } + | K_AANSWER { proto_field_set(DNS_AA); } + | K_TRUNC { proto_field_set(DNS_TC); } + | K_RDESIRED { proto_field_set(DNS_RD); } + | K_RAVAIL { proto_field_set(DNS_RA); } + | K_ZERO { proto_field_set(DNS_ZERO); } + | K_RCODE { proto_field_set(DNS_RCODE); } + | K_QDCOUNT { proto_field_set(DNS_QD_COUNT); } + | K_ANCOUNT { proto_field_set(DNS_AN_COUNT); } + | K_NSCOUNT { proto_field_set(DNS_NS_COUNT); } + | K_ARCOUNT { proto_field_set(DNS_AR_COUNT); } + ; + +dns_query + : K_QUERY { proto_push_sub_hdr(DNS_QUERY_HDR); } + ; + +dns_query_name + : K_NAME { proto_field_set(DNS_QUERY_NAME); } + ; + +dns_query_field + : K_TYPE { proto_field_set(DNS_QUERY_TYPE); } + | K_CLASS { proto_field_set(DNS_QUERY_CLASS); } + ; + +dns_query_expr + : dns_query_field field_expr skip_white '=' skip_white field_value_expr + { proto_field_expr_eval(); } + | dns_query_field skip_white '=' skip_white field_value_expr + { proto_field_expr_eval(); } + | dns_query_name field_expr skip_white '=' skip_white field_value_expr + { if (field_expr.type & FIELD_EXPR_STRING) + field_expr.type = FIELD_EXPR_FQDN; + proto_field_expr_eval(); } + | dns_query_name skip_white '=' skip_white field_value_expr + { if (field_expr.type & FIELD_EXPR_STRING) + field_expr.type = FIELD_EXPR_FQDN; + proto_field_expr_eval(); } + ; + +dns_query_param_list + : { } + | dns_query_expr { } + | dns_query_expr delimiter dns_query_param_list { } + ; + +dns_query_hdr + : dns_query '(' dns_query_param_list ')' { } + ; + +dns_rrecord + : K_ANSWER { proto_push_sub_hdr(DNS_ANSWER_HDR); } + | K_AUTH { proto_push_sub_hdr(DNS_AUTH_HDR); } + | K_ADD { proto_push_sub_hdr(DNS_ADD_HDR); } + ; + +dns_rrecord_name + : K_NAME { proto_field_set(DNS_RRECORD_NAME); } + ; + +dns_rrecord_data_addr + : ip4_addr + { proto_hdr_field_set_u32(hdr, DNS_RRECORD_DATA, $1.s_addr); + proto_hdr_field_set_be16(hdr, DNS_RRECORD_TYPE, 1); } + | ip6_addr + { proto_hdr_field_set_bytes(hdr, DNS_RRECORD_DATA, (uint8_t *)&$1.s6_addr, 16); + proto_hdr_field_set_be16(hdr, DNS_RRECORD_TYPE, 28); } + ; + +dns_rrecord_data_fqdn + : string + { char *str = xstrdup($1 + 1); + char *fqdn; + str[strlen($1 + 1) - 1] = '\0'; + fqdn = str2fqdn(str); + proto_hdr_field_set_bytes(hdr, DNS_RRECORD_DATA, (uint8_t *) fqdn, strlen(fqdn) + 1); + xfree(fqdn); } + ; + +dns_rrecord_data_expr + : K_ADDR '(' skip_white dns_rrecord_data_addr skip_white ')' + { } + | K_NS '(' skip_white dns_rrecord_data_fqdn skip_white ')' + { proto_hdr_field_set_be16(hdr, DNS_RRECORD_TYPE, 2); } + | K_CNAME '(' skip_white dns_rrecord_data_fqdn skip_white ')' + { proto_hdr_field_set_be16(hdr, DNS_RRECORD_TYPE, 5); } + | K_PTR '(' skip_white dns_rrecord_data_fqdn skip_white ')' + { proto_hdr_field_set_be16(hdr, DNS_RRECORD_TYPE, 12); } + ; + +dns_rrecord_field + : K_TYPE { proto_field_set(DNS_RRECORD_TYPE); } + | K_CLASS { proto_field_set(DNS_RRECORD_CLASS); } + | K_TTL { proto_field_set(DNS_RRECORD_TTL); } + | K_LEN { proto_field_set(DNS_RRECORD_LEN); } + | K_DATA { proto_field_set(DNS_RRECORD_DATA); } + ; + +dns_rrecord_expr + : dns_rrecord_field field_expr skip_white '=' skip_white field_value_expr + { proto_field_expr_eval(); } + | dns_rrecord_field skip_white '=' skip_white field_value_expr + { proto_field_expr_eval(); } + | dns_rrecord_name field_expr skip_white '=' skip_white field_value_expr + { if (field_expr.type & FIELD_EXPR_STRING) + field_expr.type = FIELD_EXPR_FQDN; + proto_field_expr_eval(); } + | dns_rrecord_name skip_white '=' skip_white field_value_expr + { if (field_expr.type & FIELD_EXPR_STRING) + field_expr.type = FIELD_EXPR_FQDN; + proto_field_expr_eval(); } + | dns_rrecord_data_expr + { } + ; + +dns_rrecord_param_list + : { } + | dns_rrecord_expr { } + | dns_rrecord_expr delimiter dns_rrecord_param_list { } + ; + +dns_rrecord_hdr + : dns_rrecord '(' dns_rrecord_param_list ')' { } + ; + +dns_expr + : dns_field field_expr skip_white '=' skip_white field_value_expr + { proto_field_expr_eval(); } + | dns_field skip_white '=' skip_white field_value_expr + { proto_field_expr_eval(); } + | dns_query_hdr { proto_pop_sub_hdr(); } + | dns_rrecord_hdr { proto_pop_sub_hdr(); } + ; + +dns + : K_DNS { proto_add(PROTO_DNS); } + ; %% static void finalize_packet(void) @@ -1281,9 +1466,17 @@ void cleanup_packets(void) for (j = 0; j < pkt->headers_count; j++) { struct proto_hdr *hdr = pkt->headers[j]; + int k; + + for (k = 0; k < hdr->sub_headers_count; k++) + xfree(hdr->sub_headers[k]); + + if (hdr->sub_headers) + xfree(hdr->sub_headers); if (hdr->fields) xfree(hdr->fields); + xfree(hdr); } } -- cgit v1.2.3-54-g00ecf