00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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
00062
00063
00064 struct frag_queue {
00065
00066 struct frag_queue *next;
00067 struct frag_queue *prev;
00068
00069 __u32 id;
00070 struct in6_addr saddr;
00071 struct in6_addr daddr;
00072 struct timer_list timer;
00073 struct ipv6_frag *fragments;
00074 struct device *dev;
00075 int iif;
00076 __u8 last_in;
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
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
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
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
00234
00235
00236 if (fq->last_in&FIRST_IN) {
00237 struct device *dev = dev_get_by_index(fq->iif);
00238
00239
00240
00241
00242
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
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
00300
00301
00302
00303
00304
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
00330
00331
00332
00333 if (nfp->len & 0x7) {
00334 printk(KERN_DEBUG "fragment not rounded to 8bytes\n");
00335
00336
00337
00338
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
00364 goto err;
00365 }
00366
00367 atomic_add(skb->truesize, &ip6_frag_mem);
00368
00369
00370
00371
00372
00373
00374
00375 fq->dev = skb->dev;
00376 fq->iif = skb->dev->ifindex;
00377
00378
00379 if ((fhdr->frag_off & __constant_htons(0x0001)) == 0)
00380 fq->last_in |= LAST_IN;
00381
00382
00383
00384
00385
00386
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
00405
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
00429
00430
00431
00432
00433
00434
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
00478
00479
00480
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 }