| /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved. | 
 |  * Portions Copyright © 1997-1999 Vita Nuova Limited | 
 |  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited | 
 |  *                                (www.vitanuova.com) | 
 |  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others | 
 |  * | 
 |  * Modified for the Akaros operating system: | 
 |  * Copyright (c) 2013-2014 The Regents of the University of California | 
 |  * Copyright (c) 2013-2015 Google Inc. | 
 |  * | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
 |  * of this software and associated documentation files (the "Software"), to deal | 
 |  * in the Software without restriction, including without limitation the rights | 
 |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
 |  * copies of the Software, and to permit persons to whom the Software is | 
 |  * furnished to do so, subject to the following conditions: | 
 |  * | 
 |  * The above copyright notice and this permission notice shall be included in | 
 |  * all copies or substantial portions of the Software. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
 |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
 |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | 
 |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
 |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
 |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
 |  * SOFTWARE. */ | 
 |  | 
 | #include <kmalloc.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <syscall.h> | 
 | #include <error.h> | 
 | #include <net/ip.h> | 
 |  | 
 | struct ICMPpkt { | 
 | 	uint8_t type; | 
 | 	uint8_t code; | 
 | 	uint8_t cksum[2]; | 
 | 	uint8_t icmpid[2]; | 
 | 	uint8_t seq[2]; | 
 | }; | 
 |  | 
 | /* plan 9 uses anon struct members. | 
 |  * We have been naming the struct | 
 |  * members and just using the extra level of deref | 
 |  * e.g. i->x becomes i->i6->x. | 
 |  */ | 
 | struct IPICMP { | 
 | /* | 
 | 	Ip6hdr; | 
 | 	ICMPpkt; | 
 | */ | 
 | 	uint8_t vcf[4];		// version:4, traffic class:8, flow label:20 | 
 | 	uint8_t ploadlen[2];	// payload length: packet length - 40 | 
 | 	uint8_t proto;		// next header type | 
 | 	uint8_t ttl;		// hop limit | 
 | 	uint8_t src[IPaddrlen]; | 
 | 	uint8_t dst[IPaddrlen]; | 
 | 	uint8_t type; | 
 | 	uint8_t code; | 
 | 	uint8_t cksum[2]; | 
 | 	uint8_t icmpid[2]; | 
 | 	uint8_t seq[2]; | 
 |  | 
 | }; | 
 |  | 
 | struct NdiscC { | 
 | 	//IPICMP; | 
 | 	uint8_t vcf[4];		// version:4, traffic class:8, flow label:20 | 
 | 	uint8_t ploadlen[2];	// payload length: packet length - 40 | 
 | 	uint8_t proto;		// next header type | 
 | 	uint8_t ttl;		// hop limit | 
 | 	uint8_t src[IPaddrlen]; | 
 | 	uint8_t dst[IPaddrlen]; | 
 | 	uint8_t type; | 
 | 	uint8_t code; | 
 | 	uint8_t cksum[2]; | 
 | 	uint8_t icmpid[2]; | 
 | 	uint8_t seq[2]; | 
 |  | 
 | 	uint8_t target[IPaddrlen]; | 
 | }; | 
 |  | 
 | struct Ndpkt { | 
 | 	//NdiscC; | 
 | 	uint8_t vcf[4];		// version:4, traffic class:8, flow label:20 | 
 | 	uint8_t ploadlen[2];	// payload length: packet length - 40 | 
 | 	uint8_t proto;		// next header type | 
 | 	uint8_t ttl;		// hop limit | 
 | 	uint8_t src[IPaddrlen]; | 
 | 	uint8_t dst[IPaddrlen]; | 
 | 	uint8_t type; | 
 | 	uint8_t code; | 
 | 	uint8_t cksum[2]; | 
 | 	uint8_t icmpid[2]; | 
 | 	uint8_t seq[2]; | 
 |  | 
 | 	uint8_t target[IPaddrlen]; | 
 |  | 
 | 	uint8_t otype; | 
 | 	uint8_t olen;		// length in units of 8 octets(incl type, code), | 
 | 	// 1 for IEEE 802 addresses | 
 | 	uint8_t lnaddr[6];	// link-layer address | 
 | }; | 
 |  | 
 | enum { | 
 | 	// ICMPv6 types | 
 | 	EchoReply = 0, | 
 | 	UnreachableV6 = 1, | 
 | 	PacketTooBigV6 = 2, | 
 | 	TimeExceedV6 = 3, | 
 | 	SrcQuench = 4, | 
 | 	ParamProblemV6 = 4, | 
 | 	Redirect = 5, | 
 | 	EchoRequest = 8, | 
 | 	TimeExceed = 11, | 
 | 	InParmProblem = 12, | 
 | 	Timestamp = 13, | 
 | 	TimestampReply = 14, | 
 | 	InfoRequest = 15, | 
 | 	InfoReply = 16, | 
 | 	AddrMaskRequest = 17, | 
 | 	AddrMaskReply = 18, | 
 | 	EchoRequestV6 = 128, | 
 | 	EchoReplyV6 = 129, | 
 | 	RouterSolicit = 133, | 
 | 	RouterAdvert = 134, | 
 | 	NbrSolicit = 135, | 
 | 	NbrAdvert = 136, | 
 | 	RedirectV6 = 137, | 
 |  | 
 | 	Maxtype6 = 137, | 
 | }; | 
 |  | 
 | char *icmpnames6[Maxtype6 + 1] = { | 
 | 	[EchoReply] "EchoReply", | 
 | 	[UnreachableV6] "UnreachableV6", | 
 | 	[PacketTooBigV6] "PacketTooBigV6", | 
 | 	[TimeExceedV6] "TimeExceedV6", | 
 | 	[SrcQuench] "SrcQuench", | 
 | 	[Redirect] "Redirect", | 
 | 	[EchoRequest] "EchoRequest", | 
 | 	[TimeExceed] "TimeExceed", | 
 | 	[InParmProblem] "InParmProblem", | 
 | 	[Timestamp] "Timestamp", | 
 | 	[TimestampReply] "TimestampReply", | 
 | 	[InfoRequest] "InfoRequest", | 
 | 	[InfoReply] "InfoReply", | 
 | 	[AddrMaskRequest] "AddrMaskRequest", | 
 | 	[AddrMaskReply] "AddrMaskReply", | 
 | 	[EchoRequestV6] "EchoRequestV6", | 
 | 	[EchoReplyV6] "EchoReplyV6", | 
 | 	[RouterSolicit] "RouterSolicit", | 
 | 	[RouterAdvert] "RouterAdvert", | 
 | 	[NbrSolicit] "NbrSolicit", | 
 | 	[NbrAdvert] "NbrAdvert", | 
 | 	[RedirectV6] "RedirectV6", | 
 | }; | 
 |  | 
 | enum { | 
 | 	InMsgs6, | 
 | 	InErrors6, | 
 | 	OutMsgs6, | 
 | 	CsumErrs6, | 
 | 	LenErrs6, | 
 | 	HlenErrs6, | 
 | 	HoplimErrs6, | 
 | 	IcmpCodeErrs6, | 
 | 	TargetErrs6, | 
 | 	OptlenErrs6, | 
 | 	AddrmxpErrs6, | 
 | 	RouterAddrErrs6, | 
 |  | 
 | 	Nstats6, | 
 | }; | 
 |  | 
 | static char *statnames6[Nstats6] = { | 
 | 	[InMsgs6] "InMsgs", | 
 | 	[InErrors6] "InErrors", | 
 | 	[OutMsgs6] "OutMsgs", | 
 | 	[CsumErrs6] "CsumErrs", | 
 | 	[LenErrs6] "LenErrs", | 
 | 	[HlenErrs6] "HlenErrs", | 
 | 	[HoplimErrs6] "HoplimErrs", | 
 | 	[IcmpCodeErrs6] "IcmpCodeErrs", | 
 | 	[TargetErrs6] "TargetErrs", | 
 | 	[OptlenErrs6] "OptlenErrs", | 
 | 	[AddrmxpErrs6] "AddrmxpErrs", | 
 | 	[RouterAddrErrs6] "RouterAddrErrs", | 
 | }; | 
 |  | 
 | typedef struct Icmppriv6 { | 
 | 	uint32_t stats[Nstats6]; | 
 |  | 
 | 	/* message counts */ | 
 | 	uint32_t in[Maxtype6 + 1]; | 
 | 	uint32_t out[Maxtype6 + 1]; | 
 | } Icmppriv6; | 
 |  | 
 | typedef struct Icmpcb6 { | 
 | 	uint8_t headers; | 
 | } Icmpcb6; | 
 |  | 
 | static char *unreachcode[] = { | 
 | 	[icmp6_no_route] "no route to destination", | 
 | 	[icmp6_ad_prohib] "comm with destination administratively prohibited", | 
 | 	[icmp6_unassigned] "icmp unreachable: unassigned error code (2)", | 
 | 	[icmp6_adr_unreach] "address unreachable", | 
 | 	[icmp6_port_unreach] "port unreachable", | 
 | 	[icmp6_unkn_code] "icmp unreachable: unknown code", | 
 | }; | 
 |  | 
 | enum { | 
 | 	ICMP_USEAD6 = 40, | 
 | }; | 
 |  | 
 | enum { | 
 | 	Oflag = 1 << 5, | 
 | 	Sflag = 1 << 6, | 
 | 	Rflag = 1 << 7, | 
 | }; | 
 |  | 
 | enum { | 
 | 	slladd = 1, | 
 | 	tlladd = 2, | 
 | 	prfinfo = 3, | 
 | 	redhdr = 4, | 
 | 	mtuopt = 5, | 
 | }; | 
 |  | 
 | static void icmpkick6(void *x, struct block *bp); | 
 |  | 
 | static void icmpcreate6(struct conv *c) | 
 | { | 
 | 	c->rq = qopen(64 * 1024, Qmsg, 0, c); | 
 | 	c->wq = qbypass(icmpkick6, c); | 
 | } | 
 |  | 
 | static void set_cksum(struct block *bp) | 
 | { | 
 | 	struct IPICMP *p = (struct IPICMP *)(bp->rp); | 
 |  | 
 | 	hnputl(p->vcf, 0);	// borrow IP header as pseudoheader | 
 | 	hnputs(p->ploadlen, blocklen(bp) - IPV6HDR_LEN); | 
 | 	p->proto = 0; | 
 | 	p->ttl = ICMPv6;	// ttl gets set later | 
 | 	hnputs(p->cksum, 0); | 
 | 	hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp))); | 
 | 	p->proto = ICMPv6; | 
 | } | 
 |  | 
 | static struct block *newIPICMP(int packetlen) | 
 | { | 
 | 	struct block *nbp; | 
 |  | 
 | 	nbp = block_alloc(packetlen, MEM_WAIT); | 
 | 	nbp->wp += packetlen; | 
 | 	memset(nbp->rp, 0, packetlen); | 
 | 	return nbp; | 
 | } | 
 |  | 
 | void icmpadvise6(struct Proto *icmp, struct block *bp, char *msg) | 
 | { | 
 | 	struct conv **c, *s; | 
 | 	struct IPICMP *p; | 
 | 	uint16_t recid; | 
 |  | 
 | 	p = (struct IPICMP *)bp->rp; | 
 | 	recid = nhgets(p->icmpid); | 
 |  | 
 | 	for (c = icmp->conv; *c; c++) { | 
 | 		s = *c; | 
 | 		if (s->lport == recid) | 
 | 			if (ipcmp(s->raddr, p->dst) == 0) { | 
 | 				qhangup(s->rq, msg); | 
 | 				qhangup(s->wq, msg); | 
 | 				break; | 
 | 			} | 
 | 	} | 
 | 	freeblist(bp); | 
 | } | 
 |  | 
 | static void icmpkick6(void *x, struct block *bp) | 
 | { | 
 | 	struct conv *c = x; | 
 | 	struct IPICMP *p; | 
 | 	uint8_t laddr[IPaddrlen], raddr[IPaddrlen]; | 
 | 	Icmppriv6 *ipriv = c->p->priv; | 
 | 	Icmpcb6 *icb = (struct Icmpcb6 *)c->ptcl; | 
 |  | 
 | 	if (bp == NULL) | 
 | 		return; | 
 |  | 
 | 	if (icb->headers == 6) { | 
 | 		/* get user specified addresses */ | 
 | 		bp = pullupblock(bp, ICMP_USEAD6); | 
 | 		if (bp == NULL) | 
 | 			return; | 
 | 		bp->rp += 8; | 
 | 		ipmove(laddr, bp->rp); | 
 | 		bp->rp += IPaddrlen; | 
 | 		ipmove(raddr, bp->rp); | 
 | 		bp->rp += IPaddrlen; | 
 | 		bp = padblock(bp, sizeof(struct ip6hdr)); | 
 |  | 
 | 		if (blocklen(bp) < sizeof(struct IPICMP)) { | 
 | 			freeblist(bp); | 
 | 			return; | 
 | 		} | 
 | 		p = (struct IPICMP *)(bp->rp); | 
 |  | 
 | 		ipmove(p->dst, raddr); | 
 | 		ipmove(p->src, laddr); | 
 |  | 
 | 	} else { | 
 | 		if (blocklen(bp) < sizeof(struct IPICMP)) { | 
 | 			freeblist(bp); | 
 | 			return; | 
 | 		} | 
 | 		p = (struct IPICMP *)(bp->rp); | 
 |  | 
 | 		ipmove(p->dst, c->raddr); | 
 | 		ipmove(p->src, c->laddr); | 
 | 		hnputs(p->icmpid, c->lport); | 
 | 	} | 
 |  | 
 | 	set_cksum(bp); | 
 | 	p->vcf[0] = 0x06 << 4; | 
 | 	if (p->type <= Maxtype6) | 
 | 		ipriv->out[p->type]++; | 
 | 	ipoput6(c->p->f, bp, 0, c->ttl, c->tos, NULL); | 
 | } | 
 |  | 
 | static void icmpctl6(struct conv *c, char **argv, int argc) | 
 | { | 
 | 	Icmpcb6 *icb = (Icmpcb6*)c->ptcl; | 
 |  | 
 | 	if ((argc == 1) && strcmp(argv[0], "headers") == 0) | 
 | 		icb->headers = 6; | 
 | 	else | 
 | 		error(EINVAL, "unknown command to icmpctl6"); | 
 | } | 
 |  | 
 | static void goticmpkt6(struct Proto *icmp, struct block *bp, int muxkey) | 
 | { | 
 | 	struct conv **c, *s; | 
 | 	struct IPICMP *p = (struct IPICMP *)bp->rp; | 
 | 	uint16_t recid; | 
 | 	uint8_t *addr; | 
 |  | 
 | 	if (muxkey == 0) { | 
 | 		recid = nhgets(p->icmpid); | 
 | 		addr = p->src; | 
 | 	} else { | 
 | 		recid = muxkey; | 
 | 		addr = p->dst; | 
 | 	} | 
 |  | 
 | 	for (c = icmp->conv; *c; c++) { | 
 | 		s = *c; | 
 | 		if (s->lport == recid && ipcmp(s->raddr, addr) == 0) { | 
 | 			bp = concatblock(bp); | 
 | 			if (bp != NULL) | 
 | 				qpass(s->rq, bp); | 
 | 			return; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	freeblist(bp); | 
 | } | 
 |  | 
 | static struct block *mkechoreply6(struct block *bp) | 
 | { | 
 | 	struct IPICMP *p = (struct IPICMP *)(bp->rp); | 
 | 	uint8_t addr[IPaddrlen]; | 
 |  | 
 | 	ipmove(addr, p->src); | 
 | 	ipmove(p->src, p->dst); | 
 | 	ipmove(p->dst, addr); | 
 | 	p->type = EchoReplyV6; | 
 | 	set_cksum(bp); | 
 | 	return bp; | 
 | } | 
 |  | 
 | /* | 
 |  * sends out an ICMPv6 neighbor solicitation | 
 |  * 	suni == SRC_UNSPEC or SRC_UNI, | 
 |  *	tuni == TARG_MULTI => multicast for address resolution, | 
 |  * 	and tuni == TARG_UNI => neighbor reachability. | 
 |  */ | 
 |  | 
 | void icmpns(struct Fs *f, uint8_t *src, int suni, uint8_t *targ, int tuni, | 
 | 	    uint8_t *mac) | 
 | { | 
 | 	struct block *nbp; | 
 | 	struct Ndpkt *np; | 
 | 	struct Proto *icmp = f->t2p[ICMPv6]; | 
 | 	struct Icmppriv6 *ipriv = icmp->priv; | 
 |  | 
 | 	nbp = newIPICMP(sizeof(struct Ndpkt)); | 
 | 	np = (struct Ndpkt *)nbp->rp; | 
 |  | 
 | 	if (suni == SRC_UNSPEC) | 
 | 		memmove(np->src, v6Unspecified, IPaddrlen); | 
 | 	else | 
 | 		memmove(np->src, src, IPaddrlen); | 
 |  | 
 | 	if (tuni == TARG_UNI) | 
 | 		memmove(np->dst, targ, IPaddrlen); | 
 | 	else | 
 | 		ipv62smcast(np->dst, targ); | 
 |  | 
 | 	np->type = NbrSolicit; | 
 | 	np->code = 0; | 
 | 	memmove(np->target, targ, IPaddrlen); | 
 | 	if (suni != SRC_UNSPEC) { | 
 | 		np->otype = SRC_LLADDRESS; | 
 | 		np->olen = 1;	/* 1+1+6 = 8 = 1 8-octet */ | 
 | 		memmove(np->lnaddr, mac, sizeof(np->lnaddr)); | 
 | 	} else { | 
 | 		int r = sizeof(struct Ndpkt) - sizeof(struct NdiscC); | 
 | 		nbp->wp -= r; | 
 | 	} | 
 |  | 
 | 	set_cksum(nbp); | 
 | 	np = (struct Ndpkt *)nbp->rp; | 
 | 	np->ttl = HOP_LIMIT; | 
 | 	np->vcf[0] = 0x06 << 4; | 
 | 	ipriv->out[NbrSolicit]++; | 
 | 	netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ); | 
 | 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL); | 
 | } | 
 |  | 
 | /* | 
 |  * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags. | 
 |  */ | 
 | void icmpna(struct Fs *f, uint8_t * src, uint8_t * dst, uint8_t * targ, | 
 |             uint8_t * mac, uint8_t flags) | 
 | { | 
 | 	struct block *nbp; | 
 | 	struct Ndpkt *np; | 
 | 	struct Proto *icmp = f->t2p[ICMPv6]; | 
 | 	Icmppriv6 *ipriv = icmp->priv; | 
 |  | 
 | 	nbp = newIPICMP(sizeof(struct Ndpkt)); | 
 | 	np = (struct Ndpkt *)nbp->rp; | 
 |  | 
 | 	memmove(np->src, src, IPaddrlen); | 
 | 	memmove(np->dst, dst, IPaddrlen); | 
 |  | 
 | 	np->type = NbrAdvert; | 
 | 	np->code = 0; | 
 | 	np->icmpid[0] = flags; | 
 | 	memmove(np->target, targ, IPaddrlen); | 
 |  | 
 | 	np->otype = TARGET_LLADDRESS; | 
 | 	np->olen = 1; | 
 | 	memmove(np->lnaddr, mac, sizeof(np->lnaddr)); | 
 |  | 
 | 	set_cksum(nbp); | 
 | 	np = (struct Ndpkt *)nbp->rp; | 
 | 	np->ttl = HOP_LIMIT; | 
 | 	np->vcf[0] = 0x06 << 4; | 
 | 	ipriv->out[NbrAdvert]++; | 
 | 	netlog(f, Logicmp, "sending neighbor advertisement %I\n", src); | 
 | 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL); | 
 | } | 
 |  | 
 | void icmphostunr(struct Fs *f, struct Ipifc *ifc, | 
 |                  struct block *bp, int code, int free) | 
 | { | 
 | 	struct block *nbp; | 
 | 	struct IPICMP *np; | 
 | 	struct ip6hdr *p; | 
 | 	int osz = BLEN(bp); | 
 | 	int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU); | 
 | 	struct Proto *icmp = f->t2p[ICMPv6]; | 
 | 	Icmppriv6 *ipriv = icmp->priv; | 
 |  | 
 | 	p = (struct ip6hdr *)bp->rp; | 
 |  | 
 | 	if (isv6mcast(p->src)) | 
 | 		goto freebl; | 
 |  | 
 | 	nbp = newIPICMP(sz); | 
 | 	np = (struct IPICMP *)nbp->rp; | 
 |  | 
 | 	rlock(&ifc->rwlock); | 
 | 	if (ipv6anylocal(ifc, np->src)) { | 
 | 		netlog(f, Logicmp, "send icmphostunr -> s%I d%I\n", | 
 | 		       p->src, p->dst); | 
 | 	} else { | 
 | 		netlog(f, Logicmp, "icmphostunr fail -> s%I d%I\n", | 
 | 		       p->src, p->dst); | 
 | 		runlock(&ifc->rwlock); | 
 | 		freeblist(nbp); | 
 | 		goto freebl; | 
 | 	} | 
 |  | 
 | 	memmove(np->dst, p->src, IPaddrlen); | 
 | 	np->type = UnreachableV6; | 
 | 	np->code = code; | 
 | 	memmove(nbp->rp + sizeof(struct IPICMP), bp->rp, | 
 | 			sz - sizeof(struct IPICMP)); | 
 | 	set_cksum(nbp); | 
 | 	np->ttl = HOP_LIMIT; | 
 | 	np->vcf[0] = 0x06 << 4; | 
 | 	ipriv->out[UnreachableV6]++; | 
 |  | 
 | 	if (free) | 
 | 		ipiput6(f, ifc, nbp); | 
 | 	else | 
 | 		ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL); | 
 | 	runlock(&ifc->rwlock); | 
 | freebl: | 
 | 	if (free) | 
 | 		freeblist(bp); | 
 | } | 
 |  | 
 | void icmpttlexceeded6(struct Fs *f, struct Ipifc *ifc, struct block *bp) | 
 | { | 
 | 	struct block *nbp; | 
 | 	struct IPICMP *np; | 
 | 	struct ip6hdr *p; | 
 | 	int osz = BLEN(bp); | 
 | 	int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU); | 
 | 	struct Proto *icmp = f->t2p[ICMPv6]; | 
 | 	Icmppriv6 *ipriv = icmp->priv; | 
 |  | 
 | 	p = (struct ip6hdr *)bp->rp; | 
 |  | 
 | 	if (isv6mcast(p->src)) | 
 | 		return; | 
 |  | 
 | 	nbp = newIPICMP(sz); | 
 | 	np = (struct IPICMP *)nbp->rp; | 
 |  | 
 | 	if (ipv6anylocal(ifc, np->src)) { | 
 | 		netlog(f, Logicmp, "send icmpttlexceeded6 -> s%I d%I\n", | 
 | 		       p->src, p->dst); | 
 | 	} else { | 
 | 		netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n", | 
 | 		       p->src, p->dst); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	memmove(np->dst, p->src, IPaddrlen); | 
 | 	np->type = TimeExceedV6; | 
 | 	np->code = 0; | 
 | 	memmove(nbp->rp + sizeof(struct IPICMP), bp->rp, | 
 | 	        sz - sizeof(struct IPICMP)); | 
 | 	set_cksum(nbp); | 
 | 	np->ttl = HOP_LIMIT; | 
 | 	np->vcf[0] = 0x06 << 4; | 
 | 	ipriv->out[TimeExceedV6]++; | 
 | 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL); | 
 | } | 
 |  | 
 | void icmppkttoobig6(struct Fs *f, struct Ipifc *ifc, struct block *bp) | 
 | { | 
 | 	struct block *nbp; | 
 | 	struct IPICMP *np; | 
 | 	struct ip6hdr *p; | 
 | 	int osz = BLEN(bp); | 
 | 	int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU); | 
 | 	struct Proto *icmp = f->t2p[ICMPv6]; | 
 | 	Icmppriv6 *ipriv = icmp->priv; | 
 |  | 
 | 	p = (struct ip6hdr *)bp->rp; | 
 |  | 
 | 	if (isv6mcast(p->src)) | 
 | 		return; | 
 |  | 
 | 	nbp = newIPICMP(sz); | 
 | 	np = (struct IPICMP *)nbp->rp; | 
 |  | 
 | 	if (!ipv6anylocal(ifc, np->src)) { | 
 | 		netlog(f, Logicmp, "icmppkttoobig6 fail -> s%I d%I\n", | 
 | 		       p->src, p->dst); | 
 | 		return; | 
 | 	} | 
 | 	netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n", | 
 | 	       p->src, p->dst); | 
 |  | 
 | 	memmove(np->dst, p->src, IPaddrlen); | 
 | 	np->type = PacketTooBigV6; | 
 | 	np->code = 0; | 
 | 	hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize); | 
 | 	memmove(nbp->rp + sizeof(struct IPICMP), bp->rp, | 
 | 			sz - sizeof(struct IPICMP)); | 
 | 	set_cksum(nbp); | 
 | 	np->ttl = HOP_LIMIT; | 
 | 	np->vcf[0] = 0x06 << 4; | 
 | 	ipriv->out[PacketTooBigV6]++; | 
 | 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL); | 
 | } | 
 |  | 
 | /* | 
 |  * RFC 2461, pages 39-40, pages 57-58. | 
 |  */ | 
 | static int valid(struct Proto *icmp, struct Ipifc *ifc, | 
 |                  struct block *bp, Icmppriv6 * ipriv) | 
 | { | 
 | 	int sz, osz, unsp, n, ttl, iplen; | 
 | 	int pktsz = BLEN(bp); | 
 | 	uint8_t *packet = bp->rp; | 
 | 	struct IPICMP *p = (struct IPICMP *)packet; | 
 | 	struct Ndpkt *np; | 
 |  | 
 | 	n = blocklen(bp); | 
 | 	if (n < sizeof(struct IPICMP)) { | 
 | 		ipriv->stats[HlenErrs6]++; | 
 | 		netlog(icmp->f, Logicmp, "icmp hlen %d\n", n); | 
 | 		goto err; | 
 | 	} | 
 |  | 
 | 	iplen = nhgets(p->ploadlen); | 
 | 	if (iplen > n - IPV6HDR_LEN || (iplen % 1)) { | 
 | 		ipriv->stats[LenErrs6]++; | 
 | 		netlog(icmp->f, Logicmp, "icmp length %d\n", iplen); | 
 | 		goto err; | 
 | 	} | 
 | 	// Rather than construct explicit pseudoheader, overwrite IPv6 header | 
 | 	if (p->proto != ICMPv6) { | 
 | 		// This code assumes no extension headers!!! | 
 | 		netlog(icmp->f, Logicmp, "icmp error: extension header\n"); | 
 | 		goto err; | 
 | 	} | 
 | 	memset(packet, 0, 4); | 
 | 	ttl = p->ttl; | 
 | 	p->ttl = p->proto; | 
 | 	p->proto = 0; | 
 | 	if (ptclcsum(bp, 0, iplen + IPV6HDR_LEN)) { | 
 | 		ipriv->stats[CsumErrs6]++; | 
 | 		netlog(icmp->f, Logicmp, "icmp checksum error\n"); | 
 | 		goto err; | 
 | 	} | 
 | 	p->proto = p->ttl; | 
 | 	p->ttl = ttl; | 
 |  | 
 | 	/* additional tests for some pkt types */ | 
 | 	if ((p->type == NbrSolicit) || | 
 | 		(p->type == NbrAdvert) || | 
 | 		(p->type == RouterAdvert) || | 
 | 		(p->type == RouterSolicit) || (p->type == RedirectV6)) { | 
 |  | 
 | 		if (p->ttl != HOP_LIMIT) { | 
 | 			ipriv->stats[HoplimErrs6]++; | 
 | 			goto err; | 
 | 		} | 
 | 		if (p->code != 0) { | 
 | 			ipriv->stats[IcmpCodeErrs6]++; | 
 | 			goto err; | 
 | 		} | 
 |  | 
 | 		switch (p->type) { | 
 | 		case NbrSolicit: | 
 | 		case NbrAdvert: | 
 | 			np = (struct Ndpkt *)p; | 
 | 			if (isv6mcast(np->target)) { | 
 | 				ipriv->stats[TargetErrs6]++; | 
 | 				goto err; | 
 | 			} | 
 | 			if (optexsts(np) && (np->olen == 0)) { | 
 | 				ipriv->stats[OptlenErrs6]++; | 
 | 				goto err; | 
 | 			} | 
 |  | 
 | 			if (p->type == NbrSolicit) { | 
 | 				if (ipcmp(np->src, v6Unspecified) == 0) { | 
 | 					if (!issmcast(np->dst) || optexsts(np)) | 
 | 					{ | 
 | 						ipriv->stats[AddrmxpErrs6]++; | 
 | 						goto err; | 
 | 					} | 
 | 				} | 
 | 			} | 
 |  | 
 | 			if (p->type == NbrAdvert) { | 
 | 				if ((isv6mcast(np->dst)) && | 
 | 				    (nhgets(np->icmpid) & Sflag)) { | 
 | 					ipriv->stats[AddrmxpErrs6]++; | 
 | 					goto err; | 
 | 				} | 
 | 			} | 
 | 			break; | 
 |  | 
 | 		case RouterAdvert: | 
 | 			if (pktsz - sizeof(struct ip6hdr) < 16) { | 
 | 				ipriv->stats[HlenErrs6]++; | 
 | 				goto err; | 
 | 			} | 
 | 			if (!islinklocal(p->src)) { | 
 | 				ipriv->stats[RouterAddrErrs6]++; | 
 | 				goto err; | 
 | 			} | 
 | 			sz = sizeof(struct IPICMP) + 8; | 
 | 			while ((sz + 1) < pktsz) { | 
 | 				osz = *(packet + sz + 1); | 
 | 				if (osz <= 0) { | 
 | 					ipriv->stats[OptlenErrs6]++; | 
 | 					goto err; | 
 | 				} | 
 | 				sz += 8 * osz; | 
 | 			} | 
 | 			break; | 
 |  | 
 | 		case RouterSolicit: | 
 | 			if (pktsz - sizeof(struct ip6hdr) < 8) { | 
 | 				ipriv->stats[HlenErrs6]++; | 
 | 				goto err; | 
 | 			} | 
 | 			unsp = (ipcmp(p->src, v6Unspecified) == 0); | 
 | 			sz = sizeof(struct IPICMP) + 8; | 
 | 			while ((sz + 1) < pktsz) { | 
 | 				osz = *(packet + sz + 1); | 
 | 				if ((osz <= 0) || | 
 | 				    (unsp && (*(packet + sz) == slladd))) { | 
 | 					ipriv->stats[OptlenErrs6]++; | 
 | 					goto err; | 
 | 				} | 
 | 				sz += 8 * osz; | 
 | 			} | 
 | 			break; | 
 |  | 
 | 		case RedirectV6: | 
 | 			//to be filled in | 
 | 			break; | 
 |  | 
 | 		default: | 
 | 			goto err; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 1; | 
 |  | 
 | err: | 
 | 	ipriv->stats[InErrors6]++; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int targettype(struct Fs *f, struct Ipifc *ifc, uint8_t * target) | 
 | { | 
 | 	struct Iplifc *lifc; | 
 | 	int t; | 
 |  | 
 | 	rlock(&ifc->rwlock); | 
 | 	if (ipproxyifc(f, ifc, target)) { | 
 | 		runlock(&ifc->rwlock); | 
 | 		return t_uniproxy; | 
 | 	} | 
 |  | 
 | 	for (lifc = ifc->lifc; lifc; lifc = lifc->next) { | 
 | 		if (ipcmp(lifc->local, target) == 0) { | 
 | 			t = (lifc->tentative) ? t_unitent : t_unirany; | 
 | 			runlock(&ifc->rwlock); | 
 | 			return t; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	runlock(&ifc->rwlock); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void icmpiput6(struct Proto *icmp, struct Ipifc *ipifc, struct block *bp) | 
 | { | 
 | 	uint8_t *packet = bp->rp; | 
 | 	struct IPICMP *p = (struct IPICMP *)packet; | 
 | 	Icmppriv6 *ipriv = icmp->priv; | 
 | 	struct block *r; | 
 | 	struct Proto *pr; | 
 | 	char *msg, m2[128]; | 
 | 	struct Ndpkt *np; | 
 | 	uint8_t pktflags; | 
 | 	uint8_t lsrc[IPaddrlen]; | 
 | 	int refresh = 1; | 
 | 	struct Iplifc *lifc; | 
 |  | 
 | 	if (!valid(icmp, ipifc, bp, ipriv)) | 
 | 		goto raise; | 
 |  | 
 | 	if (p->type <= Maxtype6) | 
 | 		ipriv->in[p->type]++; | 
 | 	else | 
 | 		goto raise; | 
 |  | 
 | 	switch (p->type) { | 
 | 	case EchoRequestV6: | 
 | 		r = mkechoreply6(bp); | 
 | 		ipriv->out[EchoReply]++; | 
 | 		ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, NULL); | 
 | 		break; | 
 |  | 
 | 	case UnreachableV6: | 
 | 		if (p->code > 4) | 
 | 			msg = unreachcode[icmp6_unkn_code]; | 
 | 		else | 
 | 			msg = unreachcode[p->code]; | 
 |  | 
 | 		bp->rp += sizeof(struct IPICMP); | 
 | 		if (blocklen(bp) < 8) { | 
 | 			ipriv->stats[LenErrs6]++; | 
 | 			goto raise; | 
 | 		} | 
 | 		p = (struct IPICMP *)bp->rp; | 
 | 		pr = Fsrcvpcolx(icmp->f, p->proto); | 
 | 		if (pr != NULL && pr->advise != NULL) { | 
 | 			(*pr->advise) (pr, bp, msg); | 
 | 			return; | 
 | 		} | 
 |  | 
 | 		bp->rp -= sizeof(struct IPICMP); | 
 | 		goticmpkt6(icmp, bp, 0); | 
 | 		break; | 
 |  | 
 | 	case TimeExceedV6: | 
 | 		if (p->code == 0) { | 
 | 			snprintf(m2, sizeof(m2), "ttl exceeded at %I", p->src); | 
 |  | 
 | 			bp->rp += sizeof(struct IPICMP); | 
 | 			if (blocklen(bp) < 8) { | 
 | 				ipriv->stats[LenErrs6]++; | 
 | 				goto raise; | 
 | 			} | 
 | 			p = (struct IPICMP *)bp->rp; | 
 | 			pr = Fsrcvpcolx(icmp->f, p->proto); | 
 | 			if (pr != NULL && pr->advise != NULL) { | 
 | 				(*pr->advise) (pr, bp, m2); | 
 | 				return; | 
 | 			} | 
 | 			bp->rp -= sizeof(struct IPICMP); | 
 | 		} | 
 |  | 
 | 		goticmpkt6(icmp, bp, 0); | 
 | 		break; | 
 |  | 
 | 	case RouterAdvert: | 
 | 	case RouterSolicit: | 
 | 		/* using lsrc as a temp, munge hdr for goticmp6 | 
 | 		   memmove(lsrc, p->src, IPaddrlen); | 
 | 		   memmove(p->src, p->dst, IPaddrlen); | 
 | 		   memmove(p->dst, lsrc, IPaddrlen); */ | 
 |  | 
 | 		goticmpkt6(icmp, bp, p->type); | 
 | 		break; | 
 |  | 
 | 	case NbrSolicit: | 
 | 		np = (struct Ndpkt *)p; | 
 | 		pktflags = 0; | 
 | 		switch (targettype(icmp->f, ipifc, np->target)) { | 
 | 			case t_unirany: | 
 | 				pktflags |= Oflag; | 
 | 				/* fall through */ | 
 |  | 
 | 			case t_uniproxy: | 
 | 				if (ipcmp(np->src, v6Unspecified) != 0) { | 
 | 					arpenter(icmp->f, V6, np->src, | 
 | 						 np->lnaddr, 8 * np->olen - 2, | 
 | 						 0); | 
 | 					pktflags |= Sflag; | 
 | 				} | 
 | 				if (ipv6local(ipifc, lsrc)) { | 
 | 					icmpna(icmp->f, lsrc, | 
 | 					       (ipcmp(np->src, v6Unspecified) | 
 | 						== 0) ? v6allnodesL : np->src, | 
 | 					       np->target, ipifc->mac, | 
 | 					       pktflags); | 
 | 				} else | 
 | 					freeblist(bp); | 
 | 				break; | 
 |  | 
 | 			case t_unitent: | 
 | 				/* not clear what needs to be done. send up an | 
 | 				 * icmp mesg saying don't use this address? */ | 
 |  | 
 | 			default: | 
 | 				freeblist(bp); | 
 | 		} | 
 |  | 
 | 		break; | 
 |  | 
 | 	case NbrAdvert: | 
 | 		np = (struct Ndpkt *)p; | 
 |  | 
 | 		/* if the target address matches one of the local interface | 
 | 		 * address and the local interface address has tentative bit | 
 | 		 * set, then insert into ARP table. this is so the duplication | 
 | 		 * address detection part of ipconfig can discover duplication | 
 | 		 * through the arp table | 
 | 		 */ | 
 | 		lifc = iplocalonifc(ipifc, np->target); | 
 | 		if (lifc && lifc->tentative) | 
 | 			refresh = 0; | 
 | 		arpenter(icmp->f, V6, np->target, np->lnaddr, 8 * np->olen - 2, | 
 | 				 refresh); | 
 | 		freeblist(bp); | 
 | 		break; | 
 |  | 
 | 	case PacketTooBigV6: | 
 |  | 
 | 	default: | 
 | 		goticmpkt6(icmp, bp, 0); | 
 | 		break; | 
 | 	} | 
 | 	return; | 
 |  | 
 | raise: | 
 | 	freeblist(bp); | 
 |  | 
 | } | 
 |  | 
 | int icmpstats6(struct Proto *icmp6, char *buf, int len) | 
 | { | 
 | 	Icmppriv6 *priv; | 
 | 	char *p, *e; | 
 | 	int i; | 
 |  | 
 | 	priv = icmp6->priv; | 
 | 	p = buf; | 
 | 	e = p + len; | 
 | 	for (i = 0; i < Nstats6; i++) | 
 | 		p = seprintf(p, e, "%s: %u\n", statnames6[i], priv->stats[i]); | 
 | 	for (i = 0; i <= Maxtype6; i++) { | 
 | 		if (icmpnames6[i]) | 
 | 			p = seprintf(p, e, "%s: %u %u\n", icmpnames6[i], | 
 | 				     priv->in[i], priv->out[i]); | 
 | 		else | 
 | 			p = seprintf(p, e, "%d: %u %u\n", i, priv->in[i], | 
 | 				     priv->out[i]); | 
 | 	} | 
 | 	return p - buf; | 
 | } | 
 |  | 
 | // need to import from icmp.c | 
 | extern int icmpstate(struct conv *c, char *state, int n); | 
 | extern void icmpannounce(struct conv *c, char **argv, int argc); | 
 | extern void icmpconnect(struct conv *c, char **argv, int argc); | 
 | extern void icmpclose(struct conv *c); | 
 |  | 
 | void icmp6init(struct Fs *fs) | 
 | { | 
 | 	struct Proto *icmp6 = kzmalloc(sizeof(struct Proto), 0); | 
 |  | 
 | 	icmp6->priv = kzmalloc(sizeof(Icmppriv6), 0); | 
 | 	icmp6->name = "icmpv6"; | 
 | 	icmp6->connect = icmpconnect; | 
 | 	icmp6->announce = icmpannounce; | 
 | 	icmp6->state = icmpstate; | 
 | 	icmp6->create = icmpcreate6; | 
 | 	icmp6->close = icmpclose; | 
 | 	icmp6->rcv = icmpiput6; | 
 | 	icmp6->stats = icmpstats6; | 
 | 	icmp6->ctl = icmpctl6; | 
 | 	icmp6->advise = icmpadvise6; | 
 | 	icmp6->gc = NULL; | 
 | 	icmp6->ipproto = ICMPv6; | 
 | 	icmp6->nc = 16; | 
 | 	icmp6->ptclsize = sizeof(Icmpcb6); | 
 |  | 
 | 	Fsproto(fs, icmp6); | 
 | } |