xref: /plan9-contrib/sys/src/9k/ip/ip.c (revision 55ba833bbc29e52fa60df7522a0e5f7350ace33d)
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 
10 #define BLKIPVER(xp)	(((Ip4hdr*)((xp)->rp))->vihl&0xF0)
11 
12 static char *statnames[] =
13 {
14 [Forwarding]	"Forwarding",
15 [DefaultTTL]	"DefaultTTL",
16 [InReceives]	"InReceives",
17 [InHdrErrors]	"InHdrErrors",
18 [InAddrErrors]	"InAddrErrors",
19 [ForwDatagrams]	"ForwDatagrams",
20 [InUnknownProtos]	"InUnknownProtos",
21 [InDiscards]	"InDiscards",
22 [InDelivers]	"InDelivers",
23 [OutRequests]	"OutRequests",
24 [OutDiscards]	"OutDiscards",
25 [OutNoRoutes]	"OutNoRoutes",
26 [ReasmTimeout]	"ReasmTimeout",
27 [ReasmReqds]	"ReasmReqds",
28 [ReasmOKs]	"ReasmOKs",
29 [ReasmFails]	"ReasmFails",
30 [FragOKs]	"FragOKs",
31 [FragFails]	"FragFails",
32 [FragCreates]	"FragCreates",
33 };
34 
35 #define BLKIP(xp)	((Ip4hdr*)((xp)->rp))
36 /*
37  * This sleazy macro relies on the media header size being
38  * larger than sizeof(Ipfrag). ipreassemble checks this is true
39  */
40 #define BKFG(xp)	((Ipfrag*)((xp)->base))
41 
42 ushort		ipcsum(uchar*);
43 Block*		ip4reassemble(IP*, int, Block*, Ip4hdr*);
44 void		ipfragfree4(IP*, Fragment4*);
45 Fragment4*	ipfragallo4(IP*);
46 
47 void
ip_init_6(Fs * f)48 ip_init_6(Fs *f)
49 {
50 	v6params *v6p;
51 
52 	v6p = smalloc(sizeof(v6params));
53 
54 	v6p->rp.mflag		= 0;		/* default not managed */
55 	v6p->rp.oflag		= 0;
56 	v6p->rp.maxraint	= 600000;	/* millisecs */
57 	v6p->rp.minraint	= 200000;
58 	v6p->rp.linkmtu		= 0;		/* no mtu sent */
59 	v6p->rp.reachtime	= 0;
60 	v6p->rp.rxmitra		= 0;
61 	v6p->rp.ttl		= MAXTTL;
62 	v6p->rp.routerlt	= 3 * v6p->rp.maxraint;
63 
64 	v6p->hp.rxmithost	= 1000;		/* v6 RETRANS_TIMER */
65 
66 	v6p->cdrouter		= -1;
67 
68 	f->v6p			= v6p;
69 }
70 
71 void
initfrag(IP * ip,int size)72 initfrag(IP *ip, int size)
73 {
74 	Fragment4 *fq4, *eq4;
75 	Fragment6 *fq6, *eq6;
76 
77 	ip->fragfree4 = (Fragment4*)malloc(sizeof(Fragment4) * size);
78 	if(ip->fragfree4 == nil)
79 		panic("initfrag");
80 
81 	eq4 = &ip->fragfree4[size];
82 	for(fq4 = ip->fragfree4; fq4 < eq4; fq4++)
83 		fq4->next = fq4+1;
84 
85 	ip->fragfree4[size-1].next = nil;
86 
87 	ip->fragfree6 = (Fragment6*)malloc(sizeof(Fragment6) * size);
88 	if(ip->fragfree6 == nil)
89 		panic("initfrag");
90 
91 	eq6 = &ip->fragfree6[size];
92 	for(fq6 = ip->fragfree6; fq6 < eq6; fq6++)
93 		fq6->next = fq6+1;
94 
95 	ip->fragfree6[size-1].next = nil;
96 }
97 
98 void
ip_init(Fs * f)99 ip_init(Fs *f)
100 {
101 	IP *ip;
102 
103 	ip = smalloc(sizeof(IP));
104 	initfrag(ip, 100);
105 	f->ip = ip;
106 
107 	ip_init_6(f);
108 }
109 
110 void
iprouting(Fs * f,int on)111 iprouting(Fs *f, int on)
112 {
113 	f->ip->iprouting = on;
114 	if(f->ip->iprouting==0)
115 		f->ip->stats[Forwarding] = 2;
116 	else
117 		f->ip->stats[Forwarding] = 1;
118 }
119 
120 int
ipoput4(Fs * f,Block * bp,int gating,int ttl,int tos,Conv * c)121 ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
122 {
123 	Ipifc *ifc;
124 	uchar *gate;
125 	ulong fragoff;
126 	Block *xp, *nb;
127 	Ip4hdr *eh, *feh;
128 	int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
129 	Route *r, *sr;
130 	IP *ip;
131 	int rv = 0;
132 	uchar v4dst[IPv4addrlen];
133 
134 	ip = f->ip;
135 
136 	/* Fill out the ip header */
137 	eh = (Ip4hdr*)(bp->rp);
138 
139 	ip->stats[OutRequests]++;
140 
141 	/* Number of uchars in data and ip header to write */
142 	len = blocklen(bp);
143 
144 	if(gating){
145 		chunk = nhgets(eh->length);
146 		if(chunk > len){
147 			ip->stats[OutDiscards]++;
148 			netlog(f, Logip, "short gated packet\n");
149 			goto free;
150 		}
151 		if(chunk < len)
152 			len = chunk;
153 	}
154 	if(len >= IP_MAX){
155 		ip->stats[OutDiscards]++;
156 		netlog(f, Logip, "exceeded ip max size %V\n", eh->dst);
157 		goto free;
158 	}
159 
160 	r = v4lookup(f, eh->dst, c);
161 	if(r == nil){
162 		ip->stats[OutNoRoutes]++;
163 		netlog(f, Logip, "no interface %V\n", eh->dst);
164 		rv = -1;
165 		goto free;
166 	}
167 
168 	ifc = r->ifc;
169 	if(r->type & (Rifc|Runi))
170 		gate = eh->dst;
171 	else
172 	if(r->type & (Rbcast|Rmulti)) {
173 		if(nhgetl(r->v4.gate) == 0){
174 			hnputl(v4dst, r->v4.address);
175 			gate = v4dst;
176 		}else
177 			gate = eh->dst;
178 		sr = v4lookup(f, eh->src, nil);
179 		if(sr != nil && (sr->type & Runi))
180 			ifc = sr->ifc;
181 	}
182 	else
183 		gate = r->v4.gate;
184 
185 	if(!gating)
186 		eh->vihl = IP_VER4|IP_HLEN4;
187 	eh->ttl = ttl;
188 	if(!gating)
189 		eh->tos = tos;
190 
191 	if(!canrlock(ifc))
192 		goto free;
193 	if(waserror()){
194 		runlock(ifc);
195 		nexterror();
196 	}
197 	if(ifc->medium == nil)
198 		goto raise;
199 
200 	/* If we dont need to fragment just send it */
201 	if(c && c->maxfragsize && c->maxfragsize < ifc->maxtu)
202 		medialen = c->maxfragsize - ifc->medium->hsize;
203 	else
204 		medialen = ifc->maxtu - ifc->medium->hsize;
205 	if(len <= medialen) {
206 		if(!gating)
207 			hnputs(eh->id, incref(&ip->id4));
208 		hnputs(eh->length, len);
209 		if(!gating){
210 			eh->frag[0] = 0;
211 			eh->frag[1] = 0;
212 		}
213 		eh->cksum[0] = 0;
214 		eh->cksum[1] = 0;
215 		hnputs(eh->cksum, ipcsum(&eh->vihl));
216 		assert(bp->next == nil);
217 		ifc->medium->bwrite(ifc, bp, V4, gate);
218 		runlock(ifc);
219 		poperror();
220 		return 0;
221 	}
222 
223 	if((eh->frag[0] & (IP_DF>>8)) && !gating)
224 		print("%V: DF set\n", eh->dst);
225 
226 	if(eh->frag[0] & (IP_DF>>8)){
227 		ip->stats[FragFails]++;
228 		ip->stats[OutDiscards]++;
229 		icmpcantfrag(f, bp, medialen);
230 		netlog(f, Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
231 		goto raise;
232 	}
233 
234 	seglen = (medialen - IP4HDR) & ~7;
235 	if(seglen < 8){
236 		ip->stats[FragFails]++;
237 		ip->stats[OutDiscards]++;
238 		netlog(f, Logip, "%V seglen < 8\n", eh->dst);
239 		goto raise;
240 	}
241 
242 	dlen = len - IP4HDR;
243 	xp = bp;
244 	if(gating)
245 		lid = nhgets(eh->id);
246 	else
247 		lid = incref(&ip->id4);
248 
249 	offset = IP4HDR;
250 	while(xp != nil && offset && offset >= BLEN(xp)) {
251 		offset -= BLEN(xp);
252 		xp = xp->next;
253 	}
254 	xp->rp += offset;
255 
256 	if(gating)
257 		fragoff = nhgets(eh->frag)<<3;
258 	else
259 		fragoff = 0;
260 	dlen += fragoff;
261 	for(; fragoff < dlen; fragoff += seglen) {
262 		nb = allocb(IP4HDR+seglen);
263 		feh = (Ip4hdr*)(nb->rp);
264 
265 		memmove(nb->wp, eh, IP4HDR);
266 		nb->wp += IP4HDR;
267 
268 		if((fragoff + seglen) >= dlen) {
269 			seglen = dlen - fragoff;
270 			hnputs(feh->frag, fragoff>>3);
271 		}
272 		else
273 			hnputs(feh->frag, (fragoff>>3)|IP_MF);
274 
275 		hnputs(feh->length, seglen + IP4HDR);
276 		hnputs(feh->id, lid);
277 
278 		/* Copy up the data area */
279 		chunk = seglen;
280 		while(chunk) {
281 			if(!xp) {
282 				ip->stats[OutDiscards]++;
283 				ip->stats[FragFails]++;
284 				freeblist(nb);
285 				netlog(f, Logip, "!xp: chunk %d\n", chunk);
286 				goto raise;
287 			}
288 			blklen = chunk;
289 			if(BLEN(xp) < chunk)
290 				blklen = BLEN(xp);
291 			memmove(nb->wp, xp->rp, blklen);
292 			nb->wp += blklen;
293 			xp->rp += blklen;
294 			chunk -= blklen;
295 			if(xp->rp == xp->wp)
296 				xp = xp->next;
297 		}
298 
299 		feh->cksum[0] = 0;
300 		feh->cksum[1] = 0;
301 		hnputs(feh->cksum, ipcsum(&feh->vihl));
302 		ifc->medium->bwrite(ifc, nb, V4, gate);
303 		ip->stats[FragCreates]++;
304 	}
305 	ip->stats[FragOKs]++;
306 raise:
307 	runlock(ifc);
308 	poperror();
309 free:
310 	freeblist(bp);
311 	return rv;
312 }
313 
314 void
ipiput4(Fs * f,Ipifc * ifc,Block * bp)315 ipiput4(Fs *f, Ipifc *ifc, Block *bp)
316 {
317 	int hl;
318 	int hop, tos, proto, olen;
319 	Ip4hdr *h;
320 	Proto *p;
321 	ushort frag;
322 	int notforme;
323 	uchar *dp, v6dst[IPaddrlen];
324 	IP *ip;
325 	Route *r;
326 	Conv conv;
327 
328 	if(BLKIPVER(bp) != IP_VER4) {
329 		ipiput6(f, ifc, bp);
330 		return;
331 	}
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 < IP4HDR)
344 			hl = IP4HDR;
345 		if(hl > 64)
346 			hl = 64;
347 		bp = pullupblock(bp, hl);
348 		if(bp == nil)
349 			return;
350 	}
351 
352 	h = (Ip4hdr*)(bp->rp);
353 
354 	/* dump anything that whose header doesn't checksum */
355 	if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
356 		ip->stats[InHdrErrors]++;
357 		netlog(f, Logip, "ip: checksum error %V\n", h->src);
358 		freeblist(bp);
359 		return;
360 	}
361 	v4tov6(v6dst, h->dst);
362 	notforme = ipforme(f, v6dst) == 0;
363 
364 	/* Check header length and version */
365 	if((h->vihl&0x0F) != IP_HLEN4) {
366 		hl = (h->vihl&0xF)<<2;
367 		if(hl < (IP_HLEN4<<2)) {
368 			ip->stats[InHdrErrors]++;
369 			netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl);
370 			freeblist(bp);
371 			return;
372 		}
373 		/* If this is not routed strip off the options */
374 		if(notforme == 0) {
375 			olen = nhgets(h->length);
376 			dp = bp->rp + (hl - (IP_HLEN4<<2));
377 			memmove(dp, h, IP_HLEN4<<2);
378 			bp->rp = dp;
379 			h = (Ip4hdr*)(bp->rp);
380 			h->vihl = (IP_VER4|IP_HLEN4);
381 			hnputs(h->length, olen-hl+(IP_HLEN4<<2));
382 		}
383 	}
384 
385 	/* route */
386 	if(notforme) {
387 		if(!ip->iprouting){
388 			freeblist(bp);
389 			return;
390 		}
391 
392 		/* don't forward to source's network */
393 		memset(&conv, 0, sizeof conv);
394 		conv.r = nil;
395 		r = v4lookup(f, h->dst, &conv);
396 		if(r == nil || r->ifc == ifc){
397 			ip->stats[OutDiscards]++;
398 			freeblist(bp);
399 			return;
400 		}
401 
402 		/* don't forward if packet has timed out */
403 		hop = h->ttl;
404 		if(hop < 1) {
405 			ip->stats[InHdrErrors]++;
406 			icmpttlexceeded(f, ifc->lifc->local, bp);
407 			freeblist(bp);
408 			return;
409 		}
410 
411 		/* reassemble if the interface expects it */
412 if(r->ifc == nil) panic("nil route rfc");
413 		if(r->ifc->reassemble){
414 			frag = nhgets(h->frag);
415 			if(frag) {
416 				h->tos = 0;
417 				if(frag & IP_MF)
418 					h->tos = 1;
419 				bp = ip4reassemble(ip, frag, bp, h);
420 				if(bp == nil)
421 					return;
422 				h = (Ip4hdr*)(bp->rp);
423 			}
424 		}
425 
426 		ip->stats[ForwDatagrams]++;
427 		tos = h->tos;
428 		hop = h->ttl;
429 		ipoput4(f, bp, 1, hop - 1, tos, &conv);
430 		return;
431 	}
432 
433 	frag = nhgets(h->frag);
434 	if(frag) {
435 		h->tos = 0;
436 		if(frag & IP_MF)
437 			h->tos = 1;
438 		bp = ip4reassemble(ip, frag, bp, h);
439 		if(bp == nil)
440 			return;
441 		h = (Ip4hdr*)(bp->rp);
442 	}
443 
444 	/* don't let any frag info go up the stack */
445 	h->frag[0] = 0;
446 	h->frag[1] = 0;
447 
448 	proto = h->proto;
449 	p = Fsrcvpcol(f, proto);
450 	if(p != nil && p->rcv != nil) {
451 		ip->stats[InDelivers]++;
452 		(*p->rcv)(p, ifc, bp);
453 		return;
454 	}
455 	ip->stats[InDiscards]++;
456 	ip->stats[InUnknownProtos]++;
457 	freeblist(bp);
458 }
459 
460 int
ipstats(Fs * f,char * buf,int len)461 ipstats(Fs *f, char *buf, int len)
462 {
463 	IP *ip;
464 	char *p, *e;
465 	int i;
466 
467 	ip = f->ip;
468 	ip->stats[DefaultTTL] = MAXTTL;
469 
470 	p = buf;
471 	e = p+len;
472 	for(i = 0; i < Nipstats; i++)
473 		p = seprint(p, e, "%s: %llud\n", statnames[i], ip->stats[i]);
474 	return p - buf;
475 }
476 
477 Block*
ip4reassemble(IP * ip,int offset,Block * bp,Ip4hdr * ih)478 ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
479 {
480 	int fend;
481 	ushort id;
482 	Fragment4 *f, *fnext;
483 	ulong src, dst;
484 	Block *bl, **l, *last, *prev;
485 	int ovlap, len, fragsize, pktposn;
486 
487 	src = nhgetl(ih->src);
488 	dst = nhgetl(ih->dst);
489 	id = nhgets(ih->id);
490 
491 	/*
492 	 *  block lists are too hard, pullupblock into a single block
493 	 */
494 	if(bp->next){
495 		bp = pullupblock(bp, blocklen(bp));
496 		ih = (Ip4hdr*)(bp->rp);
497 	}
498 
499 	qlock(&ip->fraglock4);
500 
501 	/*
502 	 *  find a reassembly queue for this fragment
503 	 */
504 	for(f = ip->flisthead4; f; f = fnext){
505 		fnext = f->next;	/* because ipfragfree4 changes the list */
506 		if(f->src == src && f->dst == dst && f->id == id)
507 			break;
508 		if(f->age < NOW){
509 			ip->stats[ReasmTimeout]++;
510 			ipfragfree4(ip, f);
511 		}
512 	}
513 
514 	/*
515 	 *  if this isn't a fragmented packet, accept it
516 	 *  and get rid of any fragments that might go
517 	 *  with it.
518 	 */
519 	if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
520 		if(f != nil) {
521 			ipfragfree4(ip, f);
522 			ip->stats[ReasmFails]++;
523 		}
524 		qunlock(&ip->fraglock4);
525 		return bp;
526 	}
527 
528 	if(bp->base+IPFRAGSZ >= bp->rp){
529 		bp = padblock(bp, IPFRAGSZ);
530 		bp->rp += IPFRAGSZ;
531 	}
532 
533 	BKFG(bp)->foff = offset<<3;
534 	BKFG(bp)->flen = nhgets(ih->length)-IP4HDR;
535 
536 	/* First fragment allocates a reassembly queue */
537 	if(f == nil) {
538 		f = ipfragallo4(ip);
539 		f->id = id;
540 		f->src = src;
541 		f->dst = dst;
542 
543 		f->blist = bp;
544 
545 		qunlock(&ip->fraglock4);
546 		ip->stats[ReasmReqds]++;
547 		return nil;
548 	}
549 
550 	/*
551 	 *  find the new fragment's position in the queue
552 	 */
553 	prev = nil;
554 	l = &f->blist;
555 	bl = f->blist;
556 	while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
557 		prev = bl;
558 		l = &bl->next;
559 		bl = bl->next;
560 	}
561 
562 	/* Check overlap of a previous fragment - trim away as necessary */
563 	if(prev) {
564 		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
565 		if(ovlap > 0) {
566 			if(ovlap >= BKFG(bp)->flen) {
567 				freeblist(bp);
568 				qunlock(&ip->fraglock4);
569 				return nil;
570 			}
571 			BKFG(prev)->flen -= ovlap;
572 		}
573 	}
574 
575 	/* Link onto assembly queue */
576 	bp->next = *l;
577 	*l = bp;
578 
579 	/* Check to see if succeeding segments overlap */
580 	if(bp->next) {
581 		l = &bp->next;
582 		fend = BKFG(bp)->foff + BKFG(bp)->flen;
583 		/* Take completely covered segments out */
584 		while(*l) {
585 			ovlap = fend - BKFG(*l)->foff;
586 			if(ovlap <= 0)
587 				break;
588 			if(ovlap < BKFG(*l)->flen) {
589 				BKFG(*l)->flen -= ovlap;
590 				BKFG(*l)->foff += ovlap;
591 				/* move up ih hdrs */
592 				memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
593 				(*l)->rp += ovlap;
594 				break;
595 			}
596 			last = (*l)->next;
597 			(*l)->next = nil;
598 			freeblist(*l);
599 			*l = last;
600 		}
601 	}
602 
603 	/*
604 	 *  look for a complete packet.  if we get to a fragment
605 	 *  without IP_MF set, we're done.
606 	 */
607 	pktposn = 0;
608 	for(bl = f->blist; bl; bl = bl->next) {
609 		if(BKFG(bl)->foff != pktposn)
610 			break;
611 		if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) {
612 			bl = f->blist;
613 			len = nhgets(BLKIP(bl)->length);
614 			bl->wp = bl->rp + len;
615 
616 			/* Pullup all the fragment headers and
617 			 * return a complete packet
618 			 */
619 			for(bl = bl->next; bl; bl = bl->next) {
620 				fragsize = BKFG(bl)->flen;
621 				len += fragsize;
622 				bl->rp += IP4HDR;
623 				bl->wp = bl->rp + fragsize;
624 			}
625 
626 			bl = f->blist;
627 			f->blist = nil;
628 			ipfragfree4(ip, f);
629 			ih = BLKIP(bl);
630 			hnputs(ih->length, len);
631 			qunlock(&ip->fraglock4);
632 			ip->stats[ReasmOKs]++;
633 			return bl;
634 		}
635 		pktposn += BKFG(bl)->flen;
636 	}
637 	qunlock(&ip->fraglock4);
638 	return nil;
639 }
640 
641 /*
642  * ipfragfree4 - Free a list of fragments - assume hold fraglock4
643  */
644 void
ipfragfree4(IP * ip,Fragment4 * frag)645 ipfragfree4(IP *ip, Fragment4 *frag)
646 {
647 	Fragment4 *fl, **l;
648 
649 	if(frag->blist)
650 		freeblist(frag->blist);
651 
652 	frag->src = 0;
653 	frag->id = 0;
654 	frag->blist = nil;
655 
656 	l = &ip->flisthead4;
657 	for(fl = *l; fl; fl = fl->next) {
658 		if(fl == frag) {
659 			*l = frag->next;
660 			break;
661 		}
662 		l = &fl->next;
663 	}
664 
665 	frag->next = ip->fragfree4;
666 	ip->fragfree4 = frag;
667 
668 }
669 
670 /*
671  * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
672  */
673 Fragment4 *
ipfragallo4(IP * ip)674 ipfragallo4(IP *ip)
675 {
676 	Fragment4 *f;
677 
678 	while(ip->fragfree4 == nil) {
679 		/* free last entry on fraglist */
680 		for(f = ip->flisthead4; f->next; f = f->next)
681 			;
682 		ipfragfree4(ip, f);
683 	}
684 	f = ip->fragfree4;
685 	ip->fragfree4 = f->next;
686 	f->next = ip->flisthead4;
687 	ip->flisthead4 = f;
688 	f->age = NOW + 30000;
689 
690 	return f;
691 }
692 
693 ushort
ipcsum(uchar * addr)694 ipcsum(uchar *addr)
695 {
696 	int len;
697 	ulong sum;
698 
699 	sum = 0;
700 	len = (addr[0]&0xf)<<2;
701 
702 	while(len > 0) {
703 		sum += addr[0]<<8 | addr[1] ;
704 		len -= 2;
705 		addr += 2;
706 	}
707 
708 	sum = (sum & 0xffff) + (sum >> 16);
709 	sum = (sum & 0xffff) + (sum >> 16);
710 
711 	return (sum^0xffff);
712 }
713