Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

reassembly.c

Go to the documentation of this file.
00001 /*
00002  *      IPv6 fragment reassembly
00003  *      Linux INET6 implementation 
00004  *
00005  *      Authors:
00006  *      Pedro Roque             <roque@di.fc.ul.pt>     
00007  *
00008  *      $Id: reassembly.c,v 1.11.2.1 2000/09/13 00:49:39 davem Exp $
00009  *
00010  *      Based on: net/ipv4/ip_fragment.c
00011  *
00012  *      This program is free software; you can redistribute it and/or
00013  *      modify it under the terms of the GNU General Public License
00014  *      as published by the Free Software Foundation; either version
00015  *      2 of the License, or (at your option) any later version.
00016  */
00017 
00018 /* 
00019  *      Fixes:  
00020  *      Andi Kleen      Make it work with multiple hosts.
00021  *                      More RFC compliance.
00022  */
00023 #include <linux/errno.h>
00024 #include <linux/types.h>
00025 #include <linux/socket.h>
00026 #include <linux/sockios.h>
00027 #include <linux/sched.h>
00028 #include <linux/net.h>
00029 #include <linux/netdevice.h>
00030 #include <linux/in6.h>
00031 #include <linux/ipv6.h>
00032 #include <linux/icmpv6.h>
00033 
00034 #include <net/sock.h>
00035 #include <net/snmp.h>
00036 
00037 #include <net/ipv6.h>
00038 #include <net/protocol.h>
00039 #include <net/transp_v6.h>
00040 #include <net/rawv6.h>
00041 #include <net/ndisc.h>
00042 #include <net/addrconf.h>
00043 
00044 int sysctl_ip6frag_high_thresh = 256*1024;
00045 int sysctl_ip6frag_low_thresh = 192*1024;
00046 int sysctl_ip6frag_time = IPV6_FRAG_TIMEOUT;
00047 
00048 atomic_t ip6_frag_mem = ATOMIC_INIT(0);
00049 
00050 struct ipv6_frag {
00051         __u16                   offset;
00052         __u16                   len;
00053         struct sk_buff          *skb;
00054 
00055         struct frag_hdr         *fhdr;
00056 
00057         struct ipv6_frag        *next;
00058 };
00059 
00060 /*
00061  *      Equivalent of ipv4 struct ipq
00062  */
00063 
00064 struct frag_queue {
00065 
00066         struct frag_queue       *next;
00067         struct frag_queue       *prev;
00068 
00069         __u32                   id;             /* fragment id          */
00070         struct in6_addr         saddr;
00071         struct in6_addr         daddr;
00072         struct timer_list       timer;          /* expire timer         */
00073         struct ipv6_frag        *fragments;
00074         struct device           *dev;
00075         int                     iif;
00076         __u8                    last_in;        /* has first/last segment arrived? */
00077 #define FIRST_IN                2
00078 #define LAST_IN                 1
00079         __u8                    nexthdr;
00080         __u16                   nhoffset;
00081 };
00082 
00083 static struct frag_queue ipv6_frag_queue = {
00084         &ipv6_frag_queue, &ipv6_frag_queue,
00085         0, {{{0}}}, {{{0}}},
00086         {0}, NULL, NULL,
00087         0, 0, 0, 0
00088 };
00089 
00090 /* Memory Tracking Functions. */
00091 extern __inline__ void frag_kfree_skb(struct sk_buff *skb)
00092 {
00093         atomic_sub(skb->truesize, &ip6_frag_mem);
00094         kfree_skb(skb);
00095 }
00096 
00097 extern __inline__ void frag_kfree_s(void *ptr, int len)
00098 {
00099         atomic_sub(len, &ip6_frag_mem);
00100         kfree(ptr);
00101 }
00102  
00103 extern __inline__ void *frag_kmalloc(int size, int pri)
00104 {
00105         void *vp = kmalloc(size, pri);
00106 
00107         if(!vp)
00108                 return NULL;
00109         atomic_add(size, &ip6_frag_mem);
00110         return vp;
00111 }
00112 
00113 
00114 static void                     create_frag_entry(struct sk_buff *skb, 
00115                                                   __u8 *nhptr,
00116                                                   struct frag_hdr *fhdr);
00117 static u8 *                     reasm_frag(struct frag_queue *fq, 
00118                                            struct sk_buff **skb_in);
00119 
00120 static void                     reasm_queue(struct frag_queue *fq, 
00121                                             struct sk_buff *skb, 
00122                                             struct frag_hdr *fhdr,
00123                                             u8 *nhptr);
00124 
00125 static void                     fq_free(struct frag_queue *fq);
00126 
00127 static void frag_prune(void)
00128 {
00129         struct frag_queue *fq;
00130 
00131         while ((fq = ipv6_frag_queue.next) != &ipv6_frag_queue) {
00132                 ipv6_statistics.Ip6ReasmFails++;
00133                 fq_free(fq);
00134                 if (atomic_read(&ip6_frag_mem) <= sysctl_ip6frag_low_thresh)
00135                         return;
00136         }
00137         if (atomic_read(&ip6_frag_mem))
00138                 printk(KERN_DEBUG "IPv6 frag_prune: memleak\n");
00139         atomic_set(&ip6_frag_mem, 0);
00140 }
00141 
00142 
00143 u8* ipv6_reassembly(struct sk_buff **skbp, __u8 *nhptr)
00144 {
00145         struct sk_buff *skb = *skbp; 
00146         struct frag_hdr *fhdr = (struct frag_hdr *) (skb->h.raw);
00147         struct frag_queue *fq;
00148         struct ipv6hdr *hdr;
00149 
00150         hdr = skb->nh.ipv6h;
00151 
00152         ipv6_statistics.Ip6ReasmReqds++;
00153 
00154         /* Jumbo payload inhibits frag. header */
00155         if (hdr->payload_len==0) {
00156                 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
00157                 return NULL;
00158         }
00159         if ((u8 *)(fhdr+1) > skb->tail) {
00160                 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
00161                 return NULL;
00162         }
00163  
00164         if (!(fhdr->frag_off & __constant_htons(0xFFF9))) {
00165                 /* It is not a fragmented frame */
00166                 skb->h.raw += sizeof(struct frag_hdr);
00167                 ipv6_statistics.Ip6ReasmOKs++;
00168 
00169                 return &fhdr->nexthdr;
00170         }
00171 
00172         if (atomic_read(&ip6_frag_mem) > sysctl_ip6frag_high_thresh)
00173                 frag_prune();
00174 
00175         for (fq = ipv6_frag_queue.next; fq != &ipv6_frag_queue; fq = fq->next) {
00176                 if (fq->id == fhdr->identification && 
00177                     !ipv6_addr_cmp(&hdr->saddr, &fq->saddr) &&
00178                     !ipv6_addr_cmp(&hdr->daddr, &fq->daddr)) {
00179 
00180                         reasm_queue(fq, skb, fhdr, nhptr);
00181 
00182                         if (fq->last_in == (FIRST_IN|LAST_IN))
00183                                 return reasm_frag(fq, skbp);
00184 
00185                         return NULL;
00186                 }
00187         }
00188 
00189         create_frag_entry(skb, nhptr, fhdr);
00190 
00191         return NULL;
00192 }
00193 
00194 
00195 static void fq_free(struct frag_queue *fq)
00196 {
00197         struct ipv6_frag *fp, *back;
00198 
00199         del_timer(&fq->timer);
00200 
00201         for (fp = fq->fragments; fp; ) {
00202                 frag_kfree_skb(fp->skb);
00203                 back = fp;
00204                 fp=fp->next;
00205                 frag_kfree_s(back, sizeof(*back));
00206         }
00207 
00208         fq->prev->next = fq->next;
00209         fq->next->prev = fq->prev;
00210 
00211         fq->prev = fq->next = NULL;
00212         
00213         frag_kfree_s(fq, sizeof(*fq));
00214 }
00215 
00216 static void frag_expire(unsigned long data)
00217 {
00218         struct frag_queue *fq;
00219         struct ipv6_frag *frag;
00220 
00221         fq = (struct frag_queue *) data;
00222 
00223         frag = fq->fragments;
00224 
00225         ipv6_statistics.Ip6ReasmTimeout++;
00226         ipv6_statistics.Ip6ReasmFails++;
00227 
00228         if (frag == NULL) {
00229                 printk(KERN_DEBUG "invalid fragment queue\n");
00230                 return;
00231         }
00232 
00233         /* Send error only if the first segment arrived.
00234            (fixed --ANK (980728))
00235          */
00236         if (fq->last_in&FIRST_IN) {
00237                 struct device *dev = dev_get_by_index(fq->iif);
00238 
00239                 /*
00240                    But use as source device on which LAST ARRIVED
00241                    segment was received. And do not use fq->dev
00242                    pointer directly, device might already disappeared.
00243                  */
00244                 if (dev) {
00245                         frag->skb->dev = dev;
00246                         icmpv6_send(frag->skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,
00247                                     dev);
00248                 }
00249         }
00250         
00251         fq_free(fq);
00252 }
00253 
00254 
00255 static void create_frag_entry(struct sk_buff *skb,
00256                               __u8 *nhptr,
00257                               struct frag_hdr *fhdr)
00258 {
00259         struct frag_queue *fq;
00260         struct ipv6hdr *hdr; 
00261 
00262         fq = (struct frag_queue *) frag_kmalloc(sizeof(struct frag_queue), 
00263                                                 GFP_ATOMIC);
00264 
00265         if (fq == NULL) {
00266                 ipv6_statistics.Ip6ReasmFails++;
00267                 kfree_skb(skb);
00268                 return;
00269         }
00270 
00271         memset(fq, 0, sizeof(struct frag_queue));
00272 
00273         fq->id = fhdr->identification;
00274 
00275         hdr = skb->nh.ipv6h;
00276         ipv6_addr_copy(&fq->saddr, &hdr->saddr);
00277         ipv6_addr_copy(&fq->daddr, &hdr->daddr);
00278 
00279         /* init_timer has been done by the memset */
00280         fq->timer.function = frag_expire;
00281         fq->timer.data = (long) fq;
00282         fq->timer.expires = jiffies + sysctl_ip6frag_time;
00283 
00284         reasm_queue(fq, skb, fhdr, nhptr);
00285 
00286         if (fq->fragments) {
00287                 fq->prev = ipv6_frag_queue.prev;
00288                 fq->next = &ipv6_frag_queue;
00289                 fq->prev->next = fq;
00290                 ipv6_frag_queue.prev = fq;
00291 
00292                 add_timer(&fq->timer);
00293         } else
00294                 frag_kfree_s(fq, sizeof(*fq));
00295 }
00296 
00297 
00298 /*
00299  *      We queue the packet even if it's the last.
00300  *      It's a trade off. This allows the reassembly 
00301  *      code to be simpler (=faster) and of the
00302  *      steps we do for queueing the only unnecessary 
00303  *      one it's the kmalloc for a struct ipv6_frag.
00304  *      Feel free to try other alternatives...
00305  */
00306 
00307 static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb, 
00308                                      struct frag_hdr *fhdr, u8 *nhptr)
00309 {
00310         struct ipv6_frag *nfp, *fp, **bptr;
00311 
00312         nfp = (struct ipv6_frag *) frag_kmalloc(sizeof(struct ipv6_frag), 
00313                                                 GFP_ATOMIC);
00314 
00315         if (nfp == NULL) {              
00316                 kfree_skb(skb);
00317                 return;
00318         }
00319 
00320         nfp->offset = ntohs(fhdr->frag_off) & ~0x7;
00321         nfp->len = (ntohs(skb->nh.ipv6h->payload_len) -
00322                     ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
00323 
00324         if ((u32)nfp->offset + (u32)nfp->len >= 65536) {
00325                 icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off); 
00326                 goto err;
00327         }
00328         if (fhdr->frag_off & __constant_htons(0x0001)) {
00329                 /* Check if the fragment is rounded to 8 bytes.
00330                  * Required by the RFC.
00331                  * ... and would break our defragmentation algorithm 8)
00332                  */
00333                 if (nfp->len & 0x7) {
00334                         printk(KERN_DEBUG "fragment not rounded to 8bytes\n");
00335 
00336                         /*
00337                            It is not in specs, but I see no reasons
00338                            to send an error in this case. --ANK
00339                          */
00340                         if (nfp->offset == 0)
00341                                 icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 
00342                                                   &skb->nh.ipv6h->payload_len);
00343                         goto err;
00344                 }
00345         }
00346 
00347         nfp->skb  = skb;
00348         nfp->fhdr = fhdr;
00349         nfp->next = NULL;
00350 
00351         bptr = &fq->fragments;
00352 
00353         for (fp = fq->fragments; fp; fp=fp->next) {
00354                 if (nfp->offset <= fp->offset)
00355                         break;
00356                 bptr = &fp->next;
00357         }
00358         if (fp && fp->offset == nfp->offset) {
00359                 if (nfp->len != fp->len) {
00360                         printk(KERN_DEBUG "reasm_queue: dup with wrong len\n");
00361                 }
00362 
00363                 /* duplicate. discard it. */
00364                 goto err;
00365         }
00366 
00367         atomic_add(skb->truesize, &ip6_frag_mem);
00368 
00369         /* All the checks are done, fragment is acepted.
00370            Only now we are allowed to update reassembly data!
00371            (fixed --ANK (980728))
00372          */
00373 
00374         /* iif always set to one of the last arrived segment */
00375         fq->dev = skb->dev;
00376         fq->iif = skb->dev->ifindex;
00377 
00378         /* Last fragment */
00379         if ((fhdr->frag_off & __constant_htons(0x0001)) == 0)
00380                 fq->last_in |= LAST_IN;
00381 
00382         /* First fragment.
00383            nexthdr and nhptr are get from the first fragment.
00384            Moreover, nexthdr is UNDEFINED for all the fragments but the
00385            first one.
00386            (fixed --ANK (980728))
00387          */
00388         if (nfp->offset == 0) {
00389                 fq->nexthdr = fhdr->nexthdr;
00390                 fq->last_in |= FIRST_IN;
00391                 fq->nhoffset = nhptr - skb->nh.raw;
00392         }
00393 
00394         *bptr = nfp;
00395         nfp->next = fp;
00396         return;
00397 
00398 err:
00399         frag_kfree_s(nfp, sizeof(*nfp));
00400         kfree_skb(skb);
00401 }
00402 
00403 /*
00404  *      check if this fragment completes the packet
00405  *      returns true on success
00406  */
00407 static u8* reasm_frag(struct frag_queue *fq, struct sk_buff **skb_in)
00408 {
00409         struct ipv6_frag *fp;
00410         struct ipv6_frag *head = fq->fragments;
00411         struct ipv6_frag *tail = NULL;
00412         struct sk_buff *skb;
00413         __u32  offset = 0;
00414         __u32  payload_len;
00415         __u16  unfrag_len;
00416         __u16  copy;
00417         u8     *nhptr;
00418 
00419         for(fp = head; fp; fp=fp->next) {
00420                 if (offset != fp->offset)
00421                         return NULL;
00422 
00423                 offset += fp->len;
00424                 tail = fp;
00425         }
00426 
00427         /* 
00428          * we know the m_flag arrived and we have a queue,
00429          * starting from 0, without gaps.
00430          * this means we have all fragments.
00431          */
00432 
00433         /* Unfragmented part is taken from the first segment.
00434            (fixed --ANK (980728))
00435          */
00436         unfrag_len = (u8 *) (head->fhdr) - (u8 *) (head->skb->nh.ipv6h + 1);
00437 
00438         payload_len = (unfrag_len + tail->offset + 
00439                        (tail->skb->tail - (__u8 *) (tail->fhdr + 1)));
00440 
00441         if (payload_len > 65535) {
00442                 if (net_ratelimit())
00443                         printk(KERN_DEBUG "reasm_frag: payload len = %d\n", payload_len);
00444                 ipv6_statistics.Ip6ReasmFails++;
00445                 fq_free(fq);
00446                 return NULL;
00447         }
00448 
00449         if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL) {
00450                 if (net_ratelimit())
00451                         printk(KERN_DEBUG "reasm_frag: no memory for reassembly\n");
00452                 ipv6_statistics.Ip6ReasmFails++;
00453                 fq_free(fq);
00454                 return NULL;
00455         }
00456 
00457         copy = unfrag_len + sizeof(struct ipv6hdr);
00458 
00459         skb->nh.ipv6h = (struct ipv6hdr *) skb->data;
00460         skb->dev = fq->dev;
00461         skb->protocol = __constant_htons(ETH_P_IPV6);
00462         skb->pkt_type = head->skb->pkt_type;
00463         memcpy(skb->cb, head->skb->cb, sizeof(skb->cb));
00464         skb->dst = dst_clone(head->skb->dst);
00465 
00466         memcpy(skb_put(skb, copy), head->skb->nh.ipv6h, copy);
00467         nhptr = skb->nh.raw + fq->nhoffset;
00468         *nhptr = fq->nexthdr;
00469 
00470         skb->h.raw = skb->tail;
00471 
00472         skb->nh.ipv6h->payload_len = ntohs(payload_len);
00473 
00474         *skb_in = skb;
00475 
00476         /*
00477          *      FIXME: If we don't have a checksum we ought to be able
00478          *      to defragment and checksum in this pass. [AC]
00479          *      Note that we don't really know yet whether the protocol
00480          *      needs checksums at all. It might still be a good idea. -AK
00481          */
00482         for(fp = fq->fragments; fp; ) {
00483                 struct ipv6_frag *back;
00484 
00485                 memcpy(skb_put(skb, fp->len), (__u8*)(fp->fhdr + 1), fp->len);
00486                 frag_kfree_skb(fp->skb);
00487                 back = fp;
00488                 fp=fp->next;
00489                 frag_kfree_s(back, sizeof(*back));
00490         }
00491 
00492         del_timer(&fq->timer);
00493         fq->prev->next = fq->next;
00494         fq->next->prev = fq->prev;
00495         fq->prev = fq->next = NULL;
00496 
00497         frag_kfree_s(fq, sizeof(*fq));
00498 
00499         ipv6_statistics.Ip6ReasmOKs++;
00500         return nhptr;
00501 }