Merge commit 'origin/trunk@12184' into fedora
[iptables.git] / trunk / extensions / libxt_TCPOPTSTRIP.c
diff --git a/trunk/extensions/libxt_TCPOPTSTRIP.c b/trunk/extensions/libxt_TCPOPTSTRIP.c
new file mode 100644 (file)
index 0000000..bd74e37
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Shared library add-on to iptables to add TCPOPTSTRIP target support.
+ * Copyright (c) 2007 Sven Schnelle <svens@bitebene.org>
+ * Copyright © CC Computer Consultants GmbH, 2007
+ * Jan Engelhardt <jengelh@computergmbh.de>
+ */
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_TCPOPTSTRIP.h>
+#ifndef TCPOPT_MD5SIG
+#      define TCPOPT_MD5SIG 19
+#endif
+
+enum {
+       FLAG_STRIP = 1 << 0,
+};
+
+struct tcp_optionmap {
+       const char *name, *desc;
+       const unsigned int option;
+};
+
+static const struct option tcpoptstrip_tg_opts[] = {
+       {.name = "strip-options", .has_arg = true, .val = 's'},
+       { .name = NULL }
+};
+
+static const struct tcp_optionmap tcp_optionmap[] = {
+       {"wscale",         "Window scale",         TCPOPT_WINDOW},
+       {"mss",            "Maximum Segment Size", TCPOPT_MAXSEG},
+       {"sack-permitted", "SACK permitted",       TCPOPT_SACK_PERMITTED},
+       {"sack",           "Selective ACK",        TCPOPT_SACK},
+       {"timestamp",      "Timestamp",            TCPOPT_TIMESTAMP},
+       {"md5",            "MD5 signature",        TCPOPT_MD5SIG},
+       { .name = NULL }
+};
+
+static void tcpoptstrip_tg_help(void)
+{
+       const struct tcp_optionmap *w;
+
+       printf(
+"TCPOPTSTRIP target options:\n"
+"  --strip-options value     strip specified TCP options denoted by value\n"
+"                            (separated by comma) from TCP header\n"
+"  Instead of the numeric value, you can also use the following names:\n"
+       );
+
+       for (w = tcp_optionmap; w->name != NULL; ++w)
+               printf("    %-14s    strip \"%s\" option\n", w->name, w->desc);
+}
+
+static void tcpoptstrip_tg_init(struct xt_entry_target *t)
+{
+       struct xt_tcpoptstrip_target_info *info = (void *)t->data;
+
+       /* strictly necessary? play safe for now. */
+       memset(info->strip_bmap, 0, sizeof(info->strip_bmap));
+}
+
+static void parse_list(struct xt_tcpoptstrip_target_info *info, char *arg)
+{
+       unsigned int option;
+       char *p;
+       int i;
+
+       while (true) {
+               p = strchr(arg, ',');
+               if (p != NULL)
+                       *p = '\0';
+
+               option = 0;
+               for (i = 0; tcp_optionmap[i].name != NULL; ++i)
+                       if (strcmp(tcp_optionmap[i].name, arg) == 0) {
+                               option = tcp_optionmap[i].option;
+                               break;
+                       }
+
+               if (option == 0 && string_to_number(arg, 0, 255, &option) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Bad TCP option value \"%s\"", arg);
+
+               if (option < 2)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Option value may not be 0 or 1");
+
+               if (tcpoptstrip_test_bit(info->strip_bmap, option))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Option \"%s\" already specified", arg);
+
+               tcpoptstrip_set_bit(info->strip_bmap, option);
+               if (p == NULL)
+                       break;
+               arg = p + 1;
+       }
+}
+
+static int tcpoptstrip_tg_parse(int c, char **argv, int invert,
+                                unsigned int *flags, const void *entry,
+                                struct xt_entry_target **target)
+{
+       struct xt_tcpoptstrip_target_info *info = (void *)(*target)->data;
+
+       switch (c) {
+       case 's':
+               if (*flags & FLAG_STRIP)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "You can specify --strip-options only once");
+               parse_list(info, optarg);
+               *flags |= FLAG_STRIP;
+               return true;
+       }
+
+       return false;
+}
+
+static void tcpoptstrip_tg_check(unsigned int flags)
+{
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "TCPOPTSTRIP: --strip-options parameter required");
+}
+
+static void
+tcpoptstrip_print_list(const struct xt_tcpoptstrip_target_info *info,
+                       bool numeric)
+{
+       unsigned int i, j;
+       const char *name;
+       bool first = true;
+
+       for (i = 0; i < 256; ++i) {
+               if (!tcpoptstrip_test_bit(info->strip_bmap, i))
+                       continue;
+               if (!first)
+                       printf(",");
+
+               first = false;
+               name  = NULL;
+               if (!numeric)
+                       for (j = 0; tcp_optionmap[j].name != NULL; ++j)
+                               if (tcp_optionmap[j].option == i)
+                                       name = tcp_optionmap[j].name;
+
+               if (name != NULL)
+                       printf("%s", name);
+               else
+                       printf("%u", i);
+       }
+}
+
+static void
+tcpoptstrip_tg_print(const void *ip, const struct xt_entry_target *target,
+                     int numeric)
+{
+       const struct xt_tcpoptstrip_target_info *info =
+               (const void *)target->data;
+
+       printf("TCPOPTSTRIP options ");
+       tcpoptstrip_print_list(info, numeric);
+}
+
+static void
+tcpoptstrip_tg_save(const void *ip, const struct xt_entry_target *target)
+{
+       const struct xt_tcpoptstrip_target_info *info =
+               (const void *)target->data;
+
+       printf("--strip-options ");
+       tcpoptstrip_print_list(info, true);
+}
+
+static struct xtables_target tcpoptstrip_tg_reg = {
+       .version       = XTABLES_VERSION,
+       .name          = "TCPOPTSTRIP",
+       .family        = AF_INET,
+       .size          = XT_ALIGN(sizeof(struct xt_tcpoptstrip_target_info)),
+       .userspacesize = XT_ALIGN(sizeof(struct xt_tcpoptstrip_target_info)),
+       .help          = tcpoptstrip_tg_help,
+       .init          = tcpoptstrip_tg_init,
+       .parse         = tcpoptstrip_tg_parse,
+       .final_check   = tcpoptstrip_tg_check,
+       .print         = tcpoptstrip_tg_print,
+       .save          = tcpoptstrip_tg_save,
+       .extra_opts    = tcpoptstrip_tg_opts,
+};
+
+static struct xtables_target tcpoptstrip_tg6_reg = {
+       .version       = XTABLES_VERSION,
+       .name          = "TCPOPTSTRIP",
+       .family        = AF_INET6,
+       .size          = XT_ALIGN(sizeof(struct xt_tcpoptstrip_target_info)),
+       .userspacesize = XT_ALIGN(sizeof(struct xt_tcpoptstrip_target_info)),
+       .help          = tcpoptstrip_tg_help,
+       .init          = tcpoptstrip_tg_init,
+       .parse         = tcpoptstrip_tg_parse,
+       .final_check   = tcpoptstrip_tg_check,
+       .print         = tcpoptstrip_tg_print,
+       .save          = tcpoptstrip_tg_save,
+       .extra_opts    = tcpoptstrip_tg_opts,
+};
+
+void _init(void)
+{
+       xtables_register_target(&tcpoptstrip_tg_reg);
+       xtables_register_target(&tcpoptstrip_tg6_reg);
+}