Skip to content

Commit 9ec9a97

Browse files
Florian Westphalgregkh
Florian Westphal
authored andcommitted
netfilter: nf_nat_masquerade: defer conntrack walk to work queue
[ Upstream commit 7970a19 ] The ipv4 and device notifiers are called with RTNL mutex held. The table walk can take some time, better not block other RTNL users. 'ip a' has been reported to block for up to 20 seconds when conntrack table has many entries and device down events are frequent (e.g., PPP). Reported-and-tested-by: Martin Zaharinov <[email protected]> Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 5182d6d commit 9ec9a97

File tree

1 file changed

+24
-26
lines changed

1 file changed

+24
-26
lines changed

net/netfilter/nf_nat_masquerade.c

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,14 @@ static void nf_nat_masq_schedule(struct net *net, union nf_inet_addr *addr,
131131
put_net(net);
132132
}
133133

134-
static int device_cmp(struct nf_conn *i, void *ifindex)
134+
static int device_cmp(struct nf_conn *i, void *arg)
135135
{
136136
const struct nf_conn_nat *nat = nfct_nat(i);
137+
const struct masq_dev_work *w = arg;
137138

138139
if (!nat)
139140
return 0;
140-
return nat->masq_index == (int)(long)ifindex;
141+
return nat->masq_index == w->ifindex;
141142
}
142143

143144
static int masq_device_event(struct notifier_block *this,
@@ -153,44 +154,54 @@ static int masq_device_event(struct notifier_block *this,
153154
* and forget them.
154155
*/
155156

156-
nf_ct_iterate_cleanup_net(net, device_cmp,
157-
(void *)(long)dev->ifindex, 0, 0);
157+
nf_nat_masq_schedule(net, NULL, dev->ifindex,
158+
device_cmp, GFP_KERNEL);
158159
}
159160

160161
return NOTIFY_DONE;
161162
}
162163

163164
static int inet_cmp(struct nf_conn *ct, void *ptr)
164165
{
165-
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
166-
struct net_device *dev = ifa->ifa_dev->dev;
167166
struct nf_conntrack_tuple *tuple;
167+
struct masq_dev_work *w = ptr;
168168

169-
if (!device_cmp(ct, (void *)(long)dev->ifindex))
169+
if (!device_cmp(ct, ptr))
170170
return 0;
171171

172172
tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
173173

174-
return ifa->ifa_address == tuple->dst.u3.ip;
174+
return nf_inet_addr_cmp(&w->addr, &tuple->dst.u3);
175175
}
176176

177177
static int masq_inet_event(struct notifier_block *this,
178178
unsigned long event,
179179
void *ptr)
180180
{
181-
struct in_device *idev = ((struct in_ifaddr *)ptr)->ifa_dev;
182-
struct net *net = dev_net(idev->dev);
181+
const struct in_ifaddr *ifa = ptr;
182+
const struct in_device *idev;
183+
const struct net_device *dev;
184+
union nf_inet_addr addr;
185+
186+
if (event != NETDEV_DOWN)
187+
return NOTIFY_DONE;
183188

184189
/* The masq_dev_notifier will catch the case of the device going
185190
* down. So if the inetdev is dead and being destroyed we have
186191
* no work to do. Otherwise this is an individual address removal
187192
* and we have to perform the flush.
188193
*/
194+
idev = ifa->ifa_dev;
189195
if (idev->dead)
190196
return NOTIFY_DONE;
191197

192-
if (event == NETDEV_DOWN)
193-
nf_ct_iterate_cleanup_net(net, inet_cmp, ptr, 0, 0);
198+
memset(&addr, 0, sizeof(addr));
199+
200+
addr.ip = ifa->ifa_address;
201+
202+
dev = idev->dev;
203+
nf_nat_masq_schedule(dev_net(idev->dev), &addr, dev->ifindex,
204+
inet_cmp, GFP_KERNEL);
194205

195206
return NOTIFY_DONE;
196207
}
@@ -253,19 +264,6 @@ nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range,
253264
}
254265
EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6);
255266

256-
static int inet6_cmp(struct nf_conn *ct, void *work)
257-
{
258-
struct masq_dev_work *w = (struct masq_dev_work *)work;
259-
struct nf_conntrack_tuple *tuple;
260-
261-
if (!device_cmp(ct, (void *)(long)w->ifindex))
262-
return 0;
263-
264-
tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
265-
266-
return nf_inet_addr_cmp(&w->addr, &tuple->dst.u3);
267-
}
268-
269267
/* atomic notifier; can't call nf_ct_iterate_cleanup_net (it can sleep).
270268
*
271269
* Defer it to the system workqueue.
@@ -289,7 +287,7 @@ static int masq_inet6_event(struct notifier_block *this,
289287

290288
addr.in6 = ifa->addr;
291289

292-
nf_nat_masq_schedule(dev_net(dev), &addr, dev->ifindex, inet6_cmp,
290+
nf_nat_masq_schedule(dev_net(dev), &addr, dev->ifindex, inet_cmp,
293291
GFP_ATOMIC);
294292
return NOTIFY_DONE;
295293
}

0 commit comments

Comments
 (0)