2009-02-24 18:30:18 +03:00
/*
* Copyright ( c ) 2006 Oracle . 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 <linux/kernel.h>
# include <net/sock.h>
# include <linux/in.h>
# include <linux/if_arp.h>
2010-05-12 02:09:45 +04:00
# include <linux/jhash.h>
2011-06-16 06:09:57 +04:00
# include <linux/ratelimit.h>
2009-02-24 18:30:18 +03:00
# include "rds.h"
2015-10-30 18:49:10 +03:00
static struct rhashtable bind_hash_table ;
2017-08-25 17:21:45 +03:00
static const struct rhashtable_params ht_parms = {
2015-10-30 18:49:10 +03:00
. nelem_hint = 768 ,
. key_len = sizeof ( u64 ) ,
. key_offset = offsetof ( struct rds_sock , rs_bound_key ) ,
. head_offset = offsetof ( struct rds_sock , rs_bound_node ) ,
. max_size = 16384 ,
. min_size = 1024 ,
2014-02-12 07:34:25 +04:00
} ;
2009-02-24 18:30:18 +03:00
/*
* Return the rds_sock bound at the given local address .
*
* The rx path can race with rds_release . We notice if rds_release ( ) has
* marked this socket and don ' t return a rs ref to the rx path .
*/
struct rds_sock * rds_find_bound ( __be32 addr , __be16 port )
{
2015-10-30 18:49:10 +03:00
u64 key = ( ( u64 ) addr < < 32 ) | port ;
2009-02-24 18:30:18 +03:00
struct rds_sock * rs ;
2010-05-12 02:09:45 +04:00
2015-10-30 18:49:10 +03:00
rs = rhashtable_lookup_fast ( & bind_hash_table , & key , ht_parms ) ;
if ( rs & & ! sock_flag ( rds_rs_to_sk ( rs ) , SOCK_DEAD ) )
rds_sock_addref ( rs ) ;
else
2009-02-24 18:30:18 +03:00
rs = NULL ;
rdsdebug ( " returning rs %p for %pI4:%u \n " , rs , & addr ,
ntohs ( port ) ) ;
2012-02-03 20:09:23 +04:00
2009-02-24 18:30:18 +03:00
return rs ;
}
/* returns -ve errno or +ve port */
static int rds_add_bound ( struct rds_sock * rs , __be32 addr , __be16 * port )
{
int ret = - EADDRINUSE ;
u16 rover , last ;
2015-10-30 18:49:10 +03:00
u64 key ;
2009-02-24 18:30:18 +03:00
if ( * port ! = 0 ) {
rover = be16_to_cpu ( * port ) ;
2016-07-14 13:51:03 +03:00
if ( rover = = RDS_FLAG_PROBE_PORT )
return - EINVAL ;
2009-02-24 18:30:18 +03:00
last = rover ;
} else {
2014-01-11 16:15:59 +04:00
rover = max_t ( u16 , prandom_u32 ( ) , 2 ) ;
2009-02-24 18:30:18 +03:00
last = rover - 1 ;
}
do {
if ( rover = = 0 )
rover + + ;
2014-02-12 07:34:25 +04:00
2016-07-14 13:51:03 +03:00
if ( rover = = RDS_FLAG_PROBE_PORT )
continue ;
2015-10-30 18:49:10 +03:00
key = ( ( u64 ) addr < < 32 ) | cpu_to_be16 ( rover ) ;
if ( rhashtable_lookup_fast ( & bind_hash_table , & key , ht_parms ) )
continue ;
rs - > rs_bound_key = key ;
rs - > rs_bound_addr = addr ;
2016-07-14 13:51:03 +03:00
net_get_random_once ( & rs - > rs_hash_initval ,
sizeof ( rs - > rs_hash_initval ) ) ;
2015-10-30 18:49:10 +03:00
rs - > rs_bound_port = cpu_to_be16 ( rover ) ;
rs - > rs_bound_node . next = NULL ;
rds_sock_addref ( rs ) ;
if ( ! rhashtable_insert_fast ( & bind_hash_table ,
& rs - > rs_bound_node , ht_parms ) ) {
2010-05-12 02:09:45 +04:00
* port = rs - > rs_bound_port ;
2009-02-24 18:30:18 +03:00
ret = 0 ;
2010-05-12 02:09:45 +04:00
rdsdebug ( " rs %p binding to %pI4:%d \n " ,
rs , & addr , ( int ) ntohs ( * port ) ) ;
2009-02-24 18:30:18 +03:00
break ;
2012-02-03 20:08:50 +04:00
} else {
2017-12-22 20:38:59 +03:00
rs - > rs_bound_addr = 0 ;
2015-10-30 18:49:10 +03:00
rds_sock_put ( rs ) ;
ret = - ENOMEM ;
break ;
2009-02-24 18:30:18 +03:00
}
} while ( rover + + ! = last ) ;
return ret ;
}
void rds_remove_bound ( struct rds_sock * rs )
{
2015-10-30 18:49:10 +03:00
if ( ! rs - > rs_bound_addr )
return ;
2009-02-24 18:30:18 +03:00
2015-10-30 18:49:10 +03:00
rdsdebug ( " rs %p unbinding from %pI4:%d \n " ,
rs , & rs - > rs_bound_addr ,
ntohs ( rs - > rs_bound_port ) ) ;
2009-02-24 18:30:18 +03:00
2015-10-30 18:49:10 +03:00
rhashtable_remove_fast ( & bind_hash_table , & rs - > rs_bound_node , ht_parms ) ;
rds_sock_put ( rs ) ;
rs - > rs_bound_addr = 0 ;
2009-02-24 18:30:18 +03:00
}
int rds_bind ( struct socket * sock , struct sockaddr * uaddr , int addr_len )
{
struct sock * sk = sock - > sk ;
struct sockaddr_in * sin = ( struct sockaddr_in * ) uaddr ;
struct rds_sock * rs = rds_sk_to_rs ( sk ) ;
struct rds_transport * trans ;
int ret = 0 ;
lock_sock ( sk ) ;
if ( addr_len ! = sizeof ( struct sockaddr_in ) | |
sin - > sin_family ! = AF_INET | |
rs - > rs_bound_addr | |
sin - > sin_addr . s_addr = = htonl ( INADDR_ANY ) ) {
ret = - EINVAL ;
goto out ;
}
ret = rds_add_bound ( rs , sin - > sin_addr . s_addr , & sin - > sin_port ) ;
if ( ret )
goto out ;
2015-05-30 00:28:08 +03:00
if ( rs - > rs_transport ) { /* previously bound */
2015-10-11 23:46:03 +03:00
trans = rs - > rs_transport ;
if ( trans - > laddr_check ( sock_net ( sock - > sk ) ,
sin - > sin_addr . s_addr ) ! = 0 ) {
ret = - ENOPROTOOPT ;
rds_remove_bound ( rs ) ;
} else {
ret = 0 ;
}
2015-05-30 00:28:08 +03:00
goto out ;
}
2015-08-05 08:43:25 +03:00
trans = rds_trans_get_preferred ( sock_net ( sock - > sk ) ,
sin - > sin_addr . s_addr ) ;
2010-01-12 22:56:44 +03:00
if ( ! trans ) {
2009-02-24 18:30:18 +03:00
ret = - EADDRNOTAVAIL ;
rds_remove_bound ( rs ) ;
2015-11-05 00:42:39 +03:00
pr_info_ratelimited ( " RDS: %s could not find a transport for %pI4, load rds_tcp or rds_rdma? \n " ,
__func__ , & sin - > sin_addr . s_addr ) ;
2009-02-24 18:30:18 +03:00
goto out ;
}
rs - > rs_transport = trans ;
ret = 0 ;
out :
release_sock ( sk ) ;
return ret ;
}
2014-02-12 07:34:25 +04:00
2015-10-30 18:49:10 +03:00
void rds_bind_lock_destroy ( void )
2014-02-12 07:34:25 +04:00
{
2015-10-30 18:49:10 +03:00
rhashtable_destroy ( & bind_hash_table ) ;
}
2014-02-12 07:34:25 +04:00
2015-10-30 18:49:10 +03:00
int rds_bind_lock_init ( void )
{
return rhashtable_init ( & bind_hash_table , & ht_parms ) ;
2014-02-12 07:34:25 +04:00
}