From patchwork Tue May 15 12:23:43 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Taehee Yoo X-Patchwork-Id: 913601 X-Patchwork-Delegate: pablo@netfilter.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netfilter-devel-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="fGTo71u7"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 40lcDl29BKz9ryk for ; Tue, 15 May 2018 22:23:51 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753303AbeEOMXu (ORCPT ); Tue, 15 May 2018 08:23:50 -0400 Received: from mail-pg0-f68.google.com ([74.125.83.68]:40969 "EHLO mail-pg0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752421AbeEOMXu (ORCPT ); Tue, 15 May 2018 08:23:50 -0400 Received: by mail-pg0-f68.google.com with SMTP id w4-v6so5325235pgq.8 for ; Tue, 15 May 2018 05:23:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=WkUaJawZxzZQ3msuBfnbljKjc1Y9dOuPzCWloR9Su7M=; b=fGTo71u7cP4N9onSJ8ormdDiYZBUTzb5hmcSA6FVZawfBgcpCSCUpfZAdvGkXq+JTr c/sd2TA77CJLfOw3DxxG8cVXLV0ItHznSeDKG0YjTE+2sB55ORIn9boWRgRlBbMWr0ea L5XmcderDDMaGNe8MdjitGg0am8jOoeFGjAgYDyhUaWCHUQhTsIHzWg5OdnLX0nyn/8l 3CP7KaRrYiqavTBoPPHIaZSLf+uQi882WdnaqQ7OTABphj9FM9YM8+QBB2oxpgcqOk1J OYunroYXhjVXsZgemkYGMiBhmrbPyxZUvOC5+9VT+dyZqHFxEEiWoMxA5J4r63TfksH2 Zt1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=WkUaJawZxzZQ3msuBfnbljKjc1Y9dOuPzCWloR9Su7M=; b=hlXIbdfAEusGN43b8InyQRE80dqUIaLYQxei5fS+HNqHjj3rh8n50Xl+bwNzEQTike Nso1WBfwWCKxVRbjq3z2Tu/dkfYvCcO5kU8wtpbCYcq4uBeAVCoBQvjog4pzSCSodpKL LYMMBkovAdV9Z4x/DQR0zLOlnzm6HIvNmkpEW+p5DrJrCSxgmwvu1eP62TjZIbAEbSTT VJu5on47pgB7HKcdl61GbGa9I81+00EBvQQ8Aak81Y1V2lvm+DcL6azkPC5X9UDh7V/O kWx6l2J82c0ZzBAflqB5hcW2klnuPvpVSJmFdApAaXUNVzDarr5n0cN0oxxLaObsIXgb Dz2g== X-Gm-Message-State: ALKqPwdkt1jLvJcfoj1ijw406Yqh2PGGnXCgi8oEicIOAk0tv4Fp3ylT QemOKurHoSX89k0t3C2z6XqZAQ== X-Google-Smtp-Source: AB8JxZpgDfO5X0jvYRxQLlgohuJuIM82e749+Yotn+8nzzIPiDuf69VUmTw1W1GxbDRfnt5KYbyqow== X-Received: by 2002:a63:b70b:: with SMTP id t11-v6mr11731312pgf.390.1526387029511; Tue, 15 May 2018 05:23:49 -0700 (PDT) Received: from ap-To-be-filled-by-O-E-M.8.8.8.8 ([125.130.197.10]) by smtp.gmail.com with ESMTPSA id c83-v6sm23284072pfc.111.2018.05.15.05.23.47 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 15 May 2018 05:23:48 -0700 (PDT) From: Taehee Yoo To: pablo@netfilter.org, netfilter-devel@vger.kernel.org Cc: ap420073@gmail.com Subject: [PATCH nf 3/5] netfilter: nf_tables: add type and hook validate routine Date: Tue, 15 May 2018 21:23:43 +0900 Message-Id: <20180515122343.29387-1-ap420073@gmail.com> X-Mailer: git-send-email 2.9.3 Sender: netfilter-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org This patch adds validate callback to the nfnetlink_subsysem. It validates type and hook of both basechain and non-basechain. To validate type and hook, it constructs chain information array. Like loop detection routine, validator travels each rules and sets then marks type and hook value to the each chain information array. example : table ip test { chain prerouting { type nat hook prerouting priority 4; jump test1 } chain postrouting { type nat hook postrouting priority 5; jump test1 } chain input { type filter hook input priority 0; jump test1 } chain outout { type filter hook output priority 0; jump test2 } chain test1 { jump test2 counter } chain test2 { counter } } The test1 has below chain information. type = NFT_CHAIN_T_MIX hook = (1 << NF_INET_PRE_ROUTING | 1 << NF_INET_POST_ROUTING | 1 << NF_INET_LOCAL_IN) And the test2 has below chain information. type = NFT_CHAIN_T_MIX hook = (1 << NF_INET_PRE_ROUTING | 1 << NF_INET_POST_ROUTING | 1 << NF_INET_LOCAL_IN | 1 << NF_ONET_LOCAL_OUT) The new type NFT_CHAIN_T_MIX means that chain has both filter and nat type. Then, validator calls expr->ops->validate() Next patch makes expr->ops->validate() to use chain information array insted of basechain's data. Signed-off-by: Taehee Yoo --- include/linux/netfilter/nfnetlink.h | 1 + include/net/netfilter/nf_tables.h | 1 + include/net/netns/nftables.h | 3 + net/netfilter/nf_tables_api.c | 262 ++++++++++++++++++++++++++++++++++++ 4 files changed, 267 insertions(+) diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 34551f8..a641d52 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -29,6 +29,7 @@ struct nfnetlink_subsystem { __u8 subsys_id; /* nfnetlink subsystem ID */ __u8 cb_count; /* number of callbacks */ const struct nfnl_callback *cb; /* callback for individual types */ + int (*validate)(struct net *net); int (*commit)(struct net *net, struct sk_buff *skb); int (*abort)(struct net *net, struct sk_buff *skb); bool (*valid_genid)(struct net *net, u32 genid); diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 7eb4802..9959509 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -877,6 +877,7 @@ enum nft_chain_types { NFT_CHAIN_T_DEFAULT = 0, NFT_CHAIN_T_ROUTE, NFT_CHAIN_T_NAT, + NFT_CHAIN_T_MIX, NFT_CHAIN_T_MAX }; diff --git a/include/net/netns/nftables.h b/include/net/netns/nftables.h index 29c3851..61e94e5 100644 --- a/include/net/netns/nftables.h +++ b/include/net/netns/nftables.h @@ -4,9 +4,12 @@ #include +struct nft_chain_info; + struct netns_nftables { struct list_head tables; struct list_head commit_list; + struct nft_chain_info *chain_info; unsigned int base_seq; u8 gencursor; }; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 13c2fc3..36d8fba 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -5841,6 +5841,267 @@ static void nf_tables_commit_release(struct net *net) } } +struct nft_chain_info { + u8 type; + unsigned int hooknum; +}; + +static inline struct nft_chain_info *nft_get_chain_info(struct net *net, + struct nft_chain *chain) +{ + return net->nft.chain_info + chain->handle; +} + +static int nft_validate_chain(struct net *net, struct nft_chain *chain) +{ + struct nft_table *table = chain->table; + struct nft_expr *expr, *last; + struct nft_rule *rule; + struct nft_ctx ctx; + + list_for_each_entry(rule, &chain->rules, list) { + if (!nft_is_active_next(net, rule)) + continue; + nft_rule_for_each_expr(expr, last, rule) { + const struct nft_data *data = NULL; + int err = 0; + + if (!expr->ops->validate) + continue; + + ctx.net = net; + ctx.family = table->family; + ctx.table = table; + ctx.chain = chain; + err = expr->ops->validate(&ctx, expr, &data); + if (err < 0) + return err; + + if (!data) + continue; + + switch (data->verdict.code) { + case NFT_JUMP: + case NFT_GOTO: + err = nft_validate_chain(net, + data->verdict.chain); + if (err < 0) + return err; + default: + break; + } + } + } + return 0; +} + +static int nft_mark_chain_info(struct net *net, + struct nft_chain *pchain, + struct nft_chain *cchain); + +static int nft_mark_chain_info_setelem(const struct nft_ctx *ctx, + struct nft_set *set, + const struct nft_set_iter *iter, + struct nft_set_elem *elem) +{ + const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); + const struct nft_data *data; + + if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) && + *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END) + return 0; + + data = nft_set_ext_data(ext); + switch (data->verdict.code) { + case NFT_JUMP: + case NFT_GOTO: + return nft_mark_chain_info(ctx->net, ctx->chain, + data->verdict.chain); + default: + return 0; + } + + return 0; +} + +static int nft_mark_set_elem(struct net *net, struct nft_chain *chain) +{ + struct nft_ctx ctx; + struct nft_table *table = chain->table; + struct nft_set *set; + struct nft_set_binding *binding; + struct nft_set_iter iter; + + list_for_each_entry(set, &table->sets, list) { + if (!nft_is_active_next(net, set)) + continue; + if (!(set->flags & NFT_SET_MAP) || + set->dtype != NFT_DATA_VERDICT) + continue; + + list_for_each_entry(binding, &set->bindings, list) { + if (!(binding->flags & NFT_SET_MAP) || + binding->chain != chain) + continue; + + iter.genmask = nft_genmask_next(net); + iter.skip = 0; + iter.count = 0; + iter.err = 0; + iter.fn = nft_mark_chain_info_setelem; + + ctx.net = net; + ctx.family = table->family; + ctx.table = table; + ctx.chain = chain; + set->ops->walk(&ctx, set, &iter); + if (iter.err < 0) + return iter.err; + } + } + return 0; +} + +static int nft_mark_rule(struct net *net, struct nft_chain *chain) +{ + struct nft_rule *rule; + struct nft_expr *expr, *last; + + list_for_each_entry(rule, &chain->rules, list) { + if (!nft_is_active_next(net, rule)) + continue; + nft_rule_for_each_expr(expr, last, rule) { + const struct nft_data *data = NULL; + int err; + + if (!expr->ops->validate) + continue; + if (strcmp(expr->ops->type->name, "immediate")) + continue; + + err = expr->ops->validate(NULL, expr, &data); + if (err < 0) + return err; + + if (!data) + continue; + + switch (data->verdict.code) { + case NFT_JUMP: + case NFT_GOTO: + err = nft_mark_chain_info(net, chain, + data->verdict.chain); + if (err < 0) + return err; + default: + break; + } + } + } + return 0; +} + +static int nft_mark_chain_info(struct net *net, + struct nft_chain *pchain, + struct nft_chain *cchain) +{ + struct nft_chain_info before; + struct nft_chain_info *pinfo = nft_get_chain_info(net, pchain); + struct nft_chain_info *cinfo = nft_get_chain_info(net, cchain); + int err = 0; + + if (pchain != cchain) { + if (unlikely(nft_is_base_chain(cchain))) { + WARN_ON(1); + return -ELOOP; + } + + before.type = cinfo->type; + before.hooknum = cinfo->hooknum; + + if (cinfo->type && cinfo->type != pinfo->type) + cinfo->type = NFT_CHAIN_T_MIX; + else + cinfo->type = pinfo->type; + cinfo->hooknum |= pinfo->hooknum; + + if (cinfo->type == before.type && + cinfo->hooknum == before.hooknum) + return 0; + } + + err = nft_mark_rule(net, cchain); + if (err < 0) + return err; + return nft_mark_set_elem(net, cchain); +} + +static int nf_tables_validate(struct net *net) +{ + struct nft_table *table; + struct nft_chain *chain; + struct nft_base_chain *basechain; + struct nft_chain_info *cinfo; + u64 hgenerator = 0; + int err = 0; + + list_for_each_entry(table, &net->nft.tables, list) { + if (!nft_is_active_next(net, table)) + continue; + hgenerator = max_t(u64, hgenerator, table->hgenerator); + } + + if (!hgenerator) + return 0; + + hgenerator++; + net->nft.chain_info = kvmalloc(hgenerator * + sizeof(struct nft_chain_info), + GFP_KERNEL); + if (!net->nft.chain_info) + return -ENOMEM; + + list_for_each_entry(table, &net->nft.tables, list) { + if (!nft_is_active_next(net, table)) + continue; + + memset(net->nft.chain_info, 0, + sizeof(struct nft_chain_info) * hgenerator); + + list_for_each_entry(chain, &table->chains, list) { + if (!nft_is_active_next(net, chain)) + continue; + if (!nft_is_base_chain(chain)) + continue; + + basechain = nft_base_chain(chain); + cinfo = nft_get_chain_info(net, chain); + + cinfo->type = basechain->type->type; + cinfo->hooknum |= 1 << basechain->ops.hooknum; + + err = nft_mark_chain_info(net, chain, chain); + if (err) + goto out; + + err = nft_mark_set_elem(net, chain); + if (err < 0) + goto out; + } + list_for_each_entry(chain, &table->chains, list) { + if (!nft_is_active_next(net, chain)) + continue; + err = nft_validate_chain(net, chain); + if (err < 0) + goto out; + } + } + +out: + kvfree(net->nft.chain_info); + return err; +} + static int nf_tables_commit(struct net *net, struct sk_buff *skb) { struct nft_trans *trans, *next; @@ -6128,6 +6389,7 @@ static const struct nfnetlink_subsystem nf_tables_subsys = { .cb_count = NFT_MSG_MAX, .cb = nf_tables_cb, .commit = nf_tables_commit, + .validate = nf_tables_validate, .abort = nf_tables_abort, .valid_genid = nf_tables_valid_genid, };