diff mbox series

[1/4] selftests: can: Import tst-filter from can-tests

Message ID a4468403cc51ea6c0e8495d7d095befb37aa5aaf.1745323279.git.fmaurer@redhat.com
State New
Headers show
Series [1/4] selftests: can: Import tst-filter from can-tests | expand

Commit Message

Felix Maurer April 22, 2025, 12:02 p.m. UTC
Tests for the can subsytem have been in the can-tests repository[1] so far.
Start moving the tests to kernel selftests by importing the current
tst-filter test. Subsequent patches will update the test to be more aligned
with the kernel selftests and follow the coding style.

The imported test verifies that the single filters on raw CAN sockets work
as expected.

[1]: https://github.com/linux-can/can-tests

Signed-off-by: Felix Maurer <fmaurer@redhat.com>
---

Notes:
    I have removed the long form of the licenses in the beginning of the
    file during the import, as that is covered by the SPDX line anyways. The
    copyright is left as it was originally.

 MAINTAINERS                                   |   2 +
 tools/testing/selftests/Makefile              |   1 +
 tools/testing/selftests/net/can/.gitignore    |   2 +
 tools/testing/selftests/net/can/Makefile      |   9 +
 .../selftests/net/can/test_raw_filter.c       | 204 ++++++++++++++++++
 .../selftests/net/can/test_raw_filter.sh      |  37 ++++
 6 files changed, 255 insertions(+)
 create mode 100644 tools/testing/selftests/net/can/.gitignore
 create mode 100644 tools/testing/selftests/net/can/Makefile
 create mode 100644 tools/testing/selftests/net/can/test_raw_filter.c
 create mode 100755 tools/testing/selftests/net/can/test_raw_filter.sh

Comments

Vincent Mailhol April 24, 2025, 2:23 p.m. UTC | #1
On 24/04/2025 at 23:02, Felix Maurer wrote:
> On 24.04.25 09:42, Vincent Mailhol wrote:
>> On Tue. 22 Apr. 2025 at 21:08, Felix Maurer <fmaurer@redhat.com> wrote:
> [...]
>>> +ALL_TESTS="
>>> +       test_raw_filter
>>> +"
>>> +
>>> +net_dir=$(dirname $0)/..
>>> +source $net_dir/lib.sh
>>> +
>>> +VCANIF="vcan0"
>>
>> Here, you are making the VCANIF variable configuration, but then, in
>> your test_raw_filter.c I see:
>>
>>   #define VCANIF "vcan0"
>>
>> This means that in order to modify the interface, one would have to
>> both modify the .sh script and the .c source. Wouldn't it be possible
>> to centralize this? For example by reading the environment variable in
>> the C file?
>>
>> Or maybe there is a smarter way to pass values in the kernel selftests
>> framework which I am not aware of?
> 
> Good point, I'll try to come up with something to avoid the duplication
> (either from the selftest framework or just for the CAN tests). I'd
> prefer an argument to the program though, as I find this the more usual
> way to pass info if one ever wants to run the test directly.

Passing an argument would be the best. I am not sure how to do this with the
selftests (but I did not investigate either).

>>> +setup()
>>> +{
>>> +       ip link add name $VCANIF type vcan || exit $ksft_skip
>>> +       ip link set dev $VCANIF up
>>> +       pwd
>>> +}

Speaking of which, if you allow the user to modify the interface, then you will
one additional check here to see whether it is a virtual can interface or not
(the ip link commands are not the same for the vcan and the physical can).

Something like:

  CANIF="${CANIF:-vcan}"
  BITRATE="${BITRATE:-500000}"

  setup()
  {
  	if [ $CANIF == vcan* ]; then
  		ip link add name $CANIF type vcan || exit $ksft_skip
  	else
  		ip link set dev $CANIF type can $BITRATE 500000
  	fi
  	ip link set dev $VCANIF up
  	pwd
  }

>>> +cleanup()
>>> +{
>>> +       ip link delete $VCANIF
>>> +}
>>
>> I guess that this setup() and this cleanup() is something that you
>> will also need in the other can tests. Would it make sense to declare
>> these in a common.sh file and just do a
>>
>>   source common.sh
>>
>> here?
> 
> I usually try to avoid making changes in anticipation of the future. I'm
> not sure if all the tests need a similar environment and would prefer to
> split this when we encounter that they do. Are you okay with that?

Yes, this works. Keep this idea in back of your mind and if there is a need to
reuse those in the future, then it will be a good timing to do the factorize the
code.


Yours sincerely,
Vincent Mailhol
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 241ca9e260a2..55749b492ebb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5155,6 +5155,7 @@  F:	include/uapi/linux/can/isotp.h
 F:	include/uapi/linux/can/raw.h
 F:	net/can/
 F:	net/sched/em_canid.c
+F:	tools/testing/selftests/net/can/
 
 CAN-J1939 NETWORK LAYER
 M:	Robin van der Gracht <robin@protonic.nl>
@@ -16577,6 +16578,7 @@  X:	net/ceph/
 X:	net/mac80211/
 X:	net/rfkill/
 X:	net/wireless/
+X:	tools/testing/selftests/net/can/
 
 NETWORKING [IPSEC]
 M:	Steffen Klassert <steffen.klassert@secunet.com>
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 8daac70c2f9d..e5c9ecd52b73 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -64,6 +64,7 @@  TARGETS += mqueue
 TARGETS += nci
 TARGETS += net
 TARGETS += net/af_unix
+TARGETS += net/can
 TARGETS += net/forwarding
 TARGETS += net/hsr
 TARGETS += net/mptcp
diff --git a/tools/testing/selftests/net/can/.gitignore b/tools/testing/selftests/net/can/.gitignore
new file mode 100644
index 000000000000..764a53fc837f
--- /dev/null
+++ b/tools/testing/selftests/net/can/.gitignore
@@ -0,0 +1,2 @@ 
+# SPDX-License-Identifier: GPL-2.0-only
+test_raw_filter
diff --git a/tools/testing/selftests/net/can/Makefile b/tools/testing/selftests/net/can/Makefile
new file mode 100644
index 000000000000..44ef37f064ad
--- /dev/null
+++ b/tools/testing/selftests/net/can/Makefile
@@ -0,0 +1,9 @@ 
+# SPDX-License-Identifier: GPL-2.0
+
+top_srcdir = ../../../../..
+
+TEST_PROGS := test_raw_filter.sh
+
+TEST_GEN_FILES := test_raw_filter
+
+include ../../lib.mk
diff --git a/tools/testing/selftests/net/can/test_raw_filter.c b/tools/testing/selftests/net/can/test_raw_filter.c
new file mode 100644
index 000000000000..c84260f36565
--- /dev/null
+++ b/tools/testing/selftests/net/can/test_raw_filter.c
@@ -0,0 +1,204 @@ 
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+/*
+ * Copyright (c) 2011 Volkswagen Group Electronic Research
+ * All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/if.h>
+
+#include <linux/can.h>
+#include <linux/can/raw.h>
+
+#define ID 0x123
+#define TC 18 /* # of testcases */
+
+const int rx_res[TC] = {4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1};
+const int rxbits_res[TC] = {4369, 4369, 4369, 4369, 17, 4352, 17, 4352, 257, 257, 4112, 4112, 1, 256, 16, 4096, 1, 256};
+
+#define VCANIF "vcan0"
+
+canid_t calc_id(int testcase)
+{
+	canid_t id = ID;
+
+	if (testcase & 1)
+		id |= CAN_EFF_FLAG;
+	if (testcase & 2)
+		id |= CAN_RTR_FLAG;
+
+	return id;
+}
+
+canid_t calc_mask(int testcase)
+{
+	canid_t mask = CAN_SFF_MASK;
+
+	if (testcase > 15)
+		return (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+
+	if (testcase & 4)
+		mask |= CAN_EFF_FLAG;
+	if (testcase & 8)
+		mask |= CAN_RTR_FLAG;
+
+	return mask;
+}
+
+int main(int argc, char **argv)
+{
+	fd_set rdfs;
+	struct timeval tv;
+	int s;
+	struct sockaddr_can addr;
+	struct can_filter rfilter;
+	struct can_frame frame;
+	int testcase;
+	int have_rx;
+	int rx;
+	int rxbits, rxbitval;
+	int ret;
+	int recv_own_msgs = 1;
+	int err = 0;
+	struct ifreq ifr;
+
+	if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
+		perror("socket");
+		err = 1;
+		goto out;
+	}
+
+	strcpy(ifr.ifr_name, VCANIF);
+	if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+		perror("SIOCGIFINDEX");
+		err = 1;
+		goto out_socket;
+	}
+	addr.can_family = AF_CAN;
+	addr.can_ifindex = ifr.ifr_ifindex;
+
+	setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,
+		   &recv_own_msgs, sizeof(recv_own_msgs));
+
+	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		perror("bind");
+		err = 1;
+		goto out_socket;
+	}
+
+	printf("---\n");
+
+	for (testcase = 0; testcase < TC; testcase++) {
+
+		rfilter.can_id   = calc_id(testcase);
+		rfilter.can_mask = calc_mask(testcase);
+		setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER,
+			   &rfilter, sizeof(rfilter));
+
+		printf("testcase %2d filters : can_id = 0x%08X can_mask = 0x%08X\n",
+		       testcase, rfilter.can_id, rfilter.can_mask);
+
+		printf("testcase %2d sending patterns ... ", testcase);
+
+		frame.can_dlc = 1;
+		frame.data[0] = testcase;
+
+		frame.can_id = ID;
+		if (write(s, &frame, sizeof(frame)) < 0) {
+			perror("write");
+			exit(1);
+		}
+		frame.can_id = (ID | CAN_RTR_FLAG);
+		if (write(s, &frame, sizeof(frame)) < 0) {
+			perror("write");
+			exit(1);
+		}
+		frame.can_id = (ID | CAN_EFF_FLAG);
+		if (write(s, &frame, sizeof(frame)) < 0) {
+			perror("write");
+			exit(1);
+		}
+		frame.can_id = (ID | CAN_EFF_FLAG | CAN_RTR_FLAG);
+		if (write(s, &frame, sizeof(frame)) < 0) {
+			perror("write");
+			exit(1);
+		}
+
+		printf("ok\n");
+
+		have_rx = 1;
+		rx = 0;
+		rxbits = 0;
+
+		while (have_rx) {
+
+			have_rx = 0;
+			FD_ZERO(&rdfs);
+			FD_SET(s, &rdfs);
+			tv.tv_sec = 0;
+			tv.tv_usec = 50000; /* 50ms timeout */
+
+			ret = select(s+1, &rdfs, NULL, NULL, &tv);
+			if (ret < 0) {
+				perror("select");
+				exit(1);
+			}
+
+			if (FD_ISSET(s, &rdfs)) {
+				have_rx = 1;
+				ret = read(s, &frame, sizeof(struct can_frame));
+				if (ret < 0) {
+					perror("read");
+					exit(1);
+				}
+				if ((frame.can_id & CAN_SFF_MASK) != ID) {
+					fprintf(stderr, "received wrong can_id!\n");
+					exit(1);
+				}
+				if (frame.data[0] != testcase) {
+					fprintf(stderr, "received wrong testcase!\n");
+					exit(1);
+				}
+
+				/* test & calc rxbits */
+				rxbitval = 1 << ((frame.can_id & (CAN_EFF_FLAG|CAN_RTR_FLAG|CAN_ERR_FLAG)) >> 28);
+
+				/* only receive a rxbitval once */
+				if ((rxbits & rxbitval) == rxbitval) {
+					fprintf(stderr, "received rxbitval %d twice!\n", rxbitval);
+					exit(1);
+				}
+				rxbits |= rxbitval;
+				rx++;
+
+				printf("testcase %2d rx : can_id = 0x%08X rx = %d rxbits = %d\n",
+				       testcase, frame.can_id, rx, rxbits);
+			}
+		}
+		/* rx timed out -> check the received results */
+		if (rx_res[testcase] != rx) {
+			fprintf(stderr, "wrong rx value in testcase %d : %d (expected %d)\n",
+				testcase, rx, rx_res[testcase]);
+			exit(1);
+		}
+		if (rxbits_res[testcase] != rxbits) {
+			fprintf(stderr, "wrong rxbits value in testcase %d : %d (expected %d)\n",
+				testcase, rxbits, rxbits_res[testcase]);
+			exit(1);
+		}
+		printf("testcase %2d ok\n---\n", testcase);
+	}
+
+out_socket:
+	close(s);
+out:
+	return err;
+}
diff --git a/tools/testing/selftests/net/can/test_raw_filter.sh b/tools/testing/selftests/net/can/test_raw_filter.sh
new file mode 100755
index 000000000000..e5f175c8b27b
--- /dev/null
+++ b/tools/testing/selftests/net/can/test_raw_filter.sh
@@ -0,0 +1,37 @@ 
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+#set -x
+
+ALL_TESTS="
+	test_raw_filter
+"
+
+net_dir=$(dirname $0)/..
+source $net_dir/lib.sh
+
+VCANIF="vcan0"
+
+setup()
+{
+	ip link add name $VCANIF type vcan || exit $ksft_skip
+	ip link set dev $VCANIF up
+	pwd
+}
+
+cleanup()
+{
+	ip link delete $VCANIF
+}
+
+test_raw_filter()
+{
+	./test_raw_filter
+}
+
+trap cleanup EXIT
+setup
+
+tests_run
+
+exit $EXIT_STATUS