u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
 u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
                               __be16 dport);
-u32 secure_tcp_seq_and_tsoff(__be32 saddr, __be32 daddr,
-                            __be16 sport, __be16 dport, u32 *tsoff);
-u32 secure_tcpv6_seq_and_tsoff(const __be32 *saddr, const __be32 *daddr,
-                              __be16 sport, __be16 dport, u32 *tsoff);
+u32 secure_tcp_seq(__be32 saddr, __be32 daddr,
+                  __be16 sport, __be16 dport);
+u32 secure_tcp_ts_off(__be32 saddr, __be32 daddr);
+u32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
+                    __be16 sport, __be16 dport);
+u32 secure_tcpv6_ts_off(const __be32 *saddr, const __be32 *daddr);
 u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr,
                                __be16 sport, __be16 dport);
 u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr,
 
 /* From syncookies.c */
 struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
                                 struct request_sock *req,
-                                struct dst_entry *dst);
+                                struct dst_entry *dst, u32 tsoff);
 int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th,
                      u32 cookie);
 struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb);
 #endif
        struct dst_entry *(*route_req)(const struct sock *sk, struct flowi *fl,
                                       const struct request_sock *req);
-       __u32 (*init_seq_tsoff)(const struct sk_buff *skb, u32 *tsoff);
+       u32 (*init_seq)(const struct sk_buff *skb);
+       u32 (*init_ts_off)(const struct sk_buff *skb);
        int (*send_synack)(const struct sock *sk, struct dst_entry *dst,
                           struct flowi *fl, struct request_sock *req,
                           struct tcp_fastopen_cookie *foc,
 
 
 static __always_inline void net_secret_init(void)
 {
-       net_get_random_once(&ts_secret, sizeof(ts_secret));
        net_get_random_once(&net_secret, sizeof(net_secret));
 }
+
+static __always_inline void ts_secret_init(void)
+{
+       net_get_random_once(&ts_secret, sizeof(ts_secret));
+}
 #endif
 
 #ifdef CONFIG_INET
 #endif
 
 #if IS_ENABLED(CONFIG_IPV6)
-static u32 secure_tcpv6_ts_off(const __be32 *saddr, const __be32 *daddr)
+u32 secure_tcpv6_ts_off(const __be32 *saddr, const __be32 *daddr)
 {
        const struct {
                struct in6_addr saddr;
        if (sysctl_tcp_timestamps != 1)
                return 0;
 
+       ts_secret_init();
        return siphash(&combined, offsetofend(typeof(combined), daddr),
                       &ts_secret);
 }
+EXPORT_SYMBOL(secure_tcpv6_ts_off);
 
-u32 secure_tcpv6_seq_and_tsoff(const __be32 *saddr, const __be32 *daddr,
-                              __be16 sport, __be16 dport, u32 *tsoff)
+u32 secure_tcpv6_seq(const __be32 *saddr, const __be32 *daddr,
+                    __be16 sport, __be16 dport)
 {
        const struct {
                struct in6_addr saddr;
                .sport = sport,
                .dport = dport
        };
-       u64 hash;
+       u32 hash;
+
        net_secret_init();
        hash = siphash(&combined, offsetofend(typeof(combined), dport),
                       &net_secret);
-       *tsoff = secure_tcpv6_ts_off(saddr, daddr);
        return seq_scale(hash);
 }
-EXPORT_SYMBOL(secure_tcpv6_seq_and_tsoff);
+EXPORT_SYMBOL(secure_tcpv6_seq);
 
 u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
                               __be16 dport)
 #endif
 
 #ifdef CONFIG_INET
-static u32 secure_tcp_ts_off(__be32 saddr, __be32 daddr)
+u32 secure_tcp_ts_off(__be32 saddr, __be32 daddr)
 {
        if (sysctl_tcp_timestamps != 1)
                return 0;
 
+       ts_secret_init();
        return siphash_2u32((__force u32)saddr, (__force u32)daddr,
                            &ts_secret);
 }
  * it would be easy enough to have the former function use siphash_4u32, passing
  * the arguments as separate u32.
  */
-u32 secure_tcp_seq_and_tsoff(__be32 saddr, __be32 daddr,
-                            __be16 sport, __be16 dport, u32 *tsoff)
+u32 secure_tcp_seq(__be32 saddr, __be32 daddr,
+                  __be16 sport, __be16 dport)
 {
-       u64 hash;
+       u32 hash;
+
        net_secret_init();
        hash = siphash_3u32((__force u32)saddr, (__force u32)daddr,
                            (__force u32)sport << 16 | (__force u32)dport,
                            &net_secret);
-       *tsoff = secure_tcp_ts_off(saddr, daddr);
        return seq_scale(hash);
 }
 
 
 #include <linux/siphash.h>
 #include <linux/kernel.h>
 #include <linux/export.h>
+#include <net/secure_seq.h>
 #include <net/tcp.h>
 #include <net/route.h>
 
 
 struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
                                 struct request_sock *req,
-                                struct dst_entry *dst)
+                                struct dst_entry *dst, u32 tsoff)
 {
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct sock *child;
                                                 NULL, &own_req);
        if (child) {
                atomic_set(&req->rsk_refcnt, 1);
+               tcp_sk(child)->tsoffset = tsoff;
                sock_rps_save_rxhash(child, skb);
                inet_csk_reqsk_queue_add(sk, req, child);
        } else {
        struct rtable *rt;
        __u8 rcv_wscale;
        struct flowi4 fl4;
+       u32 tsoff = 0;
 
        if (!sock_net(sk)->ipv4.sysctl_tcp_syncookies || !th->ack || th->rst)
                goto out;
        memset(&tcp_opt, 0, sizeof(tcp_opt));
        tcp_parse_options(skb, &tcp_opt, 0, NULL);
 
+       if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) {
+               tsoff = secure_tcp_ts_off(ip_hdr(skb)->daddr, ip_hdr(skb)->saddr);
+               tcp_opt.rcv_tsecr -= tsoff;
+       }
+
        if (!cookie_timestamp_decode(&tcp_opt))
                goto out;
 
        ireq->rcv_wscale  = rcv_wscale;
        ireq->ecn_ok = cookie_ecn_ok(&tcp_opt, sock_net(sk), &rt->dst);
 
-       ret = tcp_get_cookie_sock(sk, skb, req, &rt->dst);
+       ret = tcp_get_cookie_sock(sk, skb, req, &rt->dst, tsoff);
        /* ip_queue_xmit() depends on our flow being setup
         * Normal sockets get it right from inet_csk_route_child_sock()
         */
 
 int sysctl_tcp_app_win __read_mostly = 31;
 int sysctl_tcp_adv_win_scale __read_mostly = 1;
 EXPORT_SYMBOL(sysctl_tcp_adv_win_scale);
-EXPORT_SYMBOL(sysctl_tcp_timestamps);
 
 /* rfc5961 challenge ack rate limiting */
 int sysctl_tcp_challenge_ack_limit = 1000;
        if (security_inet_conn_request(sk, skb, req))
                goto drop_and_free;
 
-       if (isn && tmp_opt.tstamp_ok)
-               af_ops->init_seq_tsoff(skb, &tcp_rsk(req)->ts_off);
+       if (tmp_opt.tstamp_ok)
+               tcp_rsk(req)->ts_off = af_ops->init_ts_off(skb);
 
        if (!want_cookie && !isn) {
                /* Kill the following clause, if you dislike this way. */
                        goto drop_and_release;
                }
 
-               isn = af_ops->init_seq_tsoff(skb, &tcp_rsk(req)->ts_off);
+               isn = af_ops->init_seq(skb);
        }
        if (!dst) {
                dst = af_ops->route_req(sk, &fl, req);
 
        if (want_cookie) {
                isn = cookie_init_sequence(af_ops, sk, skb, &req->mss);
-               tcp_rsk(req)->ts_off = 0;
                req->cookie_ts = tmp_opt.tstamp_ok;
                if (!tmp_opt.tstamp_ok)
                        inet_rsk(req)->ecn_ok = 0;
 
 struct inet_hashinfo tcp_hashinfo;
 EXPORT_SYMBOL(tcp_hashinfo);
 
-static u32 tcp_v4_init_seq_and_tsoff(const struct sk_buff *skb, u32 *tsoff)
+static u32 tcp_v4_init_seq(const struct sk_buff *skb)
 {
-       return secure_tcp_seq_and_tsoff(ip_hdr(skb)->daddr,
-                                       ip_hdr(skb)->saddr,
-                                       tcp_hdr(skb)->dest,
-                                       tcp_hdr(skb)->source, tsoff);
+       return secure_tcp_seq(ip_hdr(skb)->daddr,
+                             ip_hdr(skb)->saddr,
+                             tcp_hdr(skb)->dest,
+                             tcp_hdr(skb)->source);
+}
+
+static u32 tcp_v4_init_ts_off(const struct sk_buff *skb)
+{
+       return secure_tcp_ts_off(ip_hdr(skb)->daddr,
+                                ip_hdr(skb)->saddr);
 }
 
 int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
        struct flowi4 *fl4;
        struct rtable *rt;
        int err;
-       u32 seq;
        struct ip_options_rcu *inet_opt;
        struct inet_timewait_death_row *tcp_death_row = &sock_net(sk)->ipv4.tcp_death_row;
 
        rt = NULL;
 
        if (likely(!tp->repair)) {
-               seq = secure_tcp_seq_and_tsoff(inet->inet_saddr,
-                                              inet->inet_daddr,
-                                              inet->inet_sport,
-                                              usin->sin_port,
-                                              &tp->tsoffset);
                if (!tp->write_seq)
-                       tp->write_seq = seq;
+                       tp->write_seq = secure_tcp_seq(inet->inet_saddr,
+                                                      inet->inet_daddr,
+                                                      inet->inet_sport,
+                                                      usin->sin_port);
+               tp->tsoffset = secure_tcp_ts_off(inet->inet_saddr,
+                                                inet->inet_daddr);
        }
 
        inet->inet_id = tp->write_seq ^ jiffies;
        .cookie_init_seq =      cookie_v4_init_sequence,
 #endif
        .route_req      =       tcp_v4_route_req,
-       .init_seq_tsoff =       tcp_v4_init_seq_and_tsoff,
+       .init_seq       =       tcp_v4_init_seq,
+       .init_ts_off    =       tcp_v4_init_ts_off,
        .send_synack    =       tcp_v4_send_synack,
 };
 
 
 #include <linux/random.h>
 #include <linux/siphash.h>
 #include <linux/kernel.h>
+#include <net/secure_seq.h>
 #include <net/ipv6.h>
 #include <net/tcp.h>
 
        int mss;
        struct dst_entry *dst;
        __u8 rcv_wscale;
+       u32 tsoff = 0;
 
        if (!sock_net(sk)->ipv4.sysctl_tcp_syncookies || !th->ack || th->rst)
                goto out;
        memset(&tcp_opt, 0, sizeof(tcp_opt));
        tcp_parse_options(skb, &tcp_opt, 0, NULL);
 
+       if (tcp_opt.saw_tstamp && tcp_opt.rcv_tsecr) {
+               tsoff = secure_tcpv6_ts_off(ipv6_hdr(skb)->daddr.s6_addr32,
+                                           ipv6_hdr(skb)->saddr.s6_addr32);
+               tcp_opt.rcv_tsecr -= tsoff;
+       }
+
        if (!cookie_timestamp_decode(&tcp_opt))
                goto out;
 
        ireq->rcv_wscale = rcv_wscale;
        ireq->ecn_ok = cookie_ecn_ok(&tcp_opt, sock_net(sk), dst);
 
-       ret = tcp_get_cookie_sock(sk, skb, req, dst);
+       ret = tcp_get_cookie_sock(sk, skb, req, dst, tsoff);
 out:
        return ret;
 out_free:
 
        }
 }
 
-static u32 tcp_v6_init_seq_and_tsoff(const struct sk_buff *skb, u32 *tsoff)
+static u32 tcp_v6_init_seq(const struct sk_buff *skb)
 {
-       return secure_tcpv6_seq_and_tsoff(ipv6_hdr(skb)->daddr.s6_addr32,
-                                         ipv6_hdr(skb)->saddr.s6_addr32,
-                                         tcp_hdr(skb)->dest,
-                                         tcp_hdr(skb)->source, tsoff);
+       return secure_tcpv6_seq(ipv6_hdr(skb)->daddr.s6_addr32,
+                               ipv6_hdr(skb)->saddr.s6_addr32,
+                               tcp_hdr(skb)->dest,
+                               tcp_hdr(skb)->source);
+}
+
+static u32 tcp_v6_init_ts_off(const struct sk_buff *skb)
+{
+       return secure_tcpv6_ts_off(ipv6_hdr(skb)->daddr.s6_addr32,
+                                  ipv6_hdr(skb)->saddr.s6_addr32);
 }
 
 static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        struct flowi6 fl6;
        struct dst_entry *dst;
        int addr_type;
-       u32 seq;
        int err;
        struct inet_timewait_death_row *tcp_death_row = &sock_net(sk)->ipv4.tcp_death_row;
 
        sk_set_txhash(sk);
 
        if (likely(!tp->repair)) {
-               seq = secure_tcpv6_seq_and_tsoff(np->saddr.s6_addr32,
-                                                sk->sk_v6_daddr.s6_addr32,
-                                                inet->inet_sport,
-                                                inet->inet_dport,
-                                                &tp->tsoffset);
                if (!tp->write_seq)
-                       tp->write_seq = seq;
+                       tp->write_seq = secure_tcpv6_seq(np->saddr.s6_addr32,
+                                                        sk->sk_v6_daddr.s6_addr32,
+                                                        inet->inet_sport,
+                                                        inet->inet_dport);
+               tp->tsoffset = secure_tcpv6_ts_off(np->saddr.s6_addr32,
+                                                  sk->sk_v6_daddr.s6_addr32);
        }
 
        if (tcp_fastopen_defer_connect(sk, &err))
        .cookie_init_seq =      cookie_v6_init_sequence,
 #endif
        .route_req      =       tcp_v6_route_req,
-       .init_seq_tsoff =       tcp_v6_init_seq_and_tsoff,
+       .init_seq       =       tcp_v6_init_seq,
+       .init_ts_off    =       tcp_v6_init_ts_off,
        .send_synack    =       tcp_v6_send_synack,
 };