]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/dhcpclient.c
ip/pppoe: fix %.*s format in debug prints
[plan9front.git] / sys / src / cmd / ip / dhcpclient.c
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
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
117 usage(void)
118 {
119         fprint(2, "usage: %s [-x netextension]\n", argv0);
120         exits("usage");
121 }
122
123 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
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
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
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
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
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*
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*
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*
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 *
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*
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
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
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
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 *
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
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
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
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 }