1 /*
2 * ipconfig - configure parameters of an ip stack
3 */
4 #include <u.h>
5 #include <libc.h>
6 #include <ip.h>
7 #include <bio.h>
8 #include <ndb.h>
9 #include "../dhcp.h"
10 #include "ipconfig.h"
11
12 #define DEBUG if(debug)warning
13
14 /* possible verbs */
15 enum
16 {
17 /* commands */
18 Vadd,
19 Vremove,
20 Vunbind,
21 Vaddpref6,
22 Vra6,
23 /* media */
24 Vether,
25 Vgbe,
26 Vppp,
27 Vloopback,
28 Vtorus,
29 Vtree,
30 Vpkt,
31 };
32
33 enum
34 {
35 Taddr,
36 Taddrs,
37 Tstr,
38 Tbyte,
39 Tulong,
40 Tvec,
41 };
42
43 typedef struct Option Option;
44 struct Option
45 {
46 char *name;
47 int type;
48 };
49
50 /*
51 * I was too lazy to look up the types for each of these
52 * options. If someone feels like it, please mail me a
53 * corrected array -- presotto
54 */
55 Option option[256] =
56 {
57 [OBmask] { "ipmask", Taddr },
58 [OBtimeoff] { "timeoff", Tulong },
59 [OBrouter] { "ipgw", Taddrs },
60 [OBtimeserver] { "time", Taddrs },
61 [OBnameserver] { "name", Taddrs },
62 [OBdnserver] { "dns", Taddrs },
63 [OBlogserver] { "log", Taddrs },
64 [OBcookieserver] { "cookie", Taddrs },
65 [OBlprserver] { "lpr", Taddrs },
66 [OBimpressserver] { "impress", Taddrs },
67 [OBrlserver] { "rl", Taddrs },
68 [OBhostname] { "sys", Tstr },
69 [OBbflen] { "bflen", Tulong },
70 [OBdumpfile] { "dumpfile", Tstr },
71 [OBdomainname] { "dom", Tstr },
72 [OBswapserver] { "swap", Taddrs },
73 [OBrootpath] { "rootpath", Tstr },
74 [OBextpath] { "extpath", Tstr },
75 [OBipforward] { "ipforward", Taddrs },
76 [OBnonlocal] { "nonlocal", Taddrs },
77 [OBpolicyfilter] { "policyfilter", Taddrs },
78 [OBmaxdatagram] { "maxdatagram", Tulong },
79 [OBttl] { "ttl", Tulong },
80 [OBpathtimeout] { "pathtimeout", Taddrs },
81 [OBpathplateau] { "pathplateau", Taddrs },
82 [OBmtu] { "mtu", Tulong },
83 [OBsubnetslocal] { "subnetslocal", Taddrs },
84 [OBbaddr] { "baddr", Taddrs },
85 [OBdiscovermask] { "discovermask", Taddrs },
86 [OBsupplymask] { "supplymask", Taddrs },
87 [OBdiscoverrouter] { "discoverrouter", Taddrs },
88 [OBrsserver] { "rs", Taddrs },
89 [OBstaticroutes] { "staticroutes", Taddrs },
90 [OBtrailerencap] { "trailerencap", Taddrs },
91 [OBarptimeout] { "arptimeout", Tulong },
92 [OBetherencap] { "etherencap", Taddrs },
93 [OBtcpttl] { "tcpttl", Tulong },
94 [OBtcpka] { "tcpka", Tulong },
95 [OBtcpkag] { "tcpkag", Tulong },
96 [OBnisdomain] { "nisdomain", Tstr },
97 [OBniserver] { "ni", Taddrs },
98 [OBntpserver] { "ntp", Taddrs },
99 [OBnetbiosns] { "netbiosns", Taddrs },
100 [OBnetbiosdds] { "netbiosdds", Taddrs },
101 [OBnetbiostype] { "netbiostype", Taddrs },
102 [OBnetbiosscope] { "netbiosscope", Taddrs },
103 [OBxfontserver] { "xfont", Taddrs },
104 [OBxdispmanager] { "xdispmanager", Taddrs },
105 [OBnisplusdomain] { "nisplusdomain", Tstr },
106 [OBnisplusserver] { "nisplus", Taddrs },
107 [OBhomeagent] { "homeagent", Taddrs },
108 [OBsmtpserver] { "smtp", Taddrs },
109 [OBpop3server] { "pop3", Taddrs },
110 [OBnntpserver] { "nntp", Taddrs },
111 [OBwwwserver] { "www", Taddrs },
112 [OBfingerserver] { "finger", Taddrs },
113 [OBircserver] { "irc", Taddrs },
114 [OBstserver] { "st", Taddrs },
115 [OBstdaserver] { "stdar", Taddrs },
116
117 [ODipaddr] { "ipaddr", Taddr },
118 [ODlease] { "lease", Tulong },
119 [ODoverload] { "overload", Taddr },
120 [ODtype] { "type", Tbyte },
121 [ODserverid] { "serverid", Taddr },
122 [ODparams] { "params", Tvec },
123 [ODmessage] { "message", Tstr },
124 [ODmaxmsg] { "maxmsg", Tulong },
125 [ODrenewaltime] { "renewaltime", Tulong },
126 [ODrebindingtime] { "rebindingtime", Tulong },
127 [ODvendorclass] { "vendorclass", Tvec },
128 [ODclientid] { "clientid", Tvec },
129 [ODtftpserver] { "tftp", Taddr },
130 [ODbootfile] { "bootfile", Tstr },
131 };
132
133 uchar defrequested[] = {
134 OBmask, OBrouter, OBdnserver, OBhostname, OBdomainname, OBntpserver,
135 };
136
137 uchar requested[256];
138 int nrequested;
139
140 int Oflag;
141 int beprimary = -1;
142 Conf conf;
143 int debug;
144 int dodhcp;
145 int dolog;
146 int dondbconfig = 0;
147 int dupl_disc = 1; /* flag: V6 duplicate neighbor discovery */
148 Ctl *firstctl, **ctll;
149 Ipifc *ifc;
150 int ipv6auto = 0;
151 int myifc = -1;
152 char *ndboptions;
153 int nip;
154 int noconfig;
155 int nodhcpwatch;
156 char optmagic[4] = { 0x63, 0x82, 0x53, 0x63 };
157 int plan9 = 1;
158 int sendhostname;
159
160 static char logfile[] = "ipconfig";
161
162 char *verbs[] = {
163 [Vadd] "add",
164 [Vremove] "remove",
165 [Vunbind] "unbind",
166 [Vether] "ether",
167 [Vgbe] "gbe",
168 [Vppp] "ppp",
169 [Vloopback] "loopback",
170 [Vaddpref6] "add6",
171 [Vra6] "ra6",
172 [Vtorus] "torus",
173 [Vtree] "tree",
174 [Vpkt] "pkt",
175 };
176
177 void adddefroute(char*, uchar*);
178 int addoption(char*);
179 void binddevice(void);
180 void bootprequest(void);
181 void controldevice(void);
182 void dhcpquery(int, int);
183 void dhcprecv(void);
184 void dhcpsend(int);
185 int dhcptimer(void);
186 void dhcpwatch(int);
187 void doadd(int);
188 void doremove(void);
189 void dounbind(void);
190 int getndb(void);
191 void getoptions(uchar*);
192 int ip4cfg(void);
193 int ip6cfg(int a);
194 void lookforip(char*);
195 void mkclientid(void);
196 void ndbconfig(void);
197 int nipifcs(char*);
198 int openlisten(void);
199 uchar* optaddaddr(uchar*, int, uchar*);
200 uchar* optaddbyte(uchar*, int, int);
201 uchar* optaddstr(uchar*, int, char*);
202 uchar* optadd(uchar*, int, void*, int);
203 uchar* optaddulong(uchar*, int, ulong);
204 uchar* optaddvec(uchar*, int, uchar*, int);
205 int optgetaddrs(uchar*, int, uchar*, int);
206 int optgetp9addrs(uchar*, int, uchar*, int);
207 int optgetaddr(uchar*, int, uchar*);
208 int optgetbyte(uchar*, int);
209 int optgetstr(uchar*, int, char*, int);
210 uchar* optget(uchar*, int, int*);
211 ulong optgetulong(uchar*, int);
212 int optgetvec(uchar*, int, uchar*, int);
213 char* optgetx(uchar*, uchar);
214 Bootp* parsebootp(uchar*, int);
215 int parseoptions(uchar *p, int n);
216 int parseverb(char*);
217 void pppbinddev(void);
218 void putndb(void);
219 void tweakservers(void);
220 void usage(void);
221 int validip(uchar*);
222 void writendb(char*, int, int);
223
224 void
usage(void)225 usage(void)
226 {
227 fprint(2, "usage: %s [-6dDGnNOpPruX][-b baud][-c ctl]* [-g gw]"
228 "[-h host][-m mtu]\n"
229 "\t[-x mtpt][-o dhcpopt] type dev [verb] [laddr [mask "
230 "[raddr [fs [auth]]]]]\n", argv0);
231 exits("usage");
232 }
233
234 void
warning(char * fmt,...)235 warning(char *fmt, ...)
236 {
237 char buf[1024];
238 va_list arg;
239
240 va_start(arg, fmt);
241 vseprint(buf, buf + sizeof buf, fmt, arg);
242 va_end(arg);
243 if (dolog)
244 syslog(0, logfile, "%s", buf);
245 else
246 fprint(2, "%s: %s\n", argv0, buf);
247 }
248
249 static void
parsenorm(int argc,char ** argv)250 parsenorm(int argc, char **argv)
251 {
252 switch(argc){
253 case 5:
254 if (parseip(conf.auth, argv[4]) == -1)
255 usage();
256 /* fall through */
257 case 4:
258 if (parseip(conf.fs, argv[3]) == -1)
259 usage();
260 /* fall through */
261 case 3:
262 if (parseip(conf.raddr, argv[2]) == -1)
263 usage();
264 /* fall through */
265 case 2:
266 /*
267 * can't test for parseipmask()==-1 cuz 255.255.255.255
268 * looks like that.
269 */
270 if (strcmp(argv[1], "0") != 0)
271 parseipmask(conf.mask, argv[1]);
272 /* fall through */
273 case 1:
274 if (parseip(conf.laddr, argv[0]) == -1)
275 usage();
276 /* fall through */
277 case 0:
278 break;
279 default:
280 usage();
281 }
282 }
283
284 static void
parse6pref(int argc,char ** argv)285 parse6pref(int argc, char **argv)
286 {
287 switch(argc){
288 case 6:
289 conf.preflt = strtoul(argv[5], 0, 10);
290 /* fall through */
291 case 5:
292 conf.validlt = strtoul(argv[4], 0, 10);
293 /* fall through */
294 case 4:
295 conf.autoflag = (atoi(argv[3]) != 0);
296 /* fall through */
297 case 3:
298 conf.onlink = (atoi(argv[2]) != 0);
299 /* fall through */
300 case 2:
301 conf.prefixlen = atoi(argv[1]);
302 /* fall through */
303 case 1:
304 if (parseip(conf.v6pref, argv[0]) == -1)
305 sysfatal("bad address %s", argv[0]);
306 break;
307 }
308 DEBUG("parse6pref: pref %I len %d", conf.v6pref, conf.prefixlen);
309 }
310
311 /* parse router advertisement (keyword, value) pairs */
312 static void
parse6ra(int argc,char ** argv)313 parse6ra(int argc, char **argv)
314 {
315 int i, argsleft;
316 char *kw, *val;
317
318 if (argc % 2 != 0)
319 usage();
320
321 i = 0;
322 for (argsleft = argc; argsleft > 1; argsleft -= 2) {
323 kw = argv[i];
324 val = argv[i+1];
325 if (strcmp(kw, "recvra") == 0)
326 conf.recvra = (atoi(val) != 0);
327 else if (strcmp(kw, "sendra") == 0)
328 conf.sendra = (atoi(val) != 0);
329 else if (strcmp(kw, "mflag") == 0)
330 conf.mflag = (atoi(val) != 0);
331 else if (strcmp(kw, "oflag") == 0)
332 conf.oflag = (atoi(val) != 0);
333 else if (strcmp(kw, "maxraint") == 0)
334 conf.maxraint = atoi(val);
335 else if (strcmp(kw, "minraint") == 0)
336 conf.minraint = atoi(val);
337 else if (strcmp(kw, "linkmtu") == 0)
338 conf.linkmtu = atoi(val);
339 else if (strcmp(kw, "reachtime") == 0)
340 conf.reachtime = atoi(val);
341 else if (strcmp(kw, "rxmitra") == 0)
342 conf.rxmitra = atoi(val);
343 else if (strcmp(kw, "ttl") == 0)
344 conf.ttl = atoi(val);
345 else if (strcmp(kw, "routerlt") == 0)
346 conf.routerlt = atoi(val);
347 else {
348 warning("bad ra6 keyword %s", kw);
349 usage();
350 }
351 i += 2;
352 }
353
354 /* consistency check */
355 if (conf.maxraint < conf.minraint)
356 sysfatal("maxraint %d < minraint %d",
357 conf.maxraint, conf.minraint);
358 }
359
360 static void
init(void)361 init(void)
362 {
363 srand(truerand());
364 fmtinstall('E', eipfmt);
365 fmtinstall('I', eipfmt);
366 fmtinstall('M', eipfmt);
367 fmtinstall('V', eipfmt);
368 nsec(); /* make sure time file is open before forking */
369
370 setnetmtpt(conf.mpoint, sizeof conf.mpoint, nil);
371 conf.cputype = getenv("cputype");
372 if(conf.cputype == nil)
373 conf.cputype = "386";
374
375 ctll = &firstctl;
376 v6paraminit(&conf);
377
378 /* init set of requested dhcp parameters with the default */
379 nrequested = sizeof defrequested;
380 memcpy(requested, defrequested, nrequested);
381 }
382
383 static int
parseargs(int argc,char ** argv)384 parseargs(int argc, char **argv)
385 {
386 char *p;
387 int action, verb;
388
389 /* default to any host name we already have */
390 if(*conf.hostname == 0){
391 p = getenv("sysname");
392 if(p == nil || *p == 0)
393 p = sysname();
394 if(p != nil)
395 strncpy(conf.hostname, p, sizeof conf.hostname-1);
396 }
397
398 /* defaults */
399 conf.type = "ether";
400 conf.dev = "/net/ether0";
401 action = Vadd;
402
403 /* get optional medium and device */
404 if (argc > 0){
405 verb = parseverb(*argv);
406 switch(verb){
407 case Vether:
408 case Vgbe:
409 case Vppp:
410 case Vloopback:
411 case Vtorus:
412 case Vtree:
413 case Vpkt:
414 conf.type = *argv++;
415 argc--;
416 if(argc > 0){
417 conf.dev = *argv++;
418 argc--;
419 } else if(verb == Vppp)
420 conf.dev = "/dev/eia0";
421 break;
422 }
423 }
424
425 /* get optional verb */
426 if (argc > 0){
427 verb = parseverb(*argv);
428 switch(verb){
429 case Vether:
430 case Vgbe:
431 case Vppp:
432 case Vloopback:
433 case Vtorus:
434 case Vtree:
435 case Vpkt:
436 sysfatal("medium %s already specified", conf.type);
437 case Vadd:
438 case Vremove:
439 case Vunbind:
440 case Vaddpref6:
441 case Vra6:
442 argv++;
443 argc--;
444 action = verb;
445 break;
446 }
447 }
448
449 /* get verb-dependent arguments */
450 switch (action) {
451 case Vadd:
452 case Vremove:
453 case Vunbind:
454 parsenorm(argc, argv);
455 break;
456 case Vaddpref6:
457 parse6pref(argc, argv);
458 break;
459 case Vra6:
460 parse6ra(argc, argv);
461 break;
462 }
463 return action;
464 }
465
466 void
main(int argc,char ** argv)467 main(int argc, char **argv)
468 {
469 int retry, action;
470 Ctl *cp;
471
472 init();
473 retry = 0;
474 ARGBEGIN {
475 case '6': /* IPv6 auto config */
476 ipv6auto = 1;
477 break;
478 case 'b':
479 conf.baud = EARGF(usage());
480 break;
481 case 'c':
482 cp = malloc(sizeof *cp);
483 if(cp == nil)
484 sysfatal("%r");
485 *ctll = cp;
486 ctll = &cp->next;
487 cp->next = nil;
488 cp->ctl = EARGF(usage());
489 break;
490 case 'd':
491 dodhcp = 1;
492 break;
493 case 'D':
494 debug = 1;
495 break;
496 case 'g':
497 if (parseip(conf.gaddr, EARGF(usage())) == -1)
498 usage();
499 break;
500 case 'G':
501 plan9 = 0;
502 break;
503 case 'h':
504 snprint(conf.hostname, sizeof conf.hostname, "%s",
505 EARGF(usage()));
506 sendhostname = 1;
507 break;
508 case 'm':
509 conf.mtu = atoi(EARGF(usage()));
510 break;
511 case 'n':
512 noconfig = 1;
513 break;
514 case 'N':
515 dondbconfig = 1;
516 break;
517 case 'o':
518 if(addoption(EARGF(usage())) < 0)
519 usage();
520 break;
521 case 'O':
522 Oflag = 1;
523 break;
524 case 'p':
525 beprimary = 1;
526 break;
527 case 'P':
528 beprimary = 0;
529 break;
530 case 'r':
531 retry = 1;
532 break;
533 case 'u': /* IPv6: duplicate neighbour disc. off */
534 dupl_disc = 0;
535 break;
536 case 'x':
537 setnetmtpt(conf.mpoint, sizeof conf.mpoint, EARGF(usage()));
538 break;
539 case 'X':
540 nodhcpwatch = 1;
541 break;
542 default:
543 usage();
544 } ARGEND;
545 argv0 = "ipconfig"; /* boot invokes us as tcp? */
546
547 action = parseargs(argc, argv);
548 switch(action){
549 case Vadd:
550 doadd(retry);
551 break;
552 case Vremove:
553 doremove();
554 break;
555 case Vunbind:
556 dounbind();
557 break;
558 case Vaddpref6:
559 case Vra6:
560 doipv6(action);
561 break;
562 }
563 exits(0);
564 }
565
566 int
havendb(char * net)567 havendb(char *net)
568 {
569 Dir *d;
570 char buf[128];
571
572 snprint(buf, sizeof buf, "%s/ndb", net);
573 if((d = dirstat(buf)) == nil)
574 return 0;
575 if(d->length == 0){
576 free(d);
577 return 0;
578 }
579 free(d);
580 return 1;
581 }
582
583 void
doadd(int retry)584 doadd(int retry)
585 {
586 int tries, ppp;
587
588 ppp = strcmp(conf.type, "ppp") == 0;
589
590 /* get number of preexisting interfaces */
591 nip = nipifcs(conf.mpoint);
592 if(beprimary == -1 && (nip == 0 || !havendb(conf.mpoint)))
593 beprimary = 1;
594
595 /* get ipifc into name space and condition device for ip */
596 if(!noconfig){
597 lookforip(conf.mpoint);
598 controldevice();
599 binddevice();
600 }
601
602 if (ipv6auto && !ppp) {
603 if (ip6cfg(ipv6auto) < 0)
604 sysfatal("can't automatically start IPv6 on %s",
605 conf.dev);
606 // return;
607 } else if (validip(conf.laddr) && !isv4(conf.laddr)) {
608 if (ip6cfg(0) < 0)
609 sysfatal("can't start IPv6 on %s, address %I",
610 conf.dev, conf.laddr);
611 // return;
612 }
613
614 if(!validip(conf.laddr) && !ppp)
615 if(dondbconfig)
616 ndbconfig();
617 else
618 dodhcp = 1;
619
620 /* run dhcp if we need something */
621 if(dodhcp){
622 mkclientid();
623 for(tries = 0; tries < 30; tries++){
624 dhcpquery(!noconfig, Sselecting);
625 if(conf.state == Sbound)
626 break;
627 sleep(1000);
628 }
629 }
630
631 if(!validip(conf.laddr))
632 if(retry && dodhcp && !noconfig){
633 warning("couldn't determine ip address, retrying");
634 dhcpwatch(1);
635 return;
636 } else
637 sysfatal("no success with DHCP");
638
639 if(!noconfig)
640 if(ip4cfg() < 0)
641 sysfatal("can't start ip");
642 else if(dodhcp && conf.lease != Lforever)
643 dhcpwatch(0);
644
645 /* leave everything we've learned somewhere other procs can find it */
646 if(beprimary == 1){
647 putndb();
648 tweakservers();
649 }
650 }
651
652 void
doremove(void)653 doremove(void)
654 {
655 char file[128];
656 int cfd;
657 Ipifc *nifc;
658 Iplifc *lifc;
659
660 if(!validip(conf.laddr))
661 sysfatal("remove requires an address");
662 ifc = readipifc(conf.mpoint, ifc, -1);
663 for(nifc = ifc; nifc != nil; nifc = nifc->next){
664 if(strcmp(nifc->dev, conf.dev) != 0)
665 continue;
666 for(lifc = nifc->lifc; lifc != nil; lifc = lifc->next){
667 if(ipcmp(conf.laddr, lifc->ip) != 0)
668 continue;
669 if (validip(conf.mask) &&
670 ipcmp(conf.mask, lifc->mask) != 0)
671 continue;
672 if (validip(conf.raddr) &&
673 ipcmp(conf.raddr, lifc->net) != 0)
674 continue;
675
676 snprint(file, sizeof file, "%s/ipifc/%d/ctl",
677 conf.mpoint, nifc->index);
678 cfd = open(file, ORDWR);
679 if(cfd < 0){
680 warning("can't open %s: %r", conf.mpoint);
681 continue;
682 }
683 if(fprint(cfd, "remove %I %M", lifc->ip, lifc->mask) < 0)
684 warning("can't remove %I %M from %s: %r",
685 lifc->ip, lifc->mask, file);
686 }
687 }
688 }
689
690 void
dounbind(void)691 dounbind(void)
692 {
693 Ipifc *nifc;
694 char file[128];
695 int cfd;
696
697 ifc = readipifc(conf.mpoint, ifc, -1);
698 for(nifc = ifc; nifc != nil; nifc = nifc->next){
699 if(strcmp(nifc->dev, conf.dev) == 0){
700 snprint(file, sizeof file, "%s/ipifc/%d/ctl",
701 conf.mpoint, nifc->index);
702 cfd = open(file, ORDWR);
703 if(cfd < 0){
704 warning("can't open %s: %r", conf.mpoint);
705 break;
706 }
707 if(fprint(cfd, "unbind") < 0)
708 warning("can't unbind from %s: %r", file);
709 break;
710 }
711 }
712 }
713
714 /* set the default route */
715 void
adddefroute(char * mpoint,uchar * gaddr)716 adddefroute(char *mpoint, uchar *gaddr)
717 {
718 char buf[256];
719 int cfd;
720
721 sprint(buf, "%s/iproute", mpoint);
722 cfd = open(buf, ORDWR);
723 if(cfd < 0)
724 return;
725
726 if(isv4(gaddr))
727 fprint(cfd, "add 0 0 %I", gaddr);
728 else
729 fprint(cfd, "add :: /0 %I", gaddr);
730 close(cfd);
731 }
732
733 /* create a client id */
734 void
mkclientid(void)735 mkclientid(void)
736 {
737 if(strcmp(conf.type, "ether") == 0 || strcmp(conf.type, "gbe") == 0)
738 if(myetheraddr(conf.hwa, conf.dev) == 0){
739 conf.hwalen = 6;
740 conf.hwatype = 1;
741 conf.cid[0] = conf.hwatype;
742 memmove(&conf.cid[1], conf.hwa, conf.hwalen);
743 conf.cidlen = conf.hwalen+1;
744 } else {
745 conf.hwatype = -1;
746 snprint((char*)conf.cid, sizeof conf.cid,
747 "plan9_%ld.%d", lrand(), getpid());
748 conf.cidlen = strlen((char*)conf.cid);
749 }
750 }
751
752 /* bind ip into the namespace */
753 void
lookforip(char * net)754 lookforip(char *net)
755 {
756 char proto[64];
757
758 snprint(proto, sizeof proto, "%s/ipifc", net);
759 if(access(proto, 0) == 0)
760 return;
761 sysfatal("no ip stack bound onto %s", net);
762 }
763
764 /* send some ctls to a device */
765 void
controldevice(void)766 controldevice(void)
767 {
768 char ctlfile[256];
769 int fd;
770 Ctl *cp;
771
772 if (firstctl == nil ||
773 strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0)
774 return;
775
776 snprint(ctlfile, sizeof ctlfile, "%s/clone", conf.dev);
777 fd = open(ctlfile, ORDWR);
778 if(fd < 0)
779 sysfatal("can't open %s", ctlfile);
780
781 for(cp = firstctl; cp != nil; cp = cp->next){
782 if(write(fd, cp->ctl, strlen(cp->ctl)) < 0)
783 sysfatal("ctl message %s: %r", cp->ctl);
784 seek(fd, 0, 0);
785 }
786 // close(fd); /* or does it need to be left hanging? */
787 }
788
789 /* bind an ip stack to a device, leave the control channel open */
790 void
binddevice(void)791 binddevice(void)
792 {
793 char buf[256];
794
795 if(strcmp(conf.type, "ppp") == 0)
796 pppbinddev();
797 else if(myifc < 0){
798 /* get a new ip interface */
799 snprint(buf, sizeof buf, "%s/ipifc/clone", conf.mpoint);
800 conf.cfd = open(buf, ORDWR);
801 if(conf.cfd < 0)
802 sysfatal("opening %s/ipifc/clone: %r", conf.mpoint);
803
804 /* specify medium as ethernet, bind the interface to it */
805 if(fprint(conf.cfd, "bind %s %s", conf.type, conf.dev) < 0)
806 sysfatal("%s: bind %s %s: %r", buf, conf.type, conf.dev);
807 } else {
808 /* open the old interface */
809 snprint(buf, sizeof buf, "%s/ipifc/%d/ctl", conf.mpoint, myifc);
810 conf.cfd = open(buf, ORDWR);
811 if(conf.cfd < 0)
812 sysfatal("open %s: %r", buf);
813 }
814
815 }
816
817 /* add a logical interface to the ip stack */
818 int
ip4cfg(void)819 ip4cfg(void)
820 {
821 char buf[256];
822 int n;
823
824 if(!validip(conf.laddr))
825 return -1;
826
827 n = sprint(buf, "add");
828 n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
829
830 if(!validip(conf.mask))
831 ipmove(conf.mask, defmask(conf.laddr));
832 n += snprint(buf+n, sizeof buf-n, " %I", conf.mask);
833
834 if(validip(conf.raddr)){
835 n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
836 if(conf.mtu != 0)
837 n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
838 }
839
840 if(write(conf.cfd, buf, n) < 0){
841 warning("write(%s): %r", buf);
842 return -1;
843 }
844
845 if(beprimary==1 && validip(conf.gaddr))
846 adddefroute(conf.mpoint, conf.gaddr);
847
848 return 0;
849 }
850
851 /* remove a logical interface to the ip stack */
852 void
ipunconfig(void)853 ipunconfig(void)
854 {
855 char buf[256];
856 int n;
857
858 if(!validip(conf.laddr))
859 return;
860 DEBUG("couldn't renew IP lease, releasing %I", conf.laddr);
861 n = sprint(buf, "remove");
862 n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
863
864 if(!validip(conf.mask))
865 ipmove(conf.mask, defmask(conf.laddr));
866 n += snprint(buf+n, sizeof buf-n, " %I", conf.mask);
867
868 write(conf.cfd, buf, n);
869
870 ipmove(conf.laddr, IPnoaddr);
871 ipmove(conf.raddr, IPnoaddr);
872 ipmove(conf.mask, IPnoaddr);
873
874 /* forget configuration info */
875 if(beprimary==1)
876 writendb("", 0, 0);
877 }
878
879 void
ding(void *,char * msg)880 ding(void*, char *msg)
881 {
882 if(strstr(msg, "alarm"))
883 noted(NCONT);
884 noted(NDFLT);
885 }
886
887 void
dhcpquery(int needconfig,int startstate)888 dhcpquery(int needconfig, int startstate)
889 {
890 if(needconfig)
891 fprint(conf.cfd, "add %I %I", IPnoaddr, IPnoaddr);
892
893 conf.fd = openlisten();
894 if(conf.fd < 0){
895 conf.state = Sinit;
896 return;
897 }
898 notify(ding);
899
900 /* try dhcp for 10 seconds */
901 conf.xid = lrand();
902 conf.starttime = time(0);
903 conf.state = startstate;
904 switch(startstate){
905 case Sselecting:
906 conf.offered = 0;
907 dhcpsend(Discover);
908 break;
909 case Srenewing:
910 dhcpsend(Request);
911 break;
912 default:
913 sysfatal("internal error 0");
914 }
915 conf.resend = 0;
916 conf.timeout = time(0) + 4;
917
918 while(conf.state != Sbound){
919 dhcprecv();
920 if(dhcptimer() < 0)
921 break;
922 if(time(0) - conf.starttime > 10)
923 break;
924 }
925 close(conf.fd);
926
927 if(needconfig)
928 fprint(conf.cfd, "remove %I %I", IPnoaddr, IPnoaddr);
929
930 }
931
932 enum {
933 /*
934 * was an hour, needs to be less for the ARM/GS1 until the timer
935 * code has been cleaned up (pb).
936 */
937 Maxsleep = 450,
938 };
939
940 void
dhcpwatch(int needconfig)941 dhcpwatch(int needconfig)
942 {
943 int secs, s;
944 ulong t;
945
946 if(nodhcpwatch)
947 return;
948
949 switch(rfork(RFPROC|RFFDG|RFNOWAIT|RFNOTEG)){
950 default:
951 return;
952 case 0:
953 break;
954 }
955
956 dolog = 1; /* log, don't print */
957 procsetname("dhcpwatch");
958 /* keep trying to renew the lease */
959 for(;;){
960 if(conf.lease == 0)
961 secs = 5;
962 else
963 secs = conf.lease >> 1;
964
965 /* avoid overflows */
966 for(s = secs; s > 0; s -= t){
967 if(s > Maxsleep)
968 t = Maxsleep;
969 else
970 t = s;
971 sleep(t*1000);
972 }
973
974 if(conf.lease > 0){
975 /*
976 * during boot, the starttime can be bogus so avoid
977 * spurious ipunconfig's
978 */
979 t = time(0) - conf.starttime;
980 if(t > (3*secs)/2)
981 t = secs;
982 if(t >= conf.lease){
983 conf.lease = 0;
984 if(!noconfig){
985 ipunconfig();
986 needconfig = 1;
987 }
988 } else
989 conf.lease -= t;
990 }
991 dhcpquery(needconfig, needconfig? Sselecting: Srenewing);
992
993 if(needconfig && conf.state == Sbound){
994 if(ip4cfg() < 0)
995 sysfatal("can't start ip: %r");
996 needconfig = 0;
997 /*
998 * leave everything we've learned somewhere that
999 * other procs can find it.
1000 */
1001 if(beprimary==1){
1002 putndb();
1003 tweakservers();
1004 }
1005 }
1006 }
1007 }
1008
1009 int
dhcptimer(void)1010 dhcptimer(void)
1011 {
1012 ulong now;
1013
1014 now = time(0);
1015 if(now < conf.timeout)
1016 return 0;
1017
1018 switch(conf.state) {
1019 default:
1020 sysfatal("dhcptimer: unknown state %d", conf.state);
1021 case Sinit:
1022 case Sbound:
1023 break;
1024 case Sselecting:
1025 case Srequesting:
1026 case Srebinding:
1027 dhcpsend(conf.state == Sselecting? Discover: Request);
1028 conf.timeout = now + 4;
1029 if(++conf.resend > 5) {
1030 conf.state = Sinit;
1031 return -1;
1032 }
1033 break;
1034 case Srenewing:
1035 dhcpsend(Request);
1036 conf.timeout = now + 1;
1037 if(++conf.resend > 3) {
1038 conf.state = Srebinding;
1039 conf.resend = 0;
1040 }
1041 break;
1042 }
1043 return 0;
1044 }
1045
1046 void
dhcpsend(int type)1047 dhcpsend(int type)
1048 {
1049 Bootp bp;
1050 uchar *p;
1051 int n;
1052 uchar vendor[64];
1053 Udphdr *up = (Udphdr*)bp.udphdr;
1054
1055 memset(&bp, 0, sizeof bp);
1056
1057 hnputs(up->rport, 67);
1058 bp.op = Bootrequest;
1059 hnputl(bp.xid, conf.xid);
1060 hnputs(bp.secs, time(0)-conf.starttime);
1061 hnputs(bp.flags, 0);
1062 memmove(bp.optmagic, optmagic, 4);
1063 if(conf.hwatype >= 0 && conf.hwalen < sizeof bp.chaddr){
1064 memmove(bp.chaddr, conf.hwa, conf.hwalen);
1065 bp.hlen = conf.hwalen;
1066 bp.htype = conf.hwatype;
1067 }
1068 p = bp.optdata;
1069 p = optaddbyte(p, ODtype, type);
1070 p = optadd(p, ODclientid, conf.cid, conf.cidlen);
1071 switch(type) {
1072 default:
1073 sysfatal("dhcpsend: unknown message type: %d", type);
1074 case Discover:
1075 ipmove(up->raddr, IPv4bcast); /* broadcast */
1076 if(*conf.hostname && sendhostname)
1077 p = optaddstr(p, OBhostname, conf.hostname);
1078 if(plan9){
1079 n = snprint((char*)vendor, sizeof vendor,
1080 "plan9_%s", conf.cputype);
1081 p = optaddvec(p, ODvendorclass, vendor, n);
1082 }
1083 p = optaddvec(p, ODparams, requested, nrequested);
1084 if(validip(conf.laddr))
1085 p = optaddaddr(p, ODipaddr, conf.laddr);
1086 break;
1087 case Request:
1088 switch(conf.state){
1089 case Srenewing:
1090 ipmove(up->raddr, conf.server);
1091 v6tov4(bp.ciaddr, conf.laddr);
1092 break;
1093 case Srebinding:
1094 ipmove(up->raddr, IPv4bcast); /* broadcast */
1095 v6tov4(bp.ciaddr, conf.laddr);
1096 break;
1097 case Srequesting:
1098 ipmove(up->raddr, IPv4bcast); /* broadcast */
1099 p = optaddaddr(p, ODipaddr, conf.laddr);
1100 p = optaddaddr(p, ODserverid, conf.server);
1101 break;
1102 }
1103 p = optaddulong(p, ODlease, conf.offered);
1104 if(plan9){
1105 n = snprint((char*)vendor, sizeof vendor,
1106 "plan9_%s", conf.cputype);
1107 p = optaddvec(p, ODvendorclass, vendor, n);
1108 }
1109 p = optaddvec(p, ODparams, requested, nrequested);
1110 if(*conf.hostname && sendhostname)
1111 p = optaddstr(p, OBhostname, conf.hostname);
1112 break;
1113 case Release:
1114 ipmove(up->raddr, conf.server);
1115 v6tov4(bp.ciaddr, conf.laddr);
1116 p = optaddaddr(p, ODipaddr, conf.laddr);
1117 p = optaddaddr(p, ODserverid, conf.server);
1118 break;
1119 }
1120
1121 *p++ = OBend;
1122
1123 n = p - (uchar*)&bp;
1124 USED(n);
1125
1126 /*
1127 * We use a maximum size DHCP packet to survive the
1128 * All_Aboard NAT package from Internet Share. It
1129 * always replies to DHCP requests with a packet of the
1130 * same size, so if the request is too short the reply
1131 * is truncated.
1132 */
1133 if(write(conf.fd, &bp, sizeof bp) != sizeof bp)
1134 warning("dhcpsend: write failed: %r");
1135 }
1136
1137 void
dhcprecv(void)1138 dhcprecv(void)
1139 {
1140 int i, n, type;
1141 ulong lease;
1142 char err[ERRMAX];
1143 uchar buf[8000], vopts[256], taddr[IPaddrlen];
1144 Bootp *bp;
1145
1146 memset(buf, 0, sizeof buf);
1147 alarm(1000);
1148 n = read(conf.fd, buf, sizeof buf);
1149 alarm(0);
1150
1151 if(n < 0){
1152 rerrstr(err, sizeof err);
1153 if(strstr(err, "interrupt") == nil)
1154 warning("dhcprecv: bad read: %s", err);
1155 else
1156 DEBUG("dhcprecv: read timed out");
1157 return;
1158 }
1159 if(n == 0){
1160 warning("dhcprecv: zero-length packet read");
1161 return;
1162 }
1163
1164 bp = parsebootp(buf, n);
1165 if(bp == 0) {
1166 DEBUG("parsebootp failed: dropping packet");
1167 return;
1168 }
1169
1170 type = optgetbyte(bp->optdata, ODtype);
1171 switch(type) {
1172 default:
1173 warning("dhcprecv: unknown type: %d", type);
1174 break;
1175 case Offer:
1176 DEBUG("got offer from %V ", bp->siaddr);
1177 if(conf.state != Sselecting){
1178 // DEBUG("");
1179 break;
1180 }
1181 lease = optgetulong(bp->optdata, ODlease);
1182 if(lease == 0){
1183 /*
1184 * The All_Aboard NAT package from Internet Share
1185 * doesn't give a lease time, so we have to assume one.
1186 */
1187 warning("Offer with %lud lease, using %d", lease, MinLease);
1188 lease = MinLease;
1189 }
1190 DEBUG("lease=%lud ", lease);
1191 if(!optgetaddr(bp->optdata, ODserverid, conf.server)) {
1192 warning("Offer from server with invalid serverid");
1193 break;
1194 }
1195
1196 v4tov6(conf.laddr, bp->yiaddr);
1197 memmove(conf.sname, bp->sname, sizeof conf.sname);
1198 conf.sname[sizeof conf.sname-1] = 0;
1199 DEBUG("server=%I sname=%s", conf.server, conf.sname);
1200 conf.offered = lease;
1201 conf.state = Srequesting;
1202 dhcpsend(Request);
1203 conf.resend = 0;
1204 conf.timeout = time(0) + 4;
1205 break;
1206 case Ack:
1207 DEBUG("got ack from %V ", bp->siaddr);
1208 if (conf.state != Srequesting && conf.state != Srenewing &&
1209 conf.state != Srebinding)
1210 break;
1211
1212 /* ignore a bad lease */
1213 lease = optgetulong(bp->optdata, ODlease);
1214 if(lease == 0){
1215 /*
1216 * The All_Aboard NAT package from Internet Share
1217 * doesn't give a lease time, so we have to assume one.
1218 */
1219 warning("Ack with %lud lease, using %d", lease, MinLease);
1220 lease = MinLease;
1221 }
1222 DEBUG("lease=%lud ", lease);
1223
1224 /* address and mask */
1225 if(!validip(conf.laddr) || !Oflag)
1226 v4tov6(conf.laddr, bp->yiaddr);
1227 if(!validip(conf.mask) || !Oflag){
1228 if(!optgetaddr(bp->optdata, OBmask, conf.mask))
1229 ipmove(conf.mask, IPnoaddr);
1230 }
1231 DEBUG("ipaddr=%I ipmask=%M ", conf.laddr, conf.mask);
1232
1233 /*
1234 * get a router address either from the router option
1235 * or from the router that forwarded the dhcp packet
1236 */
1237 if(validip(conf.gaddr) && Oflag) {
1238 DEBUG("ipgw=%I ", conf.gaddr);
1239 } else if(optgetaddr(bp->optdata, OBrouter, conf.gaddr)){
1240 DEBUG("ipgw=%I ", conf.gaddr);
1241 } else if(memcmp(bp->giaddr, IPnoaddr+IPv4off, IPv4addrlen)!=0){
1242 v4tov6(conf.gaddr, bp->giaddr);
1243 DEBUG("giaddr=%I ", conf.gaddr);
1244 }
1245
1246 /* get dns servers */
1247 memset(conf.dns, 0, sizeof conf.dns);
1248 n = optgetaddrs(bp->optdata, OBdnserver, conf.dns,
1249 sizeof conf.dns/IPaddrlen);
1250 for(i = 0; i < n; i++)
1251 DEBUG("dns=%I ", conf.dns + i*IPaddrlen);
1252
1253 /* get ntp servers */
1254 memset(conf.ntp, 0, sizeof conf.ntp);
1255 n = optgetaddrs(bp->optdata, OBntpserver, conf.ntp,
1256 sizeof conf.ntp/IPaddrlen);
1257 for(i = 0; i < n; i++)
1258 DEBUG("ntp=%I ", conf.ntp + i*IPaddrlen);
1259
1260 /* get names */
1261 optgetstr(bp->optdata, OBhostname,
1262 conf.hostname, sizeof conf.hostname);
1263 optgetstr(bp->optdata, OBdomainname,
1264 conf.domainname, sizeof conf.domainname);
1265
1266 /* get anything else we asked for */
1267 getoptions(bp->optdata);
1268
1269 /* get plan9-specific options */
1270 n = optgetvec(bp->optdata, OBvendorinfo, vopts, sizeof vopts-1);
1271 if(n > 0 && parseoptions(vopts, n) == 0){
1272 if(validip(conf.fs) && Oflag)
1273 n = 1;
1274 else {
1275 n = optgetp9addrs(vopts, OP9fs, conf.fs, 2);
1276 if (n == 0)
1277 n = optgetaddrs(vopts, OP9fsv4,
1278 conf.fs, 2);
1279 }
1280 for(i = 0; i < n; i++)
1281 DEBUG("fs=%I ", conf.fs + i*IPaddrlen);
1282
1283 if(validip(conf.auth) && Oflag)
1284 n = 1;
1285 else {
1286 n = optgetp9addrs(vopts, OP9auth, conf.auth, 2);
1287 if (n == 0)
1288 n = optgetaddrs(vopts, OP9authv4,
1289 conf.auth, 2);
1290 }
1291 for(i = 0; i < n; i++)
1292 DEBUG("auth=%I ", conf.auth + i*IPaddrlen);
1293
1294 n = optgetp9addrs(vopts, OP9ipaddr, taddr, 1);
1295 if (n > 0)
1296 memmove(conf.laddr, taddr, IPaddrlen);
1297 n = optgetp9addrs(vopts, OP9ipmask, taddr, 1);
1298 if (n > 0)
1299 memmove(conf.mask, taddr, IPaddrlen);
1300 n = optgetp9addrs(vopts, OP9ipgw, taddr, 1);
1301 if (n > 0)
1302 memmove(conf.gaddr, taddr, IPaddrlen);
1303 DEBUG("new ipaddr=%I new ipmask=%M new ipgw=%I",
1304 conf.laddr, conf.mask, conf.gaddr);
1305 }
1306 conf.lease = lease;
1307 conf.state = Sbound;
1308 DEBUG("server=%I sname=%s", conf.server, conf.sname);
1309 break;
1310 case Nak:
1311 conf.state = Sinit;
1312 warning("recved dhcpnak on %s", conf.mpoint);
1313 break;
1314 }
1315 }
1316
1317 /* return pseudo-random integer in range low...(hi-1) */
1318 ulong
randint(ulong low,ulong hi)1319 randint(ulong low, ulong hi)
1320 {
1321 if (hi < low)
1322 return low;
1323 return low + nrand(hi - low);
1324 }
1325
1326 long
jitter(void)1327 jitter(void) /* compute small pseudo-random delay in ms */
1328 {
1329 return randint(0, 10*1000);
1330 }
1331
1332 int
openlisten(void)1333 openlisten(void)
1334 {
1335 int n, fd, cfd;
1336 char data[128], devdir[40];
1337
1338 if (validip(conf.laddr) &&
1339 (conf.state == Srenewing || conf.state == Srebinding))
1340 sprint(data, "%s/udp!%I!68", conf.mpoint, conf.laddr);
1341 else
1342 sprint(data, "%s/udp!*!68", conf.mpoint);
1343 for (n = 0; (cfd = announce(data, devdir)) < 0; n++) {
1344 if(!noconfig)
1345 sysfatal("can't announce for dhcp: %r");
1346
1347 /* might be another client - wait and try again */
1348 warning("can't announce %s: %r", data);
1349 sleep(jitter());
1350 if(n > 10)
1351 return -1;
1352 }
1353
1354 if(fprint(cfd, "headers") < 0)
1355 sysfatal("can't set header mode: %r");
1356
1357 sprint(data, "%s/data", devdir);
1358 fd = open(data, ORDWR);
1359 if(fd < 0)
1360 sysfatal("open %s: %r", data);
1361 close(cfd);
1362 return fd;
1363 }
1364
1365 uchar*
optadd(uchar * p,int op,void * d,int n)1366 optadd(uchar *p, int op, void *d, int n)
1367 {
1368 p[0] = op;
1369 p[1] = n;
1370 memmove(p+2, d, n);
1371 return p+n+2;
1372 }
1373
1374 uchar*
optaddbyte(uchar * p,int op,int b)1375 optaddbyte(uchar *p, int op, int b)
1376 {
1377 p[0] = op;
1378 p[1] = 1;
1379 p[2] = b;
1380 return p+3;
1381 }
1382
1383 uchar*
optaddulong(uchar * p,int op,ulong x)1384 optaddulong(uchar *p, int op, ulong x)
1385 {
1386 p[0] = op;
1387 p[1] = 4;
1388 hnputl(p+2, x);
1389 return p+6;
1390 }
1391
1392 uchar *
optaddaddr(uchar * p,int op,uchar * ip)1393 optaddaddr(uchar *p, int op, uchar *ip)
1394 {
1395 p[0] = op;
1396 p[1] = 4;
1397 v6tov4(p+2, ip);
1398 return p+6;
1399 }
1400
1401 /* add dhcp option op with value v of length n to dhcp option array p */
1402 uchar *
optaddvec(uchar * p,int op,uchar * v,int n)1403 optaddvec(uchar *p, int op, uchar *v, int n)
1404 {
1405 p[0] = op;
1406 p[1] = n;
1407 memmove(p+2, v, n);
1408 return p+2+n;
1409 }
1410
1411 uchar *
optaddstr(uchar * p,int op,char * v)1412 optaddstr(uchar *p, int op, char *v)
1413 {
1414 int n;
1415
1416 n = strlen(v)+1; /* microsoft leaves on the NUL, so we do too */
1417 p[0] = op;
1418 p[1] = n;
1419 memmove(p+2, v, n);
1420 return p+2+n;
1421 }
1422
1423 /*
1424 * parse p, looking for option `op'. if non-nil, np points to minimum length.
1425 * return nil if option is too small, else ptr to opt, and
1426 * store actual length via np if non-nil.
1427 */
1428 uchar*
optget(uchar * p,int op,int * np)1429 optget(uchar *p, int op, int *np)
1430 {
1431 int len, code;
1432
1433 while ((code = *p++) != OBend) {
1434 if(code == OBpad)
1435 continue;
1436 len = *p++;
1437 if(code != op) {
1438 p += len;
1439 continue;
1440 }
1441 if(np != nil){
1442 if(*np > len) {
1443 return 0;
1444 }
1445 *np = len;
1446 }
1447 return p;
1448 }
1449 return 0;
1450 }
1451
1452 int
optgetbyte(uchar * p,int op)1453 optgetbyte(uchar *p, int op)
1454 {
1455 int len;
1456
1457 len = 1;
1458 p = optget(p, op, &len);
1459 if(p == nil)
1460 return 0;
1461 return *p;
1462 }
1463
1464 ulong
optgetulong(uchar * p,int op)1465 optgetulong(uchar *p, int op)
1466 {
1467 int len;
1468
1469 len = 4;
1470 p = optget(p, op, &len);
1471 if(p == nil)
1472 return 0;
1473 return nhgetl(p);
1474 }
1475
1476 int
optgetaddr(uchar * p,int op,uchar * ip)1477 optgetaddr(uchar *p, int op, uchar *ip)
1478 {
1479 int len;
1480
1481 len = 4;
1482 p = optget(p, op, &len);
1483 if(p == nil)
1484 return 0;
1485 v4tov6(ip, p);
1486 return 1;
1487 }
1488
1489 /* expect at most n addresses; ip[] only has room for that many */
1490 int
optgetaddrs(uchar * p,int op,uchar * ip,int n)1491 optgetaddrs(uchar *p, int op, uchar *ip, int n)
1492 {
1493 int len, i;
1494
1495 len = 4;
1496 p = optget(p, op, &len);
1497 if(p == nil)
1498 return 0;
1499 len /= IPv4addrlen;
1500 if(len > n)
1501 len = n;
1502 for(i = 0; i < len; i++)
1503 v4tov6(&ip[i*IPaddrlen], &p[i*IPv4addrlen]);
1504 return i;
1505 }
1506
1507 /* expect at most n addresses; ip[] only has room for that many */
1508 int
optgetp9addrs(uchar * ap,int op,uchar * ip,int n)1509 optgetp9addrs(uchar *ap, int op, uchar *ip, int n)
1510 {
1511 int len, i, slen, addrs;
1512 char *p;
1513
1514 len = 1; /* minimum bytes needed */
1515 p = (char *)optget(ap, op, &len);
1516 if(p == nil)
1517 return 0;
1518 addrs = *p++; /* first byte is address count */
1519 for (i = 0; i < n && i < addrs && len > 0; i++) {
1520 slen = strlen(p) + 1;
1521 if (parseip(&ip[i*IPaddrlen], p) == -1)
1522 fprint(2, "%s: bad address %s\n", argv0, p);
1523 DEBUG("got plan 9 option %d addr %I (%s)",
1524 op, &ip[i*IPaddrlen], p);
1525 p += slen;
1526 len -= slen;
1527 }
1528 return addrs;
1529 }
1530
1531 int
optgetvec(uchar * p,int op,uchar * v,int n)1532 optgetvec(uchar *p, int op, uchar *v, int n)
1533 {
1534 int len;
1535
1536 len = 1;
1537 p = optget(p, op, &len);
1538 if(p == nil)
1539 return 0;
1540 if(len > n)
1541 len = n;
1542 memmove(v, p, len);
1543 return len;
1544 }
1545
1546 int
optgetstr(uchar * p,int op,char * s,int n)1547 optgetstr(uchar *p, int op, char *s, int n)
1548 {
1549 int len;
1550
1551 len = 1;
1552 p = optget(p, op, &len);
1553 if(p == nil)
1554 return 0;
1555 if(len >= n)
1556 len = n-1;
1557 memmove(s, p, len);
1558 s[len] = 0;
1559 return len;
1560 }
1561
1562 /*
1563 * sanity check options area
1564 * - options don't overflow packet
1565 * - options end with an OBend
1566 */
1567 int
parseoptions(uchar * p,int n)1568 parseoptions(uchar *p, int n)
1569 {
1570 int code, len, nin = n;
1571
1572 while (n > 0) {
1573 code = *p++;
1574 n--;
1575 if(code == OBend)
1576 return 0;
1577 if(code == OBpad)
1578 continue;
1579 if(n == 0) {
1580 warning("parseoptions: bad option: 0x%ux: truncated: "
1581 "opt length = %d", code, nin);
1582 return -1;
1583 }
1584
1585 len = *p++;
1586 n--;
1587 DEBUG("parseoptions: %s(%d) len %d, bytes left %d",
1588 option[code].name, code, len, n);
1589 if(len > n) {
1590 warning("parseoptions: bad option: 0x%ux: %d > %d: "
1591 "opt length = %d", code, len, n, nin);
1592 return -1;
1593 }
1594 p += len;
1595 n -= len;
1596 }
1597
1598 /* make sure packet ends with an OBend after all the optget code */
1599 *p = OBend;
1600 return 0;
1601 }
1602
1603 /*
1604 * sanity check received packet:
1605 * - magic is dhcp magic
1606 * - options don't overflow packet
1607 */
1608 Bootp *
parsebootp(uchar * p,int n)1609 parsebootp(uchar *p, int n)
1610 {
1611 Bootp *bp;
1612
1613 bp = (Bootp*)p;
1614 if(n < bp->optmagic - p) {
1615 warning("parsebootp: short bootp packet; with options, "
1616 "need %d bytes, got %d", bp->optmagic - p, n);
1617 return nil;
1618 }
1619
1620 if(conf.xid != nhgetl(bp->xid)) /* not meant for us */
1621 return nil;
1622
1623 if(bp->op != Bootreply) {
1624 warning("parsebootp: bad op %d", bp->op);
1625 return nil;
1626 }
1627
1628 n -= bp->optmagic - p;
1629 p = bp->optmagic;
1630
1631 if(n < 4) {
1632 warning("parsebootp: no option data");
1633 return nil;
1634 }
1635 if(memcmp(optmagic, p, 4) != 0) {
1636 warning("parsebootp: bad opt magic %ux %ux %ux %ux",
1637 p[0], p[1], p[2], p[3]);
1638 return nil;
1639 }
1640 p += 4;
1641 n -= 4;
1642 DEBUG("parsebootp: new packet");
1643 if(parseoptions(p, n) < 0)
1644 return nil;
1645 return bp;
1646 }
1647
1648 /* write out an ndb entry */
1649 void
writendb(char * s,int n,int append)1650 writendb(char *s, int n, int append)
1651 {
1652 char file[64];
1653 int fd;
1654
1655 snprint(file, sizeof file, "%s/ndb", conf.mpoint);
1656 if(append){
1657 fd = open(file, OWRITE);
1658 seek(fd, 0, 2);
1659 } else
1660 fd = open(file, OWRITE|OTRUNC);
1661 write(fd, s, n);
1662 close(fd);
1663 }
1664
1665 /* put server addresses into the ndb entry */
1666 char*
putaddrs(char * p,char * e,char * attr,uchar * a,int len)1667 putaddrs(char *p, char *e, char *attr, uchar *a, int len)
1668 {
1669 int i;
1670
1671 for(i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen)
1672 p = seprint(p, e, "%s=%I\n", attr, a);
1673 return p;
1674 }
1675
1676 /* make an ndb entry and put it into /net/ndb for the servers to see */
1677 void
putndb(void)1678 putndb(void)
1679 {
1680 int append;
1681 char buf[1024];
1682 char *p, *e, *np;
1683
1684 p = buf;
1685 e = buf + sizeof buf;
1686 if(getndb() == 0)
1687 append = 1;
1688 else {
1689 append = 0;
1690 p = seprint(p, e, "ip=%I ipmask=%M ipgw=%I\n",
1691 conf.laddr, conf.mask, conf.gaddr);
1692 }
1693 if(np = strchr(conf.hostname, '.')){
1694 if(*conf.domainname == 0)
1695 strcpy(conf.domainname, np+1);
1696 *np = 0;
1697 }
1698 if(*conf.hostname)
1699 p = seprint(p, e, "\tsys=%s\n", conf.hostname);
1700 if(*conf.domainname)
1701 p = seprint(p, e, "\tdom=%s.%s\n",
1702 conf.hostname, conf.domainname);
1703 if(validip(conf.fs))
1704 p = putaddrs(p, e, "\tfs", conf.fs, sizeof conf.fs);
1705 if(validip(conf.auth))
1706 p = putaddrs(p, e, "\tauth", conf.auth, sizeof conf.auth);
1707 if(validip(conf.dns))
1708 p = putaddrs(p, e, "\tdns", conf.dns, sizeof conf.dns);
1709 if(validip(conf.ntp))
1710 p = putaddrs(p, e, "\tntp", conf.ntp, sizeof conf.ntp);
1711 if(ndboptions)
1712 p = seprint(p, e, "%s\n", ndboptions);
1713 if(p > buf)
1714 writendb(buf, p-buf, append);
1715 }
1716
1717 /* get an ndb entry someone else wrote */
1718 int
getndb(void)1719 getndb(void)
1720 {
1721 char buf[1024];
1722 int fd, n;
1723 char *p;
1724
1725 snprint(buf, sizeof buf, "%s/ndb", conf.mpoint);
1726 fd = open(buf, OREAD);
1727 n = read(fd, buf, sizeof buf-1);
1728 close(fd);
1729 if(n <= 0)
1730 return -1;
1731 buf[n] = 0;
1732 p = strstr(buf, "ip=");
1733 if(p == nil)
1734 return -1;
1735 if (parseip(conf.laddr, p+3) == -1)
1736 fprint(2, "%s: bad address %s\n", argv0, p+3);
1737 return 0;
1738 }
1739
1740 /* tell a server to refresh */
1741 void
tweakserver(char * server)1742 tweakserver(char *server)
1743 {
1744 int fd;
1745 char file[64];
1746
1747 snprint(file, sizeof file, "%s/%s", conf.mpoint, server);
1748 fd = open(file, ORDWR);
1749 if(fd < 0)
1750 return;
1751 fprint(fd, "refresh");
1752 close(fd);
1753 }
1754
1755 /* tell all servers to refresh their information */
1756 void
tweakservers(void)1757 tweakservers(void)
1758 {
1759 tweakserver("dns");
1760 tweakserver("cs");
1761 }
1762
1763 /* return number of networks */
1764 int
nipifcs(char * net)1765 nipifcs(char *net)
1766 {
1767 int n;
1768 Ipifc *nifc;
1769 Iplifc *lifc;
1770
1771 n = 0;
1772 ifc = readipifc(net, ifc, -1);
1773 for(nifc = ifc; nifc != nil; nifc = nifc->next){
1774 /*
1775 * ignore loopback devices when trying to
1776 * figure out if we're the primary interface.
1777 */
1778 if(strcmp(nifc->dev, "/dev/null") != 0)
1779 for(lifc = nifc->lifc; lifc != nil; lifc = lifc->next)
1780 if(validip(lifc->ip)){
1781 n++;
1782 break;
1783 }
1784 if(strcmp(nifc->dev, conf.dev) == 0)
1785 myifc = nifc->index;
1786 }
1787 return n;
1788 }
1789
1790 /* return true if this is a valid v4 address */
1791 int
validip(uchar * addr)1792 validip(uchar *addr)
1793 {
1794 return ipcmp(addr, IPnoaddr) != 0 && ipcmp(addr, v4prefix) != 0;
1795 }
1796
1797 /* look for an action */
1798 int
parseverb(char * name)1799 parseverb(char *name)
1800 {
1801 int i;
1802
1803 for(i = 0; i < nelem(verbs); i++)
1804 if(verbs[i] != nil && strcmp(name, verbs[i]) == 0)
1805 return i;
1806 return -1;
1807 }
1808
1809 /* get everything out of ndb */
1810 void
ndbconfig(void)1811 ndbconfig(void)
1812 {
1813 int nattr, nauth = 0, ndns = 0, nfs = 0, ok;
1814 char etheraddr[32];
1815 char *attrs[10];
1816 Ndb *db;
1817 Ndbtuple *t, *nt;
1818
1819 db = ndbopen(0);
1820 if(db == nil)
1821 sysfatal("can't open ndb: %r");
1822 if (strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0 ||
1823 myetheraddr(conf.hwa, conf.dev) != 0)
1824 sysfatal("can't read hardware address");
1825 sprint(etheraddr, "%E", conf.hwa);
1826 nattr = 0;
1827 attrs[nattr++] = "ip";
1828 attrs[nattr++] = "ipmask";
1829 attrs[nattr++] = "ipgw";
1830 /* the @ triggers resolution to an IP address; see ndb(2) */
1831 attrs[nattr++] = "@dns";
1832 attrs[nattr++] = "@ntp";
1833 attrs[nattr++] = "@fs";
1834 attrs[nattr++] = "@auth";
1835 attrs[nattr] = nil;
1836 t = ndbipinfo(db, "ether", etheraddr, attrs, nattr);
1837 for(nt = t; nt != nil; nt = nt->entry) {
1838 ok = 1;
1839 if(strcmp(nt->attr, "ip") == 0)
1840 ok = parseip(conf.laddr, nt->val);
1841 else if(strcmp(nt->attr, "ipmask") == 0)
1842 parseipmask(conf.mask, nt->val); /* could be -1 */
1843 else if(strcmp(nt->attr, "ipgw") == 0)
1844 ok = parseip(conf.gaddr, nt->val);
1845 else if(ndns < 2 && strcmp(nt->attr, "dns") == 0)
1846 ok = parseip(conf.dns+IPaddrlen*ndns, nt->val);
1847 else if(strcmp(nt->attr, "ntp") == 0)
1848 ok = parseip(conf.ntp, nt->val);
1849 else if(nfs < 2 && strcmp(nt->attr, "fs") == 0)
1850 ok = parseip(conf.fs+IPaddrlen*nfs, nt->val);
1851 else if(nauth < 2 && strcmp(nt->attr, "auth") == 0)
1852 ok = parseip(conf.auth+IPaddrlen*nauth, nt->val);
1853 if (!ok)
1854 fprint(2, "%s: bad %s address in ndb: %s\n", argv0,
1855 nt->attr, nt->val);
1856 }
1857 ndbfree(t);
1858 if(!validip(conf.laddr))
1859 sysfatal("address not found in ndb");
1860 }
1861
1862 int
addoption(char * opt)1863 addoption(char *opt)
1864 {
1865 int i;
1866 Option *o;
1867
1868 if(opt == nil)
1869 return -1;
1870 for(o = option; o < &option[nelem(option)]; o++)
1871 if(o->name && strcmp(opt, o->name) == 0){
1872 i = o - option;
1873 if(memchr(requested, i, nrequested) == 0 &&
1874 nrequested < nelem(requested))
1875 requested[nrequested++] = i;
1876 return 0;
1877 }
1878 return -1;
1879 }
1880
1881 char*
optgetx(uchar * p,uchar opt)1882 optgetx(uchar *p, uchar opt)
1883 {
1884 int i, n;
1885 ulong x;
1886 char *s, *ns;
1887 char str[256];
1888 uchar ip[IPaddrlen], ips[16*IPaddrlen], vec[256];
1889 Option *o;
1890
1891 o = &option[opt];
1892 if(o->name == nil)
1893 return nil;
1894
1895 s = nil;
1896 switch(o->type){
1897 case Taddr:
1898 if(optgetaddr(p, opt, ip))
1899 s = smprint("%s=%I", o->name, ip);
1900 break;
1901 case Taddrs:
1902 n = optgetaddrs(p, opt, ips, 16);
1903 if(n > 0)
1904 s = smprint("%s=%I", o->name, ips);
1905 for(i = 1; i < n; i++){
1906 ns = smprint("%s %s=%I", s, o->name, &ips[i*IPaddrlen]);
1907 free(s);
1908 s = ns;
1909 }
1910 break;
1911 case Tulong:
1912 x = optgetulong(p, opt);
1913 if(x != 0)
1914 s = smprint("%s=%lud", o->name, x);
1915 break;
1916 case Tbyte:
1917 x = optgetbyte(p, opt);
1918 if(x != 0)
1919 s = smprint("%s=%lud", o->name, x);
1920 break;
1921 case Tstr:
1922 if(optgetstr(p, opt, str, sizeof str))
1923 s = smprint("%s=%s", o->name, str);
1924 break;
1925 case Tvec:
1926 n = optgetvec(p, opt, vec, sizeof vec);
1927 if(n > 0)
1928 /* what's %H? it's not installed */
1929 s = smprint("%s=%.*H", o->name, n, vec);
1930 break;
1931 }
1932 return s;
1933 }
1934
1935 void
getoptions(uchar * p)1936 getoptions(uchar *p)
1937 {
1938 int i;
1939 char *s, *t;
1940
1941 for(i = nelem(defrequested); i < nrequested; i++){
1942 s = optgetx(p, requested[i]);
1943 if(s != nil)
1944 DEBUG("%s ", s);
1945 if(ndboptions == nil)
1946 ndboptions = smprint("\t%s", s);
1947 else{
1948 t = ndboptions;
1949 ndboptions = smprint("\t%s%s", s, ndboptions);
1950 free(t);
1951 }
1952 free(s);
1953 }
1954 }
1955