Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/netwerk/sctp/src/netinet/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 229 kB image not shown  

Quelle  sctp_pcb.c   Sprache: C

 
/*-
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved.
 * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved.
 * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * a) Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * b) Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the distribution.
 *
 * c) Neither the name of Cisco Systems, Inc. nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */


#include <netinet/sctp_os.h>
#if defined(__FreeBSD__) && !defined(__Userspace__)
#include <sys/proc.h>
#endif
#include <netinet/sctp_var.h>
#include <netinet/sctp_sysctl.h>
#include <netinet/sctp_pcb.h>
#include <netinet/sctputil.h>
#include <netinet/sctp.h>
#include <netinet/sctp_header.h>
#include <netinet/sctp_asconf.h>
#include <netinet/sctp_output.h>
#include <netinet/sctp_timer.h>
#include <netinet/sctp_bsd_addr.h>
#if defined(INET) || defined(INET6)
#if !defined(_WIN32)
#include <netinet/udp.h>
#endif
#endif
#ifdef INET6
#if defined(__Userspace__)
#include "user_ip6_var.h"
#else
#include <netinet6/ip6_var.h>
#endif
#endif
#if defined(__FreeBSD__) && !defined(__Userspace__)
#include <sys/sched.h>
#include <sys/smp.h>
#include <sys/unistd.h>
#endif
#if defined(__Userspace__)
#include <user_socketvar.h>
#include <user_atomic.h>
#if !defined(_WIN32)
#include <netdb.h>
#endif
#endif

#if !defined(__FreeBSD__) || defined(__Userspace__)
struct sctp_base_info system_base_info;

#endif
/* FIX: we don't handle multiple link local scopes */
/* "scopeless" replacement IN6_ARE_ADDR_EQUAL */
#ifdef INET6
int
SCTP6_ARE_ADDR_EQUAL(struct sockaddr_in6 *a, struct sockaddr_in6 *b)
{
#ifdef SCTP_EMBEDDED_V6_SCOPE
#if defined(__APPLE__) && !defined(__Userspace__)
 struct in6_addr tmp_a, tmp_b;

 tmp_a = a->sin6_addr;
#if defined(APPLE_LEOPARD) || defined(APPLE_SNOWLEOPARD)
 if (in6_embedscope(&tmp_a, a, NULL, NULL) != 0) {
#else
 if (in6_embedscope(&tmp_a, a, NULL, NULL, NULL) != 0) {
#endif
  return (0);
 }
 tmp_b = b->sin6_addr;
#if defined(APPLE_LEOPARD) || defined(APPLE_SNOWLEOPARD)
 if (in6_embedscope(&tmp_b, b, NULL, NULL) != 0) {
#else
 if (in6_embedscope(&tmp_b, b, NULL, NULL, NULL) != 0) {
#endif
  return (0);
 }
 return (IN6_ARE_ADDR_EQUAL(&tmp_a, &tmp_b));
#elif defined(SCTP_KAME)
 struct sockaddr_in6 tmp_a, tmp_b;

 memcpy(&tmp_a, a, sizeof(struct sockaddr_in6));
 if (sa6_embedscope(&tmp_a, MODULE_GLOBAL(ip6_use_defzone)) != 0) {
  return (0);
 }
 memcpy(&tmp_b, b, sizeof(struct sockaddr_in6));
 if (sa6_embedscope(&tmp_b, MODULE_GLOBAL(ip6_use_defzone)) != 0) {
  return (0);
 }
 return (IN6_ARE_ADDR_EQUAL(&tmp_a.sin6_addr, &tmp_b.sin6_addr));
#else
 struct in6_addr tmp_a, tmp_b;

 tmp_a = a->sin6_addr;
 if (in6_embedscope(&tmp_a, a) != 0) {
  return (0);
 }
 tmp_b = b->sin6_addr;
 if (in6_embedscope(&tmp_b, b) != 0) {
  return (0);
 }
 return (IN6_ARE_ADDR_EQUAL(&tmp_a, &tmp_b));
#endif
#else
 return (IN6_ARE_ADDR_EQUAL(&(a->sin6_addr), &(b->sin6_addr)));
#endif /* SCTP_EMBEDDED_V6_SCOPE */
}
#endif

void
sctp_fill_pcbinfo(struct sctp_pcbinfo *spcb)
{
 /*
 * We really don't need to lock this, but I will just because it
 * does not hurt.
 */

 SCTP_INP_INFO_RLOCK();
 spcb->ep_count = SCTP_BASE_INFO(ipi_count_ep);
 spcb->asoc_count = SCTP_BASE_INFO(ipi_count_asoc);
 spcb->laddr_count = SCTP_BASE_INFO(ipi_count_laddr);
 spcb->raddr_count = SCTP_BASE_INFO(ipi_count_raddr);
 spcb->chk_count = SCTP_BASE_INFO(ipi_count_chunk);
 spcb->readq_count = SCTP_BASE_INFO(ipi_count_readq);
 spcb->stream_oque = SCTP_BASE_INFO(ipi_count_strmoq);
 spcb->free_chunks = SCTP_BASE_INFO(ipi_free_chunks);
 SCTP_INP_INFO_RUNLOCK();
}

/*-
 * Addresses are added to VRF's (Virtual Router's). For BSD we
 * have only the default VRF 0. We maintain a hash list of
 * VRF's. Each VRF has its own list of sctp_ifn's. Each of
 * these has a list of addresses. When we add a new address
 * to a VRF we lookup the ifn/ifn_index, if the ifn does
 * not exist we create it and add it to the list of IFN's
 * within the VRF. Once we have the sctp_ifn, we add the
 * address to the list. So we look something like:
 *
 * hash-vrf-table
 *   vrf-> ifn-> ifn -> ifn
 *   vrf    |
 *    ...   +--ifa-> ifa -> ifa
 *   vrf
 *
 * We keep these separate lists since the SCTP subsystem will
 * point to these from its source address selection nets structure.
 * When an address is deleted it does not happen right away on
 * the SCTP side, it gets scheduled. What we do when a
 * delete happens is immediately remove the address from
 * the master list and decrement the refcount. As our
 * addip iterator works through and frees the src address
 * selection pointing to the sctp_ifa, eventually the refcount
 * will reach 0 and we will delete it. Note that it is assumed
 * that any locking on system level ifn/ifa is done at the
 * caller of these functions and these routines will only
 * lock the SCTP structures as they add or delete things.
 *
 * Other notes on VRF concepts.
 *  - An endpoint can be in multiple VRF's
 *  - An association lives within a VRF and only one VRF.
 *  - Any incoming packet we can deduce the VRF for by
 *    looking at the mbuf/pak inbound (for BSD its VRF=0 :D)
 *  - Any downward send call or connect call must supply the
 *    VRF via ancillary data or via some sort of set default
 *    VRF socket option call (again for BSD no brainer since
 *    the VRF is always 0).
 *  - An endpoint may add multiple VRF's to it.
 *  - Listening sockets can accept associations in any
 *    of the VRF's they are in but the assoc will end up
 *    in only one VRF (gotten from the packet or connect/send).
 *
 */


struct sctp_vrf *
sctp_allocate_vrf(int vrf_id)
{
 struct sctp_vrf *vrf = NULL;
 struct sctp_vrflist *bucket;

 /* First allocate the VRF structure */
 vrf = sctp_find_vrf(vrf_id);
 if (vrf) {
  /* Already allocated */
  return (vrf);
 }
 SCTP_MALLOC(vrf, struct sctp_vrf *, sizeof(struct sctp_vrf),
      SCTP_M_VRF);
 if (vrf == NULL) {
  /* No memory */
#ifdef INVARIANTS
  panic("No memory for VRF:%d", vrf_id);
#endif
  return (NULL);
 }
 /* setup the VRF */
 memset(vrf, 0, sizeof(struct sctp_vrf));
 vrf->vrf_id = vrf_id;
 LIST_INIT(&vrf->ifnlist);
 vrf->total_ifa_count = 0;
 vrf->refcount = 0;
 /* now also setup table ids */
 SCTP_INIT_VRF_TABLEID(vrf);
 /* Init the HASH of addresses */
 vrf->vrf_addr_hash = SCTP_HASH_INIT(SCTP_VRF_ADDR_HASH_SIZE,
         &vrf->vrf_addr_hashmark);
 if (vrf->vrf_addr_hash == NULL) {
  /* No memory */
#ifdef INVARIANTS
  panic("No memory for VRF:%d", vrf_id);
#endif
  SCTP_FREE(vrf, SCTP_M_VRF);
  return (NULL);
 }

 /* Add it to the hash table */
 bucket = &SCTP_BASE_INFO(sctp_vrfhash)[(vrf_id & SCTP_BASE_INFO(hashvrfmark))];
 LIST_INSERT_HEAD(bucket, vrf, next_vrf);
 atomic_add_int(&SCTP_BASE_INFO(ipi_count_vrfs), 1);
 return (vrf);
}

struct sctp_ifn *
sctp_find_ifn(void *ifn, uint32_t ifn_index)
{
 struct sctp_ifn *sctp_ifnp;
 struct sctp_ifnlist *hash_ifn_head;

 /* We assume the lock is held for the addresses
 * if that's wrong problems could occur :-)
 */

 SCTP_IPI_ADDR_LOCK_ASSERT();
 hash_ifn_head = &SCTP_BASE_INFO(vrf_ifn_hash)[(ifn_index & SCTP_BASE_INFO(vrf_ifn_hashmark))];
 LIST_FOREACH(sctp_ifnp, hash_ifn_head, next_bucket) {
  if (sctp_ifnp->ifn_index == ifn_index) {
   return (sctp_ifnp);
  }
  if (sctp_ifnp->ifn_p && ifn && (sctp_ifnp->ifn_p == ifn)) {
   return (sctp_ifnp);
  }
 }
 return (NULL);
}

struct sctp_vrf *
sctp_find_vrf(uint32_t vrf_id)
{
 struct sctp_vrflist *bucket;
 struct sctp_vrf *liste;

 bucket = &SCTP_BASE_INFO(sctp_vrfhash)[(vrf_id & SCTP_BASE_INFO(hashvrfmark))];
 LIST_FOREACH(liste, bucket, next_vrf) {
  if (vrf_id == liste->vrf_id) {
   return (liste);
  }
 }
 return (NULL);
}

void
sctp_free_vrf(struct sctp_vrf *vrf)
{
 if (SCTP_DECREMENT_AND_CHECK_REFCOUNT(&vrf->refcount)) {
                if (vrf->vrf_addr_hash) {
                    SCTP_HASH_FREE(vrf->vrf_addr_hash, vrf->vrf_addr_hashmark);
                    vrf->vrf_addr_hash = NULL;
                }
  /* We zero'd the count */
  LIST_REMOVE(vrf, next_vrf);
  SCTP_FREE(vrf, SCTP_M_VRF);
  atomic_subtract_int(&SCTP_BASE_INFO(ipi_count_vrfs), 1);
 }
}

void
sctp_free_ifn(struct sctp_ifn *sctp_ifnp)
{
 if (SCTP_DECREMENT_AND_CHECK_REFCOUNT(&sctp_ifnp->refcount)) {
  /* We zero'd the count */
  if (sctp_ifnp->vrf) {
   sctp_free_vrf(sctp_ifnp->vrf);
  }
  SCTP_FREE(sctp_ifnp, SCTP_M_IFN);
  atomic_subtract_int(&SCTP_BASE_INFO(ipi_count_ifns), 1);
 }
}

void
sctp_update_ifn_mtu(uint32_t ifn_index, uint32_t mtu)
{
 struct sctp_ifn *sctp_ifnp;

 sctp_ifnp = sctp_find_ifn((void *)NULL, ifn_index);
 if (sctp_ifnp != NULL) {
  sctp_ifnp->ifn_mtu = mtu;
 }
}

void
sctp_free_ifa(struct sctp_ifa *sctp_ifap)
{
 if (SCTP_DECREMENT_AND_CHECK_REFCOUNT(&sctp_ifap->refcount)) {
  /* We zero'd the count */
  if (sctp_ifap->ifn_p) {
   sctp_free_ifn(sctp_ifap->ifn_p);
  }
  SCTP_FREE(sctp_ifap, SCTP_M_IFA);
  atomic_subtract_int(&SCTP_BASE_INFO(ipi_count_ifas), 1);
 }
}

static void
sctp_delete_ifn(struct sctp_ifn *sctp_ifnp, int hold_addr_lock)
{
 struct sctp_ifn *found;

 found = sctp_find_ifn(sctp_ifnp->ifn_p, sctp_ifnp->ifn_index);
 if (found == NULL) {
  /* Not in the list.. sorry */
  return;
 }
 if (hold_addr_lock == 0) {
  SCTP_IPI_ADDR_WLOCK();
 } else {
  SCTP_IPI_ADDR_WLOCK_ASSERT();
 }
 LIST_REMOVE(sctp_ifnp, next_bucket);
 LIST_REMOVE(sctp_ifnp, next_ifn);
 if (hold_addr_lock == 0) {
  SCTP_IPI_ADDR_WUNLOCK();
 }
 /* Take away the reference, and possibly free it */
 sctp_free_ifn(sctp_ifnp);
}

void
sctp_mark_ifa_addr_down(uint32_t vrf_id, struct sockaddr *addr,
   const char *if_name, uint32_t ifn_index)
{
 struct sctp_vrf *vrf;
 struct sctp_ifa *sctp_ifap;

 SCTP_IPI_ADDR_RLOCK();
 vrf = sctp_find_vrf(vrf_id);
 if (vrf == NULL) {
  SCTPDBG(SCTP_DEBUG_PCB4, "Can't find vrf_id 0x%x\n", vrf_id);
  goto out;
 }
 sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED);
 if (sctp_ifap == NULL) {
  SCTPDBG(SCTP_DEBUG_PCB4, "Can't find sctp_ifap for address\n");
  goto out;
 }
 if (sctp_ifap->ifn_p == NULL) {
  SCTPDBG(SCTP_DEBUG_PCB4, "IFA has no IFN - can't mark unusable\n");
  goto out;
 }
 if (if_name) {
  if (strncmp(if_name, sctp_ifap->ifn_p->ifn_name, SCTP_IFNAMSIZ) != 0) {
   SCTPDBG(SCTP_DEBUG_PCB4, "IFN %s of IFA not the same as %s\n",
    sctp_ifap->ifn_p->ifn_name, if_name);
   goto out;
  }
 } else {
  if (sctp_ifap->ifn_p->ifn_index != ifn_index) {
   SCTPDBG(SCTP_DEBUG_PCB4, "IFA owned by ifn_index:%d down command for ifn_index:%d - ignored\n",
    sctp_ifap->ifn_p->ifn_index, ifn_index);
   goto out;
  }
 }

 sctp_ifap->localifa_flags &= (~SCTP_ADDR_VALID);
 sctp_ifap->localifa_flags |= SCTP_ADDR_IFA_UNUSEABLE;
 out:
 SCTP_IPI_ADDR_RUNLOCK();
}

void
sctp_mark_ifa_addr_up(uint32_t vrf_id, struct sockaddr *addr,
        const char *if_name, uint32_t ifn_index)
{
 struct sctp_vrf *vrf;
 struct sctp_ifa *sctp_ifap;

 SCTP_IPI_ADDR_RLOCK();
 vrf = sctp_find_vrf(vrf_id);
 if (vrf == NULL) {
  SCTPDBG(SCTP_DEBUG_PCB4, "Can't find vrf_id 0x%x\n", vrf_id);
  goto out;
 }
 sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED);
 if (sctp_ifap == NULL) {
  SCTPDBG(SCTP_DEBUG_PCB4, "Can't find sctp_ifap for address\n");
  goto out;
 }
 if (sctp_ifap->ifn_p == NULL) {
  SCTPDBG(SCTP_DEBUG_PCB4, "IFA has no IFN - can't mark unusable\n");
  goto out;
 }
 if (if_name) {
  if (strncmp(if_name, sctp_ifap->ifn_p->ifn_name, SCTP_IFNAMSIZ) != 0) {
   SCTPDBG(SCTP_DEBUG_PCB4, "IFN %s of IFA not the same as %s\n",
    sctp_ifap->ifn_p->ifn_name, if_name);
   goto out;
  }
 } else {
  if (sctp_ifap->ifn_p->ifn_index != ifn_index) {
   SCTPDBG(SCTP_DEBUG_PCB4, "IFA owned by ifn_index:%d down command for ifn_index:%d - ignored\n",
    sctp_ifap->ifn_p->ifn_index, ifn_index);
   goto out;
  }
 }

 sctp_ifap->localifa_flags &= (~SCTP_ADDR_IFA_UNUSEABLE);
 sctp_ifap->localifa_flags |= SCTP_ADDR_VALID;
 out:
 SCTP_IPI_ADDR_RUNLOCK();
}

/*-
 * Add an ifa to an ifn.
 * Register the interface as necessary.
 * NOTE: ADDR write lock MUST be held.
 */

static void
sctp_add_ifa_to_ifn(struct sctp_ifn *sctp_ifnp, struct sctp_ifa *sctp_ifap)
{
 int ifa_af;

 LIST_INSERT_HEAD(&sctp_ifnp->ifalist, sctp_ifap, next_ifa);
 sctp_ifap->ifn_p = sctp_ifnp;
 atomic_add_int(&sctp_ifap->ifn_p->refcount, 1);
 /* update address counts */
 sctp_ifnp->ifa_count++;
 ifa_af = sctp_ifap->address.sa.sa_family;
 switch (ifa_af) {
#ifdef INET
 case AF_INET:
  sctp_ifnp->num_v4++;
  break;
#endif
#ifdef INET6
 case AF_INET6:
  sctp_ifnp->num_v6++;
  break;
#endif
 default:
  break;
 }
 if (sctp_ifnp->ifa_count == 1) {
  /* register the new interface */
  sctp_ifnp->registered_af = ifa_af;
 }
}

/*-
 * Remove an ifa from its ifn.
 * If no more addresses exist, remove the ifn too. Otherwise, re-register
 * the interface based on the remaining address families left.
 * NOTE: ADDR write lock MUST be held.
 */

static void
sctp_remove_ifa_from_ifn(struct sctp_ifa *sctp_ifap)
{
 LIST_REMOVE(sctp_ifap, next_ifa);
 if (sctp_ifap->ifn_p) {
  /* update address counts */
  sctp_ifap->ifn_p->ifa_count--;
  switch (sctp_ifap->address.sa.sa_family) {
#ifdef INET
  case AF_INET:
   sctp_ifap->ifn_p->num_v4--;
   break;
#endif
#ifdef INET6
  case AF_INET6:
   sctp_ifap->ifn_p->num_v6--;
   break;
#endif
  default:
   break;
  }

  if (LIST_EMPTY(&sctp_ifap->ifn_p->ifalist)) {
   /* remove the ifn, possibly freeing it */
   sctp_delete_ifn(sctp_ifap->ifn_p, SCTP_ADDR_LOCKED);
  } else {
   /* re-register address family type, if needed */
   if ((sctp_ifap->ifn_p->num_v6 == 0) &&
       (sctp_ifap->ifn_p->registered_af == AF_INET6)) {
    sctp_ifap->ifn_p->registered_af = AF_INET;
   } else if ((sctp_ifap->ifn_p->num_v4 == 0) &&
       (sctp_ifap->ifn_p->registered_af == AF_INET)) {
    sctp_ifap->ifn_p->registered_af = AF_INET6;
   }
   /* free the ifn refcount */
   sctp_free_ifn(sctp_ifap->ifn_p);
  }
  sctp_ifap->ifn_p = NULL;
 }
}

struct sctp_ifa *
sctp_add_addr_to_vrf(uint32_t vrf_id, void *ifn, uint32_t ifn_index,
       uint32_t ifn_type, const char *if_name, void *ifa,
       struct sockaddr *addr, uint32_t ifa_flags,
       int dynamic_add)
{
 struct sctp_vrf *vrf;
 struct sctp_ifn *sctp_ifnp, *new_sctp_ifnp;
 struct sctp_ifa *sctp_ifap, *new_sctp_ifap;
 struct sctp_ifalist *hash_addr_head;
 struct sctp_ifnlist *hash_ifn_head;
 uint32_t hash_of_addr;
 int new_ifn_af = 0;

#ifdef SCTP_DEBUG
 SCTPDBG(SCTP_DEBUG_PCB4, "vrf_id 0x%x: adding address: ", vrf_id);
 SCTPDBG_ADDR(SCTP_DEBUG_PCB4, addr);
#endif
 SCTP_MALLOC(new_sctp_ifnp, struct sctp_ifn *,
             sizeof(struct sctp_ifn), SCTP_M_IFN);
 if (new_sctp_ifnp == NULL) {
#ifdef INVARIANTS
  panic("No memory for IFN");
#endif
  return (NULL);
 }
 SCTP_MALLOC(new_sctp_ifap, struct sctp_ifa *, sizeof(struct sctp_ifa), SCTP_M_IFA);
 if (new_sctp_ifap == NULL) {
#ifdef INVARIANTS
  panic("No memory for IFA");
#endif
  SCTP_FREE(new_sctp_ifnp, SCTP_M_IFN);
  return (NULL);
 }

 SCTP_IPI_ADDR_WLOCK();
 sctp_ifnp = sctp_find_ifn(ifn, ifn_index);
 if (sctp_ifnp) {
  vrf = sctp_ifnp->vrf;
 } else {
  vrf = sctp_find_vrf(vrf_id);
  if (vrf == NULL) {
   vrf = sctp_allocate_vrf(vrf_id);
   if (vrf == NULL) {
    SCTP_IPI_ADDR_WUNLOCK();
    SCTP_FREE(new_sctp_ifnp, SCTP_M_IFN);
    SCTP_FREE(new_sctp_ifap, SCTP_M_IFA);
    return (NULL);
   }
  }
 }
 if (sctp_ifnp == NULL) {
  /* build one and add it, can't hold lock
 * until after malloc done though.
 */

  sctp_ifnp = new_sctp_ifnp;
  new_sctp_ifnp = NULL;
  memset(sctp_ifnp, 0, sizeof(struct sctp_ifn));
  sctp_ifnp->ifn_index = ifn_index;
  sctp_ifnp->ifn_p = ifn;
  sctp_ifnp->ifn_type = ifn_type;
  sctp_ifnp->refcount = 0;
  sctp_ifnp->vrf = vrf;
  atomic_add_int(&vrf->refcount, 1);
  sctp_ifnp->ifn_mtu = SCTP_GATHER_MTU_FROM_IFN_INFO(ifn, ifn_index);
  if (if_name != NULL) {
   SCTP_SNPRINTF(sctp_ifnp->ifn_name, SCTP_IFNAMSIZ, "%s", if_name);
  } else {
   SCTP_SNPRINTF(sctp_ifnp->ifn_name, SCTP_IFNAMSIZ, "%s""unknown");
  }
  hash_ifn_head = &SCTP_BASE_INFO(vrf_ifn_hash)[(ifn_index & SCTP_BASE_INFO(vrf_ifn_hashmark))];
  LIST_INIT(&sctp_ifnp->ifalist);
  LIST_INSERT_HEAD(hash_ifn_head, sctp_ifnp, next_bucket);
  LIST_INSERT_HEAD(&vrf->ifnlist, sctp_ifnp, next_ifn);
  atomic_add_int(&SCTP_BASE_INFO(ipi_count_ifns), 1);
  new_ifn_af = 1;
 }
 sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED);
 if (sctp_ifap) {
  /* Hmm, it already exists? */
  if ((sctp_ifap->ifn_p) &&
      (sctp_ifap->ifn_p->ifn_index == ifn_index)) {
   SCTPDBG(SCTP_DEBUG_PCB4, "Using existing ifn %s (0x%x) for ifa %p\n",
    sctp_ifap->ifn_p->ifn_name, ifn_index,
    (void *)sctp_ifap);
   if (new_ifn_af) {
    /* Remove the created one that we don't want */
    sctp_delete_ifn(sctp_ifnp, SCTP_ADDR_LOCKED);
   }
   if (sctp_ifap->localifa_flags & SCTP_BEING_DELETED) {
    /* easy to solve, just switch back to active */
    SCTPDBG(SCTP_DEBUG_PCB4, "Clearing deleted ifa flag\n");
    sctp_ifap->localifa_flags = SCTP_ADDR_VALID;
    sctp_ifap->ifn_p = sctp_ifnp;
    atomic_add_int(&sctp_ifap->ifn_p->refcount, 1);
   }
  exit_stage_left:
   SCTP_IPI_ADDR_WUNLOCK();
   if (new_sctp_ifnp != NULL) {
    SCTP_FREE(new_sctp_ifnp, SCTP_M_IFN);
   }
   SCTP_FREE(new_sctp_ifap, SCTP_M_IFA);
   return (sctp_ifap);
  } else {
   if (sctp_ifap->ifn_p) {
    /*
 * The last IFN gets the address, remove the
 * old one
 */

    SCTPDBG(SCTP_DEBUG_PCB4, "Moving ifa %p from %s (0x%x) to %s (0x%x)\n",
     (void *)sctp_ifap, sctp_ifap->ifn_p->ifn_name,
     sctp_ifap->ifn_p->ifn_index, if_name,
     ifn_index);
    /* remove the address from the old ifn */
    sctp_remove_ifa_from_ifn(sctp_ifap);
    /* move the address over to the new ifn */
    sctp_add_ifa_to_ifn(sctp_ifnp, sctp_ifap);
    goto exit_stage_left;
   } else {
    /* repair ifnp which was NULL ? */
    sctp_ifap->localifa_flags = SCTP_ADDR_VALID;
    SCTPDBG(SCTP_DEBUG_PCB4, "Repairing ifn %p for ifa %p\n",
     (void *)sctp_ifnp, (void *)sctp_ifap);
    sctp_add_ifa_to_ifn(sctp_ifnp, sctp_ifap);
   }
   goto exit_stage_left;
  }
 }
 sctp_ifap = new_sctp_ifap;
 memset(sctp_ifap, 0, sizeof(struct sctp_ifa));
 sctp_ifap->ifn_p = sctp_ifnp;
 atomic_add_int(&sctp_ifnp->refcount, 1);
 sctp_ifap->vrf_id = vrf_id;
 sctp_ifap->ifa = ifa;
#ifdef HAVE_SA_LEN
 memcpy(&sctp_ifap->address, addr, addr->sa_len);
#else
 switch (addr->sa_family) {
#ifdef INET
 case AF_INET:
  memcpy(&sctp_ifap->address, addr, sizeof(struct sockaddr_in));
  break;
#endif
#ifdef INET6
 case AF_INET6:
  memcpy(&sctp_ifap->address, addr, sizeof(struct sockaddr_in6));
  break;
#endif
#if defined(__Userspace__)
 case AF_CONN:
  memcpy(&sctp_ifap->address, addr, sizeof(struct sockaddr_conn));
  break;
#endif
 default:
  /* TSNH */
  break;
 }
#endif
 sctp_ifap->localifa_flags = SCTP_ADDR_VALID | SCTP_ADDR_DEFER_USE;
 sctp_ifap->flags = ifa_flags;
 /* Set scope */
 switch (sctp_ifap->address.sa.sa_family) {
#ifdef INET
 case AF_INET:
 {
  struct sockaddr_in *sin;

  sin = &sctp_ifap->address.sin;
  if (SCTP_IFN_IS_IFT_LOOP(sctp_ifap->ifn_p) ||
      (IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr))) {
   sctp_ifap->src_is_loop = 1;
  }
  if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
   sctp_ifap->src_is_priv = 1;
  }
  sctp_ifnp->num_v4++;
  if (new_ifn_af)
      new_ifn_af = AF_INET;
  break;
 }
#endif
#ifdef INET6
 case AF_INET6:
 {
  /* ok to use deprecated addresses? */
  struct sockaddr_in6 *sin6;

  sin6 = &sctp_ifap->address.sin6;
  if (SCTP_IFN_IS_IFT_LOOP(sctp_ifap->ifn_p) ||
      (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))) {
   sctp_ifap->src_is_loop = 1;
  }
  if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
   sctp_ifap->src_is_priv = 1;
  }
  sctp_ifnp->num_v6++;
  if (new_ifn_af)
   new_ifn_af = AF_INET6;
  break;
 }
#endif
#if defined(__Userspace__)
 case AF_CONN:
  if (new_ifn_af)
   new_ifn_af = AF_CONN;
  break;
#endif
 default:
  new_ifn_af = 0;
  break;
 }
 hash_of_addr = sctp_get_ifa_hash_val(&sctp_ifap->address.sa);

 if ((sctp_ifap->src_is_priv == 0) &&
     (sctp_ifap->src_is_loop == 0)) {
  sctp_ifap->src_is_glob = 1;
 }
 hash_addr_head = &vrf->vrf_addr_hash[(hash_of_addr & vrf->vrf_addr_hashmark)];
 LIST_INSERT_HEAD(hash_addr_head, sctp_ifap, next_bucket);
 sctp_ifap->refcount = 1;
 LIST_INSERT_HEAD(&sctp_ifnp->ifalist, sctp_ifap, next_ifa);
 sctp_ifnp->ifa_count++;
 vrf->total_ifa_count++;
 atomic_add_int(&SCTP_BASE_INFO(ipi_count_ifas), 1);
 if (new_ifn_af) {
  sctp_ifnp->registered_af = new_ifn_af;
 }
 SCTP_IPI_ADDR_WUNLOCK();
 if (new_sctp_ifnp != NULL) {
  SCTP_FREE(new_sctp_ifnp, SCTP_M_IFN);
 }

 if (dynamic_add) {
  /* Bump up the refcount so that when the timer
 * completes it will drop back down.
 */

  struct sctp_laddr *wi;

  atomic_add_int(&sctp_ifap->refcount, 1);
  wi = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct sctp_laddr);
  if (wi == NULL) {
   /*
 * Gak, what can we do? We have lost an address
 * change can you say HOSED?
 */

   SCTPDBG(SCTP_DEBUG_PCB4, "Lost an address change?\n");
   /* Opps, must decrement the count */
   sctp_del_addr_from_vrf(vrf_id, addr, ifn_index,
            if_name);
   return (NULL);
  }
  SCTP_INCR_LADDR_COUNT();
  memset(wi, 0, sizeof(*wi));
  (void)SCTP_GETTIME_TIMEVAL(&wi->start_time);
  wi->ifa = sctp_ifap;
  wi->action = SCTP_ADD_IP_ADDRESS;

  SCTP_WQ_ADDR_LOCK();
  LIST_INSERT_HEAD(&SCTP_BASE_INFO(addr_wq), wi, sctp_nxt_addr);
  sctp_timer_start(SCTP_TIMER_TYPE_ADDR_WQ,
     (struct sctp_inpcb *)NULL,
     (struct sctp_tcb *)NULL,
     (struct sctp_nets *)NULL);
  SCTP_WQ_ADDR_UNLOCK();
 } else {
  /* it's ready for use */
  sctp_ifap->localifa_flags &= ~SCTP_ADDR_DEFER_USE;
 }
 return (sctp_ifap);
}

void
sctp_del_addr_from_vrf(uint32_t vrf_id, struct sockaddr *addr,
         uint32_t ifn_index, const char *if_name)
{
 struct sctp_vrf *vrf;
 struct sctp_ifa *sctp_ifap = NULL;

 SCTP_IPI_ADDR_WLOCK();
 vrf = sctp_find_vrf(vrf_id);
 if (vrf == NULL) {
  SCTPDBG(SCTP_DEBUG_PCB4, "Can't find vrf_id 0x%x\n", vrf_id);
  goto out_now;
 }

#ifdef SCTP_DEBUG
 SCTPDBG(SCTP_DEBUG_PCB4, "vrf_id 0x%x: deleting address:", vrf_id);
 SCTPDBG_ADDR(SCTP_DEBUG_PCB4, addr);
#endif
 sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED);
 if (sctp_ifap) {
  /* Validate the delete */
  if (sctp_ifap->ifn_p) {
   int valid = 0;
   /*-
 * The name has priority over the ifn_index
 * if its given.
 */

   if (if_name) {
    if (strncmp(if_name, sctp_ifap->ifn_p->ifn_name, SCTP_IFNAMSIZ) == 0) {
     /* They match its a correct delete */
     valid = 1;
    }
   }
   if (!valid) {
    /* last ditch check ifn_index */
    if (ifn_index == sctp_ifap->ifn_p->ifn_index) {
     valid = 1;
    }
   }
   if (!valid) {
    SCTPDBG(SCTP_DEBUG_PCB4, "ifn:%d ifname:%s does not match addresses\n",
     ifn_index, ((if_name == NULL) ? "NULL" : if_name));
    SCTPDBG(SCTP_DEBUG_PCB4, "ifn:%d ifname:%s - ignoring delete\n",
     sctp_ifap->ifn_p->ifn_index, sctp_ifap->ifn_p->ifn_name);
    SCTP_IPI_ADDR_WUNLOCK();
    return;
   }
  }
  SCTPDBG(SCTP_DEBUG_PCB4, "Deleting ifa %p\n", (void *)sctp_ifap);
  sctp_ifap->localifa_flags &= SCTP_ADDR_VALID;
  /*
 * We don't set the flag. This means that the structure will
 * hang around in EP's that have bound specific to it until
 * they close. This gives us TCP like behavior if someone
 * removes an address (or for that matter adds it right back).
 */

  /* sctp_ifap->localifa_flags |= SCTP_BEING_DELETED; */
  vrf->total_ifa_count--;
  LIST_REMOVE(sctp_ifap, next_bucket);
  sctp_remove_ifa_from_ifn(sctp_ifap);
 }
#ifdef SCTP_DEBUG
 else {
  SCTPDBG(SCTP_DEBUG_PCB4, "Del Addr-ifn:%d Could not find address:",
   ifn_index);
  SCTPDBG_ADDR(SCTP_DEBUG_PCB1, addr);
 }
#endif

 out_now:
 SCTP_IPI_ADDR_WUNLOCK();
 if (sctp_ifap) {
  struct sctp_laddr *wi;

  wi = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct sctp_laddr);
  if (wi == NULL) {
   /*
 * Gak, what can we do? We have lost an address
 * change can you say HOSED?
 */

   SCTPDBG(SCTP_DEBUG_PCB4, "Lost an address change?\n");

   /* Oops, must decrement the count */
   sctp_free_ifa(sctp_ifap);
   return;
  }
  SCTP_INCR_LADDR_COUNT();
  memset(wi, 0, sizeof(*wi));
  (void)SCTP_GETTIME_TIMEVAL(&wi->start_time);
  wi->ifa = sctp_ifap;
  wi->action = SCTP_DEL_IP_ADDRESS;
  SCTP_WQ_ADDR_LOCK();
  /*
 * Should this really be a tailq? As it is we will process the
 * newest first :-0
 */

  LIST_INSERT_HEAD(&SCTP_BASE_INFO(addr_wq), wi, sctp_nxt_addr);
  sctp_timer_start(SCTP_TIMER_TYPE_ADDR_WQ,
     (struct sctp_inpcb *)NULL,
     (struct sctp_tcb *)NULL,
     (struct sctp_nets *)NULL);
  SCTP_WQ_ADDR_UNLOCK();
 }
 return;
}

static int
sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to)
{
 int loopback_scope;
#if defined(INET)
 int ipv4_local_scope, ipv4_addr_legal;
#endif
#if defined(INET6)
 int local_scope, site_scope, ipv6_addr_legal;
#endif
#if defined(__Userspace__)
 int conn_addr_legal;
#endif
 struct sctp_vrf *vrf;
 struct sctp_ifn *sctp_ifn;
 struct sctp_ifa *sctp_ifa;

 loopback_scope = stcb->asoc.scope.loopback_scope;
#if defined(INET)
 ipv4_local_scope = stcb->asoc.scope.ipv4_local_scope;
 ipv4_addr_legal = stcb->asoc.scope.ipv4_addr_legal;
#endif
#if defined(INET6)
 local_scope = stcb->asoc.scope.local_scope;
 site_scope = stcb->asoc.scope.site_scope;
 ipv6_addr_legal = stcb->asoc.scope.ipv6_addr_legal;
#endif
#if defined(__Userspace__)
 conn_addr_legal = stcb->asoc.scope.conn_addr_legal;
#endif

 SCTP_IPI_ADDR_RLOCK();
 vrf = sctp_find_vrf(stcb->asoc.vrf_id);
 if (vrf == NULL) {
  /* no vrf, no addresses */
  SCTP_IPI_ADDR_RUNLOCK();
  return (0);
 }

 if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
  LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) {
   if ((loopback_scope == 0) &&
       SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) {
    continue;
   }
   LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) {
    if (sctp_is_addr_restricted(stcb, sctp_ifa) &&
        (!sctp_is_addr_pending(stcb, sctp_ifa))) {
     /* We allow pending addresses, where we
 * have sent an asconf-add to be considered
 * valid.
 */

     continue;
    }
    if (sctp_ifa->address.sa.sa_family != to->sa_family) {
     continue;
    }
    switch (sctp_ifa->address.sa.sa_family) {
#ifdef INET
    case AF_INET:
     if (ipv4_addr_legal) {
      struct sockaddr_in *sin, *rsin;

      sin = &sctp_ifa->address.sin;
      rsin = (struct sockaddr_in *)to;
      if ((ipv4_local_scope == 0) &&
          IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) {
       continue;
      }
#if defined(__FreeBSD__) && !defined(__Userspace__)
      if (prison_check_ip4(stcb->sctp_ep->ip_inp.inp.inp_cred,
                           &sin->sin_addr) != 0) {
       continue;
      }
#endif
      if (sin->sin_addr.s_addr == rsin->sin_addr.s_addr) {
       SCTP_IPI_ADDR_RUNLOCK();
       return (1);
      }
     }
     break;
#endif
#ifdef INET6
    case AF_INET6:
     if (ipv6_addr_legal) {
      struct sockaddr_in6 *sin6, *rsin6;
#if defined(SCTP_EMBEDDED_V6_SCOPE) && !defined(SCTP_KAME)
      struct sockaddr_in6 lsa6;
#endif
      sin6 = &sctp_ifa->address.sin6;
      rsin6 = (struct sockaddr_in6 *)to;
#if defined(__FreeBSD__) && !defined(__Userspace__)
      if (prison_check_ip6(stcb->sctp_ep->ip_inp.inp.inp_cred,
                           &sin6->sin6_addr) != 0) {
       continue;
      }
#endif
      if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
       if (local_scope == 0)
        continue;
#if defined(SCTP_EMBEDDED_V6_SCOPE)
       if (sin6->sin6_scope_id == 0) {
#ifdef SCTP_KAME
        if (sa6_recoverscope(sin6) != 0)
         continue;
#else
        lsa6 = *sin6;
        if (in6_recoverscope(&lsa6,
                             &lsa6.sin6_addr,
                             NULL))
         continue;
        sin6 = &lsa6;
#endif /* SCTP_KAME */
       }
#endif /* SCTP_EMBEDDED_V6_SCOPE */
      }
      if ((site_scope == 0) &&
          (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) {
       continue;
      }
      if (SCTP6_ARE_ADDR_EQUAL(sin6, rsin6)) {
       SCTP_IPI_ADDR_RUNLOCK();
       return (1);
      }
     }
     break;
#endif
#if defined(__Userspace__)
    case AF_CONN:
     if (conn_addr_legal) {
      struct sockaddr_conn *sconn, *rsconn;

      sconn = &sctp_ifa->address.sconn;
      rsconn = (struct sockaddr_conn *)to;
      if (sconn->sconn_addr == rsconn->sconn_addr) {
       SCTP_IPI_ADDR_RUNLOCK();
       return (1);
      }
     }
     break;
#endif
    default:
     /* TSNH */
     break;
    }
   }
  }
 } else {
  struct sctp_laddr *laddr;

  LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list, sctp_nxt_addr) {
   if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) {
    SCTPDBG(SCTP_DEBUG_PCB1, "ifa being deleted\n");
    continue;
   }
   if (sctp_is_addr_restricted(stcb, laddr->ifa) &&
       (!sctp_is_addr_pending(stcb, laddr->ifa))) {
    /* We allow pending addresses, where we
 * have sent an asconf-add to be considered
 * valid.
 */

    continue;
   }
   if (laddr->ifa->address.sa.sa_family != to->sa_family) {
    continue;
   }
   switch (to->sa_family) {
#ifdef INET
   case AF_INET:
   {
    struct sockaddr_in *sin, *rsin;

    sin = &laddr->ifa->address.sin;
    rsin = (struct sockaddr_in *)to;
    if (sin->sin_addr.s_addr == rsin->sin_addr.s_addr) {
     SCTP_IPI_ADDR_RUNLOCK();
     return (1);
    }
    break;
   }
#endif
#ifdef INET6
   case AF_INET6:
   {
    struct sockaddr_in6 *sin6, *rsin6;

    sin6 = &laddr->ifa->address.sin6;
    rsin6 = (struct sockaddr_in6 *)to;
    if (SCTP6_ARE_ADDR_EQUAL(sin6, rsin6)) {
     SCTP_IPI_ADDR_RUNLOCK();
     return (1);
    }
    break;
   }

#endif
#if defined(__Userspace__)
   case AF_CONN:
   {
    struct sockaddr_conn *sconn, *rsconn;

    sconn = &laddr->ifa->address.sconn;
    rsconn = (struct sockaddr_conn *)to;
    if (sconn->sconn_addr == rsconn->sconn_addr) {
     SCTP_IPI_ADDR_RUNLOCK();
     return (1);
    }
    break;
   }
#endif
   default:
    /* TSNH */
    break;
   }
  }
 }
 SCTP_IPI_ADDR_RUNLOCK();
 return (0);
}

static struct sctp_tcb *
sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from,
    struct sockaddr *to, struct sctp_nets **netp, uint32_t vrf_id)
{
 /**** ASSUMES THE CALLER holds the INP_INFO_RLOCK */
 /*
 * If we support the TCP model, then we must now dig through to see
 * if we can find our endpoint in the list of tcp ep's.
 */

 uint16_t lport, rport;
 struct sctppcbhead *ephead;
 struct sctp_inpcb *inp;
 struct sctp_laddr *laddr;
 struct sctp_tcb *stcb;
 struct sctp_nets *net;
#ifdef SCTP_MVRF
 int fnd, i;
#endif

 if ((to == NULL) || (from == NULL)) {
  return (NULL);
 }

 switch (to->sa_family) {
#ifdef INET
 case AF_INET:
  if (from->sa_family == AF_INET) {
   lport = ((struct sockaddr_in *)to)->sin_port;
   rport = ((struct sockaddr_in *)from)->sin_port;
  } else {
   return (NULL);
  }
  break;
#endif
#ifdef INET6
 case AF_INET6:
  if (from->sa_family == AF_INET6) {
   lport = ((struct sockaddr_in6 *)to)->sin6_port;
   rport = ((struct sockaddr_in6 *)from)->sin6_port;
  } else {
   return (NULL);
  }
  break;
#endif
#if defined(__Userspace__)
 case AF_CONN:
  if (from->sa_family == AF_CONN) {
   lport = ((struct sockaddr_conn *)to)->sconn_port;
   rport = ((struct sockaddr_conn *)from)->sconn_port;
  } else {
   return (NULL);
  }
  break;
#endif
 default:
  return (NULL);
 }
 ephead = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR((lport | rport), SCTP_BASE_INFO(hashtcpmark))];
 /*
 * Ok now for each of the guys in this bucket we must look and see:
 * - Does the remote port match. - Does there single association's
 * addresses match this address (to). If so we update p_ep to point
 * to this ep and return the tcb from it.
 */

 LIST_FOREACH(inp, ephead, sctp_hash) {
  SCTP_INP_RLOCK(inp);
  if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
  if (lport != inp->sctp_lport) {
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
#if defined(__FreeBSD__) && !defined(__Userspace__)
  switch (to->sa_family) {
#ifdef INET
  case AF_INET:
  {
   struct sockaddr_in *sin;

   sin = (struct sockaddr_in *)to;
   if (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                        &sin->sin_addr) != 0) {
    SCTP_INP_RUNLOCK(inp);
    continue;
   }
   break;
  }
#endif
#ifdef INET6
  case AF_INET6:
  {
   struct sockaddr_in6 *sin6;

   sin6 = (struct sockaddr_in6 *)to;
   if (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                        &sin6->sin6_addr) != 0) {
    SCTP_INP_RUNLOCK(inp);
    continue;
   }
   break;
  }
#endif
  default:
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
#endif
#ifdef SCTP_MVRF
  fnd = 0;
  for (i = 0; i < inp->num_vrfs; i++) {
   if (inp->m_vrf_ids[i] == vrf_id) {
    fnd = 1;
    break;
   }
  }
  if (fnd == 0) {
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
#else
  if (inp->def_vrf_id != vrf_id) {
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
#endif
  /* check to see if the ep has one of the addresses */
  if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) {
   /* We are NOT bound all, so look further */
   int match = 0;

   LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
    if (laddr->ifa == NULL) {
     SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n", __func__);
     continue;
    }
    if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) {
     SCTPDBG(SCTP_DEBUG_PCB1, "ifa being deleted\n");
     continue;
    }
    if (laddr->ifa->address.sa.sa_family ==
        to->sa_family) {
     /* see if it matches */
#ifdef INET
     if (from->sa_family == AF_INET) {
      struct sockaddr_in *intf_addr, *sin;

      intf_addr = &laddr->ifa->address.sin;
      sin = (struct sockaddr_in *)to;
      if (sin->sin_addr.s_addr ==
          intf_addr->sin_addr.s_addr) {
       match = 1;
       break;
      }
     }
#endif
#ifdef INET6
     if (from->sa_family == AF_INET6) {
      struct sockaddr_in6 *intf_addr6;
      struct sockaddr_in6 *sin6;

      sin6 = (struct sockaddr_in6 *)
          to;
      intf_addr6 = &laddr->ifa->address.sin6;

      if (SCTP6_ARE_ADDR_EQUAL(sin6,
          intf_addr6)) {
       match = 1;
       break;
      }
     }
#endif
#if defined(__Userspace__)
     if (from->sa_family == AF_CONN) {
      struct sockaddr_conn *intf_addr, *sconn;

      intf_addr = &laddr->ifa->address.sconn;
      sconn = (struct sockaddr_conn *)to;
      if (sconn->sconn_addr ==
          intf_addr->sconn_addr) {
       match = 1;
       break;
      }
     }
#endif
    }
   }
   if (match == 0) {
    /* This endpoint does not have this address */
    SCTP_INP_RUNLOCK(inp);
    continue;
   }
  }
  /*
 * Ok if we hit here the ep has the address, does it hold
 * the tcb?
 */

  /* XXX: Why don't we TAILQ_FOREACH through sctp_asoc_list? */
  stcb = LIST_FIRST(&inp->sctp_asoc_list);
  if (stcb == NULL) {
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
  SCTP_TCB_LOCK(stcb);
  if (!sctp_does_stcb_own_this_addr(stcb, to)) {
   SCTP_TCB_UNLOCK(stcb);
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
  if (stcb->rport != rport) {
   /* remote port does not match. */
   SCTP_TCB_UNLOCK(stcb);
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
  if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
   SCTP_TCB_UNLOCK(stcb);
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
  if (!sctp_does_stcb_own_this_addr(stcb, to)) {
   SCTP_TCB_UNLOCK(stcb);
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
  /* Does this TCB have a matching address? */
  TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
   if (net->ro._l_addr.sa.sa_family != from->sa_family) {
    /* not the same family, can't be a match */
    continue;
   }
   switch (from->sa_family) {
#ifdef INET
   case AF_INET:
   {
    struct sockaddr_in *sin, *rsin;

    sin = (struct sockaddr_in *)&net->ro._l_addr;
    rsin = (struct sockaddr_in *)from;
    if (sin->sin_addr.s_addr ==
        rsin->sin_addr.s_addr) {
     /* found it */
     if (netp != NULL) {
      *netp = net;
     }
     /* Update the endpoint pointer */
     *inp_p = inp;
     SCTP_INP_RUNLOCK(inp);
     return (stcb);
    }
    break;
   }
#endif
#ifdef INET6
   case AF_INET6:
   {
    struct sockaddr_in6 *sin6, *rsin6;

    sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
    rsin6 = (struct sockaddr_in6 *)from;
    if (SCTP6_ARE_ADDR_EQUAL(sin6,
        rsin6)) {
     /* found it */
     if (netp != NULL) {
      *netp = net;
     }
     /* Update the endpoint pointer */
     *inp_p = inp;
     SCTP_INP_RUNLOCK(inp);
     return (stcb);
    }
    break;
   }
#endif
#if defined(__Userspace__)
   case AF_CONN:
   {
    struct sockaddr_conn *sconn, *rsconn;

    sconn = (struct sockaddr_conn *)&net->ro._l_addr;
    rsconn = (struct sockaddr_conn *)from;
    if (sconn->sconn_addr == rsconn->sconn_addr) {
     /* found it */
     if (netp != NULL) {
      *netp = net;
     }
     /* Update the endpoint pointer */
     *inp_p = inp;
     SCTP_INP_RUNLOCK(inp);
     return (stcb);
    }
    break;
   }
#endif
   default:
    /* TSNH */
    break;
   }
  }
  SCTP_TCB_UNLOCK(stcb);
  SCTP_INP_RUNLOCK(inp);
 }
 return (NULL);
}

/*
 * rules for use
 *
 * 1) If I return a NULL you must decrement any INP ref cnt. 2) If I find an
 * stcb, both will be locked (locked_tcb and stcb) but decrement will be done
 * (if locked == NULL). 3) Decrement happens on return ONLY if locked ==
 * NULL.
 */


struct sctp_tcb *
sctp_findassociation_ep_addr(struct sctp_inpcb **inp_p, struct sockaddr *remote,
    struct sctp_nets **netp, struct sockaddr *local, struct sctp_tcb *locked_tcb)
{
 struct sctpasochead *head;
 struct sctp_inpcb *inp;
 struct sctp_tcb *stcb = NULL;
 struct sctp_nets *net;
 uint16_t rport;

 inp = *inp_p;
 switch (remote->sa_family) {
#ifdef INET
 case AF_INET:
  rport = (((struct sockaddr_in *)remote)->sin_port);
  break;
#endif
#ifdef INET6
 case AF_INET6:
  rport = (((struct sockaddr_in6 *)remote)->sin6_port);
  break;
#endif
#if defined(__Userspace__)
 case AF_CONN:
  rport = (((struct sockaddr_conn *)remote)->sconn_port);
  break;
#endif
 default:
  return (NULL);
 }
 if (locked_tcb) {
  /*
 * UN-lock so we can do proper locking here this occurs when
 * called from load_addresses_from_init.
 */

  atomic_add_int(&locked_tcb->asoc.refcnt, 1);
  SCTP_TCB_UNLOCK(locked_tcb);
 }
 SCTP_INP_INFO_RLOCK();
 if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
     (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
  /*-
 * Now either this guy is our listener or it's the
 * connector. If it is the one that issued the connect, then
 * it's only chance is to be the first TCB in the list. If
 * it is the acceptor, then do the special_lookup to hash
 * and find the real inp.
 */

  if ((inp->sctp_socket) && SCTP_IS_LISTENING(inp)) {
   /* to is peer addr, from is my addr */
#ifndef SCTP_MVRF
   stcb = sctp_tcb_special_locate(inp_p, remote, local,
       netp, inp->def_vrf_id);
   if ((stcb != NULL) && (locked_tcb == NULL)) {
    /* we have a locked tcb, lower refcount */
    SCTP_INP_DECR_REF(inp);
   }
   if ((locked_tcb != NULL) && (locked_tcb != stcb)) {
    SCTP_INP_RLOCK(locked_tcb->sctp_ep);
    SCTP_TCB_LOCK(locked_tcb);
    atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
    SCTP_INP_RUNLOCK(locked_tcb->sctp_ep);
   }
#else
   /*-
 * MVRF is tricky, we must look in every VRF
 * the endpoint has.
 */

   int i;

   for (i = 0; i < inp->num_vrfs; i++) {
    stcb = sctp_tcb_special_locate(inp_p, remote, local,
                                   netp, inp->m_vrf_ids[i]);
    if ((stcb != NULL) && (locked_tcb == NULL)) {
     /* we have a locked tcb, lower refcount */
     SCTP_INP_DECR_REF(inp);
     break;
    }
    if ((locked_tcb != NULL) && (locked_tcb != stcb)) {
     SCTP_INP_RLOCK(locked_tcb->sctp_ep);
     SCTP_TCB_LOCK(locked_tcb);
     atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
     SCTP_INP_RUNLOCK(locked_tcb->sctp_ep);
     break;
    }
   }
#endif
   SCTP_INP_INFO_RUNLOCK();
   return (stcb);
  } else {
   SCTP_INP_WLOCK(inp);
   if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
    goto null_return;
   }
   stcb = LIST_FIRST(&inp->sctp_asoc_list);
   if (stcb == NULL) {
    goto null_return;
   }
   SCTP_TCB_LOCK(stcb);

   if (stcb->rport != rport) {
    /* remote port does not match. */
    SCTP_TCB_UNLOCK(stcb);
    goto null_return;
   }
   if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
    SCTP_TCB_UNLOCK(stcb);
    goto null_return;
   }
   if (local && !sctp_does_stcb_own_this_addr(stcb, local)) {
    SCTP_TCB_UNLOCK(stcb);
    goto null_return;
   }
   /* now look at the list of remote addresses */
   TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
#ifdef INVARIANTS
    if (net == (TAILQ_NEXT(net, sctp_next))) {
     panic("Corrupt net list");
    }
#endif
    if (net->ro._l_addr.sa.sa_family !=
        remote->sa_family) {
     /* not the same family */
     continue;
    }
    switch (remote->sa_family) {
#ifdef INET
    case AF_INET:
    {
     struct sockaddr_in *sin, *rsin;

     sin = (struct sockaddr_in *)
         &net->ro._l_addr;
     rsin = (struct sockaddr_in *)remote;
     if (sin->sin_addr.s_addr ==
         rsin->sin_addr.s_addr) {
      /* found it */
      if (netp != NULL) {
       *netp = net;
      }
      if (locked_tcb == NULL) {
       SCTP_INP_DECR_REF(inp);
      } else if (locked_tcb != stcb) {
       SCTP_TCB_LOCK(locked_tcb);
      }
      if (locked_tcb) {
       atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
      }

      SCTP_INP_WUNLOCK(inp);
      SCTP_INP_INFO_RUNLOCK();
      return (stcb);
     }
     break;
    }
#endif
#ifdef INET6
    case AF_INET6:
    {
     struct sockaddr_in6 *sin6, *rsin6;

     sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
     rsin6 = (struct sockaddr_in6 *)remote;
     if (SCTP6_ARE_ADDR_EQUAL(sin6,
         rsin6)) {
      /* found it */
      if (netp != NULL) {
       *netp = net;
      }
      if (locked_tcb == NULL) {
       SCTP_INP_DECR_REF(inp);
      } else if (locked_tcb != stcb) {
       SCTP_TCB_LOCK(locked_tcb);
      }
      if (locked_tcb) {
       atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
      }
      SCTP_INP_WUNLOCK(inp);
      SCTP_INP_INFO_RUNLOCK();
      return (stcb);
     }
     break;
    }
#endif
#if defined(__Userspace__)
    case AF_CONN:
    {
     struct sockaddr_conn *sconn, *rsconn;

     sconn = (struct sockaddr_conn *)&net->ro._l_addr;
     rsconn = (struct sockaddr_conn *)remote;
     if (sconn->sconn_addr == rsconn->sconn_addr) {
      /* found it */
      if (netp != NULL) {
       *netp = net;
      }
      if (locked_tcb == NULL) {
       SCTP_INP_DECR_REF(inp);
      } else if (locked_tcb != stcb) {
       SCTP_TCB_LOCK(locked_tcb);
      }
      if (locked_tcb) {
       atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
      }
      SCTP_INP_WUNLOCK(inp);
      SCTP_INP_INFO_RUNLOCK();
      return (stcb);
     }
     break;
    }
#endif
    default:
     /* TSNH */
     break;
    }
   }
   SCTP_TCB_UNLOCK(stcb);
  }
 } else {
  SCTP_INP_WLOCK(inp);
  if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
   goto null_return;
  }
  head = &inp->sctp_tcbhash[SCTP_PCBHASH_ALLADDR(rport,
                                                 inp->sctp_hashmark)];
  LIST_FOREACH(stcb, head, sctp_tcbhash) {
   if (stcb->rport != rport) {
    /* remote port does not match */
    continue;
   }
   SCTP_TCB_LOCK(stcb);
   if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
    SCTP_TCB_UNLOCK(stcb);
    continue;
   }
   if (local && !sctp_does_stcb_own_this_addr(stcb, local)) {
    SCTP_TCB_UNLOCK(stcb);
    continue;
   }
   /* now look at the list of remote addresses */
   TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
#ifdef INVARIANTS
    if (net == (TAILQ_NEXT(net, sctp_next))) {
     panic("Corrupt net list");
    }
#endif
    if (net->ro._l_addr.sa.sa_family !=
        remote->sa_family) {
     /* not the same family */
     continue;
    }
    switch (remote->sa_family) {
#ifdef INET
    case AF_INET:
    {
     struct sockaddr_in *sin, *rsin;

     sin = (struct sockaddr_in *)
         &net->ro._l_addr;
     rsin = (struct sockaddr_in *)remote;
     if (sin->sin_addr.s_addr ==
         rsin->sin_addr.s_addr) {
      /* found it */
      if (netp != NULL) {
       *netp = net;
      }
      if (locked_tcb == NULL) {
       SCTP_INP_DECR_REF(inp);
      } else if (locked_tcb != stcb) {
       SCTP_TCB_LOCK(locked_tcb);
      }
      if (locked_tcb) {
       atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
      }
      SCTP_INP_WUNLOCK(inp);
      SCTP_INP_INFO_RUNLOCK();
      return (stcb);
     }
     break;
    }
#endif
#ifdef INET6
    case AF_INET6:
    {
     struct sockaddr_in6 *sin6, *rsin6;

     sin6 = (struct sockaddr_in6 *)
         &net->ro._l_addr;
     rsin6 = (struct sockaddr_in6 *)remote;
     if (SCTP6_ARE_ADDR_EQUAL(sin6,
         rsin6)) {
      /* found it */
      if (netp != NULL) {
       *netp = net;
      }
      if (locked_tcb == NULL) {
       SCTP_INP_DECR_REF(inp);
      } else if (locked_tcb != stcb) {
       SCTP_TCB_LOCK(locked_tcb);
      }
      if (locked_tcb) {
       atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
      }
      SCTP_INP_WUNLOCK(inp);
      SCTP_INP_INFO_RUNLOCK();
      return (stcb);
     }
     break;
    }
#endif
#if defined(__Userspace__)
    case AF_CONN:
    {
     struct sockaddr_conn *sconn, *rsconn;

     sconn = (struct sockaddr_conn *)&net->ro._l_addr;
     rsconn = (struct sockaddr_conn *)remote;
     if (sconn->sconn_addr == rsconn->sconn_addr) {
      /* found it */
      if (netp != NULL) {
       *netp = net;
      }
      if (locked_tcb == NULL) {
       SCTP_INP_DECR_REF(inp);
      } else if (locked_tcb != stcb) {
       SCTP_TCB_LOCK(locked_tcb);
      }
      if (locked_tcb) {
       atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
      }
      SCTP_INP_WUNLOCK(inp);
      SCTP_INP_INFO_RUNLOCK();
      return (stcb);
     }
     break;
    }
#endif
    default:
     /* TSNH */
     break;
    }
   }
   SCTP_TCB_UNLOCK(stcb);
  }
 }
null_return:
 /* clean up for returning null */
 if (locked_tcb) {
  SCTP_TCB_LOCK(locked_tcb);
  atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
 }
 SCTP_INP_WUNLOCK(inp);
 SCTP_INP_INFO_RUNLOCK();
 /* not found */
 return (NULL);
}

/*
 * Find an association for a specific endpoint using the association id given
 * out in the COMM_UP notification
 */

struct sctp_tcb *
sctp_findasoc_ep_asocid_locked(struct sctp_inpcb *inp, sctp_assoc_t asoc_id, int want_lock)
{
 /*
 * Use my the assoc_id to find a endpoint
 */

 struct sctpasochead *head;
 struct sctp_tcb *stcb;
 uint32_t id;

 if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
  SCTP_PRINTF("TSNH ep_associd0\n");
  return (NULL);
 }
 id = (uint32_t)asoc_id;
 head = &inp->sctp_asocidhash[SCTP_PCBHASH_ASOC(id, inp->hashasocidmark)];
 if (head == NULL) {
  /* invalid id TSNH */
  SCTP_PRINTF("TSNH ep_associd1\n");
  return (NULL);
 }
 LIST_FOREACH(stcb, head, sctp_tcbasocidhash) {
  if (stcb->asoc.assoc_id == id) {
   if (inp != stcb->sctp_ep) {
    /*
 * some other guy has the same id active (id
 * collision ??).
 */

    SCTP_PRINTF("TSNH ep_associd2\n");
    continue;
   }
   if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
    continue;
   }
   if (want_lock) {
    SCTP_TCB_LOCK(stcb);
   }
   return (stcb);
  }
 }
 return (NULL);
}

struct sctp_tcb *
sctp_findassociation_ep_asocid(struct sctp_inpcb *inp, sctp_assoc_t asoc_id, int want_lock)
{
 struct sctp_tcb *stcb;

 SCTP_INP_RLOCK(inp);
 stcb = sctp_findasoc_ep_asocid_locked(inp, asoc_id, want_lock);
 SCTP_INP_RUNLOCK(inp);
 return (stcb);
}

/*
 * Endpoint probe expects that the INP_INFO is locked.
 */

static struct sctp_inpcb *
sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
      uint16_t lport, uint32_t vrf_id)
{
 struct sctp_inpcb *inp;
 struct sctp_laddr *laddr;
#ifdef INET
 struct sockaddr_in *sin;
#endif
#ifdef INET6
 struct sockaddr_in6 *sin6;
 struct sockaddr_in6 *intf_addr6;
#endif
#if defined(__Userspace__)
 struct sockaddr_conn *sconn;
#endif
#ifdef SCTP_MVRF
 int i;
#endif
 int  fnd;

#ifdef INET
 sin = NULL;
#endif
#ifdef INET6
 sin6 = NULL;
#endif
#if defined(__Userspace__)
 sconn = NULL;
#endif
 switch (nam->sa_family) {
#ifdef INET
 case AF_INET:
  sin = (struct sockaddr_in *)nam;
  break;
#endif
#ifdef INET6
 case AF_INET6:
  sin6 = (struct sockaddr_in6 *)nam;
  break;
#endif
#if defined(__Userspace__)
 case AF_CONN:
  sconn = (struct sockaddr_conn *)nam;
  break;
#endif
 default:
  /* unsupported family */
  return (NULL);
 }

 if (head == NULL)
  return (NULL);

 LIST_FOREACH(inp, head, sctp_hash) {
  SCTP_INP_RLOCK(inp);
  if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
  if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) &&
      (inp->sctp_lport == lport)) {
   /* got it */
   switch (nam->sa_family) {
#ifdef INET
   case AF_INET:
    if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
        SCTP_IPV6_V6ONLY(inp)) {
     /* IPv4 on a IPv6 socket with ONLY IPv6 set */
     SCTP_INP_RUNLOCK(inp);
     continue;
    }
#if defined(__FreeBSD__) && !defined(__Userspace__)
    if (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                         &sin->sin_addr) != 0) {
     SCTP_INP_RUNLOCK(inp);
     continue;
    }
#endif
    break;
#endif
#ifdef INET6
   case AF_INET6:
    /* A V6 address and the endpoint is NOT bound V6 */
    if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) {
     SCTP_INP_RUNLOCK(inp);
     continue;
    }
#if defined(__FreeBSD__) && !defined(__Userspace__)
    if (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                         &sin6->sin6_addr) != 0) {
     SCTP_INP_RUNLOCK(inp);
     continue;
    }
#endif
    break;
#endif
   default:
    break;
   }
   /* does a VRF id match? */
   fnd = 0;
#ifdef SCTP_MVRF
   for (i = 0; i < inp->num_vrfs; i++) {
    if (inp->m_vrf_ids[i] == vrf_id) {
     fnd = 1;
     break;
    }
   }
#else
   if (inp->def_vrf_id == vrf_id)
    fnd = 1;
#endif

   SCTP_INP_RUNLOCK(inp);
   if (!fnd)
    continue;
   return (inp);
  }
  SCTP_INP_RUNLOCK(inp);
 }
 switch (nam->sa_family) {
#ifdef INET
 case AF_INET:
  if (sin->sin_addr.s_addr == INADDR_ANY) {
   /* Can't hunt for one that has no address specified */
   return (NULL);
  }
  break;
#endif
#ifdef INET6
 case AF_INET6:
  if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
   /* Can't hunt for one that has no address specified */
   return (NULL);
  }
  break;
#endif
#if defined(__Userspace__)
 case AF_CONN:
  if (sconn->sconn_addr == NULL) {
   return (NULL);
  }
  break;
#endif
 default:
  break;
 }
 /*
 * ok, not bound to all so see if we can find a EP bound to this
 * address.
 */

 LIST_FOREACH(inp, head, sctp_hash) {
  SCTP_INP_RLOCK(inp);
  if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
  if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL)) {
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
  /*
 * Ok this could be a likely candidate, look at all of its
 * addresses
 */

  if (inp->sctp_lport != lport) {
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
  /* does a VRF id match? */
  fnd = 0;
#ifdef SCTP_MVRF
  for (i = 0; i < inp->num_vrfs; i++) {
   if (inp->m_vrf_ids[i] == vrf_id) {
    fnd = 1;
    break;
   }
  }
#else
  if (inp->def_vrf_id == vrf_id)
   fnd = 1;

#endif
  if (!fnd) {
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
  LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
   if (laddr->ifa == NULL) {
    SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n",
     __func__);
    continue;
   }
   SCTPDBG(SCTP_DEBUG_PCB1, "Ok laddr->ifa:%p is possible, ",
    (void *)laddr->ifa);
   if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) {
    SCTPDBG(SCTP_DEBUG_PCB1, "Huh IFA being deleted\n");
    continue;
   }
   if (laddr->ifa->address.sa.sa_family == nam->sa_family) {
    /* possible, see if it matches */
    switch (nam->sa_family) {
#ifdef INET
    case AF_INET:
#if defined(__APPLE__) && !defined(__Userspace__)
     if (sin == NULL) {
      /* TSNH */
      break;
     }
#endif
     if (sin->sin_addr.s_addr ==
         laddr->ifa->address.sin.sin_addr.s_addr) {
      SCTP_INP_RUNLOCK(inp);
      return (inp);
     }
     break;
#endif
#ifdef INET6
    case AF_INET6:
     intf_addr6 = &laddr->ifa->address.sin6;
     if (SCTP6_ARE_ADDR_EQUAL(sin6,
         intf_addr6)) {
      SCTP_INP_RUNLOCK(inp);
      return (inp);
     }
     break;
#endif
#if defined(__Userspace__)
    case AF_CONN:
     if (sconn->sconn_addr == laddr->ifa->address.sconn.sconn_addr) {
      SCTP_INP_RUNLOCK(inp);
      return (inp);
     }
     break;
#endif
    }
   }
  }
  SCTP_INP_RUNLOCK(inp);
 }
 return (NULL);
}

static struct sctp_inpcb *
sctp_isport_inuse(struct sctp_inpcb *inp, uint16_t lport, uint32_t vrf_id)
{
 struct sctppcbhead *head;
 struct sctp_inpcb *t_inp;
#ifdef SCTP_MVRF
 int i;
#endif
 int fnd;

 head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport,
     SCTP_BASE_INFO(hashmark))];
 LIST_FOREACH(t_inp, head, sctp_hash) {
  if (t_inp->sctp_lport != lport) {
   continue;
  }
  /* is it in the VRF in question */
  fnd = 0;
#ifdef SCTP_MVRF
  for (i = 0; i < inp->num_vrfs; i++) {
   if (t_inp->m_vrf_ids[i] == vrf_id) {
    fnd = 1;
    break;
   }
  }
#else
  if (t_inp->def_vrf_id == vrf_id)
   fnd = 1;
#endif
  if (!fnd)
   continue;

  /* This one is in use. */
  /* check the v6/v4 binding issue */
  if ((t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
      SCTP_IPV6_V6ONLY(t_inp)) {
   if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
    /* collision in V6 space */
    return (t_inp);
   } else {
    /* inp is BOUND_V4 no conflict */
    continue;
   }
  } else if (t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
   /* t_inp is bound v4 and v6, conflict always */
   return (t_inp);
  } else {
   /* t_inp is bound only V4 */
   if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
       SCTP_IPV6_V6ONLY(inp)) {
    /* no conflict */
    continue;
   }
   /* else fall through to conflict */
  }
  return (t_inp);
 }
 return (NULL);
}

int
sctp_swap_inpcb_for_listen(struct sctp_inpcb *inp)
{
 /* For 1-2-1 with port reuse */
 struct sctppcbhead *head;
 struct sctp_inpcb *tinp, *ninp;

 SCTP_INP_INFO_WLOCK_ASSERT();
 SCTP_INP_WLOCK_ASSERT(inp);

 if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_PORTREUSE)) {
  /* only works with port reuse on */
  return (-1);
 }
 if ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) {
  return (0);
 }
 SCTP_INP_WUNLOCK(inp);
 head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(inp->sctp_lport,
                                     SCTP_BASE_INFO(hashmark))];
 /* Kick out all non-listeners to the TCP hash */
 LIST_FOREACH_SAFE(tinp, head, sctp_hash, ninp) {
  if (tinp->sctp_lport != inp->sctp_lport) {
   continue;
  }
  if (tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
   continue;
  }
  if (tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
   continue;
  }
  if (SCTP_IS_LISTENING(tinp)) {
   continue;
  }
  SCTP_INP_WLOCK(tinp);
  LIST_REMOVE(tinp, sctp_hash);
  head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR(tinp->sctp_lport, SCTP_BASE_INFO(hashtcpmark))];
  tinp->sctp_flags |= SCTP_PCB_FLAGS_IN_TCPPOOL;
  LIST_INSERT_HEAD(head, tinp, sctp_hash);
  SCTP_INP_WUNLOCK(tinp);
 }
 SCTP_INP_WLOCK(inp);
 /* Pull from where he was */
 LIST_REMOVE(inp, sctp_hash);
 inp->sctp_flags &= ~SCTP_PCB_FLAGS_IN_TCPPOOL;
 head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(inp->sctp_lport, SCTP_BASE_INFO(hashmark))];
 LIST_INSERT_HEAD(head, inp, sctp_hash);
 return (0);
}

struct sctp_inpcb *
sctp_pcb_findep(struct sockaddr *nam, int find_tcp_pool, int have_lock,
  uint32_t vrf_id)
{
 /*
 * First we check the hash table to see if someone has this port
 * bound with just the port.
 */

 struct sctp_inpcb *inp;
 struct sctppcbhead *head;
 int lport;
 unsigned int i;
#ifdef INET
 struct sockaddr_in *sin;
#endif
#ifdef INET6
 struct sockaddr_in6 *sin6;
#endif
#if defined(__Userspace__)
 struct sockaddr_conn *sconn;
#endif

 switch (nam->sa_family) {
#ifdef INET
 case AF_INET:
  sin = (struct sockaddr_in *)nam;
  lport = sin->sin_port;
  break;
#endif
#ifdef INET6
 case AF_INET6:
  sin6 = (struct sockaddr_in6 *)nam;
  lport = sin6->sin6_port;
  break;
#endif
#if defined(__Userspace__)
 case AF_CONN:
  sconn = (struct sockaddr_conn *)nam;
  lport = sconn->sconn_port;
  break;
#endif
 default:
  return (NULL);
 }
 /*
 * I could cheat here and just cast to one of the types but we will
 * do it right. It also provides the check against an Unsupported
 * type too.
 */

 /* Find the head of the ALLADDR chain */
 if (have_lock == 0) {
  SCTP_INP_INFO_RLOCK();
 }
 head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport,
     SCTP_BASE_INFO(hashmark))];
 inp = sctp_endpoint_probe(nam, head, lport, vrf_id);

 /*
 * If the TCP model exists it could be that the main listening
 * endpoint is gone but there still exists a connected socket for this
 * guy. If so we can return the first one that we find. This may NOT
 * be the correct one so the caller should be wary on the returned INP.
 * Currently the only caller that sets find_tcp_pool is in bindx where
 * we are verifying that a user CAN bind the address. He either
 * has bound it already, or someone else has, or its open to bind,
 * so this is good enough.
 */

 if (inp == NULL && find_tcp_pool) {
  for (i = 0; i < SCTP_BASE_INFO(hashtcpmark) + 1; i++) {
   head = &SCTP_BASE_INFO(sctp_tcpephash)[i];
   inp = sctp_endpoint_probe(nam, head, lport, vrf_id);
   if (inp) {
    break;
   }
  }
 }
 if (inp) {
  SCTP_INP_INCR_REF(inp);
 }
 if (have_lock == 0) {
  SCTP_INP_INFO_RUNLOCK();
 }
 return (inp);
}

/*
 * Find an association for an endpoint with the pointer to whom you want to
 * send to and the endpoint pointer. The address can be IPv4 or IPv6. We may
 * need to change the *to to some other struct like a mbuf...
 */

struct sctp_tcb *
sctp_findassociation_addr_sa(struct sockaddr *from, struct sockaddr *to,
    struct sctp_inpcb **inp_p, struct sctp_nets **netp, int find_tcp_pool,
    uint32_t vrf_id)
{
 struct sctp_inpcb *inp = NULL;
 struct sctp_tcb *stcb;

 SCTP_INP_INFO_RLOCK();
 if (find_tcp_pool) {
  if (inp_p != NULL) {
   stcb = sctp_tcb_special_locate(inp_p, from, to, netp,
                                  vrf_id);
  } else {
   stcb = sctp_tcb_special_locate(&inp, from, to, netp,
                                  vrf_id);
  }
  if (stcb != NULL) {
   SCTP_INP_INFO_RUNLOCK();
   return (stcb);
  }
 }
 inp = sctp_pcb_findep(to, 0, 1, vrf_id);
 if (inp_p != NULL) {
  *inp_p = inp;
 }
 SCTP_INP_INFO_RUNLOCK();
 if (inp == NULL) {
  return (NULL);
 }
 /*
 * ok, we have an endpoint, now lets find the assoc for it (if any)
 * we now place the source address or from in the to of the find
 * endpoint call. Since in reality this chain is used from the
 * inbound packet side.
 */

 if (inp_p != NULL) {
  stcb = sctp_findassociation_ep_addr(inp_p, from, netp, to,
                                      NULL);
 } else {
  stcb = sctp_findassociation_ep_addr(&inp, from, netp, to,
                                      NULL);
 }
 return (stcb);
}

/*
 * This routine will grub through the mbuf that is a INIT or INIT-ACK and
 * find all addresses that the sender has specified in any address list. Each
 * address will be used to lookup the TCB and see if one exits.
 */

static struct sctp_tcb *
sctp_findassociation_special_addr(struct mbuf *m, int offset,
    struct sctphdr *sh, struct sctp_inpcb **inp_p, struct sctp_nets **netp,
    struct sockaddr *dst)
{
 struct sctp_paramhdr *phdr, param_buf;
#if defined(INET) || defined(INET6)
 struct sctp_tcb *stcb;
 uint16_t ptype;
#endif
 uint16_t plen;
#ifdef INET
 struct sockaddr_in sin4;
#endif
#ifdef INET6
 struct sockaddr_in6 sin6;
#endif

#ifdef INET
 memset(&sin4, 0, sizeof(sin4));
#ifdef HAVE_SIN_LEN
 sin4.sin_len = sizeof(sin4);
#endif
 sin4.sin_family = AF_INET;
 sin4.sin_port = sh->src_port;
#endif
#ifdef INET6
 memset(&sin6, 0, sizeof(sin6));
#ifdef HAVE_SIN6_LEN
 sin6.sin6_len = sizeof(sin6);
#endif
 sin6.sin6_family = AF_INET6;
 sin6.sin6_port = sh->src_port;
#endif

 offset += sizeof(struct sctp_init_chunk);

 phdr = sctp_get_next_param(m, offset, ¶m_buf, sizeof(param_buf));
 while (phdr != NULL) {
  /* now we must see if we want the parameter */
#if defined(INET) || defined(INET6)
  ptype = ntohs(phdr->param_type);
#endif
  plen = ntohs(phdr->param_length);
  if (plen == 0) {
   break;
  }
#ifdef INET
  if (ptype == SCTP_IPV4_ADDRESS &&
      plen == sizeof(struct sctp_ipv4addr_param)) {
   /* Get the rest of the address */
   struct sctp_ipv4addr_param ip4_param, *p4;

   phdr = sctp_get_next_param(m, offset,
       (struct sctp_paramhdr *)&ip4_param, sizeof(ip4_param));
   if (phdr == NULL) {
    return (NULL);
   }
   p4 = (struct sctp_ipv4addr_param *)phdr;
   memcpy(&sin4.sin_addr, &p4->addr, sizeof(p4->addr));
   /* look it up */
   stcb = sctp_findassociation_ep_addr(inp_p,
       (struct sockaddr *)&sin4, netp, dst, NULL);
   if (stcb != NULL) {
    return (stcb);
   }
  }
#endif
#ifdef INET6
  if (ptype == SCTP_IPV6_ADDRESS &&
      plen == sizeof(struct sctp_ipv6addr_param)) {
   /* Get the rest of the address */
   struct sctp_ipv6addr_param ip6_param, *p6;

   phdr = sctp_get_next_param(m, offset,
       (struct sctp_paramhdr *)&ip6_param, sizeof(ip6_param));
   if (phdr == NULL) {
    return (NULL);
   }
   p6 = (struct sctp_ipv6addr_param *)phdr;
   memcpy(&sin6.sin6_addr, &p6->addr, sizeof(p6->addr));
   /* look it up */
   stcb = sctp_findassociation_ep_addr(inp_p,
       (struct sockaddr *)&sin6, netp, dst, NULL);
   if (stcb != NULL) {
    return (stcb);
   }
  }
#endif
  offset += SCTP_SIZE32(plen);
  phdr = sctp_get_next_param(m, offset, ¶m_buf,
        sizeof(param_buf));
 }
 return (NULL);
}

static struct sctp_tcb *
sctp_findassoc_by_vtag(struct sockaddr *from, struct sockaddr *to, uint32_t vtag,
         struct sctp_inpcb **inp_p, struct sctp_nets **netp, uint16_t rport,
         uint16_t lport, int skip_src_check, uint32_t vrf_id, uint32_t remote_tag)
{
 /*
 * Use my vtag to hash. If we find it we then verify the source addr
 * is in the assoc. If all goes well we save a bit on rec of a
 * packet.
 */

 struct sctpasochead *head;
 struct sctp_nets *net;
 struct sctp_tcb *stcb;
#ifdef SCTP_MVRF
 unsigned int i;
#endif

 SCTP_INP_INFO_RLOCK();
 head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(vtag,
                                                         SCTP_BASE_INFO(hashasocmark))];
 LIST_FOREACH(stcb, head, sctp_asocs) {
  SCTP_INP_RLOCK(stcb->sctp_ep);
  if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
   SCTP_INP_RUNLOCK(stcb->sctp_ep);
   continue;
  }
#ifdef SCTP_MVRF
  for (i = 0; i < stcb->sctp_ep->num_vrfs; i++) {
   if (stcb->sctp_ep->m_vrf_ids[i] == vrf_id) {
    break;
   }
  }
  if (i == stcb->sctp_ep->num_vrfs) {
   SCTP_INP_RUNLOCK(inp);
   continue;
  }
#else
  if (stcb->sctp_ep->def_vrf_id != vrf_id) {
   SCTP_INP_RUNLOCK(stcb->sctp_ep);
   continue;
  }
#endif
  SCTP_TCB_LOCK(stcb);
  SCTP_INP_RUNLOCK(stcb->sctp_ep);
  if (stcb->asoc.my_vtag == vtag) {
   /* candidate */
   if (stcb->rport != rport) {
    SCTP_TCB_UNLOCK(stcb);
    continue;
   }
   if (stcb->sctp_ep->sctp_lport != lport) {
    SCTP_TCB_UNLOCK(stcb);
    continue;
   }
   if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
    SCTP_TCB_UNLOCK(stcb);
    continue;
   }
   /* RRS:Need toaddr check here */
   if (sctp_does_stcb_own_this_addr(stcb, to) == 0) {
           /* Endpoint does not own this address */
    SCTP_TCB_UNLOCK(stcb);
    continue;
   }
   if (remote_tag) {
    /* If we have both vtags that's all we match on */
    if (stcb->asoc.peer_vtag == remote_tag) {
     /* If both tags match we consider it conclusive
 * and check NO source/destination addresses
 */

     goto conclusive;
    }
   }
   if (skip_src_check) {
   conclusive:
           if (from) {
     *netp = sctp_findnet(stcb, from);
    } else {
     *netp = NULL; /* unknown */
    }
    if (inp_p)
     *inp_p = stcb->sctp_ep;
    SCTP_INP_INFO_RUNLOCK();
    return (stcb);
   }
   net = sctp_findnet(stcb, from);
   if (net) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=95 G=94

¤ Dauer der Verarbeitung: 0.61 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.