You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
7.8 KiB
285 lines
7.8 KiB
/* |
|
* 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)); |
|
}
|
|
|