linux/fs/nfs/callback.c
<<
>>
Prefs
   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
  29struct nfs_callback_data {
  30        unsigned int users;
  31        struct svc_serv *serv;
  32        struct svc_rqst *rqst;
  33        struct task_struct *task;
  34};
  35
  36static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
  37static DEFINE_MUTEX(nfs_callback_mutex);
  38static struct svc_program nfs4_callback_program;
  39
  40unsigned int nfs_callback_set_tcpport;
  41unsigned short nfs_callback_tcpport;
  42unsigned short nfs_callback_tcpport6;
  43#define NFS_CALLBACK_MAXPORTNR (65535U)
  44
  45static 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}
  58static 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
  64module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644);
  65
  66/*
  67 * This is the NFSv4 callback kernel thread.
  68 */
  69static int
  70nfs4_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 */
 104struct svc_rqst *
 105nfs4_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
 130out_err:
 131        if (ret == 0)
 132                ret = -ENOMEM;
 133        return ERR_PTR(ret);
 134}
 135
 136#if defined(CONFIG_NFS_V4_1)
 137/*
 138 * The callback service for NFSv4.1 callbacks
 139 */
 140static int
 141nfs41_callback_svc(void *vrqstp)
 142{
 143        struct svc_rqst *rqstp = vrqstp;
 144        struct svc_serv *serv = rqstp->rq_server;
 145        struct rpc_rqst *req;
 146        int error;
 147        DEFINE_WAIT(wq);
 148
 149        set_freezable();
 150
 151        while (!kthread_should_stop()) {
 152                prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
 153                spin_lock_bh(&serv->sv_cb_lock);
 154                if (!list_empty(&serv->sv_cb_list)) {
 155                        req = list_first_entry(&serv->sv_cb_list,
 156                                        struct rpc_rqst, rq_bc_list);
 157                        list_del(&req->rq_bc_list);
 158                        spin_unlock_bh(&serv->sv_cb_lock);
 159                        dprintk("Invoking bc_svc_process()\n");
 160                        error = bc_svc_process(serv, req, rqstp);
 161                        dprintk("bc_svc_process() returned w/ error code= %d\n",
 162                                error);
 163                } else {
 164                        spin_unlock_bh(&serv->sv_cb_lock);
 165                        schedule();
 166                }
 167                finish_wait(&serv->sv_cb_waitq, &wq);
 168        }
 169        return 0;
 170}
 171
 172/*
 173 * Bring up the NFSv4.1 callback service
 174 */
 175struct svc_rqst *
 176nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
 177{
 178        struct svc_rqst *rqstp;
 179        int ret;
 180
 181        /*
 182         * Create an svc_sock for the back channel service that shares the
 183         * fore channel connection.
 184         * Returns the input port (0) and sets the svc_serv bc_xprt on success
 185         */
 186        ret = svc_create_xprt(serv, "tcp-bc", &init_net, PF_INET, 0,
 187                              SVC_SOCK_ANONYMOUS);
 188        if (ret < 0) {
 189                rqstp = ERR_PTR(ret);
 190                goto out;
 191        }
 192
 193        /*
 194         * Save the svc_serv in the transport so that it can
 195         * be referenced when the session backchannel is initialized
 196         */
 197        xprt->bc_serv = serv;
 198
 199        INIT_LIST_HEAD(&serv->sv_cb_list);
 200        spin_lock_init(&serv->sv_cb_lock);
 201        init_waitqueue_head(&serv->sv_cb_waitq);
 202        rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
 203        if (IS_ERR(rqstp)) {
 204                svc_xprt_put(serv->sv_bc_xprt);
 205                serv->sv_bc_xprt = NULL;
 206        }
 207out:
 208        dprintk("--> %s return %ld\n", __func__,
 209                IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0);
 210        return rqstp;
 211}
 212
 213static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
 214                struct svc_serv *serv, struct rpc_xprt *xprt,
 215                struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
 216{
 217        if (minorversion) {
 218                *rqstpp = nfs41_callback_up(serv, xprt);
 219                *callback_svc = nfs41_callback_svc;
 220        }
 221        return minorversion;
 222}
 223
 224static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 225                struct nfs_callback_data *cb_info)
 226{
 227        if (minorversion)
 228                xprt->bc_serv = cb_info->serv;
 229}
 230#else
 231static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
 232                struct svc_serv *serv, struct rpc_xprt *xprt,
 233                struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
 234{
 235        return 0;
 236}
 237
 238static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 239                struct nfs_callback_data *cb_info)
 240{
 241}
 242#endif /* CONFIG_NFS_V4_1 */
 243
 244/*
 245 * Bring up the callback thread if it is not already up.
 246 */
 247int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
 248{
 249        struct svc_serv *serv = NULL;
 250        struct svc_rqst *rqstp;
 251        int (*callback_svc)(void *vrqstp);
 252        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 253        char svc_name[12];
 254        int ret = 0;
 255        int minorversion_setup;
 256
 257        mutex_lock(&nfs_callback_mutex);
 258        if (cb_info->users++ || cb_info->task != NULL) {
 259                nfs_callback_bc_serv(minorversion, xprt, cb_info);
 260                goto out;
 261        }
 262        serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
 263        if (!serv) {
 264                ret = -ENOMEM;
 265                goto out_err;
 266        }
 267
 268        minorversion_setup =  nfs_minorversion_callback_svc_setup(minorversion,
 269                                        serv, xprt, &rqstp, &callback_svc);
 270        if (!minorversion_setup) {
 271                /* v4.0 callback setup */
 272                rqstp = nfs4_callback_up(serv);
 273                callback_svc = nfs4_callback_svc;
 274        }
 275
 276        if (IS_ERR(rqstp)) {
 277                ret = PTR_ERR(rqstp);
 278                goto out_err;
 279        }
 280
 281        svc_sock_update_bufs(serv);
 282
 283        sprintf(svc_name, "nfsv4.%u-svc", minorversion);
 284        cb_info->serv = serv;
 285        cb_info->rqst = rqstp;
 286        cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name);
 287        if (IS_ERR(cb_info->task)) {
 288                ret = PTR_ERR(cb_info->task);
 289                svc_exit_thread(cb_info->rqst);
 290                cb_info->rqst = NULL;
 291                cb_info->task = NULL;
 292                goto out_err;
 293        }
 294out:
 295        /*
 296         * svc_create creates the svc_serv with sv_nrthreads == 1, and then
 297         * svc_prepare_thread increments that. So we need to call svc_destroy
 298         * on both success and failure so that the refcount is 1 when the
 299         * thread exits.
 300         */
 301        if (serv)
 302                svc_destroy(serv);
 303        mutex_unlock(&nfs_callback_mutex);
 304        return ret;
 305out_err:
 306        dprintk("NFS: Couldn't create callback socket or server thread; "
 307                "err = %d\n", ret);
 308        cb_info->users--;
 309        goto out;
 310}
 311
 312/*
 313 * Kill the callback thread if it's no longer being used.
 314 */
 315void nfs_callback_down(int minorversion)
 316{
 317        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 318
 319        mutex_lock(&nfs_callback_mutex);
 320        cb_info->users--;
 321        if (cb_info->users == 0 && cb_info->task != NULL) {
 322                kthread_stop(cb_info->task);
 323                svc_exit_thread(cb_info->rqst);
 324                cb_info->serv = NULL;
 325                cb_info->rqst = NULL;
 326                cb_info->task = NULL;
 327        }
 328        mutex_unlock(&nfs_callback_mutex);
 329}
 330
 331/* Boolean check of RPC_AUTH_GSS principal */
 332int
 333check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp)
 334{
 335        struct rpc_clnt *r = clp->cl_rpcclient;
 336        char *p = svc_gss_principal(rqstp);
 337
 338        if (rqstp->rq_authop->flavour != RPC_AUTH_GSS)
 339                return 1;
 340
 341        /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */
 342        if (clp->cl_minorversion != 0)
 343                return 0;
 344        /*
 345         * It might just be a normal user principal, in which case
 346         * userspace won't bother to tell us the name at all.
 347         */
 348        if (p == NULL)
 349                return 0;
 350
 351        /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
 352
 353        if (memcmp(p, "nfs@", 4) != 0)
 354                return 0;
 355        p += 4;
 356        if (strcmp(p, r->cl_server) != 0)
 357                return 0;
 358        return 1;
 359}
 360
 361/*
 362 * pg_authenticate method for nfsv4 callback threads.
 363 *
 364 * The authflavor has been negotiated, so an incorrect flavor is a server
 365 * bug. Drop packets with incorrect authflavor.
 366 *
 367 * All other checking done after NFS decoding where the nfs_client can be
 368 * found in nfs4_callback_compound
 369 */
 370static int nfs_callback_authenticate(struct svc_rqst *rqstp)
 371{
 372        switch (rqstp->rq_authop->flavour) {
 373        case RPC_AUTH_NULL:
 374                if (rqstp->rq_proc != CB_NULL)
 375                        return SVC_DROP;
 376                break;
 377        case RPC_AUTH_GSS:
 378                /* No RPC_AUTH_GSS support yet in NFSv4.1 */
 379                 if (svc_is_backchannel(rqstp))
 380                        return SVC_DROP;
 381        }
 382        return SVC_OK;
 383}
 384
 385/*
 386 * Define NFS4 callback program
 387 */
 388static struct svc_version *nfs4_callback_version[] = {
 389        [1] = &nfs4_callback_version1,
 390        [4] = &nfs4_callback_version4,
 391};
 392
 393static struct svc_stat nfs4_callback_stats;
 394
 395static struct svc_program nfs4_callback_program = {
 396        .pg_prog = NFS4_CALLBACK,                       /* RPC service number */
 397        .pg_nvers = ARRAY_SIZE(nfs4_callback_version),  /* Number of entries */
 398        .pg_vers = nfs4_callback_version,               /* version table */
 399        .pg_name = "NFSv4 callback",                    /* service name */
 400        .pg_class = "nfs",                              /* authentication class */
 401        .pg_stats = &nfs4_callback_stats,
 402        .pg_authenticate = nfs_callback_authenticate,
 403};
 404