1 /* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Rick Macklem at The University of Guelph. 7 * 8 * %sccs.include.redist.c% 9 * 10 * @(#)nfs_socket.c 7.19 (Berkeley) 10/01/90 11 */ 12 13 /* 14 * Socket operations for use by nfs 15 */ 16 17 #include "types.h" 18 #include "param.h" 19 #include "uio.h" 20 #include "user.h" 21 #include "proc.h" 22 #include "signal.h" 23 #include "mount.h" 24 #include "kernel.h" 25 #include "malloc.h" 26 #include "mbuf.h" 27 #include "vnode.h" 28 #include "domain.h" 29 #include "protosw.h" 30 #include "socket.h" 31 #include "socketvar.h" 32 #include "../netinet/in.h" 33 #include "../netinet/tcp.h" 34 #include "rpcv2.h" 35 #include "nfsv2.h" 36 #include "nfs.h" 37 #include "xdr_subs.h" 38 #include "nfsm_subs.h" 39 #include "nfsmount.h" 40 41 #include "syslog.h" 42 43 #define TRUE 1 44 #define FALSE 0 45 46 /* 47 * External data, mostly RPC constants in XDR form 48 */ 49 extern u_long rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers, rpc_auth_unix, 50 rpc_msgaccepted, rpc_call; 51 extern u_long nfs_prog, nfs_vers; 52 /* Maybe these should be bits in a u_long ?? */ 53 extern int nonidempotent[NFS_NPROCS]; 54 static int compressrequest[NFS_NPROCS] = { 55 FALSE, 56 TRUE, 57 TRUE, 58 FALSE, 59 TRUE, 60 TRUE, 61 TRUE, 62 FALSE, 63 FALSE, 64 TRUE, 65 TRUE, 66 TRUE, 67 TRUE, 68 TRUE, 69 TRUE, 70 TRUE, 71 TRUE, 72 TRUE, 73 }; 74 int nfs_sbwait(); 75 void nfs_disconnect(); 76 struct mbuf *nfs_compress(), *nfs_uncompress(); 77 78 int nfsrv_null(), 79 nfsrv_getattr(), 80 nfsrv_setattr(), 81 nfsrv_lookup(), 82 nfsrv_readlink(), 83 nfsrv_read(), 84 nfsrv_write(), 85 nfsrv_create(), 86 nfsrv_remove(), 87 nfsrv_rename(), 88 nfsrv_link(), 89 nfsrv_symlink(), 90 nfsrv_mkdir(), 91 nfsrv_rmdir(), 92 nfsrv_readdir(), 93 nfsrv_statfs(), 94 nfsrv_noop(); 95 96 int (*nfsrv_procs[NFS_NPROCS])() = { 97 nfsrv_null, 98 nfsrv_getattr, 99 nfsrv_setattr, 100 nfsrv_noop, 101 nfsrv_lookup, 102 nfsrv_readlink, 103 nfsrv_read, 104 nfsrv_noop, 105 nfsrv_write, 106 nfsrv_create, 107 nfsrv_remove, 108 nfsrv_rename, 109 nfsrv_link, 110 nfsrv_symlink, 111 nfsrv_mkdir, 112 nfsrv_rmdir, 113 nfsrv_readdir, 114 nfsrv_statfs, 115 }; 116 117 struct nfsreq nfsreqh; 118 int nfsrexmtthresh = NFS_FISHY; 119 int nfs_tcpnodelay = 1; 120 121 /* 122 * Initialize sockets and congestion for a new NFS connection. 123 * We do not free the sockaddr if error. 124 */ 125 nfs_connect(nmp) 126 register struct nfsmount *nmp; 127 { 128 register struct socket *so; 129 int s, error; 130 struct mbuf *m; 131 132 nmp->nm_so = (struct socket *)0; 133 if (error = socreate(mtod(nmp->nm_nam, struct sockaddr *)->sa_family, 134 &nmp->nm_so, nmp->nm_sotype, nmp->nm_soproto)) 135 goto bad; 136 so = nmp->nm_so; 137 nmp->nm_soflags = so->so_proto->pr_flags; 138 139 /* 140 * Protocols that do not require connections may be optionally left 141 * unconnected for servers that reply from a port other than NFS_PORT. 142 */ 143 if (nmp->nm_flag & NFSMNT_NOCONN) { 144 if (nmp->nm_soflags & PR_CONNREQUIRED) { 145 error = ENOTCONN; 146 goto bad; 147 } 148 } else { 149 if (error = soconnect(so, nmp->nm_nam)) 150 goto bad; 151 152 /* 153 * Wait for the connection to complete. Cribbed from the 154 * connect system call but with the wait at negative prio. 155 */ 156 s = splnet(); 157 while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) 158 (void) tsleep((caddr_t)&so->so_timeo, PSOCK, "nfscon", 0); 159 splx(s); 160 if (so->so_error) { 161 error = so->so_error; 162 goto bad; 163 } 164 } 165 if (nmp->nm_sotype == SOCK_DGRAM) { 166 if (nmp->nm_flag & (NFSMNT_SOFT | NFSMNT_SPONGY | NFSMNT_INT)) { 167 so->so_rcv.sb_timeo = (5 * hz); 168 so->so_snd.sb_timeo = (5 * hz); 169 } else { 170 so->so_rcv.sb_timeo = 0; 171 so->so_snd.sb_timeo = 0; 172 } 173 if (error = soreserve(so, nmp->nm_wsize + NFS_MAXPKTHDR, 174 nmp->nm_rsize + NFS_MAXPKTHDR)) 175 goto bad; 176 } else { 177 if (nmp->nm_flag & (NFSMNT_SOFT | NFSMNT_SPONGY | NFSMNT_INT)) { 178 so->so_rcv.sb_timeo = (5 * hz); 179 so->so_snd.sb_timeo = (5 * hz); 180 } else { 181 so->so_rcv.sb_timeo = 0; 182 so->so_snd.sb_timeo = 0; 183 } 184 if (so->so_proto->pr_flags & PR_CONNREQUIRED) { 185 MGET(m, M_WAIT, MT_SOOPTS); 186 *mtod(m, int *) = 1; 187 m->m_len = sizeof(int); 188 sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m); 189 } 190 if (so->so_proto->pr_domain->dom_family == AF_INET && 191 so->so_proto->pr_protocol == IPPROTO_TCP && 192 nfs_tcpnodelay) { 193 MGET(m, M_WAIT, MT_SOOPTS); 194 *mtod(m, int *) = 1; 195 m->m_len = sizeof(int); 196 sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m); 197 } 198 if (error = soreserve(so, 199 nmp->nm_wsize + NFS_MAXPKTHDR + sizeof(u_long), 200 nmp->nm_rsize + NFS_MAXPKTHDR + sizeof(u_long))) 201 goto bad; 202 } 203 so->so_rcv.sb_flags |= SB_NOINTR; 204 so->so_snd.sb_flags |= SB_NOINTR; 205 206 /* Initialize other non-zero congestion variables */ 207 nmp->nm_rto = NFS_TIMEO; 208 nmp->nm_window = 2; /* Initial send window */ 209 nmp->nm_ssthresh = NFS_MAXWINDOW; /* Slowstart threshold */ 210 nmp->nm_rttvar = nmp->nm_rto << 1; 211 nmp->nm_sent = 0; 212 nmp->nm_currexmit = 0; 213 return (0); 214 215 bad: 216 nfs_disconnect(nmp); 217 return (error); 218 } 219 220 /* 221 * Reconnect routine: 222 * Called when a connection is broken on a reliable protocol. 223 * - clean up the old socket 224 * - nfs_connect() again 225 * - set R_MUSTRESEND for all outstanding requests on mount point 226 * If this fails the mount point is DEAD! 227 * nb: Must be called with the nfs_solock() set on the mount point. 228 */ 229 nfs_reconnect(rep, nmp) 230 register struct nfsreq *rep; 231 register struct nfsmount *nmp; 232 { 233 register struct nfsreq *rp; 234 int error; 235 236 if (rep->r_procp) 237 tprintf(rep->r_procp->p_session, 238 "Nfs server %s, trying reconnect\n", 239 nmp->nm_mountp->mnt_stat.f_mntfromname); 240 else 241 tprintf(NULL, "Nfs server %s, trying a reconnect\n", 242 nmp->nm_mountp->mnt_stat.f_mntfromname); 243 while (error = nfs_connect(nmp)) { 244 #ifdef lint 245 error = error; 246 #endif /* lint */ 247 if ((nmp->nm_flag & NFSMNT_INT) && nfs_sigintr(rep->r_procp)) 248 return (EINTR); 249 (void) tsleep((caddr_t)&lbolt, PSOCK, "nfscon", 0); 250 } 251 if (rep->r_procp) 252 tprintf(rep->r_procp->p_session, 253 "Nfs server %s, reconnected\n", 254 nmp->nm_mountp->mnt_stat.f_mntfromname); 255 else 256 tprintf(NULL, "Nfs server %s, reconnected\n", 257 nmp->nm_mountp->mnt_stat.f_mntfromname); 258 259 /* 260 * Loop through outstanding request list and fix up all requests 261 * on old socket. 262 */ 263 rp = nfsreqh.r_next; 264 while (rp != &nfsreqh) { 265 if (rp->r_nmp == nmp) 266 rp->r_flags |= R_MUSTRESEND; 267 rp = rp->r_next; 268 } 269 return (0); 270 } 271 272 /* 273 * NFS disconnect. Clean up and unlink. 274 */ 275 void 276 nfs_disconnect(nmp) 277 register struct nfsmount *nmp; 278 { 279 register struct socket *so; 280 281 if (nmp->nm_so) { 282 so = nmp->nm_so; 283 nmp->nm_so = (struct socket *)0; 284 soshutdown(so, 2); 285 soclose(so); 286 } 287 } 288 289 /* 290 * This is the nfs send routine. For connection based socket types, it 291 * must be called with an nfs_solock() on the socket. 292 * "rep == NULL" indicates that it has been called from a server. 293 */ 294 nfs_send(so, nam, top, rep) 295 register struct socket *so; 296 struct mbuf *nam; 297 register struct mbuf *top; 298 struct nfsreq *rep; 299 { 300 struct mbuf *sendnam; 301 int error, soflags; 302 303 if (rep) { 304 if (rep->r_flags & R_SOFTTERM) { 305 m_freem(top); 306 return (EINTR); 307 } 308 if (rep->r_nmp->nm_so == NULL && 309 (error = nfs_reconnect(rep, rep->r_nmp))) 310 return (error); 311 rep->r_flags &= ~R_MUSTRESEND; 312 so = rep->r_nmp->nm_so; 313 soflags = rep->r_nmp->nm_soflags; 314 } else 315 soflags = so->so_proto->pr_flags; 316 if ((soflags & PR_CONNREQUIRED) || (so->so_state & SS_ISCONNECTED)) 317 sendnam = (struct mbuf *)0; 318 else 319 sendnam = nam; 320 321 error = sosend(so, sendnam, (struct uio *)0, top, 322 (struct mbuf *)0, 0); 323 if (error == EWOULDBLOCK && rep) { 324 if (rep->r_flags & R_SOFTTERM) 325 error = EINTR; 326 else { 327 rep->r_flags |= R_MUSTRESEND; 328 error = 0; 329 } 330 } 331 /* 332 * Ignore socket errors?? 333 */ 334 if (error && error != EINTR && error != ERESTART) 335 error = 0; 336 return (error); 337 } 338 339 /* 340 * Receive a Sun RPC Request/Reply. For SOCK_DGRAM, the work is all 341 * done by soreceive(), but for SOCK_STREAM we must deal with the Record 342 * Mark and consolidate the data into a new mbuf list. 343 * nb: Sometimes TCP passes the data up to soreceive() in long lists of 344 * small mbufs. 345 * For SOCK_STREAM we must be very careful to read an entire record once 346 * we have read any of it, even if the system call has been interrupted. 347 */ 348 nfs_receive(so, aname, mp, rep) 349 register struct socket *so; 350 struct mbuf **aname; 351 struct mbuf **mp; 352 register struct nfsreq *rep; 353 { 354 struct uio auio; 355 struct iovec aio; 356 register struct mbuf *m; 357 struct mbuf *m2, *mnew, **mbp; 358 caddr_t fcp, tcp; 359 u_long len; 360 struct mbuf **getnam; 361 int error, siz, mlen, soflags, rcvflg = MSG_WAITALL; 362 363 /* 364 * Set up arguments for soreceive() 365 */ 366 *mp = (struct mbuf *)0; 367 *aname = (struct mbuf *)0; 368 if (rep) 369 soflags = rep->r_nmp->nm_soflags; 370 else 371 soflags = so->so_proto->pr_flags; 372 373 /* 374 * For reliable protocols, lock against other senders/receivers 375 * in case a reconnect is necessary. 376 * For SOCK_STREAM, first get the Record Mark to find out how much 377 * more there is to get. 378 * We must lock the socket against other receivers 379 * until we have an entire rpc request/reply. 380 */ 381 if (soflags & PR_CONNREQUIRED) { 382 tryagain: 383 /* 384 * Check for fatal errors and resending request. 385 */ 386 if (rep) { 387 /* 388 * Ugh: If a reconnect attempt just happened, nm_so 389 * would have changed. NULL indicates a failed 390 * attempt that has essentially shut down this 391 * mount point. 392 */ 393 if (rep->r_mrep || (so = rep->r_nmp->nm_so) == NULL || 394 (rep->r_flags & R_SOFTTERM)) 395 return (EINTR); 396 while (rep->r_flags & R_MUSTRESEND) { 397 m = m_copym(rep->r_mreq, 0, M_COPYALL, M_WAIT); 398 nfsstats.rpcretries++; 399 if (error = nfs_send(so, rep->r_nmp->nm_nam, m, 400 rep)) 401 goto errout; 402 } 403 } 404 if ((soflags & PR_ATOMIC) == 0) { 405 aio.iov_base = (caddr_t) &len; 406 aio.iov_len = sizeof(u_long); 407 auio.uio_iov = &aio; 408 auio.uio_iovcnt = 1; 409 auio.uio_segflg = UIO_SYSSPACE; 410 auio.uio_rw = UIO_READ; 411 auio.uio_offset = 0; 412 auio.uio_resid = sizeof(u_long); 413 do { 414 error = soreceive(so, (struct mbuf **)0, &auio, 415 (struct mbuf **)0, (struct mbuf **)0, &rcvflg); 416 if (error == EWOULDBLOCK && rep) { 417 if (rep->r_flags & R_SOFTTERM) 418 return (EINTR); 419 if (rep->r_flags & R_MUSTRESEND) 420 goto tryagain; 421 } 422 } while (error == EWOULDBLOCK); 423 if (!error && auio.uio_resid > 0) 424 error = EPIPE; 425 if (error) 426 goto errout; 427 len = ntohl(len) & ~0x80000000; 428 /* 429 * This is SERIOUS! We are out of sync with the sender 430 * and forcing a disconnect/reconnect is all I can do. 431 */ 432 if (len > NFS_MAXPACKET) { 433 error = EFBIG; 434 goto errout; 435 } 436 auio.uio_resid = len; 437 do { 438 error = soreceive(so, (struct mbuf **)0, 439 &auio, mp, (struct mbuf **)0, &rcvflg); 440 } while (error == EWOULDBLOCK || error == EINTR || 441 error == ERESTART); 442 if (!error && auio.uio_resid > 0) 443 error = EPIPE; 444 } else { 445 auio.uio_resid = len = 1000000; /* Anything Big */ 446 do { 447 error = soreceive(so, (struct mbuf **)0, 448 &auio, mp, (struct mbuf **)0, &rcvflg); 449 if (error == EWOULDBLOCK && rep) { 450 if (rep->r_flags & R_SOFTTERM) 451 return (EINTR); 452 if (rep->r_flags & R_MUSTRESEND) 453 goto tryagain; 454 } 455 } while (error == EWOULDBLOCK); 456 if (!error && *mp == NULL) 457 error = EPIPE; 458 len -= auio.uio_resid; 459 } 460 errout: 461 if (error && rep && error != EINTR && error != ERESTART) { 462 m_freem(*mp); 463 *mp = (struct mbuf *)0; 464 nfs_disconnect(rep->r_nmp); 465 error = nfs_reconnect(rep, rep->r_nmp); 466 if (!error) 467 goto tryagain; 468 } 469 } else { 470 if (so->so_state & SS_ISCONNECTED) 471 getnam = (struct mbuf **)0; 472 else 473 getnam = aname; 474 auio.uio_resid = len = 1000000; 475 do { 476 error = soreceive(so, getnam, &auio, mp, 477 (struct mbuf **)0, &rcvflg); 478 if (error == EWOULDBLOCK && rep && 479 (rep->r_flags & R_SOFTTERM)) 480 return (EINTR); 481 } while (error == EWOULDBLOCK); 482 len -= auio.uio_resid; 483 } 484 if (error) { 485 m_freem(*mp); 486 *mp = (struct mbuf *)0; 487 } 488 /* 489 * Search for any mbufs that are not a multiple of 4 bytes long. 490 * These could cause pointer alignment problems, so copy them to 491 * well aligned mbufs. 492 */ 493 m = *mp; 494 mbp = mp; 495 while (m) { 496 /* 497 * All this for something that may never happen. 498 */ 499 if (m->m_next && (m->m_len & 0x3)) { 500 printf("nfs_rcv odd length!\n"); 501 mlen = 0; 502 while (m) { 503 fcp = mtod(m, caddr_t); 504 while (m->m_len > 0) { 505 if (mlen == 0) { 506 MGET(m2, M_WAIT, MT_DATA); 507 if (len >= MINCLSIZE) 508 MCLGET(m2, M_WAIT); 509 m2->m_len = 0; 510 mlen = M_TRAILINGSPACE(m2); 511 tcp = mtod(m2, caddr_t); 512 *mbp = m2; 513 mbp = &m2->m_next; 514 } 515 siz = MIN(mlen, m->m_len); 516 bcopy(fcp, tcp, siz); 517 m2->m_len += siz; 518 mlen -= siz; 519 len -= siz; 520 tcp += siz; 521 m->m_len -= siz; 522 fcp += siz; 523 } 524 MFREE(m, mnew); 525 m = mnew; 526 } 527 break; 528 } 529 len -= m->m_len; 530 mbp = &m->m_next; 531 m = m->m_next; 532 } 533 return (error); 534 } 535 536 /* 537 * Implement receipt of reply on a socket. 538 * We must search through the list of received datagrams matching them 539 * with outstanding requests using the xid, until ours is found. 540 */ 541 /* ARGSUSED */ 542 nfs_reply(nmp, myrep) 543 struct nfsmount *nmp; 544 struct nfsreq *myrep; 545 { 546 register struct mbuf *m; 547 register struct nfsreq *rep; 548 register int error = 0; 549 u_long rxid; 550 struct mbuf *mp, *nam; 551 char *cp; 552 int cnt, xfer; 553 554 /* 555 * Loop around until we get our own reply 556 */ 557 for (;;) { 558 /* 559 * Lock against other receivers so that I don't get stuck in 560 * sbwait() after someone else has received my reply for me. 561 * Also necessary for connection based protocols to avoid 562 * race conditions during a reconnect. 563 */ 564 nfs_solock(&nmp->nm_flag); 565 /* Already received, bye bye */ 566 if (myrep->r_mrep != NULL) { 567 nfs_sounlock(&nmp->nm_flag); 568 return (0); 569 } 570 /* 571 * Get the next Rpc reply off the socket 572 */ 573 if (error = nfs_receive(nmp->nm_so, &nam, &mp, myrep)) { 574 nfs_sounlock(&nmp->nm_flag); 575 576 /* 577 * Ignore routing errors on connectionless protocols?? 578 */ 579 if (NFSIGNORE_SOERROR(nmp->nm_soflags, error)) { 580 nmp->nm_so->so_error = 0; 581 continue; 582 } 583 584 /* 585 * Otherwise cleanup and return a fatal error. 586 */ 587 if (myrep->r_flags & R_TIMING) { 588 myrep->r_flags &= ~R_TIMING; 589 nmp->nm_rtt = -1; 590 } 591 if (myrep->r_flags & R_SENT) { 592 myrep->r_flags &= ~R_SENT; 593 nmp->nm_sent--; 594 } 595 return (error); 596 } 597 598 /* 599 * Get the xid and check that it is an rpc reply 600 */ 601 m = mp; 602 while (m && m->m_len == 0) 603 m = m->m_next; 604 if (m == NULL) { 605 nfsstats.rpcinvalid++; 606 m_freem(mp); 607 nfs_sounlock(&nmp->nm_flag); 608 continue; 609 } 610 bcopy(mtod(m, caddr_t), (caddr_t)&rxid, NFSX_UNSIGNED); 611 /* 612 * Loop through the request list to match up the reply 613 * Iff no match, just drop the datagram 614 */ 615 m = mp; 616 rep = nfsreqh.r_next; 617 while (rep != &nfsreqh) { 618 if (rep->r_mrep == NULL && rxid == rep->r_xid) { 619 /* Found it.. */ 620 rep->r_mrep = m; 621 /* 622 * Update timing 623 */ 624 if (rep->r_flags & R_TIMING) { 625 nfs_updatetimer(rep->r_nmp); 626 rep->r_flags &= ~R_TIMING; 627 rep->r_nmp->nm_rtt = -1; 628 } 629 if (rep->r_flags & R_SENT) { 630 rep->r_flags &= ~R_SENT; 631 rep->r_nmp->nm_sent--; 632 } 633 break; 634 } 635 rep = rep->r_next; 636 } 637 nfs_sounlock(&nmp->nm_flag); 638 if (nam) 639 m_freem(nam); 640 /* 641 * If not matched to a request, drop it. 642 * If it's mine, get out. 643 */ 644 if (rep == &nfsreqh) { 645 nfsstats.rpcunexpected++; 646 m_freem(m); 647 } else if (rep == myrep) 648 return (0); 649 } 650 } 651 652 /* 653 * nfs_request - goes something like this 654 * - fill in request struct 655 * - links it into list 656 * - calls nfs_send() for first transmit 657 * - calls nfs_receive() to get reply 658 * - break down rpc header and return with nfs reply pointed to 659 * by mrep or error 660 * nb: always frees up mreq mbuf list 661 */ 662 nfs_request(vp, mreq, xid, procnum, procp, tryhard, mp, mrp, mdp, dposp) 663 struct vnode *vp; 664 struct mbuf *mreq; 665 u_long xid; 666 int procnum; 667 struct proc *procp; 668 int tryhard; 669 struct mount *mp; 670 struct mbuf **mrp; 671 struct mbuf **mdp; 672 caddr_t *dposp; 673 { 674 register struct mbuf *m, *mrep; 675 register struct nfsreq *rep; 676 register u_long *p; 677 register int len; 678 struct nfsmount *nmp; 679 struct mbuf *md; 680 struct nfsreq *reph; 681 caddr_t dpos; 682 char *cp2; 683 int t1; 684 int s, compressed; 685 int error = 0; 686 687 nmp = VFSTONFS(mp); 688 m = mreq; 689 MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK); 690 rep->r_xid = xid; 691 rep->r_nmp = nmp; 692 rep->r_vp = vp; 693 rep->r_procp = procp; 694 if ((nmp->nm_flag & NFSMNT_SOFT) || 695 ((nmp->nm_flag & NFSMNT_SPONGY) && !tryhard)) 696 rep->r_retry = nmp->nm_retry; 697 else 698 rep->r_retry = NFS_MAXREXMIT + 1; /* past clip limit */ 699 rep->r_flags = rep->r_rexmit = 0; 700 /* 701 * Three cases: 702 * - non-idempotent requests on SOCK_DGRAM use NFS_MINIDEMTIMEO 703 * - idempotent requests on SOCK_DGRAM use 0 704 * - Reliable transports, NFS_RELIABLETIMEO 705 * Timeouts are still done on reliable transports to ensure detection 706 * of excessive connection delay. 707 */ 708 if (nmp->nm_sotype != SOCK_DGRAM) 709 rep->r_timerinit = -NFS_RELIABLETIMEO; 710 else if (nonidempotent[procnum]) 711 rep->r_timerinit = -NFS_MINIDEMTIMEO; 712 else 713 rep->r_timerinit = 0; 714 rep->r_timer = rep->r_timerinit; 715 rep->r_mrep = NULL; 716 len = 0; 717 while (m) { 718 len += m->m_len; 719 m = m->m_next; 720 } 721 mreq->m_pkthdr.len = len; 722 mreq->m_pkthdr.rcvif = (struct ifnet *)0; 723 compressed = 0; 724 m = mreq; 725 if ((nmp->nm_flag & NFSMNT_COMPRESS) && compressrequest[procnum]) { 726 mreq = nfs_compress(mreq); 727 if (mreq != m) { 728 len = mreq->m_pkthdr.len; 729 compressed++; 730 } 731 } 732 /* 733 * For non-atomic protocols, insert a Sun RPC Record Mark. 734 */ 735 if ((nmp->nm_soflags & PR_ATOMIC) == 0) { 736 M_PREPEND(mreq, sizeof(u_long), M_WAIT); 737 *mtod(mreq, u_long *) = htonl(0x80000000 | len); 738 } 739 rep->r_mreq = mreq; 740 741 /* 742 * Do the client side RPC. 743 */ 744 nfsstats.rpcrequests++; 745 /* 746 * Chain request into list of outstanding requests. Be sure 747 * to put it LAST so timer finds oldest requests first. 748 */ 749 s = splnet(); 750 reph = &nfsreqh; 751 reph->r_prev->r_next = rep; 752 rep->r_prev = reph->r_prev; 753 reph->r_prev = rep; 754 rep->r_next = reph; 755 /* 756 * If backing off another request or avoiding congestion, don't 757 * send this one now but let timer do it. If not timing a request, 758 * do it now. 759 */ 760 if (nmp->nm_sent <= 0 || nmp->nm_sotype != SOCK_DGRAM || 761 (nmp->nm_currexmit == 0 && nmp->nm_sent < nmp->nm_window)) { 762 nmp->nm_sent++; 763 rep->r_flags |= R_SENT; 764 if (nmp->nm_rtt == -1) { 765 nmp->nm_rtt = 0; 766 rep->r_flags |= R_TIMING; 767 } 768 splx(s); 769 m = m_copym(mreq, 0, M_COPYALL, M_WAIT); 770 if (nmp->nm_soflags & PR_CONNREQUIRED) 771 nfs_solock(&nmp->nm_flag); 772 error = nfs_send(nmp->nm_so, nmp->nm_nam, m, rep); 773 if (nmp->nm_soflags & PR_CONNREQUIRED) 774 nfs_sounlock(&nmp->nm_flag); 775 if (error && NFSIGNORE_SOERROR(nmp->nm_soflags, error)) 776 nmp->nm_so->so_error = error = 0; 777 } else 778 splx(s); 779 780 /* 781 * Wait for the reply from our send or the timer's. 782 */ 783 if (!error) 784 error = nfs_reply(nmp, rep); 785 786 /* 787 * RPC done, unlink the request. 788 */ 789 s = splnet(); 790 rep->r_prev->r_next = rep->r_next; 791 rep->r_next->r_prev = rep->r_prev; 792 splx(s); 793 794 /* 795 * If there was a successful reply and a tprintf msg. 796 * tprintf a response. 797 */ 798 if (!error && (rep->r_flags & R_TPRINTFMSG)) { 799 if (rep->r_procp) 800 tprintf(rep->r_procp->p_session, 801 "Nfs server %s, is alive again\n", 802 rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname); 803 else 804 tprintf(NULL, "Nfs server %s, is alive again\n", 805 rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname); 806 } 807 m_freem(rep->r_mreq); 808 mrep = rep->r_mrep; 809 FREE((caddr_t)rep, M_NFSREQ); 810 if (error) 811 return (error); 812 813 if (compressed) 814 mrep = nfs_uncompress(mrep); 815 md = mrep; 816 /* 817 * break down the rpc header and check if ok 818 */ 819 dpos = mtod(md, caddr_t); 820 nfsm_disect(p, u_long *, 5*NFSX_UNSIGNED); 821 p += 2; 822 if (*p++ == rpc_msgdenied) { 823 if (*p == rpc_mismatch) 824 error = EOPNOTSUPP; 825 else 826 error = EACCES; 827 m_freem(mrep); 828 return (error); 829 } 830 /* 831 * skip over the auth_verf, someday we may want to cache auth_short's 832 * for nfs_reqhead(), but for now just dump it 833 */ 834 if (*++p != 0) { 835 len = nfsm_rndup(fxdr_unsigned(long, *p)); 836 nfsm_adv(len); 837 } 838 nfsm_disect(p, u_long *, NFSX_UNSIGNED); 839 /* 0 == ok */ 840 if (*p == 0) { 841 nfsm_disect(p, u_long *, NFSX_UNSIGNED); 842 if (*p != 0) { 843 error = fxdr_unsigned(int, *p); 844 m_freem(mrep); 845 return (error); 846 } 847 *mrp = mrep; 848 *mdp = md; 849 *dposp = dpos; 850 return (0); 851 } 852 m_freem(mrep); 853 return (EPROTONOSUPPORT); 854 nfsmout: 855 return (error); 856 } 857 858 /* 859 * Get a request for the server main loop 860 * - receive a request via. nfs_soreceive() 861 * - verify it 862 * - fill in the cred struct. 863 */ 864 nfs_getreq(so, prog, vers, maxproc, nam, mrp, mdp, dposp, retxid, procnum, cr, 865 msk, mtch, wascomp) 866 struct socket *so; 867 u_long prog; 868 u_long vers; 869 int maxproc; 870 struct mbuf **nam; 871 struct mbuf **mrp; 872 struct mbuf **mdp; 873 caddr_t *dposp; 874 u_long *retxid; 875 u_long *procnum; 876 register struct ucred *cr; 877 struct mbuf *msk, *mtch; 878 int *wascomp; 879 { 880 register int i; 881 register u_long *p; 882 register long t1; 883 caddr_t dpos, cp2; 884 int error = 0; 885 struct mbuf *mrep, *md; 886 int len; 887 888 if (so->so_proto->pr_flags & PR_CONNREQUIRED) { 889 error = nfs_receive(so, nam, &mrep, (struct nfsreq *)0); 890 } else { 891 mrep = (struct mbuf *)0; 892 do { 893 if (mrep) { 894 m_freem(*nam); 895 m_freem(mrep); 896 } 897 error = nfs_receive(so, nam, &mrep, (struct nfsreq *)0); 898 } while (!error && nfs_badnam(*nam, msk, mtch)); 899 } 900 if (error) 901 return (error); 902 md = mrep; 903 mrep = nfs_uncompress(mrep); 904 if (mrep != md) { 905 *wascomp = 1; 906 md = mrep; 907 } else 908 *wascomp = 0; 909 dpos = mtod(mrep, caddr_t); 910 nfsm_disect(p, u_long *, 10*NFSX_UNSIGNED); 911 *retxid = *p++; 912 if (*p++ != rpc_call) { 913 m_freem(mrep); 914 return (ERPCMISMATCH); 915 } 916 if (*p++ != rpc_vers) { 917 m_freem(mrep); 918 return (ERPCMISMATCH); 919 } 920 if (*p++ != prog) { 921 m_freem(mrep); 922 return (EPROGUNAVAIL); 923 } 924 if (*p++ != vers) { 925 m_freem(mrep); 926 return (EPROGMISMATCH); 927 } 928 *procnum = fxdr_unsigned(u_long, *p++); 929 if (*procnum == NFSPROC_NULL) { 930 *mrp = mrep; 931 return (0); 932 } 933 if (*procnum > maxproc || *p++ != rpc_auth_unix) { 934 m_freem(mrep); 935 return (EPROCUNAVAIL); 936 } 937 len = fxdr_unsigned(int, *p++); 938 if (len < 0 || len > RPCAUTH_MAXSIZ) { 939 m_freem(mrep); 940 return (EBADRPC); 941 } 942 len = fxdr_unsigned(int, *++p); 943 if (len < 0 || len > NFS_MAXNAMLEN) { 944 m_freem(mrep); 945 return (EBADRPC); 946 } 947 nfsm_adv(nfsm_rndup(len)); 948 nfsm_disect(p, u_long *, 3*NFSX_UNSIGNED); 949 cr->cr_uid = fxdr_unsigned(uid_t, *p++); 950 cr->cr_gid = fxdr_unsigned(gid_t, *p++); 951 len = fxdr_unsigned(int, *p); 952 if (len < 0 || len > RPCAUTH_UNIXGIDS) { 953 m_freem(mrep); 954 return (EBADRPC); 955 } 956 nfsm_disect(p, u_long *, (len + 2)*NFSX_UNSIGNED); 957 for (i = 1; i <= len; i++) 958 if (i < NGROUPS) 959 cr->cr_groups[i] = fxdr_unsigned(gid_t, *p++); 960 else 961 p++; 962 cr->cr_ngroups = (len >= NGROUPS) ? NGROUPS : (len + 1); 963 /* 964 * Do we have any use for the verifier. 965 * According to the "Remote Procedure Call Protocol Spec." it 966 * should be AUTH_NULL, but some clients make it AUTH_UNIX? 967 * For now, just skip over it 968 */ 969 len = fxdr_unsigned(int, *++p); 970 if (len < 0 || len > RPCAUTH_MAXSIZ) { 971 m_freem(mrep); 972 return (EBADRPC); 973 } 974 if (len > 0) 975 nfsm_adv(nfsm_rndup(len)); 976 *mrp = mrep; 977 *mdp = md; 978 *dposp = dpos; 979 return (0); 980 nfsmout: 981 return (error); 982 } 983 984 /* 985 * Generate the rpc reply header 986 * siz arg. is used to decide if adding a cluster is worthwhile 987 */ 988 nfs_rephead(siz, retxid, err, mrq, mbp, bposp) 989 int siz; 990 u_long retxid; 991 int err; 992 struct mbuf **mrq; 993 struct mbuf **mbp; 994 caddr_t *bposp; 995 { 996 register u_long *p; 997 register long t1; 998 caddr_t bpos; 999 struct mbuf *mreq, *mb, *mb2; 1000 1001 NFSMGETHDR(mreq); 1002 mb = mreq; 1003 if ((siz+RPC_REPLYSIZ) > MHLEN) 1004 MCLGET(mreq, M_WAIT); 1005 p = mtod(mreq, u_long *); 1006 mreq->m_len = 6*NFSX_UNSIGNED; 1007 bpos = ((caddr_t)p)+mreq->m_len; 1008 *p++ = retxid; 1009 *p++ = rpc_reply; 1010 if (err == ERPCMISMATCH) { 1011 *p++ = rpc_msgdenied; 1012 *p++ = rpc_mismatch; 1013 *p++ = txdr_unsigned(2); 1014 *p = txdr_unsigned(2); 1015 } else { 1016 *p++ = rpc_msgaccepted; 1017 *p++ = 0; 1018 *p++ = 0; 1019 switch (err) { 1020 case EPROGUNAVAIL: 1021 *p = txdr_unsigned(RPC_PROGUNAVAIL); 1022 break; 1023 case EPROGMISMATCH: 1024 *p = txdr_unsigned(RPC_PROGMISMATCH); 1025 nfsm_build(p, u_long *, 2*NFSX_UNSIGNED); 1026 *p++ = txdr_unsigned(2); 1027 *p = txdr_unsigned(2); /* someday 3 */ 1028 break; 1029 case EPROCUNAVAIL: 1030 *p = txdr_unsigned(RPC_PROCUNAVAIL); 1031 break; 1032 default: 1033 *p = 0; 1034 if (err != VNOVAL) { 1035 nfsm_build(p, u_long *, NFSX_UNSIGNED); 1036 *p = txdr_unsigned(err); 1037 } 1038 break; 1039 }; 1040 } 1041 *mrq = mreq; 1042 *mbp = mb; 1043 *bposp = bpos; 1044 if (err != 0 && err != VNOVAL) 1045 nfsstats.srvrpc_errs++; 1046 return (0); 1047 } 1048 1049 /* 1050 * Nfs timer routine 1051 * Scan the nfsreq list and retranmit any requests that have timed out 1052 * To avoid retransmission attempts on STREAM sockets (in the future) make 1053 * sure to set the r_retry field to 0 (implies nm_retry == 0). 1054 */ 1055 nfs_timer() 1056 { 1057 register struct nfsreq *rep; 1058 register struct mbuf *m; 1059 register struct socket *so; 1060 register struct nfsmount *nmp; 1061 int s, error; 1062 1063 s = splnet(); 1064 for (rep = nfsreqh.r_next; rep != &nfsreqh; rep = rep->r_next) { 1065 nmp = rep->r_nmp; 1066 if (rep->r_mrep || (rep->r_flags & R_SOFTTERM) || 1067 (so = nmp->nm_so) == NULL) 1068 continue; 1069 if ((nmp->nm_flag & NFSMNT_INT) && nfs_sigintr(rep->r_procp)) { 1070 rep->r_flags |= R_SOFTTERM; 1071 continue; 1072 } 1073 if (rep->r_flags & R_TIMING) /* update rtt in mount */ 1074 nmp->nm_rtt++; 1075 /* If not timed out */ 1076 if (++rep->r_timer < nmp->nm_rto) 1077 continue; 1078 /* Do backoff and save new timeout in mount */ 1079 if (rep->r_flags & R_TIMING) { 1080 nfs_backofftimer(nmp); 1081 rep->r_flags &= ~R_TIMING; 1082 nmp->nm_rtt = -1; 1083 } 1084 if (rep->r_flags & R_SENT) { 1085 rep->r_flags &= ~R_SENT; 1086 nmp->nm_sent--; 1087 } 1088 1089 /* 1090 * Check for too many retries on soft mount. 1091 * nb: For hard mounts, r_retry == NFS_MAXREXMIT+1 1092 */ 1093 if (++rep->r_rexmit > NFS_MAXREXMIT) 1094 rep->r_rexmit = NFS_MAXREXMIT; 1095 1096 /* 1097 * Check for server not responding 1098 */ 1099 if ((rep->r_flags & R_TPRINTFMSG) == 0 && 1100 rep->r_rexmit > NFS_FISHY) { 1101 if (rep->r_procp && rep->r_procp->p_session) 1102 tprintf(rep->r_procp->p_session, 1103 "Nfs server %s, not responding\n", 1104 nmp->nm_mountp->mnt_stat.f_mntfromname); 1105 else 1106 tprintf(NULL, 1107 "Nfs server %s, not responding\n", 1108 nmp->nm_mountp->mnt_stat.f_mntfromname); 1109 rep->r_flags |= R_TPRINTFMSG; 1110 } 1111 if (rep->r_rexmit >= rep->r_retry) { /* too many */ 1112 nfsstats.rpctimeouts++; 1113 rep->r_flags |= R_SOFTTERM; 1114 continue; 1115 } 1116 if (nmp->nm_sotype != SOCK_DGRAM) 1117 continue; 1118 1119 /* 1120 * If there is enough space and the window allows.. 1121 * Resend it 1122 */ 1123 if (sbspace(&so->so_snd) >= rep->r_mreq->m_pkthdr.len && 1124 nmp->nm_sent < nmp->nm_window && 1125 (m = m_copym(rep->r_mreq, 0, M_COPYALL, M_DONTWAIT))){ 1126 nfsstats.rpcretries++; 1127 if ((nmp->nm_flag & NFSMNT_NOCONN) == 0) 1128 error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m, 1129 (caddr_t)0, (struct mbuf *)0, (struct mbuf *)0); 1130 else 1131 error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m, 1132 nmp->nm_nam, (struct mbuf *)0, (struct mbuf *)0); 1133 if (error) { 1134 if (NFSIGNORE_SOERROR(nmp->nm_soflags, error)) 1135 so->so_error = 0; 1136 } else { 1137 /* 1138 * We need to time the request even though we 1139 * are retransmitting. 1140 */ 1141 nmp->nm_rtt = 0; 1142 nmp->nm_sent++; 1143 rep->r_flags |= (R_SENT|R_TIMING); 1144 rep->r_timer = rep->r_timerinit; 1145 } 1146 } 1147 } 1148 splx(s); 1149 timeout(nfs_timer, (caddr_t)0, hz/NFS_HZ); 1150 } 1151 1152 /* 1153 * NFS timer update and backoff. The "Jacobson/Karels/Karn" scheme is 1154 * used here. The timer state is held in the nfsmount structure and 1155 * a single request is used to clock the response. When successful 1156 * the rtt smoothing in nfs_updatetimer is used, when failed the backoff 1157 * is done by nfs_backofftimer. We also log failure messages in these 1158 * routines. 1159 * 1160 * Congestion variables are held in the nfshost structure which 1161 * is referenced by nfsmounts and shared per-server. This separation 1162 * makes it possible to do per-mount timing which allows varying disk 1163 * access times to be dealt with, while preserving a network oriented 1164 * congestion control scheme. 1165 * 1166 * The windowing implements the Jacobson/Karels slowstart algorithm 1167 * with adjusted scaling factors. We start with one request, then send 1168 * 4 more after each success until the ssthresh limit is reached, then 1169 * we increment at a rate proportional to the window. On failure, we 1170 * remember 3/4 the current window and clamp the send limit to 1. Note 1171 * ICMP source quench is not reflected in so->so_error so we ignore that 1172 * for now. 1173 * 1174 * NFS behaves much more like a transport protocol with these changes, 1175 * shedding the teenage pedal-to-the-metal tendencies of "other" 1176 * implementations. 1177 * 1178 * Timers and congestion avoidance by Tom Talpey, Open Software Foundation. 1179 */ 1180 1181 /* 1182 * The TCP algorithm was not forgiving enough. Because the NFS server 1183 * responds only after performing lookups/diskio/etc, we have to be 1184 * more prepared to accept a spiky variance. The TCP algorithm is: 1185 * TCP_RTO(nmp) ((((nmp)->nm_srtt >> 2) + (nmp)->nm_rttvar) >> 1) 1186 */ 1187 #define NFS_RTO(nmp) (((nmp)->nm_srtt >> 3) + (nmp)->nm_rttvar) 1188 1189 nfs_updatetimer(nmp) 1190 register struct nfsmount *nmp; 1191 { 1192 1193 /* If retransmitted, clear and return */ 1194 if (nmp->nm_rexmit || nmp->nm_currexmit) { 1195 nmp->nm_rexmit = nmp->nm_currexmit = 0; 1196 return; 1197 } 1198 /* If have a measurement, do smoothing */ 1199 if (nmp->nm_srtt) { 1200 register short delta; 1201 delta = nmp->nm_rtt - (nmp->nm_srtt >> 3); 1202 if ((nmp->nm_srtt += delta) <= 0) 1203 nmp->nm_srtt = 1; 1204 if (delta < 0) 1205 delta = -delta; 1206 delta -= (nmp->nm_rttvar >> 2); 1207 if ((nmp->nm_rttvar += delta) <= 0) 1208 nmp->nm_rttvar = 1; 1209 /* Else initialize */ 1210 } else { 1211 nmp->nm_rttvar = nmp->nm_rtt << 1; 1212 if (nmp->nm_rttvar == 0) nmp->nm_rttvar = 2; 1213 nmp->nm_srtt = nmp->nm_rttvar << 2; 1214 } 1215 /* Compute new Retransmission TimeOut and clip */ 1216 nmp->nm_rto = NFS_RTO(nmp); 1217 if (nmp->nm_rto < NFS_MINTIMEO) 1218 nmp->nm_rto = NFS_MINTIMEO; 1219 else if (nmp->nm_rto > NFS_MAXTIMEO) 1220 nmp->nm_rto = NFS_MAXTIMEO; 1221 1222 /* Update window estimate */ 1223 if (nmp->nm_window < nmp->nm_ssthresh) /* quickly */ 1224 nmp->nm_window += 4; 1225 else { /* slowly */ 1226 register long incr = ++nmp->nm_winext; 1227 incr = (incr * incr) / nmp->nm_window; 1228 if (incr > 0) { 1229 nmp->nm_winext = 0; 1230 ++nmp->nm_window; 1231 } 1232 } 1233 if (nmp->nm_window > NFS_MAXWINDOW) 1234 nmp->nm_window = NFS_MAXWINDOW; 1235 } 1236 1237 nfs_backofftimer(nmp) 1238 register struct nfsmount *nmp; 1239 { 1240 register unsigned long newrto; 1241 1242 /* Clip shift count */ 1243 if (++nmp->nm_rexmit > 8 * sizeof nmp->nm_rto) 1244 nmp->nm_rexmit = 8 * sizeof nmp->nm_rto; 1245 /* Back off RTO exponentially */ 1246 newrto = NFS_RTO(nmp); 1247 newrto <<= (nmp->nm_rexmit - 1); 1248 if (newrto == 0 || newrto > NFS_MAXTIMEO) 1249 newrto = NFS_MAXTIMEO; 1250 nmp->nm_rto = newrto; 1251 1252 /* If too many retries, message, assume a bogus RTT and re-measure */ 1253 if (nmp->nm_currexmit < nmp->nm_rexmit) { 1254 nmp->nm_currexmit = nmp->nm_rexmit; 1255 if (nmp->nm_currexmit >= nfsrexmtthresh) { 1256 if (nmp->nm_currexmit == nfsrexmtthresh) { 1257 nmp->nm_rttvar += (nmp->nm_srtt >> 2); 1258 nmp->nm_srtt = 0; 1259 } 1260 } 1261 } 1262 /* Close down window but remember this point (3/4 current) for later */ 1263 nmp->nm_ssthresh = ((nmp->nm_window << 1) + nmp->nm_window) >> 2; 1264 nmp->nm_window = 1; 1265 nmp->nm_winext = 0; 1266 } 1267 1268 /* 1269 * Test for a termination signal pending on procp. 1270 * This is used for NFSMNT_INT mounts. 1271 */ 1272 nfs_sigintr(p) 1273 register struct proc *p; 1274 { 1275 if (p && p->p_sig && (((p->p_sig &~ p->p_sigmask) &~ p->p_sigignore) & 1276 NFSINT_SIGMASK)) 1277 return (1); 1278 else 1279 return (0); 1280 } 1281 1282 /* 1283 * Lock a socket against others. 1284 * Necessary for STREAM sockets to ensure you get an entire rpc request/reply 1285 * and also to avoid race conditions between the processes with nfs requests 1286 * in progress when a reconnect is necessary. 1287 */ 1288 nfs_solock(flagp) 1289 register int *flagp; 1290 { 1291 1292 while (*flagp & NFSMNT_SCKLOCK) { 1293 *flagp |= NFSMNT_WANTSCK; 1294 (void) tsleep((caddr_t)flagp, PZERO-1, "nfsolck", 0); 1295 } 1296 *flagp |= NFSMNT_SCKLOCK; 1297 } 1298 1299 /* 1300 * Unlock the stream socket for others. 1301 */ 1302 nfs_sounlock(flagp) 1303 register int *flagp; 1304 { 1305 1306 if ((*flagp & NFSMNT_SCKLOCK) == 0) 1307 panic("nfs sounlock"); 1308 *flagp &= ~NFSMNT_SCKLOCK; 1309 if (*flagp & NFSMNT_WANTSCK) { 1310 *flagp &= ~NFSMNT_WANTSCK; 1311 wakeup((caddr_t)flagp); 1312 } 1313 } 1314 1315 /* 1316 * This function compares two net addresses by family and returns TRUE 1317 * if they are the same. 1318 * If there is any doubt, return FALSE. 1319 */ 1320 nfs_netaddr_match(nam1, nam2) 1321 struct mbuf *nam1, *nam2; 1322 { 1323 register struct sockaddr *saddr1, *saddr2; 1324 1325 saddr1 = mtod(nam1, struct sockaddr *); 1326 saddr2 = mtod(nam2, struct sockaddr *); 1327 if (saddr1->sa_family != saddr2->sa_family) 1328 return (0); 1329 1330 /* 1331 * Must do each address family separately since unused fields 1332 * are undefined values and not always zeroed. 1333 */ 1334 switch (saddr1->sa_family) { 1335 case AF_INET: 1336 if (((struct sockaddr_in *)saddr1)->sin_addr.s_addr == 1337 ((struct sockaddr_in *)saddr2)->sin_addr.s_addr) 1338 return (1); 1339 break; 1340 default: 1341 break; 1342 }; 1343 return (0); 1344 } 1345 1346 /* 1347 * Check the hostname fields for nfsd's mask and match fields. 1348 * By address family: 1349 * - Bitwise AND the mask with the host address field 1350 * - Compare for == with match 1351 * return TRUE if not equal 1352 */ 1353 nfs_badnam(nam, msk, mtch) 1354 register struct mbuf *nam, *msk, *mtch; 1355 { 1356 switch (mtod(nam, struct sockaddr *)->sa_family) { 1357 case AF_INET: 1358 return ((mtod(nam, struct sockaddr_in *)->sin_addr.s_addr & 1359 mtod(msk, struct sockaddr_in *)->sin_addr.s_addr) != 1360 mtod(mtch, struct sockaddr_in *)->sin_addr.s_addr); 1361 default: 1362 printf("nfs_badmatch, unknown sa_family\n"); 1363 return (0); 1364 }; 1365 } 1366