]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-link.c
sd-rtnl: only alter link flags when reqeusted to
[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
39032b87 137 log_info_link(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
39032b87 147 log_warning_link(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) {
39032b87 173 log_debug_link(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
39032b87 193 log_debug_link(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) {
39032b87 246 log_debug_link(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
39032b87 266 log_debug_link(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);
5be4d38e 322 assert(link->network);
ff254138
TG
323
324 if (link->state == LINK_STATE_FAILED)
325 return;
326
327 if (event < 0) {
3333d748 328 log_warning_link(link, "DHCP error: %s", strerror(-event));
ff254138
TG
329 link_enter_failed(link);
330 return;
331 }
332
333 if (event == DHCP_EVENT_NO_LEASE)
39032b87 334 log_debug_link(link, "IP address in use.");
ff254138
TG
335
336 if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_EXPIRED ||
337 event == DHCP_EVENT_STOP) {
338 if (link->dhcp_address) {
339 address_drop(link->dhcp_address, link, address_drop_handler);
340
341 address_free(link->dhcp_address);
342 link->dhcp_address = NULL;
343 }
344
345 if (link->dhcp_route) {
346 route_free(link->dhcp_route);
347 link->dhcp_route = NULL;
348 }
349 }
350
351 r = sd_dhcp_client_get_address(client, &address);
352 if (r < 0) {
39032b87 353 log_warning_link(link, "DHCP error: no address");
ff254138
TG
354 link_enter_failed(link);
355 return;
356 }
357
358 r = sd_dhcp_client_get_netmask(client, &netmask);
359 if (r < 0) {
39032b87 360 log_warning_link(link, "DHCP error: no netmask");
ff254138
TG
361 link_enter_failed(link);
362 return;
363 }
364
365 prefixlen = sd_dhcp_client_prefixlen(&netmask);
366 if (prefixlen < 0) {
39032b87 367 log_warning_link(link, "DHCP error: no prefixlen");
ff254138
TG
368 link_enter_failed(link);
369 return;
370 }
371
372 r = sd_dhcp_client_get_router(client, &gateway);
373 if (r < 0) {
39032b87 374 log_warning_link(link, "DHCP error: no router");
ff254138
TG
375 link_enter_failed(link);
376 return;
377 }
378
379 if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_IP_ACQUIRE) {
380 _cleanup_address_free_ Address *addr = NULL;
381 _cleanup_route_free_ Route *rt = NULL;
3bef724f 382 struct in_addr **nameservers;
ff254138 383
62870613
ZJS
384 log_struct_link(LOG_INFO, link,
385 "MESSAGE=%s: DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
386 link->ifname,
387 ADDRESS_FMT_VAL(address),
388 prefixlen,
389 ADDRESS_FMT_VAL(gateway),
390 "ADDRESS=%u.%u.%u.%u",
391 ADDRESS_FMT_VAL(address),
392 "PREFIXLEN=%u",
393 prefixlen,
394 "GATEWAY=%u.%u.%u.%u",
395 ADDRESS_FMT_VAL(gateway),
396 NULL);
ff254138
TG
397
398 r = address_new_dynamic(&addr);
399 if (r < 0) {
39032b87 400 log_error_link(link, "Could not allocate address");
ff254138
TG
401 link_enter_failed(link);
402 return;
403 }
404
405 addr->family = AF_INET;
406 addr->in_addr.in = address;
407 addr->prefixlen = prefixlen;
408 addr->netmask = netmask;
409
410 r = route_new_dynamic(&rt);
411 if (r < 0) {
39032b87 412 log_error_link(link, "Could not allocate route");
ff254138
TG
413 link_enter_failed(link);
414 return;
415 }
416
417 rt->family = AF_INET;
418 rt->in_addr.in = gateway;
419
420 link->dhcp_address = addr;
421 link->dhcp_route = rt;
422 addr = NULL;
423 rt = NULL;
424
5be4d38e
TG
425 if (link->network->dhcp_dns) {
426 r = sd_dhcp_client_get_dns(client, &nameservers);
427 if (r >= 0) {
428 r = manager_update_resolv_conf(link->manager);
429 if (r < 0)
430 log_error("Failed to update resolv.conf");
431 }
3bef724f
TG
432 }
433
ff254138
TG
434 link_enter_set_addresses(link);
435 }
436
437 return;
438}
439
440static int link_acquire_conf(Link *link) {
441 int r;
442
443 assert(link);
444 assert(link->network);
445 assert(link->network->dhcp);
446 assert(link->manager);
447 assert(link->manager->event);
448
449 if (!link->dhcp) {
450 link->dhcp = sd_dhcp_client_new(link->manager->event);
451 if (!link->dhcp)
452 return -ENOMEM;
453
454 r = sd_dhcp_client_set_index(link->dhcp, link->ifindex);
455 if (r < 0)
456 return r;
457
458 r = sd_dhcp_client_set_mac(link->dhcp, &link->mac);
459 if (r < 0)
460 return r;
461
462 r = sd_dhcp_client_set_callback(link->dhcp, dhcp_handler, link);
463 if (r < 0)
464 return r;
465 }
466
ab47d620
TG
467 log_debug_link(link, "acquiring DHCPv4 lease");
468
ff254138
TG
469 r = sd_dhcp_client_start(link->dhcp);
470 if (r < 0)
471 return r;
472
473 return 0;
474}
475
476static int link_update_flags(Link *link, unsigned flags) {
477 int r;
478
479 assert(link);
480 assert(link->network);
481
482 if (link->state == LINK_STATE_FAILED)
483 return 0;
484
efbc88b8 485 if (link->flags == flags) {
ab47d620 486 log_debug_link(link, "link status unchanged: %#.8x", flags);
efbc88b8
TG
487 return 0;
488 }
489
3333d748
ZJS
490 if ((link->flags & IFF_UP) != (flags & IFF_UP))
491 log_info_link(link,
492 "power %s", flags & IFF_UP ? "on": "off");
efbc88b8
TG
493
494 if ((link->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
495 if (flags & IFF_LOWER_UP) {
39032b87 496 log_info_link(link, "carrier on");
efbc88b8
TG
497
498 if (link->network->dhcp) {
499 r = link_acquire_conf(link);
500 if (r < 0) {
501 link_enter_failed(link);
502 return r;
503 }
ff254138 504 }
efbc88b8 505 } else {
39032b87 506 log_info_link(link, "carrier off");
efbc88b8
TG
507
508 if (link->network->dhcp) {
509 r = sd_dhcp_client_stop(link->dhcp);
510 if (r < 0) {
511 link_enter_failed(link);
512 return r;
513 }
ff254138
TG
514 }
515 }
516 }
517
3333d748 518 log_debug_link(link,
ab47d620 519 "link status updated: %#.8x -> %#.8x", link->flags, flags);
efbc88b8 520
ff254138
TG
521 link->flags = flags;
522
523 return 0;
524}
525
dd3efc09
TG
526static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
527 Link *link = userdata;
528 int r;
529
1746cf2a
TG
530 assert(link);
531
532 if (link->state == LINK_STATE_FAILED)
533 return 1;
534
dd3efc09
TG
535 r = sd_rtnl_message_get_errno(m);
536 if (r < 0) {
3333d748
ZJS
537 log_warning_link(link,
538 "could not bring up interface: %s", strerror(-r));
dd3efc09
TG
539 link_enter_failed(link);
540 }
f882c247 541
ff254138 542 link_update_flags(link, link->flags | IFF_UP);
1746cf2a 543
f882c247
TG
544 return 1;
545}
546
547static int link_up(Link *link) {
f579559b
TG
548 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
549 int r;
550
f882c247
TG
551 assert(link);
552 assert(link->manager);
553 assert(link->manager->rtnl);
554
39032b87 555 log_debug_link(link, "bringing link up");
449f7554 556
0f49a5f7 557 r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
f579559b 558 if (r < 0) {
39032b87 559 log_error_link(link, "Could not allocate RTM_SETLINK message");
f579559b
TG
560 return r;
561 }
562
fc25d7f8
TG
563 r = sd_rtnl_message_link_set_flags(req, IFF_UP);
564 if (r < 0) {
3333d748 565 log_error_link(link, "Could not set link flags: %s", strerror(-r));
fc25d7f8
TG
566 return r;
567 }
568
dd3efc09 569 r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
f579559b 570 if (r < 0) {
3333d748
ZJS
571 log_error_link(link,
572 "Could not send rtnetlink message: %s", strerror(-r));
f579559b
TG
573 return r;
574 }
575
f882c247
TG
576 return 0;
577}
578
ef1ba606 579static int link_bridge_joined(Link *link) {
f882c247
TG
580 int r;
581
ef1ba606
TG
582 assert(link);
583 assert(link->state == LINK_STATE_JOINING_BRIDGE);
f5be5601 584 assert(link->network);
dd3efc09 585
f882c247 586 r = link_up(link);
ef1ba606
TG
587 if (r < 0) {
588 link_enter_failed(link);
589 return r;
590 }
f882c247 591
1746cf2a
TG
592 if (!link->network->dhcp)
593 return link_enter_set_addresses(link);
ef1ba606
TG
594
595 return 0;
02b59d57
TG
596}
597
598static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
599 Link *link = userdata;
600 int r;
601
1746cf2a 602 assert(link);
ef1ba606
TG
603 assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
604 assert(link->network);
02b59d57
TG
605
606 if (link->state == LINK_STATE_FAILED)
607 return 1;
608
609 r = sd_rtnl_message_get_errno(m);
ef1ba606 610 if (r < 0) {
3333d748
ZJS
611 log_struct_link(LOG_ERR, link,
612 "MESSAGE=%s: could not join bridge '%s': %s",
613 link->ifname, link->network->bridge->name, strerror(-r),
614 BRIDGE(link->network->bridge),
615 NULL);
ef1ba606
TG
616 link_enter_failed(link);
617 return 1;
3333d748 618 }
02b59d57 619
3333d748
ZJS
620 log_struct_link(LOG_DEBUG, link,
621 "MESSAGE=%s: joined bridge '%s'",
622 link->network->bridge->name,
623 BRIDGE(link->network->bridge),
624 NULL);
ab47d620 625
ef1ba606 626 link_bridge_joined(link);
02b59d57
TG
627
628 return 1;
629}
630
631static int link_enter_join_bridge(Link *link) {
632 int r;
633
634 assert(link);
635 assert(link->network);
ef1ba606 636 assert(link->state == _LINK_STATE_INVALID);
02b59d57 637
ef1ba606 638 link->state = LINK_STATE_JOINING_BRIDGE;
02b59d57 639
ef1ba606
TG
640 if (!link->network->bridge)
641 return link_bridge_joined(link);
02b59d57 642
3333d748
ZJS
643 log_struct_link(LOG_DEBUG, link,
644 "MESSAGE=%s: joining bridge '%s'",
645 link->network->bridge->name,
646 BRIDGE(link->network->bridge),
647 NULL);
39032b87 648 log_debug_link(link, "joining bridge");
449f7554 649
02b59d57 650 r = bridge_join(link->network->bridge, link, &bridge_handler);
dd3efc09 651 if (r < 0) {
3333d748
ZJS
652 log_struct_link(LOG_WARNING, link,
653 "MESSAGE=%s: could not join bridge '%s': %s",
654 link->network->bridge->name, strerror(-r),
655 BRIDGE(link->network->bridge),
656 NULL);
ef1ba606
TG
657 link_enter_failed(link);
658 return r;
659 }
660
661 return 0;
662}
663
664static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
665 Link *link = userdata;
666 int r;
667
1746cf2a
TG
668 assert(link);
669
670 if (link->state == LINK_STATE_FAILED)
671 return 1;
672
ef1ba606
TG
673 r = sd_rtnl_message_get_errno(m);
674 if (r < 0) {
3333d748 675 log_warning_link(link, "could not get state: %s", strerror(-r));
ef1ba606
TG
676 link_enter_failed(link);
677 }
678
39032b87 679 log_debug_link(link, "got link state");
1746cf2a 680
5eb036ca
TG
681 link_update(link, m);
682
ef1ba606
TG
683 return 1;
684}
685
686static int link_get(Link *link) {
687 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
688 int r;
689
690 assert(link);
691 assert(link->manager);
692 assert(link->manager->rtnl);
693
39032b87 694 log_debug_link(link, "requesting link status");
449f7554 695
ef1ba606
TG
696 r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
697 if (r < 0) {
39032b87 698 log_error_link(link, "Could not allocate RTM_GETLINK message");
ef1ba606
TG
699 return r;
700 }
701
702 r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
703 if (r < 0) {
3333d748
ZJS
704 log_error_link(link,
705 "Could not send rtnetlink message: %s", strerror(-r));
ef1ba606 706 return r;
dd3efc09 707 }
02b59d57
TG
708
709 return 0;
710}
711
712int link_configure(Link *link) {
713 int r;
714
ef1ba606
TG
715 assert(link);
716 assert(link->network);
717 assert(link->state == _LINK_STATE_INVALID);
718
dd3efc09 719 r = link_get(link);
ef1ba606
TG
720 if (r < 0) {
721 link_enter_failed(link);
722 return r;
723 }
dd3efc09 724
1746cf2a 725 return link_enter_join_bridge(link);
f579559b 726}
dd3efc09 727
22936833
TG
728int link_update(Link *link, sd_rtnl_message *m) {
729 unsigned flags;
730 int r;
731
dd3efc09 732 assert(link);
22936833
TG
733 assert(m);
734
1746cf2a
TG
735 if (link->state == LINK_STATE_FAILED)
736 return 0;
737
22936833
TG
738 r = sd_rtnl_message_link_get_flags(m, &flags);
739 if (r < 0) {
39032b87 740 log_warning_link(link, "could not get link flags");
22936833
TG
741 return r;
742 }
dd3efc09 743
ff254138 744 return link_update_flags(link, flags);
dd3efc09 745}