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, ¶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 /* 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