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