#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); }