xref: /illumos-gate/usr/src/stand/lib/fs/nfs/clnt_btcp.c (revision b531f6d16eb39863e7bbc34773fb7ef7a282a0a2)
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 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 /*
31  * Portions of this source code were derived from Berkeley 4.3 BSD
32  * under license from the Regents of the University of California.
33  */
34 
35 /*
36  * Boot subsystem client side rpc (TCP)
37  */
38 
39 #include <sys/salib.h>
40 #include <sys/errno.h>
41 #include <rpc/types.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include "socket_inet.h"
45 #include "ipv4.h"
46 #include "clnt.h"
47 #include <rpc/rpc.h>
48 #include "brpc.h"
49 #include "pmap.h"
50 #include <sys/promif.h>
51 #include <rpc/xdr.h>
52 #include <rpc/auth.h>
53 #include <rpc/auth_sys.h>
54 #include "auth_inet.h"
55 #include <rpc/rpc_msg.h>
56 #include <sys/bootdebug.h>
57 
58 #define	dprintf if (boothowto & RB_DEBUG) printf
59 
60 #define	MCALL_MSG_SIZE 24
61 
62 extern int errno;
63 
64 extern void xdrrec_create();
65 extern bool_t xdrrec_endofrecord();
66 extern bool_t xdrrec_skiprecord();
67 
68 /*
69  * If we create another clnt type this should be
70  * moved to a common file
71  */
72 struct rpc_createerr rpc_createerr;
73 
74 static int readtcp();
75 static int writetcp();
76 
77 static struct clnt_ops *clntbtcp_ops();
78 
79 /*
80  * Private data kept per client handle
81  */
82 struct ct_data {
83 	int			ct_sock;
84 	bool_t			ct_closeit;
85 	struct sockaddr_in	ct_raddr;
86 	uint_t			ct_wait_msec;
87 	struct timeval		ct_total;
88 	struct rpc_err		ct_error;
89 	XDR			ct_xdrs;
90 	char			ct_mcall[MCALL_MSG_SIZE];
91 	uint_t			ct_mpos;
92 	uint_t			ct_xdrpos;
93 };
94 
95 /*
96  * Create a TCP based client handle.
97  * If *sockp<0, *sockp is set to a newly created TCP socket.
98  * If raddr->sin_port is 0 a binder on the remote machine
99  * is consulted for the correct port number.
100  * NB: It is the clients responsibility to close *sockp.
101  * NB: The rpch->cl_auth is initialized to null authentication.
102  *	Caller may wish to set this something more useful.
103  *
104  * wait is the amount of time used between retransmitting a call if
105  * no response has been heard;  retransmition occurs until the actual
106  * rpc call times out.
107  *
108  * sendsz and recvsz are the maximum allowable packet sizes that can be
109  * sent and received.
110  */
111 CLIENT *
clntbtcp_create(struct sockaddr_in * raddr,rpcprog_t program,rpcvers_t version,struct timeval wait,int * sockp,uint_t sendsz,uint_t recvsz)112 clntbtcp_create(
113 	struct sockaddr_in *raddr,
114 	rpcprog_t program,
115 	rpcvers_t version,
116 	struct timeval wait,
117 	int *sockp,
118 	uint_t sendsz,
119 	uint_t recvsz)
120 {
121 	CLIENT *cl;
122 	struct ct_data *ct;
123 	struct rpc_msg call_msg;
124 #if 0	/* XXX not yet */
125 	int min_buf_sz;
126 	int pref_buf_sz = 64 * 1024; /* 64 KB */
127 	socklen_t optlen;
128 #endif /* not yet */
129 	cl = (CLIENT *)bkmem_alloc(sizeof (CLIENT));
130 	if (cl == NULL) {
131 		errno = ENOMEM;
132 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
133 		rpc_createerr.cf_error.re_errno = errno;
134 		return ((CLIENT *)NULL);
135 	}
136 
137 	ct = (struct ct_data *)bkmem_alloc(sizeof (*ct));
138 	if (ct == NULL) {
139 		errno = ENOMEM;
140 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
141 		rpc_createerr.cf_error.re_errno = errno;
142 		goto fooy;
143 	}
144 
145 	if (raddr->sin_port == 0) {
146 		ushort_t port;
147 		if ((port = bpmap_getport(program, version,
148 				&(rpc_createerr.cf_stat), raddr, NULL)) == 0) {
149 			goto fooy;
150 		}
151 		raddr->sin_port = htons(port);
152 	}
153 
154 	if (*sockp < 0) {
155 		struct sockaddr_in from;
156 
157 		*sockp = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
158 		if (*sockp < 0) {
159 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
160 			rpc_createerr.cf_error.re_errno = errno;
161 			goto fooy;
162 		}
163 		/*
164 		 * Bootparams assumes a local net, so be sure to let lower
165 		 * layer protocols know not to route.
166 		 */
167 		if (dontroute) {
168 			(void) setsockopt(*sockp, SOL_SOCKET, SO_DONTROUTE,
169 				(const void *)&dontroute, sizeof (dontroute));
170 		}
171 
172 		/* attempt to bind to priv port */
173 		from.sin_family = AF_INET;
174 		ipv4_getipaddr(&from.sin_addr);
175 		from.sin_addr.s_addr = htonl(from.sin_addr.s_addr);
176 		from.sin_port = get_source_port(TRUE);
177 
178 		if (bind(*sockp, (struct sockaddr *)&from, sizeof (from)) < 0) {
179 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
180 			rpc_createerr.cf_error.re_errno = errno;
181 			if (*sockp > 0)
182 				(void) close(*sockp);
183 			goto fooy;
184 		}
185 
186 		if (connect(*sockp, (struct sockaddr *)raddr,
187 			    sizeof (struct sockaddr_in)) < 0) {
188 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
189 			rpc_createerr.cf_error.re_errno = errno;
190 			if (*sockp > 0)
191 				(void) close(*sockp);
192 			goto fooy;
193 		}
194 
195 #if 0 /* XXX not yet */
196 		/*
197 		 * In the future we may want RPC to use larger transfer sizes
198 		 * over TCP.  In this case we will want to increase the
199 		 * window size.
200 		 */
201 		/*
202 		 * Resize the receive window if possible
203 		 */
204 		optlen = sizeof (int);
205 		if (getsockopt(*sockp, SOL_SOCKET, SO_RCVBUF,
206 				(void *)&min_buf_sz, &optlen) != 0)
207 			goto keep_going;
208 
209 		if (min_buf_sz < pref_buf_sz)
210 			(void) setsockopt(*sockp, SOL_SOCKET, SO_RCVBUF,
211 				(const void *)&pref_buf_sz, sizeof (int));
212 
213 keep_going:
214 #endif		/* not yet */
215 		ct->ct_closeit = TRUE;
216 	} else
217 		ct->ct_closeit = FALSE;
218 
219 	/*
220 	 * Set up the private data
221 	 */
222 	ct->ct_sock = *sockp;
223 	ct->ct_wait_msec = 0;
224 	ct->ct_total.tv_sec = wait.tv_sec;
225 	ct->ct_total.tv_usec = -1;
226 	ct->ct_raddr = *raddr;
227 
228 	/*
229 	 * Initialize the call message
230 	 */
231 
232 	/*
233 	 * XXX - The xid might need to be randomized more.  Imagine if there
234 	 * are a rack of blade servers all booting at the same time.  They
235 	 * may cause havoc on the server with xid replays.
236 	 */
237 	call_msg.rm_xid = (uint_t)prom_gettime() + 1;
238 	call_msg.rm_direction = CALL;
239 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
240 	call_msg.rm_call.cb_prog = program;
241 	call_msg.rm_call.cb_vers = version;
242 
243 	/*
244 	 * pre-serialize the static part of the call msg and stash it away
245 	 */
246 	xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
247 			XDR_ENCODE);
248 	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
249 		if (ct->ct_closeit)
250 			(void) close(*sockp);
251 		goto fooy;
252 	}
253 	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
254 	XDR_DESTROY(&(ct->ct_xdrs));
255 
256 	/*
257 	 * XXX - Memory allocations can fail in xdrrec_create, so we need to
258 	 * be able to catch those errors.
259 	 */
260 	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz, (caddr_t)ct, readtcp,
261 			writetcp);
262 
263 	cl->cl_ops = clntbtcp_ops();
264 	cl->cl_private = (caddr_t)ct;
265 	cl->cl_auth = authnone_create();
266 	return (cl);
267 
268 fooy:
269 	if (ct)
270 		bkmem_free((caddr_t)ct, sizeof (*ct));
271 	if (cl)
272 		bkmem_free((caddr_t)cl, sizeof (CLIENT));
273 	return ((CLIENT *)NULL);
274 }
275 
276 static enum clnt_stat
clntbtcp_call(CLIENT * cl,rpcproc_t proc,xdrproc_t xargs,caddr_t argsp,xdrproc_t xdr_results,caddr_t resultsp,struct timeval utimeout)277 clntbtcp_call(
278 	CLIENT *cl,
279 	rpcproc_t proc,
280 	xdrproc_t xargs,
281 	caddr_t argsp,
282 	xdrproc_t xdr_results,
283 	caddr_t resultsp,
284 	struct timeval utimeout)
285 {
286 	struct ct_data *ct;
287 	XDR *xdrs;
288 	struct rpc_msg reply_msg;
289 	uint32_t x_id;
290 	uint32_t *msg_x_id;
291 	bool_t shipnow;
292 	int nrefreshes = 2;	/* number of times to refresh cred */
293 	struct timeval timeout;
294 
295 	ct = (struct ct_data *)cl->cl_private;
296 	msg_x_id = (uint32_t *)ct->ct_mcall;
297 
298 	xdrs = &(ct->ct_xdrs);
299 
300 	ct->ct_total = utimeout;
301 
302 	/*
303 	 * We have to be able to wait for some non-zero period of time, so
304 	 * use a default timeout.
305 	 */
306 	if (ct->ct_total.tv_sec == 0)
307 		ct->ct_total.tv_sec = RPC_RCVWAIT_MSEC / 1000;
308 
309 	ct->ct_wait_msec = ct->ct_total.tv_sec * 1000 +
310 		ct->ct_total.tv_usec / 1000;
311 
312 	timeout = ct->ct_total;
313 
314 	shipnow = (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0 &&
315 			timeout.tv_usec == 0) ? FALSE : TRUE;
316 
317 call_again:
318 	xdrs->x_op = XDR_ENCODE;
319 	ct->ct_error.re_status = RPC_SUCCESS;
320 	x_id = ntohl(++(*msg_x_id));
321 	if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
322 	    (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
323 	    (! AUTH_MARSHALL(cl->cl_auth, xdrs, NULL)) ||
324 	    (! (*xargs)(xdrs, argsp))) {
325 		(void) xdrrec_endofrecord(xdrs, TRUE);
326 		ct->ct_error.re_status = RPC_CANTENCODEARGS;
327 		printf("clntbtcp_call: xdr encode args failed\n");
328 		return (ct->ct_error.re_status);
329 	}
330 
331 	if (!xdrrec_endofrecord(xdrs, shipnow)) {
332 		printf("clntbtcp_call: rpc cansend error\n");
333 		ct->ct_error.re_status = RPC_CANTSEND;
334 		return (ct->ct_error.re_status);
335 	}
336 
337 	if (!shipnow)
338 		return (RPC_SUCCESS);
339 
340 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
341 		ct->ct_error.re_status = RPC_TIMEDOUT;
342 		return (ct->ct_error.re_status);
343 	}
344 
345 	xdrs->x_op = XDR_DECODE;
346 
347 	/* CONSTCOND */
348 	while (TRUE) {
349 		reply_msg.acpted_rply.ar_verf = _null_auth;
350 		reply_msg.acpted_rply.ar_results.where = NULL;
351 		reply_msg.acpted_rply.ar_results.proc = xdr_void;
352 		if (!xdrrec_skiprecord(xdrs)) {
353 			return (ct->ct_error.re_status);
354 		}
355 
356 		if (!xdr_replymsg(xdrs, &reply_msg)) {
357 			if (ct->ct_error.re_status == RPC_SUCCESS)
358 				continue;
359 			return (ct->ct_error.re_status);
360 		}
361 		if (reply_msg.rm_xid == x_id) {
362 			break;
363 		}
364 	}
365 
366 	/*
367 	 * process header
368 	 */
369 	_seterr_reply(&reply_msg, &(ct->ct_error));
370 	if (ct->ct_error.re_status == RPC_SUCCESS) {
371 		if (!AUTH_VALIDATE(cl->cl_auth,
372 				&reply_msg.acpted_rply.ar_verf)) {
373 			ct->ct_error.re_status = RPC_AUTHERROR;
374 			ct->ct_error.re_why = AUTH_INVALIDRESP;
375 		} else if (!(*xdr_results)(xdrs, resultsp)) {
376 			if (ct->ct_error.re_status == RPC_SUCCESS) {
377 				ct->ct_error.re_status = RPC_CANTDECODERES;
378 			}
379 		}
380 		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
381 			xdrs->x_op = XDR_FREE;
382 			(void) xdr_opaque_auth(xdrs,
383 				&(reply_msg.acpted_rply.ar_verf));
384 		}
385 	} else {
386 		if (nrefreshes-- && AUTH_REFRESH(cl->cl_auth, &reply_msg,
387 						NULL)) {
388 			goto call_again;
389 		}
390 	}
391 	return (ct->ct_error.re_status);
392 }
393 
394 /*
395  * Interface between xdr serializer and tcp connection.
396  * Behaves like the system calls, read & write, but keeps some error state
397  * around for the rpc level.
398  */
399 static int
readtcp(struct ct_data * ct,caddr_t buf,int len)400 readtcp(struct ct_data *ct,
401 	caddr_t buf,
402 	int len)
403 {
404 	int inlen = 0;
405 	uint_t start, diff;
406 	struct sockaddr from;
407 	uint_t fromlen = sizeof (from);
408 
409 	if (len <= 0)
410 		return (0);
411 
412 	/*
413 	 * Do non-blocking reads here until we get some data or timeout
414 	 */
415 	start = prom_gettime();
416 	while ((inlen = recvfrom(ct->ct_sock, buf, len, 0, &from,
417 					&fromlen)) == 0) {
418 		diff = (uint_t)(prom_gettime() - start);
419 		if (diff > ct->ct_wait_msec) {
420 			errno = ETIMEDOUT;
421 			inlen = -1;
422 			break;
423 		}
424 	}
425 #ifdef DEBUG
426 	printf("readtcp: inlen = %d\n", inlen);
427 #endif
428 	switch (inlen) {
429 	case 0:
430 		/* premature eof */
431 		ct->ct_error.re_errno = ECONNRESET;
432 		ct->ct_error.re_status = RPC_CANTRECV;
433 		inlen = -1;  /* it's really an error */
434 		break;
435 	case -1:
436 		ct->ct_error.re_errno = errno;
437 		ct->ct_error.re_status = RPC_CANTRECV;
438 		break;
439 	}
440 
441 	return (inlen);
442 }
443 
444 static int
writetcp(ct,buf,len)445 writetcp(ct, buf, len)
446 	struct ct_data *ct;
447 	caddr_t buf;
448 	int len;
449 {
450 	register int i, cnt;
451 
452 	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
453 		if ((i = sendto(ct->ct_sock, (void *)buf, cnt, 0,
454 				(struct sockaddr *)&(ct->ct_raddr),
455 				sizeof (ct->ct_raddr))) == -1) {
456 			ct->ct_error.re_errno = errno;
457 			ct->ct_error.re_status = RPC_CANTSEND;
458 			return (-1);
459 		}
460 	}
461 	return (len);
462 }
463 
464 static void
clntbtcp_geterr(CLIENT * cl,struct rpc_err * errp)465 clntbtcp_geterr(
466 	CLIENT *cl,
467 	struct rpc_err *errp)
468 {
469 	struct ct_data *ct = (struct ct_data *)cl->cl_private;
470 
471 	*errp = ct->ct_error;
472 }
473 
474 
475 static bool_t
clntbtcp_freeres(CLIENT * cl,xdrproc_t xdr_res,caddr_t res_ptr)476 clntbtcp_freeres(
477 	CLIENT *cl,
478 	xdrproc_t xdr_res,
479 	caddr_t res_ptr)
480 {
481 	struct ct_data *ct = (struct ct_data *)cl->cl_private;
482 	XDR *xdrs = &(ct->ct_xdrs);
483 
484 	xdrs->x_op = XDR_FREE;
485 	return ((*xdr_res)(xdrs, res_ptr));
486 }
487 
488 static void
clntbtcp_abort()489 clntbtcp_abort()
490 	/* CLIENT *h; */
491 {
492 }
493 
494 /* ARGSUSED */
495 static bool_t
clntbtcp_control(CLIENT * cl,int request,char * info)496 clntbtcp_control(
497 	CLIENT *cl,
498 	int request,
499 	char *info)
500 {
501 	/* Not implemented in boot */
502 	return (FALSE);
503 }
504 
505 static void
clntbtcp_destroy(CLIENT * cl)506 clntbtcp_destroy(CLIENT *cl)
507 {
508 	struct ct_data *ct = (struct ct_data *)cl->cl_private;
509 
510 	if (ct->ct_closeit) {
511 		(void) socket_close(ct->ct_sock);
512 	}
513 	XDR_DESTROY(&(ct->ct_xdrs));
514 	bkmem_free((caddr_t)ct, (sizeof (struct ct_data)));
515 	bkmem_free((caddr_t)cl, sizeof (CLIENT));
516 }
517 
518 static struct clnt_ops *
clntbtcp_ops()519 clntbtcp_ops()
520 {
521 	static struct clnt_ops ops;
522 
523 	if (ops.cl_call == NULL) {
524 		ops.cl_call = clntbtcp_call;
525 		ops.cl_abort = clntbtcp_abort;
526 		ops.cl_geterr = clntbtcp_geterr;
527 		ops.cl_freeres = clntbtcp_freeres;
528 		ops.cl_destroy = clntbtcp_destroy;
529 		ops.cl_control = clntbtcp_control;
530 	}
531 	return (&ops);
532 }
533