diff options
| author | Niels de Vos <ndevos@redhat.com> | 2015-01-01 13:15:45 +0100 | 
|---|---|---|
| committer | Vijay Bellur <vbellur@redhat.com> | 2015-03-15 06:40:22 -0700 | 
| commit | aac1ec0a61d9267b6ae7a280b368dfd357b7dcdc (patch) | |
| tree | c25aafc656e401f552a222d50e97942cea1a337a /xlators/nfs | |
| parent | 1cb3b1abeda53bb430bbe1490fac154337ac9994 (diff) | |
nfs: add auth-cache for the MOUNT protocol
Authentication cache for the new fine grained contol for the MOUNT
protocol. The extended authentication (see Change-Id Ic060aac) benefits
from caching the access/permission checks that are done when an
NFS-client mounts an export.
This auth-cache will be used by Change-Id I181e8c1.
BUG: 1143880
Change-Id: I1379116572c8a4d1bf0c7ca4f826e51a79d91444
Original-author: Shreyas Siravara <shreyas.siravara@gmail.com>
CC: Richard Wareing <rwareing@fb.com>
CC: Jiffin Tony Thottan <jthottan@redhat.com>
Signed-off-by: Niels de Vos <ndevos@redhat.com>
Reviewed-on: http://review.gluster.org/9363
Tested-by: Gluster Build System <jenkins@build.gluster.com>
Reviewed-by: Vijay Bellur <vbellur@redhat.com>
Diffstat (limited to 'xlators/nfs')
| -rw-r--r-- | xlators/nfs/server/src/Makefile.am | 4 | ||||
| -rw-r--r-- | xlators/nfs/server/src/auth-cache.c | 311 | ||||
| -rw-r--r-- | xlators/nfs/server/src/auth-cache.h | 53 | ||||
| -rw-r--r-- | xlators/nfs/server/src/nfs-mem-types.h | 2 | ||||
| -rw-r--r-- | xlators/nfs/server/src/nfs.h | 3 | 
5 files changed, 371 insertions, 2 deletions
diff --git a/xlators/nfs/server/src/Makefile.am b/xlators/nfs/server/src/Makefile.am index 2b0badc4fdd..d7051eff9b8 100644 --- a/xlators/nfs/server/src/Makefile.am +++ b/xlators/nfs/server/src/Makefile.am @@ -5,13 +5,13 @@ server_la_LDFLAGS = -module -avoid-version  server_la_SOURCES = nfs.c nfs-common.c nfs-fops.c nfs-inodes.c \  	nfs-generics.c mount3.c nfs3-fh.c nfs3.c nfs3-helpers.c nlm4.c \  	nlmcbk_svc.c mount3udp_svc.c acl3.c netgroups.c exports.c \ -	mount3-auth.c +	mount3-auth.c auth-cache.c  server_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \                     $(top_builddir)/api/src/libgfapi.la  noinst_HEADERS = nfs.h nfs-common.h nfs-fops.h nfs-inodes.h nfs-generics.h \  	mount3.h nfs3-fh.h nfs3.h nfs3-helpers.h nfs-mem-types.h nlm4.h \ -	acl3.h netgroups.h exports.h mount3-auth.h +	acl3.h netgroups.h exports.h mount3-auth.h auth-cache.h  AM_CPPFLAGS = $(GF_CPPFLAGS) \  	-DLIBDIR=\"$(libdir)/glusterfs/$(PACKAGE_VERSION)/auth\" \ diff --git a/xlators/nfs/server/src/auth-cache.c b/xlators/nfs/server/src/auth-cache.c new file mode 100644 index 00000000000..85d9056cd61 --- /dev/null +++ b/xlators/nfs/server/src/auth-cache.c @@ -0,0 +1,311 @@ +/* +   Copyright 2014-present Facebook. All Rights Reserved +   This file is part of GlusterFS. + +   Author : +   Shreyas Siravara <shreyas.siravara@gmail.com> + +   This file is licensed to you under your choice of the GNU Lesser +   General Public License, version 3 or any later version (LGPLv3 or +   later), or the GNU General Public License, version 2 (GPLv2), in all +   cases as published by the Free Software Foundation. +*/ + +#include "auth-cache.h" +#include "nfs3.h" +#include "exports.h" + +enum auth_cache_lookup_results { +        ENTRY_FOUND     =  0, +        ENTRY_NOT_FOUND = -1, +        ENTRY_EXPIRED   = -2, +}; + +struct auth_cache_entry { +        time_t timestamp; +        struct export_item *item; +}; + +/* Given a filehandle and an ip, creates a colon delimited hashkey that is + * allocated on the stack. + */ +static inline void +make_hashkey(char *hashkey, struct nfs3_fh *fh, const char *host) +{ +        char    exportid[256]    = {0, }; +        char    gfid[256]        = {0, }; +        char    mountid[256]     = {0, }; +        size_t  nbytes           = 0; + +        uuid_unparse (fh->exportid, exportid); +        uuid_unparse (fh->gfid, gfid); +        uuid_unparse (fh->mountid, mountid); +        nbytes = strlen (exportid) + strlen (gfid) + strlen (host) +                 + strlen (mountid) + 5; +        hashkey = alloca (nbytes); +        snprintf (hashkey, nbytes, "%s:%s:%s:%s", exportid, gfid, +                  mountid, host); +} + +/** + * auth_cache_init -- Initialize an auth cache and set the ttl_sec + * + * @ttl_sec : The TTL to set in seconds + * + * @return : allocated auth cache struct, NULL if allocation failed. + */ +struct auth_cache * +auth_cache_init (time_t ttl_sec) +{ +        struct auth_cache *cache = GF_CALLOC (1, sizeof (*cache), +                                              gf_nfs_mt_auth_cache); + +        GF_VALIDATE_OR_GOTO ("auth-cache", cache, out); + +        cache->ttl_sec = ttl_sec; +out: +        return cache; +} + +/** + * auth_cache_entry_init -- Initialize an auth cache entry + * + * @return: Pointer to an allocated auth cache entry, NULL if allocation + *          failed. + */ +struct auth_cache_entry * +auth_cache_entry_init () +{ +        struct auth_cache_entry *entry = NULL; + +        entry = GF_CALLOC (1, sizeof (*entry), gf_nfs_mt_auth_cache_entry); +        if (!entry) +                gf_log (GF_NFS, GF_LOG_WARNING, "failed to allocate entry"); + +        return entry; +} + +/** + * auth_cache_lookup -- Lookup an item from the cache + * + * @cache: cache to lookup from + * @fh   : FH to use in lookup + * @host_addr: Address to use in lookup + * @timestamp: The timestamp to set when lookup succeeds + * @can_write: Is the host authorized to write to the filehandle? + * + * If the current time - entry time of the cache entry > ttl_sec, + * we remove the element from the dict and return ENTRY_EXPIRED. + * + * @return: ENTRY_EXPIRED if entry expired + *          ENTRY_NOT_FOUND if entry not found in dict + *          0 if found + */ +enum auth_cache_lookup_results +auth_cache_lookup (struct auth_cache *cache, struct nfs3_fh *fh, +                   const char *host_addr, time_t *timestamp, +                   gf_boolean_t *can_write) +{ +        char                    *hashkey    = NULL; +        data_t                  *entry_data = NULL; +        struct auth_cache_entry *lookup_res = NULL; +        int                      ret        = ENTRY_NOT_FOUND; + +        GF_VALIDATE_OR_GOTO (GF_NFS, cache, out); +        GF_VALIDATE_OR_GOTO (GF_NFS, cache->cache_dict, out); +        GF_VALIDATE_OR_GOTO (GF_NFS, fh, out); +        GF_VALIDATE_OR_GOTO (GF_NFS, host_addr, out); +        GF_VALIDATE_OR_GOTO (GF_NFS, timestamp, out); +        GF_VALIDATE_OR_GOTO (GF_NFS, can_write, out); + +        make_hashkey (hashkey, fh, host_addr); + +        entry_data = dict_get (cache->cache_dict, hashkey); +        if (!entry_data) { +                gf_log (GF_NFS, GF_LOG_DEBUG, "could not find entry for %s", +                        host_addr); +                goto out; +        } + +        lookup_res = (struct auth_cache_entry *)(entry_data->data); + +        if ((time (NULL) - lookup_res->timestamp) > cache->ttl_sec) { +                gf_log (GF_NFS, GF_LOG_DEBUG, "entry for host %s has expired", +                        host_addr); +                GF_FREE (lookup_res); +                entry_data->data = NULL; +                /* Remove from the cache */ +                dict_del (cache->cache_dict, hashkey); + +                ret = ENTRY_EXPIRED; +                goto out; +        } + +        *timestamp = lookup_res->timestamp; +        *can_write = lookup_res->item->opts->rw; + +        ret = ENTRY_FOUND; +out: +        return ret; +} + +/** + * auth_cache_purge -- Purge the dict in the cache and set + *                     the dict pointer to NULL. It will be allocated + *                     on the first insert into the dict. + * + * @cache: Cache to purge + * + */ +void +auth_cache_purge (struct auth_cache *cache) +{ +        dict_t *new_cache_dict = NULL; +        dict_t *old_cache_dict = cache->cache_dict; + +        if (!cache || !cache->cache_dict) +                goto out; + +        (void)__sync_lock_test_and_set (&cache->cache_dict, new_cache_dict); + +        dict_unref (old_cache_dict); +out: +        return; +} + +/** + * is_nfs_fh_cached_and_writeable -- Checks if an NFS FH is cached for the given + *                                   host + * @cache: The fh cache + * @host_addr: Address to use in lookup + * @fh: The fh to use in lookup + * + * + * @return: TRUE if cached, FALSE otherwise + * + */ +gf_boolean_t +is_nfs_fh_cached (struct auth_cache *cache, struct nfs3_fh *fh, +                  const char *host_addr) +{ +        int          ret       = 0; +        time_t       timestamp = 0; +        gf_boolean_t cached    = _gf_false; +        gf_boolean_t can_write = _gf_false; + +        if (!fh) +                goto out; + +        ret = auth_cache_lookup (cache, fh, host_addr, ×tamp, &can_write); +        cached = (ret == ENTRY_FOUND); + +out: +        return cached; +} + + +/** + * is_nfs_fh_cached_and_writeable -- Checks if an NFS FH is cached for the given + *                                   host and writable + * @cache: The fh cache + * @host_addr: Address to use in lookup + * @fh: The fh to use in lookup + * + * + * @return: TRUE if cached & writable, FALSE otherwise + * + */ +gf_boolean_t +is_nfs_fh_cached_and_writeable (struct auth_cache *cache, struct nfs3_fh *fh, +                                const char *host_addr) +{ +        int          ret       = 0; +        time_t       timestamp = 0; +        gf_boolean_t cached    = _gf_false; +        gf_boolean_t writable  = _gf_false; + +        if (!fh) +                goto out; + +        ret = auth_cache_lookup (cache, fh, host_addr, ×tamp, &writable); +        cached = ((ret == ENTRY_FOUND) && writable); + +out: +        return cached; +} + +/** + * cache_nfs_fh -- Places the nfs file handle in the underlying dict as we are + *                 using as our cache. The key is "exportid:gfid:host_addr", the + *                 value is an entry struct containing the export item that + *                 was authorized for the operation and the file handle that was + *                 authorized. + * + * @cache: The cache to place fh's in + * @fh   : The fh to cache + * @host_addr: The address of the host + * @export_item: The export item that was authorized + * + */ +int +cache_nfs_fh (struct auth_cache *cache, struct nfs3_fh *fh, +              const char *host_addr, struct export_item *export_item) +{ +        int                      ret        = -EINVAL; +        char                    *hashkey    = NULL; +        data_t                  *entry_data = NULL; +        time_t                   timestamp  = 0; +        gf_boolean_t             can_write  = _gf_false; +        struct auth_cache_entry *entry      = NULL; + +        GF_VALIDATE_OR_GOTO (GF_NFS, host_addr, out); +        GF_VALIDATE_OR_GOTO (GF_NFS, cache, out); +        GF_VALIDATE_OR_GOTO (GF_NFS, fh, out); + +        /* If a dict has not been allocated already, allocate it. */ +        if (!cache->cache_dict) { +                cache->cache_dict = dict_new (); +                if (!cache->cache_dict) { +                        ret = -ENOMEM; +                        goto out; +                } +        } + +        /* If we could already find it in the cache, just return */ +        ret = auth_cache_lookup (cache, fh, host_addr, ×tamp, &can_write); +        if (ret == 0) { +                gf_log (GF_NFS, GF_LOG_TRACE, "found cached auth/fh for host " +                        "%s", host_addr); +                goto out; +        } + +        make_hashkey (hashkey, fh, host_addr); + +        entry = auth_cache_entry_init (); +        if (!entry) { +                ret = -ENOMEM; +                goto out; +        } + +        entry->timestamp = time (NULL); +        entry->item = export_item; + +        /* The cache entry will simply be the time that the entry +         * was cached. +         */ +        entry_data = bin_to_data (entry, sizeof (*entry)); +        if (!entry_data) { +                GF_FREE (entry); +                goto out; +        } + +        ret = dict_set (cache->cache_dict, hashkey, entry_data); +        if (ret == -1) { +                GF_FREE (entry); +                goto out; +        } +        gf_log (GF_NFS, GF_LOG_TRACE, "Caching file-handle (%s)", host_addr); +        ret = 0; +out: +        return ret; +} diff --git a/xlators/nfs/server/src/auth-cache.h b/xlators/nfs/server/src/auth-cache.h new file mode 100644 index 00000000000..5f2f03c1cb8 --- /dev/null +++ b/xlators/nfs/server/src/auth-cache.h @@ -0,0 +1,53 @@ +/* +  Copyright 2014-present Facebook. All Rights Reserved + +  This file is part of GlusterFS. + +   Author : +   Shreyas Siravara <shreyas.siravara@gmail.com> + +  This file is licensed to you under your choice of the GNU Lesser +  General Public License, version 3 or any later version (LGPLv3 or +  later), or the GNU General Public License, version 2 (GPLv2), in all +  cases as published by the Free Software Foundation. +*/ + +#ifndef _AUTH_CACHE_H_ +#define _AUTH_CACHE_H_ + +#include "nfs-mem-types.h" +#include "mount3.h" +#include "exports.h" +#include "dict.h" +#include "nfs3.h" + +struct auth_cache { +        dict_t *cache_dict;      /* Dict holding fh -> authcache_entry */ +        time_t ttl_sec;          /* TTL of the auth cache in seconds */ +}; + + +/* Initializes the cache */ +struct auth_cache * +auth_cache_init (time_t ttl_sec); + +/* Inserts FH into cache */ +int +cache_nfs_fh (struct auth_cache *cache, struct nfs3_fh *fh, +              const char *host_addr, struct export_item *export_item); + +/* Checks if the filehandle cached & writable */ +gf_boolean_t +is_nfs_fh_cached_and_writeable (struct auth_cache *cache, struct nfs3_fh *fh, +                                const char *host_addr); + +/* Checks if the filehandle is cached */ +gf_boolean_t +is_nfs_fh_cached (struct auth_cache *cache, struct nfs3_fh *fh, +                  const char *host_addr); + +/* Purge the cache */ +void +auth_cache_purge (struct auth_cache *cache); + +#endif /* _AUTH_CACHE_H_ */ diff --git a/xlators/nfs/server/src/nfs-mem-types.h b/xlators/nfs/server/src/nfs-mem-types.h index 79737633d24..6d4a2ed9cc2 100644 --- a/xlators/nfs/server/src/nfs-mem-types.h +++ b/xlators/nfs/server/src/nfs-mem-types.h @@ -50,6 +50,8 @@ enum gf_nfs_mem_types_ {          gf_nfs_mt_netgroups,          gf_nfs_mt_exports,          gf_nfs_mt_arr, +        gf_nfs_mt_auth_cache, +        gf_nfs_mt_auth_cache_entry,          gf_nfs_mt_end  };  #endif diff --git a/xlators/nfs/server/src/nfs.h b/xlators/nfs/server/src/nfs.h index 4c51d11d49c..7d5dfa63acb 100644 --- a/xlators/nfs/server/src/nfs.h +++ b/xlators/nfs/server/src/nfs.h @@ -41,6 +41,9 @@  /* Disable using the exports file by default */  #define GF_NFS_DEFAULT_EXPORT_AUTH      0 +#define GF_NFS_DEFAULT_AUTH_REFRESH_INTERVAL_SEC       2 +#define GF_NFS_DEFAULT_AUTH_CACHE_TTL_SEC              300 /* 5 min */ +  /* This corresponds to the max 16 number of group IDs that are sent through an   * RPC request. Since NFS is the only one going to set this, we can be safe   * in keeping this size hardcoded.  | 
