diff options
Diffstat (limited to 'net/ipv6/reassembly.c')
-rw-r--r-- | net/ipv6/reassembly.c | 131 |
1 files changed, 38 insertions, 93 deletions
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 6ad19cfc2025..76c88a93b9b5 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c | |||
@@ -143,6 +143,18 @@ static unsigned int ip6_hashfn(struct inet_frag_queue *q) | |||
143 | return ip6qhashfn(fq->id, &fq->saddr, &fq->daddr); | 143 | return ip6qhashfn(fq->id, &fq->saddr, &fq->daddr); |
144 | } | 144 | } |
145 | 145 | ||
146 | int ip6_frag_match(struct inet_frag_queue *q, void *a) | ||
147 | { | ||
148 | struct frag_queue *fq; | ||
149 | struct ip6_create_arg *arg = a; | ||
150 | |||
151 | fq = container_of(q, struct frag_queue, q); | ||
152 | return (fq->id == arg->id && | ||
153 | ipv6_addr_equal(&fq->saddr, arg->src) && | ||
154 | ipv6_addr_equal(&fq->daddr, arg->dst)); | ||
155 | } | ||
156 | EXPORT_SYMBOL(ip6_frag_match); | ||
157 | |||
146 | /* Memory Tracking Functions. */ | 158 | /* Memory Tracking Functions. */ |
147 | static inline void frag_kfree_skb(struct sk_buff *skb, int *work) | 159 | static inline void frag_kfree_skb(struct sk_buff *skb, int *work) |
148 | { | 160 | { |
@@ -152,20 +164,16 @@ static inline void frag_kfree_skb(struct sk_buff *skb, int *work) | |||
152 | kfree_skb(skb); | 164 | kfree_skb(skb); |
153 | } | 165 | } |
154 | 166 | ||
155 | static void ip6_frag_free(struct inet_frag_queue *fq) | 167 | void ip6_frag_init(struct inet_frag_queue *q, void *a) |
156 | { | 168 | { |
157 | kfree(container_of(fq, struct frag_queue, q)); | 169 | struct frag_queue *fq = container_of(q, struct frag_queue, q); |
158 | } | 170 | struct ip6_create_arg *arg = a; |
159 | |||
160 | static inline struct frag_queue *frag_alloc_queue(void) | ||
161 | { | ||
162 | struct frag_queue *fq = kzalloc(sizeof(struct frag_queue), GFP_ATOMIC); | ||
163 | 171 | ||
164 | if(!fq) | 172 | fq->id = arg->id; |
165 | return NULL; | 173 | ipv6_addr_copy(&fq->saddr, arg->src); |
166 | atomic_add(sizeof(struct frag_queue), &ip6_frags.mem); | 174 | ipv6_addr_copy(&fq->daddr, arg->dst); |
167 | return fq; | ||
168 | } | 175 | } |
176 | EXPORT_SYMBOL(ip6_frag_init); | ||
169 | 177 | ||
170 | /* Destruction primitives. */ | 178 | /* Destruction primitives. */ |
171 | 179 | ||
@@ -193,9 +201,11 @@ static void ip6_evictor(struct inet6_dev *idev) | |||
193 | 201 | ||
194 | static void ip6_frag_expire(unsigned long data) | 202 | static void ip6_frag_expire(unsigned long data) |
195 | { | 203 | { |
196 | struct frag_queue *fq = (struct frag_queue *) data; | 204 | struct frag_queue *fq; |
197 | struct net_device *dev = NULL; | 205 | struct net_device *dev = NULL; |
198 | 206 | ||
207 | fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); | ||
208 | |||
199 | spin_lock(&fq->q.lock); | 209 | spin_lock(&fq->q.lock); |
200 | 210 | ||
201 | if (fq->q.last_in & COMPLETE) | 211 | if (fq->q.last_in & COMPLETE) |
@@ -230,98 +240,30 @@ out: | |||
230 | fq_put(fq); | 240 | fq_put(fq); |
231 | } | 241 | } |
232 | 242 | ||
233 | /* Creation primitives. */ | 243 | static __inline__ struct frag_queue * |
234 | 244 | fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst, | |
235 | 245 | struct inet6_dev *idev) | |
236 | static struct frag_queue *ip6_frag_intern(struct frag_queue *fq_in) | ||
237 | { | 246 | { |
238 | struct frag_queue *fq; | 247 | struct inet_frag_queue *q; |
248 | struct ip6_create_arg arg; | ||
239 | unsigned int hash; | 249 | unsigned int hash; |
240 | #ifdef CONFIG_SMP | ||
241 | struct hlist_node *n; | ||
242 | #endif | ||
243 | 250 | ||
244 | write_lock(&ip6_frags.lock); | 251 | arg.id = id; |
245 | hash = ip6qhashfn(fq_in->id, &fq_in->saddr, &fq_in->daddr); | 252 | arg.src = src; |
246 | #ifdef CONFIG_SMP | 253 | arg.dst = dst; |
247 | hlist_for_each_entry(fq, n, &ip6_frags.hash[hash], q.list) { | 254 | hash = ip6qhashfn(id, src, dst); |
248 | if (fq->id == fq_in->id && | ||
249 | ipv6_addr_equal(&fq_in->saddr, &fq->saddr) && | ||
250 | ipv6_addr_equal(&fq_in->daddr, &fq->daddr)) { | ||
251 | atomic_inc(&fq->q.refcnt); | ||
252 | write_unlock(&ip6_frags.lock); | ||
253 | fq_in->q.last_in |= COMPLETE; | ||
254 | fq_put(fq_in); | ||
255 | return fq; | ||
256 | } | ||
257 | } | ||
258 | #endif | ||
259 | fq = fq_in; | ||
260 | |||
261 | if (!mod_timer(&fq->q.timer, jiffies + ip6_frags_ctl.timeout)) | ||
262 | atomic_inc(&fq->q.refcnt); | ||
263 | |||
264 | atomic_inc(&fq->q.refcnt); | ||
265 | hlist_add_head(&fq->q.list, &ip6_frags.hash[hash]); | ||
266 | INIT_LIST_HEAD(&fq->q.lru_list); | ||
267 | list_add_tail(&fq->q.lru_list, &ip6_frags.lru_list); | ||
268 | ip6_frags.nqueues++; | ||
269 | write_unlock(&ip6_frags.lock); | ||
270 | return fq; | ||
271 | } | ||
272 | |||
273 | |||
274 | static struct frag_queue * | ||
275 | ip6_frag_create(__be32 id, struct in6_addr *src, struct in6_addr *dst, | ||
276 | struct inet6_dev *idev) | ||
277 | { | ||
278 | struct frag_queue *fq; | ||
279 | 255 | ||
280 | if ((fq = frag_alloc_queue()) == NULL) | 256 | q = inet_frag_find(&ip6_frags, &arg, hash); |
257 | if (q == NULL) | ||
281 | goto oom; | 258 | goto oom; |
282 | 259 | ||
283 | fq->id = id; | 260 | return container_of(q, struct frag_queue, q); |
284 | ipv6_addr_copy(&fq->saddr, src); | ||
285 | ipv6_addr_copy(&fq->daddr, dst); | ||
286 | |||
287 | init_timer(&fq->q.timer); | ||
288 | fq->q.timer.function = ip6_frag_expire; | ||
289 | fq->q.timer.data = (long) fq; | ||
290 | spin_lock_init(&fq->q.lock); | ||
291 | atomic_set(&fq->q.refcnt, 1); | ||
292 | |||
293 | return ip6_frag_intern(fq); | ||
294 | 261 | ||
295 | oom: | 262 | oom: |
296 | IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMFAILS); | 263 | IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMFAILS); |
297 | return NULL; | 264 | return NULL; |
298 | } | 265 | } |
299 | 266 | ||
300 | static __inline__ struct frag_queue * | ||
301 | fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst, | ||
302 | struct inet6_dev *idev) | ||
303 | { | ||
304 | struct frag_queue *fq; | ||
305 | struct hlist_node *n; | ||
306 | unsigned int hash; | ||
307 | |||
308 | read_lock(&ip6_frags.lock); | ||
309 | hash = ip6qhashfn(id, src, dst); | ||
310 | hlist_for_each_entry(fq, n, &ip6_frags.hash[hash], q.list) { | ||
311 | if (fq->id == id && | ||
312 | ipv6_addr_equal(src, &fq->saddr) && | ||
313 | ipv6_addr_equal(dst, &fq->daddr)) { | ||
314 | atomic_inc(&fq->q.refcnt); | ||
315 | read_unlock(&ip6_frags.lock); | ||
316 | return fq; | ||
317 | } | ||
318 | } | ||
319 | read_unlock(&ip6_frags.lock); | ||
320 | |||
321 | return ip6_frag_create(id, src, dst, idev); | ||
322 | } | ||
323 | |||
324 | |||
325 | static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, | 267 | static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, |
326 | struct frag_hdr *fhdr, int nhoff) | 268 | struct frag_hdr *fhdr, int nhoff) |
327 | { | 269 | { |
@@ -697,8 +639,11 @@ void __init ipv6_frag_init(void) | |||
697 | 639 | ||
698 | ip6_frags.ctl = &ip6_frags_ctl; | 640 | ip6_frags.ctl = &ip6_frags_ctl; |
699 | ip6_frags.hashfn = ip6_hashfn; | 641 | ip6_frags.hashfn = ip6_hashfn; |
700 | ip6_frags.destructor = ip6_frag_free; | 642 | ip6_frags.constructor = ip6_frag_init; |
643 | ip6_frags.destructor = NULL; | ||
701 | ip6_frags.skb_free = NULL; | 644 | ip6_frags.skb_free = NULL; |
702 | ip6_frags.qsize = sizeof(struct frag_queue); | 645 | ip6_frags.qsize = sizeof(struct frag_queue); |
646 | ip6_frags.match = ip6_frag_match; | ||
647 | ip6_frags.frag_expire = ip6_frag_expire; | ||
703 | inet_frags_init(&ip6_frags); | 648 | inet_frags_init(&ip6_frags); |
704 | } | 649 | } |