xref: /plan9-contrib/sys/src/9k/ip/ipv6.c (revision a3323688dee1e9a0fcc1d7d933e3ce101170952d)
19ef1f84bSDavid du Colombier #include	"u.h"
29ef1f84bSDavid du Colombier #include	"../port/lib.h"
39ef1f84bSDavid du Colombier #include	"mem.h"
49ef1f84bSDavid du Colombier #include	"dat.h"
59ef1f84bSDavid du Colombier #include	"fns.h"
69ef1f84bSDavid du Colombier #include	"../port/error.h"
79ef1f84bSDavid du Colombier 
89ef1f84bSDavid du Colombier #include	"ip.h"
99ef1f84bSDavid du Colombier #include	"ipv6.h"
109ef1f84bSDavid du Colombier 
119ef1f84bSDavid du Colombier enum
129ef1f84bSDavid du Colombier {
139ef1f84bSDavid du Colombier 	IP6FHDR		= 8,		/* sizeof(Fraghdr6) */
149ef1f84bSDavid du Colombier };
159ef1f84bSDavid du Colombier 
169ef1f84bSDavid du Colombier #define IPV6CLASS(hdr)	(((hdr)->vcf[0]&0x0F)<<2 | ((hdr)->vcf[1]&0xF0)>>2)
179ef1f84bSDavid du Colombier #define BLKIPVER(xp)	(((Ip6hdr*)((xp)->rp))->vcf[0] & 0xF0)
189ef1f84bSDavid du Colombier /*
199ef1f84bSDavid du Colombier  * This sleazy macro is stolen shamelessly from ip.c, see comment there.
209ef1f84bSDavid du Colombier  */
219ef1f84bSDavid du Colombier #define BKFG(xp)	((Ipfrag*)((xp)->base))
229ef1f84bSDavid du Colombier 
239ef1f84bSDavid du Colombier Block*		ip6reassemble(IP*, int, Block*, Ip6hdr*);
249ef1f84bSDavid du Colombier Fragment6*	ipfragallo6(IP*);
259ef1f84bSDavid du Colombier void		ipfragfree6(IP*, Fragment6*);
269ef1f84bSDavid du Colombier Block*		procopts(Block *bp);
279ef1f84bSDavid du Colombier static Block*	procxtns(IP *ip, Block *bp, int doreasm);
289ef1f84bSDavid du Colombier int		unfraglen(Block *bp, uchar *nexthdr, int setfh);
299ef1f84bSDavid du Colombier 
309ef1f84bSDavid du Colombier int
ipoput6(Fs * f,Block * bp,int gating,int ttl,int tos,Conv * c)319ef1f84bSDavid du Colombier ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
329ef1f84bSDavid du Colombier {
339ef1f84bSDavid du Colombier 	int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff;
349ef1f84bSDavid du Colombier 	int morefrags, blklen, rv = 0, tentative;
359ef1f84bSDavid du Colombier 	uchar *gate, nexthdr;
369ef1f84bSDavid du Colombier 	Block *xp, *nb;
379ef1f84bSDavid du Colombier 	Fraghdr6 fraghdr;
389ef1f84bSDavid du Colombier 	IP *ip;
399ef1f84bSDavid du Colombier 	Ip6hdr *eh;
409ef1f84bSDavid du Colombier 	Ipifc *ifc;
419ef1f84bSDavid du Colombier 	Route *r, *sr;
429ef1f84bSDavid du Colombier 
439ef1f84bSDavid du Colombier 	ip = f->ip;
449ef1f84bSDavid du Colombier 
459ef1f84bSDavid du Colombier 	/* Fill out the ip header */
469ef1f84bSDavid du Colombier 	eh = (Ip6hdr*)(bp->rp);
479ef1f84bSDavid du Colombier 
489ef1f84bSDavid du Colombier 	ip->stats[OutRequests]++;
499ef1f84bSDavid du Colombier 
509ef1f84bSDavid du Colombier 	/* Number of uchars in data and ip header to write */
519ef1f84bSDavid du Colombier 	len = blocklen(bp);
529ef1f84bSDavid du Colombier 
539ef1f84bSDavid du Colombier 	tentative = iptentative(f, eh->src);
549ef1f84bSDavid du Colombier 	if(tentative){
559ef1f84bSDavid du Colombier 		netlog(f, Logip, "reject tx of packet with tentative src address %I\n",
569ef1f84bSDavid du Colombier 			eh->src);
579ef1f84bSDavid du Colombier 		goto free;
589ef1f84bSDavid du Colombier 	}
599ef1f84bSDavid du Colombier 
609ef1f84bSDavid du Colombier 	if(gating){
619ef1f84bSDavid du Colombier 		chunk = nhgets(eh->ploadlen);
629ef1f84bSDavid du Colombier 		if(chunk > len){
639ef1f84bSDavid du Colombier 			ip->stats[OutDiscards]++;
649ef1f84bSDavid du Colombier 			netlog(f, Logip, "short gated packet\n");
659ef1f84bSDavid du Colombier 			goto free;
669ef1f84bSDavid du Colombier 		}
679ef1f84bSDavid du Colombier 		if(chunk + IP6HDR < len)
689ef1f84bSDavid du Colombier 			len = chunk + IP6HDR;
699ef1f84bSDavid du Colombier 	}
709ef1f84bSDavid du Colombier 
719ef1f84bSDavid du Colombier 	if(len >= IP_MAX){
729ef1f84bSDavid du Colombier 		ip->stats[OutDiscards]++;
739ef1f84bSDavid du Colombier 		netlog(f, Logip, "exceeded ip max size %I\n", eh->dst);
749ef1f84bSDavid du Colombier 		goto free;
759ef1f84bSDavid du Colombier 	}
769ef1f84bSDavid du Colombier 
779ef1f84bSDavid du Colombier 	r = v6lookup(f, eh->dst, c);
789ef1f84bSDavid du Colombier 	if(r == nil){
799ef1f84bSDavid du Colombier //		print("no route for %I, src %I free\n", eh->dst, eh->src);
809ef1f84bSDavid du Colombier 		ip->stats[OutNoRoutes]++;
819ef1f84bSDavid du Colombier 		netlog(f, Logip, "no interface %I\n", eh->dst);
829ef1f84bSDavid du Colombier 		rv = -1;
839ef1f84bSDavid du Colombier 		goto free;
849ef1f84bSDavid du Colombier 	}
859ef1f84bSDavid du Colombier 
869ef1f84bSDavid du Colombier 	ifc = r->ifc;
879ef1f84bSDavid du Colombier 	if(r->type & (Rifc|Runi))
889ef1f84bSDavid du Colombier 		gate = eh->dst;
899ef1f84bSDavid du Colombier 	else if(r->type & (Rbcast|Rmulti)) {
909ef1f84bSDavid du Colombier 		gate = eh->dst;
919ef1f84bSDavid du Colombier 		sr = v6lookup(f, eh->src, nil);
929ef1f84bSDavid du Colombier 		if(sr && (sr->type & Runi))
939ef1f84bSDavid du Colombier 			ifc = sr->ifc;
949ef1f84bSDavid du Colombier 	}
959ef1f84bSDavid du Colombier 	else
969ef1f84bSDavid du Colombier 		gate = r->v6.gate;
979ef1f84bSDavid du Colombier 
989ef1f84bSDavid du Colombier 	if(!gating)
999ef1f84bSDavid du Colombier 		eh->vcf[0] = IP_VER6;
1009ef1f84bSDavid du Colombier 	eh->ttl = ttl;
1019ef1f84bSDavid du Colombier 	if(!gating) {
1029ef1f84bSDavid du Colombier 		eh->vcf[0] |= tos >> 4;
1039ef1f84bSDavid du Colombier 		eh->vcf[1]  = tos << 4;
1049ef1f84bSDavid du Colombier 	}
1059ef1f84bSDavid du Colombier 
1069ef1f84bSDavid du Colombier 	if(!canrlock(ifc))
1079ef1f84bSDavid du Colombier 		goto free;
1089ef1f84bSDavid du Colombier 
1099ef1f84bSDavid du Colombier 	if(waserror()){
1109ef1f84bSDavid du Colombier 		runlock(ifc);
1119ef1f84bSDavid du Colombier 		nexterror();
1129ef1f84bSDavid du Colombier 	}
1139ef1f84bSDavid du Colombier 
1149ef1f84bSDavid du Colombier 	if(ifc->medium == nil)
1159ef1f84bSDavid du Colombier 		goto raise;
1169ef1f84bSDavid du Colombier 
1179ef1f84bSDavid du Colombier 	/* If we dont need to fragment just send it */
1189ef1f84bSDavid du Colombier 	medialen = ifc->maxtu - ifc->medium->hsize;
1199ef1f84bSDavid du Colombier 	if(len <= medialen) {
1209ef1f84bSDavid du Colombier 		hnputs(eh->ploadlen, len - IP6HDR);
1219ef1f84bSDavid du Colombier 		ifc->medium->bwrite(ifc, bp, V6, gate);
1229ef1f84bSDavid du Colombier 		runlock(ifc);
1239ef1f84bSDavid du Colombier 		poperror();
1249ef1f84bSDavid du Colombier 		return 0;
1259ef1f84bSDavid du Colombier 	}
1269ef1f84bSDavid du Colombier 
1279ef1f84bSDavid du Colombier 	if(gating && ifc->reassemble <= 0) {
1289ef1f84bSDavid du Colombier 		/*
1299ef1f84bSDavid du Colombier 		 * v6 intermediate nodes are not supposed to fragment pkts;
1309ef1f84bSDavid du Colombier 		 * we fragment if ifc->reassemble is turned on; an exception
1319ef1f84bSDavid du Colombier 		 * needed for nat.
1329ef1f84bSDavid du Colombier 		 */
1339ef1f84bSDavid du Colombier 		ip->stats[OutDiscards]++;
1349ef1f84bSDavid du Colombier 		icmppkttoobig6(f, ifc, bp);
1359ef1f84bSDavid du Colombier 		netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst);
1369ef1f84bSDavid du Colombier 		goto raise;
1379ef1f84bSDavid du Colombier 	}
1389ef1f84bSDavid du Colombier 
1399ef1f84bSDavid du Colombier 	/* start v6 fragmentation */
1409ef1f84bSDavid du Colombier 	uflen = unfraglen(bp, &nexthdr, 1);
1419ef1f84bSDavid du Colombier 	if(uflen > medialen) {
1429ef1f84bSDavid du Colombier 		ip->stats[FragFails]++;
1439ef1f84bSDavid du Colombier 		ip->stats[OutDiscards]++;
1449ef1f84bSDavid du Colombier 		netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst);
1459ef1f84bSDavid du Colombier 		goto raise;
1469ef1f84bSDavid du Colombier 	}
1479ef1f84bSDavid du Colombier 
1489ef1f84bSDavid du Colombier 	flen = len - uflen;
1499ef1f84bSDavid du Colombier 	seglen = (medialen - (uflen + IP6FHDR)) & ~7;
1509ef1f84bSDavid du Colombier 	if(seglen < 8) {
1519ef1f84bSDavid du Colombier 		ip->stats[FragFails]++;
1529ef1f84bSDavid du Colombier 		ip->stats[OutDiscards]++;
1539ef1f84bSDavid du Colombier 		netlog(f, Logip, "%I: seglen < 8\n", eh->dst);
1549ef1f84bSDavid du Colombier 		goto raise;
1559ef1f84bSDavid du Colombier 	}
1569ef1f84bSDavid du Colombier 
1579ef1f84bSDavid du Colombier 	lid = incref(&ip->id6);
1589ef1f84bSDavid du Colombier 	fraghdr.nexthdr = nexthdr;
1599ef1f84bSDavid du Colombier 	fraghdr.res = 0;
1609ef1f84bSDavid du Colombier 	hnputl(fraghdr.id, lid);
1619ef1f84bSDavid du Colombier 
1629ef1f84bSDavid du Colombier 	xp = bp;
1639ef1f84bSDavid du Colombier 	offset = uflen;
1649ef1f84bSDavid du Colombier 	while (xp && offset && offset >= BLEN(xp)) {
1659ef1f84bSDavid du Colombier 		offset -= BLEN(xp);
1669ef1f84bSDavid du Colombier 		xp = xp->next;
1679ef1f84bSDavid du Colombier 	}
1689ef1f84bSDavid du Colombier 	xp->rp += offset;
1699ef1f84bSDavid du Colombier 
1709ef1f84bSDavid du Colombier 	fragoff = 0;
1719ef1f84bSDavid du Colombier 	morefrags = 1;
1729ef1f84bSDavid du Colombier 
1739ef1f84bSDavid du Colombier 	for(; fragoff < flen; fragoff += seglen) {
1749ef1f84bSDavid du Colombier 		nb = allocb(uflen + IP6FHDR + seglen);
1759ef1f84bSDavid du Colombier 
1769ef1f84bSDavid du Colombier 		if(fragoff + seglen >= flen) {
1779ef1f84bSDavid du Colombier 			seglen = flen - fragoff;
1789ef1f84bSDavid du Colombier 			morefrags = 0;
1799ef1f84bSDavid du Colombier 		}
1809ef1f84bSDavid du Colombier 
1819ef1f84bSDavid du Colombier 		hnputs(eh->ploadlen, seglen+IP6FHDR);
1829ef1f84bSDavid du Colombier 		memmove(nb->wp, eh, uflen);
1839ef1f84bSDavid du Colombier 		nb->wp += uflen;
1849ef1f84bSDavid du Colombier 
1859ef1f84bSDavid du Colombier 		hnputs(fraghdr.offsetRM, fragoff); /* last 3 bits must be 0 */
1869ef1f84bSDavid du Colombier 		fraghdr.offsetRM[1] |= morefrags;
1879ef1f84bSDavid du Colombier 		memmove(nb->wp, &fraghdr, IP6FHDR);
1889ef1f84bSDavid du Colombier 		nb->wp += IP6FHDR;
1899ef1f84bSDavid du Colombier 
1909ef1f84bSDavid du Colombier 		/* Copy data */
1919ef1f84bSDavid du Colombier 		chunk = seglen;
1929ef1f84bSDavid du Colombier 		while (chunk) {
1939ef1f84bSDavid du Colombier 			if(!xp) {
1949ef1f84bSDavid du Colombier 				ip->stats[OutDiscards]++;
1959ef1f84bSDavid du Colombier 				ip->stats[FragFails]++;
1969ef1f84bSDavid du Colombier 				freeblist(nb);
1979ef1f84bSDavid du Colombier 				netlog(f, Logip, "!xp: chunk in v6%d\n", chunk);
1989ef1f84bSDavid du Colombier 				goto raise;
1999ef1f84bSDavid du Colombier 			}
2009ef1f84bSDavid du Colombier 			blklen = chunk;
2019ef1f84bSDavid du Colombier 			if(BLEN(xp) < chunk)
2029ef1f84bSDavid du Colombier 				blklen = BLEN(xp);
2039ef1f84bSDavid du Colombier 			memmove(nb->wp, xp->rp, blklen);
2049ef1f84bSDavid du Colombier 
2059ef1f84bSDavid du Colombier 			nb->wp += blklen;
2069ef1f84bSDavid du Colombier 			xp->rp += blklen;
2079ef1f84bSDavid du Colombier 			chunk -= blklen;
2089ef1f84bSDavid du Colombier 			if(xp->rp == xp->wp)
2099ef1f84bSDavid du Colombier 				xp = xp->next;
2109ef1f84bSDavid du Colombier 		}
2119ef1f84bSDavid du Colombier 
2129ef1f84bSDavid du Colombier 		ifc->medium->bwrite(ifc, nb, V6, gate);
2139ef1f84bSDavid du Colombier 		ip->stats[FragCreates]++;
2149ef1f84bSDavid du Colombier 	}
2159ef1f84bSDavid du Colombier 	ip->stats[FragOKs]++;
2169ef1f84bSDavid du Colombier 
2179ef1f84bSDavid du Colombier raise:
2189ef1f84bSDavid du Colombier 	runlock(ifc);
2199ef1f84bSDavid du Colombier 	poperror();
2209ef1f84bSDavid du Colombier free:
2219ef1f84bSDavid du Colombier 	freeblist(bp);
2229ef1f84bSDavid du Colombier 	return rv;
2239ef1f84bSDavid du Colombier }
2249ef1f84bSDavid du Colombier 
2259ef1f84bSDavid du Colombier void
ipiput6(Fs * f,Ipifc * ifc,Block * bp)2269ef1f84bSDavid du Colombier ipiput6(Fs *f, Ipifc *ifc, Block *bp)
2279ef1f84bSDavid du Colombier {
2289ef1f84bSDavid du Colombier 	int hl, hop, tos, notforme, tentative;
2299ef1f84bSDavid du Colombier 	uchar proto;
2309ef1f84bSDavid du Colombier 	uchar v6dst[IPaddrlen];
2319ef1f84bSDavid du Colombier 	IP *ip;
2329ef1f84bSDavid du Colombier 	Ip6hdr *h;
2339ef1f84bSDavid du Colombier 	Proto *p;
2349ef1f84bSDavid du Colombier 	Route *r, *sr;
2359ef1f84bSDavid du Colombier 
2369ef1f84bSDavid du Colombier 	ip = f->ip;
2379ef1f84bSDavid du Colombier 	ip->stats[InReceives]++;
2389ef1f84bSDavid du Colombier 
2399ef1f84bSDavid du Colombier 	/*
2409ef1f84bSDavid du Colombier 	 *  Ensure we have all the header info in the first
2419ef1f84bSDavid du Colombier 	 *  block.  Make life easier for other protocols by
2429ef1f84bSDavid du Colombier 	 *  collecting up to the first 64 bytes in the first block.
2439ef1f84bSDavid du Colombier 	 */
2449ef1f84bSDavid du Colombier 	if(BLEN(bp) < 64) {
2459ef1f84bSDavid du Colombier 		hl = blocklen(bp);
2469ef1f84bSDavid du Colombier 		if(hl < IP6HDR)
2479ef1f84bSDavid du Colombier 			hl = IP6HDR;
2489ef1f84bSDavid du Colombier 		if(hl > 64)
2499ef1f84bSDavid du Colombier 			hl = 64;
2509ef1f84bSDavid du Colombier 		bp = pullupblock(bp, hl);
2519ef1f84bSDavid du Colombier 		if(bp == nil)
2529ef1f84bSDavid du Colombier 			return;
2539ef1f84bSDavid du Colombier 	}
2549ef1f84bSDavid du Colombier 
2559ef1f84bSDavid du Colombier 	h = (Ip6hdr *)bp->rp;
2569ef1f84bSDavid du Colombier 
2579ef1f84bSDavid du Colombier 	memmove(&v6dst[0], &h->dst[0], IPaddrlen);
2589ef1f84bSDavid du Colombier 	notforme = ipforme(f, v6dst) == 0;
2599ef1f84bSDavid du Colombier 	tentative = iptentative(f, v6dst);
2609ef1f84bSDavid du Colombier 
2619ef1f84bSDavid du Colombier 	if(tentative && h->proto != ICMPv6) {
262*a3323688SDavid du Colombier 		print("ipv6 non-icmp tentative addr %I, drop\n", v6dst);
2639ef1f84bSDavid du Colombier 		freeblist(bp);
2649ef1f84bSDavid du Colombier 		return;
2659ef1f84bSDavid du Colombier 	}
2669ef1f84bSDavid du Colombier 
2679ef1f84bSDavid du Colombier 	/* Check header version */
2689ef1f84bSDavid du Colombier 	if(BLKIPVER(bp) != IP_VER6) {
2699ef1f84bSDavid du Colombier 		ip->stats[InHdrErrors]++;
2709ef1f84bSDavid du Colombier 		netlog(f, Logip, "ip: bad version %ux\n", (h->vcf[0]&0xF0)>>2);
2719ef1f84bSDavid du Colombier 		freeblist(bp);
2729ef1f84bSDavid du Colombier 		return;
2739ef1f84bSDavid du Colombier 	}
2749ef1f84bSDavid du Colombier 
2759ef1f84bSDavid du Colombier 	/* route */
2769ef1f84bSDavid du Colombier 	if(notforme) {
2779ef1f84bSDavid du Colombier 		if(!ip->iprouting){
2789ef1f84bSDavid du Colombier 			freeblist(bp);
2799ef1f84bSDavid du Colombier 			return;
2809ef1f84bSDavid du Colombier 		}
2819ef1f84bSDavid du Colombier 
2829ef1f84bSDavid du Colombier 		/* don't forward to link-local destinations */
2839ef1f84bSDavid du Colombier 		if(islinklocal(h->dst) ||
2849ef1f84bSDavid du Colombier 		   (isv6mcast(h->dst) && (h->dst[1]&0xF) <= Link_local_scop)){
2859ef1f84bSDavid du Colombier 			ip->stats[OutDiscards]++;
2869ef1f84bSDavid du Colombier 			freeblist(bp);
2879ef1f84bSDavid du Colombier 			return;
2889ef1f84bSDavid du Colombier 		}
2899ef1f84bSDavid du Colombier 
2909ef1f84bSDavid du Colombier 		/* don't forward to source's network */
2919ef1f84bSDavid du Colombier 		sr = v6lookup(f, h->src, nil);
2929ef1f84bSDavid du Colombier 		r  = v6lookup(f, h->dst, nil);
2939ef1f84bSDavid du Colombier 
2949ef1f84bSDavid du Colombier 		if(r == nil || sr == r){
2959ef1f84bSDavid du Colombier 			ip->stats[OutDiscards]++;
2969ef1f84bSDavid du Colombier 			freeblist(bp);
2979ef1f84bSDavid du Colombier 			return;
2989ef1f84bSDavid du Colombier 		}
2999ef1f84bSDavid du Colombier 
3009ef1f84bSDavid du Colombier 		/* don't forward if packet has timed out */
3019ef1f84bSDavid du Colombier 		hop = h->ttl;
3029ef1f84bSDavid du Colombier 		if(hop < 1) {
3039ef1f84bSDavid du Colombier 			ip->stats[InHdrErrors]++;
3049ef1f84bSDavid du Colombier 			icmpttlexceeded6(f, ifc, bp);
3059ef1f84bSDavid du Colombier 			freeblist(bp);
3069ef1f84bSDavid du Colombier 			return;
3079ef1f84bSDavid du Colombier 		}
3089ef1f84bSDavid du Colombier 
3099ef1f84bSDavid du Colombier 		/* process headers & reassemble if the interface expects it */
3109ef1f84bSDavid du Colombier 		bp = procxtns(ip, bp, r->ifc->reassemble);
3119ef1f84bSDavid du Colombier 		if(bp == nil)
3129ef1f84bSDavid du Colombier 			return;
3139ef1f84bSDavid du Colombier 
3149ef1f84bSDavid du Colombier 		ip->stats[ForwDatagrams]++;
3159ef1f84bSDavid du Colombier 		h = (Ip6hdr *)bp->rp;
3169ef1f84bSDavid du Colombier 		tos = IPV6CLASS(h);
3179ef1f84bSDavid du Colombier 		hop = h->ttl;
3189ef1f84bSDavid du Colombier 		ipoput6(f, bp, 1, hop-1, tos, nil);
3199ef1f84bSDavid du Colombier 		return;
3209ef1f84bSDavid du Colombier 	}
3219ef1f84bSDavid du Colombier 
3229ef1f84bSDavid du Colombier 	/* reassemble & process headers if needed */
3239ef1f84bSDavid du Colombier 	bp = procxtns(ip, bp, 1);
3249ef1f84bSDavid du Colombier 	if(bp == nil)
3259ef1f84bSDavid du Colombier 		return;
3269ef1f84bSDavid du Colombier 
3279ef1f84bSDavid du Colombier 	h = (Ip6hdr *) (bp->rp);
3289ef1f84bSDavid du Colombier 	proto = h->proto;
3299ef1f84bSDavid du Colombier 	p = Fsrcvpcol(f, proto);
3309ef1f84bSDavid du Colombier 	if(p && p->rcv) {
3319ef1f84bSDavid du Colombier 		ip->stats[InDelivers]++;
3329ef1f84bSDavid du Colombier 		(*p->rcv)(p, ifc, bp);
3339ef1f84bSDavid du Colombier 		return;
3349ef1f84bSDavid du Colombier 	}
3359ef1f84bSDavid du Colombier 
3369ef1f84bSDavid du Colombier 	ip->stats[InDiscards]++;
3379ef1f84bSDavid du Colombier 	ip->stats[InUnknownProtos]++;
3389ef1f84bSDavid du Colombier 	freeblist(bp);
3399ef1f84bSDavid du Colombier }
3409ef1f84bSDavid du Colombier 
3419ef1f84bSDavid du Colombier /*
3429ef1f84bSDavid du Colombier  * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
3439ef1f84bSDavid du Colombier  */
3449ef1f84bSDavid du Colombier void
ipfragfree6(IP * ip,Fragment6 * frag)3459ef1f84bSDavid du Colombier ipfragfree6(IP *ip, Fragment6 *frag)
3469ef1f84bSDavid du Colombier {
3479ef1f84bSDavid du Colombier 	Fragment6 *fl, **l;
3489ef1f84bSDavid du Colombier 
3499ef1f84bSDavid du Colombier 	if(frag->blist)
3509ef1f84bSDavid du Colombier 		freeblist(frag->blist);
3519ef1f84bSDavid du Colombier 
3529ef1f84bSDavid du Colombier 	memset(frag->src, 0, IPaddrlen);
3539ef1f84bSDavid du Colombier 	frag->id = 0;
3549ef1f84bSDavid du Colombier 	frag->blist = nil;
3559ef1f84bSDavid du Colombier 
3569ef1f84bSDavid du Colombier 	l = &ip->flisthead6;
3579ef1f84bSDavid du Colombier 	for(fl = *l; fl; fl = fl->next) {
3589ef1f84bSDavid du Colombier 		if(fl == frag) {
3599ef1f84bSDavid du Colombier 			*l = frag->next;
3609ef1f84bSDavid du Colombier 			break;
3619ef1f84bSDavid du Colombier 		}
3629ef1f84bSDavid du Colombier 		l = &fl->next;
3639ef1f84bSDavid du Colombier 	}
3649ef1f84bSDavid du Colombier 
3659ef1f84bSDavid du Colombier 	frag->next = ip->fragfree6;
3669ef1f84bSDavid du Colombier 	ip->fragfree6 = frag;
3679ef1f84bSDavid du Colombier }
3689ef1f84bSDavid du Colombier 
3699ef1f84bSDavid du Colombier /*
3709ef1f84bSDavid du Colombier  * ipfragallo6 - copied from ipfragalloc4
3719ef1f84bSDavid du Colombier  */
3729ef1f84bSDavid du Colombier Fragment6*
ipfragallo6(IP * ip)3739ef1f84bSDavid du Colombier ipfragallo6(IP *ip)
3749ef1f84bSDavid du Colombier {
3759ef1f84bSDavid du Colombier 	Fragment6 *f;
3769ef1f84bSDavid du Colombier 
3779ef1f84bSDavid du Colombier 	while(ip->fragfree6 == nil) {
3789ef1f84bSDavid du Colombier 		/* free last entry on fraglist */
3799ef1f84bSDavid du Colombier 		for(f = ip->flisthead6; f->next; f = f->next)
3809ef1f84bSDavid du Colombier 			;
3819ef1f84bSDavid du Colombier 		ipfragfree6(ip, f);
3829ef1f84bSDavid du Colombier 	}
3839ef1f84bSDavid du Colombier 	f = ip->fragfree6;
3849ef1f84bSDavid du Colombier 	ip->fragfree6 = f->next;
3859ef1f84bSDavid du Colombier 	f->next = ip->flisthead6;
3869ef1f84bSDavid du Colombier 	ip->flisthead6 = f;
3879ef1f84bSDavid du Colombier 	f->age = NOW + 30000;
3889ef1f84bSDavid du Colombier 
3899ef1f84bSDavid du Colombier 	return f;
3909ef1f84bSDavid du Colombier }
3919ef1f84bSDavid du Colombier 
3929ef1f84bSDavid du Colombier static Block*
procxtns(IP * ip,Block * bp,int doreasm)3939ef1f84bSDavid du Colombier procxtns(IP *ip, Block *bp, int doreasm)
3949ef1f84bSDavid du Colombier {
3959ef1f84bSDavid du Colombier 	int offset;
3969ef1f84bSDavid du Colombier 	uchar proto;
3979ef1f84bSDavid du Colombier 	Ip6hdr *h;
3989ef1f84bSDavid du Colombier 
3999ef1f84bSDavid du Colombier 	h = (Ip6hdr *)bp->rp;
4009ef1f84bSDavid du Colombier 	offset = unfraglen(bp, &proto, 0);
4019ef1f84bSDavid du Colombier 
4029ef1f84bSDavid du Colombier 	if(proto == FH && doreasm != 0) {
4039ef1f84bSDavid du Colombier 		bp = ip6reassemble(ip, offset, bp, h);
4049ef1f84bSDavid du Colombier 		if(bp == nil)
4059ef1f84bSDavid du Colombier 			return nil;
4069ef1f84bSDavid du Colombier 		offset = unfraglen(bp, &proto, 0);
4079ef1f84bSDavid du Colombier 	}
4089ef1f84bSDavid du Colombier 
4099ef1f84bSDavid du Colombier 	if(proto == DOH || offset > IP6HDR)
4109ef1f84bSDavid du Colombier 		bp = procopts(bp);
4119ef1f84bSDavid du Colombier 	return bp;
4129ef1f84bSDavid du Colombier }
4139ef1f84bSDavid du Colombier 
4149ef1f84bSDavid du Colombier /*
4159ef1f84bSDavid du Colombier  * returns length of "Unfragmentable part", i.e., sum of lengths of ipv6 hdr,
4169ef1f84bSDavid du Colombier  * hop-by-hop & routing headers if present; *nexthdr is set to nexthdr value
4179ef1f84bSDavid du Colombier  * of the last header in the "Unfragmentable part"; if setfh != 0, nexthdr
4189ef1f84bSDavid du Colombier  * field of the last header in the "Unfragmentable part" is set to FH.
4199ef1f84bSDavid du Colombier  */
4209ef1f84bSDavid du Colombier int
unfraglen(Block * bp,uchar * nexthdr,int setfh)4219ef1f84bSDavid du Colombier unfraglen(Block *bp, uchar *nexthdr, int setfh)
4229ef1f84bSDavid du Colombier {
4239ef1f84bSDavid du Colombier 	uchar *p, *q;
4249ef1f84bSDavid du Colombier 	int ufl, hs;
4259ef1f84bSDavid du Colombier 
4269ef1f84bSDavid du Colombier 	p = bp->rp;
4279ef1f84bSDavid du Colombier 	q = p+6;   /* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */
4289ef1f84bSDavid du Colombier 	*nexthdr = *q;
4299ef1f84bSDavid du Colombier 	ufl = IP6HDR;
4309ef1f84bSDavid du Colombier 	p += ufl;
4319ef1f84bSDavid du Colombier 
4329ef1f84bSDavid du Colombier 	while (*nexthdr == HBH || *nexthdr == RH) {
4339ef1f84bSDavid du Colombier 		*nexthdr = *p;
4349ef1f84bSDavid du Colombier 		hs = ((int)*(p+1) + 1) * 8;
4359ef1f84bSDavid du Colombier 		ufl += hs;
4369ef1f84bSDavid du Colombier 		q = p;
4379ef1f84bSDavid du Colombier 		p += hs;
4389ef1f84bSDavid du Colombier 	}
4399ef1f84bSDavid du Colombier 
4409ef1f84bSDavid du Colombier 	if(*nexthdr == FH)
4419ef1f84bSDavid du Colombier 		*q = *p;
4429ef1f84bSDavid du Colombier 	if(setfh)
4439ef1f84bSDavid du Colombier 		*q = FH;
4449ef1f84bSDavid du Colombier 	return ufl;
4459ef1f84bSDavid du Colombier }
4469ef1f84bSDavid du Colombier 
4479ef1f84bSDavid du Colombier Block*
procopts(Block * bp)4489ef1f84bSDavid du Colombier procopts(Block *bp)
4499ef1f84bSDavid du Colombier {
4509ef1f84bSDavid du Colombier 	return bp;
4519ef1f84bSDavid du Colombier }
4529ef1f84bSDavid du Colombier 
4539ef1f84bSDavid du Colombier Block*
ip6reassemble(IP * ip,int uflen,Block * bp,Ip6hdr * ih)4549ef1f84bSDavid du Colombier ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
4559ef1f84bSDavid du Colombier {
4569ef1f84bSDavid du Colombier 	int fend, offset, ovlap, len, fragsize, pktposn;
4579ef1f84bSDavid du Colombier 	uint id;
4589ef1f84bSDavid du Colombier 	uchar src[IPaddrlen], dst[IPaddrlen];
4599ef1f84bSDavid du Colombier 	Block *bl, **l, *last, *prev;
4609ef1f84bSDavid du Colombier 	Fraghdr6 *fraghdr;
4619ef1f84bSDavid du Colombier 	Fragment6 *f, *fnext;
4629ef1f84bSDavid du Colombier 
4639ef1f84bSDavid du Colombier 	fraghdr = (Fraghdr6 *)(bp->rp + uflen);
4649ef1f84bSDavid du Colombier 	memmove(src, ih->src, IPaddrlen);
4659ef1f84bSDavid du Colombier 	memmove(dst, ih->dst, IPaddrlen);
4669ef1f84bSDavid du Colombier 	id = nhgetl(fraghdr->id);
4679ef1f84bSDavid du Colombier 	offset = nhgets(fraghdr->offsetRM) & ~7;
4689ef1f84bSDavid du Colombier 
4699ef1f84bSDavid du Colombier 	/*
4709ef1f84bSDavid du Colombier 	 *  block lists are too hard, pullupblock into a single block
4719ef1f84bSDavid du Colombier 	 */
4729ef1f84bSDavid du Colombier 	if(bp->next){
4739ef1f84bSDavid du Colombier 		bp = pullupblock(bp, blocklen(bp));
4749ef1f84bSDavid du Colombier 		ih = (Ip6hdr *)bp->rp;
4759ef1f84bSDavid du Colombier 	}
4769ef1f84bSDavid du Colombier 
4779ef1f84bSDavid du Colombier 	qlock(&ip->fraglock6);
4789ef1f84bSDavid du Colombier 
4799ef1f84bSDavid du Colombier 	/*
4809ef1f84bSDavid du Colombier 	 *  find a reassembly queue for this fragment
4819ef1f84bSDavid du Colombier 	 */
4829ef1f84bSDavid du Colombier 	for(f = ip->flisthead6; f; f = fnext){
4839ef1f84bSDavid du Colombier 		fnext = f->next;
4849ef1f84bSDavid du Colombier 		if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id)
4859ef1f84bSDavid du Colombier 			break;
4869ef1f84bSDavid du Colombier 		if(f->age < NOW){
4879ef1f84bSDavid du Colombier 			ip->stats[ReasmTimeout]++;
4889ef1f84bSDavid du Colombier 			ipfragfree6(ip, f);
4899ef1f84bSDavid du Colombier 		}
4909ef1f84bSDavid du Colombier 	}
4919ef1f84bSDavid du Colombier 
4929ef1f84bSDavid du Colombier 	/*
4939ef1f84bSDavid du Colombier 	 *  if this isn't a fragmented packet, accept it
4949ef1f84bSDavid du Colombier 	 *  and get rid of any fragments that might go
4959ef1f84bSDavid du Colombier 	 *  with it.
4969ef1f84bSDavid du Colombier 	 */
4979ef1f84bSDavid du Colombier 	if(nhgets(fraghdr->offsetRM) == 0) {	/* 1st frag is also last */
4989ef1f84bSDavid du Colombier 		if(f) {
4999ef1f84bSDavid du Colombier 			ipfragfree6(ip, f);
5009ef1f84bSDavid du Colombier 			ip->stats[ReasmFails]++;
5019ef1f84bSDavid du Colombier 		}
5029ef1f84bSDavid du Colombier 		qunlock(&ip->fraglock6);
5039ef1f84bSDavid du Colombier 		return bp;
5049ef1f84bSDavid du Colombier 	}
5059ef1f84bSDavid du Colombier 
5069ef1f84bSDavid du Colombier 	if(bp->base+IPFRAGSZ >= bp->rp){
5079ef1f84bSDavid du Colombier 		bp = padblock(bp, IPFRAGSZ);
5089ef1f84bSDavid du Colombier 		bp->rp += IPFRAGSZ;
5099ef1f84bSDavid du Colombier 	}
5109ef1f84bSDavid du Colombier 
5119ef1f84bSDavid du Colombier 	BKFG(bp)->foff = offset;
5129ef1f84bSDavid du Colombier 	BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR;
5139ef1f84bSDavid du Colombier 
5149ef1f84bSDavid du Colombier 	/* First fragment allocates a reassembly queue */
5159ef1f84bSDavid du Colombier 	if(f == nil) {
5169ef1f84bSDavid du Colombier 		f = ipfragallo6(ip);
5179ef1f84bSDavid du Colombier 		f->id = id;
5189ef1f84bSDavid du Colombier 		memmove(f->src, src, IPaddrlen);
5199ef1f84bSDavid du Colombier 		memmove(f->dst, dst, IPaddrlen);
5209ef1f84bSDavid du Colombier 
5219ef1f84bSDavid du Colombier 		f->blist = bp;
5229ef1f84bSDavid du Colombier 
5239ef1f84bSDavid du Colombier 		qunlock(&ip->fraglock6);
5249ef1f84bSDavid du Colombier 		ip->stats[ReasmReqds]++;
5259ef1f84bSDavid du Colombier 		return nil;
5269ef1f84bSDavid du Colombier 	}
5279ef1f84bSDavid du Colombier 
5289ef1f84bSDavid du Colombier 	/*
5299ef1f84bSDavid du Colombier 	 *  find the new fragment's position in the queue
5309ef1f84bSDavid du Colombier 	 */
5319ef1f84bSDavid du Colombier 	prev = nil;
5329ef1f84bSDavid du Colombier 	l = &f->blist;
5339ef1f84bSDavid du Colombier 	bl = f->blist;
5349ef1f84bSDavid du Colombier 	while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
5359ef1f84bSDavid du Colombier 		prev = bl;
5369ef1f84bSDavid du Colombier 		l = &bl->next;
5379ef1f84bSDavid du Colombier 		bl = bl->next;
5389ef1f84bSDavid du Colombier 	}
5399ef1f84bSDavid du Colombier 
5409ef1f84bSDavid du Colombier 	/* Check overlap of a previous fragment - trim away as necessary */
5419ef1f84bSDavid du Colombier 	if(prev) {
5429ef1f84bSDavid du Colombier 		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
5439ef1f84bSDavid du Colombier 		if(ovlap > 0) {
5449ef1f84bSDavid du Colombier 			if(ovlap >= BKFG(bp)->flen) {
5459ef1f84bSDavid du Colombier 				freeblist(bp);
5469ef1f84bSDavid du Colombier 				qunlock(&ip->fraglock6);
5479ef1f84bSDavid du Colombier 				return nil;
5489ef1f84bSDavid du Colombier 			}
5499ef1f84bSDavid du Colombier 			BKFG(prev)->flen -= ovlap;
5509ef1f84bSDavid du Colombier 		}
5519ef1f84bSDavid du Colombier 	}
5529ef1f84bSDavid du Colombier 
5539ef1f84bSDavid du Colombier 	/* Link onto assembly queue */
5549ef1f84bSDavid du Colombier 	bp->next = *l;
5559ef1f84bSDavid du Colombier 	*l = bp;
5569ef1f84bSDavid du Colombier 
5579ef1f84bSDavid du Colombier 	/* Check to see if succeeding segments overlap */
5589ef1f84bSDavid du Colombier 	if(bp->next) {
5599ef1f84bSDavid du Colombier 		l = &bp->next;
5609ef1f84bSDavid du Colombier 		fend = BKFG(bp)->foff + BKFG(bp)->flen;
5619ef1f84bSDavid du Colombier 
5629ef1f84bSDavid du Colombier 		/* Take completely covered segments out */
5639ef1f84bSDavid du Colombier 		while(*l) {
5649ef1f84bSDavid du Colombier 			ovlap = fend - BKFG(*l)->foff;
5659ef1f84bSDavid du Colombier 			if(ovlap <= 0)
5669ef1f84bSDavid du Colombier 				break;
5679ef1f84bSDavid du Colombier 			if(ovlap < BKFG(*l)->flen) {
5689ef1f84bSDavid du Colombier 				BKFG(*l)->flen -= ovlap;
5699ef1f84bSDavid du Colombier 				BKFG(*l)->foff += ovlap;
5709ef1f84bSDavid du Colombier 				/* move up ih hdrs */
5719ef1f84bSDavid du Colombier 				memmove((*l)->rp + ovlap, (*l)->rp, uflen);
5729ef1f84bSDavid du Colombier 				(*l)->rp += ovlap;
5739ef1f84bSDavid du Colombier 				break;
5749ef1f84bSDavid du Colombier 			}
5759ef1f84bSDavid du Colombier 			last = (*l)->next;
5769ef1f84bSDavid du Colombier 			(*l)->next = nil;
5779ef1f84bSDavid du Colombier 			freeblist(*l);
5789ef1f84bSDavid du Colombier 			*l = last;
5799ef1f84bSDavid du Colombier 		}
5809ef1f84bSDavid du Colombier 	}
5819ef1f84bSDavid du Colombier 
5829ef1f84bSDavid du Colombier 	/*
5839ef1f84bSDavid du Colombier 	 *  look for a complete packet.  if we get to a fragment
5849ef1f84bSDavid du Colombier 	 *  with the trailing bit of fraghdr->offsetRM[1] set, we're done.
5859ef1f84bSDavid du Colombier 	 */
5869ef1f84bSDavid du Colombier 	pktposn = 0;
5879ef1f84bSDavid du Colombier 	for(bl = f->blist; bl && BKFG(bl)->foff == pktposn; bl = bl->next) {
5889ef1f84bSDavid du Colombier 		fraghdr = (Fraghdr6 *)(bl->rp + uflen);
5899ef1f84bSDavid du Colombier 		if((fraghdr->offsetRM[1] & 1) == 0) {
5909ef1f84bSDavid du Colombier 			bl = f->blist;
5919ef1f84bSDavid du Colombier 
5929ef1f84bSDavid du Colombier 			/* get rid of frag header in first fragment */
5939ef1f84bSDavid du Colombier 			memmove(bl->rp + IP6FHDR, bl->rp, uflen);
5949ef1f84bSDavid du Colombier 			bl->rp += IP6FHDR;
5959ef1f84bSDavid du Colombier 			len = nhgets(((Ip6hdr*)bl->rp)->ploadlen) - IP6FHDR;
5969ef1f84bSDavid du Colombier 			bl->wp = bl->rp + len + IP6HDR;
5979ef1f84bSDavid du Colombier 			/*
5989ef1f84bSDavid du Colombier 			 * Pullup all the fragment headers and
5999ef1f84bSDavid du Colombier 			 * return a complete packet
6009ef1f84bSDavid du Colombier 			 */
6019ef1f84bSDavid du Colombier 			for(bl = bl->next; bl; bl = bl->next) {
6029ef1f84bSDavid du Colombier 				fragsize = BKFG(bl)->flen;
6039ef1f84bSDavid du Colombier 				len += fragsize;
6049ef1f84bSDavid du Colombier 				bl->rp += uflen + IP6FHDR;
6059ef1f84bSDavid du Colombier 				bl->wp = bl->rp + fragsize;
6069ef1f84bSDavid du Colombier 			}
6079ef1f84bSDavid du Colombier 
6089ef1f84bSDavid du Colombier 			bl = f->blist;
6099ef1f84bSDavid du Colombier 			f->blist = nil;
6109ef1f84bSDavid du Colombier 			ipfragfree6(ip, f);
6119ef1f84bSDavid du Colombier 			ih = (Ip6hdr*)bl->rp;
6129ef1f84bSDavid du Colombier 			hnputs(ih->ploadlen, len);
6139ef1f84bSDavid du Colombier 			qunlock(&ip->fraglock6);
6149ef1f84bSDavid du Colombier 			ip->stats[ReasmOKs]++;
6159ef1f84bSDavid du Colombier 			return bl;
6169ef1f84bSDavid du Colombier 		}
6179ef1f84bSDavid du Colombier 		pktposn += BKFG(bl)->flen;
6189ef1f84bSDavid du Colombier 	}
6199ef1f84bSDavid du Colombier 	qunlock(&ip->fraglock6);
6209ef1f84bSDavid du Colombier 	return nil;
6219ef1f84bSDavid du Colombier }
622