• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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