1 /* 2 * linux/fs/nfs/callback.c 3 * 4 * Copyright (C) 2004 Trond Myklebust 5 * 6 * NFSv4 callback handling 7 */ 8 9 #include <linux/completion.h> 10 #include <linux/ip.h> 11 #include <linux/module.h> 12 #include <linux/sunrpc/svc.h> 13 #include <linux/sunrpc/svcsock.h> 14 #include <linux/nfs_fs.h> 15 #include <linux/mutex.h> 16 #include <linux/freezer.h> 17 #include <linux/kthread.h> 18 #include <linux/sunrpc/svcauth_gss.h> 19 #include <linux/sunrpc/bc_xprt.h> 20 21 #include <net/inet_sock.h> 22 23 #include "nfs4_fs.h" 24 #include "callback.h" 25 #include "internal.h" 26 27 #define NFSDBG_FACILITY NFSDBG_CALLBACK 28 29 struct nfs_callback_data { 30 unsigned int users; 31 struct svc_serv *serv; 32 struct svc_rqst *rqst; 33 struct task_struct *task; 34 }; 35 36 static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1]; 37 static DEFINE_MUTEX(nfs_callback_mutex); 38 static struct svc_program nfs4_callback_program; 39 40 unsigned short nfs_callback_tcpport6; 41 42 /* 43 * This is the NFSv4 callback kernel thread. 44 */ 45 static int 46 nfs4_callback_svc(void *vrqstp) 47 { 48 int err, preverr = 0; 49 struct svc_rqst *rqstp = vrqstp; 50 51 set_freezable(); 52 53 while (!kthread_should_stop()) { 54 /* 55 * Listen for a request on the socket 56 */ 57 err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT); 58 if (err == -EAGAIN || err == -EINTR) { 59 preverr = err; 60 continue; 61 } 62 if (err < 0) { 63 if (err != preverr) { 64 printk(KERN_WARNING "NFS: %s: unexpected error " 65 "from svc_recv (%d)\n", __func__, err); 66 preverr = err; 67 } 68 schedule_timeout_uninterruptible(HZ); 69 continue; 70 } 71 preverr = err; 72 svc_process(rqstp); 73 } 74 return 0; 75 } 76 77 /* 78 * Prepare to bring up the NFSv4 callback service 79 */ 80 static struct svc_rqst * 81 nfs4_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) 82 { 83 int ret; 84 85 ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET, 86 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); 87 if (ret <= 0) 88 goto out_err; 89 nfs_callback_tcpport = ret; 90 dprintk("NFS: Callback listener port = %u (af %u)\n", 91 nfs_callback_tcpport, PF_INET); 92 93 ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET6, 94 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); 95 if (ret > 0) { 96 nfs_callback_tcpport6 = ret; 97 dprintk("NFS: Callback listener port = %u (af %u)\n", 98 nfs_callback_tcpport6, PF_INET6); 99 } else if (ret == -EAFNOSUPPORT) 100 ret = 0; 101 else 102 goto out_err; 103 104 return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE); 105 106 out_err: 107 if (ret == 0) 108 ret = -ENOMEM; 109 return ERR_PTR(ret); 110 } 111 112 #if defined(CONFIG_NFS_V4_1) 113 /* 114 * The callback service for NFSv4.1 callbacks 115 */ 116 static int 117 nfs41_callback_svc(void *vrqstp) 118 { 119 struct svc_rqst *rqstp = vrqstp; 120 struct svc_serv *serv = rqstp->rq_server; 121 struct rpc_rqst *req; 122 int error; 123 DEFINE_WAIT(wq); 124 125 set_freezable(); 126 127 while (!kthread_should_stop()) { 128 prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE); 129 spin_lock_bh(&serv->sv_cb_lock); 130 if (!list_empty(&serv->sv_cb_list)) { 131 req = list_first_entry(&serv->sv_cb_list, 132 struct rpc_rqst, rq_bc_list); 133 list_del(&req->rq_bc_list); 134 spin_unlock_bh(&serv->sv_cb_lock); 135 dprintk("Invoking bc_svc_process()\n"); 136 error = bc_svc_process(serv, req, rqstp); 137 dprintk("bc_svc_process() returned w/ error code= %d\n", 138 error); 139 } else { 140 spin_unlock_bh(&serv->sv_cb_lock); 141 schedule(); 142 } 143 finish_wait(&serv->sv_cb_waitq, &wq); 144 } 145 return 0; 146 } 147 148 /* 149 * Bring up the NFSv4.1 callback service 150 */ 151 static struct svc_rqst * 152 nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) 153 { 154 struct svc_rqst *rqstp; 155 int ret; 156 157 /* 158 * Create an svc_sock for the back channel service that shares the 159 * fore channel connection. 160 * Returns the input port (0) and sets the svc_serv bc_xprt on success 161 */ 162 ret = svc_create_xprt(serv, "tcp-bc", &init_net, PF_INET, 0, 163 SVC_SOCK_ANONYMOUS); 164 if (ret < 0) { 165 rqstp = ERR_PTR(ret); 166 goto out; 167 } 168 169 /* 170 * Save the svc_serv in the transport so that it can 171 * be referenced when the session backchannel is initialized 172 */ 173 xprt->bc_serv = serv; 174 175 INIT_LIST_HEAD(&serv->sv_cb_list); 176 spin_lock_init(&serv->sv_cb_lock); 177 init_waitqueue_head(&serv->sv_cb_waitq); 178 rqstp = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE); 179 if (IS_ERR(rqstp)) { 180 svc_xprt_put(serv->sv_bc_xprt); 181 serv->sv_bc_xprt = NULL; 182 } 183 out: 184 dprintk("--> %s return %ld\n", __func__, 185 IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0); 186 return rqstp; 187 } 188 189 static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, 190 struct svc_serv *serv, struct rpc_xprt *xprt, 191 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) 192 { 193 if (minorversion) { 194 *rqstpp = nfs41_callback_up(serv, xprt); 195 *callback_svc = nfs41_callback_svc; 196 } 197 return minorversion; 198 } 199 200 static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, 201 struct nfs_callback_data *cb_info) 202 { 203 if (minorversion) 204 xprt->bc_serv = cb_info->serv; 205 } 206 #else 207 static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, 208 struct svc_serv *serv, struct rpc_xprt *xprt, 209 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) 210 { 211 return 0; 212 } 213 214 static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, 215 struct nfs_callback_data *cb_info) 216 { 217 } 218 #endif /* CONFIG_NFS_V4_1 */ 219 220 /* 221 * Bring up the callback thread if it is not already up. 222 */ 223 int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) 224 { 225 struct svc_serv *serv = NULL; 226 struct svc_rqst *rqstp; 227 int (*callback_svc)(void *vrqstp); 228 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; 229 char svc_name[12]; 230 int ret = 0; 231 int minorversion_setup; 232 struct net *net = &init_net; 233 234 mutex_lock(&nfs_callback_mutex); 235 if (cb_info->users++ || cb_info->task != NULL) { 236 nfs_callback_bc_serv(minorversion, xprt, cb_info); 237 goto out; 238 } 239 serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); 240 if (!serv) { 241 ret = -ENOMEM; 242 goto out_err; 243 } 244 /* As there is only one thread we need to over-ride the 245 * default maximum of 80 connections 246 */ 247 serv->sv_maxconn = 1024; 248 249 ret = svc_bind(serv, net); 250 if (ret < 0) { 251 printk(KERN_WARNING "NFS: bind callback service failed\n"); 252 goto out_err; 253 } 254 255 minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion, 256 serv, xprt, &rqstp, &callback_svc); 257 if (!minorversion_setup) { 258 /* v4.0 callback setup */ 259 rqstp = nfs4_callback_up(serv, xprt); 260 callback_svc = nfs4_callback_svc; 261 } 262 263 if (IS_ERR(rqstp)) { 264 ret = PTR_ERR(rqstp); 265 goto out_err; 266 } 267 268 svc_sock_update_bufs(serv); 269 270 sprintf(svc_name, "nfsv4.%u-svc", minorversion); 271 cb_info->serv = serv; 272 cb_info->rqst = rqstp; 273 cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name); 274 if (IS_ERR(cb_info->task)) { 275 ret = PTR_ERR(cb_info->task); 276 svc_exit_thread(cb_info->rqst); 277 cb_info->rqst = NULL; 278 cb_info->task = NULL; 279 goto out_err; 280 } 281 out: 282 /* 283 * svc_create creates the svc_serv with sv_nrthreads == 1, and then 284 * svc_prepare_thread increments that. So we need to call svc_destroy 285 * on both success and failure so that the refcount is 1 when the 286 * thread exits. 287 */ 288 if (serv) 289 svc_destroy(serv); 290 mutex_unlock(&nfs_callback_mutex); 291 return ret; 292 out_err: 293 dprintk("NFS: Couldn't create callback socket or server thread; " 294 "err = %d\n", ret); 295 cb_info->users--; 296 if (serv) 297 svc_shutdown_net(serv, net); 298 goto out; 299 } 300 301 /* 302 * Kill the callback thread if it's no longer being used. 303 */ 304 void nfs_callback_down(int minorversion) 305 { 306 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; 307 308 mutex_lock(&nfs_callback_mutex); 309 cb_info->users--; 310 if (cb_info->users == 0 && cb_info->task != NULL) { 311 kthread_stop(cb_info->task); 312 svc_shutdown_net(cb_info->serv, &init_net); 313 svc_exit_thread(cb_info->rqst); 314 cb_info->serv = NULL; 315 cb_info->rqst = NULL; 316 cb_info->task = NULL; 317 } 318 mutex_unlock(&nfs_callback_mutex); 319 } 320 321 /* Boolean check of RPC_AUTH_GSS principal */ 322 int 323 check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp) 324 { 325 char *p = rqstp->rq_cred.cr_principal; 326 327 if (rqstp->rq_authop->flavour != RPC_AUTH_GSS) 328 return 1; 329 330 /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */ 331 if (clp->cl_minorversion != 0) 332 return 0; 333 /* 334 * It might just be a normal user principal, in which case 335 * userspace won't bother to tell us the name at all. 336 */ 337 if (p == NULL) 338 return 0; 339 340 /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */ 341 342 if (memcmp(p, "nfs@", 4) != 0) 343 return 0; 344 p += 4; 345 if (strcmp(p, clp->cl_hostname) != 0) 346 return 0; 347 return 1; 348 } 349 350 /* 351 * pg_authenticate method for nfsv4 callback threads. 352 * 353 * The authflavor has been negotiated, so an incorrect flavor is a server 354 * bug. Drop packets with incorrect authflavor. 355 * 356 * All other checking done after NFS decoding where the nfs_client can be 357 * found in nfs4_callback_compound 358 */ 359 static int nfs_callback_authenticate(struct svc_rqst *rqstp) 360 { 361 switch (rqstp->rq_authop->flavour) { 362 case RPC_AUTH_NULL: 363 if (rqstp->rq_proc != CB_NULL) 364 return SVC_DROP; 365 break; 366 case RPC_AUTH_GSS: 367 /* No RPC_AUTH_GSS support yet in NFSv4.1 */ 368 if (svc_is_backchannel(rqstp)) 369 return SVC_DROP; 370 } 371 return SVC_OK; 372 } 373 374 /* 375 * Define NFS4 callback program 376 */ 377 static struct svc_version *nfs4_callback_version[] = { 378 [1] = &nfs4_callback_version1, 379 [4] = &nfs4_callback_version4, 380 }; 381 382 static struct svc_stat nfs4_callback_stats; 383 384 static struct svc_program nfs4_callback_program = { 385 .pg_prog = NFS4_CALLBACK, /* RPC service number */ 386 .pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */ 387 .pg_vers = nfs4_callback_version, /* version table */ 388 .pg_name = "NFSv4 callback", /* service name */ 389 .pg_class = "nfs", /* authentication class */ 390 .pg_stats = &nfs4_callback_stats, 391 .pg_authenticate = nfs_callback_authenticate, 392 }; 393