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