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
10#include <stdio.h>
11#include <stdlib.h>
12#include <errno.h>
13#include <string.h>
14#include <iptables.h>
15#include <time.h>
16#include "xtables-multi.h"
17#include "nft.h"
18
19#include <string.h>
20#include <netdb.h>
21#include <errno.h>
22#include <stdbool.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <ctype.h>
26#include <stdarg.h>
27#include <limits.h>
28#include <unistd.h>
29#include <iptables.h>
30#include <xtables.h>
31#include <libiptc/libxtc.h>
32#include <fcntl.h>
33#include <getopt.h>
34#include "xshared.h"
35#include "nft-shared.h"
36
37void xlate_ifname(struct xt_xlate *xl, const char *nftmeta, const char *ifname,
38		  bool invert)
39{
40	char iface[IFNAMSIZ];
41	int ifaclen;
42
43	if (ifname[0] == '\0')
44		return;
45
46	strcpy(iface, ifname);
47	ifaclen = strlen(iface);
48	if (iface[ifaclen - 1] == '+')
49		iface[ifaclen - 1] = '*';
50
51	xt_xlate_add(xl, "%s %s%s ", nftmeta, invert ? "!= " : "", iface);
52}
53
54int xlate_action(const struct iptables_command_state *cs, bool goto_set,
55		 struct xt_xlate *xl)
56{
57	int ret = 1, numeric = cs->options & OPT_NUMERIC;
58
59	/* If no target at all, add nothing (default to continue) */
60	if (cs->target != NULL) {
61		/* Standard target? */
62		if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
63			xt_xlate_add(xl, "accept");
64		else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
65			xt_xlate_add(xl, "drop");
66		else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
67			xt_xlate_add(xl, "return");
68		else if (cs->target->xlate) {
69			struct xt_xlate_tg_params params = {
70				.ip		= (const void *)&cs->fw,
71				.target		= cs->target->t,
72				.numeric	= numeric,
73				.escape_quotes	= !cs->restore,
74			};
75			ret = cs->target->xlate(xl, &params);
76		}
77		else
78			return 0;
79	} else if (strlen(cs->jumpto) > 0) {
80		/* Not standard, then it's a go / jump to chain */
81		if (goto_set)
82			xt_xlate_add(xl, "goto %s", cs->jumpto);
83		else
84			xt_xlate_add(xl, "jump %s", cs->jumpto);
85	}
86
87	return ret;
88}
89
90int xlate_matches(const struct iptables_command_state *cs, struct xt_xlate *xl)
91{
92	struct xtables_rule_match *matchp;
93	int ret = 1, numeric = cs->options & OPT_NUMERIC;
94
95	for (matchp = cs->matches; matchp; matchp = matchp->next) {
96		struct xt_xlate_mt_params params = {
97			.ip		= (const void *)&cs->fw,
98			.match		= matchp->match->m,
99			.numeric	= numeric,
100			.escape_quotes	= !cs->restore,
101		};
102
103		if (!matchp->match->xlate)
104			return 0;
105
106		ret = matchp->match->xlate(xl, &params);
107
108		if (strcmp(matchp->match->name, "comment") != 0)
109			xt_xlate_add(xl, " ");
110
111		if (!ret)
112			break;
113	}
114	return ret;
115}
116
117bool xlate_find_match(const struct iptables_command_state *cs, const char *p_name)
118{
119	struct xtables_rule_match *matchp;
120
121	/* Skip redundant protocol, eg. ip protocol tcp tcp dport */
122	for (matchp = cs->matches; matchp; matchp = matchp->next) {
123		if (strcmp(matchp->match->name, p_name) == 0)
124			return true;
125	}
126	return false;
127}
128
129const char *family2str[] = {
130	[NFPROTO_IPV4]	= "ip",
131	[NFPROTO_IPV6]	= "ip6",
132};
133
134static int nft_rule_xlate_add(struct nft_handle *h,
135			      const struct nft_xt_cmd_parse *p,
136			      const struct iptables_command_state *cs,
137			      bool append)
138{
139	struct xt_xlate *xl = xt_xlate_alloc(10240);
140	int ret;
141
142	if (append) {
143		xt_xlate_add(xl, "add rule %s %s %s ",
144			   family2str[h->family], p->table, p->chain);
145	} else {
146		xt_xlate_add(xl, "insert rule %s %s %s ",
147			   family2str[h->family], p->table, p->chain);
148	}
149
150	ret = h->ops->xlate(cs, xl);
151	if (ret)
152		printf("%s\n", xt_xlate_get(xl));
153
154	xt_xlate_free(xl);
155	return ret;
156}
157
158static int xlate(struct nft_handle *h, struct nft_xt_cmd_parse *p,
159		 struct iptables_command_state *cs,
160		 struct xtables_args *args, bool append,
161		 int (*cb)(struct nft_handle *h,
162			   const struct nft_xt_cmd_parse *p,
163			   const struct iptables_command_state *cs,
164			   bool append))
165{
166	unsigned int i, j;
167	int ret = 1;
168
169	for (i = 0; i < args->s.naddrs; i++) {
170		switch (h->family) {
171		case AF_INET:
172			cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr;
173			cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr;
174			for (j = 0; j < args->d.naddrs; j++) {
175				cs->fw.ip.dst.s_addr =
176					args->d.addr.v4[j].s_addr;
177				cs->fw.ip.dmsk.s_addr =
178					args->d.mask.v4[j].s_addr;
179				ret = cb(h, p, cs, append);
180			}
181			break;
182		case AF_INET6:
183			memcpy(&cs->fw6.ipv6.src,
184			       &args->s.addr.v6[i], sizeof(struct in6_addr));
185			memcpy(&cs->fw6.ipv6.smsk,
186			       &args->s.mask.v6[i], sizeof(struct in6_addr));
187			for (j = 0; j < args->d.naddrs; j++) {
188				memcpy(&cs->fw6.ipv6.dst,
189				       &args->d.addr.v6[j],
190				       sizeof(struct in6_addr));
191				memcpy(&cs->fw6.ipv6.dmsk,
192				       &args->d.mask.v6[j],
193				       sizeof(struct in6_addr));
194				ret = cb(h, p, cs, append);
195			}
196			break;
197		}
198	}
199
200	return ret;
201}
202
203static void print_ipt_cmd(int argc, char *argv[])
204{
205	int i;
206
207	printf("# ");
208	for (i = 1; i < argc; i++)
209		printf("%s ", argv[i]);
210
211	printf("\n");
212}
213
214static int do_command_xlate(struct nft_handle *h, int argc, char *argv[],
215			    char **table, bool restore)
216{
217	int ret = 0;
218	struct nft_xt_cmd_parse p = {
219		.table		= *table,
220		.restore	= restore,
221	};
222	struct iptables_command_state cs;
223	struct xtables_args args = {
224		.family = h->family,
225	};
226
227	do_parse(h, argc, argv, &p, &cs, &args);
228
229	cs.restore = restore;
230
231	if (!restore)
232		printf("nft ");
233
234	switch (p.command) {
235	case CMD_APPEND:
236		ret = 1;
237		if (!xlate(h, &p, &cs, &args, true, nft_rule_xlate_add)) {
238			print_ipt_cmd(argc, argv);
239		}
240		break;
241	case CMD_DELETE:
242		break;
243	case CMD_DELETE_NUM:
244		break;
245	case CMD_CHECK:
246		break;
247	case CMD_REPLACE:
248		break;
249	case CMD_INSERT:
250		ret = 1;
251		if (!xlate(h, &p, &cs, &args, false, nft_rule_xlate_add)) {
252			print_ipt_cmd(argc, argv);
253		}
254		break;
255	case CMD_FLUSH:
256		if (p.chain) {
257			printf("flush chain %s %s %s\n",
258				family2str[h->family], p.table, p.chain);
259		} else {
260			printf("flush table %s %s\n",
261				family2str[h->family], p.table);
262		}
263		ret = 1;
264		break;
265	case CMD_ZERO:
266		break;
267	case CMD_ZERO_NUM:
268		break;
269	case CMD_LIST:
270	case CMD_LIST|CMD_ZERO:
271	case CMD_LIST|CMD_ZERO_NUM:
272		printf("list table %s %s\n",
273		       family2str[h->family], p.table);
274		ret = 1;
275		break;
276	case CMD_LIST_RULES:
277	case CMD_LIST_RULES|CMD_ZERO:
278	case CMD_LIST_RULES|CMD_ZERO_NUM:
279		break;
280	case CMD_NEW_CHAIN:
281		printf("add chain %s %s %s\n",
282		       family2str[h->family], p.table, p.chain);
283		ret = 1;
284		break;
285	case CMD_DELETE_CHAIN:
286		printf("delete chain %s %s %s\n",
287		       family2str[h->family], p.table, p.chain);
288		ret = 1;
289		break;
290	case CMD_RENAME_CHAIN:
291		break;
292	case CMD_SET_POLICY:
293		break;
294	default:
295		/* We should never reach this... */
296		printf("Unsupported command?\n");
297		exit(1);
298	}
299
300	xtables_rule_matches_free(&cs.matches);
301
302	if (h->family == AF_INET) {
303		free(args.s.addr.v4);
304		free(args.s.mask.v4);
305		free(args.d.addr.v4);
306		free(args.d.mask.v4);
307	} else if (h->family == AF_INET6) {
308		free(args.s.addr.v6);
309		free(args.s.mask.v6);
310		free(args.d.addr.v6);
311		free(args.d.mask.v6);
312	}
313	xtables_free_opts(1);
314
315	return ret;
316}
317
318static void print_usage(const char *name, const char *version)
319{
320	fprintf(stderr, "%s %s "
321			"(c) 2014 by Pablo Neira Ayuso <pablo@netfilter.org>\n"
322			"Usage: %s [-h] [-f]\n"
323                        "	[ --help ]\n"
324                        "	[ --file=<FILE> ]\n", name, version, name);
325        exit(1);
326}
327
328static const struct option options[] = {
329	{ .name = "help",	.has_arg = false,	.val = 'h' },
330	{ .name = "file",	.has_arg = true,	.val = 'f' },
331	{ NULL },
332};
333
334static int xlate_chain_user_add(struct nft_handle *h, const char *chain,
335				const char *table)
336{
337	printf("add chain %s %s %s\n", family2str[h->family], table, chain);
338	return 0;
339}
340
341static int commit(struct nft_handle *h)
342{
343	return 1;
344}
345
346static void xlate_table_new(struct nft_handle *h, const char *table)
347{
348	printf("add table %s %s\n", family2str[h->family], table);
349}
350
351static int xlate_chain_set(struct nft_handle *h, const char *table,
352			   const char *chain, const char *policy,
353			   const struct xt_counters *counters)
354{
355	const char *type = "filter";
356
357	if (strcmp(table, "nat") == 0)
358		type = "nat";
359
360	printf("add chain %s %s %s { type %s ",
361	       family2str[h->family], table, chain, type);
362	if (strcmp(chain, "PREROUTING") == 0)
363		printf("hook prerouting priority 0; ");
364	else if (strcmp(chain, "INPUT") == 0)
365		printf("hook input priority 0; ");
366	else if (strcmp(chain, "FORWARD") == 0)
367		printf("hook forward priority 0; ");
368	else if (strcmp(chain, "OUTPUT") == 0)
369		printf("hook output priority 0; ");
370	else if (strcmp(chain, "POSTROUTING") == 0)
371		printf("hook postrouting priority 0; ");
372
373	if (strcmp(policy, "ACCEPT") == 0)
374		printf("policy accept; ");
375	else if (strcmp(policy, "DROP") == 0)
376		printf("policy drop; ");
377
378	printf("}\n");
379	return 1;
380}
381
382static struct nft_xt_restore_cb cb_xlate = {
383	.table_new	= xlate_table_new,
384	.chain_set	= xlate_chain_set,
385	.chain_user_add	= xlate_chain_user_add,
386	.do_command	= do_command_xlate,
387	.commit		= commit,
388	.abort		= commit,
389};
390
391static int xtables_xlate_main(int family, const char *progname, int argc,
392			      char *argv[])
393{
394	int ret;
395	char *table = "filter";
396	struct nft_handle h = {
397		.family = family,
398	};
399
400	xtables_globals.program_name = progname;
401	ret = xtables_init_all(&xtables_globals, family);
402	if (ret < 0) {
403		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
404				xtables_globals.program_name,
405				xtables_globals.program_version);
406				exit(1);
407	}
408#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
409	init_extensions();
410	init_extensions4();
411#endif
412
413	if (nft_init(&h, xtables_ipv4) < 0) {
414		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
415				xtables_globals.program_name,
416				xtables_globals.program_version,
417				strerror(errno));
418		nft_fini(&h);
419		exit(EXIT_FAILURE);
420	}
421
422	ret = do_command_xlate(&h, argc, argv, &table, false);
423	if (!ret)
424		fprintf(stderr, "Translation not implemented\n");
425
426	nft_fini(&h);
427	exit(!ret);
428}
429
430static int xtables_restore_xlate_main(int family, const char *progname,
431				      int argc, char *argv[])
432{
433	int ret;
434	struct nft_handle h = {
435		.family = family,
436	};
437	const char *file = NULL;
438	struct nft_xt_restore_parse p = {};
439	time_t now = time(NULL);
440	int c;
441
442	xtables_globals.program_name = progname;
443	ret = xtables_init_all(&xtables_globals, family);
444	if (ret < 0) {
445		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
446				xtables_globals.program_name,
447				xtables_globals.program_version);
448				exit(1);
449	}
450#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
451	init_extensions();
452	init_extensions4();
453#endif
454
455	if (nft_init(&h, xtables_ipv4) < 0) {
456		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
457				xtables_globals.program_name,
458				xtables_globals.program_version,
459				strerror(errno));
460		nft_fini(&h);
461		exit(EXIT_FAILURE);
462	}
463
464	opterr = 0;
465	while ((c = getopt_long(argc, argv, "hf:", options, NULL)) != -1) {
466		switch (c) {
467		case 'h':
468			print_usage(argv[0], IPTABLES_VERSION);
469			exit(0);
470		case 'f':
471			file = optarg;
472			break;
473		}
474	}
475
476	if (file == NULL) {
477		fprintf(stderr, "ERROR: missing file name\n");
478		print_usage(argv[0], IPTABLES_VERSION);
479		exit(0);
480	}
481
482	p.in = fopen(file, "r");
483	if (p.in == NULL) {
484		fprintf(stderr, "Cannot open file %s\n", file);
485		exit(1);
486	}
487
488	printf("# Translated by %s v%s on %s",
489	       argv[0], IPTABLES_VERSION, ctime(&now));
490	xtables_restore_parse(&h, &p, &cb_xlate, argc, argv);
491	printf("# Completed on %s", ctime(&now));
492
493	nft_fini(&h);
494	fclose(p.in);
495	exit(0);
496}
497
498int xtables_ip4_xlate_main(int argc, char *argv[])
499{
500	return xtables_xlate_main(NFPROTO_IPV4, "iptables-translate",
501				  argc, argv);
502}
503
504int xtables_ip6_xlate_main(int argc, char *argv[])
505{
506	return xtables_xlate_main(NFPROTO_IPV6, "ip6tables-translate",
507				  argc, argv);
508}
509
510int xtables_ip4_xlate_restore_main(int argc, char *argv[])
511{
512	return xtables_restore_xlate_main(NFPROTO_IPV4,
513					  "iptables-translate-restore",
514					  argc, argv);
515}
516
517int xtables_ip6_xlate_restore_main(int argc, char *argv[])
518{
519	return xtables_restore_xlate_main(NFPROTO_IPV6,
520					  "ip6tables-translate-restore",
521					  argc, argv);
522}
523