/* * (C) 2014 by Pablo Neira Ayuso * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include "config.h" #include #include "xtables-multi.h" #include "nft.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xshared.h" #include "nft-shared.h" void xlate_ifname(struct xt_xlate *xl, const char *nftmeta, const char *ifname, bool invert) { int ifaclen = strlen(ifname), i, j; char iface[IFNAMSIZ * 2]; if (ifaclen < 1 || ifaclen >= IFNAMSIZ) return; for (i = 0, j = 0; i < ifaclen + 1; i++, j++) { switch (ifname[i]) { case '*': iface[j++] = '\\'; /* fall through */ default: iface[j] = ifname[i]; break; } } if (ifaclen == 1 && ifname[0] == '+') { /* Nftables does not support wildcard only string. Workaround * is easy, given that this will match always or never * depending on 'invert' value. To match always, simply don't * generate an expression. To match never, use an invalid * interface name (kernel doesn't accept '/' in names) to match * against. */ if (!invert) return; strcpy(iface, "INVAL/D"); invert = false; } if (iface[j - 2] == '+') iface[j - 2] = '*'; xt_xlate_add(xl, "%s %s\"%s\" ", nftmeta, invert ? "!= " : "", iface); } int xlate_action(const struct iptables_command_state *cs, bool goto_set, struct xt_xlate *xl) { int ret = 1, numeric = cs->options & OPT_NUMERIC; /* If no target at all, add nothing (default to continue) */ if (cs->target != NULL) { /* Standard target? */ if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0) xt_xlate_add(xl, " accept"); else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0) xt_xlate_add(xl, " drop"); else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0) xt_xlate_add(xl, " return"); else if (cs->target->xlate) { xt_xlate_add(xl, " "); struct xt_xlate_tg_params params = { .ip = (const void *)&cs->fw, .target = cs->target->t, .numeric = numeric, .escape_quotes = !cs->restore, }; ret = cs->target->xlate(xl, ¶ms); } else return 0; } else if (strlen(cs->jumpto) > 0) { /* Not standard, then it's a go / jump to chain */ if (goto_set) xt_xlate_add(xl, " goto %s", cs->jumpto); else xt_xlate_add(xl, " jump %s", cs->jumpto); } return ret; } int xlate_matches(const struct iptables_command_state *cs, struct xt_xlate *xl) { struct xtables_rule_match *matchp; int ret = 1, numeric = cs->options & OPT_NUMERIC; for (matchp = cs->matches; matchp; matchp = matchp->next) { struct xt_xlate_mt_params params = { .ip = (const void *)&cs->fw, .match = matchp->match->m, .numeric = numeric, .escape_quotes = !cs->restore, }; if (!matchp->match->xlate) return 0; ret = matchp->match->xlate(xl, ¶ms); if (strcmp(matchp->match->name, "comment") != 0) xt_xlate_add(xl, " "); if (!ret) break; } return ret; } bool xlate_find_match(const struct iptables_command_state *cs, const char *p_name) { struct xtables_rule_match *matchp; /* Skip redundant protocol, eg. ip protocol tcp tcp dport */ for (matchp = cs->matches; matchp; matchp = matchp->next) { if (strcmp(matchp->match->name, p_name) == 0) return true; } return false; } const char *family2str[] = { [NFPROTO_IPV4] = "ip", [NFPROTO_IPV6] = "ip6", }; static int nft_rule_xlate_add(struct nft_handle *h, const struct nft_xt_cmd_parse *p, const struct iptables_command_state *cs, bool append) { struct xt_xlate *xl = xt_xlate_alloc(10240); int ret; if (append) { xt_xlate_add(xl, "add rule %s %s %s ", family2str[h->family], p->table, p->chain); } else { xt_xlate_add(xl, "insert rule %s %s %s ", family2str[h->family], p->table, p->chain); } ret = h->ops->xlate(cs, xl); if (ret) printf("%s\n", xt_xlate_get(xl)); xt_xlate_free(xl); return ret; } static int 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)) { unsigned int i, j; int ret = 1; for (i = 0; i < args->s.naddrs; i++) { switch (h->family) { case AF_INET: cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr; cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr; for (j = 0; j < args->d.naddrs; j++) { cs->fw.ip.dst.s_addr = args->d.addr.v4[j].s_addr; cs->fw.ip.dmsk.s_addr = args->d.mask.v4[j].s_addr; ret = cb(h, p, cs, append); } break; case AF_INET6: memcpy(&cs->fw6.ipv6.src, &args->s.addr.v6[i], sizeof(struct in6_addr)); memcpy(&cs->fw6.ipv6.smsk, &args->s.mask.v6[i], sizeof(struct in6_addr)); for (j = 0; j < args->d.naddrs; j++) { memcpy(&cs->fw6.ipv6.dst, &args->d.addr.v6[j], sizeof(struct in6_addr)); memcpy(&cs->fw6.ipv6.dmsk, &args->d.mask.v6[j], sizeof(struct in6_addr)); ret = cb(h, p, cs, append); } break; } if (!cs->restore && i < args->s.naddrs - 1) printf("nft "); } return ret; } static void print_ipt_cmd(int argc, char *argv[]) { int i; printf("# "); for (i = 1; i < argc; i++) printf("%s ", argv[i]); printf("\n"); } static int do_command_xlate(struct nft_handle *h, int argc, char *argv[], char **table, bool restore) { int ret = 0; struct nft_xt_cmd_parse p = { .table = *table, .restore = restore, .xlate = true, }; struct iptables_command_state cs; struct xtables_args args = { .family = h->family, }; do_parse(h, argc, argv, &p, &cs, &args); cs.restore = restore; if (!restore) printf("nft "); switch (p.command) { case CMD_APPEND: ret = 1; if (!xlate(h, &p, &cs, &args, true, nft_rule_xlate_add)) print_ipt_cmd(argc, argv); break; case CMD_DELETE: break; case CMD_DELETE_NUM: break; case CMD_CHECK: break; case CMD_REPLACE: break; case CMD_INSERT: ret = 1; if (!xlate(h, &p, &cs, &args, false, nft_rule_xlate_add)) print_ipt_cmd(argc, argv); break; case CMD_FLUSH: if (p.chain) { printf("flush chain %s %s %s\n", family2str[h->family], p.table, p.chain); } else { printf("flush table %s %s\n", family2str[h->family], p.table); } ret = 1; break; case CMD_ZERO: break; case CMD_ZERO_NUM: break; case CMD_LIST: case CMD_LIST|CMD_ZERO: case CMD_LIST|CMD_ZERO_NUM: printf("list table %s %s\n", family2str[h->family], p.table); ret = 1; break; case CMD_LIST_RULES: case CMD_LIST_RULES|CMD_ZERO: case CMD_LIST_RULES|CMD_ZERO_NUM: break; case CMD_NEW_CHAIN: printf("add chain %s %s %s\n", family2str[h->family], p.table, p.chain); ret = 1; break; case CMD_DELETE_CHAIN: printf("delete chain %s %s %s\n", family2str[h->family], p.table, p.chain); ret = 1; break; case CMD_RENAME_CHAIN: break; case CMD_SET_POLICY: break; default: /* We should never reach this... */ printf("Unsupported command?\n"); exit(1); } xtables_rule_matches_free(&cs.matches); if (h->family == AF_INET) { free(args.s.addr.v4); free(args.s.mask.v4); free(args.d.addr.v4); free(args.d.mask.v4); } else if (h->family == AF_INET6) { free(args.s.addr.v6); free(args.s.mask.v6); free(args.d.addr.v6); free(args.d.mask.v6); } xtables_free_opts(1); return ret; } static void print_usage(const char *name, const char *version) { fprintf(stderr, "%s %s " "(c) 2014 by Pablo Neira Ayuso \n" "Usage: %s [-h] [-f]\n" " [ --help ]\n" " [ --file= ]\n", name, version, name); exit(1); } static const struct option options[] = { { .name = "help", .has_arg = false, .val = 'h' }, { .name = "file", .has_arg = true, .val = 'f' }, { .name = "version", .has_arg = false, .val = 'V' }, { NULL }, }; static int xlate_chain_user_restore(struct nft_handle *h, const char *chain, const char *table) { printf("add chain %s %s %s\n", family2str[h->family], table, chain); return 0; } static int commit(struct nft_handle *h) { return 1; } static void xlate_table_new(struct nft_handle *h, const char *table) { printf("add table %s %s\n", family2str[h->family], table); } static int get_hook_prio(const char *table, const char *chain) { int prio = 0; if (strcmp("nat", table) == 0) { if (strcmp(chain, "PREROUTING") == 0) prio = NF_IP_PRI_NAT_DST; if (strcmp(chain, "INPUT") == 0) prio = NF_IP_PRI_NAT_SRC; if (strcmp(chain, "OUTPUT") == 0) prio = NF_IP_PRI_NAT_DST; if (strcmp(chain, "POSTROUTING") == 0) prio = NF_IP_PRI_NAT_SRC; } else if (strcmp("mangle", table) == 0) { prio = NF_IP_PRI_MANGLE; } else if (strcmp("raw", table) == 0) { prio = NF_IP_PRI_RAW; } else if (strcmp(chain, "security") == 0) { prio = NF_IP_PRI_SECURITY; } return prio; } static int xlate_chain_set(struct nft_handle *h, const char *table, const char *chain, const char *policy, const struct xt_counters *counters) { const char *type = "filter"; int prio; if (strcmp(table, "nat") == 0) type = "nat"; else if (strcmp(table, "mangle") == 0 && strcmp(chain, "OUTPUT") == 0) type = "route"; printf("add chain %s %s %s { type %s ", family2str[h->family], table, chain, type); prio = get_hook_prio(table, chain); if (strcmp(chain, "PREROUTING") == 0) printf("hook prerouting priority %d; ", prio); else if (strcmp(chain, "INPUT") == 0) printf("hook input priority %d; ", prio); else if (strcmp(chain, "FORWARD") == 0) printf("hook forward priority %d; ", prio); else if (strcmp(chain, "OUTPUT") == 0) printf("hook output priority %d; ", prio); else if (strcmp(chain, "POSTROUTING") == 0) printf("hook postrouting priority %d; ", prio); if (strcmp(policy, "ACCEPT") == 0) printf("policy accept; "); else if (strcmp(policy, "DROP") == 0) printf("policy drop; "); printf("}\n"); return 1; } static int dummy_compat_rev(const char *name, uint8_t rev, int opt) { /* Avoid querying the kernel - it's not needed when just translating * rules and not even possible when running as unprivileged user. */ return 1; } static const struct nft_xt_restore_cb cb_xlate = { .table_new = xlate_table_new, .chain_set = xlate_chain_set, .chain_restore = xlate_chain_user_restore, .do_command = do_command_xlate, .commit = commit, .abort = commit, }; static int xtables_xlate_main_common(struct nft_handle *h, int family, const char *progname) { const struct builtin_table *tables; int ret; xtables_globals.program_name = progname; xtables_globals.compat_rev = dummy_compat_rev; ret = xtables_init_all(&xtables_globals, family); if (ret < 0) { fprintf(stderr, "%s/%s Failed to initialize xtables\n", xtables_globals.program_name, xtables_globals.program_version); return 1; } switch (family) { case NFPROTO_IPV4: case NFPROTO_IPV6: /* fallthrough: same table */ #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) init_extensions(); init_extensions4(); #endif tables = xtables_ipv4; break; case NFPROTO_ARP: tables = xtables_arp; break; case NFPROTO_BRIDGE: tables = xtables_bridge; break; default: fprintf(stderr, "Unknown family %d\n", family); return 1; } if (nft_init(h, family, tables) < 0) { fprintf(stderr, "%s/%s Failed to initialize nft: %s\n", xtables_globals.program_name, xtables_globals.program_version, strerror(errno)); return 1; } return 0; } static int xtables_xlate_main(int family, const char *progname, int argc, char *argv[]) { int ret; char *table = "filter"; struct nft_handle h = { .family = family, }; ret = xtables_xlate_main_common(&h, family, progname); if (ret < 0) exit(EXIT_FAILURE); ret = do_command_xlate(&h, argc, argv, &table, false); if (!ret) fprintf(stderr, "Translation not implemented\n"); nft_fini(&h); exit(!ret); } static int xtables_restore_xlate_main(int family, const char *progname, int argc, char *argv[]) { int ret; struct nft_handle h = { .family = family, }; const char *file = NULL; struct nft_xt_restore_parse p = { .cb = &cb_xlate, }; time_t now = time(NULL); int c; ret = xtables_xlate_main_common(&h, family, progname); if (ret < 0) exit(EXIT_FAILURE); opterr = 0; while ((c = getopt_long(argc, argv, "hf:V", options, NULL)) != -1) { switch (c) { case 'h': print_usage(argv[0], PACKAGE_VERSION); exit(0); case 'f': file = optarg; break; case 'V': printf("%s v%s\n", argv[0], PACKAGE_VERSION); exit(0); } } if (file == NULL) { fprintf(stderr, "ERROR: missing file name\n"); print_usage(argv[0], PACKAGE_VERSION); exit(0); } p.in = fopen(file, "r"); if (p.in == NULL) { fprintf(stderr, "Cannot open file %s\n", file); exit(1); } printf("# Translated by %s v%s on %s", argv[0], PACKAGE_VERSION, ctime(&now)); xtables_restore_parse(&h, &p); printf("# Completed on %s", ctime(&now)); nft_fini(&h); fclose(p.in); exit(0); } int xtables_ip4_xlate_main(int argc, char *argv[]) { return xtables_xlate_main(NFPROTO_IPV4, "iptables-translate", argc, argv); } int xtables_ip6_xlate_main(int argc, char *argv[]) { return xtables_xlate_main(NFPROTO_IPV6, "ip6tables-translate", argc, argv); } int xtables_ip4_xlate_restore_main(int argc, char *argv[]) { return xtables_restore_xlate_main(NFPROTO_IPV4, "iptables-translate-restore", argc, argv); } int xtables_ip6_xlate_restore_main(int argc, char *argv[]) { return xtables_restore_xlate_main(NFPROTO_IPV6, "ip6tables-translate-restore", argc, argv); }