Files
m241Neelima/components/ip_net2-6.9/ipsctp/src/ipsctp_sock.c
T
2026-07-30 11:19:44 +05:30

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 *)&params->spp_address))
{
IPSCTP_DBG1(IPSCTP_DBG_SOCK,
"IPSCTP : lookup transport address %s."IP_LF,
ipsctp_addr_ntop((Ipsctp_addr *)&params->spp_address,
ipnet->log_buf,
256));
tp = ipsctp_transport_lookup(tcb,
(IP_CONST Ipsctp_addr *)&params->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
****************************************************************************
*/