sctp_bind.c revision df19b344c6aa7055f3422f05ec857a30a0a1fbd1
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#define _SUN_TPI_VERSION 2
#include <inet/ipclassifier.h>
#include "sctp_impl.h"
#include "sctp_asconf.h"
#include "sctp_addr.h"
/*
* Returns 0 on success, EACCES on permission failure.
*/
static int
{
/*
* Get a valid port (within the anonymous range and should not
* be a privileged one) to use if the user has not given a port.
* If multiple threads are here, they may all start with
* with the same initial port. But, it should be fine as long as
* sctp_bindi will ensure that no two threads will be assigned
* the same port.
*/
if (*requested_port == 0) {
*user_specified = 0;
} else {
int i;
/*
* If the requested_port is in the well-known privileged range,
* verify that the stream was opened by a privileged user.
* Note: No locks are held when inspecting sctp_g_*epriv_ports
* but instead the code relies on:
* - the fact that the address of the array and its size never
* changes
* - the atomic assignment of the elements of the array
*/
if (*requested_port < sctp_smallest_nonpriv_port) {
} else {
for (i = 0; i < sctp_g_num_epriv_ports; i++) {
if (*requested_port == sctp_g_epriv_ports[i]) {
break;
}
}
}
if (priv) {
/*
* sctp_bind() should take a cred_t argument so that
* we can use it here.
*/
*requested_port) != 0) {
dprint(1,
("sctp_bind(x): no prive for port %d",
*requested_port));
return (TACCES);
}
}
*user_specified = 1;
}
return (0);
}
int
{
/*
* TCP handles listen() increasing the backlog, need to check
* if it should be handled here too - VENU.
*/
return (EINVAL);
}
/* Do an anonymous bind for unbound socket doing listen(). */
if (sctp->sctp_nsaddrs == 0) {
struct sockaddr_storage ss;
int ret;
sizeof (ss))) != 0)
return (ret);
}
return (0);
}
/*
* Bind the sctp_t to a sockaddr, which includes an address and other
* information, such as port or flowinfo.
*/
int
{
int user_specified;
int err = 0;
goto done;
}
case AF_INET:
if (len < sizeof (struct sockaddr_in) ||
goto done;
}
break;
case AF_INET6:
if (len < sizeof (struct sockaddr_in6) ||
goto done;
}
/* Set the flowinfo. */
break;
default:
err = EAFNOSUPPORT;
goto done;
}
goto done;
}
goto done;
if (allocated_port == 0) {
if (bind_to_req_port_only) {
err = EADDRINUSE;
goto done;
} else {
err = EADDRNOTAVAIL;
goto done;
}
}
done:
return (err);
}
/*
*/
int
{
switch (bindop) {
case SCTP_BINDX_ADD_ADDR:
case SCTP_BINDX_REM_ADDR:
default:
return (EINVAL);
}
}
/*
* Add a list of addresses to a sctp_t.
*/
int
{
int err = 0;
if (!caller_hold_lock)
if (!caller_hold_lock)
return (EINVAL);
}
/*
* Let's do some checking here rather than undoing the
* add later (for these reasons).
*/
if (!caller_hold_lock)
return (EINVAL);
}
}
if (err != 0) {
if (!caller_hold_lock)
return (err);
}
/* Need to send ASCONF messages */
if (do_asconf) {
if (err != 0) {
if (!caller_hold_lock)
return (err);
}
}
if (!caller_hold_lock)
if (do_asconf)
return (0);
}
/*
* Remove one or more addresses bound to the sctp_t.
*/
int
{
int error = 0;
if (!caller_hold_lock)
if (!caller_hold_lock)
return (EINVAL);
}
/*
* Fail the remove if we are beyond listen, but can't send this
* to the peer.
*/
if (!caller_hold_lock)
return (EINVAL);
}
}
/* Can't delete the last address nor all of the addresses */
if (!caller_hold_lock)
return (EINVAL);
}
if (!caller_hold_lock)
return (error);
}
/*
* If the "bind_to_req_port_only" parameter is set, if the requested port
* number is available, return it, If not return 0
*
* If "bind_to_req_port_only" parameter is not set and
* If the requested port number is available, return it. If not, return
* the first anonymous port we happen across. If no anonymous ports are
* available, return 0. addr is the requested local address, if any.
*
* In either case, when succeeding update the sctp_t to record the port number
* and insert it in the bind hash table.
*/
int user_specified)
{
/* number of times we have run around the loop */
int count = 0;
/* maximum number of times to run around the loop */
int loopmax;
/*
* Lookup for free addresses is done in a loop and "loopmax"
* influences how long we spin in the loop
*/
if (bind_to_req_port_only) {
/*
* If the requested port is busy, don't bother to look
* for a new one. Setting loop maximum count to 1 has
* that effect.
*/
loopmax = 1;
} else {
/*
* If the requested port is busy, look for a free one
* in the anonymous port range.
* Set loopmax appropriately so that one does not look
* forever in the case all of the anonymous ports are in use.
*/
sctp_smallest_anon_port + 1);
}
do {
int addrcmp;
/*
* Ensure that the sctp_t is not currently in the bind hash.
* Hold the lock on the hash bucket to ensure that
* the duplicate check plus the insertion is an atomic
* operation.
*
* This function does an inline lookup on the bind hash list
* Make sure that we access only members of sctp_t
* and that we don't look at sctp_sctp, since we are not
* doing a SCTPB_REFHOLD. For more details please see the notes
* in sctp_compress()
*/
continue;
if (addrcmp != SCTP_ADDR_DISJOINT) {
if (!sctp->sctp_reuseaddr) {
/* in use */
break;
/*
* socket option SO_REUSEADDR is set
* on the binding sctp_t.
*
* We have found a match of IP source
* address and source port, which is
* refused regardless of the
* SO_REUSEADDR setting, so we break.
*/
break;
}
}
}
/* The port number is busy */
} else {
/*
* This port is ours. Insert in fanout and mark as
* bound to prevent others from getting the port
* number.
*/
/*
* We don't want sctp_next_port_to_try to "inherit"
* a port number supplied by the user in a bind.
*/
if (user_specified != 0)
return (port);
/*
* This is the only place where sctp_next_port_to_try
* is updated. After the update, it may or may not
* be in the valid range.
*/
return (port);
}
if ((count == 0) && (user_specified)) {
/*
* We may have to return an anonymous port. So
* get one to start with.
*/
user_specified = 0;
} else {
}
/*
* Don't let this loop run forever in the case where
* all of the anonymous ports are in use.
*/
return (0);
}
/*
* Don't let port fall into the privileged range.
* Since the extra privileged ports can be arbitrary we also
* ensure that we exclude those from consideration.
* sctp_g_epriv_ports is not sorted thus we loop over it until
* there are no changes.
*
* Note: No locks are held when inspecting sctp_g_*epriv_ports
* but instead the code relies on:
* - the fact that the address of the array and its size never changes
* - the atomic assignment of the elements of the array
*/
{
int i;
if (port < sctp_smallest_nonpriv_port)
for (i = 0; i < sctp_g_num_epriv_ports; i++) {
if (port == sctp_g_epriv_ports[i]) {
port++;
/*
* Make sure whether the port is in the
* valid range.
*
* XXX Note that if sctp_g_epriv_ports contains
* all the anonymous ports this will be an
* infinite loop.
*/
goto retry;
}
}
return (port);
}