From nobody Tue Jun 16 19:35:42 2026 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (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 44874243376 for ; Mon, 20 Apr 2026 15:22:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776698557; cv=none; b=OuWJSeZVxNU4N9BmWzt5+5lGdt3JZdQxLNIrRsGgwKeYEUCcjIQY1OgddUBrbN74xSErKINRNM0HG2tdxX/3TlST9KkmWrFCrOWmDh3dgmB8+oaevTyLFEui+z8Hijvry59CSg1VXwwIz6FleA5MXDhvwMNOLpeMs5wacsUWxaY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776698557; c=relaxed/simple; bh=0KaerXIyJN3eXCD1YIorjpzmF1dKF4uSJl0beyOMDpk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PlmT8QLijxv7ovhPt9yJpXZFH+SsyQdycRkpqwb3Vy25xJVYbjwTWeDWjMnXHt3Gmtm19Fg/AxOI3/Qxt5JHCwU06pKTWQw4wYQy7QxaBNWOuZ7+JADpq7D73wf78Nf8Ehh0Vk2rJ4iiRk4v0+DU/zVISKzWnuV6EJN67CyGAv8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1wEqSE-0000bd-7P; Mon, 20 Apr 2026 17:22:30 +0200 Received: from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac] helo=dude04) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wEqSD-006MEv-3D; Mon, 20 Apr 2026 17:22:30 +0200 Received: from ore by dude04 with local (Exim 4.98.2) (envelope-from ) id 1wEqSD-00000002RXM-3o1c; Mon, 20 Apr 2026 17:22:29 +0200 From: Oleksij Rempel To: Robin van der Gracht , Oliver Hartkopp , Marc Kleine-Budde Cc: Oleksij Rempel , kernel@pengutronix.de, linux-can@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v1 1/3] net: can: j1939: move j1939_xtp_abort enum to header Date: Mon, 20 Apr 2026 17:22:25 +0200 Message-ID: <20260420152228.581421-2-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260420152228.581421-1-o.rempel@pengutronix.de> References: <20260420152228.581421-1-o.rempel@pengutronix.de> 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-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Move j1939_xtp_abort to j1939-priv.h to allow KUnit tests to reference transport abort codes. No functional change. Signed-off-by: Oleksij Rempel --- net/can/j1939/j1939-priv.h | 92 ++++++++++++++++++++++++++++++++++++++ net/can/j1939/transport.c | 91 ------------------------------------- 2 files changed, 92 insertions(+), 91 deletions(-) diff --git a/net/can/j1939/j1939-priv.h b/net/can/j1939/j1939-priv.h index 81f58924b4ac..b82706505103 100644 --- a/net/can/j1939/j1939-priv.h +++ b/net/can/j1939/j1939-priv.h @@ -28,6 +28,98 @@ enum j1939_sk_errqueue_type { J1939_ERRQUEUE_RX_ABORT, }; =20 +/* J1939 Transport Protocol Abort Codes */ +enum j1939_xtp_abort { + J1939_XTP_NO_ABORT =3D 0, + J1939_XTP_ABORT_BUSY =3D 1, + /* Already in one or more connection managed sessions and + * cannot support another. + * + * EALREADY: + * Operation already in progress + */ + + J1939_XTP_ABORT_RESOURCE =3D 2, + /* System resources were needed for another task so this + * connection managed session was terminated. + * + * EMSGSIZE: + * The socket type requires that message be sent atomically, + * and the size of the message to be sent made this + * impossible. + */ + + J1939_XTP_ABORT_TIMEOUT =3D 3, + /* A timeout occurred and this is the connection abort to + * close the session. + * + * EHOSTUNREACH: + * The destination host cannot be reached (probably because + * the host is down or a remote router cannot reach it). + */ + + J1939_XTP_ABORT_GENERIC =3D 4, + /* CTS messages received when data transfer is in progress + * + * EBADMSG: + * Not a data message + */ + + J1939_XTP_ABORT_FAULT =3D 5, + /* Maximal retransmit request limit reached + * + * ENOTRECOVERABLE: + * State not recoverable + */ + + J1939_XTP_ABORT_UNEXPECTED_DATA =3D 6, + /* Unexpected data transfer packet + * + * ENOTCONN: + * Transport endpoint is not connected + */ + + J1939_XTP_ABORT_BAD_SEQ =3D 7, + /* Bad sequence number (and software is not able to recover) + * + * EILSEQ: + * Illegal byte sequence + */ + + J1939_XTP_ABORT_DUP_SEQ =3D 8, + /* Duplicate sequence number (and software is not able to + * recover) + */ + + J1939_XTP_ABORT_EDPO_UNEXPECTED =3D 9, + /* Unexpected EDPO packet (ETP) or Message size > 1785 bytes + * (TP) + */ + + J1939_XTP_ABORT_BAD_EDPO_PGN =3D 10, + /* Unexpected EDPO PGN (PGN in EDPO is bad) */ + + J1939_XTP_ABORT_EDPO_OUTOF_CTS =3D 11, + /* EDPO number of packets is greater than CTS */ + + J1939_XTP_ABORT_BAD_EDPO_OFFSET =3D 12, + /* Bad EDPO offset */ + + J1939_XTP_ABORT_OTHER_DEPRECATED =3D 13, + /* Deprecated. Use 250 instead (Any other reason) */ + + J1939_XTP_ABORT_ECTS_UNXPECTED_PGN =3D 14, + /* Unexpected ECTS PGN (PGN in ECTS is bad) */ + + J1939_XTP_ABORT_ECTS_TOO_BIG =3D 15, + /* ECTS requested packets exceeds message size */ + + J1939_XTP_ABORT_OTHER =3D 250, + /* Any other reason (if a Connection Abort reason is + * identified that is not listed in the table use code 250) + */ +}; + /* j1939 devices */ struct j1939_ecu { struct list_head list; diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index df93d57907da..46540da76ca9 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -32,97 +32,6 @@ #define J1939_ETP_CMD_EOMA 0x17 #define J1939_ETP_CMD_ABORT 0xff =20 -enum j1939_xtp_abort { - J1939_XTP_NO_ABORT =3D 0, - J1939_XTP_ABORT_BUSY =3D 1, - /* Already in one or more connection managed sessions and - * cannot support another. - * - * EALREADY: - * Operation already in progress - */ - - J1939_XTP_ABORT_RESOURCE =3D 2, - /* System resources were needed for another task so this - * connection managed session was terminated. - * - * EMSGSIZE: - * The socket type requires that message be sent atomically, - * and the size of the message to be sent made this - * impossible. - */ - - J1939_XTP_ABORT_TIMEOUT =3D 3, - /* A timeout occurred and this is the connection abort to - * close the session. - * - * EHOSTUNREACH: - * The destination host cannot be reached (probably because - * the host is down or a remote router cannot reach it). - */ - - J1939_XTP_ABORT_GENERIC =3D 4, - /* CTS messages received when data transfer is in progress - * - * EBADMSG: - * Not a data message - */ - - J1939_XTP_ABORT_FAULT =3D 5, - /* Maximal retransmit request limit reached - * - * ENOTRECOVERABLE: - * State not recoverable - */ - - J1939_XTP_ABORT_UNEXPECTED_DATA =3D 6, - /* Unexpected data transfer packet - * - * ENOTCONN: - * Transport endpoint is not connected - */ - - J1939_XTP_ABORT_BAD_SEQ =3D 7, - /* Bad sequence number (and software is not able to recover) - * - * EILSEQ: - * Illegal byte sequence - */ - - J1939_XTP_ABORT_DUP_SEQ =3D 8, - /* Duplicate sequence number (and software is not able to - * recover) - */ - - J1939_XTP_ABORT_EDPO_UNEXPECTED =3D 9, - /* Unexpected EDPO packet (ETP) or Message size > 1785 bytes - * (TP) - */ - - J1939_XTP_ABORT_BAD_EDPO_PGN =3D 10, - /* Unexpected EDPO PGN (PGN in EDPO is bad) */ - - J1939_XTP_ABORT_EDPO_OUTOF_CTS =3D 11, - /* EDPO number of packets is greater than CTS */ - - J1939_XTP_ABORT_BAD_EDPO_OFFSET =3D 12, - /* Bad EDPO offset */ - - J1939_XTP_ABORT_OTHER_DEPRECATED =3D 13, - /* Deprecated. Use 250 instead (Any other reason) */ - - J1939_XTP_ABORT_ECTS_UNXPECTED_PGN =3D 14, - /* Unexpected ECTS PGN (PGN in ECTS is bad) */ - - J1939_XTP_ABORT_ECTS_TOO_BIG =3D 15, - /* ECTS requested packets exceeds message size */ - - J1939_XTP_ABORT_OTHER =3D 250, - /* Any other reason (if a Connection Abort reason is - * identified that is not listed in the table use code 250) - */ -}; - static unsigned int j1939_tp_block =3D 255; static unsigned int j1939_tp_packet_delay; static unsigned int j1939_tp_padding =3D 1; --=20 2.47.3 From nobody Tue Jun 16 19:35:42 2026 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (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 7279822259F for ; Mon, 20 Apr 2026 15:22:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776698557; cv=none; b=R4ppN9hx8sioTdjNRpy3qHIjx8OllAUsnfFSWD2s6GbWEyvxKB1426LwuO5UDsx9hY+ZIR/sBki1IMzZqKUypmWiTZXieNJKySHQJOskPjj92DV01Cr585JnepyzkaUf5VvnxErrzdP3ToGUveAbQhV0Mf5X4l6nZuG+pVpLyws= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776698557; c=relaxed/simple; bh=q/QVc6ij1mBGLl8lUSqFUGhEz8ttoxN7gPO6+7gXinM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=R7RLaWtpQbk94yiFryz5iWVn0g2NHQDuznOBSxa1S1n8pw/AeJVsgDtFDuFoijbM3JrB0RPTaRxCZ6HnzeYxHiVEhvwQrk1AOkUSalA+K3kngBo4z9+R4Nk1D/6lEqOOmMlgMII3qDYQeAbesGaKzkmZGCBzQA2VWVuhM3eh1a0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1wEqSE-0000be-9b; Mon, 20 Apr 2026 17:22:30 +0200 Received: from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac] helo=dude04) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wEqSE-006MEy-0B; Mon, 20 Apr 2026 17:22:30 +0200 Received: from ore by dude04 with local (Exim 4.98.2) (envelope-from ) id 1wEqSD-00000002RXt-45So; Mon, 20 Apr 2026 17:22:29 +0200 From: Oleksij Rempel To: Robin van der Gracht , Oliver Hartkopp , Marc Kleine-Budde Cc: Oleksij Rempel , kernel@pengutronix.de, linux-can@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v1 2/3] net: can: j1939: move J1939_MIN_NAMELEN to shared header Date: Mon, 20 Apr 2026 17:22:26 +0200 Message-ID: <20260420152228.581421-3-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260420152228.581421-1-o.rempel@pengutronix.de> References: <20260420152228.581421-1-o.rempel@pengutronix.de> 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-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Move J1939_MIN_NAMELEN macro from socket.c to j1939-priv.h to prepare for KUnit test infrastructure. No functional change. Signed-off-by: Oleksij Rempel --- net/can/j1939/j1939-priv.h | 2 ++ net/can/j1939/socket.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/can/j1939/j1939-priv.h b/net/can/j1939/j1939-priv.h index b82706505103..cf646644f538 100644 --- a/net/can/j1939/j1939-priv.h +++ b/net/can/j1939/j1939-priv.h @@ -12,6 +12,8 @@ #include #include =20 +#define J1939_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_addr.= j1939) + /* Timeout to receive the abort signal over loop back. In case CAN * bus is open, the timeout should be triggered. */ diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index 0502b030d238..2191bfe117dc 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -21,8 +21,6 @@ =20 #include "j1939-priv.h" =20 -#define J1939_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_addr.= j1939) - /* conversion function between struct sock::sk_priority from linux and * j1939 priority field */ --=20 2.47.3 From nobody Tue Jun 16 19:35:42 2026 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (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 41EF924BBEE for ; Mon, 20 Apr 2026 15:22:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776698557; cv=none; b=sPFJusfww6T+RHqsKux449GboKqUFcR3pVjLKYIDEPMCGtfF8yq512delrtW2tzZQGsub3jPSQPpWMu0hm2pvB5Vd7Db0zDuIyNBCT/s8qpMVIxwQxqCREocWsy2cA6HZxOhE8puPfelF5KZLR5mB4kf2F8XL0iq3m4TLU2h+zY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776698557; c=relaxed/simple; bh=BZpC3Kif4rGHp2DZw6xJowV+92gw6RThBMkSSqu/JTc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QASLHCK0zytR1uDMFo4BY4MYCEmz+dfjXdsYgAc+Vi3aEZmfpy4oqSTxEVvJgwK/8ymJZxmVxb4qiA0Cg6dMcTbW6LL2a8hdj9v2Z17bjrkCJ8KheDmZJKuSlESgzzejybRRj2CpL2SsIRJOY5uItr5INs1mnI0WIWWe8e9pW0M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1wEqSE-0000bf-BC; Mon, 20 Apr 2026 17:22:30 +0200 Received: from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac] helo=dude04) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wEqSE-006MF1-0N; Mon, 20 Apr 2026 17:22:30 +0200 Received: from ore by dude04 with local (Exim 4.98.2) (envelope-from ) id 1wEqSE-00000002RYL-051S; Mon, 20 Apr 2026 17:22:30 +0200 From: Oleksij Rempel To: Robin van der Gracht , Oliver Hartkopp , Marc Kleine-Budde Cc: Oleksij Rempel , kernel@pengutronix.de, linux-can@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v1 3/3] net: can: j1939: Add initial KUnit tests for socket and transport layers Date: Mon, 20 Apr 2026 17:22:27 +0200 Message-ID: <20260420152228.581421-4-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260420152228.581421-1-o.rempel@pengutronix.de> References: <20260420152228.581421-1-o.rempel@pengutronix.de> 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-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add initial KUnit test coverage for J1939 socket and transport layer functions. These tests scratch the surface of the existing code and catch low-hanging fruit in validation logic. Tests cover priority conversion, PGN validation, address sanity checks, socket-to-sockaddr conversion, abort code handling, control message parsing, and broadcast detection. Export internal functions with VISIBLE_IF_KUNIT/EXPORT_SYMBOL_IF_KUNIT to enable testing. Tests validate both correct inputs and defensive handling of invalid values (NULL pointers, out-of-range, wrong families) to prevent kernel crashes. Signed-off-by: Oleksij Rempel --- net/can/j1939/Kconfig | 12 + net/can/j1939/Makefile | 2 + net/can/j1939/j1939-test.h | 69 ++++ net/can/j1939/socket.c | 41 ++- net/can/j1939/tests/.kunitconfig | 5 + net/can/j1939/tests/Makefile | 4 + net/can/j1939/tests/socket_test.c | 301 +++++++++++++++ net/can/j1939/tests/transport_test.c | 533 +++++++++++++++++++++++++++ net/can/j1939/transport.c | 51 ++- 9 files changed, 1008 insertions(+), 10 deletions(-) create mode 100644 net/can/j1939/j1939-test.h create mode 100644 net/can/j1939/tests/.kunitconfig create mode 100644 net/can/j1939/tests/Makefile create mode 100644 net/can/j1939/tests/socket_test.c create mode 100644 net/can/j1939/tests/transport_test.c diff --git a/net/can/j1939/Kconfig b/net/can/j1939/Kconfig index 2998298b71ec..8a3ee0370d33 100644 --- a/net/can/j1939/Kconfig +++ b/net/can/j1939/Kconfig @@ -13,3 +13,15 @@ config CAN_J1939 The relevant parts in kernel are SAE j1939-21 (datalink & transport protocol) & SAE j1939-81 (network management). + +config CAN_J1939_KUNIT_TEST + tristate "KUnit tests for CAN J1939 sockets" if !KUNIT_ALL_TESTS + depends on KUNIT && CAN_J1939 + default KUNIT_ALL_TESTS + help + This builds unit tests for the J1939 socket utility functions. + + For more information on KUnit and unit tests in general, please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. diff --git a/net/can/j1939/Makefile b/net/can/j1939/Makefile index 19181bdae173..18979ae11c98 100644 --- a/net/can/j1939/Makefile +++ b/net/can/j1939/Makefile @@ -8,3 +8,5 @@ can-j1939-objs :=3D \ main.o \ socket.o \ transport.o + +obj-$(CONFIG_CAN_J1939_KUNIT_TEST) +=3D tests/ diff --git a/net/can/j1939/j1939-test.h b/net/can/j1939/j1939-test.h new file mode 100644 index 000000000000..60169571c19e --- /dev/null +++ b/net/can/j1939/j1939-test.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * J1939 test-visible functions + * + * This header exposes internal functions for KUnit testing. + * + * Safe to include unconditionally - empty when CONFIG_KUNIT=3Dn. + * Functions are static (via VISIBLE_IF_KUNIT) when testing disabled. + */ + +#ifndef _J1939_TEST_H_ +#define _J1939_TEST_H_ + +#if IS_ENABLED(CONFIG_KUNIT) + +#include +#include +#include +#include +#include + +/* From socket.c - exposed for KUnit testing via wrapper functions + * The actual implementations are static inline for performance; these wra= ppers + * allow testing without impacting production code. + */ + +priority_t j1939_prio_wrapper(u32 sk_priority); +u32 j1939_to_sk_priority_wrapper(priority_t prio); +bool j1939_pgn_is_valid_wrapper(pgn_t pgn); +bool j1939_pgn_is_clean_pdu_wrapper(pgn_t pgn); +int j1939_sk_sanity_check(struct sockaddr_can *addr, int len); + +struct j1939_sock; +void j1939_sk_sock2sockaddr_can(struct sockaddr_can *addr, + const struct j1939_sock *jsk, int peer); + +enum j1939_sk_errqueue_type; +size_t j1939_sk_opt_stats_get_size(enum j1939_sk_errqueue_type type); + +/* From transport.c - exposed for KUnit testing + * Functions with _wrapper suffix are non-inline wrappers around static in= line + * implementations to avoid performance impact on production code. + */ + +/* Forward declarations for structures (full definitions in j1939-priv.h) = */ +struct j1939_priv; +struct j1939_sk_buff_cb; +struct j1939_addr; + +/* enum j1939_xtp_abort is defined in j1939-priv.h */ + +/* Non-inline functions exported directly */ +const char *j1939_xtp_abort_to_str(enum j1939_xtp_abort abort); +int j1939_xtp_abort_to_errno(struct j1939_priv *priv, + enum j1939_xtp_abort abort); +bool j1939_session_match(struct j1939_addr *se_addr, + struct j1939_addr *sk_addr, bool reverse); +void j1939_skbcb_swap(struct j1939_sk_buff_cb *skcb); + +/* Wrappers for hot-path inline functions */ +bool j1939_cb_is_broadcast_wrapper(const struct j1939_sk_buff_cb *skcb); +pgn_t j1939_xtp_ctl_to_pgn_wrapper(const u8 *dat); +unsigned int j1939_tp_ctl_to_size_wrapper(const u8 *dat); +unsigned int j1939_etp_ctl_to_packet_wrapper(const u8 *dat); +unsigned int j1939_etp_ctl_to_size_wrapper(const u8 *dat); + +#endif /* IS_ENABLED(CONFIG_KUNIT) */ + +#endif /* _J1939_TEST_H_ */ diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index 2191bfe117dc..24b2f7177092 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -18,8 +18,10 @@ #include #include #include +#include =20 #include "j1939-priv.h" +#include "j1939-test.h" =20 /* conversion function between struct sock::sk_priority from linux and * j1939 priority field @@ -51,6 +53,33 @@ static inline bool j1939_pgn_is_clean_pdu(pgn_t pgn) return true; } =20 +#if IS_ENABLED(CONFIG_KUNIT) +/* Wrappers for testing inline functions - no production overhead */ +VISIBLE_IF_KUNIT priority_t j1939_prio_wrapper(u32 sk_priority) +{ + return j1939_prio(sk_priority); +} +EXPORT_SYMBOL_IF_KUNIT(j1939_prio_wrapper); + +VISIBLE_IF_KUNIT u32 j1939_to_sk_priority_wrapper(priority_t prio) +{ + return j1939_to_sk_priority(prio); +} +EXPORT_SYMBOL_IF_KUNIT(j1939_to_sk_priority_wrapper); + +VISIBLE_IF_KUNIT bool j1939_pgn_is_valid_wrapper(pgn_t pgn) +{ + return j1939_pgn_is_valid(pgn); +} +EXPORT_SYMBOL_IF_KUNIT(j1939_pgn_is_valid_wrapper); + +VISIBLE_IF_KUNIT bool j1939_pgn_is_clean_pdu_wrapper(pgn_t pgn) +{ + return j1939_pgn_is_clean_pdu(pgn); +} +EXPORT_SYMBOL_IF_KUNIT(j1939_pgn_is_clean_pdu_wrapper); +#endif + static inline void j1939_sock_pending_add(struct sock *sk) { struct j1939_sock *jsk =3D j1939_sk(sk); @@ -422,7 +451,7 @@ static int j1939_sk_init(struct sock *sk) return 0; } =20 -static int j1939_sk_sanity_check(struct sockaddr_can *addr, int len) +VISIBLE_IF_KUNIT int j1939_sk_sanity_check(struct sockaddr_can *addr, int = len) { if (!addr) return -EDESTADDRREQ; @@ -438,6 +467,7 @@ static int j1939_sk_sanity_check(struct sockaddr_can *a= ddr, int len) =20 return 0; } +EXPORT_SYMBOL_IF_KUNIT(j1939_sk_sanity_check); =20 static int j1939_sk_bind(struct socket *sock, struct sockaddr_unsized *uad= dr, int len) { @@ -587,8 +617,9 @@ static int j1939_sk_connect(struct socket *sock, struct= sockaddr_unsized *uaddr, return ret; } =20 -static void j1939_sk_sock2sockaddr_can(struct sockaddr_can *addr, - const struct j1939_sock *jsk, int peer) +VISIBLE_IF_KUNIT void j1939_sk_sock2sockaddr_can(struct sockaddr_can *addr, + const struct j1939_sock *jsk, + int peer) { /* There are two holes (2 bytes and 3 bytes) to clear to avoid * leaking kernel information to user space. @@ -606,6 +637,7 @@ static void j1939_sk_sock2sockaddr_can(struct sockaddr_= can *addr, addr->can_addr.j1939.addr =3D jsk->addr.sa; } } +EXPORT_SYMBOL_IF_KUNIT(j1939_sk_sock2sockaddr_can); =20 static int j1939_sk_getname(struct socket *sock, struct sockaddr *uaddr, int peer) @@ -937,7 +969,7 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_de= vice *ndev, return NULL; } =20 -static size_t j1939_sk_opt_stats_get_size(enum j1939_sk_errqueue_type type) +VISIBLE_IF_KUNIT size_t j1939_sk_opt_stats_get_size(enum j1939_sk_errqueue= _type type) { switch (type) { case J1939_ERRQUEUE_RX_RTS: @@ -955,6 +987,7 @@ static size_t j1939_sk_opt_stats_get_size(enum j1939_sk= _errqueue_type type) 0; } } +EXPORT_SYMBOL_IF_KUNIT(j1939_sk_opt_stats_get_size); =20 static struct sk_buff * j1939_sk_get_timestamping_opt_stats(struct j1939_session *session, diff --git a/net/can/j1939/tests/.kunitconfig b/net/can/j1939/tests/.kunitc= onfig new file mode 100644 index 000000000000..9af97231cff7 --- /dev/null +++ b/net/can/j1939/tests/.kunitconfig @@ -0,0 +1,5 @@ +CONFIG_KUNIT=3Dy +CONFIG_NET=3Dy +CONFIG_CAN=3Dy +CONFIG_CAN_J1939=3Dy +CONFIG_CAN_J1939_KUNIT_TEST=3Dy diff --git a/net/can/j1939/tests/Makefile b/net/can/j1939/tests/Makefile new file mode 100644 index 000000000000..eb69872b1c11 --- /dev/null +++ b/net/can/j1939/tests/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_CAN_J1939_KUNIT_TEST) +=3D socket_test.o +obj-$(CONFIG_CAN_J1939_KUNIT_TEST) +=3D transport_test.o diff --git a/net/can/j1939/tests/socket_test.c b/net/can/j1939/tests/socket= _test.c new file mode 100644 index 000000000000..2803ee339f28 --- /dev/null +++ b/net/can/j1939/tests/socket_test.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for J1939 socket utility functions + * + * Copyright (c) 2026 Pengutronix, + * Oleksij Rempel + */ + +#include +#include +#include "../j1939-priv.h" +#include "../j1939-test.h" + +/* + * Priority conversion: J1939 prio (0=3Dhigh, 7=3Dlow) <-> sk_priority + * (7=3Dhigh, 0=3Dlow) + */ +static void j1939_test_prio_conversion_roundtrip(struct kunit *test) +{ + int i; + + for (i =3D 0; i <=3D 7; i++) { + u32 sk_prio =3D j1939_to_sk_priority_wrapper(i); + priority_t j1939_p =3D j1939_prio_wrapper(sk_prio); + + KUNIT_EXPECT_EQ(test, j1939_p, i); + } +} + +/* + * Out-of-range sk_priority values must clamp to 0 (highest) to prevent + * 3-bit wraparound. High UNIX priority (>7) =3D high importance =3D J1939= prio 0. + */ +static void j1939_test_prio_clamping(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, j1939_prio_wrapper(8), 0); + KUNIT_EXPECT_EQ(test, j1939_prio_wrapper(100), 0); + KUNIT_EXPECT_EQ(test, j1939_prio_wrapper(UINT_MAX), 0); +} + +/* PGN (Parameter Group Number) validation: 18-bit identifier (0x00000-0x3= FFFF) */ +static void j1939_test_pgn_valid_range(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_valid_wrapper(0)); + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_valid_wrapper(J1939_PGN_MAX)); + KUNIT_EXPECT_FALSE(test, j1939_pgn_is_valid_wrapper(J1939_PGN_MAX + 1)); + KUNIT_EXPECT_FALSE(test, j1939_pgn_is_valid_wrapper(0xFFFFFFFF)); +} + +static void j1939_test_pgn_valid_common(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_valid_wrapper(0xEF00)); + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_valid_wrapper(0xFECA)); /* DM1 */ + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_valid_wrapper(0xFEEC)); + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_valid_wrapper(0xF004)); +} + +/* + * Clean PDU tests: PDU1 (<0xF000) requires DA bits clear; + * PDU2 (>=3D0xF000) always clean. + * "Dirty" PDU1 (non-zero DA) causes addressing ambiguity. + */ +static void j1939_test_clean_pdu_pdu1_format(struct kunit *test) +{ + /* PDU1 with DA bits clear - clean */ + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_clean_pdu_wrapper(0xEF00)); + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_clean_pdu_wrapper(0xEA00)); + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_clean_pdu_wrapper(0x0000)); + + /* PDU1 with DA bits set - dirty */ + KUNIT_EXPECT_FALSE(test, j1939_pgn_is_clean_pdu_wrapper(0xEF01)); + KUNIT_EXPECT_FALSE(test, j1939_pgn_is_clean_pdu_wrapper(0xEF12)); + KUNIT_EXPECT_FALSE(test, j1939_pgn_is_clean_pdu_wrapper(0xEAFF)); + KUNIT_EXPECT_FALSE(test, j1939_pgn_is_clean_pdu_wrapper(0x00FF)); +} + +static void j1939_test_clean_pdu_pdu2_format(struct kunit *test) +{ + /* PDU2 - always clean, lower 8 bits are Group Extension, not DA */ + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_clean_pdu_wrapper(0xF000)); + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_clean_pdu_wrapper(0xF0FF)); + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_clean_pdu_wrapper(0xFECA)); /* DM1 */ + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_clean_pdu_wrapper(0xFEEC)); + KUNIT_EXPECT_TRUE(test, j1939_pgn_is_clean_pdu_wrapper(0xFFFF)); +} + +/* + * Socket address sanity checks catch errors early: NULL, short length, wr= ong + * family, etc. + */ +static void j1939_test_sanity_check_null_addr(struct kunit *test) +{ + int ret; + + ret =3D j1939_sk_sanity_check(NULL, 0); + KUNIT_EXPECT_EQ(test, ret, -EDESTADDRREQ); +} + +static void j1939_test_sanity_check_short_length(struct kunit *test) +{ + struct sockaddr_can addr =3D {}; + int ret; + + ret =3D j1939_sk_sanity_check(&addr, J1939_MIN_NAMELEN - 1); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +static void j1939_test_sanity_check_wrong_family(struct kunit *test) +{ + struct sockaddr_can addr =3D { + .can_family =3D AF_INET, + .can_ifindex =3D 1, + }; + int ret; + + ret =3D j1939_sk_sanity_check(&addr, J1939_MIN_NAMELEN); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +static void j1939_test_sanity_check_no_ifindex(struct kunit *test) +{ + struct sockaddr_can addr =3D { + .can_family =3D AF_CAN, + .can_ifindex =3D 0, + }; + int ret; + + ret =3D j1939_sk_sanity_check(&addr, J1939_MIN_NAMELEN); + KUNIT_EXPECT_EQ(test, ret, -ENODEV); +} + +static void j1939_test_sanity_check_dirty_pdu1_pgn(struct kunit *test) +{ + struct sockaddr_can addr =3D { + .can_family =3D AF_CAN, + .can_ifindex =3D 1, + .can_addr.j1939.pgn =3D 0xEF12, /* PDU1 with DA bits set */ + }; + int ret; + + ret =3D j1939_sk_sanity_check(&addr, J1939_MIN_NAMELEN); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +static void j1939_test_sanity_check_valid(struct kunit *test) +{ + struct sockaddr_can addr; + int ret; + + /* Valid PDU2 with full fields */ + addr =3D (struct sockaddr_can){ + .can_family =3D AF_CAN, + .can_ifindex =3D 1, + .can_addr.j1939.pgn =3D 0xFECA, + .can_addr.j1939.name =3D 0x1234567890ABCDEF, + .can_addr.j1939.addr =3D 0x80, + }; + ret =3D j1939_sk_sanity_check(&addr, J1939_MIN_NAMELEN); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* Valid clean PDU1 */ + addr.can_addr.j1939.pgn =3D 0xEF00; + ret =3D j1939_sk_sanity_check(&addr, J1939_MIN_NAMELEN); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* Valid J1939_NO_PGN wildcard */ + addr.can_addr.j1939.pgn =3D J1939_NO_PGN; + ret =3D j1939_sk_sanity_check(&addr, J1939_MIN_NAMELEN); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +/* Socket-to-sockaddr conversion for getname(): peer=3D0 (local), peer=3D1= (remote) */ +static void j1939_test_sock2sockaddr_local(struct kunit *test) +{ + struct sockaddr_can addr; + struct j1939_sock jsk =3D { + .ifindex =3D 5, + .addr =3D { + .src_name =3D 0x1234567890ABCDEF, + .dst_name =3D 0xFEDCBA0987654321, + .pgn =3D 0xFECA, + .sa =3D 0x25, + .da =3D 0x30, + }, + }; + + memset(&addr, 0xFF, sizeof(addr)); + j1939_sk_sock2sockaddr_can(&addr, &jsk, 0); + + KUNIT_EXPECT_EQ(test, addr.can_family, AF_CAN); + KUNIT_EXPECT_EQ(test, addr.can_ifindex, 5); + KUNIT_EXPECT_EQ(test, addr.can_addr.j1939.name, 0x1234567890ABCDEF); + KUNIT_EXPECT_EQ(test, addr.can_addr.j1939.addr, 0x25); + KUNIT_EXPECT_EQ(test, addr.can_addr.j1939.pgn, 0xFECA); +} + +static void j1939_test_sock2sockaddr_peer(struct kunit *test) +{ + struct sockaddr_can addr; + struct j1939_sock jsk =3D { + .ifindex =3D 5, + .addr =3D { + .src_name =3D 0x1234567890ABCDEF, + .dst_name =3D 0xFEDCBA0987654321, + .pgn =3D 0xFECA, + .sa =3D 0x25, + .da =3D 0x30, + }, + }; + + memset(&addr, 0xFF, sizeof(addr)); + j1939_sk_sock2sockaddr_can(&addr, &jsk, 1); + + KUNIT_EXPECT_EQ(test, addr.can_family, AF_CAN); + KUNIT_EXPECT_EQ(test, addr.can_ifindex, 5); + KUNIT_EXPECT_EQ(test, addr.can_addr.j1939.name, 0xFEDCBA0987654321); + KUNIT_EXPECT_EQ(test, addr.can_addr.j1939.addr, 0x30); + KUNIT_EXPECT_EQ(test, addr.can_addr.j1939.pgn, 0xFECA); +} + +/* Verify padding is zeroed to prevent kernel info leak */ +static void j1939_test_sock2sockaddr_zeroes_padding(struct kunit *test) +{ + struct sockaddr_can addr; + struct j1939_sock jsk =3D { + .ifindex =3D 1, + .addr =3D { + .src_name =3D 0, + .dst_name =3D 0, + .pgn =3D 0, + .sa =3D 0, + .da =3D 0, + }, + }; + + memset(&addr, 0xAA, sizeof(addr)); + j1939_sk_sock2sockaddr_can(&addr, &jsk, 0); + + KUNIT_EXPECT_EQ(test, addr.can_addr.j1939.name, 0ULL); + KUNIT_EXPECT_EQ(test, addr.can_addr.j1939.addr, 0); + KUNIT_EXPECT_EQ(test, addr.can_addr.j1939.pgn, 0U); +} + +/* Error queue buffer size for transport statistics (MSG_ERRQUEUE) */ + +static void j1939_test_errqueue_size_consistency(struct kunit *test) +{ + size_t rx_rts =3D j1939_sk_opt_stats_get_size(J1939_ERRQUEUE_RX_RTS); + size_t tx_ack =3D j1939_sk_opt_stats_get_size(J1939_ERRQUEUE_TX_ACK); + + /* RX_RTS should be larger (more fields) */ + KUNIT_EXPECT_GT(test, rx_rts, tx_ack); + KUNIT_EXPECT_GT(test, tx_ack, 0); + + /* All non-RX_RTS types should have same size (default case) */ + KUNIT_EXPECT_EQ(test, tx_ack, + j1939_sk_opt_stats_get_size(J1939_ERRQUEUE_TX_SCHED)); + KUNIT_EXPECT_EQ(test, tx_ack, + j1939_sk_opt_stats_get_size(J1939_ERRQUEUE_TX_ABORT)); + KUNIT_EXPECT_EQ(test, tx_ack, + j1939_sk_opt_stats_get_size(J1939_ERRQUEUE_RX_DPO)); + KUNIT_EXPECT_EQ(test, tx_ack, + j1939_sk_opt_stats_get_size(J1939_ERRQUEUE_RX_ABORT)); +} + +static struct kunit_case j1939_socket_test_cases[] =3D { + KUNIT_CASE(j1939_test_prio_conversion_roundtrip), + KUNIT_CASE(j1939_test_prio_clamping), + + KUNIT_CASE(j1939_test_pgn_valid_range), + KUNIT_CASE(j1939_test_pgn_valid_common), + + KUNIT_CASE(j1939_test_clean_pdu_pdu1_format), + KUNIT_CASE(j1939_test_clean_pdu_pdu2_format), + + KUNIT_CASE(j1939_test_sanity_check_null_addr), + KUNIT_CASE(j1939_test_sanity_check_short_length), + KUNIT_CASE(j1939_test_sanity_check_wrong_family), + KUNIT_CASE(j1939_test_sanity_check_no_ifindex), + KUNIT_CASE(j1939_test_sanity_check_dirty_pdu1_pgn), + KUNIT_CASE(j1939_test_sanity_check_valid), + + KUNIT_CASE(j1939_test_sock2sockaddr_local), + KUNIT_CASE(j1939_test_sock2sockaddr_peer), + KUNIT_CASE(j1939_test_sock2sockaddr_zeroes_padding), + + KUNIT_CASE(j1939_test_errqueue_size_consistency), + {} +}; + +static struct kunit_suite j1939_socket_test_suite =3D { + .name =3D "j1939-socket", + .test_cases =3D j1939_socket_test_cases, +}; + +kunit_test_suite(j1939_socket_test_suite); + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("KUnit tests for J1939 socket utilities"); +MODULE_AUTHOR("Oleksij Rempel "); diff --git a/net/can/j1939/tests/transport_test.c b/net/can/j1939/tests/tra= nsport_test.c new file mode 100644 index 000000000000..493a3f4b3f40 --- /dev/null +++ b/net/can/j1939/tests/transport_test.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for J1939 transport layer functions + * + * Copyright (c) 2026 Pengutronix, + * Oleksij Rempel + */ + +#include +#include +#include "../j1939-priv.h" +#include "../j1939-test.h" + +/* Abort code to errno conversion for userspace error reporting */ + +struct abort_errno_map { + u8 abort_code; + int expected_errno; +}; + +static void j1939_test_abort_to_errno(struct kunit *test) +{ + static const struct abort_errno_map tests[] =3D { + { J1939_XTP_ABORT_BUSY, EALREADY }, + { J1939_XTP_ABORT_RESOURCE, EMSGSIZE }, + { J1939_XTP_ABORT_TIMEOUT, EHOSTUNREACH }, + { J1939_XTP_ABORT_GENERIC, EBADMSG }, + { J1939_XTP_ABORT_FAULT, ENOTRECOVERABLE }, + { J1939_XTP_ABORT_UNEXPECTED_DATA, ENOTCONN }, + { J1939_XTP_ABORT_BAD_SEQ, EILSEQ }, + { J1939_XTP_ABORT_DUP_SEQ, EPROTO }, + { J1939_XTP_ABORT_EDPO_UNEXPECTED, EPROTO }, + { J1939_XTP_ABORT_BAD_EDPO_PGN, EPROTO }, + { J1939_XTP_ABORT_EDPO_OUTOF_CTS, EPROTO }, + { J1939_XTP_ABORT_BAD_EDPO_OFFSET, EPROTO }, + { J1939_XTP_ABORT_ECTS_UNXPECTED_PGN, EPROTO }, + { J1939_XTP_ABORT_ECTS_TOO_BIG, EPROTO }, + { J1939_XTP_ABORT_OTHER, EPROTO }, + { 99, EPROTO }, /* unknown */ + { 200, EPROTO }, /* unknown */ + }; + struct j1939_priv *priv; + struct net_device *ndev; + int i; + + /* Zero-init safe: netdev_warn only accesses dev->name inline array */ + ndev =3D kunit_kzalloc(test, sizeof(*ndev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ndev); + + priv =3D kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv); + priv->ndev =3D ndev; + + for (i =3D 0; i < ARRAY_SIZE(tests); i++) { + int err =3D j1939_xtp_abort_to_errno(priv, tests[i].abort_code); + + KUNIT_EXPECT_EQ_MSG(test, err, tests[i].expected_errno, + "abort_code=3D0x%02x", tests[i].abort_code); + } +} + +/* Abort code to string conversion for debug logging */ + +static void j1939_test_abort_to_str_all_codes(struct kunit *test) +{ + const char *str; + const char *expected; + + str =3D j1939_xtp_abort_to_str(J1939_XTP_ABORT_BUSY); + expected =3D "Already in one or more connection managed sessions and cann= ot support another."; + KUNIT_EXPECT_STREQ(test, str, expected); + + str =3D j1939_xtp_abort_to_str(J1939_XTP_ABORT_RESOURCE); + expected =3D "System resources were needed for another task so this conne= ction managed session was terminated."; + KUNIT_EXPECT_STREQ(test, str, expected); + + str =3D j1939_xtp_abort_to_str(J1939_XTP_ABORT_TIMEOUT); + expected =3D "A timeout occurred and this is the connection abort to clos= e the session."; + KUNIT_EXPECT_STREQ(test, str, expected); + + str =3D j1939_xtp_abort_to_str(J1939_XTP_ABORT_GENERIC); + expected =3D "CTS messages received when data transfer is in progress"; + KUNIT_EXPECT_STREQ(test, str, expected); + + str =3D j1939_xtp_abort_to_str(J1939_XTP_ABORT_FAULT); + expected =3D "Maximal retransmit request limit reached"; + KUNIT_EXPECT_STREQ(test, str, expected); + + str =3D j1939_xtp_abort_to_str(J1939_XTP_ABORT_UNEXPECTED_DATA); + expected =3D "Unexpected data transfer packet"; + KUNIT_EXPECT_STREQ(test, str, expected); + + str =3D j1939_xtp_abort_to_str(J1939_XTP_ABORT_BAD_SEQ); + expected =3D "Bad sequence number (and software is not able to recover)"; + KUNIT_EXPECT_STREQ(test, str, expected); + + str =3D j1939_xtp_abort_to_str(J1939_XTP_ABORT_DUP_SEQ); + expected =3D "Duplicate sequence number (and software is not able to reco= ver)"; + KUNIT_EXPECT_STREQ(test, str, expected); + + str =3D j1939_xtp_abort_to_str(J1939_XTP_ABORT_OTHER); + expected =3D "Any other reason (if a Connection Abort reason is identifie= d that is not listed in the table use code 250)"; + KUNIT_EXPECT_STREQ(test, str, expected); +} + +static void j1939_test_abort_to_str_unknown(struct kunit *test) +{ + const char *str; + + str =3D j1939_xtp_abort_to_str(99); + KUNIT_EXPECT_STREQ(test, str, ""); + + str =3D j1939_xtp_abort_to_str(255); + KUNIT_EXPECT_STREQ(test, str, ""); +} + +/* Control message field extraction from 8-byte transport protocol message= s */ + +static void j1939_test_tp_ctl_to_size(struct kunit *test) +{ + u8 dat[8]; + + memset(dat, 0, sizeof(dat)); + dat[1] =3D 0x00; + dat[2] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_tp_ctl_to_size_wrapper(dat), 0); + + dat[1] =3D 0x01; + dat[2] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_tp_ctl_to_size_wrapper(dat), 1); + + dat[1] =3D 0xFF; + dat[2] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_tp_ctl_to_size_wrapper(dat), 255); + + dat[1] =3D 0x00; + dat[2] =3D 0x01; + KUNIT_EXPECT_EQ(test, j1939_tp_ctl_to_size_wrapper(dat), 256); + + dat[1] =3D 0xF9; + dat[2] =3D 0x06; + KUNIT_EXPECT_EQ(test, j1939_tp_ctl_to_size_wrapper(dat), 1785); /* max TP= */ + + dat[1] =3D 0xFF; + dat[2] =3D 0xFF; + KUNIT_EXPECT_EQ(test, j1939_tp_ctl_to_size_wrapper(dat), 65535); +} + +static void j1939_test_etp_ctl_to_size(struct kunit *test) +{ + u8 dat[8]; + + memset(dat, 0, sizeof(dat)); + KUNIT_EXPECT_EQ(test, j1939_etp_ctl_to_size_wrapper(dat), 0); + + dat[1] =3D 0xFA; + dat[2] =3D 0x06; + dat[3] =3D 0x00; + dat[4] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_etp_ctl_to_size_wrapper(dat), 1786); /* min E= TP */ + + dat[1] =3D 0x00; + dat[2] =3D 0x00; + dat[3] =3D 0x01; + dat[4] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_etp_ctl_to_size_wrapper(dat), 65536); + + dat[1] =3D 0xF9; + dat[2] =3D 0xFF; + dat[3] =3D 0xFF; + dat[4] =3D 0x06; + KUNIT_EXPECT_EQ(test, j1939_etp_ctl_to_size_wrapper(dat), 117440505); /* = max ETP */ + + dat[1] =3D 0xFF; + dat[2] =3D 0xFF; + dat[3] =3D 0xFF; + dat[4] =3D 0xFF; + KUNIT_EXPECT_EQ(test, j1939_etp_ctl_to_size_wrapper(dat), 0xFFFFFFFF); +} + +static void j1939_test_etp_ctl_to_packet(struct kunit *test) +{ + u8 dat[8]; + + memset(dat, 0, sizeof(dat)); + KUNIT_EXPECT_EQ(test, j1939_etp_ctl_to_packet_wrapper(dat), 0); + + dat[2] =3D 0x01; + dat[3] =3D 0x00; + dat[4] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_etp_ctl_to_packet_wrapper(dat), 1); + + dat[2] =3D 0x00; + dat[3] =3D 0x01; + dat[4] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_etp_ctl_to_packet_wrapper(dat), 256); + + dat[2] =3D 0x00; + dat[3] =3D 0x00; + dat[4] =3D 0x01; + KUNIT_EXPECT_EQ(test, j1939_etp_ctl_to_packet_wrapper(dat), 65536); + + dat[2] =3D 0xFF; + dat[3] =3D 0xFF; + dat[4] =3D 0xFF; + KUNIT_EXPECT_EQ(test, j1939_etp_ctl_to_packet_wrapper(dat), 0xFFFFFF); +} + +/* + * PGN extraction: PDU1 (<0xF000) masks DA bits, PDU2 (>=3D0xF000) preserv= es + * all bits + */ +static void j1939_test_xtp_ctl_to_pgn_pdu1(struct kunit *test) +{ + u8 dat[8]; + + memset(dat, 0, sizeof(dat)); + dat[5] =3D 0x00; + dat[6] =3D 0xEF; + dat[7] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_xtp_ctl_to_pgn_wrapper(dat), 0xEF00); + + dat[5] =3D 0x25; /* DA should be masked */ + dat[6] =3D 0xEF; + dat[7] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_xtp_ctl_to_pgn_wrapper(dat), 0xEF00); + + dat[5] =3D 0xFF; + dat[6] =3D 0xEA; + dat[7] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_xtp_ctl_to_pgn_wrapper(dat), 0xEA00); + + dat[5] =3D 0x00; + dat[6] =3D 0x00; + dat[7] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_xtp_ctl_to_pgn_wrapper(dat), 0x0000); +} + +static void j1939_test_xtp_ctl_to_pgn_pdu2(struct kunit *test) +{ + u8 dat[8]; + + memset(dat, 0, sizeof(dat)); + dat[5] =3D 0x00; + dat[6] =3D 0xF0; + dat[7] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_xtp_ctl_to_pgn_wrapper(dat), 0xF000); + + dat[5] =3D 0xCA; /* GE preserved */ + dat[6] =3D 0xFE; + dat[7] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_xtp_ctl_to_pgn_wrapper(dat), 0xFECA); + + dat[5] =3D 0xEC; + dat[6] =3D 0xFE; + dat[7] =3D 0x00; + KUNIT_EXPECT_EQ(test, j1939_xtp_ctl_to_pgn_wrapper(dat), 0xFEEC); + + dat[5] =3D 0xFF; + dat[6] =3D 0xFF; + dat[7] =3D 0x03; + KUNIT_EXPECT_EQ(test, j1939_xtp_ctl_to_pgn_wrapper(dat), 0x3FFFF); +} + +/* Broadcast detection: dst_name=3D0 and da=3D0xFF */ + +static void j1939_test_cb_is_broadcast_true(struct kunit *test) +{ + struct j1939_sk_buff_cb skcb =3D { + .addr =3D { + .dst_name =3D 0, + .da =3D 0xFF, + }, + }; + + KUNIT_EXPECT_TRUE(test, j1939_cb_is_broadcast_wrapper(&skcb)); +} + +static void j1939_test_cb_is_broadcast_unicast_addr(struct kunit *test) +{ + struct j1939_sk_buff_cb skcb =3D { + .addr =3D { + .dst_name =3D 0, + .da =3D 0x25, + }, + }; + + KUNIT_EXPECT_FALSE(test, j1939_cb_is_broadcast_wrapper(&skcb)); +} + +static void j1939_test_cb_is_broadcast_unicast_name(struct kunit *test) +{ + struct j1939_sk_buff_cb skcb =3D { + .addr =3D { + .dst_name =3D 0x1234567890ABCDEF, + .da =3D 0xFF, + }, + }; + + KUNIT_EXPECT_FALSE(test, j1939_cb_is_broadcast_wrapper(&skcb)); +} + +/* + * SKB control buffer swap for reply messages: swaps src<->dst addresses a= nd + * flags + */ +static void j1939_test_skbcb_swap_addresses(struct kunit *test) +{ + struct j1939_sk_buff_cb skcb =3D { + .addr =3D { + .src_name =3D 0x1111111111111111, + .dst_name =3D 0x2222222222222222, + .sa =3D 0x10, + .da =3D 0x20, + }, + }; + + j1939_skbcb_swap(&skcb); + + KUNIT_EXPECT_EQ(test, skcb.addr.src_name, 0x2222222222222222ULL); + KUNIT_EXPECT_EQ(test, skcb.addr.dst_name, 0x1111111111111111ULL); + KUNIT_EXPECT_EQ(test, skcb.addr.sa, 0x20); + KUNIT_EXPECT_EQ(test, skcb.addr.da, 0x10); +} + +static void j1939_test_skbcb_swap_flags(struct kunit *test) +{ + struct j1939_sk_buff_cb skcb; + + skcb.flags =3D J1939_ECU_LOCAL_SRC; + j1939_skbcb_swap(&skcb); + KUNIT_EXPECT_EQ(test, skcb.flags & J1939_ECU_LOCAL_DST, + J1939_ECU_LOCAL_DST); + KUNIT_EXPECT_EQ(test, skcb.flags & J1939_ECU_LOCAL_SRC, 0); + + skcb.flags =3D J1939_ECU_LOCAL_DST; + j1939_skbcb_swap(&skcb); + KUNIT_EXPECT_EQ(test, skcb.flags & J1939_ECU_LOCAL_SRC, + J1939_ECU_LOCAL_SRC); + KUNIT_EXPECT_EQ(test, skcb.flags & J1939_ECU_LOCAL_DST, 0); + + skcb.flags =3D J1939_ECU_LOCAL_SRC | J1939_ECU_LOCAL_DST; + j1939_skbcb_swap(&skcb); + KUNIT_EXPECT_EQ(test, skcb.flags & (J1939_ECU_LOCAL_SRC | + J1939_ECU_LOCAL_DST), + J1939_ECU_LOCAL_SRC | J1939_ECU_LOCAL_DST); + + skcb.flags =3D 0; + j1939_skbcb_swap(&skcb); + KUNIT_EXPECT_EQ(test, + skcb.flags & (J1939_ECU_LOCAL_SRC | + J1939_ECU_LOCAL_DST), 0); +} + +static void j1939_test_skbcb_swap_preserves_other_flags(struct kunit *test) +{ + struct j1939_sk_buff_cb skcb; + + skcb.flags =3D J1939_ECU_LOCAL_SRC | 0xF0; + j1939_skbcb_swap(&skcb); + + KUNIT_EXPECT_EQ(test, skcb.flags & 0xF0, 0xF0); + KUNIT_EXPECT_EQ(test, skcb.flags & J1939_ECU_LOCAL_DST, + J1939_ECU_LOCAL_DST); + KUNIT_EXPECT_EQ(test, skcb.flags & J1939_ECU_LOCAL_SRC, 0); +} + +/* Session matching for finding active sessions: by type, name, or address= */ + +static void j1939_test_session_match_exact(struct kunit *test) +{ + struct j1939_addr se_addr =3D { + .type =3D J1939_TP, + .src_name =3D 0x1111111111111111, + .dst_name =3D 0x2222222222222222, + .sa =3D 0x10, + .da =3D 0x20, + }; + struct j1939_addr sk_addr =3D { + .type =3D J1939_TP, + .src_name =3D 0x1111111111111111, + .dst_name =3D 0x2222222222222222, + .sa =3D 0x10, + .da =3D 0x20, + }; + + KUNIT_EXPECT_TRUE(test, j1939_session_match(&se_addr, &sk_addr, false)); +} + +static void j1939_test_session_match_reverse(struct kunit *test) +{ + struct j1939_addr se_addr =3D { + .type =3D J1939_TP, + .src_name =3D 0x1111111111111111, + .dst_name =3D 0x2222222222222222, + .sa =3D 0x10, + .da =3D 0x20, + }; + struct j1939_addr sk_addr =3D { + .type =3D J1939_TP, + .src_name =3D 0x2222222222222222, + .dst_name =3D 0x1111111111111111, + .sa =3D 0x20, + .da =3D 0x10, + }; + + KUNIT_EXPECT_TRUE(test, j1939_session_match(&se_addr, &sk_addr, true)); + KUNIT_EXPECT_FALSE(test, + j1939_session_match(&se_addr, &sk_addr, false)); +} + +static void j1939_test_session_match_type_mismatch(struct kunit *test) +{ + struct j1939_addr se_addr =3D { + .type =3D J1939_TP, + .src_name =3D 0x1111111111111111, + .dst_name =3D 0x2222222222222222, + .sa =3D 0x10, + .da =3D 0x20, + }; + struct j1939_addr sk_addr =3D { + .type =3D J1939_ETP, + .src_name =3D 0x1111111111111111, + .dst_name =3D 0x2222222222222222, + .sa =3D 0x10, + .da =3D 0x20, + }; + + KUNIT_EXPECT_FALSE(test, j1939_session_match(&se_addr, &sk_addr, false)); +} + +/* NAME matching takes priority over address (NAME is stable, address can = change) */ +static void j1939_test_session_match_name_priority(struct kunit *test) +{ + struct j1939_addr se_addr =3D { + .type =3D J1939_TP, + .src_name =3D 0x1111111111111111, + .dst_name =3D 0x2222222222222222, + .sa =3D 0x10, + .da =3D 0x20, + }; + struct j1939_addr sk_addr =3D { + .type =3D J1939_TP, + .src_name =3D 0x1111111111111111, + .dst_name =3D 0x2222222222222222, + .sa =3D 0x99, + .da =3D 0x88, + }; + + KUNIT_EXPECT_TRUE(test, j1939_session_match(&se_addr, &sk_addr, false)); +} + +static void j1939_test_session_match_addr_fallback(struct kunit *test) +{ + struct j1939_addr se_addr =3D { + .type =3D J1939_TP, + .src_name =3D 0, + .dst_name =3D 0, + .sa =3D 0x10, + .da =3D 0x20, + }; + struct j1939_addr sk_addr =3D { + .type =3D J1939_TP, + .src_name =3D 0, + .dst_name =3D 0, + .sa =3D 0x10, + .da =3D 0x20, + }; + + KUNIT_EXPECT_TRUE(test, j1939_session_match(&se_addr, &sk_addr, false)); +} + +static void j1939_test_session_match_addr_mismatch(struct kunit *test) +{ + struct j1939_addr se_addr =3D { + .type =3D J1939_TP, + .src_name =3D 0, + .dst_name =3D 0, + .sa =3D 0x10, + .da =3D 0x20, + }; + struct j1939_addr sk_addr =3D { + .type =3D J1939_TP, + .src_name =3D 0, + .dst_name =3D 0, + .sa =3D 0x11, + .da =3D 0x20, + }; + + KUNIT_EXPECT_FALSE(test, j1939_session_match(&se_addr, &sk_addr, false)); +} + +static struct kunit_case j1939_transport_test_cases[] =3D { + KUNIT_CASE(j1939_test_abort_to_errno), + + KUNIT_CASE(j1939_test_abort_to_str_all_codes), + KUNIT_CASE(j1939_test_abort_to_str_unknown), + + KUNIT_CASE(j1939_test_tp_ctl_to_size), + KUNIT_CASE(j1939_test_etp_ctl_to_size), + KUNIT_CASE(j1939_test_etp_ctl_to_packet), + KUNIT_CASE(j1939_test_xtp_ctl_to_pgn_pdu1), + KUNIT_CASE(j1939_test_xtp_ctl_to_pgn_pdu2), + + KUNIT_CASE(j1939_test_cb_is_broadcast_true), + KUNIT_CASE(j1939_test_cb_is_broadcast_unicast_addr), + KUNIT_CASE(j1939_test_cb_is_broadcast_unicast_name), + + KUNIT_CASE(j1939_test_skbcb_swap_addresses), + KUNIT_CASE(j1939_test_skbcb_swap_flags), + KUNIT_CASE(j1939_test_skbcb_swap_preserves_other_flags), + + KUNIT_CASE(j1939_test_session_match_exact), + KUNIT_CASE(j1939_test_session_match_reverse), + KUNIT_CASE(j1939_test_session_match_type_mismatch), + KUNIT_CASE(j1939_test_session_match_name_priority), + KUNIT_CASE(j1939_test_session_match_addr_fallback), + KUNIT_CASE(j1939_test_session_match_addr_mismatch), + + {} +}; + +static struct kunit_suite j1939_transport_test_suite =3D { + .name =3D "j1939-transport", + .test_cases =3D j1939_transport_test_cases, +}; + +kunit_test_suite(j1939_transport_test_suite); + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("KUnit tests for J1939 transport layer"); +MODULE_AUTHOR("Oleksij Rempel "); diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c index 46540da76ca9..8cd95d5fc387 100644 --- a/net/can/j1939/transport.c +++ b/net/can/j1939/transport.c @@ -10,8 +10,10 @@ =20 #include #include +#include =20 #include "j1939-priv.h" +#include "j1939-test.h" =20 #define J1939_XTP_TX_RETRY_LIMIT 100 =20 @@ -37,7 +39,7 @@ static unsigned int j1939_tp_packet_delay; static unsigned int j1939_tp_padding =3D 1; =20 /* helpers */ -static const char *j1939_xtp_abort_to_str(enum j1939_xtp_abort abort) +VISIBLE_IF_KUNIT const char *j1939_xtp_abort_to_str(enum j1939_xtp_abort a= bort) { switch (abort) { case J1939_XTP_ABORT_BUSY: @@ -76,9 +78,10 @@ static const char *j1939_xtp_abort_to_str(enum j1939_xtp= _abort abort) return ""; } } +EXPORT_SYMBOL_IF_KUNIT(j1939_xtp_abort_to_str); =20 -static int j1939_xtp_abort_to_errno(struct j1939_priv *priv, - enum j1939_xtp_abort abort) +VISIBLE_IF_KUNIT int j1939_xtp_abort_to_errno(struct j1939_priv *priv, + enum j1939_xtp_abort abort) { int err; =20 @@ -142,6 +145,7 @@ static int j1939_xtp_abort_to_errno(struct j1939_priv *= priv, =20 return err; } +EXPORT_SYMBOL_IF_KUNIT(j1939_xtp_abort_to_errno); =20 static inline void j1939_session_list_lock(struct j1939_priv *priv) { @@ -375,14 +379,47 @@ static inline unsigned int j1939_etp_ctl_to_size(cons= t u8 *dat) (dat[2] << 8) | (dat[1] << 0); } =20 +#if IS_ENABLED(CONFIG_KUNIT) +/* Wrappers for testing inline functions - no production overhead */ +VISIBLE_IF_KUNIT bool j1939_cb_is_broadcast_wrapper(const struct j1939_sk_= buff_cb *skcb) +{ + return j1939_cb_is_broadcast(skcb); +} +EXPORT_SYMBOL_IF_KUNIT(j1939_cb_is_broadcast_wrapper); + +VISIBLE_IF_KUNIT pgn_t j1939_xtp_ctl_to_pgn_wrapper(const u8 *dat) +{ + return j1939_xtp_ctl_to_pgn(dat); +} +EXPORT_SYMBOL_IF_KUNIT(j1939_xtp_ctl_to_pgn_wrapper); + +VISIBLE_IF_KUNIT unsigned int j1939_tp_ctl_to_size_wrapper(const u8 *dat) +{ + return j1939_tp_ctl_to_size(dat); +} +EXPORT_SYMBOL_IF_KUNIT(j1939_tp_ctl_to_size_wrapper); + +VISIBLE_IF_KUNIT unsigned int j1939_etp_ctl_to_packet_wrapper(const u8 *da= t) +{ + return j1939_etp_ctl_to_packet(dat); +} +EXPORT_SYMBOL_IF_KUNIT(j1939_etp_ctl_to_packet_wrapper); + +VISIBLE_IF_KUNIT unsigned int j1939_etp_ctl_to_size_wrapper(const u8 *dat) +{ + return j1939_etp_ctl_to_size(dat); +} +EXPORT_SYMBOL_IF_KUNIT(j1939_etp_ctl_to_size_wrapper); +#endif + /* find existing session: * reverse: swap cb's src & dst * there is no problem with matching broadcasts, since * broadcasts (no dst, no da) would never call this * with reverse =3D=3D true */ -static bool j1939_session_match(struct j1939_addr *se_addr, - struct j1939_addr *sk_addr, bool reverse) +VISIBLE_IF_KUNIT bool j1939_session_match(struct j1939_addr *se_addr, + struct j1939_addr *sk_addr, bool reverse) { if (se_addr->type !=3D sk_addr->type) return false; @@ -419,6 +456,7 @@ static bool j1939_session_match(struct j1939_addr *se_a= ddr, =20 return true; } +EXPORT_SYMBOL_IF_KUNIT(j1939_session_match); =20 static struct j1939_session *j1939_session_get_by_addr_locked(struct j1939_priv *priv, @@ -478,7 +516,7 @@ j1939_session *j1939_session_get_by_addr(struct j1939_p= riv *priv, return session; } =20 -static void j1939_skbcb_swap(struct j1939_sk_buff_cb *skcb) +VISIBLE_IF_KUNIT void j1939_skbcb_swap(struct j1939_sk_buff_cb *skcb) { u8 tmp =3D 0; =20 @@ -493,6 +531,7 @@ static void j1939_skbcb_swap(struct j1939_sk_buff_cb *s= kcb) skcb->flags &=3D ~(J1939_ECU_LOCAL_SRC | J1939_ECU_LOCAL_DST); skcb->flags |=3D tmp; } +EXPORT_SYMBOL_IF_KUNIT(j1939_skbcb_swap); =20 static struct sk_buff *j1939_tp_tx_dat_new(struct j1939_priv *priv, --=20 2.47.3