1003 lines · 20.6 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. */ /* * ipconfig for IPv6 *	RS means Router Solicitation *	RA means Router Advertisement */ #include <u.h>#include <libc.h>#include <bio.h>#include <ip.h>#include "ipconfig.h"#include "../icmp.h" #pragma varargck argpos ralog 1 #define RALOG "v6routeradv" #define NetS(x) (((uchar*)x)[0]<< 8 | ((uchar*)x)[1])#define NetL(x) (((uchar*)x)[0]<<24 | ((uchar*)x)[1]<<16 | \		 ((uchar*)x)[2]<< 8 | ((uchar*)x)[3]) enum {	ICMP6LEN=	4,}; typedef struct Hdr Hdr;struct Hdr			/* ICMP v4 & v6 header */{	uchar	type;	uchar	code;	uchar	cksum[2];	/* Checksum */	uchar	data[];}; char *icmpmsg6[Maxtype6+1] ={[EchoReply]		"EchoReply",[UnreachableV6]		"UnreachableV6",[PacketTooBigV6]	"PacketTooBigV6",[TimeExceedV6]		"TimeExceedV6",[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 *icmp6opts[] ={[0]			"unknown option",[V6nd_srclladdr]	"srcll_addr",[V6nd_targlladdr]	"targll_addr",[V6nd_pfxinfo]		"prefix",[V6nd_redirhdr]		"redirect",[V6nd_mtu]		"mtu",[V6nd_home]		"home",[V6nd_srcaddrs]		"src_addrs",[V6nd_ip]		"ip",[V6nd_rdns]		"rdns",[V6nd_9fs]		"9fs",[V6nd_9auth]		"9auth",}; uchar v6allroutersL[IPaddrlen] = {	0xff, 0x02, 0, 0,	0, 0, 0, 0,	0, 0, 0, 0,	0, 0, 0, 0x02}; uchar v6allnodesL[IPaddrlen] = {	0xff, 0x02, 0, 0,	0, 0, 0, 0,	0, 0, 0, 0,	0, 0, 0, 0x01}; uchar v6Unspecified[IPaddrlen] = {	0, 0, 0, 0,	0, 0, 0, 0,	0, 0, 0, 0,	0, 0, 0, 0}; uchar v6loopback[IPaddrlen] = {	0, 0, 0, 0,	0, 0, 0, 0,	0, 0, 0, 0,	0, 0, 0, 1}; uchar v6glunicast[IPaddrlen] = {	0x08, 0, 0, 0,	0, 0, 0, 0,	0, 0, 0, 0,	0, 0, 0, 0}; uchar v6linklocal[IPaddrlen] = {	0xfe, 0x80, 0, 0,	0, 0, 0, 0,	0, 0, 0, 0,	0, 0, 0, 0}; uchar v6solpfx[IPaddrlen] = {	0xff, 0x02, 0, 0,	0, 0, 0, 0,	0, 0, 0, 1,	/* last 3 bytes filled with low-order bytes of addr being solicited */	0xff, 0, 0, 0,}; uchar v6defmask[IPaddrlen] = {	0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff,	0, 0, 0, 0,	0, 0, 0, 0}; enum{	Vadd,	Vremove,	Vunbind,	Vaddpref6,	Vra6,}; static voidralog(char *fmt, ...){	char msg[512];	va_list arg; 	va_start(arg, fmt);	vseprint(msg, msg+sizeof msg, fmt, arg);	va_end(arg);	syslog(debug, RALOG, msg);} extern voidea2lla(uchar *lla, uchar *ea){	assert(IPaddrlen == 16);	memset(lla, 0, IPaddrlen);	lla[0]  = 0xFE;	lla[1]  = 0x80;	lla[8]  = ea[0] | 0x2;	lla[9]  = ea[1];	lla[10] = ea[2];	lla[11] = 0xFF;	lla[12] = 0xFE;	lla[13] = ea[3];	lla[14] = ea[4];	lla[15] = ea[5];} extern voidipv62smcast(uchar *smcast, uchar *a){	assert(IPaddrlen == 16);	memset(smcast, 0, IPaddrlen);	smcast[0]  = 0xFF;	smcast[1]  = 0x02;	smcast[11] = 0x1;	smcast[12] = 0xFF;	smcast[13] = a[13];	smcast[14] = a[14];	smcast[15] = a[15];} voidv6paraminit(Conf *cf){	cf->sendra = cf->recvra = 0;	cf->mflag = 0;	cf->oflag = 0;	cf->maxraint = Maxv6initraintvl;	cf->minraint = Maxv6initraintvl / 4;	cf->linkmtu = 1500;	cf->reachtime = V6reachabletime;	cf->rxmitra = V6retranstimer;	cf->ttl = MAXTTL; 	cf->routerlt = 0; 	cf->prefixlen = 64;	cf->onlink = 0;	cf->autoflag = 0;	cf->validlt = cf->preflt = ~0L;} static char *optname(unsigned opt){	static char buf[32]; 	if (opt >= nelem(icmp6opts) || icmp6opts[opt] == nil) {		snprint(buf, sizeof buf, "unknown option %d", opt);		return buf;	} else		return icmp6opts[opt];} static char*opt_seprint(uchar *ps, uchar *pe, char *sps, char *spe){	int otype, osz, pktsz;	uchar *a;	char *p = sps, *e = spe; 	a = ps;	for (pktsz = pe - ps; pktsz > 0; pktsz -= osz) {		otype = a[0];		osz = a[1] * 8; 		switch (otype) {		default:			return seprint(p, e, " option=%s ", optname(otype));		case V6nd_srclladdr:		case V6nd_targlladdr:			if (pktsz < osz || osz != 8)				return seprint(p, e, " option=%s bad size=%d",					optname(otype), osz);			p = seprint(p, e, " option=%s maddr=%E", optname(otype),				a+2);			break;		case V6nd_pfxinfo:			if (pktsz < osz || osz != 32)				return seprint(p, e, " option=%s: bad size=%d",					optname(otype), osz); 			p = seprint(p, e, " option=%s pref=%I preflen=%3.3d"				" lflag=%1.1d aflag=%1.1d unused1=%1.1d"				" validlt=%ud preflt=%ud unused2=%1.1d",				optname(otype), a+16, (int)(*(a+2)),				(*(a+3) & (1 << 7)) != 0,				(*(a+3) & (1 << 6)) != 0,				(*(a+3) & 63) != 0,				NetL(a+4), NetL(a+8), NetL(a+12)!=0);			break;		}		a += osz;	}	return p;} static voidpkt2str(uchar *ps, uchar *pe, char *sps, char *spe){	int pktlen;	char *tn, *p, *e;	uchar *a;	Hdr *h; 	h = (Hdr*)ps;	a = ps + 4;	p = sps;	e = spe; 	pktlen = pe - ps;	if(pktlen < ICMP6LEN) {		seprint(sps, spe, "short pkt");		return;	} 	tn = icmpmsg6[h->type];	if(tn == nil)		p = seprint(p, e, "t=%ud c=%d ck=%4.4ux", h->type,			h->code, (ushort)NetS(h->cksum));	else		p = seprint(p, e, "t=%s c=%d ck=%4.4ux", tn,			h->code, (ushort)NetS(h->cksum)); 	switch(h->type){	case RouterSolicit:		ps += 8;		p = seprint(p, e, " unused=%1.1d ", NetL(a)!=0);		opt_seprint(ps, pe, p, e);		break;	case RouterAdvert:		ps += 16;		p = seprint(p, e, " hoplim=%3.3d mflag=%1.1d oflag=%1.1d"			" unused=%1.1d routerlt=%d reachtime=%d rxmtimer=%d",			a[0],			(*(a+1) & (1 << 7)) != 0,			(*(a+1) & (1 << 6)) != 0,			(*(a+1) & 63) != 0,			NetS(a+2), NetL(a+4), NetL(a+8));		opt_seprint(ps, pe, p, e);		break;	default:		seprint(p, e, " unexpected icmp6 pkt type");		break;	}} static voidcatch(void *a, char *msg){	USED(a);	if(strstr(msg, "alarm"))		noted(NCONT);	else		noted(NDFLT);} /* * based on libthread's threadsetname, but drags in less library code. * actually just sets the arguments displayed. */voidprocsetname(char *fmt, ...){	int fd;	char *cmdname;	char buf[128];	va_list arg; 	va_start(arg, fmt);	cmdname = vsmprint(fmt, arg);	va_end(arg);	if (cmdname == nil)		return;	snprint(buf, sizeof buf, "#p/%d/args", getpid());	if((fd = open(buf, OWRITE)) >= 0){		write(fd, cmdname, strlen(cmdname)+1);		close(fd);	}	free(cmdname);} intdialicmp(uchar *dst, int dport, int *ctlfd){	int fd, cfd, n, m;	char cmsg[100], name[128], connind[40];	char hdrs[] = "headers"; 	snprint(name, sizeof name, "%s/icmpv6/clone", conf.mpoint);	cfd = open(name, ORDWR);	if(cfd < 0)		sysfatal("dialicmp: can't open %s: %r", name); 	n = snprint(cmsg, sizeof cmsg, "connect %I!%d!r %d", dst, dport, dport);	m = write(cfd, cmsg, n);	if (m < n)		sysfatal("dialicmp: can't write %s to %s: %r", cmsg, name); 	seek(cfd, 0, 0);	n = read(cfd, connind, sizeof connind);	if (n < 0)		connind[0] = 0;	else if (n < sizeof connind)		connind[n] = 0;	else		connind[sizeof connind - 1] = 0; 	snprint(name, sizeof name, "%s/icmpv6/%s/data", conf.mpoint, connind);	fd = open(name, ORDWR);	if(fd < 0)		sysfatal("dialicmp: can't open %s: %r", name); 	n = sizeof hdrs - 1;	if(write(cfd, hdrs, n) < n)		sysfatal("dialicmp: can't write `%s' to %s: %r", hdrs, name);	*ctlfd = cfd;	return fd;} /* add ipv6 addr to an interface */intip6cfg(int autoconf){	int dupfound = 0, n;	char *p;	char buf[256];	uchar ethaddr[6];	Biobuf *bp; 	if (autoconf) {			/* create link-local addr */		if (myetheraddr(ethaddr, conf.dev) < 0)			sysfatal("myetheraddr w/ %s failed: %r", conf.dev);		ea2lla(conf.laddr, ethaddr);	} 	if (dupl_disc)		n = sprint(buf, "try");	else		n = sprint(buf, "add"); 	n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);	if(!validip(conf.mask))		ipmove(conf.mask, v6defmask);	n += snprint(buf+n, sizeof buf-n, " %M", conf.mask);	if(validip(conf.raddr)){		n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);		if(conf.mtu != 0)			n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);	} 	if(write(conf.cfd, buf, n) < 0){		warning("write(%s): %r", buf);		return -1;	} 	if (!dupl_disc)		return 0; 	sleep(3000); 	/* read arp table, look for addr duplication */	snprint(buf, sizeof buf, "%s/arp", conf.mpoint);	bp = Bopen(buf, OREAD);	if (bp == 0) {		warning("couldn't open %s: %r", buf);		return -1;	} 	snprint(buf, sizeof buf, "%I", conf.laddr);	while(p = Brdline(bp, '\n')){		p[Blinelen(bp)-1] = 0;		if(cistrstr(p, buf) != 0) {			warning("found dup entry in arp cache");			dupfound = 1;			break;		}	}	Bterm(bp); 	if (dupfound)		doremove();	else {		n = sprint(buf, "add %I %M", conf.laddr, conf.mask);		if(validip(conf.raddr)){			n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);			if(conf.mtu != 0)				n += snprint(buf+n, sizeof buf-n, " %d",					conf.mtu);		}		write(conf.cfd, buf, n);	}	return 0;} static intrecvra6on(char *net, int conn){	Ipifc* ifc; 	ifc = readipifc(net, nil, conn);	if (ifc == nil)		return 0;	else if (ifc->sendra6 > 0)		return IsRouter;	else if (ifc->recvra6 > 0)		return IsHostRecv;	else		return IsHostNoRecv;} /* send icmpv6 router solicitation to multicast address for all routers */static voidsendrs(int fd){	Routersol *rs;	uchar buff[sizeof *rs]; 	memset(buff, 0, sizeof buff);	rs = (Routersol *)buff;	memmove(rs->dst, v6allroutersL, IPaddrlen);	memmove(rs->src, v6Unspecified, IPaddrlen);	rs->type = ICMP6_RS; 	if(write(fd, rs, sizeof buff) < sizeof buff)		ralog("sendrs: write failed, pkt size %d", sizeof buff);	else		ralog("sendrs: sent solicitation to %I from %I on %s",

Lines 1–500 of 1003.