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