]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/krpc_subr.c
xnu-344.tar.gz
[apple/xnu.git] / bsd / nfs / krpc_subr.c
1 /*
2 * Copyright (c) 2000 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_level = SOL_SOCKET;
241 sopt.sopt_name = SO_RCVTIMEO;
242 sopt.sopt_val = &tv;
243 sopt.sopt_valsize = sizeof tv;
244
245 if (error = sosetopt(so, &sopt))
246 goto out;
247
248 }
249
250 /*
251 * Enable broadcast if necessary.
252 */
253
254 if (from_p) {
255 int on = 1;
256 sopt.sopt_name = SO_BROADCAST;
257 sopt.sopt_val = &on;
258 sopt.sopt_valsize = sizeof on;
259 if (error = sosetopt(so, &sopt))
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 m = m_getclr(M_WAIT, MT_SONAME);
269 sin = mtod(m, struct sockaddr_in *);
270 sin->sin_len = m->m_len = sizeof(*sin);
271 sin->sin_family = AF_INET;
272 sin->sin_addr.s_addr = INADDR_ANY;
273 tport = IPPORT_RESERVED;
274 do {
275 tport--;
276 sin->sin_port = htons(tport);
277 error = sobind(so, mtod(m, struct sockaddr *));
278 } while (error == EADDRINUSE &&
279 tport > IPPORT_RESERVED / 2);
280 m_freem(m);
281 if (error) {
282 printf("bind failed\n");
283 goto out;
284 }
285
286 /*
287 * Setup socket address for the server.
288 */
289 nam = m_get(M_WAIT, MT_SONAME);
290 if (nam == NULL) {
291 error = ENOBUFS;
292 goto out;
293 }
294 sin = mtod(nam, struct sockaddr_in *);
295 bcopy((caddr_t)sa, (caddr_t)sin, (nam->m_len = sa->sin_len));
296
297 /*
298 * Prepend RPC message header.
299 */
300 m = *data;
301 *data = NULL;
302 #if DIAGNOSTIC
303 if ((m->m_flags & M_PKTHDR) == 0)
304 panic("krpc_call: send data w/o pkthdr");
305 if (m->m_pkthdr.len < m->m_len)
306 panic("krpc_call: pkthdr.len not set");
307 #endif
308 mhead = m_prepend(m, sizeof(*call), M_WAIT);
309 if (mhead == NULL) {
310 error = ENOBUFS;
311 goto out;
312 }
313 mhead->m_pkthdr.len += sizeof(*call);
314 mhead->m_pkthdr.rcvif = NULL;
315
316 /*
317 * Fill in the RPC header
318 */
319 call = mtod(mhead, struct rpc_call *);
320 bzero((caddr_t)call, sizeof(*call));
321 xid++;
322 call->rp_xid = htonl(xid);
323 /* call->rp_direction = 0; */
324 call->rp_rpcvers = htonl(2);
325 call->rp_prog = htonl(prog);
326 call->rp_vers = htonl(vers);
327 call->rp_proc = htonl(func);
328 /* call->rp_auth = 0; */
329 /* call->rp_verf = 0; */
330
331 /*
332 * Send it, repeatedly, until a reply is received,
333 * but delay each re-send by an increasing amount.
334 * If the delay hits the maximum, start complaining.
335 */
336 timo = 0;
337 for (;;) {
338 /* Send RPC request (or re-send). */
339 m = m_copym(mhead, 0, M_COPYALL, M_WAIT);
340 if (m == NULL) {
341 error = ENOBUFS;
342 goto out;
343 }
344 error = sosend(so, mtod(nam, struct sockaddr *), NULL, m, NULL, 0);
345 if (error) {
346 printf("krpc_call: sosend: %d\n", error);
347 goto out;
348 }
349 m = NULL;
350
351 /* Determine new timeout. */
352 if (timo < MAX_RESEND_DELAY)
353 timo++;
354 else
355 printf("RPC timeout for server " IP_FORMAT "\n",
356 IP_LIST(&(sin->sin_addr.s_addr)));
357
358 /*
359 * Wait for up to timo seconds for a reply.
360 * The socket receive timeout was set to 1 second.
361 */
362 secs = timo;
363 while (secs > 0) {
364 if ((from_p) && (*from_p)){
365 FREE(*from_p, M_SONAME);
366 *from_p = NULL;
367 }
368
369 if (m) {
370 m_freem(m);
371 m = NULL;
372 }
373 auio.uio_resid = len = 1<<16;
374 rcvflg = 0;
375
376 error = soreceive(so, (struct sockaddr **) from_p, &auio, &m, NULL, &rcvflg);
377
378 if (error == EWOULDBLOCK) {
379 secs--;
380 continue;
381 }
382 if (error)
383 goto out;
384 len -= auio.uio_resid;
385
386 /* Does the reply contain at least a header? */
387 if (len < MIN_REPLY_HDR)
388 continue;
389 if (m->m_len < MIN_REPLY_HDR)
390 continue;
391 reply = mtod(m, struct rpc_reply *);
392
393 /* Is it the right reply? */
394 if (reply->rp_direction != htonl(RPC_REPLY))
395 continue;
396
397 if (reply->rp_xid != htonl(xid))
398 continue;
399
400 /* Was RPC accepted? (authorization OK) */
401 if (reply->rp_astatus != 0) {
402 error = ntohl(reply->rp_u.rpu_errno);
403 printf("rpc denied, error=%d\n", error);
404 continue;
405 }
406
407 /* Did the call succeed? */
408 if ((error = ntohl(reply->rp_u.rpu_ok.rp_rstatus)) != 0) {
409 printf("rpc status=%d\n", error);
410 continue;
411 }
412
413 goto gotreply; /* break two levels */
414
415 } /* while secs */
416 } /* forever send/receive */
417
418 error = ETIMEDOUT;
419 goto out;
420
421 gotreply:
422
423 /*
424 * Pull as much as we can into first mbuf, to make
425 * result buffer contiguous. Note that if the entire
426 * result won't fit into one mbuf, you're out of luck.
427 * XXX - Should not rely on making the entire reply
428 * contiguous (fix callers instead). -gwr
429 */
430 #if DIAGNOSTIC
431 if ((m->m_flags & M_PKTHDR) == 0)
432 panic("krpc_call: received pkt w/o header?");
433 #endif
434 len = m->m_pkthdr.len;
435 if (m->m_len < len) {
436 m = m_pullup(m, len);
437 if (m == NULL) {
438 error = ENOBUFS;
439 goto out;
440 }
441 reply = mtod(m, struct rpc_reply *);
442 }
443
444 /*
445 * Strip RPC header
446 */
447 len = sizeof(*reply);
448 if (reply->rp_u.rpu_ok.rp_auth.rp_atype != 0) {
449 len += ntohl(reply->rp_u.rpu_ok.rp_auth.rp_alen);
450 len = (len + 3) & ~3; /* XXX? */
451 }
452 m_adj(m, len);
453
454 /* result */
455 *data = m;
456 out:
457 if (nam) m_freem(nam);
458 if (mhead) m_freem(mhead);
459 soclose(so);
460 return error;
461 }