1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * This file contains routines responsible for getting the system's 29 * name and boot params. Most of it comes from the SVR4 diskless boot 30 * code (dlboot_inet), modified to work in a non socket environment. 31 */ 32 33 #include <sys/types.h> 34 #include <rpc/types.h> 35 #include <sys/errno.h> 36 #include <rpc/auth.h> 37 #include <rpc/xdr.h> 38 #include <rpc/rpc_msg.h> 39 #include <sys/t_lock.h> 40 #include "clnt.h" 41 #include <rpc/rpc.h> 42 #include <sys/utsname.h> 43 #include <netinet/in.h> 44 #include <sys/socket.h> 45 #include <net/if.h> 46 #include <netinet/if_ether.h> 47 #include <netinet/in.h> 48 #include <sys/promif.h> 49 #include <rpcsvc/bootparam.h> 50 #include "pmap.h" 51 #include "brpc.h" 52 #include "socket_inet.h" 53 #include "ipv4.h" 54 #include <sys/salib.h> 55 #include <sys/bootdebug.h> 56 57 extern int errno; 58 static struct bp_whoami_res bp; 59 static char bp_hostname[SYS_NMLN+1]; 60 static char bp_domainname[SYS_NMLN+1]; 61 static struct in_addr responder; /* network order */ 62 63 static const char *noserver = 64 "No bootparam (%s) server responding; still trying...\n"; 65 66 #define GETFILE_BTIMEO 1 67 #define GETFILE_BRETRIES 2 68 69 #define dprintf if (boothowto & RB_DEBUG) printf 70 71 /* 72 * Returns TRUE if it has set the global structure 'bp' to our boot 73 * parameters, FALSE if some failure occurred. 74 */ 75 bool_t 76 whoami(void) 77 { 78 struct bp_whoami_arg arg; 79 struct sockaddr_in to, from; 80 struct in_addr ipaddr; 81 enum clnt_stat stat; 82 bool_t retval = TRUE; 83 int rexmit; /* retransmission interval */ 84 int resp_wait; /* secs to wait for resp */ 85 int namelen; 86 int printed_waiting_msg; 87 88 /* 89 * Set our destination IP address to the limited broadcast address 90 * (INADDR_BROADCAST). 91 */ 92 to.sin_family = AF_INET; 93 to.sin_addr.s_addr = htonl(INADDR_BROADCAST); 94 to.sin_port = htons(0); 95 96 /* 97 * Set up the arguments expected by bootparamd. 98 */ 99 arg.client_address.address_type = IP_ADDR_TYPE; 100 ipv4_getipaddr(&ipaddr); 101 ipaddr.s_addr = htonl(ipaddr.s_addr); 102 bcopy((caddr_t)&ipaddr, 103 (caddr_t)&arg.client_address.bp_address_u.ip_addr, 104 sizeof (ipaddr)); 105 106 /* 107 * Retransmit/wait for up to resp_wait secs. 108 */ 109 rexmit = 0; /* start at default retransmission interval. */ 110 resp_wait = 16; 111 112 bp.client_name = &bp_hostname[0]; 113 bp.domain_name = &bp_domainname[0]; 114 115 /* 116 * Do a broadcast call to find a bootparam daemon that 117 * will tell us our hostname, domainname and any 118 * router that we have to use to talk to our NFS server. 119 */ 120 printed_waiting_msg = 0; 121 do { 122 /* 123 * First try the SunOS portmapper and if no reply is 124 * received will then try the SVR4 rpcbind. 125 * Either way, `bootpaddr' will be set to the 126 * correct address for the bootparamd that responds. 127 */ 128 stat = bpmap_rmtcall((rpcprog_t)BOOTPARAMPROG, 129 (rpcvers_t)BOOTPARAMVERS, (rpcproc_t)BOOTPARAMPROC_WHOAMI, 130 xdr_bp_whoami_arg, (caddr_t)&arg, 131 xdr_bp_whoami_res, (caddr_t)&bp, rexmit, resp_wait, 132 &to, &from, AUTH_NONE); 133 if (stat == RPC_TIMEDOUT && !printed_waiting_msg) { 134 dprintf(noserver, "whoami"); 135 printed_waiting_msg = 1; 136 } 137 /* 138 * Retransmission interval for second and subsequent tries. 139 * We expect first bpmap_rmtcall to retransmit and backoff to 140 * at least this value. 141 */ 142 rexmit = resp_wait; 143 resp_wait = 0; /* go to default wait now. */ 144 } while (stat == RPC_TIMEDOUT); 145 146 if (stat != RPC_SUCCESS) { 147 dprintf("whoami RPC call failed with rpc status: %d\n", stat); 148 retval = FALSE; 149 goto done; 150 } else { 151 if (printed_waiting_msg && (boothowto & RB_VERBOSE)) 152 printf("Bootparam response received\n"); 153 154 /* Cache responder... We'll send our getfile here... */ 155 responder.s_addr = from.sin_addr.s_addr; 156 } 157 158 namelen = strlen(bp.client_name); 159 if (namelen > SYS_NMLN) { 160 dprintf("whoami: hostname too long"); 161 retval = FALSE; 162 goto done; 163 } 164 if (namelen > 0) { 165 if (boothowto & RB_VERBOSE) 166 printf("hostname: %s\n", bp.client_name); 167 (void) sethostname(bp.client_name, namelen); 168 } else { 169 dprintf("whoami: no host name\n"); 170 retval = FALSE; 171 goto done; 172 } 173 174 namelen = strlen(bp.domain_name); 175 if (namelen > SYS_NMLN) { 176 dprintf("whoami: domainname too long"); 177 retval = FALSE; 178 goto done; 179 } 180 if (namelen > 0) 181 if (boothowto & RB_VERBOSE) 182 printf("domainname: %s\n", bp.domain_name); 183 else 184 dprintf("whoami: no domain name\n"); 185 186 if (bp.router_address.address_type == IP_ADDR_TYPE) { 187 bcopy((caddr_t)&bp.router_address.bp_address_u.ip_addr, 188 (caddr_t)&ipaddr, sizeof (ipaddr)); 189 if (ntohl(ipaddr.s_addr) != INADDR_ANY) { 190 dprintf("whoami: Router ip is: %s\n", 191 inet_ntoa(ipaddr)); 192 /* ipv4_route expects IP addresses in network order */ 193 (void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL, 194 &ipaddr); 195 } 196 } else 197 dprintf("whoami: unknown gateway addr family %d\n", 198 bp.router_address.address_type); 199 done: 200 return (retval); 201 } 202 203 /* 204 * Returns: 205 * 1) The ascii form of our root servers name in `server_name'. 206 * 2) Pathname of our root on the server in `server_path'. 207 * 208 * NOTE: it's ok for getfile() to do dynamic allocation - it's only 209 * used locally, then freed. If the server address returned from the 210 * getfile call is different from our current destination address, 211 * reset destination IP address to the new value. 212 */ 213 bool_t 214 getfile(char *fileid, char *server_name, struct in_addr *server_ip, 215 char *server_path) 216 { 217 struct bp_getfile_arg arg; 218 struct bp_getfile_res res; 219 enum clnt_stat stat; 220 struct sockaddr_in to, from; 221 int rexmit; 222 int wait; 223 uint_t max_retries = 0xFFFFFFFF; 224 int def_rexmit = 0; 225 int def_wait = 32; 226 int printed_waiting_msg; 227 228 /* 229 * For non-root requests, set a smaller timeout 230 */ 231 if (strcmp(fileid, "root") != 0) { 232 /* 233 * Only send one request per call 234 */ 235 def_wait = GETFILE_BTIMEO; 236 def_rexmit = GETFILE_BTIMEO; 237 max_retries = GETFILE_BRETRIES; 238 } 239 240 arg.client_name = bp.client_name; 241 arg.file_id = fileid; 242 243 res.server_name = (bp_machine_name_t)bkmem_zalloc(SYS_NMLN + 1); 244 res.server_path = (bp_path_t)bkmem_zalloc(SYS_NMLN + 1); 245 246 if (res.server_name == NULL || res.server_path == NULL) { 247 dprintf("getfile: rpc_call failed: No memory\n"); 248 errno = ENOMEM; 249 if (res.server_name != NULL) 250 bkmem_free(res.server_name, SYS_NMLN + 1); 251 if (res.server_path != NULL) 252 bkmem_free(res.server_path, SYS_NMLN + 1); 253 return (FALSE); 254 } 255 256 to.sin_family = AF_INET; 257 to.sin_addr.s_addr = responder.s_addr; 258 to.sin_port = htons(0); 259 260 /* 261 * Our addressing information was filled in by the call to 262 * whoami(), so now send an rpc message to the 263 * bootparam daemon requesting our server information. 264 * 265 * Wait only 32 secs for rpc_call to succeed. 266 */ 267 rexmit = def_rexmit; 268 wait = def_wait; 269 270 stat = brpc_call((rpcprog_t)BOOTPARAMPROG, (rpcvers_t)BOOTPARAMVERS, 271 (rpcproc_t)BOOTPARAMPROC_GETFILE, xdr_bp_getfile_arg, (caddr_t)&arg, 272 xdr_bp_getfile_res, (caddr_t)&res, rexmit, wait, 273 &to, &from, AUTH_NONE); 274 275 if (stat == RPC_TIMEDOUT) { 276 /* 277 * The server that answered the whoami doesn't 278 * answer our getfile. Broadcast the call to all. Keep 279 * trying forever. Set up for limited broadcast. 280 */ 281 to.sin_addr.s_addr = htonl(INADDR_BROADCAST); 282 to.sin_port = htons(0); 283 284 rexmit = def_rexmit; /* use default rexmit interval */ 285 wait = def_wait; 286 printed_waiting_msg = 0; 287 do { 288 /* 289 * Limit the number of retries 290 */ 291 if (max_retries-- == 0) 292 break; 293 294 stat = bpmap_rmtcall((rpcprog_t)BOOTPARAMPROG, 295 (rpcvers_t)BOOTPARAMVERS, 296 (rpcproc_t)BOOTPARAMPROC_GETFILE, 297 xdr_bp_getfile_arg, (caddr_t)&arg, 298 xdr_bp_getfile_res, (caddr_t)&res, rexmit, 299 wait, &to, &from, AUTH_NONE); 300 301 if (stat == RPC_SUCCESS) { 302 /* 303 * set our destination addresses to 304 * those of the server that responded. 305 * It's probably our server, and we 306 * can thus save arping for no reason later. 307 */ 308 responder.s_addr = from.sin_addr.s_addr; 309 if (printed_waiting_msg && 310 (boothowto & RB_VERBOSE)) { 311 printf( 312 "Bootparam response received.\n"); 313 } 314 break; 315 } 316 if (stat == RPC_TIMEDOUT && !printed_waiting_msg) { 317 dprintf(noserver, "getfile"); 318 printed_waiting_msg = 1; 319 } 320 /* 321 * Retransmission interval for second and 322 * subsequent tries. We expect first bpmap_rmtcall 323 * to retransmit and backoff to at least this 324 * value. 325 */ 326 rexmit = wait; 327 wait = def_wait; 328 } while (stat == RPC_TIMEDOUT); 329 } 330 331 if (stat == RPC_SUCCESS) { 332 /* got the goods */ 333 bcopy(res.server_name, server_name, strlen(res.server_name)); 334 bcopy(res.server_path, server_path, strlen(res.server_path)); 335 switch (res.server_address.address_type) { 336 case IP_ADDR_TYPE: 337 /* 338 * server_address is where we will get our root 339 * from. Replace destination entries in address if 340 * necessary. 341 */ 342 bcopy((caddr_t)&res.server_address.bp_address_u.ip_addr, 343 (caddr_t)server_ip, sizeof (struct in_addr)); 344 break; 345 default: 346 dprintf("getfile: unknown address type %d\n", 347 res.server_address.address_type); 348 server_ip->s_addr = htonl(INADDR_ANY); 349 bkmem_free(res.server_name, SYS_NMLN + 1); 350 bkmem_free(res.server_path, SYS_NMLN + 1); 351 return (FALSE); 352 } 353 } else { 354 dprintf("getfile: rpc_call failed.\n"); 355 bkmem_free(res.server_name, SYS_NMLN + 1); 356 bkmem_free(res.server_path, SYS_NMLN + 1); 357 return (FALSE); 358 } 359 360 bkmem_free(res.server_name, SYS_NMLN + 1); 361 bkmem_free(res.server_path, SYS_NMLN + 1); 362 363 return (TRUE); 364 } 365