Commit ef56b640 authored by David S. Miller's avatar David S. Miller

Merge branch 'mpls-notifications'

Benjamin Poirier says:

====================
net: mpls: Netlink notification fixes

fix missing or inaccurate route notifications when devices used in
nexthops are deleted.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 817b6531 18916818
...@@ -409,7 +409,7 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev, ...@@ -409,7 +409,7 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev,
goto err; goto err;
/* Find the output device */ /* Find the output device */
out_dev = rcu_dereference(nh->nh_dev); out_dev = nh->nh_dev;
if (!mpls_output_possible(out_dev)) if (!mpls_output_possible(out_dev))
goto tx_err; goto tx_err;
...@@ -698,7 +698,7 @@ static int mpls_nh_assign_dev(struct net *net, struct mpls_route *rt, ...@@ -698,7 +698,7 @@ static int mpls_nh_assign_dev(struct net *net, struct mpls_route *rt,
(dev->addr_len != nh->nh_via_alen)) (dev->addr_len != nh->nh_via_alen))
goto errout; goto errout;
RCU_INIT_POINTER(nh->nh_dev, dev); nh->nh_dev = dev;
if (!(dev->flags & IFF_UP)) { if (!(dev->flags & IFF_UP)) {
nh->nh_flags |= RTNH_F_DEAD; nh->nh_flags |= RTNH_F_DEAD;
...@@ -1491,26 +1491,53 @@ static void mpls_dev_destroy_rcu(struct rcu_head *head) ...@@ -1491,26 +1491,53 @@ 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) {
if (!nh->nh_dev || nh->nh_dev == dev)
deleted++;
if (nh->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;
if (rtnl_dereference(nh->nh_dev) != dev) if (nh->nh_dev != dev)
goto next; goto next;
switch (event) { switch (event) {
...@@ -1523,23 +1550,22 @@ static void mpls_ifdown(struct net_device *dev, int event) ...@@ -1523,23 +1550,22 @@ static void mpls_ifdown(struct net_device *dev, int event)
break; break;
} }
if (event == NETDEV_UNREGISTER) if (event == NETDEV_UNREGISTER)
RCU_INIT_POINTER(nh->nh_dev, NULL); nh->nh_dev = NULL;
if (nh->nh_flags != nh_flags) if (nh->nh_flags != nh_flags)
WRITE_ONCE(nh->nh_flags, nh_flags); WRITE_ONCE(nh->nh_flags, nh_flags);
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)
...@@ -1559,14 +1585,12 @@ static void mpls_ifup(struct net_device *dev, unsigned int flags) ...@@ -1559,14 +1585,12 @@ static void mpls_ifup(struct net_device *dev, unsigned int flags)
alive = 0; alive = 0;
change_nexthops(rt) { change_nexthops(rt) {
unsigned int nh_flags = nh->nh_flags; unsigned int nh_flags = nh->nh_flags;
struct net_device *nh_dev =
rtnl_dereference(nh->nh_dev);
if (!(nh_flags & flags)) { if (!(nh_flags & flags)) {
alive++; alive++;
continue; continue;
} }
if (nh_dev != dev) if (nh->nh_dev != dev)
continue; continue;
alive++; alive++;
nh_flags &= ~flags; nh_flags &= ~flags;
...@@ -1597,8 +1621,12 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event, ...@@ -1597,8 +1621,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 +1637,18 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event, ...@@ -1609,13 +1637,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 +1659,6 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event, ...@@ -1626,8 +1659,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)
...@@ -1994,7 +2025,7 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, ...@@ -1994,7 +2025,7 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh), nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh),
nh->nh_via_alen)) nh->nh_via_alen))
goto nla_put_failure; goto nla_put_failure;
dev = rtnl_dereference(nh->nh_dev); dev = nh->nh_dev;
if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex)) if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex))
goto nla_put_failure; goto nla_put_failure;
if (nh->nh_flags & RTNH_F_LINKDOWN) if (nh->nh_flags & RTNH_F_LINKDOWN)
...@@ -2012,7 +2043,7 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, ...@@ -2012,7 +2043,7 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
goto nla_put_failure; goto nla_put_failure;
for_nexthops(rt) { for_nexthops(rt) {
dev = rtnl_dereference(nh->nh_dev); dev = nh->nh_dev;
if (!dev) if (!dev)
continue; continue;
...@@ -2123,18 +2154,14 @@ static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, ...@@ -2123,18 +2154,14 @@ static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
static bool mpls_rt_uses_dev(struct mpls_route *rt, static bool mpls_rt_uses_dev(struct mpls_route *rt,
const struct net_device *dev) const struct net_device *dev)
{ {
struct net_device *nh_dev;
if (rt->rt_nhn == 1) { if (rt->rt_nhn == 1) {
struct mpls_nh *nh = rt->rt_nh; struct mpls_nh *nh = rt->rt_nh;
nh_dev = rtnl_dereference(nh->nh_dev); if (nh->nh_dev == dev)
if (dev == nh_dev)
return true; return true;
} else { } else {
for_nexthops(rt) { for_nexthops(rt) {
nh_dev = rtnl_dereference(nh->nh_dev); if (nh->nh_dev == dev)
if (nh_dev == dev)
return true; return true;
} endfor_nexthops(rt); } endfor_nexthops(rt);
} }
...@@ -2222,7 +2249,7 @@ static inline size_t lfib_nlmsg_size(struct mpls_route *rt) ...@@ -2222,7 +2249,7 @@ static inline size_t lfib_nlmsg_size(struct mpls_route *rt)
size_t nhsize = 0; size_t nhsize = 0;
for_nexthops(rt) { for_nexthops(rt) {
if (!rtnl_dereference(nh->nh_dev)) if (!nh->nh_dev)
continue; continue;
nhsize += nla_total_size(sizeof(struct rtnexthop)); nhsize += nla_total_size(sizeof(struct rtnexthop));
/* RTA_VIA */ /* RTA_VIA */
...@@ -2468,7 +2495,7 @@ static int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, ...@@ -2468,7 +2495,7 @@ static int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,
nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh), nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh),
nh->nh_via_alen)) nh->nh_via_alen))
goto nla_put_failure; goto nla_put_failure;
dev = rtnl_dereference(nh->nh_dev); dev = nh->nh_dev;
if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex)) if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex))
goto nla_put_failure; goto nla_put_failure;
...@@ -2507,7 +2534,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) ...@@ -2507,7 +2534,7 @@ static int resize_platform_label_table(struct net *net, size_t limit)
rt0 = mpls_rt_alloc(1, lo->addr_len, 0); rt0 = mpls_rt_alloc(1, lo->addr_len, 0);
if (IS_ERR(rt0)) if (IS_ERR(rt0))
goto nort0; goto nort0;
RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo); rt0->rt_nh->nh_dev = lo;
rt0->rt_protocol = RTPROT_KERNEL; rt0->rt_protocol = RTPROT_KERNEL;
rt0->rt_payload_type = MPT_IPV4; rt0->rt_payload_type = MPT_IPV4;
rt0->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; rt0->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT;
...@@ -2521,7 +2548,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) ...@@ -2521,7 +2548,7 @@ static int resize_platform_label_table(struct net *net, size_t limit)
rt2 = mpls_rt_alloc(1, lo->addr_len, 0); rt2 = mpls_rt_alloc(1, lo->addr_len, 0);
if (IS_ERR(rt2)) if (IS_ERR(rt2))
goto nort2; goto nort2;
RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo); rt2->rt_nh->nh_dev = lo;
rt2->rt_protocol = RTPROT_KERNEL; rt2->rt_protocol = RTPROT_KERNEL;
rt2->rt_payload_type = MPT_IPV6; rt2->rt_payload_type = MPT_IPV6;
rt2->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; rt2->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT;
......
...@@ -87,7 +87,7 @@ enum mpls_payload_type { ...@@ -87,7 +87,7 @@ enum mpls_payload_type {
}; };
struct mpls_nh { /* next hop label forwarding entry */ struct mpls_nh { /* next hop label forwarding entry */
struct net_device __rcu *nh_dev; struct net_device *nh_dev;
/* nh_flags is accessed under RCU in the packet path; it is /* nh_flags is accessed under RCU in the packet path; it is
* modified handling netdev events with rtnl lock held * modified handling netdev events with rtnl lock held
......
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