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 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 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 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 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 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 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 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