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