- 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
This commit is contained in:
Randall Stewart 2007-07-24 20:06:02 +00:00
parent 064d25a08a
commit 1b649582bb
13 changed files with 566 additions and 223 deletions

View File

@ -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,7 +263,58 @@ 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) {
@ -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;

View File

@ -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 */
if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) {
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
stcb->sctp_ep, stcb,
stcb->asoc.primary_destination);
}
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
}
} 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) {
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
stcb->sctp_ep, stcb,
stcb->asoc.primary_destination);
}
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
}
}
} /* 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);

View File

@ -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

View File

@ -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 */

View File

@ -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 @@ sctp_choose_boundspecific_inp(struct sctp_inpcb *inp,
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 @@ sctp_choose_boundspecific_inp(struct sctp_inpcb *inp,
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 @@ sctp_choose_boundall(struct sctp_inpcb *inp,
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 @@ sctp_choose_boundall(struct sctp_inpcb *inp,
}
/*
* 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 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
*/
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 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
*/
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 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
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;

View File

@ -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;

View File

@ -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,

View File

@ -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 */

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 *,

View File

@ -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;
}