Skip to content

Commit 9bb88c6

Browse files
committed
selftests: net: test extacks in netlink dumps
Test that extacks in dumps work. The test fills up the receive buffer to test both the inline dump (as part of sendmsg()) and delayed one (run during recvmsg()). Use YNL helpers to parse the messages. We need to add the test to YNL file to make sure the right include path are used. Reviewed-by: Jacob Keller <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 3bf39fa commit 9bb88c6

File tree

2 files changed

+130
-2
lines changed

2 files changed

+130
-2
lines changed

tools/testing/selftests/net/Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ TEST_PROGS += test_vxlan_vnifiltering.sh
7878
TEST_GEN_FILES += io_uring_zerocopy_tx
7979
TEST_PROGS += io_uring_zerocopy_tx.sh
8080
TEST_GEN_FILES += bind_bhash
81-
TEST_GEN_PROGS += netlink-dumps
8281
TEST_GEN_PROGS += sk_bind_sendto_listen
8382
TEST_GEN_PROGS += sk_connect_zero_addr
8483
TEST_GEN_PROGS += sk_so_peek_off
@@ -101,7 +100,7 @@ TEST_PROGS += ipv6_route_update_soft_lockup.sh
101100
TEST_PROGS += busy_poll_test.sh
102101

103102
# YNL files, must be before "include ..lib.mk"
104-
YNL_GEN_FILES := busy_poller
103+
YNL_GEN_FILES := busy_poller netlink-dumps
105104
TEST_GEN_FILES += $(YNL_GEN_FILES)
106105

107106
TEST_FILES := settings

tools/testing/selftests/net/netlink-dumps.c

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,140 @@
1212
#include <unistd.h>
1313

1414
#include <linux/genetlink.h>
15+
#include <linux/neighbour.h>
16+
#include <linux/netdevice.h>
1517
#include <linux/netlink.h>
1618
#include <linux/mqueue.h>
19+
#include <linux/rtnetlink.h>
1720

1821
#include "../kselftest_harness.h"
1922

23+
#include <ynl.h>
24+
25+
struct ext_ack {
26+
int err;
27+
28+
__u32 attr_offs;
29+
__u32 miss_type;
30+
__u32 miss_nest;
31+
const char *str;
32+
};
33+
34+
/* 0: no done, 1: done found, 2: extack found, -1: error */
35+
static int nl_get_extack(char *buf, size_t n, struct ext_ack *ea)
36+
{
37+
const struct nlmsghdr *nlh;
38+
const struct nlattr *attr;
39+
ssize_t rem;
40+
41+
for (rem = n; rem > 0; NLMSG_NEXT(nlh, rem)) {
42+
nlh = (struct nlmsghdr *)&buf[n - rem];
43+
if (!NLMSG_OK(nlh, rem))
44+
return -1;
45+
46+
if (nlh->nlmsg_type != NLMSG_DONE)
47+
continue;
48+
49+
ea->err = -*(int *)NLMSG_DATA(nlh);
50+
51+
if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
52+
return 1;
53+
54+
ynl_attr_for_each(attr, nlh, sizeof(int)) {
55+
switch (ynl_attr_type(attr)) {
56+
case NLMSGERR_ATTR_OFFS:
57+
ea->attr_offs = ynl_attr_get_u32(attr);
58+
break;
59+
case NLMSGERR_ATTR_MISS_TYPE:
60+
ea->miss_type = ynl_attr_get_u32(attr);
61+
break;
62+
case NLMSGERR_ATTR_MISS_NEST:
63+
ea->miss_nest = ynl_attr_get_u32(attr);
64+
break;
65+
case NLMSGERR_ATTR_MSG:
66+
ea->str = ynl_attr_get_str(attr);
67+
break;
68+
}
69+
}
70+
71+
return 2;
72+
}
73+
74+
return 0;
75+
}
76+
77+
static const struct {
78+
struct nlmsghdr nlhdr;
79+
struct ndmsg ndm;
80+
struct nlattr ahdr;
81+
__u32 val;
82+
} dump_neigh_bad = {
83+
.nlhdr = {
84+
.nlmsg_len = sizeof(dump_neigh_bad),
85+
.nlmsg_type = RTM_GETNEIGH,
86+
.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
87+
.nlmsg_seq = 1,
88+
},
89+
.ndm = {
90+
.ndm_family = 123,
91+
},
92+
.ahdr = {
93+
.nla_len = 4 + 4,
94+
.nla_type = NDA_FLAGS_EXT,
95+
},
96+
.val = -1, // should fail MASK validation
97+
};
98+
99+
TEST(dump_extack)
100+
{
101+
int netlink_sock;
102+
char buf[8192];
103+
int one = 1;
104+
int i, cnt;
105+
ssize_t n;
106+
107+
netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
108+
ASSERT_GE(netlink_sock, 0);
109+
110+
n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_CAP_ACK,
111+
&one, sizeof(one));
112+
ASSERT_EQ(n, 0);
113+
n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_EXT_ACK,
114+
&one, sizeof(one));
115+
ASSERT_EQ(n, 0);
116+
n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_GET_STRICT_CHK,
117+
&one, sizeof(one));
118+
ASSERT_EQ(n, 0);
119+
120+
/* Dump so many times we fill up the buffer */
121+
cnt = 64;
122+
for (i = 0; i < cnt; i++) {
123+
n = send(netlink_sock, &dump_neigh_bad,
124+
sizeof(dump_neigh_bad), 0);
125+
ASSERT_EQ(n, sizeof(dump_neigh_bad));
126+
}
127+
128+
/* Read out the ENOBUFS */
129+
n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT);
130+
EXPECT_EQ(n, -1);
131+
EXPECT_EQ(errno, ENOBUFS);
132+
133+
for (i = 0; i < cnt; i++) {
134+
struct ext_ack ea = {};
135+
136+
n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT);
137+
if (n < 0) {
138+
ASSERT_GE(i, 10);
139+
break;
140+
}
141+
ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr));
142+
143+
EXPECT_EQ(nl_get_extack(buf, n, &ea), 2);
144+
EXPECT_EQ(ea.attr_offs,
145+
sizeof(struct nlmsghdr) + sizeof(struct ndmsg));
146+
}
147+
}
148+
20149
static const struct {
21150
struct nlmsghdr nlhdr;
22151
struct genlmsghdr genlhdr;

0 commit comments

Comments
 (0)