Blame extensions/libxt_ipvs.c

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