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