]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-netlink/netlink-util.c
Merge pull request #17185 from yuwata/ethtool-update
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / netlink-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "sd-netlink.h"
4
5 #include "format-util.h"
6 #include "memory-util.h"
7 #include "netlink-internal.h"
8 #include "netlink-util.h"
9 #include "strv.h"
10
11 int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
12 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
13 _cleanup_strv_free_ char **alternative_names = NULL;
14 char old_name[IF_NAMESIZE + 1] = {};
15 int r;
16
17 assert(rtnl);
18 assert(ifindex > 0);
19 assert(name);
20
21 if (!ifname_valid(name))
22 return -EINVAL;
23
24 r = rtnl_get_link_alternative_names(rtnl, ifindex, &alternative_names);
25 if (r < 0)
26 log_debug_errno(r, "Failed to get alternative names on network interface %i, ignoring: %m",
27 ifindex);
28
29 if (strv_contains(alternative_names, name)) {
30 r = rtnl_delete_link_alternative_names(rtnl, ifindex, STRV_MAKE(name));
31 if (r < 0)
32 return log_debug_errno(r, "Failed to remove '%s' from alternative names on network interface %i: %m",
33 name, ifindex);
34
35 format_ifname(ifindex, old_name);
36 }
37
38 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex);
39 if (r < 0)
40 return r;
41
42 r = sd_netlink_message_append_string(message, IFLA_IFNAME, name);
43 if (r < 0)
44 return r;
45
46 r = sd_netlink_call(*rtnl, message, 0, NULL);
47 if (r < 0)
48 return r;
49
50 if (!isempty(old_name)) {
51 r = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(old_name));
52 if (r < 0)
53 log_debug_errno(r, "Failed to set '%s' as an alternative name on network interface %i, ignoring: %m",
54 old_name, ifindex);
55 }
56
57 return 0;
58 }
59
60 int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias,
61 const struct ether_addr *mac, uint32_t mtu) {
62 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
63 int r;
64
65 assert(rtnl);
66 assert(ifindex > 0);
67
68 if (!alias && !mac && mtu == 0)
69 return 0;
70
71 if (!*rtnl) {
72 r = sd_netlink_open(rtnl);
73 if (r < 0)
74 return r;
75 }
76
77 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex);
78 if (r < 0)
79 return r;
80
81 if (alias) {
82 r = sd_netlink_message_append_string(message, IFLA_IFALIAS, alias);
83 if (r < 0)
84 return r;
85 }
86
87 if (mac) {
88 r = sd_netlink_message_append_ether_addr(message, IFLA_ADDRESS, mac);
89 if (r < 0)
90 return r;
91 }
92
93 if (mtu != 0) {
94 r = sd_netlink_message_append_u32(message, IFLA_MTU, mtu);
95 if (r < 0)
96 return r;
97 }
98
99 r = sd_netlink_call(*rtnl, message, 0, NULL);
100 if (r < 0)
101 return r;
102
103 return 0;
104 }
105
106 int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret) {
107 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
108 _cleanup_strv_free_ char **names = NULL;
109 int r;
110
111 assert(rtnl);
112 assert(ifindex > 0);
113 assert(ret);
114
115 if (!*rtnl) {
116 r = sd_netlink_open(rtnl);
117 if (r < 0)
118 return r;
119 }
120
121 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
122 if (r < 0)
123 return r;
124
125 r = sd_netlink_call(*rtnl, message, 0, &reply);
126 if (r < 0)
127 return r;
128
129 r = sd_netlink_message_read_strv(reply, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &names);
130 if (r < 0 && r != -ENODATA)
131 return r;
132
133 *ret = TAKE_PTR(names);
134
135 return 0;
136 }
137
138 static int rtnl_update_link_alternative_names(sd_netlink **rtnl, uint16_t nlmsg_type, int ifindex, char * const *alternative_names) {
139 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
140 int r;
141
142 assert(rtnl);
143 assert(ifindex > 0);
144 assert(IN_SET(nlmsg_type, RTM_NEWLINKPROP, RTM_DELLINKPROP));
145
146 if (strv_isempty(alternative_names))
147 return 0;
148
149 if (!*rtnl) {
150 r = sd_netlink_open(rtnl);
151 if (r < 0)
152 return r;
153 }
154
155 r = sd_rtnl_message_new_link(*rtnl, &message, nlmsg_type, ifindex);
156 if (r < 0)
157 return r;
158
159 r = sd_netlink_message_open_container(message, IFLA_PROP_LIST);
160 if (r < 0)
161 return r;
162
163 r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, alternative_names);
164 if (r < 0)
165 return r;
166
167 r = sd_netlink_message_close_container(message);
168 if (r < 0)
169 return r;
170
171 r = sd_netlink_call(*rtnl, message, 0, NULL);
172 if (r < 0)
173 return r;
174
175 return 0;
176 }
177
178 int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) {
179 return rtnl_update_link_alternative_names(rtnl, RTM_NEWLINKPROP, ifindex, alternative_names);
180 }
181
182 int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) {
183 return rtnl_update_link_alternative_names(rtnl, RTM_DELLINKPROP, ifindex, alternative_names);
184 }
185
186 int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names) {
187 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
188 int r;
189
190 assert(rtnl);
191 assert(ifname);
192
193 if (strv_isempty(alternative_names))
194 return 0;
195
196 if (!*rtnl) {
197 r = sd_netlink_open(rtnl);
198 if (r < 0)
199 return r;
200 }
201
202 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_NEWLINKPROP, 0);
203 if (r < 0)
204 return r;
205
206 r = sd_netlink_message_append_string(message, IFLA_IFNAME, ifname);
207 if (r < 0)
208 return r;
209
210 r = sd_netlink_message_open_container(message, IFLA_PROP_LIST);
211 if (r < 0)
212 return r;
213
214 r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, alternative_names);
215 if (r < 0)
216 return r;
217
218 r = sd_netlink_message_close_container(message);
219 if (r < 0)
220 return r;
221
222 r = sd_netlink_call(*rtnl, message, 0, NULL);
223 if (r < 0)
224 return r;
225
226 return 0;
227 }
228
229 int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name) {
230 _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
231 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
232 int r, ret;
233
234 assert(name);
235
236 if (!rtnl)
237 rtnl = &our_rtnl;
238 if (!*rtnl) {
239 r = sd_netlink_open(rtnl);
240 if (r < 0)
241 return r;
242 }
243
244 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
245 if (r < 0)
246 return r;
247
248 r = sd_netlink_message_append_string(message, IFLA_ALT_IFNAME, name);
249 if (r < 0)
250 return r;
251
252 r = sd_netlink_call(*rtnl, message, 0, &reply);
253 if (r == -EINVAL)
254 return -ENODEV; /* The device doesn't exist */
255 if (r < 0)
256 return r;
257
258 r = sd_rtnl_message_link_get_ifindex(reply, &ret);
259 if (r < 0)
260 return r;
261 assert(ret > 0);
262 return ret;
263 }
264
265 int rtnl_get_link_iftype(sd_netlink **rtnl, int ifindex, unsigned short *ret) {
266 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
267 int r;
268
269 if (!*rtnl) {
270 r = sd_netlink_open(rtnl);
271 if (r < 0)
272 return r;
273 }
274
275 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
276 if (r < 0)
277 return r;
278
279 r = sd_netlink_call(*rtnl, message, 0, &reply);
280 if (r == -EINVAL)
281 return -ENODEV; /* The device does not exist */
282 if (r < 0)
283 return r;
284
285 return sd_rtnl_message_link_get_type(reply, ret);
286 }
287
288 int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) {
289 struct nlmsgerr *err;
290 int r;
291
292 assert(error <= 0);
293
294 r = message_new(rtnl, ret, NLMSG_ERROR);
295 if (r < 0)
296 return r;
297
298 rtnl_message_seal(*ret);
299 (*ret)->hdr->nlmsg_seq = serial;
300
301 err = NLMSG_DATA((*ret)->hdr);
302 err->error = error;
303
304 return 0;
305 }
306
307 int rtnl_log_parse_error(int r) {
308 return log_error_errno(r, "Failed to parse netlink message: %m");
309 }
310
311 int rtnl_log_create_error(int r) {
312 return log_error_errno(r, "Failed to create netlink message: %m");
313 }
314
315 void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length) {
316 size_t padding_length;
317 uint8_t *padding;
318
319 assert(rta);
320 assert(!data || data_length > 0);
321
322 /* fill in the attribute */
323 rta->rta_type = type;
324 rta->rta_len = RTA_LENGTH(data_length);
325 if (data)
326 /* we don't deal with the case where the user lies about the type
327 * and gives us too little data (so don't do that)
328 */
329 padding = mempcpy(RTA_DATA(rta), data, data_length);
330
331 else
332 /* if no data was passed, make sure we still initialize the padding
333 note that we can have data_length > 0 (used by some containers) */
334 padding = RTA_DATA(rta);
335
336 /* make sure also the padding at the end of the message is initialized */
337 padding_length = (uint8_t *) rta + RTA_SPACE(data_length) - padding;
338 memzero(padding, padding_length);
339 }
340
341 int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length) {
342 struct rtattr *new_rta, *sub_rta;
343 size_t message_length;
344
345 assert(rta);
346 assert(!data || data_length > 0);
347
348 /* get the new message size (with padding at the end) */
349 message_length = RTA_ALIGN(rta ? (*rta)->rta_len : 0) + RTA_SPACE(data_length);
350
351 /* buffer should be smaller than both one page or 8K to be accepted by the kernel */
352 if (message_length > MIN(page_size(), 8192UL))
353 return -ENOBUFS;
354
355 /* realloc to fit the new attribute */
356 new_rta = realloc(*rta, message_length);
357 if (!new_rta)
358 return -ENOMEM;
359 *rta = new_rta;
360
361 /* get pointer to the attribute we are about to add */
362 sub_rta = (struct rtattr *) ((uint8_t *) *rta + RTA_ALIGN((*rta)->rta_len));
363
364 rtattr_append_attribute_internal(sub_rta, type, data, data_length);
365
366 /* update rta_len */
367 (*rta)->rta_len = message_length;
368
369 return 0;
370 }
371
372 int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, OrderedSet **ret) {
373 _cleanup_ordered_set_free_free_ OrderedSet *set = NULL;
374 int r;
375
376 assert(rtnh);
377 assert(IN_SET(family, AF_INET, AF_INET6));
378
379 if (size < sizeof(struct rtnexthop))
380 return -EBADMSG;
381
382 for (; size >= sizeof(struct rtnexthop); ) {
383 _cleanup_free_ MultipathRoute *m = NULL;
384
385 if (NLMSG_ALIGN(rtnh->rtnh_len) > size)
386 return -EBADMSG;
387
388 if (rtnh->rtnh_len < sizeof(struct rtnexthop))
389 return -EBADMSG;
390
391 m = new(MultipathRoute, 1);
392 if (!m)
393 return -ENOMEM;
394
395 *m = (MultipathRoute) {
396 .ifindex = rtnh->rtnh_ifindex,
397 .weight = rtnh->rtnh_hops == 0 ? 0 : rtnh->rtnh_hops + 1,
398 };
399
400 if (rtnh->rtnh_len > sizeof(struct rtnexthop)) {
401 size_t len = rtnh->rtnh_len - sizeof(struct rtnexthop);
402
403 for (struct rtattr *attr = RTNH_DATA(rtnh); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
404 if (attr->rta_type == RTA_GATEWAY) {
405 if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(family)))
406 return -EBADMSG;
407
408 m->gateway.family = family;
409 memcpy(&m->gateway.address, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(family));
410 break;
411 } else if (attr->rta_type == RTA_VIA) {
412 uint16_t gw_family;
413
414 if (family != AF_INET)
415 return -EINVAL;
416
417 if (attr->rta_len < RTA_LENGTH(sizeof(uint16_t)))
418 return -EBADMSG;
419
420 gw_family = *(uint16_t *) RTA_DATA(attr);
421
422 if (gw_family != AF_INET6)
423 return -EBADMSG;
424
425 if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family)))
426 return -EBADMSG;
427
428 memcpy(&m->gateway, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family));
429 break;
430 }
431 }
432 }
433
434 r = ordered_set_ensure_put(&set, NULL, m);
435 if (r < 0)
436 return r;
437
438 TAKE_PTR(m);
439
440 size -= NLMSG_ALIGN(rtnh->rtnh_len);
441 rtnh = RTNH_NEXT(rtnh);
442 }
443
444 if (ret)
445 *ret = TAKE_PTR(set);
446 return 0;
447 }