Added high-level command line protocol packet building intreface,
which allows to specify specified protocol parameters to build
the protocol specific header.

Each protocol is represented by proto_gen struct which is responsible
only for providing field description (size, data) by name to trafgen's
low level packet generation layer.

All packet generation routine is performed by the generic code in
trafgen.c which parses the command line, obtains proto name, param=value
list and calls the specific protocol handler to get protocol field info
by name.

The command line syntax looks like:

trafgen/trafgen --dev lo <proto> <param> = <value> ... <param>=<value>, <proto> 
<param>=<value> ...

so the first is proto name and after there are param value pairs which
are separated by space, in case if there are multiple protocols
specified - their should be separated by "," after last param value of
the previous protocol.

Signed-off-by: Vadim Kochan <vadi...@gmail.com>
---
 proto_gen.c      | 157 ++++++++++++++++++++++++++++++++++++
 proto_gen.h      |  68 ++++++++++++++++
 trafgen.c        | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 trafgen/Makefile |   1 +
 4 files changed, 459 insertions(+), 5 deletions(-)
 create mode 100644 proto_gen.c
 create mode 100644 proto_gen.h

diff --git a/proto_gen.c b/proto_gen.c
new file mode 100644
index 0000000..eee6227
--- /dev/null
+++ b/proto_gen.c
@@ -0,0 +1,157 @@
+/*
+ * netsniff-ng - the packet sniffing beast
+ * Subject to the GPL, version 2.
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "proto_gen.h"
+#include "xmalloc.h"
+
+static struct proto_gen *protos;
+
+struct proto_gen *proto_gen_by_name(char *name)
+{
+       struct proto_gen *p = protos;
+
+       for (; p; p = p->next)
+               if (strcmp(p->name, name) == 0)
+                       return p;
+
+       return NULL;
+}
+
+struct proto_gen *proto_gen_by_id(enum net_proto id)
+{
+       struct proto_gen *p = protos;
+
+       for (; p; p = p->next)
+               if (p->id == id)
+                       return p;
+
+       return NULL;
+}
+
+void proto_gen_register(struct proto_gen *prot)
+{
+       prot->next = protos;
+       protos = prot;
+}
+
+void proto_gen_init(void)
+{
+}
+
+void proto_gen_uninit(void)
+{
+       struct proto_gen *p = protos;
+
+       for (; p; p = p->next) {
+               int f;
+
+               if (p->payload)
+                       xfree(p->payload);
+
+               if (!p->fields)
+                       continue;
+
+               for (f = 0; f < p->fields_count; f++)
+                       if (p->fields[f].is_allocated)
+                               xfree(p->fields[f].data);
+
+               xfree(p->fields);
+       }
+}
+
+void proto_gen_set_ctx(struct ctx *ctx)
+{
+       struct proto_gen *p = protos;
+
+       for (; p; p = p->next)
+               p->ctx = ctx;
+}
+
+void proto_fields_realloc(struct proto_gen *prot, int count)
+{
+       int i;
+
+       prot->fields = xrealloc(prot->fields, count * sizeof(*prot->fields));
+
+       /* zero new fields */
+       for (i = count - 1; i >= prot->fields_count; i--)
+               memset(&prot->fields[i], 0, sizeof(*prot->fields));
+
+       /* re-set data pointer to new "value" offset for small len */
+       for (i = 0; i < count; i++) {
+               struct proto_field *field = &prot->fields[i];
+
+               if (prot->fields[i].len <= sizeof(field->value))
+                       field->data = (uint8_t *)&field->value;
+       }
+
+       prot->fields_count = count;
+}
+
+void proto_field_add(struct proto_gen *prot, int id, size_t len)
+{
+       struct proto_field *field;
+
+       proto_fields_realloc(prot, prot->fields_count + 1);
+
+       field = &prot->fields[prot->fields_count - 1];
+
+       if (len > sizeof(field->value)) {
+               field->data = xzmalloc(len);
+               field->is_allocated = true;
+       } else {
+               field->data = (uint8_t *)&field->value;
+       }
+
+       field->len = len;
+       field->id = id;
+}
+
+struct proto_field *proto_field_by_id(struct proto_gen *prot, int id)
+{
+       int i;
+
+       for (i = 0; i < prot->fields_count; i++)
+               if (prot->fields[i].id == id)
+                       return &prot->fields[i];
+
+       return NULL;
+}
+
+int proto_field_set_bytes(struct proto_gen *prot, int id, uint8_t *bytes)
+{
+       struct proto_field *field = proto_field_by_id(prot, id);
+
+       if (!field)
+               return -1;
+
+       memcpy(prot->fields[id].data, bytes, prot->fields[id].len);
+       prot->fields[id].is_set = true;
+
+       return 0;
+}
+
+int proto_field_set_value(struct proto_gen *prot, int id, int value)
+{
+       return proto_field_set_bytes(prot, id, (uint8_t *)&value);
+}
+
+bool proto_field_exist(struct proto_gen *prot, int id)
+{
+       return !!proto_field_by_id(prot, id);
+}
+
+bool proto_field_is_set(struct proto_gen *prot, int id)
+{
+       struct proto_field *field = proto_field_by_id(prot, id);
+
+       if (!field)
+               return false;
+
+       return field->is_set;
+}
diff --git a/proto_gen.h b/proto_gen.h
new file mode 100644
index 0000000..acffed4
--- /dev/null
+++ b/proto_gen.h
@@ -0,0 +1,68 @@
+#ifndef PROTO_GEN_I_H
+#define PROTO_GEN_I_H
+
+#include "trafgen.h"
+#include "trafgen_conf.h"
+
+enum net_proto {
+       NET_PROTO_NONE,
+       NET_PROTO_ETH,
+       NET_PROTO_ARP,
+       NET_PROTO_IPV4,
+       NET_PROTO_IPV6,
+       NET_PROTO_UDP,
+       NET_PROTO_TCP,
+};
+
+struct proto_field {
+       int id;
+       bool is_set;
+       bool is_allocated;
+       uint8_t *data;
+       /* on x64 system it will keep even MAC address */
+       long int value;
+       int len;
+};
+
+struct proto_gen {
+       struct proto_gen *next;
+       struct ctx *ctx;
+       uint8_t *payload;
+       /* payload len */
+       int len;
+
+       /* array of fields ordered from the lower to higher offset */
+       struct proto_field *fields;
+       int fields_count;
+
+       char *name;
+       size_t min_size;
+       enum net_proto id;
+       /* in case if no lower layer proto is specified */
+       enum net_proto layer_proto;
+
+       void (*proto_init)(struct proto_gen *prot);
+       void (*print_help)(struct proto_gen *prot);
+       int (*parse_param)(struct proto_gen *prot, char *arg, char *val);
+       void (*check_fields)(struct proto_gen *prot);
+       void (*set_next_proto)(struct proto_gen *prot, struct proto_gen *next);
+};
+
+void proto_gen_register(struct proto_gen *prot);
+struct proto_gen *proto_gen_by_name(char *name);
+struct proto_gen *proto_gen_by_id(enum net_proto id);
+
+void proto_gen_init(void);
+void proto_gen_uninit(void);
+void proto_gen_set_ctx(struct ctx *ctx);
+
+void proto_fields_realloc(struct proto_gen *prot, int count);
+
+void proto_field_add(struct proto_gen *prot, int id, size_t len);
+struct proto_field *proto_field_by_id(struct proto_gen *prot, int id);
+int proto_field_set_value(struct proto_gen *prot, int id, int value);
+int proto_field_set_bytes(struct proto_gen *prot, int id, uint8_t *value);
+bool proto_field_exist(struct proto_gen *prot, int id);
+bool proto_field_is_set(struct proto_gen *prot, int id);
+
+#endif
diff --git a/trafgen.c b/trafgen.c
index f7402d6..b2dce43 100644
--- a/trafgen.c
+++ b/trafgen.c
@@ -51,6 +51,7 @@
 #include "ring_tx.h"
 #include "csum.h"
 #include "trafgen.h"
+#include "proto_gen.h"
 
 struct cpu_stats {
        unsigned long tv_sec, tv_usec;
@@ -68,6 +69,11 @@ size_t plen = 0;
 struct packet_dyn *packet_dyn = NULL;
 size_t dlen = 0;
 
+static bool has_proto = false;
+#define PROTO_MAX_LAYERS       8
+static struct proto_gen *protos[PROTO_MAX_LAYERS];
+static int protos_count = 0;
+
 static const char *short_options = "d:c:n:t:vJhS:rk:i:o:VRs:P:eE:pu:g:CHQq";
 static const struct option long_options[] = {
        {"dev",                 required_argument,      NULL, 'd'},
@@ -786,10 +792,223 @@ static int xmit_packet_precheck(struct ctx *ctx, 
unsigned int cpu)
        return 0;
 }
 
+static void proto_layer_add(struct proto_gen *prot)
+{
+       struct proto_gen *prev;
+
+       /* find lower proto if it is needed & exist */
+       if (!protos_count && prot->layer_proto) {
+               struct proto_gen *lower = proto_gen_by_id(prot->layer_proto);
+
+               if (lower) {
+                       if (lower->proto_init)
+                               lower->proto_init(lower);
+
+                       if (lower->check_fields)
+                               lower->check_fields(lower);
+
+                       proto_layer_add(lower);
+               }
+       }
+
+       if (protos_count >= PROTO_MAX_LAYERS)
+               panic("Too much proto layers\n");
+
+       prev = protos_count > 0 ? protos[protos_count - 1] : NULL;
+       protos[protos_count++] = prot;
+
+       if (prev && prev->set_next_proto)
+               prev->set_next_proto(prev, prot);
+}
+
+static void proto_fill_fields(struct proto_gen *prot)
+{
+       int i;
+
+       for (i = 0; i < prot->fields_count; i++) {
+               struct proto_field *field = &prot->fields[i];
+
+               if (!field->data) {
+                       set_fill(0, field->len);
+                       continue;
+               }
+
+               set_multi_byte(field->data, field->len);
+       }
+}
+
+static void proto_check_padding(struct proto_gen *prot)
+{
+       int pkt_len;
+
+       if (!prot->min_size)
+               return;
+
+       pkt_len = current_packet()->len;
+       if (pkt_len < prot->min_size)
+               set_fill(0, prot->min_size - pkt_len);
+}
+
+static void proto_gen_packets(void)
+{
+       int i;
+
+       realloc_packet();
+
+       for (i = 0; i < protos_count; i++) {
+               proto_fill_fields(protos[i]);
+
+               if (protos[i]->payload)
+                       set_multi_byte(protos[i]->payload, protos[i]->len);
+       }
+
+       proto_check_padding(protos[0]);
+}
+
+static char *parse_name_value(char *str, char **name, char **value)
+{
+       char *eq_ptr = strchr(str, '=');
+       char *end;
+       bool has_quote = false;
+
+       if (!eq_ptr)
+               return NULL;
+
+       /* parse name */
+       while (*str && isspace(*str))
+               str++;
+
+       end = str;
+       while (*end && !isspace(*end) && *end != '=')
+               end++;
+
+       if (!end)
+               return NULL;
+
+       *end = '\0';
+       *name = str;
+
+       /* parse value */
+       str = eq_ptr + 1;
+       while (*str && (isspace(*str) || *str == '"')) {
+               if (*str == '"') {
+                       has_quote = true;
+                       str++;
+                       break;
+               }
+               str++;
+       }
+
+       end = str;
+       while (*end) {
+               if (!has_quote && isspace(*end)) {
+                       break;
+               } else if (has_quote && *end == '"') {
+                       break;
+               }
+               end++;
+       }
+
+       *value = str;
+
+       if (*end == '\0')
+               return end;
+
+       *end = '\0';
+       return ++end;
+}
+
+static int parse_common_param(struct proto_gen *prot, char *name, char *value)
+{
+       if (!strcasecmp("payload", name) || !strcasecmp("data", name)) {
+               if (prot->payload)
+                       xfree(prot->payload);
+
+               prot->len = str2bytes(value, &prot->payload);
+
+               if (!prot->len)
+                       panic("Invalid payload\n");
+
+               return 0;
+       }
+
+       if (!strcasecmp("text", name)) {
+               if (prot->payload)
+                       xfree(prot->payload);
+
+               prot->payload = (uint8_t *)strdup(value);
+               prot->len = strlen(value);
+
+               return 0;
+       }
+
+       return -1;
+}
+
+static void parse_proto_params(struct proto_gen *prot, char *params)
+{
+       char *name = NULL, *value = NULL;
+
+       while ((params = parse_name_value(params, &name, &value))) {
+               if (!name || !value || !strlen(name) || !strlen(value))
+                       panic("Can't parse param name=value\n");
+
+               if ((prot->parse_param(prot, name, value)) == -1) {
+                       if (!parse_common_param(prot, name, value))
+                               continue;
+
+                       if (prot->print_help)
+                               prot->print_help(prot);
+
+                       panic("proto %s: Invalid argument(%s=%s)\n", prot->name,
+                                       name, value);
+               }
+       }
+}
+
+static int parse_proto(int argc, char **argv)
+{
+       char *args = cmdline_args2str(optind, argc, argv);
+       char *args_ptr = args;
+       struct proto_gen *prot;
+       char *proto_args, *proto_args_ptr = NULL;
+
+       while ((proto_args = strtok_r(args_ptr, ",", &proto_args_ptr))) {
+               char *args_list = NULL;
+               char *prot_name = strtok_r(proto_args, " ", &args_list);
+
+               if (!prot_name || !strlen(prot_name))
+                       panic("proto is null\n");
+
+               prot = proto_gen_by_name(prot_name);
+               if (!prot)
+                       panic("proto %s: Is not supported\n", prot_name);
+
+               if (prot->proto_init)
+                       prot->proto_init(prot);
+
+               parse_proto_params(prot, args_list);
+
+               proto_layer_add(prot);
+
+               if (prot->check_fields)
+                       prot->check_fields(prot);
+
+               args_ptr = NULL;
+       }
+
+       proto_gen_packets();
+
+       xfree(args);
+       return 0;
+}
+
 static void main_loop(struct ctx *ctx, char *confname, bool slow,
                      unsigned int cpu, bool invoke_cpp, unsigned long orig_num)
 {
-       compile_packets(confname, ctx->verbose, cpu, invoke_cpp);
+       if (!has_proto)
+               compile_packets(confname, ctx->verbose, cpu, invoke_cpp);
+
        if (xmit_packet_precheck(ctx, cpu) < 0)
                return;
 
@@ -1030,15 +1249,22 @@ int main(int argc, char **argv)
                }
        }
 
-       if (argc < 5)
-               help();
        if (ctx.device == NULL)
                panic("No networking device given!\n");
-       if (confname == NULL)
-               panic("No configuration file given!\n");
        if (device_mtu(ctx.device) == 0)
                panic("This is no networking device!\n");
 
+       proto_gen_init();
+       proto_gen_set_ctx(&ctx);
+
+       if (optind != argc && !parse_proto(argc, argv))
+               has_proto = true;
+
+       if (argc < 5 && !has_proto)
+               help();
+       if (confname == NULL && !has_proto)
+               panic("No configuration file given!\n");
+
        register_signal(SIGINT, signal_handler);
        register_signal(SIGQUIT, signal_handler);
        register_signal(SIGTERM, signal_handler);
@@ -1137,5 +1363,7 @@ thread_out:
        free(ctx.rhost);
        free(confname);
 
+       proto_gen_uninit();
+
        return 0;
 }
diff --git a/trafgen/Makefile b/trafgen/Makefile
index 3cb8497..c935c0b 100644
--- a/trafgen/Makefile
+++ b/trafgen/Makefile
@@ -17,6 +17,7 @@ trafgen-objs =        xmalloc.o \
                ring_tx.o \
                ring.o \
                timer.o \
+               proto_gen.o \
                trafgen_lexer.yy.o \
                trafgen_parser.tab.o \
                trafgen.o
-- 
2.4.2

-- 
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