• 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 			/* 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 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
135 		if (strcmp(matchp->match->name, p_name) == 0)
136 			return true;
137 	}
138 	return false;
139 }
140 
xlate_find_protomatch(const struct iptables_command_state * cs,uint16_t proto)141 bool xlate_find_protomatch(const struct iptables_command_state *cs,
142 			   uint16_t proto)
143 {
144 	struct protoent *pent;
145 	int i;
146 
147 	/* Skip redundant protocol, eg. ip protocol tcp tcp dport */
148 	for (i = 0; xtables_chain_protos[i].name != NULL; i++) {
149 		if (xtables_chain_protos[i].num == proto &&
150 		    xlate_find_match(cs, xtables_chain_protos[i].name))
151 			return true;
152 	}
153 	pent = getprotobynumber(proto);
154 	return pent && xlate_find_match(cs, pent->p_name);
155 }
156 
157 const char *family2str[] = {
158 	[NFPROTO_ARP]	= "arp",
159 	[NFPROTO_IPV4]	= "ip",
160 	[NFPROTO_IPV6]	= "ip6",
161 };
162 
nft_rule_xlate_add(struct nft_handle * h,const struct xt_cmd_parse * p,const struct iptables_command_state * cs,bool append)163 static int nft_rule_xlate_add(struct nft_handle *h,
164 			      const struct xt_cmd_parse *p,
165 			      const struct iptables_command_state *cs,
166 			      bool append)
167 {
168 	struct xt_xlate *xl = xt_xlate_alloc(10240);
169 	const char *tick = cs->restore ? "" : "'";
170 	const char *set;
171 	int ret;
172 
173 	xl_xlate_set_family(xl, h->family);
174 	ret = h->ops->xlate(cs, xl);
175 	if (!ret)
176 		goto err_out;
177 
178 	set = xt_xlate_set_get(xl);
179 	if (set[0]) {
180 		printf("%sadd set %s %s %s%s\n",
181 		       tick, family2str[h->family], p->table,
182 		       xt_xlate_set_get(xl), tick);
183 
184 		if (!cs->restore && p->command != CMD_NONE)
185 			printf("nft ");
186 	}
187 
188 	printf("%s%s rule %s %s %s ",
189 	       tick,
190 	       append ? "add" : "insert",
191 	       family2str[h->family], p->table, p->chain);
192 	if (!append && p->rulenum > 1)
193 		printf("index %d ", p->rulenum);
194 
195 	printf("%s%s\n", xt_xlate_rule_get(xl), tick);
196 
197 err_out:
198 	xt_xlate_free(xl);
199 	return ret;
200 }
201 
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))202 static int xlate(struct nft_handle *h, struct xt_cmd_parse *p,
203 		 struct iptables_command_state *cs,
204 		 struct xtables_args *args, bool append,
205 		 int (*cb)(struct nft_handle *h,
206 			   const struct xt_cmd_parse *p,
207 			   const struct iptables_command_state *cs,
208 			   bool append))
209 {
210 	unsigned int i, j;
211 	int ret = 1;
212 
213 	for (i = 0; i < args->s.naddrs; i++) {
214 		switch (h->family) {
215 		case NFPROTO_ARP:
216 			cs->arp.arp.src.s_addr = args->s.addr.v4[i].s_addr;
217 			cs->arp.arp.smsk.s_addr = args->s.mask.v4[i].s_addr;
218 			for (j = 0; j < args->d.naddrs; j++) {
219 				cs->arp.arp.tgt.s_addr = args->d.addr.v4[j].s_addr;
220 				cs->arp.arp.tmsk.s_addr = args->d.mask.v4[j].s_addr;
221 				ret = cb(h, p, cs, append);
222 			}
223 			break;
224 		case AF_INET:
225 			cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr;
226 			cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr;
227 			for (j = 0; j < args->d.naddrs; j++) {
228 				cs->fw.ip.dst.s_addr =
229 					args->d.addr.v4[j].s_addr;
230 				cs->fw.ip.dmsk.s_addr =
231 					args->d.mask.v4[j].s_addr;
232 				ret = cb(h, p, cs, append);
233 			}
234 			break;
235 		case AF_INET6:
236 			memcpy(&cs->fw6.ipv6.src,
237 			       &args->s.addr.v6[i], sizeof(struct in6_addr));
238 			memcpy(&cs->fw6.ipv6.smsk,
239 			       &args->s.mask.v6[i], sizeof(struct in6_addr));
240 			for (j = 0; j < args->d.naddrs; j++) {
241 				memcpy(&cs->fw6.ipv6.dst,
242 				       &args->d.addr.v6[j],
243 				       sizeof(struct in6_addr));
244 				memcpy(&cs->fw6.ipv6.dmsk,
245 				       &args->d.mask.v6[j],
246 				       sizeof(struct in6_addr));
247 				ret = cb(h, p, cs, append);
248 			}
249 			break;
250 		}
251 		if (!cs->restore && i < args->s.naddrs - 1)
252 			printf("nft ");
253 	}
254 
255 	return ret;
256 }
257 
print_ipt_cmd(int argc,char * argv[])258 static void print_ipt_cmd(int argc, char *argv[])
259 {
260 	int i;
261 
262 	printf("# ");
263 	for (i = 1; i < argc; i++)
264 		printf("%s ", argv[i]);
265 
266 	printf("\n");
267 }
268 
do_command_xlate(struct nft_handle * h,int argc,char * argv[],char ** table,bool restore)269 static int do_command_xlate(struct nft_handle *h, int argc, char *argv[],
270 			    char **table, bool restore)
271 {
272 	int ret = 0;
273 	struct xt_cmd_parse p = {
274 		.table		= *table,
275 		.restore	= restore,
276 		.line		= line,
277 		.ops		= &h->ops->cmd_parse,
278 	};
279 	struct iptables_command_state cs = {
280 		.jumpto = "",
281 		.argv = argv,
282 	};
283 
284 	struct xtables_args args = {
285 		.family = h->family,
286 	};
287 
288 	if (h->ops->init_cs)
289 		h->ops->init_cs(&cs);
290 
291 	do_parse(argc, argv, &p, &cs, &args);
292 
293 	cs.restore = restore;
294 
295 	if (!restore && p.command != CMD_NONE)
296 		printf("nft ");
297 
298 	switch (p.command) {
299 	case CMD_APPEND:
300 		ret = 1;
301 		if (!xlate(h, &p, &cs, &args, true, nft_rule_xlate_add))
302 			print_ipt_cmd(argc, argv);
303 		break;
304 	case CMD_DELETE:
305 		break;
306 	case CMD_DELETE_NUM:
307 		break;
308 	case CMD_CHECK:
309 		break;
310 	case CMD_REPLACE:
311 		break;
312 	case CMD_INSERT:
313 		ret = 1;
314 		if (!xlate(h, &p, &cs, &args, false, nft_rule_xlate_add))
315 			print_ipt_cmd(argc, argv);
316 		break;
317 	case CMD_FLUSH:
318 		if (p.chain) {
319 			printf("flush chain %s %s %s\n",
320 				family2str[h->family], p.table, p.chain);
321 		} else {
322 			printf("flush table %s %s\n",
323 				family2str[h->family], p.table);
324 		}
325 		ret = 1;
326 		break;
327 	case CMD_ZERO:
328 		break;
329 	case CMD_ZERO_NUM:
330 		break;
331 	case CMD_LIST:
332 	case CMD_LIST|CMD_ZERO:
333 	case CMD_LIST|CMD_ZERO_NUM:
334 		printf("list table %s %s\n",
335 		       family2str[h->family], p.table);
336 		ret = 1;
337 		break;
338 	case CMD_LIST_RULES:
339 	case CMD_LIST_RULES|CMD_ZERO:
340 	case CMD_LIST_RULES|CMD_ZERO_NUM:
341 		break;
342 	case CMD_NEW_CHAIN:
343 		printf("add chain %s %s %s\n",
344 		       family2str[h->family], p.table, p.chain);
345 		ret = 1;
346 		break;
347 	case CMD_DELETE_CHAIN:
348 		printf("delete chain %s %s %s\n",
349 		       family2str[h->family], p.table, p.chain);
350 		ret = 1;
351 		break;
352 	case CMD_RENAME_CHAIN:
353 		break;
354 	case CMD_SET_POLICY:
355 		break;
356 	case CMD_NONE:
357 		ret = 1;
358 		break;
359 	default:
360 		/* We should never reach this... */
361 		printf("Unsupported command?\n");
362 		exit(1);
363 	}
364 
365 	h->ops->clear_cs(&cs);
366 
367 	xtables_clear_args(&args);
368 	xtables_free_opts(1);
369 
370 	return ret;
371 }
372 
print_usage(const char * name,const char * version)373 static void print_usage(const char *name, const char *version)
374 {
375 	fprintf(stderr, "%s %s "
376 			"(c) 2014 by Pablo Neira Ayuso <pablo@netfilter.org>\n"
377 			"Usage: %s [-h] [-f <FILE>] [-V]\n"
378                         "	[ --help ]\n"
379                         "	[ --file=<FILE> ]\n"
380                         "	[ --version ]\n", name, version, name);
381         exit(1);
382 }
383 
384 static const struct option options[] = {
385 	{ .name = "help",	.has_arg = false,	.val = 'h' },
386 	{ .name = "file",	.has_arg = true,	.val = 'f' },
387 	{ .name = "version",	.has_arg = false,	.val = 'V' },
388 	{ NULL },
389 };
390 
xlate_chain_user_restore(struct nft_handle * h,const char * chain,const char * table)391 static int xlate_chain_user_restore(struct nft_handle *h, const char *chain,
392 				    const char *table)
393 {
394 	printf("add chain %s %s %s\n", family2str[h->family], table, chain);
395 	return 0;
396 }
397 
commit(struct nft_handle * h)398 static int commit(struct nft_handle *h)
399 {
400 	return 1;
401 }
402 
xlate_table_new(struct nft_handle * h,const char * table)403 static void xlate_table_new(struct nft_handle *h, const char *table)
404 {
405 	printf("add table %s %s\n", family2str[h->family], table);
406 }
407 
get_hook_prio(const char * table,const char * chain)408 static int get_hook_prio(const char *table, const char *chain)
409 {
410 	int prio = 0;
411 
412 	if (strcmp("nat", table) == 0) {
413 		if (strcmp(chain, "PREROUTING") == 0)
414 			prio = NF_IP_PRI_NAT_DST;
415 		if (strcmp(chain, "INPUT") == 0)
416 			prio = NF_IP_PRI_NAT_SRC;
417 		if (strcmp(chain, "OUTPUT") == 0)
418 			prio = NF_IP_PRI_NAT_DST;
419 		if (strcmp(chain, "POSTROUTING") == 0)
420 			prio = NF_IP_PRI_NAT_SRC;
421 	} else if (strcmp("mangle", table) == 0) {
422 		prio = NF_IP_PRI_MANGLE;
423 	} else if (strcmp("raw", table) == 0) {
424 		prio = NF_IP_PRI_RAW;
425 	} else if (strcmp(chain, "security") == 0) {
426 		prio = NF_IP_PRI_SECURITY;
427 	}
428 
429 	return prio;
430 }
431 
xlate_chain_set(struct nft_handle * h,const char * table,const char * chain,const char * policy,const struct xt_counters * counters)432 static int xlate_chain_set(struct nft_handle *h, const char *table,
433 			   const char *chain, const char *policy,
434 			   const struct xt_counters *counters)
435 {
436 	const char *type = "filter";
437 	int prio;
438 
439 	if (strcmp(table, "nat") == 0)
440 		type = "nat";
441 	else if (strcmp(table, "mangle") == 0 && strcmp(chain, "OUTPUT") == 0)
442 		type = "route";
443 
444 	printf("add chain %s %s %s { type %s ",
445 	       family2str[h->family], table, chain, type);
446 	prio = get_hook_prio(table, chain);
447 	if (strcmp(chain, "PREROUTING") == 0)
448 		printf("hook prerouting priority %d; ", prio);
449 	else if (strcmp(chain, "INPUT") == 0)
450 		printf("hook input priority %d; ", prio);
451 	else if (strcmp(chain, "FORWARD") == 0)
452 		printf("hook forward priority %d; ", prio);
453 	else if (strcmp(chain, "OUTPUT") == 0)
454 		printf("hook output priority %d; ", prio);
455 	else if (strcmp(chain, "POSTROUTING") == 0)
456 		printf("hook postrouting priority %d; ", prio);
457 
458 	if (strcmp(policy, "ACCEPT") == 0)
459 		printf("policy accept; ");
460 	else if (strcmp(policy, "DROP") == 0)
461 		printf("policy drop; ");
462 
463 	printf("}\n");
464 	return 1;
465 }
466 
dummy_compat_rev(const char * name,uint8_t rev,int opt)467 static int dummy_compat_rev(const char *name, uint8_t rev, int opt)
468 {
469 	/* Avoid querying the kernel - it's not needed when just translating
470 	 * rules and not even possible when running as unprivileged user.
471 	 */
472 	return 1;
473 }
474 
475 static const struct nft_xt_restore_cb cb_xlate = {
476 	.table_new	= xlate_table_new,
477 	.chain_set	= xlate_chain_set,
478 	.chain_restore	= xlate_chain_user_restore,
479 	.do_command	= do_command_xlate,
480 	.commit		= commit,
481 	.abort		= commit,
482 };
483 
xtables_xlate_main_common(struct nft_handle * h,int family,const char * progname)484 static int xtables_xlate_main_common(struct nft_handle *h,
485 				     int family,
486 				     const char *progname)
487 {
488 	int ret;
489 
490 	xtables_globals.program_name = progname;
491 	xtables_globals.compat_rev = dummy_compat_rev;
492 
493 	switch (family) {
494 	case NFPROTO_IPV4:
495 		ret = xtables_init_all(&xtables_globals, family);
496 		break;
497 	case NFPROTO_IPV6:
498 		ret = xtables_init_all(&xtables_globals, family);
499 		break;
500 	case NFPROTO_ARP:
501 		arptables_globals.program_name = progname;
502 		arptables_globals.compat_rev = dummy_compat_rev;
503 		ret = xtables_init_all(&arptables_globals, family);
504 		break;
505 	default:
506 		ret = -1;
507 		break;
508 	}
509 
510 	if (ret < 0) {
511 		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
512 			xtables_globals.program_name,
513 			xtables_globals.program_version);
514 		return 1;
515 	}
516 	init_extensions();
517 	switch (family) {
518 	case NFPROTO_IPV4:
519 		init_extensions4();
520 		break;
521 	case NFPROTO_IPV6:
522 		init_extensions6();
523 		break;
524 	case NFPROTO_ARP:
525 		init_extensionsa();
526 		break;
527 	case NFPROTO_BRIDGE:
528 		init_extensionsb();
529 		break;
530 	default:
531 		fprintf(stderr, "Unknown family %d\n", family);
532 		return 1;
533 	}
534 
535 	if (nft_init(h, family) < 0) {
536 		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
537 				xtables_globals.program_name,
538 				xtables_globals.program_version,
539 				strerror(errno));
540 		return 1;
541 	}
542 
543 	return 0;
544 }
545 
xtables_xlate_main(int family,const char * progname,int argc,char * argv[])546 static int xtables_xlate_main(int family, const char *progname, int argc,
547 			      char *argv[])
548 {
549 	int ret;
550 	char *table = "filter";
551 	struct nft_handle h = {
552 		.family = family,
553 	};
554 
555 	ret = xtables_xlate_main_common(&h, family, progname);
556 	if (ret < 0)
557 		exit(EXIT_FAILURE);
558 
559 	ret = do_command_xlate(&h, argc, argv, &table, false);
560 	if (!ret)
561 		fprintf(stderr, "Translation not implemented\n");
562 
563 	nft_fini(&h);
564 	xtables_fini();
565 	exit(!ret);
566 }
567 
xtables_restore_xlate_main(int family,const char * progname,int argc,char * argv[])568 static int xtables_restore_xlate_main(int family, const char *progname,
569 				      int argc, char *argv[])
570 {
571 	int ret;
572 	struct nft_handle h = {
573 		.family = family,
574 	};
575 	const char *file = NULL;
576 	struct nft_xt_restore_parse p = {
577 		.cb = &cb_xlate,
578 	};
579 	time_t now = time(NULL);
580 	int c;
581 
582 	ret = xtables_xlate_main_common(&h, family, progname);
583 	if (ret < 0)
584 		exit(EXIT_FAILURE);
585 
586 	opterr = 0;
587 	while ((c = getopt_long(argc, argv, "hf:V", options, NULL)) != -1) {
588 		switch (c) {
589 		case 'h':
590 			print_usage(argv[0], PACKAGE_VERSION);
591 			exit(0);
592 		case 'f':
593 			file = optarg;
594 			break;
595 		case 'V':
596 			printf("%s v%s\n", argv[0], PACKAGE_VERSION);
597 			exit(0);
598 		}
599 	}
600 
601 	if (file == NULL) {
602 		fprintf(stderr, "ERROR: missing file name\n");
603 		print_usage(argv[0], PACKAGE_VERSION);
604 		exit(0);
605 	}
606 
607 	p.in = fopen(file, "r");
608 	if (p.in == NULL) {
609 		fprintf(stderr, "Cannot open file %s\n", file);
610 		exit(1);
611 	}
612 
613 	printf("# Translated by %s v%s on %s",
614 	       argv[0], PACKAGE_VERSION, ctime(&now));
615 	xtables_restore_parse(&h, &p);
616 	printf("# Completed on %s", ctime(&now));
617 
618 	nft_fini(&h);
619 	xtables_fini();
620 	fclose(p.in);
621 	exit(0);
622 }
623 
xtables_arp_xlate_main(int argc,char * argv[])624 int xtables_arp_xlate_main(int argc, char *argv[])
625 {
626 	return xtables_xlate_main(NFPROTO_ARP, "arptables-translate",
627 				  argc, argv);
628 }
629 
xtables_ip4_xlate_main(int argc,char * argv[])630 int xtables_ip4_xlate_main(int argc, char *argv[])
631 {
632 	return xtables_xlate_main(NFPROTO_IPV4, "iptables-translate",
633 				  argc, argv);
634 }
635 
xtables_ip6_xlate_main(int argc,char * argv[])636 int xtables_ip6_xlate_main(int argc, char *argv[])
637 {
638 	return xtables_xlate_main(NFPROTO_IPV6, "ip6tables-translate",
639 				  argc, argv);
640 }
641 
xtables_ip4_xlate_restore_main(int argc,char * argv[])642 int xtables_ip4_xlate_restore_main(int argc, char *argv[])
643 {
644 	return xtables_restore_xlate_main(NFPROTO_IPV4,
645 					  "iptables-translate-restore",
646 					  argc, argv);
647 }
648 
xtables_ip6_xlate_restore_main(int argc,char * argv[])649 int xtables_ip6_xlate_restore_main(int argc, char *argv[])
650 {
651 	return xtables_restore_xlate_main(NFPROTO_IPV6,
652 					  "ip6tables-translate-restore",
653 					  argc, argv);
654 }
655