xref: /illumos-gate/usr/src/stand/lib/fs/nfs/rpc.c (revision b531f6d1)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * This file contains a simple implementation of RPC. Standard XDR is
27  * used.
28  */
29 
30 #include <sys/sysmacros.h>
31 #include <rpc/types.h>
32 #include <errno.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include "socket_inet.h"
36 #include "ipv4.h"
37 #include <rpc/xdr.h>
38 #include <rpc/auth.h>
39 #include <rpc/auth_sys.h>
40 #include <rpc/rpc_msg.h>
41 #include <sys/t_lock.h>
42 #include <netdb.h>
43 #include "clnt.h"
44 #include <rpc/rpc.h>
45 #include "brpc.h"
46 #include "auth_inet.h"
47 #include "pmap.h"
48 #include <sys/promif.h>
49 #include "nfs_inet.h"
50 #include <rpcsvc/nfs_prot.h>
51 #include <rpc/auth_unix.h>
52 #include <sys/salib.h>
53 #include "mac.h"
54 #include <sys/bootdebug.h>
55 
56 #define	dprintf	if (boothowto & RB_DEBUG) printf
57 
58 static struct in_addr cached_destination;
59 
60 void
rpc_disperr(struct rpc_err * stat)61 rpc_disperr(struct rpc_err *stat)
62 {
63 	if (boothowto & RB_DEBUG) {
64 		switch (stat->re_status) {
65 		case RPC_CANTENCODEARGS:
66 			printf("RPC: Can't encode arguments.\n");
67 			break;
68 		case RPC_CANTDECODERES:
69 			printf("RPC: Can't decode result.\n");
70 			break;
71 		case RPC_CANTSEND:
72 			printf("RPC: Unable to send (%s).\n",
73 			    strerror(errno));
74 			break;
75 		case RPC_CANTRECV:
76 			printf("RPC: Unable to receive (%s).\n",
77 			    strerror(errno));
78 			break;
79 		case RPC_TIMEDOUT:
80 			printf("RPC: Timed out.\n");
81 			break;
82 		case RPC_VERSMISMATCH:
83 			printf("RPC: Incompatible versions of RPC.\n");
84 			break;
85 		case RPC_AUTHERROR:
86 			printf("RPC: Authentication error:\n");
87 			switch (stat->re_why) {
88 			case AUTH_BADCRED:
89 				printf("remote: bogus credentials "
90 				    "(seal broken).\n");
91 				break;
92 			case AUTH_REJECTEDCRED:
93 				printf("remote: client should begin new "
94 				    "session.\n");
95 				break;
96 			case AUTH_BADVERF:
97 				printf("remote: bogus verifier "
98 				    "(seal broken).\n");
99 				break;
100 			case AUTH_REJECTEDVERF:
101 				printf("remote: verifier expired or was "
102 				    "replayed.\n");
103 				break;
104 			case AUTH_TOOWEAK:
105 				printf("remote: rejected due to security "
106 				    "reasons.\n");
107 				break;
108 			case AUTH_INVALIDRESP:
109 				printf("local: bogus response verifier.\n");
110 				break;
111 			case AUTH_FAILED:
112 				/* FALLTHRU */
113 			default:
114 				printf("local: unknown error.\n");
115 				break;
116 			}
117 			break;
118 		case RPC_PROGUNAVAIL:
119 			printf("RPC: Program unavailable.\n");
120 			break;
121 		case RPC_PROGVERSMISMATCH:
122 			printf("RPC: Program/version mismatch.\n");
123 			break;
124 		case RPC_PROCUNAVAIL:
125 			printf("RPC: Procedure unavailable.\n");
126 			break;
127 		case RPC_CANTDECODEARGS:
128 			printf("RPC: Server can't decode arguments.\n");
129 			break;
130 		case RPC_SYSTEMERROR:
131 			printf("RPC: Remote system error.\n");
132 			break;
133 		case RPC_UNKNOWNHOST:
134 			printf("RPC: Unknown host.\n");
135 			break;
136 		case RPC_UNKNOWNPROTO:
137 			printf("RPC: Unknown protocol.\n");
138 			break;
139 		case RPC_PMAPFAILURE:
140 			printf("RPC: Port mapper failure.\n");
141 			break;
142 		case RPC_PROGNOTREGISTERED:
143 			printf("RPC: Program not registered.\n");
144 			break;
145 		case RPC_FAILED:
146 			printf("RPC: Failed (unspecified error).\n");
147 			break;
148 		default:
149 			printf("RPC: (unknown error code).\n");
150 			break;
151 		}
152 	}
153 }
154 
155 /*
156  * rpc_hdr: sets the fields in the rpc msg header.
157  *
158  * Returns: TRUE on success, FALSE if failure.
159  */
160 /*ARGSUSED*/
161 static bool_t
rpc_hdr(XDR * xdrs,uint_t xid,rpcprog_t prog,rpcvers_t vers,rpcproc_t proc)162 rpc_hdr(XDR *xdrs, uint_t xid, rpcprog_t prog, rpcvers_t vers, rpcproc_t proc)
163 {
164 	struct rpc_msg call_msg;
165 
166 	/* setup header */
167 	call_msg.rm_xid = xid;
168 	call_msg.rm_direction = CALL;
169 	call_msg.rm_call.cb_rpcvers = (rpcvers_t)RPC_MSG_VERSION;
170 	call_msg.rm_call.cb_prog = prog;
171 	call_msg.rm_call.cb_vers = vers;
172 
173 	/* xdr the header. */
174 	if (xdr_callhdr(xdrs, &call_msg) == FALSE)
175 		return (FALSE);
176 	else
177 		return (TRUE);
178 }
179 
180 /*
181  * our version of brpc_call(). We cache in portnumber in to->sin_port for
182  * your convenience. to and from addresses are taken and received in network
183  * order.
184  */
185 enum clnt_stat
brpc_call(rpcprog_t prog,rpcvers_t vers,rpcproc_t proc,xdrproc_t in_xdr,caddr_t args,xdrproc_t out_xdr,caddr_t ret,int rexmit,int wait_time,struct sockaddr_in * to,struct sockaddr_in * from_who,uint_t auth)186 brpc_call(
187 	rpcprog_t	prog,		/* rpc program number to call. */
188 	rpcvers_t	vers,		/* rpc program version */
189 	rpcproc_t	proc,		/* rpc procedure to call */
190 	xdrproc_t	in_xdr,		/* routine to serialize arguments */
191 	caddr_t		args,		/* arg vector for remote call */
192 	xdrproc_t	out_xdr,	/* routine to deserialize results */
193 	caddr_t		ret,		/* addr of buf to place results in */
194 	int		rexmit,		/* retransmission interval (secs) */
195 	int		wait_time,	/* how long (secs) to wait (resp) */
196 	struct sockaddr_in 	*to,		/* destination */
197 	struct sockaddr_in	*from_who,	/* responder's port/address */
198 	uint_t			auth)		/* type of auth wanted. */
199 {
200 	int s;
201 	char hostname[MAXHOSTNAMELEN];
202 	struct sockaddr_in from;	/* us. */
203 	socklen_t from_len;
204 	XDR xmit_xdrs, rcv_xdrs;	/* xdr memory */
205 	AUTH *xmit_auth;		/* our chosen auth cookie */
206 	gid_t fake_gids = 1;		/* fake gids list for auth_unix */
207 	caddr_t trm_msg, rcv_msg;	/* outgoing/incoming rpc mesgs */
208 	struct rpc_msg reply;		/* our reply msg header */
209 	int trm_len, rcv_len;
210 	struct rpc_err rpc_error;	/* to store RPC errors in on rcv. */
211 	static uint_t xid;		/* current xid */
212 	uint_t xmit_len;		/* How much of the buffer we used */
213 	int nrefreshes = 2;		/* # of times to refresh cred */
214 	int flags = 0;			/* send flags */
215 	uint_t xdelay;
216 	int errors, preserve_errno;
217 	uint32_t timeout;
218 	socklen_t optlen;
219 
220 	xmit_auth = NULL;
221 
222 	trm_len = mac_get_mtu();
223 	trm_msg = bkmem_alloc(trm_len);
224 	rcv_msg = bkmem_alloc(NFSBUF_SIZE);
225 
226 	if (trm_msg == NULL || rcv_msg == NULL) {
227 		errno = ENOMEM;
228 		rpc_error.re_status = RPC_CANTSEND;
229 		goto gt_error;
230 	}
231 
232 	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
233 		rpc_error.re_status = RPC_CANTSEND;
234 		goto gt_error;
235 	}
236 
237 	if (dontroute) {
238 		(void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
239 		    (const void *)&dontroute, sizeof (dontroute));
240 	}
241 
242 	if (to->sin_addr.s_addr == cached_destination.s_addr) {
243 		optlen = sizeof (timeout);
244 		(void) getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout,
245 		    &optlen);
246 	} else {
247 		cached_destination.s_addr = htonl(INADDR_ANY);
248 	}
249 
250 	/* Bind our endpoint. */
251 	from.sin_family = AF_INET;
252 	ipv4_getipaddr(&from.sin_addr);
253 	from.sin_addr.s_addr = htonl(from.sin_addr.s_addr);
254 	from.sin_port = get_source_port(B_TRUE);
255 
256 	if (bind(s, (struct sockaddr *)&from, sizeof (from)) < 0) {
257 		rpc_error.re_status = RPC_CANTSEND;
258 		goto gt_error;
259 	}
260 
261 	bzero((caddr_t)&rpc_error, sizeof (struct rpc_err));
262 
263 	/* initialize reply's rpc_msg struct, so we can decode later. */
264 	reply.acpted_rply.ar_verf = _null_auth;	/* struct copy */
265 	reply.acpted_rply.ar_results.where = ret;
266 	reply.acpted_rply.ar_results.proc = out_xdr;
267 
268 	if (ntohs(to->sin_port) == 0) {
269 		/* snag the udp port we need. */
270 		if ((to->sin_port = (in_port_t)bpmap_getport(prog, vers,
271 		    &(rpc_error.re_status), to, NULL)) == 0)
272 			goto gt_error;
273 		to->sin_port = htons(to->sin_port);
274 	}
275 
276 	/* generate xid - increment */
277 	if (xid == 0)
278 		xid = (uint_t)(prom_gettime() / 1000) + 1;
279 	else
280 		xid++;
281 
282 	/* set up outgoing pkt as xdr modified. */
283 	xdrmem_create(&xmit_xdrs, trm_msg, trm_len, XDR_ENCODE);
284 
285 	/* setup rpc header */
286 	if (rpc_hdr(&xmit_xdrs, xid, prog, vers, proc) != TRUE) {
287 		dprintf("brpc_call: cannot setup rpc header.\n");
288 		rpc_error.re_status = RPC_FAILED;
289 		goto gt_error;
290 	}
291 
292 	/* setup authentication */
293 	switch (auth) {
294 	case AUTH_NONE:
295 		xmit_auth = authnone_create();
296 		break;
297 	case AUTH_UNIX:
298 		/*
299 		 * Assumes we've configured the stack and thus know our
300 		 * IP address/hostname, either by using DHCP or rarp/bootparams.
301 		 */
302 		(void) gethostname(hostname, sizeof (hostname));
303 		xmit_auth = authunix_create(hostname, 0, 1, 1, &fake_gids);
304 		break;
305 	default:
306 		dprintf("brpc_call: Unsupported authentication type: %d\n",
307 		    auth);
308 		rpc_error.re_status = RPC_AUTHERROR;
309 		goto gt_error;
310 	/*NOTREACHED*/
311 	}
312 
313 	/*
314 	 * rpc_hdr puts everything in the xmit buffer for the header
315 	 * EXCEPT the proc. Put it, and our authentication info into
316 	 * it now, serializing as we go. We will be at the place where
317 	 * we left off.
318 	 */
319 	xmit_xdrs.x_op = XDR_ENCODE;
320 	if ((XDR_PUTINT32(&xmit_xdrs, (int32_t *)&proc) == FALSE) ||
321 	    (AUTH_MARSHALL(xmit_auth, &xmit_xdrs, NULL) == FALSE) ||
322 	    ((*in_xdr)(&xmit_xdrs, args) == FALSE)) {
323 		rpc_error.re_status = RPC_CANTENCODEARGS;
324 		goto gt_error;
325 	} else
326 		xmit_len = (int)XDR_GETPOS(&xmit_xdrs); /* for sendto */
327 
328 	/*
329 	 * Right now the outgoing packet should be all serialized and
330 	 * ready to go... Set up timers.
331 	 */
332 
333 	xdelay = (rexmit == 0) ? RPC_REXMIT_MSEC : (rexmit * 1000);
334 	(void) setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (void *)&xdelay,
335 	    sizeof (xdelay));
336 	wait_time = (wait_time == 0) ? RPC_RCVWAIT_MSEC : (wait_time * 1000);
337 
338 	wait_time += prom_gettime();
339 
340 	/*
341 	 * send out the request. The first item in the receive buffer will
342 	 * be the xid. Check if it is correct.
343 	 */
344 	errors = 0;
345 	rpc_error.re_status = RPC_TIMEDOUT;
346 	do {
347 		if (sendto(s, trm_msg, xmit_len, flags, (struct sockaddr *)to,
348 		    sizeof (struct sockaddr_in)) < 0) {
349 			/*
350 			 * If errno is set to ETIMEDOUT, return
351 			 * with RPC status as RPC_TIMEDOUT. Calling
352 			 * funciton will take care of this error by
353 			 * retrying the RPC call.
354 			 */
355 			if (errno == ETIMEDOUT) {
356 				rpc_error.re_status = RPC_TIMEDOUT;
357 			} else {
358 				rpc_error.re_status = RPC_CANTSEND;
359 			}
360 			goto gt_error;
361 		}
362 
363 		from_len = sizeof (struct sockaddr_in);
364 		while ((rcv_len = recvfrom(s, rcv_msg, NFSBUF_SIZE,
365 		    MSG_DONTWAIT, (struct sockaddr *)from_who,
366 		    &from_len)) > 0 || errors < RPC_ALLOWABLE_ERRORS) {
367 			if (rcv_len < 0) {
368 				if (errno == EWOULDBLOCK ||
369 				    errno == ETIMEDOUT) {
370 					break; /* timeout */
371 				}
372 				rpc_error.re_status = RPC_CANTRECV;
373 				goto gt_error;
374 			}
375 			if (ntohl(*((uint32_t *)(rcv_msg))) != xid) {
376 				dprintf("brpc_call: xid: 0x%x != 0x%x\n",
377 				    *(uint32_t *)(rcv_msg), xid);
378 				continue;
379 			}
380 			/*
381 			 * Let's deserialize the data into our 'ret' buffer.
382 			 */
383 			xdrmem_create(&rcv_xdrs, rcv_msg, rcv_len, XDR_DECODE);
384 			if (xdr_replymsg(&rcv_xdrs, &reply) == FALSE) {
385 				rpc_error.re_status = RPC_CANTDECODERES;
386 				goto gt_error;
387 			}
388 			_seterr_reply(&reply, &rpc_error);
389 			switch (rpc_error.re_status) {
390 			case RPC_SUCCESS:
391 				/*
392 				 * XXX - validate for unix and none
393 				 * always return true.
394 				 */
395 				if (AUTH_VALIDATE(xmit_auth,
396 				    &reply.acpted_rply.ar_verf) == FALSE) {
397 					rpc_error.re_status = RPC_AUTHERROR;
398 					rpc_error.re_why = AUTH_INVALIDRESP;
399 					errors++;
400 				}
401 				if (reply.acpted_rply.ar_verf.oa_base !=
402 				    0) {
403 					xmit_xdrs.x_op = XDR_FREE;
404 					(void) xdr_opaque_auth(
405 					    &xmit_xdrs,
406 					    &reply.acpted_rply.ar_verf);
407 				}
408 				break;
409 
410 			case RPC_AUTHERROR:
411 				/*
412 				 * Let's see if our credentials need
413 				 * refreshing
414 				 */
415 				if (nrefreshes > 0 && AUTH_REFRESH(xmit_auth,
416 				    NULL, NULL)) {
417 					nrefreshes--;
418 				}
419 				errors++;
420 				break;
421 
422 			case RPC_PROCUNAVAIL:
423 				/*
424 				 * Might be a silly portmapper implementation
425 				 * erroneously responding to our rpc broadcast
426 				 * indirect portmapper call. For this
427 				 * particular case, we don't increment the
428 				 * error counter because we want to keep
429 				 * sifting for successful replies...
430 				 */
431 				if (to->sin_addr.s_addr !=
432 				    ntohl(INADDR_BROADCAST))
433 					errors++;
434 				break;
435 
436 			case RPC_PROGVERSMISMATCH:
437 				/*
438 				 * Successfully talked to server, but they
439 				 * don't speak our lingo.
440 				 */
441 				goto gt_error;
442 
443 			default:
444 				/* Just keep trying till there's no data... */
445 				errors++;
446 				break;
447 			}
448 
449 			if (rpc_error.re_status != RPC_SUCCESS) {
450 				dprintf("brpc_call: from: %s, error: ",
451 				    inet_ntoa(from_who->sin_addr));
452 				rpc_disperr(&rpc_error);
453 			} else
454 				break;
455 		}
456 
457 		/*
458 		 * If we're having trouble reassembling datagrams, let the
459 		 * application know ASAP so that it can take the appropriate
460 		 * actions.
461 		 */
462 
463 	} while (rpc_error.re_status != RPC_SUCCESS && errno != ETIMEDOUT &&
464 	    prom_gettime() < wait_time);
465 
466 gt_error:
467 	if (xmit_auth != NULL)
468 		AUTH_DESTROY(xmit_auth);
469 
470 	if (trm_msg != NULL)
471 		bkmem_free(trm_msg, trm_len);
472 	if (rcv_msg != NULL)
473 		bkmem_free(rcv_msg, NFSBUF_SIZE);
474 
475 	if (rpc_error.re_status != RPC_SUCCESS)
476 		rpc_disperr(&rpc_error);
477 
478 	/*
479 	 * socket calls reset errno. Since we want to hold onto the errno
480 	 * value if it is ETIMEDOUT to communicate to our caller that this
481 	 * RPC_TIMEDOUT situation is due to a stack problem (we're getting
482 	 * a reply, but the stack simply can't assemble it.), we need to
483 	 * preserve errno's value over the socket_close().
484 	 */
485 	preserve_errno = (errno == ETIMEDOUT) ? errno : 0;
486 	(void) socket_close(s);
487 	errno = preserve_errno;
488 
489 	return (rpc_error.re_status);
490 }
491