]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/ipconfig/dhcp.c
ip/ipconfig: populate /net/ndb from v6 router advertisements, configure multiple...
[plan9front.git] / sys / src / cmd / ip / ipconfig / dhcp.c
1 /*
2  * ipconfig - configure parameters of an ip stack
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <bio.h>
7 #include <ip.h>
8 #include <ndb.h>
9 #include "ipconfig.h"
10 #include "../dhcp.h"
11
12 enum
13 {
14         Taddr,
15         Taddrs,
16         Tstr,
17         Tbyte,
18         Tulong,
19         Tvec,
20 };
21
22 typedef struct Option Option;
23 struct Option
24 {
25         char    *name;
26         int     type;
27 };
28
29 /*
30  * I was too lazy to look up the types for each of these
31  * options.  If someone feels like it, please mail me a
32  * corrected array -- presotto
33  */
34 static Option option[256] =
35 {
36 [OBmask]                { "ipmask",             Taddr },
37 [OBtimeoff]             { "timeoff",            Tulong },
38 [OBrouter]              { "ipgw",               Taddrs },
39 [OBtimeserver]          { "time",               Taddrs },
40 [OBnameserver]          { "name",               Taddrs },
41 [OBdnserver]            { "dns",                Taddrs },
42 [OBlogserver]           { "log",                Taddrs },
43 [OBcookieserver]        { "cookie",             Taddrs },
44 [OBlprserver]           { "lpr",                Taddrs },
45 [OBimpressserver]       { "impress",            Taddrs },
46 [OBrlserver]            { "rl",                 Taddrs },
47 [OBhostname]            { "sys",                Tstr },
48 [OBbflen]               { "bflen",              Tulong },
49 [OBdumpfile]            { "dumpfile",           Tstr },
50 [OBdomainname]          { "dom",                Tstr },
51 [OBswapserver]          { "swap",               Taddrs },
52 [OBrootpath]            { "rootpath",           Tstr },
53 [OBextpath]             { "extpath",            Tstr },
54 [OBipforward]           { "ipforward",          Taddrs },
55 [OBnonlocal]            { "nonlocal",           Taddrs },
56 [OBpolicyfilter]        { "policyfilter",       Taddrs },
57 [OBmaxdatagram]         { "maxdatagram",        Tulong },
58 [OBttl]                 { "ttl",                Tulong },
59 [OBpathtimeout]         { "pathtimeout",        Taddrs },
60 [OBpathplateau]         { "pathplateau",        Taddrs },
61 [OBmtu]                 { "mtu",                Tulong },
62 [OBsubnetslocal]        { "subnetslocal",       Taddrs },
63 [OBbaddr]               { "baddr",              Taddrs },
64 [OBdiscovermask]        { "discovermask",       Taddrs },
65 [OBsupplymask]          { "supplymask",         Taddrs },
66 [OBdiscoverrouter]      { "discoverrouter",     Taddrs },
67 [OBrsserver]            { "rs",                 Taddrs },
68 [OBstaticroutes]        { "staticroutes",       Taddrs },
69 [OBtrailerencap]        { "trailerencap",       Taddrs },
70 [OBarptimeout]          { "arptimeout",         Tulong },
71 [OBetherencap]          { "etherencap",         Taddrs },
72 [OBtcpttl]              { "tcpttl",             Tulong },
73 [OBtcpka]               { "tcpka",              Tulong },
74 [OBtcpkag]              { "tcpkag",             Tulong },
75 [OBnisdomain]           { "nisdomain",          Tstr },
76 [OBniserver]            { "ni",                 Taddrs },
77 [OBntpserver]           { "ntp",                Taddrs },
78 [OBnetbiosns]           { "netbiosns",          Taddrs },
79 [OBnetbiosdds]          { "netbiosdds",         Taddrs },
80 [OBnetbiostype]         { "netbiostype",        Taddrs },
81 [OBnetbiosscope]        { "netbiosscope",       Taddrs },
82 [OBxfontserver]         { "xfont",              Taddrs },
83 [OBxdispmanager]        { "xdispmanager",       Taddrs },
84 [OBnisplusdomain]       { "nisplusdomain",      Tstr },
85 [OBnisplusserver]       { "nisplus",            Taddrs },
86 [OBhomeagent]           { "homeagent",          Taddrs },
87 [OBsmtpserver]          { "smtp",               Taddrs },
88 [OBpop3server]          { "pop3",               Taddrs },
89 [OBnntpserver]          { "nntp",               Taddrs },
90 [OBwwwserver]           { "www",                Taddrs },
91 [OBfingerserver]        { "finger",             Taddrs },
92 [OBircserver]           { "irc",                Taddrs },
93 [OBstserver]            { "st",                 Taddrs },
94 [OBstdaserver]          { "stdar",              Taddrs },
95
96 [ODipaddr]              { "ipaddr",             Taddr },
97 [ODlease]               { "lease",              Tulong },
98 [ODoverload]            { "overload",           Taddr },
99 [ODtype]                { "type",               Tbyte },
100 [ODserverid]            { "serverid",           Taddr },
101 [ODparams]              { "params",             Tvec },
102 [ODmessage]             { "message",            Tstr },
103 [ODmaxmsg]              { "maxmsg",             Tulong },
104 [ODrenewaltime]         { "renewaltime",        Tulong },
105 [ODrebindingtime]       { "rebindingtime",      Tulong },
106 [ODvendorclass]         { "vendorclass",        Tvec },
107 [ODclientid]            { "clientid",           Tvec },
108 [ODtftpserver]          { "tftp",               Taddr },
109 [ODbootfile]            { "bootfile",           Tstr },
110 };
111
112 static uchar defrequested[] = {
113         OBmask, OBrouter, OBdnserver, OBhostname, OBdomainname, OBntpserver,
114 };
115
116 static uchar    requested[256];
117 static int      nrequested;
118
119 static char     optmagic[4] = { 0x63, 0x82, 0x53, 0x63 };
120
121 static int      openlisten(void);
122
123 static void     dhcprecv(void);
124 static void     dhcpsend(int);
125 static void     dhcptimer(void);
126
127 static uchar*   optaddaddr(uchar*, int, uchar*);
128 static uchar*   optaddbyte(uchar*, int, int);
129 static uchar*   optaddstr(uchar*, int, char*);
130 static uchar*   optadd(uchar*, int, void*, int);
131 static uchar*   optaddulong(uchar*, int, ulong);
132 static uchar*   optaddvec(uchar*, int, uchar*, int);
133 static int      optgetaddrs(uchar*, int, uchar*, int);
134 static int      optgetp9addrs(uchar*, int, uchar*, int);
135 static int      optgetaddr(uchar*, int, uchar*);
136 static int      optgetbyte(uchar*, int);
137 static int      optgetstr(uchar*, int, char*, int);
138 static uchar*   optget(uchar*, int, int*);
139 static ulong    optgetulong(uchar*, int);
140 static int      optgetvec(uchar*, int, uchar*, int);
141 static char*    optgetx(uchar*, uchar);
142
143 static void     getoptions(uchar*);
144 static int      parseoptions(uchar *p, int n);
145 static Bootp*   parsebootp(uchar*, int);
146
147 void
148 dhcpinit(void)
149 {
150         /* init set of requested dhcp parameters with the default */
151         nrequested = sizeof defrequested;
152         memcpy(requested, defrequested, nrequested);
153 }
154
155 void
156 dhcpquery(int needconfig, int startstate)
157 {
158         if(needconfig)
159                 fprint(conf.cfd, "add %I %I", IPnoaddr, IPnoaddr);
160
161         conf.fd = openlisten();
162         if(conf.fd < 0){
163                 conf.state = Sinit;
164                 return;
165         }
166         notify(catch);
167
168         conf.xid = lrand();
169         conf.starttime = time(0);
170         conf.state = startstate;
171         switch(startstate){
172         case Sselecting:
173                 conf.offered = 0;
174                 dhcpsend(Discover);
175                 break;
176         case Srenewing:
177                 dhcpsend(Request);
178                 break;
179         default:
180                 sysfatal("internal error 0");
181         }
182         conf.resend = 0;
183         conf.timeout = time(0) + 4;
184
185         while(conf.state != Sbound && conf.state != Sinit){
186                 dhcprecv();
187                 dhcptimer();
188         }
189         close(conf.fd);
190
191         if(needconfig)
192                 fprint(conf.cfd, "remove %I %I", IPnoaddr, IPnoaddr);
193
194 }
195
196 enum {
197         /*
198          * was an hour, needs to be less for the ARM/GS1 until the timer
199          * code has been cleaned up (pb).
200          */
201         Maxsleep = 450,
202 };
203
204 void
205 dhcpwatch(int needconfig)
206 {
207         ulong secs, s, t;
208
209         if(nodhcpwatch)
210                 return;
211
212         switch(rfork(RFPROC|RFFDG|RFNOWAIT|RFNOTEG)){
213         default:
214                 return;
215         case 0:
216                 break;
217         }
218
219         dolog = 1;                      /* log, don't print */
220         procsetname("dhcpwatch on %s", conf.dev);
221         /* keep trying to renew the lease */
222         for(;;){
223                 secs = conf.lease/2;
224                 if(secs < 5)
225                         secs = 5;
226
227                 /* avoid overflows */
228                 for(s = secs; s > 0; s -= t){
229                         if(s > Maxsleep)
230                                 t = Maxsleep;
231                         else
232                                 t = s;
233                         sleep(t*1000);
234                 }
235
236                 if(conf.lease > 0){
237                         /*
238                          * during boot, the starttime can be bogus so avoid
239                          * spurious ipunconfig's
240                          */
241                         t = time(0) - conf.starttime;
242                         if(t > (3*secs)/2)
243                                 t = secs;
244                         if(t >= conf.lease){
245                                 conf.lease = 0;
246                                 if(!noconfig){
247                                         ipunconfig();
248                                         needconfig = 1;
249                                 }
250                         } else
251                                 conf.lease -= t;
252                 }
253                 dhcpquery(needconfig, needconfig? Sselecting: Srenewing);
254
255                 if(needconfig && conf.state == Sbound){
256                         if(ip4cfg() < 0)
257                                 sysfatal("can't start ip: %r");
258                         needconfig = 0;
259                         /*
260                          * leave everything we've learned somewhere that
261                          * other procs can find it.
262                          */
263                         if(beprimary)
264                                 putndb();
265                         refresh();
266                 }
267         }
268 }
269
270 static void
271 dhcptimer(void)
272 {
273         ulong now;
274
275         now = time(0);
276         if(now < conf.timeout)
277                 return;
278
279         switch(conf.state) {
280         default:
281                 sysfatal("dhcptimer: unknown state %d", conf.state);
282         case Sinit:
283         case Sbound:
284                 break;
285         case Sselecting:
286         case Srequesting:
287         case Srebinding:
288                 dhcpsend(conf.state == Sselecting? Discover: Request);
289                 conf.timeout = now + 4;
290                 if(++conf.resend > 5)
291                         conf.state = Sinit;
292                 break;
293         case Srenewing:
294                 dhcpsend(Request);
295                 conf.timeout = now + 1;
296                 if(++conf.resend > 3) {
297                         conf.state = Srebinding;
298                         conf.resend = 0;
299                 }
300                 break;
301         }
302 }
303
304 static void
305 dhcpsend(int type)
306 {
307         Bootp bp;
308         uchar *p;
309         int n;
310         uchar vendor[64];
311         Udphdr *up = (Udphdr*)bp.udphdr;
312
313         memset(&bp, 0, sizeof bp);
314
315         hnputs(up->rport, 67);
316         bp.op = Bootrequest;
317         hnputl(bp.xid, conf.xid);
318         hnputs(bp.secs, time(0)-conf.starttime);
319         hnputs(bp.flags, 0);
320         memmove(bp.optmagic, optmagic, 4);
321         if(conf.hwatype >= 0 && conf.hwalen < sizeof bp.chaddr){
322                 memmove(bp.chaddr, conf.hwa, conf.hwalen);
323                 bp.hlen = conf.hwalen;
324                 bp.htype = conf.hwatype;
325         }
326         p = bp.optdata;
327         p = optaddbyte(p, ODtype, type);
328         p = optadd(p, ODclientid, conf.cid, conf.cidlen);
329         switch(type) {
330         default:
331                 sysfatal("dhcpsend: unknown message type: %d", type);
332         case Discover:
333                 ipmove(up->raddr, IPv4bcast);   /* broadcast */
334                 if(*conf.hostname && sendhostname)
335                         p = optaddstr(p, OBhostname, conf.hostname);
336                 if(plan9){
337                         n = snprint((char*)vendor, sizeof vendor,
338                                 "plan9_%s", conf.cputype);
339                         p = optaddvec(p, ODvendorclass, vendor, n);
340                 }
341                 p = optaddvec(p, ODparams, requested, nrequested);
342                 if(validip(conf.laddr))
343                         p = optaddaddr(p, ODipaddr, conf.laddr);
344                 break;
345         case Request:
346                 switch(conf.state){
347                 case Srenewing:
348                         ipmove(up->raddr, conf.server);
349                         v6tov4(bp.ciaddr, conf.laddr);
350                         break;
351                 case Srebinding:
352                         ipmove(up->raddr, IPv4bcast);   /* broadcast */
353                         v6tov4(bp.ciaddr, conf.laddr);
354                         break;
355                 case Srequesting:
356                         ipmove(up->raddr, IPv4bcast);   /* broadcast */
357                         p = optaddaddr(p, ODipaddr, conf.laddr);
358                         p = optaddaddr(p, ODserverid, conf.server);
359                         break;
360                 }
361                 p = optaddulong(p, ODlease, conf.offered);
362                 if(plan9){
363                         n = snprint((char*)vendor, sizeof vendor,
364                                 "plan9_%s", conf.cputype);
365                         p = optaddvec(p, ODvendorclass, vendor, n);
366                 }
367                 p = optaddvec(p, ODparams, requested, nrequested);
368                 if(*conf.hostname && sendhostname)
369                         p = optaddstr(p, OBhostname, conf.hostname);
370                 break;
371         case Release:
372                 ipmove(up->raddr, conf.server);
373                 v6tov4(bp.ciaddr, conf.laddr);
374                 p = optaddaddr(p, ODipaddr, conf.laddr);
375                 p = optaddaddr(p, ODserverid, conf.server);
376                 break;
377         }
378
379         *p++ = OBend;
380
381         n = p - (uchar*)&bp;
382         USED(n);
383
384         /*
385          *  We use a maximum size DHCP packet to survive the
386          *  All_Aboard NAT package from Internet Share.  It
387          *  always replies to DHCP requests with a packet of the
388          *  same size, so if the request is too short the reply
389          *  is truncated.
390          */
391         if(write(conf.fd, &bp, sizeof bp) != sizeof bp)
392                 warning("dhcpsend: write failed: %r");
393 }
394
395 static void
396 dhcprecv(void)
397 {
398         int i, n, type;
399         ulong lease;
400         char err[ERRMAX];
401         uchar buf[8000], vopts[256], taddr[IPaddrlen];
402         Bootp *bp;
403
404         memset(buf, 0, sizeof buf);
405         alarm(1000);
406         n = read(conf.fd, buf, sizeof buf);
407         alarm(0);
408
409         if(n < 0){
410                 rerrstr(err, sizeof err);
411                 if(strstr(err, "interrupt") == nil)
412                         warning("dhcprecv: bad read: %s", err);
413                 else
414                         DEBUG("dhcprecv: read timed out");
415                 return;
416         }
417
418         bp = parsebootp(buf, n);
419         if(bp == 0) {
420                 DEBUG("parsebootp failed: dropping packet");
421                 return;
422         }
423
424         type = optgetbyte(bp->optdata, ODtype);
425         switch(type) {
426         default:
427                 warning("dhcprecv: unknown type: %d", type);
428                 break;
429         case Offer:
430                 DEBUG("got offer from %V ", bp->siaddr);
431                 if(conf.state != Sselecting)
432                         break;
433                 lease = optgetulong(bp->optdata, ODlease);
434                 if(lease == 0){
435                         /*
436                          * The All_Aboard NAT package from Internet Share
437                          * doesn't give a lease time, so we have to assume one.
438                          */
439                         warning("Offer with %lud lease, using %d", lease, MinLease);
440                         lease = MinLease;
441                 }
442                 DEBUG("lease=%lud ", lease);
443                 if(!optgetaddr(bp->optdata, ODserverid, conf.server)) {
444                         warning("Offer from server with invalid serverid");
445                         break;
446                 }
447
448                 v4tov6(conf.laddr, bp->yiaddr);
449                 memmove(conf.sname, bp->sname, sizeof conf.sname);
450                 conf.sname[sizeof conf.sname-1] = 0;
451                 DEBUG("server=%I sname=%s", conf.server, conf.sname);
452                 conf.offered = lease;
453                 conf.state = Srequesting;
454                 dhcpsend(Request);
455                 conf.resend = 0;
456                 conf.timeout = time(0) + 4;
457                 break;
458         case Ack:
459                 DEBUG("got ack from %V ", bp->siaddr);
460                 if (conf.state != Srequesting && conf.state != Srenewing &&
461                     conf.state != Srebinding)
462                         break;
463
464                 /* ignore a bad lease */
465                 lease = optgetulong(bp->optdata, ODlease);
466                 if(lease == 0){
467                         /*
468                          * The All_Aboard NAT package from Internet Share
469                          * doesn't give a lease time, so we have to assume one.
470                          */
471                         warning("Ack with %lud lease, using %d", lease, MinLease);
472                         lease = MinLease;
473                 }
474                 DEBUG("lease=%lud ", lease);
475
476                 /* address and mask */
477                 if(!validip(conf.laddr) || !Oflag)
478                         v4tov6(conf.laddr, bp->yiaddr);
479                 if(!validip(conf.mask) || !Oflag){
480                         if(!optgetaddr(bp->optdata, OBmask, conf.mask))
481                                 ipmove(conf.mask, IPnoaddr);
482                         if(ipcmp(conf.mask, IPv4bcast) == 0)
483                                 ipmove(conf.mask, IPnoaddr);
484                 }
485                 DEBUG("ipaddr=%I ipmask=%M ", conf.laddr, conf.mask);
486
487                 /*
488                  * get a router address either from the router option
489                  * or from the router that forwarded the dhcp packet
490                  */
491                 if(validip(conf.gaddr) && Oflag) {
492                         DEBUG("ipgw=%I ", conf.gaddr);
493                 } else if(optgetaddr(bp->optdata, OBrouter, conf.gaddr)){
494                         DEBUG("ipgw=%I ", conf.gaddr);
495                 } else if(memcmp(bp->giaddr, IPnoaddr+IPv4off, IPv4addrlen)!=0){
496                         v4tov6(conf.gaddr, bp->giaddr);
497                         DEBUG("giaddr=%I ", conf.gaddr);
498                 }
499
500                 /* get dns servers */
501                 memset(conf.dns, 0, sizeof conf.dns);
502                 n = optgetaddrs(bp->optdata, OBdnserver, conf.dns,
503                         sizeof conf.dns/IPaddrlen);
504                 for(i = 0; i < n; i++)
505                         DEBUG("dns=%I ", conf.dns + i*IPaddrlen);
506
507                 /* get ntp servers */
508                 memset(conf.ntp, 0, sizeof conf.ntp);
509                 n = optgetaddrs(bp->optdata, OBntpserver, conf.ntp,
510                         sizeof conf.ntp/IPaddrlen);
511                 for(i = 0; i < n; i++)
512                         DEBUG("ntp=%I ", conf.ntp + i*IPaddrlen);
513
514                 /* get names */
515                 optgetstr(bp->optdata, OBhostname,
516                         conf.hostname, sizeof conf.hostname);
517                 optgetstr(bp->optdata, OBdomainname,
518                         conf.domainname, sizeof conf.domainname);
519
520                 /* get anything else we asked for */
521                 getoptions(bp->optdata);
522
523                 /* get plan9-specific options */
524                 n = optgetvec(bp->optdata, OBvendorinfo, vopts, sizeof vopts-1);
525                 if(n > 0 && parseoptions(vopts, n) == 0){
526                         if(validip(conf.fs) && Oflag)
527                                 n = 1;
528                         else {
529                                 n = optgetp9addrs(vopts, OP9fs, conf.fs, 2);
530                                 if (n == 0)
531                                         n = optgetaddrs(vopts, OP9fsv4,
532                                                 conf.fs, 2);
533                         }
534                         for(i = 0; i < n; i++)
535                                 DEBUG("fs=%I ", conf.fs + i*IPaddrlen);
536
537                         if(validip(conf.auth) && Oflag)
538                                 n = 1;
539                         else {
540                                 n = optgetp9addrs(vopts, OP9auth, conf.auth, 2);
541                                 if (n == 0)
542                                         n = optgetaddrs(vopts, OP9authv4,
543                                                 conf.auth, 2);
544                         }
545                         for(i = 0; i < n; i++)
546                                 DEBUG("auth=%I ", conf.auth + i*IPaddrlen);
547
548                         n = optgetp9addrs(vopts, OP9ipaddr, taddr, 1);
549                         if (n > 0)
550                                 memmove(conf.laddr, taddr, IPaddrlen);
551                         n = optgetp9addrs(vopts, OP9ipmask, taddr, 1);
552                         if (n > 0)
553                                 memmove(conf.mask, taddr, IPaddrlen);
554                         n = optgetp9addrs(vopts, OP9ipgw, taddr, 1);
555                         if (n > 0)
556                                 memmove(conf.gaddr, taddr, IPaddrlen);
557                         DEBUG("new ipaddr=%I new ipmask=%M new ipgw=%I",
558                                 conf.laddr, conf.mask, conf.gaddr);
559                 }
560                 conf.lease = lease;
561                 conf.state = Sbound;
562                 DEBUG("server=%I sname=%s", conf.server, conf.sname);
563                 break;
564         case Nak:
565                 conf.state = Sinit;
566                 warning("recved dhcpnak on %s", conf.mpoint);
567                 break;
568         }
569 }
570
571 static int
572 openlisten(void)
573 {
574         int n, fd, cfd;
575         char data[128], devdir[40];
576
577         if (validip(conf.laddr) &&
578             (conf.state == Srenewing || conf.state == Srebinding))
579                 sprint(data, "%s/udp!%I!68", conf.mpoint, conf.laddr);
580         else
581                 sprint(data, "%s/udp!*!68", conf.mpoint);
582         for (n = 0; (cfd = announce(data, devdir)) < 0; n++) {
583                 if(!noconfig)
584                         sysfatal("can't announce for dhcp: %r");
585
586                 /* might be another client - wait and try again */
587                 warning("can't announce %s: %r", data);
588                 sleep(jitter());
589                 if(n > 10)
590                         return -1;
591         }
592
593         if(fprint(cfd, "headers") < 0)
594                 sysfatal("can't set header mode: %r");
595
596         sprint(data, "%s/data", devdir);
597         fd = open(data, ORDWR);
598         if(fd < 0)
599                 sysfatal("open %s: %r", data);
600         close(cfd);
601         return fd;
602 }
603
604 static uchar*
605 optadd(uchar *p, int op, void *d, int n)
606 {
607         p[0] = op;
608         p[1] = n;
609         memmove(p+2, d, n);
610         return p+n+2;
611 }
612
613 static uchar*
614 optaddbyte(uchar *p, int op, int b)
615 {
616         p[0] = op;
617         p[1] = 1;
618         p[2] = b;
619         return p+3;
620 }
621
622 static uchar*
623 optaddulong(uchar *p, int op, ulong x)
624 {
625         p[0] = op;
626         p[1] = 4;
627         hnputl(p+2, x);
628         return p+6;
629 }
630
631 static uchar *
632 optaddaddr(uchar *p, int op, uchar *ip)
633 {
634         p[0] = op;
635         p[1] = 4;
636         v6tov4(p+2, ip);
637         return p+6;
638 }
639
640 /* add dhcp option op with value v of length n to dhcp option array p */
641 static uchar *
642 optaddvec(uchar *p, int op, uchar *v, int n)
643 {
644         p[0] = op;
645         p[1] = n;
646         memmove(p+2, v, n);
647         return p+2+n;
648 }
649
650 static uchar *
651 optaddstr(uchar *p, int op, char *v)
652 {
653         int n;
654
655         n = strlen(v);
656         p[0] = op;
657         p[1] = n;
658         memmove(p+2, v, n);
659         return p+2+n;
660 }
661
662 /*
663  * parse p, looking for option `op'.  if non-nil, np points to minimum length.
664  * return nil if option is too small, else ptr to opt, and
665  * store actual length via np if non-nil.
666  */
667 static uchar*
668 optget(uchar *p, int op, int *np)
669 {
670         int len, code;
671
672         while ((code = *p++) != OBend) {
673                 if(code == OBpad)
674                         continue;
675                 len = *p++;
676                 if(code != op) {
677                         p += len;
678                         continue;
679                 }
680                 if(np != nil){
681                         if(*np > len) {
682                                 return 0;
683                         }
684                         *np = len;
685                 }
686                 return p;
687         }
688         return 0;
689 }
690
691 static int
692 optgetbyte(uchar *p, int op)
693 {
694         int len;
695
696         len = 1;
697         p = optget(p, op, &len);
698         if(p == nil)
699                 return 0;
700         return *p;
701 }
702
703 static ulong
704 optgetulong(uchar *p, int op)
705 {
706         int len;
707
708         len = 4;
709         p = optget(p, op, &len);
710         if(p == nil)
711                 return 0;
712         return nhgetl(p);
713 }
714
715 static int
716 optgetaddr(uchar *p, int op, uchar *ip)
717 {
718         int len;
719
720         len = 4;
721         p = optget(p, op, &len);
722         if(p == nil)
723                 return 0;
724         v4tov6(ip, p);
725         return 1;
726 }
727
728 /* expect at most n addresses; ip[] only has room for that many */
729 static int
730 optgetaddrs(uchar *p, int op, uchar *ip, int n)
731 {
732         int len, i;
733
734         len = 4;
735         p = optget(p, op, &len);
736         if(p == nil)
737                 return 0;
738         len /= IPv4addrlen;
739         if(len > n)
740                 len = n;
741         for(i = 0; i < len; i++)
742                 v4tov6(&ip[i*IPaddrlen], &p[i*IPv4addrlen]);
743         return i;
744 }
745
746 /* expect at most n addresses; ip[] only has room for that many */
747 static int
748 optgetp9addrs(uchar *ap, int op, uchar *ip, int n)
749 {
750         int len, i, slen, addrs;
751         char *p;
752
753         len = 1;                        /* minimum bytes needed */
754         p = (char *)optget(ap, op, &len);
755         if(p == nil)
756                 return 0;
757         addrs = *p++;                   /* first byte is address count */
758         for (i = 0; i < n  && i < addrs && len > 0; i++) {
759                 slen = strlen(p) + 1;
760                 if (parseip(&ip[i*IPaddrlen], p) == -1)
761                         fprint(2, "%s: bad address %s\n", argv0, p);
762                 DEBUG("got plan 9 option %d addr %I (%s)",
763                         op, &ip[i*IPaddrlen], p);
764                 p += slen;
765                 len -= slen;
766         }
767         return addrs;
768 }
769
770 static int
771 optgetvec(uchar *p, int op, uchar *v, int n)
772 {
773         int len;
774
775         len = 1;
776         p = optget(p, op, &len);
777         if(p == nil)
778                 return 0;
779         if(len > n)
780                 len = n;
781         memmove(v, p, len);
782         return len;
783 }
784
785 static int
786 optgetstr(uchar *p, int op, char *s, int n)
787 {
788         int len;
789
790         len = 1;
791         p = optget(p, op, &len);
792         if(p == nil)
793                 return 0;
794         if(len >= n)
795                 len = n-1;
796         memmove(s, p, len);
797         s[len] = 0;
798         return len;
799 }
800
801 int
802 addoption(char *opt)
803 {
804         int i;
805         Option *o;
806
807         if(opt == nil)
808                 return -1;
809         for(o = option; o < &option[nelem(option)]; o++)
810                 if(o->name && strcmp(opt, o->name) == 0){
811                         i = o - option;
812                         if(memchr(requested, i, nrequested) == 0 &&
813                             nrequested < nelem(requested))
814                                 requested[nrequested++] = i;
815                         return 0;
816                 }
817         return -1;
818 }
819
820 static char*
821 optgetx(uchar *p, uchar opt)
822 {
823         int i, n;
824         ulong x;
825         char *s, *ns;
826         char str[256];
827         uchar ip[IPaddrlen], ips[16*IPaddrlen], vec[256];
828         Option *o;
829
830         o = &option[opt];
831         if(o->name == nil)
832                 return nil;
833
834         s = nil;
835         switch(o->type){
836         case Taddr:
837                 if(optgetaddr(p, opt, ip))
838                         s = smprint("%s=%I", o->name, ip);
839                 break;
840         case Taddrs:
841                 n = optgetaddrs(p, opt, ips, 16);
842                 if(n > 0)
843                         s = smprint("%s=%I", o->name, ips);
844                 for(i = 1; i < n; i++){
845                         ns = smprint("%s %s=%I", s, o->name, &ips[i*IPaddrlen]);
846                         free(s);
847                         s = ns;
848                 }
849                 break;
850         case Tulong:
851                 x = optgetulong(p, opt);
852                 if(x != 0)
853                         s = smprint("%s=%lud", o->name, x);
854                 break;
855         case Tbyte:
856                 x = optgetbyte(p, opt);
857                 if(x != 0)
858                         s = smprint("%s=%lud", o->name, x);
859                 break;
860         case Tstr:
861                 if(optgetstr(p, opt, str, sizeof str))
862                         s = smprint("%s=%s", o->name, str);
863                 break;
864         case Tvec:
865                 n = optgetvec(p, opt, vec, sizeof vec);
866                 if(n > 0)
867                         /* what's %H?  it's not installed */
868                         s = smprint("%s=%.*H", o->name, n, vec);
869                 break;
870         }
871         return s;
872 }
873
874 static void
875 getoptions(uchar *p)
876 {
877         int i;
878         char *s, *t;
879
880         for(i = nelem(defrequested); i < nrequested; i++){
881                 s = optgetx(p, requested[i]);
882                 if(s != nil)
883                         DEBUG("%s ", s);
884                 if(ndboptions == nil)
885                         ndboptions = smprint("\t%s", s);
886                 else{
887                         t = ndboptions;
888                         ndboptions = smprint("\t%s%s", s, ndboptions);
889                         free(t);
890                 }
891                 free(s);
892         }
893 }
894
895 /*
896  * sanity check options area
897  *      - options don't overflow packet
898  *      - options end with an OBend
899  */
900 static int
901 parseoptions(uchar *p, int n)
902 {
903         int code, len, nin = n;
904
905         while (n > 0) {
906                 code = *p++;
907                 n--;
908                 if(code == OBend)
909                         return 0;
910                 if(code == OBpad)
911                         continue;
912                 if(n == 0) {
913                         warning("parseoptions: bad option: 0x%ux: truncated: "
914                                 "opt length = %d", code, nin);
915                         return -1;
916                 }
917
918                 len = *p++;
919                 n--;
920                 DEBUG("parseoptions: %s(%d) len %d, bytes left %d",
921                         option[code].name, code, len, n);
922                 if(len > n) {
923                         warning("parseoptions: bad option: 0x%ux: %d > %d: "
924                                 "opt length = %d", code, len, n, nin);
925                         return -1;
926                 }
927                 p += len;
928                 n -= len;
929         }
930
931         /* make sure packet ends with an OBend after all the optget code */
932         *p = OBend;
933         return 0;
934 }
935
936 /*
937  * sanity check received packet:
938  *      - magic is dhcp magic
939  *      - options don't overflow packet
940  */
941 static Bootp*
942 parsebootp(uchar *p, int n)
943 {
944         Bootp *bp;
945
946         bp = (Bootp*)p;
947         if(n < bp->optmagic - p) {
948                 warning("parsebootp: short bootp packet");
949                 return nil;
950         }
951
952         if(conf.xid != nhgetl(bp->xid))         /* not meant for us */
953                 return nil;
954
955         if(bp->op != Bootreply) {
956                 warning("parsebootp: bad op %d", bp->op);
957                 return nil;
958         }
959
960         n -= bp->optmagic - p;
961         p = bp->optmagic;
962
963         if(n < 4) {
964                 warning("parsebootp: no option data");
965                 return nil;
966         }
967         if(memcmp(optmagic, p, 4) != 0) {
968                 warning("parsebootp: bad opt magic %ux %ux %ux %ux",
969                         p[0], p[1], p[2], p[3]);
970                 return nil;
971         }
972         p += 4;
973         n -= 4;
974         DEBUG("parsebootp: new packet");
975         if(parseoptions(p, n) < 0)
976                 return nil;
977         return bp;
978 }
979