1721fffe3SKacheong Poon /*
2721fffe3SKacheong Poon * CDDL HEADER START
3721fffe3SKacheong Poon *
4721fffe3SKacheong Poon * The contents of this file are subject to the terms of the
5721fffe3SKacheong Poon * Common Development and Distribution License (the "License").
6721fffe3SKacheong Poon * You may not use this file except in compliance with the License.
7721fffe3SKacheong Poon *
8721fffe3SKacheong Poon * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9721fffe3SKacheong Poon * or http://www.opensolaris.org/os/licensing.
10721fffe3SKacheong Poon * See the License for the specific language governing permissions
11721fffe3SKacheong Poon * and limitations under the License.
12721fffe3SKacheong Poon *
13721fffe3SKacheong Poon * When distributing Covered Code, include this CDDL HEADER in each
14721fffe3SKacheong Poon * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15721fffe3SKacheong Poon * If applicable, add the following below this CDDL HEADER, with the
16721fffe3SKacheong Poon * fields enclosed by brackets "[]" replaced with your own identifying
17721fffe3SKacheong Poon * information: Portions Copyright [yyyy] [name of copyright owner]
18721fffe3SKacheong Poon *
19721fffe3SKacheong Poon * CDDL HEADER END
20721fffe3SKacheong Poon */
21721fffe3SKacheong Poon
22721fffe3SKacheong Poon /*
239cd928feSAlan Maguire * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24*7256a34eSDan McDonald * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25721fffe3SKacheong Poon */
26721fffe3SKacheong Poon
27721fffe3SKacheong Poon #include <sys/types.h>
28721fffe3SKacheong Poon #include <sys/stream.h>
29721fffe3SKacheong Poon #include <sys/strsun.h>
30721fffe3SKacheong Poon #include <sys/strsubr.h>
31721fffe3SKacheong Poon #include <sys/stropts.h>
32721fffe3SKacheong Poon #include <sys/strlog.h>
33721fffe3SKacheong Poon #define _SUN_TPI_VERSION 2
34721fffe3SKacheong Poon #include <sys/tihdr.h>
35721fffe3SKacheong Poon #include <sys/suntpi.h>
36721fffe3SKacheong Poon #include <sys/xti_inet.h>
37721fffe3SKacheong Poon #include <sys/policy.h>
38721fffe3SKacheong Poon #include <sys/squeue_impl.h>
39721fffe3SKacheong Poon #include <sys/squeue.h>
40721fffe3SKacheong Poon #include <sys/tsol/tnet.h>
41721fffe3SKacheong Poon
42721fffe3SKacheong Poon #include <rpc/pmap_prot.h>
43721fffe3SKacheong Poon
44721fffe3SKacheong Poon #include <inet/common.h>
45721fffe3SKacheong Poon #include <inet/ip.h>
46721fffe3SKacheong Poon #include <inet/tcp.h>
47721fffe3SKacheong Poon #include <inet/tcp_impl.h>
48721fffe3SKacheong Poon #include <inet/proto_set.h>
49721fffe3SKacheong Poon #include <inet/ipsec_impl.h>
50721fffe3SKacheong Poon
51721fffe3SKacheong Poon /* Setable in /etc/system */
52721fffe3SKacheong Poon /* If set to 0, pick ephemeral port sequentially; otherwise randomly. */
53721fffe3SKacheong Poon static uint32_t tcp_random_anon_port = 1;
54721fffe3SKacheong Poon
55721fffe3SKacheong Poon static int tcp_bind_select_lport(tcp_t *, in_port_t *, boolean_t,
56721fffe3SKacheong Poon cred_t *cr);
57721fffe3SKacheong Poon static in_port_t tcp_get_next_priv_port(const tcp_t *);
58721fffe3SKacheong Poon
59721fffe3SKacheong Poon /*
60721fffe3SKacheong Poon * Hash list insertion routine for tcp_t structures. Each hash bucket
61721fffe3SKacheong Poon * contains a list of tcp_t entries, and each entry is bound to a unique
62721fffe3SKacheong Poon * port. If there are multiple tcp_t's that are bound to the same port, then
63721fffe3SKacheong Poon * one of them will be linked into the hash bucket list, and the rest will
64721fffe3SKacheong Poon * hang off of that one entry. For each port, entries bound to a specific IP
65721fffe3SKacheong Poon * address will be inserted before those those bound to INADDR_ANY.
66721fffe3SKacheong Poon */
67721fffe3SKacheong Poon void
tcp_bind_hash_insert(tf_t * tbf,tcp_t * tcp,int caller_holds_lock)68721fffe3SKacheong Poon tcp_bind_hash_insert(tf_t *tbf, tcp_t *tcp, int caller_holds_lock)
69721fffe3SKacheong Poon {
70721fffe3SKacheong Poon tcp_t **tcpp;
71721fffe3SKacheong Poon tcp_t *tcpnext;
72721fffe3SKacheong Poon tcp_t *tcphash;
73721fffe3SKacheong Poon conn_t *connp = tcp->tcp_connp;
74721fffe3SKacheong Poon conn_t *connext;
75721fffe3SKacheong Poon
76721fffe3SKacheong Poon if (tcp->tcp_ptpbhn != NULL) {
77721fffe3SKacheong Poon ASSERT(!caller_holds_lock);
78721fffe3SKacheong Poon tcp_bind_hash_remove(tcp);
79721fffe3SKacheong Poon }
80721fffe3SKacheong Poon tcpp = &tbf->tf_tcp;
81721fffe3SKacheong Poon if (!caller_holds_lock) {
82721fffe3SKacheong Poon mutex_enter(&tbf->tf_lock);
83721fffe3SKacheong Poon } else {
84721fffe3SKacheong Poon ASSERT(MUTEX_HELD(&tbf->tf_lock));
85721fffe3SKacheong Poon }
86721fffe3SKacheong Poon tcphash = tcpp[0];
87721fffe3SKacheong Poon tcpnext = NULL;
88721fffe3SKacheong Poon if (tcphash != NULL) {
89721fffe3SKacheong Poon /* Look for an entry using the same port */
90721fffe3SKacheong Poon while ((tcphash = tcpp[0]) != NULL &&
91721fffe3SKacheong Poon connp->conn_lport != tcphash->tcp_connp->conn_lport)
92721fffe3SKacheong Poon tcpp = &(tcphash->tcp_bind_hash);
93721fffe3SKacheong Poon
94721fffe3SKacheong Poon /* The port was not found, just add to the end */
95721fffe3SKacheong Poon if (tcphash == NULL)
96721fffe3SKacheong Poon goto insert;
97721fffe3SKacheong Poon
98721fffe3SKacheong Poon /*
99721fffe3SKacheong Poon * OK, there already exists an entry bound to the
100721fffe3SKacheong Poon * same port.
101721fffe3SKacheong Poon *
102721fffe3SKacheong Poon * If the new tcp bound to the INADDR_ANY address
103721fffe3SKacheong Poon * and the first one in the list is not bound to
104721fffe3SKacheong Poon * INADDR_ANY we skip all entries until we find the
105721fffe3SKacheong Poon * first one bound to INADDR_ANY.
106721fffe3SKacheong Poon * This makes sure that applications binding to a
107721fffe3SKacheong Poon * specific address get preference over those binding to
108721fffe3SKacheong Poon * INADDR_ANY.
109721fffe3SKacheong Poon */
110721fffe3SKacheong Poon tcpnext = tcphash;
111721fffe3SKacheong Poon connext = tcpnext->tcp_connp;
112721fffe3SKacheong Poon tcphash = NULL;
113721fffe3SKacheong Poon if (V6_OR_V4_INADDR_ANY(connp->conn_bound_addr_v6) &&
114721fffe3SKacheong Poon !V6_OR_V4_INADDR_ANY(connext->conn_bound_addr_v6)) {
115721fffe3SKacheong Poon while ((tcpnext = tcpp[0]) != NULL) {
116721fffe3SKacheong Poon connext = tcpnext->tcp_connp;
117721fffe3SKacheong Poon if (!V6_OR_V4_INADDR_ANY(
118721fffe3SKacheong Poon connext->conn_bound_addr_v6))
119721fffe3SKacheong Poon tcpp = &(tcpnext->tcp_bind_hash_port);
120721fffe3SKacheong Poon else
121721fffe3SKacheong Poon break;
122721fffe3SKacheong Poon }
123721fffe3SKacheong Poon if (tcpnext != NULL) {
124721fffe3SKacheong Poon tcpnext->tcp_ptpbhn = &tcp->tcp_bind_hash_port;
125721fffe3SKacheong Poon tcphash = tcpnext->tcp_bind_hash;
126721fffe3SKacheong Poon if (tcphash != NULL) {
127721fffe3SKacheong Poon tcphash->tcp_ptpbhn =
128721fffe3SKacheong Poon &(tcp->tcp_bind_hash);
129721fffe3SKacheong Poon tcpnext->tcp_bind_hash = NULL;
130721fffe3SKacheong Poon }
131721fffe3SKacheong Poon }
132721fffe3SKacheong Poon } else {
133721fffe3SKacheong Poon tcpnext->tcp_ptpbhn = &tcp->tcp_bind_hash_port;
134721fffe3SKacheong Poon tcphash = tcpnext->tcp_bind_hash;
135721fffe3SKacheong Poon if (tcphash != NULL) {
136721fffe3SKacheong Poon tcphash->tcp_ptpbhn =
137721fffe3SKacheong Poon &(tcp->tcp_bind_hash);
138721fffe3SKacheong Poon tcpnext->tcp_bind_hash = NULL;
139721fffe3SKacheong Poon }
140721fffe3SKacheong Poon }
141721fffe3SKacheong Poon }
142721fffe3SKacheong Poon insert:
143721fffe3SKacheong Poon tcp->tcp_bind_hash_port = tcpnext;
144721fffe3SKacheong Poon tcp->tcp_bind_hash = tcphash;
145721fffe3SKacheong Poon tcp->tcp_ptpbhn = tcpp;
146721fffe3SKacheong Poon tcpp[0] = tcp;
147721fffe3SKacheong Poon if (!caller_holds_lock)
148721fffe3SKacheong Poon mutex_exit(&tbf->tf_lock);
149721fffe3SKacheong Poon }
150721fffe3SKacheong Poon
151721fffe3SKacheong Poon /*
152721fffe3SKacheong Poon * Hash list removal routine for tcp_t structures.
153721fffe3SKacheong Poon */
154721fffe3SKacheong Poon void
tcp_bind_hash_remove(tcp_t * tcp)155721fffe3SKacheong Poon tcp_bind_hash_remove(tcp_t *tcp)
156721fffe3SKacheong Poon {
157721fffe3SKacheong Poon tcp_t *tcpnext;
158721fffe3SKacheong Poon kmutex_t *lockp;
159721fffe3SKacheong Poon tcp_stack_t *tcps = tcp->tcp_tcps;
160721fffe3SKacheong Poon conn_t *connp = tcp->tcp_connp;
161721fffe3SKacheong Poon
162721fffe3SKacheong Poon if (tcp->tcp_ptpbhn == NULL)
163721fffe3SKacheong Poon return;
164721fffe3SKacheong Poon
165721fffe3SKacheong Poon /*
166721fffe3SKacheong Poon * Extract the lock pointer in case there are concurrent
167721fffe3SKacheong Poon * hash_remove's for this instance.
168721fffe3SKacheong Poon */
169721fffe3SKacheong Poon ASSERT(connp->conn_lport != 0);
170721fffe3SKacheong Poon lockp = &tcps->tcps_bind_fanout[TCP_BIND_HASH(
171721fffe3SKacheong Poon connp->conn_lport)].tf_lock;
172721fffe3SKacheong Poon
173721fffe3SKacheong Poon ASSERT(lockp != NULL);
174721fffe3SKacheong Poon mutex_enter(lockp);
175721fffe3SKacheong Poon if (tcp->tcp_ptpbhn) {
176721fffe3SKacheong Poon tcpnext = tcp->tcp_bind_hash_port;
177721fffe3SKacheong Poon if (tcpnext != NULL) {
178721fffe3SKacheong Poon tcp->tcp_bind_hash_port = NULL;
179721fffe3SKacheong Poon tcpnext->tcp_ptpbhn = tcp->tcp_ptpbhn;
180721fffe3SKacheong Poon tcpnext->tcp_bind_hash = tcp->tcp_bind_hash;
181721fffe3SKacheong Poon if (tcpnext->tcp_bind_hash != NULL) {
182721fffe3SKacheong Poon tcpnext->tcp_bind_hash->tcp_ptpbhn =
183721fffe3SKacheong Poon &(tcpnext->tcp_bind_hash);
184721fffe3SKacheong Poon tcp->tcp_bind_hash = NULL;
185721fffe3SKacheong Poon }
186721fffe3SKacheong Poon } else if ((tcpnext = tcp->tcp_bind_hash) != NULL) {
187721fffe3SKacheong Poon tcpnext->tcp_ptpbhn = tcp->tcp_ptpbhn;
188721fffe3SKacheong Poon tcp->tcp_bind_hash = NULL;
189721fffe3SKacheong Poon }
190721fffe3SKacheong Poon *tcp->tcp_ptpbhn = tcpnext;
191721fffe3SKacheong Poon tcp->tcp_ptpbhn = NULL;
192721fffe3SKacheong Poon }
193721fffe3SKacheong Poon mutex_exit(lockp);
194721fffe3SKacheong Poon }
195721fffe3SKacheong Poon
196721fffe3SKacheong Poon /*
197721fffe3SKacheong Poon * Don't let port fall into the privileged range.
198721fffe3SKacheong Poon * Since the extra privileged ports can be arbitrary we also
199721fffe3SKacheong Poon * ensure that we exclude those from consideration.
200721fffe3SKacheong Poon * tcp_g_epriv_ports is not sorted thus we loop over it until
201721fffe3SKacheong Poon * there are no changes.
202721fffe3SKacheong Poon *
203721fffe3SKacheong Poon * Note: No locks are held when inspecting tcp_g_*epriv_ports
204721fffe3SKacheong Poon * but instead the code relies on:
205721fffe3SKacheong Poon * - the fact that the address of the array and its size never changes
206721fffe3SKacheong Poon * - the atomic assignment of the elements of the array
207721fffe3SKacheong Poon *
208721fffe3SKacheong Poon * Returns 0 if there are no more ports available.
209721fffe3SKacheong Poon *
210721fffe3SKacheong Poon * TS note: skip multilevel ports.
211721fffe3SKacheong Poon */
212721fffe3SKacheong Poon in_port_t
tcp_update_next_port(in_port_t port,const tcp_t * tcp,boolean_t random)213721fffe3SKacheong Poon tcp_update_next_port(in_port_t port, const tcp_t *tcp, boolean_t random)
214721fffe3SKacheong Poon {
215*7256a34eSDan McDonald int i, bump;
216721fffe3SKacheong Poon boolean_t restart = B_FALSE;
217721fffe3SKacheong Poon tcp_stack_t *tcps = tcp->tcp_tcps;
218721fffe3SKacheong Poon
219721fffe3SKacheong Poon if (random && tcp_random_anon_port != 0) {
220721fffe3SKacheong Poon (void) random_get_pseudo_bytes((uint8_t *)&port,
221721fffe3SKacheong Poon sizeof (in_port_t));
222721fffe3SKacheong Poon /*
223721fffe3SKacheong Poon * Unless changed by a sys admin, the smallest anon port
224721fffe3SKacheong Poon * is 32768 and the largest anon port is 65535. It is
225721fffe3SKacheong Poon * very likely (50%) for the random port to be smaller
226721fffe3SKacheong Poon * than the smallest anon port. When that happens,
227721fffe3SKacheong Poon * add port % (anon port range) to the smallest anon
228721fffe3SKacheong Poon * port to get the random port. It should fall into the
229721fffe3SKacheong Poon * valid anon port range.
230721fffe3SKacheong Poon */
231452bd827SMatt Barden if ((port < tcps->tcps_smallest_anon_port) ||
232452bd827SMatt Barden (port > tcps->tcps_largest_anon_port)) {
233*7256a34eSDan McDonald if (tcps->tcps_smallest_anon_port ==
234*7256a34eSDan McDonald tcps->tcps_largest_anon_port) {
235*7256a34eSDan McDonald bump = 0;
236*7256a34eSDan McDonald } else {
237*7256a34eSDan McDonald bump = port % (tcps->tcps_largest_anon_port -
238721fffe3SKacheong Poon tcps->tcps_smallest_anon_port);
239721fffe3SKacheong Poon }
240*7256a34eSDan McDonald port = tcps->tcps_smallest_anon_port + bump;
241*7256a34eSDan McDonald }
242721fffe3SKacheong Poon }
243721fffe3SKacheong Poon
244721fffe3SKacheong Poon retry:
245721fffe3SKacheong Poon if (port < tcps->tcps_smallest_anon_port)
246721fffe3SKacheong Poon port = (in_port_t)tcps->tcps_smallest_anon_port;
247721fffe3SKacheong Poon
248721fffe3SKacheong Poon if (port > tcps->tcps_largest_anon_port) {
249721fffe3SKacheong Poon if (restart)
250721fffe3SKacheong Poon return (0);
251721fffe3SKacheong Poon restart = B_TRUE;
252721fffe3SKacheong Poon port = (in_port_t)tcps->tcps_smallest_anon_port;
253721fffe3SKacheong Poon }
254721fffe3SKacheong Poon
255721fffe3SKacheong Poon if (port < tcps->tcps_smallest_nonpriv_port)
256721fffe3SKacheong Poon port = (in_port_t)tcps->tcps_smallest_nonpriv_port;
257721fffe3SKacheong Poon
258721fffe3SKacheong Poon for (i = 0; i < tcps->tcps_g_num_epriv_ports; i++) {
259721fffe3SKacheong Poon if (port == tcps->tcps_g_epriv_ports[i]) {
260721fffe3SKacheong Poon port++;
261721fffe3SKacheong Poon /*
262721fffe3SKacheong Poon * Make sure whether the port is in the
263721fffe3SKacheong Poon * valid range.
264721fffe3SKacheong Poon */
265721fffe3SKacheong Poon goto retry;
266721fffe3SKacheong Poon }
267721fffe3SKacheong Poon }
268721fffe3SKacheong Poon if (is_system_labeled() &&
269721fffe3SKacheong Poon (i = tsol_next_port(crgetzone(tcp->tcp_connp->conn_cred), port,
270721fffe3SKacheong Poon IPPROTO_TCP, B_TRUE)) != 0) {
271721fffe3SKacheong Poon port = i;
272721fffe3SKacheong Poon goto retry;
273721fffe3SKacheong Poon }
274721fffe3SKacheong Poon return (port);
275721fffe3SKacheong Poon }
276721fffe3SKacheong Poon
277721fffe3SKacheong Poon /*
278721fffe3SKacheong Poon * Return the next anonymous port in the privileged port range for
279721fffe3SKacheong Poon * bind checking. It starts at IPPORT_RESERVED - 1 and goes
280721fffe3SKacheong Poon * downwards. This is the same behavior as documented in the userland
281721fffe3SKacheong Poon * library call rresvport(3N).
282721fffe3SKacheong Poon *
283721fffe3SKacheong Poon * TS note: skip multilevel ports.
284721fffe3SKacheong Poon */
285721fffe3SKacheong Poon static in_port_t
tcp_get_next_priv_port(const tcp_t * tcp)286721fffe3SKacheong Poon tcp_get_next_priv_port(const tcp_t *tcp)
287721fffe3SKacheong Poon {
288721fffe3SKacheong Poon static in_port_t next_priv_port = IPPORT_RESERVED - 1;
289721fffe3SKacheong Poon in_port_t nextport;
290721fffe3SKacheong Poon boolean_t restart = B_FALSE;
291721fffe3SKacheong Poon tcp_stack_t *tcps = tcp->tcp_tcps;
292721fffe3SKacheong Poon retry:
293721fffe3SKacheong Poon if (next_priv_port < tcps->tcps_min_anonpriv_port ||
294721fffe3SKacheong Poon next_priv_port >= IPPORT_RESERVED) {
295721fffe3SKacheong Poon next_priv_port = IPPORT_RESERVED - 1;
296721fffe3SKacheong Poon if (restart)
297721fffe3SKacheong Poon return (0);
298721fffe3SKacheong Poon restart = B_TRUE;
299721fffe3SKacheong Poon }
300721fffe3SKacheong Poon if (is_system_labeled() &&
301721fffe3SKacheong Poon (nextport = tsol_next_port(crgetzone(tcp->tcp_connp->conn_cred),
302721fffe3SKacheong Poon next_priv_port, IPPROTO_TCP, B_FALSE)) != 0) {
303721fffe3SKacheong Poon next_priv_port = nextport;
304721fffe3SKacheong Poon goto retry;
305721fffe3SKacheong Poon }
306721fffe3SKacheong Poon return (next_priv_port--);
307721fffe3SKacheong Poon }
308721fffe3SKacheong Poon
309721fffe3SKacheong Poon 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)310721fffe3SKacheong Poon tcp_bind_select_lport(tcp_t *tcp, in_port_t *requested_port_ptr,
311721fffe3SKacheong Poon boolean_t bind_to_req_port_only, cred_t *cr)
312721fffe3SKacheong Poon {
313721fffe3SKacheong Poon in_port_t mlp_port;
314721fffe3SKacheong Poon mlp_type_t addrtype, mlptype;
315721fffe3SKacheong Poon boolean_t user_specified;
316721fffe3SKacheong Poon in_port_t allocated_port;
317721fffe3SKacheong Poon in_port_t requested_port = *requested_port_ptr;
318721fffe3SKacheong Poon conn_t *connp = tcp->tcp_connp;
319721fffe3SKacheong Poon zone_t *zone;
320721fffe3SKacheong Poon tcp_stack_t *tcps = tcp->tcp_tcps;
321721fffe3SKacheong Poon in6_addr_t v6addr = connp->conn_laddr_v6;
322721fffe3SKacheong Poon
323721fffe3SKacheong Poon /*
324721fffe3SKacheong Poon * XXX It's up to the caller to specify bind_to_req_port_only or not.
325721fffe3SKacheong Poon */
326721fffe3SKacheong Poon ASSERT(cr != NULL);
327721fffe3SKacheong Poon
328721fffe3SKacheong Poon /*
329721fffe3SKacheong Poon * Get a valid port (within the anonymous range and should not
330721fffe3SKacheong Poon * be a privileged one) to use if the user has not given a port.
331721fffe3SKacheong Poon * If multiple threads are here, they may all start with
332721fffe3SKacheong Poon * with the same initial port. But, it should be fine as long as
333721fffe3SKacheong Poon * tcp_bindi will ensure that no two threads will be assigned
334721fffe3SKacheong Poon * the same port.
335721fffe3SKacheong Poon *
336721fffe3SKacheong Poon * NOTE: XXX If a privileged process asks for an anonymous port, we
337721fffe3SKacheong Poon * still check for ports only in the range > tcp_smallest_non_priv_port,
338721fffe3SKacheong Poon * unless TCP_ANONPRIVBIND option is set.
339721fffe3SKacheong Poon */
340721fffe3SKacheong Poon mlptype = mlptSingle;
341721fffe3SKacheong Poon mlp_port = requested_port;
342721fffe3SKacheong Poon if (requested_port == 0) {
343721fffe3SKacheong Poon requested_port = connp->conn_anon_priv_bind ?
344721fffe3SKacheong Poon tcp_get_next_priv_port(tcp) :
345721fffe3SKacheong Poon tcp_update_next_port(tcps->tcps_next_port_to_try,
346721fffe3SKacheong Poon tcp, B_TRUE);
347721fffe3SKacheong Poon if (requested_port == 0) {
348721fffe3SKacheong Poon return (-TNOADDR);
349721fffe3SKacheong Poon }
350721fffe3SKacheong Poon user_specified = B_FALSE;
351721fffe3SKacheong Poon
352721fffe3SKacheong Poon /*
353721fffe3SKacheong Poon * If the user went through one of the RPC interfaces to create
354721fffe3SKacheong Poon * this socket and RPC is MLP in this zone, then give him an
355721fffe3SKacheong Poon * anonymous MLP.
356721fffe3SKacheong Poon */
357721fffe3SKacheong Poon if (connp->conn_anon_mlp && is_system_labeled()) {
358721fffe3SKacheong Poon zone = crgetzone(cr);
359721fffe3SKacheong Poon addrtype = tsol_mlp_addr_type(
360721fffe3SKacheong Poon connp->conn_allzones ? ALL_ZONES : zone->zone_id,
361721fffe3SKacheong Poon IPV6_VERSION, &v6addr,
362721fffe3SKacheong Poon tcps->tcps_netstack->netstack_ip);
363721fffe3SKacheong Poon if (addrtype == mlptSingle) {
364721fffe3SKacheong Poon return (-TNOADDR);
365721fffe3SKacheong Poon }
366721fffe3SKacheong Poon mlptype = tsol_mlp_port_type(zone, IPPROTO_TCP,
367721fffe3SKacheong Poon PMAPPORT, addrtype);
368721fffe3SKacheong Poon mlp_port = PMAPPORT;
369721fffe3SKacheong Poon }
370721fffe3SKacheong Poon } else {
371721fffe3SKacheong Poon int i;
372721fffe3SKacheong Poon boolean_t priv = B_FALSE;
373721fffe3SKacheong Poon
374721fffe3SKacheong Poon /*
375721fffe3SKacheong Poon * If the requested_port is in the well-known privileged range,
376721fffe3SKacheong Poon * verify that the stream was opened by a privileged user.
377721fffe3SKacheong Poon * Note: No locks are held when inspecting tcp_g_*epriv_ports
378721fffe3SKacheong Poon * but instead the code relies on:
379721fffe3SKacheong Poon * - the fact that the address of the array and its size never
380721fffe3SKacheong Poon * changes
381721fffe3SKacheong Poon * - the atomic assignment of the elements of the array
382721fffe3SKacheong Poon */
383721fffe3SKacheong Poon if (requested_port < tcps->tcps_smallest_nonpriv_port) {
384721fffe3SKacheong Poon priv = B_TRUE;
385721fffe3SKacheong Poon } else {
386721fffe3SKacheong Poon for (i = 0; i < tcps->tcps_g_num_epriv_ports; i++) {
387721fffe3SKacheong Poon if (requested_port ==
388721fffe3SKacheong Poon tcps->tcps_g_epriv_ports[i]) {
389721fffe3SKacheong Poon priv = B_TRUE;
390721fffe3SKacheong Poon break;
391721fffe3SKacheong Poon }
392721fffe3SKacheong Poon }
393721fffe3SKacheong Poon }
394721fffe3SKacheong Poon if (priv) {
395721fffe3SKacheong Poon if (secpolicy_net_privaddr(cr, requested_port,
396721fffe3SKacheong Poon IPPROTO_TCP) != 0) {
397721fffe3SKacheong Poon if (connp->conn_debug) {
398721fffe3SKacheong Poon (void) strlog(TCP_MOD_ID, 0, 1,
399721fffe3SKacheong Poon SL_ERROR|SL_TRACE,
400721fffe3SKacheong Poon "tcp_bind: no priv for port %d",
401721fffe3SKacheong Poon requested_port);
402721fffe3SKacheong Poon }
403721fffe3SKacheong Poon return (-TACCES);
404721fffe3SKacheong Poon }
405721fffe3SKacheong Poon }
406721fffe3SKacheong Poon user_specified = B_TRUE;
407721fffe3SKacheong Poon
408721fffe3SKacheong Poon connp = tcp->tcp_connp;
409721fffe3SKacheong Poon if (is_system_labeled()) {
410721fffe3SKacheong Poon zone = crgetzone(cr);
411721fffe3SKacheong Poon addrtype = tsol_mlp_addr_type(
412721fffe3SKacheong Poon connp->conn_allzones ? ALL_ZONES : zone->zone_id,
413721fffe3SKacheong Poon IPV6_VERSION, &v6addr,
414721fffe3SKacheong Poon tcps->tcps_netstack->netstack_ip);
415721fffe3SKacheong Poon if (addrtype == mlptSingle) {
416721fffe3SKacheong Poon return (-TNOADDR);
417721fffe3SKacheong Poon }
418721fffe3SKacheong Poon mlptype = tsol_mlp_port_type(zone, IPPROTO_TCP,
419721fffe3SKacheong Poon requested_port, addrtype);
420721fffe3SKacheong Poon }
421721fffe3SKacheong Poon }
422721fffe3SKacheong Poon
423721fffe3SKacheong Poon if (mlptype != mlptSingle) {
424721fffe3SKacheong Poon if (secpolicy_net_bindmlp(cr) != 0) {
425721fffe3SKacheong Poon if (connp->conn_debug) {
426721fffe3SKacheong Poon (void) strlog(TCP_MOD_ID, 0, 1,
427721fffe3SKacheong Poon SL_ERROR|SL_TRACE,
428721fffe3SKacheong Poon "tcp_bind: no priv for multilevel port %d",
429721fffe3SKacheong Poon requested_port);
430721fffe3SKacheong Poon }
431721fffe3SKacheong Poon return (-TACCES);
432721fffe3SKacheong Poon }
433721fffe3SKacheong Poon
434721fffe3SKacheong Poon /*
435721fffe3SKacheong Poon * If we're specifically binding a shared IP address and the
436721fffe3SKacheong Poon * port is MLP on shared addresses, then check to see if this
437721fffe3SKacheong Poon * zone actually owns the MLP. Reject if not.
438721fffe3SKacheong Poon */
439721fffe3SKacheong Poon if (mlptype == mlptShared && addrtype == mlptShared) {
440721fffe3SKacheong Poon /*
441721fffe3SKacheong Poon * No need to handle exclusive-stack zones since
442721fffe3SKacheong Poon * ALL_ZONES only applies to the shared stack.
443721fffe3SKacheong Poon */
444721fffe3SKacheong Poon zoneid_t mlpzone;
445721fffe3SKacheong Poon
446721fffe3SKacheong Poon mlpzone = tsol_mlp_findzone(IPPROTO_TCP,
447721fffe3SKacheong Poon htons(mlp_port));
448721fffe3SKacheong Poon if (connp->conn_zoneid != mlpzone) {
449721fffe3SKacheong Poon if (connp->conn_debug) {
450721fffe3SKacheong Poon (void) strlog(TCP_MOD_ID, 0, 1,
451721fffe3SKacheong Poon SL_ERROR|SL_TRACE,
452721fffe3SKacheong Poon "tcp_bind: attempt to bind port "
453721fffe3SKacheong Poon "%d on shared addr in zone %d "
454721fffe3SKacheong Poon "(should be %d)",
455721fffe3SKacheong Poon mlp_port, connp->conn_zoneid,
456721fffe3SKacheong Poon mlpzone);
457721fffe3SKacheong Poon }
458721fffe3SKacheong Poon return (-TACCES);
459721fffe3SKacheong Poon }
460721fffe3SKacheong Poon }
461721fffe3SKacheong Poon
462721fffe3SKacheong Poon if (!user_specified) {
463721fffe3SKacheong Poon int err;
464721fffe3SKacheong Poon err = tsol_mlp_anon(zone, mlptype, connp->conn_proto,
465721fffe3SKacheong Poon requested_port, B_TRUE);
466721fffe3SKacheong Poon if (err != 0) {
467721fffe3SKacheong Poon if (connp->conn_debug) {
468721fffe3SKacheong Poon (void) strlog(TCP_MOD_ID, 0, 1,
469721fffe3SKacheong Poon SL_ERROR|SL_TRACE,
470721fffe3SKacheong Poon "tcp_bind: cannot establish anon "
471721fffe3SKacheong Poon "MLP for port %d",
472721fffe3SKacheong Poon requested_port);
473721fffe3SKacheong Poon }
474721fffe3SKacheong Poon return (err);
475721fffe3SKacheong Poon }
476721fffe3SKacheong Poon connp->conn_anon_port = B_TRUE;
477721fffe3SKacheong Poon }
478721fffe3SKacheong Poon connp->conn_mlp_type = mlptype;
479721fffe3SKacheong Poon }
480721fffe3SKacheong Poon
481721fffe3SKacheong Poon allocated_port = tcp_bindi(tcp, requested_port, &v6addr,
482721fffe3SKacheong Poon connp->conn_reuseaddr, B_FALSE, bind_to_req_port_only,
483721fffe3SKacheong Poon user_specified);
484721fffe3SKacheong Poon
485721fffe3SKacheong Poon if (allocated_port == 0) {
486721fffe3SKacheong Poon connp->conn_mlp_type = mlptSingle;
487721fffe3SKacheong Poon if (connp->conn_anon_port) {
488721fffe3SKacheong Poon connp->conn_anon_port = B_FALSE;
489721fffe3SKacheong Poon (void) tsol_mlp_anon(zone, mlptype, connp->conn_proto,
490721fffe3SKacheong Poon requested_port, B_FALSE);
491721fffe3SKacheong Poon }
492721fffe3SKacheong Poon if (bind_to_req_port_only) {
493721fffe3SKacheong Poon if (connp->conn_debug) {
494721fffe3SKacheong Poon (void) strlog(TCP_MOD_ID, 0, 1,
495721fffe3SKacheong Poon SL_ERROR|SL_TRACE,
496721fffe3SKacheong Poon "tcp_bind: requested addr busy");
497721fffe3SKacheong Poon }
498721fffe3SKacheong Poon return (-TADDRBUSY);
499721fffe3SKacheong Poon } else {
500721fffe3SKacheong Poon /* If we are out of ports, fail the bind. */
501721fffe3SKacheong Poon if (connp->conn_debug) {
502721fffe3SKacheong Poon (void) strlog(TCP_MOD_ID, 0, 1,
503721fffe3SKacheong Poon SL_ERROR|SL_TRACE,
504721fffe3SKacheong Poon "tcp_bind: out of ports?");
505721fffe3SKacheong Poon }
506721fffe3SKacheong Poon return (-TNOADDR);
507721fffe3SKacheong Poon }
508721fffe3SKacheong Poon }
509721fffe3SKacheong Poon
510721fffe3SKacheong Poon /* Pass the allocated port back */
511721fffe3SKacheong Poon *requested_port_ptr = allocated_port;
512721fffe3SKacheong Poon return (0);
513721fffe3SKacheong Poon }
514721fffe3SKacheong Poon
515721fffe3SKacheong Poon /*
516721fffe3SKacheong Poon * Check the address and check/pick a local port number.
517721fffe3SKacheong Poon */
518721fffe3SKacheong Poon int
tcp_bind_check(conn_t * connp,struct sockaddr * sa,socklen_t len,cred_t * cr,boolean_t bind_to_req_port_only)519721fffe3SKacheong Poon tcp_bind_check(conn_t *connp, struct sockaddr *sa, socklen_t len, cred_t *cr,
520721fffe3SKacheong Poon boolean_t bind_to_req_port_only)
521721fffe3SKacheong Poon {
522721fffe3SKacheong Poon tcp_t *tcp = connp->conn_tcp;
523721fffe3SKacheong Poon sin_t *sin;
524721fffe3SKacheong Poon sin6_t *sin6;
525721fffe3SKacheong Poon in_port_t requested_port;
526721fffe3SKacheong Poon ipaddr_t v4addr;
527721fffe3SKacheong Poon in6_addr_t v6addr;
528721fffe3SKacheong Poon ip_laddr_t laddr_type = IPVL_UNICAST_UP; /* INADDR_ANY */
529721fffe3SKacheong Poon zoneid_t zoneid = IPCL_ZONEID(connp);
530721fffe3SKacheong Poon ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
531721fffe3SKacheong Poon uint_t scopeid = 0;
532721fffe3SKacheong Poon int error = 0;
533721fffe3SKacheong Poon ip_xmit_attr_t *ixa = connp->conn_ixa;
534721fffe3SKacheong Poon
535721fffe3SKacheong Poon ASSERT((uintptr_t)len <= (uintptr_t)INT_MAX);
536721fffe3SKacheong Poon
537721fffe3SKacheong Poon if (tcp->tcp_state == TCPS_BOUND) {
538721fffe3SKacheong Poon return (0);
539721fffe3SKacheong Poon } else if (tcp->tcp_state > TCPS_BOUND) {
540721fffe3SKacheong Poon if (connp->conn_debug) {
541721fffe3SKacheong Poon (void) strlog(TCP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE,
542721fffe3SKacheong Poon "tcp_bind: bad state, %d", tcp->tcp_state);
543721fffe3SKacheong Poon }
544721fffe3SKacheong Poon return (-TOUTSTATE);
545721fffe3SKacheong Poon }
546721fffe3SKacheong Poon
547721fffe3SKacheong Poon ASSERT(sa != NULL && len != 0);
548721fffe3SKacheong Poon
549721fffe3SKacheong Poon if (!OK_32PTR((char *)sa)) {
550721fffe3SKacheong Poon if (connp->conn_debug) {
551721fffe3SKacheong Poon (void) strlog(TCP_MOD_ID, 0, 1,
552721fffe3SKacheong Poon SL_ERROR|SL_TRACE,
553721fffe3SKacheong Poon "tcp_bind: bad address parameter, "
554721fffe3SKacheong Poon "address %p, len %d",
555721fffe3SKacheong Poon (void *)sa, len);
556721fffe3SKacheong Poon }
557721fffe3SKacheong Poon return (-TPROTO);
558721fffe3SKacheong Poon }
559721fffe3SKacheong Poon
560721fffe3SKacheong Poon error = proto_verify_ip_addr(connp->conn_family, sa, len);
561721fffe3SKacheong Poon if (error != 0) {
562721fffe3SKacheong Poon return (error);
563721fffe3SKacheong Poon }
564721fffe3SKacheong Poon
565721fffe3SKacheong Poon switch (len) {
566721fffe3SKacheong Poon case sizeof (sin_t): /* Complete IPv4 address */
567721fffe3SKacheong Poon sin = (sin_t *)sa;
568721fffe3SKacheong Poon requested_port = ntohs(sin->sin_port);
569721fffe3SKacheong Poon v4addr = sin->sin_addr.s_addr;
570721fffe3SKacheong Poon IN6_IPADDR_TO_V4MAPPED(v4addr, &v6addr);
571721fffe3SKacheong Poon if (v4addr != INADDR_ANY) {
572721fffe3SKacheong Poon laddr_type = ip_laddr_verify_v4(v4addr, zoneid, ipst,
573721fffe3SKacheong Poon B_FALSE);
574721fffe3SKacheong Poon }
575721fffe3SKacheong Poon break;
576721fffe3SKacheong Poon
577721fffe3SKacheong Poon case sizeof (sin6_t): /* Complete IPv6 address */
578721fffe3SKacheong Poon sin6 = (sin6_t *)sa;
579721fffe3SKacheong Poon v6addr = sin6->sin6_addr;
580721fffe3SKacheong Poon requested_port = ntohs(sin6->sin6_port);
581721fffe3SKacheong Poon if (IN6_IS_ADDR_V4MAPPED(&v6addr)) {
582721fffe3SKacheong Poon if (connp->conn_ipv6_v6only)
583721fffe3SKacheong Poon return (EADDRNOTAVAIL);
584721fffe3SKacheong Poon
585721fffe3SKacheong Poon IN6_V4MAPPED_TO_IPADDR(&v6addr, v4addr);
586721fffe3SKacheong Poon if (v4addr != INADDR_ANY) {
587721fffe3SKacheong Poon laddr_type = ip_laddr_verify_v4(v4addr,
588721fffe3SKacheong Poon zoneid, ipst, B_FALSE);
589721fffe3SKacheong Poon }
590721fffe3SKacheong Poon } else {
591721fffe3SKacheong Poon if (!IN6_IS_ADDR_UNSPECIFIED(&v6addr)) {
592721fffe3SKacheong Poon if (IN6_IS_ADDR_LINKSCOPE(&v6addr))
593721fffe3SKacheong Poon scopeid = sin6->sin6_scope_id;
594721fffe3SKacheong Poon laddr_type = ip_laddr_verify_v6(&v6addr,
595721fffe3SKacheong Poon zoneid, ipst, B_FALSE, scopeid);
596721fffe3SKacheong Poon }
597721fffe3SKacheong Poon }
598721fffe3SKacheong Poon break;
599721fffe3SKacheong Poon
600721fffe3SKacheong Poon default:
601721fffe3SKacheong Poon if (connp->conn_debug) {
602721fffe3SKacheong Poon (void) strlog(TCP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE,
603721fffe3SKacheong Poon "tcp_bind: bad address length, %d", len);
604721fffe3SKacheong Poon }
605721fffe3SKacheong Poon return (EAFNOSUPPORT);
606721fffe3SKacheong Poon /* return (-TBADADDR); */
607721fffe3SKacheong Poon }
608721fffe3SKacheong Poon
609721fffe3SKacheong Poon /* Is the local address a valid unicast address? */
610721fffe3SKacheong Poon if (laddr_type == IPVL_BAD)
611721fffe3SKacheong Poon return (EADDRNOTAVAIL);
612721fffe3SKacheong Poon
613721fffe3SKacheong Poon connp->conn_bound_addr_v6 = v6addr;
614721fffe3SKacheong Poon if (scopeid != 0) {
615721fffe3SKacheong Poon ixa->ixa_flags |= IXAF_SCOPEID_SET;
616721fffe3SKacheong Poon ixa->ixa_scopeid = scopeid;
617721fffe3SKacheong Poon connp->conn_incoming_ifindex = scopeid;
618721fffe3SKacheong Poon } else {
619721fffe3SKacheong Poon ixa->ixa_flags &= ~IXAF_SCOPEID_SET;
620721fffe3SKacheong Poon connp->conn_incoming_ifindex = connp->conn_bound_if;
621721fffe3SKacheong Poon }
622721fffe3SKacheong Poon
623721fffe3SKacheong Poon connp->conn_laddr_v6 = v6addr;
624721fffe3SKacheong Poon connp->conn_saddr_v6 = v6addr;
625721fffe3SKacheong Poon
626721fffe3SKacheong Poon bind_to_req_port_only = requested_port != 0 && bind_to_req_port_only;
627721fffe3SKacheong Poon
628721fffe3SKacheong Poon error = tcp_bind_select_lport(tcp, &requested_port,
629721fffe3SKacheong Poon bind_to_req_port_only, cr);
630721fffe3SKacheong Poon if (error != 0) {
631721fffe3SKacheong Poon connp->conn_laddr_v6 = ipv6_all_zeros;
632721fffe3SKacheong Poon connp->conn_saddr_v6 = ipv6_all_zeros;
633721fffe3SKacheong Poon connp->conn_bound_addr_v6 = ipv6_all_zeros;
634721fffe3SKacheong Poon }
635721fffe3SKacheong Poon return (error);
636721fffe3SKacheong Poon }
637721fffe3SKacheong Poon
638721fffe3SKacheong Poon /*
639721fffe3SKacheong Poon * If the "bind_to_req_port_only" parameter is set, if the requested port
640721fffe3SKacheong Poon * number is available, return it, If not return 0
641721fffe3SKacheong Poon *
642721fffe3SKacheong Poon * If "bind_to_req_port_only" parameter is not set and
643721fffe3SKacheong Poon * If the requested port number is available, return it. If not, return
644721fffe3SKacheong Poon * the first anonymous port we happen across. If no anonymous ports are
645721fffe3SKacheong Poon * available, return 0. addr is the requested local address, if any.
646721fffe3SKacheong Poon *
647721fffe3SKacheong Poon * In either case, when succeeding update the tcp_t to record the port number
648721fffe3SKacheong Poon * and insert it in the bind hash table.
649721fffe3SKacheong Poon *
650721fffe3SKacheong Poon * Note that TCP over IPv4 and IPv6 sockets can use the same port number
651721fffe3SKacheong Poon * without setting SO_REUSEADDR. This is needed so that they
652721fffe3SKacheong Poon * can be viewed as two independent transport protocols.
653721fffe3SKacheong Poon */
654721fffe3SKacheong Poon 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)655721fffe3SKacheong Poon tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
656721fffe3SKacheong Poon int reuseaddr, boolean_t quick_connect,
657721fffe3SKacheong Poon boolean_t bind_to_req_port_only, boolean_t user_specified)
658721fffe3SKacheong Poon {
659721fffe3SKacheong Poon /* number of times we have run around the loop */
660721fffe3SKacheong Poon int count = 0;
661721fffe3SKacheong Poon /* maximum number of times to run around the loop */
662721fffe3SKacheong Poon int loopmax;
663721fffe3SKacheong Poon conn_t *connp = tcp->tcp_connp;
664721fffe3SKacheong Poon tcp_stack_t *tcps = tcp->tcp_tcps;
665721fffe3SKacheong Poon
666721fffe3SKacheong Poon /*
667721fffe3SKacheong Poon * Lookup for free addresses is done in a loop and "loopmax"
668721fffe3SKacheong Poon * influences how long we spin in the loop
669721fffe3SKacheong Poon */
670721fffe3SKacheong Poon if (bind_to_req_port_only) {
671721fffe3SKacheong Poon /*
672721fffe3SKacheong Poon * If the requested port is busy, don't bother to look
673721fffe3SKacheong Poon * for a new one. Setting loop maximum count to 1 has
674721fffe3SKacheong Poon * that effect.
675721fffe3SKacheong Poon */
676721fffe3SKacheong Poon loopmax = 1;
677721fffe3SKacheong Poon } else {
678721fffe3SKacheong Poon /*
679721fffe3SKacheong Poon * If the requested port is busy, look for a free one
680721fffe3SKacheong Poon * in the anonymous port range.
681721fffe3SKacheong Poon * Set loopmax appropriately so that one does not look
682721fffe3SKacheong Poon * forever in the case all of the anonymous ports are in use.
683721fffe3SKacheong Poon */
684721fffe3SKacheong Poon if (connp->conn_anon_priv_bind) {
685721fffe3SKacheong Poon /*
686721fffe3SKacheong Poon * loopmax =
687721fffe3SKacheong Poon * (IPPORT_RESERVED-1) - tcp_min_anonpriv_port + 1
688721fffe3SKacheong Poon */
689721fffe3SKacheong Poon loopmax = IPPORT_RESERVED -
690721fffe3SKacheong Poon tcps->tcps_min_anonpriv_port;
691721fffe3SKacheong Poon } else {
692721fffe3SKacheong Poon loopmax = (tcps->tcps_largest_anon_port -
693721fffe3SKacheong Poon tcps->tcps_smallest_anon_port + 1);
694721fffe3SKacheong Poon }
695721fffe3SKacheong Poon }
696721fffe3SKacheong Poon do {
697721fffe3SKacheong Poon uint16_t lport;
698721fffe3SKacheong Poon tf_t *tbf;
699721fffe3SKacheong Poon tcp_t *ltcp;
700721fffe3SKacheong Poon conn_t *lconnp;
701721fffe3SKacheong Poon
702721fffe3SKacheong Poon lport = htons(port);
703721fffe3SKacheong Poon
704721fffe3SKacheong Poon /*
705721fffe3SKacheong Poon * Ensure that the tcp_t is not currently in the bind hash.
706721fffe3SKacheong Poon * Hold the lock on the hash bucket to ensure that
707721fffe3SKacheong Poon * the duplicate check plus the insertion is an atomic
708721fffe3SKacheong Poon * operation.
709721fffe3SKacheong Poon *
710721fffe3SKacheong Poon * This function does an inline lookup on the bind hash list
711721fffe3SKacheong Poon * Make sure that we access only members of tcp_t
712721fffe3SKacheong Poon * and that we don't look at tcp_tcp, since we are not
713721fffe3SKacheong Poon * doing a CONN_INC_REF.
714721fffe3SKacheong Poon */
715721fffe3SKacheong Poon tcp_bind_hash_remove(tcp);
716721fffe3SKacheong Poon tbf = &tcps->tcps_bind_fanout[TCP_BIND_HASH(lport)];
717721fffe3SKacheong Poon mutex_enter(&tbf->tf_lock);
718721fffe3SKacheong Poon for (ltcp = tbf->tf_tcp; ltcp != NULL;
719721fffe3SKacheong Poon ltcp = ltcp->tcp_bind_hash) {
720721fffe3SKacheong Poon if (lport == ltcp->tcp_connp->conn_lport)
721721fffe3SKacheong Poon break;
722721fffe3SKacheong Poon }
723721fffe3SKacheong Poon
724721fffe3SKacheong Poon for (; ltcp != NULL; ltcp = ltcp->tcp_bind_hash_port) {
725721fffe3SKacheong Poon boolean_t not_socket;
726721fffe3SKacheong Poon boolean_t exclbind;
727721fffe3SKacheong Poon
728721fffe3SKacheong Poon lconnp = ltcp->tcp_connp;
729721fffe3SKacheong Poon
730721fffe3SKacheong Poon /*
731721fffe3SKacheong Poon * On a labeled system, we must treat bindings to ports
732721fffe3SKacheong Poon * on shared IP addresses by sockets with MAC exemption
733721fffe3SKacheong Poon * privilege as being in all zones, as there's
734721fffe3SKacheong Poon * otherwise no way to identify the right receiver.
735721fffe3SKacheong Poon */
736721fffe3SKacheong Poon if (!IPCL_BIND_ZONE_MATCH(lconnp, connp))
737721fffe3SKacheong Poon continue;
738721fffe3SKacheong Poon
739721fffe3SKacheong Poon /*
740721fffe3SKacheong Poon * If TCP_EXCLBIND is set for either the bound or
741721fffe3SKacheong Poon * binding endpoint, the semantics of bind
742721fffe3SKacheong Poon * is changed according to the following.
743721fffe3SKacheong Poon *
744721fffe3SKacheong Poon * spec = specified address (v4 or v6)
745721fffe3SKacheong Poon * unspec = unspecified address (v4 or v6)
746721fffe3SKacheong Poon * A = specified addresses are different for endpoints
747721fffe3SKacheong Poon *
748721fffe3SKacheong Poon * bound bind to allowed
749721fffe3SKacheong Poon * -------------------------------------
750721fffe3SKacheong Poon * unspec unspec no
751721fffe3SKacheong Poon * unspec spec no
752721fffe3SKacheong Poon * spec unspec no
753721fffe3SKacheong Poon * spec spec yes if A
754721fffe3SKacheong Poon *
755721fffe3SKacheong Poon * For labeled systems, SO_MAC_EXEMPT behaves the same
756721fffe3SKacheong Poon * as TCP_EXCLBIND, except that zoneid is ignored.
757721fffe3SKacheong Poon *
758721fffe3SKacheong Poon * Note:
759721fffe3SKacheong Poon *
760721fffe3SKacheong Poon * 1. Because of TLI semantics, an endpoint can go
761721fffe3SKacheong Poon * back from, say TCP_ESTABLISHED to TCPS_LISTEN or
762721fffe3SKacheong Poon * TCPS_BOUND, depending on whether it is originally
763721fffe3SKacheong Poon * a listener or not. That is why we need to check
764721fffe3SKacheong Poon * for states greater than or equal to TCPS_BOUND
765721fffe3SKacheong Poon * here.
766721fffe3SKacheong Poon *
767721fffe3SKacheong Poon * 2. Ideally, we should only check for state equals
768721fffe3SKacheong Poon * to TCPS_LISTEN. And the following check should be
769721fffe3SKacheong Poon * added.
770721fffe3SKacheong Poon *
771721fffe3SKacheong Poon * if (ltcp->tcp_state == TCPS_LISTEN ||
772721fffe3SKacheong Poon * !reuseaddr || !lconnp->conn_reuseaddr) {
773721fffe3SKacheong Poon * ...
774721fffe3SKacheong Poon * }
775721fffe3SKacheong Poon *
776721fffe3SKacheong Poon * The semantics will be changed to this. If the
777721fffe3SKacheong Poon * endpoint on the list is in state not equal to
778721fffe3SKacheong Poon * TCPS_LISTEN and both endpoints have SO_REUSEADDR
779721fffe3SKacheong Poon * set, let the bind succeed.
780721fffe3SKacheong Poon *
781721fffe3SKacheong Poon * Because of (1), we cannot do that for TLI
782721fffe3SKacheong Poon * endpoints. But we can do that for socket endpoints.
783721fffe3SKacheong Poon * If in future, we can change this going back
784721fffe3SKacheong Poon * semantics, we can use the above check for TLI also.
785721fffe3SKacheong Poon */
786721fffe3SKacheong Poon not_socket = !(TCP_IS_SOCKET(ltcp) &&
787721fffe3SKacheong Poon TCP_IS_SOCKET(tcp));
788721fffe3SKacheong Poon exclbind = lconnp->conn_exclbind ||
789721fffe3SKacheong Poon connp->conn_exclbind;
790721fffe3SKacheong Poon
791721fffe3SKacheong Poon if ((lconnp->conn_mac_mode != CONN_MAC_DEFAULT) ||
792721fffe3SKacheong Poon (connp->conn_mac_mode != CONN_MAC_DEFAULT) ||
793721fffe3SKacheong Poon (exclbind && (not_socket ||
794721fffe3SKacheong Poon ltcp->tcp_state <= TCPS_ESTABLISHED))) {
795721fffe3SKacheong Poon if (V6_OR_V4_INADDR_ANY(
796721fffe3SKacheong Poon lconnp->conn_bound_addr_v6) ||
797721fffe3SKacheong Poon V6_OR_V4_INADDR_ANY(*laddr) ||
798721fffe3SKacheong Poon IN6_ARE_ADDR_EQUAL(laddr,
799721fffe3SKacheong Poon &lconnp->conn_bound_addr_v6)) {
800721fffe3SKacheong Poon break;
801721fffe3SKacheong Poon }
802721fffe3SKacheong Poon continue;
803721fffe3SKacheong Poon }
804721fffe3SKacheong Poon
805721fffe3SKacheong Poon /*
806721fffe3SKacheong Poon * Check ipversion to allow IPv4 and IPv6 sockets to
807721fffe3SKacheong Poon * have disjoint port number spaces, if *_EXCLBIND
808721fffe3SKacheong Poon * is not set and only if the application binds to a
809721fffe3SKacheong Poon * specific port. We use the same autoassigned port
810721fffe3SKacheong Poon * number space for IPv4 and IPv6 sockets.
811721fffe3SKacheong Poon */
812721fffe3SKacheong Poon if (connp->conn_ipversion != lconnp->conn_ipversion &&
813721fffe3SKacheong Poon bind_to_req_port_only)
814721fffe3SKacheong Poon continue;
815721fffe3SKacheong Poon
816721fffe3SKacheong Poon /*
817721fffe3SKacheong Poon * Ideally, we should make sure that the source
818721fffe3SKacheong Poon * address, remote address, and remote port in the
819721fffe3SKacheong Poon * four tuple for this tcp-connection is unique.
820721fffe3SKacheong Poon * However, trying to find out the local source
821721fffe3SKacheong Poon * address would require too much code duplication
822721fffe3SKacheong Poon * with IP, since IP needs needs to have that code
823721fffe3SKacheong Poon * to support userland TCP implementations.
824721fffe3SKacheong Poon */
825721fffe3SKacheong Poon if (quick_connect &&
826721fffe3SKacheong Poon (ltcp->tcp_state > TCPS_LISTEN) &&
827721fffe3SKacheong Poon ((connp->conn_fport != lconnp->conn_fport) ||
828721fffe3SKacheong Poon !IN6_ARE_ADDR_EQUAL(&connp->conn_faddr_v6,
829721fffe3SKacheong Poon &lconnp->conn_faddr_v6)))
830721fffe3SKacheong Poon continue;
831721fffe3SKacheong Poon
832721fffe3SKacheong Poon if (!reuseaddr) {
833721fffe3SKacheong Poon /*
834721fffe3SKacheong Poon * No socket option SO_REUSEADDR.
835721fffe3SKacheong Poon * If existing port is bound to
836721fffe3SKacheong Poon * a non-wildcard IP address
837721fffe3SKacheong Poon * and the requesting stream is
838721fffe3SKacheong Poon * bound to a distinct
839721fffe3SKacheong Poon * different IP addresses
840721fffe3SKacheong Poon * (non-wildcard, also), keep
841721fffe3SKacheong Poon * going.
842721fffe3SKacheong Poon */
843721fffe3SKacheong Poon if (!V6_OR_V4_INADDR_ANY(*laddr) &&
844721fffe3SKacheong Poon !V6_OR_V4_INADDR_ANY(
845721fffe3SKacheong Poon lconnp->conn_bound_addr_v6) &&
846721fffe3SKacheong Poon !IN6_ARE_ADDR_EQUAL(laddr,
847721fffe3SKacheong Poon &lconnp->conn_bound_addr_v6))
848721fffe3SKacheong Poon continue;
849721fffe3SKacheong Poon if (ltcp->tcp_state >= TCPS_BOUND) {
850721fffe3SKacheong Poon /*
851721fffe3SKacheong Poon * This port is being used and
852721fffe3SKacheong Poon * its state is >= TCPS_BOUND,
853721fffe3SKacheong Poon * so we can't bind to it.
854721fffe3SKacheong Poon */
855721fffe3SKacheong Poon break;
856721fffe3SKacheong Poon }
857721fffe3SKacheong Poon } else {
858721fffe3SKacheong Poon /*
859721fffe3SKacheong Poon * socket option SO_REUSEADDR is set on the
860721fffe3SKacheong Poon * binding tcp_t.
861721fffe3SKacheong Poon *
862721fffe3SKacheong Poon * If two streams are bound to
863721fffe3SKacheong Poon * same IP address or both addr
864721fffe3SKacheong Poon * and bound source are wildcards
865721fffe3SKacheong Poon * (INADDR_ANY), we want to stop
866721fffe3SKacheong Poon * searching.
867721fffe3SKacheong Poon * We have found a match of IP source
868721fffe3SKacheong Poon * address and source port, which is
869721fffe3SKacheong Poon * refused regardless of the
870721fffe3SKacheong Poon * SO_REUSEADDR setting, so we break.
871721fffe3SKacheong Poon */
872721fffe3SKacheong Poon if (IN6_ARE_ADDR_EQUAL(laddr,
873721fffe3SKacheong Poon &lconnp->conn_bound_addr_v6) &&
874721fffe3SKacheong Poon (ltcp->tcp_state == TCPS_LISTEN ||
875721fffe3SKacheong Poon ltcp->tcp_state == TCPS_BOUND))
876721fffe3SKacheong Poon break;
877721fffe3SKacheong Poon }
878721fffe3SKacheong Poon }
879721fffe3SKacheong Poon if (ltcp != NULL) {
880721fffe3SKacheong Poon /* The port number is busy */
881721fffe3SKacheong Poon mutex_exit(&tbf->tf_lock);
882721fffe3SKacheong Poon } else {
883721fffe3SKacheong Poon /*
884721fffe3SKacheong Poon * This port is ours. Insert in fanout and mark as
885721fffe3SKacheong Poon * bound to prevent others from getting the port
886721fffe3SKacheong Poon * number.
887721fffe3SKacheong Poon */
888721fffe3SKacheong Poon tcp->tcp_state = TCPS_BOUND;
8899cd928feSAlan Maguire DTRACE_TCP6(state__change, void, NULL,
8909cd928feSAlan Maguire ip_xmit_attr_t *, connp->conn_ixa,
8919cd928feSAlan Maguire void, NULL, tcp_t *, tcp, void, NULL,
8929cd928feSAlan Maguire int32_t, TCPS_IDLE);
8939cd928feSAlan Maguire
894721fffe3SKacheong Poon connp->conn_lport = htons(port);
895721fffe3SKacheong Poon
896721fffe3SKacheong Poon ASSERT(&tcps->tcps_bind_fanout[TCP_BIND_HASH(
897721fffe3SKacheong Poon connp->conn_lport)] == tbf);
898721fffe3SKacheong Poon tcp_bind_hash_insert(tbf, tcp, 1);
899721fffe3SKacheong Poon
900721fffe3SKacheong Poon mutex_exit(&tbf->tf_lock);
901721fffe3SKacheong Poon
902721fffe3SKacheong Poon /*
903721fffe3SKacheong Poon * We don't want tcp_next_port_to_try to "inherit"
904721fffe3SKacheong Poon * a port number supplied by the user in a bind.
905721fffe3SKacheong Poon */
906721fffe3SKacheong Poon if (user_specified)
907721fffe3SKacheong Poon return (port);
908721fffe3SKacheong Poon
909721fffe3SKacheong Poon /*
910721fffe3SKacheong Poon * This is the only place where tcp_next_port_to_try
911721fffe3SKacheong Poon * is updated. After the update, it may or may not
912721fffe3SKacheong Poon * be in the valid range.
913721fffe3SKacheong Poon */
914721fffe3SKacheong Poon if (!connp->conn_anon_priv_bind)
915721fffe3SKacheong Poon tcps->tcps_next_port_to_try = port + 1;
916721fffe3SKacheong Poon return (port);
917721fffe3SKacheong Poon }
918721fffe3SKacheong Poon
919721fffe3SKacheong Poon if (connp->conn_anon_priv_bind) {
920721fffe3SKacheong Poon port = tcp_get_next_priv_port(tcp);
921721fffe3SKacheong Poon } else {
922721fffe3SKacheong Poon if (count == 0 && user_specified) {
923721fffe3SKacheong Poon /*
924721fffe3SKacheong Poon * We may have to return an anonymous port. So
925721fffe3SKacheong Poon * get one to start with.
926721fffe3SKacheong Poon */
927721fffe3SKacheong Poon port =
928721fffe3SKacheong Poon tcp_update_next_port(
929721fffe3SKacheong Poon tcps->tcps_next_port_to_try,
930721fffe3SKacheong Poon tcp, B_TRUE);
931721fffe3SKacheong Poon user_specified = B_FALSE;
932721fffe3SKacheong Poon } else {
933721fffe3SKacheong Poon port = tcp_update_next_port(port + 1, tcp,
934721fffe3SKacheong Poon B_FALSE);
935721fffe3SKacheong Poon }
936721fffe3SKacheong Poon }
937721fffe3SKacheong Poon if (port == 0)
938721fffe3SKacheong Poon break;
939721fffe3SKacheong Poon
940721fffe3SKacheong Poon /*
941721fffe3SKacheong Poon * Don't let this loop run forever in the case where
942721fffe3SKacheong Poon * all of the anonymous ports are in use.
943721fffe3SKacheong Poon */
944721fffe3SKacheong Poon } while (++count < loopmax);
945721fffe3SKacheong Poon return (0);
946721fffe3SKacheong Poon }
947