xref: /netbsd-src/sys/nfs/krpc_subr.c (revision ae1bfcddc410612bc8c58b807e1830becb69a24c)
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