2005-01-07 01:45:51 +00:00
|
|
|
/*-
|
1995-10-03 16:54:17 +00:00
|
|
|
* Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995
|
1994-05-24 10:09:53 +00:00
|
|
|
* 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.
|
|
|
|
*
|
1995-10-03 16:54:17 +00:00
|
|
|
* @(#)tcp_input.c 8.12 (Berkeley) 5/24/95
|
1999-08-28 01:08:13 +00:00
|
|
|
* $FreeBSD$
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
|
2007-03-21 18:56:03 +00:00
|
|
|
#include "opt_ipfw.h" /* for ipfw_fwd */
|
Initial import of RFC 2385 (TCP-MD5) digest support.
This is the first of two commits; bringing in the kernel support first.
This can be enabled by compiling a kernel with options TCP_SIGNATURE
and FAST_IPSEC.
For the uninitiated, this is a TCP option which provides for a means of
authenticating TCP sessions which came into being before IPSEC. It is
still relevant today, however, as it is used by many commercial router
vendors, particularly with BGP, and as such has become a requirement for
interconnect at many major Internet points of presence.
Several parts of the TCP and IP headers, including the segment payload,
are digested with MD5, including a shared secret. The PF_KEY interface
is used to manage the secrets using security associations in the SADB.
There is a limitation here in that as there is no way to map a TCP flow
per-port back to an SPI without polluting tcpcb or using the SPD; the
code to do the latter is unstable at this time. Therefore this code only
supports per-host keying granularity.
Whilst FAST_IPSEC is mutually exclusive with KAME IPSEC (and thus IPv6),
TCP_SIGNATURE applies only to IPv4. For the vast majority of prospective
users of this feature, this will not pose any problem.
This implementation is output-only; that is, the option is honoured when
responding to a host initiating a TCP session, but no effort is made
[yet] to authenticate inbound traffic. This is, however, sufficient to
interwork with Cisco equipment.
Tested with a Cisco 2501 running IOS 12.0(27), and Quagga 0.96.4 with
local patches. Patches for tcpdump to validate TCP-MD5 sessions are also
available from me upon request.
Sponsored by: sentex.net
2004-02-11 04:26:04 +00:00
|
|
|
#include "opt_inet.h"
|
2000-01-09 19:17:30 +00:00
|
|
|
#include "opt_inet6.h"
|
2000-01-15 14:56:38 +00:00
|
|
|
#include "opt_ipsec.h"
|
2002-07-31 19:06:49 +00:00
|
|
|
#include "opt_mac.h"
|
1997-09-16 18:36:06 +00:00
|
|
|
#include "opt_tcpdebug.h"
|
2004-06-23 21:04:37 +00:00
|
|
|
#include "opt_tcp_sack.h"
|
1997-09-16 18:36:06 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/param.h>
|
1995-11-09 20:23:09 +00:00
|
|
|
#include <sys/kernel.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/mbuf.h>
|
1997-04-27 20:01:29 +00:00
|
|
|
#include <sys/proc.h> /* for proc0 declaration */
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/protosw.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <sys/signalvar.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/socketvar.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <sys/sysctl.h>
|
1996-04-04 10:46:44 +00:00
|
|
|
#include <sys/syslog.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <sys/systm.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1995-10-03 16:54:17 +00:00
|
|
|
#include <machine/cpu.h> /* before tcp_seq.h, for tcp_random18() */
|
|
|
|
|
2004-02-24 15:27:41 +00:00
|
|
|
#include <vm/uma.h>
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/route.h>
|
|
|
|
|
|
|
|
#include <netinet/in.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <netinet/in_pcb.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <netinet/in_systm.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <netinet/in_var.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <netinet/ip.h>
|
2006-02-18 17:05:00 +00:00
|
|
|
#include <netinet/ip_icmp.h> /* required for icmp_var.h */
|
|
|
|
#include <netinet/icmp_var.h> /* for ICMP_BANDLIM */
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <netinet/ip_var.h>
|
2005-11-18 20:12:40 +00:00
|
|
|
#include <netinet/ip_options.h>
|
2000-07-04 16:35:15 +00:00
|
|
|
#include <netinet/ip6.h>
|
|
|
|
#include <netinet/icmp6.h>
|
|
|
|
#include <netinet6/in6_pcb.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <netinet6/ip6_var.h>
|
|
|
|
#include <netinet6/nd6.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <netinet/tcp_fsm.h>
|
|
|
|
#include <netinet/tcp_seq.h>
|
|
|
|
#include <netinet/tcp_timer.h>
|
|
|
|
#include <netinet/tcp_var.h>
|
2000-01-09 19:17:30 +00:00
|
|
|
#include <netinet6/tcp6_var.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <netinet/tcpip.h>
|
1994-09-15 10:36:56 +00:00
|
|
|
#ifdef TCPDEBUG
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <netinet/tcp_debug.h>
|
2000-01-09 19:17:30 +00:00
|
|
|
#endif /* TCPDEBUG */
|
|
|
|
|
2002-10-16 02:25:05 +00:00
|
|
|
#ifdef FAST_IPSEC
|
|
|
|
#include <netipsec/ipsec.h>
|
|
|
|
#include <netipsec/ipsec6.h>
|
|
|
|
#endif /*FAST_IPSEC*/
|
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef IPSEC
|
|
|
|
#include <netinet6/ipsec.h>
|
2000-01-15 14:56:38 +00:00
|
|
|
#include <netinet6/ipsec6.h>
|
2000-01-09 19:17:30 +00:00
|
|
|
#include <netkey/key.h>
|
|
|
|
#endif /*IPSEC*/
|
|
|
|
|
2000-03-27 19:14:27 +00:00
|
|
|
#include <machine/in_cksum.h>
|
|
|
|
|
2006-10-22 11:52:19 +00:00
|
|
|
#include <security/mac/mac_framework.h>
|
|
|
|
|
2002-08-17 02:05:25 +00:00
|
|
|
static const int tcprexmtthresh = 3;
|
1995-11-14 20:34:56 +00:00
|
|
|
|
1995-02-14 02:35:19 +00:00
|
|
|
struct tcpstat tcpstat;
|
2001-06-23 17:17:59 +00:00
|
|
|
SYSCTL_STRUCT(_net_inet_tcp, TCPCTL_STATS, stats, CTLFLAG_RW,
|
1999-05-03 23:57:32 +00:00
|
|
|
&tcpstat , tcpstat, "TCP statistics (struct tcpstat, netinet/tcp_var.h)");
|
1995-11-14 20:34:56 +00:00
|
|
|
|
2007-02-20 10:20:03 +00:00
|
|
|
static int tcp_log_in_vain = 0;
|
2004-08-16 18:32:07 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, log_in_vain, CTLFLAG_RW,
|
2007-03-21 18:36:49 +00:00
|
|
|
&tcp_log_in_vain, 0, "Log all incoming TCP segments to closed ports");
|
1996-04-04 10:46:44 +00:00
|
|
|
|
1999-08-17 12:17:53 +00:00
|
|
|
static int blackhole = 0;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, blackhole, CTLFLAG_RW,
|
2007-03-21 18:36:49 +00:00
|
|
|
&blackhole, 0, "Do not send RST on segments to closed ports");
|
1999-08-17 12:17:53 +00:00
|
|
|
|
1998-02-26 05:25:39 +00:00
|
|
|
int tcp_delack_enabled = 1;
|
2004-08-16 18:32:07 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, delayed_ack, CTLFLAG_RW,
|
|
|
|
&tcp_delack_enabled, 0,
|
1999-05-03 23:57:32 +00:00
|
|
|
"Delay ACK to try and piggyback it onto a data packet");
|
1998-02-26 05:25:39 +00:00
|
|
|
|
1999-09-14 16:14:05 +00:00
|
|
|
static int drop_synfin = 0;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, drop_synfin, CTLFLAG_RW,
|
|
|
|
&drop_synfin, 0, "Drop TCP packets with SYN+FIN set");
|
|
|
|
|
2004-01-06 23:29:46 +00:00
|
|
|
static int tcp_do_rfc3042 = 1;
|
2003-03-12 20:27:28 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, rfc3042, CTLFLAG_RW,
|
|
|
|
&tcp_do_rfc3042, 0, "Enable RFC 3042 (Limited Transmit)");
|
|
|
|
|
2004-01-06 23:29:46 +00:00
|
|
|
static int tcp_do_rfc3390 = 1;
|
2003-03-13 01:43:45 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, rfc3390, CTLFLAG_RW,
|
|
|
|
&tcp_do_rfc3390, 0,
|
|
|
|
"Enable RFC 3390 (Increasing TCP's Initial Congestion Window)");
|
|
|
|
|
2005-01-03 07:08:37 +00:00
|
|
|
static int tcp_insecure_rst = 0;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, insecure_rst, CTLFLAG_RW,
|
|
|
|
&tcp_insecure_rst, 0,
|
2007-03-19 19:00:51 +00:00
|
|
|
"Follow the old (insecure) criteria for accepting RST packets");
|
2005-01-03 07:08:37 +00:00
|
|
|
|
2004-02-24 15:27:41 +00:00
|
|
|
SYSCTL_NODE(_net_inet_tcp, OID_AUTO, reass, CTLFLAG_RW, 0,
|
2007-03-19 19:00:51 +00:00
|
|
|
"TCP Segment Reassembly Queue");
|
2004-02-24 15:27:41 +00:00
|
|
|
|
|
|
|
static int tcp_reass_maxseg = 0;
|
|
|
|
SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, maxsegments, CTLFLAG_RDTUN,
|
2007-03-19 19:00:51 +00:00
|
|
|
&tcp_reass_maxseg, 0,
|
|
|
|
"Global maximum number of TCP Segments in Reassembly Queue");
|
2004-02-24 15:27:41 +00:00
|
|
|
|
|
|
|
int tcp_reass_qsize = 0;
|
|
|
|
SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, cursegments, CTLFLAG_RD,
|
2007-03-19 19:00:51 +00:00
|
|
|
&tcp_reass_qsize, 0,
|
|
|
|
"Global number of TCP Segments currently in Reassembly Queue");
|
2004-02-24 15:27:41 +00:00
|
|
|
|
|
|
|
static int tcp_reass_maxqlen = 48;
|
|
|
|
SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, maxqlen, CTLFLAG_RW,
|
2007-03-19 19:00:51 +00:00
|
|
|
&tcp_reass_maxqlen, 0,
|
|
|
|
"Maximum number of TCP Segments per individual Reassembly Queue");
|
2004-02-24 15:27:41 +00:00
|
|
|
|
|
|
|
static int tcp_reass_overflows = 0;
|
|
|
|
SYSCTL_INT(_net_inet_tcp_reass, OID_AUTO, overflows, CTLFLAG_RD,
|
2007-03-19 19:00:51 +00:00
|
|
|
&tcp_reass_overflows, 0,
|
|
|
|
"Global number of TCP Segment Reassembly Queue Overflows");
|
2004-02-24 15:27:41 +00:00
|
|
|
|
2007-02-01 18:32:13 +00:00
|
|
|
int tcp_do_autorcvbuf = 1;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, recvbuf_auto, CTLFLAG_RW,
|
2007-03-19 19:00:51 +00:00
|
|
|
&tcp_do_autorcvbuf, 0, "Enable automatic receive buffer sizing");
|
2007-02-01 18:32:13 +00:00
|
|
|
|
|
|
|
int tcp_autorcvbuf_inc = 16*1024;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, recvbuf_inc, CTLFLAG_RW,
|
2007-03-19 19:00:51 +00:00
|
|
|
&tcp_autorcvbuf_inc, 0,
|
|
|
|
"Incrementor step size of automatic receive buffer");
|
2007-02-01 18:32:13 +00:00
|
|
|
|
|
|
|
int tcp_autorcvbuf_max = 256*1024;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, recvbuf_max, CTLFLAG_RW,
|
2007-03-19 19:00:51 +00:00
|
|
|
&tcp_autorcvbuf_max, 0, "Max size of automatic receive buffer");
|
2007-02-01 18:32:13 +00:00
|
|
|
|
1995-04-09 01:29:31 +00:00
|
|
|
struct inpcbhead tcb;
|
2000-01-09 19:17:30 +00:00
|
|
|
#define tcb6 tcb /* for KAME src sync over BSD*'s */
|
1995-04-09 01:29:31 +00:00
|
|
|
struct inpcbinfo tcbinfo;
|
2002-06-10 20:05:46 +00:00
|
|
|
struct mtx *tcbinfo_mtx;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2005-06-27 22:27:42 +00:00
|
|
|
static void tcp_dooptions(struct tcpopt *, u_char *, int, int);
|
2007-03-23 20:16:50 +00:00
|
|
|
static int tcp_do_segment(struct mbuf *, struct tcphdr *,
|
|
|
|
struct socket *, struct tcpcb *, int, int);
|
|
|
|
static void tcp_dropwithreset(struct mbuf *, struct tcphdr *,
|
|
|
|
struct tcpcb *, int, int);
|
2002-03-19 21:25:46 +00:00
|
|
|
static void tcp_pulloutofband(struct socket *,
|
|
|
|
struct tcphdr *, struct mbuf *, int);
|
|
|
|
static int tcp_reass(struct tcpcb *, struct tcphdr *, int *,
|
2002-03-24 10:19:10 +00:00
|
|
|
struct mbuf *);
|
2002-03-19 21:25:46 +00:00
|
|
|
static void tcp_xmit_timer(struct tcpcb *, int);
|
2002-08-17 02:05:25 +00:00
|
|
|
static void tcp_newreno_partial_ack(struct tcpcb *, struct tcphdr *);
|
2006-04-09 16:59:19 +00:00
|
|
|
static int tcp_timewait(struct inpcb *, struct tcpopt *,
|
2003-02-19 22:32:43 +00:00
|
|
|
struct tcphdr *, struct mbuf *, int);
|
1995-11-14 20:34:56 +00:00
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
/* Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. */
|
|
|
|
#ifdef INET6
|
|
|
|
#define ND6_HINT(tp) \
|
|
|
|
do { \
|
|
|
|
if ((tp) && (tp)->t_inpcb && \
|
2003-11-20 20:07:39 +00:00
|
|
|
((tp)->t_inpcb->inp_vflag & INP_IPV6) != 0) \
|
|
|
|
nd6_nud_hint(NULL, NULL, 0); \
|
2000-01-09 19:17:30 +00:00
|
|
|
} while (0)
|
|
|
|
#else
|
|
|
|
#define ND6_HINT(tp)
|
|
|
|
#endif
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2001-02-25 15:17:24 +00:00
|
|
|
/*
|
2001-12-02 08:49:29 +00:00
|
|
|
* Indicate whether this ack should be delayed. We can delay the ack if
|
2001-12-13 04:02:09 +00:00
|
|
|
* - there is no delayed ack timer in progress and
|
|
|
|
* - our last ack wasn't a 0-sized window. We never want to delay
|
2003-02-19 21:18:23 +00:00
|
|
|
* the ack that opens up a 0-sized window and
|
|
|
|
* - delayed acks are enabled or
|
|
|
|
* - this is a half-synchronized T/TCP connection.
|
2001-02-25 15:17:24 +00:00
|
|
|
*/
|
2003-02-19 21:18:23 +00:00
|
|
|
#define DELAY_ACK(tp) \
|
2003-02-22 21:54:57 +00:00
|
|
|
((!callout_active(tp->tt_delack) && \
|
|
|
|
(tp->t_flags & TF_RXWIN0SENT) == 0) && \
|
2003-02-19 21:18:23 +00:00
|
|
|
(tcp_delack_enabled || (tp->t_flags & TF_NEEDSYN)))
|
2001-02-25 15:17:24 +00:00
|
|
|
|
2004-02-24 15:27:41 +00:00
|
|
|
/* Initialize TCP reassembly queue */
|
2006-04-21 09:25:40 +00:00
|
|
|
static void
|
|
|
|
tcp_reass_zone_change(void *tag)
|
|
|
|
{
|
|
|
|
|
|
|
|
tcp_reass_maxseg = nmbclusters / 16;
|
|
|
|
uma_zone_set_max(tcp_reass_zone, tcp_reass_maxseg);
|
|
|
|
}
|
|
|
|
|
2004-02-24 15:27:41 +00:00
|
|
|
uma_zone_t tcp_reass_zone;
|
|
|
|
void
|
|
|
|
tcp_reass_init()
|
|
|
|
{
|
|
|
|
tcp_reass_maxseg = nmbclusters / 16;
|
|
|
|
TUNABLE_INT_FETCH("net.inet.tcp.reass.maxsegments",
|
|
|
|
&tcp_reass_maxseg);
|
|
|
|
tcp_reass_zone = uma_zcreate("tcpreass", sizeof (struct tseg_qent),
|
|
|
|
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
|
|
|
|
uma_zone_set_max(tcp_reass_zone, tcp_reass_maxseg);
|
2006-04-21 09:25:40 +00:00
|
|
|
EVENTHANDLER_REGISTER(nmbclusters_change,
|
|
|
|
tcp_reass_zone_change, NULL, EVENTHANDLER_PRI_ANY);
|
2004-02-24 15:27:41 +00:00
|
|
|
}
|
|
|
|
|
1995-11-14 20:34:56 +00:00
|
|
|
static int
|
2007-03-21 19:37:55 +00:00
|
|
|
tcp_reass(struct tcpcb *tp, struct tcphdr *th, int *tlenp, struct mbuf *m)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2000-01-09 19:17:30 +00:00
|
|
|
struct tseg_qent *q;
|
|
|
|
struct tseg_qent *p = NULL;
|
|
|
|
struct tseg_qent *nq;
|
2004-02-24 15:27:41 +00:00
|
|
|
struct tseg_qent *te = NULL;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct socket *so = tp->t_inpcb->inp_socket;
|
|
|
|
int flags;
|
|
|
|
|
2004-11-23 23:41:20 +00:00
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
|
|
|
|
2004-02-24 15:27:41 +00:00
|
|
|
/*
|
|
|
|
* XXX: tcp_reass() is rather inefficient with its data structures
|
|
|
|
* and should be rewritten (see NetBSD for optimizations). While
|
|
|
|
* doing that it should move to its own file tcp_reass.c.
|
|
|
|
*/
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
2004-11-23 23:41:20 +00:00
|
|
|
* Call with th==NULL after become established to
|
1994-05-24 10:09:53 +00:00
|
|
|
* force pre-ESTABLISHED data up to user socket.
|
|
|
|
*/
|
2004-11-23 23:41:20 +00:00
|
|
|
if (th == NULL)
|
1994-05-24 10:09:53 +00:00
|
|
|
goto present;
|
|
|
|
|
2004-02-24 15:27:41 +00:00
|
|
|
/*
|
|
|
|
* Limit the number of segments in the reassembly queue to prevent
|
|
|
|
* holding on to too many segments (and thus running out of mbufs).
|
|
|
|
* Make sure to let the missing segment through which caused this
|
|
|
|
* queue. Always keep one global queue entry spare to be able to
|
|
|
|
* process the missing segment.
|
|
|
|
*/
|
|
|
|
if (th->th_seq != tp->rcv_nxt &&
|
|
|
|
(tcp_reass_qsize + 1 >= tcp_reass_maxseg ||
|
|
|
|
tp->t_segqlen >= tcp_reass_maxqlen)) {
|
|
|
|
tcp_reass_overflows++;
|
|
|
|
tcpstat.tcps_rcvmemdrop++;
|
|
|
|
m_freem(m);
|
2005-04-10 05:21:29 +00:00
|
|
|
*tlenp = 0;
|
2004-02-24 15:27:41 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate a new queue entry. If we can't, or hit the zone limit
|
|
|
|
* just drop the pkt.
|
|
|
|
*/
|
|
|
|
te = uma_zalloc(tcp_reass_zone, M_NOWAIT);
|
2000-01-09 19:17:30 +00:00
|
|
|
if (te == NULL) {
|
|
|
|
tcpstat.tcps_rcvmemdrop++;
|
|
|
|
m_freem(m);
|
2005-04-10 05:21:29 +00:00
|
|
|
*tlenp = 0;
|
2000-01-09 19:17:30 +00:00
|
|
|
return (0);
|
|
|
|
}
|
2004-02-24 15:27:41 +00:00
|
|
|
tp->t_segqlen++;
|
|
|
|
tcp_reass_qsize++;
|
1998-08-24 07:47:39 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Find a segment which begins after this one does.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
LIST_FOREACH(q, &tp->t_segq, tqe_q) {
|
|
|
|
if (SEQ_GT(q->tqe_th->th_seq, th->th_seq))
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
2000-01-09 19:17:30 +00:00
|
|
|
p = q;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
1998-08-24 07:47:39 +00:00
|
|
|
if (p != NULL) {
|
2007-03-21 19:37:55 +00:00
|
|
|
int i;
|
1994-05-24 10:09:53 +00:00
|
|
|
/* conversion to int (in i) handles seq wraparound */
|
2000-01-09 19:17:30 +00:00
|
|
|
i = p->tqe_th->th_seq + p->tqe_len - th->th_seq;
|
1994-05-24 10:09:53 +00:00
|
|
|
if (i > 0) {
|
2000-01-09 19:17:30 +00:00
|
|
|
if (i >= *tlenp) {
|
1994-05-24 10:09:53 +00:00
|
|
|
tcpstat.tcps_rcvduppack++;
|
2000-01-09 19:17:30 +00:00
|
|
|
tcpstat.tcps_rcvdupbyte += *tlenp;
|
1994-05-24 10:09:53 +00:00
|
|
|
m_freem(m);
|
2004-02-24 15:27:41 +00:00
|
|
|
uma_zfree(tcp_reass_zone, te);
|
|
|
|
tp->t_segqlen--;
|
|
|
|
tcp_reass_qsize--;
|
1995-02-09 23:13:27 +00:00
|
|
|
/*
|
|
|
|
* Try to present any queued data
|
|
|
|
* at the left window edge to the user.
|
|
|
|
* This is needed after the 3-WHS
|
|
|
|
* completes.
|
|
|
|
*/
|
|
|
|
goto present; /* ??? */
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
m_adj(m, i);
|
2000-01-09 19:17:30 +00:00
|
|
|
*tlenp -= i;
|
|
|
|
th->th_seq += i;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
tcpstat.tcps_rcvoopack++;
|
2000-01-09 19:17:30 +00:00
|
|
|
tcpstat.tcps_rcvoobyte += *tlenp;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* While we overlap succeeding segments trim them or,
|
|
|
|
* if they are completely covered, dequeue them.
|
|
|
|
*/
|
1998-08-24 07:47:39 +00:00
|
|
|
while (q) {
|
2007-03-21 19:37:55 +00:00
|
|
|
int i = (th->th_seq + *tlenp) - q->tqe_th->th_seq;
|
1994-05-24 10:09:53 +00:00
|
|
|
if (i <= 0)
|
|
|
|
break;
|
2000-01-09 19:17:30 +00:00
|
|
|
if (i < q->tqe_len) {
|
|
|
|
q->tqe_th->th_seq += i;
|
|
|
|
q->tqe_len -= i;
|
|
|
|
m_adj(q->tqe_m, i);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
1998-08-24 07:47:39 +00:00
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
nq = LIST_NEXT(q, tqe_q);
|
|
|
|
LIST_REMOVE(q, tqe_q);
|
|
|
|
m_freem(q->tqe_m);
|
2004-02-24 15:27:41 +00:00
|
|
|
uma_zfree(tcp_reass_zone, q);
|
|
|
|
tp->t_segqlen--;
|
|
|
|
tcp_reass_qsize--;
|
1998-08-24 07:47:39 +00:00
|
|
|
q = nq;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
/* Insert the new segment queue entry into place. */
|
|
|
|
te->tqe_m = m;
|
|
|
|
te->tqe_th = th;
|
|
|
|
te->tqe_len = *tlenp;
|
|
|
|
|
1998-08-24 07:47:39 +00:00
|
|
|
if (p == NULL) {
|
2000-01-09 19:17:30 +00:00
|
|
|
LIST_INSERT_HEAD(&tp->t_segq, te, tqe_q);
|
1998-08-24 07:47:39 +00:00
|
|
|
} else {
|
2000-01-09 19:17:30 +00:00
|
|
|
LIST_INSERT_AFTER(p, te, tqe_q);
|
1998-08-24 07:47:39 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
present:
|
|
|
|
/*
|
|
|
|
* Present data to user, advancing rcv_nxt through
|
|
|
|
* completed sequence space.
|
|
|
|
*/
|
1995-02-09 23:13:27 +00:00
|
|
|
if (!TCPS_HAVEESTABLISHED(tp->t_state))
|
1994-05-24 10:09:53 +00:00
|
|
|
return (0);
|
2000-01-09 19:17:30 +00:00
|
|
|
q = LIST_FIRST(&tp->t_segq);
|
|
|
|
if (!q || q->tqe_th->th_seq != tp->rcv_nxt)
|
1994-05-24 10:09:53 +00:00
|
|
|
return (0);
|
Reduce the number of unnecessary unlock-relocks on socket buffer mutexes
associated with performing a wakeup on the socket buffer:
- When performing an sbappend*() followed by a so[rw]wakeup(), explicitly
acquire the socket buffer lock and use the _locked() variants of both
calls. Note that the _locked() sowakeup() versions unlock the mutex on
return. This is done in uipc_send(), divert_packet(), mroute
socket_send(), raw_append(), tcp_reass(), tcp_input(), and udp_append().
- When the socket buffer lock is dropped before a sowakeup(), remove the
explicit unlock and use the _locked() sowakeup() variant. This is done
in soisdisconnecting(), soisdisconnected() when setting the can't send/
receive flags and dropping data, and in uipc_rcvd() which adjusting
back-pressure on the sockets.
For UNIX domain sockets running mpsafe with a contention-intensive SMP
mysql benchmark, this results in a 1.6% query rate improvement due to
reduce mutex costs.
2004-06-26 19:10:39 +00:00
|
|
|
SOCKBUF_LOCK(&so->so_rcv);
|
1994-05-24 10:09:53 +00:00
|
|
|
do {
|
2000-01-09 19:17:30 +00:00
|
|
|
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);
|
2004-06-14 18:16:22 +00:00
|
|
|
if (so->so_rcv.sb_state & SBS_CANTRCVMORE)
|
2000-01-09 19:17:30 +00:00
|
|
|
m_freem(q->tqe_m);
|
2002-05-31 11:52:35 +00:00
|
|
|
else
|
Reduce the number of unnecessary unlock-relocks on socket buffer mutexes
associated with performing a wakeup on the socket buffer:
- When performing an sbappend*() followed by a so[rw]wakeup(), explicitly
acquire the socket buffer lock and use the _locked() variants of both
calls. Note that the _locked() sowakeup() versions unlock the mutex on
return. This is done in uipc_send(), divert_packet(), mroute
socket_send(), raw_append(), tcp_reass(), tcp_input(), and udp_append().
- When the socket buffer lock is dropped before a sowakeup(), remove the
explicit unlock and use the _locked() sowakeup() variant. This is done
in soisdisconnecting(), soisdisconnected() when setting the can't send/
receive flags and dropping data, and in uipc_rcvd() which adjusting
back-pressure on the sockets.
For UNIX domain sockets running mpsafe with a contention-intensive SMP
mysql benchmark, this results in a 1.6% query rate improvement due to
reduce mutex costs.
2004-06-26 19:10:39 +00:00
|
|
|
sbappendstream_locked(&so->so_rcv, q->tqe_m);
|
2004-02-24 15:27:41 +00:00
|
|
|
uma_zfree(tcp_reass_zone, q);
|
|
|
|
tp->t_segqlen--;
|
|
|
|
tcp_reass_qsize--;
|
1998-08-24 07:47:39 +00:00
|
|
|
q = nq;
|
2000-01-09 19:17:30 +00:00
|
|
|
} while (q && q->tqe_th->th_seq == tp->rcv_nxt);
|
|
|
|
ND6_HINT(tp);
|
Reduce the number of unnecessary unlock-relocks on socket buffer mutexes
associated with performing a wakeup on the socket buffer:
- When performing an sbappend*() followed by a so[rw]wakeup(), explicitly
acquire the socket buffer lock and use the _locked() variants of both
calls. Note that the _locked() sowakeup() versions unlock the mutex on
return. This is done in uipc_send(), divert_packet(), mroute
socket_send(), raw_append(), tcp_reass(), tcp_input(), and udp_append().
- When the socket buffer lock is dropped before a sowakeup(), remove the
explicit unlock and use the _locked() sowakeup() variant. This is done
in soisdisconnecting(), soisdisconnected() when setting the can't send/
receive flags and dropping data, and in uipc_rcvd() which adjusting
back-pressure on the sockets.
For UNIX domain sockets running mpsafe with a contention-intensive SMP
mysql benchmark, this results in a 1.6% query rate improvement due to
reduce mutex costs.
2004-06-26 19:10:39 +00:00
|
|
|
sorwakeup_locked(so);
|
1994-05-24 10:09:53 +00:00
|
|
|
return (flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TCP input routine, follows pages 65-76 of the
|
|
|
|
* protocol specification dated September, 1981 very closely.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
int
|
2007-03-21 19:37:55 +00:00
|
|
|
tcp6_input(struct mbuf **mp, int *offp, int proto)
|
2000-01-09 19:17:30 +00:00
|
|
|
{
|
2007-03-21 19:37:55 +00:00
|
|
|
struct mbuf *m = *mp;
|
2001-06-11 12:39:29 +00:00
|
|
|
struct in6_ifaddr *ia6;
|
2000-01-09 19:17:30 +00:00
|
|
|
|
|
|
|
IP6_EXTHDR_CHECK(m, *offp, sizeof(struct tcphdr), IPPROTO_DONE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* draft-itojun-ipv6-tcp-to-anycast
|
|
|
|
* better place to put this in?
|
|
|
|
*/
|
2001-06-11 12:39:29 +00:00
|
|
|
ia6 = ip6_getdstifaddr(m);
|
2004-08-16 18:32:07 +00:00
|
|
|
if (ia6 && (ia6->ia6_flags & IN6_IFF_ANYCAST)) {
|
2000-01-09 19:17:30 +00:00
|
|
|
struct ip6_hdr *ip6;
|
|
|
|
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR,
|
|
|
|
(caddr_t)&ip6->ip6_dst - (caddr_t)ip6);
|
|
|
|
return IPPROTO_DONE;
|
|
|
|
}
|
|
|
|
|
2001-09-03 20:03:55 +00:00
|
|
|
tcp_input(m, *offp);
|
2000-01-09 19:17:30 +00:00
|
|
|
return IPPROTO_DONE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
void
|
2007-03-21 19:37:55 +00:00
|
|
|
tcp_input(struct mbuf *m, int off0)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2007-03-21 19:37:55 +00:00
|
|
|
struct tcphdr *th;
|
|
|
|
struct ip *ip = NULL;
|
|
|
|
struct ipovly *ipov;
|
|
|
|
struct inpcb *inp = NULL;
|
2007-03-23 20:16:50 +00:00
|
|
|
struct tcpcb *tp = NULL;
|
|
|
|
struct socket *so = NULL;
|
1995-10-03 16:54:17 +00:00
|
|
|
u_char *optp = NULL;
|
1994-05-25 09:21:21 +00:00
|
|
|
int optlen = 0;
|
2004-02-12 20:08:28 +00:00
|
|
|
int len, tlen, off;
|
2000-01-09 19:17:30 +00:00
|
|
|
int drop_hdrlen;
|
2007-03-21 19:37:55 +00:00
|
|
|
int thflags;
|
2007-03-23 20:16:50 +00:00
|
|
|
int rstreason = 0; /* For badport_bandlim accounting purposes */
|
2004-08-17 22:05:54 +00:00
|
|
|
#ifdef IPFIREWALL_FORWARD
|
|
|
|
struct m_tag *fwd_tag;
|
|
|
|
#endif
|
2002-08-17 02:05:25 +00:00
|
|
|
#ifdef INET6
|
2007-03-23 20:16:50 +00:00
|
|
|
struct ip6_hdr *ip6 = NULL;
|
2002-08-17 02:05:25 +00:00
|
|
|
int isipv6;
|
2006-12-12 12:17:58 +00:00
|
|
|
char ip6buf[INET6_ADDRSTRLEN];
|
2002-08-17 02:05:25 +00:00
|
|
|
#else
|
|
|
|
const int isipv6 = 0;
|
|
|
|
#endif
|
2007-03-23 20:16:50 +00:00
|
|
|
struct tcpopt to; /* options in this segment */
|
2002-06-10 20:05:46 +00:00
|
|
|
|
1994-09-15 10:36:56 +00:00
|
|
|
#ifdef TCPDEBUG
|
2002-08-17 02:05:25 +00:00
|
|
|
/*
|
|
|
|
* The size of tcp_saveipgen must be the size of the max ip header,
|
|
|
|
* now IPv6.
|
|
|
|
*/
|
2007-03-24 22:15:02 +00:00
|
|
|
u_char tcp_saveipgen[IP6_HDR_LEN];
|
2002-06-23 21:22:56 +00:00
|
|
|
struct tcphdr tcp_savetcp;
|
1994-09-15 10:36:56 +00:00
|
|
|
short ostate = 0;
|
|
|
|
#endif
|
2002-08-17 02:05:25 +00:00
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
isipv6 = (mtod(m, struct ip *)->ip_v == 6) ? 1 : 0;
|
|
|
|
#endif
|
1995-02-09 23:13:27 +00:00
|
|
|
|
2007-03-23 20:16:50 +00:00
|
|
|
to.to_flags = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
tcpstat.tcps_rcvtotal++;
|
2000-01-09 19:17:30 +00:00
|
|
|
|
|
|
|
if (isipv6) {
|
2004-03-01 19:10:31 +00:00
|
|
|
#ifdef INET6
|
2000-01-09 19:17:30 +00:00
|
|
|
/* IP6_EXTHDR_CHECK() is already done at tcp6_input() */
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
tlen = sizeof(*ip6) + ntohs(ip6->ip6_plen) - off0;
|
|
|
|
if (in6_cksum(m, IPPROTO_TCP, off0, tlen)) {
|
|
|
|
tcpstat.tcps_rcvbadsum++;
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
th = (struct tcphdr *)((caddr_t)ip6 + off0);
|
2001-06-11 12:39:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Be proactive about unspecified IPv6 address in source.
|
|
|
|
* As we use all-zero to indicate unbounded/unconnected pcb,
|
|
|
|
* unspecified IPv6 address can be used to confuse us.
|
|
|
|
*
|
|
|
|
* Note that packets with unspecified IPv6 destination is
|
|
|
|
* already dropped in ip6_input.
|
|
|
|
*/
|
|
|
|
if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
|
|
|
|
/* XXX stat */
|
|
|
|
goto drop;
|
|
|
|
}
|
2004-03-01 19:10:31 +00:00
|
|
|
#else
|
|
|
|
th = NULL; /* XXX: avoid compiler warning */
|
|
|
|
#endif
|
2000-03-27 19:14:27 +00:00
|
|
|
} else {
|
|
|
|
/*
|
2002-08-17 02:05:25 +00:00
|
|
|
* Get IP and TCP header together in first mbuf.
|
|
|
|
* Note: IP leaves IP header in first mbuf.
|
2000-03-27 19:14:27 +00:00
|
|
|
*/
|
2002-08-17 02:05:25 +00:00
|
|
|
if (off0 > sizeof (struct ip)) {
|
|
|
|
ip_stripoptions(m, (struct mbuf *)0);
|
|
|
|
off0 = sizeof(struct ip);
|
|
|
|
}
|
|
|
|
if (m->m_len < sizeof (struct tcpiphdr)) {
|
2007-03-23 19:11:22 +00:00
|
|
|
if ((m = m_pullup(m, sizeof (struct tcpiphdr)))
|
|
|
|
== NULL) {
|
2002-08-17 02:05:25 +00:00
|
|
|
tcpstat.tcps_rcvshort++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
ipov = (struct ipovly *)ip;
|
|
|
|
th = (struct tcphdr *)((caddr_t)ip + off0);
|
|
|
|
tlen = ip->ip_len;
|
|
|
|
|
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {
|
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
|
|
|
|
th->th_sum = m->m_pkthdr.csum_data;
|
|
|
|
else
|
|
|
|
th->th_sum = in_pseudo(ip->ip_src.s_addr,
|
|
|
|
ip->ip_dst.s_addr,
|
|
|
|
htonl(m->m_pkthdr.csum_data +
|
|
|
|
ip->ip_len +
|
|
|
|
IPPROTO_TCP));
|
|
|
|
th->th_sum ^= 0xffff;
|
2003-08-13 08:46:54 +00:00
|
|
|
#ifdef TCPDEBUG
|
|
|
|
ipov->ih_len = (u_short)tlen;
|
|
|
|
ipov->ih_len = htons(ipov->ih_len);
|
|
|
|
#endif
|
2002-08-17 02:05:25 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Checksum extended TCP header and data.
|
|
|
|
*/
|
|
|
|
len = sizeof (struct ip) + tlen;
|
|
|
|
bzero(ipov->ih_x1, sizeof(ipov->ih_x1));
|
|
|
|
ipov->ih_len = (u_short)tlen;
|
|
|
|
ipov->ih_len = htons(ipov->ih_len);
|
|
|
|
th->th_sum = in_cksum(m, len);
|
|
|
|
}
|
|
|
|
if (th->th_sum) {
|
|
|
|
tcpstat.tcps_rcvbadsum++;
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
/* Re-initialization for later version check */
|
|
|
|
ip->ip_v = IPVERSION;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that TCP offset makes sense,
|
|
|
|
* pull out TCP options and adjust length. XXX
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
off = th->th_off << 2;
|
1994-05-24 10:09:53 +00:00
|
|
|
if (off < sizeof (struct tcphdr) || off > tlen) {
|
|
|
|
tcpstat.tcps_rcvbadoff++;
|
|
|
|
goto drop;
|
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
tlen -= off; /* tlen is used instead of ti->ti_len */
|
1994-05-24 10:09:53 +00:00
|
|
|
if (off > sizeof (struct tcphdr)) {
|
2000-01-09 19:17:30 +00:00
|
|
|
if (isipv6) {
|
2004-03-01 19:10:31 +00:00
|
|
|
#ifdef INET6
|
2000-01-09 19:17:30 +00:00
|
|
|
IP6_EXTHDR_CHECK(m, off0, off, );
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
th = (struct tcphdr *)((caddr_t)ip6 + off0);
|
2004-03-01 19:10:31 +00:00
|
|
|
#endif
|
2002-08-17 02:05:25 +00:00
|
|
|
} else {
|
|
|
|
if (m->m_len < sizeof(struct ip) + off) {
|
|
|
|
if ((m = m_pullup(m, sizeof (struct ip) + off))
|
2007-03-23 19:11:22 +00:00
|
|
|
== NULL) {
|
2002-08-17 02:05:25 +00:00
|
|
|
tcpstat.tcps_rcvshort++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
ipov = (struct ipovly *)ip;
|
|
|
|
th = (struct tcphdr *)((caddr_t)ip + off0);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
optlen = off - sizeof (struct tcphdr);
|
2000-01-09 19:17:30 +00:00
|
|
|
optp = (u_char *)(th + 1);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
thflags = th->th_flags;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1999-09-12 17:22:08 +00:00
|
|
|
/*
|
|
|
|
* If the drop_synfin option is enabled, drop all packets with
|
|
|
|
* both the SYN and FIN bits set. This prevents e.g. nmap from
|
|
|
|
* identifying the TCP/IP stack.
|
|
|
|
*
|
2001-01-24 16:25:36 +00:00
|
|
|
* This is a violation of the TCP specification.
|
1999-09-12 17:22:08 +00:00
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if (drop_synfin && (thflags & (TH_SYN|TH_FIN)) == (TH_SYN|TH_FIN))
|
1999-09-12 17:22:08 +00:00
|
|
|
goto drop;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Convert TCP protocol specific fields to host format.
|
|
|
|
*/
|
2002-02-18 20:35:27 +00:00
|
|
|
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);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1995-04-05 10:32:14 +00:00
|
|
|
/*
|
2007-03-21 18:52:58 +00:00
|
|
|
* Delay dropping TCP, IP headers, IPv6 ext headers, and TCP options.
|
1995-04-05 10:32:14 +00:00
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
drop_hdrlen = off0 + off;
|
1995-04-05 10:32:14 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Locate pcb for segment.
|
|
|
|
*/
|
2002-10-30 08:32:19 +00:00
|
|
|
INP_INFO_WLOCK(&tcbinfo);
|
1994-05-24 10:09:53 +00:00
|
|
|
findpcb:
|
2007-03-23 20:16:50 +00:00
|
|
|
INP_INFO_WLOCK_ASSERT(&tcbinfo);
|
2004-08-17 22:05:54 +00:00
|
|
|
#ifdef IPFIREWALL_FORWARD
|
|
|
|
/* Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain. */
|
|
|
|
fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
|
|
|
|
|
|
|
|
if (fwd_tag != NULL && isipv6 == 0) { /* IPv6 support is not yet */
|
|
|
|
struct sockaddr_in *next_hop;
|
|
|
|
|
|
|
|
next_hop = (struct sockaddr_in *)(fwd_tag+1);
|
1998-07-06 03:20:19 +00:00
|
|
|
/*
|
Remove (almost all) global variables that were used to hold
packet forwarding state ("annotations") during ip processing.
The code is considerably cleaner now.
The variables removed by this change are:
ip_divert_cookie used by divert sockets
ip_fw_fwd_addr used for transparent ip redirection
last_pkt used by dynamic pipes in dummynet
Removal of the first two has been done by carrying the annotations
into volatile structs prepended to the mbuf chains, and adding
appropriate code to add/remove annotations in the routines which
make use of them, i.e. ip_input(), ip_output(), tcp_input(),
bdg_forward(), ether_demux(), ether_output_frame(), div_output().
On passing, remove a bug in divert handling of fragmented packet.
Now it is the fragment at offset 0 which sets the divert status of
the whole packet, whereas formerly it was the last incoming fragment
to decide.
Removal of last_pkt required a change in the interface of ip_fw_chk()
and dummynet_io(). On passing, use the same mechanism for dummynet
annotations and for divert/forward annotations.
option IPFIREWALL_FORWARD is effectively useless, the code to
implement it is very small and is now in by default to avoid the
obfuscation of conditionally compiled code.
NOTES:
* there is at least one global variable left, sro_fwd, in ip_output().
I am not sure if/how this can be removed.
* I have deliberately avoided gratuitous style changes in this commit
to avoid cluttering the diffs. Minor stule cleanup will likely be
necessary
* this commit only focused on the IP layer. I am sure there is a
number of global variables used in the TCP and maybe UDP stack.
* despite the number of files touched, there are absolutely no API's
or data structures changed by this commit (except the interfaces of
ip_fw_chk() and dummynet_io(), which are internal anyways), so
an MFC is quite safe and unintrusive (and desirable, given the
improved readability of the code).
MFC after: 10 days
2002-06-22 11:51:02 +00:00
|
|
|
* Transparently forwarded. Pretend to be the destination.
|
2004-08-16 18:32:07 +00:00
|
|
|
* already got one like this?
|
1998-07-06 03:20:19 +00:00
|
|
|
*/
|
2004-08-17 22:05:54 +00:00
|
|
|
inp = in_pcblookup_hash(&tcbinfo,
|
|
|
|
ip->ip_src, th->th_sport,
|
2002-08-17 02:05:25 +00:00
|
|
|
ip->ip_dst, th->th_dport,
|
|
|
|
0, m->m_pkthdr.rcvif);
|
1998-07-06 03:20:19 +00:00
|
|
|
if (!inp) {
|
2004-08-17 22:05:54 +00:00
|
|
|
/* It's new. Try to find the ambushing socket. */
|
2002-08-17 02:05:25 +00:00
|
|
|
inp = in_pcblookup_hash(&tcbinfo,
|
|
|
|
ip->ip_src, th->th_sport,
|
|
|
|
next_hop->sin_addr,
|
|
|
|
next_hop->sin_port ?
|
|
|
|
ntohs(next_hop->sin_port) :
|
|
|
|
th->th_dport,
|
2006-06-29 10:49:49 +00:00
|
|
|
INPLOOKUP_WILDCARD,
|
|
|
|
m->m_pkthdr.rcvif);
|
1998-07-06 03:20:19 +00:00
|
|
|
}
|
2004-08-17 22:05:54 +00:00
|
|
|
/* Remove the tag from the packet. We don't need it anymore. */
|
|
|
|
m_tag_delete(m, fwd_tag);
|
2007-03-21 18:56:03 +00:00
|
|
|
} else
|
2004-08-17 22:05:54 +00:00
|
|
|
#endif /* IPFIREWALL_FORWARD */
|
2007-03-21 18:56:03 +00:00
|
|
|
{
|
2004-03-01 19:10:31 +00:00
|
|
|
if (isipv6) {
|
|
|
|
#ifdef INET6
|
2002-08-17 02:05:25 +00:00
|
|
|
inp = in6_pcblookup_hash(&tcbinfo,
|
|
|
|
&ip6->ip6_src, th->th_sport,
|
|
|
|
&ip6->ip6_dst, th->th_dport,
|
2006-06-29 10:49:49 +00:00
|
|
|
INPLOOKUP_WILDCARD,
|
|
|
|
m->m_pkthdr.rcvif);
|
2004-03-01 19:10:31 +00:00
|
|
|
#endif
|
|
|
|
} else
|
2002-08-17 02:05:25 +00:00
|
|
|
inp = in_pcblookup_hash(&tcbinfo,
|
|
|
|
ip->ip_src, th->th_sport,
|
|
|
|
ip->ip_dst, th->th_dport,
|
2006-06-29 10:49:49 +00:00
|
|
|
INPLOOKUP_WILDCARD,
|
|
|
|
m->m_pkthdr.rcvif);
|
2004-08-16 18:32:07 +00:00
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
|
2004-02-17 14:02:37 +00:00
|
|
|
#if defined(IPSEC) || defined(FAST_IPSEC)
|
2004-06-16 09:35:07 +00:00
|
|
|
#ifdef INET6
|
2007-03-23 19:11:22 +00:00
|
|
|
if (isipv6 && inp != NULL && ipsec6_in_reject(m, inp)) {
|
2004-02-17 14:02:37 +00:00
|
|
|
#ifdef IPSEC
|
2007-03-23 19:11:22 +00:00
|
|
|
ipsec6stat.in_polvio++;
|
2004-06-16 09:35:07 +00:00
|
|
|
#endif
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2004-06-16 09:35:07 +00:00
|
|
|
} else
|
|
|
|
#endif /* INET6 */
|
|
|
|
if (inp != NULL && ipsec4_in_reject(m, inp)) {
|
2004-02-17 14:02:37 +00:00
|
|
|
#ifdef IPSEC
|
|
|
|
ipsecstat.in_polvio++;
|
2004-06-16 09:35:07 +00:00
|
|
|
#endif
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2002-10-16 02:25:05 +00:00
|
|
|
}
|
2004-02-17 14:02:37 +00:00
|
|
|
#endif /*IPSEC || FAST_IPSEC*/
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/*
|
2007-03-21 18:36:49 +00:00
|
|
|
* If the INPCB does not exist then all data in the incoming
|
|
|
|
* segment is discarded and an appropriate RST is sent back.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
1996-04-04 10:46:44 +00:00
|
|
|
if (inp == NULL) {
|
2007-03-21 18:36:49 +00:00
|
|
|
/*
|
|
|
|
* Log communication attempts to ports that are not
|
|
|
|
* in use.
|
|
|
|
*/
|
|
|
|
if ((tcp_log_in_vain == 1 && (thflags & TH_SYN)) ||
|
|
|
|
tcp_log_in_vain == 2) {
|
|
|
|
#ifndef INET6
|
2000-01-09 19:17:30 +00:00
|
|
|
char dbuf[4*sizeof "123"], sbuf[4*sizeof "123"];
|
2007-03-21 18:36:49 +00:00
|
|
|
#else
|
|
|
|
char dbuf[INET6_ADDRSTRLEN+2], sbuf[INET6_ADDRSTRLEN+2];
|
2000-01-09 19:17:30 +00:00
|
|
|
if (isipv6) {
|
2002-08-19 19:47:13 +00:00
|
|
|
strcpy(dbuf, "[");
|
2006-12-12 12:17:58 +00:00
|
|
|
strcat(dbuf,
|
|
|
|
ip6_sprintf(ip6buf, &ip6->ip6_dst));
|
2007-03-21 18:36:49 +00:00
|
|
|
strcat(dbuf, "]");
|
|
|
|
strcpy(sbuf, "[");
|
2006-12-12 12:17:58 +00:00
|
|
|
strcat(sbuf,
|
|
|
|
ip6_sprintf(ip6buf, &ip6->ip6_src));
|
2002-08-19 19:47:13 +00:00
|
|
|
strcat(sbuf, "]");
|
2007-03-21 18:36:49 +00:00
|
|
|
} else
|
|
|
|
#endif /* INET6 */
|
|
|
|
{
|
2002-08-17 02:05:25 +00:00
|
|
|
strcpy(dbuf, inet_ntoa(ip->ip_dst));
|
|
|
|
strcpy(sbuf, inet_ntoa(ip->ip_src));
|
|
|
|
}
|
2007-03-21 18:36:49 +00:00
|
|
|
log(LOG_INFO,
|
|
|
|
"Connection attempt to TCP %s:%d "
|
|
|
|
"from %s:%d flags:0x%02x\n",
|
|
|
|
dbuf, ntohs(th->th_dport), sbuf,
|
|
|
|
ntohs(th->th_sport), thflags);
|
1999-08-19 05:22:12 +00:00
|
|
|
}
|
2007-03-21 18:36:49 +00:00
|
|
|
/*
|
|
|
|
* When blackholing do not respond with a RST but
|
|
|
|
* completely ignore the segment and drop it.
|
|
|
|
*/
|
|
|
|
if ((blackhole == 1 && (thflags & TH_SYN)) ||
|
|
|
|
blackhole == 2)
|
|
|
|
goto drop;
|
|
|
|
|
2001-02-11 07:39:51 +00:00
|
|
|
rstreason = BANDLIM_RST_CLOSEDPORT;
|
|
|
|
goto dropwithreset;
|
1996-04-04 10:46:44 +00:00
|
|
|
}
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_LOCK(inp);
|
2005-08-22 16:13:08 +00:00
|
|
|
|
|
|
|
/* Check the minimum TTL for socket. */
|
2006-01-14 16:39:31 +00:00
|
|
|
if (inp->inp_ip_minttl != 0) {
|
|
|
|
#ifdef INET6
|
|
|
|
if (isipv6 && inp->inp_ip_minttl > ip6->ip6_hlim)
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2006-01-18 18:59:30 +00:00
|
|
|
else
|
2006-01-14 16:39:31 +00:00
|
|
|
#endif
|
|
|
|
if (inp->inp_ip_minttl > ip->ip_ttl)
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2006-01-14 16:39:31 +00:00
|
|
|
}
|
2005-08-22 16:13:08 +00:00
|
|
|
|
2007-03-21 18:52:58 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2003-02-19 22:32:43 +00:00
|
|
|
if (inp->inp_vflag & INP_TIMEWAIT) {
|
|
|
|
if (thflags & TH_SYN)
|
2006-06-26 15:35:25 +00:00
|
|
|
tcp_dooptions(&to, optp, optlen, TO_SYN);
|
2006-04-09 16:59:19 +00:00
|
|
|
if (tcp_timewait(inp, &to, th, m, tlen))
|
2003-02-19 22:32:43 +00:00
|
|
|
goto findpcb;
|
2007-03-21 18:52:58 +00:00
|
|
|
/* tcp_timewait unlocks inp. */
|
2003-02-19 22:32:43 +00:00
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
|
|
|
return;
|
|
|
|
}
|
2007-03-21 18:52:58 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
tp = intotcpcb(inp);
|
2007-03-23 19:11:22 +00:00
|
|
|
if (tp == NULL) {
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_UNLOCK(inp);
|
2001-02-11 07:39:51 +00:00
|
|
|
rstreason = BANDLIM_RST_CLOSEDPORT;
|
|
|
|
goto dropwithreset;
|
2000-12-15 21:45:49 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
if (tp->t_state == TCPS_CLOSED)
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock; /* XXX: dropwithreset??? */
|
1995-05-30 08:16:23 +00:00
|
|
|
|
2002-07-31 19:06:49 +00:00
|
|
|
#ifdef MAC
|
2004-06-20 20:17:29 +00:00
|
|
|
INP_LOCK_ASSERT(inp);
|
Introduce a MAC label reference in 'struct inpcb', which caches
the MAC label referenced from 'struct socket' in the IPv4 and
IPv6-based protocols. This permits MAC labels to be checked during
network delivery operations without dereferencing inp->inp_socket
to get to so->so_label, which will eventually avoid our having to
grab the socket lock during delivery at the network layer.
This change introduces 'struct inpcb' as a labeled object to the
MAC Framework, along with the normal circus of entry points:
initialization, creation from socket, destruction, as well as a
delivery access control check.
For most policies, the inpcb label will simply be a cache of the
socket label, so a new protocol switch method is introduced,
pr_sosetlabel() to notify protocols that the socket layer label
has been updated so that the cache can be updated while holding
appropriate locks. Most protocols implement this using
pru_sosetlabel_null(), but IPv4/IPv6 protocols using inpcbs use
the the worker function in_pcbsosetlabel(), which calls into the
MAC Framework to perform a cache update.
Biba, LOMAC, and MLS implement these entry points, as do the stub
policy, and test policy.
Reviewed by: sam, bms
Obtained from: TrustedBSD Project
Sponsored by: DARPA, Network Associates Laboratories
2003-11-18 00:39:07 +00:00
|
|
|
if (mac_check_inpcb_deliver(inp, m))
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2002-07-31 19:06:49 +00:00
|
|
|
#endif
|
Introduce a MAC label reference in 'struct inpcb', which caches
the MAC label referenced from 'struct socket' in the IPv4 and
IPv6-based protocols. This permits MAC labels to be checked during
network delivery operations without dereferencing inp->inp_socket
to get to so->so_label, which will eventually avoid our having to
grab the socket lock during delivery at the network layer.
This change introduces 'struct inpcb' as a labeled object to the
MAC Framework, along with the normal circus of entry points:
initialization, creation from socket, destruction, as well as a
delivery access control check.
For most policies, the inpcb label will simply be a cache of the
socket label, so a new protocol switch method is introduced,
pr_sosetlabel() to notify protocols that the socket layer label
has been updated so that the cache can be updated while holding
appropriate locks. Most protocols implement this using
pru_sosetlabel_null(), but IPv4/IPv6 protocols using inpcbs use
the the worker function in_pcbsosetlabel(), which calls into the
MAC Framework to perform a cache update.
Biba, LOMAC, and MLS implement these entry points, as do the stub
policy, and test policy.
Reviewed by: sam, bms
Obtained from: TrustedBSD Project
Sponsored by: DARPA, Network Associates Laboratories
2003-11-18 00:39:07 +00:00
|
|
|
so = inp->inp_socket;
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(so != NULL, ("%s: so == NULL", __func__));
|
1994-09-15 10:36:56 +00:00
|
|
|
#ifdef TCPDEBUG
|
2002-12-20 11:16:52 +00:00
|
|
|
if (so->so_options & SO_DEBUG) {
|
|
|
|
ostate = tp->t_state;
|
|
|
|
if (isipv6)
|
|
|
|
bcopy((char *)ip6, (char *)tcp_saveipgen, sizeof(*ip6));
|
|
|
|
else
|
|
|
|
bcopy((char *)ip, (char *)tcp_saveipgen, sizeof(*ip));
|
|
|
|
tcp_savetcp = *th;
|
|
|
|
}
|
1994-09-15 10:36:56 +00:00
|
|
|
#endif
|
2007-03-21 18:49:43 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2002-12-20 11:16:52 +00:00
|
|
|
if (so->so_options & SO_ACCEPTCONN) {
|
|
|
|
struct in_conninfo inc;
|
|
|
|
|
2006-06-26 16:14:19 +00:00
|
|
|
bzero(&inc, sizeof(inc));
|
2001-11-22 04:50:44 +00:00
|
|
|
inc.inc_isipv6 = isipv6;
|
2007-03-23 20:16:50 +00:00
|
|
|
#ifdef INET6
|
2001-11-22 04:50:44 +00:00
|
|
|
if (isipv6) {
|
|
|
|
inc.inc6_faddr = ip6->ip6_src;
|
|
|
|
inc.inc6_laddr = ip6->ip6_dst;
|
2007-03-23 20:16:50 +00:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
2001-11-22 04:50:44 +00:00
|
|
|
inc.inc_faddr = ip->ip_src;
|
|
|
|
inc.inc_laddr = ip->ip_dst;
|
|
|
|
}
|
|
|
|
inc.inc_fport = th->th_sport;
|
|
|
|
inc.inc_lport = th->th_dport;
|
|
|
|
|
2007-03-23 19:11:22 +00:00
|
|
|
/*
|
|
|
|
* If the state is LISTEN then ignore segment if it contains
|
2001-11-22 04:50:44 +00:00
|
|
|
* a RST. If the segment contains an ACK then it is bad and
|
|
|
|
* send a RST. If it does not contain a SYN then it is not
|
|
|
|
* interesting; drop it.
|
|
|
|
*
|
|
|
|
* If the state is SYN_RECEIVED (syncache) and seg contains
|
|
|
|
* an ACK, but not for our SYN/ACK, send a RST. If the seg
|
|
|
|
* contains a RST, check the sequence number to see if it
|
|
|
|
* is a valid reset segment.
|
|
|
|
*/
|
|
|
|
if ((thflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) {
|
|
|
|
if ((thflags & (TH_RST|TH_ACK|TH_SYN)) == TH_ACK) {
|
2006-09-13 13:08:27 +00:00
|
|
|
/*
|
|
|
|
* Parse the TCP options here because
|
|
|
|
* syncookies need access to the reflected
|
|
|
|
* timestamp.
|
|
|
|
*/
|
|
|
|
tcp_dooptions(&to, optp, optlen, 0);
|
|
|
|
if (!syncache_expand(&inc, &to, th, &so, m)) {
|
2001-11-22 04:50:44 +00:00
|
|
|
/*
|
|
|
|
* No syncache entry, or ACK was not
|
|
|
|
* for our SYN/ACK. Send a RST.
|
|
|
|
*/
|
1996-10-07 04:32:42 +00:00
|
|
|
tcpstat.tcps_badsyn++;
|
2001-02-11 07:39:51 +00:00
|
|
|
rstreason = BANDLIM_RST_OPENPORT;
|
|
|
|
goto dropwithreset;
|
1996-10-07 04:32:42 +00:00
|
|
|
}
|
2002-06-10 20:05:46 +00:00
|
|
|
if (so == NULL) {
|
2001-11-22 04:50:44 +00:00
|
|
|
/*
|
|
|
|
* Could not complete 3-way handshake,
|
|
|
|
* connection is being closed down, and
|
2006-02-28 23:05:59 +00:00
|
|
|
* syncache has free'd mbuf.
|
2001-11-22 04:50:44 +00:00
|
|
|
*/
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_UNLOCK(inp);
|
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
2001-11-22 04:50:44 +00:00
|
|
|
return;
|
2002-06-10 20:05:46 +00:00
|
|
|
}
|
2001-11-22 04:50:44 +00:00
|
|
|
/*
|
|
|
|
* Socket is created in state SYN_RECEIVED.
|
|
|
|
* Continue processing segment.
|
|
|
|
*/
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_UNLOCK(inp);
|
2001-11-22 04:50:44 +00:00
|
|
|
inp = sotoinpcb(so);
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_LOCK(inp);
|
2001-11-22 04:50:44 +00:00
|
|
|
tp = intotcpcb(inp);
|
|
|
|
/*
|
|
|
|
* This is what would have happened in
|
2002-01-04 17:21:27 +00:00
|
|
|
* tcp_output() when the SYN,ACK was sent.
|
2001-11-22 04:50:44 +00:00
|
|
|
*/
|
|
|
|
tp->snd_up = tp->snd_una;
|
|
|
|
tp->snd_max = tp->snd_nxt = tp->iss + 1;
|
|
|
|
tp->last_ack_sent = tp->rcv_nxt;
|
2007-03-23 20:16:50 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Process the segment and the data it
|
|
|
|
* contains. tcp_do_segment() consumes
|
|
|
|
* the mbuf chain and unlocks the inpcb.
|
|
|
|
* XXX: The potential return value of
|
|
|
|
* TIME_WAIT nuked is supposed to be
|
|
|
|
* handled above.
|
|
|
|
*/
|
|
|
|
if (tcp_do_segment(m, th, so, tp,
|
|
|
|
drop_hdrlen, tlen))
|
|
|
|
goto findpcb; /* TIME_WAIT nuked */
|
|
|
|
return;
|
2001-11-22 04:50:44 +00:00
|
|
|
}
|
|
|
|
if (thflags & TH_RST) {
|
|
|
|
syncache_chkrst(&inc, th);
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
1996-10-07 04:32:42 +00:00
|
|
|
}
|
2001-11-22 04:50:44 +00:00
|
|
|
if (thflags & TH_ACK) {
|
|
|
|
syncache_badack(&inc);
|
|
|
|
tcpstat.tcps_badsyn++;
|
|
|
|
rstreason = BANDLIM_RST_OPENPORT;
|
|
|
|
goto dropwithreset;
|
|
|
|
}
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2001-11-22 04:50:44 +00:00
|
|
|
}
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2001-11-22 04:50:44 +00:00
|
|
|
/*
|
|
|
|
* Segment's flags are (SYN) or (SYN|FIN).
|
|
|
|
*/
|
2001-06-11 12:39:29 +00:00
|
|
|
#ifdef INET6
|
2001-11-22 04:50:44 +00:00
|
|
|
/*
|
|
|
|
* If deprecated address is forbidden,
|
|
|
|
* we do not accept SYN to deprecated interface
|
|
|
|
* address to prevent any new inbound connection from
|
|
|
|
* getting established.
|
|
|
|
* When we do not accept SYN, we send a TCP RST,
|
|
|
|
* with deprecated source address (instead of dropping
|
|
|
|
* it). We compromise it as it is much better for peer
|
|
|
|
* to send a RST, and RST will be the final packet
|
|
|
|
* for the exchange.
|
|
|
|
*
|
|
|
|
* If we do not forbid deprecated addresses, we accept
|
|
|
|
* the SYN packet. RFC2462 does not suggest dropping
|
|
|
|
* SYN in this case.
|
|
|
|
* If we decipher RFC2462 5.5.4, it says like this:
|
|
|
|
* 1. use of deprecated addr with existing
|
|
|
|
* communication is okay - "SHOULD continue to be
|
|
|
|
* used"
|
|
|
|
* 2. use of it with new communication:
|
|
|
|
* (2a) "SHOULD NOT be used if alternate address
|
|
|
|
* with sufficient scope is available"
|
|
|
|
* (2b) nothing mentioned otherwise.
|
|
|
|
* Here we fall into (2b) case as we have no choice in
|
|
|
|
* our source address selection - we must obey the peer.
|
|
|
|
*
|
|
|
|
* The wording in RFC2462 is confusing, and there are
|
|
|
|
* multiple description text for deprecated address
|
|
|
|
* handling - worse, they are not exactly the same.
|
|
|
|
* I believe 5.5.4 is the best one, so we follow 5.5.4.
|
|
|
|
*/
|
|
|
|
if (isipv6 && !ip6_use_deprecated) {
|
|
|
|
struct in6_ifaddr *ia6;
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2001-11-22 04:50:44 +00:00
|
|
|
if ((ia6 = ip6_getdstifaddr(m)) &&
|
|
|
|
(ia6->ia6_flags & IN6_IFF_DEPRECATED)) {
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_UNLOCK(inp);
|
2001-11-22 04:50:44 +00:00
|
|
|
tp = NULL;
|
|
|
|
rstreason = BANDLIM_RST_OPENPORT;
|
|
|
|
goto dropwithreset;
|
1996-02-26 21:47:13 +00:00
|
|
|
}
|
2001-11-22 04:50:44 +00:00
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
#endif
|
2001-11-22 04:50:44 +00:00
|
|
|
/*
|
2007-03-21 18:49:43 +00:00
|
|
|
* Basic sanity checks on incoming SYN requests:
|
|
|
|
*
|
|
|
|
* Don't bother responding if the destination was a
|
|
|
|
* broadcast according to RFC1122 4.2.3.10, p. 104.
|
|
|
|
*
|
2001-11-22 04:50:44 +00:00
|
|
|
* If it is from this socket, drop it, it must be forged.
|
2002-02-25 08:29:21 +00:00
|
|
|
*
|
2002-02-27 13:52:06 +00:00
|
|
|
* Note that it is quite possible to receive unicast
|
|
|
|
* link-layer packets with a broadcast IP address. Use
|
|
|
|
* in_broadcast() to find them.
|
2001-11-22 04:50:44 +00:00
|
|
|
*/
|
|
|
|
if (m->m_flags & (M_BCAST|M_MCAST))
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2001-11-22 04:50:44 +00:00
|
|
|
if (isipv6) {
|
2007-03-21 18:49:43 +00:00
|
|
|
#ifdef INET6
|
|
|
|
if (th->th_dport == th->th_sport &&
|
|
|
|
IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &ip6->ip6_src))
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2001-11-22 04:50:44 +00:00
|
|
|
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
|
|
|
|
IN6_IS_ADDR_MULTICAST(&ip6->ip6_src))
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2007-03-21 18:49:43 +00:00
|
|
|
#endif
|
2002-08-17 02:05:25 +00:00
|
|
|
} else {
|
2007-03-21 18:49:43 +00:00
|
|
|
if (th->th_dport == th->th_sport &&
|
|
|
|
ip->ip_dst.s_addr == ip->ip_src.s_addr)
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2002-08-17 02:05:25 +00:00
|
|
|
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
|
|
|
|
IN_MULTICAST(ntohl(ip->ip_src.s_addr)) ||
|
|
|
|
ip->ip_src.s_addr == htonl(INADDR_BROADCAST) ||
|
|
|
|
in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif))
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2002-08-17 02:05:25 +00:00
|
|
|
}
|
2001-11-22 04:50:44 +00:00
|
|
|
/*
|
2007-03-21 18:49:43 +00:00
|
|
|
* SYN appears to be valid. Create compressed TCP state
|
|
|
|
* for syncache.
|
2001-11-22 04:50:44 +00:00
|
|
|
*/
|
|
|
|
if (so->so_qlen <= so->so_qlimit) {
|
2003-08-13 08:46:54 +00:00
|
|
|
#ifdef TCPDEBUG
|
|
|
|
if (so->so_options & SO_DEBUG)
|
|
|
|
tcp_trace(TA_INPUT, ostate, tp,
|
|
|
|
(void *)tcp_saveipgen, &tcp_savetcp, 0);
|
|
|
|
#endif
|
2006-06-26 15:35:25 +00:00
|
|
|
tcp_dooptions(&to, optp, optlen, TO_SYN);
|
2006-06-17 17:32:38 +00:00
|
|
|
if (!syncache_add(&inc, &to, th, inp, &so, m))
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2001-11-22 04:50:44 +00:00
|
|
|
/*
|
2007-03-21 18:49:43 +00:00
|
|
|
* Entry added to syncache, mbuf used to
|
|
|
|
* send SYN-ACK packet. Everything unlocked
|
|
|
|
* already.
|
2001-11-22 04:50:44 +00:00
|
|
|
*/
|
2007-03-21 18:49:43 +00:00
|
|
|
return;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2007-03-21 18:49:43 +00:00
|
|
|
/* Catch all. Everthing that makes it down here is junk. */
|
2007-03-23 20:16:50 +00:00
|
|
|
goto dropunlock;
|
2002-05-31 11:52:35 +00:00
|
|
|
}
|
2007-03-21 18:49:43 +00:00
|
|
|
|
2007-03-23 20:16:50 +00:00
|
|
|
/*
|
|
|
|
* Segment belongs to a connection in SYN_SENT, ESTABLISHED or
|
|
|
|
* later state. tcp_do_segment() always consumes the mbuf chain
|
|
|
|
* and unlocks the inpcb.
|
|
|
|
*/
|
|
|
|
if (tcp_do_segment(m, th, so, tp, drop_hdrlen, tlen))
|
|
|
|
goto findpcb; /* XXX: TIME_WAIT was nuked. */
|
|
|
|
return;
|
2001-11-22 04:50:44 +00:00
|
|
|
|
2007-03-23 20:16:50 +00:00
|
|
|
dropwithreset:
|
|
|
|
tcp_dropwithreset(m, th, tp, tlen, rstreason);
|
|
|
|
m = NULL; /* mbuf chain got consumed. */
|
|
|
|
dropunlock:
|
|
|
|
if (tp != NULL)
|
|
|
|
INP_UNLOCK(inp);
|
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
|
|
|
drop:
|
|
|
|
if (m != NULL)
|
|
|
|
m_freem(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
|
|
|
|
struct tcpcb *tp, int drop_hdrlen, int tlen)
|
|
|
|
{
|
|
|
|
int thflags, acked, ourfinisacked, needoutput = 0;
|
|
|
|
int headlocked = 1;
|
|
|
|
int rstreason, todrop, win;
|
|
|
|
u_long tiwin;
|
|
|
|
struct tcpopt to;
|
|
|
|
|
2007-03-24 22:15:02 +00:00
|
|
|
#ifdef TCPDEBUG
|
|
|
|
/*
|
|
|
|
* The size of tcp_saveipgen must be the size of the max ip header,
|
|
|
|
* now IPv6.
|
|
|
|
*/
|
|
|
|
u_char tcp_saveipgen[IP6_HDR_LEN];
|
|
|
|
struct tcphdr tcp_savetcp;
|
|
|
|
short ostate = 0;
|
|
|
|
#endif
|
2007-03-23 20:16:50 +00:00
|
|
|
thflags = th->th_flags;
|
|
|
|
|
|
|
|
INP_INFO_WLOCK_ASSERT(&tcbinfo);
|
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
|
|
|
KASSERT(tp->t_state > TCPS_LISTEN, ("%s: TCPS_LISTEN", __func__));
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Segment received on connection.
|
|
|
|
* Reset idle time and keep-alive timer.
|
|
|
|
*/
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->t_rcvtime = ticks;
|
1996-09-13 18:47:03 +00:00
|
|
|
if (TCPS_HAVEESTABLISHED(tp->t_state))
|
1999-08-30 21:17:07 +00:00
|
|
|
callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2006-02-28 23:05:59 +00:00
|
|
|
/*
|
|
|
|
* Unscale the window into a 32-bit value.
|
|
|
|
* This value is bogus for the TCPS_SYN_SENT state
|
|
|
|
* and is overwritten later.
|
|
|
|
*/
|
|
|
|
tiwin = th->th_win << tp->snd_scale;
|
|
|
|
|
2006-06-26 15:35:25 +00:00
|
|
|
/*
|
|
|
|
* Parse options on any incoming segment.
|
|
|
|
*/
|
2007-03-23 20:16:50 +00:00
|
|
|
tcp_dooptions(&to, (u_char *)(th + 1),
|
|
|
|
(th->th_off << 2) - sizeof(struct tcphdr),
|
|
|
|
(thflags & TH_SYN) ? TO_SYN : 0);
|
2006-06-26 15:35:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If echoed timestamp is later than the current time,
|
2006-09-13 13:08:27 +00:00
|
|
|
* fall back to non RFC1323 RTT calculation. Normalize
|
|
|
|
* timestamp if syncookies were used when this connection
|
|
|
|
* was established.
|
2006-06-26 15:35:25 +00:00
|
|
|
*/
|
2006-09-13 13:08:27 +00:00
|
|
|
if ((to.to_flags & TOF_TS) && (to.to_tsecr != 0)) {
|
2006-09-26 01:21:46 +00:00
|
|
|
to.to_tsecr -= tp->ts_offset;
|
2006-09-13 13:08:27 +00:00
|
|
|
if (TSTMP_GT(to.to_tsecr, ticks))
|
|
|
|
to.to_tsecr = 0;
|
|
|
|
}
|
2006-06-26 15:35:25 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
2003-11-20 20:07:39 +00:00
|
|
|
* Process options only when we get SYN/ACK back. The SYN case
|
|
|
|
* for incoming connections is handled in tcp_syncache.
|
|
|
|
* XXX this is traditional behavior, may need to be cleaned up.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2005-06-29 21:36:49 +00:00
|
|
|
if (tp->t_state == TCPS_SYN_SENT && (thflags & TH_SYN)) {
|
2006-02-28 23:05:59 +00:00
|
|
|
if ((to.to_flags & TOF_SCALE) &&
|
|
|
|
(tp->t_flags & TF_REQ_SCALE)) {
|
2001-11-22 04:50:44 +00:00
|
|
|
tp->t_flags |= TF_RCVD_SCALE;
|
2007-03-15 15:59:28 +00:00
|
|
|
tp->snd_scale = to.to_wscale;
|
2006-02-28 23:05:59 +00:00
|
|
|
tp->snd_wnd = th->th_win << tp->snd_scale;
|
|
|
|
tiwin = tp->snd_wnd;
|
2001-11-22 04:50:44 +00:00
|
|
|
}
|
|
|
|
if (to.to_flags & TOF_TS) {
|
|
|
|
tp->t_flags |= TF_RCVD_TSTMP;
|
|
|
|
tp->ts_recent = to.to_tsval;
|
|
|
|
tp->ts_recent_age = ticks;
|
|
|
|
}
|
2007-02-28 20:48:00 +00:00
|
|
|
/* Initial send window, already scaled. */
|
|
|
|
tp->snd_wnd = th->th_win;
|
2001-11-22 04:50:44 +00:00
|
|
|
if (to.to_flags & TOF_MSS)
|
|
|
|
tcp_mss(tp, to.to_mss);
|
2004-06-23 21:04:37 +00:00
|
|
|
if (tp->sack_enable) {
|
2007-03-23 18:33:21 +00:00
|
|
|
if (!(to.to_flags & TOF_SACKPERM))
|
2004-06-23 21:04:37 +00:00
|
|
|
tp->sack_enable = 0;
|
|
|
|
else
|
|
|
|
tp->t_flags |= TF_SACK_PERMIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1994-05-24 10:09:53 +00:00
|
|
|
* 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.
|
1995-02-09 23:13:27 +00:00
|
|
|
* Make sure that the hidden state-flags are also off.
|
|
|
|
* Since we check for TCPS_ESTABLISHED above, it can only
|
|
|
|
* be TH_NEEDSYN.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
if (tp->t_state == TCPS_ESTABLISHED &&
|
2000-01-09 19:17:30 +00:00
|
|
|
(thflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
|
1995-02-09 23:13:27 +00:00
|
|
|
((tp->t_flags & (TF_NEEDSYN|TF_NEEDFIN)) == 0) &&
|
2001-11-22 04:50:44 +00:00
|
|
|
((to.to_flags & TOF_TS) == 0 ||
|
1995-02-09 23:13:27 +00:00
|
|
|
TSTMP_GEQ(to.to_tsval, tp->ts_recent)) &&
|
2004-11-02 22:22:22 +00:00
|
|
|
th->th_seq == tp->rcv_nxt && tiwin && tiwin == tp->snd_wnd &&
|
|
|
|
tp->snd_nxt == tp->snd_max) {
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1994-05-24 10:09:53 +00:00
|
|
|
* If last ACK falls within this segment's sequence numbers,
|
1995-02-09 23:13:27 +00:00
|
|
|
* record the timestamp.
|
|
|
|
* NOTE that the test is modified according to the latest
|
|
|
|
* proposal of the tcplw@cray.com list (Braden 1993/04/26).
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2001-11-22 04:50:44 +00:00
|
|
|
if ((to.to_flags & TOF_TS) != 0 &&
|
2002-08-17 02:05:25 +00:00
|
|
|
SEQ_LEQ(th->th_seq, tp->last_ack_sent)) {
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->ts_recent_age = ticks;
|
1995-02-09 23:13:27 +00:00
|
|
|
tp->ts_recent = to.to_tsval;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
if (tlen == 0) {
|
|
|
|
if (SEQ_GT(th->th_ack, tp->snd_una) &&
|
|
|
|
SEQ_LEQ(th->th_ack, tp->snd_max) &&
|
1996-03-22 18:09:21 +00:00
|
|
|
tp->snd_cwnd >= tp->snd_wnd &&
|
2004-06-23 21:04:37 +00:00
|
|
|
((!tcp_do_newreno && !tp->sack_enable &&
|
2003-01-13 11:01:20 +00:00
|
|
|
tp->t_dupacks < tcprexmtthresh) ||
|
2004-08-16 18:32:07 +00:00
|
|
|
((tcp_do_newreno || tp->sack_enable) &&
|
2007-03-23 18:33:21 +00:00
|
|
|
!IN_FASTRECOVERY(tp) &&
|
|
|
|
(to.to_flags & TOF_SACK) == 0 &&
|
2005-07-01 22:54:18 +00:00
|
|
|
TAILQ_EMPTY(&tp->snd_holes)))) {
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked,
|
|
|
|
("%s: headlocked", __func__));
|
2004-12-25 22:23:13 +00:00
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
|
|
|
headlocked = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* this is a pure ack for outstanding data.
|
|
|
|
*/
|
|
|
|
++tcpstat.tcps_predack;
|
1999-08-30 21:17:07 +00:00
|
|
|
/*
|
|
|
|
* "bad retransmit" recovery
|
|
|
|
*/
|
|
|
|
if (tp->t_rxtshift == 1 &&
|
|
|
|
ticks < tp->t_badrxtwin) {
|
2003-01-13 11:01:20 +00:00
|
|
|
++tcpstat.tcps_sndrexmitbad;
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->snd_cwnd = tp->snd_cwnd_prev;
|
|
|
|
tp->snd_ssthresh =
|
|
|
|
tp->snd_ssthresh_prev;
|
2003-07-15 21:49:53 +00:00
|
|
|
tp->snd_recover = tp->snd_recover_prev;
|
|
|
|
if (tp->t_flags & TF_WASFRECOVERY)
|
|
|
|
ENTER_FASTRECOVERY(tp);
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->snd_nxt = tp->snd_max;
|
|
|
|
tp->t_badrxtwin = 0;
|
|
|
|
}
|
Guido reported an interesting bug where an FTP connection between a
Windows 2000 box and a FreeBSD box could stall. The problem turned out
to be a timestamp reply bug in the W2K TCP stack. FreeBSD sends a
timestamp with the SYN, W2K returns a timestamp of 0 in the SYN+ACK
causing FreeBSD to calculate an insane SRTT and RTT, resulting in
a maximal retransmit timeout (60 seconds). If there is any packet
loss on the connection for the first six or so packets the retransmit
case may be hit (the window will still be too small for fast-retransmit),
causing a 60+ second pause. The W2K box gives up and closes the
connection.
This commit works around the W2K bug.
15:04:59.374588 FREEBSD.20 > W2K.1036: S 1420807004:1420807004(0) win 65535 <mss 1460,nop,wscale 2,nop,nop,timestamp 188297344 0> (DF) [tos 0x8]
15:04:59.377558 W2K.1036 > FREEBSD.20: S 4134611565:4134611565(0) ack 1420807005 win 17520 <mss 1460,nop,wscale 0,nop,nop,timestamp 0 0> (DF)
Bug reported by: Guido van Rooij <guido@gvr.org>
2002-09-17 22:21:37 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Recalculate the transmit timer / rtt.
|
|
|
|
*
|
|
|
|
* Some boxes send broken timestamp replies
|
2004-08-16 18:32:07 +00:00
|
|
|
* during the SYN+ACK phase, ignore
|
Guido reported an interesting bug where an FTP connection between a
Windows 2000 box and a FreeBSD box could stall. The problem turned out
to be a timestamp reply bug in the W2K TCP stack. FreeBSD sends a
timestamp with the SYN, W2K returns a timestamp of 0 in the SYN+ACK
causing FreeBSD to calculate an insane SRTT and RTT, resulting in
a maximal retransmit timeout (60 seconds). If there is any packet
loss on the connection for the first six or so packets the retransmit
case may be hit (the window will still be too small for fast-retransmit),
causing a 60+ second pause. The W2K box gives up and closes the
connection.
This commit works around the W2K bug.
15:04:59.374588 FREEBSD.20 > W2K.1036: S 1420807004:1420807004(0) win 65535 <mss 1460,nop,wscale 2,nop,nop,timestamp 188297344 0> (DF) [tos 0x8]
15:04:59.377558 W2K.1036 > FREEBSD.20: S 4134611565:4134611565(0) ack 1420807005 win 17520 <mss 1460,nop,wscale 0,nop,nop,timestamp 0 0> (DF)
Bug reported by: Guido van Rooij <guido@gvr.org>
2002-09-17 22:21:37 +00:00
|
|
|
* 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) {
|
2006-02-16 19:38:07 +00:00
|
|
|
if (!tp->t_rttlow ||
|
|
|
|
tp->t_rttlow > ticks - to.to_tsecr)
|
|
|
|
tp->t_rttlow = ticks - to.to_tsecr;
|
1995-02-09 23:13:27 +00:00
|
|
|
tcp_xmit_timer(tp,
|
1999-08-30 21:17:07 +00:00
|
|
|
ticks - to.to_tsecr + 1);
|
Guido reported an interesting bug where an FTP connection between a
Windows 2000 box and a FreeBSD box could stall. The problem turned out
to be a timestamp reply bug in the W2K TCP stack. FreeBSD sends a
timestamp with the SYN, W2K returns a timestamp of 0 in the SYN+ACK
causing FreeBSD to calculate an insane SRTT and RTT, resulting in
a maximal retransmit timeout (60 seconds). If there is any packet
loss on the connection for the first six or so packets the retransmit
case may be hit (the window will still be too small for fast-retransmit),
causing a 60+ second pause. The W2K box gives up and closes the
connection.
This commit works around the W2K bug.
15:04:59.374588 FREEBSD.20 > W2K.1036: S 1420807004:1420807004(0) win 65535 <mss 1460,nop,wscale 2,nop,nop,timestamp 188297344 0> (DF) [tos 0x8]
15:04:59.377558 W2K.1036 > FREEBSD.20: S 4134611565:4134611565(0) ack 1420807005 win 17520 <mss 1460,nop,wscale 0,nop,nop,timestamp 0 0> (DF)
Bug reported by: Guido van Rooij <guido@gvr.org>
2002-09-17 22:21:37 +00:00
|
|
|
} else if (tp->t_rtttime &&
|
2007-03-23 19:11:22 +00:00
|
|
|
SEQ_GT(th->th_ack, tp->t_rtseq)) {
|
2006-02-16 19:38:07 +00:00
|
|
|
if (!tp->t_rttlow ||
|
|
|
|
tp->t_rttlow > ticks - tp->t_rtttime)
|
|
|
|
tp->t_rttlow = ticks - tp->t_rtttime;
|
2002-08-17 02:05:25 +00:00
|
|
|
tcp_xmit_timer(tp,
|
|
|
|
ticks - tp->t_rtttime);
|
Guido reported an interesting bug where an FTP connection between a
Windows 2000 box and a FreeBSD box could stall. The problem turned out
to be a timestamp reply bug in the W2K TCP stack. FreeBSD sends a
timestamp with the SYN, W2K returns a timestamp of 0 in the SYN+ACK
causing FreeBSD to calculate an insane SRTT and RTT, resulting in
a maximal retransmit timeout (60 seconds). If there is any packet
loss on the connection for the first six or so packets the retransmit
case may be hit (the window will still be too small for fast-retransmit),
causing a 60+ second pause. The W2K box gives up and closes the
connection.
This commit works around the W2K bug.
15:04:59.374588 FREEBSD.20 > W2K.1036: S 1420807004:1420807004(0) win 65535 <mss 1460,nop,wscale 2,nop,nop,timestamp 188297344 0> (DF) [tos 0x8]
15:04:59.377558 W2K.1036 > FREEBSD.20: S 4134611565:4134611565(0) ack 1420807005 win 17520 <mss 1460,nop,wscale 0,nop,nop,timestamp 0 0> (DF)
Bug reported by: Guido van Rooij <guido@gvr.org>
2002-09-17 22:21:37 +00:00
|
|
|
}
|
2002-08-17 18:26:02 +00:00
|
|
|
tcp_xmit_bandwidth_limit(tp, th->th_ack);
|
2000-01-09 19:17:30 +00:00
|
|
|
acked = th->th_ack - tp->snd_una;
|
1994-05-24 10:09:53 +00:00
|
|
|
tcpstat.tcps_rcvackpack++;
|
|
|
|
tcpstat.tcps_rcvackbyte += acked;
|
|
|
|
sbdrop(&so->so_snd, acked);
|
2003-07-15 21:49:53 +00:00
|
|
|
if (SEQ_GT(tp->snd_una, tp->snd_recover) &&
|
|
|
|
SEQ_LEQ(th->th_ack, tp->snd_recover))
|
|
|
|
tp->snd_recover = th->th_ack - 1;
|
|
|
|
tp->snd_una = th->th_ack;
|
2002-10-31 23:24:13 +00:00
|
|
|
/*
|
|
|
|
* pull snd_wl2 up to prevent seq wrap relative
|
|
|
|
* to th_ack.
|
|
|
|
*/
|
2003-01-13 11:01:20 +00:00
|
|
|
tp->snd_wl2 = th->th_ack;
|
2002-08-15 17:13:18 +00:00
|
|
|
tp->t_dupacks = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
m_freem(m);
|
2000-01-09 19:17:30 +00:00
|
|
|
ND6_HINT(tp); /* some progress has been done */
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
2003-08-13 08:46:54 +00:00
|
|
|
|
|
|
|
#ifdef TCPDEBUG
|
|
|
|
if (so->so_options & SO_DEBUG)
|
|
|
|
tcp_trace(TA_INPUT, ostate, tp,
|
|
|
|
(void *)tcp_saveipgen,
|
|
|
|
&tcp_savetcp, 0);
|
|
|
|
#endif
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
if (tp->snd_una == tp->snd_max)
|
1999-08-30 21:17:07 +00:00
|
|
|
callout_stop(tp->tt_rexmt);
|
|
|
|
else if (!callout_active(tp->tt_persist))
|
2004-08-16 18:32:07 +00:00
|
|
|
callout_reset(tp->tt_rexmt,
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->t_rxtcur,
|
|
|
|
tcp_timer_rexmt, tp);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1998-05-31 18:42:49 +00:00
|
|
|
sowwakeup(so);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (so->so_snd.sb_cc)
|
|
|
|
(void) tcp_output(tp);
|
2003-02-22 21:54:57 +00:00
|
|
|
goto check_delack;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
} else if (th->th_ack == tp->snd_una &&
|
|
|
|
LIST_EMPTY(&tp->t_segq) &&
|
|
|
|
tlen <= sbspace(&so->so_rcv)) {
|
2007-02-01 18:32:13 +00:00
|
|
|
int newsize = 0; /* automatic sockbuf scaling */
|
|
|
|
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: headlocked", __func__));
|
2004-12-25 22:23:13 +00:00
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
|
|
|
headlocked = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* this is a pure, in-sequence data packet
|
|
|
|
* with nothing on the reassembly queue and
|
|
|
|
* we have enough buffer space to take it.
|
|
|
|
*/
|
2004-06-23 21:04:37 +00:00
|
|
|
/* Clean receiver SACK report if present */
|
|
|
|
if (tp->sack_enable && tp->rcv_numsacks)
|
|
|
|
tcp_clean_sackreport(tp);
|
1994-05-24 10:09:53 +00:00
|
|
|
++tcpstat.tcps_preddat;
|
2000-01-09 19:17:30 +00:00
|
|
|
tp->rcv_nxt += tlen;
|
2002-10-31 23:24:13 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
1994-05-24 10:09:53 +00:00
|
|
|
tcpstat.tcps_rcvpack++;
|
2000-01-09 19:17:30 +00:00
|
|
|
tcpstat.tcps_rcvbyte += tlen;
|
|
|
|
ND6_HINT(tp); /* some progress has been done */
|
2003-08-13 08:46:54 +00:00
|
|
|
#ifdef TCPDEBUG
|
|
|
|
if (so->so_options & SO_DEBUG)
|
|
|
|
tcp_trace(TA_INPUT, ostate, tp,
|
|
|
|
(void *)tcp_saveipgen, &tcp_savetcp, 0);
|
|
|
|
#endif
|
2007-02-01 18:32:13 +00:00
|
|
|
/*
|
|
|
|
* 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. the number of bytes received during the time it takes
|
|
|
|
* one timestamp to be reflected back to us (the RTT);
|
|
|
|
* 2. received bytes per RTT is within seven eighth of the
|
|
|
|
* current socket buffer size;
|
|
|
|
* 3. 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.
|
|
|
|
*/
|
|
|
|
if (tcp_do_autorcvbuf &&
|
|
|
|
to.to_tsecr &&
|
|
|
|
(so->so_rcv.sb_flags & SB_AUTOSIZE)) {
|
|
|
|
if (to.to_tsecr > tp->rfbuf_ts &&
|
|
|
|
to.to_tsecr - tp->rfbuf_ts < hz) {
|
|
|
|
if (tp->rfbuf_cnt >
|
|
|
|
(so->so_rcv.sb_hiwat / 8 * 7) &&
|
|
|
|
so->so_rcv.sb_hiwat <
|
|
|
|
tcp_autorcvbuf_max) {
|
|
|
|
newsize =
|
|
|
|
min(so->so_rcv.sb_hiwat +
|
|
|
|
tcp_autorcvbuf_inc,
|
|
|
|
tcp_autorcvbuf_max);
|
|
|
|
}
|
|
|
|
/* Start over with next RTT. */
|
|
|
|
tp->rfbuf_ts = 0;
|
|
|
|
tp->rfbuf_cnt = 0;
|
|
|
|
} else
|
|
|
|
tp->rfbuf_cnt += tlen; /* add up */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add data to socket buffer. */
|
Reduce the number of unnecessary unlock-relocks on socket buffer mutexes
associated with performing a wakeup on the socket buffer:
- When performing an sbappend*() followed by a so[rw]wakeup(), explicitly
acquire the socket buffer lock and use the _locked() variants of both
calls. Note that the _locked() sowakeup() versions unlock the mutex on
return. This is done in uipc_send(), divert_packet(), mroute
socket_send(), raw_append(), tcp_reass(), tcp_input(), and udp_append().
- When the socket buffer lock is dropped before a sowakeup(), remove the
explicit unlock and use the _locked() sowakeup() variant. This is done
in soisdisconnecting(), soisdisconnected() when setting the can't send/
receive flags and dropping data, and in uipc_rcvd() which adjusting
back-pressure on the sockets.
For UNIX domain sockets running mpsafe with a contention-intensive SMP
mysql benchmark, this results in a 1.6% query rate improvement due to
reduce mutex costs.
2004-06-26 19:10:39 +00:00
|
|
|
SOCKBUF_LOCK(&so->so_rcv);
|
2004-06-14 18:16:22 +00:00
|
|
|
if (so->so_rcv.sb_state & SBS_CANTRCVMORE) {
|
2002-09-22 02:54:07 +00:00
|
|
|
m_freem(m);
|
|
|
|
} else {
|
2007-02-01 18:32:13 +00:00
|
|
|
/*
|
|
|
|
* Set new socket buffer size.
|
|
|
|
* Give up when limit is reached.
|
|
|
|
*/
|
|
|
|
if (newsize)
|
|
|
|
if (!sbreserve_locked(&so->so_rcv,
|
|
|
|
newsize, so, curthread))
|
|
|
|
so->so_rcv.sb_flags &= ~SB_AUTOSIZE;
|
2002-09-22 02:54:07 +00:00
|
|
|
m_adj(m, drop_hdrlen); /* delayed header drop */
|
Reduce the number of unnecessary unlock-relocks on socket buffer mutexes
associated with performing a wakeup on the socket buffer:
- When performing an sbappend*() followed by a so[rw]wakeup(), explicitly
acquire the socket buffer lock and use the _locked() variants of both
calls. Note that the _locked() sowakeup() versions unlock the mutex on
return. This is done in uipc_send(), divert_packet(), mroute
socket_send(), raw_append(), tcp_reass(), tcp_input(), and udp_append().
- When the socket buffer lock is dropped before a sowakeup(), remove the
explicit unlock and use the _locked() sowakeup() variant. This is done
in soisdisconnecting(), soisdisconnected() when setting the can't send/
receive flags and dropping data, and in uipc_rcvd() which adjusting
back-pressure on the sockets.
For UNIX domain sockets running mpsafe with a contention-intensive SMP
mysql benchmark, this results in a 1.6% query rate improvement due to
reduce mutex costs.
2004-06-26 19:10:39 +00:00
|
|
|
sbappendstream_locked(&so->so_rcv, m);
|
2002-09-22 02:54:07 +00:00
|
|
|
}
|
Reduce the number of unnecessary unlock-relocks on socket buffer mutexes
associated with performing a wakeup on the socket buffer:
- When performing an sbappend*() followed by a so[rw]wakeup(), explicitly
acquire the socket buffer lock and use the _locked() variants of both
calls. Note that the _locked() sowakeup() versions unlock the mutex on
return. This is done in uipc_send(), divert_packet(), mroute
socket_send(), raw_append(), tcp_reass(), tcp_input(), and udp_append().
- When the socket buffer lock is dropped before a sowakeup(), remove the
explicit unlock and use the _locked() sowakeup() variant. This is done
in soisdisconnecting(), soisdisconnected() when setting the can't send/
receive flags and dropping data, and in uipc_rcvd() which adjusting
back-pressure on the sockets.
For UNIX domain sockets running mpsafe with a contention-intensive SMP
mysql benchmark, this results in a 1.6% query rate improvement due to
reduce mutex costs.
2004-06-26 19:10:39 +00:00
|
|
|
sorwakeup_locked(so);
|
2001-02-25 15:17:24 +00:00
|
|
|
if (DELAY_ACK(tp)) {
|
2003-02-19 21:18:23 +00:00
|
|
|
tp->t_flags |= TF_DELACK;
|
1998-02-26 05:25:39 +00:00
|
|
|
} else {
|
1995-03-27 07:12:24 +00:00
|
|
|
tp->t_flags |= TF_ACKNOW;
|
|
|
|
tcp_output(tp);
|
|
|
|
}
|
2003-02-22 21:54:57 +00:00
|
|
|
goto check_delack;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
win = sbspace(&so->so_rcv);
|
|
|
|
if (win < 0)
|
|
|
|
win = 0;
|
1997-07-01 05:42:16 +00:00
|
|
|
tp->rcv_wnd = imax(win, (int)(tp->rcv_adv - tp->rcv_nxt));
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2007-02-01 18:32:13 +00:00
|
|
|
/* Reset receive buffer auto scaling when not in bulk receive mode. */
|
|
|
|
tp->rfbuf_ts = 0;
|
|
|
|
tp->rfbuf_cnt = 0;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
switch (tp->t_state) {
|
|
|
|
|
1998-01-21 02:05:59 +00:00
|
|
|
/*
|
|
|
|
* If the state is SYN_RECEIVED:
|
|
|
|
* if seg contains an ACK, but not for our SYN/ACK, send a RST.
|
|
|
|
*/
|
|
|
|
case TCPS_SYN_RECEIVED:
|
2000-01-09 19:17:30 +00:00
|
|
|
if ((thflags & TH_ACK) &&
|
|
|
|
(SEQ_LEQ(th->th_ack, tp->snd_una) ||
|
2001-02-11 07:39:51 +00:00
|
|
|
SEQ_GT(th->th_ack, tp->snd_max))) {
|
|
|
|
rstreason = BANDLIM_RST_OPENPORT;
|
|
|
|
goto dropwithreset;
|
|
|
|
}
|
1998-01-21 02:05:59 +00:00
|
|
|
break;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* 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 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:
|
2000-01-09 19:17:30 +00:00
|
|
|
if ((thflags & TH_ACK) &&
|
|
|
|
(SEQ_LEQ(th->th_ack, tp->iss) ||
|
|
|
|
SEQ_GT(th->th_ack, tp->snd_max))) {
|
2004-11-02 22:22:22 +00:00
|
|
|
rstreason = BANDLIM_UNLIMITED;
|
|
|
|
goto dropwithreset;
|
1995-02-09 23:13:27 +00:00
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
if (thflags & TH_RST) {
|
2005-06-01 12:03:18 +00:00
|
|
|
if (thflags & TH_ACK) {
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: after_listen: "
|
|
|
|
"tcp_drop.2: head not locked", __func__));
|
1994-05-24 10:09:53 +00:00
|
|
|
tp = tcp_drop(tp, ECONNREFUSED);
|
2005-06-01 12:03:18 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
goto drop;
|
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
if ((thflags & TH_SYN) == 0)
|
1994-05-24 10:09:53 +00:00
|
|
|
goto drop;
|
2006-02-28 23:05:59 +00:00
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
tp->irs = th->th_seq;
|
1994-05-24 10:09:53 +00:00
|
|
|
tcp_rcvseqinit(tp);
|
2000-01-09 19:17:30 +00:00
|
|
|
if (thflags & TH_ACK) {
|
1995-11-03 22:31:54 +00:00
|
|
|
tcpstat.tcps_connects++;
|
|
|
|
soisconnected(so);
|
2002-07-31 19:06:49 +00:00
|
|
|
#ifdef MAC
|
2004-06-13 02:50:07 +00:00
|
|
|
SOCK_LOCK(so);
|
2002-07-31 19:06:49 +00:00
|
|
|
mac_set_socket_peer_from_mbuf(m, so);
|
2004-06-13 02:50:07 +00:00
|
|
|
SOCK_UNLOCK(so);
|
2002-07-31 19:06:49 +00:00
|
|
|
#endif
|
1995-11-03 22:31:54 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
1995-02-09 23:13:27 +00:00
|
|
|
tp->rcv_adv += tp->rcv_wnd;
|
|
|
|
tp->snd_una++; /* SYN is acked */
|
|
|
|
/*
|
|
|
|
* If there's data, delay ACK; if there's also a FIN
|
|
|
|
* ACKNOW will be turned on later.
|
|
|
|
*/
|
2001-02-25 15:17:24 +00:00
|
|
|
if (DELAY_ACK(tp) && tlen != 0)
|
2004-08-16 18:32:07 +00:00
|
|
|
callout_reset(tp->tt_delack, tcp_delacktime,
|
|
|
|
tcp_timer_delack, tp);
|
1995-02-09 23:13:27 +00:00
|
|
|
else
|
|
|
|
tp->t_flags |= TF_ACKNOW;
|
|
|
|
/*
|
|
|
|
* Received <SYN,ACK> in SYN_SENT[*] state.
|
|
|
|
* Transitions:
|
|
|
|
* SYN_SENT --> ESTABLISHED
|
|
|
|
* SYN_SENT* --> FIN_WAIT_1
|
|
|
|
*/
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->t_starttime = ticks;
|
1995-02-09 23:13:27 +00:00
|
|
|
if (tp->t_flags & TF_NEEDFIN) {
|
|
|
|
tp->t_state = TCPS_FIN_WAIT_1;
|
|
|
|
tp->t_flags &= ~TF_NEEDFIN;
|
2000-01-09 19:17:30 +00:00
|
|
|
thflags &= ~TH_SYN;
|
1996-09-13 18:47:03 +00:00
|
|
|
} else {
|
1995-02-09 23:13:27 +00:00
|
|
|
tp->t_state = TCPS_ESTABLISHED;
|
1999-08-30 21:17:07 +00:00
|
|
|
callout_reset(tp->tt_keep, tcp_keepidle,
|
|
|
|
tcp_timer_keep, tp);
|
1996-09-13 18:47:03 +00:00
|
|
|
}
|
1995-02-09 23:13:27 +00:00
|
|
|
} else {
|
2002-08-17 02:05:25 +00:00
|
|
|
/*
|
2004-08-16 18:32:07 +00:00
|
|
|
* Received initial SYN in SYN-SENT[*] state =>
|
|
|
|
* simultaneous open. If segment contains CC option
|
|
|
|
* and there is a cached CC, apply TAO test.
|
|
|
|
* If it succeeds, connection is * half-synchronized.
|
|
|
|
* Otherwise, do 3-way handshake:
|
|
|
|
* SYN-SENT -> SYN-RECEIVED
|
|
|
|
* SYN-SENT* -> SYN-RECEIVED*
|
|
|
|
* If there was no CC option, clear cached CC value.
|
|
|
|
*/
|
2006-02-23 21:14:34 +00:00
|
|
|
tp->t_flags |= (TF_ACKNOW | TF_NEEDSYN);
|
1999-08-30 21:17:07 +00:00
|
|
|
callout_stop(tp->tt_rexmt);
|
2004-11-02 22:22:22 +00:00
|
|
|
tp->t_state = TCPS_SYN_RECEIVED;
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: trimthenstep6: head not locked",
|
|
|
|
__func__));
|
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
2004-07-12 19:28:07 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
2000-01-09 19:17:30 +00:00
|
|
|
* Advance th->th_seq to correspond to first data byte.
|
1994-05-24 10:09:53 +00:00
|
|
|
* If data, trim to stay within window,
|
|
|
|
* dropping FIN if necessary.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
th->th_seq++;
|
|
|
|
if (tlen > tp->rcv_wnd) {
|
|
|
|
todrop = tlen - tp->rcv_wnd;
|
1994-05-24 10:09:53 +00:00
|
|
|
m_adj(m, -todrop);
|
2000-01-09 19:17:30 +00:00
|
|
|
tlen = tp->rcv_wnd;
|
|
|
|
thflags &= ~TH_FIN;
|
1994-05-24 10:09:53 +00:00
|
|
|
tcpstat.tcps_rcvpackafterwin++;
|
|
|
|
tcpstat.tcps_rcvbyteafterwin += todrop;
|
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
tp->snd_wl1 = th->th_seq - 1;
|
|
|
|
tp->rcv_up = th->th_seq;
|
1995-02-09 23:13:27 +00:00
|
|
|
/*
|
2002-08-17 02:05:25 +00:00
|
|
|
* 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.
|
1995-05-30 08:16:23 +00:00
|
|
|
*/
|
2004-08-16 18:32:07 +00:00
|
|
|
if (thflags & TH_ACK)
|
1995-02-09 23:13:27 +00:00
|
|
|
goto process_ACK;
|
2002-08-17 02:05:25 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
goto step6;
|
2002-08-17 02:05:25 +00:00
|
|
|
|
1995-02-09 23:13:27 +00:00
|
|
|
/*
|
|
|
|
* If the state is LAST_ACK or CLOSING or TIME_WAIT:
|
2004-11-02 22:22:22 +00:00
|
|
|
* do normal processing.
|
1995-02-09 23:13:27 +00:00
|
|
|
*
|
2004-11-02 22:22:22 +00:00
|
|
|
* NB: Leftover from RFC1644 T/TCP. Cases to be reused later.
|
1995-05-30 08:16:23 +00:00
|
|
|
*/
|
1995-02-09 23:13:27 +00:00
|
|
|
case TCPS_LAST_ACK:
|
|
|
|
case TCPS_CLOSING:
|
|
|
|
case TCPS_TIME_WAIT:
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(tp->t_state != TCPS_TIME_WAIT, ("%s: timewait",
|
|
|
|
__func__));
|
2004-08-16 18:32:07 +00:00
|
|
|
break; /* continue normal processing */
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* States other than LISTEN or SYN_SENT.
|
1998-09-11 16:04:03 +00:00
|
|
|
* 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.
|
1995-02-09 23:13:27 +00:00
|
|
|
* Then check the connection count, if present.
|
1995-05-30 08:16:23 +00:00
|
|
|
* Then check that at least some bytes of segment are within
|
1994-05-24 10:09:53 +00:00
|
|
|
* receive window. If segment begins before rcv_nxt,
|
|
|
|
* drop leading data (and SYN); if nothing left, just ack.
|
1995-02-09 23:13:27 +00:00
|
|
|
*
|
1998-09-11 16:04:03 +00:00
|
|
|
*
|
|
|
|
* If the RST bit is set, check the sequence number to see
|
|
|
|
* if this is a valid reset segment.
|
|
|
|
* RFC 793 page 37:
|
|
|
|
* In all states except SYN-SENT, all reset (RST) segments
|
|
|
|
* are validated by checking their SEQ-fields. A reset is
|
|
|
|
* valid if its sequence number is in the window.
|
|
|
|
* Note: this does not take into account delayed ACKs, so
|
|
|
|
* we should test against last_ack_sent instead of rcv_nxt.
|
1999-12-11 04:05:52 +00:00
|
|
|
* The sequence number in the reset segment is normally an
|
|
|
|
* echo of our outgoing acknowlegement numbers, but some hosts
|
|
|
|
* send a reset with the sequence number at the rightmost edge
|
|
|
|
* of our receive window, and we have to handle this case.
|
2004-04-26 02:56:31 +00:00
|
|
|
* Note 2: Paul Watson's paper "Slipping in the Window" has shown
|
|
|
|
* that brute force RST attacks are possible. To combat this,
|
|
|
|
* we use a much stricter check while in the ESTABLISHED state,
|
|
|
|
* only accepting RSTs where the sequence number is equal to
|
|
|
|
* last_ack_sent. In all other states (the states in which a
|
2004-08-16 18:32:07 +00:00
|
|
|
* RST is more likely), the more permissive check is used.
|
1998-09-11 16:04:03 +00:00
|
|
|
* If we have multiple segments in flight, the intial reset
|
|
|
|
* segment sequence numbers will be to the left of last_ack_sent,
|
|
|
|
* but they will eventually catch up.
|
|
|
|
* In any case, it never made sense to trim reset segments to
|
|
|
|
* fit the receive window since RFC 1122 says:
|
|
|
|
* 4.2.2.12 RST Segment: RFC-793 Section 3.4
|
|
|
|
*
|
|
|
|
* A TCP SHOULD allow a received RST segment to include data.
|
|
|
|
*
|
|
|
|
* DISCUSSION
|
|
|
|
* It has been suggested that a RST segment could contain
|
|
|
|
* ASCII text that encoded and explained the cause of the
|
|
|
|
* RST. No standard has yet been established for such
|
|
|
|
* data.
|
|
|
|
*
|
|
|
|
* If the reset segment passes the sequence number test examine
|
|
|
|
* the state:
|
|
|
|
* SYN_RECEIVED STATE:
|
|
|
|
* If passive open, return to LISTEN state.
|
|
|
|
* If active open, inform user that connection was refused.
|
2001-08-23 22:34:29 +00:00
|
|
|
* ESTABLISHED, FIN_WAIT_1, FIN_WAIT_2, CLOSE_WAIT STATES:
|
1998-09-11 16:04:03 +00:00
|
|
|
* Inform user that connection was reset, and close tcb.
|
1999-07-18 14:42:48 +00:00
|
|
|
* CLOSING, LAST_ACK STATES:
|
1998-09-11 16:04:03 +00:00
|
|
|
* Close the tcb.
|
1999-07-18 14:42:48 +00:00
|
|
|
* TIME_WAIT STATE:
|
1998-09-11 16:04:03 +00:00
|
|
|
* Drop the segment - see Stevens, vol. 2, p. 964 and
|
|
|
|
* RFC 1337.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if (thflags & TH_RST) {
|
2007-03-07 23:21:59 +00:00
|
|
|
if (SEQ_GEQ(th->th_seq, tp->last_ack_sent - 1) &&
|
|
|
|
SEQ_LEQ(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) {
|
1998-09-11 16:04:03 +00:00
|
|
|
switch (tp->t_state) {
|
|
|
|
|
|
|
|
case TCPS_SYN_RECEIVED:
|
|
|
|
so->so_error = ECONNREFUSED;
|
|
|
|
goto close;
|
|
|
|
|
|
|
|
case TCPS_ESTABLISHED:
|
2007-03-07 23:21:59 +00:00
|
|
|
if (tcp_insecure_rst == 0 &&
|
|
|
|
!(SEQ_GEQ(th->th_seq, tp->rcv_nxt - 1) &&
|
|
|
|
SEQ_LEQ(th->th_seq, tp->rcv_nxt + 1)) &&
|
|
|
|
!(SEQ_GEQ(th->th_seq, tp->last_ack_sent - 1) &&
|
|
|
|
SEQ_LEQ(th->th_seq, tp->last_ack_sent + 1))) {
|
2004-04-26 02:56:31 +00:00
|
|
|
tcpstat.tcps_badrst++;
|
|
|
|
goto drop;
|
|
|
|
}
|
1998-09-11 16:04:03 +00:00
|
|
|
case TCPS_FIN_WAIT_1:
|
|
|
|
case TCPS_FIN_WAIT_2:
|
|
|
|
case TCPS_CLOSE_WAIT:
|
|
|
|
so->so_error = ECONNRESET;
|
|
|
|
close:
|
|
|
|
tp->t_state = TCPS_CLOSED;
|
|
|
|
tcpstat.tcps_drops++;
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: trimthenstep6: "
|
|
|
|
"tcp_close: head not locked", __func__));
|
1998-09-11 16:04:03 +00:00
|
|
|
tp = tcp_close(tp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCPS_CLOSING:
|
|
|
|
case TCPS_LAST_ACK:
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: trimthenstep6: "
|
|
|
|
"tcp_close.2: head not locked", __func__));
|
1998-09-11 16:04:03 +00:00
|
|
|
tp = tcp_close(tp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCPS_TIME_WAIT:
|
2003-02-19 22:32:43 +00:00
|
|
|
KASSERT(tp->t_state != TCPS_TIME_WAIT,
|
2007-03-23 20:16:50 +00:00
|
|
|
("%s: timewait", __func__));
|
1998-09-11 16:04:03 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1994-05-24 10:09:53 +00:00
|
|
|
* RFC 1323 PAWS: If we have a timestamp reply on this segment
|
|
|
|
* and it's less than ts_recent, drop it.
|
|
|
|
*/
|
2001-11-22 04:50:44 +00:00
|
|
|
if ((to.to_flags & TOF_TS) != 0 && tp->ts_recent &&
|
1998-09-11 16:04:03 +00:00
|
|
|
TSTMP_LT(to.to_tsval, tp->ts_recent)) {
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/* Check to see if ts_recent is over 24 days old. */
|
1999-08-30 21:17:07 +00:00
|
|
|
if ((int)(ticks - tp->ts_recent_age) > TCP_PAWS_IDLE) {
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* 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 {
|
|
|
|
tcpstat.tcps_rcvduppack++;
|
2000-01-09 19:17:30 +00:00
|
|
|
tcpstat.tcps_rcvdupbyte += tlen;
|
1994-05-24 10:09:53 +00:00
|
|
|
tcpstat.tcps_pawsdrop++;
|
2002-12-30 19:31:04 +00:00
|
|
|
if (tlen)
|
2002-12-17 00:24:48 +00:00
|
|
|
goto dropafterack;
|
2002-12-30 19:31:04 +00:00
|
|
|
goto drop;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-09-11 16:04:03 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2001-02-11 07:39:51 +00:00
|
|
|
if (tp->t_state == TCPS_SYN_RECEIVED && SEQ_LT(th->th_seq, tp->irs)) {
|
|
|
|
rstreason = BANDLIM_RST_OPENPORT;
|
|
|
|
goto dropwithreset;
|
|
|
|
}
|
1998-09-11 16:04:03 +00:00
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
todrop = tp->rcv_nxt - th->th_seq;
|
1994-05-24 10:09:53 +00:00
|
|
|
if (todrop > 0) {
|
2000-01-09 19:17:30 +00:00
|
|
|
if (thflags & TH_SYN) {
|
|
|
|
thflags &= ~TH_SYN;
|
|
|
|
th->th_seq++;
|
|
|
|
if (th->th_urp > 1)
|
|
|
|
th->th_urp--;
|
1994-05-24 10:09:53 +00:00
|
|
|
else
|
2000-01-09 19:17:30 +00:00
|
|
|
thflags &= ~TH_URG;
|
1994-05-24 10:09:53 +00:00
|
|
|
todrop--;
|
|
|
|
}
|
1995-02-16 01:39:19 +00:00
|
|
|
/*
|
|
|
|
* Following if statement from Stevens, vol. 2, p. 960.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if (todrop > tlen
|
|
|
|
|| (todrop == tlen && (thflags & TH_FIN) == 0)) {
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
1995-02-16 01:39:19 +00:00
|
|
|
* 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.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
thflags &= ~TH_FIN;
|
1995-02-16 01:39:19 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Send an ACK to resynchronize and drop any data.
|
|
|
|
* But keep on processing for RST or ACK.
|
|
|
|
*/
|
|
|
|
tp->t_flags |= TF_ACKNOW;
|
2000-01-09 19:17:30 +00:00
|
|
|
todrop = tlen;
|
1995-02-16 01:39:19 +00:00
|
|
|
tcpstat.tcps_rcvduppack++;
|
|
|
|
tcpstat.tcps_rcvdupbyte += todrop;
|
1994-05-24 10:09:53 +00:00
|
|
|
} else {
|
|
|
|
tcpstat.tcps_rcvpartduppack++;
|
|
|
|
tcpstat.tcps_rcvpartdupbyte += todrop;
|
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
drop_hdrlen += todrop; /* drop from the top afterwards */
|
|
|
|
th->th_seq += todrop;
|
|
|
|
tlen -= todrop;
|
|
|
|
if (th->th_urp > todrop)
|
|
|
|
th->th_urp -= todrop;
|
1994-05-24 10:09:53 +00:00
|
|
|
else {
|
2000-01-09 19:17:30 +00:00
|
|
|
thflags &= ~TH_URG;
|
|
|
|
th->th_urp = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If new data are received on a connection after the
|
|
|
|
* user processes are gone, then RST the other end.
|
|
|
|
*/
|
|
|
|
if ((so->so_state & SS_NOFDREF) &&
|
2000-01-09 19:17:30 +00:00
|
|
|
tp->t_state > TCPS_CLOSE_WAIT && tlen) {
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: trimthenstep6: tcp_close.3: head "
|
|
|
|
"not locked", __func__));
|
1994-05-24 10:09:53 +00:00
|
|
|
tp = tcp_close(tp);
|
|
|
|
tcpstat.tcps_rcvafterclose++;
|
2001-02-11 07:39:51 +00:00
|
|
|
rstreason = BANDLIM_UNLIMITED;
|
1994-05-24 10:09:53 +00:00
|
|
|
goto dropwithreset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If segment ends after window, drop trailing data
|
|
|
|
* (and PUSH and FIN); if nothing left, just ACK.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
todrop = (th->th_seq+tlen) - (tp->rcv_nxt+tp->rcv_wnd);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (todrop > 0) {
|
|
|
|
tcpstat.tcps_rcvpackafterwin++;
|
2000-01-09 19:17:30 +00:00
|
|
|
if (todrop >= tlen) {
|
|
|
|
tcpstat.tcps_rcvbyteafterwin += tlen;
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(tp->t_state != TCPS_TIME_WAIT, ("%s: timewait",
|
|
|
|
__func__));
|
2000-01-09 19:17:30 +00:00
|
|
|
if (thflags & TH_SYN &&
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->t_state == TCPS_TIME_WAIT &&
|
2000-01-09 19:17:30 +00:00
|
|
|
SEQ_GT(th->th_seq, tp->rcv_nxt)) {
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: trimthenstep6: "
|
|
|
|
"tcp_close.4: head not locked", __func__));
|
1994-05-24 10:09:53 +00:00
|
|
|
tp = tcp_close(tp);
|
2007-03-23 20:16:50 +00:00
|
|
|
/* XXX: Shouldn't be possible. */
|
|
|
|
return (1);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if (tp->rcv_wnd == 0 && th->th_seq == tp->rcv_nxt) {
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->t_flags |= TF_ACKNOW;
|
|
|
|
tcpstat.tcps_rcvwinprobe++;
|
|
|
|
} else
|
|
|
|
goto dropafterack;
|
|
|
|
} else
|
|
|
|
tcpstat.tcps_rcvbyteafterwin += todrop;
|
|
|
|
m_adj(m, -todrop);
|
2000-01-09 19:17:30 +00:00
|
|
|
tlen -= todrop;
|
|
|
|
thflags &= ~(TH_PUSH|TH_FIN);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If last ACK falls within this segment's sequence numbers,
|
|
|
|
* record its timestamp.
|
2005-04-10 05:24:59 +00:00
|
|
|
* 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.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2001-11-22 04:50:44 +00:00
|
|
|
if ((to.to_flags & TOF_TS) != 0 &&
|
2005-04-10 05:24:59 +00:00
|
|
|
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))) {
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->ts_recent_age = ticks;
|
1995-02-09 23:13:27 +00:00
|
|
|
tp->ts_recent = to.to_tsval;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If a SYN is in the window, then this is an
|
|
|
|
* error and we send an RST and drop the connection.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if (thflags & TH_SYN) {
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: tcp_drop: trimthenstep6: "
|
|
|
|
"head not locked", __func__));
|
1994-05-24 10:09:53 +00:00
|
|
|
tp = tcp_drop(tp, ECONNRESET);
|
2001-02-11 07:39:51 +00:00
|
|
|
rstreason = BANDLIM_UNLIMITED;
|
2003-11-12 19:38:01 +00:00
|
|
|
goto drop;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
1995-02-09 23:13:27 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if ((thflags & TH_ACK) == 0) {
|
1995-02-09 23:13:27 +00:00
|
|
|
if (tp->t_state == TCPS_SYN_RECEIVED ||
|
|
|
|
(tp->t_flags & TF_NEEDSYN))
|
|
|
|
goto step6;
|
2006-06-19 12:33:52 +00:00
|
|
|
else if (tp->t_flags & TF_ACKNOW)
|
|
|
|
goto dropafterack;
|
1995-02-09 23:13:27 +00:00
|
|
|
else
|
|
|
|
goto drop;
|
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Ack processing.
|
|
|
|
*/
|
|
|
|
switch (tp->t_state) {
|
|
|
|
|
|
|
|
/*
|
1998-01-21 02:05:59 +00:00
|
|
|
* In SYN_RECEIVED state, the ack ACKs our SYN, so enter
|
|
|
|
* ESTABLISHED state and continue processing.
|
|
|
|
* The ACK was checked above.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
case TCPS_SYN_RECEIVED:
|
1995-02-09 23:13:27 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
tcpstat.tcps_connects++;
|
|
|
|
soisconnected(so);
|
|
|
|
/* 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;
|
2006-02-28 23:05:59 +00:00
|
|
|
tp->snd_wnd = tiwin;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
1995-02-09 23:13:27 +00:00
|
|
|
/*
|
1995-05-30 08:16:23 +00:00
|
|
|
* Make transitions:
|
1995-02-09 23:13:27 +00:00
|
|
|
* SYN-RECEIVED -> ESTABLISHED
|
|
|
|
* SYN-RECEIVED* -> FIN-WAIT-1
|
|
|
|
*/
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->t_starttime = ticks;
|
1995-02-09 23:13:27 +00:00
|
|
|
if (tp->t_flags & TF_NEEDFIN) {
|
|
|
|
tp->t_state = TCPS_FIN_WAIT_1;
|
|
|
|
tp->t_flags &= ~TF_NEEDFIN;
|
1996-09-13 18:47:03 +00:00
|
|
|
} else {
|
1995-02-09 23:13:27 +00:00
|
|
|
tp->t_state = TCPS_ESTABLISHED;
|
2004-08-16 18:32:07 +00:00
|
|
|
callout_reset(tp->tt_keep, tcp_keepidle,
|
1999-08-30 21:17:07 +00:00
|
|
|
tcp_timer_keep, tp);
|
1996-09-13 18:47:03 +00:00
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1995-02-09 23:13:27 +00:00
|
|
|
* If segment contains data or ACK, will call tcp_reass()
|
|
|
|
* later; if not, do so now to pass queued data to user.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if (tlen == 0 && (thflags & TH_FIN) == 0)
|
|
|
|
(void) tcp_reass(tp, (struct tcphdr *)0, 0,
|
1995-02-09 23:13:27 +00:00
|
|
|
(struct mbuf *)0);
|
2000-01-09 19:17:30 +00:00
|
|
|
tp->snd_wl1 = th->th_seq - 1;
|
2002-08-25 13:23:09 +00:00
|
|
|
/* FALLTHROUGH */
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* In ESTABLISHED state: drop duplicate ACKs; ACK out of range
|
|
|
|
* ACKs. If the ack is in the range
|
2000-01-09 19:17:30 +00:00
|
|
|
* tp->snd_una < th->th_ack <= tp->snd_max
|
|
|
|
* then advance tp->snd_una to th->th_ack and drop
|
1994-05-24 10:09:53 +00:00
|
|
|
* 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:
|
|
|
|
case TCPS_TIME_WAIT:
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(tp->t_state != TCPS_TIME_WAIT, ("%s: timewait",
|
|
|
|
__func__));
|
2005-06-27 22:27:42 +00:00
|
|
|
if (SEQ_GT(th->th_ack, tp->snd_max)) {
|
|
|
|
tcpstat.tcps_rcvacktoomuch++;
|
|
|
|
goto dropafterack;
|
|
|
|
}
|
2005-07-01 22:54:18 +00:00
|
|
|
if (tp->sack_enable &&
|
2007-03-23 18:33:21 +00:00
|
|
|
((to.to_flags & TOF_SACK) ||
|
|
|
|
!TAILQ_EMPTY(&tp->snd_holes)))
|
2005-06-27 22:27:42 +00:00
|
|
|
tcp_sack_doack(tp, &to, th->th_ack);
|
2000-01-09 19:17:30 +00:00
|
|
|
if (SEQ_LEQ(th->th_ack, tp->snd_una)) {
|
|
|
|
if (tlen == 0 && tiwin == tp->snd_wnd) {
|
1994-05-24 10:09:53 +00:00
|
|
|
tcpstat.tcps_rcvdupack++;
|
|
|
|
/*
|
|
|
|
* If we have outstanding data (other than
|
|
|
|
* a window probe), this is a completely
|
|
|
|
* duplicate ack (ie, window info didn't
|
|
|
|
* change), 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
|
1995-05-30 08:16:23 +00:00
|
|
|
* network (they're now cached at the receiver)
|
1994-05-24 10:09:53 +00:00
|
|
|
* so bump cwnd by the amount in the receiver
|
|
|
|
* to keep a constant cwnd packets in the
|
|
|
|
* network.
|
|
|
|
*/
|
1999-08-30 21:17:07 +00:00
|
|
|
if (!callout_active(tp->tt_rexmt) ||
|
2000-01-09 19:17:30 +00:00
|
|
|
th->th_ack != tp->snd_una)
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->t_dupacks = 0;
|
2003-01-13 11:01:20 +00:00
|
|
|
else if (++tp->t_dupacks > tcprexmtthresh ||
|
2007-03-23 19:11:22 +00:00
|
|
|
((tcp_do_newreno || tp->sack_enable) &&
|
|
|
|
IN_FASTRECOVERY(tp))) {
|
|
|
|
if (tp->sack_enable && IN_FASTRECOVERY(tp)) {
|
2005-05-25 17:55:27 +00:00
|
|
|
int awnd;
|
2005-04-14 20:09:52 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-05-25 17:55:27 +00:00
|
|
|
awnd = (tp->snd_nxt - tp->snd_fack) +
|
2005-05-11 21:37:42 +00:00
|
|
|
tp->sackhint.sack_bytes_rexmit;
|
2005-05-25 17:55:27 +00:00
|
|
|
if (awnd < tp->snd_ssthresh) {
|
2005-04-14 20:09:52 +00:00
|
|
|
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;
|
2003-01-13 11:01:20 +00:00
|
|
|
(void) tcp_output(tp);
|
|
|
|
goto drop;
|
|
|
|
} else if (tp->t_dupacks == tcprexmtthresh) {
|
1994-05-24 10:09:53 +00:00
|
|
|
tcp_seq onxt = tp->snd_nxt;
|
2003-01-13 11:01:20 +00:00
|
|
|
u_int win;
|
2004-07-01 23:34:06 +00:00
|
|
|
|
|
|
|
/*
|
2004-08-16 18:32:07 +00:00
|
|
|
* If we're doing sack, check to
|
|
|
|
* see if we're already in sack
|
2004-07-01 23:34:06 +00:00
|
|
|
* recovery. If we're not doing sack,
|
|
|
|
* check to see if we're in newreno
|
|
|
|
* recovery.
|
|
|
|
*/
|
|
|
|
if (tp->sack_enable) {
|
|
|
|
if (IN_FASTRECOVERY(tp)) {
|
|
|
|
tp->t_dupacks = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (tcp_do_newreno) {
|
|
|
|
if (SEQ_LEQ(th->th_ack,
|
|
|
|
tp->snd_recover)) {
|
|
|
|
tp->t_dupacks = 0;
|
|
|
|
break;
|
|
|
|
}
|
2000-05-06 03:31:09 +00:00
|
|
|
}
|
2003-01-13 11:01:20 +00:00
|
|
|
win = min(tp->snd_wnd, tp->snd_cwnd) /
|
|
|
|
2 / tp->t_maxseg;
|
1994-05-24 10:09:53 +00:00
|
|
|
if (win < 2)
|
|
|
|
win = 2;
|
|
|
|
tp->snd_ssthresh = win * tp->t_maxseg;
|
2003-07-15 21:49:53 +00:00
|
|
|
ENTER_FASTRECOVERY(tp);
|
2000-05-06 03:31:09 +00:00
|
|
|
tp->snd_recover = tp->snd_max;
|
1999-08-30 21:17:07 +00:00
|
|
|
callout_stop(tp->tt_rexmt);
|
|
|
|
tp->t_rtttime = 0;
|
2004-06-23 21:04:37 +00:00
|
|
|
if (tp->sack_enable) {
|
|
|
|
tcpstat.tcps_sack_recovery_episode++;
|
2004-10-05 18:36:24 +00:00
|
|
|
tp->sack_newdata = tp->snd_nxt;
|
2005-02-14 21:01:08 +00:00
|
|
|
tp->snd_cwnd = tp->t_maxseg;
|
2004-06-23 21:04:37 +00:00
|
|
|
(void) tcp_output(tp);
|
|
|
|
goto drop;
|
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
tp->snd_nxt = th->th_ack;
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->snd_cwnd = tp->t_maxseg;
|
|
|
|
(void) tcp_output(tp);
|
2003-04-01 21:16:46 +00:00
|
|
|
KASSERT(tp->snd_limited <= 2,
|
2007-03-23 20:16:50 +00:00
|
|
|
("%s: tp->snd_limited too big",
|
|
|
|
__func__));
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->snd_cwnd = tp->snd_ssthresh +
|
2003-04-01 21:16:46 +00:00
|
|
|
tp->t_maxseg *
|
|
|
|
(tp->t_dupacks - tp->snd_limited);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (SEQ_GT(onxt, tp->snd_nxt))
|
|
|
|
tp->snd_nxt = onxt;
|
|
|
|
goto drop;
|
2003-03-12 20:27:28 +00:00
|
|
|
} else if (tcp_do_rfc3042) {
|
|
|
|
u_long oldcwnd = tp->snd_cwnd;
|
2003-04-01 21:16:46 +00:00
|
|
|
tcp_seq oldsndmax = tp->snd_max;
|
|
|
|
u_int sent;
|
2004-02-25 08:53:17 +00:00
|
|
|
|
2003-03-12 20:27:28 +00:00
|
|
|
KASSERT(tp->t_dupacks == 1 ||
|
|
|
|
tp->t_dupacks == 2,
|
2007-03-23 20:16:50 +00:00
|
|
|
("%s: dupacks not 1 or 2",
|
|
|
|
__func__));
|
2004-01-20 21:40:25 +00:00
|
|
|
if (tp->t_dupacks == 1)
|
2003-04-01 21:16:46 +00:00
|
|
|
tp->snd_limited = 0;
|
2004-01-20 21:40:25 +00:00
|
|
|
tp->snd_cwnd =
|
|
|
|
(tp->snd_nxt - tp->snd_una) +
|
|
|
|
(tp->t_dupacks - tp->snd_limited) *
|
|
|
|
tp->t_maxseg;
|
2003-03-12 20:27:28 +00:00
|
|
|
(void) tcp_output(tp);
|
2003-04-01 21:16:46 +00:00
|
|
|
sent = tp->snd_max - oldsndmax;
|
|
|
|
if (sent > tp->t_maxseg) {
|
2004-02-25 08:53:17 +00:00
|
|
|
KASSERT((tp->t_dupacks == 2 &&
|
|
|
|
tp->snd_limited == 0) ||
|
|
|
|
(sent == tp->t_maxseg + 1 &&
|
|
|
|
tp->t_flags & TF_SENTFIN),
|
2007-03-23 20:16:50 +00:00
|
|
|
("%s: sent too much",
|
|
|
|
__func__));
|
2003-04-01 21:16:46 +00:00
|
|
|
tp->snd_limited = 2;
|
|
|
|
} else if (sent > 0)
|
|
|
|
++tp->snd_limited;
|
2003-03-12 20:27:28 +00:00
|
|
|
tp->snd_cwnd = oldcwnd;
|
|
|
|
goto drop;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
tp->t_dupacks = 0;
|
|
|
|
break;
|
|
|
|
}
|
2003-01-13 11:01:20 +00:00
|
|
|
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(SEQ_GT(th->th_ack, tp->snd_una),
|
|
|
|
("%s: th_ack <= snd_una", __func__));
|
2003-01-13 11:01:20 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* If the congestion window was inflated to account
|
|
|
|
* for the other side's cached packets, retract it.
|
|
|
|
*/
|
2004-06-23 21:04:37 +00:00
|
|
|
if (tcp_do_newreno || tp->sack_enable) {
|
2003-07-15 21:49:53 +00:00
|
|
|
if (IN_FASTRECOVERY(tp)) {
|
2003-01-13 11:01:20 +00:00
|
|
|
if (SEQ_LT(th->th_ack, tp->snd_recover)) {
|
2004-06-23 21:04:37 +00:00
|
|
|
if (tp->sack_enable)
|
|
|
|
tcp_sack_partialack(tp, th);
|
|
|
|
else
|
|
|
|
tcp_newreno_partial_ack(tp, th);
|
2002-08-17 02:05:25 +00:00
|
|
|
} else {
|
|
|
|
/*
|
2004-06-23 21:04:37 +00:00
|
|
|
* Out of fast recovery.
|
2002-08-17 02:05:25 +00:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
if (SEQ_GT(th->th_ack +
|
|
|
|
tp->snd_ssthresh,
|
|
|
|
tp->snd_max))
|
|
|
|
tp->snd_cwnd = tp->snd_max -
|
|
|
|
th->th_ack +
|
|
|
|
tp->t_maxseg;
|
|
|
|
else
|
|
|
|
tp->snd_cwnd = tp->snd_ssthresh;
|
|
|
|
}
|
|
|
|
}
|
2004-08-16 18:32:07 +00:00
|
|
|
} else {
|
|
|
|
if (tp->t_dupacks >= tcprexmtthresh &&
|
|
|
|
tp->snd_cwnd > tp->snd_ssthresh)
|
2002-08-17 02:05:25 +00:00
|
|
|
tp->snd_cwnd = tp->snd_ssthresh;
|
2004-08-16 18:32:07 +00:00
|
|
|
}
|
2003-01-13 11:01:20 +00:00
|
|
|
tp->t_dupacks = 0;
|
1995-02-09 23:13:27 +00:00
|
|
|
/*
|
2002-08-17 02:05:25 +00:00
|
|
|
* If we reach this point, ACK is not a duplicate,
|
1995-02-09 23:13:27 +00:00
|
|
|
* i.e., it ACKs something we sent.
|
|
|
|
*/
|
|
|
|
if (tp->t_flags & TF_NEEDSYN) {
|
1995-05-30 08:16:23 +00:00
|
|
|
/*
|
1996-01-31 08:22:24 +00:00
|
|
|
* 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.
|
1995-02-09 23:13:27 +00:00
|
|
|
*/
|
|
|
|
tp->t_flags &= ~TF_NEEDSYN;
|
|
|
|
tp->snd_una++;
|
1996-01-31 08:22:24 +00:00
|
|
|
/* 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;
|
2006-02-28 23:05:59 +00:00
|
|
|
/* Send window already scaled. */
|
1996-01-31 08:22:24 +00:00
|
|
|
}
|
1995-02-09 23:13:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
process_ACK:
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: process_ACK: head not locked",
|
|
|
|
__func__));
|
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
2004-07-12 19:28:07 +00:00
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
acked = th->th_ack - tp->snd_una;
|
1994-05-24 10:09:53 +00:00
|
|
|
tcpstat.tcps_rcvackpack++;
|
|
|
|
tcpstat.tcps_rcvackbyte += acked;
|
|
|
|
|
1999-08-30 21:17:07 +00:00
|
|
|
/*
|
|
|
|
* 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 && ticks < tp->t_badrxtwin) {
|
2002-07-19 18:29:38 +00:00
|
|
|
++tcpstat.tcps_sndrexmitbad;
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->snd_cwnd = tp->snd_cwnd_prev;
|
|
|
|
tp->snd_ssthresh = tp->snd_ssthresh_prev;
|
2003-07-15 21:49:53 +00:00
|
|
|
tp->snd_recover = tp->snd_recover_prev;
|
|
|
|
if (tp->t_flags & TF_WASFRECOVERY)
|
|
|
|
ENTER_FASTRECOVERY(tp);
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->snd_nxt = tp->snd_max;
|
2004-08-16 18:32:07 +00:00
|
|
|
tp->t_badrxtwin = 0; /* XXX probably not required */
|
1999-08-30 21:17:07 +00:00
|
|
|
}
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
Guido reported an interesting bug where an FTP connection between a
Windows 2000 box and a FreeBSD box could stall. The problem turned out
to be a timestamp reply bug in the W2K TCP stack. FreeBSD sends a
timestamp with the SYN, W2K returns a timestamp of 0 in the SYN+ACK
causing FreeBSD to calculate an insane SRTT and RTT, resulting in
a maximal retransmit timeout (60 seconds). If there is any packet
loss on the connection for the first six or so packets the retransmit
case may be hit (the window will still be too small for fast-retransmit),
causing a 60+ second pause. The W2K box gives up and closes the
connection.
This commit works around the W2K bug.
15:04:59.374588 FREEBSD.20 > W2K.1036: S 1420807004:1420807004(0) win 65535 <mss 1460,nop,wscale 2,nop,nop,timestamp 188297344 0> (DF) [tos 0x8]
15:04:59.377558 W2K.1036 > FREEBSD.20: S 4134611565:4134611565(0) ack 1420807005 win 17520 <mss 1460,nop,wscale 0,nop,nop,timestamp 0 0> (DF)
Bug reported by: Guido van Rooij <guido@gvr.org>
2002-09-17 22:21:37 +00:00
|
|
|
*
|
|
|
|
* Some boxes send broken timestamp replies
|
2004-08-16 18:32:07 +00:00
|
|
|
* during the SYN+ACK phase, ignore
|
Guido reported an interesting bug where an FTP connection between a
Windows 2000 box and a FreeBSD box could stall. The problem turned out
to be a timestamp reply bug in the W2K TCP stack. FreeBSD sends a
timestamp with the SYN, W2K returns a timestamp of 0 in the SYN+ACK
causing FreeBSD to calculate an insane SRTT and RTT, resulting in
a maximal retransmit timeout (60 seconds). If there is any packet
loss on the connection for the first six or so packets the retransmit
case may be hit (the window will still be too small for fast-retransmit),
causing a 60+ second pause. The W2K box gives up and closes the
connection.
This commit works around the W2K bug.
15:04:59.374588 FREEBSD.20 > W2K.1036: S 1420807004:1420807004(0) win 65535 <mss 1460,nop,wscale 2,nop,nop,timestamp 188297344 0> (DF) [tos 0x8]
15:04:59.377558 W2K.1036 > FREEBSD.20: S 4134611565:4134611565(0) ack 1420807005 win 17520 <mss 1460,nop,wscale 0,nop,nop,timestamp 0 0> (DF)
Bug reported by: Guido van Rooij <guido@gvr.org>
2002-09-17 22:21:37 +00:00
|
|
|
* timestamps of 0 or we could calculate a
|
|
|
|
* huge RTT and blow up the retransmit timer.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
Guido reported an interesting bug where an FTP connection between a
Windows 2000 box and a FreeBSD box could stall. The problem turned out
to be a timestamp reply bug in the W2K TCP stack. FreeBSD sends a
timestamp with the SYN, W2K returns a timestamp of 0 in the SYN+ACK
causing FreeBSD to calculate an insane SRTT and RTT, resulting in
a maximal retransmit timeout (60 seconds). If there is any packet
loss on the connection for the first six or so packets the retransmit
case may be hit (the window will still be too small for fast-retransmit),
causing a 60+ second pause. The W2K box gives up and closes the
connection.
This commit works around the W2K bug.
15:04:59.374588 FREEBSD.20 > W2K.1036: S 1420807004:1420807004(0) win 65535 <mss 1460,nop,wscale 2,nop,nop,timestamp 188297344 0> (DF) [tos 0x8]
15:04:59.377558 W2K.1036 > FREEBSD.20: S 4134611565:4134611565(0) ack 1420807005 win 17520 <mss 1460,nop,wscale 0,nop,nop,timestamp 0 0> (DF)
Bug reported by: Guido van Rooij <guido@gvr.org>
2002-09-17 22:21:37 +00:00
|
|
|
if ((to.to_flags & TOF_TS) != 0 &&
|
|
|
|
to.to_tsecr) {
|
2006-02-16 19:38:07 +00:00
|
|
|
if (!tp->t_rttlow || tp->t_rttlow > ticks - to.to_tsecr)
|
|
|
|
tp->t_rttlow = ticks - to.to_tsecr;
|
1999-08-30 21:17:07 +00:00
|
|
|
tcp_xmit_timer(tp, ticks - to.to_tsecr + 1);
|
Guido reported an interesting bug where an FTP connection between a
Windows 2000 box and a FreeBSD box could stall. The problem turned out
to be a timestamp reply bug in the W2K TCP stack. FreeBSD sends a
timestamp with the SYN, W2K returns a timestamp of 0 in the SYN+ACK
causing FreeBSD to calculate an insane SRTT and RTT, resulting in
a maximal retransmit timeout (60 seconds). If there is any packet
loss on the connection for the first six or so packets the retransmit
case may be hit (the window will still be too small for fast-retransmit),
causing a 60+ second pause. The W2K box gives up and closes the
connection.
This commit works around the W2K bug.
15:04:59.374588 FREEBSD.20 > W2K.1036: S 1420807004:1420807004(0) win 65535 <mss 1460,nop,wscale 2,nop,nop,timestamp 188297344 0> (DF) [tos 0x8]
15:04:59.377558 W2K.1036 > FREEBSD.20: S 4134611565:4134611565(0) ack 1420807005 win 17520 <mss 1460,nop,wscale 0,nop,nop,timestamp 0 0> (DF)
Bug reported by: Guido van Rooij <guido@gvr.org>
2002-09-17 22:21:37 +00:00
|
|
|
} else if (tp->t_rtttime && SEQ_GT(th->th_ack, tp->t_rtseq)) {
|
2006-02-16 19:38:07 +00:00
|
|
|
if (!tp->t_rttlow || tp->t_rttlow > ticks - tp->t_rtttime)
|
|
|
|
tp->t_rttlow = ticks - tp->t_rtttime;
|
1999-08-30 21:17:07 +00:00
|
|
|
tcp_xmit_timer(tp, ticks - tp->t_rtttime);
|
Guido reported an interesting bug where an FTP connection between a
Windows 2000 box and a FreeBSD box could stall. The problem turned out
to be a timestamp reply bug in the W2K TCP stack. FreeBSD sends a
timestamp with the SYN, W2K returns a timestamp of 0 in the SYN+ACK
causing FreeBSD to calculate an insane SRTT and RTT, resulting in
a maximal retransmit timeout (60 seconds). If there is any packet
loss on the connection for the first six or so packets the retransmit
case may be hit (the window will still be too small for fast-retransmit),
causing a 60+ second pause. The W2K box gives up and closes the
connection.
This commit works around the W2K bug.
15:04:59.374588 FREEBSD.20 > W2K.1036: S 1420807004:1420807004(0) win 65535 <mss 1460,nop,wscale 2,nop,nop,timestamp 188297344 0> (DF) [tos 0x8]
15:04:59.377558 W2K.1036 > FREEBSD.20: S 4134611565:4134611565(0) ack 1420807005 win 17520 <mss 1460,nop,wscale 0,nop,nop,timestamp 0 0> (DF)
Bug reported by: Guido van Rooij <guido@gvr.org>
2002-09-17 22:21:37 +00:00
|
|
|
}
|
2002-08-17 18:26:02 +00:00
|
|
|
tcp_xmit_bandwidth_limit(tp, th->th_ack);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if (th->th_ack == tp->snd_max) {
|
1999-08-30 21:17:07 +00:00
|
|
|
callout_stop(tp->tt_rexmt);
|
1994-05-24 10:09:53 +00:00
|
|
|
needoutput = 1;
|
1999-08-30 21:17:07 +00:00
|
|
|
} else if (!callout_active(tp->tt_persist))
|
|
|
|
callout_reset(tp->tt_rexmt, tp->t_rxtcur,
|
|
|
|
tcp_timer_rexmt, tp);
|
1995-02-09 23:13:27 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If no data (only SYN) was ACK'd,
|
|
|
|
* skip rest of ACK processing.
|
|
|
|
*/
|
|
|
|
if (acked == 0)
|
|
|
|
goto step6;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* When new data is acked, open the congestion window.
|
|
|
|
* If the window gives us less than ssthresh packets
|
|
|
|
* in flight, open exponentially (maxseg per packet).
|
|
|
|
* Otherwise open linearly: maxseg per window
|
1994-10-13 18:36:32 +00:00
|
|
|
* (maxseg^2 / cwnd per packet).
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2004-08-16 18:32:07 +00:00
|
|
|
if ((!tcp_do_newreno && !tp->sack_enable) ||
|
2004-06-23 21:04:37 +00:00
|
|
|
!IN_FASTRECOVERY(tp)) {
|
2007-03-21 19:37:55 +00:00
|
|
|
u_int cw = tp->snd_cwnd;
|
|
|
|
u_int incr = tp->t_maxseg;
|
2003-01-13 11:01:20 +00:00
|
|
|
if (cw > tp->snd_ssthresh)
|
|
|
|
incr = incr * incr / cw;
|
|
|
|
tp->snd_cwnd = min(cw+incr, TCP_MAXWIN<<tp->snd_scale);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2004-06-24 03:07:27 +00:00
|
|
|
SOCKBUF_LOCK(&so->so_snd);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (acked > so->so_snd.sb_cc) {
|
|
|
|
tp->snd_wnd -= so->so_snd.sb_cc;
|
2004-06-24 03:07:27 +00:00
|
|
|
sbdrop_locked(&so->so_snd, (int)so->so_snd.sb_cc);
|
1994-05-24 10:09:53 +00:00
|
|
|
ourfinisacked = 1;
|
|
|
|
} else {
|
2004-06-24 03:07:27 +00:00
|
|
|
sbdrop_locked(&so->so_snd, acked);
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->snd_wnd -= acked;
|
|
|
|
ourfinisacked = 0;
|
|
|
|
}
|
Reduce the number of unnecessary unlock-relocks on socket buffer mutexes
associated with performing a wakeup on the socket buffer:
- When performing an sbappend*() followed by a so[rw]wakeup(), explicitly
acquire the socket buffer lock and use the _locked() variants of both
calls. Note that the _locked() sowakeup() versions unlock the mutex on
return. This is done in uipc_send(), divert_packet(), mroute
socket_send(), raw_append(), tcp_reass(), tcp_input(), and udp_append().
- When the socket buffer lock is dropped before a sowakeup(), remove the
explicit unlock and use the _locked() sowakeup() variant. This is done
in soisdisconnecting(), soisdisconnected() when setting the can't send/
receive flags and dropping data, and in uipc_rcvd() which adjusting
back-pressure on the sockets.
For UNIX domain sockets running mpsafe with a contention-intensive SMP
mysql benchmark, this results in a 1.6% query rate improvement due to
reduce mutex costs.
2004-06-26 19:10:39 +00:00
|
|
|
sowwakeup_locked(so);
|
2003-01-13 11:01:20 +00:00
|
|
|
/* detect una wraparound */
|
2004-08-16 18:32:07 +00:00
|
|
|
if ((tcp_do_newreno || tp->sack_enable) &&
|
2004-06-23 21:04:37 +00:00
|
|
|
!IN_FASTRECOVERY(tp) &&
|
2003-07-15 21:49:53 +00:00
|
|
|
SEQ_GT(tp->snd_una, tp->snd_recover) &&
|
|
|
|
SEQ_LEQ(th->th_ack, tp->snd_recover))
|
|
|
|
tp->snd_recover = th->th_ack - 1;
|
2004-08-16 18:32:07 +00:00
|
|
|
if ((tcp_do_newreno || tp->sack_enable) &&
|
2004-06-23 21:04:37 +00:00
|
|
|
IN_FASTRECOVERY(tp) &&
|
2003-07-15 21:49:53 +00:00
|
|
|
SEQ_GEQ(th->th_ack, tp->snd_recover))
|
|
|
|
EXIT_FASTRECOVERY(tp);
|
2000-01-09 19:17:30 +00:00
|
|
|
tp->snd_una = th->th_ack;
|
2004-06-23 21:04:37 +00:00
|
|
|
if (tp->sack_enable) {
|
|
|
|
if (SEQ_GT(tp->snd_una, tp->snd_recover))
|
|
|
|
tp->snd_recover = tp->snd_una;
|
2004-08-16 18:32:07 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
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.
|
|
|
|
*/
|
2003-02-19 22:32:43 +00:00
|
|
|
/* XXXjl
|
|
|
|
* we should release the tp also, and use a
|
|
|
|
* compressed state.
|
|
|
|
*/
|
2004-06-14 18:16:22 +00:00
|
|
|
if (so->so_rcv.sb_state & SBS_CANTRCVMORE) {
|
2007-02-26 22:25:21 +00:00
|
|
|
int timeout;
|
|
|
|
|
2002-06-18 07:42:02 +00:00
|
|
|
soisdisconnected(so);
|
2007-02-26 22:25:21 +00:00
|
|
|
timeout = (tcp_fast_finwait2_recycle) ?
|
|
|
|
tcp_finwait2_timeout : tcp_maxidle;
|
|
|
|
callout_reset(tp->tt_2msl, timeout,
|
1999-08-30 21:17:07 +00:00
|
|
|
tcp_timer_2msl, tp);
|
2002-05-31 11:52:35 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->t_state = TCPS_FIN_WAIT_2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-08-16 18:32:07 +00:00
|
|
|
/*
|
1994-05-24 10:09:53 +00:00
|
|
|
* 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) {
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: process_ACK: "
|
|
|
|
"head not locked", __func__));
|
2003-02-24 00:52:03 +00:00
|
|
|
tcp_twstart(tp);
|
2003-02-19 22:32:43 +00:00
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
2007-03-23 20:16:50 +00:00
|
|
|
headlocked = 0;
|
2003-02-19 22:32:43 +00:00
|
|
|
m_freem(m);
|
2007-03-23 20:16:50 +00:00
|
|
|
return (0);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
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) {
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: process_ACK: "
|
|
|
|
"tcp_close: head not locked", __func__));
|
1994-05-24 10:09:53 +00:00
|
|
|
tp = tcp_close(tp);
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In TIME_WAIT state the only thing that should arrive
|
|
|
|
* is a retransmission of the remote FIN. Acknowledge
|
|
|
|
* it and restart the finack timer.
|
|
|
|
*/
|
|
|
|
case TCPS_TIME_WAIT:
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(tp->t_state != TCPS_TIME_WAIT,
|
|
|
|
("%s: timewait", __func__));
|
1999-08-30 21:17:07 +00:00
|
|
|
callout_reset(tp->tt_2msl, 2 * tcp_msl,
|
|
|
|
tcp_timer_2msl, tp);
|
1994-05-24 10:09:53 +00:00
|
|
|
goto dropafterack;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
step6:
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: step6: head not locked", __func__));
|
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
2004-07-12 19:28:07 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Update window information.
|
|
|
|
* Don't look at window if no ACK: TAC's send garbage on first SYN.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
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))))) {
|
1994-05-24 10:09:53 +00:00
|
|
|
/* keep track of pure window updates */
|
2000-01-09 19:17:30 +00:00
|
|
|
if (tlen == 0 &&
|
|
|
|
tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd)
|
1994-05-24 10:09:53 +00:00
|
|
|
tcpstat.tcps_rcvwinupd++;
|
|
|
|
tp->snd_wnd = tiwin;
|
2000-01-09 19:17:30 +00:00
|
|
|
tp->snd_wl1 = th->th_seq;
|
|
|
|
tp->snd_wl2 = th->th_ack;
|
1994-05-24 10:09:53 +00:00
|
|
|
if (tp->snd_wnd > tp->max_sndwnd)
|
|
|
|
tp->max_sndwnd = tp->snd_wnd;
|
|
|
|
needoutput = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process segments with URG.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if ((thflags & TH_URG) && th->th_urp &&
|
1994-05-24 10:09:53 +00:00
|
|
|
TCPS_HAVERCVDFIN(tp->t_state) == 0) {
|
|
|
|
/*
|
|
|
|
* This is a kludge, but if we receive and accept
|
|
|
|
* random urgent pointers, we'll crash in
|
|
|
|
* soreceive. It's hard to imagine someone
|
|
|
|
* actually wanting to send this much urgent data.
|
|
|
|
*/
|
2004-11-28 11:01:31 +00:00
|
|
|
SOCKBUF_LOCK(&so->so_rcv);
|
2000-01-09 19:17:30 +00:00
|
|
|
if (th->th_urp + so->so_rcv.sb_cc > sb_max) {
|
|
|
|
th->th_urp = 0; /* XXX */
|
|
|
|
thflags &= ~TH_URG; /* XXX */
|
2004-11-28 11:01:31 +00:00
|
|
|
SOCKBUF_UNLOCK(&so->so_rcv); /* XXX */
|
1994-05-24 10:09:53 +00:00
|
|
|
goto dodata; /* XXX */
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If this segment advances the known urgent pointer,
|
|
|
|
* then mark the data stream. This should not happen
|
|
|
|
* in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since
|
1995-05-30 08:16:23 +00:00
|
|
|
* a FIN has been received from the remote side.
|
1994-05-24 10:09:53 +00:00
|
|
|
* In these states we ignore the URG.
|
|
|
|
*
|
|
|
|
* According to RFC961 (Assigned Protocols),
|
|
|
|
* the urgent pointer points to the last octet
|
|
|
|
* of urgent data. We continue, however,
|
|
|
|
* to consider it to indicate the first octet
|
1995-05-30 08:16:23 +00:00
|
|
|
* of data past the urgent section as the original
|
1994-05-24 10:09:53 +00:00
|
|
|
* spec states (in one of two places).
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if (SEQ_GT(th->th_seq+th->th_urp, tp->rcv_up)) {
|
|
|
|
tp->rcv_up = th->th_seq + th->th_urp;
|
1994-05-24 10:09:53 +00:00
|
|
|
so->so_oobmark = so->so_rcv.sb_cc +
|
|
|
|
(tp->rcv_up - tp->rcv_nxt) - 1;
|
2004-06-24 02:57:12 +00:00
|
|
|
if (so->so_oobmark == 0)
|
2004-06-14 18:16:22 +00:00
|
|
|
so->so_rcv.sb_state |= SBS_RCVATMARK;
|
1994-05-24 10:09:53 +00:00
|
|
|
sohasoutofband(so);
|
|
|
|
tp->t_oobflags &= ~(TCPOOB_HAVEDATA | TCPOOB_HADDATA);
|
|
|
|
}
|
2004-11-28 11:01:31 +00:00
|
|
|
SOCKBUF_UNLOCK(&so->so_rcv);
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Remove out of band data so doesn't get presented to user.
|
|
|
|
* This can happen independent of advancing the URG pointer,
|
|
|
|
* but if two URG's are pending at once, some out-of-band
|
|
|
|
* data may creep in... ick.
|
|
|
|
*/
|
2002-10-30 08:32:19 +00:00
|
|
|
if (th->th_urp <= (u_long)tlen &&
|
|
|
|
!(so->so_options & SO_OOBINLINE)) {
|
|
|
|
/* hdr drop is delayed */
|
|
|
|
tcp_pulloutofband(so, th, m, drop_hdrlen);
|
|
|
|
}
|
2002-08-17 02:05:25 +00:00
|
|
|
} else {
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
2002-08-17 02:05:25 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
dodata: /* XXX */
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: dodata: head not locked", __func__));
|
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
2004-07-12 19:28:07 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2002-08-17 02:05:25 +00:00
|
|
|
if ((tlen || (thflags & TH_FIN)) &&
|
1994-05-24 10:09:53 +00:00
|
|
|
TCPS_HAVERCVDFIN(tp->t_state) == 0) {
|
2005-07-01 22:52:46 +00:00
|
|
|
tcp_seq save_start = th->th_seq;
|
|
|
|
tcp_seq save_end = th->th_seq + tlen;
|
2000-01-09 19:17:30 +00:00
|
|
|
m_adj(m, drop_hdrlen); /* delayed header drop */
|
2001-05-29 19:54:45 +00:00
|
|
|
/*
|
2002-08-17 02:05:25 +00:00
|
|
|
* 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).
|
2001-05-29 19:54:45 +00:00
|
|
|
*/
|
|
|
|
if (th->th_seq == tp->rcv_nxt &&
|
|
|
|
LIST_EMPTY(&tp->t_segq) &&
|
|
|
|
TCPS_HAVEESTABLISHED(tp->t_state)) {
|
|
|
|
if (DELAY_ACK(tp))
|
2003-02-19 21:18:23 +00:00
|
|
|
tp->t_flags |= TF_DELACK;
|
2001-05-29 19:54:45 +00:00
|
|
|
else
|
|
|
|
tp->t_flags |= TF_ACKNOW;
|
|
|
|
tp->rcv_nxt += tlen;
|
|
|
|
thflags = th->th_flags & TH_FIN;
|
|
|
|
tcpstat.tcps_rcvpack++;
|
|
|
|
tcpstat.tcps_rcvbyte += tlen;
|
|
|
|
ND6_HINT(tp);
|
Reduce the number of unnecessary unlock-relocks on socket buffer mutexes
associated with performing a wakeup on the socket buffer:
- When performing an sbappend*() followed by a so[rw]wakeup(), explicitly
acquire the socket buffer lock and use the _locked() variants of both
calls. Note that the _locked() sowakeup() versions unlock the mutex on
return. This is done in uipc_send(), divert_packet(), mroute
socket_send(), raw_append(), tcp_reass(), tcp_input(), and udp_append().
- When the socket buffer lock is dropped before a sowakeup(), remove the
explicit unlock and use the _locked() sowakeup() variant. This is done
in soisdisconnecting(), soisdisconnected() when setting the can't send/
receive flags and dropping data, and in uipc_rcvd() which adjusting
back-pressure on the sockets.
For UNIX domain sockets running mpsafe with a contention-intensive SMP
mysql benchmark, this results in a 1.6% query rate improvement due to
reduce mutex costs.
2004-06-26 19:10:39 +00:00
|
|
|
SOCKBUF_LOCK(&so->so_rcv);
|
2004-06-14 18:16:22 +00:00
|
|
|
if (so->so_rcv.sb_state & SBS_CANTRCVMORE)
|
2002-09-22 02:54:07 +00:00
|
|
|
m_freem(m);
|
|
|
|
else
|
Reduce the number of unnecessary unlock-relocks on socket buffer mutexes
associated with performing a wakeup on the socket buffer:
- When performing an sbappend*() followed by a so[rw]wakeup(), explicitly
acquire the socket buffer lock and use the _locked() variants of both
calls. Note that the _locked() sowakeup() versions unlock the mutex on
return. This is done in uipc_send(), divert_packet(), mroute
socket_send(), raw_append(), tcp_reass(), tcp_input(), and udp_append().
- When the socket buffer lock is dropped before a sowakeup(), remove the
explicit unlock and use the _locked() sowakeup() variant. This is done
in soisdisconnecting(), soisdisconnected() when setting the can't send/
receive flags and dropping data, and in uipc_rcvd() which adjusting
back-pressure on the sockets.
For UNIX domain sockets running mpsafe with a contention-intensive SMP
mysql benchmark, this results in a 1.6% query rate improvement due to
reduce mutex costs.
2004-06-26 19:10:39 +00:00
|
|
|
sbappendstream_locked(&so->so_rcv, m);
|
|
|
|
sorwakeup_locked(so);
|
2001-05-29 19:54:45 +00:00
|
|
|
} else {
|
|
|
|
thflags = tcp_reass(tp, th, &tlen, m);
|
|
|
|
tp->t_flags |= TF_ACKNOW;
|
|
|
|
}
|
2005-04-10 05:21:29 +00:00
|
|
|
if (tlen > 0 && tp->sack_enable)
|
2005-07-01 22:52:46 +00:00
|
|
|
tcp_update_sack_list(tp, save_start, save_end);
|
2007-03-23 20:16:50 +00:00
|
|
|
#if 0
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Note the amount of data that peer has sent into
|
|
|
|
* our window, in order to estimate the sender's
|
|
|
|
* buffer size.
|
2007-03-23 20:16:50 +00:00
|
|
|
* XXX: Unused.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt);
|
2007-03-23 20:16:50 +00:00
|
|
|
#endif
|
1994-05-24 10:09:53 +00:00
|
|
|
} else {
|
|
|
|
m_freem(m);
|
2000-01-09 19:17:30 +00:00
|
|
|
thflags &= ~TH_FIN;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If FIN is received ACK the FIN and let the user know
|
|
|
|
* that the connection is closing.
|
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if (thflags & TH_FIN) {
|
1994-05-24 10:09:53 +00:00
|
|
|
if (TCPS_HAVERCVDFIN(tp->t_state) == 0) {
|
|
|
|
socantrcvmore(so);
|
1995-02-09 23:13:27 +00:00
|
|
|
/*
|
2002-08-17 02:05:25 +00:00
|
|
|
* 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.
|
1995-02-09 23:13:27 +00:00
|
|
|
*/
|
2003-02-19 21:18:23 +00:00
|
|
|
if (tp->t_flags & TF_NEEDSYN)
|
|
|
|
tp->t_flags |= TF_DELACK;
|
1995-05-30 08:16:23 +00:00
|
|
|
else
|
1995-02-09 23:13:27 +00:00
|
|
|
tp->t_flags |= TF_ACKNOW;
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->rcv_nxt++;
|
|
|
|
}
|
|
|
|
switch (tp->t_state) {
|
|
|
|
|
2004-08-16 18:32:07 +00:00
|
|
|
/*
|
1994-05-24 10:09:53 +00:00
|
|
|
* In SYN_RECEIVED and ESTABLISHED STATES
|
|
|
|
* enter the CLOSE_WAIT state.
|
|
|
|
*/
|
|
|
|
case TCPS_SYN_RECEIVED:
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->t_starttime = ticks;
|
|
|
|
/*FALLTHROUGH*/
|
1994-05-24 10:09:53 +00:00
|
|
|
case TCPS_ESTABLISHED:
|
|
|
|
tp->t_state = TCPS_CLOSE_WAIT;
|
|
|
|
break;
|
|
|
|
|
2004-08-16 18:32:07 +00:00
|
|
|
/*
|
1994-05-24 10:09:53 +00:00
|
|
|
* If still in FIN_WAIT_1 STATE FIN has not been acked so
|
|
|
|
* enter the CLOSING state.
|
|
|
|
*/
|
|
|
|
case TCPS_FIN_WAIT_1:
|
|
|
|
tp->t_state = TCPS_CLOSING;
|
|
|
|
break;
|
|
|
|
|
2004-08-16 18:32:07 +00:00
|
|
|
/*
|
1994-05-24 10:09:53 +00:00
|
|
|
* In FIN_WAIT_2 state enter the TIME_WAIT state,
|
1995-05-30 08:16:23 +00:00
|
|
|
* starting the time-wait timer, turning off the other
|
1994-05-24 10:09:53 +00:00
|
|
|
* standard timers.
|
|
|
|
*/
|
|
|
|
case TCPS_FIN_WAIT_2:
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked == 1, ("%s: dodata: "
|
|
|
|
"TCP_FIN_WAIT_2: head not locked", __func__));
|
2003-02-19 22:32:43 +00:00
|
|
|
tcp_twstart(tp);
|
2003-02-24 00:52:03 +00:00
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
2007-03-23 20:16:50 +00:00
|
|
|
return (0);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* In TIME_WAIT state restart the 2 MSL time_wait timer.
|
|
|
|
*/
|
|
|
|
case TCPS_TIME_WAIT:
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(tp->t_state != TCPS_TIME_WAIT,
|
|
|
|
("%s: timewait", __func__));
|
1999-08-30 21:17:07 +00:00
|
|
|
callout_reset(tp->tt_2msl, 2 * tcp_msl,
|
|
|
|
tcp_timer_2msl, tp);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-12-25 22:23:13 +00:00
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
|
|
|
headlocked = 0;
|
1994-09-15 10:36:56 +00:00
|
|
|
#ifdef TCPDEBUG
|
2002-05-31 11:52:35 +00:00
|
|
|
if (so->so_options & SO_DEBUG)
|
2000-01-09 19:17:30 +00:00
|
|
|
tcp_trace(TA_INPUT, ostate, tp, (void *)tcp_saveipgen,
|
|
|
|
&tcp_savetcp, 0);
|
1994-09-15 10:36:56 +00:00
|
|
|
#endif
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Return any desired output.
|
|
|
|
*/
|
|
|
|
if (needoutput || (tp->t_flags & TF_ACKNOW))
|
|
|
|
(void) tcp_output(tp);
|
2003-03-13 11:46:57 +00:00
|
|
|
|
2003-02-22 21:54:57 +00:00
|
|
|
check_delack:
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked == 0, ("%s: check_delack: head locked",
|
|
|
|
__func__));
|
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
2003-02-22 21:54:57 +00:00
|
|
|
if (tp->t_flags & TF_DELACK) {
|
2003-02-19 21:18:23 +00:00
|
|
|
tp->t_flags &= ~TF_DELACK;
|
2004-08-16 18:32:07 +00:00
|
|
|
callout_reset(tp->tt_delack, tcp_delacktime,
|
|
|
|
tcp_timer_delack, tp);
|
2003-02-19 21:18:23 +00:00
|
|
|
}
|
2007-03-23 20:16:50 +00:00
|
|
|
INP_UNLOCK(tp->t_inpcb);
|
|
|
|
return (0);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
dropafterack:
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: dropafterack: head not locked", __func__));
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Generate an ACK dropping incoming segment if it occupies
|
|
|
|
* sequence space, where the ACK reflects our state.
|
1998-09-11 16:04:03 +00:00
|
|
|
*
|
|
|
|
* 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.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2000-01-09 19:17:30 +00:00
|
|
|
if (tp->t_state == TCPS_SYN_RECEIVED && (thflags & TH_ACK) &&
|
|
|
|
(SEQ_GT(tp->snd_una, th->th_ack) ||
|
2001-02-11 07:39:51 +00:00
|
|
|
SEQ_GT(th->th_ack, tp->snd_max)) ) {
|
|
|
|
rstreason = BANDLIM_RST_OPENPORT;
|
|
|
|
goto dropwithreset;
|
|
|
|
}
|
1995-02-09 23:13:27 +00:00
|
|
|
#ifdef TCPDEBUG
|
2002-05-31 11:52:35 +00:00
|
|
|
if (so->so_options & SO_DEBUG)
|
2000-01-09 19:17:30 +00:00
|
|
|
tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen,
|
|
|
|
&tcp_savetcp, 0);
|
1995-02-09 23:13:27 +00:00
|
|
|
#endif
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: headlocked should be 1", __func__));
|
2004-12-25 22:26:13 +00:00
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->t_flags |= TF_ACKNOW;
|
|
|
|
(void) tcp_output(tp);
|
2007-03-23 20:16:50 +00:00
|
|
|
INP_UNLOCK(tp->t_inpcb);
|
2004-11-07 19:19:35 +00:00
|
|
|
m_freem(m);
|
2007-03-23 20:16:50 +00:00
|
|
|
return (0);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
dropwithreset:
|
2007-03-23 20:16:50 +00:00
|
|
|
KASSERT(headlocked, ("%s: dropwithreset: head not locked", __func__));
|
|
|
|
|
|
|
|
tcp_dropwithreset(m, th, tp, tlen, rstreason);
|
|
|
|
|
|
|
|
if (tp != NULL)
|
|
|
|
INP_UNLOCK(tp->t_inpcb);
|
|
|
|
if (headlocked)
|
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
drop:
|
|
|
|
/*
|
|
|
|
* Drop space held by incoming segment and return.
|
|
|
|
*/
|
|
|
|
#ifdef TCPDEBUG
|
|
|
|
if (tp == NULL || (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
|
|
|
|
tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen,
|
|
|
|
&tcp_savetcp, 0);
|
|
|
|
#endif
|
|
|
|
if (tp != NULL)
|
|
|
|
INP_UNLOCK(tp->t_inpcb);
|
|
|
|
if (headlocked)
|
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
|
|
|
m_freem(m);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Issue RST on TCP segment. The mbuf must still include the original
|
|
|
|
* packet header.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
tcp_dropwithreset(struct mbuf *m, struct tcphdr *th, struct tcpcb *tp,
|
|
|
|
int tlen, int rstreason)
|
|
|
|
{
|
|
|
|
struct ip *ip;
|
|
|
|
#ifdef INET6
|
|
|
|
struct ip6_hdr *ip6;
|
|
|
|
#endif
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Generate a RST, dropping incoming segment.
|
|
|
|
* Make ACK acceptable to originator of segment.
|
|
|
|
* Don't bother to respond if destination was broadcast/multicast.
|
|
|
|
*/
|
2007-03-23 20:16:50 +00:00
|
|
|
if ((th->th_flags & TH_RST) || m->m_flags & (M_BCAST|M_MCAST))
|
1994-05-24 10:09:53 +00:00
|
|
|
goto drop;
|
2007-03-23 20:16:50 +00:00
|
|
|
#ifdef INET6
|
|
|
|
if (mtod(m, struct ip *)->ip_v == 6) {
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
2000-01-28 06:13:09 +00:00
|
|
|
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
|
|
|
|
IN6_IS_ADDR_MULTICAST(&ip6->ip6_src))
|
2000-01-09 19:17:30 +00:00
|
|
|
goto drop;
|
2007-03-23 20:16:50 +00:00
|
|
|
/* IPv6 anycast check is done at tcp6_input() */
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
ip = mtod(m, struct ip *);
|
2002-08-17 02:05:25 +00:00
|
|
|
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
|
|
|
|
IN_MULTICAST(ntohl(ip->ip_src.s_addr)) ||
|
2004-08-16 18:32:07 +00:00
|
|
|
ip->ip_src.s_addr == htonl(INADDR_BROADCAST) ||
|
|
|
|
in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif))
|
2002-08-17 02:05:25 +00:00
|
|
|
goto drop;
|
|
|
|
}
|
2001-02-11 07:39:51 +00:00
|
|
|
|
2007-03-23 20:16:50 +00:00
|
|
|
/* Perform bandwidth limiting. */
|
2001-02-11 07:39:51 +00:00
|
|
|
if (badport_bandlim(rstreason) < 0)
|
|
|
|
goto drop;
|
2004-08-16 18:32:07 +00:00
|
|
|
|
2007-03-23 20:16:50 +00:00
|
|
|
/* tcp_respond consumes the mbuf chain. */
|
|
|
|
if (th->th_flags & TH_ACK) {
|
|
|
|
tcp_respond(tp, mtod(m, void *), th, m, (tcp_seq)0,
|
|
|
|
th->th_ack, TH_RST);
|
|
|
|
} else {
|
|
|
|
if (th->th_flags & TH_SYN)
|
2000-01-09 19:17:30 +00:00
|
|
|
tlen++;
|
|
|
|
tcp_respond(tp, mtod(m, void *), th, m, th->th_seq+tlen,
|
2007-03-23 20:16:50 +00:00
|
|
|
(tcp_seq)0, TH_RST|TH_ACK);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
drop:
|
2004-11-07 19:19:35 +00:00
|
|
|
m_freem(m);
|
1994-05-24 10:09:53 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2001-11-22 04:50:44 +00:00
|
|
|
/*
|
|
|
|
* Parse TCP options and place in tcpopt.
|
|
|
|
*/
|
1995-11-14 20:34:56 +00:00
|
|
|
static void
|
2007-03-21 19:37:55 +00:00
|
|
|
tcp_dooptions(struct tcpopt *to, u_char *cp, int cnt, int flags)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
|
|
|
int opt, optlen;
|
|
|
|
|
2001-11-22 04:50:44 +00:00
|
|
|
to->to_flags = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
for (; cnt > 0; cnt -= optlen, cp += optlen) {
|
|
|
|
opt = cp[0];
|
|
|
|
if (opt == TCPOPT_EOL)
|
|
|
|
break;
|
|
|
|
if (opt == TCPOPT_NOP)
|
|
|
|
optlen = 1;
|
|
|
|
else {
|
2000-07-09 13:01:59 +00:00
|
|
|
if (cnt < 2)
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
optlen = cp[1];
|
2000-07-09 13:01:59 +00:00
|
|
|
if (optlen < 2 || optlen > cnt)
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
switch (opt) {
|
|
|
|
case TCPOPT_MAXSEG:
|
|
|
|
if (optlen != TCPOLEN_MAXSEG)
|
|
|
|
continue;
|
2006-06-26 15:35:25 +00:00
|
|
|
if (!(flags & TO_SYN))
|
1994-05-24 10:09:53 +00:00
|
|
|
continue;
|
2001-11-22 04:50:44 +00:00
|
|
|
to->to_flags |= TOF_MSS;
|
|
|
|
bcopy((char *)cp + 2,
|
|
|
|
(char *)&to->to_mss, sizeof(to->to_mss));
|
2002-02-18 20:35:27 +00:00
|
|
|
to->to_mss = ntohs(to->to_mss);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
case TCPOPT_WINDOW:
|
|
|
|
if (optlen != TCPOLEN_WINDOW)
|
|
|
|
continue;
|
2006-06-26 15:35:25 +00:00
|
|
|
if (!(flags & TO_SYN))
|
1994-05-24 10:09:53 +00:00
|
|
|
continue;
|
2001-11-22 04:50:44 +00:00
|
|
|
to->to_flags |= TOF_SCALE;
|
2007-03-15 15:59:28 +00:00
|
|
|
to->to_wscale = min(cp[2], TCP_MAX_WINSHIFT);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
case TCPOPT_TIMESTAMP:
|
|
|
|
if (optlen != TCPOLEN_TIMESTAMP)
|
|
|
|
continue;
|
2001-11-22 04:50:44 +00:00
|
|
|
to->to_flags |= TOF_TS;
|
1995-02-09 23:13:27 +00:00
|
|
|
bcopy((char *)cp + 2,
|
|
|
|
(char *)&to->to_tsval, sizeof(to->to_tsval));
|
2002-02-18 20:35:27 +00:00
|
|
|
to->to_tsval = ntohl(to->to_tsval);
|
1995-02-09 23:13:27 +00:00
|
|
|
bcopy((char *)cp + 6,
|
|
|
|
(char *)&to->to_tsecr, sizeof(to->to_tsecr));
|
2002-02-18 20:35:27 +00:00
|
|
|
to->to_tsecr = ntohl(to->to_tsecr);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
Initial import of RFC 2385 (TCP-MD5) digest support.
This is the first of two commits; bringing in the kernel support first.
This can be enabled by compiling a kernel with options TCP_SIGNATURE
and FAST_IPSEC.
For the uninitiated, this is a TCP option which provides for a means of
authenticating TCP sessions which came into being before IPSEC. It is
still relevant today, however, as it is used by many commercial router
vendors, particularly with BGP, and as such has become a requirement for
interconnect at many major Internet points of presence.
Several parts of the TCP and IP headers, including the segment payload,
are digested with MD5, including a shared secret. The PF_KEY interface
is used to manage the secrets using security associations in the SADB.
There is a limitation here in that as there is no way to map a TCP flow
per-port back to an SPI without polluting tcpcb or using the SPD; the
code to do the latter is unstable at this time. Therefore this code only
supports per-host keying granularity.
Whilst FAST_IPSEC is mutually exclusive with KAME IPSEC (and thus IPv6),
TCP_SIGNATURE applies only to IPv4. For the vast majority of prospective
users of this feature, this will not pose any problem.
This implementation is output-only; that is, the option is honoured when
responding to a host initiating a TCP session, but no effort is made
[yet] to authenticate inbound traffic. This is, however, sufficient to
interwork with Cisco equipment.
Tested with a Cisco 2501 running IOS 12.0(27), and Quagga 0.96.4 with
local patches. Patches for tcpdump to validate TCP-MD5 sessions are also
available from me upon request.
Sponsored by: sentex.net
2004-02-11 04:26:04 +00:00
|
|
|
#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 | TOF_SIGLEN);
|
|
|
|
break;
|
2004-02-13 18:21:45 +00:00
|
|
|
#endif
|
2004-06-23 21:04:37 +00:00
|
|
|
case TCPOPT_SACK_PERMITTED:
|
2006-06-26 15:35:25 +00:00
|
|
|
if (optlen != TCPOLEN_SACK_PERMITTED)
|
2004-06-23 21:04:37 +00:00
|
|
|
continue;
|
2006-06-26 15:35:25 +00:00
|
|
|
if (!(flags & TO_SYN))
|
|
|
|
continue;
|
|
|
|
if (!tcp_do_sack)
|
|
|
|
continue;
|
2007-03-23 18:33:21 +00:00
|
|
|
to->to_flags |= TOF_SACKPERM;
|
2004-06-23 21:04:37 +00:00
|
|
|
break;
|
|
|
|
case TCPOPT_SACK:
|
2005-06-27 22:27:42 +00:00
|
|
|
if (optlen <= 2 || (optlen - 2) % TCPOLEN_SACK != 0)
|
2004-06-23 21:04:37 +00:00
|
|
|
continue;
|
2007-03-23 18:33:21 +00:00
|
|
|
to->to_flags |= TOF_SACK;
|
2005-06-27 22:27:42 +00:00
|
|
|
to->to_nsacks = (optlen - 2) / TCPOLEN_SACK;
|
|
|
|
to->to_sacks = cp + 2;
|
|
|
|
tcpstat.tcps_sack_rcv_blocks++;
|
2004-06-23 21:04:37 +00:00
|
|
|
break;
|
2001-11-22 04:50:44 +00:00
|
|
|
default:
|
|
|
|
continue;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pull out of band byte out of a segment so
|
|
|
|
* it doesn't appear in the user's data queue.
|
|
|
|
* It is still reflected in the segment length for
|
|
|
|
* sequencing purposes.
|
|
|
|
*/
|
1995-11-14 20:34:56 +00:00
|
|
|
static void
|
2007-03-21 19:37:55 +00:00
|
|
|
tcp_pulloutofband(struct socket *so, struct tcphdr *th, struct mbuf *m,
|
|
|
|
int off)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2000-01-09 19:17:30 +00:00
|
|
|
int cnt = off + th->th_urp - 1;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
while (cnt >= 0) {
|
|
|
|
if (m->m_len > cnt) {
|
|
|
|
char *cp = mtod(m, caddr_t) + cnt;
|
|
|
|
struct tcpcb *tp = sototcpcb(so);
|
|
|
|
|
|
|
|
tp->t_iobc = *cp;
|
|
|
|
tp->t_oobflags |= TCPOOB_HAVEDATA;
|
|
|
|
bcopy(cp+1, cp, (unsigned)(m->m_len - cnt - 1));
|
|
|
|
m->m_len--;
|
2000-01-25 01:26:47 +00:00
|
|
|
if (m->m_flags & M_PKTHDR)
|
|
|
|
m->m_pkthdr.len--;
|
1994-05-24 10:09:53 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
cnt -= m->m_len;
|
|
|
|
m = m->m_next;
|
2007-03-23 19:11:22 +00:00
|
|
|
if (m == NULL)
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
panic("tcp_pulloutofband");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Collect new round-trip time estimate
|
|
|
|
* and update averages and current timeout.
|
|
|
|
*/
|
1995-11-14 20:34:56 +00:00
|
|
|
static void
|
2007-03-21 19:37:55 +00:00
|
|
|
tcp_xmit_timer(struct tcpcb *tp, int rtt)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2007-03-21 19:37:55 +00:00
|
|
|
int delta;
|
1996-03-22 18:09:21 +00:00
|
|
|
|
2004-11-28 11:06:22 +00:00
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
|
|
|
|
1996-03-22 18:09:21 +00:00
|
|
|
tcpstat.tcps_rttupdated++;
|
|
|
|
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;
|
2002-08-17 18:26:02 +00:00
|
|
|
if (tp->t_rttbest > tp->t_srtt + tp->t_rttvar)
|
|
|
|
tp->t_rttbest = tp->t_srtt + tp->t_rttvar;
|
1996-03-22 18:09:21 +00:00
|
|
|
} 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);
|
2002-08-17 18:26:02 +00:00
|
|
|
tp->t_rttbest = tp->t_srtt + tp->t_rttvar;
|
1996-03-22 18:09:21 +00:00
|
|
|
}
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->t_rtttime = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
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).
|
|
|
|
*/
|
1996-03-22 18:09:21 +00:00
|
|
|
TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),
|
1996-03-25 20:13:21 +00:00
|
|
|
max(tp->t_rttmin, rtt + 2), TCPTV_REXMTMAX);
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 bigger than
|
|
|
|
* an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES
|
|
|
|
* to utilize large mbufs. 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.
|
1995-02-09 23:13:27 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* In case of T/TCP, we call this routine during implicit connection
|
|
|
|
* setup as well (offer = -1), to initialize maxseg from the cached
|
|
|
|
* MSS of our peer.
|
2003-11-20 20:07:39 +00:00
|
|
|
*
|
|
|
|
* NOTE that this routine is only called when we process an incoming
|
|
|
|
* segment. Outgoing SYN/ACK MSS settings are handled in tcp_mssopt().
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
1995-02-09 23:13:27 +00:00
|
|
|
void
|
2007-03-21 19:37:55 +00:00
|
|
|
tcp_mss(struct tcpcb *tp, int offer)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2003-11-20 20:07:39 +00:00
|
|
|
int rtt, mss;
|
1994-05-24 10:09:53 +00:00
|
|
|
u_long bufsize;
|
2003-11-20 20:07:39 +00:00
|
|
|
u_long maxmtu;
|
2002-08-17 02:05:25 +00:00
|
|
|
struct inpcb *inp = tp->t_inpcb;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct socket *so;
|
2003-11-20 20:07:39 +00:00
|
|
|
struct hc_metrics_lite metrics;
|
1995-02-09 23:13:27 +00:00
|
|
|
int origoffer = offer;
|
2006-09-06 21:51:59 +00:00
|
|
|
int mtuflags = 0;
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
2002-08-17 02:05:25 +00:00
|
|
|
int isipv6 = ((inp->inp_vflag & INP_IPV6) != 0) ? 1 : 0;
|
|
|
|
size_t min_protoh = isipv6 ?
|
|
|
|
sizeof (struct ip6_hdr) + sizeof (struct tcphdr) :
|
|
|
|
sizeof (struct tcpiphdr);
|
2000-01-09 19:17:30 +00:00
|
|
|
#else
|
2003-11-20 20:07:39 +00:00
|
|
|
const size_t min_protoh = sizeof(struct tcpiphdr);
|
2000-01-09 19:17:30 +00:00
|
|
|
#endif
|
2002-08-17 02:05:25 +00:00
|
|
|
|
2003-11-20 20:07:39 +00:00
|
|
|
/* initialize */
|
|
|
|
#ifdef INET6
|
|
|
|
if (isipv6) {
|
2006-09-06 21:51:59 +00:00
|
|
|
maxmtu = tcp_maxmtu6(&inp->inp_inc, &mtuflags);
|
2003-11-20 20:07:39 +00:00
|
|
|
tp->t_maxopd = tp->t_maxseg = tcp_v6mssdflt;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
2006-09-06 21:51:59 +00:00
|
|
|
maxmtu = tcp_maxmtu(&inp->inp_inc, &mtuflags);
|
2003-11-20 20:07:39 +00:00
|
|
|
tp->t_maxopd = tp->t_maxseg = tcp_mssdflt;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
so = inp->inp_socket;
|
|
|
|
|
1995-02-09 23:13:27 +00:00
|
|
|
/*
|
2004-04-23 22:44:59 +00:00
|
|
|
* no route to sender, stay with default mss and return
|
1995-02-09 23:13:27 +00:00
|
|
|
*/
|
2003-11-20 20:07:39 +00:00
|
|
|
if (maxmtu == 0)
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
offer =
|
|
|
|
#ifdef INET6
|
|
|
|
isipv6 ? tcp_v6mssdflt :
|
|
|
|
#endif
|
|
|
|
tcp_mssdflt;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case -1:
|
|
|
|
/*
|
2004-11-02 22:22:22 +00:00
|
|
|
* Offer == -1 means that we didn't receive SYN yet.
|
2003-11-20 20:07:39 +00:00
|
|
|
*/
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
|
|
default:
|
2004-01-08 17:40:07 +00:00
|
|
|
/*
|
|
|
|
* Prevent DoS attack with too small MSS. Round up
|
|
|
|
* to at least minmss.
|
|
|
|
*/
|
|
|
|
offer = max(offer, tcp_minmss);
|
2003-11-20 20:07:39 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
offer = max(offer, 64);
|
|
|
|
}
|
1995-02-09 23:13:27 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
2003-11-20 20:07:39 +00:00
|
|
|
* rmx information is now retrieved from tcp_hostcache
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2003-11-20 20:07:39 +00:00
|
|
|
tcp_hc_get(&inp->inp_inc, &metrics);
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
2003-11-20 20:07:39 +00:00
|
|
|
* if there's a discovered mtu int tcp hostcache, use it
|
2000-01-09 19:17:30 +00:00
|
|
|
* else, use the link mtu.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2003-11-20 20:07:39 +00:00
|
|
|
if (metrics.rmx_mtu)
|
2004-04-23 22:44:59 +00:00
|
|
|
mss = min(metrics.rmx_mtu, maxmtu) - min_protoh;
|
2002-08-17 02:05:25 +00:00
|
|
|
else {
|
2003-10-20 16:19:01 +00:00
|
|
|
#ifdef INET6
|
2000-01-09 19:17:30 +00:00
|
|
|
if (isipv6) {
|
2003-11-20 20:07:39 +00:00
|
|
|
mss = maxmtu - min_protoh;
|
|
|
|
if (!path_mtu_discovery &&
|
|
|
|
!in6_localaddr(&inp->in6p_faddr))
|
2000-01-09 19:17:30 +00:00
|
|
|
mss = min(mss, tcp_v6mssdflt);
|
2003-10-20 16:19:01 +00:00
|
|
|
} else
|
|
|
|
#endif
|
2003-11-20 20:07:39 +00:00
|
|
|
{
|
|
|
|
mss = maxmtu - min_protoh;
|
|
|
|
if (!path_mtu_discovery &&
|
|
|
|
!in_localaddr(inp->inp_faddr))
|
2002-08-17 02:05:25 +00:00
|
|
|
mss = min(mss, tcp_mssdflt);
|
2003-11-20 20:07:39 +00:00
|
|
|
}
|
1995-02-09 23:13:27 +00:00
|
|
|
}
|
|
|
|
mss = min(mss, offer);
|
2003-11-20 20:07:39 +00:00
|
|
|
|
1995-02-09 23:13:27 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
/*
|
2004-11-02 22:22:22 +00:00
|
|
|
* origoffer==-1 indicates, that no segments were received yet.
|
|
|
|
* In this case we just guess.
|
1995-02-09 23:13:27 +00:00
|
|
|
*/
|
2004-08-16 18:32:07 +00:00
|
|
|
if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
|
1995-02-09 23:13:27 +00:00
|
|
|
(origoffer == -1 ||
|
|
|
|
(tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP))
|
|
|
|
mss -= TCPOLEN_TSTAMP_APPA;
|
2003-11-20 20:07:39 +00:00
|
|
|
tp->t_maxseg = mss;
|
1995-02-09 23:13:27 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
#if (MCLBYTES & (MCLBYTES - 1)) == 0
|
|
|
|
if (mss > MCLBYTES)
|
|
|
|
mss &= ~(MCLBYTES-1);
|
|
|
|
#else
|
|
|
|
if (mss > MCLBYTES)
|
|
|
|
mss = mss / MCLBYTES * MCLBYTES;
|
|
|
|
#endif
|
2003-11-20 20:07:39 +00:00
|
|
|
tp->t_maxseg = mss;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
2003-11-20 20:07:39 +00:00
|
|
|
* 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.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2004-06-24 01:37:04 +00:00
|
|
|
SOCKBUF_LOCK(&so->so_snd);
|
2003-11-20 20:07:39 +00:00
|
|
|
if ((so->so_snd.sb_hiwat == tcp_sendspace) && metrics.rmx_sendpipe)
|
|
|
|
bufsize = metrics.rmx_sendpipe;
|
|
|
|
else
|
1995-02-09 23:13:27 +00:00
|
|
|
bufsize = so->so_snd.sb_hiwat;
|
|
|
|
if (bufsize < mss)
|
|
|
|
mss = bufsize;
|
|
|
|
else {
|
|
|
|
bufsize = roundup(bufsize, mss);
|
|
|
|
if (bufsize > sb_max)
|
|
|
|
bufsize = sb_max;
|
2002-07-22 22:31:09 +00:00
|
|
|
if (bufsize > so->so_snd.sb_hiwat)
|
2004-06-24 01:37:04 +00:00
|
|
|
(void)sbreserve_locked(&so->so_snd, bufsize, so, NULL);
|
1995-02-09 23:13:27 +00:00
|
|
|
}
|
2004-06-24 01:37:04 +00:00
|
|
|
SOCKBUF_UNLOCK(&so->so_snd);
|
1995-02-09 23:13:27 +00:00
|
|
|
tp->t_maxseg = mss;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2004-06-24 01:37:04 +00:00
|
|
|
SOCKBUF_LOCK(&so->so_rcv);
|
2003-11-20 20:07:39 +00:00
|
|
|
if ((so->so_rcv.sb_hiwat == tcp_recvspace) && metrics.rmx_recvpipe)
|
|
|
|
bufsize = metrics.rmx_recvpipe;
|
|
|
|
else
|
1995-02-09 23:13:27 +00:00
|
|
|
bufsize = so->so_rcv.sb_hiwat;
|
|
|
|
if (bufsize > mss) {
|
|
|
|
bufsize = roundup(bufsize, mss);
|
|
|
|
if (bufsize > sb_max)
|
|
|
|
bufsize = sb_max;
|
2002-07-22 22:31:09 +00:00
|
|
|
if (bufsize > so->so_rcv.sb_hiwat)
|
2004-06-24 01:37:04 +00:00
|
|
|
(void)sbreserve_locked(&so->so_rcv, bufsize, so, NULL);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2004-06-24 01:37:04 +00:00
|
|
|
SOCKBUF_UNLOCK(&so->so_rcv);
|
2003-11-20 20:07:39 +00:00
|
|
|
/*
|
|
|
|
* While we're here, check the others too
|
|
|
|
*/
|
|
|
|
if (tp->t_srtt == 0 && (rtt = metrics.rmx_rtt)) {
|
|
|
|
tp->t_srtt = rtt;
|
|
|
|
tp->t_rttbest = tp->t_srtt + TCP_RTT_SCALE;
|
|
|
|
tcpstat.tcps_usedrtt++;
|
|
|
|
if (metrics.rmx_rttvar) {
|
|
|
|
tp->t_rttvar = metrics.rmx_rttvar;
|
|
|
|
tcpstat.tcps_usedrttvar++;
|
|
|
|
} 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 * mss, metrics.rmx_ssthresh);
|
|
|
|
tcpstat.tcps_usedssthresh++;
|
|
|
|
}
|
|
|
|
if (metrics.rmx_bandwidth)
|
|
|
|
tp->snd_bandwidth = metrics.rmx_bandwidth;
|
1999-08-30 21:17:07 +00:00
|
|
|
|
1995-02-09 23:13:27 +00:00
|
|
|
/*
|
1999-08-30 21:17:07 +00:00
|
|
|
* Set the slow-start flight size depending on whether this
|
|
|
|
* is a local network or not.
|
2003-11-20 20:07:39 +00:00
|
|
|
*
|
|
|
|
* Extend this so we cache the cwnd too and retrieve it here.
|
|
|
|
* Make cwnd even bigger than RFC3390 suggests but only if we
|
|
|
|
* have previous experience with the remote host. Be careful
|
|
|
|
* not make cwnd bigger than remote receive window or our own
|
|
|
|
* send socket buffer. Maybe put some additional upper bound
|
|
|
|
* on the retrieved cwnd. Should do incremental updates to
|
|
|
|
* hostcache when cwnd collapses so next connection doesn't
|
|
|
|
* overloads the path again.
|
|
|
|
*
|
|
|
|
* RFC3390 says only do this if SYN or SYN/ACK didn't got lost.
|
|
|
|
* We currently check only in syncache_socket for that.
|
1995-02-09 23:13:27 +00:00
|
|
|
*/
|
2003-11-20 20:07:39 +00:00
|
|
|
#define TCP_METRICS_CWND
|
|
|
|
#ifdef TCP_METRICS_CWND
|
|
|
|
if (metrics.rmx_cwnd)
|
|
|
|
tp->snd_cwnd = max(mss,
|
|
|
|
min(metrics.rmx_cwnd / 2,
|
|
|
|
min(tp->snd_wnd, so->so_snd.sb_hiwat)));
|
|
|
|
else
|
|
|
|
#endif
|
2003-03-13 01:43:45 +00:00
|
|
|
if (tcp_do_rfc3390)
|
|
|
|
tp->snd_cwnd = min(4 * mss, max(2 * mss, 4380));
|
2003-11-20 20:07:39 +00:00
|
|
|
#ifdef INET6
|
2003-03-13 01:43:45 +00:00
|
|
|
else if ((isipv6 && in6_localaddr(&inp->in6p_faddr)) ||
|
2003-11-25 20:58:59 +00:00
|
|
|
(!isipv6 && in_localaddr(inp->inp_faddr)))
|
|
|
|
#else
|
|
|
|
else if (in_localaddr(inp->inp_faddr))
|
2003-11-20 20:07:39 +00:00
|
|
|
#endif
|
2003-11-25 20:58:59 +00:00
|
|
|
tp->snd_cwnd = mss * ss_fltsz_local;
|
2003-03-13 01:43:45 +00:00
|
|
|
else
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->snd_cwnd = mss * ss_fltsz;
|
2006-09-06 21:51:59 +00:00
|
|
|
|
|
|
|
/* Check the interface for TSO capabilities. */
|
|
|
|
if (mtuflags & CSUM_TSO)
|
|
|
|
tp->t_flags |= TF_TSO;
|
1995-02-09 23:13:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine the MSS option to send on an outgoing SYN.
|
|
|
|
*/
|
|
|
|
int
|
2007-03-21 19:37:55 +00:00
|
|
|
tcp_mssopt(struct in_conninfo *inc)
|
1995-02-09 23:13:27 +00:00
|
|
|
{
|
2003-11-20 20:07:39 +00:00
|
|
|
int mss = 0;
|
|
|
|
u_long maxmtu = 0;
|
|
|
|
u_long thcmtu = 0;
|
|
|
|
size_t min_protoh;
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
2003-11-20 20:07:39 +00:00
|
|
|
int isipv6 = inc->inc_isipv6 ? 1 : 0;
|
2000-01-09 19:17:30 +00:00
|
|
|
#endif
|
2002-08-17 02:05:25 +00:00
|
|
|
|
2003-11-20 20:07:39 +00:00
|
|
|
KASSERT(inc != NULL, ("tcp_mssopt with NULL in_conninfo pointer"));
|
1995-02-09 23:13:27 +00:00
|
|
|
|
2003-10-20 15:27:48 +00:00
|
|
|
#ifdef INET6
|
2003-11-20 20:07:39 +00:00
|
|
|
if (isipv6) {
|
|
|
|
mss = tcp_v6mssdflt;
|
2006-09-06 21:51:59 +00:00
|
|
|
maxmtu = tcp_maxmtu6(inc, NULL);
|
2003-11-20 20:07:39 +00:00
|
|
|
thcmtu = tcp_hc_getmtu(inc); /* IPv4 and IPv6 */
|
|
|
|
min_protoh = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
|
|
|
|
} else
|
2003-10-20 15:27:48 +00:00
|
|
|
#endif
|
2003-11-20 20:07:39 +00:00
|
|
|
{
|
|
|
|
mss = tcp_mssdflt;
|
2006-09-06 21:51:59 +00:00
|
|
|
maxmtu = tcp_maxmtu(inc, NULL);
|
2003-11-20 20:07:39 +00:00
|
|
|
thcmtu = tcp_hc_getmtu(inc); /* IPv4 and IPv6 */
|
|
|
|
min_protoh = sizeof(struct tcpiphdr);
|
|
|
|
}
|
|
|
|
if (maxmtu && thcmtu)
|
|
|
|
mss = min(maxmtu, thcmtu) - min_protoh;
|
|
|
|
else if (maxmtu || thcmtu)
|
|
|
|
mss = max(maxmtu, thcmtu) - min_protoh;
|
|
|
|
|
|
|
|
return (mss);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2000-05-06 03:31:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
2002-08-17 02:05:25 +00:00
|
|
|
* 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.
|
2000-05-06 03:31:09 +00:00
|
|
|
*/
|
2002-08-17 02:05:25 +00:00
|
|
|
static void
|
2007-03-21 19:37:55 +00:00
|
|
|
tcp_newreno_partial_ack(struct tcpcb *tp, struct tcphdr *th)
|
2000-05-06 03:31:09 +00:00
|
|
|
{
|
2002-08-17 02:05:25 +00:00
|
|
|
tcp_seq onxt = tp->snd_nxt;
|
|
|
|
u_long ocwnd = tp->snd_cwnd;
|
2000-05-06 03:31:09 +00:00
|
|
|
|
2002-08-17 02:05:25 +00:00
|
|
|
callout_stop(tp->tt_rexmt);
|
|
|
|
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 + (th->th_ack - tp->snd_una);
|
2002-09-30 18:55:45 +00:00
|
|
|
tp->t_flags |= TF_ACKNOW;
|
2002-08-17 02:05:25 +00:00
|
|
|
(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.
|
|
|
|
*/
|
2005-07-05 19:23:02 +00:00
|
|
|
if (tp->snd_cwnd > th->th_ack - tp->snd_una)
|
|
|
|
tp->snd_cwnd -= th->th_ack - tp->snd_una;
|
|
|
|
else
|
|
|
|
tp->snd_cwnd = 0;
|
|
|
|
tp->snd_cwnd += tp->t_maxseg;
|
2000-05-06 03:31:09 +00:00
|
|
|
}
|
2003-02-19 22:32:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2004-08-16 18:32:07 +00:00
|
|
|
static int
|
2007-03-21 19:37:55 +00:00
|
|
|
tcp_timewait(struct inpcb *inp, struct tcpopt *to, struct tcphdr *th,
|
|
|
|
struct mbuf *m, int tlen)
|
2003-02-19 22:32:43 +00:00
|
|
|
{
|
2006-04-09 16:59:19 +00:00
|
|
|
struct tcptw *tw;
|
2003-02-19 22:32:43 +00:00
|
|
|
int thflags;
|
|
|
|
tcp_seq seq;
|
|
|
|
#ifdef INET6
|
|
|
|
int isipv6 = (mtod(m, struct ip *)->ip_v == 6) ? 1 : 0;
|
|
|
|
#else
|
|
|
|
const int isipv6 = 0;
|
|
|
|
#endif
|
|
|
|
|
2006-09-07 13:06:00 +00:00
|
|
|
/* tcbinfo lock required for tcp_twclose(), tcp_timer_2msl_reset(). */
|
2006-04-09 16:59:19 +00:00
|
|
|
INP_INFO_WLOCK_ASSERT(&tcbinfo);
|
|
|
|
INP_LOCK_ASSERT(inp);
|
|
|
|
|
2006-04-04 12:26:07 +00:00
|
|
|
/*
|
|
|
|
* XXXRW: Time wait state for inpcb has been recycled, but inpcb is
|
|
|
|
* still present. This is undesirable, but temporarily necessary
|
|
|
|
* until we work out how to handle inpcb's who's timewait state has
|
|
|
|
* been removed.
|
|
|
|
*/
|
2006-04-09 16:59:19 +00:00
|
|
|
tw = intotw(inp);
|
2006-04-04 12:26:07 +00:00
|
|
|
if (tw == NULL)
|
|
|
|
goto drop;
|
|
|
|
|
2003-02-19 22:32:43 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
#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, tw->rcv_nxt)) {
|
Update TCP for infrastructural changes to the socket/pcb refcount model,
pru_abort(), pru_detach(), and in_pcbdetach():
- Universally support and enforce the invariant that so_pcb is
never NULL, converting dozens of unnecessary NULL checks into
assertions, and eliminating dozens of unnecessary error handling
cases in protocol code.
- In some cases, eliminate unnecessary pcbinfo locking, as it is no
longer required to ensure so_pcb != NULL. For example, the receive
code no longer requires the pcbinfo lock, and the send code only
requires it if building a new connection on an otherwise unconnected
socket triggered via sendto() with an address. This should
significnatly reduce tcbinfo lock contention in the receive and send
cases.
- In order to support the invariant that so_pcb != NULL, it is now
necessary for the TCP code to not discard the tcpcb any time a
connection is dropped, but instead leave the tcpcb until the socket
is shutdown. This case is handled by setting INP_DROPPED, to
substitute for using a NULL so_pcb to indicate that the connection
has been dropped. This requires the inpcb lock, but not the pcbinfo
lock.
- Unlike all other protocols in the tree, TCP may need to retain access
to the socket after the file descriptor has been closed. Set
SS_PROTOREF in tcp_detach() in order to prevent the socket from being
freed, and add a flag, INP_SOCKREF, so that the TCP code knows whether
or not it needs to free the socket when the connection finally does
close. The typical case where this occurs is if close() is called on
a TCP socket before all sent data in the send socket buffer has been
transmitted or acknowledged. If INP_SOCKREF is found when the
connection is dropped, we release the inpcb, tcpcb, and socket instead
of flagging INP_DROPPED.
- Abort and detach protocol switch methods no longer return failures,
nor attempt to free sockets, as the socket layer does this.
- Annotate the existence of a long-standing race in the TCP timer code,
in which timers are stopped but not drained when the socket is freed,
as waiting for drain may lead to deadlocks, or have to occur in a
context where waiting is not permitted. This race has been handled
by testing to see if the tcpcb pointer in the inpcb is NULL (and vice
versa), which is not normally permitted, but may be true of a inpcb
and tcpcb have been freed. Add a counter to test how often this race
has actually occurred, and a large comment for each instance where
we compare potentially freed memory with NULL. This will have to be
fixed in the near future, but requires is to further address how to
handle the timer shutdown shutdown issue.
- Several TCP calls no longer potentially free the passed inpcb/tcpcb,
so no longer need to return a pointer to indicate whether the argument
passed in is still valid.
- Un-macroize debugging and locking setup for various protocol switch
methods for TCP, as it lead to more obscurity, and as locking becomes
more customized to the methods, offers less benefit.
- Assert copyright on tcp_usrreq.c due to significant modifications that
have been made as part of this work.
These changes significantly modify the memory management and connection
logic of our TCP implementation, and are (as such) High Risk Changes,
and likely to contain serious bugs. Please report problems to the
current@ mailing list ASAP, ideally with simple test cases, and
optionally, packet traces.
MFC after: 3 months
2006-04-01 16:36:36 +00:00
|
|
|
tcp_twclose(tw, 0);
|
2003-02-19 22:32:43 +00:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Drop the 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 == tw->rcv_nxt)
|
2006-09-07 13:06:00 +00:00
|
|
|
tcp_timer_2msl_reset(tw, 1);
|
2003-02-19 22:32:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-02-26 18:20:41 +00:00
|
|
|
* Acknowledge the segment if it has data or is not a duplicate ACK.
|
2003-02-19 22:32:43 +00:00
|
|
|
*/
|
2004-08-16 18:32:07 +00:00
|
|
|
if (thflags != TH_ACK || tlen != 0 ||
|
2003-02-26 18:20:41 +00:00
|
|
|
th->th_seq != tw->rcv_nxt || th->th_ack != tw->snd_nxt)
|
2004-02-28 15:12:20 +00:00
|
|
|
tcp_twrespond(tw, TH_ACK);
|
2003-02-19 22:32:43 +00:00
|
|
|
goto drop;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate a RST, dropping incoming segment.
|
|
|
|
* Make ACK acceptable to originator of segment.
|
|
|
|
* Don't bother to respond if destination was broadcast/multicast.
|
|
|
|
*/
|
|
|
|
if (m->m_flags & (M_BCAST|M_MCAST))
|
|
|
|
goto drop;
|
|
|
|
if (isipv6) {
|
|
|
|
struct ip6_hdr *ip6;
|
|
|
|
|
|
|
|
/* IPv6 anycast check is done at tcp6_input() */
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
|
|
|
|
IN6_IS_ADDR_MULTICAST(&ip6->ip6_src))
|
|
|
|
goto drop;
|
|
|
|
} else {
|
|
|
|
struct ip *ip;
|
|
|
|
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
|
|
|
|
IN_MULTICAST(ntohl(ip->ip_src.s_addr)) ||
|
|
|
|
ip->ip_src.s_addr == htonl(INADDR_BROADCAST) ||
|
|
|
|
in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif))
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
if (thflags & TH_ACK) {
|
|
|
|
tcp_respond(NULL,
|
|
|
|
mtod(m, void *), th, m, 0, th->th_ack, TH_RST);
|
|
|
|
} else {
|
|
|
|
seq = th->th_seq + (thflags & TH_SYN ? 1 : 0);
|
|
|
|
tcp_respond(NULL,
|
|
|
|
mtod(m, void *), th, m, seq, 0, TH_RST|TH_ACK);
|
|
|
|
}
|
2006-04-09 16:59:19 +00:00
|
|
|
INP_UNLOCK(inp);
|
2003-02-19 22:32:43 +00:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
drop:
|
2006-04-09 16:59:19 +00:00
|
|
|
INP_UNLOCK(inp);
|
2003-02-19 22:32:43 +00:00
|
|
|
m_freem(m);
|
|
|
|
return (0);
|
|
|
|
}
|