665 lines · 12.1 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. */ #include <u.h>#include <libc.h>#include <ip.h>#include "dhcp.h" void	bootpdump(uchar *p, int n);void	dhcpinit(void);void	dhcprecv(void);void	dhcpsend(int);void	myfatal(char *fmt, ...);int	openlisten(char*);uchar	*optaddaddr(uchar*, int, uchar*);uchar	*optaddbyte(uchar*, int, int);uchar	*optadd(uchar*, int, void*, int);uchar	*optaddulong(uchar*, int, ulong);uchar	*optget(Bootp*, int, int);int	optgetaddr(Bootp*, int, uchar*);int	optgetbyte(Bootp*, int);ulong	optgetulong(Bootp*, int);Bootp	*parse(uchar*, int);void	stdinthread(void*);ulong	thread(void(*f)(void*), void *a);void	timerthread(void*);void	usage(void); struct {	QLock	lk;	int	state;	int	fd;	ulong	xid;	ulong	starttime;	char	cid[100];	char	sname[64];	uchar	server[IPaddrlen];		/* server IP address */	uchar	client[IPaddrlen];		/* client IP address */	uchar	mask[IPaddrlen];		/* client mask */	ulong	lease;		/* lease time */	ulong	resend;		/* number of resends for current state */	ulong	timeout;	/* time to timeout - seconds */} dhcp; char	net[64]; char optmagic[4] = { 0x63, 0x82, 0x53, 0x63 }; voidmain(int argc, char *argv[]){	char *p; 	setnetmtpt(net, sizeof(net), nil); 	ARGBEGIN{	case 'x':		p = ARGF();		if(p == nil)			usage();		setnetmtpt(net, sizeof(net), p);	}ARGEND; 	fmtinstall('E', eipfmt);	fmtinstall('I', eipfmt);	fmtinstall('V', eipfmt); 	dhcpinit(); 	rfork(RFNOTEG|RFREND); 	thread(timerthread, 0);	thread(stdinthread, 0); 	qlock(&dhcp.lk);	dhcp.starttime = time(0);	dhcp.fd = openlisten(net);	dhcpsend(Discover);	dhcp.state = Sselecting;	dhcp.resend = 0;	dhcp.timeout = 4; 	while(dhcp.state != Sbound)		dhcprecv(); 	/* allows other clients on this machine */	close(dhcp.fd);	dhcp.fd = -1; 	print("ip=%I\n", dhcp.client);	print("mask=%I\n", dhcp.mask);	print("end\n"); 	/* keep lease alive */	for(;;) {//fprint(2, "got lease for %d\n", dhcp.lease);		qunlock(&dhcp.lk);		sleep(dhcp.lease*500);	/* wait half of lease time */		qlock(&dhcp.lk); //fprint(2, "try renue\n", dhcp.lease);		dhcp.starttime = time(0);		dhcp.fd = openlisten(net);		dhcp.xid = time(0)*getpid();		dhcpsend(Request);		dhcp.state = Srenewing;		dhcp.resend = 0;		dhcp.timeout = 1; 		while(dhcp.state != Sbound)			dhcprecv(); 		/* allows other clients on this machine */		close(dhcp.fd);		dhcp.fd = -1;	}} voidusage(void){	fprint(2, "usage: %s [-x netextension]\n", argv0);	exits("usage");} voidtimerthread(void*){	for(;;) {		sleep(1000);		qlock(&dhcp.lk);		if(--dhcp.timeout > 0) {			qunlock(&dhcp.lk);			continue;		} 		switch(dhcp.state) {		default:			myfatal("timerthread: unknown state %d", dhcp.state);		case Sinit:			break;		case Sselecting:			dhcpsend(Discover);			dhcp.timeout = 4;			dhcp.resend++;			if(dhcp.resend>5)				myfatal("dhcp: giving up: selecting");			break;		case Srequesting:			dhcpsend(Request);			dhcp.timeout = 4;			dhcp.resend++;			if(dhcp.resend>5)				myfatal("dhcp: giving up: requesting");			break;		case Srenewing:			dhcpsend(Request);			dhcp.timeout = 1;			dhcp.resend++;			if(dhcp.resend>3) {				dhcp.state = Srebinding;				dhcp.resend = 0;			}			break;		case Srebinding:			dhcpsend(Request);			dhcp.timeout = 4;			dhcp.resend++;			if(dhcp.resend>5)				myfatal("dhcp: giving up: rebinding");			break;		case Sbound:			break;		}		qunlock(&dhcp.lk);	}} voidstdinthread(void*){	uchar buf[100];	int n; 	for(;;) {		n = read(0, buf, sizeof(buf));		if(n <= 0)			break;	}	/* shutdown cleanly */	qlock(&dhcp.lk);	if(dhcp.client) {		if(dhcp.fd < 0)			dhcp.fd = openlisten(net);		dhcpsend(Release);	}	qunlock(&dhcp.lk); 	postnote(PNGROUP, getpid(), "die");	exits(0);} voiddhcpinit(void){	int fd; 	dhcp.state = Sinit;	dhcp.timeout = 4; 	fd = open("/dev/random", 0);	if(fd >= 0) {		read(fd, &dhcp.xid, sizeof(dhcp.xid));		close(fd);	} else		dhcp.xid = time(0)*getpid();	srand(dhcp.xid); 	sprint(dhcp.cid, "%s.%d", getenv("sysname"), getpid());} voiddhcpsend(int type){	int n;	uchar *p;	Bootp bp;	Udphdr *up; 	memset(&bp, 0, sizeof bp);	up = (Udphdr*)bp.udphdr; 	hnputs(up->rport, 67);	bp.op = Bootrequest;	hnputl(bp.xid, dhcp.xid);	hnputs(bp.secs, time(0) - dhcp.starttime);	hnputs(bp.flags, Fbroadcast);		/* reply must be broadcast */	memmove(bp.optmagic, optmagic, 4);	p = bp.optdata;	p = optaddbyte(p, ODtype, type);	p = optadd(p, ODclientid, dhcp.cid, strlen(dhcp.cid));	switch(type) {	default:		myfatal("dhcpsend: unknown message type: %d", type);	case Discover:		ipmove(up->raddr, IPv4bcast);	/* broadcast */		break;	case Request:		if(dhcp.state == Sbound || dhcp.state == Srenewing)			ipmove(up->raddr, dhcp.server);		else			ipmove(up->raddr, IPv4bcast);	/* broadcast */		p = optaddulong(p, ODlease, dhcp.lease);		if(dhcp.state == Sselecting || dhcp.state == Srequesting) {			p = optaddaddr(p, ODipaddr, dhcp.client);	/* mistake?? */			p = optaddaddr(p, ODserverid, dhcp.server);		} else			v6tov4(bp.ciaddr, dhcp.client);		break;	case Release:		ipmove(up->raddr, dhcp.server);		v6tov4(bp.ciaddr, dhcp.client);		p = optaddaddr(p, ODipaddr, dhcp.client);		p = optaddaddr(p, ODserverid, dhcp.server);		break;	} 	*p++ = OBend; 	n = p - (uchar*)&bp; 	if(write(dhcp.fd, &bp, n) != n)		myfatal("dhcpsend: write failed: %r");} voiddhcprecv(void){	uchar buf[2000];	Bootp *bp;	int n, type;	ulong lease;	uchar mask[IPaddrlen]; 	qunlock(&dhcp.lk);	n = read(dhcp.fd, buf, sizeof(buf));	qlock(&dhcp.lk); 	if(n <= 0)		myfatal("dhcprecv: bad read: %r"); 	bp = parse(buf, n);	if(bp == 0)		return; if(1) {fprint(2, "recved\n");bootpdump(buf, n);} 	type = optgetbyte(bp, ODtype);	switch(type) {	default:		fprint(2, "dhcprecv: unknown type: %d\n", type);		break;	case Offer:		if(dhcp.state != Sselecting)			break;		lease = optgetulong(bp, ODlease);		if(lease == 0)			myfatal("bad lease");		if(!optgetaddr(bp, OBmask, mask))			memset(mask, 0xff, sizeof(mask));		v4tov6(dhcp.client, bp->yiaddr);		if(!optgetaddr(bp, ODserverid, dhcp.server)) {			fprint(2, "dhcprecv: Offer from server with invalid serverid\n");			break;		} 		dhcp.lease = lease;		ipmove(dhcp.mask, mask);		memmove(dhcp.sname, bp->sname, sizeof(dhcp.sname));		dhcp.sname[sizeof(dhcp.sname)-1] = 0; 		dhcpsend(Request);		dhcp.state = Srequesting;		dhcp.resend = 0;		dhcp.timeout = 4;		break;	case Ack:		if(dhcp.state != Srequesting)		if(dhcp.state != Srenewing)		if(dhcp.state != Srebinding)			break;		lease = optgetulong(bp, ODlease);		if(lease == 0)			myfatal("bad lease");		if(!optgetaddr(bp, OBmask, mask))			memset(mask, 0xff, sizeof(mask));		v4tov6(dhcp.client, bp->yiaddr);		dhcp.lease = lease;		ipmove(dhcp.mask, mask);		dhcp.state = Sbound;		break;	case Nak:		myfatal("recved nak");		break;	} } intopenlisten(char *net){	int n, fd, cfd;	char data[128], devdir[40]; //	sprint(data, "%s/udp!*!bootpc", net);	sprint(data, "%s/udp!*!68", net);	for(n = 0; ; n++) {		cfd = announce(data, devdir);		if(cfd >= 0)			break;		/* might be another client - wait and try again */		fprint(2, "dhcpclient: can't announce %s: %r", data);		sleep(1000);		if(n > 10)			myfatal("can't announce: giving up: %r");	} 	if(fprint(cfd, "headers") < 0)		myfatal("can't set header mode: %r"); 	sprint(data, "%s/data", devdir);	fd = open(data, ORDWR);	if(fd < 0)		myfatal("open %s: %r", data);	close(cfd);	return fd;} uchar*optadd(uchar *p, int op, void *d, int n){	p[0] = op;	p[1] = n;	memmove(p+2, d, n);	return p+n+2;} uchar*optaddbyte(uchar *p, int op, int b){	p[0] = op;	p[1] = 1;	p[2] = b;	return p+3;} uchar*optaddulong(uchar *p, int op, ulong x){	p[0] = op;	p[1] = 4;	hnputl(p+2, x);	return p+6;} uchar *optaddaddr(uchar *p, int op, uchar *ip){	p[0] = op;	p[1] = 4;	v6tov4(p+2, ip);	return p+6;} uchar*optget(Bootp *bp, int op, int n){	int len, code;	uchar *p; 	p = bp->optdata;	for(;;) {		code = *p++;		if(code == OBpad)			continue;		if(code == OBend)			return 0;		len = *p++;		if(code != op) {			p += len;			continue;		}		if(n && n != len)			return 0;		return p;	}}  intoptgetbyte(Bootp *bp, int op){	uchar *p; 	p = optget(bp, op, 1);	if(p == 0)		return 0;	return *p;} ulongoptgetulong(Bootp *bp, int op){	uchar *p; 	p = optget(bp, op, 4);	if(p == 0)		return 0;	return nhgetl(p);} intoptgetaddr(Bootp *bp, int op, uchar *ip){	uchar *p; 	p = optget(bp, op, 4);	if(p == 0)		return 0;	v4tov6(ip, p);	return 1;} /* make sure packet looks ok */Bootp *parse(uchar *p, int n){	int len, code;	Bootp *bp; 	bp = (Bootp*)p;	if(n < bp->optmagic - p) {		fprint(2, "dhcpclient: parse: short bootp packet");		return 0;	} 	if(dhcp.xid != nhgetl(bp->xid)) {		fprint(2, "dhcpclient: parse: bad xid: got %ux expected %lux\n",			nhgetl(bp->xid), dhcp.xid);		return 0;	}

Lines 1–500 of 665.