/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
*/
/*
*/
#include <sys/sysmacros.h>
#include <inet/ipclassifier.h>
#include "sctp_impl.h"
#include "sctp_addr.h"
static void sctp_ipif_inactive(sctp_ipif_t *);
sctp_stack_t *);
static int sctp_get_all_ipifs(sctp_t *, int);
static int sctp_compare_ipif_list(sctp_ipif_hash_t *,
sctp_ipif_hash_t *);
(SCTP_IPIF_HASH - 1))
(SCTP_IPIF_HASH - 1))
((sctp_ipif_state) == SCTP_IPIFS_UP || \
((ipif)->sctp_ipif_isv6 && \
/*
* SCTP Interface list manipulation functions, locking used.
*/
/*
* Delete an SCTP IPIF from the list if the refcount goes to 0 and it is
* marked as condemned. Also, check if the ILL needs to go away.
*/
static void
{
sctp_ipif->sctp_ipif_refcnt != 0) {
return;
}
if (sctp_ill->sctp_ill_ipifcnt == 0 &&
sctp_ill_list, (void *)sctp_ill);
}
}
}
/*
* Lookup an SCTP IPIF given an IP address. Increments sctp_ipif refcnt.
* We are either looking for a IPIF with the given address before
* inserting it into the global list or looking for an IPIF for an
* address given an SCTP. In the former case we always check the zoneid,
* but for the latter case, check_zid could be B_FALSE if the connp
* for the sctp has conn_all_zones set. When looking for an address we
* give preference to one that is up, so even though we may find one that
* is not up we keep looking if there is one up, we hold the down addr
* in backup_ipif in case we don't find one that is up - i.e. we return
* the backup_ipif in that case. Note that if we are looking for. If we
* are specifically looking for an up address, then usable will be set
* to true.
*/
static sctp_ipif_t *
{
int j;
int hindex;
return (NULL);
}
if ((!check_zid ||
addr)))) {
if (refhold)
return (sctp_ipif);
} else if (sctp_ipif->sctp_ipif_state ==
}
}
}
if (backup_ipif != NULL) {
if (refhold)
return (backup_ipif);
}
return (NULL);
}
/*
* Populate the list with all the SCTP ipifs for a given ipversion.
* Increments sctp_ipif refcnt.
* Called with no locks held.
*/
static int
{
int i;
int j;
int error = 0;
for (i = 0; i < SCTP_IPIF_HASH; i++) {
continue;
sctp_ipif->sctp_ipif_saddr) ||
continue;
}
goto free_stuff;
}
}
return (0);
return (ENOMEM);
}
/*
* Given a list of address, fills in the list of SCTP ipifs if all the addresses
* are present in the SCTP interface list, return number of addresses filled
* or error. If the caller wants the list of addresses, it sends a pre-allocated
* buffer - list. Currently, this list is only used on a clustered node when
* the SCTP is in the listen state (from sctp_bind_add()). When called on a
* clustered node, the input is always a list of addresses (even if the
* original bind() was to INADDR_ANY).
* Called with no locks held.
*/
int
{
int cnt;
int err = 0;
int saddr_cnt = 0;
/*
* Need to check for port and address depending on the state.
* After a socket is bound, we need to make sure that subsequent
* bindx() has correct port. After an association is established,
* we need to check for changing the bound address to invalid
* addresses.
*/
}
switch (connp->conn_family) {
case AF_INET:
goto free_ret;
}
if (check_addrs &&
goto free_ret;
}
}
break;
case AF_INET6:
goto free_ret;
}
/* Contains the interface index */
if (connp->conn_ipv6_v6only &&
IN6_IS_ADDR_V4MAPPED(&addr)) {
err = EAFNOSUPPORT;
goto free_ret;
}
if (check_addrs &&
(IN6_IS_ADDR_LINKLOCAL(&addr) ||
IN6_IS_ADDR_UNSPECIFIED(&addr))) {
goto free_ret;
}
}
break;
default:
err = EAFNOSUPPORT;
goto free_ret;
}
if (lookup_saddr) {
/* Address not in the list */
goto free_ret;
cl_sctp_check_addrs == NULL) {
goto free_ret;
}
}
if (!bind_to_all) {
/*
* If an address is added after association setup,
* we need to wait for the peer to send us an ASCONF
* ACK before we can start using it.
* saddr_ipif_dontsrc will be reset (to 0) when we
* get the ASCONF ACK for this address.
*/
if (err != 0) {
err = EADDRINUSE;
goto free_ret;
}
saddr_cnt++;
p += sizeof (addr);
}
}
}
if (bind_to_all) {
/*
* Free whatever we might have added before encountering
* inaddr_any.
*/
if (sctp->sctp_nsaddrs > 0) {
}
if (err != 0)
return (err);
}
return (0);
if (saddr_cnt != 0)
return (err);
}
static int
{
int cnt;
int hindex;
&ipif->sctp_ipif_saddr)) {
if (ipif->sctp_ipif_id !=
return (0);
return (EALREADY);
}
}
ipif_obj);
}
/* Need to do something */
return (ENOMEM);
}
sctp->sctp_nsaddrs++;
return (0);
}
/*
* Given a source address, walk through the peer address list to see
* if the source address is being used. If it is, reset that.
* A cleared saddr will then make sctp_make_mp lookup the destination again
* and as part of that look for a new source.
*/
static void
{
continue;
}
}
static void
{
int cnt;
int hindex;
if (!locked)
&ipif->sctp_ipif_saddr)) {
ipif_obj);
sctp->sctp_nsaddrs--;
break;
}
ipif_obj);
}
if (!locked)
}
static int
{
int i;
int j;
int overlap = 0;
for (i = 0; i < list1->ipif_count; i++) {
for (j = 0; j < list2->ipif_count; j++) {
if (IN6_ARE_ADDR_EQUAL(
overlap++;
break;
}
obj2);
}
}
return (overlap);
}
int
{
int i;
int overlap = 0;
for (i = 0; i < SCTP_IPIF_HASH; i++) {
&sctp2->sctp_saddrs[i]);
}
return (SCTP_ADDR_EQUAL);
}
return (SCTP_ADDR_SUBSET);
if (overlap > 0)
return (SCTP_ADDR_OVERLAP);
return (SCTP_ADDR_DISJOINT);
}
static int
{
int i;
int error = 0;
for (i = 0; i < list1->ipif_count; i++) {
if (error != 0) {
return (error);
}
}
return (error);
}
int
{
int error = 0;
int i;
for (i = 0; i < SCTP_IPIF_HASH; i++) {
continue;
}
if (error != 0) {
return (error);
}
}
return (0);
}
void
{
int i;
int l;
if (sctp->sctp_nsaddrs == 0)
return;
for (i = 0; i < SCTP_IPIF_HASH; i++) {
continue;
}
sctp->sctp_nsaddrs--;
}
}
sctp->sctp_bound_to_all = 0;
}
/*
* held.
*/
void
{
int i;
break;
}
sctp_ill);
}
switch (op) {
case SCTP_ILL_INSERT:
/* Unmark it if it is condemned */
sctp_ill->sctp_ill_state = 0;
return;
}
/* Need to re-try? */
"ILL %p to SCTP's ILL list", (void *)ill);
return;
}
"ILL %p to SCTP's ILL list", (void *)ill);
return;
}
(void *)sctp_ill);
break;
case SCTP_ILL_REMOVE:
return;
}
if (sctp_ill->sctp_ill_ipifcnt == 0) {
(void *)sctp_ill);
} else {
}
break;
}
}
/*
* The ILL's index is being changed, just remove it from the old list,
* change the SCTP ILL's index and re-insert using the new index.
*/
void
{
sctp_ill);
/*
* if the new index hashes to the same value, all's
* done.
*/
(void *)sctp_ill);
(void *)sctp_ill);
}
if (once)
break;
/* We might have one for v4 and for v6 */
}
}
}
/* move ipif from f_ill to t_ill */
void
{
int i;
break;
}
}
break;
}
}
break;
}
/* Should be an ASSERT? */
ip1dbg(("sctp_move_ipif: error moving ipif %p from %p to %p\n",
return;
}
}
/*
* Walk the list of SCTPs and find each that has oipif in it's saddr list, and
* if so replace it with nipif.
*/
void
{
int count;
if (sctp->sctp_condemned ||
continue;
}
sctp->sctp_refcnt++;
/* We have the writer lock */
/*
* Can't have more than one referring
* to the same sctp_ipif.
*/
break;
}
sobj);
}
}
}
/*
* Given an ipif, walk the hash list in the global ipif table and for
* any other SCTP ipif with the same address and non-zero reference, walk
* the SCTP list and update the saddr list, if required, to point to the
* new SCTP ipif. If it is a loopback interface, then there could be
* multiple interfaces with 127.0.0.1 if there are zones configured, so
* check the zoneid in addition to the address.
*/
void
{
int cnt;
sipif->sctp_ipif_zoneid)) {
/*
* There can only be one address up at any time
* and we are here because ipif has been brought
* up.
*/
/*
* Someone has a reference to this we need to update to
* point to the new sipif.
*/
}
sipif);
}
}
/*
* Insert a new SCTP ipif using 'ipif'. v6addr is the address that existed
* prior to the current address in 'ipif'. Only when an existing address
* is changed on an IPIF, will v6addr be specified. If the IPIF already
* exists in the global SCTP ipif table, then we either removed it, if
* it doesn't have any existing reference, or mark it condemned otherwise.
* If an address is being brought up (IPIF_UP), then we need to scan
* the SCTP list to check if there is any SCTP that points to the *same*
* address on a different SCTP ipif and update in that case.
*/
void
{
int i;
int hindex;
/* Index for new address */
/*
* The address on this IPIF is changing, we need to look for
* this old address and mark it condemned, before creating
* one for the new address.
*/
break;
}
}
ip1dbg(("sctp_update_ipif_addr: ill not found ..\n"));
return;
}
if (osctp_ipif != NULL) {
/* The address is the same? */
chk_n_updt = B_TRUE;
} else {
}
if (chk_n_updt) {
sctps);
}
return;
}
/*
* We are effectively removing this address from the ILL.
*/
if (osctp_ipif->sctp_ipif_refcnt != 0) {
} else {
int ohindex;
/* hash index for the old one */
}
}
/* Try again? */
"IPIF %p to SCTP's IPIF list", (void *)ipif);
return;
}
else
/*
* additions.
*/
(void *)sctp_ipif);
}
/* Insert, Remove, Mark up or Mark down the ipif */
void
{
int i;
break;
}
}
return;
}
&ipif->ipif_v6lcl_addr));
break;
}
}
return;
}
switch (op) {
case SCTP_IPIF_REMOVE:
{
if (sctp_ipif->sctp_ipif_refcnt != 0) {
return;
}
if (sctp_ill->sctp_ill_ipifcnt == 0 &&
}
}
break;
}
case SCTP_IPIF_UP:
break;
case SCTP_IPIF_UPDATE:
break;
case SCTP_IPIF_DOWN:
break;
}
}
/*
* SCTP source address list manipulaton, locking not used (except for
* sctp locking by the caller.
*/
/* Remove a specific saddr from the list */
void
{
sctp->sctp_bound_to_all = 0;
}
/*
* Delete source address from the existing list. No error checking done here
* Called with no locks held.
*/
void
{
int cnt;
int ifindex = 0;
if (!fanout_locked) {
}
switch (connp->conn_family) {
case AF_INET:
break;
case AF_INET6:
break;
}
}
sctp->sctp_bound_to_all = 0;
if (!fanout_locked) {
}
}
/*
* Given an address get the corresponding entry from the list
* Called with no locks held.
*/
{
int cnt;
int hindex;
return (NULL);
}
/*
* Zone check shouldn't be needed.
*/
(ifindex == 0 ||
return (ipif_obj);
}
ipif_obj);
}
return (NULL);
}
/* Given an address, add it to the source address list */
int
{
return (EINVAL);
B_FALSE) != 0) {
return (EINVAL);
}
return (0);
}
/*
* Remove or mark as dontsrc addresses that are currently not part of the
* association. One would delete addresses when processing an INIT and
* mark as dontsrc when processing an INIT-ACK.
*/
void
{
int i;
int l;
int scanned = 0;
int naddr;
int nsaddr;
/*
* Irregardless of the supported address in the INIT, v4
* must be supported.
*/
for (i = 0; i < SCTP_IPIF_HASH; i++) {
continue;
}
for (l = 0; l < naddr; l++) {
scanned++;
no_del_addr)) {
goto next_obj;
}
/*
* unsupported address.
* On a clustered node, we trust the clustering module
* to do the right thing w.r.t loopback addresses, so
* we ignore loopback addresses in this check.
*/
if ((SCTP_IS_IPIF_LOOPBACK(ipif) &&
cl_sctp_check_addrs == NULL) ||
if (!delete) {
goto next_obj;
}
sctp->sctp_bound_to_all = 0;
B_TRUE);
continue;
}
}
return;
}
obj);
}
}
}
/* Get the first valid address from the list. Called with no locks held */
{
int i;
int l;
int scanned = 0;
for (i = 0; i < SCTP_IPIF_HASH; i++) {
continue;
}
if (!SCTP_DONT_SRC(obj) &&
return (ipif->sctp_ipif_saddr);
}
scanned++;
goto got_none;
}
obj);
}
}
/* Need to double check this */
else
IN6_IPADDR_TO_V4MAPPED(0, &addr);
return (addr);
}
/*
* Return the list of local addresses of an association. The parameter
* myaddrs is supposed to be either (struct sockaddr_in *) or (struct
* sockaddr_in6 *) depending on the address family.
*/
int
{
int i;
int l;
int scanned = 0;
if (sctp->sctp_nsaddrs == 0)
return (EINVAL);
/*
* Skip loopback addresses for non-loopback assoc., ignore
* this on a clustered node.
*/
(cl_sctp_check_addrs == NULL)) {
skip_lback = B_TRUE;
}
for (i = 0; i < SCTP_IPIF_HASH; i++) {
continue;
}
scanned++;
SCTP_DONT_SRC(obj) ||
goto done;
}
continue;
}
switch (family) {
case AF_INET:
break;
case AF_INET6:
/*
* Note that flowinfo is only returned for
* getpeername just like for TCP and UDP.
*/
sin6->sin6_flowinfo = 0;
else
sin6->sin6_scope_id = 0;
sin6->__sin6_src_id = 0;
break;
}
added++;
goto done;
}
obj);
}
}
done:
return (0);
}
/*
* Given the supported address family, walk through the source address list
* and return the total length of the available addresses. If 'p' is not
* null, construct the parameter list for the addresses in 'p'.
* 'modify' will only be set when we want the source address list to
* be modified. The source address list will be modified only when
* generating an INIT chunk. For generating an INIT-ACK 'modify' will
* be false since the 'sctp' will be that of the listener.
*/
{
int i;
int l;
int scanned = 0;
int naddr;
int nsaddr;
/*
* On a clustered node don't bother changing anything
* on the loopback interface.
*/
for (i = 0; i < SCTP_IPIF_HASH; i++) {
continue;
}
for (l = 0; l < naddr; l++) {
scanned++;
/*
* or unsupported addresses, if required.
*/
sctp->sctp_bound_to_all = 0;
B_TRUE);
continue;
}
goto next_addr;
goto next_addr;
}
goto next_addr;
if (p != NULL)
if (!ipif->sctp_ipif_isv6) {
if (p != NULL) {
}
} else {
if (p != NULL) {
}
}
return (paramlen);
}
obj);
}
}
return (paramlen);
}
/*
* This is used on a clustered node to obtain a list of addresses, the list
* consists of sockaddr_in structs for v4 and sockaddr_in6 for v6. The list
* is then passed onto the clustering module which sends back the correct
* list based on the port info. Regardless of the input, i.e INADDR_ANY
* or specific address(es), we create the list since it could be modified by
* the clustering module. When given a list of addresses, we simply
* create the list of sockaddr_in or sockaddr_in6 structs using those
* addresses. If there is an INADDR_ANY in the input list, or if the
* input is INADDR_ANY, we create a list of sockaddr_in or sockaddr_in6
* structs consisting all the addresses in the global interface list
* except those that are hosted on the loopback interface. We create
* a list of sockaddr_in[6] structs just so that it can be directly input
* to sctp_valid_addr_list() once the clustering module has processed it.
*/
int
{
int cnt;
int icnt;
uchar_t *p;
int err = 0;
*size = 0;
/*
* Create a list of sockaddr_in[6] structs using the input list.
*/
p = *addrlist;
/*
* We need to create a list of all the available
* addresses if there is an INADDR_ANY. However,
* if we are beyond LISTEN, then this is invalid
* (see sctp_valid_addr_list(). So, we just fail
* it here rather than wait till it fails in
* sctp_valid_addr_list().
*/
*size = 0;
*addrcnt = 0;
return (EINVAL);
}
*uspec = 1;
goto get_all_addrs;
} else {
p += sizeof (*s4);
}
}
} else {
p = *addrlist;
/*
* Comments for INADDR_ANY, above, apply here too.
*/
*size = 0;
*addrcnt = 0;
return (EINVAL);
}
*uspec = 1;
goto get_all_addrs;
} else {
p += sizeof (*s6);
}
}
}
return (err);
/*
* Allocate max possible size. We allocate the max. size here because
* the clustering module could end up adding addresses to the list.
* We allocate upfront so that the clustering module need to bother
* re-sizing the list.
*/
*size = sizeof (struct sockaddr_in) *
} else {
*size = sizeof (struct sockaddr_in6) *
}
*addrcnt = 0;
p = *addrlist;
/*
* Walk through the global interface list and add all addresses,
* except those that are hosted on loopback interfaces.
*/
continue;
for (icnt = 0;
icnt++) {
sctp_ipif->sctp_ipif_isv6) ||
!sctp_ipif->sctp_ipif_isv6)) {
continue;
}
s4 = (struct sockaddr_in *)p;
p += sizeof (*s4);
} else {
s6 = (struct sockaddr_in6 *)p;
s6->sin6_scope_id =
p += sizeof (*s6);
}
(*addrcnt)++;
}
}
return (err);
}
/*
* Get a list of addresses from the source address list. The caller is
* responsible for allocating sufficient buffer for this.
*/
void
{
int cnt;
int icnt;
int naddr;
int scanned = 0;
continue;
}
return;
}
scanned++;
sizeof (ipif->sctp_ipif_saddr));
p += sizeof (ipif->sctp_ipif_saddr);
return;
}
obj);
}
}
}
/*
* Get a list of addresses from the remote address list. The caller is
* responsible for allocating sufficient buffer for this.
*/
void
{
return;
}
}
static void
{
int i;
int l;
if (sctps->sctps_ills_count == 0)
return;
for (i = 0; i < SCTP_ILL_HASH; i++) {
sctp_ill);
sctp_ill =
}
}
}
static void
{
int i;
int l;
if (sctps->sctps_g_ipifs_count == 0)
return;
for (i = 0; i < SCTP_IPIF_HASH; i++) {
}
}
}
/* Initialize the SCTP ILL list and lock */
void
{
int i;
for (i = 0; i < SCTP_ILL_HASH; i++) {
sizeof (sctp_ill_t),
}
for (i = 0; i < SCTP_IPIF_HASH; i++) {
}
}
void
{
int i;
for (i = 0; i < SCTP_ILL_HASH; i++)
for (i = 0; i < SCTP_IPIF_HASH; i++)
}