Add trafgen_l7.c module with DNS proto header generation with
support of filling DNS query/answer/authority/additional sections
as sub headers.

Introcuded new concept as 'sub header' which is needed to easy handle
DNS sections which might be added on-demand, and to simplify using
sub-header as regular header with a fields, offset, etc. There is a
parent header which contains array of pointers of sub-headers, and the
array is ordered as they are located in the parent header. The
sub-headers mostly encapsulated by the parent header which 'knows'
the semantic of them. The new proto_hdr->push_sub_header(...) callback
was added to tell the parent header to push the sub-header's fields,
sub-header also may have proto_ops which must be filled by the parent.
This sub-header concept might be used in the future if it will be needed
to support DHCP, WLAN headers.

There are 4 kinds of DNS sub-headers - query, answer, authority,
additional. 'id' of each sub-header is used to only differentiate these
types of sections. These sections have strict order inside DNS header,
and there was added the proto_hdr_move_sub_header(...) to sort them in
required order.

Actually there are only 2 proto_hdr's which describes 4 DNS sections -
query & rrecord, because rrecord covers another 3 - answer, auhority,
additional which have the same layout.

Signed-off-by: Vadim Kochan <vadi...@gmail.com>
---
 trafgen/Makefile |   1 +
 trafgen_l4.c     |  32 ++++++++++
 trafgen_l7.c     | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 trafgen_l7.h     |  45 ++++++++++++++
 trafgen_proto.c  | 128 ++++++++++++++++++++++++++++++++++++++++
 trafgen_proto.h  |  18 +++++-
 6 files changed, 398 insertions(+), 1 deletion(-)
 create mode 100644 trafgen_l7.c
 create mode 100644 trafgen_l7.h

diff --git a/trafgen/Makefile b/trafgen/Makefile
index 876ed93..95a31e0 100644
--- a/trafgen/Makefile
+++ b/trafgen/Makefile
@@ -25,6 +25,7 @@ trafgen-objs =        xmalloc.o \
                trafgen_l2.o \
                trafgen_l3.o \
                trafgen_l4.o \
+               trafgen_l7.o \
                trafgen_lexer.yy.o \
                trafgen_parser.tab.o \
                trafgen.o
diff --git a/trafgen_l4.c b/trafgen_l4.c
index 5a694b3..198d622 100644
--- a/trafgen_l4.c
+++ b/trafgen_l4.c
@@ -80,6 +80,21 @@ static void udp_packet_finish(struct proto_hdr *hdr)
        udp_csum_update(hdr);
 }
 
+static void udp_set_next_proto(struct proto_hdr *hdr, enum proto_id pid)
+{
+       uint16_t dport;
+
+       switch (pid) {
+       case PROTO_DNS:
+               dport = 53;
+               break;
+       default:
+               bug();
+       }
+
+       proto_hdr_field_set_default_be16(hdr, UDP_DPORT, dport);
+}
+
 static const struct proto_ops udp_proto_ops = {
        .id             = PROTO_UDP,
        .layer          = PROTO_L4,
@@ -87,6 +102,7 @@ static const struct proto_ops udp_proto_ops = {
        .packet_update  = udp_csum_update,
        .packet_finish  = udp_packet_finish,
        .field_changed  = udp_field_changed,
+       .set_next_proto = udp_set_next_proto,
 };
 
 static struct proto_field tcp_fields[] = {
@@ -160,6 +176,21 @@ static void tcp_csum_update(struct proto_hdr *hdr)
        hdr->is_csum_valid = true;
 }
 
+static void tcp_set_next_proto(struct proto_hdr *hdr, enum proto_id pid)
+{
+       uint16_t dport;
+
+       switch (pid) {
+       case PROTO_DNS:
+               dport = 53;
+               break;
+       default:
+               bug();
+       }
+
+       proto_hdr_field_set_default_be16(hdr, TCP_DPORT, dport);
+}
+
 static const struct proto_ops tcp_proto_ops = {
        .id             = PROTO_TCP,
        .layer          = PROTO_L4,
@@ -167,6 +198,7 @@ static const struct proto_ops tcp_proto_ops = {
        .packet_update  = tcp_csum_update,
        .packet_finish  = tcp_csum_update,
        .field_changed  = tcp_field_changed,
+       .set_next_proto = tcp_set_next_proto,
 };
 
 static struct proto_field icmpv4_fields[] = {
diff --git a/trafgen_l7.c b/trafgen_l7.c
new file mode 100644
index 0000000..1e82ccb
--- /dev/null
+++ b/trafgen_l7.c
@@ -0,0 +1,175 @@
+/*
+ * netsniff-ng - the packet sniffing beast
+ * Subject to the GPL, version 2.
+ */
+
+#include <string.h>
+
+#include "str.h"
+#include "xmalloc.h"
+#include "built_in.h"
+#include "trafgen_l7.h"
+#include "trafgen_proto.h"
+
+static struct proto_field dns_fields[] = {
+       { .id = DNS_ID,       .len = 2, .offset = 0 },
+       { .id = DNS_QR,       .len = 2, .offset = 2, .shift = 15, .mask = 
0x8000 },
+       { .id = DNS_OPCODE,   .len = 2, .offset = 2, .shift = 11, .mask = 
0x7800 },
+       { .id = DNS_AA,       .len = 2, .offset = 2, .shift = 10, .mask = 
0x0400 },
+       { .id = DNS_TC,       .len = 2, .offset = 2, .shift = 9,  .mask = 
0x0200 },
+       { .id = DNS_RD,       .len = 2, .offset = 2, .shift = 8,  .mask = 
0x0100 },
+       { .id = DNS_RA,       .len = 2, .offset = 2, .shift = 7,  .mask = 0x80 
},
+       { .id = DNS_ZERO,     .len = 2, .offset = 2, .shift = 4,  .mask = 0x30 
},
+       { .id = DNS_RCODE,    .len = 2, .offset = 2, .shift = 0,  .mask = 0xf },
+       { .id = DNS_QD_COUNT, .len = 2, .offset = 4, },
+       { .id = DNS_AN_COUNT, .len = 2, .offset = 6, },
+       { .id = DNS_NS_COUNT, .len = 2, .offset = 8, },
+       { .id = DNS_AR_COUNT, .len = 2, .offset = 10, },
+};
+
+static struct proto_field dns_query_fields[] = {
+       { .id = DNS_QUERY_NAME,  .len = 0, .offset = 0 },
+       { .id = DNS_QUERY_TYPE,  .len = 2, .offset = 0 },
+       { .id = DNS_QUERY_CLASS, .len = 2, .offset = 2 },
+};
+
+static void dns_query_header_init(struct proto_hdr *hdr)
+{
+       proto_header_fields_add(hdr, dns_query_fields, 
array_size(dns_query_fields));
+}
+
+static void dns_query_header_finish(struct proto_hdr *hdr)
+{
+       proto_hdr_field_set_default_string(hdr, DNS_QUERY_NAME, 
"www.netsniff-ng.com");
+       proto_hdr_field_set_default_be16(hdr, DNS_QUERY_CLASS, 1);
+       proto_hdr_field_set_default_be16(hdr, DNS_QUERY_TYPE, 1);
+}
+
+static const struct proto_ops dns_proto_query_ops = {
+       .header_init    = dns_query_header_init,
+       .header_finish  = dns_query_header_finish,
+};
+
+static struct proto_field dns_rrecord_fields[] = {
+       { .id = DNS_RRECORD_NAME,  .len = 0, .offset = 0 },
+       { .id = DNS_RRECORD_TYPE,  .len = 2, .offset = 0 },
+       { .id = DNS_RRECORD_CLASS, .len = 2, .offset = 2 },
+       { .id = DNS_RRECORD_TTL,   .len = 4, .offset = 4 },
+       { .id = DNS_RRECORD_LEN,   .len = 2, .offset = 8 },
+       { .id = DNS_RRECORD_DATA,  .len = 0, .offset = 10 },
+};
+
+static void dns_rrecord_header_init(struct proto_hdr *hdr)
+{
+       proto_header_fields_add(hdr, dns_rrecord_fields, 
array_size(dns_rrecord_fields));
+}
+
+static void dns_rrecord_header_finish(struct proto_hdr *hdr)
+{
+       struct proto_field *data = proto_hdr_field_by_id(hdr, DNS_RRECORD_DATA);
+
+       proto_hdr_field_set_default_be32(hdr, DNS_RRECORD_TTL, 1);
+       proto_hdr_field_set_default_be16(hdr, DNS_RRECORD_CLASS, 1);
+       proto_hdr_field_set_default_be16(hdr, DNS_RRECORD_LEN, data->len);
+}
+
+static const struct proto_ops dns_proto_rrecord_ops = {
+       .header_init    = dns_rrecord_header_init,
+       .header_finish  = dns_rrecord_header_finish,
+};
+
+static void dns_header_init(struct proto_hdr *hdr)
+{
+       proto_lower_default_add(hdr, PROTO_UDP);
+
+       proto_header_fields_add(hdr, dns_fields, array_size(dns_fields));
+}
+
+static void dns_sort_headers(struct proto_hdr *hdr, uint32_t id, int index)
+{
+       int i;
+
+       for (i = index; i < hdr->headers_count; i++) {
+               struct proto_hdr *sub_hdr = hdr->sub_headers[i];
+
+               if (sub_hdr->id == id && sub_hdr->index != index) {
+                       proto_hdr_move_sub_header(hdr, sub_hdr, 
hdr->sub_headers[index]);
+                       index++;
+               }
+       }
+}
+
+static void dns_header_finish(struct proto_hdr *hdr)
+{
+       uint16_t ar_count = 0;
+       uint16_t ns_count = 0;
+       uint16_t qd_count = 0;
+       uint16_t an_count = 0;
+       int i;
+
+       for (i = 0; i < hdr->headers_count; i++) {
+               struct proto_hdr *sub_hdr = hdr->sub_headers[i];
+
+               switch (sub_hdr->id) {
+               case DNS_QUERY_HDR:
+                       qd_count++;
+                       break;
+
+               case DNS_ANSWER_HDR:
+                       an_count++;
+                       break;
+
+               case DNS_AUTH_HDR:
+                       ns_count++;
+                       break;
+
+               case DNS_ADD_HDR:
+                       ar_count++;
+                       break;
+               }
+       }
+
+       dns_sort_headers(hdr, DNS_QUERY_HDR, 0);
+       dns_sort_headers(hdr, DNS_ANSWER_HDR, qd_count);
+       dns_sort_headers(hdr, DNS_AUTH_HDR, qd_count + an_count);
+       dns_sort_headers(hdr, DNS_ADD_HDR, qd_count + an_count + ns_count);
+
+       proto_hdr_field_set_default_be16(hdr, DNS_QD_COUNT, qd_count);
+       proto_hdr_field_set_default_be16(hdr, DNS_AN_COUNT, an_count);
+       proto_hdr_field_set_default_be16(hdr, DNS_NS_COUNT, ns_count);
+       proto_hdr_field_set_default_be16(hdr, DNS_AR_COUNT, ar_count);
+
+       if (an_count)
+               proto_hdr_field_set_default_be16(hdr, DNS_QR, 1);
+}
+
+static void dns_push_sub_header(struct proto_hdr *hdr, struct proto_hdr 
*sub_hdr)
+{
+       switch (sub_hdr->id) {
+       case DNS_QUERY_HDR:
+               sub_hdr->ops = &dns_proto_query_ops;
+               break;
+
+       case DNS_ANSWER_HDR:
+       case DNS_AUTH_HDR:
+       case DNS_ADD_HDR:
+               sub_hdr->ops = &dns_proto_rrecord_ops;
+               break;
+
+       default:
+               bug();
+       }
+}
+
+static const struct proto_ops dns_proto_ops = {
+       .id              = PROTO_DNS,
+       .layer           = PROTO_L7,
+       .header_init     = dns_header_init,
+       .header_finish   = dns_header_finish,
+       .push_sub_header = dns_push_sub_header,
+};
+
+void protos_l7_init(void)
+{
+       proto_ops_register(&dns_proto_ops);
+}
diff --git a/trafgen_l7.h b/trafgen_l7.h
new file mode 100644
index 0000000..cf19fa4
--- /dev/null
+++ b/trafgen_l7.h
@@ -0,0 +1,45 @@
+#ifndef TRAFGEN_L7_H
+#define TRAFGEN_L7_H
+
+enum dns_field {
+       DNS_ID,
+       DNS_QR,
+       DNS_OPCODE,
+       DNS_AA,
+       DNS_TC,
+       DNS_RD,
+       DNS_RA,
+       DNS_ZERO,
+       DNS_RCODE,
+       DNS_QD_COUNT,
+       DNS_AN_COUNT,
+       DNS_NS_COUNT,
+       DNS_AR_COUNT,
+};
+
+enum dns_header {
+       DNS_UNDEF_HDR,
+       DNS_QUERY_HDR,
+       DNS_ANSWER_HDR,
+       DNS_AUTH_HDR,
+       DNS_ADD_HDR,
+};
+
+enum dns_query_field {
+       DNS_QUERY_NAME,
+       DNS_QUERY_TYPE,
+       DNS_QUERY_CLASS,
+};
+
+enum dns_rrecord_field {
+       DNS_RRECORD_NAME,
+       DNS_RRECORD_TYPE,
+       DNS_RRECORD_CLASS,
+       DNS_RRECORD_TTL,
+       DNS_RRECORD_LEN,
+       DNS_RRECORD_DATA,
+};
+
+extern void protos_l7_init(void);
+
+#endif /* TRAFGEN_L7_H */
diff --git a/trafgen_proto.c b/trafgen_proto.c
index e300e7f..1fa2802 100644
--- a/trafgen_proto.c
+++ b/trafgen_proto.c
@@ -14,6 +14,7 @@
 #include "trafgen_l2.h"
 #include "trafgen_l3.h"
 #include "trafgen_l4.h"
+#include "trafgen_l7.h"
 #include "trafgen_proto.h"
 
 #define field_shift_and_mask(f, v) (((v) << (f)->shift) & \
@@ -157,6 +158,112 @@ void proto_header_finish(struct proto_hdr *hdr)
                hdr->ops->header_finish(hdr);
 }
 
+struct proto_hdr *proto_hdr_push_sub_header(struct proto_hdr *hdr, int id)
+{
+       struct proto_hdr *sub_hdr;
+
+       sub_hdr = xzmalloc(sizeof(struct proto_hdr));
+       sub_hdr->index = hdr->headers_count;
+       sub_hdr->parent = hdr;
+       sub_hdr->id = id;
+
+       hdr->headers_count++;
+       hdr->sub_headers = xrealloc(hdr->sub_headers,
+                                   hdr->headers_count * sizeof(struct 
proto_hdr *));
+
+       hdr->sub_headers[hdr->headers_count - 1] = sub_hdr;
+
+       if (hdr->ops->push_sub_header)
+               hdr->ops->push_sub_header(hdr, sub_hdr);
+
+       if (sub_hdr->ops->header_init)
+               sub_hdr->ops->header_init(sub_hdr);
+
+       return sub_hdr;
+}
+
+static void __proto_hdr_set_offset(struct proto_hdr *hdr, uint16_t pkt_offset)
+{
+       int i;
+
+       hdr->pkt_offset = pkt_offset;
+
+       for (i = 0; i < hdr->fields_count; i++) {
+               struct proto_field *f = &hdr->fields[i];
+
+               f->pkt_offset = pkt_offset + f->offset;
+       }
+}
+
+void proto_hdr_move_sub_header(struct proto_hdr *hdr, struct proto_hdr *from,
+                              struct proto_hdr *to)
+{
+       struct proto_hdr *src_hdr, *dst_hdr, *tmp;
+       uint8_t *src_ptr, *dst_ptr;
+       uint16_t to_pkt_offset;
+       uint16_t to_index;
+       uint16_t pkt_offset;
+       int idx_shift;
+       size_t len = 0;
+       uint8_t *buf;
+       int i;
+
+       if (hdr->headers_count < 2)
+               return;
+       if (from->index == to->index)
+               return;
+
+       buf = xzmalloc(from->len);
+       memcpy(buf, proto_header_ptr(from), from->len);
+
+       to_pkt_offset = to->pkt_offset;
+       to_index = to->index;
+
+       if (from->index < to->index) {
+               src_hdr = hdr->sub_headers[from->index + 1];
+               dst_hdr = to;
+
+               src_ptr = proto_header_ptr(src_hdr);
+               dst_ptr = proto_header_ptr(from);
+               len = (to->pkt_offset + to->len) - src_hdr->pkt_offset;
+
+               pkt_offset = from->pkt_offset;
+               idx_shift = 1;
+       } else {
+               src_hdr = to;
+               dst_hdr = hdr->sub_headers[from->index - 1];
+
+               src_ptr = proto_header_ptr(src_hdr);
+               dst_ptr = src_ptr + from->len;
+               len = from->pkt_offset - to->pkt_offset;
+
+               pkt_offset = to->pkt_offset + from->len;
+               idx_shift = -1;
+       }
+
+       hdr->sub_headers[from->index] = to;
+       hdr->sub_headers[to->index] = from;
+
+       for (i = src_hdr->index; i <= dst_hdr->index; i++) {
+               tmp = hdr->sub_headers[i];
+
+               __proto_hdr_set_offset(tmp, pkt_offset);
+               pkt_offset += tmp->len;
+       }
+
+       for (i = src_hdr->index; i <= dst_hdr->index; i++)
+               hdr->sub_headers[i]->index = i + idx_shift;
+
+       memmove(dst_ptr, src_ptr, len);
+
+       from->pkt_offset = to_pkt_offset;
+       from->index = to_index;
+
+       memcpy(proto_header_ptr(from), buf, from->len);
+
+       xfree(buf);
+}
+
 struct proto_hdr *proto_lower_default_add(struct proto_hdr *upper,
                                          enum proto_id pid)
 {
@@ -481,6 +588,16 @@ void proto_hdr_field_set_default_dev_ipv6(struct proto_hdr 
*hdr, uint32_t fid)
        __proto_hdr_field_set_dev_ipv6(hdr, fid, true);
 }
 
+void proto_hdr_field_set_string(struct proto_hdr *hdr, uint32_t fid, const 
char *str)
+{
+       proto_hdr_field_set_bytes(hdr, fid, (uint8_t *)str, strlen(str) + 1);
+}
+
+void proto_hdr_field_set_default_string(struct proto_hdr *hdr, uint32_t fid, 
const char *str)
+{
+       proto_hdr_field_set_default_bytes(hdr, fid, (uint8_t *)str, strlen(str) 
+ 1);
+}
+
 void proto_field_set_u8(struct proto_field *field, uint8_t val)
 {
        __proto_field_set_bytes(field, &val, 1, false, false);
@@ -532,6 +649,16 @@ void proto_field_set_bytes(struct proto_field *field, 
const uint8_t *bytes, size
        __proto_field_set_bytes(field, bytes, len, false, false);
 }
 
+void proto_field_set_string(struct proto_field *field, const char *str)
+{
+       proto_field_set_bytes(field, (uint8_t *)str, strlen(str) + 1);
+}
+
+void proto_field_set_default_string(struct proto_field *field, const char *str)
+{
+       __proto_field_set_bytes(field, (uint8_t *)str, strlen(str) + 1, true, 
false);
+}
+
 void protos_init(const char *dev)
 {
        ctx.dev = dev;
@@ -539,6 +666,7 @@ void protos_init(const char *dev)
        protos_l2_init();
        protos_l3_init();
        protos_l4_init();
+       protos_l7_init();
 }
 
 void proto_packet_update(uint32_t idx)
diff --git a/trafgen_proto.h b/trafgen_proto.h
index d9a6a24..8b1d900 100644
--- a/trafgen_proto.h
+++ b/trafgen_proto.h
@@ -19,6 +19,7 @@ enum proto_id {
        PROTO_ICMP6,
        PROTO_UDP,
        PROTO_TCP,
+       PROTO_DNS,
        __PROTO_MAX,
 };
 
@@ -27,6 +28,7 @@ enum proto_layer {
        PROTO_L2,
        PROTO_L3,
        PROTO_L4,
+       PROTO_L7,
 };
 
 struct proto_field;
@@ -38,6 +40,7 @@ struct proto_ops {
 
        void (*header_init)(struct proto_hdr *hdr);
        void (*header_finish)(struct proto_hdr *hdr);
+       void (*push_sub_header)(struct proto_hdr *hdr, struct proto_hdr 
*sub_hdr);
        void (*field_changed)(struct proto_field *field);
        void (*packet_finish)(struct proto_hdr *hdr);
        void (*packet_update)(struct proto_hdr *hdr);
@@ -46,12 +49,16 @@ struct proto_ops {
 
 struct proto_hdr {
        const struct proto_ops *ops;
+       struct proto_hdr *parent;
+       struct proto_hdr **sub_headers;
+       uint32_t headers_count;
        uint16_t pkt_offset;
        uint32_t pkt_id;
-       uint32_t index;
+       int index;
        struct proto_field *fields;
        size_t fields_count;
        bool is_csum_valid;
+       uint32_t id;
        size_t len;
 };
 
@@ -95,6 +102,10 @@ extern void proto_header_finish(struct proto_hdr *hdr);
 extern void proto_packet_finish(void);
 extern void proto_packet_update(uint32_t idx);
 
+extern struct proto_hdr *proto_hdr_push_sub_header(struct proto_hdr *hdr, int 
id);
+extern void proto_hdr_move_sub_header(struct proto_hdr *hdr, struct proto_hdr 
*from,
+                                     struct proto_hdr *to);
+
 extern struct proto_hdr *proto_lower_default_add(struct proto_hdr *hdr,
                                                 enum proto_id pid);
 
@@ -142,6 +153,9 @@ extern void proto_hdr_field_set_default_dev_ipv4(struct 
proto_hdr *hdr, uint32_t
 extern void proto_hdr_field_set_dev_ipv6(struct proto_hdr *hdr, uint32_t fid);
 extern void proto_hdr_field_set_default_dev_ipv6(struct proto_hdr *hdr, 
uint32_t fid);
 
+extern void proto_hdr_field_set_string(struct proto_hdr *hdr, uint32_t fid, 
const char *str);
+extern void proto_hdr_field_set_default_string(struct proto_hdr *hdr, uint32_t 
fid, const char *str);
+
 extern void proto_field_dyn_apply(struct proto_field *field);
 
 extern struct proto_field *proto_hdr_field_by_id(struct proto_hdr *hdr, 
uint32_t fid);
@@ -156,6 +170,8 @@ extern uint32_t proto_field_get_u32(struct proto_field 
*field);
 extern void proto_field_set_be16(struct proto_field *field, uint16_t val);
 extern void proto_field_set_be32(struct proto_field *field, uint32_t val);
 extern void proto_field_set_bytes(struct proto_field *field, const uint8_t 
*bytes, size_t len);
+extern void proto_field_set_string(struct proto_field *field, const char *str);
+extern void proto_field_set_default_string(struct proto_field *field, const 
char *str);
 
 extern void proto_field_func_add(struct proto_field *field,
                                 struct proto_field_func *func);
-- 
2.11.0

-- 
You received this message because you are subscribed to the Google Groups 
"netsniff-ng" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to netsniff-ng+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to