Home | History | Annotate | Download | only in extensions
      1 /*
      2  * Shared library add-on to iptables to add IPVS matching.
      3  *
      4  * Detailed doc is in the kernel module source net/netfilter/xt_ipvs.c
      5  *
      6  * Author: Hannes Eder <heder (at) google.com>
      7  */
      8 #include <stdbool.h>
      9 #include <stdio.h>
     10 #include <string.h>
     11 #include <xtables.h>
     12 #include <linux/ip_vs.h>
     13 #include <linux/netfilter/xt_ipvs.h>
     14 
     15 enum {
     16 	/* For xt_ipvs: make sure this matches up with %XT_IPVS_*'s order */
     17 	O_IPVS = 0,
     18 	O_VPROTO,
     19 	O_VADDR,
     20 	O_VPORT,
     21 	O_VDIR,
     22 	O_VMETHOD,
     23 	O_VPORTCTL,
     24 };
     25 
     26 #define s struct xt_ipvs_mtinfo
     27 static const struct xt_option_entry ipvs_mt_opts[] = {
     28 	{.name = "ipvs", .id = O_IPVS, .type = XTTYPE_NONE,
     29 	 .flags = XTOPT_INVERT},
     30 	{.name = "vproto", .id = O_VPROTO, .type = XTTYPE_STRING,
     31 	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, l4proto)},
     32 	{.name = "vaddr", .id = O_VADDR, .type = XTTYPE_HOSTMASK,
     33 	 .flags = XTOPT_INVERT},
     34 	{.name = "vport", .id = O_VPORT, .type = XTTYPE_PORT,
     35 	 .flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT,
     36 	 XTOPT_POINTER(s, vport)},
     37 	{.name = "vdir", .id = O_VDIR, .type = XTTYPE_STRING},
     38 	{.name = "vmethod", .id = O_VMETHOD, .type = XTTYPE_STRING,
     39 	 .flags = XTOPT_INVERT},
     40 	{.name = "vportctl", .id = O_VPORTCTL, .type = XTTYPE_PORT,
     41 	 .flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT,
     42 	 XTOPT_POINTER(s, vportctl)},
     43 	XTOPT_TABLEEND,
     44 };
     45 #undef s
     46 
     47 static void ipvs_mt_help(void)
     48 {
     49 	printf(
     50 "IPVS match options:\n"
     51 "[!] --ipvs                      packet belongs to an IPVS connection\n"
     52 "\n"
     53 "Any of the following options implies --ipvs (even negated)\n"
     54 "[!] --vproto protocol           VIP protocol to match; by number or name,\n"
     55 "                                e.g. \"tcp\"\n"
     56 "[!] --vaddr address[/mask]      VIP address to match\n"
     57 "[!] --vport port                VIP port to match; by number or name,\n"
     58 "                                e.g. \"http\"\n"
     59 "    --vdir {ORIGINAL|REPLY}     flow direction of packet\n"
     60 "[!] --vmethod {GATE|IPIP|MASQ}  IPVS forwarding method used\n"
     61 "[!] --vportctl port             VIP port of the controlling connection to\n"
     62 "                                match, e.g. 21 for FTP\n"
     63 		);
     64 }
     65 
     66 static void ipvs_mt_parse(struct xt_option_call *cb)
     67 {
     68 	struct xt_ipvs_mtinfo *data = cb->data;
     69 
     70 	xtables_option_parse(cb);
     71 	switch (cb->entry->id) {
     72 	case O_VPROTO:
     73 		data->l4proto = cb->val.protocol;
     74 		break;
     75 	case O_VADDR:
     76 		memcpy(&data->vaddr, &cb->val.haddr, sizeof(cb->val.haddr));
     77 		memcpy(&data->vmask, &cb->val.hmask, sizeof(cb->val.hmask));
     78 		break;
     79 	case O_VDIR:
     80 		if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
     81 			data->bitmask |= XT_IPVS_DIR;
     82 			data->invert   &= ~XT_IPVS_DIR;
     83 		} else if (strcasecmp(cb->arg, "REPLY") == 0) {
     84 			data->bitmask |= XT_IPVS_DIR;
     85 			data->invert  |= XT_IPVS_DIR;
     86 		} else {
     87 			xtables_param_act(XTF_BAD_VALUE,
     88 					  "ipvs", "--vdir", cb->arg);
     89 		}
     90 		break;
     91 	case O_VMETHOD:
     92 		if (strcasecmp(cb->arg, "GATE") == 0)
     93 			data->fwd_method = IP_VS_CONN_F_DROUTE;
     94 		else if (strcasecmp(cb->arg, "IPIP") == 0)
     95 			data->fwd_method = IP_VS_CONN_F_TUNNEL;
     96 		else if (strcasecmp(cb->arg, "MASQ") == 0)
     97 			data->fwd_method = IP_VS_CONN_F_MASQ;
     98 		else
     99 			xtables_param_act(XTF_BAD_VALUE,
    100 					  "ipvs", "--vmethod", cb->arg);
    101 		break;
    102 	}
    103 	data->bitmask |= 1 << cb->entry->id;
    104 	if (cb->invert)
    105 		data->invert |= 1 << cb->entry->id;
    106 }
    107 
    108 static void ipvs_mt_check(struct xt_fcheck_call *cb)
    109 {
    110 	struct xt_ipvs_mtinfo *info = cb->data;
    111 
    112 	if (cb->xflags == 0)
    113 		xtables_error(PARAMETER_PROBLEM,
    114 			      "IPVS: At least one option is required");
    115 	if (info->bitmask & XT_IPVS_ONCE_MASK) {
    116 		if (info->invert & XT_IPVS_IPVS_PROPERTY)
    117 			xtables_error(PARAMETER_PROBLEM,
    118 				      "! --ipvs cannot be together with"
    119 				      " other options");
    120 		info->bitmask |= XT_IPVS_IPVS_PROPERTY;
    121 	}
    122 }
    123 
    124 /* Shamelessly copied from libxt_conntrack.c */
    125 static void ipvs_mt_dump_addr(const union nf_inet_addr *addr,
    126 			      const union nf_inet_addr *mask,
    127 			      unsigned int family, bool numeric)
    128 {
    129 	char buf[BUFSIZ];
    130 
    131 	if (family == NFPROTO_IPV4) {
    132 		if (!numeric && addr->ip == 0) {
    133 			printf(" anywhere");
    134 			return;
    135 		}
    136 		if (numeric)
    137 			strcpy(buf, xtables_ipaddr_to_numeric(&addr->in));
    138 		else
    139 			strcpy(buf, xtables_ipaddr_to_anyname(&addr->in));
    140 		strcat(buf, xtables_ipmask_to_numeric(&mask->in));
    141 		printf(" %s", buf);
    142 	} else if (family == NFPROTO_IPV6) {
    143 		if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
    144 		    addr->ip6[2] == 0 && addr->ip6[3] == 0) {
    145 			printf(" anywhere");
    146 			return;
    147 		}
    148 		if (numeric)
    149 			strcpy(buf, xtables_ip6addr_to_numeric(&addr->in6));
    150 		else
    151 			strcpy(buf, xtables_ip6addr_to_anyname(&addr->in6));
    152 		strcat(buf, xtables_ip6mask_to_numeric(&mask->in6));
    153 		printf(" %s", buf);
    154 	}
    155 }
    156 
    157 static void ipvs_mt_dump(const void *ip, const struct xt_ipvs_mtinfo *data,
    158 			 unsigned int family, bool numeric, const char *prefix)
    159 {
    160 	if (data->bitmask == XT_IPVS_IPVS_PROPERTY) {
    161 		if (data->invert & XT_IPVS_IPVS_PROPERTY)
    162 			printf(" !");
    163 		printf(" %sipvs", prefix);
    164 	}
    165 
    166 	if (data->bitmask & XT_IPVS_PROTO) {
    167 		if (data->invert & XT_IPVS_PROTO)
    168 			printf(" !");
    169 		printf(" %sproto %u", prefix, data->l4proto);
    170 	}
    171 
    172 	if (data->bitmask & XT_IPVS_VADDR) {
    173 		if (data->invert & XT_IPVS_VADDR)
    174 			printf(" !");
    175 
    176 		printf(" %svaddr", prefix);
    177 		ipvs_mt_dump_addr(&data->vaddr, &data->vmask, family, numeric);
    178 	}
    179 
    180 	if (data->bitmask & XT_IPVS_VPORT) {
    181 		if (data->invert & XT_IPVS_VPORT)
    182 			printf(" !");
    183 
    184 		printf(" %svport %u", prefix, ntohs(data->vport));
    185 	}
    186 
    187 	if (data->bitmask & XT_IPVS_DIR) {
    188 		if (data->invert & XT_IPVS_DIR)
    189 			printf(" %svdir REPLY", prefix);
    190 		else
    191 			printf(" %svdir ORIGINAL", prefix);
    192 	}
    193 
    194 	if (data->bitmask & XT_IPVS_METHOD) {
    195 		if (data->invert & XT_IPVS_METHOD)
    196 			printf(" !");
    197 
    198 		printf(" %svmethod", prefix);
    199 		switch (data->fwd_method) {
    200 		case IP_VS_CONN_F_DROUTE:
    201 			printf(" GATE");
    202 			break;
    203 		case IP_VS_CONN_F_TUNNEL:
    204 			printf(" IPIP");
    205 			break;
    206 		case IP_VS_CONN_F_MASQ:
    207 			printf(" MASQ");
    208 			break;
    209 		default:
    210 			/* Hu? */
    211 			printf(" UNKNOWN");
    212 			break;
    213 		}
    214 	}
    215 
    216 	if (data->bitmask & XT_IPVS_VPORTCTL) {
    217 		if (data->invert & XT_IPVS_VPORTCTL)
    218 			printf(" !");
    219 
    220 		printf(" %svportctl %u", prefix, ntohs(data->vportctl));
    221 	}
    222 }
    223 
    224 static void ipvs_mt4_print(const void *ip, const struct xt_entry_match *match,
    225 			   int numeric)
    226 {
    227 	const struct xt_ipvs_mtinfo *data = (const void *)match->data;
    228 	ipvs_mt_dump(ip, data, NFPROTO_IPV4, numeric, "");
    229 }
    230 
    231 static void ipvs_mt6_print(const void *ip, const struct xt_entry_match *match,
    232 			   int numeric)
    233 {
    234 	const struct xt_ipvs_mtinfo *data = (const void *)match->data;
    235 	ipvs_mt_dump(ip, data, NFPROTO_IPV6, numeric, "");
    236 }
    237 
    238 static void ipvs_mt4_save(const void *ip, const struct xt_entry_match *match)
    239 {
    240 	const struct xt_ipvs_mtinfo *data = (const void *)match->data;
    241 	ipvs_mt_dump(ip, data, NFPROTO_IPV4, true, "--");
    242 }
    243 
    244 static void ipvs_mt6_save(const void *ip, const struct xt_entry_match *match)
    245 {
    246 	const struct xt_ipvs_mtinfo *data = (const void *)match->data;
    247 	ipvs_mt_dump(ip, data, NFPROTO_IPV6, true, "--");
    248 }
    249 
    250 static struct xtables_match ipvs_matches_reg[] = {
    251 	{
    252 		.version       = XTABLES_VERSION,
    253 		.name          = "ipvs",
    254 		.revision      = 0,
    255 		.family        = NFPROTO_IPV4,
    256 		.size          = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
    257 		.userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
    258 		.help          = ipvs_mt_help,
    259 		.x6_parse      = ipvs_mt_parse,
    260 		.x6_fcheck     = ipvs_mt_check,
    261 		.print         = ipvs_mt4_print,
    262 		.save          = ipvs_mt4_save,
    263 		.x6_options    = ipvs_mt_opts,
    264 	},
    265 	{
    266 		.version       = XTABLES_VERSION,
    267 		.name          = "ipvs",
    268 		.revision      = 0,
    269 		.family        = NFPROTO_IPV6,
    270 		.size          = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
    271 		.userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
    272 		.help          = ipvs_mt_help,
    273 		.x6_parse      = ipvs_mt_parse,
    274 		.x6_fcheck     = ipvs_mt_check,
    275 		.print         = ipvs_mt6_print,
    276 		.save          = ipvs_mt6_save,
    277 		.x6_options    = ipvs_mt_opts,
    278 	},
    279 };
    280 
    281 void _init(void)
    282 {
    283 	xtables_register_matches(ipvs_matches_reg,
    284 				 ARRAY_SIZE(ipvs_matches_reg));
    285 }
    286