890 lines · 18.3 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * Internet Control Message Protocol for IPv6 */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h"#include "ip.h"#include "ipv6.h" enum{	InMsgs6,	InErrors6,	OutMsgs6,	CsumErrs6,	LenErrs6,	HlenErrs6,	HoplimErrs6,	IcmpCodeErrs6,	TargetErrs6,	OptlenErrs6,	AddrmxpErrs6,	RouterAddrErrs6, 	Nstats6,}; enum {	ICMP_USEAD6	= 40,}; enum {	Oflag	= 1<<5,	Sflag	= 1<<6,	Rflag	= 1<<7,}; 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,}; /* on-the-wire packet formats */typedef struct IPICMP IPICMP;typedef struct Ndpkt Ndpkt;typedef struct NdiscC NdiscC; /* we do this to avoid possible struct padding  */#define ICMPHDR \	IPV6HDR; \	uchar	type; \	uchar	code; \	uchar	cksum[2]; \	uchar	icmpid[2]; \	uchar	seq[2] struct IPICMP {	ICMPHDR;	uchar	payload[];}; #define IPICMPSZ offsetof(IPICMP, payload[0]) struct NdiscC {	ICMPHDR;	uchar	target[IPaddrlen];	uchar	payload[];}; #define NDISCSZ offsetof(NdiscC, payload[0]) struct Ndpkt {	ICMPHDR;	uchar	target[IPaddrlen];	uchar	otype;	uchar	olen;		/* length in units of 8 octets(incl type, code),				 * 1 for IEEE 802 addresses */	uchar	lnaddr[6];	/* link-layer address */	uchar	payload[];}; #define NDPKTSZ offsetof(Ndpkt, payload[0]) typedef struct Icmppriv6{	ulong	stats[Nstats6]; 	/* message counts */	ulong	in[Maxtype6+1];	ulong	out[Maxtype6+1];} Icmppriv6; typedef struct Icmpcb6{	QLock;	uchar	headers;} Icmpcb6; 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",}; 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",}; static char *unreachcode[] ={[Icmp6_no_route]	"no route to destination",[Icmp6_ad_prohib]	"comm with destination administratively prohibited",[Icmp6_out_src_scope]	"beyond scope of source address",[Icmp6_adr_unreach]	"address unreachable",[Icmp6_port_unreach]	"port unreachable",[Icmp6_gress_src_fail]	"source address failed ingress/egress policy",[Icmp6_rej_route]	"reject route to destination",[Icmp6_unknown]		"icmp unreachable: unknown code",}; static void icmpkick6(void *x, Block *bp); static voidicmpcreate6(Conv *c){	c->rq = qopen(64*1024, Qmsg, 0, c);	c->wq = qbypass(icmpkick6, c);} static voidset_cksum(Block *bp){	IPICMP *p = (IPICMP *)(bp->rp); 	hnputl(p->vcf, 0);  	/* borrow IP header as pseudoheader */	hnputs(p->ploadlen, blocklen(bp) - IP6HDR);	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 Block *newIPICMP(int packetlen){	Block *nbp; 	nbp = allocb(packetlen);	nbp->wp += packetlen;	memset(nbp->rp, 0, packetlen);	return nbp;} voidicmpadvise6(Proto *icmp, Block *bp, char *msg){	ushort recid;	Conv **c, *s;	IPICMP *p; 	p = (IPICMP *)bp->rp;	recid = nhgets(p->icmpid); 	for(c = icmp->conv; *c; c++) {		s = *c;		if(s->lport == recid && ipcmp(s->raddr, p->dst) == 0){			qhangup(s->rq, msg);			qhangup(s->wq, msg);			break;		}	}	freeblist(bp);} static voidicmpkick6(void *x, Block *bp){	uchar laddr[IPaddrlen], raddr[IPaddrlen];	Conv *c = x;	IPICMP *p;	Icmppriv6 *ipriv = c->p->priv;	Icmpcb6 *icb = (Icmpcb6*)c->ptcl; 	if(bp == nil)		return; 	if(icb->headers==6) {		/* get user specified addresses */		bp = pullupblock(bp, ICMP_USEAD6);		if(bp == nil)			return;		bp->rp += 8;		ipmove(laddr, bp->rp);		bp->rp += IPaddrlen;		ipmove(raddr, bp->rp);		bp->rp += IPaddrlen;		bp = padblock(bp, IP6HDR);	} 	if(blocklen(bp) < IPICMPSZ){		freeblist(bp);		return;	}	p = (IPICMP *)(bp->rp);	if(icb->headers == 6) {		ipmove(p->dst, raddr);		ipmove(p->src, laddr);	} else {		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, nil);} char*icmpctl6(Conv *c, char **argv, int argc){	Icmpcb6 *icb; 	icb = (Icmpcb6*) c->ptcl;	if(argc==1 && strcmp(argv[0], "headers")==0) {		icb->headers = 6;		return nil;	}	return "unknown control request";} static voidgoticmpkt6(Proto *icmp, Block *bp, int muxkey){	ushort recid;	uchar *addr;	Conv **c, *s;	IPICMP *p = (IPICMP *)bp->rp; 	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 != nil)				qpass(s->rq, bp);			return;		}	} 	freeblist(bp);} static Block *mkechoreply6(Block *bp, Ipifc *ifc){	uchar addr[IPaddrlen];	IPICMP *p = (IPICMP *)(bp->rp); 	ipmove(addr, p->src);	if(!isv6mcast(p->dst))		ipmove(p->src, p->dst);	else if (!ipv6anylocal(ifc, p->src))		return nil;	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. */extern voidicmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac){	Block *nbp;	Ndpkt *np;	Proto *icmp = f->t2p[ICMPv6];	Icmppriv6 *ipriv = icmp->priv; 	nbp = newIPICMP(NDPKTSZ);	np = (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_LLADDR;		np->olen = 1;		/* 1+1+6 = 8 = 1 8-octet */		memmove(np->lnaddr, mac, sizeof(np->lnaddr));	} else		nbp->wp -= NDPKTSZ - NDISCSZ; 	set_cksum(nbp);	np = (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, nil);} /* * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags. */extern voidicmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags){	Block *nbp;	Ndpkt *np;	Proto *icmp = f->t2p[ICMPv6];	Icmppriv6 *ipriv = icmp->priv; 	nbp = newIPICMP(NDPKTSZ);	np = (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_LLADDR;	np->olen = 1;	memmove(np->lnaddr, mac, sizeof(np->lnaddr)); 	set_cksum(nbp);	np = (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, nil);} /* if free is true, freeblist(bp) before return. */extern voidicmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free){	int osz, sz;	Block *nbp;	IPICMP *np;	Icmppriv6 *ipriv;	Ip6hdr *p;	Proto *icmp; 	osz = BLEN(bp);	sz = MIN(IPICMPSZ + osz, v6MINTU);	icmp = f->t2p[ICMPv6];	ipriv = icmp->priv;	p = (Ip6hdr *)bp->rp;	if(isv6mcast(p->src))		goto freebl;	nbp = newIPICMP(sz);	np = (IPICMP *)nbp->rp; 	rlock(ifc);	if(!ipv6anylocal(ifc, np->src)){		netlog(f, Logicmp, "icmphostunr fail -> src %I dst %I\n",			p->src, p->dst);		runlock(ifc);		freeblist(nbp);		goto freebl;	} 	netlog(f, Logicmp, "send icmphostunr -> src %I dst %I\n", p->src, p->dst);	memmove(np->dst, p->src, IPaddrlen);	np->type = UnreachableV6;	np->code = code;	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);	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, nil);	runlock(ifc);freebl:	if(free)		freeblist(bp);} extern voidicmpttlexceeded6(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; 	p = (Ip6hdr *)bp->rp;	if(isv6mcast(p->src))		return; 	nbp = newIPICMP(sz);	np = (IPICMP *) nbp->rp;	if(ipv6anylocal(ifc, np->src))		netlog(f, Logicmp, "send icmpttlexceeded6 -> src %I dst %I\n",			p->src, p->dst);	else {

Lines 1–500 of 890.