2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* INET An implementation of the TCP/IP protocol suite for the LINUX
|
|
|
|
* operating system. INET is implemented using the BSD Socket
|
|
|
|
* interface as the means of communication with the user level.
|
|
|
|
*
|
|
|
|
* Implementation of the Transmission Control Protocol(TCP).
|
|
|
|
*
|
|
|
|
* IPv4 specific functions
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* code split from:
|
|
|
|
* linux/ipv4/tcp.c
|
|
|
|
* linux/ipv4/tcp_input.c
|
|
|
|
* linux/ipv4/tcp_output.c
|
|
|
|
*
|
|
|
|
* See tcp.c for author information
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Changes:
|
|
|
|
* David S. Miller : New socket lookup architecture.
|
|
|
|
* This code is dedicated to John Dyson.
|
|
|
|
* David S. Miller : Change semantics of established hash,
|
|
|
|
* half is devoted to TIME_WAIT sockets
|
|
|
|
* and the rest go in the other half.
|
|
|
|
* Andi Kleen : Add support for syncookies and fixed
|
|
|
|
* some bugs: ip options weren't passed to
|
|
|
|
* the TCP layer, missed a check for an
|
|
|
|
* ACK bit.
|
|
|
|
* Andi Kleen : Implemented fast path mtu discovery.
|
|
|
|
* Fixed many serious bugs in the
|
2005-06-19 09:47:21 +04:00
|
|
|
* request_sock handling and moved
|
2005-04-17 02:20:36 +04:00
|
|
|
* most of it into the af independent code.
|
|
|
|
* Added tail drop and some other bugfixes.
|
2005-11-11 04:13:47 +03:00
|
|
|
* Added new listen semantics.
|
2005-04-17 02:20:36 +04:00
|
|
|
* Mike McLagan : Routing by source
|
|
|
|
* Juan Jose Ciarlante: ip_dynaddr bits
|
|
|
|
* Andi Kleen: various fixes.
|
|
|
|
* Vitaly E. Lavrov : Transparent proxy revived after year
|
|
|
|
* coma.
|
|
|
|
* Andi Kleen : Fix new listen.
|
|
|
|
* Andi Kleen : Fix accept error reporting.
|
|
|
|
* YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which
|
|
|
|
* Alexey Kuznetsov allow both IPv4 and IPv6 sockets to bind
|
|
|
|
* a single port at the same time.
|
|
|
|
*/
|
|
|
|
|
2012-03-12 11:03:32 +04:00
|
|
|
#define pr_fmt(fmt) "TCP: " fmt
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-12-30 10:04:08 +03:00
|
|
|
#include <linux/bottom_half.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/fcntl.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/random.h>
|
|
|
|
#include <linux/cache.h>
|
|
|
|
#include <linux/jhash.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/times.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 11:04:11 +03:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-09-12 14:01:34 +04:00
|
|
|
#include <net/net_namespace.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <net/icmp.h>
|
2005-08-10 06:59:20 +04:00
|
|
|
#include <net/inet_hashtables.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <net/tcp.h>
|
2005-08-16 09:18:02 +04:00
|
|
|
#include <net/transp_v6.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <net/ipv6.h>
|
|
|
|
#include <net/inet_common.h>
|
2005-12-14 10:25:19 +03:00
|
|
|
#include <net/timewait_sock.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <net/xfrm.h>
|
2006-05-24 05:05:53 +04:00
|
|
|
#include <net/netdma.h>
|
2011-08-04 07:50:44 +04:00
|
|
|
#include <net/secure_seq.h>
|
2011-12-12 01:47:04 +04:00
|
|
|
#include <net/tcp_memcontrol.h>
|
2013-07-10 18:13:17 +04:00
|
|
|
#include <net/busy_poll.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
#include <linux/inet.h>
|
|
|
|
#include <linux/ipv6.h>
|
|
|
|
#include <linux/stddef.h>
|
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
#include <linux/crypto.h>
|
|
|
|
#include <linux/scatterlist.h>
|
|
|
|
|
2006-09-23 01:15:41 +04:00
|
|
|
int sysctl_tcp_tw_reuse __read_mostly;
|
|
|
|
int sysctl_tcp_low_latency __read_mostly;
|
2010-07-10 01:22:10 +04:00
|
|
|
EXPORT_SYMBOL(sysctl_tcp_low_latency);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
2012-01-31 09:18:33 +04:00
|
|
|
static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
|
2011-10-24 10:46:04 +04:00
|
|
|
__be32 daddr, __be32 saddr, const struct tcphdr *th);
|
2006-11-15 06:07:45 +03:00
|
|
|
#endif
|
|
|
|
|
2008-11-20 11:40:07 +03:00
|
|
|
struct inet_hashinfo tcp_hashinfo;
|
2010-07-10 01:22:10 +04:00
|
|
|
EXPORT_SYMBOL(tcp_hashinfo);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2011-10-21 13:22:42 +04:00
|
|
|
static inline __u32 tcp_v4_init_sequence(const struct sk_buff *skb)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-04-21 09:47:35 +04:00
|
|
|
return secure_tcp_sequence_number(ip_hdr(skb)->daddr,
|
|
|
|
ip_hdr(skb)->saddr,
|
2007-04-11 08:04:22 +04:00
|
|
|
tcp_hdr(skb)->dest,
|
|
|
|
tcp_hdr(skb)->source);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2005-12-14 10:25:19 +03:00
|
|
|
int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
|
|
|
|
{
|
|
|
|
const struct tcp_timewait_sock *tcptw = tcp_twsk(sktw);
|
|
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
|
|
|
|
|
|
|
/* With PAWS, it is safe from the viewpoint
|
|
|
|
of data integrity. Even without PAWS it is safe provided sequence
|
|
|
|
spaces do not overlap i.e. at data rates <= 80Mbit/sec.
|
|
|
|
|
|
|
|
Actually, the idea is close to VJ's one, only timestamp cache is
|
|
|
|
held not per host, but per port pair and TW bucket is used as state
|
|
|
|
holder.
|
|
|
|
|
|
|
|
If TW bucket has been already destroyed we fall back to VJ's scheme
|
|
|
|
and use initial timestamp retrieved from peer table.
|
|
|
|
*/
|
|
|
|
if (tcptw->tw_ts_recent_stamp &&
|
|
|
|
(twp == NULL || (sysctl_tcp_tw_reuse &&
|
2007-03-05 03:12:44 +03:00
|
|
|
get_seconds() - tcptw->tw_ts_recent_stamp > 1))) {
|
2005-12-14 10:25:19 +03:00
|
|
|
tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
|
|
|
|
if (tp->write_seq == 0)
|
|
|
|
tp->write_seq = 1;
|
|
|
|
tp->rx_opt.ts_recent = tcptw->tw_ts_recent;
|
|
|
|
tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
|
|
|
|
sock_hold(sktw);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tcp_twsk_unique);
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* This will initiate an outgoing connection. */
|
|
|
|
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
|
|
|
{
|
2011-04-27 00:28:44 +04:00
|
|
|
struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct inet_sock *inet = inet_sk(sk);
|
|
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
2011-02-25 00:38:12 +03:00
|
|
|
__be16 orig_sport, orig_dport;
|
2006-09-27 08:27:15 +04:00
|
|
|
__be32 daddr, nexthop;
|
2011-05-07 03:11:19 +04:00
|
|
|
struct flowi4 *fl4;
|
2011-04-27 00:28:44 +04:00
|
|
|
struct rtable *rt;
|
2005-04-17 02:20:36 +04:00
|
|
|
int err;
|
2011-04-21 13:45:37 +04:00
|
|
|
struct ip_options_rcu *inet_opt;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (addr_len < sizeof(struct sockaddr_in))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (usin->sin_family != AF_INET)
|
|
|
|
return -EAFNOSUPPORT;
|
|
|
|
|
|
|
|
nexthop = daddr = usin->sin_addr.s_addr;
|
2011-04-21 13:45:37 +04:00
|
|
|
inet_opt = rcu_dereference_protected(inet->inet_opt,
|
|
|
|
sock_owned_by_user(sk));
|
|
|
|
if (inet_opt && inet_opt->opt.srr) {
|
2005-04-17 02:20:36 +04:00
|
|
|
if (!daddr)
|
|
|
|
return -EINVAL;
|
2011-04-21 13:45:37 +04:00
|
|
|
nexthop = inet_opt->opt.faddr;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2011-02-25 00:38:12 +03:00
|
|
|
orig_sport = inet->inet_sport;
|
|
|
|
orig_dport = usin->sin_port;
|
2011-05-07 03:11:19 +04:00
|
|
|
fl4 = &inet->cork.fl.u.ip4;
|
|
|
|
rt = ip_route_connect(fl4, nexthop, inet->inet_saddr,
|
2011-03-03 01:31:35 +03:00
|
|
|
RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
|
|
|
|
IPPROTO_TCP,
|
2013-08-28 10:04:14 +04:00
|
|
|
orig_sport, orig_dport, sk);
|
2011-03-03 01:31:35 +03:00
|
|
|
if (IS_ERR(rt)) {
|
|
|
|
err = PTR_ERR(rt);
|
|
|
|
if (err == -ENETUNREACH)
|
2013-11-28 21:51:22 +04:00
|
|
|
IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
|
2011-03-03 01:31:35 +03:00
|
|
|
return err;
|
2007-06-01 09:49:28 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
|
|
|
|
ip_rt_put(rt);
|
|
|
|
return -ENETUNREACH;
|
|
|
|
}
|
|
|
|
|
2011-04-21 13:45:37 +04:00
|
|
|
if (!inet_opt || !inet_opt->opt.srr)
|
2011-05-07 03:11:19 +04:00
|
|
|
daddr = fl4->daddr;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-10-15 10:30:45 +04:00
|
|
|
if (!inet->inet_saddr)
|
2011-05-07 03:11:19 +04:00
|
|
|
inet->inet_saddr = fl4->saddr;
|
2009-10-15 10:30:45 +04:00
|
|
|
inet->inet_rcv_saddr = inet->inet_saddr;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-10-15 10:30:45 +04:00
|
|
|
if (tp->rx_opt.ts_recent_stamp && inet->inet_daddr != daddr) {
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Reset inherited state */
|
|
|
|
tp->rx_opt.ts_recent = 0;
|
|
|
|
tp->rx_opt.ts_recent_stamp = 0;
|
2012-04-19 07:40:39 +04:00
|
|
|
if (likely(!tp->repair))
|
|
|
|
tp->write_seq = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2005-08-10 07:44:40 +04:00
|
|
|
if (tcp_death_row.sysctl_tw_recycle &&
|
2012-07-10 14:14:24 +04:00
|
|
|
!tp->rx_opt.ts_recent_stamp && fl4->daddr == daddr)
|
|
|
|
tcp_fetch_timewait_stamp(sk, &rt->dst);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-10-15 10:30:45 +04:00
|
|
|
inet->inet_dport = usin->sin_port;
|
|
|
|
inet->inet_daddr = daddr;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-12-14 10:26:10 +03:00
|
|
|
inet_csk(sk)->icsk_ext_hdr_len = 0;
|
2011-04-21 13:45:37 +04:00
|
|
|
if (inet_opt)
|
|
|
|
inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-11-10 12:51:18 +03:00
|
|
|
tp->rx_opt.mss_clamp = TCP_MSS_DEFAULT;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* Socket identity is still unknown (sport may be zero).
|
|
|
|
* However we set state to SYN-SENT and not releasing socket
|
|
|
|
* lock select source port, enter ourselves into the hash tables and
|
|
|
|
* complete initialization after this.
|
|
|
|
*/
|
|
|
|
tcp_set_state(sk, TCP_SYN_SENT);
|
2005-12-14 10:25:31 +03:00
|
|
|
err = inet_hash_connect(&tcp_death_row, sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (err)
|
|
|
|
goto failure;
|
|
|
|
|
2011-05-07 03:11:19 +04:00
|
|
|
rt = ip_route_newports(fl4, rt, orig_sport, orig_dport,
|
2011-03-03 01:31:35 +03:00
|
|
|
inet->inet_sport, inet->inet_dport, sk);
|
|
|
|
if (IS_ERR(rt)) {
|
|
|
|
err = PTR_ERR(rt);
|
|
|
|
rt = NULL;
|
2005-04-17 02:20:36 +04:00
|
|
|
goto failure;
|
2011-03-03 01:31:35 +03:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
/* OK, now commit destination to socket. */
|
2006-07-01 00:36:35 +04:00
|
|
|
sk->sk_gso_type = SKB_GSO_TCPV4;
|
2010-06-11 10:31:35 +04:00
|
|
|
sk_setup_caps(sk, &rt->dst);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-04-19 07:40:39 +04:00
|
|
|
if (!tp->write_seq && likely(!tp->repair))
|
2009-10-15 10:30:45 +04:00
|
|
|
tp->write_seq = secure_tcp_sequence_number(inet->inet_saddr,
|
|
|
|
inet->inet_daddr,
|
|
|
|
inet->inet_sport,
|
2005-04-17 02:20:36 +04:00
|
|
|
usin->sin_port);
|
|
|
|
|
2009-10-15 10:30:45 +04:00
|
|
|
inet->inet_id = tp->write_seq ^ jiffies;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-11-22 05:13:58 +04:00
|
|
|
err = tcp_connect(sk);
|
2012-04-19 07:40:39 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
rt = NULL;
|
|
|
|
if (err)
|
|
|
|
goto failure;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
failure:
|
2006-11-17 15:57:30 +03:00
|
|
|
/*
|
|
|
|
* This unhashes the socket and releases the local port,
|
|
|
|
* if necessary.
|
|
|
|
*/
|
2005-04-17 02:20:36 +04:00
|
|
|
tcp_set_state(sk, TCP_CLOSE);
|
|
|
|
ip_rt_put(rt);
|
|
|
|
sk->sk_route_caps = 0;
|
2009-10-15 10:30:45 +04:00
|
|
|
inet->inet_dport = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
return err;
|
|
|
|
}
|
2010-07-10 01:22:10 +04:00
|
|
|
EXPORT_SYMBOL(tcp_v4_connect);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/*
|
2012-07-23 11:48:52 +04:00
|
|
|
* This routine reacts to ICMP_FRAG_NEEDED mtu indications as defined in RFC1191.
|
|
|
|
* It can be called through tcp_release_cb() if socket was owned by user
|
|
|
|
* at the time tcp_v4_err() was called to handle ICMP message.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2012-07-23 11:48:52 +04:00
|
|
|
static void tcp_v4_mtu_reduced(struct sock *sk)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct dst_entry *dst;
|
|
|
|
struct inet_sock *inet = inet_sk(sk);
|
2012-07-23 11:48:52 +04:00
|
|
|
u32 mtu = tcp_sk(sk)->mtu_info;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-07-16 14:28:06 +04:00
|
|
|
dst = inet_csk_update_pmtu(sk, mtu);
|
|
|
|
if (!dst)
|
2005-04-17 02:20:36 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Something is about to be wrong... Remember soft error
|
|
|
|
* for the case, if this connection will not able to recover.
|
|
|
|
*/
|
|
|
|
if (mtu < dst_mtu(dst) && ip_dont_fragment(sk, dst))
|
|
|
|
sk->sk_err_soft = EMSGSIZE;
|
|
|
|
|
|
|
|
mtu = dst_mtu(dst);
|
|
|
|
|
|
|
|
if (inet->pmtudisc != IP_PMTUDISC_DONT &&
|
2013-11-05 05:24:17 +04:00
|
|
|
ip_sk_accept_pmtu(sk) &&
|
2005-12-14 10:26:10 +03:00
|
|
|
inet_csk(sk)->icsk_pmtu_cookie > mtu) {
|
2005-04-17 02:20:36 +04:00
|
|
|
tcp_sync_mss(sk, mtu);
|
|
|
|
|
|
|
|
/* Resend the TCP packet because it's
|
|
|
|
* clear that the old packet has been
|
|
|
|
* dropped. This is the new "fast" path mtu
|
|
|
|
* discovery.
|
|
|
|
*/
|
|
|
|
tcp_simple_retransmit(sk);
|
|
|
|
} /* else let the usual retransmit timer handle it */
|
|
|
|
}
|
|
|
|
|
2012-07-12 08:27:49 +04:00
|
|
|
static void do_redirect(struct sk_buff *skb, struct sock *sk)
|
|
|
|
{
|
|
|
|
struct dst_entry *dst = __sk_dst_check(sk, 0);
|
|
|
|
|
2012-07-12 11:41:25 +04:00
|
|
|
if (dst)
|
2012-07-17 14:29:28 +04:00
|
|
|
dst->ops->redirect(dst, sk, skb);
|
2012-07-12 08:27:49 +04:00
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* This routine is called by the ICMP module when it gets some
|
|
|
|
* sort of error condition. If err < 0 then the socket should
|
|
|
|
* be closed and the error returned to the user. If err > 0
|
|
|
|
* it's just the icmp type << 8 | icmp code. After adjustment
|
|
|
|
* header points to the first 8 bytes of the tcp header. We need
|
|
|
|
* to find the appropriate port.
|
|
|
|
*
|
|
|
|
* The locking strategy used here is very "optimistic". When
|
|
|
|
* someone else accesses the socket the ICMP is just dropped
|
|
|
|
* and for some paths there is no check at all.
|
|
|
|
* A more general error queue to queue errors for later handling
|
|
|
|
* is probably better.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2009-08-26 04:16:27 +04:00
|
|
|
void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2011-04-22 08:53:02 +04:00
|
|
|
const struct iphdr *iph = (const struct iphdr *)icmp_skb->data;
|
2009-08-26 04:16:27 +04:00
|
|
|
struct tcphdr *th = (struct tcphdr *)(icmp_skb->data + (iph->ihl << 2));
|
Revert Backoff [v3]: Revert RTO on ICMP destination unreachable
Here, an ICMP host/network unreachable message, whose payload fits to
TCP's SND.UNA, is taken as an indication that the RTO retransmission has
not been lost due to congestion, but because of a route failure
somewhere along the path.
With true congestion, a router won't trigger such a message and the
patched TCP will operate as standard TCP.
This patch reverts one RTO backoff, if an ICMP host/network unreachable
message, whose payload fits to TCP's SND.UNA, arrives.
Based on the new RTO, the retransmission timer is reset to reflect the
remaining time, or - if the revert clocked out the timer - a retransmission
is sent out immediately.
Backoffs are only reverted, if TCP is in RTO loss recovery, i.e. if
there have been retransmissions and reversible backoffs, already.
Changes from v2:
1) Renaming of skb in tcp_v4_err() moved to another patch.
2) Reintroduced tcp_bound_rto() and __tcp_set_rto().
3) Fixed code comments.
Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
Acked-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-08-26 04:16:31 +04:00
|
|
|
struct inet_connection_sock *icsk;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct tcp_sock *tp;
|
|
|
|
struct inet_sock *inet;
|
2009-08-26 04:16:27 +04:00
|
|
|
const int type = icmp_hdr(icmp_skb)->type;
|
|
|
|
const int code = icmp_hdr(icmp_skb)->code;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct sock *sk;
|
Revert Backoff [v3]: Revert RTO on ICMP destination unreachable
Here, an ICMP host/network unreachable message, whose payload fits to
TCP's SND.UNA, is taken as an indication that the RTO retransmission has
not been lost due to congestion, but because of a route failure
somewhere along the path.
With true congestion, a router won't trigger such a message and the
patched TCP will operate as standard TCP.
This patch reverts one RTO backoff, if an ICMP host/network unreachable
message, whose payload fits to TCP's SND.UNA, arrives.
Based on the new RTO, the retransmission timer is reset to reflect the
remaining time, or - if the revert clocked out the timer - a retransmission
is sent out immediately.
Backoffs are only reverted, if TCP is in RTO loss recovery, i.e. if
there have been retransmissions and reversible backoffs, already.
Changes from v2:
1) Renaming of skb in tcp_v4_err() moved to another patch.
2) Reintroduced tcp_bound_rto() and __tcp_set_rto().
3) Fixed code comments.
Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
Acked-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-08-26 04:16:31 +04:00
|
|
|
struct sk_buff *skb;
|
2012-08-31 16:29:13 +04:00
|
|
|
struct request_sock *req;
|
2005-04-17 02:20:36 +04:00
|
|
|
__u32 seq;
|
Revert Backoff [v3]: Revert RTO on ICMP destination unreachable
Here, an ICMP host/network unreachable message, whose payload fits to
TCP's SND.UNA, is taken as an indication that the RTO retransmission has
not been lost due to congestion, but because of a route failure
somewhere along the path.
With true congestion, a router won't trigger such a message and the
patched TCP will operate as standard TCP.
This patch reverts one RTO backoff, if an ICMP host/network unreachable
message, whose payload fits to TCP's SND.UNA, arrives.
Based on the new RTO, the retransmission timer is reset to reflect the
remaining time, or - if the revert clocked out the timer - a retransmission
is sent out immediately.
Backoffs are only reverted, if TCP is in RTO loss recovery, i.e. if
there have been retransmissions and reversible backoffs, already.
Changes from v2:
1) Renaming of skb in tcp_v4_err() moved to another patch.
2) Reintroduced tcp_bound_rto() and __tcp_set_rto().
3) Fixed code comments.
Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
Acked-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-08-26 04:16:31 +04:00
|
|
|
__u32 remaining;
|
2005-04-17 02:20:36 +04:00
|
|
|
int err;
|
2009-08-26 04:16:27 +04:00
|
|
|
struct net *net = dev_net(icmp_skb->dev);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-08-26 04:16:27 +04:00
|
|
|
if (icmp_skb->len < (iph->ihl << 2) + 8) {
|
2008-07-15 10:03:00 +04:00
|
|
|
ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
|
2005-04-17 02:20:36 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-07-15 10:01:40 +04:00
|
|
|
sk = inet_lookup(net, &tcp_hashinfo, iph->daddr, th->dest,
|
2009-08-26 04:16:27 +04:00
|
|
|
iph->saddr, th->source, inet_iif(icmp_skb));
|
2005-04-17 02:20:36 +04:00
|
|
|
if (!sk) {
|
2008-07-15 10:03:00 +04:00
|
|
|
ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
|
2005-04-17 02:20:36 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (sk->sk_state == TCP_TIME_WAIT) {
|
2006-10-11 06:41:46 +04:00
|
|
|
inet_twsk_put(inet_twsk(sk));
|
2005-04-17 02:20:36 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bh_lock_sock(sk);
|
|
|
|
/* If too many ICMPs get dropped on busy
|
|
|
|
* servers this needs to be solved differently.
|
2012-07-23 11:48:52 +04:00
|
|
|
* We do take care of PMTU discovery (RFC1191) special case :
|
|
|
|
* we can receive locally generated ICMP messages while socket is held.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2013-01-19 20:10:37 +04:00
|
|
|
if (sock_owned_by_user(sk)) {
|
|
|
|
if (!(type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED))
|
|
|
|
NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
if (sk->sk_state == TCP_CLOSE)
|
|
|
|
goto out;
|
|
|
|
|
2010-03-18 14:27:32 +03:00
|
|
|
if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) {
|
|
|
|
NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
Revert Backoff [v3]: Revert RTO on ICMP destination unreachable
Here, an ICMP host/network unreachable message, whose payload fits to
TCP's SND.UNA, is taken as an indication that the RTO retransmission has
not been lost due to congestion, but because of a route failure
somewhere along the path.
With true congestion, a router won't trigger such a message and the
patched TCP will operate as standard TCP.
This patch reverts one RTO backoff, if an ICMP host/network unreachable
message, whose payload fits to TCP's SND.UNA, arrives.
Based on the new RTO, the retransmission timer is reset to reflect the
remaining time, or - if the revert clocked out the timer - a retransmission
is sent out immediately.
Backoffs are only reverted, if TCP is in RTO loss recovery, i.e. if
there have been retransmissions and reversible backoffs, already.
Changes from v2:
1) Renaming of skb in tcp_v4_err() moved to another patch.
2) Reintroduced tcp_bound_rto() and __tcp_set_rto().
3) Fixed code comments.
Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
Acked-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-08-26 04:16:31 +04:00
|
|
|
icsk = inet_csk(sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
tp = tcp_sk(sk);
|
2012-08-31 16:29:13 +04:00
|
|
|
req = tp->fastopen_rsk;
|
2005-04-17 02:20:36 +04:00
|
|
|
seq = ntohl(th->seq);
|
|
|
|
if (sk->sk_state != TCP_LISTEN &&
|
2012-08-31 16:29:13 +04:00
|
|
|
!between(seq, tp->snd_una, tp->snd_nxt) &&
|
|
|
|
(req == NULL || seq != tcp_rsk(req)->snt_isn)) {
|
|
|
|
/* For a Fast Open socket, allow seq to be snt_isn. */
|
2008-07-17 07:31:16 +04:00
|
|
|
NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (type) {
|
2012-07-12 08:27:49 +04:00
|
|
|
case ICMP_REDIRECT:
|
|
|
|
do_redirect(icmp_skb, sk);
|
|
|
|
goto out;
|
2005-04-17 02:20:36 +04:00
|
|
|
case ICMP_SOURCE_QUENCH:
|
|
|
|
/* Just silently ignore these. */
|
|
|
|
goto out;
|
|
|
|
case ICMP_PARAMETERPROB:
|
|
|
|
err = EPROTO;
|
|
|
|
break;
|
|
|
|
case ICMP_DEST_UNREACH:
|
|
|
|
if (code > NR_ICMP_UNREACH)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
|
2013-03-18 11:01:28 +04:00
|
|
|
/* We are not interested in TCP_LISTEN and open_requests
|
|
|
|
* (SYN-ACKs send out by Linux are always <576bytes so
|
|
|
|
* they should go through unfragmented).
|
|
|
|
*/
|
|
|
|
if (sk->sk_state == TCP_LISTEN)
|
|
|
|
goto out;
|
|
|
|
|
2012-07-23 11:48:52 +04:00
|
|
|
tp->mtu_info = info;
|
tcp: fix possible socket refcount problem
Commit 6f458dfb40 (tcp: improve latencies of timer triggered events)
added bug leading to following trace :
[ 2866.131281] IPv4: Attempt to release TCP socket in state 1 ffff880019ec0000
[ 2866.131726]
[ 2866.132188] =========================
[ 2866.132281] [ BUG: held lock freed! ]
[ 2866.132281] 3.6.0-rc1+ #622 Not tainted
[ 2866.132281] -------------------------
[ 2866.132281] kworker/0:1/652 is freeing memory ffff880019ec0000-ffff880019ec0a1f, with a lock still held there!
[ 2866.132281] (sk_lock-AF_INET-RPC){+.+...}, at: [<ffffffff81903619>] tcp_sendmsg+0x29/0xcc6
[ 2866.132281] 4 locks held by kworker/0:1/652:
[ 2866.132281] #0: (rpciod){.+.+.+}, at: [<ffffffff81083567>] process_one_work+0x1de/0x47f
[ 2866.132281] #1: ((&task->u.tk_work)){+.+.+.}, at: [<ffffffff81083567>] process_one_work+0x1de/0x47f
[ 2866.132281] #2: (sk_lock-AF_INET-RPC){+.+...}, at: [<ffffffff81903619>] tcp_sendmsg+0x29/0xcc6
[ 2866.132281] #3: (&icsk->icsk_retransmit_timer){+.-...}, at: [<ffffffff81078017>] run_timer_softirq+0x1ad/0x35f
[ 2866.132281]
[ 2866.132281] stack backtrace:
[ 2866.132281] Pid: 652, comm: kworker/0:1 Not tainted 3.6.0-rc1+ #622
[ 2866.132281] Call Trace:
[ 2866.132281] <IRQ> [<ffffffff810bc527>] debug_check_no_locks_freed+0x112/0x159
[ 2866.132281] [<ffffffff818a0839>] ? __sk_free+0xfd/0x114
[ 2866.132281] [<ffffffff811549fa>] kmem_cache_free+0x6b/0x13a
[ 2866.132281] [<ffffffff818a0839>] __sk_free+0xfd/0x114
[ 2866.132281] [<ffffffff818a08c0>] sk_free+0x1c/0x1e
[ 2866.132281] [<ffffffff81911e1c>] tcp_write_timer+0x51/0x56
[ 2866.132281] [<ffffffff81078082>] run_timer_softirq+0x218/0x35f
[ 2866.132281] [<ffffffff81078017>] ? run_timer_softirq+0x1ad/0x35f
[ 2866.132281] [<ffffffff810f5831>] ? rb_commit+0x58/0x85
[ 2866.132281] [<ffffffff81911dcb>] ? tcp_write_timer_handler+0x148/0x148
[ 2866.132281] [<ffffffff81070bd6>] __do_softirq+0xcb/0x1f9
[ 2866.132281] [<ffffffff81a0a00c>] ? _raw_spin_unlock+0x29/0x2e
[ 2866.132281] [<ffffffff81a1227c>] call_softirq+0x1c/0x30
[ 2866.132281] [<ffffffff81039f38>] do_softirq+0x4a/0xa6
[ 2866.132281] [<ffffffff81070f2b>] irq_exit+0x51/0xad
[ 2866.132281] [<ffffffff81a129cd>] do_IRQ+0x9d/0xb4
[ 2866.132281] [<ffffffff81a0a3ef>] common_interrupt+0x6f/0x6f
[ 2866.132281] <EOI> [<ffffffff8109d006>] ? sched_clock_cpu+0x58/0xd1
[ 2866.132281] [<ffffffff81a0a172>] ? _raw_spin_unlock_irqrestore+0x4c/0x56
[ 2866.132281] [<ffffffff81078692>] mod_timer+0x178/0x1a9
[ 2866.132281] [<ffffffff818a00aa>] sk_reset_timer+0x19/0x26
[ 2866.132281] [<ffffffff8190b2cc>] tcp_rearm_rto+0x99/0xa4
[ 2866.132281] [<ffffffff8190dfba>] tcp_event_new_data_sent+0x6e/0x70
[ 2866.132281] [<ffffffff8190f7ea>] tcp_write_xmit+0x7de/0x8e4
[ 2866.132281] [<ffffffff818a565d>] ? __alloc_skb+0xa0/0x1a1
[ 2866.132281] [<ffffffff8190f952>] __tcp_push_pending_frames+0x2e/0x8a
[ 2866.132281] [<ffffffff81904122>] tcp_sendmsg+0xb32/0xcc6
[ 2866.132281] [<ffffffff819229c2>] inet_sendmsg+0xaa/0xd5
[ 2866.132281] [<ffffffff81922918>] ? inet_autobind+0x5f/0x5f
[ 2866.132281] [<ffffffff810ee7f1>] ? trace_clock_local+0x9/0xb
[ 2866.132281] [<ffffffff8189adab>] sock_sendmsg+0xa3/0xc4
[ 2866.132281] [<ffffffff810f5de6>] ? rb_reserve_next_event+0x26f/0x2d5
[ 2866.132281] [<ffffffff8103e6a9>] ? native_sched_clock+0x29/0x6f
[ 2866.132281] [<ffffffff8103e6f8>] ? sched_clock+0x9/0xd
[ 2866.132281] [<ffffffff810ee7f1>] ? trace_clock_local+0x9/0xb
[ 2866.132281] [<ffffffff8189ae03>] kernel_sendmsg+0x37/0x43
[ 2866.132281] [<ffffffff8199ce49>] xs_send_kvec+0x77/0x80
[ 2866.132281] [<ffffffff8199cec1>] xs_sendpages+0x6f/0x1a0
[ 2866.132281] [<ffffffff8107826d>] ? try_to_del_timer_sync+0x55/0x61
[ 2866.132281] [<ffffffff8199d0d2>] xs_tcp_send_request+0x55/0xf1
[ 2866.132281] [<ffffffff8199bb90>] xprt_transmit+0x89/0x1db
[ 2866.132281] [<ffffffff81999bcd>] ? call_connect+0x3c/0x3c
[ 2866.132281] [<ffffffff81999d92>] call_transmit+0x1c5/0x20e
[ 2866.132281] [<ffffffff819a0d55>] __rpc_execute+0x6f/0x225
[ 2866.132281] [<ffffffff81999bcd>] ? call_connect+0x3c/0x3c
[ 2866.132281] [<ffffffff819a0f33>] rpc_async_schedule+0x28/0x34
[ 2866.132281] [<ffffffff810835d6>] process_one_work+0x24d/0x47f
[ 2866.132281] [<ffffffff81083567>] ? process_one_work+0x1de/0x47f
[ 2866.132281] [<ffffffff819a0f0b>] ? __rpc_execute+0x225/0x225
[ 2866.132281] [<ffffffff81083a6d>] worker_thread+0x236/0x317
[ 2866.132281] [<ffffffff81083837>] ? process_scheduled_works+0x2f/0x2f
[ 2866.132281] [<ffffffff8108b7b8>] kthread+0x9a/0xa2
[ 2866.132281] [<ffffffff81a12184>] kernel_thread_helper+0x4/0x10
[ 2866.132281] [<ffffffff81a0a4b0>] ? retint_restore_args+0x13/0x13
[ 2866.132281] [<ffffffff8108b71e>] ? __init_kthread_worker+0x5a/0x5a
[ 2866.132281] [<ffffffff81a12180>] ? gs_change+0x13/0x13
[ 2866.308506] IPv4: Attempt to release TCP socket in state 1 ffff880019ec0000
[ 2866.309689] =============================================================================
[ 2866.310254] BUG TCP (Not tainted): Object already free
[ 2866.310254] -----------------------------------------------------------------------------
[ 2866.310254]
The bug comes from the fact that timer set in sk_reset_timer() can run
before we actually do the sock_hold(). socket refcount reaches zero and
we free the socket too soon.
timer handler is not allowed to reduce socket refcnt if socket is owned
by the user, or we need to change sk_reset_timer() implementation.
We should take a reference on the socket in case TCP_DELACK_TIMER_DEFERRED
or TCP_DELACK_TIMER_DEFERRED bit are set in tsq_flags
Also fix a typo in tcp_delack_timer(), where TCP_WRITE_TIMER_DEFERRED
was used instead of TCP_DELACK_TIMER_DEFERRED.
For consistency, use same socket refcount change for TCP_MTU_REDUCED_DEFERRED,
even if not fired from a timer.
Reported-by: Fengguang Wu <fengguang.wu@intel.com>
Tested-by: Fengguang Wu <fengguang.wu@intel.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-08-20 04:22:46 +04:00
|
|
|
if (!sock_owned_by_user(sk)) {
|
2012-07-23 11:48:52 +04:00
|
|
|
tcp_v4_mtu_reduced(sk);
|
tcp: fix possible socket refcount problem
Commit 6f458dfb40 (tcp: improve latencies of timer triggered events)
added bug leading to following trace :
[ 2866.131281] IPv4: Attempt to release TCP socket in state 1 ffff880019ec0000
[ 2866.131726]
[ 2866.132188] =========================
[ 2866.132281] [ BUG: held lock freed! ]
[ 2866.132281] 3.6.0-rc1+ #622 Not tainted
[ 2866.132281] -------------------------
[ 2866.132281] kworker/0:1/652 is freeing memory ffff880019ec0000-ffff880019ec0a1f, with a lock still held there!
[ 2866.132281] (sk_lock-AF_INET-RPC){+.+...}, at: [<ffffffff81903619>] tcp_sendmsg+0x29/0xcc6
[ 2866.132281] 4 locks held by kworker/0:1/652:
[ 2866.132281] #0: (rpciod){.+.+.+}, at: [<ffffffff81083567>] process_one_work+0x1de/0x47f
[ 2866.132281] #1: ((&task->u.tk_work)){+.+.+.}, at: [<ffffffff81083567>] process_one_work+0x1de/0x47f
[ 2866.132281] #2: (sk_lock-AF_INET-RPC){+.+...}, at: [<ffffffff81903619>] tcp_sendmsg+0x29/0xcc6
[ 2866.132281] #3: (&icsk->icsk_retransmit_timer){+.-...}, at: [<ffffffff81078017>] run_timer_softirq+0x1ad/0x35f
[ 2866.132281]
[ 2866.132281] stack backtrace:
[ 2866.132281] Pid: 652, comm: kworker/0:1 Not tainted 3.6.0-rc1+ #622
[ 2866.132281] Call Trace:
[ 2866.132281] <IRQ> [<ffffffff810bc527>] debug_check_no_locks_freed+0x112/0x159
[ 2866.132281] [<ffffffff818a0839>] ? __sk_free+0xfd/0x114
[ 2866.132281] [<ffffffff811549fa>] kmem_cache_free+0x6b/0x13a
[ 2866.132281] [<ffffffff818a0839>] __sk_free+0xfd/0x114
[ 2866.132281] [<ffffffff818a08c0>] sk_free+0x1c/0x1e
[ 2866.132281] [<ffffffff81911e1c>] tcp_write_timer+0x51/0x56
[ 2866.132281] [<ffffffff81078082>] run_timer_softirq+0x218/0x35f
[ 2866.132281] [<ffffffff81078017>] ? run_timer_softirq+0x1ad/0x35f
[ 2866.132281] [<ffffffff810f5831>] ? rb_commit+0x58/0x85
[ 2866.132281] [<ffffffff81911dcb>] ? tcp_write_timer_handler+0x148/0x148
[ 2866.132281] [<ffffffff81070bd6>] __do_softirq+0xcb/0x1f9
[ 2866.132281] [<ffffffff81a0a00c>] ? _raw_spin_unlock+0x29/0x2e
[ 2866.132281] [<ffffffff81a1227c>] call_softirq+0x1c/0x30
[ 2866.132281] [<ffffffff81039f38>] do_softirq+0x4a/0xa6
[ 2866.132281] [<ffffffff81070f2b>] irq_exit+0x51/0xad
[ 2866.132281] [<ffffffff81a129cd>] do_IRQ+0x9d/0xb4
[ 2866.132281] [<ffffffff81a0a3ef>] common_interrupt+0x6f/0x6f
[ 2866.132281] <EOI> [<ffffffff8109d006>] ? sched_clock_cpu+0x58/0xd1
[ 2866.132281] [<ffffffff81a0a172>] ? _raw_spin_unlock_irqrestore+0x4c/0x56
[ 2866.132281] [<ffffffff81078692>] mod_timer+0x178/0x1a9
[ 2866.132281] [<ffffffff818a00aa>] sk_reset_timer+0x19/0x26
[ 2866.132281] [<ffffffff8190b2cc>] tcp_rearm_rto+0x99/0xa4
[ 2866.132281] [<ffffffff8190dfba>] tcp_event_new_data_sent+0x6e/0x70
[ 2866.132281] [<ffffffff8190f7ea>] tcp_write_xmit+0x7de/0x8e4
[ 2866.132281] [<ffffffff818a565d>] ? __alloc_skb+0xa0/0x1a1
[ 2866.132281] [<ffffffff8190f952>] __tcp_push_pending_frames+0x2e/0x8a
[ 2866.132281] [<ffffffff81904122>] tcp_sendmsg+0xb32/0xcc6
[ 2866.132281] [<ffffffff819229c2>] inet_sendmsg+0xaa/0xd5
[ 2866.132281] [<ffffffff81922918>] ? inet_autobind+0x5f/0x5f
[ 2866.132281] [<ffffffff810ee7f1>] ? trace_clock_local+0x9/0xb
[ 2866.132281] [<ffffffff8189adab>] sock_sendmsg+0xa3/0xc4
[ 2866.132281] [<ffffffff810f5de6>] ? rb_reserve_next_event+0x26f/0x2d5
[ 2866.132281] [<ffffffff8103e6a9>] ? native_sched_clock+0x29/0x6f
[ 2866.132281] [<ffffffff8103e6f8>] ? sched_clock+0x9/0xd
[ 2866.132281] [<ffffffff810ee7f1>] ? trace_clock_local+0x9/0xb
[ 2866.132281] [<ffffffff8189ae03>] kernel_sendmsg+0x37/0x43
[ 2866.132281] [<ffffffff8199ce49>] xs_send_kvec+0x77/0x80
[ 2866.132281] [<ffffffff8199cec1>] xs_sendpages+0x6f/0x1a0
[ 2866.132281] [<ffffffff8107826d>] ? try_to_del_timer_sync+0x55/0x61
[ 2866.132281] [<ffffffff8199d0d2>] xs_tcp_send_request+0x55/0xf1
[ 2866.132281] [<ffffffff8199bb90>] xprt_transmit+0x89/0x1db
[ 2866.132281] [<ffffffff81999bcd>] ? call_connect+0x3c/0x3c
[ 2866.132281] [<ffffffff81999d92>] call_transmit+0x1c5/0x20e
[ 2866.132281] [<ffffffff819a0d55>] __rpc_execute+0x6f/0x225
[ 2866.132281] [<ffffffff81999bcd>] ? call_connect+0x3c/0x3c
[ 2866.132281] [<ffffffff819a0f33>] rpc_async_schedule+0x28/0x34
[ 2866.132281] [<ffffffff810835d6>] process_one_work+0x24d/0x47f
[ 2866.132281] [<ffffffff81083567>] ? process_one_work+0x1de/0x47f
[ 2866.132281] [<ffffffff819a0f0b>] ? __rpc_execute+0x225/0x225
[ 2866.132281] [<ffffffff81083a6d>] worker_thread+0x236/0x317
[ 2866.132281] [<ffffffff81083837>] ? process_scheduled_works+0x2f/0x2f
[ 2866.132281] [<ffffffff8108b7b8>] kthread+0x9a/0xa2
[ 2866.132281] [<ffffffff81a12184>] kernel_thread_helper+0x4/0x10
[ 2866.132281] [<ffffffff81a0a4b0>] ? retint_restore_args+0x13/0x13
[ 2866.132281] [<ffffffff8108b71e>] ? __init_kthread_worker+0x5a/0x5a
[ 2866.132281] [<ffffffff81a12180>] ? gs_change+0x13/0x13
[ 2866.308506] IPv4: Attempt to release TCP socket in state 1 ffff880019ec0000
[ 2866.309689] =============================================================================
[ 2866.310254] BUG TCP (Not tainted): Object already free
[ 2866.310254] -----------------------------------------------------------------------------
[ 2866.310254]
The bug comes from the fact that timer set in sk_reset_timer() can run
before we actually do the sock_hold(). socket refcount reaches zero and
we free the socket too soon.
timer handler is not allowed to reduce socket refcnt if socket is owned
by the user, or we need to change sk_reset_timer() implementation.
We should take a reference on the socket in case TCP_DELACK_TIMER_DEFERRED
or TCP_DELACK_TIMER_DEFERRED bit are set in tsq_flags
Also fix a typo in tcp_delack_timer(), where TCP_WRITE_TIMER_DEFERRED
was used instead of TCP_DELACK_TIMER_DEFERRED.
For consistency, use same socket refcount change for TCP_MTU_REDUCED_DEFERRED,
even if not fired from a timer.
Reported-by: Fengguang Wu <fengguang.wu@intel.com>
Tested-by: Fengguang Wu <fengguang.wu@intel.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-08-20 04:22:46 +04:00
|
|
|
} else {
|
|
|
|
if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags))
|
|
|
|
sock_hold(sk);
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = icmp_err_convert[code].errno;
|
Revert Backoff [v3]: Revert RTO on ICMP destination unreachable
Here, an ICMP host/network unreachable message, whose payload fits to
TCP's SND.UNA, is taken as an indication that the RTO retransmission has
not been lost due to congestion, but because of a route failure
somewhere along the path.
With true congestion, a router won't trigger such a message and the
patched TCP will operate as standard TCP.
This patch reverts one RTO backoff, if an ICMP host/network unreachable
message, whose payload fits to TCP's SND.UNA, arrives.
Based on the new RTO, the retransmission timer is reset to reflect the
remaining time, or - if the revert clocked out the timer - a retransmission
is sent out immediately.
Backoffs are only reverted, if TCP is in RTO loss recovery, i.e. if
there have been retransmissions and reversible backoffs, already.
Changes from v2:
1) Renaming of skb in tcp_v4_err() moved to another patch.
2) Reintroduced tcp_bound_rto() and __tcp_set_rto().
3) Fixed code comments.
Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
Acked-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-08-26 04:16:31 +04:00
|
|
|
/* check if icmp_skb allows revert of backoff
|
|
|
|
* (see draft-zimmermann-tcp-lcd) */
|
|
|
|
if (code != ICMP_NET_UNREACH && code != ICMP_HOST_UNREACH)
|
|
|
|
break;
|
|
|
|
if (seq != tp->snd_una || !icsk->icsk_retransmits ||
|
|
|
|
!icsk->icsk_backoff)
|
|
|
|
break;
|
|
|
|
|
2012-08-31 16:29:13 +04:00
|
|
|
/* XXX (TFO) - revisit the following logic for TFO */
|
|
|
|
|
2010-11-13 00:35:00 +03:00
|
|
|
if (sock_owned_by_user(sk))
|
|
|
|
break;
|
|
|
|
|
Revert Backoff [v3]: Revert RTO on ICMP destination unreachable
Here, an ICMP host/network unreachable message, whose payload fits to
TCP's SND.UNA, is taken as an indication that the RTO retransmission has
not been lost due to congestion, but because of a route failure
somewhere along the path.
With true congestion, a router won't trigger such a message and the
patched TCP will operate as standard TCP.
This patch reverts one RTO backoff, if an ICMP host/network unreachable
message, whose payload fits to TCP's SND.UNA, arrives.
Based on the new RTO, the retransmission timer is reset to reflect the
remaining time, or - if the revert clocked out the timer - a retransmission
is sent out immediately.
Backoffs are only reverted, if TCP is in RTO loss recovery, i.e. if
there have been retransmissions and reversible backoffs, already.
Changes from v2:
1) Renaming of skb in tcp_v4_err() moved to another patch.
2) Reintroduced tcp_bound_rto() and __tcp_set_rto().
3) Fixed code comments.
Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
Acked-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-08-26 04:16:31 +04:00
|
|
|
icsk->icsk_backoff--;
|
2014-02-27 02:02:48 +04:00
|
|
|
inet_csk(sk)->icsk_rto = (tp->srtt_us ? __tcp_set_rto(tp) :
|
2011-06-08 15:08:38 +04:00
|
|
|
TCP_TIMEOUT_INIT) << icsk->icsk_backoff;
|
Revert Backoff [v3]: Revert RTO on ICMP destination unreachable
Here, an ICMP host/network unreachable message, whose payload fits to
TCP's SND.UNA, is taken as an indication that the RTO retransmission has
not been lost due to congestion, but because of a route failure
somewhere along the path.
With true congestion, a router won't trigger such a message and the
patched TCP will operate as standard TCP.
This patch reverts one RTO backoff, if an ICMP host/network unreachable
message, whose payload fits to TCP's SND.UNA, arrives.
Based on the new RTO, the retransmission timer is reset to reflect the
remaining time, or - if the revert clocked out the timer - a retransmission
is sent out immediately.
Backoffs are only reverted, if TCP is in RTO loss recovery, i.e. if
there have been retransmissions and reversible backoffs, already.
Changes from v2:
1) Renaming of skb in tcp_v4_err() moved to another patch.
2) Reintroduced tcp_bound_rto() and __tcp_set_rto().
3) Fixed code comments.
Signed-off-by: Damian Lukowski <damian@tvk.rwth-aachen.de>
Acked-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-08-26 04:16:31 +04:00
|
|
|
tcp_bound_rto(sk);
|
|
|
|
|
|
|
|
skb = tcp_write_queue_head(sk);
|
|
|
|
BUG_ON(!skb);
|
|
|
|
|
|
|
|
remaining = icsk->icsk_rto - min(icsk->icsk_rto,
|
|
|
|
tcp_time_stamp - TCP_SKB_CB(skb)->when);
|
|
|
|
|
|
|
|
if (remaining) {
|
|
|
|
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
|
|
|
|
remaining, TCP_RTO_MAX);
|
|
|
|
} else {
|
|
|
|
/* RTO revert clocked out retransmission.
|
|
|
|
* Will retransmit now */
|
|
|
|
tcp_retransmit_timer(sk);
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
case ICMP_TIME_EXCEEDED:
|
|
|
|
err = EHOSTUNREACH;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2012-08-31 16:29:13 +04:00
|
|
|
/* XXX (TFO) - if it's a TFO socket and has been accepted, rather
|
|
|
|
* than following the TCP_SYN_RECV case and closing the socket,
|
|
|
|
* we ignore the ICMP error and keep trying like a fully established
|
|
|
|
* socket. Is this the right thing to do?
|
|
|
|
*/
|
|
|
|
if (req && req->sk == NULL)
|
|
|
|
goto out;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
switch (sk->sk_state) {
|
2005-06-19 09:47:21 +04:00
|
|
|
struct request_sock *req, **prev;
|
2005-04-17 02:20:36 +04:00
|
|
|
case TCP_LISTEN:
|
|
|
|
if (sock_owned_by_user(sk))
|
|
|
|
goto out;
|
|
|
|
|
2005-08-10 07:10:42 +04:00
|
|
|
req = inet_csk_search_req(sk, &prev, th->dest,
|
|
|
|
iph->daddr, iph->saddr);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (!req)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* ICMPs are not backlogged, hence we cannot get
|
|
|
|
an established socket here.
|
|
|
|
*/
|
2008-07-26 08:43:18 +04:00
|
|
|
WARN_ON(req->sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
|
|
|
if (seq != tcp_rsk(req)->snt_isn) {
|
2008-07-17 07:31:16 +04:00
|
|
|
NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Still in SYN_RECV, just remove it silently.
|
|
|
|
* There is no good way to pass the error to the newly
|
|
|
|
* created socket, and POSIX does not want network
|
|
|
|
* errors returned from accept().
|
|
|
|
*/
|
2005-08-10 07:10:42 +04:00
|
|
|
inet_csk_reqsk_queue_drop(sk, req, prev);
|
2013-01-31 12:24:06 +04:00
|
|
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
case TCP_SYN_SENT:
|
|
|
|
case TCP_SYN_RECV: /* Cannot happen.
|
2012-08-31 16:29:13 +04:00
|
|
|
It can f.e. if SYNs crossed,
|
|
|
|
or Fast Open.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
if (!sock_owned_by_user(sk)) {
|
|
|
|
sk->sk_err = err;
|
|
|
|
|
|
|
|
sk->sk_error_report(sk);
|
|
|
|
|
|
|
|
tcp_done(sk);
|
|
|
|
} else {
|
|
|
|
sk->sk_err_soft = err;
|
|
|
|
}
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we've already connected we will keep trying
|
|
|
|
* until we time out, or the user gives up.
|
|
|
|
*
|
|
|
|
* rfc1122 4.2.3.9 allows to consider as hard errors
|
|
|
|
* only PROTO_UNREACH and PORT_UNREACH (well, FRAG_FAILED too,
|
|
|
|
* but it is obsoleted by pmtu discovery).
|
|
|
|
*
|
|
|
|
* Note, that in modern internet, where routing is unreliable
|
|
|
|
* and in each dark corner broken firewalls sit, sending random
|
|
|
|
* errors ordered by their masters even this two messages finally lose
|
|
|
|
* their original sense (even Linux sends invalid PORT_UNREACHs)
|
|
|
|
*
|
|
|
|
* Now we are in compliance with RFCs.
|
|
|
|
* --ANK (980905)
|
|
|
|
*/
|
|
|
|
|
|
|
|
inet = inet_sk(sk);
|
|
|
|
if (!sock_owned_by_user(sk) && inet->recverr) {
|
|
|
|
sk->sk_err = err;
|
|
|
|
sk->sk_error_report(sk);
|
|
|
|
} else { /* Only an error on timeout */
|
|
|
|
sk->sk_err_soft = err;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
bh_unlock_sock(sk);
|
|
|
|
sock_put(sk);
|
|
|
|
}
|
|
|
|
|
2013-06-07 09:11:46 +04:00
|
|
|
void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-04-11 08:04:22 +04:00
|
|
|
struct tcphdr *th = tcp_hdr(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-08-30 03:44:56 +04:00
|
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
2010-04-11 06:15:53 +04:00
|
|
|
th->check = ~tcp_v4_check(skb->len, saddr, daddr, 0);
|
2007-04-09 22:59:07 +04:00
|
|
|
skb->csum_start = skb_transport_header(skb) - skb->head;
|
2006-11-21 05:07:29 +03:00
|
|
|
skb->csum_offset = offsetof(struct tcphdr, check);
|
2005-04-17 02:20:36 +04:00
|
|
|
} else {
|
2010-04-11 06:15:53 +04:00
|
|
|
th->check = tcp_v4_check(skb->len, saddr, daddr,
|
2008-11-20 02:44:53 +03:00
|
|
|
csum_partial(th,
|
2005-04-17 02:20:36 +04:00
|
|
|
th->doff << 2,
|
|
|
|
skb->csum));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-11 06:15:53 +04:00
|
|
|
/* This routine computes an IPv4 TCP checksum. */
|
2010-04-11 06:15:55 +04:00
|
|
|
void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb)
|
2010-04-11 06:15:53 +04:00
|
|
|
{
|
2011-10-21 13:22:42 +04:00
|
|
|
const struct inet_sock *inet = inet_sk(sk);
|
2010-04-11 06:15:53 +04:00
|
|
|
|
|
|
|
__tcp_v4_send_check(skb, inet->inet_saddr, inet->inet_daddr);
|
|
|
|
}
|
2010-07-10 01:22:10 +04:00
|
|
|
EXPORT_SYMBOL(tcp_v4_send_check);
|
2010-04-11 06:15:53 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* This routine will send an RST to the other tcp.
|
|
|
|
*
|
|
|
|
* Someone asks: why I NEVER use socket parameters (TOS, TTL etc.)
|
|
|
|
* for reset.
|
|
|
|
* Answer: if a packet caused RST, it is not for a socket
|
|
|
|
* existing in our system, if it is matched to a socket,
|
|
|
|
* it is just duplicate segment or bug in other side's TCP.
|
|
|
|
* So that we build reply only basing on parameters
|
|
|
|
* arrived with segment.
|
|
|
|
* Exception: precedence violation. We do not implement it in any case.
|
|
|
|
*/
|
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2011-10-21 13:22:42 +04:00
|
|
|
const struct tcphdr *th = tcp_hdr(skb);
|
2006-11-15 06:07:45 +03:00
|
|
|
struct {
|
|
|
|
struct tcphdr th;
|
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
2006-11-15 07:51:49 +03:00
|
|
|
__be32 opt[(TCPOLEN_MD5SIG_ALIGNED >> 2)];
|
2006-11-15 06:07:45 +03:00
|
|
|
#endif
|
|
|
|
} rep;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ip_reply_arg arg;
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
|
|
|
struct tcp_md5sig_key *key;
|
2012-02-01 02:35:48 +04:00
|
|
|
const __u8 *hash_location = NULL;
|
|
|
|
unsigned char newhash[16];
|
|
|
|
int genhash;
|
|
|
|
struct sock *sk1 = NULL;
|
2006-11-15 06:07:45 +03:00
|
|
|
#endif
|
2008-07-17 07:20:58 +04:00
|
|
|
struct net *net;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* Never send a reset in response to a reset. */
|
|
|
|
if (th->rst)
|
|
|
|
return;
|
|
|
|
|
2009-06-02 09:14:27 +04:00
|
|
|
if (skb_rtable(skb)->rt_type != RTN_LOCAL)
|
2005-04-17 02:20:36 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Swap the send and the receive. */
|
2006-11-15 06:07:45 +03:00
|
|
|
memset(&rep, 0, sizeof(rep));
|
|
|
|
rep.th.dest = th->source;
|
|
|
|
rep.th.source = th->dest;
|
|
|
|
rep.th.doff = sizeof(struct tcphdr) / 4;
|
|
|
|
rep.th.rst = 1;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (th->ack) {
|
2006-11-15 06:07:45 +03:00
|
|
|
rep.th.seq = th->ack_seq;
|
2005-04-17 02:20:36 +04:00
|
|
|
} else {
|
2006-11-15 06:07:45 +03:00
|
|
|
rep.th.ack = 1;
|
|
|
|
rep.th.ack_seq = htonl(ntohl(th->seq) + th->syn + th->fin +
|
|
|
|
skb->len - (th->doff << 2));
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2006-11-17 15:57:30 +03:00
|
|
|
memset(&arg, 0, sizeof(arg));
|
2006-11-15 06:07:45 +03:00
|
|
|
arg.iov[0].iov_base = (unsigned char *)&rep;
|
|
|
|
arg.iov[0].iov_len = sizeof(rep.th);
|
|
|
|
|
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
2012-02-01 02:35:48 +04:00
|
|
|
hash_location = tcp_parse_md5sig_option(th);
|
|
|
|
if (!sk && hash_location) {
|
|
|
|
/*
|
|
|
|
* active side is lost. Try to find listening socket through
|
|
|
|
* source port, and then find md5 key through listening socket.
|
|
|
|
* we are not loose security here:
|
|
|
|
* Incoming packet is checked with md5 hash with finding key,
|
|
|
|
* no RST generated if md5 hash doesn't match.
|
|
|
|
*/
|
|
|
|
sk1 = __inet_lookup_listener(dev_net(skb_dst(skb)->dev),
|
2013-01-22 13:50:24 +04:00
|
|
|
&tcp_hashinfo, ip_hdr(skb)->saddr,
|
|
|
|
th->source, ip_hdr(skb)->daddr,
|
2012-02-01 02:35:48 +04:00
|
|
|
ntohs(th->source), inet_iif(skb));
|
|
|
|
/* don't send rst if it can't find key */
|
|
|
|
if (!sk1)
|
|
|
|
return;
|
|
|
|
rcu_read_lock();
|
|
|
|
key = tcp_md5_do_lookup(sk1, (union tcp_md5_addr *)
|
|
|
|
&ip_hdr(skb)->saddr, AF_INET);
|
|
|
|
if (!key)
|
|
|
|
goto release_sk1;
|
|
|
|
|
|
|
|
genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, NULL, skb);
|
|
|
|
if (genhash || memcmp(hash_location, newhash, 16) != 0)
|
|
|
|
goto release_sk1;
|
|
|
|
} else {
|
|
|
|
key = sk ? tcp_md5_do_lookup(sk, (union tcp_md5_addr *)
|
|
|
|
&ip_hdr(skb)->saddr,
|
|
|
|
AF_INET) : NULL;
|
|
|
|
}
|
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
if (key) {
|
|
|
|
rep.opt[0] = htonl((TCPOPT_NOP << 24) |
|
|
|
|
(TCPOPT_NOP << 16) |
|
|
|
|
(TCPOPT_MD5SIG << 8) |
|
|
|
|
TCPOLEN_MD5SIG);
|
|
|
|
/* Update length and the length the header thinks exists */
|
|
|
|
arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED;
|
|
|
|
rep.th.doff = arg.iov[0].iov_len / 4;
|
|
|
|
|
2008-07-19 11:01:42 +04:00
|
|
|
tcp_v4_md5_hash_hdr((__u8 *) &rep.opt[1],
|
2008-10-10 01:37:47 +04:00
|
|
|
key, ip_hdr(skb)->saddr,
|
|
|
|
ip_hdr(skb)->daddr, &rep.th);
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
#endif
|
2007-04-21 09:47:35 +04:00
|
|
|
arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
|
|
|
|
ip_hdr(skb)->saddr, /* XXX */
|
2008-10-08 22:34:06 +04:00
|
|
|
arg.iov[0].iov_len, IPPROTO_TCP, 0);
|
2005-04-17 02:20:36 +04:00
|
|
|
arg.csumoffset = offsetof(struct tcphdr, check) / 2;
|
2008-10-01 18:41:00 +04:00
|
|
|
arg.flags = (sk && inet_sk(sk)->transparent) ? IP_REPLY_ARG_NOSRCCHECK : 0;
|
2012-02-04 16:38:09 +04:00
|
|
|
/* When socket is gone, all binding information is lost.
|
2012-10-12 08:34:17 +04:00
|
|
|
* routing might fail in this case. No choice here, if we choose to force
|
|
|
|
* input interface, we will misroute in case of asymmetric route.
|
2012-02-04 16:38:09 +04:00
|
|
|
*/
|
2012-10-12 08:34:17 +04:00
|
|
|
if (sk)
|
|
|
|
arg.bound_dev_if = sk->sk_bound_dev_if;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-06-02 09:19:30 +04:00
|
|
|
net = dev_net(skb_dst(skb)->dev);
|
2011-10-24 11:06:21 +04:00
|
|
|
arg.tos = ip_hdr(skb)->tos;
|
2012-07-19 11:34:03 +04:00
|
|
|
ip_send_unicast_reply(net, skb, ip_hdr(skb)->saddr,
|
2012-06-28 14:21:41 +04:00
|
|
|
ip_hdr(skb)->daddr, &arg, arg.iov[0].iov_len);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-07-17 07:22:25 +04:00
|
|
|
TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
|
|
|
|
TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS);
|
2012-02-01 02:35:48 +04:00
|
|
|
|
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
|
|
|
release_sk1:
|
|
|
|
if (sk1) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
sock_put(sk1);
|
|
|
|
}
|
|
|
|
#endif
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The code following below sending ACKs in SYN-RECV and TIME-WAIT states
|
|
|
|
outside socket context is ugly, certainly. What can I do?
|
|
|
|
*/
|
|
|
|
|
2008-04-18 07:45:16 +04:00
|
|
|
static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
|
2013-02-11 09:50:19 +04:00
|
|
|
u32 win, u32 tsval, u32 tsecr, int oif,
|
2008-10-01 18:41:00 +04:00
|
|
|
struct tcp_md5sig_key *key,
|
2011-10-24 11:06:21 +04:00
|
|
|
int reply_flags, u8 tos)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2011-10-21 13:22:42 +04:00
|
|
|
const struct tcphdr *th = tcp_hdr(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
struct {
|
|
|
|
struct tcphdr th;
|
2006-11-15 07:51:49 +03:00
|
|
|
__be32 opt[(TCPOLEN_TSTAMP_ALIGNED >> 2)
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
2006-11-15 07:51:49 +03:00
|
|
|
+ (TCPOLEN_MD5SIG_ALIGNED >> 2)
|
2006-11-15 06:07:45 +03:00
|
|
|
#endif
|
|
|
|
];
|
2005-04-17 02:20:36 +04:00
|
|
|
} rep;
|
|
|
|
struct ip_reply_arg arg;
|
2009-06-02 09:19:30 +04:00
|
|
|
struct net *net = dev_net(skb_dst(skb)->dev);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
memset(&rep.th, 0, sizeof(struct tcphdr));
|
2006-11-17 15:57:30 +03:00
|
|
|
memset(&arg, 0, sizeof(arg));
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
arg.iov[0].iov_base = (unsigned char *)&rep;
|
|
|
|
arg.iov[0].iov_len = sizeof(rep.th);
|
2013-02-11 09:50:19 +04:00
|
|
|
if (tsecr) {
|
2006-11-15 06:07:45 +03:00
|
|
|
rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
|
|
|
|
(TCPOPT_TIMESTAMP << 8) |
|
|
|
|
TCPOLEN_TIMESTAMP);
|
2013-02-11 09:50:19 +04:00
|
|
|
rep.opt[1] = htonl(tsval);
|
|
|
|
rep.opt[2] = htonl(tsecr);
|
2007-01-09 11:11:15 +03:00
|
|
|
arg.iov[0].iov_len += TCPOLEN_TSTAMP_ALIGNED;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Swap the send and the receive. */
|
|
|
|
rep.th.dest = th->source;
|
|
|
|
rep.th.source = th->dest;
|
|
|
|
rep.th.doff = arg.iov[0].iov_len / 4;
|
|
|
|
rep.th.seq = htonl(seq);
|
|
|
|
rep.th.ack_seq = htonl(ack);
|
|
|
|
rep.th.ack = 1;
|
|
|
|
rep.th.window = htons(win);
|
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
|
|
|
if (key) {
|
2013-02-11 09:50:19 +04:00
|
|
|
int offset = (tsecr) ? 3 : 0;
|
2006-11-15 06:07:45 +03:00
|
|
|
|
|
|
|
rep.opt[offset++] = htonl((TCPOPT_NOP << 24) |
|
|
|
|
(TCPOPT_NOP << 16) |
|
|
|
|
(TCPOPT_MD5SIG << 8) |
|
|
|
|
TCPOLEN_MD5SIG);
|
|
|
|
arg.iov[0].iov_len += TCPOLEN_MD5SIG_ALIGNED;
|
|
|
|
rep.th.doff = arg.iov[0].iov_len/4;
|
|
|
|
|
2008-07-19 11:01:42 +04:00
|
|
|
tcp_v4_md5_hash_hdr((__u8 *) &rep.opt[offset],
|
2008-08-01 07:49:48 +04:00
|
|
|
key, ip_hdr(skb)->saddr,
|
|
|
|
ip_hdr(skb)->daddr, &rep.th);
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
#endif
|
2008-10-01 18:41:00 +04:00
|
|
|
arg.flags = reply_flags;
|
2007-04-21 09:47:35 +04:00
|
|
|
arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
|
|
|
|
ip_hdr(skb)->saddr, /* XXX */
|
2005-04-17 02:20:36 +04:00
|
|
|
arg.iov[0].iov_len, IPPROTO_TCP, 0);
|
|
|
|
arg.csumoffset = offsetof(struct tcphdr, check) / 2;
|
2008-04-18 07:45:16 +04:00
|
|
|
if (oif)
|
|
|
|
arg.bound_dev_if = oif;
|
2011-10-24 11:06:21 +04:00
|
|
|
arg.tos = tos;
|
2012-07-19 11:34:03 +04:00
|
|
|
ip_send_unicast_reply(net, skb, ip_hdr(skb)->saddr,
|
2012-06-28 14:21:41 +04:00
|
|
|
ip_hdr(skb)->daddr, &arg, arg.iov[0].iov_len);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-07-17 07:22:25 +04:00
|
|
|
TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
|
|
|
|
{
|
2005-08-10 07:09:30 +04:00
|
|
|
struct inet_timewait_sock *tw = inet_twsk(sk);
|
2006-11-15 06:07:45 +03:00
|
|
|
struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-04-18 07:45:16 +04:00
|
|
|
tcp_v4_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
|
2006-11-17 15:57:30 +03:00
|
|
|
tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
|
2013-02-11 09:50:19 +04:00
|
|
|
tcp_time_stamp + tcptw->tw_ts_offset,
|
2008-04-18 07:45:16 +04:00
|
|
|
tcptw->tw_ts_recent,
|
|
|
|
tw->tw_bound_dev_if,
|
2008-10-01 18:41:00 +04:00
|
|
|
tcp_twsk_md5_key(tcptw),
|
2011-10-24 11:06:21 +04:00
|
|
|
tw->tw_transparent ? IP_REPLY_ARG_NOSRCCHECK : 0,
|
|
|
|
tw->tw_tos
|
2008-04-18 07:45:16 +04:00
|
|
|
);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-08-10 07:09:30 +04:00
|
|
|
inet_twsk_put(tw);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2008-08-07 10:50:04 +04:00
|
|
|
static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
|
2006-11-17 15:57:30 +03:00
|
|
|
struct request_sock *req)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2012-08-31 16:29:13 +04:00
|
|
|
/* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
|
|
|
|
* sk->sk_state == TCP_SYN_RECV -> for Fast Open.
|
|
|
|
*/
|
|
|
|
tcp_v4_send_ack(skb, (sk->sk_state == TCP_LISTEN) ?
|
|
|
|
tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt,
|
|
|
|
tcp_rsk(req)->rcv_nxt, req->rcv_wnd,
|
2013-02-11 09:50:19 +04:00
|
|
|
tcp_time_stamp,
|
2008-04-18 07:45:16 +04:00
|
|
|
req->ts_recent,
|
|
|
|
0,
|
2012-01-31 09:18:33 +04:00
|
|
|
tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr,
|
|
|
|
AF_INET),
|
2011-10-24 11:06:21 +04:00
|
|
|
inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0,
|
|
|
|
ip_hdr(skb)->tos);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-02-18 09:29:19 +03:00
|
|
|
* Send a SYN-ACK after having received a SYN.
|
2005-06-19 09:47:21 +04:00
|
|
|
* This still operates on a request_sock only, not on a big
|
2005-04-17 02:20:36 +04:00
|
|
|
* socket.
|
|
|
|
*/
|
2010-01-18 06:09:39 +03:00
|
|
|
static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
|
|
|
|
struct request_sock *req,
|
2014-05-12 07:22:11 +04:00
|
|
|
u16 queue_mapping,
|
|
|
|
struct tcp_fastopen_cookie *foc)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
|
|
|
const struct inet_request_sock *ireq = inet_rsk(req);
|
2011-05-19 02:32:03 +04:00
|
|
|
struct flowi4 fl4;
|
2005-04-17 02:20:36 +04:00
|
|
|
int err = -1;
|
2013-12-23 10:37:28 +04:00
|
|
|
struct sk_buff *skb;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* First, grab a route. */
|
2012-07-18 01:02:46 +04:00
|
|
|
if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
|
2008-02-29 22:43:03 +03:00
|
|
|
return -1;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-05-12 07:22:11 +04:00
|
|
|
skb = tcp_make_synack(sk, dst, req, foc);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (skb) {
|
2013-10-10 02:21:29 +04:00
|
|
|
__tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-06-01 05:47:50 +04:00
|
|
|
skb_set_queue_mapping(skb, queue_mapping);
|
2013-10-10 02:21:29 +04:00
|
|
|
err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr,
|
|
|
|
ireq->ir_rmt_addr,
|
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
|
|
|
ireq->opt);
|
2006-11-14 16:21:36 +03:00
|
|
|
err = net_xmit_eval(err);
|
2012-09-22 08:18:55 +04:00
|
|
|
if (!tcp_rsk(req)->snt_synack && !err)
|
|
|
|
tcp_rsk(req)->snt_synack = tcp_time_stamp;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-03-17 12:23:34 +04:00
|
|
|
static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req)
|
2008-02-29 22:43:03 +03:00
|
|
|
{
|
2014-05-12 07:22:11 +04:00
|
|
|
int res = tcp_v4_send_synack(sk, NULL, req, 0, NULL);
|
2012-10-28 03:16:46 +04:00
|
|
|
|
2014-03-04 00:31:36 +04:00
|
|
|
if (!res) {
|
2012-10-28 03:16:46 +04:00
|
|
|
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
|
2014-03-04 00:31:36 +04:00
|
|
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
|
|
|
|
}
|
2012-10-28 03:16:46 +04:00
|
|
|
return res;
|
2008-02-29 22:43:03 +03:00
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
2005-06-19 09:47:21 +04:00
|
|
|
* IPv4 request_sock destructor.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2005-06-19 09:47:21 +04:00
|
|
|
static void tcp_v4_reqsk_destructor(struct request_sock *req)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2005-11-08 20:41:34 +03:00
|
|
|
kfree(inet_rsk(req)->opt);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2011-08-30 07:21:44 +04:00
|
|
|
/*
|
2012-05-17 03:15:34 +04:00
|
|
|
* Return true if a syncookie should be sent
|
2011-08-30 07:21:44 +04:00
|
|
|
*/
|
2012-05-17 03:15:34 +04:00
|
|
|
bool tcp_syn_flood_action(struct sock *sk,
|
2011-08-30 07:21:44 +04:00
|
|
|
const struct sk_buff *skb,
|
|
|
|
const char *proto)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2011-08-30 07:21:44 +04:00
|
|
|
const char *msg = "Dropping request";
|
2012-05-17 03:15:34 +04:00
|
|
|
bool want_cookie = false;
|
2011-08-30 07:21:44 +04:00
|
|
|
struct listen_sock *lopt;
|
|
|
|
|
2010-06-03 04:43:12 +04:00
|
|
|
#ifdef CONFIG_SYN_COOKIES
|
2011-08-30 07:21:44 +04:00
|
|
|
if (sysctl_tcp_syncookies) {
|
2010-06-03 04:43:12 +04:00
|
|
|
msg = "Sending cookies";
|
2012-05-17 03:15:34 +04:00
|
|
|
want_cookie = true;
|
2011-08-30 07:21:44 +04:00
|
|
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDOCOOKIES);
|
|
|
|
} else
|
2006-01-04 06:58:06 +03:00
|
|
|
#endif
|
2011-08-30 07:21:44 +04:00
|
|
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPREQQFULLDROP);
|
|
|
|
|
|
|
|
lopt = inet_csk(sk)->icsk_accept_queue.listen_opt;
|
2013-07-26 19:43:23 +04:00
|
|
|
if (!lopt->synflood_warned && sysctl_tcp_syncookies != 2) {
|
2011-08-30 07:21:44 +04:00
|
|
|
lopt->synflood_warned = 1;
|
2012-03-12 11:03:32 +04:00
|
|
|
pr_info("%s: Possible SYN flooding on port %d. %s. Check SNMP counters.\n",
|
2011-08-30 07:21:44 +04:00
|
|
|
proto, ntohs(tcp_hdr(skb)->dest), msg);
|
|
|
|
}
|
|
|
|
return want_cookie;
|
2010-06-03 04:43:12 +04:00
|
|
|
}
|
2011-08-30 07:21:44 +04:00
|
|
|
EXPORT_SYMBOL(tcp_syn_flood_action);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/*
|
2005-06-19 09:47:21 +04:00
|
|
|
* Save and compile IPv4 options into the request_sock if needed.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2012-09-26 15:59:09 +04:00
|
|
|
static struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2011-04-21 13:45:37 +04:00
|
|
|
const struct ip_options *opt = &(IPCB(skb)->opt);
|
|
|
|
struct ip_options_rcu *dopt = NULL;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (opt && opt->optlen) {
|
2011-04-21 13:45:37 +04:00
|
|
|
int opt_size = sizeof(*dopt) + opt->optlen;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
dopt = kmalloc(opt_size, GFP_ATOMIC);
|
|
|
|
if (dopt) {
|
2011-04-21 13:45:37 +04:00
|
|
|
if (ip_options_echo(&dopt->opt, skb)) {
|
2005-04-17 02:20:36 +04:00
|
|
|
kfree(dopt);
|
|
|
|
dopt = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dopt;
|
|
|
|
}
|
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
|
|
|
/*
|
|
|
|
* RFC2385 MD5 checksumming requires a mapping of
|
|
|
|
* IP address->MD5 Key.
|
|
|
|
* We need to maintain these in the sk structure.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Find the Key structure for an address. */
|
2012-01-31 09:18:33 +04:00
|
|
|
struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk,
|
|
|
|
const union tcp_md5_addr *addr,
|
|
|
|
int family)
|
2006-11-15 06:07:45 +03:00
|
|
|
{
|
|
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
2012-01-31 09:18:33 +04:00
|
|
|
struct tcp_md5sig_key *key;
|
|
|
|
unsigned int size = sizeof(struct in_addr);
|
2012-01-31 22:45:40 +04:00
|
|
|
struct tcp_md5sig_info *md5sig;
|
2006-11-15 06:07:45 +03:00
|
|
|
|
2012-01-31 22:45:40 +04:00
|
|
|
/* caller either holds rcu_read_lock() or socket lock */
|
|
|
|
md5sig = rcu_dereference_check(tp->md5sig_info,
|
2012-03-07 08:45:43 +04:00
|
|
|
sock_owned_by_user(sk) ||
|
|
|
|
lockdep_is_held(&sk->sk_lock.slock));
|
2012-01-31 22:45:40 +04:00
|
|
|
if (!md5sig)
|
2006-11-15 06:07:45 +03:00
|
|
|
return NULL;
|
2012-01-31 09:18:33 +04:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
if (family == AF_INET6)
|
|
|
|
size = sizeof(struct in6_addr);
|
|
|
|
#endif
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 05:06:00 +04:00
|
|
|
hlist_for_each_entry_rcu(key, &md5sig->head, node) {
|
2012-01-31 09:18:33 +04:00
|
|
|
if (key->family != family)
|
|
|
|
continue;
|
|
|
|
if (!memcmp(&key->addr, addr, size))
|
|
|
|
return key;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-01-31 09:18:33 +04:00
|
|
|
EXPORT_SYMBOL(tcp_md5_do_lookup);
|
2006-11-15 06:07:45 +03:00
|
|
|
|
|
|
|
struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
|
|
|
|
struct sock *addr_sk)
|
|
|
|
{
|
2012-01-31 09:18:33 +04:00
|
|
|
union tcp_md5_addr *addr;
|
|
|
|
|
|
|
|
addr = (union tcp_md5_addr *)&inet_sk(addr_sk)->inet_daddr;
|
|
|
|
return tcp_md5_do_lookup(sk, addr, AF_INET);
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tcp_v4_md5_lookup);
|
|
|
|
|
2006-12-01 04:22:29 +03:00
|
|
|
static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk,
|
|
|
|
struct request_sock *req)
|
2006-11-15 06:07:45 +03:00
|
|
|
{
|
2012-01-31 09:18:33 +04:00
|
|
|
union tcp_md5_addr *addr;
|
|
|
|
|
2013-10-10 02:21:29 +04:00
|
|
|
addr = (union tcp_md5_addr *)&inet_rsk(req)->ir_rmt_addr;
|
2012-01-31 09:18:33 +04:00
|
|
|
return tcp_md5_do_lookup(sk, addr, AF_INET);
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This can be called on a newly created socket, from other files */
|
2012-01-31 09:18:33 +04:00
|
|
|
int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
|
|
|
|
int family, const u8 *newkey, u8 newkeylen, gfp_t gfp)
|
2006-11-15 06:07:45 +03:00
|
|
|
{
|
|
|
|
/* Add Key to the list */
|
2007-10-30 06:55:27 +03:00
|
|
|
struct tcp_md5sig_key *key;
|
2006-11-15 06:07:45 +03:00
|
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
2012-01-31 09:18:33 +04:00
|
|
|
struct tcp_md5sig_info *md5sig;
|
2006-11-15 06:07:45 +03:00
|
|
|
|
2013-06-14 10:56:31 +04:00
|
|
|
key = tcp_md5_do_lookup(sk, addr, family);
|
2006-11-15 06:07:45 +03:00
|
|
|
if (key) {
|
|
|
|
/* Pre-existing entry - just update that one. */
|
2012-01-31 09:18:33 +04:00
|
|
|
memcpy(key->key, newkey, newkeylen);
|
2007-10-30 06:55:27 +03:00
|
|
|
key->keylen = newkeylen;
|
2012-01-31 09:18:33 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2011-09-29 21:10:10 +04:00
|
|
|
|
2012-01-31 22:45:40 +04:00
|
|
|
md5sig = rcu_dereference_protected(tp->md5sig_info,
|
|
|
|
sock_owned_by_user(sk));
|
2012-01-31 09:18:33 +04:00
|
|
|
if (!md5sig) {
|
|
|
|
md5sig = kmalloc(sizeof(*md5sig), gfp);
|
|
|
|
if (!md5sig)
|
2006-11-15 06:07:45 +03:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2012-01-31 09:18:33 +04:00
|
|
|
sk_nocaps_add(sk, NETIF_F_GSO_MASK);
|
|
|
|
INIT_HLIST_HEAD(&md5sig->head);
|
2012-01-31 22:45:40 +04:00
|
|
|
rcu_assign_pointer(tp->md5sig_info, md5sig);
|
2012-01-31 09:18:33 +04:00
|
|
|
}
|
2006-11-15 06:07:45 +03:00
|
|
|
|
2012-01-31 14:56:48 +04:00
|
|
|
key = sock_kmalloc(sk, sizeof(*key), gfp);
|
2012-01-31 09:18:33 +04:00
|
|
|
if (!key)
|
|
|
|
return -ENOMEM;
|
2013-05-20 10:52:26 +04:00
|
|
|
if (!tcp_alloc_md5sig_pool()) {
|
2012-01-31 14:56:48 +04:00
|
|
|
sock_kfree_s(sk, key, sizeof(*key));
|
2012-01-31 09:18:33 +04:00
|
|
|
return -ENOMEM;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
2012-01-31 09:18:33 +04:00
|
|
|
|
|
|
|
memcpy(key->key, newkey, newkeylen);
|
|
|
|
key->keylen = newkeylen;
|
|
|
|
key->family = family;
|
|
|
|
memcpy(&key->addr, addr,
|
|
|
|
(family == AF_INET6) ? sizeof(struct in6_addr) :
|
|
|
|
sizeof(struct in_addr));
|
|
|
|
hlist_add_head_rcu(&key->node, &md5sig->head);
|
2006-11-15 06:07:45 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2012-01-31 09:18:33 +04:00
|
|
|
EXPORT_SYMBOL(tcp_md5_do_add);
|
2006-11-15 06:07:45 +03:00
|
|
|
|
2012-01-31 09:18:33 +04:00
|
|
|
int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
|
2006-11-15 06:07:45 +03:00
|
|
|
{
|
2012-01-31 09:18:33 +04:00
|
|
|
struct tcp_md5sig_key *key;
|
|
|
|
|
2013-06-14 10:56:31 +04:00
|
|
|
key = tcp_md5_do_lookup(sk, addr, family);
|
2012-01-31 09:18:33 +04:00
|
|
|
if (!key)
|
|
|
|
return -ENOENT;
|
|
|
|
hlist_del_rcu(&key->node);
|
2012-01-31 14:56:48 +04:00
|
|
|
atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
|
2012-01-31 09:18:33 +04:00
|
|
|
kfree_rcu(key, rcu);
|
|
|
|
return 0;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
2012-01-31 09:18:33 +04:00
|
|
|
EXPORT_SYMBOL(tcp_md5_do_del);
|
2006-11-15 06:07:45 +03:00
|
|
|
|
2012-10-26 18:31:40 +04:00
|
|
|
static void tcp_clear_md5_list(struct sock *sk)
|
2006-11-15 06:07:45 +03:00
|
|
|
{
|
|
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
2012-01-31 09:18:33 +04:00
|
|
|
struct tcp_md5sig_key *key;
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 05:06:00 +04:00
|
|
|
struct hlist_node *n;
|
2012-01-31 22:45:40 +04:00
|
|
|
struct tcp_md5sig_info *md5sig;
|
2006-11-15 06:07:45 +03:00
|
|
|
|
2012-01-31 22:45:40 +04:00
|
|
|
md5sig = rcu_dereference_protected(tp->md5sig_info, 1);
|
|
|
|
|
hlist: drop the node parameter from iterators
I'm not sure why, but the hlist for each entry iterators were conceived
list_for_each_entry(pos, head, member)
The hlist ones were greedy and wanted an extra parameter:
hlist_for_each_entry(tpos, pos, head, member)
Why did they need an extra pos parameter? I'm not quite sure. Not only
they don't really need it, it also prevents the iterator from looking
exactly like the list iterator, which is unfortunate.
Besides the semantic patch, there was some manual work required:
- Fix up the actual hlist iterators in linux/list.h
- Fix up the declaration of other iterators based on the hlist ones.
- A very small amount of places were using the 'node' parameter, this
was modified to use 'obj->member' instead.
- Coccinelle didn't handle the hlist_for_each_entry_safe iterator
properly, so those had to be fixed up manually.
The semantic patch which is mostly the work of Peter Senna Tschudin is here:
@@
iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host;
type T;
expression a,c,d,e;
identifier b;
statement S;
@@
-T b;
<+... when != b
(
hlist_for_each_entry(a,
- b,
c, d) S
|
hlist_for_each_entry_continue(a,
- b,
c) S
|
hlist_for_each_entry_from(a,
- b,
c) S
|
hlist_for_each_entry_rcu(a,
- b,
c, d) S
|
hlist_for_each_entry_rcu_bh(a,
- b,
c, d) S
|
hlist_for_each_entry_continue_rcu_bh(a,
- b,
c) S
|
for_each_busy_worker(a, c,
- b,
d) S
|
ax25_uid_for_each(a,
- b,
c) S
|
ax25_for_each(a,
- b,
c) S
|
inet_bind_bucket_for_each(a,
- b,
c) S
|
sctp_for_each_hentry(a,
- b,
c) S
|
sk_for_each(a,
- b,
c) S
|
sk_for_each_rcu(a,
- b,
c) S
|
sk_for_each_from
-(a, b)
+(a)
S
+ sk_for_each_from(a) S
|
sk_for_each_safe(a,
- b,
c, d) S
|
sk_for_each_bound(a,
- b,
c) S
|
hlist_for_each_entry_safe(a,
- b,
c, d, e) S
|
hlist_for_each_entry_continue_rcu(a,
- b,
c) S
|
nr_neigh_for_each(a,
- b,
c) S
|
nr_neigh_for_each_safe(a,
- b,
c, d) S
|
nr_node_for_each(a,
- b,
c) S
|
nr_node_for_each_safe(a,
- b,
c, d) S
|
- for_each_gfn_sp(a, c, d, b) S
+ for_each_gfn_sp(a, c, d) S
|
- for_each_gfn_indirect_valid_sp(a, c, d, b) S
+ for_each_gfn_indirect_valid_sp(a, c, d) S
|
for_each_host(a,
- b,
c) S
|
for_each_host_safe(a,
- b,
c, d) S
|
for_each_mesh_entry(a,
- b,
c, d) S
)
...+>
[akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c]
[akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c]
[akpm@linux-foundation.org: checkpatch fixes]
[akpm@linux-foundation.org: fix warnings]
[akpm@linux-foudnation.org: redo intrusive kvm changes]
Tested-by: Peter Senna Tschudin <peter.senna@gmail.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Cc: Wu Fengguang <fengguang.wu@intel.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-02-28 05:06:00 +04:00
|
|
|
hlist_for_each_entry_safe(key, n, &md5sig->head, node) {
|
2012-01-31 09:18:33 +04:00
|
|
|
hlist_del_rcu(&key->node);
|
2012-01-31 14:56:48 +04:00
|
|
|
atomic_sub(sizeof(*key), &sk->sk_omem_alloc);
|
2012-01-31 09:18:33 +04:00
|
|
|
kfree_rcu(key, rcu);
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-11-17 15:57:30 +03:00
|
|
|
static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
|
|
|
|
int optlen)
|
2006-11-15 06:07:45 +03:00
|
|
|
{
|
|
|
|
struct tcp_md5sig cmd;
|
|
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr;
|
|
|
|
|
|
|
|
if (optlen < sizeof(cmd))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2006-11-17 15:57:30 +03:00
|
|
|
if (copy_from_user(&cmd, optval, sizeof(cmd)))
|
2006-11-15 06:07:45 +03:00
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (sin->sin_family != AF_INET)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-01-31 22:45:40 +04:00
|
|
|
if (!cmd.tcpm_key || !cmd.tcpm_keylen)
|
2012-01-31 09:18:33 +04:00
|
|
|
return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
|
|
|
|
AF_INET);
|
2006-11-15 06:07:45 +03:00
|
|
|
|
|
|
|
if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-01-31 09:18:33 +04:00
|
|
|
return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin->sin_addr.s_addr,
|
|
|
|
AF_INET, cmd.tcpm_key, cmd.tcpm_keylen,
|
|
|
|
GFP_KERNEL);
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
|
2008-07-19 11:01:42 +04:00
|
|
|
static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
|
|
|
|
__be32 daddr, __be32 saddr, int nbytes)
|
2006-11-15 06:07:45 +03:00
|
|
|
{
|
|
|
|
struct tcp4_pseudohdr *bp;
|
2008-07-19 11:01:42 +04:00
|
|
|
struct scatterlist sg;
|
2006-11-15 06:07:45 +03:00
|
|
|
|
|
|
|
bp = &hp->md5_blk.ip4;
|
|
|
|
|
|
|
|
/*
|
2008-07-19 11:01:42 +04:00
|
|
|
* 1. the TCP pseudo-header (in the order: source IP address,
|
2006-11-15 06:07:45 +03:00
|
|
|
* destination IP address, zero-padded protocol number, and
|
|
|
|
* segment length)
|
|
|
|
*/
|
|
|
|
bp->saddr = saddr;
|
|
|
|
bp->daddr = daddr;
|
|
|
|
bp->pad = 0;
|
2008-04-17 07:48:12 +04:00
|
|
|
bp->protocol = IPPROTO_TCP;
|
2008-07-19 11:01:42 +04:00
|
|
|
bp->len = cpu_to_be16(nbytes);
|
2007-10-26 11:41:21 +04:00
|
|
|
|
2008-07-19 11:01:42 +04:00
|
|
|
sg_init_one(&sg, bp, sizeof(*bp));
|
|
|
|
return crypto_hash_update(&hp->md5_desc, &sg, sizeof(*bp));
|
|
|
|
}
|
|
|
|
|
2012-01-31 09:18:33 +04:00
|
|
|
static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
|
2011-10-24 10:46:04 +04:00
|
|
|
__be32 daddr, __be32 saddr, const struct tcphdr *th)
|
2008-07-19 11:01:42 +04:00
|
|
|
{
|
|
|
|
struct tcp_md5sig_pool *hp;
|
|
|
|
struct hash_desc *desc;
|
|
|
|
|
|
|
|
hp = tcp_get_md5sig_pool();
|
|
|
|
if (!hp)
|
|
|
|
goto clear_hash_noput;
|
|
|
|
desc = &hp->md5_desc;
|
|
|
|
|
|
|
|
if (crypto_hash_init(desc))
|
|
|
|
goto clear_hash;
|
|
|
|
if (tcp_v4_md5_hash_pseudoheader(hp, daddr, saddr, th->doff << 2))
|
|
|
|
goto clear_hash;
|
|
|
|
if (tcp_md5_hash_header(hp, th))
|
|
|
|
goto clear_hash;
|
|
|
|
if (tcp_md5_hash_key(hp, key))
|
|
|
|
goto clear_hash;
|
|
|
|
if (crypto_hash_final(desc, md5_hash))
|
2006-11-15 06:07:45 +03:00
|
|
|
goto clear_hash;
|
|
|
|
|
|
|
|
tcp_put_md5sig_pool();
|
|
|
|
return 0;
|
2008-07-19 11:01:42 +04:00
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
clear_hash:
|
|
|
|
tcp_put_md5sig_pool();
|
|
|
|
clear_hash_noput:
|
|
|
|
memset(md5_hash, 0, 16);
|
2008-07-19 11:01:42 +04:00
|
|
|
return 1;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
|
2008-07-19 11:01:42 +04:00
|
|
|
int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
|
2011-10-24 10:46:04 +04:00
|
|
|
const struct sock *sk, const struct request_sock *req,
|
|
|
|
const struct sk_buff *skb)
|
2006-11-15 06:07:45 +03:00
|
|
|
{
|
2008-07-19 11:01:42 +04:00
|
|
|
struct tcp_md5sig_pool *hp;
|
|
|
|
struct hash_desc *desc;
|
2011-10-24 10:46:04 +04:00
|
|
|
const struct tcphdr *th = tcp_hdr(skb);
|
2006-11-15 06:07:45 +03:00
|
|
|
__be32 saddr, daddr;
|
|
|
|
|
|
|
|
if (sk) {
|
2009-10-15 10:30:45 +04:00
|
|
|
saddr = inet_sk(sk)->inet_saddr;
|
|
|
|
daddr = inet_sk(sk)->inet_daddr;
|
2008-07-19 11:01:42 +04:00
|
|
|
} else if (req) {
|
2013-10-10 02:21:29 +04:00
|
|
|
saddr = inet_rsk(req)->ir_loc_addr;
|
|
|
|
daddr = inet_rsk(req)->ir_rmt_addr;
|
2006-11-15 06:07:45 +03:00
|
|
|
} else {
|
2008-07-19 11:01:42 +04:00
|
|
|
const struct iphdr *iph = ip_hdr(skb);
|
|
|
|
saddr = iph->saddr;
|
|
|
|
daddr = iph->daddr;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
2008-07-19 11:01:42 +04:00
|
|
|
|
|
|
|
hp = tcp_get_md5sig_pool();
|
|
|
|
if (!hp)
|
|
|
|
goto clear_hash_noput;
|
|
|
|
desc = &hp->md5_desc;
|
|
|
|
|
|
|
|
if (crypto_hash_init(desc))
|
|
|
|
goto clear_hash;
|
|
|
|
|
|
|
|
if (tcp_v4_md5_hash_pseudoheader(hp, daddr, saddr, skb->len))
|
|
|
|
goto clear_hash;
|
|
|
|
if (tcp_md5_hash_header(hp, th))
|
|
|
|
goto clear_hash;
|
|
|
|
if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2))
|
|
|
|
goto clear_hash;
|
|
|
|
if (tcp_md5_hash_key(hp, key))
|
|
|
|
goto clear_hash;
|
|
|
|
if (crypto_hash_final(desc, md5_hash))
|
|
|
|
goto clear_hash;
|
|
|
|
|
|
|
|
tcp_put_md5sig_pool();
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
clear_hash:
|
|
|
|
tcp_put_md5sig_pool();
|
|
|
|
clear_hash_noput:
|
|
|
|
memset(md5_hash, 0, 16);
|
|
|
|
return 1;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
2008-07-19 11:01:42 +04:00
|
|
|
EXPORT_SYMBOL(tcp_v4_md5_hash_skb);
|
2006-11-15 06:07:45 +03:00
|
|
|
|
2012-05-17 03:15:34 +04:00
|
|
|
static bool tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
|
2006-11-15 06:07:45 +03:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* This gets called for each TCP segment that arrives
|
|
|
|
* so we want to be efficient.
|
|
|
|
* We have 3 drop cases:
|
|
|
|
* o No MD5 hash and one expected.
|
|
|
|
* o MD5 hash and we're not expecting one.
|
|
|
|
* o MD5 hash and its wrong.
|
|
|
|
*/
|
2011-10-21 13:22:42 +04:00
|
|
|
const __u8 *hash_location = NULL;
|
2006-11-15 06:07:45 +03:00
|
|
|
struct tcp_md5sig_key *hash_expected;
|
2007-04-21 09:47:35 +04:00
|
|
|
const struct iphdr *iph = ip_hdr(skb);
|
2011-10-21 13:22:42 +04:00
|
|
|
const struct tcphdr *th = tcp_hdr(skb);
|
2006-11-15 06:07:45 +03:00
|
|
|
int genhash;
|
|
|
|
unsigned char newhash[16];
|
|
|
|
|
2012-01-31 09:18:33 +04:00
|
|
|
hash_expected = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&iph->saddr,
|
|
|
|
AF_INET);
|
2008-04-17 07:29:53 +04:00
|
|
|
hash_location = tcp_parse_md5sig_option(th);
|
2006-11-15 06:07:45 +03:00
|
|
|
|
|
|
|
/* We've parsed the options - do we have a hash? */
|
|
|
|
if (!hash_expected && !hash_location)
|
2012-05-17 03:15:34 +04:00
|
|
|
return false;
|
2006-11-15 06:07:45 +03:00
|
|
|
|
|
|
|
if (hash_expected && !hash_location) {
|
2008-07-30 14:03:15 +04:00
|
|
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
|
2012-05-17 03:15:34 +04:00
|
|
|
return true;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!hash_expected && hash_location) {
|
2008-07-30 14:03:15 +04:00
|
|
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED);
|
2012-05-17 03:15:34 +04:00
|
|
|
return true;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Okay, so this is hash_expected and hash_location -
|
|
|
|
* so we need to calculate the checksum.
|
|
|
|
*/
|
2008-07-19 11:01:42 +04:00
|
|
|
genhash = tcp_v4_md5_hash_skb(newhash,
|
|
|
|
hash_expected,
|
|
|
|
NULL, NULL, skb);
|
2006-11-15 06:07:45 +03:00
|
|
|
|
|
|
|
if (genhash || memcmp(hash_location, newhash, 16) != 0) {
|
2012-05-14 01:56:26 +04:00
|
|
|
net_info_ratelimited("MD5 Hash failed for (%pI4, %d)->(%pI4, %d)%s\n",
|
|
|
|
&iph->saddr, ntohs(th->source),
|
|
|
|
&iph->daddr, ntohs(th->dest),
|
|
|
|
genhash ? " tcp_v4_calc_md5_hash failed"
|
|
|
|
: "");
|
2012-05-17 03:15:34 +04:00
|
|
|
return true;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
2012-05-17 03:15:34 +04:00
|
|
|
return false;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2006-11-16 13:30:37 +03:00
|
|
|
struct request_sock_ops tcp_request_sock_ops __read_mostly = {
|
2005-04-17 02:20:36 +04:00
|
|
|
.family = PF_INET,
|
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
|
|
|
.obj_size = sizeof(struct tcp_request_sock),
|
2010-01-18 06:09:39 +03:00
|
|
|
.rtx_syn_ack = tcp_v4_rtx_synack,
|
2005-06-19 09:47:21 +04:00
|
|
|
.send_ack = tcp_v4_reqsk_send_ack,
|
|
|
|
.destructor = tcp_v4_reqsk_destructor,
|
2005-04-17 02:20:36 +04:00
|
|
|
.send_reset = tcp_v4_send_reset,
|
2010-01-18 06:09:39 +03:00
|
|
|
.syn_ack_timeout = tcp_syn_ack_timeout,
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
2009-09-01 23:25:03 +04:00
|
|
|
static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
|
2006-11-15 06:07:45 +03:00
|
|
|
.md5_lookup = tcp_v4_reqsk_md5_lookup,
|
2009-07-16 09:04:51 +04:00
|
|
|
.calc_md5_hash = tcp_v4_md5_hash_skb,
|
2006-11-15 06:07:45 +03:00
|
|
|
};
|
2006-12-01 06:16:28 +03:00
|
|
|
#endif
|
2006-11-15 06:07:45 +03:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct tcp_options_received tmp_opt;
|
2005-06-19 09:47:21 +04:00
|
|
|
struct request_sock *req;
|
2009-12-02 21:07:39 +03:00
|
|
|
struct inet_request_sock *ireq;
|
2009-12-02 21:25:27 +03:00
|
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
2009-12-02 21:07:39 +03:00
|
|
|
struct dst_entry *dst = NULL;
|
2007-04-21 09:47:35 +04:00
|
|
|
__be32 saddr = ip_hdr(skb)->saddr;
|
|
|
|
__be32 daddr = ip_hdr(skb)->daddr;
|
2005-04-17 02:20:36 +04:00
|
|
|
__u32 isn = TCP_SKB_CB(skb)->when;
|
2014-05-12 07:22:11 +04:00
|
|
|
bool want_cookie = false, fastopen;
|
2012-08-31 16:29:13 +04:00
|
|
|
struct flowi4 fl4;
|
|
|
|
struct tcp_fastopen_cookie foc = { .len = -1 };
|
2014-05-12 07:22:11 +04:00
|
|
|
int err;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* Never answer to SYNs send to broadcast or multicast */
|
2009-06-02 09:14:27 +04:00
|
|
|
if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
|
2005-04-17 02:20:36 +04:00
|
|
|
goto drop;
|
|
|
|
|
|
|
|
/* TW buckets are converted to open requests without
|
|
|
|
* limitations, they conserve resources and peer is
|
|
|
|
* evidently real one.
|
|
|
|
*/
|
2013-07-26 19:43:23 +04:00
|
|
|
if ((sysctl_tcp_syncookies == 2 ||
|
|
|
|
inet_csk_reqsk_queue_is_full(sk)) && !isn) {
|
2011-08-30 07:21:44 +04:00
|
|
|
want_cookie = tcp_syn_flood_action(sk, skb, "TCP");
|
|
|
|
if (!want_cookie)
|
|
|
|
goto drop;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Accept backlog is full. If we have already queued enough
|
|
|
|
* of warm entries in syn queue, drop request. It is better than
|
|
|
|
* clogging syn queue with openreqs with exponentially increasing
|
|
|
|
* timeout.
|
|
|
|
*/
|
2013-01-28 21:52:37 +04:00
|
|
|
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) {
|
|
|
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto drop;
|
2013-01-28 21:52:37 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-06-10 23:39:35 +04:00
|
|
|
req = inet_reqsk_alloc(&tcp_request_sock_ops);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (!req)
|
|
|
|
goto drop;
|
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
|
|
|
tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops;
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
tcp_clear_options(&tmp_opt);
|
2009-11-10 12:51:18 +03:00
|
|
|
tmp_opt.mss_clamp = TCP_MSS_DEFAULT;
|
2009-12-02 21:25:27 +03:00
|
|
|
tmp_opt.user_mss = tp->rx_opt.user_mss;
|
2013-03-17 12:23:34 +04:00
|
|
|
tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-04-10 14:12:40 +04:00
|
|
|
if (want_cookie && !tmp_opt.saw_tstamp)
|
2005-04-17 02:20:36 +04:00
|
|
|
tcp_clear_options(&tmp_opt);
|
|
|
|
|
|
|
|
tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
|
|
|
|
tcp_openreq_init(req, &tmp_opt, skb);
|
|
|
|
|
tcp: Revert per-route SACK/DSACK/TIMESTAMP changes.
It creates a regression, triggering badness for SYN_RECV
sockets, for example:
[19148.022102] Badness at net/ipv4/inet_connection_sock.c:293
[19148.022570] NIP: c02a0914 LR: c02a0904 CTR: 00000000
[19148.023035] REGS: eeecbd30 TRAP: 0700 Not tainted (2.6.32)
[19148.023496] MSR: 00029032 <EE,ME,CE,IR,DR> CR: 24002442 XER: 00000000
[19148.024012] TASK = eee9a820[1756] 'privoxy' THREAD: eeeca000
This is likely caused by the change in the 'estab' parameter
passed to tcp_parse_options() when invoked by the functions
in net/ipv4/tcp_minisocks.c
But even if that is fixed, the ->conn_request() changes made in
this patch series is fundamentally wrong. They try to use the
listening socket's 'dst' to probe the route settings. The
listening socket doesn't even have a route, and you can't
get the right route (the child request one) until much later
after we setup all of the state, and it must be done by hand.
This stuff really isn't ready, so the best thing to do is a
full revert. This reverts the following commits:
f55017a93f1a74d50244b1254b9a2bd7ac9bbf7d
022c3f7d82f0f1c68018696f2f027b87b9bb45c2
1aba721eba1d84a2defce45b950272cee1e6c72a
cda42ebd67ee5fdf09d7057b5a4584d36fe8a335
345cda2fd695534be5a4494f1b59da9daed33663
dc343475ed062e13fc260acccaab91d7d80fd5b2
05eaade2782fb0c90d3034fd7a7d5a16266182bb
6a2a2d6bf8581216e08be15fcb563cfd6c430e1e
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-12-16 07:56:42 +03:00
|
|
|
ireq = inet_rsk(req);
|
2013-10-10 02:21:29 +04:00
|
|
|
ireq->ir_loc_addr = daddr;
|
|
|
|
ireq->ir_rmt_addr = saddr;
|
tcp: Revert per-route SACK/DSACK/TIMESTAMP changes.
It creates a regression, triggering badness for SYN_RECV
sockets, for example:
[19148.022102] Badness at net/ipv4/inet_connection_sock.c:293
[19148.022570] NIP: c02a0914 LR: c02a0904 CTR: 00000000
[19148.023035] REGS: eeecbd30 TRAP: 0700 Not tainted (2.6.32)
[19148.023496] MSR: 00029032 <EE,ME,CE,IR,DR> CR: 24002442 XER: 00000000
[19148.024012] TASK = eee9a820[1756] 'privoxy' THREAD: eeeca000
This is likely caused by the change in the 'estab' parameter
passed to tcp_parse_options() when invoked by the functions
in net/ipv4/tcp_minisocks.c
But even if that is fixed, the ->conn_request() changes made in
this patch series is fundamentally wrong. They try to use the
listening socket's 'dst' to probe the route settings. The
listening socket doesn't even have a route, and you can't
get the right route (the child request one) until much later
after we setup all of the state, and it must be done by hand.
This stuff really isn't ready, so the best thing to do is a
full revert. This reverts the following commits:
f55017a93f1a74d50244b1254b9a2bd7ac9bbf7d
022c3f7d82f0f1c68018696f2f027b87b9bb45c2
1aba721eba1d84a2defce45b950272cee1e6c72a
cda42ebd67ee5fdf09d7057b5a4584d36fe8a335
345cda2fd695534be5a4494f1b59da9daed33663
dc343475ed062e13fc260acccaab91d7d80fd5b2
05eaade2782fb0c90d3034fd7a7d5a16266182bb
6a2a2d6bf8581216e08be15fcb563cfd6c430e1e
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-12-16 07:56:42 +03:00
|
|
|
ireq->no_srccheck = inet_sk(sk)->transparent;
|
2012-09-26 15:59:09 +04:00
|
|
|
ireq->opt = tcp_v4_save_options(skb);
|
tcp: Revert per-route SACK/DSACK/TIMESTAMP changes.
It creates a regression, triggering badness for SYN_RECV
sockets, for example:
[19148.022102] Badness at net/ipv4/inet_connection_sock.c:293
[19148.022570] NIP: c02a0914 LR: c02a0904 CTR: 00000000
[19148.023035] REGS: eeecbd30 TRAP: 0700 Not tainted (2.6.32)
[19148.023496] MSR: 00029032 <EE,ME,CE,IR,DR> CR: 24002442 XER: 00000000
[19148.024012] TASK = eee9a820[1756] 'privoxy' THREAD: eeeca000
This is likely caused by the change in the 'estab' parameter
passed to tcp_parse_options() when invoked by the functions
in net/ipv4/tcp_minisocks.c
But even if that is fixed, the ->conn_request() changes made in
this patch series is fundamentally wrong. They try to use the
listening socket's 'dst' to probe the route settings. The
listening socket doesn't even have a route, and you can't
get the right route (the child request one) until much later
after we setup all of the state, and it must be done by hand.
This stuff really isn't ready, so the best thing to do is a
full revert. This reverts the following commits:
f55017a93f1a74d50244b1254b9a2bd7ac9bbf7d
022c3f7d82f0f1c68018696f2f027b87b9bb45c2
1aba721eba1d84a2defce45b950272cee1e6c72a
cda42ebd67ee5fdf09d7057b5a4584d36fe8a335
345cda2fd695534be5a4494f1b59da9daed33663
dc343475ed062e13fc260acccaab91d7d80fd5b2
05eaade2782fb0c90d3034fd7a7d5a16266182bb
6a2a2d6bf8581216e08be15fcb563cfd6c430e1e
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-12-16 07:56:42 +03:00
|
|
|
|
2009-03-28 00:10:28 +03:00
|
|
|
if (security_inet_conn_request(sk, skb, req))
|
tcp: Revert per-route SACK/DSACK/TIMESTAMP changes.
It creates a regression, triggering badness for SYN_RECV
sockets, for example:
[19148.022102] Badness at net/ipv4/inet_connection_sock.c:293
[19148.022570] NIP: c02a0914 LR: c02a0904 CTR: 00000000
[19148.023035] REGS: eeecbd30 TRAP: 0700 Not tainted (2.6.32)
[19148.023496] MSR: 00029032 <EE,ME,CE,IR,DR> CR: 24002442 XER: 00000000
[19148.024012] TASK = eee9a820[1756] 'privoxy' THREAD: eeeca000
This is likely caused by the change in the 'estab' parameter
passed to tcp_parse_options() when invoked by the functions
in net/ipv4/tcp_minisocks.c
But even if that is fixed, the ->conn_request() changes made in
this patch series is fundamentally wrong. They try to use the
listening socket's 'dst' to probe the route settings. The
listening socket doesn't even have a route, and you can't
get the right route (the child request one) until much later
after we setup all of the state, and it must be done by hand.
This stuff really isn't ready, so the best thing to do is a
full revert. This reverts the following commits:
f55017a93f1a74d50244b1254b9a2bd7ac9bbf7d
022c3f7d82f0f1c68018696f2f027b87b9bb45c2
1aba721eba1d84a2defce45b950272cee1e6c72a
cda42ebd67ee5fdf09d7057b5a4584d36fe8a335
345cda2fd695534be5a4494f1b59da9daed33663
dc343475ed062e13fc260acccaab91d7d80fd5b2
05eaade2782fb0c90d3034fd7a7d5a16266182bb
6a2a2d6bf8581216e08be15fcb563cfd6c430e1e
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-12-16 07:56:42 +03:00
|
|
|
goto drop_and_free;
|
2009-03-28 00:10:28 +03:00
|
|
|
|
2010-06-21 15:48:45 +04:00
|
|
|
if (!want_cookie || tmp_opt.tstamp_ok)
|
2013-01-05 20:10:48 +04:00
|
|
|
TCP_ECN_create_request(req, skb, sock_net(sk));
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (want_cookie) {
|
|
|
|
isn = cookie_v4_init_sequence(sk, skb, &req->mss);
|
2010-06-21 15:48:45 +04:00
|
|
|
req->cookie_ts = tmp_opt.tstamp_ok;
|
2005-04-17 02:20:36 +04:00
|
|
|
} else if (!isn) {
|
|
|
|
/* VJ's idea. We save last timestamp seen
|
|
|
|
* from the destination in peer table, when entering
|
|
|
|
* state TIME-WAIT, and check against it before
|
|
|
|
* accepting new connection request.
|
|
|
|
*
|
|
|
|
* If "isn" is not zero, this request hit alive
|
|
|
|
* timewait bucket, so that all the necessary checks
|
|
|
|
* are made in the function processing timewait state.
|
|
|
|
*/
|
|
|
|
if (tmp_opt.saw_tstamp &&
|
2005-08-10 07:44:40 +04:00
|
|
|
tcp_death_row.sysctl_tw_recycle &&
|
2012-07-18 01:02:46 +04:00
|
|
|
(dst = inet_csk_route_req(sk, &fl4, req)) != NULL &&
|
2012-07-10 14:14:24 +04:00
|
|
|
fl4.daddr == saddr) {
|
|
|
|
if (!tcp_peer_is_proven(req, dst, true)) {
|
2008-07-17 07:31:16 +04:00
|
|
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
|
2008-03-03 22:59:32 +03:00
|
|
|
goto drop_and_release;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Kill the following clause, if you dislike this way. */
|
|
|
|
else if (!sysctl_tcp_syncookies &&
|
2005-08-10 07:10:42 +04:00
|
|
|
(sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) <
|
2005-04-17 02:20:36 +04:00
|
|
|
(sysctl_max_syn_backlog >> 2)) &&
|
2012-07-10 14:14:24 +04:00
|
|
|
!tcp_peer_is_proven(req, dst, false)) {
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Without syncookies last quarter of
|
|
|
|
* backlog is filled with destinations,
|
|
|
|
* proven to be alive.
|
|
|
|
* It means that we continue to communicate
|
|
|
|
* to destinations, already remembered
|
|
|
|
* to the moment of synflood.
|
|
|
|
*/
|
2012-03-12 11:03:32 +04:00
|
|
|
LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"),
|
2008-10-31 10:53:57 +03:00
|
|
|
&saddr, ntohs(tcp_hdr(skb)->source));
|
2008-03-03 22:59:32 +03:00
|
|
|
goto drop_and_release;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2006-11-11 01:06:49 +03:00
|
|
|
isn = tcp_v4_init_sequence(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2014-05-12 07:22:11 +04:00
|
|
|
if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
|
2012-08-31 16:29:13 +04:00
|
|
|
goto drop_and_free;
|
|
|
|
|
2014-05-12 07:22:11 +04:00
|
|
|
tcp_rsk(req)->snt_isn = isn;
|
|
|
|
tcp_rsk(req)->snt_synack = tcp_time_stamp;
|
|
|
|
tcp_openreq_init_rwin(req, sk, dst);
|
|
|
|
fastopen = !want_cookie &&
|
|
|
|
tcp_try_fastopen(sk, skb, req, &foc, dst);
|
|
|
|
err = tcp_v4_send_synack(sk, dst, req,
|
|
|
|
skb_get_queue_mapping(skb), &foc);
|
|
|
|
if (!fastopen) {
|
2012-08-31 16:29:13 +04:00
|
|
|
if (err || want_cookie)
|
|
|
|
goto drop_and_free;
|
|
|
|
|
2012-09-22 08:18:55 +04:00
|
|
|
tcp_rsk(req)->snt_synack = tcp_time_stamp;
|
2012-08-31 16:29:13 +04:00
|
|
|
tcp_rsk(req)->listener = NULL;
|
|
|
|
inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
|
2014-05-12 07:22:11 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2008-03-03 22:59:32 +03:00
|
|
|
drop_and_release:
|
|
|
|
dst_release(dst);
|
2005-04-17 02:20:36 +04:00
|
|
|
drop_and_free:
|
2005-06-19 09:47:21 +04:00
|
|
|
reqsk_free(req);
|
2005-04-17 02:20:36 +04:00
|
|
|
drop:
|
2013-01-31 12:24:06 +04:00
|
|
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2010-07-10 01:22:10 +04:00
|
|
|
EXPORT_SYMBOL(tcp_v4_conn_request);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The three way handshake has completed - we got a valid synack -
|
|
|
|
* now create the new socket.
|
|
|
|
*/
|
|
|
|
struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
|
2005-06-19 09:47:21 +04:00
|
|
|
struct request_sock *req,
|
2005-04-17 02:20:36 +04:00
|
|
|
struct dst_entry *dst)
|
|
|
|
{
|
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
|
|
|
struct inet_request_sock *ireq;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct inet_sock *newinet;
|
|
|
|
struct tcp_sock *newtp;
|
|
|
|
struct sock *newsk;
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
|
|
|
struct tcp_md5sig_key *key;
|
|
|
|
#endif
|
2011-04-21 13:45:37 +04:00
|
|
|
struct ip_options_rcu *inet_opt;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (sk_acceptq_is_full(sk))
|
|
|
|
goto exit_overflow;
|
|
|
|
|
|
|
|
newsk = tcp_create_openreq_child(sk, req, skb);
|
|
|
|
if (!newsk)
|
2010-10-21 15:06:43 +04:00
|
|
|
goto exit_nonewsk;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-07-01 00:36:35 +04:00
|
|
|
newsk->sk_gso_type = SKB_GSO_TCPV4;
|
2012-08-19 07:30:38 +04:00
|
|
|
inet_sk_rx_dst_set(newsk, skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
newtp = tcp_sk(newsk);
|
|
|
|
newinet = inet_sk(newsk);
|
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
|
|
|
ireq = inet_rsk(req);
|
2013-10-10 02:21:29 +04:00
|
|
|
newinet->inet_daddr = ireq->ir_rmt_addr;
|
|
|
|
newinet->inet_rcv_saddr = ireq->ir_loc_addr;
|
|
|
|
newinet->inet_saddr = ireq->ir_loc_addr;
|
2011-04-21 13:45:37 +04:00
|
|
|
inet_opt = ireq->opt;
|
|
|
|
rcu_assign_pointer(newinet->inet_opt, inet_opt);
|
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
|
|
|
ireq->opt = NULL;
|
2005-08-10 07:10:42 +04:00
|
|
|
newinet->mc_index = inet_iif(skb);
|
2007-04-21 09:47:35 +04:00
|
|
|
newinet->mc_ttl = ip_hdr(skb)->ttl;
|
2012-02-09 13:35:49 +04:00
|
|
|
newinet->rcv_tos = ip_hdr(skb)->tos;
|
2005-12-14 10:26:10 +03:00
|
|
|
inet_csk(newsk)->icsk_ext_hdr_len = 0;
|
2011-04-21 13:45:37 +04:00
|
|
|
if (inet_opt)
|
|
|
|
inet_csk(newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen;
|
2009-10-15 10:30:45 +04:00
|
|
|
newinet->inet_id = newtp->write_seq ^ jiffies;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-03-10 13:20:21 +04:00
|
|
|
if (!dst) {
|
|
|
|
dst = inet_csk_route_child_sock(sk, newsk, req);
|
|
|
|
if (!dst)
|
|
|
|
goto put_and_exit;
|
|
|
|
} else {
|
|
|
|
/* syncookie case : see end of cookie_v4_check() */
|
|
|
|
}
|
2011-05-09 02:28:03 +04:00
|
|
|
sk_setup_caps(newsk, dst);
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
tcp_sync_mss(newsk, dst_mtu(dst));
|
2010-12-13 23:52:14 +03:00
|
|
|
newtp->advmss = dst_metric_advmss(dst);
|
2008-09-21 11:21:51 +04:00
|
|
|
if (tcp_sk(sk)->rx_opt.user_mss &&
|
|
|
|
tcp_sk(sk)->rx_opt.user_mss < newtp->advmss)
|
|
|
|
newtp->advmss = tcp_sk(sk)->rx_opt.user_mss;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
tcp_initialize_rcv_mss(newsk);
|
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
|
|
|
/* Copy over the MD5 key from the original socket */
|
2012-01-31 09:18:33 +04:00
|
|
|
key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&newinet->inet_daddr,
|
|
|
|
AF_INET);
|
2009-10-15 10:30:45 +04:00
|
|
|
if (key != NULL) {
|
2006-11-15 06:07:45 +03:00
|
|
|
/*
|
|
|
|
* We're using one, so create a matching key
|
|
|
|
* on the newsk structure. If we fail to get
|
|
|
|
* memory, then we end up not copying the key
|
|
|
|
* across. Shucks.
|
|
|
|
*/
|
2012-01-31 09:18:33 +04:00
|
|
|
tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newinet->inet_daddr,
|
|
|
|
AF_INET, key->key, key->keylen, GFP_ATOMIC);
|
2010-05-16 11:36:33 +04:00
|
|
|
sk_nocaps_add(newsk, NETIF_F_GSO_MASK);
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-05-09 02:28:03 +04:00
|
|
|
if (__inet_inherit_port(sk, newsk) < 0)
|
|
|
|
goto put_and_exit;
|
2009-12-04 06:46:54 +03:00
|
|
|
__inet_hash_nolisten(newsk, NULL);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return newsk;
|
|
|
|
|
|
|
|
exit_overflow:
|
2008-07-17 07:31:16 +04:00
|
|
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
|
2010-10-21 15:06:43 +04:00
|
|
|
exit_nonewsk:
|
|
|
|
dst_release(dst);
|
2005-04-17 02:20:36 +04:00
|
|
|
exit:
|
2008-07-17 07:31:16 +04:00
|
|
|
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
|
2005-04-17 02:20:36 +04:00
|
|
|
return NULL;
|
2011-05-09 02:28:03 +04:00
|
|
|
put_and_exit:
|
inet: Fix kmemleak in tcp_v4/6_syn_recv_sock and dccp_v4/6_request_recv_sock
If in either of the above functions inet_csk_route_child_sock() or
__inet_inherit_port() fails, the newsk will not be freed:
unreferenced object 0xffff88022e8a92c0 (size 1592):
comm "softirq", pid 0, jiffies 4294946244 (age 726.160s)
hex dump (first 32 bytes):
0a 01 01 01 0a 01 01 02 00 00 00 00 a7 cc 16 00 ................
02 00 03 01 00 00 00 00 00 00 00 00 00 00 00 00 ................
backtrace:
[<ffffffff8153d190>] kmemleak_alloc+0x21/0x3e
[<ffffffff810ab3e7>] kmem_cache_alloc+0xb5/0xc5
[<ffffffff8149b65b>] sk_prot_alloc.isra.53+0x2b/0xcd
[<ffffffff8149b784>] sk_clone_lock+0x16/0x21e
[<ffffffff814d711a>] inet_csk_clone_lock+0x10/0x7b
[<ffffffff814ebbc3>] tcp_create_openreq_child+0x21/0x481
[<ffffffff814e8fa5>] tcp_v4_syn_recv_sock+0x3a/0x23b
[<ffffffff814ec5ba>] tcp_check_req+0x29f/0x416
[<ffffffff814e8e10>] tcp_v4_do_rcv+0x161/0x2bc
[<ffffffff814eb917>] tcp_v4_rcv+0x6c9/0x701
[<ffffffff814cea9f>] ip_local_deliver_finish+0x70/0xc4
[<ffffffff814cec20>] ip_local_deliver+0x4e/0x7f
[<ffffffff814ce9f8>] ip_rcv_finish+0x1fc/0x233
[<ffffffff814cee68>] ip_rcv+0x217/0x267
[<ffffffff814a7bbe>] __netif_receive_skb+0x49e/0x553
[<ffffffff814a7cc3>] netif_receive_skb+0x50/0x82
This happens, because sk_clone_lock initializes sk_refcnt to 2, and thus
a single sock_put() is not enough to free the memory. Additionally, things
like xfrm, memcg, cookie_values,... may have been initialized.
We have to free them properly.
This is fixed by forcing a call to tcp_done(), ending up in
inet_csk_destroy_sock, doing the final sock_put(). tcp_done() is necessary,
because it ends up doing all the cleanup on xfrm, memcg, cookie_values,
xfrm,...
Before calling tcp_done, we have to set the socket to SOCK_DEAD, to
force it entering inet_csk_destroy_sock. To avoid the warning in
inet_csk_destroy_sock, inet_num has to be set to 0.
As inet_csk_destroy_sock does a dec on orphan_count, we first have to
increase it.
Calling tcp_done() allows us to remove the calls to
tcp_clear_xmit_timer() and tcp_cleanup_congestion_control().
A similar approach is taken for dccp by calling dccp_done().
This is in the kernel since 093d282321 (tproxy: fix hash locking issue
when using port redirection in __inet_inherit_port()), thus since
version >= 2.6.37.
Signed-off-by: Christoph Paasch <christoph.paasch@uclouvain.be>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-12-14 08:07:58 +04:00
|
|
|
inet_csk_prepare_forced_close(newsk);
|
|
|
|
tcp_done(newsk);
|
2011-05-09 02:28:03 +04:00
|
|
|
goto exit;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2010-07-10 01:22:10 +04:00
|
|
|
EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
|
|
|
|
{
|
2007-04-11 08:04:22 +04:00
|
|
|
struct tcphdr *th = tcp_hdr(skb);
|
2007-04-21 09:47:35 +04:00
|
|
|
const struct iphdr *iph = ip_hdr(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
struct sock *nsk;
|
2005-06-19 09:47:21 +04:00
|
|
|
struct request_sock **prev;
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Find possible connection requests. */
|
2005-08-10 07:10:42 +04:00
|
|
|
struct request_sock *req = inet_csk_search_req(sk, &prev, th->source,
|
|
|
|
iph->saddr, iph->daddr);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (req)
|
2012-08-31 16:29:12 +04:00
|
|
|
return tcp_check_req(sk, skb, req, prev, false);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-03-25 20:26:21 +03:00
|
|
|
nsk = inet_lookup_established(sock_net(sk), &tcp_hashinfo, iph->saddr,
|
2008-01-31 16:06:40 +03:00
|
|
|
th->source, iph->daddr, th->dest, inet_iif(skb));
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (nsk) {
|
|
|
|
if (nsk->sk_state != TCP_TIME_WAIT) {
|
|
|
|
bh_lock_sock(nsk);
|
|
|
|
return nsk;
|
|
|
|
}
|
2006-10-11 06:41:46 +04:00
|
|
|
inet_twsk_put(inet_twsk(nsk));
|
2005-04-17 02:20:36 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_SYN_COOKIES
|
2010-06-03 04:43:44 +04:00
|
|
|
if (!th->syn)
|
2005-04-17 02:20:36 +04:00
|
|
|
sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));
|
|
|
|
#endif
|
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The socket must have it's spinlock held when we get
|
|
|
|
* here.
|
|
|
|
*
|
|
|
|
* We have a potential double-lock case here, so even when
|
|
|
|
* doing backlog processing we use the BH locking scheme.
|
|
|
|
* This is because we cannot sleep with the original spinlock
|
|
|
|
* held.
|
|
|
|
*/
|
|
|
|
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
|
|
|
|
{
|
2006-11-15 06:07:45 +03:00
|
|
|
struct sock *rsk;
|
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
|
|
|
/*
|
|
|
|
* We really want to reject the packet as early as possible
|
|
|
|
* if:
|
|
|
|
* o We're expecting an MD5'd packet and this is no MD5 tcp option
|
|
|
|
* o There is an MD5 option and we're not expecting one
|
|
|
|
*/
|
2006-11-17 15:57:30 +03:00
|
|
|
if (tcp_v4_inbound_md5_hash(sk, skb))
|
2006-11-15 06:07:45 +03:00
|
|
|
goto discard;
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
|
2012-07-30 03:20:37 +04:00
|
|
|
struct dst_entry *dst = sk->sk_rx_dst;
|
|
|
|
|
2011-08-14 23:45:55 +04:00
|
|
|
sock_rps_save_rxhash(sk, skb);
|
2012-07-30 03:20:37 +04:00
|
|
|
if (dst) {
|
2012-07-27 10:23:40 +04:00
|
|
|
if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif ||
|
|
|
|
dst->ops->check(dst, 0) == NULL) {
|
2012-07-24 03:29:00 +04:00
|
|
|
dst_release(dst);
|
|
|
|
sk->sk_rx_dst = NULL;
|
|
|
|
}
|
|
|
|
}
|
2013-09-03 23:23:22 +04:00
|
|
|
tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len);
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-03-19 03:43:48 +03:00
|
|
|
if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
|
2005-04-17 02:20:36 +04:00
|
|
|
goto csum_err;
|
|
|
|
|
|
|
|
if (sk->sk_state == TCP_LISTEN) {
|
|
|
|
struct sock *nsk = tcp_v4_hnd_req(sk, skb);
|
|
|
|
if (!nsk)
|
|
|
|
goto discard;
|
|
|
|
|
|
|
|
if (nsk != sk) {
|
2011-08-14 23:45:55 +04:00
|
|
|
sock_rps_save_rxhash(nsk, skb);
|
2006-11-15 06:07:45 +03:00
|
|
|
if (tcp_child_process(sk, nsk, skb)) {
|
|
|
|
rsk = nsk;
|
2005-04-17 02:20:36 +04:00
|
|
|
goto reset;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2010-06-03 13:03:58 +04:00
|
|
|
} else
|
2011-08-14 23:45:55 +04:00
|
|
|
sock_rps_save_rxhash(sk, skb);
|
2010-06-03 13:03:58 +04:00
|
|
|
|
2007-04-11 08:04:22 +04:00
|
|
|
if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
|
2006-11-15 06:07:45 +03:00
|
|
|
rsk = sk;
|
2005-04-17 02:20:36 +04:00
|
|
|
goto reset;
|
2006-11-15 06:07:45 +03:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
reset:
|
2006-11-15 06:07:45 +03:00
|
|
|
tcp_v4_send_reset(rsk, skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
discard:
|
|
|
|
kfree_skb(skb);
|
|
|
|
/* Be careful here. If this function gets more complicated and
|
|
|
|
* gcc suffers from register pressure on the x86, sk (in %ebx)
|
|
|
|
* might be destroyed here. This current version compiles correctly,
|
|
|
|
* but you have been warned.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
csum_err:
|
2013-04-29 12:39:56 +04:00
|
|
|
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_CSUMERRORS);
|
2008-07-17 07:22:25 +04:00
|
|
|
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto discard;
|
|
|
|
}
|
2010-07-10 01:22:10 +04:00
|
|
|
EXPORT_SYMBOL(tcp_v4_do_rcv);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-06-28 09:01:22 +04:00
|
|
|
void tcp_v4_early_demux(struct sk_buff *skb)
|
2012-06-20 08:22:05 +04:00
|
|
|
{
|
|
|
|
const struct iphdr *iph;
|
|
|
|
const struct tcphdr *th;
|
|
|
|
struct sock *sk;
|
|
|
|
|
|
|
|
if (skb->pkt_type != PACKET_HOST)
|
2012-06-28 09:01:22 +04:00
|
|
|
return;
|
2012-06-20 08:22:05 +04:00
|
|
|
|
2012-10-23 01:42:47 +04:00
|
|
|
if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct tcphdr)))
|
2012-06-28 09:01:22 +04:00
|
|
|
return;
|
2012-06-20 08:22:05 +04:00
|
|
|
|
|
|
|
iph = ip_hdr(skb);
|
2012-10-23 01:42:47 +04:00
|
|
|
th = tcp_hdr(skb);
|
2012-06-20 08:22:05 +04:00
|
|
|
|
|
|
|
if (th->doff < sizeof(struct tcphdr) / 4)
|
2012-06-28 09:01:22 +04:00
|
|
|
return;
|
2012-06-20 08:22:05 +04:00
|
|
|
|
2012-10-23 01:42:47 +04:00
|
|
|
sk = __inet_lookup_established(dev_net(skb->dev), &tcp_hashinfo,
|
2012-06-20 08:22:05 +04:00
|
|
|
iph->saddr, th->source,
|
2012-06-23 21:38:10 +04:00
|
|
|
iph->daddr, ntohs(th->dest),
|
2012-07-24 05:19:31 +04:00
|
|
|
skb->skb_iif);
|
2012-06-20 08:22:05 +04:00
|
|
|
if (sk) {
|
|
|
|
skb->sk = sk;
|
|
|
|
skb->destructor = sock_edemux;
|
|
|
|
if (sk->sk_state != TCP_TIME_WAIT) {
|
|
|
|
struct dst_entry *dst = sk->sk_rx_dst;
|
2012-07-27 10:23:40 +04:00
|
|
|
|
2012-06-20 08:22:05 +04:00
|
|
|
if (dst)
|
|
|
|
dst = dst_check(dst, 0);
|
2012-07-24 03:29:00 +04:00
|
|
|
if (dst &&
|
2012-07-27 10:23:40 +04:00
|
|
|
inet_sk(sk)->rx_dst_ifindex == skb->skb_iif)
|
2012-07-24 03:29:00 +04:00
|
|
|
skb_dst_set_noref(skb, dst);
|
2012-06-20 08:22:05 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-06 16:58:01 +04:00
|
|
|
/* Packet is added to VJ-style prequeue for processing in process
|
|
|
|
* context, if a reader task is waiting. Apparently, this exciting
|
|
|
|
* idea (VJ's mail "Re: query about TCP header on tcp-ip" of 07 Sep 93)
|
|
|
|
* failed somewhere. Latency? Burstiness? Well, at least now we will
|
|
|
|
* see, why it failed. 8)8) --ANK
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
bool tcp_prequeue(struct sock *sk, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
|
|
|
|
|
|
|
if (sysctl_tcp_low_latency || !tp->ucopy.task)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (skb->len <= tcp_hdrlen(skb) &&
|
|
|
|
skb_queue_len(&tp->ucopy.prequeue) == 0)
|
|
|
|
return false;
|
|
|
|
|
2013-04-30 11:50:54 +04:00
|
|
|
skb_dst_force(skb);
|
2013-03-06 16:58:01 +04:00
|
|
|
__skb_queue_tail(&tp->ucopy.prequeue, skb);
|
|
|
|
tp->ucopy.memory += skb->truesize;
|
|
|
|
if (tp->ucopy.memory > sk->sk_rcvbuf) {
|
|
|
|
struct sk_buff *skb1;
|
|
|
|
|
|
|
|
BUG_ON(sock_owned_by_user(sk));
|
|
|
|
|
|
|
|
while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) {
|
|
|
|
sk_backlog_rcv(sk, skb1);
|
|
|
|
NET_INC_STATS_BH(sock_net(sk),
|
|
|
|
LINUX_MIB_TCPPREQUEUEDROPPED);
|
|
|
|
}
|
|
|
|
|
|
|
|
tp->ucopy.memory = 0;
|
|
|
|
} else if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
|
|
|
|
wake_up_interruptible_sync_poll(sk_sleep(sk),
|
|
|
|
POLLIN | POLLRDNORM | POLLRDBAND);
|
|
|
|
if (!inet_csk_ack_scheduled(sk))
|
|
|
|
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
|
|
|
|
(3 * tcp_rto_min(sk)) / 4,
|
|
|
|
TCP_RTO_MAX);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tcp_prequeue);
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* From tcp_input.c
|
|
|
|
*/
|
|
|
|
|
|
|
|
int tcp_v4_rcv(struct sk_buff *skb)
|
|
|
|
{
|
2007-04-21 09:47:35 +04:00
|
|
|
const struct iphdr *iph;
|
2011-10-21 13:22:42 +04:00
|
|
|
const struct tcphdr *th;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct sock *sk;
|
|
|
|
int ret;
|
2008-07-17 07:20:58 +04:00
|
|
|
struct net *net = dev_net(skb->dev);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (skb->pkt_type != PACKET_HOST)
|
|
|
|
goto discard_it;
|
|
|
|
|
|
|
|
/* Count it even if it's bad */
|
2008-07-17 07:22:25 +04:00
|
|
|
TCP_INC_STATS_BH(net, TCP_MIB_INSEGS);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
|
|
|
|
goto discard_it;
|
|
|
|
|
2007-04-11 08:04:22 +04:00
|
|
|
th = tcp_hdr(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (th->doff < sizeof(struct tcphdr) / 4)
|
|
|
|
goto bad_packet;
|
|
|
|
if (!pskb_may_pull(skb, th->doff * 4))
|
|
|
|
goto discard_it;
|
|
|
|
|
|
|
|
/* An explanation is required here, I think.
|
|
|
|
* Packet length and doff are validated by header prediction,
|
2005-11-11 04:13:47 +03:00
|
|
|
* provided case of th->doff==0 is eliminated.
|
2005-04-17 02:20:36 +04:00
|
|
|
* So, we defer the checks. */
|
2014-05-03 03:29:38 +04:00
|
|
|
|
|
|
|
if (skb_checksum_init(skb, IPPROTO_TCP, inet_compute_pseudo))
|
2013-04-29 12:39:56 +04:00
|
|
|
goto csum_error;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-04-11 08:04:22 +04:00
|
|
|
th = tcp_hdr(skb);
|
2007-04-21 09:47:35 +04:00
|
|
|
iph = ip_hdr(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
TCP_SKB_CB(skb)->seq = ntohl(th->seq);
|
|
|
|
TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
|
|
|
|
skb->len - th->doff * 4);
|
|
|
|
TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
|
|
|
|
TCP_SKB_CB(skb)->when = 0;
|
2011-09-27 10:20:08 +04:00
|
|
|
TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph);
|
2005-04-17 02:20:36 +04:00
|
|
|
TCP_SKB_CB(skb)->sacked = 0;
|
|
|
|
|
2008-10-07 22:41:57 +04:00
|
|
|
sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (!sk)
|
|
|
|
goto no_tcp_socket;
|
|
|
|
|
2010-03-09 08:55:56 +03:00
|
|
|
process:
|
|
|
|
if (sk->sk_state == TCP_TIME_WAIT)
|
|
|
|
goto do_time_wait;
|
|
|
|
|
2010-03-08 02:21:57 +03:00
|
|
|
if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) {
|
|
|
|
NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
|
2010-01-12 03:28:01 +03:00
|
|
|
goto discard_and_relse;
|
2010-03-08 02:21:57 +03:00
|
|
|
}
|
2010-01-12 03:28:01 +03:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
|
|
|
|
goto discard_and_relse;
|
2006-01-07 10:06:10 +03:00
|
|
|
nf_reset(skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-09-01 02:28:39 +04:00
|
|
|
if (sk_filter(sk, skb))
|
2005-04-17 02:20:36 +04:00
|
|
|
goto discard_and_relse;
|
|
|
|
|
2013-07-10 18:13:26 +04:00
|
|
|
sk_mark_napi_id(sk, skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
skb->dev = NULL;
|
|
|
|
|
2006-07-03 11:25:13 +04:00
|
|
|
bh_lock_sock_nested(sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
ret = 0;
|
|
|
|
if (!sock_owned_by_user(sk)) {
|
2006-05-24 05:05:53 +04:00
|
|
|
#ifdef CONFIG_NET_DMA
|
|
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
|
|
|
if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
|
2012-04-05 03:10:46 +04:00
|
|
|
tp->ucopy.dma_chan = net_dma_find_channel();
|
2006-05-24 05:05:53 +04:00
|
|
|
if (tp->ucopy.dma_chan)
|
2005-04-17 02:20:36 +04:00
|
|
|
ret = tcp_v4_do_rcv(sk, skb);
|
2006-05-24 05:05:53 +04:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
if (!tcp_prequeue(sk, skb))
|
2009-05-05 05:01:29 +04:00
|
|
|
ret = tcp_v4_do_rcv(sk, skb);
|
2006-05-24 05:05:53 +04:00
|
|
|
}
|
2012-04-23 03:38:54 +04:00
|
|
|
} else if (unlikely(sk_add_backlog(sk, skb,
|
|
|
|
sk->sk_rcvbuf + sk->sk_sndbuf))) {
|
2010-03-04 21:01:41 +03:00
|
|
|
bh_unlock_sock(sk);
|
2010-03-08 02:21:57 +03:00
|
|
|
NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP);
|
2010-03-04 21:01:41 +03:00
|
|
|
goto discard_and_relse;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
bh_unlock_sock(sk);
|
|
|
|
|
|
|
|
sock_put(sk);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
no_tcp_socket:
|
|
|
|
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
|
|
|
|
goto discard_it;
|
|
|
|
|
|
|
|
if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
|
2013-04-29 12:39:56 +04:00
|
|
|
csum_error:
|
|
|
|
TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS);
|
2005-04-17 02:20:36 +04:00
|
|
|
bad_packet:
|
2008-07-17 07:22:25 +04:00
|
|
|
TCP_INC_STATS_BH(net, TCP_MIB_INERRS);
|
2005-04-17 02:20:36 +04:00
|
|
|
} else {
|
2006-11-15 06:07:45 +03:00
|
|
|
tcp_v4_send_reset(NULL, skb);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
discard_it:
|
|
|
|
/* Discard frame. */
|
|
|
|
kfree_skb(skb);
|
2007-02-09 17:24:47 +03:00
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
discard_and_relse:
|
|
|
|
sock_put(sk);
|
|
|
|
goto discard_it;
|
|
|
|
|
|
|
|
do_time_wait:
|
|
|
|
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
|
2006-10-11 06:41:46 +04:00
|
|
|
inet_twsk_put(inet_twsk(sk));
|
2005-04-17 02:20:36 +04:00
|
|
|
goto discard_it;
|
|
|
|
}
|
|
|
|
|
2013-04-29 12:39:56 +04:00
|
|
|
if (skb->len < (th->doff << 2)) {
|
2006-10-11 06:41:46 +04:00
|
|
|
inet_twsk_put(inet_twsk(sk));
|
2013-04-29 12:39:56 +04:00
|
|
|
goto bad_packet;
|
|
|
|
}
|
|
|
|
if (tcp_checksum_complete(skb)) {
|
|
|
|
inet_twsk_put(inet_twsk(sk));
|
|
|
|
goto csum_error;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2006-10-11 06:41:46 +04:00
|
|
|
switch (tcp_timewait_state_process(inet_twsk(sk), skb, th)) {
|
2005-04-17 02:20:36 +04:00
|
|
|
case TCP_TW_SYN: {
|
2008-03-25 15:47:49 +03:00
|
|
|
struct sock *sk2 = inet_lookup_listener(dev_net(skb->dev),
|
2008-01-31 16:06:40 +03:00
|
|
|
&tcp_hashinfo,
|
2013-01-22 13:50:24 +04:00
|
|
|
iph->saddr, th->source,
|
2007-04-21 09:47:35 +04:00
|
|
|
iph->daddr, th->dest,
|
2005-08-10 07:10:42 +04:00
|
|
|
inet_iif(skb));
|
2005-04-17 02:20:36 +04:00
|
|
|
if (sk2) {
|
2006-10-11 06:41:46 +04:00
|
|
|
inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
|
|
|
|
inet_twsk_put(inet_twsk(sk));
|
2005-04-17 02:20:36 +04:00
|
|
|
sk = sk2;
|
|
|
|
goto process;
|
|
|
|
}
|
|
|
|
/* Fall through to ACK */
|
|
|
|
}
|
|
|
|
case TCP_TW_ACK:
|
|
|
|
tcp_v4_timewait_ack(sk, skb);
|
|
|
|
break;
|
|
|
|
case TCP_TW_RST:
|
|
|
|
goto no_tcp_socket;
|
|
|
|
case TCP_TW_SUCCESS:;
|
|
|
|
}
|
|
|
|
goto discard_it;
|
|
|
|
}
|
|
|
|
|
2010-12-02 05:09:13 +03:00
|
|
|
static struct timewait_sock_ops tcp_timewait_sock_ops = {
|
|
|
|
.twsk_obj_size = sizeof(struct tcp_timewait_sock),
|
|
|
|
.twsk_unique = tcp_twsk_unique,
|
|
|
|
.twsk_destructor= tcp_twsk_destructor,
|
|
|
|
};
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-08-09 18:11:00 +04:00
|
|
|
void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
|
2012-08-06 09:09:33 +04:00
|
|
|
{
|
|
|
|
struct dst_entry *dst = skb_dst(skb);
|
|
|
|
|
|
|
|
dst_hold(dst);
|
|
|
|
sk->sk_rx_dst = dst;
|
|
|
|
inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
|
|
|
|
}
|
2012-08-09 18:11:00 +04:00
|
|
|
EXPORT_SYMBOL(inet_sk_rx_dst_set);
|
2012-08-06 09:09:33 +04:00
|
|
|
|
2009-09-01 23:25:04 +04:00
|
|
|
const struct inet_connection_sock_af_ops ipv4_specific = {
|
2006-03-21 09:48:35 +03:00
|
|
|
.queue_xmit = ip_queue_xmit,
|
|
|
|
.send_check = tcp_v4_send_check,
|
|
|
|
.rebuild_header = inet_sk_rebuild_header,
|
2012-08-06 09:09:33 +04:00
|
|
|
.sk_rx_dst_set = inet_sk_rx_dst_set,
|
2006-03-21 09:48:35 +03:00
|
|
|
.conn_request = tcp_v4_conn_request,
|
|
|
|
.syn_recv_sock = tcp_v4_syn_recv_sock,
|
|
|
|
.net_header_len = sizeof(struct iphdr),
|
|
|
|
.setsockopt = ip_setsockopt,
|
|
|
|
.getsockopt = ip_getsockopt,
|
|
|
|
.addr2sockaddr = inet_csk_addr2sockaddr,
|
|
|
|
.sockaddr_len = sizeof(struct sockaddr_in),
|
[SOCK] proto: Add hashinfo member to struct proto
This way we can remove TCP and DCCP specific versions of
sk->sk_prot->get_port: both v4 and v6 use inet_csk_get_port
sk->sk_prot->hash: inet_hash is directly used, only v6 need
a specific version to deal with mapped sockets
sk->sk_prot->unhash: both v4 and v6 use inet_hash directly
struct inet_connection_sock_af_ops also gets a new member, bind_conflict, so
that inet_csk_get_port can find the per family routine.
Now only the lookup routines receive as a parameter a struct inet_hashtable.
With this we further reuse code, reducing the difference among INET transport
protocols.
Eventually work has to be done on UDP and SCTP to make them share this
infrastructure and get as a bonus inet_diag interfaces so that iproute can be
used with these protocols.
net-2.6/net/ipv4/inet_hashtables.c:
struct proto | +8
struct inet_connection_sock_af_ops | +8
2 structs changed
__inet_hash_nolisten | +18
__inet_hash | -210
inet_put_port | +8
inet_bind_bucket_create | +1
__inet_hash_connect | -8
5 functions changed, 27 bytes added, 218 bytes removed, diff: -191
net-2.6/net/core/sock.c:
proto_seq_show | +3
1 function changed, 3 bytes added, diff: +3
net-2.6/net/ipv4/inet_connection_sock.c:
inet_csk_get_port | +15
1 function changed, 15 bytes added, diff: +15
net-2.6/net/ipv4/tcp.c:
tcp_set_state | -7
1 function changed, 7 bytes removed, diff: -7
net-2.6/net/ipv4/tcp_ipv4.c:
tcp_v4_get_port | -31
tcp_v4_hash | -48
tcp_v4_destroy_sock | -7
tcp_v4_syn_recv_sock | -2
tcp_unhash | -179
5 functions changed, 267 bytes removed, diff: -267
net-2.6/net/ipv6/inet6_hashtables.c:
__inet6_hash | +8
1 function changed, 8 bytes added, diff: +8
net-2.6/net/ipv4/inet_hashtables.c:
inet_unhash | +190
inet_hash | +242
2 functions changed, 432 bytes added, diff: +432
vmlinux:
16 functions changed, 485 bytes added, 492 bytes removed, diff: -7
/home/acme/git/net-2.6/net/ipv6/tcp_ipv6.c:
tcp_v6_get_port | -31
tcp_v6_hash | -7
tcp_v6_syn_recv_sock | -9
3 functions changed, 47 bytes removed, diff: -47
/home/acme/git/net-2.6/net/dccp/proto.c:
dccp_destroy_sock | -7
dccp_unhash | -179
dccp_hash | -49
dccp_set_state | -7
dccp_done | +1
5 functions changed, 1 bytes added, 242 bytes removed, diff: -241
/home/acme/git/net-2.6/net/dccp/ipv4.c:
dccp_v4_get_port | -31
dccp_v4_request_recv_sock | -2
2 functions changed, 33 bytes removed, diff: -33
/home/acme/git/net-2.6/net/dccp/ipv6.c:
dccp_v6_get_port | -31
dccp_v6_hash | -7
dccp_v6_request_recv_sock | +5
3 functions changed, 5 bytes added, 38 bytes removed, diff: -33
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-02-03 15:06:04 +03:00
|
|
|
.bind_conflict = inet_csk_bind_conflict,
|
2006-03-21 09:45:21 +03:00
|
|
|
#ifdef CONFIG_COMPAT
|
2006-03-21 09:48:35 +03:00
|
|
|
.compat_setsockopt = compat_ip_setsockopt,
|
|
|
|
.compat_getsockopt = compat_ip_getsockopt,
|
2006-03-21 09:45:21 +03:00
|
|
|
#endif
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
2010-07-10 01:22:10 +04:00
|
|
|
EXPORT_SYMBOL(ipv4_specific);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
2009-09-01 23:25:03 +04:00
|
|
|
static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
|
2006-11-15 06:07:45 +03:00
|
|
|
.md5_lookup = tcp_v4_md5_lookup,
|
2008-07-19 11:01:42 +04:00
|
|
|
.calc_md5_hash = tcp_v4_md5_hash_skb,
|
2006-11-15 06:07:45 +03:00
|
|
|
.md5_parse = tcp_v4_parse_md5_keys,
|
|
|
|
};
|
2006-12-01 06:16:28 +03:00
|
|
|
#endif
|
2006-11-15 06:07:45 +03:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* NOTE: A lot of things set to zero explicitly by call to
|
|
|
|
* sk_alloc() so need not be done here.
|
|
|
|
*/
|
|
|
|
static int tcp_v4_init_sock(struct sock *sk)
|
|
|
|
{
|
2005-08-10 11:03:31 +04:00
|
|
|
struct inet_connection_sock *icsk = inet_csk(sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-04-19 13:55:21 +04:00
|
|
|
tcp_init_sock(sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-12-14 10:15:52 +03:00
|
|
|
icsk->icsk_af_ops = &ipv4_specific;
|
2012-04-19 13:55:21 +04:00
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
2012-04-23 11:21:58 +04:00
|
|
|
tcp_sk(sk)->af_specific = &tcp_sock_ipv4_specific;
|
2006-11-15 06:07:45 +03:00
|
|
|
#endif
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-06-15 04:04:49 +04:00
|
|
|
void tcp_v4_destroy_sock(struct sock *sk)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
|
|
|
|
|
|
|
tcp_clear_xmit_timers(sk);
|
|
|
|
|
2005-08-10 11:03:31 +04:00
|
|
|
tcp_cleanup_congestion_control(sk);
|
2005-06-23 23:19:55 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Cleanup up the write buffer. */
|
2007-03-07 23:12:44 +03:00
|
|
|
tcp_write_queue_purge(sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* Cleans up our, hopefully empty, out_of_order_queue. */
|
2007-02-09 17:24:47 +03:00
|
|
|
__skb_queue_purge(&tp->out_of_order_queue);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-11-15 06:07:45 +03:00
|
|
|
#ifdef CONFIG_TCP_MD5SIG
|
|
|
|
/* Clean up the MD5 key list, if any */
|
|
|
|
if (tp->md5sig_info) {
|
2012-01-31 09:18:33 +04:00
|
|
|
tcp_clear_md5_list(sk);
|
2012-01-31 22:45:40 +04:00
|
|
|
kfree_rcu(tp->md5sig_info, rcu);
|
2006-11-15 06:07:45 +03:00
|
|
|
tp->md5sig_info = NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-05-24 05:05:53 +04:00
|
|
|
#ifdef CONFIG_NET_DMA
|
|
|
|
/* Cleans up our sk_async_wait_queue */
|
2007-02-09 17:24:47 +03:00
|
|
|
__skb_queue_purge(&sk->sk_async_wait_queue);
|
2006-05-24 05:05:53 +04:00
|
|
|
#endif
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* Clean prequeue, it must be empty really */
|
|
|
|
__skb_queue_purge(&tp->ucopy.prequeue);
|
|
|
|
|
|
|
|
/* Clean up a referenced TCP bind bucket. */
|
2005-08-10 07:10:42 +04:00
|
|
|
if (inet_csk(sk)->icsk_bind_hash)
|
[SOCK] proto: Add hashinfo member to struct proto
This way we can remove TCP and DCCP specific versions of
sk->sk_prot->get_port: both v4 and v6 use inet_csk_get_port
sk->sk_prot->hash: inet_hash is directly used, only v6 need
a specific version to deal with mapped sockets
sk->sk_prot->unhash: both v4 and v6 use inet_hash directly
struct inet_connection_sock_af_ops also gets a new member, bind_conflict, so
that inet_csk_get_port can find the per family routine.
Now only the lookup routines receive as a parameter a struct inet_hashtable.
With this we further reuse code, reducing the difference among INET transport
protocols.
Eventually work has to be done on UDP and SCTP to make them share this
infrastructure and get as a bonus inet_diag interfaces so that iproute can be
used with these protocols.
net-2.6/net/ipv4/inet_hashtables.c:
struct proto | +8
struct inet_connection_sock_af_ops | +8
2 structs changed
__inet_hash_nolisten | +18
__inet_hash | -210
inet_put_port | +8
inet_bind_bucket_create | +1
__inet_hash_connect | -8
5 functions changed, 27 bytes added, 218 bytes removed, diff: -191
net-2.6/net/core/sock.c:
proto_seq_show | +3
1 function changed, 3 bytes added, diff: +3
net-2.6/net/ipv4/inet_connection_sock.c:
inet_csk_get_port | +15
1 function changed, 15 bytes added, diff: +15
net-2.6/net/ipv4/tcp.c:
tcp_set_state | -7
1 function changed, 7 bytes removed, diff: -7
net-2.6/net/ipv4/tcp_ipv4.c:
tcp_v4_get_port | -31
tcp_v4_hash | -48
tcp_v4_destroy_sock | -7
tcp_v4_syn_recv_sock | -2
tcp_unhash | -179
5 functions changed, 267 bytes removed, diff: -267
net-2.6/net/ipv6/inet6_hashtables.c:
__inet6_hash | +8
1 function changed, 8 bytes added, diff: +8
net-2.6/net/ipv4/inet_hashtables.c:
inet_unhash | +190
inet_hash | +242
2 functions changed, 432 bytes added, diff: +432
vmlinux:
16 functions changed, 485 bytes added, 492 bytes removed, diff: -7
/home/acme/git/net-2.6/net/ipv6/tcp_ipv6.c:
tcp_v6_get_port | -31
tcp_v6_hash | -7
tcp_v6_syn_recv_sock | -9
3 functions changed, 47 bytes removed, diff: -47
/home/acme/git/net-2.6/net/dccp/proto.c:
dccp_destroy_sock | -7
dccp_unhash | -179
dccp_hash | -49
dccp_set_state | -7
dccp_done | +1
5 functions changed, 1 bytes added, 242 bytes removed, diff: -241
/home/acme/git/net-2.6/net/dccp/ipv4.c:
dccp_v4_get_port | -31
dccp_v4_request_recv_sock | -2
2 functions changed, 33 bytes removed, diff: -33
/home/acme/git/net-2.6/net/dccp/ipv6.c:
dccp_v6_get_port | -31
dccp_v6_hash | -7
dccp_v6_request_recv_sock | +5
3 functions changed, 5 bytes added, 38 bytes removed, diff: -33
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-02-03 15:06:04 +03:00
|
|
|
inet_put_port(sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-08-31 16:29:13 +04:00
|
|
|
BUG_ON(tp->fastopen_rsk != NULL);
|
TCPCT part 1d: define TCP cookie option, extend existing struct's
Data structures are carefully composed to require minimal additions.
For example, the struct tcp_options_received cookie_plus variable fits
between existing 16-bit and 8-bit variables, requiring no additional
space (taking alignment into consideration). There are no additions to
tcp_request_sock, and only 1 pointer in tcp_sock.
This is a significantly revised implementation of an earlier (year-old)
patch that no longer applies cleanly, with permission of the original
author (Adam Langley):
http://thread.gmane.org/gmane.linux.network/102586
The principle difference is using a TCP option to carry the cookie nonce,
instead of a user configured offset in the data. This is more flexible and
less subject to user configuration error. Such a cookie option has been
suggested for many years, and is also useful without SYN data, allowing
several related concepts to use the same extension option.
"Re: SYN floods (was: does history repeat itself?)", September 9, 1996.
http://www.merit.net/mail.archives/nanog/1996-09/msg00235.html
"Re: what a new TCP header might look like", May 12, 1998.
ftp://ftp.isi.edu/end2end/end2end-interest-1998.mail
These functions will also be used in subsequent patches that implement
additional features.
Requires:
TCPCT part 1a: add request_values parameter for sending SYNACK
TCPCT part 1b: generate Responder Cookie secret
TCPCT part 1c: sysctl_tcp_cookie_size, socket option TCP_COOKIE_TRANSACTIONS
Signed-off-by: William.Allen.Simpson@gmail.com
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-12-02 21:17:05 +03:00
|
|
|
|
2012-07-19 10:43:09 +04:00
|
|
|
/* If socket is aborted during connect operation */
|
|
|
|
tcp_free_fastopen_req(tp);
|
|
|
|
|
2011-12-12 01:47:02 +04:00
|
|
|
sk_sockets_allocated_dec(sk);
|
2011-12-12 01:47:04 +04:00
|
|
|
sock_release_memcg(sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tcp_v4_destroy_sock);
|
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
/* Proc filesystem TCP sock list dumping. */
|
|
|
|
|
2010-06-07 11:43:42 +04:00
|
|
|
/*
|
|
|
|
* Get next listener socket follow cur. If cur is NULL, get first socket
|
|
|
|
* starting from bucket given in st->bucket; when st->bucket is zero the
|
|
|
|
* very first socket in the hash table is returned.
|
|
|
|
*/
|
2005-04-17 02:20:36 +04:00
|
|
|
static void *listening_get_next(struct seq_file *seq, void *cur)
|
|
|
|
{
|
2005-08-10 07:10:42 +04:00
|
|
|
struct inet_connection_sock *icsk;
|
2008-11-24 04:22:55 +03:00
|
|
|
struct hlist_nulls_node *node;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct sock *sk = cur;
|
2008-11-20 11:40:07 +03:00
|
|
|
struct inet_listen_hashbucket *ilb;
|
2008-11-03 13:49:10 +03:00
|
|
|
struct tcp_iter_state *st = seq->private;
|
2008-04-14 09:11:14 +04:00
|
|
|
struct net *net = seq_file_net(seq);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (!sk) {
|
2010-06-07 11:43:42 +04:00
|
|
|
ilb = &tcp_hashinfo.listening_hash[st->bucket];
|
2008-11-20 11:40:07 +03:00
|
|
|
spin_lock_bh(&ilb->lock);
|
2008-11-24 04:22:55 +03:00
|
|
|
sk = sk_nulls_head(&ilb->head);
|
2010-06-07 11:43:42 +04:00
|
|
|
st->offset = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
goto get_sk;
|
|
|
|
}
|
2008-11-20 11:40:07 +03:00
|
|
|
ilb = &tcp_hashinfo.listening_hash[st->bucket];
|
2005-04-17 02:20:36 +04:00
|
|
|
++st->num;
|
2010-06-07 11:43:42 +04:00
|
|
|
++st->offset;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (st->state == TCP_SEQ_STATE_OPENREQ) {
|
2005-06-19 09:47:21 +04:00
|
|
|
struct request_sock *req = cur;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-11-16 13:30:37 +03:00
|
|
|
icsk = inet_csk(st->syn_wait_sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
req = req->dl_next;
|
|
|
|
while (1) {
|
|
|
|
while (req) {
|
2008-07-19 11:15:13 +04:00
|
|
|
if (req->rsk_ops->family == st->family) {
|
2005-04-17 02:20:36 +04:00
|
|
|
cur = req;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
req = req->dl_next;
|
|
|
|
}
|
2006-11-16 13:30:37 +03:00
|
|
|
if (++st->sbucket >= icsk->icsk_accept_queue.listen_opt->nr_table_entries)
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
get_req:
|
2005-08-10 07:10:42 +04:00
|
|
|
req = icsk->icsk_accept_queue.listen_opt->syn_table[st->sbucket];
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2010-12-23 20:32:46 +03:00
|
|
|
sk = sk_nulls_next(st->syn_wait_sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
st->state = TCP_SEQ_STATE_LISTENING;
|
2005-08-10 07:10:42 +04:00
|
|
|
read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
} else {
|
2007-02-09 17:24:47 +03:00
|
|
|
icsk = inet_csk(sk);
|
2005-08-10 07:10:42 +04:00
|
|
|
read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
|
|
|
|
if (reqsk_queue_len(&icsk->icsk_accept_queue))
|
2005-04-17 02:20:36 +04:00
|
|
|
goto start_req;
|
2005-08-10 07:10:42 +04:00
|
|
|
read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
|
2010-12-23 20:32:46 +03:00
|
|
|
sk = sk_nulls_next(sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
get_sk:
|
2008-11-24 04:22:55 +03:00
|
|
|
sk_nulls_for_each_from(sk, node) {
|
2010-11-22 06:26:12 +03:00
|
|
|
if (!net_eq(sock_net(sk), net))
|
|
|
|
continue;
|
|
|
|
if (sk->sk_family == st->family) {
|
2005-04-17 02:20:36 +04:00
|
|
|
cur = sk;
|
|
|
|
goto out;
|
|
|
|
}
|
2007-02-09 17:24:47 +03:00
|
|
|
icsk = inet_csk(sk);
|
2005-08-10 07:10:42 +04:00
|
|
|
read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
|
|
|
|
if (reqsk_queue_len(&icsk->icsk_accept_queue)) {
|
2005-04-17 02:20:36 +04:00
|
|
|
start_req:
|
|
|
|
st->uid = sock_i_uid(sk);
|
|
|
|
st->syn_wait_sk = sk;
|
|
|
|
st->state = TCP_SEQ_STATE_OPENREQ;
|
|
|
|
st->sbucket = 0;
|
|
|
|
goto get_req;
|
|
|
|
}
|
2005-08-10 07:10:42 +04:00
|
|
|
read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2008-11-20 11:40:07 +03:00
|
|
|
spin_unlock_bh(&ilb->lock);
|
2010-06-07 11:43:42 +04:00
|
|
|
st->offset = 0;
|
2005-08-10 06:59:44 +04:00
|
|
|
if (++st->bucket < INET_LHTABLE_SIZE) {
|
2008-11-20 11:40:07 +03:00
|
|
|
ilb = &tcp_hashinfo.listening_hash[st->bucket];
|
|
|
|
spin_lock_bh(&ilb->lock);
|
2008-11-24 04:22:55 +03:00
|
|
|
sk = sk_nulls_head(&ilb->head);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto get_sk;
|
|
|
|
}
|
|
|
|
cur = NULL;
|
|
|
|
out:
|
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *listening_get_idx(struct seq_file *seq, loff_t *pos)
|
|
|
|
{
|
2010-06-07 11:43:42 +04:00
|
|
|
struct tcp_iter_state *st = seq->private;
|
|
|
|
void *rc;
|
|
|
|
|
|
|
|
st->bucket = 0;
|
|
|
|
st->offset = 0;
|
|
|
|
rc = listening_get_next(seq, NULL);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
while (rc && *pos) {
|
|
|
|
rc = listening_get_next(seq, rc);
|
|
|
|
--*pos;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 11:22:02 +04:00
|
|
|
static inline bool empty_bucket(const struct tcp_iter_state *st)
|
2008-08-28 12:08:02 +04:00
|
|
|
{
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 11:22:02 +04:00
|
|
|
return hlist_nulls_empty(&tcp_hashinfo.ehash[st->bucket].chain);
|
2008-08-28 12:08:02 +04:00
|
|
|
}
|
|
|
|
|
2010-06-07 11:43:42 +04:00
|
|
|
/*
|
|
|
|
* Get first established socket starting from bucket given in st->bucket.
|
|
|
|
* If st->bucket is zero, the very first socket in the hash is returned.
|
|
|
|
*/
|
2005-04-17 02:20:36 +04:00
|
|
|
static void *established_get_first(struct seq_file *seq)
|
|
|
|
{
|
2008-11-03 13:49:10 +03:00
|
|
|
struct tcp_iter_state *st = seq->private;
|
2008-04-14 09:11:14 +04:00
|
|
|
struct net *net = seq_file_net(seq);
|
2005-04-17 02:20:36 +04:00
|
|
|
void *rc = NULL;
|
|
|
|
|
2010-06-07 11:43:42 +04:00
|
|
|
st->offset = 0;
|
|
|
|
for (; st->bucket <= tcp_hashinfo.ehash_mask; ++st->bucket) {
|
2005-04-17 02:20:36 +04:00
|
|
|
struct sock *sk;
|
2008-11-17 06:40:17 +03:00
|
|
|
struct hlist_nulls_node *node;
|
2008-11-21 07:39:09 +03:00
|
|
|
spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, st->bucket);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-08-28 12:08:02 +04:00
|
|
|
/* Lockless fast path for the common case of empty buckets */
|
|
|
|
if (empty_bucket(st))
|
|
|
|
continue;
|
|
|
|
|
2008-11-21 07:39:09 +03:00
|
|
|
spin_lock_bh(lock);
|
2008-11-17 06:40:17 +03:00
|
|
|
sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[st->bucket].chain) {
|
2008-03-21 14:13:54 +03:00
|
|
|
if (sk->sk_family != st->family ||
|
2008-03-25 21:57:35 +03:00
|
|
|
!net_eq(sock_net(sk), net)) {
|
2005-04-17 02:20:36 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
rc = sk;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-11-21 07:39:09 +03:00
|
|
|
spin_unlock_bh(lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
out:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *established_get_next(struct seq_file *seq, void *cur)
|
|
|
|
{
|
|
|
|
struct sock *sk = cur;
|
2008-11-17 06:40:17 +03:00
|
|
|
struct hlist_nulls_node *node;
|
2008-11-03 13:49:10 +03:00
|
|
|
struct tcp_iter_state *st = seq->private;
|
2008-04-14 09:11:14 +04:00
|
|
|
struct net *net = seq_file_net(seq);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
++st->num;
|
2010-06-07 11:43:42 +04:00
|
|
|
++st->offset;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 11:22:02 +04:00
|
|
|
sk = sk_nulls_next(sk);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-11-17 06:40:17 +03:00
|
|
|
sk_nulls_for_each_from(sk, node) {
|
2008-03-25 21:57:35 +03:00
|
|
|
if (sk->sk_family == st->family && net_eq(sock_net(sk), net))
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 11:22:02 +04:00
|
|
|
return sk;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 11:22:02 +04:00
|
|
|
spin_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
|
|
|
|
++st->bucket;
|
|
|
|
return established_get_first(seq);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *established_get_idx(struct seq_file *seq, loff_t pos)
|
|
|
|
{
|
2010-06-07 11:43:42 +04:00
|
|
|
struct tcp_iter_state *st = seq->private;
|
|
|
|
void *rc;
|
|
|
|
|
|
|
|
st->bucket = 0;
|
|
|
|
rc = established_get_first(seq);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
while (rc && pos) {
|
|
|
|
rc = established_get_next(seq, rc);
|
|
|
|
--pos;
|
2006-11-17 15:57:30 +03:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *tcp_get_idx(struct seq_file *seq, loff_t pos)
|
|
|
|
{
|
|
|
|
void *rc;
|
2008-11-03 13:49:10 +03:00
|
|
|
struct tcp_iter_state *st = seq->private;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
st->state = TCP_SEQ_STATE_LISTENING;
|
|
|
|
rc = listening_get_idx(seq, &pos);
|
|
|
|
|
|
|
|
if (!rc) {
|
|
|
|
st->state = TCP_SEQ_STATE_ESTABLISHED;
|
|
|
|
rc = established_get_idx(seq, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2010-06-07 11:43:42 +04:00
|
|
|
static void *tcp_seek_last_pos(struct seq_file *seq)
|
|
|
|
{
|
|
|
|
struct tcp_iter_state *st = seq->private;
|
|
|
|
int offset = st->offset;
|
|
|
|
int orig_num = st->num;
|
|
|
|
void *rc = NULL;
|
|
|
|
|
|
|
|
switch (st->state) {
|
|
|
|
case TCP_SEQ_STATE_OPENREQ:
|
|
|
|
case TCP_SEQ_STATE_LISTENING:
|
|
|
|
if (st->bucket >= INET_LHTABLE_SIZE)
|
|
|
|
break;
|
|
|
|
st->state = TCP_SEQ_STATE_LISTENING;
|
|
|
|
rc = listening_get_next(seq, NULL);
|
|
|
|
while (offset-- && rc)
|
|
|
|
rc = listening_get_next(seq, rc);
|
|
|
|
if (rc)
|
|
|
|
break;
|
|
|
|
st->bucket = 0;
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 11:22:02 +04:00
|
|
|
st->state = TCP_SEQ_STATE_ESTABLISHED;
|
2010-06-07 11:43:42 +04:00
|
|
|
/* Fallthrough */
|
|
|
|
case TCP_SEQ_STATE_ESTABLISHED:
|
|
|
|
if (st->bucket > tcp_hashinfo.ehash_mask)
|
|
|
|
break;
|
|
|
|
rc = established_get_first(seq);
|
|
|
|
while (offset-- && rc)
|
|
|
|
rc = established_get_next(seq, rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
st->num = orig_num;
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static void *tcp_seq_start(struct seq_file *seq, loff_t *pos)
|
|
|
|
{
|
2008-11-03 13:49:10 +03:00
|
|
|
struct tcp_iter_state *st = seq->private;
|
2010-06-07 11:43:42 +04:00
|
|
|
void *rc;
|
|
|
|
|
|
|
|
if (*pos && *pos == st->last_pos) {
|
|
|
|
rc = tcp_seek_last_pos(seq);
|
|
|
|
if (rc)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
st->state = TCP_SEQ_STATE_LISTENING;
|
|
|
|
st->num = 0;
|
2010-06-07 11:43:42 +04:00
|
|
|
st->bucket = 0;
|
|
|
|
st->offset = 0;
|
|
|
|
rc = *pos ? tcp_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
|
|
|
|
|
|
|
|
out:
|
|
|
|
st->last_pos = *pos;
|
|
|
|
return rc;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *tcp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
|
|
{
|
2010-06-07 11:43:42 +04:00
|
|
|
struct tcp_iter_state *st = seq->private;
|
2005-04-17 02:20:36 +04:00
|
|
|
void *rc = NULL;
|
|
|
|
|
|
|
|
if (v == SEQ_START_TOKEN) {
|
|
|
|
rc = tcp_get_idx(seq, 0);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (st->state) {
|
|
|
|
case TCP_SEQ_STATE_OPENREQ:
|
|
|
|
case TCP_SEQ_STATE_LISTENING:
|
|
|
|
rc = listening_get_next(seq, v);
|
|
|
|
if (!rc) {
|
|
|
|
st->state = TCP_SEQ_STATE_ESTABLISHED;
|
2010-06-07 11:43:42 +04:00
|
|
|
st->bucket = 0;
|
|
|
|
st->offset = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
rc = established_get_first(seq);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TCP_SEQ_STATE_ESTABLISHED:
|
|
|
|
rc = established_get_next(seq, v);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
++*pos;
|
2010-06-07 11:43:42 +04:00
|
|
|
st->last_pos = *pos;
|
2005-04-17 02:20:36 +04:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tcp_seq_stop(struct seq_file *seq, void *v)
|
|
|
|
{
|
2008-11-03 13:49:10 +03:00
|
|
|
struct tcp_iter_state *st = seq->private;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
switch (st->state) {
|
|
|
|
case TCP_SEQ_STATE_OPENREQ:
|
|
|
|
if (v) {
|
2005-08-10 07:10:42 +04:00
|
|
|
struct inet_connection_sock *icsk = inet_csk(st->syn_wait_sk);
|
|
|
|
read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
case TCP_SEQ_STATE_LISTENING:
|
|
|
|
if (v != SEQ_START_TOKEN)
|
2008-11-20 11:40:07 +03:00
|
|
|
spin_unlock_bh(&tcp_hashinfo.listening_hash[st->bucket].lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
case TCP_SEQ_STATE_ESTABLISHED:
|
|
|
|
if (v)
|
2008-11-21 07:39:09 +03:00
|
|
|
spin_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-30 10:46:30 +04:00
|
|
|
int tcp_seq_open(struct inode *inode, struct file *file)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2013-04-01 02:16:14 +04:00
|
|
|
struct tcp_seq_afinfo *afinfo = PDE_DATA(inode);
|
2005-04-17 02:20:36 +04:00
|
|
|
struct tcp_iter_state *s;
|
2008-04-14 09:12:41 +04:00
|
|
|
int err;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-04-14 09:12:41 +04:00
|
|
|
err = seq_open_net(inode, file, &afinfo->seq_ops,
|
|
|
|
sizeof(struct tcp_iter_state));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2008-03-21 14:13:54 +03:00
|
|
|
|
2008-04-14 09:12:41 +04:00
|
|
|
s = ((struct seq_file *)file->private_data)->private;
|
2005-04-17 02:20:36 +04:00
|
|
|
s->family = afinfo->family;
|
2010-06-07 11:43:42 +04:00
|
|
|
s->last_pos = 0;
|
2008-03-21 14:13:54 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2011-10-30 10:46:30 +04:00
|
|
|
EXPORT_SYMBOL(tcp_seq_open);
|
2008-03-21 14:13:54 +03:00
|
|
|
|
2008-03-21 14:14:45 +03:00
|
|
|
int tcp_proc_register(struct net *net, struct tcp_seq_afinfo *afinfo)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
struct proc_dir_entry *p;
|
|
|
|
|
2008-04-14 09:12:13 +04:00
|
|
|
afinfo->seq_ops.start = tcp_seq_start;
|
|
|
|
afinfo->seq_ops.next = tcp_seq_next;
|
|
|
|
afinfo->seq_ops.stop = tcp_seq_stop;
|
|
|
|
|
2008-05-02 15:10:08 +04:00
|
|
|
p = proc_create_data(afinfo->name, S_IRUGO, net->proc_net,
|
2011-10-30 10:46:30 +04:00
|
|
|
afinfo->seq_fops, afinfo);
|
2008-05-02 15:10:08 +04:00
|
|
|
if (!p)
|
2005-04-17 02:20:36 +04:00
|
|
|
rc = -ENOMEM;
|
|
|
|
return rc;
|
|
|
|
}
|
2010-07-10 01:22:10 +04:00
|
|
|
EXPORT_SYMBOL(tcp_proc_register);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-03-21 14:14:45 +03:00
|
|
|
void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2013-02-18 05:34:56 +04:00
|
|
|
remove_proc_entry(afinfo->name, net->proc_net);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2010-07-10 01:22:10 +04:00
|
|
|
EXPORT_SYMBOL(tcp_proc_unregister);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2011-10-21 13:22:42 +04:00
|
|
|
static void get_openreq4(const struct sock *sk, const struct request_sock *req,
|
2013-11-15 02:31:57 +04:00
|
|
|
struct seq_file *f, int i, kuid_t uid)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
|
|
|
const struct inet_request_sock *ireq = inet_rsk(req);
|
2012-08-09 01:13:53 +04:00
|
|
|
long delta = req->expires - jiffies;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-04-24 12:02:16 +04:00
|
|
|
seq_printf(f, "%4d: %08X:%04X %08X:%04X"
|
2013-11-15 02:31:57 +04:00
|
|
|
" %02X %08X:%08X %02X:%08lX %08X %5u %8d %u %d %pK",
|
2005-04-17 02:20:36 +04:00
|
|
|
i,
|
2013-10-10 02:21:29 +04:00
|
|
|
ireq->ir_loc_addr,
|
2009-10-15 10:30:45 +04:00
|
|
|
ntohs(inet_sk(sk)->inet_sport),
|
2013-10-10 02:21:29 +04:00
|
|
|
ireq->ir_rmt_addr,
|
|
|
|
ntohs(ireq->ir_rmt_port),
|
2005-04-17 02:20:36 +04:00
|
|
|
TCP_SYN_RECV,
|
|
|
|
0, 0, /* could print option size, but that is af dependent. */
|
|
|
|
1, /* timers active (only the expire timer) */
|
2012-08-09 01:13:53 +04:00
|
|
|
jiffies_delta_to_clock_t(delta),
|
2012-10-28 03:16:46 +04:00
|
|
|
req->num_timeout,
|
2012-05-24 11:10:10 +04:00
|
|
|
from_kuid_munged(seq_user_ns(f), uid),
|
2005-04-17 02:20:36 +04:00
|
|
|
0, /* non standard timer */
|
|
|
|
0, /* open_requests have no inode */
|
|
|
|
atomic_read(&sk->sk_refcnt),
|
2013-11-15 02:31:57 +04:00
|
|
|
req);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2013-11-15 02:31:57 +04:00
|
|
|
static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int timer_active;
|
|
|
|
unsigned long timer_expires;
|
2011-10-21 13:22:42 +04:00
|
|
|
const struct tcp_sock *tp = tcp_sk(sk);
|
2007-02-22 12:13:58 +03:00
|
|
|
const struct inet_connection_sock *icsk = inet_csk(sk);
|
2011-10-21 13:22:42 +04:00
|
|
|
const struct inet_sock *inet = inet_sk(sk);
|
2012-08-31 16:29:13 +04:00
|
|
|
struct fastopen_queue *fastopenq = icsk->icsk_accept_queue.fastopenq;
|
2009-10-15 10:30:45 +04:00
|
|
|
__be32 dest = inet->inet_daddr;
|
|
|
|
__be32 src = inet->inet_rcv_saddr;
|
|
|
|
__u16 destp = ntohs(inet->inet_dport);
|
|
|
|
__u16 srcp = ntohs(inet->inet_sport);
|
2009-12-04 03:06:13 +03:00
|
|
|
int rx_queue;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
tcp: Tail loss probe (TLP)
This patch series implement the Tail loss probe (TLP) algorithm described
in http://tools.ietf.org/html/draft-dukkipati-tcpm-tcp-loss-probe-01. The
first patch implements the basic algorithm.
TLP's goal is to reduce tail latency of short transactions. It achieves
this by converting retransmission timeouts (RTOs) occuring due
to tail losses (losses at end of transactions) into fast recovery.
TLP transmits one packet in two round-trips when a connection is in
Open state and isn't receiving any ACKs. The transmitted packet, aka
loss probe, can be either new or a retransmission. When there is tail
loss, the ACK from a loss probe triggers FACK/early-retransmit based
fast recovery, thus avoiding a costly RTO. In the absence of loss,
there is no change in the connection state.
PTO stands for probe timeout. It is a timer event indicating
that an ACK is overdue and triggers a loss probe packet. The PTO value
is set to max(2*SRTT, 10ms) and is adjusted to account for delayed
ACK timer when there is only one oustanding packet.
TLP Algorithm
On transmission of new data in Open state:
-> packets_out > 1: schedule PTO in max(2*SRTT, 10ms).
-> packets_out == 1: schedule PTO in max(2*RTT, 1.5*RTT + 200ms)
-> PTO = min(PTO, RTO)
Conditions for scheduling PTO:
-> Connection is in Open state.
-> Connection is either cwnd limited or no new data to send.
-> Number of probes per tail loss episode is limited to one.
-> Connection is SACK enabled.
When PTO fires:
new_segment_exists:
-> transmit new segment.
-> packets_out++. cwnd remains same.
no_new_packet:
-> retransmit the last segment.
Its ACK triggers FACK or early retransmit based recovery.
ACK path:
-> rearm RTO at start of ACK processing.
-> reschedule PTO if need be.
In addition, the patch includes a small variation to the Early Retransmit
(ER) algorithm, such that ER and TLP together can in principle recover any
N-degree of tail loss through fast recovery. TLP is controlled by the same
sysctl as ER, tcp_early_retrans sysctl.
tcp_early_retrans==0; disables TLP and ER.
==1; enables RFC5827 ER.
==2; delayed ER.
==3; TLP and delayed ER. [DEFAULT]
==4; TLP only.
The TLP patch series have been extensively tested on Google Web servers.
It is most effective for short Web trasactions, where it reduced RTOs by 15%
and improved HTTP response time (average by 6%, 99th percentile by 10%).
The transmitted probes account for <0.5% of the overall transmissions.
Signed-off-by: Nandita Dukkipati <nanditad@google.com>
Acked-by: Neal Cardwell <ncardwell@google.com>
Acked-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-03-11 14:00:43 +04:00
|
|
|
if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
|
|
|
|
icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
|
|
|
|
icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
|
2005-04-17 02:20:36 +04:00
|
|
|
timer_active = 1;
|
2005-08-10 07:10:42 +04:00
|
|
|
timer_expires = icsk->icsk_timeout;
|
|
|
|
} else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
|
2005-04-17 02:20:36 +04:00
|
|
|
timer_active = 4;
|
2005-08-10 07:10:42 +04:00
|
|
|
timer_expires = icsk->icsk_timeout;
|
2007-02-22 12:13:58 +03:00
|
|
|
} else if (timer_pending(&sk->sk_timer)) {
|
2005-04-17 02:20:36 +04:00
|
|
|
timer_active = 2;
|
2007-02-22 12:13:58 +03:00
|
|
|
timer_expires = sk->sk_timer.expires;
|
2005-04-17 02:20:36 +04:00
|
|
|
} else {
|
|
|
|
timer_active = 0;
|
|
|
|
timer_expires = jiffies;
|
|
|
|
}
|
|
|
|
|
2009-12-04 03:06:13 +03:00
|
|
|
if (sk->sk_state == TCP_LISTEN)
|
|
|
|
rx_queue = sk->sk_ack_backlog;
|
|
|
|
else
|
|
|
|
/*
|
|
|
|
* because we dont lock socket, we might find a transient negative value
|
|
|
|
*/
|
|
|
|
rx_queue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
|
|
|
|
|
2008-04-24 12:02:16 +04:00
|
|
|
seq_printf(f, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX "
|
2013-11-15 02:31:57 +04:00
|
|
|
"%08X %5u %8d %lu %d %pK %lu %lu %u %u %d",
|
2007-02-22 12:13:58 +03:00
|
|
|
i, src, srcp, dest, destp, sk->sk_state,
|
2006-06-28 00:29:00 +04:00
|
|
|
tp->write_seq - tp->snd_una,
|
2009-12-04 03:06:13 +03:00
|
|
|
rx_queue,
|
2005-04-17 02:20:36 +04:00
|
|
|
timer_active,
|
2012-08-09 01:13:53 +04:00
|
|
|
jiffies_delta_to_clock_t(timer_expires - jiffies),
|
2005-08-10 07:10:42 +04:00
|
|
|
icsk->icsk_retransmits,
|
2012-05-24 11:10:10 +04:00
|
|
|
from_kuid_munged(seq_user_ns(f), sock_i_uid(sk)),
|
2005-08-10 11:03:31 +04:00
|
|
|
icsk->icsk_probes_out,
|
2007-02-22 12:13:58 +03:00
|
|
|
sock_i_ino(sk),
|
|
|
|
atomic_read(&sk->sk_refcnt), sk,
|
2008-06-28 07:00:19 +04:00
|
|
|
jiffies_to_clock_t(icsk->icsk_rto),
|
|
|
|
jiffies_to_clock_t(icsk->icsk_ack.ato),
|
2005-08-10 07:10:42 +04:00
|
|
|
(icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong,
|
2005-04-17 02:20:36 +04:00
|
|
|
tp->snd_cwnd,
|
2012-08-31 16:29:13 +04:00
|
|
|
sk->sk_state == TCP_LISTEN ?
|
|
|
|
(fastopenq ? fastopenq->max_qlen : 0) :
|
2013-11-15 02:31:57 +04:00
|
|
|
(tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh));
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2011-10-21 13:22:42 +04:00
|
|
|
static void get_timewait4_sock(const struct inet_timewait_sock *tw,
|
2013-11-15 02:31:57 +04:00
|
|
|
struct seq_file *f, int i)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2006-09-28 05:43:50 +04:00
|
|
|
__be32 dest, src;
|
2005-04-17 02:20:36 +04:00
|
|
|
__u16 destp, srcp;
|
2014-03-27 18:19:19 +04:00
|
|
|
s32 delta = tw->tw_ttd - inet_tw_time_stamp();
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
dest = tw->tw_daddr;
|
|
|
|
src = tw->tw_rcv_saddr;
|
|
|
|
destp = ntohs(tw->tw_dport);
|
|
|
|
srcp = ntohs(tw->tw_sport);
|
|
|
|
|
2008-04-24 12:02:16 +04:00
|
|
|
seq_printf(f, "%4d: %08X:%04X %08X:%04X"
|
2013-11-15 02:31:57 +04:00
|
|
|
" %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK",
|
2005-04-17 02:20:36 +04:00
|
|
|
i, src, srcp, dest, destp, tw->tw_substate, 0, 0,
|
2012-08-09 01:13:53 +04:00
|
|
|
3, jiffies_delta_to_clock_t(delta), 0, 0, 0, 0,
|
2013-11-15 02:31:57 +04:00
|
|
|
atomic_read(&tw->tw_refcnt), tw);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#define TMPSZ 150
|
|
|
|
|
|
|
|
static int tcp4_seq_show(struct seq_file *seq, void *v)
|
|
|
|
{
|
2008-11-03 13:49:10 +03:00
|
|
|
struct tcp_iter_state *st;
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 11:22:02 +04:00
|
|
|
struct sock *sk = v;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-11-15 02:31:57 +04:00
|
|
|
seq_setwidth(seq, TMPSZ - 1);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (v == SEQ_START_TOKEN) {
|
2013-11-15 02:31:57 +04:00
|
|
|
seq_puts(seq, " sl local_address rem_address st tx_queue "
|
2005-04-17 02:20:36 +04:00
|
|
|
"rx_queue tr tm->when retrnsmt uid timeout "
|
|
|
|
"inode");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
st = seq->private;
|
|
|
|
|
|
|
|
switch (st->state) {
|
|
|
|
case TCP_SEQ_STATE_LISTENING:
|
|
|
|
case TCP_SEQ_STATE_ESTABLISHED:
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 11:22:02 +04:00
|
|
|
if (sk->sk_state == TCP_TIME_WAIT)
|
2013-11-15 02:31:57 +04:00
|
|
|
get_timewait4_sock(v, seq, st->num);
|
tcp/dccp: remove twchain
TCP listener refactoring, part 3 :
Our goal is to hash SYN_RECV sockets into main ehash for fast lookup,
and parallel SYN processing.
Current inet_ehash_bucket contains two chains, one for ESTABLISH (and
friend states) sockets, another for TIME_WAIT sockets only.
As the hash table is sized to get at most one socket per bucket, it
makes little sense to have separate twchain, as it makes the lookup
slightly more complicated, and doubles hash table memory usage.
If we make sure all socket types have the lookup keys at the same
offsets, we can use a generic and faster lookup. It turns out TIME_WAIT
and ESTABLISHED sockets already have common lookup fields for IPv4.
[ INET_TW_MATCH() is no longer needed ]
I'll provide a follow-up to factorize IPv6 lookup as well, to remove
INET6_TW_MATCH()
This way, SYN_RECV pseudo sockets will be supported the same.
A new sock_gen_put() helper is added, doing either a sock_put() or
inet_twsk_put() [ and will support SYN_RECV later ].
Note this helper should only be called in real slow path, when rcu
lookup found a socket that was moved to another identity (freed/reused
immediately), but could eventually be used in other contexts, like
sock_edemux()
Before patch :
dmesg | grep "TCP established"
TCP established hash table entries: 524288 (order: 11, 8388608 bytes)
After patch :
TCP established hash table entries: 524288 (order: 10, 4194304 bytes)
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-10-03 11:22:02 +04:00
|
|
|
else
|
2013-11-15 02:31:57 +04:00
|
|
|
get_tcp4_sock(v, seq, st->num);
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
case TCP_SEQ_STATE_OPENREQ:
|
2013-11-15 02:31:57 +04:00
|
|
|
get_openreq4(st->syn_wait_sk, v, seq, st->num, st->uid);
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
out:
|
2013-11-15 02:31:57 +04:00
|
|
|
seq_pad(seq, '\n');
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-30 10:46:30 +04:00
|
|
|
static const struct file_operations tcp_afinfo_seq_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = tcp_seq_open,
|
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
|
|
|
.release = seq_release_net
|
|
|
|
};
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
static struct tcp_seq_afinfo tcp4_seq_afinfo = {
|
|
|
|
.name = "tcp",
|
|
|
|
.family = AF_INET,
|
2011-10-30 10:46:30 +04:00
|
|
|
.seq_fops = &tcp_afinfo_seq_fops,
|
2008-04-14 09:12:13 +04:00
|
|
|
.seq_ops = {
|
|
|
|
.show = tcp4_seq_show,
|
|
|
|
},
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
2010-01-17 06:35:32 +03:00
|
|
|
static int __net_init tcp4_proc_init_net(struct net *net)
|
2008-03-25 00:56:02 +03:00
|
|
|
{
|
|
|
|
return tcp_proc_register(net, &tcp4_seq_afinfo);
|
|
|
|
}
|
|
|
|
|
2010-01-17 06:35:32 +03:00
|
|
|
static void __net_exit tcp4_proc_exit_net(struct net *net)
|
2008-03-25 00:56:02 +03:00
|
|
|
{
|
|
|
|
tcp_proc_unregister(net, &tcp4_seq_afinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pernet_operations tcp4_net_ops = {
|
|
|
|
.init = tcp4_proc_init_net,
|
|
|
|
.exit = tcp4_proc_exit_net,
|
|
|
|
};
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
int __init tcp4_proc_init(void)
|
|
|
|
{
|
2008-03-25 00:56:02 +03:00
|
|
|
return register_pernet_subsys(&tcp4_net_ops);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void tcp4_proc_exit(void)
|
|
|
|
{
|
2008-03-25 00:56:02 +03:00
|
|
|
unregister_pernet_subsys(&tcp4_net_ops);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
|
|
|
|
struct proto tcp_prot = {
|
|
|
|
.name = "TCP",
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.close = tcp_close,
|
|
|
|
.connect = tcp_v4_connect,
|
|
|
|
.disconnect = tcp_disconnect,
|
2005-08-10 07:10:42 +04:00
|
|
|
.accept = inet_csk_accept,
|
2005-04-17 02:20:36 +04:00
|
|
|
.ioctl = tcp_ioctl,
|
|
|
|
.init = tcp_v4_init_sock,
|
|
|
|
.destroy = tcp_v4_destroy_sock,
|
|
|
|
.shutdown = tcp_shutdown,
|
|
|
|
.setsockopt = tcp_setsockopt,
|
|
|
|
.getsockopt = tcp_getsockopt,
|
|
|
|
.recvmsg = tcp_recvmsg,
|
2010-07-11 00:41:55 +04:00
|
|
|
.sendmsg = tcp_sendmsg,
|
|
|
|
.sendpage = tcp_sendpage,
|
2005-04-17 02:20:36 +04:00
|
|
|
.backlog_rcv = tcp_v4_do_rcv,
|
tcp: TCP Small Queues
This introduce TSQ (TCP Small Queues)
TSQ goal is to reduce number of TCP packets in xmit queues (qdisc &
device queues), to reduce RTT and cwnd bias, part of the bufferbloat
problem.
sk->sk_wmem_alloc not allowed to grow above a given limit,
allowing no more than ~128KB [1] per tcp socket in qdisc/dev layers at a
given time.
TSO packets are sized/capped to half the limit, so that we have two
TSO packets in flight, allowing better bandwidth use.
As a side effect, setting the limit to 40000 automatically reduces the
standard gso max limit (65536) to 40000/2 : It can help to reduce
latencies of high prio packets, having smaller TSO packets.
This means we divert sock_wfree() to a tcp_wfree() handler, to
queue/send following frames when skb_orphan() [2] is called for the
already queued skbs.
Results on my dev machines (tg3/ixgbe nics) are really impressive,
using standard pfifo_fast, and with or without TSO/GSO.
Without reduction of nominal bandwidth, we have reduction of buffering
per bulk sender :
< 1ms on Gbit (instead of 50ms with TSO)
< 8ms on 100Mbit (instead of 132 ms)
I no longer have 4 MBytes backlogged in qdisc by a single netperf
session, and both side socket autotuning no longer use 4 Mbytes.
As skb destructor cannot restart xmit itself ( as qdisc lock might be
taken at this point ), we delegate the work to a tasklet. We use one
tasklest per cpu for performance reasons.
If tasklet finds a socket owned by the user, it sets TSQ_OWNED flag.
This flag is tested in a new protocol method called from release_sock(),
to eventually send new segments.
[1] New /proc/sys/net/ipv4/tcp_limit_output_bytes tunable
[2] skb_orphan() is usually called at TX completion time,
but some drivers call it in their start_xmit() handler.
These drivers should at least use BQL, or else a single TCP
session can still fill the whole NIC TX ring, since TSQ will
have no effect.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Dave Taht <dave.taht@bufferbloat.net>
Cc: Tom Herbert <therbert@google.com>
Cc: Matt Mathis <mattmathis@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Cc: Nandita Dukkipati <nanditad@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-07-11 09:50:31 +04:00
|
|
|
.release_cb = tcp_release_cb,
|
2012-07-23 11:48:52 +04:00
|
|
|
.mtu_reduced = tcp_v4_mtu_reduced,
|
[SOCK] proto: Add hashinfo member to struct proto
This way we can remove TCP and DCCP specific versions of
sk->sk_prot->get_port: both v4 and v6 use inet_csk_get_port
sk->sk_prot->hash: inet_hash is directly used, only v6 need
a specific version to deal with mapped sockets
sk->sk_prot->unhash: both v4 and v6 use inet_hash directly
struct inet_connection_sock_af_ops also gets a new member, bind_conflict, so
that inet_csk_get_port can find the per family routine.
Now only the lookup routines receive as a parameter a struct inet_hashtable.
With this we further reuse code, reducing the difference among INET transport
protocols.
Eventually work has to be done on UDP and SCTP to make them share this
infrastructure and get as a bonus inet_diag interfaces so that iproute can be
used with these protocols.
net-2.6/net/ipv4/inet_hashtables.c:
struct proto | +8
struct inet_connection_sock_af_ops | +8
2 structs changed
__inet_hash_nolisten | +18
__inet_hash | -210
inet_put_port | +8
inet_bind_bucket_create | +1
__inet_hash_connect | -8
5 functions changed, 27 bytes added, 218 bytes removed, diff: -191
net-2.6/net/core/sock.c:
proto_seq_show | +3
1 function changed, 3 bytes added, diff: +3
net-2.6/net/ipv4/inet_connection_sock.c:
inet_csk_get_port | +15
1 function changed, 15 bytes added, diff: +15
net-2.6/net/ipv4/tcp.c:
tcp_set_state | -7
1 function changed, 7 bytes removed, diff: -7
net-2.6/net/ipv4/tcp_ipv4.c:
tcp_v4_get_port | -31
tcp_v4_hash | -48
tcp_v4_destroy_sock | -7
tcp_v4_syn_recv_sock | -2
tcp_unhash | -179
5 functions changed, 267 bytes removed, diff: -267
net-2.6/net/ipv6/inet6_hashtables.c:
__inet6_hash | +8
1 function changed, 8 bytes added, diff: +8
net-2.6/net/ipv4/inet_hashtables.c:
inet_unhash | +190
inet_hash | +242
2 functions changed, 432 bytes added, diff: +432
vmlinux:
16 functions changed, 485 bytes added, 492 bytes removed, diff: -7
/home/acme/git/net-2.6/net/ipv6/tcp_ipv6.c:
tcp_v6_get_port | -31
tcp_v6_hash | -7
tcp_v6_syn_recv_sock | -9
3 functions changed, 47 bytes removed, diff: -47
/home/acme/git/net-2.6/net/dccp/proto.c:
dccp_destroy_sock | -7
dccp_unhash | -179
dccp_hash | -49
dccp_set_state | -7
dccp_done | +1
5 functions changed, 1 bytes added, 242 bytes removed, diff: -241
/home/acme/git/net-2.6/net/dccp/ipv4.c:
dccp_v4_get_port | -31
dccp_v4_request_recv_sock | -2
2 functions changed, 33 bytes removed, diff: -33
/home/acme/git/net-2.6/net/dccp/ipv6.c:
dccp_v6_get_port | -31
dccp_v6_hash | -7
dccp_v6_request_recv_sock | +5
3 functions changed, 5 bytes added, 38 bytes removed, diff: -33
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-02-03 15:06:04 +03:00
|
|
|
.hash = inet_hash,
|
|
|
|
.unhash = inet_unhash,
|
|
|
|
.get_port = inet_csk_get_port,
|
2005-04-17 02:20:36 +04:00
|
|
|
.enter_memory_pressure = tcp_enter_memory_pressure,
|
tcp: TCP_NOTSENT_LOWAT socket option
Idea of this patch is to add optional limitation of number of
unsent bytes in TCP sockets, to reduce usage of kernel memory.
TCP receiver might announce a big window, and TCP sender autotuning
might allow a large amount of bytes in write queue, but this has little
performance impact if a large part of this buffering is wasted :
Write queue needs to be large only to deal with large BDP, not
necessarily to cope with scheduling delays (incoming ACKS make room
for the application to queue more bytes)
For most workloads, using a value of 128 KB or less is OK to give
applications enough time to react to POLLOUT events in time
(or being awaken in a blocking sendmsg())
This patch adds two ways to set the limit :
1) Per socket option TCP_NOTSENT_LOWAT
2) A sysctl (/proc/sys/net/ipv4/tcp_notsent_lowat) for sockets
not using TCP_NOTSENT_LOWAT socket option (or setting a zero value)
Default value being UINT_MAX (0xFFFFFFFF), meaning this has no effect.
This changes poll()/select()/epoll() to report POLLOUT
only if number of unsent bytes is below tp->nosent_lowat
Note this might increase number of sendmsg()/sendfile() calls
when using non blocking sockets,
and increase number of context switches for blocking sockets.
Note this is not related to SO_SNDLOWAT (as SO_SNDLOWAT is
defined as :
Specify the minimum number of bytes in the buffer until
the socket layer will pass the data to the protocol)
Tested:
netperf sessions, and watching /proc/net/protocols "memory" column for TCP
With 200 concurrent netperf -t TCP_STREAM sessions, amount of kernel memory
used by TCP buffers shrinks by ~55 % (20567 pages instead of 45458)
lpq83:~# echo -1 >/proc/sys/net/ipv4/tcp_notsent_lowat
lpq83:~# (super_netperf 200 -t TCP_STREAM -H remote -l 90 &); sleep 60 ; grep TCP /proc/net/protocols
TCPv6 1880 2 45458 no 208 yes ipv6 y y y y y y y y y y y y y n y y y y y
TCP 1696 508 45458 no 208 yes kernel y y y y y y y y y y y y y n y y y y y
lpq83:~# echo 131072 >/proc/sys/net/ipv4/tcp_notsent_lowat
lpq83:~# (super_netperf 200 -t TCP_STREAM -H remote -l 90 &); sleep 60 ; grep TCP /proc/net/protocols
TCPv6 1880 2 20567 no 208 yes ipv6 y y y y y y y y y y y y y n y y y y y
TCP 1696 508 20567 no 208 yes kernel y y y y y y y y y y y y y n y y y y y
Using 128KB has no bad effect on the throughput or cpu usage
of a single flow, although there is an increase of context switches.
A bonus is that we hold socket lock for a shorter amount
of time and should improve latencies of ACK processing.
lpq83:~# echo -1 >/proc/sys/net/ipv4/tcp_notsent_lowat
lpq83:~# perf stat -e context-switches ./netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3
OMNI Send TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.7.84 () port 0 AF_INET : +/-2.500% @ 99% conf.
Local Remote Local Elapsed Throughput Throughput Local Local Remote Remote Local Remote Service
Send Socket Recv Socket Send Time Units CPU CPU CPU CPU Service Service Demand
Size Size Size (sec) Util Util Util Util Demand Demand Units
Final Final % Method % Method
1651584 6291456 16384 20.00 17447.90 10^6bits/s 3.13 S -1.00 U 0.353 -1.000 usec/KB
Performance counter stats for './netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3':
412,514 context-switches
200.034645535 seconds time elapsed
lpq83:~# echo 131072 >/proc/sys/net/ipv4/tcp_notsent_lowat
lpq83:~# perf stat -e context-switches ./netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3
OMNI Send TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.7.84 () port 0 AF_INET : +/-2.500% @ 99% conf.
Local Remote Local Elapsed Throughput Throughput Local Local Remote Remote Local Remote Service
Send Socket Recv Socket Send Time Units CPU CPU CPU CPU Service Service Demand
Size Size Size (sec) Util Util Util Util Demand Demand Units
Final Final % Method % Method
1593240 6291456 16384 20.00 17321.16 10^6bits/s 3.35 S -1.00 U 0.381 -1.000 usec/KB
Performance counter stats for './netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3':
2,675,818 context-switches
200.029651391 seconds time elapsed
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Acked-By: Yuchung Cheng <ycheng@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-07-23 07:27:07 +04:00
|
|
|
.stream_memory_free = tcp_stream_memory_free,
|
2005-04-17 02:20:36 +04:00
|
|
|
.sockets_allocated = &tcp_sockets_allocated,
|
2005-08-10 07:11:41 +04:00
|
|
|
.orphan_count = &tcp_orphan_count,
|
2005-04-17 02:20:36 +04:00
|
|
|
.memory_allocated = &tcp_memory_allocated,
|
|
|
|
.memory_pressure = &tcp_memory_pressure,
|
2013-10-20 03:25:36 +04:00
|
|
|
.sysctl_mem = sysctl_tcp_mem,
|
2005-04-17 02:20:36 +04:00
|
|
|
.sysctl_wmem = sysctl_tcp_wmem,
|
|
|
|
.sysctl_rmem = sysctl_tcp_rmem,
|
|
|
|
.max_header = MAX_TCP_HEADER,
|
|
|
|
.obj_size = sizeof(struct tcp_sock),
|
2008-11-17 06:40:17 +03:00
|
|
|
.slab_flags = SLAB_DESTROY_BY_RCU,
|
2005-12-14 10:25:19 +03:00
|
|
|
.twsk_prot = &tcp_timewait_sock_ops,
|
2005-06-19 09:47:21 +04:00
|
|
|
.rsk_prot = &tcp_request_sock_ops,
|
2008-03-23 02:50:58 +03:00
|
|
|
.h.hashinfo = &tcp_hashinfo,
|
2010-07-11 00:41:55 +04:00
|
|
|
.no_autobind = true,
|
2006-03-21 09:48:35 +03:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_setsockopt = compat_tcp_setsockopt,
|
|
|
|
.compat_getsockopt = compat_tcp_getsockopt,
|
|
|
|
#endif
|
2012-08-01 03:43:02 +04:00
|
|
|
#ifdef CONFIG_MEMCG_KMEM
|
2011-12-12 01:47:04 +04:00
|
|
|
.init_cgroup = tcp_init_cgroup,
|
|
|
|
.destroy_cgroup = tcp_destroy_cgroup,
|
|
|
|
.proto_cgroup = tcp_proto_cgroup,
|
|
|
|
#endif
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
2010-07-10 01:22:10 +04:00
|
|
|
EXPORT_SYMBOL(tcp_prot);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-04-04 01:31:33 +04:00
|
|
|
static int __net_init tcp_sk_init(struct net *net)
|
|
|
|
{
|
2013-01-05 20:10:48 +04:00
|
|
|
net->ipv4.sysctl_tcp_ecn = 2;
|
2012-07-19 11:34:03 +04:00
|
|
|
return 0;
|
2008-04-04 01:31:33 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __net_exit tcp_sk_exit(struct net *net)
|
|
|
|
{
|
2009-12-03 05:29:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __net_exit tcp_sk_exit_batch(struct list_head *net_exit_list)
|
|
|
|
{
|
|
|
|
inet_twsk_purge(&tcp_hashinfo, &tcp_death_row, AF_INET);
|
2008-04-04 01:31:33 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct pernet_operations __net_initdata tcp_sk_ops = {
|
2009-12-03 05:29:09 +03:00
|
|
|
.init = tcp_sk_init,
|
|
|
|
.exit = tcp_sk_exit,
|
|
|
|
.exit_batch = tcp_sk_exit_batch,
|
2008-04-04 01:31:33 +04:00
|
|
|
};
|
|
|
|
|
2008-02-29 22:13:15 +03:00
|
|
|
void __init tcp_v4_init(void)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-11-20 11:40:07 +03:00
|
|
|
inet_hashinfo_init(&tcp_hashinfo);
|
2009-02-22 11:10:18 +03:00
|
|
|
if (register_pernet_subsys(&tcp_sk_ops))
|
2005-04-17 02:20:36 +04:00
|
|
|
panic("Failed to create the TCP control socket.\n");
|
|
|
|
}
|