syn_fastopen:1, /* SYN includes Fast Open option */
                syn_fastopen_exp:1,/* SYN includes Fast Open exp. option */
                syn_data_acked:1,/* data in SYN is acked by SYN-ACK */
+               save_syn:1,     /* Save headers of SYN packet */
                is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */
        u32     tlp_high_seq;   /* snd_nxt at the time of TLP retransmit. */
 
         * socket. Used to retransmit SYNACKs etc.
         */
        struct request_sock *fastopen_rsk;
+       u32     *saved_syn;
 };
 
 enum tsq_flags {
        return 0;
 }
 
+static inline void tcp_saved_syn_free(struct tcp_sock *tp)
+{
+       kfree(tp->saved_syn);
+       tp->saved_syn = NULL;
+}
+
 #endif /* _LINUX_TCP_H */
 
        struct timer_list               rsk_timer;
        const struct request_sock_ops   *rsk_ops;
        struct sock                     *sk;
+       u32                             *saved_syn;
        u32                             secid;
        u32                             peer_secid;
 };
                req->rsk_ops = ops;
                sock_hold(sk_listener);
                req->rsk_listener = sk_listener;
-
+               req->saved_syn = NULL;
                /* Following is temporary. It is coupled with debugging
                 * helpers in reqsk_put() & reqsk_free()
                 */
        req->rsk_ops->destructor(req);
        if (req->rsk_listener)
                sock_put(req->rsk_listener);
+       kfree(req->saved_syn);
        kmem_cache_free(req->rsk_ops->slab, req);
 }
 
 
 #define TCP_TIMESTAMP          24
 #define TCP_NOTSENT_LOWAT      25      /* limit number of unsent bytes in write queue */
 #define TCP_CC_INFO            26      /* Get Congestion Control (optional) info */
+#define TCP_SAVE_SYN           27      /* Record SYN headers for new connections */
+#define TCP_SAVED_SYN          28      /* Get SYN headers recorded for connection */
 
 struct tcp_repair_opt {
        __u32   opt_code;
 
                        icsk->icsk_syn_retries = val;
                break;
 
+       case TCP_SAVE_SYN:
+               if (val < 0 || val > 1)
+                       err = -EINVAL;
+               else
+                       tp->save_syn = val;
+               break;
+
        case TCP_LINGER2:
                if (val < 0)
                        tp->linger2 = -1;
        case TCP_NOTSENT_LOWAT:
                val = tp->notsent_lowat;
                break;
+       case TCP_SAVE_SYN:
+               val = tp->save_syn;
+               break;
+       case TCP_SAVED_SYN: {
+               if (get_user(len, optlen))
+                       return -EFAULT;
+
+               lock_sock(sk);
+               if (tp->saved_syn) {
+                       len = min_t(unsigned int, tp->saved_syn[0], len);
+                       if (put_user(len, optlen)) {
+                               release_sock(sk);
+                               return -EFAULT;
+                       }
+                       if (copy_to_user(optval, tp->saved_syn + 1, len)) {
+                               release_sock(sk);
+                               return -EFAULT;
+                       }
+                       tcp_saved_syn_free(tp);
+                       release_sock(sk);
+               } else {
+                       release_sock(sk);
+                       len = 0;
+                       if (put_user(len, optlen))
+                               return -EFAULT;
+               }
+               return 0;
+       }
        default:
                return -ENOPROTOOPT;
        }
 
        return want_cookie;
 }
 
+static void tcp_reqsk_record_syn(const struct sock *sk,
+                                struct request_sock *req,
+                                const struct sk_buff *skb)
+{
+       if (tcp_sk(sk)->save_syn) {
+               u32 len = skb_network_header_len(skb) + tcp_hdrlen(skb);
+               u32 *copy;
+
+               copy = kmalloc(len + sizeof(u32), GFP_ATOMIC);
+               if (copy) {
+                       copy[0] = len;
+                       memcpy(©[1], skb_network_header(skb), len);
+                       req->saved_syn = copy;
+               }
+       }
+}
+
 int tcp_conn_request(struct request_sock_ops *rsk_ops,
                     const struct tcp_request_sock_ops *af_ops,
                     struct sock *sk, struct sk_buff *skb)
                tcp_rsk(req)->tfo_listener = false;
                af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
        }
+       tcp_reqsk_record_syn(sk, req, skb);
 
        return 0;
 
 
 
        /* If socket is aborted during connect operation */
        tcp_free_fastopen_req(tp);
+       tcp_saved_syn_free(tp);
 
        sk_sockets_allocated_dec(sk);
        sock_release_memcg(sk);
 
                newtp->fastopen_rsk = NULL;
                newtp->syn_data_acked = 0;
 
+               newtp->saved_syn = req->saved_syn;
+               req->saved_syn = NULL;
+
                TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_PASSIVEOPENS);
        }
        return newsk;