| /* |
| * 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 |
| * or http://www.opensolaris.org/os/licensing. |
| * 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 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/t_lock.h> |
| #include <sys/param.h> |
| #include <sys/systm.h> |
| #include <sys/buf.h> |
| #include <sys/vfs.h> |
| #include <sys/vnode.h> |
| #include <sys/debug.h> |
| #include <sys/errno.h> |
| #include <sys/stropts.h> |
| #include <sys/cmn_err.h> |
| #include <sys/sysmacros.h> |
| #include <sys/filio.h> |
| |
| #include <sys/project.h> |
| #include <sys/tihdr.h> |
| #include <sys/strsubr.h> |
| #include <sys/esunddi.h> |
| #include <sys/ddi.h> |
| |
| #include <sys/sockio.h> |
| #include <sys/socket.h> |
| #include <sys/socketvar.h> |
| #include <sys/strsun.h> |
| |
| #include <netinet/sctp.h> |
| #include <inet/sctp_itf.h> |
| #include <fs/sockfs/sockcommon.h> |
| #include "socksctp.h" |
| |
| /* |
| * SCTP sockfs sonode operations, 1-1 socket |
| */ |
| static int sosctp_init(struct sonode *, struct sonode *, struct cred *, int); |
| static int sosctp_accept(struct sonode *, int, struct cred *, struct sonode **); |
| static int sosctp_bind(struct sonode *, struct sockaddr *, socklen_t, int, |
| struct cred *); |
| static int sosctp_listen(struct sonode *, int, struct cred *); |
| static int sosctp_connect(struct sonode *, const struct sockaddr *, socklen_t, |
| int, int, struct cred *); |
| static int sosctp_recvmsg(struct sonode *, struct nmsghdr *, struct uio *, |
| struct cred *); |
| static int sosctp_sendmsg(struct sonode *, struct nmsghdr *, struct uio *, |
| struct cred *); |
| static int sosctp_getpeername(struct sonode *, struct sockaddr *, socklen_t *, |
| boolean_t, struct cred *); |
| static int sosctp_getsockname(struct sonode *, struct sockaddr *, socklen_t *, |
| struct cred *); |
| static int sosctp_shutdown(struct sonode *, int, struct cred *); |
| static int sosctp_getsockopt(struct sonode *, int, int, void *, socklen_t *, |
| int, struct cred *); |
| static int sosctp_setsockopt(struct sonode *, int, int, const void *, |
| socklen_t, struct cred *); |
| static int sosctp_ioctl(struct sonode *, int, intptr_t, int, struct cred *, |
| int32_t *); |
| static int sosctp_close(struct sonode *, int, struct cred *); |
| void sosctp_fini(struct sonode *, struct cred *); |
| |
| /* |
| * SCTP sockfs sonode operations, 1-N socket |
| */ |
| static int sosctp_seq_connect(struct sonode *, const struct sockaddr *, |
| socklen_t, int, int, struct cred *); |
| static int sosctp_seq_sendmsg(struct sonode *, struct nmsghdr *, struct uio *, |
| struct cred *); |
| |
| /* |
| * Socket association upcalls, 1-N socket connection |
| */ |
| sock_upper_handle_t sctp_assoc_newconn(sock_upper_handle_t, |
| sock_lower_handle_t, sock_downcalls_t *, struct cred *, pid_t, |
| sock_upcalls_t **); |
| static void sctp_assoc_connected(sock_upper_handle_t, sock_connid_t, |
| struct cred *, pid_t); |
| static int sctp_assoc_disconnected(sock_upper_handle_t, sock_connid_t, int); |
| static void sctp_assoc_disconnecting(sock_upper_handle_t, sock_opctl_action_t, |
| uintptr_t arg); |
| static ssize_t sctp_assoc_recv(sock_upper_handle_t, mblk_t *, size_t, int, |
| int *, boolean_t *); |
| static void sctp_assoc_xmitted(sock_upper_handle_t, boolean_t); |
| static void sctp_assoc_properties(sock_upper_handle_t, |
| struct sock_proto_props *); |
| |
| sonodeops_t sosctp_sonodeops = { |
| sosctp_init, /* sop_init */ |
| sosctp_accept, /* sop_accept */ |
| sosctp_bind, /* sop_bind */ |
| sosctp_listen, /* sop_listen */ |
| sosctp_connect, /* sop_connect */ |
| sosctp_recvmsg, /* sop_recvmsg */ |
| sosctp_sendmsg, /* sop_sendmsg */ |
| so_sendmblk_notsupp, /* sop_sendmblk */ |
| sosctp_getpeername, /* sop_getpeername */ |
| sosctp_getsockname, /* sop_getsockname */ |
| sosctp_shutdown, /* sop_shutdown */ |
| sosctp_getsockopt, /* sop_getsockopt */ |
| sosctp_setsockopt, /* sop_setsockopt */ |
| sosctp_ioctl, /* sop_ioctl */ |
| so_poll, /* sop_poll */ |
| sosctp_close, /* sop_close */ |
| }; |
| |
| sonodeops_t sosctp_seq_sonodeops = { |
| sosctp_init, /* sop_init */ |
| so_accept_notsupp, /* sop_accept */ |
| sosctp_bind, /* sop_bind */ |
| sosctp_listen, /* sop_listen */ |
| sosctp_seq_connect, /* sop_connect */ |
| sosctp_recvmsg, /* sop_recvmsg */ |
| sosctp_seq_sendmsg, /* sop_sendmsg */ |
| so_sendmblk_notsupp, /* sop_sendmblk */ |
| so_getpeername_notsupp, /* sop_getpeername */ |
| sosctp_getsockname, /* sop_getsockname */ |
| so_shutdown_notsupp, /* sop_shutdown */ |
| sosctp_getsockopt, /* sop_getsockopt */ |
| sosctp_setsockopt, /* sop_setsockopt */ |
| sosctp_ioctl, /* sop_ioctl */ |
| so_poll, /* sop_poll */ |
| sosctp_close, /* sop_close */ |
| }; |
| |
| sock_upcalls_t sosctp_sock_upcalls = { |
| so_newconn, |
| so_connected, |
| so_disconnected, |
| so_opctl, |
| so_queue_msg, |
| so_set_prop, |
| so_txq_full, |
| NULL, /* su_signal_oob */ |
| }; |
| |
| sock_upcalls_t sosctp_assoc_upcalls = { |
| sctp_assoc_newconn, |
| sctp_assoc_connected, |
| sctp_assoc_disconnected, |
| sctp_assoc_disconnecting, |
| sctp_assoc_recv, |
| sctp_assoc_properties, |
| sctp_assoc_xmitted, |
| NULL, /* su_recv_space */ |
| NULL, /* su_signal_oob */ |
| }; |
| |
| /* ARGSUSED */ |
| static int |
| sosctp_init(struct sonode *so, struct sonode *pso, struct cred *cr, int flags) |
| { |
| struct sctp_sonode *ss; |
| struct sctp_sonode *pss; |
| sctp_sockbuf_limits_t sbl; |
| sock_upcalls_t *upcalls; |
| |
| ss = SOTOSSO(so); |
| |
| if (pso != NULL) { |
| /* |
| * Passive open, just inherit settings from parent. We should |
| * not end up here for SOCK_SEQPACKET type sockets, since no |
| * new sonode is created in that case. |
| */ |
| ASSERT(so->so_type == SOCK_STREAM); |
| pss = SOTOSSO(pso); |
| |
| mutex_enter(&pso->so_lock); |
| so->so_state |= (SS_ISBOUND | SS_ISCONNECTED | |
| (pso->so_state & SS_ASYNC)); |
| sosctp_so_inherit(pss, ss); |
| so->so_proto_props = pso->so_proto_props; |
| so->so_mode = pso->so_mode; |
| mutex_exit(&pso->so_lock); |
| |
| return (0); |
| } |
| |
| if (so->so_type == SOCK_STREAM) { |
| upcalls = &sosctp_sock_upcalls; |
| so->so_mode = SM_CONNREQUIRED; |
| } else { |
| ASSERT(so->so_type == SOCK_SEQPACKET); |
| upcalls = &sosctp_assoc_upcalls; |
| } |
| so->so_proto_handle = (sock_lower_handle_t)sctp_create(so, NULL, |
| so->so_family, SCTP_CAN_BLOCK, upcalls, &sbl, cr); |
| if (so->so_proto_handle == NULL) |
| return (ENOMEM); |
| |
| so->so_rcvbuf = sbl.sbl_rxbuf; |
| so->so_rcvlowat = sbl.sbl_rxlowat; |
| so->so_sndbuf = sbl.sbl_txbuf; |
| so->so_sndlowat = sbl.sbl_txlowat; |
| |
| return (0); |
| } |
| |
| /* |
| * Accept incoming connection. |
| */ |
| /*ARGSUSED*/ |
| static int |
| sosctp_accept(struct sonode *so, int fflag, struct cred *cr, |
| struct sonode **nsop) |
| { |
| int error = 0; |
| |
| if ((so->so_state & SS_ACCEPTCONN) == 0) |
| return (EINVAL); |
| |
| error = so_acceptq_dequeue(so, (fflag & (FNONBLOCK|FNDELAY)), nsop); |
| |
| return (error); |
| } |
| |
| /* |
| * Bind local endpoint. |
| */ |
| /*ARGSUSED*/ |
| static int |
| sosctp_bind(struct sonode *so, struct sockaddr *name, socklen_t namelen, |
| int flags, struct cred *cr) |
| { |
| int error; |
| |
| if (!(flags & _SOBIND_LOCK_HELD)) { |
| mutex_enter(&so->so_lock); |
| so_lock_single(so); /* Set SOLOCKED */ |
| } else { |
| ASSERT(MUTEX_HELD(&so->so_lock)); |
| } |
| |
| /* |
| * X/Open requires this check |
| */ |
| if (so->so_state & SS_CANTSENDMORE) { |
| error = EINVAL; |
| goto done; |
| } |
| |
| |
| /* |
| * Protocol module does address family checks. |
| */ |
| mutex_exit(&so->so_lock); |
| |
| error = sctp_bind((struct sctp_s *)so->so_proto_handle, name, namelen); |
| |
| mutex_enter(&so->so_lock); |
| if (error == 0) { |
| so->so_state |= SS_ISBOUND; |
| } else { |
| eprintsoline(so, error); |
| } |
| done: |
| if (!(flags & _SOBIND_LOCK_HELD)) { |
| so_unlock_single(so, SOLOCKED); |
| mutex_exit(&so->so_lock); |
| } else { |
| /* If the caller held the lock don't release it here */ |
| ASSERT(MUTEX_HELD(&so->so_lock)); |
| ASSERT(so->so_flag & SOLOCKED); |
| } |
| |
| return (error); |
| } |
| |
| /* |
| * Turn socket into a listen socket. |
| */ |
| /* ARGSUSED */ |
| static int |
| sosctp_listen(struct sonode *so, int backlog, struct cred *cr) |
| { |
| int error = 0; |
| |
| mutex_enter(&so->so_lock); |
| so_lock_single(so); |
| |
| /* |
| * If this socket is trying to do connect, or if it has |
| * been connected, disallow. |
| */ |
| if (so->so_state & (SS_ISCONNECTING | SS_ISCONNECTED | |
| SS_ISDISCONNECTING | SS_CANTRCVMORE | SS_CANTSENDMORE)) { |
| error = EINVAL; |
| eprintsoline(so, error); |
| goto done; |
| } |
| |
| if (backlog < 0) { |
| backlog = 0; |
| } |
| |
| /* |
| * If listen() is only called to change backlog, we don't |
| * need to notify protocol module. |
| */ |
| if (so->so_state & SS_ACCEPTCONN) { |
| so->so_backlog = backlog; |
| goto done; |
| } |
| |
| mutex_exit(&so->so_lock); |
| error = sctp_listen((struct sctp_s *)so->so_proto_handle); |
| mutex_enter(&so->so_lock); |
| if (error == 0) { |
| so->so_state |= (SS_ACCEPTCONN|SS_ISBOUND); |
| so->so_backlog = backlog; |
| } else { |
| eprintsoline(so, error); |
| } |
| done: |
| so_unlock_single(so, SOLOCKED); |
| mutex_exit(&so->so_lock); |
| |
| return (error); |
| } |
| |
| /* |
| * Active open. |
| */ |
| /*ARGSUSED*/ |
| static int |
| sosctp_connect(struct sonode *so, const struct sockaddr *name, |
| socklen_t namelen, int fflag, int flags, struct cred *cr) |
| { |
| int error = 0; |
| |
| ASSERT(so->so_type == SOCK_STREAM); |
| |
| mutex_enter(&so->so_lock); |
| so_lock_single(so); |
| |
| /* |
| * Can't connect() after listen(), or if the socket is already |
| * connected. |
| */ |
| if (so->so_state & (SS_ACCEPTCONN|SS_ISCONNECTED|SS_ISCONNECTING)) { |
| if (so->so_state & SS_ISCONNECTED) { |
| error = EISCONN; |
| } else if (so->so_state & SS_ISCONNECTING) { |
| error = EALREADY; |
| } else { |
| error = EOPNOTSUPP; |
| } |
| eprintsoline(so, error); |
| goto done; |
| } |
| |
| /* |
| * Check for failure of an earlier call |
| */ |
| if (so->so_error != 0) { |
| error = sogeterr(so, B_TRUE); |
| eprintsoline(so, error); |
| goto done; |
| } |
| |
| /* |
| * Connection is closing, or closed, don't allow reconnect. |
| * TCP allows this to proceed, but the socket remains unwriteable. |
| * BSD returns EINVAL. |
| */ |
| if (so->so_state & (SS_ISDISCONNECTING|SS_CANTRCVMORE| |
| SS_CANTSENDMORE)) { |
| error = EINVAL; |
| eprintsoline(so, error); |
| goto done; |
| } |
| |
| if (name == NULL || namelen == 0) { |
| mutex_exit(&so->so_lock); |
| error = EINVAL; |
| eprintsoline(so, error); |
| goto done; |
| } |
| |
| soisconnecting(so); |
| mutex_exit(&so->so_lock); |
| |
| error = sctp_connect((struct sctp_s *)so->so_proto_handle, |
| name, namelen); |
| |
| mutex_enter(&so->so_lock); |
| if (error == 0) { |
| /* |
| * Allow other threads to access the socket |
| */ |
| error = sowaitconnected(so, fflag, 0); |
| } |
| done: |
| so_unlock_single(so, SOLOCKED); |
| mutex_exit(&so->so_lock); |
| 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 |
| sosctp_seq_connect(struct sonode *so, const struct sockaddr *name, |
| socklen_t namelen, int fflag, int flags, struct cred *cr) |
| { |
| struct sctp_soassoc *ssa; |
| struct sctp_sonode *ss; |
| int error; |
| |
| ASSERT(so->so_type == SOCK_SEQPACKET); |
| |
| mutex_enter(&so->so_lock); |
| so_lock_single(so); |
| |
| if (name == NULL || namelen == 0) { |
| error = EINVAL; |
| eprintsoline(so, error); |
| goto done; |
| } |
| |
| ss = SOTOSSO(so); |
| |
| error = sosctp_assoc_createconn(ss, name, namelen, NULL, 0, fflag, |
| cr, &ssa); |
| if (error != 0) { |
| if ((error == EHOSTUNREACH) && (flags & _SOCONNECT_XPG4_2)) { |
| error = ENETUNREACH; |
| } |
| } |
| if (ssa != NULL) { |
| SSA_REFRELE(ss, ssa); |
| } |
| |
| done: |
| so_unlock_single(so, SOLOCKED); |
| mutex_exit(&so->so_lock); |
| return (error); |
| } |
| |
| /* |
| * Receive data. |
| */ |
| /* ARGSUSED */ |
| static int |
| sosctp_recvmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop, |
| struct cred *cr) |
| { |
| struct sctp_sonode *ss = SOTOSSO(so); |
| struct sctp_soassoc *ssa = NULL; |
| int flags, error = 0; |
| struct T_unitdata_ind *tind; |
| int len, count, readcnt = 0, rxqueued; |
| socklen_t controllen, namelen; |
| void *opt; |
| mblk_t *mp; |
| rval_t rval; |
| |
| controllen = msg->msg_controllen; |
| namelen = msg->msg_namelen; |
| flags = msg->msg_flags; |
| msg->msg_flags = 0; |
| msg->msg_controllen = 0; |
| msg->msg_namelen = 0; |
| |
| if (so->so_type == SOCK_STREAM) { |
| if (!(so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING| |
| SS_CANTRCVMORE))) { |
| return (ENOTCONN); |
| } |
| } else { |
| /* NOTE: Will come here from vop_read() as well */ |
| /* For 1-N socket, recv() cannot be used. */ |
| if (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. |
| */ |
| if (so->so_rcv_q_head == NULL && ss->ss_assoccnt == 0 && |
| !(so->so_state & SS_ACCEPTCONN)) { |
| return (ENOTCONN); |
| } |
| } |
| |
| /* |
| * out-of-band data not supported. |
| */ |
| if (flags & MSG_OOB) { |
| 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. |
| */ |
| |
| mutex_enter(&so->so_lock); |
| |
| /* |
| * Allow just one reader at a time. |
| */ |
| error = so_lock_read_intr(so, |
| uiop->uio_fmode | ((flags & MSG_DONTWAIT) ? FNONBLOCK : 0)); |
| if (error) { |
| mutex_exit(&so->so_lock); |
| return (error); |
| } |
| mutex_exit(&so->so_lock); |
| again: |
| error = so_dequeue_msg(so, &mp, uiop, &rval, flags | MSG_DUPCTRL); |
| if (mp != NULL) { |
| if (so->so_type == SOCK_SEQPACKET) { |
| ssa = *(struct sctp_soassoc **)DB_BASE(mp); |
| } |
| |
| tind = (struct T_unitdata_ind *)mp->b_rptr; |
| |
| len = tind->SRC_length; |
| |
| if (namelen > 0 && len > 0) { |
| |
| opt = sogetoff(mp, tind->SRC_offset, len, 1); |
| |
| ASSERT(opt != NULL); |
| |
| msg->msg_name = kmem_alloc(len, KM_SLEEP); |
| msg->msg_namelen = len; |
| |
| bcopy(opt, msg->msg_name, len); |
| } |
| |
| len = tind->OPT_length; |
| if (controllen == 0) { |
| if (len > 0) { |
| msg->msg_flags |= MSG_CTRUNC; |
| } |
| } else if (len > 0) { |
| opt = sogetoff(mp, tind->OPT_offset, len, |
| __TPI_ALIGN_SIZE); |
| |
| ASSERT(opt != NULL); |
| sosctp_pack_cmsg(opt, msg, len); |
| } |
| |
| if (mp->b_flag & SCTP_NOTIFICATION) { |
| msg->msg_flags |= MSG_NOTIFICATION; |
| } |
| |
| if (!(mp->b_flag & SCTP_PARTIAL_DATA)) |
| msg->msg_flags |= MSG_EOR; |
| freemsg(mp); |
| } |
| 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. |
| */ |
| if (ssa == NULL) { |
| mutex_enter(&so->so_lock); |
| rxqueued = so->so_rcv_queued; |
| |
| so->so_rcv_queued = rxqueued - readcnt; |
| count = so->so_rcvbuf - so->so_rcv_queued; |
| |
| ASSERT(so->so_rcv_q_head != NULL || |
| so->so_rcv_head != NULL || |
| so->so_rcv_queued == 0); |
| |
| so_unlock_read(so); |
| mutex_exit(&so->so_lock); |
| |
| if (readcnt > 0 && (((count > 0) && |
| (rxqueued >= so->so_rcvlowat)) || |
| (so->so_rcv_queued == 0))) { |
| /* |
| * If amount of queued data is higher than watermark, |
| * updata SCTP's idea of available buffer space. |
| */ |
| sctp_recvd((struct sctp_s *)so->so_proto_handle, count); |
| } |
| } else { |
| mutex_enter(&so->so_lock); |
| rxqueued = ssa->ssa_rcv_queued; |
| |
| ssa->ssa_rcv_queued = rxqueued - readcnt; |
| count = so->so_rcvbuf - ssa->ssa_rcv_queued; |
| |
| so_unlock_read(so); |
| |
| if (readcnt > 0 && |
| (((count > 0) && (rxqueued >= so->so_rcvlowat)) || |
| (ssa->ssa_rcv_queued == 0))) { |
| /* |
| * If amount of queued data is higher than watermark, |
| * updata SCTP's idea of available buffer space. |
| */ |
| mutex_exit(&so->so_lock); |
| |
| sctp_recvd((struct sctp_s *)ssa->ssa_conn, count); |
| |
| mutex_enter(&so->so_lock); |
| } |
| /* |
| * MOREDATA flag is set if all data could not be copied |
| */ |
| if (!(flags & MSG_PEEK) && !(rval.r_val1 & MOREDATA)) { |
| SSA_REFRELE(ss, ssa); |
| } |
| mutex_exit(&so->so_lock); |
| } |
| |
| return (error); |
| } |
| |
| int |
| sosctp_uiomove(mblk_t *hdr_mp, ssize_t count, ssize_t blk_size, int wroff, |
| struct uio *uiop, int flags, cred_t *cr) |
| { |
| ssize_t size; |
| int error; |
| mblk_t *mp; |
| dblk_t *dp; |
| |
| /* |
| * Loop until we have all data copied into mblk's. |
| */ |
| while (count > 0) { |
| size = MIN(count, blk_size); |
| |
| /* |
| * As a message can be splitted up and sent in different |
| * packets, each mblk will have the extra space before |
| * data to accommodate what SCTP wants to put in there. |
| */ |
| while ((mp = allocb_cred(size + wroff, cr, |
| curproc->p_pid)) == NULL) { |
| if ((uiop->uio_fmode & (FNDELAY|FNONBLOCK)) || |
| (flags & MSG_DONTWAIT)) { |
| return (EAGAIN); |
| } |
| if ((error = strwaitbuf(size + wroff, BPRI_MED))) { |
| return (error); |
| } |
| } |
| |
| dp = mp->b_datap; |
| dp->db_cpid = curproc->p_pid; |
| ASSERT(wroff <= dp->db_lim - mp->b_wptr); |
| mp->b_rptr += wroff; |
| error = uiomove(mp->b_rptr, size, UIO_WRITE, uiop); |
| if (error != 0) { |
| freeb(mp); |
| return (error); |
| } |
| mp->b_wptr = mp->b_rptr + size; |
| count -= size; |
| hdr_mp->b_cont = mp; |
| hdr_mp = mp; |
| } |
| return (0); |
| } |
| |
| /* |
| * Send message. |
| */ |
| static int |
| sosctp_sendmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop, |
| struct cred *cr) |
| { |
| struct sctp_sonode *ss = SOTOSSO(so); |
| mblk_t *mctl; |
| struct cmsghdr *cmsg; |
| struct sctp_sndrcvinfo *sinfo; |
| int optlen, flags, fflag; |
| ssize_t count, msglen; |
| int error; |
| |
| ASSERT(so->so_type == SOCK_STREAM); |
| |
| flags = msg->msg_flags; |
| if (flags & MSG_OOB) { |
| /* |
| * No out-of-band data support. |
| */ |
| return (EOPNOTSUPP); |
| } |
| |
| if (msg->msg_controllen != 0) { |
| optlen = msg->msg_controllen; |
| cmsg = sosctp_find_cmsg(msg->msg_control, optlen, SCTP_SNDRCV); |
| if (cmsg != NULL) { |
| if (cmsg->cmsg_len < |
| (sizeof (*sinfo) + sizeof (*cmsg))) { |
| eprintsoline(so, EINVAL); |
| return (EINVAL); |
| } |
| sinfo = (struct sctp_sndrcvinfo *)(cmsg + 1); |
| |
| /* Both flags should not be set together. */ |
| if ((sinfo->sinfo_flags & MSG_EOF) && |
| (sinfo->sinfo_flags & MSG_ABORT)) { |
| eprintsoline(so, EINVAL); |
| return (EINVAL); |
| } |
| |
| /* Initiate a graceful shutdown. */ |
| if (sinfo->sinfo_flags & MSG_EOF) { |
| /* Can't include data in MSG_EOF message. */ |
| if (uiop->uio_resid != 0) { |
| eprintsoline(so, EINVAL); |
| return (EINVAL); |
| } |
| |
| /* |
| * This is the same sequence as done in |
| * shutdown(SHUT_WR). |
| */ |
| mutex_enter(&so->so_lock); |
| so_lock_single(so); |
| socantsendmore(so); |
| cv_broadcast(&so->so_snd_cv); |
| so->so_state |= SS_ISDISCONNECTING; |
| mutex_exit(&so->so_lock); |
| |
| pollwakeup(&so->so_poll_list, POLLOUT); |
| sctp_recvd((struct sctp_s *)so->so_proto_handle, |
| so->so_rcvbuf); |
| error = sctp_disconnect( |
| (struct sctp_s *)so->so_proto_handle); |
| |
| mutex_enter(&so->so_lock); |
| so_unlock_single(so, SOLOCKED); |
| mutex_exit(&so->so_lock); |
| return (error); |
| } |
| } |
| } else { |
| optlen = 0; |
| } |
| |
| mutex_enter(&so->so_lock); |
| for (;;) { |
| if (so->so_state & SS_CANTSENDMORE) { |
| mutex_exit(&so->so_lock); |
| return (EPIPE); |
| } |
| |
| if (so->so_error != 0) { |
| error = sogeterr(so, B_TRUE); |
| mutex_exit(&so->so_lock); |
| return (error); |
| } |
| |
| if (!so->so_snd_qfull) |
| break; |
| |
| if (so->so_state & SS_CLOSING) { |
| mutex_exit(&so->so_lock); |
| return (EINTR); |
| } |
| /* |
| * Xmit window full in a blocking socket. |
| */ |
| if ((uiop->uio_fmode & (FNDELAY|FNONBLOCK)) || |
| (flags & MSG_DONTWAIT)) { |
| mutex_exit(&so->so_lock); |
| return (EAGAIN); |
| } else { |
| /* |
| * Wait for space to become available and try again. |
| */ |
| error = cv_wait_sig(&so->so_snd_cv, &so->so_lock); |
| if (!error) { /* signal */ |
| mutex_exit(&so->so_lock); |
| return (EINTR); |
| } |
| } |
| } |
| msglen = count = uiop->uio_resid; |
| |
| /* Don't allow sending a message larger than the send buffer size. */ |
| /* XXX Transport module need to enforce this */ |
| if (msglen > so->so_sndbuf) { |
| mutex_exit(&so->so_lock); |
| return (EMSGSIZE); |
| } |
| |
| /* |
| * Allow piggybacking data on handshake messages (SS_ISCONNECTING). |
| */ |
| if (!(so->so_state & (SS_ISCONNECTING | SS_ISCONNECTED))) { |
| /* |
| * 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. |
| * |
| * We also need to make sure that the peer address is |
| * provided before we attempt to do the connect. |
| */ |
| if ((so->so_state & SS_ACCEPTCONN) || |
| msg->msg_name == NULL) { |
| mutex_exit(&so->so_lock); |
| error = ENOTCONN; |
| goto error_nofree; |
| } |
| mutex_exit(&so->so_lock); |
| fflag = uiop->uio_fmode; |
| if (flags & MSG_DONTWAIT) { |
| fflag |= FNDELAY; |
| } |
| error = sosctp_connect(so, msg->msg_name, msg->msg_namelen, |
| fflag, (so->so_version == SOV_XPG4_2) * _SOCONNECT_XPG4_2, |
| cr); |
| if (error) { |
| /* |
| * Check for non-fatal errors, socket connected |
| * while the lock had been lifted. |
| */ |
| if (error != EISCONN && error != EALREADY) { |
| goto error_nofree; |
| } |
| error = 0; |
| } |
| } else { |
| mutex_exit(&so->so_lock); |
| } |
| |
| mctl = sctp_alloc_hdr(msg->msg_name, msg->msg_namelen, |
| msg->msg_control, optlen, SCTP_CAN_BLOCK); |
| if (mctl == NULL) { |
| error = EINTR; |
| goto error_nofree; |
| } |
| |
| /* Copy in the message. */ |
| if ((error = sosctp_uiomove(mctl, count, ss->ss_wrsize, ss->ss_wroff, |
| uiop, flags, cr)) != 0) { |
| goto error_ret; |
| } |
| error = sctp_sendmsg((struct sctp_s *)so->so_proto_handle, mctl, 0); |
| if (error == 0) |
| return (0); |
| |
| error_ret: |
| freemsg(mctl); |
| error_nofree: |
| mutex_enter(&so->so_lock); |
| if ((error == EPIPE) && (so->so_state & SS_CANTSENDMORE)) { |
| /* |
| * We received shutdown between the time lock was |
| * lifted and call to sctp_sendmsg(). |
| */ |
| mutex_exit(&so->so_lock); |
| return (EPIPE); |
| } |
| mutex_exit(&so->so_lock); |
| return (error); |
| } |
| |
| /* |
| * Send message on 1-N socket. Connects automatically if there is |
| * no association. |
| */ |
| static int |
| sosctp_seq_sendmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop, |
| struct cred *cr) |
| { |
| struct sctp_sonode *ss; |
| struct sctp_soassoc *ssa; |
| struct cmsghdr *cmsg; |
| struct sctp_sndrcvinfo *sinfo; |
| int aid = 0; |
| mblk_t *mctl; |
| int namelen, optlen, flags; |
| ssize_t count, msglen; |
| int error; |
| uint16_t s_flags = 0; |
| |
| ASSERT(so->so_type == SOCK_SEQPACKET); |
| |
| /* |
| * There shouldn't be problems with alignment, as the memory for |
| * msg_control was alloced with kmem_alloc. |
| */ |
| cmsg = sosctp_find_cmsg(msg->msg_control, msg->msg_controllen, |
| SCTP_SNDRCV); |
| if (cmsg != NULL) { |
| if (cmsg->cmsg_len < (sizeof (*sinfo) + sizeof (*cmsg))) { |
| eprintsoline(so, EINVAL); |
| return (EINVAL); |
| } |
| sinfo = (struct sctp_sndrcvinfo *)(cmsg + 1); |
| s_flags = sinfo->sinfo_flags; |
| aid = sinfo->sinfo_assoc_id; |
| } |
| |
| ss = SOTOSSO(so); |
| namelen = msg->msg_namelen; |
| |
| if (msg->msg_controllen > 0) { |
| optlen = msg->msg_controllen; |
| } else { |
| optlen = 0; |
| } |
| |
| mutex_enter(&so->so_lock); |
| |
| /* |
| * 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. |
| */ |
| if (msg->msg_name == NULL || namelen == 0 || |
| (s_flags & MSG_EOF)) { |
| error = EINVAL; |
| eprintsoline(so, error); |
| goto done; |
| } |
| flags = uiop->uio_fmode; |
| if (msg->msg_flags & MSG_DONTWAIT) { |
| flags |= FNDELAY; |
| } |
| so_lock_single(so); |
| error = sosctp_assoc_createconn(ss, msg->msg_name, namelen, |
| msg->msg_control, optlen, flags, cr, &ssa); |
| if (error) { |
| if ((so->so_version == SOV_XPG4_2) && |
| (error == EHOSTUNREACH)) { |
| error = ENETUNREACH; |
| } |
| if (ssa == NULL) { |
| /* |
| * Fatal error during connect(). Bail out. |
| * If ssa exists, it means that the handshake |
| * is in progress. |
| */ |
| eprintsoline(so, error); |
| so_unlock_single(so, SOLOCKED); |
| goto done; |
| } |
| /* |
| * All the errors are non-fatal ones, don't return |
| * e.g. EINPROGRESS from sendmsg(). |
| */ |
| error = 0; |
| } |
| so_unlock_single(so, SOLOCKED); |
| } else { |
| if ((error = sosctp_assoc(ss, aid, &ssa)) != 0) { |
| eprintsoline(so, error); |
| goto done; |
| } |
| } |
| |
| /* |
| * Now we have an association. |
| */ |
| flags = msg->msg_flags; |
| |
| /* |
| * MSG_EOF initiates graceful shutdown. |
| */ |
| if (s_flags & MSG_EOF) { |
| if (uiop->uio_resid) { |
| /* |
| * Can't include data in MSG_EOF message. |
| */ |
| error = EINVAL; |
| } else { |
| mutex_exit(&so->so_lock); |
| ssa->ssa_state |= SS_ISDISCONNECTING; |
| sctp_recvd((struct sctp_s *)ssa->ssa_conn, |
| so->so_rcvbuf); |
| error = sctp_disconnect((struct sctp_s *)ssa->ssa_conn); |
| mutex_enter(&so->so_lock); |
| } |
| goto refrele; |
| } |
| |
| for (;;) { |
| if (ssa->ssa_state & SS_CANTSENDMORE) { |
| SSA_REFRELE(ss, ssa); |
| mutex_exit(&so->so_lock); |
| return (EPIPE); |
| } |
| if (ssa->ssa_error != 0) { |
| error = ssa->ssa_error; |
| ssa->ssa_error = 0; |
| goto refrele; |
| } |
| |
| if (!ssa->ssa_snd_qfull) |
| break; |
| |
| if (so->so_state & SS_CLOSING) { |
| error = EINTR; |
| goto refrele; |
| } |
| if ((uiop->uio_fmode & (FNDELAY|FNONBLOCK)) || |
| (flags & MSG_DONTWAIT)) { |
| error = EAGAIN; |
| goto refrele; |
| } else { |
| /* |
| * Wait for space to become available and try again. |
| */ |
| error = cv_wait_sig(&so->so_snd_cv, &so->so_lock); |
| if (!error) { /* signal */ |
| error = EINTR; |
| goto refrele; |
| } |
| } |
| } |
| |
| msglen = count = uiop->uio_resid; |
| |
| /* Don't allow sending a message larger than the send buffer size. */ |
| if (msglen > so->so_sndbuf) { |
| error = EMSGSIZE; |
| goto refrele; |
| } |
| |
| /* |
| * Update TX buffer usage here so that we can lift the socket lock. |
| */ |
| mutex_exit(&so->so_lock); |
| |
| mctl = sctp_alloc_hdr(msg->msg_name, namelen, msg->msg_control, |
| optlen, SCTP_CAN_BLOCK); |
| if (mctl == NULL) { |
| error = EINTR; |
| goto lock_rele; |
| } |
| |
| /* Copy in the message. */ |
| if ((error = sosctp_uiomove(mctl, count, ssa->ssa_wrsize, |
| ssa->ssa_wroff, uiop, flags, cr)) != 0) { |
| goto lock_rele; |
| } |
| error = sctp_sendmsg((struct sctp_s *)ssa->ssa_conn, mctl, 0); |
| lock_rele: |
| mutex_enter(&so->so_lock); |
| if (error != 0) { |
| freemsg(mctl); |
| if ((error == EPIPE) && (ssa->ssa_state & SS_CANTSENDMORE)) { |
| /* |
| * We received shutdown between the time lock was |
| * lifted and call to sctp_sendmsg(). |
| */ |
| SSA_REFRELE(ss, ssa); |
| mutex_exit(&so->so_lock); |
| return (EPIPE); |
| } |
| } |
| |
| refrele: |
| SSA_REFRELE(ss, ssa); |
| done: |
| mutex_exit(&so->so_lock); |
| return (error); |
| } |
| |
| /* |
| * Get address of remote node. |
| */ |
| /* ARGSUSED */ |
| static int |
| sosctp_getpeername(struct sonode *so, struct sockaddr *addr, socklen_t *addrlen, |
| boolean_t accept, struct cred *cr) |
| { |
| return (sctp_getpeername((struct sctp_s *)so->so_proto_handle, addr, |
| addrlen)); |
| } |
| |
| /* |
| * Get local address. |
| */ |
| /* ARGSUSED */ |
| static int |
| sosctp_getsockname(struct sonode *so, struct sockaddr *addr, socklen_t *addrlen, |
| struct cred *cr) |
| { |
| return (sctp_getsockname((struct sctp_s *)so->so_proto_handle, addr, |
| addrlen)); |
| } |
| |
| /* |
| * Called from shutdown(). |
| */ |
| /* ARGSUSED */ |
| static int |
| sosctp_shutdown(struct sonode *so, int how, struct cred *cr) |
| { |
| uint_t state_change; |
| int wakesig = 0; |
| int error = 0; |
| |
| mutex_enter(&so->so_lock); |
| /* |
| * 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. |
| */ |
| state_change = so->so_state; |
| |
| switch (how) { |
| case SHUT_RD: |
| socantrcvmore(so); |
| break; |
| case SHUT_WR: |
| socantsendmore(so); |
| break; |
| case SHUT_RDWR: |
| socantsendmore(so); |
| socantrcvmore(so); |
| break; |
| default: |
| mutex_exit(&so->so_lock); |
| return (EINVAL); |
| } |
| |
| state_change = so->so_state & ~state_change; |
| |
| if (state_change & SS_CANTRCVMORE) { |
| if (so->so_rcv_q_head == NULL) { |
| cv_signal(&so->so_rcv_cv); |
| } |
| wakesig = POLLIN|POLLRDNORM; |
| |
| socket_sendsig(so, SOCKETSIG_READ); |
| } |
| if (state_change & SS_CANTSENDMORE) { |
| cv_broadcast(&so->so_snd_cv); |
| wakesig |= POLLOUT; |
| |
| so->so_state |= SS_ISDISCONNECTING; |
| } |
| mutex_exit(&so->so_lock); |
| |
| pollwakeup(&so->so_poll_list, wakesig); |
| |
| if (state_change & SS_CANTSENDMORE) { |
| sctp_recvd((struct sctp_s *)so->so_proto_handle, so->so_rcvbuf); |
| error = sctp_disconnect((struct sctp_s *)so->so_proto_handle); |
| } |
| |
| /* |
| * 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 |
| sosctp_getsockopt(struct sonode *so, int level, int option_name, |
| void *optval, socklen_t *optlenp, int flags, struct cred *cr) |
| { |
| socklen_t maxlen = *optlenp; |
| socklen_t len; |
| socklen_t optlen; |
| uint8_t buffer[4]; |
| void *optbuf = &buffer; |
| int error = 0; |
| |
| |
| if (level == SOL_SOCKET) { |
| switch (option_name) { |
| /* Not supported options */ |
| case SO_SNDTIMEO: |
| case SO_RCVTIMEO: |
| case SO_EXCLBIND: |
| error = ENOPROTOOPT; |
| eprintsoline(so, error); |
| goto done; |
| |
| 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: |
| case SO_PROTOTYPE: |
| case SO_DOMAIN: |
| if (maxlen < (t_uscalar_t)sizeof (int32_t)) { |
| error = EINVAL; |
| eprintsoline(so, error); |
| goto done; |
| } |
| break; |
| case SO_LINGER: |
| if (maxlen < (t_uscalar_t)sizeof (struct linger)) { |
| error = EINVAL; |
| eprintsoline(so, error); |
| goto done; |
| } |
| break; |
| } |
| } |
| |
| if (level == IPPROTO_SCTP) { |
| /* |
| * Should go through ioctl(). |
| */ |
| return (EINVAL); |
| } |
| |
| if (maxlen > sizeof (buffer)) { |
| optbuf = kmem_alloc(maxlen, KM_SLEEP); |
| } |
| optlen = maxlen; |
| |
| /* |
| * If the resulting optlen is greater than the provided maxlen, then |
| * we sliently trucate. |
| */ |
| error = sctp_get_opt((struct sctp_s *)so->so_proto_handle, level, |
| option_name, optbuf, &optlen); |
| |
| if (error != 0) { |
| eprintsoline(so, error); |
| goto free; |
| } |
| len = optlen; |
| |
| copyout: |
| |
| len = MIN(len, maxlen); |
| bcopy(optbuf, optval, len); |
| *optlenp = optlen; |
| free: |
| if (optbuf != &buffer) { |
| kmem_free(optbuf, maxlen); |
| } |
| done: |
| return (error); |
| } |
| |
| /* |
| * Set socket options |
| */ |
| /* ARGSUSED */ |
| static int |
| sosctp_setsockopt(struct sonode *so, int level, int option_name, |
| const void *optval, t_uscalar_t optlen, struct cred *cr) |
| { |
| struct sctp_sonode *ss = SOTOSSO(so); |
| struct sctp_soassoc *ssa = NULL; |
| sctp_assoc_t id; |
| int error, rc; |
| void *conn = NULL; |
| |
| mutex_enter(&so->so_lock); |
| |
| /* |
| * For some SCTP level options, one can select the association this |
| * applies to. |
| */ |
| if (so->so_type == SOCK_STREAM) { |
| conn = so->so_proto_handle; |
| } else { |
| /* |
| * SOCK_SEQPACKET only |
| */ |
| id = 0; |
| if (level == IPPROTO_SCTP) { |
| switch (option_name) { |
| case SCTP_RTOINFO: |
| case SCTP_ASSOCINFO: |
| case SCTP_SET_PEER_PRIMARY_ADDR: |
| case SCTP_PRIMARY_ADDR: |
| case SCTP_PEER_ADDR_PARAMS: |
| /* |
| * Association ID is the first element |
| * params struct |
| */ |
| if (optlen < sizeof (sctp_assoc_t)) { |
| error = EINVAL; |
| eprintsoline(so, error); |
| goto done; |
| } |
| id = *(sctp_assoc_t *)optval; |
| break; |
| case SCTP_DEFAULT_SEND_PARAM: |
| if (optlen != sizeof (struct sctp_sndrcvinfo)) { |
| error = EINVAL; |
| eprintsoline(so, error); |
| goto done; |
| } |
| id = ((struct sctp_sndrcvinfo *) |
| optval)->sinfo_assoc_id; |
| break; |
| case SCTP_INITMSG: |
| /* |
| * Only applies to future associations |
| */ |
| conn = so->so_proto_handle; |
| break; |
| default: |
| break; |
| } |
| } else if (level == SOL_SOCKET) { |
| if (option_name == SO_LINGER) { |
| error = EOPNOTSUPP; |
| eprintsoline(so, error); |
| 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)) { |
| conn = so->so_proto_handle; |
| } |
| } else { |
| conn = NULL; |
| } |
| |
| /* |
| * If association ID was specified, do op on that assoc. |
| * Otherwise set the default setting of a socket. |
| */ |
| if (id != 0) { |
| if ((error = sosctp_assoc(ss, id, &ssa)) != 0) { |
| eprintsoline(so, error); |
| goto done; |
| } |
| conn = ssa->ssa_conn; |
| } |
| } |
| dprint(2, ("sosctp_setsockopt %p (%d) - conn %p %d %d id:%d\n", |
| (void *)ss, so->so_type, (void *)conn, level, option_name, id)); |
| |
| ASSERT(ssa == NULL || (ssa != NULL && conn != NULL)); |
| if (conn != NULL) { |
| mutex_exit(&so->so_lock); |
| error = sctp_set_opt((struct sctp_s *)conn, level, option_name, |
| optval, optlen); |
| mutex_enter(&so->so_lock); |
| if (ssa != NULL) |
| SSA_REFRELE(ss, ssa); |
| } 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. And since we are |
| * looping through all possible aids, some of them can be |
| * invalid. We just ignore this kind (sosctp_assoc()) of |
| * errors. |
| */ |
| sctp_assoc_t aid; |
| |
| mutex_exit(&so->so_lock); |
| error = sctp_set_opt((struct sctp_s *)so->so_proto_handle, |
| level, option_name, optval, optlen); |
| mutex_enter(&so->so_lock); |
| for (aid = 1; aid < ss->ss_maxassoc; aid++) { |
| if (sosctp_assoc(ss, aid, &ssa) != 0) |
| continue; |
| mutex_exit(&so->so_lock); |
| rc = sctp_set_opt((struct sctp_s *)ssa->ssa_conn, level, |
| option_name, optval, optlen); |
| mutex_enter(&so->so_lock); |
| SSA_REFRELE(ss, ssa); |
| if (error == 0) { |
| error = rc; |
| } |
| } |
| } |
| done: |
| mutex_exit(&so->so_lock); |
| return (error); |
| } |
| |
| /*ARGSUSED*/ |
| static int |
| sosctp_ioctl(struct sonode *so, int cmd, intptr_t arg, int mode, |
| struct cred *cr, int32_t *rvalp) |
| { |
| struct sctp_sonode *ss; |
| int32_t value; |
| int error; |
| int intval; |
| pid_t pid; |
| struct sctp_soassoc *ssa; |
| void *conn; |
| void *buf; |
| STRUCT_DECL(sctpopt, opt); |
| uint32_t optlen; |
| int buflen; |
| |
| ss = SOTOSSO(so); |
| |
| /* handle socket specific ioctls */ |
| switch (cmd) { |
| case FIONBIO: |
| if (so_copyin((void *)arg, &value, sizeof (int32_t), |
| (mode & (int)FKIOCTL))) { |
| return (EFAULT); |
| } |
| mutex_enter(&so->so_lock); |
| if (value) { |
| so->so_state |= SS_NDELAY; |
| } else { |
| so->so_state &= ~SS_NDELAY; |
| } |
| mutex_exit(&so->so_lock); |
| return (0); |
| |
| case FIOASYNC: |
| if (so_copyin((void *)arg, &value, sizeof (int32_t), |
| (mode & (int)FKIOCTL))) { |
| return (EFAULT); |
| } |
| mutex_enter(&so->so_lock); |
| |
| if (value) { |
| /* Turn on SIGIO */ |
| so->so_state |= SS_ASYNC; |
| } else { |
| /* Turn off SIGIO */ |
| so->so_state &= ~SS_ASYNC; |
| } |
| mutex_exit(&so->so_lock); |
| return (0); |
| |
| case SIOCSPGRP: |
| case FIOSETOWN: |
| if (so_copyin((void *)arg, &pid, sizeof (pid_t), |
| (mode & (int)FKIOCTL))) { |
| return (EFAULT); |
| } |
| mutex_enter(&so->so_lock); |
| |
| error = (pid != so->so_pgrp) ? socket_chgpgrp(so, pid) : 0; |
| mutex_exit(&so->so_lock); |
| return (error); |
| |
| case SIOCGPGRP: |
| case FIOGETOWN: |
| if (so_copyout(&so->so_pgrp, (void *)arg, |
| sizeof (pid_t), (mode & (int)FKIOCTL))) |
| return (EFAULT); |
| return (0); |
| |
| case FIONREAD: |
| /* XXX: Cannot be used unless standard buffer is used */ |
| /* |
| * Return number of bytes of data in all data messages |
| * in queue in "arg". |
| * For stream socket, amount of available data. |
| * For sock_dgram, # of available bytes + addresses. |
| */ |
| intval = (so->so_state & SS_ACCEPTCONN) ? 0 : |
| MIN(so->so_rcv_queued, INT_MAX); |
| if (so_copyout(&intval, (void *)arg, sizeof (intval), |
| (mode & (int)FKIOCTL))) |
| return (EFAULT); |
| return (0); |
| case SIOCATMARK: |
| /* |
| * No support for urgent data. |
| */ |
| intval = 0; |
| |
| if (so_copyout(&intval, (void *)arg, sizeof (int), |
| (mode & (int)FKIOCTL))) |
| return (EFAULT); |
| return (0); |
| case _I_GETPEERCRED: { |
| int error = 0; |
| |
| if ((mode & FKIOCTL) == 0) |
| return (EINVAL); |
| |
| mutex_enter(&so->so_lock); |
| if ((so->so_mode & SM_CONNREQUIRED) == 0) { |
| error = ENOTSUP; |
| } else if ((so->so_state & SS_ISCONNECTED) == 0) { |
| error = ENOTCONN; |
| } else if (so->so_peercred != NULL) { |
| k_peercred_t *kp = (k_peercred_t *)arg; |
| kp->pc_cr = so->so_peercred; |
| kp->pc_cpid = so->so_cpid; |
| crhold(so->so_peercred); |
| } else { |
| error = EINVAL; |
| } |
| mutex_exit(&so->so_lock); |
| return (error); |
| } |
| case SIOCSCTPGOPT: |
| STRUCT_INIT(opt, mode); |
| |
| if (so_copyin((void *)arg, STRUCT_BUF(opt), STRUCT_SIZE(opt), |
| (mode & (int)FKIOCTL))) { |
| return (EFAULT); |
| } |
| if ((optlen = STRUCT_FGET(opt, sopt_len)) > SO_MAXARGSIZE) |
| return (EINVAL); |
| |
| /* |
| * Find the correct sctp_t based on whether it is 1-N socket |
| * or not. |
| */ |
| intval = STRUCT_FGET(opt, sopt_aid); |
| mutex_enter(&so->so_lock); |
| if ((so->so_type == SOCK_SEQPACKET) && intval) { |
| if ((error = sosctp_assoc(ss, intval, &ssa)) != 0) { |
| mutex_exit(&so->so_lock); |
| return (error); |
| } |
| conn = ssa->ssa_conn; |
| ASSERT(conn != NULL); |
| } else { |
| conn = so->so_proto_handle; |
| ssa = NULL; |
| } |
| mutex_exit(&so->so_lock); |
| |
| /* Copyin the option buffer and then call sctp_get_opt(). */ |
| buflen = optlen; |
| /* Let's allocate a buffer enough to hold an int */ |
| if (buflen < sizeof (uint32_t)) |
| buflen = sizeof (uint32_t); |
| buf = kmem_alloc(buflen, KM_SLEEP); |
| if (so_copyin(STRUCT_FGETP(opt, sopt_val), buf, optlen, |
| (mode & (int)FKIOCTL))) { |
| if (ssa != NULL) { |
| mutex_enter(&so->so_lock); |
| SSA_REFRELE(ss, ssa); |
| mutex_exit(&so->so_lock); |
| } |
| kmem_free(buf, buflen); |
| return (EFAULT); |
| } |
| /* The option level has to be IPPROTO_SCTP */ |
| error = sctp_get_opt((struct sctp_s *)conn, IPPROTO_SCTP, |
| STRUCT_FGET(opt, sopt_name), buf, &optlen); |
| if (ssa != NULL) { |
| mutex_enter(&so->so_lock); |
| SSA_REFRELE(ss, ssa); |
| mutex_exit(&so->so_lock); |
| } |
| optlen = MIN(buflen, optlen); |
| /* No error, copyout the result with the correct buf len. */ |
| if (error == 0) { |
| STRUCT_FSET(opt, sopt_len, optlen); |
| if (so_copyout(STRUCT_BUF(opt), (void *)arg, |
| STRUCT_SIZE(opt), (mode & (int)FKIOCTL))) { |
| error = EFAULT; |
| } else if (so_copyout(buf, STRUCT_FGETP(opt, sopt_val), |
| optlen, (mode & (int)FKIOCTL))) { |
| error = EFAULT; |
| } |
| } |
| kmem_free(buf, buflen); |
| return (error); |
| |
| case SIOCSCTPSOPT: |
| STRUCT_INIT(opt, mode); |
| |
| if (so_copyin((void *)arg, STRUCT_BUF(opt), STRUCT_SIZE(opt), |
| (mode & (int)FKIOCTL))) { |
| return (EFAULT); |
| } |
| if ((optlen = STRUCT_FGET(opt, sopt_len)) > SO_MAXARGSIZE) |
| return (EINVAL); |
| |
| /* |
| * Find the correct sctp_t based on whether it is 1-N socket |
| * or not. |
| */ |
| intval = STRUCT_FGET(opt, sopt_aid); |
| mutex_enter(&so->so_lock); |
| if (intval != 0) { |
| if ((error = sosctp_assoc(ss, intval, &ssa)) != 0) { |
| mutex_exit(&so->so_lock); |
| return (error); |
| } |
| conn = ssa->ssa_conn; |
| ASSERT(conn != NULL); |
| } else { |
| conn = so->so_proto_handle; |
| ssa = NULL; |
| } |
| mutex_exit(&so->so_lock); |
| |
| /* Copyin the option buffer and then call sctp_set_opt(). */ |
| buf = kmem_alloc(optlen, KM_SLEEP); |
| if (so_copyin(STRUCT_FGETP(opt, sopt_val), buf, optlen, |
| (mode & (int)FKIOCTL))) { |
| if (ssa != NULL) { |
| mutex_enter(&so->so_lock); |
| SSA_REFRELE(ss, ssa); |
| mutex_exit(&so->so_lock); |
| } |
| kmem_free(buf, intval); |
| return (EFAULT); |
| } |
| /* The option level has to be IPPROTO_SCTP */ |
| error = sctp_set_opt((struct sctp_s *)conn, IPPROTO_SCTP, |
| STRUCT_FGET(opt, sopt_name), buf, optlen); |
| if (ssa) { |
| mutex_enter(&so->so_lock); |
| SSA_REFRELE(ss, ssa); |
| mutex_exit(&so->so_lock); |
| } |
| kmem_free(buf, optlen); |
| return (error); |
| |
| case SIOCSCTPPEELOFF: { |
| struct sonode *nso; |
| struct sctp_uc_swap us; |
| int nfd; |
| struct file *nfp; |
| struct vnode *nvp = NULL; |
| struct sockparams *sp; |
| |
| dprint(2, ("sctppeeloff %p\n", (void *)ss)); |
| |
| if (so->so_type != SOCK_SEQPACKET) { |
| return (EOPNOTSUPP); |
| } |
| if (so_copyin((void *)arg, &intval, sizeof (intval), |
| (mode & (int)FKIOCTL))) { |
| return (EFAULT); |
| } |
| if (intval == 0) { |
| return (EINVAL); |
| } |
| |
| /* |
| * Find sockparams. This is different from parent's entry, |
| * as the socket type is different. |
| */ |
| error = solookup(so->so_family, SOCK_STREAM, so->so_protocol, |
| &sp); |
| |
| /* |
| * Allocate the user fd. |
| */ |
| if ((nfd = ufalloc(0)) == -1) { |
| eprintsoline(so, EMFILE); |
| return (EMFILE); |
| } |
| |
| /* |
| * Copy the fd out. |
| */ |
| if (so_copyout(&nfd, (void *)arg, sizeof (nfd), |
| (mode & (int)FKIOCTL))) { |
| error = EFAULT; |
| goto err; |
| } |
| mutex_enter(&so->so_lock); |
| |
| /* |
| * Don't use sosctp_assoc() in order to peel off disconnected |
| * associations. |
| */ |
| ssa = ((uint32_t)intval >= ss->ss_maxassoc) ? NULL : |
| ss->ss_assocs[intval].ssi_assoc; |
| if (ssa == NULL) { |
| mutex_exit(&so->so_lock); |
| error = EINVAL; |
| goto err; |
| } |
| SSA_REFHOLD(ssa); |
| |
| nso = socksctp_create(sp, so->so_family, SOCK_STREAM, |
| so->so_protocol, so->so_version, SOCKET_NOSLEEP, |
| &error, cr); |
| if (nso == NULL) { |
| SSA_REFRELE(ss, ssa); |
| mutex_exit(&so->so_lock); |
| goto err; |
| } |
| /* cannot fail, only inheriting properties */ |
| (void) sosctp_init(nso, so, CRED(), 0); |
| nvp = SOTOV(nso); |
| so_lock_single(so); |
| mutex_exit(&so->so_lock); |
| us.sus_handle = SOTOSSO(nso); |
| us.sus_upcalls = &sosctp_sock_upcalls; |
| |
| /* |
| * Upcalls to new socket are blocked for the duration of |
| * downcall. |
| */ |
| mutex_enter(&nso->so_lock); |
| |
| error = sctp_set_opt((struct sctp_s *)ssa->ssa_conn, |
| IPPROTO_SCTP, SCTP_UC_SWAP, &us, sizeof (us)); |
| if (error) { |
| goto peelerr; |
| } |
| error = falloc(nvp, FWRITE|FREAD, &nfp, NULL); |
| if (error) { |
| goto peelerr; |
| } |
| |
| /* |
| * fill in the entries that falloc reserved |
| */ |
| nfp->f_vnode = nvp; |
| mutex_exit(&nfp->f_tlock); |
| setf(nfd, nfp); |
| |
| mutex_enter(&so->so_lock); |
| |
| sosctp_assoc_move(ss, SOTOSSO(nso), ssa); |
| |
| mutex_exit(&nso->so_lock); |
| |
| ssa->ssa_conn = NULL; |
| sosctp_assoc_free(ss, ssa); |
| |
| so_unlock_single(so, SOLOCKED); |
| mutex_exit(&so->so_lock); |
| |
| return (0); |
| |
| err: |
| setf(nfd, NULL); |
| eprintsoline(so, error); |
| return (error); |
| |
| peelerr: |
| mutex_exit(&nso->so_lock); |
| mutex_enter(&so->so_lock); |
| ASSERT(nso->so_count == 1); |
| nso->so_count = 0; |
| so_unlock_single(so, SOLOCKED); |
| SSA_REFRELE(ss, ssa); |
| mutex_exit(&so->so_lock); |
| |
| setf(nfd, NULL); |
| ASSERT(nvp->v_count == 1); |
| socket_destroy(nso); |
| eprintsoline(so, error); |
| return (error); |
| } |
| default: |
| return (EINVAL); |
| } |
| } |
| |
| /*ARGSUSED*/ |
| static int |
| sosctp_close(struct sonode *so, int flag, struct cred *cr) |
| { |
| struct sctp_sonode *ss; |
| struct sctp_sa_id *ssi; |
| struct sctp_soassoc *ssa; |
| int32_t i; |
| |
| ss = SOTOSSO(so); |
| |
| /* |
| * Initiate connection shutdown. Update SCTP's receive |
| * window. |
| */ |
| sctp_recvd((struct sctp_s *)so->so_proto_handle, |
| so->so_rcvbuf - so->so_rcv_queued); |
| (void) sctp_disconnect((struct sctp_s *)so->so_proto_handle); |
| |
| /* |
| * New associations can't come in, but old ones might get |
| * closed in upcall. Protect against that by taking a reference |
| * on the association. |
| */ |
| mutex_enter(&so->so_lock); |
| ssi = ss->ss_assocs; |
| for (i = 0; i < ss->ss_maxassoc; i++, ssi++) { |
| if ((ssa = ssi->ssi_assoc) != NULL) { |
| SSA_REFHOLD(ssa); |
| sosctp_assoc_isdisconnected(ssa, 0); |
| mutex_exit(&so->so_lock); |
| |
| sctp_recvd((struct sctp_s *)ssa->ssa_conn, |
| so->so_rcvbuf - ssa->ssa_rcv_queued); |
| (void) sctp_disconnect((struct sctp_s *)ssa->ssa_conn); |
| |
| mutex_enter(&so->so_lock); |
| SSA_REFRELE(ss, ssa); |
| } |
| } |
| mutex_exit(&so->so_lock); |
| |
| return (0); |
| } |
| |
| /* |
| * Closes incoming connections which were never accepted, frees |
| * resources. |
| */ |
| /* ARGSUSED */ |
| void |
| sosctp_fini(struct sonode *so, struct cred *cr) |
| { |
| struct sctp_sonode *ss; |
| struct sctp_sa_id *ssi; |
| struct sctp_soassoc *ssa; |
| int32_t i; |
| |
| ss = SOTOSSO(so); |
| |
| ASSERT(so->so_ops == &sosctp_sonodeops || |
| so->so_ops == &sosctp_seq_sonodeops); |
| |
| /* We are the sole owner of so now */ |
| mutex_enter(&so->so_lock); |
| |
| so_rcv_flush(so); |
| |
| /* Free all pending connections */ |
| so_acceptq_flush(so); |
| |
| ssi = ss->ss_assocs; |
| for (i = 0; i < ss->ss_maxassoc; i++, ssi++) { |
| if ((ssa = ssi->ssi_assoc) != NULL) { |
| SSA_REFHOLD(ssa); |
| mutex_exit(&so->so_lock); |
| |
| sctp_close((struct sctp_s *)ssa->ssa_conn); |
| |
| mutex_enter(&so->so_lock); |
| ssa->ssa_conn = NULL; |
| sosctp_assoc_free(ss, ssa); |
| } |
| } |
| if (ss->ss_assocs != NULL) { |
| ASSERT(ss->ss_assoccnt == 0); |
| kmem_free(ss->ss_assocs, |
| ss->ss_maxassoc * sizeof (struct sctp_sa_id)); |
| } |
| mutex_exit(&so->so_lock); |
| |
| if (so->so_proto_handle) |
| sctp_close((struct sctp_s *)so->so_proto_handle); |
| so->so_proto_handle = NULL; |
| |
| sonode_fini(so); |
| } |
| |
| /* |
| * Upcalls from SCTP |
| */ |
| |
| /* |
| * 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. |
| */ |
| /* ARGSUSED */ |
| sock_upper_handle_t |
| sctp_assoc_newconn(sock_upper_handle_t parenthandle, |
| sock_lower_handle_t connind, sock_downcalls_t *dc, |
| struct cred *peer_cred, pid_t peer_cpid, sock_upcalls_t **ucp) |
| { |
| struct sonode *lso = (struct sonode *)parenthandle; |
| struct sctp_sonode *lss = SOTOSSO(lso); |
| struct sctp_soassoc *ssa; |
| sctp_assoc_t id; |
| |
| ASSERT(lss->ss_type == SOSCTP_SOCKET); |
| ASSERT(lso->so_state & SS_ACCEPTCONN); |
| ASSERT(lso->so_proto_handle != NULL); /* closed conn */ |
| ASSERT(lso->so_type == SOCK_SEQPACKET); |
| |
| mutex_enter(&lso->so_lock); |
| |
| if ((id = sosctp_aid_get(lss)) == -1) { |
| /* |
| * Array not large enough; increase size. |
| */ |
| if (sosctp_aid_grow(lss, lss->ss_maxassoc, KM_NOSLEEP) < 0) { |
| mutex_exit(&lso->so_lock); |
| return (NULL); |
| } |
| id = sosctp_aid_get(lss); |
| ASSERT(id != -1); |
| } |
| |
| /* |
| * Create soassoc for this connection |
| */ |
| ssa = sosctp_assoc_create(lss, KM_NOSLEEP); |
| if (ssa == NULL) { |
| mutex_exit(&lso->so_lock); |
| return (NULL); |
| } |
| sosctp_aid_reserve(lss, id, 1); |
| lss->ss_assocs[id].ssi_assoc = ssa; |
| ++lss->ss_assoccnt; |
| ssa->ssa_id = id; |
| ssa->ssa_conn = (struct sctp_s *)connind; |
| ssa->ssa_state = (SS_ISBOUND | SS_ISCONNECTED); |
| ssa->ssa_wroff = lss->ss_wroff; |
| ssa->ssa_wrsize = lss->ss_wrsize; |
| |
| mutex_exit(&lso->so_lock); |
| |
| *ucp = &sosctp_assoc_upcalls; |
| |
| return ((sock_upper_handle_t)ssa); |
| } |
| |
| /* ARGSUSED */ |
| static void |
| sctp_assoc_connected(sock_upper_handle_t handle, sock_connid_t id, |
| struct cred *peer_cred, pid_t peer_cpid) |
| { |
| struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle; |
| struct sonode *so = &ssa->ssa_sonode->ss_so; |
| |
| ASSERT(so->so_type == SOCK_SEQPACKET); |
| ASSERT(ssa->ssa_conn); |
| |
| mutex_enter(&so->so_lock); |
| sosctp_assoc_isconnected(ssa); |
| mutex_exit(&so->so_lock); |
| } |
| |
| /* ARGSUSED */ |
| static int |
| sctp_assoc_disconnected(sock_upper_handle_t handle, sock_connid_t id, int error) |
| { |
| struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle; |
| struct sonode *so = &ssa->ssa_sonode->ss_so; |
| int ret; |
| |
| ASSERT(so->so_type == SOCK_SEQPACKET); |
| ASSERT(ssa->ssa_conn != NULL); |
| |
| mutex_enter(&so->so_lock); |
| sosctp_assoc_isdisconnected(ssa, error); |
| if (ssa->ssa_refcnt == 1) { |
| ret = 1; |
| ssa->ssa_conn = NULL; |
| } else { |
| ret = 0; |
| } |
| SSA_REFRELE(SOTOSSO(so), ssa); |
| |
| cv_broadcast(&so->so_snd_cv); |
| |
| mutex_exit(&so->so_lock); |
| |
| return (ret); |
| } |
| |
| /* ARGSUSED */ |
| static void |
| sctp_assoc_disconnecting(sock_upper_handle_t handle, sock_opctl_action_t action, |
| uintptr_t arg) |
| { |
| struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle; |
| struct sonode *so = &ssa->ssa_sonode->ss_so; |
| |
| ASSERT(so->so_type == SOCK_SEQPACKET); |
| ASSERT(ssa->ssa_conn != NULL); |
| ASSERT(action == SOCK_OPCTL_SHUT_SEND); |
| |
| mutex_enter(&so->so_lock); |
| sosctp_assoc_isdisconnecting(ssa); |
| mutex_exit(&so->so_lock); |
| } |
| |
| /* ARGSUSED */ |
| static ssize_t |
| sctp_assoc_recv(sock_upper_handle_t handle, mblk_t *mp, size_t len, int flags, |
| int *errorp, boolean_t *forcepush) |
| { |
| struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle; |
| struct sctp_sonode *ss = ssa->ssa_sonode; |
| struct sonode *so = &ss->ss_so; |
| struct T_unitdata_ind *tind; |
| mblk_t *mp2; |
| union sctp_notification *sn; |
| struct sctp_sndrcvinfo *sinfo; |
| |
| ASSERT(ssa->ssa_type == SOSCTP_ASSOC); |
| ASSERT(so->so_type == SOCK_SEQPACKET); |
| ASSERT(ssa->ssa_conn != NULL); /* closed conn */ |
| ASSERT(mp != NULL); |
| |
| ASSERT(errorp != NULL); |
| *errorp = 0; |
| |
| /* |
| * Should be getting T_unitdata_req's only. |
| * Must have address as part of packet. |
| */ |
| tind = (struct T_unitdata_ind *)mp->b_rptr; |
| ASSERT((DB_TYPE(mp) == M_PROTO) && |
| (tind->PRIM_type == T_UNITDATA_IND)); |
| ASSERT(tind->SRC_length); |
| |
| mutex_enter(&so->so_lock); |
| |
| /* |
| * Override b_flag for SCTP sockfs internal use |
| */ |
| mp->b_flag = (short)flags; |
| |
| /* |
| * For notify messages, need to fill in association id. |
| * For data messages, sndrcvinfo could be in ancillary data. |
| */ |
| if (flags & SCTP_NOTIFICATION) { |
| mp2 = mp->b_cont; |
| sn = (union sctp_notification *)mp2->b_rptr; |
| switch (sn->sn_header.sn_type) { |
| case SCTP_ASSOC_CHANGE: |
| sn->sn_assoc_change.sac_assoc_id = ssa->ssa_id; |
| break; |
| case SCTP_PEER_ADDR_CHANGE: |
| sn->sn_paddr_change.spc_assoc_id = ssa->ssa_id; |
| break; |
| case SCTP_REMOTE_ERROR: |
| sn->sn_remote_error.sre_assoc_id = ssa->ssa_id; |
| break; |
| case SCTP_SEND_FAILED: |
| sn->sn_send_failed.ssf_assoc_id = ssa->ssa_id; |
| break; |
| case SCTP_SHUTDOWN_EVENT: |
| sn->sn_shutdown_event.sse_assoc_id = ssa->ssa_id; |
| break; |
| case SCTP_ADAPTATION_INDICATION: |
| sn->sn_adaptation_event.sai_assoc_id = ssa->ssa_id; |
| break; |
| case SCTP_PARTIAL_DELIVERY_EVENT: |
| sn->sn_pdapi_event.pdapi_assoc_id = ssa->ssa_id; |
| break; |
| default: |
| ASSERT(0); |
| break; |
| } |
| } else { |
| if (tind->OPT_length > 0) { |
| struct cmsghdr *cmsg; |
| char *cend; |
| |
| cmsg = (struct cmsghdr *) |
| ((uchar_t *)mp->b_rptr + tind->OPT_offset); |
| cend = (char *)cmsg + tind->OPT_length; |
| for (;;) { |
| if ((char *)(cmsg + 1) > cend || |
| ((char *)cmsg + cmsg->cmsg_len) > cend) { |
| break; |
| } |
| if ((cmsg->cmsg_level == IPPROTO_SCTP) && |
| (cmsg->cmsg_type == SCTP_SNDRCV)) { |
| sinfo = (struct sctp_sndrcvinfo *) |
| (cmsg + 1); |
| sinfo->sinfo_assoc_id = ssa->ssa_id; |
| break; |
| } |
| if (cmsg->cmsg_len > 0) { |
| cmsg = (struct cmsghdr *) |
| ((uchar_t *)cmsg + cmsg->cmsg_len); |
| } else { |
| break; |
| } |
| } |
| } |
| } |
| |
| /* |
| * SCTP has reserved space in the header for storing a pointer. |
| * Put the pointer to assocation there, and queue the data. |
| */ |
| SSA_REFHOLD(ssa); |
| ASSERT((mp->b_rptr - DB_BASE(mp)) >= sizeof (ssa)); |
| *(struct sctp_soassoc **)DB_BASE(mp) = ssa; |
| |
| mutex_exit(&so->so_lock); |
| |
| return (so_queue_msg((sock_upper_handle_t)so, mp, len, 0, errorp, |
| NULL)); |
| } |
| |
| static void |
| sctp_assoc_xmitted(sock_upper_handle_t handle, boolean_t qfull) |
| { |
| struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle; |
| struct sctp_sonode *ss = ssa->ssa_sonode; |
| |
| ASSERT(ssa->ssa_type == SOSCTP_ASSOC); |
| ASSERT(ss->ss_so.so_type == SOCK_SEQPACKET); |
| ASSERT(ssa->ssa_conn != NULL); |
| |
| mutex_enter(&ss->ss_so.so_lock); |
| |
| ssa->ssa_snd_qfull = qfull; |
| |
| /* |
| * Wake blocked writers. |
| */ |
| cv_broadcast(&ss->ss_so.so_snd_cv); |
| |
| mutex_exit(&ss->ss_so.so_lock); |
| } |
| |
| static void |
| sctp_assoc_properties(sock_upper_handle_t handle, |
| struct sock_proto_props *soppp) |
| { |
| struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle; |
| struct sctp_sonode *ss; |
| |
| if (ssa->ssa_type == SOSCTP_ASSOC) { |
| ss = ssa->ssa_sonode; |
| mutex_enter(&ss->ss_so.so_lock); |
| |
| /* |
| * Only change them if they're set. |
| */ |
| if (soppp->sopp_wroff != 0) { |
| ssa->ssa_wroff = soppp->sopp_wroff; |
| } |
| if (soppp->sopp_maxblk != 0) { |
| ssa->ssa_wrsize = soppp->sopp_maxblk; |
| } |
| } else { |
| ss = (struct sctp_sonode *)handle; |
| mutex_enter(&ss->ss_so.so_lock); |
| |
| if (soppp->sopp_wroff != 0) { |
| ss->ss_wroff = soppp->sopp_wroff; |
| } |
| if (soppp->sopp_maxblk != 0) { |
| ss->ss_wrsize = soppp->sopp_maxblk; |
| } |
| } |
| |
| mutex_exit(&ss->ss_so.so_lock); |
| } |