xref: /onnv-gate/usr/src/uts/common/inet/tcp/tcp_bind.c (revision 12507:501806a754d2)
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 /*
23*12507SAlan.Maguire@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 #include <sys/types.h>
2711754SKacheong.Poon@Sun.COM #include <sys/stream.h>
2811754SKacheong.Poon@Sun.COM #include <sys/strsun.h>
2911754SKacheong.Poon@Sun.COM #include <sys/strsubr.h>
3011754SKacheong.Poon@Sun.COM #include <sys/stropts.h>
3111754SKacheong.Poon@Sun.COM #include <sys/strlog.h>
3211754SKacheong.Poon@Sun.COM #define	_SUN_TPI_VERSION 2
3311754SKacheong.Poon@Sun.COM #include <sys/tihdr.h>
3411754SKacheong.Poon@Sun.COM #include <sys/suntpi.h>
3511754SKacheong.Poon@Sun.COM #include <sys/xti_inet.h>
3611754SKacheong.Poon@Sun.COM #include <sys/policy.h>
3711754SKacheong.Poon@Sun.COM #include <sys/squeue_impl.h>
3811754SKacheong.Poon@Sun.COM #include <sys/squeue.h>
3911754SKacheong.Poon@Sun.COM #include <sys/tsol/tnet.h>
4011754SKacheong.Poon@Sun.COM 
4111754SKacheong.Poon@Sun.COM #include <rpc/pmap_prot.h>
4211754SKacheong.Poon@Sun.COM 
4311754SKacheong.Poon@Sun.COM #include <inet/common.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 #include <inet/proto_set.h>
4811754SKacheong.Poon@Sun.COM #include <inet/ipsec_impl.h>
4911754SKacheong.Poon@Sun.COM 
5011754SKacheong.Poon@Sun.COM /* Setable in /etc/system */
5111754SKacheong.Poon@Sun.COM /* If set to 0, pick ephemeral port sequentially; otherwise randomly. */
5211754SKacheong.Poon@Sun.COM static uint32_t tcp_random_anon_port = 1;
5311754SKacheong.Poon@Sun.COM 
5411754SKacheong.Poon@Sun.COM static int	tcp_bind_select_lport(tcp_t *, in_port_t *, boolean_t,
5511754SKacheong.Poon@Sun.COM 		    cred_t *cr);
5611754SKacheong.Poon@Sun.COM static in_port_t	tcp_get_next_priv_port(const tcp_t *);
5711754SKacheong.Poon@Sun.COM 
5811754SKacheong.Poon@Sun.COM /*
5911754SKacheong.Poon@Sun.COM  * Hash list insertion routine for tcp_t structures. Each hash bucket
6011754SKacheong.Poon@Sun.COM  * contains a list of tcp_t entries, and each entry is bound to a unique
6111754SKacheong.Poon@Sun.COM  * port. If there are multiple tcp_t's that are bound to the same port, then
6211754SKacheong.Poon@Sun.COM  * one of them will be linked into the hash bucket list, and the rest will
6311754SKacheong.Poon@Sun.COM  * hang off of that one entry. For each port, entries bound to a specific IP
6411754SKacheong.Poon@Sun.COM  * address will be inserted before those those bound to INADDR_ANY.
6511754SKacheong.Poon@Sun.COM  */
6611754SKacheong.Poon@Sun.COM void
tcp_bind_hash_insert(tf_t * tbf,tcp_t * tcp,int caller_holds_lock)6711754SKacheong.Poon@Sun.COM tcp_bind_hash_insert(tf_t *tbf, tcp_t *tcp, int caller_holds_lock)
6811754SKacheong.Poon@Sun.COM {
6911754SKacheong.Poon@Sun.COM 	tcp_t	**tcpp;
7011754SKacheong.Poon@Sun.COM 	tcp_t	*tcpnext;
7111754SKacheong.Poon@Sun.COM 	tcp_t	*tcphash;
7211754SKacheong.Poon@Sun.COM 	conn_t	*connp = tcp->tcp_connp;
7311754SKacheong.Poon@Sun.COM 	conn_t	*connext;
7411754SKacheong.Poon@Sun.COM 
7511754SKacheong.Poon@Sun.COM 	if (tcp->tcp_ptpbhn != NULL) {
7611754SKacheong.Poon@Sun.COM 		ASSERT(!caller_holds_lock);
7711754SKacheong.Poon@Sun.COM 		tcp_bind_hash_remove(tcp);
7811754SKacheong.Poon@Sun.COM 	}
7911754SKacheong.Poon@Sun.COM 	tcpp = &tbf->tf_tcp;
8011754SKacheong.Poon@Sun.COM 	if (!caller_holds_lock) {
8111754SKacheong.Poon@Sun.COM 		mutex_enter(&tbf->tf_lock);
8211754SKacheong.Poon@Sun.COM 	} else {
8311754SKacheong.Poon@Sun.COM 		ASSERT(MUTEX_HELD(&tbf->tf_lock));
8411754SKacheong.Poon@Sun.COM 	}
8511754SKacheong.Poon@Sun.COM 	tcphash = tcpp[0];
8611754SKacheong.Poon@Sun.COM 	tcpnext = NULL;
8711754SKacheong.Poon@Sun.COM 	if (tcphash != NULL) {
8811754SKacheong.Poon@Sun.COM 		/* Look for an entry using the same port */
8911754SKacheong.Poon@Sun.COM 		while ((tcphash = tcpp[0]) != NULL &&
9011754SKacheong.Poon@Sun.COM 		    connp->conn_lport != tcphash->tcp_connp->conn_lport)
9111754SKacheong.Poon@Sun.COM 			tcpp = &(tcphash->tcp_bind_hash);
9211754SKacheong.Poon@Sun.COM 
9311754SKacheong.Poon@Sun.COM 		/* The port was not found, just add to the end */
9411754SKacheong.Poon@Sun.COM 		if (tcphash == NULL)
9511754SKacheong.Poon@Sun.COM 			goto insert;
9611754SKacheong.Poon@Sun.COM 
9711754SKacheong.Poon@Sun.COM 		/*
9811754SKacheong.Poon@Sun.COM 		 * OK, there already exists an entry bound to the
9911754SKacheong.Poon@Sun.COM 		 * same port.
10011754SKacheong.Poon@Sun.COM 		 *
10111754SKacheong.Poon@Sun.COM 		 * If the new tcp bound to the INADDR_ANY address
10211754SKacheong.Poon@Sun.COM 		 * and the first one in the list is not bound to
10311754SKacheong.Poon@Sun.COM 		 * INADDR_ANY we skip all entries until we find the
10411754SKacheong.Poon@Sun.COM 		 * first one bound to INADDR_ANY.
10511754SKacheong.Poon@Sun.COM 		 * This makes sure that applications binding to a
10611754SKacheong.Poon@Sun.COM 		 * specific address get preference over those binding to
10711754SKacheong.Poon@Sun.COM 		 * INADDR_ANY.
10811754SKacheong.Poon@Sun.COM 		 */
10911754SKacheong.Poon@Sun.COM 		tcpnext = tcphash;
11011754SKacheong.Poon@Sun.COM 		connext = tcpnext->tcp_connp;
11111754SKacheong.Poon@Sun.COM 		tcphash = NULL;
11211754SKacheong.Poon@Sun.COM 		if (V6_OR_V4_INADDR_ANY(connp->conn_bound_addr_v6) &&
11311754SKacheong.Poon@Sun.COM 		    !V6_OR_V4_INADDR_ANY(connext->conn_bound_addr_v6)) {
11411754SKacheong.Poon@Sun.COM 			while ((tcpnext = tcpp[0]) != NULL) {
11511754SKacheong.Poon@Sun.COM 				connext = tcpnext->tcp_connp;
11611754SKacheong.Poon@Sun.COM 				if (!V6_OR_V4_INADDR_ANY(
11711754SKacheong.Poon@Sun.COM 				    connext->conn_bound_addr_v6))
11811754SKacheong.Poon@Sun.COM 					tcpp = &(tcpnext->tcp_bind_hash_port);
11911754SKacheong.Poon@Sun.COM 				else
12011754SKacheong.Poon@Sun.COM 					break;
12111754SKacheong.Poon@Sun.COM 			}
12211754SKacheong.Poon@Sun.COM 			if (tcpnext != NULL) {
12311754SKacheong.Poon@Sun.COM 				tcpnext->tcp_ptpbhn = &tcp->tcp_bind_hash_port;
12411754SKacheong.Poon@Sun.COM 				tcphash = tcpnext->tcp_bind_hash;
12511754SKacheong.Poon@Sun.COM 				if (tcphash != NULL) {
12611754SKacheong.Poon@Sun.COM 					tcphash->tcp_ptpbhn =
12711754SKacheong.Poon@Sun.COM 					    &(tcp->tcp_bind_hash);
12811754SKacheong.Poon@Sun.COM 					tcpnext->tcp_bind_hash = NULL;
12911754SKacheong.Poon@Sun.COM 				}
13011754SKacheong.Poon@Sun.COM 			}
13111754SKacheong.Poon@Sun.COM 		} else {
13211754SKacheong.Poon@Sun.COM 			tcpnext->tcp_ptpbhn = &tcp->tcp_bind_hash_port;
13311754SKacheong.Poon@Sun.COM 			tcphash = tcpnext->tcp_bind_hash;
13411754SKacheong.Poon@Sun.COM 			if (tcphash != NULL) {
13511754SKacheong.Poon@Sun.COM 				tcphash->tcp_ptpbhn =
13611754SKacheong.Poon@Sun.COM 				    &(tcp->tcp_bind_hash);
13711754SKacheong.Poon@Sun.COM 				tcpnext->tcp_bind_hash = NULL;
13811754SKacheong.Poon@Sun.COM 			}
13911754SKacheong.Poon@Sun.COM 		}
14011754SKacheong.Poon@Sun.COM 	}
14111754SKacheong.Poon@Sun.COM insert:
14211754SKacheong.Poon@Sun.COM 	tcp->tcp_bind_hash_port = tcpnext;
14311754SKacheong.Poon@Sun.COM 	tcp->tcp_bind_hash = tcphash;
14411754SKacheong.Poon@Sun.COM 	tcp->tcp_ptpbhn = tcpp;
14511754SKacheong.Poon@Sun.COM 	tcpp[0] = tcp;
14611754SKacheong.Poon@Sun.COM 	if (!caller_holds_lock)
14711754SKacheong.Poon@Sun.COM 		mutex_exit(&tbf->tf_lock);
14811754SKacheong.Poon@Sun.COM }
14911754SKacheong.Poon@Sun.COM 
15011754SKacheong.Poon@Sun.COM /*
15111754SKacheong.Poon@Sun.COM  * Hash list removal routine for tcp_t structures.
15211754SKacheong.Poon@Sun.COM  */
15311754SKacheong.Poon@Sun.COM void
tcp_bind_hash_remove(tcp_t * tcp)15411754SKacheong.Poon@Sun.COM tcp_bind_hash_remove(tcp_t *tcp)
15511754SKacheong.Poon@Sun.COM {
15611754SKacheong.Poon@Sun.COM 	tcp_t	*tcpnext;
15711754SKacheong.Poon@Sun.COM 	kmutex_t *lockp;
15811754SKacheong.Poon@Sun.COM 	tcp_stack_t	*tcps = tcp->tcp_tcps;
15911754SKacheong.Poon@Sun.COM 	conn_t		*connp = tcp->tcp_connp;
16011754SKacheong.Poon@Sun.COM 
16111754SKacheong.Poon@Sun.COM 	if (tcp->tcp_ptpbhn == NULL)
16211754SKacheong.Poon@Sun.COM 		return;
16311754SKacheong.Poon@Sun.COM 
16411754SKacheong.Poon@Sun.COM 	/*
16511754SKacheong.Poon@Sun.COM 	 * Extract the lock pointer in case there are concurrent
16611754SKacheong.Poon@Sun.COM 	 * hash_remove's for this instance.
16711754SKacheong.Poon@Sun.COM 	 */
16811754SKacheong.Poon@Sun.COM 	ASSERT(connp->conn_lport != 0);
16911754SKacheong.Poon@Sun.COM 	lockp = &tcps->tcps_bind_fanout[TCP_BIND_HASH(
17011754SKacheong.Poon@Sun.COM 	    connp->conn_lport)].tf_lock;
17111754SKacheong.Poon@Sun.COM 
17211754SKacheong.Poon@Sun.COM 	ASSERT(lockp != NULL);
17311754SKacheong.Poon@Sun.COM 	mutex_enter(lockp);
17411754SKacheong.Poon@Sun.COM 	if (tcp->tcp_ptpbhn) {
17511754SKacheong.Poon@Sun.COM 		tcpnext = tcp->tcp_bind_hash_port;
17611754SKacheong.Poon@Sun.COM 		if (tcpnext != NULL) {
17711754SKacheong.Poon@Sun.COM 			tcp->tcp_bind_hash_port = NULL;
17811754SKacheong.Poon@Sun.COM 			tcpnext->tcp_ptpbhn = tcp->tcp_ptpbhn;
17911754SKacheong.Poon@Sun.COM 			tcpnext->tcp_bind_hash = tcp->tcp_bind_hash;
18011754SKacheong.Poon@Sun.COM 			if (tcpnext->tcp_bind_hash != NULL) {
18111754SKacheong.Poon@Sun.COM 				tcpnext->tcp_bind_hash->tcp_ptpbhn =
18211754SKacheong.Poon@Sun.COM 				    &(tcpnext->tcp_bind_hash);
18311754SKacheong.Poon@Sun.COM 				tcp->tcp_bind_hash = NULL;
18411754SKacheong.Poon@Sun.COM 			}
18511754SKacheong.Poon@Sun.COM 		} else if ((tcpnext = tcp->tcp_bind_hash) != NULL) {
18611754SKacheong.Poon@Sun.COM 			tcpnext->tcp_ptpbhn = tcp->tcp_ptpbhn;
18711754SKacheong.Poon@Sun.COM 			tcp->tcp_bind_hash = NULL;
18811754SKacheong.Poon@Sun.COM 		}
18911754SKacheong.Poon@Sun.COM 		*tcp->tcp_ptpbhn = tcpnext;
19011754SKacheong.Poon@Sun.COM 		tcp->tcp_ptpbhn = NULL;
19111754SKacheong.Poon@Sun.COM 	}
19211754SKacheong.Poon@Sun.COM 	mutex_exit(lockp);
19311754SKacheong.Poon@Sun.COM }
19411754SKacheong.Poon@Sun.COM 
19511754SKacheong.Poon@Sun.COM /*
19611754SKacheong.Poon@Sun.COM  * Don't let port fall into the privileged range.
19711754SKacheong.Poon@Sun.COM  * Since the extra privileged ports can be arbitrary we also
19811754SKacheong.Poon@Sun.COM  * ensure that we exclude those from consideration.
19911754SKacheong.Poon@Sun.COM  * tcp_g_epriv_ports is not sorted thus we loop over it until
20011754SKacheong.Poon@Sun.COM  * there are no changes.
20111754SKacheong.Poon@Sun.COM  *
20211754SKacheong.Poon@Sun.COM  * Note: No locks are held when inspecting tcp_g_*epriv_ports
20311754SKacheong.Poon@Sun.COM  * but instead the code relies on:
20411754SKacheong.Poon@Sun.COM  * - the fact that the address of the array and its size never changes
20511754SKacheong.Poon@Sun.COM  * - the atomic assignment of the elements of the array
20611754SKacheong.Poon@Sun.COM  *
20711754SKacheong.Poon@Sun.COM  * Returns 0 if there are no more ports available.
20811754SKacheong.Poon@Sun.COM  *
20911754SKacheong.Poon@Sun.COM  * TS note: skip multilevel ports.
21011754SKacheong.Poon@Sun.COM  */
21111754SKacheong.Poon@Sun.COM in_port_t
tcp_update_next_port(in_port_t port,const tcp_t * tcp,boolean_t random)21211754SKacheong.Poon@Sun.COM tcp_update_next_port(in_port_t port, const tcp_t *tcp, boolean_t random)
21311754SKacheong.Poon@Sun.COM {
21411754SKacheong.Poon@Sun.COM 	int i;
21511754SKacheong.Poon@Sun.COM 	boolean_t restart = B_FALSE;
21611754SKacheong.Poon@Sun.COM 	tcp_stack_t *tcps = tcp->tcp_tcps;
21711754SKacheong.Poon@Sun.COM 
21811754SKacheong.Poon@Sun.COM 	if (random && tcp_random_anon_port != 0) {
21911754SKacheong.Poon@Sun.COM 		(void) random_get_pseudo_bytes((uint8_t *)&port,
22011754SKacheong.Poon@Sun.COM 		    sizeof (in_port_t));
22111754SKacheong.Poon@Sun.COM 		/*
22211754SKacheong.Poon@Sun.COM 		 * Unless changed by a sys admin, the smallest anon port
22311754SKacheong.Poon@Sun.COM 		 * is 32768 and the largest anon port is 65535.  It is
22411754SKacheong.Poon@Sun.COM 		 * very likely (50%) for the random port to be smaller
22511754SKacheong.Poon@Sun.COM 		 * than the smallest anon port.  When that happens,
22611754SKacheong.Poon@Sun.COM 		 * add port % (anon port range) to the smallest anon
22711754SKacheong.Poon@Sun.COM 		 * port to get the random port.  It should fall into the
22811754SKacheong.Poon@Sun.COM 		 * valid anon port range.
22911754SKacheong.Poon@Sun.COM 		 */
23011754SKacheong.Poon@Sun.COM 		if (port < tcps->tcps_smallest_anon_port) {
23111754SKacheong.Poon@Sun.COM 			port = tcps->tcps_smallest_anon_port +
23211754SKacheong.Poon@Sun.COM 			    port % (tcps->tcps_largest_anon_port -
23311754SKacheong.Poon@Sun.COM 			    tcps->tcps_smallest_anon_port);
23411754SKacheong.Poon@Sun.COM 		}
23511754SKacheong.Poon@Sun.COM 	}
23611754SKacheong.Poon@Sun.COM 
23711754SKacheong.Poon@Sun.COM retry:
23811754SKacheong.Poon@Sun.COM 	if (port < tcps->tcps_smallest_anon_port)
23911754SKacheong.Poon@Sun.COM 		port = (in_port_t)tcps->tcps_smallest_anon_port;
24011754SKacheong.Poon@Sun.COM 
24111754SKacheong.Poon@Sun.COM 	if (port > tcps->tcps_largest_anon_port) {
24211754SKacheong.Poon@Sun.COM 		if (restart)
24311754SKacheong.Poon@Sun.COM 			return (0);
24411754SKacheong.Poon@Sun.COM 		restart = B_TRUE;
24511754SKacheong.Poon@Sun.COM 		port = (in_port_t)tcps->tcps_smallest_anon_port;
24611754SKacheong.Poon@Sun.COM 	}
24711754SKacheong.Poon@Sun.COM 
24811754SKacheong.Poon@Sun.COM 	if (port < tcps->tcps_smallest_nonpriv_port)
24911754SKacheong.Poon@Sun.COM 		port = (in_port_t)tcps->tcps_smallest_nonpriv_port;
25011754SKacheong.Poon@Sun.COM 
25111754SKacheong.Poon@Sun.COM 	for (i = 0; i < tcps->tcps_g_num_epriv_ports; i++) {
25211754SKacheong.Poon@Sun.COM 		if (port == tcps->tcps_g_epriv_ports[i]) {
25311754SKacheong.Poon@Sun.COM 			port++;
25411754SKacheong.Poon@Sun.COM 			/*
25511754SKacheong.Poon@Sun.COM 			 * Make sure whether the port is in the
25611754SKacheong.Poon@Sun.COM 			 * valid range.
25711754SKacheong.Poon@Sun.COM 			 */
25811754SKacheong.Poon@Sun.COM 			goto retry;
25911754SKacheong.Poon@Sun.COM 		}
26011754SKacheong.Poon@Sun.COM 	}
26111754SKacheong.Poon@Sun.COM 	if (is_system_labeled() &&
26211754SKacheong.Poon@Sun.COM 	    (i = tsol_next_port(crgetzone(tcp->tcp_connp->conn_cred), port,
26311754SKacheong.Poon@Sun.COM 	    IPPROTO_TCP, B_TRUE)) != 0) {
26411754SKacheong.Poon@Sun.COM 		port = i;
26511754SKacheong.Poon@Sun.COM 		goto retry;
26611754SKacheong.Poon@Sun.COM 	}
26711754SKacheong.Poon@Sun.COM 	return (port);
26811754SKacheong.Poon@Sun.COM }
26911754SKacheong.Poon@Sun.COM 
27011754SKacheong.Poon@Sun.COM /*
27111754SKacheong.Poon@Sun.COM  * Return the next anonymous port in the privileged port range for
27211754SKacheong.Poon@Sun.COM  * bind checking.  It starts at IPPORT_RESERVED - 1 and goes
27311754SKacheong.Poon@Sun.COM  * downwards.  This is the same behavior as documented in the userland
27411754SKacheong.Poon@Sun.COM  * library call rresvport(3N).
27511754SKacheong.Poon@Sun.COM  *
27611754SKacheong.Poon@Sun.COM  * TS note: skip multilevel ports.
27711754SKacheong.Poon@Sun.COM  */
27811754SKacheong.Poon@Sun.COM static in_port_t
tcp_get_next_priv_port(const tcp_t * tcp)27911754SKacheong.Poon@Sun.COM tcp_get_next_priv_port(const tcp_t *tcp)
28011754SKacheong.Poon@Sun.COM {
28111754SKacheong.Poon@Sun.COM 	static in_port_t next_priv_port = IPPORT_RESERVED - 1;
28211754SKacheong.Poon@Sun.COM 	in_port_t nextport;
28311754SKacheong.Poon@Sun.COM 	boolean_t restart = B_FALSE;
28411754SKacheong.Poon@Sun.COM 	tcp_stack_t *tcps = tcp->tcp_tcps;
28511754SKacheong.Poon@Sun.COM retry:
28611754SKacheong.Poon@Sun.COM 	if (next_priv_port < tcps->tcps_min_anonpriv_port ||
28711754SKacheong.Poon@Sun.COM 	    next_priv_port >= IPPORT_RESERVED) {
28811754SKacheong.Poon@Sun.COM 		next_priv_port = IPPORT_RESERVED - 1;
28911754SKacheong.Poon@Sun.COM 		if (restart)
29011754SKacheong.Poon@Sun.COM 			return (0);
29111754SKacheong.Poon@Sun.COM 		restart = B_TRUE;
29211754SKacheong.Poon@Sun.COM 	}
29311754SKacheong.Poon@Sun.COM 	if (is_system_labeled() &&
29411754SKacheong.Poon@Sun.COM 	    (nextport = tsol_next_port(crgetzone(tcp->tcp_connp->conn_cred),
29511754SKacheong.Poon@Sun.COM 	    next_priv_port, IPPROTO_TCP, B_FALSE)) != 0) {
29611754SKacheong.Poon@Sun.COM 		next_priv_port = nextport;
29711754SKacheong.Poon@Sun.COM 		goto retry;
29811754SKacheong.Poon@Sun.COM 	}
29911754SKacheong.Poon@Sun.COM 	return (next_priv_port--);
30011754SKacheong.Poon@Sun.COM }
30111754SKacheong.Poon@Sun.COM 
30211754SKacheong.Poon@Sun.COM static int
tcp_bind_select_lport(tcp_t * tcp,in_port_t * requested_port_ptr,boolean_t bind_to_req_port_only,cred_t * cr)30311754SKacheong.Poon@Sun.COM tcp_bind_select_lport(tcp_t *tcp, in_port_t *requested_port_ptr,
30411754SKacheong.Poon@Sun.COM     boolean_t bind_to_req_port_only, cred_t *cr)
30511754SKacheong.Poon@Sun.COM {
30611754SKacheong.Poon@Sun.COM 	in_port_t	mlp_port;
30711754SKacheong.Poon@Sun.COM 	mlp_type_t 	addrtype, mlptype;
30811754SKacheong.Poon@Sun.COM 	boolean_t	user_specified;
30911754SKacheong.Poon@Sun.COM 	in_port_t	allocated_port;
31011754SKacheong.Poon@Sun.COM 	in_port_t	requested_port = *requested_port_ptr;
31111754SKacheong.Poon@Sun.COM 	conn_t		*connp = tcp->tcp_connp;
31211754SKacheong.Poon@Sun.COM 	zone_t		*zone;
31311754SKacheong.Poon@Sun.COM 	tcp_stack_t	*tcps = tcp->tcp_tcps;
31411754SKacheong.Poon@Sun.COM 	in6_addr_t	v6addr = connp->conn_laddr_v6;
31511754SKacheong.Poon@Sun.COM 
31611754SKacheong.Poon@Sun.COM 	/*
31711754SKacheong.Poon@Sun.COM 	 * XXX It's up to the caller to specify bind_to_req_port_only or not.
31811754SKacheong.Poon@Sun.COM 	 */
31911754SKacheong.Poon@Sun.COM 	ASSERT(cr != NULL);
32011754SKacheong.Poon@Sun.COM 
32111754SKacheong.Poon@Sun.COM 	/*
32211754SKacheong.Poon@Sun.COM 	 * Get a valid port (within the anonymous range and should not
32311754SKacheong.Poon@Sun.COM 	 * be a privileged one) to use if the user has not given a port.
32411754SKacheong.Poon@Sun.COM 	 * If multiple threads are here, they may all start with
32511754SKacheong.Poon@Sun.COM 	 * with the same initial port. But, it should be fine as long as
32611754SKacheong.Poon@Sun.COM 	 * tcp_bindi will ensure that no two threads will be assigned
32711754SKacheong.Poon@Sun.COM 	 * the same port.
32811754SKacheong.Poon@Sun.COM 	 *
32911754SKacheong.Poon@Sun.COM 	 * NOTE: XXX If a privileged process asks for an anonymous port, we
33011754SKacheong.Poon@Sun.COM 	 * still check for ports only in the range > tcp_smallest_non_priv_port,
33111754SKacheong.Poon@Sun.COM 	 * unless TCP_ANONPRIVBIND option is set.
33211754SKacheong.Poon@Sun.COM 	 */
33311754SKacheong.Poon@Sun.COM 	mlptype = mlptSingle;
33411754SKacheong.Poon@Sun.COM 	mlp_port = requested_port;
33511754SKacheong.Poon@Sun.COM 	if (requested_port == 0) {
33611754SKacheong.Poon@Sun.COM 		requested_port = connp->conn_anon_priv_bind ?
33711754SKacheong.Poon@Sun.COM 		    tcp_get_next_priv_port(tcp) :
33811754SKacheong.Poon@Sun.COM 		    tcp_update_next_port(tcps->tcps_next_port_to_try,
33911754SKacheong.Poon@Sun.COM 		    tcp, B_TRUE);
34011754SKacheong.Poon@Sun.COM 		if (requested_port == 0) {
34111754SKacheong.Poon@Sun.COM 			return (-TNOADDR);
34211754SKacheong.Poon@Sun.COM 		}
34311754SKacheong.Poon@Sun.COM 		user_specified = B_FALSE;
34411754SKacheong.Poon@Sun.COM 
34511754SKacheong.Poon@Sun.COM 		/*
34611754SKacheong.Poon@Sun.COM 		 * If the user went through one of the RPC interfaces to create
34711754SKacheong.Poon@Sun.COM 		 * this socket and RPC is MLP in this zone, then give him an
34811754SKacheong.Poon@Sun.COM 		 * anonymous MLP.
34911754SKacheong.Poon@Sun.COM 		 */
35011754SKacheong.Poon@Sun.COM 		if (connp->conn_anon_mlp && is_system_labeled()) {
35111754SKacheong.Poon@Sun.COM 			zone = crgetzone(cr);
35211754SKacheong.Poon@Sun.COM 			addrtype = tsol_mlp_addr_type(
35311754SKacheong.Poon@Sun.COM 			    connp->conn_allzones ? ALL_ZONES : zone->zone_id,
35411754SKacheong.Poon@Sun.COM 			    IPV6_VERSION, &v6addr,
35511754SKacheong.Poon@Sun.COM 			    tcps->tcps_netstack->netstack_ip);
35611754SKacheong.Poon@Sun.COM 			if (addrtype == mlptSingle) {
35711754SKacheong.Poon@Sun.COM 				return (-TNOADDR);
35811754SKacheong.Poon@Sun.COM 			}
35911754SKacheong.Poon@Sun.COM 			mlptype = tsol_mlp_port_type(zone, IPPROTO_TCP,
36011754SKacheong.Poon@Sun.COM 			    PMAPPORT, addrtype);
36111754SKacheong.Poon@Sun.COM 			mlp_port = PMAPPORT;
36211754SKacheong.Poon@Sun.COM 		}
36311754SKacheong.Poon@Sun.COM 	} else {
36411754SKacheong.Poon@Sun.COM 		int i;
36511754SKacheong.Poon@Sun.COM 		boolean_t priv = B_FALSE;
36611754SKacheong.Poon@Sun.COM 
36711754SKacheong.Poon@Sun.COM 		/*
36811754SKacheong.Poon@Sun.COM 		 * If the requested_port is in the well-known privileged range,
36911754SKacheong.Poon@Sun.COM 		 * verify that the stream was opened by a privileged user.
37011754SKacheong.Poon@Sun.COM 		 * Note: No locks are held when inspecting tcp_g_*epriv_ports
37111754SKacheong.Poon@Sun.COM 		 * but instead the code relies on:
37211754SKacheong.Poon@Sun.COM 		 * - the fact that the address of the array and its size never
37311754SKacheong.Poon@Sun.COM 		 *   changes
37411754SKacheong.Poon@Sun.COM 		 * - the atomic assignment of the elements of the array
37511754SKacheong.Poon@Sun.COM 		 */
37611754SKacheong.Poon@Sun.COM 		if (requested_port < tcps->tcps_smallest_nonpriv_port) {
37711754SKacheong.Poon@Sun.COM 			priv = B_TRUE;
37811754SKacheong.Poon@Sun.COM 		} else {
37911754SKacheong.Poon@Sun.COM 			for (i = 0; i < tcps->tcps_g_num_epriv_ports; i++) {
38011754SKacheong.Poon@Sun.COM 				if (requested_port ==
38111754SKacheong.Poon@Sun.COM 				    tcps->tcps_g_epriv_ports[i]) {
38211754SKacheong.Poon@Sun.COM 					priv = B_TRUE;
38311754SKacheong.Poon@Sun.COM 					break;
38411754SKacheong.Poon@Sun.COM 				}
38511754SKacheong.Poon@Sun.COM 			}
38611754SKacheong.Poon@Sun.COM 		}
38711754SKacheong.Poon@Sun.COM 		if (priv) {
38811754SKacheong.Poon@Sun.COM 			if (secpolicy_net_privaddr(cr, requested_port,
38911754SKacheong.Poon@Sun.COM 			    IPPROTO_TCP) != 0) {
39011754SKacheong.Poon@Sun.COM 				if (connp->conn_debug) {
39111754SKacheong.Poon@Sun.COM 					(void) strlog(TCP_MOD_ID, 0, 1,
39211754SKacheong.Poon@Sun.COM 					    SL_ERROR|SL_TRACE,
39311754SKacheong.Poon@Sun.COM 					    "tcp_bind: no priv for port %d",
39411754SKacheong.Poon@Sun.COM 					    requested_port);
39511754SKacheong.Poon@Sun.COM 				}
39611754SKacheong.Poon@Sun.COM 				return (-TACCES);
39711754SKacheong.Poon@Sun.COM 			}
39811754SKacheong.Poon@Sun.COM 		}
39911754SKacheong.Poon@Sun.COM 		user_specified = B_TRUE;
40011754SKacheong.Poon@Sun.COM 
40111754SKacheong.Poon@Sun.COM 		connp = tcp->tcp_connp;
40211754SKacheong.Poon@Sun.COM 		if (is_system_labeled()) {
40311754SKacheong.Poon@Sun.COM 			zone = crgetzone(cr);
40411754SKacheong.Poon@Sun.COM 			addrtype = tsol_mlp_addr_type(
40511754SKacheong.Poon@Sun.COM 			    connp->conn_allzones ? ALL_ZONES : zone->zone_id,
40611754SKacheong.Poon@Sun.COM 			    IPV6_VERSION, &v6addr,
40711754SKacheong.Poon@Sun.COM 			    tcps->tcps_netstack->netstack_ip);
40811754SKacheong.Poon@Sun.COM 			if (addrtype == mlptSingle) {
40911754SKacheong.Poon@Sun.COM 				return (-TNOADDR);
41011754SKacheong.Poon@Sun.COM 			}
41111754SKacheong.Poon@Sun.COM 			mlptype = tsol_mlp_port_type(zone, IPPROTO_TCP,
41211754SKacheong.Poon@Sun.COM 			    requested_port, addrtype);
41311754SKacheong.Poon@Sun.COM 		}
41411754SKacheong.Poon@Sun.COM 	}
41511754SKacheong.Poon@Sun.COM 
41611754SKacheong.Poon@Sun.COM 	if (mlptype != mlptSingle) {
41711754SKacheong.Poon@Sun.COM 		if (secpolicy_net_bindmlp(cr) != 0) {
41811754SKacheong.Poon@Sun.COM 			if (connp->conn_debug) {
41911754SKacheong.Poon@Sun.COM 				(void) strlog(TCP_MOD_ID, 0, 1,
42011754SKacheong.Poon@Sun.COM 				    SL_ERROR|SL_TRACE,
42111754SKacheong.Poon@Sun.COM 				    "tcp_bind: no priv for multilevel port %d",
42211754SKacheong.Poon@Sun.COM 				    requested_port);
42311754SKacheong.Poon@Sun.COM 			}
42411754SKacheong.Poon@Sun.COM 			return (-TACCES);
42511754SKacheong.Poon@Sun.COM 		}
42611754SKacheong.Poon@Sun.COM 
42711754SKacheong.Poon@Sun.COM 		/*
42811754SKacheong.Poon@Sun.COM 		 * If we're specifically binding a shared IP address and the
42911754SKacheong.Poon@Sun.COM 		 * port is MLP on shared addresses, then check to see if this
43011754SKacheong.Poon@Sun.COM 		 * zone actually owns the MLP.  Reject if not.
43111754SKacheong.Poon@Sun.COM 		 */
43211754SKacheong.Poon@Sun.COM 		if (mlptype == mlptShared && addrtype == mlptShared) {
43311754SKacheong.Poon@Sun.COM 			/*
43411754SKacheong.Poon@Sun.COM 			 * No need to handle exclusive-stack zones since
43511754SKacheong.Poon@Sun.COM 			 * ALL_ZONES only applies to the shared stack.
43611754SKacheong.Poon@Sun.COM 			 */
43711754SKacheong.Poon@Sun.COM 			zoneid_t mlpzone;
43811754SKacheong.Poon@Sun.COM 
43911754SKacheong.Poon@Sun.COM 			mlpzone = tsol_mlp_findzone(IPPROTO_TCP,
44011754SKacheong.Poon@Sun.COM 			    htons(mlp_port));
44111754SKacheong.Poon@Sun.COM 			if (connp->conn_zoneid != mlpzone) {
44211754SKacheong.Poon@Sun.COM 				if (connp->conn_debug) {
44311754SKacheong.Poon@Sun.COM 					(void) strlog(TCP_MOD_ID, 0, 1,
44411754SKacheong.Poon@Sun.COM 					    SL_ERROR|SL_TRACE,
44511754SKacheong.Poon@Sun.COM 					    "tcp_bind: attempt to bind port "
44611754SKacheong.Poon@Sun.COM 					    "%d on shared addr in zone %d "
44711754SKacheong.Poon@Sun.COM 					    "(should be %d)",
44811754SKacheong.Poon@Sun.COM 					    mlp_port, connp->conn_zoneid,
44911754SKacheong.Poon@Sun.COM 					    mlpzone);
45011754SKacheong.Poon@Sun.COM 				}
45111754SKacheong.Poon@Sun.COM 				return (-TACCES);
45211754SKacheong.Poon@Sun.COM 			}
45311754SKacheong.Poon@Sun.COM 		}
45411754SKacheong.Poon@Sun.COM 
45511754SKacheong.Poon@Sun.COM 		if (!user_specified) {
45611754SKacheong.Poon@Sun.COM 			int err;
45711754SKacheong.Poon@Sun.COM 			err = tsol_mlp_anon(zone, mlptype, connp->conn_proto,
45811754SKacheong.Poon@Sun.COM 			    requested_port, B_TRUE);
45911754SKacheong.Poon@Sun.COM 			if (err != 0) {
46011754SKacheong.Poon@Sun.COM 				if (connp->conn_debug) {
46111754SKacheong.Poon@Sun.COM 					(void) strlog(TCP_MOD_ID, 0, 1,
46211754SKacheong.Poon@Sun.COM 					    SL_ERROR|SL_TRACE,
46311754SKacheong.Poon@Sun.COM 					    "tcp_bind: cannot establish anon "
46411754SKacheong.Poon@Sun.COM 					    "MLP for port %d",
46511754SKacheong.Poon@Sun.COM 					    requested_port);
46611754SKacheong.Poon@Sun.COM 				}
46711754SKacheong.Poon@Sun.COM 				return (err);
46811754SKacheong.Poon@Sun.COM 			}
46911754SKacheong.Poon@Sun.COM 			connp->conn_anon_port = B_TRUE;
47011754SKacheong.Poon@Sun.COM 		}
47111754SKacheong.Poon@Sun.COM 		connp->conn_mlp_type = mlptype;
47211754SKacheong.Poon@Sun.COM 	}
47311754SKacheong.Poon@Sun.COM 
47411754SKacheong.Poon@Sun.COM 	allocated_port = tcp_bindi(tcp, requested_port, &v6addr,
47511754SKacheong.Poon@Sun.COM 	    connp->conn_reuseaddr, B_FALSE, bind_to_req_port_only,
47611754SKacheong.Poon@Sun.COM 	    user_specified);
47711754SKacheong.Poon@Sun.COM 
47811754SKacheong.Poon@Sun.COM 	if (allocated_port == 0) {
47911754SKacheong.Poon@Sun.COM 		connp->conn_mlp_type = mlptSingle;
48011754SKacheong.Poon@Sun.COM 		if (connp->conn_anon_port) {
48111754SKacheong.Poon@Sun.COM 			connp->conn_anon_port = B_FALSE;
48211754SKacheong.Poon@Sun.COM 			(void) tsol_mlp_anon(zone, mlptype, connp->conn_proto,
48311754SKacheong.Poon@Sun.COM 			    requested_port, B_FALSE);
48411754SKacheong.Poon@Sun.COM 		}
48511754SKacheong.Poon@Sun.COM 		if (bind_to_req_port_only) {
48611754SKacheong.Poon@Sun.COM 			if (connp->conn_debug) {
48711754SKacheong.Poon@Sun.COM 				(void) strlog(TCP_MOD_ID, 0, 1,
48811754SKacheong.Poon@Sun.COM 				    SL_ERROR|SL_TRACE,
48911754SKacheong.Poon@Sun.COM 				    "tcp_bind: requested addr busy");
49011754SKacheong.Poon@Sun.COM 			}
49111754SKacheong.Poon@Sun.COM 			return (-TADDRBUSY);
49211754SKacheong.Poon@Sun.COM 		} else {
49311754SKacheong.Poon@Sun.COM 			/* If we are out of ports, fail the bind. */
49411754SKacheong.Poon@Sun.COM 			if (connp->conn_debug) {
49511754SKacheong.Poon@Sun.COM 				(void) strlog(TCP_MOD_ID, 0, 1,
49611754SKacheong.Poon@Sun.COM 				    SL_ERROR|SL_TRACE,
49711754SKacheong.Poon@Sun.COM 				    "tcp_bind: out of ports?");
49811754SKacheong.Poon@Sun.COM 			}
49911754SKacheong.Poon@Sun.COM 			return (-TNOADDR);
50011754SKacheong.Poon@Sun.COM 		}
50111754SKacheong.Poon@Sun.COM 	}
50211754SKacheong.Poon@Sun.COM 
50311754SKacheong.Poon@Sun.COM 	/* Pass the allocated port back */
50411754SKacheong.Poon@Sun.COM 	*requested_port_ptr = allocated_port;
50511754SKacheong.Poon@Sun.COM 	return (0);
50611754SKacheong.Poon@Sun.COM }
50711754SKacheong.Poon@Sun.COM 
50811754SKacheong.Poon@Sun.COM /*
50911754SKacheong.Poon@Sun.COM  * Check the address and check/pick a local port number.
51011754SKacheong.Poon@Sun.COM  */
51111754SKacheong.Poon@Sun.COM int
tcp_bind_check(conn_t * connp,struct sockaddr * sa,socklen_t len,cred_t * cr,boolean_t bind_to_req_port_only)51211754SKacheong.Poon@Sun.COM tcp_bind_check(conn_t *connp, struct sockaddr *sa, socklen_t len, cred_t *cr,
51311754SKacheong.Poon@Sun.COM     boolean_t bind_to_req_port_only)
51411754SKacheong.Poon@Sun.COM {
51511754SKacheong.Poon@Sun.COM 	tcp_t	*tcp = connp->conn_tcp;
51611754SKacheong.Poon@Sun.COM 	sin_t	*sin;
51711754SKacheong.Poon@Sun.COM 	sin6_t  *sin6;
51811754SKacheong.Poon@Sun.COM 	in_port_t	requested_port;
51911754SKacheong.Poon@Sun.COM 	ipaddr_t	v4addr;
52011754SKacheong.Poon@Sun.COM 	in6_addr_t	v6addr;
52111754SKacheong.Poon@Sun.COM 	ip_laddr_t	laddr_type = IPVL_UNICAST_UP;	/* INADDR_ANY */
52211754SKacheong.Poon@Sun.COM 	zoneid_t	zoneid = IPCL_ZONEID(connp);
52311754SKacheong.Poon@Sun.COM 	ip_stack_t	*ipst = connp->conn_netstack->netstack_ip;
52411754SKacheong.Poon@Sun.COM 	uint_t		scopeid = 0;
52511754SKacheong.Poon@Sun.COM 	int		error = 0;
52611754SKacheong.Poon@Sun.COM 	ip_xmit_attr_t	*ixa = connp->conn_ixa;
52711754SKacheong.Poon@Sun.COM 
52811754SKacheong.Poon@Sun.COM 	ASSERT((uintptr_t)len <= (uintptr_t)INT_MAX);
52911754SKacheong.Poon@Sun.COM 
53011754SKacheong.Poon@Sun.COM 	if (tcp->tcp_state == TCPS_BOUND) {
53111754SKacheong.Poon@Sun.COM 		return (0);
53211754SKacheong.Poon@Sun.COM 	} else if (tcp->tcp_state > TCPS_BOUND) {
53311754SKacheong.Poon@Sun.COM 		if (connp->conn_debug) {
53411754SKacheong.Poon@Sun.COM 			(void) strlog(TCP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE,
53511754SKacheong.Poon@Sun.COM 			    "tcp_bind: bad state, %d", tcp->tcp_state);
53611754SKacheong.Poon@Sun.COM 		}
53711754SKacheong.Poon@Sun.COM 		return (-TOUTSTATE);
53811754SKacheong.Poon@Sun.COM 	}
53911754SKacheong.Poon@Sun.COM 
54011754SKacheong.Poon@Sun.COM 	ASSERT(sa != NULL && len != 0);
54111754SKacheong.Poon@Sun.COM 
54211754SKacheong.Poon@Sun.COM 	if (!OK_32PTR((char *)sa)) {
54311754SKacheong.Poon@Sun.COM 		if (connp->conn_debug) {
54411754SKacheong.Poon@Sun.COM 			(void) strlog(TCP_MOD_ID, 0, 1,
54511754SKacheong.Poon@Sun.COM 			    SL_ERROR|SL_TRACE,
54611754SKacheong.Poon@Sun.COM 			    "tcp_bind: bad address parameter, "
54711754SKacheong.Poon@Sun.COM 			    "address %p, len %d",
54811754SKacheong.Poon@Sun.COM 			    (void *)sa, len);
54911754SKacheong.Poon@Sun.COM 		}
55011754SKacheong.Poon@Sun.COM 		return (-TPROTO);
55111754SKacheong.Poon@Sun.COM 	}
55211754SKacheong.Poon@Sun.COM 
55311754SKacheong.Poon@Sun.COM 	error = proto_verify_ip_addr(connp->conn_family, sa, len);
55411754SKacheong.Poon@Sun.COM 	if (error != 0) {
55511754SKacheong.Poon@Sun.COM 		return (error);
55611754SKacheong.Poon@Sun.COM 	}
55711754SKacheong.Poon@Sun.COM 
55811754SKacheong.Poon@Sun.COM 	switch (len) {
55911754SKacheong.Poon@Sun.COM 	case sizeof (sin_t):	/* Complete IPv4 address */
56011754SKacheong.Poon@Sun.COM 		sin = (sin_t *)sa;
56111754SKacheong.Poon@Sun.COM 		requested_port = ntohs(sin->sin_port);
56211754SKacheong.Poon@Sun.COM 		v4addr = sin->sin_addr.s_addr;
56311754SKacheong.Poon@Sun.COM 		IN6_IPADDR_TO_V4MAPPED(v4addr, &v6addr);
56411754SKacheong.Poon@Sun.COM 		if (v4addr != INADDR_ANY) {
56511754SKacheong.Poon@Sun.COM 			laddr_type = ip_laddr_verify_v4(v4addr, zoneid, ipst,
56611754SKacheong.Poon@Sun.COM 			    B_FALSE);
56711754SKacheong.Poon@Sun.COM 		}
56811754SKacheong.Poon@Sun.COM 		break;
56911754SKacheong.Poon@Sun.COM 
57011754SKacheong.Poon@Sun.COM 	case sizeof (sin6_t): /* Complete IPv6 address */
57111754SKacheong.Poon@Sun.COM 		sin6 = (sin6_t *)sa;
57211754SKacheong.Poon@Sun.COM 		v6addr = sin6->sin6_addr;
57311754SKacheong.Poon@Sun.COM 		requested_port = ntohs(sin6->sin6_port);
57411754SKacheong.Poon@Sun.COM 		if (IN6_IS_ADDR_V4MAPPED(&v6addr)) {
57511754SKacheong.Poon@Sun.COM 			if (connp->conn_ipv6_v6only)
57611754SKacheong.Poon@Sun.COM 				return (EADDRNOTAVAIL);
57711754SKacheong.Poon@Sun.COM 
57811754SKacheong.Poon@Sun.COM 			IN6_V4MAPPED_TO_IPADDR(&v6addr, v4addr);
57911754SKacheong.Poon@Sun.COM 			if (v4addr != INADDR_ANY) {
58011754SKacheong.Poon@Sun.COM 				laddr_type = ip_laddr_verify_v4(v4addr,
58111754SKacheong.Poon@Sun.COM 				    zoneid, ipst, B_FALSE);
58211754SKacheong.Poon@Sun.COM 			}
58311754SKacheong.Poon@Sun.COM 		} else {
58411754SKacheong.Poon@Sun.COM 			if (!IN6_IS_ADDR_UNSPECIFIED(&v6addr)) {
58511754SKacheong.Poon@Sun.COM 				if (IN6_IS_ADDR_LINKSCOPE(&v6addr))
58611754SKacheong.Poon@Sun.COM 					scopeid = sin6->sin6_scope_id;
58711754SKacheong.Poon@Sun.COM 				laddr_type = ip_laddr_verify_v6(&v6addr,
58811754SKacheong.Poon@Sun.COM 				    zoneid, ipst, B_FALSE, scopeid);
58911754SKacheong.Poon@Sun.COM 			}
59011754SKacheong.Poon@Sun.COM 		}
59111754SKacheong.Poon@Sun.COM 		break;
59211754SKacheong.Poon@Sun.COM 
59311754SKacheong.Poon@Sun.COM 	default:
59411754SKacheong.Poon@Sun.COM 		if (connp->conn_debug) {
59511754SKacheong.Poon@Sun.COM 			(void) strlog(TCP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE,
59611754SKacheong.Poon@Sun.COM 			    "tcp_bind: bad address length, %d", len);
59711754SKacheong.Poon@Sun.COM 		}
59811754SKacheong.Poon@Sun.COM 		return (EAFNOSUPPORT);
59911754SKacheong.Poon@Sun.COM 		/* return (-TBADADDR); */
60011754SKacheong.Poon@Sun.COM 	}
60111754SKacheong.Poon@Sun.COM 
60211754SKacheong.Poon@Sun.COM 	/* Is the local address a valid unicast address? */
60311754SKacheong.Poon@Sun.COM 	if (laddr_type == IPVL_BAD)
60411754SKacheong.Poon@Sun.COM 		return (EADDRNOTAVAIL);
60511754SKacheong.Poon@Sun.COM 
60611754SKacheong.Poon@Sun.COM 	connp->conn_bound_addr_v6 = v6addr;
60711754SKacheong.Poon@Sun.COM 	if (scopeid != 0) {
60811754SKacheong.Poon@Sun.COM 		ixa->ixa_flags |= IXAF_SCOPEID_SET;
60911754SKacheong.Poon@Sun.COM 		ixa->ixa_scopeid = scopeid;
61011754SKacheong.Poon@Sun.COM 		connp->conn_incoming_ifindex = scopeid;
61111754SKacheong.Poon@Sun.COM 	} else {
61211754SKacheong.Poon@Sun.COM 		ixa->ixa_flags &= ~IXAF_SCOPEID_SET;
61311754SKacheong.Poon@Sun.COM 		connp->conn_incoming_ifindex = connp->conn_bound_if;
61411754SKacheong.Poon@Sun.COM 	}
61511754SKacheong.Poon@Sun.COM 
61611754SKacheong.Poon@Sun.COM 	connp->conn_laddr_v6 = v6addr;
61711754SKacheong.Poon@Sun.COM 	connp->conn_saddr_v6 = v6addr;
61811754SKacheong.Poon@Sun.COM 
61911754SKacheong.Poon@Sun.COM 	bind_to_req_port_only = requested_port != 0 && bind_to_req_port_only;
62011754SKacheong.Poon@Sun.COM 
62111754SKacheong.Poon@Sun.COM 	error = tcp_bind_select_lport(tcp, &requested_port,
62211754SKacheong.Poon@Sun.COM 	    bind_to_req_port_only, cr);
62311754SKacheong.Poon@Sun.COM 	if (error != 0) {
62411754SKacheong.Poon@Sun.COM 		connp->conn_laddr_v6 = ipv6_all_zeros;
62511754SKacheong.Poon@Sun.COM 		connp->conn_saddr_v6 = ipv6_all_zeros;
62611754SKacheong.Poon@Sun.COM 		connp->conn_bound_addr_v6 = ipv6_all_zeros;
62711754SKacheong.Poon@Sun.COM 	}
62811754SKacheong.Poon@Sun.COM 	return (error);
62911754SKacheong.Poon@Sun.COM }
63011754SKacheong.Poon@Sun.COM 
63111754SKacheong.Poon@Sun.COM /*
63211754SKacheong.Poon@Sun.COM  * If the "bind_to_req_port_only" parameter is set, if the requested port
63311754SKacheong.Poon@Sun.COM  * number is available, return it, If not return 0
63411754SKacheong.Poon@Sun.COM  *
63511754SKacheong.Poon@Sun.COM  * If "bind_to_req_port_only" parameter is not set and
63611754SKacheong.Poon@Sun.COM  * If the requested port number is available, return it.  If not, return
63711754SKacheong.Poon@Sun.COM  * the first anonymous port we happen across.  If no anonymous ports are
63811754SKacheong.Poon@Sun.COM  * available, return 0. addr is the requested local address, if any.
63911754SKacheong.Poon@Sun.COM  *
64011754SKacheong.Poon@Sun.COM  * In either case, when succeeding update the tcp_t to record the port number
64111754SKacheong.Poon@Sun.COM  * and insert it in the bind hash table.
64211754SKacheong.Poon@Sun.COM  *
64311754SKacheong.Poon@Sun.COM  * Note that TCP over IPv4 and IPv6 sockets can use the same port number
64411754SKacheong.Poon@Sun.COM  * without setting SO_REUSEADDR. This is needed so that they
64511754SKacheong.Poon@Sun.COM  * can be viewed as two independent transport protocols.
64611754SKacheong.Poon@Sun.COM  */
64711754SKacheong.Poon@Sun.COM in_port_t
tcp_bindi(tcp_t * tcp,in_port_t port,const in6_addr_t * laddr,int reuseaddr,boolean_t quick_connect,boolean_t bind_to_req_port_only,boolean_t user_specified)64811754SKacheong.Poon@Sun.COM tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
64911754SKacheong.Poon@Sun.COM     int reuseaddr, boolean_t quick_connect,
65011754SKacheong.Poon@Sun.COM     boolean_t bind_to_req_port_only, boolean_t user_specified)
65111754SKacheong.Poon@Sun.COM {
65211754SKacheong.Poon@Sun.COM 	/* number of times we have run around the loop */
65311754SKacheong.Poon@Sun.COM 	int count = 0;
65411754SKacheong.Poon@Sun.COM 	/* maximum number of times to run around the loop */
65511754SKacheong.Poon@Sun.COM 	int loopmax;
65611754SKacheong.Poon@Sun.COM 	conn_t *connp = tcp->tcp_connp;
65711754SKacheong.Poon@Sun.COM 	tcp_stack_t	*tcps = tcp->tcp_tcps;
65811754SKacheong.Poon@Sun.COM 
65911754SKacheong.Poon@Sun.COM 	/*
66011754SKacheong.Poon@Sun.COM 	 * Lookup for free addresses is done in a loop and "loopmax"
66111754SKacheong.Poon@Sun.COM 	 * influences how long we spin in the loop
66211754SKacheong.Poon@Sun.COM 	 */
66311754SKacheong.Poon@Sun.COM 	if (bind_to_req_port_only) {
66411754SKacheong.Poon@Sun.COM 		/*
66511754SKacheong.Poon@Sun.COM 		 * If the requested port is busy, don't bother to look
66611754SKacheong.Poon@Sun.COM 		 * for a new one. Setting loop maximum count to 1 has
66711754SKacheong.Poon@Sun.COM 		 * that effect.
66811754SKacheong.Poon@Sun.COM 		 */
66911754SKacheong.Poon@Sun.COM 		loopmax = 1;
67011754SKacheong.Poon@Sun.COM 	} else {
67111754SKacheong.Poon@Sun.COM 		/*
67211754SKacheong.Poon@Sun.COM 		 * If the requested port is busy, look for a free one
67311754SKacheong.Poon@Sun.COM 		 * in the anonymous port range.
67411754SKacheong.Poon@Sun.COM 		 * Set loopmax appropriately so that one does not look
67511754SKacheong.Poon@Sun.COM 		 * forever in the case all of the anonymous ports are in use.
67611754SKacheong.Poon@Sun.COM 		 */
67711754SKacheong.Poon@Sun.COM 		if (connp->conn_anon_priv_bind) {
67811754SKacheong.Poon@Sun.COM 			/*
67911754SKacheong.Poon@Sun.COM 			 * loopmax =
68011754SKacheong.Poon@Sun.COM 			 * 	(IPPORT_RESERVED-1) - tcp_min_anonpriv_port + 1
68111754SKacheong.Poon@Sun.COM 			 */
68211754SKacheong.Poon@Sun.COM 			loopmax = IPPORT_RESERVED -
68311754SKacheong.Poon@Sun.COM 			    tcps->tcps_min_anonpriv_port;
68411754SKacheong.Poon@Sun.COM 		} else {
68511754SKacheong.Poon@Sun.COM 			loopmax = (tcps->tcps_largest_anon_port -
68611754SKacheong.Poon@Sun.COM 			    tcps->tcps_smallest_anon_port + 1);
68711754SKacheong.Poon@Sun.COM 		}
68811754SKacheong.Poon@Sun.COM 	}
68911754SKacheong.Poon@Sun.COM 	do {
69011754SKacheong.Poon@Sun.COM 		uint16_t	lport;
69111754SKacheong.Poon@Sun.COM 		tf_t		*tbf;
69211754SKacheong.Poon@Sun.COM 		tcp_t		*ltcp;
69311754SKacheong.Poon@Sun.COM 		conn_t		*lconnp;
69411754SKacheong.Poon@Sun.COM 
69511754SKacheong.Poon@Sun.COM 		lport = htons(port);
69611754SKacheong.Poon@Sun.COM 
69711754SKacheong.Poon@Sun.COM 		/*
69811754SKacheong.Poon@Sun.COM 		 * Ensure that the tcp_t is not currently in the bind hash.
69911754SKacheong.Poon@Sun.COM 		 * Hold the lock on the hash bucket to ensure that
70011754SKacheong.Poon@Sun.COM 		 * the duplicate check plus the insertion is an atomic
70111754SKacheong.Poon@Sun.COM 		 * operation.
70211754SKacheong.Poon@Sun.COM 		 *
70311754SKacheong.Poon@Sun.COM 		 * This function does an inline lookup on the bind hash list
70411754SKacheong.Poon@Sun.COM 		 * Make sure that we access only members of tcp_t
70511754SKacheong.Poon@Sun.COM 		 * and that we don't look at tcp_tcp, since we are not
70611754SKacheong.Poon@Sun.COM 		 * doing a CONN_INC_REF.
70711754SKacheong.Poon@Sun.COM 		 */
70811754SKacheong.Poon@Sun.COM 		tcp_bind_hash_remove(tcp);
70911754SKacheong.Poon@Sun.COM 		tbf = &tcps->tcps_bind_fanout[TCP_BIND_HASH(lport)];
71011754SKacheong.Poon@Sun.COM 		mutex_enter(&tbf->tf_lock);
71111754SKacheong.Poon@Sun.COM 		for (ltcp = tbf->tf_tcp; ltcp != NULL;
71211754SKacheong.Poon@Sun.COM 		    ltcp = ltcp->tcp_bind_hash) {
71311754SKacheong.Poon@Sun.COM 			if (lport == ltcp->tcp_connp->conn_lport)
71411754SKacheong.Poon@Sun.COM 				break;
71511754SKacheong.Poon@Sun.COM 		}
71611754SKacheong.Poon@Sun.COM 
71711754SKacheong.Poon@Sun.COM 		for (; ltcp != NULL; ltcp = ltcp->tcp_bind_hash_port) {
71811754SKacheong.Poon@Sun.COM 			boolean_t not_socket;
71911754SKacheong.Poon@Sun.COM 			boolean_t exclbind;
72011754SKacheong.Poon@Sun.COM 
72111754SKacheong.Poon@Sun.COM 			lconnp = ltcp->tcp_connp;
72211754SKacheong.Poon@Sun.COM 
72311754SKacheong.Poon@Sun.COM 			/*
72411754SKacheong.Poon@Sun.COM 			 * On a labeled system, we must treat bindings to ports
72511754SKacheong.Poon@Sun.COM 			 * on shared IP addresses by sockets with MAC exemption
72611754SKacheong.Poon@Sun.COM 			 * privilege as being in all zones, as there's
72711754SKacheong.Poon@Sun.COM 			 * otherwise no way to identify the right receiver.
72811754SKacheong.Poon@Sun.COM 			 */
72911754SKacheong.Poon@Sun.COM 			if (!IPCL_BIND_ZONE_MATCH(lconnp, connp))
73011754SKacheong.Poon@Sun.COM 				continue;
73111754SKacheong.Poon@Sun.COM 
73211754SKacheong.Poon@Sun.COM 			/*
73311754SKacheong.Poon@Sun.COM 			 * If TCP_EXCLBIND is set for either the bound or
73411754SKacheong.Poon@Sun.COM 			 * binding endpoint, the semantics of bind
73511754SKacheong.Poon@Sun.COM 			 * is changed according to the following.
73611754SKacheong.Poon@Sun.COM 			 *
73711754SKacheong.Poon@Sun.COM 			 * spec = specified address (v4 or v6)
73811754SKacheong.Poon@Sun.COM 			 * unspec = unspecified address (v4 or v6)
73911754SKacheong.Poon@Sun.COM 			 * A = specified addresses are different for endpoints
74011754SKacheong.Poon@Sun.COM 			 *
74111754SKacheong.Poon@Sun.COM 			 * bound	bind to		allowed
74211754SKacheong.Poon@Sun.COM 			 * -------------------------------------
74311754SKacheong.Poon@Sun.COM 			 * unspec	unspec		no
74411754SKacheong.Poon@Sun.COM 			 * unspec	spec		no
74511754SKacheong.Poon@Sun.COM 			 * spec		unspec		no
74611754SKacheong.Poon@Sun.COM 			 * spec		spec		yes if A
74711754SKacheong.Poon@Sun.COM 			 *
74811754SKacheong.Poon@Sun.COM 			 * For labeled systems, SO_MAC_EXEMPT behaves the same
74911754SKacheong.Poon@Sun.COM 			 * as TCP_EXCLBIND, except that zoneid is ignored.
75011754SKacheong.Poon@Sun.COM 			 *
75111754SKacheong.Poon@Sun.COM 			 * Note:
75211754SKacheong.Poon@Sun.COM 			 *
75311754SKacheong.Poon@Sun.COM 			 * 1. Because of TLI semantics, an endpoint can go
75411754SKacheong.Poon@Sun.COM 			 * back from, say TCP_ESTABLISHED to TCPS_LISTEN or
75511754SKacheong.Poon@Sun.COM 			 * TCPS_BOUND, depending on whether it is originally
75611754SKacheong.Poon@Sun.COM 			 * a listener or not.  That is why we need to check
75711754SKacheong.Poon@Sun.COM 			 * for states greater than or equal to TCPS_BOUND
75811754SKacheong.Poon@Sun.COM 			 * here.
75911754SKacheong.Poon@Sun.COM 			 *
76011754SKacheong.Poon@Sun.COM 			 * 2. Ideally, we should only check for state equals
76111754SKacheong.Poon@Sun.COM 			 * to TCPS_LISTEN. And the following check should be
76211754SKacheong.Poon@Sun.COM 			 * added.
76311754SKacheong.Poon@Sun.COM 			 *
76411754SKacheong.Poon@Sun.COM 			 * if (ltcp->tcp_state == TCPS_LISTEN ||
76511754SKacheong.Poon@Sun.COM 			 *	!reuseaddr || !lconnp->conn_reuseaddr) {
76611754SKacheong.Poon@Sun.COM 			 *		...
76711754SKacheong.Poon@Sun.COM 			 * }
76811754SKacheong.Poon@Sun.COM 			 *
76911754SKacheong.Poon@Sun.COM 			 * The semantics will be changed to this.  If the
77011754SKacheong.Poon@Sun.COM 			 * endpoint on the list is in state not equal to
77111754SKacheong.Poon@Sun.COM 			 * TCPS_LISTEN and both endpoints have SO_REUSEADDR
77211754SKacheong.Poon@Sun.COM 			 * set, let the bind succeed.
77311754SKacheong.Poon@Sun.COM 			 *
77411754SKacheong.Poon@Sun.COM 			 * Because of (1), we cannot do that for TLI
77511754SKacheong.Poon@Sun.COM 			 * endpoints.  But we can do that for socket endpoints.
77611754SKacheong.Poon@Sun.COM 			 * If in future, we can change this going back
77711754SKacheong.Poon@Sun.COM 			 * semantics, we can use the above check for TLI also.
77811754SKacheong.Poon@Sun.COM 			 */
77911754SKacheong.Poon@Sun.COM 			not_socket = !(TCP_IS_SOCKET(ltcp) &&
78011754SKacheong.Poon@Sun.COM 			    TCP_IS_SOCKET(tcp));
78111754SKacheong.Poon@Sun.COM 			exclbind = lconnp->conn_exclbind ||
78211754SKacheong.Poon@Sun.COM 			    connp->conn_exclbind;
78311754SKacheong.Poon@Sun.COM 
78411754SKacheong.Poon@Sun.COM 			if ((lconnp->conn_mac_mode != CONN_MAC_DEFAULT) ||
78511754SKacheong.Poon@Sun.COM 			    (connp->conn_mac_mode != CONN_MAC_DEFAULT) ||
78611754SKacheong.Poon@Sun.COM 			    (exclbind && (not_socket ||
78711754SKacheong.Poon@Sun.COM 			    ltcp->tcp_state <= TCPS_ESTABLISHED))) {
78811754SKacheong.Poon@Sun.COM 				if (V6_OR_V4_INADDR_ANY(
78911754SKacheong.Poon@Sun.COM 				    lconnp->conn_bound_addr_v6) ||
79011754SKacheong.Poon@Sun.COM 				    V6_OR_V4_INADDR_ANY(*laddr) ||
79111754SKacheong.Poon@Sun.COM 				    IN6_ARE_ADDR_EQUAL(laddr,
79211754SKacheong.Poon@Sun.COM 				    &lconnp->conn_bound_addr_v6)) {
79311754SKacheong.Poon@Sun.COM 					break;
79411754SKacheong.Poon@Sun.COM 				}
79511754SKacheong.Poon@Sun.COM 				continue;
79611754SKacheong.Poon@Sun.COM 			}
79711754SKacheong.Poon@Sun.COM 
79811754SKacheong.Poon@Sun.COM 			/*
79911754SKacheong.Poon@Sun.COM 			 * Check ipversion to allow IPv4 and IPv6 sockets to
80011754SKacheong.Poon@Sun.COM 			 * have disjoint port number spaces, if *_EXCLBIND
80111754SKacheong.Poon@Sun.COM 			 * is not set and only if the application binds to a
80211754SKacheong.Poon@Sun.COM 			 * specific port. We use the same autoassigned port
80311754SKacheong.Poon@Sun.COM 			 * number space for IPv4 and IPv6 sockets.
80411754SKacheong.Poon@Sun.COM 			 */
80511754SKacheong.Poon@Sun.COM 			if (connp->conn_ipversion != lconnp->conn_ipversion &&
80611754SKacheong.Poon@Sun.COM 			    bind_to_req_port_only)
80711754SKacheong.Poon@Sun.COM 				continue;
80811754SKacheong.Poon@Sun.COM 
80911754SKacheong.Poon@Sun.COM 			/*
81011754SKacheong.Poon@Sun.COM 			 * Ideally, we should make sure that the source
81111754SKacheong.Poon@Sun.COM 			 * address, remote address, and remote port in the
81211754SKacheong.Poon@Sun.COM 			 * four tuple for this tcp-connection is unique.
81311754SKacheong.Poon@Sun.COM 			 * However, trying to find out the local source
81411754SKacheong.Poon@Sun.COM 			 * address would require too much code duplication
81511754SKacheong.Poon@Sun.COM 			 * with IP, since IP needs needs to have that code
81611754SKacheong.Poon@Sun.COM 			 * to support userland TCP implementations.
81711754SKacheong.Poon@Sun.COM 			 */
81811754SKacheong.Poon@Sun.COM 			if (quick_connect &&
81911754SKacheong.Poon@Sun.COM 			    (ltcp->tcp_state > TCPS_LISTEN) &&
82011754SKacheong.Poon@Sun.COM 			    ((connp->conn_fport != lconnp->conn_fport) ||
82111754SKacheong.Poon@Sun.COM 			    !IN6_ARE_ADDR_EQUAL(&connp->conn_faddr_v6,
82211754SKacheong.Poon@Sun.COM 			    &lconnp->conn_faddr_v6)))
82311754SKacheong.Poon@Sun.COM 				continue;
82411754SKacheong.Poon@Sun.COM 
82511754SKacheong.Poon@Sun.COM 			if (!reuseaddr) {
82611754SKacheong.Poon@Sun.COM 				/*
82711754SKacheong.Poon@Sun.COM 				 * No socket option SO_REUSEADDR.
82811754SKacheong.Poon@Sun.COM 				 * If existing port is bound to
82911754SKacheong.Poon@Sun.COM 				 * a non-wildcard IP address
83011754SKacheong.Poon@Sun.COM 				 * and the requesting stream is
83111754SKacheong.Poon@Sun.COM 				 * bound to a distinct
83211754SKacheong.Poon@Sun.COM 				 * different IP addresses
83311754SKacheong.Poon@Sun.COM 				 * (non-wildcard, also), keep
83411754SKacheong.Poon@Sun.COM 				 * going.
83511754SKacheong.Poon@Sun.COM 				 */
83611754SKacheong.Poon@Sun.COM 				if (!V6_OR_V4_INADDR_ANY(*laddr) &&
83711754SKacheong.Poon@Sun.COM 				    !V6_OR_V4_INADDR_ANY(
83811754SKacheong.Poon@Sun.COM 				    lconnp->conn_bound_addr_v6) &&
83911754SKacheong.Poon@Sun.COM 				    !IN6_ARE_ADDR_EQUAL(laddr,
84011754SKacheong.Poon@Sun.COM 				    &lconnp->conn_bound_addr_v6))
84111754SKacheong.Poon@Sun.COM 					continue;
84211754SKacheong.Poon@Sun.COM 				if (ltcp->tcp_state >= TCPS_BOUND) {
84311754SKacheong.Poon@Sun.COM 					/*
84411754SKacheong.Poon@Sun.COM 					 * This port is being used and
84511754SKacheong.Poon@Sun.COM 					 * its state is >= TCPS_BOUND,
84611754SKacheong.Poon@Sun.COM 					 * so we can't bind to it.
84711754SKacheong.Poon@Sun.COM 					 */
84811754SKacheong.Poon@Sun.COM 					break;
84911754SKacheong.Poon@Sun.COM 				}
85011754SKacheong.Poon@Sun.COM 			} else {
85111754SKacheong.Poon@Sun.COM 				/*
85211754SKacheong.Poon@Sun.COM 				 * socket option SO_REUSEADDR is set on the
85311754SKacheong.Poon@Sun.COM 				 * binding tcp_t.
85411754SKacheong.Poon@Sun.COM 				 *
85511754SKacheong.Poon@Sun.COM 				 * If two streams are bound to
85611754SKacheong.Poon@Sun.COM 				 * same IP address or both addr
85711754SKacheong.Poon@Sun.COM 				 * and bound source are wildcards
85811754SKacheong.Poon@Sun.COM 				 * (INADDR_ANY), we want to stop
85911754SKacheong.Poon@Sun.COM 				 * searching.
86011754SKacheong.Poon@Sun.COM 				 * We have found a match of IP source
86111754SKacheong.Poon@Sun.COM 				 * address and source port, which is
86211754SKacheong.Poon@Sun.COM 				 * refused regardless of the
86311754SKacheong.Poon@Sun.COM 				 * SO_REUSEADDR setting, so we break.
86411754SKacheong.Poon@Sun.COM 				 */
86511754SKacheong.Poon@Sun.COM 				if (IN6_ARE_ADDR_EQUAL(laddr,
86611754SKacheong.Poon@Sun.COM 				    &lconnp->conn_bound_addr_v6) &&
86711754SKacheong.Poon@Sun.COM 				    (ltcp->tcp_state == TCPS_LISTEN ||
86811754SKacheong.Poon@Sun.COM 				    ltcp->tcp_state == TCPS_BOUND))
86911754SKacheong.Poon@Sun.COM 					break;
87011754SKacheong.Poon@Sun.COM 			}
87111754SKacheong.Poon@Sun.COM 		}
87211754SKacheong.Poon@Sun.COM 		if (ltcp != NULL) {
87311754SKacheong.Poon@Sun.COM 			/* The port number is busy */
87411754SKacheong.Poon@Sun.COM 			mutex_exit(&tbf->tf_lock);
87511754SKacheong.Poon@Sun.COM 		} else {
87611754SKacheong.Poon@Sun.COM 			/*
87711754SKacheong.Poon@Sun.COM 			 * This port is ours. Insert in fanout and mark as
87811754SKacheong.Poon@Sun.COM 			 * bound to prevent others from getting the port
87911754SKacheong.Poon@Sun.COM 			 * number.
88011754SKacheong.Poon@Sun.COM 			 */
88111754SKacheong.Poon@Sun.COM 			tcp->tcp_state = TCPS_BOUND;
882*12507SAlan.Maguire@Sun.COM 			DTRACE_TCP6(state__change, void, NULL,
883*12507SAlan.Maguire@Sun.COM 			    ip_xmit_attr_t *, connp->conn_ixa,
884*12507SAlan.Maguire@Sun.COM 			    void, NULL, tcp_t *, tcp, void, NULL,
885*12507SAlan.Maguire@Sun.COM 			    int32_t, TCPS_IDLE);
886*12507SAlan.Maguire@Sun.COM 
88711754SKacheong.Poon@Sun.COM 			connp->conn_lport = htons(port);
88811754SKacheong.Poon@Sun.COM 
88911754SKacheong.Poon@Sun.COM 			ASSERT(&tcps->tcps_bind_fanout[TCP_BIND_HASH(
89011754SKacheong.Poon@Sun.COM 			    connp->conn_lport)] == tbf);
89111754SKacheong.Poon@Sun.COM 			tcp_bind_hash_insert(tbf, tcp, 1);
89211754SKacheong.Poon@Sun.COM 
89311754SKacheong.Poon@Sun.COM 			mutex_exit(&tbf->tf_lock);
89411754SKacheong.Poon@Sun.COM 
89511754SKacheong.Poon@Sun.COM 			/*
89611754SKacheong.Poon@Sun.COM 			 * We don't want tcp_next_port_to_try to "inherit"
89711754SKacheong.Poon@Sun.COM 			 * a port number supplied by the user in a bind.
89811754SKacheong.Poon@Sun.COM 			 */
89911754SKacheong.Poon@Sun.COM 			if (user_specified)
90011754SKacheong.Poon@Sun.COM 				return (port);
90111754SKacheong.Poon@Sun.COM 
90211754SKacheong.Poon@Sun.COM 			/*
90311754SKacheong.Poon@Sun.COM 			 * This is the only place where tcp_next_port_to_try
90411754SKacheong.Poon@Sun.COM 			 * is updated. After the update, it may or may not
90511754SKacheong.Poon@Sun.COM 			 * be in the valid range.
90611754SKacheong.Poon@Sun.COM 			 */
90711754SKacheong.Poon@Sun.COM 			if (!connp->conn_anon_priv_bind)
90811754SKacheong.Poon@Sun.COM 				tcps->tcps_next_port_to_try = port + 1;
90911754SKacheong.Poon@Sun.COM 			return (port);
91011754SKacheong.Poon@Sun.COM 		}
91111754SKacheong.Poon@Sun.COM 
91211754SKacheong.Poon@Sun.COM 		if (connp->conn_anon_priv_bind) {
91311754SKacheong.Poon@Sun.COM 			port = tcp_get_next_priv_port(tcp);
91411754SKacheong.Poon@Sun.COM 		} else {
91511754SKacheong.Poon@Sun.COM 			if (count == 0 && user_specified) {
91611754SKacheong.Poon@Sun.COM 				/*
91711754SKacheong.Poon@Sun.COM 				 * We may have to return an anonymous port. So
91811754SKacheong.Poon@Sun.COM 				 * get one to start with.
91911754SKacheong.Poon@Sun.COM 				 */
92011754SKacheong.Poon@Sun.COM 				port =
92111754SKacheong.Poon@Sun.COM 				    tcp_update_next_port(
92211754SKacheong.Poon@Sun.COM 				    tcps->tcps_next_port_to_try,
92311754SKacheong.Poon@Sun.COM 				    tcp, B_TRUE);
92411754SKacheong.Poon@Sun.COM 				user_specified = B_FALSE;
92511754SKacheong.Poon@Sun.COM 			} else {
92611754SKacheong.Poon@Sun.COM 				port = tcp_update_next_port(port + 1, tcp,
92711754SKacheong.Poon@Sun.COM 				    B_FALSE);
92811754SKacheong.Poon@Sun.COM 			}
92911754SKacheong.Poon@Sun.COM 		}
93011754SKacheong.Poon@Sun.COM 		if (port == 0)
93111754SKacheong.Poon@Sun.COM 			break;
93211754SKacheong.Poon@Sun.COM 
93311754SKacheong.Poon@Sun.COM 		/*
93411754SKacheong.Poon@Sun.COM 		 * Don't let this loop run forever in the case where
93511754SKacheong.Poon@Sun.COM 		 * all of the anonymous ports are in use.
93611754SKacheong.Poon@Sun.COM 		 */
93711754SKacheong.Poon@Sun.COM 	} while (++count < loopmax);
93811754SKacheong.Poon@Sun.COM 	return (0);
93911754SKacheong.Poon@Sun.COM }
940