]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-link.c
build: colorize gcc only if on tty
[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>
4cc7a82c 24#include <unistd.h>
f579559b 25
0b1831c2 26#include "networkd-link.h"
3be1d7e0 27#include "networkd-netdev.h"
f579559b 28#include "libudev-private.h"
505f8da7 29#include "udev-util.h"
f579559b 30#include "util.h"
505f8da7 31#include "virt.h"
1346b1f0 32#include "bus-util.h"
c6f7c917 33#include "network-internal.h"
ed942a9e 34#include "conf-parser.h"
f579559b 35
fe8db0c5
TG
36#include "dhcp-lease-internal.h"
37
78c958f8
TG
38static bool link_dhcp6_enabled(Link *link) {
39 if (link->flags & IFF_LOOPBACK)
40 return false;
41
42 if (!link->network)
43 return false;
44
45 return IN_SET(link->network->dhcp, DHCP_SUPPORT_V6, DHCP_SUPPORT_BOTH);
46}
47
48static bool link_dhcp4_enabled(Link *link) {
49 if (link->flags & IFF_LOOPBACK)
50 return false;
51
52 if (!link->network)
53 return false;
54
55 return IN_SET(link->network->dhcp, DHCP_SUPPORT_V4, DHCP_SUPPORT_BOTH);
56}
57
58static bool link_dhcp4_server_enabled(Link *link) {
59 if (link->flags & IFF_LOOPBACK)
60 return false;
61
62 if (!link->network)
63 return false;
64
65 return link->network->dhcp_server;
66}
67
68static bool link_ipv4ll_enabled(Link *link) {
69 if (link->flags & IFF_LOOPBACK)
70 return false;
71
72 if (!link->network)
73 return false;
74
75 return link->network->ipv4ll;
76}
77
51d18171
TG
78#define FLAG_STRING(string, flag, old, new) \
79 (((old ^ new) & flag) \
80 ? ((old & flag) ? (" -" string) : (" +" string)) \
81 : "")
82
83static int link_update_flags(Link *link, sd_rtnl_message *m) {
84 unsigned flags, unknown_flags_added, unknown_flags_removed, unknown_flags;
85 uint8_t operstate;
86 int r;
87
88 assert(link);
89
90 r = sd_rtnl_message_link_get_flags(m, &flags);
91 if (r < 0) {
92 log_warning_link(link, "Could not get link flags");
93 return r;
94 }
95
96 r = sd_rtnl_message_read_u8(m, IFLA_OPERSTATE, &operstate);
97 if (r < 0)
98 /* if we got a message without operstate, take it to mean
99 the state was unchanged */
100 operstate = link->kernel_operstate;
101
102 if ((link->flags == flags) && (link->kernel_operstate == operstate))
103 return 0;
104
105 if (link->flags != flags) {
106 log_debug_link(link, "flags change:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
107 FLAG_STRING("LOOPBACK", IFF_LOOPBACK, link->flags, flags),
108 FLAG_STRING("MASTER", IFF_MASTER, link->flags, flags),
109 FLAG_STRING("SLAVE", IFF_SLAVE, link->flags, flags),
110 FLAG_STRING("UP", IFF_UP, link->flags, flags),
111 FLAG_STRING("DORMANT", IFF_DORMANT, link->flags, flags),
112 FLAG_STRING("LOWER_UP", IFF_LOWER_UP, link->flags, flags),
113 FLAG_STRING("RUNNING", IFF_RUNNING, link->flags, flags),
114 FLAG_STRING("MULTICAST", IFF_MULTICAST, link->flags, flags),
115 FLAG_STRING("BROADCAST", IFF_BROADCAST, link->flags, flags),
116 FLAG_STRING("POINTOPOINT", IFF_POINTOPOINT, link->flags, flags),
117 FLAG_STRING("PROMISC", IFF_PROMISC, link->flags, flags),
118 FLAG_STRING("ALLMULTI", IFF_ALLMULTI, link->flags, flags),
119 FLAG_STRING("PORTSEL", IFF_PORTSEL, link->flags, flags),
120 FLAG_STRING("AUTOMEDIA", IFF_AUTOMEDIA, link->flags, flags),
121 FLAG_STRING("DYNAMIC", IFF_DYNAMIC, link->flags, flags),
122 FLAG_STRING("NOARP", IFF_NOARP, link->flags, flags),
123 FLAG_STRING("NOTRAILERS", IFF_NOTRAILERS, link->flags, flags),
124 FLAG_STRING("DEBUG", IFF_DEBUG, link->flags, flags),
125 FLAG_STRING("ECHO", IFF_ECHO, link->flags, flags));
126
127 unknown_flags = ~(IFF_LOOPBACK | IFF_MASTER | IFF_SLAVE | IFF_UP |
128 IFF_DORMANT | IFF_LOWER_UP | IFF_RUNNING |
129 IFF_MULTICAST | IFF_BROADCAST | IFF_POINTOPOINT |
130 IFF_PROMISC | IFF_ALLMULTI | IFF_PORTSEL |
131 IFF_AUTOMEDIA | IFF_DYNAMIC | IFF_NOARP |
132 IFF_NOTRAILERS | IFF_DEBUG | IFF_ECHO);
133 unknown_flags_added = ((link->flags ^ flags) & flags & unknown_flags);
134 unknown_flags_removed = ((link->flags ^ flags) & link->flags & unknown_flags);
135
136 /* link flags are currently at most 18 bits, let's align to
137 * printing 20 */
138 if (unknown_flags_added)
139 log_debug_link(link,
140 "unknown link flags gained: %#.5x (ignoring)",
141 unknown_flags_added);
142
143 if (unknown_flags_removed)
144 log_debug_link(link,
145 "unknown link flags lost: %#.5x (ignoring)",
146 unknown_flags_removed);
147 }
148
149 link->flags = flags;
150 link->kernel_operstate = operstate;
151
152 link_save(link);
153
154 return 0;
155}
156
505f8da7 157static int link_new(Manager *manager, sd_rtnl_message *message, Link **ret) {
14b746f7 158 _cleanup_link_unref_ Link *link = NULL;
505f8da7 159 uint16_t type;
ca4e095a 160 const char *ifname;
505f8da7 161 int r, ifindex;
f579559b 162
0c2f9b84 163 assert(manager);
505f8da7 164 assert(message);
f579559b
TG
165 assert(ret);
166
505f8da7
TG
167 r = sd_rtnl_message_get_type(message, &type);
168 if (r < 0)
169 return r;
170 else if (type != RTM_NEWLINK)
171 return -EINVAL;
172
173 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
174 if (r < 0)
175 return r;
176 else if (ifindex <= 0)
177 return -EINVAL;
178
179 r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &ifname);
180 if (r < 0)
181 return r;
182
f579559b
TG
183 link = new0(Link, 1);
184 if (!link)
185 return -ENOMEM;
186
14b746f7 187 link->n_ref = 1;
5a3eb5a7 188 link->manager = manager;
8434fd5c 189 link->state = LINK_STATE_PENDING;
505f8da7
TG
190 link->ifindex = ifindex;
191 link->ifname = strdup(ifname);
192 if (!link->ifname)
193 return -ENOMEM;
f579559b 194
512922f8
TG
195 r = sd_rtnl_message_read_ether_addr(message, IFLA_ADDRESS, &link->mac);
196 if (r < 0)
1a941ac4 197 log_debug_link(link, "MAC address not found for new device, continuing without");
512922f8 198
3c9b8860
TG
199 r = asprintf(&link->state_file, "/run/systemd/netif/links/%d",
200 link->ifindex);
fe8db0c5 201 if (r < 0)
315db1a8 202 return -ENOMEM;
fe8db0c5 203
3c9b8860
TG
204 r = asprintf(&link->lease_file, "/run/systemd/netif/leases/%d",
205 link->ifindex);
68a8723c
TG
206 if (r < 0)
207 return -ENOMEM;
208
ae06ab10
TG
209 r = hashmap_ensure_allocated(&manager->links, NULL, NULL);
210 if (r < 0)
211 return r;
212
213 r = hashmap_put(manager->links, INT_TO_PTR(link->ifindex), link);
f579559b
TG
214 if (r < 0)
215 return r;
216
51d18171
TG
217 r = link_update_flags(link, message);
218 if (r < 0)
219 return r;
220
f579559b
TG
221 *ret = link;
222 link = NULL;
223
224 return 0;
225}
226
14b746f7 227static void link_free(Link *link) {
428fd0a7
TG
228 Address *address;
229
f579559b
TG
230 if (!link)
231 return;
232
428fd0a7
TG
233 while ((address = link->addresses)) {
234 LIST_REMOVE(addresses, link->addresses, address);
235 address_free(address);
236 }
237
11bf3cce
LP
238 while ((address = link->pool_addresses)) {
239 LIST_REMOVE(addresses, link->pool_addresses, address);
240 address_free(address);
241 }
242
e5b04c8d 243 sd_dhcp_client_unref(link->dhcp_client);
a6cc569e 244 sd_dhcp_lease_unref(link->dhcp_lease);
f5be5601 245
68a8723c
TG
246 unlink(link->lease_file);
247 free(link->lease_file);
248
56cd007a 249 sd_ipv4ll_unref(link->ipv4ll);
4138fb2c 250 sd_dhcp6_client_unref(link->dhcp6_client);
6b66097b 251 sd_icmp6_nd_unref(link->icmp6_router_discovery);
4138fb2c 252
28aeb07f 253 if (link->manager)
ae06ab10 254 hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
f579559b 255
c166a070 256 free(link->ifname);
68a8723c
TG
257
258 unlink(link->state_file);
fe8db0c5 259 free(link->state_file);
c166a070 260
b5db00e5
UTL
261 udev_device_unref(link->udev_device);
262
f579559b
TG
263 free(link);
264}
265
14b746f7
TG
266Link *link_unref(Link *link) {
267 if (link && (-- link->n_ref <= 0))
268 link_free(link);
269
270 return NULL;
271}
272
273Link *link_ref(Link *link) {
274 if (link)
275 assert_se(++ link->n_ref >= 2);
276
277 return link;
278}
279
11a7f229
TG
280int link_get(Manager *m, int ifindex, Link **ret) {
281 Link *link;
11a7f229
TG
282
283 assert(m);
11a7f229
TG
284 assert(ifindex);
285 assert(ret);
286
ae06ab10 287 link = hashmap_get(m->links, INT_TO_PTR(ifindex));
11a7f229
TG
288 if (!link)
289 return -ENODEV;
290
291 *ret = link;
292
293 return 0;
294}
295
370e9930
TG
296void link_drop(Link *link) {
297 if (!link || link->state == LINK_STATE_LINGER)
298 return;
299
300 link->state = LINK_STATE_LINGER;
301
7619683b 302 log_debug_link(link, "link removed");
370e9930
TG
303
304 link_unref(link);
305
306 return;
307}
308
57bd6899
TG
309static void link_enter_unmanaged(Link *link) {
310 assert(link);
311
df9aa406 312 log_debug_link(link, "unmanaged");
57bd6899
TG
313
314 link->state = LINK_STATE_UNMANAGED;
315
316 link_save(link);
317}
318
111bb8f9
TG
319static int link_stop_clients(Link *link) {
320 int r = 0, k;
321
322 assert(link);
323 assert(link->manager);
324 assert(link->manager->event);
325
326 if (!link->network)
327 return 0;
328
ba179154 329 if (link->dhcp_client) {
111bb8f9
TG
330 k = sd_dhcp_client_stop(link->dhcp_client);
331 if (k < 0) {
3c9b8860
TG
332 log_warning_link(link, "Could not stop DHCPv4 client: %s",
333 strerror(-r));
111bb8f9
TG
334 r = k;
335 }
336 }
337
ba179154 338 if (link->ipv4ll) {
111bb8f9
TG
339 k = sd_ipv4ll_stop(link->ipv4ll);
340 if (k < 0) {
3c9b8860
TG
341 log_warning_link(link, "Could not stop IPv4 link-local: %s",
342 strerror(-r));
111bb8f9
TG
343 r = k;
344 }
345 }
346
ba179154 347 if (link->dhcp_server) {
dd43110f
TG
348 k = sd_dhcp_server_stop(link->dhcp_server);
349 if (k < 0) {
3c9b8860
TG
350 log_warning_link(link, "Could not stop DHCPv4 server: %s",
351 strerror(-r));
dd43110f
TG
352 r = k;
353 }
354 }
355
ba179154 356 if(link->icmp6_router_discovery) {
1873a3d3
PF
357
358 if (link->dhcp6_client) {
359 k = sd_dhcp6_client_stop(link->dhcp6_client);
360 if (k < 0) {
3c9b8860
TG
361 log_warning_link(link, "Could not stop DHCPv6 client: %s",
362 strerror(-r));
1873a3d3
PF
363 r = k;
364 }
365 }
4138fb2c 366
1873a3d3 367 k = sd_icmp6_nd_stop(link->icmp6_router_discovery);
4138fb2c 368 if (k < 0) {
3c9b8860
TG
369 log_warning_link(link,
370 "Could not stop ICMPv6 router discovery: %s",
371 strerror(-r));
4138fb2c
PF
372 r = k;
373 }
374 }
375
111bb8f9
TG
376 return r;
377}
378
b22d8a00 379void link_enter_failed(Link *link) {
ef1ba606 380 assert(link);
f882c247 381
370e9930 382 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
2139694e
TG
383 return;
384
39032b87 385 log_warning_link(link, "failed");
449f7554 386
ef1ba606 387 link->state = LINK_STATE_FAILED;
fe8db0c5 388
111bb8f9
TG
389 link_stop_clients(link);
390
fe8db0c5 391 link_save(link);
f882c247
TG
392}
393
4f434938
LP
394static Address* link_find_dhcp_server_address(Link *link) {
395 Address *address;
396
397 assert(link);
398 assert(link->network);
399
400 /* The the first statically configured address if there is any */
401 LIST_FOREACH(addresses, address, link->network->static_addresses) {
402
403 if (address->family != AF_INET)
404 continue;
405
af93291c 406 if (in_addr_is_null(address->family, &address->in_addr))
4f434938
LP
407 continue;
408
409 return address;
410 }
411
412 /* If that didn't work, find a suitable address we got from the pool */
413 LIST_FOREACH(addresses, address, link->pool_addresses) {
414 if (address->family != AF_INET)
415 continue;
416
417 return address;
418 }
419
420 return NULL;
421}
422
dd43110f
TG
423static int link_enter_configured(Link *link) {
424 int r;
425
426 assert(link);
427 assert(link->network);
428 assert(link->state == LINK_STATE_SETTING_ROUTES);
429
78c958f8 430 if (link_dhcp4_server_enabled(link) &&
7c16313f 431 !sd_dhcp_server_is_running(link->dhcp_server)) {
4f434938
LP
432 struct in_addr pool_start;
433 Address *address;
434
435 address = link_find_dhcp_server_address(link);
436 if (!address) {
3c9b8860
TG
437 log_warning_link(link,
438 "Failed to find suitable address for DHCPv4 server instance.");
4f434938
LP
439 link_enter_failed(link);
440 return 0;
441 }
442
dd43110f
TG
443 log_debug_link(link, "offering DHCPv4 leases");
444
3c9b8860
TG
445 r = sd_dhcp_server_set_address(link->dhcp_server,
446 &address->in_addr.in,
447 address->prefixlen);
4f434938
LP
448 if (r < 0)
449 return r;
450
451 /* offer 32 addresses starting from the address following the server address */
452 pool_start.s_addr = htobe32(be32toh(address->in_addr.in.s_addr) + 1);
453 r = sd_dhcp_server_set_lease_pool(link->dhcp_server,
454 &pool_start, 32);
455 if (r < 0)
456 return r;
457
458 /* TODO:
459 r = sd_dhcp_server_set_router(link->dhcp_server,
460 &main_address->in_addr.in);
461 if (r < 0)
462 return r;
463
464 r = sd_dhcp_server_set_prefixlen(link->dhcp_server,
465 main_address->prefixlen);
466 if (r < 0)
467 return r;
468 */
469
dd43110f
TG
470 r = sd_dhcp_server_start(link->dhcp_server);
471 if (r < 0) {
472 log_warning_link(link, "could not start DHCPv4 server "
473 "instance: %s", strerror(-r));
474
475 link_enter_failed(link);
476
477 return 0;
478 }
479 }
480
481 log_info_link(link, "link configured");
482
483 link->state = LINK_STATE_CONFIGURED;
484
485 link_save(link);
486
487 return 0;
488}
489
3c9b8860
TG
490void link_client_handler(Link *link) {
491 assert(link);
492 assert(link->network);
493
494 if (!link->static_configured)
495 return;
496
78c958f8 497 if (link_ipv4ll_enabled(link))
3c9b8860
TG
498 if (!link->ipv4ll_address ||
499 !link->ipv4ll_route)
500 return;
501
78c958f8 502 if (link_dhcp4_enabled(link) && !link->dhcp4_configured)
3c9b8860
TG
503 return;
504
9fdaa992
TG
505 if (link->state != LINK_STATE_CONFIGURED)
506 link_enter_configured(link);
3c9b8860
TG
507
508 return;
509}
510
f882c247 511static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
5da8149f 512 _cleanup_link_unref_ Link *link = userdata;
f882c247
TG
513 int r;
514
3c9b8860 515 assert(link->link_messages > 0);
370e9930
TG
516 assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES,
517 LINK_STATE_SETTING_ROUTES, LINK_STATE_FAILED,
518 LINK_STATE_LINGER));
f882c247 519
3c9b8860 520 link->link_messages --;
f882c247 521
77a008c0 522 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
f882c247
TG
523 return 1;
524
525 r = sd_rtnl_message_get_errno(m);
c166a070 526 if (r < 0 && r != -EEXIST)
c9ccc19f 527 log_struct_link(LOG_WARNING, link,
97578344 528 "MESSAGE=%-*s: could not set route: %s",
987efa17 529 IFNAMSIZ,
c9ccc19f
TG
530 link->ifname, strerror(-r),
531 "ERRNO=%d", -r,
532 NULL);
f882c247 533
3c9b8860 534 if (link->link_messages == 0) {
39032b87 535 log_debug_link(link, "routes set");
3c9b8860
TG
536 link->static_configured = true;
537 link_client_handler(link);
dd3efc09 538 }
f882c247
TG
539
540 return 1;
541}
542
543static int link_enter_set_routes(Link *link) {
a6cc569e 544 Route *rt;
f882c247
TG
545 int r;
546
547 assert(link);
548 assert(link->network);
ef1ba606 549 assert(link->state == LINK_STATE_SETTING_ADDRESSES);
f882c247 550
ef1ba606 551 link->state = LINK_STATE_SETTING_ROUTES;
f882c247 552
3d3d4255 553 LIST_FOREACH(routes, rt, link->network->static_routes) {
a6cc569e 554 r = route_configure(rt, link, &route_handler);
dd3efc09 555 if (r < 0) {
3333d748 556 log_warning_link(link,
3c9b8860 557 "could not set routes: %s",
b1666580 558 strerror(-r));
3c9b8860 559 link_enter_failed(link);
a6cc569e
TG
560 return r;
561 }
562
3c9b8860 563 link->link_messages ++;
8ddbeaa2 564 }
f5be5601 565
3c9b8860
TG
566 if (link->link_messages == 0) {
567 link->static_configured = true;
568 link_client_handler(link);
431ca2ce
TG
569 } else
570 log_debug_link(link, "setting routes");
f882c247
TG
571
572 return 0;
573}
574
b22d8a00 575int link_route_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
5da8149f 576 _cleanup_link_unref_ Link *link = userdata;
5c1d3fc9
UTL
577 int r;
578
579 assert(m);
580 assert(link);
581 assert(link->ifname);
582
5da8149f 583 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
5c1d3fc9
UTL
584 return 1;
585
586 r = sd_rtnl_message_get_errno(m);
b90b025a 587 if (r < 0 && r != -ESRCH)
5c1d3fc9 588 log_struct_link(LOG_WARNING, link,
97578344 589 "MESSAGE=%-*s: could not drop route: %s",
987efa17 590 IFNAMSIZ,
5c1d3fc9
UTL
591 link->ifname, strerror(-r),
592 "ERRNO=%d", -r,
593 NULL);
594
5bdd314c 595 return 1;
5c1d3fc9
UTL
596}
597
b22d8a00 598int link_get_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
4958aee4
TG
599 _cleanup_link_unref_ Link *link = userdata;
600 int r;
601
602 assert(rtnl);
603 assert(m);
604 assert(link);
605 assert(link->manager);
606
607 for (; m; m = sd_rtnl_message_next(m)) {
608 r = sd_rtnl_message_get_errno(m);
609 if (r < 0) {
3c9b8860
TG
610 log_debug_link(link, "getting address failed: %s",
611 strerror(-r));
4958aee4
TG
612 continue;
613 }
614
615 r = link_rtnl_process_address(rtnl, m, link->manager);
616 if (r < 0)
3c9b8860
TG
617 log_warning_link(link, "could not process address: %s",
618 strerror(-r));
4958aee4
TG
619 }
620
621 return 1;
622}
623
f882c247 624static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
5da8149f 625 _cleanup_link_unref_ Link *link = userdata;
f882c247
TG
626 int r;
627
4958aee4 628 assert(rtnl);
f5be5601
TG
629 assert(m);
630 assert(link);
631 assert(link->ifname);
3c9b8860 632 assert(link->link_messages > 0);
370e9930
TG
633 assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES,
634 LINK_STATE_FAILED, LINK_STATE_LINGER));
f882c247 635
3c9b8860 636 link->link_messages --;
f882c247 637
5da8149f 638 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
f882c247
TG
639 return 1;
640
641 r = sd_rtnl_message_get_errno(m);
c166a070 642 if (r < 0 && r != -EEXIST)
c9ccc19f 643 log_struct_link(LOG_WARNING, link,
97578344 644 "MESSAGE=%-*s: could not set address: %s",
987efa17 645 IFNAMSIZ,
3333d748
ZJS
646 link->ifname, strerror(-r),
647 "ERRNO=%d", -r,
648 NULL);
5bdd314c 649 else if (r >= 0) {
4958aee4
TG
650 /* calling handler directly so take a ref */
651 link_ref(link);
652 link_get_address_handler(rtnl, m, link);
653 }
f882c247 654
3c9b8860 655 if (link->link_messages == 0) {
39032b87 656 log_debug_link(link, "addresses set");
ef1ba606 657 link_enter_set_routes(link);
dd3efc09 658 }
f882c247
TG
659
660 return 1;
661}
662
663static int link_enter_set_addresses(Link *link) {
a6cc569e 664 Address *ad;
f882c247
TG
665 int r;
666
667 assert(link);
668 assert(link->network);
f5be5601 669 assert(link->state != _LINK_STATE_INVALID);
f882c247 670
ef1ba606 671 link->state = LINK_STATE_SETTING_ADDRESSES;
f882c247 672
3d3d4255 673 LIST_FOREACH(addresses, ad, link->network->static_addresses) {
a6cc569e 674 r = address_configure(ad, link, &address_handler);
dd3efc09 675 if (r < 0) {
3333d748 676 log_warning_link(link,
3c9b8860 677 "could not set addresses: %s",
a6cc569e 678 strerror(-r));
f5be5601
TG
679 link_enter_failed(link);
680 return r;
681 }
682
3c9b8860 683 link->link_messages ++;
f882c247
TG
684 }
685
3c9b8860 686 if (link->link_messages == 0) {
431ca2ce
TG
687 link_enter_set_routes(link);
688 } else
689 log_debug_link(link, "setting addresses");
690
f882c247
TG
691 return 0;
692}
693
b22d8a00 694int link_address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
5da8149f 695 _cleanup_link_unref_ Link *link = userdata;
ff254138
TG
696 int r;
697
698 assert(m);
699 assert(link);
700 assert(link->ifname);
701
5da8149f 702 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
ff254138
TG
703 return 1;
704
705 r = sd_rtnl_message_get_errno(m);
b90b025a 706 if (r < 0 && r != -EADDRNOTAVAIL)
c9ccc19f 707 log_struct_link(LOG_WARNING, link,
97578344 708 "MESSAGE=%-*s: could not drop address: %s",
987efa17 709 IFNAMSIZ,
c9ccc19f
TG
710 link->ifname, strerror(-r),
711 "ERRNO=%d", -r,
712 NULL);
ff254138 713
5bdd314c 714 return 1;
ff254138
TG
715}
716
3c9b8860
TG
717static int set_hostname_handler(sd_bus *bus, sd_bus_message *m, void *userdata,
718 sd_bus_error *ret_error) {
5da8149f 719 _cleanup_link_unref_ Link *link = userdata;
1346b1f0
TG
720 int r;
721
b226d99b
TG
722 assert(link);
723
5da8149f 724 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
370e9930 725 return 1;
370e9930 726
1346b1f0
TG
727 r = sd_bus_message_get_errno(m);
728 if (r < 0)
70b34f5d
MM
729 r = -r;
730 if (r > 0)
3c9b8860
TG
731 log_warning_link(link, "Could not set hostname: %s",
732 strerror(r));
1346b1f0
TG
733
734 return 1;
735}
736
3c9b8860 737int link_set_hostname(Link *link, const char *hostname) {
1346b1f0
TG
738 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
739 int r = 0;
740
b226d99b
TG
741 assert(link);
742 assert(link->manager);
1346b1f0
TG
743 assert(hostname);
744
b226d99b 745 log_debug_link(link, "Setting transient hostname: '%s'", hostname);
1346b1f0 746
3c9b8860
TG
747 if (!link->manager->bus) {
748 /* TODO: replace by assert when we can rely on kdbus */
749 log_info_link(link,
750 "Not connected to system bus, ignoring transient hostname.");
bcbca829
TG
751 return 0;
752 }
753
1346b1f0 754 r = sd_bus_message_new_method_call(
b226d99b 755 link->manager->bus,
151b9b96 756 &m,
1346b1f0
TG
757 "org.freedesktop.hostname1",
758 "/org/freedesktop/hostname1",
759 "org.freedesktop.hostname1",
151b9b96 760 "SetHostname");
1346b1f0
TG
761 if (r < 0)
762 return r;
763
764 r = sd_bus_message_append(m, "sb", hostname, false);
765 if (r < 0)
766 return r;
767
3c9b8860
TG
768 r = sd_bus_call_async(link->manager->bus, NULL, m, set_hostname_handler,
769 link, 0);
5da8149f 770 if (r < 0) {
3c9b8860
TG
771 log_error_link(link, "Could not set transient hostname: %s",
772 strerror(-r));
5da8149f
TG
773 return r;
774 }
b226d99b
TG
775
776 link_ref(link);
1346b1f0 777
5da8149f 778 return 0;
1346b1f0
TG
779}
780
4f882b2a 781static int set_mtu_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
5da8149f 782 _cleanup_link_unref_ Link *link = userdata;
4f882b2a
TG
783 int r;
784
785 assert(m);
786 assert(link);
787 assert(link->ifname);
788
5da8149f 789 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
4f882b2a
TG
790 return 1;
791
792 r = sd_rtnl_message_get_errno(m);
c9ccc19f
TG
793 if (r < 0)
794 log_struct_link(LOG_WARNING, link,
97578344
TG
795 "MESSAGE=%-*s: could not set MTU: %s",
796 IFNAMSIZ, link->ifname, strerror(-r),
c9ccc19f
TG
797 "ERRNO=%d", -r,
798 NULL);
4f882b2a
TG
799
800 return 1;
801}
802
3c9b8860 803int link_set_mtu(Link *link, uint32_t mtu) {
cf6a8911 804 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
4f882b2a
TG
805 int r;
806
807 assert(link);
808 assert(link->manager);
809 assert(link->manager->rtnl);
810
811 log_debug_link(link, "setting MTU: %" PRIu32, mtu);
812
151b9b96
LP
813 r = sd_rtnl_message_new_link(link->manager->rtnl, &req,
814 RTM_SETLINK, link->ifindex);
4f882b2a
TG
815 if (r < 0) {
816 log_error_link(link, "Could not allocate RTM_SETLINK message");
817 return r;
818 }
819
820 r = sd_rtnl_message_append_u32(req, IFLA_MTU, mtu);
821 if (r < 0) {
822 log_error_link(link, "Could not append MTU: %s", strerror(-r));
823 return r;
824 }
825
3c9b8860
TG
826 r = sd_rtnl_call_async(link->manager->rtnl, req, set_mtu_handler, link,
827 0, NULL);
4f882b2a
TG
828 if (r < 0) {
829 log_error_link(link,
3c9b8860
TG
830 "Could not send rtnetlink message: %s",
831 strerror(-r));
4f882b2a
TG
832 return r;
833 }
834
ae941762 835 link_ref(link);
b226d99b 836
4f882b2a
TG
837 return 0;
838}
839
4138fb2c
PF
840static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
841 Link *link = userdata;
842
843 assert(link);
844 assert(link->network);
845 assert(link->manager);
846
847 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
848 return;
849
850 switch(event) {
851 case DHCP6_EVENT_STOP:
852 case DHCP6_EVENT_RESEND_EXPIRE:
853 case DHCP6_EVENT_RETRANS_MAX:
854 case DHCP6_EVENT_IP_ACQUIRE:
855 log_debug_link(link, "DHCPv6 event %d", event);
856
857 break;
858
859 default:
860 if (event < 0)
861 log_warning_link(link, "DHCPv6 error: %s",
862 strerror(-event));
863 else
864 log_warning_link(link, "DHCPv6 unknown event: %d",
865 event);
866 return;
867 }
868}
869
870static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) {
871 Link *link = userdata;
872 int r;
873
874 assert(link);
875 assert(link->network);
876 assert(link->manager);
877
878 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
879 return;
880
881 switch(event) {
882 case ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE:
883 case ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER:
884 return;
885
886 case ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT:
887 case ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED:
888 break;
889
890 default:
891 if (event < 0)
892 log_warning_link(link, "ICMPv6 error: %s",
893 strerror(-event));
894 else
895 log_warning_link(link, "ICMPv6 unknown event: %d",
896 event);
897
898 return;
899 }
900
901 if (link->dhcp6_client)
902 return;
903
904 r = sd_dhcp6_client_new(&link->dhcp6_client);
905 if (r < 0)
906 return;
907
908 r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0);
909 if (r < 0) {
910 link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
911 return;
912 }
913
914 r = sd_dhcp6_client_set_mac(link->dhcp6_client, &link->mac);
915 if (r < 0) {
916 link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
917 return;
918 }
919
920 r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex);
921 if (r < 0) {
922 link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
923 return;
924 }
925
926 r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler,
927 link);
928 if (r < 0) {
929 link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
930 return;
931 }
932
933 r = sd_dhcp6_client_start(link->dhcp6_client);
934 if (r < 0)
935 link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
936}
937
ff254138
TG
938static int link_acquire_conf(Link *link) {
939 int r;
940
941 assert(link);
942 assert(link->network);
ff254138
TG
943 assert(link->manager);
944 assert(link->manager->event);
945
78c958f8 946 if (link_ipv4ll_enabled(link)) {
eb34d4af 947 assert(link->ipv4ll);
ff254138 948
5c1d3fc9
UTL
949 log_debug_link(link, "acquiring IPv4 link-local address");
950
951 r = sd_ipv4ll_start(link->ipv4ll);
124fa2c6
TG
952 if (r < 0) {
953 log_warning_link(link, "could not acquire IPv4 "
954 "link-local address");
ff254138 955 return r;
124fa2c6 956 }
5c1d3fc9
UTL
957 }
958
78c958f8 959 if (link_dhcp4_enabled(link)) {
eb34d4af 960 assert(link->dhcp_client);
ff254138 961
5c1d3fc9 962 log_debug_link(link, "acquiring DHCPv4 lease");
ab47d620 963
5c1d3fc9 964 r = sd_dhcp_client_start(link->dhcp_client);
124fa2c6
TG
965 if (r < 0) {
966 log_warning_link(link, "could not acquire DHCPv4 "
967 "lease");
5c1d3fc9 968 return r;
124fa2c6 969 }
5c1d3fc9 970 }
ff254138 971
78c958f8 972 if (link_dhcp6_enabled(link)) {
4138fb2c
PF
973 assert(link->icmp6_router_discovery);
974
975 log_debug_link(link, "discovering IPv6 routers");
976
977 r = sd_icmp6_router_solicitation_start(link->icmp6_router_discovery);
978 if (r < 0) {
3c9b8860
TG
979 log_warning_link(link,
980 "could not start IPv6 router discovery");
4138fb2c
PF
981 return r;
982 }
983 }
984
ff254138
TG
985 return 0;
986}
987
a61bb41c 988bool link_has_carrier(Link *link) {
deb2e523
TG
989 /* see Documentation/networking/operstates.txt in the kernel sources */
990
a61bb41c 991 if (link->kernel_operstate == IF_OPER_UP)
deb2e523
TG
992 return true;
993
a61bb41c 994 if (link->kernel_operstate == IF_OPER_UNKNOWN)
deb2e523 995 /* operstate may not be implemented, so fall back to flags */
a61bb41c 996 if ((link->flags & IFF_LOWER_UP) && !(link->flags & IFF_DORMANT))
deb2e523
TG
997 return true;
998
999 return false;
1000}
1001
dd3efc09 1002static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
5da8149f 1003 _cleanup_link_unref_ Link *link = userdata;
dd3efc09
TG
1004 int r;
1005
1746cf2a
TG
1006 assert(link);
1007
5da8149f 1008 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
1746cf2a
TG
1009 return 1;
1010
dd3efc09 1011 r = sd_rtnl_message_get_errno(m);
45ad2c13 1012 if (r < 0) {
9b86b393
TG
1013 /* we warn but don't fail the link, as it may
1014 be brought up later */
76800848 1015 log_struct_link(LOG_WARNING, link,
97578344 1016 "MESSAGE=%-*s: could not bring up interface: %s",
987efa17 1017 IFNAMSIZ,
c9ccc19f
TG
1018 link->ifname, strerror(-r),
1019 "ERRNO=%d", -r,
1020 NULL);
45ad2c13
TG
1021 }
1022
f882c247
TG
1023 return 1;
1024}
1025
1026static int link_up(Link *link) {
cf6a8911 1027 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
f579559b
TG
1028 int r;
1029
f882c247
TG
1030 assert(link);
1031 assert(link->manager);
1032 assert(link->manager->rtnl);
1033
39032b87 1034 log_debug_link(link, "bringing link up");
449f7554 1035
151b9b96
LP
1036 r = sd_rtnl_message_new_link(link->manager->rtnl, &req,
1037 RTM_SETLINK, link->ifindex);
f579559b 1038 if (r < 0) {
39032b87 1039 log_error_link(link, "Could not allocate RTM_SETLINK message");
f579559b
TG
1040 return r;
1041 }
1042
5d4795f3 1043 r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
fc25d7f8 1044 if (r < 0) {
3c9b8860
TG
1045 log_error_link(link, "Could not set link flags: %s",
1046 strerror(-r));
fc25d7f8
TG
1047 return r;
1048 }
1049
3c9b8860
TG
1050 r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link,
1051 0, NULL);
f579559b 1052 if (r < 0) {
3333d748 1053 log_error_link(link,
3c9b8860
TG
1054 "Could not send rtnetlink message: %s",
1055 strerror(-r));
f579559b
TG
1056 return r;
1057 }
1058
b226d99b
TG
1059 link_ref(link);
1060
f882c247
TG
1061 return 0;
1062}
1063
3f265037 1064static int link_joined(Link *link) {
f882c247
TG
1065 int r;
1066
ef1ba606 1067 assert(link);
f5be5601 1068 assert(link->network);
dd3efc09 1069
505f8da7
TG
1070 if (!(link->flags & IFF_UP)) {
1071 r = link_up(link);
1072 if (r < 0) {
1073 link_enter_failed(link);
1074 return r;
1075 }
ef1ba606 1076 }
f882c247 1077
fb6730c4 1078 return link_enter_set_addresses(link);
02b59d57
TG
1079}
1080
3c9b8860
TG
1081static int netdev_join_handler(sd_rtnl *rtnl, sd_rtnl_message *m,
1082 void *userdata) {
5da8149f 1083 _cleanup_link_unref_ Link *link = userdata;
02b59d57
TG
1084 int r;
1085
1746cf2a 1086 assert(link);
ef1ba606 1087 assert(link->network);
02b59d57 1088
52433f6b
TG
1089 link->enslaving --;
1090
5da8149f 1091 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
02b59d57
TG
1092 return 1;
1093
1094 r = sd_rtnl_message_get_errno(m);
856f962c 1095 if (r < 0 && r != -EEXIST) {
c9ccc19f 1096 log_struct_link(LOG_ERR, link,
3f265037 1097 "MESSAGE=%-*s: could not join netdev: %s",
987efa17 1098 IFNAMSIZ,
c9ccc19f
TG
1099 link->ifname, strerror(-r),
1100 "ERRNO=%d", -r,
1101 NULL);
ef1ba606
TG
1102 link_enter_failed(link);
1103 return 1;
ba179154
TG
1104 } else
1105 log_debug_link(link, "joined netdev");
02b59d57 1106
856f962c 1107 if (link->enslaving <= 0)
3f265037 1108 link_joined(link);
02b59d57
TG
1109
1110 return 1;
1111}
1112
3f265037 1113static int link_enter_join_netdev(Link *link) {
6a0a2f86 1114 NetDev *netdev;
672682a6 1115 Iterator i;
02b59d57
TG
1116 int r;
1117
1118 assert(link);
1119 assert(link->network);
8434fd5c 1120 assert(link->state == LINK_STATE_PENDING);
02b59d57 1121
52433f6b 1122 link->state = LINK_STATE_ENSLAVING;
02b59d57 1123
fe8db0c5
TG
1124 link_save(link);
1125
7951dea2
SS
1126 if (!link->network->bridge &&
1127 !link->network->bond &&
6a0a2f86 1128 hashmap_isempty(link->network->stacked_netdevs))
3f265037 1129 return link_joined(link);
02b59d57 1130
d9c67ea1 1131 if (link->network->bond) {
52433f6b 1132 log_struct_link(LOG_DEBUG, link,
97578344 1133 "MESSAGE=%-*s: enslaving by '%s'",
987efa17 1134 IFNAMSIZ,
af4e9e2c 1135 link->ifname, link->network->bond->ifname,
aa9f1140 1136 NETDEVIF(link->network->bond),
52433f6b 1137 NULL);
449f7554 1138
3f265037 1139 r = netdev_join(link->network->bond, link, &netdev_join_handler);
52433f6b
TG
1140 if (r < 0) {
1141 log_struct_link(LOG_WARNING, link,
3f265037 1142 "MESSAGE=%-*s: could not join netdev '%s': %s",
987efa17 1143 IFNAMSIZ,
3c9b8860
TG
1144 link->ifname, link->network->bond->ifname,
1145 strerror(-r),
aa9f1140 1146 NETDEVIF(link->network->bond),
52433f6b
TG
1147 NULL);
1148 link_enter_failed(link);
1149 return r;
1150 }
1151
0ad6148e
MO
1152 link->enslaving ++;
1153 }
1154
d9c67ea1 1155 if (link->network->bridge) {
0ad6148e 1156 log_struct_link(LOG_DEBUG, link,
97578344 1157 "MESSAGE=%-*s: enslaving by '%s'",
987efa17 1158 IFNAMSIZ,
af4e9e2c 1159 link->ifname, link->network->bridge->ifname,
aa9f1140 1160 NETDEVIF(link->network->bridge),
0ad6148e
MO
1161 NULL);
1162
3c9b8860
TG
1163 r = netdev_join(link->network->bridge, link,
1164 &netdev_join_handler);
0ad6148e
MO
1165 if (r < 0) {
1166 log_struct_link(LOG_WARNING, link,
3f265037 1167 "MESSAGE=%-*s: could not join netdev '%s': %s",
987efa17 1168 IFNAMSIZ,
3c9b8860
TG
1169 link->ifname, link->network->bridge->ifname,
1170 strerror(-r),
aa9f1140 1171 NETDEVIF(link->network->bridge),
0ad6148e
MO
1172 NULL);
1173 link_enter_failed(link);
1174 return r;
1175 }
1176
52433f6b
TG
1177 link->enslaving ++;
1178 }
1179
6a0a2f86 1180 HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) {
7951dea2 1181 log_struct_link(LOG_DEBUG, link,
97578344 1182 "MESSAGE=%-*s: enslaving by '%s'",
987efa17 1183 IFNAMSIZ,
3c9b8860
TG
1184 link->ifname, netdev->ifname, NETDEVIF(netdev),
1185 NULL);
7951dea2 1186
6a0a2f86 1187 r = netdev_join(netdev, link, &netdev_join_handler);
7951dea2
SS
1188 if (r < 0) {
1189 log_struct_link(LOG_WARNING, link,
3f265037 1190 "MESSAGE=%-*s: could not join netdev '%s': %s",
987efa17 1191 IFNAMSIZ,
3c9b8860
TG
1192 link->ifname, netdev->ifname,
1193 strerror(-r),
6a0a2f86 1194 NETDEVIF(netdev), NULL);
326cb406
SS
1195 link_enter_failed(link);
1196 return r;
1197 }
1198
326cb406
SS
1199 link->enslaving ++;
1200 }
1201
ef1ba606
TG
1202 return 0;
1203}
1204
a748b692 1205static int link_configure(Link *link) {
02b59d57
TG
1206 int r;
1207
ef1ba606 1208 assert(link);
b22d8a00 1209 assert(link->network);
8434fd5c 1210 assert(link->state == LINK_STATE_PENDING);
a748b692 1211
78c958f8 1212 if (link_ipv4ll_enabled(link)) {
b22d8a00 1213 r = ipv4ll_configure(link);
eb34d4af
TG
1214 if (r < 0)
1215 return r;
1216 }
1217
78c958f8 1218 if (link_dhcp4_enabled(link)) {
3c9b8860 1219 r = dhcp4_configure(link);
eb34d4af
TG
1220 if (r < 0)
1221 return r;
eb34d4af
TG
1222 }
1223
78c958f8 1224 if (link_dhcp4_server_enabled(link)) {
dd43110f
TG
1225 r = sd_dhcp_server_new(&link->dhcp_server, link->ifindex);
1226 if (r < 0)
1227 return r;
1228
1229 r = sd_dhcp_server_attach_event(link->dhcp_server, NULL, 0);
1230 if (r < 0)
1231 return r;
dd43110f
TG
1232 }
1233
78c958f8 1234 if (link_dhcp6_enabled(link)) {
4138fb2c
PF
1235 r = sd_icmp6_nd_new(&link->icmp6_router_discovery);
1236 if (r < 0)
1237 return r;
1238
1239 r = sd_icmp6_nd_attach_event(link->icmp6_router_discovery,
1240 NULL, 0);
1241 if (r < 0)
1242 return r;
1243
1244 r = sd_icmp6_nd_set_mac(link->icmp6_router_discovery,
1245 &link->mac);
1246 if (r < 0)
1247 return r;
1248
1249 r = sd_icmp6_nd_set_index(link->icmp6_router_discovery,
1250 link->ifindex);
1251 if (r < 0)
1252 return r;
1253
1254 r = sd_icmp6_nd_set_callback(link->icmp6_router_discovery,
1255 icmp6_router_handler, link);
1256 if (r < 0)
1257 return r;
1258 }
1259
a61bb41c 1260 if (link_has_carrier(link)) {
1e9be60b
TG
1261 r = link_acquire_conf(link);
1262 if (r < 0)
1263 return r;
cc544d5f 1264 }
1e9be60b 1265
3f265037 1266 return link_enter_join_netdev(link);
505f8da7
TG
1267}
1268
3c9b8860
TG
1269static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m,
1270 void *userdata) {
5da8149f 1271 _cleanup_link_unref_ Link *link = userdata;
505f8da7 1272 Network *network;
505f8da7
TG
1273 int r;
1274
1275 assert(link);
1276 assert(link->ifname);
1277 assert(link->manager);
1278
8434fd5c 1279 if (link->state != LINK_STATE_PENDING)
5da8149f 1280 return 1;
505f8da7 1281
4f561e8e 1282 log_debug_link(link, "link state is up-to-date");
505f8da7 1283
3c9b8860
TG
1284 r = network_get(link->manager, link->udev_device, link->ifname,
1285 &link->mac, &network);
57bd6899
TG
1286 if (r == -ENOENT) {
1287 link_enter_unmanaged(link);
5da8149f 1288 return 1;
57bd6899
TG
1289 } else if (r < 0)
1290 return r;
505f8da7 1291
bd2efe92 1292 if (link->flags & IFF_LOOPBACK) {
78c958f8
TG
1293 if (network->ipv4ll)
1294 log_debug_link(link, "ignoring IPv4LL for loopback link");
1295
1296 if (network->dhcp != DHCP_SUPPORT_NONE)
1297 log_debug_link(link, "ignoring DHCP clients for loopback link");
1298
1299 if (network->dhcp_server)
1300 log_debug_link(link, "ignoring DHCP server for loopback link");
bd2efe92
TG
1301 }
1302
505f8da7
TG
1303 r = network_apply(link->manager, network, link);
1304 if (r < 0)
1305 return r;
1306
a748b692
TG
1307 r = link_configure(link);
1308 if (r < 0)
1309 return r;
1310
5da8149f 1311 return 1;
505f8da7
TG
1312}
1313
4f561e8e
TG
1314int link_initialized(Link *link, struct udev_device *device) {
1315 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
1316 int r;
1317
1318 assert(link);
1319 assert(link->manager);
1320 assert(link->manager->rtnl);
1321 assert(device);
1322
8434fd5c 1323 if (link->state != LINK_STATE_PENDING)
4f561e8e
TG
1324 return 0;
1325
679b3605
TG
1326 if (link->udev_device)
1327 return 0;
1328
4f561e8e
TG
1329 log_debug_link(link, "udev initialized link");
1330
1331 link->udev_device = udev_device_ref(device);
1332
3c9b8860
TG
1333 /* udev has initialized the link, but we don't know if we have yet
1334 * processed the NEWLINK messages with the latest state. Do a GETLINK,
1335 * when it returns we know that the pending NEWLINKs have already been
1336 * processed and that we are up-to-date */
4f561e8e 1337
3c9b8860
TG
1338 r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_GETLINK,
1339 link->ifindex);
4f561e8e
TG
1340 if (r < 0)
1341 return r;
1342
3c9b8860
TG
1343 r = sd_rtnl_call_async(link->manager->rtnl, req,
1344 link_initialized_and_synced, link, 0, NULL);
4f561e8e
TG
1345 if (r < 0)
1346 return r;
1347
5da8149f
TG
1348 link_ref(link);
1349
4f561e8e
TG
1350 return 0;
1351}
1352
3c9b8860
TG
1353int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message,
1354 void *userdata) {
fbbeb65a
TG
1355 Manager *m = userdata;
1356 Link *link = NULL;
1357 uint16_t type;
1358 _cleanup_address_free_ Address *address = NULL;
428fd0a7 1359 Address *ad;
fbbeb65a 1360 char buf[INET6_ADDRSTRLEN];
c6d3b303
TG
1361 char valid_buf[FORMAT_TIMESPAN_MAX];
1362 const char *valid_str = NULL;
428fd0a7 1363 bool address_dropped = false;
fbbeb65a
TG
1364 int r, ifindex;
1365
1366 assert(rtnl);
1367 assert(message);
1368 assert(m);
1369
1370 r = sd_rtnl_message_get_type(message, &type);
1371 if (r < 0) {
1372 log_warning("rtnl: could not get message type");
1373 return 0;
1374 }
1375
1376 r = sd_rtnl_message_addr_get_ifindex(message, &ifindex);
1377 if (r < 0 || ifindex <= 0) {
5ea846cc 1378 log_warning("rtnl: received address message without valid ifindex, ignoring");
fbbeb65a
TG
1379 return 0;
1380 } else {
1381 r = link_get(m, ifindex, &link);
1382 if (r < 0 || !link) {
a821cbb0 1383 log_warning("rtnl: received address for a nonexistent link (%d), ignoring", ifindex);
fbbeb65a
TG
1384 return 0;
1385 }
1386 }
1387
1388 r = address_new_dynamic(&address);
1389 if (r < 0)
393c0c5e 1390 return r;
fbbeb65a
TG
1391
1392 r = sd_rtnl_message_addr_get_family(message, &address->family);
1393 if (r < 0 || !IN_SET(address->family, AF_INET, AF_INET6)) {
3c9b8860
TG
1394 log_warning_link(link,
1395 "rtnl: received address with invalid family, ignoring");
fbbeb65a
TG
1396 return 0;
1397 }
1398
1399 r = sd_rtnl_message_addr_get_prefixlen(message, &address->prefixlen);
1400 if (r < 0) {
3c9b8860
TG
1401 log_warning_link(link,
1402 "rtnl: received address with invalid prefixlen, ignoring");
e375dcde
TG
1403 return 0;
1404 }
1405
1406 r = sd_rtnl_message_addr_get_scope(message, &address->scope);
1407 if (r < 0) {
3c9b8860
TG
1408 log_warning_link(link,
1409 "rtnl: received address with invalid scope, ignoring");
fbbeb65a
TG
1410 return 0;
1411 }
1412
81163121
TG
1413 r = sd_rtnl_message_addr_get_flags(message, &address->flags);
1414 if (r < 0) {
1415 log_warning_link(link,
1416 "rtnl: received address with invalid flags, ignoring");
1417 return 0;
1418 }
1419
fbbeb65a
TG
1420 switch (address->family) {
1421 case AF_INET:
3c9b8860
TG
1422 r = sd_rtnl_message_read_in_addr(message, IFA_LOCAL,
1423 &address->in_addr.in);
fbbeb65a 1424 if (r < 0) {
3c9b8860
TG
1425 log_warning_link(link,
1426 "rtnl: received address without valid address, ignoring");
fbbeb65a
TG
1427 return 0;
1428 }
1429
1430 break;
1431
1432 case AF_INET6:
3c9b8860
TG
1433 r = sd_rtnl_message_read_in6_addr(message, IFA_ADDRESS,
1434 &address->in_addr.in6);
fbbeb65a 1435 if (r < 0) {
3c9b8860
TG
1436 log_warning_link(link,
1437 "rtnl: received address without valid address, ignoring");
fbbeb65a
TG
1438 return 0;
1439 }
1440
1441 break;
1442
1443 default:
1444 assert_not_reached("invalid address family");
1445 }
1446
3c9b8860
TG
1447 if (!inet_ntop(address->family, &address->in_addr, buf,
1448 INET6_ADDRSTRLEN)) {
987efa17 1449 log_warning_link(link, "could not print address");
fbbeb65a
TG
1450 return 0;
1451 }
1452
c6d3b303
TG
1453 r = sd_rtnl_message_read_cache_info(message, IFA_CACHEINFO,
1454 &address->cinfo);
1455 if (r >= 0) {
1456 if (address->cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME)
1457 valid_str = "ever";
1458 else
1459 valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
1460 address->cinfo.ifa_valid * USEC_PER_SEC,
1461 USEC_PER_SEC);
1462 }
1463
428fd0a7
TG
1464 LIST_FOREACH(addresses, ad, link->addresses) {
1465 if (address_equal(ad, address)) {
1466 LIST_REMOVE(addresses, link->addresses, ad);
1467
1468 address_free(ad);
1469
1470 address_dropped = true;
1471
1472 break;
1473 }
1474 }
1475
fbbeb65a
TG
1476 switch (type) {
1477 case RTM_NEWADDR:
428fd0a7 1478 if (!address_dropped)
c6d3b303
TG
1479 log_debug_link(link, "added address: %s/%u (valid for %s)",
1480 buf, address->prefixlen,
1481 strna(valid_str));
393c0c5e 1482 else
c6d3b303
TG
1483 log_debug_link(link, "updated address: %s/%u (valid for %s)",
1484 buf, address->prefixlen,
1485 strna(valid_str));
fbbeb65a 1486
428fd0a7
TG
1487 LIST_PREPEND(addresses, link->addresses, address);
1488 address = NULL;
1489
f5602be9
TG
1490 link_save(link);
1491
428fd0a7 1492 break;
fbbeb65a 1493 case RTM_DELADDR:
f5602be9 1494 if (address_dropped) {
c6d3b303
TG
1495 log_debug_link(link, "removed address: %s/%u (valid for %s)",
1496 buf, address->prefixlen,
1497 strna(valid_str));
428fd0a7 1498
f5602be9 1499 link_save(link);
393c0c5e 1500 } else
3c9b8860 1501 log_warning_link(link,
c6d3b303
TG
1502 "removing non-existent address: %s/%u (valid for %s)",
1503 buf, address->prefixlen,
1504 strna(valid_str));
f5602be9 1505
fbbeb65a
TG
1506 break;
1507 default:
1508 assert_not_reached("Received invalid RTNL message type");
1509 }
1510
1511 return 1;
1512}
1513
505f8da7
TG
1514int link_add(Manager *m, sd_rtnl_message *message, Link **ret) {
1515 Link *link;
fbbeb65a 1516 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
505f8da7
TG
1517 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
1518 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
1519 int r;
1520
1521 assert(m);
fbbeb65a 1522 assert(m->rtnl);
505f8da7
TG
1523 assert(message);
1524 assert(ret);
1525
1526 r = link_new(m, message, ret);
1527 if (r < 0)
1528 return r;
1529
1530 link = *ret;
1531
ae06ab10 1532 log_debug_link(link, "link %d added", link->ifindex);
505f8da7 1533
3c9b8860
TG
1534 r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, link->ifindex,
1535 0);
fbbeb65a
TG
1536 if (r < 0)
1537 return r;
1538
3c9b8860
TG
1539 r = sd_rtnl_call_async(m->rtnl, req, link_get_address_handler, link, 0,
1540 NULL);
fbbeb65a
TG
1541 if (r < 0)
1542 return r;
1543
5da8149f
TG
1544 link_ref(link);
1545
505f8da7
TG
1546 if (detect_container(NULL) <= 0) {
1547 /* not in a container, udev will be around */
ae06ab10 1548 sprintf(ifindex_str, "n%d", link->ifindex);
505f8da7
TG
1549 device = udev_device_new_from_device_id(m->udev, ifindex_str);
1550 if (!device) {
3c9b8860
TG
1551 log_warning_link(link,
1552 "could not find udev device: %m");
9fecce80 1553 return -errno;
505f8da7
TG
1554 }
1555
3c4cb064 1556 if (udev_device_get_is_initialized(device) <= 0) {
505f8da7 1557 /* not yet ready */
8434fd5c 1558 log_debug_link(link, "link pending udev initialization...");
505f8da7 1559 return 0;
3c4cb064 1560 }
505f8da7 1561
4f561e8e
TG
1562 r = link_initialized(link, device);
1563 if (r < 0)
1564 return r;
1565 } else {
5da8149f
TG
1566 /* we are calling a callback directly, so must take a ref */
1567 link_ref(link);
1568
4f561e8e
TG
1569 r = link_initialized_and_synced(m->rtnl, NULL, link);
1570 if (r < 0)
1571 return r;
1572 }
505f8da7 1573
a748b692
TG
1574 return 0;
1575}
1576
22936833 1577int link_update(Link *link, sd_rtnl_message *m) {
c49b33ac 1578 struct ether_addr mac;
ca4e095a 1579 const char *ifname;
afe7fd56 1580 uint32_t mtu;
a61bb41c 1581 bool had_carrier, carrier_gained, carrier_lost;
22936833
TG
1582 int r;
1583
dd3efc09 1584 assert(link);
b8941f74 1585 assert(link->ifname);
22936833
TG
1586 assert(m);
1587
7619683b
TG
1588 if (link->state == LINK_STATE_LINGER) {
1589 link_ref(link);
1590 log_info_link(link, "link readded");
1591 link->state = LINK_STATE_ENSLAVING;
1592 }
1593
b8941f74
TG
1594 r = sd_rtnl_message_read_string(m, IFLA_IFNAME, &ifname);
1595 if (r >= 0 && !streq(ifname, link->ifname)) {
1596 log_info_link(link, "renamed to %s", ifname);
1597
1598 free(link->ifname);
1599 link->ifname = strdup(ifname);
1600 if (!link->ifname)
1601 return -ENOMEM;
1602 }
1603
afe7fd56
TG
1604 r = sd_rtnl_message_read_u32(m, IFLA_MTU, &mtu);
1605 if (r >= 0 && mtu > 0) {
1606 link->mtu = mtu;
1607 if (!link->original_mtu) {
1608 link->original_mtu = mtu;
9842de0d 1609 log_debug_link(link, "saved original MTU: %"
afe7fd56
TG
1610 PRIu32, link->original_mtu);
1611 }
1612
1613 if (link->dhcp_client) {
3c9b8860
TG
1614 r = sd_dhcp_client_set_mtu(link->dhcp_client,
1615 link->mtu);
afe7fd56 1616 if (r < 0) {
3c9b8860
TG
1617 log_warning_link(link,
1618 "Could not update MTU in DHCP client: %s",
afe7fd56
TG
1619 strerror(-r));
1620 return r;
1621 }
1622 }
9842de0d 1623 }
69629de9 1624
e9189a1f
TG
1625 /* The kernel may broadcast NEWLINK messages without the MAC address
1626 set, simply ignore them. */
c49b33ac 1627 r = sd_rtnl_message_read_ether_addr(m, IFLA_ADDRESS, &mac);
e9189a1f 1628 if (r >= 0) {
3c9b8860
TG
1629 if (memcmp(link->mac.ether_addr_octet, mac.ether_addr_octet,
1630 ETH_ALEN)) {
c49b33ac 1631
3c9b8860
TG
1632 memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet,
1633 ETH_ALEN);
c49b33ac 1634
20861203
TG
1635 log_debug_link(link, "MAC address: "
1636 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1637 mac.ether_addr_octet[0],
1638 mac.ether_addr_octet[1],
1639 mac.ether_addr_octet[2],
1640 mac.ether_addr_octet[3],
1641 mac.ether_addr_octet[4],
1642 mac.ether_addr_octet[5]);
c49b33ac 1643
20861203
TG
1644 if (link->ipv4ll) {
1645 r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
1646 if (r < 0) {
3c9b8860
TG
1647 log_warning_link(link,
1648 "Could not update MAC address in IPv4LL client: %s",
20861203
TG
1649 strerror(-r));
1650 return r;
1651 }
c49b33ac 1652 }
c49b33ac 1653
20861203 1654 if (link->dhcp_client) {
3c9b8860
TG
1655 r = sd_dhcp_client_set_mac(link->dhcp_client,
1656 &link->mac);
20861203 1657 if (r < 0) {
3c9b8860
TG
1658 log_warning_link(link,
1659 "Could not update MAC address in DHCP client: %s",
20861203
TG
1660 strerror(-r));
1661 return r;
1662 }
c49b33ac 1663 }
4138fb2c
PF
1664
1665 if (link->dhcp6_client) {
1666 r = sd_dhcp6_client_set_mac(link->dhcp6_client,
1667 &link->mac);
1668 if (r < 0) {
3c9b8860
TG
1669 log_warning_link(link,
1670 "Could not update MAC address in DHCPv6 client: %s",
4138fb2c
PF
1671 strerror(-r));
1672 return r;
1673 }
1674 }
c49b33ac 1675 }
4f882b2a
TG
1676 }
1677
a61bb41c
TG
1678 had_carrier = link_has_carrier(link);
1679
1680 r = link_update_flags(link, m);
1681 if (r < 0)
1682 return r;
1683
1684 carrier_gained = !had_carrier && link_has_carrier(link);
1685 carrier_lost = had_carrier && !link_has_carrier(link);
1686
1687 if (carrier_gained) {
1688 log_info_link(link, "gained carrier");
1689
1690 if (link->network) {
1691 r = link_acquire_conf(link);
1692 if (r < 0) {
1693 link_enter_failed(link);
1694 return r;
1695 }
1696 }
1697 } else if (carrier_lost) {
1698 log_info_link(link, "lost carrier");
1699
1700 r = link_stop_clients(link);
1701 if (r < 0) {
1702 link_enter_failed(link);
1703 return r;
1704 }
1705 }
1706
1707 return 0;
dd3efc09 1708}
fe8db0c5 1709
e375dcde
TG
1710static void link_update_operstate(Link *link) {
1711
1712 assert(link);
1713
1714 if (link->kernel_operstate == IF_OPER_DORMANT)
1715 link->operstate = LINK_OPERSTATE_DORMANT;
a61bb41c 1716 else if (link_has_carrier(link)) {
e375dcde
TG
1717 Address *address;
1718 uint8_t scope = RT_SCOPE_NOWHERE;
1719
1720 /* if we have carrier, check what addresses we have */
1721 LIST_FOREACH(addresses, address, link->addresses) {
81163121
TG
1722 if (address->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED))
1723 continue;
1724
e375dcde
TG
1725 if (address->scope < scope)
1726 scope = address->scope;
1727 }
1728
1729 if (scope < RT_SCOPE_SITE)
1730 /* universally accessible addresses found */
1731 link->operstate = LINK_OPERSTATE_ROUTABLE;
1732 else if (scope < RT_SCOPE_HOST)
1733 /* only link or site local addresses found */
1734 link->operstate = LINK_OPERSTATE_DEGRADED;
1735 else
1736 /* no useful addresses found */
1737 link->operstate = LINK_OPERSTATE_CARRIER;
54cba0b1 1738 } else if (link->flags & IFF_UP)
d3df0e39 1739 link->operstate = LINK_OPERSTATE_NO_CARRIER;
54cba0b1 1740 else
d3df0e39 1741 link->operstate = LINK_OPERSTATE_OFF;
e375dcde
TG
1742}
1743
fe8db0c5 1744int link_save(Link *link) {
68a8723c 1745 _cleanup_free_ char *temp_path = NULL;
fe8db0c5 1746 _cleanup_fclose_ FILE *f = NULL;
e375dcde 1747 const char *admin_state, *oper_state;
fe8db0c5
TG
1748 int r;
1749
1750 assert(link);
1751 assert(link->state_file);
68a8723c 1752 assert(link->lease_file);
bbf7c048
TG
1753 assert(link->manager);
1754
e375dcde
TG
1755 link_update_operstate(link);
1756
bbf7c048
TG
1757 r = manager_save(link->manager);
1758 if (r < 0)
1759 return r;
fe8db0c5 1760
370e9930
TG
1761 if (link->state == LINK_STATE_LINGER) {
1762 unlink(link->state_file);
1763 return 0;
1764 }
1765
deb2e523
TG
1766 admin_state = link_state_to_string(link->state);
1767 assert(admin_state);
1768
e375dcde
TG
1769 oper_state = link_operstate_to_string(link->operstate);
1770 assert(oper_state);
deb2e523 1771
fe8db0c5
TG
1772 r = fopen_temporary(link->state_file, &f, &temp_path);
1773 if (r < 0)
c2d6bd61 1774 return r;
fe8db0c5
TG
1775
1776 fchmod(fileno(f), 0644);
1777
1778 fprintf(f,
1779 "# This is private data. Do not parse.\n"
deb2e523 1780 "ADMIN_STATE=%s\n"
6dcaa6f5
TG
1781 "OPER_STATE=%s\n",
1782 admin_state, oper_state);
fe8db0c5 1783
bcb7a07e 1784 if (link->network) {
ea352b40
LP
1785 char **address, **domain;
1786 bool space;
b0e39c82 1787
adc5b2e2
TG
1788 fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
1789
b0e39c82 1790 fputs("DNS=", f);
ea352b40
LP
1791 space = false;
1792 STRV_FOREACH(address, link->network->dns) {
1793 if (space)
1794 fputc(' ', f);
1795 fputs(*address, f);
1796 space = true;
1797 }
d5314fff 1798
b0e39c82
TG
1799 if (link->network->dhcp_dns &&
1800 link->dhcp_lease) {
1801 const struct in_addr *addresses;
1802
1803 r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
1804 if (r > 0) {
ea352b40
LP
1805 if (space)
1806 fputc(' ', f);
b0e39c82 1807 serialize_in_addrs(f, addresses, r);
b0e39c82
TG
1808 }
1809 }
1810
b0e39c82
TG
1811 fputs("\n", f);
1812
1813 fprintf(f, "NTP=");
ea352b40
LP
1814 space = false;
1815 STRV_FOREACH(address, link->network->ntp) {
1816 if (space)
1817 fputc(' ', f);
1818 fputs(*address, f);
1819 space = true;
1820 }
d5314fff 1821
b0e39c82
TG
1822 if (link->network->dhcp_ntp &&
1823 link->dhcp_lease) {
1824 const struct in_addr *addresses;
1825
1826 r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
1827 if (r > 0) {
ea352b40
LP
1828 if (space)
1829 fputc(' ', f);
b0e39c82 1830 serialize_in_addrs(f, addresses, r);
b0e39c82
TG
1831 }
1832 }
1833
b0e39c82 1834 fputs("\n", f);
bd8f6538 1835
6192b846 1836 fprintf(f, "DOMAINS=");
ea352b40
LP
1837 space = false;
1838 STRV_FOREACH(domain, link->network->domains) {
1839 if (space)
1840 fputc(' ', f);
1841 fputs(*domain, f);
1842 space = true;
1843 }
d5314fff 1844
ad0734e8 1845 if (link->network->dhcp_domains &&
9b4d1882
TG
1846 link->dhcp_lease) {
1847 const char *domainname;
1848
1849 r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
6192b846 1850 if (r >= 0) {
ea352b40
LP
1851 if (space)
1852 fputc(' ', f);
6192b846 1853 fputs(domainname, f);
6192b846 1854 }
9b4d1882
TG
1855 }
1856
6192b846
TG
1857 fputs("\n", f);
1858
67272d15
TG
1859 fprintf(f, "WILDCARD_DOMAIN=%s\n",
1860 yes_no(link->network->wildcard_domain));
1861
3c9b8860
TG
1862 fprintf(f, "LLMNR=%s\n",
1863 llmnr_support_to_string(link->network->llmnr));
bcb7a07e 1864 }
7374f9d8 1865
fe8db0c5 1866 if (link->dhcp_lease) {
d9876a52
TG
1867 assert(link->network);
1868
68a8723c 1869 r = dhcp_lease_save(link->dhcp_lease, link->lease_file);
fe8db0c5 1870 if (r < 0)
c2d6bd61 1871 goto fail;
fe8db0c5 1872
7374f9d8 1873 fprintf(f,
b0e39c82
TG
1874 "DHCP_LEASE=%s\n",
1875 link->lease_file);
deb2e523 1876 } else
68a8723c 1877 unlink(link->lease_file);
fe8db0c5 1878
c2d6bd61
LP
1879 r = fflush_and_check(f);
1880 if (r < 0)
1881 goto fail;
fe8db0c5 1882
c2d6bd61 1883 if (rename(temp_path, link->state_file) < 0) {
fe8db0c5 1884 r = -errno;
c2d6bd61 1885 goto fail;
fe8db0c5
TG
1886 }
1887
c2d6bd61 1888 return 0;
c2d6bd61
LP
1889fail:
1890 log_error_link(link, "Failed to save link data to %s: %s", link->state_file, strerror(-r));
1891 unlink(link->state_file);
1892 unlink(temp_path);
fe8db0c5
TG
1893 return r;
1894}
1895
1896static const char* const link_state_table[_LINK_STATE_MAX] = {
8434fd5c 1897 [LINK_STATE_PENDING] = "pending",
fe8db0c5
TG
1898 [LINK_STATE_ENSLAVING] = "configuring",
1899 [LINK_STATE_SETTING_ADDRESSES] = "configuring",
1900 [LINK_STATE_SETTING_ROUTES] = "configuring",
1901 [LINK_STATE_CONFIGURED] = "configured",
57bd6899 1902 [LINK_STATE_UNMANAGED] = "unmanaged",
fe8db0c5 1903 [LINK_STATE_FAILED] = "failed",
370e9930 1904 [LINK_STATE_LINGER] = "linger",
fe8db0c5
TG
1905};
1906
1907DEFINE_STRING_TABLE_LOOKUP(link_state, LinkState);
e375dcde
TG
1908
1909static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = {
d3df0e39
TG
1910 [LINK_OPERSTATE_OFF] = "off",
1911 [LINK_OPERSTATE_NO_CARRIER] = "no-carrier",
e375dcde
TG
1912 [LINK_OPERSTATE_DORMANT] = "dormant",
1913 [LINK_OPERSTATE_CARRIER] = "carrier",
1914 [LINK_OPERSTATE_DEGRADED] = "degraded",
1915 [LINK_OPERSTATE_ROUTABLE] = "routable",
1916};
1917
1918DEFINE_STRING_TABLE_LOOKUP(link_operstate, LinkOperationalState);