Commit 4c981e28 authored by Ido Schimmel's avatar Ido Schimmel Committed by David S. Miller

ipv6: Prepare to handle multiple netdev events

To make IPv6 more in line with IPv4 we need to be able to respond
differently to different netdev events. For example, when a netdev is
unregistered all the routes using it as their nexthop device should be
flushed, whereas when the netdev's carrier changes only the 'linkdown'
flag should be toggled.

Currently, this is not possible, as the function that traverses the
routing tables is not aware of the triggering event.

Propagate the triggering event down, so that it could be used in later
patches.
Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Acked-by: default avatarDavid Ahern <dsahern@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2127d95a
...@@ -165,11 +165,11 @@ struct rt6_rtnl_dump_arg { ...@@ -165,11 +165,11 @@ struct rt6_rtnl_dump_arg {
}; };
int rt6_dump_route(struct rt6_info *rt, void *p_arg); int rt6_dump_route(struct rt6_info *rt, void *p_arg);
void rt6_ifdown(struct net *net, struct net_device *dev);
void rt6_mtu_change(struct net_device *dev, unsigned int mtu); void rt6_mtu_change(struct net_device *dev, unsigned int mtu);
void rt6_remove_prefsrc(struct inet6_ifaddr *ifp); void rt6_remove_prefsrc(struct inet6_ifaddr *ifp);
void rt6_clean_tohost(struct net *net, struct in6_addr *gateway); void rt6_clean_tohost(struct net *net, struct in6_addr *gateway);
void rt6_sync_up(struct net_device *dev, unsigned int nh_flags); void rt6_sync_up(struct net_device *dev, unsigned int nh_flags);
void rt6_disable_ip(struct net_device *dev, unsigned long event);
static inline const struct rt6_info *skb_rt6_info(const struct sk_buff *skb) static inline const struct rt6_info *skb_rt6_info(const struct sk_buff *skb)
{ {
......
...@@ -3580,6 +3580,7 @@ static bool addr_is_local(const struct in6_addr *addr) ...@@ -3580,6 +3580,7 @@ static bool addr_is_local(const struct in6_addr *addr)
static int addrconf_ifdown(struct net_device *dev, int how) static int addrconf_ifdown(struct net_device *dev, int how)
{ {
unsigned long event = how ? NETDEV_UNREGISTER : NETDEV_DOWN;
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
struct inet6_dev *idev; struct inet6_dev *idev;
struct inet6_ifaddr *ifa, *tmp; struct inet6_ifaddr *ifa, *tmp;
...@@ -3589,8 +3590,7 @@ static int addrconf_ifdown(struct net_device *dev, int how) ...@@ -3589,8 +3590,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
ASSERT_RTNL(); ASSERT_RTNL();
rt6_ifdown(net, dev); rt6_disable_ip(dev, event);
neigh_ifdown(&nd_tbl, dev);
idev = __in6_dev_get(dev); idev = __in6_dev_get(dev);
if (!idev) if (!idev)
......
...@@ -2344,7 +2344,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, ...@@ -2344,7 +2344,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
rt->rt6i_idev = idev; rt->rt6i_idev = idev;
dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0);
/* Add this dst into uncached_list so that rt6_ifdown() can /* Add this dst into uncached_list so that rt6_disable_ip() can
* do proper release of the net_device * do proper release of the net_device
*/ */
rt6_uncached_list_add(rt); rt6_uncached_list_add(rt);
...@@ -3461,7 +3461,10 @@ void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) ...@@ -3461,7 +3461,10 @@ void rt6_clean_tohost(struct net *net, struct in6_addr *gateway)
struct arg_netdev_event { struct arg_netdev_event {
const struct net_device *dev; const struct net_device *dev;
unsigned int nh_flags; union {
unsigned int nh_flags;
unsigned long event;
};
}; };
static int fib6_ifup(struct rt6_info *rt, void *p_arg) static int fib6_ifup(struct rt6_info *rt, void *p_arg)
...@@ -3488,19 +3491,15 @@ void rt6_sync_up(struct net_device *dev, unsigned int nh_flags) ...@@ -3488,19 +3491,15 @@ void rt6_sync_up(struct net_device *dev, unsigned int nh_flags)
fib6_clean_all(dev_net(dev), fib6_ifup, &arg); fib6_clean_all(dev_net(dev), fib6_ifup, &arg);
} }
struct arg_dev_net {
struct net_device *dev;
struct net *net;
};
/* called with write lock held for table with rt */ /* called with write lock held for table with rt */
static int fib6_ifdown(struct rt6_info *rt, void *arg) static int fib6_ifdown(struct rt6_info *rt, void *p_arg)
{ {
const struct arg_dev_net *adn = arg; const struct arg_netdev_event *arg = p_arg;
const struct net_device *dev = adn->dev; const struct net_device *dev = arg->dev;
const struct net *net = dev_net(dev);
if (rt->dst.dev == dev && if (rt->dst.dev == dev &&
rt != adn->net->ipv6.ip6_null_entry && rt != net->ipv6.ip6_null_entry &&
(rt->rt6i_nsiblings == 0 || netdev_unregistering(dev) || (rt->rt6i_nsiblings == 0 || netdev_unregistering(dev) ||
!rt->rt6i_idev->cnf.ignore_routes_with_linkdown)) { !rt->rt6i_idev->cnf.ignore_routes_with_linkdown)) {
rt->rt6i_nh_flags |= (RTNH_F_DEAD | RTNH_F_LINKDOWN); rt->rt6i_nh_flags |= (RTNH_F_DEAD | RTNH_F_LINKDOWN);
...@@ -3510,15 +3509,21 @@ static int fib6_ifdown(struct rt6_info *rt, void *arg) ...@@ -3510,15 +3509,21 @@ static int fib6_ifdown(struct rt6_info *rt, void *arg)
return 0; return 0;
} }
void rt6_ifdown(struct net *net, struct net_device *dev) static void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
{ {
struct arg_dev_net adn = { struct arg_netdev_event arg = {
.dev = dev, .dev = dev,
.net = net, .event = event,
}; };
fib6_clean_all(net, fib6_ifdown, &adn); fib6_clean_all(dev_net(dev), fib6_ifdown, &arg);
rt6_uncached_list_flush_dev(net, dev); }
void rt6_disable_ip(struct net_device *dev, unsigned long event)
{
rt6_sync_down_dev(dev, event);
rt6_uncached_list_flush_dev(dev_net(dev), dev);
neigh_ifdown(&nd_tbl, dev);
} }
struct rt6_mtu_change_arg { struct rt6_mtu_change_arg {
......
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