Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * Internet Control Message Protocol for IPv6
3
 */
4
#include "u.h"
5
#include "../port/lib.h"
6
#include "mem.h"
7
#include "dat.h"
8
#include "fns.h"
9
#include "../port/error.h"
10
#include "ip.h"
11
#include "ipv6.h"
12
 
13
enum
14
{
15
	InMsgs6,
16
	InErrors6,
17
	OutMsgs6,
18
	CsumErrs6,
19
	LenErrs6,
20
	HlenErrs6,
21
	HoplimErrs6,
22
	IcmpCodeErrs6,
23
	TargetErrs6,
24
	OptlenErrs6,
25
	AddrmxpErrs6,
26
	RouterAddrErrs6,
27
 
28
	Nstats6,
29
};
30
 
31
enum {
32
	ICMP_USEAD6	= 40,
33
};
34
 
35
enum {
36
	Oflag	= 1<<5,
37
	Sflag	= 1<<6,
38
	Rflag	= 1<<7,
39
};
40
 
41
enum {
42
	/* ICMPv6 types */
43
	EchoReply	= 0,
44
	UnreachableV6	= 1,
45
	PacketTooBigV6	= 2,
46
	TimeExceedV6	= 3,
47
	SrcQuench	= 4,
48
	ParamProblemV6	= 4,
49
	Redirect	= 5,
50
	EchoRequest	= 8,
51
	TimeExceed	= 11,
52
	InParmProblem	= 12,
53
	Timestamp	= 13,
54
	TimestampReply	= 14,
55
	InfoRequest	= 15,
56
	InfoReply	= 16,
57
	AddrMaskRequest = 17,
58
	AddrMaskReply   = 18,
59
	EchoRequestV6	= 128,
60
	EchoReplyV6	= 129,
61
	RouterSolicit	= 133,
62
	RouterAdvert	= 134,
63
	NbrSolicit	= 135,
64
	NbrAdvert	= 136,
65
	RedirectV6	= 137,
66
 
67
	Maxtype6	= 137,
68
};
69
 
70
/* on-the-wire packet formats */
71
typedef struct IPICMP IPICMP;
72
typedef struct Ndpkt Ndpkt;
73
typedef struct NdiscC NdiscC;
74
 
75
/* we do this to avoid possible struct padding  */
76
#define ICMPHDR \
77
	IPV6HDR; \
78
	uchar	type; \
79
	uchar	code; \
80
	uchar	cksum[2]; \
81
	uchar	icmpid[2]; \
82
	uchar	seq[2]
83
 
84
struct IPICMP {
85
	ICMPHDR;
86
	uchar	payload[];
87
};
88
 
89
#define IPICMPSZ offsetof(IPICMP, payload[0])
90
 
91
struct NdiscC {
92
	ICMPHDR;
93
	uchar	target[IPaddrlen];
94
	uchar	payload[];
95
};
96
 
97
#define NDISCSZ offsetof(NdiscC, payload[0])
98
 
99
struct Ndpkt {
100
	ICMPHDR;
101
	uchar	target[IPaddrlen];
102
	uchar	otype;
103
	uchar	olen;		/* length in units of 8 octets(incl type, code),
104
				 * 1 for IEEE 802 addresses */
105
	uchar	lnaddr[6];	/* link-layer address */
106
	uchar	payload[];
107
};
108
 
109
#define NDPKTSZ offsetof(Ndpkt, payload[0])
110
 
111
typedef struct Icmppriv6
112
{
113
	ulong	stats[Nstats6];
114
 
115
	/* message counts */
116
	ulong	in[Maxtype6+1];
117
	ulong	out[Maxtype6+1];
118
} Icmppriv6;
119
 
120
typedef struct Icmpcb6
121
{
122
	QLock;
123
	uchar	headers;
124
} Icmpcb6;
125
 
126
char *icmpnames6[Maxtype6+1] =
127
{
128
[EchoReply]		"EchoReply",
129
[UnreachableV6]		"UnreachableV6",
130
[PacketTooBigV6]	"PacketTooBigV6",
131
[TimeExceedV6]		"TimeExceedV6",
132
[SrcQuench]		"SrcQuench",
133
[Redirect]		"Redirect",
134
[EchoRequest]		"EchoRequest",
135
[TimeExceed]		"TimeExceed",
136
[InParmProblem]		"InParmProblem",
137
[Timestamp]		"Timestamp",
138
[TimestampReply]	"TimestampReply",
139
[InfoRequest]		"InfoRequest",
140
[InfoReply]		"InfoReply",
141
[AddrMaskRequest]	"AddrMaskRequest",
142
[AddrMaskReply]		"AddrMaskReply",
143
[EchoRequestV6]		"EchoRequestV6",
144
[EchoReplyV6]		"EchoReplyV6",
145
[RouterSolicit]		"RouterSolicit",
146
[RouterAdvert]		"RouterAdvert",
147
[NbrSolicit]		"NbrSolicit",
148
[NbrAdvert]		"NbrAdvert",
149
[RedirectV6]		"RedirectV6",
150
};
151
 
152
static char *statnames6[Nstats6] =
153
{
154
[InMsgs6]	"InMsgs",
155
[InErrors6]	"InErrors",
156
[OutMsgs6]	"OutMsgs",
157
[CsumErrs6]	"CsumErrs",
158
[LenErrs6]	"LenErrs",
159
[HlenErrs6]	"HlenErrs",
160
[HoplimErrs6]	"HoplimErrs",
161
[IcmpCodeErrs6]	"IcmpCodeErrs",
162
[TargetErrs6]	"TargetErrs",
163
[OptlenErrs6]	"OptlenErrs",
164
[AddrmxpErrs6]	"AddrmxpErrs",
165
[RouterAddrErrs6]	"RouterAddrErrs",
166
};
167
 
168
static char *unreachcode[] =
169
{
170
[Icmp6_no_route]	"no route to destination",
171
[Icmp6_ad_prohib]	"comm with destination administratively prohibited",
172
[Icmp6_out_src_scope]	"beyond scope of source address",
173
[Icmp6_adr_unreach]	"address unreachable",
174
[Icmp6_port_unreach]	"port unreachable",
175
[Icmp6_gress_src_fail]	"source address failed ingress/egress policy",
176
[Icmp6_rej_route]	"reject route to destination",
177
[Icmp6_unknown]		"icmp unreachable: unknown code",
178
};
179
 
180
static void icmpkick6(void *x, Block *bp);
181
 
182
static void
183
icmpcreate6(Conv *c)
184
{
185
	c->rq = qopen(64*1024, Qmsg, 0, c);
186
	c->wq = qbypass(icmpkick6, c);
187
}
188
 
189
static void
190
set_cksum(Block *bp)
191
{
192
	IPICMP *p = (IPICMP *)(bp->rp);
193
 
194
	hnputl(p->vcf, 0);  	/* borrow IP header as pseudoheader */
195
	hnputs(p->ploadlen, blocklen(bp) - IP6HDR);
196
	p->proto = 0;
197
	p->ttl = ICMPv6;	/* ttl gets set later */
198
	hnputs(p->cksum, 0);
199
	hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp)));
200
	p->proto = ICMPv6;
201
}
202
 
203
static Block *
204
newIPICMP(int packetlen)
205
{
206
	Block *nbp;
207
 
208
	nbp = allocb(packetlen);
209
	nbp->wp += packetlen;
210
	memset(nbp->rp, 0, packetlen);
211
	return nbp;
212
}
213
 
214
void
215
icmpadvise6(Proto *icmp, Block *bp, char *msg)
216
{
217
	ushort recid;
218
	Conv **c, *s;
219
	IPICMP *p;
220
 
221
	p = (IPICMP *)bp->rp;
222
	recid = nhgets(p->icmpid);
223
 
224
	for(c = icmp->conv; *c; c++) {
225
		s = *c;
226
		if(s->lport == recid && ipcmp(s->raddr, p->dst) == 0){
227
			qhangup(s->rq, msg);
228
			qhangup(s->wq, msg);
229
			break;
230
		}
231
	}
232
	freeblist(bp);
233
}
234
 
235
static void
236
icmpkick6(void *x, Block *bp)
237
{
238
	uchar laddr[IPaddrlen], raddr[IPaddrlen];
239
	Conv *c = x;
240
	IPICMP *p;
241
	Icmppriv6 *ipriv = c->p->priv;
242
	Icmpcb6 *icb = (Icmpcb6*)c->ptcl;
243
 
244
	if(bp == nil)
245
		return;
246
 
247
	if(icb->headers==6) {
248
		/* get user specified addresses */
249
		bp = pullupblock(bp, ICMP_USEAD6);
250
		if(bp == nil)
251
			return;
252
		bp->rp += 8;
253
		ipmove(laddr, bp->rp);
254
		bp->rp += IPaddrlen;
255
		ipmove(raddr, bp->rp);
256
		bp->rp += IPaddrlen;
257
		bp = padblock(bp, IP6HDR);
258
	}
259
 
260
	if(blocklen(bp) < IPICMPSZ){
261
		freeblist(bp);
262
		return;
263
	}
264
	p = (IPICMP *)(bp->rp);
265
	if(icb->headers == 6) {
266
		ipmove(p->dst, raddr);
267
		ipmove(p->src, laddr);
268
	} else {
269
		ipmove(p->dst, c->raddr);
270
		ipmove(p->src, c->laddr);
271
		hnputs(p->icmpid, c->lport);
272
	}
273
 
274
	set_cksum(bp);
275
	p->vcf[0] = 0x06 << 4;
276
	if(p->type <= Maxtype6)
277
		ipriv->out[p->type]++;
278
	ipoput6(c->p->f, bp, 0, c->ttl, c->tos, nil);
279
}
280
 
281
char*
282
icmpctl6(Conv *c, char **argv, int argc)
283
{
284
	Icmpcb6 *icb;
285
 
286
	icb = (Icmpcb6*) c->ptcl;
287
	if(argc==1 && strcmp(argv[0], "headers")==0) {
288
		icb->headers = 6;
289
		return nil;
290
	}
291
	return "unknown control request";
292
}
293
 
294
static void
295
goticmpkt6(Proto *icmp, Block *bp, int muxkey)
296
{
297
	ushort recid;
298
	uchar *addr;
299
	Conv **c, *s;
300
	IPICMP *p = (IPICMP *)bp->rp;
301
 
302
	if(muxkey == 0) {
303
		recid = nhgets(p->icmpid);
304
		addr = p->src;
305
	} else {
306
		recid = muxkey;
307
		addr = p->dst;
308
	}
309
 
310
	for(c = icmp->conv; *c; c++){
311
		s = *c;
312
		if(s->lport == recid && ipcmp(s->raddr, addr) == 0){
313
			bp = concatblock(bp);
314
			if(bp != nil)
315
				qpass(s->rq, bp);
316
			return;
317
		}
318
	}
319
 
320
	freeblist(bp);
321
}
322
 
323
static Block *
324
mkechoreply6(Block *bp, Ipifc *ifc)
325
{
326
	uchar addr[IPaddrlen];
327
	IPICMP *p = (IPICMP *)(bp->rp);
328
 
329
	ipmove(addr, p->src);
330
	if(!isv6mcast(p->dst))
331
		ipmove(p->src, p->dst);
332
	else if (!ipv6anylocal(ifc, p->src))
333
		return nil;
334
	ipmove(p->dst, addr);
335
	p->type = EchoReplyV6;
336
	set_cksum(bp);
337
	return bp;
338
}
339
 
340
/*
341
 * sends out an ICMPv6 neighbor solicitation
342
 * 	suni == SRC_UNSPEC or SRC_UNI,
343
 *	tuni == TARG_MULTI => multicast for address resolution,
344
 * 	and tuni == TARG_UNI => neighbor reachability.
345
 */
346
extern void
347
icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac)
348
{
349
	Block *nbp;
350
	Ndpkt *np;
351
	Proto *icmp = f->t2p[ICMPv6];
352
	Icmppriv6 *ipriv = icmp->priv;
353
 
354
	nbp = newIPICMP(NDPKTSZ);
355
	np = (Ndpkt*) nbp->rp;
356
 
357
	if(suni == SRC_UNSPEC)
358
		memmove(np->src, v6Unspecified, IPaddrlen);
359
	else
360
		memmove(np->src, src, IPaddrlen);
361
 
362
	if(tuni == TARG_UNI)
363
		memmove(np->dst, targ, IPaddrlen);
364
	else
365
		ipv62smcast(np->dst, targ);
366
 
367
	np->type = NbrSolicit;
368
	np->code = 0;
369
	memmove(np->target, targ, IPaddrlen);
370
	if(suni != SRC_UNSPEC) {
371
		np->otype = SRC_LLADDR;
372
		np->olen = 1;		/* 1+1+6 = 8 = 1 8-octet */
373
		memmove(np->lnaddr, mac, sizeof(np->lnaddr));
374
	} else
375
		nbp->wp -= NDPKTSZ - NDISCSZ;
376
 
377
	set_cksum(nbp);
378
	np = (Ndpkt*)nbp->rp;
379
	np->ttl = HOP_LIMIT;
380
	np->vcf[0] = 0x06 << 4;
381
	ipriv->out[NbrSolicit]++;
382
	netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
383
	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
384
}
385
 
386
/*
387
 * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
388
 */
389
extern void
390
icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags)
391
{
392
	Block *nbp;
393
	Ndpkt *np;
394
	Proto *icmp = f->t2p[ICMPv6];
395
	Icmppriv6 *ipriv = icmp->priv;
396
 
397
	nbp = newIPICMP(NDPKTSZ);
398
	np = (Ndpkt*)nbp->rp;
399
 
400
	memmove(np->src, src, IPaddrlen);
401
	memmove(np->dst, dst, IPaddrlen);
402
 
403
	np->type = NbrAdvert;
404
	np->code = 0;
405
	np->icmpid[0] = flags;
406
	memmove(np->target, targ, IPaddrlen);
407
 
408
	np->otype = TARGET_LLADDR;
409
	np->olen = 1;
410
	memmove(np->lnaddr, mac, sizeof(np->lnaddr));
411
 
412
	set_cksum(nbp);
413
	np = (Ndpkt*) nbp->rp;
414
	np->ttl = HOP_LIMIT;
415
	np->vcf[0] = 0x06 << 4;
416
	ipriv->out[NbrAdvert]++;
417
	netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
418
	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
419
}
420
 
421
/* if free is true, freeblist(bp) before return. */
422
extern void
423
icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free)
424
{
425
	int osz, sz;
426
	Block *nbp;
427
	IPICMP *np;
428
	Icmppriv6 *ipriv;
429
	Ip6hdr *p;
430
	Proto *icmp;
431
 
432
	osz = BLEN(bp);
433
	sz = MIN(IPICMPSZ + osz, v6MINTU);
434
	icmp = f->t2p[ICMPv6];
435
	ipriv = icmp->priv;
436
	p = (Ip6hdr *)bp->rp;
437
	if(isv6mcast(p->src))
438
		goto freebl;
439
	nbp = newIPICMP(sz);
440
	np = (IPICMP *)nbp->rp;
441
 
442
	rlock(ifc);
443
	if(!ipv6anylocal(ifc, np->src)){
444
		netlog(f, Logicmp, "icmphostunr fail -> src %I dst %I\n",
445
			p->src, p->dst);
446
		runlock(ifc);
447
		freeblist(nbp);
448
		goto freebl;
449
	}
450
 
451
	netlog(f, Logicmp, "send icmphostunr -> src %I dst %I\n", p->src, p->dst);
452
	memmove(np->dst, p->src, IPaddrlen);
453
	np->type = UnreachableV6;
454
	np->code = code;
455
	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
456
	set_cksum(nbp);
457
	np->ttl = HOP_LIMIT;
458
	np->vcf[0] = 0x06 << 4;
459
	ipriv->out[UnreachableV6]++;
460
 
461
	if(free)
462
		ipiput6(f, ifc, nbp);
463
	else
464
		ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
465
	runlock(ifc);
466
freebl:
467
	if(free)
468
		freeblist(bp);
469
}
470
 
471
extern void
472
icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp)
473
{
474
	int osz = BLEN(bp);
475
	int sz = MIN(IPICMPSZ + osz, v6MINTU);
476
	Block *nbp;
477
	IPICMP *np;
478
	Ip6hdr *p;
479
	Proto *icmp = f->t2p[ICMPv6];
480
	Icmppriv6 *ipriv = icmp->priv;
481
 
482
	p = (Ip6hdr *)bp->rp;
483
	if(isv6mcast(p->src))
484
		return;
485
 
486
	nbp = newIPICMP(sz);
487
	np = (IPICMP *) nbp->rp;
488
	if(ipv6anylocal(ifc, np->src))
489
		netlog(f, Logicmp, "send icmpttlexceeded6 -> src %I dst %I\n",
490
			p->src, p->dst);
491
	else {
492
		netlog(f, Logicmp, "icmpttlexceeded6 fail -> src %I dst %I\n",
493
			p->src, p->dst);
494
		return;
495
	}
496
 
497
	memmove(np->dst, p->src, IPaddrlen);
498
	np->type = TimeExceedV6;
499
	np->code = 0;
500
	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
501
	set_cksum(nbp);
502
	np->ttl = HOP_LIMIT;
503
	np->vcf[0] = 0x06 << 4;
504
	ipriv->out[TimeExceedV6]++;
505
	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
506
}
507
 
508
extern void
509
icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp)
510
{
511
	int osz = BLEN(bp);
512
	int sz = MIN(IPICMPSZ + osz, v6MINTU);
513
	Block *nbp;
514
	IPICMP *np;
515
	Ip6hdr *p;
516
	Proto *icmp = f->t2p[ICMPv6];
517
	Icmppriv6 *ipriv = icmp->priv;
518
 
519
	p = (Ip6hdr *)bp->rp;
520
	if(isv6mcast(p->src))
521
		return;
522
 
523
	nbp = newIPICMP(sz);
524
	np = (IPICMP *)nbp->rp;
525
	if(ipv6anylocal(ifc, np->src))
526
		netlog(f, Logicmp, "send icmppkttoobig6 -> src %I dst %I\n",
527
			p->src, p->dst);
528
	else {
529
		netlog(f, Logicmp, "icmppkttoobig6 fail -> src %I dst %I\n",
530
			p->src, p->dst);
531
		return;
532
	}
533
 
534
	memmove(np->dst, p->src, IPaddrlen);
535
	np->type = PacketTooBigV6;
536
	np->code = 0;
537
	hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
538
	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
539
	set_cksum(nbp);
540
	np->ttl = HOP_LIMIT;
541
	np->vcf[0] = 0x06 << 4;
542
	ipriv->out[PacketTooBigV6]++;
543
	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
544
}
545
 
546
/*
547
 * RFC 2461, pages 39-40, pages 57-58.
548
 */
549
static int
550
valid(Proto *icmp, Ipifc *, Block *bp, Icmppriv6 *ipriv)
551
{
552
	int sz, osz, unsp, n, ttl, iplen, pktsz;
553
	uchar *packet;
554
	IPICMP *p;
555
	Ndpkt *np;
556
 
557
	n = blocklen(bp);
558
	if(n < IPICMPSZ) {
559
		ipriv->stats[HlenErrs6]++;
560
		netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
561
		goto err;
562
	}
563
 
564
	packet = bp->rp;
565
	p = (IPICMP *)packet;
566
	pktsz = BLEN(bp);
567
	iplen = nhgets(p->ploadlen);
568
	if(iplen > n - IP6HDR) {
569
		ipriv->stats[LenErrs6]++;
570
		netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
571
		goto err;
572
	}
573
 
574
	/* Rather than construct explicit pseudoheader, overwrite IPv6 header */
575
	if(p->proto != ICMPv6) {
576
		/* This code assumes no extension headers!!! */
577
		netlog(icmp->f, Logicmp, "icmp error: extension header\n");
578
		goto err;
579
	}
580
	memset(packet, 0, 4);
581
	ttl = p->ttl;
582
	p->ttl = p->proto;
583
	p->proto = 0;
584
	if(ptclcsum(bp, 0, iplen + IP6HDR)) {
585
		ipriv->stats[CsumErrs6]++;
586
		netlog(icmp->f, Logicmp, "icmp checksum error\n");
587
		goto err;
588
	}
589
	p->proto = p->ttl;
590
	p->ttl = ttl;
591
 
592
	/* additional tests for some pkt types */
593
	if (p->type != NbrSolicit   && p->type != NbrAdvert &&
594
	    p->type != RouterAdvert && p->type != RouterSolicit &&
595
	    p->type != RedirectV6)
596
		return 1;	/* TODO: unknown, presumed valid; why? */
597
	if(p->ttl != HOP_LIMIT) {
598
		ipriv->stats[HoplimErrs6]++;
599
		goto err;
600
	}
601
	if(p->code != 0) {
602
		ipriv->stats[IcmpCodeErrs6]++;
603
		goto err;
604
	}
605
 
606
	switch (p->type) {
607
	case NbrSolicit:
608
	case NbrAdvert:
609
		np = (Ndpkt*) p;
610
		if(isv6mcast(np->target)) {
611
			ipriv->stats[TargetErrs6]++;
612
			goto err;
613
		}
614
		if(optexsts(np) && np->olen == 0) {
615
			ipriv->stats[OptlenErrs6]++;
616
			goto err;
617
		}
618
		if (p->type == NbrSolicit && ipcmp(np->src, v6Unspecified) == 0)
619
			if(!issmcast(np->dst) || optexsts(np)) {
620
				ipriv->stats[AddrmxpErrs6]++;
621
				goto err;
622
			}
623
		if(p->type == NbrAdvert && isv6mcast(np->dst) &&
624
		    nhgets(np->icmpid) & Sflag){
625
			ipriv->stats[AddrmxpErrs6]++;
626
			goto err;
627
		}
628
		break;
629
	case RouterAdvert:
630
		if(pktsz - IP6HDR < 16) {
631
			ipriv->stats[HlenErrs6]++;
632
			goto err;
633
		}
634
		if(!islinklocal(p->src)) {
635
			ipriv->stats[RouterAddrErrs6]++;
636
			goto err;
637
		}
638
		for (sz = IPICMPSZ + 8; sz+1 < pktsz; sz += 8*osz) {
639
			osz = packet[sz+1];
640
			if(osz <= 0) {
641
				ipriv->stats[OptlenErrs6]++;
642
				goto err;
643
			}
644
		}
645
		break;
646
	case RouterSolicit:
647
		if(pktsz - IP6HDR < 8) {
648
			ipriv->stats[HlenErrs6]++;
649
			goto err;
650
		}
651
		unsp = (ipcmp(p->src, v6Unspecified) == 0);
652
		for (sz = IPICMPSZ + 8; sz+1 < pktsz; sz += 8*osz) {
653
			osz = packet[sz+1];
654
			if(osz <= 0 || (unsp && packet[sz] == SRC_LLADDR)) {
655
				ipriv->stats[OptlenErrs6]++;
656
				goto err;
657
			}
658
		}
659
		break;
660
	case RedirectV6:
661
		/* TODO: fill in */
662
		break;
663
	default:
664
		goto err;
665
	}
666
	return 1;
667
err:
668
	ipriv->stats[InErrors6]++;
669
	return 0;
670
}
671
 
672
static int
673
targettype(Fs *f, Ipifc *ifc, uchar *target)
674
{
675
	Iplifc *lifc;
676
	int t;
677
 
678
	rlock(ifc);
679
	if(ipproxyifc(f, ifc, target)) {
680
		runlock(ifc);
681
		return Tuniproxy;
682
	}
683
 
684
	for(lifc = ifc->lifc; lifc; lifc = lifc->next)
685
		if(ipcmp(lifc->local, target) == 0) {
686
			t = (lifc->tentative)? Tunitent: Tunirany;
687
			runlock(ifc);
688
			return t;
689
		}
690
 
691
	runlock(ifc);
692
	return 0;
693
}
694
 
695
/* bp needs to be freed with freeblist or passed on. */
696
static void
697
icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp)
698
{
699
	int type;
700
	char *msg, m2[128];
701
	uchar pktflags;
702
	uchar *packet, *src;
703
	uchar lsrc[IPaddrlen];
704
	Block *r;
705
	IPICMP *p;
706
	Icmppriv6 *ipriv;
707
	Iplifc *lifc;
708
	Ndpkt* np;
709
	Proto *pr;
710
 
711
	packet = bp->rp;
712
	p = (IPICMP *)packet;
713
	type = p->type;
714
	ipriv = icmp->priv;
715
	if(!valid(icmp, ipifc, bp, ipriv) || type > Maxtype6)
716
		goto raise;
717
 
718
	ipriv->in[type]++;
719
	switch(type) {
720
	case EchoRequestV6:
721
		bp = concatblock(bp);
722
		r = mkechoreply6(bp, ipifc);
723
		if(r == nil)
724
			goto raise;
725
		ipriv->out[EchoReply]++;
726
		ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
727
		break;
728
	case UnreachableV6:
729
		if(p->code >= nelem(unreachcode))
730
			msg = unreachcode[Icmp6_unknown];
731
		else
732
			msg = unreachcode[p->code];
733
 
734
		bp->rp += IPICMPSZ;
735
		if(blocklen(bp) < 8){
736
			ipriv->stats[LenErrs6]++;
737
			goto raise;
738
		}
739
		p = (IPICMP *)bp->rp;
740
		pr = Fsrcvpcolx(icmp->f, p->proto);
741
		if(pr != nil && pr->advise != nil) {
742
			(*pr->advise)(pr, bp, msg);
743
			return;
744
		}
745
 
746
		bp->rp -= IPICMPSZ;
747
		goticmpkt6(icmp, bp, 0);
748
		break;
749
	case TimeExceedV6:
750
		if(p->code == 0){
751
			snprint(m2, sizeof m2, "ttl exceeded at %I", p->src);
752
			bp->rp += IPICMPSZ;
753
			if(blocklen(bp) < 8){
754
				ipriv->stats[LenErrs6]++;
755
				goto raise;
756
			}
757
			p = (IPICMP *)bp->rp;
758
			pr = Fsrcvpcolx(icmp->f, p->proto);
759
			if(pr && pr->advise) {
760
				(*pr->advise)(pr, bp, m2);
761
				return;
762
			}
763
			bp->rp -= IPICMPSZ;
764
		}
765
		goticmpkt6(icmp, bp, 0);
766
		break;
767
	case RouterAdvert:
768
	case RouterSolicit:
769
		/* using lsrc as a temp, munge hdr for goticmp6 */
770
		if (0) {
771
			memmove(lsrc, p->src, IPaddrlen);
772
			memmove(p->src, p->dst, IPaddrlen);
773
			memmove(p->dst, lsrc, IPaddrlen);
774
		}
775
		goticmpkt6(icmp, bp, type);
776
		break;
777
	case NbrSolicit:
778
		np = (Ndpkt*)p;			/* within bp */
779
		pktflags = 0;
780
		switch (targettype(icmp->f, ipifc, np->target)) {
781
		case Tunirany:
782
			pktflags |= Oflag;
783
			/* fall through */
784
		case Tuniproxy:
785
			if(ipcmp(np->src, v6Unspecified) != 0) {
786
				arpenter(icmp->f, V6, np->src, np->lnaddr,
787
					8*np->olen-2, 0);
788
				pktflags |= Sflag;
789
			}
790
			if(ipv6local(ipifc, lsrc)) {
791
				src = np->src;
792
				if(ipcmp(src, v6Unspecified) == 0)
793
					src = v6allnodesL;
794
				icmpna(icmp->f, lsrc, src, np->target,
795
					ipifc->mac, pktflags);
796
			}
797
			break;
798
		case Tunitent:
799
			/*
800
			 * not clear what needs to be done.  send up
801
			 * an icmp mesg saying `don't use this address'?
802
			 */
803
			break;
804
		}
805
		freeblist(bp);
806
		break;
807
	case NbrAdvert:
808
		/*
809
		 * if the target address matches one of the local interface
810
		 * addresses and the local interface address has tentative bit
811
		 * set, insert into ARP table. this is so the duplicate address
812
		 * detection part of ipconfig can discover duplication through
813
		 * the arp table.
814
		 */
815
		np = (Ndpkt*)p;			/* within bp */
816
		lifc = iplocalonifc(ipifc, np->target);
817
		arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2,
818
			lifc && lifc->tentative);
819
		freeblist(bp);
820
		break;
821
	case PacketTooBigV6:
822
	default:
823
		goticmpkt6(icmp, bp, 0);
824
		break;
825
	}
826
	return;
827
 
828
raise:
829
	freeblist(bp);
830
}
831
 
832
int
833
icmpstats6(Proto *icmp6, char *buf, int len)
834
{
835
	Icmppriv6 *priv;
836
	char *p, *e;
837
	int i;
838
 
839
	priv = icmp6->priv;
840
	p = buf;
841
	e = p+len;
842
	for(i = 0; i < Nstats6; i++)
843
		p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]);
844
	for(i = 0; i <= Maxtype6; i++)
845
		if(icmpnames6[i])
846
			p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i],
847
				priv->in[i], priv->out[i]);
848
		else if (0)
849
			p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i],
850
				priv->out[i]);
851
	return p - buf;
852
}
853
 
854
/* import from icmp.c */
855
extern int	icmpstate(Conv *c, char *state, int n);
856
extern char*	icmpannounce(Conv *c, char **argv, int argc);
857
extern char*	icmpconnect(Conv *c, char **argv, int argc);
858
extern void	icmpclose(Conv *c);
859
 
860
void
861
icmp6init(Fs *fs)
862
{
863
	Proto *icmp6 = smalloc(sizeof(Proto));
864
 
865
	icmp6->priv = smalloc(sizeof(Icmppriv6));
866
	icmp6->name = "icmpv6";
867
	icmp6->connect = icmpconnect;
868
	icmp6->announce = icmpannounce;
869
	icmp6->state = icmpstate;
870
	icmp6->create = icmpcreate6;
871
	icmp6->close = icmpclose;
872
	icmp6->rcv = icmpiput6;
873
	icmp6->stats = icmpstats6;
874
	icmp6->ctl = icmpctl6;
875
	icmp6->advise = icmpadvise6;
876
	icmp6->gc = nil;
877
	icmp6->ipproto = ICMPv6;
878
	icmp6->nc = 16;
879
	icmp6->ptclsize = sizeof(Icmpcb6);
880
 
881
	Fsproto(fs, icmp6);
882
}