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