| // SPDX-License-Identifier: GPL-2.0 | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <ynl.h> | |
| #include <arpa/inet.h> | |
| #include <net/if.h> | |
| #include <kselftest_harness.h> | |
| #include "rt-link-user.h" | |
| static void rt_link_print(struct __test_metadata *_metadata, | |
| struct rt_link_getlink_rsp *r) | |
| { | |
| unsigned int i; | |
| 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("%6s: ", r->ifname); | |
| if (r->_present.mtu) | |
| printf("mtu %5d ", r->mtu); | |
| if (r->linkinfo._len.kind) | |
| printf("kind %-8s ", r->linkinfo.kind); | |
| else | |
| printf(" %8s ", ""); | |
| if (r->prop_list._count.alt_ifname) { | |
| printf("altname "); | |
| for (i = 0; i < r->prop_list._count.alt_ifname; i++) | |
| printf("%s ", r->prop_list.alt_ifname[i]->str); | |
| printf(" "); | |
| } | |
| if (r->linkinfo._present.data && r->linkinfo.data._present.netkit) { | |
| struct rt_link_linkinfo_netkit_attrs *netkit; | |
| const char *name; | |
| netkit = &r->linkinfo.data.netkit; | |
| printf("primary %d ", netkit->primary); | |
| name = NULL; | |
| if (netkit->_present.policy) | |
| name = rt_link_netkit_policy_str(netkit->policy); | |
| if (name) | |
| printf("policy %s ", name); | |
| } | |
| printf("\n"); | |
| } | |
| static int netkit_create(struct ynl_sock *ys) | |
| { | |
| 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, "netkit"); | |
| rt_link_newlink_req_set_linkinfo_data_netkit_policy(req, NETKIT_DROP); | |
| ret = rt_link_newlink(ys, req); | |
| rt_link_newlink_req_free(req); | |
| if (ret) | |
| return -1; | |
| if (!ynl_has_ntf(ys)) | |
| return 0; | |
| ntf = ynl_ntf_dequeue(ys); | |
| 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 netkit_delete(struct __test_metadata *_metadata, | |
| struct ynl_sock *ys, 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, req)); | |
| rt_link_dellink_req_free(req); | |
| } | |
| 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; | |
| req = rt_link_getlink_req_dump_alloc(); | |
| ASSERT_NE(NULL, req); | |
| rsp = rt_link_getlink_dump(self->ys, req); | |
| rt_link_getlink_req_dump_free(req); | |
| ASSERT_NE(NULL, rsp) { | |
| TH_LOG("dump failed: %s", self->ys->err.msg); | |
| } | |
| ASSERT_FALSE(ynl_dump_empty(rsp)); | |
| ynl_dump_foreach(rsp, link) | |
| rt_link_print(_metadata, link); | |
| rt_link_getlink_list_free(rsp); | |
| } | |
| 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(); | |
| ASSERT_NE(NULL, dreq); | |
| 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(); | |
| ASSERT_NE(NULL, req); | |
| 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 |