callback.c 10.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * 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>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/nfs_fs.h>
I
Ingo Molnar 已提交
15
#include <linux/mutex.h>
16
#include <linux/freezer.h>
17
#include <linux/kthread.h>
18
#include <linux/sunrpc/svcauth_gss.h>
19 20 21
#if defined(CONFIG_NFS_V4_1)
#include <linux/sunrpc/bc_xprt.h>
#endif
22 23 24

#include <net/inet_sock.h>

25
#include "nfs4_fs.h"
L
Linus Torvalds 已提交
26
#include "callback.h"
27
#include "internal.h"
L
Linus Torvalds 已提交
28 29 30 31 32

#define NFSDBG_FACILITY NFSDBG_CALLBACK

struct nfs_callback_data {
	unsigned int users;
33
	struct svc_serv *serv;
34
	struct svc_rqst *rqst;
35
	struct task_struct *task;
L
Linus Torvalds 已提交
36 37
};

38
static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
I
Ingo Molnar 已提交
39
static DEFINE_MUTEX(nfs_callback_mutex);
L
Linus Torvalds 已提交
40 41
static struct svc_program nfs4_callback_program;

42
unsigned int nfs_callback_set_tcpport;
L
Linus Torvalds 已提交
43
unsigned short nfs_callback_tcpport;
44
unsigned short nfs_callback_tcpport6;
45
#define NFS_CALLBACK_MAXPORTNR (65535U)
46

47
static int param_set_portnr(const char *val, const struct kernel_param *kp)
48
{
49 50 51 52 53 54 55
	unsigned long num;
	int ret;

	if (!val)
		return -EINVAL;
	ret = strict_strtoul(val, 0, &num);
	if (ret == -EINVAL || num > NFS_CALLBACK_MAXPORTNR)
56
		return -EINVAL;
57
	*((unsigned int *)kp->arg) = num;
58 59
	return 0;
}
60 61 62 63
static struct kernel_param_ops param_ops_portnr = {
	.set = param_set_portnr,
	.get = param_get_uint,
};
64 65 66
#define param_check_portnr(name, p) __param_check(name, p, unsigned int);

module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644);
L
Linus Torvalds 已提交
67 68

/*
69
 * This is the NFSv4 callback kernel thread.
L
Linus Torvalds 已提交
70
 */
71
static int
72
nfs4_callback_svc(void *vrqstp)
L
Linus Torvalds 已提交
73
{
74
	int err, preverr = 0;
75
	struct svc_rqst *rqstp = vrqstp;
L
Linus Torvalds 已提交
76

77
	set_freezable();
L
Linus Torvalds 已提交
78

79
	while (!kthread_should_stop()) {
L
Linus Torvalds 已提交
80 81 82
		/*
		 * Listen for a request on the socket
		 */
83
		err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
84 85
		if (err == -EAGAIN || err == -EINTR) {
			preverr = err;
L
Linus Torvalds 已提交
86
			continue;
87
		}
L
Linus Torvalds 已提交
88
		if (err < 0) {
89 90 91 92 93 94 95
			if (err != preverr) {
				printk(KERN_WARNING "%s: unexpected error "
					"from svc_recv (%d)\n", __func__, err);
				preverr = err;
			}
			schedule_timeout_uninterruptible(HZ);
			continue;
L
Linus Torvalds 已提交
96
		}
97
		preverr = err;
98
		svc_process(rqstp);
L
Linus Torvalds 已提交
99
	}
100
	return 0;
L
Linus Torvalds 已提交
101 102 103
}

/*
104
 * Prepare to bring up the NFSv4 callback service
L
Linus Torvalds 已提交
105
 */
106 107
struct svc_rqst *
nfs4_callback_up(struct svc_serv *serv)
L
Linus Torvalds 已提交
108
{
109
	int ret;
110

111
	ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET,
112
				nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
113
	if (ret <= 0)
114
		goto out_err;
115
	nfs_callback_tcpport = ret;
116
	dprintk("NFS: Callback listener port = %u (af %u)\n",
117
			nfs_callback_tcpport, PF_INET);
118

119
	ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET6,
120 121 122 123 124
				nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
	if (ret > 0) {
		nfs_callback_tcpport6 = ret;
		dprintk("NFS: Callback listener port = %u (af %u)\n",
				nfs_callback_tcpport6, PF_INET6);
125 126 127
	} else if (ret == -EAFNOSUPPORT)
		ret = 0;
	else
128 129
		goto out_err;

130 131 132 133 134 135 136 137
	return svc_prepare_thread(serv, &serv->sv_pools[0]);

out_err:
	if (ret == 0)
		ret = -ENOMEM;
	return ERR_PTR(ret);
}

138
#if defined(CONFIG_NFS_V4_1)
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
/*
 *  * CB_SEQUENCE operations will fail until the callback sessionid is set.
 *   */
int nfs4_set_callback_sessionid(struct nfs_client *clp)
{
	struct svc_serv *serv = clp->cl_rpcclient->cl_xprt->bc_serv;
	struct nfs4_sessionid *bc_sid;

	if (!serv->bc_xprt)
		return -EINVAL;

	/* on success freed in xprt_free */
	bc_sid = kmalloc(sizeof(struct nfs4_sessionid), GFP_KERNEL);
	if (!bc_sid)
		return -ENOMEM;
	memcpy(bc_sid->data, &clp->cl_session->sess_id.data,
		NFS4_MAX_SESSIONID_LEN);
	spin_lock_bh(&serv->sv_cb_lock);
	serv->bc_xprt->xpt_bc_sid = bc_sid;
	spin_unlock_bh(&serv->sv_cb_lock);
	dprintk("%s set xpt_bc_sid=%u:%u:%u:%u for bc_xprt %p\n", __func__,
		((u32 *)bc_sid->data)[0], ((u32 *)bc_sid->data)[1],
		((u32 *)bc_sid->data)[2], ((u32 *)bc_sid->data)[3],
		serv->bc_xprt);
	return 0;
}

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
/*
 * 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()) {
		prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
		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);
			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);
			schedule();
		}
		finish_wait(&serv->sv_cb_waitq, &wq);
	}
	return 0;
}

/*
 * Bring up the NFSv4.1 callback service
 */
struct svc_rqst *
nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
{
207 208
	struct svc_rqst *rqstp;
	int ret;
209

210 211 212 213 214 215 216 217 218
	/*
	 * Create an svc_sock for the back channel service that shares the
	 * fore channel connection.
	 * Returns the input port (0) and sets the svc_serv bc_xprt on success
	 */
	ret = svc_create_xprt(serv, "tcp-bc", &init_net, PF_INET, 0,
			      SVC_SOCK_ANONYMOUS);
	if (ret < 0) {
		rqstp = ERR_PTR(ret);
219
		goto out;
220
	}
221

222 223 224 225 226 227 228 229 230
	/*
	 * Save the svc_serv in the transport so that it can
	 * be referenced when the session backchannel is initialized
	 */
	xprt->bc_serv = serv;

	INIT_LIST_HEAD(&serv->sv_cb_list);
	spin_lock_init(&serv->sv_cb_lock);
	init_waitqueue_head(&serv->sv_cb_waitq);
231
	rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
232 233 234 235
	if (IS_ERR(rqstp)) {
		svc_xprt_put(serv->bc_xprt);
		serv->bc_xprt = NULL;
	}
236
out:
237 238
	dprintk("--> %s return %ld\n", __func__,
		IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0);
239
	return rqstp;
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
}

static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
		struct svc_serv *serv, struct rpc_xprt *xprt,
		struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
{
	if (minorversion) {
		*rqstpp = nfs41_callback_up(serv, xprt);
		*callback_svc = nfs41_callback_svc;
	}
	return minorversion;
}

static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
		struct nfs_callback_data *cb_info)
{
	if (minorversion)
		xprt->bc_serv = cb_info->serv;
}
#else
static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
		struct svc_serv *serv, struct rpc_xprt *xprt,
		struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
{
	return 0;
}

static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
		struct nfs_callback_data *cb_info)
{
}
271 272 273 274
int nfs4_set_callback_sessionid(struct nfs_client *clp)
{
	return 0;
}
275 276
#endif /* CONFIG_NFS_V4_1 */

277 278 279 280 281 282 283 284
/*
 * Bring up the callback thread if it is not already up.
 */
int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
{
	struct svc_serv *serv = NULL;
	struct svc_rqst *rqstp;
	int (*callback_svc)(void *vrqstp);
285
	struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
286 287
	char svc_name[12];
	int ret = 0;
288
	int minorversion_setup;
289 290

	mutex_lock(&nfs_callback_mutex);
291 292
	if (cb_info->users++ || cb_info->task != NULL) {
		nfs_callback_bc_serv(minorversion, xprt, cb_info);
293
		goto out;
294
	}
295 296 297 298 299 300
	serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
	if (!serv) {
		ret = -ENOMEM;
		goto out_err;
	}

301 302 303 304
	minorversion_setup =  nfs_minorversion_callback_svc_setup(minorversion,
					serv, xprt, &rqstp, &callback_svc);
	if (!minorversion_setup) {
		/* v4.0 callback setup */
305 306 307 308 309 310
		rqstp = nfs4_callback_up(serv);
		callback_svc = nfs4_callback_svc;
	}

	if (IS_ERR(rqstp)) {
		ret = PTR_ERR(rqstp);
311
		goto out_err;
312 313 314 315
	}

	svc_sock_update_bufs(serv);

316
	sprintf(svc_name, "nfsv4.%u-svc", minorversion);
317 318 319 320 321 322 323 324
	cb_info->serv = serv;
	cb_info->rqst = rqstp;
	cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name);
	if (IS_ERR(cb_info->task)) {
		ret = PTR_ERR(cb_info->task);
		svc_exit_thread(cb_info->rqst);
		cb_info->rqst = NULL;
		cb_info->task = NULL;
325 326
		goto out_err;
	}
L
Linus Torvalds 已提交
327
out:
328 329
	/*
	 * svc_create creates the svc_serv with sv_nrthreads == 1, and then
330
	 * svc_prepare_thread increments that. So we need to call svc_destroy
331 332 333 334 335
	 * on both success and failure so that the refcount is 1 when the
	 * thread exits.
	 */
	if (serv)
		svc_destroy(serv);
I
Ingo Molnar 已提交
336
	mutex_unlock(&nfs_callback_mutex);
L
Linus Torvalds 已提交
337
	return ret;
338
out_err:
339 340
	dprintk("NFS: Couldn't create callback socket or server thread; "
		"err = %d\n", ret);
341
	cb_info->users--;
L
Linus Torvalds 已提交
342 343 344 345
	goto out;
}

/*
346
 * Kill the callback thread if it's no longer being used.
L
Linus Torvalds 已提交
347
 */
348
void nfs_callback_down(int minorversion)
L
Linus Torvalds 已提交
349
{
350 351
	struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];

I
Ingo Molnar 已提交
352
	mutex_lock(&nfs_callback_mutex);
353 354 355 356 357 358 359
	cb_info->users--;
	if (cb_info->users == 0 && cb_info->task != NULL) {
		kthread_stop(cb_info->task);
		svc_exit_thread(cb_info->rqst);
		cb_info->serv = NULL;
		cb_info->rqst = NULL;
		cb_info->task = NULL;
360
	}
I
Ingo Molnar 已提交
361
	mutex_unlock(&nfs_callback_mutex);
L
Linus Torvalds 已提交
362 363
}

364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
static int check_gss_callback_principal(struct nfs_client *clp,
					struct svc_rqst *rqstp)
{
	struct rpc_clnt *r = clp->cl_rpcclient;
	char *p = svc_gss_principal(rqstp);

	/*
	 * 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)
		return SVC_DENIED;

	/* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */

	if (memcmp(p, "nfs@", 4) != 0)
		return SVC_DENIED;
	p += 4;
	if (strcmp(p, r->cl_server) != 0)
		return SVC_DENIED;
	return SVC_OK;
}

L
Linus Torvalds 已提交
387 388
static int nfs_callback_authenticate(struct svc_rqst *rqstp)
{
389
	struct nfs_client *clp;
390
	RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
391
	int ret = SVC_OK;
L
Linus Torvalds 已提交
392 393

	/* Don't talk to strangers */
394
	clp = nfs_find_client(svc_addr(rqstp), 4);
L
Linus Torvalds 已提交
395 396
	if (clp == NULL)
		return SVC_DROP;
397

398
	dprintk("%s: %s NFSv4 callback!\n", __func__,
399 400
			svc_print_addr(rqstp, buf, sizeof(buf)));

L
Linus Torvalds 已提交
401 402 403
	switch (rqstp->rq_authop->flavour) {
		case RPC_AUTH_NULL:
			if (rqstp->rq_proc != CB_NULL)
404
				ret = SVC_DENIED;
L
Linus Torvalds 已提交
405 406 407 408
			break;
		case RPC_AUTH_UNIX:
			break;
		case RPC_AUTH_GSS:
409 410
			ret = check_gss_callback_principal(clp, rqstp);
			break;
L
Linus Torvalds 已提交
411
		default:
412
			ret = SVC_DENIED;
L
Linus Torvalds 已提交
413
	}
414 415
	nfs_put_client(clp);
	return ret;
L
Linus Torvalds 已提交
416 417 418 419 420 421 422
}

/*
 * Define NFS4 callback program
 */
static struct svc_version *nfs4_callback_version[] = {
	[1] = &nfs4_callback_version1,
423
	[4] = &nfs4_callback_version4,
L
Linus Torvalds 已提交
424 425 426 427 428 429 430 431 432 433 434 435 436
};

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,
};