| /*- |
| * Copyright (c) 1982, 1986, 1993 |
| * The Regents of the University of California. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 4. Neither the name of the University nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * @(#)tcp_timer.h 8.1 (Berkeley) 6/10/93 |
| * $FreeBSD$ |
| */ |
| |
| #include <errno.h> |
| #include <stdio.h> |
| |
| #include "../tcplp.h" |
| #include "../lib/lbuf.h" |
| #include "tcp_fsm.h" |
| #include "tcp_timer.h" |
| #include "tcp_var.h" |
| |
| #include "tcp_const.h" |
| |
| /* |
| * samkumar: These options only matter if we do blackhole detection, which we |
| * are not doing in TCPlp. |
| */ |
| #if 0 |
| int V_tcp_pmtud_blackhole_detect = 0; |
| int V_tcp_pmtud_blackhole_failed = 0; |
| int V_tcp_pmtud_blackhole_activated = 0; |
| int V_tcp_pmtud_blackhole_activated_min_mss = 0; |
| #endif |
| |
| /* |
| * TCP timer processing. |
| */ |
| |
| /* |
| * samkumar: I changed these functions to accept "struct tcpcb* tp" their |
| * argument instead of "void *xtp". This is possible since we're no longer |
| * relying on FreeBSD's callout subsystem in TCPlp. I also changed them to |
| * return 1 if the connection is dropped, or 0 otherwise. |
| */ |
| |
| int |
| tcp_timer_delack(struct tcpcb* tp) |
| { |
| /* samkumar: I added this, to replace the code I removed below. */ |
| KASSERT(tpistimeractive(tp, TT_DELACK), ("Delack timer running, but unmarked")); |
| tpcleartimeractive(tp, TT_DELACK); |
| |
| /* |
| * samkumar: There used to be code here to properly handle the callout, |
| * including edge cases (return early if the callout was reset after this |
| * function was scheduled for execution, deactivate the callout, return |
| * early if the INP_DROPPED flag is set on the inpcb, and assert that the |
| * tp->t_timers state is correct). |
| * |
| * I also removed stats collection, locking, and vnet, throughout the code. |
| */ |
| tp->t_flags |= TF_ACKNOW; |
| (void) tcp_output(tp); |
| return 0; |
| } |
| |
| int |
| tcp_timer_keep(struct tcpcb* tp) |
| { |
| uint32_t ticks = tcplp_sys_get_ticks(); |
| struct tcptemp t_template; |
| |
| /* samkumar: I added this, to replace the code I removed below. */ |
| KASSERT(tpistimeractive(tp, TT_KEEP), ("Keep timer running, but unmarked")); |
| tpcleartimeractive(tp, TT_KEEP); |
| |
| /* |
| * samkumar: There used to be code here to properly handle the callout, |
| * including edge cases (return early if the callout was reset after this |
| * function was scheduled for execution, deactivate the callout, return |
| * early if the INP_DROPPED flag is set on the inpcb, and assert that the |
| * tp->t_timers state is correct). |
| * |
| * I also removed stats collection, locking, and vnet, throughout the code. |
| * I commented out checks on socket options (since we don't support those). |
| * |
| * I also allocate t_template on the stack instead of allocating it |
| * dynamically, on the heap. |
| */ |
| |
| /* |
| * Keep-alive timer went off; send something |
| * or drop connection if idle for too long. |
| */ |
| if (tp->t_state < TCPS_ESTABLISHED) |
| goto dropit; |
| if ((always_keepalive/* || inp->inp_socket->so_options & SO_KEEPALIVE*/) && |
| tp->t_state <= TCPS_CLOSING) { |
| if (ticks - tp->t_rcvtime >= TP_KEEPIDLE(tp) + TP_MAXIDLE(tp)) |
| goto dropit; |
| /* |
| * Send a packet designed to force a response |
| * if the peer is up and reachable: |
| * either an ACK if the connection is still alive, |
| * or an RST if the peer has closed the connection |
| * due to timeout or reboot. |
| * Using sequence number tp->snd_una-1 |
| * causes the transmitted zero-length segment |
| * to lie outside the receive window; |
| * by the protocol spec, this requires the |
| * correspondent TCP to respond. |
| */ |
| tcpip_maketemplate(tp, &t_template); |
| /* |
| * samkumar: I removed checks to make sure t_template was successfully |
| * allocated and then free it as appropriate. This is not necessary |
| * because I rewrote this part of the code to allocate t_template on |
| * the stack. |
| */ |
| tcp_respond(tp, tp->instance, (struct ip6_hdr*) t_template.tt_ipgen, |
| &t_template.tt_t, |
| tp->rcv_nxt, tp->snd_una - 1, 0); |
| /* |
| * samkumar: I replaced a call to callout_reset with the following |
| * code, which resets the timer the TCPlp way. |
| */ |
| tpmarktimeractive(tp, TT_KEEP); |
| tcplp_sys_set_timer(tp, TT_KEEP, TP_KEEPINTVL(tp)); |
| } else { |
| /* |
| * samkumar: I replaced a call to callout_reset with the following |
| * code, which resets the timer the TCPlp way. |
| */ |
| tpmarktimeractive(tp, TT_KEEP); |
| tcplp_sys_set_timer(tp, TT_KEEP, TP_KEEPIDLE(tp)); |
| } |
| |
| /* |
| * samkumar: There used to be some code here and below the "dropit" label |
| * that handled debug tracing/probes, vnet, and locking. I removed that |
| * code. |
| */ |
| return 0; |
| |
| dropit: |
| tp = tcp_drop(tp, ETIMEDOUT); |
| (void) tp; /* samkumar: prevent a compiler warning */ |
| return 1; |
| } |
| |
| int |
| tcp_timer_persist(struct tcpcb* tp) |
| { |
| uint32_t ticks = tcplp_sys_get_ticks(); |
| int dropped = 0; |
| |
| /* samkumar: I added this, to replace the code I removed below. */ |
| KASSERT(tpistimeractive(tp, TT_PERSIST), ("Persist timer running, but unmarked")); |
| tpcleartimeractive(tp, TT_PERSIST); // mark that this timer is no longer active |
| |
| /* |
| * samkumar: There used to be code here to properly handle the callout, |
| * including edge cases (return early if the callout was reset after this |
| * function was scheduled for execution, deactivate the callout, return |
| * early if the INP_DROPPED flag is set on the inpcb, and assert that the |
| * tp->t_timers state is correct). |
| * |
| * I also removed stats collection, locking, and vnet, throughout the code. |
| * I commented out checks on socket options (since we don't support those). |
| */ |
| |
| /* |
| * Persistance timer into zero window. |
| * Force a byte to be output, if possible. |
| */ |
| /* |
| |
| * Hack: if the peer is dead/unreachable, we do not |
| * time out if the window is closed. After a full |
| * backoff, drop the connection if the idle time |
| * (no responses to probes) reaches the maximum |
| * backoff that we would use if retransmitting. |
| */ |
| |
| if (tp->t_rxtshift == TCP_MAXRXTSHIFT && |
| (ticks - tp->t_rcvtime >= tcp_maxpersistidle || |
| ticks - tp->t_rcvtime >= TCP_REXMTVAL(tp) * tcp_totbackoff)) { |
| tp = tcp_drop(tp, ETIMEDOUT); |
| dropped = 1; |
| goto out; |
| } |
| |
| /* |
| * If the user has closed the socket then drop a persisting |
| * connection after a much reduced timeout. |
| */ |
| if (tp->t_state > TCPS_CLOSE_WAIT && |
| (ticks - tp->t_rcvtime) >= TCPTV_PERSMAX) { |
| tp = tcp_drop(tp, ETIMEDOUT); |
| dropped = 1; |
| goto out; |
| } |
| |
| tcp_setpersist(tp); |
| tp->t_flags |= TF_FORCEDATA; |
| tcplp_sys_log("Persist output: %zu bytes in sendbuf", lbuf_used_space(&tp->sendbuf)); |
| (void) tcp_output(tp); |
| tp->t_flags &= ~TF_FORCEDATA; |
| |
| out: |
| /* |
| * samkumar: There used to be some code here that handled debug |
| * tracing/probes, vnet, and locking. I removed that code. |
| */ |
| (void) tp; /* samkumar: prevent a compiler warning */ |
| return dropped; |
| } |
| |
| int |
| tcp_timer_2msl(struct tcpcb* tp) |
| { |
| uint32_t ticks = tcplp_sys_get_ticks(); |
| int dropped = 0; |
| |
| /* samkumar: I added this, to replace the code I removed below. */ |
| KASSERT(tpistimeractive(tp, TT_2MSL), ("2MSL timer running, but unmarked")); |
| tpcleartimeractive(tp, TT_2MSL); |
| |
| /* |
| * samkumar: There used to be code here to properly handle the callout, |
| * including edge cases (return early if the callout was reset after this |
| * function was scheduled for execution, deactivate the callout, return |
| * early if the INP_DROPPED flag is set on the inpcb, and assert that the |
| * tp->t_timers state is correct). |
| * |
| * I also removed stats collection, locking, and vnet, throughout the code. |
| */ |
| |
| /* |
| * 2 MSL timeout in shutdown went off. If we're closed but |
| * still waiting for peer to close and connection has been idle |
| * too long delete connection control block. Otherwise, check |
| * again in a bit. |
| * |
| * If in TIME_WAIT state just ignore as this timeout is handled in |
| * tcp_tw_2msl_scan(). |
| * |
| * If fastrecycle of FIN_WAIT_2, in FIN_WAIT_2 and receiver has closed, |
| * there's no point in hanging onto FIN_WAIT_2 socket. Just close it. |
| * Ignore fact that there were recent incoming segments. |
| */ |
| |
| /* |
| * samkumar: The above comment about ignoring this timeout if we're in the |
| * TIME_WAIT state no longer is true, since in TCPlp we changed how |
| * TIME_WAIT is handled. In FreeBSD, this timer isn't used for sockets in |
| * the TIME_WAIT state; instead the tcp_tw_2msl_scan method is called |
| * periodically on the slow timer, and expired tcptw structs are closed and |
| * freed. I changed it so that TIME-WAIT connections are still represented |
| * as tcpcb's, not tcptw's, and to rely on this timer to close the |
| * connection. |
| * |
| * Below, there used to be an if statement that checks the inpcb to tell |
| * if we're in TIME-WAIT state, and return early if so. I've replaced this |
| * with an if statement that checks the tcpcb if we're in the TIME-WAIT |
| * state, and acts appropriately if so. |
| */ |
| if (tp->t_state == TCP6S_TIME_WAIT) { |
| tp = tcp_close(tp); |
| tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL); |
| dropped = 1; |
| return dropped; |
| } |
| /* |
| * samkumar: This if statement also used to check that an inpcb is still |
| * attached and also |
| * (tp->t_inpcb->inp_socket->so_rcv.sb_state & SBS_CANTRCVMORE). |
| * We replaced the check on that flag with a call to tpiscantrcv. We |
| * haven't received a FIN, since we're in FIN-WAIT-2, so the only way it |
| * would pass the check is if the user called shutdown(SHUT_RD) |
| * or shutdown(SHUT_RDWR), which is impossible unless the host system |
| * provides an API for that. |
| */ |
| if (tcp_fast_finwait2_recycle && tp->t_state == TCPS_FIN_WAIT_2 && |
| tpiscantrcv(tp)) { |
| tp = tcp_close(tp); |
| tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL); |
| dropped = 1; |
| } else { |
| if (ticks - tp->t_rcvtime <= TP_MAXIDLE(tp)) { |
| /* |
| * samkumar: I replaced a call to callout_reset with the following |
| * code, which resets the timer the TCPlp way. |
| */ |
| tpmarktimeractive(tp, TT_2MSL); |
| tcplp_sys_set_timer(tp, TT_2MSL, TP_KEEPINTVL(tp)); |
| } else { |
| tp = tcp_close(tp); |
| tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL); |
| dropped = 1; |
| } |
| } |
| /* |
| * samkumar: There used to be some code here that handled debug |
| * tracing/probes, vnet, and locking. I removed that code. |
| */ |
| return dropped; |
| } |
| |
| int |
| tcp_timer_rexmt(struct tcpcb *tp) |
| { |
| int rexmt; |
| uint32_t ticks = tcplp_sys_get_ticks(); |
| int dropped = 0; |
| |
| /* samkumar: I added this, to replace the code I removed below. */ |
| KASSERT(tpistimeractive(tp, TT_REXMT), ("Rexmt timer running, but unmarked")); |
| tpcleartimeractive(tp, TT_REXMT); |
| |
| /* |
| * samkumar: There used to be code here to properly handle the callout, |
| * including edge cases (return early if the callout was reset after this |
| * function was scheduled for execution, deactivate the callout, return |
| * early if the INP_DROPPED flag is set on the inpcb, and assert that the |
| * tp->t_timers state is correct). |
| * |
| * I also removed stats collection, locking, and vnet, throughout the code. |
| */ |
| |
| tcp_free_sackholes(tp); |
| /* |
| * Retransmission timer went off. Message has not |
| * been acked within retransmit interval. Back off |
| * to a longer retransmit interval and retransmit one segment. |
| */ |
| tcplp_sys_log("rxtshift is %d", (int) tp->t_rxtshift); |
| if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) { |
| tp->t_rxtshift = TCP_MAXRXTSHIFT; |
| |
| tp = tcp_drop(tp, tp->t_softerror ? |
| tp->t_softerror : ETIMEDOUT); |
| dropped = 1; |
| goto out; |
| } |
| if (tp->t_state == TCPS_SYN_SENT) { |
| /* |
| * If the SYN was retransmitted, indicate CWND to be |
| * limited to 1 segment in cc_conn_init(). |
| */ |
| tp->snd_cwnd = 1; |
| } else if (tp->t_rxtshift == 1) { |
| /* |
| * first retransmit; record ssthresh and cwnd so they can |
| * be recovered if this turns out to be a "bad" retransmit. |
| * A retransmit is considered "bad" if an ACK for this |
| * segment is received within RTT/2 interval; the assumption |
| * here is that the ACK was already in flight. See |
| * "On Estimating End-to-End Network Path Properties" by |
| * Allman and Paxson for more details. |
| */ |
| tp->snd_cwnd_prev = tp->snd_cwnd; |
| tp->snd_ssthresh_prev = tp->snd_ssthresh; |
| tp->snd_recover_prev = tp->snd_recover; |
| if (IN_FASTRECOVERY(tp->t_flags)) |
| tp->t_flags |= TF_WASFRECOVERY; |
| else |
| tp->t_flags &= ~TF_WASFRECOVERY; |
| if (IN_CONGRECOVERY(tp->t_flags)) |
| tp->t_flags |= TF_WASCRECOVERY; |
| else |
| tp->t_flags &= ~TF_WASCRECOVERY; |
| tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1)); |
| tp->t_flags |= TF_PREVVALID; |
| } else |
| tp->t_flags &= ~TF_PREVVALID; |
| if (tp->t_state == TCPS_SYN_SENT) |
| rexmt = TCPTV_RTOBASE * tcp_syn_backoff[tp->t_rxtshift]; |
| else |
| rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift]; |
| TCPT_RANGESET(tp->t_rxtcur, rexmt, |
| tp->t_rttmin, TCPTV_REXMTMAX); |
| |
| /* |
| * samkumar: There used to be more than 100 lines of code here, which |
| * implemented a feature called blackhole detection. The idea here is that |
| * some routers may silently discard packets whose MTU is too large, |
| * instead of fragmenting the packet or sending an ICMP packet to give |
| * feedback to the host. Blackhole detection decreases the MTU in response |
| * to packet loss in case the packets are being dropped by such a router. |
| * |
| * In TCPlp, we do not do blackhole detection because we use a small MTU |
| * (hundreds of bytes) that is unlikely to be too large for intermediate |
| * routers in the Internet. The edge low-power wireless network is |
| * assumed to be engineered to handle 6LoWPAN correctly. |
| */ |
| |
| /* |
| * Disable RFC1323 and SACK if we haven't got any response to |
| * our third SYN to work-around some broken terminal servers |
| * (most of which have hopefully been retired) that have bad VJ |
| * header compression code which trashes TCP segments containing |
| * unknown-to-them TCP options. |
| */ |
| if (tcp_rexmit_drop_options && (tp->t_state == TCPS_SYN_SENT) && |
| (tp->t_rxtshift == 3)) |
| tp->t_flags &= ~(TF_REQ_SCALE|TF_REQ_TSTMP|TF_SACK_PERMIT); |
| /* |
| * If we backed off this far, our srtt estimate is probably bogus. |
| * Clobber it so we'll take the next rtt measurement as our srtt; |
| * move the current srtt into rttvar to keep the current |
| * retransmit times until then. |
| */ |
| if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) { |
| /* |
| * samkumar: Here, there used to be a call to "in6_losing", used to |
| * inform the lower layers about bad connectivity so it can search for |
| * different routes. The call was wrapped in a check on the inpcb's |
| * flags to check for IPv6 (which isn't relevant to us, since TCPlp |
| * assumes IPv6). |
| */ |
| tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT); |
| tp->t_srtt = 0; |
| } |
| tp->snd_nxt = tp->snd_una; |
| tp->snd_recover = tp->snd_max; |
| /* |
| * Force a segment to be sent. |
| */ |
| tp->t_flags |= TF_ACKNOW; |
| /* |
| * If timing a segment in this window, stop the timer. |
| */ |
| tp->t_rtttime = 0; |
| |
| cc_cong_signal(tp, NULL, CC_RTO); |
| |
| (void) tcp_output(tp); |
| |
| out: |
| /* |
| * samkumar: There used to be some code here that handled debug |
| * tracing/probes, vnet, and locking. I removed that code. |
| */ |
| (void) tp; /* samkumar: Prevent a compiler warning */ |
| return dropped; |
| } |
| |
| int |
| tcp_timer_active(struct tcpcb *tp, uint32_t timer_type) |
| { |
| return tpistimeractive(tp, timer_type); |
| } |
| |
| void |
| tcp_timer_activate(struct tcpcb *tp, uint32_t timer_type, uint32_t delta) { |
| if (delta) { |
| tpmarktimeractive(tp, timer_type); |
| if (tpistimeractive(tp, TT_REXMT) && tpistimeractive(tp, TT_PERSIST)) { |
| tcplp_sys_panic("TCP CRITICAL FAILURE: Retransmit and Persist timers are simultaneously running!"); |
| } |
| tcplp_sys_set_timer(tp, timer_type, (uint32_t) delta); |
| } else { |
| tpcleartimeractive(tp, timer_type); |
| tcplp_sys_stop_timer(tp, timer_type); |
| } |
| } |
| |
| void |
| tcp_cancel_timers(struct tcpcb* tp) { |
| tpcleartimeractive(tp, TT_DELACK); |
| tcplp_sys_stop_timer(tp, TT_DELACK); |
| tpcleartimeractive(tp, TT_REXMT); |
| tcplp_sys_stop_timer(tp, TT_REXMT); |
| tpcleartimeractive(tp, TT_PERSIST); |
| tcplp_sys_stop_timer(tp, TT_PERSIST); |
| tpcleartimeractive(tp, TT_KEEP); |
| tcplp_sys_stop_timer(tp, TT_KEEP); |
| tpcleartimeractive(tp, TT_2MSL); |
| tcplp_sys_stop_timer(tp, TT_2MSL); |
| } |