xref: /plan9-contrib/sys/src/cmd/ip/ipconfig/main.c (revision ed2a258a218962e0b4e0f5cbbab48c6ce1bcbf02)
1 /*
2  * ipconfig - configure parameters of an ip stack
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <ip.h>
7 #include <bio.h>
8 #include <ndb.h>
9 #include "../dhcp.h"
10 #include "ipconfig.h"
11 
12 #define DEBUG if(debug)warning
13 
14 /* possible verbs */
15 enum
16 {
17 	/* commands */
18 	Vadd,
19 	Vremove,
20 	Vunbind,
21 	Vaddpref6,
22 	Vra6,
23 	/* media */
24 	Vether,
25 	Vgbe,
26 	Vppp,
27 	Vloopback,
28 	Vtorus,
29 	Vtree,
30 };
31 
32 enum
33 {
34 	Taddr,
35 	Taddrs,
36 	Tstr,
37 	Tbyte,
38 	Tulong,
39 	Tvec,
40 };
41 
42 typedef struct Option Option;
43 struct Option
44 {
45 	char	*name;
46 	int	type;
47 };
48 
49 /*
50  * I was too lazy to look up the types for each of these
51  * options.  If someone feels like it, please mail me a
52  * corrected array -- presotto
53  */
54 Option option[256] =
55 {
56 [OBmask]		{ "ipmask",		Taddr },
57 [OBtimeoff]		{ "timeoff",		Tulong },
58 [OBrouter]		{ "ipgw",		Taddrs },
59 [OBtimeserver]		{ "time",		Taddrs },
60 [OBnameserver]		{ "name",		Taddrs },
61 [OBdnserver]		{ "dns",		Taddrs },
62 [OBlogserver]		{ "log",		Taddrs },
63 [OBcookieserver]	{ "cookie",		Taddrs },
64 [OBlprserver]		{ "lpr",		Taddrs },
65 [OBimpressserver]	{ "impress",		Taddrs },
66 [OBrlserver]		{ "rl",			Taddrs },
67 [OBhostname]		{ "sys",		Tstr },
68 [OBbflen]		{ "bflen",		Tulong },
69 [OBdumpfile]		{ "dumpfile",		Tstr },
70 [OBdomainname]		{ "dom",		Tstr },
71 [OBswapserver]		{ "swap",		Taddrs },
72 [OBrootpath]		{ "rootpath",		Tstr },
73 [OBextpath]		{ "extpath",		Tstr },
74 [OBipforward]		{ "ipforward",		Taddrs },
75 [OBnonlocal]		{ "nonlocal",		Taddrs },
76 [OBpolicyfilter]	{ "policyfilter",	Taddrs },
77 [OBmaxdatagram]		{ "maxdatagram",	Tulong },
78 [OBttl]			{ "ttl",		Tulong },
79 [OBpathtimeout]		{ "pathtimeout",	Taddrs },
80 [OBpathplateau]		{ "pathplateau",	Taddrs },
81 [OBmtu]			{ "mtu",		Tulong },
82 [OBsubnetslocal]	{ "subnetslocal",	Taddrs },
83 [OBbaddr]		{ "baddr",		Taddrs },
84 [OBdiscovermask]	{ "discovermask",	Taddrs },
85 [OBsupplymask]		{ "supplymask",		Taddrs },
86 [OBdiscoverrouter]	{ "discoverrouter",	Taddrs },
87 [OBrsserver]		{ "rs",			Taddrs },
88 [OBstaticroutes]	{ "staticroutes",	Taddrs },
89 [OBtrailerencap]	{ "trailerencap",	Taddrs },
90 [OBarptimeout]		{ "arptimeout",		Tulong },
91 [OBetherencap]		{ "etherencap",		Taddrs },
92 [OBtcpttl]		{ "tcpttl",		Tulong },
93 [OBtcpka]		{ "tcpka",		Tulong },
94 [OBtcpkag]		{ "tcpkag",		Tulong },
95 [OBnisdomain]		{ "nisdomain",		Tstr },
96 [OBniserver]		{ "ni",			Taddrs },
97 [OBntpserver]		{ "ntp",		Taddrs },
98 [OBnetbiosns]		{ "netbiosns",		Taddrs },
99 [OBnetbiosdds]		{ "netbiosdds",		Taddrs },
100 [OBnetbiostype]		{ "netbiostype",	Taddrs },
101 [OBnetbiosscope]	{ "netbiosscope",	Taddrs },
102 [OBxfontserver]		{ "xfont",		Taddrs },
103 [OBxdispmanager]	{ "xdispmanager",	Taddrs },
104 [OBnisplusdomain]	{ "nisplusdomain",	Tstr },
105 [OBnisplusserver]	{ "nisplus",		Taddrs },
106 [OBhomeagent]		{ "homeagent",		Taddrs },
107 [OBsmtpserver]		{ "smtp",		Taddrs },
108 [OBpop3server]		{ "pop3",		Taddrs },
109 [OBnntpserver]		{ "nntp",		Taddrs },
110 [OBwwwserver]		{ "www",		Taddrs },
111 [OBfingerserver]	{ "finger",		Taddrs },
112 [OBircserver]		{ "irc",		Taddrs },
113 [OBstserver]		{ "st",			Taddrs },
114 [OBstdaserver]		{ "stdar",		Taddrs },
115 
116 [ODipaddr]		{ "ipaddr",		Taddr },
117 [ODlease]		{ "lease",		Tulong },
118 [ODoverload]		{ "overload",		Taddr },
119 [ODtype]		{ "type",		Tbyte },
120 [ODserverid]		{ "serverid",		Taddr },
121 [ODparams]		{ "params",		Tvec },
122 [ODmessage]		{ "message",		Tstr },
123 [ODmaxmsg]		{ "maxmsg",		Tulong },
124 [ODrenewaltime]		{ "renewaltime",	Tulong },
125 [ODrebindingtime]	{ "rebindingtime",	Tulong },
126 [ODvendorclass]		{ "vendorclass",	Tvec },
127 [ODclientid]		{ "clientid",		Tvec },
128 [ODtftpserver]		{ "tftp",		Taddr },
129 [ODbootfile]		{ "bootfile",		Tstr },
130 };
131 
132 uchar defrequested[] = {
133 	OBmask, OBrouter, OBdnserver, OBhostname, OBdomainname, OBntpserver,
134 };
135 
136 uchar	requested[256];
137 int	nrequested;
138 
139 int	Oflag;
140 int	beprimary = -1;
141 Conf	conf;
142 int	debug;
143 int	dodhcp;
144 int	dolog;
145 int	dondbconfig = 0;
146 int	dupl_disc = 1;		/* flag: V6 duplicate neighbor discovery */
147 Ctl	*firstctl, **ctll;
148 Ipifc	*ifc;
149 int	ipv6auto = 0;
150 int	myifc = -1;
151 char	*ndboptions;
152 int	nip;
153 int	noconfig;
154 int	nodhcpwatch;
155 char 	optmagic[4] = { 0x63, 0x82, 0x53, 0x63 };
156 int	plan9 = 1;
157 int	sendhostname;
158 
159 static char logfile[] = "ipconfig";
160 
161 char *verbs[] = {
162 [Vadd]		"add",
163 [Vremove]	"remove",
164 [Vunbind]	"unbind",
165 [Vether]	"ether",
166 [Vgbe]		"gbe",
167 [Vppp]		"ppp",
168 [Vloopback]	"loopback",
169 [Vaddpref6]	"add6",
170 [Vra6]		"ra6",
171 [Vtorus]	"torus",
172 [Vtree]		"tree",
173 };
174 
175 void	adddefroute(char*, uchar*);
176 int	addoption(char*);
177 void	binddevice(void);
178 void	bootprequest(void);
179 void	controldevice(void);
180 void	dhcpquery(int, int);
181 void	dhcprecv(void);
182 void	dhcpsend(int);
183 int	dhcptimer(void);
184 void	dhcpwatch(int);
185 void	doadd(int);
186 void	doremove(void);
187 void	dounbind(void);
188 int	getndb(void);
189 void	getoptions(uchar*);
190 int	ip4cfg(void);
191 int	ip6cfg(int a);
192 void	lookforip(char*);
193 void	mkclientid(void);
194 void	ndbconfig(void);
195 int	nipifcs(char*);
196 int	openlisten(void);
197 uchar*	optaddaddr(uchar*, int, uchar*);
198 uchar*	optaddbyte(uchar*, int, int);
199 uchar*	optaddstr(uchar*, int, char*);
200 uchar*	optadd(uchar*, int, void*, int);
201 uchar*	optaddulong(uchar*, int, ulong);
202 uchar*	optaddvec(uchar*, int, uchar*, int);
203 int	optgetaddrs(uchar*, int, uchar*, int);
204 int	optgetp9addrs(uchar*, int, uchar*, int);
205 int	optgetaddr(uchar*, int, uchar*);
206 int	optgetbyte(uchar*, int);
207 int	optgetstr(uchar*, int, char*, int);
208 uchar*	optget(uchar*, int, int*);
209 ulong	optgetulong(uchar*, int);
210 int	optgetvec(uchar*, int, uchar*, int);
211 char*	optgetx(uchar*, uchar);
212 Bootp*	parsebootp(uchar*, int);
213 int	parseoptions(uchar *p, int n);
214 int	parseverb(char*);
215 void	pppbinddev(void);
216 void	putndb(void);
217 void	tweakservers(void);
218 void	usage(void);
219 int	validip(uchar*);
220 void	writendb(char*, int, int);
221 
222 void
223 usage(void)
224 {
225 	fprint(2, "usage: %s [-6dDGnNOpPruX][-b baud][-c ctl]* [-g gw]"
226 		"[-h host][-m mtu]\n"
227 		"\t[-x mtpt][-o dhcpopt] type dev [verb] [laddr [mask "
228 		"[raddr [fs [auth]]]]]\n", argv0);
229 	exits("usage");
230 }
231 
232 void
233 warning(char *fmt, ...)
234 {
235 	char buf[1024];
236 	va_list arg;
237 
238 	va_start(arg, fmt);
239 	vseprint(buf, buf + sizeof buf, fmt, arg);
240 	va_end(arg);
241 	if (dolog)
242 		syslog(0, logfile, "%s", buf);
243 	else
244 		fprint(2, "%s: %s\n", argv0, buf);
245 }
246 
247 static void
248 parsenorm(int argc, char **argv)
249 {
250 	switch(argc){
251 	case 5:
252 		 if (parseip(conf.auth, argv[4]) == -1)
253 			usage();
254 		/* fall through */
255 	case 4:
256 		 if (parseip(conf.fs, argv[3]) == -1)
257 			usage();
258 		/* fall through */
259 	case 3:
260 		 if (parseip(conf.raddr, argv[2]) == -1)
261 			usage();
262 		/* fall through */
263 	case 2:
264 		/*
265 		 * can't test for parseipmask()==-1 cuz 255.255.255.255
266 		 * looks like that.
267 		 */
268 		if (strcmp(argv[1], "0") != 0)
269 			parseipmask(conf.mask, argv[1]);
270 		/* fall through */
271 	case 1:
272 		 if (parseip(conf.laddr, argv[0]) == -1)
273 			usage();
274 		/* fall through */
275 	case 0:
276 		break;
277 	default:
278 		usage();
279 	}
280 }
281 
282 static void
283 parse6pref(int argc, char **argv)
284 {
285 	switch(argc){
286 	case 6:
287 		conf.preflt = strtoul(argv[5], 0, 10);
288 		/* fall through */
289 	case 5:
290 		conf.validlt = strtoul(argv[4], 0, 10);
291 		/* fall through */
292 	case 4:
293 		conf.autoflag = (atoi(argv[3]) != 0);
294 		/* fall through */
295 	case 3:
296 		conf.onlink = (atoi(argv[2]) != 0);
297 		/* fall through */
298 	case 2:
299 		conf.prefixlen = atoi(argv[1]);
300 		/* fall through */
301 	case 1:
302 		parseip(conf.v6pref, argv[0]);
303 		break;
304 	}
305 	DEBUG("parse6pref: pref %I len %d", conf.v6pref, conf.prefixlen);
306 }
307 
308 /* parse router advertisement (keyword, value) pairs */
309 static void
310 parse6ra(int argc, char **argv)
311 {
312 	int i, argsleft;
313 	char *kw, *val;
314 
315 	if (argc % 2 != 0)
316 		usage();
317 
318 	i = 0;
319 	for (argsleft = argc; argsleft > 1; argsleft -= 2) {
320 		kw =  argv[i];
321 		val = argv[i+1];
322 		if (strcmp(kw, "recvra") == 0)
323 			conf.recvra = (atoi(val) != 0);
324 		else if (strcmp(kw, "sendra") == 0)
325 			conf.sendra = (atoi(val) != 0);
326 		else if (strcmp(kw, "mflag") == 0)
327 			conf.mflag = (atoi(val) != 0);
328 		else if (strcmp(kw, "oflag") == 0)
329 			conf.oflag = (atoi(val) != 0);
330 		else if (strcmp(kw, "maxraint") == 0)
331 			conf.maxraint = atoi(val);
332 		else if (strcmp(kw, "minraint") == 0)
333 			conf.minraint = atoi(val);
334 		else if (strcmp(kw, "linkmtu") == 0)
335 			conf.linkmtu = atoi(val);
336 		else if (strcmp(kw, "reachtime") == 0)
337 			conf.reachtime = atoi(val);
338 		else if (strcmp(kw, "rxmitra") == 0)
339 			conf.rxmitra = atoi(val);
340 		else if (strcmp(kw, "ttl") == 0)
341 			conf.ttl = atoi(val);
342 		else if (strcmp(kw, "routerlt") == 0)
343 			conf.routerlt = atoi(val);
344 		else {
345 			warning("bad ra6 keyword %s", kw);
346 			usage();
347 		}
348 		i += 2;
349 	}
350 
351 	/* consistency check */
352 	if (conf.maxraint < conf.minraint)
353 		sysfatal("maxraint %d < minraint %d",
354 			conf.maxraint, conf.minraint);
355 }
356 
357 static void
358 init(void)
359 {
360 	srand(truerand());
361 	fmtinstall('E', eipfmt);
362 	fmtinstall('I', eipfmt);
363 	fmtinstall('M', eipfmt);
364 	fmtinstall('V', eipfmt);
365  	nsec();			/* make sure time file is open before forking */
366 
367 	setnetmtpt(conf.mpoint, sizeof conf.mpoint, nil);
368 	conf.cputype = getenv("cputype");
369 	if(conf.cputype == nil)
370 		conf.cputype = "386";
371 
372 	ctll = &firstctl;
373 	v6paraminit(&conf);
374 
375 	/* init set of requested dhcp parameters with the default */
376 	nrequested = sizeof defrequested;
377 	memcpy(requested, defrequested, nrequested);
378 }
379 
380 static int
381 parseargs(int argc, char **argv)
382 {
383 	char *p;
384 	int action, verb;
385 
386 	/* default to any host name we already have */
387 	if(*conf.hostname == 0){
388 		p = getenv("sysname");
389 		if(p == nil || *p == 0)
390 			p = sysname();
391 		if(p != nil)
392 			strncpy(conf.hostname, p, sizeof conf.hostname-1);
393 	}
394 
395 	/* defaults */
396 	conf.type = "ether";
397 	conf.dev = "/net/ether0";
398 	action = Vadd;
399 
400 	/* get optional medium and device */
401 	if (argc > 0){
402 		verb = parseverb(*argv);
403 		switch(verb){
404 		case Vether:
405 		case Vgbe:
406 		case Vppp:
407 		case Vloopback:
408 		case Vtorus:
409 		case Vtree:
410 			conf.type = *argv++;
411 			argc--;
412 			if(argc > 0){
413 				conf.dev = *argv++;
414 				argc--;
415 			} else if(verb == Vppp)
416 				conf.dev = "/dev/eia0";
417 			break;
418 		}
419 	}
420 
421 	/* get optional verb */
422 	if (argc > 0){
423 		verb = parseverb(*argv);
424 		switch(verb){
425 		case Vether:
426 		case Vgbe:
427 		case Vppp:
428 		case Vloopback:
429 		case Vtorus:
430 		case Vtree:
431 			sysfatal("medium %s already specified", conf.type);
432 		case Vadd:
433 		case Vremove:
434 		case Vunbind:
435 		case Vaddpref6:
436 		case Vra6:
437 			argv++;
438 			argc--;
439 			action = verb;
440 			break;
441 		}
442 	}
443 
444 	/* get verb-dependent arguments */
445 	switch (action) {
446 	case Vadd:
447 	case Vremove:
448 	case Vunbind:
449 		parsenorm(argc, argv);
450 		break;
451 	case Vaddpref6:
452 		parse6pref(argc, argv);
453 		break;
454 	case Vra6:
455 		parse6ra(argc, argv);
456 		break;
457 	}
458 	return action;
459 }
460 
461 void
462 main(int argc, char **argv)
463 {
464 	int retry, action;
465 	Ctl *cp;
466 
467 	init();
468 	retry = 0;
469 	ARGBEGIN {
470 	case '6': 			/* IPv6 auto config */
471 		ipv6auto = 1;
472 		break;
473 	case 'b':
474 		conf.baud = EARGF(usage());
475 		break;
476 	case 'c':
477 		cp = malloc(sizeof *cp);
478 		if(cp == nil)
479 			sysfatal("%r");
480 		*ctll = cp;
481 		ctll = &cp->next;
482 		cp->next = nil;
483 		cp->ctl = EARGF(usage());
484 		break;
485 	case 'd':
486 		dodhcp = 1;
487 		break;
488 	case 'D':
489 		debug = 1;
490 		break;
491 	case 'g':
492 		if (parseip(conf.gaddr, EARGF(usage())) == -1)
493 			usage();
494 		break;
495 	case 'G':
496 		plan9 = 0;
497 		break;
498 	case 'h':
499 		snprint(conf.hostname, sizeof conf.hostname, "%s",
500 			EARGF(usage()));
501 		sendhostname = 1;
502 		break;
503 	case 'm':
504 		conf.mtu = atoi(EARGF(usage()));
505 		break;
506 	case 'n':
507 		noconfig = 1;
508 		break;
509 	case 'N':
510 		dondbconfig = 1;
511 		break;
512 	case 'o':
513 		if(addoption(EARGF(usage())) < 0)
514 			usage();
515 		break;
516 	case 'O':
517 		Oflag = 1;
518 		break;
519 	case 'p':
520 		beprimary = 1;
521 		break;
522 	case 'P':
523 		beprimary = 0;
524 		break;
525 	case 'r':
526 		retry = 1;
527 		break;
528 	case 'u':		/* IPv6: duplicate neighbour disc. off */
529 		dupl_disc = 0;
530 		break;
531 	case 'x':
532 		setnetmtpt(conf.mpoint, sizeof conf.mpoint, EARGF(usage()));
533 		break;
534 	case 'X':
535 		nodhcpwatch = 1;
536 		break;
537 	default:
538 		usage();
539 	} ARGEND;
540 	argv0 = "ipconfig";		/* boot invokes us as tcp? */
541 
542 	action = parseargs(argc, argv);
543 	switch(action){
544 	case Vadd:
545 		doadd(retry);
546 		break;
547 	case Vremove:
548 		doremove();
549 		break;
550 	case Vunbind:
551 		dounbind();
552 		break;
553 	case Vaddpref6:
554 	case Vra6:
555 		doipv6(action);
556 		break;
557 	}
558 	exits(0);
559 }
560 
561 int
562 havendb(char *net)
563 {
564 	Dir *d;
565 	char buf[128];
566 
567 	snprint(buf, sizeof buf, "%s/ndb", net);
568 	if((d = dirstat(buf)) == nil)
569 		return 0;
570 	if(d->length == 0){
571 		free(d);
572 		return 0;
573 	}
574 	free(d);
575 	return 1;
576 }
577 
578 void
579 doadd(int retry)
580 {
581 	int tries, ppp;
582 
583 	ppp = strcmp(conf.type, "ppp") == 0;
584 
585 	/* get number of preexisting interfaces */
586 	nip = nipifcs(conf.mpoint);
587 	if(beprimary == -1 && (nip == 0 || !havendb(conf.mpoint)))
588 		beprimary = 1;
589 
590 	/* get ipifc into name space and condition device for ip */
591 	if(!noconfig){
592 		lookforip(conf.mpoint);
593 		controldevice();
594 		binddevice();
595 	}
596 
597 	if (ipv6auto && !ppp) {
598 		if (ip6cfg(ipv6auto) < 0)
599 			sysfatal("can't automatically start IPv6 on %s",
600 				conf.dev);
601 //		return;
602 	} else if (validip(conf.laddr) && !isv4(conf.laddr)) {
603 		if (ip6cfg(0) < 0)
604 			sysfatal("can't start IPv6 on %s, address %I",
605 				conf.dev, conf.laddr);
606 //		return;
607 	}
608 
609 	if(!validip(conf.laddr) && !ppp)
610 		if(dondbconfig)
611 			ndbconfig();
612 		else
613 			dodhcp = 1;
614 
615 	/* run dhcp if we need something */
616 	if(dodhcp){
617 		mkclientid();
618 		for(tries = 0; tries < 6; tries++){
619 			dhcpquery(!noconfig, Sselecting);
620 			if(conf.state == Sbound)
621 				break;
622 			sleep(1000);
623 		}
624 	}
625 
626 	if(!validip(conf.laddr))
627 		if(retry && dodhcp && !noconfig){
628 			warning("couldn't determine ip address, retrying");
629 			dhcpwatch(1);
630 			return;
631 		} else
632 			sysfatal("no success with DHCP");
633 
634 	if(!noconfig)
635 		if(ip4cfg() < 0)
636 			sysfatal("can't start ip");
637 		else if(dodhcp && conf.lease != Lforever)
638 			dhcpwatch(0);
639 
640 	/* leave everything we've learned somewhere other procs can find it */
641 	if(beprimary == 1){
642 		putndb();
643 		tweakservers();
644 	}
645 }
646 
647 void
648 doremove(void)
649 {
650 	char file[128];
651 	int cfd;
652 	Ipifc *nifc;
653 	Iplifc *lifc;
654 
655 	if(!validip(conf.laddr))
656 		sysfatal("remove requires an address");
657 	ifc = readipifc(conf.mpoint, ifc, -1);
658 	for(nifc = ifc; nifc != nil; nifc = nifc->next){
659 		if(strcmp(nifc->dev, conf.dev) != 0)
660 			continue;
661 		for(lifc = nifc->lifc; lifc != nil; lifc = lifc->next){
662 			if(ipcmp(conf.laddr, lifc->ip) != 0)
663 				continue;
664 			if (validip(conf.mask) &&
665 			    ipcmp(conf.mask, lifc->mask) != 0)
666 				continue;
667 			if (validip(conf.raddr) &&
668 			    ipcmp(conf.raddr, lifc->net) != 0)
669 				continue;
670 
671 			snprint(file, sizeof file, "%s/ipifc/%d/ctl",
672 				conf.mpoint, nifc->index);
673 			cfd = open(file, ORDWR);
674 			if(cfd < 0){
675 				warning("can't open %s: %r", conf.mpoint);
676 				continue;
677 			}
678 			if(fprint(cfd, "remove %I %M", lifc->ip, lifc->mask) < 0)
679 				warning("can't remove %I %M from %s: %r",
680 					lifc->ip, lifc->mask, file);
681 		}
682 	}
683 }
684 
685 void
686 dounbind(void)
687 {
688 	Ipifc *nifc;
689 	char file[128];
690 	int cfd;
691 
692 	ifc = readipifc(conf.mpoint, ifc, -1);
693 	for(nifc = ifc; nifc != nil; nifc = nifc->next){
694 		if(strcmp(nifc->dev, conf.dev) == 0){
695 			snprint(file, sizeof file, "%s/ipifc/%d/ctl",
696 				conf.mpoint, nifc->index);
697 			cfd = open(file, ORDWR);
698 			if(cfd < 0){
699 				warning("can't open %s: %r", conf.mpoint);
700 				break;
701 			}
702 			if(fprint(cfd, "unbind") < 0)
703 				warning("can't unbind from %s: %r", file);
704 			break;
705 		}
706 	}
707 }
708 
709 /* set the default route */
710 void
711 adddefroute(char *mpoint, uchar *gaddr)
712 {
713 	char buf[256];
714 	int cfd;
715 
716 	sprint(buf, "%s/iproute", mpoint);
717 	cfd = open(buf, ORDWR);
718 	if(cfd < 0)
719 		return;
720 	if(isv4(gaddr))
721 		fprint(cfd, "add 0 0 %I", gaddr);
722 	else
723 		fprint(cfd, "add :: /0 %I", gaddr);
724 	close(cfd);
725 }
726 
727 /* create a client id */
728 void
729 mkclientid(void)
730 {
731 	if(strcmp(conf.type, "ether") == 0 || strcmp(conf.type, "gbe") == 0)
732 		if(myetheraddr(conf.hwa, conf.dev) == 0){
733 			conf.hwalen = 6;
734 			conf.hwatype = 1;
735 			conf.cid[0] = conf.hwatype;
736 			memmove(&conf.cid[1], conf.hwa, conf.hwalen);
737 			conf.cidlen = conf.hwalen+1;
738 		} else {
739 			conf.hwatype = -1;
740 			snprint((char*)conf.cid, sizeof conf.cid,
741 				"plan9_%ld.%d", lrand(), getpid());
742 			conf.cidlen = strlen((char*)conf.cid);
743 		}
744 }
745 
746 /* bind ip into the namespace */
747 void
748 lookforip(char *net)
749 {
750 	char proto[64];
751 
752 	snprint(proto, sizeof proto, "%s/ipifc", net);
753 	if(access(proto, 0) == 0)
754 		return;
755 	sysfatal("no ip stack bound onto %s", net);
756 }
757 
758 /* send some ctls to a device */
759 void
760 controldevice(void)
761 {
762 	char ctlfile[256];
763 	int fd;
764 	Ctl *cp;
765 
766 	if (firstctl == nil ||
767 	    strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0)
768 		return;
769 
770 	snprint(ctlfile, sizeof ctlfile, "%s/clone", conf.dev);
771 	fd = open(ctlfile, ORDWR);
772 	if(fd < 0)
773 		sysfatal("can't open %s", ctlfile);
774 
775 	for(cp = firstctl; cp != nil; cp = cp->next){
776 		if(write(fd, cp->ctl, strlen(cp->ctl)) < 0)
777 			sysfatal("ctl message %s: %r", cp->ctl);
778 		seek(fd, 0, 0);
779 	}
780 //	close(fd);		/* or does it need to be left hanging? */
781 }
782 
783 /* bind an ip stack to a device, leave the control channel open */
784 void
785 binddevice(void)
786 {
787 	char buf[256];
788 
789 	if(strcmp(conf.type, "ppp") == 0)
790 		pppbinddev();
791 	else if(myifc < 0){
792 		/* get a new ip interface */
793 		snprint(buf, sizeof buf, "%s/ipifc/clone", conf.mpoint);
794 		conf.cfd = open(buf, ORDWR);
795 		if(conf.cfd < 0)
796 			sysfatal("opening %s/ipifc/clone: %r", conf.mpoint);
797 
798 		/* specify medium as ethernet, bind the interface to it */
799 		if(fprint(conf.cfd, "bind %s %s", conf.type, conf.dev) < 0)
800 			sysfatal("%s: bind %s %s: %r", buf, conf.type, conf.dev);
801 	} else {
802 		/* open the old interface */
803 		snprint(buf, sizeof buf, "%s/ipifc/%d/ctl", conf.mpoint, myifc);
804 		conf.cfd = open(buf, ORDWR);
805 		if(conf.cfd < 0)
806 			sysfatal("open %s: %r", buf);
807 	}
808 
809 }
810 
811 /* add a logical interface to the ip stack */
812 int
813 ip4cfg(void)
814 {
815 	char buf[256];
816 	int n;
817 
818 	if(!validip(conf.laddr))
819 		return -1;
820 
821 	n = sprint(buf, "add");
822 	n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
823 
824 	if(!validip(conf.mask))
825 		ipmove(conf.mask, defmask(conf.laddr));
826 	n += snprint(buf+n, sizeof buf-n, " %I", conf.mask);
827 
828 	if(validip(conf.raddr)){
829 		n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
830 		if(conf.mtu != 0)
831 			n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
832 	}
833 
834 	if(write(conf.cfd, buf, n) < 0){
835 		warning("write(%s): %r", buf);
836 		return -1;
837 	}
838 
839 	if(beprimary==1 && validip(conf.gaddr))
840 		adddefroute(conf.mpoint, conf.gaddr);
841 
842 	return 0;
843 }
844 
845 /* remove a logical interface to the ip stack */
846 void
847 ipunconfig(void)
848 {
849 	char buf[256];
850 	int n;
851 
852 	if(!validip(conf.laddr))
853 		return;
854 	DEBUG("couldn't renew IP lease, releasing %I", conf.laddr);
855 	n = sprint(buf, "remove");
856 	n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
857 
858 	if(!validip(conf.mask))
859 		ipmove(conf.mask, defmask(conf.laddr));
860 	n += snprint(buf+n, sizeof buf-n, " %I", conf.mask);
861 
862 	write(conf.cfd, buf, n);
863 
864 	ipmove(conf.laddr, IPnoaddr);
865 	ipmove(conf.raddr, IPnoaddr);
866 	ipmove(conf.mask, IPnoaddr);
867 
868 	/* forget configuration info */
869 	if(beprimary==1)
870 		writendb("", 0, 0);
871 }
872 
873 void
874 ding(void*, char *msg)
875 {
876 	if(strstr(msg, "alarm"))
877 		noted(NCONT);
878 	noted(NDFLT);
879 }
880 
881 void
882 dhcpquery(int needconfig, int startstate)
883 {
884 	if(needconfig)
885 		fprint(conf.cfd, "add %I %I", IPnoaddr, IPnoaddr);
886 
887 	conf.fd = openlisten();
888 	if(conf.fd < 0){
889 		conf.state = Sinit;
890 		return;
891 	}
892 	notify(ding);
893 
894 	/* try dhcp for 10 seconds */
895 	conf.xid = lrand();
896 	conf.starttime = time(0);
897 	conf.state = startstate;
898 	switch(startstate){
899 	case Sselecting:
900 		conf.offered = 0;
901 		dhcpsend(Discover);
902 		break;
903 	case Srenewing:
904 		dhcpsend(Request);
905 		break;
906 	default:
907 		sysfatal("internal error 0");
908 	}
909 	conf.resend = 0;
910 	conf.timeout = time(0) + 4;
911 
912 	while(conf.state != Sbound){
913 		dhcprecv();
914 		if(dhcptimer() < 0)
915 			break;
916 		if(time(0) - conf.starttime > 10)
917 			break;
918 	}
919 	close(conf.fd);
920 
921 	if(needconfig)
922 		fprint(conf.cfd, "remove %I %I", IPnoaddr, IPnoaddr);
923 
924 }
925 
926 enum {
927 	/*
928 	 * was an hour, needs to be less for the ARM/GS1 until the timer
929 	 * code has been cleaned up (pb).
930 	 */
931 	Maxsleep = 450,
932 };
933 
934 void
935 dhcpwatch(int needconfig)
936 {
937 	int secs, s;
938 	ulong t;
939 
940 	if(nodhcpwatch)
941 		return;
942 
943 	switch(rfork(RFPROC|RFFDG|RFNOWAIT|RFNOTEG)){
944 	default:
945 		return;
946 	case 0:
947 		break;
948 	}
949 
950 	dolog = 1;			/* log, don't print */
951 	procsetname("dhcpwatch");
952 	/* keep trying to renew the lease */
953 	for(;;){
954 		if(conf.lease == 0)
955 			secs = 5;
956 		else
957 			secs = conf.lease >> 1;
958 
959 		/* avoid overflows */
960 		for(s = secs; s > 0; s -= t){
961 			if(s > Maxsleep)
962 				t = Maxsleep;
963 			else
964 				t = s;
965 			sleep(t*1000);
966 		}
967 
968 		if(conf.lease > 0){
969 			/*
970 			 * during boot, the starttime can be bogus so avoid
971 			 * spurious ipinconfig's
972 			 */
973 			t = time(0) - conf.starttime;
974 			if(t > (3*secs)/2)
975 				t = secs;
976 			if(t >= conf.lease){
977 				conf.lease = 0;
978 				if(!noconfig){
979 					ipunconfig();
980 					needconfig = 1;
981 				}
982 			} else
983 				conf.lease -= t;
984 		}
985 		dhcpquery(needconfig, needconfig? Sselecting: Srenewing);
986 
987 		if(needconfig && conf.state == Sbound){
988 			if(ip4cfg() < 0)
989 				sysfatal("can't start ip: %r");
990 			needconfig = 0;
991 			/*
992 			 * leave everything we've learned somewhere that
993 			 * other procs can find it.
994 			 */
995 			if(beprimary==1){
996 				putndb();
997 				tweakservers();
998 			}
999 		}
1000 	}
1001 }
1002 
1003 int
1004 dhcptimer(void)
1005 {
1006 	ulong now;
1007 
1008 	now = time(0);
1009 	if(now < conf.timeout)
1010 		return 0;
1011 
1012 	switch(conf.state) {
1013 	default:
1014 		sysfatal("dhcptimer: unknown state %d", conf.state);
1015 	case Sinit:
1016 	case Sbound:
1017 		break;
1018 	case Sselecting:
1019 	case Srequesting:
1020 	case Srebinding:
1021 		dhcpsend(conf.state == Sselecting? Discover: Request);
1022 		conf.timeout = now + 4;
1023 		if(++conf.resend > 5) {
1024 			conf.state = Sinit;
1025 			return -1;
1026 		}
1027 		break;
1028 	case Srenewing:
1029 		dhcpsend(Request);
1030 		conf.timeout = now + 1;
1031 		if(++conf.resend > 3) {
1032 			conf.state = Srebinding;
1033 			conf.resend = 0;
1034 		}
1035 		break;
1036 	}
1037 	return 0;
1038 }
1039 
1040 void
1041 dhcpsend(int type)
1042 {
1043 	Bootp bp;
1044 	uchar *p;
1045 	int n;
1046 	uchar vendor[64];
1047 	Udphdr *up = (Udphdr*)bp.udphdr;
1048 
1049 	memset(&bp, 0, sizeof bp);
1050 
1051 	hnputs(up->rport, 67);
1052 	bp.op = Bootrequest;
1053 	hnputl(bp.xid, conf.xid);
1054 	hnputs(bp.secs, time(0)-conf.starttime);
1055 	hnputs(bp.flags, 0);
1056 	memmove(bp.optmagic, optmagic, 4);
1057 	if(conf.hwatype >= 0 && conf.hwalen < sizeof bp.chaddr){
1058 		memmove(bp.chaddr, conf.hwa, conf.hwalen);
1059 		bp.hlen = conf.hwalen;
1060 		bp.htype = conf.hwatype;
1061 	}
1062 	p = bp.optdata;
1063 	p = optaddbyte(p, ODtype, type);
1064 	p = optadd(p, ODclientid, conf.cid, conf.cidlen);
1065 	switch(type) {
1066 	default:
1067 		sysfatal("dhcpsend: unknown message type: %d", type);
1068 	case Discover:
1069 		ipmove(up->raddr, IPv4bcast);	/* broadcast */
1070 		if(*conf.hostname && sendhostname)
1071 			p = optaddstr(p, OBhostname, conf.hostname);
1072 		if(plan9){
1073 			n = snprint((char*)vendor, sizeof vendor,
1074 				"plan9_%s", conf.cputype);
1075 			p = optaddvec(p, ODvendorclass, vendor, n);
1076 		}
1077 		p = optaddvec(p, ODparams, requested, nrequested);
1078 		if(validip(conf.laddr))
1079 			p = optaddaddr(p, ODipaddr, conf.laddr);
1080 		break;
1081 	case Request:
1082 		switch(conf.state){
1083 		case Srenewing:
1084 			ipmove(up->raddr, conf.server);
1085 			v6tov4(bp.ciaddr, conf.laddr);
1086 			break;
1087 		case Srebinding:
1088 			ipmove(up->raddr, IPv4bcast);	/* broadcast */
1089 			v6tov4(bp.ciaddr, conf.laddr);
1090 			break;
1091 		case Srequesting:
1092 			ipmove(up->raddr, IPv4bcast);	/* broadcast */
1093 			p = optaddaddr(p, ODipaddr, conf.laddr);
1094 			p = optaddaddr(p, ODserverid, conf.server);
1095 			break;
1096 		}
1097 		p = optaddulong(p, ODlease, conf.offered);
1098 		if(plan9){
1099 			n = snprint((char*)vendor, sizeof vendor,
1100 				"plan9_%s", conf.cputype);
1101 			p = optaddvec(p, ODvendorclass, vendor, n);
1102 		}
1103 		p = optaddvec(p, ODparams, requested, nrequested);
1104 		if(*conf.hostname && sendhostname)
1105 			p = optaddstr(p, OBhostname, conf.hostname);
1106 		break;
1107 	case Release:
1108 		ipmove(up->raddr, conf.server);
1109 		v6tov4(bp.ciaddr, conf.laddr);
1110 		p = optaddaddr(p, ODipaddr, conf.laddr);
1111 		p = optaddaddr(p, ODserverid, conf.server);
1112 		break;
1113 	}
1114 
1115 	*p++ = OBend;
1116 
1117 	n = p - (uchar*)&bp;
1118 	USED(n);
1119 
1120 	/*
1121 	 *  We use a maximum size DHCP packet to survive the
1122 	 *  All_Aboard NAT package from Internet Share.  It
1123 	 *  always replies to DHCP requests with a packet of the
1124 	 *  same size, so if the request is too short the reply
1125 	 *  is truncated.
1126 	 */
1127 	if(write(conf.fd, &bp, sizeof bp) != sizeof bp)
1128 		warning("dhcpsend: write failed: %r");
1129 }
1130 
1131 void
1132 dhcprecv(void)
1133 {
1134 	int i, n, type;
1135 	ulong lease;
1136 	char err[ERRMAX];
1137 	uchar buf[8000], vopts[256], taddr[IPaddrlen];
1138 	Bootp *bp;
1139 
1140 	alarm(1000);
1141 	n = read(conf.fd, buf, sizeof buf);
1142 	alarm(0);
1143 
1144 	if(n < 0){
1145 		errstr(err, sizeof err);
1146 		if(strstr(err, "interrupt") == nil)
1147 			warning("dhcprecv: bad read: %s", err);
1148 		else
1149 			DEBUG("dhcprecv: read timed out");
1150 		return;
1151 	}
1152 
1153 	bp = parsebootp(buf, n);
1154 	if(bp == 0) {
1155 		DEBUG("parsebootp failed: dropping packet");
1156 		return;
1157 	}
1158 
1159 	type = optgetbyte(bp->optdata, ODtype);
1160 	switch(type) {
1161 	default:
1162 		warning("dhcprecv: unknown type: %d", type);
1163 		break;
1164 	case Offer:
1165 		DEBUG("got offer from %V ", bp->siaddr);
1166 		if(conf.state != Sselecting){
1167 //			DEBUG("");
1168 			break;
1169 		}
1170 		lease = optgetulong(bp->optdata, ODlease);
1171 		if(lease == 0){
1172 			/*
1173 			 * The All_Aboard NAT package from Internet Share
1174 			 * doesn't give a lease time, so we have to assume one.
1175 			 */
1176 			warning("Offer with %lud lease, using %d", lease, MinLease);
1177 			lease = MinLease;
1178 		}
1179 		DEBUG("lease=%lud ", lease);
1180 		if(!optgetaddr(bp->optdata, ODserverid, conf.server)) {
1181 			warning("Offer from server with invalid serverid");
1182 			break;
1183 		}
1184 
1185 		v4tov6(conf.laddr, bp->yiaddr);
1186 		memmove(conf.sname, bp->sname, sizeof conf.sname);
1187 		conf.sname[sizeof conf.sname-1] = 0;
1188 		DEBUG("server=%I sname=%s", conf.server, conf.sname);
1189 		conf.offered = lease;
1190 		conf.state = Srequesting;
1191 		dhcpsend(Request);
1192 		conf.resend = 0;
1193 		conf.timeout = time(0) + 4;
1194 		break;
1195 	case Ack:
1196 		DEBUG("got ack from %V ", bp->siaddr);
1197 		if (conf.state != Srequesting && conf.state != Srenewing &&
1198 		    conf.state != Srebinding)
1199 			break;
1200 
1201 		/* ignore a bad lease */
1202 		lease = optgetulong(bp->optdata, ODlease);
1203 		if(lease == 0){
1204 			/*
1205 			 * The All_Aboard NAT package from Internet Share
1206 			 * doesn't give a lease time, so we have to assume one.
1207 			 */
1208 			warning("Ack with %lud lease, using %d", lease, MinLease);
1209 			lease = MinLease;
1210 		}
1211 		DEBUG("lease=%lud ", lease);
1212 
1213 		/* address and mask */
1214 		if(!validip(conf.laddr) || !Oflag)
1215 			v4tov6(conf.laddr, bp->yiaddr);
1216 		if(!validip(conf.mask) || !Oflag){
1217 			if(!optgetaddr(bp->optdata, OBmask, conf.mask))
1218 				ipmove(conf.mask, IPnoaddr);
1219 		}
1220 		DEBUG("ipaddr=%I ipmask=%M ", conf.laddr, conf.mask);
1221 
1222 		/*
1223 		 * get a router address either from the router option
1224 		 * or from the router that forwarded the dhcp packet
1225 		 */
1226 		if(validip(conf.gaddr) && Oflag) {
1227 			DEBUG("ipgw=%I ", conf.gaddr);
1228 		} else if(optgetaddr(bp->optdata, OBrouter, conf.gaddr)){
1229 			DEBUG("ipgw=%I ", conf.gaddr);
1230 		} else if(memcmp(bp->giaddr, IPnoaddr+IPv4off, IPv4addrlen)!=0){
1231 			v4tov6(conf.gaddr, bp->giaddr);
1232 			DEBUG("giaddr=%I ", conf.gaddr);
1233 		}
1234 
1235 		/* get dns servers */
1236 		memset(conf.dns, 0, sizeof conf.dns);
1237 		n = optgetaddrs(bp->optdata, OBdnserver, conf.dns,
1238 			sizeof conf.dns/IPaddrlen);
1239 		for(i = 0; i < n; i++)
1240 			DEBUG("dns=%I ", conf.dns + i*IPaddrlen);
1241 
1242 		/* get ntp servers */
1243 		memset(conf.ntp, 0, sizeof conf.ntp);
1244 		n = optgetaddrs(bp->optdata, OBntpserver, conf.ntp,
1245 			sizeof conf.ntp/IPaddrlen);
1246 		for(i = 0; i < n; i++)
1247 			DEBUG("ntp=%I ", conf.ntp + i*IPaddrlen);
1248 
1249 		/* get names */
1250 		optgetstr(bp->optdata, OBhostname,
1251 			conf.hostname, sizeof conf.hostname);
1252 		optgetstr(bp->optdata, OBdomainname,
1253 			conf.domainname, sizeof conf.domainname);
1254 
1255 		/* get anything else we asked for */
1256 		getoptions(bp->optdata);
1257 
1258 		/* get plan9-specific options */
1259 		n = optgetvec(bp->optdata, OBvendorinfo, vopts, sizeof vopts-1);
1260 		if(n > 0 && parseoptions(vopts, n) == 0){
1261 			if(validip(conf.fs) && Oflag)
1262 				n = 1;
1263 			else {
1264 				n = optgetp9addrs(vopts, OP9fs, conf.fs, 2);
1265 				if (n == 0)
1266 					n = optgetaddrs(vopts, OP9fsv4,
1267 						conf.fs, 2);
1268 			}
1269 			for(i = 0; i < n; i++)
1270 				DEBUG("fs=%I ", conf.fs + i*IPaddrlen);
1271 
1272 			if(validip(conf.auth) && Oflag)
1273 				n = 1;
1274 			else {
1275 				n = optgetp9addrs(vopts, OP9auth, conf.auth, 2);
1276 				if (n == 0)
1277 					n = optgetaddrs(vopts, OP9authv4,
1278 						conf.auth, 2);
1279 			}
1280 			for(i = 0; i < n; i++)
1281 				DEBUG("auth=%I ", conf.auth + i*IPaddrlen);
1282 
1283 			n = optgetp9addrs(vopts, OP9ipaddr, taddr, 1);
1284 			if (n > 0)
1285 				memmove(conf.laddr, taddr, IPaddrlen);
1286 			n = optgetp9addrs(vopts, OP9ipmask, taddr, 1);
1287 			if (n > 0)
1288 				memmove(conf.mask, taddr, IPaddrlen);
1289 			n = optgetp9addrs(vopts, OP9ipgw, taddr, 1);
1290 			if (n > 0)
1291 				memmove(conf.gaddr, taddr, IPaddrlen);
1292 			DEBUG("new ipaddr=%I new ipmask=%M new ipgw=%I",
1293 				conf.laddr, conf.mask, conf.gaddr);
1294 		}
1295 		conf.lease = lease;
1296 		conf.state = Sbound;
1297 		DEBUG("server=%I sname=%s", conf.server, conf.sname);
1298 		break;
1299 	case Nak:
1300 		conf.state = Sinit;
1301 		warning("recved dhcpnak on %s", conf.mpoint);
1302 		break;
1303 	}
1304 }
1305 
1306 /* return pseudo-random integer in range low...(hi-1) */
1307 ulong
1308 randint(ulong low, ulong hi)
1309 {
1310 	if (hi < low)
1311 		return low;
1312 	return low + nrand(hi - low);
1313 }
1314 
1315 long
1316 jitter(void)		/* compute small pseudo-random delay in ms */
1317 {
1318 	return randint(0, 10*1000);
1319 }
1320 
1321 int
1322 openlisten(void)
1323 {
1324 	int n, fd, cfd;
1325 	char data[128], devdir[40];
1326 
1327 	if (validip(conf.laddr) &&
1328 	    (conf.state == Srenewing || conf.state == Srebinding))
1329 		sprint(data, "%s/udp!%I!68", conf.mpoint, conf.laddr);
1330 	else
1331 		sprint(data, "%s/udp!*!68", conf.mpoint);
1332 	for (n = 0; (cfd = announce(data, devdir)) < 0; n++) {
1333 		if(!noconfig)
1334 			sysfatal("can't announce for dhcp: %r");
1335 
1336 		/* might be another client - wait and try again */
1337 		warning("can't announce %s: %r", data);
1338 		sleep(jitter());
1339 		if(n > 10)
1340 			return -1;
1341 	}
1342 
1343 	if(fprint(cfd, "headers") < 0)
1344 		sysfatal("can't set header mode: %r");
1345 
1346 	sprint(data, "%s/data", devdir);
1347 	fd = open(data, ORDWR);
1348 	if(fd < 0)
1349 		sysfatal("open %s: %r", data);
1350 	close(cfd);
1351 	return fd;
1352 }
1353 
1354 uchar*
1355 optadd(uchar *p, int op, void *d, int n)
1356 {
1357 	p[0] = op;
1358 	p[1] = n;
1359 	memmove(p+2, d, n);
1360 	return p+n+2;
1361 }
1362 
1363 uchar*
1364 optaddbyte(uchar *p, int op, int b)
1365 {
1366 	p[0] = op;
1367 	p[1] = 1;
1368 	p[2] = b;
1369 	return p+3;
1370 }
1371 
1372 uchar*
1373 optaddulong(uchar *p, int op, ulong x)
1374 {
1375 	p[0] = op;
1376 	p[1] = 4;
1377 	hnputl(p+2, x);
1378 	return p+6;
1379 }
1380 
1381 uchar *
1382 optaddaddr(uchar *p, int op, uchar *ip)
1383 {
1384 	p[0] = op;
1385 	p[1] = 4;
1386 	v6tov4(p+2, ip);
1387 	return p+6;
1388 }
1389 
1390 /* add dhcp option op with value v of length n to dhcp option array p */
1391 uchar *
1392 optaddvec(uchar *p, int op, uchar *v, int n)
1393 {
1394 	p[0] = op;
1395 	p[1] = n;
1396 	memmove(p+2, v, n);
1397 	return p+2+n;
1398 }
1399 
1400 uchar *
1401 optaddstr(uchar *p, int op, char *v)
1402 {
1403 	int n;
1404 
1405 	n = strlen(v)+1;	/* microsoft leaves on the NUL, so we do too */
1406 	p[0] = op;
1407 	p[1] = n;
1408 	memmove(p+2, v, n);
1409 	return p+2+n;
1410 }
1411 
1412 /*
1413  * parse p, looking for option `op'.  if non-nil, np points to minimum length.
1414  * return nil if option is too small, else ptr to opt, and
1415  * store actual length via np if non-nil.
1416  */
1417 uchar*
1418 optget(uchar *p, int op, int *np)
1419 {
1420 	int len, code;
1421 
1422 	while ((code = *p++) != OBend) {
1423 		if(code == OBpad)
1424 			continue;
1425 		len = *p++;
1426 		if(code != op) {
1427 			p += len;
1428 			continue;
1429 		}
1430 		if(np != nil){
1431 			if(*np > len) {
1432 				return 0;
1433 			}
1434 			*np = len;
1435 		}
1436 		return p;
1437 	}
1438 	return 0;
1439 }
1440 
1441 int
1442 optgetbyte(uchar *p, int op)
1443 {
1444 	int len;
1445 
1446 	len = 1;
1447 	p = optget(p, op, &len);
1448 	if(p == nil)
1449 		return 0;
1450 	return *p;
1451 }
1452 
1453 ulong
1454 optgetulong(uchar *p, int op)
1455 {
1456 	int len;
1457 
1458 	len = 4;
1459 	p = optget(p, op, &len);
1460 	if(p == nil)
1461 		return 0;
1462 	return nhgetl(p);
1463 }
1464 
1465 int
1466 optgetaddr(uchar *p, int op, uchar *ip)
1467 {
1468 	int len;
1469 
1470 	len = 4;
1471 	p = optget(p, op, &len);
1472 	if(p == nil)
1473 		return 0;
1474 	v4tov6(ip, p);
1475 	return 1;
1476 }
1477 
1478 /* expect at most n addresses; ip[] only has room for that many */
1479 int
1480 optgetaddrs(uchar *p, int op, uchar *ip, int n)
1481 {
1482 	int len, i;
1483 
1484 	len = 4;
1485 	p = optget(p, op, &len);
1486 	if(p == nil)
1487 		return 0;
1488 	len /= IPv4addrlen;
1489 	if(len > n)
1490 		len = n;
1491 	for(i = 0; i < len; i++)
1492 		v4tov6(&ip[i*IPaddrlen], &p[i*IPv4addrlen]);
1493 	return i;
1494 }
1495 
1496 /* expect at most n addresses; ip[] only has room for that many */
1497 int
1498 optgetp9addrs(uchar *ap, int op, uchar *ip, int n)
1499 {
1500 	int len, i, slen, addrs;
1501 	char *p;
1502 
1503 	len = 1;			/* minimum bytes needed */
1504 	p = (char *)optget(ap, op, &len);
1505 	if(p == nil)
1506 		return 0;
1507 	addrs = *p++;			/* first byte is address count */
1508 	for (i = 0; i < n  && i < addrs && len > 0; i++) {
1509 		slen = strlen(p) + 1;
1510 		parseip(&ip[i*IPaddrlen], p);
1511 		DEBUG("got plan 9 option %d addr %I (%s)",
1512 			op, &ip[i*IPaddrlen], p);
1513 		p += slen;
1514 		len -= slen;
1515 	}
1516 	return addrs;
1517 }
1518 
1519 int
1520 optgetvec(uchar *p, int op, uchar *v, int n)
1521 {
1522 	int len;
1523 
1524 	len = 1;
1525 	p = optget(p, op, &len);
1526 	if(p == nil)
1527 		return 0;
1528 	if(len > n)
1529 		len = n;
1530 	memmove(v, p, len);
1531 	return len;
1532 }
1533 
1534 int
1535 optgetstr(uchar *p, int op, char *s, int n)
1536 {
1537 	int len;
1538 
1539 	len = 1;
1540 	p = optget(p, op, &len);
1541 	if(p == nil)
1542 		return 0;
1543 	if(len >= n)
1544 		len = n-1;
1545 	memmove(s, p, len);
1546 	s[len] = 0;
1547 	return len;
1548 }
1549 
1550 /*
1551  * sanity check options area
1552  * 	- options don't overflow packet
1553  * 	- options end with an OBend
1554  */
1555 int
1556 parseoptions(uchar *p, int n)
1557 {
1558 	int code, len, nin = n;
1559 
1560 	while (n > 0) {
1561 		code = *p++;
1562 		n--;
1563 		if(code == OBend)
1564 			return 0;
1565 		if(code == OBpad)
1566 			continue;
1567 		if(n == 0) {
1568 			warning("parseoptions: bad option: 0x%ux: truncated: "
1569 				"opt length = %d", code, nin);
1570 			return -1;
1571 		}
1572 
1573 		len = *p++;
1574 		n--;
1575 		DEBUG("parseoptions: %s(%d) len %d, bytes left %d",
1576 			option[code].name, code, len, n);
1577 		if(len > n) {
1578 			warning("parseoptions: bad option: 0x%ux: %d > %d: "
1579 				"opt length = %d", code, len, n, nin);
1580 			return -1;
1581 		}
1582 		p += len;
1583 		n -= len;
1584 	}
1585 
1586 	/* make sure packet ends with an OBend after all the optget code */
1587 	*p = OBend;
1588 	return 0;
1589 }
1590 
1591 /*
1592  * sanity check received packet:
1593  * 	- magic is dhcp magic
1594  * 	- options don't overflow packet
1595  */
1596 Bootp *
1597 parsebootp(uchar *p, int n)
1598 {
1599 	Bootp *bp;
1600 
1601 	bp = (Bootp*)p;
1602 	if(n < bp->optmagic - p) {
1603 		warning("parsebootp: short bootp packet");
1604 		return nil;
1605 	}
1606 
1607 	if(conf.xid != nhgetl(bp->xid))
1608 		return nil;
1609 
1610 	if(bp->op != Bootreply) {
1611 		warning("parsebootp: bad op %d", bp->op);
1612 		return nil;
1613 	}
1614 
1615 	n -= bp->optmagic - p;
1616 	p = bp->optmagic;
1617 
1618 	if(n < 4) {
1619 		warning("parsebootp: no option data");
1620 		return nil;
1621 	}
1622 	if(memcmp(optmagic, p, 4) != 0) {
1623 		warning("parsebootp: bad opt magic %ux %ux %ux %ux",
1624 			p[0], p[1], p[2], p[3]);
1625 		return nil;
1626 	}
1627 	p += 4;
1628 	n -= 4;
1629 	DEBUG("parsebootp: new packet");
1630 	if(parseoptions(p, n) < 0)
1631 		return nil;
1632 	return bp;
1633 }
1634 
1635 /* write out an ndb entry */
1636 void
1637 writendb(char *s, int n, int append)
1638 {
1639 	char file[64];
1640 	int fd;
1641 
1642 	snprint(file, sizeof file, "%s/ndb", conf.mpoint);
1643 	if(append){
1644 		fd = open(file, OWRITE);
1645 		seek(fd, 0, 2);
1646 	} else
1647 		fd = open(file, OWRITE|OTRUNC);
1648 	write(fd, s, n);
1649 	close(fd);
1650 }
1651 
1652 /* put server addresses into the ndb entry */
1653 char*
1654 putaddrs(char *p, char *e, char *attr, uchar *a, int len)
1655 {
1656 	int i;
1657 
1658 	for(i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen)
1659 		p = seprint(p, e, "%s=%I\n", attr, a);
1660 	return p;
1661 }
1662 
1663 /* make an ndb entry and put it into /net/ndb for the servers to see */
1664 void
1665 putndb(void)
1666 {
1667 	int append;
1668 	char buf[1024];
1669 	char *p, *e, *np;
1670 
1671 	p = buf;
1672 	e = buf + sizeof buf;
1673 	if(getndb() == 0)
1674 		append = 1;
1675 	else {
1676 		append = 0;
1677 		p = seprint(p, e, "ip=%I ipmask=%M ipgw=%I\n",
1678 			conf.laddr, conf.mask, conf.gaddr);
1679 	}
1680 	if(np = strchr(conf.hostname, '.')){
1681 		if(*conf.domainname == 0)
1682 			strcpy(conf.domainname, np+1);
1683 		*np = 0;
1684 	}
1685 	if(*conf.hostname)
1686 		p = seprint(p, e, "\tsys=%s\n", conf.hostname);
1687 	if(*conf.domainname)
1688 		p = seprint(p, e, "\tdom=%s.%s\n",
1689 			conf.hostname, conf.domainname);
1690 	if(validip(conf.fs))
1691 		p = putaddrs(p, e, "\tfs", conf.fs, sizeof conf.fs);
1692 	if(validip(conf.auth))
1693 		p = putaddrs(p, e, "\tauth", conf.auth, sizeof conf.auth);
1694 	if(validip(conf.dns))
1695 		p = putaddrs(p, e, "\tdns", conf.dns, sizeof conf.dns);
1696 	if(validip(conf.ntp))
1697 		p = putaddrs(p, e, "\tntp", conf.ntp, sizeof conf.ntp);
1698 	if(ndboptions)
1699 		p = seprint(p, e, "%s\n", ndboptions);
1700 	if(p > buf)
1701 		writendb(buf, p-buf, append);
1702 }
1703 
1704 /* get an ndb entry someone else wrote */
1705 int
1706 getndb(void)
1707 {
1708 	char buf[1024];
1709 	int fd, n;
1710 	char *p;
1711 
1712 	snprint(buf, sizeof buf, "%s/ndb", conf.mpoint);
1713 	fd = open(buf, OREAD);
1714 	n = read(fd, buf, sizeof buf-1);
1715 	close(fd);
1716 	if(n <= 0)
1717 		return -1;
1718 	buf[n] = 0;
1719 	p = strstr(buf, "ip=");
1720 	if(p == nil)
1721 		return -1;
1722 	parseip(conf.laddr, p+3);
1723 	return 0;
1724 }
1725 
1726 /* tell a server to refresh */
1727 void
1728 tweakserver(char *server)
1729 {
1730 	int fd;
1731 	char file[64];
1732 
1733 	snprint(file, sizeof file, "%s/%s", conf.mpoint, server);
1734 	fd = open(file, ORDWR);
1735 	if(fd < 0)
1736 		return;
1737 	fprint(fd, "refresh");
1738 	close(fd);
1739 }
1740 
1741 /* tell all servers to refresh their information */
1742 void
1743 tweakservers(void)
1744 {
1745 	tweakserver("dns");
1746 	tweakserver("cs");
1747 }
1748 
1749 /* return number of networks */
1750 int
1751 nipifcs(char *net)
1752 {
1753 	int n;
1754 	Ipifc *nifc;
1755 	Iplifc *lifc;
1756 
1757 	n = 0;
1758 	ifc = readipifc(net, ifc, -1);
1759 	for(nifc = ifc; nifc != nil; nifc = nifc->next){
1760 		/*
1761 		 * ignore loopback devices when trying to
1762 		 * figure out if we're the primary interface.
1763 		 */
1764 		if(strcmp(nifc->dev, "/dev/null") != 0)
1765 			for(lifc = nifc->lifc; lifc != nil; lifc = lifc->next)
1766 				if(validip(lifc->ip)){
1767 					n++;
1768 					break;
1769 				}
1770 		if(strcmp(nifc->dev, conf.dev) == 0)
1771 			myifc = nifc->index;
1772 	}
1773 	return n;
1774 }
1775 
1776 /* return true if this is a valid v4 address */
1777 int
1778 validip(uchar *addr)
1779 {
1780 	return ipcmp(addr, IPnoaddr) != 0 && ipcmp(addr, v4prefix) != 0;
1781 }
1782 
1783 /* look for an action */
1784 int
1785 parseverb(char *name)
1786 {
1787 	int i;
1788 
1789 	for(i = 0; i < nelem(verbs); i++)
1790 		if(verbs[i] != nil && strcmp(name, verbs[i]) == 0)
1791 			return i;
1792 	return -1;
1793 }
1794 
1795 /* get everything out of ndb */
1796 void
1797 ndbconfig(void)
1798 {
1799 	int nattr, nauth = 0, ndns = 0, nfs = 0;
1800 	char etheraddr[32];
1801 	char *attrs[10];
1802 	Ndb *db;
1803 	Ndbtuple *t, *nt;
1804 
1805 	db = ndbopen(0);
1806 	if(db == nil)
1807 		sysfatal("can't open ndb: %r");
1808 	if (strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0 ||
1809 	    myetheraddr(conf.hwa, conf.dev) != 0)
1810 		sysfatal("can't read hardware address");
1811 	sprint(etheraddr, "%E", conf.hwa);
1812 	nattr = 0;
1813 	attrs[nattr++] = "ip";
1814 	attrs[nattr++] = "ipmask";
1815 	attrs[nattr++] = "ipgw";
1816 	/* the @ triggers resolution to an IP address; see ndb(2) */
1817 	attrs[nattr++] = "@dns";
1818 	attrs[nattr++] = "@ntp";
1819 	attrs[nattr++] = "@fs";
1820 	attrs[nattr++] = "@auth";
1821 	attrs[nattr] = nil;
1822 	t = ndbipinfo(db, "ether", etheraddr, attrs, nattr);
1823 	for(nt = t; nt != nil; nt = nt->entry)
1824 		if(strcmp(nt->attr, "ip") == 0)
1825 			parseip(conf.laddr, nt->val);
1826 		else if(strcmp(nt->attr, "ipmask") == 0)
1827 			parseipmask(conf.mask, nt->val);
1828 		else if(strcmp(nt->attr, "ipgw") == 0)
1829 			parseip(conf.gaddr, nt->val);
1830 		else if(ndns < 2 && strcmp(nt->attr, "dns") == 0)
1831 			parseip(conf.dns+IPaddrlen*ndns, nt->val);
1832 		else if(strcmp(nt->attr, "ntp") == 0)
1833 			parseip(conf.ntp, nt->val);
1834 		else if(nfs < 2 && strcmp(nt->attr, "fs") == 0)
1835 			parseip(conf.fs+IPaddrlen*nfs, nt->val);
1836 		else if(nauth < 2 && strcmp(nt->attr, "auth") == 0)
1837 			parseip(conf.auth+IPaddrlen*nauth, nt->val);
1838 	ndbfree(t);
1839 	if(!validip(conf.laddr))
1840 		sysfatal("address not found in ndb");
1841 }
1842 
1843 int
1844 addoption(char *opt)
1845 {
1846 	int i;
1847 	Option *o;
1848 
1849 	if(opt == nil)
1850 		return -1;
1851 	for(o = option; o < &option[nelem(option)]; o++)
1852 		if(o->name && strcmp(opt, o->name) == 0){
1853 			i = o - option;
1854 			if(memchr(requested, i, nrequested) == 0 &&
1855 			    nrequested < nelem(requested))
1856 				requested[nrequested++] = i;
1857 			return 0;
1858 		}
1859 	return -1;
1860 }
1861 
1862 char*
1863 optgetx(uchar *p, uchar opt)
1864 {
1865 	int i, n;
1866 	ulong x;
1867 	char *s, *ns;
1868 	char str[256];
1869 	uchar ip[IPaddrlen], ips[16*IPaddrlen], vec[256];
1870 	Option *o;
1871 
1872 	o = &option[opt];
1873 	if(o->name == nil)
1874 		return nil;
1875 
1876 	s = nil;
1877 	switch(o->type){
1878 	case Taddr:
1879 		if(optgetaddr(p, opt, ip))
1880 			s = smprint("%s=%I", o->name, ip);
1881 		break;
1882 	case Taddrs:
1883 		n = optgetaddrs(p, opt, ips, 16);
1884 		if(n > 0)
1885 			s = smprint("%s=%I", o->name, ips);
1886 		for(i = 1; i < n; i++){
1887 			ns = smprint("%s %s=%I", s, o->name, &ips[i*IPaddrlen]);
1888 			free(s);
1889 			s = ns;
1890 		}
1891 		break;
1892 	case Tulong:
1893 		x = optgetulong(p, opt);
1894 		if(x != 0)
1895 			s = smprint("%s=%lud", o->name, x);
1896 		break;
1897 	case Tbyte:
1898 		x = optgetbyte(p, opt);
1899 		if(x != 0)
1900 			s = smprint("%s=%lud", o->name, x);
1901 		break;
1902 	case Tstr:
1903 		if(optgetstr(p, opt, str, sizeof str))
1904 			s = smprint("%s=%s", o->name, str);
1905 		break;
1906 	case Tvec:
1907 		n = optgetvec(p, opt, vec, sizeof vec);
1908 		if(n > 0)
1909 			/* what's %H?  it's not installed */
1910 			s = smprint("%s=%.*H", o->name, n, vec);
1911 		break;
1912 	}
1913 	return s;
1914 }
1915 
1916 void
1917 getoptions(uchar *p)
1918 {
1919 	int i;
1920 	char *s, *t;
1921 
1922 	for(i = nelem(defrequested); i < nrequested; i++){
1923 		s = optgetx(p, requested[i]);
1924 		if(s != nil)
1925 			DEBUG("%s ", s);
1926 		if(ndboptions == nil)
1927 			ndboptions = smprint("\t%s", s);
1928 		else{
1929 			t = ndboptions;
1930 			ndboptions = smprint("\t%s%s", s, ndboptions);
1931 			free(t);
1932 		}
1933 		free(s);
1934 	}
1935 }
1936