libxt_ipvs.c revision d09b6d591ca7d7d7575cb6aa20384c9830f777ab
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@google.com> 7 */ 8#include <sys/types.h> 9#include <assert.h> 10#include <ctype.h> 11#include <errno.h> 12#include <getopt.h> 13#include <netdb.h> 14#include <stdbool.h> 15#include <stdlib.h> 16#include <stdio.h> 17#include <string.h> 18#include <xtables.h> 19#include <linux/ip_vs.h> 20#include <linux/netfilter/xt_ipvs.h> 21 22static const struct option ipvs_mt_opts[] = { 23 { .name = "ipvs", .has_arg = false, .val = '0' }, 24 { .name = "vproto", .has_arg = true, .val = '1' }, 25 { .name = "vaddr", .has_arg = true, .val = '2' }, 26 { .name = "vport", .has_arg = true, .val = '3' }, 27 { .name = "vdir", .has_arg = true, .val = '4' }, 28 { .name = "vmethod", .has_arg = true, .val = '5' }, 29 { .name = "vportctl", .has_arg = true, .val = '6' }, 30 XT_GETOPT_TABLEEND, 31}; 32 33static void ipvs_mt_help(void) 34{ 35 printf( 36"IPVS match options:\n" 37"[!] --ipvs packet belongs to an IPVS connection\n" 38"\n" 39"Any of the following options implies --ipvs (even negated)\n" 40"[!] --vproto protocol VIP protocol to match; by number or name,\n" 41" e.g. \"tcp\"\n" 42"[!] --vaddr address[/mask] VIP address to match\n" 43"[!] --vport port VIP port to match; by number or name,\n" 44" e.g. \"http\"\n" 45" --vdir {ORIGINAL|REPLY} flow direction of packet\n" 46"[!] --vmethod {GATE|IPIP|MASQ} IPVS forwarding method used\n" 47"[!] --vportctl port VIP port of the controlling connection to\n" 48" match, e.g. 21 for FTP\n" 49 ); 50} 51 52static void ipvs_mt_parse_addr_and_mask(const char *arg, 53 union nf_inet_addr *address, 54 union nf_inet_addr *mask, 55 unsigned int family) 56{ 57 struct in_addr *addr = NULL; 58 struct in6_addr *addr6 = NULL; 59 unsigned int naddrs = 0; 60 61 if (family == NFPROTO_IPV4) { 62 xtables_ipparse_any(arg, &addr, &mask->in, &naddrs); 63 if (naddrs > 1) 64 xtables_error(PARAMETER_PROBLEM, 65 "multiple IP addresses not allowed"); 66 if (naddrs == 1) 67 memcpy(&address->in, addr, sizeof(*addr)); 68 } else if (family == NFPROTO_IPV6) { 69 xtables_ip6parse_any(arg, &addr6, &mask->in6, &naddrs); 70 if (naddrs > 1) 71 xtables_error(PARAMETER_PROBLEM, 72 "multiple IP addresses not allowed"); 73 if (naddrs == 1) 74 memcpy(&address->in6, addr6, sizeof(*addr6)); 75 } else { 76 /* Hu? */ 77 assert(false); 78 } 79} 80 81/* Function which parses command options; returns true if it ate an option */ 82static int ipvs_mt_parse(int c, char **argv, int invert, unsigned int *flags, 83 const void *entry, struct xt_entry_match **match, 84 unsigned int family) 85{ 86 struct xt_ipvs_mtinfo *data = (void *)(*match)->data; 87 char *p = NULL; 88 uint8_t op = 0; 89 90 if ('0' <= c && c <= '6') { 91 static const int ops[] = { 92 XT_IPVS_IPVS_PROPERTY, 93 XT_IPVS_PROTO, 94 XT_IPVS_VADDR, 95 XT_IPVS_VPORT, 96 XT_IPVS_DIR, 97 XT_IPVS_METHOD, 98 XT_IPVS_VPORTCTL 99 }; 100 op = ops[c - '0']; 101 } else 102 return 0; 103 104 if (*flags & op & XT_IPVS_ONCE_MASK) 105 goto multiple_use; 106 107 switch (c) { 108 case '0': /* --ipvs */ 109 /* Nothing to do here. */ 110 break; 111 112 case '1': /* --vproto */ 113 /* Canonicalize into lower case */ 114 for (p = optarg; *p != '\0'; ++p) 115 *p = tolower(*p); 116 117 data->l4proto = xtables_parse_protocol(optarg); 118 break; 119 120 case '2': /* --vaddr */ 121 ipvs_mt_parse_addr_and_mask(optarg, &data->vaddr, 122 &data->vmask, family); 123 break; 124 125 case '3': /* --vport */ 126 data->vport = htons(xtables_parse_port(optarg, "tcp")); 127 break; 128 129 case '4': /* --vdir */ 130 xtables_param_act(XTF_NO_INVERT, "ipvs", "--vdir", invert); 131 if (strcasecmp(optarg, "ORIGINAL") == 0) { 132 data->bitmask |= XT_IPVS_DIR; 133 data->invert &= ~XT_IPVS_DIR; 134 } else if (strcasecmp(optarg, "REPLY") == 0) { 135 data->bitmask |= XT_IPVS_DIR; 136 data->invert |= XT_IPVS_DIR; 137 } else { 138 xtables_param_act(XTF_BAD_VALUE, 139 "ipvs", "--vdir", optarg); 140 } 141 break; 142 143 case '5': /* --vmethod */ 144 if (strcasecmp(optarg, "GATE") == 0) 145 data->fwd_method = IP_VS_CONN_F_DROUTE; 146 else if (strcasecmp(optarg, "IPIP") == 0) 147 data->fwd_method = IP_VS_CONN_F_TUNNEL; 148 else if (strcasecmp(optarg, "MASQ") == 0) 149 data->fwd_method = IP_VS_CONN_F_MASQ; 150 else 151 xtables_param_act(XTF_BAD_VALUE, 152 "ipvs", "--vmethod", optarg); 153 break; 154 155 case '6': /* --vportctl */ 156 data->vportctl = htons(xtables_parse_port(optarg, "tcp")); 157 break; 158 } 159 160 if (op & XT_IPVS_ONCE_MASK) { 161 if (data->invert & XT_IPVS_IPVS_PROPERTY) 162 xtables_error(PARAMETER_PROBLEM, 163 "! --ipvs cannot be together with" 164 " other options"); 165 data->bitmask |= XT_IPVS_IPVS_PROPERTY; 166 } 167 168 data->bitmask |= op; 169 if (invert) 170 data->invert |= op; 171 *flags |= op; 172 return 1; 173 174multiple_use: 175 xtables_error(PARAMETER_PROBLEM, 176 "multiple use of the same IPVS option is not allowed"); 177} 178 179static int ipvs_mt4_parse(int c, char **argv, int invert, unsigned int *flags, 180 const void *entry, struct xt_entry_match **match) 181{ 182 return ipvs_mt_parse(c, argv, invert, flags, entry, match, 183 NFPROTO_IPV4); 184} 185 186static int ipvs_mt6_parse(int c, char **argv, int invert, unsigned int *flags, 187 const void *entry, struct xt_entry_match **match) 188{ 189 return ipvs_mt_parse(c, argv, invert, flags, entry, match, 190 NFPROTO_IPV6); 191} 192 193static void ipvs_mt_check(unsigned int flags) 194{ 195 if (flags == 0) 196 xtables_error(PARAMETER_PROBLEM, 197 "IPVS: At least one option is required"); 198} 199 200/* Shamelessly copied from libxt_conntrack.c */ 201static void ipvs_mt_dump_addr(const union nf_inet_addr *addr, 202 const union nf_inet_addr *mask, 203 unsigned int family, bool numeric) 204{ 205 char buf[BUFSIZ]; 206 207 if (family == NFPROTO_IPV4) { 208 if (!numeric && addr->ip == 0) { 209 printf("anywhere "); 210 return; 211 } 212 if (numeric) 213 strcpy(buf, xtables_ipaddr_to_numeric(&addr->in)); 214 else 215 strcpy(buf, xtables_ipaddr_to_anyname(&addr->in)); 216 strcat(buf, xtables_ipmask_to_numeric(&mask->in)); 217 printf("%s ", buf); 218 } else if (family == NFPROTO_IPV6) { 219 if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 && 220 addr->ip6[2] == 0 && addr->ip6[3] == 0) { 221 printf("anywhere "); 222 return; 223 } 224 if (numeric) 225 strcpy(buf, xtables_ip6addr_to_numeric(&addr->in6)); 226 else 227 strcpy(buf, xtables_ip6addr_to_anyname(&addr->in6)); 228 strcat(buf, xtables_ip6mask_to_numeric(&mask->in6)); 229 printf("%s ", buf); 230 } 231} 232 233static void ipvs_mt_dump(const void *ip, const struct xt_ipvs_mtinfo *data, 234 unsigned int family, bool numeric, const char *prefix) 235{ 236 if (data->bitmask == XT_IPVS_IPVS_PROPERTY) { 237 if (data->invert & XT_IPVS_IPVS_PROPERTY) 238 printf("! "); 239 printf("%sipvs ", prefix); 240 } 241 242 if (data->bitmask & XT_IPVS_PROTO) { 243 if (data->invert & XT_IPVS_PROTO) 244 printf("! "); 245 printf("%sproto %u ", prefix, data->l4proto); 246 } 247 248 if (data->bitmask & XT_IPVS_VADDR) { 249 if (data->invert & XT_IPVS_VADDR) 250 printf("! "); 251 252 printf("%svaddr ", prefix); 253 ipvs_mt_dump_addr(&data->vaddr, &data->vmask, family, numeric); 254 } 255 256 if (data->bitmask & XT_IPVS_VPORT) { 257 if (data->invert & XT_IPVS_VPORT) 258 printf("! "); 259 260 printf("%svport %u ", prefix, ntohs(data->vport)); 261 } 262 263 if (data->bitmask & XT_IPVS_DIR) { 264 if (data->invert & XT_IPVS_DIR) 265 printf("%svdir REPLY ", prefix); 266 else 267 printf("%svdir ORIGINAL ", prefix); 268 } 269 270 if (data->bitmask & XT_IPVS_METHOD) { 271 if (data->invert & XT_IPVS_METHOD) 272 printf("! "); 273 274 printf("%svmethod ", prefix); 275 switch (data->fwd_method) { 276 case IP_VS_CONN_F_DROUTE: 277 printf("GATE "); 278 break; 279 case IP_VS_CONN_F_TUNNEL: 280 printf("IPIP "); 281 break; 282 case IP_VS_CONN_F_MASQ: 283 printf("MASQ "); 284 break; 285 default: 286 /* Hu? */ 287 printf("UNKNOWN "); 288 break; 289 } 290 } 291 292 if (data->bitmask & XT_IPVS_VPORTCTL) { 293 if (data->invert & XT_IPVS_VPORTCTL) 294 printf("! "); 295 296 printf("%svportctl %u ", prefix, ntohs(data->vportctl)); 297 } 298} 299 300static void ipvs_mt4_print(const void *ip, const struct xt_entry_match *match, 301 int numeric) 302{ 303 const struct xt_ipvs_mtinfo *data = (const void *)match->data; 304 ipvs_mt_dump(ip, data, NFPROTO_IPV4, numeric, ""); 305} 306 307static void ipvs_mt6_print(const void *ip, const struct xt_entry_match *match, 308 int numeric) 309{ 310 const struct xt_ipvs_mtinfo *data = (const void *)match->data; 311 ipvs_mt_dump(ip, data, NFPROTO_IPV6, numeric, ""); 312} 313 314static void ipvs_mt4_save(const void *ip, const struct xt_entry_match *match) 315{ 316 const struct xt_ipvs_mtinfo *data = (const void *)match->data; 317 ipvs_mt_dump(ip, data, NFPROTO_IPV4, true, "--"); 318} 319 320static void ipvs_mt6_save(const void *ip, const struct xt_entry_match *match) 321{ 322 const struct xt_ipvs_mtinfo *data = (const void *)match->data; 323 ipvs_mt_dump(ip, data, NFPROTO_IPV6, true, "--"); 324} 325 326static struct xtables_match ipvs_matches_reg[] = { 327 { 328 .version = XTABLES_VERSION, 329 .name = "ipvs", 330 .revision = 0, 331 .family = NFPROTO_IPV4, 332 .size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)), 333 .userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)), 334 .help = ipvs_mt_help, 335 .parse = ipvs_mt4_parse, 336 .final_check = ipvs_mt_check, 337 .print = ipvs_mt4_print, 338 .save = ipvs_mt4_save, 339 .extra_opts = ipvs_mt_opts, 340 }, 341 { 342 .version = XTABLES_VERSION, 343 .name = "ipvs", 344 .revision = 0, 345 .family = NFPROTO_IPV6, 346 .size = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)), 347 .userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)), 348 .help = ipvs_mt_help, 349 .parse = ipvs_mt6_parse, 350 .final_check = ipvs_mt_check, 351 .print = ipvs_mt6_print, 352 .save = ipvs_mt6_save, 353 .extra_opts = ipvs_mt_opts, 354 }, 355}; 356 357void _init(void) 358{ 359 xtables_register_matches(ipvs_matches_reg, 360 ARRAY_SIZE(ipvs_matches_reg)); 361} 362