2 * ipconfig - configure parameters of an ip stack
12 #include <libsec.h> /* genrandom() */
19 Ctl *firstctl, **ctll = &firstctl;
34 int dupl_disc = 1; /* flag: V6 duplicate neighbor discovery */
39 static char logfile[] = "ipconfig";
41 static void binddevice(void);
42 static void controldevice(void);
43 extern void pppbinddev(void);
45 static void doadd(void);
46 static void doremove(void);
47 static void dounbind(void);
48 static void ndbconfig(void);
50 static int Ufmt(Fmt*);
51 #pragma varargck type "U" char*
56 fprint(2, "usage: %s [-6dDGnNOpPruX][-b baud][-c ctl]* [-g gw]"
58 "\t[-f dbfile][-x mtpt][-o dhcpopt] type dev [verb] [laddr [mask "
59 "[raddr [fs [auth]]]]]\n", argv0);
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 */
79 setnetmtpt(conf.mpoint, sizeof conf.mpoint, nil);
80 conf.cputype = getenv("cputype");
81 if(conf.cputype == nil)
90 warning(char *fmt, ...)
96 vseprint(buf, buf + sizeof buf, fmt, arg);
99 syslog(0, logfile, "%s", buf);
101 fprint(2, "%s: %s\n", argv0, buf);
105 parsenorm(int argc, char **argv)
109 if (parseip(conf.auth, argv[4]) == -1)
113 if (parseip(conf.fs, argv[3]) == -1)
117 if (parseip(conf.raddr, argv[2]) == -1)
122 * can't test for parseipmask()==-1 cuz 255.255.255.255
125 if (strcmp(argv[1], "0") != 0)
126 parseipmask(conf.mask, argv[1]);
129 if (parseip(conf.laddr, argv[0]) == -1)
140 finddev(char *dir, char *name, char *dev)
145 fd = open(dir, OREAD);
148 nd = dirreadall(fd, &d);
151 if(strncmp(d[i].name, name, strlen(name)))
153 if(strstr(d[i].name, "ctl") != nil)
154 continue; /* ignore ctl files */
155 dev = smprint("%s/%s", dir, d[i].name);
163 /* look for an action */
165 parseverb(char *name)
167 static char *verbs[] = {
174 [Vloopback] "loopback",
183 for(i = 0; i < nelem(verbs); i++)
184 if(verbs[i] != nil && strcmp(name, verbs[i]) == 0)
190 parseargs(int argc, char **argv)
195 /* default to any host name we already have */
196 if(*conf.hostname == 0){
197 p = getenv("sysname");
198 if(p == nil || *p == 0)
201 utf2idn(p, conf.hostname, sizeof(conf.hostname));
209 /* get optional medium and device */
211 verb = parseverb(*argv);
225 } else if(verb == Vppp)
226 conf.dev = finddev("/dev", "eia", "/dev/eia0");
231 conf.dev = finddev(conf.mpoint, "ether", "/net/ether0");
233 /* get optional verb */
235 verb = parseverb(*argv);
244 sysfatal("medium %s already specified", conf.type);
257 /* get verb-dependent arguments */
262 parsenorm(argc, argv);
265 parse6pref(argc, argv);
268 parse6ra(argc, argv);
275 findifc(char *net, char *dev)
279 ifc = readipifc(net, ifc, -1);
280 for(nifc = ifc; nifc != nil; nifc = nifc->next)
281 if(strcmp(nifc->dev, dev) == 0)
290 return strcmp(conf.type, "ether") == 0 || strcmp(conf.type, "gbe") == 0;
293 /* create a client id */
297 if(isether() && myetheraddr(conf.hwa, conf.dev) == 0){
300 conf.cid[0] = conf.hwatype;
301 memmove(&conf.cid[1], conf.hwa, conf.hwalen);
302 conf.cidlen = conf.hwalen+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));
310 ea2lla(conf.lladdr, conf.hwa);
314 main(int argc, char **argv)
321 case '6': /* IPv6 auto config */
325 conf.baud = EARGF(usage());
328 cp = malloc(sizeof *cp);
334 cp->ctl = EARGF(usage());
343 dbfile = EARGF(usage());
346 if (parseip(conf.gaddr, EARGF(usage())) == -1)
353 snprint(conf.hostname, sizeof conf.hostname, "%s", EARGF(usage()));
357 conf.mtu = atoi(EARGF(usage()));
366 if(addoption(EARGF(usage())) < 0)
381 case 'u': /* IPv6: duplicate neighbour disc. off */
385 setnetmtpt(conf.mpoint, sizeof conf.mpoint, EARGF(usage()));
393 argv0 = "ipconfig"; /* boot invokes us as tcp? */
395 action = parseargs(argc, argv);
397 myifc = findifc(conf.mpoint, conf.dev);
403 /* bind new interface */
406 myifc = findifc(conf.mpoint, conf.dev);
412 sysfatal("interface not found for: %s", conf.dev);
413 } else if(!noconfig) {
414 /* open old interface */
446 if(!validip(conf.laddr)){
448 ipmove(conf.laddr, conf.lladdr);
454 /* run dhcp if we need something */
456 fprint(conf.rfd, "tag dhcp");
457 dhcpquery(!noconfig, Sselecting);
460 if(!validip(conf.laddr))
461 if(rflag && dodhcp && !noconfig){
462 warning("couldn't determine ip address, retrying");
466 sysfatal("no success with DHCP");
468 DEBUG("adding address %I %M on %s", conf.laddr, conf.mask, conf.dev);
472 if(!isv4(conf.laddr)){
474 sysfatal("can't start IPv6 on %s, address %I", conf.dev, conf.laddr);
477 sysfatal("can't start IPv4 on %s, address %I", conf.dev, conf.laddr);
478 else if(dodhcp && conf.lease != Lforever)
482 /* leave everything we've learned somewhere other procs can find it */
483 if(beprimary && !dondbconfig && !ipv6auto)
491 if(!validip(conf.laddr))
492 sysfatal("remove requires an address");
494 DEBUG("removing address %I %M on %s", conf.laddr, conf.mask, conf.dev);
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);
508 if(fprint(conf.cfd, "unbind") < 0)
509 warning("can't unbind %s: %r", conf.dev);
512 /* send some ctls to a device */
520 if (firstctl == nil || !isether())
523 snprint(ctlfile, sizeof ctlfile, "%s/clone", conf.dev);
524 fd = open(ctlfile, ORDWR);
526 sysfatal("can't open %s", ctlfile);
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);
533 // close(fd); /* or does it need to be left hanging? */
536 /* bind an ip stack to a device, leave the control channel open */
543 /* open the old interface */
544 snprint(buf, sizeof buf, "%s/ipifc/%d/ctl", conf.mpoint, myifc);
545 conf.cfd = open(buf, ORDWR);
547 sysfatal("open %s: %r", buf);
548 } else if(strcmp(conf.type, "ppp") == 0)
551 /* get a new ip interface */
552 snprint(buf, sizeof buf, "%s/ipifc/clone", conf.mpoint);
553 conf.cfd = open(buf, ORDWR);
555 sysfatal("opening %s/ipifc/clone: %r", conf.mpoint);
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);
561 snprint(buf, sizeof buf, "%s/iproute", conf.mpoint);
562 conf.rfd = open(buf, OWRITE);
565 /* add a logical interface to the ip stack */
572 if(!validip(conf.laddr) || !isv4(conf.laddr))
575 n = sprint(buf, "add");
576 n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
578 if(!validip(conf.mask))
579 ipmove(conf.mask, defmask(conf.laddr));
580 n += snprint(buf+n, sizeof buf-n, " %I", conf.mask);
582 if(validip(conf.raddr)){
583 n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
585 n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
588 if(write(conf.cfd, buf, n) < 0){
589 warning("write(%s): %r", buf);
593 if(validip(conf.gaddr) && isv4(conf.gaddr))
594 adddefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
599 /* remove a logical interface from the ip stack */
603 if(!validip(conf.laddr))
606 if(!validip(conf.mask))
607 ipmove(conf.mask, defmask(conf.laddr));
609 if(validip(conf.gaddr))
610 removedefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
614 ipmove(conf.laddr, IPnoaddr);
615 ipmove(conf.raddr, IPnoaddr);
616 ipmove(conf.mask, IPnoaddr);
619 /* return true if this is not a null address */
623 return ipcmp(addr, IPnoaddr) != 0 && ipcmp(addr, v4prefix) != 0;
626 /* put server ip addresses into the ndb entry */
628 putaddrs(char *p, char *e, char *attr, uchar *a, int len)
632 for(i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen)
633 p = seprint(p, e, "%s=%I\n", attr, a);
637 /* put space separated names into ndb entry */
639 putnames(char *p, char *e, char *attr, char *s)
643 for(; *s != 0; s = x+1){
644 if((x = strchr(s, ' ')) != nil)
646 p = seprint(p, e, "%s=%U\n", attr, s);
654 /* make an ndb entry and put it into /net/ndb for the servers to see */
658 static char buf[16*1024];
659 char file[64], *p, *e, *np;
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);
674 p = seprint(p, e, "\tsys=%U\n", conf.hostname);
676 p = seprint(p, e, "\tdom=%U.%U\n",
677 conf.hostname, conf.domainname);
679 p = putnames(p, e, "\tdnsdomain", conf.dnsdomain);
680 if(validip(conf.dns))
681 p = putaddrs(p, e, "\tdns", conf.dns, sizeof conf.dns);
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);
689 p = seprint(p, e, "%s\n", ndboptions);
691 /* append preexisting entries not matching our ip */
692 snprint(file, sizeof file, "%s/ndb", conf.mpoint);
695 while((t = ndbparse(db)) != nil){
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": " ");
711 if((fd = open(file, OWRITE|OTRUNC)) < 0)
713 write(fd, buf, p-buf);
718 issrcspec(uchar *src, uchar *smask)
720 return isv4(src)? memcmp(smask+IPv4off, IPnoaddr+IPv4off, 4): ipcmp(smask, IPnoaddr);
724 routectl(char *cmd, uchar *dst, uchar *mask, uchar *gate, uchar *ia, uchar *src, uchar *smask)
728 if(issrcspec(src, smask))
729 ctl = "%s %I %M %I %I %I %M";
731 ctl = "%s %I %M %I %I";
732 DEBUG(ctl, cmd, dst, mask, gate, ia, src, smask);
735 fprint(conf.rfd, ctl, cmd, dst, mask, gate, ia, src, smask);
739 defroutectl(char *cmd, uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
741 uchar dst[IPaddrlen], mask[IPaddrlen];
744 parseip(dst, "0.0.0.0");
745 parseipmask(mask, "0.0.0.0");
751 parseip(dst, "2000::");
752 parseipmask(mask, "/3");
758 routectl(cmd, dst, mask, gaddr, ia, src, smask);
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);
766 adddefroute(uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
768 defroutectl("add", gaddr, ia, src, smask);
772 removedefroute(uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
774 defroutectl("remove", gaddr, ia, src, smask);
783 snprint(file, sizeof file, "%s/cs", conf.mpoint);
784 if((fd = open(file, OWRITE)) >= 0){
785 write(fd, "refresh", 7);
788 snprint(file, sizeof file, "%s/dns", conf.mpoint);
789 if((fd = open(file, OWRITE)) >= 0){
790 write(fd, "refresh", 7);
796 catch(void*, char *msg)
798 if(strstr(msg, "alarm"))
804 * based on libthread's threadsetname, but drags in less library code.
805 * actually just sets the arguments displayed.
808 procsetname(char *fmt, ...)
816 cmdname = vsmprint(fmt, arg);
820 snprint(buf, sizeof buf, "#p/%d/args", getpid());
821 if((fd = open(buf, OWRITE)) >= 0){
822 write(fd, cmdname, strlen(cmdname)+1);
828 /* return pseudo-random integer in range low...(hi-1) */
830 randint(ulong low, ulong hi)
834 return low + nrand(hi - low);
838 jitter(void) /* compute small pseudo-random delay in ms */
840 return randint(0, 10*1000);
844 countaddrs(uchar *a, int len)
848 for(i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen)
850 return i / IPaddrlen;
854 addaddrs(uchar *to, int nto, uchar *from, int nfrom)
858 for(i = 0; i < nfrom; i += IPaddrlen, from += IPaddrlen){
861 for(j = 0; j < nto && validip(to+j); j += IPaddrlen){
862 if(ipcmp(to+j, from) == 0)
872 addnames(char *d, char *s, int len)
878 if((e = strchr(s, ' ')) == nil)
884 if((f = strchr(p, ' ')) == nil)
886 if(f - p == n && memcmp(s, p, n) == 0)
892 if(1 + n + p - d >= len)
906 pnames(uchar *d, int nd, char *s)
914 for(l = 0; *s != 0 && *s != '.' && *s != ' '; l++)
918 if(d >= de || l > 077)
922 memmove(d-l, s-l, l);
927 return d - (de - nd);
931 gnames(char *d, int nd, uchar *s, int ns)
942 if((l & 0300) == 0300){
943 if(++p > 100 || s >= se)
945 l = (l & 077)<<8 | *s++;
963 if(s+l >= se || d+l >= de)
970 if(p != 0 || s != se || d <= de - nd || d[-1] != ' ')
973 return d - (de - nd);
981 s = va_arg(f->args, char*);
982 if(idn2utf(s, d, sizeof(d)) != nil)
984 fmtprint(f, "%s", s);
989 uniquent(Ndbtuple *t)
994 while((x = *l) != nil){
995 if(strcmp(t->attr, x->attr) != 0){
1006 /* read configuration (except laddr) for myip from ndb */
1008 ndb2conf(Ndb *db, uchar *myip)
1011 char *attrs[10], val[256];
1012 uchar ip[IPaddrlen];
1015 ipmove(conf.mask, defmask(conf.laddr));
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));
1028 attrs[nattr++] = "ipmask";
1029 attrs[nattr++] = "ipgw";
1031 attrs[nattr++] = "@dns";
1032 attrs[nattr++] = "@ntp";
1033 attrs[nattr++] = "@fs";
1034 attrs[nattr++] = "@auth";
1036 attrs[nattr++] = "dnsdomain";
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)
1044 addnames(conf.dnsdomain, val, sizeof(conf.dnsdomain));
1047 if(strcmp(nt->attr, "ipmask") == 0) {
1049 parseipmask(conf.mask, nt->val); /* could be -1 */
1052 if(parseip(ip, nt->val) == -1) {
1053 fprint(2, "%s: bad %s address in ndb: %s\n", argv0,
1057 if(strcmp(nt->attr, "ipgw") == 0) {
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);
1080 db = ndbopen(dbfile);
1084 /* add addresses for my ethernet address from ndb */
1088 uchar ips[128*IPaddrlen];
1089 char etheraddr[32], *attr;
1094 db = opendatabase();
1096 sysfatal("can't open ndb: %r");
1098 if(validip(conf.laddr)){
1099 ndb2conf(db, conf.laddr);
1104 memset(ips, 0, sizeof(ips));
1106 if(conf.hwatype != 1)
1107 sysfatal("can't read hardware address");
1108 snprint(etheraddr, sizeof(etheraddr), "%E", conf.hwa);
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,
1118 addaddrs(ips, sizeof(ips), conf.laddr, IPaddrlen);
1122 n = countaddrs(ips, sizeof(ips));
1124 sysfatal("no ip addresses found in ndb");
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)){
1132 ipmove(conf.lladdr, conf.laddr);
1133 ndb2conf(db, conf.laddr);
1139 ipmove(conf.laddr, IPnoaddr);
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);