Annotation of sys/nfs/krpc_subr.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: krpc_subr.c,v 1.14 2007/02/27 19:09:56 deraadt Exp $ */
! 2: /* $NetBSD: krpc_subr.c,v 1.12.4.1 1996/06/07 00:52:26 cgd 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/conf.h>
! 49: #include <sys/ioctl.h>
! 50: #include <sys/proc.h>
! 51: #include <sys/mount.h>
! 52: #include <sys/mbuf.h>
! 53: #include <sys/reboot.h>
! 54: #include <sys/socket.h>
! 55: #include <sys/socketvar.h>
! 56:
! 57: #include <net/if.h>
! 58: #include <netinet/in.h>
! 59:
! 60: #include <nfs/rpcv2.h>
! 61: #include <nfs/krpc.h>
! 62: #include <nfs/xdr_subs.h>
! 63: #include <dev/rndvar.h>
! 64:
! 65: /*
! 66: * Kernel support for Sun RPC
! 67: *
! 68: * Used currently for bootstrapping in nfs diskless configurations.
! 69: */
! 70:
! 71: /*
! 72: * Generic RPC headers
! 73: */
! 74:
! 75: struct auth_info {
! 76: u_int32_t authtype; /* auth type */
! 77: u_int32_t authlen; /* auth length */
! 78: };
! 79:
! 80: struct auth_unix {
! 81: int32_t ua_time;
! 82: int32_t ua_hostname; /* null */
! 83: int32_t ua_uid;
! 84: int32_t ua_gid;
! 85: int32_t ua_gidlist; /* null */
! 86: };
! 87:
! 88: struct rpc_call {
! 89: u_int32_t rp_xid; /* request transaction id */
! 90: int32_t rp_direction; /* call direction (0) */
! 91: u_int32_t rp_rpcvers; /* rpc version (2) */
! 92: u_int32_t rp_prog; /* program */
! 93: u_int32_t rp_vers; /* version */
! 94: u_int32_t rp_proc; /* procedure */
! 95: struct auth_info rpc_auth;
! 96: struct auth_unix rpc_unix;
! 97: struct auth_info rpc_verf;
! 98: };
! 99:
! 100: struct rpc_reply {
! 101: u_int32_t rp_xid; /* request transaction id */
! 102: int32_t rp_direction; /* call direction (1) */
! 103: int32_t rp_astatus; /* accept status (0: accepted) */
! 104: union {
! 105: u_int32_t rpu_errno;
! 106: struct {
! 107: struct auth_info rok_auth;
! 108: u_int32_t rok_status;
! 109: } rpu_rok;
! 110: } rp_u;
! 111: };
! 112: #define rp_errno rp_u.rpu_errno
! 113: #define rp_auth rp_u.rpu_rok.rok_auth
! 114: #define rp_status rp_u.rpu_rok.rok_status
! 115:
! 116: #define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */
! 117:
! 118: /*
! 119: * What is the longest we will wait before re-sending a request?
! 120: * Note this is also the frequency of "RPC timeout" messages.
! 121: * The re-send loop count sup linearly to this maximum, so the
! 122: * first complaint will happen after (1+2+3+4+5)=15 seconds.
! 123: */
! 124: #define MAX_RESEND_DELAY 5 /* seconds */
! 125:
! 126: /*
! 127: * Call portmap to lookup a port number for a particular rpc program
! 128: * Returns non-zero error on failure.
! 129: */
! 130: int
! 131: krpc_portmap(sin, prog, vers, portp)
! 132: struct sockaddr_in *sin; /* server address */
! 133: u_int prog, vers; /* host order */
! 134: u_int16_t *portp; /* network order */
! 135: {
! 136: struct sdata {
! 137: u_int32_t prog; /* call program */
! 138: u_int32_t vers; /* call version */
! 139: u_int32_t proto; /* call protocol */
! 140: u_int32_t port; /* call port (unused) */
! 141: } *sdata;
! 142: struct rdata {
! 143: u_int16_t pad;
! 144: u_int16_t port;
! 145: } *rdata;
! 146: struct mbuf *m;
! 147: int error;
! 148:
! 149: /* The portmapper port is fixed. */
! 150: if (prog == PMAPPROG) {
! 151: *portp = htons(PMAPPORT);
! 152: return 0;
! 153: }
! 154:
! 155: m = m_get(M_WAIT, MT_DATA);
! 156: sdata = mtod(m, struct sdata *);
! 157: m->m_len = sizeof(*sdata);
! 158:
! 159: /* Do the RPC to get it. */
! 160: sdata->prog = txdr_unsigned(prog);
! 161: sdata->vers = txdr_unsigned(vers);
! 162: sdata->proto = txdr_unsigned(IPPROTO_UDP);
! 163: sdata->port = 0;
! 164:
! 165: sin->sin_port = htons(PMAPPORT);
! 166: error = krpc_call(sin, PMAPPROG, PMAPVERS,
! 167: PMAPPROC_GETPORT, &m, NULL, -1);
! 168: if (error)
! 169: return error;
! 170:
! 171: if (m->m_len < sizeof(*rdata)) {
! 172: m = m_pullup(m, sizeof(*rdata));
! 173: if (m == NULL)
! 174: return ENOBUFS;
! 175: }
! 176: rdata = mtod(m, struct rdata *);
! 177: *portp = rdata->port;
! 178:
! 179: m_freem(m);
! 180: return 0;
! 181: }
! 182:
! 183: /*
! 184: * Do a remote procedure call (RPC) and wait for its reply.
! 185: * If from_p is non-null, then we are doing broadcast, and
! 186: * the address from whence the response came is saved there.
! 187: */
! 188: int
! 189: krpc_call(sa, prog, vers, func, data, from_p, retries)
! 190: struct sockaddr_in *sa;
! 191: u_int prog, vers, func;
! 192: struct mbuf **data; /* input/output */
! 193: struct mbuf **from_p; /* output */
! 194: int retries;
! 195: {
! 196: struct socket *so;
! 197: struct sockaddr_in *sin;
! 198: struct mbuf *m, *nam, *mhead, *from, *mopt;
! 199: struct rpc_call *call;
! 200: struct rpc_reply *reply;
! 201: struct uio auio;
! 202: int error, rcvflg, timo, secs, len;
! 203: static u_int32_t xid = 0;
! 204: u_int32_t newxid;
! 205: int *ip;
! 206: struct timeval *tv;
! 207:
! 208: /*
! 209: * Validate address family.
! 210: * Sorry, this is INET specific...
! 211: */
! 212: if (sa->sin_family != AF_INET)
! 213: return (EAFNOSUPPORT);
! 214:
! 215: /* Free at end if not null. */
! 216: nam = mhead = NULL;
! 217: from = NULL;
! 218:
! 219: /*
! 220: * Create socket and set its receive timeout.
! 221: */
! 222: if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)))
! 223: goto out;
! 224:
! 225: m = m_get(M_WAIT, MT_SOOPTS);
! 226: tv = mtod(m, struct timeval *);
! 227: m->m_len = sizeof(*tv);
! 228: tv->tv_sec = 1;
! 229: tv->tv_usec = 0;
! 230: if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m)))
! 231: goto out;
! 232:
! 233: /*
! 234: * Enable broadcast if necessary.
! 235: */
! 236: if (from_p) {
! 237: int32_t *on;
! 238: m = m_get(M_WAIT, MT_SOOPTS);
! 239: on = mtod(m, int32_t *);
! 240: m->m_len = sizeof(*on);
! 241: *on = 1;
! 242: if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m)))
! 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: m = m_getclr(M_WAIT, MT_SONAME);
! 252: sin = mtod(m, struct sockaddr_in *);
! 253: sin->sin_len = m->m_len = sizeof(*sin);
! 254: sin->sin_family = AF_INET;
! 255: sin->sin_addr.s_addr = INADDR_ANY;
! 256:
! 257: MGET(mopt, M_WAIT, MT_SOOPTS);
! 258: mopt->m_len = sizeof(int);
! 259: ip = mtod(mopt, int *);
! 260: *ip = IP_PORTRANGE_LOW;
! 261: error = sosetopt(so, IPPROTO_IP, IP_PORTRANGE, mopt);
! 262: if (error)
! 263: goto out;
! 264:
! 265: MGET(m, M_WAIT, MT_SONAME);
! 266: sin = mtod(m, struct sockaddr_in *);
! 267: sin->sin_len = m->m_len = sizeof (struct sockaddr_in);
! 268: sin->sin_family = AF_INET;
! 269: sin->sin_addr.s_addr = INADDR_ANY;
! 270: sin->sin_port = htons(0);
! 271: error = sobind(so, m);
! 272: m_freem(m);
! 273: if (error) {
! 274: printf("bind failed\n");
! 275: goto out;
! 276: }
! 277:
! 278: MGET(mopt, M_WAIT, MT_SOOPTS);
! 279: mopt->m_len = sizeof(int);
! 280: ip = mtod(mopt, int *);
! 281: *ip = IP_PORTRANGE_DEFAULT;
! 282: error = sosetopt(so, IPPROTO_IP, IP_PORTRANGE, mopt);
! 283: if (error)
! 284: goto out;
! 285:
! 286: /*
! 287: * Setup socket address for the server.
! 288: */
! 289: nam = m_get(M_WAIT, MT_SONAME);
! 290: sin = mtod(nam, struct sockaddr_in *);
! 291: bcopy((caddr_t)sa, (caddr_t)sin, (nam->m_len = sa->sin_len));
! 292:
! 293: /*
! 294: * Prepend RPC message header.
! 295: */
! 296: mhead = m_gethdr(M_WAIT, MT_DATA);
! 297: mhead->m_next = *data;
! 298: call = mtod(mhead, struct rpc_call *);
! 299: mhead->m_len = sizeof(*call);
! 300: bzero((caddr_t)call, sizeof(*call));
! 301: /* rpc_call part */
! 302: while ((newxid = arc4random()) == xid);
! 303: xid = newxid;
! 304: call->rp_xid = txdr_unsigned(xid);
! 305: /* call->rp_direction = 0; */
! 306: call->rp_rpcvers = txdr_unsigned(2);
! 307: call->rp_prog = txdr_unsigned(prog);
! 308: call->rp_vers = txdr_unsigned(vers);
! 309: call->rp_proc = txdr_unsigned(func);
! 310: /* rpc_auth part (auth_unix as root) */
! 311: call->rpc_auth.authtype = txdr_unsigned(RPCAUTH_UNIX);
! 312: call->rpc_auth.authlen = txdr_unsigned(sizeof(struct auth_unix));
! 313: /* rpc_verf part (auth_null) */
! 314: call->rpc_verf.authtype = 0;
! 315: call->rpc_verf.authlen = 0;
! 316:
! 317: /*
! 318: * Setup packet header
! 319: */
! 320: len = 0;
! 321: m = mhead;
! 322: while (m) {
! 323: len += m->m_len;
! 324: m = m->m_next;
! 325: }
! 326: mhead->m_pkthdr.len = len;
! 327: mhead->m_pkthdr.rcvif = NULL;
! 328:
! 329: /*
! 330: * Send it, repeatedly, until a reply is received,
! 331: * but delay each re-send by an increasing amount.
! 332: * If the delay hits the maximum, start complaining.
! 333: */
! 334: for (timo = 0; retries; retries--) {
! 335: /* Send RPC request (or re-send). */
! 336: m = m_copym(mhead, 0, M_COPYALL, M_WAIT);
! 337: if (m == NULL) {
! 338: error = ENOBUFS;
! 339: goto out;
! 340: }
! 341: error = sosend(so, nam, NULL, m, NULL, 0);
! 342: if (error) {
! 343: printf("krpc_call: sosend: %d\n", error);
! 344: goto out;
! 345: }
! 346: m = NULL;
! 347:
! 348: /* Determine new timeout. */
! 349: if (timo < MAX_RESEND_DELAY)
! 350: timo++;
! 351: else
! 352: printf("RPC timeout for server %s (0x%x) prog %u\n",
! 353: inet_ntoa(sin->sin_addr),
! 354: ntohl(sin->sin_addr.s_addr), prog);
! 355:
! 356: /*
! 357: * Wait for up to timo seconds for a reply.
! 358: * The socket receive timeout was set to 1 second.
! 359: */
! 360: secs = timo;
! 361: while (secs > 0) {
! 362: if (from) {
! 363: m_freem(from);
! 364: from = NULL;
! 365: }
! 366: if (m) {
! 367: m_freem(m);
! 368: m = NULL;
! 369: }
! 370: auio.uio_resid = len = 1<<16;
! 371: auio.uio_procp = NULL;
! 372: rcvflg = 0;
! 373: error = soreceive(so, &from, &auio, &m, NULL, &rcvflg);
! 374: if (error == EWOULDBLOCK) {
! 375: secs--;
! 376: continue;
! 377: }
! 378: if (error)
! 379: goto out;
! 380: len -= auio.uio_resid;
! 381:
! 382: /* Does the reply contain at least a header? */
! 383: if (len < MIN_REPLY_HDR)
! 384: continue;
! 385: if (m->m_len < MIN_REPLY_HDR)
! 386: continue;
! 387: reply = mtod(m, struct rpc_reply *);
! 388:
! 389: /* Is it the right reply? */
! 390: if (reply->rp_direction != txdr_unsigned(RPC_REPLY))
! 391: continue;
! 392:
! 393: if (reply->rp_xid != txdr_unsigned(xid))
! 394: continue;
! 395:
! 396: /* Was RPC accepted? (authorization OK) */
! 397: if (reply->rp_astatus != 0) {
! 398: error = fxdr_unsigned(u_int32_t, reply->rp_errno);
! 399: printf("rpc denied, error=%d\n", error);
! 400: continue;
! 401: }
! 402:
! 403: /* Did the call succeed? */
! 404: if (reply->rp_status != 0) {
! 405: error = fxdr_unsigned(u_int32_t, reply->rp_status);
! 406: printf("rpc denied, status=%d\n", error);
! 407: continue;
! 408: }
! 409:
! 410: goto gotreply; /* break two levels */
! 411:
! 412: } /* while secs */
! 413: } /* forever send/receive */
! 414:
! 415: error = ETIMEDOUT;
! 416: goto out;
! 417:
! 418: gotreply:
! 419:
! 420: /*
! 421: * Get RPC reply header into first mbuf,
! 422: * get its length, then strip it off.
! 423: */
! 424: len = sizeof(*reply);
! 425: if (m->m_len < len) {
! 426: m = m_pullup(m, len);
! 427: if (m == NULL) {
! 428: error = ENOBUFS;
! 429: goto out;
! 430: }
! 431: }
! 432: reply = mtod(m, struct rpc_reply *);
! 433: if (reply->rp_auth.authtype != 0) {
! 434: len += fxdr_unsigned(u_int32_t, reply->rp_auth.authlen);
! 435: len = (len + 3) & ~3; /* XXX? */
! 436: }
! 437: m_adj(m, len);
! 438:
! 439: /* result */
! 440: *data = m;
! 441: if (from_p) {
! 442: *from_p = from;
! 443: from = NULL;
! 444: }
! 445:
! 446: out:
! 447: if (nam) m_freem(nam);
! 448: if (mhead) m_freem(mhead);
! 449: if (from) m_freem(from);
! 450: soclose(so);
! 451: return error;
! 452: }
! 453:
! 454: /*
! 455: * eXternal Data Representation routines.
! 456: * (but with non-standard args...)
! 457: */
! 458:
! 459: /*
! 460: * String representation for RPC.
! 461: */
! 462: struct xdr_string {
! 463: u_int32_t len; /* length without null or padding */
! 464: char data[4]; /* data (longer, of course) */
! 465: /* data is padded to a long-word boundary */
! 466: };
! 467:
! 468: struct mbuf *
! 469: xdr_string_encode(str, len)
! 470: char *str;
! 471: int len;
! 472: {
! 473: struct mbuf *m;
! 474: struct xdr_string *xs;
! 475: int dlen; /* padded string length */
! 476: int mlen; /* message length */
! 477:
! 478: dlen = (len + 3) & ~3;
! 479: mlen = dlen + 4;
! 480:
! 481: if (mlen > MCLBYTES) /* If too big, we just can't do it. */
! 482: return (NULL);
! 483:
! 484: m = m_get(M_WAIT, MT_DATA);
! 485: if (mlen > MLEN) {
! 486: MCLGET(m, M_WAIT);
! 487: if ((m->m_flags & M_EXT) == 0) {
! 488: (void) m_free(m); /* There can be only one. */
! 489: return (NULL);
! 490: }
! 491: }
! 492: xs = mtod(m, struct xdr_string *);
! 493: m->m_len = mlen;
! 494: xs->len = txdr_unsigned(len);
! 495: bcopy(str, xs->data, len);
! 496: return (m);
! 497: }
! 498:
! 499: struct mbuf *
! 500: xdr_string_decode(m, str, len_p)
! 501: struct mbuf *m;
! 502: char *str;
! 503: int *len_p; /* bufsize - 1 */
! 504: {
! 505: struct xdr_string *xs;
! 506: int mlen; /* message length */
! 507: int slen; /* string length */
! 508:
! 509: if (m->m_len < 4) {
! 510: m = m_pullup(m, 4);
! 511: if (m == NULL)
! 512: return (NULL);
! 513: }
! 514: xs = mtod(m, struct xdr_string *);
! 515: slen = fxdr_unsigned(u_int32_t, xs->len);
! 516: mlen = 4 + ((slen + 3) & ~3);
! 517:
! 518: if (slen > *len_p)
! 519: slen = *len_p;
! 520: if (slen > m->m_pkthdr.len) {
! 521: m_freem(m);
! 522: return (NULL);
! 523: }
! 524: m_copydata(m, 4, slen, str);
! 525: m_adj(m, mlen);
! 526:
! 527: str[slen] = '\0';
! 528: *len_p = slen;
! 529:
! 530: return (m);
! 531: }
! 532:
! 533:
! 534: /*
! 535: * Inet address in RPC messages
! 536: * (Note, really four ints, NOT chars. Blech.)
! 537: */
! 538: struct xdr_inaddr {
! 539: u_int32_t atype;
! 540: u_int32_t addr[4];
! 541: };
! 542:
! 543: struct mbuf *
! 544: xdr_inaddr_encode(ia)
! 545: struct in_addr *ia; /* already in network order */
! 546: {
! 547: struct mbuf *m;
! 548: struct xdr_inaddr *xi;
! 549: u_int8_t *cp;
! 550: u_int32_t *ip;
! 551:
! 552: m = m_get(M_WAIT, MT_DATA);
! 553: xi = mtod(m, struct xdr_inaddr *);
! 554: m->m_len = sizeof(*xi);
! 555: xi->atype = txdr_unsigned(1);
! 556: ip = xi->addr;
! 557: cp = (u_int8_t *)&ia->s_addr;
! 558: *ip++ = txdr_unsigned(*cp++);
! 559: *ip++ = txdr_unsigned(*cp++);
! 560: *ip++ = txdr_unsigned(*cp++);
! 561: *ip++ = txdr_unsigned(*cp++);
! 562:
! 563: return (m);
! 564: }
! 565:
! 566: struct mbuf *
! 567: xdr_inaddr_decode(m, ia)
! 568: struct mbuf *m;
! 569: struct in_addr *ia; /* already in network order */
! 570: {
! 571: struct xdr_inaddr *xi;
! 572: u_int8_t *cp;
! 573: u_int32_t *ip;
! 574:
! 575: if (m->m_len < sizeof(*xi)) {
! 576: m = m_pullup(m, sizeof(*xi));
! 577: if (m == NULL)
! 578: return (NULL);
! 579: }
! 580: xi = mtod(m, struct xdr_inaddr *);
! 581: if (xi->atype != txdr_unsigned(1)) {
! 582: ia->s_addr = INADDR_ANY;
! 583: goto out;
! 584: }
! 585: ip = xi->addr;
! 586: cp = (u_int8_t *)&ia->s_addr;
! 587: *cp++ = fxdr_unsigned(u_int8_t, *ip++);
! 588: *cp++ = fxdr_unsigned(u_int8_t, *ip++);
! 589: *cp++ = fxdr_unsigned(u_int8_t, *ip++);
! 590: *cp++ = fxdr_unsigned(u_int8_t, *ip++);
! 591:
! 592: out:
! 593: m_adj(m, sizeof(*xi));
! 594: return (m);
! 595: }
CVSweb