When ipvlan interface goes down, forget all learned addresses. This is a way to cleanup addresses when master dev switches to another network. Signed-off-by: Dmitry Skorodumov --- drivers/net/ipvlan/ipvlan_main.c | 49 ++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 6fdfeca6081d..28ce36669d39 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -744,14 +744,10 @@ int ipvlan_link_new(struct net_device *dev, struct rtnl_newlink_params *params, } EXPORT_SYMBOL_GPL(ipvlan_link_new); -void ipvlan_link_delete(struct net_device *dev, struct list_head *head) +static void ipvlan_addrs_forget_all(struct ipvl_dev *ipvlan) { - struct ipvl_dev *ipvlan = netdev_priv(dev); struct ipvl_addr *addr, *next; - if (ipvlan_is_learnable(ipvlan->port)) - dev_set_allmulti(dev, -1); - spin_lock_bh(&ipvlan->addrs_lock); list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) { ipvlan_ht_addr_del(addr); @@ -759,6 +755,16 @@ void ipvlan_link_delete(struct net_device *dev, struct list_head *head) kfree_rcu(addr, rcu); } spin_unlock_bh(&ipvlan->addrs_lock); +} + +void ipvlan_link_delete(struct net_device *dev, struct list_head *head) +{ + struct ipvl_dev *ipvlan = netdev_priv(dev); + + if (ipvlan_is_learnable(ipvlan->port)) + dev_set_allmulti(dev, -1); + + ipvlan_addrs_forget_all(ipvlan); ida_free(&ipvlan->port->ida, dev->dev_id); list_del_rcu(&ipvlan->pnode); @@ -816,6 +822,19 @@ int ipvlan_link_register(struct rtnl_link_ops *ops) } EXPORT_SYMBOL_GPL(ipvlan_link_register); +static bool ipvlan_is_valid_dev(const struct net_device *dev) +{ + struct ipvl_dev *ipvlan = netdev_priv(dev); + + if (!netif_is_ipvlan(dev)) + return false; + + if (!ipvlan || !ipvlan->port) + return false; + + return true; +} + static int ipvlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { @@ -827,6 +846,13 @@ static int ipvlan_device_event(struct notifier_block *unused, LIST_HEAD(lst_kill); int err; + if (event == NETDEV_DOWN && ipvlan_is_valid_dev(dev)) { + struct ipvl_dev *ipvlan = netdev_priv(dev); + + ipvlan_addrs_forget_all(ipvlan); + return NOTIFY_DONE; + } + if (!netif_is_ipvlan_port(dev)) return NOTIFY_DONE; @@ -961,19 +987,6 @@ void ipvlan_del_addr(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6) kfree_rcu(addr, rcu); } -static bool ipvlan_is_valid_dev(const struct net_device *dev) -{ - struct ipvl_dev *ipvlan = netdev_priv(dev); - - if (!netif_is_ipvlan(dev)) - return false; - - if (!ipvlan || !ipvlan->port) - return false; - - return true; -} - #if IS_ENABLED(CONFIG_IPV6) static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr) { -- 2.25.1