From 024c630b03b08e4b2c66b42b6f6fba065c21defe Mon Sep 17 00:00:00 2001 From: Sam Kumar Date: Wed, 17 Nov 2021 22:24:22 -0800 Subject: [PATCH] [tcp] add TCPlp's FreeBSD-derived protocol code, with explanatory comments (#7074) This commit adds TCPlp's TCP protocol logic, which is derived from the TCP protocol logic in the FreeBSD Operating System. The FreeBSD code had to be refactored to work for TCPlp, and I have added extensive comments explaining the code that was deleted and changed. The changes are primarily modifications to work with TCPlp's data buffering (see #6926), removal of features that depend on dynamic memory allocation to save memory (SYN cache, compressed TIME-WAIT state) and removal of features that aren't relevant in this setting (e.g., TCP Segmentation Offloading, IPv4 support). --- third_party/tcplp/CMakeLists.txt | 17 + third_party/tcplp/Makefile.am | 29 + third_party/tcplp/bsdtcp/cc.h | 163 ++ third_party/tcplp/bsdtcp/cc/cc_module.h | 78 + third_party/tcplp/bsdtcp/cc/cc_newreno.c | 246 ++ third_party/tcplp/bsdtcp/icmp_var.h | 57 + third_party/tcplp/bsdtcp/ip.h | 151 ++ third_party/tcplp/bsdtcp/ip6.h | 164 ++ third_party/tcplp/bsdtcp/sys/queue.h | 754 ++++++ third_party/tcplp/bsdtcp/tcp.h | 189 ++ third_party/tcplp/bsdtcp/tcp_const.h | 98 + third_party/tcplp/bsdtcp/tcp_fsm.h | 112 + third_party/tcplp/bsdtcp/tcp_input.c | 3093 ++++++++++++++++++++++ third_party/tcplp/bsdtcp/tcp_output.c | 1479 +++++++++++ third_party/tcplp/bsdtcp/tcp_reass.c | 314 +++ third_party/tcplp/bsdtcp/tcp_sack.c | 665 +++++ third_party/tcplp/bsdtcp/tcp_seq.h | 87 + third_party/tcplp/bsdtcp/tcp_subr.c | 374 +++ third_party/tcplp/bsdtcp/tcp_timer.c | 489 ++++ third_party/tcplp/bsdtcp/tcp_timer.h | 180 ++ third_party/tcplp/bsdtcp/tcp_timewait.c | 396 +++ third_party/tcplp/bsdtcp/tcp_usrreq.c | 510 ++++ third_party/tcplp/bsdtcp/tcp_var.h | 591 +++++ third_party/tcplp/bsdtcp/types.h | 71 + third_party/tcplp/tcplp.c | 0 third_party/tcplp/tcplp.h | 93 + 26 files changed, 10400 insertions(+) create mode 100644 third_party/tcplp/bsdtcp/cc.h create mode 100644 third_party/tcplp/bsdtcp/cc/cc_module.h create mode 100644 third_party/tcplp/bsdtcp/cc/cc_newreno.c create mode 100644 third_party/tcplp/bsdtcp/icmp_var.h create mode 100644 third_party/tcplp/bsdtcp/ip.h create mode 100644 third_party/tcplp/bsdtcp/ip6.h create mode 100644 third_party/tcplp/bsdtcp/sys/queue.h create mode 100644 third_party/tcplp/bsdtcp/tcp.h create mode 100644 third_party/tcplp/bsdtcp/tcp_const.h create mode 100644 third_party/tcplp/bsdtcp/tcp_fsm.h create mode 100644 third_party/tcplp/bsdtcp/tcp_input.c create mode 100644 third_party/tcplp/bsdtcp/tcp_output.c create mode 100644 third_party/tcplp/bsdtcp/tcp_reass.c create mode 100644 third_party/tcplp/bsdtcp/tcp_sack.c create mode 100644 third_party/tcplp/bsdtcp/tcp_seq.h create mode 100644 third_party/tcplp/bsdtcp/tcp_subr.c create mode 100644 third_party/tcplp/bsdtcp/tcp_timer.c create mode 100644 third_party/tcplp/bsdtcp/tcp_timer.h create mode 100644 third_party/tcplp/bsdtcp/tcp_timewait.c create mode 100644 third_party/tcplp/bsdtcp/tcp_usrreq.c create mode 100644 third_party/tcplp/bsdtcp/tcp_var.h create mode 100644 third_party/tcplp/bsdtcp/types.h delete mode 100644 third_party/tcplp/tcplp.c diff --git a/third_party/tcplp/CMakeLists.txt b/third_party/tcplp/CMakeLists.txt index 06323f4c6..68ab45ce6 100644 --- a/third_party/tcplp/CMakeLists.txt +++ b/third_party/tcplp/CMakeLists.txt @@ -29,6 +29,15 @@ project("TCPlp" C) set(src_tcplp + bsdtcp/cc/cc_newreno.c + bsdtcp/tcp_input.c + bsdtcp/tcp_output.c + bsdtcp/tcp_reass.c + bsdtcp/tcp_sack.c + bsdtcp/tcp_subr.c + bsdtcp/tcp_timer.c + bsdtcp/tcp_timewait.c + bsdtcp/tcp_usrreq.c lib/bitmap.c lib/cbuf.c lib/lbuf.c @@ -36,6 +45,14 @@ set(src_tcplp set(tcplp_static_target "tcplp") +string(REPLACE "-Wsign-compare" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-sign-compare") + +string(REPLACE "-Wconversion" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + +string(REPLACE "-Wunused-parameter" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-parameter") + add_library(${tcplp_static_target} STATIC ${src_tcplp}) set_target_properties(${tcplp_static_target} PROPERTIES OUTPUT_NAME tcplp) target_include_directories(${tcplp_static_target} diff --git a/third_party/tcplp/Makefile.am b/third_party/tcplp/Makefile.am index 282e8e030..b7a5aba7f 100644 --- a/third_party/tcplp/Makefile.am +++ b/third_party/tcplp/Makefile.am @@ -29,6 +29,19 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am EXTRA_DIST = \ + bsdtcp/cc/cc_module.h \ + bsdtcp/sys/queue.h \ + bsdtcp/cc.h \ + bsdtcp/icmp_var.h \ + bsdtcp/ip6.h \ + bsdtcp/ip.h \ + bsdtcp/tcp_const.h \ + bsdtcp/tcp_fsm.h \ + bsdtcp/tcp.h \ + bsdtcp/tcp_seq.h \ + bsdtcp/tcp_timer.h \ + bsdtcp/tcp_var.h \ + bsdtcp/types.h \ lib/bitmap.h \ lib/cbuf.h \ lib/lbuf.h \ @@ -43,6 +56,13 @@ lib_LIBRARIES = \ override CFLAGS := $(filter-out -Wsign-compare,$(CFLAGS)) override CFLAGS := $(CFLAGS) -Wno-sign-compare +# Do not enable -Wconversion for TCPlp +override CFLAGS := $(filter-out -Wconversion,$(CFLAGS)) + +# Do not enable -Wcast-align for TCPlp +override CFLAGS := $(filter-out -Wcast-align,$(CFLAGS)) +override CFLAGS := $(CFLAGS) -Wno-cast-align + # Do not enable -Wunused-parameter for TCPlp override CFLAGS := $(filter-out -Wunused-parameter,$(CFLAGS)) override CFLAGS := $(CFLAGS) -Wno-unused-parameter @@ -65,6 +85,15 @@ libtcplp_a_CPPFLAGS = \ $(NULL) libtcplp_a_SOURCES = \ + bsdtcp/cc/cc_newreno.c \ + bsdtcp/tcp_input.c \ + bsdtcp/tcp_output.c \ + bsdtcp/tcp_reass.c \ + bsdtcp/tcp_sack.c \ + bsdtcp/tcp_subr.c \ + bsdtcp/tcp_timer.c \ + bsdtcp/tcp_timewait.c \ + bsdtcp/tcp_usrreq.c \ lib/bitmap.c \ lib/cbuf.c \ lib/lbuf.c \ diff --git a/third_party/tcplp/bsdtcp/cc.h b/third_party/tcplp/bsdtcp/cc.h new file mode 100644 index 000000000..408c5ced4 --- /dev/null +++ b/third_party/tcplp/bsdtcp/cc.h @@ -0,0 +1,163 @@ +/*- + * Copyright (c) 2007-2008 + * Swinburne University of Technology, Melbourne, Australia. + * Copyright (c) 2009-2010 Lawrence Stewart + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed at the Centre for Advanced Internet + * Architectures, Swinburne University of Technology, by Lawrence Stewart and + * James Healy, made possible in part by a grant from the Cisco University + * Research Program Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by David Hayes under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/* + * This software was first released in 2007 by James Healy and Lawrence Stewart + * whilst working on the NewTCP research project at Swinburne University of + * Technology's Centre for Advanced Internet Architectures, Melbourne, + * Australia, which was made possible in part by a grant from the Cisco + * University Research Program Fund at Community Foundation Silicon Valley. + * More details are available at: + * http://caia.swin.edu.au/urp/newtcp/ + */ + +/* + * samkumar: The FreeBSD implementation supports many congestion control + * algorithms, each represented by a struct. Each tcpcb has a pointer to the + * relevant congestion control struct, and the congestion control structs are + * themselves part of an intrusive linked list. TCPlp hardcodes the congestion + * control algorithm to New Reno, so the fields corresponding to maintaining + * the global linked list are removed. + */ + +#ifndef _NETINET_CC_H_ +#define _NETINET_CC_H_ + +/* XXX: TCP_CA_NAME_MAX define lives in tcp.h for compat reasons. */ +#include "tcp.h" + +extern const struct cc_algo newreno_cc_algo; + +/* + * Wrapper around transport structs that contain same-named congestion + * control variables. Allows algos to be shared amongst multiple CC aware + * transprots. + */ +struct cc_var { + void *cc_data; /* Per-connection private CC algorithm data. */ + int bytes_this_ack; /* # bytes acked by the current ACK. */ + tcp_seq curack; /* Most recent ACK. */ + uint32_t flags; /* Flags for cc_var (see below) */ + /* samkumar: removing type, since TCPlp uses TCP congestion control. */ + //int type; /* Indicates which ptr is valid in ccvc. */ + union ccv_container { + struct tcpcb *tcp; + struct sctp_nets *sctp; + } ccvc; +}; + +/* cc_var flags. */ +#define CCF_ABC_SENTAWND 0x0001 /* ABC counted cwnd worth of bytes? */ +#define CCF_CWND_LIMITED 0x0002 /* Are we currently cwnd limited? */ +#define CCF_DELACK 0x0004 /* Is this ack delayed? */ +#define CCF_ACKNOW 0x0008 /* Will this ack be sent now? */ +#define CCF_IPHDR_CE 0x0010 /* Does this packet set CE bit? */ +#define CCF_TCPHDR_CWR 0x0020 /* Does this packet set CWR bit? */ + +/* ACK types passed to the ack_received() hook. */ +#define CC_ACK 0x0001 /* Regular in sequence ACK. */ +#define CC_DUPACK 0x0002 /* Duplicate ACK. */ +#define CC_PARTIALACK 0x0004 /* Not yet. */ +#define CC_SACK 0x0008 /* Not yet. */ + +/* + * Congestion signal types passed to the cong_signal() hook. The highest order 8 + * bits (0x01000000 - 0x80000000) are reserved for CC algos to declare their own + * congestion signal types. + */ +#define CC_ECN 0x00000001 /* ECN marked packet received. */ +#define CC_RTO 0x00000002 /* RTO fired. */ +#define CC_RTO_ERR 0x00000004 /* RTO fired in error. */ +#define CC_NDUPACK 0x00000008 /* Threshold of dupack's reached. */ + +#define CC_SIGPRIVMASK 0xFF000000 /* Mask to check if sig is private. */ + +/* + * Structure to hold data and function pointers that together represent a + * congestion control algorithm. + */ +struct cc_algo { + char name[TCP_CA_NAME_MAX]; + + /* Init global module state on kldload. */ + int (*mod_init)(void); + + /* Cleanup global module state on kldunload. */ + int (*mod_destroy)(void); + + /* Init CC state for a new control block. */ + int (*cb_init)(struct cc_var *ccv); + + /* Cleanup CC state for a terminating control block. */ + void (*cb_destroy)(struct cc_var *ccv); + + /* Init variables for a newly established connection. */ + void (*conn_init)(struct cc_var *ccv); + + /* Called on receipt of an ack. */ + void (*ack_received)(struct cc_var *ccv, uint16_t type); + + /* Called on detection of a congestion signal. */ + void (*cong_signal)(struct cc_var *ccv, uint32_t type); + + /* Called after exiting congestion recovery. */ + void (*post_recovery)(struct cc_var *ccv); + + /* Called when data transfer resumes after an idle period. */ + void (*after_idle)(struct cc_var *ccv); + + /* Called for an additional ECN processing apart from RFC3168. */ + void (*ecnpkt_handler)(struct cc_var *ccv); + + /* + * samkumar: This field is removed since we no longer use the intrusive + * linked list. + */ + //STAILQ_ENTRY (cc_algo) entries; +}; + +/* Macro to obtain the CC algo's struct ptr. */ +//#define CC_ALGO(tp) ((tp)->cc_algo) +#define CC_ALGO(tp) (&newreno_cc_algo) // samkumar: This allows the #defines in cc_newreno.c to work as intended + +/* Macro to obtain the CC algo's data ptr. */ +#define CC_DATA(tp) ((tp)->ccv->cc_data) + +#endif /* _NETINET_CC_H_ */ diff --git a/third_party/tcplp/bsdtcp/cc/cc_module.h b/third_party/tcplp/bsdtcp/cc/cc_module.h new file mode 100644 index 000000000..a38a1f353 --- /dev/null +++ b/third_party/tcplp/bsdtcp/cc/cc_module.h @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2009-2010 Lawrence Stewart + * All rights reserved. + * + * This software was developed by Lawrence Stewart while studying at the Centre + * for Advanced Internet Architectures, Swinburne University of Technology, made + * possible in part by a grant from the Cisco University Research Program Fund + * at Community Foundation Silicon Valley. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/* + * This software was first released in 2009 by Lawrence Stewart as part of the + * NewTCP research project at Swinburne University of Technology's Centre for + * Advanced Internet Architectures, Melbourne, Australia, which was made + * possible in part by a grant from the Cisco University Research Program Fund + * at Community Foundation Silicon Valley. More details are available at: + * http://caia.swin.edu.au/urp/newtcp/ + */ + +#ifndef _NETINET_CC_MODULE_H_ +#define _NETINET_CC_MODULE_H_ + +/* samkumar: This was already commented out in FreeBSD (I didn't do it). */ +/* + * Allows a CC algorithm to manipulate a commonly named CC variable regardless + * of the transport protocol and associated C struct. + * XXXLAS: Out of action until the work to support SCTP is done. + * +#define CCV(ccv, what) \ +(*( \ + (ccv)->type == IPPROTO_TCP ? &(ccv)->ccvc.tcp->what : \ + &(ccv)->ccvc.sctp->what \ +)) + */ +#define CCV(ccv, what) (ccv)->ccvc.tcp->what + +/* + * samkumar: I've commented this out (using #if 0) because our current TCPlp + * implementation hardcodes New Reno. Thus, we don't need FreeBSD's "module" + * mechanism to choose a congestion control algorithm. + */ +#if 0 +#define DECLARE_CC_MODULE(ccname, ccalgo) \ + static moduledata_t cc_##ccname = { \ + .name = #ccname, \ + .evhand = cc_modevent, \ + .priv = ccalgo \ + }; \ + DECLARE_MODULE(ccname, cc_##ccname, \ + SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY) + +int cc_modevent(module_t mod, int type, void *data); +#endif + +#endif /* _NETINET_CC_MODULE_H_ */ diff --git a/third_party/tcplp/bsdtcp/cc/cc_newreno.c b/third_party/tcplp/bsdtcp/cc/cc_newreno.c new file mode 100644 index 000000000..aee0bdbc4 --- /dev/null +++ b/third_party/tcplp/bsdtcp/cc/cc_newreno.c @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995 + * The Regents of the University of California. + * Copyright (c) 2007-2008,2010 + * Swinburne University of Technology, Melbourne, Australia. + * Copyright (c) 2009-2010 Lawrence Stewart + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed at the Centre for Advanced Internet + * Architectures, Swinburne University of Technology, by Lawrence Stewart, James + * Healy and David Hayes, made possible in part by a grant from the Cisco + * University Research Program Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by David Hayes under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +/* + * This software was first released in 2007 by James Healy and Lawrence Stewart + * whilst working on the NewTCP research project at Swinburne University of + * Technology's Centre for Advanced Internet Architectures, Melbourne, + * Australia, which was made possible in part by a grant from the Cisco + * University Research Program Fund at Community Foundation Silicon Valley. + * More details are available at: + * http://caia.swin.edu.au/urp/newtcp/ + */ + +/* samkumar: Removed/replaced a bunch of #include's. */ + +#include "../cc.h" +#include "../tcp.h" +#include "../tcp_seq.h" +#include "../tcp_var.h" +#include "cc_module.h" + +#include "../tcp_const.h" + +static int min(int a, int b) { return (a < b) ? a : b; } + +static void newreno_ack_received(struct cc_var *ccv, uint16_t type); +static void newreno_after_idle(struct cc_var *ccv); +static void newreno_cong_signal(struct cc_var *ccv, uint32_t type); +static void newreno_post_recovery(struct cc_var *ccv); + +const struct cc_algo newreno_cc_algo = { + .name = "newreno", + .ack_received = newreno_ack_received, + .after_idle = newreno_after_idle, + .cong_signal = newreno_cong_signal, + .post_recovery = newreno_post_recovery, +}; + +/* + * samkumar: Normally, this is done in cc.c. It's commented out since we + * just hardcode using New Reno in TCPlp (for now). We don't use FreeBSD's + * mechanism to have multiple congestion control modules and choose among + * them. + */ +//struct cc_algo* V_default_cc_ptr = &newreno_cc_algo; + +/* samkumar: Constant that is referenced (may want to change this later) */ +enum { + V_tcp_do_rfc3465 = 1 +}; + +static void +newreno_ack_received(struct cc_var *ccv, uint16_t type) +{ + if (type == CC_ACK && !IN_RECOVERY(CCV(ccv, t_flags)) && + (ccv->flags & CCF_CWND_LIMITED)) { + uint32_t cw = CCV(ccv, snd_cwnd); + uint32_t incr = CCV(ccv, t_maxseg); + + /* + * Regular in-order ACK, open the congestion window. + * Method depends on which congestion control state we're + * in (slow start or cong avoid) and if ABC (RFC 3465) is + * enabled. + * + * slow start: cwnd <= ssthresh + * cong avoid: cwnd > ssthresh + * + * slow start and ABC (RFC 3465): + * Grow cwnd exponentially by the amount of data + * ACKed capping the max increment per ACK to + * (abc_l_var * maxseg) bytes. + * + * slow start without ABC (RFC 5681): + * Grow cwnd exponentially by maxseg per ACK. + * + * cong avoid and ABC (RFC 3465): + * Grow cwnd linearly by maxseg per RTT for each + * cwnd worth of ACKed data. + * + * cong avoid without ABC (RFC 5681): + * Grow cwnd linearly by approximately maxseg per RTT using + * maxseg^2 / cwnd per ACK as the increment. + * If cwnd > maxseg^2, fix the cwnd increment at 1 byte to + * avoid capping cwnd. + */ + if (cw > CCV(ccv, snd_ssthresh)) { + if (V_tcp_do_rfc3465) { + if (ccv->flags & CCF_ABC_SENTAWND) + ccv->flags &= ~CCF_ABC_SENTAWND; + else + incr = 0; + } else + incr = max((incr * incr / cw), 1); + } else if (V_tcp_do_rfc3465) { + /* + * In slow-start with ABC enabled and no RTO in sight? + * (Must not use abc_l_var > 1 if slow starting after + * an RTO. On RTO, snd_nxt = snd_una, so the + * snd_nxt == snd_max check is sufficient to + * handle this). + * + * XXXLAS: Find a way to signal SS after RTO that + * doesn't rely on tcpcb vars. + */ + if (CCV(ccv, snd_nxt) == CCV(ccv, snd_max)) + incr = min(ccv->bytes_this_ack, + V_tcp_abc_l_var * CCV(ccv, t_maxseg)); + else + incr = min(ccv->bytes_this_ack, CCV(ccv, t_maxseg)); + } + /* ABC is on by default, so incr equals 0 frequently. */ + if (incr > 0) + CCV(ccv, snd_cwnd) = min(cw + incr, + TCP_MAXWIN << CCV(ccv, snd_scale)); + } +} + +static void +newreno_after_idle(struct cc_var *ccv) +{ + int rw; + + /* + * If we've been idle for more than one retransmit timeout the old + * congestion window is no longer current and we have to reduce it to + * the restart window before we can transmit again. + * + * The restart window is the initial window or the last CWND, whichever + * is smaller. + * + * This is done to prevent us from flooding the path with a full CWND at + * wirespeed, overloading router and switch buffers along the way. + * + * See RFC5681 Section 4.1. "Restarting Idle Connections". + */ + if (V_tcp_do_rfc3390) + rw = min(4 * CCV(ccv, t_maxseg), + max(2 * CCV(ccv, t_maxseg), 4380)); + else + rw = CCV(ccv, t_maxseg) * 2; + + CCV(ccv, snd_cwnd) = min(rw, CCV(ccv, snd_cwnd)); +} + +/* + * Perform any necessary tasks before we enter congestion recovery. + */ +static void +newreno_cong_signal(struct cc_var *ccv, uint32_t type) +{ + uint32_t win; + + /* Catch algos which mistakenly leak private signal types. */ + KASSERT((type & CC_SIGPRIVMASK) == 0, + ("%s: congestion signal type 0x%08x is private\n", __func__, (unsigned int) type)); + + win = max(CCV(ccv, snd_cwnd) / 2 / CCV(ccv, t_maxseg), 2) * + CCV(ccv, t_maxseg); + + switch (type) { + case CC_NDUPACK: + if (!IN_FASTRECOVERY(CCV(ccv, t_flags))) { + if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) + CCV(ccv, snd_ssthresh) = win; + ENTER_RECOVERY(CCV(ccv, t_flags)); + } + break; + case CC_ECN: + if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) { + CCV(ccv, snd_ssthresh) = win; + CCV(ccv, snd_cwnd) = win; + ENTER_CONGRECOVERY(CCV(ccv, t_flags)); + } + break; + } +} + +/* + * Perform any necessary tasks before we exit congestion recovery. + */ +static void +newreno_post_recovery(struct cc_var *ccv) +{ + if (IN_FASTRECOVERY(CCV(ccv, t_flags))) { + /* + * Fast recovery will conclude after returning from this + * function. Window inflation should have left us with + * approximately snd_ssthresh outstanding data. But in case we + * would be inclined to send a burst, better to do it via the + * slow start mechanism. + * + * XXXLAS: Find a way to do this without needing curack + */ + if (SEQ_GT(ccv->curack + CCV(ccv, snd_ssthresh), + CCV(ccv, snd_max))) + CCV(ccv, snd_cwnd) = CCV(ccv, snd_max) - + ccv->curack + CCV(ccv, t_maxseg); + else + CCV(ccv, snd_cwnd) = CCV(ccv, snd_ssthresh); + } +} + +/* + * samkumar: I commented this out because it's not necessary for TCPlp. See the + * comment above about not using FreeBSD's mechanism for having multiple + * congestion control modules. + */ +//DECLARE_CC_MODULE(newreno, &newreno_cc_algo); diff --git a/third_party/tcplp/bsdtcp/icmp_var.h b/third_party/tcplp/bsdtcp/icmp_var.h new file mode 100644 index 000000000..4f0cc2d29 --- /dev/null +++ b/third_party/tcplp/bsdtcp/icmp_var.h @@ -0,0 +1,57 @@ +/*- + * 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. + * + * @(#)icmp_var.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD$ + */ + +/* + * samkumar: Removed some #if guards and several definitions/declarations that + * weren't necessary (and often introduced additional dependencies). + */ + +#ifndef _NETINET_ICMP_VAR_H_ +#define _NETINET_ICMP_VAR_H_ +/* + * Identifiers for ICMP sysctl nodes + */ +#define ICMPCTL_MASKREPL 1 /* allow replies to netmask requests */ +#define ICMPCTL_STATS 2 /* statistics (read-only) */ +#define ICMPCTL_ICMPLIM 3 + +#define BANDLIM_UNLIMITED -1 +#define BANDLIM_ICMP_UNREACH 0 +#define BANDLIM_ICMP_ECHO 1 +#define BANDLIM_ICMP_TSTAMP 2 +#define BANDLIM_RST_CLOSEDPORT 3 /* No connection, and no listeners */ +#define BANDLIM_RST_OPENPORT 4 /* No connection, listener */ +#define BANDLIM_ICMP6_UNREACH 5 +#define BANDLIM_SCTP_OOTB 6 +#define BANDLIM_MAX 6 + +#endif diff --git a/third_party/tcplp/bsdtcp/ip.h b/third_party/tcplp/bsdtcp/ip.h new file mode 100644 index 000000000..7f70196fe --- /dev/null +++ b/third_party/tcplp/bsdtcp/ip.h @@ -0,0 +1,151 @@ +/*- + * 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. + * + * @(#)ip.h 8.2 (Berkeley) 6/1/94 + * $FreeBSD$ + */ + +#ifndef _NETINET_IP_H_ +#define _NETINET_IP_H_ + +#define IP_MAXPACKET 65535 /* maximum packet size */ + +/* + * Definitions for IP type of service (ip_tos). + */ +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_MINCOST 0x02 + +/* + * Definitions for IP precedence (also in ip_tos) (deprecated). + */ +#define IPTOS_PREC_NETCONTROL IPTOS_DSCP_CS7 +#define IPTOS_PREC_INTERNETCONTROL IPTOS_DSCP_CS6 +#define IPTOS_PREC_CRITIC_ECP IPTOS_DSCP_CS5 +#define IPTOS_PREC_FLASHOVERRIDE IPTOS_DSCP_CS4 +#define IPTOS_PREC_FLASH IPTOS_DSCP_CS3 +#define IPTOS_PREC_IMMEDIATE IPTOS_DSCP_CS2 +#define IPTOS_PREC_PRIORITY IPTOS_DSCP_CS1 +#define IPTOS_PREC_ROUTINE IPTOS_DSCP_CS0 + +/* + * Definitions for DiffServ Codepoints as per RFC2474 and RFC5865. + */ +#define IPTOS_DSCP_CS0 0x00 +#define IPTOS_DSCP_CS1 0x20 +#define IPTOS_DSCP_AF11 0x28 +#define IPTOS_DSCP_AF12 0x30 +#define IPTOS_DSCP_AF13 0x38 +#define IPTOS_DSCP_CS2 0x40 +#define IPTOS_DSCP_AF21 0x48 +#define IPTOS_DSCP_AF22 0x50 +#define IPTOS_DSCP_AF23 0x58 +#define IPTOS_DSCP_CS3 0x60 +#define IPTOS_DSCP_AF31 0x68 +#define IPTOS_DSCP_AF32 0x70 +#define IPTOS_DSCP_AF33 0x78 +#define IPTOS_DSCP_CS4 0x80 +#define IPTOS_DSCP_AF41 0x88 +#define IPTOS_DSCP_AF42 0x90 +#define IPTOS_DSCP_AF43 0x98 +#define IPTOS_DSCP_CS5 0xa0 +#define IPTOS_DSCP_VA 0xb0 +#define IPTOS_DSCP_EF 0xb8 +#define IPTOS_DSCP_CS6 0xc0 +#define IPTOS_DSCP_CS7 0xe0 + +/* + * ECN (Explicit Congestion Notification) codepoints in RFC3168 mapped to the + * lower 2 bits of the TOS field. + */ +#define IPTOS_ECN_NOTECT 0x00 /* not-ECT */ +#define IPTOS_ECN_ECT1 0x01 /* ECN-capable transport (1) */ +#define IPTOS_ECN_ECT0 0x02 /* ECN-capable transport (0) */ +#define IPTOS_ECN_CE 0x03 /* congestion experienced */ +#define IPTOS_ECN_MASK 0x03 /* ECN field mask */ + +/* + * Definitions for options. + */ +#define IPOPT_COPIED(o) ((o)&0x80) +#define IPOPT_CLASS(o) ((o)&0x60) +#define IPOPT_NUMBER(o) ((o)&0x1f) + +#define IPOPT_CONTROL 0x00 +#define IPOPT_RESERVED1 0x20 +#define IPOPT_DEBMEAS 0x40 +#define IPOPT_RESERVED2 0x60 + +#define IPOPT_EOL 0 /* end of option list */ +#define IPOPT_NOP 1 /* no operation */ + +#define IPOPT_RR 7 /* record packet route */ +#define IPOPT_TS 68 /* timestamp */ +#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */ +#define IPOPT_LSRR 131 /* loose source route */ +#define IPOPT_ESO 133 /* extended security */ +#define IPOPT_CIPSO 134 /* commerical security */ +#define IPOPT_SATID 136 /* satnet id */ +#define IPOPT_SSRR 137 /* strict source route */ +#define IPOPT_RA 148 /* router alert */ + +/* + * Offsets to fields in options other than EOL and NOP. + */ +#define IPOPT_OPTVAL 0 /* option ID */ +#define IPOPT_OLEN 1 /* option length */ +#define IPOPT_OFFSET 2 /* offset within option */ +#define IPOPT_MINOFF 4 /* min value of above */ + +/* Flag bits for ipt_flg. */ +#define IPOPT_TS_TSONLY 0 /* timestamps only */ +#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ +#define IPOPT_TS_PRESPEC 3 /* specified modules only */ + +/* Bits for security (not byte swapped). */ +#define IPOPT_SECUR_UNCLASS 0x0000 +#define IPOPT_SECUR_CONFID 0xf135 +#define IPOPT_SECUR_EFTO 0x789a +#define IPOPT_SECUR_MMMM 0xbc4d +#define IPOPT_SECUR_RESTR 0xaf13 +#define IPOPT_SECUR_SECRET 0xd788 +#define IPOPT_SECUR_TOPSECRET 0x6bc5 + +/* + * Internet implementation parameters. + */ +#define MAXTTL 255 /* maximum time to live (seconds) */ +#define IPDEFTTL 64 /* default ttl, from RFC 1340 */ +#define IPFRAGTTL 60 /* time to live for frags, slowhz */ +#define IPTTLDEC 1 /* subtracted when forwarding */ +#define IP_MSS 576 /* default maximum segment size */ + +#endif diff --git a/third_party/tcplp/bsdtcp/ip6.h b/third_party/tcplp/bsdtcp/ip6.h new file mode 100644 index 000000000..cc079240f --- /dev/null +++ b/third_party/tcplp/bsdtcp/ip6.h @@ -0,0 +1,164 @@ +/* $FreeBSD$ */ +/* $KAME: ip6.h,v 1.18 2001/03/29 05:34:30 itojun Exp $ */ + +/*- + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * 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. + * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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. + */ + +/*- + * 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. + * + * @(#)ip.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _NETINET_IP6_H_ +#define _NETINET_IP6_H_ + +#include "types.h" + +/* samkumar: Copied from netinet/in6.h */ +struct in6_addr { + union { + uint8_t __u6_addr8[16]; + uint16_t __u6_addr16[8]; + uint32_t __u6_addr32[4]; + } __u6_addr; /* 128-bit IP6 address */ +} __attribute__((packed)); // added this to allow unaligned access +#define s6_addr __u6_addr.__u6_addr8 + +struct sockaddr_in6 { + uint8_t sin6_len; /* length of this struct */ + int sin6_family; /* AF_INET6 */ + uint16_t sin6_port; /* Transport layer port # */ + uint32_t sin6_flowinfo; /* IP6 flow information */ + struct in6_addr sin6_addr; /* IP6 address */ + uint32_t sin6_scope_id; /* scope zone index */ +}; + +/* + * Definition for internet protocol version 6. + * RFC 2460 + */ +struct ip6_hdr { + union { + struct ip6_hdrctl { + u_int32_t ip6_un1_flow; /* 20 bits of flow-ID */ + u_int16_t ip6_un1_plen; /* payload length */ + u_int8_t ip6_un1_nxt; /* next header */ + u_int8_t ip6_un1_hlim; /* hop limit */ + } ip6_un1; + u_int8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */ + } ip6_ctlun; + struct in6_addr ip6_src; /* source address */ + struct in6_addr ip6_dst; /* destination address */ +} __attribute__((packed)); + +#define ip6_vfc ip6_ctlun.ip6_un2_vfc +#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow +#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen +#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt +#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim +#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim + +#define IPV6_VERSION 0x60 +#define IPV6_VERSION_MASK 0xf0 + +/* samkumar: Removed #if guard since ECN is enabled in TCPlp. */ +/* ECN bits proposed by Sally Floyd */ +#define IP6TOS_CE 0x01 /* congestion experienced */ +#define IP6TOS_ECT 0x02 /* ECN-capable transport */ + +// samkumar: Copied from in6.h +#define IN6_ARE_ADDR_EQUAL(a, b) \ + (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) + +/* Multicast */ +#define IN6_IS_ADDR_MULTICAST(a) ((a)->s6_addr[0] == 0xff) + +/* + * Unspecified + */ +#define IN6_IS_ADDR_UNSPECIFIED(a) \ + ((a)->__u6_addr.__u6_addr32[0] == 0 && \ + (a)->__u6_addr.__u6_addr32[1] == 0 && \ + (a)->__u6_addr.__u6_addr32[2] == 0 && \ + (a)->__u6_addr.__u6_addr32[3] == 0) + +/* + * Loopback + */ +#define IN6_IS_ADDR_LOOPBACK(a) \ + ((a)->__u6_addr.__u6_addr32[0] == 0 && \ + (a)->__u6_addr.__u6_addr32[1] == 0 && \ + (a)->__u6_addr.__u6_addr32[2] == 0 && \ + (a)->__u6_addr.__u6_addr32[3] == ntohl(1)) + +/* + * Unicast Scope + * Note that we must check topmost 10 bits only, not 16 bits (see RFC2373). + */ +#define IN6_IS_ADDR_LINKLOCAL(a) \ + (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80)) +#define IN6_IS_ADDR_SITELOCAL(a) \ + (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0)) + +/* + * Mapped + */ + +#define IN6_IS_ADDR_V4MAPPED(a) \ + ((a)->__u6_addr.__u6_addr32[0] == 0 && \ + (a)->__u6_addr.__u6_addr32[1] == 0 && \ + (a)->__u6_addr.__u6_addr32[2] == ntohl(0x0000ffff)) + +#endif /* not _NETINET_IP6_H_ */ diff --git a/third_party/tcplp/bsdtcp/sys/queue.h b/third_party/tcplp/bsdtcp/sys/queue.h new file mode 100644 index 000000000..bbd1c46a9 --- /dev/null +++ b/third_party/tcplp/bsdtcp/sys/queue.h @@ -0,0 +1,754 @@ +/*- + * Copyright (c) 1991, 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD$ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* samkumar: Removing this, as it adds yet another dependency. */ +//#include + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may be traversed in either direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _CLASS_HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _CLASS_ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - + - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_FROM + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_FROM_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_FROM - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _FOREACH_REVERSE_FROM_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ +#ifdef QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + unsigned long lastline; + unsigned long prevline; + const char *lastfile; + const char *prevfile; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRACEBUF_INITIALIZER { __LINE__, 0, __FILE__, NULL } , +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) +#define QMD_SAVELINK(name, link) void **name = (void *)&(link) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define QMD_SAVELINK(name, link) +#define TRACEBUF +#define TRACEBUF_INITIALIZER +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +#ifdef __cplusplus +/* + * In C++ there can be structure lists and class lists: + */ +#define QUEUE_TYPEOF(type) type +#else +#define QUEUE_TYPEOF(type) struct type +#endif + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_CLASS_HEAD(name, type) \ +struct name { \ + class type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +#define SLIST_CLASS_ENTRY(type) \ +struct { \ + class type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_CLASS_HEAD(name, type) \ +struct name { \ + class type *stqh_first; /* first element */ \ + class type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +#define STAILQ_CLASS_ENTRY(type) \ +struct { \ + class type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + __containerof((head)->stqh_last, \ + QUEUE_TYPEOF(type), field.stqe_next)) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1); \ + QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ +} while (0) + + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_CLASS_HEAD(name, type) \ +struct name { \ + class type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +#define LIST_CLASS_ENTRY(type) \ +struct { \ + class type *le_next; /* next element */ \ + class type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_LIST_CHECK_HEAD(head, field) do { \ + if (LIST_FIRST((head)) != NULL && \ + LIST_FIRST((head))->field.le_prev != \ + &LIST_FIRST((head))) \ + panic("Bad list head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_LIST_CHECK_NEXT(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL && \ + LIST_NEXT((elm), field)->field.le_prev != \ + &((elm)->field.le_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_LIST_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.le_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_LIST_CHECK_HEAD(head, field) +#define QMD_LIST_CHECK_NEXT(elm, field) +#define QMD_LIST_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QMD_LIST_CHECK_NEXT(listelm, field); \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_LIST_CHECK_PREV(listelm, field); \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QMD_LIST_CHECK_HEAD((head), field); \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_PREV(elm, head, type, field) \ + ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ + __containerof((elm)->field.le_prev, \ + QUEUE_TYPEOF(type), field.le_next)) + +#define LIST_REMOVE(elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.le_next); \ + QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ + QMD_LIST_CHECK_NEXT(elm, field); \ + QMD_LIST_CHECK_PREV(elm, field); \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ +} while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_CLASS_HEAD(name, type) \ +struct name { \ + class type *tqh_first; /* first element */ \ + class type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +#define TAILQ_CLASS_ENTRY(type) \ +struct { \ + class type *tqe_next; /* next element */ \ + class type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_TAILQ_CHECK_HEAD(head, field) do { \ + if (!TAILQ_EMPTY(head) && \ + TAILQ_FIRST((head))->field.tqe_prev != \ + &TAILQ_FIRST((head))) \ + panic("Bad tailq head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_TAIL(head, field) do { \ + if (*(head)->tqh_last != NULL) \ + panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ + if (TAILQ_NEXT((elm), field) != NULL && \ + TAILQ_NEXT((elm), field)->field.tqe_prev != \ + &((elm)->field.tqe_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_TAILQ_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_TAILQ_CHECK_HEAD(head, field) +#define QMD_TAILQ_CHECK_TAIL(head, headname) +#define QMD_TAILQ_CHECK_NEXT(elm, field) +#define QMD_TAILQ_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + QMD_TRACE_HEAD(head1); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QMD_TAILQ_CHECK_NEXT(listelm, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&(listelm)->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_TAILQ_CHECK_PREV(listelm, field); \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&(listelm)->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QMD_TAILQ_CHECK_HEAD(head, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QMD_TAILQ_CHECK_TAIL(head, field); \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ + QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ + QMD_TAILQ_CHECK_NEXT(elm, field); \ + QMD_TAILQ_CHECK_PREV(elm, field); \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first; \ + QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/third_party/tcplp/bsdtcp/tcp.h b/third_party/tcplp/bsdtcp/tcp.h new file mode 100644 index 000000000..c51217a7e --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp.h @@ -0,0 +1,189 @@ +/*- + * 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.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD$ + */ + +/* + * samkumar: In one instance (in struct tcphdr), where there are Big Endian + * and Little Endian alternatives for ordering two fields, I hardcoded the + * Little Endian alternative. If ever we port this to a Big Endian platform, + * we should look at that very closely, and consider rewriting it. + */ + +#ifndef _NETINET_TCP_H_ +#define _NETINET_TCP_H_ + +#include +#include + +#define __func__ "BSD TCP function" + +#define KASSERT(COND, MSG) if (!(COND)) tcplp_sys_log MSG + +typedef uint32_t tcp_seq; + +#define tcp6_seq tcp_seq /* for KAME src sync over BSD*'s */ +#define tcp6hdr tcphdr /* for KAME src sync over BSD*'s */ + +/* + * TCP header. + * Per RFC 793, September, 1981. + */ +struct tcphdr { + uint16_t th_sport; /* source port */ + uint16_t th_dport; /* destination port */ + tcp_seq th_seq; /* sequence number */ + tcp_seq th_ack; /* acknowledgement number */ +#if 1 //BYTE_ORDER == LITTLE_ENDIAN + uint8_t th_x2:4, /* (unused) */ + th_off:4; /* data offset */ +#endif +#if 0 //BYTE_ORDER == BIG_ENDIAN + uint8_t th_off:4, /* data offset */ + th_x2:4; /* (unused) */ +#endif + uint8_t th_flags; +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#define TH_ECE 0x40 +#define TH_CWR 0x80 +#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG|TH_ECE|TH_CWR) +#define PRINT_TH_FLAGS "\20\1FIN\2SYN\3RST\4PUSH\5ACK\6URG\7ECE\10CWR" + + uint16_t th_win; /* window */ + uint16_t th_sum; /* checksum */ + uint16_t th_urp; /* urgent pointer */ +}; + +#define TCPOPT_EOL 0 +#define TCPOLEN_EOL 1 +#define TCPOPT_PAD 0 /* padding after EOL */ +#define TCPOLEN_PAD 1 +#define TCPOPT_NOP 1 +#define TCPOLEN_NOP 1 +#define TCPOPT_MAXSEG 2 +#define TCPOLEN_MAXSEG 4 +#define TCPOPT_WINDOW 3 +#define TCPOLEN_WINDOW 3 +#define TCPOPT_SACK_PERMITTED 4 +#define TCPOLEN_SACK_PERMITTED 2 +#define TCPOPT_SACK 5 +#define TCPOLEN_SACKHDR 2 +#define TCPOLEN_SACK 8 /* 2*sizeof(tcp_seq) */ +#define TCPOPT_TIMESTAMP 8 +#define TCPOLEN_TIMESTAMP 10 +#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */ +#define TCPOPT_SIGNATURE 19 /* Keyed MD5: RFC 2385 */ +#define TCPOLEN_SIGNATURE 18 + +/* Miscellaneous constants */ +#define MAX_SACK_BLKS 6 /* Max # SACK blocks stored at receiver side */ +#define TCP_MAX_SACK 4 /* MAX # SACKs sent in any segment */ + + +/* + * The default maximum segment size (MSS) to be used for new TCP connections + * when path MTU discovery is not enabled. + * + * RFC879 derives the default MSS from the largest datagram size hosts are + * minimally required to handle directly or through IP reassembly minus the + * size of the IP and TCP header. With IPv6 the minimum MTU is specified + * in RFC2460. + * + * For IPv4 the MSS is 576 - sizeof(struct tcpiphdr) + * For IPv6 the MSS is IPV6_MMTU - sizeof(struct ip6_hdr) - sizeof(struct tcphdr) + * + * We use explicit numerical definition here to avoid header pollution. + */ +#define TCP_MSS 536 +#define TCP6_MSS 1220 + +/* + * Limit the lowest MSS we accept for path MTU discovery and the TCP SYN MSS + * option. Allowing low values of MSS can consume significant resources and + * be used to mount a resource exhaustion attack. + * Connections requesting lower MSS values will be rounded up to this value + * and the IP_DF flag will be cleared to allow fragmentation along the path. + * + * See tcp_subr.c tcp_minmss SYSCTL declaration for more comments. Setting + * it to "0" disables the minmss check. + * + * The default value is fine for TCP across the Internet's smallest official + * link MTU (256 bytes for AX.25 packet radio). However, a connection is very + * unlikely to come across such low MTU interfaces these days (anno domini 2003). + */ +#define TCP_MINMSS 216 + +#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */ +#define TTCP_CLIENT_SND_WND 4096 /* dflt send window for T/TCP client */ + +#define TCP_MAX_WINSHIFT 14 /* maximum window shift */ + +#define TCP_MAXBURST 4 /* maximum segments in a burst */ + +#define TCP_MAXHLEN (0xf<<2) /* max length of header in bytes */ +#define TCP_MAXOLEN (TCP_MAXHLEN - sizeof(struct tcphdr)) + /* max space left for options */ + +/* + * User-settable options (used with setsockopt). These are discrete + * values and are not masked together. Some values appear to be + * bitmasks for historical reasons. + */ +#define TCP_NODELAY 1 /* don't delay send to coalesce packets */ +#define TCP_MAXSEG 2 /* set maximum segment size */ +#define TCP_NOPUSH 4 /* don't push last block of write */ +#define TCP_NOOPT 8 /* don't use TCP options */ +#define TCP_MD5SIG 16 /* use MD5 digests (RFC2385) */ +#define TCP_INFO 32 /* retrieve tcp_info structure */ +#define TCP_CONGESTION 64 /* get/set congestion control algorithm */ +#define TCP_KEEPINIT 128 /* N, time to establish connection */ +#define TCP_KEEPIDLE 256 /* L,N,X start keeplives after this period */ +#define TCP_KEEPINTVL 512 /* L,N interval between keepalives */ +#define TCP_KEEPCNT 1024 /* L,N number of keepalives before close */ +#define TCP_PCAP_OUT 2048 /* number of output packets to keep */ +#define TCP_PCAP_IN 4096 /* number of input packets to keep */ + +/* Start of reserved space for third-party user-settable options. */ +#define TCP_VENDOR SO_VENDOR + +#define TCP_CA_NAME_MAX 16 /* max congestion control name length */ + +#define TCPI_OPT_TIMESTAMPS 0x01 +#define TCPI_OPT_SACK 0x02 +#define TCPI_OPT_WSCALE 0x04 +#define TCPI_OPT_ECN 0x08 +#define TCPI_OPT_TOE 0x10 + +#endif /* !_NETINET_TCP_H_ */ diff --git a/third_party/tcplp/bsdtcp/tcp_const.h b/third_party/tcplp/bsdtcp/tcp_const.h new file mode 100644 index 000000000..31af20944 --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_const.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018, Sam Kumar + * Copyright (c) 2018, University of California, Berkeley + * 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. + * 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. + */ + +/* + * samkumar: I created this file to store many of the constants shared by the + * various files in the FreeBSD protocol logic. The original variables were + * often virtualized ("V_"-prefixed) variables. I've changed the definitions to + * be enumerations rather than globals, to save some memory. These variables + * often serve to enable, disable, or configure certain TCP-related features. + */ + +#ifndef _TCP_CONST_H_ +#define _TCP_CONST_H_ + +#include "../tcplp.h" + +#include "tcp_var.h" +#include "tcp_timer.h" + +#define MSS_6LOWPAN ((FRAMES_PER_SEG * FRAMECAP_6LOWPAN) - IP6HDR_SIZE - sizeof(struct tcphdr)) + +// I may change some of these flags later +enum tcp_input_consts { + tcp_keepcnt = TCPTV_KEEPCNT, + tcp_fast_finwait2_recycle = 0, + tcprexmtthresh = 3, + V_drop_synfin = 0, + V_tcp_do_ecn = 1, + V_tcp_ecn_maxretries = 3, + V_tcp_do_rfc3042 = 1, + V_path_mtu_discovery = 0, + V_tcp_delack_enabled = 1, + V_tcp_initcwnd_segments = 0, + V_tcp_do_rfc3390 = 0, + V_tcp_abc_l_var = 2 // this is what was in the original tcp_input.c +}; + +enum tcp_subr_consts { + tcp_delacktime = TCPTV_DELACK, + tcp_keepinit = TCPTV_KEEP_INIT, + tcp_keepidle = TCPTV_KEEP_IDLE, + tcp_keepintvl = TCPTV_KEEPINTVL, + tcp_maxpersistidle = TCPTV_KEEP_IDLE, + tcp_msl = TCPTV_MSL, + tcp_rexmit_slop = TCPTV_CPU_VAR, + tcp_finwait2_timeout = TCPTV_FINWAIT2_TIMEOUT, + + V_tcp_do_rfc1323 = 1, + V_tcp_v6mssdflt = MSS_6LOWPAN, + /* Normally, this is used to prevent DoS attacks by sending tiny MSS values in the options. */ + V_tcp_minmss = TCP_MAXOLEN + 1, // Must have enough space for TCP options, and one more byte for data. Default is 216. + V_tcp_do_sack = 1 +}; + +enum tcp_timer_consts { +// V_tcp_v6pmtud_blackhole_mss = FRAMECAP_6LOWPAN - sizeof(struct ip6_hdr) - sizeof(struct tcphdr), // Doesn't matter unless blackhole_detect is 1. + tcp_rexmit_drop_options = 0, // drop options after a few retransmits + always_keepalive = 1, +}; + +/* + * Force a time value to be in a certain range. + */ +#define TCPT_RANGESET(tv, value, tvmin, tvmax) do { \ + (tv) = (value) + tcp_rexmit_slop; \ + if ((uint64_t)(tv) < (uint64_t)(tvmin)) \ + (tv) = (tvmin); \ + if ((uint64_t)(tv) > (uint64_t)(tvmax)) \ + (tv) = (tvmax); \ +} while(0) + +#endif diff --git a/third_party/tcplp/bsdtcp/tcp_fsm.h b/third_party/tcplp/bsdtcp/tcp_fsm.h new file mode 100644 index 000000000..110e9e3cc --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_fsm.h @@ -0,0 +1,112 @@ +/*- + * 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_fsm.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD$ + */ + +/* samkumar: Removed some #ifdef guards around constants needed for TCPlp. */ + +#ifndef _NETINET_TCP_FSM_H_ +#define _NETINET_TCP_FSM_H_ + +#include "types.h" + +/* + * TCP FSM state definitions. + * + * Per RFC793, September, 1981. + */ + +#define TCP_NSTATES 11 + +#define TCPS_CLOSED 0 /* closed */ +#define TCPS_LISTEN 1 /* listening for connection */ +#define TCPS_SYN_SENT 2 /* active, have sent syn */ +#define TCPS_SYN_RECEIVED 3 /* have sent and received syn */ +/* states < TCPS_ESTABLISHED are those where connections not established */ +#define TCPS_ESTABLISHED 4 /* established */ +#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */ +/* states > TCPS_CLOSE_WAIT are those where user has closed */ +#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */ +#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */ +#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */ +/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */ +#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */ +#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */ + +/* for KAME src sync over BSD*'s */ +#define TCP6_NSTATES TCP_NSTATES +#define TCP6S_CLOSED TCPS_CLOSED +#define TCP6S_LISTEN TCPS_LISTEN +#define TCP6S_SYN_SENT TCPS_SYN_SENT +#define TCP6S_SYN_RECEIVED TCPS_SYN_RECEIVED +#define TCP6S_ESTABLISHED TCPS_ESTABLISHED +#define TCP6S_CLOSE_WAIT TCPS_CLOSE_WAIT +#define TCP6S_FIN_WAIT_1 TCPS_FIN_WAIT_1 +#define TCP6S_CLOSING TCPS_CLOSING +#define TCP6S_LAST_ACK TCPS_LAST_ACK +#define TCP6S_FIN_WAIT_2 TCPS_FIN_WAIT_2 +#define TCP6S_TIME_WAIT TCPS_TIME_WAIT + +#define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED) +#define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED) +#define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT) + + /* + * Flags used when sending segments in tcp_output. Basic flags (TH_RST, + * TH_ACK,TH_SYN,TH_FIN) are totally determined by state, with the proviso + * that TH_FIN is sent only if all data queued for output is included in the + * segment. + */ +static const uint8_t tcp_outflags[TCP_NSTATES] = { + TH_RST|TH_ACK, /* 0, CLOSED */ + 0, /* 1, LISTEN */ + TH_SYN, /* 2, SYN_SENT */ + TH_SYN|TH_ACK, /* 3, SYN_RECEIVED */ + TH_ACK, /* 4, ESTABLISHED */ + TH_ACK, /* 5, CLOSE_WAIT */ + TH_FIN|TH_ACK, /* 6, FIN_WAIT_1 */ + TH_FIN|TH_ACK, /* 7, CLOSING */ + TH_FIN|TH_ACK, /* 8, LAST_ACK */ + TH_ACK, /* 9, FIN_WAIT_2 */ + TH_ACK, /* 10, TIME_WAIT */ +}; + +#ifdef KPROF +int tcp_acounts[TCP_NSTATES][PRU_NREQ]; +#endif + +static char const * const tcpstates[] = { + "CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD", + "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING", + "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT", +}; + +#endif diff --git a/third_party/tcplp/bsdtcp/tcp_input.c b/third_party/tcplp/bsdtcp/tcp_input.c new file mode 100644 index 000000000..b00a31a93 --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_input.c @@ -0,0 +1,3093 @@ +/*- + * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2007-2008,2010 + * Swinburne University of Technology, Melbourne, Australia. + * Copyright (c) 2009-2010 Lawrence Stewart + * Copyright (c) 2010 The FreeBSD Foundation + * Copyright (c) 2010-2011 Juniper Networks, Inc. + * All rights reserved. + * + * Portions of this software were developed at the Centre for Advanced Internet + * Architectures, Swinburne University of Technology, by Lawrence Stewart, + * James Healy and David Hayes, made possible in part by a grant from the Cisco + * University Research Program Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by David Hayes under sponsorship from the FreeBSD Foundation. + * + * Portions of this software were developed by Robert N. M. Watson under + * contract to Juniper Networks, Inc. + * + * 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_input.c 8.12 (Berkeley) 5/24/95 + */ + + +/* + * Determine a reasonable value for maxseg size. + * If the route is known, check route for mtu. + * If none, use an mss that can be handled on the outgoing interface + * without forcing IP to fragment. If no route is found, route has no mtu, + * or the destination isn't local, use a default, hopefully conservative + * size (usually 512 or the default IP max size, but no more than the mtu + * of the interface), as we can't discover anything about intervening + * gateways or networks. We also initialize the congestion/slow start + * window to be a single segment if the destination isn't local. + * While looking at the routing entry, we also initialize other path-dependent + * parameters from pre-set or cached values in the routing entry. + * + * Also take into account the space needed for options that we + * send regularly. Make maxseg shorter by that amount to assure + * that we can send maxseg amount of data even when the options + * are present. Store the upper limit of the length of options plus + * data in maxopd. + * + * NOTE that this routine is only called when we process an incoming + * segment, or an ICMP need fragmentation datagram. Outgoing SYN/ACK MSS + * settings are handled in tcp_mssopt(). + */ + +#include +#include +#include + +#include "tcp.h" +#include "tcp_fsm.h" +#include "tcp_seq.h" +#include "tcp_timer.h" +#include "tcp_var.h" +#include "../lib/bitmap.h" +#include "../lib/cbuf.h" +#include "icmp_var.h" +#include "ip.h" +#include "ip6.h" +#include "sys/queue.h" + +#include "tcp_const.h" + +/* samkumar: Copied from in.h */ +#define IPPROTO_DONE 267 + +/* samkumar: Copied from sys/libkern.h */ +static int imax(int a, int b) { return (a > b ? a : b); } +static int imin(int a, int b) { return (a < b ? a : b); } + +static int min(int a, int b) { return imin(a, b); } + +static void tcp_dooptions(struct tcpopt *, uint8_t *, int, int); +static void +tcp_do_segment(struct ip6_hdr* ip6, struct tcphdr *th, otMessage* msg, + struct tcpcb *tp, int drop_hdrlen, int tlen, uint8_t iptos, + struct signals* sig); +static void tcp_xmit_timer(struct tcpcb *, int); +void tcp_hc_get(/*struct in_conninfo *inc*/ struct tcpcb* tp, struct hc_metrics_lite *hc_metrics_lite); +static void tcp_newreno_partial_ack(struct tcpcb *, struct tcphdr *); + +/* + * CC wrapper hook functions + */ +static inline void +cc_ack_received(struct tcpcb *tp, struct tcphdr *th, uint16_t type) +{ + tp->ccv->bytes_this_ack = BYTES_THIS_ACK(tp, th); + if (tp->snd_cwnd <= tp->snd_wnd) + tp->ccv->flags |= CCF_CWND_LIMITED; + else + tp->ccv->flags &= ~CCF_CWND_LIMITED; + + if (type == CC_ACK) { + if (tp->snd_cwnd > tp->snd_ssthresh) { + tp->t_bytes_acked += min(tp->ccv->bytes_this_ack, + V_tcp_abc_l_var * tp->t_maxseg); + if (tp->t_bytes_acked >= tp->snd_cwnd) { + tp->t_bytes_acked -= tp->snd_cwnd; + tp->ccv->flags |= CCF_ABC_SENTAWND; + } + } else { + tp->ccv->flags &= ~CCF_ABC_SENTAWND; + tp->t_bytes_acked = 0; + } + } + + if (CC_ALGO(tp)->ack_received != NULL) { + /* XXXLAS: Find a way to live without this */ + tp->ccv->curack = th->th_ack; + CC_ALGO(tp)->ack_received(tp->ccv, type); + } +} + +static inline void +cc_conn_init(struct tcpcb *tp) +{ + struct hc_metrics_lite metrics; + int rtt; + + /* + * samkumar: remove locks, inpcb, and stats. + */ + + /* samkumar: Used to take &inp->inp_inc as an argument. */ + tcp_hc_get(tp, &metrics); + + if (tp->t_srtt == 0 && (rtt = metrics.rmx_rtt)) { + tp->t_srtt = rtt; + tp->t_rttbest = tp->t_srtt + TCP_RTT_SCALE; + if (metrics.rmx_rttvar) { + tp->t_rttvar = metrics.rmx_rttvar; + } else { + /* default variation is +- 1 rtt */ + tp->t_rttvar = + tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE; + } + TCPT_RANGESET(tp->t_rxtcur, + ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1, + tp->t_rttmin, TCPTV_REXMTMAX); + } + if (metrics.rmx_ssthresh) { + /* + * There's some sort of gateway or interface + * buffer limit on the path. Use this to set + * the slow start threshhold, but set the + * threshold to no less than 2*mss. + */ + tp->snd_ssthresh = max(2 * tp->t_maxseg, metrics.rmx_ssthresh); + } + + /* + * Set the initial slow-start flight size. + * + * RFC5681 Section 3.1 specifies the default conservative values. + * RFC3390 specifies slightly more aggressive values. + * RFC6928 increases it to ten segments. + * Support for user specified value for initial flight size. + * + * If a SYN or SYN/ACK was lost and retransmitted, we have to + * reduce the initial CWND to one segment as congestion is likely + * requiring us to be cautious. + */ + if (tp->snd_cwnd == 1) + tp->snd_cwnd = tp->t_maxseg; /* SYN(-ACK) lost */ + else if (V_tcp_initcwnd_segments) + tp->snd_cwnd = min(V_tcp_initcwnd_segments * tp->t_maxseg, + max(2 * tp->t_maxseg, V_tcp_initcwnd_segments * 1460)); + else if (V_tcp_do_rfc3390) + tp->snd_cwnd = min(4 * tp->t_maxseg, + max(2 * tp->t_maxseg, 4380)); + else { + /* Per RFC5681 Section 3.1 */ + if (tp->t_maxseg > 2190) + tp->snd_cwnd = 2 * tp->t_maxseg; + else if (tp->t_maxseg > 1095) + tp->snd_cwnd = 3 * tp->t_maxseg; + else + tp->snd_cwnd = 4 * tp->t_maxseg; + } + + if (CC_ALGO(tp)->conn_init != NULL) + CC_ALGO(tp)->conn_init(tp->ccv); + + /* samkumar: print statement for debugging. Resurrect with DEBUG macro? */ +#ifdef INSTRUMENT_TCP + printf("TCP CC_INIT %u %d %d\n", (unsigned int) get_micros(), (int) tp->snd_cwnd, (int) tp->snd_ssthresh); +#endif +} + +inline void +cc_cong_signal(struct tcpcb *tp, struct tcphdr *th, uint32_t type) +{ + /* samkumar: Remove locks and stats from this function. */ + + switch(type) { + case CC_NDUPACK: + if (!IN_FASTRECOVERY(tp->t_flags)) { + tp->snd_recover = tp->snd_max; + if (tp->t_flags & TF_ECN_PERMIT) + tp->t_flags |= TF_ECN_SND_CWR; + } + break; + case CC_ECN: + if (!IN_CONGRECOVERY(tp->t_flags)) { + tp->snd_recover = tp->snd_max; + if (tp->t_flags & TF_ECN_PERMIT) + tp->t_flags |= TF_ECN_SND_CWR; + } + break; + case CC_RTO: + tp->t_dupacks = 0; + tp->t_bytes_acked = 0; + EXIT_RECOVERY(tp->t_flags); + tp->snd_ssthresh = max(2, min(tp->snd_wnd, tp->snd_cwnd) / 2 / + tp->t_maxseg) * tp->t_maxseg; + tp->snd_cwnd = tp->t_maxseg; + + /* + * samkumar: Stats for TCPlp: count the number of timeouts (RTOs). + * I've commented this out (with #if 0) because it isn't part of TCP + * functionality. At some point, we may want to bring it back to + * measure performance. + */ +#if 0 + tcplp_timeoutRexmitCnt++; +#endif +#ifdef INSTRUMENT_TCP + printf("TCP CC_RTO %u %d %d\n", (unsigned int) get_micros(), (int) tp->snd_cwnd, (int) tp->snd_ssthresh); +#endif + break; + case CC_RTO_ERR: + /* RTO was unnecessary, so reset everything. */ + tp->snd_cwnd = tp->snd_cwnd_prev; + tp->snd_ssthresh = tp->snd_ssthresh_prev; + tp->snd_recover = tp->snd_recover_prev; + if (tp->t_flags & TF_WASFRECOVERY) + ENTER_FASTRECOVERY(tp->t_flags); + if (tp->t_flags & TF_WASCRECOVERY) + ENTER_CONGRECOVERY(tp->t_flags); + tp->snd_nxt = tp->snd_max; + tp->t_flags &= ~TF_PREVVALID; + tp->t_badrxtwin = 0; +#ifdef INSTRUMENT_TCP + printf("TCP CC_RTO_ERR %u %d %d\n", (unsigned int) get_micros(), (int) tp->snd_cwnd, (int) tp->snd_ssthresh); +#endif + break; + } + + if (CC_ALGO(tp)->cong_signal != NULL) { + if (th != NULL) + tp->ccv->curack = th->th_ack; + CC_ALGO(tp)->cong_signal(tp->ccv, type); + } +} + +static inline void +cc_post_recovery(struct tcpcb *tp, struct tcphdr *th) +{ + /* samkumar: remove lock */ + + /* XXXLAS: KASSERT that we're in recovery? */ + if (CC_ALGO(tp)->post_recovery != NULL) { + tp->ccv->curack = th->th_ack; + CC_ALGO(tp)->post_recovery(tp->ccv); + } + /* XXXLAS: EXIT_RECOVERY ? */ + tp->t_bytes_acked = 0; +} + + +/* + * Indicate whether this ack should be delayed. We can delay the ack if + * following conditions are met: + * - There is no delayed ack timer in progress. + * - Our last ack wasn't a 0-sized window. We never want to delay + * the ack that opens up a 0-sized window. + * - LRO wasn't used for this segment. We make sure by checking that the + * segment size is not larger than the MSS. + * - Delayed acks are enabled or this is a half-synchronized T/TCP + * connection. + */ +#define DELAY_ACK(tp, tlen) \ + ((!tcp_timer_active(tp, TT_DELACK) && \ + (tp->t_flags & TF_RXWIN0SENT) == 0) && \ + (tlen <= tp->t_maxopd) && \ + (V_tcp_delack_enabled || (tp->t_flags & TF_NEEDSYN))) + +static inline void +cc_ecnpkt_handler(struct tcpcb *tp, struct tcphdr *th, uint8_t iptos) +{ + /* samkumar: remove lock */ + + if (CC_ALGO(tp)->ecnpkt_handler != NULL) { + switch (iptos & IPTOS_ECN_MASK) { + case IPTOS_ECN_CE: + tp->ccv->flags |= CCF_IPHDR_CE; + break; + case IPTOS_ECN_ECT0: + tp->ccv->flags &= ~CCF_IPHDR_CE; + break; + case IPTOS_ECN_ECT1: + tp->ccv->flags &= ~CCF_IPHDR_CE; + break; + } + + if (th->th_flags & TH_CWR) + tp->ccv->flags |= CCF_TCPHDR_CWR; + else + tp->ccv->flags &= ~CCF_TCPHDR_CWR; + + if (tp->t_flags & TF_DELACK) + tp->ccv->flags |= CCF_DELACK; + else + tp->ccv->flags &= ~CCF_DELACK; + + CC_ALGO(tp)->ecnpkt_handler(tp->ccv); + + if (tp->ccv->flags & CCF_ACKNOW) + tcp_timer_activate(tp, TT_DELACK, tcp_delacktime); + } +} + +/* + * External function: look up an entry in the hostcache and fill out the + * supplied TCP metrics structure. Fills in NULL when no entry was found or + * a value is not set. + */ +/* + * samkumar: This function is taken from tcp_hostcache.c. We have no host cache + * in TCPlp, so I changed this to always act as if there is a miss. I removed + * the first argument, formerly "struct in_coninfo *inc". + */ +void +tcp_hc_get(struct tcpcb* tp, struct hc_metrics_lite *hc_metrics_lite) +{ + bzero(hc_metrics_lite, sizeof(*hc_metrics_lite)); +} + +/* + * External function: look up an entry in the hostcache and return the + * discovered path MTU. Returns NULL if no entry is found or value is not + * set. + */ + /* + * samkumar: This function is taken from tcp_hostcache.c. We have no host cache + * in TCPlp, so I changed this to always act as if there is a miss. + */ +uint64_t +tcp_hc_getmtu(struct tcpcb* tp) +{ + return 0; +} + + +/* + * Issue RST and make ACK acceptable to originator of segment. + * The mbuf must still include the original packet header. + * tp may be NULL. + */ +/* + * samkumar: Original signature was: + * static void tcp_dropwithreset(struct mbuf *m, struct tcphdr *th, struct tcpcb *tp, + * int tlen, int rstreason) + */ +void +tcp_dropwithreset(struct ip6_hdr* ip6, struct tcphdr *th, struct tcpcb *tp, otInstance* instance, + int tlen, int rstreason) +{ + /* + * samkumar: I removed logic to skip this for broadcast or multicast + * packets. In the FreeBSD version of this function, it would just + * call m_freem(m), if m->m_flags has M_BCAST or M_MCAST set, and not + * send a response packet. + * I also removed bandwidth limiting. + */ + if (th->th_flags & TH_RST) + return; + + /* tcp_respond consumes the mbuf chain. */ + if (th->th_flags & TH_ACK) { + tcp_respond(tp, instance, ip6, th, (tcp_seq) 0, th->th_ack, TH_RST); + } else { + if (th->th_flags & TH_SYN) + tlen++; + tcp_respond(tp, instance, ip6, th, th->th_seq + tlen, (tcp_seq) 0, TH_RST | TH_ACK); + } + return; +} + +/* + * TCP input handling is split into multiple parts: + * tcp6_input is a thin wrapper around tcp_input for the extended + * ip6_protox[] call format in ip6_input + * tcp_input handles primary segment validation, inpcb lookup and + * SYN processing on listen sockets + * tcp_do_segment processes the ACK and text of the segment for + * establishing, established and closing connections + */ +/* samkumar: The signature of this function was originally: + tcp_input(struct mbuf **mp, int *offp, int proto) */ +/* NOTE: tcp_fields_to_host(th) must be called before this function is called. */ +int +tcp_input(struct ip6_hdr* ip6, struct tcphdr* th, otMessage* msg, struct tcpcb* tp, struct tcpcb_listen* tpl, + struct signals* sig) +{ + /* + * samkumar: I significantly modified this function, compared to the + * FreeBSD version. This function used to be reponsible for matching an + * incoming TCP segment to its TCB. That functionality is now done by + * TCPlp, and this function is only called once a match has been + * identified. + * + * The tp and tpl arguments are used to indicate the match. Exactly one of + * them must be NULL, and the other must be set. If tp is non-NULL, then + * this function assumes that the packet was matched to an active socket + * (connection endpoint). If tpl is non-NULL, then this function assumes + * that this packet is a candidate match for a passive socket (listener) + * and attempts to set up a new connection if the flags, sequence numbers, + * etc. look OK. + * + * TCPlp assumes that the packets are IPv6, so I removed any logic specific + * to IPv4. + * + * And of course, all code pertaining to locks and stats has been removed. + */ + int tlen = 0, off; + int thflags; + uint8_t iptos = 0; + int drop_hdrlen; + int rstreason = 0; + struct tcpopt to; /* options in this segment */ + uint8_t* optp = NULL; + int optlen = 0; + to.to_flags = 0; + KASSERT(tp || tpl, ("One of tp and tpl must be positive")); + + /* + * samkumar: Here, there used to be code that handled preprocessing: + * calling m_pullup(m, sizeof(*ip6) + sizeof(*th)) to get the headers + * contiguous in memory, setting the ip6 and th pointers, validating the + * checksum, and dropping packets with unspecified source address. In + * TCPlp, all of this is done for a packet before this function is called. + */ + + tlen = ntohs(ip6->ip6_plen); // assume *off == sizeof(*ip6) + + /* + * samkumar: Logic that handled IPv4 was deleted below. I won't add a + * comment everytime this is done, but I'm putting it here (one of the + * first instances of this) for clarity. + */ + iptos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; + + /* + * Check that TCP offset makes sense, + * pull out TCP options and adjust length. XXX + */ + off = th->th_off << 2; + if (off < sizeof (struct tcphdr) || off > tlen) { + goto drop; + } + tlen -= off; /* tlen is used instead of ti->ti_len */ + /* samkumar: now, tlen is the length of the data */ + + if (off > sizeof (struct tcphdr)) { + /* + * samkumar: I removed a call to IP6_EXTHDR_CHECK, which I believe + * checks for IPv6 extension headers. In TCPlp, we assume that these + * are handled elsewhere in the networking stack, before the incoming + * packet is processed at the TCP layer. I also removed the followup + * calls to reassign the ip6 and th pointers. + */ + optlen = off - sizeof (struct tcphdr); + optp = (uint8_t *)(th + 1); + } + + thflags = th->th_flags; + + /* + * samkumar: There used to be a call here to tcp_fields_to_host(th), which + * changes the byte order of various fields to host format. I removed this + * call from there and handle it in TCPlp, before calling this. The reason + * is that it's possible for this function to be called twice by TCPlp's + * logic (e.g., if the packet matches a TIME-WAIT socket this function + * returns early, and the packet may then match a listening socket, at + * which ppoint this function will be called again). Thus, any operations + * like this, which mutate the packet itself, need to happen before calling + * this function. + */ + + /* + * Delay dropping TCP, IP headers, IPv6 ext headers, and TCP options. + * + * samkumar: My TCP header is in a different buffer from the IP header. + * drop_hdrlen is only meaningful as an offset into the TCP buffer, + * because it is used to determine how much of the packet to discard + * before copying it into the receive buffer. Therefore, my offset does + * not include the length of IP header and options, only the length of + * the TCP header and options. + */ + drop_hdrlen = /*off0 +*/ off; + + /* + * Locate pcb for segment; if we're likely to add or remove a + * connection then first acquire pcbinfo lock. There are three cases + * where we might discover later we need a write lock despite the + * flags: ACKs moving a connection out of the syncache, ACKs for a + * connection in TIMEWAIT and SYNs not targeting a listening socket. + */ + + /* + * samkumar: Locking code is removed, invalidating most of the above + * comment. + */ + + /* + * samkumar: The FreeBSD code at logic here to check m->m_flags for the + * M_IP6_NEXTHOP flag, and search for the PACKET_TAG_IPFORWARD tag and + * store it in fwd_tag if so. In TCPlp, we assume that the IPv6 layer of + * the host network stack handles this kind of IPv6-related functionality, + * so this logic has been removed. + */ + + /* + * samkumar: Here, there was code to match the packet to an inpcb and reply + * with an RST segment if no match is found. This included taking the + * fwd_tag into account, if set above (see the previous comment). I removed + * this code because, in TCPlp, this is done before calling this function. + */ + + /* + * A previous connection in TIMEWAIT state is supposed to catch stray + * or duplicate segments arriving late. If this segment was a + * legitimate new connection attempt, the old INPCB gets removed and + * we can try again to find a listening socket. + * + * At this point, due to earlier optimism, we may hold only an inpcb + * lock, and not the inpcbinfo write lock. If so, we need to try to + * acquire it, or if that fails, acquire a reference on the inpcb, + * drop all locks, acquire a global write lock, and then re-acquire + * the inpcb lock. We may at that point discover that another thread + * has tried to free the inpcb, in which case we need to loop back + * and try to find a new inpcb to deliver to. + * + * XXXRW: It may be time to rethink timewait locking. + */ + /* + * samkumar: The original code checked inp->inp_flags & INP_TIMEWAIT. I + * changed it to instead check tp->t_state, since we don't use inpcbs in + * TCPlp. + */ + if (tp && tp->t_state == TCP6S_TIME_WAIT) { + /* + * samkumar: There's nothing wrong with the call to tcp_dooptions call + * that I've commented out below; it's just that the modified + * "tcp_twcheck" function no longer needs the options structure, so + * I figured that there's no longer a good reason to parse the options. + * In fact, this call was probably unnecessary even in the original + * FreeBSD TCP code, since tcp_twcheck, even without my modifications, + * did not use the pointer to the options structure! + */ + //if (thflags & TH_SYN) + //tcp_dooptions(&to, optp, optlen, TO_SYN); + /* + * samkumar: The original code would "goto findpcb;" if this branch is + * taken. Matching with a TCB is done outside of this function in + * TCPlp, so we instead return a special value so that the caller knows + * to try re-matching this packet to a socket. + */ + if (tcp_twcheck(tp,/*inp, &to,*/ th, /*m,*/ tlen)) + return (RELOOKUP_REQUIRED); + return (IPPROTO_DONE); + } + /* + * The TCPCB may no longer exist if the connection is winding + * down or it is in the CLOSED state. Either way we drop the + * segment and send an appropriate response. + */ + /* + * samkumar: There used to be code here that grabs the tp from the inpcb + * and drops with reset if the connection is in the closed state or if + * the tp is NULL. In TCPlp, the equivalent logic is done before entering + * this function. There was also code here to handle TCP offload, which + * TCPlp does not handle. + */ + + /* + * We've identified a valid inpcb, but it could be that we need an + * inpcbinfo write lock but don't hold it. In this case, attempt to + * acquire using the same strategy as the TIMEWAIT case above. If we + * relock, we have to jump back to 'relocked' as the connection might + * now be in TIMEWAIT. + */ + /* + * samkumar: There used to be some code here for synchronization, MAC + * management, and debugging. + */ + + /* + * When the socket is accepting connections (the INPCB is in LISTEN + * state) we look into the SYN cache if this is a new connection + * attempt or the completion of a previous one. Instead of checking + * so->so_options to check if the socket is listening, we rely on the + * arguments passed to this function (if tp == NULL, then tpl is not NULL + * and is the matching listen socket). + */ + + if (/*so->so_options & SO_ACCEPTCONN*/tp == NULL) { + /* samkumar: NULL check isn't needed but prevents a compiler warning */ + KASSERT(tpl != NULL && tpl->t_state == TCP6S_LISTEN, ("listen socket must be in listening state!")); + + /* + * samkumar: There used to be some code here that checks if the + * received segment is an ACK, and if so, searches the SYN cache to + * find an entry whose connection establishment handshake this segment + * completes. If such an entry is found, then a socket is created and + * then tcp_do_segment is called to actually run the code to mark the + * connection as established. If the received segment is an RST, then + * that is processed in the syncache as well. In TCPlp we do not use a + * SYN cache, so I've removed that code. The actual connection + * establishment/processing logic happens in tcp_do_segment anyway, + * which is called at the bottom of this function, so there's no need + * to rewrite this code with special-case logic for that. + */ + + /* + * We can't do anything without SYN. + */ + if ((thflags & TH_SYN) == 0) { + /* + * samkumar: Here, and in several other instances, the FreeBSD + * code would call tcp_log_addrs. Improving logging in these + * edge cases in TCPlp is left for the future --- for now, I just + * put "" where the address string would go. + */ + tcplp_sys_log("%s; %s: Listen socket: " + "SYN is missing, segment ignored\n", + "", __func__); + goto dropunlock; + } + /* + * (SYN|ACK) is bogus on a listen socket. + */ + if (thflags & TH_ACK) { + /* samkumar: See above comment regarding tcp_log_addrs. */ + tcplp_sys_log("%s; %s: Listen socket: " + "SYN|ACK invalid, segment rejected\n", + "", __func__); + /* samkumar: Removed call to syncache_badack(&inc); */ + rstreason = BANDLIM_RST_OPENPORT; + goto dropwithreset; + } + /* + * If the drop_synfin option is enabled, drop all + * segments with both the SYN and FIN bits set. + * This prevents e.g. nmap from identifying the + * TCP/IP stack. + * XXX: Poor reasoning. nmap has other methods + * and is constantly refining its stack detection + * strategies. + * XXX: This is a violation of the TCP specification + * and was used by RFC1644. + */ + if ((thflags & TH_FIN) && V_drop_synfin) { + /* samkumar: See above comment regarding tcp_log_addrs. */ + tcplp_sys_log("%s; %s: Listen socket: " + "SYN|FIN segment ignored (based on " + "sysctl setting)\n", "", __func__); + goto dropunlock; + } + /* + * Segment's flags are (SYN) or (SYN|FIN). + * + * TH_PUSH, TH_URG, TH_ECE, TH_CWR are ignored + * as they do not affect the state of the TCP FSM. + * The data pointed to by TH_URG and th_urp is ignored. + */ + KASSERT((thflags & (TH_RST|TH_ACK)) == 0, + ("%s: Listen socket: TH_RST or TH_ACK set", __func__)); + KASSERT(thflags & (TH_SYN), + ("%s: Listen socket: TH_SYN not set", __func__)); + + /* + * samkumar: There used to be some code here to reject incoming + * SYN packets for deprecated interface addresses unless + * V_ip6_use_deprecated is true. Rejecting the packet, in this case, + * means to "goto dropwithreset". I removed this functionality. + */ + + /* + * Basic sanity checks on incoming SYN requests: + * Don't respond if the destination is a link layer + * broadcast according to RFC1122 4.2.3.10, p. 104. + * If it is from this socket it must be forged. + * Don't respond if the source or destination is a + * global or subnet broad- or multicast address. + * Note that it is quite possible to receive unicast + * link-layer packets with a broadcast IP address. Use + * in_broadcast() to find them. + */ + + /* + * samkumar: There used to be a sanity check that drops (via + * "goto dropunlock") any broadcast or multicast packets. This check is + * done by checking m->m_flags for (M_BAST|M_MCAST). The original + * FreeBSD code for this has been removed (since checking m->m_flags + * isn't really useful to us anyway). Note that other FreeBSD code that + * checks for multicast source/destination addresses is retained below + * (but only for the IPv6 case; the original FreeBSD code also handled + * it for IPv4 addresses). + */ + + if (th->th_dport == th->th_sport && + IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &ip6->ip6_src)) { + /* samkumar: See above comment regarding tcp_log_addrs. */ + tcplp_sys_log("%s; %s: Listen socket: " + "Connection attempt to/from self " + "ignored\n", "", __func__); + goto dropunlock; + } + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || + IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) { + /* samkumar: See above comment regarding tcp_log_addrs. */ + tcplp_sys_log("%s; %s: Listen socket: " + "Connection attempt from/to multicast " + "address ignored\n", "", __func__); + goto dropunlock; + } + + /* + * samkumar: The FreeBSD code would call + * syncache_add(&inc, &to, th, inp, &so, m, NULL, NULL); + * to add an entry to the SYN cache at this point. TCPlp doesn't use a + * syncache, so we initialize the new socket right away. The code to + * initialize the socket is taken from the syncache_socket function. + */ + + tcp_dooptions(&to, optp, optlen, TO_SYN); + tp = tcplp_sys_accept_ready(tpl, &ip6->ip6_dst, th->th_sport); // Try to allocate an active socket to accept into + if (tp == NULL) { + /* If we couldn't allocate, just ignore the SYN. */ + return IPPROTO_DONE; + } + if (tp == (struct tcpcb *) -1) { + rstreason = ECONNREFUSED; + goto dropwithreset; + } + tcp_state_change(tp, TCPS_SYN_RECEIVED); + tpmarkpassiveopen(tp); + tp->t_flags |= TF_ACKNOW; // samkumar: my addition + tp->iss = tcp_new_isn(tp); + tp->irs = th->th_seq; + tcp_rcvseqinit(tp); + tcp_sendseqinit(tp); + tp->snd_wl1 = th->th_seq; + tp->snd_max = tp->iss/* + 1*/; + tp->snd_nxt = tp->iss/* + 1*/; + tp->rcv_up = th->th_seq + 1; + tp->rcv_wnd = imin(imax(cbuf_free_space(&tp->recvbuf), 0), TCP_MAXWIN); + tp->rcv_adv += tp->rcv_wnd; + tp->last_ack_sent = tp->rcv_nxt; + memcpy(&tp->laddr, &ip6->ip6_dst, sizeof(tp->laddr)); + memcpy(&tp->faddr, &ip6->ip6_src, sizeof(tp->faddr)); + tp->fport = th->th_sport; + tp->lport = tpl->lport; + + /* + * samkumar: Several of the checks below (taken from syncache_socket!) + * check for flags in sc->sc_flags. They have been written to directly + * check for the conditions on the TCP options structure or in the TCP + * header that would ordinarily be used to set flags in sc->sc_flags + * when adding an entry to the SYN cache. + * + * In effect, we combine the logic in syncache_add to set elements of + * sc with the logic in syncache_socket to transfer state from sc + * to the socket, but short-circuit the process to avoid ever storing + * data in sc. Since this isn't just adding or deleting code, I decided + * that it's better to keep comments indicating exactly how I composed + * these two functions. + */ + tp->t_flags = tp->t_flags & (TF_NOPUSH | TF_NODELAY | TF_NOOPT); +// tp->t_flags = sototcpcb(lso)->t_flags & (TF_NOPUSH|TF_NODELAY); +// if (sc->sc_flags & SCF_NOOPT) +// tp->t_flags |= TF_NOOPT; +// else { + if (!(tp->t_flags & TF_NOOPT) && V_tcp_do_rfc1323) { + if (/*sc->sc_flags & SCF_WINSCALE*/to.to_flags & TOF_SCALE) { + int wscale = 0; + + /* + * Pick the smallest possible scaling factor that + * will still allow us to scale up to sb_max, aka + * kern.ipc.maxsockbuf. + * + * We do this because there are broken firewalls that + * will corrupt the window scale option, leading to + * the other endpoint believing that our advertised + * window is unscaled. At scale factors larger than + * 5 the unscaled window will drop below 1500 bytes, + * leading to serious problems when traversing these + * broken firewalls. + * + * With the default maxsockbuf of 256K, a scale factor + * of 3 will be chosen by this algorithm. Those who + * choose a larger maxsockbuf should watch out + * for the compatiblity problems mentioned above. + * + * RFC1323: The Window field in a SYN (i.e., a + * or ) segment itself is never scaled. + */ + + /* + * samkumar: The original logic, taken from syncache_add, is + * listed below, commented out. In practice, we just use + * wscale = 0 because in TCPlp we assume that the buffers + * aren't big enough for window scaling to be all that useful. + */ +#if 0 + while (wscale < TCP_MAX_WINSHIFT && + (TCP_MAXWIN << wscale) < sb_max) + wscale++; +#endif + + tp->t_flags |= TF_REQ_SCALE|TF_RCVD_SCALE; + tp->snd_scale = /*sc->sc_requested_s_scale*/to.to_wscale; + tp->request_r_scale = wscale; + } + if (/*sc->sc_flags & SCF_TIMESTAMP*/to.to_flags & TOF_TS) { + tp->t_flags |= TF_REQ_TSTMP|TF_RCVD_TSTMP; + tp->ts_recent = /*sc->sc_tsreflect*/to.to_tsval; + tp->ts_recent_age = tcp_ts_getticks(); + tp->ts_offset = /*sc->sc_tsoff*/0; // No syncookies, so this should always be 0 + } + + /* + * samkumar: there used to be code here that would set the + * TF_SIGNATURE flag on tp->t_flags if SCF_SIGNATURE is set on + * sc->sc_flags. I've left it in below, commented out. + */ +#if 0 + #ifdef TCP_SIGNATURE + if (sc->sc_flags & SCF_SIGNATURE) + tp->t_flags |= TF_SIGNATURE; + #endif +#endif + if (/*sc->sc_flags & SCF_SACK*/ to.to_flags & TOF_SACKPERM) + tp->t_flags |= TF_SACK_PERMIT; + } + if (/*sc->sc_flags & SCF_ECN*/(th->th_flags & (TH_ECE|TH_CWR)) && V_tcp_do_ecn) + tp->t_flags |= TF_ECN_PERMIT; + + /* + * Set up MSS and get cached values from tcp_hostcache. + * This might overwrite some of the defaults we just set. + */ + tcp_mss(tp, /*sc->sc_peer_mss*/(to.to_flags & TOF_MSS) ? to.to_mss : 0); + + tcp_output(tp); // to send the SYN-ACK + + tp->accepted_from = tpl; + return (IPPROTO_DONE); + } else if (tp->t_state == TCPS_LISTEN) { + /* + * When a listen socket is torn down the SO_ACCEPTCONN + * flag is removed first while connections are drained + * from the accept queue in a unlock/lock cycle of the + * ACCEPT_LOCK, opening a race condition allowing a SYN + * attempt go through unhandled. + */ + goto dropunlock; + } + + KASSERT(tp, ("tp is still NULL!")); + + /* + * samkumar: There used to be code here to verify TCP signatures. We don't + * support TCP signatures in TCPlp. + */ + + /* + * Segment belongs to a connection in SYN_SENT, ESTABLISHED or later + * state. tcp_do_segment() always consumes the mbuf chain, unlocks + * the inpcb, and unlocks pcbinfo. + */ + tcp_do_segment(ip6, th, msg, tp, drop_hdrlen, tlen, iptos, sig); + return (IPPROTO_DONE); + + /* + * samkumar: Removed some locking and debugging code under all three of + * these labels: dropwithreset, dropunlock, and drop. I also removed some + * memory management code (e.g., calling m_freem(m) if m != NULL) since + * the caller of this function will take care of that kind of memory + * management in TCPlp. + */ +dropwithreset: + + /* + * samkumar: The check against inp != NULL is now a check on tp != NULL. + */ + if (tp != NULL) { + tcp_dropwithreset(ip6, th, tp, tp->instance, tlen, rstreason); + } else + tcp_dropwithreset(ip6, th, NULL, tpl->instance, tlen, rstreason); + goto drop; + +dropunlock: +drop: + return (IPPROTO_DONE); +} + +/* + * samkumar: Original signature + * static void + * tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, + * struct tcpcb *tp, int drop_hdrlen, int tlen, uint8_t iptos, + * int ti_locked) + */ +static void +tcp_do_segment(struct ip6_hdr* ip6, struct tcphdr *th, otMessage* msg, + struct tcpcb *tp, int drop_hdrlen, int tlen, uint8_t iptos, + struct signals* sig) +{ + /* + * samkumar: All code pertaining to locks, stats, and debug has been + * removed from this function. + */ + + int thflags, acked, ourfinisacked, needoutput = 0; + int rstreason, todrop, win; + uint64_t tiwin; + struct tcpopt to; + uint32_t ticks = tcplp_sys_get_ticks(); + otInstance* instance = tp->instance; + thflags = th->th_flags; + tp->sackhint.last_sack_ack = 0; + + /* + * If this is either a state-changing packet or current state isn't + * established, we require a write lock on tcbinfo. Otherwise, we + * allow the tcbinfo to be in either alocked or unlocked, as the + * caller may have unnecessarily acquired a write lock due to a race. + */ + + /* samkumar: There used to be synchronization code here. */ + KASSERT(tp->t_state > TCPS_LISTEN, ("%s: TCPS_LISTEN", + __func__)); + KASSERT(tp->t_state != TCPS_TIME_WAIT, ("%s: TCPS_TIME_WAIT", + __func__)); + + /* + * Segment received on connection. + * Reset idle time and keep-alive timer. + * XXX: This should be done after segment + * validation to ignore broken/spoofed segs. + */ + tp->t_rcvtime = ticks; + if (TCPS_HAVEESTABLISHED(tp->t_state)) + tcp_timer_activate(tp, TT_KEEP, TP_KEEPIDLE(tp)); + + /* + * Scale up the window into a 32-bit value. + * For the SYN_SENT state the scale is zero. + */ + tiwin = th->th_win << tp->snd_scale; + + /* + * TCP ECN processing. + */ + /* + * samkumar: I intentionally left the TCPSTAT_INC lines below commented + * out, to avoid altering the structure of the code too much by + * reorganizing the switch statement. + */ + if (tp->t_flags & TF_ECN_PERMIT) { + if (thflags & TH_CWR) + tp->t_flags &= ~TF_ECN_SND_ECE; + switch (iptos & IPTOS_ECN_MASK) { + case IPTOS_ECN_CE: + tp->t_flags |= TF_ECN_SND_ECE; + //TCPSTAT_INC(tcps_ecn_ce); + break; + case IPTOS_ECN_ECT0: + //TCPSTAT_INC(tcps_ecn_ect0); + break; + case IPTOS_ECN_ECT1: + //TCPSTAT_INC(tcps_ecn_ect1); + break; + } + + /* Process a packet differently from RFC3168. */ + cc_ecnpkt_handler(tp, th, iptos); + + /* Congestion experienced. */ + if (thflags & TH_ECE) { + cc_cong_signal(tp, th, CC_ECN); + } + } + + /* + * Parse options on any incoming segment. + */ + tcp_dooptions(&to, (uint8_t *)(th + 1), + (th->th_off << 2) - sizeof(struct tcphdr), + (thflags & TH_SYN) ? TO_SYN : 0); + + /* + * If echoed timestamp is later than the current time, + * fall back to non RFC1323 RTT calculation. Normalize + * timestamp if syncookies were used when this connection + * was established. + */ + + if ((to.to_flags & TOF_TS) && (to.to_tsecr != 0)) { + to.to_tsecr -= tp->ts_offset; + if (TSTMP_GT(to.to_tsecr, tcp_ts_getticks())) + to.to_tsecr = 0; + } + /* + * If timestamps were negotiated during SYN/ACK they should + * appear on every segment during this session and vice versa. + */ + if ((tp->t_flags & TF_RCVD_TSTMP) && !(to.to_flags & TOF_TS)) { + /* samkumar: See above comment regarding tcp_log_addrs. */ + tcplp_sys_log("%s; %s: Timestamp missing, " + "no action\n", "", __func__); + } + if (!(tp->t_flags & TF_RCVD_TSTMP) && (to.to_flags & TOF_TS)) { + /* samkumar: See above comment regarding tcp_log_addrs. */ + tcplp_sys_log("%s; %s: Timestamp not expected, " + "no action\n", "", __func__); + } + + /* + * Process options only when we get SYN/ACK back. The SYN case + * for incoming connections is handled in tcp_syncache. + * According to RFC1323 the window field in a SYN (i.e., a + * or ) segment itself is never scaled. + * XXX this is traditional behavior, may need to be cleaned up. + */ + if (tp->t_state == TCPS_SYN_SENT && (thflags & TH_SYN)) { + if ((to.to_flags & TOF_SCALE) && + (tp->t_flags & TF_REQ_SCALE)) { + tp->t_flags |= TF_RCVD_SCALE; + tp->snd_scale = to.to_wscale; + } + /* + * Initial send window. It will be updated with + * the next incoming segment to the scaled value. + */ + tp->snd_wnd = th->th_win; + if (to.to_flags & TOF_TS) { + tp->t_flags |= TF_RCVD_TSTMP; + tp->ts_recent = to.to_tsval; + tp->ts_recent_age = tcp_ts_getticks(); + } + if (to.to_flags & TOF_MSS) + tcp_mss(tp, to.to_mss); + if ((tp->t_flags & TF_SACK_PERMIT) && + (to.to_flags & TOF_SACKPERM) == 0) + tp->t_flags &= ~TF_SACK_PERMIT; + } + /* + * Header prediction: check for the two common cases + * of a uni-directional data xfer. If the packet has + * no control flags, is in-sequence, the window didn't + * change and we're not retransmitting, it's a + * candidate. If the length is zero and the ack moved + * forward, we're the sender side of the xfer. Just + * free the data acked & wake any higher level process + * that was blocked waiting for space. If the length + * is non-zero and the ack didn't move, we're the + * receiver side. If we're getting packets in-order + * (the reassembly queue is empty), add the data to + * the socket buffer and note that we need a delayed ack. + * Make sure that the hidden state-flags are also off. + * Since we check for TCPS_ESTABLISHED first, it can only + * be TH_NEEDSYN. + */ + /* + * samkumar: Replaced LIST_EMPTY(&tp->tsegq with the call to bmp_isempty). + */ + if (tp->t_state == TCPS_ESTABLISHED && + th->th_seq == tp->rcv_nxt && + (thflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK && + tp->snd_nxt == tp->snd_max && + tiwin && tiwin == tp->snd_wnd && + ((tp->t_flags & (TF_NEEDSYN|TF_NEEDFIN)) == 0) && + bmp_isempty(tp->reassbmp, REASSBMP_SIZE(tp)) && + ((to.to_flags & TOF_TS) == 0 || + TSTMP_GEQ(to.to_tsval, tp->ts_recent)) ) { + + /* + * If last ACK falls within this segment's sequence numbers, + * record the timestamp. + * NOTE that the test is modified according to the latest + * proposal of the tcplw@cray.com list (Braden 1993/04/26). + */ + if ((to.to_flags & TOF_TS) != 0 && + SEQ_LEQ(th->th_seq, tp->last_ack_sent)) { + tp->ts_recent_age = tcp_ts_getticks(); + tp->ts_recent = to.to_tsval; + } + + if (tlen == 0) { + if (SEQ_GT(th->th_ack, tp->snd_una) && + SEQ_LEQ(th->th_ack, tp->snd_max) && + !IN_RECOVERY(tp->t_flags) && + (to.to_flags & TOF_SACK) == 0 && + TAILQ_EMPTY(&tp->snd_holes)) { + /* + * This is a pure ack for outstanding data. + */ + + /* + * "bad retransmit" recovery. + */ + if (tp->t_rxtshift == 1 && + tp->t_flags & TF_PREVVALID && + (int)(ticks - tp->t_badrxtwin) < 0) { + cc_cong_signal(tp, th, CC_RTO_ERR); + } + + /* + * Recalculate the transmit timer / rtt. + * + * Some boxes send broken timestamp replies + * during the SYN+ACK phase, ignore + * timestamps of 0 or we could calculate a + * huge RTT and blow up the retransmit timer. + */ + + if ((to.to_flags & TOF_TS) != 0 && + to.to_tsecr) { + uint32_t t; + + t = tcp_ts_getticks() - to.to_tsecr; + if (!tp->t_rttlow || tp->t_rttlow > t) + tp->t_rttlow = t; + tcp_xmit_timer(tp, + TCP_TS_TO_TICKS(t) + 1); + } else if (tp->t_rtttime && + SEQ_GT(th->th_ack, tp->t_rtseq)) { + if (!tp->t_rttlow || + tp->t_rttlow > ticks - tp->t_rtttime) + tp->t_rttlow = ticks - tp->t_rtttime; + tcp_xmit_timer(tp, + ticks - tp->t_rtttime); + } + + acked = BYTES_THIS_ACK(tp, th); + + /* + * samkumar: Replaced sbdrop(&so->so_snd, acked) with this call + * to lbuf_pop. + */ + { + uint32_t poppedbytes = lbuf_pop(&tp->sendbuf, acked, &sig->links_popped); + KASSERT(poppedbytes == acked, ("More bytes were acked than are in the send buffer")); + } + if (SEQ_GT(tp->snd_una, tp->snd_recover) && + SEQ_LEQ(th->th_ack, tp->snd_recover)) + tp->snd_recover = th->th_ack - 1; + + /* + * Let the congestion control algorithm update + * congestion control related information. This + * typically means increasing the congestion + * window. + */ + cc_ack_received(tp, th, CC_ACK); + + tp->snd_una = th->th_ack; + /* + * Pull snd_wl2 up to prevent seq wrap relative + * to th_ack. + */ + tp->snd_wl2 = th->th_ack; + tp->t_dupacks = 0; + + /* + * If all outstanding data are acked, stop + * retransmit timer, otherwise restart timer + * using current (possibly backed-off) value. + * If process is waiting for space, + * wakeup/selwakeup/signal. If data + * are ready to send, let tcp_output + * decide between more output or persist. + */ + + if (tp->snd_una == tp->snd_max) + tcp_timer_activate(tp, TT_REXMT, 0); + else if (!tcp_timer_active(tp, TT_PERSIST)) + tcp_timer_activate(tp, TT_REXMT, + tp->t_rxtcur); + + /* + * samkumar: There used to be a call to sowwakeup(so); here, + * which wakes up any threads waiting for the socket to + * become ready for writing. TCPlp handles its send buffer + * differently so we do not need to replace this call with + * specialized code to handle this. + */ + + /* + * samkumar: Replaced sbavail(&so->so_snd) with this call to + * lbuf_used_space. + */ + if (lbuf_used_space(&tp->sendbuf)) + (void) tcp_output(tp); + goto check_delack; + } + } else if (th->th_ack == tp->snd_una && + /* + * samkumar: Replaced sbspace(&so->so_rcv) with this call to + * cbuf_free_space. + */ + tlen <= cbuf_free_space(&tp->recvbuf)) { + + /* + * This is a pure, in-sequence data packet with + * nothing on the reassembly queue and we have enough + * buffer space to take it. + */ + /* Clean receiver SACK report if present */ + if ((tp->t_flags & TF_SACK_PERMIT) && tp->rcv_numsacks) + tcp_clean_sackreport(tp); + + tp->rcv_nxt += tlen; + /* + * Pull snd_wl1 up to prevent seq wrap relative to + * th_seq. + */ + tp->snd_wl1 = th->th_seq; + /* + * Pull rcv_up up to prevent seq wrap relative to + * rcv_nxt. + */ + tp->rcv_up = tp->rcv_nxt; + + /* + * Automatic sizing of receive socket buffer. Often the send + * buffer size is not optimally adjusted to the actual network + * conditions at hand (delay bandwidth product). Setting the + * buffer size too small limits throughput on links with high + * bandwidth and high delay (eg. trans-continental/oceanic links). + * + * On the receive side the socket buffer memory is only rarely + * used to any significant extent. This allows us to be much + * more aggressive in scaling the receive socket buffer. For + * the case that the buffer space is actually used to a large + * extent and we run out of kernel memory we can simply drop + * the new segments; TCP on the sender will just retransmit it + * later. Setting the buffer size too big may only consume too + * much kernel memory if the application doesn't read() from + * the socket or packet loss or reordering makes use of the + * reassembly queue. + * + * The criteria to step up the receive buffer one notch are: + * 1. Application has not set receive buffer size with + * SO_RCVBUF. Setting SO_RCVBUF clears SB_AUTOSIZE. + * 2. the number of bytes received during the time it takes + * one timestamp to be reflected back to us (the RTT); + * 3. received bytes per RTT is within seven eighth of the + * current socket buffer size; + * 4. receive buffer size has not hit maximal automatic size; + * + * This algorithm does one step per RTT at most and only if + * we receive a bulk stream w/o packet losses or reorderings. + * Shrinking the buffer during idle times is not necessary as + * it doesn't consume any memory when idle. + * + * TODO: Only step up if the application is actually serving + * the buffer to better manage the socket buffer resources. + */ + + /* + * samkumar: There used to be code here to dynamically size the + * receive buffer (tp->rfbuf_ts, rp->rfbuf_cnt, and the local + * newsize variable). In TCPlp, we don't support this, as the user + * allocates the receive buffer and its size can't be changed here. + * Therefore, I removed the code that does this. Note that the + * actual resizing of the buffer is done using sbreserve_locked, + * whose call comes later (not exactly where this comment is). + */ + + /* Add data to socket buffer. */ + + /* + * samkumar: The code that was here would just free the mbuf + * (with m_freem(m)) if SBS_CANTRCVMORE is set in + * so->so_rcv.sb_state. Otherwise, it would cut drop_hdrlen bytes + * from the mbuf (using m_adj(m, drop_hdrlen)) to discard the + * headers and then append the mbuf to the receive buffer using + * sbappendstream_locked(&so->so_rcv, m, 0). I've rewritten this + * to work the TCPlp way. The check to so->so_rcv.sb_state is + * replaced by a tcpiscantrcv call, and we copy bytes into + * TCPlp's circular buffer (since we designed it to avoid + * having dynamically-allocated memory for the receive buffer). + */ + + if (!tpiscantrcv(tp)) { + size_t usedbefore = cbuf_used_space(&tp->recvbuf); + cbuf_write(&tp->recvbuf, msg, otMessageGetOffset(msg) + drop_hdrlen, tlen, cbuf_copy_from_message); + if (usedbefore == 0 && tlen > 0) { + sig->recvbuf_notempty = true; + } + } else { + /* + * samkumar: We already know tlen != 0, so if we got here, then + * it means that we got data after we called SHUT_RD, or after + * receiving a FIN. I'm going to drop the connection in this + * case. I think FreeBSD might have just dropped the packet + * silently, but Linux handles it this way; this seems to be + * the right approach to me. + */ + tcp_drop(tp, ECONNABORTED); + goto drop; + } + /* NB: sorwakeup_locked() does an implicit unlock. */ + /* + * samkumar: There used to be a call to sorwakeup_locked(so); here, + * which wakes up any threads waiting for the socket to become + * become ready for reading. TCPlp handles its buffering + * differently so we do not need to replace this call with + * specialized code to handle this. + */ + if (DELAY_ACK(tp, tlen)) { + tp->t_flags |= TF_DELACK; + } else { + tp->t_flags |= TF_ACKNOW; + tcp_output(tp); + } + goto check_delack; + } + } + + /* + * Calculate amount of space in receive window, + * and then do TCP input processing. + * Receive window is amount of space in rcv queue, + * but not less than advertised window. + */ + /* samkumar: Replaced sbspace(&so->so_rcv) with call to cbuf_free_space. */ + win = cbuf_free_space(&tp->recvbuf); + if (win < 0) + win = 0; + tp->rcv_wnd = imax(win, (int)(tp->rcv_adv - tp->rcv_nxt)); + + /* Reset receive buffer auto scaling when not in bulk receive mode. */ + /* samkumar: Removed this receive buffer autoscaling code. */ + + switch (tp->t_state) { + + /* + * If the state is SYN_RECEIVED: + * if seg contains an ACK, but not for our SYN/ACK, send a RST. + * (Added by Sam) if seg is resending the original SYN, resend the SYN/ACK + */ + /* + * samkumar: If we receive a retransmission of the original SYN, then + * resend the SYN/ACK segment. This case was probably handled by the + * SYN cache. Because TCPlp does not use a SYN cache, we need to write + * custom logic for it. It is handled in the "else if" clause below. + */ + case TCPS_SYN_RECEIVED: + if ((thflags & TH_ACK) && + (SEQ_LEQ(th->th_ack, tp->snd_una) || + SEQ_GT(th->th_ack, tp->snd_max))) { + rstreason = BANDLIM_RST_OPENPORT; + goto dropwithreset; + } else if ((thflags & TH_SYN) && !(thflags & TH_ACK) && (th->th_seq == tp->irs)) { + tp->t_flags |= TF_ACKNOW; + } + break; + + /* + * If the state is SYN_SENT: + * if seg contains an ACK, but not for our SYN, drop the input. + * if seg contains a RST, then drop the connection. + * if seg does not contain SYN, then drop it. + * Otherwise this is an acceptable SYN segment + * initialize tp->rcv_nxt and tp->irs + * if seg contains ack then advance tp->snd_una + * if seg contains an ECE and ECN support is enabled, the stream + * is ECN capable. + * if SYN has been acked change to ESTABLISHED else SYN_RCVD state + * arrange for segment to be acked (eventually) + * continue processing rest of data/controls, beginning with URG + */ + case TCPS_SYN_SENT: + if ((thflags & TH_ACK) && + (SEQ_LEQ(th->th_ack, tp->iss) || + SEQ_GT(th->th_ack, tp->snd_max))) { + rstreason = BANDLIM_UNLIMITED; + goto dropwithreset; + } + if ((thflags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) { + tp = tcp_drop(tp, ECONNREFUSED); + } + if (thflags & TH_RST) + goto drop; + if (!(thflags & TH_SYN)) + goto drop; + + tp->irs = th->th_seq; + tcp_rcvseqinit(tp); + if (thflags & TH_ACK) { + /* + * samkumar: Removed call to soisconnected(so), since TCPlp has its + * own buffering. + */ + + /* Do window scaling on this connection? */ + if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == + (TF_RCVD_SCALE|TF_REQ_SCALE)) { + tp->rcv_scale = tp->request_r_scale; + } + tp->rcv_adv += imin(tp->rcv_wnd, + TCP_MAXWIN << tp->rcv_scale); + tp->snd_una++; /* SYN is acked */ + /* + * If there's data, delay ACK; if there's also a FIN + * ACKNOW will be turned on later. + */ + if (DELAY_ACK(tp, tlen) && tlen != 0) + tcp_timer_activate(tp, TT_DELACK, + tcp_delacktime); + else + tp->t_flags |= TF_ACKNOW; + + if ((thflags & TH_ECE) && V_tcp_do_ecn) { + tp->t_flags |= TF_ECN_PERMIT; + } + + /* + * Received in SYN_SENT[*] state. + * Transitions: + * SYN_SENT --> ESTABLISHED + * SYN_SENT* --> FIN_WAIT_1 + */ + tp->t_starttime = ticks; + if (tp->t_flags & TF_NEEDFIN) { + tcp_state_change(tp, TCPS_FIN_WAIT_1); + tp->t_flags &= ~TF_NEEDFIN; + thflags &= ~TH_SYN; + } else { + tcp_state_change(tp, TCPS_ESTABLISHED); + /* samkumar: Set conn_established signal for TCPlp. */ + sig->conn_established = true; + cc_conn_init(tp); + tcp_timer_activate(tp, TT_KEEP, + TP_KEEPIDLE(tp)); + } + } else { + /* + * Received initial SYN in SYN-SENT[*] state => + * simultaneous open. + * If it succeeds, connection is * half-synchronized. + * Otherwise, do 3-way handshake: + * SYN-SENT -> SYN-RECEIVED + * SYN-SENT* -> SYN-RECEIVED* + */ + tp->t_flags |= (TF_ACKNOW | TF_NEEDSYN); + tcp_timer_activate(tp, TT_REXMT, 0); + tcp_state_change(tp, TCPS_SYN_RECEIVED); + /* + * samkumar: We would have incremented snd_next in tcp_output when + * we sent the original SYN, so decrement it here. (Another + * consequence of removing the SYN cache.) + */ + tp->snd_nxt--; + } + + /* + * Advance th->th_seq to correspond to first data byte. + * If data, trim to stay within window, + * dropping FIN if necessary. + */ + th->th_seq++; + if (tlen > tp->rcv_wnd) { + todrop = tlen - tp->rcv_wnd; + /* + * samkumar: I removed a call to m_adj(m, -todrop), which intends + * to trim the data so it fits in the window. We can just read less + * when copying into the receive buffer in TCPlp, so we don't need + * to do this. + */ + (void) todrop; /* samkumar: Prevent a compiler warning */ + tlen = tp->rcv_wnd; + thflags &= ~TH_FIN; + } + tp->snd_wl1 = th->th_seq - 1; + tp->rcv_up = th->th_seq; + /* + * Client side of transaction: already sent SYN and data. + * If the remote host used T/TCP to validate the SYN, + * our data will be ACK'd; if so, enter normal data segment + * processing in the middle of step 5, ack processing. + * Otherwise, goto step 6. + */ + if (thflags & TH_ACK) + goto process_ACK; + + goto step6; + + /* + * If the state is LAST_ACK or CLOSING or TIME_WAIT: + * do normal processing. + * + * NB: Leftover from RFC1644 T/TCP. Cases to be reused later. + */ + case TCPS_LAST_ACK: + case TCPS_CLOSING: + break; /* continue normal processing */ + } + + /* + * States other than LISTEN or SYN_SENT. + * First check the RST flag and sequence number since reset segments + * are exempt from the timestamp and connection count tests. This + * fixes a bug introduced by the Stevens, vol. 2, p. 960 bugfix + * below which allowed reset segments in half the sequence space + * to fall though and be processed (which gives forged reset + * segments with a random sequence number a 50 percent chance of + * killing a connection). + * Then check timestamp, if present. + * Then check the connection count, if present. + * Then check that at least some bytes of segment are within + * receive window. If segment begins before rcv_nxt, + * drop leading data (and SYN); if nothing left, just ack. + */ + if (thflags & TH_RST) { + /* + * RFC5961 Section 3.2 + * + * - RST drops connection only if SEG.SEQ == RCV.NXT. + * - If RST is in window, we send challenge ACK. + * + * Note: to take into account delayed ACKs, we should + * test against last_ack_sent instead of rcv_nxt. + * Note 2: we handle special case of closed window, not + * covered by the RFC. + */ + if ((SEQ_GEQ(th->th_seq, tp->last_ack_sent) && + SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) || + (tp->rcv_wnd == 0 && tp->last_ack_sent == th->th_seq)) { + + /* + * samkumar: This if statement used to also be prefaced with + * "V_tcp_insecure_rst ||". But I removed it, since there's no + * reason to support an insecure option in TCPlp (my guess is that + * FreeBSD supported it for legacy reasons). + */ + if (tp->last_ack_sent == th->th_seq) { + /* + * samkumar: Normally, the error number would be stored in + * so->so_error. Instead, we put it in this "droperror" local + * variable and then pass it to tcplp_sys_connection_lost. + */ + int droperror = 0; + /* Drop the connection. */ + switch (tp->t_state) { + case TCPS_SYN_RECEIVED: + droperror = ECONNREFUSED; + goto close; + case TCPS_ESTABLISHED: + case TCPS_FIN_WAIT_1: + case TCPS_FIN_WAIT_2: + case TCPS_CLOSE_WAIT: + droperror = ECONNRESET; + close: + tcp_state_change(tp, TCPS_CLOSED); + /* FALLTHROUGH */ + default: + tp = tcp_close(tp); + tcplp_sys_connection_lost(tp, droperror); + } + } else { + /* Send challenge ACK. */ + tcp_respond(tp, tp->instance, ip6, th, tp->rcv_nxt, tp->snd_nxt, TH_ACK); + tp->last_ack_sent = tp->rcv_nxt; + } + } + goto drop; + } + + /* + * RFC5961 Section 4.2 + * Send challenge ACK for any SYN in synchronized state. + */ + /* + * samkumar: I added the check for the SYN-RECEIVED state in this if + * statement (another consequence of removing the SYN cache). + */ + if ((thflags & TH_SYN) && tp->t_state != TCPS_SYN_SENT && tp->t_state != TCP6S_SYN_RECEIVED) { + /* + * samkumar: The modern way to handle this is to send a Challenge ACK. + * FreeBSD supports this, but it also has this V_tcp_insecure_syn + * options that will cause it to drop the connection if the SYN falls + * in the receive window. In TCPlp we *only* support Challenge ACKs + * (the secure way of doing it), so I've removed code for the insecure + * way. (Presumably the reason why FreeBSD supports the insecure way is + * for legacy code, which we don't really care about in TCPlp). + */ + /* Send challenge ACK. */ + tcplp_sys_log("Sending challenge ACK\n"); + tcp_respond(tp, tp->instance, ip6, th, tp->rcv_nxt, tp->snd_nxt, TH_ACK); + tp->last_ack_sent = tp->rcv_nxt; + goto drop; + } + + /* + * RFC 1323 PAWS: If we have a timestamp reply on this segment + * and it's less than ts_recent, drop it. + */ + if ((to.to_flags & TOF_TS) != 0 && tp->ts_recent && + TSTMP_LT(to.to_tsval, tp->ts_recent)) { + + /* Check to see if ts_recent is over 24 days old. */ + if (tcp_ts_getticks() - tp->ts_recent_age > TCP_PAWS_IDLE) { + /* + * Invalidate ts_recent. If this segment updates + * ts_recent, the age will be reset later and ts_recent + * will get a valid value. If it does not, setting + * ts_recent to zero will at least satisfy the + * requirement that zero be placed in the timestamp + * echo reply when ts_recent isn't valid. The + * age isn't reset until we get a valid ts_recent + * because we don't want out-of-order segments to be + * dropped when ts_recent is old. + */ + tp->ts_recent = 0; + } else { + if (tlen) + goto dropafterack; + goto drop; + } + } + + /* + * In the SYN-RECEIVED state, validate that the packet belongs to + * this connection before trimming the data to fit the receive + * window. Check the sequence number versus IRS since we know + * the sequence numbers haven't wrapped. This is a partial fix + * for the "LAND" DoS attack. + */ + if (tp->t_state == TCPS_SYN_RECEIVED && SEQ_LT(th->th_seq, tp->irs)) { + rstreason = BANDLIM_RST_OPENPORT; + goto dropwithreset; + } + + todrop = tp->rcv_nxt - th->th_seq; + if (todrop > 0) { + if (thflags & TH_SYN) { + thflags &= ~TH_SYN; + th->th_seq++; + if (th->th_urp > 1) + th->th_urp--; + else + thflags &= ~TH_URG; + todrop--; + } + /* + * Following if statement from Stevens, vol. 2, p. 960. + */ + if (todrop > tlen + || (todrop == tlen && (thflags & TH_FIN) == 0)) { + /* + * Any valid FIN must be to the left of the window. + * At this point the FIN must be a duplicate or out + * of sequence; drop it. + */ + thflags &= ~TH_FIN; + + /* + * Send an ACK to resynchronize and drop any data. + * But keep on processing for RST or ACK. + */ + tp->t_flags |= TF_ACKNOW; + todrop = tlen; + } + /* samkumar: There was an else case that only collected stats. */ + drop_hdrlen += todrop; /* drop from the top afterwards */ + th->th_seq += todrop; + tlen -= todrop; + if (th->th_urp > todrop) + th->th_urp -= todrop; + else { + thflags &= ~TH_URG; + th->th_urp = 0; + } + } + + /* + * If new data are received on a connection after the + * user processes are gone, then RST the other end. + */ + /* + * samkumar: TCPlp is designed for embedded systems where there is no + * concept of a "process" that has allocated a TCP socket. Therefore, we + * do not implement the functionality in the above comment (the code for + * it used to be here, and I removed it). + */ + /* + * If segment ends after window, drop trailing data + * (and PUSH and FIN); if nothing left, just ACK. + */ + todrop = (th->th_seq + tlen) - (tp->rcv_nxt + tp->rcv_wnd); + if (todrop > 0) { + if (todrop >= tlen) { + /* + * If window is closed can only take segments at + * window edge, and have to drop data and PUSH from + * incoming segments. Continue processing, but + * remember to ack. Otherwise, drop segment + * and ack. + */ + if (tp->rcv_wnd == 0 && th->th_seq == tp->rcv_nxt) { + tp->t_flags |= TF_ACKNOW; + } else + goto dropafterack; + } + /* + * samkumar: I removed a call to m_adj(m, -todrop), which intends + * to trim the data so it fits in the window. We can just read less + * when copying into the receive buffer in TCPlp, so we don't need + * to do this. Subtracting it from tlen gives us enough information to + * do this later. In FreeBSD, this isn't possible because the mbuf + * itself becomes part of the receive buffer, so the mbuf has to be + * trimmed in order for this to work out. + */ + tlen -= todrop; + thflags &= ~(TH_PUSH|TH_FIN); + } + + /* + * If last ACK falls within this segment's sequence numbers, + * record its timestamp. + * NOTE: + * 1) That the test incorporates suggestions from the latest + * proposal of the tcplw@cray.com list (Braden 1993/04/26). + * 2) That updating only on newer timestamps interferes with + * our earlier PAWS tests, so this check should be solely + * predicated on the sequence space of this segment. + * 3) That we modify the segment boundary check to be + * Last.ACK.Sent <= SEG.SEQ + SEG.Len + * instead of RFC1323's + * Last.ACK.Sent < SEG.SEQ + SEG.Len, + * This modified check allows us to overcome RFC1323's + * limitations as described in Stevens TCP/IP Illustrated + * Vol. 2 p.869. In such cases, we can still calculate the + * RTT correctly when RCV.NXT == Last.ACK.Sent. + */ + + if ((to.to_flags & TOF_TS) != 0 && + SEQ_LEQ(th->th_seq, tp->last_ack_sent) && + SEQ_LEQ(tp->last_ack_sent, th->th_seq + tlen + + ((thflags & (TH_SYN|TH_FIN)) != 0))) { + tp->ts_recent_age = tcp_ts_getticks(); + tp->ts_recent = to.to_tsval; + } + + /* + * If the ACK bit is off: if in SYN-RECEIVED state or SENDSYN + * flag is on (half-synchronized state), then queue data for + * later processing; else drop segment and return. + */ + if ((thflags & TH_ACK) == 0) { + if (tp->t_state == TCPS_SYN_RECEIVED || + (tp->t_flags & TF_NEEDSYN)) + goto step6; + else if (tp->t_flags & TF_ACKNOW) + goto dropafterack; + else + goto drop; + } + + tcplp_sys_log("Processing ACK\n"); + + /* + * Ack processing. + */ + switch (tp->t_state) { + + /* + * In SYN_RECEIVED state, the ack ACKs our SYN, so enter + * ESTABLISHED state and continue processing. + * The ACK was checked above. + */ + case TCPS_SYN_RECEIVED: + /* + * samkumar: Removed call to soisconnected(so), since TCPlp has its + * own buffering. + */ + /* Do window scaling? */ + if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == + (TF_RCVD_SCALE|TF_REQ_SCALE)) { + tp->rcv_scale = tp->request_r_scale; + tp->snd_wnd = tiwin; + } + /* + * Make transitions: + * SYN-RECEIVED -> ESTABLISHED + * SYN-RECEIVED* -> FIN-WAIT-1 + */ + tp->t_starttime = ticks; + if (tp->t_flags & TF_NEEDFIN) { + tcp_state_change(tp, TCPS_FIN_WAIT_1); + tp->t_flags &= ~TF_NEEDFIN; + } else { + tcp_state_change(tp, TCPS_ESTABLISHED); + /* samkumar: Set conn_established signal for TCPlp. */ + sig->conn_established = true; + cc_conn_init(tp); + tcp_timer_activate(tp, TT_KEEP, TP_KEEPIDLE(tp)); + /* + * samkumar: I added this check to account for simultaneous open. + * If this socket was opened actively, then the fact that we are + * in SYN-RECEIVED indicates that we are in simultaneous open. + * Therefore, don't ACK the SYN-ACK (unless it contains data or + * something, which will be processed later). + */ + if (!tpispassiveopen(tp)) { + tp->t_flags &= ~TF_ACKNOW; + } else { + /* + * samkumar: Otherwise, we entered the ESTABLISHED state by + * accepting a connection, so call the appropriate callback in + * TCPlp. TODO: consider using signals to handle this? + */ + bool accepted = tcplp_sys_accepted_connection(tp->accepted_from, tp, &ip6->ip6_src, th->th_sport); + if (!accepted) { + rstreason = ECONNREFUSED; + goto dropwithreset; + } + } + } + /* + * If segment contains data or ACK, will call tcp_reass() + * later; if not, do so now to pass queued data to user. + */ + if (tlen == 0 && (thflags & TH_FIN) == 0) + (void) tcp_reass(tp, (struct tcphdr *)0, 0, + (otMessage*)0, 0, sig); + + tp->snd_wl1 = th->th_seq - 1; + /* FALLTHROUGH */ + + /* + * In ESTABLISHED state: drop duplicate ACKs; ACK out of range + * ACKs. If the ack is in the range + * tp->snd_una < th->th_ack <= tp->snd_max + * then advance tp->snd_una to th->th_ack and drop + * data from the retransmission queue. If this ACK reflects + * more up to date window information we update our window information. + */ + case TCPS_ESTABLISHED: + case TCPS_FIN_WAIT_1: + case TCPS_FIN_WAIT_2: + case TCPS_CLOSE_WAIT: + case TCPS_CLOSING: + case TCPS_LAST_ACK: + if (SEQ_GT(th->th_ack, tp->snd_max)) { + goto dropafterack; + } + + if ((tp->t_flags & TF_SACK_PERMIT) && + ((to.to_flags & TOF_SACK) || + !TAILQ_EMPTY(&tp->snd_holes))) + tcp_sack_doack(tp, &to, th->th_ack); + + if (SEQ_LEQ(th->th_ack, tp->snd_una)) { + if (tlen == 0 && tiwin == tp->snd_wnd) { + /* + * If this is the first time we've seen a + * FIN from the remote, this is not a + * duplicate and it needs to be processed + * normally. This happens during a + * simultaneous close. + */ + if ((thflags & TH_FIN) && + (TCPS_HAVERCVDFIN(tp->t_state) == 0)) { + tp->t_dupacks = 0; + break; + } + /* + * If we have outstanding data (other than + * a window probe), this is a completely + * duplicate ack (ie, window info didn't + * change and FIN isn't set), + * the ack is the biggest we've + * seen and we've seen exactly our rexmt + * threshhold of them, assume a packet + * has been dropped and retransmit it. + * Kludge snd_nxt & the congestion + * window so we send only this one + * packet. + * + * We know we're losing at the current + * window size so do congestion avoidance + * (set ssthresh to half the current window + * and pull our congestion window back to + * the new ssthresh). + * + * Dup acks mean that packets have left the + * network (they're now cached at the receiver) + * so bump cwnd by the amount in the receiver + * to keep a constant cwnd packets in the + * network. + * + * When using TCP ECN, notify the peer that + * we reduced the cwnd. + */ + if (!tcp_timer_active(tp, TT_REXMT) || + th->th_ack != tp->snd_una) + tp->t_dupacks = 0; + else if (++tp->t_dupacks > tcprexmtthresh || + IN_FASTRECOVERY(tp->t_flags)) { + cc_ack_received(tp, th, CC_DUPACK); + if ((tp->t_flags & TF_SACK_PERMIT) && + IN_FASTRECOVERY(tp->t_flags)) { + int awnd; + + /* + * Compute the amount of data in flight first. + * We can inject new data into the pipe iff + * we have less than 1/2 the original window's + * worth of data in flight. + */ + awnd = (tp->snd_nxt - tp->snd_fack) + + tp->sackhint.sack_bytes_rexmit; + if (awnd < tp->snd_ssthresh) { + tp->snd_cwnd += tp->t_maxseg; + if (tp->snd_cwnd > tp->snd_ssthresh) + tp->snd_cwnd = tp->snd_ssthresh; + } + } else + tp->snd_cwnd += tp->t_maxseg; +#ifdef INSTRUMENT_TCP + printf("TCP DUPACK\n"); +#endif + (void) tcp_output(tp); + goto drop; + } else if (tp->t_dupacks == tcprexmtthresh) { + tcp_seq onxt = tp->snd_nxt; + + /* + * If we're doing sack, check to + * see if we're already in sack + * recovery. If we're not doing sack, + * check to see if we're in newreno + * recovery. + */ + if (tp->t_flags & TF_SACK_PERMIT) { + if (IN_FASTRECOVERY(tp->t_flags)) { + tp->t_dupacks = 0; + break; + } + } else { + if (SEQ_LEQ(th->th_ack, + tp->snd_recover)) { + tp->t_dupacks = 0; + break; + } + } + /* Congestion signal before ack. */ + cc_cong_signal(tp, th, CC_NDUPACK); + cc_ack_received(tp, th, CC_DUPACK); + tcp_timer_activate(tp, TT_REXMT, 0); + tp->t_rtttime = 0; + +#ifdef INSTRUMENT_TCP + printf("TCP DUPACK_THRESH\n"); +#endif + if (tp->t_flags & TF_SACK_PERMIT) { + tp->sack_newdata = tp->snd_nxt; + tp->snd_cwnd = tp->t_maxseg; + (void) tcp_output(tp); + goto drop; + } + + tp->snd_nxt = th->th_ack; + tp->snd_cwnd = tp->t_maxseg; + (void) tcp_output(tp); + tp->snd_cwnd = tp->snd_ssthresh + + tp->t_maxseg * + (tp->t_dupacks - tp->snd_limited); +#ifdef INSTRUMENT_TCP + printf("TCP SET_cwnd %d\n", (int) tp->snd_cwnd); +#endif + if (SEQ_GT(onxt, tp->snd_nxt)) + tp->snd_nxt = onxt; + goto drop; + } else if (V_tcp_do_rfc3042) { + /* + * Process first and second duplicate + * ACKs. Each indicates a segment + * leaving the network, creating room + * for more. Make sure we can send a + * packet on reception of each duplicate + * ACK by increasing snd_cwnd by one + * segment. Restore the original + * snd_cwnd after packet transmission. + */ + uint64_t oldcwnd; + tcp_seq oldsndmax; + uint32_t sent; + int avail; + cc_ack_received(tp, th, CC_DUPACK); + oldcwnd = tp->snd_cwnd; + oldsndmax = tp->snd_max; + +#ifdef INSTRUMENT_TCP + printf("TCP LIM_TRANS\n"); +#endif + + KASSERT(tp->t_dupacks == 1 || + tp->t_dupacks == 2, + ("%s: dupacks not 1 or 2", + __func__)); + if (tp->t_dupacks == 1) + tp->snd_limited = 0; + tp->snd_cwnd = + (tp->snd_nxt - tp->snd_una) + + (tp->t_dupacks - tp->snd_limited) * + tp->t_maxseg; + /* + * Only call tcp_output when there + * is new data available to be sent. + * Otherwise we would send pure ACKs. + */ + /* + * samkumar: Replace sbavail(&so->so_snd) with the call to + * lbuf_used_space. + */ + avail = lbuf_used_space(&tp->sendbuf) - + (tp->snd_nxt - tp->snd_una); + if (avail > 0) + (void) tcp_output(tp); + sent = tp->snd_max - oldsndmax; + if (sent > tp->t_maxseg) { + KASSERT((tp->t_dupacks == 2 && + tp->snd_limited == 0) || + (sent == tp->t_maxseg + 1 && + tp->t_flags & TF_SENTFIN), + ("%s: sent too much", + __func__)); + tp->snd_limited = 2; + } else if (sent > 0) + ++tp->snd_limited; + tp->snd_cwnd = oldcwnd; +#ifdef INSTRUMENT_TCP + printf("TCP RESET_cwnd %d\n", (int) tp->snd_cwnd); +#endif + goto drop; + } + } else + tp->t_dupacks = 0; + break; + } + + KASSERT(SEQ_GT(th->th_ack, tp->snd_una), + ("%s: th_ack <= snd_una", __func__)); + + /* + * If the congestion window was inflated to account + * for the other side's cached packets, retract it. + */ + if (IN_FASTRECOVERY(tp->t_flags)) { + if (SEQ_LT(th->th_ack, tp->snd_recover)) { + if (tp->t_flags & TF_SACK_PERMIT) + tcp_sack_partialack(tp, th); + else + tcp_newreno_partial_ack(tp, th); + } else + cc_post_recovery(tp, th); + } + + tp->t_dupacks = 0; + /* + * If we reach this point, ACK is not a duplicate, + * i.e., it ACKs something we sent. + */ + if (tp->t_flags & TF_NEEDSYN) { + /* + * T/TCP: Connection was half-synchronized, and our + * SYN has been ACK'd (so connection is now fully + * synchronized). Go to non-starred state, + * increment snd_una for ACK of SYN, and check if + * we can do window scaling. + */ + tp->t_flags &= ~TF_NEEDSYN; + tp->snd_una++; + /* Do window scaling? */ + if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == + (TF_RCVD_SCALE|TF_REQ_SCALE)) { + tp->rcv_scale = tp->request_r_scale; + /* Send window already scaled. */ + } + } + +process_ACK: + acked = BYTES_THIS_ACK(tp, th); + + tcplp_sys_log("Bytes acked: %d\n", acked); + /* + * If we just performed our first retransmit, and the ACK + * arrives within our recovery window, then it was a mistake + * to do the retransmit in the first place. Recover our + * original cwnd and ssthresh, and proceed to transmit where + * we left off. + */ + if (tp->t_rxtshift == 1 && tp->t_flags & TF_PREVVALID && + (int)(ticks - tp->t_badrxtwin) < 0) + cc_cong_signal(tp, th, CC_RTO_ERR); + + /* + * If we have a timestamp reply, update smoothed + * round trip time. If no timestamp is present but + * transmit timer is running and timed sequence + * number was acked, update smoothed round trip time. + * Since we now have an rtt measurement, cancel the + * timer backoff (cf., Phil Karn's retransmit alg.). + * Recompute the initial retransmit timer. + * + * Some boxes send broken timestamp replies + * during the SYN+ACK phase, ignore + * timestamps of 0 or we could calculate a + * huge RTT and blow up the retransmit timer. + */ + + if ((to.to_flags & TOF_TS) != 0 && to.to_tsecr) { + uint32_t t; + + t = tcp_ts_getticks() - to.to_tsecr; + if (!tp->t_rttlow || tp->t_rttlow > t) + tp->t_rttlow = t; + tcp_xmit_timer(tp, TCP_TS_TO_TICKS(t) + 1); + } else if (tp->t_rtttime && SEQ_GT(th->th_ack, tp->t_rtseq)) { + if (!tp->t_rttlow || tp->t_rttlow > ticks - tp->t_rtttime) + tp->t_rttlow = ticks - tp->t_rtttime; + tcp_xmit_timer(tp, ticks - tp->t_rtttime); + } + + /* + * If all outstanding data is acked, stop retransmit + * timer and remember to restart (more output or persist). + * If there is more data to be acked, restart retransmit + * timer, using current (possibly backed-off) value. + */ + if (th->th_ack == tp->snd_max) { + tcp_timer_activate(tp, TT_REXMT, 0); + needoutput = 1; + } else if (!tcp_timer_active(tp, TT_PERSIST)) { + tcp_timer_activate(tp, TT_REXMT, tp->t_rxtcur); + } + + /* + * If no data (only SYN) was ACK'd, + * skip rest of ACK processing. + */ + if (acked == 0) + goto step6; + + /* + * Let the congestion control algorithm update congestion + * control related information. This typically means increasing + * the congestion window. + */ + cc_ack_received(tp, th, CC_ACK); + + /* + * samkumar: I replaced the calls to sbavail(&so->so_snd) with new + * calls to lbuf_used_space, and then I modified the code to actually + * remove code from the send buffer, formerly done via + * sbcut_locked(&so->so_send, (int)sbavail(&so->so_snd)) in the if case + * and sbcut_locked(&so->so_snd, acked) in the else case, to use the + * data structures for TCPlp's data buffering. + */ + if (acked > lbuf_used_space(&tp->sendbuf)) { + uint32_t poppedbytes; + uint32_t usedspace = lbuf_used_space(&tp->sendbuf); + tp->snd_wnd -= usedspace; + poppedbytes = lbuf_pop(&tp->sendbuf, usedspace, &sig->links_popped); + KASSERT(poppedbytes == usedspace, ("Could not fully empty send buffer")); + ourfinisacked = 1; + } else { + uint32_t poppedbytes = lbuf_pop(&tp->sendbuf, acked, &sig->links_popped); + KASSERT(poppedbytes == acked, ("Could not remove acked bytes from send buffer")); + tp->snd_wnd -= acked; + ourfinisacked = 0; + } + /* NB: sowwakeup_locked() does an implicit unlock. */ + /* + * samkumar: There used to be a call to sowwakeup(so); here, + * which wakes up any threads waiting for the socket to + * become ready for writing. TCPlp handles its send buffer + * differently so we do not need to replace this call with + * specialized code to handle this. + */ + /* Detect una wraparound. */ + if (!IN_RECOVERY(tp->t_flags) && + SEQ_GT(tp->snd_una, tp->snd_recover) && + SEQ_LEQ(th->th_ack, tp->snd_recover)) + tp->snd_recover = th->th_ack - 1; + /* XXXLAS: Can this be moved up into cc_post_recovery? */ + if (IN_RECOVERY(tp->t_flags) && + SEQ_GEQ(th->th_ack, tp->snd_recover)) { + EXIT_RECOVERY(tp->t_flags); + } + tp->snd_una = th->th_ack; + if (tp->t_flags & TF_SACK_PERMIT) { + if (SEQ_GT(tp->snd_una, tp->snd_recover)) + tp->snd_recover = tp->snd_una; + } + if (SEQ_LT(tp->snd_nxt, tp->snd_una)) + tp->snd_nxt = tp->snd_una; + + switch (tp->t_state) { + + /* + * In FIN_WAIT_1 STATE in addition to the processing + * for the ESTABLISHED state if our FIN is now acknowledged + * then enter FIN_WAIT_2. + */ + case TCPS_FIN_WAIT_1: + if (ourfinisacked) { + /* + * If we can't receive any more + * data, then closing user can proceed. + * Starting the timer is contrary to the + * specification, but if we don't get a FIN + * we'll hang forever. + * + * XXXjl: + * we should release the tp also, and use a + * compressed state. + */ + /* + * samkumar: I replaced a check for the SBS_CANTRCVMORE flag + * in so->so_rcv.sb_state with a call to tcpiscantrcv. + */ + if (tpiscantrcv(tp)) { + /* samkumar: Removed a call to soisdisconnected(so). */ + tcp_timer_activate(tp, TT_2MSL, + (tcp_fast_finwait2_recycle ? + tcp_finwait2_timeout : + TP_MAXIDLE(tp))); + } + tcp_state_change(tp, TCPS_FIN_WAIT_2); + } + break; + + /* + * In CLOSING STATE in addition to the processing for + * the ESTABLISHED state if the ACK acknowledges our FIN + * then enter the TIME-WAIT state, otherwise ignore + * the segment. + */ + case TCPS_CLOSING: + if (ourfinisacked) { + /* + * samkumar: I added the line below. We need to avoid sending + * an ACK in the TIME-WAIT state, since we don't want to + * ACK ACKs. This edge case appears because TCPlp, unlike the + * original FreeBSD code, uses tcpcbs for connections in the + * TIME-WAIT state (FreeBSD uses a different, smaller + * structure). + */ + tp->t_flags &= ~TF_ACKNOW; + tcp_twstart(tp); + return; + } + break; + + /* + * In LAST_ACK, we may still be waiting for data to drain + * and/or to be acked, as well as for the ack of our FIN. + * If our FIN is now acknowledged, delete the TCB, + * enter the closed state and return. + */ + case TCPS_LAST_ACK: + if (ourfinisacked) { + tp = tcp_close(tp); + tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL); + goto drop; + } + break; + } + } + +step6: + + /* + * Update window information. + * Don't look at window if no ACK: TAC's send garbage on first SYN. + */ + if ((thflags & TH_ACK) && + (SEQ_LT(tp->snd_wl1, th->th_seq) || + (tp->snd_wl1 == th->th_seq && (SEQ_LT(tp->snd_wl2, th->th_ack) || + (tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd))))) { + /* keep track of pure window updates */ + /* + * samkumar: There used to be an if statement here that would check if + * this is a "pure" window update (tlen == 0 && + * tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd) and keep + * statistics for how often that happens. + */ + tp->snd_wnd = tiwin; + tp->snd_wl1 = th->th_seq; + tp->snd_wl2 = th->th_ack; + if (tp->snd_wnd > tp->max_sndwnd) + tp->max_sndwnd = tp->snd_wnd; + needoutput = 1; + } + + /* + * Process segments with URG. + */ + /* + * samkumar: TCPlp does not support the urgent pointer, so we omit all + * urgent-pointer-related processing and buffering. The code below is the + * code that was in the "else" case that handles no valid urgent data in + * the received packet. + */ + { + /* + * If no out of band data is expected, + * pull receive urgent pointer along + * with the receive window. + */ + if (SEQ_GT(tp->rcv_nxt, tp->rcv_up)) + tp->rcv_up = tp->rcv_nxt; + } + + /* + * Process the segment text, merging it into the TCP sequencing queue, + * and arranging for acknowledgment of receipt if necessary. + * This process logically involves adjusting tp->rcv_wnd as data + * is presented to the user (this happens in tcp_usrreq.c, + * case PRU_RCVD). If a FIN has already been received on this + * connection then we just ignore the text. + */ + if ((tlen || (thflags & TH_FIN)) && + TCPS_HAVERCVDFIN(tp->t_state) == 0) { + tcp_seq save_start = th->th_seq; + /* + * samkumar: I removed a call to m_adj(m, drop_hdrlen), which intends + * to drop data from the mbuf so it can be chained into the receive + * header. This is not necessary for TCPlp because we copy the data + * anyway; we just add the offset when copying data into the receive + * buffer. + */ + /* + * Insert segment which includes th into TCP reassembly queue + * with control block tp. Set thflags to whether reassembly now + * includes a segment with FIN. This handles the common case + * inline (segment is the next to be received on an established + * connection, and the queue is empty), avoiding linkage into + * and removal from the queue and repetition of various + * conversions. + * Set DELACK for segments received in order, but ack + * immediately when segments are out of order (so + * fast retransmit can work). + */ + /* + * samkumar: I replaced LIST_EMPTY(&tp->t_segq) with the calls to + * tpiscantrcv and bmp_isempty on the second line below. + */ + if (th->th_seq == tp->rcv_nxt && + (tpiscantrcv(tp) || bmp_isempty(tp->reassbmp, REASSBMP_SIZE(tp))) && + TCPS_HAVEESTABLISHED(tp->t_state)) { + if (DELAY_ACK(tp, tlen)) + tp->t_flags |= TF_DELACK; + else + tp->t_flags |= TF_ACKNOW; + tp->rcv_nxt += tlen; + thflags = th->th_flags & TH_FIN; + + /* + * samkumar: I replaced the code that used to be here (which would + * free the mbuf with m_freem(m) if the SBS_CANTRCVMORE flag is set + * on so->so_rcv.sb_state, and otherwise call + * sbappendstream_locked(&so->so_rcv, m, 0);). + */ + if (!tpiscantrcv(tp)) { + size_t usedbefore = cbuf_used_space(&tp->recvbuf); + cbuf_write(&tp->recvbuf, msg, otMessageGetOffset(msg) + drop_hdrlen, tlen, cbuf_copy_from_message); + if (usedbefore == 0 && tlen > 0) { + sig->recvbuf_notempty = true; + } + } else if (tlen > 0) { + /* + * samkumar: We already know tlen != 0, so if we got here, then + * it means that we got data after we called SHUT_RD, or after + * receiving a FIN. I'm going to drop the connection in this + * case. I think FreeBSD might have just dropped the packet + * silently, but Linux handles it this way; this seems to be + * the right approach to me. + */ + tcp_drop(tp, ECONNABORTED); + goto drop; + } + /* NB: sorwakeup_locked() does an implicit unlock. */ + /* + * samkumar: There used to be a call to sorwakeup_locked(so); here, + * which wakes up any threads waiting for the socket to become + * become ready for reading. TCPlp handles its buffering + * differently so we do not need to replace this call with + * specialized code to handle this. + */ + } else if (tpiscantrcv(tp)) { + /* + * samkumar: We will reach this point if we get out-of-order data + * on a socket which was shut down with SHUT_RD, or where we + * already received a FIN. My response here is to drop the segment + * and send an RST. + */ + tcp_drop(tp, ECONNABORTED); + goto drop; + } else { + /* + * XXX: Due to the header drop above "th" is + * theoretically invalid by now. Fortunately + * m_adj() doesn't actually frees any mbufs + * when trimming from the head. + */ + thflags = tcp_reass(tp, th, &tlen, msg, otMessageGetOffset(msg) + drop_hdrlen, sig); + tp->t_flags |= TF_ACKNOW; + } + // Only place tlen is used after the call to tcp_reass is below + if (tlen > 0 && (tp->t_flags & TF_SACK_PERMIT)) + tcp_update_sack_list(tp, save_start, save_start + tlen); + /* + * samkumar: This is not me commenting things out; this was already + * commented out in the FreeBSD code. + */ +#if 0 + /* + * Note the amount of data that peer has sent into + * our window, in order to estimate the sender's + * buffer size. + * XXX: Unused. + */ + if (SEQ_GT(tp->rcv_adv, tp->rcv_nxt)) + len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt); + else + len = so->so_rcv.sb_hiwat; +#endif + } else { + thflags &= ~TH_FIN; + } + + /* + * If FIN is received ACK the FIN and let the user know + * that the connection is closing. + */ + if (thflags & TH_FIN) { + tcplp_sys_log("FIN Processing start\n"); + if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { + /* samkumar: replace socantrcvmore with tpcantrcvmore */ + tpcantrcvmore(tp); + /* + * If connection is half-synchronized + * (ie NEEDSYN flag on) then delay ACK, + * so it may be piggybacked when SYN is sent. + * Otherwise, since we received a FIN then no + * more input can be expected, send ACK now. + */ + if (tp->t_flags & TF_NEEDSYN) + tp->t_flags |= TF_DELACK; + else + tp->t_flags |= TF_ACKNOW; + tp->rcv_nxt++; + } + /* + * samkumar: This -2 state is added by me, so that we do not consider + * any more FINs in reassembly. + */ + if (tp->reass_fin_index != -2) { + sig->rcvd_fin = true; + tp->reass_fin_index = -2; + } + switch (tp->t_state) { + + /* + * In SYN_RECEIVED and ESTABLISHED STATES + * enter the CLOSE_WAIT state. + */ + case TCPS_SYN_RECEIVED: + tp->t_starttime = ticks; + /* FALLTHROUGH */ + case TCPS_ESTABLISHED: + tcp_state_change(tp, TCPS_CLOSE_WAIT); + break; + + /* + * If still in FIN_WAIT_1 STATE FIN has not been acked so + * enter the CLOSING state. + */ + case TCPS_FIN_WAIT_1: + tcp_state_change(tp, TCPS_CLOSING); + break; + + /* + * In FIN_WAIT_2 state enter the TIME_WAIT state, + * starting the time-wait timer, turning off the other + * standard timers. + */ + case TCPS_FIN_WAIT_2: + tcp_twstart(tp); + return; + } + } + + /* + * samkumar: Remove code for synchronization and debugging, here and in + * the labels below. I also removed the line to free the mbuf if it hasn't + * been freed already (the line was "m_freem(m)"). + */ + /* + * Return any desired output. + */ + if (needoutput || (tp->t_flags & TF_ACKNOW)) + (void) tcp_output(tp); + +check_delack: + if (tp->t_flags & TF_DELACK) { + tp->t_flags &= ~TF_DELACK; + tcp_timer_activate(tp, TT_DELACK, tcp_delacktime); + } + return; + +dropafterack: + /* + * Generate an ACK dropping incoming segment if it occupies + * sequence space, where the ACK reflects our state. + * + * We can now skip the test for the RST flag since all + * paths to this code happen after packets containing + * RST have been dropped. + * + * In the SYN-RECEIVED state, don't send an ACK unless the + * segment we received passes the SYN-RECEIVED ACK test. + * If it fails send a RST. This breaks the loop in the + * "LAND" DoS attack, and also prevents an ACK storm + * between two listening ports that have been sent forged + * SYN segments, each with the source address of the other. + */ + if (tp->t_state == TCPS_SYN_RECEIVED && (thflags & TH_ACK) && + (SEQ_GT(tp->snd_una, th->th_ack) || + SEQ_GT(th->th_ack, tp->snd_max)) ) { + rstreason = BANDLIM_RST_OPENPORT; + goto dropwithreset; + } + + tp->t_flags |= TF_ACKNOW; + (void) tcp_output(tp); + return; + +dropwithreset: + if (tp != NULL) { + tcp_dropwithreset(ip6, th, tp, instance, tlen, rstreason); + } else + tcp_dropwithreset(ip6, th, NULL, instance, tlen, rstreason); + return; + +drop: + return; +} + +/* + * Parse TCP options and place in tcpopt. + */ +static void +tcp_dooptions(struct tcpopt *to, uint8_t *cp, int cnt, int flags) +{ + int opt, optlen; + + to->to_flags = 0; + for (; cnt > 0; cnt -= optlen, cp += optlen) { + opt = cp[0]; + if (opt == TCPOPT_EOL) + break; + if (opt == TCPOPT_NOP) + optlen = 1; + else { + if (cnt < 2) + break; + optlen = cp[1]; + if (optlen < 2 || optlen > cnt) + break; + } + switch (opt) { + case TCPOPT_MAXSEG: + if (optlen != TCPOLEN_MAXSEG) + continue; + if (!(flags & TO_SYN)) + continue; + to->to_flags |= TOF_MSS; + bcopy((char *)cp + 2, + (char *)&to->to_mss, sizeof(to->to_mss)); + to->to_mss = ntohs(to->to_mss); + break; + case TCPOPT_WINDOW: + if (optlen != TCPOLEN_WINDOW) + continue; + if (!(flags & TO_SYN)) + continue; + to->to_flags |= TOF_SCALE; + to->to_wscale = min(cp[2], TCP_MAX_WINSHIFT); + break; + case TCPOPT_TIMESTAMP: + if (optlen != TCPOLEN_TIMESTAMP) + continue; + to->to_flags |= TOF_TS; + bcopy((char *)cp + 2, + (char *)&to->to_tsval, sizeof(to->to_tsval)); + to->to_tsval = ntohl(to->to_tsval); + bcopy((char *)cp + 6, + (char *)&to->to_tsecr, sizeof(to->to_tsecr)); + to->to_tsecr = ntohl(to->to_tsecr); + break; +#ifdef TCP_SIGNATURE + /* + * XXX In order to reply to a host which has set the + * TCP_SIGNATURE option in its initial SYN, we have to + * record the fact that the option was observed here + * for the syncache code to perform the correct response. + */ + case TCPOPT_SIGNATURE: + if (optlen != TCPOLEN_SIGNATURE) + continue; + to->to_flags |= TOF_SIGNATURE; + to->to_signature = cp + 2; + break; +#endif + case TCPOPT_SACK_PERMITTED: + if (optlen != TCPOLEN_SACK_PERMITTED) + continue; + if (!(flags & TO_SYN)) + continue; + if (!V_tcp_do_sack) + continue; + to->to_flags |= TOF_SACKPERM; + break; + case TCPOPT_SACK: + if (optlen <= 2 || (optlen - 2) % TCPOLEN_SACK != 0) + continue; + if (flags & TO_SYN) + continue; + to->to_flags |= TOF_SACK; + to->to_nsacks = (optlen - 2) / TCPOLEN_SACK; + to->to_sacks = cp + 2; + break; + default: + continue; + } + } +} + + +/* + * Collect new round-trip time estimate + * and update averages and current timeout. + */ +static void +tcp_xmit_timer(struct tcpcb *tp, int rtt) +{ + int delta; + + tp->t_rttupdated++; + if (tp->t_srtt != 0) { + /* + * srtt is stored as fixed point with 5 bits after the + * binary point (i.e., scaled by 8). The following magic + * is equivalent to the smoothing algorithm in rfc793 with + * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed + * point). Adjust rtt to origin 0. + */ + delta = ((rtt - 1) << TCP_DELTA_SHIFT) + - (tp->t_srtt >> (TCP_RTT_SHIFT - TCP_DELTA_SHIFT)); + + if ((tp->t_srtt += delta) <= 0) + tp->t_srtt = 1; + + /* + * We accumulate a smoothed rtt variance (actually, a + * smoothed mean difference), then set the retransmit + * timer to smoothed rtt + 4 times the smoothed variance. + * rttvar is stored as fixed point with 4 bits after the + * binary point (scaled by 16). The following is + * equivalent to rfc793 smoothing with an alpha of .75 + * (rttvar = rttvar*3/4 + |delta| / 4). This replaces + * rfc793's wired-in beta. + */ + if (delta < 0) + delta = -delta; + delta -= tp->t_rttvar >> (TCP_RTTVAR_SHIFT - TCP_DELTA_SHIFT); + if ((tp->t_rttvar += delta) <= 0) + tp->t_rttvar = 1; + if (tp->t_rttbest > tp->t_srtt + tp->t_rttvar) + tp->t_rttbest = tp->t_srtt + tp->t_rttvar; + } else { + /* + * No rtt measurement yet - use the unsmoothed rtt. + * Set the variance to half the rtt (so our first + * retransmit happens at 3*rtt). + */ + tp->t_srtt = rtt << TCP_RTT_SHIFT; + tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1); + tp->t_rttbest = tp->t_srtt + tp->t_rttvar; + } + tp->t_rtttime = 0; + tp->t_rxtshift = 0; + + /* + * the retransmit should happen at rtt + 4 * rttvar. + * Because of the way we do the smoothing, srtt and rttvar + * will each average +1/2 tick of bias. When we compute + * the retransmit timer, we want 1/2 tick of rounding and + * 1 extra tick because of +-1/2 tick uncertainty in the + * firing of the timer. The bias will give us exactly the + * 1.5 tick we need. But, because the bias is + * statistical, we have to test that we don't drop below + * the minimum feasible timer (which is 2 ticks). + */ + TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp), + max(tp->t_rttmin, rtt + 2), TCPTV_REXMTMAX); + +#ifdef INSTRUMENT_TCP + printf("TCP timer %u %d %d %d\n", (unsigned int) get_micros(), rtt, (int) tp->t_srtt, (int) tp->t_rttvar); +#endif + + + /* + * We received an ack for a packet that wasn't retransmitted; + * it is probably safe to discard any error indications we've + * received recently. This isn't quite right, but close enough + * for now (a route might have failed after we sent a segment, + * and the return path might not be symmetrical). + */ + tp->t_softerror = 0; +} + +/* + * samkumar: Taken from netinet6/in6.c. + * + * This function is supposed to check whether the provided address is an + * IPv6 address of this host. This function, however, is used only as a hint, + * as the MSS is clamped at V_tcp_v6mssdflt for connections to non-local + * addresses. It is difficult for us to actually determine if the address + * belongs to us, so we are conservative and only return 1 (true) if it is + * obviously so---we keep the part of the function that checks for loopback or + * link local and remove the rest of the code that checks for the addresses + * assigned to interfaces. In cases where we return 0 but should have returned + * 1, we may conservatively clamp the MTU, but that should be OK for TCPlp. + * In fact, the constants are set such that we'll get the right answer whether + * we clamp or not, so this shouldn't really matter at all. + */ +int +in6_localaddr(struct in6_addr *in6) +{ + if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6)) + return 1; + return (0); +} + +/* + * Determine a reasonable value for maxseg size. + * If the route is known, check route for mtu. + * If none, use an mss that can be handled on the outgoing interface + * without forcing IP to fragment. If no route is found, route has no mtu, + * or the destination isn't local, use a default, hopefully conservative + * size (usually 512 or the default IP max size, but no more than the mtu + * of the interface), as we can't discover anything about intervening + * gateways or networks. We also initialize the congestion/slow start + * window to be a single segment if the destination isn't local. + * While looking at the routing entry, we also initialize other path-dependent + * parameters from pre-set or cached values in the routing entry. + * + * Also take into account the space needed for options that we + * send regularly. Make maxseg shorter by that amount to assure + * that we can send maxseg amount of data even when the options + * are present. Store the upper limit of the length of options plus + * data in maxopd. + * + * NOTE that this routine is only called when we process an incoming + * segment, or an ICMP need fragmentation datagram. Outgoing SYN/ACK MSS + * settings are handled in tcp_mssopt(). + */ +/* + * samkumar: Using struct tcpcb instead of the inpcb. + */ +void +tcp_mss_update(struct tcpcb *tp, int offer, int mtuoffer, + struct hc_metrics_lite *metricptr, struct tcp_ifcap *cap) +{ + /* + * samkumar: I removed all IPv4-specific logic and cases, including logic + * to check for IPv4 vs. IPv6, as well as all locking and debugging code. + */ + int mss = 0; + uint64_t maxmtu = 0; + struct hc_metrics_lite metrics; + int origoffer; + size_t min_protoh = IP6HDR_SIZE + sizeof (struct tcphdr); + + if (mtuoffer != -1) { + KASSERT(offer == -1, ("%s: conflict", __func__)); + offer = mtuoffer - min_protoh; + } + origoffer = offer; + + maxmtu = tcp_maxmtu6(tp, cap); + tp->t_maxopd = tp->t_maxseg = V_tcp_v6mssdflt; + + /* + * No route to sender, stay with default mss and return. + */ + if (maxmtu == 0) { + /* + * In case we return early we need to initialize metrics + * to a defined state as tcp_hc_get() would do for us + * if there was no cache hit. + */ + if (metricptr != NULL) + bzero(metricptr, sizeof(struct hc_metrics_lite)); + return; + } + + /* What have we got? */ + switch (offer) { + case 0: + /* + * Offer == 0 means that there was no MSS on the SYN + * segment, in this case we use tcp_mssdflt as + * already assigned to t_maxopd above. + */ + offer = tp->t_maxopd; + break; + + case -1: + /* + * Offer == -1 means that we didn't receive SYN yet. + */ + /* FALLTHROUGH */ + + default: + /* + * Prevent DoS attack with too small MSS. Round up + * to at least minmss. + */ + offer = max(offer, V_tcp_minmss); + } + + /* + * rmx information is now retrieved from tcp_hostcache. + */ + tcp_hc_get(tp, &metrics); + if (metricptr != NULL) + bcopy(&metrics, metricptr, sizeof(struct hc_metrics_lite)); + + /* + * If there's a discovered mtu in tcp hostcache, use it. + * Else, use the link mtu. + */ + if (metrics.rmx_mtu) + mss = min(metrics.rmx_mtu, maxmtu) - min_protoh; + else { + mss = maxmtu - min_protoh; + if (!V_path_mtu_discovery && + !in6_localaddr(&tp->faddr)) + mss = min(mss, V_tcp_v6mssdflt); + /* + * XXX - The above conditional (mss = maxmtu - min_protoh) + * probably violates the TCP spec. + * The problem is that, since we don't know the + * other end's MSS, we are supposed to use a conservative + * default. But, if we do that, then MTU discovery will + * never actually take place, because the conservative + * default is much less than the MTUs typically seen + * on the Internet today. For the moment, we'll sweep + * this under the carpet. + * + * The conservative default might not actually be a problem + * if the only case this occurs is when sending an initial + * SYN with options and data to a host we've never talked + * to before. Then, they will reply with an MSS value which + * will get recorded and the new parameters should get + * recomputed. For Further Study. + */ + } + mss = min(mss, offer); + + /* + * Sanity check: make sure that maxopd will be large + * enough to allow some data on segments even if the + * all the option space is used (40bytes). Otherwise + * funny things may happen in tcp_output. + */ + /* + * samkumar: When I was experimenting with different MSS values, I had + * changed this to "mss = max(mss, TCP_MAXOLEN + 1);" but I am changing it + * back for the version that will be merged into OpenThread. + */ + mss = max(mss, 64); + + /* + * maxopd stores the maximum length of data AND options + * in a segment; maxseg is the amount of data in a normal + * segment. We need to store this value (maxopd) apart + * from maxseg, because now every segment carries options + * and thus we normally have somewhat less data in segments. + */ + tp->t_maxopd = mss; + + /* + * origoffer==-1 indicates that no segments were received yet. + * In this case we just guess. + */ + if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP && + (origoffer == -1 || + (tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP)) + mss -= TCPOLEN_TSTAMP_APPA; + + tp->t_maxseg = mss; +} + +void +tcp_mss(struct tcpcb *tp, int offer) +{ + struct hc_metrics_lite metrics; + struct tcp_ifcap cap; + + KASSERT(tp != NULL, ("%s: tp == NULL", __func__)); + + bzero(&cap, sizeof(cap)); + tcp_mss_update(tp, offer, -1, &metrics, &cap); + + /* + * samkumar: There used to be code below that might modify the MSS, but I + * removed all of it (see the comments below for the reason). It used to + * read tp->t_maxseg into the local variable mss, modify mss, and then + * reassign tp->t_maxseg to mss. I've kept the assignments, commented out, + * for clarity. + */ + //mss = tp->t_maxseg; + + /* + * If there's a pipesize, change the socket buffer to that size, + * don't change if sb_hiwat is different than default (then it + * has been changed on purpose with setsockopt). + * Make the socket buffers an integral number of mss units; + * if the mss is larger than the socket buffer, decrease the mss. + */ + + /* + * samkumar: There used to be code here would would limit the MSS to at + * most the size of the send buffer, and then round up the send buffer to + * a multiple of the MSS using + * "sbreserve_locked(&so->so_snd, bufsize, so, NULL);". With TCPlp, we do + * not do this, because the linked buffer used at the send buffer doesn't + * have a real limit. Had we used a circular buffer, then limiting the MSS + * to the buffer size would have made sense, but we still would not be able + * to resize the send buffer because it is not allocated by TCPlp. + */ + + /* + * samkumar: See the comment above about me removing code that modifies + * the MSS, making this assignment and the one above both unnecessary. + */ + //tp->t_maxseg = mss; + + /* + * samkumar: There used to be code here that would round up the receive + * buffer size to a multiple of the MSS, assuming that the receive buffer + * size is bigger than the MSS. The new buffer size is set using + * "sbreserve_locked(&so->so_rcv, bufsize, so, NULL);". In TCPlp, the + * buffer is not allocated by TCPlp so I removed the code for this. + */ + /* + * samkumar: There used to be code here to handle TCP Segmentation + * Offloading (TSO); I removed it becuase we don't support that in TCPlp. + */ +} + +/* + * Determine the MSS option to send on an outgoing SYN. + */ +/* + * samkumar: In the signature, changed "struct in_conninfo *inc" to + * "struct tcpcb* tp". + */ +int +tcp_mssopt(struct tcpcb* tp) +{ + /* + * samkumar: I removed all processing code specific to IPv4, or to decide + * between IPv4 and IPv6. This is OK because TCPlp assumes IPv6. + */ + int mss = 0; + uint64_t maxmtu = 0; + uint64_t thcmtu = 0; + size_t min_protoh; + + KASSERT(tp != NULL, ("tcp_mssopt with NULL tcpcb pointer")); + + mss = V_tcp_v6mssdflt; + maxmtu = tcp_maxmtu6(tp, NULL); + min_protoh = IP6HDR_SIZE + sizeof(struct tcphdr); + + thcmtu = tcp_hc_getmtu(tp); /* IPv4 and IPv6 */ + + if (maxmtu && thcmtu) + mss = min(maxmtu, thcmtu) - min_protoh; + else if (maxmtu || thcmtu) + mss = max(maxmtu, thcmtu) - min_protoh; + + return (mss); +} + +/* + * On a partial ack arrives, force the retransmission of the + * next unacknowledged segment. Do not clear tp->t_dupacks. + * By setting snd_nxt to ti_ack, this forces retransmission timer to + * be started again. + */ +static void +tcp_newreno_partial_ack(struct tcpcb *tp, struct tcphdr *th) +{ + tcp_seq onxt = tp->snd_nxt; + uint64_t ocwnd = tp->snd_cwnd; + + tcp_timer_activate(tp, TT_REXMT, 0); + tp->t_rtttime = 0; + tp->snd_nxt = th->th_ack; + /* + * Set snd_cwnd to one segment beyond acknowledged offset. + * (tp->snd_una has not yet been updated when this function is called.) + */ + tp->snd_cwnd = tp->t_maxseg + BYTES_THIS_ACK(tp, th); + tp->t_flags |= TF_ACKNOW; +#ifdef INSTRUMENT_TCP + printf("TCP Partial_ACK\n"); +#endif + (void) tcp_output(tp); + tp->snd_cwnd = ocwnd; + if (SEQ_GT(onxt, tp->snd_nxt)) + tp->snd_nxt = onxt; + /* + * Partial window deflation. Relies on fact that tp->snd_una + * not updated yet. + */ + if (tp->snd_cwnd > BYTES_THIS_ACK(tp, th)) + tp->snd_cwnd -= BYTES_THIS_ACK(tp, th); + else + tp->snd_cwnd = 0; + tp->snd_cwnd += tp->t_maxseg; +#ifdef INSTRUMENT_TCP + printf("TCP Partial_ACK_final %d\n", (int) tp->snd_cwnd); +#endif +} diff --git a/third_party/tcplp/bsdtcp/tcp_output.c b/third_party/tcplp/bsdtcp/tcp_output.c new file mode 100644 index 000000000..104403f51 --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_output.c @@ -0,0 +1,1479 @@ +/*- + * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995 + * 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_output.c 8.4 (Berkeley) 5/24/95 + */ + +#include +#include + +#include "../tcplp.h" +#include "tcp.h" +#include "tcp_fsm.h" +#include "tcp_var.h" +#include "tcp_seq.h" +#include "tcp_timer.h" +#include "ip.h" +#include "../lib/cbuf.h" + +#include "tcp_const.h" + +#include +#include +#include + +static inline void +cc_after_idle(struct tcpcb *tp) +{ + /* samkumar: Removed synchronization. */ + if (CC_ALGO(tp)->after_idle != NULL) + CC_ALGO(tp)->after_idle(tp->ccv); +} + +long min(long a, long b) { + if (a < b) { + return a; + } else { + return b; + } +} + +unsigned long ulmin(unsigned long a, unsigned long b) { + if (a < b) { + return a; + } else { + return b; + } +} + +#define lmin(a, b) min(a, b) + +void +tcp_setpersist(struct tcpcb *tp) +{ + int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1; + int tt; + + tp->t_flags &= ~TF_PREVVALID; + if (tcp_timer_active(tp, TT_REXMT)) + printf("PANIC: tcp_setpersist: retransmit pending\n"); + /* + * Start/restart persistance timer. + */ + TCPT_RANGESET(tt, t * tcp_backoff[tp->t_rxtshift], + TCPTV_PERSMIN, TCPTV_PERSMAX); + tcp_timer_activate(tp, TT_PERSIST, tt); + if (tp->t_rxtshift < TCP_MAXRXTSHIFT) + tp->t_rxtshift++; +} + +/* + * Tcp output routine: figure out what should be sent and send it. + */ +int +tcp_output(struct tcpcb *tp) +{ + /* + * samkumar: The biggest change in this function is in how outgoing + * segments are built and sent out. That code has been updated to account + * for TCPlp's buffering, and using otMessages rather than mbufs to + * construct the outgoing segments. + * + * And, of course, all code corresponding to locks, stats, and debugging + * has been removed, and all code specific to IPv4 or to decide between + * IPv6 and IPv4 handling has been removed. + */ + + struct tcphdr* th = NULL; + int idle; + long len, recwin, sendwin; + int off, flags, error = 0; /* Keep compiler happy */ + int sendalot, mtu; + int sack_rxmit, sack_bytes_rxmt; + struct sackhole* p; + unsigned ipoptlen, optlen, hdrlen; + struct tcpopt to; + uint8_t opt[TCP_MAXOLEN]; + uint32_t ticks = tcplp_sys_get_ticks(); + + /* samkumar: Code for TCP offload has been removed. */ + + /* + * Determine length of data that should be transmitted, + * and flags that will be used. + * If there is some data or critical controls (SYN, RST) + * to send, then transmit; otherwise, investigate further. + */ + idle = (tp->t_flags & TF_LASTIDLE) || (tp->snd_max == tp->snd_una); + if (idle && ticks - tp->t_rcvtime >= tp->t_rxtcur) + cc_after_idle(tp); + + tp->t_flags &= ~TF_LASTIDLE; + if (idle) { + if (tp->t_flags & TF_MORETOCOME) { + tp->t_flags |= TF_LASTIDLE; + idle = 0; + } + } + /* samkumar: This would be printed once per _window_ that is transmitted. */ +#ifdef INSTRUMENT_TCP + printf("TCP output %u %d %d\n", (unsigned int) get_micros(), (int) tp->snd_wnd, (int) tp->snd_cwnd); +#endif + +again: + /* + * If we've recently taken a timeout, snd_max will be greater than + * snd_nxt. There may be SACK information that allows us to avoid + * resending already delivered data. Adjust snd_nxt accordingly. + */ + if ((tp->t_flags & TF_SACK_PERMIT) && + SEQ_LT(tp->snd_nxt, tp->snd_max)) + tcp_sack_adjust(tp); + sendalot = 0; + /* samkumar: Removed code for supporting TSO. */ + mtu = 0; + off = tp->snd_nxt - tp->snd_una; + sendwin = min(tp->snd_wnd, tp->snd_cwnd); + + flags = tcp_outflags[tp->t_state]; + /* + * Send any SACK-generated retransmissions. If we're explicitly trying + * to send out new data (when sendalot is 1), bypass this function. + * If we retransmit in fast recovery mode, decrement snd_cwnd, since + * we're replacing a (future) new transmission with a retransmission + * now, and we previously incremented snd_cwnd in tcp_input(). + */ + /* + * Still in sack recovery , reset rxmit flag to zero. + */ + sack_rxmit = 0; + sack_bytes_rxmt = 0; + len = 0; + p = NULL; + if ((tp->t_flags & TF_SACK_PERMIT) && IN_FASTRECOVERY(tp->t_flags) && + (p = tcp_sack_output(tp, &sack_bytes_rxmt))) { + long cwin; + + cwin = min(tp->snd_wnd, tp->snd_cwnd) - sack_bytes_rxmt; + if (cwin < 0) + cwin = 0; + /* Do not retransmit SACK segments beyond snd_recover */ + if (SEQ_GT(p->end, tp->snd_recover)) { + /* + * (At least) part of sack hole extends beyond + * snd_recover. Check to see if we can rexmit data + * for this hole. + */ + if (SEQ_GEQ(p->rxmit, tp->snd_recover)) { + /* + * Can't rexmit any more data for this hole. + * That data will be rexmitted in the next + * sack recovery episode, when snd_recover + * moves past p->rxmit. + */ + p = NULL; + goto after_sack_rexmit; + } else + /* Can rexmit part of the current hole */ + len = ((long)ulmin(cwin, + tp->snd_recover - p->rxmit)); + } else + len = ((long)ulmin(cwin, p->end - p->rxmit)); + off = p->rxmit - tp->snd_una; + KASSERT(off >= 0,("%s: sack block to the left of una : %d", + __func__, off)); + if (len > 0) { + sack_rxmit = 1; + sendalot = 1; + } + } +after_sack_rexmit: + /* + * Get standard flags, and add SYN or FIN if requested by 'hidden' + * state flags. + */ + if (tp->t_flags & TF_NEEDFIN) + flags |= TH_FIN; + if (tp->t_flags & TF_NEEDSYN) + flags |= TH_SYN; + + /* + * If in persist timeout with window of 0, send 1 byte. + * Otherwise, if window is small but nonzero + * and timer expired, we will send what we can + * and go to transmit state. + */ + if (tp->t_flags & TF_FORCEDATA) { + if (sendwin == 0) { + /* + * If we still have some data to send, then + * clear the FIN bit. Usually this would + * happen below when it realizes that we + * aren't sending all the data. However, + * if we have exactly 1 byte of unsent data, + * then it won't clear the FIN bit below, + * and if we are in persist state, we wind + * up sending the packet without recording + * that we sent the FIN bit. + * + * We can't just blindly clear the FIN bit, + * because if we don't have any more data + * to send then the probe will be the FIN + * itself. + */ + /* + * samkumar: Replaced call to sbused(&so->so_snd) with the call to + * lbuf_used_space below. + */ + if (off < lbuf_used_space(&tp->sendbuf)) + flags &= ~TH_FIN; + sendwin = 1; + } else { + tcp_timer_activate(tp, TT_PERSIST, 0); + tp->t_rxtshift = 0; + } + } + + /* + * If snd_nxt == snd_max and we have transmitted a FIN, the + * offset will be > 0 even if so_snd.sb_cc is 0, resulting in + * a negative length. This can also occur when TCP opens up + * its congestion window while receiving additional duplicate + * acks after fast-retransmit because TCP will reset snd_nxt + * to snd_max after the fast-retransmit. + * + * In the normal retransmit-FIN-only case, however, snd_nxt will + * be set to snd_una, the offset will be 0, and the length may + * wind up 0. + * + * If sack_rxmit is true we are retransmitting from the scoreboard + * in which case len is already set. + */ + if (sack_rxmit == 0) { + if (sack_bytes_rxmt == 0) + /* + * samkumar: Replaced sbavail(&so->so_snd) with this call to + * lbuf_used_space. + */ + len = ((long)ulmin(lbuf_used_space(&tp->sendbuf), sendwin) - + off); + else { + long cwin; + + /* + * We are inside of a SACK recovery episode and are + * sending new data, having retransmitted all the + * data possible in the scoreboard. + */ + /* + * samkumar: Replaced sbavail(&so->so_snd) with this call to + * lbuf_used_space. + */ + len = ((long)ulmin(lbuf_used_space(&tp->sendbuf), tp->snd_wnd) - + off); + /* + * Don't remove this (len > 0) check ! + * We explicitly check for len > 0 here (although it + * isn't really necessary), to work around a gcc + * optimization issue - to force gcc to compute + * len above. Without this check, the computation + * of len is bungled by the optimizer. + */ + if (len > 0) { + cwin = tp->snd_cwnd - + (tp->snd_nxt - tp->sack_newdata) - + sack_bytes_rxmt; + if (cwin < 0) + cwin = 0; + len = lmin(len, cwin); + } + } + } + + /* + * Lop off SYN bit if it has already been sent. However, if this + * is SYN-SENT state and if segment contains data and if we don't + * know that foreign host supports TAO, suppress sending segment. + */ + if ((flags & TH_SYN) && SEQ_GT(tp->snd_nxt, tp->snd_una)) { + if (tp->t_state != TCPS_SYN_RECEIVED) + flags &= ~TH_SYN; + off--, len++; + } + + /* + * Be careful not to send data and/or FIN on SYN segments. + * This measure is needed to prevent interoperability problems + * with not fully conformant TCP implementations. + */ + if ((flags & TH_SYN) && (tp->t_flags & TF_NOOPT)) { + len = 0; + flags &= ~TH_FIN; + } + + if (len <= 0) { + /* + * If FIN has been sent but not acked, + * but we haven't been called to retransmit, + * len will be < 0. Otherwise, window shrank + * after we sent into it. If window shrank to 0, + * cancel pending retransmit, pull snd_nxt back + * to (closed) window, and set the persist timer + * if it isn't already going. If the window didn't + * close completely, just wait for an ACK. + * + * We also do a general check here to ensure that + * we will set the persist timer when we have data + * to send, but a 0-byte window. This makes sure + * the persist timer is set even if the packet + * hits one of the "goto send" lines below. + */ + len = 0; + /* + * samkumar: Replaced sbavail(&so->so_snd) with this call to + * lbuf_used_space. + */ + if ((sendwin == 0) && (TCPS_HAVEESTABLISHED(tp->t_state)) && + (off < (int) lbuf_used_space(&tp->sendbuf))) { + tcp_timer_activate(tp, TT_REXMT, 0); + tp->t_rxtshift = 0; + tp->snd_nxt = tp->snd_una; + if (!tcp_timer_active(tp, TT_PERSIST)) { + tcp_setpersist(tp); + } + } + } + + + /* len will be >= 0 after this point. */ + KASSERT(len >= 0, ("[%s:%d]: len < 0", __func__, __LINE__)); + + /* + * Automatic sizing of send socket buffer. Often the send buffer + * size is not optimally adjusted to the actual network conditions + * at hand (delay bandwidth product). Setting the buffer size too + * small limits throughput on links with high bandwidth and high + * delay (eg. trans-continental/oceanic links). Setting the + * buffer size too big consumes too much real kernel memory, + * especially with many connections on busy servers. + * + * The criteria to step up the send buffer one notch are: + * 1. receive window of remote host is larger than send buffer + * (with a fudge factor of 5/4th); + * 2. send buffer is filled to 7/8th with data (so we actually + * have data to make use of it); + * 3. send buffer fill has not hit maximal automatic size; + * 4. our send window (slow start and cogestion controlled) is + * larger than sent but unacknowledged data in send buffer. + * + * The remote host receive window scaling factor may limit the + * growing of the send buffer before it reaches its allowed + * maximum. + * + * It scales directly with slow start or congestion window + * and does at most one step per received ACK. This fast + * scaling has the drawback of growing the send buffer beyond + * what is strictly necessary to make full use of a given + * delay*bandwith product. However testing has shown this not + * to be much of an problem. At worst we are trading wasting + * of available bandwith (the non-use of it) for wasting some + * socket buffer memory. + * + * TODO: Shrink send buffer during idle periods together + * with congestion window. Requires another timer. Has to + * wait for upcoming tcp timer rewrite. + * + * XXXGL: should there be used sbused() or sbavail()? + */ + /* + * samkumar: There used to be code here to dynamically size the + * send buffer (by calling sbreserve_locked). In TCPlp, we don't support + * this, as the send buffer doesn't have a well-defined size (and even if + * we were to use a circular buffer, it would be a fixed-size buffer + * allocated by the application). Therefore, I removed the code that does + * this. + */ + + /* + * samkumar: There used to be code here to handle TCP Segmentation + * Offloading (TSO); I removed it becuase we don't support that in TCPlp. + */ + + if (sack_rxmit) { + /* + * samkumar: Replaced sbused(&so->so_snd) with this call to + * lbuf_used_space. + */ + if (SEQ_LT(p->rxmit + len, tp->snd_una + lbuf_used_space(&tp->sendbuf))) + flags &= ~TH_FIN; + } else { + if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + + /* + * samkumar: Replaced sbused(&so->so_snd) with this call to + * lbuf_used_space. + */ + lbuf_used_space(&tp->sendbuf))) + flags &= ~TH_FIN; + } + + /* + * samkumar: Replaced sbspace(&so->so_rcv) with this call to + * cbuf_free_space. + */ + recwin = cbuf_free_space(&tp->recvbuf); + + /* + * Sender silly window avoidance. We transmit under the following + * conditions when len is non-zero: + * + * - We have a full segment (or more with TSO) + * - This is the last buffer in a write()/send() and we are + * either idle or running NODELAY + * - we've timed out (e.g. persist timer) + * - we have more then 1/2 the maximum send window's worth of + * data (receiver may be limited the window size) + * - we need to retransmit + */ + if (len) { + if (len >= tp->t_maxseg) + goto send; + /* + * NOTE! on localhost connections an 'ack' from the remote + * end may occur synchronously with the output and cause + * us to flush a buffer queued with moretocome. XXX + * + * note: the len + off check is almost certainly unnecessary. + */ + /* + * samkumar: Replaced sbavail(&so->so_snd) with this call to + * lbuf_used_space. + */ + if (!(tp->t_flags & TF_MORETOCOME) && /* normal case */ + (idle || (tp->t_flags & TF_NODELAY)) && + len + off >= lbuf_used_space(&tp->sendbuf) && + (tp->t_flags & TF_NOPUSH) == 0) { + goto send; + } + if (tp->t_flags & TF_FORCEDATA) /* typ. timeout case */ + goto send; + if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0) + goto send; + if (SEQ_LT(tp->snd_nxt, tp->snd_max)) /* retransmit case */ + goto send; + if (sack_rxmit) + goto send; + } + + /* + * Sending of standalone window updates. + * + * Window updates are important when we close our window due to a + * full socket buffer and are opening it again after the application + * reads data from it. Once the window has opened again and the + * remote end starts to send again the ACK clock takes over and + * provides the most current window information. + * + * We must avoid the silly window syndrome whereas every read + * from the receive buffer, no matter how small, causes a window + * update to be sent. We also should avoid sending a flurry of + * window updates when the socket buffer had queued a lot of data + * and the application is doing small reads. + * + * Prevent a flurry of pointless window updates by only sending + * an update when we can increase the advertized window by more + * than 1/4th of the socket buffer capacity. When the buffer is + * getting full or is very small be more aggressive and send an + * update whenever we can increase by two mss sized segments. + * In all other situations the ACK's to new incoming data will + * carry further window increases. + * + * Don't send an independent window update if a delayed + * ACK is pending (it will get piggy-backed on it) or the + * remote side already has done a half-close and won't send + * more data. Skip this if the connection is in T/TCP + * half-open state. + */ + if (recwin > 0 && !(tp->t_flags & TF_NEEDSYN) && + !(tp->t_flags & TF_DELACK) && + !TCPS_HAVERCVDFIN(tp->t_state)) { + /* + * "adv" is the amount we could increase the window, + * taking into account that we are limited by + * TCP_MAXWIN << tp->rcv_scale. + */ + long adv; + int oldwin; + + adv = min(recwin, (long)TCP_MAXWIN << tp->rcv_scale); + if (SEQ_GT(tp->rcv_adv, tp->rcv_nxt)) { + oldwin = (tp->rcv_adv - tp->rcv_nxt); + adv -= oldwin; + } else + oldwin = 0; + + /* + * If the new window size ends up being the same as the old + * size when it is scaled, then don't force a window update. + */ + if (oldwin >> tp->rcv_scale == (adv + oldwin) >> tp->rcv_scale) + goto dontupdate; + + /* + * samkumar: Here, FreeBSD has some heuristics to decide whether or + * not to send a window update. The code for the original heuristics + * is commented out, using #if 0. These heuristics compare "adv," + * the size of the window update, with the size of the local receive + * buffer. The FreeBSD heuristics aren't applicable because they are + * orders of magnitude off from what we see in TCPlp. For example, + * FreeBSD only sends a window update if it is at least two segments + * big. Note that, in the experiments I did, the second case did not + * filter window updates further because, in the experiments, the + * receive buffer was smaller than 8 segments. + * + * I replaced these heuristics with a simpler version, which you can + * see below. For the experiments I did, the first condition + * (checking if adv >= (long)(2 * tp->t_maxseg)) wasn't included; this + * did not matter because the receive buffer was smaller than 8 + * segments, so any condition that would have triggered the first + * condition would have triggered the second one anyway. I've included + * the first condition in this version in an effort to be more robust, + * in case someone does try to run TCPlp with a large receive buffer. + * + * It may be worth studying this more and revisiting the heuristic to + * use here. In case we try to resurrect the old FreeBSD heuristics, + * note that so->so_rcv.sb_hiwat in FreeBSD corresponds roughly to + * cbuf_size(&tp->recvbuf) in TCPlp. + */ +#if 0 + if (adv >= (long)(2 * tp->t_maxseg) && + (adv >= (long)(so->so_rcv.sb_hiwat / 4) || + recwin <= (long)(so->so_rcv.sb_hiwat / 8) || + so->so_rcv.sb_hiwat <= 8 * tp->t_maxseg)) + goto send; +#endif + if (adv >= (long)(2 * tp->t_maxseg) || + adv >= (long)cbuf_size(&tp->recvbuf) / 4) + goto send; + } +dontupdate: + + /* + * Send if we owe the peer an ACK, RST, SYN, or urgent data. ACKNOW + * is also a catch-all for the retransmit timer timeout case. + */ + if (tp->t_flags & TF_ACKNOW) { + goto send; + } + if ((flags & TH_RST) || + ((flags & TH_SYN) && (tp->t_flags & TF_NEEDSYN) == 0)) + goto send; + if (SEQ_GT(tp->snd_up, tp->snd_una)) + goto send; + /* + * If our state indicates that FIN should be sent + * and we have not yet done so, then we need to send. + */ + if (flags & TH_FIN && + ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una)) + goto send; + /* + * In SACK, it is possible for tcp_output to fail to send a segment + * after the retransmission timer has been turned off. Make sure + * that the retransmission timer is set. + */ + if ((tp->t_flags & TF_SACK_PERMIT) && + SEQ_GT(tp->snd_max, tp->snd_una) && + !tcp_timer_active(tp, TT_REXMT) && + !tcp_timer_active(tp, TT_PERSIST)) { + tcp_timer_activate(tp, TT_REXMT, tp->t_rxtcur); + goto just_return; + } + + /* + * TCP window updates are not reliable, rather a polling protocol + * using ``persist'' packets is used to insure receipt of window + * updates. The three ``states'' for the output side are: + * idle not doing retransmits or persists + * persisting to move a small or zero window + * (re)transmitting and thereby not persisting + * + * tcp_timer_active(tp, TT_PERSIST) + * is true when we are in persist state. + * (tp->t_flags & TF_FORCEDATA) + * is set when we are called to send a persist packet. + * tcp_timer_active(tp, TT_REXMT) + * is set when we are retransmitting + * The output side is idle when both timers are zero. + * + * If send window is too small, there is data to transmit, and no + * retransmit or persist is pending, then go to persist state. + * If nothing happens soon, send when timer expires: + * if window is nonzero, transmit what we can, + * otherwise force out a byte. + */ + /* + * samkumar: Replaced sbavail(&so->so_snd) with this call to + * lbuf_used_space. + */ + if (lbuf_used_space(&tp->sendbuf) && !tcp_timer_active(tp, TT_REXMT) && + !tcp_timer_active(tp, TT_PERSIST)) { + tp->t_rxtshift = 0; + tcp_setpersist(tp); + } + + /* + * No reason to send a segment, just return. + */ +just_return: + return (0); + +send: + if (len > 0) { + if (len >= tp->t_maxseg) + tp->t_flags2 |= TF2_PLPMTU_MAXSEGSNT; + else + tp->t_flags2 &= ~TF2_PLPMTU_MAXSEGSNT; + } + /* + * Before ESTABLISHED, force sending of initial options + * unless TCP set not to do any options. + * NOTE: we assume that the IP/TCP header plus TCP options + * always fit in a single mbuf, leaving room for a maximum + * link header, i.e. + * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MCLBYTES + */ + optlen = 0; + hdrlen = sizeof (struct ip6_hdr) + sizeof (struct tcphdr); + + /* + * Compute options for segment. + * We only have to care about SYN and established connection + * segments. Options for SYN-ACK segments are handled in TCP + * syncache. + * Sam: I've done away with the syncache. However, it seems that + * the existing logic works fine for SYN-ACK as well + */ + if ((tp->t_flags & TF_NOOPT) == 0) { + to.to_flags = 0; + /* Maximum segment size. */ + if (flags & TH_SYN) { + tp->snd_nxt = tp->iss; + to.to_mss = tcp_mssopt(tp); + to.to_flags |= TOF_MSS; + } + /* Window scaling. */ + if ((flags & TH_SYN) && (tp->t_flags & TF_REQ_SCALE)) { + to.to_wscale = tp->request_r_scale; + to.to_flags |= TOF_SCALE; + } + /* Timestamps. */ + if ((tp->t_flags & TF_RCVD_TSTMP) || + ((flags & TH_SYN) && (tp->t_flags & TF_REQ_TSTMP))) { + to.to_tsval = tcp_ts_getticks() + tp->ts_offset; + to.to_tsecr = tp->ts_recent; + to.to_flags |= TOF_TS; + /* + * samkumar: I removed the code to set the timestamp tp->rfbuf_ts + * for receive buffer autosizing, since we don't do autosizing on + * the receive buffer in TCPlp. + */ + } + + /* Selective ACK's. */ + if (tp->t_flags & TF_SACK_PERMIT) { + if (flags & TH_SYN) + to.to_flags |= TOF_SACKPERM; + else if (TCPS_HAVEESTABLISHED(tp->t_state) && + (tp->t_flags & TF_SACK_PERMIT) && + tp->rcv_numsacks > 0) { + to.to_flags |= TOF_SACK; + to.to_nsacks = tp->rcv_numsacks; + to.to_sacks = (uint8_t *)tp->sackblks; + } + } + + /* + * samkumar: Remove logic to set TOF_SIGNATURE flag in to.to_flags, + * since TCPlp does not support TCP signatures. + */ + + /* Processing the options. */ + hdrlen += optlen = tcp_addoptions(&to, opt); + } + /* + * samkumar: This used to be set to ip6_optlen(tp->t_inpcb), instead of 0, + * along with some additional code to handle IPSEC. In TCPlp we don't set + * IPv6 options here; we expect those to be set by the host network stack. + * Of course, code that supports IPv4 has been removed as well. + */ + ipoptlen = 0; + + /* + * Adjust data length if insertion of options will + * bump the packet length beyond the t_maxopd length. + * Clear the FIN bit because we cut off the tail of + * the segment. + */ + if (len + optlen + ipoptlen > tp->t_maxopd) { + flags &= ~TH_FIN; + /* + * samkumar: Remove code for TCP segmentation offloading. + */ + len = tp->t_maxopd - optlen - ipoptlen; + sendalot = 1; + } + /* + * samkumar: The else case of the above "if" statement would set tso to 0. + * Removing this since we no longer need a tso variable. + */ + KASSERT(len + hdrlen + ipoptlen <= IP_MAXPACKET, + ("%s: len > IP_MAXPACKET", __func__)); + + /* + * This KASSERT is here to catch edge cases at a well defined place. + * Before, those had triggered (random) panic conditions further down. + */ + KASSERT(len >= 0, ("[%s:%d]: len < 0", __func__, __LINE__)); + + /* + * Grab a header mbuf, attaching a copy of data to + * be transmitted, and initialize the header from + * the template for sends on this connection. + */ + + /* + * samkumar: The code to allocate, build, and send outgoing segments has + * been rewritten. I've left the original code to build the output mbuf + * here in a comment, for reference. The new code is below. + */ +#if 0 + if (len) { + struct mbuf *mb; + uint32_t moff; + + if ((tp->t_flags & TF_FORCEDATA) && len == 1) + TCPSTAT_INC(tcps_sndprobe); + else if (SEQ_LT(tp->snd_nxt, tp->snd_max) || sack_rxmit) { + tp->t_sndrexmitpack++; + TCPSTAT_INC(tcps_sndrexmitpack); + TCPSTAT_ADD(tcps_sndrexmitbyte, len); + } else { + TCPSTAT_INC(tcps_sndpack); + TCPSTAT_ADD(tcps_sndbyte, len); + } +#ifdef INET6 + if (MHLEN < hdrlen + max_linkhdr) + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + else +#endif + m = m_gethdr(M_NOWAIT, MT_DATA); + + if (m == NULL) { + SOCKBUF_UNLOCK(&so->so_snd); + error = ENOBUFS; + sack_rxmit = 0; + goto out; + } + + m->m_data += max_linkhdr; + m->m_len = hdrlen; + + /* + * Start the m_copy functions from the closest mbuf + * to the offset in the socket buffer chain. + */ + mb = sbsndptr(&so->so_snd, off, len, &moff); + + if (len <= MHLEN - hdrlen - max_linkhdr) { + m_copydata(mb, moff, (int)len, + mtod(m, caddr_t) + hdrlen); + m->m_len += len; + } else { + m->m_next = m_copy(mb, moff, (int)len); + if (m->m_next == NULL) { + SOCKBUF_UNLOCK(&so->so_snd); + (void) m_free(m); + error = ENOBUFS; + sack_rxmit = 0; + goto out; + } + } + + /* + * If we're sending everything we've got, set PUSH. + * (This will keep happy those implementations which only + * give data to the user when a buffer fills or + * a PUSH comes in.) + */ + if (off + len == sbused(&so->so_snd)) + flags |= TH_PUSH; + SOCKBUF_UNLOCK(&so->so_snd); + } else { + SOCKBUF_UNLOCK(&so->so_snd); + if (tp->t_flags & TF_ACKNOW) + TCPSTAT_INC(tcps_sndacks); + else if (flags & (TH_SYN|TH_FIN|TH_RST)) + TCPSTAT_INC(tcps_sndctrl); + else if (SEQ_GT(tp->snd_up, tp->snd_una)) + TCPSTAT_INC(tcps_sndurg); + else + TCPSTAT_INC(tcps_sndwinup); + + m = m_gethdr(M_NOWAIT, MT_DATA); + if (m == NULL) { + error = ENOBUFS; + sack_rxmit = 0; + goto out; + } +#ifdef INET6 + if (isipv6 && (MHLEN < hdrlen + max_linkhdr) && + MHLEN >= hdrlen) { + M_ALIGN(m, hdrlen); + } else +#endif + m->m_data += max_linkhdr; + m->m_len = hdrlen; + } +#endif + + KASSERT(ipoptlen == 0, ("No IP options supported")); // samkumar + + otMessage* message = tcplp_sys_new_message(tp->instance); + if (message == NULL) { + error = ENOBUFS; + sack_rxmit = 0; + goto out; + } + if (otMessageSetLength(message, sizeof(struct tcphdr) + optlen + len) != OT_ERROR_NONE) { + tcplp_sys_free_message(tp->instance, message); + error = ENOBUFS; + sack_rxmit = 0; + goto out; + } + if (len) { + uint32_t used_space = lbuf_used_space(&tp->sendbuf); + + /* + * The TinyOS version has a way to avoid the copying we have to do here. + * Because it is possible to send iovecs directly in the BLIP stack, and + * an lbuf is made of iovecs, we could just "save" the starting and ending + * iovecs, modify them to get exactly the slice we want, call "send" on + * the resulting chain, and then restore the starting and ending iovecs + * once "send" returns. + * + * In RIOT, pktsnips have additional behavior regarding memory management + * that precludes this optimization. But, now that we have moved to + * cbufs, this is not relevant anymore. + */ + { + otLinkedBuffer* start; + size_t start_offset; + otLinkedBuffer* end; + size_t end_offset; + int rv = lbuf_getrange(&tp->sendbuf, off, len, &start, &start_offset, &end, &end_offset); + KASSERT(rv == 0, ("Reading send buffer out of range!\n")); + size_t message_offset = otMessageGetOffset(message) + sizeof(struct tcphdr) + optlen; + for (otLinkedBuffer* curr = start; curr != end->mNext; curr = curr->mNext) { + const uint8_t* data_to_copy = curr->mData; + size_t length_to_copy = curr->mLength; + if (curr == start) { + data_to_copy += start_offset; + length_to_copy -= start_offset; + } + if (curr == end) { + length_to_copy -= end_offset; + } + otMessageWrite(message, message_offset, data_to_copy, length_to_copy); + message_offset += length_to_copy; + } + } + + /* + * If we're sending everything we've got, set PUSH. + * (This will keep happy those implementations which only + * give data to the user when a buffer fills or + * a PUSH comes in.) + */ + /* samkumar: Replaced call to sbused(&so->so_snd) with used_space. */ + if (off + len == used_space) + flags |= TH_PUSH; + } + + char outbuf[sizeof(struct tcphdr) + TCP_MAXOLEN]; + th = (struct tcphdr*) (&outbuf[0]); + + /* + * samkumar: I replaced the original call to tcpip_fillheaders with the + * one below. + */ + otMessageInfo ip6info; + tcpip_fillheaders(tp, &ip6info, th); + + /* + * Fill in fields, remembering maximum advertised + * window for use in delaying messages about window sizes. + * If resending a FIN, be sure not to use a new sequence number. + */ + if (flags & TH_FIN && tp->t_flags & TF_SENTFIN && + tp->snd_nxt == tp->snd_max) + tp->snd_nxt--; + /* + * If we are starting a connection, send ECN setup + * SYN packet. If we are on a retransmit, we may + * resend those bits a number of times as per + * RFC 3168. + */ + if (tp->t_state == TCPS_SYN_SENT && V_tcp_do_ecn) { + if (tp->t_rxtshift >= 1) { + if (tp->t_rxtshift <= V_tcp_ecn_maxretries) + flags |= TH_ECE|TH_CWR; + } else + flags |= TH_ECE|TH_CWR; + } + + /* + * samkumar: Make tcp_output reply with ECE flag in the SYN-ACK for + * ECN-enabled connections. The existing code in FreeBSD didn't have to do + * this, because it didn't use tcp_output to send the SYN-ACK; it + * constructed the SYN-ACK segment manually. Yet another consequnce of + * removing the SYN cache... + */ + if (tp->t_state == TCPS_SYN_RECEIVED && tp->t_flags & TF_ECN_PERMIT && + V_tcp_do_ecn) { + flags |= TH_ECE; + } + + if (tp->t_state == TCPS_ESTABLISHED && + (tp->t_flags & TF_ECN_PERMIT)) { + /* + * If the peer has ECN, mark data packets with + * ECN capable transmission (ECT). + * Ignore pure ack packets, retransmissions and window probes. + */ + if (len > 0 && SEQ_GEQ(tp->snd_nxt, tp->snd_max) && + !((tp->t_flags & TF_FORCEDATA) && len == 1)) { + /* + * samkumar: Replaced ip6->ip6_flow |= htonl(IPTOS_ECN_ECT0 << 20); + * with the following code, which will cause OpenThread to set the + * ECT0 bit in the header. + */ + ip6info.mEcn = OT_ECN_CAPABLE_0; + } + + /* + * Reply with proper ECN notifications. + */ + if (tp->t_flags & TF_ECN_SND_CWR) { + flags |= TH_CWR; + tp->t_flags &= ~TF_ECN_SND_CWR; + } + if (tp->t_flags & TF_ECN_SND_ECE) + flags |= TH_ECE; + } + + /* + * If we are doing retransmissions, then snd_nxt will + * not reflect the first unsent octet. For ACK only + * packets, we do not want the sequence number of the + * retransmitted packet, we want the sequence number + * of the next unsent octet. So, if there is no data + * (and no SYN or FIN), use snd_max instead of snd_nxt + * when filling in ti_seq. But if we are in persist + * state, snd_max might reflect one byte beyond the + * right edge of the window, so use snd_nxt in that + * case, since we know we aren't doing a retransmission. + * (retransmit and persist are mutually exclusive...) + */ + if (sack_rxmit == 0) { + if (len || (flags & (TH_SYN|TH_FIN)) || + tcp_timer_active(tp, TT_PERSIST)) + th->th_seq = htonl(tp->snd_nxt); + else + th->th_seq = htonl(tp->snd_max); + } else { + th->th_seq = htonl(p->rxmit); + p->rxmit += len; + tp->sackhint.sack_bytes_rexmit += len; + } + + /* + * samkumar: Check if this is a retransmission (added as part of TCPlp). + * This kind of stats collection is useful but not necessary for TCP, so + * I've left it as a comment in case we want to bring this back to measure + * performance. + */ +#if 0 + if (len > 0 && !tcp_timer_active(tp, TT_PERSIST) && SEQ_LT(ntohl(th->th_seq), tp->snd_max)) { + tcplp_totalRexmitCnt++; + } +#endif + + th->th_ack = htonl(tp->rcv_nxt); + if (optlen) { + bcopy(opt, th + 1, optlen); + th->th_off = (sizeof (struct tcphdr) + optlen) >> 2; + } + th->th_flags = flags; + /* + * Calculate receive window. Don't shrink window, + * but avoid silly window syndrome. + */ + /* samkumar: Replaced so->so_rcv.sb_hiwat with this call to cbuf_size. */ + if (recwin < (long)(cbuf_size(&tp->recvbuf) / 4) && + recwin < (long)tp->t_maxseg) + recwin = 0; + if (SEQ_GT(tp->rcv_adv, tp->rcv_nxt) && + recwin < (long)(tp->rcv_adv - tp->rcv_nxt)) + recwin = (long)(tp->rcv_adv - tp->rcv_nxt); + if (recwin > (long)TCP_MAXWIN << tp->rcv_scale) + recwin = (long)TCP_MAXWIN << tp->rcv_scale; + + /* + * According to RFC1323 the window field in a SYN (i.e., a + * or ) segment itself is never scaled. The + * case is handled in syncache. + */ + if (flags & TH_SYN) + th->th_win = htons((uint16_t) + (min(cbuf_size(&tp->recvbuf), TCP_MAXWIN))); + else + th->th_win = htons((uint16_t)(recwin >> tp->rcv_scale)); + + /* + * Adjust the RXWIN0SENT flag - indicate that we have advertised + * a 0 window. This may cause the remote transmitter to stall. This + * flag tells soreceive() to disable delayed acknowledgements when + * draining the buffer. This can occur if the receiver is attempting + * to read more data than can be buffered prior to transmitting on + * the connection. + */ + if (th->th_win == 0) { + tp->t_flags |= TF_RXWIN0SENT; + } else + tp->t_flags &= ~TF_RXWIN0SENT; + if (SEQ_GT(tp->snd_up, tp->snd_nxt)) { + th->th_urp = htons((uint16_t)(tp->snd_up - tp->snd_nxt)); + th->th_flags |= TH_URG; + } else + /* + * If no urgent pointer to send, then we pull + * the urgent pointer to the left edge of the send window + * so that it doesn't drift into the send window on sequence + * number wraparound. + */ + tp->snd_up = tp->snd_una; /* drag it along */ + + /* + * samkumar: Removed code for TCP signatures. + */ + /* + * Put TCP length in extended header, and then + * checksum extended header and data. + */ + /* + * samkumar: The code to implement the above comment isn't relevant to us. + * Checksum computation is not handled using FreeBSD code, so we don't need + * to build an extended header. + */ + /* + * samkumar: Removed code for TCP Segmentation Offloading. + */ + /* samkumar: Removed mbuf-specific assertions an debug code. */ + /* + * Fill in IP length and desired time to live and + * send to IP level. There should be a better way + * to handle ttl and tos; we could keep them in + * the template, but need a way to checksum without them. + */ + /* + * m->m_pkthdr.len should have been set before checksum calculation, + * because in6_cksum() need it. + */ + /* + * samkumar: The IPv6 packet length and hop limit are handled by the host + * network stack, not by TCPlp. I've also removed code for Path MTU + * discovery. And of course, I've removed debug code as well. + */ + /* samkumar: I've replaced the call to ip6_output with the following. */ + otMessageWrite(message, 0, outbuf, sizeof(struct tcphdr) + optlen); + tcplp_sys_send_message(tp->instance, message, &ip6info); + +out: + /* + * In transmit state, time the transmission and arrange for + * the retransmit. In persist state, just set snd_max. + */ + if ((tp->t_flags & TF_FORCEDATA) == 0 || + !tcp_timer_active(tp, TT_PERSIST)) { + tcp_seq startseq = tp->snd_nxt; + + /* + * Advance snd_nxt over sequence space of this segment. + */ + if (flags & (TH_SYN|TH_FIN)) { + if (flags & TH_SYN) + tp->snd_nxt++; + if (flags & TH_FIN) { + tp->snd_nxt++; + tp->t_flags |= TF_SENTFIN; + } + } + if (sack_rxmit) + goto timer; + tp->snd_nxt += len; + if (SEQ_GT(tp->snd_nxt, tp->snd_max)) { + tp->snd_max = tp->snd_nxt; + /* + * Time this transmission if not a retransmission and + * not currently timing anything. + */ + if (tp->t_rtttime == 0) { + tp->t_rtttime = ticks; + tp->t_rtseq = startseq; + } + } + + /* + * Set retransmit timer if not currently set, + * and not doing a pure ack or a keep-alive probe. + * Initial value for retransmit timer is smoothed + * round-trip time + 2 * round-trip time variance. + * Initialize shift counter which is used for backoff + * of retransmit time. + */ +timer: + if (!tcp_timer_active(tp, TT_REXMT) && + ((sack_rxmit && tp->snd_nxt != tp->snd_max) || + (tp->snd_nxt != tp->snd_una))) { + if (tcp_timer_active(tp, TT_PERSIST)) { + tcp_timer_activate(tp, TT_PERSIST, 0); + tp->t_rxtshift = 0; + } + tcp_timer_activate(tp, TT_REXMT, tp->t_rxtcur); + /* + * samkumar: Replaced sbavail(&so->so_snd) with this call to + * lbuf_used_space. + */ + } else if (len == 0 && lbuf_used_space(&tp->sendbuf) && + !tcp_timer_active(tp, TT_REXMT) && + !tcp_timer_active(tp, TT_PERSIST)) { + /* + * Avoid a situation where we do not set persist timer + * after a zero window condition. For example: + * 1) A -> B: packet with enough data to fill the window + * 2) B -> A: ACK for #1 + new data (0 window + * advertisement) + * 3) A -> B: ACK for #2, 0 len packet + * + * In this case, A will not activate the persist timer, + * because it chose to send a packet. Unless tcp_output + * is called for some other reason (delayed ack timer, + * another input packet from B, socket syscall), A will + * not send zero window probes. + * + * So, if you send a 0-length packet, but there is data + * in the socket buffer, and neither the rexmt or + * persist timer is already set, then activate the + * persist timer. + */ + tp->t_rxtshift = 0; + tcp_setpersist(tp); + } + } else { + /* + * Persist case, update snd_max but since we are in + * persist mode (no window) we do not update snd_nxt. + */ + int xlen = len; + if (flags & TH_SYN) + ++xlen; + if (flags & TH_FIN) { + ++xlen; + tp->t_flags |= TF_SENTFIN; + } + if (SEQ_GT(tp->snd_nxt + xlen, tp->snd_max)) + tp->snd_max = tp->snd_nxt + len; + } + + if (error) { + + /* + * We know that the packet was lost, so back out the + * sequence number advance, if any. + * + * If the error is EPERM the packet got blocked by the + * local firewall. Normally we should terminate the + * connection but the blocking may have been spurious + * due to a firewall reconfiguration cycle. So we treat + * it like a packet loss and let the retransmit timer and + * timeouts do their work over time. + * XXX: It is a POLA question whether calling tcp_drop right + * away would be the really correct behavior instead. + */ + if (((tp->t_flags & TF_FORCEDATA) == 0 || + !tcp_timer_active(tp, TT_PERSIST)) && + ((flags & TH_SYN) == 0) && + (error != EPERM)) { + if (sack_rxmit) { + p->rxmit -= len; + tp->sackhint.sack_bytes_rexmit -= len; + KASSERT(tp->sackhint.sack_bytes_rexmit >= 0, + ("sackhint bytes rtx >= 0")); + } else + tp->snd_nxt -= len; + } + switch (error) { + case EPERM: + tp->t_softerror = error; + return (error); + case ENOBUFS: + if (!tcp_timer_active(tp, TT_REXMT) && + !tcp_timer_active(tp, TT_PERSIST)) + tcp_timer_activate(tp, TT_REXMT, tp->t_rxtcur); + tp->snd_cwnd = tp->t_maxseg; +#ifdef INSTRUMENT_TCP + printf("TCP ALLOCFAIL %u %d\n", (unsigned int) get_micros(), (int) tp->snd_cwnd); +#endif + return (0); + case EMSGSIZE: + /* + * For some reason the interface we used initially + * to send segments changed to another or lowered + * its MTU. + * If TSO was active we either got an interface + * without TSO capabilits or TSO was turned off. + * If we obtained mtu from ip_output() then update + * it and try again. + */ + /* samkumar: Removed code for TCP Segmentation Offloading. */ + if (mtu != 0) { + tcp_mss_update(tp, -1, mtu, NULL, NULL); + goto again; + } + return (error); + case EHOSTDOWN: + case EHOSTUNREACH: + case ENETDOWN: + case ENETUNREACH: + if (TCPS_HAVERCVDSYN(tp->t_state)) { + tp->t_softerror = error; + return (0); + } + /* FALLTHROUGH */ + default: + return (error); + } + } + + /* + * Data sent (as far as we can tell). + * If this advertises a larger window than any other segment, + * then remember the size of the advertised window. + * Any pending ACK has now been sent. + */ + if (recwin >= 0 && SEQ_GT(tp->rcv_nxt + recwin, tp->rcv_adv)) + tp->rcv_adv = tp->rcv_nxt + recwin; + tp->last_ack_sent = tp->rcv_nxt; + tp->t_flags &= ~(TF_ACKNOW | TF_DELACK); + if (tcp_timer_active(tp, TT_DELACK)) + tcp_timer_activate(tp, TT_DELACK, 0); + + /* + * samkumar: This was already commented out (using #if 0) in the original + * FreeBSD code. + */ +#if 0 + /* + * This completely breaks TCP if newreno is turned on. What happens + * is that if delayed-acks are turned on on the receiver, this code + * on the transmitter effectively destroys the TCP window, forcing + * it to four packets (1.5Kx4 = 6K window). + */ + if (sendalot && --maxburst) + goto again; +#endif + if (sendalot) + goto again; + return (0); +} + +/* + * Insert TCP options according to the supplied parameters to the place + * optp in a consistent way. Can handle unaligned destinations. + * + * The order of the option processing is crucial for optimal packing and + * alignment for the scarce option space. + * + * The optimal order for a SYN/SYN-ACK segment is: + * MSS (4) + NOP (1) + Window scale (3) + SACK permitted (2) + + * Timestamp (10) + Signature (18) = 38 bytes out of a maximum of 40. + * + * The SACK options should be last. SACK blocks consume 8*n+2 bytes. + * So a full size SACK blocks option is 34 bytes (with 4 SACK blocks). + * At minimum we need 10 bytes (to generate 1 SACK block). If both + * TCP Timestamps (12 bytes) and TCP Signatures (18 bytes) are present, + * we only have 10 bytes for SACK options (40 - (12 + 18)). + */ +int +tcp_addoptions(struct tcpopt *to, uint8_t *optp) +{ + uint32_t mask, optlen = 0; + + for (mask = 1; mask < TOF_MAXOPT; mask <<= 1) { + if ((to->to_flags & mask) != mask) + continue; + if (optlen == TCP_MAXOLEN) + break; + switch (to->to_flags & mask) { + case TOF_MSS: + while (optlen % 4) { + optlen += TCPOLEN_NOP; + *optp++ = TCPOPT_NOP; + } + if (TCP_MAXOLEN - optlen < TCPOLEN_MAXSEG) + continue; + optlen += TCPOLEN_MAXSEG; + *optp++ = TCPOPT_MAXSEG; + *optp++ = TCPOLEN_MAXSEG; + to->to_mss = htons(to->to_mss); + bcopy((uint8_t *)&to->to_mss, optp, sizeof(to->to_mss)); + optp += sizeof(to->to_mss); + break; + case TOF_SCALE: + while (!optlen || optlen % 2 != 1) { + optlen += TCPOLEN_NOP; + *optp++ = TCPOPT_NOP; + } + if (TCP_MAXOLEN - optlen < TCPOLEN_WINDOW) + continue; + optlen += TCPOLEN_WINDOW; + *optp++ = TCPOPT_WINDOW; + *optp++ = TCPOLEN_WINDOW; + *optp++ = to->to_wscale; + break; + case TOF_SACKPERM: + while (optlen % 2) { + optlen += TCPOLEN_NOP; + *optp++ = TCPOPT_NOP; + } + if (TCP_MAXOLEN - optlen < TCPOLEN_SACK_PERMITTED) + continue; + optlen += TCPOLEN_SACK_PERMITTED; + *optp++ = TCPOPT_SACK_PERMITTED; + *optp++ = TCPOLEN_SACK_PERMITTED; + break; + case TOF_TS: + while (!optlen || optlen % 4 != 2) { + optlen += TCPOLEN_NOP; + *optp++ = TCPOPT_NOP; + } + if (TCP_MAXOLEN - optlen < TCPOLEN_TIMESTAMP) + continue; + optlen += TCPOLEN_TIMESTAMP; + *optp++ = TCPOPT_TIMESTAMP; + *optp++ = TCPOLEN_TIMESTAMP; + to->to_tsval = htonl(to->to_tsval); + to->to_tsecr = htonl(to->to_tsecr); + bcopy((uint8_t *)&to->to_tsval, optp, sizeof(to->to_tsval)); + optp += sizeof(to->to_tsval); + bcopy((uint8_t *)&to->to_tsecr, optp, sizeof(to->to_tsecr)); + optp += sizeof(to->to_tsecr); + break; + case TOF_SIGNATURE: + { + int siglen = TCPOLEN_SIGNATURE - 2; + + while (!optlen || optlen % 4 != 2) { + optlen += TCPOLEN_NOP; + *optp++ = TCPOPT_NOP; + } + if (TCP_MAXOLEN - optlen < TCPOLEN_SIGNATURE) + continue; + optlen += TCPOLEN_SIGNATURE; + *optp++ = TCPOPT_SIGNATURE; + *optp++ = TCPOLEN_SIGNATURE; + to->to_signature = optp; + while (siglen--) + *optp++ = 0; + break; + } + case TOF_SACK: + { + int sackblks = 0; + struct sackblk *sack = (struct sackblk *)to->to_sacks; + tcp_seq sack_seq; + + while (!optlen || optlen % 4 != 2) { + optlen += TCPOLEN_NOP; + *optp++ = TCPOPT_NOP; + } + if (TCP_MAXOLEN - optlen < TCPOLEN_SACKHDR + TCPOLEN_SACK) + continue; + optlen += TCPOLEN_SACKHDR; + *optp++ = TCPOPT_SACK; + sackblks = min(to->to_nsacks, + (TCP_MAXOLEN - optlen) / TCPOLEN_SACK); + *optp++ = TCPOLEN_SACKHDR + sackblks * TCPOLEN_SACK; + while (sackblks--) { + sack_seq = htonl(sack->start); + bcopy((uint8_t *)&sack_seq, optp, sizeof(sack_seq)); + optp += sizeof(sack_seq); + sack_seq = htonl(sack->end); + bcopy((uint8_t *)&sack_seq, optp, sizeof(sack_seq)); + optp += sizeof(sack_seq); + optlen += TCPOLEN_SACK; + sack++; + } + /* samkumar: Removed TCPSTAT_INC(tcps_sack_send_blocks); */ + break; + } + default: + printf("PANIC: %s: unknown TCP option type", __func__); + break; + } + } + + /* Terminate and pad TCP options to a 4 byte boundary. */ + if (optlen % 4) { + optlen += TCPOLEN_EOL; + *optp++ = TCPOPT_EOL; + } + /* + * According to RFC 793 (STD0007): + * "The content of the header beyond the End-of-Option option + * must be header padding (i.e., zero)." + * and later: "The padding is composed of zeros." + */ + while (optlen % 4) { + optlen += TCPOLEN_PAD; + *optp++ = TCPOPT_PAD; + } + + KASSERT(optlen <= TCP_MAXOLEN, ("%s: TCP options too long", __func__)); + return (optlen); +} diff --git a/third_party/tcplp/bsdtcp/tcp_reass.c b/third_party/tcplp/bsdtcp/tcp_reass.c new file mode 100644 index 000000000..bf53f80ad --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_reass.c @@ -0,0 +1,314 @@ +/*- + * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995 + * 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_input.c 8.12 (Berkeley) 5/24/95 + */ + +#include "../tcplp.h" +#include "../lib/bitmap.h" +#include "../lib/cbuf.h" +#include "tcp.h" +#include "tcp_fsm.h" +#include "tcp_seq.h" +#include "tcp_var.h" + +/* + * samkumar: Segments are only reassembled within the window; data outside the + * window is thrown away. So, the total amount of reassembly data cannot exceed + * the size of the receive window. + * + * I have essentially rewritten it to use TCPlp's data structure for the + * reassembly buffer. I have kept the original code as a comment below this + * function, for reference. + * + * Looking at the usage of this function in tcp_input, this just has to set + * *tlenp to 0 if the received segment is already completely buffered; it does + * not need to update it if only part of the segment is trimmed off. + */ +int +tcp_reass(struct tcpcb* tp, struct tcphdr* th, int* tlenp, otMessage* data, off_t data_offset, struct signals* sig) +{ + size_t mergeable, written; + size_t offset; + size_t start_index; + size_t usedbefore; + int tlen = *tlenp; + size_t merged = 0; + int flags = 0; + + /* + * Call with th==NULL after become established to + * force pre-ESTABLISHED data up to user socket. + */ + if (th == NULL) + goto present; + + /* Insert the new segment queue entry into place. */ + KASSERT(SEQ_GEQ(th->th_seq, tp->rcv_nxt), ("Adding past segment to the reassembly queue\n")); + offset = (size_t) (th->th_seq - tp->rcv_nxt); + + if (cbuf_reass_count_set(&tp->recvbuf, (size_t) offset, tp->reassbmp, tlen) >= (size_t) tlen) { + *tlenp = 0; + goto present; + } + written = cbuf_reass_write(&tp->recvbuf, (size_t) offset, data, data_offset, tlen, tp->reassbmp, &start_index, cbuf_copy_from_message); + + if ((th->th_flags & TH_FIN) && (tp->reass_fin_index == -1)) { + tp->reass_fin_index = (int16_t) (start_index + tlen); + } + KASSERT(written == tlen, ("Reassembly write out of bounds: tried to write %d, but wrote %d\n", tlen, (int) written)); + +present: + /* + * Present data to user, advancing rcv_nxt through + * completed sequence space. + */ + mergeable = cbuf_reass_count_set(&tp->recvbuf, 0, tp->reassbmp, (size_t) 0xFFFFFFFF); + usedbefore = cbuf_used_space(&tp->recvbuf); + if (!tpiscantrcv(tp) || usedbefore == 0) { + /* If usedbefore == 0, but we can't receive more, then we still need to move the buffer + along by merging and then popping, in case we receive a FIN later on. */ + if (tp->reass_fin_index >= 0 && cbuf_reass_within_offset(&tp->recvbuf, mergeable, (size_t) tp->reass_fin_index)) { + tp->reass_fin_index = -2; // So we won't consider any more FINs + flags = TH_FIN; + } + merged = cbuf_reass_merge(&tp->recvbuf, mergeable, tp->reassbmp); + KASSERT(merged == mergeable, ("Reassembly merge out of bounds: tried to merge %d, but merged %d\n", (int) mergeable, (int) merged)); + if (tpiscantrcv(tp)) { + cbuf_pop(&tp->recvbuf, merged); // So no data really enters the buffer + } else if (usedbefore == 0 && merged > 0) { + sig->recvbuf_notempty = true; + } + } else { + /* If there is data in the buffer AND we can't receive more, then that must be because we received a FIN, + but the user hasn't yet emptied the buffer of its contents. */ + KASSERT (tp->reass_fin_index == -2, ("Can't receive more, and data in buffer, but haven't received a FIN\n")); + } + + tp->rcv_nxt += mergeable; + + return flags; +} +#if 0 +int +tcp_reass(struct tcpcb *tp, struct tcphdr *th, int *tlenp, struct mbuf *m) +{ + struct tseg_qent *q; + struct tseg_qent *p = NULL; + struct tseg_qent *nq; + struct tseg_qent *te = NULL; + struct socket *so = tp->t_inpcb->inp_socket; + char *s = NULL; + int flags; + struct tseg_qent tqs; + + INP_WLOCK_ASSERT(tp->t_inpcb); + + /* + * XXX: tcp_reass() is rather inefficient with its data structures + * and should be rewritten (see NetBSD for optimizations). + */ + + /* + * Call with th==NULL after become established to + * force pre-ESTABLISHED data up to user socket. + */ + if (th == NULL) + goto present; + + /* + * Limit the number of segments that can be queued to reduce the + * potential for mbuf exhaustion. For best performance, we want to be + * able to queue a full window's worth of segments. The size of the + * socket receive buffer determines our advertised window and grows + * automatically when socket buffer autotuning is enabled. Use it as the + * basis for our queue limit. + * Always let the missing segment through which caused this queue. + * NB: Access to the socket buffer is left intentionally unlocked as we + * can tolerate stale information here. + * + * XXXLAS: Using sbspace(so->so_rcv) instead of so->so_rcv.sb_hiwat + * should work but causes packets to be dropped when they shouldn't. + * Investigate why and re-evaluate the below limit after the behaviour + * is understood. + */ + if ((th->th_seq != tp->rcv_nxt || !TCPS_HAVEESTABLISHED(tp->t_state)) && + tp->t_segqlen >= (so->so_rcv.sb_hiwat / tp->t_maxseg) + 1) { + TCPSTAT_INC(tcps_rcvreassfull); + *tlenp = 0; + if ((s = tcp_log_addrs(&tp->t_inpcb->inp_inc, th, NULL, NULL))) { + log(LOG_DEBUG, "%s; %s: queue limit reached, " + "segment dropped\n", s, __func__); + free(s, M_TCPLOG); + } + m_freem(m); + return (0); + } + + /* + * Allocate a new queue entry. If we can't, or hit the zone limit + * just drop the pkt. + * + * Use a temporary structure on the stack for the missing segment + * when the zone is exhausted. Otherwise we may get stuck. + */ + te = uma_zalloc(tcp_reass_zone, M_NOWAIT); + if (te == NULL) { + if (th->th_seq != tp->rcv_nxt || !TCPS_HAVEESTABLISHED(tp->t_state)) { + TCPSTAT_INC(tcps_rcvmemdrop); + m_freem(m); + *tlenp = 0; + if ((s = tcp_log_addrs(&tp->t_inpcb->inp_inc, th, NULL, + NULL))) { + log(LOG_DEBUG, "%s; %s: global zone limit " + "reached, segment dropped\n", s, __func__); + free(s, M_TCPLOG); + } + return (0); + } else { + bzero(&tqs, sizeof(struct tseg_qent)); + te = &tqs; + if ((s = tcp_log_addrs(&tp->t_inpcb->inp_inc, th, NULL, + NULL))) { + log(LOG_DEBUG, + "%s; %s: global zone limit reached, using " + "stack for missing segment\n", s, __func__); + free(s, M_TCPLOG); + } + } + } + tp->t_segqlen++; + + /* + * Find a segment which begins after this one does. + */ + LIST_FOREACH(q, &tp->t_segq, tqe_q) { + if (SEQ_GT(q->tqe_th->th_seq, th->th_seq)) + break; + p = q; + } + + /* + * If there is a preceding segment, it may provide some of + * our data already. If so, drop the data from the incoming + * segment. If it provides all of our data, drop us. + */ + if (p != NULL) { + int i; + /* conversion to int (in i) handles seq wraparound */ + i = p->tqe_th->th_seq + p->tqe_len - th->th_seq; + if (i > 0) { + if (i >= *tlenp) { + TCPSTAT_INC(tcps_rcvduppack); + TCPSTAT_ADD(tcps_rcvdupbyte, *tlenp); + m_freem(m); + if (te != &tqs) + uma_zfree(tcp_reass_zone, te); + tp->t_segqlen--; + /* + * Try to present any queued data + * at the left window edge to the user. + * This is needed after the 3-WHS + * completes. + */ + goto present; /* ??? */ + } + m_adj(m, i); + *tlenp -= i; + th->th_seq += i; + } + } + tp->t_rcvoopack++; + TCPSTAT_INC(tcps_rcvoopack); + TCPSTAT_ADD(tcps_rcvoobyte, *tlenp); + + /* + * While we overlap succeeding segments trim them or, + * if they are completely covered, dequeue them. + */ + while (q) { + int i = (th->th_seq + *tlenp) - q->tqe_th->th_seq; + if (i <= 0) + break; + if (i < q->tqe_len) { + q->tqe_th->th_seq += i; + q->tqe_len -= i; + m_adj(q->tqe_m, i); + break; + } + + nq = LIST_NEXT(q, tqe_q); + LIST_REMOVE(q, tqe_q); + m_freem(q->tqe_m); + uma_zfree(tcp_reass_zone, q); + tp->t_segqlen--; + q = nq; + } + + /* Insert the new segment queue entry into place. */ + te->tqe_m = m; + te->tqe_th = th; + te->tqe_len = *tlenp; + + if (p == NULL) { + LIST_INSERT_HEAD(&tp->t_segq, te, tqe_q); + } else { + KASSERT(te != &tqs, ("%s: temporary stack based entry not " + "first element in queue", __func__)); + LIST_INSERT_AFTER(p, te, tqe_q); + } + +present: + /* + * Present data to user, advancing rcv_nxt through + * completed sequence space. + */ + if (!TCPS_HAVEESTABLISHED(tp->t_state)) + return (0); + q = LIST_FIRST(&tp->t_segq); + if (!q || q->tqe_th->th_seq != tp->rcv_nxt) + return (0); + SOCKBUF_LOCK(&so->so_rcv); + do { + tp->rcv_nxt += q->tqe_len; + flags = q->tqe_th->th_flags & TH_FIN; + nq = LIST_NEXT(q, tqe_q); + LIST_REMOVE(q, tqe_q); + if (so->so_rcv.sb_state & SBS_CANTRCVMORE) + m_freem(q->tqe_m); + else + sbappendstream_locked(&so->so_rcv, q->tqe_m, 0); + if (q != &tqs) + uma_zfree(tcp_reass_zone, q); + tp->t_segqlen--; + q = nq; + } while (q && q->tqe_th->th_seq == tp->rcv_nxt); + sorwakeup_locked(so); + return (flags); +} +#endif diff --git a/third_party/tcplp/bsdtcp/tcp_sack.c b/third_party/tcplp/bsdtcp/tcp_sack.c new file mode 100644 index 000000000..e2f47dac4 --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_sack.c @@ -0,0 +1,665 @@ +/*- + * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995 + * 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_sack.c 8.12 (Berkeley) 5/24/95 + */ + +/*- + * @@(#)COPYRIGHT 1.1 (NRL) 17 January 1995 + * + * NRL grants permission for redistribution and use in source and binary + * forms, with or without modification, of the software and documentation + * created at NRL 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgements: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * This product includes software developed at the Information + * Technology Division, US Naval Research Laboratory. + * 4. Neither the name of the NRL nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL 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 NRL 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. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the US Naval + * Research Laboratory (NRL). + */ + +/* samkumar: Removed a bunch of #include's and VNET declarations. */ + +#include +#include "tcp.h" +#include "tcp_fsm.h" +#include "tcp_seq.h" +#include "tcp_timer.h" +#include "tcp_var.h" +#include "sys/queue.h" + +enum tcp_sack_consts { + V_tcp_sack_maxholes = MAX_SACKHOLES +}; + +/* + * samkumar: Removed tcp_sack_globalmaxholes and tcp_sack_globalholes. + * There used to be a counter, V_tcp_sack_globalholes, that kept track of the + * total number of SACK holes allocated across all TCP connections. + */ + +/* + * samkumar: I added these three functions. The first, tcp_sack_init, + * initializes a per-connection pool of SACK holes. + * + * The next two, sackhole_alloc and sackhole_free, allocate and deallocate SACK + * holes from the pool. Previously, the FreeBSD code would allocate SACK holes + * dynamically, for example, using the code + * "hole = (struct sackhole *)uma_zalloc(V_sack_hole_zone, M_NOWAIT);". + * TCPlp avoids dynamic memory allocation in the TCP implementation, so we + * replace it with this per-connection pool. + */ + +void +tcp_sack_init(struct tcpcb* tp) +{ + bmp_init(tp->sackhole_bmp, SACKHOLE_BMP_SIZE); +} + +struct sackhole* sackhole_alloc(struct tcpcb* tp) { + size_t freeindex = bmp_countset(tp->sackhole_bmp, SACKHOLE_BMP_SIZE, 0, SACKHOLE_BMP_SIZE); + if (freeindex >= SACKHOLE_BMP_SIZE) { + return NULL; // all sackholes are allocated already! + } + bmp_setrange(tp->sackhole_bmp, freeindex, 1); + return &tp->sackhole_pool[freeindex]; +} + +void sackhole_free(struct tcpcb* tp, struct sackhole* tofree) { + size_t freeindex = (size_t) (tofree - &tp->sackhole_pool[0]); + KASSERT(tofree == &tp->sackhole_pool[freeindex], ("sackhole pool unaligned\n")); + bmp_clrrange(tp->sackhole_bmp, freeindex, 1); +} + +/* + * samkumar: Throughout the remaining functions, I have replaced allocation and + * deallocation of SACK holes, which previously used uma_zalloc and uma_zfree, + * with calls to sackhole_alloc and sackhole_free. I've also removed code for + * locking, global stats collection, global SACK hole limits, and debugging + * probes. + */ + + +/* + * This function is called upon receipt of new valid data (while not in + * header prediction mode), and it updates the ordered list of sacks. + */ +void +tcp_update_sack_list(struct tcpcb *tp, tcp_seq rcv_start, tcp_seq rcv_end) +{ + /* + * First reported block MUST be the most recent one. Subsequent + * blocks SHOULD be in the order in which they arrived at the + * receiver. These two conditions make the implementation fully + * compliant with RFC 2018. + */ + struct sackblk head_blk, saved_blks[MAX_SACK_BLKS]; + int num_head, num_saved, i; + + /* Check arguments. */ + KASSERT(SEQ_LT(rcv_start, rcv_end), ("rcv_start < rcv_end")); + + /* SACK block for the received segment. */ + head_blk.start = rcv_start; + head_blk.end = rcv_end; + + /* + * Merge updated SACK blocks into head_blk, and save unchanged SACK + * blocks into saved_blks[]. num_saved will have the number of the + * saved SACK blocks. + */ + num_saved = 0; + for (i = 0; i < tp->rcv_numsacks; i++) { + tcp_seq start = tp->sackblks[i].start; + tcp_seq end = tp->sackblks[i].end; + if (SEQ_GEQ(start, end) || SEQ_LEQ(start, tp->rcv_nxt)) { + /* + * Discard this SACK block. + */ + } else if (SEQ_LEQ(head_blk.start, end) && + SEQ_GEQ(head_blk.end, start)) { + /* + * Merge this SACK block into head_blk. This SACK + * block itself will be discarded. + */ + if (SEQ_GT(head_blk.start, start)) + head_blk.start = start; + if (SEQ_LT(head_blk.end, end)) + head_blk.end = end; + } else { + /* + * Save this SACK block. + */ + saved_blks[num_saved].start = start; + saved_blks[num_saved].end = end; + num_saved++; + } + } + + /* + * Update SACK list in tp->sackblks[]. + */ + num_head = 0; + if (SEQ_GT(head_blk.start, tp->rcv_nxt)) { + /* + * The received data segment is an out-of-order segment. Put + * head_blk at the top of SACK list. + */ + tp->sackblks[0] = head_blk; + num_head = 1; + /* + * If the number of saved SACK blocks exceeds its limit, + * discard the last SACK block. + */ + if (num_saved >= MAX_SACK_BLKS) + num_saved--; + } + if (num_saved > 0) { + /* + * Copy the saved SACK blocks back. + */ + bcopy(saved_blks, &tp->sackblks[num_head], + sizeof(struct sackblk) * num_saved); + } + + /* Save the number of SACK blocks. */ + tp->rcv_numsacks = num_head + num_saved; +} + +/* + * Delete all receiver-side SACK information. + */ +void +tcp_clean_sackreport(struct tcpcb *tp) +{ + int i; + + tp->rcv_numsacks = 0; + for (i = 0; i < MAX_SACK_BLKS; i++) + tp->sackblks[i].start = tp->sackblks[i].end=0; +} + +/* + * Allocate struct sackhole. + */ +static struct sackhole * +tcp_sackhole_alloc(struct tcpcb *tp, tcp_seq start, tcp_seq end) +{ + struct sackhole *hole; + + /* + * samkumar: This if block also used to also return NULL if + * V_tcp_sack_globalholes >= V_tcp_sack_globalmaxholes + * but I removed that check since it doesn't make sense to enforce a global + * limit on SACK holes when we have a fixed-size pool (moreover, a separate + * pool per connection). The per-connection limit is sufficient. + */ + if (tp->snd_numholes >= V_tcp_sack_maxholes) { + return NULL; + } + + hole = sackhole_alloc(tp); + if (hole == NULL) + return NULL; + + hole->start = start; + hole->end = end; + hole->rxmit = start; + + tp->snd_numholes++; + + return hole; +} + +/* + * Free struct sackhole. + */ +static void +tcp_sackhole_free(struct tcpcb *tp, struct sackhole *hole) +{ + sackhole_free(tp, hole); + + tp->snd_numholes--; + + KASSERT(tp->snd_numholes >= 0, ("tp->snd_numholes >= 0")); +} + +/* + * Insert new SACK hole into scoreboard. + */ +static struct sackhole * +tcp_sackhole_insert(struct tcpcb *tp, tcp_seq start, tcp_seq end, + struct sackhole *after) +{ + struct sackhole *hole; + + /* Allocate a new SACK hole. */ + hole = tcp_sackhole_alloc(tp, start, end); + if (hole == NULL) + return NULL; + + /* Insert the new SACK hole into scoreboard. */ + if (after != NULL) + TAILQ_INSERT_AFTER(&tp->snd_holes, after, hole, scblink); + else + TAILQ_INSERT_TAIL(&tp->snd_holes, hole, scblink); + + /* Update SACK hint. */ + if (tp->sackhint.nexthole == NULL) + tp->sackhint.nexthole = hole; + + return hole; +} + +/* + * Remove SACK hole from scoreboard. + */ +static void +tcp_sackhole_remove(struct tcpcb *tp, struct sackhole *hole) +{ + + /* Update SACK hint. */ + if (tp->sackhint.nexthole == hole) + tp->sackhint.nexthole = TAILQ_NEXT(hole, scblink); + + /* Remove this SACK hole. */ + TAILQ_REMOVE(&tp->snd_holes, hole, scblink); + + /* Free this SACK hole. */ + tcp_sackhole_free(tp, hole); +} + +/* + * Process cumulative ACK and the TCP SACK option to update the scoreboard. + * tp->snd_holes is an ordered list of holes (oldest to newest, in terms of + * the sequence space). + */ +void +tcp_sack_doack(struct tcpcb *tp, struct tcpopt *to, tcp_seq th_ack) +{ + struct sackhole *cur, *temp; + struct sackblk sack, sack_blocks[TCP_MAX_SACK + 1], *sblkp; + int i, j, num_sack_blks; + + num_sack_blks = 0; + /* + * If SND.UNA will be advanced by SEG.ACK, and if SACK holes exist, + * treat [SND.UNA, SEG.ACK) as if it is a SACK block. + */ + if (SEQ_LT(tp->snd_una, th_ack) && !TAILQ_EMPTY(&tp->snd_holes)) { + sack_blocks[num_sack_blks].start = tp->snd_una; + sack_blocks[num_sack_blks++].end = th_ack; + } + /* + * Append received valid SACK blocks to sack_blocks[], but only if we + * received new blocks from the other side. + */ + if (to->to_flags & TOF_SACK) { + for (i = 0; i < to->to_nsacks; i++) { + bcopy((to->to_sacks + i * TCPOLEN_SACK), + &sack, sizeof(sack)); + sack.start = ntohl(sack.start); + sack.end = ntohl(sack.end); + if (SEQ_GT(sack.end, sack.start) && + SEQ_GT(sack.start, tp->snd_una) && + SEQ_GT(sack.start, th_ack) && + SEQ_LT(sack.start, tp->snd_max) && + SEQ_GT(sack.end, tp->snd_una) && + SEQ_LEQ(sack.end, tp->snd_max)) + sack_blocks[num_sack_blks++] = sack; + } + } + /* + * Return if SND.UNA is not advanced and no valid SACK block is + * received. + */ + if (num_sack_blks == 0) + return; + + /* + * Sort the SACK blocks so we can update the scoreboard with just one + * pass. The overhead of sorting upto 4+1 elements is less than + * making upto 4+1 passes over the scoreboard. + */ + for (i = 0; i < num_sack_blks; i++) { + for (j = i + 1; j < num_sack_blks; j++) { + if (SEQ_GT(sack_blocks[i].end, sack_blocks[j].end)) { + sack = sack_blocks[i]; + sack_blocks[i] = sack_blocks[j]; + sack_blocks[j] = sack; + } + } + } + if (TAILQ_EMPTY(&tp->snd_holes)) + /* + * Empty scoreboard. Need to initialize snd_fack (it may be + * uninitialized or have a bogus value). Scoreboard holes + * (from the sack blocks received) are created later below + * (in the logic that adds holes to the tail of the + * scoreboard). + */ + tp->snd_fack = SEQ_MAX(tp->snd_una, th_ack); + /* + * In the while-loop below, incoming SACK blocks (sack_blocks[]) and + * SACK holes (snd_holes) are traversed from their tails with just + * one pass in order to reduce the number of compares especially when + * the bandwidth-delay product is large. + * + * Note: Typically, in the first RTT of SACK recovery, the highest + * three or four SACK blocks with the same ack number are received. + * In the second RTT, if retransmitted data segments are not lost, + * the highest three or four SACK blocks with ack number advancing + * are received. + */ + sblkp = &sack_blocks[num_sack_blks - 1]; /* Last SACK block */ + tp->sackhint.last_sack_ack = sblkp->end; + if (SEQ_LT(tp->snd_fack, sblkp->start)) { + /* + * The highest SACK block is beyond fack. Append new SACK + * hole at the tail. If the second or later highest SACK + * blocks are also beyond the current fack, they will be + * inserted by way of hole splitting in the while-loop below. + */ + temp = tcp_sackhole_insert(tp, tp->snd_fack,sblkp->start,NULL); + if (temp != NULL) { + tp->snd_fack = sblkp->end; + /* Go to the previous sack block. */ + sblkp--; + } else { + /* + * We failed to add a new hole based on the current + * sack block. Skip over all the sack blocks that + * fall completely to the right of snd_fack and + * proceed to trim the scoreboard based on the + * remaining sack blocks. This also trims the + * scoreboard for th_ack (which is sack_blocks[0]). + */ + while (sblkp >= sack_blocks && + SEQ_LT(tp->snd_fack, sblkp->start)) + sblkp--; + if (sblkp >= sack_blocks && + SEQ_LT(tp->snd_fack, sblkp->end)) + tp->snd_fack = sblkp->end; + } + } else if (SEQ_LT(tp->snd_fack, sblkp->end)) + /* fack is advanced. */ + tp->snd_fack = sblkp->end; + /* We must have at least one SACK hole in scoreboard. */ + KASSERT(!TAILQ_EMPTY(&tp->snd_holes), + ("SACK scoreboard must not be empty")); + cur = TAILQ_LAST(&tp->snd_holes, sackhole_head); /* Last SACK hole. */ + /* + * Since the incoming sack blocks are sorted, we can process them + * making one sweep of the scoreboard. + */ + while (sblkp >= sack_blocks && cur != NULL) { + if (SEQ_GEQ(sblkp->start, cur->end)) { + /* + * SACKs data beyond the current hole. Go to the + * previous sack block. + */ + sblkp--; + continue; + } + if (SEQ_LEQ(sblkp->end, cur->start)) { + /* + * SACKs data before the current hole. Go to the + * previous hole. + */ + cur = TAILQ_PREV(cur, sackhole_head, scblink); + continue; + } + tp->sackhint.sack_bytes_rexmit -= (cur->rxmit - cur->start); + KASSERT(tp->sackhint.sack_bytes_rexmit >= 0, + ("sackhint bytes rtx >= 0")); + if (SEQ_LEQ(sblkp->start, cur->start)) { + /* Data acks at least the beginning of hole. */ + if (SEQ_GEQ(sblkp->end, cur->end)) { + /* Acks entire hole, so delete hole. */ + temp = cur; + cur = TAILQ_PREV(cur, sackhole_head, scblink); + tcp_sackhole_remove(tp, temp); + /* + * The sack block may ack all or part of the + * next hole too, so continue onto the next + * hole. + */ + continue; + } else { + /* Move start of hole forward. */ + cur->start = sblkp->end; + cur->rxmit = SEQ_MAX(cur->rxmit, cur->start); + } + } else { + /* Data acks at least the end of hole. */ + if (SEQ_GEQ(sblkp->end, cur->end)) { + /* Move end of hole backward. */ + cur->end = sblkp->start; + cur->rxmit = SEQ_MIN(cur->rxmit, cur->end); + } else { + /* + * ACKs some data in middle of a hole; need + * to split current hole + */ + temp = tcp_sackhole_insert(tp, sblkp->end, + cur->end, cur); + if (temp != NULL) { + if (SEQ_GT(cur->rxmit, temp->rxmit)) { + temp->rxmit = cur->rxmit; + tp->sackhint.sack_bytes_rexmit + += (temp->rxmit + - temp->start); + } + cur->end = sblkp->start; + cur->rxmit = SEQ_MIN(cur->rxmit, + cur->end); + } + } + } + tp->sackhint.sack_bytes_rexmit += (cur->rxmit - cur->start); + /* + * Testing sblkp->start against cur->start tells us whether + * we're done with the sack block or the sack hole. + * Accordingly, we advance one or the other. + */ + if (SEQ_LEQ(sblkp->start, cur->start)) + cur = TAILQ_PREV(cur, sackhole_head, scblink); + else + sblkp--; + } +} + +/* + * Free all SACK holes to clear the scoreboard. + */ +void +tcp_free_sackholes(struct tcpcb *tp) +{ + struct sackhole *q; + + while ((q = TAILQ_FIRST(&tp->snd_holes)) != NULL) + tcp_sackhole_remove(tp, q); + tp->sackhint.sack_bytes_rexmit = 0; + + KASSERT(tp->snd_numholes == 0, ("tp->snd_numholes == 0")); + KASSERT(tp->sackhint.nexthole == NULL, + ("tp->sackhint.nexthole == NULL")); +} + +/* + * Partial ack handling within a sack recovery episode. Keeping this very + * simple for now. When a partial ack is received, force snd_cwnd to a value + * that will allow the sender to transmit no more than 2 segments. If + * necessary, a better scheme can be adopted at a later point, but for now, + * the goal is to prevent the sender from bursting a large amount of data in + * the midst of sack recovery. + */ +void +tcp_sack_partialack(struct tcpcb *tp, struct tcphdr *th) +{ + int num_segs = 1; + + tcp_timer_activate(tp, TT_REXMT, 0); + tp->t_rtttime = 0; + /* Send one or 2 segments based on how much new data was acked. */ + if ((BYTES_THIS_ACK(tp, th) / tp->t_maxseg) >= 2) + num_segs = 2; + tp->snd_cwnd = (tp->sackhint.sack_bytes_rexmit + + (tp->snd_nxt - tp->sack_newdata) + num_segs * tp->t_maxseg); + if (tp->snd_cwnd > tp->snd_ssthresh) + tp->snd_cwnd = tp->snd_ssthresh; + tp->t_flags |= TF_ACKNOW; + (void) tcp_output(tp); +} + +/* + * samkumar: Removed this function for now, but I left it in as a comment + * (using #if 0) in case it is useful later for debugging. + */ +#if 0 +/* + * Debug version of tcp_sack_output() that walks the scoreboard. Used for + * now to sanity check the hint. + */ +static struct sackhole * +tcp_sack_output_debug(struct tcpcb *tp, int *sack_bytes_rexmt) +{ + struct sackhole *p; + + INP_WLOCK_ASSERT(tp->t_inpcb); + *sack_bytes_rexmt = 0; + TAILQ_FOREACH(p, &tp->snd_holes, scblink) { + if (SEQ_LT(p->rxmit, p->end)) { + if (SEQ_LT(p->rxmit, tp->snd_una)) {/* old SACK hole */ + continue; + } + *sack_bytes_rexmt += (p->rxmit - p->start); + break; + } + *sack_bytes_rexmt += (p->rxmit - p->start); + } + return (p); +} +#endif + +/* + * Returns the next hole to retransmit and the number of retransmitted bytes + * from the scoreboard. We store both the next hole and the number of + * retransmitted bytes as hints (and recompute these on the fly upon SACK/ACK + * reception). This avoids scoreboard traversals completely. + * + * The loop here will traverse *at most* one link. Here's the argument. For + * the loop to traverse more than 1 link before finding the next hole to + * retransmit, we would need to have at least 1 node following the current + * hint with (rxmit == end). But, for all holes following the current hint, + * (start == rxmit), since we have not yet retransmitted from them. + * Therefore, in order to traverse more 1 link in the loop below, we need to + * have at least one node following the current hint with (start == rxmit == + * end). But that can't happen, (start == end) means that all the data in + * that hole has been sacked, in which case, the hole would have been removed + * from the scoreboard. + */ +struct sackhole * +tcp_sack_output(struct tcpcb *tp, int *sack_bytes_rexmt) +{ + struct sackhole *hole = NULL; + + *sack_bytes_rexmt = tp->sackhint.sack_bytes_rexmit; + hole = tp->sackhint.nexthole; + if (hole == NULL || SEQ_LT(hole->rxmit, hole->end)) + goto out; + while ((hole = TAILQ_NEXT(hole, scblink)) != NULL) { + if (SEQ_LT(hole->rxmit, hole->end)) { + tp->sackhint.nexthole = hole; + break; + } + } +out: + return (hole); +} + +/* + * After a timeout, the SACK list may be rebuilt. This SACK information + * should be used to avoid retransmitting SACKed data. This function + * traverses the SACK list to see if snd_nxt should be moved forward. + */ +void +tcp_sack_adjust(struct tcpcb *tp) +{ + struct sackhole *p, *cur = TAILQ_FIRST(&tp->snd_holes); + + if (cur == NULL) + return; /* No holes */ + if (SEQ_GEQ(tp->snd_nxt, tp->snd_fack)) + return; /* We're already beyond any SACKed blocks */ + /*- + * Two cases for which we want to advance snd_nxt: + * i) snd_nxt lies between end of one hole and beginning of another + * ii) snd_nxt lies between end of last hole and snd_fack + */ + while ((p = TAILQ_NEXT(cur, scblink)) != NULL) { + if (SEQ_LT(tp->snd_nxt, cur->end)) + return; + if (SEQ_GEQ(tp->snd_nxt, p->start)) + cur = p; + else { + tp->snd_nxt = p->start; + return; + } + } + if (SEQ_LT(tp->snd_nxt, cur->end)) + return; + tp->snd_nxt = tp->snd_fack; +} diff --git a/third_party/tcplp/bsdtcp/tcp_seq.h b/third_party/tcplp/bsdtcp/tcp_seq.h new file mode 100644 index 000000000..09cbb1d29 --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_seq.h @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 1982, 1986, 1993, 1995 + * 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_seq.h 8.3 (Berkeley) 6/21/95 + * $FreeBSD$ + */ + +#ifndef _NETINET_TCP_SEQ_H_ +#define _NETINET_TCP_SEQ_H_ + +#include "../tcplp.h" + +/* + * TCP sequence numbers are 32 bit integers operated + * on with modular arithmetic. These macros can be + * used to compare such integers. + */ +#define SEQ_LT(a,b) ((int)((a)-(b)) < 0) +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define SEQ_GT(a,b) ((int)((a)-(b)) > 0) +#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0) + +#define SEQ_MIN(a, b) ((SEQ_LT(a, b)) ? (a) : (b)) +#define SEQ_MAX(a, b) ((SEQ_GT(a, b)) ? (a) : (b)) + +/* for modulo comparisons of timestamps */ +#define TSTMP_LT(a,b) ((int)((a)-(b)) < 0) +#define TSTMP_GT(a,b) ((int)((a)-(b)) > 0) +#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0) + +/* + * Macros to initialize tcp sequence numbers for + * send and receive from initial send and receive + * sequence numbers. + */ +#define tcp_rcvseqinit(tp) \ + (tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1 + +#define tcp_sendseqinit(tp) \ + (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = \ + (tp)->snd_recover = (tp)->iss + +/* + * Clock macros for RFC 1323 timestamps. + */ +#define TCP_TS_TO_TICKS(_t) ((_t) * hz / 1000) + +/* Timestamp wrap-around time, 24 days. */ +#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * 1000) + +/* + * tcp_ts_getticks() in ms, should be 1ms < x < 1000ms according to RFC 1323. + * We always use 1ms granularity independent of hz. + */ +static __inline uint32_t +tcp_ts_getticks(void) +{ + /* samkumar: This used to be implemented using getmicrouptime(). */ + return tcplp_sys_get_millis(); +} + +#endif /* _NETINET_TCP_SEQ_H_ */ diff --git a/third_party/tcplp/bsdtcp/tcp_subr.c b/third_party/tcplp/bsdtcp/tcp_subr.c new file mode 100644 index 000000000..0774b478b --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_subr.c @@ -0,0 +1,374 @@ +/*- + * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995 + * 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_subr.c 8.2 (Berkeley) 5/24/95 + */ + +#include +#include +#include + +#include "../tcplp.h" +#include "ip.h" +#include "ip6.h" +#include "tcp.h" +#include "tcp_fsm.h" +#include "tcp_var.h" +#include "tcp_seq.h" +#include "tcp_timer.h" +#include "../lib/bitmap.h" +#include "../lib/cbuf.h" +#include "cc.h" + +#include "tcp_const.h" + +/* samkumar: Eventually, replace this with OpenThread's random generator. */ +// A simple linear congruential number generator +tcp_seq seed = (tcp_seq) 0xbeaddeed; +tcp_seq tcp_new_isn(struct tcpcb* tp) { + seed = (((tcp_seq) 0xfaded011) * seed) + (tcp_seq) 0x1ead1eaf; + return seed; +} + +/* + * samkumar: There used to be a function, void tcp_init(void), that would + * initialize global state for TCP, including a hash table to store TCBs, + * allocating memory zones for sockets, and setting global configurable state. + * None of that is needed for TCPlp: TCB allocation and matching is done by + * the host system and global configurable state is removed with hardcoded + * values in order to save memory, for example. Thus, I've removed the function + * entirely. + */ + +/* + * A subroutine which makes it easy to track TCP state changes with DTrace. + * This function shouldn't be called for t_state initializations that don't + * correspond to actual TCP state transitions. + */ +void +tcp_state_change(struct tcpcb *tp, int newstate) +{ +#if 0 +#if defined(KDTRACE_HOOKS) + int pstate = tp->t_state; +#endif +#endif + tcplp_sys_log("Socket %p: %s --> %s\n", tp, tcpstates[tp->t_state], tcpstates[newstate]); + tp->t_state = newstate; + + // samkumar: may need to do other actions too, so call into the host + tcplp_sys_on_state_change(tp, newstate); +#if 0 + TCP_PROBE6(state__change, NULL, tp, NULL, tp, NULL, pstate); +#endif +} + + /* samkumar: Based on tcp_newtcb in tcp_subr.c, and tcp_usr_attach in tcp_usrreq.c. */ +__attribute__((used)) void initialize_tcb(struct tcpcb* tp) { + uint32_t ticks = tcplp_sys_get_ticks(); + + /* samkumar: Clear all fields starting laddr; rest are initialized by the host. */ + memset(((uint8_t*) tp) + offsetof(struct tcpcb, laddr), 0x00, sizeof(struct tcpcb) - offsetof(struct tcpcb, laddr)); + tp->reass_fin_index = -1; + + /* + * samkumar: Only New Reno congestion control is implemented at the moment, + * so there's no need to record the congestion control algorithm used for + * each TCB. + */ + // CC_ALGO(tp) = CC_DEFAULT(); + // tp->ccv->type = IPPROTO_TCP; + tp->ccv->ccvc.tcp = tp; + + /* + * samkumar: The original code used to choose a different constant + * depending on whether it's an IPv4 or IPv6 connection. In TCPlp, we + * unconditionally choose the IPv6 branch. + */ + tp->t_maxseg = tp->t_maxopd = +//#ifdef INET6 + /*isipv6 ? */V_tcp_v6mssdflt /*:*/ +//#endif /* INET6 */ + /*V_tcp_mssdflt*/; + + if (V_tcp_do_rfc1323) + tp->t_flags = (TF_REQ_SCALE|TF_REQ_TSTMP); + if (V_tcp_do_sack) + tp->t_flags |= TF_SACK_PERMIT; + TAILQ_INIT(&tp->snd_holes); + + /* + * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no + * rtt estimate. Set rttvar so that srtt + 4 * rttvar gives + * reasonable initial retransmit time. + */ + tp->t_srtt = TCPTV_SRTTBASE; + tp->t_rttvar = ((TCPTV_RTOBASE - TCPTV_SRTTBASE) << TCP_RTTVAR_SHIFT) / 4; + tp->t_rttmin = TCPTV_MIN < 1 ? 1 : TCPTV_MIN; /* samkumar: used to be tcp_rexmit_min, which was set in tcp_init */ + tp->t_rxtcur = TCPTV_RTOBASE; + tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT; + tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT; + tp->t_rcvtime = ticks; + + /* samkumar: Taken from tcp_usr_attach in tcp_usrreq.c. */ + tp->t_state = TCP6S_CLOSED; + + /* samkumar: Added to initialize the per-TCP sackhole pool. */ + tcp_sack_init(tp); +} + + +/* + * samkumar: Most of this function was no longer needed. It did things like + * reference-counting for the TCB, updating host cache stats for better + * starting values of, e.g., ssthresh, for new connections, freeing resources + * for TCP offloading, etc. There's no host cache in TCPlp and the host system + * is responsible for managing TCB memory, so much of this isn't needed. I just + * kept (and adpated) the few parts of the function that appeared to be needed + * for TCPlp. + */ +void +tcp_discardcb(struct tcpcb *tp) +{ + tcp_cancel_timers(tp); + + /* Allow the CC algorithm to clean up after itself. */ + if (CC_ALGO(tp)->cb_destroy != NULL) + CC_ALGO(tp)->cb_destroy(tp->ccv); + + tcp_free_sackholes(tp); +} + + + /* + * Attempt to close a TCP control block, marking it as dropped, and freeing + * the socket if we hold the only reference. + */ +/* + * samkumar: Most of this function has to do with dropping the reference to + * the inpcb, freeing resources at the socket layer and marking it as + * disconnected, and miscellaneous cleanup. I've rewritten this to do what is + * needed for TCP. + */ +struct tcpcb * +tcp_close(struct tcpcb *tp) +{ + tcp_state_change(tp, TCP6S_CLOSED); // for the print statement + tcp_discardcb(tp); + // Don't reset the TCB by calling initialize_tcb, since that overwrites the buffer contents. + return tp; +} + +/* + * Create template to be used to send tcp packets on a connection. + * Allocates an mbuf and fills in a skeletal tcp/ip header. The only + * use for this function is in keepalives, which use tcp_respond. + */ +/* samkumar: I changed the signature of this function. Instead of allocating + * the struct tcptemp using malloc, populating it, and then returning it, I + * have the caller allocate it. This function merely populates it now. + */ +void +tcpip_maketemplate(struct tcpcb* tp, struct tcptemp* t) +{ + tcpip_fillheaders(tp, (void *)&t->tt_ipgen, (void *)&t->tt_t); +} + +/* + * Fill in the IP and TCP headers for an outgoing packet, given the tcpcb. + * tcp_template used to store this data in mbufs, but we now recopy it out + * of the tcpcb each time to conserve mbufs. + */ +/* + * samkumar: This has a different signature from the original function in + * tcp_subr.c. In particular, IP header information is filled into an + * otMessageInfo rather than into a struct representing the on-wire header + * format. Additionally, I have changed it to always assume IPv6; I removed the + * code for IPv4. + */ +void +tcpip_fillheaders(struct tcpcb* tp, otMessageInfo* ip_ptr, void *tcp_ptr) +{ + struct tcphdr *th = (struct tcphdr *)tcp_ptr; + + /* Fill in the IP header */ + + /* samkumar: The old IPv6 code, for reference. */ + // ip6 = (struct ip6_hdr *)ip_ptr; + // ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | + // (inp->inp_flow & IPV6_FLOWINFO_MASK); + // ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | + // (IPV6_VERSION & IPV6_VERSION_MASK); + // ip6->ip6_nxt = IPPROTO_TCP; + // ip6->ip6_plen = htons(sizeof(struct tcphdr)); + // ip6->ip6_src = inp->in6p_laddr; + // ip6->ip6_dst = inp->in6p_faddr; + + memset(ip_ptr, 0x00, sizeof(otMessageInfo)); + memcpy(&ip_ptr->mSockAddr, &tp->laddr, sizeof(ip_ptr->mSockAddr)); + memcpy(&ip_ptr->mPeerAddr, &tp->faddr, sizeof(ip_ptr->mPeerAddr)); + + /* Fill in the TCP header */ + /* samkumar: I kept the old code for ports commented out, for reference. */ + //th->th_sport = inp->inp_lport; + //th->th_dport = inp->inp_fport; + th->th_sport = tp->lport; + th->th_dport = tp->fport; + th->th_seq = 0; + th->th_ack = 0; + th->th_x2 = 0; + th->th_off = 5; + th->th_flags = 0; + th->th_win = 0; + th->th_urp = 0; + th->th_sum = 0; /* in_pseudo() is called later for ipv4 */ +} + +/* + * Send a single message to the TCP at address specified by + * the given TCP/IP header. If m == NULL, then we make a copy + * of the tcpiphdr at th and send directly to the addressed host. + * This is used to force keep alive messages out using the TCP + * template for a connection. If flags are given then we send + * a message back to the TCP which originated the segment th, + * and discard the mbuf containing it and any other attached mbufs. + * + * In any case the ack and sequence number of the transmitted + * segment are as specified by the parameters. + * + * NOTE: If m != NULL, then th must point to *inside* the mbuf. + */ +/* samkumar: Original signature was +void +tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m, + tcp_seq ack, tcp_seq seq, int flags) +*/ +/* + * samkumar: Essentially all of the code had to be discarded/rewritten since I + * have to send out packets by allocating buffers from the host system, + * populating them, and passing them back to the host system to send out. I + * simplified the code by only using the logic that was fully necessary, + * eliminating the code for IPv4 packets and keeping only the code for IPv6 + * packets. I also removed all of the mbuf logic, instead using the logic for + * using the host system's buffering (in particular, the code to reuse the + * provided mbuf is no longer there). + */ +void +tcp_respond(struct tcpcb *tp, otInstance* instance, struct ip6_hdr* ip6gen, struct tcphdr *thgen, + tcp_seq ack, tcp_seq seq, int flags) +{ + otMessage* message = tcplp_sys_new_message(instance); + if (message == NULL) { + return; + } + if (otMessageSetLength(message, sizeof(struct tcphdr)) != OT_ERROR_NONE) { + tcplp_sys_free_message(instance, message); + return; + } + + struct tcphdr th; + struct tcphdr* nth = &th; + otMessageInfo ip6info; + int win = 0; + if (tp != NULL) { + if (!(flags & TH_RST)) { + win = cbuf_free_space(&tp->recvbuf); + if (win > (long)TCP_MAXWIN << tp->rcv_scale) + win = (long)TCP_MAXWIN << tp->rcv_scale; + } + } + memset(&ip6info, 0x00, sizeof(otMessageInfo)); + memcpy(&ip6info.mSockAddr, &ip6gen->ip6_dst, sizeof(ip6info.mSockAddr)); + memcpy(&ip6info.mPeerAddr, &ip6gen->ip6_src, sizeof(ip6info.mPeerAddr)); + nth->th_sport = thgen->th_dport; + nth->th_dport = thgen->th_sport; + nth->th_seq = htonl(seq); + nth->th_ack = htonl(ack); + nth->th_x2 = 0; + nth->th_off = sizeof(struct tcphdr) >> 2; + nth->th_flags = flags; + if (tp != NULL) + nth->th_win = htons((uint16_t) (win >> tp->rcv_scale)); + else + nth->th_win = htons((uint16_t)win); + nth->th_urp = 0; + nth->th_sum = 0; + + otMessageWrite(message, 0, &th, sizeof(struct tcphdr)); + + tcplp_sys_send_message(instance, message, &ip6info); +} + +/* + * Drop a TCP connection, reporting + * the specified error. If connection is synchronized, + * then send a RST to peer. + */ +/* + * samkumar: I changed the parameter "errno" to "errnum" since it caused + * problems during compilation. I also the code for asserting locks, + * incermenting stats, and managing the sockets layer. + */ +struct tcpcb * +tcp_drop(struct tcpcb *tp, int errnum) +{ + if (TCPS_HAVERCVDSYN(tp->t_state)) { + tcp_state_change(tp, TCPS_CLOSED); + (void) tcp_output(tp); + } + if (errnum == ETIMEDOUT && tp->t_softerror) + errnum = tp->t_softerror; + tp = tcp_close(tp); + tcplp_sys_connection_lost(tp, errnum); + return tp; +} + +/* + * Look-up the routing entry to the peer of this inpcb. If no route + * is found and it cannot be allocated, then return 0. This routine + * is called by TCP routines that access the rmx structure and by + * tcp_mss_update to get the peer/interface MTU. + */ +/* + * samkumar: In TCPlp, we don't bother with keeping track of the MTU for each + * route. The MSS we choose for the 6LoWPAN/802.15.4 network is probably the + * bottleneck, so we just use that. (I also removed the struct in_conninfo * + * that was formerly the first argument). + */ +uint64_t +tcp_maxmtu6(struct tcpcb* tp, struct tcp_ifcap *cap) +{ + uint64_t maxmtu = 0; + + KASSERT (tp != NULL, ("tcp_maxmtu6 with NULL tcpcb pointer")); + if (!IN6_IS_ADDR_UNSPECIFIED(&tp->faddr)) { + maxmtu = FRAMES_PER_SEG * FRAMECAP_6LOWPAN; + } + + return (maxmtu); +} diff --git a/third_party/tcplp/bsdtcp/tcp_timer.c b/third_party/tcplp/bsdtcp/tcp_timer.c new file mode 100644 index 000000000..a2737ceda --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_timer.c @@ -0,0 +1,489 @@ +/*- + * 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 +#include + +#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. + */ + +void +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\n")); + 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); +} + +void +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\n")); + 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; + +dropit: + tp = tcp_drop(tp, ETIMEDOUT); + (void) tp; /* samkumar: prevent a compiler warning */ +} + +void +tcp_timer_persist(struct tcpcb* tp) +{ + uint32_t ticks = tcplp_sys_get_ticks(); + + /* samkumar: I added this, to replace the code I removed below. */ + KASSERT(tpistimeractive(tp, TT_PERSIST), ("Persist timer running, but unmarked\n")); + 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); + 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); + goto out; + } + + tcp_setpersist(tp); + tp->t_flags |= TF_FORCEDATA; + tcplp_sys_log("Persist output: %zu bytes in sendbuf\n", 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; +} + +void +tcp_timer_2msl(struct tcpcb* tp) +{ + uint32_t ticks = tcplp_sys_get_ticks(); + + /* samkumar: I added this, to replace the code I removed below. */ + KASSERT(tpistimeractive(tp, TT_2MSL), ("2MSL timer running, but unmarked\n")); + 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); + return; + } + /* + * 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); + } 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); + } + } + /* + * samkumar: There used to be some code here that handled debug + * tracing/probes, vnet, and locking. I removed that code. + */ +} + +void +tcp_timer_rexmt(struct tcpcb *tp) +{ + int rexmt; + uint32_t ticks = tcplp_sys_get_ticks(); + + /* samkumar: I added this, to replace the code I removed below. */ + KASSERT(tpistimeractive(tp, TT_REXMT), ("Rexmt timer running, but unmarked\n")); + 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\n", (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); + 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; +} + +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)) { + char* msg = "TCP CRITICAL FAILURE: Retransmit and Persist timers are simultaneously running!\n"; + tcplp_sys_log("%s\n", msg); + } + 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); +} diff --git a/third_party/tcplp/bsdtcp/tcp_timer.h b/third_party/tcplp/bsdtcp/tcp_timer.h new file mode 100644 index 000000000..331ac242e --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_timer.h @@ -0,0 +1,180 @@ +/*- + * 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$ + */ + +/** + * samkumar: The FreeBSD Operating System uses its callout subsystem to + * implement TCP timers. I've removed the relevant definitions/declarations + * below, since TCPlp relies on the host system to implement TCP timers. To + * save memory, I've removed some configurability (e.g., per-TCB keepalive + * parameters) and global statistics (e.g. tcp_keepcnt). + */ + +#ifndef _NETINET_TCP_TIMER_H_ +#define _NETINET_TCP_TIMER_H_ + +#include "tcp_var.h" + +/* + * The TCPT_REXMT timer is used to force retransmissions. + * The TCP has the TCPT_REXMT timer set whenever segments + * have been sent for which ACKs are expected but not yet + * received. If an ACK is received which advances tp->snd_una, + * then the retransmit timer is cleared (if there are no more + * outstanding segments) or reset to the base value (if there + * are more ACKs expected). Whenever the retransmit timer goes off, + * we retransmit one unacknowledged segment, and do a backoff + * on the retransmit timer. + * + * The TCPT_PERSIST timer is used to keep window size information + * flowing even if the window goes shut. If all previous transmissions + * have been acknowledged (so that there are no retransmissions in progress), + * and the window is too small to bother sending anything, then we start + * the TCPT_PERSIST timer. When it expires, if the window is nonzero, + * we go to transmit state. Otherwise, at intervals send a single byte + * into the peer's window to force him to update our window information. + * We do this at most as often as TCPT_PERSMIN time intervals, + * but no more frequently than the current estimate of round-trip + * packet time. The TCPT_PERSIST timer is cleared whenever we receive + * a window update from the peer. + * + * The TCPT_KEEP timer is used to keep connections alive. If an + * connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time, + * but not yet established, then we drop the connection. Once the connection + * is established, if the connection is idle for TCPTV_KEEP_IDLE time + * (and keepalives have been enabled on the socket), we begin to probe + * the connection. We force the peer to send us a segment by sending: + * + * This segment is (deliberately) outside the window, and should elicit + * an ack segment in response from the peer. If, despite the TCPT_KEEP + * initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE + * amount of time probing, then we drop the connection. + */ + +#define TT_DELACK 0x0001 +#define TT_REXMT 0x0002 +#define TT_PERSIST 0x0004 +#define TT_KEEP 0x0008 +#define TT_2MSL 0x0010 + +/* + * Time constants. + */ +#define TCPTV_MSL ( 30*hz) /* max seg lifetime (hah!) */ +#define TCPTV_SRTTBASE 0 /* base roundtrip time; + if 0, no idea yet */ +#define TCPTV_RTOBASE ( 3*hz) /* assumed RTO if no info */ + +#define TCPTV_PERSMIN ( 5*hz) /* retransmit persistence */ +#define TCPTV_PERSMAX ( 60*hz) /* maximum persist interval */ + +#define TCPTV_KEEP_INIT ( 75*hz) /* initial connect keepalive */ +#define TCPTV_KEEP_IDLE (120*60*hz) /* dflt time before probing */ +#define TCPTV_KEEPINTVL ( 75*hz) /* default probe interval */ +#define TCPTV_KEEPCNT 8 /* max probes before drop */ + +#define TCPTV_FINWAIT2_TIMEOUT (60*hz) /* FIN_WAIT_2 timeout if no receiver */ + +/* + * Minimum retransmit timer is 3 ticks, for algorithmic stability. + * TCPT_RANGESET() will add another TCPTV_CPU_VAR to deal with + * the expected worst-case processing variances by the kernels + * representing the end points. Such variances do not always show + * up in the srtt because the timestamp is often calculated at + * the interface rather then at the TCP layer. This value is + * typically 50ms. However, it is also possible that delayed + * acks (typically 100ms) could create issues so we set the slop + * to 200ms to try to cover it. Note that, properly speaking, + * delayed-acks should not create a major issue for interactive + * environments which 'P'ush the last segment, at least as + * long as implementations do the required 'at least one ack + * for every two packets' for the non-interactive streaming case. + * (maybe the RTO calculation should use 2*RTT instead of RTT + * to handle the ack-every-other-packet case). + * + * The prior minimum of 1*hz (1 second) badly breaks throughput on any + * networks faster then a modem that has minor (e.g. 1%) packet loss. + */ +#define TCPTV_MIN ( hz/33 ) /* minimum allowable value */ +#define TCPTV_CPU_VAR ( hz/5 ) /* cpu variance allowed (200ms) */ +#define TCPTV_REXMTMAX ( 64*hz) /* max allowable REXMT value */ + +#define TCPTV_TWTRUNC 8 /* RTO factor to truncate TW */ + +#define TCP_LINGERTIME 120 /* linger at most 2 minutes */ + +#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */ + +#define TCPTV_DELACK ( hz/10 ) /* 100ms timeout */ + +#ifdef TCPTIMERS +static const char *tcptimers[] = + { "REXMT", "PERSIST", "KEEP", "2MSL", "DELACK" }; +#endif + +int tcp_timer_active(struct tcpcb *tp, uint32_t timer_type); +void tcp_timer_activate(struct tcpcb *tp, uint32_t timer_type, uint32_t delta); +void tcp_cancel_timers(struct tcpcb* tp); + +/* I moved the definition of TCPT_RANGESET to tcp_const.h. */ + +static const int tcp_syn_backoff[TCP_MAXRXTSHIFT + 1] = + { 1, 1, 1, 1, 1, 2, 4, 8, 16, 32, 64, 64, 64 }; + +static const int tcp_backoff[TCP_MAXRXTSHIFT + 1] = + { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 512, 512, 512 }; + +static const int tcp_totbackoff = 2559; /* sum of tcp_backoff[] */ + +void tcp_timer_delack(struct tcpcb* tp); +void tcp_timer_keep(struct tcpcb* tp); +void tcp_timer_persist(struct tcpcb* tp); +void tcp_timer_2msl(struct tcpcb* tp); +void tcp_timer_rexmt(struct tcpcb *tp); +int tcp_timer_active(struct tcpcb *tp, uint32_t timer_type); + +/* + * samkumar: Modified to use default keepalive parameters, since we removed + * the fields from tcpcb that allow them to be set on a per-connection basis. + */ +#define TP_KEEPINIT(tp) (/*(tp)->t_keepinit ? (tp)->t_keepinit :*/ tcp_keepinit) +#define TP_KEEPIDLE(tp) (/*(tp)->t_keepidle ? (tp)->t_keepidle :*/ tcp_keepidle) +#define TP_KEEPINTVL(tp) (/*(tp)->t_keepintvl ? (tp)->t_keepintvl :*/ tcp_keepintvl) +#define TP_KEEPCNT(tp) (/*(tp)->t_keepcnt ? (tp)->t_keepcnt :*/ tcp_keepcnt) +#define TP_MAXIDLE(tp) (TP_KEEPCNT(tp) * TP_KEEPINTVL(tp)) + +/* + * samkumar: MOVED NECESSARY EXTERN DECLARATIONS TO TCP_SUBR.C + * Removed timer declarations that aren't needed since timers are handled + * by the host system (in this case, OpenThread), in TCPlp. + */ + +#endif /* !_NETINET_TCP_TIMER_H_ */ diff --git a/third_party/tcplp/bsdtcp/tcp_timewait.c b/third_party/tcplp/bsdtcp/tcp_timewait.c new file mode 100644 index 000000000..07e2c9ecc --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_timewait.c @@ -0,0 +1,396 @@ +/*- + * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995 + * 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_subr.c 8.2 (Berkeley) 5/24/95 + */ + +#include + +#include "tcp.h" +#include "tcp_fsm.h" +#include "tcp_seq.h" +#include "tcp_timer.h" +#include "tcp_var.h" + +#include "tcp_const.h" +#include +#include + +/* + * samkumar: The V_nolocaltimewait variable corresponds to the + * net.inet.tcp.nolocaltimewait option in FreeBSD. When set to 1, it skips the + * TIME-WAIT state for TCP connections where both endpoints are local IP + * addresses, to save resources on HTTP accelerators, database servers/clients, + * etc. In TCPlp, I eliminated support for this feature, but I have kept the + * code for it, commented out with "#if 0", in case we choose to bring it back + * at a later time. + * + * See also the "#if 0" block in tcp_twstart. + */ +#if 0 +enum tcp_timewait_consts { + V_nolocaltimewait = 0 +}; +#endif + +/* + * samkumar: The FreeBSD code used a separate, smaller structure, called + * struct tcptw, to respresent connections in the TIME-WAIT state. In TCPlp, + * we use the full struct tcpcb structure even in the TIME-WAIT state. This + * consumes more memory, but switching to a different structure like + * struct tcptw to save memory would be difficult because the host system or + * application has allocated these structures; we can't simply "free" the + * struct tcpcb. It would have to have been done via a callback or something, + * and in the common case of statically allocated sockets, this would actually + * result in more memory (since an application would need to allocate both the + * struct tcpcb and the struct tcptw, if it uses a static allocation approach). + * + * Below, I've changed the function signatures to accept "struct tcpcb* tp" + * instead of "struct tcptw *tw" and I have reimplemented the functions + * to work using tp (of type struct tcpcb) instead of tw (of type + * struct tcptw). + * + * Conceptually, the biggest change is in how timers are handled. The FreeBSD + * code had a 2MSL timer, which was set for sockets that enter certain + * "closing" states of the TCP state machine. But when the TIME-WAIT state was + * entered, the state is transferred from struct tcpcb into struct tcptw. + * The final timeout is handled as follows; the function tcp_tw_2msl_scan is + * called periodically on the slow timer, and it iterates over a linked list + * of all the struct tcptw and checks the tw->tw_time field to identify which + * TIME-WAIT sockets have expired. + * + * In our switch to using struct tcpcb even in the TIME-WAIT state, we rely on + * the timer system for struct tcpcb. I modified the 2msl callback in + * tcp_timer.c to check for the TIME-WAIT case and handle it correctly. + */ + +static void +tcp_tw_2msl_reset(struct tcpcb* tp, int rearm) +{ + /* + * samkumar: This function used to set tw->tw_time to ticks + 2 * tcp_msl + * and insert tw into the linked list V_twq_2msl. I've replaced this, along + * with the associated locking logic, with the following call, which uses + * the timer system in place for full TCBs. + */ + tcp_timer_activate(tp, TT_2MSL, 2 * tcp_msl); +} + +/* + * samkumar: I've rewritten this code since I need to send out packets via the + * host system for TCPlp: allocating buffers from the host system, populate + * them, and then pass them back to the host system. I simplified the code by + * only using the logic that was fully necessary, eliminating the code for IPv4 + * packets and keeping only the code for IPv6 packets. I also removed all of + * the mbuf logic, instead using the logic for using the host system's + * buffering. + * + * This rewritten code always returns 0. The original code would return + * whatever is returned by ip_output or ip6_output (FreeBSD's functions for + * sending out IP packets). I believe 0 indicates success, and a nonzero + * value represents an error code. It seems that the return value of + * tcp_twrespond is ignored by all instances of its use in TCPlp (maybe even + * in all of FreeBSD), so this is a moot point. + */ +static int +tcp_twrespond(struct tcpcb* tp, int flags) +{ + struct tcphdr* nth; + struct tcpopt to; + uint32_t optlen = 0; + uint8_t opt[TCP_MAXOLEN]; + + to.to_flags = 0; + + /* + * Send a timestamp and echo-reply if both our side and our peer + * have sent timestamps in our SYN's and this is not a RST. + */ + if ((tp->t_flags & TF_RCVD_TSTMP) && flags == TH_ACK) { + to.to_flags |= TOF_TS; + to.to_tsval = tcp_ts_getticks() + tp->ts_offset; + to.to_tsecr = tp->ts_recent; + } + optlen = tcp_addoptions(&to, opt); + + otMessage* message = tcplp_sys_new_message(tp->instance); + if (message == NULL) { + return 0; // drop the message + } + if (otMessageSetLength(message, sizeof(struct tcphdr) + optlen) != OT_ERROR_NONE) { + tcplp_sys_free_message(tp->instance, message); + return 0; // drop the message + } + + char outbuf[sizeof(struct tcphdr) + optlen]; + nth = (struct tcphdr*) &outbuf[0]; + otMessageInfo ip6info; + memset(&ip6info, 0x00, sizeof(ip6info)); + + memcpy(&ip6info.mSockAddr, &tp->laddr, sizeof(ip6info.mSockAddr)); + memcpy(&ip6info.mPeerAddr, &tp->faddr, sizeof(ip6info.mPeerAddr)); + nth->th_sport = tp->lport; + nth->th_dport = tp->fport; + nth->th_seq = htonl(tp->snd_nxt); + nth->th_ack = htonl(tp->rcv_nxt); + nth->th_x2 = 0; + nth->th_off = (sizeof(struct tcphdr) + optlen) >> 2; + nth->th_flags = flags; + nth->th_win = htons(tp->tw_last_win); + nth->th_urp = 0; + nth->th_sum = 0; + + memcpy(nth + 1, opt, optlen); + otMessageWrite(message, 0, outbuf, sizeof(struct tcphdr) + optlen); + tcplp_sys_send_message(tp->instance, message, &ip6info); + + return 0; +} + +/* + * Move a TCP connection into TIME_WAIT state. + * tcbinfo is locked. + * inp is locked, and is unlocked before returning. + */ +/* + * samkumar: Locking is removed (so above comments regarding locks are no + * not relevant for TCPlp). Rather than allocating a struct tcptw and + * discarding the struct tcpcb, this function just switches the tcpcb state + * to correspond to TIME-WAIT (updating variables as appropriate). We also + * eliminate the "V_nolocaltimewait" optimization. + */ +void +tcp_twstart(struct tcpcb *tp) +{ + int acknow; + + /* + * samkumar: The following code, commented out using "#if 0", handles the + * net.inet.tcp.nolocaltimewait option in FreeBSD. The option skips the + * TIME-WAIT state for TCP connections where both endpoints are local. + * I'm removing this optimization for TCPlp, but I've left the code + * commented out as it's a potentially useful feature that we may choose + * to restore later. + * + * See also the "#if 0" block near the top of this file. + */ +#if 0 + if (V_nolocaltimewait) { + int error = 0; +#ifdef INET6 + if (isipv6) + error = in6_localaddr(&inp->in6p_faddr); +#endif +#if defined(INET6) && defined(INET) + else +#endif +#ifdef INET + error = in_localip(inp->inp_faddr); +#endif + if (error) { + tp = tcp_close(tp); + if (tp != NULL) + INP_WUNLOCK(inp); + return; + } + } +#endif + + /* + * For use only by DTrace. We do not reference the state + * after this point so modifying it in place is not a problem. + */ + /* + * samkumar: The above comment is not true anymore. I use this state, since + * I don't associate every struct tcpcb with a struct inpcb. + */ + tcp_state_change(tp, TCPS_TIME_WAIT); + + /* + * samkumar: There used to be code here to allocate a struct tcptw + * using "tw = uma_zalloc(V_tcptw_zone, M_NOWAIT);" and if it fails, close + * an existing TIME-WAIT connection, in LRU fashion, to allocate memory. + */ + + /* + * Recover last window size sent. + */ + if (SEQ_GT(tp->rcv_adv, tp->rcv_nxt)) + tp->tw_last_win = (tp->rcv_adv - tp->rcv_nxt) >> tp->rcv_scale; + else + tp->tw_last_win = 0; + + /* + * Set t_recent if timestamps are used on the connection. + */ + if ((tp->t_flags & (TF_REQ_TSTMP|TF_RCVD_TSTMP|TF_NOOPT)) == + (TF_REQ_TSTMP|TF_RCVD_TSTMP)) { + /* + * samkumar: This used to do: + * tw->t_recent = tp->ts_recent; + * tw->ts_offset = tp->ts_offset; + * But since we're keeping the state in tp, we don't need to do this + * anymore. */ + } else { + tp->ts_recent = 0; + tp->ts_offset = 0; + } + + /* + * samkumar: There used to be code here to populate various fields in + * tw based on their values in tp, but there's no need for that now since + * we can just read the values from tp. tw->tw_time was set to 0, but we + * don't need to do that either since we're relying on the old timer system + * anyway. + */ + +/* XXX + * If this code will + * be used for fin-wait-2 state also, then we may need + * a ts_recent from the last segment. + */ + acknow = tp->t_flags & TF_ACKNOW; + + /* + * First, discard tcpcb state, which includes stopping its timers and + * freeing it. tcp_discardcb() used to also release the inpcb, but + * that work is now done in the caller. + * + * Note: soisdisconnected() call used to be made in tcp_discardcb(), + * and might not be needed here any longer. + */ + /* + * samkumar: Below, I removed the code to discard tp, update inpcb and + * release a reference to socket, but kept the rest. I also added a call + * to cancel any pending timers on the TCB (which discarding it, as the + * original code did, would have done). + */ + tcp_cancel_timers(tp); + if (acknow) + tcp_twrespond(tp, TH_ACK); + tcp_tw_2msl_reset(tp, 0); +} + +/* + * Returns 1 if the TIME_WAIT state was killed and we should start over, + * looking for a pcb in the listen state. Returns 0 otherwise. + */ +/* + * samkumar: Old signature was + * int + * tcp_twcheck(struct inpcb *inp, struct tcpopt *to, struct tcphdr *th, + * struct mbuf *m, int tlen) + */ +int +tcp_twcheck(struct tcpcb* tp, struct tcphdr *th, int tlen) +{ + int thflags; + tcp_seq seq; + + /* + * samkumar: There used to be code here that obtains the struct tcptw from + * the inpcb, and does "goto drop" if that fails. + */ + + thflags = th->th_flags; + + /* + * NOTE: for FIN_WAIT_2 (to be added later), + * must validate sequence number before accepting RST + */ + + /* + * If the segment contains RST: + * Drop the segment - see Stevens, vol. 2, p. 964 and + * RFC 1337. + */ + if (thflags & TH_RST) + goto drop; + + /* + * samkumar: This was commented out (using #if 0) in the original FreeBSD + * code. + */ +#if 0 +/* PAWS not needed at the moment */ + /* + * RFC 1323 PAWS: If we have a timestamp reply on this segment + * and it's less than ts_recent, drop it. + */ + if ((to.to_flags & TOF_TS) != 0 && tp->ts_recent && + TSTMP_LT(to.to_tsval, tp->ts_recent)) { + if ((thflags & TH_ACK) == 0) + goto drop; + goto ack; + } + /* + * ts_recent is never updated because we never accept new segments. + */ +#endif + + /* + * If a new connection request is received + * while in TIME_WAIT, drop the old connection + * and start over if the sequence numbers + * are above the previous ones. + */ + if ((thflags & TH_SYN) && SEQ_GT(th->th_seq, tp->rcv_nxt)) { + /* + * samkumar: The FreeBSD code would call tcp_twclose(tw, 0); but we + * do it as below since TCPlp represents TIME-WAIT connects as + * struct tcpcb's. + */ + tcp_close(tp); + tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL); + return (1); + } + + /* + * Drop the segment if it does not contain an ACK. + */ + if ((thflags & TH_ACK) == 0) + goto drop; + + /* + * Reset the 2MSL timer if this is a duplicate FIN. + */ + if (thflags & TH_FIN) { + seq = th->th_seq + tlen + (thflags & TH_SYN ? 1 : 0); + if (seq + 1 == tp->rcv_nxt) + tcp_tw_2msl_reset(tp, 1); + } + + /* + * Acknowledge the segment if it has data or is not a duplicate ACK. + */ + if (thflags != TH_ACK || tlen != 0 || + th->th_seq != tp->rcv_nxt || th->th_ack != tp->snd_nxt) + tcp_twrespond(tp, TH_ACK); +drop: + return (0); +} diff --git a/third_party/tcplp/bsdtcp/tcp_usrreq.c b/third_party/tcplp/bsdtcp/tcp_usrreq.c new file mode 100644 index 000000000..b7e8695c1 --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_usrreq.c @@ -0,0 +1,510 @@ +/*- + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. + * Copyright (c) 2006-2007 Robert N. M. Watson + * Copyright (c) 2010-2011 Juniper Networks, Inc. + * All rights reserved. + * + * Portions of this software were developed by Robert N. M. Watson under + * contract to Juniper Networks, Inc. + * + * 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. + * + * From: @(#)tcp_usrreq.c 8.2 (Berkeley) 1/3/94 + */ + +#include +#include +#include "../tcplp.h" +#include "../lib/cbuf.h" +#include "tcp.h" +#include "tcp_fsm.h" +#include "tcp_seq.h" +#include "tcp_var.h" +#include "tcp_timer.h" +//#include +#include "ip6.h" + +#include "tcp_const.h" + +#include + +//static void tcp_disconnect(struct tcpcb *); +static void tcp_usrclosed(struct tcpcb *); + +/* + * samkumar: Removed tcp6_usr_bind, since checking if an address/port is free + * needs to be done at the host system (along with other socket management + * duties). TCPlp doesn't know what other sockets are in the system, or which + * other addresses/ports are busy. + */ + +/* samkumar: This is based on a function in in6_pcb.c. */ +static int in6_pcbconnect(struct tcpcb* tp, struct sockaddr_in6* nam) { + register struct sockaddr_in6 *sin6 = nam; + tp->faddr = sin6->sin6_addr; + tp->fport = sin6->sin6_port; + return 0; +} + +/* + * Initiate connection to peer. + * Create a template for use in transmissions on this connection. + * Enter SYN_SENT state, and mark socket as connecting. + * Start keep-alive timer, and seed output sequence space. + * Send initial segment on connection. + */ +/* + * samkumar: I removed locking, statistics, and inpcb management. The signature + * used to be + * + * static int + * tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) + */ +static int +tcp6_connect(struct tcpcb *tp, struct sockaddr_in6 *nam) +{ + int error; + + int sb_max = cbuf_free_space(&tp->recvbuf); // same as sendbuf + + /* + * samkumar: For autobind, the original BSD code assigned the port first + * (with logic that also looked at the address) and then the address. This + * was done by calling into other parts of the FreeBSD network stack, + * outside of the TCP stack. Here, we just use the tcplp_sys_autobind + * function to do all of that work. + */ + bool autobind_addr = IN6_IS_ADDR_UNSPECIFIED(&tp->laddr); + bool autobind_port = (tp->lport == 0); + if (autobind_addr || autobind_port) { + otSockAddr foreign; + otSockAddr local; + + memcpy(&foreign.mAddress, &nam->sin6_addr, sizeof(foreign.mAddress)); + foreign.mPort = ntohs(nam->sin6_port); + + if (!autobind_addr) { + memcpy(&local.mAddress, &tp->laddr, sizeof(local.mAddress)); + } + + if (!autobind_port) { + local.mPort = ntohs(tp->lport); + } + + if (!tcplp_sys_autobind(tp->instance, &foreign, &local, autobind_addr, autobind_port)) { + // Autobind failed + error = EINVAL; + goto out; + } + + if (autobind_addr) { + memcpy(&tp->laddr, &local.mAddress, sizeof(tp->laddr)); + } + + if (autobind_port) { + tp->lport = htons(local.mPort); + } + } + error = in6_pcbconnect(tp, nam); + if (error != 0) + goto out; + + /* Compute window scaling to request. */ + while (tp->request_r_scale < TCP_MAX_WINSHIFT && + (TCP_MAXWIN << tp->request_r_scale) < sb_max) + tp->request_r_scale++; + + tcp_state_change(tp, TCPS_SYN_SENT); + tp->iss = tcp_new_isn(tp); + tcp_sendseqinit(tp); + + return 0; + +out: + return error; +} + +/* + * samkumar: I removed locking, statistics, inpcb management, and debug probes. + * I also remove codepaths that check for IPv6, since the address is assumed to + * be IPv6. The signature used to be + * + * static int + * tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) + */ +int +tcp6_usr_connect(struct tcpcb* tp, struct sockaddr_in6* sin6p) +{ + int error = 0; + + if (tp->t_state != TCPS_CLOSED) { // samkumar: This is a check that I added + return (EISCONN); + } + + /* samkumar: I removed the following error check since we receive sin6p + * in the function argument and don't need to convert a struct sockaddr to + * a struct sockaddr_in6 anymore. + * + * if (nam->sa_len != sizeof (*sin6p)) + * return (EINVAL); + */ + + /* + * Must disallow TCP ``connections'' to multicast addresses. + */ + /* samkumar: I commented out the check on sin6p->sin6_family. */ + if (/*sin6p->sin6_family == AF_INET6 + && */IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) + return (EAFNOSUPPORT); + + /* + * samkumar: There was some code here that obtained the TCB (struct tcpcb*) + * by getting the inpcb from the socket and the TCB from the inpcb. I + * removed that code. + */ + + /* + * XXXRW: Some confusion: V4/V6 flags relate to binding, and + * therefore probably require the hash lock, which isn't held here. + * Is this a significant problem? + */ + if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { + tcplp_sys_log("V4-Mapped Address!\n"); + + /* + * samkumar: There used to be code that woulf handle the case of + * v4-mapped addresses. It would call in6_sin6_2_sin to convert the + * struct sockaddr_in6 to a struct sockaddr_in, set the INP_IPV4 flag + * and clear the INP_IPV6 flag on inp->inp_vflag, do some other + * processing, and finally call tcp_connect and tcp_output. However, + * it would first check if the IN6P_IPV6_V6ONLY flag was set in + * inp->inp_flags, and if so, it would return with EINVAL. In TCPlp, we + * support IPv6 only, so I removed the check for IN6P_IPV6_V6ONLY and + * always act as if that flag is set. I kept the code in that if + * statement making the check, and removed the other code that actually + * handled this case. + */ + error = EINVAL; + goto out; + } + + /* + * samkumar: I removed some code here that set/cleared some flags in the` + * inpcb and called prison_remote_ip6. + */ + + /* + * samkumar: Originally, the struct thread *td was passed along to + * tcp6_connect. + */ + if ((error = tcp6_connect(tp, sin6p)) != 0) + goto out; + + tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp)); + error = tcp_output(tp); + +out: + return (error); +} + +/* + * Do a send by putting data in output queue and updating urgent + * marker if URG set. Possibly send more data. Unlike the other + * pru_*() routines, the mbuf chains are our responsibility. We + * must either enqueue them or free them. The other pru_* routines + * generally are caller-frees. + */ +/* + * samkumar: I removed locking, statistics, inpcb management, and debug probes. + * I also removed support for the urgent pointer. + * + * I changed the signature of this function. It used to be + * static int + * tcp_usr_send(struct socket *so, int flags, struct mbuf *m, + * struct sockaddr *nam, struct mbuf *control, struct thread *td) + * + * The new function signature works as follows. DATA is a new linked buffer to + * add to the end of the send buffer. EXTENDBY is the number of bytes by which + * to extend the final linked buffer of the send buffer. Either DATA should be + * NULL, or EXTENDBY should be 0. + */ +int tcp_usr_send(struct tcpcb* tp, int moretocome, otLinkedBuffer* data, size_t extendby) +{ + int error = 0; + + /* + * samkumar: This if statement and the next are checks that I added + */ + if (tp->t_state < TCPS_ESTABLISHED) { + error = ENOTCONN; + goto out; + } + + if (tpiscantsend(tp)) { + error = EPIPE; + goto out; + } + + /* + * samkumar: There used to be logic here that acquired locks, dealt with + * INP_TIMEWAIT and INP_DROPPED flags on inp->inp_flags, and handled the + * control mbuf passed as an argument (which would result in an error since + * TCP doesn't support control information). I've deleted that code, but + * left the following if block. + */ + if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) { + error = ECONNRESET; + goto out; + } + + /* + * The following code used to be wrapped in an if statement: + * "if (!(flags & PRUS_OOB))", that only executed it if the "out of band" + * flag was not set. In TCB, "out of band" data is conveyed via the urgent + * pointer, and TCPlp does not support the urgent pointer. Therefore, I + * removed the "if" check and put its body below. + */ + + /* + * samkumar; The FreeBSD code calls sbappendstream(&so->so_snd, m, flags); + * I've replaced it with the following logic, which appends to the + * send buffer according to TCPlp's data structures. + */ + if (data == NULL) { + if (extendby == 0) { + goto out; + } + lbuf_extend(&tp->sendbuf, extendby); + } else { + if (data->mLength == 0) { + goto out; + } + lbuf_append(&tp->sendbuf, data); + } + + /* + * samkumar: There used to be code here to handle "implied connect," + * which initiates the TCP handshake if sending data on a socket that + * isn't yet connected. TCPlp doesn't support this at the moment, but + * it might be worth revisiting when implementing TCP Fast Open. + */ + + /* + * samkumar: There used to be code here handling the PRUS_EOF flag in + * the former flags parameter. I've removed this code. + */ + + /* + * samkumar: The code below was previously wrapped in an if statement + * that checked that the INP_DROPPED flag in inp->inp_flags and the + * PRUS_NOTREADY flag in the former flags parameter were both clear. + * If either one was set, then tcp_output would not be called. + * + * The "more to come" functionality was previously triggered via the + * PRUS_MORETOCOME flag in the flags parameter to this function. Since + * that's the only flag that TCPlp uses here, I replaced the flags + * parameter with a "moretocome" parameter, which we check instead. + */ + if (moretocome) + tp->t_flags |= TF_MORETOCOME; + error = tcp_output(tp); + if (moretocome) + tp->t_flags &= ~TF_MORETOCOME; + + /* + * samkumar: This is where the "if (!(flags & PRUS_OOB))" block would end. + * There used to be a large "else" block handling out-of-band data, but I + * removed that entire block since we do not support the urgent pointer in + * TCPlp. + */ +out: + return (error); +} + +/* + * After a receive, possibly send window update to peer. + */ +int +tcp_usr_rcvd(struct tcpcb* tp) +{ + int error = 0; + + /* + * samkumar: There used to be logic here that acquired locks, dealt with + * INP_TIMEWAIT and INP_DROPPED flags on inp->inp_flags, and added debug + * probes I've deleted that code, but left the following if block. + */ + if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) { + error = ECONNRESET; + goto out; + } + + tcp_output(tp); + +out: + return (error); +} + +/* + * samkumar: Removed the tcp_disconnect function. It is meant to be a + * "friendly" disconnect to complement the unceremonious "abort" functionality + * that is also provbided. The FreeBSD implementation called it from + * tcp_usr_close, which we removed (see the comment below for the reason why). + * It's not called from anywhere else, so I'm removing this function entirely. + */ + +/* + * Mark the connection as being incapable of further output. + */ +/* + * samkumar: Modified to remove locking, socket/inpcb handling, and debug + * probes. + */ +int +tcp_usr_shutdown(struct tcpcb* tp) +{ + int error = 0; + + /* + * samkumar: replaced checks on the INP_TIMEWAIT and INP_DROPPED flags on + * inp->inp_flags with these checks on tp->t_state. + */ + if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) { + error = ECONNRESET; + goto out; + } + + /* samkumar: replaced socantsendmore with tpcantsendmore */ + tpcantsendmore(tp); + tcp_usrclosed(tp); + + /* + * samkumar: replaced check on INP_DROPPED flag in inp->inp_flags with + * this check on tp->t_state. + */ + if (tp->t_state != TCPS_CLOSED) + error = tcp_output(tp); + +out: + return (error); +} + + +/* + * User issued close, and wish to trail through shutdown states: + * if never received SYN, just forget it. If got a SYN from peer, + * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. + * If already got a FIN from peer, then almost done; go to LAST_ACK + * state. In all other cases, have already sent FIN to peer (e.g. + * after PRU_SHUTDOWN), and just have to play tedious game waiting + * for peer to send FIN or not respond to keep-alives, etc. + * We can let the user exit from the close as soon as the FIN is acked. + */ +/* + * Removed locking, TCP Offload, and socket/inpcb handling. + */ +static void +tcp_usrclosed(struct tcpcb *tp) +{ + switch (tp->t_state) { + case TCPS_LISTEN: + tcp_state_change(tp, TCPS_CLOSED); + /* FALLTHROUGH */ + case TCPS_CLOSED: + tp = tcp_close(tp); + tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL); + /* + * tcp_close() should never return NULL here as the socket is + * still open. + */ + KASSERT(tp != NULL, + ("tcp_usrclosed: tcp_close() returned NULL")); + break; + + case TCPS_SYN_SENT: + case TCPS_SYN_RECEIVED: + tp->t_flags |= TF_NEEDFIN; + break; + + case TCPS_ESTABLISHED: + tcp_state_change(tp, TCPS_FIN_WAIT_1); + break; + + case TCPS_CLOSE_WAIT: + tcp_state_change(tp, TCPS_LAST_ACK); + break; + } + if (tp->t_state >= TCPS_FIN_WAIT_2) { + /* samkumar: commented out the following "soisdisconnected" line. */ + // soisdisconnected(tp->t_inpcb->inp_socket); + /* Prevent the connection hanging in FIN_WAIT_2 forever. */ + if (tp->t_state == TCPS_FIN_WAIT_2) { + int timeout; + + timeout = (tcp_fast_finwait2_recycle) ? + tcp_finwait2_timeout : TP_MAXIDLE(tp); + tcp_timer_activate(tp, TT_2MSL, timeout); + } + } +} + +/* + * samkumar: I removed the tcp_usr_close function. It was meant to be called in + * case the socket is closed. It calls tcp_disconnect if the underlying TCP + * connection is still alive when the socket is closed ("full TCP state"). + * In TCPlp, we can't handle this because we want to free up the underlying + * memory immediately when the user deallocates a TCP connection, making it + * unavailable for the somewhat more ceremonious closing that tcp_disconnect + * would allow. The host system is expected to simply abort the connection if + * the application deallocates it. + */ + +/* + * Abort the TCP. Drop the connection abruptly. + */ +/* + * samkumar: Modified to remove locking, socket/inpcb handling, and debug + * probes. + */ +void +tcp_usr_abort(struct tcpcb* tp) +{ + /* + * If we still have full TCP state, and we're not dropped, drop. + */ + /* + * I replaced the checks on inp->inp_flags (which tested for the absence of + * INP_TIMEWAIT and INP_DROPPED flags), with the following checks on + * tp->t_state. + */ + if (tp->t_state != TCP6S_TIME_WAIT && + tp->t_state != TCP6S_CLOSED) { + tcp_drop(tp, ECONNABORTED); + } else if (tp->t_state == TCPS_TIME_WAIT) { // samkumar: I added this clause + tp = tcp_close(tp); + tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL); + } +} diff --git a/third_party/tcplp/bsdtcp/tcp_var.h b/third_party/tcplp/bsdtcp/tcp_var.h new file mode 100644 index 000000000..489b80213 --- /dev/null +++ b/third_party/tcplp/bsdtcp/tcp_var.h @@ -0,0 +1,591 @@ +/*- + * Copyright (c) 1982, 1986, 1993, 1994, 1995 + * 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_var.h 8.4 (Berkeley) 5/24/95 + * $FreeBSD$ + */ + +#ifndef _NETINET_TCP_VAR_H_ +#define _NETINET_TCP_VAR_H_ + +/* For memmove(). */ +#include + +/* Dependencies on OpenThread. */ +#include +#include + +/* Dependencies on TCPlp buffering libraries. */ +#include "../lib/bitmap.h" +#include "../lib/cbuf.h" +#include "../lib/lbuf.h" + +#include "cc.h" +#include "tcp.h" +#include "types.h" +#include "ip6.h" + +#include "sys/queue.h" + +/* Implement byte-order-specific functions using OpenThread. */ +uint16_t tcplp_sys_hostswap16(uint16_t hostport); +uint32_t tcplp_sys_hostswap32(uint32_t hostport); + +/* + * It seems that these are defined as macros in Mac OS X, which is why we need + * the #ifndef checks. Simply redefining them as functions would break the + * build. + */ + +#ifndef htons +static inline uint16_t htons(uint16_t hostport) { + return tcplp_sys_hostswap16(hostport); +} +#endif + +#ifndef ntohs +static inline uint16_t ntohs(uint16_t hostport) { + return tcplp_sys_hostswap16(hostport); +} +#endif + +#ifndef htonl +static inline uint32_t htonl(uint32_t hostport) { + return tcplp_sys_hostswap32(hostport); +} +#endif + +#ifndef ntohl +static inline uint32_t ntohl(uint32_t hostport) { + return tcplp_sys_hostswap32(hostport); +} +#endif + +// From ip_compat.h +#ifndef bcopy +#define bcopy(a,b,c) memmove(b,a,c) +#endif + +#ifndef bzero +#define bzero(a,b) memset(a,0x00,b) +#endif + +struct sackblk { + tcp_seq start; /* start seq no. of sack block */ + tcp_seq end; /* end seq no. */ +}; + +struct sackhole { + tcp_seq start; /* start seq no. of hole */ + tcp_seq end; /* end seq no. */ + tcp_seq rxmit; /* next seq. no in hole to be retransmitted */ + TAILQ_ENTRY(sackhole) scblink; /* scoreboard linkage */ +}; + +struct sackhint { + struct sackhole *nexthole; + int sack_bytes_rexmit; + tcp_seq last_sack_ack; /* Most recent/largest sacked ack */ +}; + +struct tcptemp { + uint8_t tt_ipgen[40]; /* the size must be of max ip header, now IPv6 */ + struct tcphdr tt_t; +}; + +#define tcp6cb tcpcb /* for KAME src sync over BSD*'s */ + +/* Abridged TCB for passive sockets. */ +struct tcpcb_listen { + int t_state; /* Always CLOSED or LISTEN. */ + otInstance* instance; + struct in6_addr laddr; + uint16_t lport; +}; + +#define TCB_CANTRCVMORE 0x20 +#define TCB_CANTSENDMORE 0x40 + +#define TCB_PASSIVE 0x80 + +#define tpcantrcvmore(tp) (tp)->miscflags |= TCB_CANTRCVMORE +#define tpcantsendmore(tp) (tp)->miscflags |= TCB_CANTSENDMORE +#define tpiscantrcv(tp) (((tp)->miscflags & TCB_CANTRCVMORE) != 0) +#define tpiscantsend(tp) (((tp)->miscflags & TCB_CANTSENDMORE) != 0) +#define tpmarktimeractive(tp, timer) (tp)->miscflags |= timer +#define tpistimeractive(tp, timer) (((tp)->miscflags & timer) != 0) +#define tpcleartimeractive(tp, timer) (tp)->miscflags &= ~timer +#define tpmarkpassiveopen(tp) (tp)->miscflags |= TCB_PASSIVE +#define tpispassiveopen(tp) (((tp)->miscflags & TCB_PASSIVE) != 0) + +#define REASSBMP_SIZE(tp) BITS_TO_BYTES((tp)->recvbuf.size) + +/* These estimates are used to allocate sackholes (see tcp_sack.c). */ +#define AVG_SACKHOLES 2 // per TCB +#define MAX_SACKHOLES 5 // per TCB +#define SACKHOLE_POOL_SIZE MAX_SACKHOLES +#define SACKHOLE_BMP_SIZE BITS_TO_BYTES(SACKHOLE_POOL_SIZE) + +struct signals; + +/* + * Tcp control block, one per tcp; fields: + * Organized for 16 byte cacheline efficiency. + */ +/* + * samkumar: I added some fields for TCPlp to the beginning of this structure, + * replaced the fields for buffering and timers, and deleted unused fields to + * save memory. I've left some of the deleted fields in, as comments, for + * clarity. At times, I reduced the configurability of the implementation + * (e.g., by removing the ability to set keepalive parameters) in order to + * reduce the size of this structure. + */ +struct tcpcb { + /* + * samkumar: Extra fields that I added. TCPlp doesn't have a struct inpcb, + * so some of the fields I added represent data that would normally be + * stored in the inpcb. + */ + otInstance *instance; + + struct tcpcb_listen* accepted_from; + + struct lbufhead sendbuf; + struct cbufhead recvbuf; + uint8_t* reassbmp; + int32_t reass_fin_index; + + struct in6_addr laddr; // local IP address + struct in6_addr faddr; // foreign IP address + + uint16_t lport; // local port, network byte order + uint16_t fport; // foreign port, network byte order + uint8_t miscflags; + + /* samkumar: This field was there previously. */ + uint8_t t_state; /* state of this connection */ + + /* Pool of SACK holes (on per-connection basis, for OpenThread port). */ + struct sackhole sackhole_pool[SACKHOLE_POOL_SIZE]; + uint8_t sackhole_bmp[SACKHOLE_BMP_SIZE]; + +#if 0 + struct tsegqe_head t_segq; /* segment reassembly queue */ + void *t_pspare[2]; /* new reassembly queue */ + int t_segqlen; /* segment reassembly queue length */ +#endif + + int t_dupacks; /* consecutive dup acks recd */ + +#if 0 + struct tcp_timer *t_timers; /* All the TCP timers in one struct */ + + struct inpcb *t_inpcb; /* back pointer to internet pcb */ +#endif + + uint16_t tw_last_win; // samkumar: Taken from struct tcptw + + uint32_t t_flags; + +// struct vnet *t_vnet; /* back pointer to parent vnet */ + + tcp_seq snd_una; /* sent but unacknowledged */ + tcp_seq snd_max; /* highest sequence number sent; + * used to recognize retransmits + */ + tcp_seq snd_nxt; /* send next */ + tcp_seq snd_up; /* send urgent pointer */ + + tcp_seq snd_wl1; /* window update seg seq number */ + tcp_seq snd_wl2; /* window update seg ack number */ + tcp_seq iss; /* initial send sequence number */ + tcp_seq irs; /* initial receive sequence number */ + + tcp_seq rcv_nxt; /* receive next */ + tcp_seq rcv_adv; /* advertised window */ + uint64_t rcv_wnd; /* receive window */ + tcp_seq rcv_up; /* receive urgent pointer */ + + uint64_t snd_wnd; /* send window */ + uint64_t snd_cwnd; /* congestion-controlled window */ +// uint64_t snd_spare1; /* unused */ + uint64_t snd_ssthresh; /* snd_cwnd size threshold for + * for slow start exponential to + * linear switch + */ +// uint64_t snd_spare2; /* unused */ + tcp_seq snd_recover; /* for use in NewReno Fast Recovery */ + + uint32_t t_maxopd; /* mss plus options */ + + uint32_t t_rcvtime; /* inactivity time */ + uint32_t t_starttime; /* time connection was established */ + uint32_t t_rtttime; /* RTT measurement start time */ + tcp_seq t_rtseq; /* sequence number being timed */ + +// uint32_t t_bw_spare1; /* unused */ +// tcp_seq t_bw_spare2; /* unused */ + + int t_rxtcur; /* current retransmit value (ticks) */ + uint32_t t_maxseg; /* maximum segment size */ + int t_srtt; /* smoothed round-trip time */ + int t_rttvar; /* variance in round-trip time */ + + int t_rxtshift; /* log(2) of rexmt exp. backoff */ + uint32_t t_rttmin; /* minimum rtt allowed */ + uint32_t t_rttbest; /* best rtt we've seen */ + uint64_t t_rttupdated; /* number of times rtt sampled */ + uint64_t max_sndwnd; /* largest window peer has offered */ + + int t_softerror; /* possible error not yet reported */ +/* out-of-band data */ +// char t_oobflags; /* have some */ +// char t_iobc; /* input character */ +/* RFC 1323 variables */ + uint8_t snd_scale; /* window scaling for send window */ + uint8_t rcv_scale; /* window scaling for recv window */ + uint8_t request_r_scale; /* pending window scaling */ + u_int32_t ts_recent; /* timestamp echo data */ + uint32_t ts_recent_age; /* when last updated */ + u_int32_t ts_offset; /* our timestamp offset */ + + tcp_seq last_ack_sent; +/* experimental */ + uint64_t snd_cwnd_prev; /* cwnd prior to retransmit */ + uint64_t snd_ssthresh_prev; /* ssthresh prior to retransmit */ + tcp_seq snd_recover_prev; /* snd_recover prior to retransmit */ +// int t_sndzerowin; /* zero-window updates sent */ + uint32_t t_badrxtwin; /* window for retransmit recovery */ + uint8_t snd_limited; /* segments limited transmitted */ + +/* SACK related state */ + int snd_numholes; /* number of holes seen by sender */ + TAILQ_HEAD(sackhole_head, sackhole) snd_holes; + /* SACK scoreboard (sorted) */ + tcp_seq snd_fack; /* last seq number(+1) sack'd by rcv'r*/ + int rcv_numsacks; /* # distinct sack blks present */ + struct sackblk sackblks[MAX_SACK_BLKS]; /* seq nos. of sack blocks */ + tcp_seq sack_newdata; /* New data xmitted in this recovery + episode starts at this seq number */ + struct sackhint sackhint; /* SACK scoreboard hint */ + + int t_rttlow; /* smallest observed RTT */ +#if 0 + u_int32_t rfbuf_ts; /* recv buffer autoscaling timestamp */ + int rfbuf_cnt; /* recv buffer autoscaling byte count */ + struct toedev *tod; /* toedev handling this connection */ +#endif +// int t_sndrexmitpack; /* retransmit packets sent */ +// int t_rcvoopack; /* out-of-order packets received */ +// void *t_toe; /* TOE pcb pointer */ + int t_bytes_acked; /* # bytes acked during current RTT */ +// struct cc_algo *cc_algo; /* congestion control algorithm */ + struct cc_var ccv[1]; /* congestion control specific vars */ +#if 0 + struct osd *osd; /* storage for Khelp module data */ +#endif +#if 0 // Just use the default values for the KEEP constants (see tcp_timer.h) + uint32_t t_keepinit; /* time to establish connection */ + uint32_t t_keepidle; /* time before keepalive probes begin */ + uint32_t t_keepintvl; /* interval between keepalives */ + uint32_t t_keepcnt; /* number of keepalives before close */ +#endif +#if 0 // Don't support TCP Segment Offloading + uint32_t t_tsomax; /* TSO total burst length limit in bytes */ + uint32_t t_tsomaxsegcount; /* TSO maximum segment count */ + uint32_t t_tsomaxsegsize; /* TSO maximum segment size in bytes */ +#endif +// uint32_t t_pmtud_saved_maxopd; /* pre-blackhole MSS */ + uint32_t t_flags2; /* More tcpcb flags storage */ + +// uint32_t t_ispare[8]; /* 5 UTO, 3 TBD */ +// void *t_pspare2[4]; /* 1 TCP_SIGNATURE, 3 TBD */ +#if 0 +#if defined(_KERNEL) && defined(TCPPCAP) + struct mbufq t_inpkts; /* List of saved input packets. */ + struct mbufq t_outpkts; /* List of saved output packets. */ +#ifdef _LP64 + uint64_t _pad[0]; /* all used! */ +#else + uint64_t _pad[2]; /* 2 are available */ +#endif /* _LP64 */ +#else + uint64_t _pad[6]; +#endif /* defined(_KERNEL) && defined(TCPPCAP) */ +#endif +}; + +/* Defined in tcp_subr.c. */ +void initialize_tcb(struct tcpcb* tp); + +/* Copied from the "dead" portions below. */ + +void tcp_init(void); +void tcp_state_change(struct tcpcb *, int); +tcp_seq tcp_new_isn(struct tcpcb *); +struct tcpcb *tcp_close(struct tcpcb *); +struct tcpcb *tcp_drop(struct tcpcb *, int); +void +tcp_respond(struct tcpcb *tp, otInstance* instance, struct ip6_hdr* ip6gen, struct tcphdr *thgen, + tcp_seq ack, tcp_seq seq, int flags); +void tcp_setpersist(struct tcpcb *); +void cc_cong_signal(struct tcpcb *tp, struct tcphdr *th, uint32_t type); + +/* Added, since there is no header file for tcp_usrreq.c. */ +int tcp6_usr_connect(struct tcpcb* tp, struct sockaddr_in6* sinp6); +int tcp_usr_send(struct tcpcb* tp, int moretocome, struct otLinkedBuffer* data, size_t extendby); +int tcp_usr_rcvd(struct tcpcb* tp); +int tcp_usr_shutdown(struct tcpcb* tp); +void tcp_usr_abort(struct tcpcb* tp); + +/* + * Flags and utility macros for the t_flags field. + */ +#define TF_ACKNOW 0x000001 /* ack peer immediately */ +#define TF_DELACK 0x000002 /* ack, but try to delay it */ +#define TF_NODELAY 0x000004 /* don't delay packets to coalesce */ +#define TF_NOOPT 0x000008 /* don't use tcp options */ +#define TF_SENTFIN 0x000010 /* have sent FIN */ +#define TF_REQ_SCALE 0x000020 /* have/will request window scaling */ +#define TF_RCVD_SCALE 0x000040 /* other side has requested scaling */ +#define TF_REQ_TSTMP 0x000080 /* have/will request timestamps */ +#define TF_RCVD_TSTMP 0x000100 /* a timestamp was received in SYN */ +#define TF_SACK_PERMIT 0x000200 /* other side said I could SACK */ +#define TF_NEEDSYN 0x000400 /* send SYN (implicit state) */ +#define TF_NEEDFIN 0x000800 /* send FIN (implicit state) */ +#define TF_NOPUSH 0x001000 /* don't push */ +#define TF_PREVVALID 0x002000 /* saved values for bad rxmit valid */ +#define TF_MORETOCOME 0x010000 /* More data to be appended to sock */ +#define TF_LQ_OVERFLOW 0x020000 /* listen queue overflow */ +#define TF_LASTIDLE 0x040000 /* connection was previously idle */ +#define TF_RXWIN0SENT 0x080000 /* sent a receiver win 0 in response */ +#define TF_FASTRECOVERY 0x100000 /* in NewReno Fast Recovery */ +#define TF_WASFRECOVERY 0x200000 /* was in NewReno Fast Recovery */ +#define TF_SIGNATURE 0x400000 /* require MD5 digests (RFC2385) */ +#define TF_FORCEDATA 0x800000 /* force out a byte */ +#define TF_TSO 0x1000000 /* TSO enabled on this connection */ +#define TF_TOE 0x2000000 /* this connection is offloaded */ +#define TF_ECN_PERMIT 0x4000000 /* connection ECN-ready */ +#define TF_ECN_SND_CWR 0x8000000 /* ECN CWR in queue */ +#define TF_ECN_SND_ECE 0x10000000 /* ECN ECE in queue */ +#define TF_CONGRECOVERY 0x20000000 /* congestion recovery mode */ +#define TF_WASCRECOVERY 0x40000000 /* was in congestion recovery */ + +#define IN_FASTRECOVERY(t_flags) (t_flags & TF_FASTRECOVERY) +#define ENTER_FASTRECOVERY(t_flags) t_flags |= TF_FASTRECOVERY +#define EXIT_FASTRECOVERY(t_flags) t_flags &= ~TF_FASTRECOVERY + +#define IN_CONGRECOVERY(t_flags) (t_flags & TF_CONGRECOVERY) +#define ENTER_CONGRECOVERY(t_flags) t_flags |= TF_CONGRECOVERY +#define EXIT_CONGRECOVERY(t_flags) t_flags &= ~TF_CONGRECOVERY + +#define IN_RECOVERY(t_flags) (t_flags & (TF_CONGRECOVERY | TF_FASTRECOVERY)) +#define ENTER_RECOVERY(t_flags) t_flags |= (TF_CONGRECOVERY | TF_FASTRECOVERY) +#define EXIT_RECOVERY(t_flags) t_flags &= ~(TF_CONGRECOVERY | TF_FASTRECOVERY) + +#define BYTES_THIS_ACK(tp, th) (th->th_ack - tp->snd_una) + +/* + * Flags for the t_oobflags field. + */ +#define TCPOOB_HAVEDATA 0x01 +#define TCPOOB_HADDATA 0x02 + +#ifdef TCP_SIGNATURE +/* + * Defines which are needed by the xform_tcp module and tcp_[in|out]put + * for SADB verification and lookup. + */ +#define TCP_SIGLEN 16 /* length of computed digest in bytes */ +#define TCP_KEYLEN_MIN 1 /* minimum length of TCP-MD5 key */ +#define TCP_KEYLEN_MAX 80 /* maximum length of TCP-MD5 key */ +/* + * Only a single SA per host may be specified at this time. An SPI is + * needed in order for the KEY_ALLOCSA() lookup to work. + */ +#define TCP_SIG_SPI 0x1000 +#endif /* TCP_SIGNATURE */ + +/* + * Flags for PLPMTU handling, t_flags2 + */ +#define TF2_PLPMTU_BLACKHOLE 0x00000001 /* Possible PLPMTUD Black Hole. */ +#define TF2_PLPMTU_PMTUD 0x00000002 /* Allowed to attempt PLPMTUD. */ +#define TF2_PLPMTU_MAXSEGSNT 0x00000004 /* Last seg sent was full seg. */ + +/* + * Structure to hold TCP options that are only used during segment + * processing (in tcp_input), but not held in the tcpcb. + * It's basically used to reduce the number of parameters + * to tcp_dooptions and tcp_addoptions. + * The binary order of the to_flags is relevant for packing of the + * options in tcp_addoptions. + */ +struct tcpopt { + u_int64_t to_flags; /* which options are present */ +#define TOF_MSS 0x0001 /* maximum segment size */ +#define TOF_SCALE 0x0002 /* window scaling */ +#define TOF_SACKPERM 0x0004 /* SACK permitted */ +#define TOF_TS 0x0010 /* timestamp */ +#define TOF_SIGNATURE 0x0040 /* TCP-MD5 signature option (RFC2385) */ +#define TOF_SACK 0x0080 /* Peer sent SACK option */ +#define TOF_MAXOPT 0x0100 + u_int32_t to_tsval; /* new timestamp */ + u_int32_t to_tsecr; /* reflected timestamp */ + uint8_t *to_sacks; /* pointer to the first SACK blocks */ + uint8_t *to_signature; /* pointer to the TCP-MD5 signature */ + u_int16_t to_mss; /* maximum segment size */ + u_int8_t to_wscale; /* window scaling */ + u_int8_t to_nsacks; /* number of SACK blocks */ + u_int32_t to_spare; /* UTO */ +}; + +/* + * Flags for tcp_dooptions. + */ +#define TO_SYN 0x01 /* parse SYN-only options */ + +struct hc_metrics_lite { /* must stay in sync with hc_metrics */ + uint64_t rmx_mtu; /* MTU for this path */ + uint64_t rmx_ssthresh; /* outbound gateway buffer limit */ + uint64_t rmx_rtt; /* estimated round trip time */ + uint64_t rmx_rttvar; /* estimated rtt variance */ + uint64_t rmx_bandwidth; /* estimated bandwidth */ + uint64_t rmx_cwnd; /* congestion window */ + uint64_t rmx_sendpipe; /* outbound delay-bandwidth product */ + uint64_t rmx_recvpipe; /* inbound delay-bandwidth product */ +}; + +/* + * Used by tcp_maxmtu() to communicate interface specific features + * and limits at the time of connection setup. + */ +struct tcp_ifcap { + int ifcap; + uint32_t tsomax; + uint32_t tsomaxsegcount; + uint32_t tsomaxsegsize; +}; + +void tcp_mss(struct tcpcb *, int); +void tcp_mss_update(struct tcpcb *, int, int, struct hc_metrics_lite *, + struct tcp_ifcap *); + +/* + * The smoothed round-trip time and estimated variance + * are stored as fixed point numbers scaled by the values below. + * For convenience, these scales are also used in smoothing the average + * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed). + * With these scales, srtt has 3 bits to the right of the binary point, + * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the + * binary point, and is smoothed with an ALPHA of 0.75. + */ +#define TCP_RTT_SCALE 32 /* multiplier for srtt; 3 bits frac. */ +#define TCP_RTT_SHIFT 5 /* shift for srtt; 3 bits frac. */ +#define TCP_RTTVAR_SCALE 16 /* multiplier for rttvar; 2 bits */ +#define TCP_RTTVAR_SHIFT 4 /* shift for rttvar; 2 bits */ +#define TCP_DELTA_SHIFT 2 /* see tcp_input.c */ + +/* My definition of the max macro */ +#define max(x, y) ((x) > (y) ? (x) : (y)) + +/* + * The initial retransmission should happen at rtt + 4 * rttvar. + * Because of the way we do the smoothing, srtt and rttvar + * will each average +1/2 tick of bias. When we compute + * the retransmit timer, we want 1/2 tick of rounding and + * 1 extra tick because of +-1/2 tick uncertainty in the + * firing of the timer. The bias will give us exactly the + * 1.5 tick we need. But, because the bias is + * statistical, we have to test that we don't drop below + * the minimum feasible timer (which is 2 ticks). + * This version of the macro adapted from a paper by Lawrence + * Brakmo and Larry Peterson which outlines a problem caused + * by insufficient precision in the original implementation, + * which results in inappropriately large RTO values for very + * fast networks. + */ +#define TCP_REXMTVAL(tp) \ + max((tp)->t_rttmin, (((tp)->t_srtt >> (TCP_RTT_SHIFT - TCP_DELTA_SHIFT)) \ + + (tp)->t_rttvar) >> TCP_DELTA_SHIFT) + +/* Copied from below. */ +static inline void +tcp_fields_to_host(struct tcphdr *th) +{ + + th->th_seq = ntohl(th->th_seq); + th->th_ack = ntohl(th->th_ack); + th->th_win = ntohs(th->th_win); + th->th_urp = ntohs(th->th_urp); +} + +void tcp_twstart(struct tcpcb*); +void tcp_twclose(struct tcpcb*, int); +int tcp_twcheck(struct tcpcb*, struct tcphdr *, int); +void tcp_dropwithreset(struct ip6_hdr* ip6, struct tcphdr *th, struct tcpcb *tp, otInstance* instance, + int tlen, int rstreason); +int tcp_input(struct ip6_hdr* ip6, struct tcphdr* th, otMessage* msg, struct tcpcb* tp, struct tcpcb_listen* tpl, + struct signals* sig); +int tcp_output(struct tcpcb *); +void tcpip_maketemplate(struct tcpcb *, struct tcptemp*); +void tcpip_fillheaders(struct tcpcb *, otMessageInfo *, void *); +uint64_t tcp_maxmtu6(struct tcpcb*, struct tcp_ifcap *); +int tcp_addoptions(struct tcpopt *, uint8_t *); +int tcp_mssopt(struct tcpcb*); +int tcp_reass(struct tcpcb *, struct tcphdr *, int *, otMessage *, off_t, struct signals*); +void tcp_sack_init(struct tcpcb *); // Sam: new function that I added +void tcp_sack_doack(struct tcpcb *, struct tcpopt *, tcp_seq); +void tcp_update_sack_list(struct tcpcb *tp, tcp_seq rcv_laststart, tcp_seq rcv_lastend); +void tcp_clean_sackreport(struct tcpcb *tp); +void tcp_sack_adjust(struct tcpcb *tp); +struct sackhole *tcp_sack_output(struct tcpcb *tp, int *sack_bytes_rexmt); +void tcp_sack_partialack(struct tcpcb *, struct tcphdr *); +void tcp_free_sackholes(struct tcpcb *tp); + +#define tcps_rcvmemdrop tcps_rcvreassfull /* compat */0 + +/* + * Identifiers for TCP sysctl nodes + */ +#define TCPCTL_DO_RFC1323 1 /* use RFC-1323 extensions */ +#define TCPCTL_MSSDFLT 3 /* MSS default */ +#define TCPCTL_STATS 4 /* statistics (read-only) */ +#define TCPCTL_RTTDFLT 5 /* default RTT estimate */ +#define TCPCTL_KEEPIDLE 6 /* keepalive idle timer */ +#define TCPCTL_KEEPINTVL 7 /* interval to send keepalives */ +#define TCPCTL_SENDSPACE 8 /* send buffer space */ +#define TCPCTL_RECVSPACE 9 /* receive buffer space */ +#define TCPCTL_KEEPINIT 10 /* timeout for establishing syn */ +#define TCPCTL_PCBLIST 11 /* list of all outstanding PCBs */ +#define TCPCTL_DELACKTIME 12 /* time before sending delayed ACK */ +#define TCPCTL_V6MSSDFLT 13 /* MSS default for IPv6 */ +#define TCPCTL_SACK 14 /* Selective Acknowledgement,rfc 2018 */ +#define TCPCTL_DROP 15 /* drop tcp connection */ + +#endif /* _NETINET_TCP_VAR_H_ */ diff --git a/third_party/tcplp/bsdtcp/types.h b/third_party/tcplp/bsdtcp/types.h new file mode 100644 index 000000000..e638bf460 --- /dev/null +++ b/third_party/tcplp/bsdtcp/types.h @@ -0,0 +1,71 @@ +/*- + * Copyright (c) 1982, 1986, 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * 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. + * + * @(#)types.h 8.6 (Berkeley) 2/19/95 + * $FreeBSD$ + */ + +/* + * samkumar: I've omitted definitions for u_char, u_short, u_int, etc., because + * they come predefined by certain compilers, sometimes differently. I just + * replaced all usages of these types in the code with the corresponding + * standard integer types (uint8_t, uint16_t, etc.). + */ + +#ifndef _SYS_TYPES_H_ +#define _SYS_TYPES_H_ + +#include + +typedef uint8_t __uint8_t; +typedef uint16_t __uint16_t; +typedef uint32_t __uint32_t; +typedef uint64_t __uint64_t; +typedef int8_t __int8_t; +typedef int16_t __int16_t; +typedef int32_t __int32_t; +typedef int64_t __int64_t; + +typedef __uint8_t u_int8_t; /* unsigned integrals (deprecated) */ +typedef __uint16_t u_int16_t; +typedef __uint32_t u_int32_t; +typedef __uint64_t u_int64_t; + +typedef __uint64_t u_quad_t; /* quads (deprecated) */ +typedef __int64_t quad_t; +typedef quad_t * qaddr_t; + +typedef char * caddr_t; /* core address */ +typedef const char * c_caddr_t; /* core address, pointer to const */ + +#endif diff --git a/third_party/tcplp/tcplp.c b/third_party/tcplp/tcplp.c deleted file mode 100644 index e69de29bb..000000000 diff --git a/third_party/tcplp/tcplp.h b/third_party/tcplp/tcplp.h index e69de29bb..eccd98bed 100644 --- a/third_party/tcplp/tcplp.h +++ b/third_party/tcplp/tcplp.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018, Sam Kumar + * Copyright (c) 2018, University of California, Berkeley + * 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. + * 3. Neither the name of the copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. + */ + +/** + * @file + * This file defines the interface between TCPlp and the host network stack + * (OpenThread, in this case). It is based on content taken from TCPlp, + * modified to work with this port of TCPlp to OpenThread. + */ + +#ifndef TCPLP_H_ +#define TCPLP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "bsdtcp/ip6.h" +#include "bsdtcp/tcp.h" +#include "bsdtcp/tcp_fsm.h" +#include "bsdtcp/tcp_timer.h" +#include "bsdtcp/tcp_var.h" +#include +#include + +#define hz 1000 // number of ticks per second +#define MICROS_PER_TICK 1000 // number of microseconds per tick + +#define FRAMES_PER_SEG 5 +#define FRAMECAP_6LOWPAN (122 - 11 - 5) + +#define IP6HDR_SIZE (2 + 1 + 1 + 16 + 16) // IPHC header (2) + Next header (1) + Hop count (1) + Dest. addr (16) + Src. addr (16) + +#define RELOOKUP_REQUIRED -1 +#define CONN_LOST_NORMAL 0 + +struct signals { + int links_popped; + bool conn_established; + bool recvbuf_notempty; + bool rcvd_fin; +}; + +/* + * Functions that the TCP protocol logic can call to interact with the rest of + * the system. + */ +otMessage* tcplp_sys_new_message(otInstance* instance); +void tcplp_sys_free_message(otInstance* instance, otMessage* pkt); +void tcplp_sys_send_message(otInstance* instance, otMessage* pkt, otMessageInfo* info); +uint32_t tcplp_sys_get_ticks(); +uint32_t tcplp_sys_get_millis(); +void tcplp_sys_set_timer(struct tcpcb* tcb, uint8_t timer_flag, uint32_t delay); +void tcplp_sys_stop_timer(struct tcpcb* tcb, uint8_t timer_flag); +struct tcpcb* tcplp_sys_accept_ready(struct tcpcb_listen* tpl, struct in6_addr* addr, uint16_t port); +bool tcplp_sys_accepted_connection(struct tcpcb_listen* tpl, struct tcpcb* accepted, struct in6_addr* addr, uint16_t port); +void tcplp_sys_connection_lost(struct tcpcb* tcb, uint8_t errnum); +void tcplp_sys_on_state_change(struct tcpcb* tcb, int newstate); +void tcplp_sys_log(const char* format, ...); +bool tcplp_sys_autobind(otInstance *aInstance, const otSockAddr *aPeer, otSockAddr *aToBind, bool aBindAddress, bool aBindPort); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TCPLP_H_