blob: c22e6c5b81e8ffcc78cc9cee761440ef35d83c45 [file] [log] [blame]
/*
* (C) 2012 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.
*
* This code has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdbool.h>
#include <errno.h>
#include <netdb.h> /* getprotobynumber */
#include <time.h>
#include <stdarg.h>
#include <inttypes.h>
#include <xtables.h>
#include <libiptc/libxtc.h>
#include <libiptc/xtcshared.h>
#include <stdlib.h>
#include <string.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <netinet/ip6.h>
#include <linux/netlink.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_tables_compat.h>
#include <libmnl/libmnl.h>
#include <libnftables/table.h>
#include <libnftables/chain.h>
#include <libnftables/rule.h>
#include <libnftables/expr.h>
#include <netinet/in.h> /* inet_ntoa */
#include <arpa/inet.h>
#include "nft.h"
#include "xshared.h" /* proto_to_name */
#include "nft-shared.h"
#include "xtables-config-parser.h"
static void *nft_fn;
static int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
int (*cb)(const struct nlmsghdr *nlh, void *data),
void *data)
{
int ret;
char buf[MNL_SOCKET_BUFFER_SIZE];
if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_send");
return -1;
}
ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, h->seq, h->portid, cb, data);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
}
if (ret == -1) {
return -1;
}
return 0;
}
#define FILTER 0
#define MANGLE 1
#define RAW 2
#define SECURITY 3
#define NAT 4
#define TABLES_MAX 5
struct builtin_chain {
const char *name;
const char *type;
uint32_t prio;
uint32_t hook;
};
static struct builtin_table {
const char *name;
struct builtin_chain chains[NF_INET_NUMHOOKS];
} tables[TABLES_MAX] = {
[RAW] = {
.name = "raw",
.chains = {
{
.name = "PREROUTING",
.type = "filter",
.prio = -300, /* NF_IP_PRI_RAW */
.hook = NF_INET_PRE_ROUTING,
},
{
.name = "OUTPUT",
.type = "filter",
.prio = -300, /* NF_IP_PRI_RAW */
.hook = NF_INET_LOCAL_OUT,
},
},
},
[MANGLE] = {
.name = "mangle",
.chains = {
{
.name = "PREROUTING",
.type = "filter",
.prio = -150, /* NF_IP_PRI_MANGLE */
.hook = NF_INET_PRE_ROUTING,
},
{
.name = "INPUT",
.type = "filter",
.prio = -150, /* NF_IP_PRI_MANGLE */
.hook = NF_INET_LOCAL_IN,
},
{
.name = "FORWARD",
.type = "filter",
.prio = -150, /* NF_IP_PRI_MANGLE */
.hook = NF_INET_FORWARD,
},
{
.name = "OUTPUT",
.type = "route",
.prio = -150, /* NF_IP_PRI_MANGLE */
.hook = NF_INET_LOCAL_OUT,
},
{
.name = "POSTROUTING",
.type = "filter",
.prio = -150, /* NF_IP_PRI_MANGLE */
.hook = NF_INET_POST_ROUTING,
},
},
},
[FILTER] = {
.name = "filter",
.chains = {
{
.name = "INPUT",
.type = "filter",
.prio = 0, /* NF_IP_PRI_FILTER */
.hook = NF_INET_LOCAL_IN,
},
{
.name = "FORWARD",
.type = "filter",
.prio = 0, /* NF_IP_PRI_FILTER */
.hook = NF_INET_FORWARD,
},
{
.name = "OUTPUT",
.type = "filter",
.prio = 0, /* NF_IP_PRI_FILTER */
.hook = NF_INET_LOCAL_OUT,
},
},
},
[SECURITY] = {
.name = "security",
.chains = {
{
.name = "INPUT",
.type = "filter",
.prio = 150, /* NF_IP_PRI_SECURITY */
.hook = NF_INET_LOCAL_IN,
},
{
.name = "FORWARD",
.type = "filter",
.prio = 150, /* NF_IP_PRI_SECURITY */
.hook = NF_INET_FORWARD,
},
{
.name = "OUTPUT",
.type = "filter",
.prio = 150, /* NF_IP_PRI_SECURITY */
.hook = NF_INET_LOCAL_OUT,
},
},
},
[NAT] = {
.name = "nat",
.chains = {
{
.name = "PREROUTING",
.type = "nat",
.prio = -100, /* NF_IP_PRI_NAT_DST */
.hook = NF_INET_PRE_ROUTING,
},
{
.name = "INPUT",
.type = "nat",
.prio = 100, /* NF_IP_PRI_NAT_SRC */
.hook = NF_INET_LOCAL_IN,
},
{
.name = "POSTROUTING",
.type = "nat",
.prio = 100, /* NF_IP_PRI_NAT_SRC */
.hook = NF_INET_POST_ROUTING,
},
{
.name = "OUTPUT",
.type = "nat",
.prio = -100, /* NF_IP_PRI_NAT_DST */
.hook = NF_INET_LOCAL_OUT,
},
},
},
};
static int
nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t,
bool dormant)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nft_table *t;
int ret;
t = nft_table_alloc();
if (t == NULL)
return -1;
nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)_t->name);
if (dormant) {
nft_table_attr_set_u32(t, NFT_TABLE_ATTR_FLAGS,
NFT_TABLE_F_DORMANT);
}
nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family,
NLM_F_ACK|NLM_F_EXCL, h->seq);
nft_table_nlmsg_build_payload(nlh, t);
nft_table_free(t);
ret = mnl_talk(h, nlh, NULL, NULL);
if (ret < 0) {
if (errno != EEXIST)
perror("mnl-talk:nft_table_init_one");
}
return ret;
}
static struct nft_chain *
nft_chain_builtin_alloc(struct builtin_table *table,
struct builtin_chain *chain, int policy)
{
struct nft_chain *c;
c = nft_chain_alloc();
if (c == NULL)
return NULL;
nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table->name);
nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain->name);
nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_HOOKNUM, chain->hook);
nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_PRIO, chain->prio);
nft_chain_attr_set_u32(c, NFT_CHAIN_ATTR_POLICY, policy);
nft_chain_attr_set(c, NFT_CHAIN_ATTR_TYPE, (char *)chain->type);
return c;
}
static void
nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table,
struct builtin_chain *chain, int policy)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nft_chain *c;
c = nft_chain_builtin_alloc(table, chain, policy);
if (c == NULL)
return;
/* NLM_F_CREATE requests module autoloading */
nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family,
NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE,
h->seq);
nft_chain_nlmsg_build_payload(nlh, c);
nft_chain_free(c);
if (mnl_talk(h, nlh, NULL, NULL) < 0) {
if (errno != EEXIST)
perror("mnl_talk:nft_chain_builtin_add");
}
}
/* find if built-in table already exists */
static struct builtin_table *nft_table_builtin_find(const char *table)
{
int i;
bool found = false;
for (i=0; i<TABLES_MAX; i++) {
if (strcmp(tables[i].name, table) != 0)
continue;
found = true;
break;
}
return found ? &tables[i] : NULL;
}
/* find if built-in chain already exists */
static struct builtin_chain *
nft_chain_builtin_find(struct builtin_table *t, const char *chain)
{
int i;
bool found = false;
for (i=0; i<NF_IP_NUMHOOKS && t->chains[i].name != NULL; i++) {
if (strcmp(t->chains[i].name, chain) != 0)
continue;
found = true;
break;
}
return found ? &t->chains[i] : NULL;
}
static void
__nft_chain_builtin_init(struct nft_handle *h,
struct builtin_table *table, const char *chain,
int policy)
{
int i, default_policy;
/* Initialize all built-in chains. Exception, for e one received as
* parameter, set the default policy as requested.
*/
for (i=0; i<NF_IP_NUMHOOKS && table->chains[i].name != NULL; i++) {
if (chain && strcmp(table->chains[i].name, chain) == 0)
default_policy = policy;
else
default_policy = NF_ACCEPT;
nft_chain_builtin_add(h, table, &table->chains[i],
default_policy);
}
}
static int
nft_chain_builtin_init(struct nft_handle *h, const char *table,
const char *chain, int policy)
{
int ret = 0;
struct builtin_table *t;
t = nft_table_builtin_find(table);
if (t == NULL) {
ret = -1;
goto out;
}
if (nft_table_builtin_add(h, t, false) < 0) {
/* Built-in table already initialized, skip. */
if (errno == EEXIST)
goto out;
}
__nft_chain_builtin_init(h, t, chain, policy);
out:
return ret;
}
static bool nft_chain_builtin(struct nft_chain *c)
{
/* Check if this chain has hook number, in that case is built-in.
* Should we better export the flags to user-space via nf_tables?
*/
return nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM) != NULL;
}
int nft_init(struct nft_handle *h)
{
h->nl = mnl_socket_open(NETLINK_NETFILTER);
if (h->nl == NULL) {
perror("mnl_socket_open");
return -1;
}
if (mnl_socket_bind(h->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
return -1;
}
h->portid = mnl_socket_get_portid(h->nl);
return 0;
}
void nft_fini(struct nft_handle *h)
{
mnl_socket_close(h->nl);
}
int nft_table_add(struct nft_handle *h, const struct nft_table *t)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family,
NLM_F_ACK|NLM_F_EXCL, h->seq);
nft_table_nlmsg_build_payload(nlh, t);
return mnl_talk(h, nlh, NULL, NULL);
}
int nft_chain_add(struct nft_handle *h, const struct nft_chain *c)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family,
NLM_F_ACK|NLM_F_EXCL, h->seq);
nft_chain_nlmsg_build_payload(nlh, c);
return mnl_talk(h, nlh, NULL, NULL);
}
int nft_table_set_dormant(struct nft_handle *h, const char *table)
{
int ret = 0, i;
struct builtin_table *t;
t = nft_table_builtin_find(table);
if (t == NULL) {
ret = -1;
goto out;
}
/* Add this table as dormant */
if (nft_table_builtin_add(h, t, true) < 0) {
/* Built-in table already initialized, skip. */
if (errno == EEXIST)
goto out;
}
for (i=0; t->chains[i].name != NULL && i<NF_INET_NUMHOOKS; i++)
__nft_chain_builtin_init(h, t, t->chains[i].name, NF_ACCEPT);
out:
return ret;
}
int nft_table_wake_dormant(struct nft_handle *h, const char *table)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nft_table *t;
t = nft_table_alloc();
if (t == NULL)
return -1;
nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)table);
nft_table_attr_set_u32(t, NFT_TABLE_ATTR_FLAGS, 0);
nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family,
NLM_F_ACK, h->seq);
nft_table_nlmsg_build_payload(nlh, t);
nft_table_free(t);
return mnl_talk(h, nlh, NULL, NULL);
}
static void nft_chain_print_debug(struct nft_chain *c, struct nlmsghdr *nlh)
{
#ifdef NLDEBUG
char tmp[1024];
nft_chain_snprintf(tmp, sizeof(tmp), c, 0, 0);
printf("DEBUG: chain: %s", tmp);
mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
#endif
}
static int
__nft_chain_set(struct nft_handle *h, const char *table,
const char *chain, int policy,
const struct xt_counters *counters)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nft_chain *c;
struct builtin_table *_t;
struct builtin_chain *_c;
int ret;
_t = nft_table_builtin_find(table);
/* if this built-in table does not exists, create it */
if (_t != NULL)
nft_table_builtin_add(h, _t, false);
_c = nft_chain_builtin_find(_t, chain);
if (_c != NULL) {
/* This is a built-in chain */
c = nft_chain_builtin_alloc(_t, _c, policy);
if (c == NULL)
return -1;
} else {
errno = ENOENT;
return -1;
}
if (counters) {
nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_BYTES,
counters->bcnt);
nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_PACKETS,
counters->pcnt);
}
nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family,
NLM_F_ACK, h->seq);
nft_chain_nlmsg_build_payload(nlh, c);
nft_chain_print_debug(c, nlh);
nft_chain_free(c);
ret = mnl_talk(h, nlh, NULL, NULL);
if (ret < 0)
perror("mnl_talk:__nft_chain_policy");
return ret;
}
int nft_chain_set(struct nft_handle *h, const char *table,
const char *chain, const char *policy,
const struct xt_counters *counters)
{
int ret = -1;
nft_fn = nft_chain_set;
if (strcmp(policy, "DROP") == 0)
ret = __nft_chain_set(h, table, chain, NF_DROP, counters);
else if (strcmp(policy, "ACCEPT") == 0)
ret = __nft_chain_set(h, table, chain, NF_ACCEPT, counters);
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
static int __add_match(struct nft_rule_expr *e, struct xt_entry_match *m)
{
void *info;
nft_rule_expr_set(e, NFT_EXPR_MT_NAME, m->u.user.name, strlen(m->u.user.name));
nft_rule_expr_set_u32(e, NFT_EXPR_MT_REV, m->u.user.revision);
info = calloc(1, m->u.match_size);
if (info == NULL)
return -ENOMEM;
memcpy(info, m->data, m->u.match_size);
nft_rule_expr_set(e, NFT_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m));
return 0;
}
static int add_match(struct nft_rule *r, struct xt_entry_match *m)
{
struct nft_rule_expr *expr;
int ret;
expr = nft_rule_expr_alloc("match");
if (expr == NULL)
return -ENOMEM;
ret = __add_match(expr, m);
nft_rule_add_expr(r, expr);
return ret;
}
static int __add_target(struct nft_rule_expr *e, struct xt_entry_target *t)
{
void *info = NULL;
nft_rule_expr_set(e, NFT_EXPR_TG_NAME, t->u.user.name,
strlen(t->u.user.name));
nft_rule_expr_set_u32(e, NFT_EXPR_TG_REV, t->u.user.revision);
if (info == NULL) {
info = calloc(1, t->u.target_size);
if (info == NULL)
return -ENOMEM;
memcpy(info, t->data, t->u.target_size);
}
nft_rule_expr_set(e, NFT_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t));
return 0;
}
static int add_target(struct nft_rule *r, struct xt_entry_target *t)
{
struct nft_rule_expr *expr;
int ret;
expr = nft_rule_expr_alloc("target");
if (expr == NULL)
return -ENOMEM;
ret = __add_target(expr, t);
nft_rule_add_expr(r, expr);
return ret;
}
static int add_jumpto(struct nft_rule *r, const char *name, int verdict)
{
struct nft_rule_expr *expr;
expr = nft_rule_expr_alloc("immediate");
if (expr == NULL)
return -ENOMEM;
nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_DREG, NFT_REG_VERDICT);
nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_VERDICT, verdict);
nft_rule_expr_set_str(expr, NFT_EXPR_IMM_CHAIN, (char *)name);
nft_rule_add_expr(r, expr);
return 0;
}
static int add_verdict(struct nft_rule *r, int verdict)
{
struct nft_rule_expr *expr;
expr = nft_rule_expr_alloc("immediate");
if (expr == NULL)
return -ENOMEM;
nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_DREG, NFT_REG_VERDICT);
nft_rule_expr_set_u32(expr, NFT_EXPR_IMM_VERDICT, verdict);
nft_rule_add_expr(r, expr);
return 0;
}
static void nft_rule_print_debug(struct nft_rule *r, struct nlmsghdr *nlh)
{
#ifdef NLDEBUG
char tmp[1024];
nft_rule_snprintf(tmp, sizeof(tmp), r, 0, 0);
printf("DEBUG: rule: %s", tmp);
mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
#endif
}
static int add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes)
{
struct nft_rule_expr *expr;
expr = nft_rule_expr_alloc("counter");
if (expr == NULL)
return -ENOMEM;
nft_rule_expr_set_u64(expr, NFT_EXPR_CTR_BYTES, packets);
nft_rule_expr_set_u64(expr, NFT_EXPR_CTR_PACKETS, bytes);
nft_rule_add_expr(r, expr);
return 0;
}
void add_compat(struct nft_rule *r, uint32_t proto, bool inv)
{
nft_rule_attr_set_u32(r, NFT_RULE_ATTR_COMPAT_PROTO, proto);
nft_rule_attr_set_u32(r, NFT_RULE_ATTR_COMPAT_FLAGS,
inv ? NFT_RULE_COMPAT_F_INV : 0);
}
static struct nft_rule *
nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
struct iptables_command_state *cs)
{
struct nft_rule *r;
int ret = 0, ip_flags = 0;
struct xtables_rule_match *matchp;
r = nft_rule_alloc();
if (r == NULL)
return NULL;
nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FAMILY, h->family);
nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table);
nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain);
ip_flags = h->ops->add(r, cs);
for (matchp = cs->matches; matchp; matchp = matchp->next) {
if (add_match(r, matchp->match->m) < 0)
goto err;
}
/* Counters need to me added before the target, otherwise they are
* increased for each rule because of the way nf_tables works.
*/
if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
goto err;
/* If no target at all, add nothing (default to continue) */
if (cs->target != NULL) {
/* Standard target? */
if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
ret = add_verdict(r, NF_ACCEPT);
else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
ret = add_verdict(r, NF_DROP);
else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
ret = add_verdict(r, NFT_RETURN);
else
ret = add_target(r, cs->target->t);
} else if (strlen(cs->jumpto) > 0) {
/* Not standard, then it's a go / jump to chain */
if (ip_flags & IPT_F_GOTO)
ret = add_jumpto(r, cs->jumpto, NFT_GOTO);
else
ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
}
if (ret < 0)
goto err;
return r;
err:
nft_rule_free(r);
return NULL;
}
int
nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
struct iptables_command_state *cs, uint64_t handle,
bool verbose)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nft_rule *r;
uint16_t flags = NLM_F_ACK|NLM_F_CREATE;
int ret = 1;
/* If built-in chains don't exist for this table, create them */
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
nft_chain_builtin_init(h, table, chain, NF_ACCEPT);
nft_fn = nft_rule_append;
r = nft_rule_new(h, chain, table, cs);
if (r == NULL) {
ret = 0;
goto err;
}
if (handle > 0) {
nft_rule_attr_set(r, NFT_RULE_ATTR_HANDLE, &handle);
flags |= NLM_F_REPLACE;
} else
flags |= NLM_F_APPEND;
if (h->commit) {
nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS,
NFT_RULE_F_COMMIT);
}
nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family,
flags, h->seq);
nft_rule_nlmsg_build_payload(nlh, r);
nft_rule_print_debug(r, nlh);
nft_rule_free(r);
ret = mnl_talk(h, nlh, NULL, NULL);
if (ret < 0)
perror("mnl_talk:nft_rule_append");
err:
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
static void nft_match_save(struct nft_rule_expr *expr)
{
const char *name;
const struct xtables_match *match;
struct xt_entry_match *emu;
const void *mtinfo;
size_t len;
name = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME);
match = xtables_find_match(name, XTF_TRY_LOAD, NULL);
if (match == NULL)
return;
mtinfo = nft_rule_expr_get(expr, NFT_EXPR_MT_INFO, &len);
if (mtinfo == NULL)
return;
emu = calloc(1, sizeof(struct xt_entry_match) + len);
if (emu == NULL)
return;
memcpy(&emu->data, mtinfo, len);
if (match->alias)
printf("-m %s", match->alias(emu));
else
printf("-m %s", match->name);
/* FIXME missing parameter */
if (match->save)
match->save(NULL, emu);
printf(" ");
free(emu);
}
static void nft_target_save(struct nft_rule_expr *expr)
{
const char *name;
const struct xtables_target *target;
struct xt_entry_target *emu;
const void *tginfo;
size_t len;
name = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME);
/* Standard target not supported, we use native immediate expression */
if (strcmp(name, "") == 0) {
printf("ERROR: standard target seen, should not happen\n");
return;
}
target = xtables_find_target(name, XTF_TRY_LOAD);
if (target == NULL)
return;
tginfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &len);
if (tginfo == NULL)
return;
emu = calloc(1, sizeof(struct xt_entry_match) + len);
if (emu == NULL)
return;
memcpy(emu->data, tginfo, len);
if (target->alias)
printf("-j %s", target->alias(emu));
else
printf("-j %s", target->name);
/* FIXME missing parameter */
if (target->save)
target->save(NULL, emu);
free(emu);
}
static void nft_immediate_save(struct nft_rule_expr *expr)
{
uint32_t verdict;
verdict = nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT);
switch(verdict) {
case NF_ACCEPT:
printf("-j ACCEPT");
break;
case NF_DROP:
printf("-j DROP");
break;
case NFT_RETURN:
printf("-j RETURN");
break;
case NFT_GOTO:
printf("-g %s",
nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN));
break;
case NFT_JUMP:
printf("-j %s",
nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN));
break;
}
}
static void
nft_print_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter)
{
uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY);
uint32_t value;
const char *name;
char ifname[IFNAMSIZ];
const char *ifname_ptr;
size_t len;
e = nft_rule_expr_iter_next(iter);
if (e == NULL)
return;
name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME);
/* meta should be followed by cmp */
if (strcmp(name, "cmp") != 0) {
DEBUGP("skipping no cmp after meta\n");
return;
}
switch(key) {
case NFT_META_IIF:
value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA);
if_indextoname(value, ifname);
switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) {
case NFT_CMP_EQ:
printf("-i %s ", ifname);
break;
case NFT_CMP_NEQ:
printf("! -i %s ", ifname);
break;
}
break;
case NFT_META_OIF:
value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA);
if_indextoname(value, ifname);
switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) {
case NFT_CMP_EQ:
printf("-o %s ", ifname);
break;
case NFT_CMP_NEQ:
printf("! -o %s ", ifname);
break;
}
break;
case NFT_META_IIFNAME:
ifname_ptr = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len);
memcpy(ifname, ifname_ptr, len);
ifname[len] = '\0';
/* if this is zero, then assume this is a interface mask */
if (if_nametoindex(ifname) == 0) {
ifname[len] = '+';
ifname[len+1] = '\0';
}
switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) {
case NFT_CMP_EQ:
printf("-i %s ", ifname);
break;
case NFT_CMP_NEQ:
printf("! -i %s ", ifname);
break;
}
break;
case NFT_META_OIFNAME:
ifname_ptr = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len);
memcpy(ifname, ifname_ptr, len);
ifname[len] = '\0';
/* if this is zero, then assume this is a interface mask */
if (if_nametoindex(ifname) == 0) {
ifname[len] = '+';
ifname[len+1] = '\0';
}
switch(nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP)) {
case NFT_CMP_EQ:
printf("-o %s ", ifname);
break;
case NFT_CMP_NEQ:
printf("! -o %s ", ifname);
break;
}
break;
default:
DEBUGP("unknown meta key %d\n", key);
break;
}
}
static void
nft_print_counters(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
bool counters)
{
if (counters) {
printf("-c %"PRIu64" %"PRIu64" ",
nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS),
nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES));
}
}
void
nft_rule_print_save(struct nft_rule *r, enum nft_rule_print type, bool counters)
{
struct nft_rule_expr_iter *iter;
struct nft_rule_expr *expr;
const char *chain = nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN);
/* print chain name */
switch(type) {
case NFT_RULE_APPEND:
printf("-A %s ", chain);
break;
case NFT_RULE_DEL:
printf("-D %s ", chain);
break;
}
iter = nft_rule_expr_iter_create(r);
if (iter == NULL)
return;
expr = nft_rule_expr_iter_next(iter);
while (expr != NULL) {
const char *name =
nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
if (strcmp(name, "counter") == 0) {
nft_print_counters(expr, iter, counters);
} else if (strcmp(name, "payload") == 0) {
struct nft_family_ops *ops = nft_family_ops_lookup(
nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY));
ops->print_payload(expr, iter);
} else if (strcmp(name, "meta") == 0) {
nft_print_meta(expr, iter);
} else if (strcmp(name, "match") == 0) {
nft_match_save(expr);
} else if (strcmp(name, "target") == 0) {
nft_target_save(expr);
} else if (strcmp(name, "immediate") == 0) {
nft_immediate_save(expr);
}
expr = nft_rule_expr_iter_next(iter);
}
printf("\n");
}
static int nft_chain_list_cb(const struct nlmsghdr *nlh, void *data)
{
struct nft_chain *c;
struct nft_chain_list *list = data;
c = nft_chain_alloc();
if (c == NULL) {
perror("OOM");
goto err;
}
if (nft_chain_nlmsg_parse(nlh, c) < 0) {
perror("nft_rule_nlmsg_parse");
goto out;
}
nft_chain_list_add_tail(c, list);
return MNL_CB_OK;
out:
nft_chain_free(c);
err:
return MNL_CB_OK;
}
static struct nft_chain_list *nft_chain_list_get(struct nft_handle *h)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
int ret;
struct nft_chain_list *list;
list = nft_chain_list_alloc();
if (list == NULL) {
errno = ENOMEM;
return NULL;
}
nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
NLM_F_DUMP, h->seq);
ret = mnl_talk(h, nlh, nft_chain_list_cb, list);
if (ret < 0)
perror("mnl_talk:nft_chain_list_get");
return list;
}
struct nft_chain_list *nft_chain_dump(struct nft_handle *h)
{
return nft_chain_list_get(h);
}
static const char *policy_name[NF_ACCEPT+1] = {
[NF_DROP] = "DROP",
[NF_ACCEPT] = "ACCEPT",
};
static void nft_chain_print_save(struct nft_chain *c, bool basechain)
{
const char *chain = nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
uint64_t pkts = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_PACKETS);
uint64_t bytes = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_BYTES);
/* print chain name */
if (basechain) {
uint32_t pol = NF_ACCEPT;
/* no default chain policy? don't crash, display accept */
if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_POLICY))
pol = nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_POLICY);
printf(":%s %s [%"PRIu64":%"PRIu64"]\n", chain, policy_name[pol],
pkts, bytes);
} else
printf(":%s - [%"PRIu64":%"PRIu64"]\n", chain, pkts, bytes);
}
int nft_chain_save(struct nft_handle *h, struct nft_chain_list *list,
const char *table)
{
struct nft_chain_list_iter *iter;
struct nft_chain *c;
iter = nft_chain_list_iter_create(list);
if (iter == NULL)
return 0;
c = nft_chain_list_iter_next(iter);
while (c != NULL) {
const char *chain_table =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
bool basechain = false;
if (strcmp(table, chain_table) != 0)
goto next;
basechain = nft_chain_builtin(c);
nft_chain_print_save(c, basechain);
next:
c = nft_chain_list_iter_next(iter);
}
nft_chain_list_iter_destroy(iter);
nft_chain_list_free(list);
return 1;
}
static int nft_rule_list_cb(const struct nlmsghdr *nlh, void *data)
{
struct nft_rule *r;
struct nft_rule_list *list = data;
r = nft_rule_alloc();
if (r == NULL) {
perror("OOM");
goto err;
}
if (nft_rule_nlmsg_parse(nlh, r) < 0) {
perror("nft_rule_nlmsg_parse");
goto out;
}
nft_rule_list_add_tail(r, list);
return MNL_CB_OK;
out:
nft_rule_free(r);
nft_rule_list_free(list);
err:
return MNL_CB_OK;
}
static struct nft_rule_list *nft_rule_list_get(struct nft_handle *h)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nft_rule_list *list;
int ret;
list = nft_rule_list_alloc();
if (list == NULL)
return 0;
nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family,
NLM_F_DUMP, h->seq);
ret = mnl_talk(h, nlh, nft_rule_list_cb, list);
if (ret < 0) {
perror("mnl_talk:nft_rule_save");
nft_rule_list_free(list);
return NULL;
}
return list;
}
int nft_rule_save(struct nft_handle *h, const char *table, bool counters)
{
struct nft_rule_list *list;
struct nft_rule_list_iter *iter;
struct nft_rule *r;
list = nft_rule_list_get(h);
if (list == NULL)
return 0;
iter = nft_rule_list_iter_create(list);
if (iter == NULL)
return 0;
r = nft_rule_list_iter_next(iter);
while (r != NULL) {
const char *rule_table =
nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE);
if (strcmp(table, rule_table) != 0)
goto next;
nft_rule_print_save(r, NFT_RULE_APPEND, counters);
next:
r = nft_rule_list_iter_next(iter);
}
nft_rule_list_iter_destroy(iter);
nft_rule_list_free(list);
/* the core expects 1 for success and 0 for error */
return 1;
}
static void
__nft_rule_flush(struct nft_handle *h, const char *table, const char *chain)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nft_rule *r;
r = nft_rule_alloc();
if (r == NULL)
return;
nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table);
nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain);
if (h->commit) {
nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS,
NFT_RULE_F_COMMIT);
}
/* Delete all rules in this table + chain */
nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, h->family,
NLM_F_ACK, h->seq);
nft_rule_nlmsg_build_payload(nlh, r);
nft_rule_free(r);
if (mnl_talk(h, nlh, NULL, NULL) < 0) {
if (errno != EEXIST)
perror("mnl_talk:__nft_rule_flush");
}
}
int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table)
{
int ret;
struct nft_chain_list *list;
struct nft_chain_list_iter *iter;
struct nft_chain *c;
nft_fn = nft_rule_flush;
list = nft_chain_list_get(h);
if (list == NULL) {
ret = 0;
goto err;
}
iter = nft_chain_list_iter_create(list);
if (iter == NULL)
goto err;
c = nft_chain_list_iter_next(iter);
while (c != NULL) {
const char *table_name =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
const char *chain_name =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
if (strcmp(table, table_name) != 0)
goto next;
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
__nft_rule_flush(h, table_name, chain_name);
if (chain != NULL)
break;
next:
c = nft_chain_list_iter_next(iter);
}
nft_chain_list_iter_destroy(iter);
err:
nft_chain_list_free(list);
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nft_chain *c;
int ret;
/* If built-in chains don't exist for this table, create them */
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
nft_chain_builtin_init(h, table, NULL, NF_ACCEPT);
c = nft_chain_alloc();
if (c == NULL)
return 0;
nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table);
nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain);
nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family,
NLM_F_ACK|NLM_F_EXCL, h->seq);
nft_chain_nlmsg_build_payload(nlh, c);
nft_chain_free(c);
ret = mnl_talk(h, nlh, NULL, NULL);
if (ret < 0) {
if (errno != EEXIST)
perror("mnl_talk:nft_chain_add");
}
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
static int __nft_chain_del(struct nft_handle *h, struct nft_chain *c)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
int ret;
nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, h->family,
NLM_F_ACK, h->seq);
nft_chain_nlmsg_build_payload(nlh, c);
ret = mnl_talk(h, nlh, NULL, NULL);
if (ret < 0) {
if (errno != EEXIST && errno != ENOENT)
perror("mnl_talk:__nft_chain_del");
}
return ret;
}
int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table)
{
struct nft_chain_list *list;
struct nft_chain_list_iter *iter;
struct nft_chain *c;
int ret = 0;
int deleted_ctr = 0;
list = nft_chain_list_get(h);
if (list == NULL)
goto err;
iter = nft_chain_list_iter_create(list);
if (iter == NULL)
goto err;
c = nft_chain_list_iter_next(iter);
while (c != NULL) {
const char *table_name =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
const char *chain_name =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
/* don't delete built-in chain */
if (nft_chain_builtin(c))
goto next;
if (strcmp(table, table_name) != 0)
goto next;
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
ret = __nft_chain_del(h, c);
if (ret < 0)
break;
deleted_ctr++;
if (chain != NULL)
break;
next:
c = nft_chain_list_iter_next(iter);
}
nft_chain_list_iter_destroy(iter);
err:
nft_chain_list_free(list);
/* chain not found */
if (ret < 0 && deleted_ctr == 0)
errno = ENOENT;
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
struct nft_chain *
nft_chain_list_find(struct nft_chain_list *list,
const char *table, const char *chain)
{
struct nft_chain_list_iter *iter;
struct nft_chain *c;
iter = nft_chain_list_iter_create(list);
if (iter == NULL)
return NULL;
c = nft_chain_list_iter_next(iter);
while (c != NULL) {
const char *table_name =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
const char *chain_name =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
if (strcmp(table, table_name) != 0)
goto next;
if (strcmp(chain, chain_name) != 0)
goto next;
nft_chain_list_iter_destroy(iter);
return c;
next:
c = nft_chain_list_iter_next(iter);
}
nft_chain_list_iter_destroy(iter);
return NULL;
}
static struct nft_chain *
nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
{
struct nft_chain_list *list;
list = nft_chain_list_get(h);
if (list == NULL)
return NULL;
return nft_chain_list_find(list, table, chain);
}
int nft_chain_user_rename(struct nft_handle *h,const char *chain,
const char *table, const char *newname)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nft_chain *c;
uint64_t handle;
int ret;
/* If built-in chains don't exist for this table, create them */
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
nft_chain_builtin_init(h, table, NULL, NF_ACCEPT);
/* Find the old chain to be renamed */
c = nft_chain_find(h, table, chain);
if (c == NULL) {
errno = ENOENT;
return -1;
}
handle = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_HANDLE);
/* Now prepare the new name for the chain */
c = nft_chain_alloc();
if (c == NULL)
return -1;
nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table);
nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)newname);
nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_HANDLE, handle);
nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family,
NLM_F_ACK, h->seq);
nft_chain_nlmsg_build_payload(nlh, c);
nft_chain_free(c);
ret = mnl_talk(h, nlh, NULL, NULL);
if (ret < 0) {
if (errno != EEXIST)
perror("mnl_talk:nft_chain_rename");
}
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
static int nft_table_list_cb(const struct nlmsghdr *nlh, void *data)
{
struct nft_table *t;
struct nft_table_list *list = data;
t = nft_table_alloc();
if (t == NULL) {
perror("OOM");
goto err;
}
if (nft_table_nlmsg_parse(nlh, t) < 0) {
perror("nft_rule_nlmsg_parse");
goto out;
}
nft_table_list_add_tail(t, list);
return MNL_CB_OK;
out:
nft_table_free(t);
err:
return MNL_CB_OK;
}
static struct nft_table_list *nft_table_list_get(struct nft_handle *h)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
int ret;
struct nft_table_list *list;
list = nft_table_list_alloc();
if (list == NULL)
return 0;
nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family,
NLM_F_DUMP, h->seq);
ret = mnl_talk(h, nlh, nft_table_list_cb, list);
if (ret < 0)
perror("mnl_talk:nft_table_list_get");
return list;
}
bool nft_table_find(struct nft_handle *h, const char *tablename)
{
struct nft_table_list *list;
struct nft_table_list_iter *iter;
struct nft_table *t;
bool ret = false;
list = nft_table_list_get(h);
if (list == NULL)
goto err;
iter = nft_table_list_iter_create(list);
if (iter == NULL)
goto err;
t = nft_table_list_iter_next(iter);
while (t != NULL) {
const char *this_tablename =
nft_table_attr_get(t, NFT_TABLE_ATTR_NAME);
if (strcmp(tablename, this_tablename) == 0)
return true;
t = nft_table_list_iter_next(iter);
}
nft_table_list_free(list);
err:
return ret;
}
int nft_for_each_table(struct nft_handle *h,
int (*func)(struct nft_handle *h, const char *tablename, bool counters),
bool counters)
{
int ret = 1;
struct nft_table_list *list;
struct nft_table_list_iter *iter;
struct nft_table *t;
list = nft_table_list_get(h);
if (list == NULL) {
ret = 0;
goto err;
}
iter = nft_table_list_iter_create(list);
if (iter == NULL)
return 0;
t = nft_table_list_iter_next(iter);
while (t != NULL) {
const char *tablename =
nft_table_attr_get(t, NFT_TABLE_ATTR_NAME);
func(h, tablename, counters);
t = nft_table_list_iter_next(iter);
}
nft_table_list_free(list);
err:
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
int nft_table_purge_chains(struct nft_handle *h, const char *this_table,
struct nft_chain_list *chain_list)
{
struct nft_chain_list_iter *iter;
struct nft_chain *chain_obj;
iter = nft_chain_list_iter_create(chain_list);
if (iter == NULL)
return 0;
chain_obj = nft_chain_list_iter_next(iter);
while (chain_obj != NULL) {
const char *table =
nft_chain_attr_get_str(chain_obj, NFT_CHAIN_ATTR_TABLE);
if (strcmp(this_table, table) != 0)
goto next;
if (nft_chain_builtin(chain_obj))
goto next;
if ( __nft_chain_del(h, chain_obj) < 0) {
if (errno != EBUSY)
return -1;
}
next:
chain_obj = nft_chain_list_iter_next(iter);
}
nft_chain_list_iter_destroy(iter);
return 0;
}
static inline int
match_different(const struct xt_entry_match *a,
const unsigned char *a_elems,
const unsigned char *b_elems,
unsigned char **maskptr)
{
const struct xt_entry_match *b;
unsigned int i;
/* Offset of b is the same as a. */
b = (void *)b_elems + ((unsigned char *)a - a_elems);
if (a->u.match_size != b->u.match_size)
return 1;
if (strcmp(a->u.user.name, b->u.user.name) != 0)
return 1;
*maskptr += XT_ALIGN(sizeof(*a));
for (i = 0; i < a->u.match_size - XT_ALIGN(sizeof(*a)); i++)
if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
return 1;
*maskptr += i;
return 0;
}
static void
nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
int family, struct iptables_command_state *cs)
{
uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY);
struct nft_family_ops *ops = nft_family_ops_lookup(family);
const char *name;
e = nft_rule_expr_iter_next(iter);
if (e == NULL)
return;
name = nft_rule_expr_get_str(e, NFT_RULE_EXPR_ATTR_NAME);
if (strcmp(name, "cmp") != 0) {
DEBUGP("skipping no cmp after meta\n");
return;
}
ops->parse_meta(e, key, cs);
}
static void
nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
int family, struct iptables_command_state *cs)
{
struct nft_family_ops *ops = nft_family_ops_lookup(family);
uint32_t offset;
offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET);
ops->parse_payload(iter, cs, offset);
}
static void
nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
struct xt_counters *counters)
{
counters->pcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_PACKETS);
counters->bcnt = nft_rule_expr_get_u64(e, NFT_EXPR_CTR_BYTES);
}
static void
nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter,
int family, struct iptables_command_state *cs)
{
int verdict = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT);
const char *chain = nft_rule_expr_get_str(e, NFT_EXPR_IMM_CHAIN);
struct nft_family_ops *ops;
/* Standard target? */
switch(verdict) {
case NF_ACCEPT:
cs->jumpto = "ACCEPT";
return;
case NF_DROP:
cs->jumpto = "DROP";
return;
case NFT_RETURN:
cs->jumpto = "RETURN";
return;
case NFT_GOTO:
ops = nft_family_ops_lookup(family);
ops->parse_immediate(cs);
case NFT_JUMP:
cs->jumpto = chain;
return;
}
}
static void
nft_rule_to_iptables_command_state(struct nft_rule *r,
struct iptables_command_state *cs)
{
struct nft_rule_expr_iter *iter;
struct nft_rule_expr *expr;
int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY);
iter = nft_rule_expr_iter_create(r);
if (iter == NULL)
return;
expr = nft_rule_expr_iter_next(iter);
while (expr != NULL) {
const char *name =
nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
if (strcmp(name, "counter") == 0) {
nft_parse_counter(expr, iter, &cs->counters);
} else if (strcmp(name, "payload") == 0) {
nft_parse_payload(expr, iter, family, cs);
} else if (strcmp(name, "meta") == 0) {
nft_parse_meta(expr, iter, family, cs);
} else if (strcmp(name, "immediate") == 0) {
nft_parse_immediate(expr, iter, family, cs);
}
expr = nft_rule_expr_iter_next(iter);
}
nft_rule_expr_iter_destroy(iter);
}
static int matches_howmany(struct xtables_rule_match *matches)
{
struct xtables_rule_match *matchp;
int matches_ctr = 0;
for (matchp = matches; matchp; matchp = matchp->next)
matches_ctr++;
return matches_ctr;
}
static bool
__find_match(struct nft_rule_expr *expr, struct xtables_rule_match *matches)
{
const char *matchname = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME);
/* Netlink aligns this match info, don't trust this length variable */
const char *data = nft_rule_expr_get_str(expr, NFT_EXPR_MT_INFO);
struct xtables_rule_match *matchp;
bool found = false;
for (matchp = matches; matchp; matchp = matchp->next) {
struct xt_entry_match *m = matchp->match->m;
if (strcmp(m->u.user.name, matchname) != 0) {
DEBUGP("mismatching match name\n");
continue;
}
if (memcmp(data, m->data, m->u.user.match_size - sizeof(*m)) != 0) {
DEBUGP("mismatch match data\n");
continue;
}
found = true;
break;
}
return found;
}
static bool find_matches(struct xtables_rule_match *matches, struct nft_rule *r)
{
struct nft_rule_expr_iter *iter;
struct nft_rule_expr *expr;
int kernel_matches = 0;
iter = nft_rule_expr_iter_create(r);
if (iter == NULL)
return false;
expr = nft_rule_expr_iter_next(iter);
while (expr != NULL) {
const char *name =
nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
if (strcmp(name, "match") == 0) {
if (!__find_match(expr, matches))
return false;
kernel_matches++;
}
expr = nft_rule_expr_iter_next(iter);
}
nft_rule_expr_iter_destroy(iter);
/* same number of matches? */
if (matches_howmany(matches) != kernel_matches)
return false;
return true;
}
static bool __find_target(struct nft_rule_expr *expr, struct xt_entry_target *t)
{
size_t len;
const char *tgname = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME);
/* Netlink aligns this target info, don't trust this length variable */
const char *data = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &len);
if (strcmp(t->u.user.name, tgname) != 0) {
DEBUGP("mismatching target name\n");
return false;
}
if (memcmp(data, t->data, t->u.user.target_size - sizeof(*t)) != 0)
return false;
return true;
}
static int targets_howmany(struct xtables_target *target)
{
return target != NULL ? 1 : 0;
}
static bool
find_target(struct xtables_target *target, struct nft_rule *r)
{
struct nft_rule_expr_iter *iter;
struct nft_rule_expr *expr;
int kernel_targets = 0;
/* Special case: we use native immediate expressions to emulated
* standard targets. Also, we don't want to crash with no targets.
*/
if (target == NULL || strcmp(target->name, "standard") == 0)
return true;
iter = nft_rule_expr_iter_create(r);
if (iter == NULL)
return false;
expr = nft_rule_expr_iter_next(iter);
while (expr != NULL) {
const char *name =
nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
if (strcmp(name, "target") == 0) {
/* we may support several targets in the future */
if (!__find_target(expr, target->t))
return false;
kernel_targets++;
}
expr = nft_rule_expr_iter_next(iter);
}
nft_rule_expr_iter_destroy(iter);
/* same number of targets? */
if (targets_howmany(target) != kernel_targets) {
DEBUGP("kernel targets is %d but we passed %d\n",
kernel_targets, targets_howmany(target));
return false;
}
return true;
}
static bool
find_immediate(struct nft_rule *r, const char *jumpto)
{
struct nft_rule_expr_iter *iter;
struct nft_rule_expr *expr;
iter = nft_rule_expr_iter_create(r);
if (iter == NULL)
return false;
expr = nft_rule_expr_iter_next(iter);
while (expr != NULL) {
const char *name =
nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
if (strcmp(name, "immediate") == 0) {
int verdict = nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT);
const char *verdict_name = NULL;
/* No target specified but immediate shows up, this
* is not the rule we are looking for.
*/
if (strlen(jumpto) == 0)
return false;
switch(verdict) {
case NF_ACCEPT:
verdict_name = "ACCEPT";
break;
case NF_DROP:
verdict_name = "DROP";
break;
case NFT_RETURN:
verdict_name = "RETURN";
break;
}
/* Standard target? */
if (verdict_name && strcmp(jumpto, verdict_name) != 0)
return false;
}
expr = nft_rule_expr_iter_next(iter);
}
nft_rule_expr_iter_destroy(iter);
return true;
}
static void
__nft_rule_del(struct nft_handle *h, struct nft_rule *r)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
int ret;
nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, h->family,
NLM_F_ACK, h->seq);
nft_rule_nlmsg_build_payload(nlh, r);
nft_rule_print_debug(r, nlh);
ret = mnl_talk(h, nlh, NULL, NULL);
if (ret < 0)
perror("mnl_talk:nft_rule_del");
}
static struct nft_rule_list *nft_rule_list_create(struct nft_handle *h)
{
return nft_rule_list_get(h);
}
static void nft_rule_list_destroy(struct nft_rule_list *list)
{
nft_rule_list_free(list);
}
static struct nft_rule *
nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table,
struct iptables_command_state *cs, int rulenum)
{
struct nft_rule *r;
struct nft_rule_list_iter *iter;
int rule_ctr = 0;
bool found = false;
iter = nft_rule_list_iter_create(list);
if (iter == NULL)
return 0;
r = nft_rule_list_iter_next(iter);
while (r != NULL) {
const char *rule_table =
nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE);
const char *rule_chain =
nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN);
const struct nft_family_ops *ops = nft_family_ops_lookup(
nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY));
struct iptables_command_state this = {};
if (strcmp(table, rule_table) != 0 ||
strcmp(chain, rule_chain) != 0) {
DEBUGP("different chain / table\n");
goto next;
}
if (rulenum >= 0) {
/* Delete by rule number case */
if (rule_ctr != rulenum)
goto next;
found = true;
break;
} else {
/* Delete by matching rule case */
DEBUGP("comparing with... ");
#ifdef DEBUG_DEL
nft_rule_print_save(r, NFT_RULE_APPEND, 0);
#endif
nft_rule_to_iptables_command_state(r, &this);
if (!ops->is_same(cs, &this))
goto next;
if (!find_matches(cs->matches, r)) {
DEBUGP("matches not found\n");
goto next;
}
if (!find_target(cs->target, r)) {
DEBUGP("target not found\n");
goto next;
}
if (!find_immediate(r, cs->jumpto)) {
DEBUGP("immediate not found\n");
goto next;
}
found = true;
break;
}
next:
rule_ctr++;
r = nft_rule_list_iter_next(iter);
}
nft_rule_list_iter_destroy(iter);
return found ? r : NULL;
}
int nft_rule_check(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *e,
bool verbose)
{
struct nft_rule_list *list;
int ret;
nft_fn = nft_rule_check;
list = nft_rule_list_create(h);
if (list == NULL)
return 0;
ret = nft_rule_find(list, chain, table, e, -1) ? 1 : 0;
if (ret == 0)
errno = ENOENT;
nft_rule_list_destroy(list);
return ret;
}
int nft_rule_delete(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *cs,
bool verbose)
{
int ret = 0;
struct nft_rule *r;
struct nft_rule_list *list;
nft_fn = nft_rule_delete;
list = nft_rule_list_create(h);
if (list == NULL)
return 0;
r = nft_rule_find(list, chain, table, cs, -1);
if (r != NULL) {
ret = 1;
if (h->commit) {
nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS,
NFT_RULE_F_COMMIT);
}
DEBUGP("deleting rule\n");
__nft_rule_del(h, r);
} else
errno = ENOENT;
nft_rule_list_destroy(list);
return ret;
}
static int
nft_rule_add(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *cs,
uint64_t handle, bool verbose)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct nft_rule *r;
int ret = 1;
r = nft_rule_new(h, chain, table, cs);
if (r == NULL) {
ret = 0;
goto err;
}
nft_rule_attr_set_u64(r, NFT_RULE_ATTR_POSITION, handle);
if (h->commit) {
nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS,
NFT_RULE_F_COMMIT);
}
nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, h->family,
NLM_F_ACK|NLM_F_CREATE, h->seq);
nft_rule_nlmsg_build_payload(nlh, r);
nft_rule_print_debug(r, nlh);
nft_rule_free(r);
ret = mnl_talk(h, nlh, NULL, NULL);
if (ret < 0)
perror("mnl_talk:nft_rule_add_num");
err:
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
int nft_rule_insert(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *cs,
int rulenum, bool verbose)
{
struct nft_rule_list *list;
struct nft_rule *r;
uint64_t handle;
/* If built-in chains don't exist for this table, create them */
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
nft_chain_builtin_init(h, table, chain, NF_ACCEPT);
nft_fn = nft_rule_insert;
list = nft_rule_list_create(h);
if (list == NULL)
goto err;
r = nft_rule_find(list, chain, table, cs, rulenum);
if (r == NULL) {
errno = ENOENT;
goto err;
}
handle = nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE);
DEBUGP("adding after rule handle %"PRIu64"\n", handle);
if (h->commit) {
nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS,
NFT_RULE_F_COMMIT);
}
nft_rule_list_destroy(list);
return nft_rule_add(h, chain, table, cs, handle, verbose);
err:
nft_rule_list_destroy(list);
return 0;
}
int nft_rule_delete_num(struct nft_handle *h, const char *chain,
const char *table, int rulenum, bool verbose)
{
int ret = 0;
struct nft_rule *r;
struct nft_rule_list *list;
nft_fn = nft_rule_delete_num;
list = nft_rule_list_create(h);
if (list == NULL)
return 0;
r = nft_rule_find(list, chain, table, NULL, rulenum);
if (r != NULL) {
ret = 1;
if (h->commit) {
nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS,
NFT_RULE_F_COMMIT);
}
DEBUGP("deleting rule by number %d\n", rulenum);
__nft_rule_del(h, r);
} else
errno = ENOENT;
nft_rule_list_destroy(list);
return ret;
}
int nft_rule_replace(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *cs,
int rulenum, bool verbose)
{
int ret = 0;
struct nft_rule *r;
struct nft_rule_list *list;
nft_fn = nft_rule_replace;
list = nft_rule_list_create(h);
if (list == NULL)
return 0;
r = nft_rule_find(list, chain, table, cs, rulenum);
if (r != NULL) {
DEBUGP("replacing rule with handle=%llu\n",
(unsigned long long)
nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE));
if (h->commit) {
nft_rule_attr_set_u32(r, NFT_RULE_ATTR_FLAGS,
NFT_RULE_F_COMMIT);
}
ret = nft_rule_append(h, chain, table, cs,
nft_rule_attr_get_u64(r, NFT_RULE_ATTR_HANDLE),
verbose);
} else
errno = ENOENT;
nft_rule_list_destroy(list);
return ret;
}
static void
print_header(unsigned int format, const char *chain, const char *pol,
const struct xt_counters *counters, bool basechain, uint32_t refs)
{
printf("Chain %s", chain);
if (basechain) {
printf(" (policy %s", pol);
if (!(format & FMT_NOCOUNTS)) {
fputc(' ', stdout);
print_num(counters->pcnt, (format|FMT_NOTABLE));
fputs("packets, ", stdout);
print_num(counters->bcnt, (format|FMT_NOTABLE));
fputs("bytes", stdout);
}
printf(")\n");
} else {
printf(" (%u references)\n", refs);
}
if (format & FMT_LINENUMBERS)
printf(FMT("%-4s ", "%s "), "num");
if (!(format & FMT_NOCOUNTS)) {
if (format & FMT_KILOMEGAGIGA) {
printf(FMT("%5s ","%s "), "pkts");
printf(FMT("%5s ","%s "), "bytes");
} else {
printf(FMT("%8s ","%s "), "pkts");
printf(FMT("%10s ","%s "), "bytes");
}
}
if (!(format & FMT_NOTARGET))
printf(FMT("%-9s ","%s "), "target");
fputs(" prot ", stdout);
if (format & FMT_OPTIONS)
fputs("opt", stdout);
if (format & FMT_VIA) {
printf(FMT(" %-6s ","%s "), "in");
printf(FMT("%-6s ","%s "), "out");
}
printf(FMT(" %-19s ","%s "), "source");
printf(FMT(" %-19s "," %s "), "destination");
printf("\n");
}
static void
print_match(struct nft_rule_expr *expr, int numeric)
{
size_t len;
const char *match_name = nft_rule_expr_get_str(expr, NFT_EXPR_MT_NAME);
const void *match_info = nft_rule_expr_get(expr, NFT_EXPR_MT_INFO, &len);
const struct xtables_match *match =
xtables_find_match(match_name, XTF_TRY_LOAD, NULL);
struct xt_entry_match *m =
calloc(1, sizeof(struct xt_entry_match) + len);
/* emulate struct xt_entry_match since ->print needs it */
memcpy((void *)&m->data, match_info, len);
if (match) {
if (match->print)
/* FIXME missing first parameter */
match->print(NULL, m, numeric);
else
printf("%s ", match_name);
} else {
if (match_name[0])
printf("UNKNOWN match `%s' ", match_name);
}
free(m);
}
static void
print_firewall(const struct iptables_command_state *cs, struct nft_rule *r,
unsigned int num, unsigned int format)
{
const struct xtables_target *target = NULL;
const char *targname = NULL;
const void *targinfo = NULL;
int family;
struct nft_family_ops *ops;
uint8_t flags = 0;
struct nft_rule_expr_iter *iter;
struct nft_rule_expr *expr;
struct xt_entry_target *t;
size_t target_len = 0;
iter = nft_rule_expr_iter_create(r);
if (iter == NULL)
return;
expr = nft_rule_expr_iter_next(iter);
while (expr != NULL) {
const char *name =
nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
if (strcmp(name, "target") == 0) {
targname = nft_rule_expr_get_str(expr,
NFT_EXPR_TG_NAME);
targinfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO,
&target_len);
break;
} else if (strcmp(name, "immediate") == 0) {
uint32_t verdict =
nft_rule_expr_get_u32(expr, NFT_EXPR_IMM_VERDICT);
switch(verdict) {
case NF_ACCEPT:
targname = "ACCEPT";
break;
case NF_DROP:
targname = "DROP";
break;
case NFT_RETURN:
targname = "RETURN";
break;
case NFT_GOTO:
targname = nft_rule_expr_get_str(expr,
NFT_EXPR_IMM_CHAIN);
break;
case NFT_JUMP:
targname = nft_rule_expr_get_str(expr,
NFT_EXPR_IMM_CHAIN);
break;
}
}
expr = nft_rule_expr_iter_next(iter);
}
nft_rule_expr_iter_destroy(iter);
family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY);
ops = nft_family_ops_lookup(family);
flags = ops->print_firewall(cs, targname, num, format);
if (format & FMT_NOTABLE)
fputs(" ", stdout);
#ifdef IPT_F_GOTO
if(flags & IPT_F_GOTO)
printf("[goto] ");
#endif
iter = nft_rule_expr_iter_create(r);
if (iter == NULL)
return;
expr = nft_rule_expr_iter_next(iter);
while (expr != NULL) {
const char *name =
nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
if (strcmp(name, "match") == 0)
print_match(expr, format & FMT_NUMERIC);
expr = nft_rule_expr_iter_next(iter);
}
nft_rule_expr_iter_destroy(iter);
t = calloc(1, sizeof(struct xt_entry_target) + target_len);
if (t == NULL)
return;
/* emulate struct xt_entry_match since ->print needs it */
memcpy((void *)&t->data, targinfo, target_len);
if (targname) {
target = xtables_find_target(targname, XTF_TRY_LOAD);
if (target) {
if (target->print)
/* FIXME missing first parameter */
target->print(NULL, t, format & FMT_NUMERIC);
} else
printf("[%ld bytes of unknown target data] ",
target_len);
}
free(t);
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
}
static int
__nft_rule_list(struct nft_handle *h, struct nft_chain *c, const char *table,
int rulenum, unsigned int format,
void (*cb)(const struct iptables_command_state *cs,
struct nft_rule *r, unsigned int num,
unsigned int format))
{
struct nft_rule_list *list;
struct nft_rule_list_iter *iter;
struct nft_rule *r;
int rule_ctr = 0, ret = 0;
const char *chain = nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
list = nft_rule_list_get(h);
if (list == NULL)
return 0;
iter = nft_rule_list_iter_create(list);
if (iter == NULL)
goto err;
r = nft_rule_list_iter_next(iter);
while (r != NULL) {
const char *rule_table =
nft_rule_attr_get_str(r, NFT_RULE_ATTR_TABLE);
const char *rule_chain =
nft_rule_attr_get_str(r, NFT_RULE_ATTR_CHAIN);
rule_ctr++;
if (strcmp(table, rule_table) != 0 ||
strcmp(chain, rule_chain) != 0)
goto next;
if (rulenum > 0 && rule_ctr != rulenum) {
/* List by rule number case */
goto next;
}
struct iptables_command_state cs = {};
/* Show all rules case */
nft_rule_to_iptables_command_state(r, &cs);
cb(&cs, r, rule_ctr, format);
if (rulenum > 0 && rule_ctr == rulenum) {
ret = 1;
break;
}
next:
r = nft_rule_list_iter_next(iter);
}
nft_rule_list_iter_destroy(iter);
err:
nft_rule_list_free(list);
if (ret == 0)
errno = ENOENT;
return ret;
}
int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
int rulenum, unsigned int format)
{
struct nft_chain_list *list;
struct nft_chain_list_iter *iter;
struct nft_chain *c;
bool found = false;
/* If built-in chains don't exist for this table, create them */
if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
nft_chain_builtin_init(h, table, NULL, NF_ACCEPT);
list = nft_chain_dump(h);
iter = nft_chain_list_iter_create(list);
if (iter == NULL)
goto err;
c = nft_chain_list_iter_next(iter);
while (c != NULL) {
const char *chain_table =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
const char *chain_name =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
uint32_t policy =
nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_POLICY);
uint32_t refs =
nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_USE);
struct xt_counters ctrs = {
.pcnt = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_PACKETS),
.bcnt = nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_BYTES),
};
bool basechain = false;
if (nft_chain_attr_get(c, NFT_CHAIN_ATTR_HOOKNUM))
basechain = true;
if (strcmp(table, chain_table) != 0)
goto next;
if (chain && strcmp(chain, chain_name) != 0)
goto next;
if (found)
printf("\n");
if (!rulenum) {
print_header(format, chain_name, policy_name[policy],
&ctrs, basechain, refs);
}
__nft_rule_list(h, c, table, rulenum, format, print_firewall);
/* we printed the chain we wanted, stop processing. */
if (chain)
break;
found = true;
next:
c = nft_chain_list_iter_next(iter);
}
nft_chain_list_iter_destroy(iter);
err:
nft_chain_list_free(list);
return 1;
}
static void
list_save(const struct iptables_command_state *cs, struct nft_rule *r,
unsigned int num, unsigned int format)
{
nft_rule_print_save(r, NFT_RULE_APPEND, !(format & FMT_NOCOUNTS));
}
static int
nft_rule_list_chain_save(struct nft_handle *h, const char *chain,
const char *table, struct nft_chain_list *list,
int counters)
{
struct nft_chain_list_iter *iter;
struct nft_chain *c;
iter = nft_chain_list_iter_create(list);
if (iter == NULL)
return 0;
c = nft_chain_list_iter_next(iter);
while (c != NULL) {
const char *chain_table =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
const char *chain_name =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
uint32_t policy =
nft_chain_attr_get_u32(c, NFT_CHAIN_ATTR_POLICY);
if (strcmp(table, chain_table) != 0 ||
(chain && strcmp(chain, chain_name) != 0))
goto next;
/* this is a base chain */
if (nft_chain_builtin(c)) {
printf("-P %s %s", chain_name, policy_name[policy]);
if (counters) {
printf(" -c %"PRIu64" %"PRIu64"\n",
nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_PACKETS),
nft_chain_attr_get_u64(c, NFT_CHAIN_ATTR_BYTES));
} else
printf("\n");
} else {
printf("-N %s\n", chain_name);
}
next:
c = nft_chain_list_iter_next(iter);
}
nft_chain_list_iter_destroy(iter);
return 1;
}
int nft_rule_list_save(struct nft_handle *h, const char *chain,
const char *table, int rulenum, int counters)
{
struct nft_chain_list *list;
struct nft_chain_list_iter *iter;
struct nft_chain *c;
int ret = 1;
list = nft_chain_dump(h);
/* Dump policies and custom chains first */
if (!rulenum)
nft_rule_list_chain_save(h, chain, table, list, counters);
/* Now dump out rules in this table */
iter = nft_chain_list_iter_create(list);
if (iter == NULL)
goto err;
c = nft_chain_list_iter_next(iter);
while (c != NULL) {
const char *chain_table =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_TABLE);
const char *chain_name =
nft_chain_attr_get_str(c, NFT_CHAIN_ATTR_NAME);
if (strcmp(table, chain_table) != 0)
goto next;
if (chain && strcmp(chain, chain_name) != 0)
goto next;
ret = __nft_rule_list(h, c, table, rulenum,
counters ? 0 : FMT_NOCOUNTS, list_save);
/* we printed the chain we wanted, stop processing. */
if (chain)
break;
next:
c = nft_chain_list_iter_next(iter);
}
nft_chain_list_iter_destroy(iter);
err:
nft_chain_list_free(list);
return ret;
}
static int nft_action(struct nft_handle *h, int type)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t seq;
int ret;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES<< 8) | type;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = seq = time(NULL);
struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
nfg->nfgen_family = h->family;
nfg->version = NFNETLINK_V0;
nfg->res_id = 0;
ret = mnl_talk(h, nlh, NULL, NULL);
if (ret < 0) {
if (errno != EEXIST)
perror("mnl-talk:nft_commit");
}
return ret == 0 ? 1 : 0;
}
int nft_commit(struct nft_handle *h)
{
return nft_action(h, NFT_MSG_COMMIT);
}
int nft_abort(struct nft_handle *h)
{
return nft_action(h, NFT_MSG_ABORT);
}
int nft_compatible_revision(const char *name, uint8_t rev, int opt)
{
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq, type;
int ret = 0;
if (opt == IPT_SO_GET_REVISION_MATCH)
type = 0;
else
type = 1;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = (NFNL_SUBSYS_NFT_COMPAT << 8) | NFNL_MSG_COMPAT_GET;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = seq = time(NULL);
struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
nfg->nfgen_family = AF_INET;
nfg->version = NFNETLINK_V0;
nfg->res_id = 0;
mnl_attr_put_strz(nlh, NFTA_COMPAT_NAME, name);
mnl_attr_put_u32(nlh, NFTA_COMPAT_REV, htonl(rev));
mnl_attr_put_u32(nlh, NFTA_COMPAT_TYPE, htonl(type));
DEBUGP("requesting `%s' rev=%d type=%d via nft_compat\n",
name, rev, type);
nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL) {
perror("mnl_socket_open");
return 0;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
goto err;
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_send");
goto err;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("mnl_socket_recvfrom");
goto err;
}
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
if (ret == -1) {
perror("mnl_cb_run");
goto err;
}
err:
mnl_socket_close(nl);
return ret < 0 ? 0 : 1;
}
/* Translates errno numbers into more human-readable form than strerror. */
const char *nft_strerror(int err)
{
unsigned int i;
static struct table_struct {
void *fn;
int err;
const char *message;
} table[] =
{
{ nft_chain_user_del, ENOTEMPTY, "Chain is not empty" },
{ nft_chain_user_del, EINVAL, "Can't delete built-in chain" },
{ nft_chain_user_del, EMLINK,
"Can't delete chain with references left" },
{ nft_chain_user_add, EEXIST, "Chain already exists" },
{ nft_rule_add, E2BIG, "Index of insertion too big" },
{ nft_rule_replace, E2BIG, "Index of replacement too big" },
{ nft_rule_delete_num, E2BIG, "Index of deletion too big" },
/* { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
{ TC_ZERO_COUNTER, E2BIG, "Index of counter too big" }, */
{ nft_rule_add, ELOOP, "Loop found in table" },
{ nft_rule_add, EINVAL, "Target problem" },
/* ENOENT for DELETE probably means no matching rule */
{ nft_rule_delete, ENOENT,
"Bad rule (does a matching rule exist in that chain?)" },
{ nft_chain_set, ENOENT, "Bad built-in chain name" },
{ nft_chain_set, EINVAL, "Bad policy name" },
{ NULL, EPERM, "Permission denied (you must be root)" },
{ NULL, 0, "Incompatible with this kernel" },
{ NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
{ NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
{ NULL, ENOMEM, "Memory allocation problem" },
{ NULL, ENOENT, "No chain/target/match by that name" },
};
for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
if ((!table[i].fn || table[i].fn == nft_fn)
&& table[i].err == err)
return table[i].message;
}
return strerror(err);
}
static void xtables_config_perror(uint32_t flags, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (flags & NFT_LOAD_VERBOSE)
vfprintf(stderr, fmt, args);
va_end(args);
}
int nft_xtables_config_load(struct nft_handle *h, const char *filename,
uint32_t flags)
{
struct nft_table_list *table_list = nft_table_list_alloc();
struct nft_chain_list *chain_list = nft_chain_list_alloc();
struct nft_table_list_iter *titer;
struct nft_chain_list_iter *citer;
struct nft_table *table;
struct nft_chain *chain;
if (xtables_config_parse(filename, table_list, chain_list) < 0) {
if (errno == ENOENT) {
xtables_config_perror(flags,
"configuration file `%s' does not exists\n",
filename);
} else {
xtables_config_perror(flags,
"Fatal error parsing config file: %s\n",
strerror(errno));
}
return -1;
}
/* Stage 1) create tables */
titer = nft_table_list_iter_create(table_list);
while ((table = nft_table_list_iter_next(titer)) != NULL) {
if (nft_table_add(h, table) < 0) {
if (errno == EEXIST) {
xtables_config_perror(flags,
"table `%s' already exists, skipping\n",
(char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME));
} else {
xtables_config_perror(flags,
"table `%s' cannot be create, reason `%s'. Exitting\n",
(char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME),
strerror(errno));
nft_table_list_iter_destroy(titer);
nft_table_list_free(table_list);
return -1;
}
continue;
}
xtables_config_perror(flags, "table `%s' has been created\n",
(char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME));
}
nft_table_list_iter_destroy(titer);
nft_table_list_free(table_list);
/* Stage 2) create chains */
citer = nft_chain_list_iter_create(chain_list);
while ((chain = nft_chain_list_iter_next(citer)) != NULL) {
if (nft_chain_add(h, chain) < 0) {
if (errno == EEXIST) {
xtables_config_perror(flags,
"chain `%s' already exists in table `%s', skipping\n",
(char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME),
(char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_TABLE));
} else {
xtables_config_perror(flags,
"chain `%s' cannot be create, reason `%s'. Exitting\n",
(char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME),
strerror(errno));
nft_chain_list_iter_destroy(citer);
nft_chain_list_free(chain_list);
return -1;
}
continue;
}
xtables_config_perror(flags,
"chain `%s' in table `%s' has been created\n",
(char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_NAME),
(char *)nft_chain_attr_get(chain, NFT_CHAIN_ATTR_TABLE));
}
nft_chain_list_iter_destroy(citer);
nft_chain_list_free(chain_list);
return 0;
}
int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
const char *table)
{
struct nft_chain_list *list;
struct nft_chain_list_iter *iter;
struct nft_chain *c;
struct nlmsghdr *nlh;
char buf[MNL_SOCKET_BUFFER_SIZE];
int ret = 0;
list = nft_chain_list_get(h);
if (list == NULL)
goto err;
iter = nft_chain_list_iter_create(list);
if (iter == NULL)
goto err;
c = nft_chain_list_iter_next(iter);
while (c != NULL) {
const char *chain_name =
nft_chain_attr_get(c, NFT_CHAIN_ATTR_NAME);
const char *chain_table =
nft_chain_attr_get(c, NFT_CHAIN_ATTR_TABLE);
if (strcmp(table, chain_table) != 0)
goto next;
if (chain != NULL && strcmp(chain, chain_name) != 0)
goto next;
nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_PACKETS, 0);
nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_BYTES, 0);
nft_chain_attr_unset(c, NFT_CHAIN_ATTR_HANDLE);
nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
h->family, NLM_F_ACK, h->seq);
nft_chain_nlmsg_build_payload(nlh, c);
ret = mnl_talk(h, nlh, NULL, NULL);
if (ret < 0)
perror("mnl_talk:nft_chain_zero_counters");
if (chain != NULL)
break;
next:
c = nft_chain_list_iter_next(iter);
}
nft_chain_list_iter_destroy(iter);
err:
nft_chain_list_free(list);
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}