summaryrefslogtreecommitdiffstats
path: root/sys/netinet/sctp_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/sctp_output.c')
-rw-r--r--sys/netinet/sctp_output.c551
1 files changed, 231 insertions, 320 deletions
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index d8bc9aa..96c0393 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -1,5 +1,7 @@
/*-
* Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2008-2011, by Randall Stewart. All rights reserved.
+ * Copyright (c) 2008-2011, by Michael Tuexen. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -3389,54 +3391,10 @@ static uint8_t
sctp_get_ect(struct sctp_tcb *stcb,
struct sctp_tmit_chunk *chk)
{
- uint8_t this_random;
-
- /* Huh? */
- if (SCTP_BASE_SYSCTL(sctp_ecn_enable) == 0)
- return (0);
-
- if (SCTP_BASE_SYSCTL(sctp_ecn_nonce) == 0)
- /* no nonce, always return ECT0 */
- return (SCTP_ECT0_BIT);
-
- if (stcb->asoc.peer_supports_ecn_nonce == 0) {
- /* Peer does NOT support it, so we send a ECT0 only */
+ if ((stcb != NULL) && (stcb->asoc.ecn_allowed == 1)) {
return (SCTP_ECT0_BIT);
- }
- if (chk == NULL)
- return (SCTP_ECT0_BIT);
-
- if ((stcb->asoc.hb_random_idx > 3) ||
- ((stcb->asoc.hb_random_idx == 3) &&
- (stcb->asoc.hb_ect_randombit > 7))) {
- uint32_t rndval;
-
-warp_drive_sa:
- rndval = sctp_select_initial_TSN(&stcb->sctp_ep->sctp_ep);
- memcpy(stcb->asoc.hb_random_values, &rndval,
- sizeof(stcb->asoc.hb_random_values));
- this_random = stcb->asoc.hb_random_values[0];
- stcb->asoc.hb_random_idx = 0;
- stcb->asoc.hb_ect_randombit = 0;
- } else {
- if (stcb->asoc.hb_ect_randombit > 7) {
- stcb->asoc.hb_ect_randombit = 0;
- stcb->asoc.hb_random_idx++;
- if (stcb->asoc.hb_random_idx > 3) {
- goto warp_drive_sa;
- }
- }
- this_random = stcb->asoc.hb_random_values[stcb->asoc.hb_random_idx];
- }
- if ((this_random >> stcb->asoc.hb_ect_randombit) & 0x01) {
- if (chk != NULL)
- /* ECN Nonce stuff */
- chk->rec.data.ect_nonce = SCTP_ECT1_BIT;
- stcb->asoc.hb_ect_randombit++;
- return (SCTP_ECT1_BIT);
} else {
- stcb->asoc.hb_ect_randombit++;
- return (SCTP_ECT0_BIT);
+ return (0);
}
}
@@ -3461,7 +3419,8 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
#if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING)
SCTP_UNUSED
#endif
- union sctp_sockstore *over_addr
+ union sctp_sockstore *over_addr,
+ struct mbuf *init
)
/* nofragment_flag to tell if IP_DF should be set (IPv4 only) */
{
@@ -3525,6 +3484,20 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
SCTP_BUF_LEN(newm) = len;
SCTP_BUF_NEXT(newm) = m;
m = newm;
+ if (net != NULL) {
+#ifdef INVARIANTS
+ if (net->flowidset == 0) {
+ panic("Flow ID not set");
+ }
+#endif
+ m->m_pkthdr.flowid = net->flowid;
+ m->m_flags |= M_FLOWID;
+ } else {
+ if ((init != NULL) && (init->m_flags & M_FLOWID)) {
+ m->m_pkthdr.flowid = init->m_pkthdr.flowid;
+ m->m_flags |= M_FLOWID;
+ }
+ }
packet_length = sctp_calculate_len(m);
ip = mtod(m, struct ip *);
ip->ip_v = IPVERSION;
@@ -3544,17 +3517,9 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
ip->ip_ttl = inp->ip_inp.inp.inp_ip_ttl;
ip->ip_len = packet_length;
- if (stcb) {
- if ((stcb->asoc.ecn_allowed) && ecn_ok) {
- /* Enable ECN */
- ip->ip_tos = ((u_char)(tos_value & 0xfc) | sctp_get_ect(stcb, chk));
- } else {
- /* No ECN */
- ip->ip_tos = (u_char)(tos_value & 0xfc);
- }
- } else {
- /* no association at all */
- ip->ip_tos = (tos_value & 0xfc);
+ ip->ip_tos = tos_value & 0xfc;
+ if (ecn_ok) {
+ ip->ip_tos |= sctp_get_ect(stcb, chk);
}
if (port) {
ip->ip_p = IPPROTO_UDP;
@@ -3686,7 +3651,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
* Stop any running T3
* timers here?
*/
- if ((stcb->asoc.sctp_cmt_on_off == 1) &&
+ 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",
@@ -3854,6 +3819,20 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
SCTP_BUF_LEN(newm) = len;
SCTP_BUF_NEXT(newm) = m;
m = newm;
+ if (net != NULL) {
+#ifdef INVARIANTS
+ if (net->flowidset == 0) {
+ panic("Flow ID not set");
+ }
+#endif
+ m->m_pkthdr.flowid = net->flowid;
+ m->m_flags |= M_FLOWID;
+ } else {
+ if ((init != NULL) && (init->m_flags & M_FLOWID)) {
+ m->m_pkthdr.flowid = init->m_pkthdr.flowid;
+ m->m_flags |= M_FLOWID;
+ }
+ }
packet_length = sctp_calculate_len(m);
ip6h = mtod(m, struct ip6_hdr *);
@@ -3881,18 +3860,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
} else {
ro = (sctp_route_t *) & net->ro;
}
- if (stcb != NULL) {
- if ((stcb->asoc.ecn_allowed) && ecn_ok) {
- /* Enable ECN */
- tosBottom = (((((struct in6pcb *)inp)->in6p_flowinfo & 0x0c) | sctp_get_ect(stcb, chk)) << 4);
- } else {
- /* No ECN */
- tosBottom = ((((struct in6pcb *)inp)->in6p_flowinfo & 0x0c) << 4);
- }
- } else {
- /* we could get no asoc if it is a O-O-T-B packet */
- tosBottom = ((((struct in6pcb *)inp)->in6p_flowinfo & 0x0c) << 4);
+ tosBottom = (((struct in6pcb *)inp)->in6p_flowinfo & 0x0c);
+ if (ecn_ok) {
+ tosBottom |= sctp_get_ect(stcb, chk);
}
+ tosBottom <<= 4;
ip6h->ip6_flow = htonl(((tosTop << 24) | ((tosBottom | flowTop) << 16) | flowBottom));
if (port) {
ip6h->ip6_nxt = IPPROTO_UDP;
@@ -4167,7 +4139,6 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked
struct sctp_adaptation_layer_indication *ali;
struct sctp_ecn_supported_param *ecn;
struct sctp_prsctp_supported_param *prsctp;
- struct sctp_ecn_nonce_supported_param *ecn_nonce;
struct sctp_supported_chunk_types_param *pr_supported;
int cnt_inits_to = 0;
int padval, ret;
@@ -4290,7 +4261,7 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked
stcb->asoc.cookie_preserve_req = 0;
}
/* ECN parameter */
- if (SCTP_BASE_SYSCTL(sctp_ecn_enable) == 1) {
+ if (stcb->asoc.ecn_allowed == 1) {
ecn->ph.param_type = htons(SCTP_ECN_CAPABLE);
ecn->ph.param_length = htons(sizeof(*ecn));
SCTP_BUF_LEN(m) += sizeof(*ecn);
@@ -4326,14 +4297,6 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked
SCTP_BUF_LEN(m) += SCTP_SIZE32(p_len);
- /* ECN nonce: And now tell the peer we support ECN nonce */
- if (SCTP_BASE_SYSCTL(sctp_ecn_nonce)) {
- ecn_nonce = (struct sctp_ecn_nonce_supported_param *)
- ((caddr_t)pr_supported + SCTP_SIZE32(p_len));
- ecn_nonce->ph.param_type = htons(SCTP_ECN_NONCE_SUPPORTED);
- ecn_nonce->ph.param_length = htons(sizeof(*ecn_nonce));
- SCTP_BUF_LEN(m) += sizeof(*ecn_nonce);
- }
/* add authentication parameters */
if (!SCTP_BASE_SYSCTL(sctp_auth_disable)) {
struct sctp_auth_random *randp;
@@ -4431,7 +4394,7 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked
(struct sockaddr *)&net->ro._l_addr,
m, 0, NULL, 0, 0, 0, NULL, 0,
inp->sctp_lport, stcb->rport, htonl(0),
- net->port, so_locked, NULL);
+ net->port, so_locked, NULL, NULL);
SCTPDBG(SCTP_DEBUG_OUTPUT4, "lowlevel_output - %d\n", ret);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
(void)SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
@@ -4555,11 +4518,10 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt,
case SCTP_HAS_NAT_SUPPORT:
*nat_friendly = 1;
/* fall through */
- case SCTP_ECN_NONCE_SUPPORTED:
case SCTP_PRSCTP_SUPPORTED:
if (padded_size != sizeof(struct sctp_paramhdr)) {
- SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error ecnnonce/prsctp/nat support %d\n", plen);
+ SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error prsctp/nat support %d\n", plen);
goto invalid_size;
}
at += padded_size;
@@ -4960,7 +4922,6 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct sctp_adaptation_layer_indication *ali;
struct sctp_ecn_supported_param *ecn;
struct sctp_prsctp_supported_param *prsctp;
- struct sctp_ecn_nonce_supported_param *ecn_nonce;
struct sctp_supported_chunk_types_param *pr_supported;
union sctp_sockstore store, store1, *over_addr;
struct sockaddr_in *sin, *to_sin;
@@ -5403,7 +5364,8 @@ do_a_abort:
ecn = (struct sctp_ecn_supported_param *)((caddr_t)ali + sizeof(*ali));
/* ECN parameter */
- if (SCTP_BASE_SYSCTL(sctp_ecn_enable) == 1) {
+ if (((asoc != NULL) && (asoc->ecn_allowed == 1)) ||
+ (inp->sctp_ecn_enable == 1)) {
ecn->ph.param_type = htons(SCTP_ECN_CAPABLE);
ecn->ph.param_length = htons(sizeof(*ecn));
SCTP_BUF_LEN(m) += sizeof(*ecn);
@@ -5444,14 +5406,6 @@ do_a_abort:
bzero((caddr_t)pr_supported + p_len, SCTP_SIZE32(p_len) - p_len);
SCTP_BUF_LEN(m) += SCTP_SIZE32(p_len);
- /* ECN nonce: And now tell the peer we support ECN nonce */
- if (SCTP_BASE_SYSCTL(sctp_ecn_nonce)) {
- ecn_nonce = (struct sctp_ecn_nonce_supported_param *)
- ((caddr_t)pr_supported + SCTP_SIZE32(p_len));
- ecn_nonce->ph.param_type = htons(SCTP_ECN_NONCE_SUPPORTED);
- ecn_nonce->ph.param_length = htons(sizeof(*ecn_nonce));
- SCTP_BUF_LEN(m) += sizeof(*ecn_nonce);
- }
/* add authentication parameters */
if (!SCTP_BASE_SYSCTL(sctp_auth_disable)) {
struct sctp_auth_random *randp;
@@ -5611,57 +5565,11 @@ do_a_abort:
(void)sctp_lowlevel_chunk_output(inp, NULL, NULL, to, m, 0, NULL, 0, 0,
0, NULL, 0,
inp->sctp_lport, sh->src_port, init_chk->init.initiate_tag,
- port, SCTP_SO_NOT_LOCKED, over_addr);
+ port, SCTP_SO_NOT_LOCKED, over_addr, init_pkt);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
}
-void
-sctp_insert_on_wheel(struct sctp_tcb *stcb,
- struct sctp_association *asoc,
- struct sctp_stream_out *strq, int holds_lock)
-{
- if (holds_lock == 0) {
- SCTP_TCB_SEND_LOCK(stcb);
- }
- if ((strq->next_spoke.tqe_next == NULL) &&
- (strq->next_spoke.tqe_prev == NULL)) {
- TAILQ_INSERT_TAIL(&asoc->out_wheel, strq, next_spoke);
- }
- if (holds_lock == 0) {
- SCTP_TCB_SEND_UNLOCK(stcb);
- }
-}
-
-void
-sctp_remove_from_wheel(struct sctp_tcb *stcb,
- struct sctp_association *asoc,
- struct sctp_stream_out *strq,
- int holds_lock)
-{
- /* take off and then setup so we know it is not on the wheel */
- if (holds_lock == 0) {
- SCTP_TCB_SEND_LOCK(stcb);
- }
- if (TAILQ_EMPTY(&strq->outqueue)) {
- if (asoc->last_out_stream == strq) {
- asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead, next_spoke);
- if (asoc->last_out_stream == NULL) {
- asoc->last_out_stream = TAILQ_LAST(&asoc->out_wheel, sctpwheel_listhead);
- }
- if (asoc->last_out_stream == strq) {
- asoc->last_out_stream = NULL;
- }
- }
- TAILQ_REMOVE(&asoc->out_wheel, strq, next_spoke);
- strq->next_spoke.tqe_next = NULL;
- strq->next_spoke.tqe_prev = NULL;
- }
- if (holds_lock == 0) {
- SCTP_TCB_SEND_UNLOCK(stcb);
- }
-}
-
static void
sctp_prune_prsctp(struct sctp_tcb *stcb,
struct sctp_association *asoc,
@@ -5718,9 +5626,7 @@ sctp_prune_prsctp(struct sctp_tcb *stcb,
} /* if chunk has enabled */
} /* tailqforeach */
- chk = TAILQ_FIRST(&asoc->send_queue);
- while (chk) {
- nchk = TAILQ_NEXT(chk, sctp_next);
+ TAILQ_FOREACH_SAFE(chk, &asoc->send_queue, sctp_next, nchk) {
/* Here we must move to the sent queue and mark */
if (PR_SCTP_BUF_ENABLED(chk->flags)) {
if (chk->rec.data.timetodrop.tv_sec >= (long)srcv->sinfo_timetolive) {
@@ -5742,8 +5648,7 @@ sctp_prune_prsctp(struct sctp_tcb *stcb,
} /* end if chk->data */
} /* end if right class */
} /* end if chk pr-sctp */
- chk = nchk;
- } /* end while (chk) */
+ } /* tailqforeachsafe (chk) */
} /* if enabled in asoc */
}
@@ -5927,11 +5832,7 @@ sctp_msg_append(struct sctp_tcb *stcb,
sp->strseq = strm->next_sequence_sent;
strm->next_sequence_sent++;
}
- if ((strm->next_spoke.tqe_next == NULL) &&
- (strm->next_spoke.tqe_prev == NULL)) {
- /* Not on wheel, insert */
- sctp_insert_on_wheel(stcb, &stcb->asoc, strm, 1);
- }
+ stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc, strm, sp, 1);
m = NULL;
SCTP_TCB_SEND_UNLOCK(stcb);
out_now:
@@ -6445,9 +6346,7 @@ sctp_toss_old_cookies(struct sctp_tcb *stcb, struct sctp_association *asoc)
{
struct sctp_tmit_chunk *chk, *nchk;
- chk = TAILQ_FIRST(&asoc->control_send_queue);
- while (chk) {
- nchk = TAILQ_NEXT(chk, sctp_next);
+ TAILQ_FOREACH_SAFE(chk, &asoc->control_send_queue, sctp_next, nchk) {
if (chk->rec.chunk_id.id == SCTP_COOKIE_ECHO) {
TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next);
if (chk->data) {
@@ -6457,7 +6356,6 @@ sctp_toss_old_cookies(struct sctp_tcb *stcb, struct sctp_association *asoc)
asoc->ctrl_queue_cnt--;
sctp_free_a_chunk(stcb, chk);
}
- chk = nchk;
}
}
@@ -6465,19 +6363,16 @@ void
sctp_toss_old_asconf(struct sctp_tcb *stcb)
{
struct sctp_association *asoc;
- struct sctp_tmit_chunk *chk, *chk_tmp;
+ struct sctp_tmit_chunk *chk, *nchk;
struct sctp_asconf_chunk *acp;
asoc = &stcb->asoc;
- for (chk = TAILQ_FIRST(&asoc->asconf_send_queue); chk != NULL;
- chk = chk_tmp) {
- /* get next chk */
- chk_tmp = TAILQ_NEXT(chk, sctp_next);
+ TAILQ_FOREACH_SAFE(chk, &asoc->asconf_send_queue, sctp_next, nchk) {
/* find SCTP_ASCONF chunk in queue */
if (chk->rec.chunk_id.id == SCTP_ASCONF) {
if (chk->data) {
acp = mtod(chk->data, struct sctp_asconf_chunk *);
- if (compare_with_wrap(ntohl(acp->serial_number), stcb->asoc.asconf_seq_out_acked, MAX_SEQ)) {
+ if (SCTP_TSN_GT(ntohl(acp->serial_number), asoc->asconf_seq_out_acked)) {
/* Not Acked yet */
break;
}
@@ -6518,6 +6413,7 @@ sctp_clean_up_datalist(struct sctp_tcb *stcb,
}
/* record time */
data_list[i]->sent_rcv_time = net->last_sent_time;
+ data_list[i]->rec.data.cwnd_at_send = net->cwnd;
data_list[i]->rec.data.fast_retran_tsn = data_list[i]->rec.data.TSN_seq;
if (data_list[i]->whoTo == NULL) {
data_list[i]->whoTo = net;
@@ -6525,8 +6421,7 @@ sctp_clean_up_datalist(struct sctp_tcb *stcb,
}
/* on to the sent queue */
tp1 = TAILQ_LAST(&asoc->sent_queue, sctpchunk_listhead);
- if ((tp1) && (compare_with_wrap(tp1->rec.data.TSN_seq,
- data_list[i]->rec.data.TSN_seq, MAX_TSN))) {
+ if ((tp1) && SCTP_TSN_GT(tp1->rec.data.TSN_seq, data_list[i]->rec.data.TSN_seq)) {
struct sctp_tmit_chunk *tpp;
/* need to move back */
@@ -6537,8 +6432,7 @@ sctp_clean_up_datalist(struct sctp_tcb *stcb,
goto all_done;
}
tp1 = tpp;
- if (compare_with_wrap(tp1->rec.data.TSN_seq,
- data_list[i]->rec.data.TSN_seq, MAX_TSN)) {
+ if (SCTP_TSN_GT(tp1->rec.data.TSN_seq, data_list[i]->rec.data.TSN_seq)) {
goto back_up_more;
}
TAILQ_INSERT_AFTER(&asoc->sent_queue, tp1, data_list[i], sctp_next);
@@ -6589,9 +6483,7 @@ sctp_clean_up_ctl(struct sctp_tcb *stcb, struct sctp_association *asoc)
{
struct sctp_tmit_chunk *chk, *nchk;
- for (chk = TAILQ_FIRST(&asoc->control_send_queue);
- chk; chk = nchk) {
- nchk = TAILQ_NEXT(chk, sctp_next);
+ TAILQ_FOREACH_SAFE(chk, &asoc->control_send_queue, sctp_next, nchk) {
if ((chk->rec.chunk_id.id == SCTP_SELECTIVE_ACK) ||
(chk->rec.chunk_id.id == SCTP_NR_SELECTIVE_ACK) || /* EY */
(chk->rec.chunk_id.id == SCTP_HEARTBEAT_REQUEST) ||
@@ -6756,6 +6648,7 @@ one_more_time:
}
atomic_subtract_int(&asoc->stream_queue_cnt, 1);
TAILQ_REMOVE(&strq->outqueue, sp, next);
+ stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up);
if (sp->net) {
sctp_free_remote_addr(sp->net);
sp->net = NULL;
@@ -7071,7 +6964,6 @@ dont_do_it:
chk->rec.data.payloadtype = sp->ppid;
chk->rec.data.context = sp->context;
chk->rec.data.doing_fast_retransmit = 0;
- chk->rec.data.ect_nonce = 0; /* ECN Nonce */
chk->rec.data.timetodrop = sp->ts;
chk->flags = sp->act_flags;
@@ -7165,6 +7057,7 @@ dont_do_it:
send_lock_up = 1;
}
TAILQ_REMOVE(&strq->outqueue, sp, next);
+ stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up);
if (sp->net) {
sctp_free_remote_addr(sp->net);
sp->net = NULL;
@@ -7194,24 +7087,6 @@ out_of:
}
-static struct sctp_stream_out *
-sctp_select_a_stream(struct sctp_tcb *stcb, struct sctp_association *asoc)
-{
- struct sctp_stream_out *strq;
-
- /* Find the next stream to use */
- if (asoc->last_out_stream == NULL) {
- strq = TAILQ_FIRST(&asoc->out_wheel);
- } else {
- strq = TAILQ_NEXT(asoc->last_out_stream, next_spoke);
- if (strq == NULL) {
- strq = TAILQ_FIRST(&asoc->out_wheel);
- }
- }
- return (strq);
-}
-
-
static void
sctp_fill_outqueue(struct sctp_tcb *stcb,
struct sctp_nets *net, int frag_point, int eeor_mode, int *quit_now)
@@ -7220,7 +7095,6 @@ sctp_fill_outqueue(struct sctp_tcb *stcb,
struct sctp_stream_out *strq, *strqn;
int goal_mtu, moved_how_much, total_moved = 0, bail = 0;
int locked, giveup;
- struct sctp_stream_queue_pending *sp;
SCTP_TCB_LOCK_ASSERT(stcb);
asoc = &stcb->asoc;
@@ -7244,46 +7118,17 @@ sctp_fill_outqueue(struct sctp_tcb *stcb,
strq = asoc->locked_on_sending;
locked = 1;
} else {
- strq = sctp_select_a_stream(stcb, asoc);
+ strq = stcb->asoc.ss_functions.sctp_ss_select_stream(stcb, net, asoc);
locked = 0;
}
strqn = strq;
while ((goal_mtu > 0) && strq) {
- sp = TAILQ_FIRST(&strq->outqueue);
- if (sp == NULL) {
- break;
- }
- /**
- * Honor the users' choice if given. If not given,
- * pull it only to the primary path in case of not using
- * CMT.
- */
- if (((sp->net != NULL) &&
- (sp->net != net)) ||
- ((sp->net == NULL) &&
- (asoc->sctp_cmt_on_off == 0) &&
- (asoc->primary_destination != net))) {
- /* Do not pull to this network */
- if (locked) {
- break;
- } else {
- strq = sctp_select_a_stream(stcb, asoc);
- if (strq == NULL)
- /* none left */
- break;
- if (strqn == strq) {
- /* I have circled */
- break;
- }
- continue;
- }
- }
giveup = 0;
bail = 0;
moved_how_much = sctp_move_to_outqueue(stcb, strq, goal_mtu, frag_point, &locked,
&giveup, eeor_mode, &bail);
if (moved_how_much)
- asoc->last_out_stream = strq;
+ stcb->asoc.ss_functions.sctp_ss_scheduled(stcb, net, asoc, strq, moved_how_much);
if (locked) {
asoc->locked_on_sending = strq;
@@ -7292,23 +7137,10 @@ sctp_fill_outqueue(struct sctp_tcb *stcb,
break;
} else {
asoc->locked_on_sending = NULL;
- if (TAILQ_EMPTY(&strq->outqueue)) {
- if (strq == strqn) {
- /* Must move start to next one */
- strqn = TAILQ_NEXT(strq, next_spoke);
- if (strqn == NULL) {
- strqn = TAILQ_FIRST(&asoc->out_wheel);
- if (strqn == NULL) {
- break;
- }
- }
- }
- sctp_remove_from_wheel(stcb, asoc, strq, 0);
- }
if ((giveup) || bail) {
break;
}
- strq = sctp_select_a_stream(stcb, asoc);
+ strq = stcb->asoc.ss_functions.sctp_ss_select_stream(stcb, net, asoc);
if (strq == NULL) {
break;
}
@@ -7320,12 +7152,14 @@ sctp_fill_outqueue(struct sctp_tcb *stcb,
if (bail)
*quit_now = 1;
+ stcb->asoc.ss_functions.sctp_ss_packet_done(stcb, net, asoc);
+
if (total_moved == 0) {
if ((stcb->asoc.sctp_cmt_on_off == 0) &&
(net == stcb->asoc.primary_destination)) {
/* ran dry for primary network net */
SCTP_STAT_INCR(sctps_primary_randry);
- } else if (stcb->asoc.sctp_cmt_on_off == 1) {
+ } else if (stcb->asoc.sctp_cmt_on_off > 0) {
/* ran dry with CMT on */
SCTP_STAT_INCR(sctps_cmt_randry);
}
@@ -7348,16 +7182,16 @@ void
sctp_move_chunks_from_net(struct sctp_tcb *stcb, struct sctp_nets *net)
{
struct sctp_association *asoc;
- struct sctp_stream_out *outs;
struct sctp_tmit_chunk *chk;
struct sctp_stream_queue_pending *sp;
+ unsigned int i;
if (net == NULL) {
return;
}
asoc = &stcb->asoc;
- TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) {
- TAILQ_FOREACH(sp, &outs->outqueue, next) {
+ for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
+ TAILQ_FOREACH(sp, &stcb->asoc.strmout[i].outqueue, next) {
if (sp->net == net) {
sctp_free_remote_addr(sp->net);
sp->net = NULL;
@@ -7394,7 +7228,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;
@@ -7447,10 +7281,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) &&
- TAILQ_EMPTY(&asoc->out_wheel)) {
+ stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) {
+nothing_to_send:
*reason_code = 9;
return (0);
}
@@ -7462,12 +7298,28 @@ 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;
else
max_send_per_dest = 0;
- if ((no_data_chunks == 0) && (!TAILQ_EMPTY(&asoc->out_wheel))) {
+ if ((no_data_chunks == 0) &&
+ (!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc))) {
TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
/*
* This for loop we are in takes in each net, if
@@ -7526,7 +7378,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
*reason_code = 8;
return (0);
}
- if (asoc->sctp_cmt_on_off == 1) {
+ if (asoc->sctp_cmt_on_off > 0) {
/* get the last start point */
start_at = asoc->last_net_cmt_send_started;
if (start_at == NULL) {
@@ -7627,9 +7479,7 @@ again_one_more_time:
/* ASCONF transmission */
/************************/
/* Now first lets go through the asconf queue */
- for (chk = TAILQ_FIRST(&asoc->asconf_send_queue);
- chk; chk = nchk) {
- nchk = TAILQ_NEXT(chk, sctp_next);
+ TAILQ_FOREACH_SAFE(chk, &asoc->asconf_send_queue, sctp_next, nchk) {
if (chk->rec.chunk_id.id != SCTP_ASCONF) {
continue;
}
@@ -7743,7 +7593,7 @@ again_one_more_time:
no_fragmentflg, 0, NULL, asconf,
inp->sctp_lport, stcb->rport,
htonl(stcb->asoc.peer_vtag),
- net->port, so_locked, NULL))) {
+ net->port, so_locked, NULL, NULL))) {
if (error == ENOBUFS) {
asoc->ifp_had_enobuf = 1;
SCTP_STAT_INCR(sctps_lowlevelerr);
@@ -7805,9 +7655,29 @@ again_one_more_time:
/* Control transmission */
/************************/
/* Now first lets go through the control queue */
- for (chk = TAILQ_FIRST(&asoc->control_send_queue);
- chk; chk = nchk) {
- nchk = TAILQ_NEXT(chk, sctp_next);
+ 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
@@ -7815,6 +7685,7 @@ again_one_more_time:
*/
continue;
}
+ skip_net_check:
if (chk->data == NULL) {
continue;
}
@@ -7932,6 +7803,20 @@ again_one_more_time:
if (chk->rec.chunk_id.id == SCTP_COOKIE_ECHO) {
cookie = 1;
no_out_cnt = 1;
+ } else if (chk->rec.chunk_id.id == SCTP_ECN_ECHO) {
+ /*
+ * Increment ecne send count
+ * here this means we may be
+ * over-zealous in our
+ * counting if the send
+ * fails, but its the best
+ * place to do it (we used
+ * to do it in the queue of
+ * the chunk, but that did
+ * not tell how many times
+ * it was sent.
+ */
+ SCTP_STAT_INCR(sctps_sendecne);
}
chk->sent = SCTP_DATAGRAM_SENT;
chk->snd_count++;
@@ -7964,7 +7849,7 @@ again_one_more_time:
no_fragmentflg, 0, NULL, asconf,
inp->sctp_lport, stcb->rport,
htonl(stcb->asoc.peer_vtag),
- net->port, so_locked, NULL))) {
+ net->port, so_locked, NULL, NULL))) {
if (error == ENOBUFS) {
asoc->ifp_had_enobuf = 1;
SCTP_STAT_INCR(sctps_lowlevelerr);
@@ -8028,7 +7913,7 @@ again_one_more_time:
}
}
/* JRI: if dest is in PF state, do not send data to it */
- if ((asoc->sctp_cmt_on_off == 1) &&
+ if ((asoc->sctp_cmt_on_off > 0) &&
(asoc->sctp_cmt_pf > 0) &&
(net->dest_state & SCTP_ADDR_PF)) {
goto no_data_fill;
@@ -8036,7 +7921,7 @@ again_one_more_time:
if (net->flight_size >= net->cwnd) {
goto no_data_fill;
}
- if ((asoc->sctp_cmt_on_off == 1) &&
+ if ((asoc->sctp_cmt_on_off > 0) &&
(SCTP_BASE_SYSCTL(sctp_buffer_splitting) & SCTP_RECV_BUFFER_SPLITTING) &&
(net->flight_size > max_rwnd_per_dest)) {
goto no_data_fill;
@@ -8047,7 +7932,7 @@ again_one_more_time:
* net. For now, this is better than nothing and it disabled
* by default...
*/
- if ((asoc->sctp_cmt_on_off == 1) &&
+ if ((asoc->sctp_cmt_on_off > 0) &&
(SCTP_BASE_SYSCTL(sctp_buffer_splitting) & SCTP_SEND_BUFFER_SPLITTING) &&
(max_send_per_dest > 0) &&
(net->flight_size > max_send_per_dest)) {
@@ -8091,7 +7976,7 @@ again_one_more_time:
if ((((asoc->state & SCTP_STATE_OPEN) == SCTP_STATE_OPEN) &&
(skip_data_for_this_net == 0)) ||
(cookie)) {
- for (chk = TAILQ_FIRST(&asoc->send_queue); chk; chk = nchk) {
+ TAILQ_FOREACH_SAFE(chk, &asoc->send_queue, sctp_next, nchk) {
if (no_data_chunks) {
/* let only control go out */
*reason_code = 1;
@@ -8102,7 +7987,6 @@ again_one_more_time:
*reason_code = 2;
break;
}
- nchk = TAILQ_NEXT(chk, sctp_next);
if ((chk->whoTo != NULL) &&
(chk->whoTo != net)) {
/* Don't send the chunk on this net */
@@ -8268,7 +8152,7 @@ no_data_fill:
* restart it.
*/
sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net);
- } else if ((asoc->sctp_cmt_on_off == 1) &&
+ } else if ((asoc->sctp_cmt_on_off > 0) &&
(asoc->sctp_cmt_pf > 0) &&
pf_hbflag &&
((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF) &&
@@ -8296,7 +8180,7 @@ no_data_fill:
asconf,
inp->sctp_lport, stcb->rport,
htonl(stcb->asoc.peer_vtag),
- net->port, so_locked, NULL))) {
+ net->port, so_locked, NULL, NULL))) {
/* error, we could not output */
if (error == ENOBUFS) {
SCTP_STAT_INCR(sctps_lowlevelerr);
@@ -9006,7 +8890,7 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
auth_offset, auth, stcb->asoc.authinfo.active_keyid,
no_fragmentflg, 0, NULL, 0,
inp->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag),
- chk->whoTo->port, so_locked, NULL))) {
+ chk->whoTo->port, so_locked, NULL, NULL))) {
SCTP_STAT_INCR(sctps_lowlevelerr);
return (error);
}
@@ -9264,7 +9148,7 @@ one_chunk_around:
auth_offset, auth, auth_keyid,
no_fragmentflg, 0, NULL, 0,
inp->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag),
- net->port, so_locked, NULL))) {
+ net->port, so_locked, NULL, NULL))) {
/* error, we could not output */
SCTP_STAT_INCR(sctps_lowlevelerr);
return (error);
@@ -9445,14 +9329,15 @@ sctp_chunk_output(struct sctp_inpcb *inp,
*/
struct sctp_association *asoc;
struct sctp_nets *net;
- int error = 0, num_out = 0, tot_out = 0, ret = 0, reason_code = 0,
- burst_cnt = 0, burst_limit = 0;
+ int error = 0, num_out = 0, tot_out = 0, ret = 0, reason_code = 0;
+ unsigned int burst_cnt = 0;
struct timeval now;
int now_filled = 0;
int nagle_on = 0;
int frag_point = sctp_get_frag_point(stcb, &stcb->asoc);
int un_sent = 0;
- int fr_done, tot_frs = 0;
+ int fr_done;
+ unsigned int tot_frs = 0;
asoc = &stcb->asoc;
if (from_where == SCTP_OUTPUT_FROM_USR_SEND) {
@@ -9549,12 +9434,11 @@ sctp_chunk_output(struct sctp_inpcb *inp,
&now, &now_filled, frag_point, so_locked);
return;
}
- if (tot_frs > asoc->max_burst) {
+ if ((asoc->fr_max_burst > 0) && (tot_frs >= asoc->fr_max_burst)) {
/* Hit FR burst limit */
return;
}
if ((num_out == 0) && (ret == 0)) {
-
/* No more retrans to send */
break;
}
@@ -9563,7 +9447,6 @@ sctp_chunk_output(struct sctp_inpcb *inp,
sctp_auditing(12, inp, stcb, NULL);
#endif
/* Check for bad destinations, if they exist move chunks around. */
- burst_limit = asoc->max_burst;
TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
if ((net->dest_state & SCTP_ADDR_NOT_REACHABLE) ==
SCTP_ADDR_NOT_REACHABLE) {
@@ -9576,7 +9459,7 @@ 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 == 1) &&
+ } else if ((asoc->sctp_cmt_on_off > 0) &&
(asoc->sctp_cmt_pf > 0) &&
((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF)) {
/*
@@ -9592,24 +9475,29 @@ sctp_chunk_output(struct sctp_inpcb *inp,
* { burst_limit = asoc->max_burst *
* SCTP_SAT_NETWORK_BURST_INCR; }
*/
- if (SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst)) {
- if ((net->flight_size + (burst_limit * net->mtu)) < net->cwnd) {
- /*
- * JRS - Use the congestion control
- * given in the congestion control
- * module
- */
- asoc->cc_functions.sctp_cwnd_update_after_output(stcb, net, burst_limit);
- if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_MAXBURST_ENABLE) {
- sctp_log_maxburst(stcb, net, 0, burst_limit, SCTP_MAX_BURST_APPLIED);
+ if (asoc->max_burst > 0) {
+ if (SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst)) {
+ if ((net->flight_size + (asoc->max_burst * net->mtu)) < net->cwnd) {
+ /*
+ * JRS - Use the congestion
+ * control given in the
+ * congestion control module
+ */
+ asoc->cc_functions.sctp_cwnd_update_after_output(stcb, net, asoc->max_burst);
+ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_MAXBURST_ENABLE) {
+ sctp_log_maxburst(stcb, net, 0, asoc->max_burst, SCTP_MAX_BURST_APPLIED);
+ }
+ SCTP_STAT_INCR(sctps_maxburstqueued);
+ }
+ net->fast_retran_ip = 0;
+ } else {
+ if (net->flight_size == 0) {
+ /*
+ * Should be decaying the
+ * cwnd here
+ */
+ ;
}
- SCTP_STAT_INCR(sctps_maxburstqueued);
- }
- net->fast_retran_ip = 0;
- } else {
- if (net->flight_size == 0) {
- /* Should be decaying the cwnd here */
- ;
}
}
}
@@ -9656,7 +9544,7 @@ sctp_chunk_output(struct sctp_inpcb *inp,
}
if (TAILQ_EMPTY(&asoc->control_send_queue) &&
TAILQ_EMPTY(&asoc->send_queue) &&
- TAILQ_EMPTY(&asoc->out_wheel)) {
+ stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) {
/* Nothing left to send */
break;
}
@@ -9664,11 +9552,13 @@ sctp_chunk_output(struct sctp_inpcb *inp,
/* Nothing left to send */
break;
}
- } while (num_out && (SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst) ||
- (burst_cnt < burst_limit)));
+ } while (num_out &&
+ ((asoc->max_burst == 0) ||
+ SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst) ||
+ (burst_cnt < asoc->max_burst)));
if (SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst) == 0) {
- if (burst_cnt >= burst_limit) {
+ if ((asoc->max_burst > 0) && (burst_cnt >= asoc->max_burst)) {
SCTP_STAT_INCR(sctps_maxburstqueued);
asoc->burst_limit_applied = 1;
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_MAXBURST_ENABLE) {
@@ -10018,7 +9908,7 @@ sctp_send_sack(struct sctp_tcb *stcb)
if (a_chk->whoTo) {
atomic_add_int(&a_chk->whoTo->ref_count, 1);
}
- if (compare_with_wrap(asoc->highest_tsn_inside_map, asoc->highest_tsn_inside_nr_map, MAX_TSN)) {
+ if (SCTP_TSN_GT(asoc->highest_tsn_inside_map, asoc->highest_tsn_inside_nr_map)) {
highest_tsn = asoc->highest_tsn_inside_map;
} else {
highest_tsn = asoc->highest_tsn_inside_nr_map;
@@ -10065,15 +9955,9 @@ sctp_send_sack(struct sctp_tcb *stcb)
limit = mtod(a_chk->data, caddr_t);
limit += space;
- /* 0x01 is used by nonce for ecn */
- if ((SCTP_BASE_SYSCTL(sctp_ecn_enable)) &&
- (SCTP_BASE_SYSCTL(sctp_ecn_nonce)) &&
- (asoc->peer_supports_ecn_nonce))
- flags = (asoc->receiver_nonce_sum & SCTP_SACK_NONCE_SUM);
- else
- flags = 0;
+ flags = 0;
- if ((asoc->sctp_cmt_on_off == 1) &&
+ if ((asoc->sctp_cmt_on_off > 0) &&
SCTP_BASE_SYSCTL(sctp_cmt_use_dac)) {
/*-
* CMT DAC algorithm: If 2 (i.e., 0x10) packets have been
@@ -10113,15 +9997,15 @@ sctp_send_sack(struct sctp_tcb *stcb)
}
}
- if (compare_with_wrap(asoc->mapping_array_base_tsn, asoc->cumulative_tsn, MAX_TSN)) {
+ if (SCTP_TSN_GT(asoc->mapping_array_base_tsn, asoc->cumulative_tsn)) {
offset = 1;
} else {
offset = asoc->mapping_array_base_tsn - asoc->cumulative_tsn;
}
if (((type == SCTP_SELECTIVE_ACK) &&
- compare_with_wrap(highest_tsn, asoc->cumulative_tsn, MAX_TSN)) ||
+ SCTP_TSN_GT(highest_tsn, asoc->cumulative_tsn)) ||
((type == SCTP_NR_SELECTIVE_ACK) &&
- compare_with_wrap(asoc->highest_tsn_inside_map, asoc->cumulative_tsn, MAX_TSN))) {
+ SCTP_TSN_GT(asoc->highest_tsn_inside_map, asoc->cumulative_tsn))) {
/* we have a gap .. maybe */
for (i = 0; i < siz; i++) {
tsn_map = asoc->mapping_array[i];
@@ -10193,12 +10077,12 @@ sctp_send_sack(struct sctp_tcb *stcb)
siz = (((MAX_TSN - asoc->mapping_array_base_tsn) + 1) + asoc->highest_tsn_inside_nr_map + 7) / 8;
}
- if (compare_with_wrap(asoc->mapping_array_base_tsn, asoc->cumulative_tsn, MAX_TSN)) {
+ if (SCTP_TSN_GT(asoc->mapping_array_base_tsn, asoc->cumulative_tsn)) {
offset = 1;
} else {
offset = asoc->mapping_array_base_tsn - asoc->cumulative_tsn;
}
- if (compare_with_wrap(asoc->highest_tsn_inside_nr_map, asoc->cumulative_tsn, MAX_TSN)) {
+ if (SCTP_TSN_GT(asoc->highest_tsn_inside_nr_map, asoc->cumulative_tsn)) {
/* we have a gap .. maybe */
for (i = 0; i < siz; i++) {
tsn_map = asoc->nr_mapping_array[i];
@@ -10377,7 +10261,7 @@ sctp_send_abort_tcb(struct sctp_tcb *stcb, struct mbuf *operr, int so_locked
(struct sockaddr *)&stcb->asoc.primary_destination->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);
+ stcb->asoc.primary_destination->port, so_locked, NULL, NULL);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
}
@@ -10414,7 +10298,7 @@ sctp_send_shutdown_complete(struct sctp_tcb *stcb,
m_shutdown_comp, 0, NULL, 0, 1, 0, NULL, 0,
stcb->sctp_ep->sctp_lport, stcb->rport,
htonl(vtag),
- net->port, SCTP_SO_NOT_LOCKED, NULL);
+ net->port, SCTP_SO_NOT_LOCKED, NULL, NULL);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
return;
}
@@ -10459,6 +10343,10 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh,
SCTP_BUF_RESV_UF(mout, max_linkhdr);
SCTP_BUF_LEN(mout) = len;
SCTP_BUF_NEXT(mout) = NULL;
+ if (m->m_flags & M_FLOWID) {
+ mout->m_pkthdr.flowid = m->m_pkthdr.flowid;
+ mout->m_flags |= M_FLOWID;
+ }
iph_out = NULL;
#ifdef INET6
ip6_out = NULL;
@@ -10866,10 +10754,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);
+ ctsn = ntohl(ecne->tsn);
+ if (SCTP_TSN_GT(high_tsn, ctsn)) {
+ ecne->tsn = htonl(high_tsn);
+ SCTP_STAT_INCR(sctps_queue_upd_ecne);
+ }
+ cnt = ntohl(ecne->num_pkts_since_cwr);
+ cnt++;
+ ecne->num_pkts_since_cwr = htonl(cnt);
return;
}
}
@@ -10879,7 +10776,7 @@ sctp_send_ecn_echo(struct sctp_tcb *stcb, struct sctp_nets *net,
return;
}
chk->copy_by_ref = 0;
- SCTP_STAT_INCR(sctps_sendecne);
+ SCTP_STAT_INCR(sctps_queue_upd_ecne);
chk->rec.chunk_id.id = SCTP_ECN_ECHO;
chk->rec.chunk_id.can_take_data = 0;
chk->asoc = &stcb->asoc;
@@ -10901,7 +10798,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++;
}
@@ -11079,7 +10977,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;
@@ -11087,18 +10985,28 @@ 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 */
+ if ((chk->rec.chunk_id.id == SCTP_ECN_CWR) && (net == chk->whoTo)) {
+ /*
+ * found a previous CWR queued to same destination
+ * update it if needed
+ */
+ uint32_t ctsn;
+
cwr = mtod(chk->data, struct sctp_cwr_chunk *);
- if (compare_with_wrap(high_tsn, ntohl(cwr->tsn),
- MAX_TSN)) {
+ ctsn = ntohl(cwr->tsn);
+ if (SCTP_TSN_GT(high_tsn, ctsn)) {
cwr->tsn = htonl(high_tsn);
}
+ if (override & SCTP_CWR_REDUCE_OVERRIDE) {
+ /* Make sure override is carried */
+ cwr->ch.chunk_flags |= SCTP_CWR_REDUCE_OVERRIDE;
+ }
return;
}
}
- /* nope could not find one to update so we must build one */
sctp_alloc_a_chunk(stcb, chk);
if (chk == NULL) {
return;
@@ -11121,7 +11029,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);
@@ -11503,6 +11411,10 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
SCTP_BUF_RESV_UF(mout, max_linkhdr);
SCTP_BUF_LEN(mout) = len;
SCTP_BUF_NEXT(mout) = err_cause;
+ if (m->m_flags & M_FLOWID) {
+ mout->m_pkthdr.flowid = m->m_pkthdr.flowid;
+ mout->m_flags |= M_FLOWID;
+ }
iph_out = NULL;
#ifdef INET6
ip6_out = NULL;
@@ -11748,6 +11660,10 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag,
SCTP_BUF_RESV_UF(mout, max_linkhdr);
SCTP_BUF_LEN(mout) = len;
SCTP_BUF_NEXT(mout) = scm;
+ if (m->m_flags & M_FLOWID) {
+ mout->m_pkthdr.flowid = m->m_pkthdr.flowid;
+ mout->m_flags |= M_FLOWID;
+ }
iph_out = NULL;
#ifdef INET6
ip6_out = NULL;
@@ -12466,8 +12382,7 @@ sctp_lower_sosend(struct socket *so,
TAILQ_INIT(&asoc->strmout[i].outqueue);
asoc->strmout[i].stream_no = i;
asoc->strmout[i].last_msg_incomplete = 0;
- asoc->strmout[i].next_spoke.tqe_next = 0;
- asoc->strmout[i].next_spoke.tqe_prev = 0;
+ asoc->ss_functions.sctp_ss_init_stream(&asoc->strmout[i], NULL);
}
}
}
@@ -12860,11 +12775,7 @@ skip_preblock:
SCTP_STAT_INCR(sctps_sends_with_unord);
}
TAILQ_INSERT_TAIL(&strm->outqueue, sp, next);
- if ((strm->next_spoke.tqe_next == NULL) &&
- (strm->next_spoke.tqe_prev == NULL)) {
- /* Not on wheel, insert */
- sctp_insert_on_wheel(stcb, asoc, strm, 1);
- }
+ stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, asoc, strm, sp, 1);
SCTP_TCB_SEND_UNLOCK(stcb);
} else {
SCTP_TCB_SEND_LOCK(stcb);
OpenPOWER on IntegriCloud