Branch data Line data Source code
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/errno.h>
16 : : #include <linux/mutex.h>
17 : : #include <linux/freezer.h>
18 : : #include <linux/kthread.h>
19 : : #include <linux/sunrpc/svcauth_gss.h>
20 : : #include <linux/sunrpc/bc_xprt.h>
21 : :
22 : : #include <net/inet_sock.h>
23 : :
24 : : #include "nfs4_fs.h"
25 : : #include "callback.h"
26 : : #include "internal.h"
27 : : #include "netns.h"
28 : :
29 : : #define NFSDBG_FACILITY NFSDBG_CALLBACK
30 : :
31 : : struct nfs_callback_data {
32 : : unsigned int users;
33 : : struct svc_serv *serv;
34 : : struct svc_rqst *rqst;
35 : : struct task_struct *task;
36 : : };
37 : :
38 : : static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
39 : : static DEFINE_MUTEX(nfs_callback_mutex);
40 : : static struct svc_program nfs4_callback_program;
41 : :
42 : 0 : static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
43 : : {
44 : : int ret;
45 : 0 : struct nfs_net *nn = net_generic(net, nfs_net_id);
46 : :
47 : 0 : ret = svc_create_xprt(serv, "tcp", net, PF_INET,
48 : : nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
49 [ # # ]: 0 : if (ret <= 0)
50 : : goto out_err;
51 : 0 : nn->nfs_callback_tcpport = ret;
52 : : dprintk("NFS: Callback listener port = %u (af %u, net %p)\n",
53 : : nn->nfs_callback_tcpport, PF_INET, net);
54 : :
55 : 0 : ret = svc_create_xprt(serv, "tcp", net, PF_INET6,
56 : : nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
57 [ # # ]: 0 : if (ret > 0) {
58 : 0 : nn->nfs_callback_tcpport6 = ret;
59 : : dprintk("NFS: Callback listener port = %u (af %u, net %p)\n",
60 : : nn->nfs_callback_tcpport6, PF_INET6, net);
61 [ # # ]: 0 : } else if (ret != -EAFNOSUPPORT)
62 : : goto out_err;
63 : : return 0;
64 : :
65 : : out_err:
66 [ # # ]: 0 : return (ret) ? ret : -ENOMEM;
67 : : }
68 : :
69 : : /*
70 : : * This is the NFSv4 callback kernel thread.
71 : : */
72 : : static int
73 : 0 : nfs4_callback_svc(void *vrqstp)
74 : : {
75 : : int err;
76 : : struct svc_rqst *rqstp = vrqstp;
77 : :
78 : 0 : set_freezable();
79 : :
80 [ # # ]: 0 : while (!kthread_should_stop()) {
81 : : /*
82 : : * Listen for a request on the socket
83 : : */
84 : 0 : err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
85 [ # # ]: 0 : if (err == -EAGAIN || err == -EINTR)
86 : 0 : continue;
87 : 0 : svc_process(rqstp);
88 : : }
89 : 0 : return 0;
90 : : }
91 : :
92 : : /*
93 : : * Prepare to bring up the NFSv4 callback service
94 : : */
95 : : static struct svc_rqst *
96 : : nfs4_callback_up(struct svc_serv *serv)
97 : : {
98 : 0 : return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
99 : : }
100 : :
101 : : #if defined(CONFIG_NFS_V4_1)
102 : : static int nfs41_callback_up_net(struct svc_serv *serv, struct net *net)
103 : : {
104 : : /*
105 : : * Create an svc_sock for the back channel service that shares the
106 : : * fore channel connection.
107 : : * Returns the input port (0) and sets the svc_serv bc_xprt on success
108 : : */
109 : : return svc_create_xprt(serv, "tcp-bc", net, PF_INET, 0,
110 : : SVC_SOCK_ANONYMOUS);
111 : : }
112 : :
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 : : if (try_to_freeze())
129 : : continue;
130 : :
131 : : prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
132 : : spin_lock_bh(&serv->sv_cb_lock);
133 : : if (!list_empty(&serv->sv_cb_list)) {
134 : : req = list_first_entry(&serv->sv_cb_list,
135 : : struct rpc_rqst, rq_bc_list);
136 : : list_del(&req->rq_bc_list);
137 : : spin_unlock_bh(&serv->sv_cb_lock);
138 : : dprintk("Invoking bc_svc_process()\n");
139 : : error = bc_svc_process(serv, req, rqstp);
140 : : dprintk("bc_svc_process() returned w/ error code= %d\n",
141 : : error);
142 : : } else {
143 : : spin_unlock_bh(&serv->sv_cb_lock);
144 : : schedule();
145 : : }
146 : : finish_wait(&serv->sv_cb_waitq, &wq);
147 : : }
148 : : return 0;
149 : : }
150 : :
151 : : /*
152 : : * Bring up the NFSv4.1 callback service
153 : : */
154 : : static struct svc_rqst *
155 : : nfs41_callback_up(struct svc_serv *serv)
156 : : {
157 : : struct svc_rqst *rqstp;
158 : :
159 : : INIT_LIST_HEAD(&serv->sv_cb_list);
160 : : spin_lock_init(&serv->sv_cb_lock);
161 : : init_waitqueue_head(&serv->sv_cb_waitq);
162 : : rqstp = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
163 : : if (IS_ERR(rqstp)) {
164 : : svc_xprt_put(serv->sv_bc_xprt);
165 : : serv->sv_bc_xprt = NULL;
166 : : }
167 : : dprintk("--> %s return %d\n", __func__, PTR_ERR_OR_ZERO(rqstp));
168 : : return rqstp;
169 : : }
170 : :
171 : : static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
172 : : struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
173 : : {
174 : : *rqstpp = nfs41_callback_up(serv);
175 : : *callback_svc = nfs41_callback_svc;
176 : : }
177 : :
178 : : static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
179 : : struct svc_serv *serv)
180 : : {
181 : : if (minorversion)
182 : : /*
183 : : * Save the svc_serv in the transport so that it can
184 : : * be referenced when the session backchannel is initialized
185 : : */
186 : : xprt->bc_serv = serv;
187 : : }
188 : : #else
189 : : static int nfs41_callback_up_net(struct svc_serv *serv, struct net *net)
190 : : {
191 : : return 0;
192 : : }
193 : :
194 : : static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
195 : : struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
196 : : {
197 : : *rqstpp = ERR_PTR(-ENOTSUPP);
198 : : *callback_svc = ERR_PTR(-ENOTSUPP);
199 : : }
200 : :
201 : : static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
202 : : struct svc_serv *serv)
203 : : {
204 : : }
205 : : #endif /* CONFIG_NFS_V4_1 */
206 : :
207 : 0 : static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
208 : : struct svc_serv *serv)
209 : : {
210 : : struct svc_rqst *rqstp;
211 : : int (*callback_svc)(void *vrqstp);
212 : 0 : struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
213 : : int ret;
214 : :
215 : : nfs_callback_bc_serv(minorversion, xprt, serv);
216 : :
217 [ # # ]: 0 : if (cb_info->task)
218 : : return 0;
219 : :
220 [ # # ]: 0 : switch (minorversion) {
221 : : case 0:
222 : : /* v4.0 callback setup */
223 : : rqstp = nfs4_callback_up(serv);
224 : : callback_svc = nfs4_callback_svc;
225 : : break;
226 : : default:
227 : : nfs_minorversion_callback_svc_setup(serv,
228 : : &rqstp, &callback_svc);
229 : : }
230 : :
231 [ # # ]: 0 : if (IS_ERR(rqstp))
232 : : return PTR_ERR(rqstp);
233 : :
234 : 0 : svc_sock_update_bufs(serv);
235 : :
236 : 0 : cb_info->serv = serv;
237 : 0 : cb_info->rqst = rqstp;
238 [ # # ]: 0 : cb_info->task = kthread_run(callback_svc, cb_info->rqst,
239 : : "nfsv4.%u-svc", minorversion);
240 [ # # ]: 0 : if (IS_ERR(cb_info->task)) {
241 : : ret = PTR_ERR(cb_info->task);
242 : 0 : svc_exit_thread(cb_info->rqst);
243 : 0 : cb_info->rqst = NULL;
244 : 0 : cb_info->task = NULL;
245 : : return ret;
246 : : }
247 : : dprintk("nfs_callback_up: service started\n");
248 : : return 0;
249 : : }
250 : :
251 : 0 : static void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struct net *net)
252 : : {
253 : 0 : struct nfs_net *nn = net_generic(net, nfs_net_id);
254 : :
255 [ # # ]: 0 : if (--nn->cb_users[minorversion])
256 : 0 : return;
257 : :
258 : : dprintk("NFS: destroy per-net callback data; net=%p\n", net);
259 : 0 : svc_shutdown_net(serv, net);
260 : : }
261 : :
262 : 0 : static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, struct net *net)
263 : : {
264 : 0 : struct nfs_net *nn = net_generic(net, nfs_net_id);
265 : : int ret;
266 : :
267 [ # # ]: 0 : if (nn->cb_users[minorversion]++)
268 : : return 0;
269 : :
270 : : dprintk("NFS: create per-net callback data; net=%p\n", net);
271 : :
272 : 0 : ret = svc_bind(serv, net);
273 [ # # ]: 0 : if (ret < 0) {
274 : 0 : printk(KERN_WARNING "NFS: bind callback service failed\n");
275 : 0 : goto err_bind;
276 : : }
277 : :
278 [ # # # ]: 0 : switch (minorversion) {
279 : : case 0:
280 : 0 : ret = nfs4_callback_up_net(serv, net);
281 : 0 : break;
282 : : case 1:
283 : : case 2:
284 : : ret = nfs41_callback_up_net(serv, net);
285 : : break;
286 : : default:
287 : 0 : printk(KERN_ERR "NFS: unknown callback version: %d\n",
288 : : minorversion);
289 : : ret = -EINVAL;
290 : 0 : break;
291 : : }
292 : :
293 [ # # ]: 0 : if (ret < 0) {
294 : 0 : printk(KERN_ERR "NFS: callback service start failed\n");
295 : : goto err_socks;
296 : : }
297 : : return 0;
298 : :
299 : : err_socks:
300 : 0 : svc_rpcb_cleanup(serv, net);
301 : : err_bind:
302 : : dprintk("NFS: Couldn't create callback socket: err = %d; "
303 : : "net = %p\n", ret, net);
304 : 0 : return ret;
305 : : }
306 : :
307 : 0 : static struct svc_serv *nfs_callback_create_svc(int minorversion)
308 : : {
309 : 0 : struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
310 : : struct svc_serv *serv;
311 : :
312 : : /*
313 : : * Check whether we're already up and running.
314 : : */
315 [ # # ]: 0 : if (cb_info->task) {
316 : : /*
317 : : * Note: increase service usage, because later in case of error
318 : : * svc_destroy() will be called.
319 : : */
320 : 0 : svc_get(cb_info->serv);
321 : 0 : return cb_info->serv;
322 : : }
323 : :
324 : : /*
325 : : * Sanity check: if there's no task,
326 : : * we should be the first user ...
327 : : */
328 [ # # ]: 0 : if (cb_info->users)
329 : 0 : printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
330 : : cb_info->users);
331 : :
332 : 0 : serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
333 [ # # ]: 0 : if (!serv) {
334 : 0 : printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
335 : 0 : return ERR_PTR(-ENOMEM);
336 : : }
337 : : /* As there is only one thread we need to over-ride the
338 : : * default maximum of 80 connections
339 : : */
340 : 0 : serv->sv_maxconn = 1024;
341 : : dprintk("nfs_callback_create_svc: service created\n");
342 : 0 : return serv;
343 : : }
344 : :
345 : : /*
346 : : * Bring up the callback thread if it is not already up.
347 : : */
348 : 0 : int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
349 : : {
350 : : struct svc_serv *serv;
351 : 0 : struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
352 : : int ret;
353 : 0 : struct net *net = xprt->xprt_net;
354 : :
355 : 0 : mutex_lock(&nfs_callback_mutex);
356 : :
357 : 0 : serv = nfs_callback_create_svc(minorversion);
358 [ # # ]: 0 : if (IS_ERR(serv)) {
359 : : ret = PTR_ERR(serv);
360 : 0 : goto err_create;
361 : : }
362 : :
363 : 0 : ret = nfs_callback_up_net(minorversion, serv, net);
364 [ # # ]: 0 : if (ret < 0)
365 : : goto err_net;
366 : :
367 : 0 : ret = nfs_callback_start_svc(minorversion, xprt, serv);
368 [ # # ]: 0 : if (ret < 0)
369 : : goto err_start;
370 : :
371 : 0 : cb_info->users++;
372 : : /*
373 : : * svc_create creates the svc_serv with sv_nrthreads == 1, and then
374 : : * svc_prepare_thread increments that. So we need to call svc_destroy
375 : : * on both success and failure so that the refcount is 1 when the
376 : : * thread exits.
377 : : */
378 : : err_net:
379 : 0 : svc_destroy(serv);
380 : : err_create:
381 : 0 : mutex_unlock(&nfs_callback_mutex);
382 : 0 : return ret;
383 : :
384 : : err_start:
385 : 0 : nfs_callback_down_net(minorversion, serv, net);
386 : : dprintk("NFS: Couldn't create server thread; err = %d\n", ret);
387 : 0 : goto err_net;
388 : : }
389 : :
390 : : /*
391 : : * Kill the callback thread if it's no longer being used.
392 : : */
393 : 0 : void nfs_callback_down(int minorversion, struct net *net)
394 : : {
395 : 0 : struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
396 : :
397 : 0 : mutex_lock(&nfs_callback_mutex);
398 : 0 : nfs_callback_down_net(minorversion, cb_info->serv, net);
399 : 0 : cb_info->users--;
400 [ # # ][ # # ]: 0 : if (cb_info->users == 0 && cb_info->task != NULL) {
401 : 0 : kthread_stop(cb_info->task);
402 : : dprintk("nfs_callback_down: service stopped\n");
403 : 0 : svc_exit_thread(cb_info->rqst);
404 : : dprintk("nfs_callback_down: service destroyed\n");
405 : 0 : cb_info->serv = NULL;
406 : 0 : cb_info->rqst = NULL;
407 : 0 : cb_info->task = NULL;
408 : : }
409 : 0 : mutex_unlock(&nfs_callback_mutex);
410 : 0 : }
411 : :
412 : : /* Boolean check of RPC_AUTH_GSS principal */
413 : : int
414 : 0 : check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp)
415 : : {
416 : 0 : char *p = rqstp->rq_cred.cr_principal;
417 : :
418 [ # # ]: 0 : if (rqstp->rq_authop->flavour != RPC_AUTH_GSS)
419 : : return 1;
420 : :
421 : : /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */
422 [ # # ]: 0 : if (clp->cl_minorversion != 0)
423 : : return 0;
424 : : /*
425 : : * It might just be a normal user principal, in which case
426 : : * userspace won't bother to tell us the name at all.
427 : : */
428 [ # # ]: 0 : if (p == NULL)
429 : : return 0;
430 : :
431 : : /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
432 : :
433 [ # # ]: 0 : if (memcmp(p, "nfs@", 4) != 0)
434 : : return 0;
435 : 0 : p += 4;
436 [ # # ]: 0 : if (strcmp(p, clp->cl_hostname) != 0)
437 : : return 0;
438 : 0 : return 1;
439 : : }
440 : :
441 : : /*
442 : : * pg_authenticate method for nfsv4 callback threads.
443 : : *
444 : : * The authflavor has been negotiated, so an incorrect flavor is a server
445 : : * bug. Drop packets with incorrect authflavor.
446 : : *
447 : : * All other checking done after NFS decoding where the nfs_client can be
448 : : * found in nfs4_callback_compound
449 : : */
450 : 0 : static int nfs_callback_authenticate(struct svc_rqst *rqstp)
451 : : {
452 [ # # ]: 0 : switch (rqstp->rq_authop->flavour) {
453 : : case RPC_AUTH_NULL:
454 [ # # ]: 0 : if (rqstp->rq_proc != CB_NULL)
455 : : return SVC_DROP;
456 : : break;
457 : : case RPC_AUTH_GSS:
458 : : /* No RPC_AUTH_GSS support yet in NFSv4.1 */
459 : : if (svc_is_backchannel(rqstp))
460 : : return SVC_DROP;
461 : : }
462 : 0 : return SVC_OK;
463 : : }
464 : :
465 : : /*
466 : : * Define NFS4 callback program
467 : : */
468 : : static struct svc_version *nfs4_callback_version[] = {
469 : : [1] = &nfs4_callback_version1,
470 : : [4] = &nfs4_callback_version4,
471 : : };
472 : :
473 : : static struct svc_stat nfs4_callback_stats;
474 : :
475 : : static struct svc_program nfs4_callback_program = {
476 : : .pg_prog = NFS4_CALLBACK, /* RPC service number */
477 : : .pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */
478 : : .pg_vers = nfs4_callback_version, /* version table */
479 : : .pg_name = "NFSv4 callback", /* service name */
480 : : .pg_class = "nfs", /* authentication class */
481 : : .pg_stats = &nfs4_callback_stats,
482 : : .pg_authenticate = nfs_callback_authenticate,
483 : : };
|