mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-15 22:41:38 +00:00
Merge tag 'nf-next-25-05-23' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next
Pablo Neira Ayuso says:
====================
Netfilter updates for net-next
The following batch contains Netfilter updates for net-next,
specifically 26 patches: 5 patches adding/updating selftests,
4 fixes, 3 PREEMPT_RT fixes, and 14 patches to enhance nf_tables):
1) Improve selftest coverage for pipapo 4 bit group format, from
Florian Westphal.
2) Fix incorrect dependencies when compiling a kernel without
legacy ip{6}tables support, also from Florian.
3) Two patches to fix nft_fib vrf issues, including selftest updates
to improve coverage, also from Florian Westphal.
4) Fix incorrect nesting in nft_tunnel's GENEVE support, from
Fernando F. Mancera.
5) Three patches to fix PREEMPT_RT issues with nf_dup infrastructure
and nft_inner to match in inner headers, from Sebastian Andrzej Siewior.
6) Integrate conntrack information into nft trace infrastructure,
from Florian Westphal.
7) A series of 13 patches to allow to specify wildcard netdevice in
netdev basechain and flowtables, eg.
table netdev filter {
chain ingress {
type filter hook ingress devices = { eth0, eth1, vlan* } priority 0; policy accept;
}
}
This also allows for runtime hook registration on NETDEV_{UN}REGISTER
event, from Phil Sutter.
netfilter pull request 25-05-23
* tag 'nf-next-25-05-23' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next: (26 commits)
selftests: netfilter: Torture nftables netdev hooks
netfilter: nf_tables: Add notifications for hook changes
netfilter: nf_tables: Support wildcard netdev hook specs
netfilter: nf_tables: Sort labels in nft_netdev_hook_alloc()
netfilter: nf_tables: Handle NETDEV_CHANGENAME events
netfilter: nf_tables: Wrap netdev notifiers
netfilter: nf_tables: Respect NETDEV_REGISTER events
netfilter: nf_tables: Prepare for handling NETDEV_REGISTER events
netfilter: nf_tables: Have a list of nf_hook_ops in nft_hook
netfilter: nf_tables: Pass nf_hook_ops to nft_unregister_flowtable_hook()
netfilter: nf_tables: Introduce nft_register_flowtable_ops()
netfilter: nf_tables: Introduce nft_hook_find_ops{,_rcu}()
netfilter: nf_tables: Introduce functions freeing nft_hook objects
netfilter: nf_tables: add packets conntrack state to debug trace info
netfilter: conntrack: make nf_conntrack_id callable without a module dependency
netfilter: nf_dup_netdev: Move the recursion counter struct netdev_xmit
netfilter: nft_inner: Use nested-BH locking for nft_pcpu_tun_ctx
netfilter: nf_dup{4, 6}: Move duplication check to task_struct
netfilter: nft_tunnel: fix geneve_opt dump
selftests: netfilter: nft_fib.sh: add type and oif tests with and without VRFs
...
====================
Link: https://patch.msgid.link/20250523132712.458507-1-pablo@netfilter.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
@@ -31,9 +31,6 @@
|
||||
const struct nf_ipv6_ops __rcu *nf_ipv6_ops __read_mostly;
|
||||
EXPORT_SYMBOL_GPL(nf_ipv6_ops);
|
||||
|
||||
DEFINE_PER_CPU(bool, nf_skb_duplicated);
|
||||
EXPORT_SYMBOL_GPL(nf_skb_duplicated);
|
||||
|
||||
#ifdef CONFIG_JUMP_LABEL
|
||||
struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
|
||||
EXPORT_SYMBOL(nf_hooks_needed);
|
||||
|
||||
@@ -505,6 +505,11 @@ u32 nf_ct_get_id(const struct nf_conn *ct)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_get_id);
|
||||
|
||||
static u32 nf_conntrack_get_id(const struct nf_conntrack *nfct)
|
||||
{
|
||||
return nf_ct_get_id(nf_ct_to_nf_conn(nfct));
|
||||
}
|
||||
|
||||
static void
|
||||
clean_from_lists(struct nf_conn *ct)
|
||||
{
|
||||
@@ -2710,6 +2715,7 @@ static const struct nf_ct_hook nf_conntrack_hook = {
|
||||
.attach = nf_conntrack_attach,
|
||||
.set_closing = nf_conntrack_set_closing,
|
||||
.confirm = __nf_conntrack_confirm,
|
||||
.get_id = nf_conntrack_get_id,
|
||||
};
|
||||
|
||||
void nf_conntrack_init_end(void)
|
||||
|
||||
@@ -15,12 +15,26 @@
|
||||
|
||||
#define NF_RECURSION_LIMIT 2
|
||||
|
||||
static DEFINE_PER_CPU(u8, nf_dup_skb_recursion);
|
||||
#ifndef CONFIG_PREEMPT_RT
|
||||
static u8 *nf_get_nf_dup_skb_recursion(void)
|
||||
{
|
||||
return this_cpu_ptr(&softnet_data.xmit.nf_dup_skb_recursion);
|
||||
}
|
||||
#else
|
||||
|
||||
static u8 *nf_get_nf_dup_skb_recursion(void)
|
||||
{
|
||||
return ¤t->net_xmit.nf_dup_skb_recursion;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev,
|
||||
enum nf_dev_hooks hook)
|
||||
{
|
||||
if (__this_cpu_read(nf_dup_skb_recursion) > NF_RECURSION_LIMIT)
|
||||
u8 *nf_dup_skb_recursion = nf_get_nf_dup_skb_recursion();
|
||||
|
||||
if (*nf_dup_skb_recursion > NF_RECURSION_LIMIT)
|
||||
goto err;
|
||||
|
||||
if (hook == NF_NETDEV_INGRESS && skb_mac_header_was_set(skb)) {
|
||||
@@ -32,9 +46,9 @@ static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev,
|
||||
|
||||
skb->dev = dev;
|
||||
skb_clear_tstamp(skb);
|
||||
__this_cpu_inc(nf_dup_skb_recursion);
|
||||
(*nf_dup_skb_recursion)++;
|
||||
dev_queue_xmit(skb);
|
||||
__this_cpu_dec(nf_dup_skb_recursion);
|
||||
(*nf_dup_skb_recursion)--;
|
||||
return;
|
||||
err:
|
||||
kfree_skb(skb);
|
||||
|
||||
@@ -300,40 +300,75 @@ void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)
|
||||
static int nft_netdev_register_hooks(struct net *net,
|
||||
struct list_head *hook_list)
|
||||
{
|
||||
struct nf_hook_ops *ops;
|
||||
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;
|
||||
list_for_each_entry(ops, &hook->ops_list, list) {
|
||||
err = nf_register_net_hook(net, ops);
|
||||
if (err < 0)
|
||||
goto err_register;
|
||||
|
||||
j++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_register:
|
||||
list_for_each_entry(hook, hook_list, list) {
|
||||
if (j-- <= 0)
|
||||
break;
|
||||
list_for_each_entry(ops, &hook->ops_list, list) {
|
||||
if (j-- <= 0)
|
||||
break;
|
||||
|
||||
nf_unregister_net_hook(net, &hook->ops);
|
||||
nf_unregister_net_hook(net, ops);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nft_netdev_hook_free_ops(struct nft_hook *hook)
|
||||
{
|
||||
struct nf_hook_ops *ops, *next;
|
||||
|
||||
list_for_each_entry_safe(ops, next, &hook->ops_list, list) {
|
||||
list_del(&ops->list);
|
||||
kfree(ops);
|
||||
}
|
||||
}
|
||||
|
||||
static void nft_netdev_hook_free(struct nft_hook *hook)
|
||||
{
|
||||
nft_netdev_hook_free_ops(hook);
|
||||
kfree(hook);
|
||||
}
|
||||
|
||||
static void __nft_netdev_hook_free_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct nft_hook *hook = container_of(rcu, struct nft_hook, rcu);
|
||||
|
||||
nft_netdev_hook_free(hook);
|
||||
}
|
||||
|
||||
static void nft_netdev_hook_free_rcu(struct nft_hook *hook)
|
||||
{
|
||||
call_rcu(&hook->rcu, __nft_netdev_hook_free_rcu);
|
||||
}
|
||||
|
||||
static void nft_netdev_unregister_hooks(struct net *net,
|
||||
struct list_head *hook_list,
|
||||
bool release_netdev)
|
||||
{
|
||||
struct nft_hook *hook, *next;
|
||||
struct nf_hook_ops *ops;
|
||||
|
||||
list_for_each_entry_safe(hook, next, hook_list, list) {
|
||||
nf_unregister_net_hook(net, &hook->ops);
|
||||
list_for_each_entry(ops, &hook->ops_list, list)
|
||||
nf_unregister_net_hook(net, ops);
|
||||
if (release_netdev) {
|
||||
list_del(&hook->list);
|
||||
kfree_rcu(hook, rcu);
|
||||
nft_netdev_hook_free_rcu(hook);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2253,7 +2288,7 @@ void nf_tables_chain_destroy(struct nft_chain *chain)
|
||||
list_for_each_entry_safe(hook, next,
|
||||
&basechain->hook_list, list) {
|
||||
list_del_rcu(&hook->list);
|
||||
kfree_rcu(hook, rcu);
|
||||
nft_netdev_hook_free_rcu(hook);
|
||||
}
|
||||
}
|
||||
module_put(basechain->type->owner);
|
||||
@@ -2274,19 +2309,20 @@ void nf_tables_chain_destroy(struct nft_chain *chain)
|
||||
static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
|
||||
const struct nlattr *attr)
|
||||
{
|
||||
struct nf_hook_ops *ops;
|
||||
struct net_device *dev;
|
||||
struct nft_hook *hook;
|
||||
int err;
|
||||
|
||||
hook = kzalloc(sizeof(struct nft_hook), GFP_KERNEL_ACCOUNT);
|
||||
if (!hook) {
|
||||
err = -ENOMEM;
|
||||
goto err_hook_alloc;
|
||||
}
|
||||
if (!hook)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_LIST_HEAD(&hook->ops_list);
|
||||
|
||||
err = nla_strscpy(hook->ifname, attr, IFNAMSIZ);
|
||||
if (err < 0)
|
||||
goto err_hook_dev;
|
||||
goto err_hook_free;
|
||||
|
||||
hook->ifnamelen = nla_len(attr);
|
||||
|
||||
@@ -2294,18 +2330,22 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
|
||||
* indirectly serializing all the other holders of the commit_mutex with
|
||||
* the rtnl_mutex.
|
||||
*/
|
||||
dev = __dev_get_by_name(net, hook->ifname);
|
||||
if (!dev) {
|
||||
err = -ENOENT;
|
||||
goto err_hook_dev;
|
||||
}
|
||||
hook->ops.dev = dev;
|
||||
for_each_netdev(net, dev) {
|
||||
if (strncmp(dev->name, hook->ifname, hook->ifnamelen))
|
||||
continue;
|
||||
|
||||
ops = kzalloc(sizeof(struct nf_hook_ops), GFP_KERNEL_ACCOUNT);
|
||||
if (!ops) {
|
||||
err = -ENOMEM;
|
||||
goto err_hook_free;
|
||||
}
|
||||
ops->dev = dev;
|
||||
list_add_tail(&ops->list, &hook->ops_list);
|
||||
}
|
||||
return hook;
|
||||
|
||||
err_hook_dev:
|
||||
kfree(hook);
|
||||
err_hook_alloc:
|
||||
err_hook_free:
|
||||
nft_netdev_hook_free(hook);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
@@ -2315,7 +2355,8 @@ static struct nft_hook *nft_hook_list_find(struct list_head *hook_list,
|
||||
struct nft_hook *hook;
|
||||
|
||||
list_for_each_entry(hook, hook_list, list) {
|
||||
if (!strcmp(hook->ifname, this->ifname))
|
||||
if (!strncmp(hook->ifname, this->ifname,
|
||||
min(hook->ifnamelen, this->ifnamelen)))
|
||||
return hook;
|
||||
}
|
||||
|
||||
@@ -2345,7 +2386,7 @@ static int nf_tables_parse_netdev_hooks(struct net *net,
|
||||
}
|
||||
if (nft_hook_list_find(hook_list, hook)) {
|
||||
NL_SET_BAD_ATTR(extack, tmp);
|
||||
kfree(hook);
|
||||
nft_netdev_hook_free(hook);
|
||||
err = -EEXIST;
|
||||
goto err_hook;
|
||||
}
|
||||
@@ -2363,7 +2404,7 @@ static int nf_tables_parse_netdev_hooks(struct net *net,
|
||||
err_hook:
|
||||
list_for_each_entry_safe(hook, next, hook_list, list) {
|
||||
list_del(&hook->list);
|
||||
kfree(hook);
|
||||
nft_netdev_hook_free(hook);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@@ -2506,7 +2547,7 @@ static void nft_chain_release_hook(struct nft_chain_hook *hook)
|
||||
|
||||
list_for_each_entry_safe(h, next, &hook->list, list) {
|
||||
list_del(&h->list);
|
||||
kfree(h);
|
||||
nft_netdev_hook_free(h);
|
||||
}
|
||||
module_put(hook->type->owner);
|
||||
}
|
||||
@@ -2559,6 +2600,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
|
||||
struct nft_chain_hook *hook, u32 flags)
|
||||
{
|
||||
struct nft_chain *chain;
|
||||
struct nf_hook_ops *ops;
|
||||
struct nft_hook *h;
|
||||
|
||||
basechain->type = hook->type;
|
||||
@@ -2567,8 +2609,10 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
|
||||
|
||||
if (nft_base_chain_netdev(family, hook->num)) {
|
||||
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);
|
||||
list_for_each_entry(h, &basechain->hook_list, list) {
|
||||
list_for_each_entry(ops, &h->ops_list, list)
|
||||
nft_basechain_hook_init(ops, family, hook, chain);
|
||||
}
|
||||
}
|
||||
nft_basechain_hook_init(&basechain->ops, family, hook, chain);
|
||||
|
||||
@@ -2787,15 +2831,17 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
|
||||
|
||||
if (nft_base_chain_netdev(ctx->family, basechain->ops.hooknum)) {
|
||||
list_for_each_entry_safe(h, next, &hook.list, list) {
|
||||
h->ops.pf = basechain->ops.pf;
|
||||
h->ops.hooknum = basechain->ops.hooknum;
|
||||
h->ops.priority = basechain->ops.priority;
|
||||
h->ops.priv = basechain->ops.priv;
|
||||
h->ops.hook = basechain->ops.hook;
|
||||
list_for_each_entry(ops, &h->ops_list, list) {
|
||||
ops->pf = basechain->ops.pf;
|
||||
ops->hooknum = basechain->ops.hooknum;
|
||||
ops->priority = basechain->ops.priority;
|
||||
ops->priv = basechain->ops.priv;
|
||||
ops->hook = basechain->ops.hook;
|
||||
}
|
||||
|
||||
if (nft_hook_list_find(&basechain->hook_list, h)) {
|
||||
list_del(&h->list);
|
||||
kfree(h);
|
||||
nft_netdev_hook_free(h);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -2913,10 +2959,12 @@ err_trans:
|
||||
err_hooks:
|
||||
if (nla[NFTA_CHAIN_HOOK]) {
|
||||
list_for_each_entry_safe(h, next, &hook.list, list) {
|
||||
if (unregister)
|
||||
nf_unregister_net_hook(ctx->net, &h->ops);
|
||||
if (unregister) {
|
||||
list_for_each_entry(ops, &h->ops_list, list)
|
||||
nf_unregister_net_hook(ctx->net, ops);
|
||||
}
|
||||
list_del(&h->list);
|
||||
kfree_rcu(h, rcu);
|
||||
nft_netdev_hook_free_rcu(h);
|
||||
}
|
||||
module_put(hook.type->owner);
|
||||
}
|
||||
@@ -8785,6 +8833,7 @@ static int nft_flowtable_parse_hook(const struct nft_ctx *ctx,
|
||||
struct netlink_ext_ack *extack, bool add)
|
||||
{
|
||||
struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1];
|
||||
struct nf_hook_ops *ops;
|
||||
struct nft_hook *hook;
|
||||
int hooknum, priority;
|
||||
int err;
|
||||
@@ -8839,11 +8888,13 @@ static int nft_flowtable_parse_hook(const struct nft_ctx *ctx,
|
||||
}
|
||||
|
||||
list_for_each_entry(hook, &flowtable_hook->list, list) {
|
||||
hook->ops.pf = NFPROTO_NETDEV;
|
||||
hook->ops.hooknum = flowtable_hook->num;
|
||||
hook->ops.priority = flowtable_hook->priority;
|
||||
hook->ops.priv = &flowtable->data;
|
||||
hook->ops.hook = flowtable->data.type->hook;
|
||||
list_for_each_entry(ops, &hook->ops_list, list) {
|
||||
ops->pf = NFPROTO_NETDEV;
|
||||
ops->hooknum = flowtable_hook->num;
|
||||
ops->priority = flowtable_hook->priority;
|
||||
ops->priv = &flowtable->data;
|
||||
ops->hook = flowtable->data.type->hook;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -8885,12 +8936,12 @@ nft_flowtable_type_get(struct net *net, u8 family)
|
||||
}
|
||||
|
||||
/* Only called from error and netdev event paths. */
|
||||
static void nft_unregister_flowtable_hook(struct net *net,
|
||||
struct nft_flowtable *flowtable,
|
||||
struct nft_hook *hook)
|
||||
static void nft_unregister_flowtable_ops(struct net *net,
|
||||
struct nft_flowtable *flowtable,
|
||||
struct nf_hook_ops *ops)
|
||||
{
|
||||
nf_unregister_net_hook(net, &hook->ops);
|
||||
flowtable->data.type->setup(&flowtable->data, hook->ops.dev,
|
||||
nf_unregister_net_hook(net, ops);
|
||||
flowtable->data.type->setup(&flowtable->data, ops->dev,
|
||||
FLOW_BLOCK_UNBIND);
|
||||
}
|
||||
|
||||
@@ -8900,14 +8951,14 @@ static void __nft_unregister_flowtable_net_hooks(struct net *net,
|
||||
bool release_netdev)
|
||||
{
|
||||
struct nft_hook *hook, *next;
|
||||
struct nf_hook_ops *ops;
|
||||
|
||||
list_for_each_entry_safe(hook, next, hook_list, list) {
|
||||
nf_unregister_net_hook(net, &hook->ops);
|
||||
flowtable->data.type->setup(&flowtable->data, hook->ops.dev,
|
||||
FLOW_BLOCK_UNBIND);
|
||||
list_for_each_entry(ops, &hook->ops_list, list)
|
||||
nft_unregister_flowtable_ops(net, flowtable, ops);
|
||||
if (release_netdev) {
|
||||
list_del(&hook->list);
|
||||
kfree_rcu(hook, rcu);
|
||||
nft_netdev_hook_free_rcu(hook);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8919,6 +8970,26 @@ static void nft_unregister_flowtable_net_hooks(struct net *net,
|
||||
__nft_unregister_flowtable_net_hooks(net, flowtable, hook_list, false);
|
||||
}
|
||||
|
||||
static int nft_register_flowtable_ops(struct net *net,
|
||||
struct nft_flowtable *flowtable,
|
||||
struct nf_hook_ops *ops)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = flowtable->data.type->setup(&flowtable->data,
|
||||
ops->dev, FLOW_BLOCK_BIND);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = nf_register_net_hook(net, ops);
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
flowtable->data.type->setup(&flowtable->data,
|
||||
ops->dev, FLOW_BLOCK_UNBIND);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nft_register_flowtable_net_hooks(struct net *net,
|
||||
struct nft_table *table,
|
||||
struct list_head *hook_list,
|
||||
@@ -8926,6 +8997,7 @@ static int nft_register_flowtable_net_hooks(struct net *net,
|
||||
{
|
||||
struct nft_hook *hook, *next;
|
||||
struct nft_flowtable *ft;
|
||||
struct nf_hook_ops *ops;
|
||||
int err, i = 0;
|
||||
|
||||
list_for_each_entry(hook, hook_list, list) {
|
||||
@@ -8939,33 +9011,27 @@ static int nft_register_flowtable_net_hooks(struct net *net,
|
||||
}
|
||||
}
|
||||
|
||||
err = flowtable->data.type->setup(&flowtable->data,
|
||||
hook->ops.dev,
|
||||
FLOW_BLOCK_BIND);
|
||||
if (err < 0)
|
||||
goto err_unregister_net_hooks;
|
||||
list_for_each_entry(ops, &hook->ops_list, list) {
|
||||
err = nft_register_flowtable_ops(net, flowtable, ops);
|
||||
if (err < 0)
|
||||
goto err_unregister_net_hooks;
|
||||
|
||||
err = nf_register_net_hook(net, &hook->ops);
|
||||
if (err < 0) {
|
||||
flowtable->data.type->setup(&flowtable->data,
|
||||
hook->ops.dev,
|
||||
FLOW_BLOCK_UNBIND);
|
||||
goto err_unregister_net_hooks;
|
||||
i++;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_net_hooks:
|
||||
list_for_each_entry_safe(hook, next, hook_list, list) {
|
||||
if (i-- <= 0)
|
||||
break;
|
||||
list_for_each_entry(ops, &hook->ops_list, list) {
|
||||
if (i-- <= 0)
|
||||
break;
|
||||
|
||||
nft_unregister_flowtable_hook(net, flowtable, hook);
|
||||
nft_unregister_flowtable_ops(net, flowtable, ops);
|
||||
}
|
||||
list_del_rcu(&hook->list);
|
||||
kfree_rcu(hook, rcu);
|
||||
nft_netdev_hook_free_rcu(hook);
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -8977,7 +9043,7 @@ static void nft_hooks_destroy(struct list_head *hook_list)
|
||||
|
||||
list_for_each_entry_safe(hook, next, hook_list, list) {
|
||||
list_del_rcu(&hook->list);
|
||||
kfree_rcu(hook, rcu);
|
||||
nft_netdev_hook_free_rcu(hook);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8988,6 +9054,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
|
||||
const struct nlattr * const *nla = ctx->nla;
|
||||
struct nft_flowtable_hook flowtable_hook;
|
||||
struct nft_hook *hook, *next;
|
||||
struct nf_hook_ops *ops;
|
||||
struct nft_trans *trans;
|
||||
bool unregister = false;
|
||||
u32 flags;
|
||||
@@ -9001,7 +9068,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
|
||||
list_for_each_entry_safe(hook, next, &flowtable_hook.list, list) {
|
||||
if (nft_hook_list_find(&flowtable->hook_list, hook)) {
|
||||
list_del(&hook->list);
|
||||
kfree(hook);
|
||||
nft_netdev_hook_free(hook);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9045,10 +9112,13 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
|
||||
|
||||
err_flowtable_update_hook:
|
||||
list_for_each_entry_safe(hook, next, &flowtable_hook.list, list) {
|
||||
if (unregister)
|
||||
nft_unregister_flowtable_hook(ctx->net, flowtable, hook);
|
||||
if (unregister) {
|
||||
list_for_each_entry(ops, &hook->ops_list, list)
|
||||
nft_unregister_flowtable_ops(ctx->net,
|
||||
flowtable, ops);
|
||||
}
|
||||
list_del_rcu(&hook->list);
|
||||
kfree_rcu(hook, rcu);
|
||||
nft_netdev_hook_free_rcu(hook);
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -9194,7 +9264,7 @@ static void nft_flowtable_hook_release(struct nft_flowtable_hook *flowtable_hook
|
||||
|
||||
list_for_each_entry_safe(this, next, &flowtable_hook->list, list) {
|
||||
list_del(&this->list);
|
||||
kfree(this);
|
||||
nft_netdev_hook_free(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9557,7 +9627,7 @@ static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
|
||||
flowtable->data.type->free(&flowtable->data);
|
||||
list_for_each_entry_safe(hook, next, &flowtable->hook_list, list) {
|
||||
list_del_rcu(&hook->list);
|
||||
kfree_rcu(hook, rcu);
|
||||
nft_netdev_hook_free_rcu(hook);
|
||||
}
|
||||
kfree(flowtable->name);
|
||||
module_put(flowtable->data.type->owner);
|
||||
@@ -9590,46 +9660,190 @@ nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static void nft_flowtable_event(unsigned long event, struct net_device *dev,
|
||||
struct nft_flowtable *flowtable)
|
||||
struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook,
|
||||
const struct net_device *dev)
|
||||
{
|
||||
struct nf_hook_ops *ops;
|
||||
|
||||
list_for_each_entry(ops, &hook->ops_list, list) {
|
||||
if (ops->dev == dev)
|
||||
return ops;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nft_hook_find_ops);
|
||||
|
||||
struct nf_hook_ops *nft_hook_find_ops_rcu(const struct nft_hook *hook,
|
||||
const struct net_device *dev)
|
||||
{
|
||||
struct nf_hook_ops *ops;
|
||||
|
||||
list_for_each_entry_rcu(ops, &hook->ops_list, list) {
|
||||
if (ops->dev == dev)
|
||||
return ops;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nft_hook_find_ops_rcu);
|
||||
|
||||
static void
|
||||
nf_tables_device_notify(const struct nft_table *table, int attr,
|
||||
const char *name, const struct nft_hook *hook,
|
||||
const struct net_device *dev, int event)
|
||||
{
|
||||
struct net *net = dev_net(dev);
|
||||
struct nlmsghdr *nlh;
|
||||
struct sk_buff *skb;
|
||||
u16 flags = 0;
|
||||
|
||||
if (!nfnetlink_has_listeners(net, NFNLGRP_NFT_DEV))
|
||||
return;
|
||||
|
||||
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!skb)
|
||||
goto err;
|
||||
|
||||
event = event == NETDEV_REGISTER ? NFT_MSG_NEWDEV : NFT_MSG_DELDEV;
|
||||
event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
|
||||
nlh = nfnl_msg_put(skb, 0, 0, event, flags, table->family,
|
||||
NFNETLINK_V0, nft_base_seq(net));
|
||||
if (!nlh)
|
||||
goto err;
|
||||
|
||||
if (nla_put_string(skb, NFTA_DEVICE_TABLE, table->name) ||
|
||||
nla_put_string(skb, attr, name) ||
|
||||
nla_put(skb, NFTA_DEVICE_SPEC, hook->ifnamelen, hook->ifname) ||
|
||||
nla_put_string(skb, NFTA_DEVICE_NAME, dev->name))
|
||||
goto err;
|
||||
|
||||
nlmsg_end(skb, nlh);
|
||||
nfnetlink_send(skb, net, 0, NFNLGRP_NFT_DEV,
|
||||
nlmsg_report(nlh), GFP_KERNEL);
|
||||
return;
|
||||
err:
|
||||
if (skb)
|
||||
kfree_skb(skb);
|
||||
nfnetlink_set_err(net, 0, NFNLGRP_NFT_DEV, -ENOBUFS);
|
||||
}
|
||||
|
||||
void
|
||||
nf_tables_chain_device_notify(const struct nft_chain *chain,
|
||||
const struct nft_hook *hook,
|
||||
const struct net_device *dev, int event)
|
||||
{
|
||||
nf_tables_device_notify(chain->table, NFTA_DEVICE_CHAIN,
|
||||
chain->name, hook, dev, event);
|
||||
}
|
||||
|
||||
static void
|
||||
nf_tables_flowtable_device_notify(const struct nft_flowtable *ft,
|
||||
const struct nft_hook *hook,
|
||||
const struct net_device *dev, int event)
|
||||
{
|
||||
nf_tables_device_notify(ft->table, NFTA_DEVICE_FLOWTABLE,
|
||||
ft->name, hook, dev, event);
|
||||
}
|
||||
|
||||
static int nft_flowtable_event(unsigned long event, struct net_device *dev,
|
||||
struct nft_flowtable *flowtable, bool changename)
|
||||
{
|
||||
struct nf_hook_ops *ops;
|
||||
struct nft_hook *hook;
|
||||
bool match;
|
||||
|
||||
list_for_each_entry(hook, &flowtable->hook_list, list) {
|
||||
if (hook->ops.dev != dev)
|
||||
continue;
|
||||
ops = nft_hook_find_ops(hook, dev);
|
||||
match = !strncmp(hook->ifname, dev->name, hook->ifnamelen);
|
||||
|
||||
/* flow_offload_netdev_event() cleans up entries for us. */
|
||||
nft_unregister_flowtable_hook(dev_net(dev), flowtable, hook);
|
||||
list_del_rcu(&hook->list);
|
||||
kfree_rcu(hook, rcu);
|
||||
switch (event) {
|
||||
case NETDEV_UNREGISTER:
|
||||
/* NOP if not found or new name still matching */
|
||||
if (!ops || (changename && match))
|
||||
continue;
|
||||
|
||||
/* flow_offload_netdev_event() cleans up entries for us. */
|
||||
nft_unregister_flowtable_ops(dev_net(dev),
|
||||
flowtable, ops);
|
||||
list_del_rcu(&ops->list);
|
||||
kfree_rcu(ops, rcu);
|
||||
break;
|
||||
case NETDEV_REGISTER:
|
||||
/* NOP if not matching or already registered */
|
||||
if (!match || (changename && ops))
|
||||
continue;
|
||||
|
||||
ops = kzalloc(sizeof(struct nf_hook_ops),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
if (!ops)
|
||||
return 1;
|
||||
|
||||
ops->pf = NFPROTO_NETDEV;
|
||||
ops->hooknum = flowtable->hooknum;
|
||||
ops->priority = flowtable->data.priority;
|
||||
ops->priv = &flowtable->data;
|
||||
ops->hook = flowtable->data.type->hook;
|
||||
ops->dev = dev;
|
||||
if (nft_register_flowtable_ops(dev_net(dev),
|
||||
flowtable, ops)) {
|
||||
kfree(ops);
|
||||
return 1;
|
||||
}
|
||||
list_add_tail_rcu(&ops->list, &hook->ops_list);
|
||||
break;
|
||||
}
|
||||
nf_tables_flowtable_device_notify(flowtable, hook, dev, event);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __nf_tables_flowtable_event(unsigned long event,
|
||||
struct net_device *dev,
|
||||
bool changename)
|
||||
{
|
||||
struct nftables_pernet *nft_net = nft_pernet(dev_net(dev));
|
||||
struct nft_flowtable *flowtable;
|
||||
struct nft_table *table;
|
||||
|
||||
list_for_each_entry(table, &nft_net->tables, list) {
|
||||
list_for_each_entry(flowtable, &table->flowtables, list) {
|
||||
if (nft_flowtable_event(event, dev,
|
||||
flowtable, changename))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nf_tables_flowtable_event(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct nft_flowtable *flowtable;
|
||||
struct nftables_pernet *nft_net;
|
||||
struct nft_table *table;
|
||||
int ret = NOTIFY_DONE;
|
||||
struct net *net;
|
||||
|
||||
if (event != NETDEV_UNREGISTER)
|
||||
return 0;
|
||||
if (event != NETDEV_REGISTER &&
|
||||
event != NETDEV_UNREGISTER &&
|
||||
event != NETDEV_CHANGENAME)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
net = dev_net(dev);
|
||||
nft_net = nft_pernet(net);
|
||||
mutex_lock(&nft_net->commit_mutex);
|
||||
list_for_each_entry(table, &nft_net->tables, list) {
|
||||
list_for_each_entry(flowtable, &table->flowtables, list) {
|
||||
nft_flowtable_event(event, dev, flowtable);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&nft_net->commit_mutex);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
if (event == NETDEV_CHANGENAME) {
|
||||
if (__nf_tables_flowtable_event(NETDEV_REGISTER, dev, true)) {
|
||||
ret = NOTIFY_BAD;
|
||||
goto out_unlock;
|
||||
}
|
||||
__nf_tables_flowtable_event(NETDEV_UNREGISTER, dev, true);
|
||||
} else if (__nf_tables_flowtable_event(event, dev, false)) {
|
||||
ret = NOTIFY_BAD;
|
||||
}
|
||||
out_unlock:
|
||||
mutex_unlock(&nft_net->commit_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct notifier_block nf_tables_flowtable_notifier = {
|
||||
|
||||
@@ -220,6 +220,7 @@ static int nft_chain_offload_priority(const struct nft_base_chain *basechain)
|
||||
|
||||
bool nft_chain_offload_support(const struct nft_base_chain *basechain)
|
||||
{
|
||||
struct nf_hook_ops *ops;
|
||||
struct net_device *dev;
|
||||
struct nft_hook *hook;
|
||||
|
||||
@@ -227,13 +228,16 @@ bool nft_chain_offload_support(const struct nft_base_chain *basechain)
|
||||
return false;
|
||||
|
||||
list_for_each_entry(hook, &basechain->hook_list, list) {
|
||||
if (hook->ops.pf != NFPROTO_NETDEV ||
|
||||
hook->ops.hooknum != NF_NETDEV_INGRESS)
|
||||
return false;
|
||||
list_for_each_entry(ops, &hook->ops_list, list) {
|
||||
if (ops->pf != NFPROTO_NETDEV ||
|
||||
ops->hooknum != NF_NETDEV_INGRESS)
|
||||
return false;
|
||||
|
||||
dev = hook->ops.dev;
|
||||
if (!dev->netdev_ops->ndo_setup_tc && !flow_indr_dev_exists())
|
||||
return false;
|
||||
dev = ops->dev;
|
||||
if (!dev->netdev_ops->ndo_setup_tc &&
|
||||
!flow_indr_dev_exists())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -455,34 +459,37 @@ static int nft_flow_block_chain(struct nft_base_chain *basechain,
|
||||
const struct net_device *this_dev,
|
||||
enum flow_block_command cmd)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct nf_hook_ops *ops;
|
||||
struct nft_hook *hook;
|
||||
int err, i = 0;
|
||||
|
||||
list_for_each_entry(hook, &basechain->hook_list, list) {
|
||||
dev = hook->ops.dev;
|
||||
if (this_dev && this_dev != dev)
|
||||
continue;
|
||||
list_for_each_entry(ops, &hook->ops_list, list) {
|
||||
if (this_dev && this_dev != ops->dev)
|
||||
continue;
|
||||
|
||||
err = nft_chain_offload_cmd(basechain, dev, cmd);
|
||||
if (err < 0 && cmd == FLOW_BLOCK_BIND) {
|
||||
if (!this_dev)
|
||||
goto err_flow_block;
|
||||
err = nft_chain_offload_cmd(basechain, ops->dev, cmd);
|
||||
if (err < 0 && cmd == FLOW_BLOCK_BIND) {
|
||||
if (!this_dev)
|
||||
goto err_flow_block;
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_flow_block:
|
||||
list_for_each_entry(hook, &basechain->hook_list, list) {
|
||||
if (i-- <= 0)
|
||||
break;
|
||||
list_for_each_entry(ops, &hook->ops_list, list) {
|
||||
if (i-- <= 0)
|
||||
break;
|
||||
|
||||
dev = hook->ops.dev;
|
||||
nft_chain_offload_cmd(basechain, dev, FLOW_BLOCK_UNBIND);
|
||||
nft_chain_offload_cmd(basechain, ops->dev,
|
||||
FLOW_BLOCK_UNBIND);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@@ -638,7 +645,7 @@ static struct nft_chain *__nft_offload_get_chain(const struct nftables_pernet *n
|
||||
found = NULL;
|
||||
basechain = nft_base_chain(chain);
|
||||
list_for_each_entry(hook, &basechain->hook_list, list) {
|
||||
if (hook->ops.dev != dev)
|
||||
if (!nft_hook_find_ops(hook, dev))
|
||||
continue;
|
||||
|
||||
found = hook;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/netfilter/nf_tables.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_tables_core.h>
|
||||
#include <net/netfilter/nf_tables.h>
|
||||
|
||||
@@ -90,6 +91,49 @@ static int nf_trace_fill_dev_info(struct sk_buff *nlskb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nf_trace_fill_ct_info(struct sk_buff *nlskb,
|
||||
const struct sk_buff *skb)
|
||||
{
|
||||
const struct nf_ct_hook *ct_hook;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
const struct nf_conn *ct;
|
||||
u32 state;
|
||||
|
||||
ct_hook = rcu_dereference(nf_ct_hook);
|
||||
if (!ct_hook)
|
||||
return 0;
|
||||
|
||||
ct = nf_ct_get(skb, &ctinfo);
|
||||
if (!ct) {
|
||||
if (ctinfo != IP_CT_UNTRACKED) /* not seen by conntrack or invalid */
|
||||
return 0;
|
||||
|
||||
state = NF_CT_STATE_UNTRACKED_BIT;
|
||||
} else {
|
||||
state = NF_CT_STATE_BIT(ctinfo);
|
||||
}
|
||||
|
||||
if (nla_put_be32(nlskb, NFTA_TRACE_CT_STATE, htonl(state)))
|
||||
return -1;
|
||||
|
||||
if (ct) {
|
||||
u32 id = ct_hook->get_id(&ct->ct_general);
|
||||
u32 status = READ_ONCE(ct->status);
|
||||
u8 dir = CTINFO2DIR(ctinfo);
|
||||
|
||||
if (nla_put_u8(nlskb, NFTA_TRACE_CT_DIRECTION, dir))
|
||||
return -1;
|
||||
|
||||
if (nla_put_be32(nlskb, NFTA_TRACE_CT_ID, (__force __be32)id))
|
||||
return -1;
|
||||
|
||||
if (status && nla_put_be32(nlskb, NFTA_TRACE_CT_STATUS, htonl(status)))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nf_trace_fill_pkt_info(struct sk_buff *nlskb,
|
||||
const struct nft_pktinfo *pkt)
|
||||
{
|
||||
@@ -210,7 +254,11 @@ void nft_trace_notify(const struct nft_pktinfo *pkt,
|
||||
nla_total_size(sizeof(__be32)) + /* trace type */
|
||||
nla_total_size(0) + /* VERDICT, nested */
|
||||
nla_total_size(sizeof(u32)) + /* verdict code */
|
||||
nla_total_size(sizeof(u32)) + /* id */
|
||||
nla_total_size(sizeof(u32)) + /* ct id */
|
||||
nla_total_size(sizeof(u8)) + /* ct direction */
|
||||
nla_total_size(sizeof(u32)) + /* ct state */
|
||||
nla_total_size(sizeof(u32)) + /* ct status */
|
||||
nla_total_size(sizeof(u32)) + /* trace id */
|
||||
nla_total_size(NFT_TRACETYPE_LL_HSIZE) +
|
||||
nla_total_size(NFT_TRACETYPE_NETWORK_HSIZE) +
|
||||
nla_total_size(NFT_TRACETYPE_TRANSPORT_HSIZE) +
|
||||
@@ -291,6 +339,10 @@ void nft_trace_notify(const struct nft_pktinfo *pkt,
|
||||
|
||||
if (nf_trace_fill_pkt_info(skb, pkt))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nf_trace_fill_ct_info(skb, pkt->skb))
|
||||
goto nla_put_failure;
|
||||
|
||||
info->packet_dumped = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = {
|
||||
[NFNLGRP_NFTABLES] = NFNL_SUBSYS_NFTABLES,
|
||||
[NFNLGRP_ACCT_QUOTA] = NFNL_SUBSYS_ACCT,
|
||||
[NFNLGRP_NFTRACE] = NFNL_SUBSYS_NFTABLES,
|
||||
[NFNLGRP_NFT_DEV] = NFNL_SUBSYS_NFTABLES,
|
||||
};
|
||||
|
||||
static struct nfnl_net *nfnl_pernet(struct net *net)
|
||||
|
||||
@@ -318,38 +318,68 @@ static const struct nft_chain_type nft_chain_filter_netdev = {
|
||||
},
|
||||
};
|
||||
|
||||
static void nft_netdev_event(unsigned long event, struct net_device *dev,
|
||||
struct nft_base_chain *basechain)
|
||||
static int nft_netdev_event(unsigned long event, struct net_device *dev,
|
||||
struct nft_base_chain *basechain, bool changename)
|
||||
{
|
||||
struct nft_table *table = basechain->chain.table;
|
||||
struct nf_hook_ops *ops;
|
||||
struct nft_hook *hook;
|
||||
bool match;
|
||||
|
||||
list_for_each_entry(hook, &basechain->hook_list, list) {
|
||||
if (hook->ops.dev != dev)
|
||||
continue;
|
||||
ops = nft_hook_find_ops(hook, dev);
|
||||
match = !strncmp(hook->ifname, dev->name, hook->ifnamelen);
|
||||
|
||||
if (!(basechain->chain.table->flags & NFT_TABLE_F_DORMANT))
|
||||
nf_unregister_net_hook(dev_net(dev), &hook->ops);
|
||||
switch (event) {
|
||||
case NETDEV_UNREGISTER:
|
||||
/* NOP if not found or new name still matching */
|
||||
if (!ops || (changename && match))
|
||||
continue;
|
||||
|
||||
list_del_rcu(&hook->list);
|
||||
kfree_rcu(hook, rcu);
|
||||
if (!(table->flags & NFT_TABLE_F_DORMANT))
|
||||
nf_unregister_net_hook(dev_net(dev), ops);
|
||||
|
||||
list_del_rcu(&ops->list);
|
||||
kfree_rcu(ops, rcu);
|
||||
break;
|
||||
case NETDEV_REGISTER:
|
||||
/* NOP if not matching or already registered */
|
||||
if (!match || (changename && ops))
|
||||
continue;
|
||||
|
||||
ops = kmemdup(&basechain->ops,
|
||||
sizeof(struct nf_hook_ops),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
if (!ops)
|
||||
return 1;
|
||||
|
||||
ops->dev = dev;
|
||||
|
||||
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
|
||||
nf_register_net_hook(dev_net(dev), ops)) {
|
||||
kfree(ops);
|
||||
return 1;
|
||||
}
|
||||
list_add_tail_rcu(&ops->list, &hook->ops_list);
|
||||
break;
|
||||
}
|
||||
nf_tables_chain_device_notify(&basechain->chain,
|
||||
hook, dev, event);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nf_tables_netdev_event(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
static int __nf_tables_netdev_event(unsigned long event,
|
||||
struct net_device *dev,
|
||||
bool changename)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct nft_base_chain *basechain;
|
||||
struct nftables_pernet *nft_net;
|
||||
struct nft_chain *chain;
|
||||
struct nft_table *table;
|
||||
|
||||
if (event != NETDEV_UNREGISTER)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
nft_net = nft_pernet(dev_net(dev));
|
||||
mutex_lock(&nft_net->commit_mutex);
|
||||
list_for_each_entry(table, &nft_net->tables, list) {
|
||||
if (table->family != NFPROTO_NETDEV &&
|
||||
table->family != NFPROTO_INET)
|
||||
@@ -364,12 +394,40 @@ static int nf_tables_netdev_event(struct notifier_block *this,
|
||||
basechain->ops.hooknum != NF_INET_INGRESS)
|
||||
continue;
|
||||
|
||||
nft_netdev_event(event, dev, basechain);
|
||||
if (nft_netdev_event(event, dev, basechain, changename))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&nft_net->commit_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
static int nf_tables_netdev_event(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct nftables_pernet *nft_net;
|
||||
int ret = NOTIFY_DONE;
|
||||
|
||||
if (event != NETDEV_REGISTER &&
|
||||
event != NETDEV_UNREGISTER &&
|
||||
event != NETDEV_CHANGENAME)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
nft_net = nft_pernet(dev_net(dev));
|
||||
mutex_lock(&nft_net->commit_mutex);
|
||||
|
||||
if (event == NETDEV_CHANGENAME) {
|
||||
if (__nf_tables_netdev_event(NETDEV_REGISTER, dev, true)) {
|
||||
ret = NOTIFY_BAD;
|
||||
goto out_unlock;
|
||||
}
|
||||
__nf_tables_netdev_event(NETDEV_UNREGISTER, dev, true);
|
||||
} else if (__nf_tables_netdev_event(event, dev, false)) {
|
||||
ret = NOTIFY_BAD;
|
||||
}
|
||||
out_unlock:
|
||||
mutex_unlock(&nft_net->commit_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct notifier_block nf_tables_netdev_notifier = {
|
||||
|
||||
@@ -175,7 +175,7 @@ static bool nft_flowtable_find_dev(const struct net_device *dev,
|
||||
bool found = false;
|
||||
|
||||
list_for_each_entry_rcu(hook, &ft->hook_list, list) {
|
||||
if (hook->ops.dev != dev)
|
||||
if (!nft_hook_find_ops_rcu(hook, dev))
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
|
||||
@@ -23,7 +23,14 @@
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
|
||||
static DEFINE_PER_CPU(struct nft_inner_tun_ctx, nft_pcpu_tun_ctx);
|
||||
struct nft_inner_tun_ctx_locked {
|
||||
struct nft_inner_tun_ctx ctx;
|
||||
local_lock_t bh_lock;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct nft_inner_tun_ctx_locked, nft_pcpu_tun_ctx) = {
|
||||
.bh_lock = INIT_LOCAL_LOCK(bh_lock),
|
||||
};
|
||||
|
||||
/* Same layout as nft_expr but it embeds the private expression data area. */
|
||||
struct __nft_expr {
|
||||
@@ -237,12 +244,15 @@ static bool nft_inner_restore_tun_ctx(const struct nft_pktinfo *pkt,
|
||||
struct nft_inner_tun_ctx *this_cpu_tun_ctx;
|
||||
|
||||
local_bh_disable();
|
||||
this_cpu_tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
|
||||
local_lock_nested_bh(&nft_pcpu_tun_ctx.bh_lock);
|
||||
this_cpu_tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx.ctx);
|
||||
if (this_cpu_tun_ctx->cookie != (unsigned long)pkt->skb) {
|
||||
local_bh_enable();
|
||||
local_unlock_nested_bh(&nft_pcpu_tun_ctx.bh_lock);
|
||||
return false;
|
||||
}
|
||||
*tun_ctx = *this_cpu_tun_ctx;
|
||||
local_unlock_nested_bh(&nft_pcpu_tun_ctx.bh_lock);
|
||||
local_bh_enable();
|
||||
|
||||
return true;
|
||||
@@ -254,9 +264,11 @@ static void nft_inner_save_tun_ctx(const struct nft_pktinfo *pkt,
|
||||
struct nft_inner_tun_ctx *this_cpu_tun_ctx;
|
||||
|
||||
local_bh_disable();
|
||||
this_cpu_tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
|
||||
local_lock_nested_bh(&nft_pcpu_tun_ctx.bh_lock);
|
||||
this_cpu_tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx.ctx);
|
||||
if (this_cpu_tun_ctx->cookie != tun_ctx->cookie)
|
||||
*this_cpu_tun_ctx = *tun_ctx;
|
||||
local_unlock_nested_bh(&nft_pcpu_tun_ctx.bh_lock);
|
||||
local_bh_enable();
|
||||
}
|
||||
|
||||
|
||||
@@ -621,10 +621,10 @@ static int nft_tunnel_opts_dump(struct sk_buff *skb,
|
||||
struct geneve_opt *opt;
|
||||
int offset = 0;
|
||||
|
||||
inner = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS_GENEVE);
|
||||
if (!inner)
|
||||
goto failure;
|
||||
while (opts->len > offset) {
|
||||
inner = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS_GENEVE);
|
||||
if (!inner)
|
||||
goto failure;
|
||||
opt = (struct geneve_opt *)(opts->u.data + offset);
|
||||
if (nla_put_be16(skb, NFTA_TUNNEL_KEY_GENEVE_CLASS,
|
||||
opt->opt_class) ||
|
||||
@@ -634,8 +634,8 @@ static int nft_tunnel_opts_dump(struct sk_buff *skb,
|
||||
opt->length * 4, opt->opt_data))
|
||||
goto inner_failure;
|
||||
offset += sizeof(*opt) + opt->length * 4;
|
||||
nla_nest_end(skb, inner);
|
||||
}
|
||||
nla_nest_end(skb, inner);
|
||||
}
|
||||
nla_nest_end(skb, nest);
|
||||
return 0;
|
||||
|
||||
@@ -91,7 +91,7 @@ tcpoptstrip_tg4(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
return tcpoptstrip_mangle_packet(skb, par, ip_hdrlen(skb));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_IP6_NF_MANGLE)
|
||||
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
|
||||
static unsigned int
|
||||
tcpoptstrip_tg6(struct sk_buff *skb, const struct xt_action_param *par)
|
||||
{
|
||||
@@ -119,7 +119,7 @@ static struct xt_target tcpoptstrip_tg_reg[] __read_mostly = {
|
||||
.targetsize = sizeof(struct xt_tcpoptstrip_target_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
#if IS_ENABLED(CONFIG_IP6_NF_MANGLE)
|
||||
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
|
||||
{
|
||||
.name = "TCPOPTSTRIP",
|
||||
.family = NFPROTO_IPV6,
|
||||
|
||||
@@ -48,7 +48,7 @@ static struct xt_target mark_tg_reg[] __read_mostly = {
|
||||
.targetsize = sizeof(struct xt_mark_tginfo2),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
#if IS_ENABLED(CONFIG_IP_NF_ARPTABLES)
|
||||
#if IS_ENABLED(CONFIG_IP_NF_ARPTABLES) || IS_ENABLED(CONFIG_NFT_COMPAT_ARP)
|
||||
{
|
||||
.name = "MARK",
|
||||
.revision = 2,
|
||||
|
||||
Reference in New Issue
Block a user