krpc_subr.c revision 1.1 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.1 1994/04/18 06:18:19 glass 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 * Call portmap to lookup a port number for a particular rpc program
110 * Returns the port number in host order, or ZERO if it couldn't.
111 */
112 int
113 krpc_portmap(sa, prog, vers, portp)
114 struct sockaddr *sa; /* server address */
115 u_long prog, vers; /* host order */
116 u_short *portp; /* network order */
117 {
118 struct sdata {
119 u_long prog; /* call program */
120 u_long vers; /* call version */
121 u_long proto; /* call protocol */
122 u_long port; /* call port (unused) */
123 } *sdata;
124 struct rdata {
125 u_short pad;
126 u_short port;
127 } *rdata;
128 struct mbuf *m;
129 int error;
130
131 /* The portmapper port is fixed. */
132 if (prog == PMAPPROG) {
133 *portp = htons(PMAPPORT);
134 return 0;
135 }
136
137 m = m_get(M_WAIT, MT_DATA);
138 if (m == NULL)
139 return ENOBUFS;
140 m->m_len = sizeof(*sdata);
141 sdata = mtod(m, struct sdata *);
142
143 /* Do the RPC to get it. */
144 sdata->prog = htonl(prog);
145 sdata->vers = htonl(vers);
146 sdata->proto = htonl(IPPROTO_UDP);
147 sdata->port = 0;
148
149 error = krpc_call(sa, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT,
150 &m, sizeof(*rdata));
151 if (error)
152 return error;
153
154 rdata = mtod(m, struct rdata *);
155 *portp = rdata->port;
156
157 m_freem(m);
158 return 0;
159 }
160
161 /*
162 * Do a remote procedure call (RPC) and wait for its reply.
163 */
164 int
165 krpc_call(sa, prog, vers, func, data, want)
166 struct sockaddr *sa;
167 u_long prog, vers, func;
168 struct mbuf **data; /* input/output */
169 int want; /* required response data length */
170 {
171 struct socket *so;
172 struct sockaddr_in *sin;
173 struct timeval *tv;
174 struct mbuf *m, *nam, *mhead;
175 struct rpc_call *call;
176 struct rpc_reply *reply;
177 struct uio auio;
178 int error, rcvflg, timo, secs, len;
179 static u_long xid = ~0xFF;
180
181 /*
182 * Validate address family.
183 * Sorry, this is INET specific...
184 */
185 if (sa->sa_family != AF_INET)
186 return (EAFNOSUPPORT);
187
188 /* Free at end if not null. */
189 nam = mhead = NULL;
190
191 /*
192 * Create socket and set its recieve timeout.
193 */
194 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)))
195 goto out;
196
197 m = m_get(M_WAIT, MT_SOOPTS);
198 if (m == NULL) {
199 error = ENOBUFS;
200 goto out;
201 }
202 tv = mtod(m, struct timeval *);
203 m->m_len = sizeof(*tv);
204 tv->tv_sec = 1;
205 tv->tv_usec = 0;
206 if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m)))
207 goto out;
208
209 /*
210 * Setup socket address for the server.
211 */
212 nam = m_get(M_WAIT, MT_SONAME);
213 if (nam == NULL) {
214 error = ENOBUFS;
215 goto out;
216 }
217 sin = mtod(nam, struct sockaddr_in *);
218 bcopy((caddr_t)sa, (caddr_t)sin, (nam->m_len = sa->sa_len));
219
220 /*
221 * Set the port number that the request will use.
222 */
223 if ((error = krpc_portmap(sa, prog, vers, &sin->sin_port)))
224 goto out;
225
226 /*
227 * Build the RPC message header.
228 */
229 mhead = m_gethdr(M_WAIT, MT_DATA);
230 if (mhead == NULL) {
231 error = ENOBUFS;
232 goto out;
233 }
234 mhead->m_len = sizeof(*call);
235 call = mtod(mhead, struct rpc_call *);
236 bzero((caddr_t)call, sizeof(*call));
237 call->rp_xid = ++xid; /* no need to put in network order */
238 /* call->rp_direction = 0; */
239 call->rp_rpcvers = htonl(2);
240 call->rp_prog = htonl(prog);
241 call->rp_vers = htonl(vers);
242 call->rp_proc = htonl(func);
243 /* call->rp_auth = 0; */
244 /* call->rp_verf = 0; */
245
246 /*
247 * Prepend RPC header and setup packet header.
248 */
249 for (len = 0, m = *data; m ; m = m->m_next)
250 len += m->m_len;
251 mhead->m_next = *data;
252 mhead->m_pkthdr.len = mhead->m_len + len;
253 mhead->m_pkthdr.rcvif = NULL;
254 *data = NULL;
255
256 /*
257 * Send it, repeatedly, until a reply is received, but
258 * delay each send by an increasing amount. (10 sec. max)
259 * When the send delay hits 10 sec. start complaining.
260 */
261 timo = 0;
262 for (;;) {
263 /* Send RPC request (or re-send). */
264 m = m_copym(mhead, 0, M_COPYALL, M_WAIT);
265 error = sosend(so, nam, NULL, m, NULL, 0);
266 if (error)
267 goto out;
268 m = NULL;
269
270 /* Determine new timeout (1 to 10) */
271 if (timo < 10)
272 timo++;
273 else
274 printf("RPC timeout for server 0x%X\n",
275 ntohl(sin->sin_addr.s_addr));
276
277 /*
278 * Wait for up to timo seconds for a reply.
279 * The socket receive timeout was set to 1 second.
280 */
281 secs = timo;
282 while (secs > 0) {
283 auio.uio_resid = len = 1<<16;
284 rcvflg = 0;
285 error = soreceive(so, NULL, &auio, &m, NULL, &rcvflg);
286 if (error == EWOULDBLOCK) {
287 secs--;
288 continue;
289 }
290 if (error)
291 goto out;
292 len -= auio.uio_resid;
293
294 /* Is the reply complete and the right one? */
295 if (len < MIN_REPLY_HDR) {
296 m_freem(m);
297 continue;
298 }
299 if (m->m_len < MIN_REPLY_HDR) {
300 m = m_pullup(m, MIN_REPLY_HDR);
301 if (!m)
302 continue;
303 }
304 reply = mtod(m, struct rpc_reply *);
305 if ((reply->rp_direction == htonl(RPC_REPLY)) &&
306 (reply->rp_xid == xid))
307 goto gotreply; /* break two levels */
308 } /* while secs */
309 } /* forever send/receive */
310 gotreply:
311
312 /*
313 * Got the reply. Check and strip header.
314 */
315 if (reply->rp_astatus != 0) {
316 error = reply->rp_u.rpu_errno;
317 m_freem(m);
318 goto out;
319 }
320 len = sizeof(*reply);
321 if (reply->rp_u.rpu_ok.rp_auth.rp_atype != 0) {
322 len += ntohl(reply->rp_u.rpu_ok.rp_auth.rp_alen);
323 len = (len + 3) & ~3; /* XXX? */
324 }
325 m_adj(m, len);
326 if (m == NULL) {
327 error = ENOBUFS;
328 goto out;
329 }
330 if (m->m_len < want) {
331 m = m_pullup(m, want);
332 if (m == NULL) {
333 error = ENOBUFS;
334 goto out;
335 }
336 }
337 /* result */
338 *data = m;
339
340 out:
341 if (nam) m_freem(nam);
342 if (mhead) m_freem(mhead);
343 soclose(so);
344 return error;
345 }
346
347
348