From patchwork Mon Mar 12 02:48:46 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Herbert X-Patchwork-Id: 146034 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id B8534B6FA4 for ; Mon, 12 Mar 2012 13:48:51 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751180Ab2CLCsu (ORCPT ); Sun, 11 Mar 2012 22:48:50 -0400 Received: from mail-yw0-f74.google.com ([209.85.213.74]:47869 "EHLO mail-yw0-f74.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750770Ab2CLCss (ORCPT ); Sun, 11 Mar 2012 22:48:48 -0400 Received: by yhgm50 with SMTP id m50so815385yhg.1 for ; Sun, 11 Mar 2012 19:48:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=date:from:to:cc:subject:message-id:user-agent:mime-version :content-type; bh=QOfKk8k8AdEl8dkZw52WpPBf/9ACVZ6CFoaeZNUprsQ=; b=GcqmDMI+9Z6o0NWSGEvsuLIhQm6zcPqPkDoijFiERLzL11X81XyvXy6q30+z3IlhQS PTRZ9cjTwOg76br8zi6Pr3N3G1v2rSfB2G2uXLTob8uC5FG2o2bE8GCkoU6d69TzAKBF tbqKowsnKDEJmG5cNG0rUFtWwXCr8LnNPFvLQZ/KCf/zRJvqT2XtoEjMK6VqrudsTxeU q+tbENz3nwY44mZ0CpbnFzIKdOG9YM0fweS1RjqeyUvqsczCaIf8VG4JCfc6/+tjVZCr 3n57qCcIvHhpTLkLI55DJ+ZSerF8SRYZYPTiC6UHCBYe04vu5toCRXn4ucsNtLES+oEC fWbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=date:from:to:cc:subject:message-id:user-agent:mime-version :content-type:x-gm-message-state; bh=QOfKk8k8AdEl8dkZw52WpPBf/9ACVZ6CFoaeZNUprsQ=; b=Fzr8QEH1900TBJI/CfHp9i6e4gQlhvXsizfR+9iTD5+WyWAQSRoP4afqh19rI+Ofz+ dCeCy1m8wF4ytfsIBxuFS+O56bURWqCDLrG2wisj8nFp7jprJcM6PkX9aT4qdYy8V+Ib qX5E3dKkh/pNkGKidCA2btG0NV3GgdWsGM/lcv6tjLkbJ8A/Lqow2IN0Hn8NmgThA24q mO4C++X+YGaz2QWTuHcqsqJg0t+mdHymnkN1rdOeCp8znJiSG3/80JJxUSL08fA9oD6p 74CBEBkwhoSFFx2ONdN2OrzdGyXIZkspkR1ZkysKlKzbLxoHDH6mITwNMUIKBJnxLejn gZdA== Received: by 10.101.152.19 with SMTP id e19mr4312277ano.9.1331520527525; Sun, 11 Mar 2012 19:48:47 -0700 (PDT) Received: by 10.101.152.19 with SMTP id e19mr4312265ano.9.1331520527426; Sun, 11 Mar 2012 19:48:47 -0700 (PDT) Received: from wpzn4.hot.corp.google.com (216-239-44-65.google.com [216.239.44.65]) by gmr-mx.google.com with ESMTPS id d10si3908063anf.1.2012.03.11.19.48.47 (version=TLSv1/SSLv3 cipher=AES128-SHA); Sun, 11 Mar 2012 19:48:47 -0700 (PDT) Received: from pokey.mtv.corp.google.com (pokey.mtv.corp.google.com [172.18.96.23]) by wpzn4.hot.corp.google.com (Postfix) with ESMTP id 43E2E1E004D; Sun, 11 Mar 2012 19:48:47 -0700 (PDT) Received: by pokey.mtv.corp.google.com (Postfix, from userid 60832) id CA31422F04A; Sun, 11 Mar 2012 19:48:46 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by pokey.mtv.corp.google.com (Postfix) with ESMTP id B6EB522EE9F; Sun, 11 Mar 2012 19:48:46 -0700 (PDT) Date: Sun, 11 Mar 2012 19:48:46 -0700 (PDT) From: Tom Herbert To: davem@davemloft.net, netdev@vger.kernel.org cc: eric.dumazet@gmail.com Subject: [PATCH] net: Provide SYN packet for passive connections Message-ID: User-Agent: Alpine 2.00 (DEB 1167 2008-08-23) MIME-Version: 1.0 X-Gm-Message-State: ALoCoQl7AfOrK8utdTWipQ7HtOOxNNqpx2yQwf0HalZqkClOM++BxPlF4KZQNXzIMxcl7gisvzWh2XTAonDQFhX82QrmrPc3g3dVL5ioNXPyasNWrCYes9o8T4l7CbXwFVtZUVl3Rw8A7vF4Ygm2y7RdDQtYH6A/pGX93ShrE2MAKFFYlzQDft4= Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This patch allows a server application to get the TCP SYN packets for its passive connections. This is useful if the server is doing fingerprinting of clients based on SYN packet contents. Two socket options are added: TCP_SAVE_SYN and TCP_SAVED_SYN. The first is used on a listener socket to enable saving the SYN packets for child connections. The latter is used to retrieve the SYN for and accepted connection. TCP_SAVED_SYN is read once, it frees the saved SYN packet. The data returned TCP_SAVED_SYN is the IP header (v4 or v6) through the TCP header. Signed-off-by: Tom Herbert --- include/linux/tcp.h | 2 ++ include/net/inet_connection_sock.h | 31 +++++++++++++++++++++++++++++++ include/net/request_sock.h | 6 +++++- net/ipv4/inet_connection_sock.c | 2 ++ net/ipv4/tcp.c | 32 ++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 2 ++ net/ipv4/tcp_minisocks.c | 2 ++ net/ipv6/tcp_ipv6.c | 2 ++ 8 files changed, 78 insertions(+), 1 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index b6c62d2..1f6bde9 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -106,6 +106,8 @@ enum { #define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/ #define TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */ #define TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */ +#define TCP_SAVE_SYN 19 /* SYN recorded for incoming connection */ +#define TCP_SAVED_SYN 20 /* Return SYN recorded for incoming connection */ /* for TCP_INFO socket option */ #define TCPI_OPT_TIMESTAMPS 1 diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index dbf9aab..16279c3 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -82,6 +82,8 @@ struct inet_connection_sock_af_ops { * @icsk_ext_hdr_len: Network protocol overhead (IP/IPv6 options) * @icsk_ack: Delayed ACK control data * @icsk_mtup; MTU probing control data + * @icsk_save_syn: Save SYN packets in children + * @icsk_saved_syn: SYN packet for passive connection */ struct inet_connection_sock { /* inet_sock has to be the first member! */ @@ -124,6 +126,8 @@ struct inet_connection_sock { /* Information on the current probe. */ int probe_size; } icsk_mtup; + __u8 icsk_save_syn:1; + struct sk_buff *icsk_saved_syn; u32 icsk_ca_priv[16]; u32 icsk_user_timeout; #define ICSK_CA_PRIV_SIZE (16 * sizeof(u32)) @@ -311,11 +315,38 @@ static inline void inet_csk_reqsk_queue_drop(struct sock *sk, reqsk_free(req); } +static inline void inet_csk_reqsk_record_syn(struct sock *sk, + struct request_sock *req, + struct sk_buff *skb) +{ + if (inet_csk(sk)->icsk_save_syn) { + BUG_ON(req->saved_syn); + req->saved_syn = skb_clone(skb, GFP_ATOMIC); + } +} + +static inline void inet_csk_reqsk_move_syn(struct sock *sk, + struct request_sock *req) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + + icsk->icsk_saved_syn = req->saved_syn; + req->saved_syn = NULL; +} + extern void inet_csk_reqsk_queue_prune(struct sock *parent, const unsigned long interval, const unsigned long timeout, const unsigned long max_rto); +static inline void inet_csk_free_syn(struct sock *sk) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + + kfree_skb(icsk->icsk_saved_syn); + icsk->icsk_saved_syn = NULL; +} + extern void inet_csk_destroy_sock(struct sock *sk); /* diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 4c0766e..1cf8fd1 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -65,20 +65,24 @@ struct request_sock { struct sock *sk; u32 secid; u32 peer_secid; + struct sk_buff *saved_syn; }; static inline struct request_sock *reqsk_alloc(const struct request_sock_ops *ops) { struct request_sock *req = kmem_cache_alloc(ops->slab, GFP_ATOMIC); - if (req != NULL) + if (req != NULL) { + req->saved_syn = NULL; req->rsk_ops = ops; + } return req; } static inline void __reqsk_free(struct request_sock *req) { + kfree_skb(req->saved_syn); kmem_cache_free(req->rsk_ops->slab, req); } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 19d66ce..c5f1cd3 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -646,6 +646,8 @@ void inet_csk_destroy_sock(struct sock *sk) /* If it has not 0 inet_sk(sk)->inet_num, it must be bound */ WARN_ON(inet_sk(sk)->inet_num && !inet_csk(sk)->icsk_bind_hash); + inet_csk_free_syn(sk); + sk->sk_prot->destroy(sk); sk_stream_kill_queues(sk); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 22ef5f9..6725561 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2352,6 +2352,13 @@ static int do_tcp_setsockopt(struct sock *sk, int level, icsk->icsk_syn_retries = val; break; + case TCP_SAVE_SYN: + if (val < 0 || val > 1) + err = -EINVAL; + else + icsk->icsk_save_syn = val; + break; + case TCP_LINGER2: if (val < 0) tp->linger2 = -1; @@ -2632,6 +2639,31 @@ static int do_tcp_getsockopt(struct sock *sk, int level, case TCP_USER_TIMEOUT: val = jiffies_to_msecs(icsk->icsk_user_timeout); break; + case TCP_SAVE_SYN: + val = icsk->icsk_save_syn; + break; + case TCP_SAVED_SYN: { + if (get_user(len, optlen)) + return -EFAULT; + + if (icsk->icsk_saved_syn) { + struct sk_buff *skb = icsk->icsk_saved_syn; + void *b = skb_network_header(skb); + void *e = (void *)tcp_hdr(skb) + tcp_hdrlen(skb); + + len = min_t(unsigned int, e - b, len); + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, b, len)) + return -EFAULT; + inet_csk_free_syn(sk); + } else { + len = 0; + if (put_user(len, optlen)) + return -EFAULT; + } + return 0; + } default: return -ENOPROTOOPT; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 507924b..6e265af 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1414,6 +1414,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) want_cookie) goto drop_and_free; + inet_csk_reqsk_record_syn(sk, req, skb); + inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); return 0; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 3cabafb..b2ba421 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -554,6 +554,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->rx_opt.mss_clamp = req->mss; TCP_ECN_openreq_child(newtp, req); + inet_csk_reqsk_move_syn(newsk, req); + TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_PASSIVEOPENS); } return newsk; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 12c6ece..9b3efb8 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1219,6 +1219,8 @@ have_isn: want_cookie) goto drop_and_free; + inet_csk_reqsk_record_syn(sk, req, skb); + inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); return 0;