diff options
Diffstat (limited to 'src/rule.c')
-rw-r--r-- | src/rule.c | 607 |
1 files changed, 313 insertions, 294 deletions
@@ -8,11 +8,10 @@ * Development of this code funded by Astaro AG (http://www.astaro.com/) */ +#include <nft.h> + #include <stddef.h> -#include <stdlib.h> #include <stdio.h> -#include <stdint.h> -#include <string.h> #include <inttypes.h> #include <errno.h> @@ -26,6 +25,8 @@ #include <json.h> #include <cache.h> #include <owner.h> +#include <intervals.h> +#include "nftutils.h" #include <libnftnl/common.h> #include <libnftnl/ruleset.h> @@ -72,10 +73,10 @@ static uint32_t tcp_dflt_timeout[] = { static uint32_t udp_dflt_timeout[] = { [NFTNL_CTTIMEOUT_UDP_UNREPLIED] = 30, - [NFTNL_CTTIMEOUT_UDP_REPLIED] = 180, + [NFTNL_CTTIMEOUT_UDP_REPLIED] = 120, }; -struct timeout_protocol timeout_protocol[IPPROTO_MAX] = { +struct timeout_protocol timeout_protocol[UINT8_MAX + 1] = { [IPPROTO_TCP] = { .array_size = NFTNL_CTTIMEOUT_TCP_MAX, .state_to_name = tcp_state_to_name, @@ -103,11 +104,11 @@ int timeout_str2num(uint16_t l4proto, struct timeout_state *ts) void handle_free(struct handle *h) { - xfree(h->table.name); - xfree(h->chain.name); - xfree(h->set.name); - xfree(h->flowtable.name); - xfree(h->obj.name); + free_const(h->table.name); + free_const(h->chain.name); + free_const(h->set.name); + free_const(h->flowtable.name); + free_const(h->obj.name); } void handle_merge(struct handle *dst, const struct handle *src) @@ -145,11 +146,12 @@ struct set *set_alloc(const struct location *loc) { struct set *set; + assert(loc); + set = xzalloc(sizeof(*set)); set->refcnt = 1; set->handle.set_id = ++set_id; - if (loc != NULL) - set->location = *loc; + set->location = *loc; init_list_head(&set->stmt_list); @@ -160,7 +162,7 @@ struct set *set_clone(const struct set *set) { struct set *new_set; - new_set = set_alloc(NULL); + new_set = set_alloc(&internal_location); handle_merge(&new_set->handle, &set->handle); new_set->flags = set->flags; new_set->gc_int = set->gc_int; @@ -189,16 +191,16 @@ void set_free(struct set *set) if (--set->refcnt > 0) return; - if (set->init != NULL) - expr_free(set->init); + + expr_free(set->init); if (set->comment) - xfree(set->comment); + free_const(set->comment); handle_free(&set->handle); list_for_each_entry_safe(stmt, next, &set->stmt_list, list) stmt_free(stmt); expr_free(set->key); expr_free(set->data); - xfree(set); + free(set); } struct set *set_lookup_fuzzy(const char *set_name, @@ -215,10 +217,6 @@ struct set *set_lookup_fuzzy(const char *set_name, list_for_each_entry(set, &table->set_cache.list, cache.list) { if (set_is_anonymous(set->flags)) continue; - if (!strcmp(set->handle.set.name, set_name)) { - *t = table; - return set; - } if (string_misspell_update(set->handle.set.name, set_name, set, &st)) *t = table; @@ -370,21 +368,21 @@ static void set_print_declaration(const struct set *set, nft_print(octx, "%s", opts->stmt_separator); } - if (!list_empty(&set->stmt_list)) + if (!list_empty(&set->stmt_list)) { + unsigned int flags = octx->flags; + nft_print(octx, "%s%s", opts->tab, opts->tab); - if (!list_empty(&set->stmt_list)) { octx->flags |= NFT_CTX_OUTPUT_STATELESS; list_for_each_entry(stmt, &set->stmt_list, list) { stmt_print(stmt, octx); if (!list_is_last(&stmt->list, &set->stmt_list)) nft_print(octx, " "); } - octx->flags &= ~NFT_CTX_OUTPUT_STATELESS; - } + octx->flags = flags; - if (!list_empty(&set->stmt_list)) nft_print(octx, "%s", opts->stmt_separator); + } if (set->automerge) nft_print(octx, "%s%sauto-merge%s", opts->tab, opts->tab, @@ -456,6 +454,8 @@ struct rule *rule_alloc(const struct location *loc, const struct handle *h) { struct rule *rule; + assert(loc); + rule = xzalloc(sizeof(*rule)); rule->location = *loc; init_list_head(&rule->list); @@ -479,8 +479,8 @@ void rule_free(struct rule *rule) return; stmt_list_free(&rule->stmts); handle_free(&rule->handle); - xfree(rule->comment); - xfree(rule); + free_const(rule->comment); + free(rule); } void rule_print(const struct rule *rule, struct output_ctx *octx) @@ -557,16 +557,16 @@ void scope_release(const struct scope *scope) list_for_each_entry_safe(sym, next, &scope->symbols, list) { assert(sym->refcnt == 1); list_del(&sym->list); - xfree(sym->identifier); + free_const(sym->identifier); expr_free(sym->expr); - xfree(sym); + free(sym); } } void scope_free(struct scope *scope) { scope_release(scope); - xfree(scope); + free(scope); } void symbol_bind(struct scope *scope, const char *identifier, struct expr *expr) @@ -597,9 +597,9 @@ struct symbol *symbol_get(const struct scope *scope, const char *identifier) static void symbol_put(struct symbol *sym) { if (--sym->refcnt == 0) { - xfree(sym->identifier); + free_const(sym->identifier); expr_free(sym->expr); - xfree(sym); + free(sym); } } @@ -679,6 +679,7 @@ static const char * const chain_hookname_str_array[] = { "postrouting", "output", "ingress", + "egress", NULL, }; @@ -697,17 +698,16 @@ const char *chain_hookname_lookup(const char *name) /* 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_alloc(void) { struct chain *chain; chain = xzalloc(sizeof(*chain)); + chain->location = internal_location; chain->refcnt = 1; chain->handle.chain_id = ++chain_id; init_list_head(&chain->rules); init_list_head(&chain->scope.symbols); - if (name != NULL) - chain->handle.chain.name = xstrdup(name); chain->policy = NULL; return chain; @@ -729,16 +729,20 @@ void chain_free(struct chain *chain) list_for_each_entry_safe(rule, next, &chain->rules, list) rule_free(rule); handle_free(&chain->handle); - scope_release(&chain->scope); - xfree(chain->type); + free_const(chain->type.str); expr_free(chain->dev_expr); for (i = 0; i < chain->dev_array_len; i++) - xfree(chain->dev_array[i]); - xfree(chain->dev_array); + free_const(chain->dev_array[i]); + free(chain->dev_array); expr_free(chain->priority.expr); expr_free(chain->policy); - xfree(chain->comment); - xfree(chain); + free_const(chain->comment); + + /* MUST be released after all expressions, they could + * hold refcounts. + */ + scope_release(&chain->scope); + free(chain); } struct chain *chain_binding_lookup(const struct table *table, @@ -761,14 +765,13 @@ struct chain *chain_lookup_fuzzy(const struct handle *h, struct table *table; struct chain *chain; + if (!h->chain.name) + return NULL; + string_misspell_init(&st); list_for_each_entry(table, &cache->table_cache.list, cache.list) { list_for_each_entry(chain, &table->chain_cache.list, cache.list) { - if (!strcmp(chain->handle.chain.name, h->chain.name)) { - *t = table; - return chain; - } if (string_misspell_update(chain->handle.chain.name, h->chain.name, chain, &st)) *t = table; @@ -830,6 +833,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum) return "forward"; case NF_ARP_OUT: return "output"; + case __NF_ARP_INGRESS: + return "ingress"; default: break; } @@ -838,6 +843,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum) switch (hooknum) { case NF_NETDEV_INGRESS: return "ingress"; + case NF_NETDEV_EGRESS: + return "egress"; } break; default: @@ -863,7 +870,7 @@ struct prio_tag { const char *str; }; -const static struct prio_tag std_prios[] = { +static const struct prio_tag std_prios[] = { { NF_IP_PRI_RAW, "raw" }, { NF_IP_PRI_MANGLE, "mangle" }, { NF_IP_PRI_NAT_DST, "dstnat" }, @@ -872,7 +879,7 @@ const static struct prio_tag std_prios[] = { { NF_IP_PRI_NAT_SRC, "srcnat" }, }; -const static struct prio_tag bridge_std_prios[] = { +static const struct prio_tag bridge_std_prios[] = { { NF_BR_PRI_NAT_DST_BRIDGED, "dstnat" }, { NF_BR_PRI_FILTER_BRIDGED, "filter" }, { NF_BR_PRI_NAT_DST_OTHER, "out" }, @@ -926,7 +933,8 @@ static bool std_prio_family_hook_compat(int prio, int family, int hook) case NFPROTO_INET: case NFPROTO_IPV4: case NFPROTO_IPV6: - if (hook == NF_INET_PRE_ROUTING) + if (hook == NF_INET_PRE_ROUTING || + hook == NF_INET_LOCAL_OUT) return true; } break; @@ -935,7 +943,8 @@ static bool std_prio_family_hook_compat(int prio, int family, int hook) case NFPROTO_INET: case NFPROTO_IPV4: case NFPROTO_IPV6: - if (hook == NF_INET_POST_ROUTING) + if (hook == NF_INET_LOCAL_IN || + hook == NF_INET_POST_ROUTING) return true; } } @@ -968,10 +977,11 @@ static const char *prio2str(const struct output_ctx *octx, const struct expr *expr) { const struct prio_tag *prio_arr; - int std_prio, offset, prio; + const uint32_t reach = 10; const char *std_prio_str; - const int reach = 10; + int std_prio, prio; size_t i, arr_size; + int64_t offset; mpz_export_data(&prio, expr->value, BYTEORDER_HOST_ENDIAN, sizeof(int)); if (family == NFPROTO_BRIDGE) { @@ -986,19 +996,21 @@ static const char *prio2str(const struct output_ctx *octx, for (i = 0; i < arr_size; ++i) { std_prio = prio_arr[i].val; std_prio_str = prio_arr[i].str; - if (abs(prio - std_prio) <= reach) { + + offset = (int64_t)prio - std_prio; + if (llabs(offset) <= reach) { if (!std_prio_family_hook_compat(std_prio, family, hook)) break; - offset = prio - std_prio; + strncpy(buf, std_prio_str, bufsize); if (offset > 0) snprintf(buf + strlen(buf), - bufsize - strlen(buf), " + %d", + bufsize - strlen(buf), " + %" PRIu64, offset); else if (offset < 0) snprintf(buf + strlen(buf), - bufsize - strlen(buf), " - %d", + bufsize - strlen(buf), " - %" PRIu64, -offset); return buf; } @@ -1024,7 +1036,7 @@ static void chain_print_declaration(const struct chain *chain, 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, + nft_print(octx, "\t\ttype %s hook %s", chain->type.str, hooknum2str(chain->handle.family, chain->hook.num)); if (chain->dev_array_len == 1) { nft_print(octx, " device \"%s\"", chain->dev_array[0]); @@ -1057,13 +1069,19 @@ static void chain_print_declaration(const struct chain *chain, void chain_rules_print(const struct chain *chain, struct output_ctx *octx, const char *indent) { + unsigned int flags = octx->flags; struct rule *rule; + if (chain->flags & CHAIN_F_BINDING) + octx->flags &= ~NFT_CTX_OUTPUT_HANDLE; + list_for_each_entry(rule, &chain->rules, list) { nft_print(octx, "\t\t%s", indent ? : ""); rule_print(rule, octx); nft_print(octx, "\n"); } + + octx->flags = flags; } static void chain_print(const struct chain *chain, struct output_ctx *octx) @@ -1084,8 +1102,21 @@ void chain_print_plain(const struct chain *chain, struct output_ctx *octx) if (chain->flags & CHAIN_F_BASECHAIN) { mpz_export_data(&policy, chain->policy->value, BYTEORDER_HOST_ENDIAN, sizeof(int)); - nft_print(octx, " { type %s hook %s priority %s; policy %s; }", - chain->type, chain->hook.name, + nft_print(octx, " { type %s hook %s ", + chain->type.str, chain->hook.name); + + if (chain->dev_array_len > 0) { + int i; + + nft_print(octx, "devices = { "); + for (i = 0; i < chain->dev_array_len; i++) { + nft_print(octx, "%s", chain->dev_array[i]); + if (i + 1 != chain->dev_array_len) + nft_print(octx, ", "); + } + nft_print(octx, " } "); + } + nft_print(octx, "priority %s; policy %s; }", prio2str(octx, priobuf, sizeof(priobuf), chain->handle.family, chain->hook.num, chain->priority.expr), @@ -1100,6 +1131,7 @@ struct table *table_alloc(void) struct table *table; table = xzalloc(sizeof(*table)); + table->location = internal_location; init_list_head(&table->chains); init_list_head(&table->sets); init_list_head(&table->objs); @@ -1126,7 +1158,7 @@ void table_free(struct table *table) if (--table->refcnt > 0) return; if (table->comment) - xfree(table->comment); + free_const(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, cache.list) @@ -1156,7 +1188,7 @@ void table_free(struct table *table) cache_free(&table->set_cache); cache_free(&table->obj_cache); cache_free(&table->ft_cache); - xfree(table); + free(table); } struct table *table_get(struct table *table) @@ -1174,9 +1206,6 @@ struct table *table_lookup_fuzzy(const struct handle *h, string_misspell_init(&st); list_for_each_entry(table, &cache->table_cache.list, cache.list) { - if (!strcmp(table->handle.table.name, h->table.name)) - return table; - string_misspell_update(table->handle.table.name, h->table.name, table, &st); } @@ -1186,6 +1215,7 @@ struct table *table_lookup_fuzzy(const struct handle *h, static const char *table_flags_name[TABLE_FLAGS_MAX] = { "dormant", "owner", + "persist", }; const char *table_flag_name(uint32_t flag) @@ -1196,6 +1226,17 @@ const char *table_flag_name(uint32_t flag) return table_flags_name[flag]; } +unsigned int parse_table_flag(const char *name) +{ + int i; + + for (i = 0; i < TABLE_FLAGS_MAX; i++) { + if (!strcmp(name, table_flags_name[i])) + return 1 << i; + } + return 0; +} + static void table_print_flags(const struct table *table, const char **delim, struct output_ctx *octx) { @@ -1229,6 +1270,11 @@ static void table_print(const struct table *table, struct output_ctx *octx) const char *delim = ""; const char *family = family2str(table->handle.family); + if (table->has_xt_stmts) + fprintf(octx->error_fp, + "# Warning: table %s %s is managed by iptables-nft, do not touch!\n", + family, table->handle.table.name); + nft_print(octx, "table %s %s {", family, table->handle.table.name); if (nft_output_handle(octx) || table->flags & TABLE_F_OWNER) nft_print(octx, " #"); @@ -1274,6 +1320,8 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj, { struct cmd *cmd; + assert(loc); + cmd = xzalloc(sizeof(*cmd)); init_list_head(&cmd->list); cmd->op = op; @@ -1281,105 +1329,12 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj, cmd->handle = *h; cmd->location = *loc; cmd->data = data; - return cmd; -} - -void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc) -{ - 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++; -} + cmd->attr = xzalloc_array(NFT_NLATTR_LOC_MAX, + sizeof(struct nlerr_loc)); + cmd->attr_array_len = NFT_NLATTR_LOC_MAX; + init_list_head(&cmd->collapse_list); -void nft_cmd_expand(struct cmd *cmd) -{ - struct list_head new_cmds; - struct set *set, *newset; - struct flowtable *ft; - struct table *table; - struct chain *chain; - struct rule *rule; - struct obj *obj; - struct cmd *new; - struct handle h; - - init_list_head(&new_cmds); - - switch (cmd->obj) { - case CMD_OBJ_TABLE: - table = cmd->table; - if (!table) - return; - - 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); - } - list_for_each_entry(obj, &table->objs, list) { - handle_merge(&obj->handle, &table->handle); - memset(&h, 0, sizeof(h)); - handle_merge(&h, &obj->handle); - new = cmd_alloc(CMD_ADD, obj_type_to_cmd(obj->type), &h, - &obj->location, obj_get(obj)); - list_add_tail(&new->list, &new_cmds); - } - list_for_each_entry(set, &table->sets, list) { - handle_merge(&set->handle, &table->handle); - memset(&h, 0, sizeof(h)); - handle_merge(&h, &set->handle); - new = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &h, - &set->location, set_get(set)); - list_add_tail(&new->list, &new_cmds); - } - list_for_each_entry(ft, &table->flowtables, list) { - handle_merge(&ft->handle, &table->handle); - memset(&h, 0, sizeof(h)); - handle_merge(&h, &ft->handle); - new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h, - &ft->location, flowtable_get(ft)); - list_add_tail(&new->list, &new_cmds); - } - list_for_each_entry(chain, &table->chains, list) { - 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)); - list_add_tail(&new->list, &new_cmds); - } - } - 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); - newset = set_clone(set); - newset->handle.set_id = set->handle.set_id; - newset->init = set->init; - set->init = NULL; - new = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEMS, &h, - &set->location, newset); - list_add(&new->list, &cmd->list); - break; - default: - break; - } + return cmd; } struct markup *markup_alloc(uint32_t format) @@ -1394,7 +1349,7 @@ struct markup *markup_alloc(uint32_t format) void markup_free(struct markup *m) { - xfree(m); + free(m); } struct monitor *monitor_alloc(uint32_t format, uint32_t type, const char *event) @@ -1412,8 +1367,8 @@ struct monitor *monitor_alloc(uint32_t format, uint32_t type, const char *event) void monitor_free(struct monitor *m) { - xfree(m->event); - xfree(m); + free_const(m->event); + free(m); } void cmd_free(struct cmd *cmd) @@ -1427,6 +1382,8 @@ void cmd_free(struct cmd *cmd) set_free(cmd->elem.set); break; case CMD_OBJ_SET: + case CMD_OBJ_MAP: + case CMD_OBJ_METER: case CMD_OBJ_SETELEMS: set_free(cmd->set); break; @@ -1465,65 +1422,66 @@ void cmd_free(struct cmd *cmd) BUG("invalid command object type %u\n", cmd->obj); } } - xfree(cmd->arg); - xfree(cmd); + free(cmd->attr); + free_const(cmd->arg); + free(cmd); } #include <netlink.h> #include <mnl.h> -static int __do_add_setelems(struct netlink_ctx *ctx, struct set *set, - struct expr *expr, uint32_t flags) +static int __do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd, + struct set *set, struct expr *expr, uint32_t flags) { expr->set_flags |= set->flags; - if (mnl_nft_setelem_add(ctx, set, expr, flags) < 0) + if (mnl_nft_setelem_add(ctx, cmd, set, expr, flags) < 0) return -1; - if (!set_is_anonymous(set->flags) && - set->init != NULL && set->init != expr && - set->flags & NFT_SET_INTERVAL && - set->desc.field_count <= 1) { - interval_map_decompose(expr); - list_splice_tail_init(&expr->expressions, &set->init->expressions); - set->init->size += expr->size; - expr->size = 0; - } - return 0; } -static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd, +static int do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t flags) { struct expr *init = cmd->expr; struct set *set = cmd->elem.set; if (set_is_non_concat_range(set) && - set_to_intervals(ctx->msgs, set, init, true, - ctx->nft->debug_mask, set->automerge, - &ctx->nft->output) < 0) + set_to_intervals(set, init, true) < 0) return -1; - return __do_add_setelems(ctx, set, init, flags); + return __do_add_elements(ctx, cmd, set, init, flags); +} + +static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd, + uint32_t flags) +{ + struct set *set = cmd->set; + + return __do_add_elements(ctx, cmd, set, set->init, flags); } static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd, - uint32_t flags, bool add) + uint32_t flags) { struct set *set = cmd->set; if (set->init != NULL) { + /* Update set->init->size (NFTNL_SET_DESC_SIZE) before adding + * the set to the kernel. Calling this from do_add_setelems() + * comes too late which might result in spurious ENFILE errors. + */ if (set_is_non_concat_range(set) && - set_to_intervals(ctx->msgs, set, set->init, true, - ctx->nft->debug_mask, set->automerge, - &ctx->nft->output) < 0) + set_to_intervals(set, set->init, true) < 0) return -1; } - if (add && mnl_nft_set_add(ctx, cmd, flags) < 0) + + if (mnl_nft_set_add(ctx, cmd, flags) < 0) return -1; - if (set->init != NULL) { - return __do_add_setelems(ctx, set, set->init, flags); - } + + if (set_is_anonymous(set->flags)) + return __do_add_elements(ctx, cmd, set, set->init, flags); + return 0; } @@ -1542,11 +1500,11 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl) case CMD_OBJ_RULE: return mnl_nft_rule_add(ctx, cmd, flags | NLM_F_APPEND); case CMD_OBJ_SET: - return do_add_set(ctx, cmd, flags, true); + return do_add_set(ctx, cmd, flags); case CMD_OBJ_SETELEMS: - return do_add_set(ctx, cmd, flags, false); - case CMD_OBJ_ELEMENTS: return do_add_setelems(ctx, cmd, flags); + case CMD_OBJ_ELEMENTS: + return do_add_elements(ctx, cmd, flags); case CMD_OBJ_COUNTER: case CMD_OBJ_QUOTA: case CMD_OBJ_CT_HELPER: @@ -1597,12 +1555,10 @@ static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd) struct set *set = cmd->elem.set; if (set_is_non_concat_range(set) && - set_to_intervals(ctx->msgs, set, expr, false, - ctx->nft->debug_mask, set->automerge, - &ctx->nft->output) < 0) + set_to_intervals(set, expr, false) < 0) return -1; - if (mnl_nft_setelem_del(ctx, cmd) < 0) + if (mnl_nft_setelem_del(ctx, cmd, &cmd->handle, cmd->elem.expr) < 0) return -1; return 0; @@ -1644,8 +1600,7 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd) } } -static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd, - struct table *table) +static int do_list_table(struct netlink_ctx *ctx, struct table *table) { table_print(table, &ctx->nft->output); return 0; @@ -1653,11 +1608,6 @@ static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd, static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd) { - struct print_fmt_options opts = { - .tab = "\t", - .nl = "\n", - .stmt_separator = "\n", - }; struct table *table; struct set *set; @@ -1675,13 +1625,12 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd) !set_is_literal(set->flags)) continue; if (cmd->obj == CMD_OBJ_METERS && - !set_is_meter(set->flags)) + !set_is_meter_compat(set->flags)) continue; if (cmd->obj == CMD_OBJ_MAPS && !map_is_literal(set->flags)) continue; - set_print_declaration(set, &opts, &ctx->nft->output); - nft_print(&ctx->nft->output, "%s}%s", opts.tab, opts.nl); + set_print(set, &ctx->nft->output); } nft_print(&ctx->nft->output, "}\n"); @@ -1693,9 +1642,10 @@ struct obj *obj_alloc(const struct location *loc) { struct obj *obj; + assert(loc); + obj = xzalloc(sizeof(*obj)); - if (loc != NULL) - obj->location = *loc; + obj->location = *loc; obj->refcnt = 1; return obj; @@ -1711,9 +1661,18 @@ void obj_free(struct obj *obj) { if (--obj->refcnt > 0) return; - xfree(obj->comment); + free_const(obj->comment); handle_free(&obj->handle); - xfree(obj); + if (obj->type == NFT_OBJECT_CT_TIMEOUT) { + struct timeout_state *ts, *next; + + list_for_each_entry_safe(ts, next, &obj->ct_timeout.timeout_list, head) { + list_del(&ts->head); + free_const(ts->timeout_str); + free(ts); + } + } + free(obj); } struct obj *obj_lookup_fuzzy(const char *obj_name, @@ -1728,10 +1687,6 @@ struct obj *obj_lookup_fuzzy(const char *obj_name, list_for_each_entry(table, &cache->table_cache.list, cache.list) { list_for_each_entry(obj, &table->obj_cache.list, cache.list) { - if (!strcmp(obj->handle.obj.name, obj_name)) { - *t = table; - return obj; - } if (string_misspell_update(obj->handle.obj.name, obj_name, obj, &st)) *t = table; @@ -1742,10 +1697,10 @@ struct obj *obj_lookup_fuzzy(const char *obj_name, static void print_proto_name_proto(uint8_t l4, struct output_ctx *octx) { - const struct protoent *p = getprotobynumber(l4); + char name[NFT_PROTONAME_MAXSIZE]; - if (p) - nft_print(octx, "%s", p->p_name); + if (nft_getprotobynumber(l4, name, sizeof(name))) + nft_print(octx, "%s", name); else nft_print(octx, "%d", l4); } @@ -1760,11 +1715,14 @@ static void print_proto_timeout_policy(uint8_t l4, const uint32_t *timeout, nft_print(octx, "%s%spolicy = { ", opts->tab, opts->tab); for (i = 0; i < timeout_protocol[l4].array_size; i++) { if (timeout[i] != timeout_protocol[l4].dflt_timeout[i]) { + uint64_t timeout_ms; + if (comma) nft_print(octx, ", "); - nft_print(octx, "%s : %u", - timeout_protocol[l4].state_to_name[i], - timeout[i]); + timeout_ms = timeout[i] * 1000u; + nft_print(octx, "%s : ", + timeout_protocol[l4].state_to_name[i]); + time_print(timeout_ms, octx); comma = true; } } @@ -1808,13 +1766,12 @@ static void obj_print_data(const struct obj *obj, 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"); - break; - } - nft_print(octx, "packets %" PRIu64 " bytes %" PRIu64 "%s", - obj->counter.packets, obj->counter.bytes, opts->nl); + if (nft_output_stateless(octx)) + nft_print(octx, "%s", opts->nl); + else + nft_print(octx, "%s%s%spackets %" PRIu64 " bytes %" PRIu64 "%s", + opts->nl, opts->tab, opts->tab, + obj->counter.packets, obj->counter.bytes, opts->nl); break; case NFT_OBJECT_QUOTA: { const char *data_unit; @@ -1987,7 +1944,7 @@ static const char * const obj_type_name_array[] = { [NFT_OBJECT_CT_EXPECT] = "ct expectation", }; -const char *obj_type_name(enum stmt_types type) +const char *obj_type_name(unsigned int type) { assert(type <= NFT_OBJECT_MAX && obj_type_name_array[type]); @@ -2005,7 +1962,7 @@ static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = { [NFT_OBJECT_CT_EXPECT] = CMD_OBJ_CT_EXPECT, }; -uint32_t obj_type_to_cmd(uint32_t type) +enum cmd_obj obj_type_to_cmd(uint32_t type) { assert(type <= NFT_OBJECT_MAX && obj_type_cmd_array[type]); @@ -2097,9 +2054,10 @@ struct flowtable *flowtable_alloc(const struct location *loc) { struct flowtable *flowtable; + assert(loc); + flowtable = xzalloc(sizeof(*flowtable)); - if (loc != NULL) - flowtable->location = *loc; + flowtable->location = *loc; flowtable->refcnt = 1; return flowtable; @@ -2123,10 +2081,10 @@ void flowtable_free(struct flowtable *flowtable) if (flowtable->dev_array != NULL) { for (i = 0; i < flowtable->dev_array_len; i++) - xfree(flowtable->dev_array[i]); - xfree(flowtable->dev_array); + free_const(flowtable->dev_array[i]); + free(flowtable->dev_array); } - xfree(flowtable); + free(flowtable); } static void flowtable_print_declaration(const struct flowtable *flowtable, @@ -2149,12 +2107,15 @@ static void flowtable_print_declaration(const struct flowtable *flowtable, if (nft_output_handle(octx)) nft_print(octx, " # handle %" PRIu64, flowtable->handle.handle.id); nft_print(octx, "%s", opts->nl); - nft_print(octx, "%s%shook %s priority %s%s", - opts->tab, opts->tab, - hooknum2str(NFPROTO_NETDEV, flowtable->hook.num), - prio2str(octx, priobuf, sizeof(priobuf), NFPROTO_NETDEV, - flowtable->hook.num, flowtable->priority.expr), - opts->stmt_separator); + + if (flowtable->priority.expr) { + nft_print(octx, "%s%shook %s priority %s%s", + opts->tab, opts->tab, + hooknum2str(NFPROTO_NETDEV, flowtable->hook.num), + prio2str(octx, priobuf, sizeof(priobuf), NFPROTO_NETDEV, + flowtable->hook.num, flowtable->priority.expr), + opts->stmt_separator); + } if (flowtable->dev_array_len > 0) { nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab); @@ -2206,10 +2167,6 @@ struct flowtable *flowtable_lookup_fuzzy(const char *ft_name, list_for_each_entry(table, &cache->table_cache.list, cache.list) { list_for_each_entry(ft, &table->ft_cache.list, cache.list) { - if (!strcmp(ft->handle.flowtable.name, ft_name)) { - *t = table; - return ft; - } if (string_misspell_update(ft->handle.flowtable.name, ft_name, ft, &st)) *t = table; @@ -2276,15 +2233,10 @@ static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd) table->handle.family != family) continue; - cmd->handle.family = table->handle.family; - cmd->handle.table.name = table->handle.table.name; - - if (do_list_table(ctx, cmd, table) < 0) + if (do_list_table(ctx, table) < 0) return -1; } - cmd->handle.table.name = NULL; - return 0; } @@ -2308,9 +2260,14 @@ static int do_list_tables(struct netlink_ctx *ctx, struct cmd *cmd) static void table_print_declaration(struct table *table, struct output_ctx *octx) { - nft_print(octx, "table %s %s {\n", - family2str(table->handle.family), - table->handle.table.name); + const char *family = family2str(table->handle.family); + + if (table->has_xt_stmts) + fprintf(octx->error_fp, + "# Warning: table %s %s is managed by iptables-nft, do not touch!\n", + family, table->handle.table.name); + + nft_print(octx, "table %s %s {\n", family, table->handle.table.name); } static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd, @@ -2320,13 +2277,9 @@ static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd, table_print_declaration(table, &ctx->nft->output); - list_for_each_entry(chain, &table->chain_cache.list, cache.list) { - if (chain->handle.family != cmd->handle.family || - strcmp(cmd->handle.chain.name, chain->handle.chain.name) != 0) - continue; - + chain = chain_cache_find(table, cmd->handle.chain.name); + if (chain) chain_print(chain, &ctx->nft->output); - } nft_print(&ctx->nft->output, "}\n"); @@ -2372,17 +2325,30 @@ static void __do_list_set(struct netlink_ctx *ctx, struct cmd *cmd, static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd, struct table *table) { - struct set *set; + struct set *set = cmd->set; - set = set_cache_find(table, cmd->handle.set.name); - if (set == NULL) - return -1; + if (!set) { + set = set_cache_find(table, cmd->handle.set.name); + if (set == NULL) + return -1; + } __do_list_set(ctx, cmd, set); return 0; } +static int do_list_hooks(struct netlink_ctx *ctx, struct cmd *cmd) +{ + const char *devname = cmd->handle.obj.name; + int hooknum = -1; + + if (cmd->handle.chain.name) + hooknum = cmd->handle.chain_id; + + return mnl_nft_dump_nf_hooks(ctx, cmd->handle.family, hooknum, devname); +} + static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) { struct table *table = NULL; @@ -2398,7 +2364,7 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_OBJ_TABLE: if (!cmd->handle.table.name) return do_list_tables(ctx, cmd); - return do_list_table(ctx, cmd, table); + return do_list_table(ctx, table); case CMD_OBJ_CHAIN: return do_list_chain(ctx, cmd, table); case CMD_OBJ_CHAINS: @@ -2408,6 +2374,8 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_OBJ_SET: return do_list_set(ctx, cmd, table); case CMD_OBJ_RULESET: + case CMD_OBJ_RULES: + case CMD_OBJ_RULE: return do_list_ruleset(ctx, cmd); case CMD_OBJ_METERS: return do_list_sets(ctx, cmd); @@ -2427,8 +2395,10 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_OBJ_CT_HELPERS: return do_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER); case CMD_OBJ_CT_TIMEOUT: + case CMD_OBJ_CT_TIMEOUTS: return do_list_obj(ctx, cmd, NFT_OBJECT_CT_TIMEOUT); case CMD_OBJ_CT_EXPECT: + case CMD_OBJ_CT_EXPECTATIONS: return do_list_obj(ctx, cmd, NFT_OBJECT_CT_EXPECT); case CMD_OBJ_LIMIT: case CMD_OBJ_LIMITS: @@ -2443,6 +2413,8 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) return do_list_flowtable(ctx, cmd, table); case CMD_OBJ_FLOWTABLES: return do_list_flowtables(ctx, cmd); + case CMD_OBJ_HOOKS: + return do_list_hooks(ctx, cmd); default: BUG("invalid command object type %u\n", cmd->obj); } @@ -2450,7 +2422,7 @@ 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) +static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd, bool reset) { struct set *set, *new_set; struct expr *init; @@ -2468,7 +2440,7 @@ 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, - cmd->elem.set, new_set, init); + cmd->elem.set, new_set, init, reset); if (err >= 0) __do_list_set(ctx, cmd, new_set); @@ -2484,7 +2456,7 @@ static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd) { switch (cmd->obj) { case CMD_OBJ_ELEMENTS: - return do_get_setelems(ctx, cmd); + return do_get_setelems(ctx, cmd, false); default: BUG("invalid command object type %u\n", cmd->obj); } @@ -2513,6 +2485,23 @@ static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_OBJ_QUOTA: type = NFT_OBJECT_QUOTA; break; + case CMD_OBJ_RULES: + ret = netlink_reset_rules(ctx, cmd, true); + if (ret < 0) + return ret; + + return do_command_list(ctx, cmd); + case CMD_OBJ_RULE: + return netlink_reset_rules(ctx, cmd, false); + case CMD_OBJ_ELEMENTS: + return do_get_setelems(ctx, cmd, true); + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + ret = netlink_list_setelems(ctx, &cmd->handle, cmd->set, true); + if (ret < 0) + return ret; + + return do_command_list(ctx, cmd); default: BUG("invalid command object type %u\n", cmd->obj); } @@ -2630,6 +2619,7 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_REPLACE: return do_command_replace(ctx, cmd); case CMD_DELETE: + case CMD_DESTROY: return do_command_delete(ctx, cmd); case CMD_GET: return do_command_get(ctx, cmd); @@ -2745,49 +2735,78 @@ static void payload_do_merge(struct stmt *sa[], unsigned int n) } /** - * payload_try_merge - try to merge consecutive payload match statements + * stmt_reduce - reduce statements in rule * * @rule: nftables rule * + * This function aims to: + * + * - remove redundant statement, e.g. remove 'meta protocol ip' if family is ip + * - merge consecutive payload match statements + * * Locate sequences of payload match statements referring to adjacent * header locations and merge those using only equality relations. * * As a side-effect, payload match statements are ordered in ascending * order according to the location of the payload. */ -static void payload_try_merge(const struct rule *rule) +static void stmt_reduce(const struct rule *rule) { + struct stmt *stmt, *dstmt = NULL, *next; struct stmt *sa[rule->num_stmts]; - struct stmt *stmt, *next; unsigned int idx = 0; list_for_each_entry_safe(stmt, next, &rule->stmts, list) { + /* delete this redundant statement */ + if (dstmt) { + list_del(&dstmt->list); + stmt_free(dstmt); + dstmt = NULL; + } + /* Must not merge across other statements */ - if (stmt->ops->type != STMT_EXPRESSION) - goto do_merge; + if (stmt->ops->type != STMT_EXPRESSION) { + if (idx >= 2) + payload_do_merge(sa, idx); + idx = 0; + continue; + } if (stmt->expr->etype != EXPR_RELATIONAL) continue; - if (stmt->expr->left->etype != EXPR_PAYLOAD) - continue; if (stmt->expr->right->etype != EXPR_VALUE) continue; - switch (stmt->expr->op) { - case OP_EQ: - case OP_IMPLICIT: - case OP_NEQ: - break; - default: - continue; - } - sa[idx++] = stmt; - continue; -do_merge: - if (idx < 2) - continue; - payload_do_merge(sa, idx); - idx = 0; + if (stmt->expr->left->etype == EXPR_PAYLOAD) { + switch (stmt->expr->op) { + case OP_EQ: + case OP_IMPLICIT: + break; + default: + continue; + } + + sa[idx++] = stmt; + } else if (stmt->expr->left->etype == EXPR_META) { + switch (stmt->expr->op) { + case OP_EQ: + case OP_IMPLICIT: + if (stmt->expr->left->meta.key == NFT_META_PROTOCOL && + !stmt->expr->left->meta.inner_desc) { + uint16_t protocol; + + protocol = mpz_get_uint16(stmt->expr->right->value); + if ((rule->handle.family == NFPROTO_IPV4 && + protocol == ETH_P_IP) || + (rule->handle.family == NFPROTO_IPV6 && + protocol == ETH_P_IPV6)) + dstmt = stmt; + } + break; + default: + break; + } + } } if (idx > 1) @@ -2796,6 +2815,6 @@ do_merge: struct error_record *rule_postprocess(struct rule *rule) { - payload_try_merge(rule); + stmt_reduce(rule); return NULL; } |