mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
net: mpls: Fix notifications when deleting a device
There are various problems related to netlink notifications for mpls route
changes in response to interfaces being deleted:
* delete interface of only nexthop
DELROUTE notification is missing RTA_OIF attribute
* delete interface of non-last nexthop
NEWROUTE notification is missing entirely
* delete interface of last nexthop
DELROUTE notification is missing nexthop
All of these problems stem from the fact that existing routes are modified
in-place before sending a notification. Restructure mpls_ifdown() to avoid
changing the route in the DELROUTE cases and to create a copy in the
NEWROUTE case.
Fixes: f8efb73c97
("mpls: multipath route support")
Signed-off-by: Benjamin Poirier <bpoirier@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
817b653160
commit
7d4741eacd
@ -1491,22 +1491,52 @@ static void mpls_dev_destroy_rcu(struct rcu_head *head)
|
|||||||
kfree(mdev);
|
kfree(mdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mpls_ifdown(struct net_device *dev, int event)
|
static int mpls_ifdown(struct net_device *dev, int event)
|
||||||
{
|
{
|
||||||
struct mpls_route __rcu **platform_label;
|
struct mpls_route __rcu **platform_label;
|
||||||
struct net *net = dev_net(dev);
|
struct net *net = dev_net(dev);
|
||||||
u8 alive, deleted;
|
|
||||||
unsigned index;
|
unsigned index;
|
||||||
|
|
||||||
platform_label = rtnl_dereference(net->mpls.platform_label);
|
platform_label = rtnl_dereference(net->mpls.platform_label);
|
||||||
for (index = 0; index < net->mpls.platform_labels; index++) {
|
for (index = 0; index < net->mpls.platform_labels; index++) {
|
||||||
struct mpls_route *rt = rtnl_dereference(platform_label[index]);
|
struct mpls_route *rt = rtnl_dereference(platform_label[index]);
|
||||||
|
bool nh_del = false;
|
||||||
|
u8 alive = 0;
|
||||||
|
|
||||||
if (!rt)
|
if (!rt)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
alive = 0;
|
if (event == NETDEV_UNREGISTER) {
|
||||||
deleted = 0;
|
u8 deleted = 0;
|
||||||
|
|
||||||
|
for_nexthops(rt) {
|
||||||
|
struct net_device *nh_dev =
|
||||||
|
rtnl_dereference(nh->nh_dev);
|
||||||
|
|
||||||
|
if (!nh_dev || nh_dev == dev)
|
||||||
|
deleted++;
|
||||||
|
if (nh_dev == dev)
|
||||||
|
nh_del = true;
|
||||||
|
} endfor_nexthops(rt);
|
||||||
|
|
||||||
|
/* if there are no more nexthops, delete the route */
|
||||||
|
if (deleted == rt->rt_nhn) {
|
||||||
|
mpls_route_update(net, index, NULL, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nh_del) {
|
||||||
|
size_t size = sizeof(*rt) + rt->rt_nhn *
|
||||||
|
rt->rt_nh_size;
|
||||||
|
struct mpls_route *orig = rt;
|
||||||
|
|
||||||
|
rt = kmalloc(size, GFP_KERNEL);
|
||||||
|
if (!rt)
|
||||||
|
return -ENOMEM;
|
||||||
|
memcpy(rt, orig, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
change_nexthops(rt) {
|
change_nexthops(rt) {
|
||||||
unsigned int nh_flags = nh->nh_flags;
|
unsigned int nh_flags = nh->nh_flags;
|
||||||
|
|
||||||
@ -1530,16 +1560,15 @@ static void mpls_ifdown(struct net_device *dev, int event)
|
|||||||
next:
|
next:
|
||||||
if (!(nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)))
|
if (!(nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)))
|
||||||
alive++;
|
alive++;
|
||||||
if (!rtnl_dereference(nh->nh_dev))
|
|
||||||
deleted++;
|
|
||||||
} endfor_nexthops(rt);
|
} endfor_nexthops(rt);
|
||||||
|
|
||||||
WRITE_ONCE(rt->rt_nhn_alive, alive);
|
WRITE_ONCE(rt->rt_nhn_alive, alive);
|
||||||
|
|
||||||
/* if there are no more nexthops, delete the route */
|
if (nh_del)
|
||||||
if (event == NETDEV_UNREGISTER && deleted == rt->rt_nhn)
|
mpls_route_update(net, index, rt, NULL);
|
||||||
mpls_route_update(net, index, NULL, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mpls_ifup(struct net_device *dev, unsigned int flags)
|
static void mpls_ifup(struct net_device *dev, unsigned int flags)
|
||||||
@ -1597,8 +1626,12 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
|
|||||||
return NOTIFY_OK;
|
return NOTIFY_OK;
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
|
int err;
|
||||||
|
|
||||||
case NETDEV_DOWN:
|
case NETDEV_DOWN:
|
||||||
mpls_ifdown(dev, event);
|
err = mpls_ifdown(dev, event);
|
||||||
|
if (err)
|
||||||
|
return notifier_from_errno(err);
|
||||||
break;
|
break;
|
||||||
case NETDEV_UP:
|
case NETDEV_UP:
|
||||||
flags = dev_get_flags(dev);
|
flags = dev_get_flags(dev);
|
||||||
@ -1609,13 +1642,18 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
|
|||||||
break;
|
break;
|
||||||
case NETDEV_CHANGE:
|
case NETDEV_CHANGE:
|
||||||
flags = dev_get_flags(dev);
|
flags = dev_get_flags(dev);
|
||||||
if (flags & (IFF_RUNNING | IFF_LOWER_UP))
|
if (flags & (IFF_RUNNING | IFF_LOWER_UP)) {
|
||||||
mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);
|
mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);
|
||||||
else
|
} else {
|
||||||
mpls_ifdown(dev, event);
|
err = mpls_ifdown(dev, event);
|
||||||
|
if (err)
|
||||||
|
return notifier_from_errno(err);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NETDEV_UNREGISTER:
|
case NETDEV_UNREGISTER:
|
||||||
mpls_ifdown(dev, event);
|
err = mpls_ifdown(dev, event);
|
||||||
|
if (err)
|
||||||
|
return notifier_from_errno(err);
|
||||||
mdev = mpls_dev_get(dev);
|
mdev = mpls_dev_get(dev);
|
||||||
if (mdev) {
|
if (mdev) {
|
||||||
mpls_dev_sysctl_unregister(dev, mdev);
|
mpls_dev_sysctl_unregister(dev, mdev);
|
||||||
@ -1626,8 +1664,6 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
|
|||||||
case NETDEV_CHANGENAME:
|
case NETDEV_CHANGENAME:
|
||||||
mdev = mpls_dev_get(dev);
|
mdev = mpls_dev_get(dev);
|
||||||
if (mdev) {
|
if (mdev) {
|
||||||
int err;
|
|
||||||
|
|
||||||
mpls_dev_sysctl_unregister(dev, mdev);
|
mpls_dev_sysctl_unregister(dev, mdev);
|
||||||
err = mpls_dev_sysctl_register(dev, mdev);
|
err = mpls_dev_sysctl_register(dev, mdev);
|
||||||
if (err)
|
if (err)
|
||||||
|
Loading…
Reference in New Issue
Block a user