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