tcp: Use a struct to represent a saved_syn
authorMartin KaFai Lau <kafai@fb.com>
Thu, 20 Aug 2020 19:00:14 +0000 (12:00 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Mon, 24 Aug 2020 21:34:59 +0000 (14:34 -0700)
The TCP_SAVE_SYN has both the network header and tcp header.
The total length of the saved syn packet is currently stored in
the first 4 bytes (u32) of an array and the actual packet data is
stored after that.

A later patch will add a bpf helper that allows to get the tcp header
alone from the saved syn without the network header.  It will be more
convenient to have a direct offset to a specific header instead of
re-parsing it.  This requires to separately store the network hdrlen.
The total header length (i.e. network + tcp) is still needed for the
current usage in getsockopt.  Although this total length can be obtained
by looking into the tcphdr and then get the (th->doff << 2), this patch
chooses to directly store the tcp hdrlen in the second four bytes of
this newly created "struct saved_syn".  By using a new struct, it can
give a readable name to each individual header length.

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Acked-by: John Fastabend <john.fastabend@gmail.com>
Link: https://lore.kernel.org/bpf/20200820190014.2883694-1-kafai@fb.com
include/linux/tcp.h
include/net/request_sock.h
net/core/filter.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c

index 14b62d7df94232c72b13bfe08d9b6660a2454d02..2088d5a079af538e72ea970306eee6d026e95b6c 100644 (file)
@@ -406,7 +406,7 @@ struct tcp_sock {
         * socket. Used to retransmit SYNACKs etc.
         */
        struct request_sock __rcu *fastopen_rsk;
-       u32     *saved_syn;
+       struct saved_syn *saved_syn;
 };
 
 enum tsq_enum {
@@ -484,6 +484,11 @@ static inline void tcp_saved_syn_free(struct tcp_sock *tp)
        tp->saved_syn = NULL;
 }
 
+static inline u32 tcp_saved_syn_len(const struct saved_syn *saved_syn)
+{
+       return saved_syn->network_hdrlen + saved_syn->tcp_hdrlen;
+}
+
 struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,
                                               const struct sk_buff *orig_skb);
 
index b2eb8b4ba69780f148223e6c2337d8da73a81e02..7d9ed99a77bd10cd47c8f2b98defcfbcb2f51446 100644 (file)
@@ -41,6 +41,12 @@ struct request_sock_ops {
 
 int inet_rtx_syn_ack(const struct sock *parent, struct request_sock *req);
 
+struct saved_syn {
+       u32 network_hdrlen;
+       u32 tcp_hdrlen;
+       u8 data[];
+};
+
 /* struct request_sock - mini sock to represent a connection request
  */
 struct request_sock {
@@ -60,7 +66,7 @@ struct request_sock {
        struct timer_list               rsk_timer;
        const struct request_sock_ops   *rsk_ops;
        struct sock                     *sk;
-       u32                             *saved_syn;
+       struct saved_syn                *saved_syn;
        u32                             secid;
        u32                             peer_secid;
 };
index b2df52086445f46617bfe6d865b780a74092522d..c847b1285acd493f7abdd34f5c71092125310887 100644 (file)
@@ -4550,9 +4550,9 @@ static int _bpf_getsockopt(struct sock *sk, int level, int optname,
                        tp = tcp_sk(sk);
 
                        if (optlen <= 0 || !tp->saved_syn ||
-                           optlen > tp->saved_syn[0])
+                           optlen > tcp_saved_syn_len(tp->saved_syn))
                                goto err_clear;
-                       memcpy(optval, tp->saved_syn + 1, optlen);
+                       memcpy(optval, tp->saved_syn->data, optlen);
                        break;
                default:
                        goto err_clear;
index 31f3b858db8199f71ccaca72f351c7932d576cb5..87d3036d8bd8f5b51e605350cc453724a4ed6947 100644 (file)
@@ -3788,20 +3788,21 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
 
                lock_sock(sk);
                if (tp->saved_syn) {
-                       if (len < tp->saved_syn[0]) {
-                               if (put_user(tp->saved_syn[0], optlen)) {
+                       if (len < tcp_saved_syn_len(tp->saved_syn)) {
+                               if (put_user(tcp_saved_syn_len(tp->saved_syn),
+                                            optlen)) {
                                        release_sock(sk);
                                        return -EFAULT;
                                }
                                release_sock(sk);
                                return -EINVAL;
                        }
-                       len = tp->saved_syn[0];
+                       len = tcp_saved_syn_len(tp->saved_syn);
                        if (put_user(len, optlen)) {
                                release_sock(sk);
                                return -EFAULT;
                        }
-                       if (copy_to_user(optval, tp->saved_syn + 1, len)) {
+                       if (copy_to_user(optval, tp->saved_syn->data, len)) {
                                release_sock(sk);
                                return -EFAULT;
                        }
index 184ea556f50e35141a4be5940c692db41e09f464..4aaedcf71973345a8096b3c805cb8cdfa73d03bd 100644 (file)
@@ -6599,13 +6599,15 @@ static void tcp_reqsk_record_syn(const struct sock *sk,
 {
        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(&copy[1], skb_network_header(skb), len);
-                       req->saved_syn = copy;
+               struct saved_syn *saved_syn;
+
+               saved_syn = kmalloc(struct_size(saved_syn, data, len),
+                                   GFP_ATOMIC);
+               if (saved_syn) {
+                       saved_syn->network_hdrlen = skb_network_header_len(skb);
+                       saved_syn->tcp_hdrlen = tcp_hdrlen(skb);
+                       memcpy(saved_syn->data, skb_network_header(skb), len);
+                       req->saved_syn = saved_syn;
                }
        }
 }