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