From nobody Thu Sep 24 13:42:43 2026 Received: from m16.mail.163.com (m16.mail.163.com [117.135.210.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D398B3B14D2; Wed, 23 Sep 2026 03:43:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.2 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790135010; cv=none; b=Y8BlCqmyMjK2+hfOdoxLJcur8A7B+wAH4u25ln1qbKPGDGXMSL+2osH6hBcoqxaNITtc80WYZQEugCLdXKIAi/mWPBF3sCBmgW9sk83keSrsTIsnSU76pmhwYhNlOC2Hbftt8vkPNdoKNbnT8g4igdKuiOfF8izgnGWl4UGX6Yg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790135010; c=relaxed/simple; bh=uVoG7MLIklO9mSQNiRJPAPLHzAImZnjnPgSHg4UYrHY=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=gU9MlCM3Q4DBqmplTJE8j4MjkPZNZsxFWkaCQ+fsd2qVicvU5HrPilw04IrwmrPTsGKJwrlyD83DaE68c9o7hWXvIsWsVaL2Hwuyl/Wm+AaqzwvAaVMEQJN/5wOgwYMGrlVuD9zMQgOLIfPyaMjlcY0ZvgTohOi4WOoPs+aovzw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=cNf8/EeC; arc=none smtp.client-ip=117.135.210.2 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="cNf8/EeC" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=Pv WdHzOaQK1MoZGGrkPXvWCBi6wgVhZHU49kXz8ZfmI=; b=cNf8/EeC/ciBdD30mL og5y70QTzayicZVL6DIZAc/dnh+zbaaQZZSD18U6Bd7618e8wd6Mguzcd76Jet1E 6wP8xgn7MMtvHwVvURQVdtyL6j3FSNtt9+I9gKUB6VV8Ynv12jF/bGq7eppF/nD1 7XFHXmtCT9RVlKM8kartl+iyw= Received: from chaosheng-ASUS-TUF-Gaming-F16-FX607JV-FX607JV.lan (unknown []) by gzga-smtp-mtada-g0-2 (Coremail) with SMTP id _____wDX_4bKSrNqInP_AA--.22590S2; Wed, 23 Sep 2026 11:43:06 +0800 (CST) From: Quchaosheng To: Oliver Hartkopp , Marc Kleine-Budde , Shuah Khan Cc: Vincent Mailhol , linux-can@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, quchaosheng000406@163.com Subject: [PATCH v2] selftests: can: add CAN XL raw socket tests Date: Wed, 23 Sep 2026 11:42:56 +0800 Message-ID: <20260923034256.396514-1-quchaosheng000406@163.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wDX_4bKSrNqInP_AA--.22590S2 X-Coremail-Antispam: 1Uf129KBjvAXoW3KrW8WFyrWw17ury7JrykAFb_yoW8Jr4rAo ZrKa13Xr1rJr1Duayvqrn7Wa1fZr4Ut3y5AayYvryrtFyDurW2qrWSka1Sqa93JFyF9rZr XrZ2qr4fXFZYvr1fn29KB7ZKAUJUUUU8529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvjTRtXozUUUUU X-CM-SenderInfo: xtxfxtprvkv0bjqqikiqw6il2tof0z/xtbDAApMwWqzSsohjQAA3y Content-Type: text/plain; charset="utf-8" The CAN raw socket CAN XL support (CAN_RAW_XL_FRAMES, CAN_RAW_XL_VCID_OPTS) is currently untested in kselftest. The only CAN test in the tree, test_raw_filter, exercises classic CAN filter matching and never touches CAN XL. Add a set of CAN XL tests to test_raw_filter. vcan is used as the test interface, which reports CAN XL capability when the MTU is large enough, so these run without CAN hardware. Tests are skipped when the interface cannot carry CAN XL frames, so a classic CAN or CAN FD only interface does not turn them into failures. Covered behaviour: - payload round-trips for 1, 8, 64 and the maximum of 2048 bytes, including the priority, SDU type and acceptance field - a CAN XL frame is rejected with -EINVAL while CAN_RAW_XL_FRAMES is disabled - a frame without the mandatory CANXL_XLF flag is rejected - the declared length must match the payload actually written - enabling CAN XL implicitly enables CAN FD, and CAN FD cannot be disabled again while CAN XL stays enabled - the VCID is stripped by default, and a VCID tagged frame is only forwarded once CAN_RAW_XL_VCID_RX_FILTER is set - CAN_RAW_XL_VCID_TX_SET overwrites the VCID without clobbering the priority, and a non-matching RX VCID filter drops the frame A blocking read() cannot be used to assert that a frame was filtered out, as it never returns; the new recv_timeout() helper waits for a frame with a bounded timeout instead. Verified on a vcan device: 26/26 pass with the default CAN XL MTU. With the interface forced down to a classic CAN MTU the seven XL tests skip and the remaining 19 still pass. Reported-by: Sashiko Link: https://sashiko.dev/#/patchset/20260923025317.374997-1-quchaosheng000= 406@163.com?part=3D1 Assisted-by: LLM Signed-off-by: Quchaosheng --- .../selftests/net/can/test_raw_filter.c | 396 ++++++++++++++++++ 1 file changed, 396 insertions(+) diff --git a/tools/testing/selftests/net/can/test_raw_filter.c b/tools/test= ing/selftests/net/can/test_raw_filter.c index bb8ae8854273..0eac9f497b40 100644 --- a/tools/testing/selftests/net/can/test_raw_filter.c +++ b/tools/testing/selftests/net/can/test_raw_filter.c @@ -16,11 +16,19 @@ #include #include =20 +#include +#include + #include #include +#include =20 #include "kselftest_harness.h" =20 +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + #define ID 0x123 =20 char CANIF[IFNAMSIZ]; @@ -390,6 +398,394 @@ TEST_F(can_filters, test_filter) } } =20 +/* --- CAN XL --- */ + +#define XLF 0x80 + +/* CAN XL requires an interface whose MTU is large enough; a classic CAN or + * CAN FD only interface cannot carry CAN XL frames. + */ +static bool can_iface_supports_xl(void) +{ + struct ifreq ifr; + int s, ret; + + s =3D socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (s < 0) + return false; + + strncpy(ifr.ifr_name, CANIF, sizeof(ifr.ifr_name)); + ret =3D ioctl(s, SIOCGIFMTU, &ifr); + close(s); + + if (ret < 0) + return false; + + return ifr.ifr_mtu >=3D CANXL_MIN_MTU; +} + +static int open_xl_socket(int send_xl, const struct can_raw_vcid_options *= vcid) +{ + struct sockaddr_can addr =3D { }; + struct ifreq ifr; + int one =3D 1; + int s, ret; + + s =3D socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (s < 0) + return -1; + + strncpy(ifr.ifr_name, CANIF, sizeof(ifr.ifr_name)); + ret =3D ioctl(s, SIOCGIFINDEX, &ifr); + if (ret < 0) + goto close_sock; + + addr.can_family =3D AF_CAN; + addr.can_ifindex =3D ifr.ifr_ifindex; + + ret =3D bind(s, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) + goto close_sock; + + ret =3D setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &one, sizeof(on= e)); + if (ret < 0) + goto close_sock; + + if (vcid) { + ret =3D setsockopt(s, SOL_CAN_RAW, CAN_RAW_XL_VCID_OPTS, vcid, + sizeof(*vcid)); + if (ret < 0) + goto close_sock; + } + + ret =3D setsockopt(s, SOL_CAN_RAW, CAN_RAW_XL_FRAMES, &send_xl, + sizeof(send_xl)); + if (ret < 0) + goto close_sock; + + return s; + +close_sock: + close(s); + return -1; +} + +static void fill_canxl(struct canxl_frame *cxl, unsigned int len, __u8 sdt) +{ + int i; + + memset(cxl, 0, sizeof(*cxl)); + cxl->prio =3D ID; + cxl->flags =3D XLF; + cxl->sdt =3D sdt; + cxl->len =3D len; + cxl->af =3D 0xdeadbeef; + + for (i =3D 0; i < len; i++) + cxl->data[i] =3D i & 0xff; +} + +/* Wait up to 200ms for a frame. Returns the frame size, or -1 if nothing + * arrived. A blocking read() cannot be used to assert that a frame was + * filtered out, as it would simply wait forever. + */ +static int recv_timeout(int sock, void *buf, size_t len) +{ + struct timeval tv =3D { + .tv_sec =3D 0, + .tv_usec =3D 200000, + }; + fd_set rdfs; + int ret; + + FD_ZERO(&rdfs); + FD_SET(sock, &rdfs); + + ret =3D select(sock + 1, &rdfs, NULL, NULL, &tv); + if (ret <=3D 0) + return -1; + + return read(sock, buf, len); +} + +FIXTURE(canxl) { + int sock; +}; + +FIXTURE_SETUP(canxl) +{ + self->sock =3D open_xl_socket(1, NULL); +} + +FIXTURE_TEARDOWN(canxl) +{ + if (self->sock >=3D 0) + close(self->sock); +} + +/* Round-trip payload lengths, including the maximum CAN XL payload */ +TEST_F(canxl, payload_roundtrip) +{ + const unsigned int lens[] =3D { 1, 8, 64, 2048 }; + struct canxl_frame tx; + + if (!can_iface_supports_xl()) + SKIP(return, "%s does not support CAN XL", CANIF); + + ASSERT_GE(self->sock, 0) + TH_LOG("failed to open CAN XL socket on %s", CANIF); + + for (unsigned int i =3D 0; i < ARRAY_SIZE(lens); i++) { + unsigned int len =3D lens[i]; + struct canxl_frame rx; + int ret; + + fill_canxl(&tx, len, 0x11); + + ret =3D write(self->sock, &tx, CANXL_HDR_SIZE + len); + ASSERT_EQ(ret, CANXL_HDR_SIZE + len) + TH_LOG("failed to send CAN XL frame of len %u", len); + + ret =3D read(self->sock, &rx, sizeof(rx)); + ASSERT_EQ(ret, CANXL_HDR_SIZE + len) + TH_LOG("failed to receive CAN XL frame of len %u", len); + + ASSERT_EQ(rx.len, len) + TH_LOG("wrong payload length in received frame"); + ASSERT_EQ(rx.sdt, tx.sdt) + TH_LOG("wrong SDU type in received frame"); + ASSERT_EQ(rx.af, tx.af) + TH_LOG("wrong acceptance field in received frame"); + ASSERT_EQ(rx.prio, tx.prio) + TH_LOG("wrong priority in received frame"); + ASSERT_EQ(memcmp(rx.data, tx.data, len), 0) + TH_LOG("payload corrupted"); + } +} + +/* A CAN XL frame must not be accepted when CAN_RAW_XL_FRAMES is off */ +TEST(canxl_send_requires_xl_frames) +{ + struct canxl_frame tx; + int s, ret; + + /* a plain socket that did not enable CAN XL */ + s =3D open_xl_socket(0, NULL); + ASSERT_GE(s, 0) + TH_LOG("failed to open CAN socket on %s", CANIF); + + fill_canxl(&tx, 8, 0x11); + + ret =3D write(s, &tx, CANXL_HDR_SIZE + tx.len); + ASSERT_LT(ret, 0) + TH_LOG("CAN XL frame accepted without CAN_RAW_XL_FRAMES"); + ASSERT_EQ(errno, EINVAL); + + close(s); +} + +/* A frame without the mandatory CANXL_XLF flag must be rejected */ +TEST(canxl_requires_xlf_flag) +{ + struct canxl_frame tx; + int s, ret; + + s =3D open_xl_socket(1, NULL); + if (!can_iface_supports_xl()) + SKIP(return, "%s does not support CAN XL", CANIF); + + ASSERT_GE(s, 0) + TH_LOG("failed to open CAN XL socket on %s", CANIF); + + fill_canxl(&tx, 8, 0x11); + tx.flags =3D 0; + + ret =3D write(s, &tx, CANXL_HDR_SIZE + tx.len); + ASSERT_LT(ret, 0) + TH_LOG("frame without CANXL_XLF accepted"); + ASSERT_EQ(errno, EINVAL); + + close(s); +} + +/* The declared length must match the payload actually written */ +TEST(canxl_declared_len_must_match_payload) +{ + struct canxl_frame tx; + int s, ret; + + s =3D open_xl_socket(1, NULL); + if (!can_iface_supports_xl()) + SKIP(return, "%s does not support CAN XL", CANIF); + + ASSERT_GE(s, 0) + TH_LOG("failed to open CAN XL socket on %s", CANIF); + + fill_canxl(&tx, 8, 0x11); + tx.len =3D 100; + + ret =3D write(s, &tx, CANXL_HDR_SIZE + 8); + ASSERT_LT(ret, 0) + TH_LOG("frame with mismatching length accepted"); + ASSERT_EQ(errno, EINVAL); + + close(s); +} + +/* Enabling CAN XL must implicitly enable CAN FD, and it must not be turna= ble + * off again while CAN XL stays enabled + */ +TEST(canxl_enables_fd_and_cannot_be_disabled) +{ + struct canfd_frame tx; + struct canxl_frame rx; + int s, ret; + int zero =3D 0; + + s =3D open_xl_socket(1, NULL); + if (!can_iface_supports_xl()) + SKIP(return, "%s does not support CAN XL", CANIF); + + ASSERT_GE(s, 0) + TH_LOG("failed to open CAN XL socket on %s", CANIF); + + ret =3D setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &zero, sizeof(zero)= ); + ASSERT_LT(ret, 0) + TH_LOG("CAN FD could be disabled while CAN XL was enabled"); + ASSERT_EQ(errno, EINVAL); + + memset(&tx, 0, sizeof(tx)); + tx.can_id =3D ID; + tx.len =3D 8; + + ret =3D write(s, &tx, CANFD_MTU); + ASSERT_EQ(ret, CANFD_MTU) + TH_LOG("CAN FD not implicitly enabled by CAN XL"); + + ret =3D read(s, &rx, sizeof(rx)); + ASSERT_EQ(ret, CANFD_MTU) + TH_LOG("CAN FD frame not delivered to a CAN XL socket"); + + close(s); +} + +/* Without VCID options the VCID is stripped on TX and a VCID-tagged frame= is + * not forwarded on RX + */ +TEST(canxl_vcid_stripped_by_default) +{ + struct can_raw_vcid_options pass =3D { + .flags =3D CAN_RAW_XL_VCID_TX_PASS, + }; + struct canxl_frame tx, rx; + int tx_sock, rx_sock, ret; + + if (!can_iface_supports_xl()) + SKIP(return, "%s does not support CAN XL", CANIF); + + tx_sock =3D open_xl_socket(1, &pass); + ASSERT_GE(tx_sock, 0) + TH_LOG("CAN XL not supported on %s", CANIF); + + rx_sock =3D open_xl_socket(1, NULL); + ASSERT_GE(rx_sock, 0); + + fill_canxl(&tx, 8, 0x11); + tx.prio |=3D 0x42 << CANXL_VCID_OFFSET; + + ret =3D write(tx_sock, &tx, CANXL_HDR_SIZE + tx.len); + ASSERT_EQ(ret, CANXL_HDR_SIZE + tx.len); + + /* the RX socket has no VCID filter, so it drops VCID tagged frames */ + ret =3D recv_timeout(rx_sock, &rx, sizeof(rx)); + ASSERT_LT(ret, 0) + TH_LOG("VCID tagged frame forwarded without CAN_RAW_XL_VCID_RX_FILTER"); + + close(tx_sock); + close(rx_sock); +} + +/* CAN_RAW_XL_VCID_TX_SET overwrites the VCID, and the RX side can select = it */ +TEST(canxl_vcid_set_and_filter) +{ + struct can_raw_vcid_options opts =3D { + .flags =3D CAN_RAW_XL_VCID_TX_SET, + .tx_vcid =3D 0x77, + }; + struct can_raw_vcid_options filter =3D { + .flags =3D CAN_RAW_XL_VCID_RX_FILTER, + .rx_vcid =3D 0x77, + .rx_vcid_mask =3D 0xff, + }; + struct canxl_frame tx, rx; + int tx_sock, rx_sock, ret; + + tx_sock =3D open_xl_socket(1, &opts); + if (!can_iface_supports_xl()) + SKIP(return, "%s does not support CAN XL", CANIF); + + ASSERT_GE(tx_sock, 0) + TH_LOG("failed to open CAN XL VCID socket on %s", CANIF); + + rx_sock =3D open_xl_socket(1, &filter); + ASSERT_GE(rx_sock, 0); + + fill_canxl(&tx, 8, 0x11); + tx.prio |=3D 0x42 << CANXL_VCID_OFFSET; + + ret =3D write(tx_sock, &tx, CANXL_HDR_SIZE + tx.len); + ASSERT_EQ(ret, CANXL_HDR_SIZE + tx.len); + + ret =3D read(rx_sock, &rx, sizeof(rx)); + ASSERT_EQ(ret, CANXL_HDR_SIZE + 8); + + ASSERT_EQ((rx.prio & CANXL_VCID_MASK) >> CANXL_VCID_OFFSET, 0x77) + TH_LOG("CAN_RAW_XL_VCID_TX_SET did not overwrite the VCID"); + ASSERT_EQ(rx.prio & CANXL_PRIO_MASK, ID) + TH_LOG("priority clobbered by the VCID option"); + + close(tx_sock); + close(rx_sock); +} + +/* A non-matching VCID filter must not forward the frame */ +TEST(canxl_vcid_filter_mismatch) +{ + struct can_raw_vcid_options pass =3D { + .flags =3D CAN_RAW_XL_VCID_TX_PASS, + }; + struct can_raw_vcid_options filter =3D { + .flags =3D CAN_RAW_XL_VCID_RX_FILTER, + .rx_vcid =3D 0x99, + .rx_vcid_mask =3D 0xff, + }; + struct canxl_frame tx, rx; + int tx_sock, rx_sock, ret; + + tx_sock =3D open_xl_socket(1, &pass); + if (!can_iface_supports_xl()) + SKIP(return, "%s does not support CAN XL", CANIF); + + ASSERT_GE(tx_sock, 0) + TH_LOG("failed to open CAN XL VCID socket on %s", CANIF); + + rx_sock =3D open_xl_socket(1, &filter); + ASSERT_GE(rx_sock, 0); + + fill_canxl(&tx, 8, 0x11); + tx.prio |=3D 0x42 << CANXL_VCID_OFFSET; + + ret =3D write(tx_sock, &tx, CANXL_HDR_SIZE + tx.len); + ASSERT_EQ(ret, CANXL_HDR_SIZE + tx.len); + + ret =3D recv_timeout(rx_sock, &rx, sizeof(rx)); + ASSERT_LT(ret, 0) + TH_LOG("frame with non-matching VCID was forwarded"); + + close(tx_sock); + close(rx_sock); +} + int main(int argc, char **argv) { char *ifname =3D getenv("CANIF"); --=20 2.43.0