/* * Copyright (c) 2010-2013 Patrick McHardy <kaber@trash.net> */ #include <stdio.h> #include <string.h> #include <xtables.h> #include <linux/netfilter/nf_conntrack_common.h> #include <linux/netfilter/xt_CT.h> static void ct_help(void) { printf( "CT target options:\n" " --notrack Don't track connection\n" " --helper name Use conntrack helper 'name' for connection\n" " --ctevents event[,event...] Generate specified conntrack events for connection\n" " --expevents event[,event...] Generate specified expectation events for connection\n" " --zone {ID|mark} Assign/Lookup connection in zone ID/packet nfmark\n" " --zone-orig {ID|mark} Same as 'zone' option, but only applies to ORIGINAL direction\n" " --zone-reply {ID|mark} Same as 'zone' option, but only applies to REPLY direction\n" ); } static void ct_help_v1(void) { printf( "CT target options:\n" " --notrack Don't track connection\n" " --helper name Use conntrack helper 'name' for connection\n" " --timeout name Use timeout policy 'name' for connection\n" " --ctevents event[,event...] Generate specified conntrack events for connection\n" " --expevents event[,event...] Generate specified expectation events for connection\n" " --zone {ID|mark} Assign/Lookup connection in zone ID/packet nfmark\n" " --zone-orig {ID|mark} Same as 'zone' option, but only applies to ORIGINAL direction\n" " --zone-reply {ID|mark} Same as 'zone' option, but only applies to REPLY direction\n" ); } enum { O_NOTRACK = 0, O_HELPER, O_TIMEOUT, O_CTEVENTS, O_EXPEVENTS, O_ZONE, O_ZONE_ORIG, O_ZONE_REPLY, }; #define s struct xt_ct_target_info static const struct xt_option_entry ct_opts[] = { {.name = "notrack", .id = O_NOTRACK, .type = XTTYPE_NONE}, {.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING, .flags = XTOPT_PUT, XTOPT_POINTER(s, helper)}, {.name = "ctevents", .id = O_CTEVENTS, .type = XTTYPE_STRING}, {.name = "expevents", .id = O_EXPEVENTS, .type = XTTYPE_STRING}, {.name = "zone-orig", .id = O_ZONE_ORIG, .type = XTTYPE_STRING}, {.name = "zone-reply", .id = O_ZONE_REPLY, .type = XTTYPE_STRING}, {.name = "zone", .id = O_ZONE, .type = XTTYPE_STRING}, XTOPT_TABLEEND, }; #undef s #define s struct xt_ct_target_info_v1 static const struct xt_option_entry ct_opts_v1[] = { {.name = "notrack", .id = O_NOTRACK, .type = XTTYPE_NONE}, {.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING, .flags = XTOPT_PUT, XTOPT_POINTER(s, helper)}, {.name = "timeout", .id = O_TIMEOUT, .type = XTTYPE_STRING, .flags = XTOPT_PUT, XTOPT_POINTER(s, timeout)}, {.name = "ctevents", .id = O_CTEVENTS, .type = XTTYPE_STRING}, {.name = "expevents", .id = O_EXPEVENTS, .type = XTTYPE_STRING}, {.name = "zone-orig", .id = O_ZONE_ORIG, .type = XTTYPE_STRING}, {.name = "zone-reply", .id = O_ZONE_REPLY, .type = XTTYPE_STRING}, {.name = "zone", .id = O_ZONE, .type = XTTYPE_STRING}, XTOPT_TABLEEND, }; #undef s struct event_tbl { const char *name; unsigned int event; }; static const struct event_tbl ct_event_tbl[] = { { "new", IPCT_NEW }, { "related", IPCT_RELATED }, { "destroy", IPCT_DESTROY }, { "reply", IPCT_REPLY }, { "assured", IPCT_ASSURED }, { "protoinfo", IPCT_PROTOINFO }, { "helper", IPCT_HELPER }, { "mark", IPCT_MARK }, { "natseqinfo", IPCT_NATSEQADJ }, { "secmark", IPCT_SECMARK }, }; static const struct event_tbl exp_event_tbl[] = { { "new", IPEXP_NEW }, }; static void ct_parse_zone_id(const char *opt, unsigned int opt_id, uint16_t *zone_id, uint16_t *flags) { if (opt_id == O_ZONE_ORIG) *flags |= XT_CT_ZONE_DIR_ORIG; if (opt_id == O_ZONE_REPLY) *flags |= XT_CT_ZONE_DIR_REPL; *zone_id = 0; if (strcasecmp(opt, "mark") == 0) { *flags |= XT_CT_ZONE_MARK; } else { uintmax_t val; if (!xtables_strtoul(opt, NULL, &val, 0, UINT16_MAX)) xtables_error(PARAMETER_PROBLEM, "Cannot parse %s as a zone ID\n", opt); *zone_id = (uint16_t)val; } } static void ct_print_zone_id(const char *pfx, uint16_t zone_id, uint16_t flags) { printf(" %s", pfx); if ((flags & (XT_CT_ZONE_DIR_ORIG | XT_CT_ZONE_DIR_REPL)) == XT_CT_ZONE_DIR_ORIG) printf("-orig"); if ((flags & (XT_CT_ZONE_DIR_ORIG | XT_CT_ZONE_DIR_REPL)) == XT_CT_ZONE_DIR_REPL) printf("-reply"); if (flags & XT_CT_ZONE_MARK) printf(" mark"); else printf(" %u", zone_id); } static uint32_t ct_parse_events(const struct event_tbl *tbl, unsigned int size, const char *events) { char str[strlen(events) + 1], *e = str, *t; unsigned int mask = 0, i; strcpy(str, events); while ((t = strsep(&e, ","))) { for (i = 0; i < size; i++) { if (strcmp(t, tbl[i].name)) continue; mask |= 1 << tbl[i].event; break; } if (i == size) xtables_error(PARAMETER_PROBLEM, "Unknown event type \"%s\"", t); } return mask; } static void ct_print_events(const char *pfx, const struct event_tbl *tbl, unsigned int size, uint32_t mask) { const char *sep = ""; unsigned int i; printf(" %s ", pfx); for (i = 0; i < size; i++) { if (mask & (1 << tbl[i].event)) { printf("%s%s", sep, tbl[i].name); sep = ","; } } } static void ct_parse(struct xt_option_call *cb) { struct xt_ct_target_info *info = cb->data; xtables_option_parse(cb); switch (cb->entry->id) { case O_NOTRACK: info->flags |= XT_CT_NOTRACK; break; case O_ZONE_ORIG: case O_ZONE_REPLY: case O_ZONE: ct_parse_zone_id(cb->arg, cb->entry->id, &info->zone, &info->flags); break; case O_CTEVENTS: info->ct_events = ct_parse_events(ct_event_tbl, ARRAY_SIZE(ct_event_tbl), cb->arg); break; case O_EXPEVENTS: info->exp_events = ct_parse_events(exp_event_tbl, ARRAY_SIZE(exp_event_tbl), cb->arg); break; } } static void ct_parse_v1(struct xt_option_call *cb) { struct xt_ct_target_info_v1 *info = cb->data; xtables_option_parse(cb); switch (cb->entry->id) { case O_NOTRACK: info->flags |= XT_CT_NOTRACK; break; case O_ZONE_ORIG: case O_ZONE_REPLY: case O_ZONE: ct_parse_zone_id(cb->arg, cb->entry->id, &info->zone, &info->flags); break; case O_CTEVENTS: info->ct_events = ct_parse_events(ct_event_tbl, ARRAY_SIZE(ct_event_tbl), cb->arg); break; case O_EXPEVENTS: info->exp_events = ct_parse_events(exp_event_tbl, ARRAY_SIZE(exp_event_tbl), cb->arg); break; } } static void ct_print(const void *ip, const struct xt_entry_target *target, int numeric) { const struct xt_ct_target_info *info = (const struct xt_ct_target_info *)target->data; printf(" CT"); if (info->flags & XT_CT_NOTRACK) printf(" notrack"); if (info->helper[0]) printf(" helper %s", info->helper); if (info->ct_events) ct_print_events("ctevents", ct_event_tbl, ARRAY_SIZE(ct_event_tbl), info->ct_events); if (info->exp_events) ct_print_events("expevents", exp_event_tbl, ARRAY_SIZE(exp_event_tbl), info->exp_events); if (info->flags & XT_CT_ZONE_MARK || info->zone) ct_print_zone_id("zone", info->zone, info->flags); } static void ct_print_v1(const void *ip, const struct xt_entry_target *target, int numeric) { const struct xt_ct_target_info_v1 *info = (const struct xt_ct_target_info_v1 *)target->data; if (info->flags & XT_CT_NOTRACK_ALIAS) { printf (" NOTRACK"); return; } printf(" CT"); if (info->flags & XT_CT_NOTRACK) printf(" notrack"); if (info->helper[0]) printf(" helper %s", info->helper); if (info->timeout[0]) printf(" timeout %s", info->timeout); if (info->ct_events) ct_print_events("ctevents", ct_event_tbl, ARRAY_SIZE(ct_event_tbl), info->ct_events); if (info->exp_events) ct_print_events("expevents", exp_event_tbl, ARRAY_SIZE(exp_event_tbl), info->exp_events); if (info->flags & XT_CT_ZONE_MARK || info->zone) ct_print_zone_id("zone", info->zone, info->flags); } static void ct_save(const void *ip, const struct xt_entry_target *target) { const struct xt_ct_target_info *info = (const struct xt_ct_target_info *)target->data; if (info->flags & XT_CT_NOTRACK_ALIAS) return; if (info->flags & XT_CT_NOTRACK) printf(" --notrack"); if (info->helper[0]) printf(" --helper %s", info->helper); if (info->ct_events) ct_print_events("--ctevents", ct_event_tbl, ARRAY_SIZE(ct_event_tbl), info->ct_events); if (info->exp_events) ct_print_events("--expevents", exp_event_tbl, ARRAY_SIZE(exp_event_tbl), info->exp_events); if (info->flags & XT_CT_ZONE_MARK || info->zone) ct_print_zone_id("--zone", info->zone, info->flags); } static void ct_save_v1(const void *ip, const struct xt_entry_target *target) { const struct xt_ct_target_info_v1 *info = (const struct xt_ct_target_info_v1 *)target->data; if (info->flags & XT_CT_NOTRACK_ALIAS) return; if (info->flags & XT_CT_NOTRACK) printf(" --notrack"); if (info->helper[0]) printf(" --helper %s", info->helper); if (info->timeout[0]) printf(" --timeout %s", info->timeout); if (info->ct_events) ct_print_events("--ctevents", ct_event_tbl, ARRAY_SIZE(ct_event_tbl), info->ct_events); if (info->exp_events) ct_print_events("--expevents", exp_event_tbl, ARRAY_SIZE(exp_event_tbl), info->exp_events); if (info->flags & XT_CT_ZONE_MARK || info->zone) ct_print_zone_id("--zone", info->zone, info->flags); } static const char * ct_print_name_alias(const struct xt_entry_target *target) { struct xt_ct_target_info *info = (void *)target->data; return info->flags & XT_CT_NOTRACK_ALIAS ? "NOTRACK" : "CT"; } static void notrack_ct0_tg_init(struct xt_entry_target *target) { struct xt_ct_target_info *info = (void *)target->data; info->flags = XT_CT_NOTRACK; } static void notrack_ct1_tg_init(struct xt_entry_target *target) { struct xt_ct_target_info_v1 *info = (void *)target->data; info->flags = XT_CT_NOTRACK; } static void notrack_ct2_tg_init(struct xt_entry_target *target) { struct xt_ct_target_info_v1 *info = (void *)target->data; info->flags = XT_CT_NOTRACK | XT_CT_NOTRACK_ALIAS; } static struct xtables_target ct_target_reg[] = { { .family = NFPROTO_UNSPEC, .name = "CT", .version = XTABLES_VERSION, .size = XT_ALIGN(sizeof(struct xt_ct_target_info)), .userspacesize = offsetof(struct xt_ct_target_info, ct), .help = ct_help, .print = ct_print, .save = ct_save, .x6_parse = ct_parse, .x6_options = ct_opts, }, { .family = NFPROTO_UNSPEC, .name = "CT", .revision = 1, .version = XTABLES_VERSION, .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)), .userspacesize = offsetof(struct xt_ct_target_info_v1, ct), .help = ct_help_v1, .print = ct_print_v1, .save = ct_save_v1, .x6_parse = ct_parse_v1, .x6_options = ct_opts_v1, }, { .family = NFPROTO_UNSPEC, .name = "CT", .revision = 2, .version = XTABLES_VERSION, .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)), .userspacesize = offsetof(struct xt_ct_target_info_v1, ct), .help = ct_help_v1, .print = ct_print_v1, .save = ct_save_v1, .alias = ct_print_name_alias, .x6_parse = ct_parse_v1, .x6_options = ct_opts_v1, }, { .family = NFPROTO_UNSPEC, .name = "NOTRACK", .real_name = "CT", .revision = 0, .version = XTABLES_VERSION, .size = XT_ALIGN(sizeof(struct xt_ct_target_info)), .userspacesize = offsetof(struct xt_ct_target_info, ct), .init = notrack_ct0_tg_init, }, { .family = NFPROTO_UNSPEC, .name = "NOTRACK", .real_name = "CT", .revision = 1, .version = XTABLES_VERSION, .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)), .userspacesize = offsetof(struct xt_ct_target_info_v1, ct), .init = notrack_ct1_tg_init, }, { .family = NFPROTO_UNSPEC, .name = "NOTRACK", .real_name = "CT", .revision = 2, .ext_flags = XTABLES_EXT_ALIAS, .version = XTABLES_VERSION, .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)), .userspacesize = offsetof(struct xt_ct_target_info_v1, ct), .init = notrack_ct2_tg_init, }, { .family = NFPROTO_UNSPEC, .name = "NOTRACK", .revision = 0, .version = XTABLES_VERSION, }, }; void _init(void) { xtables_register_targets(ct_target_reg, ARRAY_SIZE(ct_target_reg)); }