diff options
author | Michael Tuexen <tuexen@FreeBSD.org> | 2011-08-03 20:21:00 +0000 |
---|---|---|
committer | Michael Tuexen <tuexen@FreeBSD.org> | 2011-08-03 20:21:00 +0000 |
commit | ca85e9482aa4d92ab9da1852b69cc56ae1bec21c (patch) | |
tree | 6202f1b3524e68a30395d3b510eebc5a90fa3068 /sys/netinet | |
parent | f36e5acfc1caf0df5adf0126cb8a6bc89ea2ee7e (diff) | |
download | src-ca85e9482aa4d92ab9da1852b69cc56ae1bec21c.tar.gz src-ca85e9482aa4d92ab9da1852b69cc56ae1bec21c.zip |
The result of a joint work between rrs@ and myself at the IETF:
* Decouple the path supervision using a separate HB timer per path.
* Add support for potentially failed state.
* Bring back RTO.min to 1 second.
* Accept packets on IP-addresses already announced via an ASCONF
* While there: do some cleanups.
Approved by: re@
MFC after: 2 months.
Notes
Notes:
svn path=/head/; revision=224641
Diffstat (limited to 'sys/netinet')
-rw-r--r-- | sys/netinet/sctp.h | 22 | ||||
-rw-r--r-- | sys/netinet/sctp_asconf.c | 45 | ||||
-rw-r--r-- | sys/netinet/sctp_cc_functions.c | 361 | ||||
-rw-r--r-- | sys/netinet/sctp_constants.h | 30 | ||||
-rw-r--r-- | sys/netinet/sctp_header.h | 3 | ||||
-rw-r--r-- | sys/netinet/sctp_indata.c | 175 | ||||
-rw-r--r-- | sys/netinet/sctp_input.c | 98 | ||||
-rw-r--r-- | sys/netinet/sctp_output.c | 458 | ||||
-rw-r--r-- | sys/netinet/sctp_output.h | 2 | ||||
-rw-r--r-- | sys/netinet/sctp_pcb.c | 82 | ||||
-rw-r--r-- | sys/netinet/sctp_pcb.h | 4 | ||||
-rw-r--r-- | sys/netinet/sctp_structs.h | 21 | ||||
-rw-r--r-- | sys/netinet/sctp_sysctl.c | 37 | ||||
-rw-r--r-- | sys/netinet/sctp_sysctl.h | 28 | ||||
-rw-r--r-- | sys/netinet/sctp_timer.c | 386 | ||||
-rw-r--r-- | sys/netinet/sctp_timer.h | 6 | ||||
-rw-r--r-- | sys/netinet/sctp_uio.h | 28 | ||||
-rw-r--r-- | sys/netinet/sctp_usrreq.c | 510 | ||||
-rw-r--r-- | sys/netinet/sctp_var.h | 5 | ||||
-rw-r--r-- | sys/netinet/sctputil.c | 182 |
20 files changed, 1079 insertions, 1404 deletions
diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index 3c8cf364ca6c..11aa02a0a568 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -119,6 +119,7 @@ struct sctp_paramhdr { #define SCTP_RECVNXTINFO 0x00000020 #define SCTP_DEFAULT_SNDINFO 0x00000021 #define SCTP_DEFAULT_PRINFO 0x00000022 +#define SCTP_PEER_ADDR_THLDS 0x00000023 /* * read-only options @@ -564,7 +565,6 @@ struct sctp_error_unrecognized_chunk { #define SCTP_BLK_LOGGING_ENABLE 0x00000001 #define SCTP_CWND_MONITOR_ENABLE 0x00000002 #define SCTP_CWND_LOGGING_ENABLE 0x00000004 -#define SCTP_EARLYFR_LOGGING_ENABLE 0x00000010 #define SCTP_FLIGHT_LOGGING_ENABLE 0x00000020 #define SCTP_FR_LOGGING_ENABLE 0x00000040 #define SCTP_LOCK_LOGGING_ENABLE 0x00000080 @@ -572,23 +572,23 @@ struct sctp_error_unrecognized_chunk { #define SCTP_MBCNT_LOGGING_ENABLE 0x00000200 #define SCTP_MBUF_LOGGING_ENABLE 0x00000400 #define SCTP_NAGLE_LOGGING_ENABLE 0x00000800 -#define SCTP_RECV_RWND_LOGGING_ENABLE 0x00001000 +#define SCTP_RECV_RWND_LOGGING_ENABLE 0x00001000 #define SCTP_RTTVAR_LOGGING_ENABLE 0x00002000 #define SCTP_SACK_LOGGING_ENABLE 0x00004000 -#define SCTP_SACK_RWND_LOGGING_ENABLE 0x00008000 +#define SCTP_SACK_RWND_LOGGING_ENABLE 0x00008000 #define SCTP_SB_LOGGING_ENABLE 0x00010000 #define SCTP_STR_LOGGING_ENABLE 0x00020000 #define SCTP_WAKE_LOGGING_ENABLE 0x00040000 #define SCTP_LOG_MAXBURST_ENABLE 0x00080000 #define SCTP_LOG_RWND_ENABLE 0x00100000 -#define SCTP_LOG_SACK_ARRIVALS_ENABLE 0x00200000 -#define SCTP_LTRACE_CHUNK_ENABLE 0x00400000 -#define SCTP_LTRACE_ERROR_ENABLE 0x00800000 -#define SCTP_LAST_PACKET_TRACING 0x01000000 -#define SCTP_THRESHOLD_LOGGING 0x02000000 -#define SCTP_LOG_AT_SEND_2_SCTP 0x04000000 -#define SCTP_LOG_AT_SEND_2_OUTQ 0x08000000 -#define SCTP_LOG_TRY_ADVANCE 0x10000000 +#define SCTP_LOG_SACK_ARRIVALS_ENABLE 0x00200000 +#define SCTP_LTRACE_CHUNK_ENABLE 0x00400000 +#define SCTP_LTRACE_ERROR_ENABLE 0x00800000 +#define SCTP_LAST_PACKET_TRACING 0x01000000 +#define SCTP_THRESHOLD_LOGGING 0x02000000 +#define SCTP_LOG_AT_SEND_2_SCTP 0x04000000 +#define SCTP_LOG_AT_SEND_2_OUTQ 0x08000000 +#define SCTP_LOG_TRY_ADVANCE 0x10000000 #undef SCTP_PACKED diff --git a/sys/netinet/sctp_asconf.c b/sys/netinet/sctp_asconf.c index a6db580f6abe..69f9cf195670 100644 --- a/sys/netinet/sctp_asconf.c +++ b/sys/netinet/sctp_asconf.c @@ -198,8 +198,9 @@ sctp_asconf_error_response(uint32_t id, uint16_t cause, uint8_t * error_tlv, static struct mbuf * sctp_process_asconf_add_ip(struct mbuf *m, struct sctp_asconf_paramhdr *aph, - struct sctp_tcb *stcb, int response_required) + struct sctp_tcb *stcb, int send_hb, int response_required) { + struct sctp_nets *net; struct mbuf *m_reply = NULL; struct sockaddr_storage sa_source, sa_store; struct sctp_paramhdr *ph; @@ -284,7 +285,7 @@ sctp_process_asconf_add_ip(struct mbuf *m, struct sctp_asconf_paramhdr *aph, SCTPDBG_ADDR(SCTP_DEBUG_ASCONF1, sa); } /* add the address */ - if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, + if (sctp_add_remote_addr(stcb, sa, &net, SCTP_DONOT_SETSCOPE, SCTP_ADDR_DYNAMIC_ADDED) != 0) { SCTPDBG(SCTP_DEBUG_ASCONF1, "process_asconf_add_ip: error adding address\n"); @@ -298,10 +299,12 @@ sctp_process_asconf_add_ip(struct mbuf *m, struct sctp_asconf_paramhdr *aph, m_reply = sctp_asconf_success_response(aph->correlation_id); } - sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, - NULL, SCTP_FROM_SCTP_ASCONF + SCTP_LOC_1); + sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, net); sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, - stcb, NULL); + stcb, net); + if (send_hb) { + sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); + } } return m_reply; } @@ -554,7 +557,12 @@ sctp_process_asconf_set_primary(struct mbuf *m, "process_asconf_set_primary: primary address set\n"); /* notify upper layer */ sctp_ulp_notify(SCTP_NOTIFY_ASCONF_SET_PRIMARY, stcb, 0, sa, SCTP_SO_NOT_LOCKED); - + if ((stcb->asoc.primary_destination->dest_state & SCTP_ADDR_REACHABLE) && + (!(stcb->asoc.primary_destination->dest_state & SCTP_ADDR_PF)) && + (stcb->asoc.alternate)) { + sctp_free_remote_addr(stcb->asoc.alternate); + stcb->asoc.alternate = NULL; + } if (response_required) { m_reply = sctp_asconf_success_response(aph->correlation_id); } @@ -622,7 +630,7 @@ sctp_handle_asconf(struct mbuf *m, unsigned int offset, struct sctp_asconf_ack_chunk *ack_cp; struct sctp_asconf_paramhdr *aph, *ack_aph; struct sctp_ipv6addr_param *p_addr; - unsigned int asconf_limit; + unsigned int asconf_limit, cnt; int error = 0; /* did an error occur? */ /* asconf param buffer */ @@ -717,6 +725,7 @@ sctp_handle_asconf(struct mbuf *m, unsigned int offset, goto send_reply; } /* process through all parameters */ + cnt = 0; while (aph != NULL) { unsigned int param_length, param_type; @@ -749,7 +758,8 @@ sctp_handle_asconf(struct mbuf *m, unsigned int offset, case SCTP_ADD_IP_ADDRESS: asoc->peer_supports_asconf = 1; m_result = sctp_process_asconf_add_ip(m, aph, stcb, - error); + (cnt < SCTP_BASE_SYSCTL(sctp_hb_maxburst)), error); + cnt++; break; case SCTP_DEL_IP_ADDRESS: asoc->peer_supports_asconf = 1; @@ -1959,7 +1969,7 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int status; - if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0 && + if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0 || sctp_is_feature_off(inp, SCTP_PCB_FLAGS_DO_ASCONF)) { /* subset bound, no ASCONF allowed case, so ignore */ return; @@ -2075,8 +2085,7 @@ sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, stcb->asoc.primary_destination); #else - sctp_send_asconf(stcb, stcb->asoc.primary_destination, - addr_locked); + sctp_send_asconf(stcb, NULL, addr_locked); #endif } } @@ -2328,8 +2337,7 @@ sctp_asconf_iterator_stcb(struct sctp_inpcb *inp, struct sctp_tcb *stcb, * 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, - SCTP_ADDR_NOT_LOCKED); + sctp_send_asconf(stcb, NULL, SCTP_ADDR_NOT_LOCKED); } } @@ -2384,8 +2392,7 @@ sctp_set_primary_ip_address_sa(struct sctp_tcb *stcb, struct sockaddr *sa) stcb->sctp_ep, stcb, stcb->asoc.primary_destination); #else - sctp_send_asconf(stcb, stcb->asoc.primary_destination, - SCTP_ADDR_NOT_LOCKED); + sctp_send_asconf(stcb, NULL, SCTP_ADDR_NOT_LOCKED); #endif } } else { @@ -2421,8 +2428,7 @@ sctp_set_primary_ip_address(struct sctp_ifa *ifa) stcb->sctp_ep, stcb, stcb->asoc.primary_destination); #else - sctp_send_asconf(stcb, stcb->asoc.primary_destination, - SCTP_ADDR_NOT_LOCKED); + sctp_send_asconf(stcb, NULL, SCTP_ADDR_NOT_LOCKED); #endif } } @@ -2965,8 +2971,7 @@ sctp_process_initack_addresses(struct sctp_tcb *stcb, struct mbuf *m, stcb->sctp_ep, stcb, stcb->asoc.primary_destination); #else - sctp_send_asconf(stcb, stcb->asoc.primary_destination, - SCTP_ADDR_NOT_LOCKED); + sctp_send_asconf(stcb, NULL, SCTP_ADDR_NOT_LOCKED); #endif } } @@ -3540,5 +3545,5 @@ sctp_asconf_send_nat_state_update(struct sctp_tcb *stcb, } skip_rest: /* Now we must send the asconf into the queue */ - sctp_send_asconf(stcb, net, 0); + sctp_send_asconf(stcb, net, SCTP_ADDR_NOT_LOCKED); } diff --git a/sys/netinet/sctp_cc_functions.c b/sys/netinet/sctp_cc_functions.c index 85beb6adaaed..b1596cccdbb0 100644 --- a/sys/netinet/sctp_cc_functions.c +++ b/sys/netinet/sctp_cc_functions.c @@ -728,40 +728,6 @@ sctp_cwnd_update_after_sack_common(struct sctp_tcb *stcb, } } #endif - if (SCTP_BASE_SYSCTL(sctp_early_fr)) { - /* - * So, first of all do we need to have a Early FR - * timer running? - */ - if ((!TAILQ_EMPTY(&asoc->sent_queue) && - (net->ref_count > 1) && - (net->flight_size < net->cwnd)) || - (reneged_all)) { - /* - * yes, so in this case stop it if its - * running, and then restart it. Reneging - * all is a special case where we want to - * run the Early FR timer and then force the - * last few unacked to be sent, causing us - * to illicit a sack with gaps to force out - * the others. - */ - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - SCTP_STAT_INCR(sctps_earlyfrstpidsck2); - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net, - SCTP_FROM_SCTP_INDATA + SCTP_LOC_20); - } - SCTP_STAT_INCR(sctps_earlyfrstrid); - sctp_timer_start(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net); - } else { - /* No, stop it if its running */ - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - SCTP_STAT_INCR(sctps_earlyfrstpidsck3); - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net, - SCTP_FROM_SCTP_INDATA + SCTP_LOC_21); - } - } - } /* if nothing was acked on this destination skip it */ if (net->net_ack == 0) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) { @@ -769,51 +735,6 @@ sctp_cwnd_update_after_sack_common(struct sctp_tcb *stcb, } continue; } - if (net->net_ack2 > 0) { - /* - * Karn's rule applies to clearing error count, this - * is optional. - */ - net->error_count = 0; - if ((net->dest_state & SCTP_ADDR_NOT_REACHABLE) == - SCTP_ADDR_NOT_REACHABLE) { - /* addr came good */ - net->dest_state &= ~SCTP_ADDR_NOT_REACHABLE; - net->dest_state |= SCTP_ADDR_REACHABLE; - sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, - SCTP_RECEIVED_SACK, (void *)net, SCTP_SO_NOT_LOCKED); - /* now was it the primary? if so restore */ - if (net->dest_state & SCTP_ADDR_WAS_PRIMARY) { - (void)sctp_set_primary_addr(stcb, (struct sockaddr *)NULL, net); - } - } - /* - * JRS 5/14/07 - If CMT PF is on and the destination - * is in PF state, set the destination to active - * state and set the cwnd to one or two MTU's based - * on whether PF1 or PF2 is being used. - * - * Should we stop any running T3 timer here? - */ - if ((asoc->sctp_cmt_on_off > 0) && - (asoc->sctp_cmt_pf > 0) && - ((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF)) { - net->dest_state &= ~SCTP_ADDR_PF; - old_cwnd = net->cwnd; - net->cwnd = net->mtu * asoc->sctp_cmt_pf; - SDT_PROBE(sctp, cwnd, net, ack, - stcb->asoc.my_vtag, ((stcb->sctp_ep->sctp_lport << 16) | (stcb->rport)), net, - old_cwnd, net->cwnd); - SCTPDBG(SCTP_DEBUG_INDATA1, "Destination %p moved from PF to reachable with cwnd %d.\n", - net, net->cwnd); - /* - * Since the cwnd value is explicitly set, - * skip the code that updates the cwnd - * value. - */ - goto skip_cwnd_update; - } - } #ifdef JANA_CMT_FAST_RECOVERY /* * CMT fast recovery code @@ -833,7 +754,7 @@ sctp_cwnd_update_after_sack_common(struct sctp_tcb *stcb, * If we are in loss recovery we skip any cwnd * update */ - goto skip_cwnd_update; + return; } /* * Did any measurements go on for this network? @@ -856,7 +777,7 @@ sctp_cwnd_update_after_sack_common(struct sctp_tcb *stcb, if (net->cc_mod.rtcc.lbw) { if (cc_bw_limit(stcb, net, nbw)) { /* Hold here, no update */ - goto skip_cwnd_update; + continue; } } else { uint64_t vtag, probepoint; @@ -1049,27 +970,25 @@ sctp_cwnd_update_after_sack_common(struct sctp_tcb *stcb, SCTP_CWND_LOG_NO_CUMACK); } } -skip_cwnd_update: - /* - * NOW, according to Karn's rule do we need to restore the - * RTO timer back? Check our net_ack2. If not set then we - * have a ambiguity.. i.e. all data ack'd was sent to more - * than one place. - */ - if (net->net_ack2) { - /* restore any doubled timers */ - net->RTO = (net->lastsa >> SCTP_RTT_SHIFT) + net->lastsv; - if (net->RTO < stcb->asoc.minrto) { - net->RTO = stcb->asoc.minrto; - } - if (net->RTO > stcb->asoc.maxrto) { - net->RTO = stcb->asoc.maxrto; - } - } } } static void +sctp_cwnd_update_exit_pf_common(struct sctp_tcb *stcb, struct sctp_nets *net) +{ + int old_cwnd; + + old_cwnd = net->cwnd; + net->cwnd = net->mtu; + SDT_PROBE(sctp, cwnd, net, ack, + stcb->asoc.my_vtag, ((stcb->sctp_ep->sctp_lport << 16) | (stcb->rport)), net, + old_cwnd, net->cwnd); + SCTPDBG(SCTP_DEBUG_INDATA1, "Destination %p moved from PF to reachable with cwnd %d.\n", + net, net->cwnd); +} + + +static void sctp_cwnd_update_after_timeout(struct sctp_tcb *stcb, struct sctp_nets *net) { int old_cwnd = net->cwnd; @@ -1344,32 +1263,6 @@ sctp_cwnd_update_after_output(struct sctp_tcb *stcb, } static void -sctp_cwnd_update_after_fr_timer(struct sctp_inpcb *inp, - struct sctp_tcb *stcb, struct sctp_nets *net) -{ - int old_cwnd = net->cwnd; - - sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_EARLY_FR_TMR, SCTP_SO_NOT_LOCKED); - /* - * make a small adjustment to cwnd and force to CA. - */ - if (net->cwnd > net->mtu) - /* drop down one MTU after sending */ - net->cwnd -= net->mtu; - if (net->cwnd < net->ssthresh) - /* still in SS move to CA */ - net->ssthresh = net->cwnd - 1; - SDT_PROBE(sctp, cwnd, net, fr, - stcb->asoc.my_vtag, - ((stcb->sctp_ep->sctp_lport << 16) | (stcb->rport)), - net, - old_cwnd, net->cwnd); - if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { - sctp_log_cwnd(stcb, net, (old_cwnd - net->cwnd), SCTP_CWND_LOG_FROM_FR); - } -} - -static void sctp_cwnd_update_after_sack(struct sctp_tcb *stcb, struct sctp_association *asoc, int accum_moved, int reneged_all, int will_exit) @@ -1858,40 +1751,6 @@ sctp_hs_cwnd_update_after_sack(struct sctp_tcb *stcb, } } #endif - if (SCTP_BASE_SYSCTL(sctp_early_fr)) { - /* - * So, first of all do we need to have a Early FR - * timer running? - */ - if ((!TAILQ_EMPTY(&asoc->sent_queue) && - (net->ref_count > 1) && - (net->flight_size < net->cwnd)) || - (reneged_all)) { - /* - * yes, so in this case stop it if its - * running, and then restart it. Reneging - * all is a special case where we want to - * run the Early FR timer and then force the - * last few unacked to be sent, causing us - * to illicit a sack with gaps to force out - * the others. - */ - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - SCTP_STAT_INCR(sctps_earlyfrstpidsck2); - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net, - SCTP_FROM_SCTP_INDATA + SCTP_LOC_20); - } - SCTP_STAT_INCR(sctps_earlyfrstrid); - sctp_timer_start(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net); - } else { - /* No, stop it if its running */ - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - SCTP_STAT_INCR(sctps_earlyfrstpidsck3); - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net, - SCTP_FROM_SCTP_INDATA + SCTP_LOC_21); - } - } - } /* if nothing was acked on this destination skip it */ if (net->net_ack == 0) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) { @@ -1899,47 +1758,6 @@ sctp_hs_cwnd_update_after_sack(struct sctp_tcb *stcb, } continue; } - if (net->net_ack2 > 0) { - /* - * Karn's rule applies to clearing error count, this - * is optional. - */ - net->error_count = 0; - if ((net->dest_state & SCTP_ADDR_NOT_REACHABLE) == - SCTP_ADDR_NOT_REACHABLE) { - /* addr came good */ - net->dest_state &= ~SCTP_ADDR_NOT_REACHABLE; - net->dest_state |= SCTP_ADDR_REACHABLE; - sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, - SCTP_RECEIVED_SACK, (void *)net, SCTP_SO_NOT_LOCKED); - /* now was it the primary? if so restore */ - if (net->dest_state & SCTP_ADDR_WAS_PRIMARY) { - (void)sctp_set_primary_addr(stcb, (struct sockaddr *)NULL, net); - } - } - /* - * JRS 5/14/07 - If CMT PF is on and the destination - * is in PF state, set the destination to active - * state and set the cwnd to one or two MTU's based - * on whether PF1 or PF2 is being used. - * - * Should we stop any running T3 timer here? - */ - if ((asoc->sctp_cmt_on_off > 0) && - (asoc->sctp_cmt_pf > 0) && - ((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF)) { - net->dest_state &= ~SCTP_ADDR_PF; - net->cwnd = net->mtu * asoc->sctp_cmt_pf; - SCTPDBG(SCTP_DEBUG_INDATA1, "Destination %p moved from PF to reachable with cwnd %d.\n", - net, net->cwnd); - /* - * Since the cwnd value is explicitly set, - * skip the code that updates the cwnd - * value. - */ - goto skip_cwnd_update; - } - } #ifdef JANA_CMT_FAST_RECOVERY /* * CMT fast recovery code @@ -1959,7 +1777,7 @@ sctp_hs_cwnd_update_after_sack(struct sctp_tcb *stcb, * If we are in loss recovery we skip any cwnd * update */ - goto skip_cwnd_update; + return; } /* * CMT: CUC algorithm. Update cwnd if pseudo-cumack has @@ -2004,23 +1822,6 @@ sctp_hs_cwnd_update_after_sack(struct sctp_tcb *stcb, SCTP_CWND_LOG_NO_CUMACK); } } -skip_cwnd_update: - /* - * NOW, according to Karn's rule do we need to restore the - * RTO timer back? Check our net_ack2. If not set then we - * have a ambiguity.. i.e. all data ack'd was sent to more - * than one place. - */ - if (net->net_ack2) { - /* restore any doubled timers */ - net->RTO = (net->lastsa >> SCTP_RTT_SHIFT) + net->lastsv; - if (net->RTO < stcb->asoc.minrto) { - net->RTO = stcb->asoc.minrto; - } - if (net->RTO > stcb->asoc.maxrto) { - net->RTO = stcb->asoc.maxrto; - } - } } } @@ -2340,40 +2141,6 @@ sctp_htcp_cwnd_update_after_sack(struct sctp_tcb *stcb, } } #endif - if (SCTP_BASE_SYSCTL(sctp_early_fr)) { - /* - * So, first of all do we need to have a Early FR - * timer running? - */ - if ((!TAILQ_EMPTY(&asoc->sent_queue) && - (net->ref_count > 1) && - (net->flight_size < net->cwnd)) || - (reneged_all)) { - /* - * yes, so in this case stop it if its - * running, and then restart it. Reneging - * all is a special case where we want to - * run the Early FR timer and then force the - * last few unacked to be sent, causing us - * to illicit a sack with gaps to force out - * the others. - */ - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - SCTP_STAT_INCR(sctps_earlyfrstpidsck2); - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net, - SCTP_FROM_SCTP_INDATA + SCTP_LOC_20); - } - SCTP_STAT_INCR(sctps_earlyfrstrid); - sctp_timer_start(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net); - } else { - /* No, stop it if its running */ - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - SCTP_STAT_INCR(sctps_earlyfrstpidsck3); - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net, - SCTP_FROM_SCTP_INDATA + SCTP_LOC_21); - } - } - } /* if nothing was acked on this destination skip it */ if (net->net_ack == 0) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) { @@ -2381,47 +2148,6 @@ sctp_htcp_cwnd_update_after_sack(struct sctp_tcb *stcb, } continue; } - if (net->net_ack2 > 0) { - /* - * Karn's rule applies to clearing error count, this - * is optional. - */ - net->error_count = 0; - if ((net->dest_state & SCTP_ADDR_NOT_REACHABLE) == - SCTP_ADDR_NOT_REACHABLE) { - /* addr came good */ - net->dest_state &= ~SCTP_ADDR_NOT_REACHABLE; - net->dest_state |= SCTP_ADDR_REACHABLE; - sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, - SCTP_RECEIVED_SACK, (void *)net, SCTP_SO_NOT_LOCKED); - /* now was it the primary? if so restore */ - if (net->dest_state & SCTP_ADDR_WAS_PRIMARY) { - (void)sctp_set_primary_addr(stcb, (struct sockaddr *)NULL, net); - } - } - /* - * JRS 5/14/07 - If CMT PF is on and the destination - * is in PF state, set the destination to active - * state and set the cwnd to one or two MTU's based - * on whether PF1 or PF2 is being used. - * - * Should we stop any running T3 timer here? - */ - if ((asoc->sctp_cmt_on_off > 0) && - (asoc->sctp_cmt_pf > 0) && - ((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF)) { - net->dest_state &= ~SCTP_ADDR_PF; - net->cwnd = net->mtu * asoc->sctp_cmt_pf; - SCTPDBG(SCTP_DEBUG_INDATA1, "Destination %p moved from PF to reachable with cwnd %d.\n", - net, net->cwnd); - /* - * Since the cwnd value is explicitly set, - * skip the code that updates the cwnd - * value. - */ - goto skip_cwnd_update; - } - } #ifdef JANA_CMT_FAST_RECOVERY /* * CMT fast recovery code @@ -2441,7 +2167,7 @@ sctp_htcp_cwnd_update_after_sack(struct sctp_tcb *stcb, * If we are in loss recovery we skip any cwnd * update */ - goto skip_cwnd_update; + return; } /* * CMT: CUC algorithm. Update cwnd if pseudo-cumack has @@ -2457,23 +2183,6 @@ sctp_htcp_cwnd_update_after_sack(struct sctp_tcb *stcb, SCTP_CWND_LOG_NO_CUMACK); } } -skip_cwnd_update: - /* - * NOW, according to Karn's rule do we need to restore the - * RTO timer back? Check our net_ack2. If not set then we - * have a ambiguity.. i.e. all data ack'd was sent to more - * than one place. - */ - if (net->net_ack2) { - /* restore any doubled timers */ - net->RTO = (net->lastsa >> SCTP_RTT_SHIFT) + net->lastsv; - if (net->RTO < stcb->asoc.minrto) { - net->RTO = stcb->asoc.minrto; - } - if (net->RTO > stcb->asoc.maxrto) { - net->RTO = stcb->asoc.maxrto; - } - } } } @@ -2566,30 +2275,6 @@ sctp_htcp_cwnd_update_after_timeout(struct sctp_tcb *stcb, } static void -sctp_htcp_cwnd_update_after_fr_timer(struct sctp_inpcb *inp, - struct sctp_tcb *stcb, struct sctp_nets *net) -{ - int old_cwnd; - - old_cwnd = net->cwnd; - - sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_EARLY_FR_TMR, SCTP_SO_NOT_LOCKED); - net->cc_mod.htcp_ca.last_cong = sctp_get_tick_count(); - /* - * make a small adjustment to cwnd and force to CA. - */ - if (net->cwnd > net->mtu) - /* drop down one MTU after sending */ - net->cwnd -= net->mtu; - if (net->cwnd < net->ssthresh) - /* still in SS move to CA */ - net->ssthresh = net->cwnd - 1; - if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_MONITOR_ENABLE) { - sctp_log_cwnd(stcb, net, (old_cwnd - net->cwnd), SCTP_CWND_LOG_FROM_FR); - } -} - -static void sctp_htcp_cwnd_update_after_ecn_echo(struct sctp_tcb *stcb, struct sctp_nets *net, int in_window, int num_pkt_lost) { @@ -2618,42 +2303,42 @@ struct sctp_cc_functions sctp_cc_functions[] = { { .sctp_set_initial_cc_param = sctp_set_initial_cc_param, .sctp_cwnd_update_after_sack = sctp_cwnd_update_after_sack, + .sctp_cwnd_update_exit_pf = sctp_cwnd_update_exit_pf_common, .sctp_cwnd_update_after_fr = sctp_cwnd_update_after_fr, .sctp_cwnd_update_after_timeout = sctp_cwnd_update_after_timeout, .sctp_cwnd_update_after_ecn_echo = sctp_cwnd_update_after_ecn_echo, .sctp_cwnd_update_after_packet_dropped = sctp_cwnd_update_after_packet_dropped, .sctp_cwnd_update_after_output = sctp_cwnd_update_after_output, - .sctp_cwnd_update_after_fr_timer = sctp_cwnd_update_after_fr_timer }, { .sctp_set_initial_cc_param = sctp_set_initial_cc_param, .sctp_cwnd_update_after_sack = sctp_hs_cwnd_update_after_sack, + .sctp_cwnd_update_exit_pf = sctp_cwnd_update_exit_pf_common, .sctp_cwnd_update_after_fr = sctp_hs_cwnd_update_after_fr, .sctp_cwnd_update_after_timeout = sctp_cwnd_update_after_timeout, .sctp_cwnd_update_after_ecn_echo = sctp_cwnd_update_after_ecn_echo, .sctp_cwnd_update_after_packet_dropped = sctp_cwnd_update_after_packet_dropped, .sctp_cwnd_update_after_output = sctp_cwnd_update_after_output, - .sctp_cwnd_update_after_fr_timer = sctp_cwnd_update_after_fr_timer }, { .sctp_set_initial_cc_param = sctp_htcp_set_initial_cc_param, .sctp_cwnd_update_after_sack = sctp_htcp_cwnd_update_after_sack, + .sctp_cwnd_update_exit_pf = sctp_cwnd_update_exit_pf_common, .sctp_cwnd_update_after_fr = sctp_htcp_cwnd_update_after_fr, .sctp_cwnd_update_after_timeout = sctp_htcp_cwnd_update_after_timeout, .sctp_cwnd_update_after_ecn_echo = sctp_htcp_cwnd_update_after_ecn_echo, .sctp_cwnd_update_after_packet_dropped = sctp_cwnd_update_after_packet_dropped, .sctp_cwnd_update_after_output = sctp_cwnd_update_after_output, - .sctp_cwnd_update_after_fr_timer = sctp_htcp_cwnd_update_after_fr_timer }, { .sctp_set_initial_cc_param = sctp_set_rtcc_initial_cc_param, .sctp_cwnd_update_after_sack = sctp_cwnd_update_rtcc_after_sack, + .sctp_cwnd_update_exit_pf = sctp_cwnd_update_exit_pf_common, .sctp_cwnd_update_after_fr = sctp_cwnd_update_after_fr, .sctp_cwnd_update_after_timeout = sctp_cwnd_update_after_timeout, .sctp_cwnd_update_after_ecn_echo = sctp_cwnd_update_rtcc_after_ecn_echo, .sctp_cwnd_update_after_packet_dropped = sctp_cwnd_update_after_packet_dropped, .sctp_cwnd_update_after_output = sctp_cwnd_update_after_output, - .sctp_cwnd_update_after_fr_timer = sctp_cwnd_update_after_fr_timer, .sctp_cwnd_update_packet_transmitted = sctp_cwnd_update_rtcc_packet_transmitted, .sctp_cwnd_update_tsn_acknowledged = sctp_cwnd_update_rtcc_tsn_acknowledged, .sctp_cwnd_new_transmission_begins = sctp_cwnd_new_rtcc_transmission_begins, diff --git a/sys/netinet/sctp_constants.h b/sys/netinet/sctp_constants.h index 6c2df1693fb1..4087c8d266d1 100644 --- a/sys/netinet/sctp_constants.h +++ b/sys/netinet/sctp_constants.h @@ -416,7 +416,7 @@ __FBSDID("$FreeBSD$"); #define SCTP_STR_RESET_IN_REQUEST 0x000e #define SCTP_STR_RESET_TSN_REQUEST 0x000f #define SCTP_STR_RESET_RESPONSE 0x0010 -#define SCTP_STR_RESET_ADD_STREAMS 0x0011 +#define SCTP_STR_RESET_ADD_STREAMS 0x0011 #define SCTP_MAX_RESET_PARAMS 2 #define SCTP_STREAM_RESET_TSN_DELTA 0x1000 @@ -508,14 +508,10 @@ __FBSDID("$FreeBSD$"); /* SCTP reachability state for each address */ #define SCTP_ADDR_REACHABLE 0x001 -#define SCTP_ADDR_NOT_REACHABLE 0x002 #define SCTP_ADDR_NOHB 0x004 #define SCTP_ADDR_BEING_DELETED 0x008 #define SCTP_ADDR_NOT_IN_ASSOC 0x010 -#define SCTP_ADDR_WAS_PRIMARY 0x020 -#define SCTP_ADDR_SWITCH_PRIMARY 0x040 #define SCTP_ADDR_OUT_OF_SCOPE 0x080 -#define SCTP_ADDR_DOUBLE_SWITCH 0x100 #define SCTP_ADDR_UNCONFIRMED 0x200 #define SCTP_ADDR_REQ_PRIMARY 0x400 /* JRS 5/13/07 - Added potentially failed state for CMT PF */ @@ -579,14 +575,13 @@ __FBSDID("$FreeBSD$"); #define SCTP_TIMER_TYPE_EVENTWAKE 13 #define SCTP_TIMER_TYPE_STRRESET 14 #define SCTP_TIMER_TYPE_INPKILL 15 -#define SCTP_TIMER_TYPE_EARLYFR 17 -#define SCTP_TIMER_TYPE_ASOCKILL 18 -#define SCTP_TIMER_TYPE_ADDR_WQ 19 -#define SCTP_TIMER_TYPE_ZERO_COPY 20 -#define SCTP_TIMER_TYPE_ZCOPY_SENDQ 21 -#define SCTP_TIMER_TYPE_PRIM_DELETED 22 +#define SCTP_TIMER_TYPE_ASOCKILL 16 +#define SCTP_TIMER_TYPE_ADDR_WQ 17 +#define SCTP_TIMER_TYPE_ZERO_COPY 18 +#define SCTP_TIMER_TYPE_ZCOPY_SENDQ 19 +#define SCTP_TIMER_TYPE_PRIM_DELETED 20 /* add new timers here - and increment LAST */ -#define SCTP_TIMER_TYPE_LAST 23 +#define SCTP_TIMER_TYPE_LAST 21 #define SCTP_IS_TIMER_TYPE_VALID(t) (((t) > SCTP_TIMER_TYPE_NONE) && \ ((t) < SCTP_TIMER_TYPE_LAST)) @@ -655,16 +650,17 @@ __FBSDID("$FreeBSD$"); #define SCTP_DEFAULT_SECRET_LIFE_SEC 3600 #define SCTP_RTO_UPPER_BOUND (60000) /* 60 sec in ms */ -#define SCTP_RTO_LOWER_BOUND (300) /* 0.3 sec is ms */ +#define SCTP_RTO_LOWER_BOUND (1000) /* 1 sec is ms */ #define SCTP_RTO_INITIAL (3000) /* 3 sec in ms */ #define SCTP_INP_KILL_TIMEOUT 20/* number of ms to retry kill of inpcb */ #define SCTP_ASOC_KILL_TIMEOUT 10 /* number of ms to retry kill of inpcb */ -#define SCTP_DEF_MAX_INIT 8 -#define SCTP_DEF_MAX_SEND 10 -#define SCTP_DEF_MAX_PATH_RTX 5 +#define SCTP_DEF_MAX_INIT 8 +#define SCTP_DEF_MAX_SEND 10 +#define SCTP_DEF_MAX_PATH_RTX 5 +#define SCTP_DEF_PATH_PF_THRESHOLD SCTP_DEF_MAX_PATH_RTX #define SCTP_DEF_PMTU_RAISE_SEC 600 /* 10 min between raise attempts */ @@ -679,7 +675,7 @@ __FBSDID("$FreeBSD$"); /* Send window update (incr * this > hiwat). Should be a power of 2 */ #define SCTP_MINIMAL_RWND (4096) /* minimal rwnd */ -#define SCTP_ADDRMAX 24 +#define SCTP_ADDRMAX 16 /* SCTP DEBUG Switch parameters */ #define SCTP_DEBUG_TIMER1 0x00000001 diff --git a/sys/netinet/sctp_header.h b/sys/netinet/sctp_header.h index dc8d91785057..1aff6a1485e9 100644 --- a/sys/netinet/sctp_header.h +++ b/sys/netinet/sctp_header.h @@ -98,9 +98,10 @@ struct sctp_heartbeat_info_param { uint32_t time_value_2; uint32_t random_value1; uint32_t random_value2; - uint16_t user_req; uint8_t addr_family; uint8_t addr_len; + /* make sure that this structure is 4 byte aligned */ + uint8_t padding[2]; char address[SCTP_ADDRMAX]; } SCTP_PACKED; diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c index e142a3e5246e..48b79fb89da0 100644 --- a/sys/netinet/sctp_indata.c +++ b/sys/netinet/sctp_indata.c @@ -2434,7 +2434,8 @@ sctp_sack_check(struct sctp_tcb *stcb, int was_a_gap, int *abort_flag) sctp_timer_stop(SCTP_TIMER_TYPE_RECV, stcb->sctp_ep, stcb, NULL, SCTP_FROM_SCTP_INDATA + SCTP_LOC_18); } - sctp_send_shutdown(stcb, stcb->asoc.primary_destination); + sctp_send_shutdown(stcb, + ((stcb->asoc.alternate) ? stcb->asoc.alternate : stcb->asoc.primary_destination)); sctp_send_sack(stcb, SCTP_SO_NOT_LOCKED); } else { int is_a_gap; @@ -4054,9 +4055,50 @@ sctp_express_handle_sack(struct sctp_tcb *stcb, uint32_t cumack, } /* JRS - Use the congestion control given in the CC module */ - if ((asoc->last_acked_seq != cumack) && (ecne_seen == 0)) + if ((asoc->last_acked_seq != cumack) && (ecne_seen == 0)) { + TAILQ_FOREACH(net, &asoc->nets, sctp_next) { + if (net->net_ack2 > 0) { + /* + * Karn's rule applies to clearing error + * count, this is optional. + */ + net->error_count = 0; + if (!(net->dest_state & SCTP_ADDR_REACHABLE)) { + /* addr came good */ + net->dest_state |= SCTP_ADDR_REACHABLE; + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, + SCTP_RECEIVED_SACK, (void *)net, SCTP_SO_NOT_LOCKED); + } + if (net == stcb->asoc.primary_destination) { + if (stcb->asoc.alternate) { + /* + * release the alternate, + * primary is good + */ + sctp_free_remote_addr(stcb->asoc.alternate); + stcb->asoc.alternate = NULL; + } + } + if (net->dest_state & SCTP_ADDR_PF) { + net->dest_state &= ~SCTP_ADDR_PF; + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_3); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); + asoc->cc_functions.sctp_cwnd_update_exit_pf(stcb, net); + /* Done with this net */ + net->net_ack = 0; + } + /* restore any doubled timers */ + net->RTO = (net->lastsa >> SCTP_RTT_SHIFT) + net->lastsv; + if (net->RTO < stcb->asoc.minrto) { + net->RTO = stcb->asoc.minrto; + } + if (net->RTO > stcb->asoc.maxrto) { + net->RTO = stcb->asoc.maxrto; + } + } + } asoc->cc_functions.sctp_cwnd_update_after_sack(stcb, asoc, 1, 0, 0); - + } asoc->last_acked_seq = cumack; if (TAILQ_EMPTY(&asoc->sent_queue)) { @@ -4127,13 +4169,6 @@ again: stcb, net, SCTP_FROM_SCTP_INDATA + SCTP_LOC_22); } - if (SCTP_BASE_SYSCTL(sctp_early_fr)) { - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - SCTP_STAT_INCR(sctps_earlyfrstpidsck4); - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net, - SCTP_FROM_SCTP_INDATA + SCTP_LOC_23); - } - } } } if ((j == 0) && @@ -4222,6 +4257,8 @@ again: stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_24; sctp_abort_an_association(stcb->sctp_ep, stcb, SCTP_RESPONSE_TO_USER_REQ, oper, SCTP_SO_NOT_LOCKED); } else { + struct sctp_nets *netp; + if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); @@ -4229,26 +4266,36 @@ again: SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT); SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); sctp_stop_timers_for_shutdown(stcb); - sctp_send_shutdown(stcb, - stcb->asoc.primary_destination); + if (asoc->alternate) { + netp = asoc->alternate; + } else { + netp = asoc->primary_destination; + } + sctp_send_shutdown(stcb, netp); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, - stcb->sctp_ep, stcb, asoc->primary_destination); + stcb->sctp_ep, stcb, netp); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, - stcb->sctp_ep, stcb, asoc->primary_destination); + stcb->sctp_ep, stcb, netp); } } else if ((SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) && (asoc->stream_queue_cnt == 0)) { + struct sctp_nets *netp; + + if (asoc->alternate) { + netp = asoc->alternate; + } else { + netp = asoc->primary_destination; + } if (asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT) { goto abort_out_now; } SCTP_STAT_DECR_GAUGE32(sctps_currestab); SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_ACK_SENT); SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); - sctp_send_shutdown_ack(stcb, - stcb->asoc.primary_destination); + sctp_send_shutdown_ack(stcb, netp); sctp_stop_timers_for_shutdown(stcb); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNACK, - stcb->sctp_ep, stcb, asoc->primary_destination); + stcb->sctp_ep, stcb, netp); } } /*********************************************/ @@ -4380,7 +4427,7 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup, num_dup, SCTP_LOG_NEW_SACK); } - if ((num_dup) && (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_FR_LOGGING_ENABLE | SCTP_EARLYFR_LOGGING_ENABLE))) { + if ((num_dup) && (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE)) { uint16_t i; uint32_t *dupdata, dblock; @@ -4468,13 +4515,6 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup, TAILQ_FOREACH(net, &asoc->nets, sctp_next) { sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_INDATA + SCTP_LOC_26); - if (SCTP_BASE_SYSCTL(sctp_early_fr)) { - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - SCTP_STAT_INCR(sctps_earlyfrstpidsck1); - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net, - SCTP_FROM_SCTP_INDATA + SCTP_LOC_26); - } - } net->partial_bytes_acked = 0; net->flight_size = 0; } @@ -4830,20 +4870,54 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup, asoc->saw_sack_with_nr_frags = 0; /* JRS - Use the congestion control given in the CC module */ - if (ecne_seen == 0) + if (ecne_seen == 0) { + TAILQ_FOREACH(net, &asoc->nets, sctp_next) { + if (net->net_ack2 > 0) { + /* + * Karn's rule applies to clearing error + * count, this is optional. + */ + net->error_count = 0; + if (!(net->dest_state & SCTP_ADDR_REACHABLE)) { + /* addr came good */ + net->dest_state |= SCTP_ADDR_REACHABLE; + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, + SCTP_RECEIVED_SACK, (void *)net, SCTP_SO_NOT_LOCKED); + } + if (net == stcb->asoc.primary_destination) { + if (stcb->asoc.alternate) { + /* + * release the alternate, + * primary is good + */ + sctp_free_remote_addr(stcb->asoc.alternate); + stcb->asoc.alternate = NULL; + } + } + if (net->dest_state & SCTP_ADDR_PF) { + net->dest_state &= ~SCTP_ADDR_PF; + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_3); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); + asoc->cc_functions.sctp_cwnd_update_exit_pf(stcb, net); + /* Done with this net */ + net->net_ack = 0; + } + /* restore any doubled timers */ + net->RTO = (net->lastsa >> SCTP_RTT_SHIFT) + net->lastsv; + if (net->RTO < stcb->asoc.minrto) { + net->RTO = stcb->asoc.minrto; + } + if (net->RTO > stcb->asoc.maxrto) { + net->RTO = stcb->asoc.maxrto; + } + } + } asoc->cc_functions.sctp_cwnd_update_after_sack(stcb, asoc, accum_moved, reneged_all, will_exit_fast_recovery); - + } if (TAILQ_EMPTY(&asoc->sent_queue)) { /* nothing left in-flight */ TAILQ_FOREACH(net, &asoc->nets, sctp_next) { /* stop all timers */ - if (SCTP_BASE_SYSCTL(sctp_early_fr)) { - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - SCTP_STAT_INCR(sctps_earlyfrstpidsck4); - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net, - SCTP_FROM_SCTP_INDATA + SCTP_LOC_29); - } - } sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_INDATA + SCTP_LOC_30); net->flight_size = 0; @@ -4918,6 +4992,13 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup, sctp_abort_an_association(stcb->sctp_ep, stcb, SCTP_RESPONSE_TO_USER_REQ, oper, SCTP_SO_NOT_LOCKED); return; } else { + struct sctp_nets *netp; + + if (asoc->alternate) { + netp = asoc->alternate; + } else { + netp = asoc->primary_destination; + } if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); @@ -4925,27 +5006,32 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup, SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT); SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); sctp_stop_timers_for_shutdown(stcb); - sctp_send_shutdown(stcb, - stcb->asoc.primary_destination); + sctp_send_shutdown(stcb, netp); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, - stcb->sctp_ep, stcb, asoc->primary_destination); + stcb->sctp_ep, stcb, netp); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, - stcb->sctp_ep, stcb, asoc->primary_destination); + stcb->sctp_ep, stcb, netp); } return; } else if ((SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) && (asoc->stream_queue_cnt == 0)) { + struct sctp_nets *netp; + + if (asoc->alternate) { + netp = asoc->alternate; + } else { + netp = asoc->primary_destination; + } if (asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT) { goto abort_out_now; } SCTP_STAT_DECR_GAUGE32(sctps_currestab); SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_ACK_SENT); SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); - sctp_send_shutdown_ack(stcb, - stcb->asoc.primary_destination); + sctp_send_shutdown_ack(stcb, netp); sctp_stop_timers_for_shutdown(stcb); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNACK, - stcb->sctp_ep, stcb, asoc->primary_destination); + stcb->sctp_ep, stcb, netp); return; } } @@ -5056,13 +5142,6 @@ again: stcb, net, SCTP_FROM_SCTP_INDATA + SCTP_LOC_22); } - if (SCTP_BASE_SYSCTL(sctp_early_fr)) { - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - SCTP_STAT_INCR(sctps_earlyfrstpidsck4); - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net, - SCTP_FROM_SCTP_INDATA + SCTP_LOC_23); - } - } } } if ((j == 0) && diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index 4c4d779b39d0..d8f0ae539707 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -537,6 +537,7 @@ sctp_handle_heartbeat_ack(struct sctp_heartbeat_chunk *cp, struct sctp_nets *r_net, *f_net; struct timeval tv; int req_prim = 0; + uint16_t old_error_counter; #ifdef INET struct sockaddr_in *sin; @@ -599,7 +600,6 @@ sctp_handle_heartbeat_ack(struct sctp_heartbeat_chunk *cp, r_net->dest_state &= ~SCTP_ADDR_UNCONFIRMED; if (r_net->dest_state & SCTP_ADDR_REQ_PRIMARY) { stcb->asoc.primary_destination = r_net; - r_net->dest_state &= ~SCTP_ADDR_WAS_PRIMARY; r_net->dest_state &= ~SCTP_ADDR_REQ_PRIMARY; f_net = TAILQ_FIRST(&stcb->asoc.nets); if (f_net != r_net) { @@ -616,44 +616,37 @@ sctp_handle_heartbeat_ack(struct sctp_heartbeat_chunk *cp, } sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED, stcb, 0, (void *)r_net, SCTP_SO_NOT_LOCKED); + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_3); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net); } + old_error_counter = r_net->error_count; r_net->error_count = 0; r_net->hb_responded = 1; tv.tv_sec = cp->heartbeat.hb_info.time_value_1; tv.tv_usec = cp->heartbeat.hb_info.time_value_2; - if (r_net->dest_state & SCTP_ADDR_NOT_REACHABLE) { - r_net->dest_state &= ~SCTP_ADDR_NOT_REACHABLE; + /* Now lets do a RTO with this */ + r_net->RTO = sctp_calculate_rto(stcb, &stcb->asoc, r_net, &tv, sctp_align_safe_nocopy, + SCTP_RTT_FROM_NON_DATA); + if (!(r_net->dest_state & SCTP_ADDR_REACHABLE)) { r_net->dest_state |= SCTP_ADDR_REACHABLE; sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, SCTP_HEARTBEAT_SUCCESS, (void *)r_net, SCTP_SO_NOT_LOCKED); - /* now was it the primary? if so restore */ - if (r_net->dest_state & SCTP_ADDR_WAS_PRIMARY) { - (void)sctp_set_primary_addr(stcb, (struct sockaddr *)NULL, r_net); - } } - /* - * JRS 5/14/07 - If CMT PF is on and the destination is in PF state, - * set the destination to active state and set the cwnd to one or - * two MTU's based on whether PF1 or PF2 is being used. If a T3 - * timer is running, for the destination, stop the timer because a - * PF-heartbeat was received. - */ - if ((stcb->asoc.sctp_cmt_on_off > 0) && - (stcb->asoc.sctp_cmt_pf > 0) && - ((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF)) { - if (SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) { - sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, - stcb, net, - SCTP_FROM_SCTP_INPUT + SCTP_LOC_5); - } - net->dest_state &= ~SCTP_ADDR_PF; - net->cwnd = net->mtu * stcb->asoc.sctp_cmt_pf; - SCTPDBG(SCTP_DEBUG_INPUT1, "Destination %p moved from PF to reachable with cwnd %d.\n", - net, net->cwnd); + if (r_net->dest_state & SCTP_ADDR_PF) { + r_net->dest_state &= ~SCTP_ADDR_PF; + stcb->asoc.cc_functions.sctp_cwnd_update_exit_pf(stcb, net); + } + if (old_error_counter > 0) { + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_3); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net); + } + if (r_net == stcb->asoc.primary_destination) { + if (stcb->asoc.alternate) { + /* release the alternate, primary is good */ + sctp_free_remote_addr(stcb->asoc.alternate); + stcb->asoc.alternate = NULL; + } } - /* Now lets do a RTO with this */ - r_net->RTO = sctp_calculate_rto(stcb, &stcb->asoc, r_net, &tv, sctp_align_safe_nocopy, - SCTP_RTT_FROM_NON_DATA); /* Mobility adaptation */ if (req_prim) { if ((sctp_is_mobility_feature_on(stcb->sctp_ep, @@ -825,6 +818,35 @@ sctp_handle_abort(struct sctp_abort_chunk *cp, } static void +sctp_start_net_timers(struct sctp_tcb *stcb) +{ + uint32_t cnt_hb_sent; + struct sctp_nets *net; + + cnt_hb_sent = 0; + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + /* + * For each network start: 1) A pmtu timer. 2) A HB timer 3) + * If the dest in unconfirmed send a hb as well if under + * max_hb_burst have been sent. + */ + sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, net); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); + if ((net->dest_state & SCTP_ADDR_UNCONFIRMED) && + (cnt_hb_sent < SCTP_BASE_SYSCTL(sctp_hb_maxburst))) { + sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); + cnt_hb_sent++; + } + } + if (cnt_hb_sent) { + sctp_chunk_output(stcb->sctp_ep, stcb, + SCTP_OUTPUT_FROM_COOKIE_ACK, + SCTP_SO_NOT_LOCKED); + } +} + + +static void sctp_handle_shutdown(struct sctp_shutdown_chunk *cp, struct sctp_tcb *stcb, struct sctp_nets *net, int *abort_flag) { @@ -916,7 +938,7 @@ sctp_handle_shutdown(struct sctp_shutdown_chunk *cp, } else { /* no outstanding data to send, so move on... */ /* send SHUTDOWN-ACK */ - sctp_send_shutdown_ack(stcb, stcb->asoc.primary_destination); + sctp_send_shutdown_ack(stcb, net); /* move to SHUTDOWN-ACK-SENT state */ if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { @@ -2685,7 +2707,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, /* TSNH! Huh, why do I need to add this address here? */ int ret; - ret = sctp_add_remote_addr(*stcb, to, SCTP_DONOT_SETSCOPE, + ret = sctp_add_remote_addr(*stcb, to, NULL, SCTP_DONOT_SETSCOPE, SCTP_IN_COOKIE_PROC); netl = sctp_findnet(*stcb, to); } @@ -2697,10 +2719,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, send_int_conf = 1; } } - if (*stcb) { - sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, *inp_p, - *stcb, NULL); - } + sctp_start_net_timers(*stcb); if ((*inp_p)->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) { if (!had_a_existing_tcb || (((*inp_p)->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)) { @@ -2890,6 +2909,7 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp, /* state change only needed when I am in right state */ SCTPDBG(SCTP_DEBUG_INPUT2, "moving to OPEN state\n"); SCTP_SET_STATE(asoc, SCTP_STATE_OPEN); + sctp_start_net_timers(stcb); if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) { sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb, asoc->primary_destination); @@ -3380,7 +3400,7 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc, * Only retransmit if we KNOW we wont destroy the * tcb */ - (void)sctp_send_hb(stcb, 1, net, SCTP_SO_NOT_LOCKED); + sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); } break; case SCTP_SHUTDOWN: @@ -3999,8 +4019,7 @@ strres_nochunk: /* setup chunk parameters */ chk->sent = SCTP_DATAGRAM_UNSENT; chk->snd_count = 0; - chk->whoTo = stcb->asoc.primary_destination; - atomic_add_int(&chk->whoTo->ref_count, 1); + chk->whoTo = NULL; ch = mtod(chk->data, struct sctp_chunkhdr *); ch->chunk_type = SCTP_STREAM_RESET; @@ -4630,8 +4649,7 @@ process_control_chunks: if ((stcb != NULL) && (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT)) { - sctp_send_shutdown_ack(stcb, - stcb->asoc.primary_destination); + sctp_send_shutdown_ack(stcb, NULL); *offset = length; sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_CONTROL_PROC, SCTP_SO_NOT_LOCKED); if (locked_tcb) { diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index 043b3b274c34..803c30945738 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -3543,7 +3543,8 @@ sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *er *error = EINVAL; return (-1); } - if (sctp_add_remote_addr(stcb, (struct sockaddr *)&sin, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { + if (sctp_add_remote_addr(stcb, (struct sockaddr *)&sin, NULL, + SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { *error = ENOBUFS; return (1); } @@ -3574,13 +3575,15 @@ sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *er *error = EINVAL; return (-1); } - if (sctp_add_remote_addr(stcb, (struct sockaddr *)&sin, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { + if (sctp_add_remote_addr(stcb, (struct sockaddr *)&sin, NULL, + SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { *error = ENOBUFS; return (1); } } else #endif - if (sctp_add_remote_addr(stcb, (struct sockaddr *)&sin6, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { + if (sctp_add_remote_addr(stcb, (struct sockaddr *)&sin6, NULL, + SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { *error = ENOBUFS; return (1); } @@ -3828,28 +3831,7 @@ sctp_handle_no_route(struct sctp_tcb *stcb, (void *)net, so_locked); net->dest_state &= ~SCTP_ADDR_REACHABLE; - net->dest_state |= SCTP_ADDR_NOT_REACHABLE; - /* - * JRS 5/14/07 - If a destination is - * unreachable, the PF bit is turned off. - * This allows an unambiguous use of the PF - * bit for destinations that are reachable - * but potentially failed. If the - * destination is set to the unreachable - * state, also set the destination to the PF - * state. - */ - /* - * Add debug message here if destination is - * not in PF state. - */ - /* Stop any running T3 timers here? */ - if ((stcb->asoc.sctp_cmt_on_off > 0) && - (stcb->asoc.sctp_cmt_pf > 0)) { - net->dest_state &= ~SCTP_ADDR_PF; - SCTPDBG(SCTP_DEBUG_OUTPUT1, "Destination %p moved from PF to unreachable.\n", - net); - } + net->dest_state &= ~SCTP_ADDR_PF; } } if (stcb) { @@ -3859,14 +3841,16 @@ sctp_handle_no_route(struct sctp_tcb *stcb, alt = sctp_find_alternate_net(stcb, net, 0); if (alt != net) { - if (sctp_set_primary_addr(stcb, (struct sockaddr *)NULL, alt) == 0) { - net->dest_state |= SCTP_ADDR_WAS_PRIMARY; - if (net->ro._s_addr) { - sctp_free_ifa(net->ro._s_addr); - net->ro._s_addr = NULL; - } - net->src_addr_selected = 0; + if (stcb->asoc.alternate) { + sctp_free_remote_addr(stcb->asoc.alternate); } + stcb->asoc.alternate = alt; + atomic_add_int(&stcb->asoc.alternate->ref_count, 1); + if (net->ro._s_addr) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + } + net->src_addr_selected = 0; } } } @@ -6498,6 +6482,7 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, int added_control = 0; int un_sent, do_chunk_output = 1; struct sctp_association *asoc; + struct sctp_nets *net; ca = (struct sctp_copy_all *)ptr; if (ca->m == NULL) { @@ -6531,6 +6516,11 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, m = NULL; } SCTP_TCB_LOCK_ASSERT(stcb); + if (stcb->asoc.alternate) { + net = stcb->asoc.alternate; + } else { + net = stcb->asoc.primary_destination; + } if (ca->sndrcv.sinfo_flags & SCTP_ABORT) { /* Abort this assoc with m as the user defined reason */ if (m) { @@ -6569,7 +6559,7 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, } } else { if (m) { - ret = sctp_msg_append(stcb, stcb->asoc.primary_destination, m, + ret = sctp_msg_append(stcb, net, m, &ca->sndrcv, 1); } asoc = &stcb->asoc; @@ -6596,14 +6586,14 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, * only send SHUTDOWN the first time * through */ - sctp_send_shutdown(stcb, stcb->asoc.primary_destination); + sctp_send_shutdown(stcb, net); if (SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); } SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT); SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, - asoc->primary_destination); + net); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb, asoc->primary_destination); added_control = 1; @@ -7733,13 +7723,13 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, struct sctp_auth_chunk *auth = NULL; uint16_t auth_keyid; int override_ok = 1; + int skip_fill_up = 0; int data_auth_reqd = 0; /* * JRS 5/14/07 - Add flag for whether a heartbeat is sent to the * destination. */ - int pf_hbflag = 0; int quit_now = 0; *num_out = 0; @@ -7806,7 +7796,22 @@ nothing_to_send: max_send_per_dest = SCTP_SB_LIMIT_SND(stcb->sctp_socket) / asoc->numnets; else max_send_per_dest = 0; + if (no_data_chunks == 0) { + /* How many non-directed chunks are there? */ + TAILQ_FOREACH(chk, &asoc->send_queue, sctp_next) { + if (chk->whoTo == NULL) { + /* + * We already have non-directed chunks on + * the queue, no need to do a fill-up. + */ + skip_fill_up = 1; + break; + } + } + + } if ((no_data_chunks == 0) && + (skip_fill_up == 0) && (!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc))) { TAILQ_FOREACH(net, &asoc->nets, sctp_next) { /* @@ -7821,8 +7826,10 @@ nothing_to_send: * copy by reference (we hope). */ net->window_probe = 0; - if ((net->dest_state & SCTP_ADDR_NOT_REACHABLE) || - (net->dest_state & SCTP_ADDR_UNCONFIRMED)) { + if ((net != stcb->asoc.alternate) && + ((net->dest_state & SCTP_ADDR_PF) || + (!(net->dest_state & SCTP_ADDR_REACHABLE)) || + (net->dest_state & SCTP_ADDR_UNCONFIRMED))) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) { sctp_log_cwnd(stcb, net, 1, SCTP_CWND_LOG_FILL_OUTQ_CALLED); @@ -7833,16 +7840,6 @@ nothing_to_send: (net->flight_size == 0)) { (*stcb->asoc.cc_functions.sctp_cwnd_new_transmission_begins) (stcb, net); } - if ((asoc->sctp_cmt_on_off == 0) && - (asoc->primary_destination != net) && - (net->ref_count < 2)) { - /* nothing can be in queue for this guy */ - if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) { - sctp_log_cwnd(stcb, net, 2, - SCTP_CWND_LOG_FILL_OUTQ_CALLED); - } - continue; - } if (net->flight_size >= net->cwnd) { /* skip this network, no room - can't fill */ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) { @@ -7886,6 +7883,16 @@ nothing_to_send: } else { start_at = TAILQ_FIRST(&asoc->nets); } + TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) { + if (chk->whoTo == NULL) { + if (asoc->alternate) { + chk->whoTo = asoc->alternate; + } else { + chk->whoTo = asoc->primary_destination; + } + atomic_add_int(&chk->whoTo->ref_count, 1); + } + } old_start_at = NULL; again_one_more_time: for (net = start_at; net != NULL; net = TAILQ_NEXT(net, sctp_next)) { @@ -7896,15 +7903,6 @@ again_one_more_time: break; } tsns_sent = 0xa; - if ((asoc->sctp_cmt_on_off == 0) && - (asoc->primary_destination != net) && - (net->ref_count < 2)) { - /* - * Ref-count of 1 so we cannot have data or control - * queued to this address. Skip it (non-CMT). - */ - continue; - } if (TAILQ_EMPTY(&asoc->control_send_queue) && TAILQ_EMPTY(&asoc->asconf_send_queue) && (net->flight_size >= net->cwnd)) { @@ -8266,15 +8264,8 @@ again_one_more_time: (chk->rec.chunk_id.id == SCTP_ECN_CWR) || (chk->rec.chunk_id.id == SCTP_PACKET_DROPPED) || (chk->rec.chunk_id.id == SCTP_ASCONF_ACK)) { - if (chk->rec.chunk_id.id == SCTP_HEARTBEAT_REQUEST) { hbflag = 1; - /* - * JRS 5/14/07 - Set the - * flag to say a heartbeat - * is being sent. - */ - pf_hbflag = 1; } /* remove these chunks at the end */ if ((chk->rec.chunk_id.id == SCTP_SELECTIVE_ACK) || @@ -8408,7 +8399,7 @@ again_one_more_time: } /* JRI: if dest is in PF state, do not send data to it */ if ((asoc->sctp_cmt_on_off > 0) && - (asoc->sctp_cmt_pf > 0) && + (net != stcb->asoc.alternate) && (net->dest_state & SCTP_ADDR_PF)) { goto no_data_fill; } @@ -8486,6 +8477,17 @@ again_one_more_time: /* Don't send the chunk on this net */ continue; } + if (asoc->sctp_cmt_on_off == 0) { + if ((asoc->alternate) && + (asoc->alternate != net) && + (chk->whoTo == NULL)) { + continue; + } else if ((net != asoc->primary_destination) && + (asoc->alternate == NULL) && + (chk->whoTo == NULL)) { + continue; + } + } if ((chk->send_size > omtu) && ((chk->flags & CHUNK_FLAGS_FRAGMENT_OK) == 0)) { /*- * strange, we have a chunk that is @@ -8646,18 +8648,6 @@ no_data_fill: * restart it. */ sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net); - } else if ((asoc->sctp_cmt_on_off > 0) && - (asoc->sctp_cmt_pf > 0) && - pf_hbflag && - ((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF) && - (!SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer))) { - /* - * JRS 5/14/07 - If a HB has been sent to a - * PF destination and no T3 timer is - * currently running, start the T3 timer to - * track the HBs that were sent. - */ - sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net); } /* Now send it, if there is anything to send :> */ if ((error = sctp_lowlevel_chunk_output(inp, @@ -8747,24 +8737,6 @@ no_data_fill: } SCTP_STAT_INCR_BY(sctps_senddata, bundle_at); sctp_clean_up_datalist(stcb, asoc, data_list, bundle_at, net); - if (SCTP_BASE_SYSCTL(sctp_early_fr)) { - if (net->flight_size < net->cwnd) { - /* start or restart it */ - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, inp, stcb, net, - SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_2); - } - SCTP_STAT_INCR(sctps_earlyfrstrout); - sctp_timer_start(SCTP_TIMER_TYPE_EARLYFR, inp, stcb, net); - } else { - /* stop it if its running */ - if (SCTP_OS_TIMER_PENDING(&net->fr_timer.timer)) { - SCTP_STAT_INCR(sctps_earlyfrstpout); - sctp_timer_stop(SCTP_TIMER_TYPE_EARLYFR, inp, stcb, net, - SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_3); - } - } - } } if (one_chunk) { break; @@ -8833,8 +8805,7 @@ sctp_queue_op_err(struct sctp_tcb *stcb, struct mbuf *op_err) chk->flags = 0; chk->asoc = &stcb->asoc; chk->data = op_err; - chk->whoTo = chk->asoc->primary_destination; - atomic_add_int(&chk->whoTo->ref_count, 1); + chk->whoTo = NULL; hdr = mtod(op_err, struct sctp_chunkhdr *); hdr->chunk_type = SCTP_OPERATION_ERROR; hdr->chunk_flags = 0; @@ -8929,7 +8900,7 @@ sctp_send_cookie_echo(struct mbuf *m, chk->flags = CHUNK_FLAGS_FRAGMENT_OK; chk->asoc = &stcb->asoc; chk->data = cookie; - chk->whoTo = chk->asoc->primary_destination; + chk->whoTo = net; atomic_add_int(&chk->whoTo->ref_count, 1); TAILQ_INSERT_HEAD(&chk->asoc->control_send_queue, chk, sctp_next); chk->asoc->ctrl_queue_cnt++; @@ -9039,10 +9010,10 @@ sctp_send_cookie_ack(struct sctp_tcb *stcb) chk->data = cookie_ack; if (chk->asoc->last_control_chunk_from != NULL) { chk->whoTo = chk->asoc->last_control_chunk_from; + atomic_add_int(&chk->whoTo->ref_count, 1); } else { - chk->whoTo = chk->asoc->primary_destination; + chk->whoTo = NULL; } - atomic_add_int(&chk->whoTo->ref_count, 1); hdr = mtod(cookie_ack, struct sctp_chunkhdr *); hdr->chunk_type = SCTP_COOKIE_ACK; hdr->chunk_flags = 0; @@ -9084,8 +9055,9 @@ sctp_send_shutdown_ack(struct sctp_tcb *stcb, struct sctp_nets *net) chk->asoc = &stcb->asoc; chk->data = m_shutdown_ack; chk->whoTo = net; - atomic_add_int(&net->ref_count, 1); - + if (chk->whoTo) { + atomic_add_int(&chk->whoTo->ref_count, 1); + } ack_cp = mtod(m_shutdown_ack, struct sctp_shutdown_ack_chunk *); ack_cp->ch.chunk_type = SCTP_SHUTDOWN_ACK; ack_cp->ch.chunk_flags = 0; @@ -9126,8 +9098,9 @@ sctp_send_shutdown(struct sctp_tcb *stcb, struct sctp_nets *net) chk->asoc = &stcb->asoc; chk->data = m_shutdown; chk->whoTo = net; - atomic_add_int(&net->ref_count, 1); - + if (chk->whoTo) { + atomic_add_int(&chk->whoTo->ref_count, 1); + } shutdown_cp = mtod(m_shutdown, struct sctp_shutdown_chunk *); shutdown_cp->ch.chunk_type = SCTP_SHUTDOWN; shutdown_cp->ch.chunk_flags = 0; @@ -9178,7 +9151,9 @@ sctp_send_asconf(struct sctp_tcb *stcb, struct sctp_nets *net, int addr_locked) chk->flags = CHUNK_FLAGS_FRAGMENT_OK; chk->asoc = &stcb->asoc; chk->whoTo = net; - atomic_add_int(&chk->whoTo->ref_count, 1); + if (chk->whoTo) { + atomic_add_int(&chk->whoTo->ref_count, 1); + } TAILQ_INSERT_TAIL(&chk->asoc->asconf_send_queue, chk, sctp_next); chk->asoc->ctrl_queue_cnt++; return; @@ -9208,17 +9183,27 @@ sctp_send_asconf_ack(struct sctp_tcb *stcb) net = sctp_find_alternate_net(stcb, stcb->asoc.last_control_chunk_from, 0); if (net == NULL) { /* no alternate */ - if (stcb->asoc.last_control_chunk_from == NULL) - net = stcb->asoc.primary_destination; - else + if (stcb->asoc.last_control_chunk_from == NULL) { + if (stcb->asoc.alternate) { + net = stcb->asoc.alternate; + } else { + net = stcb->asoc.primary_destination; + } + } else { net = stcb->asoc.last_control_chunk_from; + } } } else { /* normal case */ - if (stcb->asoc.last_control_chunk_from == NULL) - net = stcb->asoc.primary_destination; - else + if (stcb->asoc.last_control_chunk_from == NULL) { + if (stcb->asoc.alternate) { + net = stcb->asoc.alternate; + } else { + net = stcb->asoc.primary_destination; + } + } else { net = stcb->asoc.last_control_chunk_from; + } } latest_ack->last_sent_to = net; @@ -9256,6 +9241,9 @@ sctp_send_asconf_ack(struct sctp_tcb *stcb) chk->copy_by_ref = 0; chk->whoTo = net; + if (chk->whoTo) { + atomic_add_int(&chk->whoTo->ref_count, 1); + } chk->data = m_ack; chk->send_size = 0; /* Get size */ @@ -9267,7 +9255,6 @@ sctp_send_asconf_ack(struct sctp_tcb *stcb) chk->snd_count = 0; chk->flags |= CHUNK_FLAGS_FRAGMENT_OK; /* XXX */ chk->asoc = &stcb->asoc; - atomic_add_int(&chk->whoTo->ref_count, 1); TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue, chk, sctp_next); chk->asoc->ctrl_queue_cnt++; @@ -9797,7 +9784,11 @@ sctp_timer_validation(struct sctp_inpcb *inp, SCTP_TCB_LOCK_ASSERT(stcb); /* Gak, we did not have a timer somewhere */ SCTPDBG(SCTP_DEBUG_OUTPUT3, "Deadlock avoided starting timer on a dest at retran\n"); - sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, asoc->primary_destination); + if (asoc->alternate) { + sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, asoc->alternate); + } else { + sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, asoc->primary_destination); + } return (ret); } @@ -9945,8 +9936,7 @@ sctp_chunk_output(struct sctp_inpcb *inp, #endif /* Check for bad destinations, if they exist move chunks around. */ TAILQ_FOREACH(net, &asoc->nets, sctp_next) { - if ((net->dest_state & SCTP_ADDR_NOT_REACHABLE) == - SCTP_ADDR_NOT_REACHABLE) { + if (!(net->dest_state & SCTP_ADDR_REACHABLE)) { /*- * if possible move things off of this address we * still may send below due to the dormant state but @@ -9956,16 +9946,6 @@ sctp_chunk_output(struct sctp_inpcb *inp, */ if (net->ref_count > 1) sctp_move_chunks_from_net(stcb, net); - } else if ((asoc->sctp_cmt_on_off > 0) && - (asoc->sctp_cmt_pf > 0) && - ((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF)) { - /* - * JRS 5/14/07 - If CMT PF is on and the current - * destination is in PF state, move all queued data - * to an alternate desination. - */ - if (net->ref_count > 1) - sctp_move_chunks_from_net(stcb, net); } else { /*- * if ((asoc->sat_network) || (net->addr_is_local)) @@ -10123,10 +10103,9 @@ send_forward_tsn(struct sctp_tcb *stcb, chk->sent = SCTP_DATAGRAM_UNSENT; chk->snd_count = 0; /* Do we correct its output location? */ - if (chk->whoTo != asoc->primary_destination) { + if (chk->whoTo) { sctp_free_remote_addr(chk->whoTo); - chk->whoTo = asoc->primary_destination; - atomic_add_int(&chk->whoTo->ref_count, 1); + chk->whoTo = NULL; } goto sctp_fill_in_rest; } @@ -10150,8 +10129,6 @@ send_forward_tsn(struct sctp_tcb *stcb, SCTP_BUF_RESV_UF(chk->data, SCTP_MIN_OVERHEAD); chk->sent = SCTP_DATAGRAM_UNSENT; chk->snd_count = 0; - chk->whoTo = asoc->primary_destination; - atomic_add_int(&chk->whoTo->ref_count, 1); TAILQ_INSERT_TAIL(&asoc->control_send_queue, chk, sctp_next); asoc->ctrl_queue_cnt++; sctp_fill_in_rest: @@ -10346,8 +10323,10 @@ sctp_send_sack(struct sctp_tcb *stcb, int so_locked sctp_m_freem(a_chk->data); a_chk->data = NULL; } - sctp_free_remote_addr(a_chk->whoTo); - a_chk->whoTo = NULL; + if (a_chk->whoTo) { + sctp_free_remote_addr(a_chk->whoTo); + a_chk->whoTo = NULL; + } break; } } @@ -10379,13 +10358,13 @@ sctp_send_sack(struct sctp_tcb *stcb, int so_locked a_chk->whoTo = NULL; if ((asoc->numduptsns) || - (asoc->last_data_chunk_from->dest_state & SCTP_ADDR_NOT_REACHABLE)) { + (!(asoc->last_data_chunk_from->dest_state & SCTP_ADDR_REACHABLE))) { /*- * Ok, we have some duplicates or the destination for the * sack is unreachable, lets see if we can select an * alternate than asoc->last_data_chunk_from */ - if ((!(asoc->last_data_chunk_from->dest_state & SCTP_ADDR_NOT_REACHABLE)) && + if ((asoc->last_data_chunk_from->dest_state & SCTP_ADDR_REACHABLE) && (asoc->used_alt_onsack > asoc->numnets)) { /* We used an alt last time, don't this time */ a_chk->whoTo = NULL; @@ -10710,6 +10689,7 @@ sctp_send_abort_tcb(struct sctp_tcb *stcb, struct mbuf *operr, int so_locked int sz; uint32_t auth_offset = 0; struct sctp_auth_chunk *auth = NULL; + struct sctp_nets *net; /*- * Add an AUTH chunk, if chunk requires it and save the offset into @@ -10750,16 +10730,19 @@ sctp_send_abort_tcb(struct sctp_tcb *stcb, struct mbuf *operr, int so_locked /* Put AUTH chunk at the front of the chain */ SCTP_BUF_NEXT(m_end) = m_abort; } - + if (stcb->asoc.alternate) { + net = stcb->asoc.alternate; + } else { + net = stcb->asoc.primary_destination; + } /* fill in the ABORT chunk */ abort = mtod(m_abort, struct sctp_abort_chunk *); abort->ch.chunk_type = SCTP_ABORT_ASSOCIATION; abort->ch.chunk_flags = 0; abort->ch.chunk_length = htons(sizeof(*abort) + sz); - (void)sctp_lowlevel_chunk_output(stcb->sctp_ep, stcb, - stcb->asoc.primary_destination, - (struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr, + (void)sctp_lowlevel_chunk_output(stcb->sctp_ep, stcb, net, + (struct sockaddr *)&net->ro._l_addr, m_out, auth_offset, auth, stcb->asoc.authinfo.active_keyid, 1, 0, NULL, 0, stcb->sctp_ep->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag), stcb->asoc.primary_destination->port, so_locked, NULL, NULL); @@ -11030,120 +11013,22 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh, } -static struct sctp_nets * -sctp_select_hb_destination(struct sctp_tcb *stcb, struct timeval *now) -{ - struct sctp_nets *net, *hnet; - int ms_goneby, highest_ms, state_overide = 0; - - (void)SCTP_GETTIME_TIMEVAL(now); - highest_ms = 0; - hnet = NULL; - SCTP_TCB_LOCK_ASSERT(stcb); - TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { - if ( - ((net->dest_state & SCTP_ADDR_NOHB) && ((net->dest_state & SCTP_ADDR_UNCONFIRMED) == 0)) || - (net->dest_state & SCTP_ADDR_OUT_OF_SCOPE) - ) { - /* - * Skip this guy from consideration if HB is off AND - * its confirmed - */ - continue; - } - if (sctp_destination_is_reachable(stcb, (struct sockaddr *)&net->ro._l_addr) == 0) { - /* skip this dest net from consideration */ - continue; - } - if (net->last_sent_time.tv_sec) { - /* Sent to so we subtract */ - ms_goneby = (now->tv_sec - net->last_sent_time.tv_sec) * 1000; - } else - /* Never been sent to */ - ms_goneby = 0x7fffffff; - /*- - * When the address state is unconfirmed but still - * considered reachable, we HB at a higher rate. Once it - * goes confirmed OR reaches the "unreachable" state, thenw - * we cut it back to HB at a more normal pace. - */ - if ((net->dest_state & (SCTP_ADDR_UNCONFIRMED | SCTP_ADDR_NOT_REACHABLE)) == SCTP_ADDR_UNCONFIRMED) { - state_overide = 1; - } else { - state_overide = 0; - } - - if ((((unsigned int)ms_goneby >= net->RTO) || (state_overide)) && - (ms_goneby > highest_ms)) { - highest_ms = ms_goneby; - hnet = net; - } - } - if (hnet && - ((hnet->dest_state & (SCTP_ADDR_UNCONFIRMED | SCTP_ADDR_NOT_REACHABLE)) == SCTP_ADDR_UNCONFIRMED)) { - state_overide = 1; - } else { - state_overide = 0; - } - - if (hnet && highest_ms && (((unsigned int)highest_ms >= hnet->RTO) || state_overide)) { - /*- - * Found the one with longest delay bounds OR it is - * unconfirmed and still not marked unreachable. - */ - SCTPDBG(SCTP_DEBUG_OUTPUT4, "net:%p is the hb winner -", hnet); -#ifdef SCTP_DEBUG - if (hnet) { - SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT4, - (struct sockaddr *)&hnet->ro._l_addr); - } else { - SCTPDBG(SCTP_DEBUG_OUTPUT4, " none\n"); - } -#endif - /* update the timer now */ - hnet->last_sent_time = *now; - return (hnet); - } - /* Nothing to HB */ - return (NULL); -} - -int -sctp_send_hb(struct sctp_tcb *stcb, int user_req, struct sctp_nets *u_net, int so_locked +void +sctp_send_hb(struct sctp_tcb *stcb, struct sctp_nets *net, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { struct sctp_tmit_chunk *chk; - struct sctp_nets *net; struct sctp_heartbeat_chunk *hb; struct timeval now; SCTP_TCB_LOCK_ASSERT(stcb); - if (user_req == 0) { - net = sctp_select_hb_destination(stcb, &now); - if (net == NULL) { - /*- - * All our busy none to send to, just start the - * timer again. - */ - if (stcb->asoc.state == 0) { - return (0); - } - sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, - stcb->sctp_ep, - stcb, - net); - return (0); - } - } else { - net = u_net; - if (net == NULL) { - return (0); - } - (void)SCTP_GETTIME_TIMEVAL(&now); + if (net == NULL) { + return; } + (void)SCTP_GETTIME_TIMEVAL(&now); switch (net->ro._l_addr.sa.sa_family) { #ifdef INET case AF_INET: @@ -11154,12 +11039,12 @@ sctp_send_hb(struct sctp_tcb *stcb, int user_req, struct sctp_nets *u_net, int s break; #endif default: - return (0); + return; } sctp_alloc_a_chunk(stcb, chk); if (chk == NULL) { SCTPDBG(SCTP_DEBUG_OUTPUT4, "Gak, can't get a chunk for hb\n"); - return (0); + return; } chk->copy_by_ref = 0; chk->rec.chunk_id.id = SCTP_HEARTBEAT_REQUEST; @@ -11170,7 +11055,7 @@ sctp_send_hb(struct sctp_tcb *stcb, int user_req, struct sctp_nets *u_net, int s chk->data = sctp_get_mbuf_for_msg(chk->send_size, 0, M_DONTWAIT, 1, MT_HEADER); if (chk->data == NULL) { sctp_free_a_chunk(stcb, chk, so_locked); - return (0); + return; } SCTP_BUF_RESV_UF(chk->data, SCTP_MIN_OVERHEAD); SCTP_BUF_LEN(chk->data) = chk->send_size; @@ -11191,7 +11076,6 @@ sctp_send_hb(struct sctp_tcb *stcb, int user_req, struct sctp_nets *u_net, int s hb->heartbeat.hb_info.time_value_1 = now.tv_sec; hb->heartbeat.hb_info.time_value_2 = now.tv_usec; /* Did our user request this one, put it in */ - hb->heartbeat.hb_info.user_req = user_req; hb->heartbeat.hb_info.addr_family = net->ro._l_addr.sa.sa_family; hb->heartbeat.hb_info.addr_len = net->ro._l_addr.sa.sa_len; if (net->dest_state & SCTP_ADDR_UNCONFIRMED) { @@ -11221,57 +11105,14 @@ sctp_send_hb(struct sctp_tcb *stcb, int user_req, struct sctp_nets *u_net, int s break; #endif default: - return (0); + return; break; } - - /* - * JRS 5/14/07 - In CMT PF, the T3 timer is used to track - * PF-heartbeats. Because of this, threshold management is done by - * the t3 timer handler, and does not need to be done upon the send - * of a PF-heartbeat. If CMT PF is on and the destination to which a - * heartbeat is being sent is in PF state, do NOT do threshold - * management. - */ - if ((stcb->asoc.sctp_cmt_pf == 0) || - ((net->dest_state & SCTP_ADDR_PF) != SCTP_ADDR_PF)) { - /* ok we have a destination that needs a beat */ - /* lets do the theshold management Qiaobing style */ - if (sctp_threshold_management(stcb->sctp_ep, stcb, net, - stcb->asoc.max_send_times)) { - /*- - * we have lost the association, in a way this is - * quite bad since we really are one less time since - * we really did not send yet. This is the down side - * to the Q's style as defined in the RFC and not my - * alternate style defined in the RFC. - */ - if (chk->data != NULL) { - sctp_m_freem(chk->data); - chk->data = NULL; - } - /* - * Here we do NOT use the macro since the - * association is now gone. - */ - if (chk->whoTo) { - sctp_free_remote_addr(chk->whoTo); - chk->whoTo = NULL; - } - sctp_free_a_chunk((struct sctp_tcb *)NULL, chk, so_locked); - return (-1); - } - } net->hb_responded = 0; TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, chk, sctp_next); stcb->asoc.ctrl_queue_cnt++; SCTP_STAT_INCR(sctps_sendheartbeat); - /*- - * Call directly med level routine to put out the chunk. It will - * always tumble out control chunks aka HB but it may even tumble - * out data too. - */ - return (1); + return; } void @@ -11282,6 +11123,9 @@ sctp_send_ecn_echo(struct sctp_tcb *stcb, struct sctp_nets *net, struct sctp_ecne_chunk *ecne; struct sctp_tmit_chunk *chk; + if (net == NULL) { + return; + } asoc = &stcb->asoc; SCTP_TCB_LOCK_ASSERT(stcb); TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) { @@ -11323,6 +11167,7 @@ sctp_send_ecn_echo(struct sctp_tcb *stcb, struct sctp_nets *net, chk->snd_count = 0; chk->whoTo = net; atomic_add_int(&chk->whoTo->ref_count, 1); + stcb->asoc.ecn_echo_cnt_onq++; ecne = mtod(chk->data, struct sctp_ecne_chunk *); ecne->ch.chunk_type = SCTP_ECN_ECHO; @@ -11477,10 +11322,10 @@ jump_out: if (net) { /* we should hit here */ chk->whoTo = net; + atomic_add_int(&chk->whoTo->ref_count, 1); } else { - chk->whoTo = asoc->primary_destination; + chk->whoTo = NULL; } - atomic_add_int(&chk->whoTo->ref_count, 1); chk->rec.chunk_id.id = SCTP_PACKET_DROPPED; chk->rec.chunk_id.can_take_data = 1; drp->ch.chunk_type = SCTP_PACKET_DROPPED; @@ -11518,8 +11363,9 @@ sctp_send_cwr(struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t high_tsn, u asoc = &stcb->asoc; SCTP_TCB_LOCK_ASSERT(stcb); - - + if (net == NULL) { + return; + } TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) { if ((chk->rec.chunk_id.id == SCTP_ECN_CWR) && (net == chk->whoTo)) { /* @@ -11849,9 +11695,12 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb, /* setup chunk parameters */ chk->sent = SCTP_DATAGRAM_UNSENT; chk->snd_count = 0; - chk->whoTo = asoc->primary_destination; + if (stcb->asoc.alternate) { + chk->whoTo = stcb->asoc.alternate; + } else { + chk->whoTo = stcb->asoc.primary_destination; + } atomic_add_int(&chk->whoTo->ref_count, 1); - ch = mtod(chk->data, struct sctp_chunkhdr *); ch->chunk_type = SCTP_STREAM_RESET; ch->chunk_flags = 0; @@ -12910,7 +12759,11 @@ sctp_lower_sosend(struct socket *so, goto out_unlocked; } } else { - net = stcb->asoc.primary_destination; + if (stcb->asoc.alternate) { + net = stcb->asoc.alternate; + } else { + net = stcb->asoc.primary_destination; + } } atomic_add_int(&stcb->total_sends, 1); /* Keep the stcb from being freed under our feet */ @@ -13579,15 +13432,22 @@ dataless_eof: if ((SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) && (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_RECEIVED) && (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT)) { + struct sctp_nets *netp; + + if (stcb->asoc.alternate) { + netp = stcb->asoc.alternate; + } else { + netp = stcb->asoc.primary_destination; + } /* only send SHUTDOWN the first time through */ - sctp_send_shutdown(stcb, stcb->asoc.primary_destination); + sctp_send_shutdown(stcb, netp); if (SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); } SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT); SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, - asoc->primary_destination); + netp); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb, asoc->primary_destination); } diff --git a/sys/netinet/sctp_output.h b/sys/netinet/sctp_output.h index 5cbadf34909d..68a08c11c959 100644 --- a/sys/netinet/sctp_output.h +++ b/sys/netinet/sctp_output.h @@ -155,7 +155,7 @@ void send_forward_tsn(struct sctp_tcb *, struct sctp_association *); void sctp_send_sack(struct sctp_tcb *, int); -int sctp_send_hb(struct sctp_tcb *, int, struct sctp_nets *, int); +void sctp_send_hb(struct sctp_tcb *, struct sctp_nets *, int); void sctp_send_ecn_echo(struct sctp_tcb *, struct sctp_nets *, uint32_t); diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index 70593655fdc6..728fa7c69745 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -1096,8 +1096,15 @@ sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to) continue; } LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { - if (sctp_is_addr_restricted(stcb, sctp_ifa)) + if (sctp_is_addr_restricted(stcb, sctp_ifa) && + (!sctp_is_addr_pending(stcb, sctp_ifa))) { + /* + * We allow pending addresses, where + * we have sent an asconf-add to be + * considered valid. + */ continue; + } switch (sctp_ifa->address.sa.sa_family) { #ifdef INET case AF_INET: @@ -1155,7 +1162,13 @@ sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to) struct sctp_laddr *laddr; LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list, sctp_nxt_addr) { - if (sctp_is_addr_restricted(stcb, laddr->ifa)) { + if (sctp_is_addr_restricted(stcb, laddr->ifa) && + (!sctp_is_addr_pending(stcb, laddr->ifa))) { + /* + * We allow pending addresses, where we have + * sent an asconf-add to be considered + * valid. + */ continue; } if (laddr->ifa->address.sa.sa_family != to->sa_family) { @@ -2614,6 +2627,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id) m->max_init_times = SCTP_BASE_SYSCTL(sctp_init_rtx_max_default); m->max_send_times = SCTP_BASE_SYSCTL(sctp_assoc_rtx_max_default); m->def_net_failure = SCTP_BASE_SYSCTL(sctp_path_rtx_max_default); + m->def_net_pf_threshold = SCTP_BASE_SYSCTL(sctp_path_pf_threshold); m->sctp_sws_sender = SCTP_SWS_SENDER_DEF; m->sctp_sws_receiver = SCTP_SWS_RECEIVER_DEF; m->max_burst = SCTP_BASE_SYSCTL(sctp_max_burst_default); @@ -2768,7 +2782,6 @@ sctp_move_pcb_and_assoc(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp, * all of them. */ - stcb->asoc.hb_timer.ep = (void *)new_inp; stcb->asoc.dack_timer.ep = (void *)new_inp; stcb->asoc.asconf_timer.ep = (void *)new_inp; stcb->asoc.strreset_timer.ep = (void *)new_inp; @@ -2780,7 +2793,6 @@ sctp_move_pcb_and_assoc(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp, TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { net->pmtu_timer.ep = (void *)new_inp; net->rxt_timer.ep = (void *)new_inp; - net->fr_timer.ep = (void *)new_inp; } SCTP_INP_WUNLOCK(new_inp); SCTP_INP_WUNLOCK(old_inp); @@ -3452,11 +3464,18 @@ sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from) } if ((SCTP_GET_STATE(&asoc->asoc) != SCTP_STATE_SHUTDOWN_SENT) && (SCTP_GET_STATE(&asoc->asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT)) { + struct sctp_nets *netp; + + if (asoc->asoc.alternate) { + netp = asoc->asoc.alternate; + } else { + netp = asoc->asoc.primary_destination; + } /* * there is nothing queued to send, * so I send shutdown */ - sctp_send_shutdown(asoc, asoc->asoc.primary_destination); + sctp_send_shutdown(asoc, netp); if ((SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); @@ -3464,7 +3483,7 @@ sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from) SCTP_SET_STATE(&asoc->asoc, SCTP_STATE_SHUTDOWN_SENT); SCTP_CLEAR_SUBSTATE(&asoc->asoc, SCTP_STATE_SHUTDOWN_PENDING); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, asoc->sctp_ep, asoc, - asoc->asoc.primary_destination); + netp); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, asoc->sctp_ep, asoc, asoc->asoc.primary_destination); sctp_chunk_output(inp, asoc, SCTP_OUTPUT_FROM_SHUT_TMR, SCTP_SO_LOCKED); @@ -3808,7 +3827,7 @@ sctp_is_address_on_local_host(struct sockaddr *addr, uint32_t vrf_id) */ int sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, - int set_scope, int from) + struct sctp_nets **netp, int set_scope, int from) { /* * The following is redundant to the same lines in the @@ -3942,7 +3961,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, return (-1); } SCTP_INCR_RADDR_COUNT(); - bzero(net, sizeof(*net)); + bzero(net, sizeof(struct sctp_nets)); (void)SCTP_GETTIME_TIMEVAL(&net->start_time); memcpy(&net->ro._l_addr, newaddr, newaddr->sa_len); switch (newaddr->sa_family) { @@ -3968,6 +3987,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, addr_inscope = 1; } net->failure_threshold = stcb->asoc.def_net_failure; + net->pf_threshold = stcb->asoc.def_net_pf_threshold; if (addr_inscope == 0) { net->dest_state = (SCTP_ADDR_REACHABLE | SCTP_ADDR_OUT_OF_SCOPE); @@ -4003,10 +4023,16 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, if (newaddr->sa_family == AF_INET6) net->tos_flowlabel = stcb->asoc.default_flowlabel; #endif + if (sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_DONOT_HEARTBEAT)) { + net->dest_state |= SCTP_ADDR_NOHB; + } else { + net->dest_state &= ~SCTP_ADDR_NOHB; + } + net->heart_beat_delay = stcb->asoc.heart_beat_delay; /* Init the timer structure */ SCTP_OS_TIMER_INIT(&net->rxt_timer.timer); - SCTP_OS_TIMER_INIT(&net->fr_timer.timer); SCTP_OS_TIMER_INIT(&net->pmtu_timer.timer); + SCTP_OS_TIMER_INIT(&net->hb_timer.timer); /* Now generate a route for this guy */ #ifdef INET6 @@ -4153,8 +4179,6 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, /* No route to current primary adopt new primary */ stcb->asoc.primary_destination = net; } - sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, - net); /* Validate primary is first */ net = TAILQ_FIRST(&stcb->asoc.nets); if ((net != stcb->asoc.primary_destination) && @@ -4169,6 +4193,9 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, TAILQ_INSERT_HEAD(&stcb->asoc.nets, stcb->asoc.primary_destination, sctp_next); } + if (netp) { + *netp = net; + } return (0); } @@ -4393,7 +4420,7 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr, LIST_INSERT_HEAD(head, stcb, sctp_asocs); SCTP_INP_INFO_WUNLOCK(); - if ((err = sctp_add_remote_addr(stcb, firstaddr, SCTP_DO_SETSCOPE, SCTP_ALLOC_ASOC))) { + if ((err = sctp_add_remote_addr(stcb, firstaddr, NULL, SCTP_DO_SETSCOPE, SCTP_ALLOC_ASOC))) { /* failure.. memory error? */ if (asoc->strmout) { SCTP_FREE(asoc->strmout, SCTP_M_STRMO); @@ -4418,7 +4445,6 @@ sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr, return (NULL); } /* Init all the timers */ - SCTP_OS_TIMER_INIT(&asoc->hb_timer.timer); SCTP_OS_TIMER_INIT(&asoc->dack_timer.timer); SCTP_OS_TIMER_INIT(&asoc->strreset_timer.timer); SCTP_OS_TIMER_INIT(&asoc->asconf_timer.timer); @@ -4488,6 +4514,10 @@ out: /* Clear net */ asoc->last_control_chunk_from = NULL; } + if (net == stcb->asoc.alternate) { + sctp_free_remote_addr(stcb->asoc.alternate); + stcb->asoc.alternate = NULL; + } sctp_free_remote_addr(net); } @@ -4699,6 +4729,10 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre /* there is no asoc, really TSNH :-0 */ return (1); } + if (stcb->asoc.alternate) { + sctp_free_remote_addr(stcb->asoc.alternate); + stcb->asoc.alternate = NULL; + } /* TEMP CODE */ if (stcb->freed_from_where == 0) { /* Only record the first place free happened from */ @@ -4737,8 +4771,6 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre } } /* now clean up any other timers */ - (void)SCTP_OS_TIMER_STOP(&asoc->hb_timer.timer); - asoc->hb_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&asoc->dack_timer.timer); asoc->dack_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&asoc->strreset_timer.timer); @@ -4762,12 +4794,12 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre (void)SCTP_OS_TIMER_STOP(&asoc->delete_prim_timer.timer); asoc->delete_prim_timer.self = NULL; TAILQ_FOREACH(net, &asoc->nets, sctp_next) { - (void)SCTP_OS_TIMER_STOP(&net->fr_timer.timer); - net->fr_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&net->rxt_timer.timer); net->rxt_timer.self = NULL; (void)SCTP_OS_TIMER_STOP(&net->pmtu_timer.timer); net->pmtu_timer.self = NULL; + (void)SCTP_OS_TIMER_STOP(&net->hb_timer.timer); + net->hb_timer.self = NULL; } /* Now the read queue needs to be cleaned up (only once) */ if ((stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0) { @@ -4935,7 +4967,6 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre * Now restop the timers to be sure this is paranoia at is finest! */ (void)SCTP_OS_TIMER_STOP(&asoc->strreset_timer.timer); - (void)SCTP_OS_TIMER_STOP(&asoc->hb_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->dack_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->strreset_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->asconf_timer.timer); @@ -4943,9 +4974,9 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre (void)SCTP_OS_TIMER_STOP(&asoc->autoclose_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->delayed_event_timer.timer); TAILQ_FOREACH(net, &asoc->nets, sctp_next) { - (void)SCTP_OS_TIMER_STOP(&net->fr_timer.timer); (void)SCTP_OS_TIMER_STOP(&net->rxt_timer.timer); (void)SCTP_OS_TIMER_STOP(&net->pmtu_timer.timer); + (void)SCTP_OS_TIMER_STOP(&net->hb_timer.timer); } asoc->strreset_timer.type = SCTP_TIMER_TYPE_NONE; @@ -6189,7 +6220,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, #ifdef INET case AF_INET: if (stcb->asoc.ipv4_addr_legal) { - if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_2)) { + if (sctp_add_remote_addr(stcb, sa, NULL, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_2)) { return (-1); } } @@ -6198,7 +6229,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, #ifdef INET6 case AF_INET6: if (stcb->asoc.ipv6_addr_legal) { - if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_3)) { + if (sctp_add_remote_addr(stcb, sa, NULL, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_3)) { return (-2); } } @@ -6289,7 +6320,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, /* the assoc was freed? */ return (-7); } - if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_4)) { + if (sctp_add_remote_addr(stcb, sa, NULL, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_4)) { return (-8); } } else if (stcb_tmp == stcb) { @@ -6376,7 +6407,7 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m, * we must add the address, no scope * set */ - if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_5)) { + if (sctp_add_remote_addr(stcb, sa, NULL, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_5)) { return (-17); } } else if (stcb_tmp == stcb) { @@ -6748,7 +6779,10 @@ sctp_set_primary_addr(struct sctp_tcb *stcb, struct sockaddr *sa, return (0); } stcb->asoc.primary_destination = net; - net->dest_state &= ~SCTP_ADDR_WAS_PRIMARY; + if (!(net->dest_state & SCTP_ADDR_PF) && (stcb->asoc.alternate)) { + sctp_free_remote_addr(stcb->asoc.alternate); + stcb->asoc.alternate = NULL; + } net = TAILQ_FIRST(&stcb->asoc.nets); if (net != stcb->asoc.primary_destination) { /* diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h index 783968191af8..a7f5580e26ea 100644 --- a/sys/netinet/sctp_pcb.h +++ b/sys/netinet/sctp_pcb.h @@ -293,6 +293,8 @@ struct sctp_pcb { uint16_t def_net_failure; + uint16_t def_net_pf_threshold; + /* number of streams to pre-open on a association */ uint16_t pre_open_stream_count; uint16_t max_open_streams_intome; @@ -582,7 +584,7 @@ void sctp_remove_laddr(struct sctp_laddr *); void sctp_del_local_addr_ep(struct sctp_inpcb *, struct sctp_ifa *); -int sctp_add_remote_addr(struct sctp_tcb *, struct sockaddr *, int, int); +int sctp_add_remote_addr(struct sctp_tcb *, struct sockaddr *, struct sctp_nets **, int, int); void sctp_remove_net(struct sctp_tcb *, struct sctp_nets *); diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h index 0f9bcaf5268b..df3898f064d4 100644 --- a/sys/netinet/sctp_structs.h +++ b/sys/netinet/sctp_structs.h @@ -251,6 +251,7 @@ struct sctp_nets { * structure shared by all. */ struct sctp_timer pmtu_timer; + struct sctp_timer hb_timer; /* * The following two in combination equate to a route entry for v6 @@ -273,7 +274,6 @@ struct sctp_nets { /* This is used for SHUTDOWN/SHUTDOWN-ACK/SEND or INIT timers */ struct sctp_timer rxt_timer; - struct sctp_timer fr_timer; /* for early fr */ /* last time in seconds I sent to it */ struct timeval last_sent_time; @@ -327,12 +327,15 @@ struct sctp_nets { uint32_t marked_retrans;/* number or DATA chunks marked for timer * based retransmissions */ uint32_t marked_fastretrans; + uint32_t heart_beat_delay; /* Heart Beat delay in ms */ /* if this guy is ok or not ... status */ uint16_t dest_state; - /* number of transmit failures to down this guy */ + /* number of timeouts to consider the destination unreachable */ uint16_t failure_threshold; - /* error stats on destination */ + /* number of timeouts to consider the destination potentially failed */ + uint16_t pf_threshold; + /* error stats on the destination */ uint16_t error_count; /* UDP port number in case of UDP tunneling */ uint16_t port; @@ -661,6 +664,7 @@ struct sctp_cc_functions { void (*sctp_cwnd_update_after_sack) (struct sctp_tcb *stcb, struct sctp_association *asoc, int accum_moved, int reneged_all, int will_exit); + void (*sctp_cwnd_update_exit_pf) (struct sctp_tcb *stcb, struct sctp_nets *net); void (*sctp_cwnd_update_after_fr) (struct sctp_tcb *stcb, struct sctp_association *asoc); void (*sctp_cwnd_update_after_timeout) (struct sctp_tcb *stcb, @@ -672,8 +676,6 @@ struct sctp_cc_functions { uint32_t * bottle_bw, uint32_t * on_queue); void (*sctp_cwnd_update_after_output) (struct sctp_tcb *stcb, struct sctp_nets *net, int burst_limit); - void (*sctp_cwnd_update_after_fr_timer) (struct sctp_inpcb *inp, - struct sctp_tcb *stcb, struct sctp_nets *net); void (*sctp_cwnd_update_packet_transmitted) (struct sctp_tcb *stcb, struct sctp_nets *net); void (*sctp_cwnd_update_tsn_acknowledged) (struct sctp_nets *net, @@ -753,7 +755,6 @@ struct sctp_association { struct sctp_nonpad_sndrcvinfo def_send; /* 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 strreset_timer; /* stream reset */ @@ -828,6 +829,7 @@ struct sctp_association { uint8_t *mapping_array; /* primary destination to use */ struct sctp_nets *primary_destination; + struct sctp_nets *alternate; /* If primary is down or PF */ /* For CMT */ struct sctp_nets *last_net_cmt_send_started; /* last place I got a data chunk from */ @@ -1023,8 +1025,8 @@ struct sctp_association { unsigned int size_on_all_streams; unsigned int cnt_on_all_streams; - /* Heart Beat delay in ticks */ - unsigned int heart_beat_delay; + /* Heart Beat delay in ms */ + uint32_t heart_beat_delay; /* autoclose */ unsigned int sctp_autoclose_ticks; @@ -1094,6 +1096,8 @@ struct sctp_association { uint16_t def_net_failure; + uint16_t def_net_pf_threshold; + /* * lock flag: 0 is ok to send, 1+ (duals as a retran count) is * awaiting ACK @@ -1133,7 +1137,6 @@ struct sctp_association { uint8_t last_flags_delivered; uint8_t hb_ect_randombit; 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 */ diff --git a/sys/netinet/sctp_sysctl.c b/sys/netinet/sctp_sysctl.c index bb7b6e265d22..25d75f88e084 100644 --- a/sys/netinet/sctp_sysctl.c +++ b/sys/netinet/sctp_sysctl.c @@ -83,16 +83,14 @@ sctp_init_sysctls() SCTP_BASE_SYSCTL(sctp_init_rtx_max_default) = SCTPCTL_INIT_RTX_MAX_DEFAULT; SCTP_BASE_SYSCTL(sctp_assoc_rtx_max_default) = SCTPCTL_ASSOC_RTX_MAX_DEFAULT; SCTP_BASE_SYSCTL(sctp_path_rtx_max_default) = SCTPCTL_PATH_RTX_MAX_DEFAULT; + SCTP_BASE_SYSCTL(sctp_path_pf_threshold) = SCTPCTL_PATH_PF_THRESHOLD_DEFAULT; SCTP_BASE_SYSCTL(sctp_add_more_threshold) = SCTPCTL_ADD_MORE_ON_OUTPUT_DEFAULT; SCTP_BASE_SYSCTL(sctp_nr_outgoing_streams_default) = SCTPCTL_OUTGOING_STREAMS_DEFAULT; SCTP_BASE_SYSCTL(sctp_cmt_on_off) = SCTPCTL_CMT_ON_OFF_DEFAULT; /* EY */ SCTP_BASE_SYSCTL(sctp_nr_sack_on_off) = SCTPCTL_NR_SACK_ON_OFF_DEFAULT; SCTP_BASE_SYSCTL(sctp_cmt_use_dac) = SCTPCTL_CMT_USE_DAC_DEFAULT; - SCTP_BASE_SYSCTL(sctp_cmt_pf) = SCTPCTL_CMT_PF_DEFAULT; SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst) = SCTPCTL_CWND_MAXBURST_DEFAULT; - SCTP_BASE_SYSCTL(sctp_early_fr) = SCTPCTL_EARLY_FAST_RETRAN_DEFAULT; - SCTP_BASE_SYSCTL(sctp_early_fr_msec) = SCTPCTL_EARLY_FAST_RETRAN_MSEC_DEFAULT; SCTP_BASE_SYSCTL(sctp_asconf_auth_nochk) = SCTPCTL_ASCONF_AUTH_NOCHK_DEFAULT; SCTP_BASE_SYSCTL(sctp_auth_disable) = SCTPCTL_AUTH_DISABLE_DEFAULT; SCTP_BASE_SYSCTL(sctp_nat_friendly) = SCTPCTL_NAT_FRIENDLY_DEFAULT; @@ -494,6 +492,7 @@ sctp_assoclist(SYSCTL_HANDLER_ARGS) xraddr.active = ((net->dest_state & SCTP_ADDR_REACHABLE) == SCTP_ADDR_REACHABLE); xraddr.confirmed = ((net->dest_state & SCTP_ADDR_UNCONFIRMED) == 0); xraddr.heartbeat_enabled = ((net->dest_state & SCTP_ADDR_NOHB) == 0); + xraddr.potentially_failed = ((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF); xraddr.rto = net->RTO; xraddr.max_path_rtx = net->failure_threshold; xraddr.rtx = net->marked_retrans; @@ -502,6 +501,7 @@ sctp_assoclist(SYSCTL_HANDLER_ARGS) xraddr.flight_size = net->flight_size; xraddr.mtu = net->mtu; xraddr.rtt = net->rtt / 1000; + xraddr.heartbeat_interval = net->heart_beat_delay; xraddr.start_time.tv_sec = (uint32_t) net->start_time.tv_sec; xraddr.start_time.tv_usec = (uint32_t) net->start_time.tv_usec; SCTP_INP_RUNLOCK(inp); @@ -633,16 +633,14 @@ sysctl_sctp_check(SYSCTL_HANDLER_ARGS) RANGECHK(SCTP_BASE_SYSCTL(sctp_init_rtx_max_default), SCTPCTL_INIT_RTX_MAX_MIN, SCTPCTL_INIT_RTX_MAX_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_assoc_rtx_max_default), SCTPCTL_ASSOC_RTX_MAX_MIN, SCTPCTL_ASSOC_RTX_MAX_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_path_rtx_max_default), SCTPCTL_PATH_RTX_MAX_MIN, SCTPCTL_PATH_RTX_MAX_MAX); + RANGECHK(SCTP_BASE_SYSCTL(sctp_path_pf_threshold), SCTPCTL_PATH_PF_THRESHOLD_MIN, SCTPCTL_PATH_PF_THRESHOLD_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_add_more_threshold), SCTPCTL_ADD_MORE_ON_OUTPUT_MIN, SCTPCTL_ADD_MORE_ON_OUTPUT_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_nr_outgoing_streams_default), SCTPCTL_OUTGOING_STREAMS_MIN, SCTPCTL_OUTGOING_STREAMS_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_cmt_on_off), SCTPCTL_CMT_ON_OFF_MIN, SCTPCTL_CMT_ON_OFF_MAX); /* EY */ RANGECHK(SCTP_BASE_SYSCTL(sctp_nr_sack_on_off), SCTPCTL_NR_SACK_ON_OFF_MIN, SCTPCTL_NR_SACK_ON_OFF_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_cmt_use_dac), SCTPCTL_CMT_USE_DAC_MIN, SCTPCTL_CMT_USE_DAC_MAX); - RANGECHK(SCTP_BASE_SYSCTL(sctp_cmt_pf), SCTPCTL_CMT_PF_MIN, SCTPCTL_CMT_PF_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst), SCTPCTL_CWND_MAXBURST_MIN, SCTPCTL_CWND_MAXBURST_MAX); - RANGECHK(SCTP_BASE_SYSCTL(sctp_early_fr), SCTPCTL_EARLY_FAST_RETRAN_MIN, SCTPCTL_EARLY_FAST_RETRAN_MAX); - RANGECHK(SCTP_BASE_SYSCTL(sctp_early_fr_msec), SCTPCTL_EARLY_FAST_RETRAN_MSEC_MIN, SCTPCTL_EARLY_FAST_RETRAN_MSEC_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_asconf_auth_nochk), SCTPCTL_ASCONF_AUTH_NOCHK_MIN, SCTPCTL_ASCONF_AUTH_NOCHK_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_auth_disable), SCTPCTL_AUTH_DISABLE_MIN, SCTPCTL_AUTH_DISABLE_MAX); RANGECHK(SCTP_BASE_SYSCTL(sctp_nat_friendly), SCTPCTL_NAT_FRIENDLY_MIN, SCTPCTL_NAT_FRIENDLY_MAX); @@ -793,17 +791,6 @@ sysctl_stat_get(SYSCTL_HANDLER_ARGS) sb.sctps_timoautoclose += sarry->sctps_timoautoclose; sb.sctps_timoassockill += sarry->sctps_timoassockill; sb.sctps_timoinpkill += sarry->sctps_timoinpkill; - sb.sctps_earlyfrstart += sarry->sctps_earlyfrstart; - sb.sctps_earlyfrstop += sarry->sctps_earlyfrstop; - sb.sctps_earlyfrmrkretrans += sarry->sctps_earlyfrmrkretrans; - sb.sctps_earlyfrstpout += sarry->sctps_earlyfrstpout; - sb.sctps_earlyfrstpidsck1 += sarry->sctps_earlyfrstpidsck1; - sb.sctps_earlyfrstpidsck2 += sarry->sctps_earlyfrstpidsck2; - sb.sctps_earlyfrstpidsck3 += sarry->sctps_earlyfrstpidsck3; - sb.sctps_earlyfrstpidsck4 += sarry->sctps_earlyfrstpidsck4; - sb.sctps_earlyfrstrid += sarry->sctps_earlyfrstrid; - sb.sctps_earlyfrstrout += sarry->sctps_earlyfrstrout; - sb.sctps_earlyfrstrtmr += sarry->sctps_earlyfrstrtmr; sb.sctps_hdrops += sarry->sctps_hdrops; sb.sctps_badsum += sarry->sctps_badsum; sb.sctps_noport += sarry->sctps_noport; @@ -994,6 +981,10 @@ SYSCTL_VNET_PROC(_net_inet_sctp, OID_AUTO, path_rtx_max, CTLTYPE_UINT | CTLFLAG_ &SCTP_BASE_SYSCTL(sctp_path_rtx_max_default), 0, sysctl_sctp_check, "IU", SCTPCTL_PATH_RTX_MAX_DESC); +SYSCTL_VNET_PROC(_net_inet_sctp, OID_AUTO, path_pf_threshold, CTLTYPE_UINT | CTLFLAG_RW, + &SCTP_BASE_SYSCTL(sctp_path_pf_threshold), 0, sysctl_sctp_check, "IU", + SCTPCTL_PATH_PF_THRESHOLD_DESC); + SYSCTL_VNET_PROC(_net_inet_sctp, OID_AUTO, add_more_on_output, CTLTYPE_UINT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_add_more_threshold), 0, sysctl_sctp_check, "IU", SCTPCTL_ADD_MORE_ON_OUTPUT_DESC); @@ -1014,22 +1005,10 @@ SYSCTL_VNET_PROC(_net_inet_sctp, OID_AUTO, cmt_use_dac, CTLTYPE_UINT | CTLFLAG_R &SCTP_BASE_SYSCTL(sctp_cmt_use_dac), 0, sysctl_sctp_check, "IU", SCTPCTL_CMT_USE_DAC_DESC); -SYSCTL_VNET_PROC(_net_inet_sctp, OID_AUTO, cmt_pf, CTLTYPE_UINT | CTLFLAG_RW, - &SCTP_BASE_SYSCTL(sctp_cmt_pf), 0, sysctl_sctp_check, "IU", - SCTPCTL_CMT_PF_DESC); - SYSCTL_VNET_PROC(_net_inet_sctp, OID_AUTO, cwnd_maxburst, CTLTYPE_UINT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst), 0, sysctl_sctp_check, "IU", SCTPCTL_CWND_MAXBURST_DESC); -SYSCTL_VNET_PROC(_net_inet_sctp, OID_AUTO, early_fast_retran, CTLTYPE_UINT | CTLFLAG_RW, - &SCTP_BASE_SYSCTL(sctp_early_fr), 0, sysctl_sctp_check, "IU", - SCTPCTL_EARLY_FAST_RETRAN_DESC); - -SYSCTL_VNET_PROC(_net_inet_sctp, OID_AUTO, early_fast_retran_msec, CTLTYPE_UINT | CTLFLAG_RW, - &SCTP_BASE_SYSCTL(sctp_early_fr_msec), 0, sysctl_sctp_check, "IU", - SCTPCTL_EARLY_FAST_RETRAN_MSEC_DESC); - SYSCTL_VNET_PROC(_net_inet_sctp, OID_AUTO, asconf_auth_nochk, CTLTYPE_UINT | CTLFLAG_RW, &SCTP_BASE_SYSCTL(sctp_asconf_auth_nochk), 0, sysctl_sctp_check, "IU", SCTPCTL_ASCONF_AUTH_NOCHK_DESC); diff --git a/sys/netinet/sctp_sysctl.h b/sys/netinet/sctp_sysctl.h index 6429d594fec8..f62679f8e640 100644 --- a/sys/netinet/sctp_sysctl.h +++ b/sys/netinet/sctp_sysctl.h @@ -74,16 +74,14 @@ struct sctp_sysctl { uint32_t sctp_init_rtx_max_default; uint32_t sctp_assoc_rtx_max_default; uint32_t sctp_path_rtx_max_default; + uint32_t sctp_path_pf_threshold; uint32_t sctp_add_more_threshold; uint32_t sctp_nr_outgoing_streams_default; uint32_t sctp_cmt_on_off; uint32_t sctp_cmt_use_dac; /* EY 5/5/08 - nr_sack flag variable */ uint32_t sctp_nr_sack_on_off; - uint32_t sctp_cmt_pf; uint32_t sctp_use_cwnd_based_maxburst; - uint32_t sctp_early_fr; - uint32_t sctp_early_fr_msec; uint32_t sctp_asconf_auth_nochk; uint32_t sctp_auth_disable; uint32_t sctp_nat_friendly; @@ -322,6 +320,12 @@ struct sctp_sysctl { #define SCTPCTL_PATH_RTX_MAX_MAX 0xFFFFFFFF #define SCTPCTL_PATH_RTX_MAX_DEFAULT SCTP_DEF_MAX_PATH_RTX +/* path_pf_threshold: threshold for considering the path potentially failed */ +#define SCTPCTL_PATH_PF_THRESHOLD_DESC "Default potentially failed threshold" +#define SCTPCTL_PATH_PF_THRESHOLD_MIN 0 +#define SCTPCTL_PATH_PF_THRESHOLD_MAX 0xFFFF +#define SCTPCTL_PATH_PF_THRESHOLD_DEFAULT SCTPCTL_PATH_PF_THRESHOLD_MAX + /* add_more_on_output: When space-wise is it worthwhile to try to add more to a socket send buffer */ #define SCTPCTL_ADD_MORE_ON_OUTPUT_DESC "When space-wise is it worthwhile to try to add more to a socket send buffer" #define SCTPCTL_ADD_MORE_ON_OUTPUT_MIN 0 @@ -352,30 +356,12 @@ struct sctp_sysctl { #define SCTPCTL_CMT_USE_DAC_MAX 1 #define SCTPCTL_CMT_USE_DAC_DEFAULT 0 -/* JRS 5/2107 - CMT PF type flag */ -#define SCTPCTL_CMT_PF_DESC "CMT PF type flag" -#define SCTPCTL_CMT_PF_MIN 0 -#define SCTPCTL_CMT_PF_MAX 2 -#define SCTPCTL_CMT_PF_DEFAULT 0 - /* cwnd_maxburst: Use a CWND adjusting maxburst */ #define SCTPCTL_CWND_MAXBURST_DESC "Use a CWND adjusting maxburst" #define SCTPCTL_CWND_MAXBURST_MIN 0 #define SCTPCTL_CWND_MAXBURST_MAX 1 #define SCTPCTL_CWND_MAXBURST_DEFAULT 1 -/* early_fast_retran: Early Fast Retransmit with timer */ -#define SCTPCTL_EARLY_FAST_RETRAN_DESC "Early Fast Retransmit with timer" -#define SCTPCTL_EARLY_FAST_RETRAN_MIN 0 -#define SCTPCTL_EARLY_FAST_RETRAN_MAX 0xFFFFFFFF -#define SCTPCTL_EARLY_FAST_RETRAN_DEFAULT 0 - -/* early_fast_retran_msec: Early Fast Retransmit minimum timer value */ -#define SCTPCTL_EARLY_FAST_RETRAN_MSEC_DESC "Early Fast Retransmit minimum timer value" -#define SCTPCTL_EARLY_FAST_RETRAN_MSEC_MIN 0 -#define SCTPCTL_EARLY_FAST_RETRAN_MSEC_MAX 0xFFFFFFFF -#define SCTPCTL_EARLY_FAST_RETRAN_MSEC_DEFAULT SCTP_MINFR_MSEC_TIMER - /* asconf_auth_nochk: Disable SCTP ASCONF AUTH requirement */ #define SCTPCTL_ASCONF_AUTH_NOCHK_DESC "Disable SCTP ASCONF AUTH requirement" #define SCTPCTL_ASCONF_AUTH_NOCHK_MIN 0 diff --git a/sys/netinet/sctp_timer.c b/sys/netinet/sctp_timer.c index 133af4aed452..a33e7924b224 100644 --- a/sys/netinet/sctp_timer.c +++ b/sys/netinet/sctp_timer.c @@ -55,103 +55,6 @@ __FBSDID("$FreeBSD$"); void -sctp_early_fr_timer(struct sctp_inpcb *inp, - struct sctp_tcb *stcb, - struct sctp_nets *net) -{ - struct sctp_tmit_chunk *chk, *pchk; - struct timeval now, min_wait, tv; - unsigned int cur_rto, cnt = 0, cnt_resend = 0; - - /* an early FR is occuring. */ - (void)SCTP_GETTIME_TIMEVAL(&now); - /* get cur rto in micro-seconds */ - if (net->lastsa == 0) { - /* Hmm no rtt estimate yet? */ - cur_rto = stcb->asoc.initial_rto >> 2; - } else { - - cur_rto = (net->lastsa >> SCTP_RTT_SHIFT) + net->lastsv; - } - if (cur_rto < SCTP_BASE_SYSCTL(sctp_early_fr_msec)) { - cur_rto = SCTP_BASE_SYSCTL(sctp_early_fr_msec); - } - cur_rto *= 1000; - tv.tv_sec = cur_rto / 1000000; - tv.tv_usec = cur_rto % 1000000; - min_wait = now; - timevalsub(&min_wait, &tv); - if (min_wait.tv_sec < 0 || min_wait.tv_usec < 0) { - /* - * if we hit here, we don't have enough seconds on the clock - * to account for the RTO. We just let the lower seconds be - * the bounds and don't worry about it. This may mean we - * will mark a lot more than we should. - */ - min_wait.tv_sec = min_wait.tv_usec = 0; - } - TAILQ_FOREACH_REVERSE_SAFE(chk, &stcb->asoc.sent_queue, sctpchunk_listhead, sctp_next, pchk) { - if (chk->whoTo != net) { - continue; - } - if (chk->sent == SCTP_DATAGRAM_RESEND) - cnt_resend++; - else if ((chk->sent > SCTP_DATAGRAM_UNSENT) && - (chk->sent < SCTP_DATAGRAM_RESEND)) { - /* pending, may need retran */ - if (chk->sent_rcv_time.tv_sec > min_wait.tv_sec) { - /* - * we have reached a chunk that was sent - * some seconds past our min.. forget it we - * will find no more to send. - */ - continue; - } else if (chk->sent_rcv_time.tv_sec == min_wait.tv_sec) { - /* - * we must look at the micro seconds to - * know. - */ - if (chk->sent_rcv_time.tv_usec >= min_wait.tv_usec) { - /* - * ok it was sent after our boundary - * time. - */ - continue; - } - } - if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_EARLYFR_LOGGING_ENABLE) { - sctp_log_fr(chk->rec.data.TSN_seq, chk->snd_count, - 4, SCTP_FR_MARKED_EARLY); - } - SCTP_STAT_INCR(sctps_earlyfrmrkretrans); - chk->sent = SCTP_DATAGRAM_RESEND; - sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); - /* double book size since we are doing an early FR */ - chk->book_size_scale++; - cnt += chk->send_size; - if ((cnt + net->flight_size) > net->cwnd) { - /* Mark all we could possibly resend */ - break; - } - } - } - if (cnt) { - /* - * JRS - Use the congestion control given in the congestion - * control module - */ - stcb->asoc.cc_functions.sctp_cwnd_update_after_fr_timer(inp, stcb, net); - } else if (cnt_resend) { - sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_EARLY_FR_TMR, SCTP_SO_NOT_LOCKED); - } - /* Restart it? */ - if (net->flight_size < net->cwnd) { - SCTP_STAT_INCR(sctps_earlyfrstrtmr); - sctp_timer_start(SCTP_TIMER_TYPE_EARLYFR, stcb->sctp_ep, stcb, net); - } -} - -void sctp_audit_retranmission_queue(struct sctp_association *asoc) { struct sctp_tmit_chunk *chk; @@ -195,44 +98,23 @@ sctp_threshold_management(struct sctp_inpcb *inp, struct sctp_tcb *stcb, /* We had a threshold failure */ if (net->dest_state & SCTP_ADDR_REACHABLE) { net->dest_state &= ~SCTP_ADDR_REACHABLE; - net->dest_state |= SCTP_ADDR_NOT_REACHABLE; net->dest_state &= ~SCTP_ADDR_REQ_PRIMARY; - if (net == stcb->asoc.primary_destination) { - net->dest_state |= SCTP_ADDR_WAS_PRIMARY; - } - /* - * JRS 5/14/07 - If a destination is - * unreachable, the PF bit is turned off. - * This allows an unambiguous use of the PF - * bit for destinations that are reachable - * but potentially failed. If the - * destination is set to the unreachable - * state, also set the destination to the PF - * state. - */ - /* - * Add debug message here if destination is - * not in PF state. - */ - /* Stop any running T3 timers here? */ - if ((stcb->asoc.sctp_cmt_on_off > 0) && - (stcb->asoc.sctp_cmt_pf > 0)) { - net->dest_state &= ~SCTP_ADDR_PF; - SCTPDBG(SCTP_DEBUG_TIMER4, "Destination %p moved from PF to unreachable.\n", - net); - } + net->dest_state &= ~SCTP_ADDR_PF; sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, stcb, SCTP_FAILED_THRESHOLD, (void *)net, SCTP_SO_NOT_LOCKED); } + } else if ((net->pf_threshold < net->failure_threshold) && + (net->error_count > net->pf_threshold)) { + if (!(net->dest_state & SCTP_ADDR_PF)) { + net->dest_state |= SCTP_ADDR_PF; + net->last_active = sctp_get_tick_count(); + sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_TIMER + SCTP_LOC_3); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); + } } - /*********HOLD THIS COMMENT FOR PATCH OF ALTERNATE - *********ROUTING CODE - */ - /*********HOLD THIS COMMENT FOR END OF PATCH OF ALTERNATE - *********ROUTING CODE - */ } if (stcb == NULL) return (0); @@ -407,31 +289,10 @@ sctp_find_alternate_net(struct sctp_tcb *stcb, } } } - /* - * JRS 5/14/07 - After all destination have been considered - * as alternates, check to see if there was some active - * destination (not in PF state). If not, check to see if - * there was some PF destination with the minimum number of - * errors. If not, return the original destination. If - * there is a min_errors_net, remove the PF flag from that - * destination, set the cwnd to one or two MTUs, and return - * the destination as an alt. If there was some active - * destination with a highest cwnd, return the destination - * as an alt. - */ if (max_cwnd_net == NULL) { if (min_errors_net == NULL) { return (net); } - min_errors_net->dest_state &= ~SCTP_ADDR_PF; - min_errors_net->cwnd = min_errors_net->mtu * stcb->asoc.sctp_cmt_pf; - if (SCTP_OS_TIMER_PENDING(&min_errors_net->rxt_timer.timer)) { - sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, - stcb, min_errors_net, - SCTP_FROM_SCTP_TIMER + SCTP_LOC_2); - } - SCTPDBG(SCTP_DEBUG_TIMER4, "Destination %p moved from PF to active with %d errors.\n", - min_errors_net, min_errors_net->error_count); return (min_errors_net); } else { return (max_cwnd_net); @@ -646,15 +507,12 @@ sctp_mark_all_for_resend(struct sctp_tcb *stcb, /* get cur rto in micro-seconds */ cur_rto = (net->lastsa >> SCTP_RTT_SHIFT) + net->lastsv; cur_rto *= 1000; - if (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_EARLYFR_LOGGING_ENABLE | SCTP_FR_LOGGING_ENABLE)) { + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(cur_rto, stcb->asoc.peers_rwnd, window_probe, SCTP_FR_T3_MARK_TIME); - sctp_log_fr(net->flight_size, - SCTP_OS_TIMER_PENDING(&net->fr_timer.timer), - SCTP_OS_TIMER_ACTIVE(&net->fr_timer.timer), - SCTP_FR_CWND_REPORT); + sctp_log_fr(net->flight_size, 0, 0, SCTP_FR_CWND_REPORT); sctp_log_fr(net->flight_size, net->cwnd, stcb->asoc.total_flight, SCTP_FR_CWND_REPORT); } tv.tv_sec = cur_rto / 1000000; @@ -670,7 +528,7 @@ sctp_mark_all_for_resend(struct sctp_tcb *stcb, */ min_wait.tv_sec = min_wait.tv_usec = 0; } - if (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_EARLYFR_LOGGING_ENABLE | SCTP_FR_LOGGING_ENABLE)) { + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(cur_rto, now.tv_sec, now.tv_usec, SCTP_FR_T3_MARK_TIME); sctp_log_fr(0, min_wait.tv_sec, min_wait.tv_usec, SCTP_FR_T3_MARK_TIME); } @@ -717,7 +575,7 @@ start_again: */ /* validate its been outstanding long enough */ - if (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_EARLYFR_LOGGING_ENABLE | SCTP_FR_LOGGING_ENABLE)) { + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(chk->rec.data.TSN_seq, chk->sent_rcv_time.tv_sec, chk->sent_rcv_time.tv_usec, @@ -729,7 +587,7 @@ start_again: * some seconds past our min.. forget it we * will find no more to send. */ - if (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_EARLYFR_LOGGING_ENABLE | SCTP_FR_LOGGING_ENABLE)) { + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(0, chk->sent_rcv_time.tv_sec, chk->sent_rcv_time.tv_usec, @@ -747,12 +605,6 @@ start_again: * ok it was sent after our boundary * time. */ - if (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_EARLYFR_LOGGING_ENABLE | SCTP_FR_LOGGING_ENABLE)) { - sctp_log_fr(0, - chk->sent_rcv_time.tv_sec, - chk->sent_rcv_time.tv_usec, - SCTP_FR_T3_STOPPED); - } continue; } } @@ -791,7 +643,7 @@ start_again: tsnfirst = chk->rec.data.TSN_seq; } tsnlast = chk->rec.data.TSN_seq; - if (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_EARLYFR_LOGGING_ENABLE | SCTP_FR_LOGGING_ENABLE)) { + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(chk->rec.data.TSN_seq, chk->snd_count, 0, SCTP_FR_T3_MARKED); } @@ -860,7 +712,7 @@ start_again: /* we did not subtract the same things? */ audit_tf = 1; } - if (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_EARLYFR_LOGGING_ENABLE | SCTP_FR_LOGGING_ENABLE)) { + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { sctp_log_fr(tsnfirst, tsnlast, num_mk, SCTP_FR_T3_TIMEOUT); } #ifdef SCTP_DEBUG @@ -978,61 +830,6 @@ sctp_t3rxt_timer(struct sctp_inpcb *inp, win_probe = 0; } - /* - * JRS 5/14/07 - If CMT PF is on and the destination if not already - * in PF state, set the destination to PF state and store the - * current time as the time that the destination was last active. In - * addition, find an alternate destination with PF-based - * find_alt_net(). - */ - if ((stcb->asoc.sctp_cmt_on_off > 0) && - (stcb->asoc.sctp_cmt_pf > 0)) { - if ((net->dest_state & SCTP_ADDR_PF) != SCTP_ADDR_PF) { - net->dest_state |= SCTP_ADDR_PF; - net->last_active = sctp_get_tick_count(); - SCTPDBG(SCTP_DEBUG_TIMER4, "Destination %p moved from active to PF.\n", - net); - } - alt = sctp_find_alternate_net(stcb, net, 2); - } else if (stcb->asoc.sctp_cmt_on_off > 0) { - /* - * CMT: Using RTX_SSTHRESH policy for CMT. If CMT is being - * used, then pick dest with largest ssthresh for any - * retransmission. - */ - alt = sctp_find_alternate_net(stcb, net, 1); - /* - * CUCv2: If a different dest is picked for the - * retransmission, then new (rtx-)pseudo_cumack needs to be - * tracked for orig dest. Let CUCv2 track new (rtx-) - * pseudo-cumack always. - */ - net->find_pseudo_cumack = 1; - net->find_rtx_pseudo_cumack = 1; - } else { /* CMT is OFF */ - alt = sctp_find_alternate_net(stcb, net, 0); - } - num_mk = 0; - num_abandoned = 0; - (void)sctp_mark_all_for_resend(stcb, net, alt, win_probe, - &num_mk, &num_abandoned); - /* FR Loss recovery just ended with the T3. */ - stcb->asoc.fast_retran_loss_recovery = 0; - - /* CMT FR loss recovery ended with the T3 */ - net->fast_retran_loss_recovery = 0; - if ((stcb->asoc.cc_functions.sctp_cwnd_new_transmission_begins) && - (net->flight_size == 0)) { - (*stcb->asoc.cc_functions.sctp_cwnd_new_transmission_begins) (stcb, net); - } - /* - * setup the sat loss recovery that prevents satellite cwnd advance. - */ - stcb->asoc.sat_t3_loss_recovery = 1; - stcb->asoc.sat_t3_recovery_tsn = stcb->asoc.sending_seq; - - /* Backoff the timer and cwnd */ - sctp_backoff_on_timeout(stcb, net, win_probe, num_mk, num_abandoned); if (win_probe == 0) { /* We don't do normal threshold management on window probes */ if (sctp_threshold_management(inp, stcb, net, @@ -1051,17 +848,15 @@ sctp_t3rxt_timer(struct sctp_inpcb *inp, } else { ms_goneby = 0; } - if ((ms_goneby > net->RTO) || (net->RTO == 0)) { - /* - * no recent feed back in an RTO or - * more, request a RTT update - */ - if (sctp_send_hb(stcb, 1, net, SCTP_SO_NOT_LOCKED) < 0) + if ((net->dest_state & SCTP_ADDR_PF) == 0) { + if ((ms_goneby > net->RTO) || (net->RTO == 0)) { /* - * Less than 0 means we lost - * the assoc + * no recent feed back in an + * RTO or more, request a + * RTT update */ - return (1); + sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); + } } } } @@ -1078,7 +873,52 @@ sctp_t3rxt_timer(struct sctp_inpcb *inp, return (1); } } - if (net->dest_state & SCTP_ADDR_NOT_REACHABLE) { + if (stcb->asoc.sctp_cmt_on_off > 0) { + if (net->pf_threshold < net->failure_threshold) { + alt = sctp_find_alternate_net(stcb, net, 2); + } else { + /* + * CMT: Using RTX_SSTHRESH policy for CMT. If CMT is + * being used, then pick dest with largest ssthresh + * for any retransmission. + */ + alt = sctp_find_alternate_net(stcb, net, 1); + /* + * CUCv2: If a different dest is picked for the + * retransmission, then new (rtx-)pseudo_cumack + * needs to be tracked for orig dest. Let CUCv2 + * track new (rtx-) pseudo-cumack always. + */ + net->find_pseudo_cumack = 1; + net->find_rtx_pseudo_cumack = 1; + } + } else { + alt = sctp_find_alternate_net(stcb, net, 0); + } + + num_mk = 0; + num_abandoned = 0; + (void)sctp_mark_all_for_resend(stcb, net, alt, win_probe, + &num_mk, &num_abandoned); + /* FR Loss recovery just ended with the T3. */ + stcb->asoc.fast_retran_loss_recovery = 0; + + /* CMT FR loss recovery ended with the T3 */ + net->fast_retran_loss_recovery = 0; + if ((stcb->asoc.cc_functions.sctp_cwnd_new_transmission_begins) && + (net->flight_size == 0)) { + (*stcb->asoc.cc_functions.sctp_cwnd_new_transmission_begins) (stcb, net); + } + /* + * setup the sat loss recovery that prevents satellite cwnd advance. + */ + stcb->asoc.sat_t3_loss_recovery = 1; + stcb->asoc.sat_t3_recovery_tsn = stcb->asoc.sending_seq; + + /* Backoff the timer and cwnd */ + sctp_backoff_on_timeout(stcb, net, win_probe, num_mk, num_abandoned); + if ((!(net->dest_state & SCTP_ADDR_REACHABLE)) || + (net->dest_state & SCTP_ADDR_PF)) { /* Move all pending over too */ sctp_move_chunks_from_net(stcb, net); @@ -1106,23 +946,12 @@ sctp_t3rxt_timer(struct sctp_inpcb *inp, * change-primary then this flag must be cleared * from any net structures. */ - if (sctp_set_primary_addr(stcb, - (struct sockaddr *)NULL, - alt) == 0) { - net->dest_state |= SCTP_ADDR_WAS_PRIMARY; + if (stcb->asoc.alternate) { + sctp_free_remote_addr(stcb->asoc.alternate); } + stcb->asoc.alternate = alt; + atomic_add_int(&stcb->asoc.alternate->ref_count, 1); } - } else if ((stcb->asoc.sctp_cmt_on_off > 0) && - (stcb->asoc.sctp_cmt_pf > 0) && - ((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF)) { - /* - * JRS 5/14/07 - If the destination hasn't failed completely - * but is in PF state, a PF-heartbeat needs to be sent - * manually. - */ - if (sctp_send_hb(stcb, 1, net, SCTP_SO_NOT_LOCKED) < 0) - /* Return less than 0 means we lost the association */ - return (1); } /* * Special case for cookie-echo'ed case, we don't do output but must @@ -1324,7 +1153,7 @@ sctp_strreset_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, atomic_add_int(&alt->ref_count, 1); } } - if (net->dest_state & SCTP_ADDR_NOT_REACHABLE) { + if (!(net->dest_state & SCTP_ADDR_REACHABLE)) { /* * If the address went un-reachable, we need to move to * alternates for ALL chk's in queue @@ -1414,7 +1243,7 @@ sctp_asconf_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); chk->sent = SCTP_DATAGRAM_RESEND; } - if (net->dest_state & SCTP_ADDR_NOT_REACHABLE) { + if (!(net->dest_state & SCTP_ADDR_REACHABLE)) { /* * If the address went un-reachable, we need to move * to the alternate for ALL chunks in queue @@ -1569,11 +1398,15 @@ sctp_audit_stream_queues_for_size(struct sctp_inpcb *inp, int sctp_heartbeat_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, - struct sctp_nets *net, int cnt_of_unconf) + struct sctp_nets *net) { - int ret; + uint8_t net_was_pf; + net_was_pf = 0; if (net) { + if (net->dest_state & SCTP_ADDR_PF) { + net_was_pf = 1; + } if (net->hb_responded == 0) { if (net->ro._s_addr) { /* @@ -1585,6 +1418,10 @@ sctp_heartbeat_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, net->src_addr_selected = 0; } sctp_backoff_on_timeout(stcb, net, 1, 0, 0); + if (sctp_threshold_management(inp, stcb, net, stcb->asoc.max_send_times)) { + /* Assoc is over */ + return (1); + } } /* Zero PBA, if it needs it */ if (net->partial_bytes_acked) { @@ -1596,41 +1433,13 @@ sctp_heartbeat_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, (TAILQ_EMPTY(&stcb->asoc.sent_queue))) { sctp_audit_stream_queues_for_size(inp, stcb); } - /* Send a new HB, this will do threshold managment, pick a new dest */ - if (cnt_of_unconf == 0) { - if (sctp_send_hb(stcb, 0, NULL, SCTP_SO_NOT_LOCKED) < 0) { - return (1); - } - } else { + if (!(net->dest_state & SCTP_ADDR_NOHB) && + !((net_was_pf == 0) && (net->dest_state & SCTP_ADDR_PF))) { /* - * this will send out extra hb's up to maxburst if there are - * any unconfirmed addresses. + * when move to PF during threshold mangement, a HB has been + * queued in that routine */ - uint32_t cnt_sent = 0; - - TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { - if ((net->dest_state & SCTP_ADDR_UNCONFIRMED) && - (net->dest_state & SCTP_ADDR_REACHABLE)) { - cnt_sent++; - if (net->hb_responded == 0) { - /* Did we respond last time? */ - if (net->ro._s_addr) { - sctp_free_ifa(net->ro._s_addr); - net->ro._s_addr = NULL; - net->src_addr_selected = 0; - } - } - ret = sctp_send_hb(stcb, 1, net, SCTP_SO_NOT_LOCKED); - if (ret < 0) - return 1; - else if (ret == 0) { - break; - } - if (SCTP_BASE_SYSCTL(sctp_hb_maxburst) && - (cnt_sent >= SCTP_BASE_SYSCTL(sctp_hb_maxburst))) - break; - } - } + sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); } return (0); } @@ -1733,7 +1542,14 @@ sctp_autoclose_timer(struct sctp_inpcb *inp, */ if (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) { /* only send SHUTDOWN 1st time thru */ - sctp_send_shutdown(stcb, stcb->asoc.primary_destination); + struct sctp_nets *netp; + + if (stcb->asoc.alternate) { + netp = stcb->asoc.alternate; + } else { + netp = stcb->asoc.primary_destination; + } + sctp_send_shutdown(stcb, netp); if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { SCTP_STAT_DECR_GAUGE32(sctps_currestab); @@ -1742,10 +1558,10 @@ sctp_autoclose_timer(struct sctp_inpcb *inp, SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, - asoc->primary_destination); + netp); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb, - asoc->primary_destination); + netp); } } } else { diff --git a/sys/netinet/sctp_timer.h b/sys/netinet/sctp_timer.h index 5857291da890..7b1341a3cf48 100644 --- a/sys/netinet/sctp_timer.h +++ b/sys/netinet/sctp_timer.h @@ -42,10 +42,6 @@ __FBSDID("$FreeBSD$"); #define SCTP_RTT_SHIFT 3 #define SCTP_RTT_VAR_SHIFT 2 -void -sctp_early_fr_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb, - struct sctp_nets *net); - struct sctp_nets * sctp_find_alternate_net(struct sctp_tcb *, struct sctp_nets *, int mode); @@ -65,7 +61,7 @@ sctp_shutdown_timer(struct sctp_inpcb *, struct sctp_tcb *, struct sctp_nets *); int sctp_heartbeat_timer(struct sctp_inpcb *, struct sctp_tcb *, - struct sctp_nets *, int); + struct sctp_nets *); int sctp_cookie_timer(struct sctp_inpcb *, struct sctp_tcb *, diff --git a/sys/netinet/sctp_uio.h b/sys/netinet/sctp_uio.h index 9b9acbfc6f64..d4a0df183f96 100644 --- a/sys/netinet/sctp_uio.h +++ b/sys/netinet/sctp_uio.h @@ -329,7 +329,8 @@ struct sctp_paddr_change { #define SCTP_ADDR_CONFIRMED 0x0006 #define SCTP_ACTIVE 0x0001 /* SCTP_ADDR_REACHABLE */ -#define SCTP_INACTIVE 0x0002 /* SCTP_ADDR_NOT_REACHABLE */ +#define SCTP_INACTIVE 0x0002 /* neither SCTP_ADDR_REACHABLE nor + * SCTP_ADDR_UNCONFIRMED */ #define SCTP_UNCONFIRMED 0x0200 /* SCTP_ADDR_UNCONFIRMED */ /* remote error events */ @@ -516,6 +517,13 @@ struct sctp_paddrparams { #define SPP_IPV6_FLOWLABEL 0x00000100 #define SPP_IPV4_TOS 0x00000200 +struct sctp_paddrthlds { + sctp_assoc_t spt_assoc_id; + struct sockaddr_storage spt_address; + uint16_t spt_pathmaxrxt; + uint16_t spt_pathpfthld; +}; + struct sctp_paddrinfo { struct sockaddr_storage spinfo_address; sctp_assoc_t spinfo_assoc_id; @@ -978,18 +986,8 @@ struct sctpstat { * fired */ uint32_t sctps_timoassockill; /* Number of asoc free timers expired */ uint32_t sctps_timoinpkill; /* Number of inp free timers expired */ - /* Early fast retransmission counters */ - uint32_t sctps_earlyfrstart; - uint32_t sctps_earlyfrstop; - uint32_t sctps_earlyfrmrkretrans; - uint32_t sctps_earlyfrstpout; - uint32_t sctps_earlyfrstpidsck1; - uint32_t sctps_earlyfrstpidsck2; - uint32_t sctps_earlyfrstpidsck3; - uint32_t sctps_earlyfrstpidsck4; - uint32_t sctps_earlyfrstrid; - uint32_t sctps_earlyfrstrout; - uint32_t sctps_earlyfrstrtmr; + /* former early FR counters */ + uint32_t sctps_spare[11]; /* others */ uint32_t sctps_hdrops; /* packet shorter than header */ uint32_t sctps_badsum; /* checksum error */ @@ -1162,9 +1160,11 @@ struct xsctp_raddr { uint8_t active; /* sctpAssocLocalRemEntry 3 */ uint8_t confirmed; /* */ uint8_t heartbeat_enabled; /* sctpAssocLocalRemEntry 4 */ + uint8_t potentially_failed; struct sctp_timeval start_time; /* sctpAssocLocalRemEntry 8 */ uint32_t rtt; - uint32_t extra_padding[32]; /* future */ + uint32_t heartbeat_interval; + uint32_t extra_padding[31]; /* future */ }; #define SCTP_MAX_LOGGING_SIZE 30000 diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index ab87772f0d47..275e9a284f65 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -275,33 +275,8 @@ sctp_notify(struct sctp_inpcb *inp, */ if (net->dest_state & SCTP_ADDR_REACHABLE) { /* Ok that destination is NOT reachable */ - SCTP_PRINTF("ICMP (thresh %d/%d) takes interface %p down\n", - net->error_count, - net->failure_threshold, - net); - net->dest_state &= ~SCTP_ADDR_REACHABLE; - net->dest_state |= SCTP_ADDR_NOT_REACHABLE; - /* - * JRS 5/14/07 - If a destination is unreachable, - * the PF bit is turned off. This allows an - * unambiguous use of the PF bit for destinations - * that are reachable but potentially failed. If the - * destination is set to the unreachable state, also - * set the destination to the PF state. - */ - /* - * Add debug message here if destination is not in - * PF state. - */ - /* Stop any running T3 timers here? */ - if ((stcb->asoc.sctp_cmt_on_off > 0) && - (stcb->asoc.sctp_cmt_pf > 0)) { - net->dest_state &= ~SCTP_ADDR_PF; - SCTPDBG(SCTP_DEBUG_TIMER4, "Destination %p moved from PF to unreachable.\n", - net); - } - net->error_count = net->failure_threshold + 1; + net->dest_state &= ~SCTP_ADDR_PF; sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, stcb, SCTP_FAILED_THRESHOLD, (void *)net, SCTP_SO_NOT_LOCKED); @@ -837,9 +812,15 @@ sctp_disconnect(struct socket *so) if ((SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) && (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT)) { /* only send SHUTDOWN 1st time thru */ + struct sctp_nets *netp; + + if (stcb->asoc.alternate) { + netp = stcb->asoc.alternate; + } else { + netp = stcb->asoc.primary_destination; + } sctp_stop_timers_for_shutdown(stcb); - sctp_send_shutdown(stcb, - stcb->asoc.primary_destination); + sctp_send_shutdown(stcb, netp); sctp_chunk_output(stcb->sctp_ep, stcb, SCTP_OUTPUT_FROM_T3, SCTP_SO_LOCKED); if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { @@ -848,11 +829,10 @@ sctp_disconnect(struct socket *so) SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT); SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, - stcb->sctp_ep, stcb, - asoc->primary_destination); + stcb->sctp_ep, stcb, netp); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, - stcb->sctp_ep, stcb, - asoc->primary_destination); + stcb->sctp_ep, stcb, netp); + } } else { /* @@ -865,9 +845,17 @@ sctp_disconnect(struct socket *so) * we will allow user data to be sent first * and move to SHUTDOWN-PENDING */ + struct sctp_nets *netp; + + if (stcb->asoc.alternate) { + netp = stcb->asoc.alternate; + } else { + netp = stcb->asoc.primary_destination; + } + asoc->state |= SCTP_STATE_SHUTDOWN_PENDING; sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb, - asoc->primary_destination); + netp); if (asoc->locked_on_sending) { /* Locked to send out the data */ struct sctp_stream_queue_pending *sp; @@ -1047,9 +1035,15 @@ sctp_shutdown(struct socket *so) /* there is nothing queued to send, so I'm done... */ if (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) { /* only send SHUTDOWN the first time through */ + struct sctp_nets *netp; + + if (stcb->asoc.alternate) { + netp = stcb->asoc.alternate; + } else { + netp = stcb->asoc.primary_destination; + } sctp_stop_timers_for_shutdown(stcb); - sctp_send_shutdown(stcb, - stcb->asoc.primary_destination); + sctp_send_shutdown(stcb, netp); sctp_chunk_output(stcb->sctp_ep, stcb, SCTP_OUTPUT_FROM_T3, SCTP_SO_LOCKED); if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) || (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { @@ -1058,20 +1052,26 @@ sctp_shutdown(struct socket *so) SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT); SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, - stcb->sctp_ep, stcb, - asoc->primary_destination); + stcb->sctp_ep, stcb, netp); sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, - stcb->sctp_ep, stcb, - asoc->primary_destination); + stcb->sctp_ep, stcb, netp); } } else { /* * we still got (or just got) data to send, so set * SHUTDOWN_PENDING */ + struct sctp_nets *netp; + + if (stcb->asoc.alternate) { + netp = stcb->asoc.alternate; + } else { + netp = stcb->asoc.primary_destination; + } + asoc->state |= SCTP_STATE_SHUTDOWN_PENDING; sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb, - asoc->primary_destination); + netp); if (asoc->locked_on_sending) { /* Locked to send out the data */ @@ -2389,7 +2389,7 @@ flags_out: } } if (stcb) { - /* Applys to the specific association */ + /* Applies to the specific association */ paddrp->spp_flags = 0; if (net) { int ovh; @@ -2400,7 +2400,7 @@ flags_out: ovh = SCTP_MED_V4_OVERHEAD; } - + paddrp->spp_hbinterval = net->heart_beat_delay; paddrp->spp_pathmaxrxt = net->failure_threshold; paddrp->spp_pathmtu = net->mtu - ovh; /* get flags for HB */ @@ -2444,11 +2444,12 @@ flags_out: paddrp->spp_flags |= SPP_IPV6_FLOWLABEL; #endif /* default settings should be these */ - if (stcb->asoc.hb_is_disabled == 0) { - paddrp->spp_flags |= SPP_HB_ENABLE; - } else { + if (sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_DONOT_HEARTBEAT)) { paddrp->spp_flags |= SPP_HB_DISABLE; + } else { + paddrp->spp_flags |= SPP_HB_ENABLE; } + paddrp->spp_hbinterval = stcb->asoc.heart_beat_delay; TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { if (SCTP_OS_TIMER_PENDING(&net->pmtu_timer.timer)) { cnt++; @@ -2458,7 +2459,6 @@ flags_out: paddrp->spp_flags |= SPP_PMTUD_ENABLE; } } - paddrp->spp_hbinterval = stcb->asoc.heart_beat_delay; paddrp->spp_assoc_id = sctp_get_associd(stcb); SCTP_TCB_UNLOCK(stcb); } else { @@ -3080,6 +3080,95 @@ flags_out: } break; } + case SCTP_PEER_ADDR_THLDS: + { + struct sctp_paddrthlds *thlds; + struct sctp_nets *net; + + SCTP_CHECK_AND_CAST(thlds, optval, struct sctp_paddrthlds, *optsize); + SCTP_FIND_STCB(inp, stcb, thlds->spt_assoc_id); + + net = NULL; + if (stcb) { + net = sctp_findnet(stcb, (struct sockaddr *)&thlds->spt_address); + } else { + /* + * We increment here since + * sctp_findassociation_ep_addr() wil do a + * decrement if it finds the stcb as long as + * the locked tcb (last argument) is NOT a + * TCB.. aka NULL. + */ + SCTP_INP_INCR_REF(inp); + stcb = sctp_findassociation_ep_addr(&inp, (struct sockaddr *)&thlds->spt_address, &net, NULL, NULL); + if (stcb == NULL) { + SCTP_INP_DECR_REF(inp); + } + } + if (stcb && (net == NULL)) { + struct sockaddr *sa; + + sa = (struct sockaddr *)&thlds->spt_address; +#ifdef INET + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)sa; + if (sin->sin_addr.s_addr) { + error = EINVAL; + SCTP_TCB_UNLOCK(stcb); + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, error); + break; + } + } else +#endif +#ifdef INET6 + if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)sa; + if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + error = EINVAL; + SCTP_TCB_UNLOCK(stcb); + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, error); + break; + } + } else +#endif + { + error = EAFNOSUPPORT; + SCTP_TCB_UNLOCK(stcb); + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, error); + break; + } + } + if (stcb) { + if (net) { + thlds->spt_pathmaxrxt = net->failure_threshold; + thlds->spt_pathpfthld = net->pf_threshold; + } else { + thlds->spt_pathmaxrxt = stcb->asoc.def_net_failure; + thlds->spt_pathpfthld = stcb->asoc.def_net_pf_threshold; + } + thlds->spt_assoc_id = sctp_get_associd(stcb); + SCTP_TCB_UNLOCK(stcb); + } else { + if (thlds->spt_assoc_id == SCTP_FUTURE_ASSOC) { + /* Use endpoint defaults */ + SCTP_INP_RLOCK(inp); + thlds->spt_pathmaxrxt = inp->sctp_ep.def_net_failure; + thlds->spt_pathpfthld = inp->sctp_ep.def_net_pf_threshold; + SCTP_INP_RUNLOCK(inp); + } else { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + error = EINVAL; + } + } + if (error == 0) { + *optsize = sizeof(struct sctp_paddrthlds); + } + break; + } default: SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOPROTOOPT); error = ENOPROTOOPT; @@ -3138,6 +3227,12 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, */ if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { /* only valid for bound all sockets */ + if ((SCTP_BASE_SYSCTL(sctp_auto_asconf) == 0) && + (*mopt != 0)) { + /* forbidden by admin */ + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EPERM); + return (EPERM); + } set_opt = SCTP_PCB_FLAGS_AUTO_ASCONF; } else { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); @@ -4368,7 +4463,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, break; } case SCTP_PEER_ADDR_PARAMS: - /* Applys to the specific association */ + /* Applies to the specific association */ { struct sctp_paddrparams *paddrp; struct sctp_nets *net; @@ -4459,29 +4554,33 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, ovh = SCTP_MED_V4_OVERHEAD; } - if (paddrp->spp_hbinterval) - stcb->asoc.heart_beat_delay = paddrp->spp_hbinterval; - else if (paddrp->spp_flags & SPP_HB_TIME_IS_ZERO) - stcb->asoc.heart_beat_delay = 0; - /* network sets ? */ if (net) { /************************NET SPECIFIC SET ******************/ - if (paddrp->spp_flags & SPP_HB_DEMAND) { - /* on demand HB */ - if (sctp_send_hb(stcb, 1, net, SCTP_SO_LOCKED) < 0) { - /* asoc destroyed */ - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); - error = EINVAL; - break; - } - } if (paddrp->spp_flags & SPP_HB_DISABLE) { + if (!(net->dest_state & SCTP_ADDR_UNCONFIRMED) && + !(net->dest_state & SCTP_ADDR_NOHB)) { + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net, + SCTP_FROM_SCTP_USRREQ + SCTP_LOC_10); + } net->dest_state |= SCTP_ADDR_NOHB; } if (paddrp->spp_flags & SPP_HB_ENABLE) { + if (paddrp->spp_hbinterval) { + net->heart_beat_delay = paddrp->spp_hbinterval; + } else if (paddrp->spp_flags & SPP_HB_TIME_IS_ZERO) { + net->heart_beat_delay = 0; + } + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net, + SCTP_FROM_SCTP_USRREQ + SCTP_LOC_10); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net); net->dest_state &= ~SCTP_ADDR_NOHB; } + if (paddrp->spp_flags & SPP_HB_DEMAND) { + /* on demand HB */ + sctp_send_hb(stcb, net, SCTP_SO_LOCKED); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net); + } if ((paddrp->spp_flags & SPP_PMTUD_DISABLE) && (paddrp->spp_pathmtu >= SCTP_SMALLEST_PMTU)) { if (SCTP_OS_TIMER_PENDING(&net->pmtu_timer.timer)) { sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net, @@ -4499,8 +4598,33 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net); } } - if (paddrp->spp_pathmaxrxt) + if (paddrp->spp_pathmaxrxt) { + if (net->dest_state & SCTP_ADDR_PF) { + if (net->error_count > paddrp->spp_pathmaxrxt) { + net->dest_state &= ~SCTP_ADDR_PF; + } + } else { + if ((net->error_count <= paddrp->spp_pathmaxrxt) && + (net->error_count > net->pf_threshold)) { + net->dest_state |= SCTP_ADDR_PF; + sctp_send_hb(stcb, net, SCTP_SO_LOCKED); + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_TIMER + SCTP_LOC_3); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); + } + } + if (net->dest_state & SCTP_ADDR_REACHABLE) { + if (net->error_count > paddrp->spp_pathmaxrxt) { + net->dest_state &= ~SCTP_ADDR_REACHABLE; + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, stcb, SCTP_RESPONSE_TO_USER_REQ, net, SCTP_SO_LOCKED); + } + } else { + if (net->error_count <= paddrp->spp_pathmaxrxt) { + net->dest_state |= SCTP_ADDR_REACHABLE; + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, SCTP_RESPONSE_TO_USER_REQ, net, SCTP_SO_LOCKED); + } + } net->failure_threshold = paddrp->spp_pathmaxrxt; + } #ifdef INET if (paddrp->spp_flags & SPP_IPV4_TOS) { if (net->ro._l_addr.sin.sin_family == AF_INET) { @@ -4517,13 +4641,67 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, #endif } else { /************************ASSOC ONLY -- NO NET SPECIFIC SET ******************/ - if (paddrp->spp_pathmaxrxt) + if (paddrp->spp_pathmaxrxt) { stcb->asoc.def_net_failure = paddrp->spp_pathmaxrxt; - + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + if (net->dest_state & SCTP_ADDR_PF) { + if (net->error_count > paddrp->spp_pathmaxrxt) { + net->dest_state &= ~SCTP_ADDR_PF; + } + } else { + if ((net->error_count <= paddrp->spp_pathmaxrxt) && + (net->error_count > net->pf_threshold)) { + net->dest_state |= SCTP_ADDR_PF; + sctp_send_hb(stcb, net, SCTP_SO_LOCKED); + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_TIMER + SCTP_LOC_3); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); + } + } + if (net->dest_state & SCTP_ADDR_REACHABLE) { + if (net->error_count > paddrp->spp_pathmaxrxt) { + net->dest_state &= ~SCTP_ADDR_REACHABLE; + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, stcb, SCTP_RESPONSE_TO_USER_REQ, net, SCTP_SO_LOCKED); + } + } else { + if (net->error_count <= paddrp->spp_pathmaxrxt) { + net->dest_state |= SCTP_ADDR_REACHABLE; + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, SCTP_RESPONSE_TO_USER_REQ, net, SCTP_SO_LOCKED); + } + } + net->failure_threshold = paddrp->spp_pathmaxrxt; + } + } if (paddrp->spp_flags & SPP_HB_ENABLE) { + if (paddrp->spp_hbinterval) { + stcb->asoc.heart_beat_delay = paddrp->spp_hbinterval; + } else if (paddrp->spp_flags & SPP_HB_TIME_IS_ZERO) { + stcb->asoc.heart_beat_delay = 0; + } /* Turn back on the timer */ - stcb->asoc.hb_is_disabled = 0; - sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net); + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + if (paddrp->spp_hbinterval) { + net->heart_beat_delay = paddrp->spp_hbinterval; + } else if (paddrp->spp_flags & SPP_HB_TIME_IS_ZERO) { + net->heart_beat_delay = 0; + } + if (net->dest_state & SCTP_ADDR_NOHB) { + net->dest_state &= ~SCTP_ADDR_NOHB; + } + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net, + SCTP_FROM_SCTP_USRREQ + SCTP_LOC_10); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net); + } + } + if (paddrp->spp_flags & SPP_HB_DISABLE) { + /* Turn back on the timer */ + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + if (!(net->dest_state & SCTP_ADDR_NOHB)) { + net->dest_state |= SCTP_ADDR_NOHB; + if (!(net->dest_state & SCTP_ADDR_UNCONFIRMED)) { + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_10); + } + } + } } if ((paddrp->spp_flags & SPP_PMTUD_DISABLE) && (paddrp->spp_pathmtu >= SCTP_SMALLEST_PMTU)) { TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { @@ -4546,41 +4724,25 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, } } } - if (paddrp->spp_flags & SPP_HB_DISABLE) { - int cnt_of_unconf = 0; - struct sctp_nets *lnet; - - stcb->asoc.hb_is_disabled = 1; - TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) { - if (lnet->dest_state & SCTP_ADDR_UNCONFIRMED) { - cnt_of_unconf++; - } - } - /* - * stop the timer ONLY if we - * have no unconfirmed - * addresses - */ - if (cnt_of_unconf == 0) { - TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { - sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net, - SCTP_FROM_SCTP_USRREQ + SCTP_LOC_11); - } - } - } - if (paddrp->spp_flags & SPP_HB_ENABLE) { - /* start up the timer. */ +#ifdef INET + if (paddrp->spp_flags & SPP_IPV4_TOS) { TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { - sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net); + if (net->ro._l_addr.sin.sin_family == AF_INET) { + net->tos_flowlabel = paddrp->spp_ipv4_tos & 0x000000fc; + } } - } -#ifdef INET - if (paddrp->spp_flags & SPP_IPV4_TOS) stcb->asoc.default_tos = paddrp->spp_ipv4_tos & 0x000000fc; + } #endif #ifdef INET6 - if (paddrp->spp_flags & SPP_IPV6_FLOWLABEL) + if (paddrp->spp_flags & SPP_IPV6_FLOWLABEL) { + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + if (net->ro._l_addr.sin6.sin6_family == AF_INET6) { + net->tos_flowlabel = paddrp->spp_ipv6_flowlabel; + } + } stcb->asoc.default_flowlabel = paddrp->spp_ipv6_flowlabel; + } #endif } @@ -4605,8 +4767,12 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT] = MSEC_TO_TICKS(paddrp->spp_hbinterval); } if (paddrp->spp_flags & SPP_HB_ENABLE) { + if (paddrp->spp_flags & SPP_HB_TIME_IS_ZERO) { + inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT] = 0; + } else if (paddrp->spp_hbinterval) { + inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT] = MSEC_TO_TICKS(paddrp->spp_hbinterval); + } sctp_feature_off(inp, SCTP_PCB_FLAGS_DONOT_HEARTBEAT); - } else if (paddrp->spp_flags & SPP_HB_DISABLE) { sctp_feature_on(inp, SCTP_PCB_FLAGS_DONOT_HEARTBEAT); } @@ -4769,10 +4935,12 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, (!(net->dest_state & SCTP_ADDR_UNCONFIRMED))) { /* Ok we need to set it */ if (sctp_set_primary_addr(stcb, (struct sockaddr *)NULL, net) == 0) { - if (net->dest_state & SCTP_ADDR_SWITCH_PRIMARY) { - net->dest_state |= SCTP_ADDR_DOUBLE_SWITCH; + if ((stcb->asoc.alternate) && + (!(net->dest_state & SCTP_ADDR_PF)) && + (net->dest_state & SCTP_ADDR_REACHABLE)) { + sctp_free_remote_addr(stcb->asoc.alternate); + stcb->asoc.alternate = NULL; } - net->dest_state |= SCTP_ADDR_SWITCH_PRIMARY; } } } else { @@ -4930,7 +5098,9 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, error = EINVAL; break; } - if (td != NULL && (error = prison_local_ip6(td->td_ucred, &(((struct sockaddr_in6 *)(addrs->addr))->sin6_addr), + if (td != NULL && + (error = prison_local_ip6(td->td_ucred, + &(((struct sockaddr_in6 *)(addrs->addr))->sin6_addr), (SCTP_IPV6_V6ONLY(inp) != 0))) != 0) { SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, error); break; @@ -5170,6 +5340,146 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, } break; } + case SCTP_PEER_ADDR_THLDS: + /* Applies to the specific association */ + { + struct sctp_paddrthlds *thlds; + struct sctp_nets *net; + + SCTP_CHECK_AND_CAST(thlds, optval, struct sctp_paddrthlds, optsize); + SCTP_FIND_STCB(inp, stcb, thlds->spt_assoc_id); + net = NULL; + if (stcb) { + net = sctp_findnet(stcb, (struct sockaddr *)&thlds->spt_assoc_id); + } else { + /* + * We increment here since + * sctp_findassociation_ep_addr() wil do a + * decrement if it finds the stcb as long as + * the locked tcb (last argument) is NOT a + * TCB.. aka NULL. + */ + SCTP_INP_INCR_REF(inp); + stcb = sctp_findassociation_ep_addr(&inp, + (struct sockaddr *)&thlds->spt_assoc_id, + &net, NULL, NULL); + if (stcb == NULL) { + SCTP_INP_DECR_REF(inp); + } + } + if (stcb && (net == NULL)) { + struct sockaddr *sa; + + sa = (struct sockaddr *)&thlds->spt_assoc_id; +#ifdef INET + if (sa->sa_family == AF_INET) { + + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)sa; + if (sin->sin_addr.s_addr) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + SCTP_TCB_UNLOCK(stcb); + error = EINVAL; + break; + } + } else +#endif +#ifdef INET6 + if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)sa; + if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + SCTP_TCB_UNLOCK(stcb); + error = EINVAL; + break; + } + } else +#endif + { + error = EAFNOSUPPORT; + SCTP_TCB_UNLOCK(stcb); + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, error); + break; + } + } + if (stcb) { + if (net) { + if (net->dest_state & SCTP_ADDR_PF) { + if ((net->failure_threshold > thlds->spt_pathmaxrxt) || + (net->failure_threshold <= thlds->spt_pathpfthld)) { + net->dest_state &= ~SCTP_ADDR_PF; + } + } else { + if ((net->failure_threshold > thlds->spt_pathpfthld) && + (net->failure_threshold <= thlds->spt_pathmaxrxt)) { + net->dest_state |= SCTP_ADDR_PF; + sctp_send_hb(stcb, net, SCTP_SO_LOCKED); + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_TIMER + SCTP_LOC_3); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); + } + } + if (net->dest_state & SCTP_ADDR_REACHABLE) { + if (net->failure_threshold > thlds->spt_pathmaxrxt) { + net->dest_state &= ~SCTP_ADDR_REACHABLE; + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, stcb, SCTP_RESPONSE_TO_USER_REQ, net, SCTP_SO_LOCKED); + } + } else { + if (net->failure_threshold <= thlds->spt_pathmaxrxt) { + net->dest_state |= SCTP_ADDR_REACHABLE; + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, SCTP_RESPONSE_TO_USER_REQ, net, SCTP_SO_LOCKED); + } + } + net->failure_threshold = thlds->spt_pathmaxrxt; + net->pf_threshold = thlds->spt_pathpfthld; + } else { + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + if (net->dest_state & SCTP_ADDR_PF) { + if ((net->failure_threshold > thlds->spt_pathmaxrxt) || + (net->failure_threshold <= thlds->spt_pathpfthld)) { + net->dest_state &= ~SCTP_ADDR_PF; + } + } else { + if ((net->failure_threshold > thlds->spt_pathpfthld) && + (net->failure_threshold <= thlds->spt_pathmaxrxt)) { + net->dest_state |= SCTP_ADDR_PF; + sctp_send_hb(stcb, net, SCTP_SO_LOCKED); + sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_TIMER + SCTP_LOC_3); + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); + } + } + if (net->dest_state & SCTP_ADDR_REACHABLE) { + if (net->failure_threshold > thlds->spt_pathmaxrxt) { + net->dest_state &= ~SCTP_ADDR_REACHABLE; + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, stcb, SCTP_RESPONSE_TO_USER_REQ, net, SCTP_SO_LOCKED); + } + } else { + if (net->failure_threshold <= thlds->spt_pathmaxrxt) { + net->dest_state |= SCTP_ADDR_REACHABLE; + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, SCTP_RESPONSE_TO_USER_REQ, net, SCTP_SO_LOCKED); + } + } + net->failure_threshold = thlds->spt_pathmaxrxt; + net->pf_threshold = thlds->spt_pathpfthld; + } + stcb->asoc.def_net_failure = thlds->spt_pathmaxrxt; + stcb->asoc.def_net_pf_threshold = thlds->spt_pathpfthld; + } + } else { + if (thlds->spt_assoc_id == SCTP_FUTURE_ASSOC) { + SCTP_INP_WLOCK(inp); + inp->sctp_ep.def_net_failure = thlds->spt_pathmaxrxt; + inp->sctp_ep.def_net_pf_threshold = thlds->spt_pathpfthld; + SCTP_INP_WUNLOCK(inp); + } else { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + error = EINVAL; + } + } + break; + } default: SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOPROTOOPT); error = ENOPROTOOPT; diff --git a/sys/netinet/sctp_var.h b/sys/netinet/sctp_var.h index e48dfe49da10..b41f5c8303de 100644 --- a/sys/netinet/sctp_var.h +++ b/sys/netinet/sctp_var.h @@ -179,7 +179,6 @@ extern struct pr_usrreqs sctp_usrreqs; if (SCTP_DECREMENT_AND_CHECK_REFCOUNT(&(__net)->ref_count)) { \ (void)SCTP_OS_TIMER_STOP(&(__net)->rxt_timer.timer); \ (void)SCTP_OS_TIMER_STOP(&(__net)->pmtu_timer.timer); \ - (void)SCTP_OS_TIMER_STOP(&(__net)->fr_timer.timer); \ if ((__net)->ro.ro_rt) { \ RTFREE((__net)->ro.ro_rt); \ (__net)->ro.ro_rt = NULL; \ @@ -189,7 +188,7 @@ extern struct pr_usrreqs sctp_usrreqs; (__net)->ro._s_addr = NULL; \ } \ (__net)->src_addr_selected = 0; \ - (__net)->dest_state = SCTP_ADDR_NOT_REACHABLE; \ + (__net)->dest_state &= ~SCTP_ADDR_REACHABLE; \ SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_net), (__net)); \ SCTP_DECR_RADDR_COUNT(); \ } \ @@ -312,6 +311,8 @@ extern struct pr_usrreqs sctp_usrreqs; #endif +#define SCTP_PF_ENABLED(_net) (_net->pf_threshold < _net->failure_threshold) +#define SCTP_NET_IS_PF(_net) (_net->pf_threshold < _net->error_count) struct sctp_nets; struct sctp_inpcb; diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index 9a8bd2e9dd19..9beedbb52ef1 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -739,15 +739,14 @@ sctp_stop_timers_for_shutdown(struct sctp_tcb *stcb) asoc = &stcb->asoc; - (void)SCTP_OS_TIMER_STOP(&asoc->hb_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->dack_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->strreset_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->asconf_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->autoclose_timer.timer); (void)SCTP_OS_TIMER_STOP(&asoc->delayed_event_timer.timer); TAILQ_FOREACH(net, &asoc->nets, sctp_next) { - (void)SCTP_OS_TIMER_STOP(&net->fr_timer.timer); (void)SCTP_OS_TIMER_STOP(&net->pmtu_timer.timer); + (void)SCTP_OS_TIMER_STOP(&net->hb_timer.timer); } } @@ -921,7 +920,7 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb, asoc->sctp_cmt_on_off = m->sctp_cmt_on_off; asoc->ecn_allowed = m->sctp_ecn_enable; asoc->sctp_nr_sack_on_off = (uint8_t) SCTP_BASE_SYSCTL(sctp_nr_sack_on_off); - asoc->sctp_cmt_pf = (uint8_t) SCTP_BASE_SYSCTL(sctp_cmt_pf); + asoc->sctp_cmt_pf = (uint8_t) 0; asoc->sctp_frag_point = m->sctp_frag_point; asoc->sctp_features = m->sctp_features; #ifdef INET @@ -946,11 +945,6 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb, asoc->peer_vtag_nonce = sctp_select_a_tag(m, stcb->sctp_ep->sctp_lport, stcb->rport, 0); asoc->vrf_id = vrf_id; - if (sctp_is_feature_on(m, SCTP_PCB_FLAGS_DONOT_HEARTBEAT)) - asoc->hb_is_disabled = 1; - else - asoc->hb_is_disabled = 0; - #ifdef SCTP_ASOCLOG_OF_TSNS asoc->tsn_in_at = 0; asoc->tsn_out_at = 0; @@ -989,6 +983,7 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb, asoc->max_init_times = m->sctp_ep.max_init_times; asoc->max_send_times = m->sctp_ep.max_send_times; asoc->def_net_failure = m->sctp_ep.def_net_failure; + asoc->def_net_pf_threshold = m->sctp_ep.def_net_pf_threshold; asoc->free_chunk_cnt = 0; asoc->iam_blocking = 0; @@ -1632,11 +1627,10 @@ sctp_timeout_handler(void *t) case SCTP_TIMER_TYPE_RECV: if ((stcb == NULL) || (inp == NULL)) { break; - } { - SCTP_STAT_INCR(sctps_timosack); - stcb->asoc.timosack++; - sctp_send_sack(stcb, SCTP_SO_NOT_LOCKED); } + SCTP_STAT_INCR(sctps_timosack); + stcb->asoc.timosack++; + sctp_send_sack(stcb, SCTP_SO_NOT_LOCKED); #ifdef SCTP_AUDITING_ENABLED sctp_auditing(4, inp, stcb, net); #endif @@ -1658,33 +1652,20 @@ sctp_timeout_handler(void *t) sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_SHUT_TMR, SCTP_SO_NOT_LOCKED); break; case SCTP_TIMER_TYPE_HEARTBEAT: - { - struct sctp_nets *lnet; - int cnt_of_unconf = 0; - - if ((stcb == NULL) || (inp == NULL)) { - break; - } - SCTP_STAT_INCR(sctps_timoheartbeat); - stcb->asoc.timoheartbeat++; - TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) { - if ((lnet->dest_state & SCTP_ADDR_UNCONFIRMED) && - (lnet->dest_state & SCTP_ADDR_REACHABLE)) { - cnt_of_unconf++; - } - } - if (cnt_of_unconf == 0) { - if (sctp_heartbeat_timer(inp, stcb, lnet, - cnt_of_unconf)) { - /* no need to unlock on tcb its gone */ - goto out_decr; - } - } + if ((stcb == NULL) || (inp == NULL) || (net == NULL)) { + break; + } + SCTP_STAT_INCR(sctps_timoheartbeat); + stcb->asoc.timoheartbeat++; + if (sctp_heartbeat_timer(inp, stcb, net)) { + /* no need to unlock on tcb its gone */ + goto out_decr; + } #ifdef SCTP_AUDITING_ENABLED - sctp_auditing(4, inp, stcb, lnet); + sctp_auditing(4, inp, stcb, net); #endif - sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, - stcb->sctp_ep, stcb, lnet); + if (!(net->dest_state & SCTP_ADDR_NOHB)) { + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_HB_TMR, SCTP_SO_NOT_LOCKED); } break; @@ -1780,14 +1761,6 @@ sctp_timeout_handler(void *t) SCTP_STAT_INCR(sctps_timostrmrst); sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_STRRST_TMR, SCTP_SO_NOT_LOCKED); break; - case SCTP_TIMER_TYPE_EARLYFR: - /* Need to do FR of things for net */ - if ((stcb == NULL) || (inp == NULL)) { - break; - } - SCTP_STAT_INCR(sctps_timoearlyfr); - sctp_early_fr_timer(inp, stcb, net); - break; case SCTP_TIMER_TYPE_ASCONF: if ((stcb == NULL) || (inp == NULL)) { break; @@ -1898,7 +1871,7 @@ void sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net) { - int to_ticks; + uint32_t to_ticks; struct sctp_timer *tmr; if ((t_type != SCTP_TIMER_TYPE_ADDR_WQ) && (inp == NULL)) @@ -1985,71 +1958,38 @@ sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, * though we use a different timer. We also add the HB timer * PLUS a random jitter. */ - if ((inp == NULL) || (stcb == NULL)) { + if ((inp == NULL) || (stcb == NULL) || (net == NULL)) { return; } else { uint32_t rndval; - uint8_t this_random; - int cnt_of_unconf = 0; - struct sctp_nets *lnet; - - TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) { - if ((lnet->dest_state & SCTP_ADDR_UNCONFIRMED) && - (lnet->dest_state & SCTP_ADDR_REACHABLE)) { - cnt_of_unconf++; - } - } - if (cnt_of_unconf) { - net = lnet = NULL; - (void)sctp_heartbeat_timer(inp, stcb, lnet, cnt_of_unconf); - } - if (stcb->asoc.hb_random_idx > 3) { - rndval = sctp_select_initial_TSN(&inp->sctp_ep); - memcpy(stcb->asoc.hb_random_values, &rndval, - sizeof(stcb->asoc.hb_random_values)); - stcb->asoc.hb_random_idx = 0; - } - this_random = stcb->asoc.hb_random_values[stcb->asoc.hb_random_idx]; - stcb->asoc.hb_random_idx++; - stcb->asoc.hb_ect_randombit = 0; - /* - * this_random will be 0 - 256 ms RTO is in ms. - */ - if ((stcb->asoc.hb_is_disabled) && - (cnt_of_unconf == 0)) { + uint32_t jitter; + + if ((net->dest_state & SCTP_ADDR_NOHB) && + !(net->dest_state & SCTP_ADDR_UNCONFIRMED)) { return; } - if (net) { - int delay; - - delay = stcb->asoc.heart_beat_delay; - TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) { - if ((lnet->dest_state & SCTP_ADDR_UNCONFIRMED) && - ((lnet->dest_state & SCTP_ADDR_OUT_OF_SCOPE) == 0) && - (lnet->dest_state & SCTP_ADDR_REACHABLE)) { - delay = 0; - } - } - if (net->RTO == 0) { - /* Never been checked */ - to_ticks = this_random + stcb->asoc.initial_rto + delay; - } else { - /* set rto_val to the ms */ - to_ticks = delay + net->RTO + this_random; - } + if (net->RTO == 0) { + to_ticks = stcb->asoc.initial_rto; } else { - if (cnt_of_unconf) { - to_ticks = this_random + stcb->asoc.initial_rto; - } else { - to_ticks = stcb->asoc.heart_beat_delay + this_random + stcb->asoc.initial_rto; - } + to_ticks = net->RTO; + } + rndval = sctp_select_initial_TSN(&inp->sctp_ep); + jitter = rndval % to_ticks; + if (jitter >= (to_ticks >> 1)) { + to_ticks = to_ticks + (jitter - (to_ticks >> 1)); + } else { + to_ticks = to_ticks - jitter; + } + if (!(net->dest_state & SCTP_ADDR_UNCONFIRMED) && + !(net->dest_state & SCTP_ADDR_PF)) { + to_ticks += net->heart_beat_delay; } /* * Now we must convert the to_ticks that are now in * ms to ticks. */ to_ticks = MSEC_TO_TICKS(to_ticks); - tmr = &stcb->asoc.hb_timer; + tmr = &net->hb_timer; } break; case SCTP_TIMER_TYPE_COOKIE: @@ -2150,35 +2090,6 @@ sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, } tmr = &stcb->asoc.strreset_timer; break; - - case SCTP_TIMER_TYPE_EARLYFR: - { - unsigned int msec; - - if ((stcb == NULL) || (net == NULL)) { - return; - } - if (net->flight_size > net->cwnd) { - /* no need to start */ - return; - } - SCTP_STAT_INCR(sctps_earlyfrstart); - if (net->lastsa == 0) { - /* Hmm no rtt estimate yet? */ - msec = stcb->asoc.initial_rto >> 2; - } else { - msec = ((net->lastsa >> 2) + net->lastsv) >> 1; - } - if (msec < SCTP_BASE_SYSCTL(sctp_early_fr_msec)) { - msec = SCTP_BASE_SYSCTL(sctp_early_fr_msec); - if (msec < SCTP_MINFR_MSEC_FLOOR) { - msec = SCTP_MINFR_MSEC_FLOOR; - } - } - to_ticks = MSEC_TO_TICKS(msec); - tmr = &net->fr_timer; - } - break; case SCTP_TIMER_TYPE_ASCONF: /* * Here the timer comes from the stcb but its value is from @@ -2273,13 +2184,6 @@ sctp_timer_stop(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, case SCTP_TIMER_TYPE_ADDR_WQ: tmr = &SCTP_BASE_INFO(addr_wq_timer); break; - case SCTP_TIMER_TYPE_EARLYFR: - if ((stcb == NULL) || (net == NULL)) { - return; - } - tmr = &net->fr_timer; - SCTP_STAT_INCR(sctps_earlyfrstop); - break; case SCTP_TIMER_TYPE_SEND: if ((stcb == NULL) || (net == NULL)) { return; @@ -2305,10 +2209,10 @@ sctp_timer_stop(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb, tmr = &net->rxt_timer; break; case SCTP_TIMER_TYPE_HEARTBEAT: - if (stcb == NULL) { + if ((stcb == NULL) || (net == NULL)) { return; } - tmr = &stcb->asoc.hb_timer; + tmr = &net->hb_timer; break; case SCTP_TIMER_TYPE_COOKIE: if ((stcb == NULL) || (net == NULL)) { @@ -6209,7 +6113,7 @@ sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr, #ifdef INET case AF_INET: incr = sizeof(struct sockaddr_in); - if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { + if (sctp_add_remote_addr(stcb, sa, NULL, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { /* assoc gone no un-lock */ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ENOBUFS); (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_7); @@ -6222,7 +6126,7 @@ sctp_connectx_helper_add(struct sctp_tcb *stcb, struct sockaddr *addr, #ifdef INET6 case AF_INET6: incr = sizeof(struct sockaddr_in6); - if (sctp_add_remote_addr(stcb, sa, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { + if (sctp_add_remote_addr(stcb, sa, NULL, SCTP_DONOT_SETSCOPE, SCTP_ADDR_IS_CONFIRMED)) { /* assoc gone no un-lock */ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ENOBUFS); (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_8); |