]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/ipconfig/main.c
merge
[plan9front.git] / sys / src / cmd / ip / ipconfig / main.c
1 /*
2  * ipconfig - configure parameters of an ip stack
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <bio.h>
7 #include <ip.h>
8 #include <ndb.h>
9 #include "ipconfig.h"
10 #include "../dhcp.h"
11
12 #include <libsec.h> /* genrandom() */
13
14 Conf    conf;
15 int     myifc = -1;
16 int     beprimary = -1;
17 int     noconfig;
18 Ipifc   *ifc;
19 Ctl     *firstctl, **ctll = &firstctl;
20
21 int     debug;
22 int     dolog;
23
24 int     plan9 = 1;
25 int     Oflag;
26 int     rflag;
27
28 int     dodhcp;
29 int     nodhcpwatch;
30 int     sendhostname;
31 char    *ndboptions;
32
33 int     ipv6auto;
34 int     dupl_disc = 1;          /* flag: V6 duplicate neighbor discovery */
35
36 int     dondbconfig;
37 char    *dbfile;
38
39 static char logfile[] = "ipconfig";
40
41 static void     binddevice(void);
42 static void     controldevice(void);
43 extern void     pppbinddev(void);
44
45 static void     doadd(void);
46 static void     doremove(void);
47 static void     dounbind(void);
48 static void     ndbconfig(void);
49
50 void
51 usage(void)
52 {
53         fprint(2, "usage: %s [-6dDGnNOpPruX][-b baud][-c ctl]* [-g gw]"
54                 "[-h host][-m mtu]\n"
55                 "\t[-f dbfile][-x mtpt][-o dhcpopt] type dev [verb] [laddr [mask "
56                 "[raddr [fs [auth]]]]]\n", argv0);
57         exits("usage");
58 }
59
60 static void
61 init(void)
62 {
63         srand(truerand());
64         fmtinstall('E', eipfmt);
65         fmtinstall('I', eipfmt);
66         fmtinstall('M', eipfmt);
67         fmtinstall('V', eipfmt);
68         nsec();                 /* make sure time file is open before forking */
69
70         conf.cfd = -1;
71         conf.rfd = -1;
72
73         setnetmtpt(conf.mpoint, sizeof conf.mpoint, nil);
74         conf.cputype = getenv("cputype");
75         if(conf.cputype == nil)
76                 conf.cputype = "386";
77
78         v6paraminit(&conf);
79
80         dhcpinit();
81 }
82
83 void
84 warning(char *fmt, ...)
85 {
86         char buf[1024];
87         va_list arg;
88
89         va_start(arg, fmt);
90         vseprint(buf, buf + sizeof buf, fmt, arg);
91         va_end(arg);
92         if (dolog)
93                 syslog(0, logfile, "%s", buf);
94         else
95                 fprint(2, "%s: %s\n", argv0, buf);
96 }
97
98 static void
99 parsenorm(int argc, char **argv)
100 {
101         switch(argc){
102         case 5:
103                  if (parseip(conf.auth, argv[4]) == -1)
104                         usage();
105                 /* fall through */
106         case 4:
107                  if (parseip(conf.fs, argv[3]) == -1)
108                         usage();
109                 /* fall through */
110         case 3:
111                  if (parseip(conf.raddr, argv[2]) == -1)
112                         usage();
113                 /* fall through */
114         case 2:
115                 /*
116                  * can't test for parseipmask()==-1 cuz 255.255.255.255
117                  * looks like that.
118                  */
119                 if (strcmp(argv[1], "0") != 0)
120                         parseipmask(conf.mask, argv[1]);
121                 /* fall through */
122         case 1:
123                  if (parseip(conf.laddr, argv[0]) == -1)
124                         usage();
125                 /* fall through */
126         case 0:
127                 break;
128         default:
129                 usage();
130         }
131 }
132
133 static char*
134 finddev(char *dir, char *name, char *dev)
135 {
136         int fd, i, nd;
137         Dir *d;
138
139         fd = open(dir, OREAD);
140         if(fd >= 0){
141                 d = nil;
142                 nd = dirreadall(fd, &d);
143                 close(fd);
144                 for(i=0; i<nd; i++){
145                         if(strncmp(d[i].name, name, strlen(name)))
146                                 continue;
147                         if(strstr(d[i].name, "ctl") != nil)
148                                 continue;       /* ignore ctl files */
149                         dev = smprint("%s/%s", dir, d[i].name);
150                         break;
151                 }
152                 free(d);
153         }
154         return dev;
155 }
156
157 /* look for an action */
158 static int
159 parseverb(char *name)
160 {
161         static char *verbs[] = {
162                 [Vadd]          "add",
163                 [Vremove]       "remove",
164                 [Vunbind]       "unbind",
165                 [Vether]        "ether",
166                 [Vgbe]          "gbe",
167                 [Vppp]          "ppp",
168                 [Vloopback]     "loopback",
169                 [Vaddpref6]     "add6",
170                 [Vra6]          "ra6",
171                 [Vtorus]        "torus",
172                 [Vtree]         "tree",
173                 [Vpkt]          "pkt",
174         };
175         int i;
176
177         for(i = 0; i < nelem(verbs); i++)
178                 if(verbs[i] != nil && strcmp(name, verbs[i]) == 0)
179                         return i;
180         return -1;
181 }
182
183 static int
184 parseargs(int argc, char **argv)
185 {
186         char *p;
187         int action, verb;
188
189         /* default to any host name we already have */
190         if(*conf.hostname == 0){
191                 p = getenv("sysname");
192                 if(p == nil || *p == 0)
193                         p = sysname();
194                 if(p != nil)
195                         strncpy(conf.hostname, p, sizeof conf.hostname-1);
196         }
197
198         /* defaults */
199         conf.type = "ether";
200         conf.dev = nil;
201         action = Vadd;
202
203         /* get optional medium and device */
204         if (argc > 0){
205                 verb = parseverb(*argv);
206                 switch(verb){
207                 case Vether:
208                 case Vgbe:
209                 case Vppp:
210                 case Vloopback:
211                 case Vtorus:
212                 case Vtree:
213                 case Vpkt:
214                         conf.type = *argv++;
215                         argc--;
216                         if(argc > 0){
217                                 conf.dev = *argv++;
218                                 argc--;
219                         } else if(verb == Vppp)
220                                 conf.dev = finddev("/dev", "eia", "/dev/eia0");
221                         break;
222                 }
223         }
224         if(conf.dev == nil)
225                 conf.dev = finddev(conf.mpoint, "ether", "/net/ether0");
226
227         /* get optional verb */
228         if (argc > 0){
229                 verb = parseverb(*argv);
230                 switch(verb){
231                 case Vether:
232                 case Vgbe:
233                 case Vppp:
234                 case Vloopback:
235                 case Vtorus:
236                 case Vtree:
237                 case Vpkt:
238                         sysfatal("medium %s already specified", conf.type);
239                 case Vadd:
240                 case Vremove:
241                 case Vunbind:
242                 case Vaddpref6:
243                 case Vra6:
244                         argv++;
245                         argc--;
246                         action = verb;
247                         break;
248                 }
249         }
250
251         /* get verb-dependent arguments */
252         switch (action) {
253         case Vadd:
254         case Vremove:
255         case Vunbind:
256                 parsenorm(argc, argv);
257                 break;
258         case Vaddpref6:
259                 parse6pref(argc, argv);
260                 break;
261         case Vra6:
262                 parse6ra(argc, argv);
263                 break;
264         }
265         return action;
266 }
267
268 static int
269 findifc(char *net, char *dev)
270 {
271         Ipifc *nifc;
272
273         ifc = readipifc(net, ifc, -1);
274         for(nifc = ifc; nifc != nil; nifc = nifc->next)
275                 if(strcmp(nifc->dev, dev) == 0)
276                         return nifc->index;
277
278         return -1;
279 }
280
281 void
282 main(int argc, char **argv)
283 {
284         int action;
285         Ctl *cp;
286
287         init();
288         ARGBEGIN {
289         case '6':                       /* IPv6 auto config */
290                 ipv6auto = 1;
291                 break;
292         case 'b':
293                 conf.baud = EARGF(usage());
294                 break;
295         case 'c':
296                 cp = malloc(sizeof *cp);
297                 if(cp == nil)
298                         sysfatal("%r");
299                 *ctll = cp;
300                 ctll = &cp->next;
301                 cp->next = nil;
302                 cp->ctl = EARGF(usage());
303                 break;
304         case 'd':
305                 dodhcp = 1;
306                 break;
307         case 'D':
308                 debug = 1;
309                 break;
310         case 'f':
311                 dbfile = EARGF(usage());
312                 break;
313         case 'g':
314                 if (parseip(conf.gaddr, EARGF(usage())) == -1)
315                         usage();
316                 break;
317         case 'G':
318                 plan9 = 0;
319                 break;
320         case 'h':
321                 snprint(conf.hostname, sizeof conf.hostname, "%s", EARGF(usage()));
322                 sendhostname = 1;
323                 break;
324         case 'm':
325                 conf.mtu = atoi(EARGF(usage()));
326                 break;
327         case 'n':
328                 noconfig = 1;
329                 break;
330         case 'N':
331                 dondbconfig = 1;
332                 break;
333         case 'o':
334                 if(addoption(EARGF(usage())) < 0)
335                         usage();
336                 break;
337         case 'O':
338                 Oflag = 1;
339                 break;
340         case 'p':
341                 beprimary = 1;
342                 break;
343         case 'P':
344                 beprimary = 0;
345                 break;
346         case 'r':
347                 rflag = 1;
348                 break;
349         case 'u':               /* IPv6: duplicate neighbour disc. off */
350                 dupl_disc = 0;
351                 break;
352         case 'x':
353                 setnetmtpt(conf.mpoint, sizeof conf.mpoint, EARGF(usage()));
354                 break;
355         case 'X':
356                 nodhcpwatch = 1;
357                 break;
358         default:
359                 usage();
360         } ARGEND;
361         argv0 = "ipconfig";             /* boot invokes us as tcp? */
362
363         action = parseargs(argc, argv);
364
365         myifc = findifc(conf.mpoint, conf.dev);
366         if(myifc < 0) {
367                 switch(action){
368                 default:
369                         if(noconfig)
370                                 break;
371                         /* bind new interface */
372                         controldevice();
373                         binddevice();
374                         myifc = findifc(conf.mpoint, conf.dev);
375                 case Vremove:
376                 case Vunbind:
377                         break;
378                 }
379                 if(myifc < 0)
380                         sysfatal("interface not found for: %s", conf.dev);
381         } else {
382                 /* open old interface */
383                 binddevice();
384         }
385
386         switch(action){
387         case Vadd:
388                 if(dondbconfig){
389                         dodhcp = 0;
390                         ndbconfig();
391                         break;
392                 }
393                 doadd();
394                 break;
395         case Vaddpref6:
396         case Vra6:
397                 doipv6(action);
398                 break;
399         case Vremove:
400                 doremove();
401                 break;
402         case Vunbind:
403                 dounbind();
404                 break;
405         }
406         exits(nil);
407 }
408
409 static int
410 isether(void)
411 {
412         return strcmp(conf.type, "ether") == 0 || strcmp(conf.type, "gbe") == 0;
413 }
414
415 /* create link local address */
416 static void
417 mklladdr(void)
418 {
419         if(isether() && myetheraddr(conf.hwa, conf.dev) == 0){
420                 conf.hwalen = 6;
421                 conf.hwatype = 1;
422         } else {
423                 genrandom(conf.hwa, sizeof(conf.hwa));
424                 conf.hwatype = -1;
425         }
426         ea2lla(conf.lladdr, conf.hwa);
427 }
428
429 /* create a client id */
430 static void
431 mkclientid(void)
432 {
433         if(isether() && myetheraddr(conf.hwa, conf.dev) == 0){
434                 conf.hwalen = 6;
435                 conf.hwatype = 1;
436                 conf.cid[0] = conf.hwatype;
437                 memmove(&conf.cid[1], conf.hwa, conf.hwalen);
438                 conf.cidlen = conf.hwalen+1;
439         } else {
440                 conf.hwatype = -1;
441                 snprint((char*)conf.cid, sizeof conf.cid,
442                         "plan9_%ld.%d", lrand(), getpid());
443                 conf.cidlen = strlen((char*)conf.cid);
444         }
445 }
446
447 static void
448 doadd(void)
449 {
450         if(!validip(conf.laddr)){
451                 if(ipv6auto){
452                         mklladdr();
453                         ipmove(conf.laddr, conf.lladdr);
454                         dodhcp = 0;
455                 } else
456                         dodhcp = 1;
457         }
458
459         /* run dhcp if we need something */
460         if(dodhcp){
461                 fprint(conf.rfd, "tag dhcp");
462
463                 mkclientid();
464                 dhcpquery(!noconfig, Sselecting);
465         }
466
467         if(!validip(conf.laddr))
468                 if(rflag && dodhcp && !noconfig){
469                         warning("couldn't determine ip address, retrying");
470                         dhcpwatch(1);
471                         return;
472                 } else
473                         sysfatal("no success with DHCP");
474
475         DEBUG("adding address %I %M on %s", conf.laddr, conf.mask, conf.dev);
476         if(noconfig)
477                 return;
478
479         if(!isv4(conf.laddr)){
480                 if(ip6cfg() < 0)
481                         sysfatal("can't start IPv6 on %s, address %I", conf.dev, conf.laddr);
482         } else {
483                 if(ip4cfg() < 0)
484                         sysfatal("can't start IPv4 on %s, address %I", conf.dev, conf.laddr);
485                 else if(dodhcp && conf.lease != Lforever)
486                         dhcpwatch(0);
487         }
488
489         /* leave everything we've learned somewhere other procs can find it */
490         if(beprimary && !dondbconfig && !ipv6auto)
491                 putndb();
492         refresh();
493 }
494
495 static void
496 doremove(void)
497 {
498         if(!validip(conf.laddr))
499                 sysfatal("remove requires an address");
500         if(fprint(conf.cfd, "remove %I %M", conf.laddr, conf.mask) < 0)
501                 warning("can't remove %I %M: %r", conf.laddr, conf.mask);
502 }
503
504 static void
505 dounbind(void)
506 {
507         if(fprint(conf.cfd, "unbind") < 0)
508                 warning("can't unbind %s: %r", conf.dev);
509 }
510
511 /* send some ctls to a device */
512 static void
513 controldevice(void)
514 {
515         char ctlfile[256];
516         int fd;
517         Ctl *cp;
518
519         if (firstctl == nil || !isether())
520                 return;
521
522         snprint(ctlfile, sizeof ctlfile, "%s/clone", conf.dev);
523         fd = open(ctlfile, ORDWR);
524         if(fd < 0)
525                 sysfatal("can't open %s", ctlfile);
526
527         for(cp = firstctl; cp != nil; cp = cp->next){
528                 if(write(fd, cp->ctl, strlen(cp->ctl)) < 0)
529                         sysfatal("ctl message %s: %r", cp->ctl);
530                 seek(fd, 0, 0);
531         }
532 //      close(fd);              /* or does it need to be left hanging? */
533 }
534
535 /* bind an ip stack to a device, leave the control channel open */
536 static void
537 binddevice(void)
538 {
539         char buf[256];
540
541         if(myifc >= 0){
542                 /* open the old interface */
543                 snprint(buf, sizeof buf, "%s/ipifc/%d/ctl", conf.mpoint, myifc);
544                 conf.cfd = open(buf, ORDWR);
545                 if(conf.cfd < 0)
546                         sysfatal("open %s: %r", buf);
547         } else if(strcmp(conf.type, "ppp") == 0)
548                 pppbinddev();
549         else {
550                 /* get a new ip interface */
551                 snprint(buf, sizeof buf, "%s/ipifc/clone", conf.mpoint);
552                 conf.cfd = open(buf, ORDWR);
553                 if(conf.cfd < 0)
554                         sysfatal("opening %s/ipifc/clone: %r", conf.mpoint);
555
556                 /* specify medium as ethernet, bind the interface to it */
557                 if(fprint(conf.cfd, "bind %s %s", conf.type, conf.dev) < 0)
558                         sysfatal("%s: bind %s %s: %r", buf, conf.type, conf.dev);
559         }
560         snprint(buf, sizeof buf, "%s/iproute", conf.mpoint);
561         conf.rfd = open(buf, OWRITE);
562 }
563
564 /* add a logical interface to the ip stack */
565 int
566 ip4cfg(void)
567 {
568         char buf[256];
569         int n;
570
571         if(!validip(conf.laddr) || !isv4(conf.laddr))
572                 return -1;
573
574         n = sprint(buf, "add");
575         n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
576
577         if(!validip(conf.mask))
578                 ipmove(conf.mask, defmask(conf.laddr));
579         n += snprint(buf+n, sizeof buf-n, " %I", conf.mask);
580
581         if(validip(conf.raddr)){
582                 n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
583                 if(conf.mtu != 0)
584                         n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
585         }
586
587         if(write(conf.cfd, buf, n) < 0){
588                 warning("write(%s): %r", buf);
589                 return -1;
590         }
591
592         if(validip(conf.gaddr) && isv4(conf.gaddr))
593                 adddefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
594
595         return 0;
596 }
597
598 /* remove a logical interface from the ip stack */
599 void
600 ipunconfig(void)
601 {
602         if(!validip(conf.laddr))
603                 return;
604
605         DEBUG("couldn't renew IP lease, releasing %I", conf.laddr);
606
607         if(!validip(conf.mask))
608                 ipmove(conf.mask, defmask(conf.laddr));
609
610         doremove();
611
612         ipmove(conf.laddr, IPnoaddr);
613         ipmove(conf.raddr, IPnoaddr);
614         ipmove(conf.mask, IPnoaddr);
615 }
616
617 /* return true if this is not a null address */
618 int
619 validip(uchar *addr)
620 {
621         return ipcmp(addr, IPnoaddr) != 0 && ipcmp(addr, v4prefix) != 0;
622 }
623
624 /* put server ip addresses into the ndb entry */
625 static char*
626 putaddrs(char *p, char *e, char *attr, uchar *a, int len)
627 {
628         int i;
629
630         for(i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen)
631                 p = seprint(p, e, "%s=%I\n", attr, a);
632         return p;
633 }
634
635 /* put space separated names into ndb entry */
636 static char*
637 putnames(char *p, char *e, char *attr, char *s)
638 {
639         char *x;
640
641         for(; *s != 0; s = x+1){
642                 if((x = strchr(s, ' ')) == nil)
643                         x = strchr(s, 0);
644                 p = seprint(p, e, "%s=%.*s\n", attr, (int)(x - s), s);
645                 if(*x == 0)
646                         break;
647         }
648         return p;
649 }
650
651 /* make an ndb entry and put it into /net/ndb for the servers to see */
652 void
653 putndb(void)
654 {
655         static char buf[16*1024];
656         char file[64], *p, *e, *np;
657         Ndbtuple *t, *nt;
658         Ndb *db;
659         int fd;
660
661         p = buf;
662         e = buf + sizeof buf;
663         p = seprint(p, e, "ip=%I ipmask=%M ipgw=%I\n",
664                 conf.laddr, conf.mask, conf.gaddr);
665         if(np = strchr(conf.hostname, '.')){
666                 if(*conf.domainname == 0)
667                         strcpy(conf.domainname, np+1);
668                 *np = 0;
669         }
670         if(*conf.hostname)
671                 p = seprint(p, e, "\tsys=%s\n", conf.hostname);
672         if(*conf.domainname)
673                 p = seprint(p, e, "\tdom=%s.%s\n",
674                         conf.hostname, conf.domainname);
675         if(*conf.dnsdomain)
676                 p = putnames(p, e, "\tdnsdomain", conf.dnsdomain);
677         if(validip(conf.dns))
678                 p = putaddrs(p, e, "\tdns", conf.dns, sizeof conf.dns);
679         if(validip(conf.fs))
680                 p = putaddrs(p, e, "\tfs", conf.fs, sizeof conf.fs);
681         if(validip(conf.auth))
682                 p = putaddrs(p, e, "\tauth", conf.auth, sizeof conf.auth);
683         if(validip(conf.ntp))
684                 p = putaddrs(p, e, "\tntp", conf.ntp, sizeof conf.ntp);
685         if(ndboptions)
686                 p = seprint(p, e, "%s\n", ndboptions);
687
688         /* append preexisting entries not matching our ip */
689         snprint(file, sizeof file, "%s/ndb", conf.mpoint);
690         db = ndbopen(file);
691         if(db != nil ){
692                 while((t = ndbparse(db)) != nil){
693                         uchar ip[IPaddrlen];
694
695                         if((nt = ndbfindattr(t, t, "ip")) == nil
696                         || parseip(ip, nt->val) == -1
697                         || ipcmp(ip, conf.laddr) != 0){
698                                 p = seprint(p, e, "\n");
699                                 for(nt = t; nt != nil; nt = nt->entry)
700                                         p = seprint(p, e, "%s=%s%s", nt->attr, nt->val,
701                                                 nt->entry==nil? "\n": nt->line!=nt->entry? "\n\t": " ");
702                         }
703                         ndbfree(t);
704                 }
705                 ndbclose(db);
706         }
707
708         if((fd = open(file, OWRITE|OTRUNC)) < 0)
709                 return;
710         write(fd, buf, p-buf);
711         close(fd);
712 }
713
714 static int
715 issrcspec(uchar *src, uchar *smask)
716 {
717         return isv4(src)? memcmp(smask+IPv4off, IPnoaddr+IPv4off, 4): ipcmp(smask, IPnoaddr);
718 }
719
720 void
721 addroute(uchar *dst, uchar *mask, uchar *gate, uchar *ia, uchar *src, uchar *smask)
722 {
723         char *cmd;
724
725         if(issrcspec(src, smask))
726                 cmd = "add %I %M %I %I %I %M";
727         else
728                 cmd = "add %I %M %I %I";
729         fprint(conf.rfd, cmd, dst, mask, gate, ia, src, smask);
730 }
731
732 void
733 removeroute(uchar *dst, uchar *mask, uchar *src, uchar *smask)
734 {
735         char *cmd;
736
737         if(issrcspec(src, smask))
738                 cmd = "remove %I %M %I %M";
739         else
740                 cmd = "remove %I %M";
741         fprint(conf.rfd, cmd, dst, mask, src, smask);
742 }
743
744 void
745 adddefroute(uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
746 {
747         uchar dst[IPaddrlen], mask[IPaddrlen];
748
749         if(isv4(gaddr)){
750                 parseip(dst, "0.0.0.0");
751                 parseipmask(mask, "0.0.0.0");
752                 if(src == nil)
753                         src = dst;
754                 if(smask == nil)
755                         smask = mask;
756         } else {
757                 parseip(dst, "2000::");
758                 parseipmask(mask, "/3");
759                 if(src == nil)
760                         src = IPnoaddr;
761                 if(smask == nil)
762                         smask = IPnoaddr;
763         }
764         addroute(dst, mask, gaddr, ia, src, smask);
765
766         /* also add a source specific route */
767         if(ipcmp(src, IPnoaddr) != 0 && ipcmp(src, v4prefix) != 0)
768                 addroute(dst, mask, gaddr, ia, src, IPallbits);
769 }
770
771 void
772 refresh(void)
773 {
774         char file[64];
775         int fd;
776
777         snprint(file, sizeof file, "%s/cs", conf.mpoint);
778         if((fd = open(file, OWRITE)) >= 0){
779                 write(fd, "refresh", 7);
780                 close(fd);
781         }
782         snprint(file, sizeof file, "%s/dns", conf.mpoint);
783         if((fd = open(file, OWRITE)) >= 0){
784                 write(fd, "refresh", 7);
785                 close(fd);
786         }
787 }
788
789 void
790 catch(void*, char *msg)
791 {
792         if(strstr(msg, "alarm"))
793                 noted(NCONT);
794         noted(NDFLT);
795 }
796
797 /*
798  * based on libthread's threadsetname, but drags in less library code.
799  * actually just sets the arguments displayed.
800  */
801 void
802 procsetname(char *fmt, ...)
803 {
804         int fd;
805         char *cmdname;
806         char buf[128];
807         va_list arg;
808
809         va_start(arg, fmt);
810         cmdname = vsmprint(fmt, arg);
811         va_end(arg);
812         if (cmdname == nil)
813                 return;
814         snprint(buf, sizeof buf, "#p/%d/args", getpid());
815         if((fd = open(buf, OWRITE)) >= 0){
816                 write(fd, cmdname, strlen(cmdname)+1);
817                 close(fd);
818         }
819         free(cmdname);
820 }
821
822 /* return pseudo-random integer in range low...(hi-1) */
823 ulong
824 randint(ulong low, ulong hi)
825 {
826         if (hi < low)
827                 return low;
828         return low + nrand(hi - low);
829 }
830
831 long
832 jitter(void)            /* compute small pseudo-random delay in ms */
833 {
834         return randint(0, 10*1000);
835 }
836
837 int
838 countaddrs(uchar *a, int len)
839 {
840         int i;
841
842         for(i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen)
843                 ;
844         return i / IPaddrlen;
845 }
846
847 void
848 addaddrs(uchar *to, int nto, uchar *from, int nfrom)
849 {
850         int i, j;
851
852         for(i = 0; i < nfrom; i += IPaddrlen, from += IPaddrlen){
853                 if(!validip(from))
854                         continue;
855                 for(j = 0; j < nto && validip(to+j); j += IPaddrlen){
856                         if(ipcmp(to+j, from) == 0)
857                                 return;
858                 }
859                 if(j == nto)
860                         return;
861                 ipmove(to+j, from);
862         }
863 }
864
865 void
866 addnames(char *d, char *s, int len)
867 {
868         char *p, *e, *f;
869         int n;
870
871         for(;;s++){
872                 if((e = strchr(s, ' ')) == nil)
873                         e = strchr(s, 0);
874                 n = e - s;
875                 if(n == 0)
876                         goto next;
877                 for(p = d;;p++){
878                         if((f = strchr(p, ' ')) == nil)
879                                 f = strchr(p, 0);
880                         if(f - p == n && memcmp(s, p, n) == 0)
881                                 goto next;
882                         p = f;
883                         if(*p == 0)
884                                 break;
885                 }
886                 if(1 + n + p - d >= len)
887                         break;
888                 if(p > d)
889                         *p++ = ' ';
890                 p[n] = 0;
891                 memmove(p, s, n);
892 next:
893                 s = e;
894                 if(*s == 0)
895                         break;
896         }
897 }
898
899 static Ndbtuple*
900 uniquent(Ndbtuple *t)
901 {
902         Ndbtuple **l, *x;
903
904         l = &t->entry;
905         while((x = *l) != nil){
906                 if(strcmp(t->attr, x->attr) != 0){
907                         l = &x->entry;
908                         continue;
909                 }
910                 *l = x->entry;
911                 x->entry = nil;
912                 ndbfree(x);
913         }
914         return t;
915 }
916
917 /* read configuration (except laddr) for myip from ndb */
918 void
919 ndb2conf(Ndb *db, uchar *myip)
920 {
921         int nattr;
922         char *attrs[10], val[64];
923         uchar ip[IPaddrlen];
924         Ndbtuple *t, *nt;
925
926         ipmove(conf.mask, defmask(conf.laddr));
927
928         memset(conf.gaddr, 0, sizeof(conf.gaddr));
929         memset(conf.dns, 0, sizeof(conf.dns));
930         memset(conf.ntp, 0, sizeof(conf.ntp));
931         memset(conf.fs, 0, sizeof(conf.fs));
932         memset(conf.auth, 0, sizeof(conf.auth));
933         memset(conf.dnsdomain, 0, sizeof(conf.dnsdomain));
934
935         if(db == nil)
936                 return;
937
938         nattr = 0;
939         attrs[nattr++] = "ipmask";
940         attrs[nattr++] = "ipgw";
941
942         attrs[nattr++] = "@dns";
943         attrs[nattr++] = "@ntp";
944         attrs[nattr++] = "@fs";
945         attrs[nattr++] = "@auth";
946
947         attrs[nattr++] = "dnsdomain";
948
949         snprint(val, sizeof(val), "%I", myip);
950         t = ndbipinfo(db, "ip", val, attrs, nattr);
951         for(nt = t; nt != nil; nt = nt->entry) {
952                 if(strcmp(nt->attr, "dnsdomain") == 0) {
953                         addnames(conf.dnsdomain, nt->val, sizeof(conf.dnsdomain));
954                         continue;
955                 }
956                 if(strcmp(nt->attr, "ipmask") == 0) {
957                         nt = uniquent(nt);
958                         parseipmask(conf.mask, nt->val);  /* could be -1 */
959                         continue;
960                 }
961                 if(parseip(ip, nt->val) == -1) {
962                         fprint(2, "%s: bad %s address in ndb: %s\n", argv0,
963                                 nt->attr, nt->val);
964                         continue;
965                 }
966                 if(strcmp(nt->attr, "ipgw") == 0) {
967                         nt = uniquent(nt);
968                         ipmove(conf.gaddr, ip);
969                 } else if(strcmp(nt->attr, "dns") == 0) {
970                         addaddrs(conf.dns, sizeof(conf.dns), ip, IPaddrlen);
971                 } else if(strcmp(nt->attr, "ntp") == 0) {
972                         addaddrs(conf.ntp, sizeof(conf.ntp), ip, IPaddrlen);
973                 } else if(strcmp(nt->attr, "fs") == 0) {
974                         addaddrs(conf.fs, sizeof(conf.fs), ip, IPaddrlen);
975                 } else if(strcmp(nt->attr, "auth") == 0) {
976                         addaddrs(conf.auth, sizeof(conf.auth), ip, IPaddrlen);
977                 }
978         }
979         ndbfree(t);
980 }
981
982 Ndb*
983 opendatabase(void)
984 {
985         static Ndb *db;
986
987         if(db != nil)
988                 ndbclose(db);
989         db = ndbopen(dbfile);
990         return db;
991 }
992
993 /* add addresses for my ethernet address from ndb */
994 static void
995 ndbconfig(void)
996 {
997         uchar ips[128*IPaddrlen];
998         char etheraddr[32], *attr;
999         Ndbtuple *t, *nt;
1000         Ndb *db;
1001         int n, i;
1002
1003         db = opendatabase();
1004         if(db == nil)
1005                 sysfatal("can't open ndb: %r");
1006
1007         if(validip(conf.laddr)){
1008                 ndb2conf(db, conf.laddr);
1009                 doadd();
1010                 return;
1011         }
1012
1013         memset(ips, 0, sizeof(ips));
1014
1015         if(!isether() || myetheraddr(conf.hwa, conf.dev) != 0)
1016                 sysfatal("can't read hardware address");
1017         snprint(etheraddr, sizeof(etheraddr), "%E", conf.hwa);
1018
1019         attr = "ip";
1020         t = ndbipinfo(db, "ether", etheraddr, &attr, 1);
1021         for(nt = t; nt != nil; nt = nt->entry) {
1022                 if(parseip(conf.laddr, nt->val) == -1){
1023                         fprint(2, "%s: bad %s address in ndb: %s\n", argv0,
1024                                 nt->attr, nt->val);
1025                         continue;
1026                 }
1027                 addaddrs(ips, sizeof(ips), conf.laddr, IPaddrlen);
1028         }
1029         ndbfree(t);
1030
1031         n = countaddrs(ips, sizeof(ips));
1032         if(n == 0)
1033                 sysfatal("no ip addresses found in ndb");
1034
1035         /* add link local address first, if not already done */
1036         if(!validip(conf.lladdr) && !findllip(conf.lladdr, ifc)){
1037                 for(i = 0; i < n; i++){
1038                         ipmove(conf.laddr, ips+i*IPaddrlen);
1039                         if(ISIPV6LINKLOCAL(conf.laddr)){
1040                                 ipv6auto = 0;
1041                                 ipmove(conf.lladdr, conf.laddr);
1042                                 ndb2conf(db, conf.laddr);
1043                                 doadd();
1044                                 break;
1045                         }
1046                 }
1047                 if(ipv6auto){
1048                         ipmove(conf.laddr, IPnoaddr);
1049                         doadd();
1050                 }
1051         }
1052
1053         /* add v4 addresses and v6 if link local address is available */
1054         for(i = 0; i < n; i++){
1055                 ipmove(conf.laddr, ips+i*IPaddrlen);
1056                 if(isv4(conf.laddr)
1057                 || validip(conf.lladdr) && ipcmp(conf.laddr, conf.lladdr) != 0){
1058                         ndb2conf(db, conf.laddr);
1059                         doadd();
1060                 }
1061         }
1062 }