| /* | 
 |  * 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); | 
 | 	} | 
 | } |