1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include "dhcp.h"
5
6 void bootpdump(uchar *p, int n);
7 void dhcpinit(void);
8 void dhcprecv(void);
9 void dhcpsend(int);
10 void myfatal(char *fmt, ...);
11 int openlisten(char*);
12 uchar *optaddaddr(uchar*, int, uchar*);
13 uchar *optaddbyte(uchar*, int, int);
14 uchar *optadd(uchar*, int, void*, int);
15 uchar *optaddulong(uchar*, int, ulong);
16 uchar *optget(Bootp*, int, int);
17 int optgetaddr(Bootp*, int, uchar*);
18 int optgetbyte(Bootp*, int);
19 ulong optgetulong(Bootp*, int);
20 Bootp *parse(uchar*, int);
21 void stdinthread(void*);
22 ulong thread(void(*f)(void*), void *a);
23 void timerthread(void*);
24 void usage(void);
25
26 struct {
27 QLock lk;
28 int state;
29 int fd;
30 ulong xid;
31 ulong starttime;
32 char cid[100];
33 char sname[64];
34 uchar server[IPaddrlen]; /* server IP address */
35 uchar client[IPaddrlen]; /* client IP address */
36 uchar mask[IPaddrlen]; /* client mask */
37 ulong lease; /* lease time */
38 ulong resend; /* number of resends for current state */
39 ulong timeout; /* time to timeout - seconds */
40 } dhcp;
41
42 char net[64];
43
44 char optmagic[4] = { 0x63, 0x82, 0x53, 0x63 };
45
46 void
main(int argc,char * argv[])47 main(int argc, char *argv[])
48 {
49 char *p;
50
51 setnetmtpt(net, sizeof(net), nil);
52
53 ARGBEGIN{
54 case 'x':
55 p = ARGF();
56 if(p == nil)
57 usage();
58 setnetmtpt(net, sizeof(net), p);
59 }ARGEND;
60
61 fmtinstall('E', eipfmt);
62 fmtinstall('I', eipfmt);
63 fmtinstall('V', eipfmt);
64
65 dhcpinit();
66
67 rfork(RFNOTEG|RFREND);
68
69 thread(timerthread, 0);
70 thread(stdinthread, 0);
71
72 qlock(&dhcp.lk);
73 dhcp.starttime = time(0);
74 dhcp.fd = openlisten(net);
75 dhcpsend(Discover);
76 dhcp.state = Sselecting;
77 dhcp.resend = 0;
78 dhcp.timeout = 4;
79
80 while(dhcp.state != Sbound)
81 dhcprecv();
82
83 /* allows other clients on this machine */
84 close(dhcp.fd);
85 dhcp.fd = -1;
86
87 print("ip=%I\n", dhcp.client);
88 print("mask=%I\n", dhcp.mask);
89 print("end\n");
90
91 /* keep lease alive */
92 for(;;) {
93 //fprint(2, "got lease for %d\n", dhcp.lease);
94 qunlock(&dhcp.lk);
95 sleep(dhcp.lease*500); /* wait half of lease time */
96 qlock(&dhcp.lk);
97
98 //fprint(2, "try renue\n", dhcp.lease);
99 dhcp.starttime = time(0);
100 dhcp.fd = openlisten(net);
101 dhcp.xid = time(0)*getpid();
102 dhcpsend(Request);
103 dhcp.state = Srenewing;
104 dhcp.resend = 0;
105 dhcp.timeout = 1;
106
107 while(dhcp.state != Sbound)
108 dhcprecv();
109
110 /* allows other clients on this machine */
111 close(dhcp.fd);
112 dhcp.fd = -1;
113 }
114 }
115
116 void
usage(void)117 usage(void)
118 {
119 fprint(2, "usage: %s [-x netextension]\n", argv0);
120 exits("usage");
121 }
122
123 void
timerthread(void *)124 timerthread(void*)
125 {
126 for(;;) {
127 sleep(1000);
128 qlock(&dhcp.lk);
129 if(--dhcp.timeout > 0) {
130 qunlock(&dhcp.lk);
131 continue;
132 }
133
134 switch(dhcp.state) {
135 default:
136 myfatal("timerthread: unknown state %d", dhcp.state);
137 case Sinit:
138 break;
139 case Sselecting:
140 dhcpsend(Discover);
141 dhcp.timeout = 4;
142 dhcp.resend++;
143 if(dhcp.resend>5)
144 myfatal("dhcp: giving up: selecting");
145 break;
146 case Srequesting:
147 dhcpsend(Request);
148 dhcp.timeout = 4;
149 dhcp.resend++;
150 if(dhcp.resend>5)
151 myfatal("dhcp: giving up: requesting");
152 break;
153 case Srenewing:
154 dhcpsend(Request);
155 dhcp.timeout = 1;
156 dhcp.resend++;
157 if(dhcp.resend>3) {
158 dhcp.state = Srebinding;
159 dhcp.resend = 0;
160 }
161 break;
162 case Srebinding:
163 dhcpsend(Request);
164 dhcp.timeout = 4;
165 dhcp.resend++;
166 if(dhcp.resend>5)
167 myfatal("dhcp: giving up: rebinding");
168 break;
169 case Sbound:
170 break;
171 }
172 qunlock(&dhcp.lk);
173 }
174 }
175
176 void
stdinthread(void *)177 stdinthread(void*)
178 {
179 uchar buf[100];
180 int n;
181
182 for(;;) {
183 n = read(0, buf, sizeof(buf));
184 if(n <= 0)
185 break;
186 }
187 /* shutdown cleanly */
188 qlock(&dhcp.lk);
189 if(dhcp.client) {
190 if(dhcp.fd < 0)
191 dhcp.fd = openlisten(net);
192 dhcpsend(Release);
193 }
194 qunlock(&dhcp.lk);
195
196 postnote(PNGROUP, getpid(), "die");
197 exits(0);
198 }
199
200 void
dhcpinit(void)201 dhcpinit(void)
202 {
203 int fd;
204
205 dhcp.state = Sinit;
206 dhcp.timeout = 4;
207
208 fd = open("/dev/random", 0);
209 if(fd >= 0) {
210 read(fd, &dhcp.xid, sizeof(dhcp.xid));
211 close(fd);
212 } else
213 dhcp.xid = time(0)*getpid();
214 srand(dhcp.xid);
215
216 sprint(dhcp.cid, "%s.%d", getenv("sysname"), getpid());
217 }
218
219 void
dhcpsend(int type)220 dhcpsend(int type)
221 {
222 int n;
223 uchar *p;
224 Bootp bp;
225 Udphdr *up;
226
227 memset(&bp, 0, sizeof bp);
228 up = (Udphdr*)bp.udphdr;
229
230 hnputs(up->rport, 67);
231 bp.op = Bootrequest;
232 hnputl(bp.xid, dhcp.xid);
233 hnputs(bp.secs, time(0) - dhcp.starttime);
234 hnputs(bp.flags, Fbroadcast); /* reply must be broadcast */
235 memmove(bp.optmagic, optmagic, 4);
236 p = bp.optdata;
237 p = optaddbyte(p, ODtype, type);
238 p = optadd(p, ODclientid, dhcp.cid, strlen(dhcp.cid));
239 switch(type) {
240 default:
241 myfatal("dhcpsend: unknown message type: %d", type);
242 case Discover:
243 ipmove(up->raddr, IPv4bcast); /* broadcast */
244 break;
245 case Request:
246 if(dhcp.state == Sbound || dhcp.state == Srenewing)
247 ipmove(up->raddr, dhcp.server);
248 else
249 ipmove(up->raddr, IPv4bcast); /* broadcast */
250 p = optaddulong(p, ODlease, dhcp.lease);
251 if(dhcp.state == Sselecting || dhcp.state == Srequesting) {
252 p = optaddaddr(p, ODipaddr, dhcp.client); /* mistake?? */
253 p = optaddaddr(p, ODserverid, dhcp.server);
254 } else
255 v6tov4(bp.ciaddr, dhcp.client);
256 break;
257 case Release:
258 ipmove(up->raddr, dhcp.server);
259 v6tov4(bp.ciaddr, dhcp.client);
260 p = optaddaddr(p, ODipaddr, dhcp.client);
261 p = optaddaddr(p, ODserverid, dhcp.server);
262 break;
263 }
264
265 *p++ = OBend;
266
267 n = p - (uchar*)&bp;
268
269 if(write(dhcp.fd, &bp, n) != n)
270 myfatal("dhcpsend: write failed: %r");
271 }
272
273 void
dhcprecv(void)274 dhcprecv(void)
275 {
276 uchar buf[2000];
277 Bootp *bp;
278 int n, type;
279 ulong lease;
280 uchar mask[IPaddrlen];
281
282 qunlock(&dhcp.lk);
283 n = read(dhcp.fd, buf, sizeof(buf));
284 qlock(&dhcp.lk);
285
286 if(n <= 0)
287 myfatal("dhcprecv: bad read: %r");
288
289 bp = parse(buf, n);
290 if(bp == 0)
291 return;
292
293 if(1) {
294 fprint(2, "recved\n");
295 bootpdump(buf, n);
296 }
297
298 type = optgetbyte(bp, ODtype);
299 switch(type) {
300 default:
301 fprint(2, "dhcprecv: unknown type: %d\n", type);
302 break;
303 case Offer:
304 if(dhcp.state != Sselecting)
305 break;
306 lease = optgetulong(bp, ODlease);
307 if(lease == 0)
308 myfatal("bad lease");
309 if(!optgetaddr(bp, OBmask, mask))
310 memset(mask, 0xff, sizeof(mask));
311 v4tov6(dhcp.client, bp->yiaddr);
312 if(!optgetaddr(bp, ODserverid, dhcp.server)) {
313 fprint(2, "dhcprecv: Offer from server with invalid serverid\n");
314 break;
315 }
316
317 dhcp.lease = lease;
318 ipmove(dhcp.mask, mask);
319 memmove(dhcp.sname, bp->sname, sizeof(dhcp.sname));
320 dhcp.sname[sizeof(dhcp.sname)-1] = 0;
321
322 dhcpsend(Request);
323 dhcp.state = Srequesting;
324 dhcp.resend = 0;
325 dhcp.timeout = 4;
326 break;
327 case Ack:
328 if(dhcp.state != Srequesting)
329 if(dhcp.state != Srenewing)
330 if(dhcp.state != Srebinding)
331 break;
332 lease = optgetulong(bp, ODlease);
333 if(lease == 0)
334 myfatal("bad lease");
335 if(!optgetaddr(bp, OBmask, mask))
336 memset(mask, 0xff, sizeof(mask));
337 v4tov6(dhcp.client, bp->yiaddr);
338 dhcp.lease = lease;
339 ipmove(dhcp.mask, mask);
340 dhcp.state = Sbound;
341 break;
342 case Nak:
343 myfatal("recved nak");
344 break;
345 }
346
347 }
348
349 int
openlisten(char * net)350 openlisten(char *net)
351 {
352 int n, fd, cfd;
353 char data[128], devdir[40];
354
355 // sprint(data, "%s/udp!*!bootpc", net);
356 sprint(data, "%s/udp!*!68", net);
357 for(n = 0; ; n++) {
358 cfd = announce(data, devdir);
359 if(cfd >= 0)
360 break;
361 /* might be another client - wait and try again */
362 fprint(2, "dhcpclient: can't announce %s: %r", data);
363 sleep(1000);
364 if(n > 10)
365 myfatal("can't announce: giving up: %r");
366 }
367
368 if(fprint(cfd, "headers") < 0)
369 myfatal("can't set header mode: %r");
370
371 sprint(data, "%s/data", devdir);
372 fd = open(data, ORDWR);
373 if(fd < 0)
374 myfatal("open %s: %r", data);
375 close(cfd);
376 return fd;
377 }
378
379 uchar*
optadd(uchar * p,int op,void * d,int n)380 optadd(uchar *p, int op, void *d, int n)
381 {
382 p[0] = op;
383 p[1] = n;
384 memmove(p+2, d, n);
385 return p+n+2;
386 }
387
388 uchar*
optaddbyte(uchar * p,int op,int b)389 optaddbyte(uchar *p, int op, int b)
390 {
391 p[0] = op;
392 p[1] = 1;
393 p[2] = b;
394 return p+3;
395 }
396
397 uchar*
optaddulong(uchar * p,int op,ulong x)398 optaddulong(uchar *p, int op, ulong x)
399 {
400 p[0] = op;
401 p[1] = 4;
402 hnputl(p+2, x);
403 return p+6;
404 }
405
406 uchar *
optaddaddr(uchar * p,int op,uchar * ip)407 optaddaddr(uchar *p, int op, uchar *ip)
408 {
409 p[0] = op;
410 p[1] = 4;
411 v6tov4(p+2, ip);
412 return p+6;
413 }
414
415 uchar*
optget(Bootp * bp,int op,int n)416 optget(Bootp *bp, int op, int n)
417 {
418 int len, code;
419 uchar *p;
420
421 p = bp->optdata;
422 for(;;) {
423 code = *p++;
424 if(code == OBpad)
425 continue;
426 if(code == OBend)
427 return 0;
428 len = *p++;
429 if(code != op) {
430 p += len;
431 continue;
432 }
433 if(n && n != len)
434 return 0;
435 return p;
436 }
437 }
438
439
440 int
optgetbyte(Bootp * bp,int op)441 optgetbyte(Bootp *bp, int op)
442 {
443 uchar *p;
444
445 p = optget(bp, op, 1);
446 if(p == 0)
447 return 0;
448 return *p;
449 }
450
451 ulong
optgetulong(Bootp * bp,int op)452 optgetulong(Bootp *bp, int op)
453 {
454 uchar *p;
455
456 p = optget(bp, op, 4);
457 if(p == 0)
458 return 0;
459 return nhgetl(p);
460 }
461
462 int
optgetaddr(Bootp * bp,int op,uchar * ip)463 optgetaddr(Bootp *bp, int op, uchar *ip)
464 {
465 uchar *p;
466
467 p = optget(bp, op, 4);
468 if(p == 0)
469 return 0;
470 v4tov6(ip, p);
471 return 1;
472 }
473
474 /* make sure packet looks ok */
475 Bootp *
parse(uchar * p,int n)476 parse(uchar *p, int n)
477 {
478 int len, code;
479 Bootp *bp;
480
481 bp = (Bootp*)p;
482 if(n < bp->optmagic - p) {
483 fprint(2, "dhcpclient: parse: short bootp packet");
484 return 0;
485 }
486
487 if(dhcp.xid != nhgetl(bp->xid)) {
488 fprint(2, "dhcpclient: parse: bad xid: got %ux expected %lux\n",
489 nhgetl(bp->xid), dhcp.xid);
490 return 0;
491 }
492
493 if(bp->op != Bootreply) {
494 fprint(2, "dhcpclient: parse: bad op\n");
495 return 0;
496 }
497
498 n -= bp->optmagic - p;
499 p = bp->optmagic;
500
501 if(n < 4) {
502 fprint(2, "dhcpclient: parse: not option data");
503 return 0;
504 }
505 if(memcmp(optmagic, p, 4) != 0) {
506 fprint(2, "dhcpclient: parse: bad opt magic %ux %ux %ux %ux\n",
507 p[0], p[1], p[2], p[3]);
508 return 0;
509 }
510 p += 4;
511 n -= 4;
512 while(n>0) {
513 code = *p++;
514 n--;
515 if(code == OBpad)
516 continue;
517 if(code == OBend)
518 return bp;
519 if(n == 0) {
520 fprint(2, "dhcpclient: parse: bad option: %d", code);
521 return 0;
522 }
523 len = *p++;
524 n--;
525 if(len > n) {
526 fprint(2, "dhcpclient: parse: bad option: %d", code);
527 return 0;
528 }
529 p += len;
530 n -= len;
531 }
532
533 /* fix up nonstandard packets */
534 /* assume there is space */
535 *p = OBend;
536
537 return bp;
538 }
539
540 void
bootpdump(uchar * p,int n)541 bootpdump(uchar *p, int n)
542 {
543 int len, i, code;
544 Bootp *bp;
545 Udphdr *up;
546
547 bp = (Bootp*)p;
548 up = (Udphdr*)bp->udphdr;
549
550 if(n < bp->optmagic - p) {
551 fprint(2, "dhcpclient: short bootp packet");
552 return;
553 }
554
555 fprint(2, "laddr=%I lport=%d raddr=%I rport=%d\n", up->laddr,
556 nhgets(up->lport), up->raddr, nhgets(up->rport));
557 fprint(2, "op=%d htype=%d hlen=%d hops=%d\n", bp->op, bp->htype,
558 bp->hlen, bp->hops);
559 fprint(2, "xid=%ux secs=%d flags=%ux\n", nhgetl(bp->xid),
560 nhgets(bp->secs), nhgets(bp->flags));
561 fprint(2, "ciaddr=%V yiaddr=%V siaddr=%V giaddr=%V\n",
562 bp->ciaddr, bp->yiaddr, bp->siaddr, bp->giaddr);
563 fprint(2, "chaddr=");
564 for(i=0; i<16; i++)
565 fprint(2, "%ux ", bp->chaddr[i]);
566 fprint(2, "\n");
567 fprint(2, "sname=%s\n", bp->sname);
568 fprint(2, "file = %s\n", bp->file);
569
570 n -= bp->optmagic - p;
571 p = bp->optmagic;
572
573 if(n < 4)
574 return;
575 if(memcmp(optmagic, p, 4) != 0)
576 fprint(2, "dhcpclient: bad opt magic %ux %ux %ux %ux\n",
577 p[0], p[1], p[2], p[3]);
578 p += 4;
579 n -= 4;
580
581 while(n>0) {
582 code = *p++;
583 n--;
584 if(code == OBpad)
585 continue;
586 if(code == OBend)
587 break;
588 if(n == 0) {
589 fprint(2, " bad option: %d", code);
590 return;
591 }
592 len = *p++;
593 n--;
594 if(len > n) {
595 fprint(2, " bad option: %d", code);
596 return;
597 }
598 switch(code) {
599 default:
600 fprint(2, "unknown option %d\n", code);
601 for(i = 0; i<len; i++)
602 fprint(2, "%ux ", p[i]);
603 case ODtype:
604 fprint(2, "DHCP type %d\n", p[0]);
605 break;
606 case ODclientid:
607 fprint(2, "client id=");
608 for(i = 0; i<len; i++)
609 fprint(2, "%ux ", p[i]);
610 fprint(2, "\n");
611 break;
612 case ODlease:
613 fprint(2, "lease=%d\n", nhgetl(p));
614 break;
615 case ODserverid:
616 fprint(2, "server id=%V\n", p);
617 break;
618 case OBmask:
619 fprint(2, "mask=%V\n", p);
620 break;
621 case OBrouter:
622 fprint(2, "router=%V\n", p);
623 break;
624 }
625 p += len;
626 n -= len;
627 }
628 }
629
630 ulong
thread(void (* f)(void *),void * a)631 thread(void(*f)(void*), void *a)
632 {
633 int pid;
634
635 pid = rfork(RFNOWAIT|RFMEM|RFPROC);
636 if(pid < 0)
637 myfatal("rfork failed: %r");
638 if(pid != 0)
639 return pid;
640 (*f)(a);
641 return 0; /* never reaches here */
642 }
643
644 void
myfatal(char * fmt,...)645 myfatal(char *fmt, ...)
646 {
647 char buf[1024];
648 va_list arg;
649
650 va_start(arg, fmt);
651 vseprint(buf, buf+sizeof(buf), fmt, arg);
652 va_end(arg);
653 fprint(2, "%s: %s\n", argv0, buf);
654 postnote(PNGROUP, getpid(), "die");
655 exits(buf);
656 }
657