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