1028 lines
34 KiB
C
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
|