bb4d01e
From fbd40ea0180a2d328c5adc61414dc8bab9335ce2 Mon Sep 17 00:00:00 2001
bb4d01e
From: "David S. Miller" <davem@davemloft.net>
bb4d01e
Date: Sun, 13 Mar 2016 23:28:00 -0400
bb4d01e
Subject: ipv4: Don't do expensive useless work during inetdev destroy.
bb4d01e
bb4d01e
When an inetdev is destroyed, every address assigned to the interface
bb4d01e
is removed.  And in this scenerio we do two pointless things which can
bb4d01e
be very expensive if the number of assigned interfaces is large:
bb4d01e
bb4d01e
1) Address promotion.  We are deleting all addresses, so there is no
bb4d01e
   point in doing this.
bb4d01e
bb4d01e
2) A full nf conntrack table purge for every address.  We only need to
bb4d01e
   do this once, as is already caught by the existing
bb4d01e
   masq_dev_notifier so masq_inet_event() can skip this.
bb4d01e
bb4d01e
Reported-by: Solar Designer <solar@openwall.com>
bb4d01e
Signed-off-by: David S. Miller <davem@davemloft.net>
bb4d01e
Tested-by: Cyrill Gorcunov <gorcunov@openvz.org>
bb4d01e
---
bb4d01e
 net/ipv4/devinet.c                          |  4 ++++
bb4d01e
 net/ipv4/fib_frontend.c                     |  4 ++++
bb4d01e
 net/ipv4/netfilter/nf_nat_masquerade_ipv4.c | 12 ++++++++++--
bb4d01e
 3 files changed, 18 insertions(+), 2 deletions(-)
bb4d01e
bb4d01e
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
bb4d01e
index 65e76a4..e333bc8 100644
bb4d01e
--- a/net/ipv4/devinet.c
bb4d01e
+++ b/net/ipv4/devinet.c
bb4d01e
@@ -334,6 +334,9 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
bb4d01e
 
bb4d01e
 	ASSERT_RTNL();
bb4d01e
 
bb4d01e
+	if (in_dev->dead)
bb4d01e
+		goto no_promotions;
bb4d01e
+
bb4d01e
 	/* 1. Deleting primary ifaddr forces deletion all secondaries
bb4d01e
 	 * unless alias promotion is set
bb4d01e
 	 **/
bb4d01e
@@ -380,6 +383,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
bb4d01e
 			fib_del_ifaddr(ifa, ifa1);
bb4d01e
 	}
bb4d01e
 
bb4d01e
+no_promotions:
bb4d01e
 	/* 2. Unlink it */
bb4d01e
 
bb4d01e
 	*ifap = ifa1->ifa_next;
bb4d01e
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
bb4d01e
index 4734475..21add55 100644
bb4d01e
--- a/net/ipv4/fib_frontend.c
bb4d01e
+++ b/net/ipv4/fib_frontend.c
bb4d01e
@@ -922,6 +922,9 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
bb4d01e
 		subnet = 1;
bb4d01e
 	}
bb4d01e
 
bb4d01e
+	if (in_dev->dead)
bb4d01e
+		goto no_promotions;
bb4d01e
+
bb4d01e
 	/* Deletion is more complicated than add.
bb4d01e
 	 * We should take care of not to delete too much :-)
bb4d01e
 	 *
bb4d01e
@@ -997,6 +1000,7 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
bb4d01e
 		}
bb4d01e
 	}
bb4d01e
 
bb4d01e
+no_promotions:
bb4d01e
 	if (!(ok & BRD_OK))
bb4d01e
 		fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
bb4d01e
 	if (subnet && ifa->ifa_prefixlen < 31) {
bb4d01e
diff --git a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
bb4d01e
index c6eb421..ea91058 100644
bb4d01e
--- a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
bb4d01e
+++ b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
bb4d01e
@@ -108,10 +108,18 @@ static int masq_inet_event(struct notifier_block *this,
bb4d01e
 			   unsigned long event,
bb4d01e
 			   void *ptr)
bb4d01e
 {
bb4d01e
-	struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev;
bb4d01e
+	struct in_device *idev = ((struct in_ifaddr *)ptr)->ifa_dev;
bb4d01e
 	struct netdev_notifier_info info;
bb4d01e
 
bb4d01e
-	netdev_notifier_info_init(&info, dev);
bb4d01e
+	/* The masq_dev_notifier will catch the case of the device going
bb4d01e
+	 * down.  So if the inetdev is dead and being destroyed we have
bb4d01e
+	 * no work to do.  Otherwise this is an individual address removal
bb4d01e
+	 * and we have to perform the flush.
bb4d01e
+	 */
bb4d01e
+	if (idev->dead)
bb4d01e
+		return NOTIFY_DONE;
bb4d01e
+
bb4d01e
+	netdev_notifier_info_init(&info, idev->dev);
bb4d01e
 	return masq_device_event(this, event, &info;;
bb4d01e
 }
bb4d01e
 
bb4d01e
-- 
bb4d01e
cgit v0.12
bb4d01e