File Index
Files and directories
Sign in
FILE

netlink-dumps.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/net/netlink-dumps.c C
  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
 25struct ext_ack {
 26	int err;
 27
 28	__u32 attr_offs;
 29	__u32 miss_type;
 30	__u32 miss_nest;
 31	const char *str;
 32};
 33
 34enum get_ea_ret {
 35	ERROR = -1,
 36	NO_CTRL = 0,
 37	FOUND_DONE,
 38	FOUND_ERR,
 39	FOUND_EXTACK,
 40};
 41
 42static enum get_ea_ret
 43nl_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
 90static 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
112TEST(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
173static 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
199TEST(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
220TEST(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
234TEST(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
263TEST_HARNESS_MAIN
~/ Read-only file preview.