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