xref: /aosp_15_r20/external/iptables/extensions/libebt_ip6.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1 /* ebt_ip6
2  *
3  * Authors:
4  * Kuo-Lang Tseng <[email protected]>
5  * Manohar Castelino <[email protected]>
6  *
7  * Summary:
8  * This is just a modification of the IPv4 code written by
9  * Bart De Schuymer <[email protected]>
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 void
parse_port_range(const char * protocol,const char * portstring,uint16_t * ports)53 parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
54 {
55 	char *buffer;
56 	char *cp;
57 
58 	buffer = xtables_strdup(portstring);
59 	if ((cp = strchr(buffer, ':')) == NULL)
60 		ports[0] = ports[1] = xtables_parse_port(buffer, NULL);
61 	else {
62 		*cp = '\0';
63 		cp++;
64 
65 		ports[0] = buffer[0] ? xtables_parse_port(buffer, NULL) : 0;
66 		ports[1] = cp[0] ? xtables_parse_port(cp, NULL) : 0xFFFF;
67 
68 		if (ports[0] > ports[1])
69 			xtables_error(PARAMETER_PROBLEM,
70 				      "invalid portrange (min > max)");
71 	}
72 	free(buffer);
73 }
74 
print_port_range(uint16_t * ports)75 static void print_port_range(uint16_t *ports)
76 {
77 	if (ports[0] == ports[1])
78 		printf("%d ", ports[0]);
79 	else
80 		printf("%d:%d ", ports[0], ports[1]);
81 }
82 
print_icmp_code(uint8_t * code)83 static void print_icmp_code(uint8_t *code)
84 {
85 	if (code[0] == code[1])
86 		printf("/%"PRIu8 " ", code[0]);
87 	else
88 		printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]);
89 }
90 
print_icmp_type(uint8_t * type,uint8_t * code)91 static void print_icmp_type(uint8_t *type, uint8_t *code)
92 {
93 	unsigned int i;
94 
95 	if (type[0] != type[1]) {
96 		printf("%"PRIu8 ":%" PRIu8, type[0], type[1]);
97 		print_icmp_code(code);
98 		return;
99 	}
100 
101 	for (i = 0; i < ARRAY_SIZE(icmpv6_codes); i++) {
102 		if (icmpv6_codes[i].type != type[0])
103 			continue;
104 
105 		if (icmpv6_codes[i].code_min == code[0] &&
106 		    icmpv6_codes[i].code_max == code[1]) {
107 			printf("%s ", icmpv6_codes[i].name);
108 			return;
109 		}
110 	}
111 	printf("%"PRIu8, type[0]);
112 	print_icmp_code(code);
113 }
114 
brip6_print_help(void)115 static void brip6_print_help(void)
116 {
117 	printf(
118 "ip6 options:\n"
119 "--ip6-src    [!] address[/mask]: ipv6 source specification\n"
120 "--ip6-dst    [!] address[/mask]: ipv6 destination specification\n"
121 "--ip6-tclass [!] tclass        : ipv6 traffic class specification\n"
122 "--ip6-proto  [!] protocol      : ipv6 protocol specification\n"
123 "--ip6-sport  [!] port[:port]   : tcp/udp source port or port range\n"
124 "--ip6-dport  [!] port[:port]   : tcp/udp destination port or port range\n"
125 "--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n");
126 	printf("Valid ICMPv6 Types:");
127 	xt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes));
128 }
129 
brip6_init(struct xt_entry_match * match)130 static void brip6_init(struct xt_entry_match *match)
131 {
132 	struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
133 
134 	ipinfo->invflags = 0;
135 	ipinfo->bitmask = 0;
136 	memset(ipinfo->saddr.s6_addr, 0, sizeof(ipinfo->saddr.s6_addr));
137 	memset(ipinfo->smsk.s6_addr, 0, sizeof(ipinfo->smsk.s6_addr));
138 	memset(ipinfo->daddr.s6_addr, 0, sizeof(ipinfo->daddr.s6_addr));
139 	memset(ipinfo->dmsk.s6_addr, 0, sizeof(ipinfo->dmsk.s6_addr));
140 }
141 
142 /* wrap xtables_ip6parse_any(), ignoring any but the first returned address */
ebt_parse_ip6_address(char * address,struct in6_addr * addr,struct in6_addr * msk)143 static void ebt_parse_ip6_address(char *address,
144 				  struct in6_addr *addr, struct in6_addr *msk)
145 {
146 	struct in6_addr *addrp;
147 	unsigned int naddrs;
148 
149 	xtables_ip6parse_any(address, &addrp, msk, &naddrs);
150 	if (naddrs != 1)
151 		xtables_error(PARAMETER_PROBLEM,
152 			      "Invalid IPv6 Address '%s' specified", address);
153 	memcpy(addr, addrp, sizeof(*addr));
154 	free(addrp);
155 }
156 
157 #define OPT_SOURCE 0x01
158 #define OPT_DEST   0x02
159 #define OPT_TCLASS 0x04
160 #define OPT_PROTO  0x08
161 #define OPT_SPORT  0x10
162 #define OPT_DPORT  0x20
163 static int
brip6_parse(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_match ** match)164 brip6_parse(int c, char **argv, int invert, unsigned int *flags,
165 	   const void *entry, struct xt_entry_match **match)
166 {
167 	struct ebt_ip6_info *info = (struct ebt_ip6_info *)(*match)->data;
168 	unsigned int i;
169 	char *end;
170 
171 	switch (c) {
172 	case IP_SOURCE:
173 		if (invert)
174 			info->invflags |= EBT_IP6_SOURCE;
175 		ebt_parse_ip6_address(optarg, &info->saddr, &info->smsk);
176 		info->bitmask |= EBT_IP6_SOURCE;
177 		break;
178 	case IP_DEST:
179 		if (invert)
180 			info->invflags |= EBT_IP6_DEST;
181 		ebt_parse_ip6_address(optarg, &info->daddr, &info->dmsk);
182 		info->bitmask |= EBT_IP6_DEST;
183 		break;
184 	case IP_SPORT:
185 		if (invert)
186 			info->invflags |= EBT_IP6_SPORT;
187 		parse_port_range(NULL, optarg, info->sport);
188 		info->bitmask |= EBT_IP6_SPORT;
189 		break;
190 	case IP_DPORT:
191 		if (invert)
192 			info->invflags |= EBT_IP6_DPORT;
193 		parse_port_range(NULL, optarg, info->dport);
194 		info->bitmask |= EBT_IP6_DPORT;
195 		break;
196 	case IP_ICMP6:
197 		if (invert)
198 			info->invflags |= EBT_IP6_ICMP6;
199 		ebt_parse_icmpv6(optarg, info->icmpv6_type, info->icmpv6_code);
200 		info->bitmask |= EBT_IP6_ICMP6;
201 		break;
202 	case IP_TCLASS:
203 		if (invert)
204 			info->invflags |= EBT_IP6_TCLASS;
205 		if (!xtables_strtoui(optarg, &end, &i, 0, 255))
206 			xtables_error(PARAMETER_PROBLEM, "Problem with specified IPv6 traffic class '%s'", optarg);
207 		info->tclass = i;
208 		info->bitmask |= EBT_IP6_TCLASS;
209 		break;
210 	case IP_PROTO:
211 		if (invert)
212 			info->invflags |= EBT_IP6_PROTO;
213 		info->protocol = xtables_parse_protocol(optarg);
214 		info->bitmask |= EBT_IP6_PROTO;
215 		break;
216 	default:
217 		return 0;
218 	}
219 
220 	*flags |= info->bitmask;
221 	return 1;
222 }
223 
brip6_final_check(unsigned int flags)224 static void brip6_final_check(unsigned int flags)
225 {
226 	if (!flags)
227 		xtables_error(PARAMETER_PROBLEM,
228 			      "You must specify proper arguments");
229 }
230 
brip6_print(const void * ip,const struct xt_entry_match * match,int numeric)231 static void brip6_print(const void *ip, const struct xt_entry_match *match,
232 		       int numeric)
233 {
234 	struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
235 
236 	if (ipinfo->bitmask & EBT_IP6_SOURCE) {
237 		printf("--ip6-src ");
238 		if (ipinfo->invflags & EBT_IP6_SOURCE)
239 			printf("! ");
240 		printf("%s", xtables_ip6addr_to_numeric(&ipinfo->saddr));
241 		printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->smsk));
242 	}
243 	if (ipinfo->bitmask & EBT_IP6_DEST) {
244 		printf("--ip6-dst ");
245 		if (ipinfo->invflags & EBT_IP6_DEST)
246 			printf("! ");
247 		printf("%s", xtables_ip6addr_to_numeric(&ipinfo->daddr));
248 		printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->dmsk));
249 	}
250 	if (ipinfo->bitmask & EBT_IP6_TCLASS) {
251 		printf("--ip6-tclass ");
252 		if (ipinfo->invflags & EBT_IP6_TCLASS)
253 			printf("! ");
254 		printf("0x%02X ", ipinfo->tclass);
255 	}
256 	if (ipinfo->bitmask & EBT_IP6_PROTO) {
257 		struct protoent *pe;
258 
259 		printf("--ip6-proto ");
260 		if (ipinfo->invflags & EBT_IP6_PROTO)
261 			printf("! ");
262 		pe = getprotobynumber(ipinfo->protocol);
263 		if (pe == NULL) {
264 			printf("%d ", ipinfo->protocol);
265 		} else {
266 			printf("%s ", pe->p_name);
267 		}
268 	}
269 	if (ipinfo->bitmask & EBT_IP6_SPORT) {
270 		printf("--ip6-sport ");
271 		if (ipinfo->invflags & EBT_IP6_SPORT)
272 			printf("! ");
273 		print_port_range(ipinfo->sport);
274 	}
275 	if (ipinfo->bitmask & EBT_IP6_DPORT) {
276 		printf("--ip6-dport ");
277 		if (ipinfo->invflags & EBT_IP6_DPORT)
278 			printf("! ");
279 		print_port_range(ipinfo->dport);
280 	}
281 	if (ipinfo->bitmask & EBT_IP6_ICMP6) {
282 		printf("--ip6-icmp-type ");
283 		if (ipinfo->invflags & EBT_IP6_ICMP6)
284 			printf("! ");
285 		print_icmp_type(ipinfo->icmpv6_type, ipinfo->icmpv6_code);
286 	}
287 }
288 
brip_xlate_th(struct xt_xlate * xl,const struct ebt_ip6_info * info,int bit,const char * pname)289 static void brip_xlate_th(struct xt_xlate *xl,
290 			  const struct ebt_ip6_info *info, int bit,
291 			  const char *pname)
292 {
293 	const uint16_t *ports;
294 
295 	if ((info->bitmask & bit) == 0)
296 		return;
297 
298 	switch (bit) {
299 	case EBT_IP6_SPORT:
300 		if (pname)
301 			xt_xlate_add(xl, "%s sport ", pname);
302 		else
303 			xt_xlate_add(xl, "@th,0,16 ");
304 
305 		ports = info->sport;
306 		break;
307 	case EBT_IP6_DPORT:
308 		if (pname)
309 			xt_xlate_add(xl, "%s dport ", pname);
310 		else
311 			xt_xlate_add(xl, "@th,16,16 ");
312 
313 		ports = info->dport;
314 		break;
315 	default:
316 		return;
317 	}
318 
319 	if (info->invflags & bit)
320 		xt_xlate_add(xl, "!= ");
321 
322 	if (ports[0] == ports[1])
323 		xt_xlate_add(xl, "%d ", ports[0]);
324 	else
325 		xt_xlate_add(xl, "%d-%d ", ports[0], ports[1]);
326 }
327 
brip_xlate_nh(struct xt_xlate * xl,const struct ebt_ip6_info * info,int bit)328 static void brip_xlate_nh(struct xt_xlate *xl,
329 			  const struct ebt_ip6_info *info, int bit)
330 {
331 	struct in6_addr *addrp, *maskp;
332 
333 	if ((info->bitmask & bit) == 0)
334 		return;
335 
336 	switch (bit) {
337 	case EBT_IP6_SOURCE:
338 		xt_xlate_add(xl, "ip6 saddr ");
339 		addrp = (struct in6_addr *)&info->saddr;
340 		maskp = (struct in6_addr *)&info->smsk;
341 		break;
342 	case EBT_IP6_DEST:
343 		xt_xlate_add(xl, "ip6 daddr ");
344 		addrp = (struct in6_addr *)&info->daddr;
345 		maskp = (struct in6_addr *)&info->dmsk;
346 		break;
347 	default:
348 		return;
349 	}
350 
351 	if (info->invflags & bit)
352 		xt_xlate_add(xl, "!= ");
353 
354 	xt_xlate_add(xl, "%s%s ", xtables_ip6addr_to_numeric(addrp),
355 				  xtables_ip6mask_to_numeric(maskp));
356 }
357 
brip6_xlate_proto_to_name(uint8_t proto)358 static const char *brip6_xlate_proto_to_name(uint8_t proto)
359 {
360 	switch (proto) {
361 	case IPPROTO_TCP:
362 		return "tcp";
363 	case IPPROTO_UDP:
364 		return "udp";
365 	case IPPROTO_UDPLITE:
366 		return "udplite";
367 	case IPPROTO_SCTP:
368 		return "sctp";
369 	case IPPROTO_DCCP:
370 		return "dccp";
371 	default:
372 		return NULL;
373 	}
374 }
375 
brip6_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)376 static int brip6_xlate(struct xt_xlate *xl,
377 		      const struct xt_xlate_mt_params *params)
378 {
379 	const struct ebt_ip6_info *info = (const void *)params->match->data;
380 	const char *pname = NULL;
381 
382 	if ((info->bitmask & (EBT_IP6_SOURCE|EBT_IP6_DEST|EBT_IP6_ICMP6|EBT_IP6_TCLASS)) == 0)
383 		xt_xlate_add(xl, "ether type ip6 ");
384 
385 	brip_xlate_nh(xl, info, EBT_IP6_SOURCE);
386 	brip_xlate_nh(xl, info, EBT_IP6_DEST);
387 
388 	if (info->bitmask & EBT_IP6_TCLASS) {
389 		xt_xlate_add(xl, "ip6 dscp ");
390 		if (info->invflags & EBT_IP6_TCLASS)
391 			xt_xlate_add(xl, "!= ");
392 		xt_xlate_add(xl, "0x%02x ", info->tclass & 0x3f); /* remove ECN bits */
393 	}
394 
395 	if (info->bitmask & EBT_IP6_PROTO) {
396 		struct protoent *pe;
397 
398 		if (info->bitmask & (EBT_IP6_SPORT|EBT_IP6_DPORT|EBT_IP6_ICMP6) &&
399 		    (info->invflags & EBT_IP6_PROTO) == 0) {
400 			/* port number given and not inverted, no need to
401 			 * add explicit 'meta l4proto'.
402 			 */
403 			pname = brip6_xlate_proto_to_name(info->protocol);
404 		} else {
405 			xt_xlate_add(xl, "meta l4proto ");
406 			if (info->invflags & EBT_IP6_PROTO)
407 				xt_xlate_add(xl, "!= ");
408 			pe = getprotobynumber(info->protocol);
409 			if (pe == NULL)
410 				xt_xlate_add(xl, "%d ", info->protocol);
411 			else
412 				xt_xlate_add(xl, "%s ", pe->p_name);
413 		}
414 	}
415 
416 	brip_xlate_th(xl, info, EBT_IP6_SPORT, pname);
417 	brip_xlate_th(xl, info, EBT_IP6_DPORT, pname);
418 
419 	if (info->bitmask & EBT_IP6_ICMP6) {
420 		xt_xlate_add(xl, "icmpv6 type ");
421 		if (info->invflags & EBT_IP6_ICMP6)
422 			xt_xlate_add(xl, "!= ");
423 
424 		if (info->icmpv6_type[0] == info->icmpv6_type[1])
425 			xt_xlate_add(xl, "%d ", info->icmpv6_type[0]);
426 		else
427 			xt_xlate_add(xl, "%d-%d ", info->icmpv6_type[0],
428 						   info->icmpv6_type[1]);
429 
430 		if (info->icmpv6_code[0] == 0 &&
431 		    info->icmpv6_code[1] == 0xff)
432 			return 1;
433 
434 		xt_xlate_add(xl, "icmpv6 code ");
435 		if (info->invflags & EBT_IP6_ICMP6)
436 			xt_xlate_add(xl, "!= ");
437 
438 		if (info->icmpv6_code[0] == info->icmpv6_code[1])
439 			xt_xlate_add(xl, "%d ", info->icmpv6_code[0]);
440 		else
441 			xt_xlate_add(xl, "%d-%d ", info->icmpv6_code[0],
442 						   info->icmpv6_code[1]);
443 	}
444 
445 	return 1;
446 }
447 
448 static struct xtables_match brip6_match = {
449 	.name		= "ip6",
450 	.revision	= 0,
451 	.version	= XTABLES_VERSION,
452 	.family		= NFPROTO_BRIDGE,
453 	.size		= XT_ALIGN(sizeof(struct ebt_ip6_info)),
454 	.userspacesize	= XT_ALIGN(sizeof(struct ebt_ip6_info)),
455 	.init		= brip6_init,
456 	.help		= brip6_print_help,
457 	.parse		= brip6_parse,
458 	.final_check	= brip6_final_check,
459 	.print		= brip6_print,
460 	.xlate		= brip6_xlate,
461 	.extra_opts	= brip6_opts,
462 };
463 
_init(void)464 void _init(void)
465 {
466 	xtables_register_match(&brip6_match);
467 }
468