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