diff options
author | rrs <rrs@FreeBSD.org> | 2007-07-14 09:36:28 +0000 |
---|---|---|
committer | rrs <rrs@FreeBSD.org> | 2007-07-14 09:36:28 +0000 |
commit | 1e9af2c480a638933876e637c1f3b9f1b7bad3e9 (patch) | |
tree | 008308315d74a9df2eec618692844a82f1ace245 /sys/netinet/sctp_output.c | |
parent | 0948603d9eefb8b7f5898f99c8703b2e5b7f9647 (diff) | |
download | FreeBSD-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.c | 201 |
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) { |