aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/reassembly.c
diff options
context:
space:
mode:
authorZach Brown <zach.brown@oracle.com>2006-04-10 19:05:34 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-04-11 20:21:05 -0400
commitf6596f9d2b4f0255f6cd68c76b85fe4cec6352af (patch)
tree6e95cca360198b2e546904d17760ba71d1dc5ee8 /net/ipv6/reassembly.c
parenta145410dccdb44f81d3b56763ef9b6f721f4e47c (diff)
[IPv6] reassembly: Always compute hash under the fragment lock.
This closes a race where an ipq6hashfn() caller could get a hash value and race with the cycling of the random seed. By the time they got to the read_lock they'd have a stale hash value and might not find previous fragments of their datagram. This matches the previous patch to IPv4. Signed-off-by: Zach Brown <zach.brown@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/reassembly.c')
-rw-r--r--net/ipv6/reassembly.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index b67a45fb93e9..eef985e010ea 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -121,6 +121,10 @@ static __inline__ void fq_unlink(struct frag_queue *fq)
121 write_unlock(&ip6_frag_lock); 121 write_unlock(&ip6_frag_lock);
122} 122}
123 123
124/*
125 * callers should be careful not to use the hash value outside the ipfrag_lock
126 * as doing so could race with ipfrag_hash_rnd being recalculated.
127 */
124static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr, 128static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr,
125 struct in6_addr *daddr) 129 struct in6_addr *daddr)
126{ 130{
@@ -324,15 +328,16 @@ out:
324/* Creation primitives. */ 328/* Creation primitives. */
325 329
326 330
327static struct frag_queue *ip6_frag_intern(unsigned int hash, 331static struct frag_queue *ip6_frag_intern(struct frag_queue *fq_in)
328 struct frag_queue *fq_in)
329{ 332{
330 struct frag_queue *fq; 333 struct frag_queue *fq;
334 unsigned int hash;
331#ifdef CONFIG_SMP 335#ifdef CONFIG_SMP
332 struct hlist_node *n; 336 struct hlist_node *n;
333#endif 337#endif
334 338
335 write_lock(&ip6_frag_lock); 339 write_lock(&ip6_frag_lock);
340 hash = ip6qhashfn(fq_in->id, &fq_in->saddr, &fq_in->daddr);
336#ifdef CONFIG_SMP 341#ifdef CONFIG_SMP
337 hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) { 342 hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) {
338 if (fq->id == fq_in->id && 343 if (fq->id == fq_in->id &&
@@ -362,7 +367,7 @@ static struct frag_queue *ip6_frag_intern(unsigned int hash,
362 367
363 368
364static struct frag_queue * 369static struct frag_queue *
365ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst) 370ip6_frag_create(u32 id, struct in6_addr *src, struct in6_addr *dst)
366{ 371{
367 struct frag_queue *fq; 372 struct frag_queue *fq;
368 373
@@ -379,7 +384,7 @@ ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr
379 spin_lock_init(&fq->lock); 384 spin_lock_init(&fq->lock);
380 atomic_set(&fq->refcnt, 1); 385 atomic_set(&fq->refcnt, 1);
381 386
382 return ip6_frag_intern(hash, fq); 387 return ip6_frag_intern(fq);
383 388
384oom: 389oom:
385 IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); 390 IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
@@ -391,9 +396,10 @@ fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst)
391{ 396{
392 struct frag_queue *fq; 397 struct frag_queue *fq;
393 struct hlist_node *n; 398 struct hlist_node *n;
394 unsigned int hash = ip6qhashfn(id, src, dst); 399 unsigned int hash;
395 400
396 read_lock(&ip6_frag_lock); 401 read_lock(&ip6_frag_lock);
402 hash = ip6qhashfn(id, src, dst);
397 hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) { 403 hlist_for_each_entry(fq, n, &ip6_frag_hash[hash], list) {
398 if (fq->id == id && 404 if (fq->id == id &&
399 ipv6_addr_equal(src, &fq->saddr) && 405 ipv6_addr_equal(src, &fq->saddr) &&
@@ -405,7 +411,7 @@ fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst)
405 } 411 }
406 read_unlock(&ip6_frag_lock); 412 read_unlock(&ip6_frag_lock);
407 413
408 return ip6_frag_create(hash, id, src, dst); 414 return ip6_frag_create(id, src, dst);
409} 415}
410 416
411 417