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