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, ¶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 .escape_quotes = !cs->restore,
119 };
120
121 if (!matchp->match->xlate)
122 return 0;
123
124 ret = matchp->match->xlate(xl, ¶ms);
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