1 /*-
2 * Copyright (c) 1982, 1986, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * @(#)tcp_timer.h 8.1 (Berkeley) 6/10/93
30 * $FreeBSD$
31 */
32
33 #include <errno.h>
34 #include <stdio.h>
35
36 #include "../tcplp.h"
37 #include "../lib/lbuf.h"
38 #include "tcp_fsm.h"
39 #include "tcp_timer.h"
40 #include "tcp_var.h"
41
42 #include "tcp_const.h"
43
44 /*
45 * samkumar: These options only matter if we do blackhole detection, which we
46 * are not doing in TCPlp.
47 */
48 #if 0
49 int V_tcp_pmtud_blackhole_detect = 0;
50 int V_tcp_pmtud_blackhole_failed = 0;
51 int V_tcp_pmtud_blackhole_activated = 0;
52 int V_tcp_pmtud_blackhole_activated_min_mss = 0;
53 #endif
54
55 /*
56 * TCP timer processing.
57 */
58
59 /*
60 * samkumar: I changed these functions to accept "struct tcpcb* tp" their
61 * argument instead of "void *xtp". This is possible since we're no longer
62 * relying on FreeBSD's callout subsystem in TCPlp. I also changed them to
63 * return 1 if the connection is dropped, or 0 otherwise.
64 */
65
66 int
tcp_timer_delack(struct tcpcb * tp)67 tcp_timer_delack(struct tcpcb* tp)
68 {
69 /* samkumar: I added this, to replace the code I removed below. */
70 KASSERT(tpistimeractive(tp, TT_DELACK), ("Delack timer running, but unmarked"));
71 tpcleartimeractive(tp, TT_DELACK);
72
73 /*
74 * samkumar: There used to be code here to properly handle the callout,
75 * including edge cases (return early if the callout was reset after this
76 * function was scheduled for execution, deactivate the callout, return
77 * early if the INP_DROPPED flag is set on the inpcb, and assert that the
78 * tp->t_timers state is correct).
79 *
80 * I also removed stats collection, locking, and vnet, throughout the code.
81 */
82 tp->t_flags |= TF_ACKNOW;
83 (void) tcp_output(tp);
84 return 0;
85 }
86
87 int
tcp_timer_keep(struct tcpcb * tp)88 tcp_timer_keep(struct tcpcb* tp)
89 {
90 uint32_t ticks = tcplp_sys_get_ticks();
91 struct tcptemp t_template;
92
93 /* samkumar: I added this, to replace the code I removed below. */
94 KASSERT(tpistimeractive(tp, TT_KEEP), ("Keep timer running, but unmarked"));
95 tpcleartimeractive(tp, TT_KEEP);
96
97 /*
98 * samkumar: There used to be code here to properly handle the callout,
99 * including edge cases (return early if the callout was reset after this
100 * function was scheduled for execution, deactivate the callout, return
101 * early if the INP_DROPPED flag is set on the inpcb, and assert that the
102 * tp->t_timers state is correct).
103 *
104 * I also removed stats collection, locking, and vnet, throughout the code.
105 * I commented out checks on socket options (since we don't support those).
106 *
107 * I also allocate t_template on the stack instead of allocating it
108 * dynamically, on the heap.
109 */
110
111 /*
112 * Keep-alive timer went off; send something
113 * or drop connection if idle for too long.
114 */
115 if (tp->t_state < TCPS_ESTABLISHED)
116 goto dropit;
117 if ((always_keepalive/* || inp->inp_socket->so_options & SO_KEEPALIVE*/) &&
118 tp->t_state <= TCPS_CLOSING) {
119 if (ticks - tp->t_rcvtime >= TP_KEEPIDLE(tp) + TP_MAXIDLE(tp))
120 goto dropit;
121 /*
122 * Send a packet designed to force a response
123 * if the peer is up and reachable:
124 * either an ACK if the connection is still alive,
125 * or an RST if the peer has closed the connection
126 * due to timeout or reboot.
127 * Using sequence number tp->snd_una-1
128 * causes the transmitted zero-length segment
129 * to lie outside the receive window;
130 * by the protocol spec, this requires the
131 * correspondent TCP to respond.
132 */
133 tcpip_maketemplate(tp, &t_template);
134 /*
135 * samkumar: I removed checks to make sure t_template was successfully
136 * allocated and then free it as appropriate. This is not necessary
137 * because I rewrote this part of the code to allocate t_template on
138 * the stack.
139 */
140 tcp_respond(tp, tp->instance, (struct ip6_hdr*) t_template.tt_ipgen,
141 &t_template.tt_t,
142 tp->rcv_nxt, tp->snd_una - 1, 0);
143 /*
144 * samkumar: I replaced a call to callout_reset with the following
145 * code, which resets the timer the TCPlp way.
146 */
147 tpmarktimeractive(tp, TT_KEEP);
148 tcplp_sys_set_timer(tp, TT_KEEP, TP_KEEPINTVL(tp));
149 } else {
150 /*
151 * samkumar: I replaced a call to callout_reset with the following
152 * code, which resets the timer the TCPlp way.
153 */
154 tpmarktimeractive(tp, TT_KEEP);
155 tcplp_sys_set_timer(tp, TT_KEEP, TP_KEEPIDLE(tp));
156 }
157
158 /*
159 * samkumar: There used to be some code here and below the "dropit" label
160 * that handled debug tracing/probes, vnet, and locking. I removed that
161 * code.
162 */
163 return 0;
164
165 dropit:
166 tp = tcp_drop(tp, ETIMEDOUT);
167 (void) tp; /* samkumar: prevent a compiler warning */
168 return 1;
169 }
170
171 int
tcp_timer_persist(struct tcpcb * tp)172 tcp_timer_persist(struct tcpcb* tp)
173 {
174 uint32_t ticks = tcplp_sys_get_ticks();
175 int dropped = 0;
176
177 /* samkumar: I added this, to replace the code I removed below. */
178 KASSERT(tpistimeractive(tp, TT_PERSIST), ("Persist timer running, but unmarked"));
179 tpcleartimeractive(tp, TT_PERSIST); // mark that this timer is no longer active
180
181 /*
182 * samkumar: There used to be code here to properly handle the callout,
183 * including edge cases (return early if the callout was reset after this
184 * function was scheduled for execution, deactivate the callout, return
185 * early if the INP_DROPPED flag is set on the inpcb, and assert that the
186 * tp->t_timers state is correct).
187 *
188 * I also removed stats collection, locking, and vnet, throughout the code.
189 * I commented out checks on socket options (since we don't support those).
190 */
191
192 /*
193 * Persistance timer into zero window.
194 * Force a byte to be output, if possible.
195 */
196 /*
197
198 * Hack: if the peer is dead/unreachable, we do not
199 * time out if the window is closed. After a full
200 * backoff, drop the connection if the idle time
201 * (no responses to probes) reaches the maximum
202 * backoff that we would use if retransmitting.
203 */
204
205 if (tp->t_rxtshift == TCP_MAXRXTSHIFT &&
206 (ticks - tp->t_rcvtime >= tcp_maxpersistidle ||
207 ticks - tp->t_rcvtime >= TCP_REXMTVAL(tp) * tcp_totbackoff)) {
208 tp = tcp_drop(tp, ETIMEDOUT);
209 dropped = 1;
210 goto out;
211 }
212
213 /*
214 * If the user has closed the socket then drop a persisting
215 * connection after a much reduced timeout.
216 */
217 if (tp->t_state > TCPS_CLOSE_WAIT &&
218 (ticks - tp->t_rcvtime) >= TCPTV_PERSMAX) {
219 tp = tcp_drop(tp, ETIMEDOUT);
220 dropped = 1;
221 goto out;
222 }
223
224 tcp_setpersist(tp);
225 tp->t_flags |= TF_FORCEDATA;
226 tcplp_sys_log("Persist output: %zu bytes in sendbuf", lbuf_used_space(&tp->sendbuf));
227 (void) tcp_output(tp);
228 tp->t_flags &= ~TF_FORCEDATA;
229
230 out:
231 /*
232 * samkumar: There used to be some code here that handled debug
233 * tracing/probes, vnet, and locking. I removed that code.
234 */
235 (void) tp; /* samkumar: prevent a compiler warning */
236 return dropped;
237 }
238
239 int
tcp_timer_2msl(struct tcpcb * tp)240 tcp_timer_2msl(struct tcpcb* tp)
241 {
242 uint32_t ticks = tcplp_sys_get_ticks();
243 int dropped = 0;
244
245 /* samkumar: I added this, to replace the code I removed below. */
246 KASSERT(tpistimeractive(tp, TT_2MSL), ("2MSL timer running, but unmarked"));
247 tpcleartimeractive(tp, TT_2MSL);
248
249 /*
250 * samkumar: There used to be code here to properly handle the callout,
251 * including edge cases (return early if the callout was reset after this
252 * function was scheduled for execution, deactivate the callout, return
253 * early if the INP_DROPPED flag is set on the inpcb, and assert that the
254 * tp->t_timers state is correct).
255 *
256 * I also removed stats collection, locking, and vnet, throughout the code.
257 */
258
259 /*
260 * 2 MSL timeout in shutdown went off. If we're closed but
261 * still waiting for peer to close and connection has been idle
262 * too long delete connection control block. Otherwise, check
263 * again in a bit.
264 *
265 * If in TIME_WAIT state just ignore as this timeout is handled in
266 * tcp_tw_2msl_scan().
267 *
268 * If fastrecycle of FIN_WAIT_2, in FIN_WAIT_2 and receiver has closed,
269 * there's no point in hanging onto FIN_WAIT_2 socket. Just close it.
270 * Ignore fact that there were recent incoming segments.
271 */
272
273 /*
274 * samkumar: The above comment about ignoring this timeout if we're in the
275 * TIME_WAIT state no longer is true, since in TCPlp we changed how
276 * TIME_WAIT is handled. In FreeBSD, this timer isn't used for sockets in
277 * the TIME_WAIT state; instead the tcp_tw_2msl_scan method is called
278 * periodically on the slow timer, and expired tcptw structs are closed and
279 * freed. I changed it so that TIME-WAIT connections are still represented
280 * as tcpcb's, not tcptw's, and to rely on this timer to close the
281 * connection.
282 *
283 * Below, there used to be an if statement that checks the inpcb to tell
284 * if we're in TIME-WAIT state, and return early if so. I've replaced this
285 * with an if statement that checks the tcpcb if we're in the TIME-WAIT
286 * state, and acts appropriately if so.
287 */
288 if (tp->t_state == TCP6S_TIME_WAIT) {
289 tp = tcp_close(tp);
290 tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL);
291 dropped = 1;
292 return dropped;
293 }
294 /*
295 * samkumar: This if statement also used to check that an inpcb is still
296 * attached and also
297 * (tp->t_inpcb->inp_socket->so_rcv.sb_state & SBS_CANTRCVMORE).
298 * We replaced the check on that flag with a call to tpiscantrcv. We
299 * haven't received a FIN, since we're in FIN-WAIT-2, so the only way it
300 * would pass the check is if the user called shutdown(SHUT_RD)
301 * or shutdown(SHUT_RDWR), which is impossible unless the host system
302 * provides an API for that.
303 */
304 if (tcp_fast_finwait2_recycle && tp->t_state == TCPS_FIN_WAIT_2 &&
305 tpiscantrcv(tp)) {
306 tp = tcp_close(tp);
307 tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL);
308 dropped = 1;
309 } else {
310 if (ticks - tp->t_rcvtime <= TP_MAXIDLE(tp)) {
311 /*
312 * samkumar: I replaced a call to callout_reset with the following
313 * code, which resets the timer the TCPlp way.
314 */
315 tpmarktimeractive(tp, TT_2MSL);
316 tcplp_sys_set_timer(tp, TT_2MSL, TP_KEEPINTVL(tp));
317 } else {
318 tp = tcp_close(tp);
319 tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL);
320 dropped = 1;
321 }
322 }
323 /*
324 * samkumar: There used to be some code here that handled debug
325 * tracing/probes, vnet, and locking. I removed that code.
326 */
327 return dropped;
328 }
329
330 int
tcp_timer_rexmt(struct tcpcb * tp)331 tcp_timer_rexmt(struct tcpcb *tp)
332 {
333 int rexmt;
334 uint32_t ticks = tcplp_sys_get_ticks();
335 int dropped = 0;
336
337 /* samkumar: I added this, to replace the code I removed below. */
338 KASSERT(tpistimeractive(tp, TT_REXMT), ("Rexmt timer running, but unmarked"));
339 tpcleartimeractive(tp, TT_REXMT);
340
341 /*
342 * samkumar: There used to be code here to properly handle the callout,
343 * including edge cases (return early if the callout was reset after this
344 * function was scheduled for execution, deactivate the callout, return
345 * early if the INP_DROPPED flag is set on the inpcb, and assert that the
346 * tp->t_timers state is correct).
347 *
348 * I also removed stats collection, locking, and vnet, throughout the code.
349 */
350
351 tcp_free_sackholes(tp);
352 /*
353 * Retransmission timer went off. Message has not
354 * been acked within retransmit interval. Back off
355 * to a longer retransmit interval and retransmit one segment.
356 */
357 tcplp_sys_log("rxtshift is %d", (int) tp->t_rxtshift);
358 if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
359 tp->t_rxtshift = TCP_MAXRXTSHIFT;
360
361 tp = tcp_drop(tp, tp->t_softerror ?
362 tp->t_softerror : ETIMEDOUT);
363 dropped = 1;
364 goto out;
365 }
366 if (tp->t_state == TCPS_SYN_SENT) {
367 /*
368 * If the SYN was retransmitted, indicate CWND to be
369 * limited to 1 segment in cc_conn_init().
370 */
371 tp->snd_cwnd = 1;
372 } else if (tp->t_rxtshift == 1) {
373 /*
374 * first retransmit; record ssthresh and cwnd so they can
375 * be recovered if this turns out to be a "bad" retransmit.
376 * A retransmit is considered "bad" if an ACK for this
377 * segment is received within RTT/2 interval; the assumption
378 * here is that the ACK was already in flight. See
379 * "On Estimating End-to-End Network Path Properties" by
380 * Allman and Paxson for more details.
381 */
382 tp->snd_cwnd_prev = tp->snd_cwnd;
383 tp->snd_ssthresh_prev = tp->snd_ssthresh;
384 tp->snd_recover_prev = tp->snd_recover;
385 if (IN_FASTRECOVERY(tp->t_flags))
386 tp->t_flags |= TF_WASFRECOVERY;
387 else
388 tp->t_flags &= ~TF_WASFRECOVERY;
389 if (IN_CONGRECOVERY(tp->t_flags))
390 tp->t_flags |= TF_WASCRECOVERY;
391 else
392 tp->t_flags &= ~TF_WASCRECOVERY;
393 tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1));
394 tp->t_flags |= TF_PREVVALID;
395 } else
396 tp->t_flags &= ~TF_PREVVALID;
397 if (tp->t_state == TCPS_SYN_SENT)
398 rexmt = TCPTV_RTOBASE * tcp_syn_backoff[tp->t_rxtshift];
399 else
400 rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
401 TCPT_RANGESET(tp->t_rxtcur, rexmt,
402 tp->t_rttmin, TCPTV_REXMTMAX);
403
404 /*
405 * samkumar: There used to be more than 100 lines of code here, which
406 * implemented a feature called blackhole detection. The idea here is that
407 * some routers may silently discard packets whose MTU is too large,
408 * instead of fragmenting the packet or sending an ICMP packet to give
409 * feedback to the host. Blackhole detection decreases the MTU in response
410 * to packet loss in case the packets are being dropped by such a router.
411 *
412 * In TCPlp, we do not do blackhole detection because we use a small MTU
413 * (hundreds of bytes) that is unlikely to be too large for intermediate
414 * routers in the Internet. The edge low-power wireless network is
415 * assumed to be engineered to handle 6LoWPAN correctly.
416 */
417
418 /*
419 * Disable RFC1323 and SACK if we haven't got any response to
420 * our third SYN to work-around some broken terminal servers
421 * (most of which have hopefully been retired) that have bad VJ
422 * header compression code which trashes TCP segments containing
423 * unknown-to-them TCP options.
424 */
425 if (tcp_rexmit_drop_options && (tp->t_state == TCPS_SYN_SENT) &&
426 (tp->t_rxtshift == 3))
427 tp->t_flags &= ~(TF_REQ_SCALE|TF_REQ_TSTMP|TF_SACK_PERMIT);
428 /*
429 * If we backed off this far, our srtt estimate is probably bogus.
430 * Clobber it so we'll take the next rtt measurement as our srtt;
431 * move the current srtt into rttvar to keep the current
432 * retransmit times until then.
433 */
434 if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
435 /*
436 * samkumar: Here, there used to be a call to "in6_losing", used to
437 * inform the lower layers about bad connectivity so it can search for
438 * different routes. The call was wrapped in a check on the inpcb's
439 * flags to check for IPv6 (which isn't relevant to us, since TCPlp
440 * assumes IPv6).
441 */
442 tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
443 tp->t_srtt = 0;
444 }
445 tp->snd_nxt = tp->snd_una;
446 tp->snd_recover = tp->snd_max;
447 /*
448 * Force a segment to be sent.
449 */
450 tp->t_flags |= TF_ACKNOW;
451 /*
452 * If timing a segment in this window, stop the timer.
453 */
454 tp->t_rtttime = 0;
455
456 cc_cong_signal(tp, NULL, CC_RTO);
457
458 (void) tcp_output(tp);
459
460 out:
461 /*
462 * samkumar: There used to be some code here that handled debug
463 * tracing/probes, vnet, and locking. I removed that code.
464 */
465 (void) tp; /* samkumar: Prevent a compiler warning */
466 return dropped;
467 }
468
469 int
tcp_timer_active(struct tcpcb * tp,uint32_t timer_type)470 tcp_timer_active(struct tcpcb *tp, uint32_t timer_type)
471 {
472 return tpistimeractive(tp, timer_type);
473 }
474
475 void
tcp_timer_activate(struct tcpcb * tp,uint32_t timer_type,uint32_t delta)476 tcp_timer_activate(struct tcpcb *tp, uint32_t timer_type, uint32_t delta) {
477 if (delta) {
478 tpmarktimeractive(tp, timer_type);
479 if (tpistimeractive(tp, TT_REXMT) && tpistimeractive(tp, TT_PERSIST)) {
480 tcplp_sys_panic("TCP CRITICAL FAILURE: Retransmit and Persist timers are simultaneously running!");
481 }
482 tcplp_sys_set_timer(tp, timer_type, (uint32_t) delta);
483 } else {
484 tpcleartimeractive(tp, timer_type);
485 tcplp_sys_stop_timer(tp, timer_type);
486 }
487 }
488
489 void
tcp_cancel_timers(struct tcpcb * tp)490 tcp_cancel_timers(struct tcpcb* tp) {
491 tpcleartimeractive(tp, TT_DELACK);
492 tcplp_sys_stop_timer(tp, TT_DELACK);
493 tpcleartimeractive(tp, TT_REXMT);
494 tcplp_sys_stop_timer(tp, TT_REXMT);
495 tpcleartimeractive(tp, TT_PERSIST);
496 tcplp_sys_stop_timer(tp, TT_PERSIST);
497 tpcleartimeractive(tp, TT_KEEP);
498 tcplp_sys_stop_timer(tp, TT_KEEP);
499 tpcleartimeractive(tp, TT_2MSL);
500 tcplp_sys_stop_timer(tp, TT_2MSL);
501 }
502