From aac1ec0a61d9267b6ae7a280b368dfd357b7dcdc Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Thu, 1 Jan 2015 13:15:45 +0100 Subject: 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 CC: Richard Wareing CC: Jiffin Tony Thottan Signed-off-by: Niels de Vos Reviewed-on: http://review.gluster.org/9363 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- xlators/nfs/server/src/Makefile.am | 4 +- xlators/nfs/server/src/auth-cache.c | 311 +++++++++++++++++++++++++++++++++ xlators/nfs/server/src/auth-cache.h | 53 ++++++ xlators/nfs/server/src/nfs-mem-types.h | 2 + xlators/nfs/server/src/nfs.h | 3 + 5 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 xlators/nfs/server/src/auth-cache.c create mode 100644 xlators/nfs/server/src/auth-cache.h (limited to 'xlators') 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 + + 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 + + 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. -- cgit