[PATCH can-next v2 9/9] selftests: can: check SO_J1939_PROMISC receive effect
From: Oleksij Rempel
Date: Wed Nov 04 2026 - 11:21:29 EST
Add a test that SO_J1939_PROMISC changes what the RX path delivers. The
existing coverage only round-trips the flag through set/getsockopt, so a
kernel that stores the flag but never acts on it still passes.
It sends two frames, once before and once after enabling the flag. Each
misses one of the socket's filters, the bound address or the bound PGN.
Only the frames sent after enabling the flag must arrive.
Signed-off-by: Oleksij Rempel <o.rempel@xxxxxxxxxxxxxx>
---
Changes in v2:
- drop the Assisted-by trailer; the cover letter says how an LLM was used
- wrap at 80 columns and fix the checkpatch warnings (Marc)
- name all constants and avoid casts
- check the address and the PGN filter each on its own
---
tools/testing/selftests/net/can/test_j1939_opts.c | 64 ++++++++++++++++++++++-
1 file changed, 62 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/net/can/test_j1939_opts.c b/tools/testing/selftests/net/can/test_j1939_opts.c
index 10d0e379840277cddbe9d1f5090f900ce7dd65dc..bbf2698de7d9491fca4881ade4c63284926ef9eb 100644
--- a/tools/testing/selftests/net/can/test_j1939_opts.c
+++ b/tools/testing/selftests/net/can/test_j1939_opts.c
@@ -6,8 +6,9 @@
* and addressing: the SO_J1939_FILTER PGN whitelist, the SO_J1939_SEND_PRIO
* send priority seen in the CAN id (via a CAN_RAW peer, including the kernel's
* default), unconnected sendto(2), a wrong-family bind rejection,
- * SO_J1939_ERRQUEUE TX-completion notifications, and set/getsockopt
- * round-trips of SO_J1939_SEND_PRIO, SO_J1939_PROMISC and SO_J1939_ERRQUEUE.
+ * SO_J1939_ERRQUEUE TX-completion notifications, the SO_J1939_PROMISC receive
+ * effect, and set/getsockopt round-trips of SO_J1939_SEND_PRIO,
+ * SO_J1939_PROMISC and SO_J1939_ERRQUEUE.
*
* Run by test_j1939_opts.sh, see j1939_tests.h.
*/
@@ -20,6 +21,9 @@
/* arbitrary addresses */
#define SA_TX 0x80
#define SA_RX 0x90
+#define SA_OTHER 0x50 /* neither SA_TX nor SA_RX */
+_Static_assert(SA_OTHER != SA_TX && SA_OTHER != SA_RX && SA_TX != SA_RX,
+ "test addresses overlap");
/* two different destination-specific PGNs, otherwise arbitrary */
#define PGN_A 0x0ab00
@@ -130,6 +134,62 @@ TEST_F(j1939_opts, filter_pgn)
TH_LOG("PGN_B was received but should have been filtered out");
}
+/*
+ * Frames that a socket bound to (SA_RX, PGN_A) must not get without
+ * SO_J1939_PROMISC: each misses one of the two, so each filter is tested on
+ * its own.
+ */
+static const struct {
+ uint8_t da;
+ pgn_t pgn;
+} promisc_probes[] = {
+ { SA_RX, PGN_B }, /* the bound address, another PGN */
+ { SA_OTHER, PGN_A }, /* another address, the bound PGN */
+};
+
+/* SO_J1939_PROMISC delivers frames that do not match the bound (addr, pgn). */
+TEST_F(j1939_opts, promisc_receive)
+{
+ uint8_t tx_buf[CAN_MAX_DLEN];
+ uint8_t rx_buf[CAN_MAX_DLEN];
+ int enable = 1;
+ size_t i;
+
+ fill_pattern(tx_buf, sizeof(tx_buf));
+
+ self->rx = j1939_open(self->ifindex, SA_RX, PGN_A);
+ ASSERT_GE(self->rx, 0);
+ self->tx = j1939_open(self->ifindex, SA_TX, J1939_NO_PGN);
+ ASSERT_GE(self->tx, 0);
+
+ for (i = 0; i < ARRAY_SIZE(promisc_probes); i++) {
+ ASSERT_TRUE(j1939_sendto_ok(self->tx, tx_buf, sizeof(tx_buf),
+ self->ifindex, J1939_NO_NAME,
+ promisc_probes[i].da,
+ promisc_probes[i].pgn))
+ TH_LOG("sendto probe %zu failed: %d", i, errno);
+ EXPECT_EQ(recv_timeout(self->rx, rx_buf, sizeof(rx_buf),
+ J1939_NEG_RECV_TIMEOUT_MS), -1)
+ TH_LOG("probe %zu was received without promisc", i);
+ }
+
+ /* SO_J1939_PROMISC returns the flag value (1), not 0, on success. */
+ ASSERT_GE(setsockopt(self->rx, SOL_CAN_J1939, SO_J1939_PROMISC,
+ &enable, sizeof(enable)), 0)
+ TH_LOG("SO_J1939_PROMISC failed: %d", errno);
+
+ for (i = 0; i < ARRAY_SIZE(promisc_probes); i++) {
+ ASSERT_TRUE(j1939_sendto_ok(self->tx, tx_buf, sizeof(tx_buf),
+ self->ifindex, J1939_NO_NAME,
+ promisc_probes[i].da,
+ promisc_probes[i].pgn))
+ TH_LOG("sendto probe %zu failed: %d", i, errno);
+ EXPECT_TRUE(recv_verify_ok(self->rx, tx_buf, sizeof(tx_buf),
+ J1939_RECV_TIMEOUT_MS))
+ TH_LOG("probe %zu not received despite promisc", i);
+ }
+}
+
/* Without SO_J1939_SEND_PRIO the kernel's default priority is used. */
TEST_F(j1939_opts, send_prio_default)
{
--
2.47.3