FreeBSD/Linux Kernel Cross Reference
sys/ip/ipv6.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 #include "ip.h"
9 #include "ipv6.h"
10
11 enum
12 {
13 IP6FHDR = 8, /* sizeof(Fraghdr6) */
14 };
15
16 #define IPV6CLASS(hdr) (((hdr)->vcf[0]&0x0F)<<2 | ((hdr)->vcf[1]&0xF0)>>2)
17 #define BLKIPVER(xp) (((Ip6hdr*)((xp)->rp))->vcf[0] & 0xF0)
18 /*
19 * This sleazy macro is stolen shamelessly from ip.c, see comment there.
20 */
21 #define BKFG(xp) ((Ipfrag*)((xp)->base))
22
23 typedef struct IP IP;
24 typedef struct Fragment4 Fragment4;
25 typedef struct Fragment6 Fragment6;
26 typedef struct Ipfrag Ipfrag;
27
28 Block* ip6reassemble(IP*, int, Block*, Ip6hdr*);
29 Fragment6* ipfragallo6(IP*);
30 void ipfragfree6(IP*, Fragment6*);
31 Block* procopts(Block *bp);
32 static Block* procxtns(IP *ip, Block *bp, int doreasm);
33 int unfraglen(Block *bp, uchar *nexthdr, int setfh);
34
35 /* MIB II counters */
36 enum
37 {
38 Forwarding,
39 DefaultTTL,
40 InReceives,
41 InHdrErrors,
42 InAddrErrors,
43 ForwDatagrams,
44 InUnknownProtos,
45 InDiscards,
46 InDelivers,
47 OutRequests,
48 OutDiscards,
49 OutNoRoutes,
50 ReasmTimeout,
51 ReasmReqds,
52 ReasmOKs,
53 ReasmFails,
54 FragOKs,
55 FragFails,
56 FragCreates,
57
58 Nstats,
59 };
60
61 static char *statnames[] =
62 {
63 [Forwarding] "Forwarding",
64 [DefaultTTL] "DefaultTTL",
65 [InReceives] "InReceives",
66 [InHdrErrors] "InHdrErrors",
67 [InAddrErrors] "InAddrErrors",
68 [ForwDatagrams] "ForwDatagrams",
69 [InUnknownProtos] "InUnknownProtos",
70 [InDiscards] "InDiscards",
71 [InDelivers] "InDelivers",
72 [OutRequests] "OutRequests",
73 [OutDiscards] "OutDiscards",
74 [OutNoRoutes] "OutNoRoutes",
75 [ReasmTimeout] "ReasmTimeout",
76 [ReasmReqds] "ReasmReqds",
77 [ReasmOKs] "ReasmOKs",
78 [ReasmFails] "ReasmFails",
79 [FragOKs] "FragOKs",
80 [FragFails] "FragFails",
81 [FragCreates] "FragCreates",
82 };
83
84 struct Fragment4
85 {
86 Block* blist;
87 Fragment4* next;
88 ulong src;
89 ulong dst;
90 ushort id;
91 ulong age;
92 };
93
94 struct Fragment6
95 {
96 Block* blist;
97 Fragment6* next;
98 uchar src[IPaddrlen];
99 uchar dst[IPaddrlen];
100 uint id;
101 ulong age;
102 };
103
104 struct Ipfrag
105 {
106 ushort foff;
107 ushort flen;
108 };
109
110 /* an instance of IP */
111 struct IP
112 {
113 ulong stats[Nstats];
114
115 QLock fraglock4;
116 Fragment4* flisthead4;
117 Fragment4* fragfree4;
118 Ref id4;
119
120 QLock fraglock6;
121 Fragment6* flisthead6;
122 Fragment6* fragfree6;
123 Ref id6;
124
125 int iprouting; /* true if we route like a gateway */
126 };
127
128 int
129 ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
130 {
131 int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff;
132 int morefrags, blklen, rv = 0, tentative;
133 uchar *gate, nexthdr;
134 Block *xp, *nb;
135 Fraghdr6 fraghdr;
136 IP *ip;
137 Ip6hdr *eh;
138 Ipifc *ifc;
139 Route *r, *sr;
140
141 ip = f->ip;
142
143 /* Fill out the ip header */
144 eh = (Ip6hdr*)(bp->rp);
145
146 ip->stats[OutRequests]++;
147
148 /* Number of uchars in data and ip header to write */
149 len = blocklen(bp);
150
151 tentative = iptentative(f, eh->src);
152 if(tentative){
153 netlog(f, Logip, "reject tx of packet with tentative src address %I\n",
154 eh->src);
155 goto free;
156 }
157
158 if(gating){
159 chunk = nhgets(eh->ploadlen);
160 if(chunk > len){
161 ip->stats[OutDiscards]++;
162 netlog(f, Logip, "short gated packet\n");
163 goto free;
164 }
165 if(chunk + IP6HDR < len)
166 len = chunk + IP6HDR;
167 }
168
169 if(len >= IP_MAX){
170 ip->stats[OutDiscards]++;
171 netlog(f, Logip, "exceeded ip max size %I\n", eh->dst);
172 goto free;
173 }
174
175 r = v6lookup(f, eh->dst, c);
176 if(r == nil){
177 // print("no route for %I, src %I free\n", eh->dst, eh->src);
178 ip->stats[OutNoRoutes]++;
179 netlog(f, Logip, "no interface %I\n", eh->dst);
180 rv = -1;
181 goto free;
182 }
183
184 ifc = r->ifc;
185 if(r->type & (Rifc|Runi))
186 gate = eh->dst;
187 else if(r->type & (Rbcast|Rmulti)) {
188 gate = eh->dst;
189 sr = v6lookup(f, eh->src, nil);
190 if(sr && (sr->type & Runi))
191 ifc = sr->ifc;
192 }
193 else
194 gate = r->v6.gate;
195
196 if(!gating)
197 eh->vcf[0] = IP_VER6;
198 eh->ttl = ttl;
199 if(!gating) {
200 eh->vcf[0] |= tos >> 4;
201 eh->vcf[1] = tos << 4;
202 }
203
204 if(!canrlock(ifc))
205 goto free;
206
207 if(waserror()){
208 runlock(ifc);
209 nexterror();
210 }
211
212 if(ifc->m == nil)
213 goto raise;
214
215 /* If we dont need to fragment just send it */
216 medialen = ifc->maxtu - ifc->m->hsize;
217 if(len <= medialen) {
218 hnputs(eh->ploadlen, len - IP6HDR);
219 ifc->m->bwrite(ifc, bp, V6, gate);
220 runlock(ifc);
221 poperror();
222 return 0;
223 }
224
225 if(gating && ifc->reassemble <= 0) {
226 /*
227 * v6 intermediate nodes are not supposed to fragment pkts;
228 * we fragment if ifc->reassemble is turned on; an exception
229 * needed for nat.
230 */
231 ip->stats[OutDiscards]++;
232 icmppkttoobig6(f, ifc, bp);
233 netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst);
234 goto raise;
235 }
236
237 /* start v6 fragmentation */
238 uflen = unfraglen(bp, &nexthdr, 1);
239 if(uflen > medialen) {
240 ip->stats[FragFails]++;
241 ip->stats[OutDiscards]++;
242 netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst);
243 goto raise;
244 }
245
246 flen = len - uflen;
247 seglen = (medialen - (uflen + IP6FHDR)) & ~7;
248 if(seglen < 8) {
249 ip->stats[FragFails]++;
250 ip->stats[OutDiscards]++;
251 netlog(f, Logip, "%I: seglen < 8\n", eh->dst);
252 goto raise;
253 }
254
255 lid = incref(&ip->id6);
256 fraghdr.nexthdr = nexthdr;
257 fraghdr.res = 0;
258 hnputl(fraghdr.id, lid);
259
260 xp = bp;
261 offset = uflen;
262 while (xp && offset && offset >= BLEN(xp)) {
263 offset -= BLEN(xp);
264 xp = xp->next;
265 }
266 xp->rp += offset;
267
268 fragoff = 0;
269 morefrags = 1;
270
271 for(; fragoff < flen; fragoff += seglen) {
272 nb = allocb(uflen + IP6FHDR + seglen);
273
274 if(fragoff + seglen >= flen) {
275 seglen = flen - fragoff;
276 morefrags = 0;
277 }
278
279 hnputs(eh->ploadlen, seglen+IP6FHDR);
280 memmove(nb->wp, eh, uflen);
281 nb->wp += uflen;
282
283 hnputs(fraghdr.offsetRM, fragoff); /* last 3 bits must be 0 */
284 fraghdr.offsetRM[1] |= morefrags;
285 memmove(nb->wp, &fraghdr, IP6FHDR);
286 nb->wp += IP6FHDR;
287
288 /* Copy data */
289 chunk = seglen;
290 while (chunk) {
291 if(!xp) {
292 ip->stats[OutDiscards]++;
293 ip->stats[FragFails]++;
294 freeblist(nb);
295 netlog(f, Logip, "!xp: chunk in v6%d\n", chunk);
296 goto raise;
297 }
298 blklen = chunk;
299 if(BLEN(xp) < chunk)
300 blklen = BLEN(xp);
301 memmove(nb->wp, xp->rp, blklen);
302
303 nb->wp += blklen;
304 xp->rp += blklen;
305 chunk -= blklen;
306 if(xp->rp == xp->wp)
307 xp = xp->next;
308 }
309
310 ifc->m->bwrite(ifc, nb, V6, gate);
311 ip->stats[FragCreates]++;
312 }
313 ip->stats[FragOKs]++;
314
315 raise:
316 runlock(ifc);
317 poperror();
318 free:
319 freeblist(bp);
320 return rv;
321 }
322
323 void
324 ipiput6(Fs *f, Ipifc *ifc, Block *bp)
325 {
326 int hl, hop, tos, notforme, tentative;
327 uchar proto;
328 uchar v6dst[IPaddrlen];
329 IP *ip;
330 Ip6hdr *h;
331 Proto *p;
332 Route *r, *sr;
333
334 ip = f->ip;
335 ip->stats[InReceives]++;
336
337 /*
338 * Ensure we have all the header info in the first
339 * block. Make life easier for other protocols by
340 * collecting up to the first 64 bytes in the first block.
341 */
342 if(BLEN(bp) < 64) {
343 hl = blocklen(bp);
344 if(hl < IP6HDR)
345 hl = IP6HDR;
346 if(hl > 64)
347 hl = 64;
348 bp = pullupblock(bp, hl);
349 if(bp == nil)
350 return;
351 }
352
353 h = (Ip6hdr *)bp->rp;
354
355 memmove(&v6dst[0], &h->dst[0], IPaddrlen);
356 notforme = ipforme(f, v6dst) == 0;
357 tentative = iptentative(f, v6dst);
358
359 if(tentative && h->proto != ICMPv6) {
360 print("tentative addr, drop\n");
361 freeblist(bp);
362 return;
363 }
364
365 /* Check header version */
366 if(BLKIPVER(bp) != IP_VER6) {
367 ip->stats[InHdrErrors]++;
368 netlog(f, Logip, "ip: bad version %ux\n", (h->vcf[0]&0xF0)>>2);
369 freeblist(bp);
370 return;
371 }
372
373 /* route */
374 if(notforme) {
375 if(!ip->iprouting){
376 freeb(bp);
377 return;
378 }
379
380 /* don't forward to link-local destinations */
381 if(islinklocal(h->dst) ||
382 (isv6mcast(h->dst) && (h->dst[1]&0xF) <= Link_local_scop)){
383 ip->stats[OutDiscards]++;
384 freeblist(bp);
385 return;
386 }
387
388 /* don't forward to source's network */
389 sr = v6lookup(f, h->src, nil);
390 r = v6lookup(f, h->dst, nil);
391
392 if(r == nil || sr == r){
393 ip->stats[OutDiscards]++;
394 freeblist(bp);
395 return;
396 }
397
398 /* don't forward if packet has timed out */
399 hop = h->ttl;
400 if(hop < 1) {
401 ip->stats[InHdrErrors]++;
402 icmpttlexceeded6(f, ifc, bp);
403 freeblist(bp);
404 return;
405 }
406
407 /* process headers & reassemble if the interface expects it */
408 bp = procxtns(ip, bp, r->ifc->reassemble);
409 if(bp == nil)
410 return;
411
412 ip->stats[ForwDatagrams]++;
413 h = (Ip6hdr *)bp->rp;
414 tos = IPV6CLASS(h);
415 hop = h->ttl;
416 ipoput6(f, bp, 1, hop-1, tos, nil);
417 return;
418 }
419
420 /* reassemble & process headers if needed */
421 bp = procxtns(ip, bp, 1);
422 if(bp == nil)
423 return;
424
425 h = (Ip6hdr *) (bp->rp);
426 proto = h->proto;
427 p = Fsrcvpcol(f, proto);
428 if(p && p->rcv) {
429 ip->stats[InDelivers]++;
430 (*p->rcv)(p, ifc, bp);
431 return;
432 }
433
434 ip->stats[InDiscards]++;
435 ip->stats[InUnknownProtos]++;
436 freeblist(bp);
437 }
438
439 /*
440 * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
441 */
442 void
443 ipfragfree6(IP *ip, Fragment6 *frag)
444 {
445 Fragment6 *fl, **l;
446
447 if(frag->blist)
448 freeblist(frag->blist);
449
450 memset(frag->src, 0, IPaddrlen);
451 frag->id = 0;
452 frag->blist = nil;
453
454 l = &ip->flisthead6;
455 for(fl = *l; fl; fl = fl->next) {
456 if(fl == frag) {
457 *l = frag->next;
458 break;
459 }
460 l = &fl->next;
461 }
462
463 frag->next = ip->fragfree6;
464 ip->fragfree6 = frag;
465 }
466
467 /*
468 * ipfragallo6 - copied from ipfragalloc4
469 */
470 Fragment6*
471 ipfragallo6(IP *ip)
472 {
473 Fragment6 *f;
474
475 while(ip->fragfree6 == nil) {
476 /* free last entry on fraglist */
477 for(f = ip->flisthead6; f->next; f = f->next)
478 ;
479 ipfragfree6(ip, f);
480 }
481 f = ip->fragfree6;
482 ip->fragfree6 = f->next;
483 f->next = ip->flisthead6;
484 ip->flisthead6 = f;
485 f->age = NOW + 30000;
486
487 return f;
488 }
489
490 static Block*
491 procxtns(IP *ip, Block *bp, int doreasm)
492 {
493 int offset;
494 uchar proto;
495 Ip6hdr *h;
496
497 h = (Ip6hdr *)bp->rp;
498 offset = unfraglen(bp, &proto, 0);
499
500 if(proto == FH && doreasm != 0) {
501 bp = ip6reassemble(ip, offset, bp, h);
502 if(bp == nil)
503 return nil;
504 offset = unfraglen(bp, &proto, 0);
505 }
506
507 if(proto == DOH || offset > IP6HDR)
508 bp = procopts(bp);
509 return bp;
510 }
511
512 /*
513 * returns length of "Unfragmentable part", i.e., sum of lengths of ipv6 hdr,
514 * hop-by-hop & routing headers if present; *nexthdr is set to nexthdr value
515 * of the last header in the "Unfragmentable part"; if setfh != 0, nexthdr
516 * field of the last header in the "Unfragmentable part" is set to FH.
517 */
518 int
519 unfraglen(Block *bp, uchar *nexthdr, int setfh)
520 {
521 uchar *p, *q;
522 int ufl, hs;
523
524 p = bp->rp;
525 q = p+6; /* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */
526 *nexthdr = *q;
527 ufl = IP6HDR;
528 p += ufl;
529
530 while (*nexthdr == HBH || *nexthdr == RH) {
531 *nexthdr = *p;
532 hs = ((int)*(p+1) + 1) * 8;
533 ufl += hs;
534 q = p;
535 p += hs;
536 }
537
538 if(*nexthdr == FH)
539 *q = *p;
540 if(setfh)
541 *q = FH;
542 return ufl;
543 }
544
545 Block*
546 procopts(Block *bp)
547 {
548 return bp;
549 }
550
551 Block*
552 ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
553 {
554 int fend, offset, ovlap, len, fragsize, pktposn;
555 uint id;
556 uchar src[IPaddrlen], dst[IPaddrlen];
557 Block *bl, **l, *last, *prev;
558 Fraghdr6 *fraghdr;
559 Fragment6 *f, *fnext;
560
561 fraghdr = (Fraghdr6 *)(bp->rp + uflen);
562 memmove(src, ih->src, IPaddrlen);
563 memmove(dst, ih->dst, IPaddrlen);
564 id = nhgetl(fraghdr->id);
565 offset = nhgets(fraghdr->offsetRM) & ~7;
566
567 /*
568 * block lists are too hard, pullupblock into a single block
569 */
570 if(bp->next){
571 bp = pullupblock(bp, blocklen(bp));
572 ih = (Ip6hdr *)bp->rp;
573 }
574
575 qlock(&ip->fraglock6);
576
577 /*
578 * find a reassembly queue for this fragment
579 */
580 for(f = ip->flisthead6; f; f = fnext){
581 fnext = f->next;
582 if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id)
583 break;
584 if(f->age < NOW){
585 ip->stats[ReasmTimeout]++;
586 ipfragfree6(ip, f);
587 }
588 }
589
590 /*
591 * if this isn't a fragmented packet, accept it
592 * and get rid of any fragments that might go
593 * with it.
594 */
595 if(nhgets(fraghdr->offsetRM) == 0) { /* 1st frag is also last */
596 if(f) {
597 ipfragfree6(ip, f);
598 ip->stats[ReasmFails]++;
599 }
600 qunlock(&ip->fraglock6);
601 return bp;
602 }
603
604 if(bp->base+sizeof(Ipfrag) >= bp->rp){
605 bp = padblock(bp, sizeof(Ipfrag));
606 bp->rp += sizeof(Ipfrag);
607 }
608
609 BKFG(bp)->foff = offset;
610 BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR;
611
612 /* First fragment allocates a reassembly queue */
613 if(f == nil) {
614 f = ipfragallo6(ip);
615 f->id = id;
616 memmove(f->src, src, IPaddrlen);
617 memmove(f->dst, dst, IPaddrlen);
618
619 f->blist = bp;
620
621 qunlock(&ip->fraglock6);
622 ip->stats[ReasmReqds]++;
623 return nil;
624 }
625
626 /*
627 * find the new fragment's position in the queue
628 */
629 prev = nil;
630 l = &f->blist;
631 bl = f->blist;
632 while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
633 prev = bl;
634 l = &bl->next;
635 bl = bl->next;
636 }
637
638 /* Check overlap of a previous fragment - trim away as necessary */
639 if(prev) {
640 ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
641 if(ovlap > 0) {
642 if(ovlap >= BKFG(bp)->flen) {
643 freeblist(bp);
644 qunlock(&ip->fraglock6);
645 return nil;
646 }
647 BKFG(prev)->flen -= ovlap;
648 }
649 }
650
651 /* Link onto assembly queue */
652 bp->next = *l;
653 *l = bp;
654
655 /* Check to see if succeeding segments overlap */
656 if(bp->next) {
657 l = &bp->next;
658 fend = BKFG(bp)->foff + BKFG(bp)->flen;
659
660 /* Take completely covered segments out */
661 while(*l) {
662 ovlap = fend - BKFG(*l)->foff;
663 if(ovlap <= 0)
664 break;
665 if(ovlap < BKFG(*l)->flen) {
666 BKFG(*l)->flen -= ovlap;
667 BKFG(*l)->foff += ovlap;
668 /* move up ih hdrs */
669 memmove((*l)->rp + ovlap, (*l)->rp, uflen);
670 (*l)->rp += ovlap;
671 break;
672 }
673 last = (*l)->next;
674 (*l)->next = nil;
675 freeblist(*l);
676 *l = last;
677 }
678 }
679
680 /*
681 * look for a complete packet. if we get to a fragment
682 * with the trailing bit of fraghdr->offsetRM[1] set, we're done.
683 */
684 pktposn = 0;
685 for(bl = f->blist; bl && BKFG(bl)->foff == pktposn; bl = bl->next) {
686 fraghdr = (Fraghdr6 *)(bl->rp + uflen);
687 if((fraghdr->offsetRM[1] & 1) == 0) {
688 bl = f->blist;
689
690 /* get rid of frag header in first fragment */
691 memmove(bl->rp + IP6FHDR, bl->rp, uflen);
692 bl->rp += IP6FHDR;
693 len = nhgets(((Ip6hdr*)bl->rp)->ploadlen) - IP6FHDR;
694 bl->wp = bl->rp + len + IP6HDR;
695 /*
696 * Pullup all the fragment headers and
697 * return a complete packet
698 */
699 for(bl = bl->next; bl; bl = bl->next) {
700 fragsize = BKFG(bl)->flen;
701 len += fragsize;
702 bl->rp += uflen + IP6FHDR;
703 bl->wp = bl->rp + fragsize;
704 }
705
706 bl = f->blist;
707 f->blist = nil;
708 ipfragfree6(ip, f);
709 ih = (Ip6hdr*)bl->rp;
710 hnputs(ih->ploadlen, len);
711 qunlock(&ip->fraglock6);
712 ip->stats[ReasmOKs]++;
713 return bl;
714 }
715 pktposn += BKFG(bl)->flen;
716 }
717 qunlock(&ip->fraglock6);
718 return nil;
719 }
Cache object: 78e8c0db82cc2ff090c036c11e7a1daf
|