diff options
author | rrs <rrs@FreeBSD.org> | 2007-03-15 11:27:14 +0000 |
---|---|---|
committer | rrs <rrs@FreeBSD.org> | 2007-03-15 11:27:14 +0000 |
commit | bd8786ed778eb3e2e64f4bc1078d8653aa1a6d54 (patch) | |
tree | 3a099c736ba497f25dc6fd964a7995f33f2282ad /sys/netinet/sctp_output.c | |
parent | 7f1d3da162e9e7e9561d1b1ab3e88b00622c7b5e (diff) | |
download | FreeBSD-src-bd8786ed778eb3e2e64f4bc1078d8653aa1a6d54.zip FreeBSD-src-bd8786ed778eb3e2e64f4bc1078d8653aa1a6d54.tar.gz |
- Sysctl's move to seperate file
- moved away from ifn/ifa access to sctp_ifa/sctp_ifn
built and managed by the add-ip code.
- cleaned up add-ip code to use the iterator
- made iterator be a thread, which enables auto-asconf now.
- rewrote and cleaned up source address selection (also
made it use new structures).
- Fixed a couple of memory leaks.
- DACK now settable as to how many packets to delay as
well as time.
- connectx() to latest socket API, new associd arg.
- Fixed issue with revoking and loosing potential to
send when we inflate the flight size. We now inflate
the cwnd too and deflate it later when the revoked
chunk is sent or acked.
- Got rid of some temp debug code
- src addr selection moved to a common file (sctp_output.c)
- Support for simple VRF's (we have support for multi-vfr
via compile switch that is scrubbed from BSD but we won't
need multi-vrf until we first get VRF :-D)
- Rest of mib work for address information now done
- Limit number of addresses in INIT/INIT-ACK to
a #def (30).
Reviewed by: gnn
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; |