diff options
Diffstat (limited to 'sys/netinet/sctp_output.c')
-rw-r--r-- | sys/netinet/sctp_output.c | 1471 |
1 files changed, 1350 insertions, 121 deletions
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index 7572130..883f272 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include <netinet/sctp_os.h> #include <sys/proc.h> #include <netinet/sctp_var.h> +#include <netinet/sctp_sysctl.h> #include <netinet/sctp_header.h> #include <netinet/sctp_pcb.h> #include <netinet/sctputil.h> @@ -48,11 +49,6 @@ __FBSDID("$FreeBSD$"); #include <netinet/sctp_indata.h> #include <netinet/sctp_bsd_addr.h> -#ifdef SCTP_DEBUG -extern uint32_t sctp_debug_on; - -#endif - #define SCTP_MAX_GAPS_INARRAY 4 @@ -1860,9 +1856,1188 @@ struct sack_track sack_array[256] = { }; +int +sctp_is_address_in_scope(struct sctp_ifa *ifa, + int ipv4_addr_legal, + int ipv6_addr_legal, + int loopback_scope, + int ipv4_local_scope, + int local_scope, + int site_scope, + int do_update) +{ + if ((loopback_scope == 0) && + (ifa->ifn_p) && SCTP_IFN_IS_IFT_LOOP(ifa->ifn_p)) { + /* + * skip loopback if not in scope * + */ + return (0); + } + if ((ifa->address.sa.sa_family == AF_INET) && ipv4_addr_legal) { + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)&ifa->address.sin; + 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->address.sa.sa_family == AF_INET6) && ipv6_addr_legal) { + struct sockaddr_in6 *sin6; + + /* + * Must update the flags, bummer, which means any IFA locks + * must now be applied HERE <-> + */ + if (do_update) { + sctp_gather_internal_ifa_flags(ifa); + } + if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { + return (0); + } + /* ok to use deprecated addresses? */ + sin6 = (struct sockaddr_in6 *)&ifa->address.sin6; + 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 sctp_ifa *ifa) +{ + struct sctp_paramhdr *parmh; + struct mbuf *mret; + int len; + + if (ifa->address.sa.sa_family == AF_INET) { + len = sizeof(struct sctp_ipv4addr_param); + } else if (ifa->address.sa.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->address.sa.sa_family == AF_INET) { + struct sctp_ipv4addr_param *ipv4p; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)&ifa->address.sin; + 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->address.sa.sa_family == AF_INET6) { + struct sctp_ipv6addr_param *ipv6p; + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)&ifa->address.sin6; + 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) +{ + struct sctp_vrf *vrf = NULL; + int cnt, limit_out = 0, total_count; + uint32_t vrf_id; + + vrf_id = SCTP_DEFAULT_VRFID; + SCTP_IPI_ADDR_LOCK(); + vrf = sctp_find_vrf(vrf_id); + if (vrf == NULL) { + SCTP_IPI_ADDR_UNLOCK(); + return (m_at); + } + if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { + struct sctp_ifa *sctp_ifap; + struct sctp_ifn *sctp_ifnp; + + cnt = cnt_inits_to; + if (vrf->total_ifa_count > SCTP_COUNT_LIMIT) { + limit_out = 1; + cnt = SCTP_ADDRESS_LIMIT; + goto skip_count; + } + LIST_FOREACH(sctp_ifnp, &vrf->ifnlist, next_ifn) { + if ((scope->loopback_scope == 0) && + SCTP_IFN_IS_IFT_LOOP(sctp_ifnp)) { + /* + * Skip loopback devices if loopback_scope + * not set + */ + continue; + } + LIST_FOREACH(sctp_ifap, &sctp_ifnp->ifalist, next_ifa) { + if (sctp_is_address_in_scope(sctp_ifap, + scope->ipv4_addr_legal, + scope->ipv6_addr_legal, + scope->loopback_scope, + scope->ipv4_local_scope, + scope->local_scope, + scope->site_scope, 1) == 0) { + continue; + } + cnt++; + if (cnt > SCTP_ADDRESS_LIMIT) { + break; + } + } + if (cnt > SCTP_ADDRESS_LIMIT) { + break; + } + } +skip_count: + if (cnt > 1) { + total_count = 0; + LIST_FOREACH(sctp_ifnp, &vrf->ifnlist, next_ifn) { + cnt = 0; + if ((scope->loopback_scope == 0) && + SCTP_IFN_IS_IFT_LOOP(sctp_ifnp)) { + /* + * Skip loopback devices if + * loopback_scope not set + */ + continue; + } + LIST_FOREACH(sctp_ifap, &sctp_ifnp->ifalist, next_ifa) { + if (sctp_is_address_in_scope(sctp_ifap, + scope->ipv4_addr_legal, + scope->ipv6_addr_legal, + scope->loopback_scope, + scope->ipv4_local_scope, + scope->local_scope, + scope->site_scope, 0) == 0) { + continue; + } + m_at = sctp_add_addr_to_mbuf(m_at, sctp_ifap); + if (limit_out) { + cnt++; + total_count++; + if (cnt >= 2) { + /* + * two from each + * address + */ + break; + } + if (total_count > SCTP_ADDRESS_LIMIT) { + /* No more addresses */ + break; + } + } + } + } + } + } else { + struct sctp_laddr *laddr; + + cnt = cnt_inits_to; + /* First, how many ? */ + LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { + if (laddr->ifa == NULL) { + continue; + } + if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) + /* + * Address being deleted by the system, dont + * list. + */ + continue; + if (laddr->action == SCTP_DEL_IP_ADDRESS) { + /* + * Address being deleted on this ep don't + * list. + */ + continue; + } + 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, 1) == 0) { + continue; + } + cnt++; + } + if (cnt > SCTP_ADDRESS_LIMIT) { + limit_out = 1; + } + /* + * 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) { + cnt = 0; + if (laddr->ifa == NULL) { + continue; + } + if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) + 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) == 0) { + continue; + } + m_at = sctp_add_addr_to_mbuf(m_at, laddr->ifa); + cnt++; + if (cnt >= SCTP_ADDRESS_LIMIT) { + break; + } + } + } + } + SCTP_IPI_ADDR_UNLOCK(); + return (m_at); +} + +static struct sctp_ifa * +sctp_is_ifa_addr_prefered(struct sctp_ifa *ifa, + uint8_t dest_is_loop, + uint8_t dest_is_priv, + sa_family_t fam) +{ + uint8_t dest_is_global = 0; + + /* + * is_scope -> dest_is_priv is true if destination is a private + * address + */ + /* dest_is_loop is true if destination is a loopback addresses */ + + /* + * Here we determine if its a 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-v4 no-v6 ----------------------------------------- G | + * L | yes-v4 no-v6 ----------------------------------------- L + * | P | no ----------------------------------------- P | + * P | yes ----------------------------------------- G | + * P | no ----------------------------------------- L | G + * | no ----------------------------------------- P | G | + * no ----------------------------------------- G | G | + * yes ----------------------------------------- + */ + + if (ifa->address.sa.sa_family != fam) { + /* forget mis-matched family */ + return (NULL); + } + if ((dest_is_priv == 0) && (dest_is_loop == 0)) { + dest_is_global = 1; + } + /* Ok the address may be ok */ + if (fam == AF_INET6) { + /* ok to use deprecated addresses? */ + if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { + return (NULL); + } + if (ifa->src_is_priv) { + if (dest_is_loop) { + return (NULL); + } + } + if (ifa->src_is_glob) { + + if (dest_is_loop) { + return (NULL); + } + } + } + /* + * Now that we know what is what, implement or table this could in + * theory be done slicker (it used to be), but this is + * straightforward and easier to validate :-) + */ + if ((ifa->src_is_loop) && (dest_is_priv)) { + return (NULL); + } + if ((ifa->src_is_glob) && (dest_is_priv)) { + return (NULL); + } + if ((ifa->src_is_loop) && (dest_is_global)) { + return (NULL); + } + if ((ifa->src_is_priv) && (dest_is_global)) { + return (NULL); + } + /* its a prefered address */ + return (ifa); +} + +static struct sctp_ifa * +sctp_is_ifa_addr_acceptable(struct sctp_ifa *ifa, + uint8_t dest_is_loop, + uint8_t dest_is_priv, + sa_family_t fam) +{ + uint8_t dest_is_global = 0; + + + /* + * Here we determine if its a acceptable address. A acceptable + * address means it is the same scope or higher scope but we can + * allow for NAT which means its ok to have a global dest and a + * private src. + * + * L = loopback, P = private, G = global + * ----------------------------------------- src | dest | result + * ----------------------------------------- L | L | yes + * ----------------------------------------- P | L | + * yes-v4 no-v6 ----------------------------------------- G | + * L | yes ----------------------------------------- L | + * P | no ----------------------------------------- P | P + * | yes ----------------------------------------- G | P + * | yes - May not work ----------------------------------------- + * L | G | no ----------------------------------------- P + * | G | yes - May not work + * ----------------------------------------- G | G | yes + * ----------------------------------------- + */ + + if (ifa->address.sa.sa_family != fam) { + /* forget non matching family */ + return (NULL); + } + /* Ok the address may be ok */ + if ((dest_is_loop == 0) && (dest_is_priv == 0)) { + dest_is_global = 1; + } + if (fam == AF_INET6) { + /* ok to use deprecated addresses? */ + if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { + return (NULL); + } + if (ifa->src_is_priv) { + /* Special case, linklocal to loop */ + if (dest_is_loop) + return (NULL); + } + } + /* + * Now that we know what is what, implement or table this could in + * theory be done slicker (it used to be), but this is + * straightforward and easier to validate :-) + */ + + if ((ifa->src_is_loop == 0) && (dest_is_priv)) { + return (NULL); + } + if ((ifa->src_is_loop == 0) && (dest_is_global)) { + return (NULL); + } + /* its an acceptable address */ + return (ifa); +} + +int +sctp_is_addr_restricted(struct sctp_tcb *stcb, struct sctp_ifa *ifa) +{ + struct sctp_laddr *laddr; + + if (stcb == NULL) { + /* There are no restrictions, no TCB :-) */ + return (0); + } + LIST_FOREACH(laddr, &stcb->asoc.sctp_restricted_addrs, sctp_nxt_addr) { + if (laddr->ifa == NULL) { +#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) { + /* Yes it is on the list */ + return (1); + } + } + return (0); +} + + +int +sctp_is_addr_in_ep(struct sctp_inpcb *inp, struct sctp_ifa *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) && laddr->action == 0) + /* same pointer */ + return (1); + } + return (0); +} + + + +static struct sctp_ifa * +sctp_choose_boundspecific_inp(struct sctp_inpcb *inp, + struct route *ro, + uint32_t vrf_id, + int non_asoc_addr_ok, + uint8_t dest_is_priv, + uint8_t dest_is_loop, + sa_family_t fam) +{ + struct sctp_laddr *laddr, *starting_point; + void *ifn; + int resettotop = 0; + struct sctp_ifn *sctp_ifn; + struct sctp_ifa *sctp_ifa, *pass; + struct sctp_vrf *vrf; + uint32_t ifn_index; + + vrf = sctp_find_vrf(vrf_id); + if (vrf == NULL) + return (NULL); + + ifn = SCTP_GET_IFN_VOID_FROM_ROUTE(ro); + ifn_index = SCTP_GET_IF_INDEX_FROM_ROUTE(ro); + sctp_ifn = sctp_find_ifn(vrf, ifn, ifn_index); + /* + * first question, is the ifn we will emit on in our list, if so, we + * want such an address. Note that we first looked for a prefered + * address. + */ + if (sctp_ifn) { + /* is a prefered one on the interface we route out? */ + LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { + if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0)) + continue; + pass = sctp_is_ifa_addr_prefered(sctp_ifa, dest_is_loop, dest_is_priv, fam); + if (pass == NULL) + continue; + if (sctp_is_addr_in_ep(inp, pass)) { + atomic_add_int(&pass->refcount, 1); + return (pass); + } + } + } + /* + * ok, now we now need to find one on the list of the addresses. We + * can't get one on the emitting interface so lets find first a + * prefered one. If not that a acceptable one otherwise... we return + * NULL. + */ + starting_point = inp->next_addr_touse; +once_again: + if (inp->next_addr_touse == NULL) { + inp->next_addr_touse = LIST_FIRST(&inp->sctp_addr_list); + resettotop = 1; + } + for (laddr = inp->next_addr_touse; laddr; laddr = LIST_NEXT(laddr, sctp_nxt_addr)) { + if (laddr->ifa == NULL) { + /* address has been removed */ + continue; + } + pass = sctp_is_ifa_addr_prefered(laddr->ifa, dest_is_loop, dest_is_priv, fam); + if (pass == NULL) + continue; + atomic_add_int(&pass->refcount, 1); + return (pass); + } + if (resettotop == 0) { + inp->next_addr_touse = NULL; + goto once_again; + } + inp->next_addr_touse = starting_point; + resettotop = 0; +once_again_too: + if (inp->next_addr_touse == NULL) { + inp->next_addr_touse = LIST_FIRST(&inp->sctp_addr_list); + resettotop = 1; + } + /* ok, what about an acceptable address in the inp */ + for (laddr = inp->next_addr_touse; laddr; laddr = LIST_NEXT(laddr, sctp_nxt_addr)) { + if (laddr->ifa == NULL) { + /* address has been removed */ + continue; + } + pass = sctp_is_ifa_addr_acceptable(laddr->ifa, dest_is_loop, dest_is_priv, fam); + if (pass == NULL) + continue; + atomic_add_int(&pass->refcount, 1); + return (pass); + } + if (resettotop == 0) { + inp->next_addr_touse = NULL; + goto once_again_too; + } + /* + * no address bound can be a source for the destination we are in + * trouble + */ + return (NULL); +} + + + +static struct sctp_ifa * +sctp_choose_boundspecific_stcb(struct sctp_inpcb *inp, + struct sctp_tcb *stcb, + struct sctp_nets *net, + struct route *ro, + uint32_t vrf_id, + uint8_t dest_is_priv, + uint8_t dest_is_loop, + int non_asoc_addr_ok, + sa_family_t fam) +{ + struct sctp_laddr *laddr, *starting_point; + void *ifn; + struct sctp_ifn *sctp_ifn; + struct sctp_ifa *sctp_ifa, *pass; + uint8_t start_at_beginning = 0; + struct sctp_vrf *vrf; + uint32_t ifn_index; + + /* + * first question, is the ifn we will emit on in our list, if so, we + * want that one. + */ + vrf = sctp_find_vrf(vrf_id); + if (vrf == NULL) + return (NULL); + + ifn = SCTP_GET_IFN_VOID_FROM_ROUTE(ro); + ifn_index = SCTP_GET_IF_INDEX_FROM_ROUTE(ro); + sctp_ifn = sctp_find_ifn(vrf, ifn, ifn_index); + /* + * first question, is the ifn we will emit on in our list, if so, we + * want that one.. First we look for a prefered. Second we go for an + * acceptable. + */ + if (sctp_ifn) { + /* first try for an prefered address on the ep */ + LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { + if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0)) + continue; + if (sctp_is_addr_in_ep(inp, sctp_ifa)) { + pass = sctp_is_ifa_addr_prefered(sctp_ifa, dest_is_loop, dest_is_priv, fam); + if (pass == NULL) + continue; + if ((non_asoc_addr_ok == 0) && + (sctp_is_addr_restricted(stcb, pass))) { + /* on the no-no list */ + continue; + } + atomic_add_int(&pass->refcount, 1); + return (pass); + } + } + /* next try for an acceptable address on the ep */ + LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { + if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0)) + continue; + if (sctp_is_addr_in_ep(inp, sctp_ifa)) { + pass = sctp_is_ifa_addr_acceptable(sctp_ifa, dest_is_loop, dest_is_priv, fam); + if (pass == NULL) + continue; + if ((non_asoc_addr_ok == 0) && + (sctp_is_addr_restricted(stcb, pass))) { + /* on the no-no list */ + continue; + } + atomic_add_int(&pass->refcount, 1); + return (pass); + } + } + + } + /* + * 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; +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; + } + pass = sctp_is_ifa_addr_prefered(laddr->ifa, dest_is_loop, dest_is_priv, fam); + if (pass == NULL) + continue; + if ((non_asoc_addr_ok == 0) && + (sctp_is_addr_restricted(stcb, pass))) { + /* on the no-no list */ + continue; + } + stcb->asoc.last_used_address = laddr; + atomic_add_int(&pass->refcount, 1); + return (pass); + + } + 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; + } + pass = sctp_is_ifa_addr_acceptable(laddr->ifa, dest_is_loop, dest_is_priv, fam); + if (pass == NULL) + continue; + if ((non_asoc_addr_ok == 0) && + (sctp_is_addr_restricted(stcb, pass))) { + /* on the no-no list */ + continue; + } + stcb->asoc.last_used_address = laddr; + atomic_add_int(&pass->refcount, 1); + return (pass); + } + if (start_at_beginning == 0) { + stcb->asoc.last_used_address = NULL; + goto sctp_from_the_top2; + } + return (NULL); +} + +static struct sctp_ifa * +sctp_select_nth_prefered_addr_from_ifn_boundall(struct sctp_ifn *ifn, + struct sctp_tcb *stcb, + int non_asoc_addr_ok, + uint8_t dest_is_loop, + uint8_t dest_is_priv, + int addr_wanted, + sa_family_t fam) +{ + struct sctp_ifa *ifa, *pass; + int num_eligible_addr = 0; + + LIST_FOREACH(ifa, &ifn->ifalist, next_ifa) { + if ((ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0)) + continue; + pass = sctp_is_ifa_addr_prefered(ifa, dest_is_loop, dest_is_priv, fam); + if (pass == NULL) + continue; + if (stcb) { + if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, pass)) { + /* + * It is restricted for some reason.. + * probably not yet added. + */ + continue; + } + } + if (num_eligible_addr >= addr_wanted) { + return (pass); + } + num_eligible_addr++; + } + return (NULL); +} -extern int sctp_peer_chunk_oh; + +static int +sctp_count_num_prefered_boundall(struct sctp_ifn *ifn, + struct sctp_tcb *stcb, + int non_asoc_addr_ok, + uint8_t dest_is_loop, + uint8_t dest_is_priv, + sa_family_t fam) +{ + struct sctp_ifa *ifa, *pass; + int num_eligible_addr = 0; + + LIST_FOREACH(ifa, &ifn->ifalist, next_ifa) { + if ((ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0)) { + continue; + } + pass = sctp_is_ifa_addr_prefered(ifa, dest_is_loop, dest_is_priv, fam); + if (pass == NULL) { + continue; + } + if (stcb) { + if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, pass)) { + /* + * It is restricted for some reason.. + * probably not yet added. + */ + continue; + } + } + num_eligible_addr++; + } + return (num_eligible_addr); +} + +static struct sctp_ifa * +sctp_choose_boundall(struct sctp_inpcb *inp, + struct sctp_tcb *stcb, + struct sctp_nets *net, + struct route *ro, + uint32_t vrf_id, + uint8_t dest_is_priv, + uint8_t dest_is_loop, + int non_asoc_addr_ok, + sa_family_t fam) +{ + int cur_addr_num = 0, num_prefered = 0; + void *ifn; + struct sctp_ifn *sctp_ifn, *looked_at = NULL, *emit_ifn; + struct sctp_ifa *sctp_ifa, *pass; + uint32_t ifn_index; + struct sctp_vrf *vrf; + + /* + * For boundall we can use any address in the association. If + * non_asoc_addr_ok is set we can use any address (at least in + * theory). So we look for 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 :-<. + */ + vrf = sctp_find_vrf(vrf_id); + if (vrf == NULL) + return (NULL); + + ifn = SCTP_GET_IFN_VOID_FROM_ROUTE(ro); + ifn_index = SCTP_GET_IF_INDEX_FROM_ROUTE(ro); + + emit_ifn = looked_at = sctp_ifn = sctp_find_ifn(vrf, ifn, ifn_index); + if (sctp_ifn == NULL) { + /* ?? We don't have this guy ?? */ + goto bound_all_plan_b; + } + if (net) { + cur_addr_num = net->indx_of_eligible_next_to_use; + } + num_prefered = sctp_count_num_prefered_boundall(sctp_ifn, + stcb, + non_asoc_addr_ok, + dest_is_loop, + dest_is_priv, fam); +#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_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 + sctp_ifa = sctp_select_nth_prefered_addr_from_ifn_boundall(sctp_ifn, stcb, non_asoc_addr_ok, dest_is_loop, + dest_is_priv, cur_addr_num, fam); + + /* if sctp_ifa is NULL something changed??, fall to plan b. */ + if (sctp_ifa) { + atomic_add_int(&sctp_ifa->refcount, 1); + if (net) { + /* save off where the next one we will want */ + net->indx_of_eligible_next_to_use = cur_addr_num + 1; + } + return (sctp_ifa); + } + /* + * plan_b: Look at all interfaces and find a prefered address. If no + * prefered fall through to plan_c. + */ +bound_all_plan_b: + LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { + if (dest_is_loop == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { + /* wrong base scope */ + continue; + } + if ((sctp_ifn == looked_at) && looked_at) + /* already looked at this guy */ + continue; + num_prefered = sctp_count_num_prefered_boundall(sctp_ifn, stcb, non_asoc_addr_ok, + dest_is_loop, dest_is_priv, fam); +#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; + } +#ifdef SCTP_DEBUG + if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) { + printf("num prefered:%d on interface:%p cur_addr_num:%d\n", + num_prefered, + sctp_ifn, + cur_addr_num); + } +#endif + + /* + * 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; + } + pass = sctp_select_nth_prefered_addr_from_ifn_boundall(sctp_ifn, stcb, non_asoc_addr_ok, dest_is_loop, + dest_is_priv, cur_addr_num, fam); + if (pass == NULL) + continue; + if (net) { + net->indx_of_eligible_next_to_use = cur_addr_num + 1; +#ifdef SCTP_DEBUG + if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) { + printf("we selected %d\n", cur_addr_num); + printf("Source:"); + sctp_print_address(&pass->address.sa); + printf("Dest:"); + sctp_print_address(&net->ro._l_addr.sa); + } +#endif + } + atomic_add_int(&pass->refcount, 1); + return (pass); + + } + + /* + * plan_c: See if we have an acceptable address on the emit + * interface + */ +#ifdef SCTP_DEBUG + if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) { + if (net) { + printf("Plan C no prefered for Dest:"); + sctp_print_address(&net->ro._l_addr.sa); + } + } +#endif + + LIST_FOREACH(sctp_ifa, &emit_ifn->ifalist, next_ifa) { + if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0)) + continue; + pass = sctp_is_ifa_addr_acceptable(sctp_ifa, dest_is_loop, dest_is_priv, fam); + if (pass == NULL) + continue; + if (stcb) { + if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, pass)) { + /* + * It is restricted for some reason.. + * probably not yet added. + */ + continue; + } + } + atomic_add_int(&pass->refcount, 1); + return (pass); + } + + /* + * plan_d: We are in trouble. No prefered address on the emit + * interface. And not even a perfered address on all interfaces. Go + * out and see if we can find an acceptable address somewhere + * amongst all interfaces. + */ + LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { + if (dest_is_loop == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { + /* wrong base scope */ + continue; + } + if ((sctp_ifn == looked_at) && looked_at) + /* already looked at this guy */ + continue; + + LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { + if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0)) + continue; + pass = sctp_is_ifa_addr_acceptable(sctp_ifa, dest_is_loop, dest_is_priv, fam); + if (pass == NULL) + continue; + if (stcb) { + if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, pass)) { + /* + * It is restricted for some + * reason.. probably not yet added. + */ + continue; + } + } + atomic_add_int(&pass->refcount, 1); + return (pass); + } + } + /* + * Ok we can find NO address to source from that is not on our + * negative list and non_asoc_address is NOT ok, or its on our + * negative list. We cant source to it :-( + */ + return (NULL); +} + + + +/* tcb may be NULL */ +struct sctp_ifa * +sctp_source_address_selection(struct sctp_inpcb *inp, + struct sctp_tcb *stcb, + struct route *ro, + struct sctp_nets *net, + int non_asoc_addr_ok, uint32_t vrf_id) +{ + + struct sockaddr_in *to = (struct sockaddr_in *)&ro->ro_dst; + struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&ro->ro_dst; + struct sctp_ifa *answer; + uint8_t dest_is_priv, dest_is_loop; + int did_rtalloc = 0; + sa_family_t fam; + + /* + * 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 zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz + * For V4 ------------------------------------------ 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 ------------------------------------------ + * zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz For V6 + * ------------------------------------------ source * dest * + * result ----------------------------------------- <a> Linklocal * + * Global * ----------------------------------------- <b> + * Linklocal * Linklocal * No problem + * ----------------------------------------- <c> Global * + * Linklocal * Huh, How will this work? + * ----------------------------------------- <d> Global * + * Global * No Problem ------------------------------------------ + * zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz + * + * 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 IP + * 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 the bound addresses.. + * + */ + if (ro->ro_rt == NULL) { + /* + * Need a route to cache. + * + */ + rtalloc_ign(ro, 0UL); + did_rtalloc = 1; + } + if (ro->ro_rt == NULL) { + return (NULL); + } + fam = to->sin_family; + dest_is_priv = dest_is_loop = 0; + /* Setup our scopes for the destination */ + if (fam == AF_INET) { + /* Scope based on outbound address */ + if ((IN4_ISPRIVATE_ADDRESS(&to->sin_addr))) { + dest_is_priv = 1; + } else if (IN4_ISLOOPBACK_ADDRESS(&to->sin_addr)) { + dest_is_loop = 1; + if (net != NULL) { + /* mark it as local */ + net->addr_is_local = 1; + } + } + } else if (fam == AF_INET6) { + /* Scope based on outbound address */ + if (IN6_IS_ADDR_LOOPBACK(&to6->sin6_addr)) { + /* + * If the route goes to the loopback address OR the + * address is a loopback address, we are loopback + * scope. But we don't use dest_is_priv (link local + * addresses). + */ + dest_is_loop = 1; + if (net != NULL) { + /* mark it as local */ + net->addr_is_local = 1; + } + } else if (IN6_IS_ADDR_LINKLOCAL(&to6->sin6_addr)) { + dest_is_priv = 1; + } + } + 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. + */ + answer = sctp_choose_boundall(inp, stcb, net, ro, vrf_id, + dest_is_priv, + dest_is_loop, + non_asoc_addr_ok, + fam); + return (answer); + } + /* + * Three possiblities here: + * + * a) stcb is NULL, which means we operate only from the list of + * addresses (ifa's) bound to the endpoint 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) { + answer = sctp_choose_boundspecific_stcb(inp, stcb, net, ro, vrf_id, + dest_is_priv, dest_is_loop, non_asoc_addr_ok, fam); + + } else { + answer = sctp_choose_boundspecific_inp(inp, ro, vrf_id, non_asoc_addr_ok, dest_is_priv, dest_is_loop, fam); + + } + return (answer); +} static int sctp_find_cmsg(int c_type, void *data, struct mbuf *control, int cpsize) @@ -1914,10 +3089,7 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *control, int cpsize) } -extern int sctp_mbuf_threshold_count; - - -__inline struct mbuf * +struct mbuf * sctp_get_mbuf_for_msg(unsigned int space_needed, int want_header, int how, int allonebuf, int type) { @@ -2121,8 +3293,6 @@ sctp_get_ect(struct sctp_tcb *stcb, } } -extern int sctp_no_csum_on_loopback; - static int sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, struct sctp_tcb *stcb, /* may be NULL */ @@ -2156,12 +3326,20 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, uint32_t csum; int ret; unsigned int have_mtu; + uint32_t vrf_id; struct route *ro; + if ((net) && (net->dest_state & SCTP_ADDR_OUT_OF_SCOPE)) { sctp_m_freem(m); return (EFAULT); } + if (stcb == NULL) { + vrf_id = SCTP_DEFAULT_VRFID; + } else { + vrf_id = stcb->asoc.vrf_id; + } + /* fill in the HMAC digest for any AUTH chunk in the packet */ if ((auth != NULL) && (stcb != NULL)) { sctp_fill_hmac_digest_m(m, auth_offset, auth, stcb); @@ -2186,7 +3364,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } if (to->sa_family == AF_INET) { - struct ip *ip; + struct ip *ip = NULL; struct route iproute; uint8_t tos_value; @@ -2250,16 +3428,25 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, if (net) { if (net->src_addr_selected == 0) { /* Cache the source address */ - ((struct sockaddr_in *)&net->ro._s_addr)->sin_addr = sctp_ipv4_source_address_selection(inp, - stcb, - ro, net, out_of_asoc_ok); - if (ro->ro_rt) - net->src_addr_selected = 1; + net->ro._s_addr = sctp_source_address_selection(inp, stcb, + ro, net, out_of_asoc_ok, vrf_id); + if (net->ro._s_addr == NULL) { + /* No route to host */ + goto no_route; + } + net->src_addr_selected = 1; } - ip->ip_src = ((struct sockaddr_in *)&net->ro._s_addr)->sin_addr; + ip->ip_src = net->ro._s_addr->address.sin.sin_addr; } else { - ip->ip_src = sctp_ipv4_source_address_selection(inp, - stcb, ro, net, out_of_asoc_ok); + struct sctp_ifa *_lsrc; + + _lsrc = sctp_source_address_selection(inp, + stcb, ro, net, out_of_asoc_ok, vrf_id); + if (_lsrc == NULL) { + goto no_route; + } + ip->ip_src = _lsrc->address.sin.sin_addr; + sctp_free_ifa(_lsrc); } /* @@ -2273,22 +3460,30 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, /* * src addr selection failed to find a route (or * valid source addr), so we can't get there from - * here! + * here (yet)! */ + no_route: #ifdef SCTP_DEBUG if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) { - printf("low_level_output: dropped v4 packet- no valid source addr\n"); - printf("Destination was %x\n", (uint32_t) (ntohl(ip->ip_dst.s_addr))); + printf("low_level_output: dropped packet - no valid source addr\n"); + if (net) { + printf("Destination was "); + sctp_print_address(&net->ro._l_addr.sa); + } } #endif /* SCTP_DEBUG */ if (net) { - if ((net->dest_state & SCTP_ADDR_REACHABLE) && stcb) - sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, - stcb, - SCTP_FAILED_THRESHOLD, - (void *)net); - net->dest_state &= ~SCTP_ADDR_REACHABLE; - net->dest_state |= SCTP_ADDR_NOT_REACHABLE; + if (net->dest_state & SCTP_ADDR_CONFIRMED) { + if ((net->dest_state & SCTP_ADDR_REACHABLE) && stcb) { + printf("no route takes interface %p down\n", net); + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, + stcb, + SCTP_FAILED_THRESHOLD, + (void *)net); + net->dest_state &= ~SCTP_ADDR_REACHABLE; + net->dest_state |= SCTP_ADDR_NOT_REACHABLE; + } + } if (stcb) { if (net == stcb->asoc.primary_destination) { /* need a new primary */ @@ -2300,6 +3495,10 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, (struct sockaddr *)NULL, alt) == 0) { net->dest_state |= SCTP_ADDR_WAS_PRIMARY; + if (net->ro._s_addr) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + } net->src_addr_selected = 0; } } @@ -2349,8 +3548,10 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, #endif if (net == NULL) { /* free tempy routes */ - if (ro->ro_rt) + if (ro->ro_rt) { RTFREE(ro->ro_rt); + ro->ro_rt = NULL; + } } else { /* PMTU check versus smallest asoc MTU goes here */ if (ro->ro_rt != NULL) { @@ -2361,6 +3562,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } } else { /* route was freed */ + if (net->ro._s_addr && + net->src_addr_selected) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + } net->src_addr_selected = 0; } } @@ -2448,16 +3654,30 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, if (net) { if (net->src_addr_selected == 0) { /* Cache the source address */ - ((struct sockaddr_in6 *)&net->ro._s_addr)->sin6_addr = sctp_ipv6_source_address_selection(inp, - stcb, ro, net, out_of_asoc_ok); - - if (ro->ro_rt) - net->src_addr_selected = 1; + net->ro._s_addr = sctp_source_address_selection(inp, + stcb, + ro, + net, + out_of_asoc_ok, + vrf_id); + if (net->ro._s_addr == NULL) { +#ifdef SCTP_DEBUG + printf("V6:No route to host\n"); +#endif + goto no_route; + } + net->src_addr_selected = 1; } - lsa6->sin6_addr = ((struct sockaddr_in6 *)&net->ro._s_addr)->sin6_addr; + lsa6->sin6_addr = net->ro._s_addr->address.sin6.sin6_addr; } else { - lsa6->sin6_addr = sctp_ipv6_source_address_selection( - inp, stcb, ro, net, out_of_asoc_ok); + struct sctp_ifa *_lsrc; + + _lsrc = sctp_source_address_selection(inp, stcb, ro, net, out_of_asoc_ok, vrf_id); + if (_lsrc == NULL) { + goto no_route; + } + lsa6->sin6_addr = _lsrc->address.sin6.sin6_addr; + sctp_free_ifa(_lsrc); } lsa6->sin6_port = inp->sctp_lport; @@ -2467,38 +3687,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, * valid source addr), so we can't get there from * here! */ -#ifdef SCTP_DEBUG - if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) { - printf("low_level_output: dropped v6 pkt- no valid source addr\n"); - } -#endif - sctp_m_freem(o_pak); - if (net) { - if ((net->dest_state & SCTP_ADDR_REACHABLE) && stcb) - sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, - stcb, - SCTP_FAILED_THRESHOLD, - (void *)net); - net->dest_state &= ~SCTP_ADDR_REACHABLE; - net->dest_state |= SCTP_ADDR_NOT_REACHABLE; - if (stcb) { - if (net == stcb->asoc.primary_destination) { - /* need a new primary */ - struct sctp_nets *alt; - - alt = sctp_find_alternate_net(stcb, net, 0); - if (alt != net) { - if (sctp_set_primary_addr(stcb, - (struct sockaddr *)NULL, - alt) == 0) { - net->dest_state |= SCTP_ADDR_WAS_PRIMARY; - net->src_addr_selected = 0; - } - } - } - } - } - return (EHOSTUNREACH); + goto no_route; } /* * XXX: sa6 may not have a valid sin6_scope_id in the @@ -2565,8 +3754,9 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, #endif /* SCTP_DEBUG_OUTPUT */ SCTP_STAT_INCR(sctps_sendpackets); SCTP_STAT_INCR_COUNTER64(sctps_outpackets); - if (ret) + if (ret) { SCTP_STAT_INCR(sctps_senderrors); + } if (net == NULL) { /* Now if we had a temp route free it */ if (ro->ro_rt) { @@ -2576,6 +3766,12 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, /* PMTU check versus smallest asoc MTU goes here */ if (ro->ro_rt == NULL) { /* Route was freed */ + + if (net->ro._s_addr && + net->src_addr_selected) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + } net->src_addr_selected = 0; } if (ro->ro_rt != NULL) { @@ -3264,7 +4460,9 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int abort_flag, padval, sz_of; int num_ext; int p_len; + uint32_t vrf_id; + vrf_id = SCTP_DEFAULT_VRFID; if (stcb) { asoc = &stcb->asoc; } else { @@ -3358,7 +4556,7 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, to = (struct sockaddr *)&store; iph = mtod(init_pkt, struct ip *); if (iph->ip_v == IPVERSION) { - struct in_addr addr; + struct sctp_ifa *addr; struct route iproute; sin->sin_family = AF_INET; @@ -3375,12 +4573,16 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, memset(&iproute, 0, sizeof(iproute)); ro = &iproute; memcpy(&ro->ro_dst, sin, sizeof(*sin)); - addr = sctp_ipv4_source_address_selection(inp, NULL, - ro, NULL, 0); + addr = sctp_source_address_selection(inp, NULL, + ro, NULL, 0, vrf_id); + if (addr == NULL) + return; + if (ro->ro_rt) { RTFREE(ro->ro_rt); + ro->ro_rt = NULL; } - stc.laddress[0] = addr.s_addr; + stc.laddress[0] = addr->address.sin.sin_addr.s_addr; stc.laddress[1] = 0; stc.laddress[2] = 0; stc.laddress[3] = 0; @@ -3395,14 +4597,14 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, stc.ipv4_scope = 1; #endif /* SCTP_DONT_DO_PRIVADDR_SCOPE */ /* Must use the address in this case */ - if (sctp_is_address_on_local_host((struct sockaddr *)sin)) { + if (sctp_is_address_on_local_host((struct sockaddr *)sin, vrf_id)) { stc.loopback_scope = 1; stc.ipv4_scope = 1; stc.site_scope = 1; - stc.local_scope = 1; + stc.local_scope = 0; } } else if (iph->ip_v == (IPV6_VERSION >> 4)) { - struct in6_addr addr; + struct sctp_ifa *addr; struct route_in6 iproute6; @@ -3417,9 +4619,9 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, sin6->sin6_scope_id = 0; stc.addr_type = SCTP_IPV6_ADDRESS; stc.scope_id = 0; - if (sctp_is_address_on_local_host((struct sockaddr *)sin6)) { + if (sctp_is_address_on_local_host((struct sockaddr *)sin6, vrf_id)) { stc.loopback_scope = 1; - stc.local_scope = 1; + stc.local_scope = 0; stc.site_scope = 1; stc.ipv4_scope = 1; } else if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { @@ -3459,12 +4661,16 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, memset(&iproute6, 0, sizeof(iproute6)); ro = (struct route *)&iproute6; memcpy(&ro->ro_dst, sin6, sizeof(*sin6)); - addr = sctp_ipv6_source_address_selection(inp, NULL, - ro, NULL, 0); + addr = sctp_source_address_selection(inp, NULL, + ro, NULL, 0, vrf_id); + if (addr == NULL) + return; + if (ro->ro_rt) { RTFREE(ro->ro_rt); + ro->ro_rt = NULL; } - memcpy(&stc.laddress, &addr, sizeof(struct in6_addr)); + memcpy(&stc.laddress, &addr->address.sin6.sin6_addr, sizeof(struct in6_addr)); stc.laddr_type = SCTP_IPV6_ADDRESS; } } else { @@ -3501,13 +4707,16 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, * strange case here, the INIT should have * did the selection. */ - net->ro._s_addr.sin.sin_addr = - sctp_ipv4_source_address_selection(inp, - stcb, (struct route *)&net->ro, net, 0); + net->ro._s_addr = sctp_source_address_selection(inp, + stcb, (struct route *)&net->ro, + net, 0, vrf_id); + if (net->ro._s_addr == NULL) + return; + net->src_addr_selected = 1; } - stc.laddress[0] = net->ro._s_addr.sin.sin_addr.s_addr; + stc.laddress[0] = net->ro._s_addr->address.sin.sin_addr.s_addr; stc.laddress[1] = 0; stc.laddress[2] = 0; stc.laddress[3] = 0; @@ -3522,12 +4731,15 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, * strange case here, the INIT should have * did the selection. */ - net->ro._s_addr.sin6.sin6_addr = - sctp_ipv6_source_address_selection(inp, - stcb, (struct route *)&net->ro, net, 0); + net->ro._s_addr = sctp_source_address_selection(inp, + stcb, (struct route *)&net->ro, + net, 0, vrf_id); + if (net->ro._s_addr == NULL) + return; + net->src_addr_selected = 1; } - memcpy(&stc.laddress, &net->ro._l_addr.sin6.sin6_addr, + memcpy(&stc.laddress, &net->ro._s_addr->address.sin6.sin6_addr, sizeof(struct in6_addr)); stc.laddr_type = SCTP_IPV6_ADDRESS; } @@ -3670,7 +4882,7 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, uint16_t random_len; /* generate and add RANDOM parameter */ - random_len = sctp_auth_random_len; + random_len = SCTP_AUTH_RANDOM_SIZE_DEFAULT; random = (struct sctp_auth_random *)(mtod(m, caddr_t)+SCTP_BUF_LEN(m)); random->ph.param_type = htons(SCTP_RANDOM); p_len = sizeof(*random) + random_len; @@ -4011,7 +5223,6 @@ sctp_get_frag_point(struct sctp_tcb *stcb, } return (siz); } -extern unsigned int sctp_max_chunks_on_queue; static void sctp_set_prsctp_policy(struct sctp_tcb *stcb, @@ -4612,7 +5823,7 @@ sctp_sendall(struct sctp_inpcb *inp, struct uio *uio, struct mbuf *m, } ca->m = m; } - ret = sctp_initiate_iterator(NULL, sctp_sendall_iterator, + ret = sctp_initiate_iterator(NULL, sctp_sendall_iterator, NULL, SCTP_PCB_ANY_FLAGS, SCTP_PCB_ANY_FEATURES, SCTP_ASOC_ANY_STATE, (void *)ca, 0, sctp_sendall_completes, inp, 1); @@ -4807,7 +6018,6 @@ sctp_clean_up_ctl(struct sctp_tcb *stcb, struct sctp_association *asoc) } } -extern int sctp_min_split_point; static __inline int sctp_can_we_split_this(struct sctp_tcb *stcb, @@ -5194,14 +6404,14 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net, int frag_point, int eeor_mode) { struct sctp_association *asoc; - struct sctp_stream_out *strq, *strqn; + struct sctp_stream_out *strq, *strqn, *strqt; int goal_mtu, moved_how_much, total_moved = 0; int locked, giveup; struct sctp_stream_queue_pending *sp; SCTP_TCB_LOCK_ASSERT(stcb); asoc = &stcb->asoc; -#ifdef AF_INET6 +#ifdef INET6 if (net->ro._l_addr.sin6.sin6_family == AF_INET6) { goal_mtu = net->mtu - SCTP_MIN_OVERHEAD; } else { @@ -5268,13 +6478,14 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, break; } else { asoc->locked_on_sending = NULL; + strqt = sctp_select_a_stream(stcb, asoc); if (TAILQ_FIRST(&strq->outqueue) == NULL) { sctp_remove_from_wheel(stcb, asoc, strq); } if (giveup) { break; } - strq = sctp_select_a_stream(stcb, asoc); + strq = strqt; if (strq == NULL) { break; } @@ -5336,8 +6547,6 @@ sctp_move_to_an_alt(struct sctp_tcb *stcb, } } -extern int sctp_early_fr; - int sctp_med_chunk_output(struct sctp_inpcb *inp, struct sctp_tcb *stcb, @@ -5364,7 +6573,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, /* temp arrays for unlinking */ struct sctp_tmit_chunk *data_list[SCTP_MAX_DATA_BUNDLING]; int no_fragmentflg, error; - int one_chunk, hbflag; + int one_chunk, hbflag, skip_data_for_this_net; int asconf, cookie, no_out_cnt; int bundle_at, ctl_cnt, no_data_chunks, cwnd_full_ind, eeor_mode; unsigned int mtu, r_mtu, omtu, mx_mtu, to_out; @@ -5515,7 +6724,11 @@ again_one_more_time: endoutchain = outchain = NULL; no_fragmentflg = 1; one_chunk = 0; - + if (net->dest_state & SCTP_ADDR_UNCONFIRMED) { + skip_data_for_this_net = 1; + } else { + skip_data_for_this_net = 0; + } if ((net->ro.ro_rt) && (net->ro.ro_rt->rt_ifp)) { /* * if we have a route and an ifp check to see if we @@ -5741,9 +6954,8 @@ again_one_more_time: */ sctp_move_to_an_alt(stcb, asoc, net); } - sctp_clean_up_ctl(stcb, asoc); *reason_code = 7; - return (error); + continue; } else asoc->ifp_had_enobuf = 0; /* Only HB or ASCONF advances time */ @@ -5804,7 +7016,7 @@ again_one_more_time: else omtu = 0; } - if (((asoc->state & SCTP_STATE_OPEN) == SCTP_STATE_OPEN) || + if ((((asoc->state & SCTP_STATE_OPEN) == SCTP_STATE_OPEN) && (skip_data_for_this_net == 0)) || (cookie)) { for (chk = TAILQ_FIRST(&asoc->send_queue); chk; chk = nchk) { if (no_data_chunks) { @@ -6009,9 +7221,8 @@ again_one_more_time: */ sctp_move_to_an_alt(stcb, asoc, net); } - sctp_clean_up_ctl(stcb, asoc); *reason_code = 6; - return (error); + continue; } else { asoc->ifp_had_enobuf = 0; } @@ -6907,7 +8118,11 @@ one_chunk_around: * flag since this flag dictates if we * subtracted from the fs */ - data_list[i]->rec.data.chunk_was_revoked = 0; + if (data_list[i]->rec.data.chunk_was_revoked) { + /* Deflate the cwnd */ + data_list[i]->whoTo->cwnd -= data_list[i]->book_size; + data_list[i]->rec.data.chunk_was_revoked = 0; + } data_list[i]->snd_count++; sctp_ucount_decr(asoc->sent_queue_retran_cnt); /* record the time */ @@ -7526,10 +8741,14 @@ sctp_send_sack(struct sctp_tcb *stcb) sctp_alloc_a_chunk(stcb, a_chk); if (a_chk == NULL) { /* No memory so we drop the idea, and set a timer */ - sctp_timer_stop(SCTP_TIMER_TYPE_RECV, - stcb->sctp_ep, stcb, NULL, SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_5); - sctp_timer_start(SCTP_TIMER_TYPE_RECV, - stcb->sctp_ep, stcb, NULL); + if (stcb->asoc.delayed_ack) { + sctp_timer_stop(SCTP_TIMER_TYPE_RECV, + stcb->sctp_ep, stcb, NULL, SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_5); + sctp_timer_start(SCTP_TIMER_TYPE_RECV, + stcb->sctp_ep, stcb, NULL); + } else { + stcb->asoc.send_sack = 1; + } return; } a_chk->copy_by_ref = 0; @@ -7537,6 +8756,9 @@ sctp_send_sack(struct sctp_tcb *stcb) a_chk->rec.chunk_id.id = SCTP_SELECTIVE_ACK; a_chk->rec.chunk_id.can_take_data = 1; } + /* Clear our pkt counts */ + asoc->data_pkts_seen = 0; + a_chk->asoc = asoc; a_chk->snd_count = 0; a_chk->send_size = 0; /* fill in later */ @@ -7595,10 +8817,14 @@ sctp_send_sack(struct sctp_tcb *stcb) if (a_chk->whoTo) atomic_subtract_int(&a_chk->whoTo->ref_count, 1); sctp_free_a_chunk(stcb, a_chk); - sctp_timer_stop(SCTP_TIMER_TYPE_RECV, - stcb->sctp_ep, stcb, NULL, SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_6); - sctp_timer_start(SCTP_TIMER_TYPE_RECV, - stcb->sctp_ep, stcb, NULL); + if (stcb->asoc.delayed_ack) { + sctp_timer_stop(SCTP_TIMER_TYPE_RECV, + stcb->sctp_ep, stcb, NULL, SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_6); + sctp_timer_start(SCTP_TIMER_TYPE_RECV, + stcb->sctp_ep, stcb, NULL); + } else { + stcb->asoc.send_sack = 1; + } return; } /* ok, lets go through and fill it in */ @@ -7737,6 +8963,7 @@ sctp_send_sack(struct sctp_tcb *stcb) sack->ch.chunk_length = htons(a_chk->send_size); TAILQ_INSERT_TAIL(&asoc->control_send_queue, a_chk, sctp_next); asoc->ctrl_queue_cnt++; + asoc->send_sack = 0; SCTP_STAT_INCR(sctps_sendsacks); return; } @@ -9214,7 +10441,6 @@ sctp_sosend(struct socket *so, } -extern unsigned int sctp_add_more_threshold; int sctp_lower_sosend(struct socket *so, struct sockaddr *addr, @@ -9381,6 +10607,8 @@ sctp_lower_sosend(struct socket *so, * UDP style, we must go ahead and start the INIT * process */ + uint32_t vrf; + if ((use_rcvinfo) && (srcv) && ((srcv->sinfo_flags & SCTP_ABORT) || ((srcv->sinfo_flags & SCTP_EOF) && @@ -9393,7 +10621,8 @@ sctp_lower_sosend(struct socket *so, goto out_unlocked; } /* get an asoc/stcb struct */ - stcb = sctp_aloc_assoc(inp, addr, 1, &error, 0); + vrf = SCTP_DEFAULT_VRFID; + stcb = sctp_aloc_assoc(inp, addr, 1, &error, 0, vrf); if (stcb == NULL) { /* Error is setup for us in the call */ goto out_unlocked; @@ -9522,7 +10751,7 @@ sctp_lower_sosend(struct socket *so, asoc = &stcb->asoc; } } - if (((so->so_state & SS_NBIO) + if ((SCTP_SO_IS_NBIO(so) || (flags & MSG_NBIO) )) { non_blocking = 1; |