xref: /plan9/sys/src/cmd/ip/dhcpclient.c (revision f27a9a5a0b699d2f44893d9491ecc2336a1fbc19)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <ip.h>
47dd7cddfSDavid du Colombier #include "dhcp.h"
57dd7cddfSDavid du Colombier 
67dd7cddfSDavid du Colombier void	bootpdump(uchar *p, int n);
77dd7cddfSDavid du Colombier void	dhcpinit(void);
8*f27a9a5aSDavid du Colombier void	dhcprecv(void);
9*f27a9a5aSDavid du Colombier void	dhcpsend(int);
10*f27a9a5aSDavid du Colombier void	myfatal(char *fmt, ...);
11*f27a9a5aSDavid du Colombier int	openlisten(char*);
127dd7cddfSDavid du Colombier uchar	*optaddaddr(uchar*, int, uchar*);
13*f27a9a5aSDavid du Colombier uchar	*optaddbyte(uchar*, int, int);
14*f27a9a5aSDavid du Colombier uchar	*optadd(uchar*, int, void*, int);
15*f27a9a5aSDavid du Colombier uchar	*optaddulong(uchar*, int, ulong);
167dd7cddfSDavid du Colombier uchar	*optget(Bootp*, int, int);
17*f27a9a5aSDavid du Colombier int	optgetaddr(Bootp*, int, uchar*);
187dd7cddfSDavid du Colombier int	optgetbyte(Bootp*, int);
197dd7cddfSDavid du Colombier ulong	optgetulong(Bootp*, int);
207dd7cddfSDavid du Colombier Bootp	*parse(uchar*, int);
21*f27a9a5aSDavid du Colombier void	stdinthread(void*);
227dd7cddfSDavid du Colombier ulong	thread(void(*f)(void*), void *a);
23*f27a9a5aSDavid du Colombier void	timerthread(void*);
24*f27a9a5aSDavid du Colombier void	usage(void);
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier struct {
277dd7cddfSDavid du Colombier 	QLock	lk;
287dd7cddfSDavid du Colombier 	int	state;
297dd7cddfSDavid du Colombier 	int	fd;
307dd7cddfSDavid du Colombier 	ulong	xid;
317dd7cddfSDavid du Colombier 	ulong	starttime;
327dd7cddfSDavid du Colombier 	char	cid[100];
337dd7cddfSDavid du Colombier 	char	sname[64];
347dd7cddfSDavid du Colombier 	uchar	server[IPaddrlen];		/* server IP address */
357dd7cddfSDavid du Colombier 	uchar	client[IPaddrlen];		/* client IP address */
367dd7cddfSDavid du Colombier 	uchar	mask[IPaddrlen];		/* client mask */
377dd7cddfSDavid du Colombier 	ulong	lease;		/* lease time */
387dd7cddfSDavid du Colombier 	ulong	resend;		/* number of resends for current state */
397dd7cddfSDavid du Colombier 	ulong	timeout;	/* time to timeout - seconds */
407dd7cddfSDavid du Colombier } dhcp;
417dd7cddfSDavid du Colombier 
429a747e4fSDavid du Colombier char	net[64];
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier char optmagic[4] = { 0x63, 0x82, 0x53, 0x63 };
457dd7cddfSDavid du Colombier 
467dd7cddfSDavid du Colombier void
main(int argc,char * argv[])477dd7cddfSDavid du Colombier main(int argc, char *argv[])
487dd7cddfSDavid du Colombier {
497dd7cddfSDavid du Colombier 	char *p;
507dd7cddfSDavid du Colombier 
517dd7cddfSDavid du Colombier 	setnetmtpt(net, sizeof(net), nil);
527dd7cddfSDavid du Colombier 
537dd7cddfSDavid du Colombier 	ARGBEGIN{
547dd7cddfSDavid du Colombier 	case 'x':
557dd7cddfSDavid du Colombier 		p = ARGF();
567dd7cddfSDavid du Colombier 		if(p == nil)
577dd7cddfSDavid du Colombier 			usage();
587dd7cddfSDavid du Colombier 		setnetmtpt(net, sizeof(net), p);
597dd7cddfSDavid du Colombier 	}ARGEND;
607dd7cddfSDavid du Colombier 
619a747e4fSDavid du Colombier 	fmtinstall('E', eipfmt);
629a747e4fSDavid du Colombier 	fmtinstall('I', eipfmt);
639a747e4fSDavid du Colombier 	fmtinstall('V', eipfmt);
647dd7cddfSDavid du Colombier 
657dd7cddfSDavid du Colombier 	dhcpinit();
667dd7cddfSDavid du Colombier 
677dd7cddfSDavid du Colombier 	rfork(RFNOTEG|RFREND);
687dd7cddfSDavid du Colombier 
697dd7cddfSDavid du Colombier 	thread(timerthread, 0);
707dd7cddfSDavid du Colombier 	thread(stdinthread, 0);
717dd7cddfSDavid du Colombier 
727dd7cddfSDavid du Colombier 	qlock(&dhcp.lk);
737dd7cddfSDavid du Colombier 	dhcp.starttime = time(0);
747dd7cddfSDavid du Colombier 	dhcp.fd = openlisten(net);
757dd7cddfSDavid du Colombier 	dhcpsend(Discover);
767dd7cddfSDavid du Colombier 	dhcp.state = Sselecting;
777dd7cddfSDavid du Colombier 	dhcp.resend = 0;
787dd7cddfSDavid du Colombier 	dhcp.timeout = 4;
797dd7cddfSDavid du Colombier 
807dd7cddfSDavid du Colombier 	while(dhcp.state != Sbound)
817dd7cddfSDavid du Colombier 		dhcprecv();
827dd7cddfSDavid du Colombier 
837dd7cddfSDavid du Colombier 	/* allows other clients on this machine */
847dd7cddfSDavid du Colombier 	close(dhcp.fd);
857dd7cddfSDavid du Colombier 	dhcp.fd = -1;
867dd7cddfSDavid du Colombier 
877dd7cddfSDavid du Colombier 	print("ip=%I\n", dhcp.client);
887dd7cddfSDavid du Colombier 	print("mask=%I\n", dhcp.mask);
897dd7cddfSDavid du Colombier 	print("end\n");
907dd7cddfSDavid du Colombier 
917dd7cddfSDavid du Colombier 	/* keep lease alive */
927dd7cddfSDavid du Colombier 	for(;;) {
937dd7cddfSDavid du Colombier //fprint(2, "got lease for %d\n", dhcp.lease);
947dd7cddfSDavid du Colombier 		qunlock(&dhcp.lk);
95*f27a9a5aSDavid du Colombier 		sleep(dhcp.lease*500);	/* wait half of lease time */
967dd7cddfSDavid du Colombier 		qlock(&dhcp.lk);
977dd7cddfSDavid du Colombier 
987dd7cddfSDavid du Colombier //fprint(2, "try renue\n", dhcp.lease);
997dd7cddfSDavid du Colombier 		dhcp.starttime = time(0);
1007dd7cddfSDavid du Colombier 		dhcp.fd = openlisten(net);
1017dd7cddfSDavid du Colombier 		dhcp.xid = time(0)*getpid();
1027dd7cddfSDavid du Colombier 		dhcpsend(Request);
1037dd7cddfSDavid du Colombier 		dhcp.state = Srenewing;
1047dd7cddfSDavid du Colombier 		dhcp.resend = 0;
1057dd7cddfSDavid du Colombier 		dhcp.timeout = 1;
1067dd7cddfSDavid du Colombier 
1077dd7cddfSDavid du Colombier 		while(dhcp.state != Sbound)
1087dd7cddfSDavid du Colombier 			dhcprecv();
1097dd7cddfSDavid du Colombier 
1107dd7cddfSDavid du Colombier 		/* allows other clients on this machine */
1117dd7cddfSDavid du Colombier 		close(dhcp.fd);
1127dd7cddfSDavid du Colombier 		dhcp.fd = -1;
1137dd7cddfSDavid du Colombier 	}
1147dd7cddfSDavid du Colombier }
1157dd7cddfSDavid du Colombier 
1167dd7cddfSDavid du Colombier void
usage(void)1177dd7cddfSDavid du Colombier usage(void)
1187dd7cddfSDavid du Colombier {
1197dd7cddfSDavid du Colombier 	fprint(2, "usage: %s [-x netextension]\n", argv0);
1207dd7cddfSDavid du Colombier 	exits("usage");
1217dd7cddfSDavid du Colombier }
1227dd7cddfSDavid du Colombier 
1237dd7cddfSDavid du Colombier void
timerthread(void *)1247dd7cddfSDavid du Colombier timerthread(void*)
1257dd7cddfSDavid du Colombier {
1267dd7cddfSDavid du Colombier 	for(;;) {
1277dd7cddfSDavid du Colombier 		sleep(1000);
1287dd7cddfSDavid du Colombier 		qlock(&dhcp.lk);
1297dd7cddfSDavid du Colombier 		if(--dhcp.timeout > 0) {
1307dd7cddfSDavid du Colombier 			qunlock(&dhcp.lk);
1317dd7cddfSDavid du Colombier 			continue;
1327dd7cddfSDavid du Colombier 		}
1337dd7cddfSDavid du Colombier 
1347dd7cddfSDavid du Colombier 		switch(dhcp.state) {
1357dd7cddfSDavid du Colombier 		default:
1367dd7cddfSDavid du Colombier 			myfatal("timerthread: unknown state %d", dhcp.state);
1377dd7cddfSDavid du Colombier 		case Sinit:
1387dd7cddfSDavid du Colombier 			break;
1397dd7cddfSDavid du Colombier 		case Sselecting:
1407dd7cddfSDavid du Colombier 			dhcpsend(Discover);
1417dd7cddfSDavid du Colombier 			dhcp.timeout = 4;
1427dd7cddfSDavid du Colombier 			dhcp.resend++;
1437dd7cddfSDavid du Colombier 			if(dhcp.resend>5)
1447dd7cddfSDavid du Colombier 				myfatal("dhcp: giving up: selecting");
1457dd7cddfSDavid du Colombier 			break;
1467dd7cddfSDavid du Colombier 		case Srequesting:
1477dd7cddfSDavid du Colombier 			dhcpsend(Request);
1487dd7cddfSDavid du Colombier 			dhcp.timeout = 4;
1497dd7cddfSDavid du Colombier 			dhcp.resend++;
1507dd7cddfSDavid du Colombier 			if(dhcp.resend>5)
1517dd7cddfSDavid du Colombier 				myfatal("dhcp: giving up: requesting");
1527dd7cddfSDavid du Colombier 			break;
1537dd7cddfSDavid du Colombier 		case Srenewing:
1547dd7cddfSDavid du Colombier 			dhcpsend(Request);
1557dd7cddfSDavid du Colombier 			dhcp.timeout = 1;
1567dd7cddfSDavid du Colombier 			dhcp.resend++;
1577dd7cddfSDavid du Colombier 			if(dhcp.resend>3) {
1587dd7cddfSDavid du Colombier 				dhcp.state = Srebinding;
1597dd7cddfSDavid du Colombier 				dhcp.resend = 0;
1607dd7cddfSDavid du Colombier 			}
1617dd7cddfSDavid du Colombier 			break;
1627dd7cddfSDavid du Colombier 		case Srebinding:
1637dd7cddfSDavid du Colombier 			dhcpsend(Request);
1647dd7cddfSDavid du Colombier 			dhcp.timeout = 4;
1657dd7cddfSDavid du Colombier 			dhcp.resend++;
1667dd7cddfSDavid du Colombier 			if(dhcp.resend>5)
1677dd7cddfSDavid du Colombier 				myfatal("dhcp: giving up: rebinding");
1687dd7cddfSDavid du Colombier 			break;
1697dd7cddfSDavid du Colombier 		case Sbound:
1707dd7cddfSDavid du Colombier 			break;
1717dd7cddfSDavid du Colombier 		}
1727dd7cddfSDavid du Colombier 		qunlock(&dhcp.lk);
1737dd7cddfSDavid du Colombier 	}
1747dd7cddfSDavid du Colombier }
1757dd7cddfSDavid du Colombier 
1767dd7cddfSDavid du Colombier void
stdinthread(void *)1777dd7cddfSDavid du Colombier stdinthread(void*)
1787dd7cddfSDavid du Colombier {
1797dd7cddfSDavid du Colombier 	uchar buf[100];
1807dd7cddfSDavid du Colombier 	int n;
1817dd7cddfSDavid du Colombier 
1827dd7cddfSDavid du Colombier 	for(;;) {
1837dd7cddfSDavid du Colombier 		n = read(0, buf, sizeof(buf));
1847dd7cddfSDavid du Colombier 		if(n <= 0)
1857dd7cddfSDavid du Colombier 			break;
1867dd7cddfSDavid du Colombier 	}
1877dd7cddfSDavid du Colombier 	/* shutdown cleanly */
1887dd7cddfSDavid du Colombier 	qlock(&dhcp.lk);
1897dd7cddfSDavid du Colombier 	if(dhcp.client) {
1907dd7cddfSDavid du Colombier 		if(dhcp.fd < 0)
1917dd7cddfSDavid du Colombier 			dhcp.fd = openlisten(net);
1927dd7cddfSDavid du Colombier 		dhcpsend(Release);
1937dd7cddfSDavid du Colombier 	}
1947dd7cddfSDavid du Colombier 	qunlock(&dhcp.lk);
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier 	postnote(PNGROUP, getpid(), "die");
1977dd7cddfSDavid du Colombier 	exits(0);
1987dd7cddfSDavid du Colombier }
1997dd7cddfSDavid du Colombier 
2007dd7cddfSDavid du Colombier void
dhcpinit(void)2017dd7cddfSDavid du Colombier dhcpinit(void)
2027dd7cddfSDavid du Colombier {
2037dd7cddfSDavid du Colombier 	int fd;
2047dd7cddfSDavid du Colombier 
2057dd7cddfSDavid du Colombier 	dhcp.state = Sinit;
2067dd7cddfSDavid du Colombier 	dhcp.timeout = 4;
2077dd7cddfSDavid du Colombier 
2087dd7cddfSDavid du Colombier 	fd = open("/dev/random", 0);
2097dd7cddfSDavid du Colombier 	if(fd >= 0) {
2107dd7cddfSDavid du Colombier 		read(fd, &dhcp.xid, sizeof(dhcp.xid));
2117dd7cddfSDavid du Colombier 		close(fd);
2127dd7cddfSDavid du Colombier 	} else
2137dd7cddfSDavid du Colombier 		dhcp.xid = time(0)*getpid();
2147dd7cddfSDavid du Colombier 	srand(dhcp.xid);
2157dd7cddfSDavid du Colombier 
2167dd7cddfSDavid du Colombier 	sprint(dhcp.cid, "%s.%d", getenv("sysname"), getpid());
2177dd7cddfSDavid du Colombier }
2187dd7cddfSDavid du Colombier 
2197dd7cddfSDavid du Colombier void
dhcpsend(int type)2207dd7cddfSDavid du Colombier dhcpsend(int type)
2217dd7cddfSDavid du Colombier {
2227dd7cddfSDavid du Colombier 	int n;
223*f27a9a5aSDavid du Colombier 	uchar *p;
224*f27a9a5aSDavid du Colombier 	Bootp bp;
225*f27a9a5aSDavid du Colombier 	Udphdr *up;
2267dd7cddfSDavid du Colombier 
227*f27a9a5aSDavid du Colombier 	memset(&bp, 0, sizeof bp);
228*f27a9a5aSDavid du Colombier 	up = (Udphdr*)bp.udphdr;
2297dd7cddfSDavid du Colombier 
2307dd7cddfSDavid du Colombier 	hnputs(up->rport, 67);
2317dd7cddfSDavid du Colombier 	bp.op = Bootrequest;
2327dd7cddfSDavid du Colombier 	hnputl(bp.xid, dhcp.xid);
2337dd7cddfSDavid du Colombier 	hnputs(bp.secs, time(0) - dhcp.starttime);
234*f27a9a5aSDavid du Colombier 	hnputs(bp.flags, Fbroadcast);		/* reply must be broadcast */
2357dd7cddfSDavid du Colombier 	memmove(bp.optmagic, optmagic, 4);
2367dd7cddfSDavid du Colombier 	p = bp.optdata;
2377dd7cddfSDavid du Colombier 	p = optaddbyte(p, ODtype, type);
2387dd7cddfSDavid du Colombier 	p = optadd(p, ODclientid, dhcp.cid, strlen(dhcp.cid));
2397dd7cddfSDavid du Colombier 	switch(type) {
2407dd7cddfSDavid du Colombier 	default:
2417dd7cddfSDavid du Colombier 		myfatal("dhcpsend: unknown message type: %d", type);
2427dd7cddfSDavid du Colombier 	case Discover:
243*f27a9a5aSDavid du Colombier 		ipmove(up->raddr, IPv4bcast);	/* broadcast */
2447dd7cddfSDavid du Colombier 		break;
2457dd7cddfSDavid du Colombier 	case Request:
2467dd7cddfSDavid du Colombier 		if(dhcp.state == Sbound || dhcp.state == Srenewing)
2477dd7cddfSDavid du Colombier 			ipmove(up->raddr, dhcp.server);
2487dd7cddfSDavid du Colombier 		else
249*f27a9a5aSDavid du Colombier 			ipmove(up->raddr, IPv4bcast);	/* broadcast */
2507dd7cddfSDavid du Colombier 		p = optaddulong(p, ODlease, dhcp.lease);
2517dd7cddfSDavid du Colombier 		if(dhcp.state == Sselecting || dhcp.state == Srequesting) {
252*f27a9a5aSDavid du Colombier 			p = optaddaddr(p, ODipaddr, dhcp.client);	/* mistake?? */
2537dd7cddfSDavid du Colombier 			p = optaddaddr(p, ODserverid, dhcp.server);
2547dd7cddfSDavid du Colombier 		} else
2557dd7cddfSDavid du Colombier 			v6tov4(bp.ciaddr, dhcp.client);
2567dd7cddfSDavid du Colombier 		break;
2577dd7cddfSDavid du Colombier 	case Release:
2587dd7cddfSDavid du Colombier 		ipmove(up->raddr, dhcp.server);
2597dd7cddfSDavid du Colombier 		v6tov4(bp.ciaddr, dhcp.client);
2607dd7cddfSDavid du Colombier 		p = optaddaddr(p, ODipaddr, dhcp.client);
2617dd7cddfSDavid du Colombier 		p = optaddaddr(p, ODserverid, dhcp.server);
2627dd7cddfSDavid du Colombier 		break;
2637dd7cddfSDavid du Colombier 	}
2647dd7cddfSDavid du Colombier 
2657dd7cddfSDavid du Colombier 	*p++ = OBend;
2667dd7cddfSDavid du Colombier 
2677dd7cddfSDavid du Colombier 	n = p - (uchar*)&bp;
2687dd7cddfSDavid du Colombier 
2697dd7cddfSDavid du Colombier 	if(write(dhcp.fd, &bp, n) != n)
2707dd7cddfSDavid du Colombier 		myfatal("dhcpsend: write failed: %r");
2717dd7cddfSDavid du Colombier }
2727dd7cddfSDavid du Colombier 
2737dd7cddfSDavid du Colombier void
dhcprecv(void)2747dd7cddfSDavid du Colombier dhcprecv(void)
2757dd7cddfSDavid du Colombier {
2767dd7cddfSDavid du Colombier 	uchar buf[2000];
2777dd7cddfSDavid du Colombier 	Bootp *bp;
2787dd7cddfSDavid du Colombier 	int n, type;
2797dd7cddfSDavid du Colombier 	ulong lease;
2807dd7cddfSDavid du Colombier 	uchar mask[IPaddrlen];
2817dd7cddfSDavid du Colombier 
2827dd7cddfSDavid du Colombier 	qunlock(&dhcp.lk);
2837dd7cddfSDavid du Colombier 	n = read(dhcp.fd, buf, sizeof(buf));
2847dd7cddfSDavid du Colombier 	qlock(&dhcp.lk);
2857dd7cddfSDavid du Colombier 
2867dd7cddfSDavid du Colombier 	if(n <= 0)
2877dd7cddfSDavid du Colombier 		myfatal("dhcprecv: bad read: %r");
2887dd7cddfSDavid du Colombier 
2897dd7cddfSDavid du Colombier 	bp = parse(buf, n);
2907dd7cddfSDavid du Colombier 	if(bp == 0)
2917dd7cddfSDavid du Colombier 		return;
2927dd7cddfSDavid du Colombier 
293*f27a9a5aSDavid du Colombier if(1) {
2947dd7cddfSDavid du Colombier fprint(2, "recved\n");
2957dd7cddfSDavid du Colombier bootpdump(buf, n);
2967dd7cddfSDavid du Colombier }
2977dd7cddfSDavid du Colombier 
2987dd7cddfSDavid du Colombier 	type = optgetbyte(bp, ODtype);
2997dd7cddfSDavid du Colombier 	switch(type) {
3007dd7cddfSDavid du Colombier 	default:
3017dd7cddfSDavid du Colombier 		fprint(2, "dhcprecv: unknown type: %d\n", type);
3027dd7cddfSDavid du Colombier 		break;
3037dd7cddfSDavid du Colombier 	case Offer:
3047dd7cddfSDavid du Colombier 		if(dhcp.state != Sselecting)
3057dd7cddfSDavid du Colombier 			break;
3067dd7cddfSDavid du Colombier 		lease = optgetulong(bp, ODlease);
3077dd7cddfSDavid du Colombier 		if(lease == 0)
3087dd7cddfSDavid du Colombier 			myfatal("bad lease");
3097dd7cddfSDavid du Colombier 		if(!optgetaddr(bp, OBmask, mask))
3107dd7cddfSDavid du Colombier 			memset(mask, 0xff, sizeof(mask));
3117dd7cddfSDavid du Colombier 		v4tov6(dhcp.client, bp->yiaddr);
3127dd7cddfSDavid du Colombier 		if(!optgetaddr(bp, ODserverid, dhcp.server)) {
3137dd7cddfSDavid du Colombier 			fprint(2, "dhcprecv: Offer from server with invalid serverid\n");
3147dd7cddfSDavid du Colombier 			break;
3157dd7cddfSDavid du Colombier 		}
3167dd7cddfSDavid du Colombier 
3177dd7cddfSDavid du Colombier 		dhcp.lease = lease;
3187dd7cddfSDavid du Colombier 		ipmove(dhcp.mask, mask);
3197dd7cddfSDavid du Colombier 		memmove(dhcp.sname, bp->sname, sizeof(dhcp.sname));
3207dd7cddfSDavid du Colombier 		dhcp.sname[sizeof(dhcp.sname)-1] = 0;
3217dd7cddfSDavid du Colombier 
3227dd7cddfSDavid du Colombier 		dhcpsend(Request);
3237dd7cddfSDavid du Colombier 		dhcp.state = Srequesting;
3247dd7cddfSDavid du Colombier 		dhcp.resend = 0;
3257dd7cddfSDavid du Colombier 		dhcp.timeout = 4;
3267dd7cddfSDavid du Colombier 		break;
3277dd7cddfSDavid du Colombier 	case Ack:
3287dd7cddfSDavid du Colombier 		if(dhcp.state != Srequesting)
3297dd7cddfSDavid du Colombier 		if(dhcp.state != Srenewing)
3307dd7cddfSDavid du Colombier 		if(dhcp.state != Srebinding)
3317dd7cddfSDavid du Colombier 			break;
3327dd7cddfSDavid du Colombier 		lease = optgetulong(bp, ODlease);
3337dd7cddfSDavid du Colombier 		if(lease == 0)
3347dd7cddfSDavid du Colombier 			myfatal("bad lease");
3357dd7cddfSDavid du Colombier 		if(!optgetaddr(bp, OBmask, mask))
3367dd7cddfSDavid du Colombier 			memset(mask, 0xff, sizeof(mask));
3377dd7cddfSDavid du Colombier 		v4tov6(dhcp.client, bp->yiaddr);
3387dd7cddfSDavid du Colombier 		dhcp.lease = lease;
3397dd7cddfSDavid du Colombier 		ipmove(dhcp.mask, mask);
3407dd7cddfSDavid du Colombier 		dhcp.state = Sbound;
3417dd7cddfSDavid du Colombier 		break;
3427dd7cddfSDavid du Colombier 	case Nak:
3437dd7cddfSDavid du Colombier 		myfatal("recved nak");
3447dd7cddfSDavid du Colombier 		break;
3457dd7cddfSDavid du Colombier 	}
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier }
3487dd7cddfSDavid du Colombier 
3497dd7cddfSDavid du Colombier int
openlisten(char * net)3507dd7cddfSDavid du Colombier openlisten(char *net)
3517dd7cddfSDavid du Colombier {
352*f27a9a5aSDavid du Colombier 	int n, fd, cfd;
353*f27a9a5aSDavid du Colombier 	char data[128], devdir[40];
3547dd7cddfSDavid du Colombier 
3557dd7cddfSDavid du Colombier //	sprint(data, "%s/udp!*!bootpc", net);
3567dd7cddfSDavid du Colombier 	sprint(data, "%s/udp!*!68", net);
3577dd7cddfSDavid du Colombier 	for(n = 0; ; n++) {
3587dd7cddfSDavid du Colombier 		cfd = announce(data, devdir);
3597dd7cddfSDavid du Colombier 		if(cfd >= 0)
3607dd7cddfSDavid du Colombier 			break;
3617dd7cddfSDavid du Colombier 		/* might be another client - wait and try again */
362*f27a9a5aSDavid du Colombier 		fprint(2, "dhcpclient: can't announce %s: %r", data);
3637dd7cddfSDavid du Colombier 		sleep(1000);
3647dd7cddfSDavid du Colombier 		if(n > 10)
3657dd7cddfSDavid du Colombier 			myfatal("can't announce: giving up: %r");
3667dd7cddfSDavid du Colombier 	}
3677dd7cddfSDavid du Colombier 
3687dd7cddfSDavid du Colombier 	if(fprint(cfd, "headers") < 0)
3697dd7cddfSDavid du Colombier 		myfatal("can't set header mode: %r");
3707dd7cddfSDavid du Colombier 
3717dd7cddfSDavid du Colombier 	sprint(data, "%s/data", devdir);
3727dd7cddfSDavid du Colombier 	fd = open(data, ORDWR);
3737dd7cddfSDavid du Colombier 	if(fd < 0)
374*f27a9a5aSDavid du Colombier 		myfatal("open %s: %r", data);
3757dd7cddfSDavid du Colombier 	close(cfd);
3767dd7cddfSDavid du Colombier 	return fd;
3777dd7cddfSDavid du Colombier }
3787dd7cddfSDavid du Colombier 
3797dd7cddfSDavid du Colombier uchar*
optadd(uchar * p,int op,void * d,int n)3807dd7cddfSDavid du Colombier optadd(uchar *p, int op, void *d, int n)
3817dd7cddfSDavid du Colombier {
3827dd7cddfSDavid du Colombier 	p[0] = op;
3837dd7cddfSDavid du Colombier 	p[1] = n;
3847dd7cddfSDavid du Colombier 	memmove(p+2, d, n);
3857dd7cddfSDavid du Colombier 	return p+n+2;
3867dd7cddfSDavid du Colombier }
3877dd7cddfSDavid du Colombier 
3887dd7cddfSDavid du Colombier uchar*
optaddbyte(uchar * p,int op,int b)3897dd7cddfSDavid du Colombier optaddbyte(uchar *p, int op, int b)
3907dd7cddfSDavid du Colombier {
3917dd7cddfSDavid du Colombier 	p[0] = op;
3927dd7cddfSDavid du Colombier 	p[1] = 1;
3937dd7cddfSDavid du Colombier 	p[2] = b;
3947dd7cddfSDavid du Colombier 	return p+3;
3957dd7cddfSDavid du Colombier }
3967dd7cddfSDavid du Colombier 
3977dd7cddfSDavid du Colombier uchar*
optaddulong(uchar * p,int op,ulong x)3987dd7cddfSDavid du Colombier optaddulong(uchar *p, int op, ulong x)
3997dd7cddfSDavid du Colombier {
4007dd7cddfSDavid du Colombier 	p[0] = op;
4017dd7cddfSDavid du Colombier 	p[1] = 4;
4027dd7cddfSDavid du Colombier 	hnputl(p+2, x);
4037dd7cddfSDavid du Colombier 	return p+6;
4047dd7cddfSDavid du Colombier }
4057dd7cddfSDavid du Colombier 
4067dd7cddfSDavid du Colombier uchar *
optaddaddr(uchar * p,int op,uchar * ip)4077dd7cddfSDavid du Colombier optaddaddr(uchar *p, int op, uchar *ip)
4087dd7cddfSDavid du Colombier {
4097dd7cddfSDavid du Colombier 	p[0] = op;
4107dd7cddfSDavid du Colombier 	p[1] = 4;
4117dd7cddfSDavid du Colombier 	v6tov4(p+2, ip);
4127dd7cddfSDavid du Colombier 	return p+6;
4137dd7cddfSDavid du Colombier }
4147dd7cddfSDavid du Colombier 
4157dd7cddfSDavid du Colombier uchar*
optget(Bootp * bp,int op,int n)4167dd7cddfSDavid du Colombier optget(Bootp *bp, int op, int n)
4177dd7cddfSDavid du Colombier {
4187dd7cddfSDavid du Colombier 	int len, code;
4197dd7cddfSDavid du Colombier 	uchar *p;
4207dd7cddfSDavid du Colombier 
4217dd7cddfSDavid du Colombier 	p = bp->optdata;
4227dd7cddfSDavid du Colombier 	for(;;) {
4237dd7cddfSDavid du Colombier 		code = *p++;
4247dd7cddfSDavid du Colombier 		if(code == OBpad)
4257dd7cddfSDavid du Colombier 			continue;
4267dd7cddfSDavid du Colombier 		if(code == OBend)
4277dd7cddfSDavid du Colombier 			return 0;
4287dd7cddfSDavid du Colombier 		len = *p++;
4297dd7cddfSDavid du Colombier 		if(code != op) {
4307dd7cddfSDavid du Colombier 			p += len;
4317dd7cddfSDavid du Colombier 			continue;
4327dd7cddfSDavid du Colombier 		}
4337dd7cddfSDavid du Colombier 		if(n && n != len)
4347dd7cddfSDavid du Colombier 			return 0;
4357dd7cddfSDavid du Colombier 		return p;
4367dd7cddfSDavid du Colombier 	}
4377dd7cddfSDavid du Colombier }
4387dd7cddfSDavid du Colombier 
4397dd7cddfSDavid du Colombier 
4407dd7cddfSDavid du Colombier int
optgetbyte(Bootp * bp,int op)4417dd7cddfSDavid du Colombier optgetbyte(Bootp *bp, int op)
4427dd7cddfSDavid du Colombier {
4437dd7cddfSDavid du Colombier 	uchar *p;
4447dd7cddfSDavid du Colombier 
4457dd7cddfSDavid du Colombier 	p = optget(bp, op, 1);
4467dd7cddfSDavid du Colombier 	if(p == 0)
4477dd7cddfSDavid du Colombier 		return 0;
4487dd7cddfSDavid du Colombier 	return *p;
4497dd7cddfSDavid du Colombier }
4507dd7cddfSDavid du Colombier 
4517dd7cddfSDavid du Colombier ulong
optgetulong(Bootp * bp,int op)4527dd7cddfSDavid du Colombier optgetulong(Bootp *bp, int op)
4537dd7cddfSDavid du Colombier {
4547dd7cddfSDavid du Colombier 	uchar *p;
4557dd7cddfSDavid du Colombier 
4567dd7cddfSDavid du Colombier 	p = optget(bp, op, 4);
4577dd7cddfSDavid du Colombier 	if(p == 0)
4587dd7cddfSDavid du Colombier 		return 0;
4597dd7cddfSDavid du Colombier 	return nhgetl(p);
4607dd7cddfSDavid du Colombier }
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier int
optgetaddr(Bootp * bp,int op,uchar * ip)4637dd7cddfSDavid du Colombier optgetaddr(Bootp *bp, int op, uchar *ip)
4647dd7cddfSDavid du Colombier {
4657dd7cddfSDavid du Colombier 	uchar *p;
4667dd7cddfSDavid du Colombier 
4677dd7cddfSDavid du Colombier 	p = optget(bp, op, 4);
4687dd7cddfSDavid du Colombier 	if(p == 0)
4697dd7cddfSDavid du Colombier 		return 0;
4707dd7cddfSDavid du Colombier 	v4tov6(ip, p);
4717dd7cddfSDavid du Colombier 	return 1;
4727dd7cddfSDavid du Colombier }
4737dd7cddfSDavid du Colombier 
4747dd7cddfSDavid du Colombier /* make sure packet looks ok */
4757dd7cddfSDavid du Colombier Bootp *
parse(uchar * p,int n)4767dd7cddfSDavid du Colombier parse(uchar *p, int n)
4777dd7cddfSDavid du Colombier {
4787dd7cddfSDavid du Colombier 	int len, code;
4797dd7cddfSDavid du Colombier 	Bootp *bp;
4807dd7cddfSDavid du Colombier 
4817dd7cddfSDavid du Colombier 	bp = (Bootp*)p;
4827dd7cddfSDavid du Colombier 	if(n < bp->optmagic - p) {
4837dd7cddfSDavid du Colombier 		fprint(2, "dhcpclient: parse: short bootp packet");
4847dd7cddfSDavid du Colombier 		return 0;
4857dd7cddfSDavid du Colombier 	}
4867dd7cddfSDavid du Colombier 
4877dd7cddfSDavid du Colombier 	if(dhcp.xid != nhgetl(bp->xid)) {
488*f27a9a5aSDavid du Colombier 		fprint(2, "dhcpclient: parse: bad xid: got %ux expected %lux\n",
489*f27a9a5aSDavid du Colombier 			nhgetl(bp->xid), dhcp.xid);
4907dd7cddfSDavid du Colombier 		return 0;
4917dd7cddfSDavid du Colombier 	}
4927dd7cddfSDavid du Colombier 
4937dd7cddfSDavid du Colombier 	if(bp->op != Bootreply) {
4947dd7cddfSDavid du Colombier 		fprint(2, "dhcpclient: parse: bad op\n");
4957dd7cddfSDavid du Colombier 		return 0;
4967dd7cddfSDavid du Colombier 	}
4977dd7cddfSDavid du Colombier 
4987dd7cddfSDavid du Colombier 	n -= bp->optmagic - p;
4997dd7cddfSDavid du Colombier 	p = bp->optmagic;
5007dd7cddfSDavid du Colombier 
5017dd7cddfSDavid du Colombier 	if(n < 4) {
5027dd7cddfSDavid du Colombier 		fprint(2, "dhcpclient: parse: not option data");
5037dd7cddfSDavid du Colombier 		return 0;
5047dd7cddfSDavid du Colombier 	}
5057dd7cddfSDavid du Colombier 	if(memcmp(optmagic, p, 4) != 0) {
506*f27a9a5aSDavid du Colombier 		fprint(2, "dhcpclient: parse: bad opt magic %ux %ux %ux %ux\n",
507*f27a9a5aSDavid du Colombier 			p[0], p[1], p[2], p[3]);
5087dd7cddfSDavid du Colombier 		return 0;
5097dd7cddfSDavid du Colombier 	}
5107dd7cddfSDavid du Colombier 	p += 4;
5117dd7cddfSDavid du Colombier 	n -= 4;
5127dd7cddfSDavid du Colombier 	while(n>0) {
5137dd7cddfSDavid du Colombier 		code = *p++;
5147dd7cddfSDavid du Colombier 		n--;
5157dd7cddfSDavid du Colombier 		if(code == OBpad)
5167dd7cddfSDavid du Colombier 			continue;
5177dd7cddfSDavid du Colombier 		if(code == OBend)
5187dd7cddfSDavid du Colombier 			return bp;
5197dd7cddfSDavid du Colombier 		if(n == 0) {
5207dd7cddfSDavid du Colombier 			fprint(2, "dhcpclient: parse: bad option: %d", code);
5217dd7cddfSDavid du Colombier 			return 0;
5227dd7cddfSDavid du Colombier 		}
5237dd7cddfSDavid du Colombier 		len = *p++;
5247dd7cddfSDavid du Colombier 		n--;
5257dd7cddfSDavid du Colombier 		if(len > n) {
5267dd7cddfSDavid du Colombier 			fprint(2, "dhcpclient: parse: bad option: %d", code);
5277dd7cddfSDavid du Colombier 			return 0;
5287dd7cddfSDavid du Colombier 		}
5297dd7cddfSDavid du Colombier 		p += len;
5307dd7cddfSDavid du Colombier 		n -= len;
5317dd7cddfSDavid du Colombier 	}
5327dd7cddfSDavid du Colombier 
5337dd7cddfSDavid du Colombier 	/* fix up nonstandard packets */
5347dd7cddfSDavid du Colombier 	/* assume there is space */
5357dd7cddfSDavid du Colombier 	*p = OBend;
5367dd7cddfSDavid du Colombier 
5377dd7cddfSDavid du Colombier 	return bp;
5387dd7cddfSDavid du Colombier }
5397dd7cddfSDavid du Colombier 
5407dd7cddfSDavid du Colombier void
bootpdump(uchar * p,int n)5417dd7cddfSDavid du Colombier bootpdump(uchar *p, int n)
5427dd7cddfSDavid du Colombier {
5437dd7cddfSDavid du Colombier 	int len, i, code;
544*f27a9a5aSDavid du Colombier 	Bootp *bp;
545*f27a9a5aSDavid du Colombier 	Udphdr *up;
5467dd7cddfSDavid du Colombier 
5477dd7cddfSDavid du Colombier 	bp = (Bootp*)p;
548*f27a9a5aSDavid du Colombier 	up = (Udphdr*)bp->udphdr;
5497dd7cddfSDavid du Colombier 
5507dd7cddfSDavid du Colombier 	if(n < bp->optmagic - p) {
5517dd7cddfSDavid du Colombier 		fprint(2, "dhcpclient: short bootp packet");
5527dd7cddfSDavid du Colombier 		return;
5537dd7cddfSDavid du Colombier 	}
5547dd7cddfSDavid du Colombier 
555*f27a9a5aSDavid du Colombier 	fprint(2, "laddr=%I lport=%d raddr=%I rport=%d\n", up->laddr,
556*f27a9a5aSDavid du Colombier 		nhgets(up->lport), up->raddr, nhgets(up->rport));
557*f27a9a5aSDavid du Colombier 	fprint(2, "op=%d htype=%d hlen=%d hops=%d\n", bp->op, bp->htype,
558*f27a9a5aSDavid du Colombier 		bp->hlen, bp->hops);
559*f27a9a5aSDavid du Colombier 	fprint(2, "xid=%ux secs=%d flags=%ux\n", nhgetl(bp->xid),
560*f27a9a5aSDavid du Colombier 		nhgets(bp->secs), nhgets(bp->flags));
5617dd7cddfSDavid du Colombier 	fprint(2, "ciaddr=%V yiaddr=%V siaddr=%V giaddr=%V\n",
5627dd7cddfSDavid du Colombier 		bp->ciaddr, bp->yiaddr, bp->siaddr, bp->giaddr);
5637dd7cddfSDavid du Colombier 	fprint(2, "chaddr=");
5647dd7cddfSDavid du Colombier 	for(i=0; i<16; i++)
5657dd7cddfSDavid du Colombier 		fprint(2, "%ux ", bp->chaddr[i]);
5667dd7cddfSDavid du Colombier 	fprint(2, "\n");
5677dd7cddfSDavid du Colombier 	fprint(2, "sname=%s\n", bp->sname);
5687dd7cddfSDavid du Colombier 	fprint(2, "file = %s\n", bp->file);
5697dd7cddfSDavid du Colombier 
5707dd7cddfSDavid du Colombier 	n -= bp->optmagic - p;
5717dd7cddfSDavid du Colombier 	p = bp->optmagic;
5727dd7cddfSDavid du Colombier 
5737dd7cddfSDavid du Colombier 	if(n < 4)
5747dd7cddfSDavid du Colombier 		return;
5757dd7cddfSDavid du Colombier 	if(memcmp(optmagic, p, 4) != 0)
576*f27a9a5aSDavid du Colombier 		fprint(2, "dhcpclient: bad opt magic %ux %ux %ux %ux\n",
577*f27a9a5aSDavid du Colombier 			p[0], p[1], p[2], p[3]);
5787dd7cddfSDavid du Colombier 	p += 4;
5797dd7cddfSDavid du Colombier 	n -= 4;
5807dd7cddfSDavid du Colombier 
5817dd7cddfSDavid du Colombier 	while(n>0) {
5827dd7cddfSDavid du Colombier 		code = *p++;
5837dd7cddfSDavid du Colombier 		n--;
5847dd7cddfSDavid du Colombier 		if(code == OBpad)
5857dd7cddfSDavid du Colombier 			continue;
5867dd7cddfSDavid du Colombier 		if(code == OBend)
5877dd7cddfSDavid du Colombier 			break;
5887dd7cddfSDavid du Colombier 		if(n == 0) {
5897dd7cddfSDavid du Colombier 			fprint(2, " bad option: %d", code);
5907dd7cddfSDavid du Colombier 			return;
5917dd7cddfSDavid du Colombier 		}
5927dd7cddfSDavid du Colombier 		len = *p++;
5937dd7cddfSDavid du Colombier 		n--;
5947dd7cddfSDavid du Colombier 		if(len > n) {
5957dd7cddfSDavid du Colombier 			fprint(2, " bad option: %d", code);
5967dd7cddfSDavid du Colombier 			return;
5977dd7cddfSDavid du Colombier 		}
5987dd7cddfSDavid du Colombier 		switch(code) {
5997dd7cddfSDavid du Colombier 		default:
6007dd7cddfSDavid du Colombier 			fprint(2, "unknown option %d\n", code);
6017dd7cddfSDavid du Colombier 			for(i = 0; i<len; i++)
6027dd7cddfSDavid du Colombier 				fprint(2, "%ux ", p[i]);
6037dd7cddfSDavid du Colombier 		case ODtype:
6047dd7cddfSDavid du Colombier 			fprint(2, "DHCP type %d\n", p[0]);
6057dd7cddfSDavid du Colombier 			break;
6067dd7cddfSDavid du Colombier 		case ODclientid:
6077dd7cddfSDavid du Colombier 			fprint(2, "client id=");
6087dd7cddfSDavid du Colombier 			for(i = 0; i<len; i++)
6097dd7cddfSDavid du Colombier 				fprint(2, "%ux ", p[i]);
6107dd7cddfSDavid du Colombier 			fprint(2, "\n");
6117dd7cddfSDavid du Colombier 			break;
6127dd7cddfSDavid du Colombier 		case ODlease:
6137dd7cddfSDavid du Colombier 			fprint(2, "lease=%d\n", nhgetl(p));
6147dd7cddfSDavid du Colombier 			break;
6157dd7cddfSDavid du Colombier 		case ODserverid:
6167dd7cddfSDavid du Colombier 			fprint(2, "server id=%V\n", p);
6177dd7cddfSDavid du Colombier 			break;
6187dd7cddfSDavid du Colombier 		case OBmask:
6197dd7cddfSDavid du Colombier 			fprint(2, "mask=%V\n", p);
6207dd7cddfSDavid du Colombier 			break;
6217dd7cddfSDavid du Colombier 		case OBrouter:
6227dd7cddfSDavid du Colombier 			fprint(2, "router=%V\n", p);
6237dd7cddfSDavid du Colombier 			break;
6247dd7cddfSDavid du Colombier 		}
6257dd7cddfSDavid du Colombier 		p += len;
6267dd7cddfSDavid du Colombier 		n -= len;
6277dd7cddfSDavid du Colombier 	}
6287dd7cddfSDavid du Colombier }
6297dd7cddfSDavid du Colombier 
6307dd7cddfSDavid du Colombier ulong
thread(void (* f)(void *),void * a)6317dd7cddfSDavid du Colombier thread(void(*f)(void*), void *a)
6327dd7cddfSDavid du Colombier {
6337dd7cddfSDavid du Colombier 	int pid;
634*f27a9a5aSDavid du Colombier 
6357dd7cddfSDavid du Colombier 	pid = rfork(RFNOWAIT|RFMEM|RFPROC);
6367dd7cddfSDavid du Colombier 	if(pid < 0)
6377dd7cddfSDavid du Colombier 		myfatal("rfork failed: %r");
6387dd7cddfSDavid du Colombier 	if(pid != 0)
6397dd7cddfSDavid du Colombier 		return pid;
6407dd7cddfSDavid du Colombier 	(*f)(a);
641*f27a9a5aSDavid du Colombier 	return 0;	/* never reaches here */
6427dd7cddfSDavid du Colombier }
6437dd7cddfSDavid du Colombier 
6447dd7cddfSDavid du Colombier void
myfatal(char * fmt,...)6457dd7cddfSDavid du Colombier myfatal(char *fmt, ...)
6467dd7cddfSDavid du Colombier {
6477dd7cddfSDavid du Colombier 	char buf[1024];
6487dd7cddfSDavid du Colombier 	va_list arg;
6497dd7cddfSDavid du Colombier 
6507dd7cddfSDavid du Colombier 	va_start(arg, fmt);
6519a747e4fSDavid du Colombier 	vseprint(buf, buf+sizeof(buf), fmt, arg);
6527dd7cddfSDavid du Colombier 	va_end(arg);
6537dd7cddfSDavid du Colombier 	fprint(2, "%s: %s\n", argv0, buf);
6547dd7cddfSDavid du Colombier 	postnote(PNGROUP, getpid(), "die");
6557dd7cddfSDavid du Colombier 	exits(buf);
6567dd7cddfSDavid du Colombier }
657