OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | From 1bb0c3ec899827cfa4668bb63a08713a40744d21 Mon Sep 17 00:00:00 2001 |
2 | From: Florian Westphal <fw@strlen.de> |
||
3 | Date: Sun, 9 Jul 2017 08:58:30 +0200 |
||
4 | Subject: [PATCH] netfilter: conntrack: cache route for forwarded connections |
||
5 | |||
6 | ... to avoid per-packet FIB lookup if possible. |
||
7 | |||
8 | The cached dst is re-used provided the input interface |
||
9 | is the same as that of the previous packet in the same direction. |
||
10 | |||
11 | If not, the cached dst is invalidated. |
||
12 | |||
13 | For ipv6 we also need to store sernum, else dst_check doesn't work, |
||
14 | pointed out by Eric Dumazet. |
||
15 | |||
16 | This should speed up forwarding when conntrack is already in use |
||
17 | anyway, especially when using reverse path filtering -- active RPF |
||
18 | enforces two FIB lookups for each packet. |
||
19 | |||
20 | Before the routing cache removal this didn't matter since RPF was performed |
||
21 | only when route cache didn't yield a result; but without route cache it |
||
22 | comes at higher price. |
||
23 | |||
24 | Julian Anastasov suggested to add NETDEV_UNREGISTER handler to |
||
25 | avoid holding on to dsts of 'frozen' conntracks. |
||
26 | |||
27 | Signed-off-by: Florian Westphal <fw@strlen.de> |
||
28 | --- |
||
29 | include/net/netfilter/nf_conntrack_extend.h | 4 + |
||
30 | include/net/netfilter/nf_conntrack_rtcache.h | 34 +++ |
||
31 | net/netfilter/Kconfig | 12 + |
||
32 | net/netfilter/Makefile | 3 + |
||
33 | net/netfilter/nf_conntrack_rtcache.c | 428 +++++++++++++++++++++++++++ |
||
34 | 5 files changed, 481 insertions(+) |
||
35 | create mode 100644 include/net/netfilter/nf_conntrack_rtcache.h |
||
36 | create mode 100644 net/netfilter/nf_conntrack_rtcache.c |
||
37 | |||
38 | --- a/include/net/netfilter/nf_conntrack_extend.h |
||
39 | +++ b/include/net/netfilter/nf_conntrack_extend.h |
||
40 | @@ -28,6 +28,9 @@ enum nf_ct_ext_id { |
||
41 | #if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) |
||
42 | NF_CT_EXT_SYNPROXY, |
||
43 | #endif |
||
44 | +#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE) |
||
45 | + NF_CT_EXT_RTCACHE, |
||
46 | +#endif |
||
47 | NF_CT_EXT_NUM, |
||
48 | }; |
||
49 | |||
50 | @@ -40,6 +43,7 @@ enum nf_ct_ext_id { |
||
51 | #define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout |
||
52 | #define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels |
||
53 | #define NF_CT_EXT_SYNPROXY_TYPE struct nf_conn_synproxy |
||
54 | +#define NF_CT_EXT_RTCACHE_TYPE struct nf_conn_rtcache |
||
55 | |||
56 | /* Extensions: optional stuff which isn't permanently in struct. */ |
||
57 | struct nf_ct_ext { |
||
58 | --- /dev/null |
||
59 | +++ b/include/net/netfilter/nf_conntrack_rtcache.h |
||
60 | @@ -0,0 +1,34 @@ |
||
61 | +#include <linux/gfp.h> |
||
62 | +#include <net/netfilter/nf_conntrack.h> |
||
63 | +#include <net/netfilter/nf_conntrack_extend.h> |
||
64 | + |
||
65 | +struct dst_entry; |
||
66 | + |
||
67 | +struct nf_conn_dst_cache { |
||
68 | + struct dst_entry *dst; |
||
69 | + int iif; |
||
70 | +#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) |
||
71 | + u32 cookie; |
||
72 | +#endif |
||
73 | + |
||
74 | +}; |
||
75 | + |
||
76 | +struct nf_conn_rtcache { |
||
77 | + struct nf_conn_dst_cache cached_dst[IP_CT_DIR_MAX]; |
||
78 | +}; |
||
79 | + |
||
80 | +static inline |
||
81 | +struct nf_conn_rtcache *nf_ct_rtcache_find(const struct nf_conn *ct) |
||
82 | +{ |
||
83 | +#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE) |
||
84 | + return nf_ct_ext_find(ct, NF_CT_EXT_RTCACHE); |
||
85 | +#else |
||
86 | + return NULL; |
||
87 | +#endif |
||
88 | +} |
||
89 | + |
||
90 | +static inline int nf_conn_rtcache_iif_get(const struct nf_conn_rtcache *rtc, |
||
91 | + enum ip_conntrack_dir dir) |
||
92 | +{ |
||
93 | + return rtc->cached_dst[dir].iif; |
||
94 | +} |
||
95 | --- a/net/netfilter/Kconfig |
||
96 | +++ b/net/netfilter/Kconfig |
||
97 | @@ -118,6 +118,18 @@ config NF_CONNTRACK_EVENTS |
||
98 | |||
99 | If unsure, say `N'. |
||
100 | |||
101 | +config NF_CONNTRACK_RTCACHE |
||
102 | + tristate "Cache route entries in conntrack objects" |
||
103 | + depends on NETFILTER_ADVANCED |
||
104 | + depends on NF_CONNTRACK |
||
105 | + help |
||
106 | + If this option is enabled, the connection tracking code will |
||
107 | + cache routing information for each connection that is being |
||
108 | + forwarded, at a cost of 32 bytes per conntrack object. |
||
109 | + |
||
110 | + To compile it as a module, choose M here. If unsure, say N. |
||
111 | + The module will be called nf_conntrack_rtcache. |
||
112 | + |
||
113 | config NF_CONNTRACK_TIMEOUT |
||
114 | bool 'Connection tracking timeout' |
||
115 | depends on NETFILTER_ADVANCED |
||
116 | --- a/net/netfilter/Makefile |
||
117 | +++ b/net/netfilter/Makefile |
||
118 | @@ -19,6 +19,9 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += n |
||
119 | # connection tracking |
||
120 | obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o |
||
121 | |||
122 | +# optional conntrack route cache extension |
||
123 | +obj-$(CONFIG_NF_CONNTRACK_RTCACHE) += nf_conntrack_rtcache.o |
||
124 | + |
||
125 | obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o |
||
126 | |||
127 | # netlink interface for nf_conntrack |
||
128 | --- /dev/null |
||
129 | +++ b/net/netfilter/nf_conntrack_rtcache.c |
||
130 | @@ -0,0 +1,428 @@ |
||
131 | +/* route cache for netfilter. |
||
132 | + * |
||
133 | + * (C) 2014 Red Hat GmbH |
||
134 | + * |
||
135 | + * This program is free software; you can redistribute it and/or modify |
||
136 | + * it under the terms of the GNU General Public License version 2 as |
||
137 | + * published by the Free Software Foundation. |
||
138 | + */ |
||
139 | + |
||
140 | +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
||
141 | + |
||
142 | +#include <linux/types.h> |
||
143 | +#include <linux/netfilter.h> |
||
144 | +#include <linux/skbuff.h> |
||
145 | +#include <linux/stddef.h> |
||
146 | +#include <linux/kernel.h> |
||
147 | +#include <linux/netdevice.h> |
||
148 | +#include <linux/export.h> |
||
149 | +#include <linux/module.h> |
||
150 | + |
||
151 | +#include <net/dst.h> |
||
152 | + |
||
153 | +#include <net/netfilter/nf_conntrack.h> |
||
154 | +#include <net/netfilter/nf_conntrack_core.h> |
||
155 | +#include <net/netfilter/nf_conntrack_extend.h> |
||
156 | +#include <net/netfilter/nf_conntrack_rtcache.h> |
||
157 | + |
||
158 | +#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) |
||
159 | +#include <net/ip6_fib.h> |
||
160 | +#endif |
||
161 | + |
||
162 | +static void __nf_conn_rtcache_destroy(struct nf_conn_rtcache *rtc, |
||
163 | + enum ip_conntrack_dir dir) |
||
164 | +{ |
||
165 | + struct dst_entry *dst = rtc->cached_dst[dir].dst; |
||
166 | + |
||
167 | + dst_release(dst); |
||
168 | +} |
||
169 | + |
||
170 | +static void nf_conn_rtcache_destroy(struct nf_conn *ct) |
||
171 | +{ |
||
172 | + struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct); |
||
173 | + |
||
174 | + if (!rtc) |
||
175 | + return; |
||
176 | + |
||
177 | + __nf_conn_rtcache_destroy(rtc, IP_CT_DIR_ORIGINAL); |
||
178 | + __nf_conn_rtcache_destroy(rtc, IP_CT_DIR_REPLY); |
||
179 | +} |
||
180 | + |
||
181 | +static void nf_ct_rtcache_ext_add(struct nf_conn *ct) |
||
182 | +{ |
||
183 | + struct nf_conn_rtcache *rtc; |
||
184 | + |
||
185 | + rtc = nf_ct_ext_add(ct, NF_CT_EXT_RTCACHE, GFP_ATOMIC); |
||
186 | + if (rtc) { |
||
187 | + rtc->cached_dst[IP_CT_DIR_ORIGINAL].iif = -1; |
||
188 | + rtc->cached_dst[IP_CT_DIR_ORIGINAL].dst = NULL; |
||
189 | + rtc->cached_dst[IP_CT_DIR_REPLY].iif = -1; |
||
190 | + rtc->cached_dst[IP_CT_DIR_REPLY].dst = NULL; |
||
191 | + } |
||
192 | +} |
||
193 | + |
||
194 | +static struct nf_conn_rtcache *nf_ct_rtcache_find_usable(struct nf_conn *ct) |
||
195 | +{ |
||
196 | + return nf_ct_rtcache_find(ct); |
||
197 | +} |
||
198 | + |
||
199 | +static struct dst_entry * |
||
200 | +nf_conn_rtcache_dst_get(const struct nf_conn_rtcache *rtc, |
||
201 | + enum ip_conntrack_dir dir) |
||
202 | +{ |
||
203 | + return rtc->cached_dst[dir].dst; |
||
204 | +} |
||
205 | + |
||
206 | +static u32 nf_rtcache_get_cookie(int pf, const struct dst_entry *dst) |
||
207 | +{ |
||
208 | +#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) |
||
209 | + if (pf == NFPROTO_IPV6) { |
||
210 | + const struct rt6_info *rt = (const struct rt6_info *)dst; |
||
211 | + |
||
212 | + if (rt->rt6i_node) |
||
213 | + return (u32)rt->rt6i_node->fn_sernum; |
||
214 | + } |
||
215 | +#endif |
||
216 | + return 0; |
||
217 | +} |
||
218 | + |
||
219 | +static void nf_conn_rtcache_dst_set(int pf, |
||
220 | + struct nf_conn_rtcache *rtc, |
||
221 | + struct dst_entry *dst, |
||
222 | + enum ip_conntrack_dir dir, int iif) |
||
223 | +{ |
||
224 | + if (rtc->cached_dst[dir].iif != iif) |
||
225 | + rtc->cached_dst[dir].iif = iif; |
||
226 | + |
||
227 | + if (rtc->cached_dst[dir].dst != dst) { |
||
228 | + struct dst_entry *old; |
||
229 | + |
||
230 | + dst_hold(dst); |
||
231 | + |
||
232 | + old = xchg(&rtc->cached_dst[dir].dst, dst); |
||
233 | + dst_release(old); |
||
234 | + |
||
235 | +#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) |
||
236 | + if (pf == NFPROTO_IPV6) |
||
237 | + rtc->cached_dst[dir].cookie = |
||
238 | + nf_rtcache_get_cookie(pf, dst); |
||
239 | +#endif |
||
240 | + } |
||
241 | +} |
||
242 | + |
||
243 | +static void nf_conn_rtcache_dst_obsolete(struct nf_conn_rtcache *rtc, |
||
244 | + enum ip_conntrack_dir dir) |
||
245 | +{ |
||
246 | + struct dst_entry *old; |
||
247 | + |
||
248 | + pr_debug("Invalidate iif %d for dir %d on cache %p\n", |
||
249 | + rtc->cached_dst[dir].iif, dir, rtc); |
||
250 | + |
||
251 | + old = xchg(&rtc->cached_dst[dir].dst, NULL); |
||
252 | + dst_release(old); |
||
253 | + rtc->cached_dst[dir].iif = -1; |
||
254 | +} |
||
255 | + |
||
256 | +static unsigned int nf_rtcache_in(u_int8_t pf, |
||
257 | + struct sk_buff *skb, |
||
258 | + const struct nf_hook_state *state) |
||
259 | +{ |
||
260 | + struct nf_conn_rtcache *rtc; |
||
261 | + enum ip_conntrack_info ctinfo; |
||
262 | + enum ip_conntrack_dir dir; |
||
263 | + struct dst_entry *dst; |
||
264 | + struct nf_conn *ct; |
||
265 | + int iif; |
||
266 | + u32 cookie; |
||
267 | + |
||
268 | + if (skb_dst(skb) || skb->sk) |
||
269 | + return NF_ACCEPT; |
||
270 | + |
||
271 | + ct = nf_ct_get(skb, &ctinfo); |
||
272 | + if (!ct) |
||
273 | + return NF_ACCEPT; |
||
274 | + |
||
275 | + rtc = nf_ct_rtcache_find_usable(ct); |
||
276 | + if (!rtc) |
||
277 | + return NF_ACCEPT; |
||
278 | + |
||
279 | + /* if iif changes, don't use cache and let ip stack |
||
280 | + * do route lookup. |
||
281 | + * |
||
282 | + * If rp_filter is enabled it might toss skb, so |
||
283 | + * we don't want to avoid these checks. |
||
284 | + */ |
||
285 | + dir = CTINFO2DIR(ctinfo); |
||
286 | + iif = nf_conn_rtcache_iif_get(rtc, dir); |
||
287 | + if (state->in->ifindex != iif) { |
||
288 | + pr_debug("ct %p, iif %d, cached iif %d, skip cached entry\n", |
||
289 | + ct, iif, state->in->ifindex); |
||
290 | + return NF_ACCEPT; |
||
291 | + } |
||
292 | + dst = nf_conn_rtcache_dst_get(rtc, dir); |
||
293 | + if (dst == NULL) |
||
294 | + return NF_ACCEPT; |
||
295 | + |
||
296 | + cookie = nf_rtcache_get_cookie(pf, dst); |
||
297 | + |
||
298 | + dst = dst_check(dst, cookie); |
||
299 | + pr_debug("obtained dst %p for skb %p, cookie %d\n", dst, skb, cookie); |
||
300 | + if (likely(dst)) |
||
301 | + skb_dst_set_noref(skb, dst); |
||
302 | + else |
||
303 | + nf_conn_rtcache_dst_obsolete(rtc, dir); |
||
304 | + |
||
305 | + return NF_ACCEPT; |
||
306 | +} |
||
307 | + |
||
308 | +static unsigned int nf_rtcache_forward(u_int8_t pf, |
||
309 | + struct sk_buff *skb, |
||
310 | + const struct nf_hook_state *state) |
||
311 | +{ |
||
312 | + struct nf_conn_rtcache *rtc; |
||
313 | + enum ip_conntrack_info ctinfo; |
||
314 | + enum ip_conntrack_dir dir; |
||
315 | + struct nf_conn *ct; |
||
316 | + struct dst_entry *dst = skb_dst(skb); |
||
317 | + int iif; |
||
318 | + |
||
319 | + ct = nf_ct_get(skb, &ctinfo); |
||
320 | + if (!ct) |
||
321 | + return NF_ACCEPT; |
||
322 | + |
||
323 | + if (dst && dst_xfrm(dst)) |
||
324 | + return NF_ACCEPT; |
||
325 | + |
||
326 | + if (!nf_ct_is_confirmed(ct)) { |
||
327 | + if (WARN_ON(nf_ct_rtcache_find(ct))) |
||
328 | + return NF_ACCEPT; |
||
329 | + nf_ct_rtcache_ext_add(ct); |
||
330 | + return NF_ACCEPT; |
||
331 | + } |
||
332 | + |
||
333 | + rtc = nf_ct_rtcache_find_usable(ct); |
||
334 | + if (!rtc) |
||
335 | + return NF_ACCEPT; |
||
336 | + |
||
337 | + dir = CTINFO2DIR(ctinfo); |
||
338 | + iif = nf_conn_rtcache_iif_get(rtc, dir); |
||
339 | + pr_debug("ct %p, skb %p, dir %d, iif %d, cached iif %d\n", |
||
340 | + ct, skb, dir, iif, state->in->ifindex); |
||
341 | + if (likely(state->in->ifindex == iif)) |
||
342 | + return NF_ACCEPT; |
||
343 | + |
||
344 | + nf_conn_rtcache_dst_set(pf, rtc, skb_dst(skb), dir, state->in->ifindex); |
||
345 | + return NF_ACCEPT; |
||
346 | +} |
||
347 | + |
||
348 | +static unsigned int nf_rtcache_in4(void *priv, |
||
349 | + struct sk_buff *skb, |
||
350 | + const struct nf_hook_state *state) |
||
351 | +{ |
||
352 | + return nf_rtcache_in(NFPROTO_IPV4, skb, state); |
||
353 | +} |
||
354 | + |
||
355 | +static unsigned int nf_rtcache_forward4(void *priv, |
||
356 | + struct sk_buff *skb, |
||
357 | + const struct nf_hook_state *state) |
||
358 | +{ |
||
359 | + return nf_rtcache_forward(NFPROTO_IPV4, skb, state); |
||
360 | +} |
||
361 | + |
||
362 | +#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) |
||
363 | +static unsigned int nf_rtcache_in6(void *priv, |
||
364 | + struct sk_buff *skb, |
||
365 | + const struct nf_hook_state *state) |
||
366 | +{ |
||
367 | + return nf_rtcache_in(NFPROTO_IPV6, skb, state); |
||
368 | +} |
||
369 | + |
||
370 | +static unsigned int nf_rtcache_forward6(void *priv, |
||
371 | + struct sk_buff *skb, |
||
372 | + const struct nf_hook_state *state) |
||
373 | +{ |
||
374 | + return nf_rtcache_forward(NFPROTO_IPV6, skb, state); |
||
375 | +} |
||
376 | +#endif |
||
377 | + |
||
378 | +static int nf_rtcache_dst_remove(struct nf_conn *ct, void *data) |
||
379 | +{ |
||
380 | + struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct); |
||
381 | + struct net_device *dev = data; |
||
382 | + |
||
383 | + if (!rtc) |
||
384 | + return 0; |
||
385 | + |
||
386 | + if (dev->ifindex == rtc->cached_dst[IP_CT_DIR_ORIGINAL].iif || |
||
387 | + dev->ifindex == rtc->cached_dst[IP_CT_DIR_REPLY].iif) { |
||
388 | + nf_conn_rtcache_dst_obsolete(rtc, IP_CT_DIR_ORIGINAL); |
||
389 | + nf_conn_rtcache_dst_obsolete(rtc, IP_CT_DIR_REPLY); |
||
390 | + } |
||
391 | + |
||
392 | + return 0; |
||
393 | +} |
||
394 | + |
||
395 | +static int nf_rtcache_netdev_event(struct notifier_block *this, |
||
396 | + unsigned long event, void *ptr) |
||
397 | +{ |
||
398 | + struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
||
399 | + struct net *net = dev_net(dev); |
||
400 | + |
||
401 | + if (event == NETDEV_DOWN) |
||
402 | + nf_ct_iterate_cleanup_net(net, nf_rtcache_dst_remove, dev, 0, 0); |
||
403 | + |
||
404 | + return NOTIFY_DONE; |
||
405 | +} |
||
406 | + |
||
407 | +static struct notifier_block nf_rtcache_notifier = { |
||
408 | + .notifier_call = nf_rtcache_netdev_event, |
||
409 | +}; |
||
410 | + |
||
411 | +static struct nf_hook_ops rtcache_ops[] = { |
||
412 | + { |
||
413 | + .hook = nf_rtcache_in4, |
||
414 | + .pf = NFPROTO_IPV4, |
||
415 | + .hooknum = NF_INET_PRE_ROUTING, |
||
416 | + .priority = NF_IP_PRI_LAST, |
||
417 | + }, |
||
418 | + { |
||
419 | + .hook = nf_rtcache_forward4, |
||
420 | + .pf = NFPROTO_IPV4, |
||
421 | + .hooknum = NF_INET_FORWARD, |
||
422 | + .priority = NF_IP_PRI_LAST, |
||
423 | + }, |
||
424 | +#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6) |
||
425 | + { |
||
426 | + .hook = nf_rtcache_in6, |
||
427 | + .pf = NFPROTO_IPV6, |
||
428 | + .hooknum = NF_INET_PRE_ROUTING, |
||
429 | + .priority = NF_IP_PRI_LAST, |
||
430 | + }, |
||
431 | + { |
||
432 | + .hook = nf_rtcache_forward6, |
||
433 | + .pf = NFPROTO_IPV6, |
||
434 | + .hooknum = NF_INET_FORWARD, |
||
435 | + .priority = NF_IP_PRI_LAST, |
||
436 | + }, |
||
437 | +#endif |
||
438 | +}; |
||
439 | + |
||
440 | +static struct nf_ct_ext_type rtcache_extend __read_mostly = { |
||
441 | + .len = sizeof(struct nf_conn_rtcache), |
||
442 | + .align = __alignof__(struct nf_conn_rtcache), |
||
443 | + .id = NF_CT_EXT_RTCACHE, |
||
444 | + .destroy = nf_conn_rtcache_destroy, |
||
445 | +}; |
||
446 | + |
||
447 | +static void __net_exit rtcache_net_exit(struct net *net) |
||
448 | +{ |
||
449 | + /* remove hooks so no new connections get rtcache extension */ |
||
450 | + nf_unregister_net_hooks(net, rtcache_ops, ARRAY_SIZE(rtcache_ops)); |
||
451 | +} |
||
452 | + |
||
453 | +static struct pernet_operations rtcache_ops_net_ops = { |
||
454 | + .exit = rtcache_net_exit, |
||
455 | +}; |
||
456 | + |
||
457 | +static int __init nf_conntrack_rtcache_init(void) |
||
458 | +{ |
||
459 | + int ret = nf_ct_extend_register(&rtcache_extend); |
||
460 | + |
||
461 | + if (ret < 0) { |
||
462 | + pr_err("nf_conntrack_rtcache: Unable to register extension\n"); |
||
463 | + return ret; |
||
464 | + } |
||
465 | + |
||
466 | + ret = register_pernet_subsys(&rtcache_ops_net_ops); |
||
467 | + if (ret) { |
||
468 | + nf_ct_extend_unregister(&rtcache_extend); |
||
469 | + return ret; |
||
470 | + } |
||
471 | + |
||
472 | + ret = nf_register_net_hooks(&init_net, rtcache_ops, |
||
473 | + ARRAY_SIZE(rtcache_ops)); |
||
474 | + if (ret < 0) { |
||
475 | + nf_ct_extend_unregister(&rtcache_extend); |
||
476 | + unregister_pernet_subsys(&rtcache_ops_net_ops); |
||
477 | + return ret; |
||
478 | + } |
||
479 | + |
||
480 | + ret = register_netdevice_notifier(&nf_rtcache_notifier); |
||
481 | + if (ret) { |
||
482 | + nf_unregister_net_hooks(&init_net, rtcache_ops, |
||
483 | + ARRAY_SIZE(rtcache_ops)); |
||
484 | + nf_ct_extend_unregister(&rtcache_extend); |
||
485 | + unregister_pernet_subsys(&rtcache_ops_net_ops); |
||
486 | + } |
||
487 | + |
||
488 | + return ret; |
||
489 | +} |
||
490 | + |
||
491 | +static int nf_rtcache_ext_remove(struct nf_conn *ct, void *data) |
||
492 | +{ |
||
493 | + struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct); |
||
494 | + |
||
495 | + return rtc != NULL; |
||
496 | +} |
||
497 | + |
||
498 | +static bool __exit nf_conntrack_rtcache_wait_for_dying(struct net *net) |
||
499 | +{ |
||
500 | + bool wait = false; |
||
501 | + int cpu; |
||
502 | + |
||
503 | + for_each_possible_cpu(cpu) { |
||
504 | + struct nf_conntrack_tuple_hash *h; |
||
505 | + struct hlist_nulls_node *n; |
||
506 | + struct nf_conn *ct; |
||
507 | + struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); |
||
508 | + |
||
509 | + rcu_read_lock(); |
||
510 | + spin_lock_bh(&pcpu->lock); |
||
511 | + |
||
512 | + hlist_nulls_for_each_entry(h, n, &pcpu->dying, hnnode) { |
||
513 | + ct = nf_ct_tuplehash_to_ctrack(h); |
||
514 | + if (nf_ct_rtcache_find(ct) != NULL) { |
||
515 | + wait = true; |
||
516 | + break; |
||
517 | + } |
||
518 | + } |
||
519 | + spin_unlock_bh(&pcpu->lock); |
||
520 | + rcu_read_unlock(); |
||
521 | + } |
||
522 | + |
||
523 | + return wait; |
||
524 | +} |
||
525 | + |
||
526 | +static void __exit nf_conntrack_rtcache_fini(void) |
||
527 | +{ |
||
528 | + struct net *net; |
||
529 | + int count = 0; |
||
530 | + |
||
531 | + synchronize_net(); |
||
532 | + |
||
533 | + unregister_netdevice_notifier(&nf_rtcache_notifier); |
||
534 | + |
||
535 | + rtnl_lock(); |
||
536 | + |
||
537 | + /* zap all conntracks with rtcache extension */ |
||
538 | + for_each_net(net) |
||
539 | + nf_ct_iterate_cleanup_net(net, nf_rtcache_ext_remove, NULL, 0, 0); |
||
540 | + |
||
541 | + for_each_net(net) { |
||
542 | + /* .. and make sure they're gone from dying list, too */ |
||
543 | + while (nf_conntrack_rtcache_wait_for_dying(net)) { |
||
544 | + msleep(200); |
||
545 | + WARN_ONCE(++count > 25, "Waiting for all rtcache conntracks to go away\n"); |
||
546 | + } |
||
547 | + } |
||
548 | + |
||
549 | + rtnl_unlock(); |
||
550 | + synchronize_net(); |
||
551 | + nf_ct_extend_unregister(&rtcache_extend); |
||
552 | +} |
||
553 | +module_init(nf_conntrack_rtcache_init); |
||
554 | +module_exit(nf_conntrack_rtcache_fini); |
||
555 | + |
||
556 | +MODULE_LICENSE("GPL"); |
||
557 | +MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); |
||
558 | +MODULE_DESCRIPTION("Conntrack route cache extension"); |