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