| From 1c742b9bdd199e302da488def5c4292d50b6aff8 Mon Sep 17 00:00:00 2001 |
| From: Taehee Yoo <ap420073@gmail.com> |
| Date: Sun, 7 Oct 2018 00:09:18 +0900 |
| Subject: netfilter: xt_TEE: fix wrong interface selection |
| |
| [ Upstream commit f24d2d4f9586985509320f90308723d3d0c4e47f ] |
| |
| TEE netdevice notifier handler checks only interface name. however |
| each netns can have same interface name. hence other netns's interface |
| could be selected. |
| |
| test commands: |
| %ip netns add vm1 |
| %iptables -I INPUT -p icmp -j TEE --gateway 192.168.1.1 --oif enp2s0 |
| %ip link set enp2s0 netns vm1 |
| |
| Above rule is in the root netns. but that rule could get enp2s0 |
| ifindex of vm1 by notifier handler. |
| |
| After this patch, TEE rule is added to the per-netns list. |
| |
| Fixes: 9e2f6c5d78db ("netfilter: Rework xt_TEE netdevice notifier") |
| Signed-off-by: Taehee Yoo <ap420073@gmail.com> |
| Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/netfilter/xt_TEE.c | 69 +++++++++++++++++++++++++++++++----------- |
| 1 file changed, 52 insertions(+), 17 deletions(-) |
| |
| diff --git a/net/netfilter/xt_TEE.c b/net/netfilter/xt_TEE.c |
| index 0d0d68c989df..673ad2099f97 100644 |
| --- a/net/netfilter/xt_TEE.c |
| +++ b/net/netfilter/xt_TEE.c |
| @@ -14,6 +14,8 @@ |
| #include <linux/skbuff.h> |
| #include <linux/route.h> |
| #include <linux/netfilter/x_tables.h> |
| +#include <net/net_namespace.h> |
| +#include <net/netns/generic.h> |
| #include <net/route.h> |
| #include <net/netfilter/ipv4/nf_dup_ipv4.h> |
| #include <net/netfilter/ipv6/nf_dup_ipv6.h> |
| @@ -25,8 +27,15 @@ struct xt_tee_priv { |
| int oif; |
| }; |
| |
| +static unsigned int tee_net_id __read_mostly; |
| static const union nf_inet_addr tee_zero_address; |
| |
| +struct tee_net { |
| + struct list_head priv_list; |
| + /* lock protects the priv_list */ |
| + struct mutex lock; |
| +}; |
| + |
| static unsigned int |
| tee_tg4(struct sk_buff *skb, const struct xt_action_param *par) |
| { |
| @@ -51,17 +60,16 @@ tee_tg6(struct sk_buff *skb, const struct xt_action_param *par) |
| } |
| #endif |
| |
| -static DEFINE_MUTEX(priv_list_mutex); |
| -static LIST_HEAD(priv_list); |
| - |
| static int tee_netdev_event(struct notifier_block *this, unsigned long event, |
| void *ptr) |
| { |
| struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
| + struct net *net = dev_net(dev); |
| + struct tee_net *tn = net_generic(net, tee_net_id); |
| struct xt_tee_priv *priv; |
| |
| - mutex_lock(&priv_list_mutex); |
| - list_for_each_entry(priv, &priv_list, list) { |
| + mutex_lock(&tn->lock); |
| + list_for_each_entry(priv, &tn->priv_list, list) { |
| switch (event) { |
| case NETDEV_REGISTER: |
| if (!strcmp(dev->name, priv->tginfo->oif)) |
| @@ -79,13 +87,14 @@ static int tee_netdev_event(struct notifier_block *this, unsigned long event, |
| break; |
| } |
| } |
| - mutex_unlock(&priv_list_mutex); |
| + mutex_unlock(&tn->lock); |
| |
| return NOTIFY_DONE; |
| } |
| |
| static int tee_tg_check(const struct xt_tgchk_param *par) |
| { |
| + struct tee_net *tn = net_generic(par->net, tee_net_id); |
| struct xt_tee_tginfo *info = par->targinfo; |
| struct xt_tee_priv *priv; |
| |
| @@ -106,9 +115,9 @@ static int tee_tg_check(const struct xt_tgchk_param *par) |
| priv->oif = -1; |
| info->priv = priv; |
| |
| - mutex_lock(&priv_list_mutex); |
| - list_add(&priv->list, &priv_list); |
| - mutex_unlock(&priv_list_mutex); |
| + mutex_lock(&tn->lock); |
| + list_add(&priv->list, &tn->priv_list); |
| + mutex_unlock(&tn->lock); |
| } else |
| info->priv = NULL; |
| |
| @@ -118,12 +127,13 @@ static int tee_tg_check(const struct xt_tgchk_param *par) |
| |
| static void tee_tg_destroy(const struct xt_tgdtor_param *par) |
| { |
| + struct tee_net *tn = net_generic(par->net, tee_net_id); |
| struct xt_tee_tginfo *info = par->targinfo; |
| |
| if (info->priv) { |
| - mutex_lock(&priv_list_mutex); |
| + mutex_lock(&tn->lock); |
| list_del(&info->priv->list); |
| - mutex_unlock(&priv_list_mutex); |
| + mutex_unlock(&tn->lock); |
| kfree(info->priv); |
| } |
| static_key_slow_dec(&xt_tee_enabled); |
| @@ -156,6 +166,21 @@ static struct xt_target tee_tg_reg[] __read_mostly = { |
| #endif |
| }; |
| |
| +static int __net_init tee_net_init(struct net *net) |
| +{ |
| + struct tee_net *tn = net_generic(net, tee_net_id); |
| + |
| + INIT_LIST_HEAD(&tn->priv_list); |
| + mutex_init(&tn->lock); |
| + return 0; |
| +} |
| + |
| +static struct pernet_operations tee_net_ops = { |
| + .init = tee_net_init, |
| + .id = &tee_net_id, |
| + .size = sizeof(struct tee_net), |
| +}; |
| + |
| static struct notifier_block tee_netdev_notifier = { |
| .notifier_call = tee_netdev_event, |
| }; |
| @@ -164,22 +189,32 @@ static int __init tee_tg_init(void) |
| { |
| int ret; |
| |
| - ret = xt_register_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); |
| - if (ret) |
| + ret = register_pernet_subsys(&tee_net_ops); |
| + if (ret < 0) |
| return ret; |
| + |
| + ret = xt_register_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); |
| + if (ret < 0) |
| + goto cleanup_subsys; |
| + |
| ret = register_netdevice_notifier(&tee_netdev_notifier); |
| - if (ret) { |
| - xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); |
| - return ret; |
| - } |
| + if (ret < 0) |
| + goto unregister_targets; |
| |
| return 0; |
| + |
| +unregister_targets: |
| + xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); |
| +cleanup_subsys: |
| + unregister_pernet_subsys(&tee_net_ops); |
| + return ret; |
| } |
| |
| static void __exit tee_tg_exit(void) |
| { |
| unregister_netdevice_notifier(&tee_netdev_notifier); |
| xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); |
| + unregister_pernet_subsys(&tee_net_ops); |
| } |
| |
| module_init(tee_tg_init); |
| -- |
| 2.19.1 |
| |