xref: /illumos-gate/usr/src/stand/lib/fs/nfs/bootparams.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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