sctp_addr.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <inet/ipclassifier.h>
#include "sctp_impl.h"
#include "sctp_addr.h"
static void sctp_ipif_inactive(sctp_ipif_t *);
static int sctp_get_all_ipifs(sctp_t *, int);
static int sctp_compare_ipif_list(sctp_ipif_hash_t *,
sctp_ipif_hash_t *);
void sctp_free_saddrs(sctp_t *);
void sctp_update_ill(ill_t *, int);
void sctp_update_ipif(ipif_t *, int);
void sctp_del_saddr_list(sctp_t *, const void *, int,
int sctp_getmyaddrs(void *, void *, int *);
void sctp_saddr_init();
void sctp_saddr_fini();
#define SCTP_IPIF_USABLE(sctp_ipif_state) \
((sctp_ipif_state) == SCTP_IPIFS_UP || \
((sctp_ipif_state) == SCTP_IPIFS_DOWN))
/* Global list of SCTP ILLs */
uint32_t sctp_ills_count = 0;
/* Global list of SCTP IPIFs */
/*
*
*
* 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.
* Called with no locks held.
*/
static void
{
sctp_ipif->sctp_ipif_refcnt != 0) {
return;
}
if (rw_tryupgrade(&sctp_g_ills_lock) != 0) {
if (sctp_ill->sctp_ill_ipifcnt == 0 &&
(void *)sctp_ill);
}
}
}
/*
* Lookup an SCTP IPIF given an IP address. Increments sctp_ipif refcnt.
* Called with no locks held.
*/
static sctp_ipif_t *
{
int i;
int j;
for (i = 0; i < SCTP_IPIF_HASH; i++) {
if (sctp_g_ipifs[i].ipif_count == 0)
continue;
for (j = 0; j < sctp_g_ipifs[i].ipif_count; j++) {
addr)) {
if (refhold)
return (sctp_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++) {
if (sctp_g_ipifs[i].ipif_count == 0)
continue;
for (j = 0; j < sctp_g_ipifs[i].ipif_count; j++) {
&sctp_ipif->sctp_ipif_saddr)) ||
&sctp_ipif->sctp_ipif_saddr))) {
continue;
}
if (error != 0)
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.
* Called with no locks held.
*/
int
{
struct sockaddr_in *sin4;
struct sockaddr_in6 *sin6;
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 (sctp->sctp_family) {
case AF_INET:
goto free_ret;
}
if (check_addrs &&
goto free_ret;
}
}
break;
case AF_INET6:
goto free_ret;
}
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) {
sctp->sctp_zoneid);
/* Address not in the list */
goto free_ret;
} else if (check_addrs &&
PHYI_LOOPBACK)) {
goto free_ret;
}
}
if (!bind_to_all) {
if (err != 0) {
err = EADDRINUSE;
goto free_ret;
}
saddr_cnt++;
}
}
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);
}
{
int cnt;
return (NULL);
return (ipif_obj);
ipif_obj);
}
return (NULL);
}
static int
{
int cnt;
return (EALREADY);
ipif_obj);
}
/* Need to do something */
return (ENOMEM);
}
sctp->sctp_nsaddrs++;
return (0);
}
static void
{
int cnt;
ipif_obj);
sctp->sctp_nsaddrs--;
break;
}
ipif_obj);
}
}
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++) {
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--;
}
}
}
/*
* 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? */
ip1dbg(("sctp_ill_insert: mem error..\n"));
return;
}
ip1dbg(("sctp_ill_insert: mem error..\n"));
return;
}
(void *)sctp_ill);
break;
case SCTP_ILL_REMOVE:
return;
}
if (sctp_ill->sctp_ill_ipifcnt == 0) {
(void *)sctp_ill);
} else {
}
break;
}
}
/* 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;
}
}
/* Insert, Remove, Mark up or Mark down the ipif */
void
{
int i;
break;
sctp_ill);
}
return;
}
break;
}
return;
}
#ifdef DEBUG
#endif
switch (op) {
case SCTP_IPIF_INSERT:
sctp_ipif->sctp_ipif_state = 0;
return;
}
/* Try again? */
ip1dbg(("sctp_ipif_insert: mem failure..\n"));
return;
}
(void *)sctp_ipif);
break;
case SCTP_IPIF_REMOVE:
{
if (sctp_ipif->sctp_ipif_refcnt != 0) {
return;
}
if (rw_tryupgrade(&sctp_g_ills_lock) != 0) {
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
{
if (sctp->sctp_bound_to_all)
sctp->sctp_bound_to_all = 0;
}
/*
* Delete source address from the existing list. No error checking done here
* Called with no locks held.
*/
void
{
struct sockaddr_in *sin4;
struct sockaddr_in6 *sin6;
int cnt;
if (!fanout_locked) {
}
switch (sctp->sctp_family) {
case AF_INET:
break;
case AF_INET6:
break;
}
sctp->sctp_zoneid);
}
if (sctp->sctp_bound_to_all)
sctp->sctp_bound_to_all = 0;
if (!fanout_locked) {
}
}
/*
* Given an address get the corresponding entry from the list
* Called with no locks held.
*/
{
return (NULL);
return (saddr_ipifs);
}
/* 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 (!obj->saddr_ipif_dontsrc &&
return (ipif->sctp_ipif_saddr);
}
scanned++;
goto got_none;
obj);
}
}
/* Need to double check this */
else
IN6_IPADDR_TO_V4MAPPED(0, &addr);
return (addr);
}
/* Given a list, get the combined lengths. Called with no locks held. */
{
int i;
int l;
int scanned = 0;
for (i = 0; i < SCTP_IPIF_HASH; i++) {
continue;
scanned++;
return (paramlen);
continue;
}
if (IN6_IS_ADDR_V4MAPPED(&addr)) {
/*
* Only send v4 address if the other side
* supports it.
*/
if (af & PARM_SUPP_V4)
} else if (!IN6_IS_ADDR_LINKLOCAL(&addr) &&
(af & PARM_SUPP_V6)) {
}
return (paramlen);
obj);
}
}
return (paramlen);
}
/*
* 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;
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin4;
int scanned = 0;
if (sctp->sctp_nsaddrs == 0)
return (EINVAL);
/* Skip loopback addresses for non-loopback assoc. */
skip_lback = B_TRUE;
for (i = 0; i < SCTP_IPIF_HASH; i++) {
continue;
scanned++;
skip_lback)) {
goto done;
continue;
}
switch (family) {
case AF_INET:
break;
case AF_INET6:
break;
}
added++;
goto done;
obj);
}
}
done:
return (0);
}
/*
* Given a list construct the list of addresses in p. Called with no locks
* held
*/
{
int i;
int l;
int scanned = 0;
for (i = 0; i < SCTP_IPIF_HASH; i++) {
continue;
scanned++;
return (added);
continue;
}
if (IN6_IS_ADDR_V4MAPPED(&addr)) {
/* The other side does not support v4 */
if (!(supp_af & PARM_SUPP_V4))
continue;
added += PARM_ADDR4_LEN;
} else if (!IN6_IS_ADDR_LINKLOCAL(&addr) &&
(supp_af & PARM_SUPP_V6)) {
added += PARM_ADDR6_LEN;
}
return (added);
obj);
}
}
return (added);
}
/* Initialize the SCTP ILL list and lock */
void
{
int i;
for (i = 0; i < SCTP_ILL_HASH; i++) {
sctp_g_ills[i].ill_count = 0;
}
for (i = 0; i < SCTP_IPIF_HASH; i++) {
sctp_g_ipifs[i].ipif_count = 0;
}
}
void
{
int i;
for (i = 0; i < SCTP_ILL_HASH; i++)
for (i = 0; i < SCTP_IPIF_HASH; i++)
}