Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tcp: provide SYN headers for passive connections

This patch allows a server application to get the TCP SYN headers 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 socket to enable saving the SYN headers
for child connections. This can be set before or after the listen()
call.

The latter is used to retrieve the SYN headers for passive connections,
if the parent listener has enabled TCP_SAVE_SYN.

TCP_SAVED_SYN is read once, it frees the saved SYN headers.

The data returned in TCP_SAVED_SYN are network (IPv4/IPv6) and TCP
headers.

Original patch was written by Tom Herbert, I changed it to not hold
a full skb (and associated dst and conntracking reference).

We have used such patch for about 3 years at Google.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Neal Cardwell <ncardwell@google.com>
Tested-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
cd8ae852 fcba67c9

+70 -1
+8
include/linux/tcp.h
··· 199 199 syn_fastopen:1, /* SYN includes Fast Open option */ 200 200 syn_fastopen_exp:1,/* SYN includes Fast Open exp. option */ 201 201 syn_data_acked:1,/* data in SYN is acked by SYN-ACK */ 202 + save_syn:1, /* Save headers of SYN packet */ 202 203 is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */ 203 204 u32 tlp_high_seq; /* snd_nxt at the time of TLP retransmit. */ 204 205 ··· 327 326 * socket. Used to retransmit SYNACKs etc. 328 327 */ 329 328 struct request_sock *fastopen_rsk; 329 + u32 *saved_syn; 330 330 }; 331 331 332 332 enum tsq_flags { ··· 393 391 } 394 392 queue->fastopenq->max_qlen = backlog; 395 393 return 0; 394 + } 395 + 396 + static inline void tcp_saved_syn_free(struct tcp_sock *tp) 397 + { 398 + kfree(tp->saved_syn); 399 + tp->saved_syn = NULL; 396 400 } 397 401 398 402 #endif /* _LINUX_TCP_H */
+3 -1
include/net/request_sock.h
··· 64 64 struct timer_list rsk_timer; 65 65 const struct request_sock_ops *rsk_ops; 66 66 struct sock *sk; 67 + u32 *saved_syn; 67 68 u32 secid; 68 69 u32 peer_secid; 69 70 }; ··· 78 77 req->rsk_ops = ops; 79 78 sock_hold(sk_listener); 80 79 req->rsk_listener = sk_listener; 81 - 80 + req->saved_syn = NULL; 82 81 /* Following is temporary. It is coupled with debugging 83 82 * helpers in reqsk_put() & reqsk_free() 84 83 */ ··· 105 104 req->rsk_ops->destructor(req); 106 105 if (req->rsk_listener) 107 106 sock_put(req->rsk_listener); 107 + kfree(req->saved_syn); 108 108 kmem_cache_free(req->rsk_ops->slab, req); 109 109 } 110 110
+2
include/uapi/linux/tcp.h
··· 113 113 #define TCP_TIMESTAMP 24 114 114 #define TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes in write queue */ 115 115 #define TCP_CC_INFO 26 /* Get Congestion Control (optional) info */ 116 + #define TCP_SAVE_SYN 27 /* Record SYN headers for new connections */ 117 + #define TCP_SAVED_SYN 28 /* Get SYN headers recorded for connection */ 116 118 117 119 struct tcp_repair_opt { 118 120 __u32 opt_code;
+35
net/ipv4/tcp.c
··· 2482 2482 icsk->icsk_syn_retries = val; 2483 2483 break; 2484 2484 2485 + case TCP_SAVE_SYN: 2486 + if (val < 0 || val > 1) 2487 + err = -EINVAL; 2488 + else 2489 + tp->save_syn = val; 2490 + break; 2491 + 2485 2492 case TCP_LINGER2: 2486 2493 if (val < 0) 2487 2494 tp->linger2 = -1; ··· 2825 2818 case TCP_NOTSENT_LOWAT: 2826 2819 val = tp->notsent_lowat; 2827 2820 break; 2821 + case TCP_SAVE_SYN: 2822 + val = tp->save_syn; 2823 + break; 2824 + case TCP_SAVED_SYN: { 2825 + if (get_user(len, optlen)) 2826 + return -EFAULT; 2827 + 2828 + lock_sock(sk); 2829 + if (tp->saved_syn) { 2830 + len = min_t(unsigned int, tp->saved_syn[0], len); 2831 + if (put_user(len, optlen)) { 2832 + release_sock(sk); 2833 + return -EFAULT; 2834 + } 2835 + if (copy_to_user(optval, tp->saved_syn + 1, len)) { 2836 + release_sock(sk); 2837 + return -EFAULT; 2838 + } 2839 + tcp_saved_syn_free(tp); 2840 + release_sock(sk); 2841 + } else { 2842 + release_sock(sk); 2843 + len = 0; 2844 + if (put_user(len, optlen)) 2845 + return -EFAULT; 2846 + } 2847 + return 0; 2848 + } 2828 2849 default: 2829 2850 return -ENOPROTOOPT; 2830 2851 }
+18
net/ipv4/tcp_input.c
··· 6060 6060 return want_cookie; 6061 6061 } 6062 6062 6063 + static void tcp_reqsk_record_syn(const struct sock *sk, 6064 + struct request_sock *req, 6065 + const struct sk_buff *skb) 6066 + { 6067 + if (tcp_sk(sk)->save_syn) { 6068 + u32 len = skb_network_header_len(skb) + tcp_hdrlen(skb); 6069 + u32 *copy; 6070 + 6071 + copy = kmalloc(len + sizeof(u32), GFP_ATOMIC); 6072 + if (copy) { 6073 + copy[0] = len; 6074 + memcpy(&copy[1], skb_network_header(skb), len); 6075 + req->saved_syn = copy; 6076 + } 6077 + } 6078 + } 6079 + 6063 6080 int tcp_conn_request(struct request_sock_ops *rsk_ops, 6064 6081 const struct tcp_request_sock_ops *af_ops, 6065 6082 struct sock *sk, struct sk_buff *skb) ··· 6209 6192 tcp_rsk(req)->tfo_listener = false; 6210 6193 af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT); 6211 6194 } 6195 + tcp_reqsk_record_syn(sk, req, skb); 6212 6196 6213 6197 return 0; 6214 6198
+1
net/ipv4/tcp_ipv4.c
··· 1802 1802 1803 1803 /* If socket is aborted during connect operation */ 1804 1804 tcp_free_fastopen_req(tp); 1805 + tcp_saved_syn_free(tp); 1805 1806 1806 1807 sk_sockets_allocated_dec(sk); 1807 1808 sock_release_memcg(sk);
+3
net/ipv4/tcp_minisocks.c
··· 536 536 newtp->fastopen_rsk = NULL; 537 537 newtp->syn_data_acked = 0; 538 538 539 + newtp->saved_syn = req->saved_syn; 540 + req->saved_syn = NULL; 541 + 539 542 TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_PASSIVEOPENS); 540 543 } 541 544 return newsk;