| /* Shared library add-on to iptables to add addrtype matching support |
| * |
| * Copyright (c) 2003-2013 Patrick McHardy <kaber@trash.net> |
| * |
| * This program is released under the terms of GNU GPL */ |
| #include <stdio.h> |
| #include <string.h> |
| #include <strings.h> |
| #include <xtables.h> |
| #include <linux/netfilter/xt_addrtype.h> |
| |
| enum { |
| O_SRC_TYPE = 0, |
| O_DST_TYPE, |
| O_LIMIT_IFACE_IN, |
| O_LIMIT_IFACE_OUT, |
| F_SRC_TYPE = 1 << O_SRC_TYPE, |
| F_DST_TYPE = 1 << O_DST_TYPE, |
| F_LIMIT_IFACE_IN = 1 << O_LIMIT_IFACE_IN, |
| F_LIMIT_IFACE_OUT = 1 << O_LIMIT_IFACE_OUT, |
| }; |
| |
| /* from linux/rtnetlink.h, must match order of enumeration */ |
| static const char *const rtn_names[] = { |
| "UNSPEC", |
| "UNICAST", |
| "LOCAL", |
| "BROADCAST", |
| "ANYCAST", |
| "MULTICAST", |
| "BLACKHOLE", |
| "UNREACHABLE", |
| "PROHIBIT", |
| "THROW", |
| "NAT", |
| "XRESOLVE", |
| NULL |
| }; |
| |
| static void addrtype_help_types(void) |
| { |
| int i; |
| |
| for (i = 0; rtn_names[i]; i++) |
| printf(" %s\n", rtn_names[i]); |
| } |
| |
| static void addrtype_help_v0(void) |
| { |
| printf( |
| "Address type match options:\n" |
| " [!] --src-type type[,...] Match source address type\n" |
| " [!] --dst-type type[,...] Match destination address type\n" |
| "\n" |
| "Valid types: \n"); |
| addrtype_help_types(); |
| } |
| |
| static void addrtype_help_v1(void) |
| { |
| printf( |
| "Address type match options:\n" |
| " [!] --src-type type[,...] Match source address type\n" |
| " [!] --dst-type type[,...] Match destination address type\n" |
| " --limit-iface-in Match only on the packet's incoming device\n" |
| " --limit-iface-out Match only on the packet's outgoing device\n" |
| "\n" |
| "Valid types: \n"); |
| addrtype_help_types(); |
| } |
| |
| static int |
| parse_type(const char *name, size_t len, uint16_t *mask) |
| { |
| int i; |
| |
| for (i = 0; rtn_names[i]; i++) |
| if (strncasecmp(name, rtn_names[i], len) == 0) { |
| /* build up bitmask for kernel module */ |
| *mask |= (1 << i); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static void parse_types(const char *arg, uint16_t *mask) |
| { |
| const char *comma; |
| |
| while ((comma = strchr(arg, ',')) != NULL) { |
| if (comma == arg || !parse_type(arg, comma-arg, mask)) |
| xtables_error(PARAMETER_PROBLEM, |
| "addrtype: bad type `%s'", arg); |
| arg = comma + 1; |
| } |
| |
| if (strlen(arg) == 0 || !parse_type(arg, strlen(arg), mask)) |
| xtables_error(PARAMETER_PROBLEM, "addrtype: bad type \"%s\"", arg); |
| } |
| |
| static void addrtype_parse_v0(struct xt_option_call *cb) |
| { |
| struct xt_addrtype_info *info = cb->data; |
| |
| xtables_option_parse(cb); |
| switch (cb->entry->id) { |
| case O_SRC_TYPE: |
| parse_types(cb->arg, &info->source); |
| if (cb->invert) |
| info->invert_source = 1; |
| break; |
| case O_DST_TYPE: |
| parse_types(cb->arg, &info->dest); |
| if (cb->invert) |
| info->invert_dest = 1; |
| break; |
| } |
| } |
| |
| static void addrtype_parse_v1(struct xt_option_call *cb) |
| { |
| struct xt_addrtype_info_v1 *info = cb->data; |
| |
| xtables_option_parse(cb); |
| switch (cb->entry->id) { |
| case O_SRC_TYPE: |
| parse_types(cb->arg, &info->source); |
| if (cb->invert) |
| info->flags |= XT_ADDRTYPE_INVERT_SOURCE; |
| break; |
| case O_DST_TYPE: |
| parse_types(cb->arg, &info->dest); |
| if (cb->invert) |
| info->flags |= XT_ADDRTYPE_INVERT_DEST; |
| break; |
| case O_LIMIT_IFACE_IN: |
| info->flags |= XT_ADDRTYPE_LIMIT_IFACE_IN; |
| break; |
| case O_LIMIT_IFACE_OUT: |
| info->flags |= XT_ADDRTYPE_LIMIT_IFACE_OUT; |
| break; |
| } |
| } |
| |
| static void addrtype_check(struct xt_fcheck_call *cb) |
| { |
| if (!(cb->xflags & (F_SRC_TYPE | F_DST_TYPE))) |
| xtables_error(PARAMETER_PROBLEM, |
| "addrtype: you must specify --src-type or --dst-type"); |
| } |
| |
| static void print_types(uint16_t mask) |
| { |
| const char *sep = ""; |
| int i; |
| |
| for (i = 0; rtn_names[i]; i++) |
| if (mask & (1 << i)) { |
| printf("%s%s", sep, rtn_names[i]); |
| sep = ","; |
| } |
| } |
| |
| static void addrtype_print_v0(const void *ip, const struct xt_entry_match *match, |
| int numeric) |
| { |
| const struct xt_addrtype_info *info = (const void *)match->data; |
| |
| printf(" ADDRTYPE match"); |
| if (info->source) { |
| printf(" src-type "); |
| if (info->invert_source) |
| printf("!"); |
| print_types(info->source); |
| } |
| if (info->dest) { |
| printf(" dst-type"); |
| if (info->invert_dest) |
| printf("!"); |
| print_types(info->dest); |
| } |
| } |
| |
| static void addrtype_print_v1(const void *ip, const struct xt_entry_match *match, |
| int numeric) |
| { |
| const struct xt_addrtype_info_v1 *info = (const void *)match->data; |
| |
| printf(" ADDRTYPE match"); |
| if (info->source) { |
| printf(" src-type "); |
| if (info->flags & XT_ADDRTYPE_INVERT_SOURCE) |
| printf("!"); |
| print_types(info->source); |
| } |
| if (info->dest) { |
| printf(" dst-type "); |
| if (info->flags & XT_ADDRTYPE_INVERT_DEST) |
| printf("!"); |
| print_types(info->dest); |
| } |
| if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN) |
| printf(" limit-in"); |
| if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT) |
| printf(" limit-out"); |
| } |
| |
| static void addrtype_save_v0(const void *ip, const struct xt_entry_match *match) |
| { |
| const struct xt_addrtype_info *info = (const void *)match->data; |
| |
| if (info->source) { |
| if (info->invert_source) |
| printf(" !"); |
| printf(" --src-type "); |
| print_types(info->source); |
| } |
| if (info->dest) { |
| if (info->invert_dest) |
| printf(" !"); |
| printf(" --dst-type "); |
| print_types(info->dest); |
| } |
| } |
| |
| static void addrtype_save_v1(const void *ip, const struct xt_entry_match *match) |
| { |
| const struct xt_addrtype_info_v1 *info = (const void *)match->data; |
| |
| if (info->source) { |
| if (info->flags & XT_ADDRTYPE_INVERT_SOURCE) |
| printf(" !"); |
| printf(" --src-type "); |
| print_types(info->source); |
| } |
| if (info->dest) { |
| if (info->flags & XT_ADDRTYPE_INVERT_DEST) |
| printf(" !"); |
| printf(" --dst-type "); |
| print_types(info->dest); |
| } |
| if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN) |
| printf(" --limit-iface-in"); |
| if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT) |
| printf(" --limit-iface-out"); |
| } |
| |
| static const char *const rtn_lnames[] = { |
| "unspec", |
| "unicast", |
| "local", |
| "broadcast", |
| "anycast", |
| "multicast", |
| "blackhole", |
| "unreachable", |
| "prohibit", |
| NULL, |
| }; |
| |
| static bool multiple_bits_set(uint16_t val) |
| { |
| int first = ffs(val); |
| |
| return first && (val >> first) > 0; |
| } |
| |
| static int addrtype_xlate(struct xt_xlate *xl, |
| const struct xt_xlate_mt_params *params) |
| { |
| const struct xt_addrtype_info_v1 *info = |
| (const void *)params->match->data; |
| const char *sep = ""; |
| bool need_braces; |
| uint16_t val; |
| int i; |
| |
| xt_xlate_add(xl, "fib "); |
| |
| if (info->source) { |
| xt_xlate_add(xl, "saddr "); |
| val = info->source; |
| } else { |
| xt_xlate_add(xl, "daddr "); |
| val = info->dest; |
| } |
| |
| if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN) |
| xt_xlate_add(xl, ". iif "); |
| else if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT) |
| xt_xlate_add(xl, ". oif "); |
| |
| xt_xlate_add(xl, "type "); |
| |
| if (info->flags & (XT_ADDRTYPE_INVERT_SOURCE | XT_ADDRTYPE_INVERT_DEST)) |
| xt_xlate_add(xl, "!= "); |
| |
| need_braces = multiple_bits_set(val); |
| |
| if (need_braces) |
| xt_xlate_add(xl, "{ "); |
| |
| for (i = 0; rtn_lnames[i]; i++) { |
| if (val & (1 << i)) { |
| xt_xlate_add(xl, "%s%s", sep, rtn_lnames[i]); |
| sep = ", "; |
| } |
| } |
| |
| if (need_braces) |
| xt_xlate_add(xl, " }"); |
| |
| return 1; |
| } |
| |
| static const struct xt_option_entry addrtype_opts_v0[] = { |
| {.name = "src-type", .id = O_SRC_TYPE, .type = XTTYPE_STRING, |
| .flags = XTOPT_INVERT}, |
| {.name = "dst-type", .id = O_DST_TYPE, .type = XTTYPE_STRING, |
| .flags = XTOPT_INVERT}, |
| XTOPT_TABLEEND, |
| }; |
| |
| static const struct xt_option_entry addrtype_opts_v1[] = { |
| {.name = "src-type", .id = O_SRC_TYPE, .type = XTTYPE_STRING, |
| .flags = XTOPT_INVERT}, |
| {.name = "dst-type", .id = O_DST_TYPE, .type = XTTYPE_STRING, |
| .flags = XTOPT_INVERT}, |
| {.name = "limit-iface-in", .id = O_LIMIT_IFACE_IN, |
| .type = XTTYPE_NONE, .excl = F_LIMIT_IFACE_OUT}, |
| {.name = "limit-iface-out", .id = O_LIMIT_IFACE_OUT, |
| .type = XTTYPE_NONE, .excl = F_LIMIT_IFACE_IN}, |
| XTOPT_TABLEEND, |
| }; |
| |
| static struct xtables_match addrtype_mt_reg[] = { |
| { |
| .name = "addrtype", |
| .version = XTABLES_VERSION, |
| .family = NFPROTO_IPV4, |
| .size = XT_ALIGN(sizeof(struct xt_addrtype_info)), |
| .userspacesize = XT_ALIGN(sizeof(struct xt_addrtype_info)), |
| .help = addrtype_help_v0, |
| .print = addrtype_print_v0, |
| .save = addrtype_save_v0, |
| .x6_parse = addrtype_parse_v0, |
| .x6_fcheck = addrtype_check, |
| .x6_options = addrtype_opts_v0, |
| }, |
| { |
| .name = "addrtype", |
| .revision = 1, |
| .version = XTABLES_VERSION, |
| .family = NFPROTO_UNSPEC, |
| .size = XT_ALIGN(sizeof(struct xt_addrtype_info_v1)), |
| .userspacesize = XT_ALIGN(sizeof(struct xt_addrtype_info_v1)), |
| .help = addrtype_help_v1, |
| .print = addrtype_print_v1, |
| .save = addrtype_save_v1, |
| .x6_parse = addrtype_parse_v1, |
| .x6_fcheck = addrtype_check, |
| .x6_options = addrtype_opts_v1, |
| .xlate = addrtype_xlate, |
| }, |
| }; |
| |
| |
| void _init(void) |
| { |
| xtables_register_matches(addrtype_mt_reg, ARRAY_SIZE(addrtype_mt_reg)); |
| } |