/*- * 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.
*/
cnt = cnt_inits_to; if (vrf->total_ifa_count > SCTP_COUNT_LIMIT) {
limit_out = 1;
cnt = SCTP_ADDRESS_LIMIT; goto skip_count;
}
LIST_FOREACH(sctp_ifnp, &vrf->ifnlist, next_ifn) { if ((scope->loopback_scope == 0) &&
SCTP_IFN_IS_IFT_LOOP(sctp_ifnp)) { /* * Skip loopback devices if loopback_scope * not set
*/ continue;
}
LIST_FOREACH(sctp_ifap, &sctp_ifnp->ifalist, next_ifa) { #ifdefined(__FreeBSD__) && !defined(__Userspace__) #ifdef INET if ((sctp_ifap->address.sa.sa_family == AF_INET) &&
(prison_check_ip4(inp->ip_inp.inp.inp_cred,
&sctp_ifap->address.sin.sin_addr) != 0)) { continue;
} #endif #ifdef INET6 if ((sctp_ifap->address.sa.sa_family == AF_INET6) &&
(prison_check_ip6(inp->ip_inp.inp.inp_cred,
&sctp_ifap->address.sin6.sin6_addr) != 0)) { continue;
} #endif #endif if (sctp_is_addr_restricted(stcb, sctp_ifap)) { continue;
} #ifdefined(__Userspace__) if (sctp_ifap->address.sa.sa_family == AF_CONN) { continue;
} #endif if (sctp_is_address_in_scope(sctp_ifap, scope, 1) == 0) { continue;
}
cnt++; if (cnt > SCTP_ADDRESS_LIMIT) { break;
}
} if (cnt > SCTP_ADDRESS_LIMIT) { break;
}
}
skip_count: if (cnt > 1) {
total_count = 0;
LIST_FOREACH(sctp_ifnp, &vrf->ifnlist, next_ifn) {
cnt = 0; if ((scope->loopback_scope == 0) &&
SCTP_IFN_IS_IFT_LOOP(sctp_ifnp)) { /* * Skip loopback devices if * loopback_scope not set
*/ continue;
}
LIST_FOREACH(sctp_ifap, &sctp_ifnp->ifalist, next_ifa) { #ifdefined(__FreeBSD__) && !defined(__Userspace__) #ifdef INET if ((sctp_ifap->address.sa.sa_family == AF_INET) &&
(prison_check_ip4(inp->ip_inp.inp.inp_cred,
&sctp_ifap->address.sin.sin_addr) != 0)) { continue;
} #endif #ifdef INET6 if ((sctp_ifap->address.sa.sa_family == AF_INET6) &&
(prison_check_ip6(inp->ip_inp.inp.inp_cred,
&sctp_ifap->address.sin6.sin6_addr) != 0)) { continue;
} #endif #endif if (sctp_is_addr_restricted(stcb, sctp_ifap)) { continue;
} #ifdefined(__Userspace__) if (sctp_ifap->address.sa.sa_family == AF_CONN) { continue;
} #endif if (sctp_is_address_in_scope(sctp_ifap,
scope, 0) == 0) { continue;
} if ((chunk_len != NULL) &&
(padding_len != NULL) &&
(*padding_len > 0)) {
memset(mtod(m_at, caddr_t) + *chunk_len, 0, *padding_len);
SCTP_BUF_LEN(m_at) += *padding_len;
*chunk_len += *padding_len;
*padding_len = 0;
}
m_at = sctp_add_addr_to_mbuf(m_at, sctp_ifap, chunk_len); if (limit_out) {
cnt++;
total_count++; if (cnt >= 2) { /* two from each address */ break;
} if (total_count > SCTP_ADDRESS_LIMIT) { /* No more addresses */ break;
}
}
}
}
}
} else { struct sctp_laddr *laddr;
cnt = cnt_inits_to; /* First, how many ? */
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { continue;
} if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) /* Address being deleted by the system, dont * list.
*/ continue; if (laddr->action == SCTP_DEL_IP_ADDRESS) { /* Address being deleted on this ep * don't list.
*/ continue;
} #ifdefined(__Userspace__) if (laddr->ifa->address.sa.sa_family == AF_CONN) { continue;
} #endif if (sctp_is_address_in_scope(laddr->ifa,
scope, 1) == 0) { continue;
}
cnt++;
} /* * To get through a NAT we only list addresses if we have * more than one. That way if you just bind a single address * we let the source of the init dictate our address.
*/ if (cnt > 1) {
cnt = cnt_inits_to;
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { continue;
} if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) { continue;
} #ifdefined(__Userspace__) if (laddr->ifa->address.sa.sa_family == AF_CONN) { continue;
} #endif if (sctp_is_address_in_scope(laddr->ifa,
scope, 0) == 0) { continue;
} if ((chunk_len != NULL) &&
(padding_len != NULL) &&
(*padding_len > 0)) {
memset(mtod(m_at, caddr_t) + *chunk_len, 0, *padding_len);
SCTP_BUF_LEN(m_at) += *padding_len;
*chunk_len += *padding_len;
*padding_len = 0;
}
m_at = sctp_add_addr_to_mbuf(m_at, laddr->ifa, chunk_len);
cnt++; if (cnt >= SCTP_ADDRESS_LIMIT) { break;
}
}
}
}
SCTP_IPI_ADDR_RUNLOCK(); return (m_at);
}
staticstruct sctp_ifa *
sctp_is_ifa_addr_preferred(struct sctp_ifa *ifa,
uint8_t dest_is_loop,
uint8_t dest_is_priv,
sa_family_t fam)
{
uint8_t dest_is_global = 0; /* dest_is_priv is true if destination is a private address */ /* dest_is_loop is true if destination is a loopback addresses */
/** * Here we determine if its a preferred address. A preferred address * means it is the same scope or higher scope then the destination. * L = loopback, P = private, G = global * ----------------------------------------- * src | dest | result * ---------------------------------------- * L | L | yes * ----------------------------------------- * P | L | yes-v4 no-v6 * ----------------------------------------- * G | L | yes-v4 no-v6 * ----------------------------------------- * L | P | no * ----------------------------------------- * P | P | yes * ----------------------------------------- * G | P | no * ----------------------------------------- * L | G | no * ----------------------------------------- * P | G | no * ----------------------------------------- * G | G | yes * -----------------------------------------
*/
if (ifa->address.sa.sa_family != fam) { /* forget mis-matched family */ return (NULL);
} if ((dest_is_priv == 0) && (dest_is_loop == 0)) {
dest_is_global = 1;
}
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Is destination preferred:");
SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, &ifa->address.sa); /* Ok the address may be ok */ #ifdef INET6 if (fam == AF_INET6) { /* ok to use deprecated addresses? no lets not! */ if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:1\n"); return (NULL);
} if (ifa->src_is_priv && !ifa->src_is_loop) { if (dest_is_loop) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:2\n"); return (NULL);
}
} if (ifa->src_is_glob) { if (dest_is_loop) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:3\n"); return (NULL);
}
}
} #endif /* Now that we know what is what, implement or table * this could in theory be done slicker (it used to be), but this * is straightforward and easier to validate :-)
*/
SCTPDBG(SCTP_DEBUG_OUTPUT3, "src_loop:%d src_priv:%d src_glob:%d\n",
ifa->src_is_loop, ifa->src_is_priv, ifa->src_is_glob);
SCTPDBG(SCTP_DEBUG_OUTPUT3, "dest_loop:%d dest_priv:%d dest_glob:%d\n",
dest_is_loop, dest_is_priv, dest_is_global);
if ((ifa->src_is_loop) && (dest_is_priv)) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:4\n"); return (NULL);
} if ((ifa->src_is_glob) && (dest_is_priv)) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:5\n"); return (NULL);
} if ((ifa->src_is_loop) && (dest_is_global)) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:6\n"); return (NULL);
} if ((ifa->src_is_priv) && (dest_is_global)) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "NO:7\n"); return (NULL);
}
SCTPDBG(SCTP_DEBUG_OUTPUT3, "YES\n"); /* its a preferred address */ return (ifa);
}
/** * Here we determine if its a acceptable address. A acceptable * address means it is the same scope or higher scope but we can * allow for NAT which means its ok to have a global dest and a * private src. * * L = loopback, P = private, G = global * ----------------------------------------- * src | dest | result * ----------------------------------------- * L | L | yes * ----------------------------------------- * P | L | yes-v4 no-v6 * ----------------------------------------- * G | L | yes * ----------------------------------------- * L | P | no * ----------------------------------------- * P | P | yes * ----------------------------------------- * G | P | yes - May not work * ----------------------------------------- * L | G | no * ----------------------------------------- * P | G | yes - May not work * ----------------------------------------- * G | G | yes * -----------------------------------------
*/
if (ifa->address.sa.sa_family != fam) { /* forget non matching family */
SCTPDBG(SCTP_DEBUG_OUTPUT3, "ifa_fam:%d fam:%d\n",
ifa->address.sa.sa_family, fam); return (NULL);
} /* Ok the address may be ok */
SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT3, &ifa->address.sa);
SCTPDBG(SCTP_DEBUG_OUTPUT3, "dst_is_loop:%d dest_is_priv:%d\n",
dest_is_loop, dest_is_priv); if ((dest_is_loop == 0) && (dest_is_priv == 0)) {
dest_is_global = 1;
} #ifdef INET6 if (fam == AF_INET6) { /* ok to use deprecated addresses? */ if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { return (NULL);
} if (ifa->src_is_priv) { /* Special case, linklocal to loop */ if (dest_is_loop) return (NULL);
}
} #endif /* * Now that we know what is what, implement our table. * This could in theory be done slicker (it used to be), but this * is straightforward and easier to validate :-)
*/
SCTPDBG(SCTP_DEBUG_OUTPUT3, "ifa->src_is_loop:%d dest_is_priv:%d\n",
ifa->src_is_loop,
dest_is_priv); if ((ifa->src_is_loop == 1) && (dest_is_priv)) { return (NULL);
}
SCTPDBG(SCTP_DEBUG_OUTPUT3, "ifa->src_is_loop:%d dest_is_glob:%d\n",
ifa->src_is_loop,
dest_is_global); if ((ifa->src_is_loop == 1) && (dest_is_global)) { return (NULL);
}
SCTPDBG(SCTP_DEBUG_OUTPUT3, "address is acceptable\n"); /* its an acceptable address */ return (ifa);
}
if (stcb == NULL) { /* There are no restrictions, no TCB :-) */ return (0);
}
LIST_FOREACH(laddr, &stcb->asoc.sctp_restricted_addrs, sctp_nxt_addr) { if (laddr->ifa == NULL) {
SCTPDBG(SCTP_DEBUG_OUTPUT1, "%s: NULL ifa\n",
__func__); continue;
} if (laddr->ifa == ifa) { /* Yes it is on the list */ return (1);
}
} return (0);
}
vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) return (NULL);
ifn = SCTP_GET_IFN_VOID_FROM_ROUTE(ro);
ifn_index = SCTP_GET_IF_INDEX_FROM_ROUTE(ro);
sctp_ifn = sctp_find_ifn(ifn, ifn_index); /* * first question, is the ifn we will emit on in our list, if so, we * want such an address. Note that we first looked for a * preferred address.
*/ if (sctp_ifn) { /* is a preferred one on the interface we route out? */
LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { #ifdefined(__FreeBSD__) && !defined(__Userspace__) #ifdef INET if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
(prison_check_ip4(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin.sin_addr) != 0)) { continue;
} #endif #ifdef INET6 if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
(prison_check_ip6(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin6.sin6_addr) != 0)) { continue;
} #endif #endif if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
(non_asoc_addr_ok == 0)) continue;
sifa = sctp_is_ifa_addr_preferred(sctp_ifa,
dest_is_loop,
dest_is_priv, fam); if (sifa == NULL) continue; if (sctp_is_addr_in_ep(inp, sifa)) {
atomic_add_int(&sifa->refcount, 1); return (sifa);
}
}
} /* * ok, now we now need to find one on the list of the addresses. * We can't get one on the emitting interface so let's find first * a preferred one. If not that an acceptable one otherwise... * we return NULL.
*/
starting_point = inp->next_addr_touse;
once_again: if (inp->next_addr_touse == NULL) {
inp->next_addr_touse = LIST_FIRST(&inp->sctp_addr_list);
resettotop = 1;
} for (laddr = inp->next_addr_touse; laddr;
laddr = LIST_NEXT(laddr, sctp_nxt_addr)) { if (laddr->ifa == NULL) { /* address has been removed */ continue;
} if (laddr->action == SCTP_DEL_IP_ADDRESS) { /* address is being deleted */ continue;
}
sifa = sctp_is_ifa_addr_preferred(laddr->ifa, dest_is_loop,
dest_is_priv, fam); if (sifa == NULL) continue;
atomic_add_int(&sifa->refcount, 1); return (sifa);
} if (resettotop == 0) {
inp->next_addr_touse = NULL; goto once_again;
}
/* * first question, is the ifn we will emit on in our list, if so, we * want that one.
*/
vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) return (NULL);
/* * first question, is the ifn we will emit on in our list? If so, * we want that one. First we look for a preferred. Second, we go * for an acceptable.
*/ if (sctp_ifn) { /* first try for a preferred address on the ep */
LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { #ifdefined(__FreeBSD__) && !defined(__Userspace__) #ifdef INET if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
(prison_check_ip4(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin.sin_addr) != 0)) { continue;
} #endif #ifdef INET6 if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
(prison_check_ip6(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin6.sin6_addr) != 0)) { continue;
} #endif #endif if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0)) continue; if (sctp_is_addr_in_ep(inp, sctp_ifa)) {
sifa = sctp_is_ifa_addr_preferred(sctp_ifa, dest_is_loop, dest_is_priv, fam); if (sifa == NULL) continue; if (((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, sifa))) ||
(non_asoc_addr_ok &&
(sctp_is_addr_restricted(stcb, sifa)) &&
(!sctp_is_addr_pending(stcb, sifa)))) { /* on the no-no list */ continue;
}
atomic_add_int(&sifa->refcount, 1); return (sifa);
}
} /* next try for an acceptable address on the ep */
LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { #ifdefined(__FreeBSD__) && !defined(__Userspace__) #ifdef INET if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
(prison_check_ip4(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin.sin_addr) != 0)) { continue;
} #endif #ifdef INET6 if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
(prison_check_ip6(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin6.sin6_addr) != 0)) { continue;
} #endif #endif if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0)) continue; if (sctp_is_addr_in_ep(inp, sctp_ifa)) {
sifa= sctp_is_ifa_addr_acceptable(sctp_ifa, dest_is_loop, dest_is_priv,fam); if (sifa == NULL) continue; if (((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, sifa))) ||
(non_asoc_addr_ok &&
(sctp_is_addr_restricted(stcb, sifa)) &&
(!sctp_is_addr_pending(stcb, sifa)))) { /* on the no-no list */ continue;
}
atomic_add_int(&sifa->refcount, 1); return (sifa);
}
}
} /* * if we can't find one like that then we must look at all * addresses bound to pick one at first preferable then * secondly acceptable.
*/
starting_point = stcb->asoc.last_used_address;
sctp_from_the_top: if (stcb->asoc.last_used_address == NULL) {
start_at_beginning = 1;
stcb->asoc.last_used_address = LIST_FIRST(&inp->sctp_addr_list);
} /* search beginning with the last used address */ for (laddr = stcb->asoc.last_used_address; laddr;
laddr = LIST_NEXT(laddr, sctp_nxt_addr)) { if (laddr->ifa == NULL) { /* address has been removed */ continue;
} if (laddr->action == SCTP_DEL_IP_ADDRESS) { /* address is being deleted */ continue;
}
sifa = sctp_is_ifa_addr_preferred(laddr->ifa, dest_is_loop, dest_is_priv, fam); if (sifa == NULL) continue; if (((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, sifa))) ||
(non_asoc_addr_ok &&
(sctp_is_addr_restricted(stcb, sifa)) &&
(!sctp_is_addr_pending(stcb, sifa)))) { /* on the no-no list */ continue;
}
stcb->asoc.last_used_address = laddr;
atomic_add_int(&sifa->refcount, 1); return (sifa);
} if (start_at_beginning == 0) {
stcb->asoc.last_used_address = NULL; goto sctp_from_the_top;
} /* now try for any higher scope than the destination */
stcb->asoc.last_used_address = starting_point;
start_at_beginning = 0;
sctp_from_the_top2: if (stcb->asoc.last_used_address == NULL) {
start_at_beginning = 1;
stcb->asoc.last_used_address = LIST_FIRST(&inp->sctp_addr_list);
} /* search beginning with the last used address */ for (laddr = stcb->asoc.last_used_address; laddr;
laddr = LIST_NEXT(laddr, sctp_nxt_addr)) { if (laddr->ifa == NULL) { /* address has been removed */ continue;
} if (laddr->action == SCTP_DEL_IP_ADDRESS) { /* address is being deleted */ continue;
}
sifa = sctp_is_ifa_addr_acceptable(laddr->ifa, dest_is_loop,
dest_is_priv, fam); if (sifa == NULL) continue; if (((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, sifa))) ||
(non_asoc_addr_ok &&
(sctp_is_addr_restricted(stcb, sifa)) &&
(!sctp_is_addr_pending(stcb, sifa)))) { /* on the no-no list */ continue;
}
stcb->asoc.last_used_address = laddr;
atomic_add_int(&sifa->refcount, 1); return (sifa);
} if (start_at_beginning == 0) {
stcb->asoc.last_used_address = NULL; goto sctp_from_the_top2;
} return (NULL);
}
if (fam == AF_INET6) {
memcpy(&sin6, &ro->ro_dst, sizeof(struct sockaddr_in6)); #ifdef SCTP_KAME
(void)sa6_recoverscope(&sin6); #else
(void)in6_recoverscope(&sin6, &sin6.sin6_addr, NULL); #endif/* SCTP_KAME */
} #endif/* SCTP_EMBEDDED_V6_SCOPE */ #endif/* INET6 */
LIST_FOREACH(ifa, &ifn->ifalist, next_ifa) { #ifdefined(__FreeBSD__) && !defined(__Userspace__) #ifdef INET if ((ifa->address.sa.sa_family == AF_INET) &&
(prison_check_ip4(inp->ip_inp.inp.inp_cred,
&ifa->address.sin.sin_addr) != 0)) { continue;
} #endif #ifdef INET6 if ((ifa->address.sa.sa_family == AF_INET6) &&
(prison_check_ip6(inp->ip_inp.inp.inp_cred,
&ifa->address.sin6.sin6_addr) != 0)) { continue;
} #endif #endif if ((ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
(non_asoc_addr_ok == 0)) continue;
sifa = sctp_is_ifa_addr_preferred(ifa, dest_is_loop,
dest_is_priv, fam); if (sifa == NULL) continue; #ifdef INET6 if (fam == AF_INET6 &&
dest_is_loop &&
sifa->src_is_loop && sifa->src_is_priv) { /* don't allow fe80::1 to be a src on loop ::1, we don't list it * to the peer so we will get an abort.
*/ continue;
} #ifdef SCTP_EMBEDDED_V6_SCOPE if (fam == AF_INET6 &&
IN6_IS_ADDR_LINKLOCAL(&sifa->address.sin6.sin6_addr) &&
IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) { /* link-local <-> link-local must belong to the same scope. */
memcpy(&lsa6, &sifa->address.sin6, sizeof(struct sockaddr_in6)); #ifdef SCTP_KAME
(void)sa6_recoverscope(&lsa6); #else
(void)in6_recoverscope(&lsa6, &lsa6.sin6_addr, NULL); #endif/* SCTP_KAME */ if (sin6.sin6_scope_id != lsa6.sin6_scope_id) { continue;
}
} #endif/* SCTP_EMBEDDED_V6_SCOPE */ #endif/* INET6 */
#ifdefined(__FreeBSD__) || defined(__APPLE__) || defined(__Userspace__) /* Check if the IPv6 address matches to next-hop. In the mobile case, old IPv6 address may be not deleted from the interface. Then, the interface has previous and new addresses. We should use one corresponding to the next-hop. (by micchie)
*/ #ifdef INET6 if (stcb && fam == AF_INET6 &&
sctp_is_mobility_feature_on(stcb->sctp_ep, SCTP_MOBILITY_BASE)) { if (sctp_v6src_match_nexthop(&sifa->address.sin6, ro) == 0) { continue;
}
} #endif #ifdef INET /* Avoid topologically incorrect IPv4 address */ if (stcb && fam == AF_INET &&
sctp_is_mobility_feature_on(stcb->sctp_ep, SCTP_MOBILITY_BASE)) { if (sctp_v4src_match_nexthop(sifa, ro) == 0) { continue;
}
} #endif #endif if (stcb) { if (sctp_is_address_in_scope(ifa, &stcb->asoc.scope, 0) == 0) { continue;
} if (((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, sifa))) ||
(non_asoc_addr_ok &&
(sctp_is_addr_restricted(stcb, sifa)) &&
(!sctp_is_addr_pending(stcb, sifa)))) { /* * It is restricted for some reason.. * probably not yet added.
*/ continue;
}
} if (num_eligible_addr >= addr_wanted) { return (sifa);
}
num_eligible_addr++;
} return (NULL);
}
/*- * For boundall we can use any address in the association. * If non_asoc_addr_ok is set we can use any address (at least in * theory). So we look for preferred addresses first. If we find one, * we use it. Otherwise we next try to get an address on the * interface, which we should be able to do (unless non_asoc_addr_ok * is false and we are routed out that way). In these cases where we * can't use the address of the interface we go through all the * ifn's looking for an address we can use and fill that in. Punting * means we send back address 0, which will probably cause problems * actually since then IP will fill in the address of the route ifn, * which means we probably already rejected it.. i.e. here comes an * abort :-<.
*/
vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) return (NULL);
ifn = SCTP_GET_IFN_VOID_FROM_ROUTE(ro);
ifn_index = SCTP_GET_IF_INDEX_FROM_ROUTE(ro);
SCTPDBG(SCTP_DEBUG_OUTPUT2,"ifn from route:%p ifn_index:%d\n", ifn, ifn_index);
emit_ifn = looked_at = sctp_ifn = sctp_find_ifn(ifn, ifn_index); if (sctp_ifn == NULL) { /* ?? We don't have this guy ?? */
SCTPDBG(SCTP_DEBUG_OUTPUT2,"No ifn emit interface?\n"); goto bound_all_plan_b;
}
SCTPDBG(SCTP_DEBUG_OUTPUT2,"ifn_index:%d name:%s is emit interface\n",
ifn_index, sctp_ifn->ifn_name);
if (net) {
cur_addr_num = net->indx_of_eligible_next_to_use;
}
num_preferred = sctp_count_num_preferred_boundall(sctp_ifn,
inp, stcb,
non_asoc_addr_ok,
dest_is_loop,
dest_is_priv, fam);
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Found %d preferred source addresses for intf:%s\n",
num_preferred, sctp_ifn->ifn_name); if (num_preferred == 0) { /* * no eligible addresses, we must use some other interface * address if we can find one.
*/ goto bound_all_plan_b;
} /* * Ok we have num_eligible_addr set with how many we can use, this * may vary from call to call due to addresses being deprecated * etc..
*/ if (cur_addr_num >= num_preferred) {
cur_addr_num = 0;
} /* * select the nth address from the list (where cur_addr_num is the * nth) and 0 is the first one, 1 is the second one etc...
*/
SCTPDBG(SCTP_DEBUG_OUTPUT2, "cur_addr_num:%d\n", cur_addr_num);
/* if sctp_ifa is NULL something changed??, fall to plan b. */ if (sctp_ifa) {
atomic_add_int(&sctp_ifa->refcount, 1); if (net) { /* save off where the next one we will want */
net->indx_of_eligible_next_to_use = cur_addr_num + 1;
} return (sctp_ifa);
} /* * plan_b: Look at all interfaces and find a preferred address. If * no preferred fall through to plan_c.
*/
bound_all_plan_b:
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Trying Plan B\n");
LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) {
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Examine interface %s\n",
sctp_ifn->ifn_name); if (dest_is_loop == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { /* wrong base scope */
SCTPDBG(SCTP_DEBUG_OUTPUT2, "skip\n"); continue;
} if ((sctp_ifn == looked_at) && looked_at) { /* already looked at this guy */
SCTPDBG(SCTP_DEBUG_OUTPUT2, "already seen\n"); continue;
}
num_preferred = sctp_count_num_preferred_boundall(sctp_ifn, inp, stcb, non_asoc_addr_ok,
dest_is_loop, dest_is_priv, fam);
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Found ifn:%p %d preferred source addresses\n",
ifn, num_preferred); if (num_preferred == 0) { /* None on this interface. */
SCTPDBG(SCTP_DEBUG_OUTPUT2, "No preferred -- skipping to next\n"); continue;
}
SCTPDBG(SCTP_DEBUG_OUTPUT2, "num preferred:%d on interface:%p cur_addr_num:%d\n",
num_preferred, (void *)sctp_ifn, cur_addr_num);
/* * Ok we have num_eligible_addr set with how many we can * use, this may vary from call to call due to addresses * being deprecated etc..
*/ if (cur_addr_num >= num_preferred) {
cur_addr_num = 0;
}
sifa = sctp_select_nth_preferred_addr_from_ifn_boundall(sctp_ifn, inp, stcb, non_asoc_addr_ok, dest_is_loop,
dest_is_priv, cur_addr_num, fam, ro); if (sifa == NULL) continue; if (net) {
net->indx_of_eligible_next_to_use = cur_addr_num + 1;
SCTPDBG(SCTP_DEBUG_OUTPUT2, "we selected %d\n",
cur_addr_num);
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Source:");
SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, &sifa->address.sa);
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Dest:");
SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, &net->ro._l_addr.sa);
}
atomic_add_int(&sifa->refcount, 1); return (sifa);
} #ifdef INET
again_with_private_addresses_allowed: #endif /* plan_c: do we have an acceptable address on the emit interface */
sifa = NULL;
SCTPDBG(SCTP_DEBUG_OUTPUT2,"Trying Plan C: find acceptable on interface\n"); if (emit_ifn == NULL) {
SCTPDBG(SCTP_DEBUG_OUTPUT2,"Jump to Plan D - no emit_ifn\n"); goto plan_d;
}
LIST_FOREACH(sctp_ifa, &emit_ifn->ifalist, next_ifa) {
SCTPDBG(SCTP_DEBUG_OUTPUT2, "ifa:%p\n", (void *)sctp_ifa); #ifdefined(__FreeBSD__) && !defined(__Userspace__) #ifdef INET if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
(prison_check_ip4(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin.sin_addr) != 0)) {
SCTPDBG(SCTP_DEBUG_OUTPUT2,"Jailed\n"); continue;
} #endif #ifdef INET6 if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
(prison_check_ip6(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin6.sin6_addr) != 0)) {
SCTPDBG(SCTP_DEBUG_OUTPUT2,"Jailed\n"); continue;
} #endif #endif if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
(non_asoc_addr_ok == 0)) {
SCTPDBG(SCTP_DEBUG_OUTPUT2,"Defer\n"); continue;
}
sifa = sctp_is_ifa_addr_acceptable(sctp_ifa, dest_is_loop,
dest_is_priv, fam); if (sifa == NULL) {
SCTPDBG(SCTP_DEBUG_OUTPUT2, "IFA not acceptable\n"); continue;
} if (stcb) { if (sctp_is_address_in_scope(sifa, &stcb->asoc.scope, 0) == 0) {
SCTPDBG(SCTP_DEBUG_OUTPUT2, "NOT in scope\n");
sifa = NULL; continue;
} if (((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, sifa))) ||
(non_asoc_addr_ok &&
(sctp_is_addr_restricted(stcb, sifa)) &&
(!sctp_is_addr_pending(stcb, sifa)))) { /* * It is restricted for some * reason.. probably not yet added.
*/
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Its restricted\n");
sifa = NULL; continue;
}
}
atomic_add_int(&sifa->refcount, 1); goto out;
}
plan_d: /* * plan_d: We are in trouble. No preferred address on the emit * interface. And not even a preferred address on all interfaces. * Go out and see if we can find an acceptable address somewhere * amongst all interfaces.
*/
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Trying Plan D looked_at is %p\n", (void *)looked_at);
LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { if (dest_is_loop == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { /* wrong base scope */ continue;
}
LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { #ifdefined(__FreeBSD__) && !defined(__Userspace__) #ifdef INET if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
(prison_check_ip4(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin.sin_addr) != 0)) { continue;
} #endif #ifdef INET6 if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
(prison_check_ip6(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin6.sin6_addr) != 0)) { continue;
} #endif #endif if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
(non_asoc_addr_ok == 0)) continue;
sifa = sctp_is_ifa_addr_acceptable(sctp_ifa,
dest_is_loop,
dest_is_priv, fam); if (sifa == NULL) continue; if (stcb) { if (sctp_is_address_in_scope(sifa, &stcb->asoc.scope, 0) == 0) {
sifa = NULL; continue;
} if (((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, sifa))) ||
(non_asoc_addr_ok &&
(sctp_is_addr_restricted(stcb, sifa)) &&
(!sctp_is_addr_pending(stcb, sifa)))) { /* * It is restricted for some * reason.. probably not yet added.
*/
sifa = NULL; continue;
}
} goto out;
}
} #ifdef INET if (stcb) { if ((retried == 0) && (stcb->asoc.scope.ipv4_local_scope == 0)) {
stcb->asoc.scope.ipv4_local_scope = 1;
retried = 1; goto again_with_private_addresses_allowed;
} elseif (retried == 1) {
stcb->asoc.scope.ipv4_local_scope = 0;
}
} #endif
out: #ifdef INET if (sifa) { if (retried == 1) {
LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { if (dest_is_loop == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { /* wrong base scope */ continue;
}
LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { struct sctp_ifa *tmp_sifa;
#ifdefined(__FreeBSD__) && !defined(__Userspace__) #ifdef INET if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
(prison_check_ip4(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin.sin_addr) != 0)) { continue;
} #endif #ifdef INET6 if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
(prison_check_ip6(inp->ip_inp.inp.inp_cred,
&sctp_ifa->address.sin6.sin6_addr) != 0)) { continue;
} #endif #endif if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
(non_asoc_addr_ok == 0)) continue;
tmp_sifa = sctp_is_ifa_addr_acceptable(sctp_ifa,
dest_is_loop,
dest_is_priv, fam); if (tmp_sifa == NULL) { continue;
} if (tmp_sifa == sifa) { continue;
} if (stcb) { if (sctp_is_address_in_scope(tmp_sifa,
&stcb->asoc.scope, 0) == 0) { continue;
} if (((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, tmp_sifa))) ||
(non_asoc_addr_ok &&
(sctp_is_addr_restricted(stcb, tmp_sifa)) &&
(!sctp_is_addr_pending(stcb, tmp_sifa)))) { /* * It is restricted for some * reason.. probably not yet added.
*/ continue;
}
} if ((tmp_sifa->address.sin.sin_family == AF_INET) &&
(IN4_ISPRIVATE_ADDRESS(&(tmp_sifa->address.sin.sin_addr)))) {
sctp_add_local_addr_restricted(stcb, tmp_sifa);
}
}
}
}
atomic_add_int(&sifa->refcount, 1);
} #endif return (sifa);
}
/** * Rules: * - Find the route if needed, cache if I can. * - Look at interface address in route, Is it in the bound list. If so we * have the best source. * - If not we must rotate amongst the addresses. * * Caveats and issues * * Do we need to pay attention to scope. We can have a private address * or a global address we are sourcing or sending to. So if we draw * it out
--> --------------------
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.