summaryrefslogtreecommitdiff
path: root/kernels/linux-libre-lts-knock/tcp_stealth_4.9_1.diff
diff options
context:
space:
mode:
Diffstat (limited to 'kernels/linux-libre-lts-knock/tcp_stealth_4.9_1.diff')
-rw-r--r--kernels/linux-libre-lts-knock/tcp_stealth_4.9_1.diff642
1 files changed, 642 insertions, 0 deletions
diff --git a/kernels/linux-libre-lts-knock/tcp_stealth_4.9_1.diff b/kernels/linux-libre-lts-knock/tcp_stealth_4.9_1.diff
new file mode 100644
index 000000000..40ca22975
--- /dev/null
+++ b/kernels/linux-libre-lts-knock/tcp_stealth_4.9_1.diff
@@ -0,0 +1,642 @@
+diff --git a/include/linux/tcp.h b/include/linux/tcp.h
+index a17ae7b..e48e86f 100644
+--- a/include/linux/tcp.h
++++ b/include/linux/tcp.h
+@@ -19,6 +19,7 @@
+
+
+ #include <linux/skbuff.h>
++#include <linux/cryptohash.h>
+ #include <linux/win_minmax.h>
+ #include <net/sock.h>
+ #include <net/inet_connection_sock.h>
+@@ -353,6 +354,21 @@ struct tcp_sock {
+ struct tcp_md5sig_info __rcu *md5sig_info;
+ #endif
+
++#ifdef CONFIG_TCP_STEALTH
++/* Stealth TCP socket configuration */
++ struct {
++ #define TCP_STEALTH_MODE_AUTH BIT(0)
++ #define TCP_STEALTH_MODE_INTEGRITY BIT(1)
++ #define TCP_STEALTH_MODE_INTEGRITY_LEN BIT(2)
++ u8 mode;
++ u8 secret[MD5_MESSAGE_BYTES];
++ u16 integrity_hash;
++ size_t integrity_len;
++ struct skb_mstamp mstamp;
++ bool saw_tsval;
++ } stealth;
++#endif
++
+ /* TCP fastopen related information */
+ struct tcp_fastopen_request *fastopen_req;
+ /* fastopen_rsk points to request_sock that resulted in this big
+diff --git a/include/net/secure_seq.h b/include/net/secure_seq.h
+index 3f36d45..ec392a1 100644
+--- a/include/net/secure_seq.h
++++ b/include/net/secure_seq.h
+@@ -14,5 +14,10 @@ u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport);
+ u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
+ __be16 sport, __be16 dport);
++#ifdef CONFIG_TCP_STEALTH
++u32 tcp_stealth_do_auth(struct sock *sk, struct sk_buff *skb);
++u32 tcp_stealth_sequence_number(struct sock *sk, __be32 *daddr,
++ u32 daddr_size, __be16 dport);
++#endif
+
+ #endif /* _NET_SECURE_SEQ */
+diff --git a/include/net/tcp.h b/include/net/tcp.h
+index 123979f..f7a0d7c 100644
+--- a/include/net/tcp.h
++++ b/include/net/tcp.h
+@@ -429,6 +429,12 @@ void tcp_parse_options(const struct sk_buff *skb,
+ struct tcp_options_received *opt_rx,
+ int estab, struct tcp_fastopen_cookie *foc);
+ const u8 *tcp_parse_md5sig_option(const struct tcphdr *th);
++#ifdef CONFIG_TCP_STEALTH
++const bool tcp_parse_tsval_option(u32 *tsval, const struct tcphdr *th);
++int tcp_stealth_integrity(u16 *hash, u8 *secret, u8 *payload, int len);
++#define be32_isn_to_be16_av(x) (((__be16 *)&x)[0])
++#define be32_isn_to_be16_ih(x) (((__be16 *)&x)[1])
++#endif
+
+ /*
+ * TCP v4 functions exported for the inet6 API
+diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
+index 73ac0db..d2a35e9 100644
+--- a/include/uapi/linux/tcp.h
++++ b/include/uapi/linux/tcp.h
+@@ -116,6 +116,9 @@ enum {
+ #define TCP_SAVE_SYN 27 /* Record SYN headers for new connections */
+ #define TCP_SAVED_SYN 28 /* Get SYN headers recorded for connection */
+ #define TCP_REPAIR_WINDOW 29 /* Get/set window parameters */
++#define TCP_STEALTH 30
++#define TCP_STEALTH_INTEGRITY 31
++#define TCP_STEALTH_INTEGRITY_LEN 32
+
+ struct tcp_repair_opt {
+ __u32 opt_code;
+diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c
+index fd3ce46..2d49cee 100644
+--- a/net/core/secure_seq.c
++++ b/net/core/secure_seq.c
+@@ -8,7 +8,11 @@
+ #include <linux/ktime.h>
+ #include <linux/string.h>
+ #include <linux/net.h>
++#include <linux/socket.h>
++#include <linux/ip.h>
++#include <linux/ipv6.h>
+
++#include <net/tcp.h>
+ #include <net/secure_seq.h>
+
+ #if IS_ENABLED(CONFIG_IPV6) || IS_ENABLED(CONFIG_INET)
+@@ -39,6 +43,102 @@ static u32 seq_scale(u32 seq)
+ }
+ #endif
+
++#ifdef CONFIG_TCP_STEALTH
++u32 tcp_stealth_sequence_number(struct sock *sk, __be32 *daddr,
++ u32 daddr_size, __be16 dport)
++{
++ struct tcp_sock *tp = tcp_sk(sk);
++ struct tcp_md5sig_key *md5;
++
++ __u32 sec[MD5_MESSAGE_BYTES / sizeof(__u32)];
++ __u32 i;
++ __u32 tsval = 0;
++
++ __be32 iv[MD5_DIGEST_WORDS] = { 0 };
++ __be32 isn;
++
++ memcpy(iv, daddr, (daddr_size > sizeof(iv)) ? sizeof(iv) : daddr_size);
++
++#ifdef CONFIG_TCP_MD5SIG
++ md5 = tp->af_specific->md5_lookup(sk, sk);
++#else
++ md5 = NULL;
++#endif
++ if (likely(sysctl_tcp_timestamps && !md5) || tp->stealth.saw_tsval)
++ tsval = tp->stealth.mstamp.stamp_jiffies;
++
++ ((__be16 *)iv)[2] ^= cpu_to_be16(tp->stealth.integrity_hash);
++ iv[2] ^= cpu_to_be32(tsval);
++ ((__be16 *)iv)[6] ^= dport;
++
++ for (i = 0; i < MD5_DIGEST_WORDS; i++)
++ iv[i] = le32_to_cpu(iv[i]);
++ for (i = 0; i < MD5_MESSAGE_BYTES / sizeof(__le32); i++)
++ sec[i] = le32_to_cpu(((__le32 *)tp->stealth.secret)[i]);
++
++ md5_transform(iv, sec);
++
++ isn = cpu_to_be32(iv[0]) ^ cpu_to_be32(iv[1]) ^
++ cpu_to_be32(iv[2]) ^ cpu_to_be32(iv[3]);
++
++ if (tp->stealth.mode & TCP_STEALTH_MODE_INTEGRITY)
++ be32_isn_to_be16_ih(isn) =
++ cpu_to_be16(tp->stealth.integrity_hash);
++
++ return be32_to_cpu(isn);
++}
++EXPORT_SYMBOL(tcp_stealth_sequence_number);
++
++u32 tcp_stealth_do_auth(struct sock *sk, struct sk_buff *skb)
++{
++ struct tcp_sock *tp = tcp_sk(sk);
++ struct tcphdr *th = tcp_hdr(skb);
++ __be32 isn = th->seq;
++ __be32 hash;
++ __be32 *daddr;
++ u32 daddr_size;
++
++ tp->stealth.saw_tsval =
++ tcp_parse_tsval_option(&tp->stealth.mstamp.stamp_jiffies, th);
++
++ if (tp->stealth.mode & TCP_STEALTH_MODE_INTEGRITY_LEN)
++ tp->stealth.integrity_hash =
++ be16_to_cpu(be32_isn_to_be16_ih(isn));
++
++ switch (tp->inet_conn.icsk_inet.sk.sk_family) {
++#if IS_ENABLED(CONFIG_IPV6)
++ case PF_INET6:
++ daddr_size = sizeof(ipv6_hdr(skb)->daddr.s6_addr32);
++ daddr = ipv6_hdr(skb)->daddr.s6_addr32;
++ break;
++#endif
++ case PF_INET:
++ daddr_size = sizeof(ip_hdr(skb)->daddr);
++ daddr = &ip_hdr(skb)->daddr;
++ break;
++ default:
++ pr_err("TCP Stealth: Unknown network layer protocol, stop!\n");
++ return 1;
++ }
++
++ hash = tcp_stealth_sequence_number(sk, daddr, daddr_size, th->dest);
++ cpu_to_be32s(&hash);
++
++ if (tp->stealth.mode & TCP_STEALTH_MODE_AUTH &&
++ tp->stealth.mode & TCP_STEALTH_MODE_INTEGRITY_LEN &&
++ be32_isn_to_be16_av(isn) == be32_isn_to_be16_av(hash))
++ return 0;
++
++ if (tp->stealth.mode & TCP_STEALTH_MODE_AUTH &&
++ !(tp->stealth.mode & TCP_STEALTH_MODE_INTEGRITY_LEN) &&
++ isn == hash)
++ return 0;
++
++ return 1;
++}
++EXPORT_SYMBOL(tcp_stealth_do_auth);
++#endif
++
+ #if IS_ENABLED(CONFIG_IPV6)
+ __u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr,
+ __be16 sport, __be16 dport)
+diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
+index b54b3ca..9ec6794 100644
+--- a/net/ipv4/Kconfig
++++ b/net/ipv4/Kconfig
+@@ -728,3 +728,13 @@ config TCP_MD5SIG
+ on the Internet.
+
+ If unsure, say N.
++
++config TCP_STEALTH
++ bool "TCP: Stealth TCP socket support"
++ default n
++ ---help---
++ This option enables support for stealth TCP sockets. If you do not
++ know what this means, you do not need it.
++
++ If unsure, say N.
++
+diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
+index 814af89..c9f2ba5 100644
+--- a/net/ipv4/tcp.c
++++ b/net/ipv4/tcp.c
+@@ -269,6 +269,7 @@
+ #include <linux/err.h>
+ #include <linux/time.h>
+ #include <linux/slab.h>
++#include <linux/vmalloc.h>
+
+ #include <net/icmp.h>
+ #include <net/inet_common.h>
+@@ -2386,6 +2387,49 @@ static int tcp_repair_options_est(struct tcp_sock *tp,
+ return 0;
+ }
+
++#ifdef CONFIG_TCP_STEALTH
++int tcp_stealth_integrity(__be16 *hash, u8 *secret, u8 *payload, int len)
++{
++ struct scatterlist sg[2];
++ struct crypto_ahash *tfm;
++ struct ahash_request *req;
++ __be16 h[MD5_DIGEST_WORDS * 2];
++ int i;
++ int err = 0;
++
++ tfm = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC);
++ if (IS_ERR(tfm)) {
++ err = -PTR_ERR(tfm);
++ goto out;
++ }
++ req = ahash_request_alloc(tfm, GFP_ATOMIC);
++ if (!req)
++ err = -EFAULT;
++ goto out;
++
++ sg_init_table(sg, 2);
++ sg_set_buf(&sg[0], secret, MD5_MESSAGE_BYTES);
++ sg_set_buf(&sg[1], payload, len);
++
++ ahash_request_set_callback(req, 0, NULL, NULL);
++ ahash_request_set_crypt(req, sg, (u8 *)h, MD5_MESSAGE_BYTES + len);
++
++ if (crypto_ahash_digest(req)) {
++ err = -EFAULT;
++ goto out;
++ }
++
++ *hash = be16_to_cpu(h[0]);
++ for (i = 1; i < MD5_DIGEST_WORDS * 2; i++)
++ *hash ^= be16_to_cpu(h[i]);
++
++out:
++ ahash_request_free(req);
++ crypto_free_ahash(tfm);
++ return err;
++}
++#endif
++
+ /*
+ * Socket option code for TCP.
+ */
+@@ -2417,6 +2461,66 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
+ release_sock(sk);
+ return err;
+ }
++#ifdef CONFIG_TCP_STEALTH
++ case TCP_STEALTH: {
++ u8 secret[MD5_MESSAGE_BYTES] = { 0 };
++
++ val = copy_from_user(secret, optval,
++ min_t(unsigned int, optlen,
++ MD5_MESSAGE_BYTES));
++ if (val != 0)
++ return -EFAULT;
++
++ lock_sock(sk);
++ memcpy(tp->stealth.secret, secret, MD5_MESSAGE_BYTES);
++ tp->stealth.mode = TCP_STEALTH_MODE_AUTH;
++ tp->stealth.mstamp.v64 = 0;
++ tp->stealth.saw_tsval = false;
++ release_sock(sk);
++ return err;
++ }
++ case TCP_STEALTH_INTEGRITY: {
++ u8 *payload;
++
++ lock_sock(sk);
++
++ if (!(tp->stealth.mode & TCP_STEALTH_MODE_AUTH)) {
++ err = -EOPNOTSUPP;
++ goto stealth_integrity_out_1;
++ }
++
++ if (optlen < 1 || optlen > USHRT_MAX) {
++ err = -EINVAL;
++ goto stealth_integrity_out_1;
++ }
++
++ payload = vmalloc(optlen);
++ if (!payload) {
++ err = -ENOMEM;
++ goto stealth_integrity_out_1;
++ }
++
++ val = copy_from_user(payload, optval, optlen);
++ if (val != 0) {
++ err = -EFAULT;
++ goto stealth_integrity_out_2;
++ }
++
++ err = tcp_stealth_integrity(&tp->stealth.integrity_hash,
++ tp->stealth.secret, payload,
++ optlen);
++ if (err)
++ goto stealth_integrity_out_2;
++
++ tp->stealth.mode |= TCP_STEALTH_MODE_INTEGRITY;
++
++stealth_integrity_out_2:
++ vfree(payload);
++stealth_integrity_out_1:
++ release_sock(sk);
++ return err;
++ }
++#endif
+ default:
+ /* fallthru */
+ break;
+@@ -2671,6 +2775,18 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
+ tp->notsent_lowat = val;
+ sk->sk_write_space(sk);
+ break;
++#ifdef CONFIG_TCP_STEALTH
++ case TCP_STEALTH_INTEGRITY_LEN:
++ if (!(tp->stealth.mode & TCP_STEALTH_MODE_AUTH)) {
++ err = -EOPNOTSUPP;
++ } else if (val < 1 || val > USHRT_MAX) {
++ err = -EINVAL;
++ } else {
++ tp->stealth.integrity_len = val;
++ tp->stealth.mode |= TCP_STEALTH_MODE_INTEGRITY_LEN;
++ }
++ break;
++#endif
+ default:
+ err = -ENOPROTOOPT;
+ break;
+diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
+index c71d49c..24f6d73 100644
+--- a/net/ipv4/tcp_input.c
++++ b/net/ipv4/tcp_input.c
+@@ -77,6 +77,9 @@
+ #include <linux/errqueue.h>
+
+ int sysctl_tcp_timestamps __read_mostly = 1;
++#ifdef CONFIG_TCP_STEALTH
++EXPORT_SYMBOL(sysctl_tcp_timestamps);
++#endif
+ int sysctl_tcp_window_scaling __read_mostly = 1;
+ int sysctl_tcp_sack __read_mostly = 1;
+ int sysctl_tcp_fack __read_mostly = 1;
+@@ -3926,6 +3929,47 @@ static bool tcp_fast_parse_options(const struct sk_buff *skb,
+ return true;
+ }
+
++#ifdef CONFIG_TCP_STEALTH
++/* Parse only the TSVal field of the TCP Timestamp option header.
++ */
++const bool tcp_parse_tsval_option(u32 *tsval, const struct tcphdr *th)
++{
++ int length = (th->doff << 2) - sizeof(*th);
++ const u8 *ptr = (const u8 *)(th + 1);
++
++ /* If the TCP option is too short, we can short cut */
++ if (length < TCPOLEN_TIMESTAMP)
++ return false;
++
++ while (length > 0) {
++ int opcode = *ptr++;
++ int opsize;
++
++ switch (opcode) {
++ case TCPOPT_EOL:
++ return false;
++ case TCPOPT_NOP:
++ length--;
++ continue;
++ case TCPOPT_TIMESTAMP:
++ opsize = *ptr++;
++ if (opsize != TCPOLEN_TIMESTAMP || opsize > length)
++ return false;
++ *tsval = get_unaligned_be32(ptr);
++ return true;
++ default:
++ opsize = *ptr++;
++ if (opsize < 2 || opsize > length)
++ return false;
++ }
++ ptr += opsize - 2;
++ length -= opsize;
++ }
++ return false;
++}
++EXPORT_SYMBOL(tcp_parse_tsval_option);
++#endif
++
+ #ifdef CONFIG_TCP_MD5SIG
+ /*
+ * Parse MD5 Signature option
+@@ -4631,6 +4675,31 @@ err:
+
+ }
+
++#ifdef CONFIG_TCP_STEALTH
++static int __tcp_stealth_integrity_check(struct sock *sk, struct sk_buff *skb)
++{
++ struct tcphdr *th = tcp_hdr(skb);
++ struct tcp_sock *tp = tcp_sk(sk);
++ u16 hash;
++ __be32 seq = cpu_to_be32(TCP_SKB_CB(skb)->seq - 1);
++ char *data = skb->data + th->doff * 4;
++ int len = skb->len - th->doff * 4;
++
++ if (len < tp->stealth.integrity_len)
++ return 1;
++
++ if (tcp_stealth_integrity(&hash, tp->stealth.secret, data,
++ tp->stealth.integrity_len))
++ return 1;
++
++ if (be32_isn_to_be16_ih(seq) != cpu_to_be16(hash))
++ return 1;
++
++ tp->stealth.mode &= ~TCP_STEALTH_MODE_INTEGRITY_LEN;
++ return 0;
++}
++#endif
++
+ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
+ {
+ struct tcp_sock *tp = tcp_sk(sk);
+@@ -4641,6 +4710,15 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
+ __kfree_skb(skb);
+ return;
+ }
++
++#ifdef CONFIG_TCP_STEALTH
++ if (unlikely(tp->stealth.mode & TCP_STEALTH_MODE_INTEGRITY_LEN) &&
++ __tcp_stealth_integrity_check(sk, skb)) {
++ tcp_reset(sk);
++ goto drop;
++ }
++#endif
++
+ skb_dst_drop(skb);
+ __skb_pull(skb, tcp_hdr(skb)->doff * 4);
+
+@@ -5456,6 +5534,15 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
+ int eaten = 0;
+ bool fragstolen = false;
+
++#ifdef CONFIG_TCP_STEALTH
++ if (unlikely(tp->stealth.mode &
++ TCP_STEALTH_MODE_INTEGRITY_LEN) &&
++ __tcp_stealth_integrity_check(sk, skb)) {
++ tcp_reset(sk);
++ goto discard;
++ }
++#endif
++
+ if (tp->ucopy.task == current &&
+ tp->copied_seq == tp->rcv_nxt &&
+ len - tcp_header_len <= tp->ucopy.len &&
+diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
+index 2259114..542da7a 100644
+--- a/net/ipv4/tcp_ipv4.c
++++ b/net/ipv4/tcp_ipv4.c
+@@ -74,6 +74,7 @@
+ #include <net/xfrm.h>
+ #include <net/secure_seq.h>
+ #include <net/busy_poll.h>
++#include <net/secure_seq.h>
+
+ #include <linux/inet.h>
+ #include <linux/ipv6.h>
+@@ -233,6 +234,21 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+ sk->sk_gso_type = SKB_GSO_TCPV4;
+ sk_setup_caps(sk, &rt->dst);
+
++#ifdef CONFIG_TCP_STEALTH
++ /* If CONFIG_TCP_STEALTH is defined, we need to know the timestamp as
++ * early as possible and thus move taking the snapshot of tcp_time_stamp
++ * here.
++ */
++ skb_mstamp_get(&tp->stealth.mstamp);
++
++ if (!tp->write_seq && likely(!tp->repair) &&
++ unlikely(tp->stealth.mode & TCP_STEALTH_MODE_AUTH))
++ tp->write_seq = tcp_stealth_sequence_number(sk,
++ &inet->inet_daddr,
++ sizeof(inet->inet_daddr),
++ usin->sin_port);
++#endif
++
+ if (!tp->write_seq && likely(!tp->repair))
+ tp->write_seq = secure_tcp_sequence_number(inet->inet_saddr,
+ inet->inet_daddr,
+@@ -1382,6 +1398,8 @@ static struct sock *tcp_v4_cookie_check(struct sock *sk, struct sk_buff *skb)
+ */
+ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
+ {
++ struct tcp_sock *tp = tcp_sk(sk);
++ struct tcphdr *th = tcp_hdr(skb);
+ struct sock *rsk;
+
+ if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
+@@ -1403,6 +1421,15 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
+ if (tcp_checksum_complete(skb))
+ goto csum_err;
+
++#ifdef CONFIG_TCP_STEALTH
++ if (sk->sk_state == TCP_LISTEN && th->syn && !th->fin &&
++ unlikely(tp->stealth.mode & TCP_STEALTH_MODE_AUTH) &&
++ tcp_stealth_do_auth(sk, skb)) {
++ rsk = sk;
++ goto reset;
++ }
++#endif
++
+ if (sk->sk_state == TCP_LISTEN) {
+ struct sock *nsk = tcp_v4_cookie_check(sk, skb);
+
+diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
+index 896e9df..7bf3142 100644
+--- a/net/ipv4/tcp_output.c
++++ b/net/ipv4/tcp_output.c
+@@ -939,6 +939,13 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
+ tcb = TCP_SKB_CB(skb);
+ memset(&opts, 0, sizeof(opts));
+
++#ifdef TCP_STEALTH
++ if (unlikely(tcb->tcp_flags & TCPHDR_SYN &&
++ tp->stealth.mode & TCP_STEALTH_MODE_AUTH)) {
++ skb->skb_mstamp = tp->stealth.mstamp;
++ }
++#endif
++
+ if (unlikely(tcb->tcp_flags & TCPHDR_SYN))
+ tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5);
+ else
+@@ -3349,7 +3356,15 @@ int tcp_connect(struct sock *sk)
+ return -ENOBUFS;
+
+ tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
++#ifdef CONFIG_TCP_STEALTH
++ /* The timetamp was already made at the time the ISN was generated
++ * as we need to know its value in the stealth_tcp_sequence_number()
++ * function.
++ */
++ tp->retrans_stamp = tp->stealth.mstamp.stamp_jiffies;
++#else
+ tp->retrans_stamp = tcp_time_stamp;
++#endif
+ tcp_connect_queue_skb(sk, buff);
+ tcp_ecn_send_syn(sk, buff);
+
+diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
+index b9f1fee..b301a49 100644
+--- a/net/ipv6/tcp_ipv6.c
++++ b/net/ipv6/tcp_ipv6.c
+@@ -62,6 +62,7 @@
+ #include <net/inet_common.h>
+ #include <net/secure_seq.h>
+ #include <net/busy_poll.h>
++#include <net/secure_seq.h>
+
+ #include <linux/proc_fs.h>
+ #include <linux/seq_file.h>
+@@ -278,6 +279,21 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
+
+ sk_set_txhash(sk);
+
++#ifdef CONFIG_TCP_STEALTH
++ /* If CONFIG_TCP_STEALTH is defined, we need to know the timestamp as
++ * early as possible and thus move taking the snapshot of tcp_time_stamp
++ * here.
++ */
++ skb_mstamp_get(&tp->stealth.mstamp);
++
++ if (!tp->write_seq && likely(!tp->repair) &&
++ unlikely(tp->stealth.mode & TCP_STEALTH_MODE_AUTH))
++ tp->write_seq = tcp_stealth_sequence_number(sk,
++ sk->sk_v6_daddr.s6_addr32,
++ sizeof(sk->sk_v6_daddr),
++ inet->inet_dport);
++#endif
++
+ if (!tp->write_seq && likely(!tp->repair))
+ tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32,
+ sk->sk_v6_daddr.s6_addr32,
+@@ -1215,7 +1231,8 @@ static void tcp_v6_restore_cb(struct sk_buff *skb)
+ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+ {
+ struct ipv6_pinfo *np = inet6_sk(sk);
+- struct tcp_sock *tp;
++ struct tcp_sock *tp = tcp_sk(sk);
++ struct tcphdr *th = tcp_hdr(skb);
+ struct sk_buff *opt_skb = NULL;
+
+ /* Imagine: socket is IPv6. IPv4 packet arrives,
+@@ -1275,6 +1292,13 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+ if (tcp_checksum_complete(skb))
+ goto csum_err;
+
++#ifdef CONFIG_TCP_STEALTH
++ if (sk->sk_state == TCP_LISTEN && th->syn && !th->fin &&
++ tp->stealth.mode & TCP_STEALTH_MODE_AUTH &&
++ tcp_stealth_do_auth(sk, skb))
++ goto reset;
++#endif
++
+ if (sk->sk_state == TCP_LISTEN) {
+ struct sock *nsk = tcp_v6_cookie_check(sk, skb);
+