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_PROTOCOL,
|
|
.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_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)
|
|
{
|
|
if (family == NFPROTO_IPV4) {
|
|
if (!numeric && addr->ip == 0) {
|
|
printf(" anywhere");
|
|
return;
|
|
}
|
|
if (numeric)
|
|
printf(" %s%s",
|
|
xtables_ipaddr_to_numeric(&addr->in),
|
|
xtables_ipmask_to_numeric(&mask->in));
|
|
else
|
|
printf(" %s%s",
|
|
xtables_ipaddr_to_anyname(&addr->in),
|
|
xtables_ipmask_to_numeric(&mask->in));
|
|
} 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)
|
|
printf(" %s%s",
|
|
xtables_ip6addr_to_numeric(&addr->in6),
|
|
xtables_ip6mask_to_numeric(&mask->in6));
|
|
else
|
|
printf(" %s%s",
|
|
xtables_ip6addr_to_anyname(&addr->in6),
|
|
xtables_ip6mask_to_numeric(&mask->in6));
|
|
}
|
|
}
|
|
|
|
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(" %svproto %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));
|
|
}
|