summaryrefslogtreecommitdiffstats
path: root/sys/netinet/sctp_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/sctp_output.c')
-rw-r--r--sys/netinet/sctp_output.c1471
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;
OpenPOWER on IntegriCloud