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