2005-04-17 02:20:36 +04:00
/* SCTP kernel reference Implementation
* ( C ) Copyright IBM Corp . 2001 , 2003
* Copyright ( c ) Cisco 1999 , 2000
* Copyright ( c ) Motorola 1999 , 2000 , 2001
* Copyright ( c ) La Monte H . P . Yarroll 2001
*
* This file is part of the SCTP kernel reference implementation .
*
* A collection class to handle the storage of transport addresses .
*
* The SCTP reference implementation is free software ;
* you can redistribute it and / or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* The SCTP reference implementation 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 General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with GNU CC ; see the file COPYING . If not , write to
* the Free Software Foundation , 59 Temple Place - Suite 330 ,
* Boston , MA 02111 - 1307 , USA .
*
* Please send any bug reports or fixes you make to the
* email address ( es ) :
* lksctp developers < lksctp - developers @ lists . sourceforge . net >
*
* Or submit a bug report through the following website :
* http : //www.sf.net/projects/lksctp
*
* Written or modified by :
* La Monte H . P . Yarroll < piggy @ acm . org >
* Karl Knutson < karl @ athena . chicago . il . us >
* Jon Grimm < jgrimm @ us . ibm . com >
* Daisy Chang < daisyc @ us . ibm . com >
*
* Any bugs reported given to us we will try to fix . . . any fixes shared will
* be incorporated into the next SCTP release .
*/
# include <linux/types.h>
# include <linux/sched.h>
# include <linux/in.h>
# include <net/sock.h>
# include <net/ipv6.h>
# include <net/if_inet6.h>
# include <net/sctp/sctp.h>
# include <net/sctp/sm.h>
/* Forward declarations for internal helpers. */
static int sctp_copy_one_addr ( struct sctp_bind_addr * , union sctp_addr * ,
2005-10-07 10:46:04 +04:00
sctp_scope_t scope , gfp_t gfp ,
2005-07-12 07:57:47 +04:00
int flags ) ;
2005-04-17 02:20:36 +04:00
static void sctp_bind_addr_clean ( struct sctp_bind_addr * ) ;
/* First Level Abstractions. */
/* Copy 'src' to 'dest' taking 'scope' into account. Omit addresses
* in ' src ' which have a broader scope than ' scope ' .
*/
int sctp_bind_addr_copy ( struct sctp_bind_addr * dest ,
const struct sctp_bind_addr * src ,
2005-10-07 10:46:04 +04:00
sctp_scope_t scope , gfp_t gfp ,
2005-07-12 07:57:47 +04:00
int flags )
2005-04-17 02:20:36 +04:00
{
struct sctp_sockaddr_entry * addr ;
struct list_head * pos ;
int error = 0 ;
/* All addresses share the same port. */
dest - > port = src - > port ;
/* Extract the addresses which are relevant for this scope. */
list_for_each ( pos , & src - > address_list ) {
addr = list_entry ( pos , struct sctp_sockaddr_entry , list ) ;
2006-11-21 04:04:10 +03:00
error = sctp_copy_one_addr ( dest , & addr - > a_h , scope ,
2005-04-17 02:20:36 +04:00
gfp , flags ) ;
if ( error < 0 )
goto out ;
}
/* If there are no addresses matching the scope and
* this is global scope , try to get a link scope address , with
* the assumption that we must be sitting behind a NAT .
*/
if ( list_empty ( & dest - > address_list ) & & ( SCTP_SCOPE_GLOBAL = = scope ) ) {
list_for_each ( pos , & src - > address_list ) {
addr = list_entry ( pos , struct sctp_sockaddr_entry ,
list ) ;
2006-11-21 04:04:10 +03:00
error = sctp_copy_one_addr ( dest , & addr - > a_h ,
2005-04-17 02:20:36 +04:00
SCTP_SCOPE_LINK , gfp ,
flags ) ;
if ( error < 0 )
goto out ;
}
}
out :
if ( error )
sctp_bind_addr_clean ( dest ) ;
return error ;
}
/* Initialize the SCTP_bind_addr structure for either an endpoint or
* an association .
*/
void sctp_bind_addr_init ( struct sctp_bind_addr * bp , __u16 port )
{
bp - > malloced = 0 ;
INIT_LIST_HEAD ( & bp - > address_list ) ;
bp - > port = port ;
}
/* Dispose of the address list. */
static void sctp_bind_addr_clean ( struct sctp_bind_addr * bp )
{
struct sctp_sockaddr_entry * addr ;
struct list_head * pos , * temp ;
/* Empty the bind address list. */
list_for_each_safe ( pos , temp , & bp - > address_list ) {
addr = list_entry ( pos , struct sctp_sockaddr_entry , list ) ;
list_del ( pos ) ;
kfree ( addr ) ;
SCTP_DBG_OBJCNT_DEC ( addr ) ;
}
}
/* Dispose of an SCTP_bind_addr structure */
void sctp_bind_addr_free ( struct sctp_bind_addr * bp )
{
/* Empty the bind address list. */
sctp_bind_addr_clean ( bp ) ;
if ( bp - > malloced ) {
kfree ( bp ) ;
SCTP_DBG_OBJCNT_DEC ( bind_addr ) ;
}
}
/* Add an address to the bind address list in the SCTP_bind_addr structure. */
int sctp_add_bind_addr ( struct sctp_bind_addr * bp , union sctp_addr * new ,
2006-07-22 01:49:25 +04:00
__u8 use_as_src , gfp_t gfp )
2005-04-17 02:20:36 +04:00
{
struct sctp_sockaddr_entry * addr ;
/* Add the address to the bind address list. */
addr = t_new ( struct sctp_sockaddr_entry , gfp ) ;
if ( ! addr )
return - ENOMEM ;
2006-11-21 04:04:10 +03:00
memcpy ( & addr - > a_h , new , sizeof ( * new ) ) ;
2005-04-17 02:20:36 +04:00
/* Fix up the port if it has not yet been set.
* Both v4 and v6 have the port at the same offset .
*/
2006-11-21 04:04:10 +03:00
if ( ! addr - > a_h . v4 . sin_port )
addr - > a_h . v4 . sin_port = bp - > port ;
2005-04-17 02:20:36 +04:00
2006-11-21 04:04:42 +03:00
flip_to_n ( & addr - > a , & addr - > a_h ) ;
2006-07-22 01:49:25 +04:00
addr - > use_as_src = use_as_src ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & addr - > list ) ;
list_add_tail ( & addr - > list , & bp - > address_list ) ;
SCTP_DBG_OBJCNT_INC ( addr ) ;
return 0 ;
}
/* Delete an address from the bind address list in the SCTP_bind_addr
* structure .
*/
int sctp_del_bind_addr ( struct sctp_bind_addr * bp , union sctp_addr * del_addr )
{
struct list_head * pos , * temp ;
struct sctp_sockaddr_entry * addr ;
list_for_each_safe ( pos , temp , & bp - > address_list ) {
addr = list_entry ( pos , struct sctp_sockaddr_entry , list ) ;
2006-11-21 04:04:10 +03:00
if ( sctp_cmp_addr_exact ( & addr - > a_h , del_addr ) ) {
2005-04-17 02:20:36 +04:00
/* Found the exact match. */
list_del ( pos ) ;
kfree ( addr ) ;
SCTP_DBG_OBJCNT_DEC ( addr ) ;
return 0 ;
}
}
return - EINVAL ;
}
/* Create a network byte-order representation of all the addresses
* formated as SCTP parameters .
*
* The second argument is the return value for the length .
*/
union sctp_params sctp_bind_addrs_to_raw ( const struct sctp_bind_addr * bp ,
2005-07-12 07:57:47 +04:00
int * addrs_len ,
2005-10-07 10:46:04 +04:00
gfp_t gfp )
2005-04-17 02:20:36 +04:00
{
union sctp_params addrparms ;
union sctp_params retval ;
int addrparms_len ;
union sctp_addr_param rawaddr ;
int len ;
struct sctp_sockaddr_entry * addr ;
struct list_head * pos ;
struct sctp_af * af ;
addrparms_len = 0 ;
len = 0 ;
/* Allocate enough memory at once. */
list_for_each ( pos , & bp - > address_list ) {
len + = sizeof ( union sctp_addr_param ) ;
}
/* Don't even bother embedding an address if there
* is only one .
*/
if ( len = = sizeof ( union sctp_addr_param ) ) {
retval . v = NULL ;
goto end_raw ;
}
retval . v = kmalloc ( len , gfp ) ;
if ( ! retval . v )
goto end_raw ;
addrparms = retval ;
list_for_each ( pos , & bp - > address_list ) {
addr = list_entry ( pos , struct sctp_sockaddr_entry , list ) ;
2006-11-21 04:04:10 +03:00
af = sctp_get_af_specific ( addr - > a_h . v4 . sin_family ) ;
len = af - > to_addr_param ( & addr - > a_h , & rawaddr ) ;
2005-04-17 02:20:36 +04:00
memcpy ( addrparms . v , & rawaddr , len ) ;
addrparms . v + = len ;
addrparms_len + = len ;
}
end_raw :
* addrs_len = addrparms_len ;
return retval ;
}
/*
* Create an address list out of the raw address list format ( IPv4 and IPv6
* address parameters ) .
*/
int sctp_raw_to_bind_addrs ( struct sctp_bind_addr * bp , __u8 * raw_addr_list ,
2005-10-07 10:46:04 +04:00
int addrs_len , __u16 port , gfp_t gfp )
2005-04-17 02:20:36 +04:00
{
union sctp_addr_param * rawaddr ;
struct sctp_paramhdr * param ;
union sctp_addr addr ;
int retval = 0 ;
int len ;
struct sctp_af * af ;
/* Convert the raw address to standard address format */
while ( addrs_len ) {
param = ( struct sctp_paramhdr * ) raw_addr_list ;
rawaddr = ( union sctp_addr_param * ) raw_addr_list ;
af = sctp_get_af_specific ( param_type2af ( param - > type ) ) ;
if ( unlikely ( ! af ) ) {
retval = - EINVAL ;
sctp_bind_addr_clean ( bp ) ;
break ;
}
af - > from_addr_param ( & addr , rawaddr , port , 0 ) ;
2006-07-22 01:49:25 +04:00
retval = sctp_add_bind_addr ( bp , & addr , 1 , gfp ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
/* Can't finish building the list, clean up. */
sctp_bind_addr_clean ( bp ) ;
break ;
}
len = ntohs ( param - > length ) ;
addrs_len - = len ;
raw_addr_list + = len ;
}
return retval ;
}
/********************************************************************
* 2 nd Level Abstractions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Does this contain a specified address? Allow wildcarding. */
int sctp_bind_addr_match ( struct sctp_bind_addr * bp ,
const union sctp_addr * addr ,
struct sctp_sock * opt )
{
struct sctp_sockaddr_entry * laddr ;
struct list_head * pos ;
list_for_each ( pos , & bp - > address_list ) {
laddr = list_entry ( pos , struct sctp_sockaddr_entry , list ) ;
2006-11-21 04:04:10 +03:00
if ( opt - > pf - > cmp_addr ( & laddr - > a_h , addr , opt ) )
2005-04-17 02:20:36 +04:00
return 1 ;
}
return 0 ;
}
/* Find the first address in the bind address list that is not present in
* the addrs packed array .
*/
union sctp_addr * sctp_find_unmatch_addr ( struct sctp_bind_addr * bp ,
const union sctp_addr * addrs ,
int addrcnt ,
struct sctp_sock * opt )
{
struct sctp_sockaddr_entry * laddr ;
union sctp_addr * addr ;
void * addr_buf ;
struct sctp_af * af ;
struct list_head * pos ;
int i ;
list_for_each ( pos , & bp - > address_list ) {
laddr = list_entry ( pos , struct sctp_sockaddr_entry , list ) ;
addr_buf = ( union sctp_addr * ) addrs ;
for ( i = 0 ; i < addrcnt ; i + + ) {
2006-11-21 04:02:40 +03:00
union sctp_addr tmp ;
2005-04-17 02:20:36 +04:00
addr = ( union sctp_addr * ) addr_buf ;
af = sctp_get_af_specific ( addr - > v4 . sin_family ) ;
if ( ! af )
return NULL ;
2006-11-21 04:02:40 +03:00
flip_to_h ( & tmp , addr ) ;
2005-04-17 02:20:36 +04:00
2006-11-21 04:04:10 +03:00
if ( opt - > pf - > cmp_addr ( & laddr - > a_h , & tmp , opt ) )
2005-04-17 02:20:36 +04:00
break ;
addr_buf + = af - > sockaddr_len ;
}
if ( i = = addrcnt )
2006-11-21 04:04:10 +03:00
return & laddr - > a_h ;
2005-04-17 02:20:36 +04:00
}
return NULL ;
}
/* Copy out addresses from the global local address list. */
static int sctp_copy_one_addr ( struct sctp_bind_addr * dest ,
union sctp_addr * addr ,
2005-10-07 10:46:04 +04:00
sctp_scope_t scope , gfp_t gfp ,
2005-07-12 07:57:47 +04:00
int flags )
2005-04-17 02:20:36 +04:00
{
int error = 0 ;
if ( sctp_is_any ( addr ) ) {
error = sctp_copy_local_addr_list ( dest , scope , gfp , flags ) ;
} else if ( sctp_in_scope ( addr , scope ) ) {
/* Now that the address is in scope, check to see if
* the address type is supported by local sock as
* well as the remote peer .
*/
if ( ( ( ( AF_INET = = addr - > sa . sa_family ) & &
( flags & SCTP_ADDR4_PEERSUPP ) ) ) | |
( ( ( AF_INET6 = = addr - > sa . sa_family ) & &
( flags & SCTP_ADDR6_ALLOWED ) & &
( flags & SCTP_ADDR6_PEERSUPP ) ) ) )
2006-07-22 01:49:25 +04:00
error = sctp_add_bind_addr ( dest , addr , 1 , gfp ) ;
2005-04-17 02:20:36 +04:00
}
return error ;
}
/* Is this a wildcard address? */
int sctp_is_any ( const union sctp_addr * addr )
{
struct sctp_af * af = sctp_get_af_specific ( addr - > sa . sa_family ) ;
if ( ! af )
return 0 ;
return af - > is_any ( addr ) ;
}
/* Is 'addr' valid for 'scope'? */
int sctp_in_scope ( const union sctp_addr * addr , sctp_scope_t scope )
{
sctp_scope_t addr_scope = sctp_scope ( addr ) ;
/* The unusable SCTP addresses will not be considered with
* any defined scopes .
*/
if ( SCTP_SCOPE_UNUSABLE = = addr_scope )
return 0 ;
/*
* For INIT and INIT - ACK address list , let L be the level of
* of requested destination address , sender and receiver
* SHOULD include all of its addresses with level greater
* than or equal to L .
*/
if ( addr_scope < = scope )
return 1 ;
return 0 ;
}
/********************************************************************
* 3 rd Level Abstractions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* What is the scope of 'addr'? */
sctp_scope_t sctp_scope ( const union sctp_addr * addr )
{
struct sctp_af * af ;
af = sctp_get_af_specific ( addr - > sa . sa_family ) ;
if ( ! af )
return SCTP_SCOPE_UNUSABLE ;
return af - > scope ( ( union sctp_addr * ) addr ) ;
}