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