summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Kochan <vadim4j@gmail.com>2017-06-01 13:12:28 +0300
committerTobias Klauser <tklauser@distanz.ch>2017-06-02 09:15:27 +0200
commit9d5e303a998e4618d492c9d0befb09e3cd3d746b (patch)
tree8ed2c72a573cad5acf92b62f4f0048c9cff251c4
parenta2524f2bc9c0e8651221ba88b6013db55cec0190 (diff)
trafgen: l7: Add DNS header generation API
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 <vadim4j@gmail.com> Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
-rw-r--r--trafgen/Makefile1
-rw-r--r--trafgen_l4.c32
-rw-r--r--trafgen_l7.c170
-rw-r--r--trafgen_l7.h45
-rw-r--r--trafgen_proto.c127
-rw-r--r--trafgen_proto.h19
6 files changed, 392 insertions, 2 deletions
diff --git a/trafgen/Makefile b/trafgen/Makefile
index 3369d4a..d38f0b0 100644
--- a/trafgen/Makefile
+++ b/trafgen/Makefile
@@ -29,6 +29,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..f801677
--- /dev/null
+++ b/trafgen_l7.c
@@ -0,0 +1,170 @@
+/*
+ * 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->sub_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)
+{
+ size_t ar_count = 0;
+ size_t ns_count = 0;
+ size_t qd_count = 0;
+ size_t an_count = 0;
+ int i;
+
+ for (i = 0; i < hdr->sub_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 7969b77..0353d6b 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,111 @@ 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->sub_headers_count;
+ sub_hdr->parent = hdr;
+ sub_hdr->id = id;
+
+ hdr->sub_headers_count++;
+ hdr->sub_headers = xrealloc(hdr->sub_headers,
+ hdr->sub_headers_count * sizeof(struct proto_hdr *));
+
+ hdr->sub_headers[hdr->sub_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)
+{
+ size_t 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->sub_headers_count < 2)
+ return;
+ if (from->index == to->index)
+ return;
+
+ buf = xmemdupz(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 +587,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 +648,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 +665,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 94eb4e7..f3d07ba 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 sub_headers_count;
uint16_t pkt_offset;
uint32_t pkt_id;
uint32_t 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);
@@ -155,8 +169,9 @@ extern void proto_field_set_u32(struct proto_field *field, uint32_t val);
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_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);