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_