/* * 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. */ /* * This file contains routines responsible for getting the system's * name and boot params. Most of it comes from the SVR4 diskless boot * code (dlboot_inet), modified to work in a non socket environment. */ #include #include #include #include #include #include #include #include "clnt.h" #include #include #include #include #include #include #include #include #include #include "pmap.h" #include "brpc.h" #include "socket_inet.h" #include "ipv4.h" #include #include extern int errno; static struct bp_whoami_res bp; static char bp_hostname[SYS_NMLN+1]; static char bp_domainname[SYS_NMLN+1]; static struct in_addr responder; /* network order */ static const char *noserver = "No bootparam (%s) server responding; still trying...\n"; #define GETFILE_BTIMEO 1 #define GETFILE_BRETRIES 2 #define dprintf if (boothowto & RB_DEBUG) printf /* * Returns TRUE if it has set the global structure 'bp' to our boot * parameters, FALSE if some failure occurred. */ bool_t whoami(void) { struct bp_whoami_arg arg; struct sockaddr_in to, from; struct in_addr ipaddr; enum clnt_stat stat; bool_t retval = TRUE; int rexmit; /* retransmission interval */ int resp_wait; /* secs to wait for resp */ int namelen; int printed_waiting_msg; /* * Set our destination IP address to the limited broadcast address * (INADDR_BROADCAST). */ to.sin_family = AF_INET; to.sin_addr.s_addr = htonl(INADDR_BROADCAST); to.sin_port = htons(0); /* * Set up the arguments expected by bootparamd. */ arg.client_address.address_type = IP_ADDR_TYPE; ipv4_getipaddr(&ipaddr); ipaddr.s_addr = htonl(ipaddr.s_addr); bcopy((caddr_t)&ipaddr, (caddr_t)&arg.client_address.bp_address_u.ip_addr, sizeof (ipaddr)); /* * Retransmit/wait for up to resp_wait secs. */ rexmit = 0; /* start at default retransmission interval. */ resp_wait = 16; bp.client_name = &bp_hostname[0]; bp.domain_name = &bp_domainname[0]; /* * Do a broadcast call to find a bootparam daemon that * will tell us our hostname, domainname and any * router that we have to use to talk to our NFS server. */ printed_waiting_msg = 0; do { /* * First try the SunOS portmapper and if no reply is * received will then try the SVR4 rpcbind. * Either way, `bootpaddr' will be set to the * correct address for the bootparamd that responds. */ stat = bpmap_rmtcall((rpcprog_t)BOOTPARAMPROG, (rpcvers_t)BOOTPARAMVERS, (rpcproc_t)BOOTPARAMPROC_WHOAMI, xdr_bp_whoami_arg, (caddr_t)&arg, xdr_bp_whoami_res, (caddr_t)&bp, rexmit, resp_wait, &to, &from, AUTH_NONE); if (stat == RPC_TIMEDOUT && !printed_waiting_msg) { dprintf(noserver, "whoami"); printed_waiting_msg = 1; } /* * Retransmission interval for second and subsequent tries. * We expect first bpmap_rmtcall to retransmit and backoff to * at least this value. */ rexmit = resp_wait; resp_wait = 0; /* go to default wait now. */ } while (stat == RPC_TIMEDOUT); if (stat != RPC_SUCCESS) { dprintf("whoami RPC call failed with rpc status: %d\n", stat); retval = FALSE; goto done; } else { if (printed_waiting_msg && (boothowto & RB_VERBOSE)) printf("Bootparam response received\n"); /* Cache responder... We'll send our getfile here... */ responder.s_addr = from.sin_addr.s_addr; } namelen = strlen(bp.client_name); if (namelen > SYS_NMLN) { dprintf("whoami: hostname too long"); retval = FALSE; goto done; } if (namelen > 0) { if (boothowto & RB_VERBOSE) printf("hostname: %s\n", bp.client_name); (void) sethostname(bp.client_name, namelen); } else { dprintf("whoami: no host name\n"); retval = FALSE; goto done; } namelen = strlen(bp.domain_name); if (namelen > SYS_NMLN) { dprintf("whoami: domainname too long"); retval = FALSE; goto done; } if (namelen > 0) if (boothowto & RB_VERBOSE) printf("domainname: %s\n", bp.domain_name); else dprintf("whoami: no domain name\n"); if (bp.router_address.address_type == IP_ADDR_TYPE) { bcopy((caddr_t)&bp.router_address.bp_address_u.ip_addr, (caddr_t)&ipaddr, sizeof (ipaddr)); if (ntohl(ipaddr.s_addr) != INADDR_ANY) { dprintf("whoami: Router ip is: %s\n", inet_ntoa(ipaddr)); /* ipv4_route expects IP addresses in network order */ (void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL, &ipaddr); } } else dprintf("whoami: unknown gateway addr family %d\n", bp.router_address.address_type); done: return (retval); } /* * Returns: * 1) The ascii form of our root servers name in `server_name'. * 2) Pathname of our root on the server in `server_path'. * * NOTE: it's ok for getfile() to do dynamic allocation - it's only * used locally, then freed. If the server address returned from the * getfile call is different from our current destination address, * reset destination IP address to the new value. */ bool_t getfile(char *fileid, char *server_name, struct in_addr *server_ip, char *server_path) { struct bp_getfile_arg arg; struct bp_getfile_res res; enum clnt_stat stat; struct sockaddr_in to, from; int rexmit; int wait; uint_t max_retries = 0xFFFFFFFF; int def_rexmit = 0; int def_wait = 32; int printed_waiting_msg; /* * For non-root requests, set a smaller timeout */ if (strcmp(fileid, "root") != 0) { /* * Only send one request per call */ def_wait = GETFILE_BTIMEO; def_rexmit = GETFILE_BTIMEO; max_retries = GETFILE_BRETRIES; } arg.client_name = bp.client_name; arg.file_id = fileid; res.server_name = (bp_machine_name_t)bkmem_zalloc(SYS_NMLN + 1); res.server_path = (bp_path_t)bkmem_zalloc(SYS_NMLN + 1); if (res.server_name == NULL || res.server_path == NULL) { dprintf("getfile: rpc_call failed: No memory\n"); errno = ENOMEM; if (res.server_name != NULL) bkmem_free(res.server_name, SYS_NMLN + 1); if (res.server_path != NULL) bkmem_free(res.server_path, SYS_NMLN + 1); return (FALSE); } to.sin_family = AF_INET; to.sin_addr.s_addr = responder.s_addr; to.sin_port = htons(0); /* * Our addressing information was filled in by the call to * whoami(), so now send an rpc message to the * bootparam daemon requesting our server information. * * Wait only 32 secs for rpc_call to succeed. */ rexmit = def_rexmit; wait = def_wait; stat = brpc_call((rpcprog_t)BOOTPARAMPROG, (rpcvers_t)BOOTPARAMVERS, (rpcproc_t)BOOTPARAMPROC_GETFILE, xdr_bp_getfile_arg, (caddr_t)&arg, xdr_bp_getfile_res, (caddr_t)&res, rexmit, wait, &to, &from, AUTH_NONE); if (stat == RPC_TIMEDOUT) { /* * The server that answered the whoami doesn't * answer our getfile. Broadcast the call to all. Keep * trying forever. Set up for limited broadcast. */ to.sin_addr.s_addr = htonl(INADDR_BROADCAST); to.sin_port = htons(0); rexmit = def_rexmit; /* use default rexmit interval */ wait = def_wait; printed_waiting_msg = 0; do { /* * Limit the number of retries */ if (max_retries-- == 0) break; stat = bpmap_rmtcall((rpcprog_t)BOOTPARAMPROG, (rpcvers_t)BOOTPARAMVERS, (rpcproc_t)BOOTPARAMPROC_GETFILE, xdr_bp_getfile_arg, (caddr_t)&arg, xdr_bp_getfile_res, (caddr_t)&res, rexmit, wait, &to, &from, AUTH_NONE); if (stat == RPC_SUCCESS) { /* * set our destination addresses to * those of the server that responded. * It's probably our server, and we * can thus save arping for no reason later. */ responder.s_addr = from.sin_addr.s_addr; if (printed_waiting_msg && (boothowto & RB_VERBOSE)) { printf( "Bootparam response received.\n"); } break; } if (stat == RPC_TIMEDOUT && !printed_waiting_msg) { dprintf(noserver, "getfile"); printed_waiting_msg = 1; } /* * Retransmission interval for second and * subsequent tries. We expect first bpmap_rmtcall * to retransmit and backoff to at least this * value. */ rexmit = wait; wait = def_wait; } while (stat == RPC_TIMEDOUT); } if (stat == RPC_SUCCESS) { /* got the goods */ bcopy(res.server_name, server_name, strlen(res.server_name)); bcopy(res.server_path, server_path, strlen(res.server_path)); switch (res.server_address.address_type) { case IP_ADDR_TYPE: /* * server_address is where we will get our root * from. Replace destination entries in address if * necessary. */ bcopy((caddr_t)&res.server_address.bp_address_u.ip_addr, (caddr_t)server_ip, sizeof (struct in_addr)); break; default: dprintf("getfile: unknown address type %d\n", res.server_address.address_type); server_ip->s_addr = htonl(INADDR_ANY); bkmem_free(res.server_name, SYS_NMLN + 1); bkmem_free(res.server_path, SYS_NMLN + 1); return (FALSE); } } else { dprintf("getfile: rpc_call failed.\n"); bkmem_free(res.server_name, SYS_NMLN + 1); bkmem_free(res.server_path, SYS_NMLN + 1); return (FALSE); } bkmem_free(res.server_name, SYS_NMLN + 1); bkmem_free(res.server_path, SYS_NMLN + 1); return (TRUE); }