xref: /onnv-gate/usr/src/uts/common/inet/tcp/tcp_socket.c (revision 13062:36a559d3de13)
111754SKacheong.Poon@Sun.COM /*
211754SKacheong.Poon@Sun.COM  * CDDL HEADER START
311754SKacheong.Poon@Sun.COM  *
411754SKacheong.Poon@Sun.COM  * The contents of this file are subject to the terms of the
511754SKacheong.Poon@Sun.COM  * Common Development and Distribution License (the "License").
611754SKacheong.Poon@Sun.COM  * You may not use this file except in compliance with the License.
711754SKacheong.Poon@Sun.COM  *
811754SKacheong.Poon@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911754SKacheong.Poon@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011754SKacheong.Poon@Sun.COM  * See the License for the specific language governing permissions
1111754SKacheong.Poon@Sun.COM  * and limitations under the License.
1211754SKacheong.Poon@Sun.COM  *
1311754SKacheong.Poon@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411754SKacheong.Poon@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511754SKacheong.Poon@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611754SKacheong.Poon@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711754SKacheong.Poon@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811754SKacheong.Poon@Sun.COM  *
1911754SKacheong.Poon@Sun.COM  * CDDL HEADER END
2011754SKacheong.Poon@Sun.COM  */
2111754SKacheong.Poon@Sun.COM 
2211754SKacheong.Poon@Sun.COM /*
2312504SAnders.Persson@Sun.COM  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2411754SKacheong.Poon@Sun.COM  */
2511754SKacheong.Poon@Sun.COM 
2611754SKacheong.Poon@Sun.COM /* This file contains all TCP kernel socket related functions. */
2711754SKacheong.Poon@Sun.COM 
2811754SKacheong.Poon@Sun.COM #include <sys/types.h>
2911754SKacheong.Poon@Sun.COM #include <sys/strlog.h>
3011754SKacheong.Poon@Sun.COM #include <sys/policy.h>
3111754SKacheong.Poon@Sun.COM #include <sys/sockio.h>
3211754SKacheong.Poon@Sun.COM #include <sys/strsubr.h>
3311754SKacheong.Poon@Sun.COM #include <sys/strsun.h>
3411754SKacheong.Poon@Sun.COM #include <sys/squeue_impl.h>
3511754SKacheong.Poon@Sun.COM #include <sys/squeue.h>
3612643SAnders.Persson@Sun.COM #define	_SUN_TPI_VERSION 2
3711754SKacheong.Poon@Sun.COM #include <sys/tihdr.h>
3811754SKacheong.Poon@Sun.COM #include <sys/timod.h>
3911754SKacheong.Poon@Sun.COM #include <sys/tpicommon.h>
4011754SKacheong.Poon@Sun.COM #include <sys/socketvar.h>
4111754SKacheong.Poon@Sun.COM 
4211754SKacheong.Poon@Sun.COM #include <inet/common.h>
4311754SKacheong.Poon@Sun.COM #include <inet/proto_set.h>
4411754SKacheong.Poon@Sun.COM #include <inet/ip.h>
4511754SKacheong.Poon@Sun.COM #include <inet/tcp.h>
4611754SKacheong.Poon@Sun.COM #include <inet/tcp_impl.h>
4711754SKacheong.Poon@Sun.COM 
4811754SKacheong.Poon@Sun.COM static void	tcp_activate(sock_lower_handle_t, sock_upper_handle_t,
4911754SKacheong.Poon@Sun.COM 		    sock_upcalls_t *, int, cred_t *);
5011754SKacheong.Poon@Sun.COM static int	tcp_accept(sock_lower_handle_t, sock_lower_handle_t,
5111754SKacheong.Poon@Sun.COM 		    sock_upper_handle_t, cred_t *);
5211754SKacheong.Poon@Sun.COM static int	tcp_bind(sock_lower_handle_t, struct sockaddr *,
5311754SKacheong.Poon@Sun.COM 		    socklen_t, cred_t *);
5411754SKacheong.Poon@Sun.COM static int	tcp_listen(sock_lower_handle_t, int, cred_t *);
5511754SKacheong.Poon@Sun.COM static int	tcp_connect(sock_lower_handle_t, const struct sockaddr *,
5611754SKacheong.Poon@Sun.COM 		    socklen_t, sock_connid_t *, cred_t *);
5711754SKacheong.Poon@Sun.COM static int	tcp_getsockopt(sock_lower_handle_t, int, int, void *,
5811754SKacheong.Poon@Sun.COM 		    socklen_t *, cred_t *);
5911754SKacheong.Poon@Sun.COM static int	tcp_setsockopt(sock_lower_handle_t, int, int, const void *,
6011754SKacheong.Poon@Sun.COM 		    socklen_t, cred_t *);
6111754SKacheong.Poon@Sun.COM static int	tcp_sendmsg(sock_lower_handle_t, mblk_t *, struct nmsghdr *,
6211754SKacheong.Poon@Sun.COM 		    cred_t *cr);
6311754SKacheong.Poon@Sun.COM static int	tcp_shutdown(sock_lower_handle_t, int, cred_t *);
6411754SKacheong.Poon@Sun.COM static void	tcp_clr_flowctrl(sock_lower_handle_t);
6511754SKacheong.Poon@Sun.COM static int	tcp_ioctl(sock_lower_handle_t, int, intptr_t, int, int32_t *,
6611754SKacheong.Poon@Sun.COM 		    cred_t *);
6711754SKacheong.Poon@Sun.COM static int	tcp_close(sock_lower_handle_t, int, cred_t *);
6811754SKacheong.Poon@Sun.COM 
6911754SKacheong.Poon@Sun.COM sock_downcalls_t sock_tcp_downcalls = {
7011754SKacheong.Poon@Sun.COM 	tcp_activate,
7111754SKacheong.Poon@Sun.COM 	tcp_accept,
7211754SKacheong.Poon@Sun.COM 	tcp_bind,
7311754SKacheong.Poon@Sun.COM 	tcp_listen,
7411754SKacheong.Poon@Sun.COM 	tcp_connect,
7511754SKacheong.Poon@Sun.COM 	tcp_getpeername,
7611754SKacheong.Poon@Sun.COM 	tcp_getsockname,
7711754SKacheong.Poon@Sun.COM 	tcp_getsockopt,
7811754SKacheong.Poon@Sun.COM 	tcp_setsockopt,
7911754SKacheong.Poon@Sun.COM 	tcp_sendmsg,
8011754SKacheong.Poon@Sun.COM 	NULL,
8111754SKacheong.Poon@Sun.COM 	NULL,
8211754SKacheong.Poon@Sun.COM 	NULL,
8311754SKacheong.Poon@Sun.COM 	tcp_shutdown,
8411754SKacheong.Poon@Sun.COM 	tcp_clr_flowctrl,
8511754SKacheong.Poon@Sun.COM 	tcp_ioctl,
8611754SKacheong.Poon@Sun.COM 	tcp_close,
8711754SKacheong.Poon@Sun.COM };
8811754SKacheong.Poon@Sun.COM 
8911754SKacheong.Poon@Sun.COM /* ARGSUSED */
9011754SKacheong.Poon@Sun.COM static void
tcp_activate(sock_lower_handle_t proto_handle,sock_upper_handle_t sock_handle,sock_upcalls_t * sock_upcalls,int flags,cred_t * cr)9111754SKacheong.Poon@Sun.COM tcp_activate(sock_lower_handle_t proto_handle, sock_upper_handle_t sock_handle,
9211754SKacheong.Poon@Sun.COM     sock_upcalls_t *sock_upcalls, int flags, cred_t *cr)
9311754SKacheong.Poon@Sun.COM {
9411754SKacheong.Poon@Sun.COM 	conn_t *connp = (conn_t *)proto_handle;
9511754SKacheong.Poon@Sun.COM 	struct sock_proto_props sopp;
9611754SKacheong.Poon@Sun.COM 	extern struct module_info tcp_rinfo;
9711754SKacheong.Poon@Sun.COM 
9811754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_upper_handle == NULL);
9911754SKacheong.Poon@Sun.COM 
10011754SKacheong.Poon@Sun.COM 	/* All Solaris components should pass a cred for this operation. */
10111754SKacheong.Poon@Sun.COM 	ASSERT(cr != NULL);
10211754SKacheong.Poon@Sun.COM 
10311754SKacheong.Poon@Sun.COM 	sopp.sopp_flags = SOCKOPT_RCVHIWAT | SOCKOPT_RCVLOWAT |
10411754SKacheong.Poon@Sun.COM 	    SOCKOPT_MAXPSZ | SOCKOPT_MAXBLK | SOCKOPT_RCVTIMER |
10511754SKacheong.Poon@Sun.COM 	    SOCKOPT_RCVTHRESH | SOCKOPT_MAXADDRLEN | SOCKOPT_MINPSZ;
10611754SKacheong.Poon@Sun.COM 
10711754SKacheong.Poon@Sun.COM 	sopp.sopp_rxhiwat = SOCKET_RECVHIWATER;
10811754SKacheong.Poon@Sun.COM 	sopp.sopp_rxlowat = SOCKET_RECVLOWATER;
10911754SKacheong.Poon@Sun.COM 	sopp.sopp_maxpsz = INFPSZ;
11011754SKacheong.Poon@Sun.COM 	sopp.sopp_maxblk = INFPSZ;
11111754SKacheong.Poon@Sun.COM 	sopp.sopp_rcvtimer = SOCKET_TIMER_INTERVAL;
11211754SKacheong.Poon@Sun.COM 	sopp.sopp_rcvthresh = SOCKET_RECVHIWATER >> 3;
11311754SKacheong.Poon@Sun.COM 	sopp.sopp_maxaddrlen = sizeof (sin6_t);
11411754SKacheong.Poon@Sun.COM 	sopp.sopp_minpsz = (tcp_rinfo.mi_minpsz == 1) ? 0 :
11511754SKacheong.Poon@Sun.COM 	    tcp_rinfo.mi_minpsz;
11611754SKacheong.Poon@Sun.COM 
11711754SKacheong.Poon@Sun.COM 	connp->conn_upcalls = sock_upcalls;
11811754SKacheong.Poon@Sun.COM 	connp->conn_upper_handle = sock_handle;
11911754SKacheong.Poon@Sun.COM 
12011754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_rcvbuf != 0 &&
12111754SKacheong.Poon@Sun.COM 	    connp->conn_rcvbuf == connp->conn_tcp->tcp_rwnd);
12211754SKacheong.Poon@Sun.COM 	(*sock_upcalls->su_set_proto_props)(sock_handle, &sopp);
12311754SKacheong.Poon@Sun.COM }
12411754SKacheong.Poon@Sun.COM 
12512643SAnders.Persson@Sun.COM /*ARGSUSED*/
12611754SKacheong.Poon@Sun.COM static int
tcp_accept(sock_lower_handle_t lproto_handle,sock_lower_handle_t eproto_handle,sock_upper_handle_t sock_handle,cred_t * cr)12711754SKacheong.Poon@Sun.COM tcp_accept(sock_lower_handle_t lproto_handle,
12811754SKacheong.Poon@Sun.COM     sock_lower_handle_t eproto_handle, sock_upper_handle_t sock_handle,
12911754SKacheong.Poon@Sun.COM     cred_t *cr)
13011754SKacheong.Poon@Sun.COM {
13111754SKacheong.Poon@Sun.COM 	conn_t *lconnp, *econnp;
13211754SKacheong.Poon@Sun.COM 	tcp_t *listener, *eager;
13311754SKacheong.Poon@Sun.COM 
13412644SAnders.Persson@Sun.COM 	/*
13512644SAnders.Persson@Sun.COM 	 * KSSL can move a socket from one listener to another, in which
13612644SAnders.Persson@Sun.COM 	 * case `lproto_handle' points to the new listener. To ensure that
13712644SAnders.Persson@Sun.COM 	 * the original listener is used the information is obtained from
13812644SAnders.Persson@Sun.COM 	 * the eager.
13912644SAnders.Persson@Sun.COM 	 */
14011754SKacheong.Poon@Sun.COM 	econnp = (conn_t *)eproto_handle;
14111754SKacheong.Poon@Sun.COM 	eager = econnp->conn_tcp;
14212644SAnders.Persson@Sun.COM 	ASSERT(IPCL_IS_NONSTR(econnp));
14311754SKacheong.Poon@Sun.COM 	ASSERT(eager->tcp_listener != NULL);
14412644SAnders.Persson@Sun.COM 	listener = eager->tcp_listener;
14512644SAnders.Persson@Sun.COM 	lconnp = (conn_t *)listener->tcp_connp;
14612644SAnders.Persson@Sun.COM 	ASSERT(listener->tcp_state == TCPS_LISTEN);
14712643SAnders.Persson@Sun.COM 	ASSERT(lconnp->conn_upper_handle != NULL);
14812643SAnders.Persson@Sun.COM 
14912643SAnders.Persson@Sun.COM 	/*
15012643SAnders.Persson@Sun.COM 	 * It is possible for the accept thread to race with the thread that
15112643SAnders.Persson@Sun.COM 	 * made the su_newconn upcall in tcp_newconn_notify. Both
15212643SAnders.Persson@Sun.COM 	 * tcp_newconn_notify and tcp_accept require that conn_upper_handle
15312643SAnders.Persson@Sun.COM 	 * and conn_upcalls be set before returning, so they both write to
15412643SAnders.Persson@Sun.COM 	 * them. However, we're guaranteed that the value written is the same
15512643SAnders.Persson@Sun.COM 	 * for both threads.
15612643SAnders.Persson@Sun.COM 	 */
15712643SAnders.Persson@Sun.COM 	ASSERT(econnp->conn_upper_handle == NULL ||
15812643SAnders.Persson@Sun.COM 	    econnp->conn_upper_handle == sock_handle);
15912643SAnders.Persson@Sun.COM 	ASSERT(econnp->conn_upcalls == NULL ||
16012643SAnders.Persson@Sun.COM 	    econnp->conn_upcalls == lconnp->conn_upcalls);
16112643SAnders.Persson@Sun.COM 	econnp->conn_upper_handle = sock_handle;
16212643SAnders.Persson@Sun.COM 	econnp->conn_upcalls = lconnp->conn_upcalls;
16312643SAnders.Persson@Sun.COM 
16412643SAnders.Persson@Sun.COM 	ASSERT(econnp->conn_netstack ==
16512643SAnders.Persson@Sun.COM 	    listener->tcp_connp->conn_netstack);
16612643SAnders.Persson@Sun.COM 	ASSERT(eager->tcp_tcps == listener->tcp_tcps);
16711754SKacheong.Poon@Sun.COM 
16811754SKacheong.Poon@Sun.COM 	/*
16912643SAnders.Persson@Sun.COM 	 * We should have a minimum of 2 references on the conn at this
17012643SAnders.Persson@Sun.COM 	 * point. One for TCP and one for the newconn notification
17112643SAnders.Persson@Sun.COM 	 * (which is now taken over by IP). In the normal case we would
17212643SAnders.Persson@Sun.COM 	 * also have another reference (making a total of 3) for the conn
17312643SAnders.Persson@Sun.COM 	 * being in the classifier hash list. However the eager could have
17412643SAnders.Persson@Sun.COM 	 * received an RST subsequently and tcp_closei_local could have
17512643SAnders.Persson@Sun.COM 	 * removed the eager from the classifier hash list, hence we can't
17612643SAnders.Persson@Sun.COM 	 * assert that reference.
17711754SKacheong.Poon@Sun.COM 	 */
17812643SAnders.Persson@Sun.COM 	ASSERT(econnp->conn_ref >= 2);
17912643SAnders.Persson@Sun.COM 
18012643SAnders.Persson@Sun.COM 	mutex_enter(&listener->tcp_eager_lock);
18112643SAnders.Persson@Sun.COM 	/*
18212643SAnders.Persson@Sun.COM 	 * Non-STREAMS listeners never defer the notification of new
18312643SAnders.Persson@Sun.COM 	 * connections.
18412643SAnders.Persson@Sun.COM 	 */
18512643SAnders.Persson@Sun.COM 	ASSERT(!listener->tcp_eager_prev_q0->tcp_conn_def_q0);
18612643SAnders.Persson@Sun.COM 	tcp_eager_unlink(eager);
18712643SAnders.Persson@Sun.COM 	mutex_exit(&listener->tcp_eager_lock);
18812643SAnders.Persson@Sun.COM 	CONN_DEC_REF(listener->tcp_connp);
18912643SAnders.Persson@Sun.COM 
19012840SAnders.Persson@Sun.COM 	return ((eager->tcp_state < TCPS_ESTABLISHED) ? ECONNABORTED : 0);
19111754SKacheong.Poon@Sun.COM }
19211754SKacheong.Poon@Sun.COM 
19311754SKacheong.Poon@Sun.COM static int
tcp_bind(sock_lower_handle_t proto_handle,struct sockaddr * sa,socklen_t len,cred_t * cr)19411754SKacheong.Poon@Sun.COM tcp_bind(sock_lower_handle_t proto_handle, struct sockaddr *sa,
19511754SKacheong.Poon@Sun.COM     socklen_t len, cred_t *cr)
19611754SKacheong.Poon@Sun.COM {
19711754SKacheong.Poon@Sun.COM 	int 		error;
19811754SKacheong.Poon@Sun.COM 	conn_t		*connp = (conn_t *)proto_handle;
19911754SKacheong.Poon@Sun.COM 
20011754SKacheong.Poon@Sun.COM 	/* All Solaris components should pass a cred for this operation. */
20111754SKacheong.Poon@Sun.COM 	ASSERT(cr != NULL);
20211754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_upper_handle != NULL);
20311754SKacheong.Poon@Sun.COM 
20412504SAnders.Persson@Sun.COM 	error = squeue_synch_enter(connp, NULL);
20511754SKacheong.Poon@Sun.COM 	if (error != 0) {
20611754SKacheong.Poon@Sun.COM 		/* failed to enter */
20711754SKacheong.Poon@Sun.COM 		return (ENOSR);
20811754SKacheong.Poon@Sun.COM 	}
20911754SKacheong.Poon@Sun.COM 
21011754SKacheong.Poon@Sun.COM 	/* binding to a NULL address really means unbind */
21111754SKacheong.Poon@Sun.COM 	if (sa == NULL) {
21211754SKacheong.Poon@Sun.COM 		if (connp->conn_tcp->tcp_state < TCPS_LISTEN)
21311754SKacheong.Poon@Sun.COM 			error = tcp_do_unbind(connp);
21411754SKacheong.Poon@Sun.COM 		else
21511754SKacheong.Poon@Sun.COM 			error = EINVAL;
21611754SKacheong.Poon@Sun.COM 	} else {
21711754SKacheong.Poon@Sun.COM 		error = tcp_do_bind(connp, sa, len, cr, B_TRUE);
21811754SKacheong.Poon@Sun.COM 	}
21911754SKacheong.Poon@Sun.COM 
22012504SAnders.Persson@Sun.COM 	squeue_synch_exit(connp);
22111754SKacheong.Poon@Sun.COM 
22211754SKacheong.Poon@Sun.COM 	if (error < 0) {
22311754SKacheong.Poon@Sun.COM 		if (error == -TOUTSTATE)
22411754SKacheong.Poon@Sun.COM 			error = EINVAL;
22511754SKacheong.Poon@Sun.COM 		else
22611754SKacheong.Poon@Sun.COM 			error = proto_tlitosyserr(-error);
22711754SKacheong.Poon@Sun.COM 	}
22811754SKacheong.Poon@Sun.COM 
22911754SKacheong.Poon@Sun.COM 	return (error);
23011754SKacheong.Poon@Sun.COM }
23111754SKacheong.Poon@Sun.COM 
23211754SKacheong.Poon@Sun.COM /* ARGSUSED */
23311754SKacheong.Poon@Sun.COM static int
tcp_listen(sock_lower_handle_t proto_handle,int backlog,cred_t * cr)23411754SKacheong.Poon@Sun.COM tcp_listen(sock_lower_handle_t proto_handle, int backlog, cred_t *cr)
23511754SKacheong.Poon@Sun.COM {
23611754SKacheong.Poon@Sun.COM 	conn_t	*connp = (conn_t *)proto_handle;
23712643SAnders.Persson@Sun.COM 	tcp_t	*tcp = connp->conn_tcp;
23811754SKacheong.Poon@Sun.COM 	int 	error;
23911754SKacheong.Poon@Sun.COM 
24011754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_upper_handle != NULL);
24111754SKacheong.Poon@Sun.COM 
24211754SKacheong.Poon@Sun.COM 	/* All Solaris components should pass a cred for this operation. */
24311754SKacheong.Poon@Sun.COM 	ASSERT(cr != NULL);
24411754SKacheong.Poon@Sun.COM 
24512504SAnders.Persson@Sun.COM 	error = squeue_synch_enter(connp, NULL);
24611754SKacheong.Poon@Sun.COM 	if (error != 0) {
24711754SKacheong.Poon@Sun.COM 		/* failed to enter */
24811754SKacheong.Poon@Sun.COM 		return (ENOBUFS);
24911754SKacheong.Poon@Sun.COM 	}
25011754SKacheong.Poon@Sun.COM 
25111754SKacheong.Poon@Sun.COM 	error = tcp_do_listen(connp, NULL, 0, backlog, cr, B_FALSE);
25211754SKacheong.Poon@Sun.COM 	if (error == 0) {
25312643SAnders.Persson@Sun.COM 		/*
25412643SAnders.Persson@Sun.COM 		 * sockfs needs to know what's the maximum number of socket
25512643SAnders.Persson@Sun.COM 		 * that can be queued on the listener.
25612643SAnders.Persson@Sun.COM 		 */
25711754SKacheong.Poon@Sun.COM 		(*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle,
25812643SAnders.Persson@Sun.COM 		    SOCK_OPCTL_ENAB_ACCEPT,
25912643SAnders.Persson@Sun.COM 		    (uintptr_t)(tcp->tcp_conn_req_max +
26012643SAnders.Persson@Sun.COM 		    tcp->tcp_tcps->tcps_conn_req_max_q0));
26111754SKacheong.Poon@Sun.COM 	} else if (error < 0) {
26211754SKacheong.Poon@Sun.COM 		if (error == -TOUTSTATE)
26311754SKacheong.Poon@Sun.COM 			error = EINVAL;
26411754SKacheong.Poon@Sun.COM 		else
26511754SKacheong.Poon@Sun.COM 			error = proto_tlitosyserr(-error);
26611754SKacheong.Poon@Sun.COM 	}
26712504SAnders.Persson@Sun.COM 	squeue_synch_exit(connp);
26811754SKacheong.Poon@Sun.COM 	return (error);
26911754SKacheong.Poon@Sun.COM }
27011754SKacheong.Poon@Sun.COM 
27111754SKacheong.Poon@Sun.COM static int
tcp_connect(sock_lower_handle_t proto_handle,const struct sockaddr * sa,socklen_t len,sock_connid_t * id,cred_t * cr)27211754SKacheong.Poon@Sun.COM tcp_connect(sock_lower_handle_t proto_handle, const struct sockaddr *sa,
27311754SKacheong.Poon@Sun.COM     socklen_t len, sock_connid_t *id, cred_t *cr)
27411754SKacheong.Poon@Sun.COM {
27511754SKacheong.Poon@Sun.COM 	conn_t		*connp = (conn_t *)proto_handle;
27611754SKacheong.Poon@Sun.COM 	int		error;
27711754SKacheong.Poon@Sun.COM 
27811754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_upper_handle != NULL);
27911754SKacheong.Poon@Sun.COM 
28011754SKacheong.Poon@Sun.COM 	/* All Solaris components should pass a cred for this operation. */
28111754SKacheong.Poon@Sun.COM 	ASSERT(cr != NULL);
28211754SKacheong.Poon@Sun.COM 
28311754SKacheong.Poon@Sun.COM 	error = proto_verify_ip_addr(connp->conn_family, sa, len);
28411754SKacheong.Poon@Sun.COM 	if (error != 0) {
28511754SKacheong.Poon@Sun.COM 		return (error);
28611754SKacheong.Poon@Sun.COM 	}
28711754SKacheong.Poon@Sun.COM 
28812504SAnders.Persson@Sun.COM 	error = squeue_synch_enter(connp, NULL);
28911754SKacheong.Poon@Sun.COM 	if (error != 0) {
29011754SKacheong.Poon@Sun.COM 		/* failed to enter */
29111754SKacheong.Poon@Sun.COM 		return (ENOSR);
29211754SKacheong.Poon@Sun.COM 	}
29311754SKacheong.Poon@Sun.COM 
29411754SKacheong.Poon@Sun.COM 	/*
29511754SKacheong.Poon@Sun.COM 	 * TCP supports quick connect, so no need to do an implicit bind
29611754SKacheong.Poon@Sun.COM 	 */
29711754SKacheong.Poon@Sun.COM 	error = tcp_do_connect(connp, sa, len, cr, curproc->p_pid);
29811754SKacheong.Poon@Sun.COM 	if (error == 0) {
29911754SKacheong.Poon@Sun.COM 		*id = connp->conn_tcp->tcp_connid;
30011754SKacheong.Poon@Sun.COM 	} else if (error < 0) {
30111754SKacheong.Poon@Sun.COM 		if (error == -TOUTSTATE) {
30211754SKacheong.Poon@Sun.COM 			switch (connp->conn_tcp->tcp_state) {
30311754SKacheong.Poon@Sun.COM 			case TCPS_SYN_SENT:
30411754SKacheong.Poon@Sun.COM 				error = EALREADY;
30511754SKacheong.Poon@Sun.COM 				break;
30611754SKacheong.Poon@Sun.COM 			case TCPS_ESTABLISHED:
30711754SKacheong.Poon@Sun.COM 				error = EISCONN;
30811754SKacheong.Poon@Sun.COM 				break;
30911754SKacheong.Poon@Sun.COM 			case TCPS_LISTEN:
31011754SKacheong.Poon@Sun.COM 				error = EOPNOTSUPP;
31111754SKacheong.Poon@Sun.COM 				break;
31211754SKacheong.Poon@Sun.COM 			default:
31311754SKacheong.Poon@Sun.COM 				error = EINVAL;
31411754SKacheong.Poon@Sun.COM 				break;
31511754SKacheong.Poon@Sun.COM 			}
31611754SKacheong.Poon@Sun.COM 		} else {
31711754SKacheong.Poon@Sun.COM 			error = proto_tlitosyserr(-error);
31811754SKacheong.Poon@Sun.COM 		}
31911754SKacheong.Poon@Sun.COM 	}
32011754SKacheong.Poon@Sun.COM 
32111754SKacheong.Poon@Sun.COM 	if (connp->conn_tcp->tcp_loopback) {
32211754SKacheong.Poon@Sun.COM 		struct sock_proto_props sopp;
32311754SKacheong.Poon@Sun.COM 
32411754SKacheong.Poon@Sun.COM 		sopp.sopp_flags = SOCKOPT_LOOPBACK;
32511754SKacheong.Poon@Sun.COM 		sopp.sopp_loopback = B_TRUE;
32611754SKacheong.Poon@Sun.COM 
32711754SKacheong.Poon@Sun.COM 		(*connp->conn_upcalls->su_set_proto_props)(
32811754SKacheong.Poon@Sun.COM 		    connp->conn_upper_handle, &sopp);
32911754SKacheong.Poon@Sun.COM 	}
33011754SKacheong.Poon@Sun.COM done:
33112504SAnders.Persson@Sun.COM 	squeue_synch_exit(connp);
33211754SKacheong.Poon@Sun.COM 
33311754SKacheong.Poon@Sun.COM 	return ((error == 0) ? EINPROGRESS : error);
33411754SKacheong.Poon@Sun.COM }
33511754SKacheong.Poon@Sun.COM 
33611754SKacheong.Poon@Sun.COM /* ARGSUSED3 */
33711754SKacheong.Poon@Sun.COM int
tcp_getpeername(sock_lower_handle_t proto_handle,struct sockaddr * addr,socklen_t * addrlenp,cred_t * cr)33811754SKacheong.Poon@Sun.COM tcp_getpeername(sock_lower_handle_t proto_handle, struct sockaddr *addr,
33911754SKacheong.Poon@Sun.COM     socklen_t *addrlenp, cred_t *cr)
34011754SKacheong.Poon@Sun.COM {
34111754SKacheong.Poon@Sun.COM 	conn_t	*connp = (conn_t *)proto_handle;
34211754SKacheong.Poon@Sun.COM 	tcp_t	*tcp = connp->conn_tcp;
34311754SKacheong.Poon@Sun.COM 
34411754SKacheong.Poon@Sun.COM 	/* All Solaris components should pass a cred for this operation. */
34511754SKacheong.Poon@Sun.COM 	ASSERT(cr != NULL);
34611754SKacheong.Poon@Sun.COM 
34711754SKacheong.Poon@Sun.COM 	ASSERT(tcp != NULL);
34811754SKacheong.Poon@Sun.COM 	if (tcp->tcp_state < TCPS_SYN_RCVD)
34911754SKacheong.Poon@Sun.COM 		return (ENOTCONN);
35011754SKacheong.Poon@Sun.COM 
35111754SKacheong.Poon@Sun.COM 	return (conn_getpeername(connp, addr, addrlenp));
35211754SKacheong.Poon@Sun.COM }
35311754SKacheong.Poon@Sun.COM 
35411754SKacheong.Poon@Sun.COM /* ARGSUSED3 */
35511754SKacheong.Poon@Sun.COM int
tcp_getsockname(sock_lower_handle_t proto_handle,struct sockaddr * addr,socklen_t * addrlenp,cred_t * cr)35611754SKacheong.Poon@Sun.COM tcp_getsockname(sock_lower_handle_t proto_handle, struct sockaddr *addr,
35711754SKacheong.Poon@Sun.COM     socklen_t *addrlenp, cred_t *cr)
35811754SKacheong.Poon@Sun.COM {
35911754SKacheong.Poon@Sun.COM 	conn_t	*connp = (conn_t *)proto_handle;
36011754SKacheong.Poon@Sun.COM 
36111754SKacheong.Poon@Sun.COM 	/* All Solaris components should pass a cred for this operation. */
36211754SKacheong.Poon@Sun.COM 	ASSERT(cr != NULL);
36311754SKacheong.Poon@Sun.COM 
36411754SKacheong.Poon@Sun.COM 	return (conn_getsockname(connp, addr, addrlenp));
36511754SKacheong.Poon@Sun.COM }
36611754SKacheong.Poon@Sun.COM 
36711754SKacheong.Poon@Sun.COM /* returns UNIX error, the optlen is a value-result arg */
36811754SKacheong.Poon@Sun.COM static int
tcp_getsockopt(sock_lower_handle_t proto_handle,int level,int option_name,void * optvalp,socklen_t * optlen,cred_t * cr)36911754SKacheong.Poon@Sun.COM tcp_getsockopt(sock_lower_handle_t proto_handle, int level, int option_name,
37011754SKacheong.Poon@Sun.COM     void *optvalp, socklen_t *optlen, cred_t *cr)
37111754SKacheong.Poon@Sun.COM {
37211754SKacheong.Poon@Sun.COM 	conn_t		*connp = (conn_t *)proto_handle;
37311754SKacheong.Poon@Sun.COM 	int		error;
37411754SKacheong.Poon@Sun.COM 	t_uscalar_t	max_optbuf_len;
37511754SKacheong.Poon@Sun.COM 	void		*optvalp_buf;
37611754SKacheong.Poon@Sun.COM 	int		len;
37711754SKacheong.Poon@Sun.COM 
37811754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_upper_handle != NULL);
37911754SKacheong.Poon@Sun.COM 
38011754SKacheong.Poon@Sun.COM 	error = proto_opt_check(level, option_name, *optlen, &max_optbuf_len,
38111754SKacheong.Poon@Sun.COM 	    tcp_opt_obj.odb_opt_des_arr,
38211754SKacheong.Poon@Sun.COM 	    tcp_opt_obj.odb_opt_arr_cnt,
38311754SKacheong.Poon@Sun.COM 	    B_FALSE, B_TRUE, cr);
38411754SKacheong.Poon@Sun.COM 	if (error != 0) {
38511754SKacheong.Poon@Sun.COM 		if (error < 0) {
38611754SKacheong.Poon@Sun.COM 			error = proto_tlitosyserr(-error);
38711754SKacheong.Poon@Sun.COM 		}
38811754SKacheong.Poon@Sun.COM 		return (error);
38911754SKacheong.Poon@Sun.COM 	}
39011754SKacheong.Poon@Sun.COM 
39111754SKacheong.Poon@Sun.COM 	optvalp_buf = kmem_alloc(max_optbuf_len, KM_SLEEP);
39211754SKacheong.Poon@Sun.COM 
39312504SAnders.Persson@Sun.COM 	error = squeue_synch_enter(connp, NULL);
39411754SKacheong.Poon@Sun.COM 	if (error == ENOMEM) {
39511754SKacheong.Poon@Sun.COM 		kmem_free(optvalp_buf, max_optbuf_len);
39611754SKacheong.Poon@Sun.COM 		return (ENOMEM);
39711754SKacheong.Poon@Sun.COM 	}
39811754SKacheong.Poon@Sun.COM 
39911754SKacheong.Poon@Sun.COM 	len = tcp_opt_get(connp, level, option_name, optvalp_buf);
40012504SAnders.Persson@Sun.COM 	squeue_synch_exit(connp);
40111754SKacheong.Poon@Sun.COM 
40211754SKacheong.Poon@Sun.COM 	if (len == -1) {
40311754SKacheong.Poon@Sun.COM 		kmem_free(optvalp_buf, max_optbuf_len);
40411754SKacheong.Poon@Sun.COM 		return (EINVAL);
40511754SKacheong.Poon@Sun.COM 	}
40611754SKacheong.Poon@Sun.COM 
40711754SKacheong.Poon@Sun.COM 	/*
40811754SKacheong.Poon@Sun.COM 	 * update optlen and copy option value
40911754SKacheong.Poon@Sun.COM 	 */
41011754SKacheong.Poon@Sun.COM 	t_uscalar_t size = MIN(len, *optlen);
41111754SKacheong.Poon@Sun.COM 
41211754SKacheong.Poon@Sun.COM 	bcopy(optvalp_buf, optvalp, size);
41311754SKacheong.Poon@Sun.COM 	bcopy(&size, optlen, sizeof (size));
41411754SKacheong.Poon@Sun.COM 
41511754SKacheong.Poon@Sun.COM 	kmem_free(optvalp_buf, max_optbuf_len);
41611754SKacheong.Poon@Sun.COM 	return (0);
41711754SKacheong.Poon@Sun.COM }
41811754SKacheong.Poon@Sun.COM 
41911754SKacheong.Poon@Sun.COM static int
tcp_setsockopt(sock_lower_handle_t proto_handle,int level,int option_name,const void * optvalp,socklen_t optlen,cred_t * cr)42011754SKacheong.Poon@Sun.COM tcp_setsockopt(sock_lower_handle_t proto_handle, int level, int option_name,
42111754SKacheong.Poon@Sun.COM     const void *optvalp, socklen_t optlen, cred_t *cr)
42211754SKacheong.Poon@Sun.COM {
42311754SKacheong.Poon@Sun.COM 	conn_t		*connp = (conn_t *)proto_handle;
42411754SKacheong.Poon@Sun.COM 	int		error;
42511754SKacheong.Poon@Sun.COM 
42611754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_upper_handle != NULL);
42711754SKacheong.Poon@Sun.COM 	/*
42811754SKacheong.Poon@Sun.COM 	 * Entering the squeue synchronously can result in a context switch,
42911754SKacheong.Poon@Sun.COM 	 * which can cause a rather sever performance degradation. So we try to
43011754SKacheong.Poon@Sun.COM 	 * handle whatever options we can without entering the squeue.
43111754SKacheong.Poon@Sun.COM 	 */
43211754SKacheong.Poon@Sun.COM 	if (level == IPPROTO_TCP) {
43311754SKacheong.Poon@Sun.COM 		switch (option_name) {
43411754SKacheong.Poon@Sun.COM 		case TCP_NODELAY:
43511754SKacheong.Poon@Sun.COM 			if (optlen != sizeof (int32_t))
43611754SKacheong.Poon@Sun.COM 				return (EINVAL);
43711754SKacheong.Poon@Sun.COM 			mutex_enter(&connp->conn_tcp->tcp_non_sq_lock);
43811754SKacheong.Poon@Sun.COM 			connp->conn_tcp->tcp_naglim = *(int *)optvalp ? 1 :
43911754SKacheong.Poon@Sun.COM 			    connp->conn_tcp->tcp_mss;
44011754SKacheong.Poon@Sun.COM 			mutex_exit(&connp->conn_tcp->tcp_non_sq_lock);
44111754SKacheong.Poon@Sun.COM 			return (0);
44211754SKacheong.Poon@Sun.COM 		default:
44311754SKacheong.Poon@Sun.COM 			break;
44411754SKacheong.Poon@Sun.COM 		}
44511754SKacheong.Poon@Sun.COM 	}
44611754SKacheong.Poon@Sun.COM 
44712504SAnders.Persson@Sun.COM 	error = squeue_synch_enter(connp, NULL);
44811754SKacheong.Poon@Sun.COM 	if (error == ENOMEM) {
44911754SKacheong.Poon@Sun.COM 		return (ENOMEM);
45011754SKacheong.Poon@Sun.COM 	}
45111754SKacheong.Poon@Sun.COM 
45211754SKacheong.Poon@Sun.COM 	error = proto_opt_check(level, option_name, optlen, NULL,
45311754SKacheong.Poon@Sun.COM 	    tcp_opt_obj.odb_opt_des_arr,
45411754SKacheong.Poon@Sun.COM 	    tcp_opt_obj.odb_opt_arr_cnt,
45511754SKacheong.Poon@Sun.COM 	    B_TRUE, B_FALSE, cr);
45611754SKacheong.Poon@Sun.COM 
45711754SKacheong.Poon@Sun.COM 	if (error != 0) {
45811754SKacheong.Poon@Sun.COM 		if (error < 0) {
45911754SKacheong.Poon@Sun.COM 			error = proto_tlitosyserr(-error);
46011754SKacheong.Poon@Sun.COM 		}
46112504SAnders.Persson@Sun.COM 		squeue_synch_exit(connp);
46211754SKacheong.Poon@Sun.COM 		return (error);
46311754SKacheong.Poon@Sun.COM 	}
46411754SKacheong.Poon@Sun.COM 
46511754SKacheong.Poon@Sun.COM 	error = tcp_opt_set(connp, SETFN_OPTCOM_NEGOTIATE, level, option_name,
46611754SKacheong.Poon@Sun.COM 	    optlen, (uchar_t *)optvalp, (uint_t *)&optlen, (uchar_t *)optvalp,
46711754SKacheong.Poon@Sun.COM 	    NULL, cr);
46812504SAnders.Persson@Sun.COM 	squeue_synch_exit(connp);
46911754SKacheong.Poon@Sun.COM 
47011754SKacheong.Poon@Sun.COM 	ASSERT(error >= 0);
47111754SKacheong.Poon@Sun.COM 
47211754SKacheong.Poon@Sun.COM 	return (error);
47311754SKacheong.Poon@Sun.COM }
47411754SKacheong.Poon@Sun.COM 
47511754SKacheong.Poon@Sun.COM /* ARGSUSED */
47611754SKacheong.Poon@Sun.COM static int
tcp_sendmsg(sock_lower_handle_t proto_handle,mblk_t * mp,struct nmsghdr * msg,cred_t * cr)47711754SKacheong.Poon@Sun.COM tcp_sendmsg(sock_lower_handle_t proto_handle, mblk_t *mp, struct nmsghdr *msg,
47811754SKacheong.Poon@Sun.COM     cred_t *cr)
47911754SKacheong.Poon@Sun.COM {
48011754SKacheong.Poon@Sun.COM 	tcp_t		*tcp;
48111754SKacheong.Poon@Sun.COM 	uint32_t	msize;
48211754SKacheong.Poon@Sun.COM 	conn_t *connp = (conn_t *)proto_handle;
48311754SKacheong.Poon@Sun.COM 	int32_t		tcpstate;
48411754SKacheong.Poon@Sun.COM 
48511754SKacheong.Poon@Sun.COM 	/* All Solaris components should pass a cred for this operation. */
48611754SKacheong.Poon@Sun.COM 	ASSERT(cr != NULL);
48711754SKacheong.Poon@Sun.COM 
48811754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_ref >= 2);
48911754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_upper_handle != NULL);
49011754SKacheong.Poon@Sun.COM 
49111754SKacheong.Poon@Sun.COM 	if (msg->msg_controllen != 0) {
49211754SKacheong.Poon@Sun.COM 		freemsg(mp);
49311754SKacheong.Poon@Sun.COM 		return (EOPNOTSUPP);
49411754SKacheong.Poon@Sun.COM 	}
49511754SKacheong.Poon@Sun.COM 
49611754SKacheong.Poon@Sun.COM 	switch (DB_TYPE(mp)) {
49711754SKacheong.Poon@Sun.COM 	case M_DATA:
49811754SKacheong.Poon@Sun.COM 		tcp = connp->conn_tcp;
49911754SKacheong.Poon@Sun.COM 		ASSERT(tcp != NULL);
50011754SKacheong.Poon@Sun.COM 
50111754SKacheong.Poon@Sun.COM 		tcpstate = tcp->tcp_state;
50211754SKacheong.Poon@Sun.COM 		if (tcpstate < TCPS_ESTABLISHED) {
50311754SKacheong.Poon@Sun.COM 			freemsg(mp);
50411754SKacheong.Poon@Sun.COM 			/*
50511754SKacheong.Poon@Sun.COM 			 * We return ENOTCONN if the endpoint is trying to
50611754SKacheong.Poon@Sun.COM 			 * connect or has never been connected, and EPIPE if it
50711754SKacheong.Poon@Sun.COM 			 * has been disconnected. The connection id helps us
50811754SKacheong.Poon@Sun.COM 			 * distinguish between the last two cases.
50911754SKacheong.Poon@Sun.COM 			 */
51011754SKacheong.Poon@Sun.COM 			return ((tcpstate == TCPS_SYN_SENT) ? ENOTCONN :
51111754SKacheong.Poon@Sun.COM 			    ((tcp->tcp_connid > 0) ? EPIPE : ENOTCONN));
51211754SKacheong.Poon@Sun.COM 		} else if (tcpstate > TCPS_CLOSE_WAIT) {
51311754SKacheong.Poon@Sun.COM 			freemsg(mp);
51411754SKacheong.Poon@Sun.COM 			return (EPIPE);
51511754SKacheong.Poon@Sun.COM 		}
51611754SKacheong.Poon@Sun.COM 
51711754SKacheong.Poon@Sun.COM 		msize = msgdsize(mp);
51811754SKacheong.Poon@Sun.COM 
51911754SKacheong.Poon@Sun.COM 		mutex_enter(&tcp->tcp_non_sq_lock);
52011754SKacheong.Poon@Sun.COM 		tcp->tcp_squeue_bytes += msize;
52111754SKacheong.Poon@Sun.COM 		/*
52211754SKacheong.Poon@Sun.COM 		 * Squeue Flow Control
52311754SKacheong.Poon@Sun.COM 		 */
52411754SKacheong.Poon@Sun.COM 		if (TCP_UNSENT_BYTES(tcp) > connp->conn_sndbuf) {
52511754SKacheong.Poon@Sun.COM 			tcp_setqfull(tcp);
52611754SKacheong.Poon@Sun.COM 		}
52711754SKacheong.Poon@Sun.COM 		mutex_exit(&tcp->tcp_non_sq_lock);
52811754SKacheong.Poon@Sun.COM 
52911754SKacheong.Poon@Sun.COM 		/*
53011754SKacheong.Poon@Sun.COM 		 * The application may pass in an address in the msghdr, but
53111754SKacheong.Poon@Sun.COM 		 * we ignore the address on connection-oriented sockets.
53211754SKacheong.Poon@Sun.COM 		 * Just like BSD this code does not generate an error for
53311754SKacheong.Poon@Sun.COM 		 * TCP (a CONNREQUIRED socket) when sending to an address
53411754SKacheong.Poon@Sun.COM 		 * passed in with sendto/sendmsg. Instead the data is
53511754SKacheong.Poon@Sun.COM 		 * delivered on the connection as if no address had been
53611754SKacheong.Poon@Sun.COM 		 * supplied.
53711754SKacheong.Poon@Sun.COM 		 */
53811754SKacheong.Poon@Sun.COM 		CONN_INC_REF(connp);
53911754SKacheong.Poon@Sun.COM 
54011754SKacheong.Poon@Sun.COM 		if (msg->msg_flags & MSG_OOB) {
54111754SKacheong.Poon@Sun.COM 			SQUEUE_ENTER_ONE(connp->conn_sqp, mp, tcp_output_urgent,
54211754SKacheong.Poon@Sun.COM 			    connp, NULL, tcp_squeue_flag, SQTAG_TCP_OUTPUT);
54311754SKacheong.Poon@Sun.COM 		} else {
54411754SKacheong.Poon@Sun.COM 			SQUEUE_ENTER_ONE(connp->conn_sqp, mp, tcp_output,
54511754SKacheong.Poon@Sun.COM 			    connp, NULL, tcp_squeue_flag, SQTAG_TCP_OUTPUT);
54611754SKacheong.Poon@Sun.COM 		}
54711754SKacheong.Poon@Sun.COM 
54811754SKacheong.Poon@Sun.COM 		return (0);
54911754SKacheong.Poon@Sun.COM 
55011754SKacheong.Poon@Sun.COM 	default:
55111754SKacheong.Poon@Sun.COM 		ASSERT(0);
55211754SKacheong.Poon@Sun.COM 	}
55311754SKacheong.Poon@Sun.COM 
55411754SKacheong.Poon@Sun.COM 	freemsg(mp);
55511754SKacheong.Poon@Sun.COM 	return (0);
55611754SKacheong.Poon@Sun.COM }
55711754SKacheong.Poon@Sun.COM 
55811754SKacheong.Poon@Sun.COM /* ARGSUSED */
55911754SKacheong.Poon@Sun.COM static int
tcp_shutdown(sock_lower_handle_t proto_handle,int how,cred_t * cr)56011754SKacheong.Poon@Sun.COM tcp_shutdown(sock_lower_handle_t proto_handle, int how, cred_t *cr)
56111754SKacheong.Poon@Sun.COM {
56211754SKacheong.Poon@Sun.COM 	conn_t  *connp = (conn_t *)proto_handle;
56311754SKacheong.Poon@Sun.COM 	tcp_t   *tcp = connp->conn_tcp;
56411754SKacheong.Poon@Sun.COM 
56511754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_upper_handle != NULL);
56611754SKacheong.Poon@Sun.COM 
56711754SKacheong.Poon@Sun.COM 	/* All Solaris components should pass a cred for this operation. */
56811754SKacheong.Poon@Sun.COM 	ASSERT(cr != NULL);
56911754SKacheong.Poon@Sun.COM 
57011754SKacheong.Poon@Sun.COM 	/*
57111754SKacheong.Poon@Sun.COM 	 * X/Open requires that we check the connected state.
57211754SKacheong.Poon@Sun.COM 	 */
57311754SKacheong.Poon@Sun.COM 	if (tcp->tcp_state < TCPS_SYN_SENT)
57411754SKacheong.Poon@Sun.COM 		return (ENOTCONN);
57511754SKacheong.Poon@Sun.COM 
57611754SKacheong.Poon@Sun.COM 	/* shutdown the send side */
57711754SKacheong.Poon@Sun.COM 	if (how != SHUT_RD) {
57811754SKacheong.Poon@Sun.COM 		mblk_t *bp;
57911754SKacheong.Poon@Sun.COM 
58011754SKacheong.Poon@Sun.COM 		bp = allocb_wait(0, BPRI_HI, STR_NOSIG, NULL);
58111754SKacheong.Poon@Sun.COM 		CONN_INC_REF(connp);
58211754SKacheong.Poon@Sun.COM 		SQUEUE_ENTER_ONE(connp->conn_sqp, bp, tcp_shutdown_output,
58311754SKacheong.Poon@Sun.COM 		    connp, NULL, SQ_NODRAIN, SQTAG_TCP_SHUTDOWN_OUTPUT);
58411754SKacheong.Poon@Sun.COM 
58511754SKacheong.Poon@Sun.COM 		(*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle,
58611754SKacheong.Poon@Sun.COM 		    SOCK_OPCTL_SHUT_SEND, 0);
58711754SKacheong.Poon@Sun.COM 	}
58811754SKacheong.Poon@Sun.COM 
58911754SKacheong.Poon@Sun.COM 	/* shutdown the recv side */
59011754SKacheong.Poon@Sun.COM 	if (how != SHUT_WR)
59111754SKacheong.Poon@Sun.COM 		(*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle,
59211754SKacheong.Poon@Sun.COM 		    SOCK_OPCTL_SHUT_RECV, 0);
59311754SKacheong.Poon@Sun.COM 
59411754SKacheong.Poon@Sun.COM 	return (0);
59511754SKacheong.Poon@Sun.COM }
59611754SKacheong.Poon@Sun.COM 
59711754SKacheong.Poon@Sun.COM static void
tcp_clr_flowctrl(sock_lower_handle_t proto_handle)59811754SKacheong.Poon@Sun.COM tcp_clr_flowctrl(sock_lower_handle_t proto_handle)
59911754SKacheong.Poon@Sun.COM {
60011754SKacheong.Poon@Sun.COM 	conn_t  *connp = (conn_t *)proto_handle;
60111754SKacheong.Poon@Sun.COM 	tcp_t	*tcp = connp->conn_tcp;
60211754SKacheong.Poon@Sun.COM 	mblk_t *mp;
60311754SKacheong.Poon@Sun.COM 	int error;
60411754SKacheong.Poon@Sun.COM 
60511754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_upper_handle != NULL);
60611754SKacheong.Poon@Sun.COM 
60711754SKacheong.Poon@Sun.COM 	/*
60811754SKacheong.Poon@Sun.COM 	 * If tcp->tcp_rsrv_mp == NULL, it means that tcp_clr_flowctrl()
60911754SKacheong.Poon@Sun.COM 	 * is currently running.
61011754SKacheong.Poon@Sun.COM 	 */
61111754SKacheong.Poon@Sun.COM 	mutex_enter(&tcp->tcp_rsrv_mp_lock);
61211754SKacheong.Poon@Sun.COM 	if ((mp = tcp->tcp_rsrv_mp) == NULL) {
61311754SKacheong.Poon@Sun.COM 		mutex_exit(&tcp->tcp_rsrv_mp_lock);
61411754SKacheong.Poon@Sun.COM 		return;
61511754SKacheong.Poon@Sun.COM 	}
61611754SKacheong.Poon@Sun.COM 	tcp->tcp_rsrv_mp = NULL;
61711754SKacheong.Poon@Sun.COM 	mutex_exit(&tcp->tcp_rsrv_mp_lock);
61811754SKacheong.Poon@Sun.COM 
61912504SAnders.Persson@Sun.COM 	error = squeue_synch_enter(connp, mp);
62011754SKacheong.Poon@Sun.COM 	ASSERT(error == 0);
62111754SKacheong.Poon@Sun.COM 
62211754SKacheong.Poon@Sun.COM 	mutex_enter(&tcp->tcp_rsrv_mp_lock);
62311754SKacheong.Poon@Sun.COM 	tcp->tcp_rsrv_mp = mp;
62411754SKacheong.Poon@Sun.COM 	mutex_exit(&tcp->tcp_rsrv_mp_lock);
62511754SKacheong.Poon@Sun.COM 
62611754SKacheong.Poon@Sun.COM 	if (tcp->tcp_fused) {
62711754SKacheong.Poon@Sun.COM 		tcp_fuse_backenable(tcp);
62811754SKacheong.Poon@Sun.COM 	} else {
62911754SKacheong.Poon@Sun.COM 		tcp->tcp_rwnd = connp->conn_rcvbuf;
63011754SKacheong.Poon@Sun.COM 		/*
63111754SKacheong.Poon@Sun.COM 		 * Send back a window update immediately if TCP is above
63211754SKacheong.Poon@Sun.COM 		 * ESTABLISHED state and the increase of the rcv window
63311754SKacheong.Poon@Sun.COM 		 * that the other side knows is at least 1 MSS after flow
63411754SKacheong.Poon@Sun.COM 		 * control is lifted.
63511754SKacheong.Poon@Sun.COM 		 */
63611754SKacheong.Poon@Sun.COM 		if (tcp->tcp_state >= TCPS_ESTABLISHED &&
63711754SKacheong.Poon@Sun.COM 		    tcp_rwnd_reopen(tcp) == TH_ACK_NEEDED) {
63811754SKacheong.Poon@Sun.COM 			tcp_xmit_ctl(NULL, tcp,
63911754SKacheong.Poon@Sun.COM 			    (tcp->tcp_swnd == 0) ? tcp->tcp_suna :
64011754SKacheong.Poon@Sun.COM 			    tcp->tcp_snxt, tcp->tcp_rnxt, TH_ACK);
64111754SKacheong.Poon@Sun.COM 		}
64211754SKacheong.Poon@Sun.COM 	}
64311754SKacheong.Poon@Sun.COM 
64412504SAnders.Persson@Sun.COM 	squeue_synch_exit(connp);
64511754SKacheong.Poon@Sun.COM }
64611754SKacheong.Poon@Sun.COM 
64711754SKacheong.Poon@Sun.COM /* ARGSUSED */
64811754SKacheong.Poon@Sun.COM static int
tcp_ioctl(sock_lower_handle_t proto_handle,int cmd,intptr_t arg,int mode,int32_t * rvalp,cred_t * cr)64911754SKacheong.Poon@Sun.COM tcp_ioctl(sock_lower_handle_t proto_handle, int cmd, intptr_t arg,
65011754SKacheong.Poon@Sun.COM     int mode, int32_t *rvalp, cred_t *cr)
65111754SKacheong.Poon@Sun.COM {
65211754SKacheong.Poon@Sun.COM 	conn_t  	*connp = (conn_t *)proto_handle;
65311754SKacheong.Poon@Sun.COM 	int		error;
65411754SKacheong.Poon@Sun.COM 
65511754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_upper_handle != NULL);
65611754SKacheong.Poon@Sun.COM 
65711754SKacheong.Poon@Sun.COM 	/* All Solaris components should pass a cred for this operation. */
65811754SKacheong.Poon@Sun.COM 	ASSERT(cr != NULL);
65911754SKacheong.Poon@Sun.COM 
66011754SKacheong.Poon@Sun.COM 	/*
66111754SKacheong.Poon@Sun.COM 	 * If we don't have a helper stream then create one.
66211754SKacheong.Poon@Sun.COM 	 * ip_create_helper_stream takes care of locking the conn_t,
66311754SKacheong.Poon@Sun.COM 	 * so this check for NULL is just a performance optimization.
66411754SKacheong.Poon@Sun.COM 	 */
66511754SKacheong.Poon@Sun.COM 	if (connp->conn_helper_info == NULL) {
66611754SKacheong.Poon@Sun.COM 		tcp_stack_t *tcps = connp->conn_tcp->tcp_tcps;
66711754SKacheong.Poon@Sun.COM 
66811754SKacheong.Poon@Sun.COM 		/*
66911754SKacheong.Poon@Sun.COM 		 * Create a helper stream for non-STREAMS socket.
67011754SKacheong.Poon@Sun.COM 		 */
67111754SKacheong.Poon@Sun.COM 		error = ip_create_helper_stream(connp, tcps->tcps_ldi_ident);
67211754SKacheong.Poon@Sun.COM 		if (error != 0) {
67311754SKacheong.Poon@Sun.COM 			ip0dbg(("tcp_ioctl: create of IP helper stream "
67411754SKacheong.Poon@Sun.COM 			    "failed %d\n", error));
67511754SKacheong.Poon@Sun.COM 			return (error);
67611754SKacheong.Poon@Sun.COM 		}
67711754SKacheong.Poon@Sun.COM 	}
67811754SKacheong.Poon@Sun.COM 
67911754SKacheong.Poon@Sun.COM 	switch (cmd) {
68011754SKacheong.Poon@Sun.COM 		case ND_SET:
68111754SKacheong.Poon@Sun.COM 		case ND_GET:
68211754SKacheong.Poon@Sun.COM 		case _SIOCSOCKFALLBACK:
68311754SKacheong.Poon@Sun.COM 		case TCP_IOC_ABORT_CONN:
68411754SKacheong.Poon@Sun.COM 		case TI_GETPEERNAME:
68511754SKacheong.Poon@Sun.COM 		case TI_GETMYNAME:
68611754SKacheong.Poon@Sun.COM 			ip1dbg(("tcp_ioctl: cmd 0x%x on non streams socket",
68711754SKacheong.Poon@Sun.COM 			    cmd));
68811754SKacheong.Poon@Sun.COM 			error = EINVAL;
68911754SKacheong.Poon@Sun.COM 			break;
69011754SKacheong.Poon@Sun.COM 		default:
69111754SKacheong.Poon@Sun.COM 			/*
69211754SKacheong.Poon@Sun.COM 			 * If the conn is not closing, pass on to IP using
69311754SKacheong.Poon@Sun.COM 			 * helper stream. Bump the ioctlref to prevent tcp_close
69411754SKacheong.Poon@Sun.COM 			 * from closing the rq/wq out from underneath the ioctl
69511754SKacheong.Poon@Sun.COM 			 * if it ends up queued or aborted/interrupted.
69611754SKacheong.Poon@Sun.COM 			 */
69711754SKacheong.Poon@Sun.COM 			mutex_enter(&connp->conn_lock);
69811754SKacheong.Poon@Sun.COM 			if (connp->conn_state_flags & (CONN_CLOSING)) {
69911754SKacheong.Poon@Sun.COM 				mutex_exit(&connp->conn_lock);
70011754SKacheong.Poon@Sun.COM 				error = EINVAL;
70111754SKacheong.Poon@Sun.COM 				break;
70211754SKacheong.Poon@Sun.COM 			}
70311754SKacheong.Poon@Sun.COM 			CONN_INC_IOCTLREF_LOCKED(connp);
70411754SKacheong.Poon@Sun.COM 			error = ldi_ioctl(connp->conn_helper_info->iphs_handle,
70511754SKacheong.Poon@Sun.COM 			    cmd, arg, mode, cr, rvalp);
70611754SKacheong.Poon@Sun.COM 			CONN_DEC_IOCTLREF(connp);
70711754SKacheong.Poon@Sun.COM 			break;
70811754SKacheong.Poon@Sun.COM 	}
70911754SKacheong.Poon@Sun.COM 	return (error);
71011754SKacheong.Poon@Sun.COM }
71111754SKacheong.Poon@Sun.COM 
71211754SKacheong.Poon@Sun.COM /* ARGSUSED */
71311754SKacheong.Poon@Sun.COM static int
tcp_close(sock_lower_handle_t proto_handle,int flags,cred_t * cr)71411754SKacheong.Poon@Sun.COM tcp_close(sock_lower_handle_t proto_handle, int flags, cred_t *cr)
71511754SKacheong.Poon@Sun.COM {
71611754SKacheong.Poon@Sun.COM 	conn_t *connp = (conn_t *)proto_handle;
71711754SKacheong.Poon@Sun.COM 
71811754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_upper_handle != NULL);
71911754SKacheong.Poon@Sun.COM 
72011754SKacheong.Poon@Sun.COM 	/* All Solaris components should pass a cred for this operation. */
72111754SKacheong.Poon@Sun.COM 	ASSERT(cr != NULL);
72211754SKacheong.Poon@Sun.COM 
72311754SKacheong.Poon@Sun.COM 	tcp_close_common(connp, flags);
72411754SKacheong.Poon@Sun.COM 
72511754SKacheong.Poon@Sun.COM 	ip_free_helper_stream(connp);
72611754SKacheong.Poon@Sun.COM 
72711754SKacheong.Poon@Sun.COM 	/*
72811754SKacheong.Poon@Sun.COM 	 * Drop IP's reference on the conn. This is the last reference
72911754SKacheong.Poon@Sun.COM 	 * on the connp if the state was less than established. If the
73011754SKacheong.Poon@Sun.COM 	 * connection has gone into timewait state, then we will have
73111754SKacheong.Poon@Sun.COM 	 * one ref for the TCP and one more ref (total of two) for the
73211754SKacheong.Poon@Sun.COM 	 * classifier connected hash list (a timewait connections stays
73311754SKacheong.Poon@Sun.COM 	 * in connected hash till closed).
73411754SKacheong.Poon@Sun.COM 	 *
73511754SKacheong.Poon@Sun.COM 	 * We can't assert the references because there might be other
73611754SKacheong.Poon@Sun.COM 	 * transient reference places because of some walkers or queued
73711754SKacheong.Poon@Sun.COM 	 * packets in squeue for the timewait state.
73811754SKacheong.Poon@Sun.COM 	 */
73911754SKacheong.Poon@Sun.COM 	CONN_DEC_REF(connp);
74012643SAnders.Persson@Sun.COM 
74112643SAnders.Persson@Sun.COM 	/*
74212643SAnders.Persson@Sun.COM 	 * EINPROGRESS tells sockfs to wait for a 'closed' upcall before
74312643SAnders.Persson@Sun.COM 	 * freeing the socket.
74412643SAnders.Persson@Sun.COM 	 */
74512643SAnders.Persson@Sun.COM 	return (EINPROGRESS);
74611754SKacheong.Poon@Sun.COM }
74711754SKacheong.Poon@Sun.COM 
74811754SKacheong.Poon@Sun.COM /* ARGSUSED */
74911754SKacheong.Poon@Sun.COM sock_lower_handle_t
tcp_create(int family,int type,int proto,sock_downcalls_t ** sock_downcalls,uint_t * smodep,int * errorp,int flags,cred_t * credp)75011754SKacheong.Poon@Sun.COM tcp_create(int family, int type, int proto, sock_downcalls_t **sock_downcalls,
75111754SKacheong.Poon@Sun.COM     uint_t *smodep, int *errorp, int flags, cred_t *credp)
75211754SKacheong.Poon@Sun.COM {
75311754SKacheong.Poon@Sun.COM 	conn_t		*connp;
75411754SKacheong.Poon@Sun.COM 	boolean_t	isv6 = family == AF_INET6;
75511754SKacheong.Poon@Sun.COM 	if (type != SOCK_STREAM || (family != AF_INET && family != AF_INET6) ||
75611754SKacheong.Poon@Sun.COM 	    (proto != 0 && proto != IPPROTO_TCP)) {
75711754SKacheong.Poon@Sun.COM 		*errorp = EPROTONOSUPPORT;
75811754SKacheong.Poon@Sun.COM 		return (NULL);
75911754SKacheong.Poon@Sun.COM 	}
76011754SKacheong.Poon@Sun.COM 
76111754SKacheong.Poon@Sun.COM 	connp = tcp_create_common(credp, isv6, B_TRUE, errorp);
76211754SKacheong.Poon@Sun.COM 	if (connp == NULL) {
76311754SKacheong.Poon@Sun.COM 		return (NULL);
76411754SKacheong.Poon@Sun.COM 	}
76511754SKacheong.Poon@Sun.COM 
76611754SKacheong.Poon@Sun.COM 	/*
76711754SKacheong.Poon@Sun.COM 	 * Put the ref for TCP. Ref for IP was already put
76811754SKacheong.Poon@Sun.COM 	 * by ipcl_conn_create. Also Make the conn_t globally
76911754SKacheong.Poon@Sun.COM 	 * visible to walkers
77011754SKacheong.Poon@Sun.COM 	 */
77111754SKacheong.Poon@Sun.COM 	mutex_enter(&connp->conn_lock);
77211754SKacheong.Poon@Sun.COM 	CONN_INC_REF_LOCKED(connp);
77311754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_ref == 2);
77411754SKacheong.Poon@Sun.COM 	connp->conn_state_flags &= ~CONN_INCIPIENT;
77511754SKacheong.Poon@Sun.COM 
77611754SKacheong.Poon@Sun.COM 	connp->conn_flags |= IPCL_NONSTR;
77711754SKacheong.Poon@Sun.COM 	mutex_exit(&connp->conn_lock);
77811754SKacheong.Poon@Sun.COM 
77911754SKacheong.Poon@Sun.COM 	ASSERT(errorp != NULL);
78011754SKacheong.Poon@Sun.COM 	*errorp = 0;
78111754SKacheong.Poon@Sun.COM 	*sock_downcalls = &sock_tcp_downcalls;
78211754SKacheong.Poon@Sun.COM 	*smodep = SM_CONNREQUIRED | SM_EXDATA | SM_ACCEPTSUPP |
78311754SKacheong.Poon@Sun.COM 	    SM_SENDFILESUPP;
78411754SKacheong.Poon@Sun.COM 
78511754SKacheong.Poon@Sun.COM 	return ((sock_lower_handle_t)connp);
78611754SKacheong.Poon@Sun.COM }
78711754SKacheong.Poon@Sun.COM 
78812643SAnders.Persson@Sun.COM /*
78912643SAnders.Persson@Sun.COM  * tcp_fallback
79012643SAnders.Persson@Sun.COM  *
79112643SAnders.Persson@Sun.COM  * A direct socket is falling back to using STREAMS. The queue
79212643SAnders.Persson@Sun.COM  * that is being passed down was created using tcp_open() with
79312643SAnders.Persson@Sun.COM  * the SO_FALLBACK flag set. As a result, the queue is not
79412643SAnders.Persson@Sun.COM  * associated with a conn, and the q_ptrs instead contain the
79512643SAnders.Persson@Sun.COM  * dev and minor area that should be used.
79612643SAnders.Persson@Sun.COM  *
79712643SAnders.Persson@Sun.COM  * The 'issocket' flag indicates whether the FireEngine
79812643SAnders.Persson@Sun.COM  * optimizations should be used. The common case would be that
79912643SAnders.Persson@Sun.COM  * optimizations are enabled, and they might be subsequently
80012643SAnders.Persson@Sun.COM  * disabled using the _SIOCSOCKFALLBACK ioctl.
80112643SAnders.Persson@Sun.COM  */
80212643SAnders.Persson@Sun.COM 
80312643SAnders.Persson@Sun.COM /*
80412643SAnders.Persson@Sun.COM  * An active connection is falling back to TPI. Gather all the information
80512643SAnders.Persson@Sun.COM  * required by the STREAM head and TPI sonode and send it up.
80612643SAnders.Persson@Sun.COM  */
80712643SAnders.Persson@Sun.COM static void
tcp_fallback_noneager(tcp_t * tcp,mblk_t * stropt_mp,queue_t * q,boolean_t issocket,so_proto_quiesced_cb_t quiesced_cb,sock_quiesce_arg_t * arg)80812643SAnders.Persson@Sun.COM tcp_fallback_noneager(tcp_t *tcp, mblk_t *stropt_mp, queue_t *q,
80912643SAnders.Persson@Sun.COM     boolean_t issocket, so_proto_quiesced_cb_t quiesced_cb,
81012643SAnders.Persson@Sun.COM     sock_quiesce_arg_t *arg)
81112643SAnders.Persson@Sun.COM {
81212643SAnders.Persson@Sun.COM 	conn_t			*connp = tcp->tcp_connp;
81312643SAnders.Persson@Sun.COM 	struct stroptions	*stropt;
81412643SAnders.Persson@Sun.COM 	struct T_capability_ack tca;
81512643SAnders.Persson@Sun.COM 	struct sockaddr_in6	laddr, faddr;
81612643SAnders.Persson@Sun.COM 	socklen_t 		laddrlen, faddrlen;
81712643SAnders.Persson@Sun.COM 	short			opts;
81812643SAnders.Persson@Sun.COM 	int			error;
81912643SAnders.Persson@Sun.COM 	mblk_t			*mp, *mpnext;
82012643SAnders.Persson@Sun.COM 
82112643SAnders.Persson@Sun.COM 	connp->conn_dev = (dev_t)RD(q)->q_ptr;
82212643SAnders.Persson@Sun.COM 	connp->conn_minor_arena = WR(q)->q_ptr;
82312643SAnders.Persson@Sun.COM 
82412643SAnders.Persson@Sun.COM 	RD(q)->q_ptr = WR(q)->q_ptr = connp;
82512643SAnders.Persson@Sun.COM 
82612643SAnders.Persson@Sun.COM 	connp->conn_rq = RD(q);
82712643SAnders.Persson@Sun.COM 	connp->conn_wq = WR(q);
82812643SAnders.Persson@Sun.COM 
82912643SAnders.Persson@Sun.COM 	WR(q)->q_qinfo = &tcp_sock_winit;
83012643SAnders.Persson@Sun.COM 
83112643SAnders.Persson@Sun.COM 	if (!issocket)
83212643SAnders.Persson@Sun.COM 		tcp_use_pure_tpi(tcp);
83312643SAnders.Persson@Sun.COM 
83412643SAnders.Persson@Sun.COM 	/*
83512643SAnders.Persson@Sun.COM 	 * free the helper stream
83612643SAnders.Persson@Sun.COM 	 */
83712643SAnders.Persson@Sun.COM 	ip_free_helper_stream(connp);
83812643SAnders.Persson@Sun.COM 
83912643SAnders.Persson@Sun.COM 	/*
84012643SAnders.Persson@Sun.COM 	 * Notify the STREAM head about options
84112643SAnders.Persson@Sun.COM 	 */
84212643SAnders.Persson@Sun.COM 	DB_TYPE(stropt_mp) = M_SETOPTS;
84312643SAnders.Persson@Sun.COM 	stropt = (struct stroptions *)stropt_mp->b_rptr;
84412643SAnders.Persson@Sun.COM 	stropt_mp->b_wptr += sizeof (struct stroptions);
84512643SAnders.Persson@Sun.COM 	stropt->so_flags = SO_HIWAT | SO_WROFF | SO_MAXBLK;
84612643SAnders.Persson@Sun.COM 
84712643SAnders.Persson@Sun.COM 	stropt->so_wroff = connp->conn_ht_iphc_len + (tcp->tcp_loopback ? 0 :
84812643SAnders.Persson@Sun.COM 	    tcp->tcp_tcps->tcps_wroff_xtra);
84912643SAnders.Persson@Sun.COM 	if (tcp->tcp_snd_sack_ok)
85012643SAnders.Persson@Sun.COM 		stropt->so_wroff += TCPOPT_MAX_SACK_LEN;
85112643SAnders.Persson@Sun.COM 	stropt->so_hiwat = connp->conn_rcvbuf;
85212643SAnders.Persson@Sun.COM 	stropt->so_maxblk = tcp_maxpsz_set(tcp, B_FALSE);
85312643SAnders.Persson@Sun.COM 
85412643SAnders.Persson@Sun.COM 	putnext(RD(q), stropt_mp);
85512643SAnders.Persson@Sun.COM 
85612643SAnders.Persson@Sun.COM 	/*
85712643SAnders.Persson@Sun.COM 	 * Collect the information needed to sync with the sonode
85812643SAnders.Persson@Sun.COM 	 */
85912643SAnders.Persson@Sun.COM 	tcp_do_capability_ack(tcp, &tca, TC1_INFO|TC1_ACCEPTOR_ID);
86012643SAnders.Persson@Sun.COM 
86112643SAnders.Persson@Sun.COM 	laddrlen = faddrlen = sizeof (sin6_t);
86212643SAnders.Persson@Sun.COM 	(void) tcp_getsockname((sock_lower_handle_t)connp,
86312643SAnders.Persson@Sun.COM 	    (struct sockaddr *)&laddr, &laddrlen, CRED());
86412643SAnders.Persson@Sun.COM 	error = tcp_getpeername((sock_lower_handle_t)connp,
86512643SAnders.Persson@Sun.COM 	    (struct sockaddr *)&faddr, &faddrlen, CRED());
86612643SAnders.Persson@Sun.COM 	if (error != 0)
86712643SAnders.Persson@Sun.COM 		faddrlen = 0;
86812643SAnders.Persson@Sun.COM 
86912643SAnders.Persson@Sun.COM 	opts = 0;
87012643SAnders.Persson@Sun.COM 	if (connp->conn_oobinline)
87112643SAnders.Persson@Sun.COM 		opts |= SO_OOBINLINE;
87212643SAnders.Persson@Sun.COM 	if (connp->conn_ixa->ixa_flags & IXAF_DONTROUTE)
87312643SAnders.Persson@Sun.COM 		opts |= SO_DONTROUTE;
87412643SAnders.Persson@Sun.COM 
87512643SAnders.Persson@Sun.COM 	/*
87612643SAnders.Persson@Sun.COM 	 * Notify the socket that the protocol is now quiescent,
87712643SAnders.Persson@Sun.COM 	 * and it's therefore safe move data from the socket
87812643SAnders.Persson@Sun.COM 	 * to the stream head.
87912643SAnders.Persson@Sun.COM 	 */
88012643SAnders.Persson@Sun.COM 	mp = (*quiesced_cb)(connp->conn_upper_handle, arg, &tca,
88112643SAnders.Persson@Sun.COM 	    (struct sockaddr *)&laddr, laddrlen,
88212643SAnders.Persson@Sun.COM 	    (struct sockaddr *)&faddr, faddrlen, opts);
88312643SAnders.Persson@Sun.COM 
88412643SAnders.Persson@Sun.COM 	while (mp != NULL) {
88512643SAnders.Persson@Sun.COM 		mpnext = mp->b_next;
88612643SAnders.Persson@Sun.COM 		tcp->tcp_rcv_list = mp->b_next;
88712643SAnders.Persson@Sun.COM 		mp->b_next = NULL;
88812643SAnders.Persson@Sun.COM 		putnext(q, mp);
88912643SAnders.Persson@Sun.COM 		mp = mpnext;
89012643SAnders.Persson@Sun.COM 	}
89112643SAnders.Persson@Sun.COM 	ASSERT(tcp->tcp_rcv_last_head == NULL);
89212643SAnders.Persson@Sun.COM 	ASSERT(tcp->tcp_rcv_last_tail == NULL);
89312643SAnders.Persson@Sun.COM 	ASSERT(tcp->tcp_rcv_cnt == 0);
89412643SAnders.Persson@Sun.COM 
89512643SAnders.Persson@Sun.COM 	/*
89612643SAnders.Persson@Sun.COM 	 * All eagers in q0 are marked as being non-STREAM, so they will
89712643SAnders.Persson@Sun.COM 	 * make su_newconn upcalls when the handshake completes, which
89812643SAnders.Persson@Sun.COM 	 * will fail (resulting in the conn being closed). So we just blow
89912643SAnders.Persson@Sun.COM 	 * off everything in q0 instead of waiting for the inevitable.
90012643SAnders.Persson@Sun.COM 	 */
90112643SAnders.Persson@Sun.COM 	if (tcp->tcp_conn_req_cnt_q0 != 0)
90212643SAnders.Persson@Sun.COM 		tcp_eager_cleanup(tcp, B_TRUE);
90312643SAnders.Persson@Sun.COM }
90412643SAnders.Persson@Sun.COM 
90512643SAnders.Persson@Sun.COM /*
90612643SAnders.Persson@Sun.COM  * An eager is falling back to TPI. All we have to do is send
90712643SAnders.Persson@Sun.COM  * up a T_CONN_IND.
90812643SAnders.Persson@Sun.COM  */
90912643SAnders.Persson@Sun.COM static void
tcp_fallback_eager(tcp_t * eager,boolean_t issocket,so_proto_quiesced_cb_t quiesced_cb,sock_quiesce_arg_t * arg)91012643SAnders.Persson@Sun.COM tcp_fallback_eager(tcp_t *eager, boolean_t issocket,
91112643SAnders.Persson@Sun.COM     so_proto_quiesced_cb_t quiesced_cb, sock_quiesce_arg_t *arg)
91212643SAnders.Persson@Sun.COM {
91312643SAnders.Persson@Sun.COM 	conn_t *connp = eager->tcp_connp;
91412643SAnders.Persson@Sun.COM 	tcp_t *listener = eager->tcp_listener;
91512643SAnders.Persson@Sun.COM 	mblk_t *mp;
91612643SAnders.Persson@Sun.COM 
91712643SAnders.Persson@Sun.COM 	ASSERT(listener != NULL);
91812643SAnders.Persson@Sun.COM 
91912643SAnders.Persson@Sun.COM 	/*
92012643SAnders.Persson@Sun.COM 	 * Notify the socket that the protocol is now quiescent,
92112643SAnders.Persson@Sun.COM 	 * and it's therefore safe move data from the socket
92212643SAnders.Persson@Sun.COM 	 * to tcp's rcv queue.
92312643SAnders.Persson@Sun.COM 	 */
92412643SAnders.Persson@Sun.COM 	mp = (*quiesced_cb)(connp->conn_upper_handle, arg, NULL, NULL, 0,
92512643SAnders.Persson@Sun.COM 	    NULL, 0, 0);
92612643SAnders.Persson@Sun.COM 
92712643SAnders.Persson@Sun.COM 	if (mp != NULL) {
92812643SAnders.Persson@Sun.COM 		ASSERT(eager->tcp_rcv_cnt == 0);
92912643SAnders.Persson@Sun.COM 
93012643SAnders.Persson@Sun.COM 		eager->tcp_rcv_list = mp;
93112643SAnders.Persson@Sun.COM 		eager->tcp_rcv_cnt = msgdsize(mp);
93212643SAnders.Persson@Sun.COM 		while (mp->b_next != NULL) {
93312643SAnders.Persson@Sun.COM 			mp = mp->b_next;
93412643SAnders.Persson@Sun.COM 			eager->tcp_rcv_cnt += msgdsize(mp);
93512643SAnders.Persson@Sun.COM 		}
93612643SAnders.Persson@Sun.COM 		eager->tcp_rcv_last_head = mp;
93712643SAnders.Persson@Sun.COM 		while (mp->b_cont)
93812643SAnders.Persson@Sun.COM 			mp = mp->b_cont;
93912643SAnders.Persson@Sun.COM 		eager->tcp_rcv_last_tail = mp;
94012643SAnders.Persson@Sun.COM 		if (eager->tcp_rcv_cnt > eager->tcp_rwnd)
94112643SAnders.Persson@Sun.COM 			eager->tcp_rwnd = 0;
94212643SAnders.Persson@Sun.COM 		else
94312643SAnders.Persson@Sun.COM 			eager->tcp_rwnd -= eager->tcp_rcv_cnt;
94412643SAnders.Persson@Sun.COM 	}
94512643SAnders.Persson@Sun.COM 
94612643SAnders.Persson@Sun.COM 	if (!issocket)
94712643SAnders.Persson@Sun.COM 		eager->tcp_issocket = B_FALSE;
94812643SAnders.Persson@Sun.COM 	/*
94912643SAnders.Persson@Sun.COM 	 * The stream for this eager does not yet exist, so mark it as
95012643SAnders.Persson@Sun.COM 	 * being detached.
95112643SAnders.Persson@Sun.COM 	 */
95212643SAnders.Persson@Sun.COM 	eager->tcp_detached = B_TRUE;
95312643SAnders.Persson@Sun.COM 	eager->tcp_hard_binding = B_TRUE;
95412643SAnders.Persson@Sun.COM 	connp->conn_rq = listener->tcp_connp->conn_rq;
95512643SAnders.Persson@Sun.COM 	connp->conn_wq = listener->tcp_connp->conn_wq;
95612643SAnders.Persson@Sun.COM 
95712643SAnders.Persson@Sun.COM 	/* Send up the connection indication */
95812643SAnders.Persson@Sun.COM 	mp = eager->tcp_conn.tcp_eager_conn_ind;
95912643SAnders.Persson@Sun.COM 	ASSERT(mp != NULL);
96012643SAnders.Persson@Sun.COM 	eager->tcp_conn.tcp_eager_conn_ind = NULL;
96112643SAnders.Persson@Sun.COM 
96212643SAnders.Persson@Sun.COM 	/*
96312643SAnders.Persson@Sun.COM 	 * TLI/XTI applications will get confused by
96412643SAnders.Persson@Sun.COM 	 * sending eager as an option since it violates
96512643SAnders.Persson@Sun.COM 	 * the option semantics. So remove the eager as
96612643SAnders.Persson@Sun.COM 	 * option since TLI/XTI app doesn't need it anyway.
96712643SAnders.Persson@Sun.COM 	 */
96812643SAnders.Persson@Sun.COM 	if (!issocket) {
96912643SAnders.Persson@Sun.COM 		struct T_conn_ind *conn_ind;
97012643SAnders.Persson@Sun.COM 
97112643SAnders.Persson@Sun.COM 		conn_ind = (struct T_conn_ind *)mp->b_rptr;
97212643SAnders.Persson@Sun.COM 		conn_ind->OPT_length = 0;
97312643SAnders.Persson@Sun.COM 		conn_ind->OPT_offset = 0;
97412643SAnders.Persson@Sun.COM 	}
97512643SAnders.Persson@Sun.COM 
97612643SAnders.Persson@Sun.COM 	/*
97712643SAnders.Persson@Sun.COM 	 * Sockfs guarantees that the listener will not be closed
97812643SAnders.Persson@Sun.COM 	 * during fallback. So we can safely use the listener's queue.
97912643SAnders.Persson@Sun.COM 	 */
98012643SAnders.Persson@Sun.COM 	putnext(listener->tcp_connp->conn_rq, mp);
98112643SAnders.Persson@Sun.COM }
98212643SAnders.Persson@Sun.COM 
98312643SAnders.Persson@Sun.COM 
98411754SKacheong.Poon@Sun.COM int
tcp_fallback(sock_lower_handle_t proto_handle,queue_t * q,boolean_t direct_sockfs,so_proto_quiesced_cb_t quiesced_cb,sock_quiesce_arg_t * arg)98511754SKacheong.Poon@Sun.COM tcp_fallback(sock_lower_handle_t proto_handle, queue_t *q,
98612643SAnders.Persson@Sun.COM     boolean_t direct_sockfs, so_proto_quiesced_cb_t quiesced_cb,
98712643SAnders.Persson@Sun.COM     sock_quiesce_arg_t *arg)
98811754SKacheong.Poon@Sun.COM {
98911754SKacheong.Poon@Sun.COM 	tcp_t			*tcp;
99011754SKacheong.Poon@Sun.COM 	conn_t 			*connp = (conn_t *)proto_handle;
99111754SKacheong.Poon@Sun.COM 	int			error;
99211754SKacheong.Poon@Sun.COM 	mblk_t			*stropt_mp;
99311754SKacheong.Poon@Sun.COM 	mblk_t			*ordrel_mp;
99411754SKacheong.Poon@Sun.COM 
99511754SKacheong.Poon@Sun.COM 	tcp = connp->conn_tcp;
99611754SKacheong.Poon@Sun.COM 
99711754SKacheong.Poon@Sun.COM 	stropt_mp = allocb_wait(sizeof (struct stroptions), BPRI_HI, STR_NOSIG,
99811754SKacheong.Poon@Sun.COM 	    NULL);
99911754SKacheong.Poon@Sun.COM 
100011754SKacheong.Poon@Sun.COM 	/* Pre-allocate the T_ordrel_ind mblk. */
100111754SKacheong.Poon@Sun.COM 	ASSERT(tcp->tcp_ordrel_mp == NULL);
100211754SKacheong.Poon@Sun.COM 	ordrel_mp = allocb_wait(sizeof (struct T_ordrel_ind), BPRI_HI,
100311754SKacheong.Poon@Sun.COM 	    STR_NOSIG, NULL);
100411754SKacheong.Poon@Sun.COM 	ordrel_mp->b_datap->db_type = M_PROTO;
100511754SKacheong.Poon@Sun.COM 	((struct T_ordrel_ind *)ordrel_mp->b_rptr)->PRIM_type = T_ORDREL_IND;
100611754SKacheong.Poon@Sun.COM 	ordrel_mp->b_wptr += sizeof (struct T_ordrel_ind);
100711754SKacheong.Poon@Sun.COM 
100811754SKacheong.Poon@Sun.COM 	/*
100911754SKacheong.Poon@Sun.COM 	 * Enter the squeue so that no new packets can come in
101011754SKacheong.Poon@Sun.COM 	 */
101112504SAnders.Persson@Sun.COM 	error = squeue_synch_enter(connp, NULL);
101211754SKacheong.Poon@Sun.COM 	if (error != 0) {
101311754SKacheong.Poon@Sun.COM 		/* failed to enter, free all the pre-allocated messages. */
101411754SKacheong.Poon@Sun.COM 		freeb(stropt_mp);
101511754SKacheong.Poon@Sun.COM 		freeb(ordrel_mp);
101611754SKacheong.Poon@Sun.COM 		return (ENOMEM);
101711754SKacheong.Poon@Sun.COM 	}
101811754SKacheong.Poon@Sun.COM 
101911754SKacheong.Poon@Sun.COM 	/*
102011754SKacheong.Poon@Sun.COM 	 * Both endpoints must be of the same type (either STREAMS or
102111754SKacheong.Poon@Sun.COM 	 * non-STREAMS) for fusion to be enabled. So if we are fused,
102211754SKacheong.Poon@Sun.COM 	 * we have to unfuse.
102311754SKacheong.Poon@Sun.COM 	 */
102411754SKacheong.Poon@Sun.COM 	if (tcp->tcp_fused)
102511754SKacheong.Poon@Sun.COM 		tcp_unfuse(tcp);
102611754SKacheong.Poon@Sun.COM 
102712643SAnders.Persson@Sun.COM 	if (tcp->tcp_listener != NULL) {
102812643SAnders.Persson@Sun.COM 		/* The eager will deal with opts when accept() is called */
102912643SAnders.Persson@Sun.COM 		freeb(stropt_mp);
103012643SAnders.Persson@Sun.COM 		tcp_fallback_eager(tcp, direct_sockfs, quiesced_cb, arg);
103112643SAnders.Persson@Sun.COM 	} else {
103212643SAnders.Persson@Sun.COM 		tcp_fallback_noneager(tcp, stropt_mp, q, direct_sockfs,
103312643SAnders.Persson@Sun.COM 		    quiesced_cb, arg);
103412643SAnders.Persson@Sun.COM 	}
103512643SAnders.Persson@Sun.COM 
103611754SKacheong.Poon@Sun.COM 	/*
103711754SKacheong.Poon@Sun.COM 	 * No longer a direct socket
103812643SAnders.Persson@Sun.COM 	 *
103912643SAnders.Persson@Sun.COM 	 * Note that we intentionally leave the upper_handle and upcalls
104012643SAnders.Persson@Sun.COM 	 * intact, since eagers may still be using them.
104111754SKacheong.Poon@Sun.COM 	 */
104211754SKacheong.Poon@Sun.COM 	connp->conn_flags &= ~IPCL_NONSTR;
104311754SKacheong.Poon@Sun.COM 	tcp->tcp_ordrel_mp = ordrel_mp;
104411754SKacheong.Poon@Sun.COM 
104511754SKacheong.Poon@Sun.COM 	/*
104611754SKacheong.Poon@Sun.COM 	 * There should be atleast two ref's (IP + TCP)
104711754SKacheong.Poon@Sun.COM 	 */
104811754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_ref >= 2);
104912504SAnders.Persson@Sun.COM 	squeue_synch_exit(connp);
105011754SKacheong.Poon@Sun.COM 
105111754SKacheong.Poon@Sun.COM 	return (0);
105211754SKacheong.Poon@Sun.COM }
105312643SAnders.Persson@Sun.COM 
105412643SAnders.Persson@Sun.COM /*
105512643SAnders.Persson@Sun.COM  * Notifies a non-STREAMS based listener about a new connection. This
105612643SAnders.Persson@Sun.COM  * function is executed on the *eager*'s squeue once the 3 way handshake
105712643SAnders.Persson@Sun.COM  * has completed. Note that the behavior differs from STREAMS, where the
1058*13062SAnders.Persson@Sun.COM  * T_CONN_IND is sent up by tcp_send_conn_ind() while on the *listener*'s
105912643SAnders.Persson@Sun.COM  * squeue.
106012643SAnders.Persson@Sun.COM  *
1061*13062SAnders.Persson@Sun.COM  * Returns B_TRUE if the notification succeeded and an upper handle was
1062*13062SAnders.Persson@Sun.COM  * obtained. `tcp' should be closed on failure.
106312643SAnders.Persson@Sun.COM  */
106412643SAnders.Persson@Sun.COM boolean_t
tcp_newconn_notify(tcp_t * tcp,ip_recv_attr_t * ira)106512643SAnders.Persson@Sun.COM tcp_newconn_notify(tcp_t *tcp, ip_recv_attr_t *ira)
106612643SAnders.Persson@Sun.COM {
106712643SAnders.Persson@Sun.COM 	tcp_t *listener = tcp->tcp_listener;
106812643SAnders.Persson@Sun.COM 	conn_t *lconnp = listener->tcp_connp;
106912643SAnders.Persson@Sun.COM 	conn_t *econnp = tcp->tcp_connp;
107012643SAnders.Persson@Sun.COM 	tcp_t *tail;
107112643SAnders.Persson@Sun.COM 	ipaddr_t *addr_cache;
107212643SAnders.Persson@Sun.COM 	sock_upper_handle_t upper;
107312643SAnders.Persson@Sun.COM 	struct sock_proto_props sopp;
107412643SAnders.Persson@Sun.COM 
107512643SAnders.Persson@Sun.COM 	mutex_enter(&listener->tcp_eager_lock);
107612643SAnders.Persson@Sun.COM 	/*
107712643SAnders.Persson@Sun.COM 	 * Take the eager out, if it is in the list of droppable eagers
107812643SAnders.Persson@Sun.COM 	 * as we are here because the 3W handshake is over.
107912643SAnders.Persson@Sun.COM 	 */
108012643SAnders.Persson@Sun.COM 	MAKE_UNDROPPABLE(tcp);
108112643SAnders.Persson@Sun.COM 	/*
108212643SAnders.Persson@Sun.COM 	 * The eager already has an extra ref put in tcp_input_data
108312643SAnders.Persson@Sun.COM 	 * so that it stays till accept comes back even though it
108412643SAnders.Persson@Sun.COM 	 * might get into TCPS_CLOSED as a result of a TH_RST etc.
108512643SAnders.Persson@Sun.COM 	 */
108612643SAnders.Persson@Sun.COM 	ASSERT(listener->tcp_conn_req_cnt_q0 > 0);
108712643SAnders.Persson@Sun.COM 	listener->tcp_conn_req_cnt_q0--;
108812643SAnders.Persson@Sun.COM 	listener->tcp_conn_req_cnt_q++;
108912643SAnders.Persson@Sun.COM 
109012643SAnders.Persson@Sun.COM 	/* Move from SYN_RCVD to ESTABLISHED list  */
109112643SAnders.Persson@Sun.COM 	tcp->tcp_eager_next_q0->tcp_eager_prev_q0 = tcp->tcp_eager_prev_q0;
109212643SAnders.Persson@Sun.COM 	tcp->tcp_eager_prev_q0->tcp_eager_next_q0 = tcp->tcp_eager_next_q0;
109312643SAnders.Persson@Sun.COM 	tcp->tcp_eager_prev_q0 = NULL;
109412643SAnders.Persson@Sun.COM 	tcp->tcp_eager_next_q0 = NULL;
109512643SAnders.Persson@Sun.COM 
109612643SAnders.Persson@Sun.COM 	/*
109712643SAnders.Persson@Sun.COM 	 * Insert at end of the queue because connections are accepted
109812643SAnders.Persson@Sun.COM 	 * in chronological order. Leaving the older connections at front
109912643SAnders.Persson@Sun.COM 	 * of the queue helps reducing search time.
110012643SAnders.Persson@Sun.COM 	 */
110112643SAnders.Persson@Sun.COM 	tail = listener->tcp_eager_last_q;
110212643SAnders.Persson@Sun.COM 	if (tail != NULL)
110312643SAnders.Persson@Sun.COM 		tail->tcp_eager_next_q = tcp;
110412643SAnders.Persson@Sun.COM 	else
110512643SAnders.Persson@Sun.COM 		listener->tcp_eager_next_q = tcp;
110612643SAnders.Persson@Sun.COM 	listener->tcp_eager_last_q = tcp;
110712643SAnders.Persson@Sun.COM 	tcp->tcp_eager_next_q = NULL;
110812643SAnders.Persson@Sun.COM 
110912643SAnders.Persson@Sun.COM 	/* we have timed out before */
111012643SAnders.Persson@Sun.COM 	if (tcp->tcp_syn_rcvd_timeout != 0) {
111112643SAnders.Persson@Sun.COM 		tcp->tcp_syn_rcvd_timeout = 0;
111212643SAnders.Persson@Sun.COM 		listener->tcp_syn_rcvd_timeout--;
111312643SAnders.Persson@Sun.COM 		if (listener->tcp_syn_defense &&
111412643SAnders.Persson@Sun.COM 		    listener->tcp_syn_rcvd_timeout <=
111512643SAnders.Persson@Sun.COM 		    (listener->tcp_tcps->tcps_conn_req_max_q0 >> 5) &&
111612643SAnders.Persson@Sun.COM 		    10*MINUTES < TICK_TO_MSEC(ddi_get_lbolt64() -
111712643SAnders.Persson@Sun.COM 		    listener->tcp_last_rcv_lbolt)) {
111812643SAnders.Persson@Sun.COM 			/*
111912643SAnders.Persson@Sun.COM 			 * Turn off the defense mode if we
112012643SAnders.Persson@Sun.COM 			 * believe the SYN attack is over.
112112643SAnders.Persson@Sun.COM 			 */
112212643SAnders.Persson@Sun.COM 			listener->tcp_syn_defense = B_FALSE;
112312643SAnders.Persson@Sun.COM 			if (listener->tcp_ip_addr_cache) {
112412643SAnders.Persson@Sun.COM 				kmem_free((void *)listener->tcp_ip_addr_cache,
112512643SAnders.Persson@Sun.COM 				    IP_ADDR_CACHE_SIZE * sizeof (ipaddr_t));
112612643SAnders.Persson@Sun.COM 				listener->tcp_ip_addr_cache = NULL;
112712643SAnders.Persson@Sun.COM 			}
112812643SAnders.Persson@Sun.COM 		}
112912643SAnders.Persson@Sun.COM 	}
113012643SAnders.Persson@Sun.COM 	addr_cache = (ipaddr_t *)(listener->tcp_ip_addr_cache);
113112643SAnders.Persson@Sun.COM 	if (addr_cache != NULL) {
113212643SAnders.Persson@Sun.COM 		/*
113312643SAnders.Persson@Sun.COM 		 * We have finished a 3-way handshake with this
113412643SAnders.Persson@Sun.COM 		 * remote host. This proves the IP addr is good.
113512643SAnders.Persson@Sun.COM 		 * Cache it!
113612643SAnders.Persson@Sun.COM 		 */
113712643SAnders.Persson@Sun.COM 		addr_cache[IP_ADDR_CACHE_HASH(tcp->tcp_connp->conn_faddr_v4)] =
113812643SAnders.Persson@Sun.COM 		    tcp->tcp_connp->conn_faddr_v4;
113912643SAnders.Persson@Sun.COM 	}
114012643SAnders.Persson@Sun.COM 	mutex_exit(&listener->tcp_eager_lock);
114112643SAnders.Persson@Sun.COM 
114212643SAnders.Persson@Sun.COM 	/*
114312643SAnders.Persson@Sun.COM 	 * Notify the ULP about the newconn. It is guaranteed that no
114412643SAnders.Persson@Sun.COM 	 * tcp_accept() call will be made for the eager if the
114512643SAnders.Persson@Sun.COM 	 * notification fails.
114612643SAnders.Persson@Sun.COM 	 */
114712643SAnders.Persson@Sun.COM 	if ((upper = (*lconnp->conn_upcalls->su_newconn)
114812643SAnders.Persson@Sun.COM 	    (lconnp->conn_upper_handle, (sock_lower_handle_t)econnp,
114912643SAnders.Persson@Sun.COM 	    &sock_tcp_downcalls, ira->ira_cred, ira->ira_cpid,
115012643SAnders.Persson@Sun.COM 	    &econnp->conn_upcalls)) == NULL) {
115112643SAnders.Persson@Sun.COM 		return (B_FALSE);
115212643SAnders.Persson@Sun.COM 	}
115312643SAnders.Persson@Sun.COM 	econnp->conn_upper_handle = upper;
115412643SAnders.Persson@Sun.COM 
115512643SAnders.Persson@Sun.COM 	tcp->tcp_detached = B_FALSE;
115612643SAnders.Persson@Sun.COM 	tcp->tcp_hard_binding = B_FALSE;
115712643SAnders.Persson@Sun.COM 	tcp->tcp_tconnind_started = B_TRUE;
115812643SAnders.Persson@Sun.COM 
115912643SAnders.Persson@Sun.COM 	if (econnp->conn_keepalive) {
116012643SAnders.Persson@Sun.COM 		tcp->tcp_ka_last_intrvl = 0;
116112643SAnders.Persson@Sun.COM 		tcp->tcp_ka_tid = TCP_TIMER(tcp, tcp_keepalive_timer,
116212643SAnders.Persson@Sun.COM 		    tcp->tcp_ka_interval);
116312643SAnders.Persson@Sun.COM 	}
116412643SAnders.Persson@Sun.COM 
116512643SAnders.Persson@Sun.COM 	/* Update the necessary parameters */
116612643SAnders.Persson@Sun.COM 	tcp_get_proto_props(tcp, &sopp);
116712643SAnders.Persson@Sun.COM 
116812643SAnders.Persson@Sun.COM 	(*econnp->conn_upcalls->su_set_proto_props)
116912643SAnders.Persson@Sun.COM 	    (econnp->conn_upper_handle, &sopp);
117012643SAnders.Persson@Sun.COM 
117112643SAnders.Persson@Sun.COM 	return (B_TRUE);
117212643SAnders.Persson@Sun.COM }
1173