summaryrefslogtreecommitdiff
path: root/usr/src/psm/stand/boot/sparc/common/wbcli.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/psm/stand/boot/sparc/common/wbcli.c')
-rw-r--r--usr/src/psm/stand/boot/sparc/common/wbcli.c1425
1 files changed, 1425 insertions, 0 deletions
diff --git a/usr/src/psm/stand/boot/sparc/common/wbcli.c b/usr/src/psm/stand/boot/sparc/common/wbcli.c
new file mode 100644
index 0000000000..f6d6d9a00a
--- /dev/null
+++ b/usr/src/psm/stand/boot/sparc/common/wbcli.c
@@ -0,0 +1,1425 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* EXPORT DELETE START */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/salib.h>
+#include <sys/promif.h>
+#include <sys/wanboot_impl.h>
+#include <netinet/in.h>
+#include <parseURL.h>
+#include <bootlog.h>
+#include <sys/socket.h>
+#include <netinet/inetutil.h>
+#include <netinet/dhcp.h>
+#include <dhcp_impl.h>
+#include <lib/inet/mac.h>
+#include <lib/inet/ipv4.h>
+#include <lib/inet/dhcpv4.h>
+#include <lib/sock/sock_test.h>
+#include <sys/sunos_dhcp_class.h>
+#include <aes.h>
+#include <des3.h>
+#include <hmac_sha1.h>
+#include <netdb.h>
+#include <wanboot_conf.h>
+#include <bootinfo.h>
+/* EXPORT DELETE END */
+
+#include "wbcli.h"
+
+/* EXPORT DELETE START */
+
+#define skipspace(p) while (isspace(*(p))) ++p
+
+#define skiptext(p) while (*(p) != '\0' && !isspace(*(p)) && \
+ *(p) != '=' && *(p) != ',') ++p
+
+#define PROMPT "boot> "
+#define TEST_PROMPT "boot-test> "
+
+#define CLI_SET 0
+#define CLI_FAIL (-1)
+#define CLI_EXIT (-2)
+#define CLI_CONT (-3)
+
+#define CLF_CMD 0x00000001 /* builtin command */
+#define CLF_ARG 0x00000002 /* boot argument directive */
+
+#define CLF_IF 0x00000100 /* interface parameter */
+#define CLF_BM 0x00000200 /* bootmisc parameter */
+
+#define CLF_VALSET 0x00010000 /* value set, may be null */
+#define CLF_HIDDEN 0x00020000 /* don't show its value (key) */
+#define CLF_VALMOD 0x00040000 /* value modified by the user */
+
+/*
+ * Macros for use in managing the flags in the cli_list[].
+ * The conventions we follow are:
+ *
+ * CLF_VALSET is cleared if a value is removed from varptr
+ * CLF_VALSET is set if a value has been placed in varptr
+ * (that value need not be vetted)
+ * CLF_HIDDEN is set if a value must not be exposed to the user
+ * CLF_HIDDEN is cleared if a value can be exposed to the user
+ * CLF_VALMOD is cleared if a value in varptr has not been modified
+ * CLF_VALMOD is set if a value in varptr has been modified by
+ * the user
+ */
+#ifdef DEBUG
+#define CLF_SETVAL(var) { \
+ (((var)->flags) |= CLF_VALSET); \
+ printf("set %s\n", var->varname);\
+ }
+
+#define CLF_ISSET(var) (printf("%s\n", \
+ (((var)->flags) & CLF_VALSET) != 0 \
+ ? "is set" : "not set"), \
+ ((((var)->flags) & CLF_VALSET) != 0))
+
+#define CLF_CLRHIDDEN(var) { \
+ (((var)->flags) &= ~CLF_HIDDEN); \
+ printf("unhide %s\n", var->varname); \
+ }
+
+#define CLF_ISHIDDEN(var) (printf("%s\n", \
+ (((var)->flags) & CLF_HIDDEN) != 0 \
+ ? "is hidden" : "not hidden"), \
+ ((((var)->flags) & CLF_HIDDEN) != 0))
+
+#define CLF_MODVAL(var) { \
+ (((var)->flags) |= \
+ (CLF_VALMOD | CLF_VALSET)); \
+ printf("modified %s\n", var->varname);\
+ }
+
+#define CLF_ISMOD(var) (printf("%s\n", \
+ (((var)->flags) & CLF_VALMOD) != 0 \
+ ? "is set" : "not set"), \
+ ((((var)->flags) & CLF_VALMOD) != 0))
+#else /* DEBUG */
+
+#define CLF_SETVAL(var) (((var)->flags) |= CLF_VALSET)
+#define CLF_ISSET(var) ((((var)->flags) & CLF_VALSET) != 0)
+#define CLF_CLRHIDDEN(var) (((var)->flags) &= ~CLF_HIDDEN)
+#define CLF_ISHIDDEN(var) ((((var)->flags) & CLF_HIDDEN) != 0)
+#define CLF_MODVAL(var) (((var)->flags) |= (CLF_VALMOD | CLF_VALSET))
+#define CLF_ISMOD(var) ((((var)->flags) & CLF_VALMOD) != 0)
+
+#endif /* DEBUG */
+
+/*
+ * The width of the widest varname below - currently "subnet_mask".
+ */
+#define VAR_MAXWIDTH strlen(BI_SUBNET_MASK)
+
+struct cli_ent;
+typedef int claction_t(struct cli_ent *, char *, boolean_t);
+
+typedef struct cli_ent {
+ char *varname;
+ claction_t *action;
+ int flags;
+ void *varptr;
+ uint_t varlen;
+ uint_t varmax;
+} cli_ent_t;
+
+static cli_ent_t *find_cli_ent(char *varstr);
+
+static char cmdbuf[2048]; /* interpreter buffer */
+static char hostip[INET_ADDRSTRLEN];
+static char subnet[INET_ADDRSTRLEN];
+static char router[INET_ADDRSTRLEN];
+static char hostname[MAXHOSTNAMELEN];
+static char httpproxy[INET_ADDRSTRLEN + 5]; /* a.b.c.d:p */
+static char bootserverURL[URL_MAX_STRLEN + 1];
+static unsigned char clientid[WB_MAX_CID_LEN];
+static unsigned char aeskey[AES_128_KEY_SIZE];
+static unsigned char des3key[DES3_KEY_SIZE];
+static unsigned char sha1key[WANBOOT_HMAC_KEY_SIZE];
+static boolean_t args_specified_prompt = B_FALSE;
+
+extern bc_handle_t bc_handle;
+extern int getchar(void);
+
+static claction_t clcid, clkey, clip, clstr, clurl, clhp;
+static claction_t clhelp, cllist, clprompt, cldhcp, cltest, clgo, clexit;
+
+static cli_ent_t cli_list[] = {
+ /*
+ * Commands/bootargs:
+ */
+ { "test", cltest, CLF_ARG,
+ NULL, 0, 0 },
+ { "dhcp", cldhcp, CLF_ARG,
+ NULL, 0, 0 },
+ { "prompt", clprompt, CLF_CMD | CLF_ARG,
+ NULL, 0, 0 },
+ { "list", cllist, CLF_CMD,
+ NULL, 0, 0 },
+ { "help", clhelp, CLF_CMD,
+ NULL, 0, 0 },
+ { "go", clgo, CLF_CMD,
+ NULL, 0, 0 },
+ { "exit", clexit, CLF_CMD,
+ NULL, 0, 0 },
+
+ /*
+ * Interface:
+ */
+ { BI_HOST_IP, clip, CLF_IF,
+ hostip, 0, sizeof (hostip) },
+ { BI_SUBNET_MASK, clip, CLF_IF,
+ subnet, 0, sizeof (subnet) },
+ { BI_ROUTER_IP, clip, CLF_IF,
+ router, 0, sizeof (router) },
+ { BI_HOSTNAME, clstr, CLF_IF,
+ hostname, 0, sizeof (hostname) },
+ { BI_HTTP_PROXY, clhp, CLF_IF,
+ httpproxy, 0, sizeof (httpproxy) },
+ { BI_CLIENT_ID, clcid, CLF_IF,
+ clientid, 0, sizeof (clientid) },
+
+ /*
+ * Bootmisc:
+ */
+ { BI_AES_KEY, clkey, CLF_BM | CLF_HIDDEN,
+ aeskey, 0, sizeof (aeskey) },
+ { BI_3DES_KEY, clkey, CLF_BM | CLF_HIDDEN,
+ des3key, 0, sizeof (des3key) },
+ { BI_SHA1_KEY, clkey, CLF_BM | CLF_HIDDEN,
+ sha1key, 0, sizeof (sha1key) },
+ { BI_BOOTSERVER, clurl, CLF_BM,
+ bootserverURL, 0, sizeof (bootserverURL) },
+};
+
+static int num_cli_ent = (sizeof (cli_list) / sizeof (cli_ent_t));
+
+/*
+ * Fetch a line from the user, handling backspace appropriately.
+ */
+static int
+editline(char *buf, int count)
+{
+ int i = 0;
+ char c;
+
+ while (i < count - 1) {
+ c = getchar();
+ if (c == '\n') {
+ break;
+ } else if (c == '\b') {
+ /* Clear for backspace. */
+ if (i > 0)
+ i--;
+ continue;
+ } else {
+ buf[i++] = c;
+ }
+ }
+ buf[i] = '\0';
+ return (i);
+}
+
+/*
+ * Assign a client-id to cliptr, or output cliptr's value as a client-id.
+ * On assignment the value is specified in valstr, either in hexascii or
+ * as a quoted string; on output its value is printed in hexascii.
+ */
+static int
+clcid(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ uint_t len, vmax;
+ boolean_t hexascii = B_TRUE;
+ char buffer[2 * WB_MAX_CID_LEN + 1];
+
+ if (out) {
+ len = cliptr->varlen * 2 + 1;
+ (void) octet_to_hexascii(cliptr->varptr, cliptr->varlen,
+ buffer, &len);
+ printf("%s", buffer);
+ return (CLI_CONT);
+ } else {
+ len = strlen(valstr);
+ vmax = cliptr->varmax - 1; /* space for the prefix */
+
+ /*
+ * Check whether the value is a quoted string; if so, strip
+ * the quotes and note that it's not in hexascii.
+ */
+ if ((valstr[0] == '"' || valstr[0] == '\'') &&
+ valstr[len-1] == valstr[0]) {
+ hexascii = B_FALSE;
+ ++valstr;
+ len -= 2;
+ valstr[len] = '\0';
+ } else {
+ /*
+ * If the value contains any non-hex digits assume
+ * that it's not in hexascii.
+ */
+ char *p;
+
+ for (p = valstr; *p != '\0'; ++p) {
+ if (!isxdigit(*p)) {
+ hexascii = B_FALSE;
+ break;
+ }
+ }
+ }
+
+ if (hexascii) {
+ if (len > vmax * 2 ||
+ hexascii_to_octet(valstr, len,
+ (char *)(cliptr->varptr), &vmax) != 0) {
+ return (CLI_FAIL);
+ }
+ cliptr->varlen = vmax;
+ } else {
+ if (len > vmax) {
+ return (CLI_FAIL);
+ }
+ bcopy(valstr, cliptr->varptr, len);
+ cliptr->varlen = len;
+ }
+
+ return (CLI_SET);
+ }
+}
+
+/*
+ * Assign a key to cliptr, or output cliptr's value as a key.
+ * On assignment the value is specified in valstr in hexascii;
+ * on output its value is printed in hexascii, provided the key
+ * was entered at the interpreter (not obtained from OBP and
+ * thus hidden).
+ */
+static int
+clkey(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ uint_t len, vmax;
+
+ if (out) {
+ char buffer[2 * WANBOOT_MAXKEYLEN + 1];
+
+ if (!CLF_ISHIDDEN(cliptr)) {
+ len = cliptr->varlen * 2 + 1;
+ (void) octet_to_hexascii(cliptr->varptr,
+ cliptr->varlen, buffer, &len);
+ printf("%s", buffer);
+ } else {
+ printf("*HIDDEN*");
+ }
+ return (CLI_CONT);
+ } else {
+ len = strlen(valstr);
+ vmax = cliptr->varmax;
+ if (len != vmax * 2 || hexascii_to_octet(valstr, len,
+ cliptr->varptr, &vmax) != 0) {
+ return (CLI_FAIL);
+ }
+ cliptr->varlen = vmax;
+ CLF_CLRHIDDEN(cliptr);
+ return (CLI_SET);
+ }
+}
+
+/*
+ * Assign an IP address to cliptr, or output cliptr's value as an
+ * IP address. On assignment the value is specified in valstr in
+ * dotted-decimal format; on output its value is printed in dotted-
+ * decimal format.
+ */
+static int
+clip(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ uint_t len;
+
+ if (out) {
+ printf("%s", (char *)cliptr->varptr);
+ return (CLI_CONT);
+ }
+
+ if (inet_addr(valstr) == (in_addr_t)-1 ||
+ (len = strlen(valstr)) >= cliptr->varmax) {
+ return (CLI_FAIL);
+ }
+
+ (void) strcpy(cliptr->varptr, valstr);
+ cliptr->varlen = len + 1;
+ return (CLI_SET);
+}
+
+/*
+ * Assign an arbitrary string to cliptr, or output cliptr's value as a string.
+ */
+static int
+clstr(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ uint_t len;
+
+ if (out) {
+ printf("%s", (char *)cliptr->varptr);
+ return (CLI_CONT);
+ } else {
+ if ((len = strlen(valstr)) >= cliptr->varmax) {
+ return (CLI_FAIL);
+ } else {
+ (void) strcpy(cliptr->varptr, valstr);
+ cliptr->varlen = len + 1;
+ return (CLI_SET);
+ }
+ }
+}
+
+/*
+ * Assign a URL to cliptr (having verified the format), or output cliptr's
+ * value as a URL. The host must be specified in dotted-decimal, and the
+ * scheme must not be https.
+ */
+static int
+clurl(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ url_t u;
+ uint_t len;
+
+ if (out) {
+ printf("%s", (char *)cliptr->varptr);
+ return (CLI_CONT);
+ }
+
+ if (url_parse(valstr, &u) != URL_PARSE_SUCCESS ||
+ u.https || inet_addr(u.hport.hostname) == (in_addr_t)-1 ||
+ (len = strlen(valstr)) >= cliptr->varmax) {
+ return (CLI_FAIL);
+ }
+
+ (void) strcpy(cliptr->varptr, valstr);
+ cliptr->varlen = len + 1;
+ return (CLI_SET);
+}
+
+/*
+ * Assign a hostport to cliptr (having verified the format), or output cliptr's
+ * value as a hostport. The host must be specified in dotted-decimal.
+ */
+static int
+clhp(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ url_hport_t u;
+ uint_t len;
+
+ if (out) {
+ printf("%s", (char *)cliptr->varptr);
+ return (CLI_CONT);
+ }
+
+ if (url_parse_hostport(valstr, &u, URL_DFLT_PROXY_PORT) !=
+ URL_PARSE_SUCCESS ||
+ inet_addr(u.hostname) == (in_addr_t)-1 ||
+ (len = strlen(valstr)) >= cliptr->varmax) {
+ return (CLI_FAIL);
+ }
+
+ (void) strcpy(cliptr->varptr, valstr);
+ cliptr->varlen = len + 1;
+ return (CLI_SET);
+}
+
+/*
+ * Exit the interpreter and return to the booter.
+ */
+/*ARGSUSED*/
+static int
+clgo(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ return (CLI_EXIT);
+}
+
+/*
+ * Exit the interpreter and return to OBP.
+ */
+/*ARGSUSED*/
+static int
+clexit(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ prom_exit_to_mon();
+ /*NOTREACHED*/
+ return (CLI_EXIT);
+}
+
+/*
+ * Provide simple help information.
+ */
+/*ARGSUSED*/
+static int
+clhelp(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ printf("var=val - set variable\n");
+ printf("var= - unset variable\n");
+ printf("var - print variable\n");
+ printf("list - list variables and their values\n");
+ printf("prompt - prompt for unset variables\n");
+ printf("go - continue booting\n");
+ printf("exit - quit boot interpreter and return to OBP\n");
+
+ return (CLI_CONT);
+}
+
+/*
+ * List variables and their current values.
+ */
+/*ARGSUSED*/
+static int
+cllist(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ int wanted = (int)valstr;
+ int i;
+
+ wanted &= ~(CLF_CMD | CLF_ARG);
+
+ for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; cliptr++) {
+ if ((cliptr->flags & (CLF_CMD | CLF_ARG)) != 0 ||
+ (cliptr->flags & wanted) == 0) {
+ continue;
+ }
+ printf("%s: ", cliptr->varname);
+ /*
+ * Line the values up - space to the width of the widest
+ * varname + 1 for the ':'.
+ */
+ for (i = VAR_MAXWIDTH + 1 - strlen(cliptr->varname);
+ i > 0; --i) {
+ printf(" ");
+ }
+
+ if (CLF_ISSET(cliptr) || CLF_ISHIDDEN(cliptr)) {
+ (void) cliptr->action(cliptr, NULL, B_TRUE);
+ printf("\n");
+ } else {
+ printf("UNSET\n");
+ }
+ }
+
+ return (CLI_CONT);
+}
+
+/*
+ * Prompt for wanted values.
+ */
+/*ARGSUSED*/
+static int
+clprompt(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ char *p;
+ int wanted = (int)valstr;
+
+ /*
+ * If processing boot arguments, simply note the fact that clprompt()
+ * should be invoked later when other parameters may be supplied.
+ */
+ if ((wanted & CLF_ARG) != 0) {
+ args_specified_prompt = B_TRUE;
+ return (CLI_CONT);
+ }
+ wanted &= ~(CLF_CMD | CLF_ARG);
+
+ for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) {
+ if ((cliptr->flags & wanted) == 0) {
+ continue;
+ }
+
+ printf("%s", cliptr->varname);
+ if (CLF_ISSET(cliptr)) {
+ printf(" [");
+ (void) cliptr->action(cliptr, NULL, B_TRUE);
+ printf("]");
+ }
+ printf("? ");
+ (void) editline(cmdbuf, sizeof (cmdbuf));
+ printf("\n");
+
+ p = cmdbuf;
+ skipspace(p);
+ if (*p == '\0') { /* nothing there */
+ continue;
+ }
+
+ /* Get valstr and nul terminate */
+ valstr = p;
+ ++p;
+ skiptext(p);
+ *p = '\0';
+
+ /* If empty value, do nothing */
+ if (strlen(valstr) == 0) {
+ continue;
+ }
+
+ switch (cliptr->action(cliptr, valstr, B_FALSE)) {
+ case CLI_SET:
+ CLF_MODVAL(cliptr);
+ break;
+ case CLI_FAIL:
+ printf("Incorrect format, parameter unchanged!\n");
+ break;
+ case CLI_EXIT:
+ return (CLI_EXIT);
+ case CLI_CONT:
+ break;
+ }
+ }
+
+ return (CLI_CONT);
+}
+
+/*
+ * If the PROM has done DHCP, bind the interface; otherwise do the full
+ * DHCP packet exchange.
+ */
+/*ARGSUSED*/
+static int
+cldhcp(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ static boolean_t first_time = B_TRUE;
+ static int ret = CLI_CONT;
+
+ if (first_time) {
+ /*
+ * Set DHCP's idea of the client_id from our cached value.
+ */
+ cliptr = find_cli_ent(BI_CLIENT_ID);
+ if (CLF_ISMOD(cliptr)) {
+ dhcp_set_client_id(cliptr->varptr, cliptr->varlen);
+ }
+
+ bootlog("wanboot", BOOTLOG_INFO, "Starting DHCP configuration");
+
+ (void) ipv4_setpromiscuous(B_TRUE);
+ if (dhcp() == 0) {
+ bootlog("wanboot", BOOTLOG_INFO,
+ "DHCP configuration succeeded");
+ } else {
+ bootlog("wanboot", BOOTLOG_CRIT,
+ "DHCP configuration failed");
+ ret = CLI_FAIL;
+ }
+ (void) ipv4_setpromiscuous(B_FALSE);
+
+ first_time = B_FALSE;
+ }
+
+ return (ret);
+}
+
+/*
+ * Invoke the socket test interpreter (for testing purposes only).
+ */
+/*ARGSUSED*/
+static int
+cltest(cli_ent_t *cliptr, char *valstr, boolean_t out)
+{
+ (void) ipv4_setpromiscuous(B_FALSE);
+ printf("\n");
+ for (;;) {
+ printf(TEST_PROMPT);
+ if (editline(cmdbuf, sizeof (cmdbuf)) > 0) {
+ printf("\n");
+ (void) st_interpret(cmdbuf);
+ } else {
+ prom_exit_to_mon();
+ /* NOTREACHED */
+ }
+ }
+
+ /* NOTREACHED */
+ return (CLI_CONT);
+}
+
+/*
+ * Return the cliptr corresponding to the named variable.
+ */
+static cli_ent_t *
+find_cli_ent(char *varstr)
+{
+ cli_ent_t *cliptr;
+
+ for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) {
+ if (strcmp(varstr, cliptr->varname) == 0) {
+ return (cliptr);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Evaluate the commands provided by the user (either as "-o" boot arguments
+ * or interactively at the boot interpreter).
+ */
+static int
+cli_eval_buf(char *inbuf, int wanted)
+{
+ char *p, *varstr, *end_varstr, *valstr, *end_valstr;
+ boolean_t assign;
+ cli_ent_t *cliptr;
+
+ for (p = inbuf; *p != '\0'; ) {
+ skipspace(p);
+
+ /* If nothing more on line, go get the next one */
+ if (*p == '\0') {
+ break;
+ } else if (*p == ',') { /* orphan ',' ? */
+ ++p;
+ continue;
+ }
+
+ /* Get ptrs to start & end of variable */
+ varstr = p;
+ ++p;
+ skiptext(p);
+ end_varstr = p;
+ skipspace(p);
+
+ /* See if we're doing an assignment */
+ valstr = NULL;
+ if (*p != '=') { /* nope, just printing */
+ assign = B_FALSE;
+ } else {
+ assign = B_TRUE;
+ ++p; /* past '=' */
+ skipspace(p);
+
+ /* Assigning something? (else clear variable) */
+ if (*p != '\0' && *p != ',') {
+ /* Get ptrs to start & end of valstr */
+ valstr = p;
+ ++p;
+ skiptext(p);
+ end_valstr = p;
+ skipspace(p);
+ }
+ }
+
+ /* Skip ',' delimiter if present */
+ if (*p == ',') {
+ ++p;
+ }
+
+ /* Nul-terminate varstr and valstr (if appropriate) */
+ *end_varstr = '\0';
+ if (valstr != NULL) {
+ *end_valstr = '\0';
+ }
+
+ if ((cliptr = find_cli_ent(varstr)) == NULL) {
+ printf("Unknown variable '%s'; ignored\n", varstr);
+ continue;
+ }
+
+ /*
+ * It's an error to specify a parameter which can only be a
+ * boot argument (and not a command) when not processing the
+ * boot arguments.
+ */
+ if ((cliptr->flags & (CLF_CMD | CLF_ARG)) == CLF_ARG &&
+ (wanted & CLF_ARG) == 0) {
+ printf("'%s' may only be specified as a "
+ "boot argument; ignored\n", varstr);
+ continue;
+ }
+
+ /*
+ * When doing an assignment, verify that it's not a command
+ * or argument name, and that it is permissible in the current
+ * context. An 'empty' assignment (var=) is treated the same
+ * as a null assignment (var="").
+ *
+ * If processing the boot arguments, it is an error to not
+ * assign a value to a non-argument parameter.
+ */
+ if (assign) {
+ if ((cliptr->flags & (CLF_CMD | CLF_ARG)) != 0) {
+ printf("'%s' is a command and cannot "
+ "be assigned\n", varstr);
+ return (CLI_FAIL);
+ }
+ if ((cliptr->flags & wanted) == 0) {
+ printf("'%s' cannot be assigned\n", varstr);
+ return (CLI_FAIL);
+ }
+
+ if (valstr == NULL) {
+ cliptr->varlen = 0;
+ CLF_MODVAL(cliptr);
+ continue;
+ }
+ } else if ((wanted & CLF_ARG) != 0 &&
+ (cliptr->flags & (CLF_CMD | CLF_ARG)) == 0) {
+ printf("'%s' must be assigned when specified in "
+ " the boot arguments\n", varstr);
+ return (CLI_FAIL);
+ }
+
+ /*
+ * Pass 'wanted' to command-handling functions, in particular
+ * clprompt() and cllist().
+ */
+ if ((cliptr->flags & CLF_CMD) != 0) {
+ valstr = (char *)wanted;
+ }
+
+ /*
+ * Call the parameter's action function.
+ */
+ switch (cliptr->action(cliptr, valstr, !assign)) {
+ case CLI_SET:
+ CLF_MODVAL(cliptr);
+ break;
+ case CLI_FAIL:
+ printf("Incorrect format: variable '%s' not set\n",
+ cliptr->varname);
+ break;
+ case CLI_EXIT:
+ return (CLI_EXIT);
+ case CLI_CONT:
+ if (!assign) {
+ printf("\n");
+ }
+ break;
+ }
+ }
+
+ return (CLI_CONT);
+}
+
+static void
+cli_interpret(int wanted)
+{
+ printf("\n");
+ do {
+ printf(PROMPT);
+ (void) editline(cmdbuf, sizeof (cmdbuf));
+ printf("\n");
+
+ } while (cli_eval_buf(cmdbuf, wanted) != CLI_EXIT);
+}
+
+#if defined(__sparcv9)
+/*
+ * This routine queries the PROM to see what encryption keys exist.
+ */
+static void
+get_prom_encr_keys()
+{
+ cli_ent_t *cliptr;
+ char encr_key[WANBOOT_MAXKEYLEN];
+ int keylen;
+ int status;
+ int ret;
+
+ /*
+ * At the top of the priority list, we have AES.
+ */
+ ret = prom_get_security_key(WANBOOT_AES_128_KEY_NAME, encr_key,
+ WANBOOT_MAXKEYLEN, &keylen, &status);
+ if ((ret == 0) && (status == 0) && (keylen == AES_128_KEY_SIZE)) {
+ cliptr = find_cli_ent(BI_AES_KEY);
+ bcopy(encr_key, cliptr->varptr, AES_128_KEY_SIZE);
+ cliptr->varlen = AES_128_KEY_SIZE;
+ CLF_MODVAL(cliptr);
+ }
+
+ /*
+ * Next, 3DES.
+ */
+ ret = prom_get_security_key(WANBOOT_DES3_KEY_NAME, encr_key,
+ WANBOOT_MAXKEYLEN, &keylen, &status);
+ if ((ret == 0) && (status == 0) && (keylen == DES3_KEY_SIZE)) {
+ cliptr = find_cli_ent(BI_3DES_KEY);
+ bcopy(encr_key, cliptr->varptr, DES3_KEY_SIZE);
+ cliptr->varlen = DES3_KEY_SIZE;
+ CLF_MODVAL(cliptr);
+ }
+}
+
+/*
+ * This routine queries the PROM to see what hashing keys exist.
+ */
+static void
+get_prom_hash_keys()
+{
+ cli_ent_t *cliptr;
+ char hash_key[WANBOOT_HMAC_KEY_SIZE];
+ int keylen;
+ int status;
+ int ret;
+
+ /*
+ * The only supported key thus far is SHA1.
+ */
+ ret = prom_get_security_key(WANBOOT_HMAC_SHA1_KEY_NAME, hash_key,
+ WANBOOT_HMAC_KEY_SIZE, &keylen, &status);
+ if ((ret == 0) && (status == 0) && (keylen == WANBOOT_HMAC_KEY_SIZE)) {
+ cliptr = find_cli_ent(BI_SHA1_KEY);
+ bcopy(hash_key, cliptr->varptr, WANBOOT_HMAC_KEY_SIZE);
+ cliptr->varlen = WANBOOT_HMAC_KEY_SIZE;
+ CLF_MODVAL(cliptr);
+ }
+}
+#endif /* defined(__sparcv9) */
+
+/*
+ * For the given parameter type(s), get values from bootinfo and cache in
+ * the local variables used by the "boot>" interpreter.
+ */
+static void
+bootinfo_defaults(int which)
+{
+ cli_ent_t *cliptr;
+
+ for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) {
+ if ((cliptr->flags & which) != 0 && !CLF_ISSET(cliptr)) {
+ size_t len = cliptr->varmax;
+
+ if (bootinfo_get(cliptr->varname, cliptr->varptr,
+ &len, NULL) == BI_E_SUCCESS) {
+ cliptr->varlen = len;
+ CLF_SETVAL(cliptr);
+ }
+ }
+ }
+}
+
+/*
+ * For the given parameter type(s), store values entered at the "boot>"
+ * interpreter back into bootinfo.
+ */
+static void
+update_bootinfo(int which)
+{
+ cli_ent_t *cliptr;
+
+ for (cliptr = cli_list; cliptr < &cli_list[num_cli_ent]; ++cliptr) {
+ if ((cliptr->flags & which) != 0 && CLF_ISMOD(cliptr)) {
+ (void) bootinfo_put(cliptr->varname,
+ cliptr->varptr, cliptr->varlen, 0);
+ }
+ }
+}
+
+/*
+ * Return the net-config-strategy: "dhcp", "manual" or "rarp"
+ */
+static char *
+net_config_strategy(void)
+{
+ static char ncs[8]; /* "dhcp" or "manual" */
+ size_t len = sizeof (ncs);
+
+ if (ncs[0] == '\0' &&
+ bootinfo_get(BI_NET_CONFIG_STRATEGY, ncs, &len, NULL) !=
+ BI_E_SUCCESS) {
+ /*
+ * Support for old PROMs: create the net-config-strategy
+ * property under /chosen with an appropriate value. If we
+ * have a bootp-response (not interested in its value, just
+ * its presence) then we did DHCP; otherwise configuration
+ * is manual.
+ */
+ if (bootinfo_get(BI_BOOTP_RESPONSE, NULL, NULL,
+ NULL) == BI_E_BUF2SMALL) {
+ (void) strcpy(ncs, "dhcp");
+ } else {
+ (void) strcpy(ncs, "manual");
+ }
+ (void) bootinfo_put(BI_NET_CONFIG_STRATEGY, ncs, strlen(ncs),
+ BI_R_CHOSEN);
+
+ bootlog("wanboot", BOOTLOG_INFO,
+ "Default net-config-strategy: %s", ncs);
+ }
+
+ return (ncs);
+}
+
+/*
+ * If there is no client-id property published in /chosen (by the PROM or the
+ * boot interpreter) provide a default client-id based on the MAC address of
+ * the client.
+ * As specified in RFC2132 (section 9.14), this is prefixed with a byte
+ * which specifies the ARP hardware type defined in RFC1700 (for Ethernet,
+ * this should be 1).
+ */
+static void
+generate_default_clientid(void)
+{
+ char clid[WB_MAX_CID_LEN];
+ size_t len = sizeof (clid);
+
+ if (bootinfo_get(BI_CLIENT_ID, clid, &len, NULL) != BI_E_SUCCESS) {
+ len = mac_get_addr_len() + 1; /* include hwtype */
+
+ if (len > sizeof (clid)) {
+ return;
+ }
+
+ clid[0] = mac_arp_type(mac_get_type());
+ bcopy(mac_get_addr_buf(), &clid[1], len - 1);
+
+ (void) bootinfo_put(BI_CLIENT_ID, clid, len, 0);
+ }
+}
+
+/*
+ * Determine the URL of the boot server from the 'file' parameter to OBP,
+ * the SbootURI or BootFile DHCP options, or the 'bootserver' value entered
+ * either as a "-o" argument or at the interpreter.
+ */
+static void
+determine_bootserver_url(void)
+{
+ char bs[URL_MAX_STRLEN + 1];
+ size_t len;
+ url_t url;
+
+ if (bootinfo_get(BI_BOOTSERVER, bs, &len, NULL) != BI_E_SUCCESS) {
+ /*
+ * If OBP has published a network-boot-file property in
+ * /chosen (or there is a DHCP BootFile or SbootURI vendor
+ * option) and it's a URL, construct the bootserver URL
+ * from it.
+ */
+ len = URL_MAX_STRLEN;
+ if (bootinfo_get(BI_NETWORK_BOOT_FILE, bs, &len, NULL) !=
+ BI_E_SUCCESS) {
+ len = URL_MAX_STRLEN;
+ if (bootinfo_get(BI_BOOTFILE, bs, &len, NULL) !=
+ BI_E_SUCCESS) {
+ return;
+ }
+ }
+ if (url_parse(bs, &url) == URL_PARSE_SUCCESS) {
+ (void) bootinfo_put(BI_BOOTSERVER, bs, len, 0);
+ }
+ }
+}
+
+/*
+ * Provide a classful subnet mask based on the client's IP address.
+ */
+static in_addr_t
+generate_classful_subnet(in_addr_t client_ipaddr)
+{
+ struct in_addr subnetmask;
+ char *netstr;
+
+ if (IN_CLASSA(client_ipaddr)) {
+ subnetmask.s_addr = IN_CLASSA_NET;
+ } else if (IN_CLASSB(client_ipaddr)) {
+ subnetmask.s_addr = IN_CLASSB_NET;
+ } else {
+ subnetmask.s_addr = IN_CLASSC_NET;
+ }
+
+ netstr = inet_ntoa(subnetmask);
+ (void) bootinfo_put(BI_SUBNET_MASK, netstr, strlen(netstr) + 1, 0);
+
+ return (subnetmask.s_addr);
+}
+
+/*
+ * Informational output to the user (if interactive) or the bootlogger.
+ */
+static void
+info(const char *msg, boolean_t interactive)
+{
+ if (interactive) {
+ printf("%s\n", msg);
+ } else {
+ bootlog("wanboot", BOOTLOG_INFO, "%s", msg);
+ }
+}
+
+/*
+ * Determine whether we have sufficient information to proceed with booting,
+ * either for configuring the interface and downloading the bootconf file,
+ * or for downloading the miniroot.
+ */
+static int
+config_incomplete(int why, boolean_t interactive)
+{
+ boolean_t error = B_FALSE;
+ char buf[URL_MAX_STRLEN + 1];
+ size_t len;
+ char *urlstr;
+ url_t u;
+ struct hostent *hp;
+ in_addr_t client_ipaddr, ipaddr, bsnet, pxnet;
+ static in_addr_t subnetmask, clnet;
+ static boolean_t have_router = B_FALSE;
+ static boolean_t have_proxy = B_FALSE;
+ boolean_t have_root_server = B_FALSE;
+ boolean_t have_boot_logger = B_FALSE;
+ in_addr_t rsnet, blnet;
+
+ /*
+ * Note that 'have_router', 'have_proxy', 'subnetmask', and 'clnet'
+ * are static, so that their values (gathered when checking the
+ * interface configuration) may be used again when checking the boot
+ * configuration.
+ */
+ if (why == CLF_IF) {
+ /*
+ * A valid host IP address is an absolute requirement.
+ */
+ len = sizeof (buf);
+ if (bootinfo_get(BI_HOST_IP, buf, &len, NULL) == BI_E_SUCCESS) {
+ if ((client_ipaddr = inet_addr(buf)) == (in_addr_t)-1) {
+ info("host-ip invalid!", interactive);
+ error = B_TRUE;
+ }
+ } else {
+ info("host-ip not set!", interactive);
+ error = B_TRUE;
+ }
+
+ /*
+ * If a subnet mask was provided, use it; otherwise infer it.
+ */
+ len = sizeof (buf);
+ if (bootinfo_get(BI_SUBNET_MASK, buf, &len, NULL) ==
+ BI_E_SUCCESS) {
+ if ((subnetmask = inet_addr(buf)) == (in_addr_t)-1) {
+ info("subnet-mask invalid!", interactive);
+ error = B_TRUE;
+ }
+ } else {
+ info("Defaulting to classful subnetting", interactive);
+
+ subnetmask = generate_classful_subnet(client_ipaddr);
+ }
+ clnet = client_ipaddr & subnetmask;
+
+ /*
+ * A legal bootserver URL is also an absolute requirement.
+ */
+ len = sizeof (buf);
+ if (bootinfo_get(BI_BOOTSERVER, buf, &len, NULL) ==
+ BI_E_SUCCESS) {
+ if (url_parse(buf, &u) != URL_PARSE_SUCCESS ||
+ u.https ||
+ (ipaddr = inet_addr(u.hport.hostname)) ==
+ (in_addr_t)-1) {
+ info("bootserver not legal URL!", interactive);
+ error = B_TRUE;
+ } else {
+ bsnet = ipaddr & subnetmask;
+ }
+ } else {
+ info("bootserver not specified!", interactive);
+ error = B_TRUE;
+ }
+
+ /*
+ * Is there a correctly-defined router?
+ */
+ len = sizeof (buf);
+ if (bootinfo_get(BI_ROUTER_IP, buf, &len, NULL) ==
+ BI_E_SUCCESS) {
+ if ((ipaddr = inet_addr(buf)) == (in_addr_t)-1) {
+ info("router-ip invalid!", interactive);
+ error = B_TRUE;
+ } else if (clnet != (ipaddr & subnetmask)) {
+ info("router not on local subnet!",
+ interactive);
+ error = B_TRUE;
+ } else {
+ have_router = B_TRUE;
+ }
+ }
+
+ /*
+ * Is there a correctly-defined proxy?
+ */
+ len = sizeof (buf);
+ if (bootinfo_get(BI_HTTP_PROXY, buf, &len, NULL) ==
+ BI_E_SUCCESS) {
+ url_hport_t u;
+
+ if (url_parse_hostport(buf, &u, URL_DFLT_PROXY_PORT) !=
+ URL_PARSE_SUCCESS ||
+ (ipaddr = inet_addr(u.hostname)) == (in_addr_t)-1) {
+ info("http-proxy port invalid!", interactive);
+ error = B_TRUE;
+ } else {
+ /*
+ * The proxy is only of use to us if it's on
+ * our local subnet, or if a router has been
+ * specified (which should hopefully allow us
+ * to access the proxy).
+ */
+ pxnet = ipaddr & subnetmask;
+ have_proxy = (have_router || pxnet == clnet);
+ }
+ }
+
+ /*
+ * If there is no router and no proxy (either on the local
+ * subnet or reachable via a router), then the bootserver
+ * URL must be on the local net.
+ */
+ if (!error && !have_router && !have_proxy && bsnet != clnet) {
+ info("bootserver URL not on local subnet",
+ interactive);
+ error = B_TRUE;
+ }
+ } else {
+ /*
+ * There must be a correctly-defined root_server URL.
+ */
+ if ((urlstr = bootconf_get(&bc_handle,
+ BC_ROOT_SERVER)) == NULL) {
+ info("no root_server URL!", interactive);
+ error = B_TRUE;
+ } else if (url_parse(urlstr, &u) != URL_PARSE_SUCCESS) {
+ info("root_server not legal URL!", interactive);
+ error = B_TRUE;
+ } else if ((hp = gethostbyname(u.hport.hostname)) == NULL) {
+ info("cannot resolve root_server hostname!",
+ interactive);
+ error = B_TRUE;
+ } else {
+ rsnet = *(in_addr_t *)hp->h_addr & subnetmask;
+ have_root_server = B_TRUE;
+ }
+
+ /*
+ * Is there a correctly-defined (non-empty) boot_logger URL?
+ */
+ if ((urlstr = bootconf_get(&bc_handle,
+ BC_BOOT_LOGGER)) != NULL) {
+ if (url_parse(urlstr, &u) != URL_PARSE_SUCCESS) {
+ info("boot_logger not legal URL!", interactive);
+ error = B_TRUE;
+ } else if ((hp = gethostbyname(u.hport.hostname)) ==
+ NULL) {
+ info("cannot resolve boot_logger hostname!",
+ interactive);
+ error = B_TRUE;
+ } else {
+ blnet = *(in_addr_t *)hp->h_addr & subnetmask;
+ have_boot_logger = B_TRUE;
+ }
+ }
+
+ /*
+ * If there is no router and no proxy (either on the local
+ * subnet or reachable via a router), then the root_server
+ * URL (and the boot_logger URL if specified) must be on the
+ * local net.
+ */
+ if (!error && !have_router && !have_proxy) {
+ if (have_root_server && rsnet != clnet) {
+ info("root_server URL not on local subnet",
+ interactive);
+ error = B_TRUE;
+ }
+ if (have_boot_logger && blnet != clnet) {
+ info("boot_logger URL not on local subnet",
+ interactive);
+ error = B_TRUE;
+ }
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Actually setup our network interface with the values derived from the
+ * PROM, DHCP or interactively from the user.
+ */
+static void
+setup_interface()
+{
+ char str[MAXHOSTNAMELEN]; /* will accomodate an IP too */
+ size_t len;
+ struct in_addr in_addr;
+
+ len = sizeof (str);
+ if (bootinfo_get(BI_HOST_IP, str, &len, NULL) == BI_E_SUCCESS &&
+ (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) {
+ in_addr.s_addr = htonl(in_addr.s_addr);
+ ipv4_setipaddr(&in_addr);
+ }
+
+ len = sizeof (str);
+ if (bootinfo_get(BI_SUBNET_MASK, str, &len, NULL) == BI_E_SUCCESS &&
+ (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) {
+ in_addr.s_addr = htonl(in_addr.s_addr);
+ ipv4_setnetmask(&in_addr);
+ }
+
+ len = sizeof (str);
+ if (bootinfo_get(BI_ROUTER_IP, str, &len, NULL) == BI_E_SUCCESS &&
+ (in_addr.s_addr = inet_addr(str)) != (in_addr_t)-1) {
+ in_addr.s_addr = htonl(in_addr.s_addr);
+ ipv4_setdefaultrouter(&in_addr);
+ (void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL, &in_addr);
+ }
+
+ len = sizeof (str);
+ if (bootinfo_get(BI_HOSTNAME, str, &len, NULL) == BI_E_SUCCESS) {
+ (void) sethostname(str, len);
+ }
+}
+
+/* EXPORT DELETE END */
+boolean_t
+wanboot_init_interface(char *boot_arguments)
+{
+/* EXPORT DELETE START */
+ boolean_t interactive;
+ int which;
+
+#if defined(__sparcv9)
+ /*
+ * Get the keys from PROM before we allow the user
+ * to override them from the CLI.
+ */
+ get_prom_encr_keys();
+ get_prom_hash_keys();
+#endif /* defined(__sparcv9) */
+
+ /*
+ * If there is already a bootp-response property under
+ * /chosen then the PROM must have done DHCP for us;
+ * invoke dhcp() to 'bind' the interface.
+ */
+ if (bootinfo_get(BI_BOOTP_RESPONSE, NULL, NULL, NULL) ==
+ BI_E_BUF2SMALL) {
+ (void) cldhcp(NULL, NULL, 0);
+ }
+
+ /*
+ * Obtain default interface values from bootinfo.
+ */
+ bootinfo_defaults(CLF_IF);
+
+ /*
+ * Process the boot arguments (following the "-o" option).
+ */
+ if (boot_arguments != NULL) {
+ (void) cli_eval_buf(boot_arguments,
+ (CLF_ARG | CLF_IF | CLF_BM));
+ }
+
+ /*
+ * Stash away any interface/bootmisc parameter values we got
+ * from either the PROM or the boot arguments.
+ */
+ update_bootinfo(CLF_IF | CLF_BM);
+
+ /*
+ * If we don't already have a value for bootserver, try to
+ * deduce one. Refresh wbcli's idea of these values.
+ */
+ determine_bootserver_url();
+ bootinfo_defaults(CLF_BM);
+
+ /*
+ * Check that the information we have collected thus far is sufficient.
+ */
+ interactive = args_specified_prompt;
+
+ if (interactive) {
+ /*
+ * Drop into the boot interpreter to allow the input
+ * of keys, bootserver and bootmisc, and in the case
+ * that net-config-strategy == "manual" the interface
+ * parameters.
+ */
+ which = CLF_BM | CLF_CMD;
+ if (strcmp(net_config_strategy(), "manual") == 0)
+ which |= CLF_IF;
+
+ do {
+ cli_interpret(which);
+ update_bootinfo(CLF_IF | CLF_BM);
+ } while (config_incomplete(CLF_IF, interactive));
+ } else {
+ /*
+ * The user is not to be given the opportunity to
+ * enter further values; fail.
+ */
+ if (config_incomplete(CLF_IF, interactive)) {
+ bootlog("wanboot", BOOTLOG_CRIT,
+ "interface incorrectly configured");
+ return (B_FALSE);
+ }
+ }
+
+ /*
+ * If a wanboot-enabled PROM hasn't processed client-id in
+ * network-boot-arguments, or no value for client-id has been
+ * specified to the boot interpreter, then provide a default
+ * client-id based on our MAC address.
+ */
+ generate_default_clientid();
+
+ /*
+ * If net-config-strategy == "manual" then we must setup
+ * the interface now; if "dhcp" then it will already have
+ * been setup.
+ */
+ if (strcmp(net_config_strategy(), "manual") == 0)
+ setup_interface();
+/* EXPORT DELETE END */
+ return (B_TRUE);
+}
+
+boolean_t
+wanboot_verify_config(void)
+{
+/* EXPORT DELETE START */
+ /*
+ * Check that the wanboot.conf file defines a valid root_server
+ * URL, and check that, if given, the boot_logger URL is valid.
+ */
+ if (config_incomplete(0, B_FALSE)) {
+ bootlog("wanboot", BOOTLOG_CRIT,
+ "incomplete boot configuration");
+ return (B_FALSE);
+ }
+/* EXPORT DELETE END */
+ return (B_TRUE);
+}