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 &ap;
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