/* * Copyright (c) 2019 Pablo Neira Ayuso * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 (or any * later) as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags) { switch (cmd->obj) { case CMD_OBJ_CHAIN: case CMD_OBJ_SET: case CMD_OBJ_COUNTER: case CMD_OBJ_QUOTA: case CMD_OBJ_LIMIT: case CMD_OBJ_SECMARK: case CMD_OBJ_FLOWTABLE: flags |= NFT_CACHE_TABLE; break; case CMD_OBJ_ELEMENTS: flags |= NFT_CACHE_TABLE | NFT_CACHE_CHAIN | NFT_CACHE_SET | NFT_CACHE_OBJECT | NFT_CACHE_SETELEM; break; case CMD_OBJ_RULE: flags |= NFT_CACHE_TABLE | NFT_CACHE_CHAIN | NFT_CACHE_SET | NFT_CACHE_OBJECT | NFT_CACHE_FLOWTABLE; if (cmd->handle.index.id || cmd->handle.position.id) flags |= NFT_CACHE_RULE | NFT_CACHE_UPDATE; break; default: break; } return flags; } static unsigned int evaluate_cache_del(struct cmd *cmd, unsigned int flags) { switch (cmd->obj) { case CMD_OBJ_ELEMENTS: flags |= NFT_CACHE_SETELEM; break; default: break; } return flags; } static unsigned int evaluate_cache_get(struct cmd *cmd, unsigned int flags) { switch (cmd->obj) { case CMD_OBJ_ELEMENTS: flags |= NFT_CACHE_TABLE | NFT_CACHE_SET | NFT_CACHE_SETELEM; break; default: break; } return flags; } static unsigned int evaluate_cache_flush(struct cmd *cmd, unsigned int flags) { switch (cmd->obj) { case CMD_OBJ_SET: case CMD_OBJ_MAP: case CMD_OBJ_METER: flags |= NFT_CACHE_SET; break; case CMD_OBJ_RULESET: flags |= NFT_CACHE_FLUSHED; break; default: break; } return flags; } static unsigned int evaluate_cache_rename(struct cmd *cmd, unsigned int flags) { switch (cmd->obj) { case CMD_OBJ_CHAIN: flags |= NFT_CACHE_CHAIN; break; default: break; } return flags; } unsigned int cache_evaluate(struct nft_ctx *nft, struct list_head *cmds) { unsigned int flags = NFT_CACHE_EMPTY; struct cmd *cmd; list_for_each_entry(cmd, cmds, list) { switch (cmd->op) { case CMD_ADD: case CMD_INSERT: case CMD_CREATE: flags = evaluate_cache_add(cmd, flags); if (nft_output_echo(&nft->output)) flags |= NFT_CACHE_FULL; break; case CMD_REPLACE: flags = NFT_CACHE_FULL; break; case CMD_DELETE: flags |= NFT_CACHE_TABLE | NFT_CACHE_CHAIN | NFT_CACHE_SET | NFT_CACHE_FLOWTABLE | NFT_CACHE_OBJECT; flags = evaluate_cache_del(cmd, flags); break; case CMD_GET: flags = evaluate_cache_get(cmd, flags); break; case CMD_RESET: flags |= NFT_CACHE_TABLE; break; case CMD_LIST: case CMD_EXPORT: flags |= NFT_CACHE_FULL | NFT_CACHE_REFRESH; break; case CMD_MONITOR: flags |= NFT_CACHE_FULL; break; case CMD_FLUSH: flags = evaluate_cache_flush(cmd, flags); break; case CMD_RENAME: flags = evaluate_cache_rename(cmd, flags); break; case CMD_DESCRIBE: case CMD_IMPORT: break; default: break; } } 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; }