summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/netinet/sctp.h4
-rw-r--r--sys/netinet/sctp_header.h6
-rw-r--r--sys/netinet/sctp_indata.c8
-rw-r--r--sys/netinet/sctp_input.c112
-rw-r--r--sys/netinet/sctp_output.c77
-rw-r--r--sys/netinet/sctp_output.h2
-rw-r--r--sys/netinet/sctp_pcb.c1
-rw-r--r--sys/netinet/sctp_structs.h7
-rw-r--r--sys/netinet/sctputil.c1
9 files changed, 165 insertions, 53 deletions
diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h
index 8e9c0c1..135ce8d 100644
--- a/sys/netinet/sctp.h
+++ b/sys/netinet/sctp.h
@@ -417,6 +417,10 @@ struct sctp_error_unrecognized_chunk {
#define SCTP_BADCRC 0x02
#define SCTP_PACKET_TRUNCATED 0x04
+/* Flag for ECN -CWR */
+#define SCTP_CWR_REDUCE_OVERRIDE 0x01
+#define SCTP_CWR_IN_SAME_WINDOW 0x02
+
#define SCTP_SAT_NETWORK_MIN 400 /* min ms for RTT to set satellite
* time */
#define SCTP_SAT_NETWORK_BURST_INCR 2 /* how many times to multiply maxburst
diff --git a/sys/netinet/sctp_header.h b/sys/netinet/sctp_header.h
index 7fc3e56..2e94fb7 100644
--- a/sys/netinet/sctp_header.h
+++ b/sys/netinet/sctp_header.h
@@ -360,9 +360,15 @@ struct sctp_cookie_ack_chunk {
} SCTP_PACKED;
/* Explicit Congestion Notification Echo (ECNE) */
+struct old_sctp_ecne_chunk {
+ struct sctp_chunkhdr ch;
+ uint32_t tsn;
+} SCTP_PACKED;
+
struct sctp_ecne_chunk {
struct sctp_chunkhdr ch;
uint32_t tsn;
+ uint32_t num_pkts_since_cwr;
} SCTP_PACKED;
/* Congestion Window Reduced (CWR) */
diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c
index 7ede155..fce913e 100644
--- a/sys/netinet/sctp_indata.c
+++ b/sys/netinet/sctp_indata.c
@@ -3829,6 +3829,10 @@ sctp_express_handle_sack(struct sctp_tcb *stcb, uint32_t cumack,
}
/* First setup for CC stuff */
TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+ if (SCTP_TSN_GT(cumack, net->cwr_window_tsn)) {
+ /* Drag along the window_tsn for cwr's */
+ net->cwr_window_tsn = cumack;
+ }
net->prev_cwnd = net->cwnd;
net->net_ack = 0;
net->net_ack2 = 0;
@@ -4522,6 +4526,10 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup,
* destination address basis.
*/
TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+ if (SCTP_TSN_GT(cum_ack, net->cwr_window_tsn)) {
+ /* Drag along the window_tsn for cwr's */
+ net->cwr_window_tsn = cum_ack;
+ }
net->prev_cwnd = net->cwnd;
net->net_ack = 0;
net->net_ack2 = 0;
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index bb8722f..664ff62 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -1678,8 +1678,6 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd);
asoc->pre_open_streams = ntohs(initack_cp->init.num_outbound_streams);
- /* Note last_cwr_tsn? where is this used? */
- asoc->last_cwr_tsn = asoc->init_seq_number - 1;
if (ntohl(init_cp->init.initiate_tag) != asoc->peer_vtag) {
/*
* Ok the peer probably discarded our data (if we
@@ -1835,7 +1833,6 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number;
asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1;
- asoc->last_cwr_tsn = asoc->init_seq_number - 1;
asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1;
asoc->str_reset_seq_in = asoc->init_seq_number;
@@ -2073,7 +2070,6 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
asoc->init_seq_number = ntohl(initack_cp->init.initial_tsn);
asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number;
asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1;
- asoc->last_cwr_tsn = asoc->init_seq_number - 1;
asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1;
asoc->str_reset_seq_in = asoc->init_seq_number;
@@ -2915,25 +2911,38 @@ sctp_handle_ecn_echo(struct sctp_ecne_chunk *cp,
{
struct sctp_nets *net;
struct sctp_tmit_chunk *lchk;
- uint32_t tsn;
+ struct sctp_ecne_chunk bkup;
+ uint8_t override_bit = 0;
+ uint32_t tsn, window_data_tsn;
+ int len;
+ int pkt_cnt;
- if (ntohs(cp->ch.chunk_length) != sizeof(struct sctp_ecne_chunk)) {
+ len = ntohs(cp->ch.chunk_length);
+ if ((len != sizeof(struct sctp_ecne_chunk)) &&
+ (len != sizeof(struct old_sctp_ecne_chunk))) {
return;
}
+ if (len == sizeof(struct old_sctp_ecne_chunk)) {
+ /* Its the old format */
+ memcpy(&bkup, cp, sizeof(struct old_sctp_ecne_chunk));
+ bkup.num_pkts_since_cwr = htonl(1);
+ cp = &bkup;
+ }
SCTP_STAT_INCR(sctps_recvecne);
tsn = ntohl(cp->tsn);
+ pkt_cnt = ntohl(cp->num_pkts_since_cwr);
/* ECN Nonce stuff: need a resync and disable the nonce sum check */
/* Also we make sure we disable the nonce_wait */
- lchk = TAILQ_FIRST(&stcb->asoc.send_queue);
+ lchk = TAILQ_LAST(&stcb->asoc.send_queue, sctpchunk_listhead);
if (lchk == NULL) {
- stcb->asoc.nonce_resync_tsn = stcb->asoc.sending_seq;
+ window_data_tsn = stcb->asoc.nonce_resync_tsn = stcb->asoc.sending_seq - 1;
} else {
- stcb->asoc.nonce_resync_tsn = lchk->rec.data.TSN_seq;
+ window_data_tsn = stcb->asoc.nonce_resync_tsn = lchk->rec.data.TSN_seq;
}
stcb->asoc.nonce_wait_for_ecne = 0;
stcb->asoc.nonce_sum_check = 0;
- /* Find where it was sent, if possible */
+ /* Find where it was sent to if possible. */
net = NULL;
TAILQ_FOREACH(lchk, &stcb->asoc.sent_queue, sctp_next) {
if (lchk->rec.data.TSN_seq == tsn) {
@@ -2944,32 +2953,71 @@ sctp_handle_ecn_echo(struct sctp_ecne_chunk *cp,
break;
}
}
- if (net == NULL)
- /* default is we use the primary */
- net = stcb->asoc.primary_destination;
-
- if (SCTP_TSN_GT(tsn, stcb->asoc.last_cwr_tsn)) {
+ if (net == NULL) {
+ /*
+ * What to do. A previous send of a CWR was possibly lost.
+ * See how old it is, we may have it marked on the actual
+ * net.
+ */
+ TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+ if (tsn == net->last_cwr_tsn) {
+ /* Found him, send it off */
+ goto out;
+ }
+ }
+ /*
+ * If we reach here, we need to send a special CWR that says
+ * hey, we did this a long time ago and you lost the
+ * response.
+ */
+ net = TAILQ_FIRST(&stcb->asoc.nets);
+ override_bit = SCTP_CWR_REDUCE_OVERRIDE;
+ }
+out:
+ if (SCTP_TSN_GT(tsn, net->cwr_window_tsn)) {
/*
* JRS - Use the congestion control given in the pluggable
* CC module
*/
+ int ocwnd;
+
+ ocwnd = net->cwnd;
stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo(stcb, net);
/*
- * we reduce once every RTT. So we will only lower cwnd at
- * the next sending seq i.e. the resync_tsn.
+ * We reduce once every RTT. So we will only lower cwnd at
+ * the next sending seq i.e. the window_data_tsn
*/
- stcb->asoc.last_cwr_tsn = stcb->asoc.nonce_resync_tsn;
+ net->cwr_window_tsn = window_data_tsn;
+ net->ecn_ce_pkt_cnt += pkt_cnt;
+ net->lost_cnt = pkt_cnt;
+ net->last_cwr_tsn = tsn;
+ } else {
+ override_bit |= SCTP_CWR_IN_SAME_WINDOW;
+ if (SCTP_TSN_GT(tsn, net->last_cwr_tsn)) {
+ /*
+ * Another loss in the same window update how man
+ * marks we have had
+ */
+
+ if (pkt_cnt > net->lost_cnt) {
+ /* Should be the case */
+ net->ecn_ce_pkt_cnt += (pkt_cnt - net->lost_cnt);
+ net->lost_cnt = pkt_cnt;
+ }
+ net->last_cwr_tsn = tsn;
+ }
}
/*
* We always send a CWR this way if our previous one was lost our
* peer will get an update, or if it is not time again to reduce we
- * still get the cwr to the peer.
+ * still get the cwr to the peer. Note we set the override when we
+ * could not find the TSN on the chunk or the destination network.
*/
- sctp_send_cwr(stcb, net, tsn);
+ sctp_send_cwr(stcb, net, net->last_cwr_tsn, override_bit);
}
static void
-sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb)
+sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb, struct sctp_nets *net)
{
/*
* Here we get a CWR from the peer. We must look in the outqueue and
@@ -2978,18 +3026,22 @@ sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb)
*/
struct sctp_tmit_chunk *chk;
struct sctp_ecne_chunk *ecne;
+ int override;
+ uint32_t cwr_tsn;
+
+ cwr_tsn = ntohl(cp->tsn);
+ override = cp->ch.chunk_flags & SCTP_CWR_REDUCE_OVERRIDE;
TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) {
if (chk->rec.chunk_id.id != SCTP_ECN_ECHO) {
continue;
}
- /*
- * Look for and remove if it is the right TSN. Since there
- * is only ONE ECNE on the control queue at any one time we
- * don't need to worry about more than one!
- */
+ if ((override == 0) && (chk->whoTo != net)) {
+ /* Must be from the right src unless override is set */
+ continue;
+ }
ecne = mtod(chk->data, struct sctp_ecne_chunk *);
- if (SCTP_TSN_GE(ntohl(cp->tsn), ntohl(ecne->tsn))) {
+ if (SCTP_TSN_GE(cwr_tsn, ntohl(ecne->tsn))) {
/* this covers this ECNE, we can remove it */
stcb->asoc.ecn_echo_cnt_onq--;
TAILQ_REMOVE(&stcb->asoc.control_send_queue, chk,
@@ -3000,7 +3052,9 @@ sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb)
}
stcb->asoc.ctrl_queue_cnt--;
sctp_free_a_chunk(stcb, chk);
- break;
+ if (override == 0) {
+ break;
+ }
}
}
}
@@ -5041,7 +5095,7 @@ process_control_chunks:
__LINE__);
}
stcb->asoc.overall_error_count = 0;
- sctp_handle_ecn_cwr((struct sctp_cwr_chunk *)ch, stcb);
+ sctp_handle_ecn_cwr((struct sctp_cwr_chunk *)ch, stcb, *netp);
}
break;
case SCTP_SHUTDOWN_COMPLETE:
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index 480c88e..0651054 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -7274,7 +7274,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
* fomulate and send the low level chunks. Making sure to combine
* any control in the control chunk queue also.
*/
- struct sctp_nets *net, *start_at, *old_start_at = NULL;
+ struct sctp_nets *net, *start_at, *sack_goes_to = NULL, *old_start_at = NULL;
struct mbuf *outchain, *endoutchain;
struct sctp_tmit_chunk *chk, *nchk;
@@ -7327,10 +7327,12 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
no_data_chunks = 0;
/* Nothing to possible to send? */
- if (TAILQ_EMPTY(&asoc->control_send_queue) &&
+ if ((TAILQ_EMPTY(&asoc->control_send_queue) ||
+ (asoc->ctrl_queue_cnt == stcb->asoc.ecn_echo_cnt_onq)) &&
TAILQ_EMPTY(&asoc->asconf_send_queue) &&
TAILQ_EMPTY(&asoc->send_queue) &&
stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) {
+nothing_to_send:
*reason_code = 9;
return (0);
}
@@ -7342,6 +7344,21 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
no_data_chunks = 1;
}
}
+ if (stcb->asoc.ecn_echo_cnt_onq) {
+ /* Record where a sack goes, if any */
+ if (no_data_chunks &&
+ (asoc->ctrl_queue_cnt == stcb->asoc.ecn_echo_cnt_onq)) {
+ /* Nothing but ECNe to send - we don't do that */
+ goto nothing_to_send;
+ }
+ TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
+ if ((chk->rec.chunk_id.id == SCTP_SELECTIVE_ACK) ||
+ (chk->rec.chunk_id.id == SCTP_NR_SELECTIVE_ACK)) {
+ sack_goes_to = chk->whoTo;
+ break;
+ }
+ }
+ }
max_rwnd_per_dest = ((asoc->peers_rwnd + asoc->total_flight) / asoc->numnets);
if (stcb->sctp_socket)
max_send_per_dest = SCTP_SB_LIMIT_SND(stcb->sctp_socket) / asoc->numnets;
@@ -7685,6 +7702,28 @@ again_one_more_time:
/************************/
/* Now first lets go through the control queue */
TAILQ_FOREACH_SAFE(chk, &asoc->control_send_queue, sctp_next, nchk) {
+ if ((sack_goes_to) &&
+ (chk->rec.chunk_id.id == SCTP_ECN_ECHO) &&
+ (chk->whoTo != sack_goes_to)) {
+ /*
+ * if we have a sack in queue, and we are
+ * looking at an ecn echo that is NOT queued
+ * to where the sack is going..
+ */
+ if (chk->whoTo == net) {
+ /*
+ * Don't transmit it to where its
+ * going (current net)
+ */
+ continue;
+ } else if (sack_goes_to == net) {
+ /*
+ * But do transmit it to this
+ * address
+ */
+ goto skip_net_check;
+ }
+ }
if (chk->whoTo != net) {
/*
* No, not sent to the network we are
@@ -7692,6 +7731,7 @@ again_one_more_time:
*/
continue;
}
+ skip_net_check:
if (chk->data == NULL) {
continue;
}
@@ -10761,11 +10801,19 @@ sctp_send_ecn_echo(struct sctp_tcb *stcb, struct sctp_nets *net,
asoc = &stcb->asoc;
SCTP_TCB_LOCK_ASSERT(stcb);
TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
- if (chk->rec.chunk_id.id == SCTP_ECN_ECHO) {
+ if ((chk->rec.chunk_id.id == SCTP_ECN_ECHO) && (net == chk->whoTo)) {
/* found a previous ECN_ECHO update it if needed */
+ uint32_t cnt, ctsn;
+
ecne = mtod(chk->data, struct sctp_ecne_chunk *);
- ecne->tsn = htonl(high_tsn);
- SCTP_STAT_INCR(sctps_queue_upd_ecne);
+ ctsn = ntohl(ecne->tsn);
+ if (SCTP_TSN_GT(high_tsn, ctsn)) {
+ ecne->tsn = htonl(high_tsn);
+ cnt = ntohl(ecne->num_pkts_since_cwr);
+ cnt++;
+ ecne->num_pkts_since_cwr = htonl(cnt);
+ SCTP_STAT_INCR(sctps_queue_upd_ecne);
+ }
return;
}
}
@@ -10797,7 +10845,8 @@ sctp_send_ecn_echo(struct sctp_tcb *stcb, struct sctp_nets *net,
ecne->ch.chunk_flags = 0;
ecne->ch.chunk_length = htons(sizeof(struct sctp_ecne_chunk));
ecne->tsn = htonl(high_tsn);
- TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, chk, sctp_next);
+ ecne->num_pkts_since_cwr = htonl(1);
+ TAILQ_INSERT_HEAD(&stcb->asoc.control_send_queue, chk, sctp_next);
asoc->ctrl_queue_cnt++;
}
@@ -10975,7 +11024,7 @@ jump_out:
}
void
-sctp_send_cwr(struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t high_tsn)
+sctp_send_cwr(struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t high_tsn, uint8_t override)
{
struct sctp_association *asoc;
struct sctp_cwr_chunk *cwr;
@@ -10983,17 +11032,7 @@ sctp_send_cwr(struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t high_tsn)
asoc = &stcb->asoc;
SCTP_TCB_LOCK_ASSERT(stcb);
- TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
- if (chk->rec.chunk_id.id == SCTP_ECN_CWR) {
- /* found a previous ECN_CWR update it if needed */
- cwr = mtod(chk->data, struct sctp_cwr_chunk *);
- if (SCTP_TSN_GT(high_tsn, ntohl(cwr->tsn))) {
- cwr->tsn = htonl(high_tsn);
- }
- return;
- }
- }
- /* nope could not find one to update so we must build one */
+
sctp_alloc_a_chunk(stcb, chk);
if (chk == NULL) {
return;
@@ -11016,7 +11055,7 @@ sctp_send_cwr(struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t high_tsn)
atomic_add_int(&chk->whoTo->ref_count, 1);
cwr = mtod(chk->data, struct sctp_cwr_chunk *);
cwr->ch.chunk_type = SCTP_ECN_CWR;
- cwr->ch.chunk_flags = 0;
+ cwr->ch.chunk_flags = override;
cwr->ch.chunk_length = htons(sizeof(struct sctp_cwr_chunk));
cwr->tsn = htonl(high_tsn);
TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, chk, sctp_next);
diff --git a/sys/netinet/sctp_output.h b/sys/netinet/sctp_output.h
index 70fa719..cab1f52 100644
--- a/sys/netinet/sctp_output.h
+++ b/sys/netinet/sctp_output.h
@@ -163,7 +163,7 @@ sctp_send_packet_dropped(struct sctp_tcb *, struct sctp_nets *, struct mbuf *,
-void sctp_send_cwr(struct sctp_tcb *, struct sctp_nets *, uint32_t);
+void sctp_send_cwr(struct sctp_tcb *, struct sctp_nets *, uint32_t, uint8_t);
void
diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c
index f48ed6a..246ff0d 100644
--- a/sys/netinet/sctp_pcb.c
+++ b/sys/netinet/sctp_pcb.c
@@ -3854,6 +3854,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
net->RTO_measured = 0;
stcb->asoc.numnets++;
*(&net->ref_count) = 1;
+ net->cwr_window_tsn = net->last_cwr_tsn = stcb->asoc.sending_seq - 1;
net->tos_flowlabel = 0;
if (SCTP_BASE_SYSCTL(sctp_udp_tunneling_for_client_enable)) {
net->port = htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port));
diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h
index 62326be..4a8fa46 100644
--- a/sys/netinet/sctp_structs.h
+++ b/sys/netinet/sctp_structs.h
@@ -211,7 +211,10 @@ struct sctp_nets {
/* mtu discovered so far */
uint32_t mtu;
uint32_t ssthresh; /* not sure about this one for split */
-
+ uint32_t last_cwr_tsn;
+ uint32_t cwr_window_tsn;
+ uint32_t ecn_ce_pkt_cnt;
+ uint32_t lost_cnt;
/* smoothed average things for RTT and RTO itself */
int lastsa;
int lastsv;
@@ -864,7 +867,6 @@ struct sctp_association {
uint32_t highest_tsn_inside_nr_map;
uint32_t last_echo_tsn;
- uint32_t last_cwr_tsn;
uint32_t fast_recovery_tsn;
uint32_t sat_t3_recovery_tsn;
uint32_t tsn_last_delivered;
@@ -1048,7 +1050,6 @@ struct sctp_association {
uint16_t ecn_echo_cnt_onq;
uint16_t free_chunk_cnt;
-
uint8_t stream_locked;
uint8_t authenticated; /* packet authenticated ok */
/*
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index 617494c..5917578 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -971,7 +971,6 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb,
asoc->last_net_cmt_send_started = NULL;
/* This will need to be adjusted */
- asoc->last_cwr_tsn = asoc->init_seq_number - 1;
asoc->last_acked_seq = asoc->init_seq_number - 1;
asoc->advanced_peer_ack_point = asoc->last_acked_seq;
asoc->asconf_seq_in = asoc->last_acked_seq;
OpenPOWER on IntegriCloud