xref: /plan9/sys/src/9/ip/icmp6.c (revision 58da3067adcdccaaa043d0bfde28ba83b7ced07d)
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
icmpcreate6(Conv * c)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
set_cksum(Block * bp)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 *
newIPICMP(int packetlen)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
icmpadvise6(Proto * icmp,Block * bp,char * msg)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
icmpkick6(void * x,Block * bp)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*
icmpctl6(Conv * c,char ** argv,int argc)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
goticmpkt6(Proto * icmp,Block * bp,int muxkey)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 *
mkechoreply6(Block * bp,Ipifc * ifc)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
icmpns(Fs * f,uchar * src,int suni,uchar * targ,int tuni,uchar * mac)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
icmpna(Fs * f,uchar * src,uchar * dst,uchar * targ,uchar * mac,uchar flags)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
icmphostunr(Fs * f,Ipifc * ifc,Block * bp,int code,int free)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
icmpttlexceeded6(Fs * f,Ipifc * ifc,Block * bp)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
icmppkttoobig6(Fs * f,Ipifc * ifc,Block * bp)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
valid(Proto * icmp,Ipifc *,Block * bp,Icmppriv6 * ipriv)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
targettype(Fs * f,Ipifc * ifc,uchar * target)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
icmpiput6(Proto * icmp,Ipifc * ipifc,Block * bp)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
icmpstats6(Proto * icmp6,char * buf,int len)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
icmp6init(Fs * fs)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 }
883