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