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