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 int nfs_callback_set_tcpport; 41 unsigned short nfs_callback_tcpport; 42 unsigned short nfs_callback_tcpport6; 43 #define NFS_CALLBACK_MAXPORTNR (65535U) 44 45 static int param_set_portnr(const char *val, const struct kernel_param *kp) 46 { 47 unsigned long num; 48 int ret; 49 50 if (!val) 51 return -EINVAL; 52 ret = strict_strtoul(val, 0, &num); 53 if (ret == -EINVAL || num > NFS_CALLBACK_MAXPORTNR) 54 return -EINVAL; 55 *((unsigned int *)kp->arg) = num; 56 return 0; 57 } 58 static struct kernel_param_ops param_ops_portnr = { 59 .set = param_set_portnr, 60 .get = param_get_uint, 61 }; 62 #define param_check_portnr(name, p) __param_check(name, p, unsigned int); 63 64 module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644); 65 66 /* 67 * This is the NFSv4 callback kernel thread. 68 */ 69 static int 70 nfs4_callback_svc(void *vrqstp) 71 { 72 int err, preverr = 0; 73 struct svc_rqst *rqstp = vrqstp; 74 75 set_freezable(); 76 77 while (!kthread_should_stop()) { 78 /* 79 * Listen for a request on the socket 80 */ 81 err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT); 82 if (err == -EAGAIN || err == -EINTR) { 83 preverr = err; 84 continue; 85 } 86 if (err < 0) { 87 if (err != preverr) { 88 printk(KERN_WARNING "%s: unexpected error " 89 "from svc_recv (%d)\n", __func__, err); 90 preverr = err; 91 } 92 schedule_timeout_uninterruptible(HZ); 93 continue; 94 } 95 preverr = err; 96 svc_process(rqstp); 97 } 98 return 0; 99 } 100 101 /* 102 * Prepare to bring up the NFSv4 callback service 103 */ 104 struct svc_rqst * 105 nfs4_callback_up(struct svc_serv *serv) 106 { 107 int ret; 108 109 ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET, 110 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); 111 if (ret <= 0) 112 goto out_err; 113 nfs_callback_tcpport = ret; 114 dprintk("NFS: Callback listener port = %u (af %u)\n", 115 nfs_callback_tcpport, PF_INET); 116 117 ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET6, 118 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); 119 if (ret > 0) { 120 nfs_callback_tcpport6 = ret; 121 dprintk("NFS: Callback listener port = %u (af %u)\n", 122 nfs_callback_tcpport6, PF_INET6); 123 } else if (ret == -EAFNOSUPPORT) 124 ret = 0; 125 else 126 goto out_err; 127 128 return svc_prepare_thread(serv, &serv->sv_pools[0]); 129 130 out_err: 131 if (ret == 0) 132 ret = -ENOMEM; 133 return ERR_PTR(ret); 134 } 135 136 #if defined(CONFIG_NFS_V4_1) 137 /* 138 * * CB_SEQUENCE operations will fail until the callback sessionid is set. 139 * */ 140 int nfs4_set_callback_sessionid(struct nfs_client *clp) 141 { 142 struct svc_serv *serv = clp->cl_rpcclient->cl_xprt->bc_serv; 143 struct nfs4_sessionid *bc_sid; 144 145 if (!serv->sv_bc_xprt) 146 return -EINVAL; 147 148 /* on success freed in xprt_free */ 149 bc_sid = kmalloc(sizeof(struct nfs4_sessionid), GFP_KERNEL); 150 if (!bc_sid) 151 return -ENOMEM; 152 memcpy(bc_sid->data, &clp->cl_session->sess_id.data, 153 NFS4_MAX_SESSIONID_LEN); 154 spin_lock_bh(&serv->sv_cb_lock); 155 serv->sv_bc_xprt->xpt_bc_sid = bc_sid; 156 spin_unlock_bh(&serv->sv_cb_lock); 157 dprintk("%s set xpt_bc_sid=%u:%u:%u:%u for sv_bc_xprt %p\n", __func__, 158 ((u32 *)bc_sid->data)[0], ((u32 *)bc_sid->data)[1], 159 ((u32 *)bc_sid->data)[2], ((u32 *)bc_sid->data)[3], 160 serv->sv_bc_xprt); 161 return 0; 162 } 163 164 /* 165 * The callback service for NFSv4.1 callbacks 166 */ 167 static int 168 nfs41_callback_svc(void *vrqstp) 169 { 170 struct svc_rqst *rqstp = vrqstp; 171 struct svc_serv *serv = rqstp->rq_server; 172 struct rpc_rqst *req; 173 int error; 174 DEFINE_WAIT(wq); 175 176 set_freezable(); 177 178 while (!kthread_should_stop()) { 179 prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE); 180 spin_lock_bh(&serv->sv_cb_lock); 181 if (!list_empty(&serv->sv_cb_list)) { 182 req = list_first_entry(&serv->sv_cb_list, 183 struct rpc_rqst, rq_bc_list); 184 list_del(&req->rq_bc_list); 185 spin_unlock_bh(&serv->sv_cb_lock); 186 dprintk("Invoking bc_svc_process()\n"); 187 error = bc_svc_process(serv, req, rqstp); 188 dprintk("bc_svc_process() returned w/ error code= %d\n", 189 error); 190 } else { 191 spin_unlock_bh(&serv->sv_cb_lock); 192 schedule(); 193 } 194 finish_wait(&serv->sv_cb_waitq, &wq); 195 } 196 return 0; 197 } 198 199 /* 200 * Bring up the NFSv4.1 callback service 201 */ 202 struct svc_rqst * 203 nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) 204 { 205 struct svc_rqst *rqstp; 206 int ret; 207 208 /* 209 * Create an svc_sock for the back channel service that shares the 210 * fore channel connection. 211 * Returns the input port (0) and sets the svc_serv bc_xprt on success 212 */ 213 ret = svc_create_xprt(serv, "tcp-bc", &init_net, PF_INET, 0, 214 SVC_SOCK_ANONYMOUS); 215 if (ret < 0) { 216 rqstp = ERR_PTR(ret); 217 goto out; 218 } 219 220 /* 221 * Save the svc_serv in the transport so that it can 222 * be referenced when the session backchannel is initialized 223 */ 224 xprt->bc_serv = serv; 225 226 INIT_LIST_HEAD(&serv->sv_cb_list); 227 spin_lock_init(&serv->sv_cb_lock); 228 init_waitqueue_head(&serv->sv_cb_waitq); 229 rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); 230 if (IS_ERR(rqstp)) { 231 svc_xprt_put(serv->sv_bc_xprt); 232 serv->sv_bc_xprt = NULL; 233 } 234 out: 235 dprintk("--> %s return %ld\n", __func__, 236 IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0); 237 return rqstp; 238 } 239 240 static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, 241 struct svc_serv *serv, struct rpc_xprt *xprt, 242 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) 243 { 244 if (minorversion) { 245 *rqstpp = nfs41_callback_up(serv, xprt); 246 *callback_svc = nfs41_callback_svc; 247 } 248 return minorversion; 249 } 250 251 static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, 252 struct nfs_callback_data *cb_info) 253 { 254 if (minorversion) 255 xprt->bc_serv = cb_info->serv; 256 } 257 #else 258 static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, 259 struct svc_serv *serv, struct rpc_xprt *xprt, 260 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) 261 { 262 return 0; 263 } 264 265 static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, 266 struct nfs_callback_data *cb_info) 267 { 268 } 269 int nfs4_set_callback_sessionid(struct nfs_client *clp) 270 { 271 return 0; 272 } 273 #endif /* CONFIG_NFS_V4_1 */ 274 275 /* 276 * Bring up the callback thread if it is not already up. 277 */ 278 int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) 279 { 280 struct svc_serv *serv = NULL; 281 struct svc_rqst *rqstp; 282 int (*callback_svc)(void *vrqstp); 283 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; 284 char svc_name[12]; 285 int ret = 0; 286 int minorversion_setup; 287 288 mutex_lock(&nfs_callback_mutex); 289 if (cb_info->users++ || cb_info->task != NULL) { 290 nfs_callback_bc_serv(minorversion, xprt, cb_info); 291 goto out; 292 } 293 serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); 294 if (!serv) { 295 ret = -ENOMEM; 296 goto out_err; 297 } 298 299 minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion, 300 serv, xprt, &rqstp, &callback_svc); 301 if (!minorversion_setup) { 302 /* v4.0 callback setup */ 303 rqstp = nfs4_callback_up(serv); 304 callback_svc = nfs4_callback_svc; 305 } 306 307 if (IS_ERR(rqstp)) { 308 ret = PTR_ERR(rqstp); 309 goto out_err; 310 } 311 312 svc_sock_update_bufs(serv); 313 314 sprintf(svc_name, "nfsv4.%u-svc", minorversion); 315 cb_info->serv = serv; 316 cb_info->rqst = rqstp; 317 cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name); 318 if (IS_ERR(cb_info->task)) { 319 ret = PTR_ERR(cb_info->task); 320 svc_exit_thread(cb_info->rqst); 321 cb_info->rqst = NULL; 322 cb_info->task = NULL; 323 goto out_err; 324 } 325 out: 326 /* 327 * svc_create creates the svc_serv with sv_nrthreads == 1, and then 328 * svc_prepare_thread increments that. So we need to call svc_destroy 329 * on both success and failure so that the refcount is 1 when the 330 * thread exits. 331 */ 332 if (serv) 333 svc_destroy(serv); 334 mutex_unlock(&nfs_callback_mutex); 335 return ret; 336 out_err: 337 dprintk("NFS: Couldn't create callback socket or server thread; " 338 "err = %d\n", ret); 339 cb_info->users--; 340 goto out; 341 } 342 343 /* 344 * Kill the callback thread if it's no longer being used. 345 */ 346 void nfs_callback_down(int minorversion) 347 { 348 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; 349 350 mutex_lock(&nfs_callback_mutex); 351 cb_info->users--; 352 if (cb_info->users == 0 && cb_info->task != NULL) { 353 kthread_stop(cb_info->task); 354 svc_exit_thread(cb_info->rqst); 355 cb_info->serv = NULL; 356 cb_info->rqst = NULL; 357 cb_info->task = NULL; 358 } 359 mutex_unlock(&nfs_callback_mutex); 360 } 361 362 static int check_gss_callback_principal(struct nfs_client *clp, 363 struct svc_rqst *rqstp) 364 { 365 struct rpc_clnt *r = clp->cl_rpcclient; 366 char *p = svc_gss_principal(rqstp); 367 368 /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */ 369 if (clp->cl_minorversion != 0) 370 return SVC_DROP; 371 /* 372 * It might just be a normal user principal, in which case 373 * userspace won't bother to tell us the name at all. 374 */ 375 if (p == NULL) 376 return SVC_DENIED; 377 378 /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */ 379 380 if (memcmp(p, "nfs@", 4) != 0) 381 return SVC_DENIED; 382 p += 4; 383 if (strcmp(p, r->cl_server) != 0) 384 return SVC_DENIED; 385 return SVC_OK; 386 } 387 388 /* pg_authenticate method helper */ 389 static struct nfs_client *nfs_cb_find_client(struct svc_rqst *rqstp) 390 { 391 struct nfs4_sessionid *sessionid = bc_xprt_sid(rqstp); 392 int is_cb_compound = rqstp->rq_proc == CB_COMPOUND ? 1 : 0; 393 394 dprintk("--> %s rq_proc %d\n", __func__, rqstp->rq_proc); 395 if (svc_is_backchannel(rqstp)) 396 /* Sessionid (usually) set after CB_NULL ping */ 397 return nfs4_find_client_sessionid(svc_addr(rqstp), sessionid, 398 is_cb_compound); 399 else 400 /* No callback identifier in pg_authenticate */ 401 return nfs4_find_client_no_ident(svc_addr(rqstp)); 402 } 403 404 /* pg_authenticate method for nfsv4 callback threads. */ 405 static int nfs_callback_authenticate(struct svc_rqst *rqstp) 406 { 407 struct nfs_client *clp; 408 RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); 409 int ret = SVC_OK; 410 411 /* Don't talk to strangers */ 412 clp = nfs_cb_find_client(rqstp); 413 if (clp == NULL) 414 return SVC_DROP; 415 416 dprintk("%s: %s NFSv4 callback!\n", __func__, 417 svc_print_addr(rqstp, buf, sizeof(buf))); 418 419 switch (rqstp->rq_authop->flavour) { 420 case RPC_AUTH_NULL: 421 if (rqstp->rq_proc != CB_NULL) 422 ret = SVC_DENIED; 423 break; 424 case RPC_AUTH_UNIX: 425 break; 426 case RPC_AUTH_GSS: 427 ret = check_gss_callback_principal(clp, rqstp); 428 break; 429 default: 430 ret = SVC_DENIED; 431 } 432 nfs_put_client(clp); 433 return ret; 434 } 435 436 /* 437 * Define NFS4 callback program 438 */ 439 static struct svc_version *nfs4_callback_version[] = { 440 [1] = &nfs4_callback_version1, 441 [4] = &nfs4_callback_version4, 442 }; 443 444 static struct svc_stat nfs4_callback_stats; 445 446 static struct svc_program nfs4_callback_program = { 447 .pg_prog = NFS4_CALLBACK, /* RPC service number */ 448 .pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */ 449 .pg_vers = nfs4_callback_version, /* version table */ 450 .pg_name = "NFSv4 callback", /* service name */ 451 .pg_class = "nfs", /* authentication class */ 452 .pg_stats = &nfs4_callback_stats, 453 .pg_authenticate = nfs_callback_authenticate, 454 }; 455