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