tcp_bind.c revision 9cd928fe5e3ea4e05f64cfb380beb54b2623e7dc
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * CDDL HEADER START
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * The contents of this file are subject to the terms of the
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * Common Development and Distribution License (the "License").
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * You may not use this file except in compliance with the License.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * or http://www.opensolaris.org/os/licensing.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * See the License for the specific language governing permissions
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * and limitations under the License.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * When distributing Covered Code, include this CDDL HEADER in each
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * If applicable, add the following below this CDDL HEADER, with the
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * fields enclosed by brackets "[]" replaced with your own identifying
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * information: Portions Copyright [yyyy] [name of copyright owner]
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * CDDL HEADER END
4f5dd3943bef8a04be7e3b838b822bb9a7ad6cb3Lennart Poettering * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering/* Setable in /etc/system */
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering/* If set to 0, pick ephemeral port sequentially; otherwise randomly. */
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringstatic int tcp_bind_select_lport(tcp_t *, in_port_t *, boolean_t,
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringstatic in_port_t tcp_get_next_priv_port(const tcp_t *);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * Hash list insertion routine for tcp_t structures. Each hash bucket
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * contains a list of tcp_t entries, and each entry is bound to a unique
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * port. If there are multiple tcp_t's that are bound to the same port, then
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * one of them will be linked into the hash bucket list, and the rest will
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * hang off of that one entry. For each port, entries bound to a specific IP
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * address will be inserted before those those bound to INADDR_ANY.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringtcp_bind_hash_insert(tf_t *tbf, tcp_t *tcp, int caller_holds_lock)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering /* Look for an entry using the same port */
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering connp->conn_lport != tcphash->tcp_connp->conn_lport)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering /* The port was not found, just add to the end */
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * OK, there already exists an entry bound to the
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * If the new tcp bound to the INADDR_ANY address
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * and the first one in the list is not bound to
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * INADDR_ANY we skip all entries until we find the
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * first one bound to INADDR_ANY.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * This makes sure that applications binding to a
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * specific address get preference over those binding to
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (V6_OR_V4_INADDR_ANY(connp->conn_bound_addr_v6) &&
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering !V6_OR_V4_INADDR_ANY(connext->conn_bound_addr_v6)) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering tcpnext->tcp_ptpbhn = &tcp->tcp_bind_hash_port;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering tcpnext->tcp_ptpbhn = &tcp->tcp_bind_hash_port;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * Hash list removal routine for tcp_t structures.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * Extract the lock pointer in case there are concurrent
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * hash_remove's for this instance.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering lockp = &tcps->tcps_bind_fanout[TCP_BIND_HASH(
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering tcpnext->tcp_bind_hash = tcp->tcp_bind_hash;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering } else if ((tcpnext = tcp->tcp_bind_hash) != NULL) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * Don't let port fall into the privileged range.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * Since the extra privileged ports can be arbitrary we also
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * ensure that we exclude those from consideration.
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering * tcp_g_epriv_ports is not sorted thus we loop over it until
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering * there are no changes.
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering * Note: No locks are held when inspecting tcp_g_*epriv_ports
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * but instead the code relies on:
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * - the fact that the address of the array and its size never changes
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * - the atomic assignment of the elements of the array
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * Returns 0 if there are no more ports available.
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering * TS note: skip multilevel ports.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringtcp_update_next_port(in_port_t port, const tcp_t *tcp, boolean_t random)
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering if (random && tcp_random_anon_port != 0) {
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering (void) random_get_pseudo_bytes((uint8_t *)&port,
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering * Unless changed by a sys admin, the smallest anon port
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering * is 32768 and the largest anon port is 65535. It is
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering * very likely (50%) for the random port to be smaller
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * than the smallest anon port. When that happens,
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * add port % (anon port range) to the smallest anon
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering * port to get the random port. It should fall into the
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering * valid anon port range.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (port < tcps->tcps_smallest_anon_port) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (port < tcps->tcps_smallest_anon_port)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering port = (in_port_t)tcps->tcps_smallest_anon_port;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (port > tcps->tcps_largest_anon_port) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering port = (in_port_t)tcps->tcps_smallest_anon_port;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (port < tcps->tcps_smallest_nonpriv_port)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering port = (in_port_t)tcps->tcps_smallest_nonpriv_port;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering for (i = 0; i < tcps->tcps_g_num_epriv_ports; i++) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (port == tcps->tcps_g_epriv_ports[i]) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * Make sure whether the port is in the
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * valid range.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering (i = tsol_next_port(crgetzone(tcp->tcp_connp->conn_cred), port,
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * Return the next anonymous port in the privileged port range for
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * bind checking. It starts at IPPORT_RESERVED - 1 and goes
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * downwards. This is the same behavior as documented in the userland
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * library call rresvport(3N).
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering * TS note: skip multilevel ports.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering static in_port_t next_priv_port = IPPORT_RESERVED - 1;
if (restart)
if (is_system_labeled() &&
goto retry;
return (next_priv_port--);
if (requested_port == 0) {
if (requested_port == 0) {
return (-TNOADDR);
return (-TNOADDR);
if (requested_port ==
if (priv) {
IPPROTO_TCP) != 0) {
return (-TACCES);
if (is_system_labeled()) {
return (-TNOADDR);
return (-TACCES);
mlpzone);
return (-TACCES);
if (!user_specified) {
int err;
if (err != 0) {
return (err);
if (allocated_port == 0) {
if (bind_to_req_port_only) {
return (-TADDRBUSY);
return (-TNOADDR);
int error = 0;
return (-TOUTSTATE);
return (-TPROTO);
if (error != 0) {
return (error);
switch (len) {
B_FALSE);
return (EADDRNOTAVAIL);
return (EAFNOSUPPORT);
return (EADDRNOTAVAIL);
if (scopeid != 0) {
if (error != 0) {
return (error);
int count = 0;
int loopmax;
if (bind_to_req_port_only) {
if (V6_OR_V4_INADDR_ANY(
if (quick_connect &&
if (!reuseaddr) {
if (user_specified)
return (port);
return (port);
port =
B_FALSE);
if (port == 0)