xref: /linux/tools/testing/selftests/net/netlink-dumps.c (revision 509d3f45847627f4c5cdce004c3ec79262b5239c)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #define _GNU_SOURCE
4 
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <sys/socket.h>
9 #include <sys/stat.h>
10 #include <sys/syscall.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13 
14 #include <linux/genetlink.h>
15 #include <linux/neighbour.h>
16 #include <linux/netdevice.h>
17 #include <linux/netlink.h>
18 #include <linux/mqueue.h>
19 #include <linux/rtnetlink.h>
20 
21 #include "kselftest_harness.h"
22 
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 enum get_ea_ret {
35 	ERROR = -1,
36 	NO_CTRL = 0,
37 	FOUND_DONE,
38 	FOUND_ERR,
39 	FOUND_EXTACK,
40 };
41 
42 static enum get_ea_ret
nl_get_extack(char * buf,size_t n,struct ext_ack * ea)43 nl_get_extack(char *buf, size_t n, struct ext_ack *ea)
44 {
45 	enum get_ea_ret ret = NO_CTRL;
46 	const struct nlmsghdr *nlh;
47 	const struct nlattr *attr;
48 	ssize_t rem;
49 
50 	for (rem = n; rem > 0; NLMSG_NEXT(nlh, rem)) {
51 		nlh = (struct nlmsghdr *)&buf[n - rem];
52 		if (!NLMSG_OK(nlh, rem))
53 			return ERROR;
54 
55 		if (nlh->nlmsg_type == NLMSG_ERROR)
56 			ret = FOUND_ERR;
57 		else if (nlh->nlmsg_type == NLMSG_DONE)
58 			ret = FOUND_DONE;
59 		else
60 			continue;
61 
62 		ea->err = -*(int *)NLMSG_DATA(nlh);
63 
64 		if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
65 			return ret;
66 
67 		ynl_attr_for_each(attr, nlh, sizeof(int)) {
68 			switch (ynl_attr_type(attr)) {
69 			case NLMSGERR_ATTR_OFFS:
70 				ea->attr_offs = ynl_attr_get_u32(attr);
71 				break;
72 			case NLMSGERR_ATTR_MISS_TYPE:
73 				ea->miss_type = ynl_attr_get_u32(attr);
74 				break;
75 			case NLMSGERR_ATTR_MISS_NEST:
76 				ea->miss_nest = ynl_attr_get_u32(attr);
77 				break;
78 			case NLMSGERR_ATTR_MSG:
79 				ea->str = ynl_attr_get_str(attr);
80 				break;
81 			}
82 		}
83 
84 		return FOUND_EXTACK;
85 	}
86 
87 	return ret;
88 }
89 
90 static const struct {
91 	struct nlmsghdr nlhdr;
92 	struct ndmsg ndm;
93 	struct nlattr ahdr;
94 	__u32 val;
95 } dump_neigh_bad = {
96 	.nlhdr = {
97 		.nlmsg_len	= sizeof(dump_neigh_bad),
98 		.nlmsg_type	= RTM_GETNEIGH,
99 		.nlmsg_flags	= NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
100 		.nlmsg_seq	= 1,
101 	},
102 	.ndm = {
103 		.ndm_family	= 123,
104 	},
105 	.ahdr = {
106 		.nla_len	= 4 + 4,
107 		.nla_type	= NDA_FLAGS_EXT,
108 	},
109 	.val = -1, // should fail MASK validation
110 };
111 
TEST(dump_extack)112 TEST(dump_extack)
113 {
114 	int netlink_sock;
115 	int i, cnt, ret;
116 	char buf[8192];
117 	int one = 1;
118 	ssize_t n;
119 
120 	netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
121 	ASSERT_GE(netlink_sock, 0);
122 
123 	n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_CAP_ACK,
124 		       &one, sizeof(one));
125 	ASSERT_EQ(n, 0);
126 	n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_EXT_ACK,
127 		       &one, sizeof(one));
128 	ASSERT_EQ(n, 0);
129 	n = setsockopt(netlink_sock, SOL_NETLINK, NETLINK_GET_STRICT_CHK,
130 		       &one, sizeof(one));
131 	ASSERT_EQ(n, 0);
132 
133 	/* Dump so many times we fill up the buffer */
134 	cnt = 80;
135 	for (i = 0; i < cnt; i++) {
136 		n = send(netlink_sock, &dump_neigh_bad,
137 			 sizeof(dump_neigh_bad), 0);
138 		ASSERT_EQ(n, sizeof(dump_neigh_bad));
139 	}
140 
141 	/* Read out the ENOBUFS */
142 	n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT);
143 	EXPECT_EQ(n, -1);
144 	EXPECT_EQ(errno, ENOBUFS);
145 
146 	ret = NO_CTRL;
147 	for (i = 0; i < cnt; i++) {
148 		struct ext_ack ea = {};
149 
150 		n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT);
151 		if (n < 0) {
152 			ASSERT_GE(i, 10);
153 			break;
154 		}
155 		ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr));
156 
157 		ret = nl_get_extack(buf, n, &ea);
158 		/* Once we fill the buffer we'll see one ENOBUFS followed
159 		 * by a number of EBUSYs. Then the last recv() will finally
160 		 * trigger and complete the dump.
161 		 */
162 		if (ret == FOUND_ERR && (ea.err == ENOBUFS || ea.err == EBUSY))
163 			continue;
164 		EXPECT_EQ(ret, FOUND_EXTACK);
165 		EXPECT_EQ(ea.err, EINVAL);
166 		EXPECT_EQ(ea.attr_offs,
167 			  sizeof(struct nlmsghdr) + sizeof(struct ndmsg));
168 	}
169 	/* Make sure last message was a full DONE+extack */
170 	EXPECT_EQ(ret, FOUND_EXTACK);
171 }
172 
173 static const struct {
174 	struct nlmsghdr nlhdr;
175 	struct genlmsghdr genlhdr;
176 	struct nlattr ahdr;
177 	__u16 val;
178 	__u16 pad;
179 } dump_policies = {
180 	.nlhdr = {
181 		.nlmsg_len	= sizeof(dump_policies),
182 		.nlmsg_type	= GENL_ID_CTRL,
183 		.nlmsg_flags	= NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
184 		.nlmsg_seq	= 1,
185 	},
186 	.genlhdr = {
187 		.cmd		= CTRL_CMD_GETPOLICY,
188 		.version	= 2,
189 	},
190 	.ahdr = {
191 		.nla_len	= 6,
192 		.nla_type	= CTRL_ATTR_FAMILY_ID,
193 	},
194 	.val = GENL_ID_CTRL,
195 	.pad = 0,
196 };
197 
198 // Sanity check for the test itself, make sure the dump doesn't fit in one msg
TEST(test_sanity)199 TEST(test_sanity)
200 {
201 	int netlink_sock;
202 	char buf[8192];
203 	ssize_t n;
204 
205 	netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
206 	ASSERT_GE(netlink_sock, 0);
207 
208 	n = send(netlink_sock, &dump_policies, sizeof(dump_policies), 0);
209 	ASSERT_EQ(n, sizeof(dump_policies));
210 
211 	n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT);
212 	ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr));
213 
214 	n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT);
215 	ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr));
216 
217 	close(netlink_sock);
218 }
219 
TEST(close_in_progress)220 TEST(close_in_progress)
221 {
222 	int netlink_sock;
223 	ssize_t n;
224 
225 	netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
226 	ASSERT_GE(netlink_sock, 0);
227 
228 	n = send(netlink_sock, &dump_policies, sizeof(dump_policies), 0);
229 	ASSERT_EQ(n, sizeof(dump_policies));
230 
231 	close(netlink_sock);
232 }
233 
TEST(close_with_ref)234 TEST(close_with_ref)
235 {
236 	char cookie[NOTIFY_COOKIE_LEN] = {};
237 	int netlink_sock, mq_fd;
238 	struct sigevent sigev;
239 	ssize_t n;
240 
241 	netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
242 	ASSERT_GE(netlink_sock, 0);
243 
244 	n = send(netlink_sock, &dump_policies, sizeof(dump_policies), 0);
245 	ASSERT_EQ(n, sizeof(dump_policies));
246 
247 	mq_fd = syscall(__NR_mq_open, "sed", O_CREAT | O_WRONLY, 0600, 0);
248 	ASSERT_GE(mq_fd, 0);
249 
250 	memset(&sigev, 0, sizeof(sigev));
251 	sigev.sigev_notify		= SIGEV_THREAD;
252 	sigev.sigev_value.sival_ptr	= cookie;
253 	sigev.sigev_signo		= netlink_sock;
254 
255 	syscall(__NR_mq_notify, mq_fd, &sigev);
256 
257 	close(netlink_sock);
258 
259 	// give mqueue time to fire
260 	usleep(100 * 1000);
261 }
262 
263 TEST_HARNESS_MAIN
264