| /* |
| * (C) 2014 by Pablo Neira Ayuso <pablo@netfilter.org> |
| * |
| * 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 <time.h> |
| #include "xtables-multi.h" |
| #include "nft.h" |
| |
| #include <string.h> |
| #include <netdb.h> |
| #include <errno.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| #include <stdarg.h> |
| #include <limits.h> |
| #include <unistd.h> |
| #include <iptables.h> |
| #include <xtables.h> |
| #include <libiptc/libxtc.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #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 && p.command != CMD_NONE) |
| 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; |
| case CMD_NONE: |
| ret = 1; |
| break; |
| default: |
| /* We should never reach this... */ |
| printf("Unsupported command?\n"); |
| exit(1); |
| } |
| |
| nft_clear_iptables_command_state(&cs); |
| |
| 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 <pablo@netfilter.org>\n" |
| "Usage: %s [-h] [-f]\n" |
| " [ --help ]\n" |
| " [ --file=<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); |
| xtables_fini(); |
| 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); |
| xtables_fini(); |
| 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); |
| } |