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, ¶ms);
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, ¶ms);
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