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