xref: /plan9/sys/src/9/ip/ipv6.c (revision c1dd2601e42f5138f07719cdf2819a51c0f6f850)
13ff48bf5SDavid du Colombier #include	"u.h"
23ff48bf5SDavid du Colombier #include	"../port/lib.h"
33ff48bf5SDavid du Colombier #include	"mem.h"
43ff48bf5SDavid du Colombier #include	"dat.h"
53ff48bf5SDavid du Colombier #include	"fns.h"
63ff48bf5SDavid du Colombier #include	"../port/error.h"
73ff48bf5SDavid du Colombier 
83ff48bf5SDavid du Colombier #include	"ip.h"
93ff48bf5SDavid du Colombier #include	"ipv6.h"
103ff48bf5SDavid du Colombier 
113ff48bf5SDavid du Colombier enum
123ff48bf5SDavid du Colombier {
133ff48bf5SDavid du Colombier 	IP6FHDR		= 8, 		/* sizeof(Fraghdr6) */
143ff48bf5SDavid du Colombier };
153ff48bf5SDavid du Colombier 
16107aedb4SDavid du Colombier #define IPV6CLASS(hdr)	(((hdr)->vcf[0]&0x0F)<<2 | ((hdr)->vcf[1]&0xF0)>>2)
173ff48bf5SDavid du Colombier #define BLKIPVER(xp)	(((Ip6hdr*)((xp)->rp))->vcf[0] & 0xF0)
183ff48bf5SDavid du Colombier /*
193ff48bf5SDavid du Colombier  * This sleazy macro is stolen shamelessly from ip.c, see comment there.
203ff48bf5SDavid du Colombier  */
213ff48bf5SDavid du Colombier #define BKFG(xp)	((Ipfrag*)((xp)->base))
223ff48bf5SDavid du Colombier 
233ff48bf5SDavid du Colombier Block*		ip6reassemble(IP*, int, Block*, Ip6hdr*);
243ff48bf5SDavid du Colombier Fragment6*	ipfragallo6(IP*);
25107aedb4SDavid du Colombier void		ipfragfree6(IP*, Fragment6*);
26107aedb4SDavid du Colombier Block*		procopts(Block *bp);
273ff48bf5SDavid du Colombier static Block*	procxtns(IP *ip, Block *bp, int doreasm);
283ff48bf5SDavid du Colombier int		unfraglen(Block *bp, uchar *nexthdr, int setfh);
293ff48bf5SDavid du Colombier 
30e6c6b7f8SDavid du Colombier int
ipoput6(Fs * f,Block * bp,int gating,int ttl,int tos,Conv * c)31a6a9e072SDavid du Colombier ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
323ff48bf5SDavid du Colombier {
33107aedb4SDavid du Colombier 	int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff;
34107aedb4SDavid du Colombier 	int morefrags, blklen, rv = 0, tentative;
353ff48bf5SDavid du Colombier 	uchar *gate, nexthdr;
363ff48bf5SDavid du Colombier 	Block *xp, *nb;
37107aedb4SDavid du Colombier 	Fraghdr6 fraghdr;
383ff48bf5SDavid du Colombier 	IP *ip;
39107aedb4SDavid du Colombier 	Ip6hdr *eh;
40107aedb4SDavid du Colombier 	Ipifc *ifc;
41107aedb4SDavid du Colombier 	Route *r, *sr;
423ff48bf5SDavid du Colombier 
433ff48bf5SDavid du Colombier 	ip = f->ip;
443ff48bf5SDavid du Colombier 
453ff48bf5SDavid du Colombier 	/* Fill out the ip header */
463ff48bf5SDavid du Colombier 	eh = (Ip6hdr*)(bp->rp);
473ff48bf5SDavid du Colombier 
483ff48bf5SDavid du Colombier 	ip->stats[OutRequests]++;
493ff48bf5SDavid du Colombier 
503ff48bf5SDavid du Colombier 	/* Number of uchars in data and ip header to write */
513ff48bf5SDavid du Colombier 	len = blocklen(bp);
523ff48bf5SDavid du Colombier 
533ff48bf5SDavid du Colombier 	tentative = iptentative(f, eh->src);
543ff48bf5SDavid du Colombier 	if(tentative){
55ac020a8fSDavid du Colombier 		netlog(f, Logip, "reject tx of packet with tentative src address %I\n",
56ac020a8fSDavid du Colombier 			eh->src);
573ff48bf5SDavid du Colombier 		goto free;
583ff48bf5SDavid du Colombier 	}
593ff48bf5SDavid du Colombier 
603ff48bf5SDavid du Colombier 	if(gating){
613ff48bf5SDavid du Colombier 		chunk = nhgets(eh->ploadlen);
623ff48bf5SDavid du Colombier 		if(chunk > len){
633ff48bf5SDavid du Colombier 			ip->stats[OutDiscards]++;
643ff48bf5SDavid du Colombier 			netlog(f, Logip, "short gated packet\n");
653ff48bf5SDavid du Colombier 			goto free;
663ff48bf5SDavid du Colombier 		}
670774058cSDavid du Colombier 		if(chunk + IP6HDR < len)
680774058cSDavid du Colombier 			len = chunk + IP6HDR;
693ff48bf5SDavid du Colombier 	}
703ff48bf5SDavid du Colombier 
713ff48bf5SDavid du Colombier 	if(len >= IP_MAX){
723ff48bf5SDavid du Colombier 		ip->stats[OutDiscards]++;
733ff48bf5SDavid du Colombier 		netlog(f, Logip, "exceeded ip max size %I\n", eh->dst);
743ff48bf5SDavid du Colombier 		goto free;
753ff48bf5SDavid du Colombier 	}
763ff48bf5SDavid du Colombier 
77a6a9e072SDavid du Colombier 	r = v6lookup(f, eh->dst, c);
783ff48bf5SDavid du Colombier 	if(r == nil){
79ef9eff0bSDavid du Colombier //		print("no route for %I, src %I free\n", eh->dst, eh->src);
803ff48bf5SDavid du Colombier 		ip->stats[OutNoRoutes]++;
813ff48bf5SDavid du Colombier 		netlog(f, Logip, "no interface %I\n", eh->dst);
82e6c6b7f8SDavid du Colombier 		rv = -1;
833ff48bf5SDavid du Colombier 		goto free;
843ff48bf5SDavid du Colombier 	}
853ff48bf5SDavid du Colombier 
863ff48bf5SDavid du Colombier 	ifc = r->ifc;
873ff48bf5SDavid du Colombier 	if(r->type & (Rifc|Runi))
883ff48bf5SDavid du Colombier 		gate = eh->dst;
89107aedb4SDavid du Colombier 	else if(r->type & (Rbcast|Rmulti)) {
903ff48bf5SDavid du Colombier 		gate = eh->dst;
91a6a9e072SDavid du Colombier 		sr = v6lookup(f, eh->src, nil);
92107aedb4SDavid du Colombier 		if(sr && (sr->type & Runi))
933ff48bf5SDavid du Colombier 			ifc = sr->ifc;
94f2c197d9SDavid du Colombier 	}
95f2c197d9SDavid du Colombier 	else
963ff48bf5SDavid du Colombier 		gate = r->v6.gate;
973ff48bf5SDavid du Colombier 
983ff48bf5SDavid du Colombier 	if(!gating)
993ff48bf5SDavid du Colombier 		eh->vcf[0] = IP_VER6;
1003ff48bf5SDavid du Colombier 	eh->ttl = ttl;
1013ff48bf5SDavid du Colombier 	if(!gating) {
102107aedb4SDavid du Colombier 		eh->vcf[0] |= tos >> 4;
103107aedb4SDavid du Colombier 		eh->vcf[1]  = tos << 4;
1043ff48bf5SDavid du Colombier 	}
1053ff48bf5SDavid du Colombier 
106107aedb4SDavid du Colombier 	if(!canrlock(ifc))
1073ff48bf5SDavid du Colombier 		goto free;
1083ff48bf5SDavid du Colombier 
1093ff48bf5SDavid du Colombier 	if(waserror()){
1103ff48bf5SDavid du Colombier 		runlock(ifc);
1113ff48bf5SDavid du Colombier 		nexterror();
1123ff48bf5SDavid du Colombier 	}
1133ff48bf5SDavid du Colombier 
114107aedb4SDavid du Colombier 	if(ifc->m == nil)
1153ff48bf5SDavid du Colombier 		goto raise;
1163ff48bf5SDavid du Colombier 
1173ff48bf5SDavid du Colombier 	/* If we dont need to fragment just send it */
1183f695129SDavid du Colombier 	medialen = ifc->maxtu - ifc->m->hsize;
1193ff48bf5SDavid du Colombier 	if(len <= medialen) {
1200774058cSDavid du Colombier 		hnputs(eh->ploadlen, len - IP6HDR);
1213ff48bf5SDavid du Colombier 		ifc->m->bwrite(ifc, bp, V6, gate);
1223ff48bf5SDavid du Colombier 		runlock(ifc);
1233ff48bf5SDavid du Colombier 		poperror();
124e6c6b7f8SDavid du Colombier 		return 0;
1253ff48bf5SDavid du Colombier 	}
1263ff48bf5SDavid du Colombier 
127107aedb4SDavid du Colombier 	if(gating && ifc->reassemble <= 0) {
128107aedb4SDavid du Colombier 		/*
129107aedb4SDavid du Colombier 		 * v6 intermediate nodes are not supposed to fragment pkts;
130107aedb4SDavid du Colombier 		 * we fragment if ifc->reassemble is turned on; an exception
131107aedb4SDavid du Colombier 		 * needed for nat.
1323ff48bf5SDavid du Colombier 		 */
1333ff48bf5SDavid du Colombier 		ip->stats[OutDiscards]++;
1343ff48bf5SDavid du Colombier 		icmppkttoobig6(f, ifc, bp);
1353ff48bf5SDavid du Colombier 		netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst);
1363ff48bf5SDavid du Colombier 		goto raise;
1373ff48bf5SDavid du Colombier 	}
1383ff48bf5SDavid du Colombier 
1393ff48bf5SDavid du Colombier 	/* start v6 fragmentation */
1403ff48bf5SDavid du Colombier 	uflen = unfraglen(bp, &nexthdr, 1);
1413ff48bf5SDavid du Colombier 	if(uflen > medialen) {
1423ff48bf5SDavid du Colombier 		ip->stats[FragFails]++;
1433ff48bf5SDavid du Colombier 		ip->stats[OutDiscards]++;
1443ff48bf5SDavid du Colombier 		netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst);
1453ff48bf5SDavid du Colombier 		goto raise;
1463ff48bf5SDavid du Colombier 	}
1473ff48bf5SDavid du Colombier 
1483ff48bf5SDavid du Colombier 	flen = len - uflen;
1493ff48bf5SDavid du Colombier 	seglen = (medialen - (uflen + IP6FHDR)) & ~7;
1503ff48bf5SDavid du Colombier 	if(seglen < 8) {
1513ff48bf5SDavid du Colombier 		ip->stats[FragFails]++;
1523ff48bf5SDavid du Colombier 		ip->stats[OutDiscards]++;
1533ff48bf5SDavid du Colombier 		netlog(f, Logip, "%I: seglen < 8\n", eh->dst);
1543ff48bf5SDavid du Colombier 		goto raise;
1553ff48bf5SDavid du Colombier 	}
1563ff48bf5SDavid du Colombier 
1573ff48bf5SDavid du Colombier 	lid = incref(&ip->id6);
1583ff48bf5SDavid du Colombier 	fraghdr.nexthdr = nexthdr;
1593ff48bf5SDavid du Colombier 	fraghdr.res = 0;
1603ff48bf5SDavid du Colombier 	hnputl(fraghdr.id, lid);
1613ff48bf5SDavid du Colombier 
1623ff48bf5SDavid du Colombier 	xp = bp;
1633ff48bf5SDavid du Colombier 	offset = uflen;
164107aedb4SDavid du Colombier 	while (xp && offset && offset >= BLEN(xp)) {
1653ff48bf5SDavid du Colombier 		offset -= BLEN(xp);
1663ff48bf5SDavid du Colombier 		xp = xp->next;
1673ff48bf5SDavid du Colombier 	}
1683ff48bf5SDavid du Colombier 	xp->rp += offset;
1693ff48bf5SDavid du Colombier 
1703ff48bf5SDavid du Colombier 	fragoff = 0;
1713ff48bf5SDavid du Colombier 	morefrags = 1;
1723ff48bf5SDavid du Colombier 
1733ff48bf5SDavid du Colombier 	for(; fragoff < flen; fragoff += seglen) {
1743ff48bf5SDavid du Colombier 		nb = allocb(uflen + IP6FHDR + seglen);
1753ff48bf5SDavid du Colombier 
1763ff48bf5SDavid du Colombier 		if(fragoff + seglen >= flen) {
1773ff48bf5SDavid du Colombier 			seglen = flen - fragoff;
1783ff48bf5SDavid du Colombier 			morefrags = 0;
1793ff48bf5SDavid du Colombier 		}
1803ff48bf5SDavid du Colombier 
1813ff48bf5SDavid du Colombier 		hnputs(eh->ploadlen, seglen+IP6FHDR);
1823ff48bf5SDavid du Colombier 		memmove(nb->wp, eh, uflen);
1833ff48bf5SDavid du Colombier 		nb->wp += uflen;
1843ff48bf5SDavid du Colombier 
185107aedb4SDavid du Colombier 		hnputs(fraghdr.offsetRM, fragoff); /* last 3 bits must be 0 */
1863ff48bf5SDavid du Colombier 		fraghdr.offsetRM[1] |= morefrags;
1873ff48bf5SDavid du Colombier 		memmove(nb->wp, &fraghdr, IP6FHDR);
1883ff48bf5SDavid du Colombier 		nb->wp += IP6FHDR;
1893ff48bf5SDavid du Colombier 
1903ff48bf5SDavid du Colombier 		/* Copy data */
1913ff48bf5SDavid du Colombier 		chunk = seglen;
1923ff48bf5SDavid du Colombier 		while (chunk) {
1933ff48bf5SDavid du Colombier 			if(!xp) {
1943ff48bf5SDavid du Colombier 				ip->stats[OutDiscards]++;
1953ff48bf5SDavid du Colombier 				ip->stats[FragFails]++;
1963ff48bf5SDavid du Colombier 				freeblist(nb);
1973ff48bf5SDavid du Colombier 				netlog(f, Logip, "!xp: chunk in v6%d\n", chunk);
1983ff48bf5SDavid du Colombier 				goto raise;
1993ff48bf5SDavid du Colombier 			}
2003ff48bf5SDavid du Colombier 			blklen = chunk;
2013ff48bf5SDavid du Colombier 			if(BLEN(xp) < chunk)
2023ff48bf5SDavid du Colombier 				blklen = BLEN(xp);
2033ff48bf5SDavid du Colombier 			memmove(nb->wp, xp->rp, blklen);
2043ff48bf5SDavid du Colombier 
2053ff48bf5SDavid du Colombier 			nb->wp += blklen;
2063ff48bf5SDavid du Colombier 			xp->rp += blklen;
2073ff48bf5SDavid du Colombier 			chunk -= blklen;
2083ff48bf5SDavid du Colombier 			if(xp->rp == xp->wp)
2093ff48bf5SDavid du Colombier 				xp = xp->next;
2103ff48bf5SDavid du Colombier 		}
2113ff48bf5SDavid du Colombier 
2123ff48bf5SDavid du Colombier 		ifc->m->bwrite(ifc, nb, V6, gate);
2133ff48bf5SDavid du Colombier 		ip->stats[FragCreates]++;
2143ff48bf5SDavid du Colombier 	}
2153ff48bf5SDavid du Colombier 	ip->stats[FragOKs]++;
2163ff48bf5SDavid du Colombier 
2173ff48bf5SDavid du Colombier raise:
2183ff48bf5SDavid du Colombier 	runlock(ifc);
2193ff48bf5SDavid du Colombier 	poperror();
2203ff48bf5SDavid du Colombier free:
2213ff48bf5SDavid du Colombier 	freeblist(bp);
222e6c6b7f8SDavid du Colombier 	return rv;
2233ff48bf5SDavid du Colombier }
2243ff48bf5SDavid du Colombier 
2253ff48bf5SDavid du Colombier void
ipiput6(Fs * f,Ipifc * ifc,Block * bp)2263ff48bf5SDavid du Colombier ipiput6(Fs *f, Ipifc *ifc, Block *bp)
2273ff48bf5SDavid du Colombier {
228107aedb4SDavid du Colombier 	int hl, hop, tos, notforme, tentative;
2293ff48bf5SDavid du Colombier 	uchar proto;
2303ff48bf5SDavid du Colombier 	uchar v6dst[IPaddrlen];
2313ff48bf5SDavid du Colombier 	IP *ip;
232107aedb4SDavid du Colombier 	Ip6hdr *h;
233107aedb4SDavid du Colombier 	Proto *p;
2343ff48bf5SDavid du Colombier 	Route *r, *sr;
2353ff48bf5SDavid du Colombier 
2363ff48bf5SDavid du Colombier 	ip = f->ip;
2373ff48bf5SDavid du Colombier 	ip->stats[InReceives]++;
2383ff48bf5SDavid du Colombier 
2393ff48bf5SDavid du Colombier 	/*
2403ff48bf5SDavid du Colombier 	 *  Ensure we have all the header info in the first
2413ff48bf5SDavid du Colombier 	 *  block.  Make life easier for other protocols by
2423ff48bf5SDavid du Colombier 	 *  collecting up to the first 64 bytes in the first block.
2433ff48bf5SDavid du Colombier 	 */
2443ff48bf5SDavid du Colombier 	if(BLEN(bp) < 64) {
2453ff48bf5SDavid du Colombier 		hl = blocklen(bp);
2463ff48bf5SDavid du Colombier 		if(hl < IP6HDR)
2473ff48bf5SDavid du Colombier 			hl = IP6HDR;
2483ff48bf5SDavid du Colombier 		if(hl > 64)
2493ff48bf5SDavid du Colombier 			hl = 64;
2503ff48bf5SDavid du Colombier 		bp = pullupblock(bp, hl);
2513ff48bf5SDavid du Colombier 		if(bp == nil)
2523ff48bf5SDavid du Colombier 			return;
2533ff48bf5SDavid du Colombier 	}
2543ff48bf5SDavid du Colombier 
255107aedb4SDavid du Colombier 	h = (Ip6hdr *)bp->rp;
2563ff48bf5SDavid du Colombier 
257107aedb4SDavid du Colombier 	memmove(&v6dst[0], &h->dst[0], IPaddrlen);
2583ff48bf5SDavid du Colombier 	notforme = ipforme(f, v6dst) == 0;
2593ff48bf5SDavid du Colombier 	tentative = iptentative(f, v6dst);
2603ff48bf5SDavid du Colombier 
261107aedb4SDavid du Colombier 	if(tentative && h->proto != ICMPv6) {
262*c1dd2601SDavid du Colombier 		print("ipv6 non-icmp tentative addr %I, drop\n", v6dst);
2633ff48bf5SDavid du Colombier 		freeblist(bp);
2643ff48bf5SDavid du Colombier 		return;
2653ff48bf5SDavid du Colombier 	}
2663ff48bf5SDavid du Colombier 
2673ff48bf5SDavid du Colombier 	/* Check header version */
2683ff48bf5SDavid du Colombier 	if(BLKIPVER(bp) != IP_VER6) {
2693ff48bf5SDavid du Colombier 		ip->stats[InHdrErrors]++;
2703ff48bf5SDavid du Colombier 		netlog(f, Logip, "ip: bad version %ux\n", (h->vcf[0]&0xF0)>>2);
2713ff48bf5SDavid du Colombier 		freeblist(bp);
2723ff48bf5SDavid du Colombier 		return;
2733ff48bf5SDavid du Colombier 	}
2743ff48bf5SDavid du Colombier 
2753ff48bf5SDavid du Colombier 	/* route */
2763ff48bf5SDavid du Colombier 	if(notforme) {
2773ff48bf5SDavid du Colombier 		if(!ip->iprouting){
2783e5d0078SDavid du Colombier 			freeblist(bp);
2793ff48bf5SDavid du Colombier 			return;
2803ff48bf5SDavid du Colombier 		}
2817366567fSDavid du Colombier 
2827366567fSDavid du Colombier 		/* don't forward to link-local destinations */
2837366567fSDavid du Colombier 		if(islinklocal(h->dst) ||
2847366567fSDavid du Colombier 		   (isv6mcast(h->dst) && (h->dst[1]&0xF) <= Link_local_scop)){
2857366567fSDavid du Colombier 			ip->stats[OutDiscards]++;
2867366567fSDavid du Colombier 			freeblist(bp);
2877366567fSDavid du Colombier 			return;
2887366567fSDavid du Colombier 		}
2897366567fSDavid du Colombier 
2903ff48bf5SDavid du Colombier 		/* don't forward to source's network */
291a6a9e072SDavid du Colombier 		sr = v6lookup(f, h->src, nil);
292a6a9e072SDavid du Colombier 		r  = v6lookup(f, h->dst, nil);
2933ff48bf5SDavid du Colombier 
2943ff48bf5SDavid du Colombier 		if(r == nil || sr == r){
2953ff48bf5SDavid du Colombier 			ip->stats[OutDiscards]++;
2963ff48bf5SDavid du Colombier 			freeblist(bp);
2973ff48bf5SDavid du Colombier 			return;
2983ff48bf5SDavid du Colombier 		}
2993ff48bf5SDavid du Colombier 
3003ff48bf5SDavid du Colombier 		/* don't forward if packet has timed out */
3013ff48bf5SDavid du Colombier 		hop = h->ttl;
3023ff48bf5SDavid du Colombier 		if(hop < 1) {
3033ff48bf5SDavid du Colombier 			ip->stats[InHdrErrors]++;
3043ff48bf5SDavid du Colombier 			icmpttlexceeded6(f, ifc, bp);
3053ff48bf5SDavid du Colombier 			freeblist(bp);
3063ff48bf5SDavid du Colombier 			return;
3073ff48bf5SDavid du Colombier 		}
3083ff48bf5SDavid du Colombier 
3093ff48bf5SDavid du Colombier 		/* process headers & reassemble if the interface expects it */
3103ff48bf5SDavid du Colombier 		bp = procxtns(ip, bp, r->ifc->reassemble);
3113ff48bf5SDavid du Colombier 		if(bp == nil)
3123ff48bf5SDavid du Colombier 			return;
3133ff48bf5SDavid du Colombier 
3143ff48bf5SDavid du Colombier 		ip->stats[ForwDatagrams]++;
315107aedb4SDavid du Colombier 		h = (Ip6hdr *)bp->rp;
3163ff48bf5SDavid du Colombier 		tos = IPV6CLASS(h);
3173ff48bf5SDavid du Colombier 		hop = h->ttl;
318a6a9e072SDavid du Colombier 		ipoput6(f, bp, 1, hop-1, tos, nil);
3193ff48bf5SDavid du Colombier 		return;
3203ff48bf5SDavid du Colombier 	}
3213ff48bf5SDavid du Colombier 
3223ff48bf5SDavid du Colombier 	/* reassemble & process headers if needed */
3233ff48bf5SDavid du Colombier 	bp = procxtns(ip, bp, 1);
3243ff48bf5SDavid du Colombier 	if(bp == nil)
3253ff48bf5SDavid du Colombier 		return;
3263ff48bf5SDavid du Colombier 
3273ff48bf5SDavid du Colombier 	h = (Ip6hdr *) (bp->rp);
3283ff48bf5SDavid du Colombier 	proto = h->proto;
3293ff48bf5SDavid du Colombier 	p = Fsrcvpcol(f, proto);
330107aedb4SDavid du Colombier 	if(p && p->rcv) {
3313ff48bf5SDavid du Colombier 		ip->stats[InDelivers]++;
3323ff48bf5SDavid du Colombier 		(*p->rcv)(p, ifc, bp);
3333ff48bf5SDavid du Colombier 		return;
3343ff48bf5SDavid du Colombier 	}
3353ff48bf5SDavid du Colombier 
3363ff48bf5SDavid du Colombier 	ip->stats[InDiscards]++;
3373ff48bf5SDavid du Colombier 	ip->stats[InUnknownProtos]++;
3383ff48bf5SDavid du Colombier 	freeblist(bp);
3393ff48bf5SDavid du Colombier }
3403ff48bf5SDavid du Colombier 
3413ff48bf5SDavid du Colombier /*
3423ff48bf5SDavid du Colombier  * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
3433ff48bf5SDavid du Colombier  */
3443ff48bf5SDavid du Colombier void
ipfragfree6(IP * ip,Fragment6 * frag)3453ff48bf5SDavid du Colombier ipfragfree6(IP *ip, Fragment6 *frag)
3463ff48bf5SDavid du Colombier {
3473ff48bf5SDavid du Colombier 	Fragment6 *fl, **l;
3483ff48bf5SDavid du Colombier 
3493ff48bf5SDavid du Colombier 	if(frag->blist)
3503ff48bf5SDavid du Colombier 		freeblist(frag->blist);
3513ff48bf5SDavid du Colombier 
3523ff48bf5SDavid du Colombier 	memset(frag->src, 0, IPaddrlen);
3533ff48bf5SDavid du Colombier 	frag->id = 0;
3543ff48bf5SDavid du Colombier 	frag->blist = nil;
3553ff48bf5SDavid du Colombier 
3563ff48bf5SDavid du Colombier 	l = &ip->flisthead6;
3573ff48bf5SDavid du Colombier 	for(fl = *l; fl; fl = fl->next) {
3583ff48bf5SDavid du Colombier 		if(fl == frag) {
3593ff48bf5SDavid du Colombier 			*l = frag->next;
3603ff48bf5SDavid du Colombier 			break;
3613ff48bf5SDavid du Colombier 		}
3623ff48bf5SDavid du Colombier 		l = &fl->next;
3633ff48bf5SDavid du Colombier 	}
3643ff48bf5SDavid du Colombier 
3653ff48bf5SDavid du Colombier 	frag->next = ip->fragfree6;
3663ff48bf5SDavid du Colombier 	ip->fragfree6 = frag;
3673ff48bf5SDavid du Colombier }
3683ff48bf5SDavid du Colombier 
3693ff48bf5SDavid du Colombier /*
3703ff48bf5SDavid du Colombier  * ipfragallo6 - copied from ipfragalloc4
3713ff48bf5SDavid du Colombier  */
3723ff48bf5SDavid du Colombier Fragment6*
ipfragallo6(IP * ip)3733ff48bf5SDavid du Colombier ipfragallo6(IP *ip)
3743ff48bf5SDavid du Colombier {
3753ff48bf5SDavid du Colombier 	Fragment6 *f;
3763ff48bf5SDavid du Colombier 
3773ff48bf5SDavid du Colombier 	while(ip->fragfree6 == nil) {
3783ff48bf5SDavid du Colombier 		/* free last entry on fraglist */
3793ff48bf5SDavid du Colombier 		for(f = ip->flisthead6; f->next; f = f->next)
3803ff48bf5SDavid du Colombier 			;
3813ff48bf5SDavid du Colombier 		ipfragfree6(ip, f);
3823ff48bf5SDavid du Colombier 	}
3833ff48bf5SDavid du Colombier 	f = ip->fragfree6;
3843ff48bf5SDavid du Colombier 	ip->fragfree6 = f->next;
3853ff48bf5SDavid du Colombier 	f->next = ip->flisthead6;
3863ff48bf5SDavid du Colombier 	ip->flisthead6 = f;
3873ff48bf5SDavid du Colombier 	f->age = NOW + 30000;
3883ff48bf5SDavid du Colombier 
3893ff48bf5SDavid du Colombier 	return f;
3903ff48bf5SDavid du Colombier }
3913ff48bf5SDavid du Colombier 
3923ff48bf5SDavid du Colombier static Block*
procxtns(IP * ip,Block * bp,int doreasm)393107aedb4SDavid du Colombier procxtns(IP *ip, Block *bp, int doreasm)
394107aedb4SDavid du Colombier {
3953ff48bf5SDavid du Colombier 	int offset;
3963ff48bf5SDavid du Colombier 	uchar proto;
3973ff48bf5SDavid du Colombier 	Ip6hdr *h;
3983ff48bf5SDavid du Colombier 
399107aedb4SDavid du Colombier 	h = (Ip6hdr *)bp->rp;
4003ff48bf5SDavid du Colombier 	offset = unfraglen(bp, &proto, 0);
4013ff48bf5SDavid du Colombier 
402107aedb4SDavid du Colombier 	if(proto == FH && doreasm != 0) {
4033ff48bf5SDavid du Colombier 		bp = ip6reassemble(ip, offset, bp, h);
4043ff48bf5SDavid du Colombier 		if(bp == nil)
4053ff48bf5SDavid du Colombier 			return nil;
4063ff48bf5SDavid du Colombier 		offset = unfraglen(bp, &proto, 0);
4073ff48bf5SDavid du Colombier 	}
4083ff48bf5SDavid du Colombier 
4093ff48bf5SDavid du Colombier 	if(proto == DOH || offset > IP6HDR)
4103ff48bf5SDavid du Colombier 		bp = procopts(bp);
4113ff48bf5SDavid du Colombier 	return bp;
4123ff48bf5SDavid du Colombier }
4133ff48bf5SDavid du Colombier 
414107aedb4SDavid du Colombier /*
415107aedb4SDavid du Colombier  * returns length of "Unfragmentable part", i.e., sum of lengths of ipv6 hdr,
4163ff48bf5SDavid du Colombier  * hop-by-hop & routing headers if present; *nexthdr is set to nexthdr value
4173ff48bf5SDavid du Colombier  * of the last header in the "Unfragmentable part"; if setfh != 0, nexthdr
4183ff48bf5SDavid du Colombier  * field of the last header in the "Unfragmentable part" is set to FH.
4193ff48bf5SDavid du Colombier  */
4203ff48bf5SDavid du Colombier int
unfraglen(Block * bp,uchar * nexthdr,int setfh)4213ff48bf5SDavid du Colombier unfraglen(Block *bp, uchar *nexthdr, int setfh)
4223ff48bf5SDavid du Colombier {
4233ff48bf5SDavid du Colombier 	uchar *p, *q;
4243ff48bf5SDavid du Colombier 	int ufl, hs;
4253ff48bf5SDavid du Colombier 
4263ff48bf5SDavid du Colombier 	p = bp->rp;
4273ff48bf5SDavid du Colombier 	q = p+6;   /* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */
4283ff48bf5SDavid du Colombier 	*nexthdr = *q;
4293ff48bf5SDavid du Colombier 	ufl = IP6HDR;
4303ff48bf5SDavid du Colombier 	p += ufl;
4313ff48bf5SDavid du Colombier 
4320c6300e7SDavid du Colombier 	while (*nexthdr == HBH || *nexthdr == RH) {
4333ff48bf5SDavid du Colombier 		*nexthdr = *p;
4343ff48bf5SDavid du Colombier 		hs = ((int)*(p+1) + 1) * 8;
4353ff48bf5SDavid du Colombier 		ufl += hs;
4363ff48bf5SDavid du Colombier 		q = p;
4373ff48bf5SDavid du Colombier 		p += hs;
438f2c197d9SDavid du Colombier 	}
4393ff48bf5SDavid du Colombier 
4403ff48bf5SDavid du Colombier 	if(*nexthdr == FH)
4413ff48bf5SDavid du Colombier 		*q = *p;
4423ff48bf5SDavid du Colombier 	if(setfh)
4433ff48bf5SDavid du Colombier 		*q = FH;
4443ff48bf5SDavid du Colombier 	return ufl;
4453ff48bf5SDavid du Colombier }
4463ff48bf5SDavid du Colombier 
4473ff48bf5SDavid du Colombier Block*
procopts(Block * bp)4483ff48bf5SDavid du Colombier procopts(Block *bp)
4493ff48bf5SDavid du Colombier {
4503ff48bf5SDavid du Colombier 	return bp;
4513ff48bf5SDavid du Colombier }
4523ff48bf5SDavid du Colombier 
4533ff48bf5SDavid du Colombier Block*
ip6reassemble(IP * ip,int uflen,Block * bp,Ip6hdr * ih)4543ff48bf5SDavid du Colombier ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
4553ff48bf5SDavid du Colombier {
456107aedb4SDavid du Colombier 	int fend, offset, ovlap, len, fragsize, pktposn;
4573ff48bf5SDavid du Colombier 	uint id;
4583ff48bf5SDavid du Colombier 	uchar src[IPaddrlen], dst[IPaddrlen];
4593ff48bf5SDavid du Colombier 	Block *bl, **l, *last, *prev;
460107aedb4SDavid du Colombier 	Fraghdr6 *fraghdr;
461107aedb4SDavid du Colombier 	Fragment6 *f, *fnext;
4623ff48bf5SDavid du Colombier 
4633ff48bf5SDavid du Colombier 	fraghdr = (Fraghdr6 *)(bp->rp + uflen);
4643ff48bf5SDavid du Colombier 	memmove(src, ih->src, IPaddrlen);
4653ff48bf5SDavid du Colombier 	memmove(dst, ih->dst, IPaddrlen);
4663ff48bf5SDavid du Colombier 	id = nhgetl(fraghdr->id);
4673ff48bf5SDavid du Colombier 	offset = nhgets(fraghdr->offsetRM) & ~7;
4683ff48bf5SDavid du Colombier 
4693ff48bf5SDavid du Colombier 	/*
4703ff48bf5SDavid du Colombier 	 *  block lists are too hard, pullupblock into a single block
4713ff48bf5SDavid du Colombier 	 */
4723ff48bf5SDavid du Colombier 	if(bp->next){
4733ff48bf5SDavid du Colombier 		bp = pullupblock(bp, blocklen(bp));
474107aedb4SDavid du Colombier 		ih = (Ip6hdr *)bp->rp;
4753ff48bf5SDavid du Colombier 	}
4763ff48bf5SDavid du Colombier 
4773ff48bf5SDavid du Colombier 	qlock(&ip->fraglock6);
4783ff48bf5SDavid du Colombier 
4793ff48bf5SDavid du Colombier 	/*
4803ff48bf5SDavid du Colombier 	 *  find a reassembly queue for this fragment
4813ff48bf5SDavid du Colombier 	 */
4823ff48bf5SDavid du Colombier 	for(f = ip->flisthead6; f; f = fnext){
4833ff48bf5SDavid du Colombier 		fnext = f->next;
4843ff48bf5SDavid du Colombier 		if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id)
4853ff48bf5SDavid du Colombier 			break;
4863ff48bf5SDavid du Colombier 		if(f->age < NOW){
4873ff48bf5SDavid du Colombier 			ip->stats[ReasmTimeout]++;
4883ff48bf5SDavid du Colombier 			ipfragfree6(ip, f);
4893ff48bf5SDavid du Colombier 		}
4903ff48bf5SDavid du Colombier 	}
4913ff48bf5SDavid du Colombier 
4923ff48bf5SDavid du Colombier 	/*
4933ff48bf5SDavid du Colombier 	 *  if this isn't a fragmented packet, accept it
4943ff48bf5SDavid du Colombier 	 *  and get rid of any fragments that might go
4953ff48bf5SDavid du Colombier 	 *  with it.
4963ff48bf5SDavid du Colombier 	 */
497107aedb4SDavid du Colombier 	if(nhgets(fraghdr->offsetRM) == 0) {	/* 1st frag is also last */
498107aedb4SDavid du Colombier 		if(f) {
4993ff48bf5SDavid du Colombier 			ipfragfree6(ip, f);
5003ff48bf5SDavid du Colombier 			ip->stats[ReasmFails]++;
5013ff48bf5SDavid du Colombier 		}
5023ff48bf5SDavid du Colombier 		qunlock(&ip->fraglock6);
5033ff48bf5SDavid du Colombier 		return bp;
5043ff48bf5SDavid du Colombier 	}
5053ff48bf5SDavid du Colombier 
5067ec5746aSDavid du Colombier 	if(bp->base+IPFRAGSZ >= bp->rp){
5077ec5746aSDavid du Colombier 		bp = padblock(bp, IPFRAGSZ);
5087ec5746aSDavid du Colombier 		bp->rp += IPFRAGSZ;
5093ff48bf5SDavid du Colombier 	}
5103ff48bf5SDavid du Colombier 
5113ff48bf5SDavid du Colombier 	BKFG(bp)->foff = offset;
5123ff48bf5SDavid du Colombier 	BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR;
5133ff48bf5SDavid du Colombier 
5143ff48bf5SDavid du Colombier 	/* First fragment allocates a reassembly queue */
5153ff48bf5SDavid du Colombier 	if(f == nil) {
5163ff48bf5SDavid du Colombier 		f = ipfragallo6(ip);
5173ff48bf5SDavid du Colombier 		f->id = id;
5183ff48bf5SDavid du Colombier 		memmove(f->src, src, IPaddrlen);
5193ff48bf5SDavid du Colombier 		memmove(f->dst, dst, IPaddrlen);
5203ff48bf5SDavid du Colombier 
5213ff48bf5SDavid du Colombier 		f->blist = bp;
5223ff48bf5SDavid du Colombier 
5233ff48bf5SDavid du Colombier 		qunlock(&ip->fraglock6);
5243ff48bf5SDavid du Colombier 		ip->stats[ReasmReqds]++;
5253ff48bf5SDavid du Colombier 		return nil;
5263ff48bf5SDavid du Colombier 	}
5273ff48bf5SDavid du Colombier 
5283ff48bf5SDavid du Colombier 	/*
5293ff48bf5SDavid du Colombier 	 *  find the new fragment's position in the queue
5303ff48bf5SDavid du Colombier 	 */
5313ff48bf5SDavid du Colombier 	prev = nil;
5323ff48bf5SDavid du Colombier 	l = &f->blist;
5333ff48bf5SDavid du Colombier 	bl = f->blist;
5343ff48bf5SDavid du Colombier 	while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
5353ff48bf5SDavid du Colombier 		prev = bl;
5363ff48bf5SDavid du Colombier 		l = &bl->next;
5373ff48bf5SDavid du Colombier 		bl = bl->next;
5383ff48bf5SDavid du Colombier 	}
5393ff48bf5SDavid du Colombier 
5403ff48bf5SDavid du Colombier 	/* Check overlap of a previous fragment - trim away as necessary */
5413ff48bf5SDavid du Colombier 	if(prev) {
5423ff48bf5SDavid du Colombier 		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
5433ff48bf5SDavid du Colombier 		if(ovlap > 0) {
5443ff48bf5SDavid du Colombier 			if(ovlap >= BKFG(bp)->flen) {
5453ff48bf5SDavid du Colombier 				freeblist(bp);
5463ff48bf5SDavid du Colombier 				qunlock(&ip->fraglock6);
5473ff48bf5SDavid du Colombier 				return nil;
5483ff48bf5SDavid du Colombier 			}
5493ff48bf5SDavid du Colombier 			BKFG(prev)->flen -= ovlap;
5503ff48bf5SDavid du Colombier 		}
5513ff48bf5SDavid du Colombier 	}
5523ff48bf5SDavid du Colombier 
5533ff48bf5SDavid du Colombier 	/* Link onto assembly queue */
5543ff48bf5SDavid du Colombier 	bp->next = *l;
5553ff48bf5SDavid du Colombier 	*l = bp;
5563ff48bf5SDavid du Colombier 
5573ff48bf5SDavid du Colombier 	/* Check to see if succeeding segments overlap */
5583ff48bf5SDavid du Colombier 	if(bp->next) {
5593ff48bf5SDavid du Colombier 		l = &bp->next;
5603ff48bf5SDavid du Colombier 		fend = BKFG(bp)->foff + BKFG(bp)->flen;
5613ff48bf5SDavid du Colombier 
5623ff48bf5SDavid du Colombier 		/* Take completely covered segments out */
5633ff48bf5SDavid du Colombier 		while(*l) {
5643ff48bf5SDavid du Colombier 			ovlap = fend - BKFG(*l)->foff;
5653ff48bf5SDavid du Colombier 			if(ovlap <= 0)
5663ff48bf5SDavid du Colombier 				break;
5673ff48bf5SDavid du Colombier 			if(ovlap < BKFG(*l)->flen) {
5683ff48bf5SDavid du Colombier 				BKFG(*l)->flen -= ovlap;
5693ff48bf5SDavid du Colombier 				BKFG(*l)->foff += ovlap;
5703ff48bf5SDavid du Colombier 				/* move up ih hdrs */
5713ff48bf5SDavid du Colombier 				memmove((*l)->rp + ovlap, (*l)->rp, uflen);
5723ff48bf5SDavid du Colombier 				(*l)->rp += ovlap;
5733ff48bf5SDavid du Colombier 				break;
5743ff48bf5SDavid du Colombier 			}
5753ff48bf5SDavid du Colombier 			last = (*l)->next;
5763ff48bf5SDavid du Colombier 			(*l)->next = nil;
5773ff48bf5SDavid du Colombier 			freeblist(*l);
5783ff48bf5SDavid du Colombier 			*l = last;
5793ff48bf5SDavid du Colombier 		}
5803ff48bf5SDavid du Colombier 	}
5813ff48bf5SDavid du Colombier 
5823ff48bf5SDavid du Colombier 	/*
5833ff48bf5SDavid du Colombier 	 *  look for a complete packet.  if we get to a fragment
5843ff48bf5SDavid du Colombier 	 *  with the trailing bit of fraghdr->offsetRM[1] set, we're done.
5853ff48bf5SDavid du Colombier 	 */
5863ff48bf5SDavid du Colombier 	pktposn = 0;
587107aedb4SDavid du Colombier 	for(bl = f->blist; bl && BKFG(bl)->foff == pktposn; bl = bl->next) {
5883ff48bf5SDavid du Colombier 		fraghdr = (Fraghdr6 *)(bl->rp + uflen);
5893ff48bf5SDavid du Colombier 		if((fraghdr->offsetRM[1] & 1) == 0) {
5903ff48bf5SDavid du Colombier 			bl = f->blist;
5913ff48bf5SDavid du Colombier 
5923ff48bf5SDavid du Colombier 			/* get rid of frag header in first fragment */
5933ff48bf5SDavid du Colombier 			memmove(bl->rp + IP6FHDR, bl->rp, uflen);
5943ff48bf5SDavid du Colombier 			bl->rp += IP6FHDR;
595107aedb4SDavid du Colombier 			len = nhgets(((Ip6hdr*)bl->rp)->ploadlen) - IP6FHDR;
5963ff48bf5SDavid du Colombier 			bl->wp = bl->rp + len + IP6HDR;
597107aedb4SDavid du Colombier 			/*
598107aedb4SDavid du Colombier 			 * Pullup all the fragment headers and
5993ff48bf5SDavid du Colombier 			 * return a complete packet
6003ff48bf5SDavid du Colombier 			 */
6013ff48bf5SDavid du Colombier 			for(bl = bl->next; bl; bl = bl->next) {
6023ff48bf5SDavid du Colombier 				fragsize = BKFG(bl)->flen;
6033ff48bf5SDavid du Colombier 				len += fragsize;
6043ff48bf5SDavid du Colombier 				bl->rp += uflen + IP6FHDR;
6053ff48bf5SDavid du Colombier 				bl->wp = bl->rp + fragsize;
6063ff48bf5SDavid du Colombier 			}
6073ff48bf5SDavid du Colombier 
6083ff48bf5SDavid du Colombier 			bl = f->blist;
6093ff48bf5SDavid du Colombier 			f->blist = nil;
6103ff48bf5SDavid du Colombier 			ipfragfree6(ip, f);
611107aedb4SDavid du Colombier 			ih = (Ip6hdr*)bl->rp;
6123ff48bf5SDavid du Colombier 			hnputs(ih->ploadlen, len);
6133ff48bf5SDavid du Colombier 			qunlock(&ip->fraglock6);
6143ff48bf5SDavid du Colombier 			ip->stats[ReasmOKs]++;
6153ff48bf5SDavid du Colombier 			return bl;
6163ff48bf5SDavid du Colombier 		}
6173ff48bf5SDavid du Colombier 		pktposn += BKFG(bl)->flen;
6183ff48bf5SDavid du Colombier 	}
6193ff48bf5SDavid du Colombier 	qunlock(&ip->fraglock6);
6203ff48bf5SDavid du Colombier 	return nil;
6213ff48bf5SDavid du Colombier }
622