1 /* ebt_ip6
2 *
3 * Authors:
4 * Kuo-Lang Tseng <kuo-lang.tseng@intel.com>
5 * Manohar Castelino <manohar.castelino@intel.com>
6 *
7 * Summary:
8 * This is just a modification of the IPv4 code written by
9 * Bart De Schuymer <bdschuym@pandora.be>
10 * with the changes required to support IPv6
11 *
12 */
13
14 #include <errno.h>
15 #include <arpa/inet.h>
16 #include <inttypes.h>
17 #include <limits.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <getopt.h>
22 #include <netdb.h>
23 #include <xtables.h>
24 #include <linux/netfilter_bridge/ebt_ip6.h>
25
26 #include "libxt_icmp.h"
27
28 #define IP_SOURCE '1'
29 #define IP_DEST '2'
30 #define IP_TCLASS '3'
31 #define IP_PROTO '4'
32 #define IP_SPORT '5'
33 #define IP_DPORT '6'
34 #define IP_ICMP6 '7'
35
36 static const struct option brip6_opts[] = {
37 { .name = "ip6-source", .has_arg = true, .val = IP_SOURCE },
38 { .name = "ip6-src", .has_arg = true, .val = IP_SOURCE },
39 { .name = "ip6-destination", .has_arg = true, .val = IP_DEST },
40 { .name = "ip6-dst", .has_arg = true, .val = IP_DEST },
41 { .name = "ip6-tclass", .has_arg = true, .val = IP_TCLASS },
42 { .name = "ip6-protocol", .has_arg = true, .val = IP_PROTO },
43 { .name = "ip6-proto", .has_arg = true, .val = IP_PROTO },
44 { .name = "ip6-source-port", .has_arg = true, .val = IP_SPORT },
45 { .name = "ip6-sport", .has_arg = true, .val = IP_SPORT },
46 { .name = "ip6-destination-port",.has_arg = true,.val = IP_DPORT },
47 { .name = "ip6-dport", .has_arg = true, .val = IP_DPORT },
48 { .name = "ip6-icmp-type", .has_arg = true, .val = IP_ICMP6 },
49 XT_GETOPT_TABLEEND,
50 };
51
52 static const struct xt_icmp_names icmpv6_codes[] = {
53 { "destination-unreachable", 1, 0, 0xFF },
54 { "no-route", 1, 0, 0 },
55 { "communication-prohibited", 1, 1, 1 },
56 { "address-unreachable", 1, 3, 3 },
57 { "port-unreachable", 1, 4, 4 },
58
59 { "packet-too-big", 2, 0, 0xFF },
60
61 { "time-exceeded", 3, 0, 0xFF },
62 /* Alias */ { "ttl-exceeded", 3, 0, 0xFF },
63 { "ttl-zero-during-transit", 3, 0, 0 },
64 { "ttl-zero-during-reassembly", 3, 1, 1 },
65
66 { "parameter-problem", 4, 0, 0xFF },
67 { "bad-header", 4, 0, 0 },
68 { "unknown-header-type", 4, 1, 1 },
69 { "unknown-option", 4, 2, 2 },
70
71 { "echo-request", 128, 0, 0xFF },
72 /* Alias */ { "ping", 128, 0, 0xFF },
73
74 { "echo-reply", 129, 0, 0xFF },
75 /* Alias */ { "pong", 129, 0, 0xFF },
76
77 { "router-solicitation", 133, 0, 0xFF },
78
79 { "router-advertisement", 134, 0, 0xFF },
80
81 { "neighbour-solicitation", 135, 0, 0xFF },
82 /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF },
83
84 { "neighbour-advertisement", 136, 0, 0xFF },
85 /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF },
86
87 { "redirect", 137, 0, 0xFF },
88 };
89
90 static void
parse_port_range(const char * protocol,const char * portstring,uint16_t * ports)91 parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
92 {
93 char *buffer;
94 char *cp;
95
96 buffer = strdup(portstring);
97 if ((cp = strchr(buffer, ':')) == NULL)
98 ports[0] = ports[1] = xtables_parse_port(buffer, NULL);
99 else {
100 *cp = '\0';
101 cp++;
102
103 ports[0] = buffer[0] ? xtables_parse_port(buffer, NULL) : 0;
104 ports[1] = cp[0] ? xtables_parse_port(cp, NULL) : 0xFFFF;
105
106 if (ports[0] > ports[1])
107 xtables_error(PARAMETER_PROBLEM,
108 "invalid portrange (min > max)");
109 }
110 free(buffer);
111 }
112
parse_range(const char * str,unsigned int res[])113 static char *parse_range(const char *str, unsigned int res[])
114 {
115 char *next;
116
117 if (!xtables_strtoui(str, &next, &res[0], 0, 255))
118 return NULL;
119
120 res[1] = res[0];
121 if (*next == ':') {
122 str = next + 1;
123 if (!xtables_strtoui(str, &next, &res[1], 0, 255))
124 return NULL;
125 }
126
127 return next;
128 }
129
130 static int
parse_icmpv6(const char * icmpv6type,uint8_t type[],uint8_t code[])131 parse_icmpv6(const char *icmpv6type, uint8_t type[], uint8_t code[])
132 {
133 static const unsigned int limit = ARRAY_SIZE(icmpv6_codes);
134 unsigned int match = limit;
135 unsigned int i, number[2];
136
137 for (i = 0; i < limit; i++) {
138 if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type)))
139 continue;
140 if (match != limit)
141 xtables_error(PARAMETER_PROBLEM, "Ambiguous ICMPv6 type `%s':"
142 " `%s' or `%s'?",
143 icmpv6type, icmpv6_codes[match].name,
144 icmpv6_codes[i].name);
145 match = i;
146 }
147
148 if (match < limit) {
149 type[0] = type[1] = icmpv6_codes[match].type;
150 code[0] = icmpv6_codes[match].code_min;
151 code[1] = icmpv6_codes[match].code_max;
152 } else {
153 char *next = parse_range(icmpv6type, number);
154 if (!next) {
155 xtables_error(PARAMETER_PROBLEM, "Unknown ICMPv6 type `%s'",
156 icmpv6type);
157 return -1;
158 }
159 type[0] = (uint8_t) number[0];
160 type[1] = (uint8_t) number[1];
161 switch (*next) {
162 case 0:
163 code[0] = 0;
164 code[1] = 255;
165 return 0;
166 case '/':
167 next = parse_range(next+1, number);
168 code[0] = (uint8_t) number[0];
169 code[1] = (uint8_t) number[1];
170 if (next == NULL)
171 return -1;
172 if (next && *next == 0)
173 return 0;
174 /* fallthrough */
175 default:
176 xtables_error(PARAMETER_PROBLEM, "unknown character %c", *next);
177 return -1;
178 }
179 }
180 return 0;
181 }
182
print_port_range(uint16_t * ports)183 static void print_port_range(uint16_t *ports)
184 {
185 if (ports[0] == ports[1])
186 printf("%d ", ports[0]);
187 else
188 printf("%d:%d ", ports[0], ports[1]);
189 }
190
print_icmp_code(uint8_t * code)191 static void print_icmp_code(uint8_t *code)
192 {
193 if (code[0] == code[1])
194 printf("/%"PRIu8 " ", code[0]);
195 else
196 printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]);
197 }
198
print_icmp_type(uint8_t * type,uint8_t * code)199 static void print_icmp_type(uint8_t *type, uint8_t *code)
200 {
201 unsigned int i;
202
203 if (type[0] != type[1]) {
204 printf("%"PRIu8 ":%" PRIu8, type[0], type[1]);
205 print_icmp_code(code);
206 return;
207 }
208
209 for (i = 0; i < ARRAY_SIZE(icmpv6_codes); i++) {
210 if (icmpv6_codes[i].type != type[0])
211 continue;
212
213 if (icmpv6_codes[i].code_min == code[0] &&
214 icmpv6_codes[i].code_max == code[1]) {
215 printf("%s ", icmpv6_codes[i].name);
216 return;
217 }
218 }
219 printf("%"PRIu8, type[0]);
220 print_icmp_code(code);
221 }
222
brip6_print_help(void)223 static void brip6_print_help(void)
224 {
225 printf(
226 "ip6 options:\n"
227 "--ip6-src [!] address[/mask]: ipv6 source specification\n"
228 "--ip6-dst [!] address[/mask]: ipv6 destination specification\n"
229 "--ip6-tclass [!] tclass : ipv6 traffic class specification\n"
230 "--ip6-proto [!] protocol : ipv6 protocol specification\n"
231 "--ip6-sport [!] port[:port] : tcp/udp source port or port range\n"
232 "--ip6-dport [!] port[:port] : tcp/udp destination port or port range\n"
233 "--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n");
234 printf("Valid ICMPv6 Types:");
235 xt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes));
236 }
237
brip6_init(struct xt_entry_match * match)238 static void brip6_init(struct xt_entry_match *match)
239 {
240 struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
241
242 ipinfo->invflags = 0;
243 ipinfo->bitmask = 0;
244 memset(ipinfo->saddr.s6_addr, 0, sizeof(ipinfo->saddr.s6_addr));
245 memset(ipinfo->smsk.s6_addr, 0, sizeof(ipinfo->smsk.s6_addr));
246 memset(ipinfo->daddr.s6_addr, 0, sizeof(ipinfo->daddr.s6_addr));
247 memset(ipinfo->dmsk.s6_addr, 0, sizeof(ipinfo->dmsk.s6_addr));
248 }
249
numeric_to_addr(const char * num)250 static struct in6_addr *numeric_to_addr(const char *num)
251 {
252 static struct in6_addr ap;
253 int err;
254
255 if ((err=inet_pton(AF_INET6, num, &ap)) == 1)
256 return ≈
257 return (struct in6_addr *)NULL;
258 }
259
parse_ip6_mask(char * mask)260 static struct in6_addr *parse_ip6_mask(char *mask)
261 {
262 static struct in6_addr maskaddr;
263 struct in6_addr *addrp;
264 unsigned int bits;
265
266 if (mask == NULL) {
267 /* no mask at all defaults to 128 bits */
268 memset(&maskaddr, 0xff, sizeof maskaddr);
269 return &maskaddr;
270 }
271 if ((addrp = numeric_to_addr(mask)) != NULL)
272 return addrp;
273 if (!xtables_strtoui(mask, NULL, &bits, 0, 128))
274 xtables_error(PARAMETER_PROBLEM, "Invalid IPv6 Mask '%s' specified", mask);
275 if (bits != 0) {
276 char *p = (char *)&maskaddr;
277 memset(p, 0xff, bits / 8);
278 memset(p + (bits / 8) + 1, 0, (128 - bits) / 8);
279 p[bits / 8] = 0xff << (8 - (bits & 7));
280 return &maskaddr;
281 }
282
283 memset(&maskaddr, 0, sizeof maskaddr);
284 return &maskaddr;
285 }
286
287 /* Set the ipv6 mask and address. Callers should check ebt_errormsg[0].
288 * The string pointed to by address can be altered. */
ebt_parse_ip6_address(char * address,struct in6_addr * addr,struct in6_addr * msk)289 static void ebt_parse_ip6_address(char *address, struct in6_addr *addr, struct in6_addr *msk)
290 {
291 struct in6_addr *tmp_addr;
292 char buf[256];
293 char *p;
294 int i;
295 int err;
296
297 strncpy(buf, address, sizeof(buf) - 1);
298 /* first the mask */
299 buf[sizeof(buf) - 1] = '\0';
300 if ((p = strrchr(buf, '/')) != NULL) {
301 *p = '\0';
302 tmp_addr = parse_ip6_mask(p + 1);
303 } else
304 tmp_addr = parse_ip6_mask(NULL);
305
306 *msk = *tmp_addr;
307
308 /* if a null mask is given, the name is ignored, like in "any/0" */
309 if (!memcmp(msk, &in6addr_any, sizeof(in6addr_any)))
310 strcpy(buf, "::");
311
312 if ((err=inet_pton(AF_INET6, buf, addr)) < 1) {
313 xtables_error(PARAMETER_PROBLEM, "Invalid IPv6 Address '%s' specified", buf);
314 return;
315 }
316
317 for (i = 0; i < 4; i++)
318 addr->s6_addr32[i] &= msk->s6_addr32[i];
319 }
320
321 #define OPT_SOURCE 0x01
322 #define OPT_DEST 0x02
323 #define OPT_TCLASS 0x04
324 #define OPT_PROTO 0x08
325 #define OPT_SPORT 0x10
326 #define OPT_DPORT 0x20
327 static int
brip6_parse(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_match ** match)328 brip6_parse(int c, char **argv, int invert, unsigned int *flags,
329 const void *entry, struct xt_entry_match **match)
330 {
331 struct ebt_ip6_info *info = (struct ebt_ip6_info *)(*match)->data;
332 unsigned int i;
333 char *end;
334
335 switch (c) {
336 case IP_SOURCE:
337 if (invert)
338 info->invflags |= EBT_IP6_SOURCE;
339 ebt_parse_ip6_address(optarg, &info->saddr, &info->smsk);
340 info->bitmask |= EBT_IP6_SOURCE;
341 break;
342 case IP_DEST:
343 if (invert)
344 info->invflags |= EBT_IP6_DEST;
345 ebt_parse_ip6_address(optarg, &info->daddr, &info->dmsk);
346 info->bitmask |= EBT_IP6_DEST;
347 break;
348 case IP_SPORT:
349 if (invert)
350 info->invflags |= EBT_IP6_SPORT;
351 parse_port_range(NULL, optarg, info->sport);
352 info->bitmask |= EBT_IP6_SPORT;
353 break;
354 case IP_DPORT:
355 if (invert)
356 info->invflags |= EBT_IP6_DPORT;
357 parse_port_range(NULL, optarg, info->dport);
358 info->bitmask |= EBT_IP6_DPORT;
359 break;
360 case IP_ICMP6:
361 if (invert)
362 info->invflags |= EBT_IP6_ICMP6;
363 if (parse_icmpv6(optarg, info->icmpv6_type, info->icmpv6_code))
364 return 0;
365 info->bitmask |= EBT_IP6_ICMP6;
366 break;
367 case IP_TCLASS:
368 if (invert)
369 info->invflags |= EBT_IP6_TCLASS;
370 if (!xtables_strtoui(optarg, &end, &i, 0, 255))
371 xtables_error(PARAMETER_PROBLEM, "Problem with specified IPv6 traffic class '%s'", optarg);
372 info->tclass = i;
373 info->bitmask |= EBT_IP6_TCLASS;
374 break;
375 case IP_PROTO:
376 if (invert)
377 info->invflags |= EBT_IP6_PROTO;
378 info->protocol = xtables_parse_protocol(optarg);
379 info->bitmask |= EBT_IP6_PROTO;
380 break;
381 default:
382 return 0;
383 }
384
385 *flags |= info->bitmask;
386 return 1;
387 }
388
brip6_final_check(unsigned int flags)389 static void brip6_final_check(unsigned int flags)
390 {
391 if (!flags)
392 xtables_error(PARAMETER_PROBLEM,
393 "You must specify proper arguments");
394 }
395
brip6_print(const void * ip,const struct xt_entry_match * match,int numeric)396 static void brip6_print(const void *ip, const struct xt_entry_match *match,
397 int numeric)
398 {
399 struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
400
401 if (ipinfo->bitmask & EBT_IP6_SOURCE) {
402 printf("--ip6-src ");
403 if (ipinfo->invflags & EBT_IP6_SOURCE)
404 printf("! ");
405 printf("%s", xtables_ip6addr_to_numeric(&ipinfo->saddr));
406 printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->smsk));
407 }
408 if (ipinfo->bitmask & EBT_IP6_DEST) {
409 printf("--ip6-dst ");
410 if (ipinfo->invflags & EBT_IP6_DEST)
411 printf("! ");
412 printf("%s", xtables_ip6addr_to_numeric(&ipinfo->daddr));
413 printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->dmsk));
414 }
415 if (ipinfo->bitmask & EBT_IP6_TCLASS) {
416 printf("--ip6-tclass ");
417 if (ipinfo->invflags & EBT_IP6_TCLASS)
418 printf("! ");
419 printf("0x%02X ", ipinfo->tclass);
420 }
421 if (ipinfo->bitmask & EBT_IP6_PROTO) {
422 struct protoent *pe;
423
424 printf("--ip6-proto ");
425 if (ipinfo->invflags & EBT_IP6_PROTO)
426 printf("! ");
427 pe = getprotobynumber(ipinfo->protocol);
428 if (pe == NULL) {
429 printf("%d ", ipinfo->protocol);
430 } else {
431 printf("%s ", pe->p_name);
432 }
433 }
434 if (ipinfo->bitmask & EBT_IP6_SPORT) {
435 printf("--ip6-sport ");
436 if (ipinfo->invflags & EBT_IP6_SPORT)
437 printf("! ");
438 print_port_range(ipinfo->sport);
439 }
440 if (ipinfo->bitmask & EBT_IP6_DPORT) {
441 printf("--ip6-dport ");
442 if (ipinfo->invflags & EBT_IP6_DPORT)
443 printf("! ");
444 print_port_range(ipinfo->dport);
445 }
446 if (ipinfo->bitmask & EBT_IP6_ICMP6) {
447 printf("--ip6-icmp-type ");
448 if (ipinfo->invflags & EBT_IP6_ICMP6)
449 printf("! ");
450 print_icmp_type(ipinfo->icmpv6_type, ipinfo->icmpv6_code);
451 }
452 }
453
brip_xlate_th(struct xt_xlate * xl,const struct ebt_ip6_info * info,int bit,const char * pname)454 static void brip_xlate_th(struct xt_xlate *xl,
455 const struct ebt_ip6_info *info, int bit,
456 const char *pname)
457 {
458 const uint16_t *ports;
459
460 if ((info->bitmask & bit) == 0)
461 return;
462
463 switch (bit) {
464 case EBT_IP6_SPORT:
465 if (pname)
466 xt_xlate_add(xl, "%s sport ", pname);
467 else
468 xt_xlate_add(xl, "@th,0,16 ");
469
470 ports = info->sport;
471 break;
472 case EBT_IP6_DPORT:
473 if (pname)
474 xt_xlate_add(xl, "%s dport ", pname);
475 else
476 xt_xlate_add(xl, "@th,16,16 ");
477
478 ports = info->dport;
479 break;
480 default:
481 return;
482 }
483
484 if (info->invflags & bit)
485 xt_xlate_add(xl, "!= ");
486
487 if (ports[0] == ports[1])
488 xt_xlate_add(xl, "%d ", ports[0]);
489 else
490 xt_xlate_add(xl, "%d-%d ", ports[0], ports[1]);
491 }
492
brip_xlate_nh(struct xt_xlate * xl,const struct ebt_ip6_info * info,int bit)493 static void brip_xlate_nh(struct xt_xlate *xl,
494 const struct ebt_ip6_info *info, int bit)
495 {
496 struct in6_addr *addrp, *maskp;
497
498 if ((info->bitmask & bit) == 0)
499 return;
500
501 switch (bit) {
502 case EBT_IP6_SOURCE:
503 xt_xlate_add(xl, "ip6 saddr ");
504 addrp = (struct in6_addr *)&info->saddr;
505 maskp = (struct in6_addr *)&info->smsk;
506 break;
507 case EBT_IP6_DEST:
508 xt_xlate_add(xl, "ip6 daddr ");
509 addrp = (struct in6_addr *)&info->daddr;
510 maskp = (struct in6_addr *)&info->dmsk;
511 break;
512 default:
513 return;
514 }
515
516 if (info->invflags & bit)
517 xt_xlate_add(xl, "!= ");
518
519 xt_xlate_add(xl, "%s%s ", xtables_ip6addr_to_numeric(addrp),
520 xtables_ip6mask_to_numeric(maskp));
521 }
522
brip6_xlate_proto_to_name(uint8_t proto)523 static const char *brip6_xlate_proto_to_name(uint8_t proto)
524 {
525 switch (proto) {
526 case IPPROTO_TCP:
527 return "tcp";
528 case IPPROTO_UDP:
529 return "udp";
530 case IPPROTO_UDPLITE:
531 return "udplite";
532 case IPPROTO_SCTP:
533 return "sctp";
534 case IPPROTO_DCCP:
535 return "dccp";
536 default:
537 return NULL;
538 }
539 }
540
brip6_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)541 static int brip6_xlate(struct xt_xlate *xl,
542 const struct xt_xlate_mt_params *params)
543 {
544 const struct ebt_ip6_info *info = (const void *)params->match->data;
545 const char *pname = NULL;
546
547 if ((info->bitmask & (EBT_IP6_SOURCE|EBT_IP6_DEST|EBT_IP6_ICMP6|EBT_IP6_TCLASS)) == 0)
548 xt_xlate_add(xl, "ether type ip6 ");
549
550 brip_xlate_nh(xl, info, EBT_IP6_SOURCE);
551 brip_xlate_nh(xl, info, EBT_IP6_DEST);
552
553 if (info->bitmask & EBT_IP6_TCLASS) {
554 xt_xlate_add(xl, "ip6 dscp ");
555 if (info->invflags & EBT_IP6_TCLASS)
556 xt_xlate_add(xl, "!= ");
557 xt_xlate_add(xl, "0x%02x ", info->tclass & 0x3f); /* remove ECN bits */
558 }
559
560 if (info->bitmask & EBT_IP6_PROTO) {
561 struct protoent *pe;
562
563 if (info->bitmask & (EBT_IP6_SPORT|EBT_IP6_DPORT|EBT_IP6_ICMP6) &&
564 (info->invflags & EBT_IP6_PROTO) == 0) {
565 /* port number given and not inverted, no need to
566 * add explicit 'meta l4proto'.
567 */
568 pname = brip6_xlate_proto_to_name(info->protocol);
569 } else {
570 xt_xlate_add(xl, "meta l4proto ");
571 if (info->invflags & EBT_IP6_PROTO)
572 xt_xlate_add(xl, "!= ");
573 pe = getprotobynumber(info->protocol);
574 if (pe == NULL)
575 xt_xlate_add(xl, "%d ", info->protocol);
576 else
577 xt_xlate_add(xl, "%s ", pe->p_name);
578 }
579 }
580
581 brip_xlate_th(xl, info, EBT_IP6_SPORT, pname);
582 brip_xlate_th(xl, info, EBT_IP6_DPORT, pname);
583
584 if (info->bitmask & EBT_IP6_ICMP6) {
585 xt_xlate_add(xl, "icmpv6 type ");
586 if (info->invflags & EBT_IP6_ICMP6)
587 xt_xlate_add(xl, "!= ");
588
589 if (info->icmpv6_type[0] == info->icmpv6_type[1])
590 xt_xlate_add(xl, "%d ", info->icmpv6_type[0]);
591 else
592 xt_xlate_add(xl, "%d-%d ", info->icmpv6_type[0],
593 info->icmpv6_type[1]);
594
595 if (info->icmpv6_code[0] == 0 &&
596 info->icmpv6_code[1] == 0xff)
597 return 1;
598
599 xt_xlate_add(xl, "icmpv6 code ");
600 if (info->invflags & EBT_IP6_ICMP6)
601 xt_xlate_add(xl, "!= ");
602
603 if (info->icmpv6_code[0] == info->icmpv6_code[1])
604 xt_xlate_add(xl, "%d ", info->icmpv6_code[0]);
605 else
606 xt_xlate_add(xl, "%d-%d ", info->icmpv6_code[0],
607 info->icmpv6_code[1]);
608 }
609
610 return 1;
611 }
612
613 static struct xtables_match brip6_match = {
614 .name = "ip6",
615 .revision = 0,
616 .version = XTABLES_VERSION,
617 .family = NFPROTO_BRIDGE,
618 .size = XT_ALIGN(sizeof(struct ebt_ip6_info)),
619 .userspacesize = XT_ALIGN(sizeof(struct ebt_ip6_info)),
620 .init = brip6_init,
621 .help = brip6_print_help,
622 .parse = brip6_parse,
623 .final_check = brip6_final_check,
624 .print = brip6_print,
625 .xlate = brip6_xlate,
626 .extra_opts = brip6_opts,
627 };
628
_init(void)629 void _init(void)
630 {
631 xtables_register_match(&brip6_match);
632 }
633