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