summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cache.c94
-rw-r--r--src/cmd.c53
-rw-r--r--src/ct.c8
-rw-r--r--src/datatype.c21
-rw-r--r--src/evaluate.c406
-rw-r--r--src/expression.c25
-rw-r--r--src/json.c30
-rw-r--r--src/libnftables.c18
-rw-r--r--src/main.c136
-rw-r--r--src/mergesort.c63
-rw-r--r--src/meta.c15
-rw-r--r--src/mnl.c223
-rw-r--r--src/monitor.c4
-rw-r--r--src/netlink.c225
-rw-r--r--src/netlink_delinearize.c99
-rw-r--r--src/netlink_linearize.c194
-rw-r--r--src/parser_bison.y327
-rw-r--r--src/parser_json.c6
-rw-r--r--src/payload.c7
-rw-r--r--src/proto.c65
-rw-r--r--src/rule.c196
-rw-r--r--src/scanner.l3
-rw-r--r--src/segtree.c78
-rw-r--r--src/socket.c6
-rw-r--r--src/statement.c56
25 files changed, 1863 insertions, 495 deletions
diff --git a/src/cache.c b/src/cache.c
index a45111a7..ed260900 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -12,6 +12,9 @@
#include <erec.h>
#include <utils.h>
#include <cache.h>
+#include <netlink.h>
+#include <mnl.h>
+#include <libnftnl/chain.h>
static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
{
@@ -143,6 +146,8 @@ unsigned int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds)
break;
case CMD_LIST:
case CMD_EXPORT:
+ flags |= NFT_CACHE_FULL | NFT_CACHE_REFRESH;
+ break;
case CMD_MONITOR:
flags |= NFT_CACHE_FULL;
break;
@@ -162,3 +167,92 @@ unsigned int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds)
return flags;
}
+
+struct chain_cache_dump_ctx {
+ struct netlink_ctx *nlctx;
+ struct table *table;
+};
+
+static int chain_cache_cb(struct nftnl_chain *nlc, void *arg)
+{
+ struct chain_cache_dump_ctx *ctx = arg;
+ const char *chain_name, *table_name;
+ uint32_t hash, family;
+ struct chain *chain;
+
+ table_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE);
+ chain_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME);
+ family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY);
+
+ if (strcmp(table_name, ctx->table->handle.table.name) ||
+ family != ctx->table->handle.family)
+ return 0;
+
+ hash = djb_hash(chain_name) % NFT_CACHE_HSIZE;
+ chain = netlink_delinearize_chain(ctx->nlctx, nlc);
+
+ if (chain->flags & CHAIN_F_BINDING) {
+ list_add_tail(&chain->list, &ctx->table->chain_bindings);
+ } else {
+ list_add_tail(&chain->hlist, &ctx->table->chain_htable[hash]);
+ list_add_tail(&chain->list, &ctx->table->chains);
+ }
+
+ nftnl_chain_list_del(nlc);
+ nftnl_chain_free(nlc);
+
+ return 0;
+}
+
+int chain_cache_init(struct netlink_ctx *ctx, struct table *table,
+ struct nftnl_chain_list *chain_list)
+{
+ struct chain_cache_dump_ctx dump_ctx = {
+ .nlctx = ctx,
+ .table = table,
+ };
+ nftnl_chain_list_foreach(chain_list, chain_cache_cb, &dump_ctx);
+
+ return 0;
+}
+
+struct nftnl_chain_list *chain_cache_dump(struct netlink_ctx *ctx, int *err)
+{
+ struct nftnl_chain_list *chain_list;
+
+ chain_list = mnl_nft_chain_dump(ctx, AF_UNSPEC);
+ if (chain_list == NULL) {
+ if (errno == EINTR) {
+ *err = -1;
+ return NULL;
+ }
+ *err = 0;
+ return NULL;
+ }
+
+ return chain_list;
+}
+
+void chain_cache_add(struct chain *chain, struct table *table)
+{
+ uint32_t hash;
+
+ hash = djb_hash(chain->handle.chain.name) % NFT_CACHE_HSIZE;
+ list_add_tail(&chain->hlist, &table->chain_htable[hash]);
+ list_add_tail(&chain->list, &table->chains);
+}
+
+struct chain *chain_cache_find(const struct table *table,
+ const struct handle *handle)
+{
+ struct chain *chain;
+ uint32_t hash;
+
+ hash = djb_hash(handle->chain.name) % NFT_CACHE_HSIZE;
+ list_for_each_entry(chain, &table->chain_htable[hash], hlist) {
+ if (!strcmp(chain->handle.chain.name, handle->chain.name))
+ return chain;
+ }
+
+ return NULL;
+}
diff --git a/src/cmd.c b/src/cmd.c
index c8ea4492..9cb5b6a3 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -6,10 +6,11 @@
#include <iface.h>
#include <errno.h>
#include <stdlib.h>
+#include <cache.h>
#include <string.h>
static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd,
- struct location *loc)
+ const struct location *loc)
{
struct table *table;
@@ -24,7 +25,7 @@ static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd,
}
static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
- struct location *loc)
+ const struct location *loc)
{
const struct table *table;
struct chain *chain;
@@ -40,8 +41,42 @@ static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
return 1;
}
+static int nft_cmd_enoent_rule(struct netlink_ctx *ctx, const struct cmd *cmd,
+ const struct location *loc)
+{
+ unsigned int flags = NFT_CACHE_TABLE |
+ NFT_CACHE_CHAIN;
+ const struct table *table;
+ struct chain *chain;
+
+ if (cache_update(ctx->nft, flags, ctx->msgs) < 0)
+ return 0;
+
+ table = table_lookup_fuzzy(&cmd->handle, &ctx->nft->cache);
+ if (table && strcmp(cmd->handle.table.name, table->handle.table.name)) {
+ netlink_io_error(ctx, loc, "%s; did you mean table ‘%s’ in family %s?",
+ strerror(ENOENT), table->handle.table.name,
+ family2str(table->handle.family));
+ return 1;
+ } else if (!table) {
+ return 0;
+ }
+
+ chain = chain_lookup_fuzzy(&cmd->handle, &ctx->nft->cache, &table);
+ if (chain && strcmp(cmd->handle.chain.name, chain->handle.chain.name)) {
+ netlink_io_error(ctx, loc, "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ strerror(ENOENT),
+ chain->handle.chain.name,
+ family2str(table->handle.family),
+ table->handle.table.name);
+ return 1;
+ }
+
+ return 0;
+}
+
static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
- struct location *loc)
+ const struct location *loc)
{
const struct table *table;
struct set *set;
@@ -60,7 +95,7 @@ static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
}
static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd,
- struct location *loc)
+ const struct location *loc)
{
const struct table *table;
struct obj *obj;
@@ -77,7 +112,8 @@ static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd,
}
static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx,
- const struct cmd *cmd, struct location *loc)
+ const struct cmd *cmd,
+ const struct location *loc)
{
const struct table *table;
struct flowtable *ft;
@@ -95,7 +131,7 @@ static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx,
}
static void nft_cmd_enoent(struct netlink_ctx *ctx, const struct cmd *cmd,
- struct location *loc, int err)
+ const struct location *loc, int err)
{
int ret = 0;
@@ -109,6 +145,9 @@ static void nft_cmd_enoent(struct netlink_ctx *ctx, const struct cmd *cmd,
case CMD_OBJ_SET:
ret = nft_cmd_enoent_set(ctx, cmd, loc);
break;
+ case CMD_OBJ_RULE:
+ ret = nft_cmd_enoent_rule(ctx, cmd, loc);
+ break;
case CMD_OBJ_COUNTER:
case CMD_OBJ_QUOTA:
case CMD_OBJ_CT_HELPER:
@@ -135,7 +174,7 @@ static void nft_cmd_enoent(struct netlink_ctx *ctx, const struct cmd *cmd,
void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
struct mnl_err *err)
{
- struct location *loc = NULL;
+ const struct location *loc = NULL;
int i;
for (i = 0; i < cmd->num_attrs; i++) {
diff --git a/src/ct.c b/src/ct.c
index 0842c838..2218ecc7 100644
--- a/src/ct.c
+++ b/src/ct.c
@@ -351,9 +351,11 @@ static void ct_expr_clone(struct expr *new, const struct expr *expr)
new->ct = expr->ct;
}
-static void ct_expr_pctx_update(struct proto_ctx *ctx, const struct expr *expr)
+static void ct_expr_pctx_update(struct proto_ctx *ctx,
+ const struct location *loc,
+ const struct expr *left,
+ const struct expr *right)
{
- const struct expr *left = expr->left, *right = expr->right;
const struct proto_desc *base = NULL, *desc;
uint32_t nhproto;
@@ -366,7 +368,7 @@ static void ct_expr_pctx_update(struct proto_ctx *ctx, const struct expr *expr)
if (!desc)
return;
- proto_ctx_update(ctx, left->ct.base + 1, &expr->location, desc);
+ proto_ctx_update(ctx, left->ct.base + 1, loc, desc);
}
#define NFTNL_UDATA_CT_KEY 0
diff --git a/src/datatype.c b/src/datatype.c
index 90905258..7382307e 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -247,20 +247,25 @@ const struct datatype invalid_type = {
.print = invalid_type_print,
};
+void expr_chain_export(const struct expr *e, char *chain_name)
+{
+ unsigned int len;
+
+ len = e->len / BITS_PER_BYTE;
+ if (len >= NFT_CHAIN_MAXNAMELEN)
+ BUG("verdict expression length %u is too large (%u bits max)",
+ e->len, NFT_CHAIN_MAXNAMELEN * BITS_PER_BYTE);
+
+ mpz_export_data(chain_name, e->value, BYTEORDER_HOST_ENDIAN, len);
+}
+
static void verdict_jump_chain_print(const char *what, const struct expr *e,
struct output_ctx *octx)
{
char chain[NFT_CHAIN_MAXNAMELEN];
- unsigned int len;
memset(chain, 0, sizeof(chain));
-
- len = e->len / BITS_PER_BYTE;
- if (len >= sizeof(chain))
- BUG("verdict expression length %u is too large (%lu bits max)",
- e->len, (unsigned long)sizeof(chain) * BITS_PER_BYTE);
-
- mpz_export_data(chain, e->value, BYTEORDER_HOST_ENDIAN, len);
+ expr_chain_export(e, chain);
nft_print(octx, "%s %s", what, chain);
}
diff --git a/src/evaluate.c b/src/evaluate.c
index 4156d896..abbf83ae 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -19,6 +19,7 @@
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_synproxy.h>
#include <linux/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_log.h>
#include <linux/netfilter_ipv4.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
@@ -80,6 +81,7 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set);
static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
const char *name,
struct expr *key,
+ struct expr *data,
struct expr *expr)
{
struct cmd *cmd;
@@ -93,6 +95,7 @@ static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
set->flags = NFT_SET_ANONYMOUS | expr->set_flags;
set->handle.set.name = xstrdup(name);
set->key = key;
+ set->data = data;
set->init = expr;
set->automerge = set->flags & NFT_SET_INTERVAL;
@@ -321,8 +324,11 @@ static int expr_evaluate_string(struct eval_ctx *ctx, struct expr **exprp)
return 0;
}
- if (datalen >= 1 &&
- data[datalen - 1] == '\\') {
+ if (datalen == 0)
+ return expr_error(ctx->msgs, expr,
+ "All-wildcard strings are not supported");
+
+ if (data[datalen - 1] == '\\') {
char unescaped_str[data_len];
memset(unescaped_str, 0, sizeof(unescaped_str));
@@ -704,33 +710,43 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
return -1;
rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
- } else {
- /* No conflict: Same payload protocol as context, adjust offset
- * if needed.
- */
- if (desc == payload->payload.desc) {
- payload->payload.offset +=
- ctx->pctx.protocol[base].offset;
- return 0;
- }
- /* If we already have context and this payload is on the same
- * base, try to resolve the protocol conflict.
- */
- if (payload->payload.base == desc->base) {
- err = resolve_protocol_conflict(ctx, desc, payload);
- if (err <= 0)
- return err;
+ return 0;
+ }
+
+ if (payload->payload.base == desc->base &&
+ proto_ctx_is_ambiguous(&ctx->pctx, base)) {
+ desc = proto_ctx_find_conflict(&ctx->pctx, base, payload->payload.desc);
+ assert(desc);
- desc = ctx->pctx.protocol[base].desc;
- if (desc == payload->payload.desc)
- return 0;
- }
return expr_error(ctx->msgs, payload,
"conflicting protocols specified: %s vs. %s",
- ctx->pctx.protocol[base].desc->name,
+ desc->name,
payload->payload.desc->name);
}
- return 0;
+
+ /* No conflict: Same payload protocol as context, adjust offset
+ * if needed.
+ */
+ if (desc == payload->payload.desc) {
+ payload->payload.offset += ctx->pctx.protocol[base].offset;
+ return 0;
+ }
+ /* If we already have context and this payload is on the same
+ * base, try to resolve the protocol conflict.
+ */
+ if (payload->payload.base == desc->base) {
+ err = resolve_protocol_conflict(ctx, desc, payload);
+ if (err <= 0)
+ return err;
+
+ desc = ctx->pctx.protocol[base].desc;
+ if (desc == payload->payload.desc)
+ return 0;
+ }
+ return expr_error(ctx->msgs, payload,
+ "conflicting protocols specified: %s vs. %s",
+ ctx->pctx.protocol[base].desc->name,
+ payload->payload.desc->name);
}
static bool payload_needs_adjustment(const struct expr *expr)
@@ -1411,7 +1427,7 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
struct expr_ctx ectx = ctx->ectx;
struct expr *map = *expr, *mappings;
const struct datatype *dtype;
- struct expr *key;
+ struct expr *key, *data;
expr_set_context(&ctx->ectx, NULL, 0);
if (expr_evaluate(ctx, &map->map) < 0)
@@ -1430,15 +1446,14 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
ctx->ectx.byteorder,
ctx->ectx.len, NULL);
+ dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
+ data = constant_expr_alloc(&netlink_location, dtype,
+ dtype->byteorder, ectx.len, NULL);
+
mappings = implicit_set_declaration(ctx, "__map%d",
- key,
+ key, data,
mappings);
- dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
-
- mappings->set->data = constant_expr_alloc(&netlink_location,
- dtype, dtype->byteorder,
- ectx.len, NULL);
if (ectx.len && mappings->set->data->len != ectx.len)
BUG("%d vs %d\n", mappings->set->data->len, ectx.len);
@@ -1873,8 +1888,7 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
* Update protocol context for payload and meta iiftype
* equality expressions.
*/
- if (expr_is_singleton(right))
- relational_expr_pctx_update(&ctx->pctx, rel);
+ relational_expr_pctx_update(&ctx->pctx, rel);
/* fall through */
case OP_NEQ:
@@ -1896,16 +1910,30 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
return -1;
break;
case EXPR_SET:
+ if (right->size == 0)
+ return expr_error(ctx->msgs, right, "Set is empty");
+
right = rel->right =
implicit_set_declaration(ctx, "__set%d",
- expr_get(left), right);
+ expr_get(left), NULL,
+ right);
/* fall through */
case EXPR_SET_REF:
+ if (rel->left->etype == EXPR_CT &&
+ (rel->left->ct.key == NFT_CT_SRC ||
+ rel->left->ct.key == NFT_CT_DST))
+ return expr_error(ctx->msgs, left,
+ "specify either ip or ip6 for address matching");
+
/* Data for range lookups needs to be in big endian order */
if (right->set->flags & NFT_SET_INTERVAL &&
byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0)
return -1;
break;
+ case EXPR_CONCAT:
+ return expr_binary_error(ctx->msgs, left, right,
+ "Use concatenations with sets and maps, not singleton values");
+ break;
default:
BUG("invalid expression type %s\n", expr_name(right));
}
@@ -1982,9 +2010,11 @@ static int expr_evaluate_meta(struct eval_ctx *ctx, struct expr **exprp)
static int expr_evaluate_socket(struct eval_ctx *ctx, struct expr **expr)
{
+ enum nft_socket_keys key = (*expr)->socket.key;
int maxval = 0;
- if((*expr)->socket.key == NFT_SOCKET_TRANSPARENT)
+ if (key == NFT_SOCKET_TRANSPARENT ||
+ key == NFT_SOCKET_WILDCARD)
maxval = 1;
__expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->byteorder,
(*expr)->len, maxval);
@@ -2007,10 +2037,15 @@ static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp)
{
struct expr *new = expr_clone((*exprp)->sym->expr);
+ if (expr_evaluate(ctx, &new) < 0) {
+ expr_free(new);
+ return -1;
+ }
+
expr_free(*exprp);
*exprp = new;
- return expr_evaluate(ctx, exprp);
+ return 0;
}
static int expr_evaluate_xfrm(struct eval_ctx *ctx, struct expr **exprp)
@@ -2389,7 +2424,7 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
set->set_flags |= NFT_SET_TIMEOUT;
setref = implicit_set_declaration(ctx, stmt->meter.name,
- expr_get(key), set);
+ expr_get(key), NULL, set);
setref->set->desc.size = stmt->meter.size;
stmt->meter.set = setref;
@@ -3092,6 +3127,63 @@ static int stmt_evaluate_synproxy(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
+static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule,
+ enum cmd_ops op);
+
+static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ struct chain *chain = stmt->chain.chain;
+ struct cmd *cmd;
+
+ chain->flags |= CHAIN_F_BINDING;
+
+ if (ctx->table != NULL) {
+ list_add_tail(&chain->list, &ctx->table->chains);
+ } else {
+ struct rule *rule, *next;
+ struct handle h;
+
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &chain->handle);
+ h.family = ctx->rule->handle.family;
+ xfree(h.table.name);
+ h.table.name = xstrdup(ctx->rule->handle.table.name);
+ h.chain.location = stmt->location;
+ h.chain_id = chain->handle.chain_id;
+
+ cmd = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &h, &stmt->location,
+ chain);
+ cmd->location = stmt->location;
+ list_add_tail(&cmd->list, &ctx->cmd->list);
+ h.chain_id = chain->handle.chain_id;
+
+ list_for_each_entry_safe(rule, next, &chain->rules, list) {
+ struct eval_ctx rule_ctx = {
+ .nft = ctx->nft,
+ .msgs = ctx->msgs,
+ };
+ struct handle h2 = {};
+
+ handle_merge(&rule->handle, &ctx->rule->handle);
+ xfree(rule->handle.table.name);
+ rule->handle.table.name = xstrdup(ctx->rule->handle.table.name);
+ xfree(rule->handle.chain.name);
+ rule->handle.chain.name = NULL;
+ rule->handle.chain_id = chain->handle.chain_id;
+ if (rule_evaluate(&rule_ctx, rule, CMD_INVALID) < 0)
+ return -1;
+
+ handle_merge(&h2, &rule->handle);
+ cmd = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h2,
+ &rule->location, rule);
+ list_add_tail(&cmd->list, &ctx->cmd->list);
+ list_del(&rule->list);
+ }
+ }
+
+ return 0;
+}
+
static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
{
int err;
@@ -3198,8 +3290,49 @@ static int stmt_evaluate_queue(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
+static int stmt_evaluate_log_prefix(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ char prefix[NF_LOG_PREFIXLEN] = {}, tmp[NF_LOG_PREFIXLEN] = {};
+ int len = sizeof(prefix), offset = 0, ret;
+ struct expr *expr;
+ size_t size = 0;
+
+ if (stmt->log.prefix->etype != EXPR_LIST)
+ return 0;
+
+ list_for_each_entry(expr, &stmt->log.prefix->expressions, list) {
+ switch (expr->etype) {
+ case EXPR_VALUE:
+ expr_to_string(expr, tmp);
+ ret = snprintf(prefix + offset, len, "%s", tmp);
+ break;
+ case EXPR_VARIABLE:
+ ret = snprintf(prefix + offset, len, "%s",
+ expr->sym->expr->identifier);
+ break;
+ default:
+ BUG("unknown expresion type %s\n", expr_name(expr));
+ break;
+ }
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ }
+
+ if (len == NF_LOG_PREFIXLEN)
+ return stmt_error(ctx, stmt, "log prefix is too long");
+
+ expr = constant_expr_alloc(&stmt->log.prefix->location, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(prefix) * BITS_PER_BYTE, prefix);
+ expr_free(stmt->log.prefix);
+ stmt->log.prefix = expr;
+
+ return 0;
+}
+
static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
{
+ int ret = 0;
+
if (stmt->log.flags & (STMT_LOG_GROUP | STMT_LOG_SNAPLEN |
STMT_LOG_QTHRESHOLD)) {
if (stmt->log.flags & STMT_LOG_LEVEL)
@@ -3213,7 +3346,11 @@ static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
(stmt->log.flags & ~STMT_LOG_LEVEL || stmt->log.logflags))
return stmt_error(ctx, stmt,
"log level audit doesn't support any further options");
- return 0;
+
+ if (stmt->log.prefix)
+ ret = stmt_evaluate_log_prefix(ctx, stmt);
+
+ return ret;
}
static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
@@ -3318,7 +3455,7 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
ctx->ectx.len, NULL);
mappings = implicit_set_declaration(ctx, "__objmap%d",
- key, mappings);
+ key, NULL, mappings);
mappings->set->objtype = stmt->objref.type;
map->mappings = mappings;
@@ -3438,12 +3575,14 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
return stmt_evaluate_map(ctx, stmt);
case STMT_SYNPROXY:
return stmt_evaluate_synproxy(ctx, stmt);
+ case STMT_CHAIN:
+ return stmt_evaluate_chain(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
}
-static int setelem_evaluate(struct eval_ctx *ctx, struct expr **expr)
+static int setelem_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
{
struct table *table;
struct set *set;
@@ -3459,9 +3598,12 @@ static int setelem_evaluate(struct eval_ctx *ctx, struct expr **expr)
ctx->set = set;
expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
- if (expr_evaluate(ctx, expr) < 0)
+ if (expr_evaluate(ctx, &cmd->expr) < 0)
return -1;
ctx->set = NULL;
+
+ cmd->elem.set = set_get(set);
+
return 0;
}
@@ -3530,11 +3672,6 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
return set_key_data_error(ctx, set,
set->data->dtype, type);
} else if (set_is_objmap(set->flags)) {
- if (set->data) {
- assert(set->data->etype == EXPR_VALUE);
- assert(set->data->dtype == &string_type);
- }
-
assert(set->data == NULL);
set->data = constant_expr_alloc(&netlink_location, &string_type,
BYTEORDER_HOST_ENDIAN,
@@ -3590,7 +3727,6 @@ static bool evaluate_priority(struct eval_ctx *ctx, struct prio_spec *prio,
mpz_export_data(prio_str, prio->expr->value, BYTEORDER_HOST_ENDIAN,
NFT_NAME_MAXLEN);
loc = prio->expr->location;
- expr_free(prio->expr);
if (sscanf(prio_str, "%s %c %d", prio_fst, &op, &prio_snd) < 3) {
priority = std_prio_lookup(prio_str, family, hook);
@@ -3607,6 +3743,7 @@ static bool evaluate_priority(struct eval_ctx *ctx, struct prio_spec *prio,
else
return false;
}
+ expr_free(prio->expr);
prio->expr = constant_expr_alloc(&loc, &integer_type,
BYTEORDER_HOST_ENDIAN,
sizeof(int) * BITS_PER_BYTE,
@@ -3614,6 +3751,69 @@ static bool evaluate_priority(struct eval_ctx *ctx, struct prio_spec *prio,
return true;
}
+static bool evaluate_expr_variable(struct eval_ctx *ctx, struct expr **exprp)
+{
+ struct expr *expr;
+
+ if (expr_evaluate(ctx, exprp) < 0)
+ return false;
+
+ expr = *exprp;
+ if (expr->etype != EXPR_VALUE &&
+ expr->etype != EXPR_SET) {
+ expr_error(ctx->msgs, expr, "%s is not a valid "
+ "variable expression", expr_name(expr));
+ return false;
+ }
+
+ return true;
+}
+
+static bool evaluate_device_expr(struct eval_ctx *ctx, struct expr **dev_expr)
+{
+ struct expr *expr, *next, *key;
+ LIST_HEAD(tmp);
+
+ if ((*dev_expr)->etype == EXPR_VARIABLE) {
+ expr_set_context(&ctx->ectx, &ifname_type,
+ IFNAMSIZ * BITS_PER_BYTE);
+ if (!evaluate_expr_variable(ctx, dev_expr))
+ return false;
+ }
+
+ if ((*dev_expr)->etype != EXPR_SET &&
+ (*dev_expr)->etype != EXPR_LIST)
+ return true;
+
+ list_for_each_entry_safe(expr, next, &(*dev_expr)->expressions, list) {
+ list_del(&expr->list);
+
+ switch (expr->etype) {
+ case EXPR_VARIABLE:
+ expr_set_context(&ctx->ectx, &ifname_type,
+ IFNAMSIZ * BITS_PER_BYTE);
+ if (!evaluate_expr_variable(ctx, &expr))
+ return false;
+ break;
+ case EXPR_SET_ELEM:
+ key = expr_clone(expr->key);
+ expr_free(expr);
+ expr = key;
+ break;
+ case EXPR_VALUE:
+ break;
+ default:
+ BUG("invalid expresion type %s\n", expr_name(expr));
+ break;
+ }
+
+ list_add(&expr->list, &tmp);
+ }
+ list_splice_init(&tmp, &(*dev_expr)->expressions);
+
+ return true;
+}
+
static uint32_t str2hooknum(uint32_t family, const char *hook);
static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
@@ -3624,17 +3824,19 @@ static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
if (table == NULL)
return table_not_found(ctx);
- ft->hook.num = str2hooknum(NFPROTO_NETDEV, ft->hook.name);
- if (ft->hook.num == NF_INET_NUMHOOKS)
- return chain_error(ctx, ft, "invalid hook %s", ft->hook.name);
-
- if (!evaluate_priority(ctx, &ft->priority, NFPROTO_NETDEV, ft->hook.num))
- return __stmt_binary_error(ctx, &ft->priority.loc, NULL,
- "invalid priority expression %s.",
- expr_name(ft->priority.expr));
+ if (ft->hook.name) {
+ ft->hook.num = str2hooknum(NFPROTO_NETDEV, ft->hook.name);
+ if (ft->hook.num == NF_INET_NUMHOOKS)
+ return chain_error(ctx, ft, "invalid hook %s",
+ ft->hook.name);
+ if (!evaluate_priority(ctx, &ft->priority, NFPROTO_NETDEV, ft->hook.num))
+ return __stmt_binary_error(ctx, &ft->priority.loc, NULL,
+ "invalid priority expression %s.",
+ expr_name(ft->priority.expr));
+ }
- if (!ft->dev_expr)
- return chain_error(ctx, ft, "Unbound flowtable not allowed (must specify devices)");
+ if (ft->dev_expr && !evaluate_device_expr(ctx, &ft->dev_expr))
+ return -1;
return 0;
}
@@ -3667,7 +3869,7 @@ static int rule_cache_update(struct eval_ctx *ctx, enum cmd_ops op)
if (!table)
return table_not_found(ctx);
- chain = chain_lookup(table, &rule->handle);
+ chain = chain_cache_find(table, &rule->handle);
if (!chain)
return chain_not_found(ctx);
@@ -3763,10 +3965,12 @@ static uint32_t str2hooknum(uint32_t family, const char *hook)
return NF_INET_NUMHOOKS;
switch (family) {
+ case NFPROTO_INET:
+ if (!strcmp(hook, "ingress"))
+ return NF_INET_INGRESS;
case NFPROTO_IPV4:
case NFPROTO_BRIDGE:
case NFPROTO_IPV6:
- case NFPROTO_INET:
/* These families have overlapping values for each hook */
if (!strcmp(hook, "prerouting"))
return NF_INET_PRE_ROUTING;
@@ -3798,25 +4002,6 @@ static uint32_t str2hooknum(uint32_t family, const char *hook)
return NF_INET_NUMHOOKS;
}
-static bool evaluate_policy(struct eval_ctx *ctx, struct expr **exprp)
-{
- struct expr *expr;
-
- ctx->ectx.dtype = &policy_type;
- ctx->ectx.len = NFT_NAME_MAXLEN * BITS_PER_BYTE;
- if (expr_evaluate(ctx, exprp) < 0)
- return false;
-
- expr = *exprp;
- if (expr->etype != EXPR_VALUE) {
- expr_error(ctx->msgs, expr, "%s is not a valid "
- "policy expression", expr_name(expr));
- return false;
- }
-
- return true;
-}
-
static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
{
struct table *table;
@@ -3830,12 +4015,12 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
if (chain_lookup(table, &ctx->cmd->handle) == NULL) {
chain = chain_alloc(NULL);
handle_merge(&chain->handle, &ctx->cmd->handle);
- chain_add_hash(chain, table);
+ chain_cache_add(chain, table);
}
return 0;
- } else {
+ } else if (!(chain->flags & CHAIN_F_BINDING)) {
if (chain_lookup(table, &chain->handle) == NULL)
- chain_add_hash(chain_get(chain), table);
+ chain_cache_add(chain_get(chain), table);
}
if (chain->flags & CHAIN_F_BASECHAIN) {
@@ -3852,15 +4037,22 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
"invalid priority expression %s in this context.",
expr_name(chain->priority.expr));
if (chain->policy) {
- if (!evaluate_policy(ctx, &chain->policy))
+ expr_set_context(&ctx->ectx, &policy_type,
+ NFT_NAME_MAXLEN * BITS_PER_BYTE);
+ if (!evaluate_expr_variable(ctx, &chain->policy))
return chain_error(ctx, chain, "invalid policy expression %s",
expr_name(chain->policy));
}
- if (chain->handle.family == NFPROTO_NETDEV) {
+ if (chain->handle.family == NFPROTO_NETDEV ||
+ (chain->handle.family == NFPROTO_INET &&
+ chain->hook.num == NF_INET_INGRESS)) {
if (!chain->dev_expr)
return __stmt_binary_error(ctx, &chain->loc, NULL,
"Missing `device' in this chain definition");
+
+ if (!evaluate_device_expr(ctx, &chain->dev_expr))
+ return -1;
} else if (chain->dev_expr) {
return __stmt_binary_error(ctx, &chain->dev_expr->location, NULL,
"This chain type cannot be bound to device");
@@ -3978,7 +4170,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- return setelem_evaluate(ctx, &cmd->expr);
+ return setelem_evaluate(ctx, cmd);
case CMD_OBJ_SET:
handle_merge(&cmd->set->handle, &cmd->handle);
return set_evaluate(ctx, cmd->set);
@@ -4006,15 +4198,30 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
}
}
+static void table_del_cache(struct eval_ctx *ctx, struct cmd *cmd)
+{
+ struct table *table;
+
+ table = table_lookup(&cmd->handle, &ctx->nft->cache);
+ if (!table)
+ return;
+
+ list_del(&table->list);
+ table_free(table);
+}
+
static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- return setelem_evaluate(ctx, &cmd->expr);
+ return setelem_evaluate(ctx, cmd);
case CMD_OBJ_SET:
case CMD_OBJ_RULE:
case CMD_OBJ_CHAIN:
+ return 0;
case CMD_OBJ_TABLE:
+ table_del_cache(ctx, cmd);
+ return 0;
case CMD_OBJ_FLOWTABLE:
case CMD_OBJ_COUNTER:
case CMD_OBJ_QUOTA:
@@ -4032,21 +4239,9 @@ static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
static int cmd_evaluate_get(struct eval_ctx *ctx, struct cmd *cmd)
{
- struct table *table;
- struct set *set;
-
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
- if (table == NULL)
- return table_not_found(ctx);
-
- set = set_lookup(table, cmd->handle.set.name);
- if (set == NULL || set_is_map(set->flags))
- return set_not_found(ctx, &ctx->cmd->handle.set.location,
- ctx->cmd->handle.set.name);
-
- return setelem_evaluate(ctx, &cmd->expr);
+ return setelem_evaluate(ctx, cmd);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -4224,6 +4419,14 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd)
}
}
+static void __flush_set_cache(struct set *set)
+{
+ if (set->init != NULL) {
+ expr_free(set->init);
+ set->init = NULL;
+ }
+}
+
static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
{
struct table *table;
@@ -4251,6 +4454,9 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
else if (!set_is_literal(set->flags))
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
+
+ __flush_set_cache(set);
+
return 0;
case CMD_OBJ_MAP:
table = table_lookup(&cmd->handle, &ctx->nft->cache);
@@ -4265,6 +4471,8 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
+ __flush_set_cache(set);
+
return 0;
case CMD_OBJ_METER:
table = table_lookup(&cmd->handle, &ctx->nft->cache);
@@ -4279,6 +4487,8 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
+ __flush_set_cache(set);
+
return 0;
default:
BUG("invalid command object type %u\n", cmd->obj);
diff --git a/src/expression.c b/src/expression.c
index a6bde70f..87bd4d01 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -175,6 +175,15 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
}
}
+void expr_to_string(const struct expr *expr, char *string)
+{
+ int len = expr->len / BITS_PER_BYTE;
+
+ assert(expr->dtype == &string_type);
+
+ mpz_export_data(string, expr->value, BYTEORDER_HOST_ENDIAN, len);
+}
+
void expr_set_type(struct expr *expr, const struct datatype *dtype,
enum byteorder byteorder)
{
@@ -699,16 +708,26 @@ struct expr *relational_expr_alloc(const struct location *loc, enum ops op,
void relational_expr_pctx_update(struct proto_ctx *ctx,
const struct expr *expr)
{
- const struct expr *left = expr->left;
+ const struct expr *left = expr->left, *right = expr->right;
const struct expr_ops *ops;
+ const struct expr *i;
assert(expr->etype == EXPR_RELATIONAL);
assert(expr->op == OP_EQ || expr->op == OP_IMPLICIT);
ops = expr_ops(left);
if (ops->pctx_update &&
- (left->flags & EXPR_F_PROTOCOL))
- ops->pctx_update(ctx, expr);
+ (left->flags & EXPR_F_PROTOCOL)) {
+ if (expr_is_singleton(right))
+ ops->pctx_update(ctx, &expr->location, left, right);
+ else if (right->etype == EXPR_SET) {
+ list_for_each_entry(i, &right->expressions, list) {
+ if (i->etype == EXPR_SET_ELEM &&
+ i->key->etype == EXPR_VALUE)
+ ops->pctx_update(ctx, &expr->location, left, i->key);
+ }
+ }
+ }
}
static void range_expr_print(const struct expr *expr, struct output_ctx *octx)
diff --git a/src/json.c b/src/json.c
index ed713181..a8824d3f 100644
--- a/src/json.c
+++ b/src/json.c
@@ -62,7 +62,7 @@ static json_t *set_dtype_json(const struct expr *key)
tok = strtok(namedup, " .");
while (tok) {
- json_t *jtok = json_string(xstrdup(tok));
+ json_t *jtok = json_string(tok);
if (!root)
root = jtok;
else if (json_is_string(root))
@@ -98,6 +98,9 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
"table", set->handle.table.name,
"type", set_dtype_json(set->key),
"handle", set->handle.handle.id);
+
+ if (set->comment)
+ json_object_set_new(root, "comment", json_string(set->comment));
if (datatype_ext)
json_object_set_new(root, "map", json_string(datatype_ext));
@@ -137,7 +140,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
json_object_set_new(root, "gc-interval", tmp);
}
- if (set->init && set->init->size > 0) {
+ if (!nft_output_terse(octx) && set->init && set->init->size > 0) {
json_t *array = json_array();
const struct expr *i;
@@ -586,7 +589,7 @@ json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx)
return NULL;
/* these element attributes require formal set elem syntax */
- if (expr->timeout || expr->expiration || expr->comment) {
+ if (expr->timeout || expr->expiration || expr->comment || expr->stmt) {
root = json_pack("{s:o}", "val", root);
if (expr->timeout) {
@@ -601,6 +604,12 @@ json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx)
tmp = json_string(expr->comment);
json_object_set_new(root, "comment", tmp);
}
+ if (expr->stmt) {
+ tmp = stmt_print_json(expr->stmt, octx);
+ /* XXX: detect and complain about clashes? */
+ json_object_update_missing(root, tmp);
+ json_decref(tmp);
+ }
return json_pack("{s:o}", "elem", root);
}
@@ -1224,9 +1233,12 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
json_t *root = json_object(), *flags;
- if (stmt->log.flags & STMT_LOG_PREFIX)
- json_object_set_new(root, "prefix",
- json_string(stmt->log.prefix));
+ if (stmt->log.flags & STMT_LOG_PREFIX) {
+ char prefix[NF_LOG_PREFIXLEN] = {};
+
+ expr_to_string(stmt->log.prefix, prefix);
+ json_object_set_new(root, "prefix", json_string(prefix));
+ }
if (stmt->log.flags & STMT_LOG_GROUP)
json_object_set_new(root, "group",
json_integer(stmt->log.group));
@@ -1565,7 +1577,7 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd)
{
unsigned int family = cmd->handle.family;
- json_t *root = json_array();
+ json_t *root = json_array(), *tmp;
struct table *table;
list_for_each_entry(table, &ctx->nft->cache.list, list) {
@@ -1573,7 +1585,9 @@ static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd)
table->handle.family != family)
continue;
- json_array_extend(root, table_print_json_full(ctx, table));
+ tmp = table_print_json_full(ctx, table);
+ json_array_extend(root, tmp);
+ json_decref(tmp);
}
return root;
diff --git a/src/libnftables.c b/src/libnftables.c
index 668e3fc4..a180a9a3 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -21,7 +21,7 @@ static int nft_netlink(struct nft_ctx *nft,
struct list_head *cmds, struct list_head *msgs,
struct mnl_socket *nf_sock)
{
- uint32_t batch_seqnum, seqnum = 0, num_cmds = 0;
+ uint32_t batch_seqnum, seqnum = 0, last_seqnum = UINT32_MAX, num_cmds = 0;
struct netlink_ctx ctx = {
.nft = nft,
.msgs = msgs,
@@ -65,7 +65,14 @@ static int nft_netlink(struct nft_ctx *nft,
ret = -1;
list_for_each_entry_safe(err, tmp, &err_list, head) {
- list_for_each_entry(cmd, cmds, list) {
+ /* cmd seqnums are monotonic: only reset the starting position
+ * if the error seqnum is lower than the previous one.
+ */
+ if (err->seqnum < last_seqnum)
+ cmd = list_first_entry(cmds, struct cmd, list);
+
+ list_for_each_entry_from(cmd, cmds, list) {
+ last_seqnum = cmd->seqnum;
if (err->seqnum == cmd->seqnum ||
err->seqnum == batch_seqnum) {
nft_cmd_error(&ctx, cmd, err);
@@ -76,6 +83,11 @@ static int nft_netlink(struct nft_ctx *nft,
}
}
}
+
+ if (&cmd->list == cmds) {
+ /* not found, rewind */
+ last_seqnum = UINT32_MAX;
+ }
}
out:
mnl_batch_reset(ctx.batch);
@@ -88,10 +100,12 @@ static void nft_init(struct nft_ctx *ctx)
realm_table_rt_init(ctx);
devgroup_table_init(ctx);
ct_label_table_init(ctx);
+ expr_handler_init();
}
static void nft_exit(struct nft_ctx *ctx)
{
+ expr_handler_exit();
ct_label_table_exit(ctx);
realm_table_rt_exit(ctx);
devgroup_table_exit(ctx);
diff --git a/src/main.c b/src/main.c
index d830c7a2..3c26f510 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,17 +24,37 @@
static struct nft_ctx *nft;
-/*
- * These options are grouped separately in the help, so we give them named
- * indices for use there.
- */
enum opt_indices {
+ /* General options */
IDX_HELP,
IDX_VERSION,
IDX_VERSION_LONG,
- IDX_CHECK,
+ /* Ruleset input handling */
IDX_FILE,
+#define IDX_RULESET_INPUT_START IDX_FILE
IDX_INTERACTIVE,
+ IDX_INCLUDEPATH,
+ IDX_CHECK,
+#define IDX_RULESET_INPUT_END IDX_CHECK
+ /* Ruleset list formatting */
+ IDX_HANDLE,
+#define IDX_RULESET_LIST_START IDX_HANDLE
+ IDX_STATELESS,
+ IDX_TERSE,
+ IDX_SERVICE,
+ IDX_REVERSEDNS,
+ IDX_GUID,
+ IDX_NUMERIC,
+ IDX_NUMERIC_PRIO,
+ IDX_NUMERIC_PROTO,
+ IDX_NUMERIC_TIME,
+#define IDX_RULESET_LIST_END IDX_NUMERIC_TIME
+ /* Command output formatting */
+ IDX_ECHO,
+#define IDX_CMD_OUTPUT_START IDX_ECHO
+ IDX_JSON,
+ IDX_DEBUG,
+#define IDX_CMD_OUTPUT_END IDX_DEBUG
};
enum opt_vals {
@@ -72,46 +92,46 @@ struct nft_opt {
(struct nft_opt) { .name = n, .val = v, .arg = a, .help = h }
static const struct nft_opt nft_options[] = {
- NFT_OPT("help", OPT_HELP, NULL,
- "Show this help"),
- NFT_OPT("version", OPT_VERSION, NULL,
- "Show version information"),
- NFT_OPT(NULL, OPT_VERSION_LONG, NULL,
- "Show extended version information"),
- NFT_OPT("check", OPT_CHECK, NULL,
- "Check commands validity without actually applying the changes."),
- NFT_OPT("file", OPT_FILE, "<filename>",
- "Read input from <filename>"),
- NFT_OPT("interactive", OPT_INTERACTIVE, NULL,
- "Read input from interactive CLI"),
- NFT_OPT("numeric", OPT_NUMERIC, NULL,
- "Print fully numerical output."),
- NFT_OPT("stateless", OPT_STATELESS, NULL,
- "Omit stateful information of ruleset."),
- NFT_OPT("reversedns", OPT_IP2NAME, NULL,
- "Translate IP addresses to names."),
- NFT_OPT("service", OPT_SERVICE, NULL,
- "Translate ports to service names as described in /etc/services."),
- NFT_OPT("includepath", OPT_INCLUDEPATH, "<directory>",
- "Add <directory> to the paths searched for include files. Default is: " DEFAULT_INCLUDE_PATH),
- NFT_OPT("debug", OPT_DEBUG, "<level [,level...]>",
- "Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"),
- NFT_OPT("handle", OPT_HANDLE_OUTPUT, NULL,
- "Output rule handle."),
- NFT_OPT("echo", OPT_ECHO, NULL,
- "Echo what has been added, inserted or replaced."),
- NFT_OPT("json", OPT_JSON, NULL,
- "Format output in JSON"),
- NFT_OPT("guid", OPT_GUID, NULL,
- "Print UID/GID as defined in /etc/passwd and /etc/group."),
- NFT_OPT("numeric-priority", OPT_NUMERIC_PRIO, NULL,
- "Print chain priority numerically."),
- NFT_OPT("numeric-protocol", OPT_NUMERIC_PROTO, NULL,
- "Print layer 4 protocols numerically."),
- NFT_OPT("numeric-time", OPT_NUMERIC_TIME, NULL,
- "Print time values numerically."),
- NFT_OPT("terse", OPT_TERSE, NULL,
- "Omit contents of sets."),
+ [IDX_HELP] = NFT_OPT("help", OPT_HELP, NULL,
+ "Show this help"),
+ [IDX_VERSION] = NFT_OPT("version", OPT_VERSION, NULL,
+ "Show version information"),
+ [IDX_VERSION_LONG] = NFT_OPT(NULL, OPT_VERSION_LONG, NULL,
+ "Show extended version information"),
+ [IDX_FILE] = NFT_OPT("file", OPT_FILE, "<filename>",
+ "Read input from <filename>"),
+ [IDX_INTERACTIVE] = NFT_OPT("interactive", OPT_INTERACTIVE, NULL,
+ "Read input from interactive CLI"),
+ [IDX_INCLUDEPATH] = NFT_OPT("includepath", OPT_INCLUDEPATH, "<directory>",
+ "Add <directory> to the paths searched for include files. Default is: " DEFAULT_INCLUDE_PATH),
+ [IDX_CHECK] = NFT_OPT("check", OPT_CHECK, NULL,
+ "Check commands validity without actually applying the changes."),
+ [IDX_HANDLE] = NFT_OPT("handle", OPT_HANDLE_OUTPUT, NULL,
+ "Output rule handle."),
+ [IDX_STATELESS] = NFT_OPT("stateless", OPT_STATELESS, NULL,
+ "Omit stateful information of ruleset."),
+ [IDX_TERSE] = NFT_OPT("terse", OPT_TERSE, NULL,
+ "Omit contents of sets."),
+ [IDX_SERVICE] = NFT_OPT("service", OPT_SERVICE, NULL,
+ "Translate ports to service names as described in /etc/services."),
+ [IDX_REVERSEDNS] = NFT_OPT("reversedns", OPT_IP2NAME, NULL,
+ "Translate IP addresses to names."),
+ [IDX_GUID] = NFT_OPT("guid", OPT_GUID, NULL,
+ "Print UID/GID as defined in /etc/passwd and /etc/group."),
+ [IDX_NUMERIC] = NFT_OPT("numeric", OPT_NUMERIC, NULL,
+ "Print fully numerical output."),
+ [IDX_NUMERIC_PRIO] = NFT_OPT("numeric-priority", OPT_NUMERIC_PRIO, NULL,
+ "Print chain priority numerically."),
+ [IDX_NUMERIC_PROTO] = NFT_OPT("numeric-protocol", OPT_NUMERIC_PROTO, NULL,
+ "Print layer 4 protocols numerically."),
+ [IDX_NUMERIC_TIME] = NFT_OPT("numeric-time", OPT_NUMERIC_TIME, NULL,
+ "Print time values numerically."),
+ [IDX_ECHO] = NFT_OPT("echo", OPT_ECHO, NULL,
+ "Echo what has been added, inserted or replaced."),
+ [IDX_JSON] = NFT_OPT("json", OPT_JSON, NULL,
+ "Format output in JSON"),
+ [IDX_DEBUG] = NFT_OPT("debug", OPT_DEBUG, "<level [,level...]>",
+ "Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"),
};
#define NR_NFT_OPTIONS (sizeof(nft_options) / sizeof(nft_options[0]))
@@ -169,23 +189,35 @@ static void print_option(const struct nft_opt *opt)
static void show_help(const char *name)
{
+ int i;
+
printf("Usage: %s [ options ] [ cmds... ]\n"
"\n"
- "Options:\n", name);
+ "Options (general):\n", name);
print_option(&nft_options[IDX_HELP]);
print_option(&nft_options[IDX_VERSION]);
print_option(&nft_options[IDX_VERSION_LONG]);
- fputs("\n", stdout);
+ printf("\n"
+ "Options (ruleset input handling):"
+ "\n");
- print_option(&nft_options[IDX_CHECK]);
- print_option(&nft_options[IDX_FILE]);
- print_option(&nft_options[IDX_INTERACTIVE]);
+ for (i = IDX_RULESET_INPUT_START; i <= IDX_RULESET_INPUT_END; i++)
+ print_option(&nft_options[i]);
- fputs("\n", stdout);
+ printf("\n"
+ "Options (ruleset list formatting):"
+ "\n");
+
+ for (i = IDX_RULESET_LIST_START; i <= IDX_RULESET_LIST_END; i++)
+ print_option(&nft_options[i]);
+
+ printf("\n"
+ "Options (command output formatting):"
+ "\n");
- for (size_t i = IDX_INTERACTIVE + 1; i < NR_NFT_OPTIONS; ++i)
+ for (i = IDX_CMD_OUTPUT_START; i <= IDX_CMD_OUTPUT_END; i++)
print_option(&nft_options[i]);
fputs("\n", stdout);
diff --git a/src/mergesort.c b/src/mergesort.c
index 649b7806..41f35856 100644
--- a/src/mergesort.c
+++ b/src/mergesort.c
@@ -11,43 +11,60 @@
#include <gmputil.h>
#include <list.h>
-static int expr_msort_cmp(const struct expr *e1, const struct expr *e2);
+static void expr_msort_value(const struct expr *expr, mpz_t value);
-static int concat_expr_msort_cmp(const struct expr *e1, const struct expr *e2)
+static void concat_expr_msort_value(const struct expr *expr, mpz_t value)
{
- struct list_head *l = (&e2->expressions)->next;
- const struct expr *i1, *i2;
- int ret;
-
- list_for_each_entry(i1, &e1->expressions, list) {
- i2 = list_entry(l, typeof(struct expr), list);
-
- ret = expr_msort_cmp(i1, i2);
- if (ret)
- return ret;
-
- l = l->next;
+ unsigned int len = 0, ilen;
+ const struct expr *i;
+ char data[512];
+
+ list_for_each_entry(i, &expr->expressions, list) {
+ ilen = div_round_up(i->len, BITS_PER_BYTE);
+ mpz_export_data(data + len, i->value, i->byteorder, ilen);
+ len += ilen;
}
- return false;
+ mpz_import_data(value, data, BYTEORDER_HOST_ENDIAN, len);
}
-static int expr_msort_cmp(const struct expr *e1, const struct expr *e2)
+static void expr_msort_value(const struct expr *expr, mpz_t value)
{
- switch (e1->etype) {
+ switch (expr->etype) {
case EXPR_SET_ELEM:
- return expr_msort_cmp(e1->key, e2->key);
+ expr_msort_value(expr->key, value);
+ break;
+ case EXPR_BINOP:
+ case EXPR_MAPPING:
+ expr_msort_value(expr->left, value);
+ break;
case EXPR_VALUE:
- return mpz_cmp(e1->value, e2->value);
+ mpz_set(value, expr->value);
+ break;
case EXPR_CONCAT:
- return concat_expr_msort_cmp(e1, e2);
- case EXPR_MAPPING:
- return expr_msort_cmp(e1->left, e2->left);
+ concat_expr_msort_value(expr, value);
+ break;
default:
- BUG("Unknown expression %s\n", expr_name(e1));
+ BUG("Unknown expression %s\n", expr_name(expr));
}
}
+static int expr_msort_cmp(const struct expr *e1, const struct expr *e2)
+{
+ mpz_t value1, value2;
+ int ret;
+
+ mpz_init(value1);
+ mpz_init(value2);
+ expr_msort_value(e1, value1);
+ expr_msort_value(e2, value2);
+ ret = mpz_cmp(value1, value2);
+ mpz_clear(value1);
+ mpz_clear(value2);
+
+ return ret;
+}
+
static void list_splice_sorted(struct list_head *list, struct list_head *head)
{
struct list_head *h = head->next;
diff --git a/src/meta.c b/src/meta.c
index acc348eb..73d58b1f 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -73,7 +73,7 @@ static struct error_record *tchandle_type_parse(struct parse_ctx *ctx,
else if (strcmp(sym->identifier, "none") == 0)
handle = TC_H_UNSPEC;
else if (strchr(sym->identifier, ':')) {
- uint16_t tmp;
+ uint32_t tmp;
char *colon;
str = xstrdup(sym->identifier);
@@ -753,10 +753,11 @@ static void meta_expr_clone(struct expr *new, const struct expr *expr)
* Update LL protocol context based on IIFTYPE meta match in non-LL hooks.
*/
static void meta_expr_pctx_update(struct proto_ctx *ctx,
- const struct expr *expr)
+ const struct location *loc,
+ const struct expr *left,
+ const struct expr *right)
{
const struct hook_proto_desc *h = &hook_proto_desc[ctx->family];
- const struct expr *left = expr->left, *right = expr->right;
const struct proto_desc *desc;
uint8_t protonum;
@@ -771,7 +772,7 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
if (desc == NULL)
desc = &proto_unknown;
- proto_ctx_update(ctx, PROTO_BASE_LL_HDR, &expr->location, desc);
+ proto_ctx_update(ctx, PROTO_BASE_LL_HDR, loc, desc);
break;
case NFT_META_NFPROTO:
protonum = mpz_get_uint8(right->value);
@@ -784,7 +785,7 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
desc = h->desc;
}
- proto_ctx_update(ctx, PROTO_BASE_NETWORK_HDR, &expr->location, desc);
+ proto_ctx_update(ctx, PROTO_BASE_NETWORK_HDR, loc, desc);
break;
case NFT_META_L4PROTO:
desc = proto_find_upper(&proto_inet_service,
@@ -792,7 +793,7 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
if (desc == NULL)
desc = &proto_unknown;
- proto_ctx_update(ctx, PROTO_BASE_TRANSPORT_HDR, &expr->location, desc);
+ proto_ctx_update(ctx, PROTO_BASE_TRANSPORT_HDR, loc, desc);
break;
case NFT_META_PROTOCOL:
if (h->base != PROTO_BASE_LL_HDR)
@@ -806,7 +807,7 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
if (desc == NULL)
desc = &proto_unknown;
- proto_ctx_update(ctx, PROTO_BASE_NETWORK_HDR, &expr->location, desc);
+ proto_ctx_update(ctx, PROTO_BASE_NETWORK_HDR, loc, desc);
break;
default:
break;
diff --git a/src/mnl.c b/src/mnl.c
index 94e80261..ffa1e140 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -273,24 +273,16 @@ static int mnl_set_rcvbuffer(const struct mnl_socket *nl, socklen_t bufsiz)
return ret;
}
-static size_t mnl_nft_batch_to_msg(struct netlink_ctx *ctx, struct msghdr *msg,
- const struct sockaddr_nl *snl,
- struct iovec *iov, unsigned int iov_len)
+static void mnl_nft_batch_to_msg(struct netlink_ctx *ctx, struct msghdr *msg,
+ const struct sockaddr_nl *snl,
+ struct iovec *iov, unsigned int iov_len)
{
- unsigned int i;
- size_t len = 0;
-
msg->msg_name = (struct sockaddr_nl *)snl;
msg->msg_namelen = sizeof(*snl);
msg->msg_iov = iov;
msg->msg_iovlen = iov_len;
nftnl_batch_iovec(ctx->batch, iov, iov_len);
-
- for (i = 0; i < iov_len; i++)
- len += msg->msg_iov[i].iov_len;
-
- return len;
}
static ssize_t mnl_nft_socket_sendmsg(struct netlink_ctx *ctx,
@@ -385,7 +377,6 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
struct iovec iov[iov_len];
struct msghdr msg = {};
unsigned int rcvbufsiz;
- size_t batch_size;
fd_set readfds;
static mnl_cb_t cb_ctl_array[NLMSG_MIN_TYPE] = {
[NLMSG_ERROR] = mnl_batch_extack_cb,
@@ -397,14 +388,12 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
mnl_set_sndbuffer(ctx->nft->nf_sock, ctx->batch);
- batch_size = mnl_nft_batch_to_msg(ctx, &msg, &snl, iov, iov_len);
+ mnl_nft_batch_to_msg(ctx, &msg, &snl, iov, iov_len);
+ rcvbufsiz = num_cmds * 1024;
if (nft_output_echo(&ctx->nft->output)) {
- rcvbufsiz = num_cmds * 1024;
if (rcvbufsiz < NFT_MNL_ECHO_RCVBUFF_DEFAULT)
rcvbufsiz = NFT_MNL_ECHO_RCVBUFF_DEFAULT;
- } else {
- rcvbufsiz = num_cmds * div_round_up(batch_size, num_cmds) * 4;
}
mnl_set_rcvbuffer(ctx->nft->nf_sock, rcvbufsiz);
@@ -437,13 +426,55 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
return 0;
}
+struct mnl_nft_rule_build_ctx {
+ struct netlink_linearize_ctx *lctx;
+ struct nlmsghdr *nlh;
+ struct cmd *cmd;
+};
+
+static int mnl_nft_expr_build_cb(struct nftnl_expr *nle, void *data)
+{
+ struct mnl_nft_rule_build_ctx *ctx = data;
+ struct nlmsghdr *nlh = ctx->nlh;
+ struct cmd *cmd = ctx->cmd;
+ struct nft_expr_loc *eloc;
+ struct nlattr *nest;
+
+ eloc = nft_expr_loc_find(nle, ctx->lctx);
+ if (eloc)
+ cmd_add_loc(cmd, nlh->nlmsg_len, eloc->loc);
+
+ nest = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
+ nftnl_expr_build_payload(nlh, nle);
+ mnl_attr_nest_end(nlh, nest);
+
+ nftnl_rule_del_expr(nle);
+ nftnl_expr_free(nle);
+
+ return 0;
+}
+
+static void mnl_nft_rule_build_ctx_init(struct mnl_nft_rule_build_ctx *rule_ctx,
+ struct nlmsghdr *nlh,
+ struct cmd *cmd,
+ struct netlink_linearize_ctx *lctx)
+{
+ memset(rule_ctx, 0, sizeof(*rule_ctx));
+ rule_ctx->nlh = nlh;
+ rule_ctx->cmd = cmd;
+ rule_ctx->lctx = lctx;
+}
+
int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
+ struct mnl_nft_rule_build_ctx rule_ctx;
+ struct netlink_linearize_ctx lctx;
struct rule *rule = cmd->rule;
struct handle *h = &rule->handle;
struct nftnl_rule *nlr;
struct nlmsghdr *nlh;
+ struct nlattr *nest;
nlr = nftnl_rule_alloc();
if (!nlr)
@@ -457,7 +488,8 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
if (h->position_id)
nftnl_rule_set_u32(nlr, NFTNL_RULE_POSITION_ID, h->position_id);
- netlink_linearize_rule(ctx, nlr, rule);
+ netlink_linearize_init(&lctx, nlr);
+ netlink_linearize_rule(ctx, rule, &lctx);
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_NEWRULE,
cmd->handle.family,
@@ -466,10 +498,21 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location);
mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name);
cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location);
- mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
+
+ if (h->chain_id)
+ mnl_attr_put_u32(nlh, NFTA_RULE_CHAIN_ID, htonl(h->chain_id));
+ else
+ mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name);
+
+ mnl_nft_rule_build_ctx_init(&rule_ctx, nlh, cmd, &lctx);
+
+ nest = mnl_attr_nest_start(nlh, NFTA_RULE_EXPRESSIONS);
+ nftnl_expr_foreach(nlr, mnl_nft_expr_build_cb, &rule_ctx);
+ mnl_attr_nest_end(nlh, nest);
nftnl_rule_nlmsg_build_payload(nlh, nlr);
nftnl_rule_free(nlr);
+ netlink_linearize_fini(&lctx);
mnl_nft_batch_continue(ctx->batch);
@@ -478,11 +521,14 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ struct mnl_nft_rule_build_ctx rule_ctx;
+ struct netlink_linearize_ctx lctx;
struct rule *rule = cmd->rule;
struct handle *h = &rule->handle;
unsigned int flags = 0;
struct nftnl_rule *nlr;
struct nlmsghdr *nlh;
+ struct nlattr *nest;
if (nft_output_echo(&ctx->nft->output))
flags |= NLM_F_ECHO;
@@ -493,7 +539,8 @@ int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_rule_set_u32(nlr, NFTNL_RULE_FAMILY, h->family);
- netlink_linearize_rule(ctx, nlr, rule);
+ netlink_linearize_init(&lctx, nlr);
+ netlink_linearize_rule(ctx, rule, &lctx);
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_NEWRULE,
cmd->handle.family,
@@ -506,8 +553,15 @@ int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd)
cmd_add_loc(cmd, nlh->nlmsg_len, &h->handle.location);
mnl_attr_put_u64(nlh, NFTA_RULE_HANDLE, htobe64(h->handle.id));
+ mnl_nft_rule_build_ctx_init(&rule_ctx, nlh, cmd, &lctx);
+
+ nest = mnl_attr_nest_start(nlh, NFTA_RULE_EXPRESSIONS);
+ nftnl_expr_foreach(nlr, mnl_nft_expr_build_cb, &rule_ctx);
+ mnl_attr_nest_end(nlh, nest);
+
nftnl_rule_nlmsg_build_payload(nlh, nlr);
nftnl_rule_free(nlr);
+ netlink_linearize_fini(&lctx);
mnl_nft_batch_continue(ctx->batch);
@@ -608,6 +662,7 @@ err:
int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
+ struct nftnl_udata_buf *udbuf;
int priority, policy, i = 0;
struct nftnl_chain *nlc;
unsigned int ifname_len;
@@ -668,6 +723,16 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
xfree(dev_array);
}
+ if (cmd->chain->comment) {
+ udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
+ if (!udbuf)
+ memory_allocation_error();
+ if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_CHAIN_COMMENT, cmd->chain->comment))
+ memory_allocation_error();
+ nftnl_chain_set_data(nlc, NFTNL_CHAIN_USERDATA, nftnl_udata_buf_data(udbuf),
+ nftnl_udata_buf_len(udbuf));
+ nftnl_udata_buf_free(udbuf);
+ }
}
netlink_dump_chain(nlc, ctx);
@@ -679,7 +744,18 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, cmd->handle.table.name);
cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.chain.location);
- mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name);
+
+ if (!cmd->chain || !(cmd->chain->flags & CHAIN_F_BINDING)) {
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name);
+ } else {
+ if (cmd->handle.chain.name)
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME,
+ cmd->handle.chain.name);
+
+ mnl_attr_put_u32(nlh, NFTA_CHAIN_ID, htonl(cmd->handle.chain_id));
+ if (cmd->chain->flags)
+ nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FLAGS, cmd->chain->flags);
+ }
if (cmd->chain && cmd->chain->policy) {
mpz_export_data(&policy, cmd->chain->policy->value,
@@ -815,6 +891,7 @@ err:
int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
+ struct nftnl_udata_buf *udbuf;
struct nftnl_table *nlt;
struct nlmsghdr *nlh;
@@ -823,10 +900,22 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
memory_allocation_error();
nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, cmd->handle.family);
- if (cmd->table)
+ if (cmd->table) {
nftnl_table_set_u32(nlt, NFTNL_TABLE_FLAGS, cmd->table->flags);
- else
+
+ if (cmd->table->comment) {
+ udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
+ if (!udbuf)
+ memory_allocation_error();
+ if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_TABLE_COMMENT, cmd->table->comment))
+ memory_allocation_error();
+ nftnl_table_set_data(nlt, NFTNL_TABLE_USERDATA, nftnl_udata_buf_data(udbuf),
+ nftnl_udata_buf_len(udbuf));
+ nftnl_udata_buf_free(udbuf);
+ }
+ } else {
nftnl_table_set_u32(nlt, NFTNL_TABLE_FLAGS, 0);
+ }
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_NEWTABLE,
@@ -1027,6 +1116,11 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
sizeof(set->desc.field_len[0]));
}
+ if (set->comment) {
+ if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_SET_COMMENT, set->comment))
+ memory_allocation_error();
+ }
+
nftnl_set_set_data(nls, NFTNL_SET_USERDATA, nftnl_udata_buf_data(udbuf),
nftnl_udata_buf_len(udbuf));
nftnl_udata_buf_free(udbuf);
@@ -1156,6 +1250,7 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
struct obj *obj = cmd->object;
+ struct nftnl_udata_buf *udbuf;
struct nftnl_obj *nlo;
struct nlmsghdr *nlh;
@@ -1166,6 +1261,17 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
nftnl_obj_set_u32(nlo, NFTNL_OBJ_FAMILY, cmd->handle.family);
nftnl_obj_set_u32(nlo, NFTNL_OBJ_TYPE, obj->type);
+ if (obj->comment) {
+ udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
+ if (!udbuf)
+ memory_allocation_error();
+ if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_OBJ_COMMENT, obj->comment))
+ memory_allocation_error();
+ nftnl_obj_set_data(nlo, NFTNL_OBJ_USERDATA, nftnl_udata_buf_data(udbuf),
+ nftnl_udata_buf_len(udbuf));
+ nftnl_udata_buf_free(udbuf);
+ }
+
switch (obj->type) {
case NFT_OBJECT_COUNTER:
nftnl_obj_set_u64(nlo, NFTNL_OBJ_CTR_PKTS,
@@ -1590,29 +1696,13 @@ err:
return NULL;
}
-int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
- unsigned int flags)
+static const char **nft_flowtable_dev_array(struct cmd *cmd)
{
- struct nftnl_flowtable *flo;
unsigned int ifname_len;
const char **dev_array;
char ifname[IFNAMSIZ];
- struct nlmsghdr *nlh;
int i = 0, len = 1;
struct expr *expr;
- int priority;
-
- flo = nftnl_flowtable_alloc();
- if (!flo)
- memory_allocation_error();
-
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY,
- cmd->handle.family);
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM,
- cmd->flowtable->hook.num);
- mpz_export_data(&priority, cmd->flowtable->priority.expr->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, priority);
list_for_each_entry(expr, &cmd->flowtable->dev_expr->expressions, list)
len++;
@@ -1628,14 +1718,52 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
}
dev_array[i] = NULL;
- nftnl_flowtable_set_data(flo, NFTNL_FLOWTABLE_DEVICES,
- dev_array, sizeof(char *) * len);
- i = 0;
+ return dev_array;
+}
+
+static void nft_flowtable_dev_array_free(const char **dev_array)
+{
+ int i = 0;
+
while (dev_array[i] != NULL)
xfree(dev_array[i++]);
free(dev_array);
+}
+
+int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
+ unsigned int flags)
+{
+ struct nftnl_flowtable *flo;
+ const char **dev_array;
+ struct nlmsghdr *nlh;
+ int priority;
+
+ flo = nftnl_flowtable_alloc();
+ if (!flo)
+ memory_allocation_error();
+
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY,
+ cmd->handle.family);
+
+ if (cmd->flowtable->hook.name) {
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM,
+ cmd->flowtable->hook.num);
+ mpz_export_data(&priority, cmd->flowtable->priority.expr->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, priority);
+ } else {
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, 0);
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, 0);
+ }
+
+ if (cmd->flowtable->dev_expr) {
+ dev_array = nft_flowtable_dev_array(cmd);
+ nftnl_flowtable_set_data(flo, NFTNL_FLOWTABLE_DEVICES,
+ dev_array, 0);
+ nft_flowtable_dev_array_free(dev_array);
+ }
nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FLAGS,
cmd->flowtable->flags);
@@ -1662,6 +1790,7 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct nftnl_flowtable *flo;
+ const char **dev_array;
struct nlmsghdr *nlh;
flo = nftnl_flowtable_alloc();
@@ -1671,6 +1800,16 @@ int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY,
cmd->handle.family);
+ if (cmd->flowtable && cmd->flowtable->dev_expr) {
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, 0);
+ nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, 0);
+
+ dev_array = nft_flowtable_dev_array(cmd);
+ nftnl_flowtable_set_data(flo, NFTNL_FLOWTABLE_DEVICES,
+ dev_array, 0);
+ nft_flowtable_dev_array_free(dev_array);
+ }
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
NFT_MSG_DELFLOWTABLE, cmd->handle.family,
0, ctx->seqnum);
diff --git a/src/monitor.c b/src/monitor.c
index bb269c02..3872ebcf 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -214,6 +214,10 @@ static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type,
nft_mon_print(monh, "%s %s", family2str(t->handle.family),
t->handle.table.name);
+
+ if (t->flags & TABLE_F_DORMANT)
+ nft_mon_print(monh, " { flags dormant; }");
+
if (nft_output_handle(&monh->ctx->nft->output))
nft_mon_print(monh, " # handle %" PRIu64 "",
t->handle.handle.id);
diff --git a/src/netlink.c b/src/netlink.c
index fb0a17ba..f8ac2b9e 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -123,7 +123,7 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
netlink_gen_data(key, &nld);
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
- if (set->set_flags & NFT_SET_INTERVAL && expr->key->field_count > 1) {
+ if (set->set_flags & NFT_SET_INTERVAL && key->field_count > 1) {
key->flags |= EXPR_F_INTERVAL_END;
netlink_gen_data(key, &nld);
key->flags &= ~EXPR_F_INTERVAL_END;
@@ -269,31 +269,41 @@ static void netlink_gen_constant_data(const struct expr *expr,
div_round_up(expr->len, BITS_PER_BYTE), data);
}
-static void netlink_gen_verdict(const struct expr *expr,
- struct nft_data_linearize *data)
+static void netlink_gen_chain(const struct expr *expr,
+ struct nft_data_linearize *data)
{
char chain[NFT_CHAIN_MAXNAMELEN];
unsigned int len;
- data->verdict = expr->verdict;
+ len = expr->chain->len / BITS_PER_BYTE;
- switch (expr->verdict) {
- case NFT_JUMP:
- case NFT_GOTO:
- len = expr->chain->len / BITS_PER_BYTE;
+ if (!len)
+ BUG("chain length is 0");
- if (!len)
- BUG("chain length is 0");
+ if (len > sizeof(chain))
+ BUG("chain is too large (%u, %u max)",
+ len, (unsigned int)sizeof(chain));
- if (len > sizeof(chain))
- BUG("chain is too large (%u, %u max)",
- len, (unsigned int)sizeof(chain));
+ memset(chain, 0, sizeof(chain));
- memset(chain, 0, sizeof(chain));
+ mpz_export_data(chain, expr->chain->value,
+ BYTEORDER_HOST_ENDIAN, len);
+ snprintf(data->chain, NFT_CHAIN_MAXNAMELEN, "%s", chain);
+}
- mpz_export_data(chain, expr->chain->value,
- BYTEORDER_HOST_ENDIAN, len);
- snprintf(data->chain, NFT_CHAIN_MAXNAMELEN, "%s", chain);
+static void netlink_gen_verdict(const struct expr *expr,
+ struct nft_data_linearize *data)
+{
+
+ data->verdict = expr->verdict;
+
+ switch (expr->verdict) {
+ case NFT_JUMP:
+ case NFT_GOTO:
+ if (expr->chain)
+ netlink_gen_chain(expr, data);
+ else
+ data->chain_id = expr->chain_id;
break;
}
}
@@ -462,12 +472,34 @@ void netlink_dump_chain(const struct nftnl_chain *nlc, struct netlink_ctx *ctx)
fprintf(fp, "\n");
}
+static int chain_parse_udata_cb(const struct nftnl_udata *attr, void *data)
+{
+ unsigned char *value = nftnl_udata_get(attr);
+ uint8_t type = nftnl_udata_type(attr);
+ const struct nftnl_udata **tb = data;
+ uint8_t len = nftnl_udata_len(attr);
+
+ switch (type) {
+ case NFTNL_UDATA_CHAIN_COMMENT:
+ if (value[len - 1] != '\0')
+ return -1;
+ break;
+ default:
+ return 0;
+ }
+ tb[type] = attr;
+ return 0;
+}
+
struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
const struct nftnl_chain *nlc)
{
+ const struct nftnl_udata *ud[NFTNL_UDATA_OBJ_MAX + 1] = {};
int priority, policy, len = 0, i;
const char * const *dev_array;
struct chain *chain;
+ const char *udata;
+ uint32_t ulen;
chain = chain_alloc(nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME));
chain->handle.family =
@@ -524,56 +556,45 @@ struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
chain->flags |= CHAIN_F_BASECHAIN;
}
- return chain;
-}
-
-static int list_chain_cb(struct nftnl_chain *nlc, void *arg)
-{
- struct netlink_ctx *ctx = arg;
- const struct handle *h = ctx->data;
- const char *table;
- const char *name;
- struct chain *chain;
- uint32_t family;
-
- table = nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE);
- name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME);
- family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY);
-
- if (h->family != family || strcmp(table, h->table.name) != 0)
- return 0;
- if (h->chain.name && strcmp(name, h->chain.name) != 0)
- return 0;
-
- chain = netlink_delinearize_chain(ctx, nlc);
- list_add_tail(&chain->list, &ctx->list);
+ if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_USERDATA)) {
+ udata = nftnl_chain_get_data(nlc, NFTNL_CHAIN_USERDATA, &ulen);
+ if (nftnl_udata_parse(udata, ulen, chain_parse_udata_cb, ud) < 0) {
+ netlink_io_error(ctx, NULL, "Cannot parse userdata");
+ return NULL;
+ }
+ if (ud[NFTNL_UDATA_CHAIN_COMMENT])
+ chain->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_CHAIN_COMMENT]));
+ }
- return 0;
+ return chain;
}
-int netlink_list_chains(struct netlink_ctx *ctx, const struct handle *h)
+static int table_parse_udata_cb(const struct nftnl_udata *attr, void *data)
{
- struct nftnl_chain_list *chain_cache;
-
- chain_cache = mnl_nft_chain_dump(ctx, h->family);
- if (chain_cache == NULL) {
- if (errno == EINTR)
- return -1;
+ unsigned char *value = nftnl_udata_get(attr);
+ const struct nftnl_udata **tb = data;
+ uint8_t type = nftnl_udata_type(attr);
+ uint8_t len = nftnl_udata_len(attr);
- return 0;
+ switch (type) {
+ case NFTNL_UDATA_TABLE_COMMENT:
+ if (value[len - 1] != '\0')
+ return -1;
+ break;
+ default:
+ return 0;
}
-
- ctx->data = h;
- nftnl_chain_list_foreach(chain_cache, list_chain_cb, ctx);
- nftnl_chain_list_free(chain_cache);
-
+ tb[type] = attr;
return 0;
}
struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
const struct nftnl_table *nlt)
{
+ const struct nftnl_udata *ud[NFTNL_UDATA_TABLE_MAX + 1] = {};
struct table *table;
+ const char *udata;
+ uint32_t ulen;
table = table_alloc();
table->handle.family = nftnl_table_get_u32(nlt, NFTNL_TABLE_FAMILY);
@@ -581,6 +602,16 @@ struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
table->flags = nftnl_table_get_u32(nlt, NFTNL_TABLE_FLAGS);
table->handle.handle.id = nftnl_table_get_u64(nlt, NFTNL_TABLE_HANDLE);
+ if (nftnl_table_is_set(nlt, NFTNL_TABLE_USERDATA)) {
+ udata = nftnl_table_get_data(nlt, NFTNL_TABLE_USERDATA, &ulen);
+ if (nftnl_udata_parse(udata, ulen, table_parse_udata_cb, ud) < 0) {
+ netlink_io_error(ctx, NULL, "Cannot parse userdata");
+ return NULL;
+ }
+ if (ud[NFTNL_UDATA_TABLE_COMMENT])
+ table->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_TABLE_COMMENT]));
+ }
+
return table;
}
@@ -648,6 +679,7 @@ void netlink_dump_set(const struct nftnl_set *nls, struct netlink_ctx *ctx)
static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data)
{
+ unsigned char *value = nftnl_udata_get(attr);
const struct nftnl_udata **tb = data;
uint8_t type = nftnl_udata_type(attr);
uint8_t len = nftnl_udata_len(attr);
@@ -665,6 +697,10 @@ static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data)
if (len < 3)
return -1;
break;
+ case NFTNL_UDATA_SET_COMMENT:
+ if (value[len - 1] != '\0')
+ return -1;
+ break;
default:
return 0;
}
@@ -738,11 +774,11 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
enum byteorder databyteorder = BYTEORDER_INVALID;
const struct datatype *keytype, *datatype = NULL;
struct expr *typeof_expr_key, *typeof_expr_data;
+ const char *udata, *comment = NULL;
uint32_t flags, key, objtype = 0;
const struct datatype *dtype;
uint32_t data_interval = 0;
bool automerge = false;
- const char *udata;
struct set *set;
uint32_t ulen;
uint32_t klen;
@@ -770,6 +806,8 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
typeof_expr_key = set_make_key(ud[NFTNL_UDATA_SET_KEY_TYPEOF]);
if (ud[NFTNL_UDATA_SET_DATA_TYPEOF])
typeof_expr_data = set_make_key(ud[NFTNL_UDATA_SET_DATA_TYPEOF]);
+ if (ud[NFTNL_UDATA_SET_COMMENT])
+ comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_SET_COMMENT]));
}
key = nftnl_set_get_u32(nls, NFTNL_SET_KEY_TYPE);
@@ -806,6 +844,8 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
set->handle.table.name = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_TABLE));
set->handle.set.name = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_NAME));
set->automerge = automerge;
+ if (comment)
+ set->comment = comment;
if (nftnl_set_is_set(nls, NFTNL_SET_EXPR)) {
const struct nftnl_expr *nle;
@@ -1172,6 +1212,42 @@ static int list_setelem_cb(struct nftnl_set_elem *nlse, void *arg)
return netlink_delinearize_setelem(nlse, ctx->set, &ctx->nft->cache);
}
+static int list_setelem_debug_cb(struct nftnl_set_elem *nlse, void *arg)
+{
+ int r;
+
+ r = list_setelem_cb(nlse, arg);
+ if (r == 0) {
+ struct netlink_ctx *ctx = arg;
+ FILE *fp = ctx->nft->output.output_fp;
+
+ fprintf(fp, "\t");
+ nftnl_set_elem_fprintf(fp, nlse, 0, 0);
+ fprintf(fp, "\n");
+ }
+
+ return r;
+}
+
+static int list_setelements(struct nftnl_set *s, struct netlink_ctx *ctx)
+{
+ FILE *fp = ctx->nft->output.output_fp;
+
+ if (fp && (ctx->nft->debug_mask & NFT_DEBUG_NETLINK)) {
+ const char *table, *name;
+ uint32_t family = nftnl_set_get_u32(s, NFTNL_SET_FAMILY);
+
+ table = nftnl_set_get_str(s, NFTNL_SET_TABLE);
+ name = nftnl_set_get_str(s, NFTNL_SET_NAME);
+
+ fprintf(fp, "%s %s @%s\n", family2str(family), table, name);
+
+ return nftnl_set_elem_foreach(s, list_setelem_debug_cb, ctx);
+ }
+
+ return nftnl_set_elem_foreach(s, list_setelem_cb, ctx);
+}
+
int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
struct set *set)
{
@@ -1199,7 +1275,7 @@ int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
ctx->set = set;
set->init = set_expr_alloc(&internal_location, set);
- nftnl_set_elem_foreach(nls, list_setelem_cb, ctx);
+ list_setelements(nls, ctx);
if (set->flags & NFT_SET_INTERVAL && set->desc.field_count > 1)
concat_range_aggregate(set->init);
@@ -1215,7 +1291,7 @@ int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
}
int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
- const struct location *loc, struct table *table,
+ const struct location *loc, struct set *cache_set,
struct set *set, struct expr *init)
{
struct nftnl_set *nls, *nls_out = NULL;
@@ -1243,12 +1319,12 @@ int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
ctx->set = set;
set->init = set_expr_alloc(loc, set);
- nftnl_set_elem_foreach(nls_out, list_setelem_cb, ctx);
+ list_setelements(nls_out, ctx);
if (set->flags & NFT_SET_INTERVAL && set->desc.field_count > 1)
concat_range_aggregate(set->init);
else if (set->flags & NFT_SET_INTERVAL)
- err = get_set_decompose(table, set);
+ err = get_set_decompose(cache_set, set);
else
list_expr_sort(&ctx->set->init->expressions);
@@ -1270,11 +1346,33 @@ void netlink_dump_obj(struct nftnl_obj *nln, struct netlink_ctx *ctx)
fprintf(fp, "\n");
}
+static int obj_parse_udata_cb(const struct nftnl_udata *attr, void *data)
+{
+ unsigned char *value = nftnl_udata_get(attr);
+ uint8_t type = nftnl_udata_type(attr);
+ const struct nftnl_udata **tb = data;
+ uint8_t len = nftnl_udata_len(attr);
+
+ switch (type) {
+ case NFTNL_UDATA_OBJ_COMMENT:
+ if (value[len - 1] != '\0')
+ return -1;
+ break;
+ default:
+ return 0;
+ }
+ tb[type] = attr;
+ return 0;
+}
+
struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
struct nftnl_obj *nlo)
{
+ const struct nftnl_udata *ud[NFTNL_UDATA_OBJ_MAX + 1] = {};
+ const char *udata;
struct obj *obj;
uint32_t type;
+ uint32_t ulen;
obj = obj_alloc(&netlink_location);
obj->handle.family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY);
@@ -1284,6 +1382,15 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
xstrdup(nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME));
obj->handle.handle.id =
nftnl_obj_get_u64(nlo, NFTNL_OBJ_HANDLE);
+ if (nftnl_obj_is_set(nlo, NFTNL_OBJ_USERDATA)) {
+ udata = nftnl_obj_get_data(nlo, NFTNL_OBJ_USERDATA, &ulen);
+ if (nftnl_udata_parse(udata, ulen, obj_parse_udata_cb, ud) < 0) {
+ netlink_io_error(ctx, NULL, "Cannot parse userdata");
+ return NULL;
+ }
+ if (ud[NFTNL_UDATA_OBJ_COMMENT])
+ obj->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_OBJ_COMMENT]));
+ }
type = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE);
switch (type) {
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 7f7ad262..43d7ff82 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -27,6 +27,7 @@
#include <erec.h>
#include <sys/socket.h>
#include <libnftnl/udata.h>
+#include <cache.h>
#include <xt.h>
static int netlink_parse_expr(const struct nftnl_expr *nle,
@@ -163,6 +164,24 @@ err:
return NULL;
}
+static void netlink_parse_chain_verdict(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ struct expr *expr,
+ enum nft_verdicts verdict)
+{
+ char chain_name[NFT_CHAIN_MAXNAMELEN] = {};
+ struct chain *chain;
+
+ expr_chain_export(expr->chain, chain_name);
+ chain = chain_binding_lookup(ctx->table, chain_name);
+ if (chain) {
+ ctx->stmt = chain_stmt_alloc(loc, chain, verdict);
+ expr_free(expr);
+ } else {
+ ctx->stmt = verdict_stmt_alloc(loc, expr);
+ }
+}
+
static void netlink_parse_immediate(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
@@ -182,12 +201,23 @@ static void netlink_parse_immediate(struct netlink_parse_ctx *ctx,
}
dreg = netlink_parse_register(nle, NFTNL_EXPR_IMM_DREG);
-
expr = netlink_alloc_data(loc, &nld, dreg);
- if (dreg == NFT_REG_VERDICT)
- ctx->stmt = verdict_stmt_alloc(loc, expr);
- else
+
+ if (dreg == NFT_REG_VERDICT) {
+ switch (expr->verdict) {
+ case NFT_JUMP:
+ netlink_parse_chain_verdict(ctx, loc, expr, NFT_JUMP);
+ break;
+ case NFT_GOTO:
+ netlink_parse_chain_verdict(ctx, loc, expr, NFT_GOTO);
+ break;
+ default:
+ ctx->stmt = verdict_stmt_alloc(loc, expr);
+ break;
+ }
+ } else {
netlink_set_register(ctx, dreg, expr);
+ }
}
static void netlink_parse_xfrm(struct netlink_parse_ctx *ctx,
@@ -901,7 +931,11 @@ static void netlink_parse_log(struct netlink_parse_ctx *ctx,
stmt = log_stmt_alloc(loc);
prefix = nftnl_expr_get_str(nle, NFTNL_EXPR_LOG_PREFIX);
if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_PREFIX)) {
- stmt->log.prefix = xstrdup(prefix);
+ stmt->log.prefix = constant_expr_alloc(&internal_location,
+ &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ (strlen(prefix) + 1) * BITS_PER_BYTE,
+ prefix);
stmt->log.flags |= STMT_LOG_PREFIX;
}
if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_GROUP)) {
@@ -1594,12 +1628,14 @@ static void netlink_parse_objref(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
}
-static const struct {
+struct expr_handler {
const char *name;
void (*parse)(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle);
-} netlink_parsers[] = {
+};
+
+static const struct expr_handler netlink_parsers[] = {
{ .name = "immediate", .parse = netlink_parse_immediate },
{ .name = "cmp", .parse = netlink_parse_cmp },
{ .name = "lookup", .parse = netlink_parse_lookup },
@@ -1640,25 +1676,48 @@ static const struct {
{ .name = "synproxy", .parse = netlink_parse_synproxy },
};
+static const struct expr_handler **expr_handle_ht;
+
+#define NFT_EXPR_HSIZE 4096
+
+void expr_handler_init(void)
+{
+ unsigned int i;
+ uint32_t hash;
+
+ expr_handle_ht = calloc(NFT_EXPR_HSIZE, sizeof(expr_handle_ht));
+ if (!expr_handle_ht)
+ memory_allocation_error();
+
+ for (i = 0; i < array_size(netlink_parsers); i++) {
+ hash = djb_hash(netlink_parsers[i].name) % NFT_EXPR_HSIZE;
+ assert(expr_handle_ht[hash] == NULL);
+ expr_handle_ht[hash] = &netlink_parsers[i];
+ }
+}
+
+void expr_handler_exit(void)
+{
+ xfree(expr_handle_ht);
+}
+
static int netlink_parse_expr(const struct nftnl_expr *nle,
struct netlink_parse_ctx *ctx)
{
const char *type = nftnl_expr_get_str(nle, NFTNL_EXPR_NAME);
struct location loc;
- unsigned int i;
+ uint32_t hash;
memset(&loc, 0, sizeof(loc));
loc.indesc = &indesc_netlink;
loc.nle = nle;
- for (i = 0; i < array_size(netlink_parsers); i++) {
- if (strcmp(type, netlink_parsers[i].name))
- continue;
- netlink_parsers[i].parse(ctx, &loc, nle);
- return 0;
- }
+ hash = djb_hash(type) % NFT_EXPR_HSIZE;
+ if (expr_handle_ht[hash])
+ expr_handle_ht[hash]->parse(ctx, &loc, nle);
+ else
+ netlink_error(ctx, &loc, "unknown expression type '%s'", type);
- netlink_error(ctx, &loc, "unknown expression type '%s'", type);
return 0;
}
@@ -1682,13 +1741,19 @@ struct stmt *netlink_parse_set_expr(const struct set *set,
const struct nftnl_expr *nle)
{
struct netlink_parse_ctx ctx, *pctx = &ctx;
+ struct handle h = {};
- pctx->rule = rule_alloc(&netlink_location, &set->handle);
+ handle_merge(&h, &set->handle);
+ pctx->rule = rule_alloc(&netlink_location, &h);
pctx->table = table_lookup(&set->handle, cache);
assert(pctx->table != NULL);
if (netlink_parse_expr(nle, pctx) < 0)
return NULL;
+
+ init_list_head(&pctx->rule->stmts);
+ rule_free(pctx->rule);
+
return pctx->stmt;
}
@@ -2063,7 +2128,7 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *e
expr_free(binop);
} else if (binop->left->dtype->flags & DTYPE_F_PREFIX &&
- binop->op == OP_AND &&
+ binop->op == OP_AND && expr->right->etype == EXPR_VALUE &&
expr_mask_is_prefix(binop->right)) {
expr->left = expr_get(binop->left);
expr->right = prefix_expr_alloc(&expr->location,
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 08f7f89f..38f66be8 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -10,6 +10,7 @@
*/
#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_log.h>
#include <string.h>
#include <rule.h>
@@ -23,11 +24,34 @@
#include <linux/netfilter.h>
#include <libnftnl/udata.h>
+struct nft_expr_loc *nft_expr_loc_find(const struct nftnl_expr *nle,
+ struct netlink_linearize_ctx *ctx)
+{
+ struct nft_expr_loc *eloc;
+ uint32_t hash;
-struct netlink_linearize_ctx {
- struct nftnl_rule *nlr;
- unsigned int reg_low;
-};
+ hash = (uint64_t)nle % NFT_EXPR_LOC_HSIZE;
+ list_for_each_entry(eloc, &ctx->expr_loc_htable[hash], hlist) {
+ if (eloc->nle == nle)
+ return eloc;
+ }
+
+ return NULL;
+}
+
+static void nft_expr_loc_add(const struct nftnl_expr *nle,
+ const struct location *loc,
+ struct netlink_linearize_ctx *ctx)
+{
+ struct nft_expr_loc *eloc;
+ uint32_t hash;
+
+ eloc = xmalloc(sizeof(*eloc));
+ eloc->nle = nle;
+ eloc->loc = loc;
+ hash = (uint64_t)nle % NFT_EXPR_LOC_HSIZE;
+ list_add_tail(&eloc->hlist, &ctx->expr_loc_htable[hash]);
+}
static void netlink_put_register(struct nftnl_expr *nle,
uint32_t attr, uint32_t reg)
@@ -104,6 +128,14 @@ static void netlink_gen_concat(struct netlink_linearize_ctx *ctx,
}
}
+static void nft_rule_add_expr(struct netlink_linearize_ctx *ctx,
+ struct nftnl_expr *nle,
+ const struct location *loc)
+{
+ nft_expr_loc_add(nle, loc, ctx);
+ nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
static void netlink_gen_fib(struct netlink_linearize_ctx *ctx,
const struct expr *expr,
enum nft_registers dreg)
@@ -115,7 +147,7 @@ static void netlink_gen_fib(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_FIB_RESULT, expr->fib.result);
nftnl_expr_set_u32(nle, NFTNL_EXPR_FIB_FLAGS, expr->fib.flags);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_hash(struct netlink_linearize_ctx *ctx,
@@ -143,7 +175,7 @@ static void netlink_gen_hash(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_SEED, expr->hash.seed);
nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_OFFSET, expr->hash.offset);
nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_TYPE, expr->hash.type);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
@@ -161,7 +193,7 @@ static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_LEN,
div_round_up(expr->len, BITS_PER_BYTE));
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_exthdr(struct netlink_linearize_ctx *ctx,
@@ -180,7 +212,7 @@ static void netlink_gen_exthdr(struct netlink_linearize_ctx *ctx,
div_round_up(expr->len, BITS_PER_BYTE));
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op);
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_FLAGS, expr->exthdr.flags);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_meta(struct netlink_linearize_ctx *ctx,
@@ -192,7 +224,7 @@ static void netlink_gen_meta(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("meta");
netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_rt(struct netlink_linearize_ctx *ctx,
@@ -204,7 +236,7 @@ static void netlink_gen_rt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("rt");
netlink_put_register(nle, NFTNL_EXPR_RT_DREG, dreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_RT_KEY, expr->rt.key);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_socket(struct netlink_linearize_ctx *ctx,
@@ -216,7 +248,7 @@ static void netlink_gen_socket(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("socket");
netlink_put_register(nle, NFTNL_EXPR_SOCKET_DREG, dreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_SOCKET_KEY, expr->socket.key);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_osf(struct netlink_linearize_ctx *ctx,
@@ -229,7 +261,7 @@ static void netlink_gen_osf(struct netlink_linearize_ctx *ctx,
netlink_put_register(nle, NFTNL_EXPR_OSF_DREG, dreg);
nftnl_expr_set_u8(nle, NFTNL_EXPR_OSF_TTL, expr->osf.ttl);
nftnl_expr_set_u32(nle, NFTNL_EXPR_OSF_FLAGS, expr->osf.flags);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_numgen(struct netlink_linearize_ctx *ctx,
@@ -243,7 +275,7 @@ static void netlink_gen_numgen(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_TYPE, expr->numgen.type);
nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_MODULUS, expr->numgen.mod);
nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_OFFSET, expr->numgen.offset);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_ct(struct netlink_linearize_ctx *ctx,
@@ -259,7 +291,7 @@ static void netlink_gen_ct(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u8(nle, NFTNL_EXPR_CT_DIR,
expr->ct.direction);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_map(struct netlink_linearize_ctx *ctx,
@@ -297,7 +329,7 @@ static void netlink_gen_map(struct netlink_linearize_ctx *ctx,
if (dreg == NFT_REG_VERDICT)
release_register(ctx, expr->map);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx,
@@ -323,7 +355,7 @@ static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_LOOKUP_FLAGS, NFT_LOOKUP_F_INV);
release_register(ctx, expr->left);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static enum nft_cmp_ops netlink_gen_cmp_op(enum ops op)
@@ -369,7 +401,7 @@ static struct expr *netlink_gen_prefix(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, nld.len);
nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld.value, nld.len);
nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &zero.value, zero.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
return expr->right->prefix;
}
@@ -399,7 +431,7 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx,
netlink_gen_data(range->right, &nld);
nftnl_expr_set(nle, NFTNL_EXPR_RANGE_TO_DATA,
nld.value, nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
break;
case OP_EQ:
case OP_IMPLICIT:
@@ -409,7 +441,7 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx,
netlink_gen_cmp_op(OP_GTE));
netlink_gen_data(range->left, &nld);
nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
nle = alloc_nft_expr("cmp");
netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
@@ -417,7 +449,7 @@ static void netlink_gen_range(struct netlink_linearize_ctx *ctx,
netlink_gen_cmp_op(OP_LTE));
netlink_gen_data(range->right, &nld);
nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
break;
default:
BUG("invalid range operation %u\n", expr->op);
@@ -454,13 +486,13 @@ static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len);
nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld2.value, nld2.len);
nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &nld.value, nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
nle = alloc_nft_expr("cmp");
netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_NEQ);
nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
mpz_clear(zero);
release_register(ctx, expr->left);
@@ -533,7 +565,7 @@ static void netlink_gen_relational(struct netlink_linearize_ctx *ctx,
nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, len);
release_register(ctx, expr->left);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void combine_binop(mpz_t mask, mpz_t xor, const mpz_t m, const mpz_t x)
@@ -569,7 +601,7 @@ static void netlink_gen_shift(struct netlink_linearize_ctx *ctx,
nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_DATA, nld.value,
nld.len);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx,
@@ -639,7 +671,7 @@ static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx,
mpz_clear(xor);
mpz_clear(mask);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_binop(struct netlink_linearize_ctx *ctx,
@@ -694,15 +726,16 @@ static void netlink_gen_unary(struct netlink_linearize_ctx *ctx,
byte_size);
nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_OP,
netlink_gen_unary_op(expr->op));
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx,
const struct expr *expr,
enum nft_registers dreg)
{
- struct nftnl_expr *nle;
+ const struct location *loc = &expr->location;
struct nft_data_linearize nld;
+ struct nftnl_expr *nle;
nle = alloc_nft_expr("immediate");
netlink_put_register(nle, NFTNL_EXPR_IMM_DREG, dreg);
@@ -712,17 +745,20 @@ static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx,
nftnl_expr_set(nle, NFTNL_EXPR_IMM_DATA, nld.value, nld.len);
break;
case EXPR_VERDICT:
- if ((expr->chain != NULL) &&
- !nftnl_expr_is_set(nle, NFTNL_EXPR_IMM_CHAIN)) {
+ if (expr->chain) {
nftnl_expr_set_str(nle, NFTNL_EXPR_IMM_CHAIN,
nld.chain);
+ loc = &expr->chain->location;
+ } else if (expr->chain_id) {
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_IMM_CHAIN_ID,
+ nld.chain_id);
}
nftnl_expr_set_u32(nle, NFTNL_EXPR_IMM_VERDICT, nld.verdict);
break;
default:
break;
}
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, loc);
}
static void netlink_gen_xfrm(struct netlink_linearize_ctx *ctx,
@@ -736,7 +772,7 @@ static void netlink_gen_xfrm(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_XFRM_KEY, expr->xfrm.key);
nftnl_expr_set_u8(nle, NFTNL_EXPR_XFRM_DIR, expr->xfrm.direction);
nftnl_expr_set_u32(nle, NFTNL_EXPR_XFRM_SPNUM, expr->xfrm.spnum);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_expr(struct netlink_linearize_ctx *ctx,
@@ -819,7 +855,7 @@ static void netlink_gen_objref_stmt(struct netlink_linearize_ctx *ctx,
default:
BUG("unsupported expression %u\n", expr->etype);
}
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static struct nftnl_expr *netlink_gen_connlimit_stmt(const struct stmt *stmt)
@@ -938,7 +974,7 @@ static void netlink_gen_exthdr_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_LEN,
div_round_up(expr->len, BITS_PER_BYTE));
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
@@ -971,7 +1007,7 @@ static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
expr->len / BITS_PER_BYTE);
if (csum_off) {
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_CSUM_TYPE,
- NFT_PAYLOAD_CSUM_INET);
+ desc->checksum_type);
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_CSUM_OFFSET,
csum_off / BITS_PER_BYTE);
}
@@ -980,7 +1016,7 @@ static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_FLAGS,
NFT_PAYLOAD_L4CSUM_PSEUDOHDR);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &expr->location);
}
static void netlink_gen_meta_stmt(struct netlink_linearize_ctx *ctx,
@@ -996,7 +1032,7 @@ static void netlink_gen_meta_stmt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("meta");
netlink_put_register(nle, NFTNL_EXPR_META_SREG, sreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, stmt->meta.key);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
@@ -1006,8 +1042,10 @@ static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("log");
if (stmt->log.prefix != NULL) {
- nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX,
- stmt->log.prefix);
+ char prefix[NF_LOG_PREFIXLEN] = {};
+
+ expr_to_string(stmt->log.prefix, prefix);
+ nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, prefix);
}
if (stmt->log.flags & STMT_LOG_GROUP) {
nftnl_expr_set_u16(nle, NFTNL_EXPR_LOG_GROUP, stmt->log.group);
@@ -1025,7 +1063,7 @@ static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_LOG_FLAGS,
stmt->log.logflags);
}
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx,
@@ -1039,7 +1077,7 @@ static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u8(nle, NFTNL_EXPR_REJECT_CODE,
stmt->reject.icmp_code);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static unsigned int nat_addrlen(uint8_t family)
@@ -1170,7 +1208,7 @@ static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
registers--;
}
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_tproxy_stmt(struct netlink_linearize_ctx *ctx,
@@ -1209,7 +1247,7 @@ static void netlink_gen_tproxy_stmt(struct netlink_linearize_ctx *ctx,
registers--;
}
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_synproxy_stmt(struct netlink_linearize_ctx *ctx,
@@ -1224,7 +1262,7 @@ static void netlink_gen_synproxy_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_SYNPROXY_FLAGS,
stmt->synproxy.flags);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx,
@@ -1255,7 +1293,7 @@ static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx,
if (stmt->dup.to != NULL)
release_register(ctx, stmt->dup.to);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_fwd_stmt(struct netlink_linearize_ctx *ctx,
@@ -1282,7 +1320,7 @@ static void netlink_gen_fwd_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_FWD_NFPROTO,
stmt->fwd.family);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_queue_stmt(struct netlink_linearize_ctx *ctx,
@@ -1307,7 +1345,7 @@ static void netlink_gen_queue_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_FLAGS,
stmt->queue.flags);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
mpz_clear(low);
mpz_clear(high);
@@ -1330,7 +1368,7 @@ static void netlink_gen_ct_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u8(nle, NFTNL_EXPR_CT_DIR,
stmt->ct.direction);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_notrack_stmt(struct netlink_linearize_ctx *ctx,
@@ -1339,7 +1377,7 @@ static void netlink_gen_notrack_stmt(struct netlink_linearize_ctx *ctx,
struct nftnl_expr *nle;
nle = alloc_nft_expr("notrack");
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_flow_offload_stmt(struct netlink_linearize_ctx *ctx,
@@ -1350,7 +1388,7 @@ static void netlink_gen_flow_offload_stmt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("flow_offload");
nftnl_expr_set_str(nle, NFTNL_EXPR_FLOW_TABLE_NAME,
stmt->flow.table_name);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
@@ -1372,7 +1410,7 @@ static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_OP, stmt->set.op);
nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name);
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
if (stmt->set.stmt)
nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
@@ -1408,7 +1446,7 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
netlink_gen_stmt_stateful(stmt->map.stmt), 0);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
}
static void netlink_gen_meter_stmt(struct netlink_linearize_ctx *ctx,
@@ -1439,7 +1477,13 @@ static void netlink_gen_meter_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR,
netlink_gen_stmt_stateful(stmt->meter.stmt), 0);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
+}
+
+static void netlink_gen_chain_stmt(struct netlink_linearize_ctx *ctx,
+ const struct stmt *stmt)
+{
+ return netlink_gen_expr(ctx, stmt->chain.expr, NFT_REG_VERDICT);
}
static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
@@ -1485,7 +1529,7 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
case STMT_LIMIT:
case STMT_QUOTA:
nle = netlink_gen_stmt_stateful(stmt);
- nftnl_rule_add_expr(ctx->nlr, nle);
+ nft_rule_add_expr(ctx, nle, &stmt->location);
break;
case STMT_NOTRACK:
return netlink_gen_notrack_stmt(ctx, stmt);
@@ -1495,23 +1539,47 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
return netlink_gen_objref_stmt(ctx, stmt);
case STMT_MAP:
return netlink_gen_map_stmt(ctx, stmt);
+ case STMT_CHAIN:
+ return netlink_gen_chain_stmt(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
}
-void netlink_linearize_rule(struct netlink_ctx *ctx, struct nftnl_rule *nlr,
- const struct rule *rule)
+void netlink_linearize_init(struct netlink_linearize_ctx *lctx,
+ struct nftnl_rule *nlr)
{
- struct netlink_linearize_ctx lctx;
- const struct stmt *stmt;
+ int i;
+
+ memset(lctx, 0, sizeof(*lctx));
+ lctx->reg_low = NFT_REG_1;
+ lctx->nlr = nlr;
+ lctx->expr_loc_htable =
+ xmalloc(sizeof(struct list_head) * NFT_EXPR_LOC_HSIZE);
+ for (i = 0; i < NFT_EXPR_LOC_HSIZE; i++)
+ init_list_head(&lctx->expr_loc_htable[i]);
+}
- memset(&lctx, 0, sizeof(lctx));
- lctx.reg_low = NFT_REG_1;
- lctx.nlr = nlr;
+void netlink_linearize_fini(struct netlink_linearize_ctx *lctx)
+{
+ struct nft_expr_loc *eloc, *next;
+ int i;
+
+ for (i = 0; i < NFT_EXPR_LOC_HSIZE; i++) {
+ list_for_each_entry_safe(eloc, next, &lctx->expr_loc_htable[i], hlist)
+ xfree(eloc);
+ }
+ xfree(lctx->expr_loc_htable);
+}
+
+void netlink_linearize_rule(struct netlink_ctx *ctx,
+ const struct rule *rule,
+ struct netlink_linearize_ctx *lctx)
+{
+ const struct stmt *stmt;
list_for_each_entry(stmt, &rule->stmts, list)
- netlink_gen_stmt(&lctx, stmt);
+ netlink_gen_stmt(lctx, stmt);
if (rule->comment) {
struct nftnl_udata_buf *udata;
@@ -1523,12 +1591,12 @@ void netlink_linearize_rule(struct netlink_ctx *ctx, struct nftnl_rule *nlr,
if (!nftnl_udata_put_strz(udata, NFTNL_UDATA_RULE_COMMENT,
rule->comment))
memory_allocation_error();
- nftnl_rule_set_data(nlr, NFTNL_RULE_USERDATA,
+ nftnl_rule_set_data(lctx->nlr, NFTNL_RULE_USERDATA,
nftnl_udata_buf_data(udata),
nftnl_udata_buf_len(udata));
nftnl_udata_buf_free(udata);
}
- netlink_dump_rule(nlr, ctx);
+ netlink_dump_rule(lctx->nlr, ctx);
}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 8e937ca3..9bf4f71f 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -121,6 +121,17 @@ static struct expr *handle_concat_expr(const struct location *loc,
return expr;
}
+static bool already_set(const void *attr, const struct location *loc,
+ struct parser_state *state)
+{
+ if (!attr)
+ return false;
+
+ erec_queue(error(loc, "You can only specify this once. This statement is duplicated."),
+ state->msgs);
+ return true;
+}
+
#define YYLLOC_DEFAULT(Current, Rhs, N) location_update(&Current, Rhs, N)
#define symbol_value(loc, str) \
@@ -213,6 +224,7 @@ int nft_lex(void *, void *, void *);
%token SOCKET "socket"
%token TRANSPARENT "transparent"
+%token WILDCARD "wildcard"
%token TPROXY "tproxy"
@@ -594,8 +606,8 @@ int nft_lex(void *, void *, void *);
%type <table> table_block_alloc table_block
%destructor { close_scope(state); table_free($$); } table_block_alloc
-%type <chain> chain_block_alloc chain_block
-%destructor { close_scope(state); chain_free($$); } chain_block_alloc
+%type <chain> chain_block_alloc chain_block subchain_block
+%destructor { close_scope(state); chain_free($$); } chain_block_alloc subchain_block
%type <rule> rule rule_alloc
%destructor { rule_free($$); } rule
@@ -642,7 +654,9 @@ int nft_lex(void *, void *, void *);
%destructor { stmt_free($$); } tproxy_stmt
%type <stmt> synproxy_stmt synproxy_stmt_alloc
%destructor { stmt_free($$); } synproxy_stmt synproxy_stmt_alloc
-
+%type <stmt> chain_stmt
+%destructor { stmt_free($$); } chain_stmt
+%type <val> chain_stmt_type
%type <stmt> queue_stmt queue_stmt_alloc
%destructor { stmt_free($$); } queue_stmt queue_stmt_alloc
@@ -860,6 +874,7 @@ common_block : INCLUDE QUOTED_STRING stmt_separator
if (symbol_lookup(scope, $2) != NULL) {
erec_queue(error(&@2, "redefinition of symbol '%s'", $2),
state->msgs);
+ expr_free($4);
xfree($2);
YYERROR;
}
@@ -1002,10 +1017,18 @@ add_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, $3);
}
+ | COUNTER obj_spec counter_obj '{' counter_block '}'
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, $3);
+ }
| QUOTA obj_spec quota_obj quota_config
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_QUOTA, &$2, &@$, $3);
}
+ | QUOTA obj_spec quota_obj '{' quota_block '}'
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_QUOTA, &$2, &@$, $3);
+ }
| CT HELPER obj_spec ct_obj_alloc '{' ct_helper_block '}'
{
$$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_HELPER, &$3, &@$, $4);
@@ -1022,14 +1045,26 @@ add_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3);
}
+ | LIMIT obj_spec limit_obj '{' limit_block '}'
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3);
+ }
| SECMARK obj_spec secmark_obj secmark_config
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3);
}
+ | SECMARK obj_spec secmark_obj '{' secmark_block '}'
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3);
+ }
| SYNPROXY obj_spec synproxy_obj synproxy_config
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
}
+ | SYNPROXY obj_spec synproxy_obj '{' synproxy_block '}'
+ {
+ $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
+ }
;
replace_cmd : RULE ruleid_spec rule
@@ -1179,6 +1214,13 @@ delete_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
}
+ | FLOWTABLE flowtable_spec flowtable_block_alloc
+ '{' flowtable_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
+ }
| COUNTER obj_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_COUNTER, &$2, &@$, NULL);
@@ -1523,6 +1565,14 @@ table_options : FLAGS STRING
YYERROR;
}
}
+ | comment_spec
+ {
+ if (already_set($<table>0->comment, &@$, state)) {
+ xfree($1);
+ YYERROR;
+ }
+ $<table>0->comment = $1;
+ }
;
table_block : /* empty */ { $$ = $<table>-1; }
@@ -1673,6 +1723,23 @@ chain_block : /* empty */ { $$ = $<chain>-1; }
list_add_tail(&$2->list, &$1->rules);
$$ = $1;
}
+ | chain_block comment_spec stmt_separator
+ {
+ if (already_set($1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $1->comment = $2;
+ }
+ ;
+
+subchain_block : /* empty */ { $$ = $<chain>-1; }
+ | subchain_block stmt_separator
+ | subchain_block rule stmt_separator
+ {
+ list_add_tail(&$2->list, &$1->rules);
+ $$ = $1;
+ }
;
typeof_expr : primary_expr
@@ -1749,6 +1816,15 @@ set_block : /* empty */ { $$ = $<set>-1; }
$$ = $1;
}
| set_block set_mechanism stmt_separator
+ | set_block comment_spec stmt_separator
+ {
+ if (already_set($1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $1->comment = $2;
+ $$ = $1;
+ }
;
set_block_expr : set_expr
@@ -1872,6 +1948,15 @@ map_block : /* empty */ { $$ = $<set>-1; }
$1->init = $4;
$$ = $1;
}
+ | map_block comment_spec stmt_separator
+ {
+ if (already_set($1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $1->comment = $2;
+ $$ = $1;
+ }
| map_block set_mechanism stmt_separator
;
@@ -1927,6 +2012,11 @@ flowtable_expr : '{' flowtable_list_expr '}'
$2->location = @$;
$$ = $2;
}
+ | variable_expr
+ {
+ $1->location = @$;
+ $$ = $1;
+ }
;
flowtable_list_expr : flowtable_expr_member
@@ -1949,6 +2039,11 @@ flowtable_expr_member : STRING
strlen($1) * BITS_PER_BYTE, $1);
xfree($1);
}
+ | variable_expr
+ {
+ datatype_set($1->sym->expr, &ifname_type);
+ $$ = $1;
+ }
;
data_type_atom_expr : type_identifier
@@ -1995,6 +2090,14 @@ counter_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | counter_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
quota_block : /* empty */ { $$ = $<obj>-1; }
@@ -2004,6 +2107,14 @@ quota_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | quota_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
ct_helper_block : /* empty */ { $$ = $<obj>-1; }
@@ -2013,6 +2124,14 @@ ct_helper_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | ct_helper_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
ct_timeout_block : /*empty */
@@ -2026,6 +2145,14 @@ ct_timeout_block : /*empty */
{
$$ = $1;
}
+ | ct_timeout_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
ct_expect_block : /*empty */ { $$ = $<obj>-1; }
@@ -2035,6 +2162,14 @@ ct_expect_block : /*empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | ct_expect_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
limit_block : /* empty */ { $$ = $<obj>-1; }
@@ -2044,6 +2179,14 @@ limit_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | limit_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
secmark_block : /* empty */ { $$ = $<obj>-1; }
@@ -2053,6 +2196,14 @@ secmark_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | secmark_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
synproxy_block : /* empty */ { $$ = $<obj>-1; }
@@ -2062,6 +2213,14 @@ synproxy_block : /* empty */ { $$ = $<obj>-1; }
{
$$ = $1;
}
+ | synproxy_block comment_spec
+ {
+ if (already_set($<obj>1->comment, &@2, state)) {
+ xfree($2);
+ YYERROR;
+ }
+ $<obj>1->comment = $2;
+ }
;
type_identifier : STRING { $$ = $1; }
@@ -2129,7 +2288,6 @@ extended_prio_spec : int_num
{
struct prio_spec spec = {0};
- datatype_set($1->sym->expr, &priority_type);
spec.expr = $1;
$$ = spec;
}
@@ -2188,6 +2346,12 @@ dev_spec : DEVICE string
compound_expr_add($$, expr);
}
+ | DEVICE variable_expr
+ {
+ datatype_set($2->sym->expr, &ifname_type);
+ $$ = compound_expr_alloc(&@$, EXPR_LIST);
+ compound_expr_add($$, $2);
+ }
| DEVICES '=' flowtable_expr
{
$$ = $3;
@@ -2520,6 +2684,20 @@ stmt : verdict_stmt
| set_stmt
| map_stmt
| synproxy_stmt
+ | chain_stmt
+ ;
+
+chain_stmt_type : JUMP { $$ = NFT_JUMP; }
+ | GOTO { $$ = NFT_GOTO; }
+ ;
+
+chain_stmt : chain_stmt_type chain_block_alloc '{' subchain_block '}'
+ {
+ $2->location = @2;
+ close_scope(state);
+ $4->location = @4;
+ $$ = chain_stmt_alloc(&@$, $4, $1);
+ }
;
verdict_stmt : verdict_expr
@@ -2629,7 +2807,127 @@ log_args : log_arg
log_arg : PREFIX string
{
- $<stmt>0->log.prefix = $2;
+ struct scope *scope = current_scope(state);
+ bool done = false, another_var = false;
+ char *start, *end, scratch = '\0';
+ struct expr *expr, *item;
+ struct symbol *sym;
+ enum {
+ PARSE_TEXT,
+ PARSE_VAR,
+ } prefix_state;
+
+ /* No variables in log prefix, skip. */
+ if (!strchr($2, '$')) {
+ expr = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ (strlen($2) + 1) * BITS_PER_BYTE, $2);
+ xfree($2);
+ $<stmt>0->log.prefix = expr;
+ $<stmt>0->log.flags |= STMT_LOG_PREFIX;
+ break;
+ }
+
+ /* Parse variables in log prefix string using a
+ * state machine parser with two states. This
+ * parser creates list of expressions composed
+ * of constant and variable expressions.
+ */
+ expr = compound_expr_alloc(&@$, EXPR_LIST);
+
+ start = (char *)$2;
+
+ if (*start != '$') {
+ prefix_state = PARSE_TEXT;
+ } else {
+ prefix_state = PARSE_VAR;
+ start++;
+ }
+ end = start;
+
+ /* Not nice, but works. */
+ while (!done) {
+ switch (prefix_state) {
+ case PARSE_TEXT:
+ while (*end != '\0' && *end != '$')
+ end++;
+
+ if (*end == '\0')
+ done = true;
+
+ *end = '\0';
+ item = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ (strlen(start) + 1) * BITS_PER_BYTE,
+ start);
+ compound_expr_add(expr, item);
+
+ if (done)
+ break;
+
+ start = end + 1;
+ end = start;
+
+ /* fall through */
+ case PARSE_VAR:
+ while (isalnum(*end) || *end == '_')
+ end++;
+
+ if (*end == '\0')
+ done = true;
+ else if (*end == '$')
+ another_var = true;
+ else
+ scratch = *end;
+
+ *end = '\0';
+
+ sym = symbol_get(scope, start);
+ if (!sym) {
+ sym = symbol_lookup_fuzzy(scope, start);
+ if (sym) {
+ erec_queue(error(&@2, "unknown identifier '%s'; "
+ "did you mean identifier ‘%s’?",
+ start, sym->identifier),
+ state->msgs);
+ } else {
+ erec_queue(error(&@2, "unknown identifier '%s'",
+ start),
+ state->msgs);
+ }
+ expr_free(expr);
+ xfree($2);
+ YYERROR;
+ }
+ item = variable_expr_alloc(&@$, scope, sym);
+ compound_expr_add(expr, item);
+
+ if (done)
+ break;
+
+ /* Restore original byte after
+ * symbol lookup.
+ */
+ if (scratch) {
+ *end = scratch;
+ scratch = '\0';
+ }
+
+ start = end;
+ if (another_var) {
+ another_var = false;
+ start++;
+ prefix_state = PARSE_VAR;
+ } else {
+ prefix_state = PARSE_TEXT;
+ }
+ end = start;
+ break;
+ }
+ }
+
+ xfree($2);
+ $<stmt>0->log.prefix = expr;
$<stmt>0->log.flags |= STMT_LOG_PREFIX;
}
| GROUP NUM
@@ -3765,6 +4063,10 @@ set_elem_option : TIMEOUT time_spec
}
| comment_spec
{
+ if (already_set($<expr>0->comment, &@1, state)) {
+ xfree($1);
+ YYERROR;
+ }
$<expr>0->comment = $1;
}
;
@@ -3799,6 +4101,10 @@ set_elem_expr_option : TIMEOUT time_spec
}
| comment_spec
{
+ if (already_set($<expr>0->comment, &@1, state)) {
+ xfree($1);
+ YYERROR;
+ }
$<expr>0->comment = $1;
}
;
@@ -3813,6 +4119,16 @@ set_rhs_expr : concat_rhs_expr
initializer_expr : rhs_expr
| list_rhs_expr
+ | '{' '}' { $$ = compound_expr_alloc(&@$, EXPR_SET); }
+ | DASH NUM
+ {
+ int32_t num = -$2;
+
+ $$ = constant_expr_alloc(&@$, &integer_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(num) * BITS_PER_BYTE,
+ &num);
+ }
;
counter_config : PACKETS NUM BYTES NUM
@@ -4403,6 +4719,7 @@ socket_expr : SOCKET socket_key
socket_key : TRANSPARENT { $$ = NFT_SOCKET_TRANSPARENT; }
| MARK { $$ = NFT_SOCKET_MARK; }
+ | WILDCARD { $$ = NFT_SOCKET_WILDCARD; }
;
offset_opt : /* empty */ { $$ = 0; }
diff --git a/src/parser_json.c b/src/parser_json.c
index 9fdef691..ac89166e 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -427,6 +427,8 @@ static struct expr *json_parse_socket_expr(struct json_ctx *ctx,
keyval = NFT_SOCKET_TRANSPARENT;
else if (!strcmp(key, "mark"))
keyval = NFT_SOCKET_MARK;
+ else if (!strcmp(key, "wildcard"))
+ keyval = NFT_SOCKET_WILDCARD;
if (keyval == -1) {
json_error(ctx, "Invalid socket key value.");
@@ -2159,7 +2161,9 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
stmt = log_stmt_alloc(int_loc);
if (!json_unpack(value, "{s:s}", "prefix", &tmpstr)) {
- stmt->log.prefix = xstrdup(tmpstr);
+ stmt->log.prefix = constant_expr_alloc(int_loc, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ (strlen(tmpstr) + 1) * BITS_PER_BYTE, tmpstr);
stmt->log.flags |= STMT_LOG_PREFIX;
}
if (!json_unpack(value, "{s:i}", "group", &tmp)) {
diff --git a/src/payload.c b/src/payload.c
index 29242537..ca422d5b 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -80,9 +80,10 @@ static void payload_expr_clone(struct expr *new, const struct expr *expr)
* Update protocol context for relational payload expressions.
*/
static void payload_expr_pctx_update(struct proto_ctx *ctx,
- const struct expr *expr)
+ const struct location *loc,
+ const struct expr *left,
+ const struct expr *right)
{
- const struct expr *left = expr->left, *right = expr->right;
const struct proto_desc *base, *desc;
unsigned int proto = 0;
@@ -102,7 +103,7 @@ static void payload_expr_pctx_update(struct proto_ctx *ctx,
assert(base->length > 0);
ctx->protocol[base->base].offset += base->length;
}
- proto_ctx_update(ctx, desc->base, &expr->location, desc);
+ proto_ctx_update(ctx, desc->base, loc, desc);
}
#define NFTNL_UDATA_SET_KEY_PAYLOAD_DESC 0
diff --git a/src/proto.c b/src/proto.c
index 7d001501..725b0385 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -193,12 +193,69 @@ void proto_ctx_update(struct proto_ctx *ctx, enum proto_bases base,
const struct location *loc,
const struct proto_desc *desc)
{
+ bool found = false;
+ unsigned int i;
+
+ switch (base) {
+ case PROTO_BASE_LL_HDR:
+ case PROTO_BASE_NETWORK_HDR:
+ break;
+ case PROTO_BASE_TRANSPORT_HDR:
+ if (ctx->protocol[base].num_protos >= PROTO_CTX_NUM_PROTOS)
+ break;
+
+ for (i = 0; i < ctx->protocol[base].num_protos; i++) {
+ if (ctx->protocol[base].protos[i].desc == desc) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ i = ctx->protocol[base].num_protos++;
+ ctx->protocol[base].protos[i].desc = desc;
+ ctx->protocol[base].protos[i].location = *loc;
+ }
+ break;
+ default:
+ BUG("unknown protocol base %d", base);
+ }
+
ctx->protocol[base].location = *loc;
ctx->protocol[base].desc = desc;
proto_ctx_debug(ctx, base, ctx->debug_mask);
}
+bool proto_ctx_is_ambiguous(struct proto_ctx *ctx, enum proto_bases base)
+{
+ return ctx->protocol[base].num_protos > 1;
+}
+
+const struct proto_desc *proto_ctx_find_conflict(struct proto_ctx *ctx,
+ enum proto_bases base,
+ const struct proto_desc *desc)
+{
+ unsigned int i;
+
+ switch (base) {
+ case PROTO_BASE_LL_HDR:
+ case PROTO_BASE_NETWORK_HDR:
+ if (desc != ctx->protocol[base].desc)
+ return ctx->protocol[base].desc;
+ break;
+ case PROTO_BASE_TRANSPORT_HDR:
+ for (i = 0; i < ctx->protocol[base].num_protos; i++) {
+ if (desc != ctx->protocol[base].protos[i].desc)
+ return ctx->protocol[base].protos[i].desc;
+ }
+ break;
+ default:
+ BUG("unknown protocol base %d", base);
+ }
+
+ return NULL;
+}
+
#define HDR_TEMPLATE(__name, __dtype, __type, __member) \
PROTO_HDR_TEMPLATE(__name, __dtype, \
BYTEORDER_BIG_ENDIAN, \
@@ -349,6 +406,7 @@ const struct proto_desc proto_icmp = {
.id = PROTO_DESC_ICMP,
.base = PROTO_BASE_TRANSPORT_HDR,
.checksum_key = ICMPHDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_INET,
.templates = {
[ICMPHDR_TYPE] = ICMPHDR_TYPE("type", &icmp_type_type, type),
[ICMPHDR_CODE] = ICMPHDR_TYPE("code", &icmp_code_type, code),
@@ -402,6 +460,7 @@ const struct proto_desc proto_igmp = {
.id = PROTO_DESC_IGMP,
.base = PROTO_BASE_TRANSPORT_HDR,
.checksum_key = IGMPHDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_INET,
.templates = {
[IGMPHDR_TYPE] = IGMPHDR_TYPE("type", &igmp_type_type, igmp_type),
[IGMPHDR_MRT] = IGMPHDR_FIELD("mrt", igmp_code),
@@ -423,6 +482,7 @@ const struct proto_desc proto_udp = {
.id = PROTO_DESC_UDP,
.base = PROTO_BASE_TRANSPORT_HDR,
.checksum_key = UDPHDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_INET,
.templates = {
[UDPHDR_SPORT] = INET_SERVICE("sport", struct udphdr, source),
[UDPHDR_DPORT] = INET_SERVICE("dport", struct udphdr, dest),
@@ -482,6 +542,7 @@ const struct proto_desc proto_tcp = {
.id = PROTO_DESC_TCP,
.base = PROTO_BASE_TRANSPORT_HDR,
.checksum_key = TCPHDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_INET,
.templates = {
[TCPHDR_SPORT] = INET_SERVICE("sport", struct tcphdr, source),
[TCPHDR_DPORT] = INET_SERVICE("dport", struct tcphdr, dest),
@@ -563,6 +624,8 @@ const struct proto_desc proto_sctp = {
.name = "sctp",
.id = PROTO_DESC_SCTP,
.base = PROTO_BASE_TRANSPORT_HDR,
+ .checksum_key = SCTPHDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_SCTP,
.templates = {
[SCTPHDR_SPORT] = INET_SERVICE("sport", struct sctphdr, source),
[SCTPHDR_DPORT] = INET_SERVICE("dport", struct sctphdr, dest),
@@ -662,6 +725,7 @@ const struct proto_desc proto_ip = {
.id = PROTO_DESC_IP,
.base = PROTO_BASE_NETWORK_HDR,
.checksum_key = IPHDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_INET,
.protocols = {
PROTO_LINK(IPPROTO_ICMP, &proto_icmp),
PROTO_LINK(IPPROTO_IGMP, &proto_igmp),
@@ -759,6 +823,7 @@ const struct proto_desc proto_icmp6 = {
.id = PROTO_DESC_ICMPV6,
.base = PROTO_BASE_TRANSPORT_HDR,
.checksum_key = ICMP6HDR_CHECKSUM,
+ .checksum_type = NFT_PAYLOAD_CSUM_INET,
.templates = {
[ICMP6HDR_TYPE] = ICMP6HDR_TYPE("type", &icmp6_type_type, icmp6_type),
[ICMP6HDR_CODE] = ICMP6HDR_TYPE("code", &icmpv6_code_type, icmp6_code),
diff --git a/src/rule.c b/src/rule.c
index 1f56faeb..dddfdf51 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -152,11 +152,18 @@ static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h,
static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags)
{
+ struct nftnl_chain_list *chain_list = NULL;
struct rule *rule, *nrule;
struct table *table;
struct chain *chain;
struct set *set;
- int ret;
+ int ret = 0;
+
+ if (flags & NFT_CACHE_CHAIN_BIT) {
+ chain_list = chain_cache_dump(ctx, &ret);
+ if (!chain_list)
+ return ret;
+ }
list_for_each_entry(table, &ctx->nft->cache.list, list) {
if (flags & NFT_CACHE_SET_BIT) {
@@ -174,10 +181,9 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags)
}
}
if (flags & NFT_CACHE_CHAIN_BIT) {
- ret = netlink_list_chains(ctx, &table->handle);
+ ret = chain_cache_init(ctx, table, chain_list);
if (ret < 0)
return -1;
- list_splice_tail_init(&ctx->list, &table->chains);
}
if (flags & NFT_CACHE_FLOWTABLE_BIT) {
ret = netlink_list_flowtables(ctx, &table->handle);
@@ -195,13 +201,20 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags)
if (flags & NFT_CACHE_RULE_BIT) {
ret = netlink_list_rules(ctx, &table->handle);
list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
- chain = chain_lookup(table, &rule->handle);
+ chain = chain_cache_find(table, &rule->handle);
+ if (!chain)
+ chain = chain_binding_lookup(table,
+ rule->handle.chain.name);
list_move_tail(&rule->list, &chain->rules);
}
if (ret < 0)
return -1;
}
}
+
+ if (flags & NFT_CACHE_CHAIN_BIT)
+ nftnl_chain_list_free(chain_list);
+
return 0;
}
@@ -231,6 +244,11 @@ static bool cache_is_complete(struct nft_cache *cache, unsigned int flags)
return (cache->flags & flags) == flags;
}
+static bool cache_needs_refresh(struct nft_cache *cache)
+{
+ return cache->flags & NFT_CACHE_REFRESH;
+}
+
static bool cache_is_updated(struct nft_cache *cache, uint16_t genid)
{
return genid && genid == cache->genid;
@@ -254,7 +272,8 @@ int cache_update(struct nft_ctx *nft, unsigned int flags, struct list_head *msgs
replay:
ctx.seqnum = cache->seqnum++;
genid = mnl_genid_get(&ctx);
- if (cache_is_complete(cache, flags) &&
+ if (!cache_needs_refresh(cache) &&
+ cache_is_complete(cache, flags) &&
cache_is_updated(cache, genid))
return 0;
@@ -354,6 +373,8 @@ void set_free(struct set *set)
return;
if (set->init != NULL)
expr_free(set->init);
+ if (set->comment)
+ xfree(set->comment);
handle_free(&set->handle);
stmt_free(set->stmt);
expr_free(set->key);
@@ -571,6 +592,13 @@ static void set_print_declaration(const struct set *set,
time_print(set->gc_int, octx);
nft_print(octx, "%s", opts->stmt_separator);
}
+
+ if (set->comment) {
+ nft_print(octx, "%s%scomment \"%s\"%s",
+ opts->tab, opts->tab,
+ set->comment,
+ opts->stmt_separator);
+ }
}
static void do_set_print(const struct set *set, struct print_fmt_options *opts,
@@ -858,12 +886,16 @@ const char *chain_hookname_lookup(const char *name)
return NULL;
}
+/* internal ID to uniquely identify a set in the batch */
+static uint32_t chain_id;
+
struct chain *chain_alloc(const char *name)
{
struct chain *chain;
chain = xzalloc(sizeof(*chain));
chain->refcnt = 1;
+ chain->handle.chain_id = ++chain_id;
init_list_head(&chain->rules);
init_list_head(&chain->scope.symbols);
if (name != NULL)
@@ -897,14 +929,10 @@ void chain_free(struct chain *chain)
xfree(chain->dev_array);
expr_free(chain->priority.expr);
expr_free(chain->policy);
+ xfree(chain->comment);
xfree(chain);
}
-void chain_add_hash(struct chain *chain, struct table *table)
-{
- list_add_tail(&chain->list, &table->chains);
-}
-
struct chain *chain_lookup(const struct table *table, const struct handle *h)
{
struct chain *chain;
@@ -916,6 +944,18 @@ struct chain *chain_lookup(const struct table *table, const struct handle *h)
return NULL;
}
+struct chain *chain_binding_lookup(const struct table *table,
+ const char *chain_name)
+{
+ struct chain *chain;
+
+ list_for_each_entry(chain, &table->chain_bindings, list) {
+ if (!strcmp(chain->handle.chain.name, chain_name))
+ return chain;
+ }
+ return NULL;
+}
+
struct chain *chain_lookup_fuzzy(const struct handle *h,
const struct nft_cache *cache,
const struct table **t)
@@ -979,6 +1019,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum)
return "postrouting";
case NF_INET_LOCAL_OUT:
return "output";
+ case NF_INET_INGRESS:
+ return "ingress";
default:
break;
};
@@ -1175,9 +1217,14 @@ static void chain_print_declaration(const struct chain *chain,
char priobuf[STD_PRIO_BUFSIZE];
int policy, i;
+ if (chain->flags & CHAIN_F_BINDING)
+ return;
+
nft_print(octx, "\tchain %s {", chain->handle.chain.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, chain->handle.handle.id);
+ if (chain->comment)
+ nft_print(octx, "\n\t\tcomment \"%s\"", chain->comment);
nft_print(octx, "\n");
if (chain->flags & CHAIN_F_BASECHAIN) {
nft_print(octx, "\t\ttype %s hook %s", chain->type,
@@ -1210,17 +1257,22 @@ static void chain_print_declaration(const struct chain *chain,
}
}
-static void chain_print(const struct chain *chain, struct output_ctx *octx)
+void chain_rules_print(const struct chain *chain, struct output_ctx *octx,
+ const char *indent)
{
struct rule *rule;
- chain_print_declaration(chain, octx);
-
list_for_each_entry(rule, &chain->rules, list) {
- nft_print(octx, "\t\t");
+ nft_print(octx, "\t\t%s", indent ? : "");
rule_print(rule, octx);
nft_print(octx, "\n");
}
+}
+
+static void chain_print(const struct chain *chain, struct output_ctx *octx)
+{
+ chain_print_declaration(chain, octx);
+ chain_rules_print(chain, octx, NULL);
nft_print(octx, "\t}\n");
}
@@ -1249,15 +1301,22 @@ void chain_print_plain(const struct chain *chain, struct output_ctx *octx)
struct table *table_alloc(void)
{
struct table *table;
+ int i;
table = xzalloc(sizeof(*table));
init_list_head(&table->chains);
init_list_head(&table->sets);
init_list_head(&table->objs);
init_list_head(&table->flowtables);
+ init_list_head(&table->chain_bindings);
init_list_head(&table->scope.symbols);
table->refcnt = 1;
+ table->chain_htable =
+ xmalloc(sizeof(struct list_head) * NFT_CACHE_HSIZE);
+ for (i = 0; i < NFT_CACHE_HSIZE; i++)
+ init_list_head(&table->chain_htable[i]);
+
return table;
}
@@ -1270,8 +1329,12 @@ void table_free(struct table *table)
if (--table->refcnt > 0)
return;
+ if (table->comment)
+ xfree(table->comment);
list_for_each_entry_safe(chain, next, &table->chains, list)
chain_free(chain);
+ list_for_each_entry_safe(chain, next, &table->chain_bindings, list)
+ chain_free(chain);
list_for_each_entry_safe(set, nset, &table->sets, list)
set_free(set);
list_for_each_entry_safe(ft, nft, &table->flowtables, list)
@@ -1280,6 +1343,7 @@ void table_free(struct table *table)
obj_free(obj);
handle_free(&table->handle);
scope_release(&table->scope);
+ xfree(table->chain_htable);
xfree(table);
}
@@ -1365,6 +1429,9 @@ static void table_print(const struct table *table, struct output_ctx *octx)
nft_print(octx, "\n");
table_print_options(table, &delim, octx);
+ if (table->comment)
+ nft_print(octx, "\tcomment \"%s\"\n", table->comment);
+
list_for_each_entry(obj, &table->objs, list) {
nft_print(octx, "%s", delim);
obj_print(obj, octx);
@@ -1406,9 +1473,11 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
return cmd;
}
-void cmd_add_loc(struct cmd *cmd, uint16_t offset, struct location *loc)
+void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc)
{
- assert(cmd->num_attrs < NFT_NLATTR_LOC_MAX);
+ if (cmd->num_attrs > NFT_NLATTR_LOC_MAX)
+ return;
+
cmd->attr[cmd->num_attrs].offset = offset;
cmd->attr[cmd->num_attrs].location = loc;
cmd->num_attrs++;
@@ -1437,6 +1506,7 @@ void nft_cmd_expand(struct cmd *cmd)
list_for_each_entry(chain, &table->chains, list) {
memset(&h, 0, sizeof(h));
handle_merge(&h, &chain->handle);
+ h.chain_id = chain->handle.chain_id;
new = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &h,
&chain->location, chain_get(chain));
list_add_tail(&new->list, &new_cmds);
@@ -1469,6 +1539,12 @@ void nft_cmd_expand(struct cmd *cmd)
list_for_each_entry(rule, &chain->rules, list) {
memset(&h, 0, sizeof(h));
handle_merge(&h, &rule->handle);
+ if (chain->flags & CHAIN_F_BINDING) {
+ rule->handle.chain_id =
+ chain->handle.chain_id;
+ rule->handle.chain.location =
+ chain->location;
+ }
new = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h,
&rule->location,
rule_get(rule));
@@ -1478,6 +1554,7 @@ void nft_cmd_expand(struct cmd *cmd)
list_splice(&new_cmds, &cmd->list);
break;
case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
set = cmd->set;
memset(&h, 0, sizeof(h));
handle_merge(&h, &set->handle);
@@ -1535,6 +1612,8 @@ void cmd_free(struct cmd *cmd)
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
expr_free(cmd->expr);
+ if (cmd->elem.set)
+ set_free(cmd->elem.set);
break;
case CMD_OBJ_SET:
case CMD_OBJ_SETELEMS:
@@ -1605,13 +1684,8 @@ static int __do_add_setelems(struct netlink_ctx *ctx, struct set *set,
static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
uint32_t flags)
{
- struct handle *h = &cmd->handle;
struct expr *init = cmd->expr;
- struct table *table;
- struct set *set;
-
- table = table_lookup(h, &ctx->nft->cache);
- set = set_lookup(table, h->set.name);
+ struct set *set = cmd->elem.set;
if (set_is_non_concat_range(set) &&
set_to_intervals(ctx->msgs, set, init, true,
@@ -1708,13 +1782,8 @@ static int do_command_insert(struct netlink_ctx *ctx, struct cmd *cmd)
static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
{
- struct handle *h = &cmd->handle;
- struct expr *expr = cmd->expr;
- struct table *table;
- struct set *set;
-
- table = table_lookup(h, &ctx->nft->cache);
- set = set_lookup(table, h->set.name);
+ struct expr *expr = cmd->elem.expr;
+ struct set *set = cmd->elem.set;
if (set_is_non_concat_range(set) &&
set_to_intervals(ctx->msgs, set, expr, false,
@@ -1831,6 +1900,7 @@ void obj_free(struct obj *obj)
{
if (--obj->refcnt > 0)
return;
+ xfree(obj->comment);
handle_free(&obj->handle);
xfree(obj);
}
@@ -1924,6 +1994,16 @@ static const char *synproxy_timestamp_to_str(const uint32_t flags)
return "";
}
+static void obj_print_comment(const struct obj *obj,
+ struct print_fmt_options *opts,
+ struct output_ctx *octx)
+{
+ if (obj->comment)
+ nft_print(octx, "%s%s%scomment \"%s\"",
+ opts->nl, opts->tab, opts->tab,
+ obj->comment);
+}
+
static void obj_print_data(const struct obj *obj,
struct print_fmt_options *opts,
struct output_ctx *octx)
@@ -1933,6 +2013,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
if (nft_output_stateless(octx)) {
nft_print(octx, "packets 0 bytes 0");
@@ -1948,6 +2030,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
data_unit = get_rate(obj->quota.bytes, &bytes);
nft_print(octx, "%s%" PRIu64 " %s",
@@ -1965,6 +2049,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
nft_print(octx, "\"%s\"%s", obj->secmark.ctx, opts->nl);
break;
@@ -1972,6 +2058,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s", opts->nl);
nft_print(octx, "%s%stype \"%s\" protocol ",
opts->tab, opts->tab, obj->ct_helper.name);
@@ -1986,6 +2074,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s", opts->nl);
nft_print(octx, "%s%sprotocol ", opts->tab, opts->tab);
print_proto_name_proto(obj->ct_timeout.l4proto, octx);
@@ -2001,6 +2091,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s", opts->nl);
nft_print(octx, "%s%sprotocol ", opts->tab, opts->tab);
print_proto_name_proto(obj->ct_expect.l4proto, octx);
@@ -2029,6 +2121,8 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " %s {", obj->handle.obj.name);
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+
+ obj_print_comment(obj, opts, octx);
nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
switch (obj->limit.type) {
case NFT_LIMIT_PKTS:
@@ -2066,6 +2160,8 @@ static void obj_print_data(const struct obj *obj,
if (nft_output_handle(octx))
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
+ obj_print_comment(obj, opts, octx);
+
if (flags & NF_SYNPROXY_OPT_MSS) {
nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
nft_print(octx, "mss %u", obj->synproxy.mss);
@@ -2272,13 +2368,15 @@ static void flowtable_print_declaration(const struct flowtable *flowtable,
flowtable->hook.num, flowtable->priority.expr),
opts->stmt_separator);
- nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab);
- for (i = 0; i < flowtable->dev_array_len; i++) {
- nft_print(octx, "%s", flowtable->dev_array[i]);
- if (i + 1 != flowtable->dev_array_len)
- nft_print(octx, ", ");
+ if (flowtable->dev_array_len > 0) {
+ nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab);
+ for (i = 0; i < flowtable->dev_array_len; i++) {
+ nft_print(octx, "%s", flowtable->dev_array[i]);
+ if (i + 1 != flowtable->dev_array_len)
+ nft_print(octx, ", ");
+ }
+ nft_print(octx, " }%s", opts->stmt_separator);
}
- nft_print(octx, " }%s", opts->stmt_separator);
if (flowtable->flags & NFT_FLOWTABLE_COUNTER)
nft_print(octx, "%s%scounter%s", opts->tab, opts->tab,
@@ -2477,9 +2575,15 @@ static int do_list_chains(struct netlink_ctx *ctx, struct cmd *cmd)
}
static void __do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
- struct table *table, struct set *set)
+ struct set *set)
{
+ struct table *table = table_alloc();
+
+ table->handle.table.name = xstrdup(cmd->handle.table.name);
+ table->handle.family = cmd->handle.family;
table_print_declaration(table, &ctx->nft->output);
+ table_free(table);
+
set_print(set, &ctx->nft->output);
nft_print(&ctx->nft->output, "}\n");
}
@@ -2493,7 +2597,7 @@ static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
if (set == NULL)
return -1;
- __do_list_set(ctx, cmd, table, set);
+ __do_list_set(ctx, cmd, set);
return 0;
}
@@ -2564,14 +2668,13 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
return 0;
}
-static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
- struct table *table)
+static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
{
struct set *set, *new_set;
struct expr *init;
int err;
- set = set_lookup(table, cmd->handle.set.name);
+ set = cmd->elem.set;
/* Create a list of elements based of what we got from command line. */
if (set_is_non_concat_range(set))
@@ -2583,9 +2686,9 @@ static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
/* Fetch from kernel the elements that have been requested .*/
err = netlink_get_setelem(ctx, &cmd->handle, &cmd->location,
- table, new_set, init);
+ cmd->elem.set, new_set, init);
if (err >= 0)
- __do_list_set(ctx, cmd, table, new_set);
+ __do_list_set(ctx, cmd, new_set);
if (set_is_non_concat_range(set))
expr_free(init);
@@ -2597,14 +2700,9 @@ static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd)
{
- struct table *table = NULL;
-
- if (cmd->handle.table.name != NULL)
- table = table_lookup(&cmd->handle, &ctx->nft->cache);
-
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- return do_get_setelems(ctx, cmd, table);
+ return do_get_setelems(ctx, cmd);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -2646,7 +2744,7 @@ static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd)
if (ret < 0)
return ret;
- return do_list_obj(ctx, cmd, type);
+ return do_command_list(ctx, cmd);
}
static int do_command_flush(struct netlink_ctx *ctx, struct cmd *cmd)
diff --git a/src/scanner.l b/src/scanner.l
index 45699c85..7afd9bfb 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -267,7 +267,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"trace" { return TRACE; }
"socket" { return SOCKET; }
-"transparent" { return TRANSPARENT;}
+"transparent" { return TRANSPARENT; }
+"wildcard" { return WILDCARD; }
"tproxy" { return TPROXY; }
diff --git a/src/segtree.c b/src/segtree.c
index 266a2b4d..ba455a6a 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -383,8 +383,8 @@ static bool interval_overlap(const struct elementary_interval *e1,
static int set_overlap(struct list_head *msgs, const struct set *set,
struct expr *init, unsigned int keylen, bool add)
{
- struct elementary_interval *new_intervals[init->size];
- struct elementary_interval *intervals[set->init->size];
+ struct elementary_interval *new_intervals[init->size + 1];
+ struct elementary_interval *intervals[set->init->size + 1];
unsigned int n, m, i, j;
int ret = 0;
@@ -687,17 +687,15 @@ struct expr *get_set_intervals(const struct set *set, const struct expr *init)
return new_init;
}
-static struct expr *get_set_interval_find(const struct table *table,
- const char *set_name,
+static struct expr *get_set_interval_find(const struct set *cache_set,
struct expr *left,
struct expr *right)
{
+ const struct set *set = cache_set;
struct expr *range = NULL;
- struct set *set;
struct expr *i;
mpz_t val;
- set = set_lookup(table, set_name);
mpz_init2(val, set->key->len);
list_for_each_entry(i, &set->init->expressions, list) {
@@ -724,7 +722,7 @@ out:
return range;
}
-int get_set_decompose(struct table *table, struct set *set)
+int get_set_decompose(struct set *cache_set, struct set *set)
{
struct expr *i, *next, *range;
struct expr *left = NULL;
@@ -737,8 +735,7 @@ int get_set_decompose(struct table *table, struct set *set)
list_del(&left->list);
list_del(&i->list);
mpz_sub_ui(i->key->value, i->key->value, 1);
- range = get_set_interval_find(table, set->handle.set.name,
- left, i);
+ range = get_set_interval_find(cache_set, left, i);
if (!range) {
expr_free(new_init);
errno = ENOENT;
@@ -751,8 +748,7 @@ int get_set_decompose(struct table *table, struct set *set)
left = NULL;
} else {
if (left) {
- range = get_set_interval_find(table,
- set->handle.set.name,
+ range = get_set_interval_find(cache_set,
left, NULL);
if (range)
compound_expr_add(new_init, range);
@@ -764,8 +760,7 @@ int get_set_decompose(struct table *table, struct set *set)
}
}
if (left) {
- range = get_set_interval_find(table, set->handle.set.name,
- left, NULL);
+ range = get_set_interval_find(cache_set, left, NULL);
if (range)
compound_expr_add(new_init, range);
else
@@ -932,6 +927,20 @@ next:
}
}
+static void interval_expr_copy(struct expr *dst, struct expr *src)
+{
+ if (src->comment)
+ dst->comment = xstrdup(src->comment);
+ if (src->timeout)
+ dst->timeout = src->timeout;
+ if (src->expiration)
+ dst->expiration = src->expiration;
+ if (src->stmt) {
+ dst->stmt = src->stmt;
+ src->stmt = NULL;
+ }
+}
+
void interval_map_decompose(struct expr *set)
{
struct expr **elements, **ranges;
@@ -1021,22 +1030,12 @@ void interval_map_decompose(struct expr *set)
tmp = set_elem_expr_alloc(&low->location, tmp);
if (low->etype == EXPR_MAPPING) {
- if (low->left->comment)
- tmp->comment = xstrdup(low->left->comment);
- if (low->left->timeout)
- tmp->timeout = low->left->timeout;
- if (low->left->expiration)
- tmp->expiration = low->left->expiration;
+ interval_expr_copy(tmp, low->left);
tmp = mapping_expr_alloc(&tmp->location, tmp,
expr_clone(low->right));
} else {
- if (low->comment)
- tmp->comment = xstrdup(low->comment);
- if (low->timeout)
- tmp->timeout = low->timeout;
- if (low->expiration)
- tmp->expiration = low->expiration;
+ interval_expr_copy(tmp, low);
}
compound_expr_add(set, tmp);
@@ -1053,22 +1052,12 @@ void interval_map_decompose(struct expr *set)
prefix = set_elem_expr_alloc(&low->location, prefix);
if (low->etype == EXPR_MAPPING) {
- if (low->left->comment)
- prefix->comment = xstrdup(low->left->comment);
- if (low->left->timeout)
- prefix->timeout = low->left->timeout;
- if (low->left->expiration)
- prefix->expiration = low->left->expiration;
+ interval_expr_copy(prefix, low->left);
prefix = mapping_expr_alloc(&low->location, prefix,
expr_clone(low->right));
} else {
- if (low->comment)
- prefix->comment = xstrdup(low->comment);
- if (low->timeout)
- prefix->timeout = low->timeout;
- if (low->expiration)
- prefix->expiration = low->expiration;
+ interval_expr_copy(prefix, low);
}
compound_expr_add(set, prefix);
@@ -1086,16 +1075,23 @@ void interval_map_decompose(struct expr *set)
i = constant_expr_alloc(&low->location, low->dtype,
low->byteorder, expr_value(low)->len, NULL);
- mpz_init_bitmask(i->value, i->len);
+ mpz_bitmask(i->value, i->len);
if (!mpz_cmp(i->value, expr_value(low)->value)) {
expr_free(i);
i = low;
} else {
- i = range_expr_alloc(&low->location, expr_value(low), i);
+ i = range_expr_alloc(&low->location,
+ expr_clone(expr_value(low)), i);
i = set_elem_expr_alloc(&low->location, i);
- if (low->etype == EXPR_MAPPING)
- i = mapping_expr_alloc(&i->location, i, low->right);
+ if (low->etype == EXPR_MAPPING) {
+ i = mapping_expr_alloc(&i->location, i,
+ expr_clone(low->right));
+ interval_expr_copy(i->left, low->left);
+ } else {
+ interval_expr_copy(i, low);
+ }
+ expr_free(low);
}
compound_expr_add(set, i);
diff --git a/src/socket.c b/src/socket.c
index d78a163a..673e5d0f 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -26,6 +26,12 @@ const struct socket_template socket_templates[] = {
.len = 4 * BITS_PER_BYTE,
.byteorder = BYTEORDER_HOST_ENDIAN,
},
+ [NFT_SOCKET_WILDCARD] = {
+ .token = "wildcard",
+ .dtype = &integer_type,
+ .len = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_HOST_ENDIAN,
+ },
};
static void socket_expr_print(const struct expr *expr, struct output_ctx *octx)
diff --git a/src/statement.c b/src/statement.c
index 21a1bc8d..6fe8e9d9 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -15,9 +15,11 @@
#include <inttypes.h>
#include <string.h>
#include <syslog.h>
+#include <rule.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
+#include <linux/netfilter/nf_log.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#include <statement.h>
@@ -111,6 +113,50 @@ struct stmt *verdict_stmt_alloc(const struct location *loc, struct expr *expr)
return stmt;
}
+static const char *chain_verdict(const struct expr *expr)
+{
+ switch (expr->verdict) {
+ case NFT_JUMP:
+ return "jump";
+ case NFT_GOTO:
+ return "goto";
+ default:
+ BUG("unknown chain verdict");
+ }
+}
+
+static void chain_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+ nft_print(octx, "%s {\n", chain_verdict(stmt->chain.expr));
+ chain_rules_print(stmt->chain.chain, octx, "\t");
+ nft_print(octx, "\t\t}");
+}
+
+static void chain_stmt_destroy(struct stmt *stmt)
+{
+ expr_free(stmt->chain.expr);
+}
+
+static const struct stmt_ops chain_stmt_ops = {
+ .type = STMT_CHAIN,
+ .name = "chain",
+ .print = chain_stmt_print,
+ .destroy = chain_stmt_destroy,
+};
+
+struct stmt *chain_stmt_alloc(const struct location *loc, struct chain *chain,
+ enum nft_verdicts verdict)
+{
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &chain_stmt_ops);
+ stmt->chain.chain = chain;
+ stmt->chain.expr = verdict_expr_alloc(loc, verdict, NULL);
+ stmt->chain.expr->chain_id = chain->handle.chain_id;
+
+ return stmt;
+}
+
static void meter_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
unsigned int flags = octx->flags;
@@ -300,8 +346,12 @@ int log_level_parse(const char *level)
static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
nft_print(octx, "log");
- if (stmt->log.flags & STMT_LOG_PREFIX)
- nft_print(octx, " prefix \"%s\"", stmt->log.prefix);
+ if (stmt->log.flags & STMT_LOG_PREFIX) {
+ char prefix[NF_LOG_PREFIXLEN] = {};
+
+ expr_to_string(stmt->log.prefix, prefix);
+ nft_print(octx, " prefix \"%s\"", prefix);
+ }
if (stmt->log.flags & STMT_LOG_GROUP)
nft_print(octx, " group %u", stmt->log.group);
if (stmt->log.flags & STMT_LOG_SNAPLEN)
@@ -338,7 +388,7 @@ static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
static void log_stmt_destroy(struct stmt *stmt)
{
- xfree(stmt->log.prefix);
+ expr_free(stmt->log.prefix);
}
static const struct stmt_ops log_stmt_ops = {