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);
53 fprint(2, "usage: %s [-6dDGnNOpPruX][-b baud][-c ctl]* [-g gw]"
55 "\t[-f dbfile][-x mtpt][-o dhcpopt] type dev [verb] [laddr [mask "
56 "[raddr [fs [auth]]]]]\n", argv0);
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 */
73 setnetmtpt(conf.mpoint, sizeof conf.mpoint, nil);
74 conf.cputype = getenv("cputype");
75 if(conf.cputype == nil)
84 warning(char *fmt, ...)
90 vseprint(buf, buf + sizeof buf, fmt, arg);
93 syslog(0, logfile, "%s", buf);
95 fprint(2, "%s: %s\n", argv0, buf);
99 parsenorm(int argc, char **argv)
103 if (parseip(conf.auth, argv[4]) == -1)
107 if (parseip(conf.fs, argv[3]) == -1)
111 if (parseip(conf.raddr, argv[2]) == -1)
116 * can't test for parseipmask()==-1 cuz 255.255.255.255
119 if (strcmp(argv[1], "0") != 0)
120 parseipmask(conf.mask, argv[1]);
123 if (parseip(conf.laddr, argv[0]) == -1)
134 finddev(char *dir, char *name, char *dev)
139 fd = open(dir, OREAD);
142 nd = dirreadall(fd, &d);
145 if(strncmp(d[i].name, name, strlen(name)))
147 if(strstr(d[i].name, "ctl") != nil)
148 continue; /* ignore ctl files */
149 dev = smprint("%s/%s", dir, d[i].name);
157 /* look for an action */
159 parseverb(char *name)
161 static char *verbs[] = {
168 [Vloopback] "loopback",
177 for(i = 0; i < nelem(verbs); i++)
178 if(verbs[i] != nil && strcmp(name, verbs[i]) == 0)
184 parseargs(int argc, char **argv)
189 /* default to any host name we already have */
190 if(*conf.hostname == 0){
191 p = getenv("sysname");
192 if(p == nil || *p == 0)
195 strncpy(conf.hostname, p, sizeof conf.hostname-1);
203 /* get optional medium and device */
205 verb = parseverb(*argv);
219 } else if(verb == Vppp)
220 conf.dev = finddev("/dev", "eia", "/dev/eia0");
225 conf.dev = finddev(conf.mpoint, "ether", "/net/ether0");
227 /* get optional verb */
229 verb = parseverb(*argv);
238 sysfatal("medium %s already specified", conf.type);
251 /* get verb-dependent arguments */
256 parsenorm(argc, argv);
259 parse6pref(argc, argv);
262 parse6ra(argc, argv);
269 findifc(char *net, char *dev)
273 ifc = readipifc(net, ifc, -1);
274 for(nifc = ifc; nifc != nil; nifc = nifc->next)
275 if(strcmp(nifc->dev, dev) == 0)
282 main(int argc, char **argv)
289 case '6': /* IPv6 auto config */
293 conf.baud = EARGF(usage());
296 cp = malloc(sizeof *cp);
302 cp->ctl = EARGF(usage());
311 dbfile = EARGF(usage());
314 if (parseip(conf.gaddr, EARGF(usage())) == -1)
321 snprint(conf.hostname, sizeof conf.hostname, "%s", EARGF(usage()));
325 conf.mtu = atoi(EARGF(usage()));
334 if(addoption(EARGF(usage())) < 0)
349 case 'u': /* IPv6: duplicate neighbour disc. off */
353 setnetmtpt(conf.mpoint, sizeof conf.mpoint, EARGF(usage()));
361 argv0 = "ipconfig"; /* boot invokes us as tcp? */
363 action = parseargs(argc, argv);
365 myifc = findifc(conf.mpoint, conf.dev);
371 /* bind new interface */
374 myifc = findifc(conf.mpoint, conf.dev);
380 sysfatal("interface not found for: %s", conf.dev);
382 /* open old interface */
412 return strcmp(conf.type, "ether") == 0 || strcmp(conf.type, "gbe") == 0;
415 /* create link local address */
419 if(isether() && myetheraddr(conf.hwa, conf.dev) == 0){
423 genrandom(conf.hwa, sizeof(conf.hwa));
426 ea2lla(conf.lladdr, conf.hwa);
429 /* create a client id */
433 if(isether() && myetheraddr(conf.hwa, conf.dev) == 0){
436 conf.cid[0] = conf.hwatype;
437 memmove(&conf.cid[1], conf.hwa, conf.hwalen);
438 conf.cidlen = conf.hwalen+1;
441 snprint((char*)conf.cid, sizeof conf.cid,
442 "plan9_%ld.%d", lrand(), getpid());
443 conf.cidlen = strlen((char*)conf.cid);
450 if(!validip(conf.laddr)){
453 ipmove(conf.laddr, conf.lladdr);
459 /* run dhcp if we need something */
461 fprint(conf.rfd, "tag dhcp");
464 dhcpquery(!noconfig, Sselecting);
467 if(!validip(conf.laddr))
468 if(rflag && dodhcp && !noconfig){
469 warning("couldn't determine ip address, retrying");
473 sysfatal("no success with DHCP");
475 DEBUG("adding address %I %M on %s", conf.laddr, conf.mask, conf.dev);
479 if(!isv4(conf.laddr)){
481 sysfatal("can't start IPv6 on %s, address %I", conf.dev, conf.laddr);
484 sysfatal("can't start IPv4 on %s, address %I", conf.dev, conf.laddr);
485 else if(dodhcp && conf.lease != Lforever)
489 /* leave everything we've learned somewhere other procs can find it */
490 if(beprimary && !dondbconfig && !ipv6auto)
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);
507 if(fprint(conf.cfd, "unbind") < 0)
508 warning("can't unbind %s: %r", conf.dev);
511 /* send some ctls to a device */
519 if (firstctl == nil || !isether())
522 snprint(ctlfile, sizeof ctlfile, "%s/clone", conf.dev);
523 fd = open(ctlfile, ORDWR);
525 sysfatal("can't open %s", ctlfile);
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);
532 // close(fd); /* or does it need to be left hanging? */
535 /* bind an ip stack to a device, leave the control channel open */
542 /* open the old interface */
543 snprint(buf, sizeof buf, "%s/ipifc/%d/ctl", conf.mpoint, myifc);
544 conf.cfd = open(buf, ORDWR);
546 sysfatal("open %s: %r", buf);
547 } else if(strcmp(conf.type, "ppp") == 0)
550 /* get a new ip interface */
551 snprint(buf, sizeof buf, "%s/ipifc/clone", conf.mpoint);
552 conf.cfd = open(buf, ORDWR);
554 sysfatal("opening %s/ipifc/clone: %r", conf.mpoint);
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);
560 snprint(buf, sizeof buf, "%s/iproute", conf.mpoint);
561 conf.rfd = open(buf, OWRITE);
564 /* add a logical interface to the ip stack */
571 if(!validip(conf.laddr) || !isv4(conf.laddr))
574 n = sprint(buf, "add");
575 n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
577 if(!validip(conf.mask))
578 ipmove(conf.mask, defmask(conf.laddr));
579 n += snprint(buf+n, sizeof buf-n, " %I", conf.mask);
581 if(validip(conf.raddr)){
582 n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
584 n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
587 if(write(conf.cfd, buf, n) < 0){
588 warning("write(%s): %r", buf);
592 if(validip(conf.gaddr) && isv4(conf.gaddr))
593 adddefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
598 /* remove a logical interface from the ip stack */
602 if(!validip(conf.laddr))
605 DEBUG("couldn't renew IP lease, releasing %I", conf.laddr);
607 if(!validip(conf.mask))
608 ipmove(conf.mask, defmask(conf.laddr));
612 ipmove(conf.laddr, IPnoaddr);
613 ipmove(conf.raddr, IPnoaddr);
614 ipmove(conf.mask, IPnoaddr);
617 /* return true if this is not a null address */
621 return ipcmp(addr, IPnoaddr) != 0 && ipcmp(addr, v4prefix) != 0;
624 /* put server ip addresses into the ndb entry */
626 putaddrs(char *p, char *e, char *attr, uchar *a, int len)
630 for(i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen)
631 p = seprint(p, e, "%s=%I\n", attr, a);
635 /* put space separated names into ndb entry */
637 putnames(char *p, char *e, char *attr, char *s)
641 for(; *s != 0; s = x+1){
642 if((x = strchr(s, ' ')) == nil)
644 p = seprint(p, e, "%s=%.*s\n", attr, (int)(x - s), s);
651 /* make an ndb entry and put it into /net/ndb for the servers to see */
655 static char buf[16*1024];
656 char file[64], *p, *e, *np;
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);
671 p = seprint(p, e, "\tsys=%s\n", conf.hostname);
673 p = seprint(p, e, "\tdom=%s.%s\n",
674 conf.hostname, conf.domainname);
676 p = putnames(p, e, "\tdnsdomain", conf.dnsdomain);
677 if(validip(conf.dns))
678 p = putaddrs(p, e, "\tdns", conf.dns, sizeof conf.dns);
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);
686 p = seprint(p, e, "%s\n", ndboptions);
688 /* append preexisting entries not matching our ip */
689 snprint(file, sizeof file, "%s/ndb", conf.mpoint);
692 while((t = ndbparse(db)) != nil){
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": " ");
708 if((fd = open(file, OWRITE|OTRUNC)) < 0)
710 write(fd, buf, p-buf);
715 issrcspec(uchar *src, uchar *smask)
717 return isv4(src)? memcmp(smask+IPv4off, IPnoaddr+IPv4off, 4): ipcmp(smask, IPnoaddr);
721 addroute(uchar *dst, uchar *mask, uchar *gate, uchar *ia, uchar *src, uchar *smask)
725 if(issrcspec(src, smask))
726 cmd = "add %I %M %I %I %I %M";
728 cmd = "add %I %M %I %I";
729 fprint(conf.rfd, cmd, dst, mask, gate, ia, src, smask);
733 removeroute(uchar *dst, uchar *mask, uchar *src, uchar *smask)
737 if(issrcspec(src, smask))
738 cmd = "remove %I %M %I %M";
740 cmd = "remove %I %M";
741 fprint(conf.rfd, cmd, dst, mask, src, smask);
745 adddefroute(uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
747 uchar dst[IPaddrlen], mask[IPaddrlen];
750 parseip(dst, "0.0.0.0");
751 parseipmask(mask, "0.0.0.0");
757 parseip(dst, "2000::");
758 parseipmask(mask, "/3");
764 addroute(dst, mask, gaddr, ia, src, smask);
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);
777 snprint(file, sizeof file, "%s/cs", conf.mpoint);
778 if((fd = open(file, OWRITE)) >= 0){
779 write(fd, "refresh", 7);
782 snprint(file, sizeof file, "%s/dns", conf.mpoint);
783 if((fd = open(file, OWRITE)) >= 0){
784 write(fd, "refresh", 7);
790 catch(void*, char *msg)
792 if(strstr(msg, "alarm"))
798 * based on libthread's threadsetname, but drags in less library code.
799 * actually just sets the arguments displayed.
802 procsetname(char *fmt, ...)
810 cmdname = vsmprint(fmt, arg);
814 snprint(buf, sizeof buf, "#p/%d/args", getpid());
815 if((fd = open(buf, OWRITE)) >= 0){
816 write(fd, cmdname, strlen(cmdname)+1);
822 /* return pseudo-random integer in range low...(hi-1) */
824 randint(ulong low, ulong hi)
828 return low + nrand(hi - low);
832 jitter(void) /* compute small pseudo-random delay in ms */
834 return randint(0, 10*1000);
838 countaddrs(uchar *a, int len)
842 for(i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen)
844 return i / IPaddrlen;
848 addaddrs(uchar *to, int nto, uchar *from, int nfrom)
852 for(i = 0; i < nfrom; i += IPaddrlen, from += IPaddrlen){
855 for(j = 0; j < nto && validip(to+j); j += IPaddrlen){
856 if(ipcmp(to+j, from) == 0)
866 addnames(char *d, char *s, int len)
872 if((e = strchr(s, ' ')) == nil)
878 if((f = strchr(p, ' ')) == nil)
880 if(f - p == n && memcmp(s, p, n) == 0)
886 if(1 + n + p - d >= len)
900 uniquent(Ndbtuple *t)
905 while((x = *l) != nil){
906 if(strcmp(t->attr, x->attr) != 0){
917 /* read configuration (except laddr) for myip from ndb */
919 ndb2conf(Ndb *db, uchar *myip)
922 char *attrs[10], val[64];
926 ipmove(conf.mask, defmask(conf.laddr));
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));
939 attrs[nattr++] = "ipmask";
940 attrs[nattr++] = "ipgw";
942 attrs[nattr++] = "@dns";
943 attrs[nattr++] = "@ntp";
944 attrs[nattr++] = "@fs";
945 attrs[nattr++] = "@auth";
947 attrs[nattr++] = "dnsdomain";
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));
956 if(strcmp(nt->attr, "ipmask") == 0) {
958 parseipmask(conf.mask, nt->val); /* could be -1 */
961 if(parseip(ip, nt->val) == -1) {
962 fprint(2, "%s: bad %s address in ndb: %s\n", argv0,
966 if(strcmp(nt->attr, "ipgw") == 0) {
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);
989 db = ndbopen(dbfile);
993 /* add addresses for my ethernet address from ndb */
997 uchar ips[128*IPaddrlen];
998 char etheraddr[32], *attr;
1003 db = opendatabase();
1005 sysfatal("can't open ndb: %r");
1007 if(validip(conf.laddr)){
1008 ndb2conf(db, conf.laddr);
1013 memset(ips, 0, sizeof(ips));
1015 if(!isether() || myetheraddr(conf.hwa, conf.dev) != 0)
1016 sysfatal("can't read hardware address");
1017 snprint(etheraddr, sizeof(etheraddr), "%E", conf.hwa);
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,
1027 addaddrs(ips, sizeof(ips), conf.laddr, IPaddrlen);
1031 n = countaddrs(ips, sizeof(ips));
1033 sysfatal("no ip addresses found in ndb");
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)){
1041 ipmove(conf.lladdr, conf.laddr);
1042 ndb2conf(db, conf.laddr);
1048 ipmove(conf.laddr, IPnoaddr);
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);
1057 || validip(conf.lladdr) && ipcmp(conf.laddr, conf.lladdr) != 0){
1058 ndb2conf(db, conf.laddr);