forsyth20121205-174063a80cc13 years agoHistory
447 lines · 10.3 KB · C
RawDownload
#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h"#include "kernel.h"#include "ip.h"#include "ppp.h" Ipaddr pppdns[2]; static	ulong	fsip;static	ulong	auip;static	ulong	gwip;static	ulong	ipmask;static	ulong	ipaddr;static	ulong	dns1ip;static	ulong	dns2ip; int		dhcpmsgtype;int		debug=0;enum{	Bootrequest = 1,	Bootreply   = 2,}; typedef struct Bootp{	/* udp.c oldheader */	uchar	raddr[IPaddrlen];	uchar	laddr[IPaddrlen];	uchar	rport[2];	uchar	lport[2];	/* bootp itself */	uchar	op;			/* opcode */	uchar	htype;		/* hardware type */	uchar	hlen;			/* hardware address len */	uchar	hops;		/* hops */	uchar	xid[4];		/* a random number */	uchar	secs[2];		/* elapsed snce client started booting */	uchar	flags[2];		/* flags */	uchar	ciaddr[4];		/* client IP address (client tells server) */	uchar	yiaddr[4];		/* client IP address (server tells client) */	uchar	siaddr[4];		/* server IP address */	uchar	giaddr[4];		/* gateway IP address */	uchar	chaddr[16];	/* client hardware address */	uchar	sname[64];	/* server host name (optional) */	uchar	file[128];		/* boot file name */	uchar	vend[128];	/* vendor-specific goo 340 */} Bootp; static	Bootp	req;static	Proc*	rcvprocp;static	int	recv;static	int	done;static	Rendez	bootpr;static	char	rcvbuf[512+2*IPaddrlen+2*2];	  /* 576 */static	uchar sid[4];static	ulong iplease; /* * bootp returns: * * "fsip d.d.d.d * auip d.d.d.d * gwip d.d.d.d * ipmask d.d.d.d * ipaddr d.d.d.d * dns1ip	d.d.d.d * dns2ip	d.d.d.d * * where d.d.d.d is the IP address in dotted decimal notation, and each * address is followed by a newline.	Last change:  SUN  13 Sep 2001    4:36 pm */ /* * Parse the vendor specific fields according to RFC 1084. * We are overloading the "cookie server" to be the Inferno  * authentication server and the "resource location server" * to be the Inferno file server. * * If the vendor specific field is formatted properly, it * will being with the four bytes 99.130.83.99 and end with * an 0xFF byte. */static intparsevend(uchar* pvend){		uchar *vend=pvend;	int dhcpmsg=0;	/* The field must start with 99.130.83.99 to be compliant */	if ((vend[0] != 99) || (vend[1] != 130) || (vend[2] != 83) || (vend[3] != 99)){		print("bad bootp vendor field: %.2x%.2x%.2x%.2x", vend[0], vend[1], vend[2], vend[3]);		return -1;	} 	/* Skip over the magic cookie */	vend += 4; 	while ((vend[0] != 0) && (vend[0] != 0xFF)) {		int i;//			if(debug){			print(">>>Opt[%d] [%d]", vend[0], vend[1]);			for(i=0; i<vend[1]; i++)				print(" %2.2x", vend[i+2]);			print("\n");		}//		switch (vend[0]) {		case 1:	/* Subnet mask field */			/* There must be only one subnet mask */			if (vend[1] == 4)				ipmask = (vend[2]<<24)|(vend[3]<<16)| (vend[4]<<8)| vend[5];			else{ 				return -1;			}			break; 		case 3:	/* Gateway/router field */			/* We are only concerned with first address */			if (vend[1] >0 && vend[1]%4==0)				gwip = (vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];			else 				return -1;			break;		case 6:	/* domain name server */			if(vend[1]>0 && vend[1] %4==0){				dns1ip=(vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];				if(vend[1]>4)					dns2ip=(vend[6]<<24)|(vend[7]<<16)|(vend[8]<<8)|vend[9];			}else				return -1;			break; 		case 8:	/* "Cookie server" (auth server) field */			/* We are only concerned with first address */			if (vend[1] > 0 && vend[1]%4==0)				auip = (vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];			else				return -1;			break; 		case 11:	/* "Resource loc server" (file server) field */			/* We are only concerned with first address */			if (vend[1] > 0 && vend[1]%4==0)				fsip = (vend[2]<<24)| (vend[3]<<16)| (vend[4]<<8)| vend[5];			else				return -1;			break;		case 51:	/* ip lease time */			if(vend[1]==4){				iplease=(vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];			}else				return -1;			break;		case 53:	/* DHCP message type */			if(vend[1]==1)				dhcpmsg=vend[2];			else				return -1;			break;		case 54:	/* server identifier */			if(vend[1]==4){				memmove(sid, vend+2, 4);			}else				return -1;			break; 		default:	/* Everything else stops us */			break;		} 		/* Skip over the field */		vend += vend[1] + 2;	}	if(debug)		print(">>>Opt[%d] [%d]\n", vend[0], vend[1]);	return dhcpmsg;} static voiddispvend(uchar* pvend){		uchar *vend=pvend; 	//print("<<<Magic : %2.2x%2.2x%2.2x%2.2x\n", vend[0], vend[1], vend[2], vend[3]);		vend += 4;		/* Skip over the magic cookie */	while ((vend[0] != 0) && (vend[0] != 0xFF)) {	//	int i;	  //	print("<<<Opt[%d] [%d]", vend[0], vend[1]);		//for(i=0; i<vend[1]; i++)		//	print(" %2.2x", vend[i+2]);		//print("\n");			vend += vend[1] + 2;	}	//print("<<<Opt[ %2.2x] [%2.2x]\n", vend[0], vend[1]);} static voidrcvbootp(void *a){	int n, fd, dhcp;	Bootp *rp; 	if(waserror())		pexit("", 0);	rcvprocp = up;	/* store for postnote below */	fd = (int)a;	while(done == 0) {		if(debug)			print("rcvbootp:looping\n"); 		n = kread(fd, rcvbuf, sizeof(rcvbuf));		if(n <= 0)			break;		rp = (Bootp*)rcvbuf;		if (memcmp(req.chaddr, rp->chaddr, 6) == 0 && rp->htype == 1 && rp->hlen == 6) {			ipaddr = (rp->yiaddr[0]<<24)| (rp->yiaddr[1]<<16)| (rp->yiaddr[2]<<8)| rp->yiaddr[3];			if(debug)				print("ipaddr = %2.2x %2.2x %2.2x %2.2x \n", rp->yiaddr[0], rp->yiaddr[1], rp->yiaddr[2], rp->yiaddr[3]);			//memmove(req.siaddr, rp->siaddr, 4);	/* siaddr */			dhcp = parsevend(rp->vend);				if(dhcpmsgtype < dhcp){				dhcpmsgtype=dhcp;				recv = 1;				wakeup(&bootpr);				if(dhcp==0 || dhcp ==5 || dhcp == 6 )					break;			}		}	}	poperror();	rcvprocp = nil; 	if(debug)		print("rcvbootp exit\n");	pexit("", 0);} static char*rbootp(Ipifc *ifc){	int cfd, dfd, tries, n;	char ia[5+3*16], im[16], *av[3];	uchar nipaddr[4], ngwip[4], nipmask[4];	char dir[Maxpath];	static uchar vend_rfc1048[] = { 99, 130, 83, 99 };	uchar *vend; 	/*	 * broadcast bootp's till we get a reply,	 * or fixed number of tries	 */	if(debug)	    print("dhcp: bootp() called\n");	tries = 0;	av[1] = "0.0.0.0";	av[2] = "0.0.0.0";	ipifcadd(ifc, av, 3, 0, nil); 	cfd = kannounce("udp!*!68", dir);	if(cfd < 0)		return "dhcp announce failed";	strcat(dir, "/data");	if(kwrite(cfd, "headers", 7) < 0){		kclose(cfd);		return "dhcp ctl headers failed";	}	kwrite(cfd, "oldheaders", 10);	dfd = kopen(dir, ORDWR);	if(dfd < 0){		kclose(cfd);		return "dhcp open data failed";	}	kclose(cfd);		while(tries<1){		tries++;		memset(sid, 0, 4);		iplease=0;		dhcpmsgtype=-2;/* DHCPDISCOVER*/		done = 0;		recv = 0;		kproc("rcvbootp", rcvbootp, (void*)dfd, KPDUPFDG);		/* Prepare DHCPDISCOVER */			memset(&req, 0, sizeof(req));		ipmove(req.raddr, IPv4bcast);		hnputs(req.rport, 67);		req.op = Bootrequest;		req.htype = 1;			/* ethernet (all we know) */		req.hlen = 6;			/* ethernet (all we know) */				memmove(req.chaddr, ifc->mac, 6);	/* Hardware MAC address */		//ipv4local(ifc, req.ciaddr);				/* Fill in the local IP address if we know it */		memset(req.file, 0, sizeof(req.file));		vend=req.vend;		memmove(vend, vend_rfc1048, 4); vend+=4;		*vend++=53; *vend++=1;*vend++=1;		/* dhcp msg type==3, dhcprequest */				*vend++=61;*vend++=7;*vend++=1;		memmove(vend, ifc->mac, 6);vend+=6;		*vend=0xff; 		if(debug)			dispvend(req.vend); 		for(n=0;n<4;n++){			if(kwrite(dfd, &req, sizeof(req))<0)	/* SEND DHCPDISCOVER */				print("DHCPDISCOVER: %r");					tsleep(&bootpr, return0, 0, 1000);	/* wait DHCPOFFER */			if(debug)				print("[DHCP] DISCOVER: msgtype = %d\n", dhcpmsgtype); 			if(dhcpmsgtype==2)		/* DHCPOFFER */				break;			else if(dhcpmsgtype==0)	/* bootp */				return nil;			else if(dhcpmsgtype== -2)	/* time out */				continue;			else				break;					}		if(dhcpmsgtype!=2)			continue; /* DHCPREQUEST */			memset(req.vend, 0, sizeof(req.vend));		vend=req.vend;		memmove(vend, vend_rfc1048, 4);vend+=4;	 		*vend++=53; *vend++=1;*vend++=3;		/* dhcp msg type==3, dhcprequest */ 		*vend++=50;	*vend++=4;				/* requested ip address */		*vend++=(ipaddr >> 24)&0xff;		*vend++=(ipaddr >> 16)&0xff;		*vend++=(ipaddr >> 8) & 0xff;		*vend++=ipaddr & 0xff; 		*vend++=51;*vend++=4;					/* lease time */		*vend++=(iplease>>24)&0xff; *vend++=(iplease>>16)&0xff; *vend++=(iplease>>8)&0xff; *vend++=iplease&0xff; 		*vend++=54; *vend++=4;					/* server identifier */		memmove(vend, sid, 4);	vend+=4;			*vend++=61;*vend++=07;*vend++=01;		/* client identifier */		memmove(vend, ifc->mac, 6);vend+=6;		*vend=0xff;		if(debug) 			dispvend(req.vend); 		if(kwrite(dfd, &req, sizeof(req))<0){			print("DHCPREQUEST: %r");			continue;		}		tsleep(&bootpr, return0, 0, 2000);		if(dhcpmsgtype==5)		/* wait for DHCPACK */			break;		else			continue;		/* CHECK ARP */		/* DHCPDECLINE */	}	kclose(dfd);	done = 1;	if(rcvprocp != nil){		postnote(rcvprocp, 1, "timeout", 0);		rcvprocp = nil;	} 	av[1] = "0.0.0.0";	av[2] = "0.0.0.0";	ipifcrem(ifc, av, 3); 	hnputl(nipaddr, ipaddr);	sprint(ia, "%V", nipaddr);	hnputl(nipmask, ipmask);	sprint(im, "%V", nipmask);	av[1] = ia;	av[2] = im;	ipifcadd(ifc, av, 3, 0, nil); 	if(gwip != 0) {		hnputl(ngwip, gwip);		n = sprint(ia, "add 0.0.0.0 0.0.0.0 %V", ngwip);		routewrite(ifc->conv->p->f, nil, ia, n);	}	return nil;} static intrbootpread(char *bp, ulong offset, int len){	int n, i;	char *buf;	uchar a[4]; 	if(debug)		print("dhcp: bootpread() \n");	buf = smalloc(READSTR);	if(waserror()){		free(buf);		nexterror();	} 	hnputl(a, fsip);	n = snprint(buf, READSTR, "fsip %15V\n", a);	hnputl(a, auip);	n += snprint(buf + n, READSTR-n, "auip %15V\n", a);	hnputl(a, gwip);	n += snprint(buf + n, READSTR-n, "gwip %15V\n", a);	hnputl(a, ipmask);	n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a);	hnputl(a, ipaddr);	n += snprint(buf + n, READSTR-n, "ipaddr %15V\n", a);	n += snprint(buf+n, READSTR-n, "expired %lud\n", iplease); 	n += snprint(buf + n, READSTR-n, "dns");	if(dns2ip){		hnputl(a, dns2ip);		n+=snprint(buf + n, READSTR-n, " %15V", a);	}	if(dns1ip){		hnputl(a, dns1ip);		n += snprint(buf + n, READSTR-n, " %15V", a);	} 	for(i=0; i<2; i++)		if(ipcmp(pppdns[i], IPnoaddr) != 0 && ipcmp(pppdns[i], v4prefix) != 0)			n += snprint(buf + n, READSTR-n, " %15I", pppdns[i]); 	snprint(buf + n, READSTR-n, "\n");	len = readstr(offset, bp, len, buf);	poperror();	free(buf);	return len;} char*	(*bootp)(Ipifc*) = rbootp;int	(*bootpread)(char*, ulong, int) = rbootpread;