Commit d54725cd authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: nf_tables: support for multiple devices per netdev hook

This patch allows you to register one netdev basechain to multiple
devices. This adds a new NFTA_HOOK_DEVS netlink attribute to specify
the list of netdevices. Basechains store a list of hooks.
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent bbaef955
...@@ -973,21 +973,21 @@ struct nft_hook { ...@@ -973,21 +973,21 @@ struct nft_hook {
* struct nft_base_chain - nf_tables base chain * struct nft_base_chain - nf_tables base chain
* *
* @ops: netfilter hook ops * @ops: netfilter hook ops
* @hook_list: list of netfilter hooks (for NFPROTO_NETDEV family)
* @type: chain type * @type: chain type
* @policy: default policy * @policy: default policy
* @stats: per-cpu chain stats * @stats: per-cpu chain stats
* @chain: the chain * @chain: the chain
* @dev_name: device name that this base chain is attached to (if any)
* @flow_block: flow block (for hardware offload) * @flow_block: flow block (for hardware offload)
*/ */
struct nft_base_chain { struct nft_base_chain {
struct nf_hook_ops ops; struct nf_hook_ops ops;
struct list_head hook_list;
const struct nft_chain_type *type; const struct nft_chain_type *type;
u8 policy; u8 policy;
u8 flags; u8 flags;
struct nft_stats __percpu *stats; struct nft_stats __percpu *stats;
struct nft_chain chain; struct nft_chain chain;
char dev_name[IFNAMSIZ];
struct flow_block flow_block; struct flow_block flow_block;
}; };
......
...@@ -144,12 +144,14 @@ enum nft_list_attributes { ...@@ -144,12 +144,14 @@ enum nft_list_attributes {
* @NFTA_HOOK_HOOKNUM: netfilter hook number (NLA_U32) * @NFTA_HOOK_HOOKNUM: netfilter hook number (NLA_U32)
* @NFTA_HOOK_PRIORITY: netfilter hook priority (NLA_U32) * @NFTA_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
* @NFTA_HOOK_DEV: netdevice name (NLA_STRING) * @NFTA_HOOK_DEV: netdevice name (NLA_STRING)
* @NFTA_HOOK_DEVS: list of netdevices (NLA_NESTED)
*/ */
enum nft_hook_attributes { enum nft_hook_attributes {
NFTA_HOOK_UNSPEC, NFTA_HOOK_UNSPEC,
NFTA_HOOK_HOOKNUM, NFTA_HOOK_HOOKNUM,
NFTA_HOOK_PRIORITY, NFTA_HOOK_PRIORITY,
NFTA_HOOK_DEV, NFTA_HOOK_DEV,
NFTA_HOOK_DEVS,
__NFTA_HOOK_MAX __NFTA_HOOK_MAX
}; };
#define NFTA_HOOK_MAX (__NFTA_HOOK_MAX - 1) #define NFTA_HOOK_MAX (__NFTA_HOOK_MAX - 1)
......
This diff is collapsed.
...@@ -317,38 +317,47 @@ static int nft_indr_block_offload_cmd(struct nft_base_chain *chain, ...@@ -317,38 +317,47 @@ static int nft_indr_block_offload_cmd(struct nft_base_chain *chain,
#define FLOW_SETUP_BLOCK TC_SETUP_BLOCK #define FLOW_SETUP_BLOCK TC_SETUP_BLOCK
static int nft_flow_block_chain(struct nft_base_chain *basechain, static int nft_flow_block_chain(struct nft_base_chain *basechain,
struct net_device *dev, const struct net_device *this_dev,
enum flow_block_command cmd) enum flow_block_command cmd)
{ {
if (dev->netdev_ops->ndo_setup_tc) struct net_device *dev;
return nft_block_offload_cmd(basechain, dev, cmd); struct nft_hook *hook;
int err;
list_for_each_entry(hook, &basechain->hook_list, list) {
dev = hook->ops.dev;
if (this_dev && this_dev != dev)
continue;
return nft_indr_block_offload_cmd(basechain, dev, cmd); if (dev->netdev_ops->ndo_setup_tc)
err = nft_block_offload_cmd(basechain, dev, cmd);
else
err = nft_indr_block_offload_cmd(basechain, dev, cmd);
if (err < 0)
return err;
}
return 0;
} }
static int nft_flow_offload_chain(struct nft_chain *chain, static int nft_flow_offload_chain(struct nft_chain *chain, u8 *ppolicy,
u8 *ppolicy,
enum flow_block_command cmd) enum flow_block_command cmd)
{ {
struct nft_base_chain *basechain; struct nft_base_chain *basechain;
struct net_device *dev;
u8 policy; u8 policy;
if (!nft_is_base_chain(chain)) if (!nft_is_base_chain(chain))
return -EOPNOTSUPP; return -EOPNOTSUPP;
basechain = nft_base_chain(chain); basechain = nft_base_chain(chain);
dev = basechain->ops.dev;
if (!dev)
return -EOPNOTSUPP;
policy = ppolicy ? *ppolicy : basechain->policy; policy = ppolicy ? *ppolicy : basechain->policy;
/* Only default policy to accept is supported for now. */ /* Only default policy to accept is supported for now. */
if (cmd == FLOW_BLOCK_BIND && policy == NF_DROP) if (cmd == FLOW_BLOCK_BIND && policy == NF_DROP)
return -EOPNOTSUPP; return -EOPNOTSUPP;
return nft_flow_block_chain(basechain, dev, cmd); return nft_flow_block_chain(basechain, NULL, cmd);
} }
int nft_flow_rule_offload_commit(struct net *net) int nft_flow_rule_offload_commit(struct net *net)
...@@ -414,6 +423,7 @@ static struct nft_chain *__nft_offload_get_chain(struct net_device *dev) ...@@ -414,6 +423,7 @@ static struct nft_chain *__nft_offload_get_chain(struct net_device *dev)
{ {
struct nft_base_chain *basechain; struct nft_base_chain *basechain;
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
struct nft_hook *hook, *found;
const struct nft_table *table; const struct nft_table *table;
struct nft_chain *chain; struct nft_chain *chain;
...@@ -426,8 +436,16 @@ static struct nft_chain *__nft_offload_get_chain(struct net_device *dev) ...@@ -426,8 +436,16 @@ static struct nft_chain *__nft_offload_get_chain(struct net_device *dev)
!(chain->flags & NFT_CHAIN_HW_OFFLOAD)) !(chain->flags & NFT_CHAIN_HW_OFFLOAD))
continue; continue;
found = NULL;
basechain = nft_base_chain(chain); basechain = nft_base_chain(chain);
if (strncmp(basechain->dev_name, dev->name, IFNAMSIZ)) list_for_each_entry(hook, &basechain->hook_list, list) {
if (hook->ops.dev != dev)
continue;
found = hook;
break;
}
if (!found)
continue; continue;
return chain; return chain;
......
...@@ -287,28 +287,35 @@ static void nft_netdev_event(unsigned long event, struct net_device *dev, ...@@ -287,28 +287,35 @@ static void nft_netdev_event(unsigned long event, struct net_device *dev,
struct nft_ctx *ctx) struct nft_ctx *ctx)
{ {
struct nft_base_chain *basechain = nft_base_chain(ctx->chain); struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
struct nft_hook *hook, *found = NULL;
int n = 0;
switch (event) { if (event != NETDEV_UNREGISTER)
case NETDEV_UNREGISTER: return;
if (strcmp(basechain->dev_name, dev->name) != 0)
return;
/* UNREGISTER events are also happpening on netns exit.
*
* Altough nf_tables core releases all tables/chains, only
* this event handler provides guarantee that
* basechain.ops->dev is still accessible, so we cannot
* skip exiting net namespaces.
*/
__nft_release_basechain(ctx);
break;
case NETDEV_CHANGENAME:
if (dev->ifindex != basechain->ops.dev->ifindex)
return;
strncpy(basechain->dev_name, dev->name, IFNAMSIZ); list_for_each_entry(hook, &basechain->hook_list, list) {
break; if (hook->ops.dev == dev)
found = hook;
n++;
} }
if (!found)
return;
if (n > 1) {
nf_unregister_net_hook(ctx->net, &found->ops);
list_del_rcu(&found->list);
kfree_rcu(found, rcu);
return;
}
/* UNREGISTER events are also happening on netns exit.
*
* Although nf_tables core releases all tables/chains, only this event
* handler provides guarantee that hook->ops.dev is still accessible,
* so we cannot skip exiting net namespaces.
*/
__nft_release_basechain(ctx);
} }
static int nf_tables_netdev_event(struct notifier_block *this, static int nf_tables_netdev_event(struct notifier_block *this,
......
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