xref: /onnv-gate/usr/src/uts/common/inet/sockmods/socksctp.c (revision 11042:2d6e217af1b4)
18348SEric.Yu@Sun.COM /*
28348SEric.Yu@Sun.COM  * CDDL HEADER START
38348SEric.Yu@Sun.COM  *
48348SEric.Yu@Sun.COM  * The contents of this file are subject to the terms of the
58348SEric.Yu@Sun.COM  * Common Development and Distribution License (the "License").
68348SEric.Yu@Sun.COM  * You may not use this file except in compliance with the License.
78348SEric.Yu@Sun.COM  *
88348SEric.Yu@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98348SEric.Yu@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108348SEric.Yu@Sun.COM  * See the License for the specific language governing permissions
118348SEric.Yu@Sun.COM  * and limitations under the License.
128348SEric.Yu@Sun.COM  *
138348SEric.Yu@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148348SEric.Yu@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158348SEric.Yu@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168348SEric.Yu@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178348SEric.Yu@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188348SEric.Yu@Sun.COM  *
198348SEric.Yu@Sun.COM  * CDDL HEADER END
208348SEric.Yu@Sun.COM  */
218348SEric.Yu@Sun.COM 
228348SEric.Yu@Sun.COM /*
238778SErik.Nordmark@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
248348SEric.Yu@Sun.COM  * Use is subject to license terms.
258348SEric.Yu@Sun.COM  */
268348SEric.Yu@Sun.COM 
278348SEric.Yu@Sun.COM #include <sys/types.h>
288348SEric.Yu@Sun.COM #include <sys/t_lock.h>
298348SEric.Yu@Sun.COM #include <sys/param.h>
308348SEric.Yu@Sun.COM #include <sys/systm.h>
318348SEric.Yu@Sun.COM #include <sys/buf.h>
328348SEric.Yu@Sun.COM #include <sys/vfs.h>
338348SEric.Yu@Sun.COM #include <sys/vnode.h>
348348SEric.Yu@Sun.COM #include <sys/debug.h>
358348SEric.Yu@Sun.COM #include <sys/errno.h>
368348SEric.Yu@Sun.COM #include <sys/stropts.h>
378348SEric.Yu@Sun.COM #include <sys/cmn_err.h>
388348SEric.Yu@Sun.COM #include <sys/sysmacros.h>
398348SEric.Yu@Sun.COM #include <sys/filio.h>
408348SEric.Yu@Sun.COM 
418348SEric.Yu@Sun.COM #include <sys/project.h>
428348SEric.Yu@Sun.COM #include <sys/tihdr.h>
438348SEric.Yu@Sun.COM #include <sys/strsubr.h>
448348SEric.Yu@Sun.COM #include <sys/esunddi.h>
458348SEric.Yu@Sun.COM #include <sys/ddi.h>
468348SEric.Yu@Sun.COM 
478348SEric.Yu@Sun.COM #include <sys/sockio.h>
488348SEric.Yu@Sun.COM #include <sys/socket.h>
498348SEric.Yu@Sun.COM #include <sys/socketvar.h>
508348SEric.Yu@Sun.COM #include <sys/strsun.h>
518348SEric.Yu@Sun.COM 
528348SEric.Yu@Sun.COM #include <netinet/sctp.h>
538348SEric.Yu@Sun.COM #include <inet/sctp_itf.h>
548348SEric.Yu@Sun.COM #include <fs/sockfs/sockcommon.h>
558348SEric.Yu@Sun.COM #include "socksctp.h"
568348SEric.Yu@Sun.COM 
578348SEric.Yu@Sun.COM /*
588348SEric.Yu@Sun.COM  * SCTP sockfs sonode operations, 1-1 socket
598348SEric.Yu@Sun.COM  */
608348SEric.Yu@Sun.COM static int sosctp_init(struct sonode *, struct sonode *, struct cred *, int);
618348SEric.Yu@Sun.COM static int sosctp_accept(struct sonode *, int, struct cred *, struct sonode **);
628348SEric.Yu@Sun.COM static int sosctp_bind(struct sonode *, struct sockaddr *, socklen_t, int,
638348SEric.Yu@Sun.COM     struct cred *);
648348SEric.Yu@Sun.COM static int sosctp_listen(struct sonode *, int, struct cred *);
658348SEric.Yu@Sun.COM static int sosctp_connect(struct sonode *, const struct sockaddr *, socklen_t,
668348SEric.Yu@Sun.COM     int, int, struct cred *);
678348SEric.Yu@Sun.COM static int sosctp_recvmsg(struct sonode *, struct nmsghdr *, struct uio *,
688348SEric.Yu@Sun.COM     struct cred *);
698348SEric.Yu@Sun.COM static int sosctp_sendmsg(struct sonode *, struct nmsghdr *, struct uio *,
708348SEric.Yu@Sun.COM     struct cred *);
718348SEric.Yu@Sun.COM static int sosctp_getpeername(struct sonode *, struct sockaddr *, socklen_t *,
728348SEric.Yu@Sun.COM     boolean_t, struct cred *);
738348SEric.Yu@Sun.COM static int sosctp_getsockname(struct sonode *, struct sockaddr *, socklen_t *,
748348SEric.Yu@Sun.COM     struct cred *);
758348SEric.Yu@Sun.COM static int sosctp_shutdown(struct sonode *, int, struct cred *);
768348SEric.Yu@Sun.COM static int sosctp_getsockopt(struct sonode *, int, int, void *, socklen_t *,
778348SEric.Yu@Sun.COM     int, struct cred *);
788348SEric.Yu@Sun.COM static int sosctp_setsockopt(struct sonode *, int, int, const void *,
798348SEric.Yu@Sun.COM     socklen_t, struct cred *);
808348SEric.Yu@Sun.COM static int sosctp_ioctl(struct sonode *, int, intptr_t, int, struct cred *,
818348SEric.Yu@Sun.COM     int32_t *);
828348SEric.Yu@Sun.COM static int sosctp_close(struct sonode *, int, struct cred *);
838348SEric.Yu@Sun.COM void sosctp_fini(struct sonode *, struct cred *);
848348SEric.Yu@Sun.COM 
858348SEric.Yu@Sun.COM /*
868348SEric.Yu@Sun.COM  * SCTP sockfs sonode operations, 1-N socket
878348SEric.Yu@Sun.COM  */
888348SEric.Yu@Sun.COM static int sosctp_seq_connect(struct sonode *, const struct sockaddr *,
898348SEric.Yu@Sun.COM     socklen_t, int, int, struct cred *);
908348SEric.Yu@Sun.COM static int sosctp_seq_sendmsg(struct sonode *, struct nmsghdr *, struct uio *,
918348SEric.Yu@Sun.COM     struct cred *);
928348SEric.Yu@Sun.COM 
938348SEric.Yu@Sun.COM /*
948348SEric.Yu@Sun.COM  * Socket association upcalls, 1-N socket connection
958348SEric.Yu@Sun.COM  */
968348SEric.Yu@Sun.COM sock_upper_handle_t sctp_assoc_newconn(sock_upper_handle_t,
978348SEric.Yu@Sun.COM     sock_lower_handle_t, sock_downcalls_t *, struct cred *, pid_t,
988348SEric.Yu@Sun.COM     sock_upcalls_t **);
998348SEric.Yu@Sun.COM static void sctp_assoc_connected(sock_upper_handle_t, sock_connid_t,
1008348SEric.Yu@Sun.COM     struct cred *, pid_t);
1018348SEric.Yu@Sun.COM static int sctp_assoc_disconnected(sock_upper_handle_t, sock_connid_t, int);
1028348SEric.Yu@Sun.COM static void sctp_assoc_disconnecting(sock_upper_handle_t, sock_opctl_action_t,
1038348SEric.Yu@Sun.COM     uintptr_t arg);
1048348SEric.Yu@Sun.COM static ssize_t sctp_assoc_recv(sock_upper_handle_t, mblk_t *, size_t, int,
1058348SEric.Yu@Sun.COM     int *, boolean_t *);
1068348SEric.Yu@Sun.COM static void sctp_assoc_xmitted(sock_upper_handle_t, boolean_t);
1078348SEric.Yu@Sun.COM static void sctp_assoc_properties(sock_upper_handle_t,
1088348SEric.Yu@Sun.COM     struct sock_proto_props *);
1098348SEric.Yu@Sun.COM 
1108348SEric.Yu@Sun.COM sonodeops_t sosctp_sonodeops = {
1118348SEric.Yu@Sun.COM 	sosctp_init,			/* sop_init	*/
1128348SEric.Yu@Sun.COM 	sosctp_accept,			/* sop_accept	*/
1138348SEric.Yu@Sun.COM 	sosctp_bind,			/* sop_bind	*/
1148348SEric.Yu@Sun.COM 	sosctp_listen,			/* sop_listen	*/
1158348SEric.Yu@Sun.COM 	sosctp_connect,			/* sop_connect	*/
1168348SEric.Yu@Sun.COM 	sosctp_recvmsg,			/* sop_recvmsg	*/
1178348SEric.Yu@Sun.COM 	sosctp_sendmsg,			/* sop_sendmsg	*/
1188348SEric.Yu@Sun.COM 	so_sendmblk_notsupp,		/* sop_sendmblk	*/
1198348SEric.Yu@Sun.COM 	sosctp_getpeername,		/* sop_getpeername */
1208348SEric.Yu@Sun.COM 	sosctp_getsockname,		/* sop_getsockname */
1218348SEric.Yu@Sun.COM 	sosctp_shutdown,		/* sop_shutdown */
1228348SEric.Yu@Sun.COM 	sosctp_getsockopt,		/* sop_getsockopt */
1238348SEric.Yu@Sun.COM 	sosctp_setsockopt,		/* sop_setsockopt */
1248348SEric.Yu@Sun.COM 	sosctp_ioctl,			/* sop_ioctl	*/
1258348SEric.Yu@Sun.COM 	so_poll,			/* sop_poll	*/
1268348SEric.Yu@Sun.COM 	sosctp_close,			/* sop_close 	*/
1278348SEric.Yu@Sun.COM };
1288348SEric.Yu@Sun.COM 
1298348SEric.Yu@Sun.COM sonodeops_t sosctp_seq_sonodeops = {
1308348SEric.Yu@Sun.COM 	sosctp_init,			/* sop_init	*/
1318348SEric.Yu@Sun.COM 	so_accept_notsupp,		/* sop_accept	*/
1328348SEric.Yu@Sun.COM 	sosctp_bind,			/* sop_bind	*/
1338348SEric.Yu@Sun.COM 	sosctp_listen,			/* sop_listen	*/
1348348SEric.Yu@Sun.COM 	sosctp_seq_connect,		/* sop_connect	*/
1358348SEric.Yu@Sun.COM 	sosctp_recvmsg,			/* sop_recvmsg	*/
1368348SEric.Yu@Sun.COM 	sosctp_seq_sendmsg,		/* sop_sendmsg	*/
1378348SEric.Yu@Sun.COM 	so_sendmblk_notsupp,		/* sop_sendmblk	*/
1388348SEric.Yu@Sun.COM 	so_getpeername_notsupp,		/* sop_getpeername */
1398348SEric.Yu@Sun.COM 	sosctp_getsockname,		/* sop_getsockname */
1408348SEric.Yu@Sun.COM 	so_shutdown_notsupp,		/* sop_shutdown */
1418348SEric.Yu@Sun.COM 	sosctp_getsockopt,		/* sop_getsockopt */
1428348SEric.Yu@Sun.COM 	sosctp_setsockopt,		/* sop_setsockopt */
1438348SEric.Yu@Sun.COM 	sosctp_ioctl,			/* sop_ioctl	*/
1448348SEric.Yu@Sun.COM 	so_poll,			/* sop_poll	*/
1458348SEric.Yu@Sun.COM 	sosctp_close,			/* sop_close 	*/
1468348SEric.Yu@Sun.COM };
1478348SEric.Yu@Sun.COM 
1488348SEric.Yu@Sun.COM sock_upcalls_t sosctp_sock_upcalls = {
1498348SEric.Yu@Sun.COM 	so_newconn,
1508348SEric.Yu@Sun.COM 	so_connected,
1518348SEric.Yu@Sun.COM 	so_disconnected,
1528348SEric.Yu@Sun.COM 	so_opctl,
1538348SEric.Yu@Sun.COM 	so_queue_msg,
1548348SEric.Yu@Sun.COM 	so_set_prop,
1558348SEric.Yu@Sun.COM 	so_txq_full,
1568348SEric.Yu@Sun.COM 	NULL,			/* su_signal_oob */
1578348SEric.Yu@Sun.COM };
1588348SEric.Yu@Sun.COM 
1598348SEric.Yu@Sun.COM sock_upcalls_t sosctp_assoc_upcalls = {
1608348SEric.Yu@Sun.COM 	sctp_assoc_newconn,
1618348SEric.Yu@Sun.COM 	sctp_assoc_connected,
1628348SEric.Yu@Sun.COM 	sctp_assoc_disconnected,
1638348SEric.Yu@Sun.COM 	sctp_assoc_disconnecting,
1648348SEric.Yu@Sun.COM 	sctp_assoc_recv,
1658348SEric.Yu@Sun.COM 	sctp_assoc_properties,
1668348SEric.Yu@Sun.COM 	sctp_assoc_xmitted,
1678348SEric.Yu@Sun.COM 	NULL,			/* su_recv_space */
1688348SEric.Yu@Sun.COM 	NULL,			/* su_signal_oob */
1698348SEric.Yu@Sun.COM };
1708348SEric.Yu@Sun.COM 
1718348SEric.Yu@Sun.COM /* ARGSUSED */
1728348SEric.Yu@Sun.COM static int
1738348SEric.Yu@Sun.COM sosctp_init(struct sonode *so, struct sonode *pso, struct cred *cr, int flags)
1748348SEric.Yu@Sun.COM {
1758348SEric.Yu@Sun.COM 	struct sctp_sonode *ss;
1768348SEric.Yu@Sun.COM 	struct sctp_sonode *pss;
1778348SEric.Yu@Sun.COM 	sctp_sockbuf_limits_t sbl;
1788348SEric.Yu@Sun.COM 	sock_upcalls_t *upcalls;
1798348SEric.Yu@Sun.COM 
1808348SEric.Yu@Sun.COM 	ss = SOTOSSO(so);
1818348SEric.Yu@Sun.COM 
1828348SEric.Yu@Sun.COM 	if (pso != NULL) {
1838348SEric.Yu@Sun.COM 		/*
1848348SEric.Yu@Sun.COM 		 * Passive open, just inherit settings from parent. We should
1858348SEric.Yu@Sun.COM 		 * not end up here for SOCK_SEQPACKET type sockets, since no
1868348SEric.Yu@Sun.COM 		 * new sonode is created in that case.
1878348SEric.Yu@Sun.COM 		 */
1888348SEric.Yu@Sun.COM 		ASSERT(so->so_type == SOCK_STREAM);
1898348SEric.Yu@Sun.COM 		pss = SOTOSSO(pso);
1908348SEric.Yu@Sun.COM 
1918348SEric.Yu@Sun.COM 		mutex_enter(&pso->so_lock);
1928348SEric.Yu@Sun.COM 		so->so_state |= (SS_ISBOUND | SS_ISCONNECTED |
1938348SEric.Yu@Sun.COM 		    (pso->so_state & SS_ASYNC));
1948348SEric.Yu@Sun.COM 		sosctp_so_inherit(pss, ss);
1958348SEric.Yu@Sun.COM 		so->so_proto_props = pso->so_proto_props;
1968348SEric.Yu@Sun.COM 		so->so_mode = pso->so_mode;
1978348SEric.Yu@Sun.COM 		mutex_exit(&pso->so_lock);
1988348SEric.Yu@Sun.COM 
1998348SEric.Yu@Sun.COM 		return (0);
2008348SEric.Yu@Sun.COM 	}
2018348SEric.Yu@Sun.COM 
2028348SEric.Yu@Sun.COM 	if (so->so_type == SOCK_STREAM) {
2038348SEric.Yu@Sun.COM 		upcalls = &sosctp_sock_upcalls;
2048348SEric.Yu@Sun.COM 		so->so_mode = SM_CONNREQUIRED;
2058348SEric.Yu@Sun.COM 	} else {
2068348SEric.Yu@Sun.COM 		ASSERT(so->so_type == SOCK_SEQPACKET);
2078348SEric.Yu@Sun.COM 		upcalls = &sosctp_assoc_upcalls;
2088348SEric.Yu@Sun.COM 	}
2098348SEric.Yu@Sun.COM 	so->so_proto_handle = (sock_lower_handle_t)sctp_create(so, NULL,
210*11042SErik.Nordmark@Sun.COM 	    so->so_family, so->so_type, SCTP_CAN_BLOCK, upcalls, &sbl, cr);
2118348SEric.Yu@Sun.COM 	if (so->so_proto_handle == NULL)
2128348SEric.Yu@Sun.COM 		return (ENOMEM);
2138348SEric.Yu@Sun.COM 
2148348SEric.Yu@Sun.COM 	so->so_rcvbuf = sbl.sbl_rxbuf;
2158348SEric.Yu@Sun.COM 	so->so_rcvlowat = sbl.sbl_rxlowat;
2168348SEric.Yu@Sun.COM 	so->so_sndbuf = sbl.sbl_txbuf;
2178348SEric.Yu@Sun.COM 	so->so_sndlowat = sbl.sbl_txlowat;
2188348SEric.Yu@Sun.COM 
2198348SEric.Yu@Sun.COM 	return (0);
2208348SEric.Yu@Sun.COM }
2218348SEric.Yu@Sun.COM 
2228348SEric.Yu@Sun.COM /*
2238348SEric.Yu@Sun.COM  * Accept incoming connection.
2248348SEric.Yu@Sun.COM  */
2258348SEric.Yu@Sun.COM /*ARGSUSED*/
2268348SEric.Yu@Sun.COM static int
2278348SEric.Yu@Sun.COM sosctp_accept(struct sonode *so, int fflag, struct cred *cr,
2288348SEric.Yu@Sun.COM     struct sonode **nsop)
2298348SEric.Yu@Sun.COM {
2308348SEric.Yu@Sun.COM 	int error = 0;
2318348SEric.Yu@Sun.COM 
2328348SEric.Yu@Sun.COM 	if ((so->so_state & SS_ACCEPTCONN) == 0)
2338348SEric.Yu@Sun.COM 		return (EINVAL);
2348348SEric.Yu@Sun.COM 
2358348SEric.Yu@Sun.COM 	error = so_acceptq_dequeue(so, (fflag & (FNONBLOCK|FNDELAY)), nsop);
2368348SEric.Yu@Sun.COM 
2378348SEric.Yu@Sun.COM 	return (error);
2388348SEric.Yu@Sun.COM }
2398348SEric.Yu@Sun.COM 
2408348SEric.Yu@Sun.COM /*
2418348SEric.Yu@Sun.COM  * Bind local endpoint.
2428348SEric.Yu@Sun.COM  */
2438348SEric.Yu@Sun.COM /*ARGSUSED*/
2448348SEric.Yu@Sun.COM static int
2458348SEric.Yu@Sun.COM sosctp_bind(struct sonode *so, struct sockaddr *name, socklen_t namelen,
2468348SEric.Yu@Sun.COM     int flags, struct cred *cr)
2478348SEric.Yu@Sun.COM {
2488348SEric.Yu@Sun.COM 	int error;
2498348SEric.Yu@Sun.COM 
2508348SEric.Yu@Sun.COM 	if (!(flags & _SOBIND_LOCK_HELD)) {
2518348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
2528348SEric.Yu@Sun.COM 		so_lock_single(so);	/* Set SOLOCKED */
2538348SEric.Yu@Sun.COM 	} else {
2548348SEric.Yu@Sun.COM 		ASSERT(MUTEX_HELD(&so->so_lock));
2558348SEric.Yu@Sun.COM 	}
2568348SEric.Yu@Sun.COM 
2578348SEric.Yu@Sun.COM 	/*
2588348SEric.Yu@Sun.COM 	 * X/Open requires this check
2598348SEric.Yu@Sun.COM 	 */
2608348SEric.Yu@Sun.COM 	if (so->so_state & SS_CANTSENDMORE) {
2618348SEric.Yu@Sun.COM 		error = EINVAL;
2628348SEric.Yu@Sun.COM 		goto done;
2638348SEric.Yu@Sun.COM 	}
2648348SEric.Yu@Sun.COM 
2658348SEric.Yu@Sun.COM 
2668348SEric.Yu@Sun.COM 	/*
2678348SEric.Yu@Sun.COM 	 * Protocol module does address family checks.
2688348SEric.Yu@Sun.COM 	 */
2698348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
2708348SEric.Yu@Sun.COM 
2718348SEric.Yu@Sun.COM 	error = sctp_bind((struct sctp_s *)so->so_proto_handle, name, namelen);
2728348SEric.Yu@Sun.COM 
2738348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
2748348SEric.Yu@Sun.COM 	if (error == 0) {
2758348SEric.Yu@Sun.COM 		so->so_state |= SS_ISBOUND;
2768348SEric.Yu@Sun.COM 	} else {
2778348SEric.Yu@Sun.COM 		eprintsoline(so, error);
2788348SEric.Yu@Sun.COM 	}
2798348SEric.Yu@Sun.COM done:
2808348SEric.Yu@Sun.COM 	if (!(flags & _SOBIND_LOCK_HELD)) {
2818348SEric.Yu@Sun.COM 		so_unlock_single(so, SOLOCKED);
2828348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
2838348SEric.Yu@Sun.COM 	} else {
2848348SEric.Yu@Sun.COM 		/* If the caller held the lock don't release it here */
2858348SEric.Yu@Sun.COM 		ASSERT(MUTEX_HELD(&so->so_lock));
2868348SEric.Yu@Sun.COM 		ASSERT(so->so_flag & SOLOCKED);
2878348SEric.Yu@Sun.COM 	}
2888348SEric.Yu@Sun.COM 
2898348SEric.Yu@Sun.COM 	return (error);
2908348SEric.Yu@Sun.COM }
2918348SEric.Yu@Sun.COM 
2928348SEric.Yu@Sun.COM /*
2938348SEric.Yu@Sun.COM  * Turn socket into a listen socket.
2948348SEric.Yu@Sun.COM  */
2958348SEric.Yu@Sun.COM /* ARGSUSED */
2968348SEric.Yu@Sun.COM static int
2978348SEric.Yu@Sun.COM sosctp_listen(struct sonode *so, int backlog, struct cred *cr)
2988348SEric.Yu@Sun.COM {
2998348SEric.Yu@Sun.COM 	int error = 0;
3008348SEric.Yu@Sun.COM 
3018348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
3028348SEric.Yu@Sun.COM 	so_lock_single(so);
3038348SEric.Yu@Sun.COM 
3048348SEric.Yu@Sun.COM 	/*
3058348SEric.Yu@Sun.COM 	 * If this socket is trying to do connect, or if it has
3068348SEric.Yu@Sun.COM 	 * been connected, disallow.
3078348SEric.Yu@Sun.COM 	 */
3088348SEric.Yu@Sun.COM 	if (so->so_state & (SS_ISCONNECTING | SS_ISCONNECTED |
3098348SEric.Yu@Sun.COM 	    SS_ISDISCONNECTING | SS_CANTRCVMORE | SS_CANTSENDMORE)) {
3108348SEric.Yu@Sun.COM 		error = EINVAL;
3118348SEric.Yu@Sun.COM 		eprintsoline(so, error);
3128348SEric.Yu@Sun.COM 		goto done;
3138348SEric.Yu@Sun.COM 	}
3148348SEric.Yu@Sun.COM 
3158348SEric.Yu@Sun.COM 	if (backlog < 0) {
3168348SEric.Yu@Sun.COM 		backlog = 0;
3178348SEric.Yu@Sun.COM 	}
3188348SEric.Yu@Sun.COM 
3198348SEric.Yu@Sun.COM 	/*
3208348SEric.Yu@Sun.COM 	 * If listen() is only called to change backlog, we don't
3218348SEric.Yu@Sun.COM 	 * need to notify protocol module.
3228348SEric.Yu@Sun.COM 	 */
3238348SEric.Yu@Sun.COM 	if (so->so_state & SS_ACCEPTCONN) {
3248348SEric.Yu@Sun.COM 		so->so_backlog = backlog;
3258348SEric.Yu@Sun.COM 		goto done;
3268348SEric.Yu@Sun.COM 	}
3278348SEric.Yu@Sun.COM 
3288348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
3298348SEric.Yu@Sun.COM 	error = sctp_listen((struct sctp_s *)so->so_proto_handle);
3308348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
3318348SEric.Yu@Sun.COM 	if (error == 0) {
3328348SEric.Yu@Sun.COM 		so->so_state |= (SS_ACCEPTCONN|SS_ISBOUND);
3338348SEric.Yu@Sun.COM 		so->so_backlog = backlog;
3348348SEric.Yu@Sun.COM 	} else {
3358348SEric.Yu@Sun.COM 		eprintsoline(so, error);
3368348SEric.Yu@Sun.COM 	}
3378348SEric.Yu@Sun.COM done:
3388348SEric.Yu@Sun.COM 	so_unlock_single(so, SOLOCKED);
3398348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
3408348SEric.Yu@Sun.COM 
3418348SEric.Yu@Sun.COM 	return (error);
3428348SEric.Yu@Sun.COM }
3438348SEric.Yu@Sun.COM 
3448348SEric.Yu@Sun.COM /*
3458348SEric.Yu@Sun.COM  * Active open.
3468348SEric.Yu@Sun.COM  */
3478348SEric.Yu@Sun.COM /*ARGSUSED*/
3488348SEric.Yu@Sun.COM static int
3498348SEric.Yu@Sun.COM sosctp_connect(struct sonode *so, const struct sockaddr *name,
3508348SEric.Yu@Sun.COM     socklen_t namelen, int fflag, int flags, struct cred *cr)
3518348SEric.Yu@Sun.COM {
3528348SEric.Yu@Sun.COM 	int error = 0;
353*11042SErik.Nordmark@Sun.COM 	pid_t pid = curproc->p_pid;
3548348SEric.Yu@Sun.COM 
3558348SEric.Yu@Sun.COM 	ASSERT(so->so_type == SOCK_STREAM);
3568348SEric.Yu@Sun.COM 
3578348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
3588348SEric.Yu@Sun.COM 	so_lock_single(so);
3598348SEric.Yu@Sun.COM 
3608348SEric.Yu@Sun.COM 	/*
3618348SEric.Yu@Sun.COM 	 * Can't connect() after listen(), or if the socket is already
3628348SEric.Yu@Sun.COM 	 * connected.
3638348SEric.Yu@Sun.COM 	 */
3648348SEric.Yu@Sun.COM 	if (so->so_state & (SS_ACCEPTCONN|SS_ISCONNECTED|SS_ISCONNECTING)) {
3658348SEric.Yu@Sun.COM 		if (so->so_state & SS_ISCONNECTED) {
3668348SEric.Yu@Sun.COM 			error = EISCONN;
3678348SEric.Yu@Sun.COM 		} else if (so->so_state & SS_ISCONNECTING) {
3688348SEric.Yu@Sun.COM 			error = EALREADY;
3698348SEric.Yu@Sun.COM 		} else {
3708348SEric.Yu@Sun.COM 			error = EOPNOTSUPP;
3718348SEric.Yu@Sun.COM 		}
3728348SEric.Yu@Sun.COM 		eprintsoline(so, error);
3738348SEric.Yu@Sun.COM 		goto done;
3748348SEric.Yu@Sun.COM 	}
3758348SEric.Yu@Sun.COM 
3768348SEric.Yu@Sun.COM 	/*
3778348SEric.Yu@Sun.COM 	 * Check for failure of an earlier call
3788348SEric.Yu@Sun.COM 	 */
3798348SEric.Yu@Sun.COM 	if (so->so_error != 0) {
3808348SEric.Yu@Sun.COM 		error = sogeterr(so, B_TRUE);
3818348SEric.Yu@Sun.COM 		eprintsoline(so, error);
3828348SEric.Yu@Sun.COM 		goto done;
3838348SEric.Yu@Sun.COM 	}
3848348SEric.Yu@Sun.COM 
3858348SEric.Yu@Sun.COM 	/*
3868348SEric.Yu@Sun.COM 	 * Connection is closing, or closed, don't allow reconnect.
3878348SEric.Yu@Sun.COM 	 * TCP allows this to proceed, but the socket remains unwriteable.
3888348SEric.Yu@Sun.COM 	 * BSD returns EINVAL.
3898348SEric.Yu@Sun.COM 	 */
3908348SEric.Yu@Sun.COM 	if (so->so_state & (SS_ISDISCONNECTING|SS_CANTRCVMORE|
3918348SEric.Yu@Sun.COM 	    SS_CANTSENDMORE)) {
3928348SEric.Yu@Sun.COM 		error = EINVAL;
3938348SEric.Yu@Sun.COM 		eprintsoline(so, error);
3948348SEric.Yu@Sun.COM 		goto done;
3958348SEric.Yu@Sun.COM 	}
3968348SEric.Yu@Sun.COM 
3978348SEric.Yu@Sun.COM 	if (name == NULL || namelen == 0) {
3988348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
3998348SEric.Yu@Sun.COM 		error = EINVAL;
4008348SEric.Yu@Sun.COM 		eprintsoline(so, error);
4018348SEric.Yu@Sun.COM 		goto done;
4028348SEric.Yu@Sun.COM 	}
4038348SEric.Yu@Sun.COM 
4048348SEric.Yu@Sun.COM 	soisconnecting(so);
4058348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
4068348SEric.Yu@Sun.COM 
4078348SEric.Yu@Sun.COM 	error = sctp_connect((struct sctp_s *)so->so_proto_handle,
408*11042SErik.Nordmark@Sun.COM 	    name, namelen, cr, pid);
4098348SEric.Yu@Sun.COM 
4108348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
4118348SEric.Yu@Sun.COM 	if (error == 0) {
4128348SEric.Yu@Sun.COM 		/*
4138348SEric.Yu@Sun.COM 		 * Allow other threads to access the socket
4148348SEric.Yu@Sun.COM 		 */
4158348SEric.Yu@Sun.COM 		error = sowaitconnected(so, fflag, 0);
4168348SEric.Yu@Sun.COM 	}
4178348SEric.Yu@Sun.COM done:
4188348SEric.Yu@Sun.COM 	so_unlock_single(so, SOLOCKED);
4198348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
4208348SEric.Yu@Sun.COM 	return (error);
4218348SEric.Yu@Sun.COM }
4228348SEric.Yu@Sun.COM 
4238348SEric.Yu@Sun.COM /*
4248348SEric.Yu@Sun.COM  * Active open for 1-N sockets, create a new association and
4258348SEric.Yu@Sun.COM  * call connect on that.
4268348SEric.Yu@Sun.COM  * If there parent hasn't been bound yet (this is the first association),
4278348SEric.Yu@Sun.COM  * make it so.
4288348SEric.Yu@Sun.COM  */
4298348SEric.Yu@Sun.COM static int
4308348SEric.Yu@Sun.COM sosctp_seq_connect(struct sonode *so, const struct sockaddr *name,
4318348SEric.Yu@Sun.COM     socklen_t namelen, int fflag, int flags, struct cred *cr)
4328348SEric.Yu@Sun.COM {
4338348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa;
4348348SEric.Yu@Sun.COM 	struct sctp_sonode *ss;
4358348SEric.Yu@Sun.COM 	int error;
4368348SEric.Yu@Sun.COM 
4378348SEric.Yu@Sun.COM 	ASSERT(so->so_type == SOCK_SEQPACKET);
4388348SEric.Yu@Sun.COM 
4398348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
4408348SEric.Yu@Sun.COM 	so_lock_single(so);
4418348SEric.Yu@Sun.COM 
4428348SEric.Yu@Sun.COM 	if (name == NULL || namelen == 0) {
4438348SEric.Yu@Sun.COM 		error = EINVAL;
4448348SEric.Yu@Sun.COM 		eprintsoline(so, error);
4458348SEric.Yu@Sun.COM 		goto done;
4468348SEric.Yu@Sun.COM 	}
4478348SEric.Yu@Sun.COM 
4488348SEric.Yu@Sun.COM 	ss = SOTOSSO(so);
4498348SEric.Yu@Sun.COM 
4508348SEric.Yu@Sun.COM 	error = sosctp_assoc_createconn(ss, name, namelen, NULL, 0, fflag,
4518348SEric.Yu@Sun.COM 	    cr, &ssa);
4528348SEric.Yu@Sun.COM 	if (error != 0) {
4538348SEric.Yu@Sun.COM 		if ((error == EHOSTUNREACH) && (flags & _SOCONNECT_XPG4_2)) {
4548348SEric.Yu@Sun.COM 			error = ENETUNREACH;
4558348SEric.Yu@Sun.COM 		}
4568348SEric.Yu@Sun.COM 	}
4578348SEric.Yu@Sun.COM 	if (ssa != NULL) {
4588348SEric.Yu@Sun.COM 		SSA_REFRELE(ss, ssa);
4598348SEric.Yu@Sun.COM 	}
4608348SEric.Yu@Sun.COM 
4618348SEric.Yu@Sun.COM done:
4628348SEric.Yu@Sun.COM 	so_unlock_single(so, SOLOCKED);
4638348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
4648348SEric.Yu@Sun.COM 	return (error);
4658348SEric.Yu@Sun.COM }
4668348SEric.Yu@Sun.COM 
4678348SEric.Yu@Sun.COM /*
4688348SEric.Yu@Sun.COM  * Receive data.
4698348SEric.Yu@Sun.COM  */
4708348SEric.Yu@Sun.COM /* ARGSUSED */
4718348SEric.Yu@Sun.COM static int
4728348SEric.Yu@Sun.COM sosctp_recvmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
4738348SEric.Yu@Sun.COM     struct cred *cr)
4748348SEric.Yu@Sun.COM {
4758348SEric.Yu@Sun.COM 	struct sctp_sonode *ss = SOTOSSO(so);
4768348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa = NULL;
4778348SEric.Yu@Sun.COM 	int flags, error = 0;
4788348SEric.Yu@Sun.COM 	struct T_unitdata_ind *tind;
4798941SAnders.Persson@Sun.COM 	ssize_t orig_resid = uiop->uio_resid;
4808348SEric.Yu@Sun.COM 	int len, count, readcnt = 0, rxqueued;
4818348SEric.Yu@Sun.COM 	socklen_t controllen, namelen;
4828348SEric.Yu@Sun.COM 	void *opt;
4838348SEric.Yu@Sun.COM 	mblk_t *mp;
4848348SEric.Yu@Sun.COM 	rval_t	rval;
4858348SEric.Yu@Sun.COM 
4868348SEric.Yu@Sun.COM 	controllen = msg->msg_controllen;
4878348SEric.Yu@Sun.COM 	namelen = msg->msg_namelen;
4888348SEric.Yu@Sun.COM 	flags = msg->msg_flags;
4898348SEric.Yu@Sun.COM 	msg->msg_flags = 0;
4908348SEric.Yu@Sun.COM 	msg->msg_controllen = 0;
4918348SEric.Yu@Sun.COM 	msg->msg_namelen = 0;
4928348SEric.Yu@Sun.COM 
4938348SEric.Yu@Sun.COM 	if (so->so_type == SOCK_STREAM) {
4948348SEric.Yu@Sun.COM 		if (!(so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING|
4958348SEric.Yu@Sun.COM 		    SS_CANTRCVMORE))) {
4968348SEric.Yu@Sun.COM 			return (ENOTCONN);
4978348SEric.Yu@Sun.COM 		}
4988348SEric.Yu@Sun.COM 	} else {
4998348SEric.Yu@Sun.COM 		/* NOTE: Will come here from vop_read() as well */
5008348SEric.Yu@Sun.COM 		/* For 1-N socket, recv() cannot be used. */
5018348SEric.Yu@Sun.COM 		if (namelen == 0)
5028348SEric.Yu@Sun.COM 			return (EOPNOTSUPP);
5038348SEric.Yu@Sun.COM 		/*
5048348SEric.Yu@Sun.COM 		 * If there are no associations, and no new connections are
5058348SEric.Yu@Sun.COM 		 * coming in, there's not going to be new messages coming
5068348SEric.Yu@Sun.COM 		 * in either.
5078348SEric.Yu@Sun.COM 		 */
5088941SAnders.Persson@Sun.COM 		if (so->so_rcv_q_head == NULL && so->so_rcv_head == NULL &&
5098941SAnders.Persson@Sun.COM 		    ss->ss_assoccnt == 0 && !(so->so_state & SS_ACCEPTCONN)) {
5108348SEric.Yu@Sun.COM 			return (ENOTCONN);
5118348SEric.Yu@Sun.COM 		}
5128348SEric.Yu@Sun.COM 	}
5138348SEric.Yu@Sun.COM 
5148348SEric.Yu@Sun.COM 	/*
5158348SEric.Yu@Sun.COM 	 * out-of-band data not supported.
5168348SEric.Yu@Sun.COM 	 */
5178348SEric.Yu@Sun.COM 	if (flags & MSG_OOB) {
5188348SEric.Yu@Sun.COM 		return (EOPNOTSUPP);
5198348SEric.Yu@Sun.COM 	}
5208348SEric.Yu@Sun.COM 
5218348SEric.Yu@Sun.COM 	/*
5228348SEric.Yu@Sun.COM 	 * flag possibilities:
5238348SEric.Yu@Sun.COM 	 *
5248348SEric.Yu@Sun.COM 	 * MSG_PEEK	Don't consume data
5258348SEric.Yu@Sun.COM 	 * MSG_WAITALL	Wait for full quantity of data (ignored if MSG_PEEK)
5268348SEric.Yu@Sun.COM 	 * MSG_DONTWAIT Non-blocking (same as FNDELAY | FNONBLOCK)
5278348SEric.Yu@Sun.COM 	 *
5288348SEric.Yu@Sun.COM 	 * MSG_WAITALL can return less than the full buffer if either
5298348SEric.Yu@Sun.COM 	 *
5308348SEric.Yu@Sun.COM 	 * 1. we would block and we are non-blocking
5318348SEric.Yu@Sun.COM 	 * 2. a full message cannot be delivered
5328348SEric.Yu@Sun.COM 	 *
5338348SEric.Yu@Sun.COM 	 * Given that we always get a full message from proto below,
5348348SEric.Yu@Sun.COM 	 * MSG_WAITALL is not meaningful.
5358348SEric.Yu@Sun.COM 	 */
5368348SEric.Yu@Sun.COM 
5378348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
5388348SEric.Yu@Sun.COM 
5398348SEric.Yu@Sun.COM 	/*
5408348SEric.Yu@Sun.COM 	 * Allow just one reader at a time.
5418348SEric.Yu@Sun.COM 	 */
5428348SEric.Yu@Sun.COM 	error = so_lock_read_intr(so,
5438348SEric.Yu@Sun.COM 	    uiop->uio_fmode | ((flags & MSG_DONTWAIT) ? FNONBLOCK : 0));
5448348SEric.Yu@Sun.COM 	if (error) {
5458348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
5468348SEric.Yu@Sun.COM 		return (error);
5478348SEric.Yu@Sun.COM 	}
5488348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
5498348SEric.Yu@Sun.COM again:
5508348SEric.Yu@Sun.COM 	error = so_dequeue_msg(so, &mp, uiop, &rval, flags | MSG_DUPCTRL);
5518348SEric.Yu@Sun.COM 	if (mp != NULL) {
5528348SEric.Yu@Sun.COM 		if (so->so_type == SOCK_SEQPACKET) {
5538348SEric.Yu@Sun.COM 			ssa = *(struct sctp_soassoc **)DB_BASE(mp);
5548348SEric.Yu@Sun.COM 		}
5558348SEric.Yu@Sun.COM 
5568348SEric.Yu@Sun.COM 		tind = (struct T_unitdata_ind *)mp->b_rptr;
5578348SEric.Yu@Sun.COM 
5588348SEric.Yu@Sun.COM 		len = tind->SRC_length;
5598348SEric.Yu@Sun.COM 
5608348SEric.Yu@Sun.COM 		if (namelen > 0 && len > 0) {
5618348SEric.Yu@Sun.COM 
5628348SEric.Yu@Sun.COM 			opt = sogetoff(mp, tind->SRC_offset, len, 1);
5638348SEric.Yu@Sun.COM 
5648348SEric.Yu@Sun.COM 			ASSERT(opt != NULL);
5658348SEric.Yu@Sun.COM 
5668348SEric.Yu@Sun.COM 			msg->msg_name = kmem_alloc(len, KM_SLEEP);
5678348SEric.Yu@Sun.COM 			msg->msg_namelen = len;
5688348SEric.Yu@Sun.COM 
5698348SEric.Yu@Sun.COM 			bcopy(opt, msg->msg_name, len);
5708348SEric.Yu@Sun.COM 		}
5718348SEric.Yu@Sun.COM 
5728348SEric.Yu@Sun.COM 		len = tind->OPT_length;
5738348SEric.Yu@Sun.COM 		if (controllen == 0) {
5748348SEric.Yu@Sun.COM 			if (len > 0) {
5758348SEric.Yu@Sun.COM 				msg->msg_flags |= MSG_CTRUNC;
5768348SEric.Yu@Sun.COM 			}
5778348SEric.Yu@Sun.COM 		} else if (len > 0) {
5788348SEric.Yu@Sun.COM 			opt = sogetoff(mp, tind->OPT_offset, len,
5798348SEric.Yu@Sun.COM 			    __TPI_ALIGN_SIZE);
5808348SEric.Yu@Sun.COM 
5818348SEric.Yu@Sun.COM 			ASSERT(opt != NULL);
5828348SEric.Yu@Sun.COM 			sosctp_pack_cmsg(opt, msg, len);
5838348SEric.Yu@Sun.COM 		}
5848348SEric.Yu@Sun.COM 
5858348SEric.Yu@Sun.COM 		if (mp->b_flag & SCTP_NOTIFICATION) {
5868348SEric.Yu@Sun.COM 			msg->msg_flags |= MSG_NOTIFICATION;
5878348SEric.Yu@Sun.COM 		}
5888348SEric.Yu@Sun.COM 
5898348SEric.Yu@Sun.COM 		if (!(mp->b_flag & SCTP_PARTIAL_DATA))
5908348SEric.Yu@Sun.COM 			msg->msg_flags |= MSG_EOR;
5918348SEric.Yu@Sun.COM 		freemsg(mp);
5928348SEric.Yu@Sun.COM 	}
5938348SEric.Yu@Sun.COM done:
5948941SAnders.Persson@Sun.COM 	if (!(flags & MSG_PEEK))
5958941SAnders.Persson@Sun.COM 		readcnt = orig_resid - uiop->uio_resid;
5968348SEric.Yu@Sun.COM 	/*
5978348SEric.Yu@Sun.COM 	 * Determine if we need to update SCTP about the buffer
5988348SEric.Yu@Sun.COM 	 * space.  For performance reason, we cannot update SCTP
5998348SEric.Yu@Sun.COM 	 * every time a message is read.  The socket buffer low
6008348SEric.Yu@Sun.COM 	 * watermark is used as the threshold.
6018348SEric.Yu@Sun.COM 	 */
6028348SEric.Yu@Sun.COM 	if (ssa == NULL) {
6038348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
6048348SEric.Yu@Sun.COM 		rxqueued = so->so_rcv_queued;
6058348SEric.Yu@Sun.COM 		count = so->so_rcvbuf - so->so_rcv_queued;
6068348SEric.Yu@Sun.COM 
6078348SEric.Yu@Sun.COM 		ASSERT(so->so_rcv_q_head != NULL ||
6088348SEric.Yu@Sun.COM 		    so->so_rcv_head != NULL ||
6098348SEric.Yu@Sun.COM 		    so->so_rcv_queued == 0);
6108348SEric.Yu@Sun.COM 
6118348SEric.Yu@Sun.COM 		so_unlock_read(so);
6128348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
6138348SEric.Yu@Sun.COM 
6148348SEric.Yu@Sun.COM 		if (readcnt > 0 && (((count > 0) &&
6158941SAnders.Persson@Sun.COM 		    ((rxqueued + readcnt) >= so->so_rcvlowat)) ||
6168941SAnders.Persson@Sun.COM 		    (rxqueued == 0))) {
6178348SEric.Yu@Sun.COM 			/*
6188348SEric.Yu@Sun.COM 			 * If amount of queued data is higher than watermark,
6198348SEric.Yu@Sun.COM 			 * updata SCTP's idea of available buffer space.
6208348SEric.Yu@Sun.COM 			 */
6218348SEric.Yu@Sun.COM 			sctp_recvd((struct sctp_s *)so->so_proto_handle, count);
6228348SEric.Yu@Sun.COM 		}
6238348SEric.Yu@Sun.COM 	} else {
6248941SAnders.Persson@Sun.COM 		/*
6258941SAnders.Persson@Sun.COM 		 * Each association keeps track of how much data it has
6268941SAnders.Persson@Sun.COM 		 * queued; we need to update the value here. Note that this
6278941SAnders.Persson@Sun.COM 		 * is slightly different from SOCK_STREAM type sockets, which
6288941SAnders.Persson@Sun.COM 		 * does not need to update the byte count, as it is already
6298941SAnders.Persson@Sun.COM 		 * done in so_dequeue_msg().
6308941SAnders.Persson@Sun.COM 		 */
6318348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
6328348SEric.Yu@Sun.COM 		rxqueued = ssa->ssa_rcv_queued;
6338348SEric.Yu@Sun.COM 
6348348SEric.Yu@Sun.COM 		ssa->ssa_rcv_queued = rxqueued - readcnt;
6358348SEric.Yu@Sun.COM 		count = so->so_rcvbuf - ssa->ssa_rcv_queued;
6368348SEric.Yu@Sun.COM 
6378348SEric.Yu@Sun.COM 		so_unlock_read(so);
6388348SEric.Yu@Sun.COM 
6398348SEric.Yu@Sun.COM 		if (readcnt > 0 &&
6408348SEric.Yu@Sun.COM 		    (((count > 0) && (rxqueued >= so->so_rcvlowat)) ||
6418348SEric.Yu@Sun.COM 		    (ssa->ssa_rcv_queued == 0))) {
6428348SEric.Yu@Sun.COM 			/*
6438348SEric.Yu@Sun.COM 			 * If amount of queued data is higher than watermark,
6448348SEric.Yu@Sun.COM 			 * updata SCTP's idea of available buffer space.
6458348SEric.Yu@Sun.COM 			 */
6468348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
6478348SEric.Yu@Sun.COM 
6488348SEric.Yu@Sun.COM 			sctp_recvd((struct sctp_s *)ssa->ssa_conn, count);
6498348SEric.Yu@Sun.COM 
6508348SEric.Yu@Sun.COM 			mutex_enter(&so->so_lock);
6518348SEric.Yu@Sun.COM 		}
6528348SEric.Yu@Sun.COM 		/*
6538348SEric.Yu@Sun.COM 		 * MOREDATA flag is set if all data could not be copied
6548348SEric.Yu@Sun.COM 		 */
6558348SEric.Yu@Sun.COM 		if (!(flags & MSG_PEEK) && !(rval.r_val1 & MOREDATA)) {
6568348SEric.Yu@Sun.COM 			SSA_REFRELE(ss, ssa);
6578348SEric.Yu@Sun.COM 		}
6588348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
6598348SEric.Yu@Sun.COM 	}
6608348SEric.Yu@Sun.COM 
6618348SEric.Yu@Sun.COM 	return (error);
6628348SEric.Yu@Sun.COM }
6638348SEric.Yu@Sun.COM 
6648348SEric.Yu@Sun.COM int
6658348SEric.Yu@Sun.COM sosctp_uiomove(mblk_t *hdr_mp, ssize_t count, ssize_t blk_size, int wroff,
666*11042SErik.Nordmark@Sun.COM     struct uio *uiop, int flags)
6678348SEric.Yu@Sun.COM {
6688348SEric.Yu@Sun.COM 	ssize_t size;
6698348SEric.Yu@Sun.COM 	int error;
6708348SEric.Yu@Sun.COM 	mblk_t *mp;
6718348SEric.Yu@Sun.COM 	dblk_t *dp;
6728348SEric.Yu@Sun.COM 
6739059SErik.Nordmark@Sun.COM 	if (blk_size == INFPSZ)
6749059SErik.Nordmark@Sun.COM 		blk_size = count;
6759059SErik.Nordmark@Sun.COM 
6768348SEric.Yu@Sun.COM 	/*
6778348SEric.Yu@Sun.COM 	 * Loop until we have all data copied into mblk's.
6788348SEric.Yu@Sun.COM 	 */
6798348SEric.Yu@Sun.COM 	while (count > 0) {
6808348SEric.Yu@Sun.COM 		size = MIN(count, blk_size);
6818348SEric.Yu@Sun.COM 
6828348SEric.Yu@Sun.COM 		/*
6838348SEric.Yu@Sun.COM 		 * As a message can be splitted up and sent in different
6848348SEric.Yu@Sun.COM 		 * packets, each mblk will have the extra space before
6858348SEric.Yu@Sun.COM 		 * data to accommodate what SCTP wants to put in there.
6868348SEric.Yu@Sun.COM 		 */
687*11042SErik.Nordmark@Sun.COM 		while ((mp = allocb(size + wroff, BPRI_MED)) == NULL) {
6888348SEric.Yu@Sun.COM 			if ((uiop->uio_fmode & (FNDELAY|FNONBLOCK)) ||
6898348SEric.Yu@Sun.COM 			    (flags & MSG_DONTWAIT)) {
6908348SEric.Yu@Sun.COM 				return (EAGAIN);
6918348SEric.Yu@Sun.COM 			}
6928348SEric.Yu@Sun.COM 			if ((error = strwaitbuf(size + wroff, BPRI_MED))) {
6938348SEric.Yu@Sun.COM 				return (error);
6948348SEric.Yu@Sun.COM 			}
6958348SEric.Yu@Sun.COM 		}
6968348SEric.Yu@Sun.COM 
6978348SEric.Yu@Sun.COM 		dp = mp->b_datap;
6988348SEric.Yu@Sun.COM 		dp->db_cpid = curproc->p_pid;
6998348SEric.Yu@Sun.COM 		ASSERT(wroff <= dp->db_lim - mp->b_wptr);
7008348SEric.Yu@Sun.COM 		mp->b_rptr += wroff;
7018348SEric.Yu@Sun.COM 		error = uiomove(mp->b_rptr, size, UIO_WRITE, uiop);
7028348SEric.Yu@Sun.COM 		if (error != 0) {
7038348SEric.Yu@Sun.COM 			freeb(mp);
7048348SEric.Yu@Sun.COM 			return (error);
7058348SEric.Yu@Sun.COM 		}
7068348SEric.Yu@Sun.COM 		mp->b_wptr = mp->b_rptr + size;
7078348SEric.Yu@Sun.COM 		count -= size;
7088348SEric.Yu@Sun.COM 		hdr_mp->b_cont = mp;
7098348SEric.Yu@Sun.COM 		hdr_mp = mp;
7108348SEric.Yu@Sun.COM 	}
7118348SEric.Yu@Sun.COM 	return (0);
7128348SEric.Yu@Sun.COM }
7138348SEric.Yu@Sun.COM 
7148348SEric.Yu@Sun.COM /*
7158348SEric.Yu@Sun.COM  * Send message.
7168348SEric.Yu@Sun.COM  */
7178348SEric.Yu@Sun.COM static int
7188348SEric.Yu@Sun.COM sosctp_sendmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
7198348SEric.Yu@Sun.COM     struct cred *cr)
7208348SEric.Yu@Sun.COM {
7218348SEric.Yu@Sun.COM 	struct sctp_sonode *ss = SOTOSSO(so);
7228348SEric.Yu@Sun.COM 	mblk_t *mctl;
7238348SEric.Yu@Sun.COM 	struct cmsghdr *cmsg;
7248348SEric.Yu@Sun.COM 	struct sctp_sndrcvinfo *sinfo;
7258348SEric.Yu@Sun.COM 	int optlen, flags, fflag;
7268348SEric.Yu@Sun.COM 	ssize_t count, msglen;
7278348SEric.Yu@Sun.COM 	int error;
7288348SEric.Yu@Sun.COM 
7298348SEric.Yu@Sun.COM 	ASSERT(so->so_type == SOCK_STREAM);
7308348SEric.Yu@Sun.COM 
7318348SEric.Yu@Sun.COM 	flags = msg->msg_flags;
7328348SEric.Yu@Sun.COM 	if (flags & MSG_OOB) {
7338348SEric.Yu@Sun.COM 		/*
7348348SEric.Yu@Sun.COM 		 * No out-of-band data support.
7358348SEric.Yu@Sun.COM 		 */
7368348SEric.Yu@Sun.COM 		return (EOPNOTSUPP);
7378348SEric.Yu@Sun.COM 	}
7388348SEric.Yu@Sun.COM 
7398348SEric.Yu@Sun.COM 	if (msg->msg_controllen != 0) {
7408348SEric.Yu@Sun.COM 		optlen = msg->msg_controllen;
7418348SEric.Yu@Sun.COM 		cmsg = sosctp_find_cmsg(msg->msg_control, optlen, SCTP_SNDRCV);
7428348SEric.Yu@Sun.COM 		if (cmsg != NULL) {
7438348SEric.Yu@Sun.COM 			if (cmsg->cmsg_len <
7448348SEric.Yu@Sun.COM 			    (sizeof (*sinfo) + sizeof (*cmsg))) {
7458348SEric.Yu@Sun.COM 				eprintsoline(so, EINVAL);
7468348SEric.Yu@Sun.COM 				return (EINVAL);
7478348SEric.Yu@Sun.COM 			}
7488348SEric.Yu@Sun.COM 			sinfo = (struct sctp_sndrcvinfo *)(cmsg + 1);
7498348SEric.Yu@Sun.COM 
7508348SEric.Yu@Sun.COM 			/* Both flags should not be set together. */
7518348SEric.Yu@Sun.COM 			if ((sinfo->sinfo_flags & MSG_EOF) &&
7528348SEric.Yu@Sun.COM 			    (sinfo->sinfo_flags & MSG_ABORT)) {
7538348SEric.Yu@Sun.COM 				eprintsoline(so, EINVAL);
7548348SEric.Yu@Sun.COM 				return (EINVAL);
7558348SEric.Yu@Sun.COM 			}
7568348SEric.Yu@Sun.COM 
7578348SEric.Yu@Sun.COM 			/* Initiate a graceful shutdown. */
7588348SEric.Yu@Sun.COM 			if (sinfo->sinfo_flags & MSG_EOF) {
7598348SEric.Yu@Sun.COM 				/* Can't include data in MSG_EOF message. */
7608348SEric.Yu@Sun.COM 				if (uiop->uio_resid != 0) {
7618348SEric.Yu@Sun.COM 					eprintsoline(so, EINVAL);
7628348SEric.Yu@Sun.COM 					return (EINVAL);
7638348SEric.Yu@Sun.COM 				}
7648348SEric.Yu@Sun.COM 
7658348SEric.Yu@Sun.COM 				/*
7668348SEric.Yu@Sun.COM 				 * This is the same sequence as done in
7678348SEric.Yu@Sun.COM 				 * shutdown(SHUT_WR).
7688348SEric.Yu@Sun.COM 				 */
7698348SEric.Yu@Sun.COM 				mutex_enter(&so->so_lock);
7708348SEric.Yu@Sun.COM 				so_lock_single(so);
7718348SEric.Yu@Sun.COM 				socantsendmore(so);
7728348SEric.Yu@Sun.COM 				cv_broadcast(&so->so_snd_cv);
7738348SEric.Yu@Sun.COM 				so->so_state |= SS_ISDISCONNECTING;
7748348SEric.Yu@Sun.COM 				mutex_exit(&so->so_lock);
7758348SEric.Yu@Sun.COM 
7768348SEric.Yu@Sun.COM 				pollwakeup(&so->so_poll_list, POLLOUT);
7778348SEric.Yu@Sun.COM 				sctp_recvd((struct sctp_s *)so->so_proto_handle,
7788348SEric.Yu@Sun.COM 				    so->so_rcvbuf);
7798348SEric.Yu@Sun.COM 				error = sctp_disconnect(
7808348SEric.Yu@Sun.COM 				    (struct sctp_s *)so->so_proto_handle);
7818348SEric.Yu@Sun.COM 
7828348SEric.Yu@Sun.COM 				mutex_enter(&so->so_lock);
7838348SEric.Yu@Sun.COM 				so_unlock_single(so, SOLOCKED);
7848348SEric.Yu@Sun.COM 				mutex_exit(&so->so_lock);
7858348SEric.Yu@Sun.COM 				return (error);
7868348SEric.Yu@Sun.COM 			}
7878348SEric.Yu@Sun.COM 		}
7888348SEric.Yu@Sun.COM 	} else {
7898348SEric.Yu@Sun.COM 		optlen = 0;
7908348SEric.Yu@Sun.COM 	}
7918348SEric.Yu@Sun.COM 
7928348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
7938348SEric.Yu@Sun.COM 	for (;;) {
7948348SEric.Yu@Sun.COM 		if (so->so_state & SS_CANTSENDMORE) {
7958348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
7968348SEric.Yu@Sun.COM 			return (EPIPE);
7978348SEric.Yu@Sun.COM 		}
7988348SEric.Yu@Sun.COM 
7998348SEric.Yu@Sun.COM 		if (so->so_error != 0) {
8008348SEric.Yu@Sun.COM 			error = sogeterr(so, B_TRUE);
8018348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
8028348SEric.Yu@Sun.COM 			return (error);
8038348SEric.Yu@Sun.COM 		}
8048348SEric.Yu@Sun.COM 
8058348SEric.Yu@Sun.COM 		if (!so->so_snd_qfull)
8068348SEric.Yu@Sun.COM 			break;
8078348SEric.Yu@Sun.COM 
8088348SEric.Yu@Sun.COM 		if (so->so_state & SS_CLOSING) {
8098348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
8108348SEric.Yu@Sun.COM 			return (EINTR);
8118348SEric.Yu@Sun.COM 		}
8128348SEric.Yu@Sun.COM 		/*
8138348SEric.Yu@Sun.COM 		 * Xmit window full in a blocking socket.
8148348SEric.Yu@Sun.COM 		 */
8158348SEric.Yu@Sun.COM 		if ((uiop->uio_fmode & (FNDELAY|FNONBLOCK)) ||
8168348SEric.Yu@Sun.COM 		    (flags & MSG_DONTWAIT)) {
8178348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
8188348SEric.Yu@Sun.COM 			return (EAGAIN);
8198348SEric.Yu@Sun.COM 		} else {
8208348SEric.Yu@Sun.COM 			/*
8218348SEric.Yu@Sun.COM 			 * Wait for space to become available and try again.
8228348SEric.Yu@Sun.COM 			 */
8238348SEric.Yu@Sun.COM 			error = cv_wait_sig(&so->so_snd_cv, &so->so_lock);
8248348SEric.Yu@Sun.COM 			if (!error) { /* signal */
8258348SEric.Yu@Sun.COM 				mutex_exit(&so->so_lock);
8268348SEric.Yu@Sun.COM 				return (EINTR);
8278348SEric.Yu@Sun.COM 			}
8288348SEric.Yu@Sun.COM 		}
8298348SEric.Yu@Sun.COM 	}
8308348SEric.Yu@Sun.COM 	msglen = count = uiop->uio_resid;
8318348SEric.Yu@Sun.COM 
8328348SEric.Yu@Sun.COM 	/* Don't allow sending a message larger than the send buffer size. */
8338348SEric.Yu@Sun.COM 	/* XXX Transport module need to enforce this */
8348348SEric.Yu@Sun.COM 	if (msglen > so->so_sndbuf) {
8358348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
8368348SEric.Yu@Sun.COM 		return (EMSGSIZE);
8378348SEric.Yu@Sun.COM 	}
8388348SEric.Yu@Sun.COM 
8398348SEric.Yu@Sun.COM 	/*
8408348SEric.Yu@Sun.COM 	 * Allow piggybacking data on handshake messages (SS_ISCONNECTING).
8418348SEric.Yu@Sun.COM 	 */
8428348SEric.Yu@Sun.COM 	if (!(so->so_state & (SS_ISCONNECTING | SS_ISCONNECTED))) {
8438348SEric.Yu@Sun.COM 		/*
8448348SEric.Yu@Sun.COM 		 * We need to check here for listener so that the
8458348SEric.Yu@Sun.COM 		 * same error will be returned as with a TCP socket.
8468348SEric.Yu@Sun.COM 		 * In this case, sosctp_connect() returns EOPNOTSUPP
8478348SEric.Yu@Sun.COM 		 * while a TCP socket returns ENOTCONN instead.  Catch it
8488348SEric.Yu@Sun.COM 		 * here to have the same behavior as a TCP socket.
8498348SEric.Yu@Sun.COM 		 *
8508348SEric.Yu@Sun.COM 		 * We also need to make sure that the peer address is
8518348SEric.Yu@Sun.COM 		 * provided before we attempt to do the connect.
8528348SEric.Yu@Sun.COM 		 */
8538348SEric.Yu@Sun.COM 		if ((so->so_state & SS_ACCEPTCONN) ||
8548348SEric.Yu@Sun.COM 		    msg->msg_name == NULL) {
8558348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
8568348SEric.Yu@Sun.COM 			error = ENOTCONN;
8578348SEric.Yu@Sun.COM 			goto error_nofree;
8588348SEric.Yu@Sun.COM 		}
8598348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
8608348SEric.Yu@Sun.COM 		fflag = uiop->uio_fmode;
8618348SEric.Yu@Sun.COM 		if (flags & MSG_DONTWAIT) {
8628348SEric.Yu@Sun.COM 			fflag |= FNDELAY;
8638348SEric.Yu@Sun.COM 		}
8648348SEric.Yu@Sun.COM 		error = sosctp_connect(so, msg->msg_name, msg->msg_namelen,
8658348SEric.Yu@Sun.COM 		    fflag, (so->so_version == SOV_XPG4_2) * _SOCONNECT_XPG4_2,
8668348SEric.Yu@Sun.COM 		    cr);
8678348SEric.Yu@Sun.COM 		if (error) {
8688348SEric.Yu@Sun.COM 			/*
8698348SEric.Yu@Sun.COM 			 * Check for non-fatal errors, socket connected
8708348SEric.Yu@Sun.COM 			 * while the lock had been lifted.
8718348SEric.Yu@Sun.COM 			 */
8728348SEric.Yu@Sun.COM 			if (error != EISCONN && error != EALREADY) {
8738348SEric.Yu@Sun.COM 				goto error_nofree;
8748348SEric.Yu@Sun.COM 			}
8758348SEric.Yu@Sun.COM 			error = 0;
8768348SEric.Yu@Sun.COM 		}
8778348SEric.Yu@Sun.COM 	} else {
8788348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
8798348SEric.Yu@Sun.COM 	}
8808348SEric.Yu@Sun.COM 
8818348SEric.Yu@Sun.COM 	mctl = sctp_alloc_hdr(msg->msg_name, msg->msg_namelen,
8828348SEric.Yu@Sun.COM 	    msg->msg_control, optlen, SCTP_CAN_BLOCK);
8838348SEric.Yu@Sun.COM 	if (mctl == NULL) {
8848348SEric.Yu@Sun.COM 		error = EINTR;
8858348SEric.Yu@Sun.COM 		goto error_nofree;
8868348SEric.Yu@Sun.COM 	}
8878348SEric.Yu@Sun.COM 
8888348SEric.Yu@Sun.COM 	/* Copy in the message. */
8898348SEric.Yu@Sun.COM 	if ((error = sosctp_uiomove(mctl, count, ss->ss_wrsize, ss->ss_wroff,
890*11042SErik.Nordmark@Sun.COM 	    uiop, flags)) != 0) {
8918348SEric.Yu@Sun.COM 		goto error_ret;
8928348SEric.Yu@Sun.COM 	}
8938348SEric.Yu@Sun.COM 	error = sctp_sendmsg((struct sctp_s *)so->so_proto_handle, mctl, 0);
8948348SEric.Yu@Sun.COM 	if (error == 0)
8958348SEric.Yu@Sun.COM 		return (0);
8968348SEric.Yu@Sun.COM 
8978348SEric.Yu@Sun.COM error_ret:
8988348SEric.Yu@Sun.COM 	freemsg(mctl);
8998348SEric.Yu@Sun.COM error_nofree:
9008348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
9018348SEric.Yu@Sun.COM 	if ((error == EPIPE) && (so->so_state & SS_CANTSENDMORE)) {
9028348SEric.Yu@Sun.COM 		/*
9038348SEric.Yu@Sun.COM 		 * We received shutdown between the time lock was
9048348SEric.Yu@Sun.COM 		 * lifted and call to sctp_sendmsg().
9058348SEric.Yu@Sun.COM 		 */
9068348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
9078348SEric.Yu@Sun.COM 		return (EPIPE);
9088348SEric.Yu@Sun.COM 	}
9098348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
9108348SEric.Yu@Sun.COM 	return (error);
9118348SEric.Yu@Sun.COM }
9128348SEric.Yu@Sun.COM 
9138348SEric.Yu@Sun.COM /*
9148348SEric.Yu@Sun.COM  * Send message on 1-N socket. Connects automatically if there is
9158348SEric.Yu@Sun.COM  * no association.
9168348SEric.Yu@Sun.COM  */
9178348SEric.Yu@Sun.COM static int
9188348SEric.Yu@Sun.COM sosctp_seq_sendmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
9198348SEric.Yu@Sun.COM     struct cred *cr)
9208348SEric.Yu@Sun.COM {
9218348SEric.Yu@Sun.COM 	struct sctp_sonode *ss;
9228348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa;
9238348SEric.Yu@Sun.COM 	struct cmsghdr *cmsg;
9248348SEric.Yu@Sun.COM 	struct sctp_sndrcvinfo *sinfo;
9258348SEric.Yu@Sun.COM 	int aid = 0;
9268348SEric.Yu@Sun.COM 	mblk_t *mctl;
9278348SEric.Yu@Sun.COM 	int namelen, optlen, flags;
9288348SEric.Yu@Sun.COM 	ssize_t count, msglen;
9298348SEric.Yu@Sun.COM 	int error;
9308348SEric.Yu@Sun.COM 	uint16_t s_flags = 0;
9318348SEric.Yu@Sun.COM 
9328348SEric.Yu@Sun.COM 	ASSERT(so->so_type == SOCK_SEQPACKET);
9338348SEric.Yu@Sun.COM 
9348348SEric.Yu@Sun.COM 	/*
9358348SEric.Yu@Sun.COM 	 * There shouldn't be problems with alignment, as the memory for
9368348SEric.Yu@Sun.COM 	 * msg_control was alloced with kmem_alloc.
9378348SEric.Yu@Sun.COM 	 */
9388348SEric.Yu@Sun.COM 	cmsg = sosctp_find_cmsg(msg->msg_control, msg->msg_controllen,
9398348SEric.Yu@Sun.COM 	    SCTP_SNDRCV);
9408348SEric.Yu@Sun.COM 	if (cmsg != NULL) {
9418348SEric.Yu@Sun.COM 		if (cmsg->cmsg_len < (sizeof (*sinfo) + sizeof (*cmsg))) {
9428348SEric.Yu@Sun.COM 			eprintsoline(so, EINVAL);
9438348SEric.Yu@Sun.COM 			return (EINVAL);
9448348SEric.Yu@Sun.COM 		}
9458348SEric.Yu@Sun.COM 		sinfo = (struct sctp_sndrcvinfo *)(cmsg + 1);
9468348SEric.Yu@Sun.COM 		s_flags = sinfo->sinfo_flags;
9478348SEric.Yu@Sun.COM 		aid = sinfo->sinfo_assoc_id;
9488348SEric.Yu@Sun.COM 	}
9498348SEric.Yu@Sun.COM 
9508348SEric.Yu@Sun.COM 	ss = SOTOSSO(so);
9518348SEric.Yu@Sun.COM 	namelen = msg->msg_namelen;
9528348SEric.Yu@Sun.COM 
9538348SEric.Yu@Sun.COM 	if (msg->msg_controllen > 0) {
9548348SEric.Yu@Sun.COM 		optlen = msg->msg_controllen;
9558348SEric.Yu@Sun.COM 	} else {
9568348SEric.Yu@Sun.COM 		optlen = 0;
9578348SEric.Yu@Sun.COM 	}
9588348SEric.Yu@Sun.COM 
9598348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
9608348SEric.Yu@Sun.COM 
9618348SEric.Yu@Sun.COM 	/*
9628348SEric.Yu@Sun.COM 	 * If there is no association id, connect to address specified
9638348SEric.Yu@Sun.COM 	 * in msg_name.  Otherwise look up the association using the id.
9648348SEric.Yu@Sun.COM 	 */
9658348SEric.Yu@Sun.COM 	if (aid == 0) {
9668348SEric.Yu@Sun.COM 		/*
9678348SEric.Yu@Sun.COM 		 * Connect and shutdown cannot be done together, so check for
9688348SEric.Yu@Sun.COM 		 * MSG_EOF.
9698348SEric.Yu@Sun.COM 		 */
9708348SEric.Yu@Sun.COM 		if (msg->msg_name == NULL || namelen == 0 ||
9718348SEric.Yu@Sun.COM 		    (s_flags & MSG_EOF)) {
9728348SEric.Yu@Sun.COM 			error = EINVAL;
9738348SEric.Yu@Sun.COM 			eprintsoline(so, error);
9748348SEric.Yu@Sun.COM 			goto done;
9758348SEric.Yu@Sun.COM 		}
9768348SEric.Yu@Sun.COM 		flags = uiop->uio_fmode;
9778348SEric.Yu@Sun.COM 		if (msg->msg_flags & MSG_DONTWAIT) {
9788348SEric.Yu@Sun.COM 			flags |= FNDELAY;
9798348SEric.Yu@Sun.COM 		}
9808348SEric.Yu@Sun.COM 		so_lock_single(so);
9818348SEric.Yu@Sun.COM 		error = sosctp_assoc_createconn(ss, msg->msg_name, namelen,
9828348SEric.Yu@Sun.COM 		    msg->msg_control, optlen, flags, cr, &ssa);
9838348SEric.Yu@Sun.COM 		if (error) {
9848348SEric.Yu@Sun.COM 			if ((so->so_version == SOV_XPG4_2) &&
9858348SEric.Yu@Sun.COM 			    (error == EHOSTUNREACH)) {
9868348SEric.Yu@Sun.COM 				error = ENETUNREACH;
9878348SEric.Yu@Sun.COM 			}
9888348SEric.Yu@Sun.COM 			if (ssa == NULL) {
9898348SEric.Yu@Sun.COM 				/*
9908348SEric.Yu@Sun.COM 				 * Fatal error during connect(). Bail out.
9918348SEric.Yu@Sun.COM 				 * If ssa exists, it means that the handshake
9928348SEric.Yu@Sun.COM 				 * is in progress.
9938348SEric.Yu@Sun.COM 				 */
9948348SEric.Yu@Sun.COM 				eprintsoline(so, error);
9958348SEric.Yu@Sun.COM 				so_unlock_single(so, SOLOCKED);
9968348SEric.Yu@Sun.COM 				goto done;
9978348SEric.Yu@Sun.COM 			}
9988348SEric.Yu@Sun.COM 			/*
9998348SEric.Yu@Sun.COM 			 * All the errors are non-fatal ones, don't return
10008348SEric.Yu@Sun.COM 			 * e.g. EINPROGRESS from sendmsg().
10018348SEric.Yu@Sun.COM 			 */
10028348SEric.Yu@Sun.COM 			error = 0;
10038348SEric.Yu@Sun.COM 		}
10048348SEric.Yu@Sun.COM 		so_unlock_single(so, SOLOCKED);
10058348SEric.Yu@Sun.COM 	} else {
10068348SEric.Yu@Sun.COM 		if ((error = sosctp_assoc(ss, aid, &ssa)) != 0) {
10078348SEric.Yu@Sun.COM 			eprintsoline(so, error);
10088348SEric.Yu@Sun.COM 			goto done;
10098348SEric.Yu@Sun.COM 		}
10108348SEric.Yu@Sun.COM 	}
10118348SEric.Yu@Sun.COM 
10128348SEric.Yu@Sun.COM 	/*
10138348SEric.Yu@Sun.COM 	 * Now we have an association.
10148348SEric.Yu@Sun.COM 	 */
10158348SEric.Yu@Sun.COM 	flags = msg->msg_flags;
10168348SEric.Yu@Sun.COM 
10178348SEric.Yu@Sun.COM 	/*
10188348SEric.Yu@Sun.COM 	 * MSG_EOF initiates graceful shutdown.
10198348SEric.Yu@Sun.COM 	 */
10208348SEric.Yu@Sun.COM 	if (s_flags & MSG_EOF) {
10218348SEric.Yu@Sun.COM 		if (uiop->uio_resid) {
10228348SEric.Yu@Sun.COM 			/*
10238348SEric.Yu@Sun.COM 			 * Can't include data in MSG_EOF message.
10248348SEric.Yu@Sun.COM 			 */
10258348SEric.Yu@Sun.COM 			error = EINVAL;
10268348SEric.Yu@Sun.COM 		} else {
10278348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
10288348SEric.Yu@Sun.COM 			ssa->ssa_state |= SS_ISDISCONNECTING;
10298348SEric.Yu@Sun.COM 			sctp_recvd((struct sctp_s *)ssa->ssa_conn,
10308348SEric.Yu@Sun.COM 			    so->so_rcvbuf);
10318348SEric.Yu@Sun.COM 			error = sctp_disconnect((struct sctp_s *)ssa->ssa_conn);
10328348SEric.Yu@Sun.COM 			mutex_enter(&so->so_lock);
10338348SEric.Yu@Sun.COM 		}
10348348SEric.Yu@Sun.COM 		goto refrele;
10358348SEric.Yu@Sun.COM 	}
10368348SEric.Yu@Sun.COM 
10378348SEric.Yu@Sun.COM 	for (;;) {
10388348SEric.Yu@Sun.COM 		if (ssa->ssa_state & SS_CANTSENDMORE) {
10398348SEric.Yu@Sun.COM 			SSA_REFRELE(ss, ssa);
10408348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
10418348SEric.Yu@Sun.COM 			return (EPIPE);
10428348SEric.Yu@Sun.COM 		}
10438348SEric.Yu@Sun.COM 		if (ssa->ssa_error != 0) {
10448348SEric.Yu@Sun.COM 			error = ssa->ssa_error;
10458348SEric.Yu@Sun.COM 			ssa->ssa_error = 0;
10468348SEric.Yu@Sun.COM 			goto refrele;
10478348SEric.Yu@Sun.COM 		}
10488348SEric.Yu@Sun.COM 
10498348SEric.Yu@Sun.COM 		if (!ssa->ssa_snd_qfull)
10508348SEric.Yu@Sun.COM 			break;
10518348SEric.Yu@Sun.COM 
10528348SEric.Yu@Sun.COM 		if (so->so_state & SS_CLOSING) {
10538348SEric.Yu@Sun.COM 			error = EINTR;
10548348SEric.Yu@Sun.COM 			goto refrele;
10558348SEric.Yu@Sun.COM 		}
10568348SEric.Yu@Sun.COM 		if ((uiop->uio_fmode & (FNDELAY|FNONBLOCK)) ||
10578348SEric.Yu@Sun.COM 		    (flags & MSG_DONTWAIT)) {
10588348SEric.Yu@Sun.COM 			error = EAGAIN;
10598348SEric.Yu@Sun.COM 			goto refrele;
10608348SEric.Yu@Sun.COM 		} else {
10618348SEric.Yu@Sun.COM 			/*
10628348SEric.Yu@Sun.COM 			 * Wait for space to become available and try again.
10638348SEric.Yu@Sun.COM 			 */
10648348SEric.Yu@Sun.COM 			error = cv_wait_sig(&so->so_snd_cv, &so->so_lock);
10658348SEric.Yu@Sun.COM 			if (!error) { /* signal */
10668348SEric.Yu@Sun.COM 				error = EINTR;
10678348SEric.Yu@Sun.COM 				goto refrele;
10688348SEric.Yu@Sun.COM 			}
10698348SEric.Yu@Sun.COM 		}
10708348SEric.Yu@Sun.COM 	}
10718348SEric.Yu@Sun.COM 
10728348SEric.Yu@Sun.COM 	msglen = count = uiop->uio_resid;
10738348SEric.Yu@Sun.COM 
10748348SEric.Yu@Sun.COM 	/* Don't allow sending a message larger than the send buffer size. */
10758348SEric.Yu@Sun.COM 	if (msglen > so->so_sndbuf) {
10768348SEric.Yu@Sun.COM 		error = EMSGSIZE;
10778348SEric.Yu@Sun.COM 		goto refrele;
10788348SEric.Yu@Sun.COM 	}
10798348SEric.Yu@Sun.COM 
10808348SEric.Yu@Sun.COM 	/*
10818348SEric.Yu@Sun.COM 	 * Update TX buffer usage here so that we can lift the socket lock.
10828348SEric.Yu@Sun.COM 	 */
10838348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
10848348SEric.Yu@Sun.COM 
10858348SEric.Yu@Sun.COM 	mctl = sctp_alloc_hdr(msg->msg_name, namelen, msg->msg_control,
10868348SEric.Yu@Sun.COM 	    optlen, SCTP_CAN_BLOCK);
10878348SEric.Yu@Sun.COM 	if (mctl == NULL) {
10888348SEric.Yu@Sun.COM 		error = EINTR;
10898348SEric.Yu@Sun.COM 		goto lock_rele;
10908348SEric.Yu@Sun.COM 	}
10918348SEric.Yu@Sun.COM 
10928348SEric.Yu@Sun.COM 	/* Copy in the message. */
10938348SEric.Yu@Sun.COM 	if ((error = sosctp_uiomove(mctl, count, ssa->ssa_wrsize,
1094*11042SErik.Nordmark@Sun.COM 	    ssa->ssa_wroff, uiop, flags)) != 0) {
10958348SEric.Yu@Sun.COM 		goto lock_rele;
10968348SEric.Yu@Sun.COM 	}
10978348SEric.Yu@Sun.COM 	error = sctp_sendmsg((struct sctp_s *)ssa->ssa_conn, mctl, 0);
10988348SEric.Yu@Sun.COM lock_rele:
10998348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
11008348SEric.Yu@Sun.COM 	if (error != 0) {
11018348SEric.Yu@Sun.COM 		freemsg(mctl);
11028348SEric.Yu@Sun.COM 		if ((error == EPIPE) && (ssa->ssa_state & SS_CANTSENDMORE)) {
11038348SEric.Yu@Sun.COM 			/*
11048348SEric.Yu@Sun.COM 			 * We received shutdown between the time lock was
11058348SEric.Yu@Sun.COM 			 * lifted and call to sctp_sendmsg().
11068348SEric.Yu@Sun.COM 			 */
11078348SEric.Yu@Sun.COM 			SSA_REFRELE(ss, ssa);
11088348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
11098348SEric.Yu@Sun.COM 			return (EPIPE);
11108348SEric.Yu@Sun.COM 		}
11118348SEric.Yu@Sun.COM 	}
11128348SEric.Yu@Sun.COM 
11138348SEric.Yu@Sun.COM refrele:
11148348SEric.Yu@Sun.COM 	SSA_REFRELE(ss, ssa);
11158348SEric.Yu@Sun.COM done:
11168348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
11178348SEric.Yu@Sun.COM 	return (error);
11188348SEric.Yu@Sun.COM }
11198348SEric.Yu@Sun.COM 
11208348SEric.Yu@Sun.COM /*
11218348SEric.Yu@Sun.COM  * Get address of remote node.
11228348SEric.Yu@Sun.COM  */
11238348SEric.Yu@Sun.COM /* ARGSUSED */
11248348SEric.Yu@Sun.COM static int
11258348SEric.Yu@Sun.COM sosctp_getpeername(struct sonode *so, struct sockaddr *addr, socklen_t *addrlen,
11268348SEric.Yu@Sun.COM     boolean_t accept, struct cred *cr)
11278348SEric.Yu@Sun.COM {
11288348SEric.Yu@Sun.COM 	return (sctp_getpeername((struct sctp_s *)so->so_proto_handle, addr,
11298348SEric.Yu@Sun.COM 	    addrlen));
11308348SEric.Yu@Sun.COM }
11318348SEric.Yu@Sun.COM 
11328348SEric.Yu@Sun.COM /*
11338348SEric.Yu@Sun.COM  * Get local address.
11348348SEric.Yu@Sun.COM  */
11358348SEric.Yu@Sun.COM /* ARGSUSED */
11368348SEric.Yu@Sun.COM static int
11378348SEric.Yu@Sun.COM sosctp_getsockname(struct sonode *so, struct sockaddr *addr, socklen_t *addrlen,
11388348SEric.Yu@Sun.COM     struct cred *cr)
11398348SEric.Yu@Sun.COM {
11408348SEric.Yu@Sun.COM 	return (sctp_getsockname((struct sctp_s *)so->so_proto_handle, addr,
11418348SEric.Yu@Sun.COM 	    addrlen));
11428348SEric.Yu@Sun.COM }
11438348SEric.Yu@Sun.COM 
11448348SEric.Yu@Sun.COM /*
11458348SEric.Yu@Sun.COM  * Called from shutdown().
11468348SEric.Yu@Sun.COM  */
11478348SEric.Yu@Sun.COM /* ARGSUSED */
11488348SEric.Yu@Sun.COM static int
11498348SEric.Yu@Sun.COM sosctp_shutdown(struct sonode *so, int how, struct cred *cr)
11508348SEric.Yu@Sun.COM {
11518348SEric.Yu@Sun.COM 	uint_t state_change;
11528348SEric.Yu@Sun.COM 	int wakesig = 0;
11538348SEric.Yu@Sun.COM 	int error = 0;
11548348SEric.Yu@Sun.COM 
11558348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
11568348SEric.Yu@Sun.COM 	/*
11578348SEric.Yu@Sun.COM 	 * Record the current state and then perform any state changes.
11588348SEric.Yu@Sun.COM 	 * Then use the difference between the old and new states to
11598348SEric.Yu@Sun.COM 	 * determine which needs to be done.
11608348SEric.Yu@Sun.COM 	 */
11618348SEric.Yu@Sun.COM 	state_change = so->so_state;
11628348SEric.Yu@Sun.COM 
11638348SEric.Yu@Sun.COM 	switch (how) {
11648348SEric.Yu@Sun.COM 	case SHUT_RD:
11658348SEric.Yu@Sun.COM 		socantrcvmore(so);
11668348SEric.Yu@Sun.COM 		break;
11678348SEric.Yu@Sun.COM 	case SHUT_WR:
11688348SEric.Yu@Sun.COM 		socantsendmore(so);
11698348SEric.Yu@Sun.COM 		break;
11708348SEric.Yu@Sun.COM 	case SHUT_RDWR:
11718348SEric.Yu@Sun.COM 		socantsendmore(so);
11728348SEric.Yu@Sun.COM 		socantrcvmore(so);
11738348SEric.Yu@Sun.COM 		break;
11748348SEric.Yu@Sun.COM 	default:
11758348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
11768348SEric.Yu@Sun.COM 		return (EINVAL);
11778348SEric.Yu@Sun.COM 	}
11788348SEric.Yu@Sun.COM 
11798348SEric.Yu@Sun.COM 	state_change = so->so_state & ~state_change;
11808348SEric.Yu@Sun.COM 
11818348SEric.Yu@Sun.COM 	if (state_change & SS_CANTRCVMORE) {
11828348SEric.Yu@Sun.COM 		if (so->so_rcv_q_head == NULL) {
11838348SEric.Yu@Sun.COM 			cv_signal(&so->so_rcv_cv);
11848348SEric.Yu@Sun.COM 		}
11858348SEric.Yu@Sun.COM 		wakesig = POLLIN|POLLRDNORM;
11868348SEric.Yu@Sun.COM 
11878348SEric.Yu@Sun.COM 		socket_sendsig(so, SOCKETSIG_READ);
11888348SEric.Yu@Sun.COM 	}
11898348SEric.Yu@Sun.COM 	if (state_change & SS_CANTSENDMORE) {
11908348SEric.Yu@Sun.COM 		cv_broadcast(&so->so_snd_cv);
11918348SEric.Yu@Sun.COM 		wakesig |= POLLOUT;
11928348SEric.Yu@Sun.COM 
11938348SEric.Yu@Sun.COM 		so->so_state |= SS_ISDISCONNECTING;
11948348SEric.Yu@Sun.COM 	}
11958348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
11968348SEric.Yu@Sun.COM 
11978348SEric.Yu@Sun.COM 	pollwakeup(&so->so_poll_list, wakesig);
11988348SEric.Yu@Sun.COM 
11998348SEric.Yu@Sun.COM 	if (state_change & SS_CANTSENDMORE) {
12008348SEric.Yu@Sun.COM 		sctp_recvd((struct sctp_s *)so->so_proto_handle, so->so_rcvbuf);
12018348SEric.Yu@Sun.COM 		error = sctp_disconnect((struct sctp_s *)so->so_proto_handle);
12028348SEric.Yu@Sun.COM 	}
12038348SEric.Yu@Sun.COM 
12048348SEric.Yu@Sun.COM 	/*
12058348SEric.Yu@Sun.COM 	 * HACK: sctp_disconnect() may return EWOULDBLOCK.  But this error is
12068348SEric.Yu@Sun.COM 	 * not documented in standard socket API.  Catch it here.
12078348SEric.Yu@Sun.COM 	 */
12088348SEric.Yu@Sun.COM 	if (error == EWOULDBLOCK)
12098348SEric.Yu@Sun.COM 		error = 0;
12108348SEric.Yu@Sun.COM 	return (error);
12118348SEric.Yu@Sun.COM }
12128348SEric.Yu@Sun.COM 
12138348SEric.Yu@Sun.COM /*
12148348SEric.Yu@Sun.COM  * Get socket options.
12158348SEric.Yu@Sun.COM  */
12168348SEric.Yu@Sun.COM /*ARGSUSED5*/
12178348SEric.Yu@Sun.COM static int
12188348SEric.Yu@Sun.COM sosctp_getsockopt(struct sonode *so, int level, int option_name,
12198348SEric.Yu@Sun.COM     void *optval, socklen_t *optlenp, int flags, struct cred *cr)
12208348SEric.Yu@Sun.COM {
12218443SRao.Shoaib@Sun.COM 	socklen_t maxlen = *optlenp;
12228443SRao.Shoaib@Sun.COM 	socklen_t len;
12238443SRao.Shoaib@Sun.COM 	socklen_t optlen;
12248443SRao.Shoaib@Sun.COM 	uint8_t	buffer[4];
12258443SRao.Shoaib@Sun.COM 	void	*optbuf = &buffer;
12268443SRao.Shoaib@Sun.COM 	int	error = 0;
12278443SRao.Shoaib@Sun.COM 
12288443SRao.Shoaib@Sun.COM 	if (level == SOL_SOCKET) {
12298443SRao.Shoaib@Sun.COM 		switch (option_name) {
12308443SRao.Shoaib@Sun.COM 		/* Not supported options */
12318443SRao.Shoaib@Sun.COM 		case SO_SNDTIMEO:
12328443SRao.Shoaib@Sun.COM 		case SO_RCVTIMEO:
12338443SRao.Shoaib@Sun.COM 		case SO_EXCLBIND:
123410833SAnders.Persson@Sun.COM 			eprintsoline(so, ENOPROTOOPT);
123510833SAnders.Persson@Sun.COM 			return (ENOPROTOOPT);
123610833SAnders.Persson@Sun.COM 		default:
123710833SAnders.Persson@Sun.COM 			error = socket_getopt_common(so, level, option_name,
123810833SAnders.Persson@Sun.COM 			    optval, optlenp, flags);
123910833SAnders.Persson@Sun.COM 			if (error >= 0)
124010833SAnders.Persson@Sun.COM 				return (error);
124110833SAnders.Persson@Sun.COM 			/* Pass the request to the protocol */
12428443SRao.Shoaib@Sun.COM 			break;
12438443SRao.Shoaib@Sun.COM 		}
12448443SRao.Shoaib@Sun.COM 	}
12458443SRao.Shoaib@Sun.COM 
12468348SEric.Yu@Sun.COM 	if (level == IPPROTO_SCTP) {
12478348SEric.Yu@Sun.COM 		/*
12488348SEric.Yu@Sun.COM 		 * Should go through ioctl().
12498348SEric.Yu@Sun.COM 		 */
12508348SEric.Yu@Sun.COM 		return (EINVAL);
12518348SEric.Yu@Sun.COM 	}
12528443SRao.Shoaib@Sun.COM 
12538443SRao.Shoaib@Sun.COM 	if (maxlen > sizeof (buffer)) {
12548443SRao.Shoaib@Sun.COM 		optbuf = kmem_alloc(maxlen, KM_SLEEP);
12558443SRao.Shoaib@Sun.COM 	}
12568443SRao.Shoaib@Sun.COM 	optlen = maxlen;
12578443SRao.Shoaib@Sun.COM 
12588443SRao.Shoaib@Sun.COM 	/*
12598443SRao.Shoaib@Sun.COM 	 * If the resulting optlen is greater than the provided maxlen, then
12608443SRao.Shoaib@Sun.COM 	 * we sliently trucate.
12618443SRao.Shoaib@Sun.COM 	 */
12628443SRao.Shoaib@Sun.COM 	error = sctp_get_opt((struct sctp_s *)so->so_proto_handle, level,
12638443SRao.Shoaib@Sun.COM 	    option_name, optbuf, &optlen);
12648443SRao.Shoaib@Sun.COM 
12658443SRao.Shoaib@Sun.COM 	if (error != 0) {
12668443SRao.Shoaib@Sun.COM 		eprintsoline(so, error);
12678443SRao.Shoaib@Sun.COM 		goto free;
12688443SRao.Shoaib@Sun.COM 	}
12698443SRao.Shoaib@Sun.COM 	len = optlen;
12708443SRao.Shoaib@Sun.COM 
12718443SRao.Shoaib@Sun.COM copyout:
12728443SRao.Shoaib@Sun.COM 
12738443SRao.Shoaib@Sun.COM 	len = MIN(len, maxlen);
12748443SRao.Shoaib@Sun.COM 	bcopy(optbuf, optval, len);
12758443SRao.Shoaib@Sun.COM 	*optlenp = optlen;
12768443SRao.Shoaib@Sun.COM free:
12778443SRao.Shoaib@Sun.COM 	if (optbuf != &buffer) {
12788443SRao.Shoaib@Sun.COM 		kmem_free(optbuf, maxlen);
12798443SRao.Shoaib@Sun.COM 	}
128010833SAnders.Persson@Sun.COM 
12818443SRao.Shoaib@Sun.COM 	return (error);
12828348SEric.Yu@Sun.COM }
12838348SEric.Yu@Sun.COM 
12848348SEric.Yu@Sun.COM /*
12858348SEric.Yu@Sun.COM  * Set socket options
12868348SEric.Yu@Sun.COM  */
12878348SEric.Yu@Sun.COM /* ARGSUSED */
12888348SEric.Yu@Sun.COM static int
12898348SEric.Yu@Sun.COM sosctp_setsockopt(struct sonode *so, int level, int option_name,
12908348SEric.Yu@Sun.COM     const void *optval, t_uscalar_t optlen, struct cred *cr)
12918348SEric.Yu@Sun.COM {
12928348SEric.Yu@Sun.COM 	struct sctp_sonode *ss = SOTOSSO(so);
12938348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa = NULL;
12948348SEric.Yu@Sun.COM 	sctp_assoc_t id;
12958348SEric.Yu@Sun.COM 	int error, rc;
12968348SEric.Yu@Sun.COM 	void *conn = NULL;
12978348SEric.Yu@Sun.COM 
12988348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
12998348SEric.Yu@Sun.COM 
13008348SEric.Yu@Sun.COM 	/*
13018348SEric.Yu@Sun.COM 	 * For some SCTP level options, one can select the association this
13028348SEric.Yu@Sun.COM 	 * applies to.
13038348SEric.Yu@Sun.COM 	 */
13048348SEric.Yu@Sun.COM 	if (so->so_type == SOCK_STREAM) {
13058348SEric.Yu@Sun.COM 		conn = so->so_proto_handle;
13068348SEric.Yu@Sun.COM 	} else {
13078348SEric.Yu@Sun.COM 		/*
13088348SEric.Yu@Sun.COM 		 * SOCK_SEQPACKET only
13098348SEric.Yu@Sun.COM 		 */
13108348SEric.Yu@Sun.COM 		id = 0;
13118348SEric.Yu@Sun.COM 		if (level == IPPROTO_SCTP) {
13128348SEric.Yu@Sun.COM 			switch (option_name) {
13138348SEric.Yu@Sun.COM 			case SCTP_RTOINFO:
13148348SEric.Yu@Sun.COM 			case SCTP_ASSOCINFO:
13158348SEric.Yu@Sun.COM 			case SCTP_SET_PEER_PRIMARY_ADDR:
13168348SEric.Yu@Sun.COM 			case SCTP_PRIMARY_ADDR:
13178348SEric.Yu@Sun.COM 			case SCTP_PEER_ADDR_PARAMS:
13188348SEric.Yu@Sun.COM 				/*
13198348SEric.Yu@Sun.COM 				 * Association ID is the first element
13208348SEric.Yu@Sun.COM 				 * params struct
13218348SEric.Yu@Sun.COM 				 */
13228348SEric.Yu@Sun.COM 				if (optlen < sizeof (sctp_assoc_t)) {
13238348SEric.Yu@Sun.COM 					error = EINVAL;
13248348SEric.Yu@Sun.COM 					eprintsoline(so, error);
13258348SEric.Yu@Sun.COM 					goto done;
13268348SEric.Yu@Sun.COM 				}
13278348SEric.Yu@Sun.COM 				id = *(sctp_assoc_t *)optval;
13288348SEric.Yu@Sun.COM 				break;
13298348SEric.Yu@Sun.COM 			case SCTP_DEFAULT_SEND_PARAM:
13308348SEric.Yu@Sun.COM 				if (optlen != sizeof (struct sctp_sndrcvinfo)) {
13318348SEric.Yu@Sun.COM 					error = EINVAL;
13328348SEric.Yu@Sun.COM 					eprintsoline(so, error);
13338348SEric.Yu@Sun.COM 					goto done;
13348348SEric.Yu@Sun.COM 				}
13358348SEric.Yu@Sun.COM 				id = ((struct sctp_sndrcvinfo *)
13368348SEric.Yu@Sun.COM 				    optval)->sinfo_assoc_id;
13378348SEric.Yu@Sun.COM 				break;
13388348SEric.Yu@Sun.COM 			case SCTP_INITMSG:
13398348SEric.Yu@Sun.COM 				/*
13408348SEric.Yu@Sun.COM 				 * Only applies to future associations
13418348SEric.Yu@Sun.COM 				 */
13428348SEric.Yu@Sun.COM 				conn = so->so_proto_handle;
13438348SEric.Yu@Sun.COM 				break;
13448348SEric.Yu@Sun.COM 			default:
13458348SEric.Yu@Sun.COM 				break;
13468348SEric.Yu@Sun.COM 			}
13478348SEric.Yu@Sun.COM 		} else if (level == SOL_SOCKET) {
13488348SEric.Yu@Sun.COM 			if (option_name == SO_LINGER) {
13498348SEric.Yu@Sun.COM 				error = EOPNOTSUPP;
13508348SEric.Yu@Sun.COM 				eprintsoline(so, error);
13518348SEric.Yu@Sun.COM 				goto done;
13528348SEric.Yu@Sun.COM 			}
13538348SEric.Yu@Sun.COM 			/*
13548348SEric.Yu@Sun.COM 			 * These 2 options are applied to all associations.
13558348SEric.Yu@Sun.COM 			 * The other socket level options are only applied
13568348SEric.Yu@Sun.COM 			 * to the socket (not associations).
13578348SEric.Yu@Sun.COM 			 */
13588348SEric.Yu@Sun.COM 			if ((option_name != SO_RCVBUF) &&
13598348SEric.Yu@Sun.COM 			    (option_name != SO_SNDBUF)) {
13608348SEric.Yu@Sun.COM 				conn = so->so_proto_handle;
13618348SEric.Yu@Sun.COM 			}
13628348SEric.Yu@Sun.COM 		} else {
13638348SEric.Yu@Sun.COM 			conn = NULL;
13648348SEric.Yu@Sun.COM 		}
13658348SEric.Yu@Sun.COM 
13668348SEric.Yu@Sun.COM 		/*
13678348SEric.Yu@Sun.COM 		 * If association ID was specified, do op on that assoc.
13688348SEric.Yu@Sun.COM 		 * Otherwise set the default setting of a socket.
13698348SEric.Yu@Sun.COM 		 */
13708348SEric.Yu@Sun.COM 		if (id != 0) {
13718348SEric.Yu@Sun.COM 			if ((error = sosctp_assoc(ss, id, &ssa)) != 0) {
13728348SEric.Yu@Sun.COM 				eprintsoline(so, error);
13738348SEric.Yu@Sun.COM 				goto done;
13748348SEric.Yu@Sun.COM 			}
13758348SEric.Yu@Sun.COM 			conn = ssa->ssa_conn;
13768348SEric.Yu@Sun.COM 		}
13778348SEric.Yu@Sun.COM 	}
13788348SEric.Yu@Sun.COM 	dprint(2, ("sosctp_setsockopt %p (%d) - conn %p %d %d id:%d\n",
13798348SEric.Yu@Sun.COM 	    (void *)ss, so->so_type, (void *)conn, level, option_name, id));
13808348SEric.Yu@Sun.COM 
13818348SEric.Yu@Sun.COM 	ASSERT(ssa == NULL || (ssa != NULL && conn != NULL));
13828348SEric.Yu@Sun.COM 	if (conn != NULL) {
13838348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
13848348SEric.Yu@Sun.COM 		error = sctp_set_opt((struct sctp_s *)conn, level, option_name,
13858348SEric.Yu@Sun.COM 		    optval, optlen);
13868348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
13878348SEric.Yu@Sun.COM 		if (ssa != NULL)
13888348SEric.Yu@Sun.COM 			SSA_REFRELE(ss, ssa);
13898348SEric.Yu@Sun.COM 	} else {
13908348SEric.Yu@Sun.COM 		/*
13918348SEric.Yu@Sun.COM 		 * 1-N socket, and we have to apply the operation to ALL
13928348SEric.Yu@Sun.COM 		 * associations. Like with anything of this sort, the
13938348SEric.Yu@Sun.COM 		 * problem is what to do if the operation fails.
13948348SEric.Yu@Sun.COM 		 * Just try to apply the setting to everyone, but store
13958348SEric.Yu@Sun.COM 		 * error number if someone returns such.  And since we are
13968348SEric.Yu@Sun.COM 		 * looping through all possible aids, some of them can be
13978348SEric.Yu@Sun.COM 		 * invalid.  We just ignore this kind (sosctp_assoc()) of
13988348SEric.Yu@Sun.COM 		 * errors.
13998348SEric.Yu@Sun.COM 		 */
14008348SEric.Yu@Sun.COM 		sctp_assoc_t aid;
14018348SEric.Yu@Sun.COM 
14028348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
14038348SEric.Yu@Sun.COM 		error = sctp_set_opt((struct sctp_s *)so->so_proto_handle,
14048348SEric.Yu@Sun.COM 		    level, option_name, optval, optlen);
14058348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
14068348SEric.Yu@Sun.COM 		for (aid = 1; aid < ss->ss_maxassoc; aid++) {
14078348SEric.Yu@Sun.COM 			if (sosctp_assoc(ss, aid, &ssa) != 0)
14088348SEric.Yu@Sun.COM 				continue;
14098348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
14108348SEric.Yu@Sun.COM 			rc = sctp_set_opt((struct sctp_s *)ssa->ssa_conn, level,
14118348SEric.Yu@Sun.COM 			    option_name, optval, optlen);
14128348SEric.Yu@Sun.COM 			mutex_enter(&so->so_lock);
14138348SEric.Yu@Sun.COM 			SSA_REFRELE(ss, ssa);
14148348SEric.Yu@Sun.COM 			if (error == 0) {
14158348SEric.Yu@Sun.COM 				error = rc;
14168348SEric.Yu@Sun.COM 			}
14178348SEric.Yu@Sun.COM 		}
14188348SEric.Yu@Sun.COM 	}
14198348SEric.Yu@Sun.COM done:
14208348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
14218348SEric.Yu@Sun.COM 	return (error);
14228348SEric.Yu@Sun.COM }
14238348SEric.Yu@Sun.COM 
14248348SEric.Yu@Sun.COM /*ARGSUSED*/
14258348SEric.Yu@Sun.COM static int
14268348SEric.Yu@Sun.COM sosctp_ioctl(struct sonode *so, int cmd, intptr_t arg, int mode,
14278348SEric.Yu@Sun.COM     struct cred *cr, int32_t *rvalp)
14288348SEric.Yu@Sun.COM {
14298348SEric.Yu@Sun.COM 	struct sctp_sonode	*ss;
14308348SEric.Yu@Sun.COM 	int32_t			value;
14318348SEric.Yu@Sun.COM 	int			error;
14328348SEric.Yu@Sun.COM 	int			intval;
14338348SEric.Yu@Sun.COM 	pid_t			pid;
14348348SEric.Yu@Sun.COM 	struct sctp_soassoc	*ssa;
14358348SEric.Yu@Sun.COM 	void			*conn;
14368348SEric.Yu@Sun.COM 	void			*buf;
14378348SEric.Yu@Sun.COM 	STRUCT_DECL(sctpopt, opt);
14388348SEric.Yu@Sun.COM 	uint32_t		optlen;
14398348SEric.Yu@Sun.COM 	int			buflen;
14408348SEric.Yu@Sun.COM 
14418348SEric.Yu@Sun.COM 	ss = SOTOSSO(so);
14428348SEric.Yu@Sun.COM 
14438348SEric.Yu@Sun.COM 	/* handle socket specific ioctls */
14448348SEric.Yu@Sun.COM 	switch (cmd) {
14458348SEric.Yu@Sun.COM 	case FIONBIO:
14468348SEric.Yu@Sun.COM 		if (so_copyin((void *)arg, &value, sizeof (int32_t),
14478348SEric.Yu@Sun.COM 		    (mode & (int)FKIOCTL))) {
14488348SEric.Yu@Sun.COM 			return (EFAULT);
14498348SEric.Yu@Sun.COM 		}
14508348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
14518348SEric.Yu@Sun.COM 		if (value) {
14528348SEric.Yu@Sun.COM 			so->so_state |= SS_NDELAY;
14538348SEric.Yu@Sun.COM 		} else {
14548348SEric.Yu@Sun.COM 			so->so_state &= ~SS_NDELAY;
14558348SEric.Yu@Sun.COM 		}
14568348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
14578348SEric.Yu@Sun.COM 		return (0);
14588348SEric.Yu@Sun.COM 
14598348SEric.Yu@Sun.COM 	case FIOASYNC:
14608348SEric.Yu@Sun.COM 		if (so_copyin((void *)arg, &value, sizeof (int32_t),
14618348SEric.Yu@Sun.COM 		    (mode & (int)FKIOCTL))) {
14628348SEric.Yu@Sun.COM 			return (EFAULT);
14638348SEric.Yu@Sun.COM 		}
14648348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
14658348SEric.Yu@Sun.COM 
14668348SEric.Yu@Sun.COM 		if (value) {
14678348SEric.Yu@Sun.COM 			/* Turn on SIGIO */
14688348SEric.Yu@Sun.COM 			so->so_state |= SS_ASYNC;
14698348SEric.Yu@Sun.COM 		} else {
14708348SEric.Yu@Sun.COM 			/* Turn off SIGIO */
14718348SEric.Yu@Sun.COM 			so->so_state &= ~SS_ASYNC;
14728348SEric.Yu@Sun.COM 		}
14738348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
14748348SEric.Yu@Sun.COM 		return (0);
14758348SEric.Yu@Sun.COM 
14768348SEric.Yu@Sun.COM 	case SIOCSPGRP:
14778348SEric.Yu@Sun.COM 	case FIOSETOWN:
14788348SEric.Yu@Sun.COM 		if (so_copyin((void *)arg, &pid, sizeof (pid_t),
14798348SEric.Yu@Sun.COM 		    (mode & (int)FKIOCTL))) {
14808348SEric.Yu@Sun.COM 			return (EFAULT);
14818348SEric.Yu@Sun.COM 		}
14828348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
14838348SEric.Yu@Sun.COM 
14848348SEric.Yu@Sun.COM 		error = (pid != so->so_pgrp) ? socket_chgpgrp(so, pid) : 0;
14858348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
14868348SEric.Yu@Sun.COM 		return (error);
14878348SEric.Yu@Sun.COM 
14888348SEric.Yu@Sun.COM 	case SIOCGPGRP:
14898348SEric.Yu@Sun.COM 	case FIOGETOWN:
14908348SEric.Yu@Sun.COM 		if (so_copyout(&so->so_pgrp, (void *)arg,
14918348SEric.Yu@Sun.COM 		    sizeof (pid_t), (mode & (int)FKIOCTL)))
14928348SEric.Yu@Sun.COM 			return (EFAULT);
14938348SEric.Yu@Sun.COM 		return (0);
14948348SEric.Yu@Sun.COM 
14958348SEric.Yu@Sun.COM 	case FIONREAD:
14968348SEric.Yu@Sun.COM 		/* XXX: Cannot be used unless standard buffer is used */
14978348SEric.Yu@Sun.COM 		/*
14988348SEric.Yu@Sun.COM 		 * Return number of bytes of data in all data messages
14998348SEric.Yu@Sun.COM 		 * in queue in "arg".
15008348SEric.Yu@Sun.COM 		 * For stream socket, amount of available data.
15018348SEric.Yu@Sun.COM 		 * For sock_dgram, # of available bytes + addresses.
15028348SEric.Yu@Sun.COM 		 */
15038348SEric.Yu@Sun.COM 		intval = (so->so_state & SS_ACCEPTCONN) ? 0 :
15048348SEric.Yu@Sun.COM 		    MIN(so->so_rcv_queued, INT_MAX);
15058348SEric.Yu@Sun.COM 		if (so_copyout(&intval, (void *)arg, sizeof (intval),
15068348SEric.Yu@Sun.COM 		    (mode & (int)FKIOCTL)))
15078348SEric.Yu@Sun.COM 			return (EFAULT);
15088348SEric.Yu@Sun.COM 		return (0);
15098348SEric.Yu@Sun.COM 	case SIOCATMARK:
15108348SEric.Yu@Sun.COM 		/*
15118348SEric.Yu@Sun.COM 		 * No support for urgent data.
15128348SEric.Yu@Sun.COM 		 */
15138348SEric.Yu@Sun.COM 		intval = 0;
15148348SEric.Yu@Sun.COM 
15158348SEric.Yu@Sun.COM 		if (so_copyout(&intval, (void *)arg, sizeof (int),
15168348SEric.Yu@Sun.COM 		    (mode & (int)FKIOCTL)))
15178348SEric.Yu@Sun.COM 			return (EFAULT);
15188348SEric.Yu@Sun.COM 		return (0);
15198778SErik.Nordmark@Sun.COM 	case _I_GETPEERCRED: {
15208778SErik.Nordmark@Sun.COM 		int error = 0;
15218778SErik.Nordmark@Sun.COM 
15228778SErik.Nordmark@Sun.COM 		if ((mode & FKIOCTL) == 0)
15238778SErik.Nordmark@Sun.COM 			return (EINVAL);
15248778SErik.Nordmark@Sun.COM 
15258778SErik.Nordmark@Sun.COM 		mutex_enter(&so->so_lock);
15268778SErik.Nordmark@Sun.COM 		if ((so->so_mode & SM_CONNREQUIRED) == 0) {
15278778SErik.Nordmark@Sun.COM 			error = ENOTSUP;
15288778SErik.Nordmark@Sun.COM 		} else if ((so->so_state & SS_ISCONNECTED) == 0) {
15298778SErik.Nordmark@Sun.COM 			error = ENOTCONN;
15308778SErik.Nordmark@Sun.COM 		} else if (so->so_peercred != NULL) {
15318778SErik.Nordmark@Sun.COM 			k_peercred_t *kp = (k_peercred_t *)arg;
15328778SErik.Nordmark@Sun.COM 			kp->pc_cr = so->so_peercred;
15338778SErik.Nordmark@Sun.COM 			kp->pc_cpid = so->so_cpid;
15348778SErik.Nordmark@Sun.COM 			crhold(so->so_peercred);
15358778SErik.Nordmark@Sun.COM 		} else {
15368778SErik.Nordmark@Sun.COM 			error = EINVAL;
15378778SErik.Nordmark@Sun.COM 		}
15388778SErik.Nordmark@Sun.COM 		mutex_exit(&so->so_lock);
15398778SErik.Nordmark@Sun.COM 		return (error);
15408778SErik.Nordmark@Sun.COM 	}
15418348SEric.Yu@Sun.COM 	case SIOCSCTPGOPT:
15428348SEric.Yu@Sun.COM 		STRUCT_INIT(opt, mode);
15438348SEric.Yu@Sun.COM 
15448348SEric.Yu@Sun.COM 		if (so_copyin((void *)arg, STRUCT_BUF(opt), STRUCT_SIZE(opt),
15458348SEric.Yu@Sun.COM 		    (mode & (int)FKIOCTL))) {
15468348SEric.Yu@Sun.COM 			return (EFAULT);
15478348SEric.Yu@Sun.COM 		}
15488348SEric.Yu@Sun.COM 		if ((optlen = STRUCT_FGET(opt, sopt_len)) > SO_MAXARGSIZE)
15498348SEric.Yu@Sun.COM 			return (EINVAL);
15508348SEric.Yu@Sun.COM 
15518348SEric.Yu@Sun.COM 		/*
15528348SEric.Yu@Sun.COM 		 * Find the correct sctp_t based on whether it is 1-N socket
15538348SEric.Yu@Sun.COM 		 * or not.
15548348SEric.Yu@Sun.COM 		 */
15558348SEric.Yu@Sun.COM 		intval = STRUCT_FGET(opt, sopt_aid);
15568348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
15578348SEric.Yu@Sun.COM 		if ((so->so_type == SOCK_SEQPACKET) && intval) {
15588348SEric.Yu@Sun.COM 			if ((error = sosctp_assoc(ss, intval, &ssa)) != 0) {
15598348SEric.Yu@Sun.COM 				mutex_exit(&so->so_lock);
15608348SEric.Yu@Sun.COM 				return (error);
15618348SEric.Yu@Sun.COM 			}
15628348SEric.Yu@Sun.COM 			conn = ssa->ssa_conn;
15638348SEric.Yu@Sun.COM 			ASSERT(conn != NULL);
15648348SEric.Yu@Sun.COM 		} else {
15658348SEric.Yu@Sun.COM 			conn = so->so_proto_handle;
15668348SEric.Yu@Sun.COM 			ssa = NULL;
15678348SEric.Yu@Sun.COM 		}
15688348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
15698348SEric.Yu@Sun.COM 
15708348SEric.Yu@Sun.COM 		/* Copyin the option buffer and then call sctp_get_opt(). */
15718348SEric.Yu@Sun.COM 		buflen = optlen;
15728348SEric.Yu@Sun.COM 		/* Let's allocate a buffer enough to hold an int */
15738348SEric.Yu@Sun.COM 		if (buflen < sizeof (uint32_t))
15748348SEric.Yu@Sun.COM 			buflen = sizeof (uint32_t);
15758348SEric.Yu@Sun.COM 		buf = kmem_alloc(buflen, KM_SLEEP);
15768348SEric.Yu@Sun.COM 		if (so_copyin(STRUCT_FGETP(opt, sopt_val), buf, optlen,
15778348SEric.Yu@Sun.COM 		    (mode & (int)FKIOCTL))) {
15788348SEric.Yu@Sun.COM 			if (ssa != NULL) {
15798348SEric.Yu@Sun.COM 				mutex_enter(&so->so_lock);
15808348SEric.Yu@Sun.COM 				SSA_REFRELE(ss, ssa);
15818348SEric.Yu@Sun.COM 				mutex_exit(&so->so_lock);
15828348SEric.Yu@Sun.COM 			}
15838348SEric.Yu@Sun.COM 			kmem_free(buf, buflen);
15848348SEric.Yu@Sun.COM 			return (EFAULT);
15858348SEric.Yu@Sun.COM 		}
15868348SEric.Yu@Sun.COM 		/* The option level has to be IPPROTO_SCTP */
15878348SEric.Yu@Sun.COM 		error = sctp_get_opt((struct sctp_s *)conn, IPPROTO_SCTP,
15888348SEric.Yu@Sun.COM 		    STRUCT_FGET(opt, sopt_name), buf, &optlen);
15898348SEric.Yu@Sun.COM 		if (ssa != NULL) {
15908348SEric.Yu@Sun.COM 			mutex_enter(&so->so_lock);
15918348SEric.Yu@Sun.COM 			SSA_REFRELE(ss, ssa);
15928348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
15938348SEric.Yu@Sun.COM 		}
15948348SEric.Yu@Sun.COM 		optlen = MIN(buflen, optlen);
15958348SEric.Yu@Sun.COM 		/* No error, copyout the result with the correct buf len. */
15968348SEric.Yu@Sun.COM 		if (error == 0) {
15978348SEric.Yu@Sun.COM 			STRUCT_FSET(opt, sopt_len, optlen);
15988348SEric.Yu@Sun.COM 			if (so_copyout(STRUCT_BUF(opt), (void *)arg,
15998348SEric.Yu@Sun.COM 			    STRUCT_SIZE(opt), (mode & (int)FKIOCTL))) {
16008348SEric.Yu@Sun.COM 				error = EFAULT;
16018348SEric.Yu@Sun.COM 			} else if (so_copyout(buf, STRUCT_FGETP(opt, sopt_val),
16028348SEric.Yu@Sun.COM 			    optlen, (mode & (int)FKIOCTL))) {
16038348SEric.Yu@Sun.COM 				error = EFAULT;
16048348SEric.Yu@Sun.COM 			}
16058348SEric.Yu@Sun.COM 		}
16068348SEric.Yu@Sun.COM 		kmem_free(buf, buflen);
16078348SEric.Yu@Sun.COM 		return (error);
16088348SEric.Yu@Sun.COM 
16098348SEric.Yu@Sun.COM 	case SIOCSCTPSOPT:
16108348SEric.Yu@Sun.COM 		STRUCT_INIT(opt, mode);
16118348SEric.Yu@Sun.COM 
16128348SEric.Yu@Sun.COM 		if (so_copyin((void *)arg, STRUCT_BUF(opt), STRUCT_SIZE(opt),
16138348SEric.Yu@Sun.COM 		    (mode & (int)FKIOCTL))) {
16148348SEric.Yu@Sun.COM 			return (EFAULT);
16158348SEric.Yu@Sun.COM 		}
16168348SEric.Yu@Sun.COM 		if ((optlen = STRUCT_FGET(opt, sopt_len)) > SO_MAXARGSIZE)
16178348SEric.Yu@Sun.COM 			return (EINVAL);
16188348SEric.Yu@Sun.COM 
16198348SEric.Yu@Sun.COM 		/*
16208348SEric.Yu@Sun.COM 		 * Find the correct sctp_t based on whether it is 1-N socket
16218348SEric.Yu@Sun.COM 		 * or not.
16228348SEric.Yu@Sun.COM 		 */
16238348SEric.Yu@Sun.COM 		intval = STRUCT_FGET(opt, sopt_aid);
16248348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
16258348SEric.Yu@Sun.COM 		if (intval != 0) {
16268348SEric.Yu@Sun.COM 			if ((error = sosctp_assoc(ss, intval, &ssa)) != 0) {
16278348SEric.Yu@Sun.COM 				mutex_exit(&so->so_lock);
16288348SEric.Yu@Sun.COM 				return (error);
16298348SEric.Yu@Sun.COM 			}
16308348SEric.Yu@Sun.COM 			conn = ssa->ssa_conn;
16318348SEric.Yu@Sun.COM 			ASSERT(conn != NULL);
16328348SEric.Yu@Sun.COM 		} else {
16338348SEric.Yu@Sun.COM 			conn = so->so_proto_handle;
16348348SEric.Yu@Sun.COM 			ssa = NULL;
16358348SEric.Yu@Sun.COM 		}
16368348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
16378348SEric.Yu@Sun.COM 
16388348SEric.Yu@Sun.COM 		/* Copyin the option buffer and then call sctp_set_opt(). */
16398348SEric.Yu@Sun.COM 		buf = kmem_alloc(optlen, KM_SLEEP);
16408348SEric.Yu@Sun.COM 		if (so_copyin(STRUCT_FGETP(opt, sopt_val), buf, optlen,
16418348SEric.Yu@Sun.COM 		    (mode & (int)FKIOCTL))) {
16428348SEric.Yu@Sun.COM 			if (ssa != NULL) {
16438348SEric.Yu@Sun.COM 				mutex_enter(&so->so_lock);
16448348SEric.Yu@Sun.COM 				SSA_REFRELE(ss, ssa);
16458348SEric.Yu@Sun.COM 				mutex_exit(&so->so_lock);
16468348SEric.Yu@Sun.COM 			}
16478348SEric.Yu@Sun.COM 			kmem_free(buf, intval);
16488348SEric.Yu@Sun.COM 			return (EFAULT);
16498348SEric.Yu@Sun.COM 		}
16508348SEric.Yu@Sun.COM 		/* The option level has to be IPPROTO_SCTP */
16518348SEric.Yu@Sun.COM 		error = sctp_set_opt((struct sctp_s *)conn, IPPROTO_SCTP,
16528348SEric.Yu@Sun.COM 		    STRUCT_FGET(opt, sopt_name), buf, optlen);
16538348SEric.Yu@Sun.COM 		if (ssa) {
16548348SEric.Yu@Sun.COM 			mutex_enter(&so->so_lock);
16558348SEric.Yu@Sun.COM 			SSA_REFRELE(ss, ssa);
16568348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
16578348SEric.Yu@Sun.COM 		}
16588348SEric.Yu@Sun.COM 		kmem_free(buf, optlen);
16598348SEric.Yu@Sun.COM 		return (error);
16608348SEric.Yu@Sun.COM 
16618348SEric.Yu@Sun.COM 	case SIOCSCTPPEELOFF: {
16628348SEric.Yu@Sun.COM 		struct sonode *nso;
16638348SEric.Yu@Sun.COM 		struct sctp_uc_swap us;
16648348SEric.Yu@Sun.COM 		int nfd;
16658348SEric.Yu@Sun.COM 		struct file *nfp;
16668348SEric.Yu@Sun.COM 		struct vnode *nvp = NULL;
16678348SEric.Yu@Sun.COM 		struct sockparams *sp;
16688348SEric.Yu@Sun.COM 
16698348SEric.Yu@Sun.COM 		dprint(2, ("sctppeeloff %p\n", (void *)ss));
16708348SEric.Yu@Sun.COM 
16718348SEric.Yu@Sun.COM 		if (so->so_type != SOCK_SEQPACKET) {
16728348SEric.Yu@Sun.COM 			return (EOPNOTSUPP);
16738348SEric.Yu@Sun.COM 		}
16748348SEric.Yu@Sun.COM 		if (so_copyin((void *)arg, &intval, sizeof (intval),
16758348SEric.Yu@Sun.COM 		    (mode & (int)FKIOCTL))) {
16768348SEric.Yu@Sun.COM 			return (EFAULT);
16778348SEric.Yu@Sun.COM 		}
16788348SEric.Yu@Sun.COM 		if (intval == 0) {
16798348SEric.Yu@Sun.COM 			return (EINVAL);
16808348SEric.Yu@Sun.COM 		}
16818348SEric.Yu@Sun.COM 
16828348SEric.Yu@Sun.COM 		/*
16838348SEric.Yu@Sun.COM 		 * Find sockparams. This is different from parent's entry,
16848348SEric.Yu@Sun.COM 		 * as the socket type is different.
16858348SEric.Yu@Sun.COM 		 */
16868348SEric.Yu@Sun.COM 		error = solookup(so->so_family, SOCK_STREAM, so->so_protocol,
16878348SEric.Yu@Sun.COM 		    &sp);
16889184SAnders.Persson@Sun.COM 		if (error != 0)
16899184SAnders.Persson@Sun.COM 			return (error);
16908348SEric.Yu@Sun.COM 
16918348SEric.Yu@Sun.COM 		/*
16928348SEric.Yu@Sun.COM 		 * Allocate the user fd.
16938348SEric.Yu@Sun.COM 		 */
16948348SEric.Yu@Sun.COM 		if ((nfd = ufalloc(0)) == -1) {
16958348SEric.Yu@Sun.COM 			eprintsoline(so, EMFILE);
169610834SAnders.Persson@Sun.COM 			SOCKPARAMS_DEC_REF(sp);
16978348SEric.Yu@Sun.COM 			return (EMFILE);
16988348SEric.Yu@Sun.COM 		}
16998348SEric.Yu@Sun.COM 
17008348SEric.Yu@Sun.COM 		/*
17018348SEric.Yu@Sun.COM 		 * Copy the fd out.
17028348SEric.Yu@Sun.COM 		 */
17038348SEric.Yu@Sun.COM 		if (so_copyout(&nfd, (void *)arg, sizeof (nfd),
17048348SEric.Yu@Sun.COM 		    (mode & (int)FKIOCTL))) {
17058348SEric.Yu@Sun.COM 			error = EFAULT;
17068348SEric.Yu@Sun.COM 			goto err;
17078348SEric.Yu@Sun.COM 		}
17088348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
17098348SEric.Yu@Sun.COM 
17108348SEric.Yu@Sun.COM 		/*
17118348SEric.Yu@Sun.COM 		 * Don't use sosctp_assoc() in order to peel off disconnected
17128348SEric.Yu@Sun.COM 		 * associations.
17138348SEric.Yu@Sun.COM 		 */
17148348SEric.Yu@Sun.COM 		ssa = ((uint32_t)intval >= ss->ss_maxassoc) ? NULL :
17158348SEric.Yu@Sun.COM 		    ss->ss_assocs[intval].ssi_assoc;
17168348SEric.Yu@Sun.COM 		if (ssa == NULL) {
17178348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
17188348SEric.Yu@Sun.COM 			error = EINVAL;
17198348SEric.Yu@Sun.COM 			goto err;
17208348SEric.Yu@Sun.COM 		}
17218348SEric.Yu@Sun.COM 		SSA_REFHOLD(ssa);
17228348SEric.Yu@Sun.COM 
17238348SEric.Yu@Sun.COM 		nso = socksctp_create(sp, so->so_family, SOCK_STREAM,
17248348SEric.Yu@Sun.COM 		    so->so_protocol, so->so_version, SOCKET_NOSLEEP,
17258348SEric.Yu@Sun.COM 		    &error, cr);
17268348SEric.Yu@Sun.COM 		if (nso == NULL) {
17278348SEric.Yu@Sun.COM 			SSA_REFRELE(ss, ssa);
17288348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
17298348SEric.Yu@Sun.COM 			goto err;
17308348SEric.Yu@Sun.COM 		}
17318348SEric.Yu@Sun.COM 		nvp = SOTOV(nso);
17328348SEric.Yu@Sun.COM 		so_lock_single(so);
17338348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
17349184SAnders.Persson@Sun.COM 
17359184SAnders.Persson@Sun.COM 		/* cannot fail, only inheriting properties */
17369184SAnders.Persson@Sun.COM 		(void) sosctp_init(nso, so, CRED(), 0);
17379184SAnders.Persson@Sun.COM 
17389184SAnders.Persson@Sun.COM 		/*
17399184SAnders.Persson@Sun.COM 		 * We have a single ref on the new socket. This is normally
17409184SAnders.Persson@Sun.COM 		 * handled by socket_{create,newconn}, but since they are not
17419184SAnders.Persson@Sun.COM 		 * used we have to do it here.
17429184SAnders.Persson@Sun.COM 		 */
17439184SAnders.Persson@Sun.COM 		nso->so_count = 1;
17449184SAnders.Persson@Sun.COM 
17459184SAnders.Persson@Sun.COM 		us.sus_handle = nso;
17468348SEric.Yu@Sun.COM 		us.sus_upcalls = &sosctp_sock_upcalls;
17478348SEric.Yu@Sun.COM 
17488348SEric.Yu@Sun.COM 		/*
17498348SEric.Yu@Sun.COM 		 * Upcalls to new socket are blocked for the duration of
17508348SEric.Yu@Sun.COM 		 * downcall.
17518348SEric.Yu@Sun.COM 		 */
17528348SEric.Yu@Sun.COM 		mutex_enter(&nso->so_lock);
17538348SEric.Yu@Sun.COM 
17548348SEric.Yu@Sun.COM 		error = sctp_set_opt((struct sctp_s *)ssa->ssa_conn,
17558348SEric.Yu@Sun.COM 		    IPPROTO_SCTP, SCTP_UC_SWAP, &us, sizeof (us));
17568348SEric.Yu@Sun.COM 		if (error) {
17578348SEric.Yu@Sun.COM 			goto peelerr;
17588348SEric.Yu@Sun.COM 		}
17598348SEric.Yu@Sun.COM 		error = falloc(nvp, FWRITE|FREAD, &nfp, NULL);
17608348SEric.Yu@Sun.COM 		if (error) {
17618348SEric.Yu@Sun.COM 			goto peelerr;
17628348SEric.Yu@Sun.COM 		}
17638348SEric.Yu@Sun.COM 
17648348SEric.Yu@Sun.COM 		/*
17658348SEric.Yu@Sun.COM 		 * fill in the entries that falloc reserved
17668348SEric.Yu@Sun.COM 		 */
17678348SEric.Yu@Sun.COM 		nfp->f_vnode = nvp;
17688348SEric.Yu@Sun.COM 		mutex_exit(&nfp->f_tlock);
17698348SEric.Yu@Sun.COM 		setf(nfd, nfp);
17708348SEric.Yu@Sun.COM 
17718348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
17728348SEric.Yu@Sun.COM 
17738348SEric.Yu@Sun.COM 		sosctp_assoc_move(ss, SOTOSSO(nso), ssa);
17748348SEric.Yu@Sun.COM 
17758348SEric.Yu@Sun.COM 		mutex_exit(&nso->so_lock);
17768348SEric.Yu@Sun.COM 
17778348SEric.Yu@Sun.COM 		ssa->ssa_conn = NULL;
17788348SEric.Yu@Sun.COM 		sosctp_assoc_free(ss, ssa);
17798348SEric.Yu@Sun.COM 
17808348SEric.Yu@Sun.COM 		so_unlock_single(so, SOLOCKED);
17818348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
17828348SEric.Yu@Sun.COM 
17838348SEric.Yu@Sun.COM 		return (0);
17848348SEric.Yu@Sun.COM 
17858348SEric.Yu@Sun.COM err:
178610834SAnders.Persson@Sun.COM 		SOCKPARAMS_DEC_REF(sp);
17878348SEric.Yu@Sun.COM 		setf(nfd, NULL);
17888348SEric.Yu@Sun.COM 		eprintsoline(so, error);
17898348SEric.Yu@Sun.COM 		return (error);
17908348SEric.Yu@Sun.COM 
17918348SEric.Yu@Sun.COM peelerr:
17928348SEric.Yu@Sun.COM 		mutex_exit(&nso->so_lock);
17938348SEric.Yu@Sun.COM 		mutex_enter(&so->so_lock);
17948348SEric.Yu@Sun.COM 		ASSERT(nso->so_count == 1);
17958348SEric.Yu@Sun.COM 		nso->so_count = 0;
17968348SEric.Yu@Sun.COM 		so_unlock_single(so, SOLOCKED);
17978348SEric.Yu@Sun.COM 		SSA_REFRELE(ss, ssa);
17988348SEric.Yu@Sun.COM 		mutex_exit(&so->so_lock);
17998348SEric.Yu@Sun.COM 
18008348SEric.Yu@Sun.COM 		setf(nfd, NULL);
18018348SEric.Yu@Sun.COM 		ASSERT(nvp->v_count == 1);
18028348SEric.Yu@Sun.COM 		socket_destroy(nso);
18038348SEric.Yu@Sun.COM 		eprintsoline(so, error);
18048348SEric.Yu@Sun.COM 		return (error);
18058348SEric.Yu@Sun.COM 	}
18068348SEric.Yu@Sun.COM 	default:
18078348SEric.Yu@Sun.COM 		return (EINVAL);
18088348SEric.Yu@Sun.COM 	}
18098348SEric.Yu@Sun.COM }
18108348SEric.Yu@Sun.COM 
18118348SEric.Yu@Sun.COM /*ARGSUSED*/
18128348SEric.Yu@Sun.COM static int
18138348SEric.Yu@Sun.COM sosctp_close(struct sonode *so, int flag, struct cred *cr)
18148348SEric.Yu@Sun.COM {
18158348SEric.Yu@Sun.COM 	struct sctp_sonode *ss;
18168348SEric.Yu@Sun.COM 	struct sctp_sa_id *ssi;
18178348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa;
18188348SEric.Yu@Sun.COM 	int32_t i;
18198348SEric.Yu@Sun.COM 
18208348SEric.Yu@Sun.COM 	ss = SOTOSSO(so);
18218348SEric.Yu@Sun.COM 
18228348SEric.Yu@Sun.COM 	/*
18238348SEric.Yu@Sun.COM 	 * Initiate connection shutdown.  Update SCTP's receive
18248348SEric.Yu@Sun.COM 	 * window.
18258348SEric.Yu@Sun.COM 	 */
18268348SEric.Yu@Sun.COM 	sctp_recvd((struct sctp_s *)so->so_proto_handle,
18278348SEric.Yu@Sun.COM 	    so->so_rcvbuf - so->so_rcv_queued);
18288348SEric.Yu@Sun.COM 	(void) sctp_disconnect((struct sctp_s *)so->so_proto_handle);
18298348SEric.Yu@Sun.COM 
18308348SEric.Yu@Sun.COM 	/*
18318348SEric.Yu@Sun.COM 	 * New associations can't come in, but old ones might get
18328348SEric.Yu@Sun.COM 	 * closed in upcall. Protect against that by taking a reference
18338348SEric.Yu@Sun.COM 	 * on the association.
18348348SEric.Yu@Sun.COM 	 */
18358348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
18368348SEric.Yu@Sun.COM 	ssi = ss->ss_assocs;
18378348SEric.Yu@Sun.COM 	for (i = 0; i < ss->ss_maxassoc; i++, ssi++) {
18388348SEric.Yu@Sun.COM 		if ((ssa = ssi->ssi_assoc) != NULL) {
18398348SEric.Yu@Sun.COM 			SSA_REFHOLD(ssa);
18408348SEric.Yu@Sun.COM 			sosctp_assoc_isdisconnected(ssa, 0);
18418348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
18428348SEric.Yu@Sun.COM 
18438348SEric.Yu@Sun.COM 			sctp_recvd((struct sctp_s *)ssa->ssa_conn,
18448348SEric.Yu@Sun.COM 			    so->so_rcvbuf - ssa->ssa_rcv_queued);
18458348SEric.Yu@Sun.COM 			(void) sctp_disconnect((struct sctp_s *)ssa->ssa_conn);
18468348SEric.Yu@Sun.COM 
18478348SEric.Yu@Sun.COM 			mutex_enter(&so->so_lock);
18488348SEric.Yu@Sun.COM 			SSA_REFRELE(ss, ssa);
18498348SEric.Yu@Sun.COM 		}
18508348SEric.Yu@Sun.COM 	}
18518348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
18528348SEric.Yu@Sun.COM 
18538348SEric.Yu@Sun.COM 	return (0);
18548348SEric.Yu@Sun.COM }
18558348SEric.Yu@Sun.COM 
18568348SEric.Yu@Sun.COM /*
18578348SEric.Yu@Sun.COM  * Closes incoming connections which were never accepted, frees
18588348SEric.Yu@Sun.COM  * resources.
18598348SEric.Yu@Sun.COM  */
18608348SEric.Yu@Sun.COM /* ARGSUSED */
18618348SEric.Yu@Sun.COM void
18628348SEric.Yu@Sun.COM sosctp_fini(struct sonode *so, struct cred *cr)
18638348SEric.Yu@Sun.COM {
18648348SEric.Yu@Sun.COM 	struct sctp_sonode *ss;
18658348SEric.Yu@Sun.COM 	struct sctp_sa_id *ssi;
18668348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa;
18678348SEric.Yu@Sun.COM 	int32_t i;
18688348SEric.Yu@Sun.COM 
18698348SEric.Yu@Sun.COM 	ss = SOTOSSO(so);
18708348SEric.Yu@Sun.COM 
18718348SEric.Yu@Sun.COM 	ASSERT(so->so_ops == &sosctp_sonodeops ||
18728348SEric.Yu@Sun.COM 	    so->so_ops == &sosctp_seq_sonodeops);
18738348SEric.Yu@Sun.COM 
18748348SEric.Yu@Sun.COM 	/* We are the sole owner of so now */
18758348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
18768348SEric.Yu@Sun.COM 
18778348SEric.Yu@Sun.COM 	so_rcv_flush(so);
18788348SEric.Yu@Sun.COM 
18798348SEric.Yu@Sun.COM 	/* Free all pending connections */
188010836SAnders.Persson@Sun.COM 	so_acceptq_flush(so, B_TRUE);
18818348SEric.Yu@Sun.COM 
18828348SEric.Yu@Sun.COM 	ssi = ss->ss_assocs;
18838348SEric.Yu@Sun.COM 	for (i = 0; i < ss->ss_maxassoc; i++, ssi++) {
18848348SEric.Yu@Sun.COM 		if ((ssa = ssi->ssi_assoc) != NULL) {
18858348SEric.Yu@Sun.COM 			SSA_REFHOLD(ssa);
18868348SEric.Yu@Sun.COM 			mutex_exit(&so->so_lock);
18878348SEric.Yu@Sun.COM 
18888348SEric.Yu@Sun.COM 			sctp_close((struct sctp_s *)ssa->ssa_conn);
18898348SEric.Yu@Sun.COM 
18908348SEric.Yu@Sun.COM 			mutex_enter(&so->so_lock);
18918348SEric.Yu@Sun.COM 			ssa->ssa_conn = NULL;
18928348SEric.Yu@Sun.COM 			sosctp_assoc_free(ss, ssa);
18938348SEric.Yu@Sun.COM 		}
18948348SEric.Yu@Sun.COM 	}
18958348SEric.Yu@Sun.COM 	if (ss->ss_assocs != NULL) {
18968348SEric.Yu@Sun.COM 		ASSERT(ss->ss_assoccnt == 0);
18978348SEric.Yu@Sun.COM 		kmem_free(ss->ss_assocs,
18988348SEric.Yu@Sun.COM 		    ss->ss_maxassoc * sizeof (struct sctp_sa_id));
18998348SEric.Yu@Sun.COM 	}
19008348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
19018348SEric.Yu@Sun.COM 
19028348SEric.Yu@Sun.COM 	if (so->so_proto_handle)
19038348SEric.Yu@Sun.COM 		sctp_close((struct sctp_s *)so->so_proto_handle);
19048348SEric.Yu@Sun.COM 	so->so_proto_handle = NULL;
19058348SEric.Yu@Sun.COM 
19068348SEric.Yu@Sun.COM 	sonode_fini(so);
19078348SEric.Yu@Sun.COM }
19088348SEric.Yu@Sun.COM 
19098348SEric.Yu@Sun.COM /*
19108348SEric.Yu@Sun.COM  * Upcalls from SCTP
19118348SEric.Yu@Sun.COM  */
19128348SEric.Yu@Sun.COM 
19138348SEric.Yu@Sun.COM /*
19148348SEric.Yu@Sun.COM  * This is the upcall function for 1-N (SOCK_SEQPACKET) socket when a new
19158348SEric.Yu@Sun.COM  * association is created.  Note that the first argument (handle) is of type
19168348SEric.Yu@Sun.COM  * sctp_sonode *, which is the one changed to a listener for new
19178348SEric.Yu@Sun.COM  * associations.  All the other upcalls for 1-N socket take sctp_soassoc *
19188348SEric.Yu@Sun.COM  * as handle.  The only exception is the su_properties upcall, which
19198348SEric.Yu@Sun.COM  * can take both types as handle.
19208348SEric.Yu@Sun.COM  */
19218348SEric.Yu@Sun.COM /* ARGSUSED */
19228348SEric.Yu@Sun.COM sock_upper_handle_t
19238348SEric.Yu@Sun.COM sctp_assoc_newconn(sock_upper_handle_t parenthandle,
19248348SEric.Yu@Sun.COM     sock_lower_handle_t connind, sock_downcalls_t *dc,
19258348SEric.Yu@Sun.COM     struct cred *peer_cred, pid_t peer_cpid, sock_upcalls_t **ucp)
19268348SEric.Yu@Sun.COM {
19278348SEric.Yu@Sun.COM 	struct sonode *lso = (struct sonode *)parenthandle;
19288348SEric.Yu@Sun.COM 	struct sctp_sonode *lss = SOTOSSO(lso);
19298348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa;
19308348SEric.Yu@Sun.COM 	sctp_assoc_t id;
19318348SEric.Yu@Sun.COM 
19328348SEric.Yu@Sun.COM 	ASSERT(lss->ss_type == SOSCTP_SOCKET);
19338348SEric.Yu@Sun.COM 	ASSERT(lso->so_state & SS_ACCEPTCONN);
19348348SEric.Yu@Sun.COM 	ASSERT(lso->so_proto_handle != NULL); /* closed conn */
19358348SEric.Yu@Sun.COM 	ASSERT(lso->so_type == SOCK_SEQPACKET);
19368348SEric.Yu@Sun.COM 
19378348SEric.Yu@Sun.COM 	mutex_enter(&lso->so_lock);
19388348SEric.Yu@Sun.COM 
19398348SEric.Yu@Sun.COM 	if ((id = sosctp_aid_get(lss)) == -1) {
19408348SEric.Yu@Sun.COM 		/*
19418348SEric.Yu@Sun.COM 		 * Array not large enough; increase size.
19428348SEric.Yu@Sun.COM 		 */
19438348SEric.Yu@Sun.COM 		if (sosctp_aid_grow(lss, lss->ss_maxassoc, KM_NOSLEEP) < 0) {
19448348SEric.Yu@Sun.COM 			mutex_exit(&lso->so_lock);
19458348SEric.Yu@Sun.COM 			return (NULL);
19468348SEric.Yu@Sun.COM 		}
19478348SEric.Yu@Sun.COM 		id = sosctp_aid_get(lss);
19488348SEric.Yu@Sun.COM 		ASSERT(id != -1);
19498348SEric.Yu@Sun.COM 	}
19508348SEric.Yu@Sun.COM 
19518348SEric.Yu@Sun.COM 	/*
19528348SEric.Yu@Sun.COM 	 * Create soassoc for this connection
19538348SEric.Yu@Sun.COM 	 */
19548348SEric.Yu@Sun.COM 	ssa = sosctp_assoc_create(lss, KM_NOSLEEP);
19558348SEric.Yu@Sun.COM 	if (ssa == NULL) {
19568348SEric.Yu@Sun.COM 		mutex_exit(&lso->so_lock);
19578348SEric.Yu@Sun.COM 		return (NULL);
19588348SEric.Yu@Sun.COM 	}
19598348SEric.Yu@Sun.COM 	sosctp_aid_reserve(lss, id, 1);
19608348SEric.Yu@Sun.COM 	lss->ss_assocs[id].ssi_assoc = ssa;
19618348SEric.Yu@Sun.COM 	++lss->ss_assoccnt;
19628348SEric.Yu@Sun.COM 	ssa->ssa_id = id;
19638348SEric.Yu@Sun.COM 	ssa->ssa_conn = (struct sctp_s *)connind;
19648348SEric.Yu@Sun.COM 	ssa->ssa_state = (SS_ISBOUND | SS_ISCONNECTED);
19658348SEric.Yu@Sun.COM 	ssa->ssa_wroff = lss->ss_wroff;
19668348SEric.Yu@Sun.COM 	ssa->ssa_wrsize = lss->ss_wrsize;
19678348SEric.Yu@Sun.COM 
19688348SEric.Yu@Sun.COM 	mutex_exit(&lso->so_lock);
19698348SEric.Yu@Sun.COM 
19708348SEric.Yu@Sun.COM 	*ucp = &sosctp_assoc_upcalls;
19718348SEric.Yu@Sun.COM 
19728348SEric.Yu@Sun.COM 	return ((sock_upper_handle_t)ssa);
19738348SEric.Yu@Sun.COM }
19748348SEric.Yu@Sun.COM 
19758348SEric.Yu@Sun.COM /* ARGSUSED */
19768348SEric.Yu@Sun.COM static void
19778348SEric.Yu@Sun.COM sctp_assoc_connected(sock_upper_handle_t handle, sock_connid_t id,
19788348SEric.Yu@Sun.COM     struct cred *peer_cred, pid_t peer_cpid)
19798348SEric.Yu@Sun.COM {
19808348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle;
19818348SEric.Yu@Sun.COM 	struct sonode *so = &ssa->ssa_sonode->ss_so;
19828348SEric.Yu@Sun.COM 
19838348SEric.Yu@Sun.COM 	ASSERT(so->so_type == SOCK_SEQPACKET);
19848348SEric.Yu@Sun.COM 	ASSERT(ssa->ssa_conn);
19858348SEric.Yu@Sun.COM 
19868348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
19878348SEric.Yu@Sun.COM 	sosctp_assoc_isconnected(ssa);
19888348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
19898348SEric.Yu@Sun.COM }
19908348SEric.Yu@Sun.COM 
19918348SEric.Yu@Sun.COM /* ARGSUSED */
19928348SEric.Yu@Sun.COM static int
19938348SEric.Yu@Sun.COM sctp_assoc_disconnected(sock_upper_handle_t handle, sock_connid_t id, int error)
19948348SEric.Yu@Sun.COM {
19958348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle;
19968348SEric.Yu@Sun.COM 	struct sonode *so = &ssa->ssa_sonode->ss_so;
19978348SEric.Yu@Sun.COM 	int ret;
19988348SEric.Yu@Sun.COM 
19998348SEric.Yu@Sun.COM 	ASSERT(so->so_type == SOCK_SEQPACKET);
20008348SEric.Yu@Sun.COM 	ASSERT(ssa->ssa_conn != NULL);
20018348SEric.Yu@Sun.COM 
20028348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
20038348SEric.Yu@Sun.COM 	sosctp_assoc_isdisconnected(ssa, error);
20048348SEric.Yu@Sun.COM 	if (ssa->ssa_refcnt == 1) {
20058348SEric.Yu@Sun.COM 		ret = 1;
20068348SEric.Yu@Sun.COM 		ssa->ssa_conn = NULL;
20078348SEric.Yu@Sun.COM 	} else {
20088348SEric.Yu@Sun.COM 		ret = 0;
20098348SEric.Yu@Sun.COM 	}
20108348SEric.Yu@Sun.COM 	SSA_REFRELE(SOTOSSO(so), ssa);
20118348SEric.Yu@Sun.COM 
20128348SEric.Yu@Sun.COM 	cv_broadcast(&so->so_snd_cv);
20138348SEric.Yu@Sun.COM 
20148348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
20158348SEric.Yu@Sun.COM 
20168348SEric.Yu@Sun.COM 	return (ret);
20178348SEric.Yu@Sun.COM }
20188348SEric.Yu@Sun.COM 
20198348SEric.Yu@Sun.COM /* ARGSUSED */
20208348SEric.Yu@Sun.COM static void
20218348SEric.Yu@Sun.COM sctp_assoc_disconnecting(sock_upper_handle_t handle, sock_opctl_action_t action,
20228348SEric.Yu@Sun.COM     uintptr_t arg)
20238348SEric.Yu@Sun.COM {
20248348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle;
20258348SEric.Yu@Sun.COM 	struct sonode *so = &ssa->ssa_sonode->ss_so;
20268348SEric.Yu@Sun.COM 
20278348SEric.Yu@Sun.COM 	ASSERT(so->so_type == SOCK_SEQPACKET);
20288348SEric.Yu@Sun.COM 	ASSERT(ssa->ssa_conn != NULL);
20298348SEric.Yu@Sun.COM 	ASSERT(action == SOCK_OPCTL_SHUT_SEND);
20308348SEric.Yu@Sun.COM 
20318348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
20328348SEric.Yu@Sun.COM 	sosctp_assoc_isdisconnecting(ssa);
20338348SEric.Yu@Sun.COM 	mutex_exit(&so->so_lock);
20348348SEric.Yu@Sun.COM }
20358348SEric.Yu@Sun.COM 
20368348SEric.Yu@Sun.COM /* ARGSUSED */
20378348SEric.Yu@Sun.COM static ssize_t
20388348SEric.Yu@Sun.COM sctp_assoc_recv(sock_upper_handle_t handle, mblk_t *mp, size_t len, int flags,
20398348SEric.Yu@Sun.COM     int *errorp, boolean_t *forcepush)
20408348SEric.Yu@Sun.COM {
20418348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle;
20428348SEric.Yu@Sun.COM 	struct sctp_sonode *ss = ssa->ssa_sonode;
20438348SEric.Yu@Sun.COM 	struct sonode *so = &ss->ss_so;
20448348SEric.Yu@Sun.COM 	struct T_unitdata_ind *tind;
20458348SEric.Yu@Sun.COM 	mblk_t *mp2;
20468348SEric.Yu@Sun.COM 	union sctp_notification *sn;
20478348SEric.Yu@Sun.COM 	struct sctp_sndrcvinfo *sinfo;
20488941SAnders.Persson@Sun.COM 	ssize_t space_available;
20498348SEric.Yu@Sun.COM 
20508348SEric.Yu@Sun.COM 	ASSERT(ssa->ssa_type == SOSCTP_ASSOC);
20518348SEric.Yu@Sun.COM 	ASSERT(so->so_type == SOCK_SEQPACKET);
20528348SEric.Yu@Sun.COM 	ASSERT(ssa->ssa_conn != NULL); /* closed conn */
20538348SEric.Yu@Sun.COM 	ASSERT(mp != NULL);
20548348SEric.Yu@Sun.COM 
20558348SEric.Yu@Sun.COM 	ASSERT(errorp != NULL);
20568348SEric.Yu@Sun.COM 	*errorp = 0;
20578348SEric.Yu@Sun.COM 
20588348SEric.Yu@Sun.COM 	/*
20598348SEric.Yu@Sun.COM 	 * Should be getting T_unitdata_req's only.
20608348SEric.Yu@Sun.COM 	 * Must have address as part of packet.
20618348SEric.Yu@Sun.COM 	 */
20628348SEric.Yu@Sun.COM 	tind = (struct T_unitdata_ind *)mp->b_rptr;
20638348SEric.Yu@Sun.COM 	ASSERT((DB_TYPE(mp) == M_PROTO) &&
20648348SEric.Yu@Sun.COM 	    (tind->PRIM_type == T_UNITDATA_IND));
20658348SEric.Yu@Sun.COM 	ASSERT(tind->SRC_length);
20668348SEric.Yu@Sun.COM 
20678348SEric.Yu@Sun.COM 	mutex_enter(&so->so_lock);
20688348SEric.Yu@Sun.COM 
20698348SEric.Yu@Sun.COM 	/*
20708348SEric.Yu@Sun.COM 	 * For notify messages, need to fill in association id.
20718348SEric.Yu@Sun.COM 	 * For data messages, sndrcvinfo could be in ancillary data.
20728348SEric.Yu@Sun.COM 	 */
207310686SRao.Shoaib@Sun.COM 	if (mp->b_flag & SCTP_NOTIFICATION) {
20748348SEric.Yu@Sun.COM 		mp2 = mp->b_cont;
20758348SEric.Yu@Sun.COM 		sn = (union sctp_notification *)mp2->b_rptr;
20768348SEric.Yu@Sun.COM 		switch (sn->sn_header.sn_type) {
20778348SEric.Yu@Sun.COM 		case SCTP_ASSOC_CHANGE:
20788348SEric.Yu@Sun.COM 			sn->sn_assoc_change.sac_assoc_id = ssa->ssa_id;
20798348SEric.Yu@Sun.COM 			break;
20808348SEric.Yu@Sun.COM 		case SCTP_PEER_ADDR_CHANGE:
20818348SEric.Yu@Sun.COM 			sn->sn_paddr_change.spc_assoc_id = ssa->ssa_id;
20828348SEric.Yu@Sun.COM 			break;
20838348SEric.Yu@Sun.COM 		case SCTP_REMOTE_ERROR:
20848348SEric.Yu@Sun.COM 			sn->sn_remote_error.sre_assoc_id = ssa->ssa_id;
20858348SEric.Yu@Sun.COM 			break;
20868348SEric.Yu@Sun.COM 		case SCTP_SEND_FAILED:
20878348SEric.Yu@Sun.COM 			sn->sn_send_failed.ssf_assoc_id = ssa->ssa_id;
20888348SEric.Yu@Sun.COM 			break;
20898348SEric.Yu@Sun.COM 		case SCTP_SHUTDOWN_EVENT:
20908348SEric.Yu@Sun.COM 			sn->sn_shutdown_event.sse_assoc_id = ssa->ssa_id;
20918348SEric.Yu@Sun.COM 			break;
20928348SEric.Yu@Sun.COM 		case SCTP_ADAPTATION_INDICATION:
20938348SEric.Yu@Sun.COM 			sn->sn_adaptation_event.sai_assoc_id = ssa->ssa_id;
20948348SEric.Yu@Sun.COM 			break;
20958348SEric.Yu@Sun.COM 		case SCTP_PARTIAL_DELIVERY_EVENT:
20968348SEric.Yu@Sun.COM 			sn->sn_pdapi_event.pdapi_assoc_id = ssa->ssa_id;
20978348SEric.Yu@Sun.COM 			break;
20988348SEric.Yu@Sun.COM 		default:
20998348SEric.Yu@Sun.COM 			ASSERT(0);
21008348SEric.Yu@Sun.COM 			break;
21018348SEric.Yu@Sun.COM 		}
21028348SEric.Yu@Sun.COM 	} else {
21038348SEric.Yu@Sun.COM 		if (tind->OPT_length > 0) {
21048348SEric.Yu@Sun.COM 			struct cmsghdr	*cmsg;
21058348SEric.Yu@Sun.COM 			char		*cend;
21068348SEric.Yu@Sun.COM 
21078348SEric.Yu@Sun.COM 			cmsg = (struct cmsghdr *)
21088348SEric.Yu@Sun.COM 			    ((uchar_t *)mp->b_rptr + tind->OPT_offset);
21098348SEric.Yu@Sun.COM 			cend = (char *)cmsg + tind->OPT_length;
21108348SEric.Yu@Sun.COM 			for (;;) {
21118348SEric.Yu@Sun.COM 				if ((char *)(cmsg + 1) > cend ||
21128348SEric.Yu@Sun.COM 				    ((char *)cmsg + cmsg->cmsg_len) > cend) {
21138348SEric.Yu@Sun.COM 					break;
21148348SEric.Yu@Sun.COM 				}
21158348SEric.Yu@Sun.COM 				if ((cmsg->cmsg_level == IPPROTO_SCTP) &&
21168348SEric.Yu@Sun.COM 				    (cmsg->cmsg_type == SCTP_SNDRCV)) {
21178348SEric.Yu@Sun.COM 					sinfo = (struct sctp_sndrcvinfo *)
21188348SEric.Yu@Sun.COM 					    (cmsg + 1);
21198348SEric.Yu@Sun.COM 					sinfo->sinfo_assoc_id = ssa->ssa_id;
21208348SEric.Yu@Sun.COM 					break;
21218348SEric.Yu@Sun.COM 				}
21228348SEric.Yu@Sun.COM 				if (cmsg->cmsg_len > 0) {
21238348SEric.Yu@Sun.COM 					cmsg = (struct cmsghdr *)
21248348SEric.Yu@Sun.COM 					    ((uchar_t *)cmsg + cmsg->cmsg_len);
21258348SEric.Yu@Sun.COM 				} else {
21268348SEric.Yu@Sun.COM 					break;
21278348SEric.Yu@Sun.COM 				}
21288348SEric.Yu@Sun.COM 			}
21298348SEric.Yu@Sun.COM 		}
21308348SEric.Yu@Sun.COM 	}
21318348SEric.Yu@Sun.COM 
21328348SEric.Yu@Sun.COM 	/*
21338348SEric.Yu@Sun.COM 	 * SCTP has reserved space in the header for storing a pointer.
21348348SEric.Yu@Sun.COM 	 * Put the pointer to assocation there, and queue the data.
21358348SEric.Yu@Sun.COM 	 */
21368348SEric.Yu@Sun.COM 	SSA_REFHOLD(ssa);
21378348SEric.Yu@Sun.COM 	ASSERT((mp->b_rptr - DB_BASE(mp)) >= sizeof (ssa));
21388348SEric.Yu@Sun.COM 	*(struct sctp_soassoc **)DB_BASE(mp) = ssa;
21398348SEric.Yu@Sun.COM 
21408941SAnders.Persson@Sun.COM 	ssa->ssa_rcv_queued += len;
21418941SAnders.Persson@Sun.COM 	space_available = so->so_rcvbuf - ssa->ssa_rcv_queued;
21428941SAnders.Persson@Sun.COM 	so_enqueue_msg(so, mp, len);
21438348SEric.Yu@Sun.COM 
21448941SAnders.Persson@Sun.COM 	/* so_notify_data drops so_lock */
21458941SAnders.Persson@Sun.COM 	so_notify_data(so, len);
21468941SAnders.Persson@Sun.COM 
21478941SAnders.Persson@Sun.COM 	return (space_available);
21488348SEric.Yu@Sun.COM }
21498348SEric.Yu@Sun.COM 
21508348SEric.Yu@Sun.COM static void
21518348SEric.Yu@Sun.COM sctp_assoc_xmitted(sock_upper_handle_t handle, boolean_t qfull)
21528348SEric.Yu@Sun.COM {
21538348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle;
21548348SEric.Yu@Sun.COM 	struct sctp_sonode *ss = ssa->ssa_sonode;
21558348SEric.Yu@Sun.COM 
21568348SEric.Yu@Sun.COM 	ASSERT(ssa->ssa_type == SOSCTP_ASSOC);
21578348SEric.Yu@Sun.COM 	ASSERT(ss->ss_so.so_type == SOCK_SEQPACKET);
21588348SEric.Yu@Sun.COM 	ASSERT(ssa->ssa_conn != NULL);
21598348SEric.Yu@Sun.COM 
21608348SEric.Yu@Sun.COM 	mutex_enter(&ss->ss_so.so_lock);
21618348SEric.Yu@Sun.COM 
21628348SEric.Yu@Sun.COM 	ssa->ssa_snd_qfull = qfull;
21638348SEric.Yu@Sun.COM 
21648348SEric.Yu@Sun.COM 	/*
21658348SEric.Yu@Sun.COM 	 * Wake blocked writers.
21668348SEric.Yu@Sun.COM 	 */
21678348SEric.Yu@Sun.COM 	cv_broadcast(&ss->ss_so.so_snd_cv);
21688348SEric.Yu@Sun.COM 
21698348SEric.Yu@Sun.COM 	mutex_exit(&ss->ss_so.so_lock);
21708348SEric.Yu@Sun.COM }
21718348SEric.Yu@Sun.COM 
21728348SEric.Yu@Sun.COM static void
21738348SEric.Yu@Sun.COM sctp_assoc_properties(sock_upper_handle_t handle,
21748348SEric.Yu@Sun.COM     struct sock_proto_props *soppp)
21758348SEric.Yu@Sun.COM {
21768348SEric.Yu@Sun.COM 	struct sctp_soassoc *ssa = (struct sctp_soassoc *)handle;
21778348SEric.Yu@Sun.COM 	struct sctp_sonode *ss;
21788348SEric.Yu@Sun.COM 
21798348SEric.Yu@Sun.COM 	if (ssa->ssa_type == SOSCTP_ASSOC) {
21808348SEric.Yu@Sun.COM 		ss = ssa->ssa_sonode;
21818348SEric.Yu@Sun.COM 		mutex_enter(&ss->ss_so.so_lock);
21828348SEric.Yu@Sun.COM 
21838348SEric.Yu@Sun.COM 		/*
21848348SEric.Yu@Sun.COM 		 * Only change them if they're set.
21858348SEric.Yu@Sun.COM 		 */
21868348SEric.Yu@Sun.COM 		if (soppp->sopp_wroff != 0) {
21878348SEric.Yu@Sun.COM 			ssa->ssa_wroff = soppp->sopp_wroff;
21888348SEric.Yu@Sun.COM 		}
21898348SEric.Yu@Sun.COM 		if (soppp->sopp_maxblk != 0) {
21908348SEric.Yu@Sun.COM 			ssa->ssa_wrsize = soppp->sopp_maxblk;
21918348SEric.Yu@Sun.COM 		}
21928348SEric.Yu@Sun.COM 	} else {
21938348SEric.Yu@Sun.COM 		ss = (struct sctp_sonode *)handle;
21948348SEric.Yu@Sun.COM 		mutex_enter(&ss->ss_so.so_lock);
21958348SEric.Yu@Sun.COM 
21968348SEric.Yu@Sun.COM 		if (soppp->sopp_wroff != 0) {
21978348SEric.Yu@Sun.COM 			ss->ss_wroff = soppp->sopp_wroff;
21988348SEric.Yu@Sun.COM 		}
21998348SEric.Yu@Sun.COM 		if (soppp->sopp_maxblk != 0) {
22008348SEric.Yu@Sun.COM 			ss->ss_wrsize = soppp->sopp_maxblk;
22018348SEric.Yu@Sun.COM 		}
22028348SEric.Yu@Sun.COM 	}
22038348SEric.Yu@Sun.COM 
22048348SEric.Yu@Sun.COM 	mutex_exit(&ss->ss_so.so_lock);
22058348SEric.Yu@Sun.COM }
2206