2005-04-17 02:20:36 +04:00
/*
* linux / fs / lockd / mon . c
*
* The kernel statd client .
*
* Copyright ( C ) 1996 , Olaf Kirch < okir @ monad . swb . de >
*/
# include <linux/types.h>
# include <linux/utsname.h>
# include <linux/kernel.h>
# include <linux/sunrpc/clnt.h>
2007-09-10 21:48:23 +04:00
# include <linux/sunrpc/xprtsock.h>
2005-04-17 02:20:36 +04:00
# include <linux/sunrpc/svc.h>
# include <linux/lockd/lockd.h>
# include <linux/lockd/sm_inter.h>
# define NLMDBG_FACILITY NLMDBG_MONITOR
static struct rpc_clnt * nsm_create ( void ) ;
static struct rpc_program nsm_program ;
/*
* Local NSM state
*/
2006-10-04 13:16:03 +04:00
int nsm_local_state ;
2005-04-17 02:20:36 +04:00
/*
* Common procedure for SM_MON / SM_UNMON calls
*/
static int
2006-10-04 13:15:56 +04:00
nsm_mon_unmon ( struct nsm_handle * nsm , u32 proc , struct nsm_res * res )
2005-04-17 02:20:36 +04:00
{
struct rpc_clnt * clnt ;
int status ;
2008-12-04 22:20:23 +03:00
struct nsm_args args = {
. addr = nsm_addr_in ( nsm ) - > sin_addr . s_addr ,
. prog = NLM_PROGRAM ,
. vers = 3 ,
. proc = NLMPROC_NSM_NOTIFY ,
NSM: Support IPv6 version of mon_name
The "mon_name" argument of the NSMPROC_MON and NSMPROC_UNMON upcalls
is a string that contains the hostname or IP address of the remote peer
to be notified when this host has rebooted. The sm-notify command uses
this identifier to contact the peer when we reboot, so it must be
either a well-qualified DNS hostname or a presentation format IP
address string.
When the "nsm_use_hostnames" sysctl is set to zero, the kernel's NSM
provides a presentation format IP address in the "mon_name" argument.
Otherwise, the "caller_name" argument from NLM requests is used,
which is usually just the DNS hostname of the peer.
To support IPv6 addresses for the mon_name argument, we use the
nsm_handle's address eye-catcher, which already contains an appropriate
presentation format address string. Using the eye-catcher string
obviates the need to use a large buffer on the stack to form the
presentation address string for the upcall.
This patch also addresses a subtle bug.
An NSMPROC_MON request and the subsequent NSMPROC_UNMON request for the
same peer are required to use the same value for the "mon_name"
argument. Otherwise, rpc.statd's NSMPROC_UNMON processing cannot
locate the database entry for that peer and remove it.
If the setting of nsm_use_hostnames is changed between the time the
kernel sends an NSMPROC_MON request and the time it sends the
NSMPROC_UNMON request for the same peer, the "mon_name" argument for
these two requests may not be the same. This is because the value of
"mon_name" is currently chosen at the moment the call is made based on
the setting of nsm_use_hostnames
To ensure both requests pass identical contents in the "mon_name"
argument, we now select which string to use for the argument in the
nsm_monitor() function. A pointer to this string is saved in the
nsm_handle so it can be used for a subsequent NSMPROC_UNMON upcall.
NB: There are other potential problems, such as how nlm_host_rebooted()
might behave if nsm_use_hostnames were changed while hosts are still
being monitored. This patch does not attempt to address those
problems.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-12-04 22:20:46 +03:00
. mon_name = nsm - > sm_mon_name ,
2008-12-04 22:20:23 +03:00
} ;
2006-03-20 21:44:23 +03:00
struct rpc_message msg = {
. rpc_argp = & args ,
. rpc_resp = res ,
} ;
2005-04-17 02:20:36 +04:00
clnt = nsm_create ( ) ;
if ( IS_ERR ( clnt ) ) {
status = PTR_ERR ( clnt ) ;
2008-12-04 22:20:31 +03:00
dprintk ( " lockd: failed to create NSM upcall transport, "
" status=%d \n " , status ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
memset ( res , 0 , sizeof ( * res ) ) ;
2006-03-20 21:44:23 +03:00
msg . rpc_proc = & clnt - > cl_procinfo [ proc ] ;
status = rpc_call_sync ( clnt , & msg , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( status < 0 )
2008-12-04 22:20:31 +03:00
dprintk ( " lockd: NSM upcall RPC failed, status=%d \n " ,
status ) ;
2005-04-17 02:20:36 +04:00
else
status = 0 ;
2007-06-10 03:49:36 +04:00
rpc_shutdown_client ( clnt ) ;
2005-04-17 02:20:36 +04:00
out :
return status ;
}
/*
* Set up monitoring of a remote host
*/
int
nsm_monitor ( struct nlm_host * host )
{
2006-10-04 13:15:53 +04:00
struct nsm_handle * nsm = host - > h_nsmhandle ;
2005-04-17 02:20:36 +04:00
struct nsm_res res ;
int status ;
2008-12-04 22:20:53 +03:00
dprintk ( " lockd: nsm_monitor(%s) \n " , nsm - > sm_name ) ;
2006-10-04 13:15:53 +04:00
if ( nsm - > sm_monitored )
2006-10-04 13:15:51 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
NSM: Support IPv6 version of mon_name
The "mon_name" argument of the NSMPROC_MON and NSMPROC_UNMON upcalls
is a string that contains the hostname or IP address of the remote peer
to be notified when this host has rebooted. The sm-notify command uses
this identifier to contact the peer when we reboot, so it must be
either a well-qualified DNS hostname or a presentation format IP
address string.
When the "nsm_use_hostnames" sysctl is set to zero, the kernel's NSM
provides a presentation format IP address in the "mon_name" argument.
Otherwise, the "caller_name" argument from NLM requests is used,
which is usually just the DNS hostname of the peer.
To support IPv6 addresses for the mon_name argument, we use the
nsm_handle's address eye-catcher, which already contains an appropriate
presentation format address string. Using the eye-catcher string
obviates the need to use a large buffer on the stack to form the
presentation address string for the upcall.
This patch also addresses a subtle bug.
An NSMPROC_MON request and the subsequent NSMPROC_UNMON request for the
same peer are required to use the same value for the "mon_name"
argument. Otherwise, rpc.statd's NSMPROC_UNMON processing cannot
locate the database entry for that peer and remove it.
If the setting of nsm_use_hostnames is changed between the time the
kernel sends an NSMPROC_MON request and the time it sends the
NSMPROC_UNMON request for the same peer, the "mon_name" argument for
these two requests may not be the same. This is because the value of
"mon_name" is currently chosen at the moment the call is made based on
the setting of nsm_use_hostnames
To ensure both requests pass identical contents in the "mon_name"
argument, we now select which string to use for the argument in the
nsm_monitor() function. A pointer to this string is saved in the
nsm_handle so it can be used for a subsequent NSMPROC_UNMON upcall.
NB: There are other potential problems, such as how nlm_host_rebooted()
might behave if nsm_use_hostnames were changed while hosts are still
being monitored. This patch does not attempt to address those
problems.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-12-04 22:20:46 +03:00
/*
* Choose whether to record the caller_name or IP address of
* this peer in the local rpc . statd ' s database .
*/
nsm - > sm_mon_name = nsm_use_hostnames ? nsm - > sm_name : nsm - > sm_addrbuf ;
2006-10-04 13:15:56 +04:00
status = nsm_mon_unmon ( nsm , SM_MON , & res ) ;
2008-12-04 22:21:15 +03:00
if ( res . status ! = 0 )
status = - EIO ;
if ( status < 0 )
2008-12-04 22:20:53 +03:00
printk ( KERN_NOTICE " lockd: cannot monitor %s \n " , nsm - > sm_name ) ;
2005-04-17 02:20:36 +04:00
else
2006-10-04 13:15:53 +04:00
nsm - > sm_monitored = 1 ;
2005-04-17 02:20:36 +04:00
return status ;
}
/*
* Cease to monitor remote host
*/
int
nsm_unmonitor ( struct nlm_host * host )
{
2006-10-04 13:15:53 +04:00
struct nsm_handle * nsm = host - > h_nsmhandle ;
2005-04-17 02:20:36 +04:00
struct nsm_res res ;
2006-10-04 13:15:51 +04:00
int status = 0 ;
2005-04-17 02:20:36 +04:00
2006-10-04 13:15:53 +04:00
if ( nsm = = NULL )
2006-10-04 13:15:51 +04:00
return 0 ;
2006-10-04 13:15:53 +04:00
host - > h_nsmhandle = NULL ;
2006-10-04 13:15:51 +04:00
2006-10-04 13:15:56 +04:00
if ( atomic_read ( & nsm - > sm_count ) = = 1
& & nsm - > sm_monitored & & ! nsm - > sm_sticky ) {
2008-12-04 22:20:53 +03:00
dprintk ( " lockd: nsm_unmonitor(%s) \n " , nsm - > sm_name ) ;
2006-10-04 13:15:56 +04:00
status = nsm_mon_unmon ( nsm , SM_UNMON , & res ) ;
2006-10-04 13:15:51 +04:00
if ( status < 0 )
2006-10-04 13:15:56 +04:00
printk ( KERN_NOTICE " lockd: cannot unmonitor %s \n " ,
2008-12-04 22:20:53 +03:00
nsm - > sm_name ) ;
2006-10-04 13:15:56 +04:00
else
nsm - > sm_monitored = 0 ;
2006-10-04 13:15:51 +04:00
}
2006-10-04 13:15:53 +04:00
nsm_release ( nsm ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
/*
* Create NSM client for the local host
*/
static struct rpc_clnt *
nsm_create ( void )
{
2006-08-23 04:06:20 +04:00
struct sockaddr_in sin = {
. sin_family = AF_INET ,
. sin_addr . s_addr = htonl ( INADDR_LOOPBACK ) ,
. sin_port = 0 ,
} ;
struct rpc_create_args args = {
2007-09-10 21:48:23 +04:00
. protocol = XPRT_TRANSPORT_UDP ,
2006-08-23 04:06:20 +04:00
. address = ( struct sockaddr * ) & sin ,
. addrsize = sizeof ( sin ) ,
. servername = " localhost " ,
. program = & nsm_program ,
. version = SM_VERSION ,
. authflavor = RPC_AUTH_NULL ,
} ;
return rpc_create ( & args ) ;
2005-04-17 02:20:36 +04:00
}
/*
* XDR functions for NSM .
2008-03-14 21:26:01 +03:00
*
* See http : //www.opengroup.org/ for details on the Network
* Status Monitor wire protocol .
2005-04-17 02:20:36 +04:00
*/
2008-03-14 21:25:32 +03:00
static __be32 * xdr_encode_nsm_string ( __be32 * p , char * string )
{
size_t len = strlen ( string ) ;
if ( len > SM_MAXSTRLEN )
len = SM_MAXSTRLEN ;
return xdr_encode_opaque ( p , string , len ) ;
}
2008-03-14 21:25:39 +03:00
/*
* " mon_name " specifies the host to be monitored .
*/
static __be32 * xdr_encode_mon_name ( __be32 * p , struct nsm_args * argp )
{
NSM: Support IPv6 version of mon_name
The "mon_name" argument of the NSMPROC_MON and NSMPROC_UNMON upcalls
is a string that contains the hostname or IP address of the remote peer
to be notified when this host has rebooted. The sm-notify command uses
this identifier to contact the peer when we reboot, so it must be
either a well-qualified DNS hostname or a presentation format IP
address string.
When the "nsm_use_hostnames" sysctl is set to zero, the kernel's NSM
provides a presentation format IP address in the "mon_name" argument.
Otherwise, the "caller_name" argument from NLM requests is used,
which is usually just the DNS hostname of the peer.
To support IPv6 addresses for the mon_name argument, we use the
nsm_handle's address eye-catcher, which already contains an appropriate
presentation format address string. Using the eye-catcher string
obviates the need to use a large buffer on the stack to form the
presentation address string for the upcall.
This patch also addresses a subtle bug.
An NSMPROC_MON request and the subsequent NSMPROC_UNMON request for the
same peer are required to use the same value for the "mon_name"
argument. Otherwise, rpc.statd's NSMPROC_UNMON processing cannot
locate the database entry for that peer and remove it.
If the setting of nsm_use_hostnames is changed between the time the
kernel sends an NSMPROC_MON request and the time it sends the
NSMPROC_UNMON request for the same peer, the "mon_name" argument for
these two requests may not be the same. This is because the value of
"mon_name" is currently chosen at the moment the call is made based on
the setting of nsm_use_hostnames
To ensure both requests pass identical contents in the "mon_name"
argument, we now select which string to use for the argument in the
nsm_monitor() function. A pointer to this string is saved in the
nsm_handle so it can be used for a subsequent NSMPROC_UNMON upcall.
NB: There are other potential problems, such as how nlm_host_rebooted()
might behave if nsm_use_hostnames were changed while hosts are still
being monitored. This patch does not attempt to address those
problems.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
2008-12-04 22:20:46 +03:00
return xdr_encode_nsm_string ( p , argp - > mon_name ) ;
2008-03-14 21:25:39 +03:00
}
2008-03-14 21:25:46 +03:00
/*
* The " my_id " argument specifies the hostname and RPC procedure
* to be called when the status manager receives notification
* ( via the SM_NOTIFY call ) that the state of host " mon_name "
* has changed .
*/
static __be32 * xdr_encode_my_id ( __be32 * p , struct nsm_args * argp )
{
p = xdr_encode_nsm_string ( p , utsname ( ) - > nodename ) ;
if ( ! p )
return ERR_PTR ( - EIO ) ;
* p + + = htonl ( argp - > prog ) ;
* p + + = htonl ( argp - > vers ) ;
* p + + = htonl ( argp - > proc ) ;
return p ;
}
2008-03-14 21:25:53 +03:00
/*
* The " mon_id " argument specifies the non - private arguments
* of an SM_MON or SM_UNMON call .
*/
static __be32 * xdr_encode_mon_id ( __be32 * p , struct nsm_args * argp )
{
p = xdr_encode_mon_name ( p , argp ) ;
if ( ! p )
return ERR_PTR ( - EIO ) ;
return xdr_encode_my_id ( p , argp ) ;
}
2008-03-14 21:26:08 +03:00
/*
* The " priv " argument may contain private information required
* by the SM_MON call . This information will be supplied in the
* SM_NOTIFY call .
*
* Linux provides the raw IP address of the monitored host ,
* left in network byte order .
*/
static __be32 * xdr_encode_priv ( __be32 * p , struct nsm_args * argp )
{
* p + + = argp - > addr ;
* p + + = 0 ;
* p + + = 0 ;
* p + + = 0 ;
return p ;
}
2005-04-17 02:20:36 +04:00
static int
2006-10-20 10:28:46 +04:00
xdr_encode_mon ( struct rpc_rqst * rqstp , __be32 * p , struct nsm_args * argp )
2005-04-17 02:20:36 +04:00
{
2008-03-14 21:26:01 +03:00
p = xdr_encode_mon_id ( p , argp ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( p ) )
return PTR_ERR ( p ) ;
2006-10-04 13:15:56 +04:00
2008-03-14 21:26:08 +03:00
p = xdr_encode_priv ( p , argp ) ;
if ( IS_ERR ( p ) )
return PTR_ERR ( p ) ;
2005-04-17 02:20:36 +04:00
rqstp - > rq_slen = xdr_adjust_iovec ( rqstp - > rq_svec , p ) ;
return 0 ;
}
static int
2006-10-20 10:28:46 +04:00
xdr_encode_unmon ( struct rpc_rqst * rqstp , __be32 * p , struct nsm_args * argp )
2005-04-17 02:20:36 +04:00
{
2008-03-14 21:26:01 +03:00
p = xdr_encode_mon_id ( p , argp ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( p ) )
return PTR_ERR ( p ) ;
rqstp - > rq_slen = xdr_adjust_iovec ( rqstp - > rq_svec , p ) ;
return 0 ;
}
static int
2006-10-20 10:28:46 +04:00
xdr_decode_stat_res ( struct rpc_rqst * rqstp , __be32 * p , struct nsm_res * resp )
2005-04-17 02:20:36 +04:00
{
resp - > status = ntohl ( * p + + ) ;
resp - > state = ntohl ( * p + + ) ;
dprintk ( " nsm: xdr_decode_stat_res status %d state %d \n " ,
resp - > status , resp - > state ) ;
return 0 ;
}
static int
2006-10-20 10:28:46 +04:00
xdr_decode_stat ( struct rpc_rqst * rqstp , __be32 * p , struct nsm_res * resp )
2005-04-17 02:20:36 +04:00
{
resp - > state = ntohl ( * p + + ) ;
return 0 ;
}
# define SM_my_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN))
2008-03-14 21:26:01 +03:00
# define SM_my_id_sz (SM_my_name_sz+3)
# define SM_mon_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN))
# define SM_mon_id_sz (SM_mon_name_sz+SM_my_id_sz)
2008-03-14 21:26:08 +03:00
# define SM_priv_sz (XDR_QUADLEN(SM_PRIV_SIZE))
# define SM_mon_sz (SM_mon_id_sz+SM_priv_sz)
2005-04-17 02:20:36 +04:00
# define SM_monres_sz 2
# define SM_unmonres_sz 1
static struct rpc_procinfo nsm_procedures [ ] = {
[ SM_MON ] = {
. p_proc = SM_MON ,
. p_encode = ( kxdrproc_t ) xdr_encode_mon ,
. p_decode = ( kxdrproc_t ) xdr_decode_stat_res ,
2007-03-30 00:47:53 +04:00
. p_arglen = SM_mon_sz ,
. p_replen = SM_monres_sz ,
2006-03-20 21:44:22 +03:00
. p_statidx = SM_MON ,
. p_name = " MONITOR " ,
2005-04-17 02:20:36 +04:00
} ,
[ SM_UNMON ] = {
. p_proc = SM_UNMON ,
. p_encode = ( kxdrproc_t ) xdr_encode_unmon ,
. p_decode = ( kxdrproc_t ) xdr_decode_stat ,
2007-03-30 00:47:53 +04:00
. p_arglen = SM_mon_id_sz ,
. p_replen = SM_unmonres_sz ,
2006-03-20 21:44:22 +03:00
. p_statidx = SM_UNMON ,
. p_name = " UNMONITOR " ,
2005-04-17 02:20:36 +04:00
} ,
} ;
static struct rpc_version nsm_version1 = {
2006-03-24 14:15:34 +03:00
. number = 1 ,
. nrprocs = ARRAY_SIZE ( nsm_procedures ) ,
2005-04-17 02:20:36 +04:00
. procs = nsm_procedures
} ;
static struct rpc_version * nsm_version [ ] = {
[ 1 ] = & nsm_version1 ,
} ;
static struct rpc_stat nsm_stats ;
static struct rpc_program nsm_program = {
. name = " statd " ,
. number = SM_PROGRAM ,
2006-03-24 14:15:34 +03:00
. nrvers = ARRAY_SIZE ( nsm_version ) ,
2005-04-17 02:20:36 +04:00
. version = nsm_version ,
. stats = & nsm_stats
} ;