Files
2026-07-30 11:19:44 +05:30

1028 lines
34 KiB
C

/* ipnet_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.
*/
/*
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 "ipnet_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#ifdef IPSCTP
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_clib.h>
#include <ipcom_cstyle.h>
#include <ipcom_syslog.h>
#include <ipcom_type.h>
#include "ipcom_sock.h"
#include "ipnet_h.h"
#include "ipnet_usr_sock.h"
#include <ipsctp.h>
#include <ipsctp_sock.h>
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
typedef struct Ipnet_sock_sctp_ops_struct
{
Ipnet_sock_inet_ops i;
Ipnet_sock_init_f network_init;
Ipnet_sock_destroy_f network_destroy;
Ipnet_sock_connect_f network_connect;
Ipnet_sock_bind_f network_bind;
#ifdef IPCOM_USE_INET
Ipnet_sock_bind_f network_bind_v4;
#endif
#ifdef IPCOM_USE_INET6
Ipnet_sock_bind_f network_bind_v6;
#endif
}
Ipnet_sock_sctp_ops;
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 LOCAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_sock_sctp_init
*===========================================================================
* Description: SCTP socket layer initialization.
* Parameters: sock - The socket that is beeing created.
* user_context - IP_TRUE if this function is called in user
* process context. IP_FALSE if it called from
* stack context.
* Returns: 0 = success, <0 = error code
*/
IP_STATIC int
ipnet_sock_sctp_init(Ipnet_socket *sock,
Ip_bool user_context)
{
Ipnet_sock_sctp_ops *ops = IP_NULL;
int ret = 0;
if (IP_NULL == sock)
{
ip_assert(IP_FALSE);
return -IP_ERRNO_EINVAL;
}
ops = (Ipnet_sock_sctp_ops *)ipnet_sock_get_ops(sock);
if (IP_NULL == ops)
{
ip_assert(IP_FALSE);
return -IP_ERRNO_EINVAL;
}
sock->proto = IP_IPPROTO_SCTP;
/*
* If socket is created in user-context, add an extra reference for the
* SCTP module. Otherwise, the SCTP modul will add a reference for the
* user when returning the socket in accept().
* NOTE : we will wait until the shutdown process finish, if we send
* SHUTDOWN packet, so socket also need be taken till that
* time. [20081120]
*/
if (IP_TRUE == user_context)
ipcom_atomic_inc(&sock->ref_count);
/*
* Defines the offset into the SCTP transport layer where the checksum
* should be stored. Other transport protocols such as TCP and UDP use
* 16 bit checksum but SCTP checksum is 32 bits. So perhaps the chk_offset
* should not be set, which means the SCTP transport layer will have to
* validate the checksum in the SCTP common header itself.
*/
sock->chk_offset = 0;
IP_BIT_CLR(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM);
sock->sctp_inst = ipnet_sock_alloc_private_data(sock,
IPSCTP_SIZE_SOCK_DATA );
ipcom_memset(sock->sctp_inst, 0, IPSCTP_SIZE_INST);
/* init socket IP4 or IP6 data, and set bound flag */
if (IP_NULL != ops->network_init)
{
ret = ops->network_init(sock, user_context);
if (ret < 0)
return ret;
}
sock->pkt_dtor = IP_NULL;
if ((ret = ipsctp_sock_init(sock)) < 0)
return ret;
return 0;
}
/*
*===========================================================================
* ipnet_sock_sctp_destroy
*===========================================================================
* Description: Frees all resources held by the SCTP socket.
* Parameters: sock - The socket that is beeing destroyed.
* Returns:
*/
IP_STATIC void
ipnet_sock_sctp_destroy(Ipnet_socket *sock)
{
Ipnet_sock_sctp_ops *ops = IP_NULL;
if (IP_NULL == sock)
{
ip_assert(IP_FALSE);
return;
}
ops = (Ipnet_sock_sctp_ops *) ipnet_sock_get_ops(sock);
if (IP_NULL == ops)
{
ip_assert(IP_FALSE);
return;
}
if (ops->network_destroy != IP_NULL)
ops->network_destroy(sock);
ipsctp_sock_free(sock);
return;
}
/*
*===========================================================================
* ipnet_sock_sctp_bind
*===========================================================================
* Description: The SCTP network layer bind.
* Parameters: sock - The socket to bind.
* name - The socket name.
* namelen - The length of the name variable.
* check_reuse - Check reuse for port or address.
* Returns: 0 = success, <0 = error code
*/
IP_STATIC int
ipnet_sock_sctp_bind(struct Ipnet_socket_struct *sock,
IP_CONST struct Ip_sockaddr *name,
Ip_socklen_t namelen,
Ip_bool check_reuse)
{
Ipnet_sock_sctp_ops *ops = IP_NULL;
int ret = 0;
if ((IP_NULL == sock)||(IP_NULL == name)||(namelen <= 0))
{
ip_assert(IP_FALSE);
return -IP_ERRNO_EINVAL;
}
ops = (Ipnet_sock_sctp_ops *)ipnet_sock_get_ops(sock);
if (IP_NULL == ops)
{
ip_assert(IP_FALSE);
return -IP_ERRNO_EINVAL;
}
/* call the common function:
* 1)do bound IP address verifying
* 2)add to Ipnet_ip4_addr_entry
*/
ret = ops->network_bind(sock, name, namelen, check_reuse);
if (ret >= 0)
{
ret = ipnet_sock_bind_to_port(sock, name, check_reuse);
if (ret >= 0)
ret = ipsctp_bind(sock, name, namelen);
}
/* do rollback in ipcom_bind for error */
return ret;
}
/*
*===========================================================================
* ipnet_sock_sctp_connect
*===========================================================================
* Description: The SCTP transport layer connect.
* Parameters: sock - The socket to connect.
* to - The address to connect to.
* tolen - The size of the 'to' buffer.
* Returns: 0 = success, <0 = error code
*
*/
IP_STATIC int
ipnet_sock_sctp_connect(Ipnet_socket *sock,
IP_CONST struct Ip_sockaddr *to,
Ip_socklen_t tolen)
{
Ipnet_sock_sctp_ops *ops = IP_NULL;
int ret = 0;
if (IP_NULL == sock)
{
ip_assert(IP_FALSE);
return -IP_ERRNO_EINVAL;
}
ops = (Ipnet_sock_sctp_ops *)ipnet_sock_get_ops(sock);
if (IP_NULL == ops)
{
ip_assert(IP_FALSE);
return -IP_ERRNO_EINVAL;
}
/* call the common function:
* 1)do binding operation if have not bind now
* 2)check the address validation
*/
ret = ops->network_connect(sock, to, tolen);
if (ret < 0)
return ret;
if (0 == sock->dport)
/* Invalid port */
return -IP_ERRNO_EINVAL;
/* The socket is a sctp socket, do the sctp connect */
if (IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_CONNECTING))
/* Asynchronous connect is still not finished */
return -IP_ERRNO_EALREADY;
return ipsctp_connect(sock, to);
}
/*
*===========================================================================
* ipnet_sock_usr_pkts_from_iov_sctp
*===========================================================================
* Description: Copies the user data into MSS sized packets.
* Parameters: sock - The socket that will be use to send.
* msg - 'msg_iov' contains the buffer(s) to send.
* flags - IP_MSG_xxx flags.
* offset - Number of bytes into the msg->msg_iov buffers
* where the copy into packets should start.
* total_buf_len - the sum of the length of all
* msg->msg_iov buffers in 'msg'.
* prslt - Points to result structure where the constructed packet
* and transient error (if any) are returned.
* prslt->pkt->next points to the next packet in case
* more than one packet was allocated.
* Returns: <0 = error code (-IPNET_ERRNO_xxx)
* 0 = this operation released the socket lock, the
* sendmsg operation must be restarted
* >0 = number of bytes that have been allocated in the
* socket send buffer and copied into the
* packet(s) pointed to by prslt->pkt.
*/
IP_STATIC int
ipnet_sock_usr_pkts_from_iov_sctp(Ipnet_socket *sock,
IP_CONST struct Ip_msghdr *msg,
int flags,
int offset,
int total_buf_len,
Ipnet_pkt_result *prslt)
{
struct Ip_cmsghdr *cmsghdr = IP_NULL;
struct Ipsctp_sndrcvinfo *sndrcv = IP_NULL;
Ipcom_pkt *pkt_head = IP_NULL;
Ipcom_pkt *pkt_tail = IP_NULL;
Ipsctp_instance *inst = IP_NULL;
Ip_u8 *buf = IP_NULL;
int buf_len = 0;
int i = 0;
int len = 0;
int bytes_to_copy;
Ip_bool non_blocking = IP_FALSE;
Ip_u32 pmtu = 0;
Ip_u32 mss = 0;
void *tcb = IP_NULL;
ip_assert(IP_NULL != msg);
ip_assert(IP_NULL != sock);
inst = (Ipsctp_instance *)sock->sctp_inst;
ip_assert(IP_NULL != inst);
prslt->pkt = IP_NULL;
prslt->transient_err = 0;
/* loop the ancillary data, seach for what we needed */
for (cmsghdr = (struct Ip_cmsghdr *)IP_CMSG_FIRSTHDR(msg);
IP_NULL != cmsghdr;
cmsghdr = (struct Ip_cmsghdr *)IP_CMSG_NXTHDR(msg, cmsghdr))
{
/* check message level */
if (IP_IPPROTO_SCTP != cmsghdr->cmsg_level)
continue;
/* check message type */
if (IPSCTP_SNDRCV != cmsghdr->cmsg_type)
continue;
/* check send-receive length */
if (cmsghdr->cmsg_len < IP_CMSG_LEN(sizeof(*sndrcv)))
{
IPCOM_LOG2(ERR,
"control message size is %u, should be %u",
cmsghdr->cmsg_len,
IP_CMSG_LEN(sizeof(*sndrcv)));
return 0;
}
sndrcv = (struct Ipsctp_sndrcvinfo *)IP_CMSG_DATA(cmsghdr);
break;
}
non_blocking = IP_BIT_ISSET(sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING)
|| IP_BIT_ISSET(flags, IP_MSG_DONTWAIT);
/*
* If offset == total_buf_len, which can in particular happen
* if both are zero, then the loop immediately below can
* access beyond the end of the iovec array. Avoid this.
*/
if (offset == total_buf_len)
{
/* for only EOF/ABORT flag set and no data need to be sent. */
if ((0 == total_buf_len)
&&(sndrcv)
&&((IPSCTP_EOF & sndrcv->sinfo_flags) || (IPSCTP_ABORT & sndrcv->sinfo_flags)))
/* only hdr alloc */
prslt->pkt = ipcom_pkt_malloc(0,
IP_FLAG_FC_STACKCONTEXT |
(non_blocking ? 0 : IP_FLAG_FC_BLOCKOK));
return 0;
}
IPNET_DATA_LOCK(sock->sctp_inst->tcb_lock);
if (sock->sctp_inst->get_tcb_info(sock, msg, sndrcv, &pmtu, &mss, (void **)&tcb))
{
IPCOM_LOG0(WARNING, "IPSCTP : no TCB");
IPNET_DATA_UNLOCK(sock->sctp_inst->tcb_lock);
return -IP_ERRNO_ENOTCONN;
}
/* release the lock if tcb is null */
if(tcb == IP_NULL)
{
IPNET_DATA_UNLOCK(sock->sctp_inst->tcb_lock);
}
for (buf_len = 0; buf_len <= 0; offset = - buf_len)
{
struct Ip_iovec *iov = &msg->msg_iov[i++];
#if 1
if (IP_UNLIKELY(IP_NULL == iov->iov_base))
return -IP_ERRNO_EFAULT;
#endif
buf = (Ip_u8 *) iov->iov_base + offset;
buf_len = (int) iov->iov_len - offset;
}
bytes_to_copy = 0;
do
{
int bytes_to_alloc = IP_MIN((int)mss, buf_len - bytes_to_copy);
if (ipsctp_usr_sock_sendbuf_add_and_return(sock, tcb, bytes_to_alloc)
< sock->send_max_bytes + bytes_to_alloc)
{
bytes_to_copy += bytes_to_alloc;
}
else
{
int lowat = 0;
int err;
ipsctp_usr_sock_sendbuf_sub(sock, tcb, bytes_to_alloc);
if (bytes_to_copy > 0 || non_blocking)
break;
lowat = IP_MAX(mss, inst->send_lowat);
lowat = IP_MIN(lowat, sock->send_max_bytes);
/* release the lock if tcb is non-null */
if(tcb != IP_NULL)
{
IPNET_DATA_UNLOCK(sock->sctp_inst->tcb_lock);
}
err = ipnet_usr_sock_wait_until_writable(sock,
lowat,
IP_NULL);
if (err != 0)
return -err; /* IP_ERRNO_EINTR */
tcb = IP_NULL;
IPNET_DATA_LOCK(sock->sctp_inst->tcb_lock);
if (sock->sctp_inst->get_tcb_info(sock, IP_NULL, sndrcv, &pmtu, &mss, (void **)&tcb))
{
IPNET_DATA_UNLOCK(sock->sctp_inst->tcb_lock);
return -IP_ERRNO_ENOTCONN;
}
if(tcb == IP_NULL)
{
IPNET_DATA_UNLOCK(sock->sctp_inst->tcb_lock);
}
if (sock->ipcom.so_errno)
{
#if 0
/* here, bytes_to_copy is 0. */
ipsctp_usr_sock_sendbuf_sub(sock, tcb, bytes_to_copy);
#endif
if(tcb != IP_NULL)
{
IPNET_DATA_UNLOCK(sock->sctp_inst->tcb_lock);
}
return -sock->ipcom.so_errno;
}
}
} while (bytes_to_copy < buf_len);
if(tcb != IP_NULL)
{
IPNET_DATA_UNLOCK(sock->sctp_inst->tcb_lock);
}
pkt_head = pkt_tail = IP_NULL;
while (bytes_to_copy > 0)
{
Ipcom_pkt *pkt = IP_NULL;
int seg_len = IP_MIN(bytes_to_copy, (int) ((mss & ~0x3) - sizeof(Ipsctp_data_chunk_hdr)));
pkt = ipcom_pkt_malloc(pmtu,
IP_FLAG_FC_STACKCONTEXT | (non_blocking ? 0 : IP_FLAG_FC_BLOCKOK));
if (IP_UNLIKELY(pkt == IP_NULL))
break;
/* make the start addr begin with 4*X; yes also padding it */
pkt->start = (pkt->maxlen - mss) & ~0x3;
pkt->end = pkt->start + (int)sizeof(Ipsctp_data_chunk_hdr) + seg_len;
ipcom_memcpy(&pkt->data[pkt->start + sizeof(Ipsctp_data_chunk_hdr)],
buf, seg_len);
#if 0
pkt->chk = ipnet_in_checksum_memcpy(&pkt->data[pkt->start],
buf,
seg_len);
#endif
/* Add this packet to the list of packets that will be sent
to ipnetd */
if (pkt_head == IP_NULL)
pkt_head = pkt;
else
pkt_tail->next = pkt;
pkt_tail = pkt;
len += seg_len;
buf += seg_len;
bytes_to_copy -= seg_len;
}
prslt->pkt = pkt_head;
return (len == 0 ? -IP_ERRNO_EWOULDBLOCK : len);
}
/*
*===========================================================================
* ipnet_sock_sctp_zombie_send_pkt_free
*===========================================================================
* Description: Free packet and remove TX socket buffer space allocated.
* This routine is called by ipcom_sendmsg() in rare cases
* when it has obtained a packet from
* ipnet_sock_usr_pkts_from_iov_sctp() but finds itself unable
* to send that packet.
* Parameters: sock - The socket from which TX space was allocated for pkt
* pkt - the packet to free.
* Returns: N/A
*
*/
IP_GLOBAL void
ipnet_sock_sctp_zombie_send_pkt_free(Ipnet_socket *sock,
IP_CONST struct Ip_msghdr *msg,
Ipcom_pkt *pkt)
{
Ipcom_pkt * next;
int len;
int totlen;
struct Ip_cmsghdr *cmsghdr = IP_NULL;
struct Ipsctp_sndrcvinfo *sndrcv = IP_NULL;
Ipsctp_instance *inst = IP_NULL;
Ip_u32 pmtu = 0;
Ip_u32 mss = 0;
void *tcb = IP_NULL;
ip_assert(IP_NULL != msg);
ip_assert(IP_NULL != sock);
inst = (Ipsctp_instance *)sock->sctp_inst;
ip_assert(IP_NULL != inst);
for (totlen = 0; pkt != IP_NULL; pkt = next)
{
next = pkt->next;
pkt->next = IP_NULL;
len = pkt->end - pkt->start;
ip_assert (len >= (int)sizeof(Ipsctp_data_chunk_hdr));
len = len - (int)sizeof(Ipsctp_data_chunk_hdr);
totlen += len;
ipcom_pkt_free (pkt);
}
/* loop the ancillary data, seach for what we needed */
for (cmsghdr = (struct Ip_cmsghdr *)IP_CMSG_FIRSTHDR(msg);
IP_NULL != cmsghdr;
cmsghdr = (struct Ip_cmsghdr *)IP_CMSG_NXTHDR(msg, cmsghdr))
{
/* check message level */
if (IP_IPPROTO_SCTP != cmsghdr->cmsg_level)
continue;
/* check message type */
if (IPSCTP_SNDRCV != cmsghdr->cmsg_type)
continue;
ip_assert (cmsghdr->cmsg_len < IP_CMSG_LEN(sizeof(*sndrcv)));
sndrcv = (struct Ipsctp_sndrcvinfo *)IP_CMSG_DATA(cmsghdr);
break;
}
IPNET_DATA_LOCK(sock->sctp_inst->tcb_lock);
if (sock->sctp_inst->get_tcb_info(sock, msg, sndrcv, &pmtu, &mss, (void **)&tcb))
{
IPNET_DATA_UNLOCK(sock->sctp_inst->tcb_lock);
return;
}
/* release the lock if tcb is null */
if (tcb == IP_NULL)
{
IPNET_DATA_UNLOCK(sock->sctp_inst->tcb_lock);
}
ipsctp_usr_sock_sendbuf_sub(sock, tcb, totlen);
if(tcb != IP_NULL)
{
IPNET_DATA_UNLOCK(sock->sctp_inst->tcb_lock);
}
return;
}
/*
*===========================================================================
* ipnet_usr_sock_sctp_recv
*===========================================================================
* Description: Receives data from a SCTP socket.
* Parameters: sock - The socket to receive data from.
* msg - Receive parameters.
* flags - IP_MSG_xxx flags.
* Returns: 0 = a packet was successfully received, -1 = error.
*
*/
IP_STATIC int
ipnet_usr_sock_sctp_recv(Ipnet_socket *sock,
struct Ip_msghdr *msg,
int flags)
{
int bytes = 0;
if (msg->msg_iovlen != 1)
{
/* if no buffer just return 0 */
if (0== msg->msg_iovlen)
return 0;
/* The scatter/gather buffers, or I/O vectors (pointed to by the msg_iov
* field) are treated as a single SCTP data chunk, rather than multiple
* chunks, for both sendmsg() and recvmsg().
*/
return -IP_ERRNO_EINVAL;
}
/* msg_waitall and msg_peek must not be IP_TRUE at the same time,
that combination is not supported by the current
implementation */
ip_assert(IP_BIT_ISFALSE(flags, IP_MSG_WAITALL)
|| IP_BIT_ISFALSE(flags, IP_MSG_PEEK));
if (IP_BIT_ISTRUE(flags, IP_MSG_OOB))
{
return -IP_ERRNO_EOPNOTSUPP;
}
#if 0
if (IP_BIT_ISTRUE(sock->sctp_inst->feature_flags, IPSCTP_INST_FLAG_EXPLICIT_EOR))
{
}
#endif
bytes = ipsctp_usr_recv(sock, msg, flags);
if (IP_UNLIKELY(bytes == -1))
{
return 0;
}
if ((bytes == 0) || (bytes == -IP_ERRNO_EWOULDBLOCK))
{
ipcom_atomic_inc(&sock->sctp_inst->wait_readable);
if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_NONBLOCKING))
return IPNET_ERRNO(EWOULDBLOCK);
else
return IPNET_ERRNO(EAGAIN);
}
return bytes;
}
#ifdef IPCOM_USE_INET6
/*
*===========================================================================
* ipnet_sock_sctp_ip6_lookup
*===========================================================================
* Description: Returns the IPv6 socket matching the parameters. Source is
* always this host and the destination is the peer.
* Parameters: vr - The virtual router the socket must be using.
* proto - The protocol the returned socket should be using.
* src_addr - The IPv6 source address in NETWORK byte order or
* the undefined address (all zeros) if all addresses should
* match. IP_NULL is will also be interpreted as the undefined
* address.
* local_port - The source port in HOST byte order or 0 if any
* source port should match.
* dst_addr - The IPv6 destination address in NETWORK byte order
* or the undefined address (all zeros) if all addresses should
* match. IP_NULL is will also be interpreted as the undefined
* address.
* peer_port - The destination port in HOST byte order or 0 if any
* destination port should match.
* Returns: A pointer to the matching socket or IP_NULL if no socket match.
*
*/
IP_STATIC Ipnet_socket *
ipnet_sock_sctp_ip6_lookup(Ip_u16 vr,
int proto,
IP_CONST void *local_addr_ptr,
Ip_u16 local_port,
IP_CONST void *peer_addr_ptr,
Ip_u16 peer_port)
{
Ipnet_socket *ret_sock = IP_NULL;
IPCOM_UNUSED_ARG(proto);
IPCOM_UNUSED_ARG(peer_port);
/* NOTE : don't support peer address */
if ((IP_NULL == local_addr_ptr) || (IP_NULL != peer_addr_ptr))
return IP_NULL;
/* get socket by LP+LA in ipnet_bind_check_reuse */
ret_sock = ipsctp_sock_lookup_sa(vr,
IP_AF_INET6,
local_addr_ptr,
local_port);
#ifdef IPCOM_USE_INET
if ((IP_NULL == ret_sock)
&& IP_IN6_IS_ADDR_V4MAPPED((struct Ip_in6_addr *)local_addr_ptr))
{
struct Ip_in_addr local_in_addr;
/* copy V4 address */
ipcom_memcpy((void *)&local_in_addr,
(void *)&((struct Ip_in6_addr *)local_addr_ptr)->in6.addr32[3],
4);
ret_sock = ipsctp_sock_lookup_sa(vr,
IP_AF_INET,
(void *)&local_in_addr,
local_port);
}
#endif /* IPCOM_USE_INET */
return ret_sock;
}
#endif /* IPCOM_USE_INET6 */
#ifdef IPCOM_USE_INET
/*
*===========================================================================
* ipnet_sock_sctp_ip4_lookup
*===========================================================================
* Description: Returns the IPv4 socket matching the parameters.
* Parameters: vr - The virtual router the socket must be bound to.
* proto - The protocol the returned socket should be using.
* local_addr_ptr - The IPv4 address in network order of the
* local address to match or IP_NULL if any address should match.
* local_port - The source port in HOST byte order or 0 if any
* source port should match.
* peer_addr_n - The IPv4 address in network order of the
* peer address to match or IP_NULL if any address should match.
* peer_port - The destination port in HOST byte order or 0 if any
* destination port should match.
* pfirst - [in] the socket descriptor to from where the scan should start
* [out] the socket descriptor to start the next search from.
* set to IP_NULL to start from 0.
* Returns: A pointer to the matching socket or IP_NULL if no socket match.
*
*/
IP_STATIC Ipnet_socket *
ipnet_sock_sctp_ip4_lookup(Ip_u16 vr,
int proto,
IP_CONST void *local_addr_ptr,
Ip_u16 local_port,
IP_CONST void *peer_addr_ptr,
Ip_u16 peer_port)
{
Ipnet_socket *ret_sock = IP_NULL;
IPCOM_UNUSED_ARG(proto);
IPCOM_UNUSED_ARG(peer_port);
/* NOTE : don't support peer address */
if ((IP_NULL == local_addr_ptr) || (IP_NULL != peer_addr_ptr))
return IP_NULL;
/* get socket by LP+LA in ipnet_bind_check_reuse */
ret_sock = ipsctp_sock_lookup_sa(vr,
IP_AF_INET,
local_addr_ptr,
local_port);
#ifdef IPCOM_USE_INET6
if (IP_NULL == ret_sock)
{
union Ip_in_addr_union local_addr;
struct Ip_in6_addr local_in6;
local_addr.in.s_addr = IP_GET_32ON16(local_addr_ptr);
ipnet_ip6_create_mapped_addr(&local_addr.in, &local_in6);
ret_sock = ipsctp_sock_lookup_sa(vr,
IP_AF_INET6,
&local_in6,
local_port);
if (IP_NULL == ret_sock)
return IP_NULL;
if (IP_BIT_ISSET(ret_sock->ip6->flags, IPNET_SOCKET_FLAG_IP6_V6ONLY))
return IP_NULL;
}
#endif /* IPCOM_USE_INET6 */
return ret_sock;
}
#endif /* IPCOM_USE_INET */
/*
*===========================================================================
* ipnet_sock_sctp_network_bind
*===========================================================================
* Description: The SCTP network layer bind for IPV4&IPV6.
* Parameters: sock - The socket to bind.
* name - The socket name.
* namelen - The length of the name variable.
* check_reuse - Check reuse for port or address.
* Returns: 0 = success, <0 = error code
*/
IP_STATIC int
ipnet_sock_sctp_network_bind(struct Ipnet_socket_struct *sock,
IP_CONST struct Ip_sockaddr *name,
Ip_socklen_t namelen,
Ip_bool check_reuse)
{
Ipnet_sock_bind_f net_bind = IP_NULL;
Ipnet_sock_sctp_ops *ops = IP_NULL;
if (sock == IP_NULL)
return -IP_ERRNO_EINVAL;
ops = (Ipnet_sock_sctp_ops *)ipnet_sock_get_ops(sock);
if ((IP_NULL == ops)||(IP_NULL == name)||(namelen <= 0))
return -IP_ERRNO_EINVAL;
switch (name->sa_family)
{
#ifdef IPCOM_USE_INET
case IP_AF_INET:
net_bind = ops->network_bind_v4;
break;
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
case IP_AF_INET6:
net_bind = ops->network_bind_v6;
break;
#endif /* IPCOM_USE_INET6 */
default:
ip_assert(IP_FALSE);
return -IP_ERRNO_EINVAL;
}
if (IP_NULL == net_bind)
return -IP_ERRNO_EINVAL;
return net_bind(sock, name, namelen, check_reuse);
}
/*
****************************************************************************
* 10 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_sock_sctp_register
*===========================================================================
* Description: Registers SCTP protocol socket support for IPv4 and/or IPv6.
* Parameters: N/A
* Returns: 0 = success, <0 = error code.
*
*/
IP_GLOBAL int
ipnet_sock_sctp_register(void)
{
#if defined(IPCOM_USE_INET) && defined(IPCOM_USE_INET6)
Ipnet_sock_bind_f bind_v4 = IP_NULL;
#endif
Ipnet_sock_sctp_ops *ops = IP_NULL;
int domains[] = {
#ifdef IPCOM_USE_INET
IP_AF_INET,
#endif
#ifdef IPCOM_USE_INET6
IP_AF_INET6,
#endif
-1
};
int types[] = {
#ifdef IPSCTP_INCLUDE_UDP_LIKE
IP_SOCK_DGRAM,
IP_SOCK_SEQPACKET,
#endif
IP_SOCK_STREAM,
-1
};
int *domain;
int *type;
for (domain = domains; *domain >= 0; domain++)
{
for (type = types; *type >=0; type++)
{
int ret;
ops = ipcom_calloc(1, sizeof(*ops));
if (IP_NULL == ops)
{
IPCOM_LOG0(CRIT, "Could not register SCP protocol, "
"out of memory");
IP_PANIC();
return -IP_ERRNO_ENOMEM;
}
switch (*domain)
{
#ifdef IPCOM_USE_INET
case IP_AF_INET:
ipnet_sock_ip4_get_ops(&ops->i);
ops->i.sock.lookup = ipnet_sock_sctp_ip4_lookup;
ops->network_bind_v4 = ops->i.network_bind;
#ifdef IPCOM_USE_INET6
/* 'bind_v4' will be used when IP_AF_INET6 is registered */
bind_v4 = ops->i.network_bind;
#endif
break;
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
case IP_AF_INET6:
ipnet_sock_ip6_get_ops(&ops->i);
ops->i.sock.lookup = ipnet_sock_sctp_ip6_lookup;
ops->network_bind_v6 = ops->i.network_bind;
#ifdef IPCOM_USE_INET
/* 'bind_v4' was set during initialization of IP_AF_INET */
ops->network_bind_v4 = bind_v4;
#endif /* IPCOM_USE_INET6 */
break;
#endif /* IPCOM_USE_INET6 */
default:
(void)ipcom_free(ops);
ip_assert(IP_FALSE);
continue;
}
ops->network_bind = ipnet_sock_sctp_network_bind;
ops->i.network_bind = ipnet_sock_sctp_bind;
ops->network_init = ops->i.sock.init;
ops->i.sock.init = ipnet_sock_sctp_init;
ops->network_destroy = ops->i.sock.destroy;
ops->i.sock.destroy = ipnet_sock_sctp_destroy;
ops->network_connect = ops->i.sock.connect;
ops->i.sock.connect = ipnet_sock_sctp_connect;
ops->i.sock.type = *type;
ops->i.sock.proto = IP_IPPROTO_SCTP;
ops->i.sock.accept = ipsctp_accept;
ops->i.sock.close = ipsctp_close;
ops->i.sock.listen = ipsctp_listen;
ops->i.sock.usr_recv = ipnet_usr_sock_sctp_recv;
ops->i.sock.pkts_from_iov = ipnet_sock_usr_pkts_from_iov_sctp;
ops->i.sock.send = ipsctp_output;
ops->i.sock.shutdown = ipsctp_shutdown;
ops->i.sock.getopt = ipsctp_getsockopt;
ops->i.sock.setopt = ipsctp_setsockopt;
ops->i.sock.getname = ipsctp_getname;
ops->i.sock.ioctl = ipsctp_ioctl;
ops->i.sock.hdr_space += IPSCTP_SIZE_SCTP_HDR;
ops->i.sock.extra_sock_space += IPSCTP_SIZE_SOCK_DATA;
ret = ipnet_sock_register_ops(&ops->i.sock);
if (ret < 0)
{
(void)ipcom_free(ops);
return ret;
}
}
}
return 0;
}
#endif