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