socksctp.c revision 769b977d34538d5f208de3a6178f04cb0876b7ca
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/socketvar.h>
#include <inet/sctp_itf.h>
#include "socksctp.h"
/*
* SCTP sockfs sonode operations, 1-1 socket
*/
static int sosctp_listen(struct sonode *, int);
int, int);
static int sosctp_getpeername(struct sonode *);
static int sosctp_getsockname(struct sonode *);
static int sosctp_shutdown(struct sonode *, int);
int);
static int sosctp_setsockopt(struct sonode *, int, int, const void *,
/*
* SCTP sockfs sonode operations, 1-N socket
*/
socklen_t, int, int);
/*
* Socket upcalls, 1-1 socket connection
*/
static void sctp_sock_connected(void *handle);
static void sctp_sock_disconnecting(void *handle);
/*
* Socket association upcalls, 1-N socket connection
*/
static void sctp_assoc_connected(void *handle);
static void sctp_assoc_disconnecting(void *handle);
static kmem_cache_t *sosctp_sockcache;
sosctp_accept, /* sop_accept */
sosctp_bind, /* sop_bind */
sosctp_listen, /* sop_listen */
sosctp_connect, /* sop_connect */
sosctp_recvmsg, /* sop_recvmsg */
sosctp_sendmsg, /* sop_sendmsg */
sosctp_getpeername, /* sop_getpeername */
sosctp_getsockname, /* sop_getsockname */
sosctp_shutdown, /* sop_shutdown */
sosctp_getsockopt, /* sop_getsockopt */
sosctp_setsockopt /* sop_setsockopt */
};
sosctp_accept, /* sop_accept */
sosctp_bind, /* sop_bind */
sosctp_listen, /* sop_listen */
sosctp_seq_connect, /* sop_connect */
sosctp_recvmsg, /* sop_recvmsg */
sosctp_seq_sendmsg, /* sop_sendmsg */
sosctp_getpeername, /* sop_getpeername */
sosctp_getsockname, /* sop_getsockname */
sosctp_shutdown, /* sop_shutdown */
sosctp_getsockopt, /* sop_getsockopt */
sosctp_setsockopt /* sop_setsockopt */
};
};
};
/*ARGSUSED*/
static int
{
so->so_nl7c_flags = 0;
return (-1);
}
return (0);
}
/*ARGSUSED*/
static void
{
}
int
sosctp_init(void)
{
int error;
if (error != 0) {
"sosctp_init: bad vnode ops template");
return (error);
}
sizeof (struct sctp_sonode), 0, sosctp_sock_constructor,
return (0);
}
static struct vnode *
int kmflags)
{
struct sctp_sonode *ss;
return (NULL);
}
now = gethrestime_sec();
so->so_pushcnt = 0;
so->so_options = 0;
so->so_delayed_error = 0;
so->so_oobsigcnt = 0;
so->so_delayed_error = 0;
ss->ss_maxassoc = 0;
ss->ss_assoccnt = 0;
if (type == SOCK_STREAM) {
} else {
}
ss->ss_rxqueued = 0;
ss->ss_txqueued = 0;
return (vp);
}
/*
* Creates a sctp socket data structure.
* tso is non-NULL if it's passive open.
*/
struct sonode *
{
int error;
int soflags;
if (version == SOV_STREAM) {
return (NULL);
}
/*
* We only support two types of SCTP socket. Let sotpi_create()
* handle all other cases, such as raw socket.
*/
}
} else {
/*
* sosctp_makevp() only fails when there is no memory.
*/
return (NULL);
}
}
return (NULL);
}
if (version == SOV_DEFAULT) {
}
return (so);
}
/*
* Free SCTP socket data structure.
* Closes incoming connections which were never accepted, frees
* resources.
*/
void
{
/*
* Need to clear these out so that sockfree() doesn't think that
* there's memory in need of free'ing.
*/
}
}
}
}
/*
* Accept incoming connection.
*/
static int
{
int error = 0;
/*
* Not a listen socket.
*/
return (EINVAL);
}
/*
* Cannot accept() connections from SOCK_SEQPACKET type
* socket.
*/
return (EOPNOTSUPP);
}
/*
* Returns right away if socket is nonblocking.
*/
if (error != 0) {
return (error);
}
/*
* accept() needs remote address right away.
*/
(void) sosctp_getpeername(nso);
return (0);
}
/*
* Bind local endpoint.
*/
int
int flags)
{
int error = 0;
if (!(flags & _SOBIND_LOCK_HELD)) {
/* LINTED - statement has no conseq */
} else {
}
/*
* Multiple binds not allowed for any SCTP socket.
* Also binding with null address is not supported.
*/
goto done;
}
/*
*/
goto done;
}
/*
* Protocol module does address family checks.
*/
if (error == 0) {
/* LINTED - statement has no conseq */
} else {
}
done:
if (!(flags & _SOBIND_LOCK_HELD)) {
/* LINTED - statement has no conseq */
} else {
/* If the caller held the lock don't release it here */
}
return (error);
}
/*
* Turn socket into a listen socket.
*/
static int
{
int error = 0;
/*
* If this socket is trying to do connect, or if it has
* been connected, disallow.
*/
goto done;
}
if (backlog < 0) {
backlog = 0;
}
/*
* If listen() is only called to change backlog, we don't
* need to notify protocol module.
*/
goto done;
}
if (error == 0) {
/* LINTED - statement has no conseq */
} else {
}
done:
return (error);
}
/*
* Active open.
*/
static int
{
int error;
/*
* Can't connect() after listen(), or if the socket is already
* connected.
*/
} else {
error = EOPNOTSUPP;
}
goto done;
}
/*
* Check for failure of an earlier call
*/
goto done;
}
/*
* Connection is closing, or closed, don't allow reconnect.
* TCP allows this to proceed, but the socket remains unwriteable.
* BSD returns EINVAL.
*/
SS_CANTSENDMORE)) {
goto done;
}
goto done;
}
if (error == 0) {
/*
* Allow other threads to access the socket
*/
}
switch (error) {
case 0:
case EINPROGRESS:
case EALREADY:
case EINTR:
/* Non-fatal errors */
break;
case EHOSTUNREACH:
if (flags & _SOCONNECT_XPG4_2) {
/*
* ENETUNREACH be returned but does not require
* EHOSTUNREACH. In order to keep the test suite
* happy we mess with the errno here.
*/
error = ENETUNREACH;
}
/* FALLTHRU */
default:
/* clear SS_ISCONNECTING in case it was set */
break;
}
done:
return (error);
}
/*
* Active open for 1-N sockets, create a new association and
* call connect on that.
* If there parent hasn't been bound yet (this is the first association),
* make it so.
*/
static int
{
struct sctp_soassoc *ssa;
struct sctp_sonode *ss;
int error;
goto done;
}
&ssa);
if (error != 0) {
error = ENETUNREACH;
}
}
}
done:
return (error);
}
/*
* Receive data.
*/
int
{
struct T_unitdata_ind *tind;
void *opt;
SS_CANTRCVMORE))) {
return (ENOTCONN);
}
} else {
/* For 1-N socket, recv() cannot be used. */
if (msg->msg_namelen == 0)
return (EOPNOTSUPP);
/*
* If there are no associations, and no new connections are
* coming in, there's not going to be new messages coming
* in either.
*/
return (ENOTCONN);
}
}
/*
* out-of-band data not supported.
*/
return (EOPNOTSUPP);
}
/*
* flag possibilities:
*
* MSG_PEEK Don't consume data
* MSG_WAITALL Wait for full quantity of data (ignored if MSG_PEEK)
* MSG_DONTWAIT Non-blocking (same as FNDELAY | FNONBLOCK)
*
* MSG_WAITALL can return less than the full buffer if either
*
* 1. we would block and we are non-blocking
* 2. a full message cannot be delivered
*
* Given that we always get a full message from proto below,
* MSG_WAITALL is not meaningful.
*/
/*
* Allow just one reader at a time.
*/
if (error) {
return (error);
}
}
} else {
msg->msg_namelen = 0;
}
if (msg->msg_controllen == 0) {
if (len > 0) {
}
} else if (len > 0) {
} else {
msg->msg_controllen = 0;
}
}
/*
* We will re-read this message the next time.
*/
if (error != 0) {
if (msg->msg_namelen > 0) {
msg->msg_namelen);
}
if (msg->msg_controllen > 0) {
}
return (error);
}
#ifdef DEBUG
#endif
/* Finished with this message? */
break;
/*
* Remove the bits that have been read, the
* next read will start from where we left
* off.
*/
#ifdef DEBUG
#endif
}
#ifdef DEBUG
#endif
else
}
goto done;
}
}
/*
* Consume this message
*/
}
}
} else {
/*
* No pending data. Return right away for nonblocking
* socket, otherwise sleep waiting for data.
*/
(flags & MSG_DONTWAIT)) {
error = EWOULDBLOCK;
} else {
} else {
goto again;
}
}
} else {
msg->msg_controllen = 0;
msg->msg_namelen = 0;
}
}
done:
/*
* Determine if we need to update SCTP about the buffer
* space. For performance reason, we cannot update SCTP
* every time a message is read. The socket buffer low
* watermark is used as the threshold.
*/
(ss->ss_rxqueued == 0))) {
/*
* If amount of queued data is higher than watermark,
* updata SCTP's idea of available buffer space.
*/
}
} else {
if (readcnt > 0 &&
(ssa->ssa_rxqueued == 0))) {
/*
* If amount of queued data is higher than watermark,
* updata SCTP's idea of available buffer space.
*/
}
if (consumed) {
}
}
return (error);
}
int
{
int error;
/*
* Loop until we have all data copied into mblk's.
*/
while (count > 0) {
/*
* As a message can be splitted up and sent in different
* packets, each mblk will have the extra space before
* data to accomodate what SCTP wants to put in there.
*/
(flags & MSG_DONTWAIT)) {
return (EAGAIN);
}
return (error);
}
}
if (error != 0) {
return (error);
}
}
return (0);
}
/*
* Send message.
*/
static int
{
struct sctp_sndrcvinfo *sinfo;
int error;
/*
* No out-of-band data support.
*/
return (EOPNOTSUPP);
}
if (msg->msg_controllen != 0) {
return (EINVAL);
}
/* Both flags should not be set together. */
return (EINVAL);
}
/* Initiate a graceful shutdown. */
/* Can't include data in MSG_EOF message. */
return (EINVAL);
}
/*
* This is the same sequence as done in
* shutdown(SHUT_WR).
*/
return (error);
}
}
} else {
optlen = 0;
}
for (;;) {
return (EPIPE);
}
return (error);
}
break;
/*
* Xmit window full in a blocking socket.
*/
(flags & MSG_DONTWAIT)) {
return (EAGAIN);
} else {
/*
* Wait for space to become available and try again.
*/
if (!error) { /* signal */
return (EINTR);
}
}
}
/* Don't allow sending a message larger than the send buffer size. */
return (EMSGSIZE);
}
/*
* Update TX buffer usage here so that we can lift the socket lock.
*/
/*
* Allow piggybacking data on handshake messages (SS_ISCONNECTING).
*/
/*
* We need to check here for listener so that the
* same error will be returned as with a TCP socket.
* In this case, sosctp_connect() returns EOPNOTSUPP
* while a TCP socket returns ENOTCONN instead. Catch it
* here to have the same behavior as a TCP socket.
*/
goto error_nofree;
}
if (flags & MSG_DONTWAIT) {
}
if (error) {
/*
* Check for non-fatal errors, socket connected
* while the lock had been lifted.
*/
goto error_nofree;
}
error = 0;
}
} else {
}
goto error_nofree;
}
/* Copy in the message. */
goto error_ret;
}
if (error == 0)
return (0);
/*
* We received shutdown between the time lock was
* lifted and call to sctp_sendmsg().
*/
return (EPIPE);
}
return (error);
}
/*
* Send message on 1-N socket. Connects automatically if there is
* no association.
*/
static int
{
struct sctp_sonode *ss;
struct sctp_soassoc *ssa;
struct sctp_sndrcvinfo *sinfo;
int aid = 0;
int error;
/*
* There shouldn't be problems with alignment, as the memory for
* msg_control was alloced with kmem_alloc.
*/
return (EINVAL);
}
}
if (msg->msg_controllen > 0) {
} else {
optlen = 0;
}
/*
* If there is no association id, connect to address specified
* in msg_name. Otherwise look up the association using the id.
*/
if (aid == 0) {
/*
* Connect and shutdown cannot be done together, so check for
* MSG_EOF.
*/
goto done;
}
}
if (error) {
(error == EHOSTUNREACH)) {
error = ENETUNREACH;
}
/*
* Fatal error during connect(). Bail out.
* If ssa exists, it means that the handshake
* is in progress.
*/
goto done;
}
/*
* All the errors are non-fatal ones, don't return
* e.g. EINPROGRESS from sendmsg().
*/
error = 0;
}
} else {
goto done;
}
}
/*
* Now we have an association.
*/
/*
* MSG_EOF initiates graceful shutdown.
*/
/*
* Can't include data in MSG_EOF message.
*/
} else {
}
goto refrele;
}
for (;;) {
return (EPIPE);
}
goto refrele;
}
break;
(flags & MSG_DONTWAIT)) {
goto refrele;
} else {
/*
* Wait for space to become available and try again.
*/
if (!error) { /* signal */
goto refrele;
}
}
}
/* Don't allow sending a message larger than the send buffer size. */
goto refrele;
}
/*
* Update TX buffer usage here so that we can lift the socket lock.
*/
goto lock_rele;
}
/* Copy in the message. */
goto lock_rele;
}
if (error != 0) {
/*
* We received shutdown between the time lock was
* lifted and call to sctp_sendmsg().
*/
return (EPIPE);
}
}
done:
return (error);
}
/*
* Get address of remote node.
*/
static int
{
int error;
/*
* SEQPACKET can have multiple end-points.
*/
return (EOPNOTSUPP);
}
} else {
&so->so_faddr_len);
}
return (error);
}
/*
* Get local address.
*/
static int
{
int error;
/*
* Zero address, except for address family
*/
sizeof (struct sockaddr_in6) : sizeof (struct sockaddr_in);
error = 0;
} else {
&so->so_laddr_len);
}
return (error);
}
/*
* Called from shutdown().
*/
static int
{
int error = 0;
short wakesig = 0;
return (EOPNOTSUPP);
}
/*
* SunOS 4.X has no check for datagram sockets.
* 5.X checks that it is connected (ENOTCONN)
*/
goto done;
}
/*
* Record the current state and then perform any state changes.
* Then use the difference between the old and new states to
* determine which needs to be done.
*/
switch (how) {
case SHUT_RD:
break;
case SHUT_WR:
break;
case SHUT_RDWR:
break;
default:
goto done;
}
if (state_change & SS_CANTRCVMORE) {
}
}
if (state_change & SS_CANTSENDMORE) {
}
if (state_change & SS_CANTSENDMORE) {
}
done:
/*
* HACK: sctp_disconnect() may return EWOULDBLOCK. But this error is
* not documented in standard socket API. Catch it here.
*/
if (error == EWOULDBLOCK)
error = 0;
return (error);
}
/*
* Get socket options.
*/
/*ARGSUSED5*/
static int
{
int error = 0;
if (level == SOL_SOCKET) {
switch (option_name) {
case SO_TYPE:
case SO_ERROR:
case SO_DEBUG:
case SO_ACCEPTCONN:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_USELOOPBACK:
case SO_OOBINLINE:
case SO_SNDBUF:
case SO_RCVBUF:
case SO_SNDLOWAT:
case SO_RCVLOWAT:
case SO_DGRAM_ERRIND:
goto done;
}
break;
case SO_LINGER:
goto done;
}
break;
}
switch (option_name) {
case SO_TYPE:
goto copyout;
case SO_ERROR:
goto copyout;
case SO_ACCEPTCONN:
SO_ACCEPTCONN : 0;
goto copyout;
case SO_DEBUG:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_USELOOPBACK:
case SO_OOBINLINE:
case SO_DGRAM_ERRIND:
goto copyout;
/*
* The following options are only returned by sockfs
* when sctp_get_opt() fails.
*/
case SO_LINGER:
break;
case SO_SNDBUF:
len = (t_uscalar_t)sizeof (int);
goto copyout;
case SO_RCVBUF:
len = (t_uscalar_t)sizeof (int);
goto copyout;
case SO_SNDLOWAT:
len = (t_uscalar_t)sizeof (int);
goto copyout;
case SO_RCVLOWAT:
len = (t_uscalar_t)sizeof (int);
goto copyout;
default:
break;
}
}
if (level == IPPROTO_SCTP) {
/*
* Should go through ioctl().
*/
goto done;
}
}
/*
* If the resulting optlen is greater than the provided maxlen, then
* we sliently trucate.
*/
if (error != 0) {
/* We have no fallback value */
goto free;
}
error = 0;
goto copyout;
}
free:
}
done:
return (error);
}
/*
* Set socket options
*/
static int
{
return (EINVAL);
}
error = EOPNOTSUPP;
return (error);
}
/* Caller allocates aligned optval, or passes null */
/* No SCTP options should be zero-length */
if (optlen == 0) {
return (error);
}
/*
* For some SCTP level options, one can select the association this
* applies to.
*/
} else {
/*
* SOCK_SEQPACKET only
*/
id = 0;
if (level == IPPROTO_SCTP) {
switch (option_name) {
case SCTP_RTOINFO:
case SCTP_ASSOCINFO:
case SCTP_PRIMARY_ADDR:
case SCTP_PEER_ADDR_PARAMS:
/*
* Association ID is the first element
* params struct
*/
if (optlen < sizeof (sctp_assoc_t)) {
goto done;
}
break;
case SCTP_DEFAULT_SEND_PARAM:
if (optlen != sizeof (struct sctp_sndrcvinfo)) {
goto done;
}
id = ((struct sctp_sndrcvinfo *)
break;
case SCTP_INITMSG:
/*
* Only applies to future associations
*/
break;
default:
break;
}
} else if (level == SOL_SOCKET) {
if (option_name == SO_LINGER) {
error = EOPNOTSUPP;
goto done;
}
/*
* These 2 options are applied to all associations.
* The other socket level options are only applied
* to the socket (not associations).
*/
if ((option_name != SO_RCVBUF) &&
(option_name != SO_SNDBUF)) {
}
} else {
}
/*
* If association ID was specified, do op on that assoc.
* Otherwise set the default setting of a socket.
*/
if (id != 0) {
goto done;
}
}
}
} else {
/*
* 1-N socket, and we have to apply the operation to ALL
* associations. Like with anything of this sort, the
* problem is what to do if the operation fails.
* Just try to apply the setting to everyone, but store
* error number if someone returns such.
*/
optlen);
error = 0;
continue;
}
if (error == 0) {
}
}
}
/*
* Check for SOL_SOCKET options and record their values.
* If we know about a SOL_SOCKET parameter and the transport
* failed it with TBADOPT or TOUTSTATE (i.e. ENOPROTOOPT or
* EPROTO) we let the setsockopt succeed.
*/
if (level == SOL_SOCKET) {
/* Check parameters */
switch (option_name) {
case SO_DEBUG:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_USELOOPBACK:
case SO_OOBINLINE:
case SO_SNDBUF:
case SO_RCVBUF:
case SO_SNDLOWAT:
case SO_RCVLOWAT:
case SO_DGRAM_ERRIND:
goto done;
}
break;
case SO_LINGER:
goto done;
}
break;
}
switch (option_name) {
case SO_TYPE:
case SO_ERROR:
case SO_ACCEPTCONN:
/* Can't be set */
error = ENOPROTOOPT;
goto done;
case SO_LINGER: {
if (l->l_onoff) {
} else {
}
break;
}
case SO_DEBUG:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_USELOOPBACK:
case SO_OOBINLINE:
case SO_DGRAM_ERRIND:
if (intvalue != 0) {
("sosctp_setsockopt: setting 0x%x\n",
option_name));
} else {
("sosctp_setsockopt: clearing 0x%x\n",
option_name));
}
break;
/*
* The following options are only returned by us when
* the sctp_set_opt fails.
* XXX XPG 4.2 applications retrieve SO_RCVBUF from
* sockfs since the transport might adjust the value
* and not return exactly what was set by the
* application.
*/
case SO_SNDBUF:
}
break;
case SO_RCVBUF:
}
break;
case SO_SNDLOWAT:
}
break;
case SO_RCVLOWAT:
}
break;
}
if (error != 0) {
("sosctp_setsockopt: ignoring error %d "
error = 0;
}
}
}
done:
return (error);
}
/*
* Upcalls from SCTP
*/
/*
* Incoming connection on listen socket.
*/
static void *
{
struct sctp_sonode *nss;
int error;
/*
* Check current # of queued conns against backlog
*/
return (NULL);
}
/*
* Need to create a new socket.
*/
return (NULL);
}
return (NULL);
}
/*
* Inherit socket properties
*/
++lss->ss_rxqueued;
/*
* Copy pointer to new socket to connind queue message
*/
/*
* Wake people who're waiting incoming conns. Note that
* soqueueconnind gets so_lock.
*/
return (nss);
}
/*
* This is the upcall function for 1-N (SOCK_SEQPACKET) socket when a new
* association is created. Note that the first argument (handle) is of type
* sctp_sonode *, which is the one changed to a listener for new
* associations. All the other upcalls for 1-N socket take sctp_soassoc *
* as handle. The only exception is the su_properties upcall, which
* can take both types as handle.
*/
static void *
{
struct sctp_soassoc *ssa;
/*
* Array not large enough; increase size.
*/
return (NULL);
}
}
/*
* Create soassoc for this connection
*/
return (NULL);
}
++lss->ss_assoccnt;
return (ssa);
}
/*
* For outgoing connections, the connection has been established.
*/
static void
sctp_sock_connected(void *handle)
{
/*
* Wake ones who're waiting for conn to become established.
*/
}
static void
sctp_assoc_connected(void *handle)
{
}
/*
* Connection got disconnected. Either with an error, or through
* normal handshake.
* Note that there is no half-closed conn, like TCP.
*/
static int
{
int event = 0;
/*
* Connection is gone, wake everybody.
*/
}
/*
* If socket is already disconnected/disconnecting,
* don't (re)send signal.
*/
event |= SCTPSIG_READ;
event |= SCTPSIG_WRITE;
if (event != 0)
return (0);
}
static int
{
int ret;
ret = 1;
} else {
ret = 0;
}
return (ret);
}
/*
* Peer sent a shutdown. After this point writes are not allowed
* to this socket, but one might still receive notifications
* (e.g. for data which never got sent).
*/
static void
sctp_sock_disconnecting(void *handle)
{
/*
* Socket not writeable anymore. Wake writers, and ones
* who're waiting on socket state change
*/
/*
* If socket already un-writeable, don't (re)send signal.
*/
}
}
static void
sctp_assoc_disconnecting(void *handle)
{
}
/*
* Incoming data.
*/
static int
{
int msglen;
union T_primitives *tpr;
#endif
/*
* Should be getting T_unitdata_req's only.
* Must have address as part of packet.
*/
#endif
/*
* First mblk has only unitdata_req
*/
}
}
/*
* Override b_flag for SCTP sockfs internal use
*/
}
static int
{
struct T_unitdata_ind *tind;
int msglen;
union sctp_notification *sn;
struct sctp_sndrcvinfo *sinfo;
/*
* Should be getting T_unitdata_req's only.
* Must have address as part of packet.
*/
/*
* First mblk has only unitdata_req
*/
/*
* Override b_flag for SCTP sockfs internal use
*/
/*
* For notify messages, need to fill in association id.
* For data messages, sndrcvinfo could be in ancillary data.
*/
if (flags & SCTP_NOTIFICATION) {
case SCTP_ASSOC_CHANGE:
break;
case SCTP_PEER_ADDR_CHANGE:
break;
case SCTP_REMOTE_ERROR:
break;
case SCTP_SEND_FAILED:
break;
case SCTP_SHUTDOWN_EVENT:
break;
case SCTP_ADAPTION_INDICATION:
break;
break;
default:
ASSERT(0);
break;
}
} else {
if (tind->OPT_length > 0) {
char *cend;
for (;;) {
break;
}
sinfo = (struct sctp_sndrcvinfo *)
(cmsg + 1);
break;
}
} else {
break;
}
}
}
}
/*
* SCTP has reserved space in the header for storing a pointer.
* Put the pointer to assocation there, and queue the data.
*/
}
}
/*
* TX queued data got acknowledged. Frees up space in TX queue.
*/
static void
{
} else {
}
/*
* Wake blocked writers.
*/
/*
* Only do pollwakeup if the amount of queued data is less than
* watermark, and the socket wasn't writeable before.
*/
} else {
}
}
static void
{
/*
* Wake blocked writers.
*/
}
/*
* SCTP notifies socket about write offset and amount of TX data per mblk.
*/
static void
{
/*
* Only change them if they're set.
*/
if (wroff != 0) {
}
if (maxblk != 0) {
}
}
static void
{
struct sctp_sonode *ss;
/*
* Only change them if they're set.
*/
if (wroff != 0) {
}
if (maxblk != 0) {
}
} else {
if (wroff != 0) {
}
if (maxblk != 0) {
}
}
}