The "samples" were always poor man's tests (used to manually confirm that C YNL works). Move all C sample programs from tools/net/ynl/samples/ to tools/net/ynl/tests/, "merge" the Makefiles. The subsequent changes will convert each sample into a proper KTAP selftests. Since these are now tests rather than samples - default to enabling asan. After all we're testing user space code here. Sort the gitignore while at it, the page-pool entry was a leftover so delete it. Signed-off-by: Jakub Kicinski --- tools/net/ynl/Makefile | 4 +- tools/net/ynl/samples/Makefile | 36 -------------- tools/net/ynl/tests/Makefile | 47 +++++++++++++++++-- tools/net/ynl/{samples => tests}/devlink.c | 0 tools/net/ynl/{samples => tests}/ethtool.c | 0 tools/net/ynl/{samples => tests}/netdev.c | 0 tools/net/ynl/{samples => tests}/ovs.c | 0 tools/net/ynl/{samples => tests}/rt-addr.c | 0 tools/net/ynl/{samples => tests}/rt-link.c | 0 tools/net/ynl/{samples => tests}/rt-route.c | 0 .../ynl/{samples => tests}/tc-filter-add.c | 0 tools/net/ynl/{samples => tests}/tc.c | 0 tools/net/ynl/{samples => tests}/.gitignore | 3 +- 13 files changed, 45 insertions(+), 45 deletions(-) delete mode 100644 tools/net/ynl/samples/Makefile rename tools/net/ynl/{samples => tests}/devlink.c (100%) rename tools/net/ynl/{samples => tests}/ethtool.c (100%) rename tools/net/ynl/{samples => tests}/netdev.c (100%) rename tools/net/ynl/{samples => tests}/ovs.c (100%) rename tools/net/ynl/{samples => tests}/rt-addr.c (100%) rename tools/net/ynl/{samples => tests}/rt-link.c (100%) rename tools/net/ynl/{samples => tests}/rt-route.c (100%) rename tools/net/ynl/{samples => tests}/tc-filter-add.c (100%) rename tools/net/ynl/{samples => tests}/tc.c (100%) rename tools/net/ynl/{samples => tests}/.gitignore (87%) diff --git a/tools/net/ynl/Makefile b/tools/net/ynl/Makefile index 9b692f368be7..d514a48dae27 100644 --- a/tools/net/ynl/Makefile +++ b/tools/net/ynl/Makefile @@ -14,12 +14,12 @@ includedir ?= $(prefix)/include SPECDIR=../../../Documentation/netlink/specs -SUBDIRS = lib generated samples ynltool tests +SUBDIRS = lib generated ynltool tests all: $(SUBDIRS) libynl.a +tests: | lib generated libynl.a ynltool: | lib generated libynl.a -samples: | lib generated libynl.a: | lib generated @echo -e "\tAR $@" @ar rcs $@ lib/ynl.o generated/*-user.o diff --git a/tools/net/ynl/samples/Makefile b/tools/net/ynl/samples/Makefile deleted file mode 100644 index d76cbd41cbb1..000000000000 --- a/tools/net/ynl/samples/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -include ../Makefile.deps - -CC=gcc -CFLAGS += -std=gnu11 -O2 -W -Wall -Wextra -Wno-unused-parameter -Wshadow \ - -I../lib/ -I../generated/ -idirafter $(UAPI_PATH) -ifeq ("$(DEBUG)","1") - CFLAGS += -g -fsanitize=address -fsanitize=leak -static-libasan -endif - -LDLIBS=../lib/ynl.a ../generated/protos.a - -SRCS=$(wildcard *.c) -BINS=$(patsubst %.c,%,${SRCS}) - -include $(wildcard *.d) - -all: $(BINS) - -CFLAGS_page-pool=$(CFLAGS_netdev) -CFLAGS_tc-filter-add:=$(CFLAGS_tc) - -$(BINS): ../lib/ynl.a ../generated/protos.a $(SRCS) - @echo -e '\tCC sample $@' - @$(COMPILE.c) $(CFLAGS_$@) $@.c -o $@.o - @$(LINK.c) $@.o -o $@ $(LDLIBS) - -clean: - rm -f *.o *.d *~ - -distclean: clean - rm -f $(BINS) - -.PHONY: all clean distclean -.DEFAULT_GOAL=all diff --git a/tools/net/ynl/tests/Makefile b/tools/net/ynl/tests/Makefile index eb166c9550db..5fa36c877235 100644 --- a/tools/net/ynl/tests/Makefile +++ b/tools/net/ynl/tests/Makefile @@ -1,21 +1,51 @@ # SPDX-License-Identifier: GPL-2.0 # Makefile for YNL tests +include ../Makefile.deps + +CC=gcc +CFLAGS += -std=gnu11 -O2 -W -Wall -Wextra -Wno-unused-parameter -Wshadow \ + -I../lib/ -I../generated/ -I../../../testing/selftests/ \ + -idirafter $(UAPI_PATH) +ifneq ("$(NDEBUG)","1") + CFLAGS += -g -fsanitize=address -fsanitize=leak -static-libasan +endif + +LDLIBS=../lib/ynl.a ../generated/protos.a + TEST_PROGS := \ test_ynl_cli.sh \ test_ynl_ethtool.sh \ # end of TEST_PROGS +SRCS=$(wildcard *.c) +BINS=$(patsubst %.c,%,${SRCS}) + +CFLAGS_tc-filter-add:=$(CFLAGS_tc) + +include $(wildcard *.d) + INSTALL_PATH ?= $(DESTDIR)/usr/share/kselftest -all: $(TEST_PROGS) +all: $(BINS) $(TEST_PROGS) + +../lib/ynl.a: + @$(MAKE) -C ../lib + + ../generated/protos.a: + @$(MAKE) -C ../generated + +$(BINS): ../lib/ynl.a ../generated/protos.a + @echo -e '\tCC test $@' + @$(COMPILE.c) $(CFLAGS_$@) $@.c -o $@.o + @$(LINK.c) $@.o -o $@ $(LDLIBS) run_tests: @for test in $(TEST_PROGS); do \ ./$$test; \ done -install: $(TEST_PROGS) +install: $(TEST_PROGS) $(BINS) @mkdir -p $(INSTALL_PATH)/ynl @cp ../../../testing/selftests/kselftest/ktap_helpers.sh $(INSTALL_PATH)/ @for test in $(TEST_PROGS); do \ @@ -26,11 +56,18 @@ install: $(TEST_PROGS) $$test > $(INSTALL_PATH)/ynl/$$name; \ chmod +x $(INSTALL_PATH)/ynl/$$name; \ done + @for bin in $(BINS); do \ + cp $$bin $(INSTALL_PATH)/ynl/$$bin; \ + done @for test in $(TEST_PROGS); do \ echo "ynl:$$test"; \ done > $(INSTALL_PATH)/kselftest-list.txt -clean distclean: - @# Nothing to clean +clean: + rm -f *.o *.d *~ -.PHONY: all install clean run_tests +distclean: clean + rm -f $(BINS) + +.PHONY: all install clean distclean run_tests +.DEFAULT_GOAL=all diff --git a/tools/net/ynl/samples/devlink.c b/tools/net/ynl/tests/devlink.c similarity index 100% rename from tools/net/ynl/samples/devlink.c rename to tools/net/ynl/tests/devlink.c diff --git a/tools/net/ynl/samples/ethtool.c b/tools/net/ynl/tests/ethtool.c similarity index 100% rename from tools/net/ynl/samples/ethtool.c rename to tools/net/ynl/tests/ethtool.c diff --git a/tools/net/ynl/samples/netdev.c b/tools/net/ynl/tests/netdev.c similarity index 100% rename from tools/net/ynl/samples/netdev.c rename to tools/net/ynl/tests/netdev.c diff --git a/tools/net/ynl/samples/ovs.c b/tools/net/ynl/tests/ovs.c similarity index 100% rename from tools/net/ynl/samples/ovs.c rename to tools/net/ynl/tests/ovs.c diff --git a/tools/net/ynl/samples/rt-addr.c b/tools/net/ynl/tests/rt-addr.c similarity index 100% rename from tools/net/ynl/samples/rt-addr.c rename to tools/net/ynl/tests/rt-addr.c diff --git a/tools/net/ynl/samples/rt-link.c b/tools/net/ynl/tests/rt-link.c similarity index 100% rename from tools/net/ynl/samples/rt-link.c rename to tools/net/ynl/tests/rt-link.c diff --git a/tools/net/ynl/samples/rt-route.c b/tools/net/ynl/tests/rt-route.c similarity index 100% rename from tools/net/ynl/samples/rt-route.c rename to tools/net/ynl/tests/rt-route.c diff --git a/tools/net/ynl/samples/tc-filter-add.c b/tools/net/ynl/tests/tc-filter-add.c similarity index 100% rename from tools/net/ynl/samples/tc-filter-add.c rename to tools/net/ynl/tests/tc-filter-add.c diff --git a/tools/net/ynl/samples/tc.c b/tools/net/ynl/tests/tc.c similarity index 100% rename from tools/net/ynl/samples/tc.c rename to tools/net/ynl/tests/tc.c diff --git a/tools/net/ynl/samples/.gitignore b/tools/net/ynl/tests/.gitignore similarity index 87% rename from tools/net/ynl/samples/.gitignore rename to tools/net/ynl/tests/.gitignore index 05087ee323ba..045385df42a4 100644 --- a/tools/net/ynl/samples/.gitignore +++ b/tools/net/ynl/tests/.gitignore @@ -1,8 +1,7 @@ -ethtool devlink +ethtool netdev ovs -page-pool rt-addr rt-link rt-route -- 2.53.0 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 Signed-off-by: Jakub Kicinski --- 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..6413e8166a7c 100644 --- a/tools/net/ynl/tests/netdev.c +++ b/tools/net/ynl/tests/netdev.c @@ -6,29 +6,29 @@ #include +#include + #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(); + if (!req) + return; + + 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(); + 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 Convert ovs.c to produce KTAP output with kselftest_harness. The single "crud" test creates a new OVS datapath, fetches it back by name, then dumps all datapaths verifying the new one appears. IIRC I added this test because ovs is a genetlink family but has a family-specific fixed header. TAP version 13 1..1 # Starting 1 tests from 1 test cases. # RUN ovs.crud ... # get: # ynl-test(3): pid:0 cache:256 # dump: # ynl-test(3): pid:0 cache:256 # OK ovs.crud ok 1 ovs.crud # PASSED: 1 / 1 tests passed. # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Jakub Kicinski --- tools/net/ynl/tests/Makefile | 3 +- tools/net/ynl/tests/ovs.c | 124 ++++++++++++++++++++++++----------- tools/net/ynl/tests/config | 1 + 3 files changed, 87 insertions(+), 41 deletions(-) diff --git a/tools/net/ynl/tests/Makefile b/tools/net/ynl/tests/Makefile index 1d77d3662f46..df9d37c8b2a4 100644 --- a/tools/net/ynl/tests/Makefile +++ b/tools/net/ynl/tests/Makefile @@ -20,12 +20,12 @@ TEST_PROGS := \ TEST_GEN_PROGS := \ netdev \ + ovs \ # end of TEST_GEN_PROGS BINS := \ devlink \ ethtool \ - ovs \ rt-addr \ rt-link \ rt-route \ @@ -34,6 +34,7 @@ BINS := \ # end of BINS CFLAGS_netdev:=$(CFLAGS_netdev) $(CFLAGS_rt-link) +CFLAGS_ovs:=$(CFLAGS_ovs_datapath) CFLAGS_tc-filter-add:=$(CFLAGS_tc) include $(wildcard *.d) diff --git a/tools/net/ynl/tests/ovs.c b/tools/net/ynl/tests/ovs.c index 3e975c003d77..6be002c28eff 100644 --- a/tools/net/ynl/tests/ovs.c +++ b/tools/net/ynl/tests/ovs.c @@ -4,57 +4,101 @@ #include +#include + #include "ovs_datapath-user.h" -int main(int argc, char **argv) +static void ovs_print_datapath(struct __test_metadata *_metadata, + struct ovs_datapath_get_rsp *dp) +{ + EXPECT_TRUE((bool)dp->_len.name); + if (!dp->_len.name) + return; + + EXPECT_TRUE((bool)dp->_hdr.dp_ifindex); + ksft_print_msg("%s(%d): pid:%u cache:%u\n", + dp->name, dp->_hdr.dp_ifindex, + dp->upcall_pid, dp->masks_cache_size); +} + +FIXTURE(ovs) { struct ynl_sock *ys; + char *dp_name; +}; + +FIXTURE_SETUP(ovs) +{ + self->ys = ynl_sock_create(&ynl_ovs_datapath_family, NULL); + ASSERT_NE(NULL, self->ys) + TH_LOG("failed to create OVS datapath socket"); +} + +FIXTURE_TEARDOWN(ovs) +{ + if (self->dp_name) { + struct ovs_datapath_del_req *req; + + req = ovs_datapath_del_req_alloc(); + if (req) { + ovs_datapath_del_req_set_name(req, self->dp_name); + ovs_datapath_del(self->ys, req); + ovs_datapath_del_req_free(req); + } + } + ynl_sock_destroy(self->ys); +} + +TEST_F(ovs, crud) +{ + struct ovs_datapath_get_req_dump *dreq; + struct ovs_datapath_new_req *new_req; + struct ovs_datapath_get_list *dps; + struct ovs_datapath_get_rsp *dp; + struct ovs_datapath_get_req *req; + bool found = false; int err; - ys = ynl_sock_create(&ynl_ovs_datapath_family, NULL); - if (!ys) - return 1; + new_req = ovs_datapath_new_req_alloc(); + ovs_datapath_new_req_set_upcall_pid(new_req, 1); + ovs_datapath_new_req_set_name(new_req, "ynl-test"); - if (argc > 1) { - struct ovs_datapath_new_req *req; + err = ovs_datapath_new(self->ys, new_req); + ovs_datapath_new_req_free(new_req); + ASSERT_EQ(0, err) { + TH_LOG("new failed: %s", self->ys->err.msg); + } + self->dp_name = "ynl-test"; - req = ovs_datapath_new_req_alloc(); - if (!req) - goto err_close; + ksft_print_msg("get:\n"); + req = ovs_datapath_get_req_alloc(); + ovs_datapath_get_req_set_name(req, "ynl-test"); - ovs_datapath_new_req_set_upcall_pid(req, 1); - ovs_datapath_new_req_set_name(req, argv[1]); - - err = ovs_datapath_new(ys, req); - ovs_datapath_new_req_free(req); - if (err) - goto err_close; - } else { - struct ovs_datapath_get_req_dump *req; - struct ovs_datapath_get_list *dps; - - printf("Dump:\n"); - req = ovs_datapath_get_req_dump_alloc(); - - dps = ovs_datapath_get_dump(ys, req); - ovs_datapath_get_req_dump_free(req); - if (!dps) - goto err_close; - - ynl_dump_foreach(dps, dp) { - printf(" %s(%d): pid:%u cache:%u\n", - dp->name, dp->_hdr.dp_ifindex, - dp->upcall_pid, dp->masks_cache_size); - } - ovs_datapath_get_list_free(dps); + dp = ovs_datapath_get(self->ys, req); + ovs_datapath_get_req_free(req); + ASSERT_NE(NULL, dp) { + TH_LOG("get failed: %s", self->ys->err.msg); } - ynl_sock_destroy(ys); + ovs_print_datapath(_metadata, dp); + EXPECT_STREQ("ynl-test", dp->name); + ovs_datapath_get_rsp_free(dp); - return 0; + ksft_print_msg("dump:\n"); + dreq = ovs_datapath_get_req_dump_alloc(); + dps = ovs_datapath_get_dump(self->ys, dreq); + ovs_datapath_get_req_dump_free(dreq); + ASSERT_NE(NULL, dps) { + TH_LOG("dump failed: %s", self->ys->err.msg); + } -err_close: - fprintf(stderr, "YNL (%d): %s\n", ys->err.code, ys->err.msg); - ynl_sock_destroy(ys); - return 2; + ynl_dump_foreach(dps, d) { + ovs_print_datapath(_metadata, d); + if (d->name && !strcmp(d->name, "ynl-test")) + found = true; + } + ovs_datapath_get_list_free(dps); + EXPECT_TRUE(found); } + +TEST_HARNESS_MAIN diff --git a/tools/net/ynl/tests/config b/tools/net/ynl/tests/config index 339f1309c03f..357b34611da4 100644 --- a/tools/net/ynl/tests/config +++ b/tools/net/ynl/tests/config @@ -3,4 +3,5 @@ CONFIG_INET_DIAG=y CONFIG_IPV6=y CONFIG_NET_NS=y CONFIG_NETDEVSIM=m +CONFIG_OPENVSWITCH=m CONFIG_VETH=m -- 2.53.0 Convert rt-link.c to use kselftest_harness.h with FIXTURE/TEST_F. Move rt-link from BINS to TEST_GEN_PROGS. Output: TAP version 13 1..3 # Starting 3 tests from 1 test cases. # RUN rt_link.dump ... # 1: lo: mtu 65536 # 2: sit0: mtu 1480 kind sit # OK rt_link.dump ok 1 rt_link.dump # RUN rt_link.netkit ... # 4: nk1: mtu 1500 kind netkit primary 1 policy blackhole # OK rt_link.netkit ok 2 rt_link.netkit # RUN rt_link.netkit_err_msg ... # OK rt_link.netkit_err_msg ok 3 rt_link.netkit_err_msg # PASSED: 3 / 3 tests passed. # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Jakub Kicinski --- tools/net/ynl/tests/Makefile | 2 +- tools/net/ynl/tests/rt-link.c | 192 +++++++++++++++++++--------------- tools/net/ynl/tests/config | 1 + 3 files changed, 108 insertions(+), 87 deletions(-) diff --git a/tools/net/ynl/tests/Makefile b/tools/net/ynl/tests/Makefile index df9d37c8b2a4..08d1146d91ce 100644 --- a/tools/net/ynl/tests/Makefile +++ b/tools/net/ynl/tests/Makefile @@ -21,13 +21,13 @@ TEST_PROGS := \ TEST_GEN_PROGS := \ netdev \ ovs \ + rt-link \ # end of TEST_GEN_PROGS BINS := \ devlink \ ethtool \ rt-addr \ - rt-link \ rt-route \ tc \ tc-filter-add \ diff --git a/tools/net/ynl/tests/rt-link.c b/tools/net/ynl/tests/rt-link.c index acdd4b4a0f74..a55f7523dbe8 100644 --- a/tools/net/ynl/tests/rt-link.c +++ b/tools/net/ynl/tests/rt-link.c @@ -7,16 +7,21 @@ #include #include +#include + #include "rt-link-user.h" -static void rt_link_print(struct rt_link_getlink_rsp *r) +static void rt_link_print(struct __test_metadata *_metadata, + struct rt_link_getlink_rsp *r) { unsigned int i; - printf("%3d: ", r->_hdr.ifi_index); + EXPECT_TRUE((bool)r->_hdr.ifi_index); + ksft_print_msg("%3d: ", r->_hdr.ifi_index); + EXPECT_TRUE((bool)r->_len.ifname); if (r->_len.ifname) - printf("%16s: ", r->ifname); + printf("%6s: ", r->ifname); if (r->_present.mtu) printf("mtu %5d ", r->mtu); @@ -50,7 +55,7 @@ static void rt_link_print(struct rt_link_getlink_rsp *r) printf("\n"); } -static int rt_link_create_netkit(struct ynl_sock *ys) +static int netkit_create(struct ynl_sock *ys) { struct rt_link_getlink_ntf *ntf_gl; struct rt_link_newlink_req *req; @@ -58,49 +63,24 @@ static int rt_link_create_netkit(struct ynl_sock *ys) int ret; req = rt_link_newlink_req_alloc(); - if (!req) { - fprintf(stderr, "Can't alloc req\n"); + if (!req) return -1; - } - /* rtnetlink doesn't provide info about the created object. - * It expects us to set the ECHO flag and the dig the info out - * of the notifications... - */ rt_link_newlink_req_set_nlflags(req, NLM_F_CREATE | NLM_F_ECHO); - rt_link_newlink_req_set_linkinfo_kind(req, "netkit"); - - /* Test error messages */ - rt_link_newlink_req_set_linkinfo_data_netkit_policy(req, 10); - ret = rt_link_newlink(ys, req); - if (ret) { - printf("Testing error message for policy being bad:\n\t%s\n", ys->err.msg); - } else { - fprintf(stderr, "Warning: unexpected success creating netkit with bad attrs\n"); - goto created; - } - rt_link_newlink_req_set_linkinfo_data_netkit_policy(req, NETKIT_DROP); ret = rt_link_newlink(ys, req); -created: rt_link_newlink_req_free(req); - if (ret) { - fprintf(stderr, "YNL: %s\n", ys->err.msg); + if (ret) return -1; - } - if (!ynl_has_ntf(ys)) { - fprintf(stderr, - "Warning: interface created but received no notification, won't delete the interface\n"); + if (!ynl_has_ntf(ys)) return 0; - } ntf = ynl_ntf_dequeue(ys); - if (ntf->cmd != RTM_NEWLINK) { - fprintf(stderr, - "Warning: unexpected notification type, won't delete the interface\n"); + if (!ntf || ntf->cmd != RTM_NEWLINK) { + ynl_ntf_free(ntf); return 0; } ntf_gl = (void *)ntf; @@ -110,75 +90,115 @@ static int rt_link_create_netkit(struct ynl_sock *ys) return ret; } -static void rt_link_del(struct ynl_sock *ys, int ifindex) +static void netkit_delete(struct __test_metadata *_metadata, + struct ynl_sock *ys, int ifindex) { struct rt_link_dellink_req *req; req = rt_link_dellink_req_alloc(); - if (!req) { - fprintf(stderr, "Can't alloc req\n"); + if (!req) return; - } req->_hdr.ifi_index = ifindex; - if (rt_link_dellink(ys, req)) - fprintf(stderr, "YNL: %s\n", ys->err.msg); - else - fprintf(stderr, - "Trying to delete a Netkit interface (ifindex %d)\n", - ifindex); - + EXPECT_EQ(0, rt_link_dellink(ys, req)); rt_link_dellink_req_free(req); } -int main(int argc, char **argv) +FIXTURE(rt_link) +{ + struct ynl_sock *ys; +}; + +FIXTURE_SETUP(rt_link) +{ + struct ynl_error yerr; + + self->ys = ynl_sock_create(&ynl_rt_link_family, &yerr); + ASSERT_NE(NULL, self->ys) { + TH_LOG("failed to create rt-link socket: %s", yerr.msg); + } +} + +FIXTURE_TEARDOWN(rt_link) +{ + ynl_sock_destroy(self->ys); +} + +TEST_F(rt_link, dump) { struct rt_link_getlink_req_dump *req; struct rt_link_getlink_list *rsp; - struct ynl_error yerr; - struct ynl_sock *ys; - int created = 0; - - ys = ynl_sock_create(&ynl_rt_link_family, &yerr); - if (!ys) { - fprintf(stderr, "YNL: %s\n", yerr.msg); - return 1; - } - - if (argc > 1) { - fprintf(stderr, "Trying to create a Netkit interface\n"); - created = rt_link_create_netkit(ys); - if (created < 0) - goto err_destroy; - } req = rt_link_getlink_req_dump_alloc(); - if (!req) - goto err_del_ifc; - - rsp = rt_link_getlink_dump(ys, req); + rsp = rt_link_getlink_dump(self->ys, req); rt_link_getlink_req_dump_free(req); - if (!rsp) - goto err_close; + ASSERT_NE(NULL, rsp) { + TH_LOG("dump failed: %s", self->ys->err.msg); + } + ASSERT_FALSE(ynl_dump_empty(rsp)); - if (ynl_dump_empty(rsp)) - fprintf(stderr, "Error: no links reported\n"); ynl_dump_foreach(rsp, link) - rt_link_print(link); + rt_link_print(_metadata, link); + rt_link_getlink_list_free(rsp); - - if (created) - rt_link_del(ys, created); - - ynl_sock_destroy(ys); - return 0; - -err_close: - fprintf(stderr, "YNL: %s\n", ys->err.msg); -err_del_ifc: - if (created) - rt_link_del(ys, created); -err_destroy: - ynl_sock_destroy(ys); - return 2; } + +TEST_F(rt_link, netkit) +{ + struct rt_link_getlink_req_dump *dreq; + struct rt_link_getlink_list *rsp; + bool found = false; + int netkit_ifindex; + + /* Create netkit with valid policy */ + netkit_ifindex = netkit_create(self->ys); + ASSERT_GT(netkit_ifindex, 0) + TH_LOG("failed to create netkit: %s", self->ys->err.msg); + + /* Verify it appears in a dump */ + dreq = rt_link_getlink_req_dump_alloc(); + rsp = rt_link_getlink_dump(self->ys, dreq); + rt_link_getlink_req_dump_free(dreq); + ASSERT_NE(NULL, rsp) { + TH_LOG("dump failed: %s", self->ys->err.msg); + } + + ynl_dump_foreach(rsp, link) { + if (link->_hdr.ifi_index == netkit_ifindex) { + rt_link_print(_metadata, link); + found = true; + } + } + rt_link_getlink_list_free(rsp); + EXPECT_TRUE(found); + + netkit_delete(_metadata, self->ys, netkit_ifindex); +} + +TEST_F(rt_link, netkit_err_msg) +{ + struct rt_link_newlink_req *req; + int ret; + + /* Test creating netkit with bad policy - should fail */ + req = rt_link_newlink_req_alloc(); + rt_link_newlink_req_set_nlflags(req, NLM_F_CREATE); + rt_link_newlink_req_set_linkinfo_kind(req, "netkit"); + rt_link_newlink_req_set_linkinfo_data_netkit_policy(req, 10); + + ret = rt_link_newlink(self->ys, req); + rt_link_newlink_req_free(req); + EXPECT_NE(0, ret) { + TH_LOG("creating netkit with bad policy should fail"); + } + + /* Expect: + * Kernel error: 'Provided default xmit policy not supported' (bad attribute: .linkinfo.data(netkit).policy) + */ + EXPECT_NE(NULL, strstr(self->ys->err.msg, "bad attribute: .linkinfo.data(netkit).policy")) { + TH_LOG("expected extack msg not found: %s", + self->ys->err.msg); + } +} + +TEST_HARNESS_MAIN diff --git a/tools/net/ynl/tests/config b/tools/net/ynl/tests/config index 357b34611da4..b4c58d86a6c2 100644 --- a/tools/net/ynl/tests/config +++ b/tools/net/ynl/tests/config @@ -3,5 +3,6 @@ CONFIG_INET_DIAG=y CONFIG_IPV6=y CONFIG_NET_NS=y CONFIG_NETDEVSIM=m +CONFIG_NETKIT=y CONFIG_OPENVSWITCH=m CONFIG_VETH=m -- 2.53.0 Convert tc.c and tc-filter-add.c to produce KTAP output with kselftest_harness. Merge the two tests together. They both test TC one is testing qdisc and the other classifiers but they can easily live in a single selftest. Make the test spawn a new netns, and run the operations on lo to avoid onerous setup and cleanup. TAP version 13 1..2 # Starting 2 tests from 1 test cases. # RUN tc.qdisc ... # lo: fq_codel limit: 10240p target: 5ms new_flow_cnt: 0 # OK tc.qdisc ok 1 tc.qdisc # RUN tc.flower ... # flower pref 1 proto: 0x8100 # flower: # vlan_id: 100 # vlan_prio: 5 # num_of_vlans: 3 # action order: 1 vlan push id 200 protocol 0x8100 priority 0 # action order: 2 vlan push id 300 protocol 0x8100 priority 0 # OK tc.flower ok 2 tc.flower # PASSED: 2 / 2 tests passed. # Totals: pass:2 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Jakub Kicinski --- tools/net/ynl/tests/Makefile | 4 +- tools/net/ynl/tests/tc-filter-add.c | 335 ----------------------- tools/net/ynl/tests/tc.c | 403 +++++++++++++++++++++++++--- tools/net/ynl/tests/config | 6 + 4 files changed, 372 insertions(+), 376 deletions(-) delete mode 100644 tools/net/ynl/tests/tc-filter-add.c diff --git a/tools/net/ynl/tests/Makefile b/tools/net/ynl/tests/Makefile index 08d1146d91ce..524092a8de7e 100644 --- a/tools/net/ynl/tests/Makefile +++ b/tools/net/ynl/tests/Makefile @@ -22,6 +22,7 @@ TEST_GEN_PROGS := \ netdev \ ovs \ rt-link \ + tc \ # end of TEST_GEN_PROGS BINS := \ @@ -29,13 +30,10 @@ BINS := \ ethtool \ rt-addr \ rt-route \ - tc \ - tc-filter-add \ # end of BINS CFLAGS_netdev:=$(CFLAGS_netdev) $(CFLAGS_rt-link) CFLAGS_ovs:=$(CFLAGS_ovs_datapath) -CFLAGS_tc-filter-add:=$(CFLAGS_tc) include $(wildcard *.d) diff --git a/tools/net/ynl/tests/tc-filter-add.c b/tools/net/ynl/tests/tc-filter-add.c deleted file mode 100644 index 97871e9e9edc..000000000000 --- a/tools/net/ynl/tests/tc-filter-add.c +++ /dev/null @@ -1,335 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "tc-user.h" - -#define TC_HANDLE (0xFFFF << 16) - -const char *vlan_act_name(struct tc_vlan *p) -{ - switch (p->v_action) { - case TCA_VLAN_ACT_POP: - return "pop"; - case TCA_VLAN_ACT_PUSH: - return "push"; - case TCA_VLAN_ACT_MODIFY: - return "modify"; - default: - break; - } - - return "not supported"; -} - -const char *gact_act_name(struct tc_gact *p) -{ - switch (p->action) { - case TC_ACT_SHOT: - return "drop"; - case TC_ACT_OK: - return "ok"; - case TC_ACT_PIPE: - return "pipe"; - default: - break; - } - - return "not supported"; -} - -static void print_vlan(struct tc_act_vlan_attrs *vlan) -{ - printf("%s ", vlan_act_name(vlan->parms)); - if (vlan->_present.push_vlan_id) - printf("id %u ", vlan->push_vlan_id); - if (vlan->_present.push_vlan_protocol) - printf("protocol %#x ", ntohs(vlan->push_vlan_protocol)); - if (vlan->_present.push_vlan_priority) - printf("priority %u ", vlan->push_vlan_priority); -} - -static void print_gact(struct tc_act_gact_attrs *gact) -{ - struct tc_gact *p = gact->parms; - - printf("%s ", gact_act_name(p)); -} - -static void flower_print(struct tc_flower_attrs *flower, const char *kind) -{ - struct tc_act_attrs *a; - unsigned int i; - - printf("%s:\n", kind); - - if (flower->_present.key_vlan_id) - printf(" vlan_id: %u\n", flower->key_vlan_id); - if (flower->_present.key_vlan_prio) - printf(" vlan_prio: %u\n", flower->key_vlan_prio); - if (flower->_present.key_num_of_vlans) - printf(" num_of_vlans: %u\n", flower->key_num_of_vlans); - - for (i = 0; i < flower->_count.act; i++) { - a = &flower->act[i]; - printf("action order: %i %s ", i + 1, a->kind); - if (a->options._present.vlan) - print_vlan(&a->options.vlan); - else if (a->options._present.gact) - print_gact(&a->options.gact); - printf("\n"); - } - printf("\n"); -} - -static void tc_filter_print(struct tc_gettfilter_rsp *f) -{ - struct tc_options_msg *opt = &f->options; - - if (opt->_present.flower) - flower_print(&opt->flower, f->kind); - else if (f->_len.kind) - printf("%s pref %u proto: %#x\n", f->kind, - (f->_hdr.tcm_info >> 16), - ntohs(TC_H_MIN(f->_hdr.tcm_info))); -} - -static int tc_filter_add(struct ynl_sock *ys, int ifi) -{ - struct tc_newtfilter_req *req; - struct tc_act_attrs *acts; - struct tc_vlan p = { - .action = TC_ACT_PIPE, - .v_action = TCA_VLAN_ACT_PUSH - }; - __u16 flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE; - int ret; - - req = tc_newtfilter_req_alloc(); - if (!req) { - fprintf(stderr, "tc_newtfilter_req_alloc failed\n"); - return -1; - } - memset(req, 0, sizeof(*req)); - - acts = tc_act_attrs_alloc(3); - if (!acts) { - fprintf(stderr, "tc_act_attrs_alloc\n"); - tc_newtfilter_req_free(req); - return -1; - } - memset(acts, 0, sizeof(*acts) * 3); - - req->_hdr.tcm_ifindex = ifi; - req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); - req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q)); - req->chain = 0; - - tc_newtfilter_req_set_nlflags(req, flags); - tc_newtfilter_req_set_kind(req, "flower"); - tc_newtfilter_req_set_options_flower_key_vlan_id(req, 100); - tc_newtfilter_req_set_options_flower_key_vlan_prio(req, 5); - tc_newtfilter_req_set_options_flower_key_num_of_vlans(req, 3); - - __tc_newtfilter_req_set_options_flower_act(req, acts, 3); - - /* Skip action at index 0 because in TC, the action array - * index starts at 1, with each index defining the action's - * order. In contrast, in YNL indexed arrays start at index 0. - */ - tc_act_attrs_set_kind(&acts[1], "vlan"); - tc_act_attrs_set_options_vlan_parms(&acts[1], &p, sizeof(p)); - tc_act_attrs_set_options_vlan_push_vlan_id(&acts[1], 200); - tc_act_attrs_set_kind(&acts[2], "vlan"); - tc_act_attrs_set_options_vlan_parms(&acts[2], &p, sizeof(p)); - tc_act_attrs_set_options_vlan_push_vlan_id(&acts[2], 300); - - tc_newtfilter_req_set_options_flower_flags(req, 0); - tc_newtfilter_req_set_options_flower_key_eth_type(req, htons(0x8100)); - - ret = tc_newtfilter(ys, req); - if (ret) - fprintf(stderr, "tc_newtfilter: %s\n", ys->err.msg); - - tc_newtfilter_req_free(req); - - return ret; -} - -static int tc_filter_show(struct ynl_sock *ys, int ifi) -{ - struct tc_gettfilter_req_dump *req; - struct tc_gettfilter_list *rsp; - - req = tc_gettfilter_req_dump_alloc(); - if (!req) { - fprintf(stderr, "tc_gettfilter_req_dump_alloc failed\n"); - return -1; - } - memset(req, 0, sizeof(*req)); - - req->_hdr.tcm_ifindex = ifi; - req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); - req->_present.chain = 1; - req->chain = 0; - - rsp = tc_gettfilter_dump(ys, req); - tc_gettfilter_req_dump_free(req); - if (!rsp) { - fprintf(stderr, "YNL: %s\n", ys->err.msg); - return -1; - } - - if (ynl_dump_empty(rsp)) - fprintf(stderr, "Error: no filters reported\n"); - else - ynl_dump_foreach(rsp, flt) tc_filter_print(flt); - - tc_gettfilter_list_free(rsp); - - return 0; -} - -static int tc_filter_del(struct ynl_sock *ys, int ifi) -{ - struct tc_deltfilter_req *req; - __u16 flags = NLM_F_REQUEST; - int ret; - - req = tc_deltfilter_req_alloc(); - if (!req) { - fprintf(stderr, "tc_deltfilter_req_alloc failed\n"); - return -1; - } - memset(req, 0, sizeof(*req)); - - req->_hdr.tcm_ifindex = ifi; - req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); - req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q)); - tc_deltfilter_req_set_nlflags(req, flags); - - ret = tc_deltfilter(ys, req); - if (ret) - fprintf(stderr, "tc_deltfilter failed: %s\n", ys->err.msg); - - tc_deltfilter_req_free(req); - - return ret; -} - -static int tc_clsact_add(struct ynl_sock *ys, int ifi) -{ - struct tc_newqdisc_req *req; - __u16 flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE; - int ret; - - req = tc_newqdisc_req_alloc(); - if (!req) { - fprintf(stderr, "tc_newqdisc_req_alloc failed\n"); - return -1; - } - memset(req, 0, sizeof(*req)); - - req->_hdr.tcm_ifindex = ifi; - req->_hdr.tcm_parent = TC_H_CLSACT; - req->_hdr.tcm_handle = TC_HANDLE; - tc_newqdisc_req_set_nlflags(req, flags); - tc_newqdisc_req_set_kind(req, "clsact"); - - ret = tc_newqdisc(ys, req); - if (ret) - fprintf(stderr, "tc_newqdisc failed: %s\n", ys->err.msg); - - tc_newqdisc_req_free(req); - - return ret; -} - -static int tc_clsact_del(struct ynl_sock *ys, int ifi) -{ - struct tc_delqdisc_req *req; - __u16 flags = NLM_F_REQUEST; - int ret; - - req = tc_delqdisc_req_alloc(); - if (!req) { - fprintf(stderr, "tc_delqdisc_req_alloc failed\n"); - return -1; - } - memset(req, 0, sizeof(*req)); - - req->_hdr.tcm_ifindex = ifi; - req->_hdr.tcm_parent = TC_H_CLSACT; - req->_hdr.tcm_handle = TC_HANDLE; - tc_delqdisc_req_set_nlflags(req, flags); - - ret = tc_delqdisc(ys, req); - if (ret) - fprintf(stderr, "tc_delqdisc failed: %s\n", ys->err.msg); - - tc_delqdisc_req_free(req); - - return ret; -} - -static int tc_filter_config(struct ynl_sock *ys, int ifi) -{ - int ret = 0; - - if (tc_filter_add(ys, ifi)) - return -1; - - ret = tc_filter_show(ys, ifi); - - if (tc_filter_del(ys, ifi)) - return -1; - - return ret; -} - -int main(int argc, char **argv) -{ - struct ynl_error yerr; - struct ynl_sock *ys; - int ifi, ret = 0; - - if (argc < 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } - ifi = if_nametoindex(argv[1]); - if (!ifi) { - perror("if_nametoindex"); - return 1; - } - - ys = ynl_sock_create(&ynl_tc_family, &yerr); - if (!ys) { - fprintf(stderr, "YNL: %s\n", yerr.msg); - return 1; - } - - if (tc_clsact_add(ys, ifi)) { - ret = 2; - goto err_destroy; - } - - if (tc_filter_config(ys, ifi)) - ret = 3; - - if (tc_clsact_del(ys, ifi)) - ret = 4; - -err_destroy: - ynl_sock_destroy(ys); - return ret; -} diff --git a/tools/net/ynl/tests/tc.c b/tools/net/ynl/tests/tc.c index 0bfff0fdd792..77e6fb69c61e 100644 --- a/tools/net/ynl/tests/tc.c +++ b/tools/net/ynl/tests/tc.c @@ -1,21 +1,34 @@ // SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include #include #include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include #include "tc-user.h" -static void tc_qdisc_print(struct tc_getqdisc_rsp *q) +#define TC_HANDLE (0xFFFF << 16) + +static bool tc_qdisc_print(struct __test_metadata *_metadata, + struct tc_getqdisc_rsp *q) { + bool was_fq_codel = false; char ifname[IF_NAMESIZE]; const char *name; name = if_indextoname(q->_hdr.tcm_ifindex, ifname); - if (name) - printf("%16s: ", name); + EXPECT_TRUE((bool)name); + ksft_print_msg("%16s: ", name ?: "no-name"); if (q->_len.kind) { printf("%s ", q->kind); @@ -27,6 +40,11 @@ static void tc_qdisc_print(struct tc_getqdisc_rsp *q) fq_codel = &q->options.fq_codel; stats = q->stats2.app.fq_codel; + EXPECT_EQ(true, + fq_codel->_present.limit && + fq_codel->_present.target && + q->stats2.app._len.fq_codel); + if (fq_codel->_present.limit) printf("limit: %dp ", fq_codel->limit); if (fq_codel->_present.target) @@ -35,46 +53,355 @@ static void tc_qdisc_print(struct tc_getqdisc_rsp *q) if (q->stats2.app._len.fq_codel) printf("new_flow_cnt: %d ", stats->qdisc_stats.new_flow_count); + was_fq_codel = true; } } - printf("\n"); + + return was_fq_codel; } -int main(int argc, char **argv) +static const char *vlan_act_name(struct tc_vlan *p) { - struct tc_getqdisc_req_dump *req; - struct tc_getqdisc_list *rsp; - struct ynl_error yerr; - struct ynl_sock *ys; - - ys = ynl_sock_create(&ynl_tc_family, &yerr); - if (!ys) { - fprintf(stderr, "YNL: %s\n", yerr.msg); - return 1; + switch (p->v_action) { + case TCA_VLAN_ACT_POP: + return "pop"; + case TCA_VLAN_ACT_PUSH: + return "push"; + case TCA_VLAN_ACT_MODIFY: + return "modify"; + default: + break; } - req = tc_getqdisc_req_dump_alloc(); - if (!req) - goto err_destroy; - - rsp = tc_getqdisc_dump(ys, req); - tc_getqdisc_req_dump_free(req); - if (!rsp) - goto err_close; - - if (ynl_dump_empty(rsp)) - fprintf(stderr, "Error: no addresses reported\n"); - ynl_dump_foreach(rsp, qdisc) - tc_qdisc_print(qdisc); - tc_getqdisc_list_free(rsp); - - ynl_sock_destroy(ys); - return 0; - -err_close: - fprintf(stderr, "YNL: %s\n", ys->err.msg); -err_destroy: - ynl_sock_destroy(ys); - return 2; + return "not supported"; } + +static const char *gact_act_name(struct tc_gact *p) +{ + switch (p->action) { + case TC_ACT_SHOT: + return "drop"; + case TC_ACT_OK: + return "ok"; + case TC_ACT_PIPE: + return "pipe"; + default: + break; + } + + return "not supported"; +} + +static void print_vlan(struct tc_act_vlan_attrs *vlan) +{ + printf("%s ", vlan_act_name(vlan->parms)); + if (vlan->_present.push_vlan_id) + printf("id %u ", vlan->push_vlan_id); + if (vlan->_present.push_vlan_protocol) + printf("protocol %#x ", ntohs(vlan->push_vlan_protocol)); + if (vlan->_present.push_vlan_priority) + printf("priority %u ", vlan->push_vlan_priority); +} + +static void print_gact(struct tc_act_gact_attrs *gact) +{ + struct tc_gact *p = gact->parms; + + printf("%s ", gact_act_name(p)); +} + +static void flower_print(struct tc_flower_attrs *flower, const char *kind) +{ + struct tc_act_attrs *a; + unsigned int i; + + ksft_print_msg("%s:\n", kind); + + if (flower->_present.key_vlan_id) + ksft_print_msg(" vlan_id: %u\n", flower->key_vlan_id); + if (flower->_present.key_vlan_prio) + ksft_print_msg(" vlan_prio: %u\n", flower->key_vlan_prio); + if (flower->_present.key_num_of_vlans) + ksft_print_msg(" num_of_vlans: %u\n", + flower->key_num_of_vlans); + + for (i = 0; i < flower->_count.act; i++) { + a = &flower->act[i]; + ksft_print_msg("action order: %i %s ", i + 1, a->kind); + if (a->options._present.vlan) + print_vlan(&a->options.vlan); + else if (a->options._present.gact) + print_gact(&a->options.gact); + printf("\n"); + } +} + +static void tc_filter_print(struct __test_metadata *_metadata, + struct tc_gettfilter_rsp *f) +{ + struct tc_options_msg *opt = &f->options; + + if (opt->_present.flower) { + EXPECT_TRUE((bool)f->_len.kind); + flower_print(&opt->flower, f->kind); + } else if (f->_len.kind) { + ksft_print_msg("%s pref %u proto: %#x\n", f->kind, + (f->_hdr.tcm_info >> 16), + ntohs(TC_H_MIN(f->_hdr.tcm_info))); + } +} + +static int tc_clsact_add(struct ynl_sock *ys, int ifi) +{ + struct tc_newqdisc_req *req; + int ret; + + req = tc_newqdisc_req_alloc(); + if (!req) + return -1; + memset(req, 0, sizeof(*req)); + + req->_hdr.tcm_ifindex = ifi; + req->_hdr.tcm_parent = TC_H_CLSACT; + req->_hdr.tcm_handle = TC_HANDLE; + tc_newqdisc_req_set_nlflags(req, + NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE); + tc_newqdisc_req_set_kind(req, "clsact"); + + ret = tc_newqdisc(ys, req); + tc_newqdisc_req_free(req); + + return ret; +} + +static int tc_clsact_del(struct ynl_sock *ys, int ifi) +{ + struct tc_delqdisc_req *req; + int ret; + + req = tc_delqdisc_req_alloc(); + if (!req) + return -1; + memset(req, 0, sizeof(*req)); + + req->_hdr.tcm_ifindex = ifi; + req->_hdr.tcm_parent = TC_H_CLSACT; + req->_hdr.tcm_handle = TC_HANDLE; + tc_delqdisc_req_set_nlflags(req, NLM_F_REQUEST); + + ret = tc_delqdisc(ys, req); + tc_delqdisc_req_free(req); + + return ret; +} + +static int tc_filter_add(struct ynl_sock *ys, int ifi) +{ + struct tc_newtfilter_req *req; + struct tc_act_attrs *acts; + struct tc_vlan p = { + .action = TC_ACT_PIPE, + .v_action = TCA_VLAN_ACT_PUSH + }; + int ret; + + req = tc_newtfilter_req_alloc(); + if (!req) + return -1; + memset(req, 0, sizeof(*req)); + + acts = tc_act_attrs_alloc(3); + if (!acts) { + tc_newtfilter_req_free(req); + return -1; + } + memset(acts, 0, sizeof(*acts) * 3); + + req->_hdr.tcm_ifindex = ifi; + req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); + req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q)); + req->chain = 0; + + tc_newtfilter_req_set_nlflags(req, NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE); + tc_newtfilter_req_set_kind(req, "flower"); + tc_newtfilter_req_set_options_flower_key_vlan_id(req, 100); + tc_newtfilter_req_set_options_flower_key_vlan_prio(req, 5); + tc_newtfilter_req_set_options_flower_key_num_of_vlans(req, 3); + + __tc_newtfilter_req_set_options_flower_act(req, acts, 3); + + /* Skip action at index 0 because in TC, the action array + * index starts at 1, with each index defining the action's + * order. In contrast, in YNL indexed arrays start at index 0. + */ + tc_act_attrs_set_kind(&acts[1], "vlan"); + tc_act_attrs_set_options_vlan_parms(&acts[1], &p, sizeof(p)); + tc_act_attrs_set_options_vlan_push_vlan_id(&acts[1], 200); + tc_act_attrs_set_kind(&acts[2], "vlan"); + tc_act_attrs_set_options_vlan_parms(&acts[2], &p, sizeof(p)); + tc_act_attrs_set_options_vlan_push_vlan_id(&acts[2], 300); + + tc_newtfilter_req_set_options_flower_flags(req, 0); + tc_newtfilter_req_set_options_flower_key_eth_type(req, htons(0x8100)); + + ret = tc_newtfilter(ys, req); + tc_newtfilter_req_free(req); + + return ret; +} + +static int tc_filter_del(struct ynl_sock *ys, int ifi) +{ + struct tc_deltfilter_req *req; + int ret; + + req = tc_deltfilter_req_alloc(); + if (!req) + return -1; + memset(req, 0, sizeof(*req)); + + req->_hdr.tcm_ifindex = ifi; + req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); + req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q)); + tc_deltfilter_req_set_nlflags(req, NLM_F_REQUEST); + + ret = tc_deltfilter(ys, req); + tc_deltfilter_req_free(req); + + return ret; +} + +FIXTURE(tc) +{ + struct ynl_sock *ys; + int ifindex; +}; + +FIXTURE_SETUP(tc) +{ + struct ynl_error yerr; + int ret; + + ret = unshare(CLONE_NEWNET); + ASSERT_EQ(0, ret); + + self->ifindex = 1; /* loopback */ + + self->ys = ynl_sock_create(&ynl_tc_family, &yerr); + ASSERT_NE(NULL, self->ys) { + TH_LOG("failed to create tc socket: %s", yerr.msg); + } +} + +FIXTURE_TEARDOWN(tc) +{ + ynl_sock_destroy(self->ys); +} + +TEST_F(tc, qdisc) +{ + struct tc_getqdisc_req_dump *dreq; + struct tc_newqdisc_req *add_req; + struct tc_delqdisc_req *del_req; + struct tc_getqdisc_list *rsp; + bool found = false; + int ret; + + add_req = tc_newqdisc_req_alloc(); + ASSERT_NE(NULL, add_req); + memset(add_req, 0, sizeof(*add_req)); + + add_req->_hdr.tcm_ifindex = self->ifindex; + add_req->_hdr.tcm_parent = TC_H_ROOT; + tc_newqdisc_req_set_nlflags(add_req, + NLM_F_REQUEST | NLM_F_CREATE); + tc_newqdisc_req_set_kind(add_req, "fq_codel"); + + ret = tc_newqdisc(self->ys, add_req); + tc_newqdisc_req_free(add_req); + ASSERT_EQ(0, ret) { + TH_LOG("qdisc add failed: %s", self->ys->err.msg); + } + + dreq = tc_getqdisc_req_dump_alloc(); + rsp = tc_getqdisc_dump(self->ys, dreq); + tc_getqdisc_req_dump_free(dreq); + ASSERT_NE(NULL, rsp) { + TH_LOG("dump failed: %s", self->ys->err.msg); + } + ASSERT_FALSE(ynl_dump_empty(rsp)); + + ynl_dump_foreach(rsp, qdisc) { + found |= tc_qdisc_print(_metadata, qdisc); + } + tc_getqdisc_list_free(rsp); + EXPECT_TRUE(found); + + del_req = tc_delqdisc_req_alloc(); + ASSERT_NE(NULL, del_req); + memset(del_req, 0, sizeof(*del_req)); + + del_req->_hdr.tcm_ifindex = self->ifindex; + del_req->_hdr.tcm_parent = TC_H_ROOT; + tc_delqdisc_req_set_nlflags(del_req, NLM_F_REQUEST); + + ret = tc_delqdisc(self->ys, del_req); + tc_delqdisc_req_free(del_req); + EXPECT_EQ(0, ret) { + TH_LOG("qdisc del failed: %s", self->ys->err.msg); + } +} + +TEST_F(tc, flower) +{ + struct tc_gettfilter_req_dump *dreq; + struct tc_gettfilter_list *rsp; + bool found = false; + int ret; + + ret = tc_clsact_add(self->ys, self->ifindex); + if (ret) + SKIP(return, "clsact not supported: %s", self->ys->err.msg); + + ret = tc_filter_add(self->ys, self->ifindex); + ASSERT_EQ(0, ret) { + TH_LOG("filter add failed: %s", self->ys->err.msg); + } + + dreq = tc_gettfilter_req_dump_alloc(); + memset(dreq, 0, sizeof(*dreq)); + dreq->_hdr.tcm_ifindex = self->ifindex; + dreq->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); + dreq->_present.chain = 1; + dreq->chain = 0; + + rsp = tc_gettfilter_dump(self->ys, dreq); + tc_gettfilter_req_dump_free(dreq); + ASSERT_NE(NULL, rsp) { + TH_LOG("filter dump failed: %s", self->ys->err.msg); + } + + ynl_dump_foreach(rsp, flt) { + tc_filter_print(_metadata, flt); + if (flt->options._present.flower) { + EXPECT_EQ(100, flt->options.flower.key_vlan_id); + EXPECT_EQ(5, flt->options.flower.key_vlan_prio); + found = true; + } + } + tc_gettfilter_list_free(rsp); + EXPECT_TRUE(found); + + ret = tc_filter_del(self->ys, self->ifindex); + EXPECT_EQ(0, ret) { + TH_LOG("filter del failed: %s", self->ys->err.msg); + } + + ret = tc_clsact_del(self->ys, self->ifindex); + EXPECT_EQ(0, ret) { + TH_LOG("clsact del failed: %s", self->ys->err.msg); + } +} + +TEST_HARNESS_MAIN diff --git a/tools/net/ynl/tests/config b/tools/net/ynl/tests/config index b4c58d86a6c2..75c0fe72391f 100644 --- a/tools/net/ynl/tests/config +++ b/tools/net/ynl/tests/config @@ -1,7 +1,13 @@ CONFIG_DUMMY=m CONFIG_INET_DIAG=y CONFIG_IPV6=y +CONFIG_NET_ACT_VLAN=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_CLS_FLOWER=m +CONFIG_NET_SCH_FQ_CODEL=m +CONFIG_NET_SCH_INGRESS=m CONFIG_NET_NS=y +CONFIG_NET_SCHED=y CONFIG_NETDEVSIM=m CONFIG_NETKIT=y CONFIG_OPENVSWITCH=m -- 2.53.0 Some tests need netdevsim setup which is painful to do from C. Add ynl_nsim_lib.sh, a shared library providing nsim_setup and nsim_cleanup functions for tests that need a netdevsim device. Signed-off-by: Jakub Kicinski --- tools/net/ynl/tests/Makefile | 5 +++++ tools/net/ynl/tests/ynl_nsim_lib.sh | 35 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tools/net/ynl/tests/ynl_nsim_lib.sh diff --git a/tools/net/ynl/tests/Makefile b/tools/net/ynl/tests/Makefile index 524092a8de7e..a329de031add 100644 --- a/tools/net/ynl/tests/Makefile +++ b/tools/net/ynl/tests/Makefile @@ -32,6 +32,8 @@ BINS := \ rt-route \ # end of BINS +TEST_FILES := ynl_nsim_lib.sh + CFLAGS_netdev:=$(CFLAGS_netdev) $(CFLAGS_rt-link) CFLAGS_ovs:=$(CFLAGS_ovs_datapath) @@ -68,6 +70,9 @@ install: $(TEST_GEN_PROGS) $(BINS) $$test > $(INSTALL_PATH)/ynl/$$name; \ chmod +x $(INSTALL_PATH)/ynl/$$name; \ done + @for file in $(TEST_FILES); do \ + cp $$file $(INSTALL_PATH)/ynl/$$file; \ + done @for bin in $(TEST_GEN_PROGS) $(BINS); do \ cp $$bin $(INSTALL_PATH)/ynl/$$bin; \ done diff --git a/tools/net/ynl/tests/ynl_nsim_lib.sh b/tools/net/ynl/tests/ynl_nsim_lib.sh new file mode 100644 index 000000000000..98cdce44a69c --- /dev/null +++ b/tools/net/ynl/tests/ynl_nsim_lib.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Shared netdevsim setup/cleanup for YNL C test wrappers + +NSIM_ID="1337" +NSIM_DEV="" +KSFT_SKIP=4 + +nsim_cleanup() { + echo "$NSIM_ID" > /sys/bus/netdevsim/del_device 2>/dev/null || true +} + +nsim_setup() { + modprobe netdevsim 2>/dev/null + if ! [ -f /sys/bus/netdevsim/new_device ]; then + echo "netdevsim module not available, skipping" >&2 + exit "$KSFT_SKIP" + fi + + trap nsim_cleanup EXIT + + echo "$NSIM_ID 1" > /sys/bus/netdevsim/new_device + udevadm settle + + NSIM_DEV=$(ls /sys/bus/netdevsim/devices/netdevsim${NSIM_ID}/net 2>/dev/null | head -1) + if [ -z "$NSIM_DEV" ]; then + echo "failed to find netdevsim device" >&2 + exit 1 + fi + + ip link set dev "$NSIM_DEV" name nsim0 + ip link set dev nsim0 up + ip addr add 192.168.1.1/24 dev nsim0 + ip addr add 2001:db8::1/64 dev nsim0 nodad +} -- 2.53.0 Convert devlink.c to use kselftest_harness.h with FIXTURE/TEST_F. Move devlink from BINS to TEST_GEN_FILES in the Makefile since it's invoked via the devlink.sh wrapper which sets up netdevsim. Output: TAP version 13 1..2 # Starting 2 tests from 1 test cases. # RUN devlink.dump ... # netdevsim/netdevsim1337 # OK devlink.dump ok 1 devlink.dump # RUN devlink.info ... # netdevsim/netdevsim1337: # driver: netdevsim # running fw: # fw.mgmt: 10.20.30 # OK devlink.info ok 2 devlink.info # PASSED: 2 / 2 tests passed. # Totals: pass:2 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Jakub Kicinski --- tools/net/ynl/tests/Makefile | 16 +++--- tools/net/ynl/tests/devlink.c | 99 +++++++++++++++++++++++----------- tools/net/ynl/tests/devlink.sh | 5 ++ 3 files changed, 84 insertions(+), 36 deletions(-) create mode 100755 tools/net/ynl/tests/devlink.sh diff --git a/tools/net/ynl/tests/Makefile b/tools/net/ynl/tests/Makefile index a329de031add..14d399a70f10 100644 --- a/tools/net/ynl/tests/Makefile +++ b/tools/net/ynl/tests/Makefile @@ -14,6 +14,7 @@ endif LDLIBS=../lib/ynl.a ../generated/protos.a TEST_PROGS := \ + devlink.sh \ test_ynl_cli.sh \ test_ynl_ethtool.sh \ # end of TEST_PROGS @@ -25,8 +26,11 @@ TEST_GEN_PROGS := \ tc \ # end of TEST_GEN_PROGS -BINS := \ +TEST_GEN_FILES := \ devlink \ +# end of TEST_GEN_FILES + +BINS := \ ethtool \ rt-addr \ rt-route \ @@ -41,7 +45,7 @@ include $(wildcard *.d) INSTALL_PATH ?= $(DESTDIR)/usr/share/kselftest -all: $(TEST_GEN_PROGS) $(BINS) +all: $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS) ../lib/ynl.a: @$(MAKE) -C ../lib @@ -49,7 +53,7 @@ all: $(TEST_GEN_PROGS) $(BINS) ../generated/protos.a: @$(MAKE) -C ../generated -$(TEST_GEN_PROGS) $(BINS): %: %.c ../lib/ynl.a ../generated/protos.a +$(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS): %: %.c ../lib/ynl.a ../generated/protos.a @echo -e '\tCC test $@' @$(COMPILE.c) $(CFLAGS_$@) $@.c -o $@.o @$(LINK.c) $@.o -o $@ $(LDLIBS) @@ -59,7 +63,7 @@ $(TEST_GEN_PROGS) $(BINS): %: %.c ../lib/ynl.a ../generated/protos.a ./$$test; \ done -install: $(TEST_GEN_PROGS) $(BINS) +install: $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS) @mkdir -p $(INSTALL_PATH)/ynl @cp ../../../testing/selftests/kselftest/ktap_helpers.sh $(INSTALL_PATH)/ @for test in $(TEST_PROGS); do \ @@ -73,7 +77,7 @@ install: $(TEST_GEN_PROGS) $(BINS) @for file in $(TEST_FILES); do \ cp $$file $(INSTALL_PATH)/ynl/$$file; \ done - @for bin in $(TEST_GEN_PROGS) $(BINS); do \ + @for bin in $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS); do \ cp $$bin $(INSTALL_PATH)/ynl/$$bin; \ done @for test in $(TEST_PROGS) $(TEST_GEN_PROGS); do \ @@ -84,7 +88,7 @@ install: $(TEST_GEN_PROGS) $(BINS) rm -f *.o *.d *~ distclean: clean - rm -f $(TEST_GEN_PROGS) $(BINS) + rm -f $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS) .PHONY: all install clean distclean run_tests .DEFAULT_GOAL=all diff --git a/tools/net/ynl/tests/devlink.c b/tools/net/ynl/tests/devlink.c index ac9dfb01f280..c666261f0f24 100644 --- a/tools/net/ynl/tests/devlink.c +++ b/tools/net/ynl/tests/devlink.c @@ -4,58 +4,97 @@ #include +#include + #include "devlink-user.h" -int main(int argc, char **argv) +FIXTURE(devlink) +{ + struct ynl_sock *ys; +}; + +FIXTURE_SETUP(devlink) +{ + self->ys = ynl_sock_create(&ynl_devlink_family, NULL); + ASSERT_NE(NULL, self->ys) + TH_LOG("failed to create devlink socket"); +} + +FIXTURE_TEARDOWN(devlink) +{ + ynl_sock_destroy(self->ys); +} + +TEST_F(devlink, dump) { struct devlink_get_list *devs; - struct ynl_sock *ys; - ys = ynl_sock_create(&ynl_devlink_family, NULL); - if (!ys) - return 1; + devs = devlink_get_dump(self->ys); + ASSERT_NE(NULL, devs) { + TH_LOG("dump failed: %s", self->ys->err.msg); + } - devs = devlink_get_dump(ys); - if (!devs) - goto err_close; + if (ynl_dump_empty(devs)) { + devlink_get_list_free(devs); + SKIP(return, "no entries in dump"); + } + + ynl_dump_foreach(devs, d) { + EXPECT_TRUE((bool)d->_len.bus_name); + EXPECT_TRUE((bool)d->_len.dev_name); + ksft_print_msg("%s/%s\n", d->bus_name, d->dev_name); + } + + devlink_get_list_free(devs); +} + +TEST_F(devlink, info) +{ + struct devlink_get_list *devs; + + devs = devlink_get_dump(self->ys); + ASSERT_NE(NULL, devs) { + TH_LOG("dump failed: %s", self->ys->err.msg); + } + + if (ynl_dump_empty(devs)) { + devlink_get_list_free(devs); + SKIP(return, "no devices to query"); + } ynl_dump_foreach(devs, d) { struct devlink_info_get_req *info_req; struct devlink_info_get_rsp *info_rsp; - unsigned i; + unsigned int i; - printf("%s/%s:\n", d->bus_name, d->dev_name); + EXPECT_TRUE((bool)d->_len.bus_name); + EXPECT_TRUE((bool)d->_len.dev_name); + ksft_print_msg("%s/%s:\n", d->bus_name, d->dev_name); info_req = devlink_info_get_req_alloc(); devlink_info_get_req_set_bus_name(info_req, d->bus_name); devlink_info_get_req_set_dev_name(info_req, d->dev_name); - info_rsp = devlink_info_get(ys, info_req); + info_rsp = devlink_info_get(self->ys, info_req); devlink_info_get_req_free(info_req); - if (!info_rsp) - goto err_free_devs; + ASSERT_NE(NULL, info_rsp) { + devlink_get_list_free(devs); + TH_LOG("info_get failed: %s", self->ys->err.msg); + } + EXPECT_TRUE((bool)info_rsp->_len.info_driver_name); if (info_rsp->_len.info_driver_name) - printf(" driver: %s\n", info_rsp->info_driver_name); + ksft_print_msg(" driver: %s\n", + info_rsp->info_driver_name); if (info_rsp->_count.info_version_running) - printf(" running fw:\n"); + ksft_print_msg(" running fw:\n"); for (i = 0; i < info_rsp->_count.info_version_running; i++) - printf(" %s: %s\n", - info_rsp->info_version_running[i].info_version_name, - info_rsp->info_version_running[i].info_version_value); - printf(" ...\n"); + ksft_print_msg(" %s: %s\n", + info_rsp->info_version_running[i].info_version_name, + info_rsp->info_version_running[i].info_version_value); devlink_info_get_rsp_free(info_rsp); } devlink_get_list_free(devs); - - ynl_sock_destroy(ys); - - return 0; - -err_free_devs: - devlink_get_list_free(devs); -err_close: - fprintf(stderr, "YNL: %s\n", ys->err.msg); - ynl_sock_destroy(ys); - return 2; } + +TEST_HARNESS_MAIN diff --git a/tools/net/ynl/tests/devlink.sh b/tools/net/ynl/tests/devlink.sh new file mode 100755 index 000000000000..55da00c985f4 --- /dev/null +++ b/tools/net/ynl/tests/devlink.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +source "$(dirname "$(realpath "$0")")/ynl_nsim_lib.sh" +nsim_setup +exec "$(dirname "$(realpath "$0")")/devlink" -- 2.53.0 Convert ethtool.c to use kselftest_harness.h with FIXTURE/TEST_F. Move ethtool from BINS to TEST_GEN_FILES and add ethtool.sh wrapper which sets up a netdevsim device before running the test binary. Output: TAP version 13 1..2 # Starting 2 tests from 1 test cases. # RUN ethtool.channels ... # nsim0: combined 1 # OK ethtool.channels ok 1 ethtool.channels # RUN ethtool.rings ... # nsim0: rx 512 tx 512 # OK ethtool.rings ok 2 ethtool.rings # PASSED: 2 / 2 tests passed. # Totals: pass:2 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Jakub Kicinski --- tools/net/ynl/tests/Makefile | 3 +- tools/net/ynl/tests/ethtool.c | 83 ++++++++++++++++++++++------------ tools/net/ynl/tests/ethtool.sh | 5 ++ 3 files changed, 62 insertions(+), 29 deletions(-) create mode 100755 tools/net/ynl/tests/ethtool.sh diff --git a/tools/net/ynl/tests/Makefile b/tools/net/ynl/tests/Makefile index 14d399a70f10..c380e9f331a3 100644 --- a/tools/net/ynl/tests/Makefile +++ b/tools/net/ynl/tests/Makefile @@ -15,6 +15,7 @@ LDLIBS=../lib/ynl.a ../generated/protos.a TEST_PROGS := \ devlink.sh \ + ethtool.sh \ test_ynl_cli.sh \ test_ynl_ethtool.sh \ # end of TEST_PROGS @@ -28,10 +29,10 @@ TEST_GEN_PROGS := \ TEST_GEN_FILES := \ devlink \ + ethtool \ # end of TEST_GEN_FILES BINS := \ - ethtool \ rt-addr \ rt-route \ # end of BINS diff --git a/tools/net/ynl/tests/ethtool.c b/tools/net/ynl/tests/ethtool.c index a7ebbd1b98db..926a75d23c9b 100644 --- a/tools/net/ynl/tests/ethtool.c +++ b/tools/net/ynl/tests/ethtool.c @@ -6,28 +6,49 @@ #include +#include + #include "ethtool-user.h" -int main(int argc, char **argv) +FIXTURE(ethtool) +{ + struct ynl_sock *ys; +}; + +FIXTURE_SETUP(ethtool) +{ + self->ys = ynl_sock_create(&ynl_ethtool_family, NULL); + ASSERT_NE(NULL, self->ys) + TH_LOG("failed to create ethtool socket"); +} + +FIXTURE_TEARDOWN(ethtool) +{ + ynl_sock_destroy(self->ys); +} + +TEST_F(ethtool, channels) { struct ethtool_channels_get_req_dump creq = {}; - struct ethtool_rings_get_req_dump rreq = {}; struct ethtool_channels_get_list *channels; - struct ethtool_rings_get_list *rings; - struct ynl_sock *ys; - ys = ynl_sock_create(&ynl_ethtool_family, NULL); - if (!ys) - return 1; + creq._present.header = 1; /* ethtool needs an empty nest */ + channels = ethtool_channels_get_dump(self->ys, &creq); + ASSERT_NE(NULL, channels) { + TH_LOG("channels dump failed: %s", self->ys->err.msg); + } - creq._present.header = 1; /* ethtool needs an empty nest, sigh */ - channels = ethtool_channels_get_dump(ys, &creq); - if (!channels) - goto err_close; + if (ynl_dump_empty(channels)) { + ethtool_channels_get_list_free(channels); + SKIP(return, "no entries in channels dump"); + } - printf("Channels:\n"); ynl_dump_foreach(channels, dev) { - printf(" %8s: ", dev->header.dev_name); + EXPECT_TRUE((bool)dev->header._len.dev_name); + ksft_print_msg("%8s: ", dev->header.dev_name); + EXPECT_TRUE(dev->_present.rx_count || + dev->_present.tx_count || + dev->_present.combined_count); if (dev->_present.rx_count) printf("rx %d ", dev->rx_count); if (dev->_present.tx_count) @@ -37,15 +58,28 @@ int main(int argc, char **argv) printf("\n"); } ethtool_channels_get_list_free(channels); +} - rreq._present.header = 1; /* ethtool needs an empty nest.. */ - rings = ethtool_rings_get_dump(ys, &rreq); - if (!rings) - goto err_close; +TEST_F(ethtool, rings) +{ + struct ethtool_rings_get_req_dump rreq = {}; + struct ethtool_rings_get_list *rings; + + rreq._present.header = 1; /* ethtool needs an empty nest */ + rings = ethtool_rings_get_dump(self->ys, &rreq); + ASSERT_NE(NULL, rings) { + TH_LOG("rings dump failed: %s", self->ys->err.msg); + } + + if (ynl_dump_empty(rings)) { + ethtool_rings_get_list_free(rings); + SKIP(return, "no entries in rings dump"); + } - printf("Rings:\n"); ynl_dump_foreach(rings, dev) { - printf(" %8s: ", dev->header.dev_name); + EXPECT_TRUE((bool)dev->header._len.dev_name); + ksft_print_msg("%8s: ", dev->header.dev_name); + EXPECT_TRUE(dev->_present.rx || dev->_present.tx); if (dev->_present.rx) printf("rx %d ", dev->rx); if (dev->_present.tx) @@ -53,13 +87,6 @@ int main(int argc, char **argv) printf("\n"); } ethtool_rings_get_list_free(rings); - - ynl_sock_destroy(ys); - - return 0; - -err_close: - fprintf(stderr, "YNL (%d): %s\n", ys->err.code, ys->err.msg); - ynl_sock_destroy(ys); - return 2; } + +TEST_HARNESS_MAIN diff --git a/tools/net/ynl/tests/ethtool.sh b/tools/net/ynl/tests/ethtool.sh new file mode 100755 index 000000000000..b4f0b2a707bc --- /dev/null +++ b/tools/net/ynl/tests/ethtool.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +source "$(dirname "$(realpath "$0")")/ynl_nsim_lib.sh" +nsim_setup +exec "$(dirname "$(realpath "$0")")/ethtool" -- 2.53.0 Convert rt-addr.c to use kselftest_harness.h with FIXTURE/TEST_F. Validate that the addresses configured by the wrapper (192.168.1.1 and 2001:db8::1) appear in the dump. Output: TAP version 13 1..1 # Starting 1 tests from 1 test cases. # RUN rt_addr.dump ... # lo: 127.0.0.1 # nsim0: 192.168.1.1 # lo: ::1 # nsim0: 2001:db8::1 # nsim0: fe80::7c66:c9ff:fe5f:bf01 # OK rt_addr.dump ok 1 rt_addr.dump # PASSED: 1 / 1 tests passed. # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Jakub Kicinski --- tools/net/ynl/tests/Makefile | 3 +- tools/net/ynl/tests/rt-addr.c | 85 +++++++++++++++++++++++----------- tools/net/ynl/tests/rt-addr.sh | 5 ++ 3 files changed, 65 insertions(+), 28 deletions(-) create mode 100755 tools/net/ynl/tests/rt-addr.sh diff --git a/tools/net/ynl/tests/Makefile b/tools/net/ynl/tests/Makefile index c380e9f331a3..52b54ea6c90f 100644 --- a/tools/net/ynl/tests/Makefile +++ b/tools/net/ynl/tests/Makefile @@ -16,6 +16,7 @@ LDLIBS=../lib/ynl.a ../generated/protos.a TEST_PROGS := \ devlink.sh \ ethtool.sh \ + rt-addr.sh \ test_ynl_cli.sh \ test_ynl_ethtool.sh \ # end of TEST_PROGS @@ -30,10 +31,10 @@ TEST_GEN_PROGS := \ TEST_GEN_FILES := \ devlink \ ethtool \ + rt-addr \ # end of TEST_GEN_FILES BINS := \ - rt-addr \ rt-route \ # end of BINS diff --git a/tools/net/ynl/tests/rt-addr.c b/tools/net/ynl/tests/rt-addr.c index 2edde5c36b18..f6c3715b2f20 100644 --- a/tools/net/ynl/tests/rt-addr.c +++ b/tools/net/ynl/tests/rt-addr.c @@ -7,9 +7,12 @@ #include #include +#include + #include "rt-addr-user.h" -static void rt_addr_print(struct rt_addr_getaddr_rsp *a) +static void rt_addr_print(struct __test_metadata *_metadata, + struct rt_addr_getaddr_rsp *a) { char ifname[IF_NAMESIZE]; char addr_str[64]; @@ -17,9 +20,11 @@ static void rt_addr_print(struct rt_addr_getaddr_rsp *a) const char *name; name = if_indextoname(a->_hdr.ifa_index, ifname); + EXPECT_NE(NULL, name); if (name) - printf("%16s: ", name); + ksft_print_msg("%16s: ", name); + EXPECT_TRUE(a->_len.address == 4 || a->_len.address == 16); switch (a->_len.address) { case 4: addr = inet_ntop(AF_INET, a->address, @@ -41,40 +46,66 @@ static void rt_addr_print(struct rt_addr_getaddr_rsp *a) printf("\n"); } -int main(int argc, char **argv) +FIXTURE(rt_addr) +{ + struct ynl_sock *ys; +}; + +FIXTURE_SETUP(rt_addr) +{ + struct ynl_error yerr; + + self->ys = ynl_sock_create(&ynl_rt_addr_family, &yerr); + ASSERT_NE(NULL, self->ys) + TH_LOG("failed to create rt-addr socket: %s", yerr.msg); +} + +FIXTURE_TEARDOWN(rt_addr) +{ + ynl_sock_destroy(self->ys); +} + +TEST_F(rt_addr, dump) { struct rt_addr_getaddr_list *rsp; struct rt_addr_getaddr_req *req; - struct ynl_error yerr; - struct ynl_sock *ys; + struct in6_addr v6_expected; + struct in_addr v4_expected; + bool found_v4 = false; + bool found_v6 = false; - ys = ynl_sock_create(&ynl_rt_addr_family, &yerr); - if (!ys) { - fprintf(stderr, "YNL: %s\n", yerr.msg); - return 1; - } + /* The bash wrapper for this test adds these addresses on nsim0, + * make sure we can find them in the dump. + */ + inet_pton(AF_INET, "192.168.1.1", &v4_expected); + inet_pton(AF_INET6, "2001:db8::1", &v6_expected); req = rt_addr_getaddr_req_alloc(); - if (!req) - goto err_destroy; + ASSERT_NE(NULL, req); - rsp = rt_addr_getaddr_dump(ys, req); + rsp = rt_addr_getaddr_dump(self->ys, req); rt_addr_getaddr_req_free(req); - if (!rsp) - goto err_close; + ASSERT_NE(NULL, rsp) { + TH_LOG("dump failed: %s", self->ys->err.msg); + } - if (ynl_dump_empty(rsp)) - fprintf(stderr, "Error: no addresses reported\n"); - ynl_dump_foreach(rsp, addr) - rt_addr_print(addr); + ASSERT_FALSE(ynl_dump_empty(rsp)) { + rt_addr_getaddr_list_free(rsp); + TH_LOG("no addresses reported"); + } + + ynl_dump_foreach(rsp, addr) { + rt_addr_print(_metadata, addr); + + found_v4 |= addr->_len.address == 4 && + !memcmp(addr->address, &v4_expected, 4); + found_v6 |= addr->_len.address == 16 && + !memcmp(addr->address, &v6_expected, 16); + } rt_addr_getaddr_list_free(rsp); - ynl_sock_destroy(ys); - return 0; - -err_close: - fprintf(stderr, "YNL: %s\n", ys->err.msg); -err_destroy: - ynl_sock_destroy(ys); - return 2; + EXPECT_TRUE(found_v4); + EXPECT_TRUE(found_v6); } + +TEST_HARNESS_MAIN diff --git a/tools/net/ynl/tests/rt-addr.sh b/tools/net/ynl/tests/rt-addr.sh new file mode 100755 index 000000000000..6fe6a4b78c54 --- /dev/null +++ b/tools/net/ynl/tests/rt-addr.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +source "$(dirname "$(realpath "$0")")/ynl_nsim_lib.sh" +nsim_setup +exec "$(dirname "$(realpath "$0")")/rt-addr" -- 2.53.0 Convert rt-route.c to use kselftest_harness.h with FIXTURE/TEST_F. This is the last test to convert so clean up the Makefile. Validate that the connected routes for 192.168.1.0/24 and 2001:db8::/64 appear in the dump. Output: TAP version 13 1..1 # Starting 1 tests from 1 test cases. # RUN rt_route.dump ... # oif: nsim0 dst: 192.168.1.0/24 # oif: lo dst: ::1/128 # oif: nsim0 dst: 2001:db8::1/128 # oif: nsim0 dst: 2001:db8::/64 # oif: nsim0 dst: fe80::/64 # oif: nsim0 dst: ff00::/8 # OK rt_route.dump ok 1 rt_route.dump # PASSED: 1 / 1 tests passed. # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Jakub Kicinski --- tools/net/ynl/tests/Makefile | 16 +++--- tools/net/ynl/tests/rt-route.c | 87 +++++++++++++++++++++++---------- tools/net/ynl/tests/rt-route.sh | 5 ++ 3 files changed, 72 insertions(+), 36 deletions(-) create mode 100755 tools/net/ynl/tests/rt-route.sh diff --git a/tools/net/ynl/tests/Makefile b/tools/net/ynl/tests/Makefile index 52b54ea6c90f..2a02958c7039 100644 --- a/tools/net/ynl/tests/Makefile +++ b/tools/net/ynl/tests/Makefile @@ -17,6 +17,7 @@ TEST_PROGS := \ devlink.sh \ ethtool.sh \ rt-addr.sh \ + rt-route.sh \ test_ynl_cli.sh \ test_ynl_ethtool.sh \ # end of TEST_PROGS @@ -32,11 +33,8 @@ TEST_GEN_FILES := \ devlink \ ethtool \ rt-addr \ -# end of TEST_GEN_FILES - -BINS := \ rt-route \ -# end of BINS +# end of TEST_GEN_FILES TEST_FILES := ynl_nsim_lib.sh @@ -47,7 +45,7 @@ include $(wildcard *.d) INSTALL_PATH ?= $(DESTDIR)/usr/share/kselftest -all: $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS) +all: $(TEST_GEN_PROGS) $(TEST_GEN_FILES) ../lib/ynl.a: @$(MAKE) -C ../lib @@ -55,7 +53,7 @@ all: $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS) ../generated/protos.a: @$(MAKE) -C ../generated -$(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS): %: %.c ../lib/ynl.a ../generated/protos.a +$(TEST_GEN_PROGS) $(TEST_GEN_FILES): %: %.c ../lib/ynl.a ../generated/protos.a @echo -e '\tCC test $@' @$(COMPILE.c) $(CFLAGS_$@) $@.c -o $@.o @$(LINK.c) $@.o -o $@ $(LDLIBS) @@ -65,7 +63,7 @@ $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS): %: %.c ../lib/ynl.a ../generated/pr ./$$test; \ done -install: $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS) +install: $(TEST_GEN_PROGS) $(TEST_GEN_FILES) @mkdir -p $(INSTALL_PATH)/ynl @cp ../../../testing/selftests/kselftest/ktap_helpers.sh $(INSTALL_PATH)/ @for test in $(TEST_PROGS); do \ @@ -79,7 +77,7 @@ install: $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS) @for file in $(TEST_FILES); do \ cp $$file $(INSTALL_PATH)/ynl/$$file; \ done - @for bin in $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS); do \ + @for bin in $(TEST_GEN_PROGS) $(TEST_GEN_FILES); do \ cp $$bin $(INSTALL_PATH)/ynl/$$bin; \ done @for test in $(TEST_PROGS) $(TEST_GEN_PROGS); do \ @@ -90,7 +88,7 @@ install: $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS) rm -f *.o *.d *~ distclean: clean - rm -f $(TEST_GEN_PROGS) $(TEST_GEN_FILES) $(BINS) + rm -f $(TEST_GEN_PROGS) $(TEST_GEN_FILES) .PHONY: all install clean distclean run_tests .DEFAULT_GOAL=all diff --git a/tools/net/ynl/tests/rt-route.c b/tools/net/ynl/tests/rt-route.c index 7427104a96df..c9fd2bc48144 100644 --- a/tools/net/ynl/tests/rt-route.c +++ b/tools/net/ynl/tests/rt-route.c @@ -7,9 +7,12 @@ #include #include +#include + #include "rt-route-user.h" -static void rt_route_print(struct rt_route_getroute_rsp *r) +static void rt_route_print(struct __test_metadata *_metadata, + struct rt_route_getroute_rsp *r) { char ifname[IF_NAMESIZE]; char route_str[64]; @@ -22,8 +25,9 @@ static void rt_route_print(struct rt_route_getroute_rsp *r) if (r->_present.oif) { name = if_indextoname(r->oif, ifname); + EXPECT_NE(NULL, name); if (name) - printf("oif: %-16s ", name); + ksft_print_msg("oif: %-16s ", name); } if (r->_len.dst) { @@ -41,40 +45,69 @@ static void rt_route_print(struct rt_route_getroute_rsp *r) printf("\n"); } -int main(int argc, char **argv) +FIXTURE(rt_route) +{ + struct ynl_sock *ys; +}; + +FIXTURE_SETUP(rt_route) +{ + struct ynl_error yerr; + + self->ys = ynl_sock_create(&ynl_rt_route_family, &yerr); + ASSERT_NE(NULL, self->ys) + TH_LOG("failed to create rt-route socket: %s", yerr.msg); +} + +FIXTURE_TEARDOWN(rt_route) +{ + ynl_sock_destroy(self->ys); +} + +TEST_F(rt_route, dump) { struct rt_route_getroute_req_dump *req; struct rt_route_getroute_list *rsp; - struct ynl_error yerr; - struct ynl_sock *ys; + struct in6_addr v6_expected; + struct in_addr v4_expected; + bool found_v4 = false; + bool found_v6 = false; - ys = ynl_sock_create(&ynl_rt_route_family, &yerr); - if (!ys) { - fprintf(stderr, "YNL: %s\n", yerr.msg); - return 1; - } + /* The bash wrapper configures 192.168.1.1/24 and 2001:db8::1/64, + * make sure we can find the connected routes in the dump. + */ + inet_pton(AF_INET, "192.168.1.0", &v4_expected); + inet_pton(AF_INET6, "2001:db8::", &v6_expected); req = rt_route_getroute_req_dump_alloc(); - if (!req) - goto err_destroy; + ASSERT_NE(NULL, req); - rsp = rt_route_getroute_dump(ys, req); + rsp = rt_route_getroute_dump(self->ys, req); rt_route_getroute_req_dump_free(req); - if (!rsp) - goto err_close; + ASSERT_NE(NULL, rsp) { + TH_LOG("dump failed: %s", self->ys->err.msg); + } - if (ynl_dump_empty(rsp)) - fprintf(stderr, "Error: no routeesses reported\n"); - ynl_dump_foreach(rsp, route) - rt_route_print(route); + ASSERT_FALSE(ynl_dump_empty(rsp)) { + rt_route_getroute_list_free(rsp); + TH_LOG("no routes reported"); + } + + ynl_dump_foreach(rsp, route) { + rt_route_print(_metadata, route); + + if (route->_hdr.rtm_table == RT_TABLE_LOCAL) + continue; + + if (route->_len.dst == 4 && route->_hdr.rtm_dst_len == 24) + found_v4 |= !memcmp(route->dst, &v4_expected, 4); + if (route->_len.dst == 16 && route->_hdr.rtm_dst_len == 64) + found_v6 |= !memcmp(route->dst, &v6_expected, 16); + } rt_route_getroute_list_free(rsp); - ynl_sock_destroy(ys); - return 0; - -err_close: - fprintf(stderr, "YNL: %s\n", ys->err.msg); -err_destroy: - ynl_sock_destroy(ys); - return 2; + EXPECT_TRUE(found_v4); + EXPECT_TRUE(found_v6); } + +TEST_HARNESS_MAIN diff --git a/tools/net/ynl/tests/rt-route.sh b/tools/net/ynl/tests/rt-route.sh new file mode 100755 index 000000000000..5963d893665c --- /dev/null +++ b/tools/net/ynl/tests/rt-route.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +source "$(dirname "$(realpath "$0")")/ynl_nsim_lib.sh" +nsim_setup +exec "$(dirname "$(realpath "$0")")/rt-route" -- 2.53.0