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