]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-link.c
journalctl: allow globbing in --unit and --user-unit
[thirdparty/systemd.git] / src / network / networkd-link.c
CommitLineData
f579559b
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <netinet/ether.h>
23#include <linux/if.h>
24
25#include "networkd.h"
26#include "libudev-private.h"
27#include "util.h"
28
29int link_new(Manager *manager, struct udev_device *device, Link **ret) {
30 _cleanup_link_free_ Link *link = NULL;
8cd11a0f 31 const char *mac;
602cc437 32 struct ether_addr *mac_addr;
c166a070 33 const char *ifname;
f579559b
TG
34 int r;
35
36 assert(device);
37 assert(ret);
38
39 link = new0(Link, 1);
40 if (!link)
41 return -ENOMEM;
42
5a3eb5a7
TG
43 link->manager = manager;
44 link->state = _LINK_STATE_INVALID;
45
0617ffab
TG
46 link->ifindex = udev_device_get_ifindex(device);
47 if (link->ifindex <= 0)
f579559b
TG
48 return -EINVAL;
49
8cd11a0f 50 mac = udev_device_get_sysattr_value(device, "address");
5a3eb5a7
TG
51 if (mac) {
52 mac_addr = ether_aton(mac);
53 if (mac_addr)
54 memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
55 }
f579559b 56
c166a070
TG
57 ifname = udev_device_get_sysname(device);
58 link->ifname = strdup(ifname);
59
0617ffab 60 r = hashmap_put(manager->links, &link->ifindex, link);
f579559b
TG
61 if (r < 0)
62 return r;
63
64 *ret = link;
65 link = NULL;
66
67 return 0;
68}
69
70void link_free(Link *link) {
71 if (!link)
72 return;
73
0617ffab 74 assert(link->manager);
f579559b 75
f5be5601
TG
76 if (link->dhcp)
77 sd_dhcp_client_free(link->dhcp);
78
79 route_free(link->dhcp_route);
80 link->dhcp_route = NULL;
81
82 address_free(link->dhcp_address);
83 link->dhcp_address = NULL;
84
0617ffab 85 hashmap_remove(link->manager->links, &link->ifindex);
f579559b 86
c166a070
TG
87 free(link->ifname);
88
f579559b
TG
89 free(link);
90}
91
aa3437a5 92int link_add(Manager *m, struct udev_device *device, Link **ret) {
f579559b
TG
93 Link *link;
94 Network *network;
95 int r;
96 uint64_t ifindex;
02b59d57 97 const char *devtype;
f579559b
TG
98
99 assert(m);
100 assert(device);
101
102 ifindex = udev_device_get_ifindex(device);
103 link = hashmap_get(m->links, &ifindex);
aa3437a5
TG
104 if (link) {
105 *ret = link;
2672953b 106 return -EEXIST;
aa3437a5 107 }
f579559b
TG
108
109 r = link_new(m, device, &link);
2672953b 110 if (r < 0)
f579559b 111 return r;
f579559b 112
aa3437a5
TG
113 *ret = link;
114
02b59d57
TG
115 devtype = udev_device_get_devtype(device);
116 if (streq_ptr(devtype, "bridge")) {
117 r = bridge_set_link(m, link);
aa3437a5
TG
118 if (r < 0 && r != -ENOENT)
119 return r;
02b59d57
TG
120 }
121
f579559b
TG
122 r = network_get(m, device, &network);
123 if (r < 0)
124 return r == -ENOENT ? 0 : r;
125
126 r = network_apply(m, network, link);
127 if (r < 0)
128 return r;
129
130 return 0;
131}
132
f882c247 133static int link_enter_configured(Link *link) {
ef1ba606
TG
134 assert(link);
135 assert(link->state == LINK_STATE_SETTING_ROUTES);
136
3333d748 137 log_link_info(link, "link configured");
f882c247
TG
138
139 link->state = LINK_STATE_CONFIGURED;
140
141 return 0;
142}
143
ef1ba606
TG
144static void link_enter_failed(Link *link) {
145 assert(link);
f882c247 146
3333d748 147 log_link_warning(link, "failed");
449f7554 148
ef1ba606 149 link->state = LINK_STATE_FAILED;
f882c247
TG
150}
151
f882c247
TG
152static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
153 Link *link = userdata;
154 int r;
155
f5be5601
TG
156 assert(link->route_messages > 0);
157 assert(link->state == LINK_STATE_SETTING_ADDRESSES ||
158 link->state == LINK_STATE_SETTING_ROUTES ||
159 link->state == LINK_STATE_FAILED);
f882c247 160
f5be5601 161 link->route_messages --;
f882c247
TG
162
163 if (link->state == LINK_STATE_FAILED)
164 return 1;
165
166 r = sd_rtnl_message_get_errno(m);
c166a070 167 if (r < 0 && r != -EEXIST)
3333d748 168 log_warning_link(link, "could not set route: %s", strerror(-r));
f882c247 169
f5be5601
TG
170 /* we might have received an old reply after moving back to SETTING_ADDRESSES,
171 * ignore it */
172 if (link->route_messages == 0 && link->state == LINK_STATE_SETTING_ROUTES) {
3333d748 173 log_link_debug(link, "routes set");
dd3efc09
TG
174 link_enter_configured(link);
175 }
f882c247
TG
176
177 return 1;
178}
179
180static int link_enter_set_routes(Link *link) {
181 Route *route;
182 int r;
183
184 assert(link);
185 assert(link->network);
ef1ba606 186 assert(link->state == LINK_STATE_SETTING_ADDRESSES);
f882c247 187
ef1ba606 188 link->state = LINK_STATE_SETTING_ROUTES;
f882c247 189
f5be5601 190 if (!link->network->static_routes && !link->dhcp_route)
dd3efc09 191 return link_enter_configured(link);
f882c247 192
3333d748 193 log_link_debug(link, "setting routes");
449f7554 194
f048a16b 195 LIST_FOREACH(static_routes, route, link->network->static_routes) {
f882c247 196 r = route_configure(route, link, &route_handler);
dd3efc09 197 if (r < 0) {
3333d748
ZJS
198 log_warning_link(link,
199 "could not set routes: %s", strerror(-r));
ef1ba606
TG
200 link_enter_failed(link);
201 return r;
dd3efc09 202 }
c166a070 203
f5be5601
TG
204 link->route_messages ++;
205 }
206
207 if (link->dhcp_route) {
208 r = route_configure(link->dhcp_route, link, &route_handler);
209 if (r < 0) {
3333d748
ZJS
210 log_warning_link(link,
211 "could not set routes: %s", strerror(-r));
f5be5601
TG
212 link_enter_failed(link);
213 return r;
214 }
215
216 link->route_messages ++;
f882c247
TG
217 }
218
219 return 0;
220}
221
f882c247
TG
222static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
223 Link *link = userdata;
224 int r;
225
f5be5601
TG
226 assert(m);
227 assert(link);
228 assert(link->ifname);
229 assert(link->addr_messages > 0);
ef1ba606 230 assert(link->state == LINK_STATE_SETTING_ADDRESSES || link->state == LINK_STATE_FAILED);
f882c247 231
f5be5601 232 link->addr_messages --;
f882c247
TG
233
234 if (link->state == LINK_STATE_FAILED)
235 return 1;
236
237 r = sd_rtnl_message_get_errno(m);
c166a070 238 if (r < 0 && r != -EEXIST)
3333d748
ZJS
239 log_struct_link(LOG_ERR, link,
240 "MESSAGE=%s: could not set address: %s",
241 link->ifname, strerror(-r),
242 "ERRNO=%d", -r,
243 NULL);
f882c247 244
f5be5601 245 if (link->addr_messages == 0) {
3333d748 246 log_link_debug(link, "addresses set");
ef1ba606 247 link_enter_set_routes(link);
dd3efc09 248 }
f882c247
TG
249
250 return 1;
251}
252
253static int link_enter_set_addresses(Link *link) {
254 Address *address;
255 int r;
256
257 assert(link);
258 assert(link->network);
f5be5601 259 assert(link->state != _LINK_STATE_INVALID);
f882c247 260
ef1ba606 261 link->state = LINK_STATE_SETTING_ADDRESSES;
f882c247 262
f5be5601 263 if (!link->network->static_addresses && !link->dhcp_address)
ef1ba606 264 return link_enter_set_routes(link);
f882c247 265
3333d748 266 log_link_debug(link, "setting addresses");
449f7554 267
f048a16b 268 LIST_FOREACH(static_addresses, address, link->network->static_addresses) {
f882c247 269 r = address_configure(address, link, &address_handler);
dd3efc09 270 if (r < 0) {
3333d748
ZJS
271 log_warning_link(link,
272 "could not set addresses: %s", strerror(-r));
ef1ba606
TG
273 link_enter_failed(link);
274 return r;
dd3efc09 275 }
c166a070 276
f5be5601
TG
277 link->addr_messages ++;
278 }
279
280 if (link->dhcp_address) {
281 r = address_configure(link->dhcp_address, link, &address_handler);
282 if (r < 0) {
3333d748
ZJS
283 log_warning_link(link,
284 "could not set addresses: %s", strerror(-r));
f5be5601
TG
285 link_enter_failed(link);
286 return r;
287 }
288
289 link->addr_messages ++;
f882c247
TG
290 }
291
292 return 0;
293}
294
ff254138
TG
295static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
296 Link *link = userdata;
297 int r;
298
299 assert(m);
300 assert(link);
301 assert(link->ifname);
302
303 if (link->state == LINK_STATE_FAILED)
304 return 1;
305
306 r = sd_rtnl_message_get_errno(m);
307 if (r < 0 && r != -EEXIST)
3333d748 308 log_warning_link(link, "could not drop address: %s", strerror(-r));
ff254138
TG
309
310 return 1;
311}
312
313static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
314 Link *link = userdata;
315 struct in_addr address;
316 struct in_addr netmask;
317 struct in_addr gateway;
318 int prefixlen;
319 int r;
320
321 assert(link);
322
323 if (link->state == LINK_STATE_FAILED)
324 return;
325
326 if (event < 0) {
3333d748 327 log_warning_link(link, "DHCP error: %s", strerror(-event));
ff254138
TG
328 link_enter_failed(link);
329 return;
330 }
331
332 if (event == DHCP_EVENT_NO_LEASE)
3333d748 333 log_link_debug(link, "IP address in use.");
ff254138
TG
334
335 if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_EXPIRED ||
336 event == DHCP_EVENT_STOP) {
337 if (link->dhcp_address) {
338 address_drop(link->dhcp_address, link, address_drop_handler);
339
340 address_free(link->dhcp_address);
341 link->dhcp_address = NULL;
342 }
343
344 if (link->dhcp_route) {
345 route_free(link->dhcp_route);
346 link->dhcp_route = NULL;
347 }
348 }
349
350 r = sd_dhcp_client_get_address(client, &address);
351 if (r < 0) {
3333d748 352 log_link_warning(link, "DHCP error: no address");
ff254138
TG
353 link_enter_failed(link);
354 return;
355 }
356
357 r = sd_dhcp_client_get_netmask(client, &netmask);
358 if (r < 0) {
3333d748 359 log_link_warning(link, "DHCP error: no netmask");
ff254138
TG
360 link_enter_failed(link);
361 return;
362 }
363
364 prefixlen = sd_dhcp_client_prefixlen(&netmask);
365 if (prefixlen < 0) {
3333d748 366 log_link_warning(link, "DHCP error: no prefixlen");
ff254138
TG
367 link_enter_failed(link);
368 return;
369 }
370
371 r = sd_dhcp_client_get_router(client, &gateway);
372 if (r < 0) {
3333d748 373 log_link_warning(link, "DHCP error: no router");
ff254138
TG
374 link_enter_failed(link);
375 return;
376 }
377
378 if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_IP_ACQUIRE) {
379 _cleanup_address_free_ Address *addr = NULL;
380 _cleanup_route_free_ Route *rt = NULL;
381
62870613
ZJS
382 log_struct_link(LOG_INFO, link,
383 "MESSAGE=%s: DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
384 link->ifname,
385 ADDRESS_FMT_VAL(address),
386 prefixlen,
387 ADDRESS_FMT_VAL(gateway),
388 "ADDRESS=%u.%u.%u.%u",
389 ADDRESS_FMT_VAL(address),
390 "PREFIXLEN=%u",
391 prefixlen,
392 "GATEWAY=%u.%u.%u.%u",
393 ADDRESS_FMT_VAL(gateway),
394 NULL);
ff254138
TG
395
396 r = address_new_dynamic(&addr);
397 if (r < 0) {
3333d748 398 log_link_error(link, "Could not allocate address");
ff254138
TG
399 link_enter_failed(link);
400 return;
401 }
402
403 addr->family = AF_INET;
404 addr->in_addr.in = address;
405 addr->prefixlen = prefixlen;
406 addr->netmask = netmask;
407
408 r = route_new_dynamic(&rt);
409 if (r < 0) {
3333d748 410 log_link_error(link, "Could not allocate route");
ff254138
TG
411 link_enter_failed(link);
412 return;
413 }
414
415 rt->family = AF_INET;
416 rt->in_addr.in = gateway;
417
418 link->dhcp_address = addr;
419 link->dhcp_route = rt;
420 addr = NULL;
421 rt = NULL;
422
423 link_enter_set_addresses(link);
424 }
425
426 return;
427}
428
429static int link_acquire_conf(Link *link) {
430 int r;
431
432 assert(link);
433 assert(link->network);
434 assert(link->network->dhcp);
435 assert(link->manager);
436 assert(link->manager->event);
437
438 if (!link->dhcp) {
439 link->dhcp = sd_dhcp_client_new(link->manager->event);
440 if (!link->dhcp)
441 return -ENOMEM;
442
443 r = sd_dhcp_client_set_index(link->dhcp, link->ifindex);
444 if (r < 0)
445 return r;
446
447 r = sd_dhcp_client_set_mac(link->dhcp, &link->mac);
448 if (r < 0)
449 return r;
450
451 r = sd_dhcp_client_set_callback(link->dhcp, dhcp_handler, link);
452 if (r < 0)
453 return r;
454 }
455
456 r = sd_dhcp_client_start(link->dhcp);
457 if (r < 0)
458 return r;
459
460 return 0;
461}
462
463static int link_update_flags(Link *link, unsigned flags) {
464 int r;
465
466 assert(link);
467 assert(link->network);
468
469 if (link->state == LINK_STATE_FAILED)
470 return 0;
471
efbc88b8 472 if (link->flags == flags) {
3333d748 473 log_debug_link(link, "link status unchanged: %#x", flags);
efbc88b8
TG
474 return 0;
475 }
476
3333d748
ZJS
477 if ((link->flags & IFF_UP) != (flags & IFF_UP))
478 log_info_link(link,
479 "power %s", flags & IFF_UP ? "on": "off");
efbc88b8
TG
480
481 if ((link->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
482 if (flags & IFF_LOWER_UP) {
3333d748 483 log_link_info(link, "carrier on");
efbc88b8
TG
484
485 if (link->network->dhcp) {
486 r = link_acquire_conf(link);
487 if (r < 0) {
488 link_enter_failed(link);
489 return r;
490 }
ff254138 491 }
efbc88b8 492 } else {
3333d748 493 log_link_info(link, "carrier off");
efbc88b8
TG
494
495 if (link->network->dhcp) {
496 r = sd_dhcp_client_stop(link->dhcp);
497 if (r < 0) {
498 link_enter_failed(link);
499 return r;
500 }
ff254138
TG
501 }
502 }
503 }
504
3333d748
ZJS
505 log_debug_link(link,
506 "link status updated: %#x -> %#x", link->flags, flags);
efbc88b8 507
ff254138
TG
508 link->flags = flags;
509
510 return 0;
511}
512
dd3efc09
TG
513static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
514 Link *link = userdata;
515 int r;
516
1746cf2a
TG
517 assert(link);
518
519 if (link->state == LINK_STATE_FAILED)
520 return 1;
521
dd3efc09
TG
522 r = sd_rtnl_message_get_errno(m);
523 if (r < 0) {
3333d748
ZJS
524 log_warning_link(link,
525 "could not bring up interface: %s", strerror(-r));
dd3efc09
TG
526 link_enter_failed(link);
527 }
f882c247 528
ff254138 529 link_update_flags(link, link->flags | IFF_UP);
1746cf2a 530
f882c247
TG
531 return 1;
532}
533
534static int link_up(Link *link) {
f579559b
TG
535 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
536 int r;
537
f882c247
TG
538 assert(link);
539 assert(link->manager);
540 assert(link->manager->rtnl);
541
3333d748 542 log_link_debug(link, "bringing link up");
449f7554 543
0f49a5f7 544 r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
f579559b 545 if (r < 0) {
3333d748 546 log_link_error(link, "Could not allocate RTM_SETLINK message");
f579559b
TG
547 return r;
548 }
549
fc25d7f8
TG
550 r = sd_rtnl_message_link_set_flags(req, IFF_UP);
551 if (r < 0) {
3333d748 552 log_error_link(link, "Could not set link flags: %s", strerror(-r));
fc25d7f8
TG
553 return r;
554 }
555
dd3efc09 556 r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
f579559b 557 if (r < 0) {
3333d748
ZJS
558 log_error_link(link,
559 "Could not send rtnetlink message: %s", strerror(-r));
f579559b
TG
560 return r;
561 }
562
f882c247
TG
563 return 0;
564}
565
ef1ba606 566static int link_bridge_joined(Link *link) {
f882c247
TG
567 int r;
568
ef1ba606
TG
569 assert(link);
570 assert(link->state == LINK_STATE_JOINING_BRIDGE);
f5be5601 571 assert(link->network);
dd3efc09 572
f882c247 573 r = link_up(link);
ef1ba606
TG
574 if (r < 0) {
575 link_enter_failed(link);
576 return r;
577 }
f882c247 578
1746cf2a
TG
579 if (!link->network->dhcp)
580 return link_enter_set_addresses(link);
ef1ba606
TG
581
582 return 0;
02b59d57
TG
583}
584
585static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
586 Link *link = userdata;
587 int r;
588
1746cf2a 589 assert(link);
ef1ba606
TG
590 assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
591 assert(link->network);
02b59d57
TG
592
593 if (link->state == LINK_STATE_FAILED)
594 return 1;
595
596 r = sd_rtnl_message_get_errno(m);
ef1ba606 597 if (r < 0) {
3333d748
ZJS
598 log_struct_link(LOG_ERR, link,
599 "MESSAGE=%s: could not join bridge '%s': %s",
600 link->ifname, link->network->bridge->name, strerror(-r),
601 BRIDGE(link->network->bridge),
602 NULL);
ef1ba606
TG
603 link_enter_failed(link);
604 return 1;
3333d748 605 }
02b59d57 606
3333d748
ZJS
607 log_struct_link(LOG_DEBUG, link,
608 "MESSAGE=%s: joined bridge '%s'",
609 link->network->bridge->name,
610 BRIDGE(link->network->bridge),
611 NULL);
ef1ba606 612 link_bridge_joined(link);
02b59d57
TG
613
614 return 1;
615}
616
617static int link_enter_join_bridge(Link *link) {
618 int r;
619
620 assert(link);
621 assert(link->network);
ef1ba606 622 assert(link->state == _LINK_STATE_INVALID);
02b59d57 623
ef1ba606 624 link->state = LINK_STATE_JOINING_BRIDGE;
02b59d57 625
ef1ba606
TG
626 if (!link->network->bridge)
627 return link_bridge_joined(link);
02b59d57 628
3333d748
ZJS
629 log_struct_link(LOG_DEBUG, link,
630 "MESSAGE=%s: joining bridge '%s'",
631 link->network->bridge->name,
632 BRIDGE(link->network->bridge),
633 NULL);
634 log_link_debug(link, "joining bridge");
449f7554 635
02b59d57 636 r = bridge_join(link->network->bridge, link, &bridge_handler);
dd3efc09 637 if (r < 0) {
3333d748
ZJS
638 log_struct_link(LOG_WARNING, link,
639 "MESSAGE=%s: could not join bridge '%s': %s",
640 link->network->bridge->name, strerror(-r),
641 BRIDGE(link->network->bridge),
642 NULL);
ef1ba606
TG
643 link_enter_failed(link);
644 return r;
645 }
646
647 return 0;
648}
649
650static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
651 Link *link = userdata;
652 int r;
653
1746cf2a
TG
654 assert(link);
655
656 if (link->state == LINK_STATE_FAILED)
657 return 1;
658
ef1ba606
TG
659 r = sd_rtnl_message_get_errno(m);
660 if (r < 0) {
3333d748 661 log_warning_link(link, "could not get state: %s", strerror(-r));
ef1ba606
TG
662 link_enter_failed(link);
663 }
664
3333d748 665 log_link_debug(link, "got link state");
1746cf2a 666
5eb036ca
TG
667 link_update(link, m);
668
ef1ba606
TG
669 return 1;
670}
671
672static int link_get(Link *link) {
673 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
674 int r;
675
676 assert(link);
677 assert(link->manager);
678 assert(link->manager->rtnl);
679
3333d748 680 log_link_debug(link, "requesting link status");
449f7554 681
ef1ba606
TG
682 r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
683 if (r < 0) {
3333d748 684 log_link_error(link, "Could not allocate RTM_GETLINK message");
ef1ba606
TG
685 return r;
686 }
687
688 r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
689 if (r < 0) {
3333d748
ZJS
690 log_error_link(link,
691 "Could not send rtnetlink message: %s", strerror(-r));
ef1ba606 692 return r;
dd3efc09 693 }
02b59d57
TG
694
695 return 0;
696}
697
698int link_configure(Link *link) {
699 int r;
700
ef1ba606
TG
701 assert(link);
702 assert(link->network);
703 assert(link->state == _LINK_STATE_INVALID);
704
dd3efc09 705 r = link_get(link);
ef1ba606
TG
706 if (r < 0) {
707 link_enter_failed(link);
708 return r;
709 }
dd3efc09 710
1746cf2a 711 return link_enter_join_bridge(link);
f579559b 712}
dd3efc09 713
22936833
TG
714int link_update(Link *link, sd_rtnl_message *m) {
715 unsigned flags;
716 int r;
717
dd3efc09 718 assert(link);
22936833
TG
719 assert(m);
720
1746cf2a
TG
721 if (link->state == LINK_STATE_FAILED)
722 return 0;
723
22936833
TG
724 r = sd_rtnl_message_link_get_flags(m, &flags);
725 if (r < 0) {
3333d748 726 log_link_warning(link, "could not get link flags");
22936833
TG
727 return r;
728 }
dd3efc09 729
ff254138 730 return link_update_flags(link, flags);
dd3efc09 731}