/src/systemd/src/libsystemd/sd-netlink/netlink-util.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | | |
3 | | #include "sd-netlink.h" |
4 | | |
5 | | #include "ether-addr-util.h" |
6 | | #include "fd-util.h" |
7 | | #include "hashmap.h" |
8 | | #include "iovec-util.h" |
9 | | #include "log.h" |
10 | | #include "memory-util.h" |
11 | | #include "netlink-internal.h" |
12 | | #include "netlink-util.h" |
13 | | #include "parse-util.h" |
14 | | #include "process-util.h" |
15 | | #include "socket-util.h" |
16 | | #include "string-util.h" |
17 | | #include "strv.h" |
18 | | |
19 | | static int parse_newlink_message( |
20 | | sd_netlink_message *message, |
21 | | char **ret_name, |
22 | 5.99k | char ***ret_altnames) { |
23 | | |
24 | 5.99k | _cleanup_strv_free_ char **altnames = NULL; |
25 | 5.99k | int r, ifindex; |
26 | | |
27 | 5.99k | assert(message); |
28 | | |
29 | 5.99k | uint16_t type; |
30 | 5.99k | r = sd_netlink_message_get_type(message, &type); |
31 | 5.99k | if (r < 0) |
32 | 0 | return r; |
33 | 5.99k | if (type != RTM_NEWLINK) |
34 | 0 | return -EPROTO; |
35 | | |
36 | 5.99k | r = sd_rtnl_message_link_get_ifindex(message, &ifindex); |
37 | 5.99k | if (r < 0) |
38 | 0 | return r; |
39 | 5.99k | if (ifindex <= 0) |
40 | 0 | return -EPROTO; |
41 | | |
42 | 5.99k | if (ret_altnames) { |
43 | 0 | r = sd_netlink_message_read_strv(message, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &altnames); |
44 | 0 | if (r < 0 && r != -ENODATA) |
45 | 0 | return r; |
46 | 0 | } |
47 | | |
48 | 5.99k | if (ret_name) { |
49 | 32 | r = sd_netlink_message_read_string_strdup(message, IFLA_IFNAME, ret_name); |
50 | 32 | if (r < 0) |
51 | 0 | return r; |
52 | 32 | } |
53 | | |
54 | 5.99k | if (ret_altnames) |
55 | 0 | *ret_altnames = TAKE_PTR(altnames); |
56 | | |
57 | 5.99k | return ifindex; |
58 | 5.99k | } |
59 | | |
60 | | int rtnl_resolve_ifname_full( |
61 | | sd_netlink **rtnl, |
62 | | ResolveInterfaceNameFlag flags, |
63 | | const char *name, |
64 | | char **ret_name, |
65 | 23.6k | char ***ret_altnames) { |
66 | | |
67 | 23.6k | _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL; |
68 | 23.6k | int r; |
69 | | |
70 | 23.6k | assert(name); |
71 | 23.6k | assert(flags > 0); |
72 | | |
73 | | /* This is similar to if_nametoindex(), but also resolves alternative names and decimal formatted |
74 | | * ifindex too. Returns ifindex, and optionally provides the main interface name and alternative |
75 | | * names. */ |
76 | | |
77 | 23.6k | if (!rtnl) |
78 | 23.6k | rtnl = &our_rtnl; |
79 | 23.6k | if (!*rtnl) { |
80 | 23.6k | r = sd_netlink_open(rtnl); |
81 | 23.6k | if (r < 0) |
82 | 0 | return r; |
83 | 23.6k | } |
84 | | |
85 | | /* First, use IFLA_IFNAME */ |
86 | 23.6k | if (FLAGS_SET(flags, RESOLVE_IFNAME_MAIN) && ifname_valid(name)) { |
87 | 17.6k | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; |
88 | | |
89 | 17.6k | r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0); |
90 | 17.6k | if (r < 0) |
91 | 0 | return r; |
92 | | |
93 | 17.6k | r = sd_netlink_message_append_string(message, IFLA_IFNAME, name); |
94 | 17.6k | if (r < 0) |
95 | 0 | return r; |
96 | | |
97 | 17.6k | r = sd_netlink_call(*rtnl, message, 0, &reply); |
98 | 17.6k | if (r >= 0) |
99 | 1.58k | return parse_newlink_message(reply, ret_name, ret_altnames); |
100 | 16.1k | if (r != -ENODEV) |
101 | 0 | return r; |
102 | 16.1k | } |
103 | | |
104 | | /* Next, try IFLA_ALT_IFNAME */ |
105 | 22.0k | if (FLAGS_SET(flags, RESOLVE_IFNAME_ALTERNATIVE) && |
106 | 22.0k | ifname_valid_full(name, IFNAME_VALID_ALTERNATIVE)) { |
107 | 16.8k | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; |
108 | | |
109 | 16.8k | r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0); |
110 | 16.8k | if (r < 0) |
111 | 0 | return r; |
112 | | |
113 | 16.8k | r = sd_netlink_message_append_string(message, IFLA_ALT_IFNAME, name); |
114 | 16.8k | if (r < 0) |
115 | 0 | return r; |
116 | | |
117 | 16.8k | r = sd_netlink_call(*rtnl, message, 0, &reply); |
118 | 16.8k | if (r >= 0) |
119 | 0 | return parse_newlink_message(reply, ret_name, ret_altnames); |
120 | 16.8k | if (r != -ENODEV) |
121 | 0 | return r; |
122 | 16.8k | } |
123 | | |
124 | | /* Finally, assume the string is a decimal formatted ifindex. */ |
125 | 22.0k | if (FLAGS_SET(flags, RESOLVE_IFNAME_NUMERIC)) { |
126 | 22.0k | int ifindex; |
127 | | |
128 | 22.0k | ifindex = parse_ifindex(name); |
129 | 22.0k | if (ifindex <= 0) |
130 | 16.8k | return -ENODEV; |
131 | | |
132 | 5.27k | return rtnl_get_ifname_full(rtnl, ifindex, ret_name, ret_altnames); |
133 | 22.0k | } |
134 | | |
135 | 0 | return -ENODEV; |
136 | 22.0k | } |
137 | | |
138 | 0 | int rtnl_resolve_interface_or_warn(sd_netlink **rtnl, const char *name) { |
139 | 0 | int r; |
140 | |
|
141 | 0 | assert(name); |
142 | |
|
143 | 0 | r = rtnl_resolve_interface(rtnl, name); |
144 | 0 | if (r < 0) |
145 | 0 | return log_error_errno(r, "Failed to resolve interface \"%s\": %m", name); |
146 | 0 | return r; |
147 | 0 | } |
148 | | |
149 | 0 | static int set_link_name(sd_netlink *rtnl, int ifindex, const char *name) { |
150 | 0 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; |
151 | 0 | int r; |
152 | |
|
153 | 0 | assert(rtnl); |
154 | 0 | assert(ifindex > 0); |
155 | 0 | assert(name); |
156 | | |
157 | | /* Assign the requested name. */ |
158 | |
|
159 | 0 | r = sd_rtnl_message_new_link(rtnl, &message, RTM_SETLINK, ifindex); |
160 | 0 | if (r < 0) |
161 | 0 | return r; |
162 | | |
163 | 0 | r = sd_netlink_message_append_string(message, IFLA_IFNAME, name); |
164 | 0 | if (r < 0) |
165 | 0 | return r; |
166 | | |
167 | 0 | return sd_netlink_call(rtnl, message, 0, NULL); |
168 | 0 | } |
169 | | |
170 | 0 | int rtnl_rename_link(sd_netlink **rtnl, const char *orig_name, const char *new_name) { |
171 | 0 | _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL; |
172 | 0 | int r, ifindex; |
173 | |
|
174 | 0 | assert(orig_name); |
175 | 0 | assert(new_name); |
176 | | |
177 | | /* This does not check alternative names. Callers must check the requested name is not used as an |
178 | | * alternative name. */ |
179 | |
|
180 | 0 | if (streq(orig_name, new_name)) |
181 | 0 | return 0; |
182 | | |
183 | 0 | if (!ifname_valid(new_name)) |
184 | 0 | return -EINVAL; |
185 | | |
186 | 0 | if (!rtnl) |
187 | 0 | rtnl = &our_rtnl; |
188 | 0 | if (!*rtnl) { |
189 | 0 | r = sd_netlink_open(rtnl); |
190 | 0 | if (r < 0) |
191 | 0 | return r; |
192 | 0 | } |
193 | | |
194 | 0 | ifindex = rtnl_resolve_ifname(rtnl, orig_name); |
195 | 0 | if (ifindex < 0) |
196 | 0 | return ifindex; |
197 | | |
198 | 0 | return set_link_name(*rtnl, ifindex, new_name); |
199 | 0 | } |
200 | | |
201 | 0 | int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const *alternative_names) { |
202 | 0 | _cleanup_strv_free_ char **original_altnames = NULL, **new_altnames = NULL; |
203 | 0 | bool altname_deleted = false; |
204 | 0 | int r; |
205 | |
|
206 | 0 | assert(ifindex > 0); |
207 | |
|
208 | 0 | if (isempty(name) && strv_isempty(alternative_names)) |
209 | 0 | return 0; |
210 | | |
211 | 0 | if (name && !ifname_valid(name)) |
212 | 0 | return -EINVAL; |
213 | | |
214 | 0 | _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL; |
215 | 0 | if (!rtnl) |
216 | 0 | rtnl = &our_rtnl; |
217 | | |
218 | | /* If the requested name is already assigned as an alternative name, then first drop it. */ |
219 | 0 | r = rtnl_get_link_alternative_names(rtnl, ifindex, &original_altnames); |
220 | 0 | if (r < 0) |
221 | 0 | log_debug_errno(r, "Failed to get alternative names on network interface %i, ignoring: %m", |
222 | 0 | ifindex); |
223 | |
|
224 | 0 | if (name) { |
225 | 0 | if (strv_contains(original_altnames, name)) { |
226 | 0 | r = rtnl_delete_link_alternative_names(rtnl, ifindex, STRV_MAKE(name)); |
227 | 0 | if (r < 0) |
228 | 0 | return log_debug_errno(r, "Failed to remove '%s' from alternative names on network interface %i: %m", |
229 | 0 | name, ifindex); |
230 | | |
231 | 0 | altname_deleted = true; |
232 | 0 | } |
233 | | |
234 | 0 | r = set_link_name(*rtnl, ifindex, name); |
235 | 0 | if (r < 0) |
236 | 0 | goto fail; |
237 | 0 | } |
238 | | |
239 | | /* Filter out already assigned names from requested alternative names. Also, dedup the request. */ |
240 | 0 | STRV_FOREACH(a, alternative_names) { |
241 | 0 | if (streq_ptr(name, *a)) |
242 | 0 | continue; |
243 | | |
244 | 0 | if (strv_contains(original_altnames, *a)) |
245 | 0 | continue; |
246 | | |
247 | 0 | if (strv_contains(new_altnames, *a)) |
248 | 0 | continue; |
249 | | |
250 | 0 | if (!ifname_valid_full(*a, IFNAME_VALID_ALTERNATIVE)) |
251 | 0 | continue; |
252 | | |
253 | 0 | r = strv_extend(&new_altnames, *a); |
254 | 0 | if (r < 0) |
255 | 0 | return r; |
256 | 0 | } |
257 | | |
258 | 0 | strv_sort(new_altnames); |
259 | | |
260 | | /* Finally, assign alternative names. */ |
261 | 0 | r = rtnl_set_link_alternative_names(rtnl, ifindex, new_altnames); |
262 | 0 | if (r == -EEXIST) /* Already assigned to another interface? */ |
263 | 0 | STRV_FOREACH(a, new_altnames) { |
264 | 0 | r = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(*a)); |
265 | 0 | if (r < 0) |
266 | 0 | log_debug_errno(r, "Failed to assign '%s' as an alternative name on network interface %i, ignoring: %m", |
267 | 0 | *a, ifindex); |
268 | 0 | } |
269 | 0 | else if (r < 0) |
270 | 0 | log_debug_errno(r, "Failed to assign alternative names on network interface %i, ignoring: %m", ifindex); |
271 | |
|
272 | 0 | return 0; |
273 | | |
274 | 0 | fail: |
275 | 0 | if (altname_deleted) { |
276 | 0 | int q = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(name)); |
277 | 0 | if (q < 0) |
278 | 0 | log_debug_errno(q, "Failed to restore '%s' as an alternative name on network interface %i, ignoring: %m", |
279 | 0 | name, ifindex); |
280 | 0 | } |
281 | |
|
282 | 0 | return r; |
283 | 0 | } |
284 | | |
285 | | int rtnl_set_link_properties( |
286 | | sd_netlink **rtnl, |
287 | | int ifindex, |
288 | | const char *alias, |
289 | | const struct hw_addr_data *hw_addr, |
290 | | uint32_t txqueues, |
291 | | uint32_t rxqueues, |
292 | | uint32_t txqueuelen, |
293 | | uint32_t mtu, |
294 | | uint32_t gso_max_size, |
295 | 0 | size_t gso_max_segments) { |
296 | |
|
297 | 0 | int r; |
298 | |
|
299 | 0 | assert(ifindex > 0); |
300 | |
|
301 | 0 | if (!alias && |
302 | 0 | (!hw_addr || hw_addr->length == 0) && |
303 | 0 | txqueues == 0 && |
304 | 0 | rxqueues == 0 && |
305 | 0 | txqueuelen == UINT32_MAX && |
306 | 0 | mtu == 0 && |
307 | 0 | gso_max_size == 0 && |
308 | 0 | gso_max_segments == 0) |
309 | 0 | return 0; |
310 | | |
311 | 0 | _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL; |
312 | 0 | if (!rtnl) |
313 | 0 | rtnl = &our_rtnl; |
314 | 0 | if (!*rtnl) { |
315 | 0 | r = sd_netlink_open(rtnl); |
316 | 0 | if (r < 0) |
317 | 0 | return r; |
318 | 0 | } |
319 | | |
320 | 0 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; |
321 | 0 | r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); |
322 | 0 | if (r < 0) |
323 | 0 | return r; |
324 | | |
325 | 0 | if (alias) { |
326 | 0 | r = sd_netlink_message_append_string(message, IFLA_IFALIAS, alias); |
327 | 0 | if (r < 0) |
328 | 0 | return r; |
329 | 0 | } |
330 | | |
331 | 0 | if (hw_addr && hw_addr->length > 0) { |
332 | 0 | r = netlink_message_append_hw_addr(message, IFLA_ADDRESS, hw_addr); |
333 | 0 | if (r < 0) |
334 | 0 | return r; |
335 | 0 | } |
336 | | |
337 | 0 | if (txqueues > 0) { |
338 | 0 | r = sd_netlink_message_append_u32(message, IFLA_NUM_TX_QUEUES, txqueues); |
339 | 0 | if (r < 0) |
340 | 0 | return r; |
341 | 0 | } |
342 | | |
343 | 0 | if (rxqueues > 0) { |
344 | 0 | r = sd_netlink_message_append_u32(message, IFLA_NUM_RX_QUEUES, rxqueues); |
345 | 0 | if (r < 0) |
346 | 0 | return r; |
347 | 0 | } |
348 | | |
349 | 0 | if (txqueuelen < UINT32_MAX) { |
350 | 0 | r = sd_netlink_message_append_u32(message, IFLA_TXQLEN, txqueuelen); |
351 | 0 | if (r < 0) |
352 | 0 | return r; |
353 | 0 | } |
354 | | |
355 | 0 | if (mtu != 0) { |
356 | 0 | r = sd_netlink_message_append_u32(message, IFLA_MTU, mtu); |
357 | 0 | if (r < 0) |
358 | 0 | return r; |
359 | 0 | } |
360 | | |
361 | 0 | if (gso_max_size > 0) { |
362 | 0 | r = sd_netlink_message_append_u32(message, IFLA_GSO_MAX_SIZE, gso_max_size); |
363 | 0 | if (r < 0) |
364 | 0 | return r; |
365 | 0 | } |
366 | | |
367 | 0 | if (gso_max_segments > 0) { |
368 | 0 | r = sd_netlink_message_append_u32(message, IFLA_GSO_MAX_SEGS, gso_max_segments); |
369 | 0 | if (r < 0) |
370 | 0 | return r; |
371 | 0 | } |
372 | | |
373 | 0 | r = sd_netlink_call(*rtnl, message, 0, NULL); |
374 | 0 | if (r < 0) |
375 | 0 | return r; |
376 | | |
377 | 0 | return 0; |
378 | 0 | } |
379 | | |
380 | | static int rtnl_update_link_alternative_names( |
381 | | sd_netlink **rtnl, |
382 | | uint16_t nlmsg_type, |
383 | | int ifindex, |
384 | 0 | char* const *alternative_names) { |
385 | |
|
386 | 0 | int r; |
387 | |
|
388 | 0 | assert(ifindex > 0); |
389 | 0 | assert(IN_SET(nlmsg_type, RTM_NEWLINKPROP, RTM_DELLINKPROP)); |
390 | |
|
391 | 0 | if (strv_isempty(alternative_names)) |
392 | 0 | return 0; |
393 | | |
394 | 0 | _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL; |
395 | 0 | if (!rtnl) |
396 | 0 | rtnl = &our_rtnl; |
397 | 0 | if (!*rtnl) { |
398 | 0 | r = sd_netlink_open(rtnl); |
399 | 0 | if (r < 0) |
400 | 0 | return r; |
401 | 0 | } |
402 | | |
403 | 0 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; |
404 | 0 | r = sd_rtnl_message_new_link(*rtnl, &message, nlmsg_type, ifindex); |
405 | 0 | if (r < 0) |
406 | 0 | return r; |
407 | | |
408 | 0 | r = sd_netlink_message_open_container(message, IFLA_PROP_LIST); |
409 | 0 | if (r < 0) |
410 | 0 | return r; |
411 | | |
412 | 0 | r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, (const char**) alternative_names); |
413 | 0 | if (r < 0) |
414 | 0 | return r; |
415 | | |
416 | 0 | r = sd_netlink_message_close_container(message); |
417 | 0 | if (r < 0) |
418 | 0 | return r; |
419 | | |
420 | 0 | r = sd_netlink_call(*rtnl, message, 0, NULL); |
421 | 0 | if (r < 0) |
422 | 0 | return r; |
423 | | |
424 | 0 | return 0; |
425 | 0 | } |
426 | | |
427 | 0 | int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) { |
428 | 0 | return rtnl_update_link_alternative_names(rtnl, RTM_NEWLINKPROP, ifindex, alternative_names); |
429 | 0 | } |
430 | | |
431 | 0 | int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) { |
432 | 0 | return rtnl_update_link_alternative_names(rtnl, RTM_DELLINKPROP, ifindex, alternative_names); |
433 | 0 | } |
434 | | |
435 | | int rtnl_set_link_alternative_names_by_ifname( |
436 | | sd_netlink **rtnl, |
437 | | const char *ifname, |
438 | 0 | char* const *alternative_names) { |
439 | |
|
440 | 0 | int r; |
441 | |
|
442 | 0 | assert(rtnl); |
443 | 0 | assert(ifname); |
444 | |
|
445 | 0 | if (strv_isempty(alternative_names)) |
446 | 0 | return 0; |
447 | | |
448 | 0 | _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL; |
449 | 0 | if (!rtnl) |
450 | 0 | rtnl = &our_rtnl; |
451 | 0 | if (!*rtnl) { |
452 | 0 | r = sd_netlink_open(rtnl); |
453 | 0 | if (r < 0) |
454 | 0 | return r; |
455 | 0 | } |
456 | | |
457 | 0 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; |
458 | 0 | r = sd_rtnl_message_new_link(*rtnl, &message, RTM_NEWLINKPROP, 0); |
459 | 0 | if (r < 0) |
460 | 0 | return r; |
461 | | |
462 | 0 | r = sd_netlink_message_append_string(message, IFLA_IFNAME, ifname); |
463 | 0 | if (r < 0) |
464 | 0 | return r; |
465 | | |
466 | 0 | r = sd_netlink_message_open_container(message, IFLA_PROP_LIST); |
467 | 0 | if (r < 0) |
468 | 0 | return r; |
469 | | |
470 | 0 | r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, (const char**) alternative_names); |
471 | 0 | if (r < 0) |
472 | 0 | return r; |
473 | | |
474 | 0 | r = sd_netlink_message_close_container(message); |
475 | 0 | if (r < 0) |
476 | 0 | return r; |
477 | | |
478 | 0 | r = sd_netlink_call(*rtnl, message, 0, NULL); |
479 | 0 | if (r < 0) |
480 | 0 | return r; |
481 | | |
482 | 0 | return 0; |
483 | 0 | } |
484 | | |
485 | | int rtnl_get_link_info_full( |
486 | | sd_netlink **rtnl, |
487 | | int ifindex, |
488 | | char **ret_name, |
489 | | char ***ret_altnames, |
490 | | unsigned short *ret_iftype, |
491 | | unsigned *ret_flags, |
492 | | char **ret_kind, |
493 | | struct hw_addr_data *ret_hw_addr, |
494 | 5.35k | struct hw_addr_data *ret_permanent_hw_addr) { |
495 | | |
496 | 5.35k | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; |
497 | 5.35k | _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL; |
498 | 5.35k | struct hw_addr_data addr = HW_ADDR_NULL, perm_addr = HW_ADDR_NULL; |
499 | 5.35k | _cleanup_free_ char *name = NULL, *kind = NULL; |
500 | 5.35k | _cleanup_strv_free_ char **altnames = NULL; |
501 | 5.35k | unsigned short iftype; |
502 | 5.35k | unsigned flags; |
503 | 5.35k | int r; |
504 | | |
505 | 5.35k | assert(ifindex > 0); |
506 | | |
507 | 5.35k | if (!rtnl) |
508 | 87 | rtnl = &our_rtnl; |
509 | 5.35k | if (!*rtnl) { |
510 | 87 | r = sd_netlink_open(rtnl); |
511 | 87 | if (r < 0) |
512 | 0 | return r; |
513 | 87 | } |
514 | | |
515 | 5.35k | r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex); |
516 | 5.35k | if (r < 0) |
517 | 0 | return r; |
518 | | |
519 | 5.35k | r = sd_netlink_call(*rtnl, message, 0, &reply); |
520 | 5.35k | if (r == -EINVAL) |
521 | 0 | return -ENODEV; /* The device does not exist */ |
522 | 5.35k | if (r < 0) |
523 | 943 | return r; |
524 | | |
525 | 4.41k | r = parse_newlink_message(reply, ret_name ? &name : NULL, ret_altnames ? &altnames : NULL); |
526 | 4.41k | if (r < 0) |
527 | 0 | return r; |
528 | 4.41k | if (r != ifindex) |
529 | 0 | return -EIO; |
530 | | |
531 | 4.41k | if (ret_iftype) { |
532 | 0 | r = sd_rtnl_message_link_get_type(reply, &iftype); |
533 | 0 | if (r < 0) |
534 | 0 | return r; |
535 | 0 | } |
536 | | |
537 | 4.41k | if (ret_flags) { |
538 | 0 | r = sd_rtnl_message_link_get_flags(reply, &flags); |
539 | 0 | if (r < 0) |
540 | 0 | return r; |
541 | 0 | } |
542 | | |
543 | 4.41k | if (ret_kind) { |
544 | 0 | r = sd_netlink_message_enter_container(reply, IFLA_LINKINFO); |
545 | 0 | if (r >= 0) { |
546 | 0 | r = sd_netlink_message_read_string_strdup(reply, IFLA_INFO_KIND, &kind); |
547 | 0 | if (r < 0 && r != -ENODATA) |
548 | 0 | return r; |
549 | | |
550 | 0 | r = sd_netlink_message_exit_container(reply); |
551 | 0 | if (r < 0) |
552 | 0 | return r; |
553 | 0 | } |
554 | 0 | } |
555 | | |
556 | 4.41k | if (ret_hw_addr) { |
557 | 0 | r = netlink_message_read_hw_addr(reply, IFLA_ADDRESS, &addr); |
558 | 0 | if (r < 0 && r != -ENODATA) |
559 | 0 | return r; |
560 | 0 | } |
561 | | |
562 | 4.41k | if (ret_permanent_hw_addr) { |
563 | 0 | r = netlink_message_read_hw_addr(reply, IFLA_PERM_ADDRESS, &perm_addr); |
564 | 0 | if (r < 0 && r != -ENODATA) |
565 | 0 | return r; |
566 | 0 | } |
567 | | |
568 | 4.41k | if (ret_name) |
569 | 32 | *ret_name = TAKE_PTR(name); |
570 | 4.41k | if (ret_altnames) |
571 | 0 | *ret_altnames = TAKE_PTR(altnames); |
572 | 4.41k | if (ret_iftype) |
573 | 0 | *ret_iftype = iftype; |
574 | 4.41k | if (ret_flags) |
575 | 0 | *ret_flags = flags; |
576 | 4.41k | if (ret_kind) |
577 | 0 | *ret_kind = TAKE_PTR(kind); |
578 | 4.41k | if (ret_hw_addr) |
579 | 0 | *ret_hw_addr = addr; |
580 | 4.41k | if (ret_permanent_hw_addr) |
581 | 0 | *ret_permanent_hw_addr = perm_addr; |
582 | 4.41k | return ifindex; |
583 | 4.41k | } |
584 | | |
585 | 0 | int rtnl_log_parse_error(int r) { |
586 | 0 | return log_error_errno(r, "Failed to parse netlink message: %m"); |
587 | 0 | } |
588 | | |
589 | 0 | int rtnl_log_create_error(int r) { |
590 | 0 | return log_error_errno(r, "Failed to create netlink message: %m"); |
591 | 0 | } |
592 | | |
593 | 34.5k | void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length) { |
594 | 34.5k | size_t padding_length; |
595 | 34.5k | uint8_t *padding; |
596 | | |
597 | 34.5k | assert(rta); |
598 | 34.5k | assert(!data || data_length > 0); |
599 | | |
600 | | /* fill in the attribute */ |
601 | 34.5k | rta->rta_type = type; |
602 | 34.5k | rta->rta_len = RTA_LENGTH(data_length); |
603 | 34.5k | if (data) |
604 | | /* we don't deal with the case where the user lies about the type |
605 | | * and gives us too little data (so don't do that) |
606 | | */ |
607 | 34.5k | padding = mempcpy(RTA_DATA(rta), data, data_length); |
608 | | |
609 | 0 | else |
610 | | /* if no data was passed, make sure we still initialize the padding |
611 | | note that we can have data_length > 0 (used by some containers) */ |
612 | 0 | padding = RTA_DATA(rta); |
613 | | |
614 | | /* make sure also the padding at the end of the message is initialized */ |
615 | 34.5k | padding_length = (uint8_t *) rta + RTA_SPACE(data_length) - padding; |
616 | 34.5k | memzero(padding, padding_length); |
617 | 34.5k | } |
618 | | |
619 | 0 | int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length) { |
620 | 0 | struct rtattr *new_rta, *sub_rta; |
621 | 0 | size_t message_length; |
622 | |
|
623 | 0 | assert(rta); |
624 | 0 | assert(!data || data_length > 0); |
625 | | |
626 | | /* get the new message size (with padding at the end) */ |
627 | 0 | message_length = RTA_ALIGN(rta ? (*rta)->rta_len : 0) + RTA_SPACE(data_length); |
628 | | |
629 | | /* buffer should be smaller than both one page or 8K to be accepted by the kernel */ |
630 | 0 | if (message_length > MIN(page_size(), 8192UL)) |
631 | 0 | return -ENOBUFS; |
632 | | |
633 | | /* realloc to fit the new attribute */ |
634 | 0 | new_rta = realloc(*rta, message_length); |
635 | 0 | if (!new_rta) |
636 | 0 | return -ENOMEM; |
637 | 0 | *rta = new_rta; |
638 | | |
639 | | /* get pointer to the attribute we are about to add */ |
640 | 0 | sub_rta = (struct rtattr *) ((uint8_t *) *rta + RTA_ALIGN((*rta)->rta_len)); |
641 | |
|
642 | 0 | rtattr_append_attribute_internal(sub_rta, type, data, data_length); |
643 | | |
644 | | /* update rta_len */ |
645 | 0 | (*rta)->rta_len = message_length; |
646 | |
|
647 | 0 | return 0; |
648 | 0 | } |
649 | | |
650 | 159k | bool netlink_pid_changed(sd_netlink *nl) { |
651 | | /* We don't support people creating an nl connection and |
652 | | * keeping it around over a fork(). Let's complain. */ |
653 | 159k | return ASSERT_PTR(nl)->original_pid != getpid_cached(); |
654 | 159k | } |
655 | | |
656 | 23.7k | static int socket_open(int family) { |
657 | 23.7k | int fd; |
658 | | |
659 | 23.7k | fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family); |
660 | 23.7k | if (fd < 0) |
661 | 0 | return -errno; |
662 | | |
663 | 23.7k | return fd_move_above_stdio(fd); |
664 | 23.7k | } |
665 | | |
666 | 23.7k | int netlink_open_family(sd_netlink **ret, int family) { |
667 | 23.7k | _cleanup_close_ int fd = -EBADF; |
668 | 23.7k | int r; |
669 | | |
670 | 23.7k | fd = socket_open(family); |
671 | 23.7k | if (fd < 0) |
672 | 0 | return fd; |
673 | | |
674 | 23.7k | r = sd_netlink_open_fd(ret, fd); |
675 | 23.7k | if (r < 0) |
676 | 0 | return r; |
677 | 23.7k | TAKE_FD(fd); |
678 | | |
679 | 23.7k | return 0; |
680 | 23.7k | } |
681 | | |
682 | 39.8k | static bool serial_used(sd_netlink *nl, uint32_t serial) { |
683 | 39.8k | assert(nl); |
684 | | |
685 | 39.8k | return |
686 | 39.8k | hashmap_contains(nl->reply_callbacks, UINT32_TO_PTR(serial)) || |
687 | 39.8k | hashmap_contains(nl->rqueue_by_serial, UINT32_TO_PTR(serial)) || |
688 | 39.8k | hashmap_contains(nl->rqueue_partial_by_serial, UINT32_TO_PTR(serial)); |
689 | 39.8k | } |
690 | | |
691 | 39.8k | void netlink_seal_message(sd_netlink *nl, sd_netlink_message *m) { |
692 | 39.8k | uint32_t picked; |
693 | | |
694 | 39.8k | assert(nl); |
695 | 39.8k | assert(!netlink_pid_changed(nl)); |
696 | 39.8k | assert(m); |
697 | 39.8k | assert(m->hdr); |
698 | | |
699 | | /* Avoid collisions with outstanding requests */ |
700 | 39.8k | do { |
701 | 39.8k | picked = nl->serial; |
702 | | |
703 | | /* Don't use seq == 0, as that is used for broadcasts, so we would get confused by replies to |
704 | | such messages */ |
705 | 39.8k | nl->serial = nl->serial == UINT32_MAX ? 1 : nl->serial + 1; |
706 | | |
707 | 39.8k | } while (serial_used(nl, picked)); |
708 | | |
709 | 39.8k | m->hdr->nlmsg_seq = picked; |
710 | 39.8k | message_seal(m); |
711 | 39.8k | } |
712 | | |
713 | 0 | static int socket_writev_message(sd_netlink *nl, sd_netlink_message **m, size_t msgcount) { |
714 | 0 | _cleanup_free_ struct iovec *iovs = NULL; |
715 | 0 | ssize_t k; |
716 | |
|
717 | 0 | assert(nl); |
718 | 0 | assert(m); |
719 | 0 | assert(msgcount > 0); |
720 | |
|
721 | 0 | iovs = new(struct iovec, msgcount); |
722 | 0 | if (!iovs) |
723 | 0 | return -ENOMEM; |
724 | | |
725 | 0 | for (size_t i = 0; i < msgcount; i++) { |
726 | 0 | assert(m[i]->hdr); |
727 | 0 | assert(m[i]->hdr->nlmsg_len > 0); |
728 | |
|
729 | 0 | iovs[i] = IOVEC_MAKE(m[i]->hdr, m[i]->hdr->nlmsg_len); |
730 | 0 | } |
731 | |
|
732 | 0 | k = writev(nl->fd, iovs, msgcount); |
733 | 0 | if (k < 0) |
734 | 0 | return -errno; |
735 | | |
736 | 0 | return k; |
737 | 0 | } |
738 | | |
739 | | int sd_netlink_sendv( |
740 | | sd_netlink *nl, |
741 | | sd_netlink_message **messages, |
742 | | size_t msgcount, |
743 | 0 | uint32_t **ret_serial) { |
744 | |
|
745 | 0 | _cleanup_free_ uint32_t *serials = NULL; |
746 | 0 | int r; |
747 | |
|
748 | 0 | assert_return(nl, -EINVAL); |
749 | 0 | assert_return(!netlink_pid_changed(nl), -ECHILD); |
750 | 0 | assert_return(messages, -EINVAL); |
751 | 0 | assert_return(msgcount > 0, -EINVAL); |
752 | | |
753 | 0 | if (ret_serial) { |
754 | 0 | serials = new(uint32_t, msgcount); |
755 | 0 | if (!serials) |
756 | 0 | return -ENOMEM; |
757 | 0 | } |
758 | | |
759 | 0 | for (size_t i = 0; i < msgcount; i++) { |
760 | 0 | assert_return(!messages[i]->sealed, -EPERM); |
761 | | |
762 | 0 | netlink_seal_message(nl, messages[i]); |
763 | 0 | if (serials) |
764 | 0 | serials[i] = message_get_serial(messages[i]); |
765 | 0 | } |
766 | | |
767 | 0 | r = socket_writev_message(nl, messages, msgcount); |
768 | 0 | if (r < 0) |
769 | 0 | return r; |
770 | | |
771 | 0 | if (ret_serial) |
772 | 0 | *ret_serial = TAKE_PTR(serials); |
773 | |
|
774 | 0 | return r; |
775 | 0 | } |