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
whoami(void)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
getfile(char * fileid,char * server_name,struct in_addr * server_ip,char * server_path)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