extensions: add connlabel match
allows to "tag" connections with up to 128 label names.
Labels are defined in /etc/xtables/connlabel.conf, example:
0 from eth0
1 via eth0
Labels can then be attached to flows, e.g.
-A PREROUTING -i eth0 -m connlabel --label "from eth0" --set
Signed-off-by: Florian Westphal <fw@strlen.de>
diff --git a/Makefile.am b/Makefile.am
index cd008a1..931c565 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -28,5 +28,9 @@
@mkdir -p -m 755 $(DESTDIR)/etc/xtables/ || :
@test -f /etc/xtables/connlabel.conf || $(INSTALL) -m 644 etc/xtables/connlabel.conf $(DESTDIR)/etc/xtables/connlabel.conf || :
+install-data-hook:
+ @mkdir -p -m 755 $(DESTDIR)/etc/xtables/ || :
+ @test -f /etc/xtables/connlabel.conf || $(INSTALL) -m 644 etc/xtables/connlabel.conf $(DESTDIR)/etc/xtables/connlabel.conf || :
+
config.status: extensions/GNUmakefile.in \
include/xtables-version.h.in include/iptables/internal.h.in
diff --git a/etc/xtables/connlabel.conf b/etc/xtables/connlabel.conf
new file mode 100644
index 0000000..9167029
--- /dev/null
+++ b/etc/xtables/connlabel.conf
@@ -0,0 +1,8 @@
+# example connlabel.conf mapping file.
+# used by the "connlabel" match to translate names to their bit-value.
+0 eth0-in
+1 eth0-out
+2 ppp-in
+3 ppp-out
+4 bulk-traffic
+5 interactive
diff --git a/extensions/libxt_connlabel.c b/extensions/libxt_connlabel.c
new file mode 100644
index 0000000..ae52901
--- /dev/null
+++ b/extensions/libxt_connlabel.c
@@ -0,0 +1,210 @@
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_connlabel.h>
+
+enum {
+ O_LABEL = 0,
+ O_SET = 1,
+};
+
+#define CONNLABEL_CFG "/etc/xtables/connlabel.conf"
+
+static void connlabel_mt_help(void)
+{
+ puts(
+"connlabel match options:\n"
+"[!] --label name Match if label has been set on connection\n"
+" --set Set label on connection");
+}
+
+static const struct xt_option_entry connlabel_mt_opts[] = {
+ {.name = "label", .id = O_LABEL, .type = XTTYPE_STRING,
+ .min = 1, .flags = XTOPT_MAND|XTOPT_INVERT},
+ {.name = "set", .id = O_SET, .type = XTTYPE_NONE},
+ XTOPT_TABLEEND,
+};
+
+static int
+xtables_parse_connlabel_numerical(const char *s, char **end)
+{
+ uintmax_t value;
+
+ if (!xtables_strtoul(s, end, &value, 0, XT_CONNLABEL_MAXBIT))
+ return -1;
+ return value;
+}
+
+static bool is_space_posix(int c)
+{
+ return c == ' ' || c == '\f' || c == '\r' || c == '\t' || c == '\v';
+}
+
+static char * trim_label(char *label)
+{
+ char *end;
+
+ while (is_space_posix(*label))
+ label++;
+ end = strchr(label, '\n');
+ if (end)
+ *end = 0;
+ else
+ end = strchr(label, '\0');
+ end--;
+
+ while (is_space_posix(*end) && end > label) {
+ *end = 0;
+ end--;
+ }
+
+ return *label ? label : NULL;
+}
+
+static void
+xtables_get_connlabel(uint16_t bit, char *buf, size_t len)
+{
+ FILE *fp = fopen(CONNLABEL_CFG, "r");
+ char label[1024];
+ char *end;
+
+ if (!fp)
+ goto error;
+
+ while (fgets(label, sizeof(label), fp)) {
+ int tmp;
+
+ if (label[0] == '#')
+ continue;
+ tmp = xtables_parse_connlabel_numerical(label, &end);
+ if (tmp < 0 || tmp < (int) bit)
+ continue;
+ if (tmp > (int) bit)
+ break;
+
+ end = trim_label(end);
+ if (!end)
+ continue;
+ snprintf(buf, len, "%s", end);
+ fclose(fp);
+ return;
+ }
+ fclose(fp);
+ error:
+ snprintf(buf, len, "%u", (unsigned int) bit);
+}
+
+
+static uint16_t xtables_parse_connlabel(const char *s)
+{
+ FILE *fp = fopen(CONNLABEL_CFG, "r");
+ char label[1024];
+ char *end;
+ int bit;
+
+ if (!fp)
+ xtables_error(PARAMETER_PROBLEM, "label '%s': could not open '%s': %s",
+ s, CONNLABEL_CFG, strerror(errno));
+
+ while (fgets(label, sizeof(label), fp)) {
+ if (label[0] == '#' || !strstr(label, s))
+ continue;
+ bit = xtables_parse_connlabel_numerical(label, &end);
+ if (bit < 0)
+ continue;
+
+ end = trim_label(end);
+ if (!end)
+ continue;
+ if (strcmp(end, s) == 0) {
+ fclose(fp);
+ return bit;
+ }
+ }
+ fclose(fp);
+ xtables_error(PARAMETER_PROBLEM, "label '%s' not found in config file %s",
+ s, CONNLABEL_CFG);
+}
+
+static void connlabel_mt_parse(struct xt_option_call *cb)
+{
+ struct xt_connlabel_mtinfo *info = cb->data;
+ int tmp;
+
+ xtables_option_parse(cb);
+
+ switch (cb->entry->id) {
+ case O_LABEL:
+ tmp = xtables_parse_connlabel_numerical(cb->arg, NULL);
+ info->bit = tmp < 0 ? xtables_parse_connlabel(cb->arg) : tmp;
+
+ if (cb->invert)
+ info->options |= XT_CONNLABEL_OP_INVERT;
+ break;
+ case O_SET:
+ info->options |= XT_CONNLABEL_OP_SET;
+ break;
+ }
+
+}
+
+static void
+connlabel_mt_print_op(const struct xt_connlabel_mtinfo *info, const char *prefix)
+{
+ if (info->options & XT_CONNLABEL_OP_SET)
+ printf(" %sset", prefix);
+}
+
+static void
+connlabel_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+ const struct xt_connlabel_mtinfo *info = (const void *)match->data;
+ char buf[1024];
+
+ printf(" connlabel");
+ if (info->options & XT_CONNLABEL_OP_INVERT)
+ printf(" !");
+ if (numeric) {
+ printf(" %u", info->bit);
+ } else {
+ xtables_get_connlabel(info->bit, buf, sizeof(buf));
+ printf(" '%s'", buf);
+ }
+ connlabel_mt_print_op(info, "");
+}
+
+static void
+connlabel_mt_save(const void *ip, const struct xt_entry_match *match)
+{
+ const struct xt_connlabel_mtinfo *info = (const void *)match->data;
+ char buf[1024];
+
+ if (info->options & XT_CONNLABEL_OP_INVERT)
+ printf(" !");
+
+ xtables_get_connlabel(info->bit, buf, sizeof(buf));
+ printf(" --label \"%s\"", buf);
+
+ connlabel_mt_print_op(info, "--");
+}
+
+static struct xtables_match connlabel_mt_reg = {
+ .family = NFPROTO_UNSPEC,
+ .name = "connlabel",
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct xt_connlabel_mtinfo)),
+ .userspacesize = offsetof(struct xt_connlabel_mtinfo, bit),
+ .help = connlabel_mt_help,
+ .print = connlabel_mt_print,
+ .save = connlabel_mt_save,
+ .x6_parse = connlabel_mt_parse,
+ .x6_options = connlabel_mt_opts,
+};
+
+void _init(void)
+{
+ xtables_register_match(&connlabel_mt_reg);
+}
diff --git a/extensions/libxt_connlabel.man b/extensions/libxt_connlabel.man
new file mode 100644
index 0000000..9fd2043
--- /dev/null
+++ b/extensions/libxt_connlabel.man
@@ -0,0 +1,32 @@
+Module matches or adds connlabels to a connection.
+connlabels are similar to connmarks, except labels are bit-based; i.e.
+all labels may be attached to a flow at the same time.
+Up to 128 unique labels are currently supported.
+.TP
+[\fB!\fP] \fB\-\-label\fP \fBname\fP
+matches if label \fBname\fP has been set on a connection.
+Instead of a name (which will be translated to a number, see EXAMPLE below),
+a number may be used instead. Using a number always overrides connlabel.conf.
+.TP
+\fB\-\-set\fP
+if the label has not been set on the connection, set it.
+Note that setting a label can fail. This is because the kernel allocates the
+conntrack label storage area when the connection is created, and it only
+reserves the amount of memory required by the ruleset that exists at
+the time the connection is created.
+In this case, the match will fail (or succeed, in case \fB\-\-label\fP
+option was negated).
+.PP
+Label translation is done via the \fB/etc/xtables/connlabel.conf\fP configuration file.
+.PP
+Example:
+.IP
+.nf
+0 eth0-in
+1 eth0-out
+2 ppp-in
+3 ppp-out
+4 bulk-traffic
+5 interactive
+.fi
+.PP
diff --git a/include/linux/netfilter/xt_connlabel.h b/include/linux/netfilter/xt_connlabel.h
new file mode 100644
index 0000000..c4bc9ee
--- /dev/null
+++ b/include/linux/netfilter/xt_connlabel.h
@@ -0,0 +1,12 @@
+#include <linux/types.h>
+
+#define XT_CONNLABEL_MAXBIT 127
+enum xt_connlabel_mtopts {
+ XT_CONNLABEL_OP_INVERT = 1 << 0,
+ XT_CONNLABEL_OP_SET = 1 << 1,
+};
+
+struct xt_connlabel_mtinfo {
+ __u16 bit;
+ __u16 options;
+};