/* * 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 2005 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)(uintptr_t)valstr; /* use uintptr_t for gcc */ 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)(uintptr_t)valstr; /* use uintrptr_t for gcc */ /* * 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) { /* use uintptr_t to suppress the gcc warning */ valstr = (char *)(uintptr_t)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); }