From 82ee26be754f9218476d9a8e36727da0382195a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 16 Feb 2021 16:10:08 -0800 Subject: [PATCH] ebpf offload - do L4 header present check earlier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and more importantly unconditionally. This requires less effort on the part of the in-kernel bpf verifier. Test: TreeHugger Signed-off-by: Maciej Żenczykowski Change-Id: Ibaa94bf096fc81c4d984dfabf515131b1c81ef09 --- Tethering/bpf_progs/bpf_tethering.h | 1 + Tethering/bpf_progs/offload.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/Tethering/bpf_progs/bpf_tethering.h b/Tethering/bpf_progs/bpf_tethering.h index 10e6bb0a6f..4e006db117 100644 --- a/Tethering/bpf_progs/bpf_tethering.h +++ b/Tethering/bpf_progs/bpf_tethering.h @@ -41,6 +41,7 @@ ERR(CHECKSUM) \ ERR(NON_TCP_UDP) \ ERR(NON_TCP) \ + ERR(SHORT_L4_HEADER) \ ERR(SHORT_TCP_HEADER) \ ERR(SHORT_UDP_HEADER) \ ERR(TRUNCATED_IPV4) \ diff --git a/Tethering/bpf_progs/offload.c b/Tethering/bpf_progs/offload.c index d4b5246a71..30b1f491f8 100644 --- a/Tethering/bpf_progs/offload.c +++ b/Tethering/bpf_progs/offload.c @@ -403,6 +403,18 @@ static inline __always_inline int do_forward4(struct __sk_buff* skb, const bool // out all the non-tcp logic. Also note that at this point is_udp === !is_tcp. const bool is_tcp = !updatetime || (ip->protocol == IPPROTO_TCP); + // This is a bit of a hack to make things easier on the bpf verifier. + // (In particular I believe the Linux 4.14 kernel's verifier can get confused later on about + // what offsets into the packet are valid and can spuriously reject the program, this is + // because it fails to realize that is_tcp && !is_tcp is impossible) + // + // For both TCP & UDP we'll need to read and modify the src/dst ports, which so happen to + // always be in the first 4 bytes of the L4 header. Additionally for UDP we'll need access + // to the checksum field which is in bytes 7 and 8. While for TCP we'll need to read the + // TCP flags (at offset 13) and access to the checksum field (2 bytes at offset 16). + // As such we *always* need access to at least 8 bytes. + if (data + l2_header_size + sizeof(*ip) + 8 > data_end) PUNT(SHORT_L4_HEADER); + struct tcphdr* tcph = is_tcp ? (void*)(ip + 1) : NULL; struct udphdr* udph = is_tcp ? NULL : (void*)(ip + 1);