]> git.bitcoin.ninja Git - flowspec-xdp/commitdiff
Implement (only manually-tested) flowspec community detection except ratelimit
authorMatt Corallo <git@bluematt.me>
Mon, 5 Apr 2021 23:30:55 +0000 (19:30 -0400)
committerMatt Corallo <git@bluematt.me>
Tue, 6 Apr 2021 01:08:03 +0000 (21:08 -0400)
genrules.py
install.sh
test.sh
xdp.c

index 480e628dca23574ccffb8e28095bf6cda662770e..51e547d2d28a12f346aac036cd63f052637b757a 100755 (executable)
@@ -249,6 +249,7 @@ def flow_label_to_rule(rules):
     return f"""if (ip6 == NULL) break;
 if (!( {ast.write("((((uint32_t)(ip6->flow_lbl[0] & 0xf)) << 2*8) | (((uint32_t)ip6->flow_lbl[1]) << 1*8) | (uint32_t)ip6->flow_lbl[0])")} )) break;"""
 
+
 with open("rules.h", "w") as out:
     parse = argparse.ArgumentParser()
     parse.add_argument("--ihl", dest="ihl", required=True, choices=["drop-options","accept-options","parse-options"])
@@ -288,72 +289,121 @@ with open("rules.h", "w") as out:
     use_v6_frags = False
     rulecnt = 0
 
+    lastrule = None
     for line in sys.stdin.readlines():
-        t = line.split("{")
-        if len(t) != 2:
-            continue
-        if t[0].strip() == "flow4":
-            proto = 4
-            rules4 += "\tdo {\\\n"
-        elif t[0].strip() == "flow6":
-            proto = 6
-            rules6 += "\tdo {\\\n"
-        else:
+        if "{" in line:
+            if lastrule is not None:
+                print("Skipped rule due to lack of understood community tag: " + lastrule)
+            lastrule = line
             continue
-
-        def write_rule(r):
-            global rules4, rules6
-            if proto == 6:
-                rules6 += "\t\t" + r.replace("\n", " \\\n\t\t") + " \\\n"
+        if "BGP.ext_community: " in line:
+            assert lastrule is not None
+
+            t = lastrule.split("{")
+            if t[0].strip() == "flow4":
+                proto = 4
+                rules4 += "\tdo {\\\n"
+            elif t[0].strip() == "flow6":
+                proto = 6
+                rules6 += "\tdo {\\\n"
             else:
-                rules4 += "\t\t" + r.replace("\n", " \\\n\t\t") + " \\\n"
-
-        rule = t[1].split("}")[0].strip()
-        for step in rule.split(";"):
-            if step.strip().startswith("src") or step.strip().startswith("dst"):
-                nets = step.strip()[3:].strip().split(" ")
-                if len(nets) > 1:
-                    assert nets[1] == "offset"
-                    offset = nets[2]
+                continue
+
+            def write_rule(r):
+                global rules4, rules6
+                if proto == 6:
+                    rules6 += "\t\t" + r.replace("\n", " \\\n\t\t") + " \\\n"
                 else:
-                    offset = None
-                if step.strip().startswith("src"):
-                    write_rule(ip_to_rule(proto, nets[0], "saddr", offset))
+                    rules4 += "\t\t" + r.replace("\n", " \\\n\t\t") + " \\\n"
+
+            rule = t[1].split("}")[0].strip()
+            for step in rule.split(";"):
+                if step.strip().startswith("src") or step.strip().startswith("dst"):
+                    nets = step.strip()[3:].strip().split(" ")
+                    if len(nets) > 1:
+                        assert nets[1] == "offset"
+                        offset = nets[2]
+                    else:
+                        offset = None
+                    if step.strip().startswith("src"):
+                        write_rule(ip_to_rule(proto, nets[0], "saddr", offset))
+                    else:
+                        write_rule(ip_to_rule(proto, nets[0], "daddr", offset))
+                elif step.strip().startswith("proto") and proto == 4:
+                    write_rule(proto_to_rule(4, step.strip()[6:]))
+                elif step.strip().startswith("next header") and proto == 6:
+                    write_rule(proto_to_rule(6, step.strip()[12:]))
+                elif step.strip().startswith("icmp type"):
+                    write_rule(icmp_type_to_rule(proto, step.strip()[10:]))
+                elif step.strip().startswith("icmp code"):
+                    write_rule(icmp_code_to_rule(proto, step.strip()[10:]))
+                elif step.strip().startswith("sport") or step.strip().startswith("dport") or step.strip().startswith("port"):
+                    write_rule(port_to_rule(step.strip().split(" ")[0], step.strip().split(" ", 1)[1]))
+                elif step.strip().startswith("length"):
+                    write_rule(len_to_rule(step.strip()[7:]))
+                elif step.strip().startswith("dscp"):
+                    write_rule(dscp_to_rule(proto, step.strip()[5:]))
+                elif step.strip().startswith("tcp flags"):
+                    write_rule(tcp_flags_to_rule(step.strip()[10:]))
+                elif step.strip().startswith("label"):
+                    write_rule(flow_label_to_rule(step.strip()[6:]))
+                elif step.strip().startswith("fragment"):
+                    if proto == 6:
+                        use_v6_frags = True
+                    write_rule(fragment_to_rule(proto, step.strip()[9:]))
+                elif step.strip() == "":
+                    pass
                 else:
-                    write_rule(ip_to_rule(proto, nets[0], "daddr", offset))
-            elif step.strip().startswith("proto") and proto == 4:
-                write_rule(proto_to_rule(4, step.strip()[6:]))
-            elif step.strip().startswith("next header") and proto == 6:
-                write_rule(proto_to_rule(6, step.strip()[12:]))
-            elif step.strip().startswith("icmp type"):
-                write_rule(icmp_type_to_rule(proto, step.strip()[10:]))
-            elif step.strip().startswith("icmp code"):
-                write_rule(icmp_code_to_rule(proto, step.strip()[10:]))
-            elif step.strip().startswith("sport") or step.strip().startswith("dport") or step.strip().startswith("port"):
-                write_rule(port_to_rule(step.strip().split(" ")[0], step.strip().split(" ", 1)[1]))
-            elif step.strip().startswith("length"):
-                write_rule(len_to_rule(step.strip()[7:]))
-            elif step.strip().startswith("dscp"):
-                write_rule(dscp_to_rule(proto, step.strip()[5:]))
-            elif step.strip().startswith("tcp flags"):
-                write_rule(tcp_flags_to_rule(step.strip()[10:]))
-            elif step.strip().startswith("label"):
-                write_rule(flow_label_to_rule(step.strip()[6:]))
-            elif step.strip().startswith("fragment"):
-                if proto == 6:
-                    use_v6_frags = True
-                write_rule(fragment_to_rule(proto, step.strip()[9:]))
-            elif step.strip() == "":
-                pass
+                    assert False
+
+            # Now write the match handling!
+            first_action = None
+            last_action = None
+            for community in line.split("("):
+                if not community.startswith("generic, "):
+                    continue
+                blocks = community.split(",")
+                assert len(blocks) == 3
+                if len(blocks[1].strip()) != 10: # Should be 0x12345678
+                    continue
+                ty = blocks[1].strip()[:6]
+                low_bytes = int(blocks[2].strip(") \n"), 16)
+                if ty == "0x8006":
+                    if low_bytes == 0:
+                        first_action = "return XDP_DROP;"
+                    else:
+                        assert False # Not yet supported
+                elif ty == "0x8007":
+                    if low_bytes & 1 == 0:
+                        last_action = "return XDP_PASS;"
+                    if low_bytes & 2 == 2:
+                        write_rule(f"const uint32_t ruleidx = STATIC_RULE_CNT + {rulecnt};")
+                        write_rule("INCREMENT_MATCH(ruleidx);")
+                elif ty == "0x8008":
+                    assert False # We do not implement the redirect action
+                elif ty == "0x8009":
+                    if low_bytes & ~0b111111 != 0:
+                        assert False # Invalid DSCP value
+                    if proto == 4:
+                        write_rule("int32_t chk = ~BE16(ip->check) & 0xffff;")
+                        write_rule("uint8_t orig_tos = ip->tos;")
+                        write_rule("ip->tos = (ip->tos & 3) | " + str(low_bytes << 2) + ";")
+                        write_rule("chk = (chk - orig_tos + ip->tos);")
+                        write_rule("if (unlikely(chk < 0)) { chk += 65534; }")
+                        write_rule("ip->check = ~BE16(chk);")
+                    else:
+                        write_rule("ip6->priority = " + str(low_bytes >> 2) + ";")
+                        write_rule("ip6->flow_lbl[0] = (ip6->flow_lbl[0] & 0x3f) | " + str((low_bytes & 3) << 6) + ";")
+            if first_action is not None:
+                write_rule(first_action)
+            if last_action is not None:
+                write_rule(last_action)
+            if proto == 6:
+                rules6 += "\t} while(0);\\\n"
             else:
-                assert False
-        write_rule(f"const uint32_t ruleidx = STATIC_RULE_CNT + {rulecnt};")
-        write_rule("DO_RETURN(ruleidx, XDP_DROP);")
-        if proto == 6:
-            rules6 += "\t} while(0);\\\n"
-        else:
-            rules4 += "\t} while(0);\\\n"
-        rulecnt += 1
+                rules4 += "\t} while(0);\\\n"
+            rulecnt += 1
+            lastrule = None
 
     out.write("\n")
     out.write(f"#define RULECNT {rulecnt}\n")
index 97df120917ada357672add6216fd7587d595b62d..af9a3ede9c5f7e81bc4dcd6c4e8bad281c86bd46 100755 (executable)
@@ -1,8 +1,8 @@
 #!/bin/bash
 set -e
 
-RULES="$(birdc show route table flowspec4)
-$(birdc show route table flowspec6)"
+RULES="$(birdc show route table flowspec4 primary all)
+$(birdc show route table flowspec6 primary all)"
 
 echo "$RULES" | ./genrules.py --8021q=drop-vlan --v6frag=ignore-parse-if-rule --ihl=drop-options
 clang -std=c99 -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O3 -emit-llvm -c xdp.c -o - | llc -O3 -march=bpf -filetype=obj -o xdp
diff --git a/test.sh b/test.sh
index 850ab36389f29418a394aba6806c41c57442f630..d9aa68a34afb1f36a1ab04e084fd43c150f0e377 100755 (executable)
--- a/test.sh
+++ b/test.sh
@@ -2,6 +2,10 @@
 
 set -e
 
+COMMUNITY_DROP="
+       Type: static univ
+       BGP.ext_community: (generic, 0x80060000, 0x0) (generic, 0x80070000, 0xf) (generic, 0x80090000, 0x3f)"
+
 TEST_PKT='#define TEST \
 "\x00\x17\x10\x95\xe8\x96\x00\x0d\xb9\x50\x11\x4c\x08\x00\x45\x00" \
 "\x00\x8c\x7d\x0f\x00\x00\x40\x11\x3a\x31\x48\xe5\x68\xce\x67\x63" \
@@ -15,70 +19,70 @@ TEST_PKT='#define TEST \
 "\xb5\xc3\xa9\xa6\x21\x14\xc7\xd9\x71\x07"'
 
 # Test all the things...
-echo "flow4 { src 72.229.104.206/32; dst 103.99.170.10/32; proto = 17; sport = 56733; dport = 4242; length = 140; dscp = 0; fragment !dont_fragment && !is_fragment && !first_fragment && !last_fragment };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow4 { src 72.229.104.206/32; dst 103.99.170.10/32; proto = 17; sport = 56733; dport = 4242; length = 140; dscp = 0; fragment !dont_fragment && !is_fragment && !first_fragment && !last_fragment };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { port = 4242; icmp code = 0; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
+echo "flow4 { port = 4242; icmp code = 0; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 # Some port tests...
-echo "flow4 { port = 4242 && = 56733; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow4 { port = 4242 && = 56733; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { port = 4242 || 1; sport = 56733 };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow4 { port = 4242 || 1; sport = 56733 };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { port = 4242 && 1 };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
+echo "flow4 { port = 4242 && 1 };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 # Some match-order tests...
 # (43 && 42) || 4242
-echo "flow4 { port = 43 && 42, 4242; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow4 { port = 43 && 42, 4242; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 # (43 && 42) || 4242
-echo "flow4 { port = 43 && 42 || 4242; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow4 { port = 43 && 42 || 4242; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 # 4242 || (42 && 43)
-echo "flow4 { port = 4242, 42 && 43; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow4 { port = 4242, 42 && 43; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 # (4242 && false) || (42 && 4242)
-echo "flow4 { port = 4242 && false, 42 && 4242; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow4 { port = 4242 && false, 42 && 4242; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 # (4242 && true) || (42 && 43)
-echo "flow4 { port = 4242 && true, 42 && 43; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow4 { port = 4242 && true, 42 && 43; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 # 42 || true
-echo "flow4 { port = 42, true; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow4 { port = 42, true; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { icmp code != 0; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
+echo "flow4 { icmp code != 0; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
@@ -92,22 +96,22 @@ TEST_PKT='#define TEST \
 "\x75\xde\xeb\x22\xd6\x80"'
 
 # Some v6 TCP tests with a DSCP of all 1s...
-echo "flow6 { src 2a01:4f8:130:71d2::2/128; dst 2620:6e:a000:2001::6/128; next header 6; port 8333 && 49778; tcp flags 0x010/0xfff;};" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow6 { src 2a01:4f8:130:71d2::2/128; dst 2620:6e:a000:2001::6/128; next header 6; port 8333 && 49778; tcp flags 0x010/0xfff;};$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { src 0:4f8:130:71d2::2/128 offset 16; dst 0:0:a000:2001::/64 offset 32; next header 6; port 8333 && 49778; tcp flags 0x010/0xfff;};" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow6 { src 0:4f8:130:71d2::2/128 offset 16; dst 0:0:a000:2001::/64 offset 32; next header 6; port 8333 && 49778; tcp flags 0x010/0xfff;};$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { dscp 0x3f; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
+echo "flow6 { dscp 0x3f; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { icmp code != 0; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
+echo "flow6 { icmp code != 0; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
@@ -122,54 +126,54 @@ TEST_PKT='#define TEST \
 "\x32\x33\x34\x35\x36\x37"'
 
 # ICMP and VLAN tests with DSCP of all 1s...
-echo "flow4 { src 10.0.0.0/8; dst 209.250.0.0/16; proto = 1; icmp type 8; icmp code >= 0; length < 100; fragment dont_fragment; };" | ./genrules.py --ihl=accept-options --8021q=parse-vlan --v6frag=ignore
+echo "flow4 { src 10.0.0.0/8; dst 209.250.0.0/16; proto = 1; icmp type 8; icmp code >= 0; length < 100; fragment dont_fragment; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=parse-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { icmp type 8; icmp code > 0; };" | ./genrules.py --ihl=drop-options --8021q=parse-vlan --v6frag=drop-frags
+echo "flow4 { icmp type 8; icmp code > 0; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=parse-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { icmp type 9; };" | ./genrules.py --ihl=drop-options --8021q=parse-vlan --v6frag=drop-frags
+echo "flow4 { icmp type 9; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=parse-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { src 10.0.0.0/8; dst 209.250.0.0/16; proto = 1; icmp type 8; icmp code >= 0; length < 100; fragment dont_fragment; };" | ./genrules.py --ihl=accept-options --8021q=parse-vlan --require-8021q=3 --v6frag=ignore
+echo "flow4 { src 10.0.0.0/8; dst 209.250.0.0/16; proto = 1; icmp type 8; icmp code >= 0; length < 100; fragment dont_fragment; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=parse-vlan --require-8021q=3 --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { src 0.0.0.0/32; };" | ./genrules.py --ihl=accept-options --8021q=parse-vlan --require-8021q=4 --v6frag=ignore
+echo "flow4 { src 0.0.0.0/32; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=parse-vlan --require-8021q=4 --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { src 0.0.0.0/32; };" | ./genrules.py --ihl=drop-options --8021q=parse-vlan --require-8021q=3 --v6frag=drop-frags
+echo "flow4 { src 0.0.0.0/32; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=parse-vlan --require-8021q=3 --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { port 42; };" | ./genrules.py --ihl=drop-options --8021q=parse-vlan --v6frag=drop-frags
+echo "flow4 { port 42; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=parse-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { dscp 0x3f; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
+echo "flow4 { dscp 0x3f; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 
 # Test --8021q option handling
-echo "flow4 { port 42; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
+echo "flow4 { port 42; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow4 { };" | ./genrules.py --ihl=drop-options --8021q=accept-vlan --v6frag=drop-frags
+echo "flow4 { };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=accept-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
@@ -186,27 +190,27 @@ TEST_PKT='#define TEST \
 "\x00\x00\x00\x00\x00\x00"'
 
 # ICMPv6 tests
-echo "flow6 { icmp type 129; icmp code 0; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow6 { icmp type 129; icmp code 0; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { icmp code != 0; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
+echo "flow6 { icmp code != 0; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { tcp flags 0x0/0x0; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
+echo "flow6 { tcp flags 0x0/0x0; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { port 42; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
+echo "flow6 { port 42; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { fragment is_fragment || first_fragment || last_fragment; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
+echo "flow6 { fragment is_fragment || first_fragment || last_fragment; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
@@ -224,50 +228,50 @@ TEST_PKT='#define TEST \
 
 # Last frag ICMPv6 tests
 
-echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment is_fragment && !first_fragment && last_fragment; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
+echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment is_fragment && !first_fragment && last_fragment; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment !is_fragment; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
+echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment !is_fragment; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment !is_fragment || first_fragment || !last_fragment; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
+echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment !is_fragment || first_fragment || !last_fragment; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment !is_fragment || first_fragment || !last_fragment; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
+echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment !is_fragment || first_fragment || !last_fragment; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 # Note on the second fragment we don't know the ICMP header (though < 256 is trivially true)
-echo "flow6 { icmp type < 256; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
+echo "flow6 { icmp type < 256; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
-clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
+clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -Wno-tautological-constant-out-of-range-compare -O0 -g xdp.c -o xdp && ./xdp
 
 #TODO Is nextheader frag correct to match on here? Should we support matching on any nexthdr?
-echo "flow6 { next header 44; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
+echo "flow6 { next header 44; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 # Test the --v6frag options (ignore-parse-if-rule is tested below)
-echo "flow6 { tcp flags 42/42; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
+echo "flow6 { tcp flags 42/42; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { tcp flags 42/42; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=drop-frags
+echo "flow6 { tcp flags 42/42; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=drop-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { tcp flags 42/42; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
+echo "flow6 { tcp flags 42/42; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
@@ -357,50 +361,50 @@ TEST_PKT='#define TEST \
 
 # First frag ICMPv6 tests
 
-echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment is_fragment && first_fragment && !last_fragment; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
+echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment is_fragment && first_fragment && !last_fragment; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment !is_fragment; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
+echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment !is_fragment; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment !is_fragment || !first_fragment || last_fragment; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
+echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment !is_fragment || !first_fragment || last_fragment; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment is_fragment && first_fragment && !last_fragment; icmp code 0; icmp type 128 };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
+echo "flow6 { src 2620:6e:a007:233::1/128; dst 2001:470:0:503::2/128; fragment is_fragment && first_fragment && !last_fragment; icmp code 0; icmp type 128 };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 #TODO Is nextheader frag correct to match on here? Should we support matching on any nexthdr?
-echo "flow6 { next header 44; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
+echo "flow6 { next header 44; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 #TODO Is nextheader frag correct to match on here? Should we support matching on any nexthdr?
-echo "flow6 { next header 58; };" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
+echo "flow6 { next header 58; };$COMMUNITY_DROP" | ./genrules.py --ihl=drop-options --8021q=drop-vlan --v6frag=parse-frags
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
 # Test accept-parse-if-rule
-echo "flow6 { icmp code 0; icmp type 128; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore-parse-if-rule
+echo "flow6 { icmp code 0; icmp type 128; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore-parse-if-rule
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { icmp code 0; icmp type 128; fragment is_fragment; };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore-parse-if-rule
+echo "flow6 { icmp code 0; icmp type 128; fragment is_fragment; };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore-parse-if-rule
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_DROP" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
 
-echo "flow6 { icmp code 0; icmp type 128; fragment !is_fragment };" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore-parse-if-rule
+echo "flow6 { icmp code 0; icmp type 128; fragment !is_fragment };$COMMUNITY_DROP" | ./genrules.py --ihl=accept-options --8021q=accept-vlan --v6frag=ignore-parse-if-rule
 echo "$TEST_PKT" >> rules.h
 echo "#define TEST_EXP XDP_PASS" >> rules.h
 clang -std=c99 -fsanitize=address -pedantic -Wall -Wextra -Wno-pointer-arith -Wno-unused-variable -O0 -g xdp.c -o xdp && ./xdp
diff --git a/xdp.c b/xdp.c
index 4e4d6c6308437ed759239c1f166f982dca36256c..3ebec88555e37bb82ac5ca6868e8ef0e0c5d20d5 100644 (file)
--- a/xdp.c
+++ b/xdp.c
@@ -96,6 +96,7 @@ struct tcphdr {
 #elif defined(__BIG_ENDIAN)
 #define BIGEND128(a, b, c, d) ((((uint128_t)a) << 3*32) | (((uint128_t)b) << 2*32) | (((uint128_t)c) << 1*32) | (((uint128_t)d) << 0*32))
 #define HTON128(a) (a)
+#define BE16(a) ((uint16_t)a)
 #else
 #error "Need endian info"
 #endif
@@ -117,6 +118,16 @@ static const uint32_t IHL_DROP = 2;
 static const uint32_t V6FRAG_DROP = 3;
 #define STATIC_RULE_CNT 4
 
+#define DO_RETURN(reason, ret) {\
+               if (ret == XDP_DROP) { INCREMENT_MATCH(reason); } \
+               return ret; \
+       }
+
+// It seems (based on drop counts) that data_end points to the last byte, not one-past-the-end.
+// This feels strange, but some documentation suggests > here as well, so we stick with that.
+#define CHECK_LEN(start, struc) \
+       if (unlikely((void*)(start) + sizeof(struct struc) > data_end)) DO_RETURN(PKT_LEN_DROP, XDP_DROP);
+
 #ifdef TEST
 // 64 bit version of xdp_md for testing
 struct xdp_md {
@@ -133,10 +144,7 @@ static const int XDP_PASS = 0;
 static const int XDP_DROP = 1;
 
 static long drop_cnt_map[RULECNT + STATIC_RULE_CNT];
-#define DO_RETURN(reason, ret) { \
-               if (ret == XDP_DROP) drop_cnt_map[reason] += 1; \
-               return ret; \
-       }
+#define INCREMENT_MATCH(reason) drop_cnt_map[reason] += 1;
 
 #else
 #include <linux/bpf.h>
@@ -148,23 +156,14 @@ struct bpf_map_def SEC("maps") drop_cnt_map = {
        .value_size = sizeof(long),
        .max_entries = RULECNT + STATIC_RULE_CNT,
 };
-#define DO_RETURN(reason, ret) {\
-               if (ret == XDP_DROP) { \
-                       long *value = bpf_map_lookup_elem(&drop_cnt_map, &reason); \
-                       if (value) \
-                               *value += 1; \
-               } \
-               return XDP_DROP; \
-       }
+#define INCREMENT_MATCH(reason) { \
+       long *value = bpf_map_lookup_elem(&drop_cnt_map, &reason); \
+       if (value) \
+               *value += 1; \
+}
 
 SEC("xdp_drop")
 #endif
-
-// It seems (based on drop counts) that data_end points to the last byte, not one-past-the-end.
-// This feels strange, but some documentation suggests > here as well, so we stick with that.
-#define CHECK_LEN(start, struc) \
-       if (unlikely((void*)(start) + sizeof(struct struc) > data_end)) DO_RETURN(PKT_LEN_DROP, XDP_DROP);
-
 int xdp_drop_prog(struct xdp_md *ctx)
 {
        const void *const data_end = (void *)(size_t)ctx->data_end;
@@ -210,7 +209,7 @@ int xdp_drop_prog(struct xdp_md *ctx)
 #ifdef NEED_V4_PARSE
        if (eth_proto == BE16(ETH_P_IP)) {
                CHECK_LEN(pktdata, iphdr);
-               const struct iphdr *ip = (struct iphdr*) pktdata;
+               struct iphdr *ip = (struct iphdr*) pktdata;
 
 #if PARSE_IHL == PARSE
                if (unlikely(ip->ihl < 5)) DO_RETURN(IHL_DROP, XDP_DROP);
@@ -246,7 +245,7 @@ int xdp_drop_prog(struct xdp_md *ctx)
 #ifdef NEED_V6_PARSE
        if (eth_proto == BE16(ETH_P_IPV6)) {
                CHECK_LEN(pktdata, ip6hdr);
-               const struct ip6hdr *ip6 = (struct ip6hdr*) pktdata;
+               struct ip6hdr *ip6 = (struct ip6hdr*) pktdata;
 
                l4hdr = pktdata + 40;
 
@@ -298,7 +297,7 @@ int xdp_drop_prog(struct xdp_md *ctx)
 #include <assert.h>
 #include <string.h>
 
-const char d[] = TEST;
+char d[] = TEST;
 int main() {
        struct xdp_md test = {
                .data = (uint64_t)d,