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