2014-03-26 17:07:35 -05:00
/*
* Copyright ( c ) 2014 Chelsio , Inc . All rights reserved .
* Copyright ( c ) 2014 Intel Corporation . All rights reserved .
*
* This software is available to you under a choice of one of two
* licenses . You may choose to be licensed under the terms of the GNU
* General Public License ( GPL ) Version 2 , available from the file
* COPYING in the main directory of this source tree , or the
* OpenIB . org BSD license below :
*
* Redistribution and use in source and binary forms , with or
* without modification , are permitted provided that the following
* conditions are met :
*
* - Redistributions of source code must retain the above
* copyright notice , this list of conditions and the following
* disclaimer .
*
* - Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
# include "iwpm_util.h"
2015-04-21 16:28:10 -04:00
# define IWPM_MAPINFO_HASH_SIZE 512
# define IWPM_MAPINFO_HASH_MASK (IWPM_MAPINFO_HASH_SIZE - 1)
# define IWPM_REMINFO_HASH_SIZE 64
# define IWPM_REMINFO_HASH_MASK (IWPM_REMINFO_HASH_SIZE - 1)
2016-07-18 14:21:49 -05:00
# define IWPM_MSG_SIZE 512
2014-03-26 17:07:35 -05:00
static LIST_HEAD ( iwpm_nlmsg_req_list ) ;
static DEFINE_SPINLOCK ( iwpm_nlmsg_req_lock ) ;
static struct hlist_head * iwpm_hash_bucket ;
static DEFINE_SPINLOCK ( iwpm_mapinfo_lock ) ;
2015-04-21 16:28:10 -04:00
static struct hlist_head * iwpm_reminfo_bucket ;
static DEFINE_SPINLOCK ( iwpm_reminfo_lock ) ;
2014-03-26 17:07:35 -05:00
static DEFINE_MUTEX ( iwpm_admin_lock ) ;
static struct iwpm_admin_data iwpm_admin ;
int iwpm_init ( u8 nl_client )
{
2015-04-21 16:28:10 -04:00
int ret = 0 ;
2014-03-26 17:07:35 -05:00
mutex_lock ( & iwpm_admin_lock ) ;
if ( atomic_read ( & iwpm_admin . refcount ) = = 0 ) {
2015-04-21 16:28:10 -04:00
iwpm_hash_bucket = kzalloc ( IWPM_MAPINFO_HASH_SIZE *
2014-03-26 17:07:35 -05:00
sizeof ( struct hlist_head ) , GFP_KERNEL ) ;
if ( ! iwpm_hash_bucket ) {
2015-04-21 16:28:10 -04:00
ret = - ENOMEM ;
goto init_exit ;
}
iwpm_reminfo_bucket = kzalloc ( IWPM_REMINFO_HASH_SIZE *
sizeof ( struct hlist_head ) , GFP_KERNEL ) ;
if ( ! iwpm_reminfo_bucket ) {
kfree ( iwpm_hash_bucket ) ;
ret = - ENOMEM ;
goto init_exit ;
2014-03-26 17:07:35 -05:00
}
}
atomic_inc ( & iwpm_admin . refcount ) ;
2015-04-21 16:28:10 -04:00
init_exit :
2014-03-26 17:07:35 -05:00
mutex_unlock ( & iwpm_admin_lock ) ;
2015-04-21 16:28:10 -04:00
if ( ! ret ) {
iwpm_set_valid ( nl_client , 1 ) ;
2015-07-02 12:47:44 -05:00
iwpm_set_registration ( nl_client , IWPM_REG_UNDEF ) ;
2015-04-21 16:28:10 -04:00
pr_debug ( " %s: Mapinfo and reminfo tables are created \n " ,
__func__ ) ;
}
return ret ;
2014-03-26 17:07:35 -05:00
}
static void free_hash_bucket ( void ) ;
2015-04-21 16:28:10 -04:00
static void free_reminfo_bucket ( void ) ;
2014-03-26 17:07:35 -05:00
int iwpm_exit ( u8 nl_client )
{
if ( ! iwpm_valid_client ( nl_client ) )
return - EINVAL ;
mutex_lock ( & iwpm_admin_lock ) ;
if ( atomic_read ( & iwpm_admin . refcount ) = = 0 ) {
mutex_unlock ( & iwpm_admin_lock ) ;
pr_err ( " %s Incorrect usage - negative refcount \n " , __func__ ) ;
return - EINVAL ;
}
if ( atomic_dec_and_test ( & iwpm_admin . refcount ) ) {
free_hash_bucket ( ) ;
2015-04-21 16:28:10 -04:00
free_reminfo_bucket ( ) ;
pr_debug ( " %s: Resources are destroyed \n " , __func__ ) ;
2014-03-26 17:07:35 -05:00
}
mutex_unlock ( & iwpm_admin_lock ) ;
iwpm_set_valid ( nl_client , 0 ) ;
2015-07-02 12:47:44 -05:00
iwpm_set_registration ( nl_client , IWPM_REG_UNDEF ) ;
2014-03-26 17:07:35 -05:00
return 0 ;
}
2015-04-21 16:28:10 -04:00
static struct hlist_head * get_mapinfo_hash_bucket ( struct sockaddr_storage * ,
2014-03-26 17:07:35 -05:00
struct sockaddr_storage * ) ;
int iwpm_create_mapinfo ( struct sockaddr_storage * local_sockaddr ,
struct sockaddr_storage * mapped_sockaddr ,
u8 nl_client )
{
2018-04-25 17:24:04 +01:00
struct hlist_head * hash_bucket_head = NULL ;
2014-03-26 17:07:35 -05:00
struct iwpm_mapping_info * map_info ;
unsigned long flags ;
2015-04-21 16:28:10 -04:00
int ret = - EINVAL ;
2014-03-26 17:07:35 -05:00
if ( ! iwpm_valid_client ( nl_client ) )
2015-04-21 16:28:10 -04:00
return ret ;
2014-03-26 17:07:35 -05:00
map_info = kzalloc ( sizeof ( struct iwpm_mapping_info ) , GFP_KERNEL ) ;
2016-11-03 16:44:10 +02:00
if ( ! map_info )
2014-03-26 17:07:35 -05:00
return - ENOMEM ;
2016-11-03 16:44:10 +02:00
2014-03-26 17:07:35 -05:00
memcpy ( & map_info - > local_sockaddr , local_sockaddr ,
sizeof ( struct sockaddr_storage ) ) ;
memcpy ( & map_info - > mapped_sockaddr , mapped_sockaddr ,
sizeof ( struct sockaddr_storage ) ) ;
map_info - > nl_client = nl_client ;
spin_lock_irqsave ( & iwpm_mapinfo_lock , flags ) ;
if ( iwpm_hash_bucket ) {
2015-04-21 16:28:10 -04:00
hash_bucket_head = get_mapinfo_hash_bucket (
2014-03-26 17:07:35 -05:00
& map_info - > local_sockaddr ,
& map_info - > mapped_sockaddr ) ;
2015-04-21 16:28:10 -04:00
if ( hash_bucket_head ) {
hlist_add_head ( & map_info - > hlist_node , hash_bucket_head ) ;
ret = 0 ;
}
2014-03-26 17:07:35 -05:00
}
spin_unlock_irqrestore ( & iwpm_mapinfo_lock , flags ) ;
2018-04-25 17:24:04 +01:00
if ( ! hash_bucket_head )
kfree ( map_info ) ;
2015-04-21 16:28:10 -04:00
return ret ;
2014-03-26 17:07:35 -05:00
}
int iwpm_remove_mapinfo ( struct sockaddr_storage * local_sockaddr ,
struct sockaddr_storage * mapped_local_addr )
{
struct hlist_node * tmp_hlist_node ;
struct hlist_head * hash_bucket_head ;
struct iwpm_mapping_info * map_info = NULL ;
unsigned long flags ;
int ret = - EINVAL ;
spin_lock_irqsave ( & iwpm_mapinfo_lock , flags ) ;
if ( iwpm_hash_bucket ) {
2015-04-21 16:28:10 -04:00
hash_bucket_head = get_mapinfo_hash_bucket (
2014-03-26 17:07:35 -05:00
local_sockaddr ,
mapped_local_addr ) ;
2015-04-21 16:28:10 -04:00
if ( ! hash_bucket_head )
goto remove_mapinfo_exit ;
2014-03-26 17:07:35 -05:00
hlist_for_each_entry_safe ( map_info , tmp_hlist_node ,
hash_bucket_head , hlist_node ) {
if ( ! iwpm_compare_sockaddr ( & map_info - > mapped_sockaddr ,
mapped_local_addr ) ) {
hlist_del_init ( & map_info - > hlist_node ) ;
kfree ( map_info ) ;
ret = 0 ;
break ;
}
}
}
2015-04-21 16:28:10 -04:00
remove_mapinfo_exit :
2014-03-26 17:07:35 -05:00
spin_unlock_irqrestore ( & iwpm_mapinfo_lock , flags ) ;
return ret ;
}
static void free_hash_bucket ( void )
{
struct hlist_node * tmp_hlist_node ;
struct iwpm_mapping_info * map_info ;
unsigned long flags ;
int i ;
/* remove all the mapinfo data from the list */
spin_lock_irqsave ( & iwpm_mapinfo_lock , flags ) ;
2015-04-21 16:28:10 -04:00
for ( i = 0 ; i < IWPM_MAPINFO_HASH_SIZE ; i + + ) {
2014-03-26 17:07:35 -05:00
hlist_for_each_entry_safe ( map_info , tmp_hlist_node ,
& iwpm_hash_bucket [ i ] , hlist_node ) {
hlist_del_init ( & map_info - > hlist_node ) ;
kfree ( map_info ) ;
}
}
/* free the hash list */
kfree ( iwpm_hash_bucket ) ;
iwpm_hash_bucket = NULL ;
spin_unlock_irqrestore ( & iwpm_mapinfo_lock , flags ) ;
}
2015-04-21 16:28:10 -04:00
static void free_reminfo_bucket ( void )
{
struct hlist_node * tmp_hlist_node ;
struct iwpm_remote_info * rem_info ;
unsigned long flags ;
int i ;
/* remove all the remote info from the list */
spin_lock_irqsave ( & iwpm_reminfo_lock , flags ) ;
for ( i = 0 ; i < IWPM_REMINFO_HASH_SIZE ; i + + ) {
hlist_for_each_entry_safe ( rem_info , tmp_hlist_node ,
& iwpm_reminfo_bucket [ i ] , hlist_node ) {
hlist_del_init ( & rem_info - > hlist_node ) ;
kfree ( rem_info ) ;
}
}
/* free the hash list */
kfree ( iwpm_reminfo_bucket ) ;
iwpm_reminfo_bucket = NULL ;
spin_unlock_irqrestore ( & iwpm_reminfo_lock , flags ) ;
}
static struct hlist_head * get_reminfo_hash_bucket ( struct sockaddr_storage * ,
struct sockaddr_storage * ) ;
void iwpm_add_remote_info ( struct iwpm_remote_info * rem_info )
{
struct hlist_head * hash_bucket_head ;
unsigned long flags ;
spin_lock_irqsave ( & iwpm_reminfo_lock , flags ) ;
if ( iwpm_reminfo_bucket ) {
hash_bucket_head = get_reminfo_hash_bucket (
& rem_info - > mapped_loc_sockaddr ,
& rem_info - > mapped_rem_sockaddr ) ;
if ( hash_bucket_head )
hlist_add_head ( & rem_info - > hlist_node , hash_bucket_head ) ;
}
spin_unlock_irqrestore ( & iwpm_reminfo_lock , flags ) ;
}
int iwpm_get_remote_info ( struct sockaddr_storage * mapped_loc_addr ,
2016-02-26 09:18:05 -06:00
struct sockaddr_storage * mapped_rem_addr ,
struct sockaddr_storage * remote_addr ,
u8 nl_client )
2015-04-21 16:28:10 -04:00
{
struct hlist_node * tmp_hlist_node ;
struct hlist_head * hash_bucket_head ;
struct iwpm_remote_info * rem_info = NULL ;
unsigned long flags ;
int ret = - EINVAL ;
if ( ! iwpm_valid_client ( nl_client ) ) {
pr_info ( " %s: Invalid client = %d \n " , __func__ , nl_client ) ;
return ret ;
}
spin_lock_irqsave ( & iwpm_reminfo_lock , flags ) ;
if ( iwpm_reminfo_bucket ) {
hash_bucket_head = get_reminfo_hash_bucket (
mapped_loc_addr ,
mapped_rem_addr ) ;
if ( ! hash_bucket_head )
goto get_remote_info_exit ;
hlist_for_each_entry_safe ( rem_info , tmp_hlist_node ,
hash_bucket_head , hlist_node ) {
if ( ! iwpm_compare_sockaddr ( & rem_info - > mapped_loc_sockaddr ,
mapped_loc_addr ) & &
! iwpm_compare_sockaddr ( & rem_info - > mapped_rem_sockaddr ,
mapped_rem_addr ) ) {
memcpy ( remote_addr , & rem_info - > remote_sockaddr ,
sizeof ( struct sockaddr_storage ) ) ;
iwpm_print_sockaddr ( remote_addr ,
" get_remote_info: Remote sockaddr: " ) ;
hlist_del_init ( & rem_info - > hlist_node ) ;
kfree ( rem_info ) ;
ret = 0 ;
break ;
}
}
}
get_remote_info_exit :
spin_unlock_irqrestore ( & iwpm_reminfo_lock , flags ) ;
return ret ;
}
2014-03-26 17:07:35 -05:00
struct iwpm_nlmsg_request * iwpm_get_nlmsg_request ( __u32 nlmsg_seq ,
u8 nl_client , gfp_t gfp )
{
struct iwpm_nlmsg_request * nlmsg_request = NULL ;
unsigned long flags ;
nlmsg_request = kzalloc ( sizeof ( struct iwpm_nlmsg_request ) , gfp ) ;
2016-11-03 16:44:10 +02:00
if ( ! nlmsg_request )
2014-03-26 17:07:35 -05:00
return NULL ;
2016-11-03 16:44:10 +02:00
2014-03-26 17:07:35 -05:00
spin_lock_irqsave ( & iwpm_nlmsg_req_lock , flags ) ;
list_add_tail ( & nlmsg_request - > inprocess_list , & iwpm_nlmsg_req_list ) ;
spin_unlock_irqrestore ( & iwpm_nlmsg_req_lock , flags ) ;
kref_init ( & nlmsg_request - > kref ) ;
kref_get ( & nlmsg_request - > kref ) ;
nlmsg_request - > nlmsg_seq = nlmsg_seq ;
nlmsg_request - > nl_client = nl_client ;
nlmsg_request - > request_done = 0 ;
nlmsg_request - > err_code = 0 ;
2016-02-26 09:18:05 -06:00
sema_init ( & nlmsg_request - > sem , 1 ) ;
down ( & nlmsg_request - > sem ) ;
2014-03-26 17:07:35 -05:00
return nlmsg_request ;
}
void iwpm_free_nlmsg_request ( struct kref * kref )
{
struct iwpm_nlmsg_request * nlmsg_request ;
unsigned long flags ;
nlmsg_request = container_of ( kref , struct iwpm_nlmsg_request , kref ) ;
spin_lock_irqsave ( & iwpm_nlmsg_req_lock , flags ) ;
list_del_init ( & nlmsg_request - > inprocess_list ) ;
spin_unlock_irqrestore ( & iwpm_nlmsg_req_lock , flags ) ;
if ( ! nlmsg_request - > request_done )
pr_debug ( " %s Freeing incomplete nlmsg request (seq = %u). \n " ,
__func__ , nlmsg_request - > nlmsg_seq ) ;
kfree ( nlmsg_request ) ;
}
struct iwpm_nlmsg_request * iwpm_find_nlmsg_request ( __u32 echo_seq )
{
struct iwpm_nlmsg_request * nlmsg_request ;
struct iwpm_nlmsg_request * found_request = NULL ;
unsigned long flags ;
spin_lock_irqsave ( & iwpm_nlmsg_req_lock , flags ) ;
list_for_each_entry ( nlmsg_request , & iwpm_nlmsg_req_list ,
inprocess_list ) {
if ( nlmsg_request - > nlmsg_seq = = echo_seq ) {
found_request = nlmsg_request ;
kref_get ( & nlmsg_request - > kref ) ;
break ;
}
}
spin_unlock_irqrestore ( & iwpm_nlmsg_req_lock , flags ) ;
return found_request ;
}
int iwpm_wait_complete_req ( struct iwpm_nlmsg_request * nlmsg_request )
{
int ret ;
2016-02-26 09:18:05 -06:00
ret = down_timeout ( & nlmsg_request - > sem , IWPM_NL_TIMEOUT ) ;
if ( ret ) {
2014-03-26 17:07:35 -05:00
ret = - EINVAL ;
pr_info ( " %s: Timeout %d sec for netlink request (seq = %u) \n " ,
__func__ , ( IWPM_NL_TIMEOUT / HZ ) , nlmsg_request - > nlmsg_seq ) ;
} else {
ret = nlmsg_request - > err_code ;
}
kref_put ( & nlmsg_request - > kref , iwpm_free_nlmsg_request ) ;
return ret ;
}
int iwpm_get_nlmsg_seq ( void )
{
return atomic_inc_return ( & iwpm_admin . nlmsg_seq ) ;
}
int iwpm_valid_client ( u8 nl_client )
{
return iwpm_admin . client_list [ nl_client ] ;
}
void iwpm_set_valid ( u8 nl_client , int valid )
{
iwpm_admin . client_list [ nl_client ] = valid ;
}
/* valid client */
2015-07-02 12:47:44 -05:00
u32 iwpm_get_registration ( u8 nl_client )
2014-03-26 17:07:35 -05:00
{
return iwpm_admin . reg_list [ nl_client ] ;
}
/* valid client */
2015-07-02 12:47:44 -05:00
void iwpm_set_registration ( u8 nl_client , u32 reg )
2014-03-26 17:07:35 -05:00
{
iwpm_admin . reg_list [ nl_client ] = reg ;
}
2015-07-02 12:47:44 -05:00
/* valid client */
u32 iwpm_check_registration ( u8 nl_client , u32 reg )
{
return ( iwpm_get_registration ( nl_client ) & reg ) ;
}
2014-03-26 17:07:35 -05:00
int iwpm_compare_sockaddr ( struct sockaddr_storage * a_sockaddr ,
struct sockaddr_storage * b_sockaddr )
{
if ( a_sockaddr - > ss_family ! = b_sockaddr - > ss_family )
return 1 ;
if ( a_sockaddr - > ss_family = = AF_INET ) {
struct sockaddr_in * a4_sockaddr =
( struct sockaddr_in * ) a_sockaddr ;
struct sockaddr_in * b4_sockaddr =
( struct sockaddr_in * ) b_sockaddr ;
if ( ! memcmp ( & a4_sockaddr - > sin_addr ,
& b4_sockaddr - > sin_addr , sizeof ( struct in_addr ) )
& & a4_sockaddr - > sin_port = = b4_sockaddr - > sin_port )
return 0 ;
} else if ( a_sockaddr - > ss_family = = AF_INET6 ) {
struct sockaddr_in6 * a6_sockaddr =
( struct sockaddr_in6 * ) a_sockaddr ;
struct sockaddr_in6 * b6_sockaddr =
( struct sockaddr_in6 * ) b_sockaddr ;
if ( ! memcmp ( & a6_sockaddr - > sin6_addr ,
& b6_sockaddr - > sin6_addr , sizeof ( struct in6_addr ) )
& & a6_sockaddr - > sin6_port = = b6_sockaddr - > sin6_port )
return 0 ;
} else {
pr_err ( " %s: Invalid sockaddr family \n " , __func__ ) ;
}
return 1 ;
}
struct sk_buff * iwpm_create_nlmsg ( u32 nl_op , struct nlmsghdr * * nlh ,
int nl_client )
{
struct sk_buff * skb = NULL ;
2016-07-18 14:21:49 -05:00
skb = dev_alloc_skb ( IWPM_MSG_SIZE ) ;
2018-01-27 21:48:01 +01:00
if ( ! skb )
2014-03-26 17:07:35 -05:00
goto create_nlmsg_exit ;
2018-01-27 21:48:01 +01:00
2014-03-26 17:07:35 -05:00
if ( ! ( ibnl_put_msg ( skb , nlh , 0 , 0 , nl_client , nl_op ,
NLM_F_REQUEST ) ) ) {
pr_warn ( " %s: Unable to put the nlmsg header \n " , __func__ ) ;
dev_kfree_skb ( skb ) ;
skb = NULL ;
}
create_nlmsg_exit :
return skb ;
}
int iwpm_parse_nlmsg ( struct netlink_callback * cb , int policy_max ,
const struct nla_policy * nlmsg_policy ,
struct nlattr * nltb [ ] , const char * msg_type )
{
int nlh_len = 0 ;
int ret ;
const char * err_str = " " ;
2017-04-12 14:34:07 +02:00
ret = nlmsg_validate ( cb - > nlh , nlh_len , policy_max - 1 , nlmsg_policy ,
NULL ) ;
2014-03-26 17:07:35 -05:00
if ( ret ) {
err_str = " Invalid attribute " ;
goto parse_nlmsg_error ;
}
2017-04-12 14:34:07 +02:00
ret = nlmsg_parse ( cb - > nlh , nlh_len , nltb , policy_max - 1 ,
nlmsg_policy , NULL ) ;
2014-03-26 17:07:35 -05:00
if ( ret ) {
err_str = " Unable to parse the nlmsg " ;
goto parse_nlmsg_error ;
}
ret = iwpm_validate_nlmsg_attr ( nltb , policy_max ) ;
if ( ret ) {
err_str = " Invalid NULL attribute " ;
goto parse_nlmsg_error ;
}
return 0 ;
parse_nlmsg_error :
pr_warn ( " %s: %s (msg type %s ret = %d) \n " ,
__func__ , err_str , msg_type , ret ) ;
return ret ;
}
void iwpm_print_sockaddr ( struct sockaddr_storage * sockaddr , char * msg )
{
struct sockaddr_in6 * sockaddr_v6 ;
struct sockaddr_in * sockaddr_v4 ;
switch ( sockaddr - > ss_family ) {
case AF_INET :
sockaddr_v4 = ( struct sockaddr_in * ) sockaddr ;
pr_debug ( " %s IPV4 %pI4: %u(0x%04X) \n " ,
msg , & sockaddr_v4 - > sin_addr ,
ntohs ( sockaddr_v4 - > sin_port ) ,
ntohs ( sockaddr_v4 - > sin_port ) ) ;
break ;
case AF_INET6 :
sockaddr_v6 = ( struct sockaddr_in6 * ) sockaddr ;
pr_debug ( " %s IPV6 %pI6: %u(0x%04X) \n " ,
msg , & sockaddr_v6 - > sin6_addr ,
ntohs ( sockaddr_v6 - > sin6_port ) ,
ntohs ( sockaddr_v6 - > sin6_port ) ) ;
break ;
default :
break ;
}
}
static u32 iwpm_ipv6_jhash ( struct sockaddr_in6 * ipv6_sockaddr )
{
u32 ipv6_hash = jhash ( & ipv6_sockaddr - > sin6_addr , sizeof ( struct in6_addr ) , 0 ) ;
u32 hash = jhash_2words ( ipv6_hash , ( __force u32 ) ipv6_sockaddr - > sin6_port , 0 ) ;
return hash ;
}
static u32 iwpm_ipv4_jhash ( struct sockaddr_in * ipv4_sockaddr )
{
u32 ipv4_hash = jhash ( & ipv4_sockaddr - > sin_addr , sizeof ( struct in_addr ) , 0 ) ;
u32 hash = jhash_2words ( ipv4_hash , ( __force u32 ) ipv4_sockaddr - > sin_port , 0 ) ;
return hash ;
}
2015-04-21 16:28:10 -04:00
static int get_hash_bucket ( struct sockaddr_storage * a_sockaddr ,
struct sockaddr_storage * b_sockaddr , u32 * hash )
2014-03-26 17:07:35 -05:00
{
2015-04-21 16:28:10 -04:00
u32 a_hash , b_hash ;
2014-03-26 17:07:35 -05:00
2015-04-21 16:28:10 -04:00
if ( a_sockaddr - > ss_family = = AF_INET ) {
a_hash = iwpm_ipv4_jhash ( ( struct sockaddr_in * ) a_sockaddr ) ;
b_hash = iwpm_ipv4_jhash ( ( struct sockaddr_in * ) b_sockaddr ) ;
2014-03-26 17:07:35 -05:00
2015-04-21 16:28:10 -04:00
} else if ( a_sockaddr - > ss_family = = AF_INET6 ) {
a_hash = iwpm_ipv6_jhash ( ( struct sockaddr_in6 * ) a_sockaddr ) ;
b_hash = iwpm_ipv6_jhash ( ( struct sockaddr_in6 * ) b_sockaddr ) ;
2014-03-26 17:07:35 -05:00
} else {
pr_err ( " %s: Invalid sockaddr family \n " , __func__ ) ;
2015-04-21 16:28:10 -04:00
return - EINVAL ;
2014-03-26 17:07:35 -05:00
}
2015-04-21 16:28:10 -04:00
if ( a_hash = = b_hash ) /* if port mapper isn't available */
* hash = a_hash ;
2014-03-26 17:07:35 -05:00
else
2015-04-21 16:28:10 -04:00
* hash = jhash_2words ( a_hash , b_hash , 0 ) ;
return 0 ;
}
static struct hlist_head * get_mapinfo_hash_bucket ( struct sockaddr_storage
* local_sockaddr , struct sockaddr_storage
* mapped_sockaddr )
{
u32 hash ;
int ret ;
2014-03-26 17:07:35 -05:00
2015-04-21 16:28:10 -04:00
ret = get_hash_bucket ( local_sockaddr , mapped_sockaddr , & hash ) ;
if ( ret )
return NULL ;
return & iwpm_hash_bucket [ hash & IWPM_MAPINFO_HASH_MASK ] ;
}
static struct hlist_head * get_reminfo_hash_bucket ( struct sockaddr_storage
* mapped_loc_sockaddr , struct sockaddr_storage
* mapped_rem_sockaddr )
{
u32 hash ;
int ret ;
ret = get_hash_bucket ( mapped_loc_sockaddr , mapped_rem_sockaddr , & hash ) ;
if ( ret )
return NULL ;
return & iwpm_reminfo_bucket [ hash & IWPM_REMINFO_HASH_MASK ] ;
2014-03-26 17:07:35 -05:00
}
static int send_mapinfo_num ( u32 mapping_num , u8 nl_client , int iwpm_pid )
{
struct sk_buff * skb = NULL ;
struct nlmsghdr * nlh ;
u32 msg_seq ;
const char * err_str = " " ;
int ret = - EINVAL ;
skb = iwpm_create_nlmsg ( RDMA_NL_IWPM_MAPINFO_NUM , & nlh , nl_client ) ;
if ( ! skb ) {
err_str = " Unable to create a nlmsg " ;
goto mapinfo_num_error ;
}
nlh - > nlmsg_seq = iwpm_get_nlmsg_seq ( ) ;
msg_seq = 0 ;
err_str = " Unable to put attribute of mapinfo number nlmsg " ;
ret = ibnl_put_attr ( skb , nlh , sizeof ( u32 ) , & msg_seq , IWPM_NLA_MAPINFO_SEQ ) ;
if ( ret )
goto mapinfo_num_error ;
ret = ibnl_put_attr ( skb , nlh , sizeof ( u32 ) ,
& mapping_num , IWPM_NLA_MAPINFO_SEND_NUM ) ;
if ( ret )
goto mapinfo_num_error ;
2017-09-29 08:25:01 -05:00
nlmsg_end ( skb , nlh ) ;
2017-06-18 15:35:20 +03:00
ret = rdma_nl_unicast ( skb , iwpm_pid ) ;
2014-03-26 17:07:35 -05:00
if ( ret ) {
skb = NULL ;
err_str = " Unable to send a nlmsg " ;
goto mapinfo_num_error ;
}
pr_debug ( " %s: Sent mapping number = %d \n " , __func__ , mapping_num ) ;
return 0 ;
mapinfo_num_error :
pr_info ( " %s: %s \n " , __func__ , err_str ) ;
if ( skb )
dev_kfree_skb ( skb ) ;
return ret ;
}
static int send_nlmsg_done ( struct sk_buff * skb , u8 nl_client , int iwpm_pid )
{
struct nlmsghdr * nlh = NULL ;
int ret = 0 ;
if ( ! skb )
return ret ;
if ( ! ( ibnl_put_msg ( skb , & nlh , 0 , 0 , nl_client ,
RDMA_NL_IWPM_MAPINFO , NLM_F_MULTI ) ) ) {
pr_warn ( " %s Unable to put NLMSG_DONE \n " , __func__ ) ;
2016-05-06 22:45:24 +03:00
dev_kfree_skb ( skb ) ;
2014-03-26 17:07:35 -05:00
return - ENOMEM ;
}
nlh - > nlmsg_type = NLMSG_DONE ;
2017-06-18 15:35:20 +03:00
ret = rdma_nl_unicast ( skb , iwpm_pid ) ;
2014-03-26 17:07:35 -05:00
if ( ret )
pr_warn ( " %s Unable to send a nlmsg \n " , __func__ ) ;
return ret ;
}
int iwpm_send_mapinfo ( u8 nl_client , int iwpm_pid )
{
struct iwpm_mapping_info * map_info ;
struct sk_buff * skb = NULL ;
struct nlmsghdr * nlh ;
int skb_num = 0 , mapping_num = 0 ;
int i = 0 , nlmsg_bytes = 0 ;
unsigned long flags ;
const char * err_str = " " ;
int ret ;
skb = dev_alloc_skb ( NLMSG_GOODSIZE ) ;
if ( ! skb ) {
ret = - ENOMEM ;
err_str = " Unable to allocate skb " ;
goto send_mapping_info_exit ;
}
skb_num + + ;
spin_lock_irqsave ( & iwpm_mapinfo_lock , flags ) ;
2017-11-29 09:47:33 +01:00
ret = - EINVAL ;
2015-04-21 16:28:10 -04:00
for ( i = 0 ; i < IWPM_MAPINFO_HASH_SIZE ; i + + ) {
2014-03-26 17:07:35 -05:00
hlist_for_each_entry ( map_info , & iwpm_hash_bucket [ i ] ,
hlist_node ) {
if ( map_info - > nl_client ! = nl_client )
continue ;
nlh = NULL ;
if ( ! ( ibnl_put_msg ( skb , & nlh , 0 , 0 , nl_client ,
RDMA_NL_IWPM_MAPINFO , NLM_F_MULTI ) ) ) {
ret = - ENOMEM ;
err_str = " Unable to put the nlmsg header " ;
goto send_mapping_info_unlock ;
}
err_str = " Unable to put attribute of the nlmsg " ;
ret = ibnl_put_attr ( skb , nlh ,
sizeof ( struct sockaddr_storage ) ,
& map_info - > local_sockaddr ,
IWPM_NLA_MAPINFO_LOCAL_ADDR ) ;
if ( ret )
goto send_mapping_info_unlock ;
ret = ibnl_put_attr ( skb , nlh ,
sizeof ( struct sockaddr_storage ) ,
& map_info - > mapped_sockaddr ,
IWPM_NLA_MAPINFO_MAPPED_ADDR ) ;
if ( ret )
goto send_mapping_info_unlock ;
2017-09-29 08:25:01 -05:00
nlmsg_end ( skb , nlh ) ;
2014-03-26 17:07:35 -05:00
iwpm_print_sockaddr ( & map_info - > local_sockaddr ,
" send_mapping_info: Local sockaddr: " ) ;
iwpm_print_sockaddr ( & map_info - > mapped_sockaddr ,
" send_mapping_info: Mapped local sockaddr: " ) ;
mapping_num + + ;
nlmsg_bytes + = nlh - > nlmsg_len ;
/* check if all mappings can fit in one skb */
if ( NLMSG_GOODSIZE - nlmsg_bytes < nlh - > nlmsg_len * 2 ) {
/* and leave room for NLMSG_DONE */
nlmsg_bytes = 0 ;
skb_num + + ;
spin_unlock_irqrestore ( & iwpm_mapinfo_lock ,
flags ) ;
/* send the skb */
ret = send_nlmsg_done ( skb , nl_client , iwpm_pid ) ;
skb = NULL ;
if ( ret ) {
err_str = " Unable to send map info " ;
goto send_mapping_info_exit ;
}
if ( skb_num = = IWPM_MAPINFO_SKB_COUNT ) {
ret = - ENOMEM ;
err_str = " Insufficient skbs for map info " ;
goto send_mapping_info_exit ;
}
skb = dev_alloc_skb ( NLMSG_GOODSIZE ) ;
if ( ! skb ) {
ret = - ENOMEM ;
err_str = " Unable to allocate skb " ;
goto send_mapping_info_exit ;
}
spin_lock_irqsave ( & iwpm_mapinfo_lock , flags ) ;
}
}
}
send_mapping_info_unlock :
spin_unlock_irqrestore ( & iwpm_mapinfo_lock , flags ) ;
send_mapping_info_exit :
if ( ret ) {
pr_warn ( " %s: %s (ret = %d) \n " , __func__ , err_str , ret ) ;
if ( skb )
dev_kfree_skb ( skb ) ;
return ret ;
}
send_nlmsg_done ( skb , nl_client , iwpm_pid ) ;
return send_mapinfo_num ( mapping_num , nl_client , iwpm_pid ) ;
}
int iwpm_mapinfo_available ( void )
{
unsigned long flags ;
int full_bucket = 0 , i = 0 ;
spin_lock_irqsave ( & iwpm_mapinfo_lock , flags ) ;
if ( iwpm_hash_bucket ) {
2015-04-21 16:28:10 -04:00
for ( i = 0 ; i < IWPM_MAPINFO_HASH_SIZE ; i + + ) {
2014-03-26 17:07:35 -05:00
if ( ! hlist_empty ( & iwpm_hash_bucket [ i ] ) ) {
full_bucket = 1 ;
break ;
}
}
}
spin_unlock_irqrestore ( & iwpm_mapinfo_lock , flags ) ;
return full_bucket ;
}