2005-04-17 02:20:36 +04:00
/*
* linux / fs / nfs / callback . c
*
* Copyright ( C ) 2004 Trond Myklebust
*
* NFSv4 callback handling
*/
# include <linux/completion.h>
# include <linux/ip.h>
# include <linux/module.h>
2017-02-08 20:51:30 +03:00
# include <linux/sched/signal.h>
2005-04-17 02:20:36 +04:00
# include <linux/sunrpc/svc.h>
# include <linux/sunrpc/svcsock.h>
# include <linux/nfs_fs.h>
2012-10-02 03:33:18 +04:00
# include <linux/errno.h>
2006-03-26 13:37:12 +04:00
# include <linux/mutex.h>
2007-07-17 15:03:35 +04:00
# include <linux/freezer.h>
2008-02-20 16:55:30 +03:00
# include <linux/kthread.h>
2008-12-24 00:18:34 +03:00
# include <linux/sunrpc/svcauth_gss.h>
2009-04-01 17:23:08 +04:00
# include <linux/sunrpc/bc_xprt.h>
2005-12-27 07:43:12 +03:00
# include <net/inet_sock.h>
2005-06-22 21:16:21 +04:00
# include "nfs4_fs.h"
2005-04-17 02:20:36 +04:00
# include "callback.h"
2006-08-23 04:06:10 +04:00
# include "internal.h"
2012-08-20 18:00:36 +04:00
# include "netns.h"
2005-04-17 02:20:36 +04:00
# define NFSDBG_FACILITY NFSDBG_CALLBACK
struct nfs_callback_data {
unsigned int users ;
2009-04-01 17:23:08 +04:00
struct svc_serv * serv ;
2005-04-17 02:20:36 +04:00
} ;
2009-04-01 17:23:14 +04:00
static struct nfs_callback_data nfs_callback_info [ NFS4_MAX_MINOR_VERSION + 1 ] ;
2006-03-26 13:37:12 +04:00
static DEFINE_MUTEX ( nfs_callback_mutex ) ;
2005-04-17 02:20:36 +04:00
static struct svc_program nfs4_callback_program ;
2012-08-20 18:00:16 +04:00
static int nfs4_callback_up_net ( struct svc_serv * serv , struct net * net )
{
int ret ;
2012-08-20 18:00:36 +04:00
struct nfs_net * nn = net_generic ( net , nfs_net_id ) ;
2012-08-20 18:00:16 +04:00
ret = svc_create_xprt ( serv , " tcp " , net , PF_INET ,
nfs_callback_set_tcpport , SVC_SOCK_ANONYMOUS ) ;
if ( ret < = 0 )
goto out_err ;
2012-08-20 18:00:36 +04:00
nn - > nfs_callback_tcpport = ret ;
2012-08-20 18:00:16 +04:00
dprintk ( " NFS: Callback listener port = %u (af %u, net %p) \n " ,
2012-08-20 18:00:36 +04:00
nn - > nfs_callback_tcpport , PF_INET , net ) ;
2012-08-20 18:00:16 +04:00
ret = svc_create_xprt ( serv , " tcp " , net , PF_INET6 ,
nfs_callback_set_tcpport , SVC_SOCK_ANONYMOUS ) ;
if ( ret > 0 ) {
2012-08-20 18:00:41 +04:00
nn - > nfs_callback_tcpport6 = ret ;
2012-08-20 18:00:16 +04:00
dprintk ( " NFS: Callback listener port = %u (af %u, net %p) \n " ,
2012-08-20 18:00:41 +04:00
nn - > nfs_callback_tcpport6 , PF_INET6 , net ) ;
2012-08-20 18:00:16 +04:00
} else if ( ret ! = - EAFNOSUPPORT )
goto out_err ;
return 0 ;
out_err :
return ( ret ) ? ret : - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
/*
2009-04-01 17:23:14 +04:00
* This is the NFSv4 callback kernel thread .
2005-04-17 02:20:36 +04:00
*/
2008-02-20 16:55:30 +03:00
static int
2009-04-01 17:22:56 +04:00
nfs4_callback_svc ( void * vrqstp )
2005-04-17 02:20:36 +04:00
{
2012-08-18 05:47:53 +04:00
int err ;
2008-02-20 16:55:30 +03:00
struct svc_rqst * rqstp = vrqstp ;
2005-04-17 02:20:36 +04:00
2007-07-17 15:03:35 +04:00
set_freezable ( ) ;
2005-04-17 02:20:36 +04:00
2008-02-20 16:55:30 +03:00
while ( ! kthread_should_stop ( ) ) {
2005-04-17 02:20:36 +04:00
/*
* Listen for a request on the socket
*/
2006-10-02 13:17:50 +04:00
err = svc_recv ( rqstp , MAX_SCHEDULE_TIMEOUT ) ;
2012-08-18 05:47:53 +04:00
if ( err = = - EAGAIN | | err = = - EINTR )
2005-04-17 02:20:36 +04:00
continue ;
2006-10-02 13:17:50 +04:00
svc_process ( rqstp ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-20 16:55:30 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-04-01 17:23:08 +04:00
# if defined(CONFIG_NFS_V4_1)
/*
* The callback service for NFSv4 .1 callbacks
*/
static int
nfs41_callback_svc ( void * vrqstp )
{
struct svc_rqst * rqstp = vrqstp ;
struct svc_serv * serv = rqstp - > rq_server ;
struct rpc_rqst * req ;
int error ;
DEFINE_WAIT ( wq ) ;
set_freezable ( ) ;
while ( ! kthread_should_stop ( ) ) {
2013-03-25 15:59:57 +04:00
if ( try_to_freeze ( ) )
continue ;
2015-03-20 22:15:14 +03:00
prepare_to_wait ( & serv - > sv_cb_waitq , & wq , TASK_INTERRUPTIBLE ) ;
2009-04-01 17:23:08 +04:00
spin_lock_bh ( & serv - > sv_cb_lock ) ;
if ( ! list_empty ( & serv - > sv_cb_list ) ) {
req = list_first_entry ( & serv - > sv_cb_list ,
struct rpc_rqst , rq_bc_list ) ;
list_del ( & req - > rq_bc_list ) ;
spin_unlock_bh ( & serv - > sv_cb_lock ) ;
2015-01-14 21:08:57 +03:00
finish_wait ( & serv - > sv_cb_waitq , & wq ) ;
2009-04-01 17:23:08 +04:00
dprintk ( " Invoking bc_svc_process() \n " ) ;
error = bc_svc_process ( serv , req , rqstp ) ;
dprintk ( " bc_svc_process() returned w/ error code= %d \n " ,
error ) ;
} else {
spin_unlock_bh ( & serv - > sv_cb_lock ) ;
2015-03-20 22:15:14 +03:00
schedule ( ) ;
2015-01-14 21:08:57 +03:00
finish_wait ( & serv - > sv_cb_waitq , & wq ) ;
2009-04-01 17:23:08 +04:00
}
2015-03-20 22:15:14 +03:00
flush_signals ( current ) ;
2009-04-01 17:23:08 +04:00
}
return 0 ;
}
static inline void nfs_callback_bc_serv ( u32 minorversion , struct rpc_xprt * xprt ,
2012-08-20 18:00:21 +04:00
struct svc_serv * serv )
2009-04-01 17:23:08 +04:00
{
if ( minorversion )
2012-08-20 18:00:21 +04:00
/*
* Save the svc_serv in the transport so that it can
* be referenced when the session backchannel is initialized
*/
xprt - > bc_serv = serv ;
2009-04-01 17:23:08 +04:00
}
# else
static inline void nfs_callback_bc_serv ( u32 minorversion , struct rpc_xprt * xprt ,
2012-08-20 18:00:21 +04:00
struct svc_serv * serv )
2009-04-01 17:23:08 +04:00
{
}
# endif /* CONFIG_NFS_V4_1 */
2012-08-20 18:00:26 +04:00
static int nfs_callback_start_svc ( int minorversion , struct rpc_xprt * xprt ,
struct svc_serv * serv )
{
2016-08-30 03:03:52 +03:00
int nrservs = nfs_callback_nr_threads ;
2012-08-20 18:00:26 +04:00
int ret ;
nfs_callback_bc_serv ( minorversion , xprt , serv ) ;
2016-08-30 03:03:52 +03:00
if ( nrservs < NFS4_MIN_NR_CALLBACK_THREADS )
nrservs = NFS4_MIN_NR_CALLBACK_THREADS ;
2016-08-30 03:03:51 +03:00
if ( serv - > sv_nrthreads - 1 = = nrservs )
2012-08-20 18:00:31 +04:00
return 0 ;
2016-08-30 03:03:51 +03:00
ret = serv - > sv_ops - > svo_setup ( serv , NULL , nrservs ) ;
if ( ret ) {
serv - > sv_ops - > svo_setup ( serv , NULL , 0 ) ;
2012-10-16 20:30:44 +04:00
return ret ;
2012-08-20 18:00:26 +04:00
}
dprintk ( " nfs_callback_up: service started \n " ) ;
return 0 ;
}
2012-08-20 18:00:46 +04:00
static void nfs_callback_down_net ( u32 minorversion , struct svc_serv * serv , struct net * net )
{
struct nfs_net * nn = net_generic ( net , nfs_net_id ) ;
if ( - - nn - > cb_users [ minorversion ] )
return ;
dprintk ( " NFS: destroy per-net callback data; net=%p \n " , net ) ;
svc_shutdown_net ( serv , net ) ;
}
2015-10-25 00:28:32 +03:00
static int nfs_callback_up_net ( int minorversion , struct svc_serv * serv ,
struct net * net , struct rpc_xprt * xprt )
2012-08-20 18:00:16 +04:00
{
2012-08-20 18:00:46 +04:00
struct nfs_net * nn = net_generic ( net , nfs_net_id ) ;
2012-08-20 18:00:16 +04:00
int ret ;
2012-08-20 18:00:46 +04:00
if ( nn - > cb_users [ minorversion ] + + )
return 0 ;
2012-08-20 18:00:16 +04:00
dprintk ( " NFS: create per-net callback data; net=%p \n " , net ) ;
ret = svc_bind ( serv , net ) ;
if ( ret < 0 ) {
printk ( KERN_WARNING " NFS: bind callback service failed \n " ) ;
goto err_bind ;
}
2015-10-25 00:28:32 +03:00
ret = - EPROTONOSUPPORT ;
2016-11-22 23:50:52 +03:00
if ( ! IS_ENABLED ( CONFIG_NFS_V4_1 ) | | minorversion = = 0 )
2015-10-25 00:28:32 +03:00
ret = nfs4_callback_up_net ( serv , net ) ;
else if ( xprt - > ops - > bc_up )
ret = xprt - > ops - > bc_up ( serv , net ) ;
2012-08-20 18:00:16 +04:00
if ( ret < 0 ) {
printk ( KERN_ERR " NFS: callback service start failed \n " ) ;
goto err_socks ;
}
return 0 ;
err_socks :
svc_rpcb_cleanup ( serv , net ) ;
err_bind :
2016-08-29 18:15:36 +03:00
nn - > cb_users [ minorversion ] - - ;
2012-08-20 18:00:31 +04:00
dprintk ( " NFS: Couldn't create callback socket: err = %d; "
" net = %p \n " , ret , net ) ;
2012-08-20 18:00:16 +04:00
return ret ;
}
2016-08-30 03:03:48 +03:00
static struct svc_serv_ops nfs40_cb_sv_ops = {
. svo_function = nfs4_callback_svc ,
2015-06-08 22:06:51 +03:00
. svo_enqueue_xprt = svc_xprt_do_enqueue ,
2016-08-30 03:03:51 +03:00
. svo_setup = svc_set_num_threads ,
2016-08-30 03:03:48 +03:00
. svo_module = THIS_MODULE ,
} ;
# if defined(CONFIG_NFS_V4_1)
static struct svc_serv_ops nfs41_cb_sv_ops = {
. svo_function = nfs41_callback_svc ,
. svo_enqueue_xprt = svc_xprt_do_enqueue ,
2016-08-30 03:03:51 +03:00
. svo_setup = svc_set_num_threads ,
2016-08-30 03:03:48 +03:00
. svo_module = THIS_MODULE ,
2015-06-08 22:03:32 +03:00
} ;
2017-03-10 05:48:13 +03:00
static struct svc_serv_ops * nfs4_cb_sv_ops [ ] = {
2016-08-30 03:03:48 +03:00
[ 0 ] = & nfs40_cb_sv_ops ,
[ 1 ] = & nfs41_cb_sv_ops ,
} ;
# else
2017-03-10 05:48:13 +03:00
static struct svc_serv_ops * nfs4_cb_sv_ops [ ] = {
2016-08-30 03:03:48 +03:00
[ 0 ] = & nfs40_cb_sv_ops ,
[ 1 ] = NULL ,
} ;
# endif
2012-08-20 18:00:11 +04:00
static struct svc_serv * nfs_callback_create_svc ( int minorversion )
{
struct nfs_callback_data * cb_info = & nfs_callback_info [ minorversion ] ;
struct svc_serv * serv ;
2016-08-30 03:03:48 +03:00
struct svc_serv_ops * sv_ops ;
2012-08-20 18:00:11 +04:00
/*
* Check whether we ' re already up and running .
*/
2016-08-30 03:03:50 +03:00
if ( cb_info - > serv ) {
2012-08-20 18:00:11 +04:00
/*
* Note : increase service usage , because later in case of error
* svc_destroy ( ) will be called .
*/
svc_get ( cb_info - > serv ) ;
return cb_info - > serv ;
}
2016-08-30 03:03:48 +03:00
switch ( minorversion ) {
case 0 :
sv_ops = nfs4_cb_sv_ops [ 0 ] ;
break ;
default :
sv_ops = nfs4_cb_sv_ops [ 1 ] ;
}
if ( sv_ops = = NULL )
return ERR_PTR ( - ENOTSUPP ) ;
2012-08-20 18:00:11 +04:00
/*
* Sanity check : if there ' s no task ,
* we should be the first user . . .
*/
if ( cb_info - > users )
printk ( KERN_WARNING " nfs_callback_create_svc: no kthread, %d users?? \n " ,
cb_info - > users ) ;
2016-08-30 03:03:48 +03:00
serv = svc_create ( & nfs4_callback_program , NFS4_CALLBACK_BUFSIZE , sv_ops ) ;
2012-08-20 18:00:11 +04:00
if ( ! serv ) {
printk ( KERN_ERR " nfs_callback_create_svc: create service failed \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2016-08-30 03:03:50 +03:00
cb_info - > serv = serv ;
2012-08-20 18:00:11 +04:00
/* As there is only one thread we need to over-ride the
* default maximum of 80 connections
*/
serv - > sv_maxconn = 1024 ;
dprintk ( " nfs_callback_create_svc: service created \n " ) ;
return serv ;
}
2009-04-01 17:22:56 +04:00
/*
* Bring up the callback thread if it is not already up .
*/
int nfs_callback_up ( u32 minorversion , struct rpc_xprt * xprt )
{
2012-08-20 18:00:11 +04:00
struct svc_serv * serv ;
2009-04-01 17:23:14 +04:00
struct nfs_callback_data * cb_info = & nfs_callback_info [ minorversion ] ;
2012-08-20 18:00:31 +04:00
int ret ;
2012-08-20 18:00:16 +04:00
struct net * net = xprt - > xprt_net ;
2009-04-01 17:22:56 +04:00
mutex_lock ( & nfs_callback_mutex ) ;
2012-08-20 18:00:11 +04:00
serv = nfs_callback_create_svc ( minorversion ) ;
if ( IS_ERR ( serv ) ) {
ret = PTR_ERR ( serv ) ;
goto err_create ;
}
2015-10-25 00:28:32 +03:00
ret = nfs_callback_up_net ( minorversion , serv , net , xprt ) ;
2012-08-20 18:00:16 +04:00
if ( ret < 0 )
goto err_net ;
2012-05-02 16:08:38 +04:00
2012-08-20 18:00:26 +04:00
ret = nfs_callback_start_svc ( minorversion , xprt , serv ) ;
if ( ret < 0 )
goto err_start ;
2008-02-20 16:55:30 +03:00
2012-08-20 18:00:31 +04:00
cb_info - > users + + ;
2008-02-11 18:00:20 +03:00
/*
* svc_create creates the svc_serv with sv_nrthreads = = 1 , and then
2008-02-20 16:55:30 +03:00
* svc_prepare_thread increments that . So we need to call svc_destroy
2008-02-11 18:00:20 +03:00
* on both success and failure so that the refcount is 1 when the
* thread exits .
*/
2012-08-20 18:00:31 +04:00
err_net :
2016-08-30 03:03:50 +03:00
if ( ! cb_info - > users )
cb_info - > serv = NULL ;
2012-08-20 18:00:11 +04:00
svc_destroy ( serv ) ;
err_create :
2006-03-26 13:37:12 +04:00
mutex_unlock ( & nfs_callback_mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
2012-08-20 18:00:26 +04:00
err_start :
2012-08-20 18:00:46 +04:00
nfs_callback_down_net ( minorversion , serv , net ) ;
2012-08-20 18:00:31 +04:00
dprintk ( " NFS: Couldn't create server thread; err = %d \n " , ret ) ;
goto err_net ;
2005-04-17 02:20:36 +04:00
}
/*
2008-06-11 18:03:11 +04:00
* Kill the callback thread if it ' s no longer being used .
2005-04-17 02:20:36 +04:00
*/
2012-08-20 18:00:06 +04:00
void nfs_callback_down ( int minorversion , struct net * net )
2005-04-17 02:20:36 +04:00
{
2009-04-01 17:23:14 +04:00
struct nfs_callback_data * cb_info = & nfs_callback_info [ minorversion ] ;
2016-08-30 03:03:51 +03:00
struct svc_serv * serv ;
2009-04-01 17:23:14 +04:00
2006-03-26 13:37:12 +04:00
mutex_lock ( & nfs_callback_mutex ) ;
2016-08-30 03:03:51 +03:00
serv = cb_info - > serv ;
nfs_callback_down_net ( minorversion , serv , net ) ;
2009-04-01 17:23:14 +04:00
cb_info - > users - - ;
2016-08-30 03:03:50 +03:00
if ( cb_info - > users = = 0 ) {
2016-08-30 03:03:51 +03:00
svc_get ( serv ) ;
serv - > sv_ops - > svo_setup ( serv , NULL , 0 ) ;
svc_destroy ( serv ) ;
2012-08-20 18:00:51 +04:00
dprintk ( " nfs_callback_down: service destroyed \n " ) ;
2009-04-01 17:23:14 +04:00
cb_info - > serv = NULL ;
2008-06-11 18:03:11 +04:00
}
2006-03-26 13:37:12 +04:00
mutex_unlock ( & nfs_callback_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-25 18:38:01 +03:00
/* Boolean check of RPC_AUTH_GSS principal */
int
check_gss_callback_principal ( struct nfs_client * clp , struct svc_rqst * rqstp )
2008-12-24 00:18:34 +03:00
{
2012-05-15 03:55:22 +04:00
char * p = rqstp - > rq_cred . cr_principal ;
2008-12-24 00:18:34 +03:00
2011-01-25 18:38:01 +03:00
if ( rqstp - > rq_authop - > flavour ! = RPC_AUTH_GSS )
return 1 ;
2011-01-06 05:04:33 +03:00
/* No RPC_AUTH_GSS on NFSv4.1 back channel yet */
if ( clp - > cl_minorversion ! = 0 )
2011-01-25 18:38:01 +03:00
return 0 ;
2008-12-24 00:18:34 +03:00
/*
* It might just be a normal user principal , in which case
* userspace won ' t bother to tell us the name at all .
*/
if ( p = = NULL )
2011-01-25 18:38:01 +03:00
return 0 ;
2008-12-24 00:18:34 +03:00
2014-06-22 04:52:17 +04:00
/*
* Did we get the acceptor from userland during the SETCLIENID
* negotiation ?
*/
if ( clp - > cl_acceptor )
return ! strcmp ( p , clp - > cl_acceptor ) ;
/*
* Otherwise try to verify it using the cl_hostname . Note that this
* doesn ' t work if a non - canonical hostname was used in the devname .
*/
2008-12-24 00:18:34 +03:00
/* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
if ( memcmp ( p , " nfs@ " , 4 ) ! = 0 )
2011-01-25 18:38:01 +03:00
return 0 ;
2008-12-24 00:18:34 +03:00
p + = 4 ;
2012-03-02 02:01:05 +04:00
if ( strcmp ( p , clp - > cl_hostname ) ! = 0 )
2011-01-25 18:38:01 +03:00
return 0 ;
return 1 ;
2008-12-24 00:18:34 +03:00
}
2011-01-25 18:38:01 +03:00
/*
* pg_authenticate method for nfsv4 callback threads .
*
* The authflavor has been negotiated , so an incorrect flavor is a server
2015-06-05 01:40:13 +03:00
* bug . Deny packets with incorrect authflavor .
2011-01-25 18:38:01 +03:00
*
* All other checking done after NFS decoding where the nfs_client can be
* found in nfs4_callback_compound
*/
2005-04-17 02:20:36 +04:00
static int nfs_callback_authenticate ( struct svc_rqst * rqstp )
{
switch ( rqstp - > rq_authop - > flavour ) {
2011-01-25 18:38:01 +03:00
case RPC_AUTH_NULL :
if ( rqstp - > rq_proc ! = CB_NULL )
2015-06-05 01:40:13 +03:00
return SVC_DENIED ;
2011-01-25 18:38:01 +03:00
break ;
case RPC_AUTH_GSS :
/* No RPC_AUTH_GSS support yet in NFSv4.1 */
if ( svc_is_backchannel ( rqstp ) )
2015-06-05 01:40:13 +03:00
return SVC_DENIED ;
2005-04-17 02:20:36 +04:00
}
2011-01-25 18:38:01 +03:00
return SVC_OK ;
2005-04-17 02:20:36 +04:00
}
/*
* Define NFS4 callback program
*/
static struct svc_version * nfs4_callback_version [ ] = {
[ 1 ] = & nfs4_callback_version1 ,
2009-12-05 21:19:01 +03:00
[ 4 ] = & nfs4_callback_version4 ,
2005-04-17 02:20:36 +04:00
} ;
static struct svc_stat nfs4_callback_stats ;
static struct svc_program nfs4_callback_program = {
. pg_prog = NFS4_CALLBACK , /* RPC service number */
. pg_nvers = ARRAY_SIZE ( nfs4_callback_version ) , /* Number of entries */
. pg_vers = nfs4_callback_version , /* version table */
. pg_name = " NFSv4 callback " , /* service name */
. pg_class = " nfs " , /* authentication class */
. pg_stats = & nfs4_callback_stats ,
. pg_authenticate = nfs_callback_authenticate ,
} ;