On 2017-01-30 at 09:33:29 +0100, Vadim Kochan <vadi...@gmail.com> wrote: > 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.
Might be a bit of a naive question: But wouldn't it be possible to enforce the sub-header order through the parser (i.e. only allow trafgen scripts which specify the respective sections in the right order? This would safe us from doing the whole header sorting/moving dance which looks a bit sacry to me (memmove of payload especially). A few minor comments inline below. > 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) You use an uint32_t id here but changed proto_hdr->i to and int. Why? In general, please try to be consistent with types and give reasons when changing them. > +{ > + int i; i and index should be type size_t as hdr->headers_count is size_t > + > + 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; size_t > + > + 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; > + } No empty lines after break please. > + } > + > + 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(); > + } No empty lines after break please. > +} > + > +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; size_t > + > + 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); Pleae use xmemdupz() > + > + 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; Please rename to sub_headers_count for better grep-ability and distinction from packet->headers_count > uint16_t pkt_offset; > uint32_t pkt_id; > - uint32_t index; > + int index; As mentioned above: Why the type change? > 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.