Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv4: Don't do expensive useless work during inetdev destroy.

When an inetdev is destroyed, every address assigned to the interface
is removed. And in this scenerio we do two pointless things which can
be very expensive if the number of assigned interfaces is large:

1) Address promotion. We are deleting all addresses, so there is no
point in doing this.

2) A full nf conntrack table purge for every address. We only need to
do this once, as is already caught by the existing
masq_dev_notifier so masq_inet_event() can skip this.

Reported-by: Solar Designer <solar@openwall.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Tested-by: Cyrill Gorcunov <gorcunov@openvz.org>

+18 -2
+4
net/ipv4/devinet.c
··· 334 334 335 335 ASSERT_RTNL(); 336 336 337 + if (in_dev->dead) 338 + goto no_promotions; 339 + 337 340 /* 1. Deleting primary ifaddr forces deletion all secondaries 338 341 * unless alias promotion is set 339 342 **/ ··· 383 380 fib_del_ifaddr(ifa, ifa1); 384 381 } 385 382 383 + no_promotions: 386 384 /* 2. Unlink it */ 387 385 388 386 *ifap = ifa1->ifa_next;
+4
net/ipv4/fib_frontend.c
··· 922 922 subnet = 1; 923 923 } 924 924 925 + if (in_dev->dead) 926 + goto no_promotions; 927 + 925 928 /* Deletion is more complicated than add. 926 929 * We should take care of not to delete too much :-) 927 930 * ··· 1000 997 } 1001 998 } 1002 999 1000 + no_promotions: 1003 1001 if (!(ok & BRD_OK)) 1004 1002 fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim); 1005 1003 if (subnet && ifa->ifa_prefixlen < 31) {
+10 -2
net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
··· 108 108 unsigned long event, 109 109 void *ptr) 110 110 { 111 - struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev; 111 + struct in_device *idev = ((struct in_ifaddr *)ptr)->ifa_dev; 112 112 struct netdev_notifier_info info; 113 113 114 - netdev_notifier_info_init(&info, dev); 114 + /* The masq_dev_notifier will catch the case of the device going 115 + * down. So if the inetdev is dead and being destroyed we have 116 + * no work to do. Otherwise this is an individual address removal 117 + * and we have to perform the flush. 118 + */ 119 + if (idev->dead) 120 + return NOTIFY_DONE; 121 + 122 + netdev_notifier_info_init(&info, idev->dev); 115 123 return masq_device_event(this, event, &info); 116 124 } 117 125