]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/dhcpd/dhcpd.c
dhcpd: ignore ";" filename from sgi arcs bootp(); command
[plan9front.git] / sys / src / cmd / ip / dhcpd / dhcpd.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include <bio.h>
5 #include <ndb.h>
6 #include "dat.h"
7
8 /*
9  *      ala rfc2131
10  */
11
12 enum {
13         Maxloglen = 1024,
14 };
15
16 typedef struct Req Req;
17 struct Req
18 {
19         int     fd;                     /* for reply */
20         Bootp   *bp;
21         Udphdr  *up;
22         uchar   *e;                     /* end of received message */
23         uchar   *p;                     /* options pointer */
24         uchar   *max;                   /* max end of reply */
25
26         /* expanded to v6 */
27         uchar   ciaddr[IPaddrlen];
28         uchar   giaddr[IPaddrlen];
29
30         /* parsed options */
31         int     p9request;              /* flag: this is a bootp with plan9 options */
32         int     genrequest;             /* flag: this is a bootp with generic options */
33         int     dhcptype;               /* dhcp message type */
34         int     leasetime;              /* dhcp lease */
35         uchar   ip[IPaddrlen];          /* requested address */
36         uchar   server[IPaddrlen];      /* server address */
37         char    msg[ERRMAX];            /* error message */
38         char    vci[32];                /* vendor class id */
39         char    *id;                    /* client id */
40         uchar   requested[32];          /* requested params */
41         uchar   vendorclass[32];
42         char    cputype[32-3];
43
44         Info    gii;                    /* about target network */
45         Info    ii;                     /* about target system */
46         int     staticbinding;
47
48         uchar buf[2*1024];              /* message buffer */
49 };
50
51 #define TFTP "/lib/tftpd"
52
53 char    *blog = "ipboot";
54 char    mysysname[64];
55 Ipifc   *ipifcs;
56 int     debug;
57 int     nobootp;
58 long    now;
59 int     slowstat, slowdyn;
60 char    net[256];
61
62 int     pptponly;       /* only answer request that came from the pptp server */
63 int     mute, mutestat;
64 int     minlease = MinLease;
65 int     staticlease = StaticLease;
66
67 uvlong  start;
68
69 static int v6opts;
70
71 /* option magic */
72 char plan9opt[4] = { 'p', '9', ' ', ' ' };
73 char genericopt[4] = { 0x63, 0x82, 0x53, 0x63 };
74
75 /* well known addresses */
76 uchar zeros[Maxhwlen];
77
78 /* option debug buffer */
79 char optbuf[1024];
80 char *op;
81 char *oe = optbuf + sizeof(optbuf);
82
83 char *optname[256] =
84 {
85 [OBend]                 "end",
86 [OBpad]                 "pad",
87 [OBmask]                "mask",
88 [OBtimeoff]             "timeoff",
89 [OBrouter]              "router",
90 [OBtimeserver]          "time",
91 [OBnameserver]          "name",
92 [OBdnserver]            "dns",
93 [OBlogserver]           "log",
94 [OBcookieserver]        "cookie",
95 [OBlprserver]           "lpr",
96 [OBimpressserver]       "impress",
97 [OBrlserver]            "rl",
98 [OBhostname]            "host",
99 [OBbflen]               "bflen",
100 [OBdumpfile]            "dumpfile",
101 [OBdomainname]          "dom",
102 [OBswapserver]          "swap",
103 [OBrootpath]            "rootpath",
104 [OBextpath]             "extpath",
105 [OBipforward]           "ipforward",
106 [OBnonlocal]            "nonlocal",
107 [OBpolicyfilter]        "policyfilter",
108 [OBmaxdatagram]         "maxdatagram",
109 [OBttl]                 "ttl",
110 [OBpathtimeout]         "pathtimeout",
111 [OBpathplateau]         "pathplateau",
112 [OBmtu]                 "mtu",
113 [OBsubnetslocal]        "subnetslocal",
114 [OBbaddr]               "baddr",
115 [OBdiscovermask]        "discovermask",
116 [OBsupplymask]          "supplymask",
117 [OBdiscoverrouter]      "discoverrouter",
118 [OBrsserver]            "rsserver",
119 [OBstaticroutes]        "staticroutes",
120 [OBtrailerencap]        "trailerencap",
121 [OBarptimeout]          "arptimeout",
122 [OBetherencap]          "etherencap",
123 [OBtcpttl]              "tcpttl",
124 [OBtcpka]               "tcpka",
125 [OBtcpkag]              "tcpkag",
126 [OBnisdomain]           "nisdomain",
127 [OBniserver]            "niserver",
128 [OBntpserver]           "ntpserver",
129 [OBvendorinfo]          "vendorinfo",
130 [OBnetbiosns]           "NBns",
131 [OBnetbiosdds]          "NBdds",
132 [OBnetbiostype]         "NBtype",
133 [OBnetbiosscope]        "NBscope",
134 [OBxfontserver]         "xfont",
135 [OBxdispmanager]        "xdisp",
136 [OBnisplusdomain]       "NPdomain",
137 [OBnisplusserver]       "NP",
138 [OBhomeagent]           "homeagent",
139 [OBsmtpserver]          "smtp",
140 [OBpop3server]          "pop3",
141 [OBnntpserver]          "nntp",
142 [OBwwwserver]           "www",
143 [OBfingerserver]        "finger",
144 [OBircserver]           "ircserver",
145 [OBstserver]            "stserver",
146 [OBstdaserver]          "stdaserver",
147
148 /* dhcp options */
149 [ODipaddr]              "ip",
150 [ODlease]               "leas",
151 [ODoverload]            "overload",
152 [ODtype]                "typ",
153 [ODserverid]            "sid",
154 [ODparams]              "params",
155 [ODmessage]             "message",
156 [ODmaxmsg]              "maxmsg",
157 [ODrenewaltime]         "renewaltime",
158 [ODrebindingtime]       "rebindingtime",
159 [ODvendorclass]         "vendorclass",
160 [ODclientid]            "cid",
161 [ODtftpserver]          "tftpserver",
162 [ODbootfile]            "bf",
163 };
164
165 void    addropt(Req*, int, uchar*);
166 void    addrsopt(Req*, int, uchar**, int);
167 void    arpenter(uchar*, uchar*);
168 void    bootp(Req*);
169 void    byteopt(Req*, int, uchar);
170 void    dhcp(Req*);
171 void    fatal(int, char*, ...);
172 void    hexopt(Req*, int, char*);
173 void    logdhcp(Req*);
174 void    logdhcpout(Req *, char *);
175 void    longopt(Req*, int, long);
176 void    maskopt(Req*, int, uchar*);
177 void    miscoptions(Req*, uchar*);
178 int     openlisten(char *net);
179 void    p9addrsopt(Req *rp, int t, uchar **ip, int i);
180 void    parseoptions(Req*);
181 void    proto(Req*, int);
182 void    rcvdecline(Req*);
183 void    rcvdiscover(Req*);
184 void    rcvinform(Req*);
185 void    rcvrelease(Req*);
186 void    rcvrequest(Req*);
187 int     readlast(int, uchar*, int);
188 char*   readsysname(void);
189 void    remrequested(Req*, int);
190 void    sendack(Req*, uchar*, int, int);
191 void    sendnak(Req*, char*);
192 void    sendoffer(Req*, uchar*, int);
193 void    stringopt(Req*, int, char*);
194 void    termopt(Req*);
195 int     validip(uchar*);
196 void    vectoropt(Req*, int, uchar*, int);
197 void    warning(int, char*, ...);
198
199 void
200 timestamp(char *tag)
201 {
202         uvlong t;
203
204         t = nsec()/1000;
205         syslog(0, blog, "%s %lludµs", tag, t - start);
206 }
207
208 void
209 usage(void)
210 {
211         fprint(2, "usage: dhcp [-dmnprsSZ] [-f directory] [-M minlease] "
212                 "[-x netmtpt] [-Z staticlease] addr n [addr n] ...\n");
213         exits("usage");
214 }
215
216 void
217 main(int argc, char **argv)
218 {
219         int i, n, fd;
220         uchar ip[IPaddrlen];
221         Req r;
222
223         setnetmtpt(net, sizeof net, nil);
224
225         fmtinstall('E', eipfmt);
226         fmtinstall('I', eipfmt);
227         fmtinstall('V', eipfmt);
228         fmtinstall('M', eipfmt);
229         ARGBEGIN {
230         case '6':
231                 v6opts = 1;
232                 break;
233         case 'd':
234                 debug = 1;
235                 break;
236         case 'f':
237                 ndbfile = EARGF(usage());
238                 break;
239         case 'm':
240                 mute = 1;
241                 break;
242         case 'M':
243                 minlease = atoi(EARGF(usage()));
244                 if(minlease <= 0)
245                         minlease = MinLease;
246                 break;
247         case 'n':
248                 nobootp = 1;
249                 break;
250         case 'p':
251                 pptponly = 1;
252                 break;
253         case 'r':
254                 mutestat = 1;
255                 break;
256         case 's':
257                 slowstat = 1;
258                 break;
259         case 'S':
260                 slowdyn = 1;
261                 break;
262         case 'x':
263                 setnetmtpt(net, sizeof net, EARGF(usage()));
264                 break;
265         case 'Z':
266                 staticlease = atoi(EARGF(usage()));
267                 if(staticlease <= 0)
268                         staticlease = StaticLease;
269                 break;
270         default:
271                 usage();
272                 break;
273         } ARGEND;
274
275         while(argc > 1){
276                 parseip(ip, argv[0]);
277                 if(!validip(ip))
278                         usage();
279                 n = atoi(argv[1]);
280                 if(n <= 0)
281                         usage();
282                 initbinding(ip, n);
283                 argc -= 2;
284                 argv += 2;
285         }
286
287         /* for debugging */
288         for(i = 0; i < 256; i++)
289                 if(optname[i] == 0)
290                         optname[i] = smprint("%d", i);
291
292         /* what is my name? */
293         strcpy(mysysname, readsysname());
294
295         /* put process in background */
296         if(!debug)
297         switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
298         case -1:
299                 fatal(1, "fork");
300         case 0:
301                 break;
302         default:
303                 exits(0);
304         }
305
306         if (chdir(TFTP) < 0)
307                 warning(1, "can't change directory to %s", TFTP);
308         fd = openlisten(net);
309
310         for(;;){
311                 memset(&r, 0, sizeof(r));
312                 r.fd = fd;
313                 n = readlast(r.fd, r.buf, sizeof(r.buf));
314                 if(n < Udphdrsize)
315                         fatal(1, "error reading requests");
316                 start = nsec()/1000;
317                 op = optbuf;
318                 *op = 0;
319                 proto(&r, n);
320                 if(r.id != nil)
321                         free(r.id);
322         }
323 }
324
325 void
326 proto(Req *rp, int n)
327 {
328         uchar relip[IPaddrlen];
329         char buf[64];
330
331         now = time(0);
332
333         rp->e = rp->buf + n;
334         rp->bp = (Bootp*)rp->buf;
335         rp->up = (Udphdr*)rp->buf;
336         if (ipcmp(rp->up->laddr, IPv4bcast) == 0)
337                 ipmove(rp->up->laddr, rp->up->ifcaddr);
338         rp->max = rp->buf + Udphdrsize + MINSUPPORTED - IPUDPHDRSIZE;
339         rp->p = rp->bp->optdata;
340         v4tov6(rp->giaddr, rp->bp->giaddr);
341         v4tov6(rp->ciaddr, rp->bp->ciaddr);
342
343         if(pptponly && rp->bp->htype != 0)
344                 return;
345
346         ipifcs = readipifc(net, ipifcs, -1);
347         if(validip(rp->giaddr))
348                 ipmove(relip, rp->giaddr);
349         else if(validip(rp->up->raddr))
350                 ipmove(relip, rp->up->raddr);
351         else
352                 ipmove(relip, rp->up->laddr);
353         if(rp->e < (uchar*)rp->bp->sname){
354                 warning(0, "packet too short");
355                 return;
356         }
357         if(rp->bp->op != Bootrequest){
358                 warning(0, "not bootrequest");
359                 return;
360         }
361
362         if(rp->e >= rp->bp->optdata){
363                 if(memcmp(rp->bp->optmagic, plan9opt, sizeof(rp->bp->optmagic)) == 0)
364                         rp->p9request = 1;
365                 if(memcmp(rp->bp->optmagic, genericopt, sizeof(rp->bp->optmagic)) == 0) {
366                         rp->genrequest = 1;
367                         parseoptions(rp);
368                 }
369         }
370         rp->p = rp->bp->optdata;
371
372         /*  If no id is specified, make one from the hardware address
373          *  of the target.  We assume all zeros is not a hardware address
374          *  which could be a mistake.
375          */
376         if(rp->id == nil){
377                 if(rp->bp->hlen > Maxhwlen){
378                         warning(0, "hlen %d", rp->bp->hlen);
379                         return;
380                 }
381                 if(memcmp(zeros, rp->bp->chaddr, rp->bp->hlen) == 0){
382                         warning(0, "no chaddr");
383                         return;
384                 }
385                 sprint(buf, "hwa%2.2ux_", rp->bp->htype);
386                 rp->id = tohex(buf, rp->bp->chaddr, rp->bp->hlen);
387         }
388
389         /* info about gateway */
390         if(lookupip(relip, &rp->gii, 1) < 0){
391                 warning(0, "lookupip failed");
392                 return;
393         }
394
395         /* info about target system */
396         if(lookup(rp->bp, &rp->ii, &rp->gii) == 0)
397                 if(rp->ii.indb && rp->ii.dhcpgroup[0] == 0)
398                         rp->staticbinding = 1;
399
400         if(rp->dhcptype)
401                 dhcp(rp);
402         else
403                 bootp(rp);
404         timestamp("done");
405 }
406
407 static void
408 slowdelay(Req *rp)
409 {
410         if(slowstat && rp->staticbinding || slowdyn && !rp->staticbinding)
411                 sleep(2000);
412 }
413
414 void
415 dhcp(Req *rp)
416 {
417         logdhcp(rp);
418
419         switch(rp->dhcptype){
420         case Discover:
421                 slowdelay(rp);
422                 rcvdiscover(rp);
423                 break;
424         case Request:
425                 rcvrequest(rp);
426                 break;
427         case Decline:
428                 rcvdecline(rp);
429                 break;
430         case Release:
431                 rcvrelease(rp);
432                 break;
433         case Inform:
434                 rcvinform(rp);
435                 break;
436         }
437 }
438
439 void
440 rcvdiscover(Req *rp)
441 {
442         Binding *b, *nb;
443
444         if(rp->staticbinding){
445                 sendoffer(rp, rp->ii.ipaddr, (staticlease > minlease? staticlease: minlease));
446                 return;
447         }
448
449         /*
450          *  first look for an outstanding offer
451          */
452         b = idtooffer(rp->id, &rp->gii);
453
454         /*
455          * rfc2131 says:
456          *   If an address is available, the new address
457          *   SHOULD be chosen as follows:
458          *
459          *      o The client's current address as recorded in the client's current
460          *        binding, ELSE
461          *
462          *      o The client's previous address as recorded in the client's (now
463          *        expired or released) binding, if that address is in the server's
464          *        pool of available addresses and not already allocated, ELSE
465          *
466          *      o The address requested in the 'Requested IP Address' option, if that
467          *        address is valid and not already allocated, ELSE
468          *
469          *      o A new address allocated from the server's pool of available
470          *        addresses; the address is selected based on the subnet from which
471          *        the message was received (if 'giaddr' is 0) or on the address of
472          *        the relay agent that forwarded the message ('giaddr' when not 0).
473          */
474         if(b == nil){
475                 b = idtobinding(rp->id, &rp->gii, 1);
476                 if(b && b->boundto && strcmp(b->boundto, rp->id) != 0)
477                 if(validip(rp->ip) && samenet(rp->ip, &rp->gii)){
478                         nb = iptobinding(rp->ip, 0);
479                         if(nb && nb->lease < now)
480                                 b = nb;
481                 }
482         }
483         if(b == nil){
484                 warning(0, "!Discover(%s via %I): no binding %I",
485                         rp->id, rp->gii.ipaddr, rp->ip);
486                 return;
487         }
488         mkoffer(b, rp->id, rp->leasetime);
489         sendoffer(rp, b->ip, b->offer);
490 }
491
492 void
493 rcvrequest(Req *rp)
494 {
495         Binding *b;
496
497         if(validip(rp->server)){
498                 /* this is a reply to an offer - SELECTING */
499
500                 /* check for hard assignment */
501                 if(rp->staticbinding){
502                         if(forme(rp->server))
503                                 sendack(rp, rp->ii.ipaddr,
504                                         (staticlease > minlease? staticlease:
505                                         minlease), 1);
506                         else
507                                 warning(0, "!Request(%s via %I): for server %I not me",
508                                         rp->id, rp->gii.ipaddr, rp->server);
509                         return;
510                 }
511
512                 b = idtooffer(rp->id, &rp->gii);
513
514                 /* if we don't have an offer, nak */
515                 if(b == nil){
516                         warning(0, "!Request(%s via %I): no offer",
517                                 rp->id, rp->gii.ipaddr);
518                         if(forme(rp->server))
519                                 sendnak(rp, "no offer for you");
520                         return;
521                 }
522
523                 /* if not for me, retract offer */
524                 if(!forme(rp->server)){
525                         b->expoffer = 0;
526                         warning(0, "!Request(%s via %I): for server %I not me",
527                                 rp->id, rp->gii.ipaddr, rp->server);
528                         return;
529                 }
530
531                 /*
532                  *  if the client is confused about what we offered, nak.
533                  *  client really shouldn't be specifying this when selecting
534                  */
535                 if(validip(rp->ip) && ipcmp(rp->ip, b->ip) != 0){
536                         warning(0, "!Request(%s via %I): requests %I, not %I",
537                                 rp->id, rp->gii.ipaddr, rp->ip, b->ip);
538                         sendnak(rp, "bad ip address option");
539                         return;
540                 }
541                 if(commitbinding(b) < 0){
542                         warning(0, "!Request(%s via %I): can't commit %I",
543                                 rp->id, rp->gii.ipaddr, b->ip);
544                         sendnak(rp, "can't commit binding");
545                         return;
546                 }
547                 sendack(rp, b->ip, b->offer, 1);
548         } else if(validip(rp->ip)){
549                 /*
550                  *  checking address/net - INIT-REBOOT
551                  *
552                  *  This is a rebooting client that remembers its old
553                  *  address.
554                  */
555                 /* check for hard assignment */
556                 if(rp->staticbinding){
557                         if(memcmp(rp->ip, rp->ii.ipaddr, IPaddrlen) != 0){
558                                 warning(0, "!Request(%s via %I): %I not valid for %E",
559                                         rp->id, rp->gii.ipaddr, rp->ip, rp->bp->chaddr);
560                                 sendnak(rp, "not valid");
561                         }
562                         sendack(rp, rp->ii.ipaddr, (staticlease > minlease?
563                                 staticlease: minlease), 1);
564                         return;
565                 }
566
567                 /* make sure the network makes sense */
568                 if(!samenet(rp->ip, &rp->gii)){
569                         warning(0, "!Request(%s via %I): bad forward of %I",
570                                 rp->id, rp->gii.ipaddr, rp->ip);
571                         sendnak(rp, "wrong network");
572                         return;
573                 }
574                 b = iptobinding(rp->ip, 0);
575                 if(b == nil){
576                         warning(0, "!Request(%s via %I): no binding for %I",
577                                 rp->id, rp->gii.ipaddr, rp->ip);
578                         return;
579                 }
580                 if(memcmp(rp->ip, b->ip, IPaddrlen) != 0 || now > b->lease){
581                         warning(0, "!Request(%s via %I): %I not valid",
582                                 rp->id, rp->gii.ipaddr, rp->ip);
583                         sendnak(rp, "not valid");
584                         return;
585                 }
586                 b->offer = b->lease - now;
587                 sendack(rp, b->ip, b->offer, 1);
588         } else if(validip(rp->ciaddr)){
589                 /*
590                  *  checking address - RENEWING or REBINDING
591                  *
592                  *  these states are indistinguishable in our action.  The only
593                  *  difference is how close to lease expiration the client is.
594                  *  If it is really close, it broadcasts the request hoping that
595                  *  some server will answer.
596                  */
597
598                 /* check for hard assignment */
599                 if(rp->staticbinding){
600                         if(ipcmp(rp->ciaddr, rp->ii.ipaddr) != 0){
601                                 warning(0, "!Request(%s via %I): %I not valid",
602                                         rp->id, rp->gii.ipaddr, rp->ciaddr);
603                                 sendnak(rp, "not valid");
604                         }
605                         sendack(rp, rp->ii.ipaddr, (staticlease > minlease?
606                                 staticlease: minlease), 1);
607                         return;
608                 }
609
610                 /* make sure the network makes sense */
611                 if(!samenet(rp->ciaddr, &rp->gii)){
612                         warning(0, "!Request(%s via %I): bad forward of %I",
613                                 rp->id, rp->gii.ipaddr, rp->ip);
614                         sendnak(rp, "wrong network");
615                         return;
616                 }
617                 b = iptobinding(rp->ciaddr, 0);
618                 if(b == nil){
619                         warning(0, "!Request(%s via %I): no binding for %I",
620                                 rp->id, rp->gii.ipaddr, rp->ciaddr);
621                         return;
622                 }
623                 if(ipcmp(rp->ciaddr, b->ip) != 0){
624                         warning(0, "!Request(%I via %s): %I not valid",
625                                 rp->id, rp->gii.ipaddr, rp->ciaddr);
626                         sendnak(rp, "invalid ip address");
627                         return;
628                 }
629                 mkoffer(b, rp->id, rp->leasetime);
630                 if(commitbinding(b) < 0){
631                         warning(0, "!Request(%s via %I): can't commit %I",
632                                 rp->id, rp->gii.ipaddr, b->ip);
633                         sendnak(rp, "can't commit binding");
634                         return;
635                 }
636                 sendack(rp, b->ip, b->offer, 1);
637         }
638 }
639
640 void
641 rcvdecline(Req *rp)
642 {
643         Binding *b;
644         char buf[64];
645
646         if(rp->staticbinding)
647                 return;
648
649         b = idtooffer(rp->id, &rp->gii);
650         if(b == nil){
651                 warning(0, "!Decline(%s via %I): no binding",
652                         rp->id, rp->gii.ipaddr);
653                 return;
654         }
655
656         /* mark ip address as in use */
657         snprint(buf, sizeof(buf), "declined by %s", rp->id);
658         mkoffer(b, buf, 0x7fffffff);
659         commitbinding(b);
660 }
661
662 void
663 rcvrelease(Req *rp)
664 {
665         Binding *b;
666
667         if(rp->staticbinding)
668                 return;
669
670         b = idtobinding(rp->id, &rp->gii, 0);
671         if(b == nil){
672                 warning(0, "!Release(%s via %I): no binding",
673                         rp->id, rp->gii.ipaddr);
674                 return;
675         }
676         if(strcmp(rp->id, b->boundto) != 0){
677                 warning(0, "!Release(%s via %I): invalid release of %I",
678                         rp->id, rp->gii.ipaddr, rp->ip);
679                 return;
680         }
681         warning(0, "Release(%s via %I): releasing %I", b->boundto, rp->gii.ipaddr, b->ip);
682         if(releasebinding(b, rp->id) < 0)
683                 warning(0, "release: couldn't release");
684 }
685
686 void
687 rcvinform(Req *rp)
688 {
689         Binding *b;
690
691         if(rp->staticbinding){
692                 sendack(rp, rp->ii.ipaddr, 0, 0);
693                 return;
694         }
695
696         b = iptobinding(rp->ciaddr, 0);
697         if(b == nil){
698                 warning(0, "!Inform(%s via %I): no binding for %I",
699                         rp->id, rp->gii.ipaddr, rp->ip);
700                 return;
701         }
702         sendack(rp, b->ip, 0, 0);
703 }
704
705 int
706 setsiaddr(uchar *siaddr, uchar *saddr, uchar *laddr)
707 {
708         if(ipcmp(saddr, IPnoaddr) != 0){
709                 v6tov4(siaddr, saddr);
710                 return 0;
711         } else {
712                 v6tov4(siaddr, laddr);
713                 return 1;
714         }
715 }
716
717 int
718 ismuted(Req *rp)
719 {
720         return mute || (mutestat && rp->staticbinding);
721 }
722
723 void
724 sendoffer(Req *rp, uchar *ip, int offer)
725 {
726         int n;
727         ushort flags;
728         Bootp *bp;
729         Udphdr *up;
730
731         bp = rp->bp;
732         up = rp->up;
733
734         /*
735          *  set destination
736          */
737         flags = nhgets(bp->flags);
738         if(validip(rp->giaddr)){
739                 ipmove(up->raddr, rp->giaddr);
740                 hnputs(up->rport, 67);
741         } else if(flags & Fbroadcast){
742                 ipmove(up->raddr, IPv4bcast);
743                 hnputs(up->rport, 68);
744         } else {
745                 ipmove(up->raddr, ip);
746                 if(bp->htype == 1)
747                         arpenter(up->raddr, bp->chaddr);
748                 hnputs(up->rport, 68);
749         }
750
751         /*
752          *  fill in standard bootp part
753          */
754         bp->op = Bootreply;
755         bp->hops = 0;
756         hnputs(bp->secs, 0);
757         memset(bp->ciaddr, 0, sizeof(bp->ciaddr));
758         v6tov4(bp->giaddr, rp->giaddr);
759         v6tov4(bp->yiaddr, ip);
760         setsiaddr(bp->siaddr, rp->ii.tftp, up->laddr);
761         strncpy(bp->sname, mysysname, sizeof(bp->sname));
762         strncpy(bp->file, rp->ii.bootf, sizeof(bp->file));
763
764         /*
765          *  set options
766          */
767         byteopt(rp, ODtype, Offer);
768         longopt(rp, ODlease, offer);
769         addropt(rp, ODserverid, up->laddr);
770         miscoptions(rp, ip);
771         termopt(rp);
772
773         logdhcpout(rp, "Offer");
774
775         /*
776          *  send
777          */
778         n = rp->p - rp->buf;
779         if(!ismuted(rp) && write(rp->fd, rp->buf, n) != n)
780                 warning(0, "offer: write failed: %r");
781 }
782
783 void
784 sendack(Req *rp, uchar *ip, int offer, int sendlease)
785 {
786         int n;
787         ushort flags;
788         Bootp *bp;
789         Udphdr *up;
790
791         bp = rp->bp;
792         up = rp->up;
793
794         /*
795          *  set destination
796          */
797         flags = nhgets(bp->flags);
798         if(validip(rp->giaddr)){
799                 ipmove(up->raddr, rp->giaddr);
800                 hnputs(up->rport, 67);
801         } else if(flags & Fbroadcast){
802                 ipmove(up->raddr, IPv4bcast);
803                 hnputs(up->rport, 68);
804         } else {
805                 ipmove(up->raddr, ip);
806                 if(bp->htype == 1)
807                         arpenter(up->raddr, bp->chaddr);
808                 hnputs(up->rport, 68);
809         }
810
811         /*
812          *  fill in standard bootp part
813          */
814         bp->op = Bootreply;
815         bp->hops = 0;
816         hnputs(bp->secs, 0);
817         v6tov4(bp->giaddr, rp->giaddr);
818         v6tov4(bp->yiaddr, ip);
819         setsiaddr(bp->siaddr, rp->ii.tftp, up->laddr);
820         strncpy(bp->sname, mysysname, sizeof(bp->sname));
821         strncpy(bp->file, rp->ii.bootf, sizeof(bp->file));
822
823         /*
824          *  set options
825          */
826         byteopt(rp, ODtype, Ack);
827         if(sendlease){
828                 longopt(rp, ODlease, offer);
829         }
830         addropt(rp, ODserverid, up->laddr);
831         miscoptions(rp, ip);
832         termopt(rp);
833
834         logdhcpout(rp, "Ack");
835
836         /*
837          *  send
838          */
839         n = rp->p - rp->buf;
840         if(!ismuted(rp) && write(rp->fd, rp->buf, n) != n)
841                 warning(0, "ack: write failed: %r");
842 }
843
844 void
845 sendnak(Req *rp, char *msg)
846 {
847         int n;
848         Bootp *bp;
849         Udphdr *up;
850
851         bp = rp->bp;
852         up = rp->up;
853
854         /*
855          *  set destination (always broadcast)
856          */
857         if(validip(rp->giaddr)){
858                 ipmove(up->raddr, rp->giaddr);
859                 hnputs(up->rport, 67);
860         } else {
861                 ipmove(up->raddr, IPv4bcast);
862                 hnputs(up->rport, 68);
863         }
864
865         /*
866          *  fill in standard bootp part
867          */
868         bp->op = Bootreply;
869         bp->hops = 0;
870         hnputs(bp->secs, 0);
871         v6tov4(bp->giaddr, rp->giaddr);
872         memset(bp->ciaddr, 0, sizeof(bp->ciaddr));
873         memset(bp->yiaddr, 0, sizeof(bp->yiaddr));
874         memset(bp->siaddr, 0, sizeof(bp->siaddr));
875
876         /*
877          *  set options
878          */
879         byteopt(rp, ODtype, Nak);
880         addropt(rp, ODserverid, up->laddr);
881         if(msg)
882                 stringopt(rp, ODmessage, msg);
883         if(strncmp(rp->id, "id", 2) == 0)
884                 hexopt(rp, ODclientid, rp->id+2);
885         termopt(rp);
886
887         logdhcpout(rp, "Nak");
888
889         /*
890          *  send nak
891          */
892         n = rp->p - rp->buf;
893         if(!ismuted(rp) && write(rp->fd, rp->buf, n) != n)
894                 warning(0, "nak: write failed: %r");
895 }
896
897 void
898 bootp(Req *rp)
899 {
900         int n;
901         Bootp *bp;
902         Udphdr *up;
903         ushort flags;
904         Iplifc *lifc;
905         Info *iip;
906
907         warning(0, "bootp %s %I->%I from %s via %I, file %s",
908                 rp->genrequest? "generic": (rp->p9request? "p9": ""),
909                 rp->up->raddr, rp->up->laddr,
910                 rp->id, rp->gii.ipaddr,
911                 rp->bp->file);
912
913         if(nobootp)
914                 return;
915
916         bp = rp->bp;
917         up = rp->up;
918         iip = &rp->ii;
919
920         if(rp->staticbinding == 0){
921                 warning(0, "bootp from unknown %s via %I", rp->id, rp->gii.ipaddr);
922                 return;
923         }
924
925         /* ignore if not for us */
926         if(*bp->sname){
927                 if(strcmp(bp->sname, mysysname) != 0){
928                         bp->sname[20] = 0;
929                         warning(0, "bootp for server %s", bp->sname);
930                         return;
931                 }
932         } else
933                 slowdelay(rp);
934
935         /* ignore file for sgi arcs command bootp(); */
936         if(strcmp(bp->file, ";") == 0)
937                 *bp->file = 0;
938
939         /* ignore if we don't know what file to load */
940         if(*bp->file == 0){
941                 if(rp->genrequest && *iip->bootf2) /* if not plan 9 & have alternate file... */
942                         strncpy(bp->file, iip->bootf2, sizeof(bp->file));
943                 else if(*iip->bootf)
944                         strncpy(bp->file, iip->bootf, sizeof(bp->file));
945                 else if(*bp->sname) /* if we were asked, respond no matter what */
946                         bp->file[0] = '\0';
947                 else {
948                         warning(0, "no bootfile for %I", iip->ipaddr);
949                         return;
950                 }
951         }
952
953         /* ignore if the file is unreadable */
954         if((!rp->genrequest) && bp->file[0] && access(bp->file, 4) < 0){
955                 warning(0, "inaccessible bootfile1 %s", bp->file);
956                 return;
957         }
958
959         bp->op = Bootreply;
960         v6tov4(bp->yiaddr, iip->ipaddr);
961         if(rp->p9request){
962                 warning(0, "p9bootp: %I", iip->ipaddr);
963                 memmove(bp->optmagic, plan9opt, 4);
964                 if(iip->gwip == 0)
965                         v4tov6(iip->gwip, bp->giaddr);
966                 rp->p += sprint((char*)rp->p, "%V %I %I %I", iip->ipmask+IPv4off, iip->fsip,
967                                 iip->auip, iip->gwip);
968                 sprint(optbuf, "%s", (char*)(bp->optmagic));
969         } else if(rp->genrequest){
970                 warning(0, "genericbootp: %I", iip->ipaddr);
971                 memmove(bp->optmagic, genericopt, 4);
972                 miscoptions(rp, iip->ipaddr);
973                 termopt(rp);
974         } else if(iip->vendor[0] != 0) {
975                 warning(0, "bootp vendor field: %s", iip->vendor);
976                 memset(rp->p, 0, 128-4);
977                 rp->p += sprint((char*)bp->optmagic, "%s", iip->vendor);
978         } else {
979                 memset(rp->p, 0, 128-4);
980                 rp->p += 128-4;
981         }
982
983         /*
984          *  set destination
985          */
986         flags = nhgets(bp->flags);
987         if(validip(rp->giaddr)){
988                 ipmove(up->raddr, rp->giaddr);
989                 hnputs(up->rport, 67);
990         } else if(flags & Fbroadcast){
991                 ipmove(up->raddr, IPv4bcast);
992                 hnputs(up->rport, 68);
993         } else {
994                 v4tov6(up->raddr, bp->yiaddr);
995                 if(bp->htype == 1)
996                         arpenter(up->raddr, bp->chaddr);
997                 hnputs(up->rport, 68);
998         }
999
1000         /*
1001          *  select best local address if destination is directly connected
1002          */
1003         lifc = findlifc(up->raddr);
1004         if(lifc)
1005                 ipmove(up->laddr, lifc->ip);
1006
1007         /*
1008          *  our identity
1009          */
1010         strncpy(bp->sname, mysysname, sizeof(bp->sname));
1011
1012         /*
1013          *  set tftp server
1014          */
1015         setsiaddr(bp->siaddr, iip->tftp, up->laddr);
1016         if(rp->genrequest && *iip->bootf2)
1017                 setsiaddr(bp->siaddr, iip->tftp2, up->laddr);
1018
1019         /*
1020          * RFC 1048 says that we must pad vendor field with
1021          * zeros until we have a 64 byte field.
1022          */
1023         n = rp->p - rp->bp->optdata;
1024         if(n < 64-4) {
1025                 memset(rp->p, 0, (64-4)-n);
1026                 rp->p += (64-4)-n;
1027         }
1028
1029         /*
1030          *  send
1031          */
1032         n = rp->p - rp->buf;
1033         if(!ismuted(rp) && write(rp->fd, rp->buf, n) != n)
1034                 warning(0, "bootp: write failed: %r");
1035
1036         warning(0, "bootp via %I: file %s xid(%ux)flag(%ux)ci(%V)gi(%V)yi(%V)si(%V) %s",
1037                         up->raddr, bp->file, nhgetl(bp->xid), nhgets(bp->flags),
1038                         bp->ciaddr, bp->giaddr, bp->yiaddr, bp->siaddr,
1039                         optbuf);
1040 }
1041
1042 void
1043 parseoptions(Req *rp)
1044 {
1045         int n, c, code;
1046         uchar *o, *p;
1047
1048         p = rp->p;
1049
1050         while(p < rp->e){
1051                 code = *p++;
1052                 if(code == 255)
1053                         break;
1054                 if(code == 0)
1055                         continue;
1056
1057                 /* ignore anything that's too long */
1058                 n = *p++;
1059                 o = p;
1060                 p += n;
1061                 if(p > rp->e)
1062                         return;
1063
1064                 switch(code){
1065                 case ODipaddr:  /* requested ip address */
1066                         if(n == IPv4addrlen)
1067                                 v4tov6(rp->ip, o);
1068                         break;
1069                 case ODlease:   /* requested lease time */
1070                         rp->leasetime = nhgetl(o);
1071                         if(rp->leasetime > MaxLease || rp->leasetime < 0)
1072                                 rp->leasetime = MaxLease;
1073                         break;
1074                 case ODtype:
1075                         c = *o;
1076                         if(c < 10 && c > 0)
1077                                 rp->dhcptype = c;
1078                         break;
1079                 case ODserverid:
1080                         if(n == IPv4addrlen)
1081                                 v4tov6(rp->server, o);
1082                         break;
1083                 case ODmessage:
1084                         if(n > sizeof rp->msg-1)
1085                                 n = sizeof rp->msg-1;
1086                         memmove(rp->msg, o, n);
1087                         rp->msg[n] = 0;
1088                         break;
1089                 case ODmaxmsg:
1090                         c = nhgets(o);
1091                         c -= 28;
1092                         c += Udphdrsize;
1093                         if(c > 0)
1094                                 rp->max = rp->buf + c;
1095                         break;
1096                 case ODclientid:
1097                         if(n <= 1)
1098                                 break;
1099                         rp->id = toid( o, n);
1100                         break;
1101                 case ODparams:
1102                         if(n > sizeof(rp->requested))
1103                                 n = sizeof(rp->requested);
1104                         memmove(rp->requested, o, n);
1105                         break;
1106                 case ODvendorclass:
1107                         if(n >= sizeof(rp->vendorclass))
1108                                 n = sizeof(rp->vendorclass)-1;
1109                         memmove(rp->vendorclass, o, n);
1110                         rp->vendorclass[n] = 0;
1111                         if(strncmp((char*)rp->vendorclass, "p9-", 3) == 0)
1112                                 strcpy(rp->cputype, (char*)rp->vendorclass+3);
1113                         break;
1114                 case OBend:
1115                         return;
1116                 }
1117         }
1118 }
1119
1120 void
1121 remrequested(Req *rp, int opt)
1122 {
1123         uchar *p;
1124
1125         p = memchr(rp->requested, opt, sizeof(rp->requested));
1126         if(p != nil)
1127                 *p = OBpad;
1128 }
1129
1130 void
1131 miscoptions(Req *rp, uchar *ip)
1132 {
1133         int i, j, na;
1134         uchar x[2*IPaddrlen], vopts[Maxoptlen];
1135         uchar *op, *omax;
1136         uchar *addrs[2];
1137         char *p;
1138         char *attr[100], **a;
1139         Ndbtuple *t;
1140
1141         addrs[0] = x;
1142         addrs[1] = x+IPaddrlen;
1143
1144         /* always supply these */
1145         maskopt(rp, OBmask, rp->gii.ipmask);
1146         if(validip(rp->gii.gwip)){
1147                 remrequested(rp, OBrouter);
1148                 addropt(rp, OBrouter, rp->gii.gwip);
1149         } else if(validip(rp->giaddr)){
1150                 remrequested(rp, OBrouter);
1151                 addropt(rp, OBrouter, rp->giaddr);
1152         }
1153
1154         /*
1155          * OBhostname for the HP4000M switches
1156          * (this causes NT to log infinite errors - tough shit)
1157          */
1158         if(*rp->ii.domain){
1159                 remrequested(rp, OBhostname);
1160                 stringopt(rp, OBhostname, rp->ii.domain);
1161         }
1162         if(*rp->ii.rootpath)
1163                 stringopt(rp, OBrootpath, rp->ii.rootpath);
1164
1165         /* figure out what we need to lookup */
1166         na = 0;
1167         a = attr;
1168         if(*rp->ii.domain == 0)
1169                 a[na++] = "dom";
1170         for(i = 0; i < sizeof(rp->requested); i++)
1171                 switch(rp->requested[i]){
1172                 case OBrouter:
1173                         a[na++] = "@ipgw";
1174                         break;
1175                 case OBdnserver:
1176                         a[na++] = "@dns";
1177                         break;
1178                 case OBnetbiosns:
1179                         a[na++] = "@wins";
1180                         break;
1181                 case OBsmtpserver:
1182                         a[na++] = "@smtp";
1183                         break;
1184                 case OBpop3server:
1185                         a[na++] = "@pop3";
1186                         break;
1187                 case OBwwwserver:
1188                         a[na++] = "@www";
1189                         break;
1190                 case OBntpserver:
1191                         a[na++] = "@ntp";
1192                         break;
1193                 case OBtimeserver:
1194                         a[na++] = "@time";
1195                         break;
1196                 }
1197         if(strncmp((char*)rp->vendorclass, "plan9_", 6) == 0
1198         || strncmp((char*)rp->vendorclass, "p9-", 3) == 0){
1199                 a[na++] = "@fs";
1200                 a[na++] = "@auth";
1201         }
1202         t = lookupinfo(ip, a, na);
1203
1204         /* lookup anything we might be missing */
1205         if(*rp->ii.domain == 0)
1206                 lookupname(rp->ii.domain, t);
1207
1208         /* add any requested ones that we know about */
1209         for(i = 0; i < sizeof(rp->requested); i++)
1210                 switch(rp->requested[i]){
1211                 case OBrouter:
1212                         j = lookupserver("ipgw", addrs, t);
1213                         addrsopt(rp, OBrouter, addrs, j);
1214                         break;
1215                 case OBdnserver:
1216                         j = lookupserver("dns", addrs, t);
1217                         addrsopt(rp, OBdnserver, addrs, j);
1218                         break;
1219                 case OBhostname:
1220                         if(*rp->ii.domain)
1221                                 stringopt(rp, OBhostname, rp->ii.domain);
1222                         break;
1223                 case OBdomainname:
1224                         p = strchr(rp->ii.domain, '.');
1225                         if(p)
1226                                 stringopt(rp, OBdomainname, p+1);
1227                         break;
1228                 case OBnetbiosns:
1229                         j = lookupserver("wins", addrs, t);
1230                         addrsopt(rp, OBnetbiosns, addrs, j);
1231                         break;
1232                 case OBnetbiostype:
1233                         /* p-node: peer to peer WINS queries */
1234                         byteopt(rp, OBnetbiostype, 0x2);
1235                         break;
1236                 case OBsmtpserver:
1237                         j = lookupserver("smtp", addrs, t);
1238                         addrsopt(rp, OBsmtpserver, addrs, j);
1239                         break;
1240                 case OBpop3server:
1241                         j = lookupserver("pop3", addrs, t);
1242                         addrsopt(rp, OBpop3server, addrs, j);
1243                         break;
1244                 case OBwwwserver:
1245                         j = lookupserver("www", addrs, t);
1246                         addrsopt(rp, OBwwwserver, addrs, j);
1247                         break;
1248                 case OBntpserver:
1249                         j = lookupserver("ntp", addrs, t);
1250                         addrsopt(rp, OBntpserver, addrs, j);
1251                         break;
1252                 case OBtimeserver:
1253                         j = lookupserver("time", addrs, t);
1254                         addrsopt(rp, OBtimeserver, addrs, j);
1255                         break;
1256                 case OBttl:
1257                         byteopt(rp, OBttl, 255);
1258                         break;
1259                 }
1260
1261         /* add plan9 specific options */
1262         if(strncmp((char*)rp->vendorclass, "plan9_", 6) == 0
1263         || strncmp((char*)rp->vendorclass, "p9-", 3) == 0){
1264                 /* point to temporary area */
1265                 op = rp->p;
1266                 omax = rp->max;
1267                 /* stash encoded options in vopts */
1268                 rp->p = vopts;
1269                 rp->max = vopts + sizeof(vopts) - 1;
1270
1271                 /* emit old v4 addresses first to make sure that they fit */
1272                 addrsopt(rp, OP9fsv4, addrs, lookupserver("fs", addrs, t));
1273                 addrsopt(rp, OP9authv4, addrs, lookupserver("auth", addrs, t));
1274
1275                 p9addrsopt(rp, OP9fs, addrs, lookupserver("fs", addrs, t));
1276                 p9addrsopt(rp, OP9auth, addrs, lookupserver("auth", addrs, t));
1277                 p9addrsopt(rp, OP9ipaddr, addrs, lookupserver("ip", addrs, t));
1278                 p9addrsopt(rp, OP9ipmask, addrs, lookupserver("ipmask", addrs, t));
1279                 p9addrsopt(rp, OP9ipgw, addrs, lookupserver("ipgw", addrs, t));
1280
1281                 /* point back to packet, encapsulate vopts into packet */
1282                 j = rp->p - vopts;
1283                 rp->p = op;
1284                 rp->max = omax;
1285                 vectoropt(rp, OBvendorinfo, vopts, j);
1286         }
1287
1288         ndbfree(t);
1289 }
1290
1291 int
1292 openlisten(char *net)
1293 {
1294         int fd, cfd;
1295         char data[128], devdir[40];
1296
1297         sprint(data, "%s/udp!*!bootp", net);
1298         cfd = announce(data, devdir);
1299         if(cfd < 0)
1300                 fatal(1, "can't announce");
1301         if(fprint(cfd, "headers") < 0)
1302                 fatal(1, "can't set header mode");
1303
1304         sprint(data, "%s/data", devdir);
1305         fd = open(data, ORDWR);
1306         if(fd < 0)
1307                 fatal(1, "open udp data");
1308         return fd;
1309 }
1310
1311 void
1312 fatal(int syserr, char *fmt, ...)
1313 {
1314         char buf[Maxloglen];
1315         va_list arg;
1316
1317         va_start(arg, fmt);
1318         vseprint(buf, buf+sizeof(buf), fmt, arg);
1319         va_end(arg);
1320         if(syserr)
1321                 syslog(1, blog, "%s: %r", buf);
1322         else
1323                 syslog(1, blog, "%s", buf);
1324         exits(buf);
1325 }
1326
1327 void
1328 warning(int syserr, char *fmt, ...)
1329 {
1330         char buf[Maxloglen];
1331         va_list arg;
1332
1333         va_start(arg, fmt);
1334         vseprint(buf, buf+sizeof(buf), fmt, arg);
1335         va_end(arg);
1336         if(syserr){
1337                 syslog(0, blog, "%s: %r", buf);
1338                 if(debug)
1339                         fprint(2, "%s: %r\n", buf);
1340         } else {
1341                 syslog(0, blog, "%s", buf);
1342                 if(debug)
1343                         fprint(2, "%s\n", buf);
1344         }
1345 }
1346
1347 char*
1348 readsysname(void)
1349 {
1350         static char name[128];
1351         char *p;
1352         int n, fd;
1353
1354         fd = open("/dev/sysname", OREAD);
1355         if(fd >= 0){
1356                 n = read(fd, name, sizeof(name)-1);
1357                 close(fd);
1358                 if(n > 0){
1359                         name[n] = 0;
1360                         return name;
1361                 }
1362         }
1363         p = getenv("sysname");
1364         if(p == nil || *p == 0)
1365                 return "unknown";
1366         return p;
1367 }
1368
1369 extern int
1370 validip(uchar *ip)
1371 {
1372         if(ipcmp(ip, IPnoaddr) == 0)
1373                 return 0;
1374         if(ipcmp(ip, v4prefix) == 0)
1375                 return 0;
1376         return 1;
1377 }
1378
1379 void
1380 longopt(Req *rp, int t, long v)
1381 {
1382         if(rp->p + 6 > rp->max)
1383                 return;
1384         *rp->p++ = t;
1385         *rp->p++ = 4;
1386         hnputl(rp->p, v);
1387         rp->p += 4;
1388
1389         op = seprint(op, oe, "%s(%ld)", optname[t], v);
1390 }
1391
1392 void
1393 addropt(Req *rp, int t, uchar *ip)
1394 {
1395         if(rp->p + 6 > rp->max)
1396                 return;
1397         if (!isv4(ip)) {
1398                 if (debug)
1399                         warning(0, "not a v4 %s server: %I", optname[t], ip);
1400                 return;
1401         }
1402         *rp->p++ = t;
1403         *rp->p++ = 4;
1404         memmove(rp->p, ip+IPv4off, 4);
1405         rp->p += 4;
1406
1407         op = seprint(op, oe, "%s(%I)", optname[t], ip);
1408 }
1409
1410 void
1411 maskopt(Req *rp, int t, uchar *ip)
1412 {
1413         if(rp->p + 6 > rp->max)
1414                 return;
1415         *rp->p++ = t;
1416         *rp->p++ = 4;
1417         memmove(rp->p, ip+IPv4off, 4);
1418         rp->p += 4;
1419
1420         op = seprint(op, oe, "%s(%M)", optname[t], ip);
1421 }
1422
1423 void
1424 addrsopt(Req *rp, int t, uchar **ip, int i)
1425 {
1426         int v4s, n;
1427
1428         if(i <= 0)
1429                 return;
1430         if(rp->p + 2 + 4*i > rp->max)
1431                 return;
1432         v4s = 0;
1433         for(n = i; n-- > 0; )
1434                 if (isv4(ip[n]))
1435                         v4s++;
1436         if (v4s <= 0) {
1437                 if (debug)
1438                         warning(0, "no v4 %s servers", optname[t]);
1439                 return;
1440         }
1441         *rp->p++ = t;
1442         *rp->p++ = 4*v4s;
1443         op = seprint(op, oe, " %s(", optname[t]);
1444         while(i-- > 0){
1445                 if (!isv4(*ip)) {
1446                         op = seprint(op, oe, " skipping %I ", *ip);
1447                         continue;
1448                 }
1449                 v6tov4(rp->p, *ip);
1450                 rp->p += 4;
1451                 op = seprint(op, oe, "%I", *ip);
1452                 ip++;
1453                 if(i > 0)
1454                         op = seprint(op, oe, " ");
1455         }
1456         op = seprint(op, oe, ")");
1457 }
1458
1459 void
1460 p9addrsopt(Req *rp, int t, uchar **ip, int i)
1461 {
1462         char *pkt, *payload;
1463
1464         if(i <= 0 || !v6opts)
1465                 return;
1466         pkt = (char *)rp->p;
1467         *pkt++ = t;                     /* option */
1468         pkt++;                          /* fill in payload length below */
1469         payload = pkt;
1470         *pkt++ = i;                     /* plan 9 address count */
1471         op = seprint(op, oe, " %s(", optname[t]);
1472         while(i-- > 0){
1473                 pkt = seprint(pkt, (char *)rp->max, "%I", *ip);
1474                 if ((uchar *)pkt+1 >= rp->max) {
1475                         op = seprint(op, oe, "<out of mem1>)");
1476                         return;
1477                 }
1478                 pkt++;                  /* leave NUL as terminator */
1479                 op = seprint(op, oe, "%I", *ip);
1480                 ip++;
1481                 if(i > 0)
1482                         op = seprint(op, oe, " ");
1483         }
1484         if ((uchar *)pkt - rp->p > 0377) {
1485                 op = seprint(op, oe, "<out of mem2>)");
1486                 return;
1487         }
1488         op = seprint(op, oe, ")");
1489         rp->p[1] = pkt - payload;       /* payload length */
1490         rp->p = (uchar *)pkt;
1491 }
1492
1493 void
1494 byteopt(Req *rp, int t, uchar v)
1495 {
1496         if(rp->p + 3 > rp->max)
1497                 return;
1498         *rp->p++ = t;
1499         *rp->p++ = 1;
1500         *rp->p++ = v;
1501
1502         op = seprint(op, oe, "%s(%d)", optname[t], v);
1503 }
1504
1505 void
1506 termopt(Req *rp)
1507 {
1508         if(rp->p + 1 > rp->max)
1509                 return;
1510         *rp->p++ = OBend;
1511 }
1512
1513 void
1514 stringopt(Req *rp, int t, char *str)
1515 {
1516         int n;
1517
1518         n = strlen(str);
1519         if(n > 255)
1520                 n = 255;
1521         if(rp->p+n+2 > rp->max)
1522                 return;
1523         *rp->p++ = t;
1524         *rp->p++ = n;
1525         memmove(rp->p, str, n);
1526         rp->p += n;
1527
1528         op = seprint(op, oe, "%s(%s)", optname[t], str);
1529 }
1530
1531 void
1532 vectoropt(Req *rp, int t, uchar *v, int n)
1533 {
1534         int i;
1535
1536         if(n > 255) {
1537                 n = 255;
1538                 op = seprint(op, oe, "vectoropt len %d > 255 ", n);
1539         }
1540         if(rp->p+n+2 > rp->max)
1541                 return;
1542         *rp->p++ = t;
1543         *rp->p++ = n;
1544         memmove(rp->p, v, n);
1545         rp->p += n;
1546
1547         op = seprint(op, oe, "%s(", optname[t]);
1548         if(n > 0)
1549                 op = seprint(op, oe, "%ud", v[0]);
1550         for(i = 1; i < n; i++)
1551                 op = seprint(op, oe, " %ud", v[i]);
1552         op = seprint(op, oe, ")");
1553 }
1554
1555 int
1556 fromhex(int x)
1557 {
1558         if(x >= '0' && x <= '9')
1559                 return x - '0';
1560         return x - 'a';
1561 }
1562
1563 void
1564 hexopt(Req *rp, int t, char *str)
1565 {
1566         int n;
1567
1568         n = strlen(str);
1569         n /= 2;
1570         if(n > 255)
1571                 n = 255;
1572         if(rp->p+n+2 > rp->max)
1573                 return;
1574         *rp->p++ = t;
1575         *rp->p++ = n;
1576         while(n-- > 0){
1577                 *rp->p++ = (fromhex(str[0])<<4)|fromhex(str[1]);
1578                 str += 2;
1579         }
1580
1581         op = seprint(op, oe, "%s(%s)", optname[t], str);
1582 }
1583
1584 void
1585 arpenter(uchar *ip, uchar *ether)
1586 {
1587         int f;
1588         char buf[256];
1589
1590         sprint(buf, "%s/arp", net);
1591         f = open(buf, OWRITE);
1592         if(f < 0){
1593                 syslog(debug, blog, "open %s: %r", buf);
1594                 return;
1595         }
1596         fprint(f, "add ether %I %E", ip, ether);
1597         close(f);
1598 }
1599
1600 char *dhcpmsgname[] =
1601 {
1602         [Discover]      "Discover",
1603         [Offer]         "Offer",
1604         [Request]       "Request",
1605         [Decline]       "Decline",
1606         [Ack]           "Ack",
1607         [Nak]           "Nak",
1608         [Release]       "Release",
1609         [Inform]        "Inform",
1610 };
1611
1612 void
1613 logdhcp(Req *rp)
1614 {
1615         char buf[4096];
1616         char *p, *e;
1617         int i;
1618
1619         p = buf;
1620         e = buf + sizeof(buf);
1621         if(rp->dhcptype > 0 && rp->dhcptype <= Inform)
1622                 p = seprint(p, e, "%s(", dhcpmsgname[rp->dhcptype]);
1623         else
1624                 p = seprint(p, e, "%d(", rp->dhcptype);
1625         p = seprint(p, e, "%I->%I) xid(%ux)flag(%ux)", rp->up->raddr, rp->up->laddr,
1626                 nhgetl(rp->bp->xid), nhgets(rp->bp->flags));
1627         if(rp->bp->htype == 1)
1628                 p = seprint(p, e, "ea(%E)", rp->bp->chaddr);
1629         if(validip(rp->ciaddr))
1630                 p = seprint(p, e, "ci(%I)", rp->ciaddr);
1631         if(validip(rp->giaddr))
1632                 p = seprint(p, e, "gi(%I)", rp->giaddr);
1633         if(validip(rp->ip))
1634                 p = seprint(p, e, "ip(%I)", rp->ip);
1635         if(rp->id != nil)
1636                 p = seprint(p, e, "id(%s)", rp->id);
1637         if(rp->leasetime)
1638                 p = seprint(p, e, "leas(%d)", rp->leasetime);
1639         if(validip(rp->server))
1640                 p = seprint(p, e, "sid(%I)", rp->server);
1641         p = seprint(p, e, "need(");
1642         for(i = 0; i < sizeof(rp->requested); i++)
1643                 if(rp->requested[i] != 0)
1644                         p = seprint(p, e, "%s ", optname[rp->requested[i]]);
1645         p = seprint(p, e, ")");
1646
1647         USED(p);
1648         syslog(0, blog, "%s", buf);
1649 }
1650
1651 void
1652 logdhcpout(Req *rp, char *type)
1653 {
1654         syslog(0, blog, "%s(%I-%I)id(%s)ci(%V)gi(%V)yi(%V)si(%V) %s",
1655                 type, rp->up->laddr, rp->up->raddr, rp->id,
1656                 rp->bp->ciaddr, rp->bp->giaddr, rp->bp->yiaddr, rp->bp->siaddr, optbuf);
1657 }
1658
1659 /*
1660  *  if we get behind, it's useless to try answering since the sender
1661  *  will probably have retransmitted with a differnt sequence number.
1662  *  So dump all but the last message in the queue.
1663  */
1664 void
1665 ding(void*, char *msg)
1666 {
1667         if(strstr(msg, "alarm"))
1668                 noted(NCONT);
1669         else
1670                 noted(NDFLT);
1671 }
1672
1673 int
1674 readlast(int fd, uchar *buf, int len)
1675 {
1676         int lastn, n;
1677
1678         notify(ding);
1679
1680         lastn = 0;
1681         for(;;){
1682                 alarm(20);
1683                 n = read(fd, buf, len);
1684                 alarm(0);
1685                 if(n < 0){
1686                         if(lastn > 0)
1687                                 return lastn;
1688                         break;
1689                 }
1690                 lastn = n;
1691         }
1692         return read(fd, buf, len);
1693 }