ebpf offload - do L4 header present check earlier

and more importantly unconditionally.  This requires less effort
on the part of the in-kernel bpf verifier.

Test: TreeHugger
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Ibaa94bf096fc81c4d984dfabf515131b1c81ef09
diff --git a/Tethering/bpf_progs/bpf_tethering.h b/Tethering/bpf_progs/bpf_tethering.h
index 10e6bb0..4e006db 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 d4b5246..30b1f49 100644
--- a/Tethering/bpf_progs/offload.c
+++ b/Tethering/bpf_progs/offload.c
@@ -403,6 +403,18 @@
     // 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);