sync to iproute2-2.6.16-060323
[iproute2.git] / tc / em_u32.c
diff --git a/tc/em_u32.c b/tc/em_u32.c
new file mode 100644 (file)
index 0000000..b8857f1
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * em_u32.c            U32 Ematch
+ *
+ *             This program is free software; you can distribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <errno.h>
+
+#include "m_ematch.h"
+
+extern struct ematch_util u32_ematch_util;
+
+static void u32_print_usage(FILE *fd)
+{
+       fprintf(fd,
+           "Usage: u32(ALIGN VALUE MASK at [ nexthdr+ ] OFFSET)\n" \
+           "where: ALIGN  := { u8 | u16 | u32 }\n" \
+           "\n" \
+           "Example: u32(u16 0x1122 0xffff at nexthdr+4)\n");
+}
+
+static int u32_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
+                         struct bstr *args)
+{
+       struct bstr *a;
+       int align, nh_len;
+       unsigned long key, mask, offmask = 0, offset;
+       struct tc_u32_key u_key;
+
+       memset(&u_key, 0, sizeof(u_key));
+
+#define PARSE_ERR(CARG, FMT, ARGS...) \
+       em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT ,##ARGS)
+
+       if (args == NULL)
+               return PARSE_ERR(args, "u32: missing arguments");
+
+       if (!bstrcmp(args, "u8"))
+               align = 1;
+       else if (!bstrcmp(args, "u16"))
+               align = 2;
+       else if (!bstrcmp(args, "u32"))
+               align = 4;
+       else
+               return PARSE_ERR(args, "u32: invalid alignment");
+
+       a = bstr_next(args);
+       if (a == NULL)
+               return PARSE_ERR(a, "u32: missing key");
+
+       key = bstrtoul(a);
+       if (key == ULONG_MAX)
+               return PARSE_ERR(a, "u32: invalid key, must be numeric");
+
+       a = bstr_next(a);
+       if (a == NULL)
+               return PARSE_ERR(a, "u32: missing mask");
+
+       mask = bstrtoul(a);
+       if (mask == ULONG_MAX)
+               return PARSE_ERR(a, "u32: invalid mask, must be numeric");
+
+       a = bstr_next(a);
+       if (a == NULL || bstrcmp(a, "at") != 0)
+               return PARSE_ERR(a, "u32: missing \"at\"");
+
+       a = bstr_next(a);
+       if (a == NULL)
+               return PARSE_ERR(a, "u32: missing offset");
+
+       nh_len = strlen("nexthdr+");
+       if (a->len > nh_len && !memcmp(a->data, "nexthdr+", nh_len)) {
+               char buf[a->len - nh_len + 1];
+               offmask = -1;
+               memcpy(buf, a->data + nh_len, a->len - nh_len);
+               offset = strtoul(buf, NULL, 0);
+       } else if (!bstrcmp(a, "nexthdr+")) {
+               a = bstr_next(a);
+               if (a == NULL)
+                       return PARSE_ERR(a, "u32: missing offset");
+               offset = bstrtoul(a);
+       } else
+               offset = bstrtoul(a);
+               
+       if (offset == ULONG_MAX)
+               return PARSE_ERR(a, "u32: invalid offset");
+
+       if (a->next)
+               return PARSE_ERR(a->next, "u32: unexpected trailer");
+
+       switch (align) {
+               case 1:
+                       if (key > 0xFF)
+                               return PARSE_ERR(a, "Illegal key (>0xFF)");
+                       if (mask > 0xFF)
+                               return PARSE_ERR(a, "Illegal mask (>0xFF)");
+
+                       key <<= 24 - ((offset & 3) * 8);
+                       mask <<= 24 - ((offset & 3) * 8);
+                       offset &= ~3;
+                       break;
+
+               case 2:
+                       if (key > 0xFFFF)
+                               return PARSE_ERR(a, "Illegal key (>0xFFFF)");
+                       if (mask > 0xFFFF)
+                               return PARSE_ERR(a, "Illegal mask (>0xFFFF)");
+
+                       if ((offset & 3) == 0) {
+                               key <<= 16;
+                               mask <<= 16;
+                       }
+                       offset &= ~3;
+                       break;
+       }
+
+       key = htonl(key);
+       mask = htonl(mask);
+
+       if (offset % 4)
+               return PARSE_ERR(a, "u32: invalid offset alignment, " \
+                   "must be aligned to 4.");
+
+       key &= mask;
+
+       u_key.mask = mask;
+       u_key.val = key;
+       u_key.off = offset;
+       u_key.offmask = offmask;
+
+       addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
+       addraw_l(n, MAX_MSG, &u_key, sizeof(u_key));
+
+#undef PARSE_ERR
+       return 0;
+}
+
+static int u32_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
+                         int data_len)
+{
+       struct tc_u32_key *u_key = data;
+
+       if (data_len < sizeof(*u_key)) {
+               fprintf(stderr, "U32 header size mismatch\n");
+               return -1;
+       }
+
+       fprintf(fd, "%08x/%08x at %s%d",
+           (unsigned int) ntohl(u_key->val),
+           (unsigned int) ntohl(u_key->mask),
+           u_key->offmask ? "nexthdr+" : "",
+           u_key->off);
+
+       return 0;
+}
+
+struct ematch_util u32_ematch_util = {
+       .kind = "u32",
+       .kind_num = TCF_EM_U32,
+       .parse_eopt = u32_parse_eopt,
+       .print_eopt = u32_print_eopt,
+       .print_usage = u32_print_usage
+};