xref: /titanic_50/usr/src/psm/stand/boot/sparc/common/wbcli.c (revision 34ae5cdf42c9fd8bc75aedb3a91cc2fba8d5182c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/param.h>
28 #include <sys/salib.h>
29 #include <sys/promif.h>
30 #include <sys/wanboot_impl.h>
31 #include <netinet/in.h>
32 #include <parseURL.h>
33 #include <bootlog.h>
34 #include <sys/socket.h>
35 #include <netinet/inetutil.h>
36 #include <netinet/dhcp.h>
37 #include <dhcp_impl.h>
38 #include <lib/inet/mac.h>
39 #include <lib/inet/ipv4.h>
40 #include <lib/inet/dhcpv4.h>
41 #include <lib/sock/sock_test.h>
42 #include <sys/sunos_dhcp_class.h>
43 #include <aes.h>
44 #include <des3.h>
45 #include <hmac_sha1.h>
46 #include <netdb.h>
47 #include <wanboot_conf.h>
48 #include <bootinfo.h>
49 
50 #include "wbcli.h"
51 
52 #define	skipspace(p)	while (isspace(*(p))) ++p
53 
54 #define	skiptext(p)	while (*(p) != '\0' && !isspace(*(p)) && \
55 			    *(p) != '=' && *(p) != ',') ++p
56 
57 #define	PROMPT		"boot> "
58 #define	TEST_PROMPT	"boot-test> "
59 
60 #define	CLI_SET		0
61 #define	CLI_FAIL	(-1)
62 #define	CLI_EXIT	(-2)
63 #define	CLI_CONT	(-3)
64 
65 #define	CLF_CMD		0x00000001	/* builtin command */
66 #define	CLF_ARG		0x00000002	/* boot argument directive */
67 
68 #define	CLF_IF		0x00000100	/* interface parameter */
69 #define	CLF_BM		0x00000200	/* bootmisc parameter */
70 
71 #define	CLF_VALSET	0x00010000	/* value set, may be null */
72 #define	CLF_HIDDEN	0x00020000	/* don't show its value (key) */
73 #define	CLF_VALMOD	0x00040000	/* value modified by the user */
74 
75 /*
76  * Macros for use in managing the flags in the cli_list[].
77  * The conventions we follow are:
78  *
79  *	CLF_VALSET is cleared	if a value is removed from varptr
80  *	CLF_VALSET is set	if a value has been placed in varptr
81  *				(that value need not be vetted)
82  *	CLF_HIDDEN is set	if a value must not be exposed to the user
83  *	CLF_HIDDEN is cleared	if a value can be exposed to the user
84  *	CLF_VALMOD is cleared	if a value in varptr has not been modified
85  *	CLF_VALMOD is set	if a value in varptr has been modified by
86  *				the user
87  */
88 #ifdef	DEBUG
89 #define	CLF_SETVAL(var)		{					\
90 					(((var)->flags) |= CLF_VALSET);	\
91 					printf("set %s\n", var->varname);\
92 				}
93 
94 #define	CLF_ISSET(var)		(printf("%s\n",				\
95 				    (((var)->flags) & CLF_VALSET) != 0	\
96 				    ? "is set" : "not set"),		\
97 				    ((((var)->flags) & CLF_VALSET) != 0))
98 
99 #define	CLF_CLRHIDDEN(var)	{					\
100 					(((var)->flags) &= ~CLF_HIDDEN); \
101 					printf("unhide %s\n", var->varname); \
102 				}
103 
104 #define	CLF_ISHIDDEN(var)	(printf("%s\n",				\
105 				    (((var)->flags) & CLF_HIDDEN) != 0	\
106 				    ? "is hidden" : "not hidden"),	\
107 				    ((((var)->flags) & CLF_HIDDEN) != 0))
108 
109 #define	CLF_MODVAL(var)		{					\
110 					(((var)->flags) |=		\
111 					(CLF_VALMOD | CLF_VALSET));	\
112 					printf("modified %s\n", var->varname);\
113 				}
114 
115 #define	CLF_ISMOD(var)		(printf("%s\n",				\
116 				    (((var)->flags) & CLF_VALMOD) != 0 \
117 				    ? "is set" : "not set"),	\
118 				    ((((var)->flags) & CLF_VALMOD) != 0))
119 #else	/* DEBUG */
120 
121 #define	CLF_SETVAL(var)		(((var)->flags) |= CLF_VALSET)
122 #define	CLF_ISSET(var)		((((var)->flags) & CLF_VALSET) != 0)
123 #define	CLF_CLRHIDDEN(var)	(((var)->flags) &= ~CLF_HIDDEN)
124 #define	CLF_ISHIDDEN(var)	((((var)->flags) & CLF_HIDDEN) != 0)
125 #define	CLF_MODVAL(var)		(((var)->flags) |= (CLF_VALMOD | CLF_VALSET))
126 #define	CLF_ISMOD(var)		((((var)->flags) & CLF_VALMOD) != 0)
127 
128 #endif	/* DEBUG */
129 
130 /*
131  * The width of the widest varname below - currently "subnet_mask".
132  */
133 #define	VAR_MAXWIDTH	strlen(BI_SUBNET_MASK)
134 
135 struct cli_ent;
136 typedef	int claction_t(struct cli_ent *, char *, boolean_t);
137 
138 typedef struct cli_ent {
139 	char   		*varname;
140 	claction_t	*action;
141 	int		flags;
142 	void		*varptr;
143 	uint_t		varlen;
144 	uint_t 		varmax;
145 } cli_ent_t;
146 
147 static cli_ent_t	 *find_cli_ent(char *varstr);
148 
149 static char		cmdbuf[2048];			/* interpreter buffer */
150 static char		hostip[INET_ADDRSTRLEN];
151 static char		subnet[INET_ADDRSTRLEN];
152 static char		router[INET_ADDRSTRLEN];
153 static char		hostname[MAXHOSTNAMELEN];
154 static char		httpproxy[INET_ADDRSTRLEN + 5];		/* a.b.c.d:p */
155 static char		bootserverURL[URL_MAX_STRLEN + 1];
156 static unsigned char	clientid[WB_MAX_CID_LEN];
157 static unsigned char	aeskey[AES_128_KEY_SIZE];
158 static unsigned char	des3key[DES3_KEY_SIZE];
159 static unsigned char	sha1key[WANBOOT_HMAC_KEY_SIZE];
160 static boolean_t	args_specified_prompt = B_FALSE;
161 
162 extern bc_handle_t	bc_handle;
163 extern int		getchar(void);
164 
165 static claction_t	clcid, clkey, clip, clstr, clurl, clhp;
166 static claction_t	clhelp, cllist, clprompt, cldhcp, cltest, clgo, clexit;
167 
168 static cli_ent_t cli_list[] = {
169 	/*
170 	 * Commands/bootargs:
171 	 */
172 	{ "test",		cltest,		CLF_ARG,
173 	    NULL,		0,		0			},
174 	{ "dhcp",		cldhcp,		CLF_ARG,
175 	    NULL,		0,		0			},
176 	{ "prompt",		clprompt,	CLF_CMD | CLF_ARG,
177 	    NULL,		0,		0			},
178 	{ "list",		cllist,		CLF_CMD,
179 	    NULL,		0,		0			},
180 	{ "help",		clhelp,		CLF_CMD,
181 	    NULL,		0,		0			},
182 	{ "go",			clgo,		CLF_CMD,
183 	    NULL,		0,		0			},
184 	{ "exit",		clexit,		CLF_CMD,
185 	    NULL,		0,		0			},
186 
187 	/*
188 	 * Interface:
189 	 */
190 	{ BI_HOST_IP,		clip,		CLF_IF,
191 	    hostip,		0,		sizeof (hostip)		},
192 	{ BI_SUBNET_MASK,	clip,		CLF_IF,
193 	    subnet,		0,		sizeof (subnet)		},
194 	{ BI_ROUTER_IP,		clip,		CLF_IF,
195 	    router,		0,		sizeof (router)		},
196 	{ BI_HOSTNAME,		clstr,		CLF_IF,
197 	    hostname,		0,		sizeof (hostname)	},
198 	{ BI_HTTP_PROXY,	clhp,		CLF_IF,
199 	    httpproxy,		0,		sizeof (httpproxy)	},
200 	{ BI_CLIENT_ID,		clcid,		CLF_IF,
201 	    clientid,		0,		sizeof (clientid)	},
202 
203 	/*
204 	 * Bootmisc:
205 	 */
206 	{ BI_AES_KEY,		clkey,		CLF_BM | CLF_HIDDEN,
207 	    aeskey,		0,		sizeof (aeskey)		},
208 	{ BI_3DES_KEY,		clkey,		CLF_BM | CLF_HIDDEN,
209 	    des3key,		0,		sizeof (des3key)	},
210 	{ BI_SHA1_KEY,		clkey,		CLF_BM | CLF_HIDDEN,
211 	    sha1key,		0,		sizeof (sha1key)	},
212 	{ BI_BOOTSERVER,	clurl,		CLF_BM,
213 	    bootserverURL,	0,		sizeof (bootserverURL)	},
214 };
215 
216 static int num_cli_ent = (sizeof (cli_list) / sizeof (cli_ent_t));
217 
218 /*
219  * Fetch a line from the user, handling backspace appropriately.
220  */
221 static int
222 editline(char *buf, int count)
223 {
224 	int	i = 0;
225 	char	c;
226 
227 	while (i < count - 1) {
228 		c = getchar();
229 		if (c == '\n') {
230 			break;
231 		} else if (c == '\b') {
232 			/* Clear for backspace. */
233 			if (i > 0)
234 				i--;
235 			continue;
236 		} else {
237 			buf[i++] = c;
238 		}
239 	}
240 	buf[i] = '\0';
241 	return (i);
242 }
243 
244 /*
245  * Assign a client-id to cliptr, or output cliptr's value as a client-id.
246  * On assignment the value is specified in valstr, either in hexascii or
247  * as a quoted string; on output its value is printed in hexascii.
248  */
249 static int
250 clcid(cli_ent_t *cliptr, char *valstr, boolean_t out)
251 {
252 	uint_t		len, vmax;
253 	boolean_t	hexascii = B_TRUE;
254 	char		buffer[2 * WB_MAX_CID_LEN + 1];
255 
256 	if (out) {
257 		len = cliptr->varlen * 2 + 1;
258 		(void) octet_to_hexascii(cliptr->varptr, cliptr->varlen,
259 		    buffer, &len);
260 		printf("%s", buffer);
261 		return (CLI_CONT);
262 	} else {
263 		len = strlen(valstr);
264 		vmax = cliptr->varmax - 1;	/* space for the prefix */
265 
266 		/*
267 		 * Check whether the value is a quoted string; if so, strip
268 		 * the quotes and note that it's not in hexascii.
269 		 */
270 		if ((valstr[0] == '"' || valstr[0] == '\'') &&
271 		    valstr[len-1] == valstr[0]) {
272 			hexascii = B_FALSE;
273 			++valstr;
274 			len -= 2;
275 			valstr[len] = '\0';
276 		} else {
277 			/*
278 			 * If the value contains any non-hex digits assume
279 			 * that it's not in hexascii.
280 			 */
281 			char	*p;
282 
283 			for (p = valstr; *p != '\0'; ++p) {
284 				if (!isxdigit(*p)) {
285 					hexascii = B_FALSE;
286 					break;
287 				}
288 			}
289 		}
290 
291 		if (hexascii) {
292 			if (len > vmax * 2 ||
293 			    hexascii_to_octet(valstr, len,
294 			    (char *)(cliptr->varptr), &vmax) != 0) {
295 				return (CLI_FAIL);
296 			}
297 			cliptr->varlen = vmax;
298 		} else {
299 			if (len > vmax) {
300 				return (CLI_FAIL);
301 			}
302 			bcopy(valstr, cliptr->varptr, len);
303 			cliptr->varlen = len;
304 		}
305 
306 		return (CLI_SET);
307 	}
308 }
309 
310 /*
311  * Assign a key to cliptr, or output cliptr's value as a key.
312  * On assignment the value is specified in valstr in hexascii;
313  * on output its value is printed in hexascii, provided the key
314  * was entered at the interpreter (not obtained from OBP and
315  * thus hidden).
316  */
317 static int
318 clkey(cli_ent_t *cliptr, char *valstr, boolean_t out)
319 {
320 	uint_t	len, vmax;
321 
322 	if (out) {
323 		char buffer[2 * WANBOOT_MAXKEYLEN + 1];
324 
325 		if (!CLF_ISHIDDEN(cliptr)) {
326 			len = cliptr->varlen * 2 + 1;
327 			(void) octet_to_hexascii(cliptr->varptr,
328 			    cliptr->varlen, buffer, &len);
329 			printf("%s", buffer);
330 		} else {
331 			printf("*HIDDEN*");
332 		}
333 		return (CLI_CONT);
334 	} else {
335 		len = strlen(valstr);
336 		vmax = cliptr->varmax;
337 		if (len != vmax * 2 || hexascii_to_octet(valstr, len,
338 		    cliptr->varptr, &vmax) != 0) {
339 			return (CLI_FAIL);
340 		}
341 		cliptr->varlen = vmax;
342 		CLF_CLRHIDDEN(cliptr);
343 		return (CLI_SET);
344 	}
345 }
346 
347 /*
348  * Assign an IP address to cliptr, or output cliptr's value as an
349  * IP address.  On assignment the value is specified in valstr in
350  * dotted-decimal format; on output its value is printed in dotted-
351  * decimal format.
352  */
353 static int
354 clip(cli_ent_t *cliptr, char *valstr, boolean_t out)
355 {
356 	uint_t		len;
357 
358 	if (out) {
359 		printf("%s", (char *)cliptr->varptr);
360 		return (CLI_CONT);
361 	}
362 
363 	if (inet_addr(valstr) == (in_addr_t)-1 ||
364 	    (len = strlen(valstr)) >= cliptr->varmax) {
365 		return (CLI_FAIL);
366 	}
367 
368 	(void) strcpy(cliptr->varptr, valstr);
369 	cliptr->varlen = len + 1;
370 	return (CLI_SET);
371 }
372 
373 /*
374  * Assign an arbitrary string to cliptr, or output cliptr's value as a string.
375  */
376 static int
377 clstr(cli_ent_t *cliptr, char *valstr, boolean_t out)
378 {
379 	uint_t	len;
380 
381 	if (out) {
382 		printf("%s", (char *)cliptr->varptr);
383 		return (CLI_CONT);
384 	} else {
385 		if ((len = strlen(valstr)) >= cliptr->varmax) {
386 			return (CLI_FAIL);
387 		} else {
388 			(void) strcpy(cliptr->varptr, valstr);
389 			cliptr->varlen = len + 1;
390 			return (CLI_SET);
391 		}
392 	}
393 }
394 
395 /*
396  * Assign a URL to cliptr (having verified the format), or output cliptr's
397  * value as a URL.  The host must be specified in dotted-decimal, and the
398  * scheme must not be https.
399  */
400 static int
401 clurl(cli_ent_t *cliptr, char *valstr, boolean_t out)
402 {
403 	url_t		u;
404 	uint_t		len;
405 
406 	if (out) {
407 		printf("%s", (char *)cliptr->varptr);
408 		return (CLI_CONT);
409 	}
410 
411 	if (url_parse(valstr, &u) != URL_PARSE_SUCCESS ||
412 	    u.https || inet_addr(u.hport.hostname) == (in_addr_t)-1 ||
413 	    (len = strlen(valstr)) >= cliptr->varmax) {
414 		return (CLI_FAIL);
415 	}
416 
417 	(void) strcpy(cliptr->varptr, valstr);
418 	cliptr->varlen = len + 1;
419 	return (CLI_SET);
420 }
421 
422 /*
423  * Assign a hostport to cliptr (having verified the format), or output cliptr's
424  * value as a hostport.  The host must be specified in dotted-decimal.
425  */
426 static int
427 clhp(cli_ent_t *cliptr, char *valstr, boolean_t out)
428 {
429 	url_hport_t	u;
430 	uint_t		len;
431 
432 	if (out) {
433 		printf("%s", (char *)cliptr->varptr);
434 		return (CLI_CONT);
435 	}
436 
437 	if (url_parse_hostport(valstr, &u, URL_DFLT_PROXY_PORT) !=
438 	    URL_PARSE_SUCCESS ||
439 	    inet_addr(u.hostname) == (in_addr_t)-1 ||
440 	    (len = strlen(valstr)) >= cliptr->varmax) {
441 		return (CLI_FAIL);
442 	}
443 
444 	(void) strcpy(cliptr->varptr, valstr);
445 	cliptr->varlen = len + 1;
446 	return (CLI_SET);
447 }
448 
449 /*
450  * Exit the interpreter and return to the booter.
451  */
452 /*ARGSUSED*/
453 static int
454 clgo(cli_ent_t *cliptr, char *valstr, boolean_t out)
455 {
456 	return (CLI_EXIT);
457 }
458 
459 /*
460  * Exit the interpreter and return to OBP.
461  */
462 /*ARGSUSED*/
463 static int
464 clexit(cli_ent_t *cliptr, char *valstr, boolean_t out)
465 {
466 	prom_exit_to_mon();
467 	/*NOTREACHED*/
468 	return (CLI_EXIT);
469 }
470 
471 /*
472  * Provide simple help information.
473  */
474 /*ARGSUSED*/
475 static int
476 clhelp(cli_ent_t *cliptr, char *valstr, boolean_t out)
477 {
478 	printf("var=val		- set variable\n");
479 	printf("var=		- unset variable\n");
480 	printf("var		- print variable\n");
481 	printf("list		- list variables and their values\n");
482 	printf("prompt		- prompt for unset variables\n");
483 	printf("go		- continue booting\n");
484 	printf("exit		- quit boot interpreter and return to OBP\n");
485 
486 	return (CLI_CONT);
487 }
488 
489 /*
490  * List variables and their current values.
491  */
492 /*ARGSUSED*/
493 static int
494 cllist(cli_ent_t *cliptr, char *valstr, boolean_t out)
495 {
496 	int	wanted = (int)(uintptr_t)valstr; /* use uintptr_t for gcc */
497 	int	i;
498 
499 	wanted  &= ~(CLF_CMD | CLF_ARG);
500 
501 	for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; cliptr++) {
502 		if ((cliptr->flags & (CLF_CMD | CLF_ARG)) != 0 ||
503 		    (cliptr->flags & wanted) == 0) {
504 			continue;
505 		}
506 		printf("%s: ", cliptr->varname);
507 		/*
508 		 * Line the values up - space to the width of the widest
509 		 * varname + 1 for the ':'.
510 		 */
511 		for (i = VAR_MAXWIDTH + 1 - strlen(cliptr->varname);
512 		    i > 0; --i) {
513 			printf(" ");
514 		}
515 
516 		if (CLF_ISSET(cliptr) || CLF_ISHIDDEN(cliptr)) {
517 			(void) cliptr->action(cliptr, NULL, B_TRUE);
518 			printf("\n");
519 		} else {
520 			printf("UNSET\n");
521 		}
522 	}
523 
524 	return (CLI_CONT);
525 }
526 
527 /*
528  * Prompt for wanted values.
529  */
530 /*ARGSUSED*/
531 static int
532 clprompt(cli_ent_t *cliptr, char *valstr, boolean_t out)
533 {
534 	char	*p;
535 	int	wanted = (int)(uintptr_t)valstr; /* use uintrptr_t for gcc */
536 
537 	/*
538 	 * If processing boot arguments, simply note the fact that clprompt()
539 	 * should be invoked later when other parameters may be supplied.
540 	 */
541 	if ((wanted & CLF_ARG) != 0) {
542 		args_specified_prompt = B_TRUE;
543 		return (CLI_CONT);
544 	}
545 	wanted  &= ~(CLF_CMD | CLF_ARG);
546 
547 	for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) {
548 		if ((cliptr->flags & wanted) == 0) {
549 			continue;
550 		}
551 
552 		printf("%s", cliptr->varname);
553 		if (CLF_ISSET(cliptr)) {
554 			printf(" [");
555 			(void) cliptr->action(cliptr, NULL, B_TRUE);
556 			printf("]");
557 		}
558 		printf("? ");
559 		(void) editline(cmdbuf, sizeof (cmdbuf));
560 		printf("\n");
561 
562 		p = cmdbuf;
563 		skipspace(p);
564 		if (*p == '\0') {	/* nothing there */
565 			continue;
566 		}
567 
568 		/* Get valstr and nul terminate */
569 		valstr = p;
570 		++p;
571 		skiptext(p);
572 		*p = '\0';
573 
574 		/* If empty value, do nothing */
575 		if (strlen(valstr) == 0) {
576 			continue;
577 		}
578 
579 		switch (cliptr->action(cliptr, valstr, B_FALSE)) {
580 		case CLI_SET:
581 			CLF_MODVAL(cliptr);
582 			break;
583 		case CLI_FAIL:
584 			printf("Incorrect format, parameter unchanged!\n");
585 			break;
586 		case CLI_EXIT:
587 			return (CLI_EXIT);
588 		case CLI_CONT:
589 			break;
590 		}
591 	}
592 
593 	return (CLI_CONT);
594 }
595 
596 /*
597  * If the PROM has done DHCP, bind the interface; otherwise do the full
598  * DHCP packet exchange.
599  */
600 /*ARGSUSED*/
601 static int
602 cldhcp(cli_ent_t *cliptr, char *valstr, boolean_t out)
603 {
604 	static boolean_t	first_time = B_TRUE;
605 	static int		ret = CLI_CONT;
606 
607 	if (first_time) {
608 		/*
609 		 * Set DHCP's idea of the client_id from our cached value.
610 		 */
611 		cliptr = find_cli_ent(BI_CLIENT_ID);
612 		if (CLF_ISMOD(cliptr)) {
613 			dhcp_set_client_id(cliptr->varptr, cliptr->varlen);
614 		}
615 
616 		bootlog("wanboot", BOOTLOG_INFO, "Starting DHCP configuration");
617 
618 		(void) ipv4_setpromiscuous(B_TRUE);
619 		if (dhcp() == 0) {
620 			bootlog("wanboot", BOOTLOG_INFO,
621 			    "DHCP configuration succeeded");
622 		} else {
623 			bootlog("wanboot", BOOTLOG_CRIT,
624 			    "DHCP configuration failed");
625 			ret = CLI_FAIL;
626 		}
627 		(void) ipv4_setpromiscuous(B_FALSE);
628 
629 		first_time = B_FALSE;
630 	}
631 
632 	return (ret);
633 }
634 
635 /*
636  * Invoke the socket test interpreter (for testing purposes only).
637  */
638 /*ARGSUSED*/
639 static int
640 cltest(cli_ent_t *cliptr, char *valstr, boolean_t out)
641 {
642 	(void) ipv4_setpromiscuous(B_FALSE);
643 	printf("\n");
644 	for (;;) {
645 		printf(TEST_PROMPT);
646 		if (editline(cmdbuf, sizeof (cmdbuf)) > 0) {
647 			printf("\n");
648 			(void) st_interpret(cmdbuf);
649 		} else {
650 			prom_exit_to_mon();
651 			/* NOTREACHED */
652 		}
653 	}
654 
655 	/* NOTREACHED */
656 	return (CLI_CONT);
657 }
658 
659 /*
660  * Return the cliptr corresponding to the named variable.
661  */
662 static cli_ent_t *
663 find_cli_ent(char *varstr)
664 {
665 	cli_ent_t	*cliptr;
666 
667 	for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) {
668 		if (strcmp(varstr, cliptr->varname) == 0) {
669 			return (cliptr);
670 		}
671 	}
672 
673 	return (NULL);
674 }
675 
676 /*
677  * Evaluate the commands provided by the user (either as "-o" boot arguments
678  * or interactively at the boot interpreter).
679  */
680 static int
681 cli_eval_buf(char *inbuf, int wanted)
682 {
683 	char		*p, *varstr, *end_varstr, *valstr, *end_valstr;
684 	boolean_t	assign;
685 	cli_ent_t	*cliptr;
686 
687 	for (p = inbuf; *p != '\0'; ) {
688 		skipspace(p);
689 
690 		/* If nothing more on line, go get the next one */
691 		if (*p == '\0') {
692 			break;
693 		} else if (*p == ',') {		/* orphan ',' ? */
694 			++p;
695 			continue;
696 		}
697 
698 		/* Get ptrs to start & end of variable */
699 		varstr = p;
700 		++p;
701 		skiptext(p);
702 		end_varstr = p;
703 		skipspace(p);
704 
705 		/* See if we're doing an assignment */
706 		valstr = NULL;
707 		if (*p != '=') {	/* nope, just printing */
708 			assign = B_FALSE;
709 		} else {
710 			assign = B_TRUE;
711 			++p;			/* past '=' */
712 			skipspace(p);
713 
714 			/* Assigning something? (else clear variable) */
715 			if (*p != '\0' && *p != ',') {
716 				/* Get ptrs to start & end of valstr */
717 				valstr = p;
718 				++p;
719 				skiptext(p);
720 				end_valstr = p;
721 				skipspace(p);
722 			}
723 		}
724 
725 		/* Skip ',' delimiter if present */
726 		if (*p == ',') {
727 			++p;
728 		}
729 
730 		/* Nul-terminate varstr and valstr (if appropriate) */
731 		*end_varstr = '\0';
732 		if (valstr != NULL) {
733 			*end_valstr = '\0';
734 		}
735 
736 		if ((cliptr = find_cli_ent(varstr)) == NULL) {
737 			printf("Unknown variable '%s'; ignored\n", varstr);
738 			continue;
739 		}
740 
741 		/*
742 		 * It's an error to specify a parameter which can only be a
743 		 * boot argument (and not a command) when not processing the
744 		 * boot arguments.
745 		 */
746 		if ((cliptr->flags & (CLF_CMD | CLF_ARG)) == CLF_ARG &&
747 		    (wanted & CLF_ARG) == 0) {
748 			printf("'%s' may only be specified as a "
749 			    "boot argument; ignored\n", varstr);
750 			continue;
751 		}
752 
753 		/*
754 		 * When doing an assignment, verify that it's not a command
755 		 * or argument name, and that it is permissible in the current
756 		 * context.  An 'empty' assignment (var=) is treated the same
757 		 * as a null assignment (var="").
758 		 *
759 		 * If processing the boot arguments, it is an error to not
760 		 * assign a value to a non-argument parameter.
761 		 */
762 		if (assign) {
763 			if ((cliptr->flags & (CLF_CMD | CLF_ARG)) != 0) {
764 				printf("'%s' is a command and cannot "
765 				    "be assigned\n", varstr);
766 				return (CLI_FAIL);
767 			}
768 			if ((cliptr->flags & wanted) == 0) {
769 				printf("'%s' cannot be assigned\n", varstr);
770 				return (CLI_FAIL);
771 			}
772 
773 			if (valstr == NULL) {
774 				cliptr->varlen = 0;
775 				CLF_MODVAL(cliptr);
776 				continue;
777 			}
778 		} else if ((wanted & CLF_ARG) != 0 &&
779 		    (cliptr->flags & (CLF_CMD | CLF_ARG)) == 0) {
780 			printf("'%s' must be assigned when specified in "
781 			    " the boot arguments\n", varstr);
782 			return (CLI_FAIL);
783 		}
784 
785 		/*
786 		 * Pass 'wanted' to command-handling functions, in particular
787 		 * clprompt() and cllist().
788 		 */
789 		if ((cliptr->flags & CLF_CMD) != 0) {
790 			/* use uintptr_t to suppress the gcc warning */
791 			valstr = (char *)(uintptr_t)wanted;
792 		}
793 
794 		/*
795 		 * Call the parameter's action function.
796 		 */
797 		switch (cliptr->action(cliptr, valstr, !assign)) {
798 		case CLI_SET:
799 			CLF_MODVAL(cliptr);
800 			break;
801 		case CLI_FAIL:
802 			printf("Incorrect format: variable '%s' not set\n",
803 			    cliptr->varname);
804 			break;
805 		case CLI_EXIT:
806 			return (CLI_EXIT);
807 		case CLI_CONT:
808 			if (!assign) {
809 				printf("\n");
810 			}
811 			break;
812 		}
813 	}
814 
815 	return (CLI_CONT);
816 }
817 
818 static void
819 cli_interpret(int wanted)
820 {
821 	printf("\n");
822 	do {
823 		printf(PROMPT);
824 		(void) editline(cmdbuf, sizeof (cmdbuf));
825 		printf("\n");
826 
827 	} while (cli_eval_buf(cmdbuf, wanted) != CLI_EXIT);
828 }
829 
830 #if	defined(__sparcv9)
831 /*
832  * This routine queries the PROM to see what encryption keys exist.
833  */
834 static void
835 get_prom_encr_keys()
836 {
837 	cli_ent_t *cliptr;
838 	char encr_key[WANBOOT_MAXKEYLEN];
839 	int keylen;
840 	int status;
841 	int ret;
842 
843 	/*
844 	 * At the top of the priority list, we have AES.
845 	 */
846 	ret = prom_get_security_key(WANBOOT_AES_128_KEY_NAME, encr_key,
847 	    WANBOOT_MAXKEYLEN, &keylen, &status);
848 	if ((ret == 0) && (status == 0) && (keylen == AES_128_KEY_SIZE)) {
849 		cliptr = find_cli_ent(BI_AES_KEY);
850 		bcopy(encr_key, cliptr->varptr, AES_128_KEY_SIZE);
851 		cliptr->varlen = AES_128_KEY_SIZE;
852 		CLF_MODVAL(cliptr);
853 	}
854 
855 	/*
856 	 * Next, 3DES.
857 	 */
858 	ret = prom_get_security_key(WANBOOT_DES3_KEY_NAME, encr_key,
859 	    WANBOOT_MAXKEYLEN, &keylen, &status);
860 	if ((ret == 0) && (status == 0) && (keylen == DES3_KEY_SIZE)) {
861 		cliptr = find_cli_ent(BI_3DES_KEY);
862 		bcopy(encr_key, cliptr->varptr, DES3_KEY_SIZE);
863 		cliptr->varlen = DES3_KEY_SIZE;
864 		CLF_MODVAL(cliptr);
865 	}
866 }
867 
868 /*
869  * This routine queries the PROM to see what hashing keys exist.
870  */
871 static void
872 get_prom_hash_keys()
873 {
874 	cli_ent_t *cliptr;
875 	char hash_key[WANBOOT_HMAC_KEY_SIZE];
876 	int keylen;
877 	int status;
878 	int ret;
879 
880 	/*
881 	 * The only supported key thus far is SHA1.
882 	 */
883 	ret = prom_get_security_key(WANBOOT_HMAC_SHA1_KEY_NAME, hash_key,
884 	    WANBOOT_HMAC_KEY_SIZE, &keylen, &status);
885 	if ((ret == 0) && (status == 0) && (keylen == WANBOOT_HMAC_KEY_SIZE)) {
886 		cliptr = find_cli_ent(BI_SHA1_KEY);
887 		bcopy(hash_key, cliptr->varptr, WANBOOT_HMAC_KEY_SIZE);
888 		cliptr->varlen = WANBOOT_HMAC_KEY_SIZE;
889 		CLF_MODVAL(cliptr);
890 	}
891 }
892 #endif	/* defined(__sparcv9) */
893 
894 /*
895  * For the given parameter type(s), get values from bootinfo and cache in
896  * the local variables used by the "boot>" interpreter.
897  */
898 static void
899 bootinfo_defaults(int which)
900 {
901 	cli_ent_t	*cliptr;
902 
903 	for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) {
904 		if ((cliptr->flags & which) != 0 && !CLF_ISSET(cliptr)) {
905 			size_t	len = cliptr->varmax;
906 
907 			if (bootinfo_get(cliptr->varname, cliptr->varptr,
908 			    &len, NULL) == BI_E_SUCCESS) {
909 				cliptr->varlen = len;
910 				CLF_SETVAL(cliptr);
911 			}
912 		}
913 	}
914 }
915 
916 /*
917  * For the given parameter type(s), store values entered at the "boot>"
918  * interpreter back into bootinfo.
919  */
920 static void
921 update_bootinfo(int which)
922 {
923 	cli_ent_t	*cliptr;
924 
925 	for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) {
926 		if ((cliptr->flags & which) != 0 && CLF_ISMOD(cliptr)) {
927 			(void) bootinfo_put(cliptr->varname,
928 			    cliptr->varptr, cliptr->varlen, 0);
929 		}
930 	}
931 }
932 
933 /*
934  * Return the net-config-strategy: "dhcp", "manual" or "rarp"
935  */
936 static char *
937 net_config_strategy(void)
938 {
939 	static char	ncs[8];		/* "dhcp" or "manual" */
940 	size_t		len = sizeof (ncs);
941 
942 	if (ncs[0] == '\0' &&
943 	    bootinfo_get(BI_NET_CONFIG_STRATEGY, ncs, &len, NULL) !=
944 	    BI_E_SUCCESS) {
945 		/*
946 		 * Support for old PROMs: create the net-config-strategy
947 		 * property under /chosen with an appropriate value.  If we
948 		 * have a bootp-response (not interested in its value, just
949 		 * its presence) then we did DHCP; otherwise configuration
950 		 * is manual.
951 		 */
952 		if (bootinfo_get(BI_BOOTP_RESPONSE, NULL, NULL,
953 		    NULL) == BI_E_BUF2SMALL) {
954 			(void) strcpy(ncs, "dhcp");
955 		} else {
956 			(void) strcpy(ncs, "manual");
957 		}
958 		(void) bootinfo_put(BI_NET_CONFIG_STRATEGY, ncs, strlen(ncs),
959 		    BI_R_CHOSEN);
960 
961 		bootlog("wanboot", BOOTLOG_INFO,
962 		    "Default net-config-strategy: %s", ncs);
963 	}
964 
965 	return (ncs);
966 }
967 
968 /*
969  * If there is no client-id property published in /chosen (by the PROM or the
970  * boot interpreter) provide a default client-id based on the MAC address of
971  * the client.
972  * As specified in RFC2132 (section 9.14), this is prefixed with a byte
973  * which specifies the ARP hardware type defined in RFC1700 (for Ethernet,
974  * this should be 1).
975  */
976 static void
977 generate_default_clientid(void)
978 {
979 	char	clid[WB_MAX_CID_LEN];
980 	size_t	len = sizeof (clid);
981 
982 	if (bootinfo_get(BI_CLIENT_ID, clid, &len, NULL) != BI_E_SUCCESS) {
983 		len = mac_get_addr_len() + 1;	/* include hwtype */
984 
985 		if (len > sizeof (clid)) {
986 			return;
987 		}
988 
989 		clid[0] = mac_arp_type(mac_get_type());
990 		bcopy(mac_get_addr_buf(), &clid[1], len - 1);
991 
992 		(void) bootinfo_put(BI_CLIENT_ID, clid, len, 0);
993 	}
994 }
995 
996 /*
997  * Determine the URL of the boot server from the 'file' parameter to OBP,
998  * the SbootURI or BootFile DHCP options, or the 'bootserver' value entered
999  * either as a "-o" argument or at the interpreter.
1000  */
1001 static void
1002 determine_bootserver_url(void)
1003 {
1004 	char	bs[URL_MAX_STRLEN + 1];
1005 	size_t	len;
1006 	url_t	url;
1007 
1008 	if (bootinfo_get(BI_BOOTSERVER, bs, &len, NULL) != BI_E_SUCCESS) {
1009 		/*
1010 		 * If OBP has published a network-boot-file property in
1011 		 * /chosen (or there is a DHCP BootFile or SbootURI vendor
1012 		 * option) and it's a URL, construct the bootserver URL
1013 		 * from it.
1014 		 */
1015 		len = URL_MAX_STRLEN;
1016 		if (bootinfo_get(BI_NETWORK_BOOT_FILE, bs, &len, NULL) !=
1017 		    BI_E_SUCCESS) {
1018 			len = URL_MAX_STRLEN;
1019 			if (bootinfo_get(BI_BOOTFILE, bs, &len, NULL) !=
1020 			    BI_E_SUCCESS) {
1021 				return;
1022 			}
1023 		}
1024 		if (url_parse(bs, &url) == URL_PARSE_SUCCESS) {
1025 			(void) bootinfo_put(BI_BOOTSERVER, bs, len, 0);
1026 		}
1027 	}
1028 }
1029 
1030 /*
1031  * Provide a classful subnet mask based on the client's IP address.
1032  */
1033 static in_addr_t
1034 generate_classful_subnet(in_addr_t client_ipaddr)
1035 {
1036 	struct in_addr	subnetmask;
1037 	char		*netstr;
1038 
1039 	if (IN_CLASSA(client_ipaddr)) {
1040 		subnetmask.s_addr = IN_CLASSA_NET;
1041 	} else if (IN_CLASSB(client_ipaddr)) {
1042 		subnetmask.s_addr = IN_CLASSB_NET;
1043 	} else if (IN_CLASSC(client_ipaddr)) {
1044 		subnetmask.s_addr = IN_CLASSC_NET;
1045 	} else {
1046 		subnetmask.s_addr = IN_CLASSE_NET;
1047 	}
1048 
1049 	netstr = inet_ntoa(subnetmask);
1050 	(void) bootinfo_put(BI_SUBNET_MASK, netstr, strlen(netstr) + 1, 0);
1051 
1052 	return (subnetmask.s_addr);
1053 }
1054 
1055 /*
1056  * Informational output to the user (if interactive) or the bootlogger.
1057  */
1058 static void
1059 info(const char *msg, boolean_t interactive)
1060 {
1061 	if (interactive) {
1062 		printf("%s\n", msg);
1063 	} else {
1064 		bootlog("wanboot", BOOTLOG_INFO, "%s", msg);
1065 	}
1066 }
1067 
1068 /*
1069  * Determine whether we have sufficient information to proceed with booting,
1070  * either for configuring the interface and downloading the bootconf file,
1071  * or for downloading the miniroot.
1072  */
1073 static int
1074 config_incomplete(int why, boolean_t interactive)
1075 {
1076 	boolean_t		error = B_FALSE;
1077 	char			buf[URL_MAX_STRLEN + 1];
1078 	size_t			len;
1079 	char			*urlstr;
1080 	url_t			u;
1081 	struct hostent		*hp;
1082 	in_addr_t		client_ipaddr, ipaddr, bsnet, pxnet;
1083 	static in_addr_t	subnetmask, clnet;
1084 	static boolean_t	have_router = B_FALSE;
1085 	static boolean_t	have_proxy = B_FALSE;
1086 	boolean_t		have_root_server = B_FALSE;
1087 	boolean_t		have_boot_logger = B_FALSE;
1088 	in_addr_t		rsnet, blnet;
1089 
1090 	/*
1091 	 * Note that 'have_router', 'have_proxy', 'subnetmask', and 'clnet'
1092 	 * are static, so that their values (gathered when checking the
1093 	 * interface configuration) may be used again when checking the boot
1094 	 * configuration.
1095 	 */
1096 	if (why == CLF_IF) {
1097 		/*
1098 		 * A valid host IP address is an absolute requirement.
1099 		 */
1100 		len = sizeof (buf);
1101 		if (bootinfo_get(BI_HOST_IP, buf, &len, NULL) == BI_E_SUCCESS) {
1102 			if ((client_ipaddr = inet_addr(buf)) == (in_addr_t)-1) {
1103 				info("host-ip invalid!", interactive);
1104 				error = B_TRUE;
1105 			}
1106 		} else {
1107 			info("host-ip not set!", interactive);
1108 			error = B_TRUE;
1109 		}
1110 
1111 		/*
1112 		 * If a subnet mask was provided, use it; otherwise infer it.
1113 		 */
1114 		len = sizeof (buf);
1115 		if (bootinfo_get(BI_SUBNET_MASK, buf, &len, NULL) ==
1116 		    BI_E_SUCCESS) {
1117 			if ((subnetmask = inet_addr(buf)) == (in_addr_t)-1) {
1118 				info("subnet-mask invalid!", interactive);
1119 				error = B_TRUE;
1120 			}
1121 		} else {
1122 			info("Defaulting to classful subnetting", interactive);
1123 
1124 			subnetmask = generate_classful_subnet(client_ipaddr);
1125 		}
1126 		clnet = client_ipaddr & subnetmask;
1127 
1128 		/*
1129 		 * A legal bootserver URL is also an absolute requirement.
1130 		 */
1131 		len = sizeof (buf);
1132 		if (bootinfo_get(BI_BOOTSERVER, buf, &len, NULL) ==
1133 		    BI_E_SUCCESS) {
1134 			if (url_parse(buf, &u) != URL_PARSE_SUCCESS ||
1135 			    u.https ||
1136 			    (ipaddr = inet_addr(u.hport.hostname)) ==
1137 			    (in_addr_t)-1) {
1138 				info("bootserver not legal URL!", interactive);
1139 				error = B_TRUE;
1140 			} else {
1141 				bsnet = ipaddr & subnetmask;
1142 			}
1143 		} else {
1144 			info("bootserver not specified!", interactive);
1145 			error = B_TRUE;
1146 		}
1147 
1148 		/*
1149 		 * Is there a correctly-defined router?
1150 		 */
1151 		len = sizeof (buf);
1152 		if (bootinfo_get(BI_ROUTER_IP, buf, &len, NULL) ==
1153 		    BI_E_SUCCESS) {
1154 			if ((ipaddr = inet_addr(buf)) == (in_addr_t)-1) {
1155 				info("router-ip invalid!", interactive);
1156 				error = B_TRUE;
1157 			} else if (clnet != (ipaddr & subnetmask)) {
1158 				info("router not on local subnet!",
1159 				    interactive);
1160 				error = B_TRUE;
1161 			} else {
1162 				have_router = B_TRUE;
1163 			}
1164 		}
1165 
1166 		/*
1167 		 * Is there a correctly-defined proxy?
1168 		 */
1169 		len = sizeof (buf);
1170 		if (bootinfo_get(BI_HTTP_PROXY, buf, &len, NULL) ==
1171 		    BI_E_SUCCESS) {
1172 			url_hport_t	u;
1173 
1174 			if (url_parse_hostport(buf, &u, URL_DFLT_PROXY_PORT) !=
1175 			    URL_PARSE_SUCCESS ||
1176 			    (ipaddr = inet_addr(u.hostname)) == (in_addr_t)-1) {
1177 				info("http-proxy port invalid!", interactive);
1178 				error = B_TRUE;
1179 			} else {
1180 				/*
1181 				 * The proxy is only of use to us if it's on
1182 				 * our local subnet, or if a router has been
1183 				 * specified (which should hopefully allow us
1184 				 * to access the proxy).
1185 				 */
1186 				pxnet = ipaddr & subnetmask;
1187 				have_proxy = (have_router || pxnet == clnet);
1188 			}
1189 		}
1190 
1191 		/*
1192 		 * If there is no router and no proxy (either on the local
1193 		 * subnet or reachable via a router), then the bootserver
1194 		 * URL must be on the local net.
1195 		 */
1196 		if (!error && !have_router && !have_proxy && bsnet != clnet) {
1197 			info("bootserver URL not on local subnet",
1198 			    interactive);
1199 			error = B_TRUE;
1200 		}
1201 	} else {
1202 		/*
1203 		 * There must be a correctly-defined root_server URL.
1204 		 */
1205 		if ((urlstr = bootconf_get(&bc_handle,
1206 		    BC_ROOT_SERVER)) == NULL) {
1207 			info("no root_server URL!", interactive);
1208 			error = B_TRUE;
1209 		} else if (url_parse(urlstr, &u) != URL_PARSE_SUCCESS) {
1210 			info("root_server not legal URL!", interactive);
1211 			error = B_TRUE;
1212 		} else if ((hp = gethostbyname(u.hport.hostname)) == NULL) {
1213 			info("cannot resolve root_server hostname!",
1214 			    interactive);
1215 			error = B_TRUE;
1216 		} else {
1217 			rsnet = *(in_addr_t *)hp->h_addr & subnetmask;
1218 			have_root_server = B_TRUE;
1219 		}
1220 
1221 		/*
1222 		 * Is there a correctly-defined (non-empty) boot_logger URL?
1223 		 */
1224 		if ((urlstr = bootconf_get(&bc_handle,
1225 		    BC_BOOT_LOGGER)) != NULL) {
1226 			if (url_parse(urlstr, &u) != URL_PARSE_SUCCESS) {
1227 				info("boot_logger not legal URL!", interactive);
1228 				error = B_TRUE;
1229 			} else if ((hp = gethostbyname(u.hport.hostname)) ==
1230 			    NULL) {
1231 				info("cannot resolve boot_logger hostname!",
1232 				    interactive);
1233 				error = B_TRUE;
1234 			} else {
1235 				blnet = *(in_addr_t *)hp->h_addr & subnetmask;
1236 				have_boot_logger = B_TRUE;
1237 			}
1238 		}
1239 
1240 		/*
1241 		 * If there is no router and no proxy (either on the local
1242 		 * subnet or reachable via a router), then the root_server
1243 		 * URL (and the boot_logger URL if specified) must be on the
1244 		 * local net.
1245 		 */
1246 		if (!error && !have_router && !have_proxy) {
1247 			if (have_root_server && rsnet != clnet) {
1248 				info("root_server URL not on local subnet",
1249 				    interactive);
1250 				error = B_TRUE;
1251 			}
1252 			if (have_boot_logger && blnet != clnet) {
1253 				info("boot_logger URL not on local subnet",
1254 				    interactive);
1255 				error = B_TRUE;
1256 			}
1257 		}
1258 	}
1259 
1260 	return (error);
1261 }
1262 
1263 /*
1264  * Actually setup our network interface with the values derived from the
1265  * PROM, DHCP or interactively from the user.
1266  */
1267 static void
1268 setup_interface()
1269 {
1270 	char		str[MAXHOSTNAMELEN];	/* will accomodate an IP too */
1271 	size_t		len;
1272 	struct in_addr	in_addr;
1273 
1274 	len = sizeof (str);
1275 	if (bootinfo_get(BI_HOST_IP, str, &len, NULL) == BI_E_SUCCESS &&
1276 	    (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) {
1277 		in_addr.s_addr = htonl(in_addr.s_addr);
1278 		ipv4_setipaddr(&in_addr);
1279 	}
1280 
1281 	len = sizeof (str);
1282 	if (bootinfo_get(BI_SUBNET_MASK, str, &len, NULL) == BI_E_SUCCESS &&
1283 	    (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) {
1284 		in_addr.s_addr = htonl(in_addr.s_addr);
1285 		ipv4_setnetmask(&in_addr);
1286 	}
1287 
1288 	len = sizeof (str);
1289 	if (bootinfo_get(BI_ROUTER_IP, str, &len, NULL) == BI_E_SUCCESS &&
1290 	    (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) {
1291 		in_addr.s_addr = htonl(in_addr.s_addr);
1292 		ipv4_setdefaultrouter(&in_addr);
1293 		(void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL, &in_addr);
1294 	}
1295 
1296 	len = sizeof (str);
1297 	if (bootinfo_get(BI_HOSTNAME, str, &len, NULL) == BI_E_SUCCESS) {
1298 		(void) sethostname(str, len);
1299 	}
1300 }
1301 
1302 boolean_t
1303 wanboot_init_interface(char *boot_arguments)
1304 {
1305 	boolean_t	interactive;
1306 	int		which;
1307 
1308 #if	defined(__sparcv9)
1309 	/*
1310 	 * Get the keys from PROM before we allow the user
1311 	 * to override them from the CLI.
1312 	 */
1313 	get_prom_encr_keys();
1314 	get_prom_hash_keys();
1315 #endif	/* defined(__sparcv9) */
1316 
1317 	/*
1318 	 * If there is already a bootp-response property under
1319 	 * /chosen then the PROM must have done DHCP for us;
1320 	 * invoke dhcp() to 'bind' the interface.
1321 	 */
1322 	if (bootinfo_get(BI_BOOTP_RESPONSE, NULL, NULL, NULL) ==
1323 	    BI_E_BUF2SMALL) {
1324 		(void) cldhcp(NULL, NULL, 0);
1325 	}
1326 
1327 	/*
1328 	 * Obtain default interface values from bootinfo.
1329 	 */
1330 	bootinfo_defaults(CLF_IF);
1331 
1332 	/*
1333 	 * Process the boot arguments (following the "-o" option).
1334 	 */
1335 	if (boot_arguments != NULL) {
1336 		(void) cli_eval_buf(boot_arguments,
1337 		    (CLF_ARG | CLF_IF | CLF_BM));
1338 	}
1339 
1340 	/*
1341 	 * Stash away any interface/bootmisc parameter values we got
1342 	 * from either the PROM or the boot arguments.
1343 	 */
1344 	update_bootinfo(CLF_IF | CLF_BM);
1345 
1346 	/*
1347 	 * If we don't already have a value for bootserver, try to
1348 	 * deduce one.  Refresh wbcli's idea of these values.
1349 	 */
1350 	determine_bootserver_url();
1351 	bootinfo_defaults(CLF_BM);
1352 
1353 	/*
1354 	 * Check that the information we have collected thus far is sufficient.
1355 	 */
1356 	interactive = args_specified_prompt;
1357 
1358 	if (interactive) {
1359 		/*
1360 		 * Drop into the boot interpreter to allow the input
1361 		 * of keys, bootserver and bootmisc, and in the case
1362 		 * that net-config-strategy == "manual" the interface
1363 		 * parameters.
1364 		 */
1365 		which = CLF_BM | CLF_CMD;
1366 		if (strcmp(net_config_strategy(), "manual") == 0)
1367 			which |= CLF_IF;
1368 
1369 		do {
1370 			cli_interpret(which);
1371 			update_bootinfo(CLF_IF | CLF_BM);
1372 		} while (config_incomplete(CLF_IF, interactive));
1373 	} else {
1374 		/*
1375 		 * The user is not to be given the opportunity to
1376 		 * enter further values; fail.
1377 		 */
1378 		if (config_incomplete(CLF_IF, interactive)) {
1379 			bootlog("wanboot", BOOTLOG_CRIT,
1380 			    "interface incorrectly configured");
1381 			return (B_FALSE);
1382 		}
1383 	}
1384 
1385 	/*
1386 	 * If a wanboot-enabled PROM hasn't processed client-id in
1387 	 * network-boot-arguments, or no value for client-id has been
1388 	 * specified to the boot interpreter, then provide a default
1389 	 * client-id based on our MAC address.
1390 	 */
1391 	generate_default_clientid();
1392 
1393 	/*
1394 	 * If net-config-strategy == "manual" then we must setup
1395 	 * the interface now; if "dhcp" then it will already have
1396 	 * been setup.
1397 	 */
1398 	if (strcmp(net_config_strategy(), "manual") == 0)
1399 		setup_interface();
1400 	return (B_TRUE);
1401 }
1402 
1403 boolean_t
1404 wanboot_verify_config(void)
1405 {
1406 	/*
1407 	 * Check that the wanboot.conf file defines a valid root_server
1408 	 * URL, and check that, if given, the boot_logger URL is valid.
1409 	 */
1410 	if (config_incomplete(0, B_FALSE)) {
1411 		bootlog("wanboot", BOOTLOG_CRIT,
1412 		    "incomplete boot configuration");
1413 		return (B_FALSE);
1414 	}
1415 	return (B_TRUE);
1416 }
1417