Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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*
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*
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
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*
448
procopts(Block *bp)
449
{
450
	return bp;
451
}
452
 
453
Block*
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
}