Skip to content

Commit fbd40ea

Browse files
committed
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>
1 parent f4fa6e6 commit fbd40ea

File tree

3 files changed

+18
-2
lines changed

3 files changed

+18
-2
lines changed

Diff for: net/ipv4/devinet.c

+4
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,9 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
334334

335335
ASSERT_RTNL();
336336

337+
if (in_dev->dead)
338+
goto no_promotions;
339+
337340
/* 1. Deleting primary ifaddr forces deletion all secondaries
338341
* unless alias promotion is set
339342
**/
@@ -380,6 +383,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
380383
fib_del_ifaddr(ifa, ifa1);
381384
}
382385

386+
no_promotions:
383387
/* 2. Unlink it */
384388

385389
*ifap = ifa1->ifa_next;

Diff for: net/ipv4/fib_frontend.c

+4
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,9 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
922922
subnet = 1;
923923
}
924924

925+
if (in_dev->dead)
926+
goto no_promotions;
927+
925928
/* Deletion is more complicated than add.
926929
* We should take care of not to delete too much :-)
927930
*
@@ -997,6 +1000,7 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
9971000
}
9981001
}
9991002

1003+
no_promotions:
10001004
if (!(ok & BRD_OK))
10011005
fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
10021006
if (subnet && ifa->ifa_prefixlen < 31) {

Diff for: net/ipv4/netfilter/nf_nat_masquerade_ipv4.c

+10-2
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,18 @@ static int masq_inet_event(struct notifier_block *this,
108108
unsigned long event,
109109
void *ptr)
110110
{
111-
struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev;
111+
struct in_device *idev = ((struct in_ifaddr *)ptr)->ifa_dev;
112112
struct netdev_notifier_info info;
113113

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);
115123
return masq_device_event(this, event, &info);
116124
}
117125

0 commit comments

Comments
 (0)