]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | #include <linux/fou.h> | |
4 | #include <linux/genetlink.h> | |
5 | #include <linux/if_macsec.h> | |
6 | #include <linux/l2tp.h> | |
7 | #include <linux/nl80211.h> | |
8 | #include <linux/unix_diag.h> | |
9 | #include <net/ethernet.h> | |
10 | #include <net/if.h> | |
11 | #include <netinet/in.h> | |
12 | #include <sys/stat.h> | |
13 | #include <unistd.h> | |
14 | ||
15 | #include "sd-event.h" | |
16 | #include "sd-netlink.h" | |
17 | ||
18 | #include "alloc-util.h" | |
19 | #include "fd-util.h" | |
20 | #include "missing_network.h" | |
21 | #include "netlink-genl.h" | |
22 | #include "netlink-internal.h" | |
23 | #include "netlink-sock-diag.h" | |
24 | #include "netlink-util.h" | |
25 | #include "socket-util.h" | |
26 | #include "stdio-util.h" | |
27 | #include "strv.h" | |
28 | #include "tests.h" | |
29 | #include "time-util.h" | |
30 | ||
31 | TEST(message_newlink_bridge) { | |
32 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
33 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; | |
34 | uint32_t cost; | |
35 | ||
36 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
37 | ||
38 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &message, RTM_NEWLINK, 1)); | |
39 | ASSERT_OK(sd_rtnl_message_link_set_family(message, AF_BRIDGE)); | |
40 | ASSERT_OK(sd_netlink_message_open_container(message, IFLA_PROTINFO)); | |
41 | ASSERT_OK(sd_netlink_message_append_u32(message, IFLA_BRPORT_COST, 10)); | |
42 | ASSERT_OK(sd_netlink_message_close_container(message)); | |
43 | ||
44 | ASSERT_OK(sd_netlink_message_rewind(message, rtnl)); | |
45 | ||
46 | ASSERT_OK(sd_netlink_message_enter_container(message, IFLA_PROTINFO)); | |
47 | ASSERT_OK(sd_netlink_message_read_u32(message, IFLA_BRPORT_COST, &cost)); | |
48 | ASSERT_EQ(cost, 10U); | |
49 | ASSERT_OK(sd_netlink_message_exit_container(message)); | |
50 | } | |
51 | ||
52 | TEST(message_getlink) { | |
53 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
54 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; | |
55 | int ifindex; | |
56 | uint8_t u8_data; | |
57 | uint16_t u16_data; | |
58 | uint32_t u32_data; | |
59 | const char *str_data; | |
60 | struct ether_addr eth_data; | |
61 | ||
62 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
63 | ifindex = (int) if_nametoindex("lo"); | |
64 | ||
65 | /* we'd really like to test NEWLINK, but let's not mess with the running kernel */ | |
66 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &message, RTM_GETLINK, ifindex)); | |
67 | ASSERT_OK_EQ(sd_netlink_call(rtnl, message, 0, &reply), 1); | |
68 | ||
69 | /* u8 */ | |
70 | ASSERT_OK(sd_netlink_message_read_u8(reply, IFLA_CARRIER, &u8_data)); | |
71 | ASSERT_OK(sd_netlink_message_read_u8(reply, IFLA_OPERSTATE, &u8_data)); | |
72 | ASSERT_OK(sd_netlink_message_read_u8(reply, IFLA_LINKMODE, &u8_data)); | |
73 | ||
74 | /* u16 */ | |
75 | ASSERT_OK(sd_netlink_message_get_type(reply, &u16_data)); | |
76 | ASSERT_EQ(u16_data, RTM_NEWLINK); | |
77 | ||
78 | /* u32 */ | |
79 | ASSERT_OK(sd_netlink_message_read_u32(reply, IFLA_MTU, &u32_data)); | |
80 | ASSERT_OK(sd_netlink_message_read_u32(reply, IFLA_GROUP, &u32_data)); | |
81 | ASSERT_OK(sd_netlink_message_read_u32(reply, IFLA_TXQLEN, &u32_data)); | |
82 | ASSERT_OK(sd_netlink_message_read_u32(reply, IFLA_NUM_TX_QUEUES, &u32_data)); | |
83 | ||
84 | /* string */ | |
85 | ASSERT_OK(sd_netlink_message_read_string(reply, IFLA_IFNAME, &str_data)); | |
86 | ||
87 | /* ether_addr */ | |
88 | ASSERT_OK(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, ð_data)); | |
89 | } | |
90 | ||
91 | TEST(message_address) { | |
92 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
93 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; | |
94 | int ifindex; | |
95 | struct in_addr in_data; | |
96 | struct ifa_cacheinfo cache; | |
97 | const char *label; | |
98 | int r; | |
99 | ||
100 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
101 | ifindex = (int) if_nametoindex("lo"); | |
102 | ||
103 | ASSERT_OK(sd_rtnl_message_new_addr(rtnl, &message, RTM_GETADDR, ifindex, AF_INET)); | |
104 | ASSERT_OK(sd_netlink_message_set_request_dump(message, true)); | |
105 | ||
106 | ASSERT_OK(r = sd_netlink_call(rtnl, message, 0, &reply)); | |
107 | ||
108 | /* If the loopback device is down we won't get any results. */ | |
109 | if (r > 0) { | |
110 | ASSERT_OK(sd_netlink_message_read_in_addr(reply, IFA_LOCAL, &in_data)); | |
111 | ASSERT_OK(sd_netlink_message_read_in_addr(reply, IFA_ADDRESS, &in_data)); | |
112 | ASSERT_OK(sd_netlink_message_read_string(reply, IFA_LABEL, &label)); | |
113 | ASSERT_OK(sd_netlink_message_read_cache_info(reply, IFA_CACHEINFO, &cache)); | |
114 | } | |
115 | } | |
116 | ||
117 | TEST(message_route) { | |
118 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
119 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; | |
120 | struct in_addr addr, addr_data; | |
121 | uint32_t index = 2, u32_data; | |
122 | ||
123 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
124 | ||
125 | ASSERT_OK(sd_rtnl_message_new_route(rtnl, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC)); | |
126 | ||
127 | addr.s_addr = htobe32(INADDR_LOOPBACK); | |
128 | ||
129 | ASSERT_OK(sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &addr)); | |
130 | ASSERT_OK(sd_netlink_message_append_u32(req, RTA_OIF, index)); | |
131 | ||
132 | ASSERT_OK(sd_netlink_message_rewind(req, rtnl)); | |
133 | ||
134 | ASSERT_OK(sd_netlink_message_read_in_addr(req, RTA_GATEWAY, &addr_data)); | |
135 | ASSERT_EQ(addr_data.s_addr, addr.s_addr); | |
136 | ||
137 | ASSERT_OK(sd_netlink_message_read_u32(req, RTA_OIF, &u32_data)); | |
138 | ASSERT_EQ(u32_data, index); | |
139 | ||
140 | ASSERT_NULL(req = sd_netlink_message_unref(req)); | |
141 | } | |
142 | ||
143 | static int link_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { | |
144 | const char *data; | |
145 | ||
146 | ASSERT_NOT_NULL(rtnl); | |
147 | ASSERT_NOT_NULL(m); | |
148 | ||
149 | ASSERT_STREQ(userdata, "foo"); | |
150 | ||
151 | ASSERT_OK(sd_netlink_message_read_string(m, IFLA_IFNAME, &data)); | |
152 | ASSERT_STREQ(data, "lo"); | |
153 | ||
154 | log_info("%s: got link info about %s", __func__, data); | |
155 | return 1; | |
156 | } | |
157 | ||
158 | TEST(netlink_event_loop) { | |
159 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; | |
160 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
161 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
162 | _cleanup_free_ char *userdata = NULL; | |
163 | int ifindex; | |
164 | ||
165 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
166 | ifindex = (int) if_nametoindex("lo"); | |
167 | ||
168 | ASSERT_NOT_NULL((userdata = strdup("foo"))); | |
169 | ||
170 | ASSERT_OK(sd_event_default(&event)); | |
171 | ASSERT_OK(sd_netlink_attach_event(rtnl, event, 0)); | |
172 | ||
173 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex)); | |
174 | ASSERT_OK(sd_netlink_call_async(rtnl, NULL, m, link_handler, NULL, userdata, 0, NULL)); | |
175 | ||
176 | ASSERT_OK(sd_event_run(event, 0)); | |
177 | ||
178 | ASSERT_OK(sd_netlink_detach_event(rtnl)); | |
179 | ASSERT_NULL(rtnl = sd_netlink_unref(rtnl)); | |
180 | } | |
181 | ||
182 | static void test_async_destroy(void *userdata) { | |
183 | } | |
184 | ||
185 | TEST(netlink_call_async) { | |
186 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
187 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL; | |
188 | _cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *slot = NULL; | |
189 | _cleanup_free_ char *userdata = NULL; | |
190 | sd_netlink_destroy_t destroy_callback; | |
191 | const char *description; | |
192 | int ifindex; | |
193 | ||
194 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
195 | ifindex = (int) if_nametoindex("lo"); | |
196 | ||
197 | ASSERT_NOT_NULL((userdata = strdup("foo"))); | |
198 | ||
199 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex)); | |
200 | ASSERT_OK(sd_netlink_call_async(rtnl, &slot, m, link_handler, test_async_destroy, userdata, 0, "hogehoge")); | |
201 | ||
202 | ASSERT_PTR_EQ(sd_netlink_slot_get_netlink(slot), rtnl); | |
203 | ||
204 | ASSERT_PTR_EQ(sd_netlink_slot_get_userdata(slot), userdata); | |
205 | ASSERT_PTR_EQ(sd_netlink_slot_set_userdata(slot, NULL), userdata); | |
206 | ASSERT_NULL(sd_netlink_slot_get_userdata(slot)); | |
207 | ASSERT_NULL(sd_netlink_slot_set_userdata(slot, userdata)); | |
208 | ASSERT_PTR_EQ(sd_netlink_slot_get_userdata(slot), userdata); | |
209 | ||
210 | ASSERT_OK_EQ(sd_netlink_slot_get_destroy_callback(slot, &destroy_callback), 1); | |
211 | ASSERT_PTR_EQ(destroy_callback, test_async_destroy); | |
212 | ASSERT_OK(sd_netlink_slot_set_destroy_callback(slot, NULL)); | |
213 | ASSERT_OK_ZERO(sd_netlink_slot_get_destroy_callback(slot, &destroy_callback)); | |
214 | ASSERT_NULL(destroy_callback); | |
215 | ASSERT_OK(sd_netlink_slot_set_destroy_callback(slot, test_async_destroy)); | |
216 | ASSERT_OK_EQ(sd_netlink_slot_get_destroy_callback(slot, &destroy_callback), 1); | |
217 | ASSERT_PTR_EQ(destroy_callback, test_async_destroy); | |
218 | ||
219 | ASSERT_OK_ZERO(sd_netlink_slot_get_floating(slot)); | |
220 | ASSERT_OK_EQ(sd_netlink_slot_set_floating(slot, 1), 1); | |
221 | ASSERT_OK_EQ(sd_netlink_slot_get_floating(slot), 1); | |
222 | ||
223 | ASSERT_OK_EQ(sd_netlink_slot_get_description(slot, &description), 1); | |
224 | ASSERT_STREQ(description, "hogehoge"); | |
225 | ASSERT_OK(sd_netlink_slot_set_description(slot, NULL)); | |
226 | ASSERT_OK_ZERO(sd_netlink_slot_get_description(slot, &description)); | |
227 | ASSERT_NULL(description); | |
228 | ||
229 | ASSERT_OK(sd_netlink_wait(rtnl, 0)); | |
230 | ASSERT_OK(sd_netlink_process(rtnl, &reply)); | |
231 | ||
232 | ASSERT_NULL(rtnl = sd_netlink_unref(rtnl)); | |
233 | } | |
234 | ||
235 | struct test_async_object { | |
236 | unsigned n_ref; | |
237 | char *ifname; | |
238 | }; | |
239 | ||
240 | static struct test_async_object *test_async_object_free(struct test_async_object *t) { | |
241 | ASSERT_NOT_NULL(t); | |
242 | ||
243 | free(t->ifname); | |
244 | return mfree(t); | |
245 | } | |
246 | ||
247 | DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(struct test_async_object, test_async_object, test_async_object_free); | |
248 | DEFINE_TRIVIAL_CLEANUP_FUNC(struct test_async_object *, test_async_object_unref); | |
249 | ||
250 | static int link_handler2(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { | |
251 | struct test_async_object *t = userdata; | |
252 | const char *data; | |
253 | ||
254 | ASSERT_NOT_NULL(rtnl); | |
255 | ASSERT_NOT_NULL(m); | |
256 | ASSERT_NOT_NULL(userdata); | |
257 | ||
258 | log_info("%s: got link info about %s", __func__, t->ifname); | |
259 | ||
260 | ASSERT_OK(sd_netlink_message_read_string(m, IFLA_IFNAME, &data)); | |
261 | ASSERT_STREQ(data, "lo"); | |
262 | ||
263 | return 1; | |
264 | } | |
265 | ||
266 | static void test_async_object_destroy(void *userdata) { | |
267 | struct test_async_object *t = userdata; | |
268 | ||
269 | ASSERT_NOT_NULL(userdata); | |
270 | ||
271 | log_info("%s: n_ref=%u", __func__, t->n_ref); | |
272 | test_async_object_unref(t); | |
273 | } | |
274 | ||
275 | TEST(async_destroy_callback) { | |
276 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
277 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL; | |
278 | _cleanup_(test_async_object_unrefp) struct test_async_object *t = NULL; | |
279 | _cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *slot = NULL; | |
280 | int ifindex; | |
281 | ||
282 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
283 | ifindex = (int) if_nametoindex("lo"); | |
284 | ||
285 | ASSERT_NOT_NULL((t = new(struct test_async_object, 1))); | |
286 | *t = (struct test_async_object) { | |
287 | .n_ref = 1, | |
288 | }; | |
289 | ASSERT_NOT_NULL((t->ifname = strdup("lo"))); | |
290 | ||
291 | /* destroy callback is called after processing message */ | |
292 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex)); | |
293 | ASSERT_OK(sd_netlink_call_async(rtnl, NULL, m, link_handler2, test_async_object_destroy, t, 0, NULL)); | |
294 | ||
295 | ASSERT_EQ(t->n_ref, 1U); | |
296 | ASSERT_PTR_EQ(test_async_object_ref(t), t); | |
297 | ASSERT_EQ(t->n_ref, 2U); | |
298 | ||
299 | ASSERT_OK(sd_netlink_wait(rtnl, 0)); | |
300 | ASSERT_OK_EQ(sd_netlink_process(rtnl, &reply), 1); | |
301 | ASSERT_EQ(t->n_ref, 1U); | |
302 | ||
303 | ASSERT_NULL(sd_netlink_message_unref(m)); | |
304 | ||
305 | /* destroy callback is called when asynchronous call is cancelled, that is, slot is freed. */ | |
306 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex)); | |
307 | ASSERT_OK(sd_netlink_call_async(rtnl, &slot, m, link_handler2, test_async_object_destroy, t, 0, NULL)); | |
308 | ||
309 | ASSERT_EQ(t->n_ref, 1U); | |
310 | ASSERT_PTR_EQ(test_async_object_ref(t), t); | |
311 | ASSERT_EQ(t->n_ref, 2U); | |
312 | ||
313 | ASSERT_NULL(slot = sd_netlink_slot_unref(slot)); | |
314 | ASSERT_EQ(t->n_ref, 1U); | |
315 | ||
316 | ASSERT_NULL(sd_netlink_message_unref(m)); | |
317 | ||
318 | /* destroy callback is also called by sd_netlink_unref() */ | |
319 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex)); | |
320 | ASSERT_OK(sd_netlink_call_async(rtnl, NULL, m, link_handler2, test_async_object_destroy, t, 0, NULL)); | |
321 | ||
322 | ASSERT_EQ(t->n_ref, 1U); | |
323 | ASSERT_PTR_EQ(test_async_object_ref(t), t); | |
324 | ASSERT_EQ(t->n_ref, 2U); | |
325 | ||
326 | ASSERT_NULL(rtnl = sd_netlink_unref(rtnl)); | |
327 | ASSERT_EQ(t->n_ref, 1U); | |
328 | } | |
329 | ||
330 | static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { | |
331 | int r, *counter = userdata; | |
332 | ||
333 | (*counter)--; | |
334 | ||
335 | ASSERT_OK(r = sd_netlink_message_get_errno(m)); | |
336 | log_info_errno(r, "%d left in pipe. got reply: %m", *counter); | |
337 | return 1; | |
338 | } | |
339 | ||
340 | TEST(pipe) { | |
341 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
342 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m1 = NULL, *m2 = NULL; | |
343 | int ifindex, counter = 0; | |
344 | ||
345 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
346 | ifindex = (int) if_nametoindex("lo"); | |
347 | ||
348 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &m1, RTM_GETLINK, ifindex)); | |
349 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &m2, RTM_GETLINK, ifindex)); | |
350 | ||
351 | counter++; | |
352 | ASSERT_OK(sd_netlink_call_async(rtnl, NULL, m1, pipe_handler, NULL, &counter, 0, NULL)); | |
353 | ||
354 | counter++; | |
355 | ASSERT_OK(sd_netlink_call_async(rtnl, NULL, m2, pipe_handler, NULL, &counter, 0, NULL)); | |
356 | ||
357 | while (counter > 0) { | |
358 | ASSERT_OK(sd_netlink_wait(rtnl, 0)); | |
359 | ASSERT_OK(sd_netlink_process(rtnl, NULL)); | |
360 | } | |
361 | ||
362 | ASSERT_NULL(rtnl = sd_netlink_unref(rtnl)); | |
363 | } | |
364 | ||
365 | TEST(message_container) { | |
366 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
367 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
368 | uint16_t u16_data; | |
369 | uint32_t u32_data; | |
370 | const char *string_data; | |
371 | ||
372 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
373 | ||
374 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0)); | |
375 | ||
376 | ASSERT_OK(sd_netlink_message_open_container(m, IFLA_LINKINFO)); | |
377 | ASSERT_OK(sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "vlan")); | |
378 | ASSERT_OK(sd_netlink_message_append_u16(m, IFLA_VLAN_ID, 100)); | |
379 | ASSERT_OK(sd_netlink_message_close_container(m)); | |
380 | ASSERT_OK(sd_netlink_message_close_container(m)); | |
381 | ||
382 | ASSERT_OK(sd_netlink_message_rewind(m, rtnl)); | |
383 | ||
384 | ASSERT_OK(sd_netlink_message_enter_container(m, IFLA_LINKINFO)); | |
385 | ASSERT_OK(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data)); | |
386 | ASSERT_STREQ("vlan", string_data); | |
387 | ||
388 | ASSERT_OK(sd_netlink_message_enter_container(m, IFLA_INFO_DATA)); | |
389 | ASSERT_OK(sd_netlink_message_read_u16(m, IFLA_VLAN_ID, &u16_data)); | |
390 | ASSERT_OK(sd_netlink_message_exit_container(m)); | |
391 | ||
392 | ASSERT_OK(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data)); | |
393 | ASSERT_STREQ("vlan", string_data); | |
394 | ASSERT_OK(sd_netlink_message_exit_container(m)); | |
395 | ||
396 | ASSERT_FAIL(sd_netlink_message_read_u32(m, IFLA_LINKINFO, &u32_data)); | |
397 | } | |
398 | ||
399 | TEST(sd_netlink_add_match) { | |
400 | _cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *s1 = NULL, *s2 = NULL; | |
401 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
402 | ||
403 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
404 | ||
405 | ASSERT_OK(sd_netlink_add_match(rtnl, &s1, RTM_NEWLINK, link_handler, NULL, NULL, NULL)); | |
406 | ASSERT_OK(sd_netlink_add_match(rtnl, &s2, RTM_NEWLINK, link_handler, NULL, NULL, NULL)); | |
407 | ASSERT_OK(sd_netlink_add_match(rtnl, NULL, RTM_NEWLINK, link_handler, NULL, NULL, NULL)); | |
408 | ||
409 | ASSERT_NULL(s1 = sd_netlink_slot_unref(s1)); | |
410 | ASSERT_NULL(s2 = sd_netlink_slot_unref(s2)); | |
411 | ||
412 | ASSERT_NULL(rtnl = sd_netlink_unref(rtnl)); | |
413 | } | |
414 | ||
415 | TEST(dump_addresses) { | |
416 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
417 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; | |
418 | ||
419 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
420 | ||
421 | ASSERT_OK(sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC)); | |
422 | ASSERT_OK(sd_netlink_message_set_request_dump(req, true)); | |
423 | ASSERT_OK(sd_netlink_call(rtnl, req, 0, &reply)); | |
424 | ||
425 | for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) { | |
426 | uint16_t type; | |
427 | unsigned char scope; | |
428 | int family, ifindex; | |
429 | uint32_t flags; | |
430 | ||
431 | ASSERT_OK(sd_netlink_message_get_type(m, &type)); | |
432 | ASSERT_EQ(type, RTM_NEWADDR); | |
433 | ||
434 | ASSERT_OK(sd_rtnl_message_addr_get_ifindex(m, &ifindex)); | |
435 | ASSERT_OK(sd_rtnl_message_addr_get_family(m, &family)); | |
436 | ASSERT_OK(sd_rtnl_message_addr_get_scope(m, &scope)); | |
437 | ASSERT_OK(sd_netlink_message_read_u32(m, IFA_FLAGS, &flags)); | |
438 | ||
439 | ASSERT_GT(ifindex, 0); | |
440 | ASSERT_TRUE(IN_SET(family, AF_INET, AF_INET6)); | |
441 | ||
442 | log_info("got IPv%i address on ifindex %i", family == AF_INET ? 4 : 6, ifindex); | |
443 | } | |
444 | } | |
445 | ||
446 | TEST(sd_netlink_message_get_errno) { | |
447 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
448 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
449 | ||
450 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
451 | ||
452 | ASSERT_OK(message_new_synthetic_error(rtnl, -ETIMEDOUT, 1, &m)); | |
453 | ASSERT_ERROR(sd_netlink_message_get_errno(m), ETIMEDOUT); | |
454 | } | |
455 | ||
456 | TEST(message_array) { | |
457 | _cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL; | |
458 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
459 | ||
460 | ASSERT_OK(sd_genl_socket_open(&genl)); | |
461 | ASSERT_OK(sd_genl_message_new(genl, CTRL_GENL_NAME, CTRL_CMD_GETFAMILY, &m)); | |
462 | ||
463 | ASSERT_OK(sd_netlink_message_open_container(m, CTRL_ATTR_MCAST_GROUPS)); | |
464 | for (unsigned i = 0; i < 10; i++) { | |
465 | char name[STRLEN("hoge") + DECIMAL_STR_MAX(uint32_t)]; | |
466 | uint32_t id = i + 1000; | |
467 | ||
468 | xsprintf(name, "hoge%" PRIu32, id); | |
469 | ASSERT_OK(sd_netlink_message_open_array(m, i + 1)); | |
470 | ASSERT_OK(sd_netlink_message_append_u32(m, CTRL_ATTR_MCAST_GRP_ID, id)); | |
471 | ASSERT_OK(sd_netlink_message_append_string(m, CTRL_ATTR_MCAST_GRP_NAME, name)); | |
472 | ASSERT_OK(sd_netlink_message_close_container(m)); | |
473 | } | |
474 | ASSERT_OK(sd_netlink_message_close_container(m)); | |
475 | ||
476 | message_seal(m); | |
477 | ASSERT_OK(sd_netlink_message_rewind(m, genl)); | |
478 | ||
479 | ASSERT_OK(sd_netlink_message_enter_container(m, CTRL_ATTR_MCAST_GROUPS)); | |
480 | for (unsigned i = 0; i < 10; i++) { | |
481 | char expected[STRLEN("hoge") + DECIMAL_STR_MAX(uint32_t)]; | |
482 | const char *name; | |
483 | uint32_t id; | |
484 | ||
485 | ASSERT_OK(sd_netlink_message_enter_array(m, i + 1)); | |
486 | ASSERT_OK(sd_netlink_message_read_u32(m, CTRL_ATTR_MCAST_GRP_ID, &id)); | |
487 | ASSERT_OK(sd_netlink_message_read_string(m, CTRL_ATTR_MCAST_GRP_NAME, &name)); | |
488 | ASSERT_OK(sd_netlink_message_exit_container(m)); | |
489 | ||
490 | ASSERT_EQ(id, i + 1000); | |
491 | xsprintf(expected, "hoge%" PRIu32, id); | |
492 | ASSERT_STREQ(name, expected); | |
493 | } | |
494 | ASSERT_OK(sd_netlink_message_exit_container(m)); | |
495 | } | |
496 | ||
497 | TEST(message_strv) { | |
498 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
499 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
500 | _cleanup_strv_free_ char **names_in = NULL, **names_out; | |
501 | const char *p; | |
502 | ||
503 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
504 | ||
505 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINKPROP, 1)); | |
506 | ||
507 | for (unsigned i = 0; i < 10; i++) { | |
508 | char name[STRLEN("hoge") + DECIMAL_STR_MAX(uint32_t)]; | |
509 | ||
510 | xsprintf(name, "hoge%" PRIu32, i + 1000); | |
511 | ASSERT_OK(strv_extend(&names_in, name)); | |
512 | } | |
513 | ||
514 | ASSERT_OK(sd_netlink_message_open_container(m, IFLA_PROP_LIST)); | |
515 | ASSERT_OK(sd_netlink_message_append_strv(m, IFLA_ALT_IFNAME, (const char**) names_in)); | |
516 | ASSERT_OK(sd_netlink_message_close_container(m)); | |
517 | ||
518 | message_seal(m); | |
519 | ASSERT_OK(sd_netlink_message_rewind(m, rtnl)); | |
520 | ||
521 | ASSERT_OK(sd_netlink_message_read_strv(m, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &names_out)); | |
522 | ASSERT_TRUE(strv_equal(names_in, names_out)); | |
523 | ||
524 | ASSERT_OK(sd_netlink_message_enter_container(m, IFLA_PROP_LIST)); | |
525 | ASSERT_OK(sd_netlink_message_read_string(m, IFLA_ALT_IFNAME, &p)); | |
526 | ASSERT_STREQ(p, "hoge1009"); | |
527 | ASSERT_OK(sd_netlink_message_exit_container(m)); | |
528 | } | |
529 | ||
530 | static int genl_ctrl_match_callback(sd_netlink *genl, sd_netlink_message *m, void *userdata) { | |
531 | const char *name; | |
532 | uint16_t id; | |
533 | uint8_t cmd; | |
534 | ||
535 | ASSERT_NOT_NULL(genl); | |
536 | ASSERT_NOT_NULL(m); | |
537 | ||
538 | ASSERT_OK(sd_genl_message_get_family_name(genl, m, &name)); | |
539 | ASSERT_STREQ(name, CTRL_GENL_NAME); | |
540 | ||
541 | ASSERT_OK(sd_genl_message_get_command(genl, m, &cmd)); | |
542 | ||
543 | switch (cmd) { | |
544 | case CTRL_CMD_NEWFAMILY: | |
545 | case CTRL_CMD_DELFAMILY: | |
546 | ASSERT_OK(sd_netlink_message_read_string(m, CTRL_ATTR_FAMILY_NAME, &name)); | |
547 | ASSERT_OK(sd_netlink_message_read_u16(m, CTRL_ATTR_FAMILY_ID, &id)); | |
548 | log_debug("%s: %s (id=%"PRIu16") family is %s.", | |
549 | __func__, name, id, cmd == CTRL_CMD_NEWFAMILY ? "added" : "removed"); | |
550 | break; | |
551 | case CTRL_CMD_NEWMCAST_GRP: | |
552 | case CTRL_CMD_DELMCAST_GRP: | |
553 | ASSERT_OK(sd_netlink_message_read_string(m, CTRL_ATTR_FAMILY_NAME, &name)); | |
554 | ASSERT_OK(sd_netlink_message_read_u16(m, CTRL_ATTR_FAMILY_ID, &id)); | |
555 | log_debug("%s: multicast group for %s (id=%"PRIu16") family is %s.", | |
556 | __func__, name, id, cmd == CTRL_CMD_NEWMCAST_GRP ? "added" : "removed"); | |
557 | break; | |
558 | default: | |
559 | log_debug("%s: received nlctrl message with unknown command '%"PRIu8"'.", __func__, cmd); | |
560 | } | |
561 | ||
562 | return 0; | |
563 | } | |
564 | ||
565 | TEST(genl) { | |
566 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; | |
567 | _cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL; | |
568 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
569 | const char *name; | |
570 | uint8_t cmd; | |
571 | int r; | |
572 | ||
573 | ASSERT_OK(sd_genl_socket_open(&genl)); | |
574 | ASSERT_OK(sd_event_default(&event)); | |
575 | ASSERT_OK(sd_netlink_attach_event(genl, event, 0)); | |
576 | ||
577 | ASSERT_OK(sd_genl_message_new(genl, CTRL_GENL_NAME, CTRL_CMD_GETFAMILY, &m)); | |
578 | ASSERT_OK(sd_genl_message_get_family_name(genl, m, &name)); | |
579 | ASSERT_STREQ(name, CTRL_GENL_NAME); | |
580 | ASSERT_OK(sd_genl_message_get_command(genl, m, &cmd)); | |
581 | ASSERT_EQ(cmd, CTRL_CMD_GETFAMILY); | |
582 | ||
583 | ASSERT_OK(sd_genl_add_match(genl, NULL, CTRL_GENL_NAME, "notify", 0, genl_ctrl_match_callback, NULL, NULL, "genl-ctrl-notify")); | |
584 | ||
585 | ASSERT_NULL(m = sd_netlink_message_unref(m)); | |
586 | ASSERT_FAIL(sd_genl_message_new(genl, "should-not-exist", CTRL_CMD_GETFAMILY, &m)); | |
587 | ASSERT_ERROR(sd_genl_message_new(genl, "should-not-exist", CTRL_CMD_GETFAMILY, &m), EOPNOTSUPP); | |
588 | ||
589 | /* These families may not be supported by kernel. Hence, ignore results. */ | |
590 | (void) sd_genl_message_new(genl, FOU_GENL_NAME, 0, &m); | |
591 | ASSERT_NULL(m = sd_netlink_message_unref(m)); | |
592 | (void) sd_genl_message_new(genl, L2TP_GENL_NAME, 0, &m); | |
593 | ASSERT_NULL(m = sd_netlink_message_unref(m)); | |
594 | (void) sd_genl_message_new(genl, MACSEC_GENL_NAME, 0, &m); | |
595 | ASSERT_NULL(m = sd_netlink_message_unref(m)); | |
596 | (void) sd_genl_message_new(genl, NL80211_GENL_NAME, 0, &m); | |
597 | ASSERT_NULL(m = sd_netlink_message_unref(m)); | |
598 | (void) sd_genl_message_new(genl, NETLBL_NLTYPE_UNLABELED_NAME, 0, &m); | |
599 | ||
600 | for (;;) { | |
601 | ASSERT_OK(r = sd_event_run(event, 500 * USEC_PER_MSEC)); | |
602 | if (r == 0) | |
603 | return; | |
604 | } | |
605 | } | |
606 | ||
607 | static void remove_dummy_interfacep(int *ifindex) { | |
608 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
609 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; | |
610 | ||
611 | if (!ifindex || *ifindex <= 0) | |
612 | return; | |
613 | ||
614 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
615 | ||
616 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &message, RTM_DELLINK, *ifindex)); | |
617 | ASSERT_OK_EQ(sd_netlink_call(rtnl, message, 0, NULL), 1); | |
618 | } | |
619 | ||
620 | TEST(rtnl_set_link_name) { | |
621 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
622 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; | |
623 | _cleanup_(remove_dummy_interfacep) int ifindex = 0; | |
624 | _cleanup_strv_free_ char **alternative_names = NULL; | |
625 | int r; | |
626 | ||
627 | if (geteuid() != 0) | |
628 | return (void) log_tests_skipped("not root"); | |
629 | ||
630 | ASSERT_OK(sd_netlink_open(&rtnl)); | |
631 | ||
632 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &message, RTM_NEWLINK, 0)); | |
633 | ASSERT_OK(sd_netlink_message_append_string(message, IFLA_IFNAME, "test-netlink")); | |
634 | ASSERT_OK(sd_netlink_message_open_container(message, IFLA_LINKINFO)); | |
635 | ASSERT_OK(sd_netlink_message_append_string(message, IFLA_INFO_KIND, "dummy")); | |
636 | r = sd_netlink_call(rtnl, message, 0, &reply); | |
637 | if (r == -EPERM) | |
638 | return (void) log_tests_skipped("missing required capabilities"); | |
639 | if (r == -EOPNOTSUPP) | |
640 | return (void) log_tests_skipped("dummy network interface is not supported"); | |
641 | ASSERT_OK(r); | |
642 | ||
643 | ASSERT_NULL(message = sd_netlink_message_unref(message)); | |
644 | ASSERT_NULL(reply = sd_netlink_message_unref(reply)); | |
645 | ||
646 | ASSERT_OK(sd_rtnl_message_new_link(rtnl, &message, RTM_GETLINK, 0)); | |
647 | ASSERT_OK(sd_netlink_message_append_string(message, IFLA_IFNAME, "test-netlink")); | |
648 | ASSERT_OK_EQ(sd_netlink_call(rtnl, message, 0, &reply), 1); | |
649 | ||
650 | ASSERT_OK(sd_rtnl_message_link_get_ifindex(reply, &ifindex)); | |
651 | ASSERT_GT(ifindex, 0); | |
652 | ||
653 | /* Test that the new name (which is currently an alternative name) is | |
654 | * restored as an alternative name on error. Create an error by using | |
655 | * an invalid device name, namely one that exceeds IFNAMSIZ | |
656 | * (alternative names can exceed IFNAMSIZ, but not regular names). */ | |
657 | r = rtnl_set_link_alternative_names(&rtnl, ifindex, STRV_MAKE("testlongalternativename", "test-shortname")); | |
658 | if (r == -EPERM) | |
659 | return (void) log_tests_skipped("missing required capabilities"); | |
660 | if (r == -EOPNOTSUPP) | |
661 | return (void) log_tests_skipped("alternative name is not supported"); | |
662 | ASSERT_OK(r); | |
663 | ||
664 | ASSERT_OK(rtnl_get_link_alternative_names(&rtnl, ifindex, &alternative_names)); | |
665 | ASSERT_TRUE(strv_contains(alternative_names, "testlongalternativename")); | |
666 | ASSERT_TRUE(strv_contains(alternative_names, "test-shortname")); | |
667 | ||
668 | ASSERT_ERROR(rtnl_set_link_name(&rtnl, ifindex, "testlongalternativename", NULL), EINVAL); | |
669 | ASSERT_OK(rtnl_set_link_name(&rtnl, ifindex, "test-shortname", STRV_MAKE("testlongalternativename", "test-shortname", "test-additional-name"))); | |
670 | ||
671 | ASSERT_NULL(alternative_names = strv_free(alternative_names)); | |
672 | ASSERT_OK(rtnl_get_link_alternative_names(&rtnl, ifindex, &alternative_names)); | |
673 | ASSERT_TRUE(strv_contains(alternative_names, "testlongalternativename")); | |
674 | ASSERT_TRUE(strv_contains(alternative_names, "test-additional-name")); | |
675 | ASSERT_FALSE(strv_contains(alternative_names, "test-shortname")); | |
676 | ||
677 | ASSERT_OK(rtnl_delete_link_alternative_names(&rtnl, ifindex, STRV_MAKE("testlongalternativename"))); | |
678 | ||
679 | ASSERT_NULL(alternative_names = strv_free(alternative_names)); | |
680 | ASSERT_OK_EQ(rtnl_get_link_alternative_names(&rtnl, ifindex, &alternative_names), ifindex); | |
681 | ASSERT_FALSE(strv_contains(alternative_names, "testlongalternativename")); | |
682 | ASSERT_TRUE(strv_contains(alternative_names, "test-additional-name")); | |
683 | ASSERT_FALSE(strv_contains(alternative_names, "test-shortname")); | |
684 | ||
685 | _cleanup_free_ char *resolved = NULL; | |
686 | ASSERT_OK_EQ(rtnl_resolve_link_alternative_name(&rtnl, "test-additional-name", NULL), ifindex); | |
687 | ASSERT_OK_EQ(rtnl_resolve_link_alternative_name(&rtnl, "test-additional-name", &resolved), ifindex); | |
688 | ASSERT_STREQ(resolved, "test-shortname"); | |
689 | ASSERT_NULL(resolved = mfree(resolved)); | |
690 | ||
691 | ASSERT_OK(rtnl_rename_link(&rtnl, "test-shortname", "test-shortname")); | |
692 | ASSERT_OK(rtnl_rename_link(&rtnl, "test-shortname", "test-shortname2")); | |
693 | ASSERT_OK(rtnl_rename_link(NULL, "test-shortname2", "test-shortname3")); | |
694 | ||
695 | ASSERT_OK_EQ(rtnl_resolve_link_alternative_name(&rtnl, "test-additional-name", NULL), ifindex); | |
696 | ASSERT_OK_EQ(rtnl_resolve_link_alternative_name(&rtnl, "test-additional-name", &resolved), ifindex); | |
697 | ASSERT_STREQ(resolved, "test-shortname3"); | |
698 | ASSERT_NULL(resolved = mfree(resolved)); | |
699 | ||
700 | ASSERT_OK_EQ(rtnl_resolve_link_alternative_name(&rtnl, "test-shortname3", NULL), ifindex); | |
701 | ASSERT_OK_EQ(rtnl_resolve_link_alternative_name(&rtnl, "test-shortname3", &resolved), ifindex); | |
702 | ASSERT_STREQ(resolved, "test-shortname3"); | |
703 | ASSERT_NULL(resolved = mfree(resolved)); | |
704 | } | |
705 | ||
706 | TEST(sock_diag_unix) { | |
707 | _cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL; | |
708 | int r; | |
709 | ||
710 | ASSERT_OK(sd_sock_diag_socket_open(&nl)); | |
711 | ||
712 | _cleanup_close_ int unix_fd = ASSERT_FD(socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0)); | |
713 | ASSERT_OK(socket_autobind(unix_fd, /* ret_name= */ NULL)); | |
714 | ASSERT_OK_ERRNO(listen(unix_fd, 123)); | |
715 | ||
716 | struct stat st; | |
717 | ASSERT_OK_ERRNO(fstat(unix_fd, &st)); | |
718 | ||
719 | uint64_t cookie; | |
720 | ASSERT_OK(socket_get_cookie(unix_fd, &cookie)); | |
721 | ||
722 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; | |
723 | ASSERT_OK(sd_sock_diag_message_new_unix(nl, &message, st.st_ino, cookie, UDIAG_SHOW_RQLEN)); | |
724 | ||
725 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *reply = NULL; | |
726 | r = sd_netlink_call(nl, message, /* timeout= */ 0, &reply); | |
727 | if (r == -ENOENT) | |
728 | return (void) log_tests_skipped("CONFIG_UNIX_DIAG disabled"); | |
729 | ASSERT_OK(r); | |
730 | } | |
731 | ||
732 | DEFINE_TEST_MAIN(LOG_DEBUG); |