Commit 70a217f1 authored by Martin KaFai Lau's avatar Martin KaFai Lau Committed by Alexei Starovoitov

tcp: Use a struct to represent a saved_syn

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: default avatarMartin KaFai Lau <kafai@fb.com>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Reviewed-by: default avatarEric Dumazet <edumazet@google.com>
Acked-by: default avatarJohn Fastabend <john.fastabend@gmail.com>
Link: https://lore.kernel.org/bpf/20200820190014.2883694-1-kafai@fb.com
parent 9c0f8cbd
...@@ -406,7 +406,7 @@ struct tcp_sock { ...@@ -406,7 +406,7 @@ struct tcp_sock {
* socket. Used to retransmit SYNACKs etc. * socket. Used to retransmit SYNACKs etc.
*/ */
struct request_sock __rcu *fastopen_rsk; struct request_sock __rcu *fastopen_rsk;
u32 *saved_syn; struct saved_syn *saved_syn;
}; };
enum tsq_enum { enum tsq_enum {
...@@ -484,6 +484,11 @@ static inline void tcp_saved_syn_free(struct tcp_sock *tp) ...@@ -484,6 +484,11 @@ static inline void tcp_saved_syn_free(struct tcp_sock *tp)
tp->saved_syn = NULL; 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, struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,
const struct sk_buff *orig_skb); const struct sk_buff *orig_skb);
......
...@@ -41,6 +41,12 @@ struct request_sock_ops { ...@@ -41,6 +41,12 @@ struct request_sock_ops {
int inet_rtx_syn_ack(const struct sock *parent, struct request_sock *req); 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 - mini sock to represent a connection request
*/ */
struct request_sock { struct request_sock {
...@@ -60,7 +66,7 @@ struct request_sock { ...@@ -60,7 +66,7 @@ struct request_sock {
struct timer_list rsk_timer; struct timer_list rsk_timer;
const struct request_sock_ops *rsk_ops; const struct request_sock_ops *rsk_ops;
struct sock *sk; struct sock *sk;
u32 *saved_syn; struct saved_syn *saved_syn;
u32 secid; u32 secid;
u32 peer_secid; u32 peer_secid;
}; };
......
...@@ -4550,9 +4550,9 @@ static int _bpf_getsockopt(struct sock *sk, int level, int optname, ...@@ -4550,9 +4550,9 @@ static int _bpf_getsockopt(struct sock *sk, int level, int optname,
tp = tcp_sk(sk); tp = tcp_sk(sk);
if (optlen <= 0 || !tp->saved_syn || if (optlen <= 0 || !tp->saved_syn ||
optlen > tp->saved_syn[0]) optlen > tcp_saved_syn_len(tp->saved_syn))
goto err_clear; goto err_clear;
memcpy(optval, tp->saved_syn + 1, optlen); memcpy(optval, tp->saved_syn->data, optlen);
break; break;
default: default:
goto err_clear; goto err_clear;
......
...@@ -3788,20 +3788,21 @@ static int do_tcp_getsockopt(struct sock *sk, int level, ...@@ -3788,20 +3788,21 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
lock_sock(sk); lock_sock(sk);
if (tp->saved_syn) { if (tp->saved_syn) {
if (len < tp->saved_syn[0]) { if (len < tcp_saved_syn_len(tp->saved_syn)) {
if (put_user(tp->saved_syn[0], optlen)) { if (put_user(tcp_saved_syn_len(tp->saved_syn),
optlen)) {
release_sock(sk); release_sock(sk);
return -EFAULT; return -EFAULT;
} }
release_sock(sk); release_sock(sk);
return -EINVAL; return -EINVAL;
} }
len = tp->saved_syn[0]; len = tcp_saved_syn_len(tp->saved_syn);
if (put_user(len, optlen)) { if (put_user(len, optlen)) {
release_sock(sk); release_sock(sk);
return -EFAULT; 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); release_sock(sk);
return -EFAULT; return -EFAULT;
} }
......
...@@ -6599,13 +6599,15 @@ static void tcp_reqsk_record_syn(const struct sock *sk, ...@@ -6599,13 +6599,15 @@ static void tcp_reqsk_record_syn(const struct sock *sk,
{ {
if (tcp_sk(sk)->save_syn) { if (tcp_sk(sk)->save_syn) {
u32 len = skb_network_header_len(skb) + tcp_hdrlen(skb); u32 len = skb_network_header_len(skb) + tcp_hdrlen(skb);
u32 *copy; struct saved_syn *saved_syn;
copy = kmalloc(len + sizeof(u32), GFP_ATOMIC); saved_syn = kmalloc(struct_size(saved_syn, data, len),
if (copy) { GFP_ATOMIC);
copy[0] = len; if (saved_syn) {
memcpy(&copy[1], skb_network_header(skb), len); saved_syn->network_hdrlen = skb_network_header_len(skb);
req->saved_syn = copy; saved_syn->tcp_hdrlen = tcp_hdrlen(skb);
memcpy(saved_syn->data, skb_network_header(skb), len);
req->saved_syn = saved_syn;
} }
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment