freebsd-skq/sys/netinet/sctp_bsd_addr.c
Randall Stewart 93164cf98c - most all includes (#include <>) migrate to the sctp_os_bsd.h file
- Finally all splxx() are removed
 - Count error fixed in mapping array which might
   cause a wrong cumack generation.
 - Invariants around panic for case D + printf when no invariants.
 - one-to-one model race condition fixed by using
   a pre-formed connection and then completing the
   work so accept won't happen on a non-formed
   association.
 - Some additional paranoia checks in sctp_output.
 - Locks that were missing in the accept code.

Approved by:	gnn
2007-01-18 09:58:43 +00:00

1970 lines
56 KiB
C

/*-
* Copyright (c) 2001-2006, Cisco Systems, Inc. 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.
*/
/* $KAME: sctp_output.c,v 1.46 2005/03/06 16:04:17 itojun Exp $ */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <netinet/sctp_os.h>
#include <netinet/sctp_var.h>
#include <netinet/sctp_pcb.h>
#include <netinet/sctp_header.h>
#include <netinet/sctputil.h>
#include <netinet/sctp_output.h>
#include <netinet/sctp_bsd_addr.h>
#include <netinet/sctp_uio.h>
#include <netinet/sctputil.h>
#include <netinet/sctp_timer.h>
#include <netinet/sctp_asconf.h>
#include <netinet/sctp_indata.h>
/* XXX
* This module needs to be rewritten with an eye towards getting
* rid of the user of ifa.. and use another list method George
* as told me of.
*/
#ifdef SCTP_DEBUG
extern uint32_t sctp_debug_on;
#endif
static struct sockaddr_in *
sctp_is_v4_ifa_addr_prefered(struct ifaddr *ifa, uint8_t loopscope, uint8_t ipv4_scope, uint8_t * sin_loop, uint8_t * sin_local)
{
struct sockaddr_in *sin;
/*
* Here we determine if its a prefered address. A prefered 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
* ----------------------------------------- G | L | yes
* ----------------------------------------- L | P | no
* ----------------------------------------- P | P | yes
* ----------------------------------------- G | P | no
* ----------------------------------------- L | G | no
* ----------------------------------------- P | G | no
* ----------------------------------------- G | G | yes
* -----------------------------------------
*/
if (ifa->ifa_addr->sa_family != AF_INET) {
/* forget non-v4 */
return (NULL);
}
/* Ok the address may be ok */
sin = (struct sockaddr_in *)ifa->ifa_addr;
if (sin->sin_addr.s_addr == 0) {
return (NULL);
}
*sin_local = *sin_loop = 0;
if ((ifa->ifa_ifp->if_type == IFT_LOOP) ||
(IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr))) {
*sin_loop = 1;
*sin_local = 1;
}
if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
*sin_local = 1;
}
if (!loopscope && *sin_loop) {
/* Its a loopback address and we don't have loop scope */
return (NULL);
}
if (!ipv4_scope && *sin_local) {
/*
* Its a private address, and we don't have private address
* scope
*/
return (NULL);
}
if (((ipv4_scope == 0) && (loopscope == 0)) && (*sin_local)) {
/* its a global src and a private dest */
return (NULL);
}
/* its a prefered address */
return (sin);
}
static struct sockaddr_in *
sctp_is_v4_ifa_addr_acceptable(struct ifaddr *ifa, uint8_t loopscope, uint8_t ipv4_scope, uint8_t * sin_loop, uint8_t * sin_local)
{
struct sockaddr_in *sin;
/*
* 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
* ----------------------------------------- G | L | yes
* ----------------------------------------- L | P | no
* ----------------------------------------- P | P | yes
* ----------------------------------------- G | P | yes -
* probably this won't work.
* ----------------------------------------- L | G |
* no ----------------------------------------- P | G |
* yes ----------------------------------------- G | G |
* yes -----------------------------------------
*/
if (ifa->ifa_addr->sa_family != AF_INET) {
/* forget non-v4 */
return (NULL);
}
/* Ok the address may be ok */
sin = (struct sockaddr_in *)ifa->ifa_addr;
if (sin->sin_addr.s_addr == 0) {
return (NULL);
}
*sin_local = *sin_loop = 0;
if ((ifa->ifa_ifp->if_type == IFT_LOOP) ||
(IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr))) {
*sin_loop = 1;
*sin_local = 1;
}
if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
*sin_local = 1;
}
if (!loopscope && *sin_loop) {
/* Its a loopback address and we don't have loop scope */
return (NULL);
}
/* its an acceptable address */
return (sin);
}
/*
* This treats the address list on the ep as a restricted list (negative
* list). If a the passed address is listed, then the address is NOT allowed
* on the association.
*/
int
sctp_is_addr_restricted(struct sctp_tcb *stcb, struct sockaddr *addr)
{
struct sctp_laddr *laddr;
#ifdef SCTP_DEBUG
int cnt = 0;
#endif
if (stcb == NULL) {
/* There are no restrictions, no TCB :-) */
return (0);
}
#ifdef SCTP_DEBUG
LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list, sctp_nxt_addr) {
cnt++;
}
if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
printf("There are %d addresses on the restricted list\n", cnt);
}
cnt = 0;
#endif
LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list, sctp_nxt_addr) {
if (laddr->ifa == NULL) {
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Help I have fallen and I can't get up!\n");
}
#endif
continue;
}
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
cnt++;
printf("Restricted address[%d]:", cnt);
sctp_print_address(laddr->ifa->ifa_addr);
}
#endif
if (sctp_cmpaddr(addr, laddr->ifa->ifa_addr) == 1) {
/* Yes it is on the list */
return (1);
}
}
return (0);
}
static int
sctp_is_addr_in_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
{
struct sctp_laddr *laddr;
if (ifa == NULL)
return (0);
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
if (laddr->ifa == NULL) {
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Help I have fallen and I can't get up!\n");
}
#endif
continue;
}
if (laddr->ifa->ifa_addr == NULL)
continue;
if (laddr->ifa == ifa)
/* same pointer */
return (1);
if (laddr->ifa->ifa_addr->sa_family != ifa->ifa_addr->sa_family) {
/* skip non compatible address comparison */
continue;
}
if (sctp_cmpaddr(ifa->ifa_addr, laddr->ifa->ifa_addr) == 1) {
/* Yes it is restricted */
return (1);
}
}
return (0);
}
static struct in_addr
sctp_choose_v4_boundspecific_inp(struct sctp_inpcb *inp,
struct route *ro,
uint8_t ipv4_scope,
uint8_t loopscope)
{
struct in_addr ans;
struct sctp_laddr *laddr;
struct sockaddr_in *sin;
struct ifnet *ifn;
struct ifaddr *ifa;
uint8_t sin_loop, sin_local;
struct rtentry *rt;
/*
* first question, is the ifn we will emit on in our list, if so, we
* want that one.
*/
rt = ro->ro_rt;
ifn = rt->rt_ifp;
if (ifn) {
/* is a prefered one on the interface we route out? */
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
sin = sctp_is_v4_ifa_addr_prefered(ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
if (sctp_is_addr_in_ep(inp, ifa)) {
return (sin->sin_addr);
}
}
/* is an acceptable one on the interface we route out? */
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
sin = sctp_is_v4_ifa_addr_acceptable(ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
if (sctp_is_addr_in_ep(inp, ifa)) {
return (sin->sin_addr);
}
}
}
/* ok, what about a prefered address in the inp */
for (laddr = LIST_FIRST(&inp->sctp_addr_list);
laddr && (laddr != inp->next_addr_touse);
laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
sin = sctp_is_v4_ifa_addr_prefered(laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
return (sin->sin_addr);
}
/* ok, what about an acceptable address in the inp */
for (laddr = LIST_FIRST(&inp->sctp_addr_list);
laddr && (laddr != inp->next_addr_touse);
laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
sin = sctp_is_v4_ifa_addr_acceptable(laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
return (sin->sin_addr);
}
/*
* no address bound can be a source for the destination we are in
* trouble
*/
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Src address selection for EP, no acceptable src address found for address\n");
}
#endif
RTFREE(ro->ro_rt);
ro->ro_rt = NULL;
memset(&ans, 0, sizeof(ans));
return (ans);
}
static struct in_addr
sctp_choose_v4_boundspecific_stcb(struct sctp_inpcb *inp,
struct sctp_tcb *stcb,
struct sctp_nets *net,
struct route *ro,
uint8_t ipv4_scope,
uint8_t loopscope,
int non_asoc_addr_ok)
{
/*
* Here we have two cases, bound all asconf allowed. bound all
* asconf not allowed.
*
*/
struct sctp_laddr *laddr, *starting_point;
struct in_addr ans;
struct ifnet *ifn;
struct ifaddr *ifa;
uint8_t sin_loop, sin_local, start_at_beginning = 0;
struct sockaddr_in *sin;
struct rtentry *rt;
/*
* first question, is the ifn we will emit on in our list, if so, we
* want that one.
*/
rt = ro->ro_rt;
ifn = rt->rt_ifp;
if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF)) {
/*
* Here we use the list of addresses on the endpoint. Then
* the addresses listed on the "restricted" list is just
* that, address that have not been added and can't be used
* (unless the non_asoc_addr_ok is set).
*/
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Have a STCB - asconf allowed, not bound all have a netgative list\n");
}
#endif
/*
* first question, is the ifn we will emit on in our list,
* if so, we want that one.
*/
if (ifn) {
/* first try for an prefered address on the ep */
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
if (sctp_is_addr_in_ep(inp, ifa)) {
sin = sctp_is_v4_ifa_addr_prefered(ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
if ((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, (struct sockaddr *)sin))) {
/* on the no-no list */
continue;
}
return (sin->sin_addr);
}
}
/* next try for an acceptable address on the ep */
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
if (sctp_is_addr_in_ep(inp, ifa)) {
sin = sctp_is_v4_ifa_addr_acceptable(ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
if ((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, (struct sockaddr *)sin))) {
/* on the no-no list */
continue;
}
return (sin->sin_addr);
}
}
}
/*
* if we can't find one like that then we must look at all
* addresses bound to pick one at first prefereable then
* secondly acceptable.
*/
starting_point = stcb->asoc.last_used_address;
sctpv4_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;
}
sin = sctp_is_v4_ifa_addr_prefered(laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
if ((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, (struct sockaddr *)sin))) {
/* on the no-no list */
continue;
}
return (sin->sin_addr);
}
if (start_at_beginning == 0) {
stcb->asoc.last_used_address = NULL;
goto sctpv4_from_the_top;
}
/* now try for any higher scope than the destination */
stcb->asoc.last_used_address = starting_point;
start_at_beginning = 0;
sctpv4_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;
}
sin = sctp_is_v4_ifa_addr_acceptable(laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
if ((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, (struct sockaddr *)sin))) {
/* on the no-no list */
continue;
}
return (sin->sin_addr);
}
if (start_at_beginning == 0) {
stcb->asoc.last_used_address = NULL;
goto sctpv4_from_the_top2;
}
} else {
/*
* Here we have an address list on the association, thats
* the only valid source addresses that we can use.
*/
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Have a STCB - no asconf allowed, not bound all have a postive list\n");
}
#endif
/*
* First look at all addresses for one that is on the
* interface we route out
*/
LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
sctp_nxt_addr) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
sin = sctp_is_v4_ifa_addr_prefered(laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
/*
* first question, is laddr->ifa an address
* associated with the emit interface
*/
if (ifn) {
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
if (laddr->ifa == ifa) {
sin = (struct sockaddr_in *)laddr->ifa->ifa_addr;
return (sin->sin_addr);
}
if (sctp_cmpaddr(ifa->ifa_addr, laddr->ifa->ifa_addr) == 1) {
sin = (struct sockaddr_in *)laddr->ifa->ifa_addr;
return (sin->sin_addr);
}
}
}
}
/* what about an acceptable one on the interface? */
LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
sctp_nxt_addr) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
sin = sctp_is_v4_ifa_addr_acceptable(laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
/*
* first question, is laddr->ifa an address
* associated with the emit interface
*/
if (ifn) {
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
if (laddr->ifa == ifa) {
sin = (struct sockaddr_in *)laddr->ifa->ifa_addr;
return (sin->sin_addr);
}
if (sctp_cmpaddr(ifa->ifa_addr, laddr->ifa->ifa_addr) == 1) {
sin = (struct sockaddr_in *)laddr->ifa->ifa_addr;
return (sin->sin_addr);
}
}
}
}
/* ok, next one that is preferable in general */
LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
sctp_nxt_addr) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
sin = sctp_is_v4_ifa_addr_prefered(laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
return (sin->sin_addr);
}
/* last, what about one that is acceptable */
LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
sctp_nxt_addr) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
sin = sctp_is_v4_ifa_addr_acceptable(laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
return (sin->sin_addr);
}
}
RTFREE(ro->ro_rt);
ro->ro_rt = NULL;
memset(&ans, 0, sizeof(ans));
return (ans);
}
static struct sockaddr_in *
sctp_select_v4_nth_prefered_addr_from_ifn_boundall(struct ifnet *ifn, struct sctp_tcb *stcb, int non_asoc_addr_ok,
uint8_t loopscope, uint8_t ipv4_scope, int cur_addr_num)
{
struct ifaddr *ifa;
struct sockaddr_in *sin;
uint8_t sin_loop, sin_local;
int num_eligible_addr = 0;
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
sin = sctp_is_v4_ifa_addr_prefered(ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
if (stcb) {
if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin)) {
/*
* It is restricted for some reason..
* probably not yet added.
*/
continue;
}
}
if (cur_addr_num == num_eligible_addr) {
return (sin);
}
}
return (NULL);
}
static int
sctp_count_v4_num_prefered_boundall(struct ifnet *ifn, struct sctp_tcb *stcb, int non_asoc_addr_ok,
uint8_t loopscope, uint8_t ipv4_scope, uint8_t * sin_loop, uint8_t * sin_local)
{
struct ifaddr *ifa;
struct sockaddr_in *sin;
int num_eligible_addr = 0;
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
sin = sctp_is_v4_ifa_addr_prefered(ifa, loopscope, ipv4_scope, sin_loop, sin_local);
if (sin == NULL)
continue;
if (stcb) {
if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin)) {
/*
* It is restricted for some reason..
* probably not yet added.
*/
continue;
}
}
num_eligible_addr++;
}
return (num_eligible_addr);
}
static struct in_addr
sctp_choose_v4_boundall(struct sctp_inpcb *inp,
struct sctp_tcb *stcb,
struct sctp_nets *net,
struct route *ro,
uint8_t ipv4_scope,
uint8_t loopscope,
int non_asoc_addr_ok)
{
int cur_addr_num = 0, num_prefered = 0;
uint8_t sin_loop, sin_local;
struct ifnet *ifn;
struct sockaddr_in *sin;
struct in_addr ans;
struct ifaddr *ifa;
struct rtentry *rt;
/*
* For v4 we can use (in boundall) 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 prefered 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 :-<.
*/
rt = ro->ro_rt;
ifn = rt->rt_ifp;
if (net) {
cur_addr_num = net->indx_of_eligible_next_to_use;
}
if (ifn == NULL) {
goto bound_all_v4_plan_c;
}
num_prefered = sctp_count_v4_num_prefered_boundall(ifn, stcb, non_asoc_addr_ok, loopscope, ipv4_scope, &sin_loop, &sin_local);
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Found %d prefered source addresses\n", num_prefered);
}
#endif
if (num_prefered == 0) {
/*
* no eligible addresses, we must use some other interface
* address if we can find one.
*/
goto bound_all_v4_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_prefered) {
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...
*/
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("cur_addr_num:%d\n", cur_addr_num);
}
#endif
sin = sctp_select_v4_nth_prefered_addr_from_ifn_boundall(ifn, stcb, non_asoc_addr_ok, loopscope,
ipv4_scope, cur_addr_num);
/* if sin is NULL something changed??, plan_a now */
if (sin) {
return (sin->sin_addr);
}
/*
* plan_b: Look at the interface that we emit on and see if we can
* find an acceptable address.
*/
bound_all_v4_plan_b:
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
sin = sctp_is_v4_ifa_addr_acceptable(ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
if (stcb) {
if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin)) {
/*
* It is restricted for some reason..
* probably not yet added.
*/
continue;
}
}
return (sin->sin_addr);
}
/*
* plan_c: Look at all interfaces and find a prefered address. If we
* reache here we are in trouble I think.
*/
bound_all_v4_plan_c:
for (ifn = TAILQ_FIRST(&ifnet);
ifn && (ifn != inp->next_ifn_touse);
ifn = TAILQ_NEXT(ifn, if_list)) {
if (loopscope == 0 && ifn->if_type == IFT_LOOP) {
/* wrong base scope */
continue;
}
if (ifn == rt->rt_ifp)
/* already looked at this guy */
continue;
num_prefered = sctp_count_v4_num_prefered_boundall(ifn, stcb, non_asoc_addr_ok,
loopscope, ipv4_scope, &sin_loop, &sin_local);
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Found ifn:%p %d prefered source addresses\n", ifn, num_prefered);
}
#endif
if (num_prefered == 0) {
/*
* None on this interface.
*/
continue;
}
/*
* 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_prefered) {
cur_addr_num = 0;
}
sin = sctp_select_v4_nth_prefered_addr_from_ifn_boundall(ifn, stcb, non_asoc_addr_ok, loopscope,
ipv4_scope, cur_addr_num);
if (sin == NULL)
continue;
return (sin->sin_addr);
}
/*
* plan_d: We are in deep trouble. No prefered address on any
* interface. And the emit interface does not even have an
* acceptable address. Take anything we can get! If this does not
* work we are probably going to emit a packet that will illicit an
* ABORT, falling through.
*/
for (ifn = TAILQ_FIRST(&ifnet);
ifn && (ifn != inp->next_ifn_touse);
ifn = TAILQ_NEXT(ifn, if_list)) {
if (loopscope == 0 && ifn->if_type == IFT_LOOP) {
/* wrong base scope */
continue;
}
if (ifn == rt->rt_ifp)
/* already looked at this guy */
continue;
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
sin = sctp_is_v4_ifa_addr_acceptable(ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
if (sin == NULL)
continue;
if (stcb) {
if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin)) {
/*
* It is restricted for some
* reason.. probably not yet added.
*/
continue;
}
}
return (sin->sin_addr);
}
}
/*
* Ok we can find NO address to source from that is not on our
* negative list. It is either the special ASCONF case where we are
* sourceing from a intf that has been ifconfig'd to a different
* address (i.e. it holds a ADD/DEL/SET-PRIM and the proper lookup
* address. OR we are hosed, and this baby is going to abort the
* association.
*/
if (non_asoc_addr_ok) {
return (((struct sockaddr_in *)(rt->rt_ifa->ifa_addr))->sin_addr);
} else {
RTFREE(ro->ro_rt);
ro->ro_rt = NULL;
memset(&ans, 0, sizeof(ans));
return (ans);
}
}
/* tcb may be NULL */
struct in_addr
sctp_ipv4_source_address_selection(struct sctp_inpcb *inp,
struct sctp_tcb *stcb, struct route *ro, struct sctp_nets *net,
int non_asoc_addr_ok)
{
struct in_addr ans;
struct sockaddr_in *to = (struct sockaddr_in *)&ro->ro_dst;
uint8_t ipv4_scope, loopscope;
/*
* 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.
*
* Cavets 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 source * dest * result
* ------------------------------------------ a Private *
* Global * NAT? ------------------------------------------ b
* Private * Private * No problem
* ------------------------------------------ c Global *
* Private * Huh, How will this work?
* ------------------------------------------ d Global *
* Global * No Problem ------------------------------------------
*
* And then we add to that what happens if there are multiple addresses
* assigned to an interface. Remember the ifa on a ifn is a linked
* list of addresses. So one interface can have more than one IPv4
* address. What happens if we have both a private and a global
* address? Do we then use context of destination to sort out which
* one is best? And what about NAT's sending P->G may get you a NAT
* translation, or should you select the G thats on the interface in
* preference.
*
* Decisions:
*
* - count the number of addresses on the interface. - if its one, no
* problem except case <c>. For <a> we will assume a NAT out there.
* - if there are more than one, then we need to worry about scope P
* or G. We should prefer G -> G and P -> P if possible. Then as a
* secondary fall back to mixed types G->P being a last ditch one. -
* The above all works for bound all, but bound specific we need to
* use the same concept but instead only consider the bound
* addresses. If the bound set is NOT assigned to the interface then
* we must use rotation amongst them.
*
* Notes: For v4, we can always punt and let ip_output decide by
* sending back a source of 0.0.0.0
*/
if (ro->ro_rt == NULL) {
/*
* Need a route to cache.
*
*/
rtalloc_ign(ro, 0UL);
}
if (ro->ro_rt == NULL) {
/* No route to host .. punt */
memset(&ans, 0, sizeof(ans));
return (ans);
}
/* Setup our scopes */
if (stcb) {
ipv4_scope = stcb->asoc.ipv4_local_scope;
loopscope = stcb->asoc.loopback_scope;
} else {
/* Scope based on outbound address */
if ((IN4_ISPRIVATE_ADDRESS(&to->sin_addr))) {
ipv4_scope = 1;
loopscope = 0;
} else if (IN4_ISLOOPBACK_ADDRESS(&to->sin_addr)) {
ipv4_scope = 1;
loopscope = 1;
} else {
ipv4_scope = 0;
loopscope = 0;
}
}
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
/*
* When bound to all if the address list is set it is a
* negative list. Addresses being added by asconf.
*/
return (sctp_choose_v4_boundall(inp, stcb, net, ro,
ipv4_scope, loopscope, non_asoc_addr_ok));
}
/*
* Three possiblities here:
*
* a) stcb is NULL, which means we operate only from the list of
* addresses (ifa's) bound to the assoc and we care not about the
* list. b) stcb is NOT-NULL, which means we have an assoc structure
* and auto-asconf is on. This means that the list of addresses is a
* NOT list. We use the list from the inp, but any listed address in
* our list is NOT yet added. However if the non_asoc_addr_ok is set
* we CAN use an address NOT available (i.e. being added). Its a
* negative list. c) stcb is NOT-NULL, which means we have an assoc
* structure and auto-asconf is off. This means that the list of
* addresses is the ONLY addresses I can use.. its positive.
*
* Note we collapse b & c into the same function just like in the v6
* address selection.
*/
if (stcb) {
return (sctp_choose_v4_boundspecific_stcb(inp, stcb, net,
ro, ipv4_scope, loopscope, non_asoc_addr_ok));
} else {
return (sctp_choose_v4_boundspecific_inp(inp, ro,
ipv4_scope, loopscope));
}
/* this should not be reached */
memset(&ans, 0, sizeof(ans));
return (ans);
}
static struct sockaddr_in6 *
sctp_is_v6_ifa_addr_acceptable(struct ifaddr *ifa, int loopscope, int loc_scope, int *sin_loop, int *sin_local)
{
struct in6_ifaddr *ifa6;
struct sockaddr_in6 *sin6;
if (ifa->ifa_addr->sa_family != AF_INET6) {
/* forget non-v6 */
return (NULL);
}
ifa6 = (struct in6_ifaddr *)ifa;
/* ok to use deprecated addresses? */
if (!ip6_use_deprecated) {
if (IFA6_IS_DEPRECATED(ifa6)) {
/* can't use this type */
return (NULL);
}
}
/* are we ok, with the current state of this address? */
if (ifa6->ia6_flags &
(IN6_IFF_DETACHED | IN6_IFF_NOTREADY | IN6_IFF_ANYCAST)) {
/* Can't use these types */
return (NULL);
}
/* Ok the address may be ok */
sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
*sin_local = *sin_loop = 0;
if ((ifa->ifa_ifp->if_type == IFT_LOOP) ||
(IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))) {
*sin_loop = 1;
}
if (!loopscope && *sin_loop) {
/* Its a loopback address and we don't have loop scope */
return (NULL);
}
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
/* we skip unspecifed addresses */
return (NULL);
}
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
*sin_local = 1;
}
if (!loc_scope && *sin_local) {
/*
* Its a link local address, and we don't have link local
* scope
*/
return (NULL);
}
return (sin6);
}
static struct sockaddr_in6 *
sctp_choose_v6_boundspecific_stcb(struct sctp_inpcb *inp,
struct sctp_tcb *stcb,
struct sctp_nets *net,
struct route *ro,
uint8_t loc_scope,
uint8_t loopscope,
int non_asoc_addr_ok)
{
/*
* Each endpoint has a list of local addresses associated with it.
* The address list is either a "negative list" i.e. those addresses
* that are NOT allowed to be used as a source OR a "postive list"
* i.e. those addresses that CAN be used.
*
* Its a negative list if asconf is allowed. What we do in this case is
* use the ep address list BUT we have to cross check it against the
* negative list.
*
* In the case where NO asconf is allowed, we have just a straight
* association level list that we must use to find a source address.
*/
struct sctp_laddr *laddr, *starting_point;
struct sockaddr_in6 *sin6;
int sin_loop, sin_local;
int start_at_beginning = 0;
struct ifnet *ifn;
struct ifaddr *ifa;
struct rtentry *rt;
rt = ro->ro_rt;
ifn = rt->rt_ifp;
if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF)) {
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Have a STCB - asconf allowed, not bound all have a netgative list\n");
}
#endif
/*
* first question, is the ifn we will emit on in our list,
* if so, we want that one.
*/
if (ifn) {
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
if (sctp_is_addr_in_ep(inp, ifa)) {
sin6 = sctp_is_v6_ifa_addr_acceptable(ifa, loopscope, loc_scope, &sin_loop, &sin_local);
if (sin6 == NULL)
continue;
if ((non_asoc_addr_ok == 0) &&
(sctp_is_addr_restricted(stcb, (struct sockaddr *)sin6))) {
/* on the no-no list */
continue;
}
return (sin6);
}
}
}
starting_point = stcb->asoc.last_used_address;
/* First try for matching scope */
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;
}
sin6 = sctp_is_v6_ifa_addr_acceptable(laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
if (sin6 == NULL)
continue;
if ((non_asoc_addr_ok == 0) && (sctp_is_addr_restricted(stcb, (struct sockaddr *)sin6))) {
/* on the no-no list */
continue;
}
/* is it of matching scope ? */
if ((loopscope == 0) &&
(loc_scope == 0) &&
(sin_loop == 0) &&
(sin_local == 0)) {
/* all of global scope we are ok with it */
return (sin6);
}
if (loopscope && sin_loop)
/* both on the loopback, thats ok */
return (sin6);
if (loc_scope && sin_local)
/* both local scope */
return (sin6);
}
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;
}
sin6 = sctp_is_v6_ifa_addr_acceptable(laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
if (sin6 == NULL)
continue;
if ((non_asoc_addr_ok == 0) && (sctp_is_addr_restricted(stcb, (struct sockaddr *)sin6))) {
/* on the no-no list */
continue;
}
return (sin6);
}
if (start_at_beginning == 0) {
stcb->asoc.last_used_address = NULL;
goto sctp_from_the_top2;
}
} else {
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Have a STCB - no asconf allowed, not bound all have a postive list\n");
}
#endif
/* First try for interface output match */
LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
sctp_nxt_addr) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
sin6 = sctp_is_v6_ifa_addr_acceptable(laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
if (sin6 == NULL)
continue;
/*
* first question, is laddr->ifa an address
* associated with the emit interface
*/
if (ifn) {
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
if (laddr->ifa == ifa) {
sin6 = (struct sockaddr_in6 *)laddr->ifa->ifa_addr;
return (sin6);
}
if (sctp_cmpaddr(ifa->ifa_addr, laddr->ifa->ifa_addr) == 1) {
sin6 = (struct sockaddr_in6 *)laddr->ifa->ifa_addr;
return (sin6);
}
}
}
}
/* Next try for matching scope */
LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
sctp_nxt_addr) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
sin6 = sctp_is_v6_ifa_addr_acceptable(laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
if (sin6 == NULL)
continue;
if ((loopscope == 0) &&
(loc_scope == 0) &&
(sin_loop == 0) &&
(sin_local == 0)) {
/* all of global scope we are ok with it */
return (sin6);
}
if (loopscope && sin_loop)
/* both on the loopback, thats ok */
return (sin6);
if (loc_scope && sin_local)
/* both local scope */
return (sin6);
}
/* ok, now try for a higher scope in the source address */
/* First try for matching scope */
LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
sctp_nxt_addr) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
sin6 = sctp_is_v6_ifa_addr_acceptable(laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
if (sin6 == NULL)
continue;
return (sin6);
}
}
RTFREE(ro->ro_rt);
ro->ro_rt = NULL;
return (NULL);
}
static struct sockaddr_in6 *
sctp_choose_v6_boundspecific_inp(struct sctp_inpcb *inp,
struct route *ro,
uint8_t loc_scope,
uint8_t loopscope)
{
/*
* Here we are bound specific and have only an inp. We must find an
* address that is bound that we can give out as a src address. We
* prefer two addresses of same scope if we can find them that way.
*/
struct sctp_laddr *laddr;
struct sockaddr_in6 *sin6;
struct ifnet *ifn;
struct ifaddr *ifa;
int sin_loop, sin_local;
struct rtentry *rt;
/*
* first question, is the ifn we will emit on in our list, if so, we
* want that one.
*/
rt = ro->ro_rt;
ifn = rt->rt_ifp;
if (ifn) {
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
sin6 = sctp_is_v6_ifa_addr_acceptable(ifa, loopscope, loc_scope, &sin_loop, &sin_local);
if (sin6 == NULL)
continue;
if (sctp_is_addr_in_ep(inp, ifa)) {
return (sin6);
}
}
}
for (laddr = LIST_FIRST(&inp->sctp_addr_list);
laddr && (laddr != inp->next_addr_touse);
laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
sin6 = sctp_is_v6_ifa_addr_acceptable(laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
if (sin6 == NULL)
continue;
if ((loopscope == 0) &&
(loc_scope == 0) &&
(sin_loop == 0) &&
(sin_local == 0)) {
/* all of global scope we are ok with it */
return (sin6);
}
if (loopscope && sin_loop)
/* both on the loopback, thats ok */
return (sin6);
if (loc_scope && sin_local)
/* both local scope */
return (sin6);
}
/*
* if we reach here, we could not find two addresses of the same
* scope to give out. Lets look for any higher level scope for a
* source address.
*/
for (laddr = LIST_FIRST(&inp->sctp_addr_list);
laddr && (laddr != inp->next_addr_touse);
laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
sin6 = sctp_is_v6_ifa_addr_acceptable(laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
if (sin6 == NULL)
continue;
return (sin6);
}
/* no address bound can be a source for the destination */
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Src address selection for EP, no acceptable src address found for address\n");
}
#endif
RTFREE(ro->ro_rt);
ro->ro_rt = NULL;
return (NULL);
}
static struct sockaddr_in6 *
sctp_select_v6_nth_addr_from_ifn_boundall(struct ifnet *ifn, struct sctp_tcb *stcb, int non_asoc_addr_ok, uint8_t loopscope,
uint8_t loc_scope, int cur_addr_num, int match_scope)
{
struct ifaddr *ifa;
struct sockaddr_in6 *sin6;
int sin_loop, sin_local;
int num_eligible_addr = 0;
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
sin6 = sctp_is_v6_ifa_addr_acceptable(ifa, loopscope, loc_scope, &sin_loop, &sin_local);
if (sin6 == NULL)
continue;
if (stcb) {
if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin6)) {
/*
* It is restricted for some reason..
* probably not yet added.
*/
continue;
}
}
if (match_scope) {
/* Here we are asked to match scope if possible */
if (loopscope && sin_loop)
/* src and destination are loopback scope */
return (sin6);
if (loc_scope && sin_local)
/* src and destination are local scope */
return (sin6);
if ((loopscope == 0) &&
(loc_scope == 0) &&
(sin_loop == 0) &&
(sin_local == 0)) {
/* src and destination are global scope */
return (sin6);
}
continue;
}
if (num_eligible_addr == cur_addr_num) {
/* this is it */
return (sin6);
}
num_eligible_addr++;
}
return (NULL);
}
static int
sctp_count_v6_num_eligible_boundall(struct ifnet *ifn, struct sctp_tcb *stcb,
int non_asoc_addr_ok, uint8_t loopscope, uint8_t loc_scope)
{
struct ifaddr *ifa;
struct sockaddr_in6 *sin6;
int num_eligible_addr = 0;
int sin_loop, sin_local;
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
sin6 = sctp_is_v6_ifa_addr_acceptable(ifa, loopscope, loc_scope, &sin_loop, &sin_local);
if (sin6 == NULL)
continue;
if (stcb) {
if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin6)) {
/*
* It is restricted for some reason..
* probably not yet added.
*/
continue;
}
}
num_eligible_addr++;
}
return (num_eligible_addr);
}
static struct sockaddr_in6 *
sctp_choose_v6_boundall(struct sctp_inpcb *inp,
struct sctp_tcb *stcb,
struct sctp_nets *net,
struct route *ro,
uint8_t loc_scope,
uint8_t loopscope,
int non_asoc_addr_ok)
{
/*
* Ok, we are bound all SO any address is ok to use as long as it is
* NOT in the negative list.
*/
int num_eligible_addr;
int cur_addr_num = 0;
int started_at_beginning = 0;
int match_scope_prefered;
/*
* first question is, how many eligible addresses are there for the
* destination ifn that we are using that are within the proper
* scope?
*/
struct ifnet *ifn;
struct sockaddr_in6 *sin6;
struct rtentry *rt;
rt = ro->ro_rt;
ifn = rt->rt_ifp;
if (net) {
cur_addr_num = net->indx_of_eligible_next_to_use;
}
if (cur_addr_num == 0) {
match_scope_prefered = 1;
} else {
match_scope_prefered = 0;
}
num_eligible_addr = sctp_count_v6_num_eligible_boundall(ifn, stcb, non_asoc_addr_ok, loopscope, loc_scope);
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Found %d eligible source addresses\n", num_eligible_addr);
}
#endif
if (num_eligible_addr == 0) {
/*
* no eligible addresses, we must use some other interface
* address if we can find one.
*/
goto bound_all_v6_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_eligible_addr) {
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...
*/
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("cur_addr_num:%d match_scope_prefered:%d select it\n",
cur_addr_num, match_scope_prefered);
}
#endif
sin6 = sctp_select_v6_nth_addr_from_ifn_boundall(ifn, stcb, non_asoc_addr_ok, loopscope,
loc_scope, cur_addr_num, match_scope_prefered);
if (match_scope_prefered && (sin6 == NULL)) {
/* retry without the preference for matching scope */
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("retry with no match_scope_prefered\n");
}
#endif
sin6 = sctp_select_v6_nth_addr_from_ifn_boundall(ifn, stcb, non_asoc_addr_ok, loopscope,
loc_scope, cur_addr_num, 0);
}
if (sin6) {
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Selected address %d ifn:%p for the route\n", cur_addr_num, ifn);
}
#endif
if (net) {
/* store so we get the next one */
if (cur_addr_num < 255)
net->indx_of_eligible_next_to_use = cur_addr_num + 1;
else
net->indx_of_eligible_next_to_use = 0;
}
return (sin6);
}
num_eligible_addr = 0;
bound_all_v6_plan_b:
/*
* ok, if we reach here we either fell through due to something
* changing during an interupt (unlikely) or we have NO eligible
* source addresses for the ifn of the route (most likely). We must
* look at all the other interfaces EXCEPT rt->rt_ifp and do the
* same game.
*/
if (inp->next_ifn_touse == NULL) {
started_at_beginning = 1;
inp->next_ifn_touse = TAILQ_FIRST(&ifnet);
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Start at first IFN:%p\n", inp->next_ifn_touse);
}
#endif
} else {
inp->next_ifn_touse = TAILQ_NEXT(inp->next_ifn_touse, if_list);
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Resume at IFN:%p\n", inp->next_ifn_touse);
}
#endif
if (inp->next_ifn_touse == NULL) {
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("IFN Resets\n");
}
#endif
started_at_beginning = 1;
inp->next_ifn_touse = TAILQ_FIRST(&ifnet);
}
}
for (ifn = inp->next_ifn_touse; ifn;
ifn = TAILQ_NEXT(ifn, if_list)) {
if (loopscope == 0 && ifn->if_type == IFT_LOOP) {
/* wrong base scope */
continue;
}
if (loc_scope && (ifn->if_index != loc_scope)) {
/*
* by definition the scope (from to->sin6_scopeid)
* must match that of the interface. If not then we
* could pick a wrong scope for the address.
* Ususally we don't hit plan-b since the route
* handles this. However we can hit plan-b when we
* send to local-host so the route is the loopback
* interface, but the destination is a link local.
*/
continue;
}
if (ifn == rt->rt_ifp) {
/* already looked at this guy */
continue;
}
/*
* Address rotation will only work when we are not rotating
* sourced interfaces and are using the interface of the
* route. We would need to have a per interface index in
* order to do proper rotation.
*/
num_eligible_addr = sctp_count_v6_num_eligible_boundall(ifn, stcb, non_asoc_addr_ok, loopscope, loc_scope);
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("IFN:%p has %d eligible\n", ifn, num_eligible_addr);
}
#endif
if (num_eligible_addr == 0) {
/* none we can use */
continue;
}
/*
* 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..
*/
inp->next_ifn_touse = ifn;
/*
* select the first one we can find with perference for
* matching scope.
*/
sin6 = sctp_select_v6_nth_addr_from_ifn_boundall(ifn, stcb, non_asoc_addr_ok, loopscope, loc_scope, 0, 1);
if (sin6 == NULL) {
/*
* can't find one with matching scope how about a
* source with higher scope
*/
sin6 = sctp_select_v6_nth_addr_from_ifn_boundall(ifn, stcb, non_asoc_addr_ok, loopscope, loc_scope, 0, 0);
if (sin6 == NULL)
/* Hmm, can't find one in the interface now */
continue;
}
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Selected the %d'th address of ifn:%p\n",
cur_addr_num,
ifn);
}
#endif
return (sin6);
}
if (started_at_beginning == 0) {
/*
* we have not been through all of them yet, force us to go
* through them all.
*/
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Force a recycle\n");
}
#endif
inp->next_ifn_touse = NULL;
goto bound_all_v6_plan_b;
}
RTFREE(ro->ro_rt);
ro->ro_rt = NULL;
return (NULL);
}
/* stcb and net may be NULL */
struct in6_addr
sctp_ipv6_source_address_selection(struct sctp_inpcb *inp,
struct sctp_tcb *stcb, struct route *ro, struct sctp_nets *net,
int non_asoc_addr_ok)
{
struct in6_addr ans;
struct sockaddr_in6 *rt_addr;
uint8_t loc_scope, loopscope;
struct sockaddr_in6 *to = (struct sockaddr_in6 *)&ro->ro_dst;
/*
* This routine is tricky standard v6 src address selection cannot
* take into account what we have bound etc, so we can't use it.
*
* Instead here is what we must do: 1) Make sure we have a route, if we
* don't have a route we can never reach the peer. 2) Once we have a
* route, determine the scope of the route. Link local, loopback or
* global. 3) Next we divide into three types. Either we are bound
* all.. which means we want to use one of the addresses of the
* interface we are going out. <or> 4a) We have not stcb, which
* means we are using the specific addresses bound on an inp, in
* this case we are similar to the stcb case (4b below) accept the
* list is always a positive list.<or> 4b) We are bound specific
* with a stcb, which means we have a list of bound addresses and we
* must see if the ifn of the route is actually one of the bound
* addresses. If not, then we must rotate addresses amongst properly
* scoped bound addresses, if so we use the address of the
* interface. 5) Always, no matter which path we take through the
* above we must be sure the source address we use is allowed to be
* used. I.e. IN6_IFF_DETACHED, IN6_IFF_NOTREADY, and
* IN6_IFF_ANYCAST addresses cannot be used. 6) Addresses that are
* deprecated MAY be used if (!ip6_use_deprecated) { if
* (IFA6_IS_DEPRECATED(ifa6)) { skip the address } }
*/
/*** 1> determine route, if not already done */
if (ro->ro_rt == NULL) {
/*
* Need a route to cache.
*/
int scope_save;
scope_save = to->sin6_scope_id;
to->sin6_scope_id = 0;
rtalloc_ign(ro, 0UL);
to->sin6_scope_id = scope_save;
}
if (ro->ro_rt == NULL) {
/*
* no route to host. this packet is going no-where. We
* probably should make sure we arrange to send back an
* error.
*/
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("No route to host, this packet cannot be sent!\n");
}
#endif
memset(&ans, 0, sizeof(ans));
return (ans);
}
/*** 2a> determine scope for outbound address/route */
loc_scope = loopscope = 0;
/*
* We base our scope on the outbound packet scope and route, NOT the
* TCB (if there is one). This way in local scope we will only use a
* local scope src address when we send to a local address.
*/
if (IN6_IS_ADDR_LOOPBACK(&to->sin6_addr)) {
/*
* If the route goes to the loopback address OR the address
* is a loopback address, we are loopback scope.
*/
loc_scope = 0;
loopscope = 1;
if (net != NULL) {
/* mark it as local */
net->addr_is_local = 1;
}
} else if (IN6_IS_ADDR_LINKLOCAL(&to->sin6_addr)) {
if (to->sin6_scope_id)
loc_scope = to->sin6_scope_id;
else {
loc_scope = 1;
}
loopscope = 0;
}
/*
* now, depending on which way we are bound we call the appropriate
* routine to do steps 3-6
*/
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Destination address:");
sctp_print_address((struct sockaddr *)to);
}
#endif
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
rt_addr = sctp_choose_v6_boundall(inp, stcb, net, ro, loc_scope, loopscope, non_asoc_addr_ok);
} else {
if (stcb)
rt_addr = sctp_choose_v6_boundspecific_stcb(inp, stcb, net, ro, loc_scope, loopscope, non_asoc_addr_ok);
else
/*
* we can't have a non-asoc address since we have no
* association
*/
rt_addr = sctp_choose_v6_boundspecific_inp(inp, ro, loc_scope, loopscope);
}
if (rt_addr == NULL) {
/* no suitable address? */
struct in6_addr in6;
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("V6 packet will reach dead-end no suitable src address\n");
}
#endif
memset(&in6, 0, sizeof(in6));
return (in6);
}
#ifdef SCTP_DEBUG
if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
printf("Source address selected is:");
sctp_print_address((struct sockaddr *)rt_addr);
}
#endif
return (rt_addr->sin6_addr);
}
static
int
sctp_is_address_in_scope(struct ifaddr *ifa,
int ipv4_addr_legal,
int ipv6_addr_legal,
int loopback_scope,
int ipv4_local_scope,
int local_scope,
int site_scope)
{
if ((loopback_scope == 0) &&
(ifa->ifa_ifp) &&
(ifa->ifa_ifp->if_type == IFT_LOOP)) {
/*
* skip loopback if not in scope *
*/
return (0);
}
if ((ifa->ifa_addr->sa_family == AF_INET) && ipv4_addr_legal) {
struct sockaddr_in *sin;
sin = (struct sockaddr_in *)ifa->ifa_addr;
if (sin->sin_addr.s_addr == 0) {
/* not in scope , unspecified */
return (0);
}
if ((ipv4_local_scope == 0) &&
(IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
/* private address not in scope */
return (0);
}
} else if ((ifa->ifa_addr->sa_family == AF_INET6) && ipv6_addr_legal) {
struct sockaddr_in6 *sin6;
struct in6_ifaddr *ifa6;
ifa6 = (struct in6_ifaddr *)ifa;
/* ok to use deprecated addresses? */
if (!ip6_use_deprecated) {
if (ifa6->ia6_flags &
IN6_IFF_DEPRECATED) {
return (0);
}
}
if (ifa6->ia6_flags &
(IN6_IFF_DETACHED |
IN6_IFF_ANYCAST |
IN6_IFF_NOTREADY)) {
return (0);
}
sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
/* skip unspecifed addresses */
return (0);
}
if ( /* (local_scope == 0) && */
(IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))) {
return (0);
}
if ((site_scope == 0) &&
(IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) {
return (0);
}
} else {
return (0);
}
return (1);
}
static struct mbuf *
sctp_add_addr_to_mbuf(struct mbuf *m, struct ifaddr *ifa)
{
struct sctp_paramhdr *parmh;
struct mbuf *mret;
int len;
if (ifa->ifa_addr->sa_family == AF_INET) {
len = sizeof(struct sctp_ipv4addr_param);
} else if (ifa->ifa_addr->sa_family == AF_INET6) {
len = sizeof(struct sctp_ipv6addr_param);
} else {
/* unknown type */
return (m);
}
if (M_TRAILINGSPACE(m) >= len) {
/* easy side we just drop it on the end */
parmh = (struct sctp_paramhdr *)(SCTP_BUF_AT(m, SCTP_BUF_LEN(m)));
mret = m;
} else {
/* Need more space */
mret = m;
while (SCTP_BUF_NEXT(mret) != NULL) {
mret = SCTP_BUF_NEXT(mret);
}
SCTP_BUF_NEXT(mret) = sctp_get_mbuf_for_msg(len, 0, M_DONTWAIT, 1, MT_DATA);
if (SCTP_BUF_NEXT(mret) == NULL) {
/* We are hosed, can't add more addresses */
return (m);
}
mret = SCTP_BUF_NEXT(mret);
parmh = mtod(mret, struct sctp_paramhdr *);
}
/* now add the parameter */
if (ifa->ifa_addr->sa_family == AF_INET) {
struct sctp_ipv4addr_param *ipv4p;
struct sockaddr_in *sin;
sin = (struct sockaddr_in *)ifa->ifa_addr;
ipv4p = (struct sctp_ipv4addr_param *)parmh;
parmh->param_type = htons(SCTP_IPV4_ADDRESS);
parmh->param_length = htons(len);
ipv4p->addr = sin->sin_addr.s_addr;
SCTP_BUF_LEN(mret) += len;
} else if (ifa->ifa_addr->sa_family == AF_INET6) {
struct sctp_ipv6addr_param *ipv6p;
struct sockaddr_in6 *sin6;
sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
ipv6p = (struct sctp_ipv6addr_param *)parmh;
parmh->param_type = htons(SCTP_IPV6_ADDRESS);
parmh->param_length = htons(len);
memcpy(ipv6p->addr, &sin6->sin6_addr,
sizeof(ipv6p->addr));
/* clear embedded scope in the address */
in6_clearscope((struct in6_addr *)ipv6p->addr);
SCTP_BUF_LEN(mret) += len;
} else {
return (m);
}
return (mret);
}
struct mbuf *
sctp_add_addresses_to_i_ia(struct sctp_inpcb *inp, struct sctp_scoping *scope, struct mbuf *m_at, int cnt_inits_to)
{
int cnt;
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
struct ifnet *ifn;
struct ifaddr *ifa;
cnt = cnt_inits_to;
TAILQ_FOREACH(ifn, &ifnet, if_list) {
if ((scope->loopback_scope == 0) &&
(ifn->if_type == IFT_LOOP)) {
/*
* Skip loopback devices if loopback_scope
* not set
*/
continue;
}
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
if (sctp_is_address_in_scope(ifa,
scope->ipv4_addr_legal,
scope->ipv6_addr_legal,
scope->loopback_scope,
scope->ipv4_local_scope,
scope->local_scope,
scope->site_scope) == 0) {
continue;
}
cnt++;
}
}
if (cnt > 1) {
TAILQ_FOREACH(ifn, &ifnet, if_list) {
if ((scope->loopback_scope == 0) &&
(ifn->if_type == IFT_LOOP)) {
/*
* Skip loopback devices if
* loopback_scope not set
*/
continue;
}
TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
if (sctp_is_address_in_scope(ifa,
scope->ipv4_addr_legal,
scope->ipv6_addr_legal,
scope->loopback_scope,
scope->ipv4_local_scope,
scope->local_scope,
scope->site_scope) == 0) {
continue;
}
m_at = sctp_add_addr_to_mbuf(m_at, ifa);
}
}
}
} else {
struct sctp_laddr *laddr;
int cnt;
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->ifa_addr == NULL)
continue;
if (sctp_is_address_in_scope(laddr->ifa,
scope->ipv4_addr_legal,
scope->ipv6_addr_legal,
scope->loopback_scope,
scope->ipv4_local_scope,
scope->local_scope,
scope->site_scope) == 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) {
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
if (laddr->ifa == NULL) {
continue;
}
if (laddr->ifa->ifa_addr == NULL) {
continue;
}
if (sctp_is_address_in_scope(laddr->ifa,
scope->ipv4_addr_legal,
scope->ipv6_addr_legal,
scope->loopback_scope,
scope->ipv4_local_scope,
scope->local_scope,
scope->site_scope) == 0) {
continue;
}
m_at = sctp_add_addr_to_mbuf(m_at, laddr->ifa);
}
}
}
return (m_at);
}