blob: 95c42d766e675f0fbe5d98e7041e5651a526841e [file] [log] [blame]
/*
* vim:noexpandtab:shiftwidth=8:tabstop=8:
*
* Copyright CEA/DAM/DIF (2008)
* contributeur : Philippe DENIEL philippe.deniel@cea.fr
* Thomas LEIBOVICI thomas.leibovici@cea.fr
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* ---------------------------------------
*/
/**
* @file nfs_ip_name.c
* @brief The management of the IP/name cache.
*/
#include "config.h"
#include "hashtable.h"
#include "log.h"
#include "nfs_core.h"
#include "nfs_exports.h"
#include "nfs_ip_stats.h"
#include "config_parsing.h"
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* Hashtable used to cache the hostname, accessed by their IP addess */
hash_table_t *ht_ip_name;
unsigned int expiration_time;
/**
* @name Compute the hash value for the entry in IP/name cache
*
* @param[in] hparam Hash table parameter.
* @param[in] buffcleff The hash key buffer
*
* @return the computed hash value.
*
* @see hashtable_init
*
*/
uint32_t ip_name_value_hash_func(hash_parameter_t *hparam,
struct gsh_buffdesc *buffclef)
{
return hash_sockaddr(buffclef->addr, true) % hparam->index_size;
}
/**
* @brief Compute the rbt value for the entry in IP/name cache
*
* @param[in] hparam Hash table parameter
* @param[in] buffclef Hash key buffer
*
* @return the computed rbt value.
*
* @see hashtable_init
*
*/
uint64_t ip_name_rbt_hash_func(hash_parameter_t *hparam,
struct gsh_buffdesc *buffclef)
{
return hash_sockaddr(buffclef->addr, true);
}
/**
*
* compare_ip_name: compares the ip address stored in the key buffers.
*
* compare the ip address stored in the key buffers. This function is to be used
* as 'compare_key' field in the hashtable storing the nfs duplicated requests.
*
* @param buff1 [IN] first key
* @param buff2 [IN] second key
*
* @return 0 if keys are identifical, 1 if they are different.
*
*/
int compare_ip_name(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2)
{
return (cmp_sockaddr(buff1->addr, buff2->addr, true) !=
0) ? 0 : 1;
}
/**
* @brief Display the ip_name stored in the buffer
*
* @param[in] pbuff Buffer to display
* @param[out] str Output string
*
* @return number of character written.
*/
int display_ip_name_key(struct gsh_buffdesc *pbuff, char *str)
{
sockaddr_t *addr = (sockaddr_t *) (pbuff->addr);
sprint_sockip(addr, str, HASHTABLE_DISPLAY_STRLEN);
return strlen(str);
}
/**
* @brief Display the ip_name stored in the buffer
*
* @param[in] pbuff Buffer to display
* @param[out] str Output string
*
* @return number of character written
*/
int display_ip_name_val(struct gsh_buffdesc *pbuff, char *str)
{
nfs_ip_name_t *nfs_ip_name = (pbuff->addr);
return snprintf(str, HASHTABLE_DISPLAY_STRLEN, "%s",
nfs_ip_name->hostname);
}
/**
*
* nfs_ip_name_add: adds an entry into IP/name cache.
*
* Adds an entry in the duplicate requests cache.
*
* @param ipaddr [IN] the ipaddr to be used as key
* @param hostname [OUT] the hostname added (found by using getnameinfo)
*
* @return IP_NAME_SUCCESS if successfull\n.
* @return IP_NAME_INSERT_MALLOC_ERROR if an error occured during the insertion process \n
* @return IP_NAME_NETDB_ERROR if an error occured during the netdb query (via gethostbyaddr).
*
*/
int nfs_ip_name_add(sockaddr_t *ipaddr, char *hostname, size_t size)
{
struct gsh_buffdesc buffkey;
struct gsh_buffdesc buffdata;
nfs_ip_name_t *nfs_ip_name = NULL;
sockaddr_t *pipaddr = NULL;
struct timeval tv0, tv1, dur;
int rc;
char ipstring[SOCK_NAME_MAX + 1];
nfs_ip_name = gsh_malloc(sizeof(nfs_ip_name_t));
pipaddr = gsh_malloc(sizeof(sockaddr_t));
/* I have to keep an integer as key, I wil use the pointer buffkey->addr
* for this, this also means that buffkey->len will be 0
*/
memcpy(pipaddr, ipaddr, sizeof(sockaddr_t));
buffkey.addr = (caddr_t) pipaddr;
buffkey.len = sizeof(sockaddr_t);
gettimeofday(&tv0, NULL);
rc = getnameinfo((struct sockaddr *)pipaddr, sizeof(sockaddr_t),
nfs_ip_name->hostname, sizeof(nfs_ip_name->hostname),
NULL, 0, 0);
gettimeofday(&tv1, NULL);
timersub(&tv1, &tv0, &dur);
sprint_sockip(pipaddr, ipstring, sizeof(ipstring));
/* display warning if DNS resolution took more that 1.0s */
if (dur.tv_sec >= 1) {
LogEvent(COMPONENT_DISPATCH,
"Warning: long DNS query for %s: %u.%06u sec",
ipstring, (unsigned int)dur.tv_sec,
(unsigned int)dur.tv_usec);
}
/* Ask for the name to be cached */
if (rc != 0) {
strmaxcpy(nfs_ip_name->hostname, ipstring,
sizeof(nfs_ip_name->hostname));
LogEvent(COMPONENT_DISPATCH,
"Cannot resolve address %s, error %s, using %s as hostname",
ipstring, gai_strerror(rc), nfs_ip_name->hostname);
}
LogDebug(COMPONENT_DISPATCH, "Inserting %s->%s to addr cache", ipstring,
nfs_ip_name->hostname);
/* I build the data with the request pointer
* that should be in state 'IN USE'
*/
nfs_ip_name->timestamp = time(NULL);
buffdata.addr = (caddr_t) nfs_ip_name;
buffdata.len = sizeof(nfs_ip_name_t);
if (HashTable_Set(ht_ip_name, &buffkey, &buffdata) != HASHTABLE_SUCCESS)
return IP_NAME_INSERT_MALLOC_ERROR;
/* Copy the value for the caller */
strmaxcpy(hostname, nfs_ip_name->hostname, size);
return IP_NAME_SUCCESS;
} /* nfs_ip_name_add */
/**
*
* nfs_ip_name_get: Tries to get an entry for ip_name cache.
*
* Tries to get an entry for ip_name cache.
*
* @param ipaddr [IN] the ip address requested
* @param hostname [OUT] the hostname
*
* @return the result previously set if *pstatus == IP_NAME_SUCCESS
*
*/
int nfs_ip_name_get(sockaddr_t *ipaddr, char *hostname, size_t size)
{
struct gsh_buffdesc buffkey;
struct gsh_buffdesc buffval;
nfs_ip_name_t *nfs_ip_name;
char ipstring[SOCK_NAME_MAX + 1];
sprint_sockip(ipaddr, ipstring, sizeof(ipstring));
buffkey.addr = (caddr_t) ipaddr;
buffkey.len = sizeof(sockaddr_t);
if (HashTable_Get(ht_ip_name,
&buffkey,
&buffval) == HASHTABLE_SUCCESS) {
nfs_ip_name = buffval.addr;
strmaxcpy(hostname, nfs_ip_name->hostname, size);
LogFullDebug(COMPONENT_DISPATCH, "Cache get hit for %s->%s",
ipstring, nfs_ip_name->hostname);
return IP_NAME_SUCCESS;
}
LogFullDebug(COMPONENT_DISPATCH, "Cache get miss for %s", ipstring);
return IP_NAME_NOT_FOUND;
} /* nfs_ip_name_get */
/**
*
* nfs_ip_name_remove: Tries to remove an entry for ip_name cache
*
* Tries to remove an entry for ip_name cache.
*
* @param ipaddr [IN] the ip address to be uncached.
*
* @return the result previously set if *pstatus == IP_NAME_SUCCESS
*
*/
int nfs_ip_name_remove(sockaddr_t *ipaddr)
{
struct gsh_buffdesc buffkey, old_value;
nfs_ip_name_t *nfs_ip_name = NULL;
char ipstring[SOCK_NAME_MAX + 1];
sprint_sockip(ipaddr, ipstring, sizeof(ipstring));
buffkey.addr = (caddr_t) ipaddr;
buffkey.len = sizeof(sockaddr_t);
if (HashTable_Del(ht_ip_name, &buffkey, NULL, &old_value) ==
HASHTABLE_SUCCESS) {
nfs_ip_name = (nfs_ip_name_t *) old_value.addr;
LogFullDebug(COMPONENT_DISPATCH, "Cache remove hit for %s->%s",
ipstring, nfs_ip_name->hostname);
gsh_free(nfs_ip_name);
return IP_NAME_SUCCESS;
}
LogFullDebug(COMPONENT_DISPATCH, "Cache remove miss for %s", ipstring);
return IP_NAME_NOT_FOUND;
} /* nfs_ip_name_remove */
/**
* @defgroup config_ipnamemap Structure and defaults for NFS_IP_Name
*
* @{
*/
/**
* @brief Default index size for IP-Name hash
*/
#define PRIME_IP_NAME 17
/**
* @brief Default value for ip_name_param.expiration-time
*/
#define IP_NAME_EXPIRATION 3600
/** @} */
/**
* @brief NFS_IP_Name configuration stanza
*/
struct ip_name_cache {
/** Configuration for hash table for NFS Name/IP map.
Defautl index size is PRIME_IP_NAME, settable with
Index_Size. */
hash_parameter_t hash_param;
/** Expiration time for ip-name mappings. Defautls to
IP_NAME_Expiration, and settable with Expiration_Time. */
uint32_t expiration_time;
};
static struct ip_name_cache ip_name_cache = {
.hash_param.hash_func_key = ip_name_value_hash_func,
.hash_param.hash_func_rbt = ip_name_rbt_hash_func,
.hash_param.compare_key = compare_ip_name,
.hash_param.key_to_str = display_ip_name_key,
.hash_param.val_to_str = display_ip_name_val,
.hash_param.flags = HT_FLAG_NONE,
};
/**
* @brief IP name cache parameters
*/
static struct config_item ip_name_params[] = {
CONF_ITEM_UI32("Index_Size", 1, 51, PRIME_IP_NAME,
ip_name_cache, hash_param.index_size),
CONF_ITEM_UI32("Expiration_Time", 1, 60*60*24, IP_NAME_EXPIRATION,
ip_name_cache, expiration_time),
CONFIG_EOL
};
static void *ip_name_init(void *link_mem, void *self_struct)
{
if (self_struct == NULL)
return &ip_name_cache;
else
return NULL;
}
static int ip_name_commit(void *node, void *link_mem, void *self_struct,
struct config_error_type *err_type)
{
struct ip_name_cache *params = self_struct;
if (!is_prime(params->hash_param.index_size)) {
LogCrit(COMPONENT_CONFIG,
"IP name cache index size must be a prime.");
return 1;
}
return 0;
}
struct config_block nfs_ip_name = {
.dbus_interface_name = "org.ganesha.nfsd.config.ip_name",
.blk_desc.name = "NFS_IP_Name",
.blk_desc.type = CONFIG_BLOCK,
.blk_desc.u.blk.init = ip_name_init,
.blk_desc.u.blk.params = ip_name_params,
.blk_desc.u.blk.commit = ip_name_commit
};
/**
*
* nfs_Init_ip_name: Init the hashtable for IP/name cache.
*
* Perform all the required initialization for hashtable IP/name cache
*
* @return 0 if successful, -1 otherwise
*
*/
int nfs_Init_ip_name(void)
{
ht_ip_name = hashtable_init(&ip_name_cache.hash_param);
if (ht_ip_name == NULL) {
LogCrit(COMPONENT_INIT,
"NFS IP_NAME: Cannot init IP/name cache");
return -1;
}
/* Set the expiration time */
expiration_time = ip_name_cache.expiration_time;
return IP_NAME_SUCCESS;
} /* nfs_Init_ip_name */