From ccbf6b6448a4210432b76fd4660798705b05f8c4 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 6 May 2013 21:07:38 +0200 Subject: 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 --- extensions/libxt_connlabel.c | 210 +++++++++++++++++++++++++++++++++++++++++ extensions/libxt_connlabel.man | 32 +++++++ 2 files changed, 242 insertions(+) create mode 100644 extensions/libxt_connlabel.c create mode 100644 extensions/libxt_connlabel.man (limited to 'extensions') diff --git a/extensions/libxt_connlabel.c b/extensions/libxt_connlabel.c new file mode 100644 index 00000000..ae52901b --- /dev/null +++ b/extensions/libxt_connlabel.c @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#include +#include + +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 00000000..9fd2043d --- /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 -- cgit v1.2.3