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