diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libnftables.c | 17 | ||||
-rw-r--r-- | src/rule.c | 75 |
2 files changed, 89 insertions, 3 deletions
diff --git a/src/libnftables.c b/src/libnftables.c index 6a22ea09..aac682b7 100644 --- a/src/libnftables.c +++ b/src/libnftables.c @@ -501,7 +501,9 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs, { struct nft_cache_filter *filter; struct cmd *cmd, *next; + bool collapsed = false; unsigned int flags; + int err = 0; filter = nft_cache_filter_init(); flags = nft_cache_evaluate(nft, cmds, filter); @@ -512,17 +514,26 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs, nft_cache_filter_fini(filter); + if (nft_cmd_collapse(cmds)) + collapsed = true; + list_for_each_entry_safe(cmd, next, cmds, list) { struct eval_ctx ectx = { .nft = nft, .msgs = msgs, }; + if (cmd_evaluate(&ectx, cmd) < 0 && - ++nft->state->nerrs == nft->parser_max_errors) - return -1; + ++nft->state->nerrs == nft->parser_max_errors) { + err = -1; + break; + } } - if (nft->state->nerrs) + if (collapsed) + nft_cmd_uncollapse(cmds); + + if (err < 0 || nft->state->nerrs) return -1; list_for_each_entry(cmd, cmds, list) { @@ -1279,6 +1279,8 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj, cmd->handle = *h; cmd->location = *loc; cmd->data = data; + init_list_head(&cmd->collapse_list); + return cmd; } @@ -1379,6 +1381,79 @@ void nft_cmd_expand(struct cmd *cmd) } } +bool nft_cmd_collapse(struct list_head *cmds) +{ + struct cmd *cmd, *next, *elems = NULL; + struct expr *expr, *enext; + bool collapse = false; + + list_for_each_entry_safe(cmd, next, cmds, list) { + if (cmd->op != CMD_ADD && + cmd->op != CMD_CREATE) { + elems = NULL; + continue; + } + + if (cmd->obj != CMD_OBJ_ELEMENTS) { + elems = NULL; + continue; + } + + if (!elems) { + elems = cmd; + continue; + } + + if (cmd->op != elems->op) { + elems = cmd; + continue; + } + + if (strcmp(elems->handle.table.name, cmd->handle.table.name) || + strcmp(elems->handle.set.name, cmd->handle.set.name)) { + elems = cmd; + continue; + } + + collapse = true; + list_for_each_entry_safe(expr, enext, &cmd->expr->expressions, list) { + expr->cmd = cmd; + list_move_tail(&expr->list, &elems->expr->expressions); + } + elems->expr->size += cmd->expr->size; + list_move_tail(&cmd->list, &elems->collapse_list); + } + + return collapse; +} + +void nft_cmd_uncollapse(struct list_head *cmds) +{ + struct cmd *cmd, *cmd_next, *collapse_cmd, *collapse_cmd_next; + struct expr *expr, *next; + + list_for_each_entry_safe(cmd, cmd_next, cmds, list) { + if (list_empty(&cmd->collapse_list)) + continue; + + assert(cmd->obj == CMD_OBJ_ELEMENTS); + + list_for_each_entry_safe(expr, next, &cmd->expr->expressions, list) { + if (!expr->cmd) + continue; + + list_move_tail(&expr->list, &expr->cmd->expr->expressions); + cmd->expr->size--; + expr->cmd = NULL; + } + + list_for_each_entry_safe(collapse_cmd, collapse_cmd_next, &cmd->collapse_list, list) { + collapse_cmd->elem.set = set_get(cmd->elem.set); + list_add(&collapse_cmd->list, &cmd->list); + } + } +} + struct markup *markup_alloc(uint32_t format) { struct markup *markup; |