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