|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * ipconfig - configure parameters of an ip stack | 
|  | */ | 
|  |  | 
|  | #include <parlib/alarm.h> | 
|  | #include <iplib/iplib.h> | 
|  | #include <ndblib/ndb.h> | 
|  | #include <parlib/common.h> | 
|  | #include <parlib/printf-ext.h> | 
|  | #include <parlib/uthread.h> | 
|  | #include <stdio.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <pthread.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <time.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include "dhcp.h" | 
|  | #include "ipconfig.h" | 
|  |  | 
|  | #define DEBUG(...) do { if (debug) warning(__VA_ARGS__); } while (0) | 
|  |  | 
|  | /* possible verbs */ | 
|  | enum { | 
|  | /* commands */ | 
|  | Vadd, | 
|  | Vremove, | 
|  | Vunbind, | 
|  | Vaddpref6, | 
|  | Vra6, | 
|  | /* media */ | 
|  | Vether, | 
|  | Vgbe, | 
|  | Vloopback, | 
|  | Vtorus, | 
|  | Vtree, | 
|  | Vpkt, | 
|  | }; | 
|  |  | 
|  | enum { | 
|  | Taddr, | 
|  | Taddrs, | 
|  | Tstr, | 
|  | Tbyte, | 
|  | Tulong, | 
|  | Tvec, | 
|  | }; | 
|  |  | 
|  | struct option { | 
|  | char *name; | 
|  | int type; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * I was too lazy to look up the types for each of these | 
|  | * options.  If someone feels like it, please mail me a | 
|  | * corrected array -- presotto | 
|  | */ | 
|  | struct option option[256] = { | 
|  | [OBmask] { "ipmask", Taddr }, | 
|  | [OBtimeoff] { "timeoff", Tulong }, | 
|  | [OBrouter] { "ipgw", Taddrs }, | 
|  | [OBtimeserver] { "time", Taddrs }, | 
|  | [OBnameserver] { "name", Taddrs }, | 
|  | [OBdnserver] { "dns", Taddrs }, | 
|  | [OBlogserver] { "log", Taddrs }, | 
|  | [OBcookieserver] { "cookie", Taddrs }, | 
|  | [OBlprserver] { "lpr", Taddrs }, | 
|  | [OBimpressserver] { "impress", Taddrs }, | 
|  | [OBrlserver] { "rl", Taddrs }, | 
|  | [OBhostname] { "sys", Tstr }, | 
|  | [OBbflen] { "bflen", Tulong }, | 
|  | [OBdumpfile] { "dumpfile", Tstr }, | 
|  | [OBdomainname] { "dom", Tstr }, | 
|  | [OBswapserver] { "swap", Taddrs }, | 
|  | [OBrootpath] { "rootpath", Tstr }, | 
|  | [OBextpath] { "extpath", Tstr }, | 
|  | [OBipforward] { "ipforward", Taddrs }, | 
|  | [OBnonlocal] { "nonlocal", Taddrs }, | 
|  | [OBpolicyfilter] { "policyfilter", Taddrs }, | 
|  | [OBmaxdatagram] { "maxdatagram", Tulong }, | 
|  | [OBttl] { "ttl", Tulong }, | 
|  | [OBpathtimeout] { "pathtimeout", Taddrs }, | 
|  | [OBpathplateau] { "pathplateau", Taddrs }, | 
|  | [OBmtu] { "mtu", Tulong }, | 
|  | [OBsubnetslocal] { "subnetslocal", Taddrs }, | 
|  | [OBbaddr] { "baddr", Taddrs }, | 
|  | [OBdiscovermask] { "discovermask", Taddrs }, | 
|  | [OBsupplymask] { "supplymask", Taddrs }, | 
|  | [OBdiscoverrouter] { "discoverrouter", Taddrs }, | 
|  | [OBrsserver] { "rs", Taddrs }, | 
|  | [OBstaticroutes] { "staticroutes", Taddrs }, | 
|  | [OBtrailerencap] { "trailerencap", Taddrs }, | 
|  | [OBarptimeout] { "arptimeout", Tulong }, | 
|  | [OBetherencap] { "etherencap", Taddrs }, | 
|  | [OBtcpttl] { "tcpttl", Tulong }, | 
|  | [OBtcpka] { "tcpka", Tulong }, | 
|  | [OBtcpkag] { "tcpkag", Tulong }, | 
|  | [OBnisdomain] { "nisdomain", Tstr }, | 
|  | [OBniserver] { "ni", Taddrs }, | 
|  | [OBntpserver] { "ntp", Taddrs }, | 
|  | [OBnetbiosns] { "netbiosns", Taddrs }, | 
|  | [OBnetbiosdds] { "netbiosdds", Taddrs }, | 
|  | [OBnetbiostype] { "netbiostype", Taddrs }, | 
|  | [OBnetbiosscope] { "netbiosscope", Taddrs }, | 
|  | [OBxfontserver] { "xfont", Taddrs }, | 
|  | [OBxdispmanager] { "xdispmanager", Taddrs }, | 
|  | [OBnisplusdomain] { "nisplusdomain", Tstr }, | 
|  | [OBnisplusserver] { "nisplus", Taddrs }, | 
|  | [OBhomeagent] { "homeagent", Taddrs }, | 
|  | [OBsmtpserver] { "smtp", Taddrs }, | 
|  | [OBpop3server] { "pop3", Taddrs }, | 
|  | [OBnntpserver] { "nntp", Taddrs }, | 
|  | [OBwwwserver] { "www", Taddrs }, | 
|  | [OBfingerserver] { "finger", Taddrs }, | 
|  | [OBircserver] { "irc", Taddrs }, | 
|  | [OBstserver] { "st", Taddrs }, | 
|  | [OBstdaserver] { "stdar", Taddrs }, | 
|  | [ODipaddr] { "ipaddr", Taddr }, | 
|  | [ODlease] { "lease", Tulong }, | 
|  | [ODoverload] { "overload", Taddr }, | 
|  | [ODtype] { "type", Tbyte }, | 
|  | [ODserverid] { "serverid", Taddr }, | 
|  | [ODparams] { "params", Tvec }, | 
|  | [ODmessage] { "message", Tstr }, | 
|  | [ODmaxmsg] { "maxmsg", Tulong }, | 
|  | [ODrenewaltime] { "renewaltime", Tulong }, | 
|  | [ODrebindingtime] { "rebindingtime", Tulong }, | 
|  | [ODvendorclass] { "vendorclass", Tvec }, | 
|  | [ODclientid] { "clientid", Tvec }, | 
|  | [ODtftpserver] { "tftp", Taddr }, | 
|  | [ODbootfile] { "bootfile", Tstr }, | 
|  | }; | 
|  |  | 
|  | uint8_t defrequested[] = { | 
|  | OBmask, OBrouter, OBdnserver, OBhostname, OBdomainname, OBntpserver, | 
|  | }; | 
|  |  | 
|  | uint8_t requested[256]; | 
|  | int nrequested; | 
|  |  | 
|  | char *argv0; | 
|  | int Oflag; | 
|  | int beprimary = -1; | 
|  | struct conf conf; | 
|  | int debug; | 
|  | int dodhcp; | 
|  | int dondbconfig; | 
|  | int dupl_disc = 1; /* flag: V6 duplicate neighbor discovery */ | 
|  | struct ctl *firstctl, **ctll; | 
|  | struct ipifc *ifc; | 
|  | int ipv6auto; | 
|  | int myifc = -1; | 
|  | char *ndboptions; | 
|  | int nip; | 
|  | int noconfig; | 
|  | int nodhcpwatch; | 
|  | char optmagic[4] = {0x63, 0x82, 0x53, 0x63}; | 
|  | int plan9 = 1; | 
|  | int sendhostname; | 
|  | int server; | 
|  |  | 
|  | char *verbs[] = { | 
|  | [Vadd] "add", | 
|  | [Vremove] "remove", | 
|  | [Vunbind] "unbind", | 
|  | [Vether] "ether", | 
|  | [Vgbe] "gbe", | 
|  | [Vloopback] "loopback", | 
|  | [Vaddpref6] "add6", | 
|  | [Vra6] "ra6", | 
|  | [Vtorus] "torus", | 
|  | [Vtree] "tree", | 
|  | [Vpkt] "pkt", | 
|  | }; | 
|  |  | 
|  | void evnotify(int rc) | 
|  | { | 
|  | struct event_msg msg = { 0 }; | 
|  |  | 
|  | msg.ev_type = EV_USER_IPI; | 
|  | msg.ev_arg1 = rc; | 
|  | sys_notify(getppid(), EV_USER_IPI, &msg); | 
|  | } | 
|  |  | 
|  | void evexit(int rc) | 
|  | { | 
|  | if (server) | 
|  | evnotify(rc); | 
|  | exit(rc); | 
|  | } | 
|  |  | 
|  | void usage(void) | 
|  | { | 
|  | fprintf(stderr, | 
|  | "usage: %s %s\n\t%s\n", | 
|  | argv0, | 
|  | "[-6dDGnNOpPruX][-b baud][-c ctl]* [-g gw] [-h host][-m mtu]", | 
|  | "[-x mtpt][-o dhcpopt] type dev [verb] [laddr [mask [raddr [fs [auth]]]]]"); | 
|  | evexit(1); | 
|  | } | 
|  |  | 
|  | void warning(char *fmt, ...) | 
|  | { | 
|  | char buf[1024]; | 
|  | va_list arg; | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | vsnprintf(buf, sizeof(buf), fmt, arg); | 
|  | va_end(arg); | 
|  | fprintf(stderr, "%s: %s\n", argv0, buf); | 
|  | } | 
|  |  | 
|  | char *sysname(void) | 
|  | { | 
|  | static char sname[256]; | 
|  |  | 
|  | gethostname(sname, sizeof(sname)); | 
|  |  | 
|  | return sname; | 
|  | } | 
|  |  | 
|  | void parsenorm(int argc, char **argv) | 
|  | { | 
|  | switch (argc) { | 
|  | case 5: | 
|  | if (parseip(conf.auth, argv[4]) == -1) | 
|  | usage(); | 
|  | /* fall through */ | 
|  | case 4: | 
|  | if (parseip(conf.fs, argv[3]) == -1) | 
|  | usage(); | 
|  | /* fall through */ | 
|  | case 3: | 
|  | if (parseip(conf.raddr, argv[2]) == -1) | 
|  | usage(); | 
|  | /* fall through */ | 
|  | case 2: | 
|  | /* | 
|  | * can't test for parseipmask()==-1 cuz 255.255.255.255 | 
|  | * looks like that. | 
|  | */ | 
|  | if (strcmp(argv[1], "0") != 0) | 
|  | parseipmask(conf.mask, argv[1]); | 
|  | /* fall through */ | 
|  | case 1: | 
|  | if (parseip(conf.laddr, argv[0]) == -1) | 
|  | usage(); | 
|  | /* fall through */ | 
|  | case 0: | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void parse6pref(int argc, char **argv) | 
|  | { | 
|  | switch (argc) { | 
|  | case 6: | 
|  | conf.preflt = strtoul(argv[5], 0, 10); | 
|  | /* fall through */ | 
|  | case 5: | 
|  | conf.validlt = strtoul(argv[4], 0, 10); | 
|  | /* fall through */ | 
|  | case 4: | 
|  | conf.autoflag = (atoi(argv[3]) != 0); | 
|  | /* fall through */ | 
|  | case 3: | 
|  | conf.onlink = (atoi(argv[2]) != 0); | 
|  | /* fall through */ | 
|  | case 2: | 
|  | conf.prefixlen = atoi(argv[1]); | 
|  | /* fall through */ | 
|  | case 1: | 
|  | if (parseip(conf.v6pref, argv[0]) == -1) { | 
|  | fprintf(stderr, "bad address %s\n", argv[0]); | 
|  | evexit(-1); | 
|  | } | 
|  | break; | 
|  | } | 
|  | DEBUG("parse6pref: pref %R len %d", conf.v6pref, conf.prefixlen); | 
|  | } | 
|  |  | 
|  | /* parse router advertisement (keyword, value) pairs */ | 
|  | void parse6ra(int argc, char *argv[]) | 
|  | { | 
|  | int i, argsleft; | 
|  | char *kw, *val; | 
|  |  | 
|  | if (argc % 2 != 0) | 
|  | usage(); | 
|  |  | 
|  | i = 0; | 
|  | for (argsleft = argc; argsleft > 1; argsleft -= 2) { | 
|  | kw = argv[i]; | 
|  | val = argv[i + 1]; | 
|  | if (strcmp(kw, "recvra") == 0) | 
|  | conf.recvra = (atoi(val) != 0); | 
|  | else if (strcmp(kw, "sendra") == 0) | 
|  | conf.sendra = (atoi(val) != 0); | 
|  | else if (strcmp(kw, "mflag") == 0) | 
|  | conf.mflag = (atoi(val) != 0); | 
|  | else if (strcmp(kw, "oflag") == 0) | 
|  | conf.oflag = (atoi(val) != 0); | 
|  | else if (strcmp(kw, "maxraint") == 0) | 
|  | conf.maxraint = atoi(val); | 
|  | else if (strcmp(kw, "minraint") == 0) | 
|  | conf.minraint = atoi(val); | 
|  | else if (strcmp(kw, "linkmtu") == 0) | 
|  | conf.linkmtu = atoi(val); | 
|  | else if (strcmp(kw, "reachtime") == 0) | 
|  | conf.reachtime = atoi(val); | 
|  | else if (strcmp(kw, "rxmitra") == 0) | 
|  | conf.rxmitra = atoi(val); | 
|  | else if (strcmp(kw, "ttl") == 0) | 
|  | conf.ttl = atoi(val); | 
|  | else if (strcmp(kw, "routerlt") == 0) | 
|  | conf.routerlt = atoi(val); | 
|  | else { | 
|  | warning("bad ra6 keyword %s", kw); | 
|  | usage(); | 
|  | } | 
|  | i += 2; | 
|  | } | 
|  |  | 
|  | /* consistency check */ | 
|  | if (conf.maxraint < conf.minraint) { | 
|  | fprintf(stderr, "maxraint %d < minraint %d\n", | 
|  | conf.maxraint, conf.minraint); | 
|  | evexit(-1); | 
|  | } | 
|  | } | 
|  |  | 
|  | void init(void) | 
|  | { | 
|  | parlib_wants_to_be_mcp = FALSE; | 
|  | srand(lrand48()); | 
|  | if (register_printf_specifier('E', printf_ethaddr, | 
|  | printf_ethaddr_info) != 0) | 
|  | fprintf(stderr, "Installing 'E' failed\n"); | 
|  | if (register_printf_specifier('R', printf_ipaddr, printf_ipaddr_info) != | 
|  | 0) | 
|  | fprintf(stderr, "Installing 'R' failed\n"); | 
|  | if (register_printf_specifier('M', printf_ipmask, printf_ipmask_info) != | 
|  | 0) | 
|  | fprintf(stderr, "Installing 'M' failed\n"); | 
|  |  | 
|  | setnetmtpt(conf.mpoint, sizeof(conf).mpoint, NULL); | 
|  | conf.cputype = getenv("cputype"); | 
|  | if (conf.cputype == NULL) | 
|  | conf.cputype = "386"; | 
|  |  | 
|  | ctll = &firstctl; | 
|  | v6paraminit(&conf); | 
|  |  | 
|  | /* init set of requested dhcp parameters with the default */ | 
|  | nrequested = sizeof(defrequested); | 
|  | memcpy(requested, defrequested, nrequested); | 
|  | } | 
|  |  | 
|  | int parseargs(int argc, char *argv[]) | 
|  | { | 
|  | char *p; | 
|  | int action, verb; | 
|  |  | 
|  | /* default to any host name we already have */ | 
|  | if (*conf.hostname == 0) { | 
|  | p = getenv("sysname"); | 
|  | if (p == NULL || *p == 0) | 
|  | p = sysname(); | 
|  | if (p != NULL) | 
|  | strncpy(conf.hostname, p, sizeof(conf).hostname - 1); | 
|  | } | 
|  |  | 
|  | /* defaults */ | 
|  | conf.type = "ether"; | 
|  | conf.dev = "/net/ether0"; | 
|  | action = Vadd; | 
|  |  | 
|  | /* get optional medium and device */ | 
|  | if (argc > 0) { | 
|  | verb = parseverb(*argv); | 
|  | switch (verb) { | 
|  | case Vether: | 
|  | case Vgbe: | 
|  | case Vloopback: | 
|  | case Vtorus: | 
|  | case Vtree: | 
|  | case Vpkt: | 
|  | conf.type = *argv++; | 
|  | argc--; | 
|  | if (argc > 0) { | 
|  | conf.dev = *argv++; | 
|  | argc--; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* get optional verb */ | 
|  | if (argc > 0) { | 
|  | verb = parseverb(*argv); | 
|  | switch (verb) { | 
|  | case Vether: | 
|  | case Vgbe: | 
|  | case Vloopback: | 
|  | case Vtorus: | 
|  | case Vtree: | 
|  | case Vpkt: | 
|  | fprintf(stderr, "medium %s already specified\n", | 
|  | conf.type); | 
|  | evexit(-1); | 
|  | case Vadd: | 
|  | case Vremove: | 
|  | case Vunbind: | 
|  | case Vaddpref6: | 
|  | case Vra6: | 
|  | argv++; | 
|  | argc--; | 
|  | action = verb; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* get verb-dependent arguments */ | 
|  | switch (action) { | 
|  | case Vadd: | 
|  | case Vremove: | 
|  | case Vunbind: | 
|  | parsenorm(argc, argv); | 
|  | break; | 
|  | case Vaddpref6: | 
|  | parse6pref(argc, argv); | 
|  | break; | 
|  | case Vra6: | 
|  | parse6ra(argc, argv); | 
|  | break; | 
|  | } | 
|  | return action; | 
|  | } | 
|  |  | 
|  | int main(int argc, char *argv[]) | 
|  | { | 
|  | int retry, action, ch; | 
|  | struct ctl *cp; | 
|  |  | 
|  | init(); | 
|  | retry = 0; | 
|  | while ((ch = getopt(argc, argv, "6b:c:dDg:h:m:nNo:OpPrSux:X")) != -1) { | 
|  | switch (ch) { | 
|  | case '6': /* IPv6 auto config */ | 
|  | ipv6auto = 1; | 
|  | break; | 
|  | case 'b': | 
|  | conf.baud = optarg; | 
|  | break; | 
|  | case 'c': | 
|  | cp = malloc(sizeof(*cp)); | 
|  | if (cp == NULL) { | 
|  | fprintf(stderr, "%r\n"); | 
|  | evexit(1); | 
|  | } | 
|  | *ctll = cp; | 
|  | ctll = &cp->next; | 
|  | cp->next = NULL; | 
|  | cp->ctl = optarg; | 
|  | break; | 
|  | case 'd': | 
|  | dodhcp = 1; | 
|  | break; | 
|  | case 'D': | 
|  | debug = 1; | 
|  | break; | 
|  | case 'g': | 
|  | if (parseip(conf.gaddr, optarg) == -1) | 
|  | usage(); | 
|  | break; | 
|  | case 'G': | 
|  | plan9 = 0; | 
|  | break; | 
|  | case 'h': | 
|  | snprintf(conf.hostname, sizeof(conf).hostname, | 
|  | "%s", optarg); | 
|  | sendhostname = 1; | 
|  | break; | 
|  | case 'm': | 
|  | conf.mtu = atoi(optarg); | 
|  | break; | 
|  | case 'n': | 
|  | noconfig = 1; | 
|  | break; | 
|  | case 'N': | 
|  | dondbconfig = 1; | 
|  | break; | 
|  | case 'o': | 
|  | if (addoption(optarg) < 0) | 
|  | usage(); | 
|  | break; | 
|  | case 'O': | 
|  | Oflag = 1; | 
|  | break; | 
|  | case 'p': | 
|  | beprimary = 1; | 
|  | break; | 
|  | case 'P': | 
|  | beprimary = 0; | 
|  | break; | 
|  | case 'r': | 
|  | retry = 1; | 
|  | break; | 
|  | case 'S': | 
|  | server = 1; | 
|  | break; | 
|  | case 'u': /* IPv6: duplicate neighbour disc. off */ | 
|  | dupl_disc = 0; | 
|  | break; | 
|  | case 'x': | 
|  | setnetmtpt(conf.mpoint, sizeof(conf).mpoint, optarg); | 
|  | break; | 
|  | case 'X': | 
|  | nodhcpwatch = 1; | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | } | 
|  | } | 
|  | argv0 = "ipconfig"; /* boot invokes us as tcp? */ | 
|  | argc -= optind; | 
|  | argv += optind; | 
|  |  | 
|  | action = parseargs(argc, argv); | 
|  | switch (action) { | 
|  | case Vadd: | 
|  | doadd(retry); | 
|  | break; | 
|  | case Vremove: | 
|  | doremove(); | 
|  | break; | 
|  | case Vunbind: | 
|  | dounbind(); | 
|  | break; | 
|  | case Vaddpref6: | 
|  | case Vra6: | 
|  | doipv6(action); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (server) { | 
|  | evnotify(0); | 
|  | uthread_sleep_forever(); | 
|  | } | 
|  |  | 
|  | evexit(0); | 
|  | } | 
|  |  | 
|  | int havendb(char *net) | 
|  | { | 
|  | struct stat s; | 
|  | char buf[128]; | 
|  |  | 
|  | snprintf(buf, sizeof(buf), "%s/ndb", net); | 
|  | if (stat(buf, &s) < 0) | 
|  | return 0; | 
|  | if (s.st_size == 0) | 
|  | return 0; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | void doadd(int retry) | 
|  | { | 
|  | /* get number of preexisting interfaces */ | 
|  | nip = nipifcs(conf.mpoint); | 
|  | if (beprimary == -1 && (nip == 0 || !havendb(conf.mpoint))) | 
|  | beprimary = 1; | 
|  |  | 
|  | /* get ipifc into name space and condition device for ip */ | 
|  | if (!noconfig) { | 
|  | lookforip(conf.mpoint); | 
|  | controldevice(); | 
|  | binddevice(); | 
|  | } | 
|  |  | 
|  | if (ipv6auto) { | 
|  | if (ip6cfg(ipv6auto) < 0) { | 
|  | fprintf(stderr, "can't automatically start IPv6 on %s\n", | 
|  | conf.dev); | 
|  | evexit(-1); | 
|  | } | 
|  | } else if (validip(conf.laddr) && !isv4(conf.laddr)) { | 
|  | if (ip6cfg(0) < 0) | 
|  | fprintf(stderr, "can't start IPv6 on %s, address %R\n", | 
|  | conf.dev, conf.laddr); | 
|  | evexit(-1); | 
|  | } | 
|  |  | 
|  | if (!validip(conf.laddr)) { | 
|  | if (dondbconfig) | 
|  | ndbconfig(); | 
|  | else | 
|  | dodhcp = 1; | 
|  | } | 
|  |  | 
|  | /* run dhcp if we need something */ | 
|  | if (dodhcp) { | 
|  | mkclientid(); | 
|  | dhcpquery(!noconfig, Sselecting); | 
|  | } | 
|  |  | 
|  | if (!validip(conf.laddr)) { | 
|  | if (retry && dodhcp && !noconfig) { | 
|  | warning("couldn't determine ip address, retrying"); | 
|  | dhcpwatch(1); | 
|  | return; | 
|  | } | 
|  | fprintf(stderr, "no success with DHCP\n"); | 
|  | evexit(-1); | 
|  | } | 
|  |  | 
|  |  | 
|  | if (!noconfig) { | 
|  | if (ip4cfg() < 0) { | 
|  | fprintf(stderr, "can't start ip\n"); | 
|  | evexit(-1); | 
|  | } | 
|  | if (dodhcp && conf.lease != Lforever) | 
|  | dhcpwatch(0); | 
|  | } | 
|  |  | 
|  | /* leave everything we've learned somewhere other procs can find it */ | 
|  | if (beprimary == 1) { | 
|  | putndb(); | 
|  | tweakservers(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void doremove(void) | 
|  | { | 
|  | char file[128]; | 
|  | char buf[256]; | 
|  | int cfd; | 
|  | struct ipifc *nifc; | 
|  | struct iplifc *lifc; | 
|  |  | 
|  | if (!validip(conf.laddr)) { | 
|  | fprintf(stderr, "remove requires an address\n"); | 
|  | evexit(-1); | 
|  | } | 
|  | ifc = readipifc(conf.mpoint, ifc, -1); | 
|  | for (nifc = ifc; nifc != NULL; nifc = nifc->next) { | 
|  | if (strcmp(nifc->dev, conf.dev) != 0) | 
|  | continue; | 
|  | for (lifc = nifc->lifc; lifc != NULL; lifc = lifc->next) { | 
|  | if (ipcmp(conf.laddr, lifc->ip) != 0) | 
|  | continue; | 
|  | if (validip(conf.mask) && ipcmp(conf.mask, lifc->mask) | 
|  | != 0) | 
|  | continue; | 
|  | if (validip(conf.raddr) && ipcmp(conf.raddr, lifc->net) | 
|  | != 0) | 
|  | continue; | 
|  |  | 
|  | snprintf(file, sizeof(file), "%s/ipifc/%d/ctl", | 
|  | conf.mpoint, nifc->index); | 
|  | cfd = open(file, O_RDWR); | 
|  | if (cfd < 0) { | 
|  | warning("can't open %s: %r", conf.mpoint); | 
|  | continue; | 
|  | } | 
|  | snprintf(buf, sizeof(buf), "remove %R %M", lifc->ip, | 
|  | lifc->mask); | 
|  | if (write(cfd, buf, strlen(buf)) != strlen(buf)) | 
|  | warning("can't remove %R %M from %s: %r", | 
|  | lifc->ip, lifc->mask, file); | 
|  | close(cfd); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void dounbind(void) | 
|  | { | 
|  | struct ipifc *nifc; | 
|  | char file[128]; | 
|  | int cfd; | 
|  |  | 
|  | ifc = readipifc(conf.mpoint, ifc, -1); | 
|  | for (nifc = ifc; nifc != NULL; nifc = nifc->next) { | 
|  | if (strcmp(nifc->dev, conf.dev) == 0) { | 
|  | snprintf(file, sizeof(file), "%s/ipifc/%d/ctl", | 
|  | conf.mpoint, nifc->index); | 
|  | cfd = open(file, O_RDWR); | 
|  | if (cfd < 0) { | 
|  | warning("can't open %s: %r", conf.mpoint); | 
|  | break; | 
|  | } | 
|  | if (write(cfd, "unbind", strlen("unbind")) < 0) | 
|  | warning("can't unbind from %s: %r", file); | 
|  | close(cfd); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* set the default route */ | 
|  | void adddefroute(char *mpoint, uint8_t *gaddr) | 
|  | { | 
|  | char buf[256]; | 
|  | int cfd; | 
|  |  | 
|  | sprintf(buf, "%s/iproute", mpoint); | 
|  | cfd = open(buf, O_RDWR); | 
|  | if (cfd < 0) | 
|  | return; | 
|  |  | 
|  | if (isv4(gaddr)) | 
|  | snprintf(buf, sizeof(buf), "add 0 0 %R", gaddr); | 
|  | else | 
|  | snprintf(buf, sizeof(buf), "add :: /0 %R", gaddr); | 
|  | write(cfd, buf, strlen(buf)); | 
|  | close(cfd); | 
|  | } | 
|  |  | 
|  | /* create a client id */ | 
|  | void mkclientid(void) | 
|  | { | 
|  | if ((strcmp(conf.type, "ether") == 0) || | 
|  | (strcmp(conf.type, "gbe") == 0)) { | 
|  | if (myetheraddr(conf.hwa, conf.dev) == 0) { | 
|  | conf.hwalen = 6; | 
|  | conf.hwatype = 1; | 
|  | conf.cid[0] = conf.hwatype; | 
|  | memmove(&conf.cid[1], conf.hwa, conf.hwalen); | 
|  | conf.cidlen = conf.hwalen + 1; | 
|  | } else { | 
|  | conf.hwatype = -1; | 
|  | snprintf((char *)conf.cid, sizeof(conf).cid, | 
|  | "plan9_%ld.%d", lrand48(), getpid()); | 
|  | conf.cidlen = strlen((char *)conf.cid); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* bind ip into the namespace */ | 
|  | void lookforip(char *net) | 
|  | { | 
|  | struct stat s; | 
|  | char proto[64]; | 
|  |  | 
|  | snprintf(proto, sizeof(proto), "%s/ipifc", net); | 
|  | if (stat(proto, &s) < 0) { | 
|  | fprintf(stderr, "no ip stack bound onto %s\n", net); | 
|  | evexit(-1); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* send some ctls to a device */ | 
|  | void controldevice(void) | 
|  | { | 
|  | char ctlfile[256]; | 
|  | int fd; | 
|  | struct ctl *cp; | 
|  |  | 
|  | if (firstctl == NULL || | 
|  | (strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0)) | 
|  | return; | 
|  |  | 
|  | snprintf(ctlfile, sizeof(ctlfile), "%s/clone", conf.dev); | 
|  | fd = open(ctlfile, O_RDWR); | 
|  | if (fd < 0) { | 
|  | fprintf(stderr, "can't open %s\n", ctlfile); | 
|  | evexit(-1); | 
|  | } | 
|  |  | 
|  | for (cp = firstctl; cp != NULL; cp = cp->next) { | 
|  | if (write(fd, cp->ctl, strlen(cp->ctl)) < 0) { | 
|  | fprintf(stderr, "ctl message %s: %r\n", cp->ctl); | 
|  | evexit(-1); | 
|  | } | 
|  | lseek(fd, 0, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* bind an ip stack to a device, leave the control channel open */ | 
|  | void binddevice(void) | 
|  | { | 
|  | char buf[256]; | 
|  |  | 
|  | if (myifc < 0) { | 
|  | /* get a new ip interface */ | 
|  | snprintf(buf, sizeof(buf), "%s/ipifc/clone", conf.mpoint); | 
|  | conf.cfd = open(buf, O_RDWR); | 
|  | if (conf.cfd < 0) { | 
|  | fprintf(stderr, "opening %s/ipifc/clone: %r\n", | 
|  | conf.mpoint); | 
|  | evexit(-1); | 
|  | } | 
|  |  | 
|  | /* specify medium as ethernet, bind the interface to it */ | 
|  | snprintf(buf, sizeof(buf), "bind %s %s", conf.type, conf.dev); | 
|  | if (write(conf.cfd, buf, strlen(buf)) != strlen(buf)) { | 
|  | fprintf(stderr, "%s: bind %s %s: %r\n", buf, conf.type, | 
|  | conf.dev); | 
|  | evexit(-1); | 
|  | } | 
|  | } else { | 
|  | /* open the old interface */ | 
|  | snprintf(buf, sizeof(buf), "%s/ipifc/%d/ctl", conf.mpoint, | 
|  | myifc); | 
|  | conf.cfd = open(buf, O_RDWR); | 
|  | if (conf.cfd < 0) { | 
|  | fprintf(stderr, "open %s: %r\n", buf); | 
|  | evexit(-1); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* add a logical interface to the ip stack */ | 
|  | int ip4cfg(void) | 
|  | { | 
|  | char buf[256]; | 
|  | int n; | 
|  |  | 
|  | if (!validip(conf.laddr)) | 
|  | return -1; | 
|  |  | 
|  | n = snprintf(buf, sizeof(buf), "add"); | 
|  | n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.laddr); | 
|  |  | 
|  | if (!validip(conf.mask)) | 
|  | ipmove(conf.mask, defmask(conf.laddr)); | 
|  | n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.mask); | 
|  |  | 
|  | if (validip(conf.raddr)) { | 
|  | n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.raddr); | 
|  | if (conf.mtu != 0) | 
|  | n += snprintf(buf + n, sizeof(buf) - n, " %d", | 
|  | conf.mtu); | 
|  | } | 
|  |  | 
|  | if (write(conf.cfd, buf, n) < 0) { | 
|  | warning("write(%s): %r", buf); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (beprimary == 1 && validip(conf.gaddr)) | 
|  | adddefroute(conf.mpoint, conf.gaddr); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* remove a logical interface to the ip stack */ | 
|  | void ipunconfig(void) | 
|  | { | 
|  | char buf[256]; | 
|  | int n; | 
|  |  | 
|  | if (!validip(conf.laddr)) | 
|  | return; | 
|  | DEBUG("couldn't renew IP lease, releasing %R", conf.laddr); | 
|  | n = sprintf(buf, "remove"); | 
|  | n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.laddr); | 
|  |  | 
|  | if (!validip(conf.mask)) | 
|  | ipmove(conf.mask, defmask(conf.laddr)); | 
|  | n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.mask); | 
|  |  | 
|  | write(conf.cfd, buf, n); | 
|  |  | 
|  | ipmove(conf.laddr, IPnoaddr); | 
|  | ipmove(conf.raddr, IPnoaddr); | 
|  | ipmove(conf.mask, IPnoaddr); | 
|  |  | 
|  | /* forget configuration info */ | 
|  | if (beprimary == 1) | 
|  | writendb("", 0, 0); | 
|  | } | 
|  |  | 
|  | void dhcpquery(int needconfig, int startstate) | 
|  | { | 
|  | char buf[256]; | 
|  |  | 
|  | if (needconfig) { | 
|  | snprintf(buf, sizeof(buf), "add %R %R", IPnoaddr, IPnoaddr); | 
|  | write(conf.cfd, buf, strlen(buf)); | 
|  | } | 
|  |  | 
|  | conf.fd = openlisten(); | 
|  | if (conf.fd < 0) { | 
|  | conf.state = Sinit; | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* try dhcp for 10 seconds */ | 
|  | conf.xid = lrand48(); | 
|  | conf.starttime = time(0); | 
|  | conf.state = startstate; | 
|  | switch (startstate) { | 
|  | case Sselecting: | 
|  | conf.offered = 0; | 
|  | dhcpsend(Discover); | 
|  | break; | 
|  | case Srenewing: | 
|  | dhcpsend(Request); | 
|  | break; | 
|  | default: | 
|  | fprintf(stderr, "internal error 0\n"); | 
|  | evexit(-1); | 
|  | } | 
|  | conf.resend = 0; | 
|  | conf.timeout = time(0) + 4; | 
|  |  | 
|  | while (conf.state != Sbound) { | 
|  | dhcprecv(); | 
|  | if (dhcptimer() < 0) | 
|  | break; | 
|  | if (time(0) - conf.starttime > 10) | 
|  | break; | 
|  | } | 
|  | close(conf.fd); | 
|  |  | 
|  | if (needconfig) { | 
|  | snprintf(buf, sizeof(buf), "remove %R %R", IPnoaddr, IPnoaddr); | 
|  | write(conf.cfd, buf, strlen(buf)); | 
|  | } | 
|  | } | 
|  |  | 
|  | enum { | 
|  | // This was an hour, but needs to be less for the ARM/GS1 until the | 
|  | // timer code has been cleaned up (pb). | 
|  | Maxsleep = 450, | 
|  | }; | 
|  |  | 
|  | static void *dhcpwatchthr(void *arg); | 
|  |  | 
|  | void dhcpwatch(int needconfig) | 
|  | { | 
|  | pthread_t tid; | 
|  | intptr_t nc = needconfig; | 
|  |  | 
|  | if (nodhcpwatch) | 
|  | return; | 
|  |  | 
|  | pthread_create(&tid, NULL, dhcpwatchthr, (void *)nc); | 
|  | } | 
|  |  | 
|  | static void *dhcpwatchthr(void *arg) | 
|  | { | 
|  | int secs, s; | 
|  | uint32_t t; | 
|  | int needconfig = (arg == NULL); | 
|  |  | 
|  | /* keep trying to renew the lease */ | 
|  | for (;;) { | 
|  | secs = conf.lease / 2; | 
|  | if (secs == 0) | 
|  | secs = 5; | 
|  |  | 
|  | /* avoid overflows */ | 
|  | for (s = secs; s > 0; s -= t) { | 
|  | t = s; | 
|  | if (t > Maxsleep) | 
|  | t = Maxsleep; | 
|  | usleep(t * 1000 * 1000); | 
|  | } | 
|  |  | 
|  | if (conf.lease > 0) { | 
|  | /* | 
|  | * during boot, the starttime can be bogus so avoid | 
|  | * spurious ipunconfig's | 
|  | */ | 
|  | t = time(0) - conf.starttime; | 
|  | if (t > (3 * secs) / 2) | 
|  | t = secs; | 
|  | if (t >= conf.lease) { | 
|  | conf.lease = 0; | 
|  | if (!noconfig) { | 
|  | ipunconfig(); | 
|  | needconfig = 1; | 
|  | } | 
|  | } else | 
|  | conf.lease -= t; | 
|  | } | 
|  | dhcpquery(needconfig, needconfig ? Sselecting : Srenewing); | 
|  |  | 
|  | if (needconfig && conf.state == Sbound) { | 
|  | if (ip4cfg() < 0) { | 
|  | fprintf(stderr, "can't start ip: %r\n"); | 
|  | evexit(-1); | 
|  | } | 
|  | needconfig = 0; | 
|  | /* | 
|  | * leave everything we've learned somewhere that | 
|  | * other procs can find it. | 
|  | */ | 
|  | if (beprimary == 1) { | 
|  | putndb(); | 
|  | tweakservers(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | int dhcptimer(void) | 
|  | { | 
|  | uint32_t now; | 
|  |  | 
|  | now = time(0); | 
|  | if (now < conf.timeout) | 
|  | return 0; | 
|  |  | 
|  | switch (conf.state) { | 
|  | default: | 
|  | fprintf(stderr, "dhcptimer: unknown state %d\n", conf.state); | 
|  | evexit(-1); | 
|  | case Sinit: | 
|  | case Sbound: | 
|  | break; | 
|  | case Sselecting: | 
|  | case Srequesting: | 
|  | case Srebinding: | 
|  | dhcpsend(conf.state == Sselecting ? Discover : Request); | 
|  | conf.timeout = now + 4; | 
|  | if (++conf.resend > 5) { | 
|  | conf.state = Sinit; | 
|  | return -1; | 
|  | } | 
|  | break; | 
|  | case Srenewing: | 
|  | dhcpsend(Request); | 
|  | conf.timeout = now + 1; | 
|  | if (++conf.resend > 3) { | 
|  | conf.state = Srebinding; | 
|  | conf.resend = 0; | 
|  | } | 
|  | break; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void dhcpsend(int type) | 
|  | { | 
|  | struct bootp bp; | 
|  | uint8_t *p; | 
|  | int n; | 
|  | uint8_t vendor[64]; | 
|  | struct udphdr *up = (struct udphdr *)bp.udphdr; | 
|  |  | 
|  | memset(&bp, 0, sizeof(bp)); | 
|  |  | 
|  | hnputs(up->rport, 67); | 
|  | bp.op = Bootrequest; | 
|  | hnputl(bp.xid, conf.xid); | 
|  | hnputs(bp.secs, time(0) - conf.starttime); | 
|  | hnputs(bp.flags, 0); | 
|  | memmove(bp.optmagic, optmagic, 4); | 
|  | if (conf.hwatype >= 0 && conf.hwalen < sizeof(bp).chaddr) { | 
|  | memmove(bp.chaddr, conf.hwa, conf.hwalen); | 
|  | bp.hlen = conf.hwalen; | 
|  | bp.htype = conf.hwatype; | 
|  | } | 
|  | p = bp.optdata; | 
|  | p = optaddbyte(p, ODtype, type); | 
|  | p = optadd(p, ODclientid, conf.cid, conf.cidlen); | 
|  | switch (type) { | 
|  | default: | 
|  | fprintf(stderr, "dhcpsend: unknown message type: %d\n", type); | 
|  | evexit(-1); | 
|  | case Discover: | 
|  | ipmove(up->raddr, IPv4bcast); /* broadcast */ | 
|  | if (*conf.hostname && sendhostname) | 
|  | p = optaddstr(p, OBhostname, conf.hostname); | 
|  | if (plan9) { | 
|  | n = snprintf((char *)vendor, sizeof(vendor), "plan9_%s", | 
|  | conf.cputype); | 
|  | p = optaddvec(p, ODvendorclass, vendor, n); | 
|  | } | 
|  | p = optaddvec(p, ODparams, requested, nrequested); | 
|  | if (validip(conf.laddr)) | 
|  | p = optaddaddr(p, ODipaddr, conf.laddr); | 
|  | break; | 
|  | case Request: | 
|  | switch (conf.state) { | 
|  | case Srenewing: | 
|  | ipmove(up->raddr, conf.server); | 
|  | v6tov4(bp.ciaddr, conf.laddr); | 
|  | break; | 
|  | case Srebinding: | 
|  | ipmove(up->raddr, IPv4bcast); /* broadcast */ | 
|  | v6tov4(bp.ciaddr, conf.laddr); | 
|  | break; | 
|  | case Srequesting: | 
|  | ipmove(up->raddr, IPv4bcast); /* broadcast */ | 
|  | p = optaddaddr(p, ODipaddr, conf.laddr); | 
|  | p = optaddaddr(p, ODserverid, conf.server); | 
|  | break; | 
|  | } | 
|  | p = optaddulong(p, ODlease, conf.offered); | 
|  | if (plan9) { | 
|  | n = snprintf((char *)vendor, sizeof(vendor), "plan9_%s", | 
|  | conf.cputype); | 
|  | p = optaddvec(p, ODvendorclass, vendor, n); | 
|  | } | 
|  | p = optaddvec(p, ODparams, requested, nrequested); | 
|  | if (*conf.hostname && sendhostname) | 
|  | p = optaddstr(p, OBhostname, conf.hostname); | 
|  | break; | 
|  | case Release: | 
|  | ipmove(up->raddr, conf.server); | 
|  | v6tov4(bp.ciaddr, conf.laddr); | 
|  | p = optaddaddr(p, ODipaddr, conf.laddr); | 
|  | p = optaddaddr(p, ODserverid, conf.server); | 
|  | break; | 
|  | } | 
|  |  | 
|  | *p++ = OBend; | 
|  |  | 
|  | n = p - (uint8_t *)&bp; | 
|  |  | 
|  | /* | 
|  | * We use a maximum size DHCP packet to survive the | 
|  | * All_Aboard NAT package from Internet Share.  It | 
|  | * always replies to DHCP requests with a packet of the | 
|  | * same size, so if the request is too short the reply | 
|  | * is truncated. | 
|  | */ | 
|  | if (write(conf.fd, &bp, sizeof(bp)) != sizeof(bp)) | 
|  | warning("dhcpsend: write failed: %r"); | 
|  | } | 
|  |  | 
|  | void rerrstr(char *buf, size_t buflen) | 
|  | { | 
|  | snprintf(buf, buflen, "%s", errstr()); | 
|  | } | 
|  |  | 
|  | void dhcprecv(void) | 
|  | { | 
|  | int i, n, type; | 
|  | uint32_t lease; | 
|  | char err[256]; | 
|  | uint8_t buf[8000], vopts[256], taddr[IPaddrlen]; | 
|  | struct bootp *bp; | 
|  | struct alarm_waiter waiter; | 
|  |  | 
|  | init_awaiter(&waiter, alarm_abort_sysc); | 
|  | waiter.data = current_uthread; | 
|  |  | 
|  | memset(buf, 0, sizeof(buf)); | 
|  | set_awaiter_rel(&waiter, 1000 * 1000); | 
|  | set_alarm(&waiter); | 
|  | n = read(conf.fd, buf, sizeof(buf)); | 
|  | unset_alarm(&waiter); | 
|  |  | 
|  | if (n < 0) { | 
|  | rerrstr(err, sizeof(err)); | 
|  | if (strstr(err, "syscall aborted") == NULL) | 
|  | warning("dhcprecv: bad read: %s", err); | 
|  | else | 
|  | DEBUG("dhcprecv: read timed out"); | 
|  | return; | 
|  | } | 
|  | if (n == 0) { | 
|  | warning("dhcprecv: zero-length packet read"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | bp = parsebootp(buf, n); | 
|  | if (bp == 0) { | 
|  | DEBUG("parsebootp failed: dropping packet"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | type = optgetbyte(bp->optdata, ODtype); | 
|  | switch (type) { | 
|  | default: | 
|  | warning("dhcprecv: unknown type: %d", type); | 
|  | break; | 
|  | case Offer: | 
|  | DEBUG("got offer from %R ", bp->siaddr); | 
|  | if (conf.state != Sselecting) { | 
|  | DEBUG(""); | 
|  | break; | 
|  | } | 
|  | lease = optgetulong(bp->optdata, ODlease); | 
|  | if (lease == 0) { | 
|  | /* | 
|  | * The All_Aboard NAT package from Internet Share | 
|  | * doesn't give a lease time, so we have to assume one. | 
|  | */ | 
|  | warning("Offer with %lud lease, using %d", lease, | 
|  | MinLease); | 
|  | lease = MinLease; | 
|  | } | 
|  | DEBUG("lease=%lud ", lease); | 
|  | if (!optgetaddr(bp->optdata, ODserverid, conf.server)) { | 
|  | warning("Offer from server with invalid serverid"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | v4tov6(conf.laddr, bp->yiaddr); | 
|  | memmove(conf.sname, bp->sname, sizeof(conf).sname); | 
|  | conf.sname[sizeof(conf).sname - 1] = 0; | 
|  | DEBUG("server=%R sname=%s", conf.server, conf.sname); | 
|  | conf.offered = lease; | 
|  | conf.state = Srequesting; | 
|  | dhcpsend(Request); | 
|  | conf.resend = 0; | 
|  | conf.timeout = time(0) + 4; | 
|  | break; | 
|  | case Ack: | 
|  | DEBUG("got ack from %R ", bp->siaddr); | 
|  | if (conf.state != Srequesting && conf.state != Srenewing && | 
|  | conf.state != Srebinding) | 
|  | break; | 
|  |  | 
|  | /* ignore a bad lease */ | 
|  | lease = optgetulong(bp->optdata, ODlease); | 
|  | if (lease == 0) { | 
|  | /* | 
|  | * The All_Aboard NAT package from Internet Share | 
|  | * doesn't give a lease time, so we have to assume one. | 
|  | */ | 
|  | warning("Ack with %lud lease, using %d", lease, | 
|  | MinLease); | 
|  | lease = MinLease; | 
|  | } | 
|  | DEBUG("lease=%lud ", lease); | 
|  |  | 
|  | /* address and mask */ | 
|  | if (!validip(conf.laddr) || !Oflag) | 
|  | v4tov6(conf.laddr, bp->yiaddr); | 
|  | if (!validip(conf.mask) || !Oflag) { | 
|  | if (!optgetaddr(bp->optdata, OBmask, conf.mask)) | 
|  | ipmove(conf.mask, IPnoaddr); | 
|  | } | 
|  | DEBUG("ipaddr=%R ipmask=%M ", conf.laddr, conf.mask); | 
|  |  | 
|  | /* | 
|  | * get a router address either from the router option | 
|  | * or from the router that forwarded the dhcp packet | 
|  | */ | 
|  | if (validip(conf.gaddr) && Oflag) { | 
|  | DEBUG("ipgw=%R ", conf.gaddr); | 
|  | } else if (optgetaddr(bp->optdata, OBrouter, conf.gaddr)) { | 
|  | DEBUG("ipgw=%R ", conf.gaddr); | 
|  | } else if (memcmp(bp->giaddr, IPnoaddr + IPv4off, IPv4addrlen) | 
|  | != 0) { | 
|  | v4tov6(conf.gaddr, bp->giaddr); | 
|  | DEBUG("giaddr=%R ", conf.gaddr); | 
|  | } | 
|  |  | 
|  | /* get dns servers */ | 
|  | memset(conf.dns, 0, sizeof(conf).dns); | 
|  | n = optgetaddrs(bp->optdata, OBdnserver, conf.dns, | 
|  | sizeof(conf).dns / IPaddrlen); | 
|  | for (i = 0; i < n; i++) | 
|  | DEBUG("dns=%R ", conf.dns + i * IPaddrlen); | 
|  |  | 
|  | /* get ntp servers */ | 
|  | memset(conf.ntp, 0, sizeof(conf).ntp); | 
|  | n = optgetaddrs(bp->optdata, OBntpserver, conf.ntp, | 
|  | sizeof(conf).ntp / IPaddrlen); | 
|  | for (i = 0; i < n; i++) | 
|  | DEBUG("ntp=%R ", conf.ntp + i * IPaddrlen); | 
|  |  | 
|  | /* get names */ | 
|  | optgetstr(bp->optdata, OBhostname, | 
|  | conf.hostname, sizeof(conf).hostname); | 
|  | optgetstr(bp->optdata, OBdomainname, | 
|  | conf.domainname, sizeof(conf).domainname); | 
|  |  | 
|  | /* get anything else we asked for */ | 
|  | getoptions(bp->optdata); | 
|  |  | 
|  | /* get plan9-specific options */ | 
|  | n = optgetvec(bp->optdata, OBvendorinfo, vopts, | 
|  | sizeof(vopts) - 1); | 
|  | if (n > 0 && parseoptions(vopts, n) == 0) { | 
|  | n = 1; | 
|  | if (!validip(conf.fs) || !Oflag) { | 
|  | n = optgetp9addrs(vopts, OP9fs, conf.fs, 2); | 
|  | if (n == 0) | 
|  | n = optgetaddrs(vopts, OP9fsv4, conf.fs, | 
|  | 2); | 
|  | } | 
|  | for (i = 0; i < n; i++) | 
|  | DEBUG("fs=%R ", conf.fs + i * IPaddrlen); | 
|  |  | 
|  | n = 1; | 
|  | if (!validip(conf.auth) || !Oflag) { | 
|  | n = optgetp9addrs(vopts, OP9auth, conf.auth, 2); | 
|  | if (n == 0) | 
|  | n = optgetaddrs(vopts, OP9authv4, | 
|  | conf.auth, 2); | 
|  | } | 
|  | for (i = 0; i < n; i++) | 
|  | DEBUG("auth=%R ", conf.auth + i * IPaddrlen); | 
|  |  | 
|  | n = optgetp9addrs(vopts, OP9ipaddr, taddr, 1); | 
|  | if (n > 0) | 
|  | memmove(conf.laddr, taddr, IPaddrlen); | 
|  | n = optgetp9addrs(vopts, OP9ipmask, taddr, 1); | 
|  | if (n > 0) | 
|  | memmove(conf.mask, taddr, IPaddrlen); | 
|  | n = optgetp9addrs(vopts, OP9ipgw, taddr, 1); | 
|  | if (n > 0) | 
|  | memmove(conf.gaddr, taddr, IPaddrlen); | 
|  | DEBUG("new ipaddr=%R new ipmask=%M new ipgw=%R", | 
|  | conf.laddr, conf.mask, conf.gaddr); | 
|  | } | 
|  | conf.lease = lease; | 
|  | conf.state = Sbound; | 
|  | DEBUG("server=%R sname=%s", conf.server, conf.sname); | 
|  | break; | 
|  | case Nak: | 
|  | conf.state = Sinit; | 
|  | warning("recved dhcpnak on %s", conf.mpoint); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* return pseudo-random integer in range low...(hi-1) */ | 
|  | uint32_t randint(uint32_t low, uint32_t hi) | 
|  | { | 
|  | if (hi < low) | 
|  | return low; | 
|  | return low + (lrand48() % hi); | 
|  | } | 
|  |  | 
|  | // compute small pseudo-random delay in ms | 
|  | long jitter(void) | 
|  | { | 
|  | return randint(0, 10 * 1000); | 
|  | } | 
|  |  | 
|  | int openlisten(void) | 
|  | { | 
|  | int n, fd, cfd; | 
|  | char data[128], devdir[40]; | 
|  |  | 
|  | if (validip(conf.laddr) && | 
|  | (conf.state == Srenewing || conf.state == Srebinding)) | 
|  | sprintf(data, "%s/udp!%R!68", conf.mpoint, conf.laddr); | 
|  | else | 
|  | sprintf(data, "%s/udp!*!68", conf.mpoint); | 
|  | for (n = 0; (cfd = announce9(data, devdir, 0)) < 0; n++) { | 
|  | if (!noconfig) { | 
|  | fprintf(stderr, "can't announce for dhcp: %r\n"); | 
|  | evexit(-1); | 
|  | } | 
|  |  | 
|  | /* might be another client - wait and try again */ | 
|  | warning("can't announce %s: %r", data); | 
|  | usleep(jitter() * 1000); | 
|  | if (n > 10) | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (write(cfd, "headers", strlen("headers")) < 0) { | 
|  | fprintf(stderr, "can't set header mode: %r\n"); | 
|  | evexit(-1); | 
|  | } | 
|  |  | 
|  | sprintf(data, "%s/data", devdir); | 
|  | fd = open(data, O_RDWR); | 
|  | if (fd < 0) { | 
|  | fprintf(stderr, "open %s: %r\n", data); | 
|  | evexit(-1); | 
|  | } | 
|  | close(cfd); | 
|  | return fd; | 
|  | } | 
|  |  | 
|  | uint8_t *optadd(uint8_t *p, int op, void *d, int n) | 
|  | { | 
|  | p[0] = op; | 
|  | p[1] = n; | 
|  | memmove(p + 2, d, n); | 
|  | return p + n + 2; | 
|  | } | 
|  |  | 
|  | uint8_t *optaddbyte(uint8_t *p, int op, int b) | 
|  | { | 
|  | p[0] = op; | 
|  | p[1] = 1; | 
|  | p[2] = b; | 
|  | return p + 3; | 
|  | } | 
|  |  | 
|  | uint8_t *optaddulong(uint8_t *p, int op, uint32_t x) | 
|  | { | 
|  | p[0] = op; | 
|  | p[1] = 4; | 
|  | hnputl(p + 2, x); | 
|  | return p + 6; | 
|  | } | 
|  |  | 
|  | uint8_t *optaddaddr(uint8_t *p, int op, uint8_t *ip) | 
|  | { | 
|  | p[0] = op; | 
|  | p[1] = 4; | 
|  | v6tov4(p + 2, ip); | 
|  | return p + 6; | 
|  | } | 
|  |  | 
|  | /* add dhcp option op with value v of length n to dhcp option array p */ | 
|  | uint8_t *optaddvec(uint8_t *p, int op, uint8_t *v, int n) | 
|  | { | 
|  | p[0] = op; | 
|  | p[1] = n; | 
|  | memmove(p + 2, v, n); | 
|  | return p + 2 + n; | 
|  | } | 
|  |  | 
|  | uint8_t *optaddstr(uint8_t *p, int op, char *v) | 
|  | { | 
|  | int n; | 
|  |  | 
|  | n = strlen(v) + 1; /* microsoft leaves on the NUL, so we do too */ | 
|  | p[0] = op; | 
|  | p[1] = n; | 
|  | memmove(p + 2, v, n); | 
|  | return p + 2 + n; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * parse p, looking for option `op'.  if non-nil, np points to minimum length. | 
|  | * return NULL if option is too small, else ptr to opt, and | 
|  | * store actual length via np if non-nil. | 
|  | */ | 
|  | uint8_t *optget(uint8_t *p, int op, int *np) | 
|  | { | 
|  | int len, code; | 
|  |  | 
|  | while ((code = *p++) != OBend) { | 
|  | if (code == OBpad) | 
|  | continue; | 
|  | len = *p++; | 
|  | if (code != op) { | 
|  | p += len; | 
|  | continue; | 
|  | } | 
|  | if (np != NULL) { | 
|  | if (*np > len) | 
|  | return 0; | 
|  | *np = len; | 
|  | } | 
|  | return p; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int optgetbyte(uint8_t *p, int op) | 
|  | { | 
|  | int len; | 
|  |  | 
|  | len = 1; | 
|  | p = optget(p, op, &len); | 
|  | if (p == NULL) | 
|  | return 0; | 
|  | return *p; | 
|  | } | 
|  |  | 
|  | uint32_t optgetulong(uint8_t *p, int op) | 
|  | { | 
|  | int len; | 
|  |  | 
|  | len = 4; | 
|  | p = optget(p, op, &len); | 
|  | if (p == NULL) | 
|  | return 0; | 
|  | return nhgetl(p); | 
|  | } | 
|  |  | 
|  | int optgetaddr(uint8_t *p, int op, uint8_t *ip) | 
|  | { | 
|  | int len; | 
|  |  | 
|  | len = 4; | 
|  | p = optget(p, op, &len); | 
|  | if (p == NULL) | 
|  | return 0; | 
|  | v4tov6(ip, p); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* expect at most n addresses; ip[] only has room for that many */ | 
|  | int optgetaddrs(uint8_t *p, int op, uint8_t *ip, int n) | 
|  | { | 
|  | int len, i; | 
|  |  | 
|  | len = 4; | 
|  | p = optget(p, op, &len); | 
|  | if (p == NULL) | 
|  | return 0; | 
|  | len /= IPv4addrlen; | 
|  | if (len > n) | 
|  | len = n; | 
|  | for (i = 0; i < len; i++) | 
|  | v4tov6(&ip[i * IPaddrlen], &p[i * IPv4addrlen]); | 
|  | return i; | 
|  | } | 
|  |  | 
|  | /* expect at most n addresses; ip[] only has room for that many */ | 
|  | int optgetp9addrs(uint8_t *ap, int op, uint8_t *ip, int n) | 
|  | { | 
|  | int len, i, slen, addrs; | 
|  | char *p; | 
|  |  | 
|  | len = 1; /* minimum bytes needed */ | 
|  | p = (char *)optget(ap, op, &len); | 
|  | if (p == NULL) | 
|  | return 0; | 
|  | addrs = *p++; /* first byte is address count */ | 
|  | for (i = 0; i < n && i < addrs && len > 0; i++) { | 
|  | slen = strlen(p) + 1; | 
|  | if (parseip(&ip[i * IPaddrlen], p) == -1) | 
|  | fprintf(stderr, "%s: bad address %s\n", argv0, p); | 
|  | DEBUG("got plan 9 option %d addr %R (%s)", op, &ip[i * IPaddrlen], p); | 
|  | p += slen; | 
|  | len -= slen; | 
|  | } | 
|  | return addrs; | 
|  | } | 
|  |  | 
|  | int optgetvec(uint8_t *p, int op, uint8_t *v, int n) | 
|  | { | 
|  | int len; | 
|  |  | 
|  | len = 1; | 
|  | p = optget(p, op, &len); | 
|  | if (p == NULL) | 
|  | return 0; | 
|  | if (len > n) | 
|  | len = n; | 
|  | memmove(v, p, len); | 
|  | return len; | 
|  | } | 
|  |  | 
|  | int optgetstr(uint8_t *p, int op, char *s, int n) | 
|  | { | 
|  | int len; | 
|  |  | 
|  | len = 1; | 
|  | p = optget(p, op, &len); | 
|  | if (p == NULL) | 
|  | return 0; | 
|  | if (len >= n) | 
|  | len = n - 1; | 
|  | memmove(s, p, len); | 
|  | s[len] = 0; | 
|  | return len; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * sanity check options area | 
|  | *	- options don't overflow packet | 
|  | *	- options end with an OBend | 
|  | */ | 
|  | int parseoptions(uint8_t *p, int n) | 
|  | { | 
|  | int code, len, nin = n; | 
|  |  | 
|  | while (n > 0) { | 
|  | code = *p++; | 
|  | n--; | 
|  | if (code == OBend) | 
|  | return 0; | 
|  | if (code == OBpad) | 
|  | continue; | 
|  | if (n == 0) { | 
|  | warning( | 
|  | "parseoptions: bad option: 0x%x: truncated: opt length = %d", | 
|  | code, nin); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | len = *p++; | 
|  | n--; | 
|  | DEBUG("parseoptions: %s(%d) len %d, bytes left %d", | 
|  | option[code].name, code, len, n); | 
|  | if (len > n) { | 
|  | warning( | 
|  | "parseoptions: bad option: 0x%x: %d > %d: opt length = %d", | 
|  | code, len, n, nin); | 
|  | return -1; | 
|  | } | 
|  | p += len; | 
|  | n -= len; | 
|  | } | 
|  |  | 
|  | /* make sure packet ends with an OBend after all the optget code */ | 
|  | *p = OBend; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * sanity check received packet: | 
|  | *	- magic is dhcp magic | 
|  | *	- options don't overflow packet | 
|  | */ | 
|  | struct bootp *parsebootp(uint8_t *p, int n) | 
|  | { | 
|  | struct bootp *bp; | 
|  |  | 
|  | bp = (struct bootp *)p; | 
|  | if (n < bp->optmagic - p) { | 
|  | warning( | 
|  | "parsebootp: short bootp packet; with options, need %d bytes, got %d", | 
|  | bp->optmagic - p, n); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | if (conf.xid != nhgetl(bp->xid)) /* not meant for us */ | 
|  | return NULL; | 
|  |  | 
|  | if (bp->op != Bootreply) { | 
|  | warning("parsebootp: bad op %d", bp->op); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | n -= bp->optmagic - p; | 
|  | p = bp->optmagic; | 
|  |  | 
|  | if (n < 4) { | 
|  | warning("parsebootp: no option data"); | 
|  | return NULL; | 
|  | } | 
|  | if (memcmp(optmagic, p, 4) != 0) { | 
|  | warning("parsebootp: bad opt magic %x %x %x %x", | 
|  | p[0], p[1], p[2], p[3]); | 
|  | return NULL; | 
|  | } | 
|  | p += 4; | 
|  | n -= 4; | 
|  | DEBUG("parsebootp: new packet"); | 
|  | if (parseoptions(p, n) < 0) | 
|  | return NULL; | 
|  | return bp; | 
|  | } | 
|  |  | 
|  | /* write out an ndb entry */ | 
|  | void writendb(char *s, int n, int append) | 
|  | { | 
|  | char file[64]; | 
|  | int fd; | 
|  |  | 
|  | snprintf(file, sizeof(file), "%s/ndb", conf.mpoint); | 
|  | if (append) { | 
|  | fd = open(file, O_WRITE); | 
|  | lseek(fd, 0, 2); | 
|  | } else | 
|  | fd = open(file, O_WRITE | O_TRUNC); | 
|  | write(fd, s, n); | 
|  | close(fd); | 
|  | } | 
|  |  | 
|  | /* put server addresses into the ndb entry */ | 
|  | size_t putaddrs(char *buf, size_t size, char *attr, uint8_t *a, int len) | 
|  | { | 
|  | int i; | 
|  | size_t n; | 
|  | char *p; | 
|  |  | 
|  | n = 0; | 
|  | p = ""; | 
|  | for (i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen) { | 
|  | n += snprintf(buf + n, size - n, "%s%s=%R\n", p, attr, a); | 
|  | p = " "; | 
|  | } | 
|  |  | 
|  | return n; | 
|  | } | 
|  |  | 
|  | /* make an ndb entry and put it into /net/ndb for the servers to see */ | 
|  | void putndb(void) | 
|  | { | 
|  | int append; | 
|  | char buf[1024]; | 
|  | char *np; | 
|  | size_t n; | 
|  |  | 
|  | buf[0] = '\0'; | 
|  | n = 0; | 
|  | if (getndb() == 0) | 
|  | append = 1; | 
|  | else { | 
|  | append = 0; | 
|  | n += snprintf(buf + n, sizeof(buf) - n, | 
|  | "ip=%R ipmask=%M ipgw=%R\n", | 
|  | conf.laddr, conf.mask, conf.gaddr); | 
|  | } | 
|  | np = strchr(conf.hostname, '.'); | 
|  | if (np != NULL) { | 
|  | if (*conf.domainname == 0) | 
|  | snprintf(conf.domainname, sizeof(conf).domainname, "%s", | 
|  | np + 1); | 
|  | *np = 0; | 
|  | } | 
|  | if (*conf.hostname) | 
|  | n += snprintf(buf + n, sizeof(buf) - n, "\tsys=%s\n", | 
|  | conf.hostname); | 
|  | if (*conf.domainname) | 
|  | n += snprintf(buf + n, sizeof(buf) - n, "\tdom=%s.%s\n", | 
|  | conf.hostname, conf.domainname); | 
|  | if (validip(conf.fs)) | 
|  | n += putaddrs(buf + n, sizeof(buf) - n, "\tfs", | 
|  | conf.fs, sizeof(conf).fs); | 
|  | if (validip(conf.auth)) | 
|  | n += putaddrs(buf + n, sizeof(buf) - n, "\tauth", | 
|  | conf.auth, sizeof(conf).auth); | 
|  | if (validip(conf.dns)) | 
|  | n += putaddrs(buf + n, sizeof(buf) - n, "\tdns", | 
|  | conf.dns, sizeof(conf).dns); | 
|  | if (validip(conf.ntp)) | 
|  | n += putaddrs(buf + n, sizeof(buf) - n, "\tntp", | 
|  | conf.ntp, sizeof(conf).ntp); | 
|  | if (ndboptions) | 
|  | n += snprintf(buf + n, sizeof(buf) - n, "%s\n", ndboptions); | 
|  | if (n > 0) | 
|  | writendb(buf, n, append); | 
|  | } | 
|  |  | 
|  | /* get an ndb entry someone else wrote */ | 
|  | int getndb(void) | 
|  | { | 
|  | char buf[1024]; | 
|  | int fd, n; | 
|  | char *p; | 
|  |  | 
|  | snprintf(buf, sizeof(buf), "%s/ndb", conf.mpoint); | 
|  | fd = open(buf, O_RDONLY); | 
|  | n = read(fd, buf, sizeof(buf) - 1); | 
|  | close(fd); | 
|  | if (n <= 0) | 
|  | return -1; | 
|  | buf[n] = 0; | 
|  | p = strstr(buf, "ip="); | 
|  | if (p == NULL) | 
|  | return -1; | 
|  | if (parseip(conf.laddr, p + 3) == -1) | 
|  | fprintf(stderr, "%s: bad address %s\n", argv0, p + 3); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* tell a server to refresh */ | 
|  | void tweakserver(char *server) | 
|  | { | 
|  | int fd; | 
|  | char file[64]; | 
|  |  | 
|  | snprintf(file, sizeof(file), "%s/%s", conf.mpoint, server); | 
|  | fd = open(file, O_RDWR); | 
|  | if (fd < 0) | 
|  | return; | 
|  | write(fd, "refresh", strlen("refresh")); | 
|  | close(fd); | 
|  | } | 
|  |  | 
|  | /* tell all servers to refresh their information */ | 
|  | void tweakservers(void) | 
|  | { | 
|  | tweakserver("dns"); | 
|  | tweakserver("cs"); | 
|  | } | 
|  |  | 
|  | /* return number of networks */ | 
|  | int nipifcs(char *net) | 
|  | { | 
|  | int n; | 
|  | struct ipifc *nifc; | 
|  | struct iplifc *lifc; | 
|  |  | 
|  | n = 0; | 
|  | ifc = readipifc(net, ifc, -1); | 
|  | for (nifc = ifc; nifc != NULL; nifc = nifc->next) { | 
|  | /* | 
|  | * ignore loopback devices when trying to | 
|  | * figure out if we're the primary interface. | 
|  | */ | 
|  | if (strcmp(nifc->dev, "/dev/null") != 0) | 
|  | for (lifc = nifc->lifc; lifc != NULL; lifc = lifc->next) | 
|  | if (validip(lifc->ip)) { | 
|  | n++; | 
|  | break; | 
|  | } | 
|  | if (strcmp(nifc->dev, conf.dev) == 0) | 
|  | myifc = nifc->index; | 
|  | } | 
|  | return n; | 
|  | } | 
|  |  | 
|  | /* return true if this is a valid v4 address */ | 
|  | int validip(uint8_t *addr) | 
|  | { | 
|  | return ipcmp(addr, IPnoaddr) != 0 && ipcmp(addr, v4prefix) != 0; | 
|  | } | 
|  |  | 
|  | /* look for an action */ | 
|  | int parseverb(char *name) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < COUNT_OF(verbs); i++) | 
|  | if (verbs[i] != NULL && strcmp(name, verbs[i]) == 0) | 
|  | return i; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* get everything out of ndb */ | 
|  | void ndbconfig(void) | 
|  | { | 
|  | int nattr, nauth = 0, ndns = 0, nfs = 0, ok; | 
|  | char etheraddr[32]; | 
|  | char *attrs[10]; | 
|  | struct ndb *db; | 
|  | struct ndbtuple *t, *nt; | 
|  |  | 
|  | db = ndbopen(0); | 
|  | if (db == NULL) { | 
|  | fprintf(stderr, "can't open ndb: %r\n"); | 
|  | evexit(-1); | 
|  | } | 
|  | if ((strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0) | 
|  | || myetheraddr(conf.hwa, conf.dev) != 0) { | 
|  | fprintf(stderr, "can't read hardware address\n"); | 
|  | evexit(-1); | 
|  | } | 
|  | snprintf(etheraddr, sizeof(etheraddr), "%E", conf.hwa); | 
|  | nattr = 0; | 
|  | attrs[nattr++] = "ip"; | 
|  | attrs[nattr++] = "ipmask"; | 
|  | attrs[nattr++] = "ipgw"; | 
|  | /* the @ triggers resolution to an IP address; see ndb(2) */ | 
|  | attrs[nattr++] = "@dns"; | 
|  | attrs[nattr++] = "@ntp"; | 
|  | attrs[nattr++] = "@fs"; | 
|  | attrs[nattr++] = "@auth"; | 
|  | attrs[nattr] = NULL; | 
|  | t = ndbipinfo(db, "ether", etheraddr, attrs, nattr); | 
|  | for (nt = t; nt != NULL; nt = nt->entry) { | 
|  | ok = 1; | 
|  | if (strcmp(nt->attr, "ip") == 0) | 
|  | ok = parseip(conf.laddr, nt->val); | 
|  | else if (strcmp(nt->attr, "ipmask") == 0) | 
|  | parseipmask(conf.mask, nt->val); /* could be -1 */ | 
|  | else if (strcmp(nt->attr, "ipgw") == 0) | 
|  | ok = parseip(conf.gaddr, nt->val); | 
|  | else if (ndns < 2 && strcmp(nt->attr, "dns") == 0) | 
|  | ok = parseip(conf.dns + IPaddrlen * ndns, nt->val); | 
|  | else if (strcmp(nt->attr, "ntp") == 0) | 
|  | ok = parseip(conf.ntp, nt->val); | 
|  | else if (nfs < 2 && strcmp(nt->attr, "fs") == 0) | 
|  | ok = parseip(conf.fs + IPaddrlen * nfs, nt->val); | 
|  | else if (nauth < 2 && strcmp(nt->attr, "auth") == 0) | 
|  | ok = parseip(conf.auth + IPaddrlen * nauth, nt->val); | 
|  | if (!ok) | 
|  | fprintf(stderr, "%s: bad %s address in ndb: %s\n", | 
|  | argv0, nt->attr, nt->val); | 
|  | } | 
|  | ndbfree(t); | 
|  | if (!validip(conf.laddr)) { | 
|  | fprintf(stderr, "address not found in ndb\n"); | 
|  | evexit(-1); | 
|  | } | 
|  | } | 
|  |  | 
|  | int addoption(char *opt) | 
|  | { | 
|  | int i; | 
|  | struct option *o; | 
|  |  | 
|  | if (opt == NULL) | 
|  | return -1; | 
|  | for (o = option; o < &option[COUNT_OF(option)]; o++) | 
|  | if (o->name && strcmp(opt, o->name) == 0) { | 
|  | i = o - option; | 
|  | if (memchr(requested, i, nrequested) == 0 && | 
|  | nrequested < COUNT_OF(requested)) | 
|  | requested[nrequested++] = i; | 
|  | return 0; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | char *optgetx(uint8_t *p, uint8_t opt) | 
|  | { | 
|  | int i, n; | 
|  | uint32_t x; | 
|  | char str[256], buf[1024]; | 
|  | uint8_t ip[IPaddrlen], ips[16 * IPaddrlen], vec[256]; | 
|  | size_t l; | 
|  | struct option *o; | 
|  |  | 
|  | o = &option[opt]; | 
|  | if (o->name == NULL) | 
|  | return NULL; | 
|  |  | 
|  | memset(buf, '\0', sizeof(buf)); | 
|  | switch (o->type) { | 
|  | case Taddr: | 
|  | if (optgetaddr(p, opt, ip)) | 
|  | snprintf(buf, sizeof(buf), "%s=%R", o->name, ip); | 
|  | break; | 
|  | case Taddrs: | 
|  | n = optgetaddrs(p, opt, ips, 16); | 
|  | if (n > 0) | 
|  | l = snprintf(buf, sizeof(buf), "%s=%R", o->name, ips); | 
|  | for (i = 1; i < n; i++) { | 
|  | l += snprintf(buf + l, sizeof(buf) - l, " %s=%R", | 
|  | o->name, &ips[i * IPaddrlen]); | 
|  | } | 
|  | break; | 
|  | case Tulong: | 
|  | x = optgetulong(p, opt); | 
|  | if (x != 0) | 
|  | snprintf(buf, sizeof(buf), "%s=%lud", o->name, x); | 
|  | break; | 
|  | case Tbyte: | 
|  | x = optgetbyte(p, opt); | 
|  | if (x != 0) | 
|  | snprintf(buf, sizeof(buf), "%s=%lud", o->name, x); | 
|  | break; | 
|  | case Tstr: | 
|  | if (optgetstr(p, opt, str, sizeof(str))) | 
|  | snprintf(buf, sizeof(buf), "%s=%s", o->name, str); | 
|  | break; | 
|  | case Tvec: | 
|  | n = optgetvec(p, opt, vec, sizeof(vec)); | 
|  | if (n > 0) /* what's %H?  it's not installed */ | 
|  | snprintf(buf, sizeof(buf), "%s=%.*H", o->name, n, vec); | 
|  | break; | 
|  | } | 
|  | return strdup(buf); | 
|  | } | 
|  |  | 
|  | void getoptions(uint8_t *p) | 
|  | { | 
|  | int i; | 
|  | char buf[1024]; | 
|  | char *s; | 
|  |  | 
|  | for (i = COUNT_OF(defrequested); i < nrequested; i++) { | 
|  | s = optgetx(p, requested[i]); | 
|  | if (s == NULL) | 
|  | continue; | 
|  | DEBUG("%s ", s); | 
|  | snprintf(buf, sizeof(buf), | 
|  | (ndboptions == NULL) ? "\t%s" : "\t%s%s", | 
|  | s, ndboptions); | 
|  | free(ndboptions); | 
|  | ndboptions = strdup(buf); | 
|  | free(s); | 
|  | } | 
|  | } |