xref: /plan9/sys/src/9/ip/icmp.c (revision a6a9e07217f318acf170f99684a55fba5200524f)
17dd7cddfSDavid du Colombier #include "u.h"
27dd7cddfSDavid du Colombier #include "../port/lib.h"
37dd7cddfSDavid du Colombier #include "mem.h"
47dd7cddfSDavid du Colombier #include "dat.h"
57dd7cddfSDavid du Colombier #include "fns.h"
67dd7cddfSDavid du Colombier #include "../port/error.h"
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier #include "ip.h"
97dd7cddfSDavid du Colombier 
107dd7cddfSDavid du Colombier typedef struct Icmp {
117dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
127dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
137dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
147dd7cddfSDavid du Colombier 	uchar	id[2];		/* Identification */
157dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
167dd7cddfSDavid du Colombier 	uchar	ttl;		/* Time to live */
177dd7cddfSDavid du Colombier 	uchar	proto;		/* Protocol */
187dd7cddfSDavid du Colombier 	uchar	ipcksum[2];	/* Header checksum */
197dd7cddfSDavid du Colombier 	uchar	src[4];		/* Ip source */
207dd7cddfSDavid du Colombier 	uchar	dst[4];		/* Ip destination */
217dd7cddfSDavid du Colombier 	uchar	type;
227dd7cddfSDavid du Colombier 	uchar	code;
237dd7cddfSDavid du Colombier 	uchar	cksum[2];
247dd7cddfSDavid du Colombier 	uchar	icmpid[2];
257dd7cddfSDavid du Colombier 	uchar	seq[2];
267dd7cddfSDavid du Colombier 	uchar	data[1];
277dd7cddfSDavid du Colombier } Icmp;
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier enum {			/* Packet Types */
307dd7cddfSDavid du Colombier 	EchoReply	= 0,
317dd7cddfSDavid du Colombier 	Unreachable	= 3,
327dd7cddfSDavid du Colombier 	SrcQuench	= 4,
3359cc4ca5SDavid du Colombier 	Redirect	= 5,
347dd7cddfSDavid du Colombier 	EchoRequest	= 8,
357dd7cddfSDavid du Colombier 	TimeExceed	= 11,
367dd7cddfSDavid du Colombier 	InParmProblem	= 12,
377dd7cddfSDavid du Colombier 	Timestamp	= 13,
387dd7cddfSDavid du Colombier 	TimestampReply	= 14,
397dd7cddfSDavid du Colombier 	InfoRequest	= 15,
407dd7cddfSDavid du Colombier 	InfoReply	= 16,
417dd7cddfSDavid du Colombier 	AddrMaskRequest = 17,
427dd7cddfSDavid du Colombier 	AddrMaskReply   = 18,
4359cc4ca5SDavid du Colombier 
447dd7cddfSDavid du Colombier 	Maxtype		= 18,
457dd7cddfSDavid du Colombier };
467dd7cddfSDavid du Colombier 
479a747e4fSDavid du Colombier enum
489a747e4fSDavid du Colombier {
499a747e4fSDavid du Colombier 	MinAdvise	= 24,	/* minimum needed for us to advise another protocol */
509a747e4fSDavid du Colombier };
519a747e4fSDavid du Colombier 
5259cc4ca5SDavid du Colombier char *icmpnames[Maxtype+1] =
5359cc4ca5SDavid du Colombier {
5459cc4ca5SDavid du Colombier [EchoReply]		"EchoReply",
5559cc4ca5SDavid du Colombier [Unreachable]		"Unreachable",
5659cc4ca5SDavid du Colombier [SrcQuench]		"SrcQuench",
5759cc4ca5SDavid du Colombier [Redirect]		"Redirect",
5859cc4ca5SDavid du Colombier [EchoRequest]		"EchoRequest",
5959cc4ca5SDavid du Colombier [TimeExceed]		"TimeExceed",
6059cc4ca5SDavid du Colombier [InParmProblem]		"InParmProblem",
6159cc4ca5SDavid du Colombier [Timestamp]		"Timestamp",
6259cc4ca5SDavid du Colombier [TimestampReply]	"TimestampReply",
6359cc4ca5SDavid du Colombier [InfoRequest]		"InfoRequest",
6459cc4ca5SDavid du Colombier [InfoReply]		"InfoReply",
6559cc4ca5SDavid du Colombier [AddrMaskRequest]	"AddrMaskRequest",
6659cc4ca5SDavid du Colombier [AddrMaskReply  ]	"AddrMaskReply  ",
6759cc4ca5SDavid du Colombier };
6859cc4ca5SDavid du Colombier 
697dd7cddfSDavid du Colombier enum {
707dd7cddfSDavid du Colombier 	IP_ICMPPROTO	= 1,
717dd7cddfSDavid du Colombier 	ICMP_IPSIZE	= 20,
727dd7cddfSDavid du Colombier 	ICMP_HDRSIZE	= 8,
737dd7cddfSDavid du Colombier };
747dd7cddfSDavid du Colombier 
7559cc4ca5SDavid du Colombier enum
767dd7cddfSDavid du Colombier {
7759cc4ca5SDavid du Colombier 	InMsgs,
7859cc4ca5SDavid du Colombier 	InErrors,
7959cc4ca5SDavid du Colombier 	OutMsgs,
8059cc4ca5SDavid du Colombier 	CsumErrs,
8159cc4ca5SDavid du Colombier 	LenErrs,
8259cc4ca5SDavid du Colombier 	HlenErrs,
8359cc4ca5SDavid du Colombier 
8459cc4ca5SDavid du Colombier 	Nstats,
8559cc4ca5SDavid du Colombier };
8659cc4ca5SDavid du Colombier 
8759cc4ca5SDavid du Colombier static char *statnames[Nstats] =
8859cc4ca5SDavid du Colombier {
8959cc4ca5SDavid du Colombier [InMsgs]	"InMsgs",
9059cc4ca5SDavid du Colombier [InErrors]	"InErrors",
9159cc4ca5SDavid du Colombier [OutMsgs]	"OutMsgs",
9259cc4ca5SDavid du Colombier [CsumErrs]	"CsumErrs",
9359cc4ca5SDavid du Colombier [LenErrs]	"LenErrs",
9459cc4ca5SDavid du Colombier [HlenErrs]	"HlenErrs",
957dd7cddfSDavid du Colombier };
967dd7cddfSDavid du Colombier 
977dd7cddfSDavid du Colombier typedef struct Icmppriv Icmppriv;
987dd7cddfSDavid du Colombier struct Icmppriv
997dd7cddfSDavid du Colombier {
10059cc4ca5SDavid du Colombier 	ulong	stats[Nstats];
1017dd7cddfSDavid du Colombier 
10259cc4ca5SDavid du Colombier 	/* message counts */
1037dd7cddfSDavid du Colombier 	ulong	in[Maxtype+1];
1047dd7cddfSDavid du Colombier 	ulong	out[Maxtype+1];
1057dd7cddfSDavid du Colombier };
1067dd7cddfSDavid du Colombier 
107e6c6b7f8SDavid du Colombier static void icmpkick(void *x, Block*);
1083ff48bf5SDavid du Colombier 
1093ff48bf5SDavid du Colombier static void
1103ff48bf5SDavid du Colombier icmpcreate(Conv *c)
1113ff48bf5SDavid du Colombier {
1123ff48bf5SDavid du Colombier 	c->rq = qopen(64*1024, Qmsg, 0, c);
113e6c6b7f8SDavid du Colombier 	c->wq = qbypass(icmpkick, c);
1143ff48bf5SDavid du Colombier }
1153ff48bf5SDavid du Colombier 
1163ff48bf5SDavid du Colombier extern char*
1177dd7cddfSDavid du Colombier icmpconnect(Conv *c, char **argv, int argc)
1187dd7cddfSDavid du Colombier {
1197dd7cddfSDavid du Colombier 	char *e;
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier 	e = Fsstdconnect(c, argv, argc);
1223ff48bf5SDavid du Colombier 	if(e != nil)
1233ff48bf5SDavid du Colombier 		return e;
1247dd7cddfSDavid du Colombier 	Fsconnected(c, e);
1257dd7cddfSDavid du Colombier 
1263ff48bf5SDavid du Colombier 	return nil;
1277dd7cddfSDavid du Colombier }
1287dd7cddfSDavid du Colombier 
1293ff48bf5SDavid du Colombier extern int
1307dd7cddfSDavid du Colombier icmpstate(Conv *c, char *state, int n)
1317dd7cddfSDavid du Colombier {
1327dd7cddfSDavid du Colombier 	USED(c);
1337dd7cddfSDavid du Colombier 	return snprint(state, n, "%s", "Datagram");
1347dd7cddfSDavid du Colombier }
1357dd7cddfSDavid du Colombier 
1363ff48bf5SDavid du Colombier extern char*
1377dd7cddfSDavid du Colombier icmpannounce(Conv *c, char **argv, int argc)
1387dd7cddfSDavid du Colombier {
1397dd7cddfSDavid du Colombier 	char *e;
1407dd7cddfSDavid du Colombier 
1417dd7cddfSDavid du Colombier 	e = Fsstdannounce(c, argv, argc);
1429a747e4fSDavid du Colombier 	if(e != nil)
1437dd7cddfSDavid du Colombier 		return e;
1447dd7cddfSDavid du Colombier 	Fsconnected(c, nil);
1457dd7cddfSDavid du Colombier 
1467dd7cddfSDavid du Colombier 	return nil;
1477dd7cddfSDavid du Colombier }
1487dd7cddfSDavid du Colombier 
1493ff48bf5SDavid du Colombier extern void
1507dd7cddfSDavid du Colombier icmpclose(Conv *c)
1517dd7cddfSDavid du Colombier {
1527dd7cddfSDavid du Colombier 	qclose(c->rq);
1537dd7cddfSDavid du Colombier 	qclose(c->wq);
1547dd7cddfSDavid du Colombier 	ipmove(c->laddr, IPnoaddr);
1557dd7cddfSDavid du Colombier 	ipmove(c->raddr, IPnoaddr);
1567dd7cddfSDavid du Colombier 	c->lport = 0;
1577dd7cddfSDavid du Colombier }
1587dd7cddfSDavid du Colombier 
1597dd7cddfSDavid du Colombier static void
160e6c6b7f8SDavid du Colombier icmpkick(void *x, Block *bp)
1617dd7cddfSDavid du Colombier {
1623ff48bf5SDavid du Colombier 	Conv *c = x;
1637dd7cddfSDavid du Colombier 	Icmp *p;
1647dd7cddfSDavid du Colombier 	Icmppriv *ipriv;
1657dd7cddfSDavid du Colombier 
1667dd7cddfSDavid du Colombier 	if(bp == nil)
1677dd7cddfSDavid du Colombier 		return;
1687dd7cddfSDavid du Colombier 
1697dd7cddfSDavid du Colombier 	if(blocklen(bp) < ICMP_IPSIZE + ICMP_HDRSIZE){
1707dd7cddfSDavid du Colombier 		freeblist(bp);
1717dd7cddfSDavid du Colombier 		return;
1727dd7cddfSDavid du Colombier 	}
1737dd7cddfSDavid du Colombier 	p = (Icmp *)(bp->rp);
1743ff48bf5SDavid du Colombier 	p->vihl = IP_VER4;
1757dd7cddfSDavid du Colombier 	ipriv = c->p->priv;
1767dd7cddfSDavid du Colombier 	if(p->type <= Maxtype)
1777dd7cddfSDavid du Colombier 		ipriv->out[p->type]++;
1787dd7cddfSDavid du Colombier 
1797dd7cddfSDavid du Colombier 	v6tov4(p->dst, c->raddr);
1807dd7cddfSDavid du Colombier 	v6tov4(p->src, c->laddr);
1817dd7cddfSDavid du Colombier 	p->proto = IP_ICMPPROTO;
1827dd7cddfSDavid du Colombier 	hnputs(p->icmpid, c->lport);
1837dd7cddfSDavid du Colombier 	memset(p->cksum, 0, sizeof(p->cksum));
1847dd7cddfSDavid du Colombier 	hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
18559cc4ca5SDavid du Colombier 	ipriv->stats[OutMsgs]++;
186*a6a9e072SDavid du Colombier 	ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
1877dd7cddfSDavid du Colombier }
1887dd7cddfSDavid du Colombier 
1897dd7cddfSDavid du Colombier extern void
1903ff48bf5SDavid du Colombier icmpttlexceeded(Fs *f, uchar *ia, Block *bp)
1917dd7cddfSDavid du Colombier {
1927dd7cddfSDavid du Colombier 	Block	*nbp;
1937dd7cddfSDavid du Colombier 	Icmp	*p, *np;
1947dd7cddfSDavid du Colombier 
1957dd7cddfSDavid du Colombier 	p = (Icmp *)bp->rp;
1967dd7cddfSDavid du Colombier 
1977dd7cddfSDavid du Colombier 	netlog(f, Logicmp, "sending icmpttlexceeded -> %V\n", p->src);
1987dd7cddfSDavid du Colombier 	nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
1997dd7cddfSDavid du Colombier 	nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
2007dd7cddfSDavid du Colombier 	np = (Icmp *)nbp->rp;
201d9306527SDavid du Colombier 	np->vihl = IP_VER4;
2027dd7cddfSDavid du Colombier 	memmove(np->dst, p->src, sizeof(np->dst));
2033ff48bf5SDavid du Colombier 	v6tov4(np->src, ia);
2047dd7cddfSDavid du Colombier 	memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
2057dd7cddfSDavid du Colombier 	np->type = TimeExceed;
2067dd7cddfSDavid du Colombier 	np->code = 0;
2077dd7cddfSDavid du Colombier 	np->proto = IP_ICMPPROTO;
2087dd7cddfSDavid du Colombier 	hnputs(np->icmpid, 0);
2097dd7cddfSDavid du Colombier 	hnputs(np->seq, 0);
2107dd7cddfSDavid du Colombier 	memset(np->cksum, 0, sizeof(np->cksum));
2117dd7cddfSDavid du Colombier 	hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
212*a6a9e072SDavid du Colombier 	ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
2137dd7cddfSDavid du Colombier 
2147dd7cddfSDavid du Colombier }
2157dd7cddfSDavid du Colombier 
216d9306527SDavid du Colombier static void
217d9306527SDavid du Colombier icmpunreachable(Fs *f, Block *bp, int code, int seq)
2187dd7cddfSDavid du Colombier {
2197dd7cddfSDavid du Colombier 	Block	*nbp;
2207dd7cddfSDavid du Colombier 	Icmp	*p, *np;
2217dd7cddfSDavid du Colombier 	int	i;
2227dd7cddfSDavid du Colombier 	uchar	addr[IPaddrlen];
2237dd7cddfSDavid du Colombier 
2247dd7cddfSDavid du Colombier 	p = (Icmp *)bp->rp;
2257dd7cddfSDavid du Colombier 
2267dd7cddfSDavid du Colombier 	/* only do this for unicast sources and destinations */
2277dd7cddfSDavid du Colombier 	v4tov6(addr, p->dst);
2287dd7cddfSDavid du Colombier 	i = ipforme(f, addr);
2297dd7cddfSDavid du Colombier 	if((i&Runi) == 0)
2307dd7cddfSDavid du Colombier 		return;
2317dd7cddfSDavid du Colombier 	v4tov6(addr, p->src);
2327dd7cddfSDavid du Colombier 	i = ipforme(f, addr);
2337dd7cddfSDavid du Colombier 	if(i != 0 && (i&Runi) == 0)
2347dd7cddfSDavid du Colombier 		return;
2357dd7cddfSDavid du Colombier 
2367dd7cddfSDavid du Colombier 	netlog(f, Logicmp, "sending icmpnoconv -> %V\n", p->src);
2377dd7cddfSDavid du Colombier 	nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8);
2387dd7cddfSDavid du Colombier 	nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8;
2397dd7cddfSDavid du Colombier 	np = (Icmp *)nbp->rp;
240d9306527SDavid du Colombier 	np->vihl = IP_VER4;
2417dd7cddfSDavid du Colombier 	memmove(np->dst, p->src, sizeof(np->dst));
2427dd7cddfSDavid du Colombier 	memmove(np->src, p->dst, sizeof(np->src));
2437dd7cddfSDavid du Colombier 	memmove(np->data, bp->rp, ICMP_IPSIZE + 8);
2447dd7cddfSDavid du Colombier 	np->type = Unreachable;
245d9306527SDavid du Colombier 	np->code = code;
2467dd7cddfSDavid du Colombier 	np->proto = IP_ICMPPROTO;
2477dd7cddfSDavid du Colombier 	hnputs(np->icmpid, 0);
248d9306527SDavid du Colombier 	hnputs(np->seq, seq);
2497dd7cddfSDavid du Colombier 	memset(np->cksum, 0, sizeof(np->cksum));
2507dd7cddfSDavid du Colombier 	hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE));
251*a6a9e072SDavid du Colombier 	ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil);
2527dd7cddfSDavid du Colombier }
2537dd7cddfSDavid du Colombier 
254d9306527SDavid du Colombier extern void
255d9306527SDavid du Colombier icmpnoconv(Fs *f, Block *bp)
256d9306527SDavid du Colombier {
257d9306527SDavid du Colombier 	icmpunreachable(f, bp, 3, 0);
258d9306527SDavid du Colombier }
259d9306527SDavid du Colombier 
260d9306527SDavid du Colombier extern void
261d9306527SDavid du Colombier icmpcantfrag(Fs *f, Block *bp, int mtu)
262d9306527SDavid du Colombier {
263d9306527SDavid du Colombier 	icmpunreachable(f, bp, 4, mtu);
264d9306527SDavid du Colombier }
265d9306527SDavid du Colombier 
2667dd7cddfSDavid du Colombier static void
2677dd7cddfSDavid du Colombier goticmpkt(Proto *icmp, Block *bp)
2687dd7cddfSDavid du Colombier {
2697dd7cddfSDavid du Colombier 	Conv	**c, *s;
2707dd7cddfSDavid du Colombier 	Icmp	*p;
2717dd7cddfSDavid du Colombier 	uchar	dst[IPaddrlen];
2727dd7cddfSDavid du Colombier 	ushort	recid;
2737dd7cddfSDavid du Colombier 
2747dd7cddfSDavid du Colombier 	p = (Icmp *) bp->rp;
2757dd7cddfSDavid du Colombier 	v4tov6(dst, p->src);
2767dd7cddfSDavid du Colombier 	recid = nhgets(p->icmpid);
2777dd7cddfSDavid du Colombier 
2787dd7cddfSDavid du Colombier 	for(c = icmp->conv; *c; c++) {
2797dd7cddfSDavid du Colombier 		s = *c;
2807dd7cddfSDavid du Colombier 		if(s->lport == recid)
2817dd7cddfSDavid du Colombier 		if(ipcmp(s->raddr, dst) == 0){
2827dd7cddfSDavid du Colombier 			bp = concatblock(bp);
2837dd7cddfSDavid du Colombier 			if(bp != nil)
2847dd7cddfSDavid du Colombier 				qpass(s->rq, bp);
2857dd7cddfSDavid du Colombier 			return;
2867dd7cddfSDavid du Colombier 		}
2877dd7cddfSDavid du Colombier 	}
2887dd7cddfSDavid du Colombier 	freeblist(bp);
2897dd7cddfSDavid du Colombier }
2907dd7cddfSDavid du Colombier 
2917dd7cddfSDavid du Colombier static Block *
2927dd7cddfSDavid du Colombier mkechoreply(Block *bp)
2937dd7cddfSDavid du Colombier {
2947dd7cddfSDavid du Colombier 	Icmp	*q;
2957dd7cddfSDavid du Colombier 	uchar	ip[4];
2967dd7cddfSDavid du Colombier 
2977dd7cddfSDavid du Colombier 	q = (Icmp *)bp->rp;
2983ff48bf5SDavid du Colombier 	q->vihl = IP_VER4;
2997dd7cddfSDavid du Colombier 	memmove(ip, q->src, sizeof(q->dst));
3007dd7cddfSDavid du Colombier 	memmove(q->src, q->dst, sizeof(q->src));
3017dd7cddfSDavid du Colombier 	memmove(q->dst, ip,  sizeof(q->dst));
3027dd7cddfSDavid du Colombier 	q->type = EchoReply;
3037dd7cddfSDavid du Colombier 	memset(q->cksum, 0, sizeof(q->cksum));
3047dd7cddfSDavid du Colombier 	hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE));
3057dd7cddfSDavid du Colombier 
3067dd7cddfSDavid du Colombier 	return bp;
3077dd7cddfSDavid du Colombier }
3087dd7cddfSDavid du Colombier 
3097dd7cddfSDavid du Colombier static char *unreachcode[] =
3107dd7cddfSDavid du Colombier {
3117dd7cddfSDavid du Colombier [0]	"net unreachable",
3127dd7cddfSDavid du Colombier [1]	"host unreachable",
3137dd7cddfSDavid du Colombier [2]	"protocol unreachable",
3147dd7cddfSDavid du Colombier [3]	"port unreachable",
3153ff48bf5SDavid du Colombier [4]	"fragmentation needed and DF set",
3167dd7cddfSDavid du Colombier [5]	"source route failed",
3177dd7cddfSDavid du Colombier };
3187dd7cddfSDavid du Colombier 
3197dd7cddfSDavid du Colombier static void
3209a747e4fSDavid du Colombier icmpiput(Proto *icmp, Ipifc*, Block *bp)
3217dd7cddfSDavid du Colombier {
3227dd7cddfSDavid du Colombier 	int	n, iplen;
3237dd7cddfSDavid du Colombier 	Icmp	*p;
3247dd7cddfSDavid du Colombier 	Block	*r;
3257dd7cddfSDavid du Colombier 	Proto	*pr;
3267dd7cddfSDavid du Colombier 	char	*msg;
3277dd7cddfSDavid du Colombier 	char	m2[128];
3287dd7cddfSDavid du Colombier 	Icmppriv *ipriv;
3297dd7cddfSDavid du Colombier 
3307dd7cddfSDavid du Colombier 	ipriv = icmp->priv;
3317dd7cddfSDavid du Colombier 
33259cc4ca5SDavid du Colombier 	ipriv->stats[InMsgs]++;
3337dd7cddfSDavid du Colombier 
3347dd7cddfSDavid du Colombier 	p = (Icmp *)bp->rp;
3357dd7cddfSDavid du Colombier 	netlog(icmp->f, Logicmp, "icmpiput %d %d\n", p->type, p->code);
3367dd7cddfSDavid du Colombier 	n = blocklen(bp);
3377dd7cddfSDavid du Colombier 	if(n < ICMP_IPSIZE+ICMP_HDRSIZE){
33859cc4ca5SDavid du Colombier 		ipriv->stats[InErrors]++;
33959cc4ca5SDavid du Colombier 		ipriv->stats[HlenErrs]++;
3407dd7cddfSDavid du Colombier 		netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
3417dd7cddfSDavid du Colombier 		goto raise;
3427dd7cddfSDavid du Colombier 	}
3437dd7cddfSDavid du Colombier 	iplen = nhgets(p->length);
3447dd7cddfSDavid du Colombier 	if(iplen > n || (iplen % 1)){
34559cc4ca5SDavid du Colombier 		ipriv->stats[LenErrs]++;
34659cc4ca5SDavid du Colombier 		ipriv->stats[InErrors]++;
3477dd7cddfSDavid du Colombier 		netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
3487dd7cddfSDavid du Colombier 		goto raise;
3497dd7cddfSDavid du Colombier 	}
3507dd7cddfSDavid du Colombier 	if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){
35159cc4ca5SDavid du Colombier 		ipriv->stats[InErrors]++;
35259cc4ca5SDavid du Colombier 		ipriv->stats[CsumErrs]++;
3537dd7cddfSDavid du Colombier 		netlog(icmp->f, Logicmp, "icmp checksum error\n");
3547dd7cddfSDavid du Colombier 		goto raise;
3557dd7cddfSDavid du Colombier 	}
3567dd7cddfSDavid du Colombier 	if(p->type <= Maxtype)
3577dd7cddfSDavid du Colombier 		ipriv->in[p->type]++;
3587dd7cddfSDavid du Colombier 
3597dd7cddfSDavid du Colombier 	switch(p->type) {
3607dd7cddfSDavid du Colombier 	case EchoRequest:
3613ff48bf5SDavid du Colombier 		if (iplen < n)
3623ff48bf5SDavid du Colombier 			bp = trimblock(bp, 0, iplen);
3637dd7cddfSDavid du Colombier 		r = mkechoreply(bp);
3647dd7cddfSDavid du Colombier 		ipriv->out[EchoReply]++;
365*a6a9e072SDavid du Colombier 		ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
3667dd7cddfSDavid du Colombier 		break;
3677dd7cddfSDavid du Colombier 	case Unreachable:
3683ff48bf5SDavid du Colombier 		if(p->code > 5 || p->code < 0)
3697dd7cddfSDavid du Colombier 			msg = unreachcode[1];
3703ff48bf5SDavid du Colombier 		else
3717dd7cddfSDavid du Colombier 			msg = unreachcode[p->code];
3727dd7cddfSDavid du Colombier 
3737dd7cddfSDavid du Colombier 		bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
3749a747e4fSDavid du Colombier 		if(blocklen(bp) < MinAdvise){
37559cc4ca5SDavid du Colombier 			ipriv->stats[LenErrs]++;
3767dd7cddfSDavid du Colombier 			goto raise;
3777dd7cddfSDavid du Colombier 		}
3787dd7cddfSDavid du Colombier 		p = (Icmp *)bp->rp;
3797dd7cddfSDavid du Colombier 		pr = Fsrcvpcolx(icmp->f, p->proto);
3807dd7cddfSDavid du Colombier 		if(pr != nil && pr->advise != nil) {
3817dd7cddfSDavid du Colombier 			(*pr->advise)(pr, bp, msg);
3827dd7cddfSDavid du Colombier 			return;
3837dd7cddfSDavid du Colombier 		}
3847dd7cddfSDavid du Colombier 
3857dd7cddfSDavid du Colombier 		bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
3867dd7cddfSDavid du Colombier 		goticmpkt(icmp, bp);
3877dd7cddfSDavid du Colombier 		break;
3887dd7cddfSDavid du Colombier 	case TimeExceed:
3897dd7cddfSDavid du Colombier 		if(p->code == 0){
3907dd7cddfSDavid du Colombier 			sprint(m2, "ttl exceeded at %V", p->src);
3917dd7cddfSDavid du Colombier 
3927dd7cddfSDavid du Colombier 			bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
3939a747e4fSDavid du Colombier 			if(blocklen(bp) < MinAdvise){
39459cc4ca5SDavid du Colombier 				ipriv->stats[LenErrs]++;
3957dd7cddfSDavid du Colombier 				goto raise;
3967dd7cddfSDavid du Colombier 			}
3977dd7cddfSDavid du Colombier 			p = (Icmp *)bp->rp;
3987dd7cddfSDavid du Colombier 			pr = Fsrcvpcolx(icmp->f, p->proto);
3997dd7cddfSDavid du Colombier 			if(pr != nil && pr->advise != nil) {
4007dd7cddfSDavid du Colombier 				(*pr->advise)(pr, bp, m2);
4017dd7cddfSDavid du Colombier 				return;
4027dd7cddfSDavid du Colombier 			}
4039a747e4fSDavid du Colombier 			bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
4047dd7cddfSDavid du Colombier 		}
4057dd7cddfSDavid du Colombier 
4067dd7cddfSDavid du Colombier 		goticmpkt(icmp, bp);
4077dd7cddfSDavid du Colombier 		break;
4087dd7cddfSDavid du Colombier 	default:
4097dd7cddfSDavid du Colombier 		goticmpkt(icmp, bp);
4107dd7cddfSDavid du Colombier 		break;
4117dd7cddfSDavid du Colombier 	}
4127dd7cddfSDavid du Colombier 	return;
4137dd7cddfSDavid du Colombier 
4147dd7cddfSDavid du Colombier raise:
4157dd7cddfSDavid du Colombier 	freeblist(bp);
4167dd7cddfSDavid du Colombier }
4177dd7cddfSDavid du Colombier 
4187dd7cddfSDavid du Colombier void
4197dd7cddfSDavid du Colombier icmpadvise(Proto *icmp, Block *bp, char *msg)
4207dd7cddfSDavid du Colombier {
4217dd7cddfSDavid du Colombier 	Conv	**c, *s;
4227dd7cddfSDavid du Colombier 	Icmp	*p;
4237dd7cddfSDavid du Colombier 	uchar	dst[IPaddrlen];
4247dd7cddfSDavid du Colombier 	ushort	recid;
4257dd7cddfSDavid du Colombier 
4267dd7cddfSDavid du Colombier 	p = (Icmp *) bp->rp;
4277dd7cddfSDavid du Colombier 	v4tov6(dst, p->dst);
4287dd7cddfSDavid du Colombier 	recid = nhgets(p->icmpid);
4297dd7cddfSDavid du Colombier 
4307dd7cddfSDavid du Colombier 	for(c = icmp->conv; *c; c++) {
4317dd7cddfSDavid du Colombier 		s = *c;
4327dd7cddfSDavid du Colombier 		if(s->lport == recid)
4337dd7cddfSDavid du Colombier 		if(ipcmp(s->raddr, dst) == 0){
4347dd7cddfSDavid du Colombier 			qhangup(s->rq, msg);
4357dd7cddfSDavid du Colombier 			qhangup(s->wq, msg);
4367dd7cddfSDavid du Colombier 			break;
4377dd7cddfSDavid du Colombier 		}
4387dd7cddfSDavid du Colombier 	}
4397dd7cddfSDavid du Colombier 	freeblist(bp);
4407dd7cddfSDavid du Colombier }
4417dd7cddfSDavid du Colombier 
4427dd7cddfSDavid du Colombier int
4437dd7cddfSDavid du Colombier icmpstats(Proto *icmp, char *buf, int len)
4447dd7cddfSDavid du Colombier {
44559cc4ca5SDavid du Colombier 	Icmppriv *priv;
44659cc4ca5SDavid du Colombier 	char *p, *e;
44759cc4ca5SDavid du Colombier 	int i;
4487dd7cddfSDavid du Colombier 
44959cc4ca5SDavid du Colombier 	priv = icmp->priv;
45059cc4ca5SDavid du Colombier 	p = buf;
45159cc4ca5SDavid du Colombier 	e = p+len;
45259cc4ca5SDavid du Colombier 	for(i = 0; i < Nstats; i++)
45359cc4ca5SDavid du Colombier 		p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]);
45459cc4ca5SDavid du Colombier 	for(i = 0; i <= Maxtype; i++){
45559cc4ca5SDavid du Colombier 		if(icmpnames[i])
45659cc4ca5SDavid du Colombier 			p = seprint(p, e, "%s: %lud %lud\n", icmpnames[i], priv->in[i], priv->out[i]);
45759cc4ca5SDavid du Colombier 		else
45859cc4ca5SDavid du Colombier 			p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
45959cc4ca5SDavid du Colombier 	}
46059cc4ca5SDavid du Colombier 	return p - buf;
4617dd7cddfSDavid du Colombier }
4627dd7cddfSDavid du Colombier 
4637dd7cddfSDavid du Colombier void
4647dd7cddfSDavid du Colombier icmpinit(Fs *fs)
4657dd7cddfSDavid du Colombier {
4667dd7cddfSDavid du Colombier 	Proto *icmp;
4677dd7cddfSDavid du Colombier 
4687dd7cddfSDavid du Colombier 	icmp = smalloc(sizeof(Proto));
4697dd7cddfSDavid du Colombier 	icmp->priv = smalloc(sizeof(Icmppriv));
4707dd7cddfSDavid du Colombier 	icmp->name = "icmp";
4717dd7cddfSDavid du Colombier 	icmp->connect = icmpconnect;
4727dd7cddfSDavid du Colombier 	icmp->announce = icmpannounce;
4737dd7cddfSDavid du Colombier 	icmp->state = icmpstate;
4747dd7cddfSDavid du Colombier 	icmp->create = icmpcreate;
4757dd7cddfSDavid du Colombier 	icmp->close = icmpclose;
4767dd7cddfSDavid du Colombier 	icmp->rcv = icmpiput;
4777dd7cddfSDavid du Colombier 	icmp->stats = icmpstats;
4787dd7cddfSDavid du Colombier 	icmp->ctl = nil;
4797dd7cddfSDavid du Colombier 	icmp->advise = icmpadvise;
4807dd7cddfSDavid du Colombier 	icmp->gc = nil;
4817dd7cddfSDavid du Colombier 	icmp->ipproto = IP_ICMPPROTO;
4827dd7cddfSDavid du Colombier 	icmp->nc = 16;
4837dd7cddfSDavid du Colombier 	icmp->ptclsize = 0;
4847dd7cddfSDavid du Colombier 
4857dd7cddfSDavid du Colombier 	Fsproto(fs, icmp);
4867dd7cddfSDavid du Colombier }
487