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