/*
|
* (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 <stdio.h>
|
#include <stdlib.h>
|
#include <errno.h>
|
#include <string.h>
|
#include <iptables.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)
|
{
|
char iface[IFNAMSIZ];
|
int ifaclen;
|
|
if (ifname[0] == '\0')
|
return;
|
|
strcpy(iface, ifname);
|
ifaclen = strlen(iface);
|
if (iface[ifaclen - 1] == '+')
|
iface[ifaclen - 1] = '*';
|
|
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) {
|
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;
|
}
|
}
|
|
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,
|
};
|
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 <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' },
|
{ NULL },
|
};
|
|
static int xlate_chain_user_add(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 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";
|
|
if (strcmp(table, "nat") == 0)
|
type = "nat";
|
|
printf("add chain %s %s %s { type %s ",
|
family2str[h->family], table, chain, type);
|
if (strcmp(chain, "PREROUTING") == 0)
|
printf("hook prerouting priority 0; ");
|
else if (strcmp(chain, "INPUT") == 0)
|
printf("hook input priority 0; ");
|
else if (strcmp(chain, "FORWARD") == 0)
|
printf("hook forward priority 0; ");
|
else if (strcmp(chain, "OUTPUT") == 0)
|
printf("hook output priority 0; ");
|
else if (strcmp(chain, "POSTROUTING") == 0)
|
printf("hook postrouting priority 0; ");
|
|
if (strcmp(policy, "ACCEPT") == 0)
|
printf("policy accept; ");
|
else if (strcmp(policy, "DROP") == 0)
|
printf("policy drop; ");
|
|
printf("}\n");
|
return 1;
|
}
|
|
static struct nft_xt_restore_cb cb_xlate = {
|
.table_new = xlate_table_new,
|
.chain_set = xlate_chain_set,
|
.chain_user_add = xlate_chain_user_add,
|
.do_command = do_command_xlate,
|
.commit = commit,
|
.abort = commit,
|
};
|
|
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,
|
};
|
|
xtables_globals.program_name = progname;
|
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);
|
exit(1);
|
}
|
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
|
init_extensions();
|
init_extensions4();
|
#endif
|
|
if (nft_init(&h, xtables_ipv4) < 0) {
|
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
|
xtables_globals.program_name,
|
xtables_globals.program_version,
|
strerror(errno));
|
nft_fini(&h);
|
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 = {};
|
time_t now = time(NULL);
|
int c;
|
|
xtables_globals.program_name = progname;
|
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);
|
exit(1);
|
}
|
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
|
init_extensions();
|
init_extensions4();
|
#endif
|
|
if (nft_init(&h, xtables_ipv4) < 0) {
|
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
|
xtables_globals.program_name,
|
xtables_globals.program_version,
|
strerror(errno));
|
nft_fini(&h);
|
exit(EXIT_FAILURE);
|
}
|
|
opterr = 0;
|
while ((c = getopt_long(argc, argv, "hf:", options, NULL)) != -1) {
|
switch (c) {
|
case 'h':
|
print_usage(argv[0], IPTABLES_VERSION);
|
exit(0);
|
case 'f':
|
file = optarg;
|
break;
|
}
|
}
|
|
if (file == NULL) {
|
fprintf(stderr, "ERROR: missing file name\n");
|
print_usage(argv[0], IPTABLES_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], IPTABLES_VERSION, ctime(&now));
|
xtables_restore_parse(&h, &p, &cb_xlate, argc, argv);
|
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);
|
}
|