]> git.feebdaed.xyz Git - 0xmirror/cilium.git/commitdiff
bpf/tests: add scapy self test
authorMarc Suñé <marc.sune@isovalent.com>
Thu, 4 Dec 2025 17:07:54 +0000 (18:07 +0100)
committerSebastian Wicki <sebastian@isovalent.com>
Tue, 16 Dec 2025 15:14:23 +0000 (15:14 +0000)
Add a Scapy BPF unit selftest, to make sure the testing framework
itself doesn't regress.

Signed-off-by: Marc Suñé <marc.sune@isovalent.com>
bpf/tests/_scapy_selftest.c [new file with mode: 0644]
bpf/tests/scapy.h
bpf/tests/scapy/pkt_defs.py
bpf/tests/scapy/selftest_pkt_defs.py [new file with mode: 0644]

diff --git a/bpf/tests/_scapy_selftest.c b/bpf/tests/_scapy_selftest.c
new file mode 100644 (file)
index 0000000..5df6dd9
--- /dev/null
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright Authors of Cilium */
+
+#include <bpf/ctx/skb.h>
+#include "common.h"
+
+#include "pktgen.h"
+
+/* We need to mock loggers to test ASSERT MACRO failures */
+#define __ASSERT_TRACE_FAIL_LEN(...)
+#define __ASSERT_TRACE_FAIL_BUF(...)
+#include "scapy.h"
+
+#define fake_test_end() (void)suite_result; } while (0)
+
+#define ASSERT1 "assert1"
+#define ASSERT2 "assert2"
+
+#define ASSERT1_FAIL "assert1_fail"
+#define ASSERT2_FAIL "assert2_fail"
+
+#define LEN_SST_EXP sizeof(BUF(SST_EXP))
+#define LEN_SST_NOT_EXP sizeof(BUF(SST_NOT_EXP))
+
+/**
+ * These are here so that test_fail_now() that returns is captured
+ * for expected errors
+ */
+static __always_inline
+int force_assert_fail_off(struct __ctx_buff *ctx)
+{
+       test_init();
+
+       BUF_DECL(SST_NOT_EXP, sst_rep);
+
+       ASSERT_CTX_BUF_OFF(ASSERT1_FAIL, "Ether", ctx, 0, SST_NOT_EXP,
+                          LEN_SST_NOT_EXP);
+
+       fake_test_end();
+
+       return TEST_PASS;
+}
+
+static __always_inline
+int force_assert_fail_off2(struct __ctx_buff *ctx)
+{
+       test_init();
+
+       BUF_DECL(SST_NOT_EXP, sst_rep);
+
+       ASSERT_CTX_BUF_OFF2(ASSERT2_FAIL, "Ether", ctx, 0, SST_NOT_EXP,
+                           BUF(SST_NOT_EXP), LEN_SST_NOT_EXP,
+                           LEN_SST_NOT_EXP);
+       fake_test_end();
+
+       return TEST_PASS;
+}
+
+static struct scapy_assert null_entry = {0};
+
+PKTGEN("tc", "1_basic_test")
+int pktgen_scapy_basic_test(struct __ctx_buff *ctx)
+{
+       struct pktgen builder;
+
+       pktgen__init(&builder, ctx);
+
+       BUF_DECL(SST_EXP, sst_req);
+       BUILDER_PUSH_BUF(builder, SST_EXP);
+
+       pktgen__finish(&builder);
+
+       return 0;
+}
+
+CHECK("tc", "1_basic_test")
+int check_scapy_basic_test(struct __ctx_buff *ctx)
+{
+       int rc, id;
+       struct scapy_assert *entry;
+
+       test_init();
+
+       BUF_DECL(SST_EXP, sst_req);
+       BUF_DECL(SST_NOT_EXP, sst_rep);
+
+       ASSERT_CTX_BUF_OFF(ASSERT1, "Ether", ctx, 0, SST_EXP,
+                          LEN_SST_EXP);
+       ASSERT_CTX_BUF_OFF2(ASSERT2, "Ether", ctx, 0, SST_EXP,
+                           BUF(SST_EXP), LEN_SST_EXP, LEN_SST_EXP);
+
+       /* Test failures */
+       rc = force_assert_fail_off(ctx);
+       assert(rc == TEST_FAIL);
+       rc = force_assert_fail_off2(ctx);
+       assert(rc == TEST_FAIL);
+
+       assert(scapy_assert_map_cnt == 2);
+
+       {
+               /* ASSERT_CTX_BUF_OFF */
+               id = 0;
+               entry = map_lookup_elem(&scapy_assert_map, &id);
+               assert(entry);
+               assert(scapy_memcmp(entry->exp_buf, BUF(SST_NOT_EXP),
+                                   LEN_SST_NOT_EXP) == 0);
+               assert(scapy_memcmp(entry->got_buf, BUF(SST_EXP),
+                                   LEN_SST_EXP) == 0);
+               assert(memcmp(entry->name, ASSERT1_FAIL,
+                             sizeof(ASSERT1_FAIL)) == 0);
+               assert(entry->len == LEN_SST_NOT_EXP);
+               rc = map_update_elem(&scapy_assert_map, &id, &null_entry,
+                                    BPF_ANY);
+               assert(rc == 0);
+       }
+       {
+               /* ASSERT_CTX_BUF_OFF2 */
+               id = 1;
+               entry = map_lookup_elem(&scapy_assert_map, &id);
+               assert(entry);
+               assert(scapy_memcmp(entry->exp_buf, BUF(SST_NOT_EXP),
+                                   LEN_SST_NOT_EXP) == 0);
+               assert(scapy_memcmp(entry->got_buf, BUF(SST_EXP),
+                                   LEN_SST_EXP) == 0);
+               assert(memcmp(entry->name, ASSERT2_FAIL,
+                             sizeof(ASSERT2_FAIL)) == 0);
+               rc = map_update_elem(&scapy_assert_map, &id, &null_entry,
+                                    BPF_ANY);
+               assert(rc == 0);
+       }
+
+       test_finish();
+
+       return 0;
+}
+
+BPF_LICENSE("Dual BSD/GPL");
index 0d555073268fe5807a31fa23d3881d86f0e22230..f292a9c70b0c13bdeff75083b54d6b9b27a70886 100644 (file)
@@ -130,12 +130,16 @@ static __u32 scapy_assert_map_cnt;
 /* Needs to be here not to blow up stack size */
 static struct scapy_assert __scapy_assert = {0};
 
+#ifndef __ASSERT_TRACE_FAIL_LEN
 #define __ASSERT_TRACE_FAIL_LEN(BUF_NAME, _BUF_LEN, LEN)               \
        test_log("Buffer '" BUF_NAME "' of len (%d) < LEN  (%d)",       \
                         _BUF_LEN, LEN)
+#endif /* __ASSERT_TRACE_FAIL_LEN */
 
+#ifndef __ASSERT_TRACE_FAIL_BUF
 #define __ASSERT_TRACE_FAIL_BUF(BUF_NAME, _BUF_LEN, LEN)               \
        test_log("CTX and buffer '" BUF_NAME "' content mismatch ")
+#endif /* __ASSERT_TRACE_FAIL_BUF */
 
 static __always_inline
 bool __assert_map_add_failure(const char *name, const __u8 name_len,
index 3ef95761196b8a09bf6c90c2913fd5aff30e7478..46f7bcb8ebaf08f104f83af83a97202c9ff09437 100644 (file)
@@ -6,6 +6,7 @@ from scapy.all import *
 from pkt_defs_common import *
 
 # Test packet/buffer definitions
+from selftest_pkt_defs import *
 from ipv6_ndp_pkt_defs import *
 from tc_l2_announce_pkt_defs import *
 from tc_l2_announce6_pkt_defs import *
diff --git a/bpf/tests/scapy/selftest_pkt_defs.py b/bpf/tests/scapy/selftest_pkt_defs.py
new file mode 100644 (file)
index 0000000..1a26fe5
--- /dev/null
@@ -0,0 +1,19 @@
+# Copyright Authors of Cilium
+# SPDX-License-Identifier: Apache-2.0
+
+from scapy.all import *
+
+from pkt_defs_common import *
+
+## Scapy self tests (_scapy_selftest.c)
+sst_req = (
+    Ether(dst=mac_bcast, src=mac_one) /
+    ARP(op="who-has", psrc=v4_ext_one, pdst=v4_svc_one, \
+        hwsrc=mac_one, hwdst=mac_bcast)
+)
+
+sst_rep = (
+    Ether(dst=mac_one, src=mac_two) /
+    ARP(op="is-at", psrc=v4_svc_one, pdst=v4_ext_one, \
+        hwsrc=mac_two, hwdst=mac_one)
+)