]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
d8921c6d | 2 | |
1c4baffc | 3 | #include "sd-netlink.h" |
d8921c6d | 4 | |
434a3483 | 5 | #include "format-util.h" |
6497a8aa | 6 | #include "memory-util.h" |
1c4baffc | 7 | #include "netlink-internal.h" |
cf0fbc49 | 8 | #include "netlink-util.h" |
f6e49154 | 9 | #include "parse-util.h" |
a5053a15 | 10 | #include "strv.h" |
d8921c6d | 11 | |
1c4baffc | 12 | int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { |
4afd3348 | 13 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; |
434a3483 YW |
14 | _cleanup_strv_free_ char **alternative_names = NULL; |
15 | char old_name[IF_NAMESIZE + 1] = {}; | |
d8921c6d TG |
16 | int r; |
17 | ||
18 | assert(rtnl); | |
19 | assert(ifindex > 0); | |
3e137a1b | 20 | assert(name); |
d8921c6d | 21 | |
4d643099 YW |
22 | if (!ifname_valid(name)) |
23 | return -EINVAL; | |
24 | ||
434a3483 YW |
25 | r = rtnl_get_link_alternative_names(rtnl, ifindex, &alternative_names); |
26 | if (r < 0) | |
27 | log_debug_errno(r, "Failed to get alternative names on network interface %i, ignoring: %m", | |
28 | ifindex); | |
29 | ||
30 | if (strv_contains(alternative_names, name)) { | |
31 | r = rtnl_delete_link_alternative_names(rtnl, ifindex, STRV_MAKE(name)); | |
4c83d994 | 32 | if (r < 0) |
434a3483 YW |
33 | return log_debug_errno(r, "Failed to remove '%s' from alternative names on network interface %i: %m", |
34 | name, ifindex); | |
35 | ||
36 | format_ifname(ifindex, old_name); | |
4c83d994 TG |
37 | } |
38 | ||
39 | r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); | |
d8921c6d TG |
40 | if (r < 0) |
41 | return r; | |
42 | ||
1c4baffc | 43 | r = sd_netlink_message_append_string(message, IFLA_IFNAME, name); |
3e137a1b TG |
44 | if (r < 0) |
45 | return r; | |
d8921c6d | 46 | |
1c4baffc | 47 | r = sd_netlink_call(*rtnl, message, 0, NULL); |
3e137a1b TG |
48 | if (r < 0) |
49 | return r; | |
50 | ||
434a3483 YW |
51 | if (!isempty(old_name)) { |
52 | r = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(old_name)); | |
53 | if (r < 0) | |
54 | log_debug_errno(r, "Failed to set '%s' as an alternative name on network interface %i, ignoring: %m", | |
55 | old_name, ifindex); | |
56 | } | |
57 | ||
3e137a1b TG |
58 | return 0; |
59 | } | |
60 | ||
face9fcc YW |
61 | int rtnl_set_link_properties( |
62 | sd_netlink **rtnl, | |
63 | int ifindex, | |
64 | const char *alias, | |
65 | const struct ether_addr *mac, | |
66 | uint32_t txqueues, | |
67 | uint32_t rxqueues, | |
68 | uint32_t txqueuelen, | |
69 | uint32_t mtu, | |
70 | uint32_t gso_max_size, | |
71 | size_t gso_max_segments) { | |
4afd3348 | 72 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; |
3e137a1b TG |
73 | int r; |
74 | ||
75 | assert(rtnl); | |
76 | assert(ifindex > 0); | |
77 | ||
face9fcc YW |
78 | if (!alias && !mac && txqueues == 0 && rxqueues == 0 && txqueuelen == UINT32_MAX && mtu == 0 && |
79 | gso_max_size == 0 && gso_max_segments == 0) | |
3e137a1b TG |
80 | return 0; |
81 | ||
aedca892 | 82 | if (!*rtnl) { |
1c4baffc | 83 | r = sd_netlink_open(rtnl); |
aedca892 TG |
84 | if (r < 0) |
85 | return r; | |
86 | } | |
87 | ||
88 | r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); | |
3e137a1b TG |
89 | if (r < 0) |
90 | return r; | |
d8921c6d | 91 | |
d2df0d0e | 92 | if (alias) { |
1c4baffc | 93 | r = sd_netlink_message_append_string(message, IFLA_IFALIAS, alias); |
d2df0d0e TG |
94 | if (r < 0) |
95 | return r; | |
d2df0d0e TG |
96 | } |
97 | ||
d8921c6d | 98 | if (mac) { |
1c4baffc | 99 | r = sd_netlink_message_append_ether_addr(message, IFLA_ADDRESS, mac); |
d8921c6d TG |
100 | if (r < 0) |
101 | return r; | |
d8921c6d TG |
102 | } |
103 | ||
face9fcc YW |
104 | if (txqueues > 0) { |
105 | r = sd_netlink_message_append_u32(message, IFLA_NUM_TX_QUEUES, txqueues); | |
106 | if (r < 0) | |
107 | return r; | |
108 | } | |
109 | ||
110 | if (rxqueues > 0) { | |
111 | r = sd_netlink_message_append_u32(message, IFLA_NUM_RX_QUEUES, rxqueues); | |
112 | if (r < 0) | |
113 | return r; | |
114 | } | |
115 | ||
ef4a91a7 UÖ |
116 | if (txqueuelen < UINT32_MAX) { |
117 | r = sd_netlink_message_append_u32(message, IFLA_TXQLEN, txqueuelen); | |
118 | if (r < 0) | |
119 | return r; | |
120 | } | |
121 | ||
4e964aa0 | 122 | if (mtu != 0) { |
1c4baffc | 123 | r = sd_netlink_message_append_u32(message, IFLA_MTU, mtu); |
d8921c6d TG |
124 | if (r < 0) |
125 | return r; | |
d8921c6d TG |
126 | } |
127 | ||
c97cad60 SS |
128 | if (gso_max_size > 0) { |
129 | r = sd_netlink_message_append_u32(message, IFLA_GSO_MAX_SIZE, gso_max_size); | |
130 | if (r < 0) | |
131 | return r; | |
132 | } | |
133 | ||
134 | if (gso_max_segments > 0) { | |
135 | r = sd_netlink_message_append_u32(message, IFLA_GSO_MAX_SEGS, gso_max_segments); | |
136 | if (r < 0) | |
137 | return r; | |
138 | } | |
139 | ||
1c4baffc | 140 | r = sd_netlink_call(*rtnl, message, 0, NULL); |
aedca892 TG |
141 | if (r < 0) |
142 | return r; | |
d8921c6d TG |
143 | |
144 | return 0; | |
145 | } | |
3815f36f | 146 | |
14982526 YW |
147 | int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret) { |
148 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; | |
149 | _cleanup_strv_free_ char **names = NULL; | |
150 | int r; | |
151 | ||
152 | assert(rtnl); | |
153 | assert(ifindex > 0); | |
154 | assert(ret); | |
155 | ||
156 | if (!*rtnl) { | |
157 | r = sd_netlink_open(rtnl); | |
158 | if (r < 0) | |
159 | return r; | |
160 | } | |
161 | ||
162 | r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex); | |
163 | if (r < 0) | |
164 | return r; | |
165 | ||
166 | r = sd_netlink_call(*rtnl, message, 0, &reply); | |
167 | if (r < 0) | |
168 | return r; | |
169 | ||
170 | r = sd_netlink_message_read_strv(reply, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &names); | |
171 | if (r < 0 && r != -ENODATA) | |
172 | return r; | |
173 | ||
174 | *ret = TAKE_PTR(names); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | static int rtnl_update_link_alternative_names(sd_netlink **rtnl, uint16_t nlmsg_type, int ifindex, char * const *alternative_names) { | |
a5053a15 YW |
180 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; |
181 | int r; | |
182 | ||
183 | assert(rtnl); | |
184 | assert(ifindex > 0); | |
14982526 | 185 | assert(IN_SET(nlmsg_type, RTM_NEWLINKPROP, RTM_DELLINKPROP)); |
a5053a15 YW |
186 | |
187 | if (strv_isempty(alternative_names)) | |
188 | return 0; | |
189 | ||
190 | if (!*rtnl) { | |
191 | r = sd_netlink_open(rtnl); | |
192 | if (r < 0) | |
193 | return r; | |
194 | } | |
195 | ||
14982526 | 196 | r = sd_rtnl_message_new_link(*rtnl, &message, nlmsg_type, ifindex); |
a5053a15 YW |
197 | if (r < 0) |
198 | return r; | |
199 | ||
200 | r = sd_netlink_message_open_container(message, IFLA_PROP_LIST); | |
201 | if (r < 0) | |
202 | return r; | |
203 | ||
204 | r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, alternative_names); | |
205 | if (r < 0) | |
206 | return r; | |
207 | ||
208 | r = sd_netlink_message_close_container(message); | |
209 | if (r < 0) | |
210 | return r; | |
211 | ||
212 | r = sd_netlink_call(*rtnl, message, 0, NULL); | |
213 | if (r < 0) | |
214 | return r; | |
215 | ||
216 | return 0; | |
217 | } | |
218 | ||
14982526 YW |
219 | int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) { |
220 | return rtnl_update_link_alternative_names(rtnl, RTM_NEWLINKPROP, ifindex, alternative_names); | |
221 | } | |
222 | ||
223 | int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) { | |
224 | return rtnl_update_link_alternative_names(rtnl, RTM_DELLINKPROP, ifindex, alternative_names); | |
225 | } | |
226 | ||
6b50cb5c YW |
227 | int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names) { |
228 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; | |
229 | int r; | |
230 | ||
231 | assert(rtnl); | |
232 | assert(ifname); | |
233 | ||
234 | if (strv_isempty(alternative_names)) | |
235 | return 0; | |
236 | ||
237 | if (!*rtnl) { | |
238 | r = sd_netlink_open(rtnl); | |
239 | if (r < 0) | |
240 | return r; | |
241 | } | |
242 | ||
243 | r = sd_rtnl_message_new_link(*rtnl, &message, RTM_NEWLINKPROP, 0); | |
244 | if (r < 0) | |
245 | return r; | |
246 | ||
247 | r = sd_netlink_message_append_string(message, IFLA_IFNAME, ifname); | |
248 | if (r < 0) | |
249 | return r; | |
250 | ||
251 | r = sd_netlink_message_open_container(message, IFLA_PROP_LIST); | |
252 | if (r < 0) | |
253 | return r; | |
254 | ||
255 | r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, alternative_names); | |
256 | if (r < 0) | |
257 | return r; | |
258 | ||
259 | r = sd_netlink_message_close_container(message); | |
260 | if (r < 0) | |
261 | return r; | |
262 | ||
263 | r = sd_netlink_call(*rtnl, message, 0, NULL); | |
264 | if (r < 0) | |
265 | return r; | |
266 | ||
267 | return 0; | |
268 | } | |
269 | ||
231d9de1 | 270 | int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name) { |
bad7cecc | 271 | _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL; |
b04c5e51 | 272 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; |
231d9de1 | 273 | int r, ret; |
b04c5e51 | 274 | |
b04c5e51 | 275 | assert(name); |
b04c5e51 | 276 | |
bad7cecc ZJS |
277 | if (!rtnl) |
278 | rtnl = &our_rtnl; | |
b04c5e51 YW |
279 | if (!*rtnl) { |
280 | r = sd_netlink_open(rtnl); | |
281 | if (r < 0) | |
282 | return r; | |
283 | } | |
284 | ||
285 | r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0); | |
286 | if (r < 0) | |
287 | return r; | |
288 | ||
289 | r = sd_netlink_message_append_string(message, IFLA_ALT_IFNAME, name); | |
290 | if (r < 0) | |
291 | return r; | |
292 | ||
293 | r = sd_netlink_call(*rtnl, message, 0, &reply); | |
d308bb99 ZJS |
294 | if (r == -EINVAL) |
295 | return -ENODEV; /* The device doesn't exist */ | |
b04c5e51 YW |
296 | if (r < 0) |
297 | return r; | |
298 | ||
231d9de1 ZJS |
299 | r = sd_rtnl_message_link_get_ifindex(reply, &ret); |
300 | if (r < 0) | |
301 | return r; | |
302 | assert(ret > 0); | |
303 | return ret; | |
b04c5e51 YW |
304 | } |
305 | ||
f6e49154 YW |
306 | int rtnl_resolve_ifname(sd_netlink **rtnl, const char *name) { |
307 | int r; | |
308 | ||
309 | /* Like if_nametoindex, but resolves "alternative names" too. */ | |
310 | ||
311 | assert(name); | |
312 | ||
313 | r = if_nametoindex(name); | |
314 | if (r > 0) | |
315 | return r; | |
316 | ||
317 | return rtnl_resolve_link_alternative_name(rtnl, name); | |
318 | } | |
319 | ||
320 | int rtnl_resolve_interface(sd_netlink **rtnl, const char *name) { | |
321 | int r; | |
322 | ||
323 | /* Like rtnl_resolve_ifname, but resolves interface numbers too. */ | |
324 | ||
325 | assert(name); | |
326 | ||
327 | r = parse_ifindex(name); | |
328 | if (r > 0) | |
329 | return r; | |
330 | assert(r < 0); | |
331 | ||
332 | return rtnl_resolve_ifname(rtnl, name); | |
333 | } | |
334 | ||
335 | int rtnl_resolve_interface_or_warn(sd_netlink **rtnl, const char *name) { | |
336 | int r; | |
337 | ||
338 | r = rtnl_resolve_interface(rtnl, name); | |
339 | if (r < 0) | |
340 | return log_error_errno(r, "Failed to resolve interface \"%s\": %m", name); | |
341 | return r; | |
342 | } | |
343 | ||
f25e642b | 344 | int rtnl_get_link_info(sd_netlink **rtnl, int ifindex, unsigned short *ret_iftype, unsigned *ret_flags) { |
ef62949a | 345 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; |
f25e642b YW |
346 | unsigned short iftype; |
347 | unsigned flags; | |
ef62949a YW |
348 | int r; |
349 | ||
f25e642b YW |
350 | assert(rtnl); |
351 | assert(ifindex > 0); | |
352 | ||
353 | if (!ret_iftype && !ret_flags) | |
354 | return 0; | |
355 | ||
ef62949a YW |
356 | if (!*rtnl) { |
357 | r = sd_netlink_open(rtnl); | |
358 | if (r < 0) | |
359 | return r; | |
360 | } | |
361 | ||
362 | r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex); | |
363 | if (r < 0) | |
364 | return r; | |
365 | ||
366 | r = sd_netlink_call(*rtnl, message, 0, &reply); | |
367 | if (r == -EINVAL) | |
368 | return -ENODEV; /* The device does not exist */ | |
369 | if (r < 0) | |
370 | return r; | |
371 | ||
f25e642b YW |
372 | if (ret_iftype) { |
373 | r = sd_rtnl_message_link_get_type(reply, &iftype); | |
374 | if (r < 0) | |
375 | return r; | |
376 | } | |
377 | ||
378 | if (ret_flags) { | |
379 | r = sd_rtnl_message_link_get_flags(reply, &flags); | |
380 | if (r < 0) | |
381 | return r; | |
382 | } | |
383 | ||
384 | if (ret_iftype) | |
385 | *ret_iftype = iftype; | |
386 | if (ret_flags) | |
387 | *ret_flags = flags; | |
388 | return 0; | |
ef62949a YW |
389 | } |
390 | ||
05d0c2e3 | 391 | int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) { |
3815f36f TG |
392 | struct nlmsgerr *err; |
393 | int r; | |
394 | ||
395 | assert(error <= 0); | |
396 | ||
05d0c2e3 | 397 | r = message_new(rtnl, ret, NLMSG_ERROR); |
3815f36f TG |
398 | if (r < 0) |
399 | return r; | |
400 | ||
628f08b6 | 401 | rtnl_message_seal(*ret); |
3815f36f TG |
402 | (*ret)->hdr->nlmsg_seq = serial; |
403 | ||
404 | err = NLMSG_DATA((*ret)->hdr); | |
3815f36f TG |
405 | err->error = error; |
406 | ||
407 | return 0; | |
408 | } | |
409 | ||
ee8c4568 | 410 | int rtnl_log_parse_error(int r) { |
8d3d7072 | 411 | return log_error_errno(r, "Failed to parse netlink message: %m"); |
ee8c4568 LP |
412 | } |
413 | ||
414 | int rtnl_log_create_error(int r) { | |
8d3d7072 | 415 | return log_error_errno(r, "Failed to create netlink message: %m"); |
ee8c4568 | 416 | } |
6497a8aa YW |
417 | |
418 | void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length) { | |
419 | size_t padding_length; | |
14b6e6b6 | 420 | uint8_t *padding; |
6497a8aa YW |
421 | |
422 | assert(rta); | |
423 | assert(!data || data_length > 0); | |
424 | ||
425 | /* fill in the attribute */ | |
426 | rta->rta_type = type; | |
427 | rta->rta_len = RTA_LENGTH(data_length); | |
428 | if (data) | |
429 | /* we don't deal with the case where the user lies about the type | |
430 | * and gives us too little data (so don't do that) | |
431 | */ | |
432 | padding = mempcpy(RTA_DATA(rta), data, data_length); | |
433 | ||
434 | else | |
435 | /* if no data was passed, make sure we still initialize the padding | |
436 | note that we can have data_length > 0 (used by some containers) */ | |
437 | padding = RTA_DATA(rta); | |
438 | ||
439 | /* make sure also the padding at the end of the message is initialized */ | |
14b6e6b6 | 440 | padding_length = (uint8_t *) rta + RTA_SPACE(data_length) - padding; |
6497a8aa YW |
441 | memzero(padding, padding_length); |
442 | } | |
443 | ||
444 | int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length) { | |
445 | struct rtattr *new_rta, *sub_rta; | |
446 | size_t message_length; | |
447 | ||
448 | assert(rta); | |
449 | assert(!data || data_length > 0); | |
450 | ||
451 | /* get the new message size (with padding at the end) */ | |
452 | message_length = RTA_ALIGN(rta ? (*rta)->rta_len : 0) + RTA_SPACE(data_length); | |
453 | ||
454 | /* buffer should be smaller than both one page or 8K to be accepted by the kernel */ | |
455 | if (message_length > MIN(page_size(), 8192UL)) | |
456 | return -ENOBUFS; | |
457 | ||
458 | /* realloc to fit the new attribute */ | |
459 | new_rta = realloc(*rta, message_length); | |
460 | if (!new_rta) | |
461 | return -ENOMEM; | |
462 | *rta = new_rta; | |
463 | ||
464 | /* get pointer to the attribute we are about to add */ | |
465 | sub_rta = (struct rtattr *) ((uint8_t *) *rta + RTA_ALIGN((*rta)->rta_len)); | |
466 | ||
467 | rtattr_append_attribute_internal(sub_rta, type, data, data_length); | |
468 | ||
469 | /* update rta_len */ | |
470 | (*rta)->rta_len = message_length; | |
471 | ||
472 | return 0; | |
473 | } | |
2fe1d557 | 474 | |
e8f52f3c YW |
475 | MultipathRoute *multipath_route_free(MultipathRoute *m) { |
476 | if (!m) | |
477 | return NULL; | |
478 | ||
479 | free(m->ifname); | |
480 | ||
481 | return mfree(m); | |
482 | } | |
483 | ||
4867b9d7 YW |
484 | int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret) { |
485 | _cleanup_(multipath_route_freep) MultipathRoute *n = NULL; | |
486 | _cleanup_free_ char *ifname = NULL; | |
487 | ||
488 | assert(m); | |
489 | assert(ret); | |
490 | ||
491 | if (m->ifname) { | |
492 | ifname = strdup(m->ifname); | |
493 | if (!ifname) | |
494 | return -ENOMEM; | |
495 | } | |
496 | ||
497 | n = new(MultipathRoute, 1); | |
498 | if (!n) | |
499 | return -ENOMEM; | |
500 | ||
501 | *n = (MultipathRoute) { | |
502 | .gateway = m->gateway, | |
503 | .weight = m->weight, | |
504 | .ifindex = m->ifindex, | |
505 | .ifname = TAKE_PTR(ifname), | |
506 | }; | |
507 | ||
508 | *ret = TAKE_PTR(n); | |
509 | ||
510 | return 0; | |
511 | } | |
512 | ||
2fe1d557 YW |
513 | int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, OrderedSet **ret) { |
514 | _cleanup_ordered_set_free_free_ OrderedSet *set = NULL; | |
515 | int r; | |
516 | ||
517 | assert(rtnh); | |
518 | assert(IN_SET(family, AF_INET, AF_INET6)); | |
519 | ||
520 | if (size < sizeof(struct rtnexthop)) | |
521 | return -EBADMSG; | |
522 | ||
523 | for (; size >= sizeof(struct rtnexthop); ) { | |
e8f52f3c | 524 | _cleanup_(multipath_route_freep) MultipathRoute *m = NULL; |
2fe1d557 YW |
525 | |
526 | if (NLMSG_ALIGN(rtnh->rtnh_len) > size) | |
527 | return -EBADMSG; | |
528 | ||
529 | if (rtnh->rtnh_len < sizeof(struct rtnexthop)) | |
530 | return -EBADMSG; | |
531 | ||
532 | m = new(MultipathRoute, 1); | |
533 | if (!m) | |
534 | return -ENOMEM; | |
535 | ||
536 | *m = (MultipathRoute) { | |
537 | .ifindex = rtnh->rtnh_ifindex, | |
234106db | 538 | .weight = rtnh->rtnh_hops, |
2fe1d557 YW |
539 | }; |
540 | ||
541 | if (rtnh->rtnh_len > sizeof(struct rtnexthop)) { | |
542 | size_t len = rtnh->rtnh_len - sizeof(struct rtnexthop); | |
543 | ||
544 | for (struct rtattr *attr = RTNH_DATA(rtnh); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) { | |
545 | if (attr->rta_type == RTA_GATEWAY) { | |
546 | if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(family))) | |
547 | return -EBADMSG; | |
548 | ||
549 | m->gateway.family = family; | |
550 | memcpy(&m->gateway.address, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(family)); | |
551 | break; | |
552 | } else if (attr->rta_type == RTA_VIA) { | |
553 | uint16_t gw_family; | |
554 | ||
555 | if (family != AF_INET) | |
556 | return -EINVAL; | |
557 | ||
558 | if (attr->rta_len < RTA_LENGTH(sizeof(uint16_t))) | |
559 | return -EBADMSG; | |
560 | ||
561 | gw_family = *(uint16_t *) RTA_DATA(attr); | |
562 | ||
563 | if (gw_family != AF_INET6) | |
564 | return -EBADMSG; | |
565 | ||
566 | if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family))) | |
567 | return -EBADMSG; | |
568 | ||
569 | memcpy(&m->gateway, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family)); | |
570 | break; | |
571 | } | |
572 | } | |
573 | } | |
574 | ||
575 | r = ordered_set_ensure_put(&set, NULL, m); | |
576 | if (r < 0) | |
577 | return r; | |
578 | ||
579 | TAKE_PTR(m); | |
580 | ||
581 | size -= NLMSG_ALIGN(rtnh->rtnh_len); | |
582 | rtnh = RTNH_NEXT(rtnh); | |
583 | } | |
584 | ||
585 | if (ret) | |
586 | *ret = TAKE_PTR(set); | |
587 | return 0; | |
588 | } |