diff options
Diffstat (limited to 'sys/netinet/sctp_bsd_addr.c')
-rw-r--r-- | sys/netinet/sctp_bsd_addr.c | 2047 |
1 files changed, 214 insertions, 1833 deletions
diff --git a/sys/netinet/sctp_bsd_addr.c b/sys/netinet/sctp_bsd_addr.c index fb33fa8..4f83668 100644 --- a/sys/netinet/sctp_bsd_addr.c +++ b/sys/netinet/sctp_bsd_addr.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2001-2006, Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2001-2007, 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: @@ -45,1925 +45,306 @@ __FBSDID("$FreeBSD$"); #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. - */ +#include <sys/unistd.h> #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) +#if defined(SCTP_USE_THREAD_BASED_ITERATOR) +void +sctp_wakeup_iterator(void) { - 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); + wakeup(&sctppcbinfo.iterator_running); } -/* - * 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) +static void +sctp_iterator_thread(void *v) { - 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); + SCTP_IPI_ITERATOR_WQ_LOCK(); + sctppcbinfo.iterator_running = 0; + while (1) { + msleep(&sctppcbinfo.iterator_running, + &sctppcbinfo.ipi_iterator_wq_mtx, + 0, "waiting_for_work", 0); + sctp_iterator_worker(); } - 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) +void +sctp_startup_iterator(void) { - 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); + int ret; + + ret = kthread_create(sctp_iterator_thread, + (void *)NULL, + &sctppcbinfo.thread_proc, + RFPROC, + SCTP_KTHREAD_PAGES, + SCTP_KTRHEAD_NAME); } - - -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) +void +sctp_gather_internal_ifa_flags(struct sctp_ifa *ifa) { - /* - * 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); + struct in6_ifaddr *ifa6; - } - 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; + ifa6 = (struct in6_ifaddr *)ifa->ifa; + ifa->flags = ifa6->ia6_flags; + if (!ip6_use_deprecated) { + if (ifa->flags & + IN6_IFF_DEPRECATED) { + ifa->localifa_flags |= SCTP_ADDR_IFA_UNUSEABLE; + } else { + ifa->localifa_flags &= ~SCTP_ADDR_IFA_UNUSEABLE; } } 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; + ifa->localifa_flags &= ~SCTP_ADDR_IFA_UNUSEABLE; } - 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); + if (ifa->flags & + (IN6_IFF_DETACHED | + IN6_IFF_ANYCAST | + IN6_IFF_NOTREADY)) { + ifa->localifa_flags |= SCTP_ADDR_IFA_UNUSEABLE; } else { - RTFREE(ro->ro_rt); - ro->ro_rt = NULL; - memset(&ans, 0, sizeof(ans)); - return (ans); + ifa->localifa_flags &= ~SCTP_ADDR_IFA_UNUSEABLE; } } -/* 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) +static uint32_t +sctp_is_desired_interface_type(struct ifaddr *ifa) { - 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); + int result; + + /* check the interface type to see if it's one we care about */ + switch (ifa->ifa_ifp->if_type) { + case IFT_ETHER: + case IFT_ISO88023: + case IFT_ISO88024: + case IFT_ISO88025: + case IFT_ISO88026: + case IFT_STARLAN: + case IFT_P10: + case IFT_P80: + case IFT_HY: + case IFT_FDDI: + case IFT_XETHER: + case IFT_ISDNBASIC: + case IFT_ISDNPRIMARY: + case IFT_PTPSERIAL: + case IFT_PPP: + case IFT_LOOP: + case IFT_SLIP: + case IFT_IP: + case IFT_IPOVERCDLC: + case IFT_IPOVERCLAW: + case IFT_VIRTUALIPADDRESS: + result = 1; + break; + default: + result = 0; + } + + return (result); } - - -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) +static void +sctp_init_ifns_for_vrf(int vrfid) { /* - * 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. + * Here we must apply ANY locks needed by the IFN we access and also + * make sure we lock any IFA that exists as we float through the + * list of IFA's */ - 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); + struct in6_ifaddr *ifa6; + struct sctp_ifa *sctp_ifa; + uint32_t ifa_flags; - } - 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 */ + TAILQ_FOREACH(ifn, &ifnet, if_list) { + TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) { + if (ifa->ifa_addr == NULL) { 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 */ + if ((ifa->ifa_addr->sa_family != AF_INET) && + (ifa->ifa_addr->sa_family != AF_INET6) + ) { + /* non inet/inet6 skip */ 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); - } + if (ifa->ifa_addr->sa_family == AF_INET6) { + ifa6 = (struct in6_ifaddr *)ifa; + ifa_flags = ifa6->ia6_flags; + if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) { + /* skip unspecifed addresses */ + continue; + } + } else if (ifa->ifa_addr->sa_family == AF_INET) { + if (((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr == 0) { + continue; } } - } - /* 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) + if (sctp_is_desired_interface_type(ifa) == 0) { + /* non desired type */ 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; + if ((ifa->ifa_addr->sa_family == AF_INET6) || + (ifa->ifa_addr->sa_family == AF_INET)) { + if (ifa->ifa_addr->sa_family == AF_INET6) { + ifa6 = (struct in6_ifaddr *)ifa; + ifa_flags = ifa6->ia6_flags; + } else { + ifa_flags = 0; + } + sctp_ifa = sctp_add_addr_to_vrf(vrfid, + (void *)ifn, + ifn->if_index, + ifn->if_type, + ifn->if_xname, + (void *)ifa, + ifa->ifa_addr, + ifa_flags + ); + if (sctp_ifa) { + sctp_ifa->localifa_flags &= ~SCTP_ADDR_DEFER_USE; + } } - 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; +void +sctp_init_vrf_list(int vrfid) +{ + if (vrfid > SCTP_MAX_VRF_ID) + /* can't do that */ + return; - 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); + /* Don't care about return here */ + (void)sctp_allocate_vrf(vrfid); - } /* - * 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. + * Now we need to build all the ifn's for this vrf and there + * addresses */ - 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); + sctp_init_ifns_for_vrf(vrfid); } +static uint8_t first_time = 0; -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) +void +sctp_addr_change(struct ifaddr *ifa, int cmd) { - /* - * 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; + struct sctp_laddr *wi; + struct sctp_ifa *ifap = NULL; + uint32_t ifa_flags = 0; + struct in6_ifaddr *ifa6; - 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... + * BSD only has one VRF, if this changes we will need to hook in the + * right things here to get the id to pass to the address managment + * routine. */ -#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); + if (first_time == 0) { + /* Special test to see if my ::1 will showup with this */ + first_time = 1; + sctp_init_ifns_for_vrf(SCTP_DEFAULT_VRFID); } -#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 ((cmd != RTM_ADD) && (cmd != RTM_DELETE)) { + /* don't know what to do with this */ + return; } - 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); + if (ifa->ifa_addr == NULL) { + return; } - 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); - } + if ((ifa->ifa_addr->sa_family != AF_INET) && + (ifa->ifa_addr->sa_family != AF_INET6) + ) { + /* non inet/inet6 skip */ + return; } - 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; + if (ifa->ifa_addr->sa_family == AF_INET6) { + ifa6 = (struct in6_ifaddr *)ifa; + ifa_flags = ifa6->ia6_flags; + if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) { + /* skip unspecifed addresses */ + return; } -#ifdef SCTP_DEBUG - if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) { - printf("Selected the %d'th address of ifn:%p\n", - cur_addr_num, - ifn); + } else if (ifa->ifa_addr->sa_family == AF_INET) { + if (((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr == 0) { + return; } -#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; + if (sctp_is_desired_interface_type(ifa) == 0) { + /* non desired type */ + return; } - 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) { + if (cmd == RTM_ADD) { + ifap = sctp_add_addr_to_vrf(SCTP_DEFAULT_VRFID, (void *)ifa->ifa_ifp, + ifa->ifa_ifp->if_index, ifa->ifa_ifp->if_type, + ifa->ifa_ifp->if_xname, + (void *)ifa, ifa->ifa_addr, ifa_flags); /* - * Need a route to cache. + * Bump up the refcount so that when the timer completes it + * will drop back down. */ - int scope_save; + if (ifap) + atomic_add_int(&ifap->refcount, 1); - scope_save = to->sin6_scope_id; - to->sin6_scope_id = 0; + } else if (cmd == RTM_DELETE) { - rtalloc_ign(ro, 0UL); - to->sin6_scope_id = scope_save; - } - if (ro->ro_rt == NULL) { + ifap = sctp_del_addr_from_vrf(SCTP_DEFAULT_VRFID, ifa->ifa_addr, ifa->ifa_ifp->if_index); /* - * no route to host. this packet is going no-where. We - * probably should make sure we arrange to send back an - * error. + * We don't bump refcount here so when it completes the + * final delete will happen. */ -#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 (ifap == NULL) + return; - if (IN6_IS_ADDR_LOOPBACK(&to->sin6_addr)) { + wi = SCTP_ZONE_GET(sctppcbinfo.ipi_zone_laddr, struct sctp_laddr); + if (wi == NULL) { /* - * If the route goes to the loopback address OR the address - * is a loopback address, we are loopback scope. + * Gak, what can we do? We have lost an address change can + * you say HOSED? */ - 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"); + if (sctp_debug_on & SCTP_DEBUG_PCB1) { + printf("Lost and address change ???\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); -} +#endif /* SCTP_DEBUG */ - -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); + /* Opps, must decrement the count */ + sctp_free_ifa(ifap); + return; } - 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); + SCTP_INCR_LADDR_COUNT(); + bzero(wi, sizeof(*wi)); + wi->ifa = ifap; + if (cmd == RTM_ADD) { + wi->action = SCTP_ADD_IP_ADDRESS; + } else if (cmd == RTM_DELETE) { + wi->action = SCTP_DEL_IP_ADDRESS; } - 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); + SCTP_IPI_ITERATOR_WQ_LOCK(); + /* + * Should this really be a tailq? As it is we will process the + * newest first :-0 + */ + LIST_INSERT_HEAD(&sctppcbinfo.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_IPI_ITERATOR_WQ_UNLOCK(); } |