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