krpc_subr.c revision 1.9 1 /* $NetBSD: krpc_subr.c,v 1.9 1995/05/20 01:52:49 mycroft Exp $ */
2
3 /*
4 * Copyright (c) 1995 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/systm.h>
47 #include <sys/conf.h>
48 #include <sys/ioctl.h>
49 #include <sys/proc.h>
50 #include <sys/mount.h>
51 #include <sys/mbuf.h>
52 #include <sys/reboot.h>
53 #include <sys/socket.h>
54 #include <sys/socketvar.h>
55
56 #include <net/if.h>
57 #include <netinet/in.h>
58
59 #include <nfs/rpcv2.h>
60 #include <nfs/krpc.h>
61 #include <nfs/xdr_subs.h>
62
63 /*
64 * Kernel support for Sun RPC
65 *
66 * Used currently for bootstrapping in nfs diskless configurations.
67 */
68
69 /*
70 * Generic RPC headers
71 */
72
73 struct auth_info {
74 int32_t rp_atype; /* auth type */
75 u_int32_t rp_alen; /* auth length */
76 };
77
78 struct rpc_call {
79 u_int32_t rp_xid; /* request transaction id */
80 int32_t rp_direction; /* call direction (0) */
81 u_int32_t rp_rpcvers; /* rpc version (2) */
82 u_int32_t rp_prog; /* program */
83 u_int32_t rp_vers; /* version */
84 u_int32_t rp_proc; /* procedure */
85 struct auth_info rp_auth;
86 struct auth_info rp_verf;
87 };
88
89 struct rpc_reply {
90 u_int32_t rp_xid; /* request transaction id */
91 int32_t rp_direction; /* call direction (1) */
92 int32_t rp_astatus; /* accept status (0: accepted) */
93 union {
94 u_int32_t rpu_errno;
95 struct {
96 struct auth_info rp_auth;
97 u_int32_t rp_rstatus; /* reply status */
98 } rpu_ok;
99 } rp_u;
100 };
101
102 #define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */
103
104 /*
105 * What is the longest we will wait before re-sending a request?
106 * Note this is also the frequency of "RPC timeout" messages.
107 * The re-send loop count sup linearly to this maximum, so the
108 * first complaint will happen after (1+2+3+4+5)=15 seconds.
109 */
110 #define MAX_RESEND_DELAY 5 /* seconds */
111
112 /*
113 * Call portmap to lookup a port number for a particular rpc program
114 * Returns non-zero error on failure.
115 */
116 int
117 krpc_portmap(sin, prog, vers, portp)
118 struct sockaddr_in *sin; /* server address */
119 u_int prog, vers; /* host order */
120 u_int16_t *portp; /* network order */
121 {
122 struct sdata {
123 u_int32_t prog; /* call program */
124 u_int32_t vers; /* call version */
125 u_int32_t proto; /* call protocol */
126 u_int32_t port; /* call port (unused) */
127 } *sdata;
128 struct rdata {
129 u_int16_t pad;
130 u_int16_t port;
131 } *rdata;
132 struct mbuf *m;
133 int error;
134
135 /* The portmapper port is fixed. */
136 if (prog == PMAPPROG) {
137 *portp = htons(PMAPPORT);
138 return 0;
139 }
140
141 m = m_get(M_WAIT, MT_DATA);
142 if (m == NULL)
143 return ENOBUFS;
144 sdata = mtod(m, struct sdata *);
145 m->m_len = sizeof(*sdata);
146
147 /* Do the RPC to get it. */
148 sdata->prog = txdr_unsigned(prog);
149 sdata->vers = txdr_unsigned(vers);
150 sdata->proto = txdr_unsigned(IPPROTO_UDP);
151 sdata->port = 0;
152
153 sin->sin_port = htons(PMAPPORT);
154 error = krpc_call(sin, PMAPPROG, PMAPVERS,
155 PMAPPROC_GETPORT, &m, NULL);
156 if (error)
157 return error;
158
159 if (m->m_len < sizeof(*rdata)) {
160 m = m_pullup(m, sizeof(*rdata));
161 if (m == NULL)
162 return ENOBUFS;
163 }
164 rdata = mtod(m, struct rdata *);
165 *portp = rdata->port;
166
167 m_freem(m);
168 return 0;
169 }
170
171 /*
172 * Do a remote procedure call (RPC) and wait for its reply.
173 * If from_p is non-null, then we are doing broadcast, and
174 * the address from whence the response came is saved there.
175 */
176 int
177 krpc_call(sa, prog, vers, func, data, from_p)
178 struct sockaddr_in *sa;
179 u_int prog, vers, func;
180 struct mbuf **data; /* input/output */
181 struct mbuf **from_p; /* output */
182 {
183 struct socket *so;
184 struct sockaddr_in *sin;
185 struct mbuf *m, *nam, *mhead, *from;
186 struct rpc_call *call;
187 struct rpc_reply *reply;
188 struct uio auio;
189 int error, rcvflg, timo, secs, len;
190 static u_int32_t xid = ~0xFF;
191 u_int tport;
192
193 /*
194 * Validate address family.
195 * Sorry, this is INET specific...
196 */
197 if (sa->sin_family != AF_INET)
198 return (EAFNOSUPPORT);
199
200 /* Free at end if not null. */
201 nam = mhead = NULL;
202 from = NULL;
203
204 /*
205 * Create socket and set its recieve timeout.
206 */
207 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)))
208 goto out;
209
210 m = m_get(M_WAIT, MT_SOOPTS);
211 if (m == NULL) {
212 error = ENOBUFS;
213 goto out;
214 } else {
215 struct timeval *tv;
216 tv = mtod(m, struct timeval *);
217 m->m_len = sizeof(*tv);
218 tv->tv_sec = 1;
219 tv->tv_usec = 0;
220 if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m)))
221 goto out;
222 }
223
224 /*
225 * Enable broadcast if necessary.
226 */
227 if (from_p) {
228 int *on;
229 m = m_get(M_WAIT, MT_SOOPTS);
230 if (m == NULL) {
231 error = ENOBUFS;
232 goto out;
233 }
234 on = mtod(m, int *);
235 m->m_len = sizeof(*on);
236 *on = 1;
237 if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m)))
238 goto out;
239 }
240
241 /*
242 * Bind the local endpoint to a reserved port,
243 * because some NFS servers refuse requests from
244 * non-reserved (non-privileged) ports.
245 */
246 m = m_getclr(M_WAIT, MT_SONAME);
247 sin = mtod(m, struct sockaddr_in *);
248 sin->sin_len = m->m_len = sizeof(*sin);
249 sin->sin_family = AF_INET;
250 sin->sin_addr.s_addr = INADDR_ANY;
251 tport = IPPORT_RESERVED;
252 do {
253 tport--;
254 sin->sin_port = htons(tport);
255 error = sobind(so, m);
256 } while (error == EADDRINUSE &&
257 tport > IPPORT_RESERVED / 2);
258 m_freem(m);
259 if (error) {
260 printf("bind failed\n");
261 goto out;
262 }
263
264 /*
265 * Setup socket address for the server.
266 */
267 nam = m_get(M_WAIT, MT_SONAME);
268 if (nam == NULL) {
269 error = ENOBUFS;
270 goto out;
271 }
272 sin = mtod(nam, struct sockaddr_in *);
273 bcopy((caddr_t)sa, (caddr_t)sin,
274 (nam->m_len = sa->sin_len));
275
276 /*
277 * Prepend RPC message header.
278 */
279 mhead = m_gethdr(M_WAIT, MT_DATA);
280 mhead->m_next = *data;
281 call = mtod(mhead, struct rpc_call *);
282 mhead->m_len = sizeof(*call);
283 bzero((caddr_t)call, sizeof(*call));
284 xid++;
285 call->rp_xid = txdr_unsigned(xid);
286 /* call->rp_direction = 0; */
287 call->rp_rpcvers = txdr_unsigned(2);
288 call->rp_prog = txdr_unsigned(prog);
289 call->rp_vers = txdr_unsigned(vers);
290 call->rp_proc = txdr_unsigned(func);
291 /* call->rp_auth = 0; */
292 /* call->rp_verf = 0; */
293
294 /*
295 * Setup packet header
296 */
297 len = 0;
298 m = mhead;
299 while (m) {
300 len += m->m_len;
301 m = m->m_next;
302 }
303 mhead->m_pkthdr.len = len;
304 mhead->m_pkthdr.rcvif = NULL;
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 != txdr_unsigned(RPC_REPLY))
367 continue;
368
369 if (reply->rp_xid != txdr_unsigned(xid))
370 continue;
371
372 /* Was RPC accepted? (authorization OK) */
373 if (reply->rp_astatus != 0) {
374 error = fxdr_unsigned(u_int32_t, reply->rp_u.rpu_errno);
375 printf("rpc denied, error=%d\n", error);
376 continue;
377 }
378
379 /* Did the call succeed? */
380 if (reply->rp_u.rpu_ok.rp_rstatus != 0) {
381 error = fxdr_unsigned(u_int32_t, reply->rp_u.rpu_ok.rp_rstatus);
382 printf("rpc denied, status=%d\n", error);
383 continue;
384 }
385
386 goto gotreply; /* break two levels */
387
388 } /* while secs */
389 } /* forever send/receive */
390
391 error = ETIMEDOUT;
392 goto out;
393
394 gotreply:
395
396 /*
397 * Get RPC reply header into first mbuf,
398 * get its length, then strip it off.
399 */
400 len = sizeof(*reply);
401 if (m->m_len < len) {
402 m = m_pullup(m, len);
403 if (m == NULL) {
404 error = ENOBUFS;
405 goto out;
406 }
407 }
408 reply = mtod(m, struct rpc_reply *);
409 if (reply->rp_u.rpu_ok.rp_auth.rp_atype != 0) {
410 len += fxdr_unsigned(u_int32_t, reply->rp_u.rpu_ok.rp_auth.rp_alen);
411 len = (len + 3) & ~3; /* XXX? */
412 }
413 m_adj(m, len);
414
415 /* result */
416 *data = m;
417 if (from_p) {
418 *from_p = from;
419 from = NULL;
420 }
421
422 out:
423 if (nam) m_freem(nam);
424 if (mhead) m_freem(mhead);
425 if (from) m_freem(from);
426 soclose(so);
427 return error;
428 }
429
430 /*
431 * eXternal Data Representation routines.
432 * (but with non-standard args...)
433 */
434
435 /*
436 * String representation for RPC.
437 */
438 struct xdr_string {
439 u_int32_t len; /* length without null or padding */
440 char data[4]; /* data (longer, of course) */
441 /* data is padded to a long-word boundary */
442 };
443
444 struct mbuf *
445 xdr_string_encode(str, len)
446 char *str;
447 int len;
448 {
449 struct mbuf *m;
450 struct xdr_string *xs;
451 int dlen; /* padded string length */
452 int mlen; /* message length */
453
454 dlen = (len + 3) & ~3;
455 mlen = dlen + 4;
456
457 m = m_get(M_WAIT, MT_DATA);
458 if (mlen > MLEN) {
459 if (mlen > MCLBYTES)
460 return(NULL);
461 MCLGET(m, M_WAIT);
462 if (m == NULL)
463 return NULL;
464 }
465 xs = mtod(m, struct xdr_string *);
466 m->m_len = mlen;
467 xs->len = txdr_unsigned(len);
468 bcopy(str, xs->data, len);
469 return (m);
470 }
471
472 struct mbuf *
473 xdr_string_decode(m, str, len_p)
474 struct mbuf *m;
475 char *str;
476 int *len_p; /* bufsize - 1 */
477 {
478 struct xdr_string *xs;
479 int mlen; /* message length */
480 int slen; /* string length */
481
482 if (m->m_len < 4) {
483 m = m_pullup(m, 4);
484 if (m == NULL)
485 return (NULL);
486 }
487 xs = mtod(m, struct xdr_string *);
488 slen = fxdr_unsigned(u_int32_t, xs->len);
489 mlen = 4 + ((slen + 3) & ~3);
490
491 if (slen > *len_p)
492 slen = *len_p;
493 m_copydata(m, 4, slen, str);
494 m_adj(m, mlen);
495
496 str[slen] = '\0';
497 *len_p = slen;
498
499 return (m);
500 }
501
502
503 /*
504 * Inet address in RPC messages
505 * (Note, really four ints, NOT chars. Blech.)
506 */
507 struct xdr_inaddr {
508 u_int32_t atype;
509 u_int32_t addr[4];
510 };
511
512 struct mbuf *
513 xdr_inaddr_encode(ia)
514 struct in_addr *ia; /* already in network order */
515 {
516 struct mbuf *m;
517 struct xdr_inaddr *xi;
518 u_int8_t *cp;
519 u_int32_t *ip;
520
521 m = m_get(M_WAIT, MT_DATA);
522 xi = mtod(m, struct xdr_inaddr *);
523 m->m_len = sizeof(*xi);
524 xi->atype = txdr_unsigned(1);
525 ip = xi->addr;
526 cp = (u_int8_t *)&ia->s_addr;
527 *ip++ = txdr_unsigned(*cp++);
528 *ip++ = txdr_unsigned(*cp++);
529 *ip++ = txdr_unsigned(*cp++);
530 *ip++ = txdr_unsigned(*cp++);
531
532 return (m);
533 }
534
535 struct mbuf *
536 xdr_inaddr_decode(m, ia)
537 struct mbuf *m;
538 struct in_addr *ia; /* already in network order */
539 {
540 struct xdr_inaddr *xi;
541 u_int8_t *cp;
542 u_int32_t *ip;
543
544 if (m->m_len < sizeof(*xi)) {
545 m = m_pullup(m, sizeof(*xi));
546 if (m == NULL)
547 return (NULL);
548 }
549 xi = mtod(m, struct xdr_inaddr *);
550 if (xi->atype != txdr_unsigned(1)) {
551 ia->s_addr = INADDR_ANY;
552 goto out;
553 }
554 ip = xi->addr;
555 cp = (u_int8_t *)&ia->s_addr;
556 *cp++ = fxdr_unsigned(u_int8_t, *ip++);
557 *cp++ = fxdr_unsigned(u_int8_t, *ip++);
558 *cp++ = fxdr_unsigned(u_int8_t, *ip++);
559 *cp++ = fxdr_unsigned(u_int8_t, *ip++);
560
561 out:
562 m_adj(m, sizeof(*xi));
563 return (m);
564 }
565