7145 lines
240 KiB
C
7145 lines
240 KiB
C
/* ipsctp_sock.c - IPNET SCTP Socket API Extensions */
|
|
|
|
/*
|
|
* Copyright (c) 2007 Wind River Systems, Inc.
|
|
*
|
|
* The right to copy, distribute or otherwise make use of this software
|
|
* may be licensed only pursuant to the terms of an applicable Wind River
|
|
* license agreement. No license to Wind River intellectual property rights
|
|
* is granted herein. All rights not licensed by Wind River are reserved
|
|
* by Wind River.
|
|
*/
|
|
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipsctp_sock.c,v $ $Revision: 1.30.10.9 $
|
|
* $Source: /home/interpeak/CVSRoot/ipsctp/src/ipsctp_sock.c,v $
|
|
* $Author: hsun $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Fei Guo <fei.guo@windriver.com>
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
modification history
|
|
--------------------
|
|
01a,24jul08,fei Created, base on kcn 19nov07 version.
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*
|
|
* Sockets API Extensions for SCTP. Implementation is based on SCTP Socket
|
|
* draft-15.
|
|
*
|
|
* The socket layer for SCTP is an optional layer which sits above the
|
|
* SCTP ULP. It is the implementation goal to have this layer handles all
|
|
* the socket specific details and simply calls the SCTP ULP routine for
|
|
* futher SCTP specific processing.
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
#ifndef IPCOM_KERNEL
|
|
#define IPCOM_KERNEL
|
|
#endif
|
|
|
|
#include <ipsctp_config.h>
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_inet.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_type.h>
|
|
#include <ipcom_sock.h>
|
|
|
|
#include <ipsctp.h>
|
|
#include <ipsctp_h.h>
|
|
#include <ipsctp_sock.h>
|
|
|
|
#include <ipnet_usr_sock.h>
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
#define IPSCTP_MAX_SACK_DELAY 500
|
|
#define IPSCTP_SOCKOPT_R 0
|
|
#define IPSCTP_SOCKOPT_W 1
|
|
#define IPSCTP_SOCKOPT_E -1
|
|
|
|
IP_STATIC /*inline*/ int
|
|
ipsctp_sockaddr_check(struct Ip_sockaddr * addr)
|
|
{
|
|
if (IP_NULL == addr)
|
|
return IPNET_ERRNO(EFAULT);
|
|
|
|
if ((IP_AF_INET != ((struct Ip_sockaddr *)addr)->sa_family)
|
|
#ifdef IPCOM_USE_INET6
|
|
&& (IP_AF_INET6 != ((struct Ip_sockaddr *)addr)->sa_family)
|
|
#endif
|
|
)
|
|
return IPNET_ERRNO(EFAULT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define IPSCTP_SOCKADDR_CHECK(addr) \
|
|
do { \
|
|
int retV = ipsctp_sockaddr_check((struct Ip_sockaddr *)addr); \
|
|
if (0 < retV) \
|
|
return retV; \
|
|
} while(0)
|
|
|
|
#define IPSCTP_SOCK_CHECK(sock, listen_sock) \
|
|
do { \
|
|
ip_assert((sock)->ipcom.type == (listen_sock)->ipcom.type); \
|
|
ip_assert((sock)->ipcom.domain == (listen_sock)->ipcom.domain); \
|
|
ip_assert((sock)->proto == (listen_sock)->proto); \
|
|
} while(0)
|
|
|
|
#define IPSCTP_SOCK_INST_CHECK(sock, inst) \
|
|
do { \
|
|
(inst) = (sock)->sctp_inst; \
|
|
if (IP_NULL == (inst)) \
|
|
return IPNET_ERRNO(EINVAL); \
|
|
} while(0)
|
|
|
|
#define IPSCTP_SOCK_GETCB_BY_ASID(assoc_id) \
|
|
do { \
|
|
if (assoc_id) \
|
|
{ \
|
|
tcb = ipsctp_tcb_lookup_by_instance(inst, \
|
|
assoc_id, \
|
|
(IP_CONST Ipsctp_addr *)IP_NULL, \
|
|
IP_NULL); \
|
|
} \
|
|
/* this case for TCP, NOT specified */ \
|
|
else \
|
|
{ \
|
|
tcb = (Ipsctp_tcb *)ipcom_list_first(&inst->assoc_list); \
|
|
} \
|
|
} while(0)
|
|
|
|
#define IPSCTP_RTOINFO_CPY(dst, src) \
|
|
do { \
|
|
(dst)->srto_initial = (src)->srto_initial; \
|
|
(dst)->srto_max = (src)->srto_max; \
|
|
(dst)->srto_min = (src)->srto_min; \
|
|
} while(0)
|
|
|
|
#define IPSCTP_SNDRCV_CPY(dst, src) \
|
|
do { \
|
|
(dst)->sinfo_stream = (src)->sinfo_stream; \
|
|
(dst)->sinfo_ssn = (src)->sinfo_ssn; \
|
|
(dst)->sinfo_flags = (src)->sinfo_flags; \
|
|
(dst)->sinfo_pr_policy = (src)->sinfo_pr_policy; \
|
|
(dst)->sinfo_ppid = (src)->sinfo_ppid; \
|
|
(dst)->sinfo_context = (src)->sinfo_context; \
|
|
(dst)->sinfo_pr_value = (src)->sinfo_pr_value; \
|
|
(dst)->sinfo_tsn = (src)->sinfo_tsn; \
|
|
(dst)->sinfo_cumtsn = (src)->sinfo_cumtsn; \
|
|
} while(0)
|
|
#define IPSCTP_IS_V6ONLY(inst) \
|
|
((IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6)) \
|
|
&& (IP_BIT_ISFALSE(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4)))
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 LOCAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_is_any
|
|
*===========================================================================
|
|
* Description: check the SCTP address if any net address.
|
|
* Parameters: name - The SCTP address that you want be checked.
|
|
* Returns: IP_TRUE is any address,
|
|
* IP_FALSE not any address.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipsctp_is_any(IP_CONST Ipsctp_addr *name)
|
|
{
|
|
ip_assert(IP_NULL != name);
|
|
switch (name->sa.sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
if (IP_INADDR_ANY == name->sin.sin_addr.s_addr)
|
|
return IP_TRUE;
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
{
|
|
if (IP_IN6_IS_ADDR_UNSPECIFIED(&name->sin6.sin6_addr))
|
|
return IP_TRUE;
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return IP_FALSE;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_is_addr_valid
|
|
*===========================================================================
|
|
* Description: check the SCTP address if address valid or not.
|
|
* Parameters: name - The SCTP address that you want be checked.
|
|
* Returns: IP_TRUE is valid address,
|
|
* IP_FALSE not valid address.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipsctp_is_addr_valid(IP_CONST Ipsctp_addr *name)
|
|
{
|
|
ip_assert(IP_NULL != name);
|
|
switch (name->sa.sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
if (IP_INADDR_ANY == name->sin.sin_addr.s_addr)
|
|
return IP_FALSE;
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
if (IP_IN6_IS_ADDR_UNSPECIFIED(&name->sin6.sin6_addr))
|
|
return IP_FALSE;
|
|
break;
|
|
#endif
|
|
default:
|
|
return IP_FALSE;
|
|
}
|
|
|
|
return IP_TRUE;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_server_add_to_backlog
|
|
*===========================================================================
|
|
* Description: Adds a socket to the listening socket backlog.
|
|
* Parameters: listen_sock - The socket that received the initial connection
|
|
* request.
|
|
* sock - The socket to add.
|
|
* from_done - Set to IP_TRUE if the socket should be added to
|
|
* the list of finished sockets.
|
|
* should be searched
|
|
* Returns: None
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipsctp_server_add_to_backlog(Ipsctp_socket *listen_sock,
|
|
Ipsctp_socket *sock,
|
|
Ip_bool from_done)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
int i = 0;
|
|
int *backlog = IP_NULL ;
|
|
|
|
if ((!listen_sock) || (!listen_sock->sctp_inst) ||
|
|
(!sock) || (!sock->sctp_inst))
|
|
{
|
|
ip_assert(IP_FALSE);
|
|
return;
|
|
}
|
|
|
|
/* select which backlog list need to add */
|
|
inst = listen_sock->sctp_inst ;
|
|
if (IP_TRUE == from_done)
|
|
backlog = inst->backlog_done;
|
|
else
|
|
backlog = inst->backlog_pending;
|
|
|
|
/* choose a NULL entry */
|
|
i = 0;
|
|
while (backlog[i] != IP_SOCKERR)
|
|
i++;
|
|
|
|
ip_assert(i < inst->backlog_len);
|
|
|
|
backlog[i] = sock->ipcom.fd;
|
|
inst->backlog_used++;
|
|
sock->sctp_inst->parent = listen_sock;
|
|
|
|
IPSCTP_SOCK_CHECK(sock, listen_sock);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_server_remove_from_backlog
|
|
*===========================================================================
|
|
* Description: Removes a socket from the listening socket backlog.
|
|
* Parameters: listen_sock - The socket that received the initial connection
|
|
* request.
|
|
* sock - The socket to remove.
|
|
* from_done - Set to IP_TRUE if the socket should be removed from
|
|
* the list of finished sockets.
|
|
* should be searched
|
|
* Returns: None
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipsctp_server_remove_from_backlog(Ipsctp_socket *listen_sock,
|
|
Ipsctp_socket *sock,
|
|
Ip_bool from_done)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
int i = 0;
|
|
int *backlog = IP_NULL ;
|
|
|
|
if ((!listen_sock) || (!listen_sock->sctp_inst) ||
|
|
(!sock) || (!sock->sctp_inst))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when removing socket from backlog.");
|
|
return;
|
|
}
|
|
|
|
/* select the backlog list */
|
|
inst = listen_sock->sctp_inst;
|
|
if (IP_TRUE == from_done)
|
|
backlog = inst->backlog_done;
|
|
else
|
|
backlog = inst->backlog_pending;
|
|
|
|
/* loop the backlog list and delete the socket */
|
|
for (i = 0; i < listen_sock->sctp_inst->backlog_used; i++)
|
|
{
|
|
/* The specified socket descriptor was found */
|
|
if (backlog[i] == sock->ipcom.fd)
|
|
{
|
|
/* delete specified socket from the backlog list */
|
|
do
|
|
{
|
|
if (i + 1 == inst->backlog_len)
|
|
backlog[i] = IP_SOCKERR;
|
|
else
|
|
backlog[i] = backlog[i+1];
|
|
} while (backlog[i++] != IP_SOCKERR);
|
|
|
|
inst->backlog_used--;
|
|
sock->sctp_inst->parent = IP_NULL;
|
|
|
|
/* The reseted socket was the only socket that had
|
|
* finished the handshake */
|
|
if ((sock->ipcom.so_errno || IP_BIT_ISSET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_ACCEPTING))
|
|
&& (IP_TRUE == from_done)
|
|
&& (backlog[0] == IP_SOCKERR))
|
|
IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_CONNABORTED);
|
|
|
|
IPSCTP_SOCK_CHECK(sock, listen_sock);
|
|
|
|
return;
|
|
}
|
|
}
|
|
/* It's an error to call this function with a socket that is not in the backlog */
|
|
IP_PANIC();
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_kclose
|
|
*===========================================================================
|
|
* Description: Closes a SCTP socket from the kernel.
|
|
* Parameters: sock - The SCTP socket to close.
|
|
* Returns: None
|
|
*/
|
|
IP_PUBLIC void
|
|
ipsctp_sock_kclose(Ipsctp_socket *sock)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
|
|
if ((!sock) || (!(inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when kernal closing socket."IP_LF);
|
|
return;
|
|
}
|
|
|
|
/* set with the CLOSED bitmap and do close operation.
|
|
* NOTE : NOT release socket until finish close operation,
|
|
* so you can not use this resource. [20081120] */
|
|
if (IP_BIT_ISFALSE(inst->inst_flags, IPSCTP_SOCKET_KCLOSED))
|
|
{
|
|
IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_KCLOSED);
|
|
/* ipcom_accept/ipcom_connect and ipnet_sock_sctp_init
|
|
* will operate reference count, so do it again */
|
|
(void) IP_STACK(do_close)(sock);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_drop_sock
|
|
*===========================================================================
|
|
* Description: Drops all the connections for the specified socket.
|
|
* Parameters: sock - The socket to be droped.
|
|
* Returns: None.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipsctp_drop_sock(Ipsctp_socket *sock)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ipsctp_tcb *tcb_t = IP_NULL;
|
|
|
|
if ((!sock)||(!(inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when droping socket."IP_LF);
|
|
return;
|
|
}
|
|
|
|
/* if no TCB, just release the socket, e.g. : new socket setup error. */
|
|
if (0 == ipsctp_list_size(&inst->assoc_list))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB created when droping socket."IP_LF);
|
|
ipsctp_sock_kclose(sock);
|
|
return;
|
|
}
|
|
|
|
/* loop for all the association and then drop them */
|
|
ipsctp_list_for_each_safe(&inst->assoc_list, tcb, tcb_t, Ipsctp_tcb)
|
|
{
|
|
|
|
if (tcb->state > IPSCTP_STATE_CLOSED)
|
|
{
|
|
/* Try to inform the peer that the connection was dropped */
|
|
ipsctp_abort_assoc_tcb_with_err(tcb,
|
|
0,
|
|
IPSCTP_ECAUSE_USER_INITIATED_ABORT,
|
|
IPSCTP_FROM_SOCK + SCTP_LOC_01);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_server_handshake_done
|
|
*===========================================================================
|
|
* Description: Moves the socket to the queue of sockets that can be returned
|
|
* by ipcom_accept().
|
|
* Parameters: sock - The socket that received the initial connection
|
|
* request.
|
|
* Returns: None
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipsctp_server_handshake_done(Ipsctp_socket *sock)
|
|
{
|
|
Ipsctp_socket *listen_sock = IP_NULL;
|
|
|
|
if ((!sock) || (!sock->sctp_inst) ||
|
|
(!sock->sctp_inst->parent))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when four-way handshake done.");
|
|
return;
|
|
}
|
|
|
|
listen_sock = sock->sctp_inst->parent;
|
|
|
|
ipcom_enter_critical_region();
|
|
|
|
|
|
ipsctp_server_remove_from_backlog(listen_sock,
|
|
sock,
|
|
IP_FALSE);
|
|
ipsctp_server_add_to_backlog(listen_sock,
|
|
sock,
|
|
IP_TRUE);
|
|
ipcom_exit_critical_region();
|
|
|
|
IPSCTP_MIB_STATS(sctp_Passive_Estabs++);
|
|
/* ipcom_accept() will no longer block (which it could do, even if
|
|
* even if ipcom_socketselect() was used, if a socket in the backlog
|
|
* recieved a SHUTDOWN after reaching connected state)
|
|
*/
|
|
IP_BIT_CLR(listen_sock->sctp_inst->inst_flags, IPSCTP_SOCKET_CONNABORTED);
|
|
if (listen_sock->ipcom.event_cb != IP_NULL)
|
|
(void)listen_sock->ipcom.event_cb(&listen_sock->ipcom,
|
|
IP_NULL,
|
|
IP_SOEVENT_CB_ACCEPT);
|
|
IP_STACK(sock_data_avail)(listen_sock, 0, IP_SHUT_RDWR);
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
if (listen_sock->nae_rx_notifier)
|
|
{
|
|
/*
|
|
* This socket has registered a callback to get notification
|
|
* each time data is queued to this socket. The ownerhip of
|
|
* the packet is NOT transfered to the handler.
|
|
*/
|
|
ipnet_sock_rx_notifier(listen_sock, IP_NULL);
|
|
}
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_add_laddr
|
|
*===========================================================================
|
|
* Description: Add SCTP local address to local address list.
|
|
* Parameters: head - The local IP address list head.
|
|
* laddr - The local IP address.
|
|
* laddr_state - The local IP address state.
|
|
* Returns: 0 = success
|
|
* <0 = error code
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_add_laddr(Ipsctp_instance *inst,
|
|
Ipsctp_addr *laddr,
|
|
Ipsctp_laddr_state laddr_state)
|
|
{
|
|
Ipsctp_laddr_entry *laddr_entry = IP_NULL;
|
|
Ipsctp_laddr_entry *laddr_tmp = IP_NULL;
|
|
Ipsctp_af_ops *af_ops = IP_NULL;
|
|
|
|
if ((IP_NULL == inst)||(IP_NULL == inst->sock)||(IP_NULL == laddr))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when adding local address.");
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* if socket is V4, only V4 address can be added */
|
|
if ((IP_AF_INET == inst->sock->ipcom.domain)
|
|
&& (IP_AF_INET != laddr->sa.sa_family))
|
|
{
|
|
IPSCTP_DBG2(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Invalid AF when adding local address(%u != %u).",
|
|
inst->sock->ipcom.domain,
|
|
laddr->sa.sa_family);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* get the AF operation pointer by socket domain */
|
|
af_ops = ipsctp_get_af_ops(inst->sock->ipcom.domain);
|
|
if (af_ops == IP_NULL)
|
|
{
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : Invalid domain %d.", inst->sock->ipcom.domain);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
/* check if added address in the list already */
|
|
ipsctp_list_for_each(&inst->laddr_list, laddr_tmp, Ipsctp_laddr_entry)
|
|
{
|
|
if (af_ops->cmp_addr(&laddr_tmp->laddr, laddr))
|
|
return 0;
|
|
}
|
|
|
|
/* malloc entry memory */
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
laddr_entry = ipcom_shm_malloc(IPSCTP_SIZE_LADDR_ENTRY);
|
|
#else
|
|
laddr_entry = ipcom_malloc(IPSCTP_SIZE_LADDR_ENTRY);
|
|
#endif
|
|
if (IP_NULL == laddr_entry)
|
|
return IPNET_ERRNO(ENOMEM);
|
|
|
|
/* init the data structure */
|
|
ipsctp_memzero(laddr_entry, IPSCTP_SIZE_LADDR_ENTRY);
|
|
(void)ipcom_memcpy((void *)&laddr_entry->laddr,
|
|
(void *)laddr,
|
|
IPSCTP_SIZE_LADDR);
|
|
laddr_entry->state = laddr_state;
|
|
/*ipcom_microtime(&laddr_entry->start_time);*/
|
|
laddr_entry->start_time = ipsctp_time_now();
|
|
|
|
/* add the bind address to the list */
|
|
ipcom_list_insert_last(&inst->laddr_list, &laddr_entry->next);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_server_socket_new
|
|
*===========================================================================
|
|
* Description: Creates a new socket that will serve as the endpoint for
|
|
* the connection request just received on the listen socket.
|
|
* Parameters: [in,out] listen_sock - The socket that received the
|
|
* connection request.
|
|
* [in] local_addr - The local IPv4 or IPv6 address to which a
|
|
* SCTP connection is requested.
|
|
* [in] local_port - The local port (in host byte order) to
|
|
* which a SCTP connection is requested.
|
|
* [in] peer_addr - The IPv4 or IPv6 address from where the
|
|
* connection request was sent.
|
|
* [in] peer_port - The remote port from where the connection
|
|
* request was sent.
|
|
* [in] netif - The interface the segment was received on.
|
|
* [in] vr - The virtual router to use.
|
|
* Returns: NOT NULL is for the new created socket,
|
|
* NULL is for failing.
|
|
*
|
|
*/
|
|
IP_PUBLIC Ipsctp_socket *
|
|
ipsctp_server_socket_new(Ipsctp_socket *listen_sock,
|
|
IP_CONST void *local_addr,
|
|
Ip_u16 local_port,
|
|
IP_CONST void *peer_addr,
|
|
Ip_u16 peer_port,
|
|
Ipsctp_netif *netif,
|
|
Ip_u16 vr,
|
|
Ip_sa_family_t sa_family)
|
|
{
|
|
Ipsctp_laddr_entry *laddr_cur = IP_NULL;
|
|
int ret;
|
|
int fd;
|
|
Ipsctp_socket *sock = IP_NULL;
|
|
Ipnet_sock_ops *ops;
|
|
|
|
/* Backlog is full */
|
|
if (listen_sock->sctp_inst->backlog_used >= listen_sock->sctp_inst->backlog_len)
|
|
{
|
|
IPSCTP_DBG3(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : backlog is full for (%u), current is %u, MAX len is %u."IP_LF,
|
|
listen_sock->ipcom.fd,
|
|
listen_sock->sctp_inst->backlog_used,
|
|
listen_sock->sctp_inst->backlog_len);
|
|
if (listen_sock->sctp_inst->backlog_done[0] != IP_SOCKERR)
|
|
goto errout;
|
|
}
|
|
|
|
fd = IP_STACK(sys_socket)(listen_sock->ipcom.domain,
|
|
listen_sock->ipcom.type,
|
|
listen_sock->proto,
|
|
IP_FALSE);
|
|
if (fd == IP_SOCKERR)
|
|
{
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : create new socket for listen(%u) error."IP_LF,
|
|
listen_sock->ipcom.fd);
|
|
goto errout;
|
|
}
|
|
|
|
sock = ipstack->sockets[fd];
|
|
ip_assert(sock != IP_NULL);
|
|
ip_assert(sock->sctp_inst != IP_NULL);
|
|
|
|
/* Copy some flags from the listen socket */
|
|
sock->flags |= listen_sock->flags &
|
|
( IPSCTP_STACK_CONST(SOCKET_FLAG_NONBLOCKING)
|
|
| IPSCTP_STACK_CONST(SOCKET_FLAG_REUSEADDR)
|
|
| IPSCTP_STACK_CONST(SOCKET_FLAG_REUSEPORT)
|
|
| IPSCTP_STACK_CONST(SOCKET_FLAG_DONTROUTE)
|
|
| IPSCTP_STACK_CONST(SOCKET_FLAG_RECV_IF)
|
|
| IPSCTP_STACK_CONST(SOCKET_FLAG_TCP_NODELAY));
|
|
|
|
sock->vr_index = vr;
|
|
sock->flags |= listen_sock->flags & IPSCTP_STACK_CONST(SOCKET_FLAG_RECVPKTINFO);
|
|
|
|
/* This socket has not yet been given to any user */
|
|
IP_BIT_CLR(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_FLAG_USER_REF);
|
|
|
|
/* set new socket accepting flag */
|
|
IP_BIT_SET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_ACCEPTING);
|
|
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
(void)ipnet_sock_set_nae_rx_action(sock->ipcom.fd,
|
|
listen_sock->nae_rx_child_action);
|
|
sock->nae_action_ctx = listen_sock->nae_action_ctx;
|
|
#endif
|
|
|
|
ipcom_enter_critical_region();
|
|
|
|
/* add to backlog list and set to parent */
|
|
ipsctp_server_add_to_backlog(listen_sock,
|
|
sock,
|
|
IP_FALSE);
|
|
ipcom_exit_critical_region();
|
|
|
|
sock->recv_max_bytes = listen_sock->recv_max_bytes;
|
|
sock->send_max_bytes = listen_sock->send_max_bytes;
|
|
sock->keep_alive_probe_delay = listen_sock->keep_alive_probe_delay;
|
|
sock->sctp_inst->linger_time = listen_sock->sctp_inst->linger_time;
|
|
sock->sctp_inst->feature_flags = listen_sock->sctp_inst->feature_flags;
|
|
sock->sctp_inst->frag_point = listen_sock->sctp_inst->frag_point;
|
|
sock->sctp_inst->pd_point = listen_sock->sctp_inst->pd_point;
|
|
sock->sctp_inst->context = listen_sock->sctp_inst->context;
|
|
sock->sctp_inst->congestion_control_module = listen_sock->sctp_inst->congestion_control_module;
|
|
sock->sctp_inst->autoclose_time = listen_sock->sctp_inst->autoclose_time;
|
|
ipsctp_list_for_each(&listen_sock->sctp_inst->laddr_list, laddr_cur, Ipsctp_laddr_entry)
|
|
{
|
|
ret = ipsctp_add_laddr(sock->sctp_inst,
|
|
&laddr_cur->laddr,
|
|
IPSCTP_LADDR_NORMAL);
|
|
if (0 != ret)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : copy local address to new socket error."IP_LF);
|
|
}
|
|
if (IP_BIT_ISSET(listen_sock->sctp_inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
{
|
|
IP_BIT_SET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_BOUNDALL);
|
|
sock->sctp_inst->lport = listen_sock->sctp_inst->lport;
|
|
}
|
|
|
|
ops = ipnet_sock_get_ops(sock);
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (sock->ipcom.domain == IP_AF_INET)
|
|
{
|
|
struct Ip_sockaddr_in name;
|
|
|
|
/* Bind the socket */
|
|
IPCOM_SA_LEN_SET(&name, sizeof(name));
|
|
name.sin_family = IP_AF_INET;
|
|
name.sin_addr.s_addr = *(Ip_u32 *) local_addr;
|
|
name.sin_port = ip_htons(local_port);
|
|
ret = ops->bind(sock,
|
|
(struct Ip_sockaddr*) &name,
|
|
sizeof(name),
|
|
IP_FALSE);
|
|
if (ret >= 0)
|
|
{
|
|
#ifdef IPNET
|
|
sock->ip4->flags = listen_sock->ip4->flags;
|
|
sock->ip4->type_of_service = listen_sock->ip4->type_of_service;
|
|
name.sin_addr.s_addr = *(Ip_u32 *) peer_addr;
|
|
name.sin_port = ip_htons(peer_port);
|
|
ret = ops->connect(sock, (struct Ip_sockaddr *) &name, sizeof(name));
|
|
#else
|
|
sock->d->ip4.daddr_n = *(Ip_u32 *) peer_addr;
|
|
sock->dport = peer_port;
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
#endif /* IPCOM_USE_INET */
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (sock->ipcom.domain == IP_AF_INET6)
|
|
{
|
|
switch (sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
{
|
|
struct Ip_sockaddr_in name;
|
|
|
|
/* Bind the socket */
|
|
IPCOM_SA_LEN_SET(&name, sizeof(name));
|
|
name.sin_family = IP_AF_INET;
|
|
name.sin_addr.s_addr = *(Ip_u32 *) local_addr;
|
|
name.sin_port = ip_htons(local_port);
|
|
ret = ops->bind(sock,
|
|
(struct Ip_sockaddr*) &name,
|
|
sizeof(name),
|
|
IP_FALSE);
|
|
if (ret >= 0)
|
|
{
|
|
#ifdef IPNET
|
|
ipnet_sock_set_unreachable(sock);
|
|
sock->ip4->daddr_n = name.sin_addr.s_addr;
|
|
sock->dport = local_port;
|
|
ipnet_sock_set_reachable(sock);
|
|
#else
|
|
sock->d->ip4.daddr_n = *(Ip_u32 *) peer_addr;
|
|
sock->dport = peer_port;
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
#endif /* IPCOM_USE_INET */
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
{
|
|
struct Ip_sockaddr_in6 name;
|
|
|
|
/* Bind the socket */
|
|
IPCOM_SA_LEN_SET(&name, sizeof(name));
|
|
name.sin6_family = IP_AF_INET6;
|
|
name.sin6_flowinfo = 0;
|
|
name.sin6_port = ip_htons(local_port);
|
|
IPSCTP_STACK_CONST(IP6_SET_ADDR)(&name.sin6_addr, (struct Ip_in6_addr *) local_addr);
|
|
name.sin6_scope_id = ipnet_ip6_get_zone(netif->ipcom.ifindex, &name.sin6_addr);
|
|
ret = ops->bind(sock,
|
|
(struct Ip_sockaddr*) &name,
|
|
sizeof(name),
|
|
IP_FALSE);
|
|
if (ret >= 0)
|
|
{
|
|
sock->ip6->flags = listen_sock->ip6->flags;
|
|
sock->ip6->tclass = listen_sock->ip6->tclass;
|
|
IPNET_IP6_SET_ADDR(&name.sin6_addr, (struct Ip_in6_addr *) peer_addr);
|
|
name.sin6_port = ip_htons(peer_port);
|
|
ret = ops->connect(sock, (struct Ip_sockaddr *) &name, sizeof(name));
|
|
}
|
|
break;
|
|
}
|
|
#endif /* IPCOM_USE_INET6 */
|
|
default:
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : invalid sa_family = %u."IP_LF, sa_family);
|
|
goto errout;
|
|
}
|
|
}
|
|
else
|
|
#endif /* IPCOM_USE_INET6 */
|
|
{
|
|
/* Unsupported domain */
|
|
IP_PANIC();
|
|
goto errout;
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
IPSCTP_DBG2(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : bind(connect)[%u] new socket for listen(%u) error."IP_LF,
|
|
fd,
|
|
listen_sock->ipcom.fd);
|
|
goto errout;
|
|
}
|
|
|
|
/*
|
|
* The owner of the new socket must be the same as the owner of
|
|
* the listen socket
|
|
*/
|
|
sock->ipcom.pid = listen_sock->ipcom.pid;
|
|
|
|
IP_BIT_SET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTING));
|
|
|
|
return sock;
|
|
|
|
errout:
|
|
if (sock)
|
|
ipsctp_drop_sock(sock);
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_resize_backlog
|
|
*===========================================================================
|
|
* Description: change the socket backlog queue size.
|
|
* Parameters: inst - The SCTP instance that you want change.
|
|
* Returns: 0 = success,
|
|
* <0 = error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_resize_backlog(Ipsctp_instance *inst,
|
|
int backlog_size)
|
|
{
|
|
int *old_backlog[2];
|
|
int *backlog[2];
|
|
int old_backlog_size = 0;
|
|
int i = 0;
|
|
int b = 0;
|
|
|
|
if (IP_NULL == inst)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when resizing backlog.");
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
backlog[0] = ipcom_shm_malloc(backlog_size * sizeof(*inst->backlog_pending) * 2);
|
|
#else
|
|
backlog[0] = ipcom_malloc(backlog_size * sizeof(*inst->backlog_pending) * 2);
|
|
#endif
|
|
if (IP_NULL == backlog[0])
|
|
{
|
|
ip_assert(IP_FALSE);
|
|
return IPNET_ERRNO(ENOMEM);
|
|
}
|
|
|
|
old_backlog[0] = inst->backlog_pending;
|
|
old_backlog[1] = inst->backlog_done;
|
|
backlog[1] = backlog[0] + backlog_size;
|
|
|
|
old_backlog_size = inst->backlog_len;
|
|
for (b = 0; b < 2; b++)
|
|
{
|
|
/* Move all done and pending connections to the new backlog */
|
|
for (i = 0; i < IP_MIN(old_backlog_size, backlog_size); i++)
|
|
backlog[b][i] = old_backlog[b][i];
|
|
|
|
/* Mark remaning slots as unused */
|
|
for (; i < backlog_size; i++)
|
|
backlog[b][i] = IP_SOCKERR;
|
|
|
|
/* Close all sockets that did not fit into the new backlog size */
|
|
for (i = IP_MIN(old_backlog_size, backlog_size); i < old_backlog_size; i++)
|
|
{
|
|
if (old_backlog[b][i] != IP_SOCKERR)
|
|
{
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : drop socket (%u) for changing backlog size."IP_LF,
|
|
old_backlog[b][i]);
|
|
ipsctp_drop_sock(ipstack->sockets[old_backlog[b][i]]);
|
|
inst->backlog_used--;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
ipcom_shm_free(old_backlog[0]);
|
|
#else
|
|
ipcom_free(old_backlog[0]);
|
|
#endif
|
|
|
|
inst->backlog_len = (Ip_u16) backlog_size;
|
|
inst->backlog_pending = backlog[0];
|
|
inst->backlog_done = backlog[1];
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_drop_tcb
|
|
*===========================================================================
|
|
* Description: Drops the connection for the specified association.
|
|
* Parameters: tcb - The tcb associated with the connection to drop.
|
|
* error_code - The reason for dropping the connection.
|
|
* Returns: The specified error code.
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipsctp_drop_tcb(Ipsctp_tcb *tcb,
|
|
int error_code)
|
|
{
|
|
Ipsctp_instance *inst = tcb->instance;
|
|
#if 0
|
|
int ret = 0;
|
|
#endif
|
|
Ipsctp_socket *sock = tcb->sock;
|
|
|
|
#if 0
|
|
if (tcb->state >= IPSCTP_STATE_CLOSED)/* TODO : check */
|
|
{
|
|
/* Try to inform the peer that the connection was dropped */
|
|
ret = ipsctp_send_abort_u32(tcb,
|
|
IPSCTP_ECAUSE_USER_INITIATED_ABORT,
|
|
0/* TODO(td) */);
|
|
}
|
|
#endif
|
|
|
|
if (tcb->tcb_errno && error_code == -IP_ERRNO_ETIMEDOUT)
|
|
/* Report the soft-error to the user */
|
|
IP_STACK(sock_data_avail(sock, tcb->tcb_errno, IP_SHUT_RDWR));
|
|
else
|
|
/* Report the socket error to the user */
|
|
IP_STACK(sock_data_avail(sock, -error_code, IP_SHUT_RDWR));
|
|
|
|
if (IP_NULL != inst->parent)
|
|
{
|
|
ipcom_enter_critical_region();
|
|
ipsctp_server_remove_from_backlog(inst->parent,
|
|
sock,
|
|
tcb->state >= IPSCTP_STATE_ESTABLISHED);
|
|
|
|
ipcom_exit_critical_region();
|
|
}
|
|
|
|
/* change state to IPSCTP_STATE_CLOSED */
|
|
(void)ipsctp_change_state(tcb, IPSCTP_STATE_CLOSED);
|
|
|
|
return error_code;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_cleanup_receive_buffers
|
|
*===========================================================================
|
|
* Description: Frees TCB's chunks in receive queues.
|
|
* Parameters: sock - The socket for cleanup.
|
|
* Returns: None.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipsctp_cleanup_receive_buffer(Ipsctp_socket *sock)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_chunk_recv *chunk_recv = IP_NULL;
|
|
Ip_bool notify_usr = IP_FALSE;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == (inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when cleanuping receive buffers."IP_LF);
|
|
return;
|
|
}
|
|
|
|
/* Lock the receive buffers */
|
|
IPSCTP_DATA_LOCK(sock->rcv_lock);
|
|
|
|
/* Free receive buffers */
|
|
chunk_recv = (Ipsctp_chunk_recv *)ipcom_list_first(&inst->recv_list);
|
|
while (chunk_recv)
|
|
{
|
|
Ipsctp_chunk_recv *chunk_next = IP_NULL;
|
|
|
|
/* get next chunk */
|
|
chunk_next = (Ipsctp_chunk_recv *)ipcom_list_next(&chunk_recv->chunk.chunk_next);
|
|
|
|
ipcom_list_remove(&chunk_recv->chunk.chunk_next);
|
|
|
|
if (IP_BIT_ISFALSE(chunk_recv->chunk.flag,
|
|
IPSCTP_CHUNK_FLAG_RECV_FIN))
|
|
{
|
|
/* notify the user that data is available */
|
|
notify_usr = IP_TRUE;
|
|
}
|
|
|
|
/* Free the chunk */
|
|
#if 0
|
|
if (IP_NULL != chunk_recv->chunk.tcb)
|
|
{
|
|
IPSCTP_RECVBUF_LEN_SUB(sock, tcb, (chunk_recv->chunk.pkt->end - chunk_recv->chunk.pkt->start));
|
|
IPSCTP_RECVBUF_CNT(sock, tcb)--;
|
|
}
|
|
#endif
|
|
IPSCTP_FREE_RECV_CHUNK(chunk_recv);
|
|
|
|
chunk_recv = chunk_next;
|
|
}
|
|
/* zero the receive bytes in socket */
|
|
sock->ipcom.rcv_bytes = 0;
|
|
|
|
/* unlock the receive buffer */
|
|
IPSCTP_DATA_UNLOCK(sock->rcv_lock);
|
|
|
|
if (notify_usr)
|
|
{
|
|
/* notify the user that data is available */
|
|
IP_STACK(sock_data_avail)(sock, 0, IP_SHUT_RD);
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_cleanup
|
|
*===========================================================================
|
|
* Description: Frees all data in send and receive queues and removes + frees
|
|
* all timer events for this connection.
|
|
* Parameters: sock - The SCTP socket to free resouces for.
|
|
* Returns: None
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipsctp_sock_cleanup(Ipsctp_socket *sock)
|
|
{
|
|
if (IP_NULL == sock)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when clearnup.");
|
|
return;
|
|
}
|
|
/* Set socket status, only CLOSE and free do this. */
|
|
IP_BIT_CLR(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTING) |
|
|
IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTED));
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getname
|
|
*===========================================================================
|
|
* Description: Returns the socket address of the local side (if bound) or
|
|
* the peer side (if connected).
|
|
* Parameters: sock - Socket descriptor pointer.
|
|
* name - Will hold the socket address if the call succeeds.
|
|
* namelenp - The length of the 'name' buffer, will be updated
|
|
* to hold the actual size of 'name'.
|
|
* local - Set to IP_TRUE if the name of local size should be
|
|
* returned and IP_FALSE if the peer socket name should be returned.
|
|
* Returns: 0 = success, <0 negative error code.
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipsctp_getname(Ipsctp_socket *sock,
|
|
struct Ip_sockaddr *name,
|
|
Ip_socklen_t *namelen,
|
|
Ip_bool local)
|
|
{
|
|
#ifdef IPCOM_USE_INET6
|
|
struct Ip_sockaddr_in6 *name_in6 = IP_NULL;
|
|
#endif /* IPCOM_USE_INET6 */
|
|
#ifdef IPCOM_USE_INET
|
|
struct Ip_sockaddr_in *name_in = IP_NULL;
|
|
#endif /* IPCOM_USE_INET */
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ipsctp_laddr_entry *laddrs = IP_NULL;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
Ipsctp_addr *sctp_addr = IP_NULL;
|
|
Ipsctp_addr zero_addr;
|
|
Ip_in_port_t addr_port = 0;
|
|
int ret_val = 0;
|
|
|
|
if ((IP_NULL == sock)
|
|
|| (IP_NULL == name)
|
|
|| (IP_NULL == namelen)
|
|
|| (IP_NULL == (inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EBADF);
|
|
}
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if ((IP_AF_INET == sock->ipcom.domain)
|
|
&& (*namelen < sizeof(struct Ip_sockaddr_in)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid name length."IP_LF);
|
|
return IPNET_ERRNO(ENOBUFS);
|
|
}
|
|
#endif /* IPCOM_USE_INET */
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if ((IP_AF_INET6 == sock->ipcom.domain)
|
|
&& (*namelen < sizeof(struct Ip_sockaddr_in6)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid name length."IP_LF);
|
|
return IPNET_ERRNO(ENOBUFS);
|
|
}
|
|
#endif /* IPCOM_USE_INET6 */
|
|
|
|
switch (sock->ipcom.domain)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
*namelen = sizeof(struct Ip_sockaddr_in);
|
|
name_in = (struct Ip_sockaddr_in *)name;
|
|
ipcom_memset(name_in->sin_zero, 0, sizeof(name_in->sin_zero));
|
|
IPCOM_SA_LEN_SET(name_in, sizeof(struct Ip_sockaddr_in));
|
|
name_in->sin_family = IP_AF_INET;
|
|
break;
|
|
#endif /* IPCOM_USE_INET */
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
*namelen = sizeof(struct Ip_sockaddr_in6);
|
|
name_in6 = (struct Ip_sockaddr_in6 *) name;
|
|
IPCOM_SA_LEN_SET(name_in6, sizeof(struct Ip_sockaddr_in6));
|
|
name_in6->sin6_family = IP_AF_INET6;
|
|
name_in6->sin6_scope_id = sock->ip6->iscope_id;
|
|
name_in6->sin6_flowinfo = ip_htonl(sock->ip6->flowinfo);
|
|
break;
|
|
#endif /* IPCOM_USE_INET6 */
|
|
default:
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : invalid socket domain = %u."IP_LF, sock->ipcom.domain);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
ipsctp_memzero(&zero_addr, sizeof(zero_addr));
|
|
sctp_addr = &zero_addr;
|
|
if (IP_TRUE == local)
|
|
{
|
|
if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_BOUND))
|
|
return IPNET_ERRNO(ENOENT);
|
|
|
|
addr_port = inst->lport;
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
{
|
|
tcb = (Ipsctp_tcb *)ipcom_list_first(&inst->assoc_list);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
/* ret_val = IPNET_ERRNO(ENOTCONN); */
|
|
goto set_addr;
|
|
}
|
|
|
|
ipsctp_list_for_each(&tcb->transport_list,
|
|
tp,
|
|
Ipsctp_transport)
|
|
{
|
|
if ((sock->ipcom.domain == tp->paddr.sa.sa_family)
|
|
&& (IP_BIT_ISFALSE(tp->state, IPSCTP_ADDR_UNCONFIRMED))
|
|
&& (IP_BIT_ISSET(tp->state,IPSCTP_ADDR_REACHABLE)))
|
|
{
|
|
#ifdef IPCOM_USE_INET6
|
|
IP_CONST struct Ip_in6_addr *src6_addr = IP_NULL;
|
|
#endif /* IPCOM_USE_INET6 */
|
|
#ifdef IPCOM_USE_INET
|
|
IP_CONST struct Ip_in_addr *src_addr = IP_NULL;
|
|
#endif /* IPCOM_USE_INET */
|
|
|
|
switch (sock->ipcom.domain)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
src_addr = ipnet_ip4_get_src_addr(sock->vr_index,
|
|
&tp->paddr.sin.sin_addr,
|
|
IP_NULL,
|
|
IP_NULL);
|
|
if (IP_NULL == src_addr)
|
|
continue;
|
|
|
|
zero_addr.sin.sin_addr.s_addr = src_addr->s_addr;
|
|
zero_addr.sin.sin_port = ip_htons(inst->lport);
|
|
break;
|
|
#endif /* IPCOM_USE_INET */
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
src6_addr = ipnet_ip6_get_src_addr(sock->vr_index,
|
|
&tp->paddr.sin6.sin6_addr,
|
|
sock->ip6->iscope_id,
|
|
IP_NULL,
|
|
IP_NULL,
|
|
sock->ip6->prefer_src_flags);
|
|
if (IP_NULL == src6_addr)
|
|
continue;
|
|
|
|
IPNET_IP6_SET_ADDR(&zero_addr.sin6.sin6_addr, src6_addr);
|
|
zero_addr.sin6.sin6_port = ip_htons(inst->lport);
|
|
break;
|
|
#endif /* IPCOM_USE_INET6 */
|
|
default:
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : invalid socket domain."IP_LF);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ipsctp_list_for_each(&inst->laddr_list,
|
|
laddrs,
|
|
Ipsctp_laddr_entry)
|
|
{
|
|
if (sock->ipcom.domain == laddrs->laddr.sa.sa_family)
|
|
{
|
|
sctp_addr = &laddrs->laddr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* here include listen TCB */
|
|
tcb = (Ipsctp_tcb *)ipcom_list_first(&inst->assoc_list);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
ret_val = IPNET_ERRNO(ENOTCONN);
|
|
goto set_addr;
|
|
}
|
|
|
|
if ((IPSCTP_STATE_LISTEN != tcb->state)
|
|
&& (tcb->state < IPSCTP_STATE_ESTABLISHED))
|
|
return IPNET_ERRNO(ENOTCONN);
|
|
|
|
addr_port = tcb->rport;
|
|
|
|
ipsctp_list_for_each(&tcb->transport_list,
|
|
tp,
|
|
Ipsctp_transport)
|
|
{
|
|
if ((sock->ipcom.domain == tp->paddr.sa.sa_family)
|
|
&& (IP_BIT_ISFALSE(tp->state, IPSCTP_ADDR_UNCONFIRMED))
|
|
&& (IP_BIT_ISSET(tp->state, IPSCTP_ADDR_REACHABLE)))
|
|
{
|
|
sctp_addr = &tp->paddr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
set_addr:
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (IP_AF_INET == sock->ipcom.domain)
|
|
{
|
|
name_in->sin_addr.s_addr = sctp_addr->sin.sin_addr.s_addr;
|
|
name_in->sin_port = ip_htons(addr_port);
|
|
}
|
|
#endif /* IPCOM_USE_INET */
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (IP_AF_INET6 == sock->ipcom.domain)
|
|
{
|
|
name_in6->sin6_addr = sctp_addr->sin6.sin6_addr;
|
|
name_in6->sin6_port = ip_htons(addr_port);
|
|
}
|
|
#endif /* IPCOM_USE_INET6 */
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_free
|
|
*===========================================================================
|
|
* Description: Frees all resources allocated by the SCTP module for this socket.
|
|
* Parameters: sock - The SCTP socket to free resouces for.
|
|
* Returns: None
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipsctp_sock_free(Ipsctp_socket *sock)
|
|
{
|
|
Ipsctp_laddr_entry *laddr_cur = IP_NULL;
|
|
Ipsctp_laddr_entry *laddr_tmp = IP_NULL;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
|
|
if ((!sock)||(!sock->sctp_inst))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument when free socket.");
|
|
return;
|
|
}
|
|
|
|
inst = sock->sctp_inst;
|
|
/* Connection closed or in listen state, free all resouces held by the socket */
|
|
if (ipsctp_sock_state(sock, LISTENING))
|
|
{
|
|
Ipsctp_socket *backlog_sock = IP_NULL;
|
|
|
|
/* Listen socket, close all sockets in the backlog */
|
|
while (inst->backlog_pending && (IP_SOCKERR != inst->backlog_pending[0]))
|
|
{
|
|
backlog_sock = ipstack->sockets[inst->backlog_pending[0]];
|
|
/* send ABORT instead of graceful shutdown */
|
|
if (backlog_sock)
|
|
{
|
|
IPSCTP_DBG2(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : abort pending socket %u, reference count %u."IP_LF,
|
|
inst->backlog_pending[0],
|
|
ipcom_atomic_get(&backlog_sock->ref_count));
|
|
ipsctp_drop_sock(backlog_sock);
|
|
}
|
|
}
|
|
|
|
while (inst->backlog_done && (IP_SOCKERR != inst->backlog_done[0]))
|
|
{
|
|
backlog_sock = ipstack->sockets[inst->backlog_done[0]];
|
|
/* send ABORT instead of graceful shutdown */
|
|
if (backlog_sock)
|
|
{
|
|
IPSCTP_DBG2(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : abort done socket %u, reference count %u."IP_LF,
|
|
inst->backlog_done[0],
|
|
ipcom_atomic_get(&backlog_sock->ref_count));
|
|
ipsctp_drop_sock(backlog_sock);
|
|
}
|
|
}
|
|
|
|
ip_assert(inst->backlog_used == 0);
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
ipcom_shm_free(inst->backlog_pending);
|
|
#else
|
|
ipcom_free(inst->backlog_pending);
|
|
#endif
|
|
}
|
|
|
|
/* free local address list */
|
|
ipsctp_list_for_each_safe(&inst->laddr_list, laddr_cur, laddr_tmp, Ipsctp_laddr_entry)
|
|
{
|
|
ipcom_list_remove(&laddr_cur->next);
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
ipcom_shm_free(laddr_cur);
|
|
#else
|
|
ipcom_free(laddr_cur);
|
|
#endif
|
|
}
|
|
|
|
/* if no TCB, also need cleanup. */
|
|
ipsctp_sock_cleanup(sock);
|
|
|
|
ipsctp_timer_remove(inst, IP_NULL, IP_NULL, IPSCTP_TIMO_SECRET);
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_FLAG_LINGER))
|
|
{
|
|
ipsctp_timer_remove(inst, IP_NULL, IP_NULL, IPSCTP_TIMO_LINGER);
|
|
}
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_FLAG_SENDALL))
|
|
{
|
|
ipsctp_timer_remove(inst, IP_NULL, IP_NULL, IPSCTP_TIMO_SENDALL);
|
|
}
|
|
|
|
ipsctp_cleanup_receive_buffer(sock);
|
|
|
|
/* destory the lock here */
|
|
ipcom_spinlock_delete(&inst->tcb_lock);
|
|
sock->sctp_inst = IP_NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_close_association
|
|
*===========================================================================
|
|
* Description: Close a association of SCTP socket.
|
|
* Parameters: sock - The SCTP control block.
|
|
* Returns: None.
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipsctp_close_association(Ipsctp_tcb *tcb)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
|
|
if ((!tcb) || (!(inst = tcb->instance)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return;
|
|
}
|
|
|
|
/* do abort process in ipsctp_close
|
|
* NOTE : before check if LISTEN TCB */
|
|
if (0 == inst->linger_time)
|
|
return;
|
|
|
|
/* if the listen TCB, just free TCB, no SHUTDOWN packet sent */
|
|
if (IPSCTP_IS_LISTEN_TCB(tcb))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : it is a listen TCB, free it."IP_LF);
|
|
/* free all the resource, and destory socket,
|
|
* for decrease the socket reference count */
|
|
(void)ipsctp_change_state(tcb, IPSCTP_STATE_CLOSED);
|
|
return;
|
|
}
|
|
|
|
switch (tcb->state)
|
|
{
|
|
/* just close, no need wait. CASE :
|
|
* INIT -->
|
|
* <-- INIT-ACK
|
|
* ------------------------
|
|
* close then client socket
|
|
*
|
|
* in ipsctp_handle_init_ack will return if not COOKIE_WAIT,
|
|
* SO here we just close the association.
|
|
*
|
|
* test on FreeBSD and Linux, they will send ABORT to the peer. */
|
|
case IPSCTP_STATE_COOKIE_WAIT:
|
|
case IPSCTP_STATE_COOKIE_ECHOED:
|
|
(void)ipsctp_change_state(tcb, IPSCTP_STATE_CLOSED);
|
|
return;
|
|
|
|
case IPSCTP_STATE_SHUTDOWN_PENDING:
|
|
case IPSCTP_STATE_SHUTDOWN_RECEIVED:
|
|
case IPSCTP_STATE_SHUTDOWN_SENT:
|
|
case IPSCTP_STATE_SHUTDOWN_ACK_SENT:
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : in %s state when shutdown."IP_LF,
|
|
ipsctp_state_to_str(tcb->state));
|
|
return;
|
|
|
|
case IPSCTP_STATE_ESTABLISHED:
|
|
break;
|
|
|
|
case IPSCTP_STATE_LISTEN:
|
|
case IPSCTP_STATE_CLOSED:
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (IP_BIT_ISSET(tcb->tcb_flags, IPSCTP_TCBC_FLAG_ABORT))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : TCB abort when shutdown."IP_LF);
|
|
return;
|
|
}
|
|
|
|
/* if the waiting send queue is not null */
|
|
if (IP_FALSE == ipsctp_is_output_queue_empty(tcb))
|
|
{
|
|
if (IPSCTP_STATE_SHUTDOWN_PENDING == tcb->state)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : already in SHUTDOWN-PENDING state.\n");
|
|
return;
|
|
}
|
|
|
|
IP_STACK(sock_data_avail)(tcb->sock, 0, IP_SHUT_WR);
|
|
/* set to shutdown pending state */
|
|
(void)ipsctp_change_state(tcb, IPSCTP_STATE_SHUTDOWN_PENDING);
|
|
}
|
|
else
|
|
{
|
|
/* NOTE : if SHUTDOWN-ACK, go through. */
|
|
if (IPSCTP_STATE_SHUTDOWN_SENT == tcb->state)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : already in SHUTDOWN-SENT state.\n");
|
|
return;
|
|
}
|
|
|
|
/* stop timers */
|
|
ipsctp_stop_timers_for_shutdown(tcb);
|
|
/* send SHUTDOWN to peer endpoint */
|
|
if (ipsctp_send_shutdown(tcb, tcb->primary_transport) < 0)
|
|
{
|
|
/* output error information, T2 timer will do the following. */
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : send SHUTDOWN error!\n");
|
|
}
|
|
(void)ipsctp_change_state(tcb, IPSCTP_STATE_SHUTDOWN_SENT);
|
|
|
|
/* start T2 timer */
|
|
(void)ipsctp_timer_add(IP_NULL,
|
|
tcb,
|
|
tcb->primary_transport,
|
|
IPSCTP_TIMO_T2);
|
|
|
|
/* start T5 timer */
|
|
(void)ipsctp_timer_add(IP_NULL,
|
|
tcb,
|
|
tcb->primary_transport,
|
|
IPSCTP_TIMO_T5);
|
|
}
|
|
|
|
ipsctp_tx_segments(tcb);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_init
|
|
*===========================================================================
|
|
* Description: Initiazlies the socket for SCTP use.
|
|
* Parameters: sock - The socket on which SCTP should be used.
|
|
* Returns: 0 = success
|
|
* <0 = socket error code
|
|
*/
|
|
IP_PUBLIC int
|
|
ipsctp_sock_init(Ipsctp_socket *sock)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
int i = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == (inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument.");
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* Init socket and instance data structure */
|
|
inst->send_lowat = 1L;
|
|
inst->recv_lowat = 1L;
|
|
inst->sock = sock;
|
|
inst->lport = 0;
|
|
inst->linger_time = IPCOM_ADDR_INFINITE;
|
|
ipcom_list_init(&inst->assoc_list);
|
|
ipcom_list_init(&inst->laddr_list);
|
|
ipcom_list_init(&inst->recv_list);
|
|
ipcom_list_init(&inst->sendall_pkt);
|
|
ipcom_list_init(&inst->timers);
|
|
|
|
if (ipcom_spinlock_create(&inst->tcb_lock) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(CRIT, "Failed to initialize SCTP TCB Cache Lock.");
|
|
return IPNET_ERRNO(ENOMEM);
|
|
}
|
|
|
|
/* set TCP/UDP bit flag */
|
|
switch (IPSCTP_SOCK_TYPE(sock))
|
|
{
|
|
case IP_SOCK_STREAM:
|
|
IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_TCP);
|
|
break;
|
|
#ifdef IPSCTP_INCLUDE_UDP_LIKE
|
|
case IP_SOCK_SEQPACKET:
|
|
IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_UDP);
|
|
break;
|
|
case IP_SOCK_DGRAM:
|
|
#endif
|
|
default:
|
|
return IPNET_ERRNO(ESOCKTNOSUPPORT);
|
|
}
|
|
|
|
IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_FLAG_USER_REF);
|
|
|
|
/* Initialize Initiation Message option parameters */
|
|
inst->initmsg.sinit_num_ostreams = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Default.NumOutStreams", IPSCTP_DEFAULT_NUM_OUT_STREAMS_DEFAULT);
|
|
inst->initmsg.sinit_max_instreams = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Default.NumInStreams", IPSCTP_DEFAULT_NUM_IN_STREAMS_DEFAULT);
|
|
inst->initmsg.sinit_max_attempts = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Max.Init.Retransmits", IPSCTP_MAX_INIT_RETRANSMITS_DEFAULT);
|
|
inst->initmsg.sinit_max_init_timeo = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Rto.Max", IPSCTP_RTO_MAX_DEFAULT);
|
|
/* Initialize Retransmission Timeout parameters */
|
|
inst->rtoinfo.srto_initial = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Rto.Initial", IPSCTP_RTO_INITIAL_DEFAULT);
|
|
inst->rtoinfo.srto_max = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Rto.Max", IPSCTP_RTO_MAX_DEFAULT);
|
|
inst->rtoinfo.srto_min = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Rto.Min", IPSCTP_RTO_MIN_DEFAULT);
|
|
/* Initialize Peer Address parameters */
|
|
inst->paddrparam.spp_hbinterval = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.HB.Interval", IPSCTP_HB_INTERVAL_DEFAULT);
|
|
inst->paddrparam.spp_pathmaxrxt = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Max.RetrPerPath", IPSCTP_MAX_RETR_PER_PATH_DEFAULT);
|
|
inst->paddrparam.spp_pathmtu = 0;
|
|
inst->paddrparam.spp_flags = 0;
|
|
#ifdef IPCOM_USE_INET
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4))
|
|
inst->paddrparam.spp_ipv4_tos = sock->ip4->type_of_service;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6))
|
|
inst->paddrparam.spp_ipv6_flowlabel = sock->ip6->flowinfo;
|
|
#endif
|
|
/* Initialize Notification Interest Options parameters */
|
|
inst->feature_flags = 0;
|
|
/* Initialize Association parameters */
|
|
inst->assocparams.sasoc_asocmaxrxt = (Ip_u16)ipcom_sysvar_get_as_int0("ipsctp.Max.RetrPerAssoc", IPSCTP_MAX_RETR_PER_ASSOC_DEFAULT);
|
|
inst->assocparams.sasoc_cookie_life = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Valid.Cookie.Life", IPSCTP_VALID_COOKIE_LIFE_DEFAULT);
|
|
/* Initialize default send parameters */
|
|
ipsctp_memzero(&inst->sndrcvinfo, IPSCTP_SIZE_SNDRCVINFO);
|
|
|
|
inst->shut_guard_time = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Default.ShutdownGuardTime", IPSCTP_DEFAULT_SHUTDOWN_GUARD_TIME_DEFAULT);
|
|
inst->secret_time = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Default.SecretLifeTime", IPSCTP_DEFAULT_SECRET_LIFE_TIME_DEFAULT);
|
|
inst->current_secret_number = 0;
|
|
inst->last_secret_number = 0;
|
|
for (i = 0; i < IPSCTP_NUMBER_OF_SECRETS; i++)
|
|
inst->secret_key[0][i] = ipcom_random();
|
|
ipcom_microtime(&inst->secret_change_time);
|
|
ipsctp_timer_add(inst,
|
|
IP_NULL,
|
|
IP_NULL,
|
|
IPSCTP_TIMO_SECRET);
|
|
|
|
inst->frag_point = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Max.Segment", IPSCTP_MAX_SEGMENT_DEFAULT);
|
|
inst->max_burst = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Max.Burst", IPSCTP_MAX_BURST_DEFAULT);
|
|
inst->sack_info.sack_delay = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Sack.DelayedTime", IPSCTP_SACK_DELAYED_TIME_DEFAULT);
|
|
inst->sack_info.sack_freq = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Sack.Freq", IPSCTP_SACK_FREQ_DEFAULT);
|
|
inst->congestion_control_module = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.Cc.Mod", IPSCTP_CC_RFC2581);
|
|
inst->pd_point = (Ip_u32)ipcom_sysvar_get_as_int0("ipsctp.PartialDelivery.Point", (sock->recv_max_bytes >> 1));
|
|
inst->sws_receiver = IPSCTP_SWS_RECEIVER_DEF;
|
|
inst->sws_sender = IPSCTP_SWS_SENDER_DEF;
|
|
|
|
if (ipsctp_style(sock, TCP))
|
|
{
|
|
inst->get_tcb_info = ipsctp_usr_sock_get_tcb_info_tcp;
|
|
}
|
|
else
|
|
{
|
|
inst->get_tcb_info = ipsctp_usr_sock_get_tcb_info_udp;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_lookup_da_udp
|
|
*===========================================================================
|
|
* Description: Look up TCB based on local address and local port.
|
|
* Parameters: sock - The SCTP socket descriptor.
|
|
* dst_addr - The IP address in network order of the
|
|
* Returns: NOT NULL - matched TCB,
|
|
* NULL - no matched TCB.
|
|
*
|
|
*/
|
|
IP_STATIC Ipsctp_tcb *
|
|
ipsctp_sock_lookup_da_udp(Ipsctp_socket *sock,
|
|
struct Ip_sockaddr *dst_addr)
|
|
{
|
|
Ipsctp_socket *cur_sock = IP_NULL;
|
|
Ipsctp_instance *tmp_inst = IP_NULL;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ip_u16 rport = 0;
|
|
Ip_u32 i = 0;
|
|
union Ip_sockaddr_union *sa = (union Ip_sockaddr_union *)dst_addr;
|
|
|
|
if ((IP_NULL == ipnet->sockets) || (IP_NULL == dst_addr) || (IP_NULL == sock))
|
|
return IP_NULL;
|
|
|
|
switch (dst_addr->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
rport = ip_ntohs(sa->sin.sin_port);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
rport = ip_ntohs(sa->sin6.sin6_port);
|
|
break;
|
|
#endif
|
|
default:
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : return with error af."IP_LF);
|
|
return IP_NULL;
|
|
}
|
|
|
|
inst = sock->sctp_inst;
|
|
|
|
/* search in all socket arrary */
|
|
for (i = 0; i < ipnet_conf_max_sockets; i++)
|
|
{
|
|
if (IP_NULL == (cur_sock = ipnet->sockets[i]))
|
|
continue;
|
|
|
|
/* itself, continue */
|
|
if (sock == cur_sock)
|
|
continue;
|
|
|
|
/* check if SCTP socket */
|
|
if (IP_NULL == (tmp_inst = cur_sock->sctp_inst))
|
|
continue;
|
|
|
|
/* NOTE : no check for local address */
|
|
if (inst->lport != tmp_inst->lport)
|
|
continue;
|
|
|
|
/* search in socket association list */
|
|
ipsctp_list_for_each(&tmp_inst->assoc_list, tcb, Ipsctp_tcb)
|
|
{
|
|
if (rport != tcb->rport)
|
|
continue;
|
|
|
|
/* NOT include listen TCB */
|
|
if (IPSCTP_IS_LISTEN_TCB(tcb))
|
|
continue;
|
|
|
|
/* search in this association transport list */
|
|
ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport)
|
|
{
|
|
#ifdef IPSCTP_DEBUG
|
|
IPSCTP_DBG2(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : lookup transport address %s/%u UDP."IP_LF,
|
|
ipsctp_addr_ntop(&tp->paddr,
|
|
ipnet->log_buf,
|
|
256),
|
|
tcb->rport);
|
|
#endif
|
|
if (tp->paddr.sa.sa_family != dst_addr->sa_family)
|
|
continue;
|
|
|
|
switch (dst_addr->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
if (sa->sin.sin_addr.s_addr != tp->paddr.sin.sin_addr.s_addr)
|
|
continue;
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
if (IP_FALSE == IP_IN6_ARE_ADDR_EQUAL(&sa->sin6.sin6_addr, &tp->paddr.sin6.sin6_addr))
|
|
continue;
|
|
break;
|
|
#endif
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
return tcb;
|
|
}
|
|
}
|
|
}
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_bind
|
|
*===========================================================================
|
|
* Description: Bind address to socket.
|
|
*
|
|
* int bind(int sd, struct sockaddr *addr, socklen_t addrlen);
|
|
*
|
|
* sd: the socket descriptor returned by socket() call.
|
|
* addr: the address structure (either struct sockaddr_in or struct
|
|
* sockaddr_in6 defined in [RFC3493]).
|
|
* addrlen: the size of the address structure.
|
|
*
|
|
* Parameters: sock - The socket to bind.
|
|
* name - The bind address.
|
|
* namelen - Bind address length.
|
|
* Returns: 0 = success, <0 = error code
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipsctp_bind(Ipsctp_socket *sock,
|
|
IP_CONST struct Ip_sockaddr *name,
|
|
Ip_socklen_t namelen)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
int ret = 0;
|
|
#if defined(IPCOM_USE_INET) && defined(IPCOM_USE_INET6)
|
|
Ipsctp_addr bindall_saddr;
|
|
#endif
|
|
Ipsctp_addr saddr;
|
|
|
|
if ((IP_NULL == sock) || (namelen < sizeof(struct Ip_sockaddr))
|
|
|| (IP_NULL == (inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
IPSCTP_SOCKADDR_CHECK(name);
|
|
#ifdef IPSCTP_ALWAYS_BOUNDALL
|
|
#ifdef IPCOM_USE_INET
|
|
if (name->sa_family == IP_AF_INET)
|
|
{
|
|
((struct Ip_sockaddr_in *)name)->sin_addr.s_addr = 0;
|
|
}
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (name->sa_family == IP_AF_INET6)
|
|
{
|
|
struct Ip_sockaddr_in6 *name_in6 = (struct Ip_sockaddr_in6 *)name;
|
|
ipcom_memset(&name_in6->sin6_addr, 0, sizeof(name_in6->sin6_addr));
|
|
}
|
|
#endif
|
|
#endif
|
|
/* close by peer, refuse for rebind. */
|
|
if (ipsctp_sock_state(sock, CLOSED))
|
|
return IPNET_ERRNO(ESHUTDOWN);
|
|
|
|
/*
|
|
* return for the second bind, if bound all flag is set;
|
|
* this case will happen with not specify bind address.
|
|
*/
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
return 0;
|
|
|
|
/* set the bind port and instance flags */
|
|
inst->lport = sock->sport;
|
|
|
|
if (IP_TRUE == ipsctp_is_any((Ipsctp_addr *)name))
|
|
IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL);
|
|
|
|
(void)ipsctp_memzero(&saddr, sizeof(saddr));
|
|
(void)ipcom_memcpy((void *)&saddr.sa,
|
|
(void *)name,
|
|
IPCOM_SA_LEN_GET(name));
|
|
switch (name->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
if (0 == saddr.sin.sin_port)
|
|
saddr.sin.sin_port = ip_htons(inst->lport);
|
|
#ifdef IPCOM_USE_INET6 /* first bind V4 and socket is V6 */
|
|
if ((IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
&& (IP_AF_INET6 == sock->ipcom.domain))
|
|
{
|
|
(void)ipsctp_memzero(&bindall_saddr, sizeof(bindall_saddr));
|
|
IPCOM_SA_LEN_SET(&bindall_saddr.sa, sizeof(struct Ip_sockaddr_in6));
|
|
bindall_saddr.sin6.sin6_family = IP_AF_INET6;
|
|
bindall_saddr.sin6.sin6_port = ip_htons(inst->lport);
|
|
ret = ipsctp_add_laddr(inst,
|
|
&bindall_saddr,
|
|
IPSCTP_LADDR_NORMAL);
|
|
}
|
|
#endif
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
if (0 == saddr.sin6.sin6_port)
|
|
saddr.sin6.sin6_port = ip_htons(inst->lport);
|
|
#ifdef IPCOM_USE_INET
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
{
|
|
(void)ipsctp_memzero(&bindall_saddr, sizeof(bindall_saddr));
|
|
IPCOM_SA_LEN_SET(&bindall_saddr.sa, sizeof(struct Ip_sockaddr_in));
|
|
bindall_saddr.sin.sin_family = IP_AF_INET;
|
|
bindall_saddr.sin.sin_port = ip_htons(inst->lport);
|
|
ret = ipsctp_add_laddr(inst,
|
|
&bindall_saddr,
|
|
IPSCTP_LADDR_NORMAL);
|
|
}
|
|
#endif
|
|
break;
|
|
#endif
|
|
default:
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
/* add the bind address to the list, if bind all flag set
|
|
* the IP address is "0.0.0.0"
|
|
*/
|
|
ret = ipsctp_add_laddr(inst,
|
|
&saddr,
|
|
IPSCTP_LADDR_NORMAL);
|
|
#ifdef IPSCTP_DEBUG
|
|
IPSCTP_DBG2(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : bind address %s/%u successful."IP_LF,
|
|
ipsctp_addr_ntop(&saddr,
|
|
ipnet->log_buf,
|
|
256),
|
|
inst->lport);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_connect
|
|
*===========================================================================
|
|
* Description: The SCTP transport layer connect.
|
|
*
|
|
* int connect(int sd, const struct sockaddr *addr, socklen_t addrlen);
|
|
*
|
|
* sd - the socket descriptor of the endpoint.
|
|
* addr - the peer's address.
|
|
* addrlen - the size of the address.
|
|
*
|
|
* Parameters: sock - The socket to connect.
|
|
* to - The coneect peer's address.
|
|
* Returns: 0 = success, <0 = error code
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipsctp_connect(Ipsctp_socket *sock,
|
|
IP_CONST struct Ip_sockaddr *to)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
int ret = 0;
|
|
|
|
if ((!sock) || (!(inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
IPSCTP_SOCKADDR_CHECK(to);
|
|
|
|
/* it's a listen socket, if passive open just return. NOTE : for accept */
|
|
if (IP_NULL != inst->parent)
|
|
return 0;
|
|
|
|
#ifdef IPSCTP_DEBUG
|
|
IPSCTP_DBG3(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : connect %s with state(0x%X),style(%d)."IP_LF,
|
|
ipsctp_addr_ntop((Ipsctp_addr *)to,
|
|
ipnet->log_buf,
|
|
256),
|
|
sock->sctp_inst->inst_flags,
|
|
ipsctp_style(sock, TCP));
|
|
#endif
|
|
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_FLAG_PEELOFF))
|
|
return IPNET_ERRNO(EISCONN);
|
|
|
|
/* check the socket flags(for close state) */
|
|
/* close by peer, refuse for relisten */
|
|
if (ipsctp_sock_state(sock, CLOSED))
|
|
return IPNET_ERRNO(ECONNREFUSED);
|
|
|
|
/* check the socket flags(for listening state) */
|
|
if (ipsctp_sock_state(sock, LISTENING) && ipsctp_style(sock, TCP))
|
|
return IPNET_ERRNO(EISCONN);
|
|
|
|
/* check whether we already have a TCB with the same addresss */
|
|
if (IP_NULL != ipsctp_sock_lookup_da(sock, (struct Ip_sockaddr *)to))
|
|
return IPNET_ERRNO(EALREADY);
|
|
|
|
/* CHECK if a socket that matches an existing peeled-off association */
|
|
/* NOTE : not include TCP and UDP, if UDP, bind with a same local port
|
|
* and different IP address, then connect to the same destination,
|
|
* it's OK. if needed in ipsctp_usr_sock_get_tcb_info_udp? */
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_LISTENING)
|
|
&& ipsctp_sock_lookup_da_udp(sock, (struct Ip_sockaddr *)to))
|
|
return IPNET_ERRNO(EADDRNOTAVAIL);
|
|
|
|
/* allocate the TCB(association) */
|
|
ret = ipsctp_alloc_tcb(inst,
|
|
(Ipsctp_addr *)to,
|
|
0, /* alloc a new v-tag */
|
|
&tcb,
|
|
IP_NULL);
|
|
if ((ret < 0) || (IP_NULL == tcb))
|
|
return ret;
|
|
/* for RTO calculate */
|
|
(void)ipcom_microtime(&tcb->time_entered);
|
|
ip_assert(IP_NULL != ipcom_list_first(&inst->assoc_list));
|
|
|
|
/* set the connecting flag */
|
|
IP_BIT_SET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTING));
|
|
|
|
IP_BIT_SET(tcb->tcb_flags, IPSCTP_TCBC_FLAG_CONNECTING);
|
|
|
|
if (ipsctp_style(sock, TCP))
|
|
IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_CONNECTED);
|
|
|
|
/* send initialize packet to the peer */
|
|
ret = ipsctp_send_init(tcb);
|
|
if (IPNET_ERRNO(ENOBUFS) == ret)
|
|
{
|
|
/* TODO: schedule connect retry */
|
|
}
|
|
|
|
if (IPNET_ERRNO(EFAULT) == ret)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* change the tcb state */
|
|
(void)ipsctp_change_state(tcb, IPSCTP_STATE_COOKIE_WAIT);
|
|
|
|
if (IP_BIT_ISFALSE(tcb->tcb_flags, IPSCTP_TCBC_FLAG_CONNECTED))
|
|
{
|
|
if (tcb->tcb_errno)
|
|
return -tcb->tcb_errno;
|
|
}
|
|
|
|
/* block if no NON-BLOCKING flag set */
|
|
return IPNET_ERRNO(EINPROGRESS);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_listen
|
|
*===========================================================================
|
|
* Description: Puts a SCTP socket into listen state.
|
|
*
|
|
* int listen(int sd, int backlog);
|
|
* sd: the socket descriptor of the SCTP endpoint.
|
|
* backlog: this specifies the max number of outstanding associations
|
|
* allowed in the socket's accept queue. These are the associations
|
|
* that have finished the four-way initiation handshake (see Section
|
|
* 5 of [RFC4960]) and are in the ESTABLISHED state. Note, a backlog
|
|
* of '0' indicates that the caller no longer wishes to receive new
|
|
* associations.
|
|
*
|
|
* Parameters: sock - The socket on which to start listening for incoming
|
|
* connection requests.
|
|
* backlog - The maximum number of pending connections to
|
|
* keep track of. (use 0 to get default)
|
|
* Returns: 0 = success, <0 = error code.
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipsctp_listen(Ipsctp_socket *sock,
|
|
int backlog)
|
|
{
|
|
union Ip_sockaddr_union sa;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
int ret = 0;
|
|
|
|
if ((IP_NULL == sock) || (backlog < 0)
|
|
|| (IP_NULL == (inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check the socket flags(for close state) */
|
|
/* close by peer, refuse for relisten */
|
|
if (ipsctp_sock_state(sock, CLOSED))
|
|
return IPNET_ERRNO(ESHUTDOWN);
|
|
|
|
/* check the socket flags(for connect state) */
|
|
if (ipsctp_sock_state(sock, CONNECTED) && ipsctp_style(sock, TCP))
|
|
return IPNET_ERRNO(EISCONN);
|
|
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_FLAG_PEELOFF))
|
|
return IPNET_ERRNO(EISCONN);
|
|
|
|
if (IP_BIT_ISFALSE(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_BOUND)))
|
|
{
|
|
Ipnet_sock_ops *ops;
|
|
|
|
/* Not bound, bind to an ephemeral port and the unspecifed address */
|
|
ipsctp_memzero(&sa, sizeof(sa));
|
|
sa.sa.sa_family = (Ip_sa_family_t)sock->ipcom.domain;
|
|
IPCOM_SA_LEN_SET(&sa.sa, sizeof(sa));
|
|
ops = ipnet_sock_get_ops(sock);
|
|
ret = ops->bind(sock, &sa.sa, sizeof(sa), IP_TRUE);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
/* set to the default backlog number */
|
|
if (0 == backlog)
|
|
backlog = 5/*ipsctp_conf_default_backlog*/;
|
|
|
|
/* just return if new backlog size same with the old backlog size */
|
|
if ((IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_LISTENING)) &&
|
|
(backlog == inst->backlog_len))
|
|
return 0;
|
|
|
|
ret = ipsctp_resize_backlog(inst, backlog);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* just return if TCB has been allocated before */
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_LISTENING))
|
|
return 0;
|
|
/* make sure no TCB now, UDP-like allow it, same with FreeBSD and Linux */
|
|
/* ip_assert(0 == ipsctp_list_size(&inst->assoc_list)); */
|
|
|
|
/* set instance flag */
|
|
IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_LISTENING);
|
|
|
|
/* allocate the listening TCB(association) */
|
|
ret = ipsctp_alloc_dummy_tcb(inst, &tcb);
|
|
if ((ret < 0) || (IP_NULL == tcb))
|
|
return ret;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_accept
|
|
*===========================================================================
|
|
* Description: Accepts a connection attempt.
|
|
*
|
|
* new_sd = accept(int sd, struct sockaddr *addr, socklen_t *addrlen);
|
|
*
|
|
* new_sd - the socket descriptor for the newly formed association.
|
|
* sd - the listening socket descriptor.
|
|
* addr - on return, will contain the primary address of the peer
|
|
* endpoint.
|
|
* addrlen - on return, will contain the size of addr.
|
|
*
|
|
* Parameters: listen_sock - The listening socket to accept a socket from.
|
|
* addr - The address of the connecting node will be copied to
|
|
* this buffer if not equal to IP_NULL.
|
|
* addrlen - Holds the length of the addr buffer and will contain
|
|
* the length of the address copied to addr upon return.
|
|
* Returns: >= 0 = the socket descriptor of the connection. <0 = error code.
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipsctp_accept(Ipsctp_socket *listen_sock,
|
|
struct Ip_sockaddr *addr,
|
|
Ip_socklen_t *addrlen)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_socket *sock = IP_NULL;
|
|
int ret = 0;
|
|
|
|
if ((IP_NULL == listen_sock) || (IP_NULL == (inst = listen_sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EBADF);
|
|
}
|
|
|
|
#ifdef IPSCTP_INCLUDE_UDP_LIKE
|
|
if (ipsctp_style(listen_sock, UDP))
|
|
return IPNET_ERRNO(EOPNOTSUPP);
|
|
#endif
|
|
|
|
if (!ipsctp_sock_state(listen_sock, LISTENING))
|
|
return IPNET_ERRNO(EINVAL);
|
|
|
|
/* A ABORT was sent to the socket in the backlog after it
|
|
* reached established state */
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_CONNABORTED))
|
|
{
|
|
IP_BIT_CLR(inst->inst_flags, IPSCTP_SOCKET_CONNABORTED);
|
|
return IPNET_ERRNO(ECONNABORTED);
|
|
}
|
|
|
|
/* No socket ready at the moment, no-blocking case */
|
|
if (inst->backlog_done[0] == IP_SOCKERR)
|
|
{
|
|
if (IP_BIT_ISFALSE(listen_sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING))
|
|
return IPNET_ERRNO(EWOULDBLOCK);
|
|
else
|
|
return IPNET_ERRNO(EAGAIN);
|
|
}
|
|
|
|
/* Remove the oldest socket from the backlog and return it */
|
|
ip_assert(inst->backlog_done[0] >= 0);
|
|
ip_assert(inst->backlog_done[0] < IP_STACK(conf_max_sockets));
|
|
|
|
sock = ipstack->sockets[inst->backlog_done[0]];
|
|
ip_assert(sock != IP_NULL);
|
|
/* clear new socket accepting flag */
|
|
IP_BIT_CLR(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_ACCEPTING);
|
|
|
|
if (addr != IP_NULL)
|
|
{
|
|
/* here we get the primary peer address */
|
|
ret = IP_STACK(sys_getname)(sock->ipcom.fd,
|
|
addr,
|
|
addrlen,
|
|
IP_FALSE);
|
|
if (ret < 0)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : get peer address failed."IP_LF);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ipcom_enter_critical_region();
|
|
ipsctp_server_remove_from_backlog(listen_sock,
|
|
sock,
|
|
IP_TRUE);
|
|
ipcom_exit_critical_region();
|
|
|
|
ip_assert(inst->backlog_used < inst->backlog_len);
|
|
|
|
/* The user is given a reference to the new socket */
|
|
ipcom_atomic_inc(&sock->ref_count);
|
|
|
|
IP_BIT_SET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_FLAG_USER_REF);
|
|
|
|
IPSCTP_SOCK_CHECK(sock, listen_sock);
|
|
|
|
return sock->ipcom.fd;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_shutdown
|
|
*===========================================================================
|
|
* Description: Shutdown the read and/or write side of a SCTP socket.
|
|
*
|
|
* int shutdown(int sd, int how);
|
|
* sd - the socket descriptor of the association to be closed.
|
|
* how - Specifies the type of shutdown. The values are as follows:
|
|
* SHUT_RD - Disables further receive operations. No SCTP protocol
|
|
* action is taken.
|
|
* SHUT_WR - Disables further send operations, and initiates the
|
|
* SCTP shutdown sequence.
|
|
* SHUT_RDWR - Disables further send and receive operations and
|
|
* initiates the SCTP shutdown sequence.
|
|
*
|
|
* Parameters: sock - The SCTP socket to shutdown.
|
|
* Returns: None.
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipsctp_shutdown(Ipsctp_socket *sock, int how)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((!sock)||(!(inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return;
|
|
}
|
|
|
|
/* only TCP can do this operation */
|
|
if (ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : shutdown UDP socket."IP_LF);
|
|
return;
|
|
}
|
|
|
|
if (IP_SHUT_RD == how)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : shutdown RD mode."IP_LF);
|
|
return;
|
|
}
|
|
/* ------- flowing is for IP_SHUT_WR ------- */
|
|
|
|
/* if no TCB just return, only socket needed free.
|
|
* e.g. : just create sock then free(in ipsctp_close) */
|
|
tcb = (Ipsctp_tcb *)ipcom_list_first(&inst->assoc_list);
|
|
if (IP_NULL == tcb)
|
|
return;
|
|
|
|
ipsctp_close_association(tcb);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_close
|
|
*===========================================================================
|
|
* Description: Closes a SCTP connection.
|
|
* Parameters: sock - The SCTP socket to close.
|
|
* Returns: 0 = success, <0 = error code
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipsctp_close(Ipsctp_socket *sock)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ipsctp_tcb *tcb_t = IP_NULL;
|
|
|
|
if ((!sock)||(!(inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* ONLY one time */
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_CLOSED))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : socket already closed."IP_LF);
|
|
return 0;
|
|
}
|
|
|
|
IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_CLOSED);
|
|
|
|
if (IP_BIT_ISSET(sock->flags, IP_STACK_CONST(SOCKET_FLAG_CLOSED)))
|
|
IP_BIT_CLR(inst->inst_flags, IPSCTP_SOCKET_FLAG_USER_REF);
|
|
|
|
/* do abort process */
|
|
if (0 == inst->linger_time)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : close(abort) for linger."IP_LF);
|
|
/* list remove will be done in ipsctp_free_tcb. */
|
|
ipsctp_list_for_each_safe(&inst->assoc_list, tcb, tcb_t, Ipsctp_tcb)
|
|
{
|
|
/* if the listen TCB, just free */
|
|
if (IPSCTP_IS_LISTEN_TCB(tcb))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : free listen TCB for close."IP_LF);
|
|
(void)ipsctp_change_state(tcb, IPSCTP_STATE_CLOSED);
|
|
continue;
|
|
}
|
|
|
|
/* abort TCB */
|
|
ipsctp_abort_assoc_tcb_with_err(tcb,
|
|
IPSCTP_INVALID_NOTIFY_ERR,
|
|
IPSCTP_ECAUSE_USER_INITIATED_ABORT,
|
|
IPSCTP_FROM_IPSCTP + SCTP_LOC_02);
|
|
}
|
|
}
|
|
#ifdef IPSCTP_INCLUDE_UDP_LIKE
|
|
else if (ipsctp_style(sock, UDP))
|
|
{
|
|
ipsctp_list_for_each_safe(&inst->assoc_list, tcb, tcb_t, Ipsctp_tcb)
|
|
{
|
|
/* close the association */
|
|
ipsctp_close_association(tcb);
|
|
|
|
/* If TCB has established, update round trip time for this route */
|
|
/* TODO(td): may be I only need get the route entry from transport */
|
|
if (tcb->state >= IPSCTP_STATE_ESTABLISHED)
|
|
{
|
|
/* get the route and update */;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* if no TCB just return, only socket needed free.
|
|
* e.g. : just create sock then free */
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_KCLOSE_PENDING)
|
|
|| (0 == ipsctp_list_size(&inst->assoc_list)))
|
|
{
|
|
IP_BIT_CLR(inst->inst_flags, IPSCTP_SOCKET_KCLOSE_PENDING);
|
|
/* ensure the close operation */
|
|
if (ipcom_atomic_get(&sock->ref_count) > 1)
|
|
ipsctp_sock_kclose(sock);
|
|
else
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : close, ref_count so smaill."IP_LF);
|
|
}
|
|
|
|
/* startup linger timer if needed */
|
|
if ((0 != inst->linger_time)
|
|
&& (IPSCTP_INVALID_LINGER_TIME != inst->linger_time)
|
|
&& (IP_BIT_ISFALSE(inst->inst_flags, IPSCTP_SOCKET_FLAG_LINGER)))
|
|
{
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : start linger timer(%u second)."IP_LF,
|
|
inst->linger_time);
|
|
(void)ipsctp_timer_add(inst, IP_NULL, IP_NULL,
|
|
IPSCTP_TIMO_LINGER);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_lookup_sa
|
|
*===========================================================================
|
|
* Description: Look up TCB based on local address and local port.
|
|
* Parameters: vr - The virtual router the socket must be bound to.
|
|
* sa_family - adrress family.
|
|
* local_addr - The IP address in network order of the local
|
|
* address to match.
|
|
* local_port - The source port in HOST byte order.
|
|
* Returns: NOT NULL - matched socket,
|
|
* NULL - no matched socket.
|
|
*
|
|
*/
|
|
IP_PUBLIC Ipsctp_socket *
|
|
ipsctp_sock_lookup_sa(Ip_u16 vr,
|
|
int sa_family,
|
|
IP_CONST void *local_addr,
|
|
Ip_u16 local_port)
|
|
{
|
|
Ipsctp_socket *cur_sock = IP_NULL;
|
|
Ipsctp_laddr_entry *laddrs = IP_NULL;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ip_u32 i = 0;
|
|
|
|
if ((IP_NULL == ipnet->sockets) || (IP_NULL == local_addr))
|
|
return IP_NULL;
|
|
|
|
/* search in all socket arrary */
|
|
for (i = 0; i < ipnet_conf_max_sockets; i++)
|
|
{
|
|
if (IP_NULL == (cur_sock = ipnet->sockets[i]))
|
|
continue;
|
|
|
|
if (IP_NULL == (inst = cur_sock->sctp_inst))
|
|
continue;
|
|
|
|
if (vr != cur_sock->vr_index)
|
|
continue;
|
|
|
|
/* if 0, no need check local port */
|
|
if (local_port && local_port != inst->lport)
|
|
continue;
|
|
|
|
/* search in this association local bound address list
|
|
if BOUNDALL the compared address is 0.0.0.0 */
|
|
ipsctp_list_for_each(&inst->laddr_list, laddrs, Ipsctp_laddr_entry)
|
|
{
|
|
if (laddrs->laddr.sa.sa_family != sa_family)
|
|
continue;
|
|
|
|
switch (sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
if (*(Ip_in_addr_t *)local_addr != laddrs->laddr.sin.sin_addr.s_addr)
|
|
continue;
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
if (IP_FALSE == IP_IN6_ARE_ADDR_EQUAL(local_addr, &laddrs->laddr.sin6.sin6_addr))
|
|
continue;
|
|
break;
|
|
#endif
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
return cur_sock;
|
|
}
|
|
}
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_lookup_da
|
|
*===========================================================================
|
|
* Description: Look up TCB based on dst_addr.
|
|
* Parameters: sock - The SCTP socket descriptor.
|
|
* dst_addr - The IP address in network order of the
|
|
* Returns: NOT NULL - matched TCB,
|
|
* NULL - no matched TCB.
|
|
*
|
|
*/
|
|
IP_PUBLIC Ipsctp_tcb *
|
|
ipsctp_sock_lookup_da(Ipsctp_socket *sock,
|
|
struct Ip_sockaddr *dst_addr)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ip_u16 rport = 0;
|
|
union Ip_sockaddr_union *sa = (union Ip_sockaddr_union *)dst_addr;
|
|
|
|
if ((IP_NULL == dst_addr)
|
|
|| (IP_NULL == sock)
|
|
|| (IP_NULL == (inst = sock->sctp_inst)))
|
|
return IP_NULL;
|
|
|
|
switch (dst_addr->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
rport = ip_ntohs(sa->sin.sin_port);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
rport = ip_ntohs(sa->sin6.sin6_port);
|
|
break;
|
|
#endif
|
|
default:
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : return with error af."IP_LF);
|
|
return IP_NULL;
|
|
}
|
|
|
|
#ifdef IPSCTP_DEBUG
|
|
IPSCTP_DBG2(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : lookup destination address %s/%u."IP_LF,
|
|
ipsctp_addr_ntop((Ipsctp_addr *)dst_addr,
|
|
ipnet->log_buf,
|
|
256),
|
|
rport);
|
|
#endif
|
|
|
|
/* search in socket association list */
|
|
ipsctp_list_for_each(&inst->assoc_list, tcb, Ipsctp_tcb)
|
|
{
|
|
if (rport != tcb->rport)
|
|
continue;
|
|
|
|
/* NOT include listen TCB */
|
|
if (IPSCTP_IS_LISTEN_TCB(tcb))
|
|
continue;
|
|
|
|
/* search in this association transport list */
|
|
ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport)
|
|
{
|
|
#ifdef IPSCTP_DEBUG
|
|
IPSCTP_DBG2(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : lookup transport address %s/%u."IP_LF,
|
|
ipsctp_addr_ntop(&tp->paddr,
|
|
ipnet->log_buf,
|
|
256),
|
|
tcb->rport);
|
|
#endif
|
|
if (tp->paddr.sa.sa_family != dst_addr->sa_family)
|
|
continue;
|
|
|
|
switch (dst_addr->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
if (sa->sin.sin_addr.s_addr != tp->paddr.sin.sin_addr.s_addr)
|
|
continue;
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
if (IP_FALSE == IP_IN6_ARE_ADDR_EQUAL(&sa->sin6.sin6_addr, &tp->paddr.sin6.sin6_addr))
|
|
continue;
|
|
break;
|
|
#endif
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
return tcb;
|
|
}
|
|
}
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sockopt_check_rw
|
|
*===========================================================================
|
|
* Description: check socket option is read or write.
|
|
* Parameters: optname - the option name.
|
|
* Returns: IPSCTP_SOCKOPT_R = read only,
|
|
* IPSCTP_SOCKOPT_R = write only.
|
|
* IPSCTP_SOCKOPT_E = neither read-only or write-only.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_sockopt_check_rw(int optname)
|
|
{
|
|
switch (optname)
|
|
{
|
|
/* these options are defined by read-only */
|
|
case IPSCTP_GET_ASSOC_ID_LIST:
|
|
case IPSCTP_GET_ASSOC_NUMBER:
|
|
case IPSCTP_LOCAL_AUTH_CHUNKS:
|
|
case IPSCTP_PEER_AUTH_CHUNKS:
|
|
case IPSCTP_GET_PEER_ADDR_INFO:
|
|
case IPSCTP_STATUS:
|
|
return IPSCTP_SOCKOPT_R;
|
|
|
|
/* these options are defined by write-only */
|
|
case IPSCTP_SET_PEER_PRIMARY_ADDR:
|
|
case IPSCTP_AUTH_CHUNK:
|
|
case IPSCTP_AUTH_KEY:
|
|
case IPSCTP_AUTH_DEACTIVATE_KEY:
|
|
case IPSCTP_AUTH_DELETE_KEY:
|
|
return IPSCTP_SOCKOPT_W;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return IPSCTP_SOCKOPT_E;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_get_sa_family_size
|
|
*===========================================================================
|
|
* Description: Get address family length.
|
|
* Parameters: inst - the socket instance.
|
|
* sa_family - domain type.
|
|
* Returns: >0 = address family length, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_get_sa_family_size(Ipsctp_instance *inst,
|
|
int sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET6
|
|
if ((inst && IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4))
|
|
|| (IP_AF_INET6 == sa_family))
|
|
return sizeof(struct Ip_sockaddr_in6);
|
|
else
|
|
#endif
|
|
#ifdef IPCOM_USE_INET
|
|
if (IP_AF_INET == sa_family)
|
|
return sizeof(struct Ip_sockaddr_in);
|
|
else
|
|
#endif
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Invalid address family(%u)."IP_LF,
|
|
sa_family);
|
|
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_rtoinfo
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_rtoinfo(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
struct Ipsctp_rtoinfo *rto_info = (struct Ipsctp_rtoinfo *)optval;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_rtoinfo) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_rtoinfo.srto_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(rto_info->srto_assoc_id);
|
|
if (!tcb && rto_info->srto_assoc_id && ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* get the values from instance or tcb */
|
|
if (tcb)
|
|
{
|
|
rto_info->srto_initial = tcb->initial_rto;
|
|
rto_info->srto_max = tcb->max_rto;
|
|
rto_info->srto_min = tcb->min_rto;
|
|
}
|
|
else
|
|
{
|
|
IPSCTP_RTOINFO_CPY(rto_info, &inst->rtoinfo);
|
|
}
|
|
|
|
*optlen = sizeof(struct Ipsctp_rtoinfo);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket route information option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_associnfo
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_associnfo(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
struct Ipsctp_assocparams *params = (struct Ipsctp_assocparams *)optval;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_assocparams) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_assocparams.sasoc_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(params->sasoc_assoc_id);
|
|
if (!tcb && params->sasoc_assoc_id && ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* get the values from instance or tcb */
|
|
if (tcb)
|
|
{
|
|
params->sasoc_asocmaxrxt = tcb->max_send_times;
|
|
params->sasoc_peer_rwnd = tcb->peers_rwnd;
|
|
params->sasoc_local_rwnd = ipsctp_calc_rwnd(tcb);
|
|
params->sasoc_cookie_life = tcb->cookie_life;
|
|
|
|
/* get the peer transports count */
|
|
params->sasoc_number_peer_destinations = (Ip_u16)ipsctp_list_size(&tcb->transport_list);
|
|
}
|
|
else
|
|
{
|
|
params->sasoc_asocmaxrxt = inst->assocparams.sasoc_asocmaxrxt;
|
|
params->sasoc_cookie_life = inst->assocparams.sasoc_cookie_life;
|
|
params->sasoc_peer_rwnd = inst->sock->recv_max_bytes;
|
|
params->sasoc_local_rwnd = inst->sock->recv_max_bytes;
|
|
|
|
params->sasoc_number_peer_destinations = 0;
|
|
}
|
|
|
|
*optlen = sizeof(struct Ipsctp_assocparams);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket association option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_default_send_param
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_default_send_param(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_sndrcvinfo *sr_info = (struct Ipsctp_sndrcvinfo *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_sndrcvinfo) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_sndrcvinfo.sinfo_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(sr_info->sinfo_assoc_id);
|
|
if (!tcb && sr_info->sinfo_assoc_id && ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* get the values from instance or tcb */
|
|
if (tcb)
|
|
IPSCTP_SNDRCV_CPY(sr_info, &tcb->def_send);
|
|
else
|
|
IPSCTP_SNDRCV_CPY(sr_info, &inst->sndrcvinfo);
|
|
|
|
*optlen = sizeof(struct Ipsctp_sndrcvinfo);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket default send param option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_peer_addr_info
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_peer_addr_info(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_paddrinfo *addr_info = (struct Ipsctp_paddrinfo *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_paddrinfo) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument.");
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* if addr_info.spinfo_address is valid, then
|
|
* find the transport with it.
|
|
*/
|
|
tcb = ipsctp_tcb_lookup_by_instance(inst,
|
|
0,
|
|
(IP_CONST Ipsctp_addr *)&addr_info->spinfo_address,
|
|
&tp);
|
|
if (IP_NULL == tp)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No transport match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* set the value */
|
|
addr_info->spinfo_state = tp->state;
|
|
addr_info->spinfo_cwnd = tp->cwnd;
|
|
addr_info->spinfo_srtt = tp->srtt;
|
|
addr_info->spinfo_rto = tp->rto;
|
|
addr_info->spinfo_mtu = tp->pmtu;
|
|
if (tcb)
|
|
addr_info->spinfo_assoc_id = tcb->my_vtag;
|
|
|
|
*optlen = sizeof(struct Ipsctp_paddrinfo);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket peer address information option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_primary_addr
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_primary_addr(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_setprim *prim_info = (struct Ipsctp_setprim *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_setprim) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_setprim.ssp_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(prim_info->ssp_assoc_id);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
if ((IP_NULL == tcb->primary_transport)
|
|
|| (*optlen < IPCOM_SA_LEN_GET(&tcb->primary_transport->paddr.sa)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No primary transport or address length error."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* copy the primary destination address from tcb */
|
|
(void)ipcom_memcpy((void *)&prim_info->ssp_addr,
|
|
(void *)&tcb->primary_transport->paddr,
|
|
IPCOM_SA_LEN_GET(&tcb->primary_transport->paddr.sa));
|
|
|
|
*optlen = sizeof(struct Ipsctp_setprim);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket primary address option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_sctp_status
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_sctp_status(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_status *status = (struct Ipsctp_status *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_status) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_status.sstat_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(status->sstat_assoc_id);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* set status value to the return buffer */
|
|
status->sstat_state = tcb->state;
|
|
status->sstat_rwnd = ipsctp_calc_rwnd(tcb);
|
|
status->sstat_unackdata = (Ip_u16)ipsctp_list_size(&tcb->transmitted_chunk_list);
|
|
status->sstat_penddata = (Ip_u16)IPSCTP_RECVBUF_QUEUE_CNT(tcb);
|
|
status->sstat_instrms = tcb->streamincnt;
|
|
status->sstat_outstrms = tcb->streamoutcnt;
|
|
|
|
status->sstat_fragmentation_point = 0; /* TODO(td): */
|
|
|
|
/* get and check primary transport */
|
|
tp = tcb->primary_transport;
|
|
if (IP_NULL == tp)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No primary transport."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
switch (tp->paddr.sa.sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
(void)ipcom_memcpy((void *)&status->sstat_primary.spinfo_address,
|
|
(void *)&tp->paddr,
|
|
sizeof(struct Ip_sockaddr_in));
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
(void)ipcom_memcpy((void *)&status->sstat_primary.spinfo_address,
|
|
(void *)&tp->paddr,
|
|
sizeof(struct Ip_sockaddr_in6));
|
|
break;
|
|
#endif
|
|
default:
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : invalid transport af."IP_LF);
|
|
break;
|
|
}
|
|
|
|
status->sstat_primary.spinfo_state = tp->state;
|
|
status->sstat_primary.spinfo_cwnd = tp->cwnd;
|
|
status->sstat_primary.spinfo_srtt = tp->srtt;
|
|
status->sstat_primary.spinfo_rto = tp->rto;
|
|
status->sstat_primary.spinfo_mtu = tp->pmtu;
|
|
|
|
*optlen = sizeof(struct Ipsctp_status);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get sctp status option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_context
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_context(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_assoc_value) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
if (as_val->assoc_id)
|
|
{
|
|
/* find tcb with sctp_assoc_value.assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
as_val->assoc_value = tcb->context;
|
|
}
|
|
else
|
|
as_val->assoc_value = inst->context;
|
|
|
|
*optlen = sizeof(struct Ipsctp_assoc_value);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket context option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_active_key
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_active_key(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_authkeyid *keyid = (struct Ipsctp_authkeyid *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_authkeyid) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_authkeyid.scact_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(keyid->scact_assoc_id);
|
|
if (!tcb && keyid->scact_assoc_id && ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* TODO(td): here need lock the data */
|
|
/* get the values to instance or tcb */
|
|
if (tcb)
|
|
keyid->scact_keynumber = 0;
|
|
else
|
|
keyid->scact_keynumber = 0;
|
|
|
|
*optlen = sizeof(struct Ipsctp_authkeyid);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket active key option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_peer_auth_chunks
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_peer_auth_chunks(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_authkeyid *keyid = (struct Ipsctp_authkeyid *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_authkeyid) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_authkeyid.scact_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(keyid->scact_assoc_id);
|
|
if (!tcb && keyid->scact_assoc_id && ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
|
|
*optlen = sizeof(struct Ipsctp_authkeyid);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket peer auth chunks option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_local_auth_chunks
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_local_auth_chunks(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_authkeyid *keyid = (struct Ipsctp_authkeyid *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_authkeyid) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_authkeyid.scact_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(keyid->scact_assoc_id);
|
|
if (!tcb && keyid->scact_assoc_id && ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
|
|
*optlen = sizeof(struct Ipsctp_authkeyid);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket local auth chunks option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_peer_addr_params
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_peer_addr_params(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_paddrparams *params = (struct Ipsctp_paddrparams *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_paddrparams) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_paddrparams.spp_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(params->spp_assoc_id);
|
|
if (!tcb && params->spp_assoc_id && ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* if params.spp_address is valid, then
|
|
* find the transport with it.
|
|
*/
|
|
if (ipsctp_is_addr_valid((Ipsctp_addr *)¶ms->spp_address))
|
|
{
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : lookup transport address %s."IP_LF,
|
|
ipsctp_addr_ntop((Ipsctp_addr *)¶ms->spp_address,
|
|
ipnet->log_buf,
|
|
256));
|
|
|
|
tp = ipsctp_transport_lookup(tcb,
|
|
(IP_CONST Ipsctp_addr *)¶ms->spp_address);
|
|
}
|
|
|
|
/* get peer address parameters and set to the return buffer */
|
|
if (tp)
|
|
{
|
|
/* get from transport */
|
|
params->spp_hbinterval = tp->hbinterval;
|
|
params->spp_pathmaxrxt = (Ip_u16)tp->err_thresh;
|
|
params->spp_pathmtu = tp->pmtu;
|
|
}
|
|
else if (tcb)
|
|
{
|
|
/* get from association */
|
|
params->spp_hbinterval = tcb->hb_interval;
|
|
params->spp_pathmaxrxt = tcb->max_transport_retrans;
|
|
params->spp_pathmtu = tcb->pmtu;
|
|
}
|
|
else
|
|
{
|
|
/* get from instance */
|
|
params->spp_hbinterval = inst->paddrparam.spp_hbinterval;
|
|
params->spp_pathmaxrxt = inst->paddrparam.spp_pathmaxrxt;
|
|
params->spp_pathmtu = inst->paddrparam.spp_pathmtu;
|
|
}
|
|
|
|
*optlen = sizeof(struct Ipsctp_paddrparams);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket peer address params option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_events
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_events(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
#define IPSCTP_EVET_GET(evt_name, evt_flag) \
|
|
do { \
|
|
if (IP_BIT_ISSET(inst->feature_flags, evt_flag)) \
|
|
evt_sub->Ipsctp_ ## evt_name = 1; \
|
|
else \
|
|
evt_sub->Ipsctp_ ## evt_name = 0; \
|
|
} while(0)
|
|
|
|
struct Ipsctp_event_subscribe *evt_sub = (struct Ipsctp_event_subscribe *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == evt_sub) ||
|
|
(sizeof(struct Ipsctp_event_subscribe) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument.");
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* get the status value from instance */
|
|
IPSCTP_EVET_GET(data_io_event, IPSCTP_EVNT_FLAGS_DATA_IO);
|
|
IPSCTP_EVET_GET(association_event, IPSCTP_EVNT_FLAGS_ASSOCIATION);
|
|
IPSCTP_EVET_GET(address_event, IPSCTP_EVNT_FLAGS_ADDRESS);
|
|
IPSCTP_EVET_GET(send_failure_event, IPSCTP_EVNT_FLAGS_SEND_FAILURE);
|
|
IPSCTP_EVET_GET(peer_error_event, IPSCTP_EVNT_FLAGS_PEER_ERROR);
|
|
IPSCTP_EVET_GET(shutdown_event, IPSCTP_EVNT_FLAGS_SHUTDOWN);
|
|
IPSCTP_EVET_GET(partial_delivery_event, IPSCTP_EVNT_FLAGS_PARTIAL_DELIVERY);
|
|
IPSCTP_EVET_GET(adaptation_layer_event, IPSCTP_EVNT_FLAGS_ADAPTATION_LAYER);
|
|
IPSCTP_EVET_GET(authentication_event, IPSCTP_EVNT_FLAGS_AUTHENTICATION);
|
|
|
|
*optlen = sizeof(struct Ipsctp_event_subscribe);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket events option OK."IP_LF);
|
|
#endif
|
|
#undef IPSCTP_EVET_GET
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_initmsg
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_initmsg(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_initmsg *init_msg = (struct Ipsctp_initmsg *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == init_msg) ||
|
|
(sizeof(struct Ipsctp_initmsg) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument.");
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* get the init message from instance */
|
|
(void)ipcom_memcpy(init_msg,
|
|
&inst->initmsg,
|
|
sizeof(inst->initmsg));
|
|
|
|
*optlen = sizeof(struct Ipsctp_initmsg);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get init message option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_maxseg
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_maxseg(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ip_u32 hdr_len = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == as_val) ||
|
|
(sizeof(struct Ipsctp_assoc_value) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument.");
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4))
|
|
{
|
|
hdr_len = sizeof(Ipsctp_data_chunk_hdr)
|
|
+ sizeof(Ipsctp_sctp_hdr)
|
|
+ sizeof(Ipnet_pkt_ip);
|
|
}
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6))
|
|
{
|
|
hdr_len = sizeof(Ipsctp_data_chunk_hdr)
|
|
+ sizeof(Ipsctp_sctp_hdr)
|
|
+ sizeof(Ipnet_pkt_ip6);
|
|
}
|
|
#endif
|
|
|
|
/* find tcb with sctp_assoc_value.assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id);
|
|
/* get the values to instance or tcb */
|
|
if (IP_NULL != tcb)
|
|
{
|
|
if (tcb->frag_point > hdr_len)
|
|
as_val->assoc_value = tcb->frag_point - hdr_len;
|
|
else
|
|
as_val->assoc_value = 0;
|
|
}
|
|
else
|
|
{
|
|
if (inst->frag_point > hdr_len)
|
|
as_val->assoc_value = inst->frag_point - hdr_len;
|
|
else
|
|
as_val->assoc_value = 0;
|
|
}
|
|
|
|
*optlen = sizeof(struct Ipsctp_assoc_value);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get max segment option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_delay_ack
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_delay_ack(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_sack_info *ssi = (struct Ipsctp_sack_info *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_sack_info) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with Ipsctp_sack_info.sack_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(ssi->sack_assoc_id);
|
|
/* set the values to instance or tcb */
|
|
if (IP_NULL != tcb)
|
|
{
|
|
ssi->sack_delay = tcb->delayed_ack;
|
|
ssi->sack_freq = tcb->sack_freq;
|
|
}
|
|
else
|
|
{
|
|
ssi->sack_delay = inst->sack_info.sack_delay;
|
|
ssi->sack_freq = inst->sack_info.sack_freq;
|
|
}
|
|
|
|
*optlen = sizeof(struct Ipsctp_sack_info);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket delay ack option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_max_burst
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_max_burst(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_assoc_value) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
if (as_val->assoc_id)
|
|
{
|
|
/* find tcb with sctp_assoc_value.assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
as_val->assoc_value = tcb->max_burst;
|
|
}
|
|
else
|
|
as_val->assoc_value = inst->max_burst;
|
|
|
|
*optlen = sizeof(struct Ipsctp_assoc_value);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket context option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_peer_addrs_size
|
|
*===========================================================================
|
|
* Description: get all peer address's length
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_peer_addrs_size(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ip_u32 as_id = IP_NULL;
|
|
Ip_u32 tp_size = 0;
|
|
int sa_len = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(Ip_u32) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
as_id = *((Ip_u32 *)optval);
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with association id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(as_id);
|
|
if (IP_NULL == tcb)
|
|
goto no_paddrs;
|
|
|
|
/* search in transport address list */
|
|
ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport)
|
|
{
|
|
sa_len = ipsctp_get_sa_family_size(inst, tp->paddr.sa.sa_family);
|
|
if (0 > sa_len)
|
|
return IPNET_ERRNO(EINVAL);
|
|
tp_size += sa_len;
|
|
}
|
|
|
|
no_paddrs:
|
|
*((Ip_u32 *)optval) = tp_size;
|
|
*optlen = sizeof(Ip_u32);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket peer address size option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_peer_addrs
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_peer_addrs(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_getaddrs *paddr = (struct Ipsctp_getaddrs *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
void *pbuf = IP_NULL;
|
|
int total = 0;
|
|
int sa_len = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_getaddrs) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with association id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(paddr->sget_assoc_id);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
total = (int)(*optlen - sizeof(struct Ipsctp_getaddrs));
|
|
*optlen = sizeof(struct Ipsctp_getaddrs);
|
|
pbuf = (void *)&paddr->sget_addrs[0];
|
|
|
|
/* copy the transport address list */
|
|
ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport)
|
|
{
|
|
sa_len = ipsctp_get_sa_family_size(inst, tp->paddr.sa.sa_family);
|
|
if (0 > sa_len)
|
|
return IPNET_ERRNO(EINVAL);
|
|
|
|
if (total < sa_len)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No enough memory."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if ((IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4))
|
|
&& (IP_AF_INET == tp->paddr.sa.sa_family)) /* TODO(td) : if change V4&V6. */
|
|
ipsctp_sin_v4_map_v6((void *)&tp->paddr.sa, pbuf);
|
|
else
|
|
#endif
|
|
ipcom_memcpy((void *)pbuf, (void *)&tp->paddr, sa_len);
|
|
|
|
*optlen += (Ip_socklen_t)sa_len;
|
|
pbuf = (Ip_u8 *)pbuf + sa_len;
|
|
total -= sa_len;
|
|
}
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket peer address option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_local_addrs_size
|
|
*===========================================================================
|
|
* Description: get all local address's length
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_local_addrs_size(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
Ipsctp_laddr_entry *laddrs = IP_NULL;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ip_u32 la_size = 0;
|
|
int sa_len = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(Ip_u32) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_BOUND))
|
|
goto valid_ret;
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* loop for all the address entry if BOUNDALL case
|
|
* NOTE : maybe not the same address list as the association setup
|
|
*/
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
{
|
|
Ipnet_netif *netif = IP_NULL;
|
|
Ip_u32 i = 0;
|
|
/*
|
|
* IPNET_NETIF_FOR_EACH_ON_VR sets netif to NULL after the for loop,
|
|
* this is a performance optimized defination
|
|
*/
|
|
/* coverity[result_independent_of_operands] */
|
|
IPNET_NETIF_FOR_EACH_ON_VR(netif, sock->vr_index, i)
|
|
{
|
|
if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LOOPBACK))
|
|
continue;
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4))
|
|
{
|
|
Ipnet_ip4_addr_entry *addr4 = IP_NULL;
|
|
for (addr4 = netif->inet4_addr_list; addr4; addr4 = addr4->next)
|
|
{
|
|
/* only for unicast case */
|
|
if (IPNET_ADDR_TYPE_UNICAST != addr4->type)
|
|
continue;
|
|
|
|
sa_len = ipsctp_get_sa_family_size(inst, IP_AF_INET);
|
|
if (0 > sa_len)
|
|
return IPNET_ERRNO(EINVAL);
|
|
la_size += sa_len;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6))
|
|
{
|
|
Ipnet_ip6_addr_entry *addr6 = IP_NULL;
|
|
for (addr6 = netif->inet6_addr_list; addr6; addr6 = addr6->next)
|
|
{
|
|
/* only for unicast case */
|
|
if (IPNET_ADDR_TYPE_UNICAST != addr6->type)
|
|
continue;
|
|
|
|
sa_len = ipsctp_get_sa_family_size(inst, IP_AF_INET6);
|
|
if (0 > sa_len)
|
|
return IPNET_ERRNO(EINVAL);
|
|
la_size += sa_len;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ipsctp_list_for_each(&inst->laddr_list, laddrs, Ipsctp_laddr_entry)
|
|
{
|
|
sa_len = ipsctp_get_sa_family_size(inst, laddrs->laddr.sa.sa_family);
|
|
if (0 > sa_len)
|
|
return IPNET_ERRNO(EINVAL);
|
|
la_size += sa_len;
|
|
}
|
|
}
|
|
#if 0 /* maybe only bind 127.0.0.1 */
|
|
ip_assert(0 != la_size);
|
|
#endif
|
|
|
|
valid_ret:
|
|
*((Ip_u32 *)optval) = la_size;
|
|
*optlen = sizeof(Ip_u32);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket local address size option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_local_addrs
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_local_addrs(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_getaddrs *paddr = (struct Ipsctp_getaddrs *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
void *pbuf = IP_NULL;
|
|
int total = 0;
|
|
int sa_len = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_getaddrs) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance pointer */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
total = (int)(*optlen - sizeof(struct Ipsctp_getaddrs)); /* get from ipsctp_getsockopt_local_addrs_size */
|
|
*optlen = sizeof(struct Ipsctp_getaddrs);
|
|
pbuf = (void *)&paddr->sget_addrs[0];
|
|
|
|
/* loop for all the address entry if BOUNDALL case
|
|
* NOTE : maybe not the same address list as the association setup
|
|
*/
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
{
|
|
Ipsctp_af_ops *af_ops = IP_NULL;
|
|
Ipnet_netif *netif = IP_NULL;
|
|
Ip_u32 i = 0;
|
|
Ipsctp_addr addr_tmp;
|
|
|
|
/*
|
|
* IPNET_NETIF_FOR_EACH_ON_VR sets netif to NULL after the for loop,
|
|
* this is a performance optimized defination
|
|
*/
|
|
/* coverity[result_independent_of_operands] */
|
|
IPNET_NETIF_FOR_EACH_ON_VR(netif, sock->vr_index, i)
|
|
{
|
|
if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LOOPBACK))
|
|
continue;
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4))
|
|
{
|
|
Ipnet_ip4_addr_entry *addr4 = IP_NULL;
|
|
|
|
af_ops = ipsctp_get_af_ops(IP_AF_INET);
|
|
if (IP_NULL == af_ops)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No address family operation."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
for (addr4 = netif->inet4_addr_list; addr4; addr4 = addr4->next)
|
|
{
|
|
/* only for unicast case */
|
|
if (IPNET_ADDR_TYPE_UNICAST != addr4->type)
|
|
continue;
|
|
|
|
sa_len = ipsctp_get_sa_family_size(inst, IP_AF_INET);
|
|
if (0 > sa_len)
|
|
return IPNET_ERRNO(EINVAL);
|
|
|
|
if (total < sa_len)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No enough memory."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
af_ops->load_soaddr((void *)&addr4->ipaddr_n,
|
|
inst->lport,
|
|
&addr_tmp);
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4))
|
|
ipsctp_sin_v4_map_v6((void *)&addr_tmp.sa, pbuf);
|
|
else
|
|
#endif
|
|
ipcom_memcpy((void *)pbuf, (void *)&addr_tmp.sa, sa_len);
|
|
|
|
*optlen += (Ip_socklen_t)sa_len;
|
|
pbuf = (Ip_u8 *)pbuf + sa_len;
|
|
total -= sa_len;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6))
|
|
{
|
|
Ipnet_ip6_addr_entry *addr6 = IP_NULL;
|
|
|
|
sa_len = sizeof(struct Ip_sockaddr_in6);
|
|
af_ops = ipsctp_get_af_ops(IP_AF_INET6);
|
|
if (IP_NULL == af_ops)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No address family operation."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
for (addr6 = netif->inet6_addr_list; addr6; addr6 = addr6->next)
|
|
{
|
|
/* only for unicast case */
|
|
if (IPNET_ADDR_TYPE_UNICAST != addr6->type)
|
|
continue;
|
|
|
|
if (total < sa_len)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No enough memory."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
af_ops->load_soaddr((void *)&addr6->addr,
|
|
inst->lport,
|
|
&addr_tmp);
|
|
|
|
ipcom_memcpy((void *)pbuf,
|
|
(void *)&addr_tmp.sin6,
|
|
sizeof(struct Ip_sockaddr_in6));
|
|
|
|
*optlen += (Ip_socklen_t)sa_len;
|
|
pbuf = (Ip_u8 *)pbuf + sa_len;
|
|
total -= sa_len;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Ipsctp_laddr_entry *laddrs = IP_NULL;
|
|
|
|
/* copy all the local address */
|
|
ipsctp_list_for_each(&inst->laddr_list, laddrs, Ipsctp_laddr_entry)
|
|
{
|
|
sa_len = ipsctp_get_sa_family_size(inst, laddrs->laddr.sa.sa_family);
|
|
if (0 > sa_len)
|
|
return IPNET_ERRNO(EINVAL);
|
|
if (total < sa_len)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No enough memory."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if ((IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4))
|
|
&& (IP_AF_INET == laddrs->laddr.sa.sa_family)) /* TODO(td) : if change V4&V6. */
|
|
ipsctp_sin_v4_map_v6((void *)&laddrs->laddr.sa, pbuf);
|
|
else
|
|
#endif
|
|
ipcom_memcpy((void *)pbuf, (void *)&laddrs->laddr.sa, sa_len);
|
|
|
|
*optlen += (Ip_socklen_t)sa_len;
|
|
pbuf = (Ip_u8 *)pbuf + sa_len;
|
|
total -= sa_len;
|
|
}
|
|
}
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket peer address option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_addrs_len
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_addrs_len(Ipsctp_socket *sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval;
|
|
int addrs_len = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_assoc_value) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
addrs_len = ipsctp_get_sa_family_size(IP_NULL, as_val->assoc_value);
|
|
if (addrs_len < 0)
|
|
return IPNET_ERRNO(EINVAL);
|
|
|
|
as_val->assoc_value = addrs_len;
|
|
*optlen = sizeof(struct Ipsctp_assoc_value);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket peer address length option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_getsockopt_peeloff
|
|
*===========================================================================
|
|
* Description: Branch off an association from the listen socket.
|
|
* Parameters: listen_sock - the socket descriptor.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_getsockopt_peeloff(Ipsctp_socket *listen_sock,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
struct Ipsctp_peeloff *po = (struct Ipsctp_peeloff *)optval;
|
|
Ipsctp_chunk_recv *ch_recv = IP_NULL;
|
|
Ipsctp_chunk_recv *ch_recv_t = IP_NULL;
|
|
Ipsctp_instance *listen_inst = IP_NULL;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ip_u32 i = 0;
|
|
Ipsctp_chunk_send *ch_send = IP_NULL;
|
|
Ipsctp_chunk_send *ch_send_t = IP_NULL;
|
|
Ip_fd fd = IP_INVALID_SOCKET;
|
|
Ipsctp_socket *sock = IP_NULL;
|
|
Ipsctp_laddr_entry *laddr_cur = IP_NULL;
|
|
Ipcom_list rcv_list;
|
|
struct Ip_sockaddr name;
|
|
int ret = 0;
|
|
Ipsctp_timer *cur_tmr = IP_NULL;
|
|
Ipsctp_timer *cur_tmr_t = IP_NULL;
|
|
|
|
if ((IP_NULL == listen_sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_peeloff) > *optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
listen_inst = inst = listen_sock->sctp_inst;
|
|
/* 1,peeloff only branch from listen socket.
|
|
* 2,peeloff can not branch again from peeloffed socket. */
|
|
if ((0 == po->assoc_id)
|
|
|| (IP_BIT_ISFALSE(inst->inst_flags, IPSCTP_SOCKET_LISTENING))
|
|
|| (IP_BIT_ISSET(listen_inst->inst_flags, IPSCTP_SOCKET_FLAG_PEELOFF)))
|
|
return IPNET_ERRNO(EINVAL);
|
|
|
|
/* find tcb with sctp_peeloff.assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(po->assoc_id);
|
|
if ((IP_NULL == tcb) || (listen_sock != tcb->sock))
|
|
{
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : No TCB for peel off(0x%x)."IP_LF, po->assoc_id);
|
|
return IPNET_ERRNO(ENOTCONN);
|
|
}
|
|
|
|
/* client state */
|
|
if (IPSCTP_STATE_ESTABLISHED > tcb->state)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : association not open for peeloff."IP_LF);
|
|
return IPNET_ERRNO(ENOTCONN);
|
|
}
|
|
|
|
/* 9.2 draft-ietf-tsvwg-sctpsocket-19
|
|
* The application uses sctp_peeloff() call to branch off an association
|
|
* into a separate socket (Note the semantics are somewhat changed from
|
|
* the traditional one-to-one style accept() call). Note that the new
|
|
* socket is a one-to-one style socket. Thus it will be confined to
|
|
* operations allowed for a one-to-one style socket.
|
|
* NOTE : !! FreeBSD and Linux does not do like this */
|
|
fd = IP_STACK(sys_socket)(listen_sock->ipcom.domain,
|
|
IP_SOCK_STREAM,
|
|
listen_sock->proto,
|
|
IP_FALSE);
|
|
if (IP_INVALID_SOCKET == fd)
|
|
{
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : peeloff new socket for listen(%u) error."IP_LF,
|
|
listen_sock->ipcom.fd);
|
|
goto errout;
|
|
}
|
|
|
|
po->fd = fd;
|
|
sock = ipstack->sockets[fd];
|
|
inst = sock->sctp_inst;
|
|
ip_assert(sock != IP_NULL);
|
|
ip_assert(sock->sctp_inst != IP_NULL);
|
|
|
|
/* Copy some flags from the listen socket */
|
|
sock->flags |= listen_sock->flags &
|
|
( IPSCTP_STACK_CONST(SOCKET_FLAG_NONBLOCKING)
|
|
| IPSCTP_STACK_CONST(SOCKET_FLAG_REUSEADDR)
|
|
| IPSCTP_STACK_CONST(SOCKET_FLAG_REUSEPORT)
|
|
| IPSCTP_STACK_CONST(SOCKET_FLAG_DONTROUTE)
|
|
| IPSCTP_STACK_CONST(SOCKET_FLAG_RECV_IF)
|
|
| IPSCTP_STACK_CONST(SOCKET_FLAG_TCP_NODELAY));
|
|
#ifdef IPNET
|
|
sock->vr_index = listen_sock->vr_index;
|
|
sock->flags |= listen_sock->flags & IPSCTP_STACK_CONST(SOCKET_FLAG_RECVPKTINFO);
|
|
#endif
|
|
|
|
sock->dport = listen_sock->dport;
|
|
|
|
sock->recv_max_bytes = listen_sock->recv_max_bytes;
|
|
sock->send_max_bytes = listen_sock->send_max_bytes;
|
|
sock->keep_alive_probe_delay = listen_sock->keep_alive_probe_delay;
|
|
|
|
/* set new socket accepting flag */
|
|
IP_BIT_SET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_ACCEPTING);
|
|
sock->sctp_inst->inst_flags |= IPSCTP_SOCKET_FLAG_PEELOFF;
|
|
|
|
sock->sctp_inst->linger_time = listen_sock->sctp_inst->linger_time;
|
|
sock->sctp_inst->feature_flags = listen_sock->sctp_inst->feature_flags;
|
|
sock->sctp_inst->frag_point = listen_sock->sctp_inst->frag_point;
|
|
sock->sctp_inst->pd_point = listen_sock->sctp_inst->pd_point;
|
|
sock->sctp_inst->context = listen_sock->sctp_inst->context;
|
|
sock->sctp_inst->congestion_control_module = listen_sock->sctp_inst->congestion_control_module;
|
|
sock->sctp_inst->autoclose_time = listen_sock->sctp_inst->autoclose_time;
|
|
ipsctp_list_for_each(&listen_sock->sctp_inst->laddr_list, laddr_cur, Ipsctp_laddr_entry)
|
|
{
|
|
ret = ipsctp_add_laddr(sock->sctp_inst,
|
|
&laddr_cur->laddr,
|
|
IPSCTP_LADDR_NORMAL);
|
|
if (0 != ret)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : copy local address to peeloff socket error."IP_LF);
|
|
}
|
|
if (IP_BIT_ISSET(listen_sock->sctp_inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
{
|
|
IP_BIT_SET(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_BOUNDALL);
|
|
}
|
|
sock->sctp_inst->lport = listen_sock->sctp_inst->lport;
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (sock->ipcom.domain == IP_AF_INET)
|
|
{
|
|
struct Ip_sockaddr_in *name_in = (struct Ip_sockaddr_in *)&name;
|
|
|
|
sock->ip4->saddr_n = listen_sock->ip4->saddr_n;
|
|
#ifdef IPNET
|
|
sock->ip4->flags = listen_sock->ip4->flags;
|
|
sock->ip4->daddr_n = listen_sock->ip4->daddr_n;
|
|
#else
|
|
sock->d->ip4.daddr_n = listen_sock->d->ip4.daddr_n;
|
|
#endif
|
|
name_in->sin_family = IP_AF_INET;
|
|
name_in->sin_port = ip_htons(sock->sctp_inst->lport);
|
|
}
|
|
else
|
|
#endif /* IPCOM_USE_INET */
|
|
#ifdef IPCOM_USE_INET6
|
|
if (sock->ipcom.domain == IP_AF_INET6)
|
|
{
|
|
struct Ip_sockaddr_in6 *name_in6 = (struct Ip_sockaddr_in6 *)&name;
|
|
|
|
sock->ip6->saddr = listen_sock->ip6->saddr;
|
|
#ifdef IPNET
|
|
sock->ip6->flags = listen_sock->ip6->flags;
|
|
sock->ip6->daddr = listen_sock->ip6->daddr;
|
|
#else
|
|
sock->d->ip6.daddr = sock->d->ip6.daddr;
|
|
#endif
|
|
name_in6->sin6_family = IP_AF_INET6;
|
|
name_in6->sin6_flowinfo = 0;
|
|
name_in6->sin6_port = ip_htons(sock->sctp_inst->lport);
|
|
name_in6->sin6_scope_id = 0;
|
|
}
|
|
else
|
|
#endif /* IPCOM_USE_INET6 */
|
|
{
|
|
/* Unsupported domain */
|
|
IP_PANIC();
|
|
goto errout;
|
|
}
|
|
|
|
/* Only need to add the reference count, for the release use.
|
|
* for Ipnet_bound_socket is a local structure.
|
|
* Only need (domain/proto/vr/local_port) */
|
|
ret = ipnet_sock_bind_to_port(sock, &name, IP_FALSE);
|
|
if (ret < 0)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : bind port to peeloff socket error."IP_LF);
|
|
goto errout;
|
|
}
|
|
/* NOTE : must set BOUND and CONNECTED flag */
|
|
IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_BOUND | IPNET_SOCKET_FLAG_CONNECTED);
|
|
|
|
/* The owner of the new socket must be the same as the owner of the listen socket */
|
|
sock->ipcom.pid = listen_sock->ipcom.pid;
|
|
|
|
if ((IP_BIT_ISSET(tcb->instance->inst_flags, IPSCTP_SOCKET_FLAG_AUTOCLOSE)))
|
|
{
|
|
IP_BIT_CLR(sock->sctp_inst->inst_flags, IPSCTP_SOCKET_FLAG_AUTOCLOSE);
|
|
sock->sctp_inst->autoclose_time = 0;
|
|
(void)ipsctp_timer_remove(tcb->instance,
|
|
tcb,
|
|
IP_NULL,
|
|
IPSCTP_TIMO_AUTOCLOSE);
|
|
}
|
|
/* change the timer instance pointer if needed. */
|
|
tcb->sock = sock;
|
|
if (tcb->dack_timer.inst)
|
|
{
|
|
tcb->dack_timer.inst = sock->sctp_inst;
|
|
}
|
|
if (tcb->asconf_timer.inst)
|
|
{
|
|
tcb->asconf_timer.inst = sock->sctp_inst;
|
|
}
|
|
if (tcb->shut_guard_timer.inst)
|
|
{
|
|
tcb->shut_guard_timer.inst = sock->sctp_inst;
|
|
}
|
|
if (tcb->autoclose_timer.inst)
|
|
{
|
|
tcb->autoclose_timer.inst = sock->sctp_inst;
|
|
}
|
|
if (tcb->delayed_event_timer.inst)
|
|
{
|
|
tcb->delayed_event_timer.inst = sock->sctp_inst;
|
|
}
|
|
if (tcb->delete_prim_timer.inst)
|
|
{
|
|
tcb->delete_prim_timer.inst = sock->sctp_inst;
|
|
}
|
|
ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport)
|
|
{
|
|
if (tp->rxt_timer.inst)
|
|
{
|
|
tp->rxt_timer.inst = sock->sctp_inst;
|
|
}
|
|
if (tp->hb_timer.inst)
|
|
{
|
|
tp->hb_timer.inst = sock->sctp_inst;
|
|
}
|
|
if (tp->fr_timer.inst)
|
|
{
|
|
tp->fr_timer.inst = sock->sctp_inst;
|
|
}
|
|
}
|
|
#ifndef IPCOM_FORWARDER_NAE
|
|
ipcom_list_insert_first(&ipsctp->active_head, &inst->active);
|
|
/* peel off all the timers, Ipcom_list MUST be the first elem */
|
|
ipsctp_list_for_each_safe(&listen_inst->timers,
|
|
cur_tmr,
|
|
cur_tmr_t,
|
|
Ipsctp_timer)
|
|
{
|
|
if (inst == cur_tmr->inst)
|
|
{
|
|
ipcom_list_remove(&cur_tmr->list);
|
|
ipcom_list_insert_last(&inst->timers, &cur_tmr->list);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* change the list */
|
|
IPSCTP_DATA_TCBLOCK(listen_inst->tcb_lock);
|
|
IPSCTP_DATA_LOCK(listen_sock->rcv_lock);
|
|
ipcom_list_remove(&tcb->next);
|
|
IPSCTP_DATA_UNLOCK(listen_sock->rcv_lock);
|
|
IPSCTP_DATA_TCBUNLOCK(listen_inst->tcb_lock);
|
|
|
|
if (ipcom_atomic_sub_and_return(&listen_sock->ref_count, 1) < 1)
|
|
{
|
|
ip_assert(IP_FALSE);
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : UDP ref_count < 1."IP_LF);
|
|
}
|
|
|
|
/* ?? */
|
|
IPSCTP_DATA_TCBLOCK(inst->tcb_lock);
|
|
IPSCTP_DATA_LOCK(sock->rcv_lock);
|
|
ipcom_list_insert_last(&sock->sctp_inst->assoc_list, &tcb->next);
|
|
IPSCTP_DATA_UNLOCK(sock->rcv_lock);
|
|
IPSCTP_DATA_TCBUNLOCK(inst->tcb_lock);
|
|
/* given a reference to the new socket */
|
|
ipcom_atomic_inc(&sock->ref_count);
|
|
|
|
ipcom_list_init(&rcv_list);
|
|
|
|
/* decrease the listen socket send buffer and increase the new socket send buffer.
|
|
* NOTE : it has ensure the snd_bytes < send_max_bytes */
|
|
ipsctp_list_for_each_safe(&tcb->transmitted_chunk_list,
|
|
ch_send,
|
|
ch_send_t,
|
|
Ipsctp_chunk_send)
|
|
{
|
|
IPSCTP_ATOMIC_ZERO_SUB(listen_sock->snd_bytes, ch_send->length);
|
|
(void)ipcom_atomic_add_and_return(&sock->snd_bytes, ch_send->length);
|
|
}
|
|
for (i = 0; i < tcb->streamoutcnt; i++)
|
|
{
|
|
ipsctp_list_for_each_safe(&tcb->streams_out[i].chunk_list,
|
|
ch_send,
|
|
ch_send_t,
|
|
Ipsctp_chunk_send)
|
|
{
|
|
IPSCTP_ATOMIC_ZERO_SUB(listen_sock->snd_bytes, ch_send->length);
|
|
ipcom_atomic_add(&sock->snd_bytes, ch_send->length);
|
|
}
|
|
}
|
|
|
|
IPSCTP_DATA_LOCK(listen_sock->rcv_lock);
|
|
/* NOTE : no IPSCTP_USE_MULTIBUF define */
|
|
ipsctp_list_for_each_safe(&listen_inst->recv_list,
|
|
ch_recv,
|
|
ch_recv_t,
|
|
Ipsctp_chunk_recv)
|
|
{
|
|
if (tcb == ch_recv->chunk.tcb)
|
|
{
|
|
ipcom_list_remove(&ch_recv->chunk.chunk_next);
|
|
IPSCTP_RECVBUF_CNT_SUB(listen_sock, IP_NULL, 1);
|
|
IPSCTP_RECVBUF_LEN_SUB(listen_sock, IP_NULL, ch_recv->length);
|
|
IPSCTP_RECVBUF_QUEUE_LEN(tcb) -= ch_recv->length;
|
|
IPSCTP_RECVBUF_QUEUE_CNT(tcb)--;
|
|
|
|
/* add to new sock length */
|
|
ipcom_list_insert_last(&rcv_list, &ch_recv->chunk.chunk_next);
|
|
}
|
|
}
|
|
IPSCTP_DATA_UNLOCK(listen_sock->rcv_lock);
|
|
|
|
/* for the new length */
|
|
tcb->instance = sock->sctp_inst;
|
|
|
|
/* if the data come between the two LOCK, it will correctly append to
|
|
* the new socket, becase we have set this.
|
|
* NOTE : ?? need test ?? */
|
|
|
|
IPSCTP_DATA_LOCK(sock->rcv_lock);
|
|
ipsctp_list_for_each_safe(&rcv_list,
|
|
ch_recv,
|
|
ch_recv_t,
|
|
Ipsctp_chunk_recv)
|
|
{
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG4(IPSCTP_DBG_ALL, "IPSCTP : chunk type %u, chunk flag %u, chunk length %u, pkt length %u."IP_LF,
|
|
ch_recv->chunk.chunk_type,
|
|
ch_recv->chunk.flag,
|
|
ch_recv->chunk.chunk_end - ch_recv->chunk.chunk_start,
|
|
ch_recv->chunk.pkt->end - ch_recv->chunk.pkt->start);
|
|
ipsctp_print_hex((void *)&ch_recv->chunk.pkt->data[ch_recv->chunk.pkt->start],
|
|
"chunk packet:\n",
|
|
ch_recv->chunk.pkt->end - ch_recv->chunk.pkt->start);
|
|
#endif
|
|
ipcom_list_remove(&ch_recv->chunk.chunk_next);
|
|
|
|
/* add to new sock length */
|
|
ipcom_list_insert_last(&inst->recv_list, &ch_recv->chunk.chunk_next);
|
|
IPSCTP_RECVBUF_CNT(sock, tcb)++;
|
|
IPSCTP_RECVBUF_LEN_ADD(tcb, ch_recv->length);
|
|
IPSCTP_RECVBUF_QUEUE_LEN(tcb) += ch_recv->length;
|
|
IPSCTP_RECVBUF_QUEUE_CNT(tcb)++;
|
|
}
|
|
IPSCTP_DATA_UNLOCK(sock->rcv_lock);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket peeloff option OK, new fd = %u."IP_LF, fd);
|
|
#endif
|
|
return 0;
|
|
|
|
errout:
|
|
if (sock)
|
|
ipsctp_drop_sock(sock);
|
|
|
|
po->fd = fd;
|
|
return IPNET_ERRNO(ECONNREFUSED);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* 7.2. setsockopt(), getsockopt()
|
|
*
|
|
* Applications use setsockopt() and getsockopt() to set or retrieve
|
|
* socket options. Socket options are used to change the default
|
|
* behavior of sockets calls. They are described in Section 8
|
|
*
|
|
* The syntax is:
|
|
*
|
|
* ret = getsockopt(int sd, int level, int optname, void *optval,
|
|
* socklen_t *optlen);
|
|
* ret = setsockopt(int sd, int level, int optname, const void *optval,
|
|
* socklen_t optlen);
|
|
*
|
|
*
|
|
* sd - the socket descriptor.
|
|
* level - set to IPPROTO_SCTP for all SCTP options.
|
|
* optname - the option name.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer (or the length of the option
|
|
* returned).
|
|
*
|
|
* All socket options set on a 1-to-1 listening sockets also apply all
|
|
* accepted sockets. All socket options set on a 1-to-many socket using
|
|
* the assoc_id 0 applies for all future assocs on the socket.
|
|
* Parameters: sock - the socket descriptor.
|
|
* level - set to IPPROTO_SCTP for all SCTP options.
|
|
* optname - the option name.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipsctp_getsockopt(Ipsctp_socket *sock,
|
|
int level,
|
|
int optname,
|
|
void *optval,
|
|
Ip_socklen_t *optlen)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
int ret = 0;
|
|
int val = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == (inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG3(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Get socket option(sock %u, level 0x%x, optname 0x%x)."IP_LF,
|
|
sock->ipcom.fd,
|
|
level,
|
|
optname);
|
|
#endif
|
|
if (IP_SOL_SOCKET == level)
|
|
{
|
|
/* Handle some socket options that is put at the generic socket level,
|
|
but really depends on the transport layer */
|
|
switch (optname)
|
|
{
|
|
case IP_SO_ACCEPTCONN:
|
|
if (IPSCTP_IS_LISTEN_INSTANCE(inst))
|
|
val = 1;
|
|
break;
|
|
|
|
case IP_SO_RCVLOWAT:
|
|
val = (int) inst->recv_lowat;
|
|
break;
|
|
|
|
case IP_SO_SNDLOWAT:
|
|
val = (int) inst->send_lowat;
|
|
break;
|
|
|
|
case IP_SO_LINGER:
|
|
{
|
|
struct Ip_linger *l = optval;
|
|
|
|
if (*optlen < sizeof(struct Ip_linger))
|
|
return IPNET_ERRNO(EINVAL);
|
|
|
|
*optlen = sizeof(struct Ip_linger);
|
|
if (IPSCTP_INVALID_LINGER_TIME != inst->linger_time)
|
|
{
|
|
l->l_onoff = 1;
|
|
l->l_linger = inst->linger_time;
|
|
}
|
|
else
|
|
{
|
|
l->l_onoff = 0;
|
|
l->l_linger = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
default:
|
|
/* Let the caller handle this option */
|
|
return IPNET_ERRNO(EOPNOTSUPP);
|
|
}
|
|
|
|
goto set_int_and_return;
|
|
}
|
|
|
|
/* if level is not SCTP, return to upper layer to handle */
|
|
if (IP_IPPROTO_SCTP != level)
|
|
return IPNET_ERRNO(EOPNOTSUPP);
|
|
|
|
/* process optname cases */
|
|
switch (optname)
|
|
{
|
|
case IPSCTP_RTOINFO:
|
|
ret = ipsctp_getsockopt_rtoinfo(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_ASSOCINFO:
|
|
ret = ipsctp_getsockopt_associnfo(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_DEFAULT_SEND_PARAM:
|
|
ret = ipsctp_getsockopt_default_send_param(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_GET_PEER_ADDR_INFO:
|
|
ret = ipsctp_getsockopt_peer_addr_info(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_PRIMARY_ADDR:
|
|
ret = ipsctp_getsockopt_primary_addr(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_PEER_ADDR_PARAMS:
|
|
ret = ipsctp_getsockopt_peer_addr_params(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_STATUS:
|
|
ret = ipsctp_getsockopt_sctp_status(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_CONTEXT:
|
|
ret = ipsctp_getsockopt_context(sock, optval, optlen);
|
|
break;
|
|
|
|
#if 0
|
|
case IPSCTP_AUTH_ACTIVE_KEY:
|
|
ret = ipsctp_getsockopt_active_key(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_PEER_AUTH_CHUNKS:
|
|
ret = ipsctp_getsockopt_peer_auth_chunks(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_LOCAL_AUTH_CHUNKS:
|
|
ret = ipsctp_getsockopt_local_auth_chunks(sock, optval, optlen);
|
|
break;
|
|
#endif
|
|
|
|
case IPSCTP_EVENTS:
|
|
ret = ipsctp_getsockopt_events(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_INITMSG:
|
|
ret = ipsctp_getsockopt_initmsg(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_MAXSEG:
|
|
ret = ipsctp_getsockopt_maxseg(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_DELAYED_SACK:
|
|
ret = ipsctp_getsockopt_delay_ack(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_MAX_BURST:
|
|
ret = ipsctp_getsockopt_max_burst(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_GET_PEER_ADDRS:
|
|
ret = ipsctp_getsockopt_peer_addrs(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_GET_LOCAL_ADDRS:
|
|
ret = ipsctp_getsockopt_local_addrs(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_GET_PEER_ADDRS_SIZE:
|
|
ret = ipsctp_getsockopt_peer_addrs_size(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_GET_LOCAL_ADDRS_SIZE:
|
|
ret = ipsctp_getsockopt_local_addrs_size(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_GET_ADDR_LEN:
|
|
ret = ipsctp_getsockopt_addrs_len(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_GET_PEELOFF:
|
|
ret = ipsctp_getsockopt_peeloff(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_NODELAY:
|
|
if (IP_BIT_ISSET(inst->feature_flags , IPSCTP_INST_FLAGS_NODELAY))
|
|
val = 1;
|
|
else
|
|
val = 0;
|
|
goto set_int_and_return;
|
|
|
|
/* This boolean flag is used to enable or disable explicit end of record
|
|
* (EOR) marking. When this option is enabled, a user may make multiple
|
|
* send system calls to send a record and must indicate that they are
|
|
* finished sending a particular record by including on the send the
|
|
* SCTP_EOR flag. If this boolean flag is disabled then each individual
|
|
* send system call is considered to have a SCTP_EOR indicator set on it
|
|
* implicitly without the user having to explicitly add this flag.
|
|
*/
|
|
case IPSCTP_EXPLICIT_EOR:
|
|
if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_EXPLICIT_EOR))
|
|
val = 1;
|
|
else
|
|
val = 0;
|
|
goto set_int_and_return;
|
|
|
|
/* This option will enable or disable the use of the extended version of
|
|
* the sctp_sndrcvinfo structure. If this option is disabled, then the
|
|
* normal sctp_sndrcvinfo structure is returned in all receive message
|
|
* calls. If this option is enabled then the sctp_extrcvinfo structure
|
|
* is returned in all receive message calls.
|
|
*/
|
|
case IPSCTP_USE_EXT_RCVINFO:
|
|
if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_EXT_RCVINFO))
|
|
val = 1;
|
|
else
|
|
val = 0;
|
|
goto set_int_and_return;
|
|
|
|
/* This socket option is a boolean flag which turns on or off mapped V4
|
|
* addresses. If this option is turned on and the socket is type
|
|
* PF_INET6, then IPv4 addresses will be mapped to V6 representation.
|
|
* If this option is turned off, then no mapping will be done of V4
|
|
* addresses and a user will receive both PF_INET6 and PF_INET type
|
|
* addresses on the socket.
|
|
*/
|
|
case IPSCTP_I_WANT_MAPPED_V4_ADDR:
|
|
if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4))
|
|
val = 1;
|
|
else
|
|
val = 0;
|
|
goto set_int_and_return;
|
|
|
|
/* This option is a on/off flag and is passed an integer where a non-
|
|
* zero is on and a zero is off. If enabled no SCTP message
|
|
* fragmentation will be performed. Instead if a message being sent
|
|
* exceeds the current PMTU size, the message will NOT be sent and
|
|
* instead a error will be indicated to the user.
|
|
*/
|
|
case IPSCTP_DISABLE_FRAGMENTS:
|
|
if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAGS_NO_FRAGMENT))
|
|
val = 1;
|
|
else
|
|
val = 0;
|
|
goto set_int_and_return;
|
|
|
|
/* pls refer to IPSCTP_FRAGMENT_INTERLEAVE's comment */
|
|
case IPSCTP_FRAGMENT_INTERLEAVE:
|
|
if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_FRAG_INTERLEAVE))
|
|
{
|
|
if (IP_BIT_ISSET(inst->feature_flags, IPSCTP_INST_FLAG_INTERLEAVE_STRMS))
|
|
val = IPSCTP_FRAG_LEVEL_2;
|
|
else
|
|
val = IPSCTP_FRAG_LEVEL_1;
|
|
}
|
|
else
|
|
val = IPSCTP_FRAG_LEVEL_0;
|
|
goto set_int_and_return;
|
|
|
|
/* This option will set or get the SCTP partial delivery point. This
|
|
* point is the size of a message where the partial delivery API will be
|
|
* invoked to help free up rwnd space for the peer. Setting this to a
|
|
* lower value will cause partial deliveries to happen more often. The
|
|
* calls argument is an integer that sets or gets the partial delivery
|
|
* point. Note also that the call will fail if the user attempts to set
|
|
* this value larger than the socket receive buffer size.
|
|
* Note that any single message having a length smaller than or equal to
|
|
* the SCTP partial delivery point will be delivered in one single read
|
|
* call as long as the user provided buffer is large enough to hold the
|
|
* message.
|
|
*/
|
|
case IPSCTP_PARTIAL_DELIVERY_POINT:
|
|
val = inst->pd_point;
|
|
goto set_int_and_return;
|
|
|
|
/* This socket option is applicable to the one-to-many style socket
|
|
* only. When set it will cause associations that are idle for more
|
|
* than the specified number of seconds to automatically close using the
|
|
* graceful shutdown procedure. An association being idle is defined as
|
|
* an association that has NOT sent or received user data. The special
|
|
* value of '0' indicates that no automatic close of any associations
|
|
* should be performed, this is the default value. The option expects
|
|
* an integer defining the number of seconds of idle time before an
|
|
* association is closed.
|
|
* An application using this option should enable receiving the
|
|
* association change notification. This is the only mechanism an
|
|
* application is informed about the closing of an association. After
|
|
* an association is closed, the association ID assigned to it can be
|
|
* reused. An application should be aware of this to avoid the possible
|
|
* problem of sending data to an incorrect peer end point.
|
|
*/
|
|
case IPSCTP_AUTOCLOSE:
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_FLAG_AUTOCLOSE))
|
|
val = inst->autoclose_time;
|
|
else
|
|
val = 0;
|
|
goto set_int_and_return;
|
|
|
|
default:
|
|
#if 0
|
|
if (IPSCTP_SOCKOPT_W == ipsctp_sockopt_check_rw(optname))
|
|
{
|
|
ret = 0;
|
|
break;
|
|
}
|
|
#endif
|
|
return IPNET_ERRNO(ENOPROTOOPT);
|
|
}
|
|
|
|
return ret;
|
|
|
|
set_int_and_return:
|
|
|
|
if (*optlen < sizeof(int))
|
|
return IPNET_ERRNO(EINVAL);
|
|
|
|
*(int*) optval = val;
|
|
*optlen = sizeof(int);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_set_peer_addr_params
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* 8.1.13. Peer Address Parameters (SCTP_PEER_ADDR_PARAMS)
|
|
*
|
|
* Applications can enable or disable heartbeats for any peer address of
|
|
* an association, modify an address's heartbeat interval, force a
|
|
* heartbeat to be sent immediately, and adjust the address's maximum
|
|
* number of retransmissions sent before an address is considered
|
|
* unreachable. The following structure is used to access and modify an
|
|
* address's parameters:
|
|
*
|
|
* struct sctp_paddrparams {
|
|
* sctp_assoc_t spp_assoc_id;
|
|
* struct sockaddr_storage spp_address;
|
|
* uint32_t spp_hbinterval;
|
|
* uint16_t spp_pathmaxrxt;
|
|
* uint32_t spp_pathmtu;
|
|
* uint32_t spp_flags;
|
|
* uint32_t spp_ipv6_flowlabel;
|
|
* uint8_t spp_ipv4_tos;
|
|
* };
|
|
*
|
|
* spp_assoc_id - (one-to-many style socket) This is filled in the
|
|
* application, and identifies the association for this query.
|
|
* spp_address - This specifies which address is of interest.
|
|
* spp_hbinterval - This contains the value of the heartbeat interval,
|
|
* in milliseconds. Note that unless the spp_flag is set to
|
|
* SPP_HB_ENABLE the value of this field is ignored. Note also that
|
|
* a value of zero indicates the current setting should be left
|
|
* unchanged. To set an actual value of zero the use of the flag
|
|
* SPP_HB_TIME_IS_ZERO should be used.
|
|
* spp_pathmaxrxt - This contains the maximum number of retransmissions
|
|
* before this address shall be considered unreachable. Note that a
|
|
* value of zero indicates the current setting should be left
|
|
* unchanged.
|
|
* spp_pathmtu - When Path MTU discovery is disabled the value
|
|
* specified here will be the "fixed" path mtu (i.e. the value of the
|
|
* spp_flags field must include the flag SPP_PMTUD_DISABLE for this
|
|
* field to have any effect). Note that if the spp_address field is
|
|
* empty then all destinations for this association will have this
|
|
* fixed path mtu set upon them. If an address is specified, then
|
|
* only that address will be effected. Note also that this option
|
|
* cannot be set on the endpoint, but must be set on each individual
|
|
* association. Also, when disabling PMTU discovery, the
|
|
* implementation may disallow this behavior if the "fixed" path mtu
|
|
* is below the constant value SCTP_SMALLEST_PMTU.
|
|
* spp_ipv6_flowlabel- This field is used in conjunction with the
|
|
* SPP_IPV6_FLOWLABEL flag.
|
|
* spp_ipv4_tos- This field is used in conjunction with the
|
|
* SPP_IPV4_TOS flag.
|
|
* spp_flags- These flags are used to control various features on an
|
|
* association. The flag field is a bit mask which may contain zero
|
|
* or more of the following options:
|
|
* SPP_HB_ENABLE - Enable heartbeats on the specified address. Note
|
|
* that if the address field is empty all addresses for the
|
|
* association have heartbeats enabled upon them.
|
|
* SPP_HB_DISABLE - Disable heartbeats on the specified address.
|
|
* Note that if the address field is empty all addresses for the
|
|
* association will have their heartbeats disabled. Note also
|
|
* that SPP_HB_ENABLE and SPP_HB_DISABLE are mutually exclusive,
|
|
* only one of these two should be specified. Enabling both
|
|
* fields will have undetermined results.
|
|
* SPP_HB_DEMAND - Request a user initiated heartbeat to be made
|
|
* immediately.
|
|
* SPP_HB_TIME_IS_ZERO - Specify's that the time for heartbeat delay
|
|
* is to be set to the value of 0 milliseconds.
|
|
* SPP_PMTUD_ENABLE - This field will enable PMTU discovery upon the
|
|
* specified address. Note that if the address field is empty
|
|
* then all addresses on the association are effected.
|
|
* SPP_PMTUD_DISABLE - This field will disable PMTU discovery upon
|
|
* the specified address. Note that if the address field is empty
|
|
* then all addresses on the association are effected. Not also
|
|
* that SPP_PMTUD_ENABLE and SPP_PMTUD_DISABLE are mutually
|
|
* exclusive. Enabling both will have undetermined results.
|
|
* SPP_IPV6_FLOWLABEL - Setting this flag enables setting of the
|
|
* IPV6 flowlabel value associated with either the association or
|
|
* the specific address. If the address field is filled in, then
|
|
* the specific destination address has this value set upon it.
|
|
* If the association is specified, but not the address, then the
|
|
* flowlabel value is set for any future destination addresses
|
|
* that may be added. The value is obtained in the
|
|
* spp_ipv6_flowlabel field.
|
|
* Upon retrieval, this flag will be set to indicate that the
|
|
* spp_ipv6_flowlabel field has a valid value returned. If a
|
|
* specific destination addresses is set (in the spp_address
|
|
* field) when called then the value returned is that of the
|
|
* address. If just an association is specified (and no address)
|
|
* then the association default flowlabel is returned. If neither
|
|
* an association nor an destination is specified, then the
|
|
* sockets default flowlabel is returned. For non IPv6 sockets,
|
|
* then this flag will be left cleared.
|
|
* SPP_IPV4_TOS - Setting this flag enables setting of the IPV4 tos
|
|
* value associated with either the association or specific
|
|
* address. If the address field is filled in, then the specific
|
|
* destination address has this value set upon it. If the
|
|
* association is specified, but not the address, then the tos
|
|
* value is set for any future destination addresses that may be
|
|
* added. The value is obtained in the spp_ipv4_tos field.
|
|
*
|
|
* Upon retrieval, this flag will be set to indicate that the
|
|
* spp_ipv4_tos field has a valid value returned. If a specific
|
|
* destination addresses is set when called (in the spp_address
|
|
* field) then that specific destination addresses tos value is
|
|
* returned. If just an association is specified then the
|
|
* association default tos is returned. If neither an association
|
|
* nor an destination is specified, then the sockets default tos
|
|
* is returned. For non IPv4 sockets, then this flag will be left
|
|
* cleared.
|
|
*
|
|
* To read or modify these parameters, the application should call
|
|
* sctp_opt_info() with the SCTP_PEER_ADDR_PARAMS option.
|
|
* Parameters: sock - the set socket descriptor.
|
|
* params - the set parameters pointer.
|
|
* tcb - which TCB need be setted.
|
|
* tp - which transport need be setted.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_apply_peer_addr_params(Ipsctp_socket *sock,
|
|
struct Ipsctp_paddrparams *params,
|
|
Ipsctp_tcb *tcb,
|
|
Ipsctp_transport *tp)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
int ret = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == params) ||
|
|
(IP_NULL == (inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* apply for the demand heatbeat interval, make sure. */
|
|
if (params->spp_flags & IPSPP_HB_DEMAND && tp && tcb)
|
|
{
|
|
ret = ipsctp_send_heartbeat(tcb, tp);
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : send heartbeat %d."IP_LF, ret);
|
|
}
|
|
|
|
if (params->spp_flags & IPSPP_HB_ENABLE)
|
|
{
|
|
if (params->spp_flags & IPSPP_HB_TIME_IS_ZERO)
|
|
params->spp_hbinterval = 0;
|
|
|
|
if ((params->spp_flags & IPSPP_HB_TIME_IS_ZERO) ||
|
|
(params->spp_hbinterval))
|
|
{
|
|
if (tp)
|
|
{
|
|
tp->hbinterval = params->spp_hbinterval;
|
|
tp->state &= ~IPSCTP_ADDR_NOHB;
|
|
}
|
|
else if (tcb) /* TODO : set each TP? */
|
|
tcb->hb_interval = params->spp_hbinterval;
|
|
else
|
|
inst->paddrparam.spp_hbinterval = params->spp_hbinterval;
|
|
}
|
|
}
|
|
|
|
if ((params->spp_flags & IPSPP_HB_DISABLE) && tp)
|
|
tp->state &= ~IPSCTP_ADDR_NOHB;
|
|
|
|
#if 0
|
|
if ((params->spp_flags & IPSPP_PMTUD_DISABLE) && params->spp_pathmtu)
|
|
{
|
|
if (tp)
|
|
tp->pmtu = params->spp_pathmtu;
|
|
/* TODO(td): apply the path MTU value */
|
|
else if (tcb)
|
|
{
|
|
IPSCTP_DATA_TCBLOCK(tcb->instance->tcb_lock);
|
|
tcb->pmtu = params->spp_pathmtu;
|
|
/* TODO(td): apply the path MTU value */
|
|
IPSCTP_DATA_TCBUNLOCK(tcb->instance->tcb_lock);
|
|
}
|
|
else
|
|
inst->paddrparam.spp_pathmtu = params->spp_pathmtu;
|
|
}
|
|
|
|
if ((params->spp_flags & IPSPP_PMTUD_ENABLE) && params->spp_pathmaxrxt)
|
|
{
|
|
ret = 0;
|
|
}
|
|
#endif
|
|
if (params->spp_pathmaxrxt)
|
|
{
|
|
/* TODO : value validate; set each TP? */
|
|
if (tp)
|
|
{
|
|
tp->err_thresh = params->spp_pathmaxrxt;
|
|
}
|
|
else
|
|
{
|
|
if (tcb)
|
|
tcb->max_transport_retrans = params->spp_pathmaxrxt;
|
|
else
|
|
inst->paddrparam.spp_pathmaxrxt = params->spp_pathmaxrxt;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_rtoinfo
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_rtoinfo(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
struct Ipsctp_rtoinfo *rto_info = (struct Ipsctp_rtoinfo *)optval;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ip_u32 new_init, new_min, new_max;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval)
|
|
|| (IP_NULL == (inst = sock->sctp_inst))
|
|
|| (sizeof(struct Ipsctp_rtoinfo) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* find tcb with sctp_rtoinfo.srto_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(rto_info->srto_assoc_id);
|
|
if (!tcb && rto_info->srto_assoc_id && ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* set the values to instance or tcb */
|
|
new_init = new_min = new_max = 0;
|
|
if (tcb)
|
|
{
|
|
if (rto_info->srto_initial)
|
|
new_init = rto_info->srto_initial;
|
|
else
|
|
new_init = tcb->initial_rto;
|
|
|
|
if (rto_info->srto_max)
|
|
new_max = rto_info->srto_max;
|
|
else
|
|
new_max = tcb->max_rto;
|
|
|
|
if (rto_info->srto_min)
|
|
new_min = rto_info->srto_min;
|
|
else
|
|
new_min = tcb->min_rto;
|
|
|
|
if ((new_min > new_init) || (new_init > new_max))
|
|
goto print_err_info;
|
|
|
|
tcb->initial_rto = new_init;
|
|
tcb->max_rto = new_max;
|
|
tcb->min_rto = new_min;
|
|
}
|
|
else
|
|
{
|
|
if (rto_info->srto_initial)
|
|
new_init = rto_info->srto_initial;
|
|
else
|
|
new_init = inst->rtoinfo.srto_initial;
|
|
|
|
if (rto_info->srto_max)
|
|
new_max = rto_info->srto_max;
|
|
else
|
|
new_max = inst->rtoinfo.srto_max;
|
|
|
|
if (rto_info->srto_min)
|
|
new_min = rto_info->srto_min;
|
|
else
|
|
new_min = inst->rtoinfo.srto_min;
|
|
|
|
if ((new_min > new_init) || (new_init > new_max))
|
|
goto print_err_info;
|
|
|
|
inst->rtoinfo.srto_initial = new_init;
|
|
inst->rtoinfo.srto_max = new_max;
|
|
inst->rtoinfo.srto_min = new_min;
|
|
}
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket route information option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
|
|
print_err_info:
|
|
/* here, print error infor */
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket route information option ERROR."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_associnfo
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_associnfo(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
struct Ipsctp_assocparams *params = (struct Ipsctp_assocparams *)optval;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_assocparams) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check the validity of the setting value */
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_rtoinfo.srto_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(params->sasoc_assoc_id);
|
|
if (!tcb && params->sasoc_assoc_id && ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* set the values to instance or tcb */
|
|
if (tcb)
|
|
{
|
|
if (params->sasoc_asocmaxrxt)
|
|
tcb->max_send_times = params->sasoc_asocmaxrxt;
|
|
|
|
if (params->sasoc_cookie_life)
|
|
tcb->cookie_life = params->sasoc_cookie_life;
|
|
|
|
params->sasoc_number_peer_destinations = (Ip_u16)ipsctp_list_size(&tcb->transport_list);
|
|
}
|
|
else
|
|
{
|
|
if (params->sasoc_asocmaxrxt)
|
|
inst->assocparams.sasoc_asocmaxrxt = params->sasoc_asocmaxrxt;
|
|
|
|
if (params->sasoc_cookie_life)
|
|
inst->assocparams.sasoc_cookie_life = params->sasoc_cookie_life;
|
|
}
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket association option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_default_send_param
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_default_send_param(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
struct Ipsctp_sndrcvinfo *sr_info = (struct Ipsctp_sndrcvinfo *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval)
|
|
|| (sizeof(struct Ipsctp_sndrcvinfo) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_sndrcvinfo.sinfo_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(sr_info->sinfo_assoc_id);
|
|
if (!tcb && sr_info->sinfo_assoc_id && ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* set the values to instance or tcb */
|
|
if (tcb)
|
|
IPSCTP_SNDRCV_CPY(&tcb->def_send, sr_info);
|
|
else
|
|
IPSCTP_SNDRCV_CPY(&inst->sndrcvinfo, sr_info);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket default address param option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_primary_addr
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_primary_addr(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
struct Ipsctp_setprim *prim_info = (struct Ipsctp_setprim *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
int ret = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval)
|
|
|| (sizeof(struct Ipsctp_setprim) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_setprim.ssp_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(prim_info->ssp_assoc_id);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* get the transport by the special address */
|
|
if (ipsctp_is_addr_valid((Ipsctp_addr*)&prim_info->ssp_addr))
|
|
tp = ipsctp_transport_lookup(tcb,
|
|
(IP_CONST Ipsctp_addr *)&prim_info->ssp_addr);
|
|
|
|
|
|
/* set the primary destination */
|
|
ret = ipsctp_set_primary_addr(tcb,
|
|
(Ipsctp_addr*)&prim_info->ssp_addr,
|
|
tp,
|
|
IP_TRUE,
|
|
IP_FALSE
|
|
);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket primary address option OK."IP_LF);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_peer_addr_params
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_peer_addr_params(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
struct Ipsctp_paddrparams *addr_parms = (struct Ipsctp_paddrparams *)optval;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
int ret = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_paddrparams) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_paddrparams.spp_assoc_id, tcb can be NULL */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(addr_parms->spp_assoc_id);
|
|
if ((IP_NULL == tcb)
|
|
&& (addr_parms->spp_assoc_id))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* if addr_parms.spp_address is valid, then
|
|
* find the transport with it.
|
|
*/
|
|
if ((IP_NULL != tcb) && (ipsctp_is_addr_valid((Ipsctp_addr*)&addr_parms->spp_address)))
|
|
{
|
|
tp = ipsctp_transport_lookup(tcb,
|
|
(IP_CONST Ipsctp_addr *)&addr_parms->spp_address);
|
|
}
|
|
|
|
if ((addr_parms->spp_flags & IPSPP_HB_DEMAND) && (!tp || !tcb))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : NULL pointer for IPSPP_HB_DEMAND."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
ret = ipsctp_apply_peer_addr_params(sock,
|
|
addr_parms,
|
|
tcb,
|
|
tp);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket peer address params option OK."IP_LF);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_context
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_context(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_assoc_value) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* set the values to instance or tcb */
|
|
if (as_val->assoc_id)
|
|
{
|
|
/* find tcb with sctp_assoc_value.assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
tcb->context = as_val->assoc_value;
|
|
}
|
|
else
|
|
inst->context = as_val->assoc_value;
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket context option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_active_key
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_active_key(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
struct Ipsctp_authkeyid *keyid = (struct Ipsctp_authkeyid *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_authkeyid) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* find tcb with sctp_authkeyid.scact_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(keyid->scact_assoc_id);
|
|
if (!tcb && keyid->scact_assoc_id && ipsctp_style(sock, UDP))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* set the values to instance or tcb */
|
|
#if 0
|
|
if (tcb)
|
|
tcb-> = keyid->scact_keynumber;
|
|
else
|
|
inst-> = keyid->scact_keynumber;
|
|
#endif
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket active key option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_events
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_events(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
#define IPSCTP_EVET_SET(evt_name, evt_flag) \
|
|
do { \
|
|
if (evt_sub->Ipsctp_ ## evt_name) \
|
|
IP_BIT_SET(inst->feature_flags , evt_flag); \
|
|
else \
|
|
IP_BIT_CLR(inst->feature_flags , evt_flag); \
|
|
} while(0)
|
|
|
|
struct Ipsctp_event_subscribe *evt_sub = (struct Ipsctp_event_subscribe *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == evt_sub) ||
|
|
(sizeof(struct Ipsctp_event_subscribe) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument.");
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
inst = sock->sctp_inst;
|
|
if (IP_NULL == inst)
|
|
return IPNET_ERRNO(EINVAL);
|
|
|
|
/* set the bit value to instance or tcb */
|
|
IPSCTP_EVET_SET(data_io_event, IPSCTP_EVNT_FLAGS_DATA_IO);
|
|
IPSCTP_EVET_SET(association_event, IPSCTP_EVNT_FLAGS_ASSOCIATION);
|
|
IPSCTP_EVET_SET(address_event, IPSCTP_EVNT_FLAGS_ADDRESS);
|
|
IPSCTP_EVET_SET(send_failure_event, IPSCTP_EVNT_FLAGS_SEND_FAILURE);
|
|
IPSCTP_EVET_SET(peer_error_event, IPSCTP_EVNT_FLAGS_PEER_ERROR);
|
|
IPSCTP_EVET_SET(shutdown_event, IPSCTP_EVNT_FLAGS_SHUTDOWN);
|
|
IPSCTP_EVET_SET(partial_delivery_event, IPSCTP_EVNT_FLAGS_PARTIAL_DELIVERY);
|
|
IPSCTP_EVET_SET(adaptation_layer_event, IPSCTP_EVNT_FLAGS_ADAPTATION_LAYER);
|
|
IPSCTP_EVET_SET(authentication_event, IPSCTP_EVNT_FLAGS_AUTHENTICATION);
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket events option OK."IP_LF);
|
|
#endif
|
|
#undef IPSCTP_EVET_SET
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_initmsg
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_initmsg(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
struct Ipsctp_initmsg *init_msg = (struct Ipsctp_initmsg *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_initmsg) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument.");
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* set the init message from instance */
|
|
if (init_msg->sinit_num_ostreams)
|
|
{
|
|
if (init_msg->sinit_num_ostreams > IPSCTP_MAX_STREAMS)
|
|
inst->initmsg.sinit_num_ostreams = IPSCTP_MAX_STREAMS;
|
|
else
|
|
inst->initmsg.sinit_num_ostreams = init_msg->sinit_num_ostreams;
|
|
}
|
|
|
|
if (init_msg->sinit_max_instreams)
|
|
{
|
|
if (init_msg->sinit_max_instreams > IPSCTP_MAX_STREAMS)
|
|
inst->initmsg.sinit_max_instreams = IPSCTP_MAX_STREAMS;
|
|
else
|
|
inst->initmsg.sinit_max_instreams = init_msg->sinit_max_instreams;
|
|
}
|
|
|
|
if (init_msg->sinit_max_attempts)
|
|
inst->initmsg.sinit_max_attempts = init_msg->sinit_max_attempts;
|
|
|
|
if (init_msg->sinit_max_init_timeo)
|
|
inst->initmsg.sinit_max_init_timeo = init_msg->sinit_max_init_timeo;
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set init message option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_maxseg
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_maxseg(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ip_u32 hdr_len = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_assoc_value) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4))
|
|
{
|
|
hdr_len = sizeof(Ipsctp_data_chunk_hdr)
|
|
+ sizeof(Ipsctp_sctp_hdr)
|
|
+ sizeof(Ipnet_pkt_ip);
|
|
}
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6))
|
|
{
|
|
hdr_len = sizeof(Ipsctp_data_chunk_hdr)
|
|
+ sizeof(Ipsctp_sctp_hdr)
|
|
+ sizeof(Ipnet_pkt_ip6);
|
|
}
|
|
#endif
|
|
|
|
/* find tcb with sctp_assoc_value.assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id);
|
|
/* set the values to instance or tcb */
|
|
if (IP_NULL != tcb)
|
|
{
|
|
if (as_val->assoc_value)
|
|
tcb->frag_point = as_val->assoc_value + hdr_len;
|
|
else
|
|
tcb->frag_point = 0;
|
|
|
|
if (tcb->frag_point != 0 && ((tcb->frag_point & ~0x3) < tcb->mss))
|
|
{
|
|
tcb->mss = tcb->frag_point & ~0x3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (as_val->assoc_value)
|
|
inst->frag_point = as_val->assoc_value + hdr_len;
|
|
else
|
|
inst->frag_point = 0;
|
|
}
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket max segment option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_delay_ack
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_delay_ack(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
struct Ipsctp_sack_info *ssi = (struct Ipsctp_sack_info *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_sack_info) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
if (ssi->sack_delay && ssi->sack_delay > IPSCTP_MAX_SACK_DELAY)
|
|
ssi->sack_delay = IPSCTP_MAX_SACK_DELAY;
|
|
|
|
/* find tcb with Ipsctp_sack_info.sack_assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(ssi->sack_assoc_id);
|
|
/* set the values to instance or tcb */
|
|
if (IP_NULL != tcb)
|
|
{
|
|
if (ssi->sack_delay)
|
|
tcb->delayed_ack = ssi->sack_delay;
|
|
|
|
if (ssi->sack_freq)
|
|
tcb->sack_freq = ssi->sack_freq;
|
|
}
|
|
else
|
|
{
|
|
if (ssi->sack_delay)
|
|
inst->sack_info.sack_delay = ssi->sack_delay;
|
|
|
|
if (ssi->sack_freq)
|
|
inst->sack_info.sack_freq = ssi->sack_freq;
|
|
}
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket delay ack option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt_max_burst
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_setsockopt_max_burst(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
struct Ipsctp_assoc_value *as_val = (struct Ipsctp_assoc_value *)optval;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval) ||
|
|
(sizeof(struct Ipsctp_assoc_value) != optlen))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* set the values to instance or tcb */
|
|
if (as_val->assoc_id)
|
|
{
|
|
/* find tcb with sctp_assoc_value.assoc_id */
|
|
IPSCTP_SOCK_GETCB_BY_ASID(as_val->assoc_id);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No TCB match."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
tcb->max_burst = as_val->assoc_value;
|
|
}
|
|
else
|
|
inst->max_burst = as_val->assoc_value;
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket context option OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_bindx_add
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_sock_bindx_add(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
struct Ipsctp_getaddrs *gaddrs = (struct Ipsctp_getaddrs *)optval;
|
|
Ipsctp_socket *ret_sock = IP_NULL;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
struct Ip_sockaddr *name = IP_NULL;
|
|
Ip_u32 gaddrs_len = 0;
|
|
Ip_u16 *port = IP_NULL;
|
|
void *local_addr = IP_NULL;
|
|
int ret = 0;
|
|
#if defined(IPCOM_USE_INET6) && defined(IPCOM_USE_INET)
|
|
struct Ip_sockaddr_in sin;
|
|
#endif
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval))
|
|
goto inval_out;
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* if already connected(include accept, not include listen),
|
|
* just return. do not support dynamic */
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_CONNECTED))
|
|
return IPNET_ERRNO(EOPNOTSUPP);
|
|
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
goto inval_out;
|
|
|
|
name = gaddrs->sget_addrs;
|
|
switch (name->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
if (IPCOM_SA_LEN_GET(name) != sizeof(struct Ip_sockaddr_in))
|
|
goto inval_out;
|
|
gaddrs_len = sizeof(struct Ipsctp_getaddrs) - sizeof(struct Ip_sockaddr) + sizeof(struct Ip_sockaddr_in);
|
|
if (IPSCTP_IS_V6ONLY(inst))
|
|
goto inval_out;
|
|
local_addr = (void *)&((struct Ip_sockaddr_in *)name)->sin_addr;
|
|
port = &((struct Ip_sockaddr_in *)name)->sin_port;
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
if (IPCOM_SA_LEN_GET(name) != sizeof(struct Ip_sockaddr_in6))
|
|
goto inval_out;
|
|
gaddrs_len = sizeof(struct Ipsctp_getaddrs) - sizeof(struct Ip_sockaddr) + sizeof(struct Ip_sockaddr_in6);
|
|
local_addr = (void *)&((struct Ip_sockaddr_in6 *)name)->sin6_addr;
|
|
port = &((struct Ip_sockaddr_in6 *)name)->sin6_port;
|
|
/* change IPV4 maped address to IPV4 address */
|
|
if (IP_IN6_IS_ADDR_V4MAPPED(&((struct Ip_sockaddr_in6 *)name)->sin6_addr))
|
|
{
|
|
if (IPSCTP_IS_V6ONLY(inst))
|
|
goto inval_out;
|
|
#ifdef IPCOM_USE_INET
|
|
{
|
|
struct Ip_sockaddr_in6 *sin6 = (struct Ip_sockaddr_in6 *)name;
|
|
IPCOM_SA_LEN_SET(&sin, sizeof(struct Ip_sockaddr_in));
|
|
sin.sin_family = IP_AF_INET;
|
|
sin.sin_port = sin6->sin6_port;
|
|
sin.sin_addr.s_addr = sin6->sin6_addr.in6.addr32[3];
|
|
name = (struct Ip_sockaddr *)&sin;
|
|
local_addr = (void *)&sin.sin_addr.s_addr;
|
|
port = &sin.sin_port;
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
#endif
|
|
/* invalid address family specified */
|
|
default:
|
|
goto inval_out;
|
|
}
|
|
if (gaddrs_len > optlen)
|
|
goto inval_out;
|
|
|
|
/* if the first bind, do user socket bind */
|
|
if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_BOUND))
|
|
{
|
|
Ipnet_sock_ops *ops = ipnet_sock_get_ops(sock);
|
|
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : first bind for bindx."IP_LF);
|
|
return ops->bind(sock, name, IPCOM_SA_LEN_GET(name), IP_TRUE);
|
|
}
|
|
/* else add to local address list */
|
|
|
|
if (gaddrs->sget_assoc_id)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : sget_assoc_id is not zero."IP_LF);
|
|
return 0;
|
|
}
|
|
|
|
/* check the bind-port if not the first time */
|
|
if (*port && ip_ntohs(*port) != inst->lport)
|
|
goto inval_out;
|
|
|
|
/* update local port number, if user does not specify,
|
|
* used in ipsctp_add_laddr */
|
|
if (0 == *port)
|
|
*port = ip_htons(inst->lport);
|
|
|
|
ret_sock = ipsctp_sock_lookup_sa(sock->vr_index,
|
|
name->sa_family,
|
|
local_addr,
|
|
ip_ntohs(*port));
|
|
if (ret_sock && ret_sock->sctp_inst == sock->sctp_inst)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : already set the same address in same socket."IP_LF);
|
|
return 0;
|
|
}
|
|
if (IP_NULL == ret_sock)
|
|
{
|
|
enum Ipnet_addr_type addr_type = IPNET_ADDR_TYPE_NOT_LOCAL;
|
|
/* check if this address on the curent system */
|
|
switch (name->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
addr_type = ipnet_ip4_get_addr_type(*(Ip_u32 *)local_addr, sock->vr_index, IP_NULL);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
addr_type = ipnet_ip6_get_addr_type((IP_CONST struct Ip_in6_addr *)local_addr, sock->vr_index, IP_NULL);
|
|
break;
|
|
#endif
|
|
/* invalid address family specified */
|
|
default:
|
|
goto inval_out;
|
|
}
|
|
if (IPNET_ADDR_TYPE_NOT_LOCAL == addr_type)
|
|
return IPNET_ERRNO(EADDRNOTAVAIL);
|
|
|
|
ret = ipsctp_add_laddr(inst,
|
|
(Ipsctp_addr *)name,
|
|
IPSCTP_LADDR_NORMAL);
|
|
if (0 != ret)
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : already set the same address."IP_LF);
|
|
return IPNET_ERRNO(EADDRINUSE);
|
|
}
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Bindx add OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
|
|
inval_out:
|
|
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_bindx_del
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_sock_bindx_del(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
struct Ipsctp_getaddrs *gaddrs = (struct Ipsctp_getaddrs *)optval;
|
|
Ipsctp_laddr_entry *laddrs_cur = IP_NULL;
|
|
Ipsctp_laddr_entry *laddrs_tmp = IP_NULL;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
struct Ip_sockaddr *name = IP_NULL;
|
|
Ip_u32 gaddrs_len = 0;
|
|
Ip_u16 *port = IP_NULL;
|
|
void *local_addr = IP_NULL;
|
|
#if defined(IPCOM_USE_INET6) && defined(IPCOM_USE_INET)
|
|
struct Ip_sockaddr_in sin;
|
|
#endif
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval))
|
|
goto inval_out;
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* if already connected(include accept, not include listen),
|
|
* just return. do not support dynamic */
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_CONNECTED))
|
|
return IPNET_ERRNO(EOPNOTSUPP);
|
|
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
goto inval_out;
|
|
|
|
name = gaddrs->sget_addrs;
|
|
switch (name->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
if (IPCOM_SA_LEN_GET(name) != sizeof(struct Ip_sockaddr_in))
|
|
goto inval_out;
|
|
gaddrs_len = sizeof(struct Ipsctp_getaddrs) - sizeof(struct Ip_sockaddr) + sizeof(struct Ip_sockaddr_in);
|
|
if (IPSCTP_IS_V6ONLY(inst))
|
|
goto inval_out;
|
|
local_addr = (void *)&((struct Ip_sockaddr_in *)name)->sin_addr;
|
|
port = &((struct Ip_sockaddr_in *)name)->sin_port;
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
if (IPCOM_SA_LEN_GET(name) != sizeof(struct Ip_sockaddr_in6))
|
|
goto inval_out;
|
|
gaddrs_len = sizeof(struct Ipsctp_getaddrs) - sizeof(struct Ip_sockaddr) + sizeof(struct Ip_sockaddr_in6);
|
|
local_addr = (void *)&((struct Ip_sockaddr_in6 *)name)->sin6_addr;
|
|
port = &((struct Ip_sockaddr_in6 *)name)->sin6_port;
|
|
/* change IPV4 maped address to IPV4 address */
|
|
if (IP_IN6_IS_ADDR_V4MAPPED(&((struct Ip_sockaddr_in6 *)name)->sin6_addr))
|
|
{
|
|
if (IPSCTP_IS_V6ONLY(inst))
|
|
goto inval_out;
|
|
#ifdef IPCOM_USE_INET
|
|
{
|
|
struct Ip_sockaddr_in6 *sin6 = (struct Ip_sockaddr_in6 *)name;
|
|
IPCOM_SA_LEN_SET(&sin, sizeof(struct Ip_sockaddr_in));
|
|
sin.sin_family = IP_AF_INET;
|
|
sin.sin_port = sin6->sin6_port;
|
|
sin.sin_addr.s_addr = sin6->sin6_addr.in6.addr32[3];
|
|
name = (struct Ip_sockaddr *)&sin;
|
|
local_addr = (void *)&sin.sin_addr.s_addr;
|
|
port = &sin.sin_port;
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
#endif
|
|
/* invalid address family specified */
|
|
default:
|
|
goto inval_out;
|
|
}
|
|
if (gaddrs_len > optlen)
|
|
goto inval_out;
|
|
|
|
if (gaddrs->sget_assoc_id)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : sget_assoc_id is not zero."IP_LF);
|
|
return 0;
|
|
}
|
|
|
|
/* check the bind-port if not the first time */
|
|
if (*port && ip_ntohs(*port) != inst->lport)
|
|
goto inval_out;
|
|
|
|
/* update local port number, if user does not specify,
|
|
* used in ipsctp_add_laddr */
|
|
if (0 == *port)
|
|
*port = ip_htons(inst->lport);
|
|
|
|
if (ipsctp_list_size(&inst->laddr_list) < 2)
|
|
goto inval_out;
|
|
|
|
/* search in this association local bound address list */
|
|
ipsctp_list_for_each_safe(&inst->laddr_list, laddrs_cur, laddrs_tmp, Ipsctp_laddr_entry)
|
|
{
|
|
if (laddrs_cur->laddr.sa.sa_family != name->sa_family)
|
|
continue;
|
|
|
|
switch (name->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
if (*(Ip_in_addr_t *)local_addr == laddrs_cur->laddr.sin.sin_addr.s_addr)
|
|
{
|
|
ipcom_list_remove((Ipcom_list *)laddrs_cur);
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
ipcom_shm_free(laddrs_cur);
|
|
#else
|
|
ipcom_free(laddrs_cur);
|
|
#endif
|
|
continue;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
if (IP_TRUE == IP_IN6_ARE_ADDR_EQUAL(local_addr, &laddrs_cur->laddr.sin6.sin6_addr))
|
|
{
|
|
ipcom_list_remove((Ipcom_list *)laddrs_cur);
|
|
#ifdef IPCOM_FORWARDER_NAE
|
|
ipcom_shm_free(laddrs_cur);
|
|
#else
|
|
ipcom_free(laddrs_cur);
|
|
#endif
|
|
continue;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
continue;
|
|
}
|
|
}
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Bindx delete OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
|
|
inval_out:
|
|
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_connectx_find
|
|
*===========================================================================
|
|
* Description: Find the matched TCB with the peer address.
|
|
* Parameters: addrs - the connected peer addresses.
|
|
* addrcnt - how many connected peer addresses in 'addrs'.
|
|
* max_addrs_len - the max memony length of the peer address.
|
|
* cnt_v4 - the number of V4 address in 'addrs'.
|
|
* cnt_v6 - the number of V6 address in 'addrs'.
|
|
* cnt_err - the number of error address 'addrs'.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC Ipsctp_tcb *
|
|
ipsctp_sock_connectx_find(Ipsctp_socket *sock,
|
|
struct Ip_sockaddr *addrs,
|
|
Ip_u32 addrcnt,
|
|
Ip_u32 max_addrs_len,
|
|
Ip_u32 *cnt_v4,
|
|
Ip_u32 *cnt_v6,
|
|
Ip_u32 *cnt_err)
|
|
{
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
struct Ip_sockaddr *sa = IP_NULL;
|
|
Ip_u32 i = 0;
|
|
Ip_u32 sa_len = 0;
|
|
Ip_u32 addrs_len = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == addrs) || (IP_NULL == cnt_v4)
|
|
|| (IP_NULL == cnt_v6) || (IP_NULL == cnt_err) ||
|
|
(0 == addrcnt) || (0 == max_addrs_len))
|
|
{
|
|
IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : cnt/max = %u/%u."IP_LF, addrcnt, max_addrs_len);
|
|
goto cnt_err_out;
|
|
}
|
|
|
|
*cnt_v4 = 0;
|
|
*cnt_v6 = 0;
|
|
*cnt_err = 0;
|
|
sa = addrs;
|
|
addrs_len = 0;
|
|
sa_len = 0;
|
|
for (i = 0; i < addrcnt; i++)
|
|
{
|
|
switch (sa->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
sa_len = sizeof(struct Ip_sockaddr_in);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
#ifdef IPCOM_USE_INET
|
|
if (IP_AF_INET == sock->ipcom.domain)
|
|
goto cnt_err_out;
|
|
#endif
|
|
/* Must not IPV4 maped address */
|
|
if (IP_IN6_IS_ADDR_V4MAPPED(&((struct Ip_sockaddr_in6 *)sa)->sin6_addr))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : IPV4 address."IP_LF);
|
|
goto cnt_err_out;
|
|
}
|
|
sa_len = sizeof(struct Ip_sockaddr_in6);
|
|
break;
|
|
#endif
|
|
/* invalid address family specified */
|
|
default:
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid af."IP_LF);
|
|
goto cnt_err_out;
|
|
}
|
|
|
|
if (IPCOM_SA_LEN_GET(sa) != sa_len)
|
|
{
|
|
IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : get/len = %u/%u."IP_LF, IPCOM_SA_LEN_GET(sa), sa_len);
|
|
goto cnt_err_out;
|
|
}
|
|
|
|
addrs_len += sa_len;
|
|
if (addrs_len > max_addrs_len)
|
|
{
|
|
IPSCTP_DBG2(IPSCTP_DBG_SOCK, "IPSCTP : len/max = %u/%u."IP_LF, addrs_len, max_addrs_len);
|
|
goto cnt_err_out;
|
|
}
|
|
|
|
tcb = ipsctp_sock_lookup_da(sock, sa);
|
|
if (IP_NULL != tcb)
|
|
return tcb;
|
|
sa = (struct Ip_sockaddr *)((Ip_caddr_t)sa + sa_len);
|
|
}
|
|
|
|
return IP_NULL;
|
|
|
|
cnt_err_out:
|
|
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
if (IP_NULL != cnt_err)
|
|
*cnt_err = 1;
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_connectx_add
|
|
*===========================================================================
|
|
* Description: Add the connected peer address to TCB transport.
|
|
* Parameters: tcb - The SCTP association control block.
|
|
* addrs - the connected peer addresses.
|
|
* addrcnt - how many connected peer addresses in 'addrs'.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_sock_connectx_add(Ipsctp_tcb *tcb,
|
|
struct Ip_sockaddr *addrs,
|
|
int addrcnt)
|
|
{
|
|
struct Ip_sockaddr *sa = IP_NULL;
|
|
Ip_u32 i = 0;
|
|
Ip_u32 sa_len = 0;
|
|
|
|
if (0 == addrcnt)
|
|
return 0;
|
|
|
|
if ((IP_NULL == addrs) || (IP_NULL == tcb) || (addrcnt < 0))
|
|
goto inval_out;
|
|
|
|
sa = addrs;
|
|
sa_len = 0;
|
|
for (i = 0; i < addrcnt; i++)
|
|
{
|
|
switch (sa->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
sa_len = sizeof(struct Ip_sockaddr_in);
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
sa_len = sizeof(struct Ip_sockaddr_in6);
|
|
break;
|
|
#endif
|
|
/* invalid address family specified */
|
|
default:
|
|
goto inval_out;
|
|
}
|
|
if (IPCOM_SA_LEN_GET(sa) != sa_len)
|
|
goto inval_out;
|
|
|
|
if (0 != ipsctp_add_remote_addr(tcb,
|
|
(Ipsctp_addr *)sa,
|
|
IPSCTP_ADDR_IS_CONFIRMED,
|
|
IP_NULL))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : add remote address error."IP_LF);
|
|
return IPNET_ERRNO(ENOBUFS);
|
|
}
|
|
sa = (struct Ip_sockaddr *)((Ip_caddr_t)sa + sa_len);
|
|
}
|
|
|
|
return 0;
|
|
|
|
inval_out:
|
|
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_connectx
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* is_delay - the INIT packet send delay flag.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_sock_connectx(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen,
|
|
Ip_bool is_delay)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ipsctp_tcb *new_tcb = IP_NULL;
|
|
Ip_u32 addrs_cnt = 0;
|
|
struct Ip_sockaddr *second_sa = IP_NULL;
|
|
struct Ip_sockaddr *sa = IP_NULL;
|
|
Ip_u32 v4_cnt = 0;
|
|
Ip_u32 v6_cnt = 0;
|
|
Ip_u32 err_cnt = 0;
|
|
int ret = 0;
|
|
Ipsctp_addr bind_any;
|
|
#ifdef IPCOM_USE_INET6
|
|
struct Ip_sockaddr_in6 *in6_to = IP_NULL;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET
|
|
struct Ip_sockaddr_in *in_to = IP_NULL;
|
|
#endif
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval))
|
|
goto inval_out;
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
/* check the socket flags(for listening state) */
|
|
if (ipsctp_sock_state(sock, LISTENING) && ipsctp_style(sock, TCP))
|
|
return IPNET_ERRNO(EISCONN);
|
|
|
|
/* it's a listen socket, if passive open just return. */
|
|
if (IP_NULL != inst->parent)
|
|
return 0;
|
|
|
|
if (ipsctp_style(sock, TCP)
|
|
&& (IP_BIT_ISSET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTED))))
|
|
return IPNET_ERRNO(EISCONN);
|
|
|
|
addrs_cnt = *((Ip_u32 *)optval);
|
|
sa = (struct Ip_sockaddr *)((Ip_u32 *)optval + 1);
|
|
|
|
/* check if TCB exist for connectx addresses */
|
|
tcb = ipsctp_sock_connectx_find(sock,
|
|
sa,
|
|
addrs_cnt,
|
|
(Ip_u32)(optlen - sizeof(Ip_u32)),
|
|
&v4_cnt,
|
|
&v6_cnt,
|
|
&err_cnt);
|
|
if ((IP_NULL != tcb) || (0 < err_cnt))
|
|
{
|
|
if (0 == err_cnt)
|
|
return IPNET_ERRNO(EALREADY);
|
|
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Not NULL."IP_LF);
|
|
goto inval_out;
|
|
}
|
|
|
|
/* more check for IPV6 socket */
|
|
#ifdef IPCOM_USE_INET6
|
|
if (IP_BIT_ISFALSE(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6)
|
|
&& (v6_cnt > 0))
|
|
{
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : v6_cnt = %u."IP_LF, v6_cnt);
|
|
goto inval_out;
|
|
}
|
|
if (IPSCTP_IS_V6ONLY(inst) && (v4_cnt > 0))
|
|
{
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : v4_cnt = %u."IP_LF, v4_cnt);
|
|
goto inval_out;
|
|
}
|
|
#endif
|
|
|
|
/* do bindall operation as default */
|
|
if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_BOUND))
|
|
{
|
|
Ipnet_sock_ops *ops = ipnet_sock_get_ops(sock);
|
|
|
|
ipcom_memset((void *)&bind_any, 0, sizeof(bind_any));
|
|
switch (sock->ipcom.domain)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
bind_any.sin.sin_family = IP_AF_INET;
|
|
bind_any.sin.sin_addr.s_addr = ip_htonl(IP_INADDR_ANY);
|
|
IPCOM_SA_LEN_SET(&bind_any.sa, sizeof(struct Ip_sockaddr_in));
|
|
break;
|
|
#endif /* IPCOM_USE_INET */
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
bind_any.sin6.sin6_family = IP_AF_INET6;
|
|
IPCOM_SA_LEN_SET(&bind_any.sa, sizeof(struct Ip_sockaddr_in6));
|
|
break;
|
|
#endif /* IPCOM_USE_INET6 */
|
|
default:
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : domain = %u."IP_LF, sock->ipcom.domain);
|
|
goto inval_out;
|
|
}
|
|
ret = ops->bind(sock,
|
|
&bind_any.sa,
|
|
IPCOM_SA_LEN_GET(&bind_any.sa),
|
|
IP_TRUE);
|
|
if (ret < 0)
|
|
{
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : ret = %d."IP_LF, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ipnet_sock_set_unreachable(sock);
|
|
/* for connect operation, use sa->sa_family instead of sock->ipcom.domain */
|
|
switch (sa->sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
in_to = (struct Ip_sockaddr_in *)sa;
|
|
second_sa = (struct Ip_sockaddr *)((Ip_caddr_t)sa + sizeof(struct Ip_sockaddr_in));
|
|
sock->ip4->daddr_n = in_to->sin_addr.s_addr;
|
|
sock->dport = ip_ntohs(in_to->sin_port);
|
|
break;
|
|
#endif /* IPCOM_USE_INET */
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
in6_to = (struct Ip_sockaddr_in6 *)sa;
|
|
second_sa = (struct Ip_sockaddr *)((Ip_caddr_t)sa + sizeof(struct Ip_sockaddr_in6));
|
|
/* Copy the peer information */
|
|
sock->ip6->daddr = in6_to->sin6_addr;
|
|
sock->ip6->flowinfo = ip_ntohl(in6_to->sin6_flowinfo);
|
|
sock->dport = ip_ntohs(in6_to->sin6_port);
|
|
if (sock->bind_to_ifindex > 0 && IPNET_IP6_IS_SCOPE_LINK_LOCAL(&in6_to->sin6_addr))
|
|
/* Socket is bound to an interface.*/
|
|
sock->ip6->iscope_id = sock->bind_to_ifindex;
|
|
else
|
|
sock->ip6->iscope_id = in6_to->sin6_scope_id;
|
|
break;
|
|
#endif /* IPCOM_USE_INET6 */
|
|
default:
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : sa_family = %u."IP_LF, sa->sa_family);
|
|
goto inval_out;
|
|
}
|
|
ipnet_sock_set_reachable(sock);
|
|
if (0 == sock->dport)
|
|
{
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : sock->dport = %u."IP_LF, sock->dport);
|
|
goto inval_out;
|
|
}
|
|
|
|
/* allocate the TCB(association) */
|
|
ret = ipsctp_alloc_tcb(inst,
|
|
(Ipsctp_addr *)sa,
|
|
0, /* alloc a new v-tag */
|
|
&new_tcb,
|
|
IP_NULL);
|
|
if ((ret < 0) || (IP_NULL == new_tcb))
|
|
return ret;
|
|
|
|
/* set the connecting flag */
|
|
IP_BIT_SET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTING));
|
|
|
|
/* if more than one connectx address, then and the other addresses to
|
|
remote transport address list */
|
|
if (addrs_cnt > 1)
|
|
{
|
|
ret = ipsctp_sock_connectx_add(new_tcb,
|
|
second_sa,
|
|
addrs_cnt - 1);
|
|
if (0 < ret)
|
|
{
|
|
ret = IPNET_ERRNO(ENOBUFS);
|
|
goto free_out;
|
|
}
|
|
}
|
|
|
|
if (IP_TRUE == is_delay)
|
|
{
|
|
IP_BIT_SET(new_tcb->tcb_flags, IPSCTP_TCBC_FLAG_DELAY_CONNECT);
|
|
ipsctp_timer_add(IP_NULL,
|
|
new_tcb,
|
|
new_tcb->primary_transport,
|
|
IPSCTP_TIMO_T1);
|
|
}
|
|
else
|
|
{
|
|
/* send initialize packet to the peer */
|
|
ret = ipsctp_send_init(new_tcb);
|
|
if (IPNET_ERRNO(ENOBUFS) == ret)
|
|
{
|
|
/* TODO: schedule connect retry */
|
|
}
|
|
/* for RTO calculate */
|
|
(void)ipcom_microtime(&new_tcb->time_entered);
|
|
}
|
|
/* change the tcb state */
|
|
(void)ipsctp_change_state(new_tcb, IPSCTP_STATE_COOKIE_WAIT);
|
|
|
|
if (ipsctp_style(sock, TCP))
|
|
IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_CONNECTED);
|
|
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Connectx OK."IP_LF);
|
|
#endif
|
|
/* blocking if set NON-BLOCKING flag ONLY for connectx */
|
|
return (is_delay ? 0 : IPNET_ERRNO(EINPROGRESS));
|
|
|
|
inval_out:
|
|
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
|
|
free_out:
|
|
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : free out for error."IP_LF);
|
|
(void)ipsctp_free_tcb(new_tcb);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sock_connectx_complete
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* Parameters: sock - the set socket descriptor.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipsctp_sock_connectx_complete(Ipsctp_socket *sock,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == optval))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
/* check instance point */
|
|
IPSCTP_SOCK_INST_CHECK(sock, inst);
|
|
|
|
if (ipsctp_style(sock, TCP)
|
|
&& (IP_BIT_ISSET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTED))))
|
|
tcb = (Ipsctp_tcb *)ipcom_list_first(&inst->assoc_list);
|
|
else
|
|
tcb = ipsctp_sock_lookup_da(sock, (struct Ip_sockaddr *)optval);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : No match TCB."IP_LF);
|
|
return IPNET_ERRNO(ENOENT);
|
|
}
|
|
|
|
if (IP_BIT_ISSET(tcb->tcb_flags, IPSCTP_TCBC_FLAG_DELAY_CONNECT))
|
|
{
|
|
IP_BIT_CLR(tcb->tcb_flags, IPSCTP_TCBC_FLAG_DELAY_CONNECT);
|
|
/* for RTO calculate */
|
|
(void)ipcom_microtime(&tcb->time_entered);
|
|
(void)ipsctp_send_init(tcb);
|
|
}
|
|
else
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Operation already in progress."IP_LF);
|
|
return IPNET_ERRNO(EALREADY);
|
|
}
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Connectx complete OK."IP_LF);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_setsockopt
|
|
*===========================================================================
|
|
* Description: base on draft-ietf-tsvwg-sctpsocket-17
|
|
* 7.2. setsockopt(), getsockopt()
|
|
*
|
|
* Applications use setsockopt() and getsockopt() to set or retrieve
|
|
* socket options. Socket options are used to change the default
|
|
* behavior of sockets calls. They are described in Section 8
|
|
*
|
|
* The syntax is:
|
|
*
|
|
* ret = getsockopt(int sd, int level, int optname, void *optval,
|
|
* socklen_t *optlen);
|
|
* ret = setsockopt(int sd, int level, int optname, const void *optval,
|
|
* socklen_t optlen);
|
|
*
|
|
*
|
|
* sd - the socket descriptor.
|
|
* level - set to IPPROTO_SCTP for all SCTP options.
|
|
* optname - the option name.
|
|
* optval - the buffer to store the value of the option.
|
|
* optlen - the size of the buffer (or the length of the option
|
|
* returned).
|
|
*
|
|
* All socket options set on a 1-to-1 listening sockets also apply all
|
|
* accepted sockets. All socket options set on a 1-to-many socket using
|
|
* the assoc_id 0 applies for all future assocs on the socket.
|
|
* Parameters: sock - the socket descriptor.
|
|
* level - set to IPPROTO_SCTP for all SCTP options.
|
|
* optname - the option name.
|
|
* optval - the buffer stored the value of this option.
|
|
* optlen - the size of the buffer.
|
|
* Returns: 0 = success, <0 error code.
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipsctp_setsockopt(Ipsctp_socket *sock,
|
|
int level,
|
|
int optname,
|
|
IP_CONST void *optval,
|
|
Ip_socklen_t optlen)
|
|
{
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
int ret = 0;
|
|
int val = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_NULL == (inst = sock->sctp_inst)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument."IP_LF);
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
#if defined(IPSCTP_DEBUG)
|
|
IPSCTP_DBG4(IPSCTP_DBG_SOCK,
|
|
"IPSCTP : Set socket option(sock %u, level 0x%x, optname 0x%x, optlen %u)."IP_LF,
|
|
sock->ipcom.fd,
|
|
level,
|
|
optname,
|
|
optlen);
|
|
#endif
|
|
if (IP_SOL_SOCKET == level)
|
|
{
|
|
switch (optname)
|
|
{
|
|
case IP_SO_RCVLOWAT:
|
|
if (optlen != sizeof(int))
|
|
return IPNET_ERRNO(EINVAL);
|
|
val = *(int*) optval;
|
|
|
|
if (val <= 0 || val > sock->recv_max_bytes)
|
|
return IPNET_ERRNO(EINVAL);
|
|
inst->recv_lowat = (Ip_u32) val;
|
|
break;
|
|
|
|
case IP_SO_SNDLOWAT:
|
|
if (optlen != sizeof(int))
|
|
return IPNET_ERRNO(EINVAL);
|
|
val = *(int*) optval;
|
|
|
|
if (val <= 0 || val > sock->send_max_bytes)
|
|
return IPNET_ERRNO(EINVAL);
|
|
inst->send_lowat = (Ip_u32) val;
|
|
break;
|
|
|
|
case IP_SO_LINGER:
|
|
{
|
|
IP_CONST struct Ip_linger *l = optval;
|
|
|
|
if (optlen != sizeof(struct Ip_linger))
|
|
return IPNET_ERRNO(EINVAL);
|
|
|
|
if (l->l_onoff)
|
|
inst->linger_time = l->l_linger;
|
|
else
|
|
inst->linger_time = IPSCTP_INVALID_LINGER_TIME;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
/* Let the caller handle this option */
|
|
return IPNET_ERRNO(EOPNOTSUPP);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* if level is not SCTP, return to upper layer to handle */
|
|
if (IP_IPPROTO_SCTP != level)
|
|
return IPNET_ERRNO(EOPNOTSUPP);
|
|
|
|
/* process optname cases */
|
|
switch (optname)
|
|
{
|
|
case IPSCTP_RTOINFO:
|
|
ret = ipsctp_setsockopt_rtoinfo(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_ASSOCINFO:
|
|
ret = ipsctp_setsockopt_associnfo(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_DEFAULT_SEND_PARAM:
|
|
ret = ipsctp_setsockopt_default_send_param(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_PRIMARY_ADDR:
|
|
ret = ipsctp_setsockopt_primary_addr(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_PEER_ADDR_PARAMS:
|
|
ret = ipsctp_setsockopt_peer_addr_params(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_CONTEXT:
|
|
ret = ipsctp_setsockopt_context(sock, optval, optlen);
|
|
break;
|
|
|
|
#if 0
|
|
case IPSCTP_AUTH_ACTIVE_KEY:
|
|
ret = ipsctp_setsockopt_active_key(sock, optval, optlen);
|
|
break;
|
|
#endif
|
|
|
|
case IPSCTP_EVENTS:
|
|
ret = ipsctp_setsockopt_events(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_INITMSG:
|
|
ret = ipsctp_setsockopt_initmsg(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_MAXSEG:
|
|
ret = ipsctp_setsockopt_maxseg(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_DELAYED_SACK:
|
|
ret = ipsctp_setsockopt_delay_ack(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_MAX_BURST:
|
|
ret = ipsctp_setsockopt_max_burst(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_BINDX_ADD_ADDR:
|
|
ret = ipsctp_sock_bindx_add(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_BINDX_REM_ADDR:
|
|
ret = ipsctp_sock_bindx_del(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_CONNECTX:
|
|
ret = ipsctp_sock_connectx(sock, optval, optlen, IP_FALSE);
|
|
break;
|
|
|
|
case IPSCTP_CONNECTX_DELAY:
|
|
ret = ipsctp_sock_connectx(sock, optval, optlen, IP_TRUE);
|
|
break;
|
|
|
|
case IPSCTP_CONNECTX_COMPLETE:
|
|
ret = ipsctp_sock_connectx_complete(sock, optval, optlen);
|
|
break;
|
|
|
|
case IPSCTP_NODELAY:
|
|
val = *(int*)optval;
|
|
if (val)
|
|
{
|
|
IP_BIT_SET(inst->feature_flags , IPSCTP_INST_FLAGS_NODELAY);
|
|
IP_BIT_SET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_TCP_NODELAY));
|
|
}
|
|
else
|
|
{
|
|
IP_BIT_CLR(inst->feature_flags , IPSCTP_INST_FLAGS_NODELAY);
|
|
IP_BIT_CLR(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_TCP_NODELAY));
|
|
}
|
|
break;
|
|
|
|
/* This boolean flag is used to enable or disable explicit end of record
|
|
* (EOR) marking. When this option is enabled, a user may make multiple
|
|
* send system calls to send a record and must indicate that they are
|
|
* finished sending a particular record by including on the send the
|
|
* SCTP_EOR flag. If this boolean flag is disabled then each individual
|
|
* send system call is considered to have a SCTP_EOR indicator set on it
|
|
* implicitly without the user having to explicitly add this flag.
|
|
*/
|
|
case IPSCTP_EXPLICIT_EOR:
|
|
val = *(int*)optval;
|
|
if (val)
|
|
IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_EXPLICIT_EOR);
|
|
else
|
|
IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_EXPLICIT_EOR);
|
|
break;
|
|
|
|
/* This option will enable or disable the use of the extended version of
|
|
* the sctp_sndrcvinfo structure. If this option is disabled, then the
|
|
* normal sctp_sndrcvinfo structure is returned in all receive message
|
|
* calls. If this option is enabled then the sctp_extrcvinfo structure
|
|
* is returned in all receive message calls.
|
|
*/
|
|
case IPSCTP_USE_EXT_RCVINFO:
|
|
val = *(int*)optval;
|
|
if (val)
|
|
IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_EXT_RCVINFO);
|
|
else
|
|
IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_EXT_RCVINFO);
|
|
break;
|
|
|
|
/* This socket option is a boolean flag which turns on or off mapped V4
|
|
* addresses. If this option is turned on and the socket is type
|
|
* PF_INET6, then IPv4 addresses will be mapped to V6 representation.
|
|
* If this option is turned off, then no mapping will be done of V4
|
|
* addresses and a user will receive both PF_INET6 and PF_INET type
|
|
* addresses on the socket.
|
|
*/
|
|
case IPSCTP_I_WANT_MAPPED_V4_ADDR:
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V6)
|
|
&& IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUND_V4))
|
|
{
|
|
val = *(int*)optval;
|
|
if (val)
|
|
IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4);
|
|
else
|
|
IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_WANT_MAPPED_V4);
|
|
}
|
|
else
|
|
ret = IPNET_ERRNO(EINVAL);
|
|
break;
|
|
|
|
/* This option is a on/off flag and is passed an integer where a non-
|
|
* zero is on and a zero is off. If enabled no SCTP message
|
|
* fragmentation will be performed. Instead if a message being sent
|
|
* exceeds the current PMTU size, the message will NOT be sent and
|
|
* instead a error will be indicated to the user.
|
|
*/
|
|
case IPSCTP_DISABLE_FRAGMENTS:
|
|
val = *(int*)optval;
|
|
if (val)
|
|
IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAGS_NO_FRAGMENT);
|
|
else
|
|
IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAGS_NO_FRAGMENT);
|
|
break;
|
|
|
|
/* pls refer to IPSCTP_FRAGMENT_INTERLEAVE's comment */
|
|
case IPSCTP_FRAGMENT_INTERLEAVE:
|
|
val = *(int*)optval;
|
|
if (IPSCTP_FRAG_LEVEL_2 == val)
|
|
{
|
|
IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_FRAG_INTERLEAVE);
|
|
IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_INTERLEAVE_STRMS);
|
|
}
|
|
else if (IPSCTP_FRAG_LEVEL_1 == val)
|
|
{
|
|
IP_BIT_SET(inst->feature_flags, IPSCTP_INST_FLAG_FRAG_INTERLEAVE);
|
|
IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_INTERLEAVE_STRMS);
|
|
}
|
|
else if (IPSCTP_FRAG_LEVEL_0 == val)
|
|
{
|
|
IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_FRAG_INTERLEAVE);
|
|
IP_BIT_CLR(inst->feature_flags, IPSCTP_INST_FLAG_INTERLEAVE_STRMS);
|
|
}
|
|
else
|
|
ret = IPNET_ERRNO(EINVAL);
|
|
break;
|
|
|
|
/* This option will set or get the SCTP partial delivery point. This
|
|
* point is the size of a message where the partial delivery API will be
|
|
* invoked to help free up rwnd space for the peer. Setting this to a
|
|
* lower value will cause partial deliveries to happen more often. The
|
|
* calls argument is an integer that sets or gets the partial delivery
|
|
* point. Note also that the call will fail if the user attempts to set
|
|
* this value larger than the socket receive buffer size.
|
|
* Note that any single message having a length smaller than or equal to
|
|
* the SCTP partial delivery point will be delivered in one single read
|
|
* call as long as the user provided buffer is large enough to hold the
|
|
* message.
|
|
*/
|
|
case IPSCTP_PARTIAL_DELIVERY_POINT:
|
|
if (*(Ip_u32 *)optval >= sock->send_max_bytes)
|
|
ret = IPNET_ERRNO(EINVAL);
|
|
else
|
|
inst->pd_point = *(Ip_u32 *)optval;
|
|
break;
|
|
|
|
/* This socket option is applicable to the one-to-many style socket
|
|
* only. When set it will cause associations that are idle for more
|
|
* than the specified number of seconds to automatically close using the
|
|
* graceful shutdown procedure. An association being idle is defined as
|
|
* an association that has NOT sent or received user data. The special
|
|
* value of '0' indicates that no automatic close of any associations
|
|
* should be performed, this is the default value. The option expects
|
|
* an integer defining the number of seconds of idle time before an
|
|
* association is closed.
|
|
* An application using this option should enable receiving the
|
|
* association change notification. This is the only mechanism an
|
|
* application is informed about the closing of an association. After
|
|
* an association is closed, the association ID assigned to it can be
|
|
* reused. An application should be aware of this to avoid the possible
|
|
* problem of sending data to an incorrect peer end point.
|
|
*/
|
|
case IPSCTP_AUTOCLOSE:
|
|
if (ipsctp_style(sock, UDP))
|
|
{
|
|
inst->autoclose_time = *(Ip_u32 *)optval;
|
|
IP_BIT_SET(inst->inst_flags, IPSCTP_SOCKET_FLAG_AUTOCLOSE);
|
|
}
|
|
else
|
|
ret = IPNET_ERRNO(EINVAL);
|
|
break;
|
|
|
|
default:
|
|
#if 0
|
|
if (IPSCTP_SOCKOPT_R == ipsctp_sockopt_check_rw(optname))
|
|
{
|
|
ret = 0;
|
|
break;
|
|
}
|
|
#endif
|
|
return IPNET_ERRNO(ENOPROTOOPT);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_ioctl
|
|
*===========================================================================
|
|
* Description: Handler for SCTP specific IOCTL
|
|
* Parameters: sock - The socket affected by the ioctl
|
|
* request - The ioctl request being issued
|
|
* argp - The argument associated with the request
|
|
* Returns: 0 = success
|
|
* <0 = socket error code
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipsctp_ioctl(Ipsctp_socket *sock,
|
|
unsigned long request,
|
|
void *argp)
|
|
{
|
|
int *iargp = argp;
|
|
|
|
if ((!sock)||(!sock->sctp_inst)||(IP_NULL == iargp))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : Invalid argument.");
|
|
return IPNET_ERRNO(EINVAL);
|
|
}
|
|
|
|
switch (request)
|
|
{
|
|
case IP_SIOCATMARK:
|
|
*iargp = 0;
|
|
break;
|
|
|
|
default:
|
|
return IPNET_ERRNO(EOPNOTSUPP);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_get_netstat_info
|
|
*===========================================================================
|
|
* Description: Returns the SCTP netstat information for the socket.
|
|
* Parameters: sock - the socket descriptor.
|
|
* Returns: The SCTP netstat information pointer.
|
|
* NOTE : THE CALLER NEED FREE THE MEMORY,
|
|
* in ipnet_netstat_print_entries.
|
|
*
|
|
*/
|
|
IP_PUBLIC void *
|
|
ipsctp_get_netstat_info(Ipsctp_socket *sock)
|
|
{
|
|
Ipsctp_netstat_tcbinfo *tcbinfo = IP_NULL;
|
|
Ipsctp_netstatinfo *netstatinfo = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
Ipsctp_laddr_entry *laddr = IP_NULL;
|
|
Ipsctp_transport *tp = IP_NULL;
|
|
Ipsctp_instance *inst = IP_NULL;
|
|
Ip_u32 cur_addr_num = 0;
|
|
Ip_u32 cur_tcb_num = 0;
|
|
Ip_u32 i = 0;
|
|
|
|
if ((IP_NULL == sock) || (IP_IPPROTO_SCTP != sock->proto))
|
|
return IP_NULL;
|
|
|
|
inst = sock->sctp_inst;
|
|
|
|
netstatinfo = (Ipsctp_netstatinfo *)ipcom_malloc(sizeof(Ipsctp_netstatinfo));
|
|
if (IP_NULL == netstatinfo)
|
|
return IP_NULL;
|
|
|
|
(void)ipcom_memset(netstatinfo, 0, sizeof(Ipsctp_netstatinfo));
|
|
netstatinfo->tcb_count = ipsctp_list_size(&inst->assoc_list);
|
|
if (0 == netstatinfo->tcb_count)
|
|
{
|
|
ipcom_free(netstatinfo);
|
|
return IP_NULL;
|
|
}
|
|
|
|
/* malloc all the TCB display memory for the special socket */
|
|
netstatinfo->tcb = (Ipsctp_netstat_tcbinfo *)ipcom_malloc(netstatinfo->tcb_count * sizeof(Ipsctp_netstat_tcbinfo));
|
|
if (IP_NULL == netstatinfo->tcb)
|
|
{
|
|
ipcom_free(netstatinfo);
|
|
return IP_NULL;
|
|
}
|
|
|
|
(void)ipcom_memset(netstatinfo->tcb, 0, netstatinfo->tcb_count * sizeof(Ipsctp_netstat_tcbinfo));
|
|
/* copy local address */
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
{
|
|
netstatinfo->laddr_count = 1;
|
|
}
|
|
else
|
|
{
|
|
netstatinfo->laddr_count = ipsctp_list_size(&inst->laddr_list);
|
|
}
|
|
|
|
/* copy the local address list for netstat display */
|
|
if (netstatinfo->laddr_count > 0)
|
|
{
|
|
netstatinfo->laddr = (union Ip_sockaddr_union *)ipcom_malloc(netstatinfo->laddr_count * sizeof(union Ip_sockaddr_union));
|
|
if (IP_NULL == netstatinfo->laddr)
|
|
goto free_ret;
|
|
|
|
(void)ipcom_memset(netstatinfo->laddr, 0, netstatinfo->laddr_count * sizeof(union Ip_sockaddr_union));
|
|
cur_addr_num = 0;
|
|
if (IP_BIT_ISSET(inst->inst_flags, IPSCTP_SOCKET_BOUNDALL))
|
|
{
|
|
ipsctp_list_for_each(&inst->laddr_list, laddr, Ipsctp_laddr_entry)
|
|
{
|
|
if (sock->ipcom.domain == laddr->laddr.sa.sa_family)
|
|
{
|
|
ipcom_memcpy((void *)&netstatinfo->laddr[cur_addr_num++],
|
|
(void *)&laddr->laddr,
|
|
sizeof(union Ip_sockaddr_union));
|
|
break; /* only one */
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ipsctp_list_for_each(&inst->laddr_list, laddr, Ipsctp_laddr_entry)
|
|
{
|
|
ipcom_memcpy((void *)&netstatinfo->laddr[cur_addr_num++],
|
|
(void *)&laddr->laddr,
|
|
sizeof(union Ip_sockaddr_union));
|
|
}
|
|
}
|
|
}
|
|
|
|
cur_tcb_num = 0;
|
|
/* loop all the association to store in netstatinfo structure */
|
|
ipsctp_list_for_each(&inst->assoc_list,
|
|
tcb,
|
|
Ipsctp_tcb)
|
|
{
|
|
tcbinfo = &netstatinfo->tcb[cur_tcb_num];
|
|
cur_tcb_num++;
|
|
|
|
tcbinfo->state = (int)tcb->state;
|
|
/* copy transport address */
|
|
tcbinfo->raddr_count = ipsctp_list_size(&tcb->transport_list);
|
|
if (tcbinfo->raddr_count > 0)
|
|
{
|
|
tcbinfo->raddr = (union Ip_sockaddr_union *)ipcom_malloc(tcbinfo->raddr_count * sizeof(union Ip_sockaddr_union));
|
|
if (IP_NULL == tcbinfo->raddr)
|
|
goto free_ret;
|
|
|
|
(void)ipcom_memset(tcbinfo->raddr, 0, tcbinfo->raddr_count * sizeof(union Ip_sockaddr_union));
|
|
cur_addr_num = 0;
|
|
ipsctp_list_for_each(&tcb->transport_list, tp, Ipsctp_transport)
|
|
{
|
|
ipcom_memcpy((void *)&tcbinfo->raddr[cur_addr_num++],
|
|
(void *)&tp->paddr,
|
|
sizeof(union Ip_sockaddr_union));
|
|
}
|
|
}
|
|
}
|
|
|
|
return netstatinfo;
|
|
|
|
free_ret:
|
|
/* free all the memory if not complete the operation */
|
|
for (i = 0; i < cur_tcb_num; i++)
|
|
{
|
|
if (netstatinfo->tcb[i].raddr_count > 0)
|
|
{
|
|
ipcom_free(netstatinfo->tcb[i].raddr);
|
|
}
|
|
}
|
|
|
|
if (netstatinfo->laddr_count > 0)
|
|
{
|
|
ipcom_free(netstatinfo->laddr);
|
|
}
|
|
ipcom_free(netstatinfo->tcb);
|
|
ipcom_free(netstatinfo);
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipsctp_sys_send_init
|
|
*===========================================================================
|
|
* Description: send INIT packet.
|
|
* Parameters: fd - socket descriptor.
|
|
* vtag - association verification tag.
|
|
* Returns: None.
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipsctp_sys_send_init(Ip_fd fd, Ip_u32 vtag)
|
|
{
|
|
Ipsctp_socket *sock = IP_NULL;
|
|
Ipsctp_tcb *tcb = IP_NULL;
|
|
int ret = 0;
|
|
|
|
if ((fd < IPNET_SOCKET_STARTFD)
|
|
|| (fd >= ipnet_conf_max_sockets)
|
|
|| (IP_NULL == (sock = ipnet->sockets[fd]))
|
|
|| (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_CLOSED)))
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : invalid parameter for sending INIT."IP_LF);
|
|
return;
|
|
}
|
|
|
|
/* get TCB by the specified association ID */
|
|
tcb = ipsctp_tcb_lookup_by_instance(sock->sctp_inst,
|
|
vtag,
|
|
IP_NULL,
|
|
IP_NULL);
|
|
if (IP_NULL == tcb)
|
|
{
|
|
IPSCTP_DBG0(IPSCTP_DBG_SOCK, "IPSCTP : send init but no TCB."IP_LF);
|
|
return;
|
|
}
|
|
|
|
/* for RTO calculate */
|
|
(void)ipcom_microtime(&tcb->time_entered);
|
|
|
|
/* set the connecting flag */
|
|
IP_BIT_SET(sock->flags, IPSCTP_STACK_CONST(SOCKET_FLAG_CONNECTING));
|
|
|
|
IP_BIT_SET(tcb->tcb_flags, IPSCTP_TCBC_FLAG_CONNECTING);
|
|
|
|
/* change the tcb state, NOTE : must first change state, ipsctp_output
|
|
* will use this state(UDP will often use). */
|
|
(void)ipsctp_change_state(tcb, IPSCTP_STATE_COOKIE_WAIT);
|
|
|
|
/* send initialize packet to the peer */
|
|
ret = ipsctp_send_init(tcb);
|
|
if (ret < 0)
|
|
{
|
|
ipcom_printf("IPSCTP : send init error(%d)."IP_LF, ret);
|
|
IPSCTP_DBG1(IPSCTP_DBG_SOCK, "IPSCTP : send init error(%d)."IP_LF, ret);
|
|
return;
|
|
}
|
|
IPSCTP_DBGSTAT_INC(IPSCTP_DBGSTAT_SYS_SEND_INIT);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|