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