[PATCH can-next v1 8/8] selftests: can: check SO_J1939_PROMISC receive effect

From: Oleksij Rempel

Date: Wed Oct 14 2026 - 12:07:55 EST


Add a test that exercises the SO_J1939_PROMISC receive effect. The
existing coverage only round-trips the flag through set/getsockopt, so a
kernel that stores it but never acts on it in the RX path stays green.

The test binds an rx socket to a specific (address, PGN), checks that a
frame to a different destination address and PGN is not delivered, then
enables SO_J1939_PROMISC and checks the same otherwise-unmatched frame is
now delivered unchanged.

Assisted-by: LLM
Signed-off-by: Oleksij Rempel <o.rempel@xxxxxxxxxxxxxx>
---
.../selftests/net/can/test_j1939_opts.c | 46 ++++++++++++++++++-
1 file changed, 44 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 f761a42db213..3afda9b4e4a8 100644
--- a/tools/testing/selftests/net/can/test_j1939_opts.c
+++ b/tools/testing/selftests/net/can/test_j1939_opts.c
@@ -5,8 +5,8 @@
* On top of the basic transport, cover the user-visible J1939 socket options 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 default of 6),
- * unconnected sendto(2), a wrong-family bind rejection, and SO_J1939_ERRQUEUE
- * TX-completion notifications.
+ * unconnected sendto(2), a wrong-family bind rejection, SO_J1939_ERRQUEUE
+ * TX-completion notifications, and the SO_J1939_PROMISC receive effect.
*
* The vcan interface is set up by the caller (see test_j1939_opts.sh); its name
* is passed via the CANIF environment variable.
@@ -22,6 +22,7 @@

#define SA_TX 0x80
#define SA_RX 0x90
+#define SA_OTHER 0x50
#define PGN_A 0x0ab00
#define PGN_B 0x0cd00
#define PRIO_SET 3
@@ -123,6 +124,47 @@ TEST_F(j1939_opts, filter_pgn)
TH_LOG("PGN_B was received but should have been filtered out");
}

+/* SO_J1939_PROMISC delivers a frame that does not match the bound (addr,pgn). */
+TEST_F(j1939_opts, promisc_receive)
+{
+ uint8_t tx_buf[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ uint8_t rx_buf[8];
+ int enable = 1;
+ int ret;
+
+ 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);
+
+ /*
+ * This frame uses a different address and PGN than rx is bound to.
+ * Without promisc, rx must not receive it.
+ */
+ ret = j1939_sendto(self->tx, tx_buf, sizeof(tx_buf), self->ifindex,
+ J1939_NO_NAME, SA_OTHER, PGN_B);
+ ASSERT_EQ(ret, (int)sizeof(tx_buf))
+ TH_LOG("sendto unmatched frame failed: %d", errno);
+ EXPECT_EQ(recv_timeout(self->rx, rx_buf, sizeof(rx_buf),
+ J1939_NEG_RECV_TIMEOUT_MS), -1)
+ TH_LOG("unmatched frame was received without promisc");
+
+ /* 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);
+
+ /* The same otherwise-unmatched frame must now be delivered. */
+ ret = j1939_sendto(self->tx, tx_buf, sizeof(tx_buf), self->ifindex,
+ J1939_NO_NAME, SA_OTHER, PGN_B);
+ ASSERT_EQ(ret, (int)sizeof(tx_buf))
+ TH_LOG("sendto unmatched frame failed: %d", errno);
+ ASSERT_EQ(recv_timeout(self->rx, rx_buf, sizeof(rx_buf),
+ J1939_RECV_TIMEOUT_MS), (int)sizeof(tx_buf))
+ TH_LOG("unmatched frame not received despite promisc");
+ EXPECT_EQ(memcmp(tx_buf, rx_buf, sizeof(tx_buf)), 0);
+}
+
/* without SO_J1939_SEND_PRIO the J1939 default priority (6) is used. */
TEST_F(j1939_opts, send_prio_default)
{
--
2.47.3