summaryrefslogtreecommitdiffstats
path: root/sys/netinet/sctp_output.c
diff options
context:
space:
mode:
authorrrs <rrs@FreeBSD.org>2007-07-14 09:36:28 +0000
committerrrs <rrs@FreeBSD.org>2007-07-14 09:36:28 +0000
commit1e9af2c480a638933876e637c1f3b9f1b7bad3e9 (patch)
tree008308315d74a9df2eec618692844a82f1ace245 /sys/netinet/sctp_output.c
parent0948603d9eefb8b7f5898f99c8703b2e5b7f9647 (diff)
downloadFreeBSD-src-1e9af2c480a638933876e637c1f3b9f1b7bad3e9.zip
FreeBSD-src-1e9af2c480a638933876e637c1f3b9f1b7bad3e9.tar.gz
- Modular congestion control, with RFC2581 being the default.
- CMT_PF states added (w/sysctl to turn the PF version on) - sctp_input.c had a missing incr of cookie case when the auth was bad. This meant a free was called without an increment to refcnt, added increment like rest of code. - There was a case, unlikely, when the scope of the destination changed (this is a TSNH case). In that case, it would not free the alloc'ed asoc (in sctp_input.c). - When listed addresses found a colliding cookie/Init, then the collided upon tcb was not unlocked in sctp_pcb.c - Add error checking on arguments of sctp_sendx(3) to prevent it from referencing a NULL pointer. - Fix an error return of sctp_sendx(3), it was returing ENOMEM not -1. - Get assoc id was changed to use the sanctified socket api method for getting a assoc id (PEER_ADDR_INFO instead of PEER_ADDR_PARAMS). - Fix it so a peeled off socket will get a proper error return if it trys to send to a different address then it is connected to. - Fix so that select_a_stream can avoid an endless loop that could hang a caller. - time_entered (state set time) was not being set in all cases to the time we went established. Approved by: re(ken smith)
Diffstat (limited to 'sys/netinet/sctp_output.c')
-rw-r--r--sys/netinet/sctp_output.c201
1 files changed, 160 insertions, 41 deletions
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index 7234ce7..0488763 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -3413,6 +3413,35 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
(void *)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 (sctp_cmt_pf) {
+ net->dest_state &= ~SCTP_ADDR_PF;
+ SCTPDBG(SCTP_DEBUG_OUTPUT1, "Destination %p moved from PF to unreachable.\n",
+ net);
+ }
}
}
if (stcb) {
@@ -6109,7 +6138,8 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net,
uint32_t frag_point,
int *locked,
int *giveup,
- int eeor_mode)
+ int eeor_mode,
+ int *bail)
{
/* Move from the stream to the send_queue keeping track of the total */
struct sctp_association *asoc;
@@ -6309,6 +6339,7 @@ out_gu:
if (chk->data == NULL) {
sp->some_taken = some_taken;
sctp_free_a_chunk(stcb, chk);
+ *bail = 1;
goto out_gu;
}
/* Pull off the data */
@@ -6381,6 +6412,7 @@ out_gu:
sp->some_taken = some_taken;
atomic_add_int(&sp->length, to_move);
chk->data = NULL;
+ *bail = 1;
sctp_free_a_chunk(stcb, chk);
goto out_gu;
} else {
@@ -6399,6 +6431,7 @@ out_gu:
SCTP_PRINTF("prepend fails HELP?\n");
sctp_free_a_chunk(stcb, chk);
#endif
+ *bail = 1;
goto out_gu;
}
sctp_snd_sb_alloc(stcb, sizeof(struct sctp_data_chunk));
@@ -6554,17 +6587,19 @@ done_it:
if (strq == NULL) {
strq = asoc->last_out_stream = TAILQ_FIRST(&asoc->out_wheel);
}
+ /* Save off the last stream */
+ asoc->last_out_stream = strq;
return (strq);
}
static void
sctp_fill_outqueue(struct sctp_tcb *stcb,
- struct sctp_nets *net, int frag_point, int eeor_mode)
+ struct sctp_nets *net, int frag_point, int eeor_mode, int *quit_now)
{
struct sctp_association *asoc;
struct sctp_stream_out *strq, *strqn, *strqt;
- int goal_mtu, moved_how_much, total_moved = 0;
+ int goal_mtu, moved_how_much, total_moved = 0, bail = 0;
int locked, giveup;
struct sctp_stream_queue_pending *sp;
@@ -6627,12 +6662,13 @@ sctp_fill_outqueue(struct sctp_tcb *stcb,
}
}
giveup = 0;
+ bail = 0;
moved_how_much = sctp_move_to_outqueue(stcb, net, strq, goal_mtu, frag_point, &locked,
- &giveup, eeor_mode);
+ &giveup, eeor_mode, &bail);
asoc->last_out_stream = strq;
if (locked) {
asoc->locked_on_sending = strq;
- if ((moved_how_much == 0) || (giveup))
+ if ((moved_how_much == 0) || (giveup) || bail)
/* no more to move for now */
break;
} else {
@@ -6651,7 +6687,7 @@ sctp_fill_outqueue(struct sctp_tcb *stcb,
}
sctp_remove_from_wheel(stcb, asoc, strq);
}
- if (giveup) {
+ if ((giveup) || bail) {
break;
}
strq = strqt;
@@ -6663,6 +6699,9 @@ sctp_fill_outqueue(struct sctp_tcb *stcb,
goal_mtu -= (moved_how_much + sizeof(struct sctp_data_chunk));
goal_mtu &= 0xfffffffc;
}
+ if (bail)
+ *quit_now = 1;
+
if (total_moved == 0) {
if ((sctp_cmt_on_off == 0) &&
(net == stcb->asoc.primary_destination)) {
@@ -6696,7 +6735,16 @@ sctp_move_to_an_alt(struct sctp_tcb *stcb,
struct sctp_nets *a_net;
SCTP_TCB_LOCK_ASSERT(stcb);
- a_net = sctp_find_alternate_net(stcb, net, 0);
+ /*
+ * JRS 5/14/07 - If CMT PF is turned on, find an alternate
+ * destination using the PF algorithm for finding alternate
+ * destinations.
+ */
+ if (sctp_cmt_pf) {
+ a_net = sctp_find_alternate_net(stcb, net, 2);
+ } else {
+ a_net = sctp_find_alternate_net(stcb, net, 0);
+ }
if ((a_net != net) &&
((a_net->dest_state & SCTP_ADDR_REACHABLE) == SCTP_ADDR_REACHABLE)) {
/*
@@ -6751,6 +6799,13 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
uint32_t auth_offset = 0;
struct sctp_auth_chunk *auth = NULL;
+ /*
+ * 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;
cwnd_full_ind = 0;
@@ -6849,13 +6904,19 @@ one_more_time:
if (sctp_logging_level & SCTP_CWND_LOGGING_ENABLE) {
sctp_log_cwnd(stcb, net, 0, SCTP_CWND_LOG_FILL_OUTQ_CALLED);
}
- sctp_fill_outqueue(stcb, net, frag_point, eeor_mode);
+ sctp_fill_outqueue(stcb, net, frag_point, eeor_mode, &quit_now);
+ if (quit_now) {
+ /* memory alloc failure */
+ no_data_chunks = 1;
+ goto skip_the_fill_from_streams;
+ }
}
if (start_at != TAILQ_FIRST(&asoc->nets)) {
/* got to pick up the beginning stuff. */
old_startat = start_at;
start_at = net = TAILQ_FIRST(&asoc->nets);
- goto one_more_time;
+ if (old_startat)
+ goto one_more_time;
}
}
skip_the_fill_from_streams:
@@ -6867,7 +6928,11 @@ skip_the_fill_from_streams:
*reason_code = 8;
return (0);
}
- chk = TAILQ_FIRST(&asoc->send_queue);
+ if (no_data_chunks) {
+ chk = TAILQ_FIRST(&asoc->control_send_queue);
+ } else {
+ chk = TAILQ_FIRST(&asoc->send_queue);
+ }
if (chk) {
send_start_at = chk->whoTo;
} else {
@@ -7034,8 +7099,15 @@ again_one_more_time:
(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)
+ 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) {
/* turn off the timer */
@@ -7338,6 +7410,15 @@ again_one_more_time:
* restart it.
*/
sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net);
+ } else if (sctp_cmt_pf && 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 :> */
SCTP_BUF_PREPEND(outchain, sizeof(struct sctphdr), M_DONTWAIT);
@@ -7422,7 +7503,14 @@ again_one_more_time:
if (bundle_at) {
/* setup for a RTO measurement */
tsns_sent = data_list[0]->rec.data.TSN_seq;
-
+ /* fill time if not already filled */
+ if (*now_filled == 0) {
+ (void)SCTP_GETTIME_TIMEVAL(&asoc->time_last_sent);
+ *now_filled = 1;
+ *now = asoc->time_last_sent;
+ } else {
+ asoc->time_last_sent = *now;
+ }
data_list[0]->do_rtt = 1;
SCTP_STAT_INCR_BY(sctps_senddata, bundle_at);
sctp_clean_up_datalist(stcb, asoc, data_list, bundle_at, net);
@@ -7456,7 +7544,8 @@ again_one_more_time:
if (old_startat == NULL) {
old_startat = send_start_at;
send_start_at = TAILQ_FIRST(&asoc->nets);
- goto again_one_more_time;
+ if (old_startat)
+ goto again_one_more_time;
}
/*
* At the end there should be no NON timed chunks hanging on this
@@ -8570,6 +8659,15 @@ sctp_chunk_output(struct sctp_inpcb *inp,
*/
if (net->ref_count > 1)
sctp_move_to_an_alt(stcb, asoc, net);
+ } else if (sctp_cmt_pf && ((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_to_an_alt(stcb, asoc, net);
} else {
/*-
* if ((asoc->sat_network) || (net->addr_is_local))
@@ -8578,16 +8676,12 @@ sctp_chunk_output(struct sctp_inpcb *inp,
*/
if (sctp_use_cwnd_based_maxburst) {
if ((net->flight_size + (burst_limit * net->mtu)) < net->cwnd) {
- int old_cwnd;
-
- if (net->ssthresh < net->cwnd)
- net->ssthresh = net->cwnd;
- old_cwnd = net->cwnd;
- net->cwnd = (net->flight_size + (burst_limit * net->mtu));
-
- if (sctp_logging_level & SCTP_CWND_MONITOR_ENABLE) {
- sctp_log_cwnd(stcb, net, (net->cwnd - old_cwnd), SCTP_CWND_LOG_FROM_BRST);
- }
+ /*
+ * 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_logging_level & SCTP_LOG_MAXBURST_ENABLE) {
sctp_log_maxburst(stcb, net, 0, burst_limit, SCTP_MAX_BURST_APPLIED);
}
@@ -9562,24 +9656,43 @@ sctp_send_hb(struct sctp_tcb *stcb, int user_req, struct sctp_nets *u_net)
/* huh compiler bug */
return (0);
}
- /* 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;
+ /*
+ * 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 ((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_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+ SCTP_DECR_CHK_COUNT();
+ return (-1);
}
- sctp_free_a_chunk(stcb, chk);
- return (-1);
}
net->hb_responded = 0;
TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, chk, sctp_next);
@@ -10679,7 +10792,8 @@ sctp_lower_sosend(struct socket *so,
}
}
/* now we must find the assoc */
- if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+ if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) ||
+ (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
SCTP_INP_RLOCK(inp);
stcb = LIST_FIRST(&inp->sctp_asoc_list);
if (stcb == NULL) {
@@ -10807,9 +10921,14 @@ sctp_lower_sosend(struct socket *so,
} else {
hold_tcblock = 1;
}
+ if (t_inp != inp) {
+ error = ENOTCONN;
+ goto out_unlocked;
+ }
}
if (stcb == NULL) {
- if (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
+ if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+ (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
error = ENOTCONN;
goto out_unlocked;
} else if (addr == NULL) {
OpenPOWER on IntegriCloud