Add a test that pairs a J1939 socket with a CAN_RAW socket so the actual CAN frames on the wire are checked in both directions: a J1939 send produces the expected CAN id (priority, PGN, source and destination) and payload, and an injected CAN frame is parsed and delivered to the J1939 socket. The base test only exercises J1939-socket-to-J1939-socket transfers, which stay on the local delivery path inside one netdevice, so the on-wire format is otherwise unchecked. Both PDU1 (destination specific) and PDU2 (broadcast) single frames are covered.
Assisted-by: LLM Signed-off-by: Oleksij Rempel <[email protected]> --- tools/testing/selftests/net/can/.gitignore | 1 + tools/testing/selftests/net/can/Makefile | 2 + .../selftests/net/can/test_j1939_wire.c | 170 ++++++++++++++++++ .../selftests/net/can/test_j1939_wire.sh | 5 + 4 files changed, 178 insertions(+) create mode 100644 tools/testing/selftests/net/can/test_j1939_wire.c create mode 100755 tools/testing/selftests/net/can/test_j1939_wire.sh diff --git a/tools/testing/selftests/net/can/.gitignore b/tools/testing/selftests/net/can/.gitignore index 7ca63a19472c..3f31fa5b7438 100644 --- a/tools/testing/selftests/net/can/.gitignore +++ b/tools/testing/selftests/net/can/.gitignore @@ -3,3 +3,4 @@ test_raw_filter test_j1939_base test_j1939_opts test_j1939_addr +test_j1939_wire diff --git a/tools/testing/selftests/net/can/Makefile b/tools/testing/selftests/net/can/Makefile index 6e4176a1b985..d80fa114a0cd 100644 --- a/tools/testing/selftests/net/can/Makefile +++ b/tools/testing/selftests/net/can/Makefile @@ -11,6 +11,7 @@ TEST_PROGS := \ test_j1939_base.sh \ test_j1939_opts.sh \ test_j1939_addr.sh \ + test_j1939_wire.sh \ TEST_FILES := j1939_lib.sh @@ -19,5 +20,6 @@ TEST_GEN_FILES := \ test_j1939_base \ test_j1939_opts \ test_j1939_addr \ + test_j1939_wire \ include ../../lib.mk diff --git a/tools/testing/selftests/net/can/test_j1939_wire.c b/tools/testing/selftests/net/can/test_j1939_wire.c new file mode 100644 index 000000000000..8f5113394f99 --- /dev/null +++ b/tools/testing/selftests/net/can/test_j1939_wire.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * J1939 wire-format selftests: a J1939 socket against CAN_RAW. + * + * Where test_j1939_base keeps transfers on the local path between two J1939 + * sockets, these pair a J1939 socket with a CAN_RAW socket and check the actual + * CAN frames on the wire both ways: a J1939 send produces the expected CAN id + * (priority, PGN, source and destination) and payload, and an injected CAN frame + * is parsed and delivered to the J1939 socket. Both destination-specific (PDU1) + * and broadcast (PDU2) single frames are covered. + * + * The vcan interface is set up by the caller (see test_j1939_wire.sh); its name + * is passed via the CANIF environment variable. + */ +#include <errno.h> + +#include "kselftest_harness.h" +#include "j1939_tests.h" + +#define SA_LOCAL 0x90 /* the J1939 socket under test */ +#define SA_PEER 0x20 /* the CAN_RAW peer */ +#define PGN_PDU1 0x0ab00 /* PF 0xab (< 240): destination specific */ +#define PGN_PDU2 0x0feee /* PF 0xfe (>= 240): broadcast */ +#define PRIO J1939_PRIO_DEFAULT + +FIXTURE(j1939_wire) +{ + int ifindex; + int j1939; + int raw; +}; + +FIXTURE_SETUP(j1939_wire) +{ + self->j1939 = -1; + self->raw = -1; + + J1939_SETUP_IFINDEX(self); + + J1939_SETUP_RAW(self, 0); +} + +FIXTURE_TEARDOWN(j1939_wire) +{ + if (self->j1939 >= 0) + close(self->j1939); + if (self->raw >= 0) + close(self->raw); +} + +/* + * Inject one CAN frame from the raw peer and confirm the J1939 socket receives + * the identical 8-byte payload. Opens the J1939 RX socket into self->j1939 (the + * fixture teardown closes it). @bcast selects a broadcast (PDU2) socket and + * frame; @pgn and @da address the injected frame. Returns 0 on success, -1 + * otherwise; the caller ASSERTs the result. + */ +static int rx_single_frame_check(FIXTURE_DATA(j1939_wire) *self, bool bcast, + pgn_t pgn, uint8_t da, const uint8_t payload[8]) +{ + uint8_t rx_buf[8]; + + if (bcast) + self->j1939 = j1939_open_flags(self->ifindex, J1939_NO_NAME, + SA_LOCAL, pgn, J1939_OPEN_BCAST); + else + self->j1939 = j1939_open(self->ifindex, SA_LOCAL, pgn); + if (self->j1939 < 0) + return -1; + + if (send_raw8(self->raw, PRIO, pgn, da, SA_PEER, payload)) + return -1; + + if (recv_timeout(self->j1939, rx_buf, sizeof(rx_buf), + J1939_RECV_TIMEOUT_MS) != (int)sizeof(rx_buf)) + return -1; + + return memcmp(rx_buf, payload, sizeof(rx_buf)) == 0 ? 0 : -1; +} + +/* + * Send one 8-byte payload from the J1939 socket to the raw peer and return the + * CAN frame seen on the wire via *frame, so each caller can assert the id fields + * it cares about. Opens the J1939 TX socket into self->j1939 (the fixture + * teardown closes it) and connects it to (@da, @pgn); @bcast opens a broadcast + * (PDU2) socket. Returns 0 on success, -1 otherwise; the caller ASSERTs the + * result. + */ +static int tx_to_raw_send_recv(FIXTURE_DATA(j1939_wire) *self, bool bcast, + pgn_t pgn, uint8_t da, const uint8_t payload[8], + struct can_frame *frame) +{ + if (bcast) + self->j1939 = j1939_open_flags(self->ifindex, J1939_NO_NAME, + SA_LOCAL, J1939_NO_PGN, + J1939_OPEN_BCAST); + else + self->j1939 = j1939_open(self->ifindex, SA_LOCAL, J1939_NO_PGN); + if (self->j1939 < 0) + return -1; + + if (j1939_connect(self->j1939, self->ifindex, da, pgn)) + return -1; + + if (send(self->j1939, payload, 8, 0) != 8) + return -1; + + if (recv_timeout(self->raw, frame, sizeof(*frame), + J1939_RECV_TIMEOUT_MS) <= 0) + return -1; + + return 0; +} + +/* J1939 TX -> CAN_RAW: destination-specific (PDU1) single frame. */ +TEST_F(j1939_wire, tx_pdu1_to_raw) +{ + uint8_t payload[8] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 }; + struct can_frame frame; + canid_t id; + + ASSERT_EQ(tx_to_raw_send_recv(self, false, PGN_PDU1, SA_PEER, payload, + &frame), 0); + id = frame.can_id & CAN_EFF_MASK; + + EXPECT_EQ(J1939_ID_PRIO(id), PRIO); + EXPECT_EQ(J1939_ID_SA(id), SA_LOCAL); /* source address */ + EXPECT_EQ(J1939_ID_PS(id), SA_PEER); /* destination (PS) */ + EXPECT_EQ(J1939_ID_PF(id), J1939_PF(PGN_PDU1)); + ASSERT_EQ(frame.can_dlc, sizeof(payload)); + EXPECT_EQ(memcmp(frame.data, payload, sizeof(payload)), 0); +} + +/* CAN_RAW -> J1939 RX: destination-specific (PDU1) single frame. */ +TEST_F(j1939_wire, rx_pdu1_from_raw) +{ + uint8_t payload[8] = { 0xde, 0xad, 0xbe, 0xef, 0x01, 0x02, 0x03, 0x04 }; + + ASSERT_EQ(rx_single_frame_check(self, false, PGN_PDU1, SA_LOCAL, + payload), 0); +} + +/* J1939 TX -> CAN_RAW: broadcast (PDU2) single frame. */ +TEST_F(j1939_wire, tx_pdu2_to_raw) +{ + uint8_t payload[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + struct can_frame frame; + canid_t id; + + ASSERT_EQ(tx_to_raw_send_recv(self, true, PGN_PDU2, J1939_NO_ADDR, + payload, &frame), 0); + id = frame.can_id & CAN_EFF_MASK; + + EXPECT_EQ(J1939_ID_PRIO(id), PRIO); + EXPECT_EQ(J1939_ID_SA(id), SA_LOCAL); /* source address */ + EXPECT_EQ((id >> 8) & J1939_PGN_MAX, PGN_PDU2); /* full broadcast PGN */ + ASSERT_EQ(frame.can_dlc, sizeof(payload)); + EXPECT_EQ(memcmp(frame.data, payload, sizeof(payload)), 0); +} + +/* CAN_RAW -> J1939 RX: broadcast (PDU2) single frame. */ +TEST_F(j1939_wire, rx_pdu2_from_raw) +{ + uint8_t payload[8] = { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7 }; + + ASSERT_EQ(rx_single_frame_check(self, true, PGN_PDU2, J1939_NO_ADDR, + payload), 0); +} + +J1939_TEST_MAIN(); diff --git a/tools/testing/selftests/net/can/test_j1939_wire.sh b/tools/testing/selftests/net/can/test_j1939_wire.sh new file mode 100755 index 000000000000..77a1bddb5854 --- /dev/null +++ b/tools/testing/selftests/net/can/test_j1939_wire.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +J1939_TEST_PROG=test_j1939_wire +source "$(dirname "$0")/j1939_lib.sh" -- 2.47.3

