- 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 sctp_getaddresses *gaddrs;
struct sockaddr *sa; struct sockaddr *sa;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
int i, sz, argsz; int i, sz, argsz;
uint16_t sport = 0;
/* validate the flags */ /* validate the flags */
if ((flags != SCTP_BINDX_ADD_ADDR) && if ((flags != SCTP_BINDX_ADD_ADDR) &&
@ -260,7 +263,58 @@ sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, int flags)
errno = ENOMEM; errno = ENOMEM;
return (-1); return (-1);
} }
/* First pre-screen the addresses */
sa = addrs; 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++) { for (i = 0; i < addrcnt; i++) {
sz = sa->sa_len; sz = sa->sa_len;
if (sa->sa_family == AF_INET) { 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; errno = EINVAL;
return (-1); 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)); 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; int add_len, len, no_end_cx = 0;
struct sockaddr *at; struct sockaddr *at;
if (addrs == NULL) {
errno = EINVAL;
return (-1);
}
#ifdef SYS_sctp_generic_sendmsg #ifdef SYS_sctp_generic_sendmsg
if (addrcnt < SCTP_SMALL_IOVEC_SIZE) { if (addrcnt < SCTP_SMALL_IOVEC_SIZE) {
socklen_t l; socklen_t l;
@ -643,10 +748,6 @@ sctp_sendx(int sd, const void *msg, size_t msg_len,
} }
#endif #endif
if (addrs == NULL) {
errno = EINVAL;
return (-1);
}
len = sizeof(int); len = sizeof(int);
at = addrs; at = addrs;
cnt = 0; cnt = 0;

View File

@ -295,8 +295,7 @@ sctp_process_asconf_add_ip(struct mbuf *m, struct sctp_asconf_paramhdr *aph,
} }
static int static int
sctp_asconf_del_remote_addrs_except(struct sctp_tcb *stcb, sctp_asconf_del_remote_addrs_except(struct sctp_tcb *stcb, struct sockaddr *src)
struct sockaddr *src)
{ {
struct sctp_nets *src_net, *net; 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 * static struct mbuf *
sctp_process_asconf_set_primary(struct mbuf *m, sctp_process_asconf_set_primary(struct mbuf *m,
struct sctp_asconf_paramhdr *aph, struct sctp_tcb *stcb, struct sctp_asconf_paramhdr *aph,
int response_required) struct sctp_tcb *stcb, int response_required)
{ {
struct mbuf *m_reply = NULL; struct mbuf *m_reply = NULL;
struct sockaddr_storage sa_source, sa_store; 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. * 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. * type: SCTP_ADD_IP_ADDRESS or SCTP_DEL_IP_ADDRESS.
* flag: 1=success, 0=failure. * 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... * DEL_IP_ADDRESS is never actually added to the list...
*/ */
if (flag) { if (flag) {
/* success case, so remove from the list */ /* success case, so remove from the restricted list */
sctp_del_local_addr_assoc(stcb, addr); sctp_del_local_addr_restricted(stcb, addr);
} }
/* else, leave it on the list */ /* 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. * type = SCTP_ADD_IP_ADDRESS, SCTP_DEL_IP_ADDRESS, SCTP_SET_PRIM_ADDR.
* returns 0 if completed, non-zero if not completed. * returns 0 if queued, -1 if not queued/removed.
* NOTE: if adding, but delete already scheduled (and not yet sent out), * NOTE: if adding, but a delete for the same address is already scheduled
* simply remove from queue. Same for deleting an address already scheduled * (and not yet sent out), simply remove it from queue. Same for deleting
* for add. If a duplicate operation is found, ignore the new one. * 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(struct sctp_tcb *stcb, struct sctp_ifa *ifa, sctp_asconf_queue_mgmt(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
uint16_t type) uint16_t type)
{ {
struct sctp_asconf_addr *aa, *aa_next; struct sctp_asconf_addr *aa, *aa_next;
struct sockaddr *sa; 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 */ /* make sure the request isn't already in the queue */
for (aa = TAILQ_FIRST(&stcb->asoc.asconf_queue); aa != NULL; for (aa = TAILQ_FIRST(&stcb->asoc.asconf_queue); aa != NULL;
aa = aa_next) { aa = aa_next) {
@ -929,30 +925,36 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
return (-1); return (-1);
} }
/* is the negative request already in queue, and not sent */ /* is the negative request already in queue, and not sent */
if (aa->sent == 0 && if ((aa->sent == 0) && (type == SCTP_ADD_IP_ADDRESS) &&
/* add requested, delete already queued */ (aa->ap.aph.ph.param_type == SCTP_DEL_IP_ADDRESS)) {
((type == SCTP_ADD_IP_ADDRESS && /* add requested, delete already queued */
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 */
TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next); TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next);
/* take the entry off the appropriate list */ /* remove the ifa from the restricted list */
sctp_asconf_addr_mgmt_ack(stcb, aa->ifa, type, 1); sctp_del_local_addr_restricted(stcb, ifa);
/* free the entry */ /* free the asconf param */
sctp_free_ifa(aa->ifa);
SCTP_FREE(aa, SCTP_M_ASC_ADDR); 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); return (-1);
} }
} /* for each aa */ } /* for each aa */
/* adding new request to the queue */ /* 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) { if (aa == NULL) {
/* didn't get memory */ /* didn't get memory */
SCTPDBG(SCTP_DEBUG_ASCONF1, SCTPDBG(SCTP_DEBUG_ASCONF1, "asconf_queue_mgmt: failed to get memory!\n");
"asconf_queue_add: failed to get memory!\n");
return (-1); return (-1);
} }
/* fill in asconf address parameter fields */ /* 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; sa = (struct sockaddr *)sin6;
aa->ap.addrp.ph.param_type = SCTP_IPV6_ADDRESS; aa->ap.addrp.ph.param_type = SCTP_IPV6_ADDRESS;
aa->ap.addrp.ph.param_length = (sizeof(struct sctp_ipv6addr_param)); aa->ap.addrp.ph.param_length = (sizeof(struct sctp_ipv6addr_param));
aa->ap.aph.ph.param_length = aa->ap.aph.ph.param_length = sizeof(struct sctp_asconf_paramhdr) +
sizeof(struct sctp_asconf_paramhdr) +
sizeof(struct sctp_ipv6addr_param); sizeof(struct sctp_ipv6addr_param);
memcpy(&aa->ap.addrp.addr, &sin6->sin6_addr, memcpy(&aa->ap.addrp.addr, &sin6->sin6_addr,
sizeof(struct in6_addr)); sizeof(struct in6_addr));
} else if (ifa->address.sa.sa_family == AF_INET) { } else if (ifa->address.sa.sa_family == AF_INET) {
/* IPv4 address */ /* 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; sa = (struct sockaddr *)sin;
aa->ap.addrp.ph.param_type = SCTP_IPV4_ADDRESS; aa->ap.addrp.ph.param_type = SCTP_IPV4_ADDRESS;
aa->ap.addrp.ph.param_length = (sizeof(struct sctp_ipv4addr_param)); aa->ap.addrp.ph.param_length = (sizeof(struct sctp_ipv4addr_param));
aa->ap.aph.ph.param_length = aa->ap.aph.ph.param_length = sizeof(struct sctp_asconf_paramhdr) +
sizeof(struct sctp_asconf_paramhdr) +
sizeof(struct sctp_ipv4addr_param); sizeof(struct sctp_ipv4addr_param);
memcpy(&aa->ap.addrp.addr, &sin->sin_addr, memcpy(&aa->ap.addrp.addr, &sin->sin_addr,
sizeof(struct in_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 */ /* add goes to the front of the queue */
TAILQ_INSERT_HEAD(&stcb->asoc.asconf_queue, aa, next); TAILQ_INSERT_HEAD(&stcb->asoc.asconf_queue, aa, next);
SCTPDBG(SCTP_DEBUG_ASCONF2, 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); SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
} else { } else {
/* delete and set primary goes to the back of the queue */ /* 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 #ifdef SCTP_DEBUG
if (sctp_debug_on && SCTP_DEBUG_ASCONF2) { if (sctp_debug_on && SCTP_DEBUG_ASCONF2) {
if (type == SCTP_DEL_IP_ADDRESS) { 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); SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
} else { } 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); SCTPDBG_ADDR(SCTP_DEBUG_ASCONF2, sa);
} }
} }
@ -1022,6 +1023,84 @@ sctp_asconf_queue_add(struct sctp_tcb *stcb, struct sctp_ifa *ifa,
return (0); 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. * 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. * 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 * simply remove from queue. Same for deleting an address already scheduled
* for add. If a duplicate operation is found, ignore the new one. * 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, sctp_asconf_queue_add_sa(struct sctp_tcb *stcb, struct sockaddr *sa,
uint16_t type) 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 */ /* delete the existing entry in the queue */
TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next); TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next);
/* free the entry */
sctp_free_ifa(aa->ifa); sctp_free_ifa(aa->ifa);
/* free the entry */
SCTP_FREE(aa, SCTP_M_ASC_ADDR); SCTP_FREE(aa, SCTP_M_ASC_ADDR);
return (-1); return (-1);
} else if (type == SCTP_DEL_IP_ADDRESS && } 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 */ /* delete the existing entry in the queue */
TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next); TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next);
/* take the entry off the appropriate list */ sctp_del_local_addr_restricted(stcb, aa->ifa);
sctp_asconf_addr_mgmt_ack(stcb, aa->ifa, type, 1);
/* free the entry */ /* free the entry */
sctp_free_ifa(aa->ifa);
SCTP_FREE(aa, SCTP_M_ASC_ADDR); SCTP_FREE(aa, SCTP_M_ASC_ADDR);
return (-1); return (-1);
} }
@ -1095,7 +1172,8 @@ sctp_asconf_queue_add_sa(struct sctp_tcb *stcb, struct sockaddr *sa,
return (-1); return (-1);
} }
/* adding new request to the queue */ /* 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) { if (aa == NULL) {
/* didn't get memory */ /* didn't get memory */
SCTPDBG(SCTP_DEBUG_ASCONF1, 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 */ /* clear the sent flag to allow new ASCONFs */
asoc->asconf_sent = 0; asoc->asconf_sent = 0;
if (!TAILQ_EMPTY(&stcb->asoc.asconf_queue)) { if (!TAILQ_EMPTY(&stcb->asoc.asconf_queue)) {
#ifdef SCTP_TIMER_BASED_ASCONF
/* we have more params, so restart our timer */ /* we have more params, so restart our timer */
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, stcb->sctp_ep, sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, stcb->sctp_ep,
stcb, net); 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 */ /* 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 * check address scope if address is out of scope, don't queue
* anything... note: this would leave the address on both inp and * 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) { if (stcb->asoc.peer_supports_asconf) {
/* queue an asconf for this addr */ /* queue an asconf for this addr */
status = sctp_asconf_queue_add(stcb, ifa, type); status = sctp_asconf_queue_add(stcb, ifa, type);
/* /*
* if queued ok, and in correct state, set the * if queued ok, and in the open state, send out the
* ASCONF timer if in non-open state, we will set * ASCONF. If in the non-open state, these will be
* this timer when the state does go open and do all * sent when the state goes open.
* the asconf's
*/ */
if (status == 0 && if (status == 0 &&
SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) { SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) {
#ifdef SCTP_TIMER_BASED_ASCONF
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp,
stcb, stcb->asoc.primary_destination); 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 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_asconf_iterator *asc;
struct sctp_ifa *ifa; struct sctp_ifa *ifa;
@ -1648,8 +1735,8 @@ sctp_iterator_ep(struct sctp_inpcb *inp, void *ptr, uint32_t val)
return (0); return (0);
} }
int static int
sctp_iterator_ep_end(struct sctp_inpcb *inp, void *ptr, uint32_t val) sctp_asconf_iterator_ep_end(struct sctp_inpcb *inp, void *ptr, uint32_t val)
{ {
struct sctp_ifa *ifa; struct sctp_ifa *ifa;
struct sctp_asconf_iterator *asc; struct sctp_asconf_iterator *asc;
@ -1683,14 +1770,15 @@ sctp_iterator_ep_end(struct sctp_inpcb *inp, void *ptr, uint32_t val)
} }
void void
sctp_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, sctp_asconf_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
uint32_t val) void *ptr, uint32_t val)
{ {
struct sctp_asconf_iterator *asc; struct sctp_asconf_iterator *asc;
struct sctp_ifa *ifa; struct sctp_ifa *ifa;
struct sctp_laddr *l; struct sctp_laddr *l;
int cnt_invalid = 0; int cnt_invalid = 0;
int type, status; int type, status;
int num_queued = 0;
asc = (struct sctp_asconf_iterator *)ptr; asc = (struct sctp_asconf_iterator *)ptr;
LIST_FOREACH(l, &asc->list_of_work, sctp_nxt_addr) { 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; continue;
} }
/* put this address on the "pending/do not use yet" list */
if (type == SCTP_ADD_IP_ADDRESS) { 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) { } else if (type == SCTP_DEL_IP_ADDRESS) {
struct sctp_nets *net; 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) { } else if (type == SCTP_SET_PRIM_ADDR) {
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) { if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) {
/* /* must validate the ifa is in the ep */
* must validate the ifa in question is in
* the ep
*/
if (sctp_is_addr_in_ep(stcb->sctp_ep, ifa) == 0) { if (sctp_is_addr_in_ep(stcb->sctp_ep, ifa) == 0) {
continue; 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 */ /* queue an asconf for this address add/delete */
if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF)) { if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF) &&
/* does the peer do asconf? */ stcb->asoc.peer_supports_asconf) {
if (stcb->asoc.peer_supports_asconf) { /* queue an asconf for this addr */
/* queue an asconf for this addr */ status = sctp_asconf_queue_add(stcb, ifa, type);
status = sctp_asconf_queue_add(stcb, ifa, type); /*
/* * if queued ok, and in the open state, update the
* if queued ok, and in correct state, set * count of queued params. If in the non-open
* the ASCONF timer if in non-open state, we * state, these get sent when the assoc goes open.
* will set this timer when the state does */
* go open and do all the asconf's if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) {
*/ if (status >= 0) {
if (status == 0 && num_queued++;
SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) {
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp,
stcb, stcb->asoc.primary_destination);
} }
} }
} }
} }
/*
* 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 void
sctp_iterator_end(void *ptr, uint32_t val) sctp_asconf_iterator_end(void *ptr, uint32_t val)
{ {
struct sctp_asconf_iterator *asc; struct sctp_asconf_iterator *asc;
struct sctp_ifa *ifa; 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 = * sa is the sockaddr to ask the peer to set primary to.
* completed, -1 = error * returns: 0 = completed, -1 = error
*/ */
int32_t int
sctp_set_primary_ip_address_sa(struct sctp_tcb *stcb, struct sockaddr *sa) sctp_set_primary_ip_address_sa(struct sctp_tcb *stcb, struct sockaddr *sa)
{ {
/* NOTE: we currently don't check the validity of the address! */ /* 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 */ /* queue an ASCONF:SET_PRIM_ADDR to be sent */
if (!sctp_asconf_queue_add_sa(stcb, sa, SCTP_SET_PRIM_ADDR)) { if (!sctp_asconf_queue_add_sa(stcb, sa, SCTP_SET_PRIM_ADDR)) {
/* set primary queuing succeeded */ /* 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, SCTPDBG(SCTP_DEBUG_ASCONF1,
"set_primary_ip_address_sa: queued on tcb=%p, ", "set_primary_ip_address_sa: queued on tcb=%p, ",
stcb); stcb);
SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, sa); 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 { } else {
SCTPDBG(SCTP_DEBUG_ASCONF1, "set_primary_ip_address_sa: failed to add to queue on tcb=%p, ", SCTPDBG(SCTP_DEBUG_ASCONF1, "set_primary_ip_address_sa: failed to add to queue on tcb=%p, ",
stcb); stcb);
@ -1908,15 +2000,18 @@ sctp_set_primary_ip_address(struct sctp_ifa *ifa)
if (!sctp_asconf_queue_add(stcb, ifa, if (!sctp_asconf_queue_add(stcb, ifa,
SCTP_SET_PRIM_ADDR)) { SCTP_SET_PRIM_ADDR)) {
/* set primary queuing succeeded */ /* 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, ", SCTPDBG(SCTP_DEBUG_ASCONF1, "set_primary_ip_address: queued on stcb=%p, ",
stcb); stcb);
SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, &ifa->address.sa); 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 stcb */
} /* for each inp */ } /* for each inp */
@ -2026,6 +2121,10 @@ sctp_compose_asconf(struct sctp_tcb *stcb, int *retlen)
if (TAILQ_EMPTY(&stcb->asoc.asconf_queue)) { if (TAILQ_EMPTY(&stcb->asoc.asconf_queue)) {
return (NULL); 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 * 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 * 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, status = sctp_asconf_queue_add_sa(stcb, sa,
SCTP_DEL_IP_ADDRESS); SCTP_DEL_IP_ADDRESS);
/* /*
* if queued ok, and in correct state, set * if queued ok, and in correct state, send
* the ASCONF timer * out the ASCONF.
*/ */
if (status == 0 && if (status == 0 &&
SCTP_GET_STATE(&stcb->asoc) == SCTP_GET_STATE(&stcb->asoc) ==
SCTP_STATE_OPEN) { SCTP_STATE_OPEN) {
#ifdef SCTP_TIMER_BASED_ASCONF
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
stcb->sctp_ep, stcb, stcb->sctp_ep, stcb,
stcb->asoc.primary_destination); 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; wi->action = type;
atomic_add_int(&ifa->refcount, 1); atomic_add_int(&ifa->refcount, 1);
LIST_INSERT_HEAD(&asc->list_of_work, wi, sctp_nxt_addr); LIST_INSERT_HEAD(&asc->list_of_work, wi, sctp_nxt_addr);
(void)sctp_initiate_iterator(sctp_iterator_ep, (void)sctp_initiate_iterator(sctp_asconf_iterator_ep,
sctp_iterator_stcb, sctp_asconf_iterator_stcb,
sctp_iterator_ep_end, sctp_asconf_iterator_ep_end,
SCTP_PCB_ANY_FLAGS, SCTP_PCB_ANY_FLAGS,
SCTP_PCB_ANY_FEATURES, SCTP_PCB_ANY_FEATURES,
SCTP_ASOC_ANY_STATE, (void *)asc, 0, SCTP_ASOC_ANY_STATE,
sctp_iterator_end, inp, 0); (void *)asc, 0,
sctp_asconf_iterator_end, inp, 0);
} else { } else {
/* invalid address! */ /* invalid address! */
return (EADDRNOTAVAIL); 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 *); uint32_t, uint32_t, struct sctp_ifa *);
int sctp_iterator_ep(struct sctp_inpcb *inp, void *ptr, uint32_t val); extern int
void sctp_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, uint32_t type); sctp_asconf_iterator_ep(struct sctp_inpcb *inp, void *ptr,
int sctp_iterator_ep_end(struct sctp_inpcb *inp, void *ptr, uint32_t val); uint32_t val);
void sctp_iterator_end(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 extern int32_t

View File

@ -2345,15 +2345,20 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp,
stcb->sctp_ep, stcb, NULL); stcb->sctp_ep, stcb, NULL);
} }
/* /*
* set ASCONF timer if ASCONFs are pending and allowed (eg. * send ASCONF if parameters are pending and ASCONFs are
* addresses changed when init/cookie echo in flight) * allowed (eg. addresses changed when init/cookie echo were
* in flight)
*/ */
if ((sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_DO_ASCONF)) && if ((sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_DO_ASCONF)) &&
(stcb->asoc.peer_supports_asconf) && (stcb->asoc.peer_supports_asconf) &&
(!TAILQ_EMPTY(&stcb->asoc.asconf_queue))) { (!TAILQ_EMPTY(&stcb->asoc.asconf_queue))) {
#ifdef SCTP_TIMER_BASED_ASCONF
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
stcb->sctp_ep, stcb, stcb->sctp_ep, stcb,
stcb->asoc.primary_destination); stcb->asoc.primary_destination);
#else
sctp_send_asconf(stcb, stcb->asoc.primary_destination);
#endif
} }
} }
/* Toss the cookie if I can */ /* Toss the cookie if I can */

View File

@ -2380,9 +2380,12 @@ sctp_choose_boundspecific_inp(struct sctp_inpcb *inp,
if (sctp_ifn) { if (sctp_ifn) {
/* is a preferred one on the interface we route out? */ /* is a preferred one on the interface we route out? */
LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { 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; 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) if (sifa == NULL)
continue; continue;
if (sctp_is_addr_in_ep(inp, sifa)) { 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); inp->next_addr_touse = LIST_FIRST(&inp->sctp_addr_list);
resettotop = 1; 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) { if (laddr->ifa == NULL) {
/* address has been removed */ /* address has been removed */
continue; 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) if (sifa == NULL)
continue; continue;
atomic_add_int(&sifa->refcount, 1); atomic_add_int(&sifa->refcount, 1);
@ -2426,12 +2431,14 @@ sctp_choose_boundspecific_inp(struct sctp_inpcb *inp,
resettotop = 1; resettotop = 1;
} }
/* ok, what about an acceptable address in the inp */ /* 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) { if (laddr->ifa == NULL) {
/* address has been removed */ /* address has been removed */
continue; 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) if (sifa == NULL)
continue; continue;
atomic_add_int(&sifa->refcount, 1); atomic_add_int(&sifa->refcount, 1);
@ -2836,7 +2843,7 @@ sctp_choose_boundall(struct sctp_inpcb *inp,
plan_d: plan_d:
/* /*
* plan_d: We are in trouble. No preferred address on the emit * 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 * out and see if we can find an acceptable address somewhere
* amongst all interfaces. * 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 * 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 * restricted list and non_asoc_address is NOT ok, or it is on our
* negative list. We cant source to it :-( * restricted list. We can't source to it :-(
*/ */
return (NULL); return (NULL);
} }
@ -2939,7 +2946,7 @@ sctp_source_address_selection(struct sctp_inpcb *inp,
* *
* Decisions: * 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. * 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 * - 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 * 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); SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, (struct sockaddr *)to);
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
/* /*
* When bound to all if the address list is set it is a * Bound all case
* negative list. Addresses being added by asconf.
*/ */
answer = sctp_choose_boundall(inp, stcb, net, ro, vrf_id, answer = sctp_choose_boundall(inp, stcb, net, ro, vrf_id,
dest_is_priv, dest_is_loop, dest_is_priv, dest_is_loop,
@ -3003,21 +3009,7 @@ sctp_source_address_selection(struct sctp_inpcb *inp,
return (answer); return (answer);
} }
/* /*
* Three possiblities here: * Subset bound case
*
* a) stcb is NULL, which means we operate only from the list of
* addresses (ifa's) bound to the endpoint and we care not about the
* list. b) stcb is NOT-NULL, which means we have an assoc structure
* and auto-asconf is on. This means that the list of addresses is a
* NOT list. We use the list from the inp, but any listed address in
* our list is NOT yet added. However if the non_asoc_addr_ok is set
* we CAN use an address NOT available (i.e. being added). Its a
* negative list. c) stcb is NOT-NULL, which means we have an assoc
* structure and auto-asconf is off. This means that the list of
* addresses is the ONLY addresses I can use.. its positive.
*
* Note we collapse b & c into the same function just like in the v6
* address selection.
*/ */
if (stcb) { if (stcb) {
answer = sctp_choose_boundspecific_stcb(inp, stcb, net, ro, 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; net->src_addr_selected = 0;
} }
if (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 */ /* Cache the source address */
net->ro._s_addr = sctp_source_address_selection(inp, stcb, net->ro._s_addr = sctp_source_address_selection(inp, stcb,
ro, net, out_of_asoc_ok, ro, net, out_of_asoc_ok,
@ -3373,8 +3369,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
} else { } else {
struct sctp_ifa *_lsrc; struct sctp_ifa *_lsrc;
_lsrc = sctp_source_address_selection(inp, temp_v4_src:
stcb, ro, net, out_of_asoc_ok, vrf_id); _lsrc = sctp_source_address_selection(inp, stcb, ro,
net,
out_of_asoc_ok,
vrf_id);
if (_lsrc == NULL) { if (_lsrc == NULL) {
goto no_route; goto no_route;
} }
@ -3621,6 +3620,10 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
net->src_addr_selected = 0; net->src_addr_selected = 0;
} }
if (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 */ /* Cache the source address */
net->ro._s_addr = sctp_source_address_selection(inp, net->ro._s_addr = sctp_source_address_selection(inp,
stcb, stcb,
@ -3639,7 +3642,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
} else { } else {
struct sctp_ifa *_lsrc; 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) { if (_lsrc == NULL) {
goto no_route; goto no_route;
} }
@ -7145,6 +7152,12 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
*/ */
hbflag = 1; hbflag = 1;
asconf = 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->sent = SCTP_DATAGRAM_SENT;
chk->snd_count++; chk->snd_count++;
@ -7158,7 +7171,12 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
*/ */
if (asconf) { if (asconf) {
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, net); 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) { if (cookie) {
sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net); sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net);
@ -7406,8 +7424,13 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
if (outchain) { if (outchain) {
/* We may need to start a control timer or two */ /* We may need to start a control timer or two */
if (asconf) { if (asconf) {
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, net); sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp,
asconf = 0; stcb, net);
/*
* do NOT clear the asconf flag as it is
* used to do appropriate source address
* selection.
*/
} }
if (cookie) { if (cookie) {
sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net); 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) sctp_send_asconf(struct sctp_tcb *stcb, struct sctp_nets *net)
{ {
/* /*
* formulate and queue an ASCONF to the peer ASCONF parameters * formulate and queue an ASCONF to the peer. ASCONF parameters
* should be queued on the assoc queue * should be queued on the assoc queue.
*/ */
struct sctp_tmit_chunk *chk; struct sctp_tmit_chunk *chk;
struct mbuf *m_asconf; struct mbuf *m_asconf;
struct sctp_asconf_chunk *acp; struct sctp_asconf_chunk *acp;
int len; int len;
SCTP_TCB_LOCK_ASSERT(stcb); SCTP_TCB_LOCK_ASSERT(stcb);
/* compose an ASCONF chunk, maximum length is PMTU */ /* compose an ASCONF chunk, maximum length is PMTU */
m_asconf = sctp_compose_asconf(stcb, &len); m_asconf = sctp_compose_asconf(stcb, &len);
@ -7937,8 +7959,8 @@ void
sctp_send_asconf_ack(struct sctp_tcb *stcb, uint32_t retrans) sctp_send_asconf_ack(struct sctp_tcb *stcb, uint32_t retrans)
{ {
/* /*
* formulate and queue a asconf-ack back to sender the asconf-ack * formulate and queue a asconf-ack back to sender. the asconf-ack
* must be stored in the tcb * must be stored in the tcb.
*/ */
struct sctp_tmit_chunk *chk; struct sctp_tmit_chunk *chk;
struct mbuf *m_ack, *m; struct mbuf *m_ack, *m;
@ -7949,10 +7971,10 @@ sctp_send_asconf_ack(struct sctp_tcb *stcb, uint32_t retrans)
return; return;
} }
/* copy the asconf_ack */ /* 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) { if (m_ack == NULL) {
/* couldn't copy it */ /* couldn't copy it */
return; return;
} }
sctp_alloc_a_chunk(stcb, chk); sctp_alloc_a_chunk(stcb, chk);
@ -11335,11 +11357,6 @@ sctp_lower_sosend(struct socket *so,
atomic_add_int(&stcb->total_sends, 1); atomic_add_int(&stcb->total_sends, 1);
if (top == NULL) { if (top == NULL) {
struct sctp_stream_queue_pending *sp; struct sctp_stream_queue_pending *sp;
#ifdef INVARIANTS
struct sctp_stream_queue_pending *msp;
#endif
struct sctp_stream_out *strm; struct sctp_stream_out *strm;
uint32_t sndout, initial_out; uint32_t sndout, initial_out;
int user_marks_eor; int user_marks_eor;
@ -11373,11 +11390,6 @@ sctp_lower_sosend(struct socket *so,
goto out; goto out;
} }
SCTP_TCB_SEND_LOCK(stcb); 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) { if (sp->msg_is_complete) {
strm->last_msg_incomplete = 0; strm->last_msg_incomplete = 0;
asoc->stream_locked = 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 int
sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, 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 */ /* bind a ep to a socket address */
struct sctppcbhead *head; 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 * zero out the port to find the address! yuck! can't do
* this earlier since need port for sctp_pcb_findep() * this earlier since need port for sctp_pcb_findep()
*/ */
ifa = sctp_find_ifa_by_addr((struct sockaddr *)&store_sa, if (sctp_ifap != NULL)
vrf_id, 0); 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) { if (ifa == NULL) {
/* Can't find an interface with that address */ /* Can't find an interface with that address */
SCTP_INP_WUNLOCK(inp); 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, if ((err = sctp_inpcb_bind(inp->sctp_socket,
(struct sockaddr *)NULL, (struct sockaddr *)NULL,
(struct sctp_ifa *)NULL,
p p
))) { ))) {
/* bind error, probably perm */ /* 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 * Add the address to the TCB local address restricted list.
* case, this is a "pending" address list (eg. addresses waiting for an * This is a "pending" address list (eg. addresses waiting for an
* ASCONF-ACK response) For the subset binding, static case, this is a * ASCONF-ACK response) and cannot be used as a valid source address.
* "valid" address list
*/ */
void 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_inpcb *inp;
struct sctp_laddr *laddr; 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 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_inpcb *inp;
struct sctp_laddr *laddr; struct sctp_laddr *laddr;

View File

@ -468,7 +468,7 @@ struct sctp_inpcb *sctp_pcb_findep(struct sockaddr *, int, int, uint32_t);
int int
sctp_inpcb_bind(struct socket *, struct sockaddr *, sctp_inpcb_bind(struct socket *, struct sockaddr *,
struct thread *); struct sctp_ifa *, struct thread *);
struct sctp_tcb * struct sctp_tcb *
sctp_findassociation_addr(struct mbuf *, int, int, 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_pcb_init(void);
void sctp_add_local_addr_assoc(struct sctp_tcb *, struct sctp_ifa *, int); void sctp_add_local_addr_restricted(struct sctp_tcb *, struct sctp_ifa *);
void sctp_del_local_addr_restricted(struct sctp_tcb *, struct sctp_ifa *);
void sctp_del_local_addr_assoc(struct sctp_tcb *, struct sctp_ifa *);
int int
sctp_load_addresses_from_init(struct sctp_tcb *, struct mbuf *, int, 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 { struct sctp_association {
/* association state */ /* association state */
int state; int state;
/* queue of pending addrs to add/delete */ /* queue of pending addrs to add/delete */
struct sctp_asconf_addrhead asconf_queue; struct sctp_asconf_addrhead asconf_queue;
struct timeval time_entered; /* time we entered state */ struct timeval time_entered; /* time we entered state */
struct timeval time_last_rcvd; struct timeval time_last_rcvd;
struct timeval time_last_sent; struct timeval time_last_sent;
@ -567,21 +569,23 @@ struct sctp_association {
/* timers and such */ /* timers and such */
struct sctp_timer hb_timer; /* hb timer */ struct sctp_timer hb_timer; /* hb timer */
struct sctp_timer dack_timer; /* Delayed ack 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 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 autoclose_timer; /* automatic close timer */
struct sctp_timer delayed_event_timer; /* timer for delayed events */ 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 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 */ /* Free chunk list */
struct sctpchunk_listhead free_chunks; struct sctpchunk_listhead free_chunks;
/* Control chunk queue */ /* Control chunk queue */
struct sctpchunk_listhead control_send_queue; struct sctpchunk_listhead control_send_queue;
@ -595,7 +599,6 @@ struct sctp_association {
struct sctpchunk_listhead sent_queue; struct sctpchunk_listhead sent_queue;
struct sctpchunk_listhead send_queue; struct sctpchunk_listhead send_queue;
/* re-assembly queue for fragmented chunks on the inbound path */ /* re-assembly queue for fragmented chunks on the inbound path */
struct sctpchunk_listhead reasmqueue; struct sctpchunk_listhead reasmqueue;
@ -618,12 +621,6 @@ struct sctp_association {
/* If an iterator is looking at me, this is it */ /* If an iterator is looking at me, this is it */
struct sctp_iterator *stcb_starting_point_for_iterator; 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 */ /* ASCONF save the last ASCONF-ACK so we can resend it if necessary */
struct mbuf *last_asconf_ack_sent; 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 * lock flag: 0 is ok to send, 1+ (duals as a retran count) is
* awaiting ACK * awaiting ACK
*/ */
uint16_t asconf_sent; /* possibly removable REM */ uint16_t asconf_sent;
uint16_t mapping_array_size; uint16_t mapping_array_size;
uint16_t last_strm_seq_delivered; uint16_t last_strm_seq_delivered;
@ -944,6 +942,7 @@ struct sctp_association {
uint8_t hb_random_idx; uint8_t hb_random_idx;
uint8_t hb_is_disabled; /* is the hb disabled? */ uint8_t hb_is_disabled; /* is the hb disabled? */
uint8_t default_tos; uint8_t default_tos;
uint8_t asconf_del_pending; /* asconf delete last addr pending */
/* ECN Nonce stuff */ /* ECN Nonce stuff */
uint8_t receiver_nonce_sum; /* nonce I sum and put in my sack */ 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_nets *alt;
struct sctp_tmit_chunk *asconf, *chk; 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) { if (stcb->asoc.asconf_sent == 0) {
/* compose a new ASCONF chunk and send it */ /* compose a new ASCONF chunk and send it */
sctp_send_asconf(stcb, net); sctp_send_asconf(stcb, net);
} else { } else {
/* Retransmission of the existing ASCONF needed... */ /*
* Retransmission of the existing ASCONF is needed
*/
/* find the existing ASCONF */ /* find the existing ASCONF */
TAILQ_FOREACH(asconf, &stcb->asoc.control_send_queue, 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 */ /* Assoc is over */
return (1); 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) { if (asconf->snd_count > stcb->asoc.max_send_times) {
/* /*
* Something is rotten, peer is not responding to * Something is rotten: our peer is not responding
* ASCONFs but maybe is to data etc. e.g. it is not * to ASCONFs but apparently is to other chunks.
* properly handling the chunk type upper bits Mark * i.e. it is not properly handling the chunk type
* this peer as ASCONF incapable and cleanup * 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"); SCTPDBG(SCTP_DEBUG_TIMER1, "asconf_timer: Peer has not responded to our repeated ASCONFs\n");
sctp_asconf_cleanup(stcb, net); sctp_asconf_cleanup(stcb, net);
return (0); return (0);
} }
/* /*
* cleared theshold management now lets backoff the address * cleared threshold management, so now backoff the net and
* & select an alternate * select an alternate
*/ */
sctp_backoff_on_timeout(stcb, asconf->whoTo, 1, 0); sctp_backoff_on_timeout(stcb, asconf->whoTo, 1, 0);
alt = sctp_find_alternate_net(stcb, asconf->whoTo, 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; asconf->whoTo = alt;
atomic_add_int(&alt->ref_count, 1); 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) { TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) {
if ((chk->whoTo == net) && if ((chk->whoTo == net) &&
(chk->rec.chunk_id.id == SCTP_ECN_ECHO)) { (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 (net->dest_state & SCTP_ADDR_NOT_REACHABLE) {
/* /*
* If the address went un-reachable, we need to move * 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); 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) if (inp == 0)
return EINVAL; return EINVAL;
error = sctp_inpcb_bind(so, addr, p); error = sctp_inpcb_bind(so, addr, NULL, p);
return error; return error;
} }
@ -1170,7 +1170,6 @@ sctp_fill_up_addresses_vrf(struct sctp_inpcb *inp,
} else { } else {
struct sctp_laddr *laddr; struct sctp_laddr *laddr;
/* The list is a NEGATIVE list */
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
if (stcb) { if (stcb) {
if (sctp_is_addr_restricted(stcb, laddr->ifa)) { 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) == if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) ==
SCTP_PCB_FLAGS_UNBOUND) { SCTP_PCB_FLAGS_UNBOUND) {
/* Bind a ephemeral port */ /* Bind a ephemeral port */
error = sctp_inpcb_bind(so, NULL, p); error = sctp_inpcb_bind(so, NULL, NULL, p);
if (error) { if (error) {
goto out_now; 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_peer_rwnd = 0;
sasoc->sasoc_local_rwnd = 0; sasoc->sasoc_local_rwnd = 0;
if (sasoc->sasoc_cookie_life) { 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); 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) == if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) ==
SCTP_PCB_FLAGS_UNBOUND) { SCTP_PCB_FLAGS_UNBOUND) {
/* Bind a ephemeral port */ /* Bind a ephemeral port */
error = sctp_inpcb_bind(so, NULL, p); error = sctp_inpcb_bind(so, NULL, NULL, p);
if (error) { if (error) {
goto out_now; goto out_now;
} }
@ -3916,7 +3915,7 @@ sctp_listen(struct socket *so, int backlog, struct thread *p)
/* We must do a bind. */ /* We must do a bind. */
SOCK_UNLOCK(so); SOCK_UNLOCK(so);
SCTP_INP_RUNLOCK(inp); 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 */ /* bind error, probably perm */
return (error); return (error);
} }

View File

@ -1373,15 +1373,15 @@ sctp_handle_addr_wq(void)
if (asc->cnt == 0) { if (asc->cnt == 0) {
SCTP_FREE(asc, SCTP_M_ASC_IT); SCTP_FREE(asc, SCTP_M_ASC_IT);
} else { } else {
(void)sctp_initiate_iterator(sctp_iterator_ep, (void)sctp_initiate_iterator(sctp_asconf_iterator_ep,
sctp_iterator_stcb, sctp_asconf_iterator_stcb,
NULL, /* No ep end for boundall */ NULL, /* No ep end for boundall */
SCTP_PCB_FLAGS_BOUNDALL, SCTP_PCB_FLAGS_BOUNDALL,
SCTP_PCB_ANY_FEATURES, SCTP_PCB_ANY_FEATURES,
SCTP_ASOC_ANY_STATE, (void *)asc, 0, SCTP_ASOC_ANY_STATE,
sctp_iterator_end, NULL, 0); (void *)asc, 0,
sctp_asconf_iterator_end, NULL, 0);
} }
} }
int retcode = 0; int retcode = 0;
@ -2078,8 +2078,8 @@ sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
break; break;
case SCTP_TIMER_TYPE_STRRESET: case SCTP_TIMER_TYPE_STRRESET:
/* /*
* Here the timer comes from the inp but its value is from * Here the timer comes from the stcb but its value is from
* the RTO. * the net's RTO.
*/ */
if ((stcb == NULL) || (net == NULL)) { if ((stcb == NULL) || (net == NULL)) {
return; return;
@ -2122,8 +2122,8 @@ sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
break; break;
case SCTP_TIMER_TYPE_ASCONF: case SCTP_TIMER_TYPE_ASCONF:
/* /*
* Here the timer comes from the inp but its value is from * Here the timer comes from the stcb but its value is from
* the RTO. * the net's RTO.
*/ */
if ((stcb == NULL) || (net == NULL)) { if ((stcb == NULL) || (net == NULL)) {
return; return;
@ -5932,7 +5932,7 @@ sctp_bindx_add_address(struct socket *so, struct sctp_inpcb *inp,
*error = EINVAL; *error = EINVAL;
return; return;
} }
*error = sctp_inpcb_bind(so, addr_touse, p); *error = sctp_inpcb_bind(so, addr_touse, NULL, p);
return; 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, struct sockaddr *sa, sctp_assoc_t assoc_id,
uint32_t vrf_id, int *error); uint32_t vrf_id, int *error);
int sctp_local_addr_count(struct sctp_tcb *stcb);
#ifdef SCTP_MBCNT_LOGGING #ifdef SCTP_MBCNT_LOGGING
void void
sctp_free_bufspace(struct sctp_tcb *, struct sctp_association *, 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); in6_sin6_2_sin(&sin, sin6_p);
inp6->inp_vflag |= INP_IPV4; inp6->inp_vflag |= INP_IPV4;
inp6->inp_vflag &= ~INP_IPV6; 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; return error;
} }
} }
@ -634,7 +634,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
return EINVAL; return EINVAL;
} }
} }
error = sctp_inpcb_bind(so, addr, p); error = sctp_inpcb_bind(so, addr, NULL, p);
return error; return error;
} }