xref: /aosp_15_r20/external/iptables/iptables/xtables-translate.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1 /*
2  * (C) 2014 by Pablo Neira Ayuso <[email protected]>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published
6  * by the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9 #include "config.h"
10 #include <time.h>
11 #include "xtables-multi.h"
12 #include "nft.h"
13 
14 #include <string.h>
15 #include <netdb.h>
16 #include <errno.h>
17 #include <stdbool.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <stdarg.h>
22 #include <limits.h>
23 #include <unistd.h>
24 #include <iptables.h>
25 #include <xtables.h>
26 #include <libiptc/libxtc.h>
27 #include <fcntl.h>
28 #include <getopt.h>
29 #include "xshared.h"
30 #include "nft-shared.h"
31 
xlate_ifname(struct xt_xlate * xl,const char * nftmeta,const char * ifname,bool invert)32 void xlate_ifname(struct xt_xlate *xl, const char *nftmeta, const char *ifname,
33 		  bool invert)
34 {
35 	int ifaclen = strlen(ifname), i, j;
36 	char iface[IFNAMSIZ * 2];
37 
38 	if (ifaclen < 1 || ifaclen >= IFNAMSIZ)
39 		return;
40 
41 	for (i = 0, j = 0; i < ifaclen + 1; i++, j++) {
42 		switch (ifname[i]) {
43 		case '*':
44 			/* asterisk is non-special mid-string */
45 			if (i == ifaclen - 1)
46 				iface[j++] = '\\';
47 			/* fall through */
48 		default:
49 			iface[j] = ifname[i];
50 			break;
51 		}
52 	}
53 
54 	if (ifaclen == 1 && ifname[0] == '+') {
55 		/* Nftables does not support wildcard only string. Workaround
56 		 * is easy, given that this will match always or never
57 		 * depending on 'invert' value. To match always, simply don't
58 		 * generate an expression. To match never, use an invalid
59 		 * interface name (kernel doesn't accept '/' in names) to match
60 		 * against. */
61 		if (!invert)
62 			return;
63 		strcpy(iface, "INVAL/D");
64 		invert = false;
65 	}
66 
67 	if (iface[j - 2] == '+')
68 		iface[j - 2] = '*';
69 
70 	xt_xlate_add(xl, "%s %s\"%s\" ", nftmeta, invert ? "!= " : "", iface);
71 }
72 
xlate_action(const struct iptables_command_state * cs,bool goto_set,struct xt_xlate * xl)73 int xlate_action(const struct iptables_command_state *cs, bool goto_set,
74 		 struct xt_xlate *xl)
75 {
76 	int ret = 1, numeric = cs->options & OPT_NUMERIC;
77 
78 	/* If no target at all, add nothing (default to continue) */
79 	if (cs->target != NULL) {
80 		/* Standard target? */
81 		if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
82 			xt_xlate_add(xl, " accept");
83 		else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
84 			xt_xlate_add(xl, " drop");
85 		else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
86 			xt_xlate_add(xl, " return");
87 		else if (cs->target->xlate) {
88 			struct xt_xlate_tg_params params = {
89 				.ip		= (const void *)&cs->fw,
90 				.target		= cs->target->t,
91 				.numeric	= numeric,
92 			};
93 			ret = cs->target->xlate(xl, &params);
94 		}
95 		else
96 			return 0;
97 	} else if (strlen(cs->jumpto) > 0) {
98 		/* Not standard, then it's a go / jump to chain */
99 		if (goto_set)
100 			xt_xlate_add(xl, " goto %s", cs->jumpto);
101 		else
102 			xt_xlate_add(xl, " jump %s", cs->jumpto);
103 	}
104 
105 	return ret;
106 }
107 
xlate_matches(const struct iptables_command_state * cs,struct xt_xlate * xl)108 int xlate_matches(const struct iptables_command_state *cs, struct xt_xlate *xl)
109 {
110 	struct xtables_rule_match *matchp;
111 	int ret = 1, numeric = cs->options & OPT_NUMERIC;
112 
113 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
114 		struct xt_xlate_mt_params params = {
115 			.ip		= (const void *)&cs->fw,
116 			.match		= matchp->match->m,
117 			.numeric	= numeric,
118 		};
119 
120 		if (!matchp->match->xlate)
121 			return 0;
122 
123 		ret = matchp->match->xlate(xl, &params);
124 		if (!ret)
125 			break;
126 	}
127 	return ret;
128 }
129 
xlate_find_match(const struct iptables_command_state * cs,const char * p_name)130 bool xlate_find_match(const struct iptables_command_state *cs, const char *p_name)
131 {
132 	struct xtables_rule_match *matchp;
133 
134 	/* Skip redundant protocol, eg. ip protocol tcp tcp dport */
135 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
136 		if (strcmp(matchp->match->name, p_name) == 0)
137 			return true;
138 	}
139 	return false;
140 }
141 
142 const char *family2str[] = {
143 	[NFPROTO_IPV4]	= "ip",
144 	[NFPROTO_IPV6]	= "ip6",
145 };
146 
nft_rule_xlate_add(struct nft_handle * h,const struct xt_cmd_parse * p,const struct iptables_command_state * cs,bool append)147 static int nft_rule_xlate_add(struct nft_handle *h,
148 			      const struct xt_cmd_parse *p,
149 			      const struct iptables_command_state *cs,
150 			      bool append)
151 {
152 	struct xt_xlate *xl = xt_xlate_alloc(10240);
153 	const char *tick = cs->restore ? "" : "'";
154 	const char *set;
155 	int ret;
156 
157 	xl_xlate_set_family(xl, h->family);
158 	ret = h->ops->xlate(cs, xl);
159 	if (!ret)
160 		goto err_out;
161 
162 	set = xt_xlate_set_get(xl);
163 	if (set[0]) {
164 		printf("%sadd set %s %s %s%s\n",
165 		       tick, family2str[h->family], p->table,
166 		       xt_xlate_set_get(xl), tick);
167 
168 		if (!cs->restore && p->command != CMD_NONE)
169 			printf("nft ");
170 	}
171 
172 	printf("%s%s rule %s %s %s ",
173 	       tick,
174 	       append ? "add" : "insert",
175 	       family2str[h->family], p->table, p->chain);
176 	if (!append && p->rulenum > 1)
177 		printf("index %d ", p->rulenum);
178 
179 	printf("%s%s\n", xt_xlate_rule_get(xl), tick);
180 
181 err_out:
182 	xt_xlate_free(xl);
183 	return ret;
184 }
185 
xlate(struct nft_handle * h,struct xt_cmd_parse * p,struct iptables_command_state * cs,struct xtables_args * args,bool append,int (* cb)(struct nft_handle * h,const struct xt_cmd_parse * p,const struct iptables_command_state * cs,bool append))186 static int xlate(struct nft_handle *h, struct xt_cmd_parse *p,
187 		 struct iptables_command_state *cs,
188 		 struct xtables_args *args, bool append,
189 		 int (*cb)(struct nft_handle *h,
190 			   const struct xt_cmd_parse *p,
191 			   const struct iptables_command_state *cs,
192 			   bool append))
193 {
194 	unsigned int i, j;
195 	int ret = 1;
196 
197 	for (i = 0; i < args->s.naddrs; i++) {
198 		switch (h->family) {
199 		case AF_INET:
200 			cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr;
201 			cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr;
202 			for (j = 0; j < args->d.naddrs; j++) {
203 				cs->fw.ip.dst.s_addr =
204 					args->d.addr.v4[j].s_addr;
205 				cs->fw.ip.dmsk.s_addr =
206 					args->d.mask.v4[j].s_addr;
207 				ret = cb(h, p, cs, append);
208 			}
209 			break;
210 		case AF_INET6:
211 			memcpy(&cs->fw6.ipv6.src,
212 			       &args->s.addr.v6[i], sizeof(struct in6_addr));
213 			memcpy(&cs->fw6.ipv6.smsk,
214 			       &args->s.mask.v6[i], sizeof(struct in6_addr));
215 			for (j = 0; j < args->d.naddrs; j++) {
216 				memcpy(&cs->fw6.ipv6.dst,
217 				       &args->d.addr.v6[j],
218 				       sizeof(struct in6_addr));
219 				memcpy(&cs->fw6.ipv6.dmsk,
220 				       &args->d.mask.v6[j],
221 				       sizeof(struct in6_addr));
222 				ret = cb(h, p, cs, append);
223 			}
224 			break;
225 		}
226 		if (!cs->restore && i < args->s.naddrs - 1)
227 			printf("nft ");
228 	}
229 
230 	return ret;
231 }
232 
print_ipt_cmd(int argc,char * argv[])233 static void print_ipt_cmd(int argc, char *argv[])
234 {
235 	int i;
236 
237 	printf("# ");
238 	for (i = 1; i < argc; i++)
239 		printf("%s ", argv[i]);
240 
241 	printf("\n");
242 }
243 
do_command_xlate(struct nft_handle * h,int argc,char * argv[],char ** table,bool restore)244 static int do_command_xlate(struct nft_handle *h, int argc, char *argv[],
245 			    char **table, bool restore)
246 {
247 	int ret = 0;
248 	struct xt_cmd_parse p = {
249 		.table		= *table,
250 		.restore	= restore,
251 		.line		= line,
252 		.xlate		= true,
253 		.ops		= &h->ops->cmd_parse,
254 	};
255 	struct iptables_command_state cs = {
256 		.jumpto = "",
257 		.argv = argv,
258 	};
259 
260 	struct xtables_args args = {
261 		.family = h->family,
262 	};
263 
264 	if (h->ops->init_cs)
265 		h->ops->init_cs(&cs);
266 
267 	do_parse(argc, argv, &p, &cs, &args);
268 
269 	cs.restore = restore;
270 
271 	if (!restore && p.command != CMD_NONE)
272 		printf("nft ");
273 
274 	switch (p.command) {
275 	case CMD_APPEND:
276 		ret = 1;
277 		if (!xlate(h, &p, &cs, &args, true, nft_rule_xlate_add))
278 			print_ipt_cmd(argc, argv);
279 		break;
280 	case CMD_DELETE:
281 		break;
282 	case CMD_DELETE_NUM:
283 		break;
284 	case CMD_CHECK:
285 		break;
286 	case CMD_REPLACE:
287 		break;
288 	case CMD_INSERT:
289 		ret = 1;
290 		if (!xlate(h, &p, &cs, &args, false, nft_rule_xlate_add))
291 			print_ipt_cmd(argc, argv);
292 		break;
293 	case CMD_FLUSH:
294 		if (p.chain) {
295 			printf("flush chain %s %s %s\n",
296 				family2str[h->family], p.table, p.chain);
297 		} else {
298 			printf("flush table %s %s\n",
299 				family2str[h->family], p.table);
300 		}
301 		ret = 1;
302 		break;
303 	case CMD_ZERO:
304 		break;
305 	case CMD_ZERO_NUM:
306 		break;
307 	case CMD_LIST:
308 	case CMD_LIST|CMD_ZERO:
309 	case CMD_LIST|CMD_ZERO_NUM:
310 		printf("list table %s %s\n",
311 		       family2str[h->family], p.table);
312 		ret = 1;
313 		break;
314 	case CMD_LIST_RULES:
315 	case CMD_LIST_RULES|CMD_ZERO:
316 	case CMD_LIST_RULES|CMD_ZERO_NUM:
317 		break;
318 	case CMD_NEW_CHAIN:
319 		printf("add chain %s %s %s\n",
320 		       family2str[h->family], p.table, p.chain);
321 		ret = 1;
322 		break;
323 	case CMD_DELETE_CHAIN:
324 		printf("delete chain %s %s %s\n",
325 		       family2str[h->family], p.table, p.chain);
326 		ret = 1;
327 		break;
328 	case CMD_RENAME_CHAIN:
329 		break;
330 	case CMD_SET_POLICY:
331 		break;
332 	case CMD_NONE:
333 		ret = 1;
334 		break;
335 	default:
336 		/* We should never reach this... */
337 		printf("Unsupported command?\n");
338 		exit(1);
339 	}
340 
341 	h->ops->clear_cs(&cs);
342 
343 	if (h->family == AF_INET) {
344 		free(args.s.addr.v4);
345 		free(args.s.mask.v4);
346 		free(args.d.addr.v4);
347 		free(args.d.mask.v4);
348 	} else if (h->family == AF_INET6) {
349 		free(args.s.addr.v6);
350 		free(args.s.mask.v6);
351 		free(args.d.addr.v6);
352 		free(args.d.mask.v6);
353 	}
354 	xtables_free_opts(1);
355 
356 	return ret;
357 }
358 
print_usage(const char * name,const char * version)359 static void print_usage(const char *name, const char *version)
360 {
361 	fprintf(stderr, "%s %s "
362 			"(c) 2014 by Pablo Neira Ayuso <[email protected]>\n"
363 			"Usage: %s [-h] [-f <FILE>] [-V]\n"
364                         "	[ --help ]\n"
365                         "	[ --file=<FILE> ]\n"
366                         "	[ --version ]\n", name, version, name);
367         exit(1);
368 }
369 
370 static const struct option options[] = {
371 	{ .name = "help",	.has_arg = false,	.val = 'h' },
372 	{ .name = "file",	.has_arg = true,	.val = 'f' },
373 	{ .name = "version",	.has_arg = false,	.val = 'V' },
374 	{ NULL },
375 };
376 
xlate_chain_user_restore(struct nft_handle * h,const char * chain,const char * table)377 static int xlate_chain_user_restore(struct nft_handle *h, const char *chain,
378 				    const char *table)
379 {
380 	printf("add chain %s %s %s\n", family2str[h->family], table, chain);
381 	return 0;
382 }
383 
commit(struct nft_handle * h)384 static int commit(struct nft_handle *h)
385 {
386 	return 1;
387 }
388 
xlate_table_new(struct nft_handle * h,const char * table)389 static void xlate_table_new(struct nft_handle *h, const char *table)
390 {
391 	printf("add table %s %s\n", family2str[h->family], table);
392 }
393 
get_hook_prio(const char * table,const char * chain)394 static int get_hook_prio(const char *table, const char *chain)
395 {
396 	int prio = 0;
397 
398 	if (strcmp("nat", table) == 0) {
399 		if (strcmp(chain, "PREROUTING") == 0)
400 			prio = NF_IP_PRI_NAT_DST;
401 		if (strcmp(chain, "INPUT") == 0)
402 			prio = NF_IP_PRI_NAT_SRC;
403 		if (strcmp(chain, "OUTPUT") == 0)
404 			prio = NF_IP_PRI_NAT_DST;
405 		if (strcmp(chain, "POSTROUTING") == 0)
406 			prio = NF_IP_PRI_NAT_SRC;
407 	} else if (strcmp("mangle", table) == 0) {
408 		prio = NF_IP_PRI_MANGLE;
409 	} else if (strcmp("raw", table) == 0) {
410 		prio = NF_IP_PRI_RAW;
411 	} else if (strcmp(chain, "security") == 0) {
412 		prio = NF_IP_PRI_SECURITY;
413 	}
414 
415 	return prio;
416 }
417 
xlate_chain_set(struct nft_handle * h,const char * table,const char * chain,const char * policy,const struct xt_counters * counters)418 static int xlate_chain_set(struct nft_handle *h, const char *table,
419 			   const char *chain, const char *policy,
420 			   const struct xt_counters *counters)
421 {
422 	const char *type = "filter";
423 	int prio;
424 
425 	if (strcmp(table, "nat") == 0)
426 		type = "nat";
427 	else if (strcmp(table, "mangle") == 0 && strcmp(chain, "OUTPUT") == 0)
428 		type = "route";
429 
430 	printf("add chain %s %s %s { type %s ",
431 	       family2str[h->family], table, chain, type);
432 	prio = get_hook_prio(table, chain);
433 	if (strcmp(chain, "PREROUTING") == 0)
434 		printf("hook prerouting priority %d; ", prio);
435 	else if (strcmp(chain, "INPUT") == 0)
436 		printf("hook input priority %d; ", prio);
437 	else if (strcmp(chain, "FORWARD") == 0)
438 		printf("hook forward priority %d; ", prio);
439 	else if (strcmp(chain, "OUTPUT") == 0)
440 		printf("hook output priority %d; ", prio);
441 	else if (strcmp(chain, "POSTROUTING") == 0)
442 		printf("hook postrouting priority %d; ", prio);
443 
444 	if (strcmp(policy, "ACCEPT") == 0)
445 		printf("policy accept; ");
446 	else if (strcmp(policy, "DROP") == 0)
447 		printf("policy drop; ");
448 
449 	printf("}\n");
450 	return 1;
451 }
452 
dummy_compat_rev(const char * name,uint8_t rev,int opt)453 static int dummy_compat_rev(const char *name, uint8_t rev, int opt)
454 {
455 	/* Avoid querying the kernel - it's not needed when just translating
456 	 * rules and not even possible when running as unprivileged user.
457 	 */
458 	return 1;
459 }
460 
461 static const struct nft_xt_restore_cb cb_xlate = {
462 	.table_new	= xlate_table_new,
463 	.chain_set	= xlate_chain_set,
464 	.chain_restore	= xlate_chain_user_restore,
465 	.do_command	= do_command_xlate,
466 	.commit		= commit,
467 	.abort		= commit,
468 };
469 
xtables_xlate_main_common(struct nft_handle * h,int family,const char * progname)470 static int xtables_xlate_main_common(struct nft_handle *h,
471 				     int family,
472 				     const char *progname)
473 {
474 	int ret;
475 
476 	xtables_globals.program_name = progname;
477 	xtables_globals.compat_rev = dummy_compat_rev;
478 	ret = xtables_init_all(&xtables_globals, family);
479 	if (ret < 0) {
480 		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
481 			xtables_globals.program_name,
482 			xtables_globals.program_version);
483 		return 1;
484 	}
485 	init_extensions();
486 	switch (family) {
487 	case NFPROTO_IPV4:
488 		init_extensions4();
489 		break;
490 	case NFPROTO_IPV6:
491 		init_extensions6();
492 		break;
493 	case NFPROTO_ARP:
494 		init_extensionsa();
495 		break;
496 	case NFPROTO_BRIDGE:
497 		init_extensionsb();
498 		break;
499 	default:
500 		fprintf(stderr, "Unknown family %d\n", family);
501 		return 1;
502 	}
503 
504 	if (nft_init(h, family) < 0) {
505 		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
506 				xtables_globals.program_name,
507 				xtables_globals.program_version,
508 				strerror(errno));
509 		return 1;
510 	}
511 
512 	return 0;
513 }
514 
xtables_xlate_main(int family,const char * progname,int argc,char * argv[])515 static int xtables_xlate_main(int family, const char *progname, int argc,
516 			      char *argv[])
517 {
518 	int ret;
519 	char *table = "filter";
520 	struct nft_handle h = {
521 		.family = family,
522 	};
523 
524 	ret = xtables_xlate_main_common(&h, family, progname);
525 	if (ret < 0)
526 		exit(EXIT_FAILURE);
527 
528 	ret = do_command_xlate(&h, argc, argv, &table, false);
529 	if (!ret)
530 		fprintf(stderr, "Translation not implemented\n");
531 
532 	nft_fini(&h);
533 	xtables_fini();
534 	exit(!ret);
535 }
536 
xtables_restore_xlate_main(int family,const char * progname,int argc,char * argv[])537 static int xtables_restore_xlate_main(int family, const char *progname,
538 				      int argc, char *argv[])
539 {
540 	int ret;
541 	struct nft_handle h = {
542 		.family = family,
543 	};
544 	const char *file = NULL;
545 	struct nft_xt_restore_parse p = {
546 		.cb = &cb_xlate,
547 	};
548 	time_t now = time(NULL);
549 	int c;
550 
551 	ret = xtables_xlate_main_common(&h, family, progname);
552 	if (ret < 0)
553 		exit(EXIT_FAILURE);
554 
555 	opterr = 0;
556 	while ((c = getopt_long(argc, argv, "hf:V", options, NULL)) != -1) {
557 		switch (c) {
558 		case 'h':
559 			print_usage(argv[0], PACKAGE_VERSION);
560 			exit(0);
561 		case 'f':
562 			file = optarg;
563 			break;
564 		case 'V':
565 			printf("%s v%s\n", argv[0], PACKAGE_VERSION);
566 			exit(0);
567 		}
568 	}
569 
570 	if (file == NULL) {
571 		fprintf(stderr, "ERROR: missing file name\n");
572 		print_usage(argv[0], PACKAGE_VERSION);
573 		exit(0);
574 	}
575 
576 	p.in = fopen(file, "r");
577 	if (p.in == NULL) {
578 		fprintf(stderr, "Cannot open file %s\n", file);
579 		exit(1);
580 	}
581 
582 	printf("# Translated by %s v%s on %s",
583 	       argv[0], PACKAGE_VERSION, ctime(&now));
584 	xtables_restore_parse(&h, &p);
585 	printf("# Completed on %s", ctime(&now));
586 
587 	nft_fini(&h);
588 	xtables_fini();
589 	fclose(p.in);
590 	exit(0);
591 }
592 
xtables_ip4_xlate_main(int argc,char * argv[])593 int xtables_ip4_xlate_main(int argc, char *argv[])
594 {
595 	return xtables_xlate_main(NFPROTO_IPV4, "iptables-translate",
596 				  argc, argv);
597 }
598 
xtables_ip6_xlate_main(int argc,char * argv[])599 int xtables_ip6_xlate_main(int argc, char *argv[])
600 {
601 	return xtables_xlate_main(NFPROTO_IPV6, "ip6tables-translate",
602 				  argc, argv);
603 }
604 
xtables_ip4_xlate_restore_main(int argc,char * argv[])605 int xtables_ip4_xlate_restore_main(int argc, char *argv[])
606 {
607 	return xtables_restore_xlate_main(NFPROTO_IPV4,
608 					  "iptables-translate-restore",
609 					  argc, argv);
610 }
611 
xtables_ip6_xlate_restore_main(int argc,char * argv[])612 int xtables_ip6_xlate_restore_main(int argc, char *argv[])
613 {
614 	return xtables_restore_xlate_main(NFPROTO_IPV6,
615 					  "ip6tables-translate-restore",
616 					  argc, argv);
617 }
618