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