]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/krpc_subr.c
xnu-792.6.56.tar.gz
[apple/xnu.git] / bsd / nfs / krpc_subr.c
CommitLineData
1c79356b 1/*
e5568f75 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
ff6e181a
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
1c79356b 12 *
ff6e181a
A
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1c79356b
A
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
ff6e181a
A
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
1c79356b
A
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
24/*
25 * Copyright (c) 1994 Gordon Ross, Adam Glass
26 * Copyright (c) 1992 Regents of the University of California.
27 * All rights reserved.
28 *
29 * This software was developed by the Computer Systems Engineering group
30 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
31 * contributed to Berkeley.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. All advertising materials mentioning features or use of this software
42 * must display the following acknowledgement:
43 * This product includes software developed by the University of
44 * California, Lawrence Berkeley Laboratory and its contributors.
45 * 4. Neither the name of the University nor the names of its contributors
46 * may be used to endorse or promote products derived from this software
47 * without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 *
61 */
62
63#include <sys/param.h>
64#include <sys/conf.h>
65#include <sys/ioctl.h>
66#include <sys/proc.h>
67#include <sys/mount.h>
91447636 68#include <sys/kpi_mbuf.h>
1c79356b
A
69#include <sys/malloc.h>
70#include <sys/socket.h>
71#include <sys/socketvar.h>
72#include <sys/systm.h>
73#include <sys/reboot.h>
91447636 74#include <sys/uio_internal.h>
1c79356b
A
75
76#include <net/if.h>
77#include <netinet/in.h>
78
79#include <nfs/rpcv2.h>
80#include <nfs/krpc.h>
81
82/*
83 * Kernel support for Sun RPC
84 *
85 * Used currently for bootstrapping in nfs diskless configurations.
86 *
87 * Note: will not work on variable-sized rpc args/results.
88 * implicit size-limit of an mbuf.
89 */
90
91/*
92 * Generic RPC headers
93 */
94
95struct auth_info {
96 u_int32_t rp_atype; /* auth type */
97 u_int32_t rp_alen; /* auth length */
98};
99
100struct rpc_call {
101 u_int32_t rp_xid; /* request transaction id */
102 int32_t rp_direction; /* call direction (0) */
103 u_int32_t rp_rpcvers; /* rpc version (2) */
104 u_int32_t rp_prog; /* program */
105 u_int32_t rp_vers; /* version */
106 u_int32_t rp_proc; /* procedure */
107 struct auth_info rp_auth;
108 struct auth_info rp_verf;
109};
110
111struct rpc_reply {
112 u_int32_t rp_xid; /* request transaction id */
113 int32_t rp_direction; /* call direction (1) */
114 int32_t rp_astatus; /* accept status (0: accepted) */
115 union {
116 u_int32_t rpu_errno;
117 struct {
118 struct auth_info rp_auth;
119 u_int32_t rp_rstatus;
120 } rpu_ok;
121 } rp_u;
122};
123
124#define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */
125
126/*
127 * What is the longest we will wait before re-sending a request?
128 * Note this is also the frequency of "RPC timeout" messages.
129 * The re-send loop count sup linearly to this maximum, so the
130 * first complaint will happen after (1+2+3+4+5)=15 seconds.
131 */
132#define MAX_RESEND_DELAY 5 /* seconds */
133
134/* copied over from nfs_boot.c for printf format. could put in .h file... */
135#define IP_FORMAT "%d.%d.%d.%d"
136#define IP_CH(ip) ((u_char *)ip)
137#define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
138
139
140/*
141 * Call portmap to lookup a port number for a particular rpc program
142 * Returns non-zero error on failure.
143 */
144int
91447636
A
145krpc_portmap(sin, prog, vers, proto, portp)
146 struct sockaddr_in *sin; /* server address */
147 u_int prog, vers, proto; /* host order */
148 u_int16_t *portp; /* network order */
1c79356b
A
149{
150 struct sdata {
151 u_int32_t prog; /* call program */
152 u_int32_t vers; /* call version */
153 u_int32_t proto; /* call protocol */
154 u_int32_t port; /* call port (unused) */
155 } *sdata;
156 struct rdata {
157 u_int16_t pad;
158 u_int16_t port;
159 } *rdata;
91447636 160 mbuf_t m;
1c79356b
A
161 int error;
162
163 /* The portmapper port is fixed. */
164 if (prog == PMAPPROG) {
165 *portp = htons(PMAPPORT);
166 return 0;
167 }
168
91447636
A
169 error = mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &m);
170 if (error)
171 return error;
172 mbuf_setlen(m, sizeof(*sdata));
173 mbuf_pkthdr_setlen(m, sizeof(*sdata));
174 sdata = mbuf_data(m);
1c79356b
A
175
176 /* Do the RPC to get it. */
177 sdata->prog = htonl(prog);
178 sdata->vers = htonl(vers);
91447636 179 sdata->proto = htonl(proto);
1c79356b
A
180 sdata->port = 0;
181
182 sin->sin_port = htons(PMAPPORT);
91447636 183 error = krpc_call(sin, SOCK_DGRAM, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT, &m, NULL);
1c79356b
A
184 if (error)
185 return error;
186
91447636 187 rdata = mbuf_data(m);
1c79356b
A
188 *portp = rdata->port;
189
91447636
A
190 if (!rdata->port)
191 error = EPROGUNAVAIL;
192
193 mbuf_freem(m);
194 return (error);
1c79356b
A
195}
196
197/*
198 * Do a remote procedure call (RPC) and wait for its reply.
199 * If from_p is non-null, then we are doing broadcast, and
200 * the address from whence the response came is saved there.
201 */
202int
91447636 203krpc_call(sa, sotype, prog, vers, func, data, from_p)
1c79356b 204 struct sockaddr_in *sa;
91447636
A
205 u_int sotype, prog, vers, func;
206 mbuf_t *data; /* input/output */
207 struct sockaddr_in *from_p; /* output */
1c79356b 208{
91447636 209 socket_t so;
1c79356b 210 struct sockaddr_in *sin;
91447636 211 mbuf_t m, nam, mhead;
1c79356b
A
212 struct rpc_call *call;
213 struct rpc_reply *reply;
91447636 214 int error, timo, secs, len;
1c79356b
A
215 static u_int32_t xid = ~0xFF;
216 u_int16_t tport;
91447636 217 int maxpacket = 1<<16;
1c79356b
A
218
219 /*
220 * Validate address family.
221 * Sorry, this is INET specific...
222 */
223 if (sa->sin_family != AF_INET)
224 return (EAFNOSUPPORT);
225
226 /* Free at end if not null. */
227 nam = mhead = NULL;
1c79356b
A
228
229 /*
230 * Create socket and set its recieve timeout.
231 */
91447636 232 if ((error = sock_socket(AF_INET, sotype, 0, 0, 0, &so)))
1c79356b
A
233 goto out;
234
235 {
236 struct timeval tv;
237
238 tv.tv_sec = 1;
239 tv.tv_usec = 0;
91447636
A
240
241 if ((error = sock_setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))))
1c79356b
A
242 goto out;
243
244 }
245
246 /*
247 * Enable broadcast if necessary.
248 */
249
91447636 250 if (from_p && (sotype == SOCK_DGRAM)) {
1c79356b 251 int on = 1;
91447636 252 if ((error = sock_setsockopt(so, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on))))
1c79356b
A
253 goto out;
254 }
255
256 /*
257 * Bind the local endpoint to a reserved port,
258 * because some NFS servers refuse requests from
259 * non-reserved (non-privileged) ports.
260 */
91447636
A
261 if ((error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_SONAME, &m)))
262 goto out;
263 sin = mbuf_data(m);
264 bzero(sin, sizeof(*sin));
265 mbuf_setlen(m, sizeof(*sin));
266 sin->sin_len = sizeof(*sin);
1c79356b
A
267 sin->sin_family = AF_INET;
268 sin->sin_addr.s_addr = INADDR_ANY;
269 tport = IPPORT_RESERVED;
270 do {
271 tport--;
272 sin->sin_port = htons(tport);
91447636 273 error = sock_bind(so, (struct sockaddr*)sin);
1c79356b
A
274 } while (error == EADDRINUSE &&
275 tport > IPPORT_RESERVED / 2);
91447636 276 mbuf_freem(m);
e5568f75 277 m = NULL;
1c79356b
A
278 if (error) {
279 printf("bind failed\n");
280 goto out;
281 }
282
283 /*
284 * Setup socket address for the server.
285 */
91447636 286 if ((error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_SONAME, &nam)))
1c79356b 287 goto out;
91447636
A
288 sin = mbuf_data(nam);
289 mbuf_setlen(nam, sa->sin_len);
290 bcopy((caddr_t)sa, (caddr_t)sin, sa->sin_len);
291
292 if (sotype == SOCK_STREAM) {
293 struct timeval tv;
294 tv.tv_sec = 60;
295 tv.tv_usec = 0;
296 error = sock_connect(so, mbuf_data(nam), MSG_DONTWAIT);
297 if (error && (error != EINPROGRESS))
298 goto out;
299 error = sock_connectwait(so, &tv);
300 if (error) {
301 if (error == EINPROGRESS)
302 error = ETIMEDOUT;
303 printf("krpc_call: error waiting for TCP socket connect: %d\n", error);
304 goto out;
305 }
1c79356b 306 }
1c79356b
A
307
308 /*
309 * Prepend RPC message header.
310 */
311 m = *data;
312 *data = NULL;
313#if DIAGNOSTIC
91447636 314 if ((mbuf_flags(m) & MBUF_PKTHDR) == 0)
1c79356b 315 panic("krpc_call: send data w/o pkthdr");
91447636 316 if (mbuf_pkthdr_len(m) < mbuf_len(m))
1c79356b
A
317 panic("krpc_call: pkthdr.len not set");
318#endif
91447636
A
319 len = sizeof(*call);
320 if (sotype == SOCK_STREAM)
321 len += 4; /* account for RPC record marker */
322 mhead = m;
323 if ((error = mbuf_prepend(&mhead, len, MBUF_WAITOK)))
324 goto out;
325 if ((error = mbuf_pkthdr_setrcvif(mhead, NULL)))
1c79356b 326 goto out;
1c79356b
A
327
328 /*
329 * Fill in the RPC header
330 */
91447636
A
331 if (sotype == SOCK_STREAM) {
332 /* first, fill in RPC record marker */
333 u_long *recmark = mbuf_data(mhead);
334 *recmark = htonl(0x80000000 | (mbuf_pkthdr_len(mhead) - 4));
335 call = (struct rpc_call *)(recmark + 1);
336 } else {
337 call = mbuf_data(mhead);
338 }
1c79356b
A
339 bzero((caddr_t)call, sizeof(*call));
340 xid++;
341 call->rp_xid = htonl(xid);
342 /* call->rp_direction = 0; */
343 call->rp_rpcvers = htonl(2);
344 call->rp_prog = htonl(prog);
345 call->rp_vers = htonl(vers);
346 call->rp_proc = htonl(func);
347 /* call->rp_auth = 0; */
348 /* call->rp_verf = 0; */
349
350 /*
351 * Send it, repeatedly, until a reply is received,
352 * but delay each re-send by an increasing amount.
353 * If the delay hits the maximum, start complaining.
354 */
355 timo = 0;
356 for (;;) {
91447636
A
357 struct msghdr msg;
358
1c79356b 359 /* Send RPC request (or re-send). */
91447636 360 if ((error = mbuf_copym(mhead, 0, MBUF_COPYALL, MBUF_WAITOK, &m)))
1c79356b 361 goto out;
91447636
A
362 bzero(&msg, sizeof(msg));
363 if (sotype == SOCK_STREAM) {
364 msg.msg_name = NULL;
365 msg.msg_namelen = 0;
366 } else {
367 msg.msg_name = mbuf_data(nam);
368 msg.msg_namelen = mbuf_len(nam);
1c79356b 369 }
91447636 370 error = sock_sendmbuf(so, &msg, m, 0, 0);
1c79356b
A
371 if (error) {
372 printf("krpc_call: sosend: %d\n", error);
373 goto out;
374 }
375 m = NULL;
376
377 /* Determine new timeout. */
378 if (timo < MAX_RESEND_DELAY)
379 timo++;
380 else
381 printf("RPC timeout for server " IP_FORMAT "\n",
382 IP_LIST(&(sin->sin_addr.s_addr)));
383
384 /*
385 * Wait for up to timo seconds for a reply.
386 * The socket receive timeout was set to 1 second.
387 */
388 secs = timo;
389 while (secs > 0) {
91447636
A
390 size_t readlen;
391
1c79356b 392 if (m) {
91447636 393 mbuf_freem(m);
1c79356b
A
394 m = NULL;
395 }
91447636
A
396 if (sotype == SOCK_STREAM) {
397 int maxretries = 60;
398 struct iovec_32 aio;
399 aio.iov_base = (uintptr_t) &len;
400 aio.iov_len = sizeof(u_long);
401 bzero(&msg, sizeof(msg));
402 msg.msg_iov = (struct iovec *) &aio;
403 msg.msg_iovlen = 1;
404 do {
405 error = sock_receive(so, &msg, MSG_WAITALL, &readlen);
406 if ((error == EWOULDBLOCK) && (--maxretries <= 0))
407 error = ETIMEDOUT;
408 } while (error == EWOULDBLOCK);
409 if (!error && readlen < aio.iov_len) {
410 /* only log a message if we got a partial word */
411 if (readlen != 0)
412 printf("short receive (%d/%d) from server " IP_FORMAT "\n",
413 readlen, sizeof(u_long), IP_LIST(&(sin->sin_addr.s_addr)));
414 error = EPIPE;
415 }
416 if (error)
417 goto out;
418 len = ntohl(len) & ~0x80000000;
419 /*
420 * This is SERIOUS! We are out of sync with the sender
421 * and forcing a disconnect/reconnect is all I can do.
422 */
423 if (len > maxpacket) {
424 printf("impossible packet length (%d) from server %s\n",
425 len, IP_LIST(&(sin->sin_addr.s_addr)));
426 error = EFBIG;
427 goto out;
428 }
429
430 do {
431 readlen = len;
432 error = sock_receivembuf(so, NULL, &m, MSG_WAITALL, &readlen);
433 } while (error == EWOULDBLOCK);
434
435 if (!error && (len > (int)readlen)) {
436 printf("short receive (%d/%d) from server %s\n",
437 readlen, len, IP_LIST(&(sin->sin_addr.s_addr)));
438 error = EPIPE;
439 }
440 } else {
441 len = maxpacket;
442 readlen = len;
443 bzero(&msg, sizeof(msg));
444 msg.msg_name = from_p;
445 msg.msg_namelen = (from_p == NULL) ? 0 : sizeof(*from_p);
446 error = sock_receivembuf(so, &msg, &m, 0, &readlen);
447 }
1c79356b
A
448
449 if (error == EWOULDBLOCK) {
450 secs--;
451 continue;
452 }
453 if (error)
454 goto out;
91447636 455 len = readlen;
1c79356b
A
456
457 /* Does the reply contain at least a header? */
458 if (len < MIN_REPLY_HDR)
459 continue;
91447636 460 if (mbuf_len(m) < MIN_REPLY_HDR)
1c79356b 461 continue;
91447636 462 reply = mbuf_data(m);
1c79356b
A
463
464 /* Is it the right reply? */
465 if (reply->rp_direction != htonl(RPC_REPLY))
466 continue;
467
468 if (reply->rp_xid != htonl(xid))
469 continue;
91447636 470
1c79356b
A
471 /* Was RPC accepted? (authorization OK) */
472 if (reply->rp_astatus != 0) {
473 error = ntohl(reply->rp_u.rpu_errno);
474 printf("rpc denied, error=%d\n", error);
90556fb8
A
475 /* convert rpc error to errno */
476 switch (error) {
477 case RPC_MISMATCH:
478 error = ERPCMISMATCH;
479 break;
480 case RPC_AUTHERR:
481 error = EAUTH;
482 break;
483 }
484 goto out;
1c79356b
A
485 }
486
487 /* Did the call succeed? */
488 if ((error = ntohl(reply->rp_u.rpu_ok.rp_rstatus)) != 0) {
489 printf("rpc status=%d\n", error);
90556fb8
A
490 /* convert rpc error to errno */
491 switch (error) {
492 case RPC_PROGUNAVAIL:
493 error = EPROGUNAVAIL;
494 break;
495 case RPC_PROGMISMATCH:
496 error = EPROGMISMATCH;
497 break;
498 case RPC_PROCUNAVAIL:
499 error = EPROCUNAVAIL;
500 break;
501 case RPC_GARBAGE:
502 error = EINVAL;
503 break;
504 case RPC_SYSTEM_ERR:
505 error = EIO;
506 break;
507 }
508 goto out;
1c79356b
A
509 }
510
511 goto gotreply; /* break two levels */
512
513 } /* while secs */
514 } /* forever send/receive */
515
516 error = ETIMEDOUT;
517 goto out;
518
519 gotreply:
520
521 /*
522 * Pull as much as we can into first mbuf, to make
523 * result buffer contiguous. Note that if the entire
524 * result won't fit into one mbuf, you're out of luck.
525 * XXX - Should not rely on making the entire reply
526 * contiguous (fix callers instead). -gwr
527 */
528#if DIAGNOSTIC
91447636 529 if ((mbuf_flags(m) & MBUF_PKTHDR) == 0)
1c79356b
A
530 panic("krpc_call: received pkt w/o header?");
531#endif
91447636
A
532 len = mbuf_pkthdr_len(m);
533 if (sotype == SOCK_STREAM)
534 len -= 4; /* the RPC record marker was read separately */
535 if (mbuf_len(m) < len) {
536 if ((error = mbuf_pullup(&m, len)))
1c79356b 537 goto out;
91447636 538 reply = mbuf_data(m);
1c79356b
A
539 }
540
541 /*
542 * Strip RPC header
543 */
544 len = sizeof(*reply);
545 if (reply->rp_u.rpu_ok.rp_auth.rp_atype != 0) {
546 len += ntohl(reply->rp_u.rpu_ok.rp_auth.rp_alen);
547 len = (len + 3) & ~3; /* XXX? */
548 }
91447636 549 mbuf_adj(m, len);
1c79356b
A
550
551 /* result */
552 *data = m;
553 out:
91447636
A
554 if (nam) mbuf_freem(nam);
555 if (mhead) mbuf_freem(mhead);
556 sock_close(so);
1c79356b
A
557 return error;
558}