Commit 115a60b1 authored by Patrick McHardy's avatar Patrick McHardy Committed by Pablo Neira Ayuso

netfilter: nf_tables: add support for multi family tables

Add support to register chains to multiple hooks for different address
families for mixed IPv4/IPv6 tables.
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
parent c9484874
...@@ -422,6 +422,8 @@ struct nft_stats { ...@@ -422,6 +422,8 @@ struct nft_stats {
u64 pkts; u64 pkts;
}; };
#define NFT_HOOK_OPS_MAX 2
/** /**
* struct nft_base_chain - nf_tables base chain * struct nft_base_chain - nf_tables base chain
* *
...@@ -432,7 +434,7 @@ struct nft_stats { ...@@ -432,7 +434,7 @@ struct nft_stats {
* @chain: the chain * @chain: the chain
*/ */
struct nft_base_chain { struct nft_base_chain {
struct nf_hook_ops ops; struct nf_hook_ops ops[NFT_HOOK_OPS_MAX];
enum nft_chain_type type; enum nft_chain_type type;
u8 policy; u8 policy;
struct nft_stats __percpu *stats; struct nft_stats __percpu *stats;
...@@ -476,6 +478,8 @@ struct nft_table { ...@@ -476,6 +478,8 @@ struct nft_table {
* @nhooks: number of hooks in this family * @nhooks: number of hooks in this family
* @owner: module owner * @owner: module owner
* @tables: used internally * @tables: used internally
* @nops: number of hook ops in this family
* @hook_ops_init: initialization function for chain hook ops
* @hooks: hookfn overrides for packet validation * @hooks: hookfn overrides for packet validation
*/ */
struct nft_af_info { struct nft_af_info {
...@@ -484,6 +488,9 @@ struct nft_af_info { ...@@ -484,6 +488,9 @@ struct nft_af_info {
unsigned int nhooks; unsigned int nhooks;
struct module *owner; struct module *owner;
struct list_head tables; struct list_head tables;
unsigned int nops;
void (*hook_ops_init)(struct nf_hook_ops *,
unsigned int);
nf_hookfn *hooks[NF_MAX_HOOKS]; nf_hookfn *hooks[NF_MAX_HOOKS];
}; };
......
...@@ -32,6 +32,7 @@ static struct nft_af_info nft_af_bridge __read_mostly = { ...@@ -32,6 +32,7 @@ static struct nft_af_info nft_af_bridge __read_mostly = {
.family = NFPROTO_BRIDGE, .family = NFPROTO_BRIDGE,
.nhooks = NF_BR_NUMHOOKS, .nhooks = NF_BR_NUMHOOKS,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.nops = 1,
.hooks = { .hooks = {
[NF_BR_LOCAL_IN] = nft_do_chain_bridge, [NF_BR_LOCAL_IN] = nft_do_chain_bridge,
[NF_BR_FORWARD] = nft_do_chain_bridge, [NF_BR_FORWARD] = nft_do_chain_bridge,
......
...@@ -32,6 +32,7 @@ static struct nft_af_info nft_af_arp __read_mostly = { ...@@ -32,6 +32,7 @@ static struct nft_af_info nft_af_arp __read_mostly = {
.family = NFPROTO_ARP, .family = NFPROTO_ARP,
.nhooks = NF_ARP_NUMHOOKS, .nhooks = NF_ARP_NUMHOOKS,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.nops = 1,
.hooks = { .hooks = {
[NF_ARP_IN] = nft_do_chain_arp, [NF_ARP_IN] = nft_do_chain_arp,
[NF_ARP_OUT] = nft_do_chain_arp, [NF_ARP_OUT] = nft_do_chain_arp,
......
...@@ -52,6 +52,7 @@ static struct nft_af_info nft_af_ipv4 __read_mostly = { ...@@ -52,6 +52,7 @@ static struct nft_af_info nft_af_ipv4 __read_mostly = {
.family = NFPROTO_IPV4, .family = NFPROTO_IPV4,
.nhooks = NF_INET_NUMHOOKS, .nhooks = NF_INET_NUMHOOKS,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.nops = 1,
.hooks = { .hooks = {
[NF_INET_LOCAL_IN] = nft_do_chain_ipv4, [NF_INET_LOCAL_IN] = nft_do_chain_ipv4,
[NF_INET_LOCAL_OUT] = nft_ipv4_output, [NF_INET_LOCAL_OUT] = nft_ipv4_output,
......
...@@ -51,6 +51,7 @@ static struct nft_af_info nft_af_ipv6 __read_mostly = { ...@@ -51,6 +51,7 @@ static struct nft_af_info nft_af_ipv6 __read_mostly = {
.family = NFPROTO_IPV6, .family = NFPROTO_IPV6,
.nhooks = NF_INET_NUMHOOKS, .nhooks = NF_INET_NUMHOOKS,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.nops = 1,
.hooks = { .hooks = {
[NF_INET_LOCAL_IN] = nft_do_chain_ipv6, [NF_INET_LOCAL_IN] = nft_do_chain_ipv6,
[NF_INET_LOCAL_OUT] = nft_ipv6_output, [NF_INET_LOCAL_OUT] = nft_ipv6_output,
......
...@@ -307,7 +307,8 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb, ...@@ -307,7 +307,8 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
return err; return err;
} }
static int nf_tables_table_enable(struct nft_table *table) static int nf_tables_table_enable(const struct nft_af_info *afi,
struct nft_table *table)
{ {
struct nft_chain *chain; struct nft_chain *chain;
int err, i = 0; int err, i = 0;
...@@ -316,7 +317,7 @@ static int nf_tables_table_enable(struct nft_table *table) ...@@ -316,7 +317,7 @@ static int nf_tables_table_enable(struct nft_table *table)
if (!(chain->flags & NFT_BASE_CHAIN)) if (!(chain->flags & NFT_BASE_CHAIN))
continue; continue;
err = nf_register_hook(&nft_base_chain(chain)->ops); err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
if (err < 0) if (err < 0)
goto err; goto err;
...@@ -331,18 +332,20 @@ static int nf_tables_table_enable(struct nft_table *table) ...@@ -331,18 +332,20 @@ static int nf_tables_table_enable(struct nft_table *table)
if (i-- <= 0) if (i-- <= 0)
break; break;
nf_unregister_hook(&nft_base_chain(chain)->ops); nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
} }
return err; return err;
} }
static int nf_tables_table_disable(struct nft_table *table) static int nf_tables_table_disable(const struct nft_af_info *afi,
struct nft_table *table)
{ {
struct nft_chain *chain; struct nft_chain *chain;
list_for_each_entry(chain, &table->chains, list) { list_for_each_entry(chain, &table->chains, list) {
if (chain->flags & NFT_BASE_CHAIN) if (chain->flags & NFT_BASE_CHAIN)
nf_unregister_hook(&nft_base_chain(chain)->ops); nf_unregister_hooks(nft_base_chain(chain)->ops,
afi->nops);
} }
return 0; return 0;
...@@ -365,12 +368,12 @@ static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb, ...@@ -365,12 +368,12 @@ static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb,
if ((flags & NFT_TABLE_F_DORMANT) && if ((flags & NFT_TABLE_F_DORMANT) &&
!(table->flags & NFT_TABLE_F_DORMANT)) { !(table->flags & NFT_TABLE_F_DORMANT)) {
ret = nf_tables_table_disable(table); ret = nf_tables_table_disable(afi, table);
if (ret >= 0) if (ret >= 0)
table->flags |= NFT_TABLE_F_DORMANT; table->flags |= NFT_TABLE_F_DORMANT;
} else if (!(flags & NFT_TABLE_F_DORMANT) && } else if (!(flags & NFT_TABLE_F_DORMANT) &&
table->flags & NFT_TABLE_F_DORMANT) { table->flags & NFT_TABLE_F_DORMANT) {
ret = nf_tables_table_enable(table); ret = nf_tables_table_enable(afi, table);
if (ret >= 0) if (ret >= 0)
table->flags &= ~NFT_TABLE_F_DORMANT; table->flags &= ~NFT_TABLE_F_DORMANT;
} }
...@@ -598,7 +601,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, ...@@ -598,7 +601,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
if (chain->flags & NFT_BASE_CHAIN) { if (chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain = nft_base_chain(chain); const struct nft_base_chain *basechain = nft_base_chain(chain);
const struct nf_hook_ops *ops = &basechain->ops; const struct nf_hook_ops *ops = &basechain->ops[0];
struct nlattr *nest; struct nlattr *nest;
nest = nla_nest_start(skb, NFTA_CHAIN_HOOK); nest = nla_nest_start(skb, NFTA_CHAIN_HOOK);
...@@ -832,6 +835,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -832,6 +835,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
u64 handle = 0; u64 handle = 0;
unsigned int i;
int err; int err;
bool create; bool create;
...@@ -904,7 +908,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -904,7 +908,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
if (nla[NFTA_CHAIN_HOOK]) { if (nla[NFTA_CHAIN_HOOK]) {
struct nf_hook_ops *ops; struct nf_hook_ops *ops;
nf_hookfn *hookfn; nf_hookfn *hookfn;
u32 hooknum; u32 hooknum, priority;
int type = NFT_CHAIN_T_DEFAULT; int type = NFT_CHAIN_T_DEFAULT;
if (nla[NFTA_CHAIN_TYPE]) { if (nla[NFTA_CHAIN_TYPE]) {
...@@ -926,6 +930,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -926,6 +930,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
if (hooknum >= afi->nhooks) if (hooknum >= afi->nhooks)
return -EINVAL; return -EINVAL;
priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
if (!(chain_type[family][type]->hook_mask & (1 << hooknum))) if (!(chain_type[family][type]->hook_mask & (1 << hooknum)))
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -938,15 +943,19 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -938,15 +943,19 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
basechain->type = type; basechain->type = type;
chain = &basechain->chain; chain = &basechain->chain;
ops = &basechain->ops; for (i = 0; i < afi->nops; i++) {
ops = &basechain->ops[i];
ops->pf = family; ops->pf = family;
ops->owner = afi->owner; ops->owner = afi->owner;
ops->hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); ops->hooknum = hooknum;
ops->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY])); ops->priority = priority;
ops->priv = chain; ops->priv = chain;
ops->hook = afi->hooks[ops->hooknum]; ops->hook = afi->hooks[ops->hooknum];
if (hookfn) if (hookfn)
ops->hook = hookfn; ops->hook = hookfn;
if (afi->hook_ops_init)
afi->hook_ops_init(ops, i);
}
chain->flags |= NFT_BASE_CHAIN; chain->flags |= NFT_BASE_CHAIN;
...@@ -993,7 +1002,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -993,7 +1002,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
if (!(table->flags & NFT_TABLE_F_DORMANT) && if (!(table->flags & NFT_TABLE_F_DORMANT) &&
chain->flags & NFT_BASE_CHAIN) { chain->flags & NFT_BASE_CHAIN) {
err = nf_register_hook(&nft_base_chain(chain)->ops); err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
if (err < 0) { if (err < 0) {
free_percpu(basechain->stats); free_percpu(basechain->stats);
kfree(basechain); kfree(basechain);
...@@ -1052,7 +1061,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -1052,7 +1061,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
if (!(table->flags & NFT_TABLE_F_DORMANT) && if (!(table->flags & NFT_TABLE_F_DORMANT) &&
chain->flags & NFT_BASE_CHAIN) chain->flags & NFT_BASE_CHAIN)
nf_unregister_hook(&nft_base_chain(chain)->ops); nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN, nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
family); family);
......
...@@ -92,7 +92,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par, ...@@ -92,7 +92,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par,
if (ctx->chain->flags & NFT_BASE_CHAIN) { if (ctx->chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain = const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain); nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops; const struct nf_hook_ops *ops = &basechain->ops[0];
par->hook_mask = 1 << ops->hooknum; par->hook_mask = 1 << ops->hooknum;
} }
...@@ -253,7 +253,7 @@ static int nft_target_validate(const struct nft_ctx *ctx, ...@@ -253,7 +253,7 @@ static int nft_target_validate(const struct nft_ctx *ctx,
if (ctx->chain->flags & NFT_BASE_CHAIN) { if (ctx->chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain = const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain); nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops; const struct nf_hook_ops *ops = &basechain->ops[0];
hook_mask = 1 << ops->hooknum; hook_mask = 1 << ops->hooknum;
if (hook_mask & target->hooks) if (hook_mask & target->hooks)
...@@ -323,7 +323,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, ...@@ -323,7 +323,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
if (ctx->chain->flags & NFT_BASE_CHAIN) { if (ctx->chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain = const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain); nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops; const struct nf_hook_ops *ops = &basechain->ops[0];
par->hook_mask = 1 << ops->hooknum; par->hook_mask = 1 << ops->hooknum;
} }
...@@ -449,7 +449,7 @@ static int nft_match_validate(const struct nft_ctx *ctx, ...@@ -449,7 +449,7 @@ static int nft_match_validate(const struct nft_ctx *ctx,
if (ctx->chain->flags & NFT_BASE_CHAIN) { if (ctx->chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain = const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain); nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops; const struct nf_hook_ops *ops = &basechain->ops[0];
hook_mask = 1 << ops->hooknum; hook_mask = 1 << ops->hooknum;
if (hook_mask & match->hooks) if (hook_mask & match->hooks)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment