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