2 #include "../port/lib.h"
6 #include "../port/error.h"
13 IP6FHDR = 8, /* sizeof(Fraghdr6) */
16 #define IPV6CLASS(hdr) (((hdr)->vcf[0]&0x0F)<<2 | ((hdr)->vcf[1]&0xF0)>>2)
17 #define BLKIPVER(xp) (((Ip6hdr*)((xp)->rp))->vcf[0] & 0xF0)
19 * This sleazy macro is stolen shamelessly from ip.c, see comment there.
21 #define BKFG(xp) ((Ipfrag*)((xp)->base))
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);
31 ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
33 int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff;
34 int morefrags, blklen, rv = 0, tentative;
45 /* Fill out the ip header */
46 eh = (Ip6hdr*)(bp->rp);
48 ip->stats[OutRequests]++;
50 /* Number of uchars in data and ip header to write */
53 tentative = iptentative(f, eh->src);
55 netlog(f, Logip, "reject tx of packet with tentative src address %I\n",
61 chunk = nhgets(eh->ploadlen);
63 ip->stats[OutDiscards]++;
64 netlog(f, Logip, "short gated packet\n");
67 if(chunk + IP6HDR < len)
72 ip->stats[OutDiscards]++;
73 netlog(f, Logip, "exceeded ip max size %I\n", eh->dst);
77 r = v6lookup(f, eh->dst, c);
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);
87 if(r->type & (Rifc|Runi))
89 else if(r->type & (Rbcast|Rmulti)) {
91 sr = v6lookup(f, eh->src, nil);
92 if(sr && (sr->type & Runi))
102 eh->vcf[0] |= tos >> 4;
103 eh->vcf[1] = tos << 4;
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);
127 if(gating && ifc->reassemble <= 0) {
129 * v6 intermediate nodes are not supposed to fragment pkts;
130 * we fragment if ifc->reassemble is turned on; an exception
133 ip->stats[OutDiscards]++;
134 icmppkttoobig6(f, ifc, bp);
135 netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst);
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);
149 seglen = (medialen - (uflen + IP6FHDR)) & ~7;
151 ip->stats[FragFails]++;
152 ip->stats[OutDiscards]++;
153 netlog(f, Logip, "%I: seglen < 8\n", eh->dst);
157 lid = incref(&ip->id6);
158 fraghdr.nexthdr = nexthdr;
160 hnputl(fraghdr.id, lid);
164 while (xp && offset && offset >= BLEN(xp)) {
173 for(; fragoff < flen; fragoff += seglen) {
174 nb = allocb(uflen + IP6FHDR + seglen);
176 if(fragoff + seglen >= flen) {
177 seglen = flen - fragoff;
181 hnputs(eh->ploadlen, seglen+IP6FHDR);
182 memmove(nb->wp, eh, uflen);
185 hnputs(fraghdr.offsetRM, fragoff); /* last 3 bits must be 0 */
186 fraghdr.offsetRM[1] |= morefrags;
187 memmove(nb->wp, &fraghdr, IP6FHDR);
194 ip->stats[OutDiscards]++;
195 ip->stats[FragFails]++;
197 netlog(f, Logip, "!xp: chunk in v6%d\n", chunk);
203 memmove(nb->wp, xp->rp, blklen);
212 ifc->m->bwrite(ifc, nb, V6, gate);
213 ip->stats[FragCreates]++;
215 ip->stats[FragOKs]++;
226 ipiput6(Fs *f, Ipifc *ifc, Block *bp)
228 int hl, hop, tos, notforme, tentative;
230 uchar v6dst[IPaddrlen];
237 ip->stats[InReceives]++;
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.
250 bp = pullupblock(bp, hl);
255 h = (Ip6hdr *)bp->rp;
257 memmove(&v6dst[0], &h->dst[0], IPaddrlen);
258 notforme = ipforme(f, v6dst) == 0;
259 tentative = iptentative(f, v6dst);
261 if(tentative && h->proto != ICMPv6) {
262 print("tentative addr, drop\n");
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);
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]++;
290 /* don't forward to source's network */
291 sr = v6lookup(f, h->src, nil);
292 r = v6lookup(f, h->dst, nil);
294 if(r == nil || sr == r){
295 ip->stats[OutDiscards]++;
300 /* don't forward if packet has timed out */
303 ip->stats[InHdrErrors]++;
304 icmpttlexceeded6(f, ifc, bp);
309 /* process headers & reassemble if the interface expects it */
310 bp = procxtns(ip, bp, r->ifc->reassemble);
314 ip->stats[ForwDatagrams]++;
315 h = (Ip6hdr *)bp->rp;
318 ipoput6(f, bp, 1, hop-1, tos, nil);
322 /* reassemble & process headers if needed */
323 bp = procxtns(ip, bp, 1);
327 h = (Ip6hdr *) (bp->rp);
329 p = Fsrcvpcol(f, proto);
331 ip->stats[InDelivers]++;
332 (*p->rcv)(p, ifc, bp);
336 ip->stats[InDiscards]++;
337 ip->stats[InUnknownProtos]++;
342 * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
345 ipfragfree6(IP *ip, Fragment6 *frag)
350 freeblist(frag->blist);
352 memset(frag->src, 0, IPaddrlen);
357 for(fl = *l; fl; fl = fl->next) {
365 frag->next = ip->fragfree6;
366 ip->fragfree6 = frag;
370 * ipfragallo6 - copied from ipfragalloc4
377 while(ip->fragfree6 == nil) {
378 /* free last entry on fraglist */
379 for(f = ip->flisthead6; f->next; f = f->next)
384 ip->fragfree6 = f->next;
385 f->next = ip->flisthead6;
387 f->age = NOW + 30000;
393 procxtns(IP *ip, Block *bp, int doreasm)
399 h = (Ip6hdr *)bp->rp;
400 offset = unfraglen(bp, &proto, 0);
402 if(proto == FH && doreasm != 0) {
403 bp = ip6reassemble(ip, offset, bp, h);
406 offset = unfraglen(bp, &proto, 0);
409 if(proto == DOH || offset > IP6HDR)
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.
421 unfraglen(Block *bp, uchar *nexthdr, int setfh)
427 q = p+6; /* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */
432 while (*nexthdr == HBH || *nexthdr == RH) {
434 hs = ((int)*(p+1) + 1) * 8;
454 ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
456 int fend, offset, ovlap, len, fragsize, pktposn;
458 uchar src[IPaddrlen], dst[IPaddrlen];
459 Block *bl, **l, *last, *prev;
461 Fragment6 *f, *fnext;
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;
470 * block lists are too hard, pullupblock into a single block
473 bp = pullupblock(bp, blocklen(bp));
474 ih = (Ip6hdr *)bp->rp;
477 qlock(&ip->fraglock6);
480 * find a reassembly queue for this fragment
482 for(f = ip->flisthead6; f; f = fnext){
484 if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id)
487 ip->stats[ReasmTimeout]++;
493 * if this isn't a fragmented packet, accept it
494 * and get rid of any fragments that might go
497 if(nhgets(fraghdr->offsetRM) == 0) { /* 1st frag is also last */
500 ip->stats[ReasmFails]++;
502 qunlock(&ip->fraglock6);
506 if(bp->base+IPFRAGSZ >= bp->rp){
507 bp = padblock(bp, IPFRAGSZ);
511 BKFG(bp)->foff = offset;
512 BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR;
514 /* First fragment allocates a reassembly queue */
518 memmove(f->src, src, IPaddrlen);
519 memmove(f->dst, dst, IPaddrlen);
523 qunlock(&ip->fraglock6);
524 ip->stats[ReasmReqds]++;
529 * find the new fragment's position in the queue
534 while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
540 /* Check overlap of a previous fragment - trim away as necessary */
542 ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
544 if(ovlap >= BKFG(bp)->flen) {
546 qunlock(&ip->fraglock6);
549 BKFG(prev)->flen -= ovlap;
553 /* Link onto assembly queue */
557 /* Check to see if succeeding segments overlap */
560 fend = BKFG(bp)->foff + BKFG(bp)->flen;
562 /* Take completely covered segments out */
564 ovlap = fend - BKFG(*l)->foff;
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);
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.
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) {
592 /* get rid of frag header in first fragment */
593 memmove(bl->rp + IP6FHDR, bl->rp, uflen);
595 len = nhgets(((Ip6hdr*)bl->rp)->ploadlen) - IP6FHDR;
596 bl->wp = bl->rp + len + IP6HDR;
598 * Pullup all the fragment headers and
599 * return a complete packet
601 for(bl = bl->next; bl; bl = bl->next) {
602 fragsize = BKFG(bl)->flen;
604 bl->rp += uflen + IP6FHDR;
605 bl->wp = bl->rp + fragsize;
611 ih = (Ip6hdr*)bl->rp;
612 hnputs(ih->ploadlen, len);
613 qunlock(&ip->fraglock6);
614 ip->stats[ReasmOKs]++;
617 pktposn += BKFG(bl)->flen;
619 qunlock(&ip->fraglock6);