2005-01-07 01:45:51 +00:00
|
|
|
/*-
|
1995-10-03 16:54:17 +00:00
|
|
|
* Copyright (c) 1982, 1986, 1988, 1990, 1993, 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_subr.c 8.2 (Berkeley) 5/24/95
|
1999-08-28 01:08:13 +00:00
|
|
|
* $FreeBSD$
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
|
1998-01-25 04:23:33 +00:00
|
|
|
#include "opt_compat.h"
|
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"
|
1999-12-07 17:39:16 +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>
|
|
|
|
#include <sys/systm.h>
|
1999-08-30 21:17:07 +00:00
|
|
|
#include <sys/callout.h>
|
1995-11-09 20:23:09 +00:00
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/sysctl.h>
|
2002-07-31 19:06:49 +00:00
|
|
|
#include <sys/mac.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/mbuf.h>
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
#include <sys/domain.h>
|
|
|
|
#endif
|
1999-07-11 18:32:46 +00:00
|
|
|
#include <sys/proc.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/socketvar.h>
|
|
|
|
#include <sys/protosw.h>
|
2001-04-17 18:08:01 +00:00
|
|
|
#include <sys/random.h>
|
1998-03-28 10:18:26 +00:00
|
|
|
|
2002-03-20 05:48:55 +00:00
|
|
|
#include <vm/uma.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
#include <net/route.h>
|
|
|
|
#include <net/if.h>
|
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/in_systm.h>
|
|
|
|
#include <netinet/ip.h>
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
#include <netinet/ip6.h>
|
|
|
|
#endif
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <netinet/in_pcb.h>
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
#include <netinet6/in6_pcb.h>
|
|
|
|
#endif
|
1995-03-16 18:17:34 +00:00
|
|
|
#include <netinet/in_var.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <netinet/ip_var.h>
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
#include <netinet6/ip6_var.h>
|
2003-11-20 20:07:39 +00:00
|
|
|
#include <netinet6/nd6.h>
|
2000-01-09 19:17:30 +00:00
|
|
|
#endif
|
2005-04-21 14:29:34 +00:00
|
|
|
#include <netinet/ip_icmp.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
|
|
|
#ifdef INET6
|
|
|
|
#include <netinet6/tcp6_var.h>
|
|
|
|
#endif
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <netinet/tcpip.h>
|
1995-02-09 23:13:27 +00:00
|
|
|
#ifdef TCPDEBUG
|
|
|
|
#include <netinet/tcp_debug.h>
|
|
|
|
#endif
|
2000-01-09 19:17:30 +00:00
|
|
|
#include <netinet6/ip6protosw.h>
|
|
|
|
|
|
|
|
#ifdef IPSEC
|
|
|
|
#include <netinet6/ipsec.h>
|
2000-07-04 16:35:15 +00:00
|
|
|
#ifdef INET6
|
|
|
|
#include <netinet6/ipsec6.h>
|
|
|
|
#endif
|
2004-11-08 18:49:51 +00:00
|
|
|
#include <netkey/key.h>
|
2000-01-09 19:17:30 +00:00
|
|
|
#endif /*IPSEC*/
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2002-10-16 02:25:05 +00:00
|
|
|
#ifdef FAST_IPSEC
|
|
|
|
#include <netipsec/ipsec.h>
|
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 <netipsec/xform.h>
|
2002-10-16 02:25:05 +00:00
|
|
|
#ifdef INET6
|
|
|
|
#include <netipsec/ipsec6.h>
|
|
|
|
#endif
|
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 <netipsec/key.h>
|
2002-10-16 02:25:05 +00:00
|
|
|
#define IPSEC
|
|
|
|
#endif /*FAST_IPSEC*/
|
|
|
|
|
2000-03-27 19:14:27 +00:00
|
|
|
#include <machine/in_cksum.h>
|
2001-08-22 00:58:16 +00:00
|
|
|
#include <sys/md5.h>
|
2000-03-27 19:14:27 +00:00
|
|
|
|
2004-08-16 18:32:07 +00:00
|
|
|
int tcp_mssdflt = TCP_MSS;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, TCPCTL_MSSDFLT, mssdflt, CTLFLAG_RW,
|
1999-05-03 23:57:32 +00:00
|
|
|
&tcp_mssdflt , 0, "Default TCP Maximum Segment Size");
|
1995-11-14 20:34:56 +00:00
|
|
|
|
1999-11-05 14:41:39 +00:00
|
|
|
#ifdef INET6
|
|
|
|
int tcp_v6mssdflt = TCP6_MSS;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, TCPCTL_V6MSSDFLT, v6mssdflt,
|
2000-01-09 19:17:30 +00:00
|
|
|
CTLFLAG_RW, &tcp_v6mssdflt , 0,
|
|
|
|
"Default TCP Maximum Segment Size for IPv6");
|
1999-11-05 14:41:39 +00:00
|
|
|
#endif
|
|
|
|
|
2004-01-08 17:40:07 +00:00
|
|
|
/*
|
|
|
|
* Minimum MSS we accept and use. This prevents DoS attacks where
|
|
|
|
* we are forced to a ridiculous low MSS like 20 and send hundreds
|
|
|
|
* of packets instead of one. The effect scales with the available
|
|
|
|
* bandwidth and quickly saturates the CPU and network interface
|
|
|
|
* with packet generation and sending. Set to zero to disable MINMSS
|
|
|
|
* checking. This setting prevents us from sending too small packets.
|
|
|
|
*/
|
|
|
|
int tcp_minmss = TCP_MINMSS;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, minmss, CTLFLAG_RW,
|
|
|
|
&tcp_minmss , 0, "Minmum TCP Maximum Segment Size");
|
|
|
|
/*
|
|
|
|
* Number of TCP segments per second we accept from remote host
|
|
|
|
* before we start to calculate average segment size. If average
|
|
|
|
* segment size drops below the minimum TCP MSS we assume a DoS
|
|
|
|
* attack and reset+drop the connection. Care has to be taken not to
|
|
|
|
* set this value too small to not kill interactive type connections
|
|
|
|
* (telnet, SSH) which send many small packets.
|
|
|
|
*/
|
|
|
|
int tcp_minmssoverload = TCP_MINMSSOVERLOAD;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, minmssoverload, CTLFLAG_RW,
|
|
|
|
&tcp_minmssoverload , 0, "Number of TCP Segments per Second allowed to"
|
|
|
|
"be under the MINMSS Size");
|
|
|
|
|
1999-08-30 21:17:07 +00:00
|
|
|
#if 0
|
2004-08-16 18:32:07 +00:00
|
|
|
static int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, TCPCTL_RTTDFLT, rttdflt, CTLFLAG_RW,
|
1999-05-03 23:57:32 +00:00
|
|
|
&tcp_rttdflt , 0, "Default maximum TCP Round Trip Time");
|
1999-08-30 21:17:07 +00:00
|
|
|
#endif
|
1995-11-14 20:34:56 +00:00
|
|
|
|
2001-11-22 04:50:44 +00:00
|
|
|
int tcp_do_rfc1323 = 1;
|
2004-08-16 18:32:07 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp, TCPCTL_DO_RFC1323, rfc1323, CTLFLAG_RW,
|
1999-05-03 23:57:32 +00:00
|
|
|
&tcp_do_rfc1323 , 0, "Enable rfc1323 (high performance TCP) extensions");
|
1995-11-14 20:34:56 +00:00
|
|
|
|
1999-08-26 19:52:17 +00:00
|
|
|
static int tcp_tcbhashsize = 0;
|
2003-10-21 18:28:36 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcbhashsize, CTLFLAG_RDTUN,
|
1999-08-26 19:52:17 +00:00
|
|
|
&tcp_tcbhashsize, 0, "Size of TCP control-block hashtable");
|
|
|
|
|
1999-12-28 23:18:33 +00:00
|
|
|
static int do_tcpdrain = 1;
|
2000-09-25 23:40:22 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, do_tcpdrain, CTLFLAG_RW, &do_tcpdrain, 0,
|
|
|
|
"Enable tcp_drain routine for extra help when low on mbufs");
|
1999-12-28 23:18:33 +00:00
|
|
|
|
2004-08-16 18:32:07 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, pcbcount, CTLFLAG_RD,
|
1999-05-03 23:57:32 +00:00
|
|
|
&tcbinfo.ipi_count, 0, "Number of active PCBs");
|
1998-05-15 20:11:40 +00:00
|
|
|
|
2001-02-23 20:51:46 +00:00
|
|
|
static int icmp_may_rst = 1;
|
2004-08-16 18:32:07 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, icmp_may_rst, CTLFLAG_RW, &icmp_may_rst, 0,
|
2001-02-23 20:51:46 +00:00
|
|
|
"Certain ICMP unreachable messages may abort connections in SYN_SENT");
|
We currently does not react to ICMP administratively prohibited
messages send by routers when they deny our traffic, this causes
a timeout when trying to connect to TCP ports/services on a remote
host, which is blocked by routers or firewalls.
rfc1122 (Requirements for Internet Hosts) section 3.2.2.1 actually
requi re that we treat such a message for a TCP session, that we
treat it like if we had recieved a RST.
quote begin.
A Destination Unreachable message that is received MUST be
reported to the transport layer. The transport layer SHOULD
use the information appropriately; for example, see Sections
4.1.3.3, 4.2.3.9, and 4.2.4 below. A transport protocol
that has its own mechanism for notifying the sender that a
port is unreachable (e.g., TCP, which sends RST segments)
MUST nevertheless accept an ICMP Port Unreachable for the
same purpose.
quote end.
I've written a small extension that implement this, it also create
a sysctl "net.inet.tcp.icmp_admin_prohib_like_rst" to control if
this new behaviour is activated.
When it's activated (set to 1) we'll treat a ICMP administratively
prohibited message (icmp type 3 code 9, 10 and 13) for a TCP
sessions, as if we recived a TCP RST, but only if the TCP session
is in SYN_SENT state.
The reason for only reacting when in SYN_SENT state, is that this
will solve the problem, and at the same time minimize the risk of
this being abused.
I suggest that we enable this new behaviour by default, but it
would be a change of current behaviour, so if people prefer to
leave it disabled by default, at least for now, this would be ok
for me, the attached diff actually have the sysctl set to 0 by
default.
PR: 23086
Submitted by: Jesper Skriver <jesper@skriver.dk>
2000-12-16 19:42:06 +00:00
|
|
|
|
2001-08-22 00:58:16 +00:00
|
|
|
static int tcp_isn_reseed_interval = 0;
|
|
|
|
SYSCTL_INT(_net_inet_tcp, OID_AUTO, isn_reseed_interval, CTLFLAG_RW,
|
|
|
|
&tcp_isn_reseed_interval, 0, "Seconds between reseeding of ISN secret");
|
2001-07-08 02:20:47 +00:00
|
|
|
|
2002-08-17 18:26:02 +00:00
|
|
|
/*
|
2004-08-16 18:32:07 +00:00
|
|
|
* TCP bandwidth limiting sysctls. Note that the default lower bound of
|
|
|
|
* 1024 exists only for debugging. A good production default would be
|
2002-08-17 18:26:02 +00:00
|
|
|
* something like 6100.
|
|
|
|
*/
|
2004-08-03 13:54:11 +00:00
|
|
|
SYSCTL_NODE(_net_inet_tcp, OID_AUTO, inflight, CTLFLAG_RW, 0,
|
|
|
|
"TCP inflight data limiting");
|
|
|
|
|
2004-01-06 23:29:46 +00:00
|
|
|
static int tcp_inflight_enable = 1;
|
2004-08-03 13:54:11 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp_inflight, OID_AUTO, enable, CTLFLAG_RW,
|
2002-08-17 18:26:02 +00:00
|
|
|
&tcp_inflight_enable, 0, "Enable automatic TCP inflight data limiting");
|
|
|
|
|
2002-10-10 21:41:30 +00:00
|
|
|
static int tcp_inflight_debug = 0;
|
2004-08-03 13:54:11 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp_inflight, OID_AUTO, debug, CTLFLAG_RW,
|
2002-08-17 18:26:02 +00:00
|
|
|
&tcp_inflight_debug, 0, "Debug TCP inflight calculations");
|
|
|
|
|
2002-12-14 21:00:17 +00:00
|
|
|
static int tcp_inflight_min = 6144;
|
2004-08-03 13:54:11 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp_inflight, OID_AUTO, min, CTLFLAG_RW,
|
2002-08-17 18:26:02 +00:00
|
|
|
&tcp_inflight_min, 0, "Lower-bound for TCP inflight window");
|
|
|
|
|
|
|
|
static int tcp_inflight_max = TCP_MAXWIN << TCP_MAX_WINSHIFT;
|
2004-08-03 13:54:11 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp_inflight, OID_AUTO, max, CTLFLAG_RW,
|
2002-08-17 18:26:02 +00:00
|
|
|
&tcp_inflight_max, 0, "Upper-bound for TCP inflight window");
|
2004-08-03 13:54:11 +00:00
|
|
|
|
2002-12-14 21:00:17 +00:00
|
|
|
static int tcp_inflight_stab = 20;
|
2004-08-03 13:54:11 +00:00
|
|
|
SYSCTL_INT(_net_inet_tcp_inflight, OID_AUTO, stab, CTLFLAG_RW,
|
2002-12-14 21:00:17 +00:00
|
|
|
&tcp_inflight_stab, 0, "Inflight Algorithm Stabilization 20 = 2 packets");
|
2002-08-17 18:26:02 +00:00
|
|
|
|
2004-06-23 21:04:37 +00:00
|
|
|
uma_zone_t sack_hole_zone;
|
|
|
|
|
2002-06-14 08:35:21 +00:00
|
|
|
static struct inpcb *tcp_notify(struct inpcb *, int);
|
2003-02-19 22:32:43 +00:00
|
|
|
static void tcp_discardcb(struct tcpcb *);
|
2004-04-20 06:33:39 +00:00
|
|
|
static void tcp_isn_tick(void *);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1995-04-09 01:29:31 +00:00
|
|
|
/*
|
Improved connection establishment performance by doing local port lookups via
a hashed port list. In the new scheme, in_pcblookup() goes away and is
replaced by a new routine, in_pcblookup_local() for doing the local port
check. Note that this implementation is space inefficient in that the PCB
struct is now too large to fit into 128 bytes. I might deal with this in the
future by using the new zone allocator, but I wanted these changes to be
extensively tested in their current form first.
Also:
1) Fixed off-by-one errors in the port lookup loops in in_pcbbind().
2) Got rid of some unneeded rehashing. Adding a new routine, in_pcbinshash()
to do the initialial hash insertion.
3) Renamed in_pcblookuphash() to in_pcblookup_hash() for easier readability.
4) Added a new routine, in_pcbremlists() to remove the PCB from the various
hash lists.
5) Added/deleted comments where appropriate.
6) Removed unnecessary splnet() locking. In general, the PCB functions should
be called at splnet()...there are unfortunately a few exceptions, however.
7) Reorganized a few structs for better cache line behavior.
8) Killed my TCP_ACK_HACK kludge. It may come back in a different form in
the future, however.
These changes have been tested on wcarchive for more than a month. In tests
done here, connection establishment overhead is reduced by more than 50
times, thus getting rid of one of the major networking scalability problems.
Still to do: make tcp_fastimo/tcp_slowtimo scale well for systems with a
large number of connections. tcp_fastimo is easy; tcp_slowtimo is difficult.
WARNING: Anything that knows about inpcb and tcpcb structs will have to be
recompiled; at the very least, this includes netstat(1).
1998-01-27 09:15:13 +00:00
|
|
|
* Target size of TCP PCB hash tables. Must be a power of two.
|
1999-02-03 08:59:30 +00:00
|
|
|
*
|
|
|
|
* Note that this can be overridden by the kernel environment
|
|
|
|
* variable net.inet.tcp.tcbhashsize
|
1995-04-09 01:29:31 +00:00
|
|
|
*/
|
|
|
|
#ifndef TCBHASHSIZE
|
Improved connection establishment performance by doing local port lookups via
a hashed port list. In the new scheme, in_pcblookup() goes away and is
replaced by a new routine, in_pcblookup_local() for doing the local port
check. Note that this implementation is space inefficient in that the PCB
struct is now too large to fit into 128 bytes. I might deal with this in the
future by using the new zone allocator, but I wanted these changes to be
extensively tested in their current form first.
Also:
1) Fixed off-by-one errors in the port lookup loops in in_pcbbind().
2) Got rid of some unneeded rehashing. Adding a new routine, in_pcbinshash()
to do the initialial hash insertion.
3) Renamed in_pcblookuphash() to in_pcblookup_hash() for easier readability.
4) Added a new routine, in_pcbremlists() to remove the PCB from the various
hash lists.
5) Added/deleted comments where appropriate.
6) Removed unnecessary splnet() locking. In general, the PCB functions should
be called at splnet()...there are unfortunately a few exceptions, however.
7) Reorganized a few structs for better cache line behavior.
8) Killed my TCP_ACK_HACK kludge. It may come back in a different form in
the future, however.
These changes have been tested on wcarchive for more than a month. In tests
done here, connection establishment overhead is reduced by more than 50
times, thus getting rid of one of the major networking scalability problems.
Still to do: make tcp_fastimo/tcp_slowtimo scale well for systems with a
large number of connections. tcp_fastimo is easy; tcp_slowtimo is difficult.
WARNING: Anything that knows about inpcb and tcpcb structs will have to be
recompiled; at the very least, this includes netstat(1).
1998-01-27 09:15:13 +00:00
|
|
|
#define TCBHASHSIZE 512
|
1995-04-09 01:29:31 +00:00
|
|
|
#endif
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1998-03-24 18:06:34 +00:00
|
|
|
/*
|
2003-02-19 22:32:43 +00:00
|
|
|
* XXX
|
|
|
|
* Callouts should be moved into struct tcp directly. They are currently
|
2003-12-17 16:12:01 +00:00
|
|
|
* separate because the tcpcb structure is exported to userland for sysctl
|
2003-02-19 22:32:43 +00:00
|
|
|
* parsing purposes, which do not know about callouts.
|
1998-03-24 18:06:34 +00:00
|
|
|
*/
|
2003-02-19 22:32:43 +00:00
|
|
|
struct tcpcb_mem {
|
1998-03-24 18:06:34 +00:00
|
|
|
struct tcpcb tcb;
|
2003-02-19 22:32:43 +00:00
|
|
|
struct callout tcpcb_mem_rexmt, tcpcb_mem_persist, tcpcb_mem_keep;
|
|
|
|
struct callout tcpcb_mem_2msl, tcpcb_mem_delack;
|
|
|
|
};
|
|
|
|
|
|
|
|
static uma_zone_t tcpcb_zone;
|
|
|
|
static uma_zone_t tcptw_zone;
|
2004-04-20 06:33:39 +00:00
|
|
|
struct callout isn_callout;
|
1998-03-24 18:06:34 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Tcp initialization
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
tcp_init()
|
|
|
|
{
|
2001-06-06 22:17:08 +00:00
|
|
|
int hashsize = TCBHASHSIZE;
|
2004-08-16 18:32:07 +00:00
|
|
|
|
1999-08-30 21:17:07 +00:00
|
|
|
tcp_delacktime = TCPTV_DELACK;
|
|
|
|
tcp_keepinit = TCPTV_KEEP_INIT;
|
|
|
|
tcp_keepidle = TCPTV_KEEP_IDLE;
|
|
|
|
tcp_keepintvl = TCPTV_KEEPINTVL;
|
|
|
|
tcp_maxpersistidle = TCPTV_KEEP_IDLE;
|
|
|
|
tcp_msl = TCPTV_MSL;
|
2002-07-18 19:06:12 +00:00
|
|
|
tcp_rexmit_min = TCPTV_MIN;
|
|
|
|
tcp_rexmit_slop = TCPTV_CPU_VAR;
|
1999-08-30 21:17:07 +00:00
|
|
|
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_INFO_LOCK_INIT(&tcbinfo, "tcp");
|
1995-04-09 01:29:31 +00:00
|
|
|
LIST_INIT(&tcb);
|
|
|
|
tcbinfo.listhead = &tcb;
|
2001-06-08 05:24:21 +00:00
|
|
|
TUNABLE_INT_FETCH("net.inet.tcp.tcbhashsize", &hashsize);
|
1999-02-04 03:27:43 +00:00
|
|
|
if (!powerof2(hashsize)) {
|
1999-02-03 08:59:30 +00:00
|
|
|
printf("WARNING: TCB hash size not a power of 2\n");
|
|
|
|
hashsize = 512; /* safe default */
|
|
|
|
}
|
1999-08-26 19:52:17 +00:00
|
|
|
tcp_tcbhashsize = hashsize;
|
1999-02-03 08:59:30 +00:00
|
|
|
tcbinfo.hashbase = hashinit(hashsize, M_PCB, &tcbinfo.hashmask);
|
|
|
|
tcbinfo.porthashbase = hashinit(hashsize, M_PCB,
|
1998-03-28 10:18:26 +00:00
|
|
|
&tcbinfo.porthashmask);
|
2004-08-16 18:32:07 +00:00
|
|
|
tcbinfo.ipi_zone = uma_zcreate("inpcb", sizeof(struct inpcb),
|
2004-08-11 20:30:08 +00:00
|
|
|
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
|
2002-03-20 05:48:55 +00:00
|
|
|
uma_zone_set_max(tcbinfo.ipi_zone, maxsockets);
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
#define TCP_MINPROTOHDR (sizeof(struct ip6_hdr) + sizeof(struct tcphdr))
|
|
|
|
#else /* INET6 */
|
|
|
|
#define TCP_MINPROTOHDR (sizeof(struct tcpiphdr))
|
|
|
|
#endif /* INET6 */
|
|
|
|
if (max_protohdr < TCP_MINPROTOHDR)
|
|
|
|
max_protohdr = TCP_MINPROTOHDR;
|
|
|
|
if (max_linkhdr + TCP_MINPROTOHDR > MHLEN)
|
1994-05-24 10:09:53 +00:00
|
|
|
panic("tcp_init");
|
2000-01-09 19:17:30 +00:00
|
|
|
#undef TCP_MINPROTOHDR
|
2003-02-19 22:32:43 +00:00
|
|
|
/*
|
|
|
|
* These have to be type stable for the benefit of the timers.
|
|
|
|
*/
|
2004-08-16 18:32:07 +00:00
|
|
|
tcpcb_zone = uma_zcreate("tcpcb", sizeof(struct tcpcb_mem),
|
2004-08-11 20:30:08 +00:00
|
|
|
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
|
2003-02-19 22:32:43 +00:00
|
|
|
uma_zone_set_max(tcpcb_zone, maxsockets);
|
2004-08-16 18:32:07 +00:00
|
|
|
tcptw_zone = uma_zcreate("tcptw", sizeof(struct tcptw),
|
2004-08-11 20:30:08 +00:00
|
|
|
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
|
2003-10-24 05:44:14 +00:00
|
|
|
uma_zone_set_max(tcptw_zone, maxsockets / 5);
|
2003-03-08 22:06:20 +00:00
|
|
|
tcp_timer_init();
|
2001-11-22 04:50:44 +00:00
|
|
|
syncache_init();
|
2003-11-20 20:07:39 +00:00
|
|
|
tcp_hc_init();
|
2004-02-24 15:27:41 +00:00
|
|
|
tcp_reass_init();
|
2004-04-20 06:33:39 +00:00
|
|
|
callout_init(&isn_callout, CALLOUT_MPSAFE);
|
|
|
|
tcp_isn_tick(NULL);
|
|
|
|
EVENTHANDLER_REGISTER(shutdown_pre_sync, tcp_fini, NULL,
|
|
|
|
SHUTDOWN_PRI_DEFAULT);
|
2004-08-16 18:32:07 +00:00
|
|
|
sack_hole_zone = uma_zcreate("sackhole", sizeof(struct sackhole),
|
2004-08-11 20:30:08 +00:00
|
|
|
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
|
2004-04-20 06:33:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
tcp_fini(xtp)
|
|
|
|
void *xtp;
|
|
|
|
{
|
|
|
|
callout_stop(&isn_callout);
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2001-06-23 03:21:46 +00:00
|
|
|
* Fill in the IP and TCP headers for an outgoing packet, given the tcpcb.
|
|
|
|
* tcp_template used to store this data in mbufs, but we now recopy it out
|
|
|
|
* of the tcpcb each time to conserve mbufs.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2001-06-23 03:21:46 +00:00
|
|
|
void
|
2003-02-19 22:18:06 +00:00
|
|
|
tcpip_fillheaders(inp, ip_ptr, tcp_ptr)
|
|
|
|
struct inpcb *inp;
|
2001-06-23 03:21:46 +00:00
|
|
|
void *ip_ptr;
|
|
|
|
void *tcp_ptr;
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2003-02-19 22:18:06 +00:00
|
|
|
struct tcphdr *th = (struct tcphdr *)tcp_ptr;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2004-12-05 22:27:53 +00:00
|
|
|
INP_LOCK_ASSERT(inp);
|
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
if ((inp->inp_vflag & INP_IPV6) != 0) {
|
2001-06-23 03:21:46 +00:00
|
|
|
struct ip6_hdr *ip6;
|
2000-01-09 19:17:30 +00:00
|
|
|
|
2001-06-23 03:21:46 +00:00
|
|
|
ip6 = (struct ip6_hdr *)ip_ptr;
|
2000-01-09 19:17:30 +00:00
|
|
|
ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) |
|
|
|
|
(inp->in6p_flowinfo & IPV6_FLOWINFO_MASK);
|
|
|
|
ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |
|
|
|
|
(IPV6_VERSION & IPV6_VERSION_MASK);
|
|
|
|
ip6->ip6_nxt = IPPROTO_TCP;
|
|
|
|
ip6->ip6_plen = sizeof(struct tcphdr);
|
|
|
|
ip6->ip6_src = inp->in6p_laddr;
|
|
|
|
ip6->ip6_dst = inp->in6p_faddr;
|
|
|
|
} else
|
|
|
|
#endif
|
2001-06-23 03:21:46 +00:00
|
|
|
{
|
2003-02-19 22:18:06 +00:00
|
|
|
struct ip *ip;
|
|
|
|
|
|
|
|
ip = (struct ip *)ip_ptr;
|
|
|
|
ip->ip_v = IPVERSION;
|
|
|
|
ip->ip_hl = 5;
|
|
|
|
ip->ip_tos = inp->inp_ip_tos;
|
|
|
|
ip->ip_len = 0;
|
|
|
|
ip->ip_id = 0;
|
|
|
|
ip->ip_off = 0;
|
|
|
|
ip->ip_ttl = inp->inp_ip_ttl;
|
|
|
|
ip->ip_sum = 0;
|
|
|
|
ip->ip_p = IPPROTO_TCP;
|
|
|
|
ip->ip_src = inp->inp_laddr;
|
|
|
|
ip->ip_dst = inp->inp_faddr;
|
2001-06-23 03:21:46 +00:00
|
|
|
}
|
2003-02-19 22:18:06 +00:00
|
|
|
th->th_sport = inp->inp_lport;
|
|
|
|
th->th_dport = inp->inp_fport;
|
|
|
|
th->th_seq = 0;
|
|
|
|
th->th_ack = 0;
|
|
|
|
th->th_x2 = 0;
|
|
|
|
th->th_off = 5;
|
|
|
|
th->th_flags = 0;
|
|
|
|
th->th_win = 0;
|
|
|
|
th->th_urp = 0;
|
|
|
|
th->th_sum = 0; /* in_pseudo() is called later for ipv4 */
|
2001-06-23 03:21:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create template to be used to send tcp packets on a connection.
|
|
|
|
* Allocates an mbuf and fills in a skeletal tcp/ip header. The only
|
|
|
|
* use for this function is in keepalives, which use tcp_respond.
|
|
|
|
*/
|
|
|
|
struct tcptemp *
|
2003-02-19 22:18:06 +00:00
|
|
|
tcpip_maketemplate(inp)
|
|
|
|
struct inpcb *inp;
|
2001-06-23 03:21:46 +00:00
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
struct tcptemp *n;
|
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
m = m_get(M_DONTWAIT, MT_HEADER);
|
2001-06-23 03:21:46 +00:00
|
|
|
if (m == NULL)
|
|
|
|
return (0);
|
|
|
|
m->m_len = sizeof(struct tcptemp);
|
|
|
|
n = mtod(m, struct tcptemp *);
|
|
|
|
|
2003-02-19 22:18:06 +00:00
|
|
|
tcpip_fillheaders(inp, (void *)&n->tt_ipgen, (void *)&n->tt_t);
|
1994-05-24 10:09:53 +00:00
|
|
|
return (n);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send a single message to the TCP at address specified by
|
2004-04-05 00:49:07 +00:00
|
|
|
* the given TCP/IP header. If m == NULL, then we make a copy
|
1994-05-24 10:09:53 +00:00
|
|
|
* of the tcpiphdr at ti and send directly to the addressed host.
|
|
|
|
* This is used to force keep alive messages out using the TCP
|
2001-06-23 03:21:46 +00:00
|
|
|
* template for a connection. If flags are given then we send
|
|
|
|
* a message back to the TCP which originated the * segment ti,
|
|
|
|
* and discard the mbuf containing it and any other attached mbufs.
|
1994-05-24 10:09:53 +00:00
|
|
|
*
|
|
|
|
* In any case the ack and sequence number of the transmitted
|
|
|
|
* segment are as specified by the parameters.
|
1997-12-19 03:36:15 +00:00
|
|
|
*
|
|
|
|
* NOTE: If m != NULL, then ti must point to *inside* the mbuf.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
void
|
2000-01-09 19:17:30 +00:00
|
|
|
tcp_respond(tp, ipgen, th, m, ack, seq, flags)
|
1994-05-24 10:09:53 +00:00
|
|
|
struct tcpcb *tp;
|
2000-01-09 19:17:30 +00:00
|
|
|
void *ipgen;
|
|
|
|
register struct tcphdr *th;
|
1994-05-24 10:09:53 +00:00
|
|
|
register struct mbuf *m;
|
|
|
|
tcp_seq ack, seq;
|
|
|
|
int flags;
|
|
|
|
{
|
|
|
|
register int tlen;
|
|
|
|
int win = 0;
|
2000-01-09 19:17:30 +00:00
|
|
|
struct ip *ip;
|
|
|
|
struct tcphdr *nth;
|
|
|
|
#ifdef INET6
|
|
|
|
struct ip6_hdr *ip6;
|
|
|
|
int isipv6;
|
|
|
|
#endif /* INET6 */
|
|
|
|
int ipflags = 0;
|
2004-05-04 02:11:47 +00:00
|
|
|
struct inpcb *inp;
|
2000-01-09 19:17:30 +00:00
|
|
|
|
2002-08-01 03:54:43 +00:00
|
|
|
KASSERT(tp != NULL || m != NULL, ("tcp_respond: tp and m both NULL"));
|
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
2002-10-20 22:52:07 +00:00
|
|
|
isipv6 = ((struct ip *)ipgen)->ip_v == 6;
|
2000-01-09 19:17:30 +00:00
|
|
|
ip6 = ipgen;
|
|
|
|
#endif /* INET6 */
|
|
|
|
ip = ipgen;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2004-04-05 00:52:05 +00:00
|
|
|
if (tp != NULL) {
|
2003-11-08 22:59:22 +00:00
|
|
|
inp = tp->t_inpcb;
|
|
|
|
KASSERT(inp != NULL, ("tcp control block w/o inpcb"));
|
|
|
|
INP_INFO_WLOCK_ASSERT(&tcbinfo);
|
|
|
|
INP_LOCK_ASSERT(inp);
|
2004-05-04 02:11:47 +00:00
|
|
|
} else
|
|
|
|
inp = NULL;
|
|
|
|
|
|
|
|
if (tp != NULL) {
|
2000-02-28 21:18:21 +00:00
|
|
|
if (!(flags & TH_RST)) {
|
2003-11-08 22:59:22 +00:00
|
|
|
win = sbspace(&inp->inp_socket->so_rcv);
|
2000-02-28 21:18:21 +00:00
|
|
|
if (win > (long)TCP_MAXWIN << tp->rcv_scale)
|
|
|
|
win = (long)TCP_MAXWIN << tp->rcv_scale;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2004-04-05 00:49:07 +00:00
|
|
|
if (m == NULL) {
|
2003-02-19 05:47:46 +00:00
|
|
|
m = m_gethdr(M_DONTWAIT, MT_HEADER);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (m == NULL)
|
|
|
|
return;
|
|
|
|
tlen = 0;
|
|
|
|
m->m_data += max_linkhdr;
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
if (isipv6) {
|
2004-08-16 18:32:07 +00:00
|
|
|
bcopy((caddr_t)ip6, mtod(m, caddr_t),
|
2000-01-09 19:17:30 +00:00
|
|
|
sizeof(struct ip6_hdr));
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
nth = (struct tcphdr *)(ip6 + 1);
|
|
|
|
} else
|
|
|
|
#endif /* INET6 */
|
|
|
|
{
|
|
|
|
bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
nth = (struct tcphdr *)(ip + 1);
|
|
|
|
}
|
|
|
|
bcopy((caddr_t)th, (caddr_t)nth, sizeof(struct tcphdr));
|
1994-05-24 10:09:53 +00:00
|
|
|
flags = TH_ACK;
|
|
|
|
} else {
|
|
|
|
m_freem(m->m_next);
|
2004-04-05 00:49:07 +00:00
|
|
|
m->m_next = NULL;
|
2000-01-09 19:17:30 +00:00
|
|
|
m->m_data = (caddr_t)ipgen;
|
|
|
|
/* m_len is set later */
|
1994-05-24 10:09:53 +00:00
|
|
|
tlen = 0;
|
|
|
|
#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
if (isipv6) {
|
|
|
|
xchg(ip6->ip6_dst, ip6->ip6_src, struct in6_addr);
|
|
|
|
nth = (struct tcphdr *)(ip6 + 1);
|
|
|
|
} else
|
|
|
|
#endif /* INET6 */
|
|
|
|
{
|
|
|
|
xchg(ip->ip_dst.s_addr, ip->ip_src.s_addr, n_long);
|
|
|
|
nth = (struct tcphdr *)(ip + 1);
|
|
|
|
}
|
|
|
|
if (th != nth) {
|
|
|
|
/*
|
|
|
|
* this is usually a case when an extension header
|
|
|
|
* exists between the IPv6 header and the
|
|
|
|
* TCP header.
|
|
|
|
*/
|
|
|
|
nth->th_sport = th->th_sport;
|
|
|
|
nth->th_dport = th->th_dport;
|
|
|
|
}
|
|
|
|
xchg(nth->th_dport, nth->th_sport, n_short);
|
1994-05-24 10:09:53 +00:00
|
|
|
#undef xchg
|
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
if (isipv6) {
|
2002-02-04 17:37:06 +00:00
|
|
|
ip6->ip6_flow = 0;
|
|
|
|
ip6->ip6_vfc = IPV6_VERSION;
|
|
|
|
ip6->ip6_nxt = IPPROTO_TCP;
|
2000-01-09 19:17:30 +00:00
|
|
|
ip6->ip6_plen = htons((u_short)(sizeof (struct tcphdr) +
|
|
|
|
tlen));
|
|
|
|
tlen += sizeof (struct ip6_hdr) + sizeof (struct tcphdr);
|
2000-01-15 14:34:56 +00:00
|
|
|
} else
|
2000-01-09 19:17:30 +00:00
|
|
|
#endif
|
2004-08-16 18:32:07 +00:00
|
|
|
{
|
|
|
|
tlen += sizeof (struct tcpiphdr);
|
|
|
|
ip->ip_len = tlen;
|
|
|
|
ip->ip_ttl = ip_defttl;
|
|
|
|
if (path_mtu_discovery)
|
|
|
|
ip->ip_off |= IP_DF;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
m->m_len = tlen;
|
|
|
|
m->m_pkthdr.len = tlen;
|
2004-04-05 00:49:07 +00:00
|
|
|
m->m_pkthdr.rcvif = NULL;
|
2002-07-31 19:06:49 +00:00
|
|
|
#ifdef MAC
|
2003-11-08 22:59:22 +00:00
|
|
|
if (inp != NULL) {
|
2002-07-31 19:06:49 +00:00
|
|
|
/*
|
|
|
|
* Packet is associated with a socket, so allow the
|
|
|
|
* label of the response to reflect the socket label.
|
|
|
|
*/
|
2004-05-04 02:11:47 +00:00
|
|
|
INP_LOCK_ASSERT(inp);
|
|
|
|
mac_create_mbuf_from_inpcb(inp, m);
|
2002-07-31 19:06:49 +00:00
|
|
|
} else {
|
|
|
|
/*
|
2003-08-21 18:39:16 +00:00
|
|
|
* Packet is not associated with a socket, so possibly
|
|
|
|
* update the label in place.
|
2002-07-31 19:06:49 +00:00
|
|
|
*/
|
2003-08-21 18:39:16 +00:00
|
|
|
mac_reflect_mbuf_tcp(m);
|
2002-07-31 19:06:49 +00:00
|
|
|
}
|
|
|
|
#endif
|
2000-01-09 19:17:30 +00:00
|
|
|
nth->th_seq = htonl(seq);
|
|
|
|
nth->th_ack = htonl(ack);
|
|
|
|
nth->th_x2 = 0;
|
|
|
|
nth->th_off = sizeof (struct tcphdr) >> 2;
|
|
|
|
nth->th_flags = flags;
|
2004-04-05 00:49:07 +00:00
|
|
|
if (tp != NULL)
|
2000-01-09 19:17:30 +00:00
|
|
|
nth->th_win = htons((u_short) (win >> tp->rcv_scale));
|
1994-05-24 10:09:53 +00:00
|
|
|
else
|
2000-01-09 19:17:30 +00:00
|
|
|
nth->th_win = htons((u_short)win);
|
|
|
|
nth->th_urp = 0;
|
|
|
|
#ifdef INET6
|
|
|
|
if (isipv6) {
|
2000-04-19 15:05:00 +00:00
|
|
|
nth->th_sum = 0;
|
2000-01-09 19:17:30 +00:00
|
|
|
nth->th_sum = in6_cksum(m, IPPROTO_TCP,
|
|
|
|
sizeof(struct ip6_hdr),
|
|
|
|
tlen - sizeof(struct ip6_hdr));
|
2004-04-05 00:49:07 +00:00
|
|
|
ip6->ip6_hlim = in6_selecthlim(tp != NULL ? tp->t_inpcb :
|
|
|
|
NULL, NULL);
|
2000-01-09 19:17:30 +00:00
|
|
|
} else
|
|
|
|
#endif /* INET6 */
|
2004-08-16 18:32:07 +00:00
|
|
|
{
|
|
|
|
nth->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
|
|
|
|
htons((u_short)(tlen - sizeof(struct ip) + ip->ip_p)));
|
|
|
|
m->m_pkthdr.csum_flags = CSUM_TCP;
|
|
|
|
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
|
|
|
|
}
|
1995-02-09 23:13:27 +00:00
|
|
|
#ifdef TCPDEBUG
|
2003-11-08 22:59:22 +00:00
|
|
|
if (tp == NULL || (inp->inp_socket->so_options & SO_DEBUG))
|
2000-01-09 19:17:30 +00:00
|
|
|
tcp_trace(TA_OUTPUT, 0, tp, mtod(m, void *), th, 0);
|
|
|
|
#endif
|
|
|
|
#ifdef INET6
|
2003-11-20 20:07:39 +00:00
|
|
|
if (isipv6)
|
|
|
|
(void) ip6_output(m, NULL, NULL, ipflags, NULL, NULL, inp);
|
|
|
|
else
|
2000-01-09 19:17:30 +00:00
|
|
|
#endif /* INET6 */
|
2003-11-20 20:07:39 +00:00
|
|
|
(void) ip_output(m, NULL, NULL, ipflags, NULL, inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new TCP control block, making an
|
|
|
|
* empty reassembly queue and hooking it to the argument
|
1998-03-24 18:06:34 +00:00
|
|
|
* protocol control block. The `inp' parameter must have
|
|
|
|
* come from the zone allocator set up in tcp_init().
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
struct tcpcb *
|
|
|
|
tcp_newtcpcb(inp)
|
|
|
|
struct inpcb *inp;
|
|
|
|
{
|
2003-02-19 22:32:43 +00:00
|
|
|
struct tcpcb_mem *tm;
|
|
|
|
struct tcpcb *tp;
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
int isipv6 = (inp->inp_vflag & INP_IPV6) != 0;
|
|
|
|
#endif /* INET6 */
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2003-02-19 22:32:43 +00:00
|
|
|
tm = uma_zalloc(tcpcb_zone, M_NOWAIT | M_ZERO);
|
|
|
|
if (tm == NULL)
|
|
|
|
return (NULL);
|
|
|
|
tp = &tm->tcb;
|
|
|
|
/* LIST_INIT(&tp->t_segq); */ /* XXX covered by M_ZERO */
|
2000-01-09 19:17:30 +00:00
|
|
|
tp->t_maxseg = tp->t_maxopd =
|
|
|
|
#ifdef INET6
|
|
|
|
isipv6 ? tcp_v6mssdflt :
|
|
|
|
#endif /* INET6 */
|
|
|
|
tcp_mssdflt;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1999-08-30 21:17:07 +00:00
|
|
|
/* Set up our timeouts. */
|
2005-03-01 12:01:17 +00:00
|
|
|
callout_init(tp->tt_rexmt = &tm->tcpcb_mem_rexmt, NET_CALLOUT_MPSAFE);
|
|
|
|
callout_init(tp->tt_persist = &tm->tcpcb_mem_persist, NET_CALLOUT_MPSAFE);
|
|
|
|
callout_init(tp->tt_keep = &tm->tcpcb_mem_keep, NET_CALLOUT_MPSAFE);
|
|
|
|
callout_init(tp->tt_2msl = &tm->tcpcb_mem_2msl, NET_CALLOUT_MPSAFE);
|
|
|
|
callout_init(tp->tt_delack = &tm->tcpcb_mem_delack, NET_CALLOUT_MPSAFE);
|
1999-08-30 21:17:07 +00:00
|
|
|
|
1995-02-09 23:13:27 +00:00
|
|
|
if (tcp_do_rfc1323)
|
|
|
|
tp->t_flags = (TF_REQ_SCALE|TF_REQ_TSTMP);
|
2004-06-23 21:04:37 +00:00
|
|
|
tp->sack_enable = tcp_do_sack;
|
2005-04-21 20:11:01 +00:00
|
|
|
if (tp->sack_enable)
|
|
|
|
TAILQ_INIT(&tp->snd_holes);
|
1998-03-24 18:06:34 +00:00
|
|
|
tp->t_inpcb = inp; /* XXX */
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
|
1996-06-14 17:17:32 +00:00
|
|
|
* rtt estimate. Set rttvar so that srtt + 4 * rttvar gives
|
1994-05-24 10:09:53 +00:00
|
|
|
* reasonable initial retransmit time.
|
|
|
|
*/
|
|
|
|
tp->t_srtt = TCPTV_SRTTBASE;
|
1996-06-14 17:17:32 +00:00
|
|
|
tp->t_rttvar = ((TCPTV_RTOBASE - TCPTV_SRTTBASE) << TCP_RTTVAR_SHIFT) / 4;
|
2002-07-18 19:06:12 +00:00
|
|
|
tp->t_rttmin = tcp_rexmit_min;
|
1996-06-14 17:17:32 +00:00
|
|
|
tp->t_rxtcur = TCPTV_RTOBASE;
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
|
2002-08-17 18:26:02 +00:00
|
|
|
tp->snd_bwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->t_rcvtime = ticks;
|
2002-08-17 18:26:02 +00:00
|
|
|
tp->t_bw_rtttime = ticks;
|
2004-08-16 18:32:07 +00:00
|
|
|
/*
|
2000-01-25 01:05:18 +00:00
|
|
|
* IPv4 TTL initialization is necessary for an IPv6 socket as well,
|
|
|
|
* because the socket may be bound to an IPv6 wildcard address,
|
|
|
|
* which may match an IPv4-mapped IPv6 address.
|
|
|
|
*/
|
1997-04-03 05:14:45 +00:00
|
|
|
inp->inp_ip_ttl = ip_defttl;
|
1994-05-24 10:09:53 +00:00
|
|
|
inp->inp_ppcb = (caddr_t)tp;
|
1998-03-24 18:06:34 +00:00
|
|
|
return (tp); /* XXX */
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Drop a TCP connection, reporting
|
|
|
|
* the specified error. If connection is synchronized,
|
|
|
|
* then send a RST to peer.
|
|
|
|
*/
|
|
|
|
struct tcpcb *
|
|
|
|
tcp_drop(tp, errno)
|
|
|
|
register struct tcpcb *tp;
|
|
|
|
int errno;
|
|
|
|
{
|
|
|
|
struct socket *so = tp->t_inpcb->inp_socket;
|
|
|
|
|
2005-06-01 12:06:07 +00:00
|
|
|
INP_INFO_WLOCK_ASSERT(&tcbinfo);
|
2004-11-23 16:06:15 +00:00
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
2005-06-01 12:06:07 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
if (TCPS_HAVERCVDSYN(tp->t_state)) {
|
|
|
|
tp->t_state = TCPS_CLOSED;
|
|
|
|
(void) tcp_output(tp);
|
|
|
|
tcpstat.tcps_drops++;
|
|
|
|
} else
|
|
|
|
tcpstat.tcps_conndrops++;
|
|
|
|
if (errno == ETIMEDOUT && tp->t_softerror)
|
|
|
|
errno = tp->t_softerror;
|
|
|
|
so->so_error = errno;
|
|
|
|
return (tcp_close(tp));
|
|
|
|
}
|
|
|
|
|
2003-02-19 22:32:43 +00:00
|
|
|
static void
|
|
|
|
tcp_discardcb(tp)
|
|
|
|
struct tcpcb *tp;
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2003-02-19 22:32:43 +00:00
|
|
|
struct tseg_qent *q;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct inpcb *inp = tp->t_inpcb;
|
|
|
|
struct socket *so = inp->inp_socket;
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
int isipv6 = (inp->inp_vflag & INP_IPV6) != 0;
|
|
|
|
#endif /* INET6 */
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2004-12-05 22:27:53 +00:00
|
|
|
INP_LOCK_ASSERT(inp);
|
|
|
|
|
1999-08-30 21:17:07 +00:00
|
|
|
/*
|
|
|
|
* Make sure that all of our timers are stopped before we
|
|
|
|
* delete the PCB.
|
|
|
|
*/
|
|
|
|
callout_stop(tp->tt_rexmt);
|
|
|
|
callout_stop(tp->tt_persist);
|
|
|
|
callout_stop(tp->tt_keep);
|
|
|
|
callout_stop(tp->tt_2msl);
|
|
|
|
callout_stop(tp->tt_delack);
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
1995-06-29 18:11:24 +00:00
|
|
|
* If we got enough samples through the srtt filter,
|
|
|
|
* save the rtt and rttvar in the routing entry.
|
2003-11-20 20:07:39 +00:00
|
|
|
* 'Enough' is arbitrarily defined as 4 rtt samples.
|
|
|
|
* 4 samples is enough for the srtt filter to converge
|
|
|
|
* to within enough % of the correct value; fewer samples
|
|
|
|
* and we could save a bogus rtt. The danger is not high
|
|
|
|
* as tcp quickly recovers from everything.
|
|
|
|
* XXX: Works very well but needs some more statistics!
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2003-11-20 20:07:39 +00:00
|
|
|
if (tp->t_rttupdated >= 4) {
|
|
|
|
struct hc_metrics_lite metrics;
|
|
|
|
u_long ssthresh;
|
2000-01-09 19:17:30 +00:00
|
|
|
|
2003-11-20 20:07:39 +00:00
|
|
|
bzero(&metrics, sizeof(metrics));
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
2003-11-20 20:07:39 +00:00
|
|
|
* Update the ssthresh always when the conditions below
|
|
|
|
* are satisfied. This gives us better new start value
|
|
|
|
* for the congestion avoidance for new connections.
|
|
|
|
* ssthresh is only set if packet loss occured on a session.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2003-11-20 20:07:39 +00:00
|
|
|
ssthresh = tp->snd_ssthresh;
|
|
|
|
if (ssthresh != 0 && ssthresh < so->so_snd.sb_hiwat / 2) {
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* convert the limit from user data bytes to
|
|
|
|
* packets then to packet data bytes.
|
|
|
|
*/
|
2003-11-20 20:07:39 +00:00
|
|
|
ssthresh = (ssthresh + tp->t_maxseg / 2) / tp->t_maxseg;
|
|
|
|
if (ssthresh < 2)
|
|
|
|
ssthresh = 2;
|
|
|
|
ssthresh *= (u_long)(tp->t_maxseg +
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
(isipv6 ? sizeof (struct ip6_hdr) +
|
|
|
|
sizeof (struct tcphdr) :
|
|
|
|
#endif
|
|
|
|
sizeof (struct tcpiphdr)
|
|
|
|
#ifdef INET6
|
|
|
|
)
|
|
|
|
#endif
|
|
|
|
);
|
2003-11-20 20:07:39 +00:00
|
|
|
} else
|
|
|
|
ssthresh = 0;
|
|
|
|
metrics.rmx_ssthresh = ssthresh;
|
|
|
|
|
|
|
|
metrics.rmx_rtt = tp->t_srtt;
|
|
|
|
metrics.rmx_rttvar = tp->t_rttvar;
|
|
|
|
/* XXX: This wraps if the pipe is more than 4 Gbit per second */
|
|
|
|
metrics.rmx_bandwidth = tp->snd_bandwidth;
|
|
|
|
metrics.rmx_cwnd = tp->snd_cwnd;
|
2004-08-16 18:32:07 +00:00
|
|
|
metrics.rmx_sendpipe = 0;
|
2003-11-20 20:07:39 +00:00
|
|
|
metrics.rmx_recvpipe = 0;
|
|
|
|
|
|
|
|
tcp_hc_update(&inp->inp_inc, &metrics);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2003-11-20 20:07:39 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/* free the reassembly queue, if any */
|
2003-02-19 22:32:43 +00:00
|
|
|
while ((q = LIST_FIRST(&tp->t_segq)) != NULL) {
|
2000-01-09 19:17:30 +00:00
|
|
|
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--;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2004-06-23 21:04:37 +00:00
|
|
|
tcp_free_sackholes(tp);
|
Improved connection establishment performance by doing local port lookups via
a hashed port list. In the new scheme, in_pcblookup() goes away and is
replaced by a new routine, in_pcblookup_local() for doing the local port
check. Note that this implementation is space inefficient in that the PCB
struct is now too large to fit into 128 bytes. I might deal with this in the
future by using the new zone allocator, but I wanted these changes to be
extensively tested in their current form first.
Also:
1) Fixed off-by-one errors in the port lookup loops in in_pcbbind().
2) Got rid of some unneeded rehashing. Adding a new routine, in_pcbinshash()
to do the initialial hash insertion.
3) Renamed in_pcblookuphash() to in_pcblookup_hash() for easier readability.
4) Added a new routine, in_pcbremlists() to remove the PCB from the various
hash lists.
5) Added/deleted comments where appropriate.
6) Removed unnecessary splnet() locking. In general, the PCB functions should
be called at splnet()...there are unfortunately a few exceptions, however.
7) Reorganized a few structs for better cache line behavior.
8) Killed my TCP_ACK_HACK kludge. It may come back in a different form in
the future, however.
These changes have been tested on wcarchive for more than a month. In tests
done here, connection establishment overhead is reduced by more than 50
times, thus getting rid of one of the major networking scalability problems.
Still to do: make tcp_fastimo/tcp_slowtimo scale well for systems with a
large number of connections. tcp_fastimo is easy; tcp_slowtimo is difficult.
WARNING: Anything that knows about inpcb and tcpcb structs will have to be
recompiled; at the very least, this includes netstat(1).
1998-01-27 09:15:13 +00:00
|
|
|
inp->inp_ppcb = NULL;
|
2002-12-24 21:00:31 +00:00
|
|
|
tp->t_inpcb = NULL;
|
2003-02-19 22:32:43 +00:00
|
|
|
uma_zfree(tcpcb_zone, tp);
|
1994-05-24 10:09:53 +00:00
|
|
|
soisdisconnected(so);
|
2003-02-19 22:32:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Close a TCP control block:
|
|
|
|
* discard all space held by the tcp
|
|
|
|
* discard internet protocol block
|
|
|
|
* wake up any sleepers
|
|
|
|
*/
|
|
|
|
struct tcpcb *
|
|
|
|
tcp_close(tp)
|
|
|
|
struct tcpcb *tp;
|
|
|
|
{
|
|
|
|
struct inpcb *inp = tp->t_inpcb;
|
2003-02-19 23:43:04 +00:00
|
|
|
#ifdef INET6
|
2003-02-19 22:32:43 +00:00
|
|
|
struct socket *so = inp->inp_socket;
|
2003-02-19 23:43:04 +00:00
|
|
|
#endif
|
2003-02-19 22:32:43 +00:00
|
|
|
|
2005-06-01 12:06:07 +00:00
|
|
|
INP_INFO_WLOCK_ASSERT(&tcbinfo);
|
2004-12-05 22:27:53 +00:00
|
|
|
INP_LOCK_ASSERT(inp);
|
|
|
|
|
2003-02-19 22:32:43 +00:00
|
|
|
tcp_discardcb(tp);
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
if (INP_CHECK_SOCKAF(so, AF_INET6))
|
|
|
|
in6_pcbdetach(inp);
|
|
|
|
else
|
2003-02-19 22:32:43 +00:00
|
|
|
#endif
|
|
|
|
in_pcbdetach(inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
tcpstat.tcps_closed++;
|
2004-04-05 00:49:07 +00:00
|
|
|
return (NULL);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
tcp_drain()
|
|
|
|
{
|
1999-12-28 23:18:33 +00:00
|
|
|
if (do_tcpdrain)
|
|
|
|
{
|
|
|
|
struct inpcb *inpb;
|
|
|
|
struct tcpcb *tcpb;
|
2000-01-09 19:17:30 +00:00
|
|
|
struct tseg_qent *te;
|
1999-12-28 23:18:33 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk the tcpbs, if existing, and flush the reassembly queue,
|
|
|
|
* if there is one...
|
|
|
|
* XXX: The "Net/3" implementation doesn't imply that the TCP
|
|
|
|
* reassembly queue should be flushed, but in a situation
|
2004-08-16 18:32:07 +00:00
|
|
|
* where we're really low on mbufs, this is potentially
|
|
|
|
* usefull.
|
1999-12-28 23:18:33 +00:00
|
|
|
*/
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_INFO_RLOCK(&tcbinfo);
|
2001-03-16 20:00:53 +00:00
|
|
|
LIST_FOREACH(inpb, tcbinfo.listhead, inp_list) {
|
2003-02-19 22:32:43 +00:00
|
|
|
if (inpb->inp_vflag & INP_TIMEWAIT)
|
|
|
|
continue;
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_LOCK(inpb);
|
2004-04-05 00:49:07 +00:00
|
|
|
if ((tcpb = intotcpcb(inpb)) != NULL) {
|
2001-03-16 20:00:53 +00:00
|
|
|
while ((te = LIST_FIRST(&tcpb->t_segq))
|
|
|
|
!= NULL) {
|
2000-01-09 19:17:30 +00:00
|
|
|
LIST_REMOVE(te, tqe_q);
|
|
|
|
m_freem(te->tqe_m);
|
2004-02-24 15:27:41 +00:00
|
|
|
uma_zfree(tcp_reass_zone, te);
|
|
|
|
tcpb->t_segqlen--;
|
|
|
|
tcp_reass_qsize--;
|
1999-12-28 23:18:33 +00:00
|
|
|
}
|
2005-04-10 05:21:29 +00:00
|
|
|
tcp_clean_sackreport(tcpb);
|
1999-12-28 23:18:33 +00:00
|
|
|
}
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_UNLOCK(inpb);
|
1999-12-28 23:18:33 +00:00
|
|
|
}
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_INFO_RUNLOCK(&tcbinfo);
|
1999-12-28 23:18:33 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Notify a tcp user of an asynchronous error;
|
|
|
|
* store error as soft error, but wake up user
|
|
|
|
* (for now, won't do anything until can select for soft error).
|
2001-02-23 21:07:06 +00:00
|
|
|
*
|
|
|
|
* Do not wake up user since there currently is no mechanism for
|
|
|
|
* reporting soft errors (yet - a kqueue filter may be added).
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2002-06-14 08:35:21 +00:00
|
|
|
static struct inpcb *
|
1994-05-24 10:09:53 +00:00
|
|
|
tcp_notify(inp, error)
|
|
|
|
struct inpcb *inp;
|
|
|
|
int error;
|
|
|
|
{
|
2001-02-23 21:07:06 +00:00
|
|
|
struct tcpcb *tp = (struct tcpcb *)inp->inp_ppcb;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2005-06-01 12:06:07 +00:00
|
|
|
INP_INFO_WLOCK_ASSERT(&tcbinfo);
|
2004-12-05 22:27:53 +00:00
|
|
|
INP_LOCK_ASSERT(inp);
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Ignore some errors if we are hooked up.
|
|
|
|
* If connection hasn't completed, has retransmitted several times,
|
|
|
|
* and receives a second error, give up now. This is better
|
|
|
|
* than waiting a long time to establish a connection that
|
|
|
|
* can never complete.
|
|
|
|
*/
|
|
|
|
if (tp->t_state == TCPS_ESTABLISHED &&
|
2003-02-15 02:37:57 +00:00
|
|
|
(error == EHOSTUNREACH || error == ENETUNREACH ||
|
|
|
|
error == EHOSTDOWN)) {
|
2004-12-23 01:34:26 +00:00
|
|
|
return (inp);
|
1994-05-24 10:09:53 +00:00
|
|
|
} else if (tp->t_state < TCPS_ESTABLISHED && tp->t_rxtshift > 3 &&
|
2002-06-14 08:35:21 +00:00
|
|
|
tp->t_softerror) {
|
2001-02-23 21:07:06 +00:00
|
|
|
tcp_drop(tp, error);
|
2002-06-14 08:35:21 +00:00
|
|
|
return (struct inpcb *)0;
|
|
|
|
} else {
|
1994-05-24 10:09:53 +00:00
|
|
|
tp->t_softerror = error;
|
2004-12-23 01:34:26 +00:00
|
|
|
return (inp);
|
2002-06-14 08:35:21 +00:00
|
|
|
}
|
2001-02-23 21:07:06 +00:00
|
|
|
#if 0
|
2003-03-02 16:54:40 +00:00
|
|
|
wakeup( &so->so_timeo);
|
1994-05-24 10:09:53 +00:00
|
|
|
sorwakeup(so);
|
|
|
|
sowwakeup(so);
|
2001-02-23 21:07:06 +00:00
|
|
|
#endif
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
1998-05-15 20:11:40 +00:00
|
|
|
static int
|
2000-07-04 11:25:35 +00:00
|
|
|
tcp_pcblist(SYSCTL_HANDLER_ARGS)
|
1998-05-15 20:11:40 +00:00
|
|
|
{
|
|
|
|
int error, i, n, s;
|
|
|
|
struct inpcb *inp, **inp_list;
|
|
|
|
inp_gen_t gencnt;
|
|
|
|
struct xinpgen xig;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The process of preparing the TCB list is too time-consuming and
|
|
|
|
* resource-intensive to repeat twice on every request.
|
|
|
|
*/
|
2004-04-05 00:49:07 +00:00
|
|
|
if (req->oldptr == NULL) {
|
1998-05-15 20:11:40 +00:00
|
|
|
n = tcbinfo.ipi_count;
|
|
|
|
req->oldidx = 2 * (sizeof xig)
|
|
|
|
+ (n + n/8) * sizeof(struct xtcpcb);
|
2004-12-23 01:34:26 +00:00
|
|
|
return (0);
|
1998-05-15 20:11:40 +00:00
|
|
|
}
|
|
|
|
|
2004-04-05 00:49:07 +00:00
|
|
|
if (req->newptr != NULL)
|
2004-12-23 01:34:26 +00:00
|
|
|
return (EPERM);
|
1998-05-15 20:11:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, now we're committed to doing something.
|
|
|
|
*/
|
|
|
|
s = splnet();
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_INFO_RLOCK(&tcbinfo);
|
1998-05-15 20:11:40 +00:00
|
|
|
gencnt = tcbinfo.ipi_gencnt;
|
|
|
|
n = tcbinfo.ipi_count;
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_INFO_RUNLOCK(&tcbinfo);
|
1998-05-15 20:11:40 +00:00
|
|
|
splx(s);
|
|
|
|
|
2004-02-26 00:27:04 +00:00
|
|
|
error = sysctl_wire_old_buffer(req, 2 * (sizeof xig)
|
2002-07-28 19:59:31 +00:00
|
|
|
+ n * sizeof(struct xtcpcb));
|
2004-02-26 00:27:04 +00:00
|
|
|
if (error != 0)
|
|
|
|
return (error);
|
2002-07-28 19:59:31 +00:00
|
|
|
|
1998-05-15 20:11:40 +00:00
|
|
|
xig.xig_len = sizeof xig;
|
|
|
|
xig.xig_count = n;
|
|
|
|
xig.xig_gen = gencnt;
|
|
|
|
xig.xig_sogen = so_gencnt;
|
|
|
|
error = SYSCTL_OUT(req, &xig, sizeof xig);
|
|
|
|
if (error)
|
2004-12-23 01:34:26 +00:00
|
|
|
return (error);
|
1998-05-15 20:11:40 +00:00
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
inp_list = malloc(n * sizeof *inp_list, M_TEMP, M_WAITOK);
|
2004-04-05 00:49:07 +00:00
|
|
|
if (inp_list == NULL)
|
2004-12-23 01:34:26 +00:00
|
|
|
return (ENOMEM);
|
2004-08-16 18:32:07 +00:00
|
|
|
|
1998-05-15 20:11:40 +00:00
|
|
|
s = splnet();
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_INFO_RLOCK(&tcbinfo);
|
2004-04-05 00:49:07 +00:00
|
|
|
for (inp = LIST_FIRST(tcbinfo.listhead), i = 0; inp != NULL && i < n;
|
2001-02-04 13:13:25 +00:00
|
|
|
inp = LIST_NEXT(inp, inp_list)) {
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_LOCK(inp);
|
2003-04-10 20:33:10 +00:00
|
|
|
if (inp->inp_gencnt <= gencnt) {
|
|
|
|
/*
|
|
|
|
* XXX: This use of cr_cansee(), introduced with
|
|
|
|
* TCP state changes, is not quite right, but for
|
|
|
|
* now, better than nothing.
|
|
|
|
*/
|
|
|
|
if (inp->inp_vflag & INP_TIMEWAIT)
|
|
|
|
error = cr_cansee(req->td->td_ucred,
|
|
|
|
intotw(inp)->tw_cred);
|
|
|
|
else
|
|
|
|
error = cr_canseesocket(req->td->td_ucred,
|
|
|
|
inp->inp_socket);
|
|
|
|
if (error == 0)
|
|
|
|
inp_list[i++] = inp;
|
|
|
|
}
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_UNLOCK(inp);
|
1998-05-15 20:11:40 +00:00
|
|
|
}
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_INFO_RUNLOCK(&tcbinfo);
|
1998-05-15 20:11:40 +00:00
|
|
|
splx(s);
|
|
|
|
n = i;
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
inp = inp_list[i];
|
|
|
|
if (inp->inp_gencnt <= gencnt) {
|
|
|
|
struct xtcpcb xt;
|
1999-06-16 19:05:17 +00:00
|
|
|
caddr_t inp_ppcb;
|
2005-05-07 00:41:36 +00:00
|
|
|
|
|
|
|
bzero(&xt, sizeof(xt));
|
1998-05-15 20:11:40 +00:00
|
|
|
xt.xt_len = sizeof xt;
|
|
|
|
/* XXX should avoid extra copy */
|
|
|
|
bcopy(inp, &xt.xt_inp, sizeof *inp);
|
1999-06-16 19:05:17 +00:00
|
|
|
inp_ppcb = inp->inp_ppcb;
|
2003-02-19 22:32:43 +00:00
|
|
|
if (inp_ppcb == NULL)
|
|
|
|
bzero((char *) &xt.xt_tp, sizeof xt.xt_tp);
|
|
|
|
else if (inp->inp_vflag & INP_TIMEWAIT) {
|
1999-06-16 19:05:17 +00:00
|
|
|
bzero((char *) &xt.xt_tp, sizeof xt.xt_tp);
|
2003-02-19 22:32:43 +00:00
|
|
|
xt.xt_tp.t_state = TCPS_TIME_WAIT;
|
|
|
|
} else
|
|
|
|
bcopy(inp_ppcb, &xt.xt_tp, sizeof xt.xt_tp);
|
2004-04-05 00:49:07 +00:00
|
|
|
if (inp->inp_socket != NULL)
|
1998-05-15 20:11:40 +00:00
|
|
|
sotoxsocket(inp->inp_socket, &xt.xt_socket);
|
2003-02-19 22:32:43 +00:00
|
|
|
else {
|
|
|
|
bzero(&xt.xt_socket, sizeof xt.xt_socket);
|
|
|
|
xt.xt_socket.xso_protocol = IPPROTO_TCP;
|
|
|
|
}
|
2003-02-15 02:37:57 +00:00
|
|
|
xt.xt_inp.inp_gencnt = inp->inp_gencnt;
|
1998-05-15 20:11:40 +00:00
|
|
|
error = SYSCTL_OUT(req, &xt, sizeof xt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!error) {
|
|
|
|
/*
|
|
|
|
* Give the user an updated idea of our state.
|
|
|
|
* If the generation differs from what we told
|
|
|
|
* her before, she knows that something happened
|
|
|
|
* while we were processing this request, and it
|
|
|
|
* might be necessary to retry.
|
|
|
|
*/
|
|
|
|
s = splnet();
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_INFO_RLOCK(&tcbinfo);
|
1998-05-15 20:11:40 +00:00
|
|
|
xig.xig_gen = tcbinfo.ipi_gencnt;
|
|
|
|
xig.xig_sogen = so_gencnt;
|
|
|
|
xig.xig_count = tcbinfo.ipi_count;
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_INFO_RUNLOCK(&tcbinfo);
|
1998-05-15 20:11:40 +00:00
|
|
|
splx(s);
|
|
|
|
error = SYSCTL_OUT(req, &xig, sizeof xig);
|
|
|
|
}
|
|
|
|
free(inp_list, M_TEMP);
|
2004-12-23 01:34:26 +00:00
|
|
|
return (error);
|
1998-05-15 20:11:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SYSCTL_PROC(_net_inet_tcp, TCPCTL_PCBLIST, pcblist, CTLFLAG_RD, 0, 0,
|
|
|
|
tcp_pcblist, "S,xtcpcb", "List of active TCP connections");
|
|
|
|
|
1999-07-11 18:32:46 +00:00
|
|
|
static int
|
2000-07-04 11:25:35 +00:00
|
|
|
tcp_getcred(SYSCTL_HANDLER_ARGS)
|
1999-07-11 18:32:46 +00:00
|
|
|
{
|
2001-02-18 13:30:20 +00:00
|
|
|
struct xucred xuc;
|
1999-07-11 18:32:46 +00:00
|
|
|
struct sockaddr_in addrs[2];
|
|
|
|
struct inpcb *inp;
|
|
|
|
int error, s;
|
|
|
|
|
2004-07-26 07:24:04 +00:00
|
|
|
error = suser_cred(req->td->td_ucred, SUSER_ALLOWJAIL);
|
1999-07-11 18:32:46 +00:00
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
error = SYSCTL_IN(req, addrs, sizeof(addrs));
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
s = splnet();
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_INFO_RLOCK(&tcbinfo);
|
1999-07-11 18:32:46 +00:00
|
|
|
inp = in_pcblookup_hash(&tcbinfo, addrs[1].sin_addr, addrs[1].sin_port,
|
1999-12-07 17:39:16 +00:00
|
|
|
addrs[0].sin_addr, addrs[0].sin_port, 0, NULL);
|
2002-06-10 20:05:46 +00:00
|
|
|
if (inp == NULL) {
|
1999-07-11 18:32:46 +00:00
|
|
|
error = ENOENT;
|
2002-06-10 20:05:46 +00:00
|
|
|
goto outunlocked;
|
1999-07-11 18:32:46 +00:00
|
|
|
}
|
2002-07-11 23:13:31 +00:00
|
|
|
INP_LOCK(inp);
|
|
|
|
if (inp->inp_socket == NULL) {
|
|
|
|
error = ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
2002-03-22 19:57:41 +00:00
|
|
|
error = cr_canseesocket(req->td->td_ucred, inp->inp_socket);
|
2001-06-24 12:18:27 +00:00
|
|
|
if (error)
|
|
|
|
goto out;
|
2002-02-27 04:45:37 +00:00
|
|
|
cru2x(inp->inp_socket->so_cred, &xuc);
|
1999-07-11 18:32:46 +00:00
|
|
|
out:
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_UNLOCK(inp);
|
|
|
|
outunlocked:
|
|
|
|
INP_INFO_RUNLOCK(&tcbinfo);
|
1999-07-11 18:32:46 +00:00
|
|
|
splx(s);
|
2002-07-11 23:18:43 +00:00
|
|
|
if (error == 0)
|
|
|
|
error = SYSCTL_OUT(req, &xuc, sizeof(struct xucred));
|
1999-07-11 18:32:46 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2001-06-24 12:18:27 +00:00
|
|
|
SYSCTL_PROC(_net_inet_tcp, OID_AUTO, getcred,
|
|
|
|
CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_PRISON, 0, 0,
|
|
|
|
tcp_getcred, "S,xucred", "Get the xucred of a TCP connection");
|
1999-07-11 18:32:46 +00:00
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
static int
|
2000-07-04 11:25:35 +00:00
|
|
|
tcp6_getcred(SYSCTL_HANDLER_ARGS)
|
2000-01-09 19:17:30 +00:00
|
|
|
{
|
2001-02-18 13:30:20 +00:00
|
|
|
struct xucred xuc;
|
2000-01-09 19:17:30 +00:00
|
|
|
struct sockaddr_in6 addrs[2];
|
2005-02-04 14:43:05 +00:00
|
|
|
struct in6_addr a6[2];
|
2000-01-09 19:17:30 +00:00
|
|
|
struct inpcb *inp;
|
|
|
|
int error, s, mapped = 0;
|
|
|
|
|
2004-07-26 07:24:04 +00:00
|
|
|
error = suser_cred(req->td->td_ucred, SUSER_ALLOWJAIL);
|
2000-01-09 19:17:30 +00:00
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
error = SYSCTL_IN(req, addrs, sizeof(addrs));
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
if (IN6_IS_ADDR_V4MAPPED(&addrs[0].sin6_addr)) {
|
|
|
|
if (IN6_IS_ADDR_V4MAPPED(&addrs[1].sin6_addr))
|
|
|
|
mapped = 1;
|
|
|
|
else
|
|
|
|
return (EINVAL);
|
2005-02-04 14:43:05 +00:00
|
|
|
} else {
|
|
|
|
error = in6_embedscope(&a6[0], &addrs[0], NULL, NULL);
|
|
|
|
if (error)
|
|
|
|
return (EINVAL);
|
|
|
|
error = in6_embedscope(&a6[1], &addrs[1], NULL, NULL);
|
|
|
|
if (error)
|
|
|
|
return (EINVAL);
|
2000-01-09 19:17:30 +00:00
|
|
|
}
|
|
|
|
s = splnet();
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_INFO_RLOCK(&tcbinfo);
|
2000-01-09 19:17:30 +00:00
|
|
|
if (mapped == 1)
|
|
|
|
inp = in_pcblookup_hash(&tcbinfo,
|
|
|
|
*(struct in_addr *)&addrs[1].sin6_addr.s6_addr[12],
|
|
|
|
addrs[1].sin6_port,
|
|
|
|
*(struct in_addr *)&addrs[0].sin6_addr.s6_addr[12],
|
|
|
|
addrs[0].sin6_port,
|
|
|
|
0, NULL);
|
|
|
|
else
|
2005-02-04 14:43:05 +00:00
|
|
|
inp = in6_pcblookup_hash(&tcbinfo, &a6[1], addrs[1].sin6_port,
|
|
|
|
&a6[0], addrs[0].sin6_port, 0, NULL);
|
2002-06-10 20:05:46 +00:00
|
|
|
if (inp == NULL) {
|
2000-01-09 19:17:30 +00:00
|
|
|
error = ENOENT;
|
2002-06-10 20:05:46 +00:00
|
|
|
goto outunlocked;
|
2002-07-11 23:13:31 +00:00
|
|
|
}
|
|
|
|
INP_LOCK(inp);
|
|
|
|
if (inp->inp_socket == NULL) {
|
|
|
|
error = ENOENT;
|
|
|
|
goto out;
|
2000-01-09 19:17:30 +00:00
|
|
|
}
|
2002-03-22 19:57:41 +00:00
|
|
|
error = cr_canseesocket(req->td->td_ucred, inp->inp_socket);
|
2001-06-24 12:18:27 +00:00
|
|
|
if (error)
|
|
|
|
goto out;
|
2002-02-27 04:45:37 +00:00
|
|
|
cru2x(inp->inp_socket->so_cred, &xuc);
|
2000-01-09 19:17:30 +00:00
|
|
|
out:
|
2002-06-10 20:05:46 +00:00
|
|
|
INP_UNLOCK(inp);
|
|
|
|
outunlocked:
|
|
|
|
INP_INFO_RUNLOCK(&tcbinfo);
|
2000-01-09 19:17:30 +00:00
|
|
|
splx(s);
|
2002-07-11 23:18:43 +00:00
|
|
|
if (error == 0)
|
|
|
|
error = SYSCTL_OUT(req, &xuc, sizeof(struct xucred));
|
2000-01-09 19:17:30 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2001-06-24 12:18:27 +00:00
|
|
|
SYSCTL_PROC(_net_inet6_tcp6, OID_AUTO, getcred,
|
|
|
|
CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_PRISON, 0, 0,
|
|
|
|
tcp6_getcred, "S,xucred", "Get the xucred of a TCP6 connection");
|
2000-01-09 19:17:30 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
void
|
1995-12-16 02:14:44 +00:00
|
|
|
tcp_ctlinput(cmd, sa, vip)
|
1994-05-24 10:09:53 +00:00
|
|
|
int cmd;
|
|
|
|
struct sockaddr *sa;
|
1995-12-16 02:14:44 +00:00
|
|
|
void *vip;
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2001-02-23 20:51:46 +00:00
|
|
|
struct ip *ip = vip;
|
|
|
|
struct tcphdr *th;
|
2001-02-26 21:19:47 +00:00
|
|
|
struct in_addr faddr;
|
|
|
|
struct inpcb *inp;
|
|
|
|
struct tcpcb *tp;
|
2002-06-14 08:35:21 +00:00
|
|
|
struct inpcb *(*notify)(struct inpcb *, int) = tcp_notify;
|
2005-04-21 14:29:34 +00:00
|
|
|
struct icmp *icp;
|
|
|
|
struct in_conninfo inc;
|
|
|
|
tcp_seq icmp_tcp_seq;
|
|
|
|
int mtu, s;
|
2001-02-26 21:19:47 +00:00
|
|
|
|
|
|
|
faddr = ((struct sockaddr_in *)sa)->sin_addr;
|
|
|
|
if (sa->sa_family != AF_INET || faddr.s_addr == INADDR_ANY)
|
|
|
|
return;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2005-04-21 12:37:12 +00:00
|
|
|
if (cmd == PRC_MSGSIZE)
|
|
|
|
notify = tcp_mtudisc;
|
2001-03-28 14:13:19 +00:00
|
|
|
else if (icmp_may_rst && (cmd == PRC_UNREACH_ADMIN_PROHIB ||
|
2002-06-30 20:07:21 +00:00
|
|
|
cmd == PRC_UNREACH_PORT || cmd == PRC_TIMXCEED_INTRANS) && ip)
|
2001-02-23 20:51:46 +00:00
|
|
|
notify = tcp_drop_syn_sent;
|
2003-11-20 20:07:39 +00:00
|
|
|
/*
|
|
|
|
* Redirects don't need to be handled up here.
|
|
|
|
*/
|
|
|
|
else if (PRC_IS_REDIRECT(cmd))
|
|
|
|
return;
|
2005-04-21 12:37:12 +00:00
|
|
|
/*
|
|
|
|
* Source quench is depreciated.
|
|
|
|
*/
|
|
|
|
else if (cmd == PRC_QUENCH)
|
|
|
|
return;
|
2003-11-20 20:07:39 +00:00
|
|
|
/*
|
|
|
|
* Hostdead is ugly because it goes linearly through all PCBs.
|
|
|
|
* XXX: We never get this from ICMP, otherwise it makes an
|
|
|
|
* excellent DoS attack on machines with many connections.
|
|
|
|
*/
|
|
|
|
else if (cmd == PRC_HOSTDEAD)
|
2004-04-05 00:49:07 +00:00
|
|
|
ip = NULL;
|
2003-09-11 21:40:21 +00:00
|
|
|
else if ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0)
|
1994-05-24 10:09:53 +00:00
|
|
|
return;
|
2004-04-05 00:49:07 +00:00
|
|
|
if (ip != NULL) {
|
2001-02-26 21:19:47 +00:00
|
|
|
s = splnet();
|
2005-04-21 14:29:34 +00:00
|
|
|
icp = (struct icmp *)((caddr_t)ip
|
|
|
|
- offsetof(struct icmp, icmp_ip));
|
2004-08-16 18:32:07 +00:00
|
|
|
th = (struct tcphdr *)((caddr_t)ip
|
2002-10-20 22:52:07 +00:00
|
|
|
+ (ip->ip_hl << 2));
|
2002-06-21 22:52:48 +00:00
|
|
|
INP_INFO_WLOCK(&tcbinfo);
|
2001-02-26 21:19:47 +00:00
|
|
|
inp = in_pcblookup_hash(&tcbinfo, faddr, th->th_dport,
|
|
|
|
ip->ip_src, th->th_sport, 0, NULL);
|
2002-06-10 20:05:46 +00:00
|
|
|
if (inp != NULL) {
|
|
|
|
INP_LOCK(inp);
|
|
|
|
if (inp->inp_socket != NULL) {
|
2005-04-21 14:29:34 +00:00
|
|
|
icmp_tcp_seq = htonl(th->th_seq);
|
2002-06-10 20:05:46 +00:00
|
|
|
tp = intotcpcb(inp);
|
2005-04-21 14:29:34 +00:00
|
|
|
if (SEQ_GEQ(icmp_tcp_seq, tp->snd_una) &&
|
|
|
|
SEQ_LT(icmp_tcp_seq, tp->snd_max)) {
|
|
|
|
if (cmd == PRC_MSGSIZE) {
|
|
|
|
/*
|
|
|
|
* MTU discovery:
|
|
|
|
* If we got a needfrag set the MTU
|
|
|
|
* in the route to the suggested new
|
|
|
|
* value (if given) and then notify.
|
|
|
|
*/
|
|
|
|
bzero(&inc, sizeof(inc));
|
|
|
|
inc.inc_flags = 0; /* IPv4 */
|
|
|
|
inc.inc_faddr = faddr;
|
|
|
|
|
|
|
|
mtu = ntohs(icp->icmp_nextmtu);
|
2005-05-04 13:48:44 +00:00
|
|
|
/*
|
|
|
|
* If no alternative MTU was
|
|
|
|
* proposed, try the next smaller
|
|
|
|
* one.
|
|
|
|
*/
|
|
|
|
if (!mtu)
|
|
|
|
mtu = ip_next_mtu(ntohs(ip->ip_len),
|
|
|
|
1);
|
|
|
|
if (mtu < max(296, (tcp_minmss)
|
|
|
|
+ sizeof(struct tcpiphdr)))
|
|
|
|
mtu = 0;
|
2005-04-21 14:29:34 +00:00
|
|
|
if (!mtu)
|
2005-05-04 13:48:44 +00:00
|
|
|
mtu = tcp_mssdflt
|
|
|
|
+ sizeof(struct tcpiphdr);
|
|
|
|
/*
|
|
|
|
* Only cache the the MTU if it
|
|
|
|
* is smaller than the interface
|
|
|
|
* or route MTU. tcp_mtudisc()
|
|
|
|
* will do right thing by itself.
|
|
|
|
*/
|
|
|
|
if (mtu <= tcp_maxmtu(&inc))
|
2005-04-21 14:29:34 +00:00
|
|
|
tcp_hc_updatemtu(&inc, mtu);
|
|
|
|
}
|
|
|
|
|
2002-06-14 08:35:21 +00:00
|
|
|
inp = (*notify)(inp, inetctlerrmap[cmd]);
|
2005-04-21 14:29:34 +00:00
|
|
|
}
|
2002-06-10 20:05:46 +00:00
|
|
|
}
|
2004-04-05 00:49:07 +00:00
|
|
|
if (inp != NULL)
|
2002-06-14 08:35:21 +00:00
|
|
|
INP_UNLOCK(inp);
|
2001-11-22 04:50:44 +00:00
|
|
|
} else {
|
|
|
|
inc.inc_fport = th->th_dport;
|
|
|
|
inc.inc_lport = th->th_sport;
|
|
|
|
inc.inc_faddr = faddr;
|
|
|
|
inc.inc_laddr = ip->ip_src;
|
|
|
|
#ifdef INET6
|
|
|
|
inc.inc_isipv6 = 0;
|
|
|
|
#endif
|
|
|
|
syncache_unreach(&inc, th);
|
2001-02-26 21:19:47 +00:00
|
|
|
}
|
2002-06-21 22:52:48 +00:00
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
2001-02-26 21:19:47 +00:00
|
|
|
splx(s);
|
1994-05-24 10:09:53 +00:00
|
|
|
} else
|
2002-06-10 20:05:46 +00:00
|
|
|
in_pcbnotifyall(&tcbinfo, faddr, inetctlerrmap[cmd], notify);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
void
|
|
|
|
tcp6_ctlinput(cmd, sa, d)
|
|
|
|
int cmd;
|
|
|
|
struct sockaddr *sa;
|
|
|
|
void *d;
|
|
|
|
{
|
|
|
|
struct tcphdr th;
|
2002-06-14 08:35:21 +00:00
|
|
|
struct inpcb *(*notify)(struct inpcb *, int) = tcp_notify;
|
2000-01-09 19:17:30 +00:00
|
|
|
struct ip6_hdr *ip6;
|
|
|
|
struct mbuf *m;
|
2001-06-11 12:39:29 +00:00
|
|
|
struct ip6ctlparam *ip6cp = NULL;
|
|
|
|
const struct sockaddr_in6 *sa6_src = NULL;
|
2000-01-09 19:17:30 +00:00
|
|
|
int off;
|
2001-06-11 12:39:29 +00:00
|
|
|
struct tcp_portonly {
|
|
|
|
u_int16_t th_sport;
|
|
|
|
u_int16_t th_dport;
|
|
|
|
} *thp;
|
2000-01-09 19:17:30 +00:00
|
|
|
|
|
|
|
if (sa->sa_family != AF_INET6 ||
|
|
|
|
sa->sa_len != sizeof(struct sockaddr_in6))
|
|
|
|
return;
|
|
|
|
|
2005-04-21 12:37:12 +00:00
|
|
|
if (cmd == PRC_MSGSIZE)
|
2000-01-09 19:17:30 +00:00
|
|
|
notify = tcp_mtudisc;
|
|
|
|
else if (!PRC_IS_REDIRECT(cmd) &&
|
2003-09-11 21:40:21 +00:00
|
|
|
((unsigned)cmd >= PRC_NCMDS || inet6ctlerrmap[cmd] == 0))
|
2000-01-09 19:17:30 +00:00
|
|
|
return;
|
2005-04-21 12:37:12 +00:00
|
|
|
/* Source quench is depreciated. */
|
|
|
|
else if (cmd == PRC_QUENCH)
|
|
|
|
return;
|
2000-01-09 19:17:30 +00:00
|
|
|
|
|
|
|
/* if the parameter is from icmp6, decode it. */
|
|
|
|
if (d != NULL) {
|
2001-06-11 12:39:29 +00:00
|
|
|
ip6cp = (struct ip6ctlparam *)d;
|
2000-01-09 19:17:30 +00:00
|
|
|
m = ip6cp->ip6c_m;
|
|
|
|
ip6 = ip6cp->ip6c_ip6;
|
|
|
|
off = ip6cp->ip6c_off;
|
2001-06-11 12:39:29 +00:00
|
|
|
sa6_src = ip6cp->ip6c_src;
|
2000-01-09 19:17:30 +00:00
|
|
|
} else {
|
|
|
|
m = NULL;
|
|
|
|
ip6 = NULL;
|
2000-10-23 07:11:01 +00:00
|
|
|
off = 0; /* fool gcc */
|
2001-06-11 12:39:29 +00:00
|
|
|
sa6_src = &sa6_any;
|
2000-01-09 19:17:30 +00:00
|
|
|
}
|
|
|
|
|
2004-04-05 00:49:07 +00:00
|
|
|
if (ip6 != NULL) {
|
2001-11-22 04:50:44 +00:00
|
|
|
struct in_conninfo inc;
|
2000-01-09 19:17:30 +00:00
|
|
|
/*
|
|
|
|
* XXX: We assume that when IPV6 is non NULL,
|
|
|
|
* M and OFF are valid.
|
|
|
|
*/
|
|
|
|
|
2000-10-23 07:11:01 +00:00
|
|
|
/* check if we can safely examine src and dst ports */
|
2001-06-11 12:39:29 +00:00
|
|
|
if (m->m_pkthdr.len < off + sizeof(*thp))
|
2000-10-23 07:11:01 +00:00
|
|
|
return;
|
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
bzero(&th, sizeof(th));
|
|
|
|
m_copydata(m, off, sizeof(*thp), (caddr_t)&th);
|
|
|
|
|
2004-08-06 03:45:45 +00:00
|
|
|
in6_pcbnotify(&tcbinfo, sa, th.th_dport,
|
2001-06-11 12:39:29 +00:00
|
|
|
(struct sockaddr *)ip6cp->ip6c_src,
|
2004-02-13 14:50:01 +00:00
|
|
|
th.th_sport, cmd, NULL, notify);
|
2001-11-22 04:50:44 +00:00
|
|
|
|
|
|
|
inc.inc_fport = th.th_dport;
|
|
|
|
inc.inc_lport = th.th_sport;
|
|
|
|
inc.inc6_faddr = ((struct sockaddr_in6 *)sa)->sin6_addr;
|
|
|
|
inc.inc6_laddr = ip6cp->ip6c_src->sin6_addr;
|
|
|
|
inc.inc_isipv6 = 1;
|
2004-08-12 18:19:36 +00:00
|
|
|
INP_INFO_WLOCK(&tcbinfo);
|
2001-11-22 04:50:44 +00:00
|
|
|
syncache_unreach(&inc, &th);
|
2004-08-12 18:19:36 +00:00
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
2000-01-09 19:17:30 +00:00
|
|
|
} else
|
2004-08-06 03:45:45 +00:00
|
|
|
in6_pcbnotify(&tcbinfo, sa, 0, (const struct sockaddr *)sa6_src,
|
2004-02-13 14:50:01 +00:00
|
|
|
0, cmd, NULL, notify);
|
2000-01-09 19:17:30 +00:00
|
|
|
}
|
|
|
|
#endif /* INET6 */
|
|
|
|
|
2001-04-17 18:08:01 +00:00
|
|
|
|
2001-08-22 00:58:16 +00:00
|
|
|
/*
|
|
|
|
* Following is where TCP initial sequence number generation occurs.
|
|
|
|
*
|
|
|
|
* There are two places where we must use initial sequence numbers:
|
|
|
|
* 1. In SYN-ACK packets.
|
|
|
|
* 2. In SYN packets.
|
|
|
|
*
|
2002-04-10 22:12:01 +00:00
|
|
|
* All ISNs for SYN-ACK packets are generated by the syncache. See
|
|
|
|
* tcp_syncache.c for details.
|
2001-08-22 00:58:16 +00:00
|
|
|
*
|
|
|
|
* The ISNs in SYN packets must be monotonic; TIME_WAIT recycling
|
|
|
|
* depends on this property. In addition, these ISNs should be
|
|
|
|
* unguessable so as to prevent connection hijacking. To satisfy
|
|
|
|
* the requirements of this situation, the algorithm outlined in
|
2004-08-16 18:32:07 +00:00
|
|
|
* RFC 1948 is used, with only small modifications.
|
2001-08-22 00:58:16 +00:00
|
|
|
*
|
|
|
|
* Implementation details:
|
|
|
|
*
|
|
|
|
* Time is based off the system timer, and is corrected so that it
|
|
|
|
* increases by one megabyte per second. This allows for proper
|
|
|
|
* recycling on high speed LANs while still leaving over an hour
|
|
|
|
* before rollover.
|
|
|
|
*
|
2004-04-20 06:33:39 +00:00
|
|
|
* As reading the *exact* system time is too expensive to be done
|
|
|
|
* whenever setting up a TCP connection, we increment the time
|
|
|
|
* offset in two ways. First, a small random positive increment
|
|
|
|
* is added to isn_offset for each connection that is set up.
|
|
|
|
* Second, the function tcp_isn_tick fires once per clock tick
|
|
|
|
* and increments isn_offset as necessary so that sequence numbers
|
|
|
|
* are incremented at approximately ISN_BYTES_PER_SECOND. The
|
|
|
|
* random positive increments serve only to ensure that the same
|
|
|
|
* exact sequence number is never sent out twice (as could otherwise
|
|
|
|
* happen when a port is recycled in less than the system tick
|
|
|
|
* interval.)
|
|
|
|
*
|
2001-08-22 00:58:16 +00:00
|
|
|
* net.inet.tcp.isn_reseed_interval controls the number of seconds
|
|
|
|
* between seeding of isn_secret. This is normally set to zero,
|
|
|
|
* as reseeding should not be necessary.
|
|
|
|
*
|
2004-11-23 15:59:43 +00:00
|
|
|
* Locking of the global variables isn_secret, isn_last_reseed, isn_offset,
|
|
|
|
* isn_offset_old, and isn_ctx is performed using the TCP pcbinfo lock. In
|
|
|
|
* general, this means holding an exclusive (write) lock.
|
2001-08-22 00:58:16 +00:00
|
|
|
*/
|
2001-04-17 18:08:01 +00:00
|
|
|
|
2001-08-22 00:58:16 +00:00
|
|
|
#define ISN_BYTES_PER_SECOND 1048576
|
2004-04-20 06:33:39 +00:00
|
|
|
#define ISN_STATIC_INCREMENT 4096
|
|
|
|
#define ISN_RANDOM_INCREMENT (4096 - 1)
|
2001-04-17 18:08:01 +00:00
|
|
|
|
2004-11-23 15:59:43 +00:00
|
|
|
static u_char isn_secret[32];
|
|
|
|
static int isn_last_reseed;
|
|
|
|
static u_int32_t isn_offset, isn_offset_old;
|
|
|
|
static MD5_CTX isn_ctx;
|
2001-04-17 18:08:01 +00:00
|
|
|
|
|
|
|
tcp_seq
|
2001-08-22 00:58:16 +00:00
|
|
|
tcp_new_isn(tp)
|
|
|
|
struct tcpcb *tp;
|
2001-04-17 18:08:01 +00:00
|
|
|
{
|
2001-08-22 00:58:16 +00:00
|
|
|
u_int32_t md5_buffer[4];
|
|
|
|
tcp_seq new_isn;
|
|
|
|
|
2004-11-23 15:59:43 +00:00
|
|
|
INP_INFO_WLOCK_ASSERT(&tcbinfo);
|
2004-12-05 22:27:53 +00:00
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
2004-11-23 15:59:43 +00:00
|
|
|
|
2001-08-22 00:58:16 +00:00
|
|
|
/* Seed if this is the first use, reseed if requested. */
|
2002-04-10 22:12:01 +00:00
|
|
|
if ((isn_last_reseed == 0) || ((tcp_isn_reseed_interval > 0) &&
|
2001-08-22 00:58:16 +00:00
|
|
|
(((u_int)isn_last_reseed + (u_int)tcp_isn_reseed_interval*hz)
|
|
|
|
< (u_int)ticks))) {
|
|
|
|
read_random(&isn_secret, sizeof(isn_secret));
|
|
|
|
isn_last_reseed = ticks;
|
|
|
|
}
|
2004-08-16 18:32:07 +00:00
|
|
|
|
2001-08-22 00:58:16 +00:00
|
|
|
/* Compute the md5 hash and return the ISN. */
|
|
|
|
MD5Init(&isn_ctx);
|
|
|
|
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_fport, sizeof(u_short));
|
|
|
|
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_lport, sizeof(u_short));
|
|
|
|
#ifdef INET6
|
|
|
|
if ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) {
|
|
|
|
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->in6p_faddr,
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->in6p_laddr,
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_faddr,
|
|
|
|
sizeof(struct in_addr));
|
|
|
|
MD5Update(&isn_ctx, (u_char *) &tp->t_inpcb->inp_laddr,
|
|
|
|
sizeof(struct in_addr));
|
|
|
|
}
|
|
|
|
MD5Update(&isn_ctx, (u_char *) &isn_secret, sizeof(isn_secret));
|
|
|
|
MD5Final((u_char *) &md5_buffer, &isn_ctx);
|
|
|
|
new_isn = (tcp_seq) md5_buffer[0];
|
2004-04-20 06:33:39 +00:00
|
|
|
isn_offset += ISN_STATIC_INCREMENT +
|
|
|
|
(arc4random() & ISN_RANDOM_INCREMENT);
|
|
|
|
new_isn += isn_offset;
|
2004-12-23 01:34:26 +00:00
|
|
|
return (new_isn);
|
2001-04-17 18:08:01 +00:00
|
|
|
}
|
|
|
|
|
2004-04-20 06:33:39 +00:00
|
|
|
/*
|
|
|
|
* Increment the offset to the next ISN_BYTES_PER_SECOND / hz boundary
|
|
|
|
* to keep time flowing at a relatively constant rate. If the random
|
|
|
|
* increments have already pushed us past the projected offset, do nothing.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
tcp_isn_tick(xtp)
|
|
|
|
void *xtp;
|
|
|
|
{
|
|
|
|
u_int32_t projected_offset;
|
2004-08-16 18:32:07 +00:00
|
|
|
|
2004-11-23 15:59:43 +00:00
|
|
|
INP_INFO_WLOCK(&tcbinfo);
|
2005-01-31 01:35:01 +00:00
|
|
|
projected_offset = isn_offset_old + ISN_BYTES_PER_SECOND / 100;
|
2004-04-20 06:33:39 +00:00
|
|
|
|
|
|
|
if (projected_offset > isn_offset)
|
|
|
|
isn_offset = projected_offset;
|
|
|
|
|
|
|
|
isn_offset_old = isn_offset;
|
2005-01-30 23:30:28 +00:00
|
|
|
callout_reset(&isn_callout, hz/100, tcp_isn_tick, NULL);
|
2004-11-23 15:59:43 +00:00
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
2004-04-20 06:33:39 +00:00
|
|
|
}
|
|
|
|
|
We currently does not react to ICMP administratively prohibited
messages send by routers when they deny our traffic, this causes
a timeout when trying to connect to TCP ports/services on a remote
host, which is blocked by routers or firewalls.
rfc1122 (Requirements for Internet Hosts) section 3.2.2.1 actually
requi re that we treat such a message for a TCP session, that we
treat it like if we had recieved a RST.
quote begin.
A Destination Unreachable message that is received MUST be
reported to the transport layer. The transport layer SHOULD
use the information appropriately; for example, see Sections
4.1.3.3, 4.2.3.9, and 4.2.4 below. A transport protocol
that has its own mechanism for notifying the sender that a
port is unreachable (e.g., TCP, which sends RST segments)
MUST nevertheless accept an ICMP Port Unreachable for the
same purpose.
quote end.
I've written a small extension that implement this, it also create
a sysctl "net.inet.tcp.icmp_admin_prohib_like_rst" to control if
this new behaviour is activated.
When it's activated (set to 1) we'll treat a ICMP administratively
prohibited message (icmp type 3 code 9, 10 and 13) for a TCP
sessions, as if we recived a TCP RST, but only if the TCP session
is in SYN_SENT state.
The reason for only reacting when in SYN_SENT state, is that this
will solve the problem, and at the same time minimize the risk of
this being abused.
I suggest that we enable this new behaviour by default, but it
would be a change of current behaviour, so if people prefer to
leave it disabled by default, at least for now, this would be ok
for me, the attached diff actually have the sysctl set to 0 by
default.
PR: 23086
Submitted by: Jesper Skriver <jesper@skriver.dk>
2000-12-16 19:42:06 +00:00
|
|
|
/*
|
2001-02-23 20:51:46 +00:00
|
|
|
* When a specific ICMP unreachable message is received and the
|
|
|
|
* connection state is SYN-SENT, drop the connection. This behavior
|
|
|
|
* is controlled by the icmp_may_rst sysctl.
|
We currently does not react to ICMP administratively prohibited
messages send by routers when they deny our traffic, this causes
a timeout when trying to connect to TCP ports/services on a remote
host, which is blocked by routers or firewalls.
rfc1122 (Requirements for Internet Hosts) section 3.2.2.1 actually
requi re that we treat such a message for a TCP session, that we
treat it like if we had recieved a RST.
quote begin.
A Destination Unreachable message that is received MUST be
reported to the transport layer. The transport layer SHOULD
use the information appropriately; for example, see Sections
4.1.3.3, 4.2.3.9, and 4.2.4 below. A transport protocol
that has its own mechanism for notifying the sender that a
port is unreachable (e.g., TCP, which sends RST segments)
MUST nevertheless accept an ICMP Port Unreachable for the
same purpose.
quote end.
I've written a small extension that implement this, it also create
a sysctl "net.inet.tcp.icmp_admin_prohib_like_rst" to control if
this new behaviour is activated.
When it's activated (set to 1) we'll treat a ICMP administratively
prohibited message (icmp type 3 code 9, 10 and 13) for a TCP
sessions, as if we recived a TCP RST, but only if the TCP session
is in SYN_SENT state.
The reason for only reacting when in SYN_SENT state, is that this
will solve the problem, and at the same time minimize the risk of
this being abused.
I suggest that we enable this new behaviour by default, but it
would be a change of current behaviour, so if people prefer to
leave it disabled by default, at least for now, this would be ok
for me, the attached diff actually have the sysctl set to 0 by
default.
PR: 23086
Submitted by: Jesper Skriver <jesper@skriver.dk>
2000-12-16 19:42:06 +00:00
|
|
|
*/
|
2002-06-14 08:35:21 +00:00
|
|
|
struct inpcb *
|
We currently does not react to ICMP administratively prohibited
messages send by routers when they deny our traffic, this causes
a timeout when trying to connect to TCP ports/services on a remote
host, which is blocked by routers or firewalls.
rfc1122 (Requirements for Internet Hosts) section 3.2.2.1 actually
requi re that we treat such a message for a TCP session, that we
treat it like if we had recieved a RST.
quote begin.
A Destination Unreachable message that is received MUST be
reported to the transport layer. The transport layer SHOULD
use the information appropriately; for example, see Sections
4.1.3.3, 4.2.3.9, and 4.2.4 below. A transport protocol
that has its own mechanism for notifying the sender that a
port is unreachable (e.g., TCP, which sends RST segments)
MUST nevertheless accept an ICMP Port Unreachable for the
same purpose.
quote end.
I've written a small extension that implement this, it also create
a sysctl "net.inet.tcp.icmp_admin_prohib_like_rst" to control if
this new behaviour is activated.
When it's activated (set to 1) we'll treat a ICMP administratively
prohibited message (icmp type 3 code 9, 10 and 13) for a TCP
sessions, as if we recived a TCP RST, but only if the TCP session
is in SYN_SENT state.
The reason for only reacting when in SYN_SENT state, is that this
will solve the problem, and at the same time minimize the risk of
this being abused.
I suggest that we enable this new behaviour by default, but it
would be a change of current behaviour, so if people prefer to
leave it disabled by default, at least for now, this would be ok
for me, the attached diff actually have the sysctl set to 0 by
default.
PR: 23086
Submitted by: Jesper Skriver <jesper@skriver.dk>
2000-12-16 19:42:06 +00:00
|
|
|
tcp_drop_syn_sent(inp, errno)
|
|
|
|
struct inpcb *inp;
|
|
|
|
int errno;
|
|
|
|
{
|
|
|
|
struct tcpcb *tp = intotcpcb(inp);
|
|
|
|
|
2005-06-01 12:06:07 +00:00
|
|
|
INP_INFO_WLOCK_ASSERT(&tcbinfo);
|
2004-11-23 16:06:15 +00:00
|
|
|
INP_LOCK_ASSERT(inp);
|
2005-06-01 12:06:07 +00:00
|
|
|
|
2004-04-05 00:52:05 +00:00
|
|
|
if (tp != NULL && tp->t_state == TCPS_SYN_SENT) {
|
2001-02-18 09:34:55 +00:00
|
|
|
tcp_drop(tp, errno);
|
2004-12-23 01:34:26 +00:00
|
|
|
return (NULL);
|
2002-06-14 08:35:21 +00:00
|
|
|
}
|
2004-12-23 01:34:26 +00:00
|
|
|
return (inp);
|
2001-02-18 09:34:55 +00:00
|
|
|
}
|
|
|
|
|
1995-09-18 15:51:40 +00:00
|
|
|
/*
|
|
|
|
* When `need fragmentation' ICMP is received, update our idea of the MSS
|
|
|
|
* based on the new value in the route. Also nudge TCP to send something,
|
|
|
|
* since we know the packet we just sent was dropped.
|
1995-09-20 21:00:59 +00:00
|
|
|
* This duplicates some code in the tcp_mss() function in tcp_input.c.
|
1995-09-18 15:51:40 +00:00
|
|
|
*/
|
2002-06-14 08:35:21 +00:00
|
|
|
struct inpcb *
|
1995-09-18 15:51:40 +00:00
|
|
|
tcp_mtudisc(inp, errno)
|
|
|
|
struct inpcb *inp;
|
|
|
|
int errno;
|
|
|
|
{
|
|
|
|
struct tcpcb *tp = intotcpcb(inp);
|
1995-09-20 21:00:59 +00:00
|
|
|
struct socket *so = inp->inp_socket;
|
2003-11-20 20:07:39 +00:00
|
|
|
u_int maxmtu;
|
|
|
|
u_int romtu;
|
1995-09-20 21:00:59 +00:00
|
|
|
int mss;
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
2004-08-19 20:16:17 +00:00
|
|
|
int isipv6;
|
2000-01-09 19:17:30 +00:00
|
|
|
#endif /* INET6 */
|
1995-09-18 15:51:40 +00:00
|
|
|
|
2004-11-23 16:06:15 +00:00
|
|
|
INP_LOCK_ASSERT(inp);
|
2004-04-05 00:49:07 +00:00
|
|
|
if (tp != NULL) {
|
2004-08-19 20:16:17 +00:00
|
|
|
#ifdef INET6
|
|
|
|
isipv6 = (tp->t_inpcb->inp_vflag & INP_IPV6) != 0;
|
|
|
|
#endif
|
2003-11-20 20:07:39 +00:00
|
|
|
maxmtu = tcp_hc_getmtu(&inp->inp_inc); /* IPv4 and IPv6 */
|
|
|
|
romtu =
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
2003-11-20 20:07:39 +00:00
|
|
|
isipv6 ? tcp_maxmtu6(&inp->inp_inc) :
|
2000-01-09 19:17:30 +00:00
|
|
|
#endif /* INET6 */
|
2003-11-20 20:07:39 +00:00
|
|
|
tcp_maxmtu(&inp->inp_inc);
|
|
|
|
if (!maxmtu)
|
|
|
|
maxmtu = romtu;
|
|
|
|
else
|
|
|
|
maxmtu = min(maxmtu, romtu);
|
|
|
|
if (!maxmtu) {
|
2000-01-09 19:17:30 +00:00
|
|
|
tp->t_maxopd = tp->t_maxseg =
|
|
|
|
#ifdef INET6
|
|
|
|
isipv6 ? tcp_v6mssdflt :
|
|
|
|
#endif /* INET6 */
|
|
|
|
tcp_mssdflt;
|
2004-12-23 01:34:26 +00:00
|
|
|
return (inp);
|
1995-09-20 21:00:59 +00:00
|
|
|
}
|
2003-11-20 20:07:39 +00:00
|
|
|
mss = maxmtu -
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
|
|
|
(isipv6 ?
|
|
|
|
sizeof(struct ip6_hdr) + sizeof(struct tcphdr) :
|
|
|
|
#endif /* INET6 */
|
|
|
|
sizeof(struct tcpiphdr)
|
|
|
|
#ifdef INET6
|
|
|
|
)
|
|
|
|
#endif /* INET6 */
|
|
|
|
;
|
|
|
|
|
1995-12-20 17:42:28 +00:00
|
|
|
/*
|
|
|
|
* XXX - The above conditional probably violates the TCP
|
|
|
|
* spec. The problem is that, since we don't know the
|
|
|
|
* other end's MSS, we are supposed to use a conservative
|
|
|
|
* default. But, if we do that, then MTU discovery will
|
|
|
|
* never actually take place, because the conservative
|
|
|
|
* default is much less than the MTUs typically seen
|
|
|
|
* on the Internet today. For the moment, we'll sweep
|
|
|
|
* this under the carpet.
|
|
|
|
*
|
|
|
|
* The conservative default might not actually be a problem
|
|
|
|
* if the only case this occurs is when sending an initial
|
|
|
|
* SYN with options and data to a host we've never talked
|
|
|
|
* to before. Then, they will reply with an MSS value which
|
|
|
|
* will get recorded and the new parameters should get
|
|
|
|
* recomputed. For Further Study.
|
|
|
|
*/
|
1995-10-10 17:45:43 +00:00
|
|
|
if (tp->t_maxopd <= mss)
|
2004-12-23 01:34:26 +00:00
|
|
|
return (inp);
|
1995-09-20 21:00:59 +00:00
|
|
|
tp->t_maxopd = mss;
|
|
|
|
|
|
|
|
if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
|
|
|
|
(tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP)
|
|
|
|
mss -= TCPOLEN_TSTAMP_APPA;
|
|
|
|
#if (MCLBYTES & (MCLBYTES - 1)) == 0
|
|
|
|
if (mss > MCLBYTES)
|
|
|
|
mss &= ~(MCLBYTES-1);
|
|
|
|
#else
|
|
|
|
if (mss > MCLBYTES)
|
|
|
|
mss = mss / MCLBYTES * MCLBYTES;
|
1995-09-18 15:51:40 +00:00
|
|
|
#endif
|
1995-09-20 21:00:59 +00:00
|
|
|
if (so->so_snd.sb_hiwat < mss)
|
|
|
|
mss = so->so_snd.sb_hiwat;
|
|
|
|
|
|
|
|
tp->t_maxseg = mss;
|
|
|
|
|
1995-10-12 17:37:25 +00:00
|
|
|
tcpstat.tcps_mturesent++;
|
1999-08-30 21:17:07 +00:00
|
|
|
tp->t_rtttime = 0;
|
1995-10-12 17:37:25 +00:00
|
|
|
tp->snd_nxt = tp->snd_una;
|
|
|
|
tcp_output(tp);
|
1995-09-20 21:00:59 +00:00
|
|
|
}
|
2004-12-23 01:34:26 +00:00
|
|
|
return (inp);
|
1995-09-18 15:51:40 +00:00
|
|
|
}
|
|
|
|
|
1995-02-09 23:13:27 +00:00
|
|
|
/*
|
|
|
|
* Look-up the routing entry to the peer of this inpcb. If no route
|
2002-12-24 21:00:31 +00:00
|
|
|
* is found and it cannot be allocated, then return NULL. This routine
|
1995-02-09 23:13:27 +00:00
|
|
|
* is called by TCP routines that access the rmx structure and by tcp_mss
|
|
|
|
* to get the interface MTU.
|
|
|
|
*/
|
2004-08-16 18:32:07 +00:00
|
|
|
u_long
|
2003-11-20 20:07:39 +00:00
|
|
|
tcp_maxmtu(inc)
|
2001-11-22 04:50:44 +00:00
|
|
|
struct in_conninfo *inc;
|
1995-02-09 23:13:27 +00:00
|
|
|
{
|
2003-11-20 20:07:39 +00:00
|
|
|
struct route sro;
|
|
|
|
struct sockaddr_in *dst;
|
|
|
|
struct ifnet *ifp;
|
|
|
|
u_long maxmtu = 0;
|
|
|
|
|
|
|
|
KASSERT(inc != NULL, ("tcp_maxmtu with NULL in_conninfo pointer"));
|
|
|
|
|
2003-11-26 20:31:13 +00:00
|
|
|
bzero(&sro, sizeof(sro));
|
2003-11-20 20:07:39 +00:00
|
|
|
if (inc->inc_faddr.s_addr != INADDR_ANY) {
|
|
|
|
dst = (struct sockaddr_in *)&sro.ro_dst;
|
|
|
|
dst->sin_family = AF_INET;
|
|
|
|
dst->sin_len = sizeof(*dst);
|
|
|
|
dst->sin_addr = inc->inc_faddr;
|
|
|
|
rtalloc_ign(&sro, RTF_CLONING);
|
|
|
|
}
|
|
|
|
if (sro.ro_rt != NULL) {
|
|
|
|
ifp = sro.ro_rt->rt_ifp;
|
|
|
|
if (sro.ro_rt->rt_rmx.rmx_mtu == 0)
|
|
|
|
maxmtu = ifp->if_mtu;
|
|
|
|
else
|
|
|
|
maxmtu = min(sro.ro_rt->rt_rmx.rmx_mtu, ifp->if_mtu);
|
|
|
|
RTFREE(sro.ro_rt);
|
1995-02-09 23:13:27 +00:00
|
|
|
}
|
2003-11-20 20:07:39 +00:00
|
|
|
return (maxmtu);
|
1995-02-09 23:13:27 +00:00
|
|
|
}
|
|
|
|
|
2000-01-09 19:17:30 +00:00
|
|
|
#ifdef INET6
|
2003-11-20 20:07:39 +00:00
|
|
|
u_long
|
|
|
|
tcp_maxmtu6(inc)
|
2001-11-22 04:50:44 +00:00
|
|
|
struct in_conninfo *inc;
|
2000-01-09 19:17:30 +00:00
|
|
|
{
|
2003-11-20 20:07:39 +00:00
|
|
|
struct route_in6 sro6;
|
|
|
|
struct ifnet *ifp;
|
|
|
|
u_long maxmtu = 0;
|
|
|
|
|
|
|
|
KASSERT(inc != NULL, ("tcp_maxmtu6 with NULL in_conninfo pointer"));
|
|
|
|
|
2003-11-26 20:31:13 +00:00
|
|
|
bzero(&sro6, sizeof(sro6));
|
2003-11-20 20:07:39 +00:00
|
|
|
if (!IN6_IS_ADDR_UNSPECIFIED(&inc->inc6_faddr)) {
|
|
|
|
sro6.ro_dst.sin6_family = AF_INET6;
|
|
|
|
sro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6);
|
|
|
|
sro6.ro_dst.sin6_addr = inc->inc6_faddr;
|
|
|
|
rtalloc_ign((struct route *)&sro6, RTF_CLONING);
|
2000-01-09 19:17:30 +00:00
|
|
|
}
|
2003-11-20 20:07:39 +00:00
|
|
|
if (sro6.ro_rt != NULL) {
|
|
|
|
ifp = sro6.ro_rt->rt_ifp;
|
|
|
|
if (sro6.ro_rt->rt_rmx.rmx_mtu == 0)
|
|
|
|
maxmtu = IN6_LINKMTU(sro6.ro_rt->rt_ifp);
|
|
|
|
else
|
|
|
|
maxmtu = min(sro6.ro_rt->rt_rmx.rmx_mtu,
|
|
|
|
IN6_LINKMTU(sro6.ro_rt->rt_ifp));
|
|
|
|
RTFREE(sro6.ro_rt);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (maxmtu);
|
2000-01-09 19:17:30 +00:00
|
|
|
}
|
|
|
|
#endif /* INET6 */
|
|
|
|
|
|
|
|
#ifdef IPSEC
|
|
|
|
/* compute ESP/AH header size for TCP, including outer IP header. */
|
|
|
|
size_t
|
|
|
|
ipsec_hdrsiz_tcp(tp)
|
|
|
|
struct tcpcb *tp;
|
|
|
|
{
|
|
|
|
struct inpcb *inp;
|
|
|
|
struct mbuf *m;
|
|
|
|
size_t hdrsiz;
|
|
|
|
struct ip *ip;
|
|
|
|
#ifdef INET6
|
|
|
|
struct ip6_hdr *ip6;
|
2003-02-19 22:32:43 +00:00
|
|
|
#endif
|
2000-01-09 19:17:30 +00:00
|
|
|
struct tcphdr *th;
|
|
|
|
|
2001-06-23 03:21:46 +00:00
|
|
|
if ((tp == NULL) || ((inp = tp->t_inpcb) == NULL))
|
2004-12-23 01:34:26 +00:00
|
|
|
return (0);
|
2003-02-19 05:47:46 +00:00
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
2000-01-09 19:17:30 +00:00
|
|
|
if (!m)
|
2004-12-23 01:34:26 +00:00
|
|
|
return (0);
|
2000-01-09 19:17:30 +00:00
|
|
|
|
|
|
|
#ifdef INET6
|
|
|
|
if ((inp->inp_vflag & INP_IPV6) != 0) {
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
th = (struct tcphdr *)(ip6 + 1);
|
|
|
|
m->m_pkthdr.len = m->m_len =
|
|
|
|
sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
|
2003-02-19 22:18:06 +00:00
|
|
|
tcpip_fillheaders(inp, ip6, th);
|
2000-01-09 19:17:30 +00:00
|
|
|
hdrsiz = ipsec6_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp);
|
|
|
|
} else
|
|
|
|
#endif /* INET6 */
|
2004-08-16 18:32:07 +00:00
|
|
|
{
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
th = (struct tcphdr *)(ip + 1);
|
|
|
|
m->m_pkthdr.len = m->m_len = sizeof(struct tcpiphdr);
|
|
|
|
tcpip_fillheaders(inp, ip, th);
|
|
|
|
hdrsiz = ipsec4_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp);
|
|
|
|
}
|
2000-01-09 19:17:30 +00:00
|
|
|
|
|
|
|
m_free(m);
|
2004-12-23 01:34:26 +00:00
|
|
|
return (hdrsiz);
|
2000-01-09 19:17:30 +00:00
|
|
|
}
|
|
|
|
#endif /*IPSEC*/
|
|
|
|
|
2003-02-19 22:32:43 +00:00
|
|
|
/*
|
|
|
|
* Move a TCP connection into TIME_WAIT state.
|
2004-12-05 22:27:53 +00:00
|
|
|
* tcbinfo is locked.
|
2003-02-19 22:32:43 +00:00
|
|
|
* inp is locked, and is unlocked before returning.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
tcp_twstart(tp)
|
|
|
|
struct tcpcb *tp;
|
|
|
|
{
|
|
|
|
struct tcptw *tw;
|
|
|
|
struct inpcb *inp;
|
|
|
|
int tw_time, acknow;
|
|
|
|
struct socket *so;
|
|
|
|
|
tcp_timewait() performs multiple non-atomic reads on the tcptw
structure, so assert the inpcb lock associated with the tcptw.
Also assert the tcbinfo lock, as tcp_timewait() may call
tcp_twclose() or tcp_2msl_rest(), which require it. Since
tcp_timewait() is already called with that lock from tcp_input(),
this doesn't change current locking, merely documents reasons for
it.
In tcp_twstart(), assert the tcbinfo lock, as tcp_timer_2msl_rest()
is called, which requires that lock.
In tcp_twclose(), assert the tcbinfo lock, as tcp_timer_2msl_stop()
is called, which requires that lock.
Document the locking strategy for the time wait queues in tcp_timer.c,
which consists of protecting the time wait queues in the same manner
as the tcbinfo structure (using the tcbinfo lock).
In tcp_timer_2msl_reset(), assert the tcbinfo lock, as the time wait
queues are modified.
In tcp_timer_2msl_stop(), assert the tcbinfo lock, as the time wait
queues may be modified.
In tcp_timer_2msl_tw(), assert the tcbinfo lock, as the time wait
queues may be modified.
MFC after: 2 weeks
2004-11-23 17:21:30 +00:00
|
|
|
INP_INFO_WLOCK_ASSERT(&tcbinfo); /* tcp_timer_2msl_reset(). */
|
2004-11-23 16:23:13 +00:00
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
|
|
|
|
2003-03-08 22:06:20 +00:00
|
|
|
tw = uma_zalloc(tcptw_zone, M_NOWAIT);
|
|
|
|
if (tw == NULL) {
|
|
|
|
tw = tcp_timer_2msl_tw(1);
|
|
|
|
if (tw == NULL) {
|
|
|
|
tcp_close(tp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2003-02-19 22:32:43 +00:00
|
|
|
inp = tp->t_inpcb;
|
|
|
|
tw->tw_inpcb = inp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recover last window size sent.
|
|
|
|
*/
|
|
|
|
tw->last_win = (tp->rcv_adv - tp->rcv_nxt) >> tp->rcv_scale;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set t_recent if timestamps are used on the connection.
|
|
|
|
*/
|
2004-08-16 18:32:07 +00:00
|
|
|
if ((tp->t_flags & (TF_REQ_TSTMP|TF_RCVD_TSTMP|TF_NOOPT)) ==
|
|
|
|
(TF_REQ_TSTMP|TF_RCVD_TSTMP))
|
2003-02-19 22:32:43 +00:00
|
|
|
tw->t_recent = tp->ts_recent;
|
|
|
|
else
|
|
|
|
tw->t_recent = 0;
|
|
|
|
|
|
|
|
tw->snd_nxt = tp->snd_nxt;
|
|
|
|
tw->rcv_nxt = tp->rcv_nxt;
|
2003-11-01 07:30:08 +00:00
|
|
|
tw->iss = tp->iss;
|
2003-11-02 07:47:03 +00:00
|
|
|
tw->irs = tp->irs;
|
2003-02-19 22:32:43 +00:00
|
|
|
tw->t_starttime = tp->t_starttime;
|
2003-03-08 22:06:20 +00:00
|
|
|
tw->tw_time = 0;
|
2003-02-19 22:32:43 +00:00
|
|
|
|
|
|
|
/* XXX
|
|
|
|
* If this code will
|
|
|
|
* be used for fin-wait-2 state also, then we may need
|
|
|
|
* a ts_recent from the last segment.
|
|
|
|
*/
|
2004-11-02 22:22:22 +00:00
|
|
|
tw_time = 2 * tcp_msl;
|
|
|
|
acknow = tp->t_flags & TF_ACKNOW;
|
2003-02-19 22:32:43 +00:00
|
|
|
tcp_discardcb(tp);
|
|
|
|
so = inp->inp_socket;
|
2004-10-18 22:19:43 +00:00
|
|
|
ACCEPT_LOCK();
|
2004-06-12 20:47:32 +00:00
|
|
|
SOCK_LOCK(so);
|
2003-02-19 22:32:43 +00:00
|
|
|
so->so_pcb = NULL;
|
|
|
|
tw->tw_cred = crhold(so->so_cred);
|
|
|
|
tw->tw_so_options = so->so_options;
|
|
|
|
sotryfree(so);
|
|
|
|
inp->inp_socket = NULL;
|
2004-06-12 20:47:32 +00:00
|
|
|
if (acknow)
|
|
|
|
tcp_twrespond(tw, TH_ACK);
|
2003-02-19 22:32:43 +00:00
|
|
|
inp->inp_ppcb = (caddr_t)tw;
|
|
|
|
inp->inp_vflag |= INP_TIMEWAIT;
|
2003-03-08 22:06:20 +00:00
|
|
|
tcp_timer_2msl_reset(tw, tw_time);
|
2003-02-19 22:32:43 +00:00
|
|
|
INP_UNLOCK(inp);
|
|
|
|
}
|
|
|
|
|
2003-11-02 07:47:03 +00:00
|
|
|
/*
|
|
|
|
* The appromixate rate of ISN increase of Microsoft TCP stacks;
|
|
|
|
* the actual rate is slightly higher due to the addition of
|
|
|
|
* random positive increments.
|
|
|
|
*
|
|
|
|
* Most other new OSes use semi-randomized ISN values, so we
|
|
|
|
* do not need to worry about them.
|
|
|
|
*/
|
|
|
|
#define MS_ISN_BYTES_PER_SECOND 250000
|
|
|
|
|
2003-11-01 07:30:08 +00:00
|
|
|
/*
|
|
|
|
* Determine if the ISN we will generate has advanced beyond the last
|
|
|
|
* sequence number used by the previous connection. If so, indicate
|
|
|
|
* that it is safe to recycle this tw socket by returning 1.
|
2004-11-23 16:23:13 +00:00
|
|
|
*
|
|
|
|
* XXXRW: This function should assert the inpcb lock as it does multiple
|
2004-12-23 01:27:13 +00:00
|
|
|
* non-atomic reads from the tcptw, but is currently called without it from
|
2004-11-23 16:23:13 +00:00
|
|
|
* in_pcb.c:in_pcblookup_local().
|
2003-11-01 07:30:08 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
tcp_twrecycleable(struct tcptw *tw)
|
|
|
|
{
|
2003-11-02 07:47:03 +00:00
|
|
|
tcp_seq new_iss = tw->iss;
|
|
|
|
tcp_seq new_irs = tw->irs;
|
2003-11-01 07:30:08 +00:00
|
|
|
|
2003-11-02 07:47:03 +00:00
|
|
|
new_iss += (ticks - tw->t_starttime) * (ISN_BYTES_PER_SECOND / hz);
|
|
|
|
new_irs += (ticks - tw->t_starttime) * (MS_ISN_BYTES_PER_SECOND / hz);
|
2004-08-16 18:32:07 +00:00
|
|
|
|
2003-11-02 07:47:03 +00:00
|
|
|
if (SEQ_GT(new_iss, tw->snd_nxt) && SEQ_GT(new_irs, tw->rcv_nxt))
|
2004-12-23 01:34:26 +00:00
|
|
|
return (1);
|
2003-11-01 07:30:08 +00:00
|
|
|
else
|
2004-12-23 01:34:26 +00:00
|
|
|
return (0);
|
2003-11-01 07:30:08 +00:00
|
|
|
}
|
|
|
|
|
2003-03-08 22:06:20 +00:00
|
|
|
struct tcptw *
|
|
|
|
tcp_twclose(struct tcptw *tw, int reuse)
|
2003-02-19 22:32:43 +00:00
|
|
|
{
|
|
|
|
struct inpcb *inp;
|
|
|
|
|
|
|
|
inp = tw->tw_inpcb;
|
tcp_timewait() performs multiple non-atomic reads on the tcptw
structure, so assert the inpcb lock associated with the tcptw.
Also assert the tcbinfo lock, as tcp_timewait() may call
tcp_twclose() or tcp_2msl_rest(), which require it. Since
tcp_timewait() is already called with that lock from tcp_input(),
this doesn't change current locking, merely documents reasons for
it.
In tcp_twstart(), assert the tcbinfo lock, as tcp_timer_2msl_rest()
is called, which requires that lock.
In tcp_twclose(), assert the tcbinfo lock, as tcp_timer_2msl_stop()
is called, which requires that lock.
Document the locking strategy for the time wait queues in tcp_timer.c,
which consists of protecting the time wait queues in the same manner
as the tcbinfo structure (using the tcbinfo lock).
In tcp_timer_2msl_reset(), assert the tcbinfo lock, as the time wait
queues are modified.
In tcp_timer_2msl_stop(), assert the tcbinfo lock, as the time wait
queues may be modified.
In tcp_timer_2msl_tw(), assert the tcbinfo lock, as the time wait
queues may be modified.
MFC after: 2 weeks
2004-11-23 17:21:30 +00:00
|
|
|
INP_INFO_WLOCK_ASSERT(&tcbinfo); /* tcp_timer_2msl_stop(). */
|
2004-11-23 16:23:13 +00:00
|
|
|
INP_LOCK_ASSERT(inp);
|
|
|
|
|
2003-02-19 22:32:43 +00:00
|
|
|
tw->tw_inpcb = NULL;
|
2003-03-08 22:06:20 +00:00
|
|
|
tcp_timer_2msl_stop(tw);
|
2003-02-19 22:32:43 +00:00
|
|
|
inp->inp_ppcb = NULL;
|
|
|
|
#ifdef INET6
|
|
|
|
if (inp->inp_vflag & INP_IPV6PROTO)
|
|
|
|
in6_pcbdetach(inp);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
in_pcbdetach(inp);
|
|
|
|
tcpstat.tcps_closed++;
|
2004-02-19 14:13:21 +00:00
|
|
|
crfree(tw->tw_cred);
|
|
|
|
tw->tw_cred = NULL;
|
2003-03-08 22:06:20 +00:00
|
|
|
if (reuse)
|
|
|
|
return (tw);
|
|
|
|
uma_zfree(tcptw_zone, tw);
|
|
|
|
return (NULL);
|
2003-02-19 22:32:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2004-02-28 15:12:20 +00:00
|
|
|
tcp_twrespond(struct tcptw *tw, int flags)
|
2003-02-19 22:32:43 +00:00
|
|
|
{
|
|
|
|
struct inpcb *inp = tw->tw_inpcb;
|
|
|
|
struct tcphdr *th;
|
|
|
|
struct mbuf *m;
|
|
|
|
struct ip *ip = NULL;
|
|
|
|
u_int8_t *optp;
|
|
|
|
u_int hdrlen, optlen;
|
|
|
|
int error;
|
|
|
|
#ifdef INET6
|
|
|
|
struct ip6_hdr *ip6 = NULL;
|
|
|
|
int isipv6 = inp->inp_inc.inc_isipv6;
|
|
|
|
#endif
|
|
|
|
|
2004-11-23 16:23:13 +00:00
|
|
|
INP_LOCK_ASSERT(inp);
|
|
|
|
|
2003-02-21 23:17:12 +00:00
|
|
|
m = m_gethdr(M_DONTWAIT, MT_HEADER);
|
2003-02-19 22:32:43 +00:00
|
|
|
if (m == NULL)
|
|
|
|
return (ENOBUFS);
|
|
|
|
m->m_data += max_linkhdr;
|
|
|
|
|
2003-05-07 05:26:27 +00:00
|
|
|
#ifdef MAC
|
2003-12-17 14:55:11 +00:00
|
|
|
mac_create_mbuf_from_inpcb(inp, m);
|
2003-05-07 05:26:27 +00:00
|
|
|
#endif
|
|
|
|
|
2003-02-19 23:43:04 +00:00
|
|
|
#ifdef INET6
|
2003-02-19 22:32:43 +00:00
|
|
|
if (isipv6) {
|
|
|
|
hdrlen = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
th = (struct tcphdr *)(ip6 + 1);
|
|
|
|
tcpip_fillheaders(inp, ip6, th);
|
2003-02-19 23:43:04 +00:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
2003-02-19 22:32:43 +00:00
|
|
|
hdrlen = sizeof(struct tcpiphdr);
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
th = (struct tcphdr *)(ip + 1);
|
|
|
|
tcpip_fillheaders(inp, ip, th);
|
|
|
|
}
|
|
|
|
optp = (u_int8_t *)(th + 1);
|
2004-08-16 18:32:07 +00:00
|
|
|
|
|
|
|
/*
|
2003-02-19 22:32:43 +00:00
|
|
|
* Send a timestamp and echo-reply if both our side and our peer
|
|
|
|
* have sent timestamps in our SYN's and this is not a RST.
|
2004-08-16 18:32:07 +00:00
|
|
|
*/
|
2003-02-19 22:32:43 +00:00
|
|
|
if (tw->t_recent && flags == TH_ACK) {
|
|
|
|
u_int32_t *lp = (u_int32_t *)optp;
|
|
|
|
|
2004-08-16 18:32:07 +00:00
|
|
|
/* Form timestamp option as shown in appendix A of RFC 1323. */
|
|
|
|
*lp++ = htonl(TCPOPT_TSTAMP_HDR);
|
|
|
|
*lp++ = htonl(ticks);
|
|
|
|
*lp = htonl(tw->t_recent);
|
|
|
|
optp += TCPOLEN_TSTAMP_APPA;
|
|
|
|
}
|
2003-02-19 22:32:43 +00:00
|
|
|
|
|
|
|
optlen = optp - (u_int8_t *)(th + 1);
|
|
|
|
|
|
|
|
m->m_len = hdrlen + optlen;
|
|
|
|
m->m_pkthdr.len = m->m_len;
|
|
|
|
|
|
|
|
KASSERT(max_linkhdr + m->m_len <= MHLEN, ("tcptw: mbuf too small"));
|
|
|
|
|
|
|
|
th->th_seq = htonl(tw->snd_nxt);
|
|
|
|
th->th_ack = htonl(tw->rcv_nxt);
|
|
|
|
th->th_off = (sizeof(struct tcphdr) + optlen) >> 2;
|
|
|
|
th->th_flags = flags;
|
|
|
|
th->th_win = htons(tw->last_win);
|
|
|
|
|
2003-02-19 23:43:04 +00:00
|
|
|
#ifdef INET6
|
2003-02-19 22:32:43 +00:00
|
|
|
if (isipv6) {
|
|
|
|
th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr),
|
|
|
|
sizeof(struct tcphdr) + optlen);
|
2003-11-20 20:07:39 +00:00
|
|
|
ip6->ip6_hlim = in6_selecthlim(inp, NULL);
|
|
|
|
error = ip6_output(m, inp->in6p_outputopts, NULL,
|
2003-02-19 22:32:43 +00:00
|
|
|
(tw->tw_so_options & SO_DONTROUTE), NULL, NULL, inp);
|
2003-02-19 23:43:04 +00:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
2003-02-19 22:32:43 +00:00
|
|
|
th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
|
2004-08-16 18:32:07 +00:00
|
|
|
htons(sizeof(struct tcphdr) + optlen + IPPROTO_TCP));
|
2003-02-19 22:32:43 +00:00
|
|
|
m->m_pkthdr.csum_flags = CSUM_TCP;
|
|
|
|
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
|
|
|
|
ip->ip_len = m->m_pkthdr.len;
|
2004-01-08 11:17:11 +00:00
|
|
|
if (path_mtu_discovery)
|
|
|
|
ip->ip_off |= IP_DF;
|
2003-11-20 20:07:39 +00:00
|
|
|
error = ip_output(m, inp->inp_options, NULL,
|
2004-09-05 02:34:12 +00:00
|
|
|
((tw->tw_so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0),
|
|
|
|
NULL, inp);
|
2003-02-19 22:32:43 +00:00
|
|
|
}
|
|
|
|
if (flags & TH_ACK)
|
|
|
|
tcpstat.tcps_sndacks++;
|
|
|
|
else
|
|
|
|
tcpstat.tcps_sndctrl++;
|
|
|
|
tcpstat.tcps_sndtotal++;
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2002-08-17 18:26:02 +00:00
|
|
|
/*
|
|
|
|
* TCP BANDWIDTH DELAY PRODUCT WINDOW LIMITING
|
|
|
|
*
|
|
|
|
* This code attempts to calculate the bandwidth-delay product as a
|
|
|
|
* means of determining the optimal window size to maximize bandwidth,
|
|
|
|
* minimize RTT, and avoid the over-allocation of buffers on interfaces and
|
|
|
|
* routers. This code also does a fairly good job keeping RTTs in check
|
|
|
|
* across slow links like modems. We implement an algorithm which is very
|
|
|
|
* similar (but not meant to be) TCP/Vegas. The code operates on the
|
|
|
|
* transmitter side of a TCP connection and so only effects the transmit
|
|
|
|
* side of the connection.
|
|
|
|
*
|
|
|
|
* BACKGROUND: TCP makes no provision for the management of buffer space
|
2004-08-16 18:32:07 +00:00
|
|
|
* at the end points or at the intermediate routers and switches. A TCP
|
2002-08-17 18:26:02 +00:00
|
|
|
* stream, whether using NewReno or not, will eventually buffer as
|
|
|
|
* many packets as it is able and the only reason this typically works is
|
|
|
|
* due to the fairly small default buffers made available for a connection
|
|
|
|
* (typicaly 16K or 32K). As machines use larger windows and/or window
|
|
|
|
* scaling it is now fairly easy for even a single TCP connection to blow-out
|
2004-08-16 18:32:07 +00:00
|
|
|
* all available buffer space not only on the local interface, but on
|
2002-08-17 18:26:02 +00:00
|
|
|
* intermediate routers and switches as well. NewReno makes a misguided
|
|
|
|
* attempt to 'solve' this problem by waiting for an actual failure to occur,
|
|
|
|
* then backing off, then steadily increasing the window again until another
|
|
|
|
* failure occurs, ad-infinitum. This results in terrible oscillation that
|
|
|
|
* is only made worse as network loads increase and the idea of intentionally
|
|
|
|
* blowing out network buffers is, frankly, a terrible way to manage network
|
|
|
|
* resources.
|
|
|
|
*
|
|
|
|
* It is far better to limit the transmit window prior to the failure
|
|
|
|
* condition being achieved. There are two general ways to do this: First
|
|
|
|
* you can 'scan' through different transmit window sizes and locate the
|
|
|
|
* point where the RTT stops increasing, indicating that you have filled the
|
|
|
|
* pipe, then scan backwards until you note that RTT stops decreasing, then
|
|
|
|
* repeat ad-infinitum. This method works in principle but has severe
|
|
|
|
* implementation issues due to RTT variances, timer granularity, and
|
|
|
|
* instability in the algorithm which can lead to many false positives and
|
|
|
|
* create oscillations as well as interact badly with other TCP streams
|
|
|
|
* implementing the same algorithm.
|
|
|
|
*
|
|
|
|
* The second method is to limit the window to the bandwidth delay product
|
|
|
|
* of the link. This is the method we implement. RTT variances and our
|
2004-08-16 18:32:07 +00:00
|
|
|
* own manipulation of the congestion window, bwnd, can potentially
|
2002-08-17 18:26:02 +00:00
|
|
|
* destabilize the algorithm. For this reason we have to stabilize the
|
|
|
|
* elements used to calculate the window. We do this by using the minimum
|
|
|
|
* observed RTT, the long term average of the observed bandwidth, and
|
|
|
|
* by adding two segments worth of slop. It isn't perfect but it is able
|
|
|
|
* to react to changing conditions and gives us a very stable basis on
|
|
|
|
* which to extend the algorithm.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
tcp_xmit_bandwidth_limit(struct tcpcb *tp, tcp_seq ack_seq)
|
|
|
|
{
|
|
|
|
u_long bw;
|
|
|
|
u_long bwnd;
|
|
|
|
int save_ticks;
|
|
|
|
|
2004-12-05 22:27:53 +00:00
|
|
|
INP_LOCK_ASSERT(tp->t_inpcb);
|
|
|
|
|
2002-08-17 18:26:02 +00:00
|
|
|
/*
|
|
|
|
* If inflight_enable is disabled in the middle of a tcp connection,
|
|
|
|
* make sure snd_bwnd is effectively disabled.
|
|
|
|
*/
|
|
|
|
if (tcp_inflight_enable == 0) {
|
|
|
|
tp->snd_bwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
|
|
|
|
tp->snd_bandwidth = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Figure out the bandwidth. Due to the tick granularity this
|
|
|
|
* is a very rough number and it MUST be averaged over a fairly
|
|
|
|
* long period of time. XXX we need to take into account a link
|
|
|
|
* that is not using all available bandwidth, but for now our
|
|
|
|
* slop will ramp us up if this case occurs and the bandwidth later
|
|
|
|
* increases.
|
2002-08-24 17:22:44 +00:00
|
|
|
*
|
|
|
|
* Note: if ticks rollover 'bw' may wind up negative. We must
|
|
|
|
* effectively reset t_bw_rtttime for this case.
|
2002-08-17 18:26:02 +00:00
|
|
|
*/
|
|
|
|
save_ticks = ticks;
|
|
|
|
if ((u_int)(save_ticks - tp->t_bw_rtttime) < 1)
|
|
|
|
return;
|
|
|
|
|
2004-08-16 18:32:07 +00:00
|
|
|
bw = (int64_t)(ack_seq - tp->t_bw_rtseq) * hz /
|
2002-08-17 18:26:02 +00:00
|
|
|
(save_ticks - tp->t_bw_rtttime);
|
|
|
|
tp->t_bw_rtttime = save_ticks;
|
|
|
|
tp->t_bw_rtseq = ack_seq;
|
2002-08-24 17:22:44 +00:00
|
|
|
if (tp->t_bw_rtttime == 0 || (int)bw < 0)
|
2002-08-17 18:26:02 +00:00
|
|
|
return;
|
|
|
|
bw = ((int64_t)tp->snd_bandwidth * 15 + bw) >> 4;
|
|
|
|
|
|
|
|
tp->snd_bandwidth = bw;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the semi-static bandwidth delay product, plus two maximal
|
|
|
|
* segments. The additional slop puts us squarely in the sweet
|
2002-12-14 21:00:17 +00:00
|
|
|
* spot and also handles the bandwidth run-up case and stabilization.
|
|
|
|
* Without the slop we could be locking ourselves into a lower
|
|
|
|
* bandwidth.
|
2002-08-17 18:26:02 +00:00
|
|
|
*
|
|
|
|
* Situations Handled:
|
|
|
|
* (1) Prevents over-queueing of packets on LANs, especially on
|
|
|
|
* high speed LANs, allowing larger TCP buffers to be
|
2004-08-16 18:32:07 +00:00
|
|
|
* specified, and also does a good job preventing
|
2002-08-17 18:26:02 +00:00
|
|
|
* over-queueing of packets over choke points like modems
|
|
|
|
* (at least for the transmit side).
|
|
|
|
*
|
|
|
|
* (2) Is able to handle changing network loads (bandwidth
|
|
|
|
* drops so bwnd drops, bandwidth increases so bwnd
|
|
|
|
* increases).
|
|
|
|
*
|
|
|
|
* (3) Theoretically should stabilize in the face of multiple
|
|
|
|
* connections implementing the same algorithm (this may need
|
|
|
|
* a little work).
|
2002-12-14 21:00:17 +00:00
|
|
|
*
|
|
|
|
* (4) Stability value (defaults to 20 = 2 maximal packets) can
|
|
|
|
* be adjusted with a sysctl but typically only needs to be
|
|
|
|
* on very slow connections. A value no smaller then 5
|
|
|
|
* should be used, but only reduce this default if you have
|
|
|
|
* no other choice.
|
2002-08-17 18:26:02 +00:00
|
|
|
*/
|
|
|
|
#define USERTT ((tp->t_srtt + tp->t_rttbest) / 2)
|
2002-12-14 21:00:17 +00:00
|
|
|
bwnd = (int64_t)bw * USERTT / (hz << TCP_RTT_SHIFT) + tcp_inflight_stab * tp->t_maxseg / 10;
|
2002-08-24 17:22:44 +00:00
|
|
|
#undef USERTT
|
2002-08-17 18:26:02 +00:00
|
|
|
|
|
|
|
if (tcp_inflight_debug > 0) {
|
|
|
|
static int ltime;
|
|
|
|
if ((u_int)(ticks - ltime) >= hz / tcp_inflight_debug) {
|
|
|
|
ltime = ticks;
|
|
|
|
printf("%p bw %ld rttbest %d srtt %d bwnd %ld\n",
|
|
|
|
tp,
|
|
|
|
bw,
|
|
|
|
tp->t_rttbest,
|
|
|
|
tp->t_srtt,
|
|
|
|
bwnd
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((long)bwnd < tcp_inflight_min)
|
|
|
|
bwnd = tcp_inflight_min;
|
|
|
|
if (bwnd > tcp_inflight_max)
|
|
|
|
bwnd = tcp_inflight_max;
|
|
|
|
if ((long)bwnd < tp->t_maxseg * 2)
|
|
|
|
bwnd = tp->t_maxseg * 2;
|
|
|
|
tp->snd_bwnd = bwnd;
|
|
|
|
}
|
|
|
|
|
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
|
2004-02-13 18:21:45 +00:00
|
|
|
/*
|
|
|
|
* Callback function invoked by m_apply() to digest TCP segment data
|
|
|
|
* contained within an mbuf chain.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
tcp_signature_apply(void *fstate, void *data, u_int len)
|
|
|
|
{
|
|
|
|
|
2004-02-14 21:49:48 +00:00
|
|
|
MD5Update(fstate, (u_char *)data, len);
|
2004-02-13 18:21:45 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
/*
|
|
|
|
* Compute TCP-MD5 hash of a TCPv4 segment. (RFC2385)
|
|
|
|
*
|
2004-02-12 20:12:48 +00:00
|
|
|
* Parameters:
|
|
|
|
* m pointer to head of mbuf chain
|
|
|
|
* off0 offset to TCP header within the mbuf chain
|
|
|
|
* len length of TCP segment data, excluding options
|
|
|
|
* optlen length of TCP segment options
|
|
|
|
* buf pointer to storage for computed MD5 digest
|
|
|
|
* direction direction of flow (IPSEC_DIR_INBOUND or OUTBOUND)
|
|
|
|
*
|
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
|
|
|
* We do this over ip, tcphdr, segment data, and the key in the SADB.
|
|
|
|
* When called from tcp_input(), we can be sure that th_sum has been
|
|
|
|
* zeroed out and verified already.
|
|
|
|
*
|
|
|
|
* This function is for IPv4 use only. Calling this function with an
|
|
|
|
* IPv6 packet in the mbuf chain will yield undefined results.
|
|
|
|
*
|
|
|
|
* Return 0 if successful, otherwise return -1.
|
|
|
|
*
|
|
|
|
* XXX The key is retrieved from the system's PF_KEY SADB, by keying a
|
|
|
|
* search with the destination IP address, and a 'magic SPI' to be
|
|
|
|
* determined by the application. This is hardcoded elsewhere to 1179
|
|
|
|
* right now. Another branch of this code exists which uses the SPD to
|
|
|
|
* specify per-application flows but it is unstable.
|
|
|
|
*/
|
|
|
|
int
|
2004-02-13 18:21:45 +00:00
|
|
|
tcp_signature_compute(struct mbuf *m, int off0, int len, int optlen,
|
2004-02-12 20:12:48 +00:00
|
|
|
u_char *buf, u_int direction)
|
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
|
|
|
{
|
|
|
|
union sockaddr_union dst;
|
|
|
|
struct ippseudo ippseudo;
|
|
|
|
MD5_CTX ctx;
|
|
|
|
int doff;
|
|
|
|
struct ip *ip;
|
|
|
|
struct ipovly *ipovly;
|
|
|
|
struct secasvar *sav;
|
|
|
|
struct tcphdr *th;
|
|
|
|
u_short savecsum;
|
|
|
|
|
2004-02-12 20:12:48 +00:00
|
|
|
KASSERT(m != NULL, ("NULL mbuf chain"));
|
|
|
|
KASSERT(buf != NULL, ("NULL signature pointer"));
|
|
|
|
|
|
|
|
/* Extract the destination from the IP header in the mbuf. */
|
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
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
bzero(&dst, sizeof(union sockaddr_union));
|
|
|
|
dst.sa.sa_len = sizeof(struct sockaddr_in);
|
|
|
|
dst.sa.sa_family = AF_INET;
|
|
|
|
dst.sin.sin_addr = (direction == IPSEC_DIR_INBOUND) ?
|
|
|
|
ip->ip_src : ip->ip_dst;
|
2004-02-12 20:12:48 +00:00
|
|
|
|
|
|
|
/* Look up an SADB entry which matches the address of the peer. */
|
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
|
|
|
sav = KEY_ALLOCSA(&dst, IPPROTO_TCP, htonl(TCP_SIG_SPI));
|
|
|
|
if (sav == NULL) {
|
|
|
|
printf("%s: SADB lookup failed for %s\n", __func__,
|
|
|
|
inet_ntoa(dst.sin.sin_addr));
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
2004-02-12 20:12:48 +00:00
|
|
|
MD5Init(&ctx);
|
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
|
|
|
ipovly = (struct ipovly *)ip;
|
|
|
|
th = (struct tcphdr *)((u_char *)ip + off0);
|
|
|
|
doff = off0 + sizeof(struct tcphdr) + optlen;
|
2004-02-12 20:12:48 +00:00
|
|
|
|
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
|
|
|
/*
|
|
|
|
* Step 1: Update MD5 hash with IP pseudo-header.
|
|
|
|
*
|
|
|
|
* XXX The ippseudo header MUST be digested in network byte order,
|
|
|
|
* or else we'll fail the regression test. Assume all fields we've
|
|
|
|
* been doing arithmetic on have been in host byte order.
|
|
|
|
* XXX One cannot depend on ipovly->ih_len here. When called from
|
|
|
|
* tcp_output(), the underlying ip_len member has not yet been set.
|
|
|
|
*/
|
|
|
|
ippseudo.ippseudo_src = ipovly->ih_src;
|
|
|
|
ippseudo.ippseudo_dst = ipovly->ih_dst;
|
|
|
|
ippseudo.ippseudo_pad = 0;
|
|
|
|
ippseudo.ippseudo_p = IPPROTO_TCP;
|
|
|
|
ippseudo.ippseudo_len = htons(len + sizeof(struct tcphdr) + optlen);
|
|
|
|
MD5Update(&ctx, (char *)&ippseudo, sizeof(struct ippseudo));
|
2004-02-12 20:12:48 +00:00
|
|
|
|
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
|
|
|
/*
|
|
|
|
* Step 2: Update MD5 hash with TCP header, excluding options.
|
|
|
|
* The TCP checksum must be set to zero.
|
|
|
|
*/
|
|
|
|
savecsum = th->th_sum;
|
|
|
|
th->th_sum = 0;
|
|
|
|
MD5Update(&ctx, (char *)th, sizeof(struct tcphdr));
|
|
|
|
th->th_sum = savecsum;
|
2004-02-12 20:12:48 +00:00
|
|
|
|
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
|
|
|
/*
|
|
|
|
* Step 3: Update MD5 hash with TCP segment data.
|
|
|
|
* Use m_apply() to avoid an early m_pullup().
|
|
|
|
*/
|
|
|
|
if (len > 0)
|
2004-02-13 18:21:45 +00:00
|
|
|
m_apply(m, doff, len, tcp_signature_apply, &ctx);
|
2004-02-12 20:12:48 +00:00
|
|
|
|
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
|
|
|
/*
|
|
|
|
* Step 4: Update MD5 hash with shared secret.
|
|
|
|
*/
|
|
|
|
MD5Update(&ctx, _KEYBUF(sav->key_auth), _KEYLEN(sav->key_auth));
|
|
|
|
MD5Final(buf, &ctx);
|
2004-02-12 20:12:48 +00:00
|
|
|
|
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
|
|
|
key_sa_recordxfer(sav, m);
|
|
|
|
KEY_FREESAV(&sav);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
#endif /* TCP_SIGNATURE */
|
2005-02-14 07:37:51 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
sysctl_drop(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
/* addrs[0] is a foreign socket, addrs[1] is a local one. */
|
|
|
|
struct sockaddr_storage addrs[2];
|
|
|
|
struct inpcb *inp;
|
|
|
|
struct tcpcb *tp;
|
|
|
|
struct sockaddr_in *fin, *lin;
|
|
|
|
#ifdef INET6
|
|
|
|
struct sockaddr_in6 *fin6, *lin6;
|
|
|
|
struct in6_addr f6, l6;
|
|
|
|
#endif
|
|
|
|
int error;
|
|
|
|
|
|
|
|
inp = NULL;
|
|
|
|
fin = lin = NULL;
|
|
|
|
#ifdef INET6
|
|
|
|
fin6 = lin6 = NULL;
|
|
|
|
#endif
|
|
|
|
error = 0;
|
|
|
|
|
|
|
|
if (req->oldptr != NULL || req->oldlen != 0)
|
|
|
|
return (EINVAL);
|
|
|
|
if (req->newptr == NULL)
|
|
|
|
return (EPERM);
|
|
|
|
if (req->newlen < sizeof(addrs))
|
|
|
|
return (ENOMEM);
|
|
|
|
error = SYSCTL_IN(req, &addrs, sizeof(addrs));
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
switch (addrs[0].ss_family) {
|
|
|
|
#ifdef INET6
|
|
|
|
case AF_INET6:
|
|
|
|
fin6 = (struct sockaddr_in6 *)&addrs[0];
|
|
|
|
lin6 = (struct sockaddr_in6 *)&addrs[1];
|
|
|
|
if (fin6->sin6_len != sizeof(struct sockaddr_in6) ||
|
|
|
|
lin6->sin6_len != sizeof(struct sockaddr_in6))
|
|
|
|
return (EINVAL);
|
|
|
|
if (IN6_IS_ADDR_V4MAPPED(&fin6->sin6_addr)) {
|
|
|
|
if (!IN6_IS_ADDR_V4MAPPED(&lin6->sin6_addr))
|
|
|
|
return (EINVAL);
|
|
|
|
in6_sin6_2_sin_in_sock((struct sockaddr *)&addrs[0]);
|
|
|
|
in6_sin6_2_sin_in_sock((struct sockaddr *)&addrs[1]);
|
|
|
|
fin = (struct sockaddr_in *)&addrs[0];
|
|
|
|
lin = (struct sockaddr_in *)&addrs[1];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
error = in6_embedscope(&f6, fin6, NULL, NULL);
|
|
|
|
if (error)
|
|
|
|
return (EINVAL);
|
|
|
|
error = in6_embedscope(&l6, lin6, NULL, NULL);
|
|
|
|
if (error)
|
|
|
|
return (EINVAL);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case AF_INET:
|
|
|
|
fin = (struct sockaddr_in *)&addrs[0];
|
|
|
|
lin = (struct sockaddr_in *)&addrs[1];
|
|
|
|
if (fin->sin_len != sizeof(struct sockaddr_in) ||
|
|
|
|
lin->sin_len != sizeof(struct sockaddr_in))
|
|
|
|
return (EINVAL);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
INP_INFO_WLOCK(&tcbinfo);
|
|
|
|
switch (addrs[0].ss_family) {
|
|
|
|
#ifdef INET6
|
|
|
|
case AF_INET6:
|
|
|
|
inp = in6_pcblookup_hash(&tcbinfo, &f6, fin6->sin6_port,
|
|
|
|
&l6, lin6->sin6_port, 0, NULL);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case AF_INET:
|
|
|
|
inp = in_pcblookup_hash(&tcbinfo, fin->sin_addr, fin->sin_port,
|
|
|
|
lin->sin_addr, lin->sin_port, 0, NULL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (inp != NULL) {
|
|
|
|
INP_LOCK(inp);
|
|
|
|
if ((tp = intotcpcb(inp)) &&
|
|
|
|
((inp->inp_socket->so_options & SO_ACCEPTCONN) == 0)) {
|
|
|
|
tp = tcp_drop(tp, ECONNABORTED);
|
|
|
|
if (tp != NULL)
|
|
|
|
INP_UNLOCK(inp);
|
|
|
|
} else
|
|
|
|
INP_UNLOCK(inp);
|
|
|
|
} else
|
|
|
|
error = ESRCH;
|
|
|
|
INP_INFO_WUNLOCK(&tcbinfo);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
SYSCTL_PROC(_net_inet_tcp, TCPCTL_DROP, drop,
|
|
|
|
CTLTYPE_STRUCT|CTLFLAG_WR|CTLFLAG_SKIP, NULL,
|
|
|
|
0, sysctl_drop, "", "Drop TCP connection");
|