lkml.org 
[lkml]   [2021]   [Dec]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.14 091/106] net: mpls: Fix notifications when deleting a device
    Date
    From: Benjamin Poirier <bpoirier@nvidia.com>

    commit 7d4741eacdefa5f0475431645b56baf00784df1f upstream.

    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: f8efb73c97e2 ("mpls: multipath route support")
    Signed-off-by: Benjamin Poirier <bpoirier@nvidia.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    net/mpls/af_mpls.c | 68 ++++++++++++++++++++++++++++++++++++++++-------------
    1 file changed, 52 insertions(+), 16 deletions(-)

    --- a/net/mpls/af_mpls.c
    +++ b/net/mpls/af_mpls.c
    @@ -1407,22 +1407,52 @@ static void mpls_dev_destroy_rcu(struct
    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 net *net = dev_net(dev);
    - u8 alive, deleted;
    unsigned index;

    platform_label = rtnl_dereference(net->mpls.platform_label);
    for (index = 0; index < net->mpls.platform_labels; index++) {
    struct mpls_route *rt = rtnl_dereference(platform_label[index]);
    + bool nh_del = false;
    + u8 alive = 0;

    if (!rt)
    continue;

    - alive = 0;
    - deleted = 0;
    + if (event == NETDEV_UNREGISTER) {
    + 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) {
    unsigned int nh_flags = nh->nh_flags;

    @@ -1446,16 +1476,15 @@ static void mpls_ifdown(struct net_devic
    next:
    if (!(nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)))
    alive++;
    - if (!rtnl_dereference(nh->nh_dev))
    - deleted++;
    } endfor_nexthops(rt);

    WRITE_ONCE(rt->rt_nhn_alive, alive);

    - /* if there are no more nexthops, delete the route */
    - if (event == NETDEV_UNREGISTER && deleted == rt->rt_nhn)
    - mpls_route_update(net, index, NULL, NULL);
    + if (nh_del)
    + mpls_route_update(net, index, rt, NULL);
    }
    +
    + return 0;
    }

    static void mpls_ifup(struct net_device *dev, unsigned int flags)
    @@ -1519,8 +1548,12 @@ static int mpls_dev_notify(struct notifi
    return NOTIFY_OK;

    switch (event) {
    + int err;
    +
    case NETDEV_DOWN:
    - mpls_ifdown(dev, event);
    + err = mpls_ifdown(dev, event);
    + if (err)
    + return notifier_from_errno(err);
    break;
    case NETDEV_UP:
    flags = dev_get_flags(dev);
    @@ -1531,13 +1564,18 @@ static int mpls_dev_notify(struct notifi
    break;
    case NETDEV_CHANGE:
    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);
    - else
    - mpls_ifdown(dev, event);
    + } else {
    + err = mpls_ifdown(dev, event);
    + if (err)
    + return notifier_from_errno(err);
    + }
    break;
    case NETDEV_UNREGISTER:
    - mpls_ifdown(dev, event);
    + err = mpls_ifdown(dev, event);
    + if (err)
    + return notifier_from_errno(err);
    mdev = mpls_dev_get(dev);
    if (mdev) {
    mpls_dev_sysctl_unregister(dev, mdev);
    @@ -1548,8 +1586,6 @@ static int mpls_dev_notify(struct notifi
    case NETDEV_CHANGENAME:
    mdev = mpls_dev_get(dev);
    if (mdev) {
    - int err;
    -
    mpls_dev_sysctl_unregister(dev, mdev);
    err = mpls_dev_sysctl_register(dev, mdev);
    if (err)

    \
     
     \ /
      Last update: 2021-12-06 16:15    [W:2.091 / U:0.388 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site