code: plan9front

Download patch

ref: 834b7f36cf4da1f4c08d27f1db9e0eed8717c542
parent: 73e115aab038880a68c9f73b5c49d8fa12d0e5e2
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Dec 17 19:40:23 EST 2022

devip: fix icmp bugs

icmpdontfrag() was not working properly, need to pass the
gating source interface. in fact, we now always pass the source
interface to all icmp*() functions, which is used to
determine source ip address of the icmp reply.

also dont generate a icmp response for packets going to
non-unicast addresses (such as broadcast).

increase the amount of icmp response payload, but keep
icmp responses below the minimum ipv4 mtu (68 bytes).

regularize icmpv6 function names.

move icmp unreachable codes to icmpv6.c.

provide the mtu value for icmppkttoobig6().

dont advise announced udp connections.

avoid code duplication in icmp.c and icmpv6.c,
by having single send function with type, code and arg
parameters.

maintain statistics for sent ipv4 icmp types.

avoid route lookup in ipout*() by passing Routehint* to
icmpnohost*().

iladvise()... more like ill advice.

--- a/sys/src/9/ip/arp.c
+++ b/sys/src/9/ip/arp.c
@@ -562,7 +562,7 @@
 		return;
 send:
 	if(!waserror()){
-		icmpns(f, src, SRC_UNI, targ, TARG_MULTI, ifc->mac);
+		icmpns6(f, src, SRC_UNI, targ, TARG_MULTI, ifc->mac);
 		poperror();
 	}
 }
@@ -606,24 +606,27 @@
 static void
 drop(Fs *f, Block *bp)
 {
-	Block *next;
-	Ipifc *ifc;
+	Routehint rh;
 	Route *r;
+	Ipifc *ifc;
+	Block *next;
 
 	for(; bp != nil; bp = next){
 		next = bp->list;
 		bp->list = nil;
 
+		rh.r = nil;
+		rh.a = nil;
 		if((bp->rp[0]&0xF0) == IP_VER4)
-			r = v4lookup(f, ((Ip4hdr*)bp->rp)->src, ((Ip4hdr*)bp->rp)->dst, nil);
+			r = v4lookup(f, ((Ip4hdr*)bp->rp)->src, ((Ip4hdr*)bp->rp)->dst, &rh);
 		else
-			r = v6lookup(f, ((Ip6hdr*)bp->rp)->src, ((Ip6hdr*)bp->rp)->dst, nil);
+			r = v6lookup(f, ((Ip6hdr*)bp->rp)->src, ((Ip6hdr*)bp->rp)->dst, &rh);
 		if(r != nil && (ifc = r->ifc) != nil && canrlock(ifc)){
 			if(!waserror()){
 				if((bp->rp[0]&0xF0) == IP_VER4)
-					icmpnohost(f, ifc, bp);
+					icmpnohost(f, ifc, bp, &rh);
 				else
-					icmphostunr6(f, ifc, bp, Icmp6_adr_unreach, (r->type & Runi) != 0);
+					icmpnohost6(f, ifc, bp, &rh);
 				poperror();
 			}
 			runlock(ifc);
--- a/sys/src/9/ip/esp.c
+++ b/sys/src/9/ip/esp.c
@@ -433,7 +433,7 @@
  * pass the result up the spi's Conv's read queue.
  */
 void
-espiput(Proto *esp, Ipifc*, Block *bp)
+espiput(Proto *esp, Ipifc *ifc, Block *bp)
 {
 	int payload, nexthdr;
 	uchar *auth, *espspi;
@@ -462,7 +462,7 @@
 		qunlock(esp);
 		netlog(f, Logesp, "esp: no conv %I -> %I!%lud\n", vers.raddr,
 			vers.laddr, vers.spi);
-		icmpnoconv(f, bp);
+		icmpnoconv(f, ifc, bp);
 		freeblist(bp);
 		return;
 	}
--- a/sys/src/9/ip/ethermedium.c
+++ b/sys/src/9/ip/ethermedium.c
@@ -712,7 +712,7 @@
 		return;
 
 	if(!lifc->tentative){
-		icmpna(f, lifc->local, v6allnodesL, ip, ifc->mac, 1<<5);
+		icmpna6(f, lifc->local, v6allnodesL, ip, ifc->mac, 1<<5);
 		return;
 	}
 
@@ -726,7 +726,7 @@
 		remroute(f, a, IPallbits, v6Unspecified, IPallbits, ip, Rmulti, ifc, tdad);
 		nexterror();
 	}
-	icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac);
+	icmpns6(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac);
 	poperror();
 	remroute(f, a, IPallbits, v6Unspecified, IPallbits, ip, Rmulti, ifc, tdad);
 }
--- a/sys/src/9/ip/icmp.c
+++ b/sys/src/9/ip/icmp.c
@@ -66,7 +66,7 @@
 	ICMP_IPSIZE	= 20,
 	ICMP_HDRSIZE	= 8,
 
-	MinAdvise	= ICMP_IPSIZE+4,	/* minimum needed for us to advise another protocol */ 
+	MinAdvise	= IP4HDR+4,	/* minimum needed for us to advise another protocol */
 };
 
 enum
@@ -212,93 +212,86 @@
 	return ipforme(f, addr) == Runi;
 }
 
-void
-icmpttlexceeded(Fs *f, Ipifc *ifc, Block *bp)
+/*
+ *  Send a ICMP packet back of type/code/arg in response to packet bp
+ *  which was received on ifc. The route hint rh is valid for the
+ *  reverse route.
+ */
+static void
+icmpsend(Fs *f, Ipifc *ifc, Block *bp, int type, int code, int arg, Routehint *rh)
 {
-	Block	*nbp;
-	Icmp	*p, *np;
+	Proto	*icmp;
 	uchar	ia[IPv4addrlen];
+	Block	*nbp;
+	Icmp	*np, *p;
+	int	sz, osz;
 
+	osz = BLEN(bp);
+	if(osz < IP4HDR)
+		return;
+
 	p = (Icmp *)bp->rp;
-	if(isv4mcast(p->dst) || !ip4reply(f, p->src) || !ipv4local(ifc, ia, 0, p->src))
+	if(!ip4reply(f, p->dst) || !ip4reply(f, p->src) || !ipv4local(ifc, ia, 0, p->src))
 		return;
 
-	netlog(f, Logicmp, "sending icmpttlexceeded %V -> src %V dst %V\n",
-		ia, p->src, p->dst);
+	netlog(f, Logicmp, "icmpsend %s (%d) %d ia %V -> src %V dst %V\n",
+		icmpnames[type], type, code, ia, p->src, p->dst);
 
-	nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
-	nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
+	sz = MIN(ICMP_IPSIZE + ICMP_HDRSIZE + osz, v4MINTU);
+	nbp = allocb(sz);
+	nbp->wp += sz;
+	memset(nbp->rp, 0, sz);
 	np = (Icmp *)nbp->rp;
 	np->vihl = IP_VER4;
 	memmove(np->src, ia, sizeof(np->src));
 	memmove(np->dst, p->src, sizeof(np->dst));
-	memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
-	np->type = TimeExceed;
-	np->code = 0;
+	memmove(np->data, bp->rp, sz - ICMP_IPSIZE - ICMP_HDRSIZE);
+	np->type = type;
+	np->code = code;
 	np->proto = IP_ICMPPROTO;
 	hnputs(np->icmpid, 0);
-	hnputs(np->seq, 0);
+	hnputs(np->seq, arg);
 	memset(np->cksum, 0, sizeof(np->cksum));
 	hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
-	ipoput4(f, nbp, nil, MAXTTL, DFLTTOS, nil);
-}
 
-static void
-icmpunreachable(Fs *f, Ipifc *ifc, Block *bp, int code, int seq)
-{
-	Block	*nbp;
-	Icmp	*p, *np;
-	uchar	ia[IPv4addrlen];
+	icmp = f->t2p[IP_ICMPPROTO];
+	((Icmppriv*)icmp->priv)->out[type]++;
 
-	p = (Icmp *)bp->rp;
-	if(isv4mcast(p->dst) || !ip4reply(f, p->src))
-		return;
+	if(rh != nil && rh->r != nil && (rh->r->type & Runi) != 0){
+		np = (Icmp *)nbp->rp;
+		np->vihl = IP_VER4|IP_HLEN4;
+		np->tos = DFLTTOS;
+		np->ttl = MAXTTL;
 
-	if(ifc == nil){
-		if(!ipforme(f, p->dst))
-			return;
-		memmove(ia, p->dst, sizeof(p->dst));
-	} else {
-		if(!ipv4local(ifc, ia, 0, p->src))
-			return;
+		(*icmp->rcv)(icmp, ifc, nbp);
+		return;
 	}
 
-	netlog(f, Logicmp, "sending icmpunreachable %V -> src %V dst %V\n",
-		ia, p->src, p->dst);
+	ipoput4(f, nbp, nil, MAXTTL, DFLTTOS, rh);
+}
 
-	nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
-	nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
-	np = (Icmp *)nbp->rp;
-	np->vihl = IP_VER4;
-	memmove(np->src, ia, sizeof(np->src));
-	memmove(np->dst, p->src, sizeof(np->dst));
-	memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
-	np->type = Unreachable;
-	np->code = code;
-	np->proto = IP_ICMPPROTO;
-	hnputs(np->icmpid, 0);
-	hnputs(np->seq, seq);
-	memset(np->cksum, 0, sizeof(np->cksum));
-	hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
-	ipoput4(f, nbp, nil, MAXTTL, DFLTTOS, nil);
+void
+icmpnohost(Fs *f, Ipifc *ifc, Block *bp, Routehint *rh)
+{
+	icmpsend(f, ifc, bp, Unreachable, 1, 0, rh);
 }
 
 void
-icmpnohost(Fs *f, Ipifc *ifc, Block *bp)
+icmpnoconv(Fs *f, Ipifc *ifc, Block *bp)
 {
-	icmpunreachable(f, ifc, bp, 1, 0);
+	icmpsend(f, ifc, bp, Unreachable, 3, 0, nil);
 }
 
 void
-icmpnoconv(Fs *f, Block *bp)
+icmpcantfrag(Fs *f, Ipifc *ifc, Block *bp, int mtu)
 {
-	icmpunreachable(f, nil, bp, 3, 0);
+	icmpsend(f, ifc, bp, Unreachable, 4, mtu, nil);
 }
 
 void
-icmpcantfrag(Fs *f, Block *bp, int mtu)
+icmpttlexceeded(Fs *f, Ipifc *ifc, Block *bp)
 {
-	icmpunreachable(f, nil, bp, 4, mtu);
+	icmpsend(f, ifc, bp, TimeExceed, 0, 0, nil);
 }
 
 static void
@@ -479,7 +472,7 @@
  * and send the advice to ip4.
  */
 void
-icmpproxyadvice(Fs *f, Block *bp, Ipifc *ifc, uchar *ip4)
+icmpproxyadvice(Fs *f, Ipifc *ifc, Block *bp, uchar *ip4)
 {
 	Icmp	*p;
 	int	hop;
@@ -543,7 +536,7 @@
 		hnputs_csum(p->icmpid, q->forward.rport, p->cksum);
 		qunlock(icmp);
 
-		icmpproxyadvice(icmp->f, bp, ifc, p->src);
+		icmpproxyadvice(icmp->f, ifc, bp, p->src);
 		return;
 	}
 	for(c = icmp->conv; (s = *c) != nil; c++){
--- a/sys/src/9/ip/icmp6.c
+++ b/sys/src/9/ip/icmp6.c
@@ -67,10 +67,22 @@
 	Maxtype6	= 137,
 };
 
+/* icmpv6 unreachability codes */
 enum {
-	MinAdvise	= IP6HDR+4,	/* minimum needed for us to advise another protocol */ 
+	Icmp6_no_route		= 0,
+	Icmp6_ad_prohib		= 1,
+	Icmp6_out_src_scope	= 2,
+	Icmp6_adr_unreach	= 3,
+	Icmp6_port_unreach	= 4,
+	Icmp6_gress_src_fail	= 5,
+	Icmp6_rej_route		= 6,
+	Icmp6_unknown		= 7,  /* our own invention for internal use */
 };
 
+enum {
+	MinAdvise	= IP6HDR+4,	/* minimum needed for us to advise another protocol */
+};
+
 /* on-the-wire packet formats */
 typedef struct IPICMP IPICMP;
 typedef struct Ndpkt Ndpkt;
@@ -346,7 +358,7 @@
  * 	and tuni == TARG_UNI => neighbor reachability.
  */
 void
-icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac)
+icmpns6(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac)
 {
 	Block *nbp;
 	Ndpkt *np;
@@ -386,7 +398,7 @@
  * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
  */
 void
-icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags)
+icmpna6(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags)
 {
 	Block *nbp;
 	Ndpkt *np;
@@ -414,108 +426,82 @@
 	ipoput6(f, nbp, nil, MAXTTL, DFLTTOS, nil);
 }
 
-void
-icmphostunr6(Fs *f, Ipifc *ifc, Block *bp, int code, int tome)
+/*
+ *  Send a ICMPv6 packet back of type/code/arg in response to packet bp
+ *  which was received on ifc. The route hint rh is valid for the
+ *  reverse route.
+ */
+static void
+icmpsend6(Fs *f, Ipifc *ifc, Block *bp, int type, int code, int arg, Routehint *rh)
 {
-	int osz = BLEN(bp);
-	int sz = MIN(IPICMPSZ + osz, v6MINTU);
+	Proto *icmp;
+	uchar ia[IPaddrlen];
+	Ip6hdr *p;
 	Block *nbp;
 	IPICMP *np;
-	Ip6hdr *p;
-	Proto *icmp = f->t2p[ICMPv6];
-	Icmppriv6 *ipriv = icmp->priv;
-	uchar ia[IPaddrlen];
+	int sz, osz;
 
+	osz = BLEN(bp);
+	if(osz < IP6HDR)
+		return;
+
 	p = (Ip6hdr *)bp->rp;
 	if(isv6mcast(p->dst) || isv6mcast(p->src) || !ipv6local(ifc, ia, 0, p->src))
 		return;
 
-	netlog(f, Logicmp, "send icmphostunr %I -> src %I dst %I\n",
-		ia, p->src, p->dst);
+	netlog(f, Logicmp, "icmpsend6 %s (%d) %d ia %I -> src %I dst %I\n",
+		icmpnames6[type], type, code, ia, p->src, p->dst);
 
+	sz = MIN(IPICMPSZ + osz, v6MINTU);
 	nbp = newIPICMP(sz);
 	np = (IPICMP *)nbp->rp;
 	ipmove(np->src, ia);
 	ipmove(np->dst, p->src);
-	np->type = UnreachableV6;
+	np->type = type;
 	np->code = code;
+	hnputs(np->icmpid, 0);
+	hnputs(np->seq, arg);
 	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
 	set_cksum(nbp);
-	ipriv->out[UnreachableV6]++;
 
-	if(tome){
+	icmp = f->t2p[ICMPv6];
+	((Icmppriv6*)icmp->priv)->out[type]++;
+
+	if(rh != nil && rh->r != nil && (rh->r->type & Runi) != 0){
 		np = (IPICMP *)nbp->rp;
 		np->vcf[0] = IP_VER6 | DFLTTOS>>4;
 		np->vcf[1] = DFLTTOS<<4;
 		np->ttl = MAXTTL;
 
-		ipiput6(f, ifc, nbp);
+		(*icmp->rcv)(icmp, ifc, nbp);
 		return;
 	}
-	ipoput6(f, nbp, nil, MAXTTL, DFLTTOS, nil);
+
+	ipoput6(f, nbp, nil, MAXTTL, DFLTTOS, rh);
 }
 
 void
-icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp)
+icmpnohost6(Fs *f, Ipifc *ifc, Block *bp, Routehint *rh)
 {
-	int osz = BLEN(bp);
-	int sz = MIN(IPICMPSZ + osz, v6MINTU);
-	Block *nbp;
-	IPICMP *np;
-	Ip6hdr *p;
-	Proto *icmp = f->t2p[ICMPv6];
-	Icmppriv6 *ipriv = icmp->priv;
-	uchar ia[IPaddrlen];
+	icmpsend6(f, ifc, bp, UnreachableV6, Icmp6_adr_unreach, 0, rh);
+}
 
-	p = (Ip6hdr *)bp->rp;
-	if(isv6mcast(p->dst) || isv6mcast(p->src) || !ipv6local(ifc, ia, 0, p->src))
-		return;
-
-	netlog(f, Logicmp, "send icmpttlexceeded6 %I -> src %I dst %I\n",
-		ia, p->src, p->dst);
-
-	nbp = newIPICMP(sz);
-	np = (IPICMP *) nbp->rp;
-	ipmove(np->src, ia);
-	ipmove(np->dst, p->src);
-	np->type = TimeExceedV6;
-	np->code = 0;
-	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
-	set_cksum(nbp);
-	ipriv->out[TimeExceedV6]++;
-	ipoput6(f, nbp, nil, MAXTTL, DFLTTOS, nil);
+void
+icmpnoconv6(Fs *f, Ipifc *ifc, Block *bp)
+{
+	icmpsend6(f, ifc, bp, UnreachableV6, Icmp6_port_unreach, 0, nil);
 }
 
 void
-icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp)
+icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp)
 {
-	int osz = BLEN(bp);
-	int sz = MIN(IPICMPSZ + osz, v6MINTU);
-	Block *nbp;
-	IPICMP *np;
-	Ip6hdr *p;
-	Proto *icmp = f->t2p[ICMPv6];
-	Icmppriv6 *ipriv = icmp->priv;
-	uchar ia[IPaddrlen];
+	icmpsend6(f, ifc, bp, TimeExceedV6, 0, 0, nil);
+}
 
-	p = (Ip6hdr *)bp->rp;
-	if(isv6mcast(p->dst) || isv6mcast(p->src) || !ipv6local(ifc, ia, 0, p->src))
-		return;
-
-	netlog(f, Logicmp, "send icmppkttoobig6 %I -> src %I dst %I\n",
-		ia, p->src, p->dst);
-
-	nbp = newIPICMP(sz);
-	np = (IPICMP *)nbp->rp;
-	ipmove(np->src, ia);
-	ipmove(np->dst, p->src);
-	np->type = PacketTooBigV6;
-	np->code = 0;
-	hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
-	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
-	set_cksum(nbp);
-	ipriv->out[PacketTooBigV6]++;
-	ipoput6(f, nbp, nil, MAXTTL, DFLTTOS, nil);
+void
+icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp, int mtu)
+{
+	icmpsend6(f, ifc, bp, PacketTooBigV6, 0, mtu, nil);
 }
 
 /*
@@ -771,7 +757,7 @@
 				pktflags |= Sflag;
 			} else
 				ipmove(ia, np->target);
-			icmpna(icmp->f, ia, (pktflags & Sflag)? np->src: v6allnodesL,
+			icmpna6(icmp->f, ia, (pktflags & Sflag)? np->src: v6allnodesL,
 				np->target, ifc->mac, pktflags);
 			break;
 		case Tunitent:
--- a/sys/src/9/ip/il.c
+++ b/sys/src/9/ip/il.c
@@ -1004,7 +1004,7 @@
 	hnputs(ih->illen, IL_HDRSIZE);
 	ih->frag[0] = 0;
 	ih->frag[1] = 0;
-	if(inih) {
+	if(inih != nil) {
 		hnputl(ih->dst, nhgetl(inih->src));
 		hnputl(ih->src, nhgetl(inih->dst));
 		hnputs(ih->ilsrc, nhgets(inih->ildst));
@@ -1317,39 +1317,41 @@
 void
 iladvise(Proto *il, Block *bp, Ipifc*, char *msg)
 {
+	uchar source[IPaddrlen], dest[IPaddrlen];
+	ushort psource, pdest;
+	Iphash *iph;
 	Ilhdr *h;
 	Ilcb *ic;		
-	uchar source[IPaddrlen], dest[IPaddrlen];
-	ushort psource;
-	Conv *s, **p;
+	Conv *s;
 
-	h = (Ilhdr*)(bp->rp);
+	if(BLEN(bp) < IL_IPSIZE+IL_HDRSIZE-8){
+		freeblist(bp);
+		return;
+	}
 
+	h = (Ilhdr*)(bp->rp);
 	v4tov6(dest, h->dst);
 	v4tov6(source, h->src);
+	pdest = nhgets(h->ildst);
 	psource = nhgets(h->ilsrc);
 
-
-	/* Look for a connection, unfortunately the destination port is missing */
+	/* Look for a connection (source/dest reversed; this is the original packet we sent) */
 	qlock(il);
-	for(p = il->conv; *p; p++) {
-		s = *p;
-		if(s->lport == psource)
-		if(ipcmp(s->laddr, source) == 0)
-		if(ipcmp(s->raddr, dest) == 0){
-			if(s->ignoreadvice)
-				break;
-			qunlock(il);
-			ic = (Ilcb*)s->ptcl;
-			switch(ic->state){
-			case Ilsyncer:
-				ilhangup(s, msg);
-				break;
-			}
-			freeblist(bp);
-			return;
-		}
-	}
+	iph = iphtlook(&((Ilpriv*)il->priv)->ht, dest, pdest, source, psource);
+	if(iph == nil || iph->match != IPmatchexact)
+		goto raise;
+	s = iphconv(iph);
+	if(s->ignoreadvice || s->state == Announced)
+		goto raise;
+	qlock(s);
+	qunlock(il);
+	ic = (Ilcb*)s->ptcl;
+	if(ic->state == Ilsyncer)
+		ilhangup(s, msg);
+	qunlock(s);
+	freeblist(bp);
+	return;
+raise:
 	qunlock(il);
 	freeblist(bp);
 }
--- a/sys/src/9/ip/ip.c
+++ b/sys/src/9/ip/ip.c
@@ -167,7 +167,7 @@
 	if(eh->frag[0] & (IP_DF>>8)){
 		ip->stats[FragFails]++;
 		ip->stats[OutDiscards]++;
-		icmpcantfrag(f, bp, medialen);
+		icmpcantfrag(f, gating!=nil? gating: ifc, bp, medialen);
 		netlog(f, Logip, "%V -> %V: can't fragment with DF flag set\n", eh->src, eh->dst);
 		goto raise;
 	}
--- a/sys/src/9/ip/ip.h
+++ b/sys/src/9/ip/ip.h
@@ -61,6 +61,7 @@
 	IP_FO=		0x1fff,		/* v4: Fragment offset */
 	IP4HDR=		IP_HLEN4<<2,	/* sizeof(Ip4hdr) */
 	IP_MAX=		64*1024,	/* Max. Internet packet size, v4 & v6 */
+	v4MINTU=	68,		/* The minimum MTU for IPv4 is 68 bytes */
 
 	/* 2^Lroot trees in the root table */
 	Lroot=		10,
@@ -737,11 +738,11 @@
  *  ip.c
  */
 extern void	iprouting(Fs*, int);
-extern void	icmpnohost(Fs*, Ipifc*, Block*);
-extern void	icmpnoconv(Fs*, Block*);
-extern void	icmpcantfrag(Fs*, Block*, int);
+extern void	icmpnohost(Fs*, Ipifc*, Block*, Routehint*);
+extern void	icmpnoconv(Fs*, Ipifc*, Block*);
+extern void	icmpcantfrag(Fs*, Ipifc*, Block*, int);
 extern void	icmpttlexceeded(Fs*, Ipifc*, Block*);
-extern void	icmpproxyadvice(Fs *, Block*, Ipifc*, uchar*);
+extern void	icmpproxyadvice(Fs*, Ipifc*, Block*, uchar*);
 
 extern ushort	ipcsum(uchar*);
 extern void	ipiput4(Fs*, Ipifc*, Block*);
--- a/sys/src/9/ip/ipv6.c
+++ b/sys/src/9/ip/ipv6.c
@@ -117,7 +117,7 @@
 		 * needed for nat.
 		 */
 		ip->stats[OutDiscards]++;
-		icmppkttoobig6(f, ifc, bp);
+		icmppkttoobig6(f, gating, bp, medialen);
 		netlog(f, Logip, "%I -> %I: gated pkts not fragmented\n", eh->src, eh->dst);
 		goto raise;
 	}
--- a/sys/src/9/ip/ipv6.h
+++ b/sys/src/9/ip/ipv6.h
@@ -66,16 +66,6 @@
 	/* various prefix lengths */
 	SOLN_PREF_LEN	= 13,
 
-	/* icmpv6 unreachability codes */
-	Icmp6_no_route		= 0,
-	Icmp6_ad_prohib		= 1,
-	Icmp6_out_src_scope	= 2,
-	Icmp6_adr_unreach	= 3,
-	Icmp6_port_unreach	= 4,
-	Icmp6_gress_src_fail	= 5,
-	Icmp6_rej_route		= 6,
-	Icmp6_unknown		= 7,  /* our own invention for internal use */
-
 	/* various flags & constants */
 	v6MINTU		= 1280,
 	IP6HDR		= 40,		/* sizeof(Ip6hdr) = 8 + 2*16 */
@@ -179,8 +169,9 @@
 extern int v6aLpreflen;
 
 void ipv62smcast(uchar *, uchar *);
-void icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac);
-void icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags);
+void icmpns6(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac);
+void icmpna6(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags);
+void icmpnohost6(Fs *f, Ipifc *ifc, Block *bp, Routehint *rh);
+void icmpnoconv6(Fs *f, Ipifc *ifc, Block *bp);
 void icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp);
-void icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp);
-void icmphostunr6(Fs *f, Ipifc *ifc, Block *bp, int code, int tome);
+void icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp, int mtu);
--- a/sys/src/9/ip/rudp.c
+++ b/sys/src/9/ip/rudp.c
@@ -514,7 +514,7 @@
 			laddr, lport);
 		uh->Unused = ottl;
 		hnputs(uh->udpplen, olen);
-		icmpnoconv(f, bp);
+		icmpnoconv(f, ifc, bp);
 		freeblist(bp);
 		return;
 	}
--- a/sys/src/9/ip/tcp.c
+++ b/sys/src/9/ip/tcp.c
@@ -3243,7 +3243,7 @@
 	/* Look for a connection (source/dest reversed; this is the original packet we sent) */
 	qlock(tcp);
 	iph = iphtlook(&((Tcppriv*)tcp->priv)->ht, dest, pdest, source, psource);
-	if(iph == nil)
+	if(iph == nil || iph->match != IPmatchexact)
 		goto raise;
 	if(iph->trans){
 		Translation *q;
@@ -3259,11 +3259,11 @@
 		hnputs(h4->tcpsport, q->forward.rport);
 		qunlock(tcp);
 
-		icmpproxyadvice(tcp->f, bp, ifc, h4->tcpsrc);
+		icmpproxyadvice(tcp->f, ifc, bp, h4->tcpsrc);
 		return;
 	}
 	s = iphconv(iph);
-	if(s->ignoreadvice || s->state == Closed)
+	if(s->ignoreadvice || s->state == Announced || s->state == Closed)
 		goto raise;
 	qlock(s);
 	qunlock(tcp);
--- a/sys/src/9/ip/udp.c
+++ b/sys/src/9/ip/udp.c
@@ -405,10 +405,10 @@
 
 		switch(version){
 		case V4:
-			icmpnoconv(f, bp);
+			icmpnoconv(f, ifc, bp);
 			break;
 		case V6:
-			icmphostunr6(f, ifc, bp, Icmp6_port_unreach, 0);
+			icmpnoconv6(f, ifc, bp);
 			break;
 		default:
 			panic("udpiput2: version %d", version);
@@ -572,7 +572,7 @@
 	/* Look for a connection (source/dest reversed; this is the original packet we sent) */
 	qlock(udp);
 	iph = iphtlook(&((Udppriv*)udp->priv)->ht, dest, pdest, source, psource);
-	if(iph == nil)
+	if(iph == nil || iph->match != IPmatchexact)
 		goto raise;
 	if(iph->trans){
 		Translation *q;
@@ -588,11 +588,11 @@
 		hnputs(h4->udpsport, q->forward.rport);
 		qunlock(udp);
 
-		icmpproxyadvice(udp->f, bp, ifc, h4->udpsrc);
+		icmpproxyadvice(udp->f, ifc, bp, h4->udpsrc);
 		return;
 	}
 	s = iphconv(iph);
-	if(s->ignoreadvice)
+	if(s->ignoreadvice || s->state == Announced)
 		goto raise;
 	qlock(s);
 	qunlock(udp);