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