summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrrs <rrs@FreeBSD.org>2007-07-24 20:06:02 +0000
committerrrs <rrs@FreeBSD.org>2007-07-24 20:06:02 +0000
commit1db8ba247453b3da31775f1a39199179ca75bfb7 (patch)
treed2ea0fd9ef19605e959e0ed17a4723e94186c995
parent4177278d87cbfa0b06b72d4b526e3a39dbd7838b (diff)
downloadFreeBSD-src-1db8ba247453b3da31775f1a39199179ca75bfb7.zip
FreeBSD-src-1db8ba247453b3da31775f1a39199179ca75bfb7.tar.gz
- take out a needless panic under invariants for sctp_output.c
- Fix addrs's error checking of sctp_sendx(3) when addrcnt is less than SCTP_SMALL_IOVEC_SIZE - re-add back inpcb_bind local address check bypass capability - Fix it so sctp_opt_info is independant of assoc_id postion. - Fix cookie life set to use MSEC_TO_TICKS() macro. - asconf changes o More comment changes/clarifications related to the old local address "not" list which is now an explicit restricted list. o Rename some functions for clarity: - sctp_add/del_local_addr_assoc to xxx_local_addr_restricted() - asconf related iterator functions to sctp_asconf_iterator_xxx() o Fix bug when the same address is deleted and added (and removed from the asconf queue) where the ifa is "freed" twice refcount wise, possibly freeing it completely. o Fix bug in output where the first ASCONF would not go out after the last address is changed (e.g. only goes out when retransmitted). o Fix bug where multiple ASCONFs can be bundled in the same packet with the and with the same serial numbers. o Fix asconf stcb iterator to not send ASCONF until after all work queue entries have been processed. o Change behavior so that when the last address is deleted (auto asconf on a bound all endpoint) no action is taken until an address is added; at that time, an ASCONF add+delete is sent (if the assoc is still up). o Fix local address counting so that address scoping is taken into account. o #ifdef SCTP_TIMER_BASED_ASCONF the old timer triggered sending of ASCONF (after an RTO). The default now is to send ASCONF immediately (except for the case of changing/deleting the last usable address). Approved by: re(ken smith)@freebsd.org
-rw-r--r--lib/libc/net/sctp_sys_calls.c113
-rw-r--r--sys/netinet/sctp_asconf.c302
-rw-r--r--sys/netinet/sctp_asconf.h12
-rw-r--r--sys/netinet/sctp_input.c9
-rw-r--r--sys/netinet/sctp_output.c112
-rw-r--r--sys/netinet/sctp_pcb.c30
-rw-r--r--sys/netinet/sctp_pcb.h7
-rw-r--r--sys/netinet/sctp_structs.h25
-rw-r--r--sys/netinet/sctp_timer.c28
-rw-r--r--sys/netinet/sctp_usrreq.c11
-rw-r--r--sys/netinet/sctputil.c130
-rw-r--r--sys/netinet/sctputil.h2
-rw-r--r--sys/netinet6/sctp6_usrreq.c4
13 files changed, 564 insertions, 221 deletions
diff --git a/lib/libc/net/sctp_sys_calls.c b/lib/libc/net/sctp_sys_calls.c
index 922e034..715fcbd 100644
--- a/lib/libc/net/sctp_sys_calls.c
+++ b/lib/libc/net/sctp_sys_calls.c
@@ -240,7 +240,10 @@ sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, int flags)
{
struct sctp_getaddresses *gaddrs;
struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
int i, sz, argsz;
+ uint16_t sport = 0;
/* validate the flags */
if ((flags != SCTP_BINDX_ADD_ADDR) &&
@@ -260,12 +263,63 @@ sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, int flags)
errno = ENOMEM;
return (-1);
}
+ /* First pre-screen the addresses */
sa = addrs;
for (i = 0; i < addrcnt; i++) {
sz = sa->sa_len;
if (sa->sa_family == AF_INET) {
if (sa->sa_len != sizeof(struct sockaddr_in))
goto out_error;
+ sin = (struct sockaddr_in *)sa;
+ if (sin->sin_port) {
+ /* non-zero port, check or save */
+ if (sport) {
+ /* Check against our port */
+ if (sport != sin->sin_port) {
+ goto out_error;
+ }
+ } else {
+ /* save off the port */
+ sport = sin->sin_port;
+ }
+ }
+ } else if (sa->sa_family == AF_INET6) {
+ if (sa->sa_len != sizeof(struct sockaddr_in6))
+ goto out_error;
+ sin6 = (struct sockaddr_in6 *)sa;
+ if (sin6->sin6_port) {
+ /* non-zero port, check or save */
+ if (sport) {
+ /* Check against our port */
+ if (sport != sin6->sin6_port) {
+ goto out_error;
+ }
+ } else {
+ /* save off the port */
+ sport = sin6->sin6_port;
+ }
+ }
+ } else {
+ /* invalid address family specified */
+ goto out_error;
+ }
+
+
+ }
+ sa = addrs;
+ /*
+ * Now if there was a port mentioned, assure that the first address
+ * has that port to make sure it fails or succeeds correctly.
+ */
+ if (sport) {
+ sin = (struct sockaddr_in *)sa;
+ sin->sin_port = sport;
+ }
+ for (i = 0; i < addrcnt; i++) {
+ sz = sa->sa_len;
+ if (sa->sa_family == AF_INET) {
+ if (sa->sa_len != sizeof(struct sockaddr_in))
+ goto out_error;
} else if (sa->sa_family == AF_INET6) {
if (sa->sa_len != sizeof(struct sockaddr_in6))
goto out_error;
@@ -298,7 +352,55 @@ sctp_opt_info(int sd, sctp_assoc_t id, int opt, void *arg, socklen_t * size)
errno = EINVAL;
return (-1);
}
- *(sctp_assoc_t *) arg = id;
+ switch (opt) {
+ case SCTP_RTOINFO:
+ ((struct sctp_rtoinfo *)arg)->srto_assoc_id = id;
+ break;
+ case SCTP_ASSOCINFO:
+ ((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
+ break;
+ case SCTP_DEFAULT_SEND_PARAM:
+ ((struct sctp_assocparams *)arg)->sasoc_assoc_id = id;
+ break;
+ case SCTP_SET_PEER_PRIMARY_ADDR:
+ ((struct sctp_setpeerprim *)arg)->sspp_assoc_id = id;
+ break;
+ case SCTP_PRIMARY_ADDR:
+ ((struct sctp_setprim *)arg)->ssp_assoc_id = id;
+ break;
+ case SCTP_PEER_ADDR_PARAMS:
+ ((struct sctp_paddrparams *)arg)->spp_assoc_id = id;
+ break;
+ case SCTP_MAXSEG:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_AUTH_KEY:
+ ((struct sctp_authkey *)arg)->sca_assoc_id = id;
+ break;
+ case SCTP_AUTH_ACTIVE_KEY:
+ ((struct sctp_authkeyid *)arg)->scact_assoc_id = id;
+ break;
+ case SCTP_DELAYED_SACK:
+ ((struct sctp_sack_info *)arg)->sack_assoc_id = id;
+ break;
+ case SCTP_CONTEXT:
+ ((struct sctp_assoc_value *)arg)->assoc_id = id;
+ break;
+ case SCTP_STATUS:
+ ((struct sctp_status *)arg)->sstat_assoc_id = id;
+ break;
+ case SCTP_GET_PEER_ADDR_INFO:
+ ((struct sctp_paddrinfo *)arg)->spinfo_assoc_id = id;
+ break;
+ case SCTP_PEER_AUTH_CHUNKS:
+ ((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
+ break;
+ case SCTP_LOCAL_AUTH_CHUNKS:
+ ((struct sctp_authchunks *)arg)->gauth_assoc_id = id;
+ break;
+ default:
+ break;
+ }
return (getsockopt(sd, IPPROTO_SCTP, opt, arg, size));
}
@@ -628,7 +730,10 @@ sctp_sendx(int sd, const void *msg, size_t msg_len,
int add_len, len, no_end_cx = 0;
struct sockaddr *at;
-
+ if (addrs == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
#ifdef SYS_sctp_generic_sendmsg
if (addrcnt < SCTP_SMALL_IOVEC_SIZE) {
socklen_t l;
@@ -643,10 +748,6 @@ sctp_sendx(int sd, const void *msg, size_t msg_len,
}
#endif
- if (addrs == NULL) {
- errno = EINVAL;
- return (-1);
- }
len = sizeof(int);
at = addrs;
cnt = 0;
diff --git a/sys/netinet/sctp_asconf.c b/sys/netinet/sctp_asconf.c
index 461637c..a091011 100644
--- a/sys/netinet/sctp_asconf.c
+++ b/sys/netinet/sctp_asconf.c
@@ -295,8 +295,7 @@ sctp_process_asconf_add_ip(struct mbuf *m, struct sctp_asconf_paramhdr *aph,
}
static int
-sctp_asconf_del_remote_addrs_except(struct sctp_tcb *stcb,
- struct sockaddr *src)
+sctp_asconf_del_remote_addrs_except(struct sctp_tcb *stcb, struct sockaddr *src)
{
struct sctp_nets *src_net, *net;
@@ -459,8 +458,8 @@ sctp_process_asconf_delete_ip(struct mbuf *m, struct sctp_asconf_paramhdr *aph,
static struct mbuf *
sctp_process_asconf_set_primary(struct mbuf *m,
- struct sctp_asconf_paramhdr *aph, struct sctp_tcb *stcb,
- int response_required)
+ struct sctp_asconf_paramhdr *aph,
+ struct sctp_tcb *stcb, int response_required)
{
struct mbuf *m_reply = NULL;
struct sockaddr_storage sa_source, sa_store;
@@ -874,7 +873,7 @@ sctp_asconf_cleanup(struct sctp_tcb *stcb, struct sctp_nets *net)
/*
* process an ADD/DELETE IP ack from peer.
- * addr corresponding sctp_ifa to the address being added/deleted.
+ * addr: corresponding sctp_ifa to the address being added/deleted.
* type: SCTP_ADD_IP_ADDRESS or SCTP_DEL_IP_ADDRESS.
* flag: 1=success, 0=failure.
*/
@@ -892,31 +891,28 @@ sctp_asconf_addr_mgmt_ack(struct sctp_tcb *stcb, struct sctp_ifa *addr,
* DEL_IP_ADDRESS is never actually added to the list...
*/
if (flag) {
- /* success case, so remove from the list */
- sctp_del_local_addr_assoc(stcb, addr);
+ /* success case, so remove from the restricted list */
+ sctp_del_local_addr_restricted(stcb, addr);
}
/* else, leave it on the list */
}
/*
- * add an asconf add/delete IP address parameter to the queue.
+ * add an asconf add/delete/set primary IP address parameter to the queue.
* type = SCTP_ADD_IP_ADDRESS, SCTP_DEL_IP_ADDRESS, SCTP_SET_PRIM_ADDR.
- * returns 0 if completed, non-zero if not completed.
- * NOTE: if adding, but delete already scheduled (and not yet sent out),
- * simply remove from queue. Same for deleting an address already scheduled
- * for add. If a duplicate operation is found, ignore the new one.
+ * returns 0 if queued, -1 if not queued/removed.
+ * NOTE: if adding, but a delete for the same address is already scheduled
+ * (and not yet sent out), simply remove it from queue. Same for deleting
+ * an address already scheduled for add. If a duplicate operation is found,
+ * ignore the new one.
*/
-static uint32_t
-sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
+static int
+sctp_asconf_queue_mgmt(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
uint16_t type)
{
struct sctp_asconf_addr *aa, *aa_next;
struct sockaddr *sa;
- /* see if peer supports ASCONF */
- if (stcb->asoc.peer_supports_asconf == 0) {
- return (-1);
- }
/* make sure the request isn't already in the queue */
for (aa = TAILQ_FIRST(&stcb->asoc.asconf_queue); aa != NULL;
aa = aa_next) {
@@ -929,30 +925,36 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
return (-1);
}
/* is the negative request already in queue, and not sent */
- if (aa->sent == 0 &&
- /* add requested, delete already queued */
- ((type == SCTP_ADD_IP_ADDRESS &&
- aa->ap.aph.ph.param_type == SCTP_DEL_IP_ADDRESS) ||
- /* delete requested, add already queued */
- (type == SCTP_DEL_IP_ADDRESS &&
- aa->ap.aph.ph.param_type == SCTP_ADD_IP_ADDRESS))) {
- /* delete the existing entry in the queue */
+ if ((aa->sent == 0) && (type == SCTP_ADD_IP_ADDRESS) &&
+ (aa->ap.aph.ph.param_type == SCTP_DEL_IP_ADDRESS)) {
+ /* add requested, delete already queued */
TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next);
- /* take the entry off the appropriate list */
- sctp_asconf_addr_mgmt_ack(stcb, aa->ifa, type, 1);
- /* free the entry */
- sctp_free_ifa(aa->ifa);
+ /* remove the ifa from the restricted list */
+ sctp_del_local_addr_restricted(stcb, ifa);
+ /* free the asconf param */
SCTP_FREE(aa, SCTP_M_ASC_ADDR);
+ SCTPDBG(SCTP_DEBUG_ASCONF2, "asconf_queue_mgmt: add removes queued entry\n");
+ return (-1);
+ }
+ if ((aa->sent == 0) && (type == SCTP_DEL_IP_ADDRESS) &&
+ (aa->ap.aph.ph.param_type == SCTP_ADD_IP_ADDRESS)) {
+ /* delete requested, add already queued */
+ TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next);
+ /* remove the aa->ifa from the restricted list */
+ sctp_del_local_addr_restricted(stcb, aa->ifa);
+ /* free the asconf param */
+ SCTP_FREE(aa, SCTP_M_ASC_ADDR);
+ SCTPDBG(SCTP_DEBUG_ASCONF2, "asconf_queue_mgmt: delete removes queued entry\n");
return (-1);
}
} /* for each aa */
/* adding new request to the queue */
- SCTP_MALLOC(aa, struct sctp_asconf_addr *, sizeof(*aa), SCTP_M_ASC_ADDR);
+ SCTP_MALLOC(aa, struct sctp_asconf_addr *, sizeof(*aa),
+ SCTP_M_ASC_ADDR);
if (aa == NULL) {
/* didn't get memory */
- SCTPDBG(SCTP_DEBUG_ASCONF1,
- "asconf_queue_add: failed to get memory!\n");
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "asconf_queue_mgmt: failed to get memory!\n");
return (-1);
}
/* fill in asconf address parameter fields */
@@ -969,20 +971,19 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
sa = (struct sockaddr *)sin6;
aa->ap.addrp.ph.param_type = SCTP_IPV6_ADDRESS;
aa->ap.addrp.ph.param_length = (sizeof(struct sctp_ipv6addr_param));
- aa->ap.aph.ph.param_length =
- sizeof(struct sctp_asconf_paramhdr) +
+ aa->ap.aph.ph.param_length = sizeof(struct sctp_asconf_paramhdr) +
sizeof(struct sctp_ipv6addr_param);
memcpy(&aa->ap.addrp.addr, &sin6->sin6_addr,
sizeof(struct in6_addr));
} else if (ifa->address.sa.sa_family == AF_INET) {
/* IPv4 address */
- struct sockaddr_in *sin = (struct sockaddr_in *)&ifa->address.sa;
+ struct sockaddr_in *sin;
+ sin = (struct sockaddr_in *)&ifa->address.sa;
sa = (struct sockaddr *)sin;
aa->ap.addrp.ph.param_type = SCTP_IPV4_ADDRESS;
aa->ap.addrp.ph.param_length = (sizeof(struct sctp_ipv4addr_param));
- aa->ap.aph.ph.param_length =
- sizeof(struct sctp_asconf_paramhdr) +
+ aa->ap.aph.ph.param_length = sizeof(struct sctp_asconf_paramhdr) +
sizeof(struct sctp_ipv4addr_param);
memcpy(&aa->ap.addrp.addr, &sin->sin_addr,
sizeof(struct in_addr));
@@ -1001,7 +1002,7 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
/* add goes to the front of the queue */
TAILQ_INSERT_HEAD(&stcb->asoc.asconf_queue, aa, next);
SCTPDBG(SCTP_DEBUG_ASCONF2,
- "asconf_queue_add: appended asconf ADD_IP_ADDRESS: ");
+ "asconf_queue_mgmt: inserted asconf ADD_IP_ADDRESS: ");
SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
} else {
/* delete and set primary goes to the back of the queue */
@@ -1009,10 +1010,10 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
#ifdef SCTP_DEBUG
if (sctp_debug_on && SCTP_DEBUG_ASCONF2) {
if (type == SCTP_DEL_IP_ADDRESS) {
- SCTP_PRINTF("asconf_queue_add: inserted asconf DEL_IP_ADDRESS: ");
+ SCTP_PRINTF("asconf_queue_mgmt: appended asconf DEL_IP_ADDRESS: ");
SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
} else {
- SCTP_PRINTF("asconf_queue_add: inserted asconf SET_PRIM_ADDR: ");
+ SCTP_PRINTF("asconf_queue_mgmt: appended asconf SET_PRIM_ADDR: ");
SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
}
}
@@ -1022,6 +1023,84 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
return (0);
}
+
+/*
+ * add an asconf operation for the given ifa and type.
+ * type = SCTP_ADD_IP_ADDRESS, SCTP_DEL_IP_ADDRESS, SCTP_SET_PRIM_ADDR.
+ * returns 0 if completed, -1 if not completed, 1 if immediate send is
+ * advisable.
+ */
+static int
+sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
+ uint16_t type)
+{
+ uint32_t status;
+ int pending_delete_queued = 0;
+
+ /* see if peer supports ASCONF */
+ if (stcb->asoc.peer_supports_asconf == 0) {
+ return (-1);
+ }
+ /*
+ * if this is deleting the last address from the assoc, mark it as
+ * pending.
+ */
+ if ((type == SCTP_DEL_IP_ADDRESS) && !stcb->asoc.asconf_del_pending &&
+ (sctp_local_addr_count(stcb) < 2)) {
+ /* set the pending delete info only */
+ stcb->asoc.asconf_del_pending = 1;
+ stcb->asoc.asconf_addr_del_pending = ifa;
+ atomic_add_int(&ifa->refcount, 1);
+ SCTPDBG(SCTP_DEBUG_ASCONF2,
+ "asconf_queue_add: mark delete last address pending\n");
+ return (-1);
+ }
+ /*
+ * if this is an add, and there is a delete also pending (i.e. the
+ * last local address is being changed), queue the pending delete
+ * too.
+ */
+ if ((type == SCTP_ADD_IP_ADDRESS) && stcb->asoc.asconf_del_pending) {
+ /* queue in the pending delete */
+ if (sctp_asconf_queue_mgmt(stcb,
+ stcb->asoc.asconf_addr_del_pending,
+ SCTP_DEL_IP_ADDRESS) == 0) {
+ SCTPDBG(SCTP_DEBUG_ASCONF2, "asconf_queue_add: queing pending delete\n");
+ pending_delete_queued = 1;
+ /* clear out the pending delete info */
+ stcb->asoc.asconf_del_pending = 0;
+ sctp_free_ifa(stcb->asoc.asconf_addr_del_pending);
+ stcb->asoc.asconf_addr_del_pending = NULL;
+ }
+ }
+ /* queue an asconf parameter */
+ status = sctp_asconf_queue_mgmt(stcb, ifa, type);
+
+ if (pending_delete_queued && (status == 0)) {
+ struct sctp_nets *net;
+
+ /*
+ * since we know that the only/last address is now being
+ * changed in this case, reset the cwnd/rto on all nets to
+ * start as a new address and path. Also clear the error
+ * counts to give the assoc the best chance to complete the
+ * address change.
+ */
+ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+ stcb->asoc.cc_functions.sctp_set_initial_cc_param(stcb,
+ net);
+ net->RTO = 0;
+ net->error_count = 0;
+ }
+ stcb->asoc.overall_error_count = 0;
+ /* queue in an advisory set primary too */
+ (void)sctp_asconf_queue_mgmt(stcb, ifa, SCTP_SET_PRIM_ADDR);
+ /* let caller know we should send this out immediately */
+ status = 1;
+ }
+ return (status);
+}
+
/*
* add an asconf add/delete IP address parameter to the queue by addr.
* type = SCTP_ADD_IP_ADDRESS, SCTP_DEL_IP_ADDRESS, SCTP_SET_PRIM_ADDR.
@@ -1030,7 +1109,7 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
* simply remove from queue. Same for deleting an address already scheduled
* for add. If a duplicate operation is found, ignore the new one.
*/
-static uint32_t
+static int
sctp_asconf_queue_add_sa(struct sctp_tcb *stcb, struct sockaddr *sa,
uint16_t type)
{
@@ -1065,8 +1144,8 @@ sctp_asconf_queue_add_sa(struct sctp_tcb *stcb, struct sockaddr *sa,
/* delete the existing entry in the queue */
TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next);
- /* free the entry */
sctp_free_ifa(aa->ifa);
+ /* free the entry */
SCTP_FREE(aa, SCTP_M_ASC_ADDR);
return (-1);
} else if (type == SCTP_DEL_IP_ADDRESS &&
@@ -1075,10 +1154,8 @@ sctp_asconf_queue_add_sa(struct sctp_tcb *stcb, struct sockaddr *sa,
/* delete the existing entry in the queue */
TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next);
- /* take the entry off the appropriate list */
- sctp_asconf_addr_mgmt_ack(stcb, aa->ifa, type, 1);
+ sctp_del_local_addr_restricted(stcb, aa->ifa);
/* free the entry */
- sctp_free_ifa(aa->ifa);
SCTP_FREE(aa, SCTP_M_ASC_ADDR);
return (-1);
}
@@ -1095,7 +1172,8 @@ sctp_asconf_queue_add_sa(struct sctp_tcb *stcb, struct sockaddr *sa,
return (-1);
}
/* adding new request to the queue */
- SCTP_MALLOC(aa, struct sctp_asconf_addr *, sizeof(*aa), SCTP_M_ASC_ADDR);
+ SCTP_MALLOC(aa, struct sctp_asconf_addr *, sizeof(*aa),
+ SCTP_M_ASC_ADDR);
if (aa == NULL) {
/* didn't get memory */
SCTPDBG(SCTP_DEBUG_ASCONF1,
@@ -1454,9 +1532,14 @@ sctp_handle_asconf_ack(struct mbuf *m, int offset,
/* clear the sent flag to allow new ASCONFs */
asoc->asconf_sent = 0;
if (!TAILQ_EMPTY(&stcb->asoc.asconf_queue)) {
+#ifdef SCTP_TIMER_BASED_ASCONF
/* we have more params, so restart our timer */
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, stcb->sctp_ep,
stcb, net);
+#else
+ /* we have more params, so send out more */
+ sctp_send_asconf(stcb, net);
+#endif
}
}
@@ -1528,7 +1611,7 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
}
}
/* put this address on the "pending/do not use yet" list */
- sctp_add_local_addr_assoc(stcb, ifa, 1);
+ sctp_add_local_addr_restricted(stcb, ifa);
/*
* check address scope if address is out of scope, don't queue
* anything... note: this would leave the address on both inp and
@@ -1585,16 +1668,20 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
if (stcb->asoc.peer_supports_asconf) {
/* queue an asconf for this addr */
status = sctp_asconf_queue_add(stcb, ifa, type);
+
/*
- * if queued ok, and in correct state, set the
- * ASCONF timer if in non-open state, we will set
- * this timer when the state does go open and do all
- * the asconf's
+ * if queued ok, and in the open state, send out the
+ * ASCONF. If in the non-open state, these will be
+ * sent when the state goes open.
*/
if (status == 0 &&
SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) {
+#ifdef SCTP_TIMER_BASED_ASCONF
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp,
stcb, stcb->asoc.primary_destination);
+#else
+ sctp_send_asconf(stcb, stcb->asoc.primary_destination);
+#endif
}
}
}
@@ -1602,7 +1689,7 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
int
-sctp_iterator_ep(struct sctp_inpcb *inp, void *ptr, uint32_t val)
+sctp_asconf_iterator_ep(struct sctp_inpcb *inp, void *ptr, uint32_t val)
{
struct sctp_asconf_iterator *asc;
struct sctp_ifa *ifa;
@@ -1648,8 +1735,8 @@ sctp_iterator_ep(struct sctp_inpcb *inp, void *ptr, uint32_t val)
return (0);
}
-int
-sctp_iterator_ep_end(struct sctp_inpcb *inp, void *ptr, uint32_t val)
+static int
+sctp_asconf_iterator_ep_end(struct sctp_inpcb *inp, void *ptr, uint32_t val)
{
struct sctp_ifa *ifa;
struct sctp_asconf_iterator *asc;
@@ -1683,14 +1770,15 @@ sctp_iterator_ep_end(struct sctp_inpcb *inp, void *ptr, uint32_t val)
}
void
-sctp_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr,
- uint32_t val)
+sctp_asconf_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+ void *ptr, uint32_t val)
{
struct sctp_asconf_iterator *asc;
struct sctp_ifa *ifa;
struct sctp_laddr *l;
int cnt_invalid = 0;
int type, status;
+ int num_queued = 0;
asc = (struct sctp_asconf_iterator *)ptr;
LIST_FOREACH(l, &asc->list_of_work, sctp_nxt_addr) {
@@ -1764,9 +1852,9 @@ sctp_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr,
continue;
}
- /* put this address on the "pending/do not use yet" list */
if (type == SCTP_ADD_IP_ADDRESS) {
- sctp_add_local_addr_assoc(stcb, ifa, 1);
+ /* prevent this address from being used as a source */
+ sctp_add_local_addr_restricted(stcb, ifa);
} else if (type == SCTP_DEL_IP_ADDRESS) {
struct sctp_nets *net;
@@ -1797,10 +1885,7 @@ sctp_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr,
}
} else if (type == SCTP_SET_PRIM_ADDR) {
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) {
- /*
- * must validate the ifa in question is in
- * the ep
- */
+ /* must validate the ifa is in the ep */
if (sctp_is_addr_in_ep(stcb->sctp_ep, ifa) == 0) {
continue;
}
@@ -1818,29 +1903,32 @@ sctp_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr,
}
}
/* queue an asconf for this address add/delete */
- if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF)) {
- /* does the peer do asconf? */
- if (stcb->asoc.peer_supports_asconf) {
- /* queue an asconf for this addr */
- status = sctp_asconf_queue_add(stcb, ifa, type);
- /*
- * if queued ok, and in correct state, set
- * the ASCONF timer if in non-open state, we
- * will set this timer when the state does
- * go open and do all the asconf's
- */
- if (status == 0 &&
- SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) {
- sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp,
- stcb, stcb->asoc.primary_destination);
+ if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF) &&
+ stcb->asoc.peer_supports_asconf) {
+ /* queue an asconf for this addr */
+ status = sctp_asconf_queue_add(stcb, ifa, type);
+ /*
+ * if queued ok, and in the open state, update the
+ * count of queued params. If in the non-open
+ * state, these get sent when the assoc goes open.
+ */
+ if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) {
+ if (status >= 0) {
+ num_queued++;
}
}
}
}
+ /*
+ * If we have queued params in the open state, send out an ASCONF.
+ */
+ if (num_queued > 0) {
+ sctp_send_asconf(stcb, stcb->asoc.primary_destination);
+ }
}
-void
-sctp_iterator_end(void *ptr, uint32_t val)
+void
+sctp_asconf_iterator_end(void *ptr, uint32_t val)
{
struct sctp_asconf_iterator *asc;
struct sctp_ifa *ifa;
@@ -1864,10 +1952,10 @@ sctp_iterator_end(void *ptr, uint32_t val)
}
/*
- * sa is the sockaddr to ask the peer to set primary to returns: 0 =
- * completed, -1 = error
+ * sa is the sockaddr to ask the peer to set primary to.
+ * returns: 0 = completed, -1 = error
*/
-int32_t
+int
sctp_set_primary_ip_address_sa(struct sctp_tcb *stcb, struct sockaddr *sa)
{
/* NOTE: we currently don't check the validity of the address! */
@@ -1875,15 +1963,19 @@ sctp_set_primary_ip_address_sa(struct sctp_tcb *stcb, struct sockaddr *sa)
/* queue an ASCONF:SET_PRIM_ADDR to be sent */
if (!sctp_asconf_queue_add_sa(stcb, sa, SCTP_SET_PRIM_ADDR)) {
/* set primary queuing succeeded */
+ SCTPDBG(SCTP_DEBUG_ASCONF1,
+ "set_primary_ip_address_sa: queued on tcb=%p, ",
+ stcb);
+ SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, sa);
if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) {
+#ifdef SCTP_TIMER_BASED_ASCONF
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
stcb->sctp_ep, stcb,
stcb->asoc.primary_destination);
+#else
+ sctp_send_asconf(stcb, stcb->asoc.primary_destination);
+#endif
}
- SCTPDBG(SCTP_DEBUG_ASCONF1,
- "set_primary_ip_address_sa: queued on tcb=%p, ",
- stcb);
- SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, sa);
} else {
SCTPDBG(SCTP_DEBUG_ASCONF1, "set_primary_ip_address_sa: failed to add to queue on tcb=%p, ",
stcb);
@@ -1908,15 +2000,18 @@ sctp_set_primary_ip_address(struct sctp_ifa *ifa)
if (!sctp_asconf_queue_add(stcb, ifa,
SCTP_SET_PRIM_ADDR)) {
/* set primary queuing succeeded */
- if (SCTP_GET_STATE(&stcb->asoc) ==
- SCTP_STATE_OPEN) {
+ SCTPDBG(SCTP_DEBUG_ASCONF1, "set_primary_ip_address: queued on stcb=%p, ",
+ stcb);
+ SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, &ifa->address.sa);
+ if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) {
+#ifdef SCTP_TIMER_BASED_ASCONF
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
stcb->sctp_ep, stcb,
stcb->asoc.primary_destination);
+#else
+ sctp_send_asconf(stcb, stcb->asoc.primary_destination);
+#endif
}
- SCTPDBG(SCTP_DEBUG_ASCONF1, "set_primary_ip_address: queued on stcb=%p, ",
- stcb);
- SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, &ifa->address.sa);
}
} /* for each stcb */
} /* for each inp */
@@ -2026,6 +2121,10 @@ sctp_compose_asconf(struct sctp_tcb *stcb, int *retlen)
if (TAILQ_EMPTY(&stcb->asoc.asconf_queue)) {
return (NULL);
}
+ /* can't send a new one if there is one in flight already */
+ if (stcb->asoc.asconf_sent > 0) {
+ return (NULL);
+ }
/*
* get a chunk header mbuf and a cluster for the asconf params since
* it's simpler to fill in the asconf chunk header lookup address on
@@ -2281,15 +2380,19 @@ sctp_process_initack_addresses(struct sctp_tcb *stcb, struct mbuf *m,
status = sctp_asconf_queue_add_sa(stcb, sa,
SCTP_DEL_IP_ADDRESS);
/*
- * if queued ok, and in correct state, set
- * the ASCONF timer
+ * if queued ok, and in correct state, send
+ * out the ASCONF.
*/
if (status == 0 &&
SCTP_GET_STATE(&stcb->asoc) ==
SCTP_STATE_OPEN) {
+#ifdef SCTP_TIMER_BASED_ASCONF
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
stcb->sctp_ep, stcb,
stcb->asoc.primary_destination);
+#else
+ sctp_send_asconf(stcb, stcb->asoc.primary_destination);
+#endif
}
}
}
@@ -2597,13 +2700,14 @@ sctp_addr_mgmt_ep_sa(struct sctp_inpcb *inp, struct sockaddr *sa,
wi->action = type;
atomic_add_int(&ifa->refcount, 1);
LIST_INSERT_HEAD(&asc->list_of_work, wi, sctp_nxt_addr);
- (void)sctp_initiate_iterator(sctp_iterator_ep,
- sctp_iterator_stcb,
- sctp_iterator_ep_end,
+ (void)sctp_initiate_iterator(sctp_asconf_iterator_ep,
+ sctp_asconf_iterator_stcb,
+ sctp_asconf_iterator_ep_end,
SCTP_PCB_ANY_FLAGS,
SCTP_PCB_ANY_FEATURES,
- SCTP_ASOC_ANY_STATE, (void *)asc, 0,
- sctp_iterator_end, inp, 0);
+ SCTP_ASOC_ANY_STATE,
+ (void *)asc, 0,
+ sctp_asconf_iterator_end, inp, 0);
} else {
/* invalid address! */
return (EADDRNOTAVAIL);
diff --git a/sys/netinet/sctp_asconf.h b/sys/netinet/sctp_asconf.h
index d47aa96..33da27e 100644
--- a/sys/netinet/sctp_asconf.h
+++ b/sys/netinet/sctp_asconf.h
@@ -58,10 +58,14 @@ sctp_addr_mgmt_ep_sa(struct sctp_inpcb *, struct sockaddr *,
uint32_t, uint32_t, struct sctp_ifa *);
-int sctp_iterator_ep(struct sctp_inpcb *inp, void *ptr, uint32_t val);
-void sctp_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, uint32_t type);
-int sctp_iterator_ep_end(struct sctp_inpcb *inp, void *ptr, uint32_t val);
-void sctp_iterator_end(void *ptr, uint32_t val);
+extern int
+sctp_asconf_iterator_ep(struct sctp_inpcb *inp, void *ptr,
+ uint32_t val);
+extern void
+sctp_asconf_iterator_stcb(struct sctp_inpcb *inp,
+ struct sctp_tcb *stcb,
+ void *ptr, uint32_t type);
+extern void sctp_asconf_iterator_end(void *ptr, uint32_t val);
extern int32_t
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index c426cd6..9d03faf 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -2345,15 +2345,20 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp,
stcb->sctp_ep, stcb, NULL);
}
/*
- * set ASCONF timer if ASCONFs are pending and allowed (eg.
- * addresses changed when init/cookie echo in flight)
+ * send ASCONF if parameters are pending and ASCONFs are
+ * allowed (eg. addresses changed when init/cookie echo were
+ * in flight)
*/
if ((sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_DO_ASCONF)) &&
(stcb->asoc.peer_supports_asconf) &&
(!TAILQ_EMPTY(&stcb->asoc.asconf_queue))) {
+#ifdef SCTP_TIMER_BASED_ASCONF
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
stcb->sctp_ep, stcb,
stcb->asoc.primary_destination);
+#else
+ sctp_send_asconf(stcb, stcb->asoc.primary_destination);
+#endif
}
}
/* Toss the cookie if I can */
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index a46c782..7d12369 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -2380,9 +2380,12 @@ sctp_choose_boundspecific_inp(struct sctp_inpcb *inp,
if (sctp_ifn) {
/* is a preferred 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))
+ if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) &&
+ (non_asoc_addr_ok == 0))
continue;
- sifa = sctp_is_ifa_addr_preferred(sctp_ifa, dest_is_loop, dest_is_priv, fam);
+ sifa = sctp_is_ifa_addr_preferred(sctp_ifa,
+ dest_is_loop,
+ dest_is_priv, fam);
if (sifa == NULL)
continue;
if (sctp_is_addr_in_ep(inp, sifa)) {
@@ -2403,12 +2406,14 @@ once_again:
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)) {
+ for (laddr = inp->next_addr_touse; laddr;
+ laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
- sifa = sctp_is_ifa_addr_preferred(laddr->ifa, dest_is_loop, dest_is_priv, fam);
+ sifa = sctp_is_ifa_addr_preferred(laddr->ifa, dest_is_loop,
+ dest_is_priv, fam);
if (sifa == NULL)
continue;
atomic_add_int(&sifa->refcount, 1);
@@ -2426,12 +2431,14 @@ once_again_too:
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)) {
+ for (laddr = inp->next_addr_touse; laddr;
+ laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
if (laddr->ifa == NULL) {
/* address has been removed */
continue;
}
- sifa = sctp_is_ifa_addr_acceptable(laddr->ifa, dest_is_loop, dest_is_priv, fam);
+ sifa = sctp_is_ifa_addr_acceptable(laddr->ifa, dest_is_loop,
+ dest_is_priv, fam);
if (sifa == NULL)
continue;
atomic_add_int(&sifa->refcount, 1);
@@ -2836,7 +2843,7 @@ bound_all_plan_b:
plan_d:
/*
* plan_d: We are in trouble. No preferred address on the emit
- * interface. And not even a perfered address on all interfaces. Go
+ * interface. And not even a preferred address on all interfaces. Go
* out and see if we can find an acceptable address somewhere
* amongst all interfaces.
*/
@@ -2875,8 +2882,8 @@ plan_d:
}
/*
* 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 :-(
+ * restricted list and non_asoc_address is NOT ok, or it is on our
+ * restricted list. We can't source to it :-(
*/
return (NULL);
}
@@ -2939,7 +2946,7 @@ sctp_source_address_selection(struct sctp_inpcb *inp,
*
* Decisions:
*
- * - count the number of addresses on the interface. - if its one, no
+ * - count the number of addresses on the interface. - if it is 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
@@ -2994,8 +3001,7 @@ sctp_source_address_selection(struct sctp_inpcb *inp,
SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, (struct sockaddr *)to);
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.
+ * Bound all case
*/
answer = sctp_choose_boundall(inp, stcb, net, ro, vrf_id,
dest_is_priv, dest_is_loop,
@@ -3003,21 +3009,7 @@ sctp_source_address_selection(struct sctp_inpcb *inp,
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.
+ * Subset bound case
*/
if (stcb) {
answer = sctp_choose_boundspecific_stcb(inp, stcb, net, ro,
@@ -3358,6 +3350,10 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
net->src_addr_selected = 0;
}
if (net->src_addr_selected == 0) {
+ if (out_of_asoc_ok) {
+ /* do not cache */
+ goto temp_v4_src;
+ }
/* Cache the source address */
net->ro._s_addr = sctp_source_address_selection(inp, stcb,
ro, net, out_of_asoc_ok,
@@ -3373,8 +3369,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
} else {
struct sctp_ifa *_lsrc;
- _lsrc = sctp_source_address_selection(inp,
- stcb, ro, net, out_of_asoc_ok, vrf_id);
+ temp_v4_src:
+ _lsrc = sctp_source_address_selection(inp, stcb, ro,
+ net,
+ out_of_asoc_ok,
+ vrf_id);
if (_lsrc == NULL) {
goto no_route;
}
@@ -3621,6 +3620,10 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
net->src_addr_selected = 0;
}
if (net->src_addr_selected == 0) {
+ if (out_of_asoc_ok) {
+ /* do not cache */
+ goto temp_v6_src;
+ }
/* Cache the source address */
net->ro._s_addr = sctp_source_address_selection(inp,
stcb,
@@ -3639,7 +3642,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
} else {
struct sctp_ifa *_lsrc;
- _lsrc = sctp_source_address_selection(inp, stcb, ro, net, out_of_asoc_ok, vrf_id);
+ temp_v6_src:
+ _lsrc = sctp_source_address_selection(inp, stcb, ro,
+ net,
+ out_of_asoc_ok,
+ vrf_id);
if (_lsrc == NULL) {
goto no_route;
}
@@ -7145,6 +7152,12 @@ again_one_more_time:
*/
hbflag = 1;
asconf = 1;
+ /*
+ * should sysctl this: don't
+ * bundle data with ASCONF
+ * since it requires AUTH
+ */
+ no_data_chunks = 1;
}
chk->sent = SCTP_DATAGRAM_SENT;
chk->snd_count++;
@@ -7158,7 +7171,12 @@ again_one_more_time:
*/
if (asconf) {
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, net);
- asconf = 0;
+ /*
+ * do NOT clear the asconf
+ * flag as it is used to do
+ * appropriate source
+ * address selection.
+ */
}
if (cookie) {
sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net);
@@ -7406,8 +7424,13 @@ again_one_more_time:
if (outchain) {
/* We may need to start a control timer or two */
if (asconf) {
- sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, net);
- asconf = 0;
+ sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp,
+ stcb, net);
+ /*
+ * do NOT clear the asconf flag as it is
+ * used to do appropriate source address
+ * selection.
+ */
}
if (cookie) {
sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net);
@@ -7895,15 +7918,14 @@ void
sctp_send_asconf(struct sctp_tcb *stcb, struct sctp_nets *net)
{
/*
- * formulate and queue an ASCONF to the peer ASCONF parameters
- * should be queued on the assoc queue
+ * formulate and queue an ASCONF to the peer. ASCONF parameters
+ * should be queued on the assoc queue.
*/
struct sctp_tmit_chunk *chk;
struct mbuf *m_asconf;
struct sctp_asconf_chunk *acp;
int len;
-
SCTP_TCB_LOCK_ASSERT(stcb);
/* compose an ASCONF chunk, maximum length is PMTU */
m_asconf = sctp_compose_asconf(stcb, &len);
@@ -7937,8 +7959,8 @@ void
sctp_send_asconf_ack(struct sctp_tcb *stcb, uint32_t retrans)
{
/*
- * formulate and queue a asconf-ack back to sender the asconf-ack
- * must be stored in the tcb
+ * formulate and queue a asconf-ack back to sender. the asconf-ack
+ * must be stored in the tcb.
*/
struct sctp_tmit_chunk *chk;
struct mbuf *m_ack, *m;
@@ -7949,10 +7971,10 @@ sctp_send_asconf_ack(struct sctp_tcb *stcb, uint32_t retrans)
return;
}
/* copy the asconf_ack */
- m_ack = SCTP_M_COPYM(stcb->asoc.last_asconf_ack_sent, 0, M_COPYALL, M_DONTWAIT);
+ m_ack = SCTP_M_COPYM(stcb->asoc.last_asconf_ack_sent, 0, M_COPYALL,
+ M_DONTWAIT);
if (m_ack == NULL) {
/* couldn't copy it */
-
return;
}
sctp_alloc_a_chunk(stcb, chk);
@@ -11335,11 +11357,6 @@ sctp_lower_sosend(struct socket *so,
atomic_add_int(&stcb->total_sends, 1);
if (top == NULL) {
struct sctp_stream_queue_pending *sp;
-
-#ifdef INVARIANTS
- struct sctp_stream_queue_pending *msp;
-
-#endif
struct sctp_stream_out *strm;
uint32_t sndout, initial_out;
int user_marks_eor;
@@ -11373,11 +11390,6 @@ sctp_lower_sosend(struct socket *so,
goto out;
}
SCTP_TCB_SEND_LOCK(stcb);
-#ifdef INVARIANTS
- msp = TAILQ_LAST(&strm->outqueue, sctp_streamhead);
- if (msp && (msp->msg_is_complete == 0))
- panic("Huh, new mesg and old not done?");
-#endif
if (sp->msg_is_complete) {
strm->last_msg_incomplete = 0;
asoc->stream_locked = 0;
diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c
index 0096047..df6689f 100644
--- a/sys/netinet/sctp_pcb.c
+++ b/sys/netinet/sctp_pcb.c
@@ -2123,9 +2123,10 @@ sctp_isport_inuse(struct sctp_inpcb *inp, uint16_t lport, uint32_t vrf_id)
+/* sctp_ifap is used to bypass normal local address validation checks */
int
sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
- struct thread *p)
+ struct sctp_ifa *sctp_ifap, struct thread *p)
{
/* bind a ep to a socket address */
struct sctppcbhead *head;
@@ -2396,8 +2397,17 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
* zero out the port to find the address! yuck! can't do
* this earlier since need port for sctp_pcb_findep()
*/
- ifa = sctp_find_ifa_by_addr((struct sockaddr *)&store_sa,
- vrf_id, 0);
+ if (sctp_ifap != NULL)
+ ifa = sctp_ifap;
+ else {
+ /*
+ * Note for BSD we hit here always other O/S's will
+ * pass things in via the sctp_ifap argument
+ * (Panda).
+ */
+ ifa = sctp_find_ifa_by_addr((struct sockaddr *)&store_sa,
+ vrf_id, 0);
+ }
if (ifa == NULL) {
/* Can't find an interface with that address */
SCTP_INP_WUNLOCK(inp);
@@ -3401,6 +3411,7 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr,
*/
if ((err = sctp_inpcb_bind(inp->sctp_socket,
(struct sockaddr *)NULL,
+ (struct sctp_ifa *)NULL,
p
))) {
/* bind error, probably perm */
@@ -4461,13 +4472,12 @@ sctp_del_local_addr_ep(struct sctp_inpcb *inp, struct sctp_ifa *ifa)
}
/*
- * Add the addr to the TCB local address list For the BOUNDALL or dynamic
- * case, this is a "pending" address list (eg. addresses waiting for an
- * ASCONF-ACK response) For the subset binding, static case, this is a
- * "valid" address list
+ * Add the address to the TCB local address restricted list.
+ * This is a "pending" address list (eg. addresses waiting for an
+ * ASCONF-ACK response) and cannot be used as a valid source address.
*/
void
-sctp_add_local_addr_assoc(struct sctp_tcb *stcb, struct sctp_ifa *ifa, int restricted_list)
+sctp_add_local_addr_restricted(struct sctp_tcb *stcb, struct sctp_ifa *ifa)
{
struct sctp_inpcb *inp;
struct sctp_laddr *laddr;
@@ -4538,10 +4548,10 @@ sctp_remove_laddr(struct sctp_laddr *laddr)
}
/*
- * Remove an address from the TCB local address list
+ * Remove a local address from the TCB local address restricted list
*/
void
-sctp_del_local_addr_assoc(struct sctp_tcb *stcb, struct sctp_ifa *ifa)
+sctp_del_local_addr_restricted(struct sctp_tcb *stcb, struct sctp_ifa *ifa)
{
struct sctp_inpcb *inp;
struct sctp_laddr *laddr;
diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h
index db0fcd8..6cdf0ca 100644
--- a/sys/netinet/sctp_pcb.h
+++ b/sys/netinet/sctp_pcb.h
@@ -468,7 +468,7 @@ struct sctp_inpcb *sctp_pcb_findep(struct sockaddr *, int, int, uint32_t);
int
sctp_inpcb_bind(struct socket *, struct sockaddr *,
- struct thread *);
+ struct sctp_ifa *, struct thread *);
struct sctp_tcb *
sctp_findassociation_addr(struct mbuf *, int, int,
@@ -533,9 +533,8 @@ int sctp_del_remote_addr(struct sctp_tcb *, struct sockaddr *);
void sctp_pcb_init(void);
-void sctp_add_local_addr_assoc(struct sctp_tcb *, struct sctp_ifa *, int);
-
-void sctp_del_local_addr_assoc(struct sctp_tcb *, struct sctp_ifa *);
+void sctp_add_local_addr_restricted(struct sctp_tcb *, struct sctp_ifa *);
+void sctp_del_local_addr_restricted(struct sctp_tcb *, struct sctp_ifa *);
int
sctp_load_addresses_from_init(struct sctp_tcb *, struct mbuf *, int, int,
diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h
index d1ee1cd..79df612 100644
--- a/sys/netinet/sctp_structs.h
+++ b/sys/netinet/sctp_structs.h
@@ -556,8 +556,10 @@ struct sctp_cc_functions {
struct sctp_association {
/* association state */
int state;
+
/* queue of pending addrs to add/delete */
struct sctp_asconf_addrhead asconf_queue;
+
struct timeval time_entered; /* time we entered state */
struct timeval time_last_rcvd;
struct timeval time_last_sent;
@@ -567,21 +569,23 @@ struct sctp_association {
/* timers and such */
struct sctp_timer hb_timer; /* hb timer */
struct sctp_timer dack_timer; /* Delayed ack timer */
- struct sctp_timer asconf_timer; /* Asconf */
+ struct sctp_timer asconf_timer; /* asconf */
struct sctp_timer strreset_timer; /* stream reset */
- struct sctp_timer shut_guard_timer; /* guard */
+ struct sctp_timer shut_guard_timer; /* shutdown guard */
struct sctp_timer autoclose_timer; /* automatic close timer */
struct sctp_timer delayed_event_timer; /* timer for delayed events */
- /* list of local addresses when add/del in progress */
+ /* list of restricted local addresses */
struct sctpladdr sctp_restricted_addrs;
- struct sctpnetlisthead nets;
+ /* last local address pending deletion (waiting for an address add) */
+ struct sctp_ifa *asconf_addr_del_pending;
+
+ struct sctpnetlisthead nets; /* remote address list */
/* Free chunk list */
struct sctpchunk_listhead free_chunks;
-
/* Control chunk queue */
struct sctpchunk_listhead control_send_queue;
@@ -595,7 +599,6 @@ struct sctp_association {
struct sctpchunk_listhead sent_queue;
struct sctpchunk_listhead send_queue;
-
/* re-assembly queue for fragmented chunks on the inbound path */
struct sctpchunk_listhead reasmqueue;
@@ -618,12 +621,6 @@ struct sctp_association {
/* If an iterator is looking at me, this is it */
struct sctp_iterator *stcb_starting_point_for_iterator;
- /* ASCONF destination address last sent to */
-/* struct sctp_nets *asconf_last_sent_to;*/
-/* Peter, greppign for the above shows only on strange set
- * I don't think we need it so I have commented it out.
- */
-
/* ASCONF save the last ASCONF-ACK so we can resend it if necessary */
struct mbuf *last_asconf_ack_sent;
@@ -907,7 +904,8 @@ struct sctp_association {
* lock flag: 0 is ok to send, 1+ (duals as a retran count) is
* awaiting ACK
*/
- uint16_t asconf_sent; /* possibly removable REM */
+ uint16_t asconf_sent;
+
uint16_t mapping_array_size;
uint16_t last_strm_seq_delivered;
@@ -944,6 +942,7 @@ struct sctp_association {
uint8_t hb_random_idx;
uint8_t hb_is_disabled; /* is the hb disabled? */
uint8_t default_tos;
+ uint8_t asconf_del_pending; /* asconf delete last addr pending */
/* ECN Nonce stuff */
uint8_t receiver_nonce_sum; /* nonce I sum and put in my sack */
diff --git a/sys/netinet/sctp_timer.c b/sys/netinet/sctp_timer.c
index 4149afc..75214ed 100644
--- a/sys/netinet/sctp_timer.c
+++ b/sys/netinet/sctp_timer.c
@@ -1301,12 +1301,14 @@ sctp_asconf_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct sctp_nets *alt;
struct sctp_tmit_chunk *asconf, *chk;
- /* is this the first send, or a retransmission? */
+ /* is this a first send, or a retransmission? */
if (stcb->asoc.asconf_sent == 0) {
/* compose a new ASCONF chunk and send it */
sctp_send_asconf(stcb, net);
} else {
- /* Retransmission of the existing ASCONF needed... */
+ /*
+ * Retransmission of the existing ASCONF is needed
+ */
/* find the existing ASCONF */
TAILQ_FOREACH(asconf, &stcb->asoc.control_send_queue,
@@ -1324,25 +1326,21 @@ sctp_asconf_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
/* Assoc is over */
return (1);
}
- /*
- * PETER? FIX? How will the following code ever run? If the
- * max_send_times is hit, threshold managment will blow away
- * the association?
- */
if (asconf->snd_count > stcb->asoc.max_send_times) {
/*
- * Something is rotten, peer is not responding to
- * ASCONFs but maybe is to data etc. e.g. it is not
- * properly handling the chunk type upper bits Mark
- * this peer as ASCONF incapable and cleanup
+ * Something is rotten: our peer is not responding
+ * to ASCONFs but apparently is to other chunks.
+ * i.e. it is not properly handling the chunk type
+ * upper bits. Mark this peer as ASCONF incapable
+ * and cleanup.
*/
SCTPDBG(SCTP_DEBUG_TIMER1, "asconf_timer: Peer has not responded to our repeated ASCONFs\n");
sctp_asconf_cleanup(stcb, net);
return (0);
}
/*
- * cleared theshold management now lets backoff the address
- * & select an alternate
+ * cleared threshold management, so now backoff the net and
+ * select an alternate
*/
sctp_backoff_on_timeout(stcb, asconf->whoTo, 1, 0);
alt = sctp_find_alternate_net(stcb, asconf->whoTo, 0);
@@ -1350,7 +1348,7 @@ sctp_asconf_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
asconf->whoTo = alt;
atomic_add_int(&alt->ref_count, 1);
- /* See if a ECN Echo is also stranded */
+ /* See if an ECN Echo is also stranded */
TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) {
if ((chk->whoTo == net) &&
(chk->rec.chunk_id.id == SCTP_ECN_ECHO)) {
@@ -1366,7 +1364,7 @@ sctp_asconf_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
if (net->dest_state & SCTP_ADDR_NOT_REACHABLE) {
/*
* If the address went un-reachable, we need to move
- * to alternates for ALL chk's in queue
+ * to the alternate for ALL chunks in queue
*/
sctp_move_all_chunks_to_alt(stcb, net, alt);
}
diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c
index 192ea43..240e96c 100644
--- a/sys/netinet/sctp_usrreq.c
+++ b/sys/netinet/sctp_usrreq.c
@@ -573,7 +573,7 @@ sctp_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
if (inp == 0)
return EINVAL;
- error = sctp_inpcb_bind(so, addr, p);
+ error = sctp_inpcb_bind(so, addr, NULL, p);
return error;
}
@@ -1170,7 +1170,6 @@ sctp_fill_up_addresses_vrf(struct sctp_inpcb *inp,
} else {
struct sctp_laddr *laddr;
- /* The list is a NEGATIVE list */
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
if (stcb) {
if (sctp_is_addr_restricted(stcb, laddr->ifa)) {
@@ -1345,7 +1344,7 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval,
if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) ==
SCTP_PCB_FLAGS_UNBOUND) {
/* Bind a ephemeral port */
- error = sctp_inpcb_bind(so, NULL, p);
+ error = sctp_inpcb_bind(so, NULL, NULL, p);
if (error) {
goto out_now;
}
@@ -3479,7 +3478,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
sasoc->sasoc_peer_rwnd = 0;
sasoc->sasoc_local_rwnd = 0;
if (sasoc->sasoc_cookie_life) {
- stcb->asoc.cookie_life = sasoc->sasoc_cookie_life;
+ stcb->asoc.cookie_life = MSEC_TO_TICKS(sasoc->sasoc_cookie_life);
}
SCTP_TCB_UNLOCK(stcb);
@@ -3805,7 +3804,7 @@ sctp_connect(struct socket *so, struct sockaddr *addr, struct thread *p)
if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) ==
SCTP_PCB_FLAGS_UNBOUND) {
/* Bind a ephemeral port */
- error = sctp_inpcb_bind(so, NULL, p);
+ error = sctp_inpcb_bind(so, NULL, NULL, p);
if (error) {
goto out_now;
}
@@ -3916,7 +3915,7 @@ sctp_listen(struct socket *so, int backlog, struct thread *p)
/* We must do a bind. */
SOCK_UNLOCK(so);
SCTP_INP_RUNLOCK(inp);
- if ((error = sctp_inpcb_bind(so, NULL, p))) {
+ if ((error = sctp_inpcb_bind(so, NULL, NULL, p))) {
/* bind error, probably perm */
return (error);
}
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index b70a1e7..bf0eb29 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -1373,15 +1373,15 @@ sctp_handle_addr_wq(void)
if (asc->cnt == 0) {
SCTP_FREE(asc, SCTP_M_ASC_IT);
} else {
- (void)sctp_initiate_iterator(sctp_iterator_ep,
- sctp_iterator_stcb,
+ (void)sctp_initiate_iterator(sctp_asconf_iterator_ep,
+ sctp_asconf_iterator_stcb,
NULL, /* No ep end for boundall */
SCTP_PCB_FLAGS_BOUNDALL,
SCTP_PCB_ANY_FEATURES,
- SCTP_ASOC_ANY_STATE, (void *)asc, 0,
- sctp_iterator_end, NULL, 0);
+ SCTP_ASOC_ANY_STATE,
+ (void *)asc, 0,
+ sctp_asconf_iterator_end, NULL, 0);
}
-
}
int retcode = 0;
@@ -2078,8 +2078,8 @@ sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
break;
case SCTP_TIMER_TYPE_STRRESET:
/*
- * Here the timer comes from the inp but its value is from
- * the RTO.
+ * Here the timer comes from the stcb but its value is from
+ * the net's RTO.
*/
if ((stcb == NULL) || (net == NULL)) {
return;
@@ -2122,8 +2122,8 @@ sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
break;
case SCTP_TIMER_TYPE_ASCONF:
/*
- * Here the timer comes from the inp but its value is from
- * the RTO.
+ * Here the timer comes from the stcb but its value is from
+ * the net's RTO.
*/
if ((stcb == NULL) || (net == NULL)) {
return;
@@ -5932,7 +5932,7 @@ sctp_bindx_add_address(struct socket *so, struct sctp_inpcb *inp,
*error = EINVAL;
return;
}
- *error = sctp_inpcb_bind(so, addr_touse, p);
+ *error = sctp_inpcb_bind(so, addr_touse, NULL, p);
return;
}
/*
@@ -6056,3 +6056,113 @@ sctp_bindx_delete_address(struct socket *so, struct sctp_inpcb *inp,
*/
}
}
+
+/*
+ * returns the valid local address count for an assoc, taking into account
+ * all scoping rules
+ */
+int
+sctp_local_addr_count(struct sctp_tcb *stcb)
+{
+ int loopback_scope, ipv4_local_scope, local_scope, site_scope;
+ int ipv4_addr_legal, ipv6_addr_legal;
+ struct sctp_vrf *vrf;
+ struct sctp_ifn *sctp_ifn;
+ struct sctp_ifa *sctp_ifa;
+ int count = 0;
+
+ /* Turn on all the appropriate scopes */
+ loopback_scope = stcb->asoc.loopback_scope;
+ ipv4_local_scope = stcb->asoc.ipv4_local_scope;
+ local_scope = stcb->asoc.local_scope;
+ site_scope = stcb->asoc.site_scope;
+ ipv4_addr_legal = ipv6_addr_legal = 0;
+ if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+ ipv6_addr_legal = 1;
+ if (SCTP_IPV6_V6ONLY(stcb->sctp_ep) == 0) {
+ ipv4_addr_legal = 1;
+ }
+ } else {
+ ipv4_addr_legal = 1;
+ }
+
+ vrf = sctp_find_vrf(stcb->asoc.vrf_id);
+ if (vrf == NULL) {
+ /* no vrf, no addresses */
+ return (0);
+ }
+ if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+ /*
+ * bound all case: go through all ifns on the vrf
+ */
+ LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) {
+ if ((loopback_scope == 0) &&
+ SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) {
+ continue;
+ }
+ LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) {
+ if (sctp_is_addr_restricted(stcb, sctp_ifa))
+ continue;
+
+ if ((sctp_ifa->address.sa.sa_family == AF_INET) &&
+ (ipv4_addr_legal)) {
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *)&sctp_ifa->address.sa;
+ if (sin->sin_addr.s_addr == 0) {
+ /* skip unspecified addrs */
+ continue;
+ }
+ if ((ipv4_local_scope == 0) &&
+ (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
+ continue;
+ }
+ /* count this one */
+ count++;
+ } else if ((sctp_ifa->address.sa.sa_family == AF_INET6) &&
+ (ipv6_addr_legal)) {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)&sctp_ifa->address.sa;
+ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ if (local_scope == 0)
+ continue;
+ if (sin6->sin6_scope_id == 0) {
+ if (sa6_recoverscope(sin6) != 0)
+ /*
+ * bad link
+ * local
+ * address
+ */
+ continue;
+ }
+ }
+ if ((site_scope == 0) &&
+ (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) {
+ continue;
+ }
+ /* count this one */
+ count++;
+ }
+ }
+ }
+ } else {
+ /*
+ * subset bound case
+ */
+ struct sctp_laddr *laddr;
+
+ LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list,
+ sctp_nxt_addr) {
+ if (sctp_is_addr_restricted(stcb, laddr->ifa)) {
+ continue;
+ }
+ /* count this one */
+ count++;
+ }
+ }
+ return (count);
+}
diff --git a/sys/netinet/sctputil.h b/sys/netinet/sctputil.h
index 0c90058..46d63d8 100644
--- a/sys/netinet/sctputil.h
+++ b/sys/netinet/sctputil.h
@@ -219,6 +219,8 @@ sctp_bindx_delete_address(struct socket *so, struct sctp_inpcb *inp,
struct sockaddr *sa, sctp_assoc_t assoc_id,
uint32_t vrf_id, int *error);
+int sctp_local_addr_count(struct sctp_tcb *stcb);
+
#ifdef SCTP_MBCNT_LOGGING
void
sctp_free_bufspace(struct sctp_tcb *, struct sctp_association *,
diff --git a/sys/netinet6/sctp6_usrreq.c b/sys/netinet6/sctp6_usrreq.c
index ca72838..6517b48 100644
--- a/sys/netinet6/sctp6_usrreq.c
+++ b/sys/netinet6/sctp6_usrreq.c
@@ -614,7 +614,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
in6_sin6_2_sin(&sin, sin6_p);
inp6->inp_vflag |= INP_IPV4;
inp6->inp_vflag &= ~INP_IPV6;
- error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, p);
+ error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, NULL, p);
return error;
}
}
@@ -634,7 +634,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
return EINVAL;
}
}
- error = sctp_inpcb_bind(so, addr, p);
+ error = sctp_inpcb_bind(so, addr, NULL, p);
return error;
}
OpenPOWER on IntegriCloud