~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/net/ipv6/reassembly.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *      IPv6 fragment reassembly
  3  *      Linux INET6 implementation 
  4  *
  5  *      Authors:
  6  *      Pedro Roque             <roque@di.fc.ul.pt>     
  7  *
  8  *      $Id: reassembly.c,v 1.22 2000/12/08 17:41:54 davem Exp $
  9  *
 10  *      Based on: net/ipv4/ip_fragment.c
 11  *
 12  *      This program is free software; you can redistribute it and/or
 13  *      modify it under the terms of the GNU General Public License
 14  *      as published by the Free Software Foundation; either version
 15  *      2 of the License, or (at your option) any later version.
 16  */
 17 
 18 /* 
 19  *      Fixes:  
 20  *      Andi Kleen      Make it work with multiple hosts.
 21  *                      More RFC compliance.
 22  *
 23  *      Horst von Brand Add missing #include <linux/string.h>
 24  *      Alexey Kuznetsov        SMP races, threading, cleanup.
 25  */
 26 #include <linux/config.h>
 27 #include <linux/errno.h>
 28 #include <linux/types.h>
 29 #include <linux/string.h>
 30 #include <linux/socket.h>
 31 #include <linux/sockios.h>
 32 #include <linux/sched.h>
 33 #include <linux/net.h>
 34 #include <linux/netdevice.h>
 35 #include <linux/in6.h>
 36 #include <linux/ipv6.h>
 37 #include <linux/icmpv6.h>
 38 
 39 #include <net/sock.h>
 40 #include <net/snmp.h>
 41 
 42 #include <net/ipv6.h>
 43 #include <net/protocol.h>
 44 #include <net/transp_v6.h>
 45 #include <net/rawv6.h>
 46 #include <net/ndisc.h>
 47 #include <net/addrconf.h>
 48 
 49 int sysctl_ip6frag_high_thresh = 256*1024;
 50 int sysctl_ip6frag_low_thresh = 192*1024;
 51 
 52 int sysctl_ip6frag_time = IPV6_FRAG_TIMEOUT;
 53 
 54 struct ip6frag_skb_cb
 55 {
 56         struct inet6_skb_parm   h;
 57         int                     offset;
 58 };
 59 
 60 #define FRAG6_CB(skb)   ((struct ip6frag_skb_cb*)((skb)->cb))
 61 
 62 
 63 /*
 64  *      Equivalent of ipv4 struct ipq
 65  */
 66 
 67 struct frag_queue
 68 {
 69         struct frag_queue       *next;
 70 
 71         __u32                   id;             /* fragment id          */
 72         struct in6_addr         saddr;
 73         struct in6_addr         daddr;
 74 
 75         spinlock_t              lock;
 76         atomic_t                refcnt;
 77         struct timer_list       timer;          /* expire timer         */
 78         struct sk_buff          *fragments;
 79         int                     len;
 80         int                     meat;
 81         int                     iif;
 82         __u8                    last_in;        /* has first/last segment arrived? */
 83 #define COMPLETE                4
 84 #define FIRST_IN                2
 85 #define LAST_IN                 1
 86         __u8                    nexthdr;
 87         __u16                   nhoffset;
 88         struct frag_queue       **pprev;
 89 };
 90 
 91 /* Hash table. */
 92 
 93 #define IP6Q_HASHSZ     64
 94 
 95 static struct frag_queue *ip6_frag_hash[IP6Q_HASHSZ];
 96 static rwlock_t ip6_frag_lock = RW_LOCK_UNLOCKED;
 97 int ip6_frag_nqueues = 0;
 98 
 99 static __inline__ void __fq_unlink(struct frag_queue *fq)
100 {
101         if(fq->next)
102                 fq->next->pprev = fq->pprev;
103         *fq->pprev = fq->next;
104         ip6_frag_nqueues--;
105 }
106 
107 static __inline__ void fq_unlink(struct frag_queue *fq)
108 {
109         write_lock(&ip6_frag_lock);
110         __fq_unlink(fq);
111         write_unlock(&ip6_frag_lock);
112 }
113 
114 static __inline__ unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr,
115                                           struct in6_addr *daddr)
116 {
117         unsigned int h = saddr->s6_addr32[3] ^ daddr->s6_addr32[3] ^ id;
118 
119         h ^= (h>>16);
120         h ^= (h>>8);
121         return h & (IP6Q_HASHSZ - 1);
122 }
123 
124 
125 atomic_t ip6_frag_mem = ATOMIC_INIT(0);
126 
127 /* Memory Tracking Functions. */
128 extern __inline__ void frag_kfree_skb(struct sk_buff *skb)
129 {
130         atomic_sub(skb->truesize, &ip6_frag_mem);
131         kfree_skb(skb);
132 }
133 
134 extern __inline__ void frag_free_queue(struct frag_queue *fq)
135 {
136         atomic_sub(sizeof(struct frag_queue), &ip6_frag_mem);
137         kfree(fq);
138 }
139 
140 extern __inline__ struct frag_queue *frag_alloc_queue(void)
141 {
142         struct frag_queue *fq = kmalloc(sizeof(struct frag_queue), GFP_ATOMIC);
143 
144         if(!fq)
145                 return NULL;
146         atomic_add(sizeof(struct frag_queue), &ip6_frag_mem);
147         return fq;
148 }
149 
150 /* Destruction primitives. */
151 
152 /* Complete destruction of fq. */
153 static void ip6_frag_destroy(struct frag_queue *fq)
154 {
155         struct sk_buff *fp;
156 
157         BUG_TRAP(fq->last_in&COMPLETE);
158         BUG_TRAP(del_timer(&fq->timer) == 0);
159 
160         /* Release all fragment data. */
161         fp = fq->fragments;
162         while (fp) {
163                 struct sk_buff *xp = fp->next;
164 
165                 frag_kfree_skb(fp);
166                 fp = xp;
167         }
168 
169         frag_free_queue(fq);
170 }
171 
172 static __inline__ void fq_put(struct frag_queue *fq)
173 {
174         if (atomic_dec_and_test(&fq->refcnt))
175                 ip6_frag_destroy(fq);
176 }
177 
178 /* Kill fq entry. It is not destroyed immediately,
179  * because caller (and someone more) holds reference count.
180  */
181 static __inline__ void fq_kill(struct frag_queue *fq)
182 {
183         if (del_timer(&fq->timer))
184                 atomic_dec(&fq->refcnt);
185 
186         if (!(fq->last_in & COMPLETE)) {
187                 fq_unlink(fq);
188                 atomic_dec(&fq->refcnt);
189                 fq->last_in |= COMPLETE;
190         }
191 }
192 
193 static void ip6_evictor(void)
194 {
195         int i, progress;
196 
197         do {
198                 if (atomic_read(&ip6_frag_mem) <= sysctl_ip6frag_low_thresh)
199                         return;
200                 progress = 0;
201                 for (i = 0; i < IP6Q_HASHSZ; i++) {
202                         struct frag_queue *fq;
203                         if (ip6_frag_hash[i] == NULL)
204                                 continue;
205 
206                         write_lock(&ip6_frag_lock);
207                         if ((fq = ip6_frag_hash[i]) != NULL) {
208                                 /* find the oldest queue for this hash bucket */
209                                 while (fq->next)
210                                         fq = fq->next;
211                                 __fq_unlink(fq);
212                                 write_unlock(&ip6_frag_lock);
213 
214                                 spin_lock(&fq->lock);
215                                 if (del_timer(&fq->timer))
216                                         atomic_dec(&fq->refcnt);
217                                 fq->last_in |= COMPLETE;
218                                 spin_unlock(&fq->lock);
219 
220                                 fq_put(fq);
221                                 IP6_INC_STATS_BH(Ip6ReasmFails);
222                                 progress = 1;
223                                 continue;
224                         }
225                         write_unlock(&ip6_frag_lock);
226                 }
227         } while (progress);
228 }
229 
230 static void ip6_frag_expire(unsigned long data)
231 {
232         struct frag_queue *fq = (struct frag_queue *) data;
233 
234         spin_lock(&fq->lock);
235 
236         if (fq->last_in & COMPLETE)
237                 goto out;
238 
239         fq_kill(fq);
240 
241         IP6_INC_STATS_BH(Ip6ReasmTimeout);
242         IP6_INC_STATS_BH(Ip6ReasmFails);
243 
244         /* Send error only if the first segment arrived. */
245         if (fq->last_in&FIRST_IN && fq->fragments) {
246                 struct net_device *dev = dev_get_by_index(fq->iif);
247 
248                 /*
249                    But use as source device on which LAST ARRIVED
250                    segment was received. And do not use fq->dev
251                    pointer directly, device might already disappeared.
252                  */
253                 if (dev) {
254                         fq->fragments->dev = dev;
255                         icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,
256                                     dev);
257                         dev_put(dev);
258                 }
259         }
260 out:
261         spin_unlock(&fq->lock);
262         fq_put(fq);
263 }
264 
265 /* Creation primitives. */
266 
267 
268 static struct frag_queue *ip6_frag_intern(unsigned int hash,
269                                           struct frag_queue *fq_in)
270 {
271         struct frag_queue *fq;
272 
273         write_lock(&ip6_frag_lock);
274 #ifdef CONFIG_SMP
275         for (fq = ip6_frag_hash[hash]; fq; fq = fq->next) {
276                 if (fq->id == fq_in->id && 
277                     !ipv6_addr_cmp(&fq_in->saddr, &fq->saddr) &&
278                     !ipv6_addr_cmp(&fq_in->daddr, &fq->daddr)) {
279                         atomic_inc(&fq->refcnt);
280                         write_unlock(&ip6_frag_lock);
281                         fq_in->last_in |= COMPLETE;
282                         fq_put(fq_in);
283                         return fq;
284                 }
285         }
286 #endif
287         fq = fq_in;
288 
289         if (!mod_timer(&fq->timer, jiffies + sysctl_ip6frag_time))
290                 atomic_inc(&fq->refcnt);
291 
292         atomic_inc(&fq->refcnt);
293         if((fq->next = ip6_frag_hash[hash]) != NULL)
294                 fq->next->pprev = &fq->next;
295         ip6_frag_hash[hash] = fq;
296         fq->pprev = &ip6_frag_hash[hash];
297         ip6_frag_nqueues++;
298         write_unlock(&ip6_frag_lock);
299         return fq;
300 }
301 
302 
303 static struct frag_queue *
304 ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst)
305 {
306         struct frag_queue *fq;
307 
308         if ((fq = frag_alloc_queue()) == NULL)
309                 goto oom;
310 
311         memset(fq, 0, sizeof(struct frag_queue));
312 
313         fq->id = id;
314         ipv6_addr_copy(&fq->saddr, src);
315         ipv6_addr_copy(&fq->daddr, dst);
316 
317         /* init_timer has been done by the memset */
318         fq->timer.function = ip6_frag_expire;
319         fq->timer.data = (long) fq;
320         fq->lock = SPIN_LOCK_UNLOCKED;
321         atomic_set(&fq->refcnt, 1);
322 
323         return ip6_frag_intern(hash, fq);
324 
325 oom:
326         IP6_INC_STATS_BH(Ip6ReasmFails);
327         return NULL;
328 }
329 
330 static __inline__ struct frag_queue *
331 fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst)
332 {
333         struct frag_queue *fq;
334         unsigned int hash = ip6qhashfn(id, src, dst);
335 
336         read_lock(&ip6_frag_lock);
337         for(fq = ip6_frag_hash[hash]; fq; fq = fq->next) {
338                 if (fq->id == id && 
339                     !ipv6_addr_cmp(src, &fq->saddr) &&
340                     !ipv6_addr_cmp(dst, &fq->daddr)) {
341                         atomic_inc(&fq->refcnt);
342                         read_unlock(&ip6_frag_lock);
343                         return fq;
344                 }
345         }
346         read_unlock(&ip6_frag_lock);
347 
348         return ip6_frag_create(hash, id, src, dst);
349 }
350 
351 
352 static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, 
353                            struct frag_hdr *fhdr, u8 *nhptr)
354 {
355         struct sk_buff *prev, *next;
356         int offset, end;
357 
358         if (fq->last_in & COMPLETE)
359                 goto err;
360 
361         offset = ntohs(fhdr->frag_off) & ~0x7;
362         end = offset + (ntohs(skb->nh.ipv6h->payload_len) -
363                         ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
364 
365         if ((unsigned int)end >= 65536) {
366                 icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off); 
367                 goto err;
368         }
369 
370         /* Is this the final fragment? */
371         if (!(fhdr->frag_off & __constant_htons(0x0001))) {
372                 /* If we already have some bits beyond end
373                  * or have different end, the segment is corrupted.
374                  */
375                 if (end < fq->len ||
376                     ((fq->last_in & LAST_IN) && end != fq->len))
377                         goto err;
378                 fq->last_in |= LAST_IN;
379                 fq->len = end;
380         } else {
381                 /* Check if the fragment is rounded to 8 bytes.
382                  * Required by the RFC.
383                  */
384                 if (end & 0x7) {
385                         printk(KERN_DEBUG "fragment not rounded to 8bytes\n");
386 
387                         /*
388                            It is not in specs, but I see no reasons
389                            to send an error in this case. --ANK
390                          */
391                         if (offset == 0)
392                                 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 
393                                                   &skb->nh.ipv6h->payload_len);
394                         goto err;
395                 }
396                 if (end > fq->len) {
397                         /* Some bits beyond end -> corruption. */
398                         if (fq->last_in & LAST_IN)
399                                 goto err;
400                         fq->len = end;
401                 }
402         }
403 
404         if (end == offset)
405                 goto err;
406 
407         /* Point into the IP datagram 'data' part. */
408         skb_pull(skb, (u8 *) (fhdr + 1) - skb->data);
409         skb_trim(skb, end - offset);
410 
411         /* Find out which fragments are in front and at the back of us
412          * in the chain of fragments so far.  We must know where to put
413          * this fragment, right?
414          */
415         prev = NULL;
416         for(next = fq->fragments; next != NULL; next = next->next) {
417                 if (FRAG6_CB(next)->offset >= offset)
418                         break;  /* bingo! */
419                 prev = next;
420         }
421 
422         /* We found where to put this one.  Check for overlap with
423          * preceding fragment, and, if needed, align things so that
424          * any overlaps are eliminated.
425          */
426         if (prev) {
427                 int i = (FRAG6_CB(prev)->offset + prev->len) - offset;
428 
429                 if (i > 0) {
430                         offset += i;
431                         if (end <= offset)
432                                 goto err;
433                         skb_pull(skb, i);
434                 }
435         }
436 
437         /* Look for overlap with succeeding segments.
438          * If we can merge fragments, do it.
439          */
440         while (next && FRAG6_CB(next)->offset < end) {
441                 int i = end - FRAG6_CB(next)->offset; /* overlap is 'i' bytes */
442 
443                 if (i < next->len) {
444                         /* Eat head of the next overlapped fragment
445                          * and leave the loop. The next ones cannot overlap.
446                          */
447                         FRAG6_CB(next)->offset += i;    /* next fragment */
448                         skb_pull(next, i);
449                         fq->meat -= i;
450                         break;
451                 } else {
452                         struct sk_buff *free_it = next;
453 
454                         /* Old fragmnet is completely overridden with
455                          * new one drop it.
456                          */
457                         next = next->next;
458 
459                         if (prev)
460                                 prev->next = next;
461                         else
462                                 fq->fragments = next;
463 
464                         fq->meat -= free_it->len;
465                         frag_kfree_skb(free_it);
466                 }
467         }
468 
469         FRAG6_CB(skb)->offset = offset;
470 
471         /* Insert this fragment in the chain of fragments. */
472         skb->next = next;
473         if (prev)
474                 prev->next = skb;
475         else
476                 fq->fragments = skb;
477 
478         fq->iif = skb->dev->ifindex;
479         skb->dev = NULL;
480         fq->meat += skb->len;
481         atomic_add(skb->truesize, &ip6_frag_mem);
482 
483         /* First fragment.
484            nexthdr and nhptr are get from the first fragment.
485            Moreover, nexthdr is UNDEFINED for all the fragments but the
486            first one.
487            (fixed --ANK (980728))
488          */
489         if (offset == 0) {
490                 fq->nexthdr = fhdr->nexthdr;
491                 fq->nhoffset = nhptr - skb->nh.raw;
492                 fq->last_in |= FIRST_IN;
493         }
494         return;
495 
496 err:
497         kfree_skb(skb);
498 }
499 
500 /*
501  *      Check if this packet is complete.
502  *      Returns NULL on failure by any reason, and pointer
503  *      to current nexthdr field in reassembled frame.
504  *
505  *      It is called with locked fq, and caller must check that
506  *      queue is eligible for reassembly i.e. it is not COMPLETE,
507  *      the last and the first frames arrived and all the bits are here.
508  */
509 static u8 *ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
510                           struct net_device *dev)
511 {
512         struct sk_buff *fp, *head = fq->fragments;
513         struct sk_buff *skb;
514         int    payload_len;
515         int    unfrag_len;
516         int    copy;
517         u8     *nhptr;
518 
519         /* 
520          * we know the m_flag arrived and we have a queue,
521          * starting from 0, without gaps.
522          * this means we have all fragments.
523          */
524 
525         fq_kill(fq);
526 
527         BUG_TRAP(head != NULL);
528         BUG_TRAP(FRAG6_CB(head)->offset == 0);
529 
530         /* Unfragmented part is taken from the first segment. */
531         unfrag_len = head->h.raw - (u8 *) (head->nh.ipv6h + 1);
532         payload_len = unfrag_len + fq->len;
533 
534         if (payload_len > 65535)
535                 goto out_oversize;
536 
537         if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL)
538                 goto out_oom;
539 
540         copy = unfrag_len + sizeof(struct ipv6hdr);
541 
542         skb->mac.raw = skb->data;
543         skb->nh.ipv6h = (struct ipv6hdr *) skb->data;
544         skb->dev = dev;
545         skb->protocol = __constant_htons(ETH_P_IPV6);
546         skb->pkt_type = head->pkt_type;
547         FRAG6_CB(skb)->h = FRAG6_CB(head)->h;
548         skb->dst = dst_clone(head->dst);
549 
550         memcpy(skb_put(skb, copy), head->nh.ipv6h, copy);
551         nhptr = skb->nh.raw + fq->nhoffset;
552         *nhptr = fq->nexthdr;
553 
554         skb->h.raw = skb->tail;
555 
556         skb->nh.ipv6h->payload_len = ntohs(payload_len);
557 
558         *skb_in = skb;
559 
560         for (fp = fq->fragments; fp; fp=fp->next)
561                 memcpy(skb_put(skb, fp->len), fp->data, fp->len);
562 
563         IP6_INC_STATS_BH(Ip6ReasmOKs);
564         return nhptr;
565 
566 out_oversize:
567         if (net_ratelimit())
568                 printk(KERN_DEBUG "ip6_frag_reasm: payload len = %d\n", payload_len);
569         goto out_fail;
570 out_oom:
571         if (net_ratelimit())
572                 printk(KERN_DEBUG "ip6_frag_reasm: no memory for reassembly\n");
573 out_fail:
574         IP6_INC_STATS_BH(Ip6ReasmFails);
575         return NULL;
576 }
577 
578 u8* ipv6_reassembly(struct sk_buff **skbp, __u8 *nhptr)
579 {
580         struct sk_buff *skb = *skbp; 
581         struct frag_hdr *fhdr = (struct frag_hdr *) (skb->h.raw);
582         struct net_device *dev = skb->dev;
583         struct frag_queue *fq;
584         struct ipv6hdr *hdr;
585 
586         hdr = skb->nh.ipv6h;
587 
588         IP6_INC_STATS_BH(Ip6ReasmReqds);
589 
590         /* Jumbo payload inhibits frag. header */
591         if (hdr->payload_len==0) {
592                 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
593                 return NULL;
594         }
595         if ((u8 *)(fhdr+1) > skb->tail) {
596                 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
597                 return NULL;
598         }
599 
600         if (!(fhdr->frag_off & __constant_htons(0xFFF9))) {
601                 /* It is not a fragmented frame */
602                 skb->h.raw += sizeof(struct frag_hdr);
603                 IP6_INC_STATS_BH(Ip6ReasmOKs);
604 
605                 return &fhdr->nexthdr;
606         }
607 
608         if (atomic_read(&ip6_frag_mem) > sysctl_ip6frag_high_thresh)
609                 ip6_evictor();
610 
611         if ((fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr)) != NULL) {
612                 u8 *ret = NULL;
613 
614                 spin_lock(&fq->lock);
615 
616                 ip6_frag_queue(fq, skb, fhdr, nhptr);
617 
618                 if (fq->last_in == (FIRST_IN|LAST_IN) &&
619                     fq->meat == fq->len)
620                         ret = ip6_frag_reasm(fq, skbp, dev);
621 
622                 spin_unlock(&fq->lock);
623                 fq_put(fq);
624                 return ret;
625         }
626 
627         IP6_INC_STATS_BH(Ip6ReasmFails);
628         kfree_skb(skb);
629         return NULL;
630 }
631 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.