forked from Minki/linux
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: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
bbaef955af
commit
d54725cd11
@ -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 {
|
|||||||
* @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)
|
||||||
|
@ -151,11 +151,64 @@ static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nft_netdev_register_hooks(struct net *net,
|
||||||
|
struct list_head *hook_list)
|
||||||
|
{
|
||||||
|
struct nft_hook *hook;
|
||||||
|
int err, j;
|
||||||
|
|
||||||
|
j = 0;
|
||||||
|
list_for_each_entry(hook, hook_list, list) {
|
||||||
|
err = nf_register_net_hook(net, &hook->ops);
|
||||||
|
if (err < 0)
|
||||||
|
goto err_register;
|
||||||
|
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_register:
|
||||||
|
list_for_each_entry(hook, hook_list, list) {
|
||||||
|
if (j-- <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
nf_unregister_net_hook(net, &hook->ops);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nft_netdev_unregister_hooks(struct net *net,
|
||||||
|
struct list_head *hook_list)
|
||||||
|
{
|
||||||
|
struct nft_hook *hook;
|
||||||
|
|
||||||
|
list_for_each_entry(hook, hook_list, list)
|
||||||
|
nf_unregister_net_hook(net, &hook->ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nft_register_basechain_hooks(struct net *net, int family,
|
||||||
|
struct nft_base_chain *basechain)
|
||||||
|
{
|
||||||
|
if (family == NFPROTO_NETDEV)
|
||||||
|
return nft_netdev_register_hooks(net, &basechain->hook_list);
|
||||||
|
|
||||||
|
return nf_register_net_hook(net, &basechain->ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nft_unregister_basechain_hooks(struct net *net, int family,
|
||||||
|
struct nft_base_chain *basechain)
|
||||||
|
{
|
||||||
|
if (family == NFPROTO_NETDEV)
|
||||||
|
nft_netdev_unregister_hooks(net, &basechain->hook_list);
|
||||||
|
else
|
||||||
|
nf_unregister_net_hook(net, &basechain->ops);
|
||||||
|
}
|
||||||
|
|
||||||
static int nf_tables_register_hook(struct net *net,
|
static int nf_tables_register_hook(struct net *net,
|
||||||
const struct nft_table *table,
|
const struct nft_table *table,
|
||||||
struct nft_chain *chain)
|
struct nft_chain *chain)
|
||||||
{
|
{
|
||||||
const struct nft_base_chain *basechain;
|
struct nft_base_chain *basechain;
|
||||||
const struct nf_hook_ops *ops;
|
const struct nf_hook_ops *ops;
|
||||||
|
|
||||||
if (table->flags & NFT_TABLE_F_DORMANT ||
|
if (table->flags & NFT_TABLE_F_DORMANT ||
|
||||||
@ -168,14 +221,14 @@ static int nf_tables_register_hook(struct net *net,
|
|||||||
if (basechain->type->ops_register)
|
if (basechain->type->ops_register)
|
||||||
return basechain->type->ops_register(net, ops);
|
return basechain->type->ops_register(net, ops);
|
||||||
|
|
||||||
return nf_register_net_hook(net, ops);
|
return nft_register_basechain_hooks(net, table->family, basechain);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nf_tables_unregister_hook(struct net *net,
|
static void nf_tables_unregister_hook(struct net *net,
|
||||||
const struct nft_table *table,
|
const struct nft_table *table,
|
||||||
struct nft_chain *chain)
|
struct nft_chain *chain)
|
||||||
{
|
{
|
||||||
const struct nft_base_chain *basechain;
|
struct nft_base_chain *basechain;
|
||||||
const struct nf_hook_ops *ops;
|
const struct nf_hook_ops *ops;
|
||||||
|
|
||||||
if (table->flags & NFT_TABLE_F_DORMANT ||
|
if (table->flags & NFT_TABLE_F_DORMANT ||
|
||||||
@ -187,7 +240,7 @@ static void nf_tables_unregister_hook(struct net *net,
|
|||||||
if (basechain->type->ops_unregister)
|
if (basechain->type->ops_unregister)
|
||||||
return basechain->type->ops_unregister(net, ops);
|
return basechain->type->ops_unregister(net, ops);
|
||||||
|
|
||||||
nf_unregister_net_hook(net, ops);
|
nft_unregister_basechain_hooks(net, table->family, basechain);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
|
static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
|
||||||
@ -742,7 +795,8 @@ static void nft_table_disable(struct net *net, struct nft_table *table, u32 cnt)
|
|||||||
if (cnt && i++ == cnt)
|
if (cnt && i++ == cnt)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
nf_unregister_net_hook(net, &nft_base_chain(chain)->ops);
|
nft_unregister_basechain_hooks(net, table->family,
|
||||||
|
nft_base_chain(chain));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -757,14 +811,16 @@ static int nf_tables_table_enable(struct net *net, struct nft_table *table)
|
|||||||
if (!nft_is_base_chain(chain))
|
if (!nft_is_base_chain(chain))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
err = nf_register_net_hook(net, &nft_base_chain(chain)->ops);
|
err = nft_register_basechain_hooks(net, table->family,
|
||||||
|
nft_base_chain(chain));
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto err;
|
goto err_register_hooks;
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
|
||||||
|
err_register_hooks:
|
||||||
if (i)
|
if (i)
|
||||||
nft_table_disable(net, table, i);
|
nft_table_disable(net, table, i);
|
||||||
return err;
|
return err;
|
||||||
@ -1225,6 +1281,46 @@ nla_put_failure:
|
|||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nft_dump_basechain_hook(struct sk_buff *skb, int family,
|
||||||
|
const struct nft_base_chain *basechain)
|
||||||
|
{
|
||||||
|
const struct nf_hook_ops *ops = &basechain->ops;
|
||||||
|
struct nft_hook *hook, *first = NULL;
|
||||||
|
struct nlattr *nest, *nest_devs;
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
nest = nla_nest_start_noflag(skb, NFTA_CHAIN_HOOK);
|
||||||
|
if (nest == NULL)
|
||||||
|
goto nla_put_failure;
|
||||||
|
if (nla_put_be32(skb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum)))
|
||||||
|
goto nla_put_failure;
|
||||||
|
if (nla_put_be32(skb, NFTA_HOOK_PRIORITY, htonl(ops->priority)))
|
||||||
|
goto nla_put_failure;
|
||||||
|
|
||||||
|
if (family == NFPROTO_NETDEV) {
|
||||||
|
nest_devs = nla_nest_start_noflag(skb, NFTA_HOOK_DEVS);
|
||||||
|
list_for_each_entry(hook, &basechain->hook_list, list) {
|
||||||
|
if (!first)
|
||||||
|
first = hook;
|
||||||
|
|
||||||
|
if (nla_put_string(skb, NFTA_DEVICE_NAME,
|
||||||
|
hook->ops.dev->name))
|
||||||
|
goto nla_put_failure;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
nla_nest_end(skb, nest_devs);
|
||||||
|
|
||||||
|
if (n == 1 &&
|
||||||
|
nla_put_string(skb, NFTA_HOOK_DEV, first->ops.dev->name))
|
||||||
|
goto nla_put_failure;
|
||||||
|
}
|
||||||
|
nla_nest_end(skb, nest);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
nla_put_failure:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
|
static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
|
||||||
u32 portid, u32 seq, int event, u32 flags,
|
u32 portid, u32 seq, int event, u32 flags,
|
||||||
int family, const struct nft_table *table,
|
int family, const struct nft_table *table,
|
||||||
@ -1253,21 +1349,10 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
|
|||||||
|
|
||||||
if (nft_is_base_chain(chain)) {
|
if (nft_is_base_chain(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;
|
|
||||||
struct nft_stats __percpu *stats;
|
struct nft_stats __percpu *stats;
|
||||||
struct nlattr *nest;
|
|
||||||
|
|
||||||
nest = nla_nest_start_noflag(skb, NFTA_CHAIN_HOOK);
|
if (nft_dump_basechain_hook(skb, family, basechain))
|
||||||
if (nest == NULL)
|
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
if (nla_put_be32(skb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum)))
|
|
||||||
goto nla_put_failure;
|
|
||||||
if (nla_put_be32(skb, NFTA_HOOK_PRIORITY, htonl(ops->priority)))
|
|
||||||
goto nla_put_failure;
|
|
||||||
if (basechain->dev_name[0] &&
|
|
||||||
nla_put_string(skb, NFTA_HOOK_DEV, basechain->dev_name))
|
|
||||||
goto nla_put_failure;
|
|
||||||
nla_nest_end(skb, nest);
|
|
||||||
|
|
||||||
if (nla_put_be32(skb, NFTA_CHAIN_POLICY,
|
if (nla_put_be32(skb, NFTA_CHAIN_POLICY,
|
||||||
htonl(basechain->policy)))
|
htonl(basechain->policy)))
|
||||||
@ -1485,6 +1570,7 @@ static void nf_tables_chain_free_chain_rules(struct nft_chain *chain)
|
|||||||
static void nf_tables_chain_destroy(struct nft_ctx *ctx)
|
static void nf_tables_chain_destroy(struct nft_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct nft_chain *chain = ctx->chain;
|
struct nft_chain *chain = ctx->chain;
|
||||||
|
struct nft_hook *hook, *next;
|
||||||
|
|
||||||
if (WARN_ON(chain->use > 0))
|
if (WARN_ON(chain->use > 0))
|
||||||
return;
|
return;
|
||||||
@ -1495,6 +1581,13 @@ static void nf_tables_chain_destroy(struct nft_ctx *ctx)
|
|||||||
if (nft_is_base_chain(chain)) {
|
if (nft_is_base_chain(chain)) {
|
||||||
struct nft_base_chain *basechain = nft_base_chain(chain);
|
struct nft_base_chain *basechain = nft_base_chain(chain);
|
||||||
|
|
||||||
|
if (ctx->family == NFPROTO_NETDEV) {
|
||||||
|
list_for_each_entry_safe(hook, next,
|
||||||
|
&basechain->hook_list, list) {
|
||||||
|
list_del_rcu(&hook->list);
|
||||||
|
kfree_rcu(hook, rcu);
|
||||||
|
}
|
||||||
|
}
|
||||||
module_put(basechain->type->owner);
|
module_put(basechain->type->owner);
|
||||||
if (rcu_access_pointer(basechain->stats)) {
|
if (rcu_access_pointer(basechain->stats)) {
|
||||||
static_branch_dec(&nft_counters_enabled);
|
static_branch_dec(&nft_counters_enabled);
|
||||||
@ -1599,9 +1692,34 @@ struct nft_chain_hook {
|
|||||||
u32 num;
|
u32 num;
|
||||||
s32 priority;
|
s32 priority;
|
||||||
const struct nft_chain_type *type;
|
const struct nft_chain_type *type;
|
||||||
struct net_device *dev;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int nft_chain_parse_netdev(struct net *net,
|
||||||
|
struct nlattr *tb[],
|
||||||
|
struct list_head *hook_list)
|
||||||
|
{
|
||||||
|
struct nft_hook *hook;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (tb[NFTA_HOOK_DEV]) {
|
||||||
|
hook = nft_netdev_hook_alloc(net, tb[NFTA_HOOK_DEV]);
|
||||||
|
if (IS_ERR(hook))
|
||||||
|
return PTR_ERR(hook);
|
||||||
|
|
||||||
|
list_add_tail(&hook->list, hook_list);
|
||||||
|
} else if (tb[NFTA_HOOK_DEVS]) {
|
||||||
|
err = nf_tables_parse_netdev_hooks(net, tb[NFTA_HOOK_DEVS],
|
||||||
|
hook_list);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int nft_chain_parse_hook(struct net *net,
|
static int nft_chain_parse_hook(struct net *net,
|
||||||
const struct nlattr * const nla[],
|
const struct nlattr * const nla[],
|
||||||
struct nft_chain_hook *hook, u8 family,
|
struct nft_chain_hook *hook, u8 family,
|
||||||
@ -1609,7 +1727,6 @@ static int nft_chain_parse_hook(struct net *net,
|
|||||||
{
|
{
|
||||||
struct nlattr *ha[NFTA_HOOK_MAX + 1];
|
struct nlattr *ha[NFTA_HOOK_MAX + 1];
|
||||||
const struct nft_chain_type *type;
|
const struct nft_chain_type *type;
|
||||||
struct net_device *dev;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
lockdep_assert_held(&net->nft.commit_mutex);
|
lockdep_assert_held(&net->nft.commit_mutex);
|
||||||
@ -1647,23 +1764,14 @@ static int nft_chain_parse_hook(struct net *net,
|
|||||||
|
|
||||||
hook->type = type;
|
hook->type = type;
|
||||||
|
|
||||||
hook->dev = NULL;
|
INIT_LIST_HEAD(&hook->list);
|
||||||
if (family == NFPROTO_NETDEV) {
|
if (family == NFPROTO_NETDEV) {
|
||||||
char ifname[IFNAMSIZ];
|
err = nft_chain_parse_netdev(net, ha, &hook->list);
|
||||||
|
if (err < 0) {
|
||||||
if (!ha[NFTA_HOOK_DEV]) {
|
|
||||||
module_put(type->owner);
|
module_put(type->owner);
|
||||||
return -EOPNOTSUPP;
|
return err;
|
||||||
}
|
}
|
||||||
|
} else if (ha[NFTA_HOOK_DEV] || ha[NFTA_HOOK_DEVS]) {
|
||||||
nla_strlcpy(ifname, ha[NFTA_HOOK_DEV], IFNAMSIZ);
|
|
||||||
dev = __dev_get_by_name(net, ifname);
|
|
||||||
if (!dev) {
|
|
||||||
module_put(type->owner);
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
hook->dev = dev;
|
|
||||||
} else if (ha[NFTA_HOOK_DEV]) {
|
|
||||||
module_put(type->owner);
|
module_put(type->owner);
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
@ -1673,6 +1781,12 @@ static int nft_chain_parse_hook(struct net *net,
|
|||||||
|
|
||||||
static void nft_chain_release_hook(struct nft_chain_hook *hook)
|
static void nft_chain_release_hook(struct nft_chain_hook *hook)
|
||||||
{
|
{
|
||||||
|
struct nft_hook *h, *next;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(h, next, &hook->list, list) {
|
||||||
|
list_del(&h->list);
|
||||||
|
kfree(h);
|
||||||
|
}
|
||||||
module_put(hook->type->owner);
|
module_put(hook->type->owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1697,6 +1811,49 @@ static struct nft_rule **nf_tables_chain_alloc_rules(const struct nft_chain *cha
|
|||||||
return kvmalloc(alloc, GFP_KERNEL);
|
return kvmalloc(alloc, GFP_KERNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nft_basechain_hook_init(struct nf_hook_ops *ops, u8 family,
|
||||||
|
const struct nft_chain_hook *hook,
|
||||||
|
struct nft_chain *chain)
|
||||||
|
{
|
||||||
|
ops->pf = family;
|
||||||
|
ops->hooknum = hook->num;
|
||||||
|
ops->priority = hook->priority;
|
||||||
|
ops->priv = chain;
|
||||||
|
ops->hook = hook->type->hooks[ops->hooknum];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
|
||||||
|
struct nft_chain_hook *hook, u32 flags)
|
||||||
|
{
|
||||||
|
struct nft_chain *chain;
|
||||||
|
struct nft_hook *h;
|
||||||
|
|
||||||
|
basechain->type = hook->type;
|
||||||
|
INIT_LIST_HEAD(&basechain->hook_list);
|
||||||
|
chain = &basechain->chain;
|
||||||
|
|
||||||
|
if (family == NFPROTO_NETDEV) {
|
||||||
|
list_splice_init(&hook->list, &basechain->hook_list);
|
||||||
|
list_for_each_entry(h, &basechain->hook_list, list)
|
||||||
|
nft_basechain_hook_init(&h->ops, family, hook, chain);
|
||||||
|
|
||||||
|
basechain->ops.hooknum = hook->num;
|
||||||
|
basechain->ops.priority = hook->priority;
|
||||||
|
} else {
|
||||||
|
nft_basechain_hook_init(&basechain->ops, family, hook, chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
chain->flags |= NFT_BASE_CHAIN | flags;
|
||||||
|
basechain->policy = NF_ACCEPT;
|
||||||
|
if (chain->flags & NFT_CHAIN_HW_OFFLOAD &&
|
||||||
|
nft_chain_offload_priority(basechain) < 0)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
flow_block_init(&basechain->flow_block);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
|
static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
|
||||||
u8 policy, u32 flags)
|
u8 policy, u32 flags)
|
||||||
{
|
{
|
||||||
@ -1715,7 +1872,6 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
|
|||||||
|
|
||||||
if (nla[NFTA_CHAIN_HOOK]) {
|
if (nla[NFTA_CHAIN_HOOK]) {
|
||||||
struct nft_chain_hook hook;
|
struct nft_chain_hook hook;
|
||||||
struct nf_hook_ops *ops;
|
|
||||||
|
|
||||||
err = nft_chain_parse_hook(net, nla, &hook, family, true);
|
err = nft_chain_parse_hook(net, nla, &hook, family, true);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
@ -1726,9 +1882,7 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
|
|||||||
nft_chain_release_hook(&hook);
|
nft_chain_release_hook(&hook);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
chain = &basechain->chain;
|
||||||
if (hook.dev != NULL)
|
|
||||||
strncpy(basechain->dev_name, hook.dev->name, IFNAMSIZ);
|
|
||||||
|
|
||||||
if (nla[NFTA_CHAIN_COUNTERS]) {
|
if (nla[NFTA_CHAIN_COUNTERS]) {
|
||||||
stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
|
stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
|
||||||
@ -1741,24 +1895,12 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
|
|||||||
static_branch_inc(&nft_counters_enabled);
|
static_branch_inc(&nft_counters_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
basechain->type = hook.type;
|
err = nft_basechain_init(basechain, family, &hook, flags);
|
||||||
chain = &basechain->chain;
|
if (err < 0) {
|
||||||
|
nft_chain_release_hook(&hook);
|
||||||
ops = &basechain->ops;
|
kfree(basechain);
|
||||||
ops->pf = family;
|
return err;
|
||||||
ops->hooknum = hook.num;
|
}
|
||||||
ops->priority = hook.priority;
|
|
||||||
ops->priv = chain;
|
|
||||||
ops->hook = hook.type->hooks[ops->hooknum];
|
|
||||||
ops->dev = hook.dev;
|
|
||||||
|
|
||||||
chain->flags |= NFT_BASE_CHAIN | flags;
|
|
||||||
basechain->policy = NF_ACCEPT;
|
|
||||||
if (chain->flags & NFT_CHAIN_HW_OFFLOAD &&
|
|
||||||
nft_chain_offload_priority(basechain) < 0)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
flow_block_init(&basechain->flow_block);
|
|
||||||
} else {
|
} else {
|
||||||
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
|
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
|
||||||
if (chain == NULL)
|
if (chain == NULL)
|
||||||
@ -1818,6 +1960,25 @@ err1:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool nft_hook_list_equal(struct list_head *hook_list1,
|
||||||
|
struct list_head *hook_list2)
|
||||||
|
{
|
||||||
|
struct nft_hook *hook;
|
||||||
|
int n = 0, m = 0;
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
list_for_each_entry(hook, hook_list2, list) {
|
||||||
|
if (!nft_hook_list_find(hook_list1, hook))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
list_for_each_entry(hook, hook_list1, list)
|
||||||
|
m++;
|
||||||
|
|
||||||
|
return n == m;
|
||||||
|
}
|
||||||
|
|
||||||
static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
|
static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
|
||||||
u32 flags)
|
u32 flags)
|
||||||
{
|
{
|
||||||
@ -1849,12 +2010,19 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
|
|||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = &basechain->ops;
|
if (ctx->family == NFPROTO_NETDEV) {
|
||||||
if (ops->hooknum != hook.num ||
|
if (!nft_hook_list_equal(&basechain->hook_list,
|
||||||
ops->priority != hook.priority ||
|
&hook.list)) {
|
||||||
ops->dev != hook.dev) {
|
nft_chain_release_hook(&hook);
|
||||||
nft_chain_release_hook(&hook);
|
return -EBUSY;
|
||||||
return -EBUSY;
|
}
|
||||||
|
} else {
|
||||||
|
ops = &basechain->ops;
|
||||||
|
if (ops->hooknum != hook.num ||
|
||||||
|
ops->priority != hook.priority) {
|
||||||
|
nft_chain_release_hook(&hook);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
nft_chain_release_hook(&hook);
|
nft_chain_release_hook(&hook);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
return nft_indr_block_offload_cmd(basechain, dev, cmd);
|
list_for_each_entry(hook, &basechain->hook_list, list) {
|
||||||
|
dev = hook->ops.dev;
|
||||||
|
if (this_dev && this_dev != dev)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
!(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,
|
|||||||
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.
|
list_for_each_entry(hook, &basechain->hook_list, list) {
|
||||||
*
|
if (hook->ops.dev == dev)
|
||||||
* Altough nf_tables core releases all tables/chains, only
|
found = hook;
|
||||||
* 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);
|
n++;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
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,
|
||||||
|
Loading…
Reference in New Issue
Block a user