| /* |
| * Shared library add-on to iptables to add IPVS matching. |
| * |
| * Detailed doc is in the kernel module source net/netfilter/xt_ipvs.c |
| * |
| * Author: Hannes Eder <heder@google.com> |
| */ |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <xtables.h> |
| #include <linux/ip_vs.h> |
| #include <linux/netfilter/xt_ipvs.h> |
| |
| enum { |
| /* For xt_ipvs: make sure this matches up with %XT_IPVS_*'s order */ |
| O_IPVS = 0, |
| O_VPROTO, |
| O_VADDR, |
| O_VPORT, |
| O_VDIR, |
| O_VMETHOD, |
| O_VPORTCTL, |
| }; |
| |
| #define s struct xt_ipvs_mtinfo |
| static const struct xt_option_entry ipvs_mt_opts[] = { |
| {.name = "ipvs", .id = O_IPVS, .type = XTTYPE_NONE, |
| .flags = XTOPT_INVERT}, |
| {.name = "vproto", .id = O_VPROTO, .type = XTTYPE_STRING, |
| .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, l4proto)}, |
| {.name = "vaddr", .id = O_VADDR, .type = XTTYPE_HOSTMASK, |
| .flags = XTOPT_INVERT}, |
| {.name = "vport", .id = O_VPORT, .type = XTTYPE_PORT, |
| .flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT, |
| XTOPT_POINTER(s, vport)}, |
| {.name = "vdir", .id = O_VDIR, .type = XTTYPE_STRING}, |
| {.name = "vmethod", .id = O_VMETHOD, .type = XTTYPE_STRING, |
| .flags = XTOPT_INVERT}, |
| {.name = "vportctl", .id = O_VPORTCTL, .type = XTTYPE_PORT, |
| .flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT, |
| XTOPT_POINTER(s, vportctl)}, |
| XTOPT_TABLEEND, |
| }; |
| #undef s |
| |
| static void ipvs_mt_help(void) |
| { |
| printf( |
| "IPVS match options:\n" |
| "[!] --ipvs packet belongs to an IPVS connection\n" |
| "\n" |
| "Any of the following options implies --ipvs (even negated)\n" |
| "[!] --vproto protocol VIP protocol to match; by number or name,\n" |
| " e.g. \"tcp\"\n" |
| "[!] --vaddr address[/mask] VIP address to match\n" |
| "[!] --vport port VIP port to match; by number or name,\n" |
| " e.g. \"http\"\n" |
| " --vdir {ORIGINAL|REPLY} flow direction of packet\n" |
| "[!] --vmethod {GATE|IPIP|MASQ} IPVS forwarding method used\n" |
| "[!] --vportctl port VIP port of the controlling connection to\n" |
| " match, e.g. 21 for FTP\n" |
| ); |
| } |
| |
| static void ipvs_mt_parse(struct xt_option_call *cb) |
| { |
| struct xt_ipvs_mtinfo *data = cb->data; |
| |
| xtables_option_parse(cb); |
| switch (cb->entry->id) { |
| case O_VPROTO: |
| data->l4proto = cb->val.protocol; |
| break; |
| case O_VADDR: |
| memcpy(&data->vaddr, &cb->val.haddr, sizeof(cb->val.haddr)); |
| memcpy(&data->vmask, &cb->val.hmask, sizeof(cb->val.hmask)); |
| break; |
| case O_VDIR: |
| if (strcasecmp(cb->arg, "ORIGINAL") == 0) { |
| data->bitmask |= XT_IPVS_DIR; |
| data->invert &= ~XT_IPVS_DIR; |
| } else if (strcasecmp(cb->arg, "REPLY") == 0) { |
| data->bitmask |= XT_IPVS_DIR; |
| data->invert |= XT_IPVS_DIR; |
| } else { |
| xtables_param_act(XTF_BAD_VALUE, |
| "ipvs", "--vdir", cb->arg); |
| } |
| break; |
| case O_VMETHOD: |
| if (strcasecmp(cb->arg, "GATE") == 0) |
| data->fwd_method = IP_VS_CONN_F_DROUTE; |
| else if (strcasecmp(cb->arg, "IPIP") == 0) |
| data->fwd_method = IP_VS_CONN_F_TUNNEL; |
| else if (strcasecmp(cb->arg, "MASQ") == 0) |
| data->fwd_method = IP_VS_CONN_F_MASQ; |
| else |
| xtables_param_act(XTF_BAD_VALUE, |
| "ipvs", "--vmethod", cb->arg); |
| break; |
| } |
| data->bitmask |= 1 << cb->entry->id; |
| if (cb->invert) |
| data->invert |= 1 << cb->entry->id; |
| } |
| |
| static void ipvs_mt_check(struct xt_fcheck_call *cb) |
| { |
| struct xt_ipvs_mtinfo *info = cb->data; |
| |
| if (cb->xflags == 0) |
| xtables_error(PARAMETER_PROBLEM, |
| "IPVS: At least one option is required"); |
| if (info->bitmask & XT_IPVS_ONCE_MASK) { |
| if (info->invert & XT_IPVS_IPVS_PROPERTY) |
| xtables_error(PARAMETER_PROBLEM, |
| "! --ipvs cannot be together with" |
| " other options"); |
| info->bitmask |= XT_IPVS_IPVS_PROPERTY; |
| } |
| } |
| |
| /* Shamelessly copied from libxt_conntrack.c */ |
| static void ipvs_mt_dump_addr(const union nf_inet_addr *addr, |
| const union nf_inet_addr *mask, |
| unsigned int family, bool numeric) |
| { |
| char buf[BUFSIZ]; |
| |
| if (family == NFPROTO_IPV4) { |
| if (!numeric && addr->ip == 0) { |
| printf(" anywhere"); |
| return; |
| } |
| if (numeric) |
| strcpy(buf, xtables_ipaddr_to_numeric(&addr->in)); |
| else |
| strcpy(buf, xtables_ipaddr_to_anyname(&addr->in)); |
| strcat(buf, xtables_ipmask_to_numeric(&mask->in)); |
| printf(" %s", buf); |
| } else if (family == NFPROTO_IPV6) { |
| if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 && |
| addr->ip6[2] == 0 && addr->ip6[3] == 0) { |
| printf(" anywhere"); |
| return; |
| } |
| if (numeric) |
| strcpy(buf, xtables_ip6addr_to_numeric(&addr->in6)); |
| else |
| strcpy(buf, xtables_ip6addr_to_anyname(&addr->in6)); |
| strcat(buf, xtables_ip6mask_to_numeric(&mask->in6)); |
| printf(" %s", buf); |
| } |
| } |
| |
| static void ipvs_mt_dump(const void *ip, const struct xt_ipvs_mtinfo *data, |
| unsigned int family, bool numeric, const char *prefix) |
| { |
| if (data->bitmask == XT_IPVS_IPVS_PROPERTY) { |
| if (data->invert & XT_IPVS_IPVS_PROPERTY) |
| printf(" !"); |
| printf(" %sipvs", prefix); |
| } |
| |
| if (data->bitmask & XT_IPVS_PROTO) { |
| if (data->invert & XT_IPVS_PROTO) |
| printf(" !"); |
| printf(" %sproto %u", prefix, data->l4proto); |
| } |
| |
| if (data->bitmask & XT_IPVS_VADDR) { |
| if (data->invert & XT_IPVS_VADDR) |
| printf(" !"); |
| |
| printf(" %svaddr", prefix); |
| ipvs_mt_dump_addr(&data->vaddr, &data->vmask, family, numeric); |
| } |
| |
| if (data->bitmask & XT_IPVS_VPORT) { |
| if (data->invert & XT_IPVS_VPORT) |
| printf(" !"); |
| |
| printf(" %svport %u", prefix, ntohs(data->vport)); |
| } |
| |
| if (data->bitmask & XT_IPVS_DIR) { |
| if (data->invert & XT_IPVS_DIR) |
| printf(" %svdir REPLY", prefix); |
| else |
| printf(" %svdir ORIGINAL", prefix); |
| } |
| |
| if (data->bitmask & XT_IPVS_METHOD) { |
| if (data->invert & XT_IPVS_METHOD) |
| printf(" !"); |
| |
| printf(" %svmethod", prefix); |
| switch (data->fwd_method) { |
| case IP_VS_CONN_F_DROUTE: |
| printf(" GATE"); |
| break; |
| case IP_VS_CONN_F_TUNNEL: |
| printf(" IPIP"); |
| break; |
| case IP_VS_CONN_F_MASQ: |
| printf(" MASQ"); |
| break; |
| default: |
| /* Hu? */ |
| printf(" UNKNOWN"); |
| break; |
| } |
| } |
| |
| if (data->bitmask & XT_IPVS_VPORTCTL) { |
| if (data->invert & XT_IPVS_VPORTCTL) |
| printf(" !"); |
| |
| printf(" %svportctl %u", prefix, ntohs(data->vportctl)); |
| } |
| } |
| |
| static void ipvs_mt4_print(const void *ip, const struct xt_entry_match *match, |
| int numeric) |
| { |
| const struct xt_ipvs_mtinfo *data = (const void *)match->data; |
| ipvs_mt_dump(ip, data, NFPROTO_IPV4, numeric, ""); |
| } |
| |
| static void ipvs_mt6_print(const void *ip, const struct xt_entry_match *match, |
| int numeric) |
| { |
| const struct xt_ipvs_mtinfo *data = (const void *)match->data; |
| ipvs_mt_dump(ip, data, NFPROTO_IPV6, numeric, ""); |
| } |
| |
| static void ipvs_mt4_save(const void *ip, const struct xt_entry_match *match) |
| { |
| const struct xt_ipvs_mtinfo *data = (const void *)match->data; |
| ipvs_mt_dump(ip, data, NFPROTO_IPV4, true, "--"); |
| } |
| |
| static void ipvs_mt6_save(const void *ip, const struct xt_entry_match *match) |
| { |
| const struct xt_ipvs_mtinfo *data = (const void *)match->data; |
| ipvs_mt_dump(ip, data, NFPROTO_IPV6, true, "--"); |
| } |
| |
| static struct xtables_match ipvs_matches_reg[] = { |
| { |
| .version = XTABLES_VERSION, |
| .name = "ipvs", |
| .revision = 0, |
| .family = NFPROTO_IPV4, |
| .size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)), |
| .userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)), |
| .help = ipvs_mt_help, |
| .x6_parse = ipvs_mt_parse, |
| .x6_fcheck = ipvs_mt_check, |
| .print = ipvs_mt4_print, |
| .save = ipvs_mt4_save, |
| .x6_options = ipvs_mt_opts, |
| }, |
| { |
| .version = XTABLES_VERSION, |
| .name = "ipvs", |
| .revision = 0, |
| .family = NFPROTO_IPV6, |
| .size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)), |
| .userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)), |
| .help = ipvs_mt_help, |
| .x6_parse = ipvs_mt_parse, |
| .x6_fcheck = ipvs_mt_check, |
| .print = ipvs_mt6_print, |
| .save = ipvs_mt6_save, |
| .x6_options = ipvs_mt_opts, |
| }, |
| }; |
| |
| void _init(void) |
| { |
| xtables_register_matches(ipvs_matches_reg, |
| ARRAY_SIZE(ipvs_matches_reg)); |
| } |