diff options
author | Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp> | 2007-07-08 01:23:21 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-07-11 01:17:17 -0400 |
commit | ecfab2c9fe5597221c2b30dec48634a2361a0d08 (patch) | |
tree | 5640796c698074105430c1c1bc24df87f4d0a6b4 /net/netfilter | |
parent | 4ba887790ce2015e8c464809c0be902fb813ad15 (diff) |
[NETFILTER]: nf_conntrack: introduce extension infrastructure
Old space allocator of conntrack had problems about extensibility.
- It required slab cache per combination of extensions.
- It expected what extensions would be assigned, but it was impossible
to expect that completely, then we allocated bigger memory object than
really required.
- It needed to search helper twice due to lock issue.
Now basic informations of a connection are stored in 'struct nf_conn'.
And a storage for extension (helper, NAT) is allocated by kmalloc.
Signed-off-by: Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/Makefile | 2 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_core.c | 4 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_extend.c | 195 |
3 files changed, 200 insertions, 1 deletions
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 3b792687f001..58b4245a1723 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -1,6 +1,6 @@ | |||
1 | netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o | 1 | netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o |
2 | 2 | ||
3 | nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o | 3 | nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o |
4 | nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o | 4 | nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o |
5 | 5 | ||
6 | obj-$(CONFIG_NETFILTER) = netfilter.o | 6 | obj-$(CONFIG_NETFILTER) = netfilter.o |
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 7a15e30356f2..b56f954895bb 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <net/netfilter/nf_conntrack_expect.h> | 36 | #include <net/netfilter/nf_conntrack_expect.h> |
37 | #include <net/netfilter/nf_conntrack_helper.h> | 37 | #include <net/netfilter/nf_conntrack_helper.h> |
38 | #include <net/netfilter/nf_conntrack_core.h> | 38 | #include <net/netfilter/nf_conntrack_core.h> |
39 | #include <net/netfilter/nf_conntrack_extend.h> | ||
39 | 40 | ||
40 | #define NF_CONNTRACK_VERSION "0.5.0" | 41 | #define NF_CONNTRACK_VERSION "0.5.0" |
41 | 42 | ||
@@ -317,6 +318,8 @@ destroy_conntrack(struct nf_conntrack *nfct) | |||
317 | if (l4proto && l4proto->destroy) | 318 | if (l4proto && l4proto->destroy) |
318 | l4proto->destroy(ct); | 319 | l4proto->destroy(ct); |
319 | 320 | ||
321 | nf_ct_ext_destroy(ct); | ||
322 | |||
320 | destroyed = rcu_dereference(nf_conntrack_destroyed); | 323 | destroyed = rcu_dereference(nf_conntrack_destroyed); |
321 | if (destroyed) | 324 | if (destroyed) |
322 | destroyed(ct); | 325 | destroyed(ct); |
@@ -650,6 +653,7 @@ void nf_conntrack_free(struct nf_conn *conntrack) | |||
650 | { | 653 | { |
651 | u_int32_t features = conntrack->features; | 654 | u_int32_t features = conntrack->features; |
652 | NF_CT_ASSERT(features >= NF_CT_F_BASIC && features < NF_CT_F_NUM); | 655 | NF_CT_ASSERT(features >= NF_CT_F_BASIC && features < NF_CT_F_NUM); |
656 | nf_ct_ext_free(conntrack); | ||
653 | DEBUGP("nf_conntrack_free: features = 0x%x, conntrack=%p\n", features, | 657 | DEBUGP("nf_conntrack_free: features = 0x%x, conntrack=%p\n", features, |
654 | conntrack); | 658 | conntrack); |
655 | kmem_cache_free(nf_ct_cache[features].cachep, conntrack); | 659 | kmem_cache_free(nf_ct_cache[features].cachep, conntrack); |
diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c new file mode 100644 index 000000000000..a1a65a1313b3 --- /dev/null +++ b/net/netfilter/nf_conntrack_extend.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /* Structure dynamic extension infrastructure | ||
2 | * Copyright (C) 2004 Rusty Russell IBM Corporation | ||
3 | * Copyright (C) 2007 Netfilter Core Team <coreteam@netfilter.org> | ||
4 | * Copyright (C) 2007 USAGI/WIDE Project <http://www.linux-ipv6.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/mutex.h> | ||
14 | #include <linux/rcupdate.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/skbuff.h> | ||
17 | #include <net/netfilter/nf_conntrack_extend.h> | ||
18 | |||
19 | static struct nf_ct_ext_type *nf_ct_ext_types[NF_CT_EXT_NUM]; | ||
20 | static DEFINE_MUTEX(nf_ct_ext_type_mutex); | ||
21 | |||
22 | /* Horrible trick to figure out smallest amount worth kmallocing. */ | ||
23 | #define CACHE(x) (x) + 0 * | ||
24 | enum { | ||
25 | NF_CT_EXT_MIN_SIZE = | ||
26 | #include <linux/kmalloc_sizes.h> | ||
27 | 1 }; | ||
28 | #undef CACHE | ||
29 | |||
30 | void __nf_ct_ext_destroy(struct nf_conn *ct) | ||
31 | { | ||
32 | unsigned int i; | ||
33 | struct nf_ct_ext_type *t; | ||
34 | |||
35 | for (i = 0; i < NF_CT_EXT_NUM; i++) { | ||
36 | if (!nf_ct_ext_exist(ct, i)) | ||
37 | continue; | ||
38 | |||
39 | rcu_read_lock(); | ||
40 | t = rcu_dereference(nf_ct_ext_types[i]); | ||
41 | |||
42 | /* Here the nf_ct_ext_type might have been unregisterd. | ||
43 | * I.e., it has responsible to cleanup private | ||
44 | * area in all conntracks when it is unregisterd. | ||
45 | */ | ||
46 | if (t && t->destroy) | ||
47 | t->destroy(ct); | ||
48 | rcu_read_unlock(); | ||
49 | } | ||
50 | } | ||
51 | EXPORT_SYMBOL(__nf_ct_ext_destroy); | ||
52 | |||
53 | static void * | ||
54 | nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp) | ||
55 | { | ||
56 | unsigned int off, len, real_len; | ||
57 | struct nf_ct_ext_type *t; | ||
58 | |||
59 | rcu_read_lock(); | ||
60 | t = rcu_dereference(nf_ct_ext_types[id]); | ||
61 | BUG_ON(t == NULL); | ||
62 | off = ALIGN(sizeof(struct nf_ct_ext), t->align); | ||
63 | len = off + t->len; | ||
64 | real_len = t->alloc_size; | ||
65 | rcu_read_unlock(); | ||
66 | |||
67 | *ext = kzalloc(real_len, gfp); | ||
68 | if (!*ext) | ||
69 | return NULL; | ||
70 | |||
71 | (*ext)->offset[id] = off; | ||
72 | (*ext)->len = len; | ||
73 | (*ext)->real_len = real_len; | ||
74 | |||
75 | return (void *)(*ext) + off; | ||
76 | } | ||
77 | |||
78 | void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) | ||
79 | { | ||
80 | struct nf_ct_ext *new; | ||
81 | int i, newlen, newoff; | ||
82 | struct nf_ct_ext_type *t; | ||
83 | |||
84 | if (!ct->ext) | ||
85 | return nf_ct_ext_create(&ct->ext, id, gfp); | ||
86 | |||
87 | if (nf_ct_ext_exist(ct, id)) | ||
88 | return NULL; | ||
89 | |||
90 | rcu_read_lock(); | ||
91 | t = rcu_dereference(nf_ct_ext_types[id]); | ||
92 | BUG_ON(t == NULL); | ||
93 | |||
94 | newoff = ALIGN(ct->ext->len, t->align); | ||
95 | newlen = newoff + t->len; | ||
96 | rcu_read_unlock(); | ||
97 | |||
98 | if (newlen >= ct->ext->real_len) { | ||
99 | new = kmalloc(newlen, gfp); | ||
100 | if (!new) | ||
101 | return NULL; | ||
102 | |||
103 | memcpy(new, ct->ext, ct->ext->len); | ||
104 | |||
105 | for (i = 0; i < NF_CT_EXT_NUM; i++) { | ||
106 | if (!nf_ct_ext_exist(ct, i)) | ||
107 | continue; | ||
108 | |||
109 | rcu_read_lock(); | ||
110 | t = rcu_dereference(nf_ct_ext_types[i]); | ||
111 | if (t && t->move) | ||
112 | t->move(ct, ct->ext + ct->ext->offset[id]); | ||
113 | rcu_read_unlock(); | ||
114 | } | ||
115 | kfree(ct->ext); | ||
116 | new->real_len = newlen; | ||
117 | ct->ext = new; | ||
118 | } | ||
119 | |||
120 | ct->ext->offset[id] = newoff; | ||
121 | ct->ext->len = newlen; | ||
122 | memset((void *)ct->ext + newoff, 0, newlen - newoff); | ||
123 | return (void *)ct->ext + newoff; | ||
124 | } | ||
125 | EXPORT_SYMBOL(__nf_ct_ext_add); | ||
126 | |||
127 | static void update_alloc_size(struct nf_ct_ext_type *type) | ||
128 | { | ||
129 | int i, j; | ||
130 | struct nf_ct_ext_type *t1, *t2; | ||
131 | enum nf_ct_ext_id min = 0, max = NF_CT_EXT_NUM - 1; | ||
132 | |||
133 | /* unnecessary to update all types */ | ||
134 | if ((type->flags & NF_CT_EXT_F_PREALLOC) == 0) { | ||
135 | min = type->id; | ||
136 | max = type->id; | ||
137 | } | ||
138 | |||
139 | /* This assumes that extended areas in conntrack for the types | ||
140 | whose NF_CT_EXT_F_PREALLOC bit set are allocated in order */ | ||
141 | for (i = min; i <= max; i++) { | ||
142 | t1 = nf_ct_ext_types[i]; | ||
143 | if (!t1) | ||
144 | continue; | ||
145 | |||
146 | t1->alloc_size = sizeof(struct nf_ct_ext) | ||
147 | + ALIGN(sizeof(struct nf_ct_ext), t1->align) | ||
148 | + t1->len; | ||
149 | for (j = 0; j < NF_CT_EXT_NUM; j++) { | ||
150 | t2 = nf_ct_ext_types[j]; | ||
151 | if (t2 == NULL || t2 == t1 || | ||
152 | (t2->flags & NF_CT_EXT_F_PREALLOC) == 0) | ||
153 | continue; | ||
154 | |||
155 | t1->alloc_size = ALIGN(t1->alloc_size, t2->align) | ||
156 | + t2->len; | ||
157 | } | ||
158 | if (t1->alloc_size < NF_CT_EXT_MIN_SIZE) | ||
159 | t1->alloc_size = NF_CT_EXT_MIN_SIZE; | ||
160 | } | ||
161 | } | ||
162 | |||
163 | /* This MUST be called in process context. */ | ||
164 | int nf_ct_extend_register(struct nf_ct_ext_type *type) | ||
165 | { | ||
166 | int ret = 0; | ||
167 | |||
168 | mutex_lock(&nf_ct_ext_type_mutex); | ||
169 | if (nf_ct_ext_types[type->id]) { | ||
170 | ret = -EBUSY; | ||
171 | goto out; | ||
172 | } | ||
173 | |||
174 | /* This ensures that nf_ct_ext_create() can allocate enough area | ||
175 | before updating alloc_size */ | ||
176 | type->alloc_size = ALIGN(sizeof(struct nf_ct_ext), type->align) | ||
177 | + type->len; | ||
178 | rcu_assign_pointer(nf_ct_ext_types[type->id], type); | ||
179 | update_alloc_size(type); | ||
180 | out: | ||
181 | mutex_unlock(&nf_ct_ext_type_mutex); | ||
182 | return ret; | ||
183 | } | ||
184 | EXPORT_SYMBOL_GPL(nf_ct_extend_register); | ||
185 | |||
186 | /* This MUST be called in process context. */ | ||
187 | void nf_ct_extend_unregister(struct nf_ct_ext_type *type) | ||
188 | { | ||
189 | mutex_lock(&nf_ct_ext_type_mutex); | ||
190 | rcu_assign_pointer(nf_ct_ext_types[type->id], NULL); | ||
191 | update_alloc_size(type); | ||
192 | mutex_unlock(&nf_ct_ext_type_mutex); | ||
193 | synchronize_rcu(); | ||
194 | } | ||
195 | EXPORT_SYMBOL_GPL(nf_ct_extend_unregister); | ||