From: Jakub Kicinski <kuba@kernel.org>
To: davem@davemloft.net
Cc: netdev@vger.kernel.org, edumazet@google.com, pabeni@redhat.com,
andrew+netdev@lunn.ch, horms@kernel.org, donald.hunter@gmail.com,
liuhangbin@gmail.com, matttbe@kernel.org,
Jakub Kicinski <kuba@kernel.org>
Subject: [PATCH net-next v2 02/10] tools: ynl: convert netdev sample to selftest
Date: Fri, 6 Mar 2026 19:36:22 -0800 [thread overview]
Message-ID: <20260307033630.1396085-3-kuba@kernel.org> (raw)
In-Reply-To: <20260307033630.1396085-1-kuba@kernel.org>
Convert netdev.c to produce KTAP output with 3 tests:
- dev_dump: dump all netdev devices, skip if empty
- dev_get: query first device from dump by ifindex
- ntf_check: subscribe to "mgmt", create a veth via rt-link,
verify netdev notification is received, then delete the veth
Remove stdin/scanf-based UI. Add rt-link dependency for the veth
notification test.
TAP version 13
1..3
# Starting 3 tests from 1 test cases.
# RUN netdev.dump ...
# lo[1] xdp-features (0): xdp-rx-metadata-features (0): xsk-fea...
# sit0[2] xdp-features (0): xdp-rx-metadata-features (0): xsk-fea...
# OK netdev.dump
ok 1 netdev.dump
# RUN netdev.get ...
# lo[1] xdp-features (0): xdp-rx-metadata-features (0): xsk-fea...
# OK netdev.get
ok 2 netdev.get
# RUN netdev.ntf_check ...
# veth0[7] xdp-features (0): xdp-rx-metadata-features (7): timesta...
# OK netdev.ntf_check
ok 3 netdev.ntf_check
# PASSED: 3 / 3 tests passed.
# Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0
Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
tools/net/ynl/tests/Makefile | 29 +++--
tools/net/ynl/tests/netdev.c | 229 +++++++++++++++++++++++++----------
2 files changed, 187 insertions(+), 71 deletions(-)
diff --git a/tools/net/ynl/tests/Makefile b/tools/net/ynl/tests/Makefile
index 5fa36c877235..1d77d3662f46 100644
--- a/tools/net/ynl/tests/Makefile
+++ b/tools/net/ynl/tests/Makefile
@@ -18,16 +18,29 @@ TEST_PROGS := \
test_ynl_ethtool.sh \
# end of TEST_PROGS
-SRCS=$(wildcard *.c)
-BINS=$(patsubst %.c,%,${SRCS})
+TEST_GEN_PROGS := \
+ netdev \
+# end of TEST_GEN_PROGS
+BINS := \
+ devlink \
+ ethtool \
+ ovs \
+ rt-addr \
+ rt-link \
+ rt-route \
+ tc \
+ tc-filter-add \
+# end of BINS
+
+CFLAGS_netdev:=$(CFLAGS_netdev) $(CFLAGS_rt-link)
CFLAGS_tc-filter-add:=$(CFLAGS_tc)
include $(wildcard *.d)
INSTALL_PATH ?= $(DESTDIR)/usr/share/kselftest
-all: $(BINS) $(TEST_PROGS)
+all: $(TEST_GEN_PROGS) $(BINS)
../lib/ynl.a:
@$(MAKE) -C ../lib
@@ -35,7 +48,7 @@ all: $(BINS) $(TEST_PROGS)
../generated/protos.a:
@$(MAKE) -C ../generated
-$(BINS): ../lib/ynl.a ../generated/protos.a
+$(TEST_GEN_PROGS) $(BINS): %: %.c ../lib/ynl.a ../generated/protos.a
@echo -e '\tCC test $@'
@$(COMPILE.c) $(CFLAGS_$@) $@.c -o $@.o
@$(LINK.c) $@.o -o $@ $(LDLIBS)
@@ -45,7 +58,7 @@ $(BINS): ../lib/ynl.a ../generated/protos.a
./$$test; \
done
-install: $(TEST_PROGS) $(BINS)
+install: $(TEST_GEN_PROGS) $(BINS)
@mkdir -p $(INSTALL_PATH)/ynl
@cp ../../../testing/selftests/kselftest/ktap_helpers.sh $(INSTALL_PATH)/
@for test in $(TEST_PROGS); do \
@@ -56,10 +69,10 @@ install: $(TEST_PROGS) $(BINS)
$$test > $(INSTALL_PATH)/ynl/$$name; \
chmod +x $(INSTALL_PATH)/ynl/$$name; \
done
- @for bin in $(BINS); do \
+ @for bin in $(TEST_GEN_PROGS) $(BINS); do \
cp $$bin $(INSTALL_PATH)/ynl/$$bin; \
done
- @for test in $(TEST_PROGS); do \
+ @for test in $(TEST_PROGS) $(TEST_GEN_PROGS); do \
echo "ynl:$$test"; \
done > $(INSTALL_PATH)/kselftest-list.txt
@@ -67,7 +80,7 @@ install: $(TEST_PROGS) $(BINS)
rm -f *.o *.d *~
distclean: clean
- rm -f $(BINS)
+ rm -f $(TEST_GEN_PROGS) $(BINS)
.PHONY: all install clean distclean run_tests
.DEFAULT_GOAL=all
diff --git a/tools/net/ynl/tests/netdev.c b/tools/net/ynl/tests/netdev.c
index 22609d44c89a..f849e3d7f4b3 100644
--- a/tools/net/ynl/tests/netdev.c
+++ b/tools/net/ynl/tests/netdev.c
@@ -6,29 +6,29 @@
#include <net/if.h>
+#include <kselftest_harness.h>
+
#include "netdev-user.h"
+#include "rt-link-user.h"
-/* netdev genetlink family code sample
- * This sample shows off basics of the netdev family but also notification
- * handling, hence the somewhat odd UI. We subscribe to notifications first
- * then wait for ifc selection, so the socket may already accumulate
- * notifications as we wait. This allows us to test that YNL can handle
- * requests and notifications getting interleaved.
- */
-
-static void netdev_print_device(struct netdev_dev_get_rsp *d, unsigned int op)
+static void netdev_print_device(struct __test_metadata *_metadata,
+ struct netdev_dev_get_rsp *d, unsigned int op)
{
char ifname[IF_NAMESIZE];
const char *name;
+ EXPECT_TRUE((bool)d->_present.ifindex);
if (!d->_present.ifindex)
return;
name = if_indextoname(d->ifindex, ifname);
+ EXPECT_TRUE((bool)name);
if (name)
- printf("%8s", name);
- printf("[%d]\t", d->ifindex);
+ ksft_print_msg("%8s[%d]\t", name, d->ifindex);
+ else
+ ksft_print_msg("[%d]\t", d->ifindex);
+ EXPECT_TRUE((bool)d->_present.xdp_features);
if (!d->_present.xdp_features)
return;
@@ -38,10 +38,12 @@ static void netdev_print_device(struct netdev_dev_get_rsp *d, unsigned int op)
printf(" %s", netdev_xdp_act_str(1 << i));
}
- printf(" xdp-rx-metadata-features (%llx):", d->xdp_rx_metadata_features);
+ printf(" xdp-rx-metadata-features (%llx):",
+ d->xdp_rx_metadata_features);
for (int i = 0; d->xdp_rx_metadata_features >= 1U << i; i++) {
if (d->xdp_rx_metadata_features & (1U << i))
- printf(" %s", netdev_xdp_rx_metadata_str(1 << i));
+ printf(" %s",
+ netdev_xdp_rx_metadata_str(1 << i));
}
printf(" xsk-features (%llx):", d->xsk_features);
@@ -58,71 +60,172 @@ static void netdev_print_device(struct netdev_dev_get_rsp *d, unsigned int op)
printf("\n");
}
-int main(int argc, char **argv)
+static int veth_create(struct ynl_sock *ys_link)
+{
+ struct rt_link_getlink_ntf *ntf_gl;
+ struct rt_link_newlink_req *req;
+ struct ynl_ntf_base_type *ntf;
+ int ret;
+
+ req = rt_link_newlink_req_alloc();
+ if (!req)
+ return -1;
+
+ rt_link_newlink_req_set_nlflags(req, NLM_F_CREATE | NLM_F_ECHO);
+ rt_link_newlink_req_set_linkinfo_kind(req, "veth");
+
+ ret = rt_link_newlink(ys_link, req);
+ rt_link_newlink_req_free(req);
+ if (ret)
+ return -1;
+
+ if (!ynl_has_ntf(ys_link))
+ return 0;
+
+ ntf = ynl_ntf_dequeue(ys_link);
+ if (!ntf || ntf->cmd != RTM_NEWLINK) {
+ ynl_ntf_free(ntf);
+ return 0;
+ }
+ ntf_gl = (void *)ntf;
+ ret = ntf_gl->obj._hdr.ifi_index;
+ ynl_ntf_free(ntf);
+
+ return ret;
+}
+
+static void veth_delete(struct __test_metadata *_metadata,
+ struct ynl_sock *ys_link, int ifindex)
+{
+ struct rt_link_dellink_req *req;
+
+ req = rt_link_dellink_req_alloc();
+ ASSERT_NE(NULL, req);
+
+ req->_hdr.ifi_index = ifindex;
+ EXPECT_EQ(0, rt_link_dellink(ys_link, req));
+ rt_link_dellink_req_free(req);
+}
+
+FIXTURE(netdev)
+{
+ struct ynl_sock *ys;
+ struct ynl_sock *ys_link;
+};
+
+FIXTURE_SETUP(netdev)
+{
+ struct ynl_error yerr;
+
+ self->ys = ynl_sock_create(&ynl_netdev_family, &yerr);
+ ASSERT_NE(NULL, self->ys) {
+ TH_LOG("Failed to create YNL netdev socket: %s", yerr.msg);
+ }
+}
+
+FIXTURE_TEARDOWN(netdev)
+{
+ if (self->ys_link)
+ ynl_sock_destroy(self->ys_link);
+ ynl_sock_destroy(self->ys);
+}
+
+TEST_F(netdev, dump)
{
struct netdev_dev_get_list *devs;
- struct ynl_ntf_base_type *ntf;
- struct ynl_error yerr;
- struct ynl_sock *ys;
+
+ devs = netdev_dev_get_dump(self->ys);
+ ASSERT_NE(NULL, devs) {
+ TH_LOG("dump failed: %s", self->ys->err.msg);
+ }
+
+ if (ynl_dump_empty(devs)) {
+ netdev_dev_get_list_free(devs);
+ SKIP(return, "no entries in dump");
+ }
+
+ ynl_dump_foreach(devs, d)
+ netdev_print_device(_metadata, d, 0);
+
+ netdev_dev_get_list_free(devs);
+}
+
+TEST_F(netdev, get)
+{
+ struct netdev_dev_get_list *devs;
+ struct netdev_dev_get_req *req;
+ struct netdev_dev_get_rsp *dev;
int ifindex = 0;
- if (argc > 1)
- ifindex = strtol(argv[1], NULL, 0);
-
- ys = ynl_sock_create(&ynl_netdev_family, &yerr);
- if (!ys) {
- fprintf(stderr, "YNL: %s\n", yerr.msg);
- return 1;
+ devs = netdev_dev_get_dump(self->ys);
+ ASSERT_NE(NULL, devs) {
+ TH_LOG("dump failed: %s", self->ys->err.msg);
}
- if (ynl_subscribe(ys, "mgmt"))
- goto err_close;
+ ynl_dump_foreach(devs, d) {
+ if (d->_present.ifindex) {
+ ifindex = d->ifindex;
+ break;
+ }
+ }
+ netdev_dev_get_list_free(devs);
- printf("Select ifc ($ifindex; or 0 = dump; or -2 ntf check): ");
- if (scanf("%d", &ifindex) != 1) {
- fprintf(stderr, "Error: unable to parse input\n");
- goto err_destroy;
+ if (!ifindex)
+ SKIP(return, "no device to query");
+
+ req = netdev_dev_get_req_alloc();
+ ASSERT_NE(NULL, req);
+ netdev_dev_get_req_set_ifindex(req, ifindex);
+
+ dev = netdev_dev_get(self->ys, req);
+ netdev_dev_get_req_free(req);
+ ASSERT_NE(NULL, dev) {
+ TH_LOG("dev_get failed: %s", self->ys->err.msg);
}
- if (ifindex > 0) {
- struct netdev_dev_get_req *req;
- struct netdev_dev_get_rsp *d;
+ netdev_print_device(_metadata, dev, 0);
+ netdev_dev_get_rsp_free(dev);
+}
- req = netdev_dev_get_req_alloc();
- netdev_dev_get_req_set_ifindex(req, ifindex);
+TEST_F(netdev, ntf_check)
+{
+ struct ynl_ntf_base_type *ntf;
+ int veth_ifindex;
+ bool received;
+ int ret;
- d = netdev_dev_get(ys, req);
- netdev_dev_get_req_free(req);
- if (!d)
- goto err_close;
-
- netdev_print_device(d, 0);
- netdev_dev_get_rsp_free(d);
- } else if (!ifindex) {
- devs = netdev_dev_get_dump(ys);
- if (!devs)
- goto err_close;
-
- if (ynl_dump_empty(devs))
- fprintf(stderr, "Error: no devices reported\n");
- ynl_dump_foreach(devs, d)
- netdev_print_device(d, 0);
- netdev_dev_get_list_free(devs);
- } else if (ifindex == -2) {
- ynl_ntf_check(ys);
+ ret = ynl_subscribe(self->ys, "mgmt");
+ ASSERT_EQ(0, ret) {
+ TH_LOG("subscribe failed: %s", self->ys->err.msg);
}
- while ((ntf = ynl_ntf_dequeue(ys))) {
- netdev_print_device((struct netdev_dev_get_rsp *)&ntf->data,
+
+ self->ys_link = ynl_sock_create(&ynl_rt_link_family, NULL);
+ ASSERT_NE(NULL, self->ys_link)
+ TH_LOG("failed to create rt-link socket");
+
+ veth_ifindex = veth_create(self->ys_link);
+ ASSERT_GT(veth_ifindex, 0)
+ TH_LOG("failed to create veth");
+
+ ynl_ntf_check(self->ys);
+
+ ntf = ynl_ntf_dequeue(self->ys);
+ received = ntf;
+ if (ntf) {
+ netdev_print_device(_metadata,
+ (struct netdev_dev_get_rsp *)&ntf->data,
ntf->cmd);
ynl_ntf_free(ntf);
}
- ynl_sock_destroy(ys);
- return 0;
+ /* Drain any remaining notifications */
+ while ((ntf = ynl_ntf_dequeue(self->ys)))
+ ynl_ntf_free(ntf);
-err_close:
- fprintf(stderr, "YNL: %s\n", ys->err.msg);
-err_destroy:
- ynl_sock_destroy(ys);
- return 2;
+ veth_delete(_metadata, self->ys_link, veth_ifindex);
+
+ ASSERT_TRUE(received)
+ TH_LOG("no notification received");
}
+
+TEST_HARNESS_MAIN
--
2.53.0
next prev parent reply other threads:[~2026-03-07 3:36 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-07 3:36 [PATCH net-next v2 00/10] tools: ynl: convert samples into selftests Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 01/10] tools: ynl: move samples to tests Jakub Kicinski
2026-03-07 3:36 ` Jakub Kicinski [this message]
2026-03-07 3:36 ` [PATCH net-next v2 03/10] tools: ynl: convert ovs sample to selftest Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 04/10] tools: ynl: convert rt-link " Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 05/10] tools: ynl: convert tc and tc-filter-add samples " Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 06/10] tools: ynl: add netdevsim wrapper library for YNL tests Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 07/10] tools: ynl: convert devlink sample to selftest Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 08/10] tools: ynl: convert ethtool " Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 09/10] tools: ynl: convert rt-addr " Jakub Kicinski
2026-03-07 3:36 ` [PATCH net-next v2 10/10] tools: ynl: convert rt-route " Jakub Kicinski
2026-03-08 17:23 ` [PATCH net-next v2 00/10] tools: ynl: convert samples into selftests Donald Hunter
2026-03-10 0:10 ` patchwork-bot+netdevbpf
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260307033630.1396085-3-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=donald.hunter@gmail.com \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=liuhangbin@gmail.com \
--cc=matttbe@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.