* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)nfs_socket.c 7.4 (Berkeley) %G%
* Socket operations for use by nfs (similar to uipc_socket.c, but never
* with copies to/from a uio vector)
* NB: For now, they only work for UDP datagram sockets.
* (Use on stream sockets would require some record boundary mark in the
* stream such as Sun's RM (Section 3.2 of the Sun RPC Message Protocol
* manual, in Networking on the Sun Workstation, Part #800-1324-03
* and different versions of send, receive and reply that do not assume
/* set lock on sockbuf sb, sleep at neg prio */
#define nfs_sblock(sb) { \
while ((sb)->sb_flags & SB_LOCK) { \
(sb)->sb_flags |= SB_WANT; \
sleep((caddr_t)&(sb)->sb_flags, PZERO-1); \
(sb)->sb_flags |= SB_LOCK; \
* External data, mostly RPC constants in XDR form
extern u_long rpc_reply
, rpc_msgdenied
, rpc_mismatch
, rpc_vers
, rpc_auth_unix
,
rpc_msgaccepted
, rpc_call
;
extern u_long nfs_prog
, nfs_vers
;
int (*nfsrv_procs
[NFS_NPROCS
])() = {
* This is a stripped down version of sosend() specific to
* udp/ip and uses the mbuf list provdied
nfs_udpsend(so
, nam
, top
, flags
, siz
)
register struct socket
*so
;
int error
= 0, s
, dontroute
;
(flags
& MSG_DONTROUTE
) && (so
->so_options
& SO_DONTROUTE
) == 0 &&
(so
->so_proto
->pr_flags
& PR_ATOMIC
);
#define snderr(errno) { error = errno; splx(s); goto release; }
if (so
->so_state
& SS_CANTSENDMORE
)
space
= sbspace(&so
->so_snd
);
so
->so_options
|= SO_DONTROUTE
;
error
= (*so
->so_proto
->pr_usrreq
)(so
,
top
, (caddr_t
)nam
, (struct mbuf
*)0, (struct mbuf
*)0);
so
->so_options
&= ~SO_DONTROUTE
;
* This is a stripped down udp specific version of soreceive()
nfs_udpreceive(so
, aname
, mp
)
register struct socket
*so
;
if (so
->so_rcv
.sb_cc
== 0) {
if (so
->so_state
& SS_CANTRCVMORE
)
nextrecord
= m
->m_nextpkt
;
if (m
->m_type
!= MT_SONAME
)
so
->so_rcv
.sb_mb
= m
->m_next
;
MFREE(m
, so
->so_rcv
.sb_mb
);
if (m
&& m
->m_type
== MT_RIGHTS
)
if (m
&& m
->m_type
== MT_CONTROL
) {
MFREE(m
, so
->so_rcv
.sb_mb
);
if (m
->m_type
!= MT_DATA
&& m
->m_type
!= MT_HEADER
)
m
= so
->so_rcv
.sb_mb
= m
->m_next
;
so
->so_rcv
.sb_mb
= nextrecord
;
so
->so_state
&= ~SS_RCVATMARK
; /* Necessary ?? */
struct nfsreq nfsreqh
= {
* Implement receipt of reply on a socket.
* We depend on the way that records are added to the sockbuf
* by sbappend*. In particular, each record (mbufs linked through m_next)
* must begin with an address, followed by optional MT_CONTROL mbuf
* and then zero or more mbufs of data.
* Although the sockbuf is locked, new data may still be appended,
* and thus we must maintain consistency of the sockbuf during that time.
* We must search through the list of received datagrams matching them
* with outstanding requests using the xid, until ours is found.
nfs_udpreply(so
, mntp
, myrep
)
register struct socket
*so
;
register struct nfsreq
*rep
;
register int error
= 0, s
;
struct sockaddr_in
*saddr
;
struct rpc_replyhead replyh
;
/* Already received, bye bye */
if (myrep
->r_mrep
!= NULL
) {
/* If a soft mount and we have run out of retries */
if (myrep
->r_retry
== 0 && myrep
->r_timer
== 0) {
if (so
->so_state
& SS_CANTRCVMORE
)
nextrecord
= m
->m_nextpkt
;
* Take off the address, check for rights and ditch any control
if (m
->m_type
!= MT_SONAME
)
panic("nfs reply SONAME");
saddr
= mtod(m
, struct sockaddr_in
*);
inaddr
= saddr
->sin_addr
.s_addr
;
MFREE(m
, so
->so_rcv
.sb_mb
);
if (m
&& m
->m_type
== MT_RIGHTS
)
panic("nfs reply RIGHTS");
if (m
&& m
->m_type
== MT_CONTROL
) {
MFREE(m
, so
->so_rcv
.sb_mb
);
m
->m_nextpkt
= nextrecord
;
so
->so_rcv
.sb_mb
= nextrecord
;
* Get the xid and check that it is an rpc reply
if (m
->m_len
>= 2*NFSX_UNSIGNED
)
bcopy(mtod(m
, caddr_t
), (caddr_t
)&replyh
, 2*NFSX_UNSIGNED
);
xfer
= (mp
->m_len
>= cnt
) ? cnt
: mp
->m_len
;
bcopy(mtod(mp
, caddr_t
), cp
, xfer
);
if (replyh
.r_rep
!= rpc_reply
|| mp
== NULL
)
* Loop through the request list to match up the reply
* Iff no match, just drop the datagram
while (!found
&& rep
!= &nfsreqh
) {
if (rep
->r_mrep
== NULL
&& replyh
.r_xid
== rep
->r_xid
&&
inaddr
== rep
->r_inaddr
) {
if (m
->m_type
!= MT_DATA
&& m
->m_type
!= MT_HEADER
)
m
= so
->so_rcv
.sb_mb
= m
->m_next
;
so
->so_rcv
.sb_mb
= nextrecord
;
/* Iff not matched to request, drop it */
sbdroprecord(&so
->so_rcv
);
} else if (so
->so_rcv
.sb_flags
& SB_WAIT
) {
so
->so_rcv
.sb_flags
&= ~SB_WAIT
;
wakeup((caddr_t
)&so
->so_rcv
.sb_cc
);
* nfs_request - goes something like this
* - fill in request struct
* - calls nfs_sosend() for first transmit
* - calls nfs_soreceive() to get reply
* - break down rpc header and return with nfs reply pointed to
* nb: always frees up mreq mbuf list
nfs_request(vp
, mreq
, xid
, mp
, mrp
, mdp
, dposp
)
register struct mbuf
*m
, *mrep
;
register struct nfsreq
*rep
;
struct sockaddr_in
*saddr
;
MALLOC(rep
, struct nfsreq
*, sizeof(struct nfsreq
), M_NFSREQ
, M_WAITOK
);
saddr
= mtod(mntp
->nm_sockaddr
, struct sockaddr_in
*);
rep
->r_inaddr
= saddr
->sin_addr
.s_addr
;
if (mntp
->nm_flag
& NFSMNT_SOFT
)
rep
->r_retry
= mntp
->nm_retrans
;
rep
->r_timer
= rep
->r_timeout
= mntp
->nm_timeo
;
m
= NFSMCOPY(mreq
, 0, M_COPYALL
, M_WAIT
);
/* Chain it into list of outstanding requests */
if (reph
->r_prev
== NULL
) {
reph
->r_prev
->r_next
= rep
;
rep
->r_prev
= reph
->r_prev
;
* Iff the NFSMCOPY above succeeded, send it off...
* otherwise the timer will retransmit later
error
= nfs_udpsend(mntp
->nm_so
, (struct mbuf
*)0, m
, 0, len
);
error
= nfs_udpreply(mntp
->nm_so
, mntp
, rep
);
rep
->r_prev
->r_next
= rep
->r_next
;
rep
->r_next
->r_prev
= rep
->r_prev
;
FREE((caddr_t
)rep
, M_NFSREQ
);
* break down the rpc header and check if ok
dpos
= mtod(md
, caddr_t
);
nfsm_disect(p
, u_long
*, 5*NFSX_UNSIGNED
);
if (*p
++ == rpc_msgdenied
) {
* skip over the auth_verf, someday we may want to cache auth_short's
* for nfs_reqhead(), but for now just dump it
len
= nfsm_rndup(fxdr_unsigned(long, *p
));
nfsm_disect(p
, u_long
*, NFSX_UNSIGNED
);
nfsm_disect(p
, u_long
*, NFSX_UNSIGNED
);
error
= fxdr_unsigned(int, *p
);
return (EPROTONOSUPPORT
);
* Get a request for the server main loop
* - receive a request via. nfs_soreceive()
* - fill in the cred struct.
nfs_getreq(so
, prog
, vers
, maxproc
, nam
, mrp
, mdp
, dposp
, retxid
, proc
, cr
)
register struct ucred
*cr
;
if (error
= nfs_udpreceive(so
, nam
, &mrep
))
dpos
= mtod(mrep
, caddr_t
);
nfsm_disect(p
, u_long
*, 10*NFSX_UNSIGNED
);
*proc
= fxdr_unsigned(u_long
, *p
++);
if (*proc
== NFSPROC_NULL
) {
if (*proc
> maxproc
|| *p
++ != rpc_auth_unix
) {
(void) fxdr_unsigned(int, *p
++);
len
= fxdr_unsigned(int, *++p
);
nfsm_adv(nfsm_rndup(len
));
nfsm_disect(p
, u_long
*, 3*NFSX_UNSIGNED
);
cr
->cr_uid
= fxdr_unsigned(uid_t
, *p
++);
cr
->cr_gid
= fxdr_unsigned(gid_t
, *p
++);
len
= fxdr_unsigned(int, *p
);
nfsm_disect(p
, u_long
*, (len
+ 2)*NFSX_UNSIGNED
);
for (i
= 1; i
<= len
; i
++)
cr
->cr_groups
[i
] = fxdr_unsigned(gid_t
, *p
++);
cr
->cr_ngroups
= len
+ 1;
* Do we have any use for the verifier.
* According to the "Remote Procedure Call Protocol Spec." it
* should be AUTH_NULL, but some clients make it AUTH_UNIX?
* For now, just skip over it
len
= fxdr_unsigned(int, *++p
);
nfsm_adv(nfsm_rndup(len
));
* Generate the rpc reply header
* siz arg. is used to decide if adding a cluster is worthwhile
nfs_rephead(siz
, retxid
, err
, mrq
, mbp
, bposp
)
struct mbuf
*mreq
, *mb
, *mb2
;
if ((siz
+RPC_REPLYSIZ
) > MHLEN
)
p
= mtod(mreq
, u_long
*);
mreq
->m_len
= 6*NFSX_UNSIGNED
;
bpos
= ((caddr_t
)p
)+mreq
->m_len
;
if (err
== ERPCMISMATCH
) {
*p
= txdr_unsigned(RPC_PROGUNAVAIL
);
*p
= txdr_unsigned(RPC_PROGMISMATCH
);
nfsm_build(p
, u_long
*, 2*NFSX_UNSIGNED
);
*p
= txdr_unsigned(2); /* someday 3 */
*p
= txdr_unsigned(RPC_PROCUNAVAIL
);
nfsm_build(p
, u_long
*, NFSX_UNSIGNED
);
if (err
!= 0 && err
!= VNOVAL
)
* Scan the nfsreq list and retranmit any requests that have timed out
* To avoid retransmission attempts on STREAM sockets (in the future) make
* sure to set the r_retry field to 0.
register struct nfsreq
*rep
;
register struct socket
*so
;
while (rep
&& rep
!= &nfsreqh
) {
else if (rep
->r_mrep
== NULL
&& rep
->r_retry
> 0) {
if ((so
->so_state
& SS_CANTSENDMORE
) == 0 &&
sbspace(&so
->so_snd
) >= rep
->r_msiz
) {
m
= NFSMCOPY(rep
->r_mreq
, 0, M_COPYALL
, M_DONTWAIT
);
rep
->r_timeout
<<= 2; /* x4 backoff */
if (rep
->r_timeout
> NFS_MAXTIMEO
)
rep
->r_timeout
= NFS_MAXTIMEO
;
rep
->r_timer
= rep
->r_timeout
;
if (rep
->r_retry
!= VNOVAL
)
m
->m_pkthdr
.len
= rep
->r_msiz
;
(*so
->so_proto
->pr_usrreq
)(so
, PRU_SEND
,
m
, (caddr_t
)0, (struct mbuf
*)0,
timeout(nfs_timer
, (caddr_t
)0, hz
/10);
* nfs_sbwait() is simply sbwait() but at a negative priority so that it
* can not be interrupted by a signal.
sleep((caddr_t
)&sb
->sb_cc
, PZERO
-2);