xref: /inferno-os/os/ip/dhcp.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7 #include "kernel.h"
8 #include "ip.h"
9 #include "ppp.h"
10 
11 Ipaddr pppdns[2];
12 
13 static	ulong	fsip;
14 static	ulong	auip;
15 static	ulong	gwip;
16 static	ulong	ipmask;
17 static	ulong	ipaddr;
18 static	ulong	dns1ip;
19 static	ulong	dns2ip;
20 
21 int		dhcpmsgtype;
22 int		debug=0;
23 enum
24 {
25 	Bootrequest = 1,
26 	Bootreply   = 2,
27 };
28 
29 typedef struct Bootp
30 {
31 	/* udp.c oldheader */
32 	uchar	raddr[IPaddrlen];
33 	uchar	laddr[IPaddrlen];
34 	uchar	rport[2];
35 	uchar	lport[2];
36 	/* bootp itself */
37 	uchar	op;			/* opcode */
38 	uchar	htype;		/* hardware type */
39 	uchar	hlen;			/* hardware address len */
40 	uchar	hops;		/* hops */
41 	uchar	xid[4];		/* a random number */
42 	uchar	secs[2];		/* elapsed snce client started booting */
43 	uchar	flags[2];		/* flags */
44 	uchar	ciaddr[4];		/* client IP address (client tells server) */
45 	uchar	yiaddr[4];		/* client IP address (server tells client) */
46 	uchar	siaddr[4];		/* server IP address */
47 	uchar	giaddr[4];		/* gateway IP address */
48 	uchar	chaddr[16];	/* client hardware address */
49 	uchar	sname[64];	/* server host name (optional) */
50 	uchar	file[128];		/* boot file name */
51 	uchar	vend[128];	/* vendor-specific goo 340 */
52 } Bootp;
53 
54 static	Bootp	req;
55 static	Proc*	rcvprocp;
56 static	int	recv;
57 static	int	done;
58 static	Rendez	bootpr;
59 static	char	rcvbuf[512+2*IPaddrlen+2*2];	  /* 576 */
60 static	uchar sid[4];
61 static	ulong iplease;
62 
63 /*
64  * bootp returns:
65  *
66  * "fsip d.d.d.d
67  * auip d.d.d.d
68  * gwip d.d.d.d
69  * ipmask d.d.d.d
70  * ipaddr d.d.d.d
71  * dns1ip	d.d.d.d
72  * dns2ip	d.d.d.d
73  *
74  * where d.d.d.d is the IP address in dotted decimal notation, and each
75  * address is followed by a newline.
76 	Last change:  SUN  13 Sep 2001    4:36 pm
77  */
78 
79 /*
80  * Parse the vendor specific fields according to RFC 1084.
81  * We are overloading the "cookie server" to be the Inferno
82  * authentication server and the "resource location server"
83  * to be the Inferno file server.
84  *
85  * If the vendor specific field is formatted properly, it
86  * will being with the four bytes 99.130.83.99 and end with
87  * an 0xFF byte.
88  */
89 static int
parsevend(uchar * pvend)90 parsevend(uchar* pvend)
91 {
92 	uchar *vend=pvend;
93 	int dhcpmsg=0;
94 	/* The field must start with 99.130.83.99 to be compliant */
95 	if ((vend[0] != 99) || (vend[1] != 130) || (vend[2] != 83) || (vend[3] != 99)){
96 		print("bad bootp vendor field: %.2x%.2x%.2x%.2x", vend[0], vend[1], vend[2], vend[3]);
97 		return -1;
98 	}
99 
100 	/* Skip over the magic cookie */
101 	vend += 4;
102 
103 	while ((vend[0] != 0) && (vend[0] != 0xFF)) {
104 		int i;
105 //
106 		if(debug){
107 			print(">>>Opt[%d] [%d]", vend[0], vend[1]);
108 			for(i=0; i<vend[1]; i++)
109 				print(" %2.2x", vend[i+2]);
110 			print("\n");
111 		}
112 //
113 		switch (vend[0]) {
114 		case 1:	/* Subnet mask field */
115 			/* There must be only one subnet mask */
116 			if (vend[1] == 4)
117 				ipmask = (vend[2]<<24)|(vend[3]<<16)| (vend[4]<<8)| vend[5];
118 			else{
119 				return -1;
120 			}
121 			break;
122 
123 		case 3:	/* Gateway/router field */
124 			/* We are only concerned with first address */
125 			if (vend[1] >0 && vend[1]%4==0)
126 				gwip = (vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
127 			else
128 				return -1;
129 			break;
130 		case 6:	/* domain name server */
131 			if(vend[1]>0 && vend[1] %4==0){
132 				dns1ip=(vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
133 				if(vend[1]>4)
134 					dns2ip=(vend[6]<<24)|(vend[7]<<16)|(vend[8]<<8)|vend[9];
135 			}else
136 				return -1;
137 			break;
138 
139 		case 8:	/* "Cookie server" (auth server) field */
140 			/* We are only concerned with first address */
141 			if (vend[1] > 0 && vend[1]%4==0)
142 				auip = (vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
143 			else
144 				return -1;
145 			break;
146 
147 		case 11:	/* "Resource loc server" (file server) field */
148 			/* We are only concerned with first address */
149 			if (vend[1] > 0 && vend[1]%4==0)
150 				fsip = (vend[2]<<24)| (vend[3]<<16)| (vend[4]<<8)| vend[5];
151 			else
152 				return -1;
153 			break;
154 		case 51:	/* ip lease time */
155 			if(vend[1]==4){
156 				iplease=(vend[2]<<24)|(vend[3]<<16)|(vend[4]<<8)|vend[5];
157 			}else
158 				return -1;
159 			break;
160 		case 53:	/* DHCP message type */
161 			if(vend[1]==1)
162 				dhcpmsg=vend[2];
163 			else
164 				return -1;
165 			break;
166 		case 54:	/* server identifier */
167 			if(vend[1]==4){
168 				memmove(sid, vend+2, 4);
169 			}else
170 				return -1;
171 			break;
172 
173 		default:	/* Everything else stops us */
174 			break;
175 		}
176 
177 		/* Skip over the field */
178 		vend += vend[1] + 2;
179 	}
180 	if(debug)
181 		print(">>>Opt[%d] [%d]\n", vend[0], vend[1]);
182 	return dhcpmsg;
183 }
184 
185 static void
dispvend(uchar * pvend)186 dispvend(uchar* pvend)
187 {
188 	uchar *vend=pvend;
189 
190 	//print("<<<Magic : %2.2x%2.2x%2.2x%2.2x\n", vend[0], vend[1], vend[2], vend[3]);
191 
192 	vend += 4;		/* Skip over the magic cookie */
193 	while ((vend[0] != 0) && (vend[0] != 0xFF)) {
194 	//	int i;
195 	  //	print("<<<Opt[%d] [%d]", vend[0], vend[1]);
196 		//for(i=0; i<vend[1]; i++)
197 		//	print(" %2.2x", vend[i+2]);
198 		//print("\n");
199 
200 		vend += vend[1] + 2;
201 	}
202 	//print("<<<Opt[ %2.2x] [%2.2x]\n", vend[0], vend[1]);
203 }
204 
205 static void
rcvbootp(void * a)206 rcvbootp(void *a)
207 {
208 	int n, fd, dhcp;
209 	Bootp *rp;
210 
211 	if(waserror())
212 		pexit("", 0);
213 	rcvprocp = up;	/* store for postnote below */
214 	fd = (int)a;
215 	while(done == 0) {
216 		if(debug)
217 			print("rcvbootp:looping\n");
218 
219 		n = kread(fd, rcvbuf, sizeof(rcvbuf));
220 		if(n <= 0)
221 			break;
222 		rp = (Bootp*)rcvbuf;
223 		if (memcmp(req.chaddr, rp->chaddr, 6) == 0 && rp->htype == 1 && rp->hlen == 6) {
224 			ipaddr = (rp->yiaddr[0]<<24)| (rp->yiaddr[1]<<16)| (rp->yiaddr[2]<<8)| rp->yiaddr[3];
225 			if(debug)
226 				print("ipaddr = %2.2x %2.2x %2.2x %2.2x \n", rp->yiaddr[0], rp->yiaddr[1], rp->yiaddr[2], rp->yiaddr[3]);
227 			//memmove(req.siaddr, rp->siaddr, 4);	/* siaddr */
228 			dhcp = parsevend(rp->vend);
229 
230 			if(dhcpmsgtype < dhcp){
231 				dhcpmsgtype=dhcp;
232 				recv = 1;
233 				wakeup(&bootpr);
234 				if(dhcp==0 || dhcp ==5 || dhcp == 6 )
235 					break;
236 			}
237 		}
238 	}
239 	poperror();
240 	rcvprocp = nil;
241 
242 	if(debug)
243 		print("rcvbootp exit\n");
244 	pexit("", 0);
245 }
246 
247 static char*
rbootp(Ipifc * ifc)248 rbootp(Ipifc *ifc)
249 {
250 	int cfd, dfd, tries, n;
251 	char ia[5+3*16], im[16], *av[3];
252 	uchar nipaddr[4], ngwip[4], nipmask[4];
253 	char dir[Maxpath];
254 	static uchar vend_rfc1048[] = { 99, 130, 83, 99 };
255 	uchar *vend;
256 
257 	/*
258 	 * broadcast bootp's till we get a reply,
259 	 * or fixed number of tries
260 	 */
261 	if(debug)
262 	    print("dhcp: bootp() called\n");
263 	tries = 0;
264 	av[1] = "0.0.0.0";
265 	av[2] = "0.0.0.0";
266 	ipifcadd(ifc, av, 3, 0, nil);
267 
268 	cfd = kannounce("udp!*!68", dir);
269 	if(cfd < 0)
270 		return "dhcp announce failed";
271 	strcat(dir, "/data");
272 	if(kwrite(cfd, "headers", 7) < 0){
273 		kclose(cfd);
274 		return "dhcp ctl headers failed";
275 	}
276 	kwrite(cfd, "oldheaders", 10);
277 	dfd = kopen(dir, ORDWR);
278 	if(dfd < 0){
279 		kclose(cfd);
280 		return "dhcp open data failed";
281 	}
282 	kclose(cfd);
283 
284 	while(tries<1){
285 		tries++;
286 		memset(sid, 0, 4);
287 		iplease=0;
288 		dhcpmsgtype=-2;
289 /* DHCPDISCOVER*/
290 		done = 0;
291 		recv = 0;
292 		kproc("rcvbootp", rcvbootp, (void*)dfd, KPDUPFDG);
293 		/* Prepare DHCPDISCOVER */
294 		memset(&req, 0, sizeof(req));
295 		ipmove(req.raddr, IPv4bcast);
296 		hnputs(req.rport, 67);
297 		req.op = Bootrequest;
298 		req.htype = 1;			/* ethernet (all we know) */
299 		req.hlen = 6;			/* ethernet (all we know) */
300 
301 		memmove(req.chaddr, ifc->mac, 6);	/* Hardware MAC address */
302 		//ipv4local(ifc, req.ciaddr);				/* Fill in the local IP address if we know it */
303 		memset(req.file, 0, sizeof(req.file));
304 		vend=req.vend;
305 		memmove(vend, vend_rfc1048, 4); vend+=4;
306 		*vend++=53; *vend++=1;*vend++=1;		/* dhcp msg type==3, dhcprequest */
307 
308 		*vend++=61;*vend++=7;*vend++=1;
309 		memmove(vend, ifc->mac, 6);vend+=6;
310 		*vend=0xff;
311 
312 		if(debug)
313 			dispvend(req.vend);
314 		for(n=0;n<4;n++){
315 			if(kwrite(dfd, &req, sizeof(req))<0)	/* SEND DHCPDISCOVER */
316 				print("DHCPDISCOVER: %r");
317 
318 			tsleep(&bootpr, return0, 0, 1000);	/* wait DHCPOFFER */
319 			if(debug)
320 				print("[DHCP] DISCOVER: msgtype = %d\n", dhcpmsgtype);
321 
322 			if(dhcpmsgtype==2)		/* DHCPOFFER */
323 				break;
324 			else if(dhcpmsgtype==0)	/* bootp */
325 				return nil;
326 			else if(dhcpmsgtype== -2)	/* time out */
327 				continue;
328 			else
329 				break;
330 
331 		}
332 		if(dhcpmsgtype!=2)
333 			continue;
334 
335 /* DHCPREQUEST */
336 		memset(req.vend, 0, sizeof(req.vend));
337 		vend=req.vend;
338 		memmove(vend, vend_rfc1048, 4);vend+=4;
339 
340 		*vend++=53; *vend++=1;*vend++=3;		/* dhcp msg type==3, dhcprequest */
341 
342 		*vend++=50;	*vend++=4;				/* requested ip address */
343 		*vend++=(ipaddr >> 24)&0xff;
344 		*vend++=(ipaddr >> 16)&0xff;
345 		*vend++=(ipaddr >> 8) & 0xff;
346 		*vend++=ipaddr & 0xff;
347 
348 		*vend++=51;*vend++=4;					/* lease time */
349 		*vend++=(iplease>>24)&0xff; *vend++=(iplease>>16)&0xff; *vend++=(iplease>>8)&0xff; *vend++=iplease&0xff;
350 
351 		*vend++=54; *vend++=4;					/* server identifier */
352 		memmove(vend, sid, 4);	vend+=4;
353 
354 		*vend++=61;*vend++=07;*vend++=01;		/* client identifier */
355 		memmove(vend, ifc->mac, 6);vend+=6;
356 		*vend=0xff;
357 		if(debug)
358 			dispvend(req.vend);
359 		if(kwrite(dfd, &req, sizeof(req))<0){
360 			print("DHCPREQUEST: %r");
361 			continue;
362 		}
363 		tsleep(&bootpr, return0, 0, 2000);
364 		if(dhcpmsgtype==5)		/* wait for DHCPACK */
365 			break;
366 		else
367 			continue;
368 		/* CHECK ARP */
369 		/* DHCPDECLINE */
370 	}
371 	kclose(dfd);
372 	done = 1;
373 	if(rcvprocp != nil){
374 		postnote(rcvprocp, 1, "timeout", 0);
375 		rcvprocp = nil;
376 	}
377 
378 	av[1] = "0.0.0.0";
379 	av[2] = "0.0.0.0";
380 	ipifcrem(ifc, av, 3);
381 
382 	hnputl(nipaddr, ipaddr);
383 	sprint(ia, "%V", nipaddr);
384 	hnputl(nipmask, ipmask);
385 	sprint(im, "%V", nipmask);
386 	av[1] = ia;
387 	av[2] = im;
388 	ipifcadd(ifc, av, 3, 0, nil);
389 
390 	if(gwip != 0) {
391 		hnputl(ngwip, gwip);
392 		n = sprint(ia, "add 0.0.0.0 0.0.0.0 %V", ngwip);
393 		routewrite(ifc->conv->p->f, nil, ia, n);
394 	}
395 	return nil;
396 }
397 
398 static int
rbootpread(char * bp,ulong offset,int len)399 rbootpread(char *bp, ulong offset, int len)
400 {
401 	int n, i;
402 	char *buf;
403 	uchar a[4];
404 
405 	if(debug)
406 		print("dhcp: bootpread() \n");
407 	buf = smalloc(READSTR);
408 	if(waserror()){
409 		free(buf);
410 		nexterror();
411 	}
412 
413 	hnputl(a, fsip);
414 	n = snprint(buf, READSTR, "fsip %15V\n", a);
415 	hnputl(a, auip);
416 	n += snprint(buf + n, READSTR-n, "auip %15V\n", a);
417 	hnputl(a, gwip);
418 	n += snprint(buf + n, READSTR-n, "gwip %15V\n", a);
419 	hnputl(a, ipmask);
420 	n += snprint(buf + n, READSTR-n, "ipmask %15V\n", a);
421 	hnputl(a, ipaddr);
422 	n += snprint(buf + n, READSTR-n, "ipaddr %15V\n", a);
423 	n += snprint(buf+n, READSTR-n, "expired %lud\n", iplease);
424 
425 	n += snprint(buf + n, READSTR-n, "dns");
426 	if(dns2ip){
427 		hnputl(a, dns2ip);
428 		n+=snprint(buf + n, READSTR-n, " %15V", a);
429 	}
430 	if(dns1ip){
431 		hnputl(a, dns1ip);
432 		n += snprint(buf + n, READSTR-n, " %15V", a);
433 	}
434 
435 	for(i=0; i<2; i++)
436 		if(ipcmp(pppdns[i], IPnoaddr) != 0 && ipcmp(pppdns[i], v4prefix) != 0)
437 			n += snprint(buf + n, READSTR-n, " %15I", pppdns[i]);
438 
439 	snprint(buf + n, READSTR-n, "\n");
440 	len = readstr(offset, bp, len, buf);
441 	poperror();
442 	free(buf);
443 	return len;
444 }
445 
446 char*	(*bootp)(Ipifc*) = rbootp;
447 int	(*bootpread)(char*, ulong, int) = rbootpread;
448