• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 13935887515

18 Mar 2025 07:10PM UTC coverage: 71.913% (-0.03%) from 71.946%
13935887515

push

github

web-flow
Several fixes and cleanups around sd_listen_fds() (#36788)

15 of 24 new or added lines in 5 files covered. (62.5%)

993 existing lines in 54 files now uncovered.

296157 of 411825 relevant lines covered (71.91%)

710024.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.14
/src/network/networkd-ndisc.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
/***
3
  Copyright © 2014 Intel Corporation. All rights reserved.
4
***/
5

6
#include <arpa/inet.h>
7
#include <netinet/icmp6.h>
8
#include <linux/if.h>
9
#include <linux/if_arp.h>
10

11
#include "sd-ndisc.h"
12

13
#include "event-util.h"
14
#include "missing_network.h"
15
#include "ndisc-router-internal.h"
16
#include "networkd-address-generation.h"
17
#include "networkd-address.h"
18
#include "networkd-dhcp6.h"
19
#include "networkd-manager.h"
20
#include "networkd-ndisc.h"
21
#include "networkd-nexthop.h"
22
#include "networkd-queue.h"
23
#include "networkd-route.h"
24
#include "networkd-state-file.h"
25
#include "networkd-sysctl.h"
26
#include "sort-util.h"
27
#include "string-table.h"
28
#include "string-util.h"
29
#include "strv.h"
30
#include "sysctl-util.h"
31

32
#define NDISC_DNSSL_MAX 64U
33
#define NDISC_RDNSS_MAX 64U
34
#define NDISC_ENCRYPTED_DNS_MAX 64U
35
/* Not defined in the RFC, but let's set an upper limit to make not consume much memory.
36
 * This should be safe as typically there should be at most 1 portal per network. */
37
#define NDISC_CAPTIVE_PORTAL_MAX 64U
38
/* Neither defined in the RFC. Just for safety. Otherwise, malformed messages can make clients trigger OOM.
39
 * Not sure if the threshold is high enough. Let's adjust later if not. */
40
#define NDISC_PREF64_MAX 64U
41

42
static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t timestamp_usec);
43

44
bool link_ndisc_enabled(Link *link) {
4,169✔
45
        assert(link);
4,169✔
46

47
        if (!socket_ipv6_is_supported())
4,169✔
48
                return false;
49

50
        if (link->flags & IFF_LOOPBACK)
4,169✔
51
                return false;
52

53
        if (link->iftype == ARPHRD_CAN)
4,169✔
54
                return false;
55

56
        if (!link->network)
4,163✔
57
                return false;
58

59
        if (!link_may_have_ipv6ll(link, /* check_multicast = */ true))
4,117✔
60
                return false;
61

62
        /* Honor explicitly specified value. */
63
        if (link->network->ndisc >= 0)
3,806✔
64
                return link->network->ndisc;
3,336✔
65

66
        /* Disable if RADV is enabled. */
67
        if (link_radv_enabled(link))
470✔
68
                return false;
69

70
        /* Accept RAs if IPv6 forwarding is disabled, and ignore RAs if IPv6 forwarding is enabled. */
71
        int t = link_get_ip_forwarding(link, AF_INET6);
464✔
72
        if (t >= 0)
464✔
73
                return !t;
×
74

75
        /* Otherwise, defaults to true. */
76
        return true;
77
}
78

79
void network_adjust_ndisc(Network *network) {
5,366✔
80
        assert(network);
5,366✔
81

82
        if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
5,366✔
83
                if (network->ndisc > 0)
615✔
84
                        log_warning("%s: IPv6AcceptRA= is enabled but IPv6 link-local addressing is disabled or not supported. "
1✔
85
                                    "Disabling IPv6AcceptRA=.", network->filename);
86
                network->ndisc = false;
615✔
87
        }
88

89
        /* When RouterAllowList=, PrefixAllowList= or RouteAllowList= are specified, then
90
         * RouterDenyList=, PrefixDenyList= or RouteDenyList= are ignored, respectively. */
91
        if (!set_isempty(network->ndisc_allow_listed_router))
5,366✔
92
                network->ndisc_deny_listed_router = set_free_free(network->ndisc_deny_listed_router);
×
93
        if (!set_isempty(network->ndisc_allow_listed_prefix))
5,366✔
94
                network->ndisc_deny_listed_prefix = set_free_free(network->ndisc_deny_listed_prefix);
5✔
95
        if (!set_isempty(network->ndisc_allow_listed_route_prefix))
5,366✔
96
                network->ndisc_deny_listed_route_prefix = set_free_free(network->ndisc_deny_listed_route_prefix);
×
97
}
5,366✔
98

99
static int ndisc_check_ready(Link *link);
100

101
static int ndisc_address_ready_callback(Address *address) {
28✔
102
        Address *a;
28✔
103

104
        assert(address);
28✔
105
        assert(address->link);
28✔
106

107
        SET_FOREACH(a, address->link->addresses)
139✔
108
                if (a->source == NETWORK_CONFIG_SOURCE_NDISC)
83✔
109
                        a->callback = NULL;
52✔
110

111
        return ndisc_check_ready(address->link);
28✔
112
}
113

114
static int ndisc_check_ready(Link *link) {
370✔
115
        bool found = false, ready = false;
370✔
116
        Address *address;
370✔
117

118
        assert(link);
370✔
119

120
        if (link->ndisc_messages > 0) {
370✔
121
                log_link_debug(link, "%s(): SLAAC addresses and routes are not set.", __func__);
253✔
122
                return 0;
253✔
123
        }
124

125
        SET_FOREACH(address, link->addresses) {
301✔
126
                if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
232✔
127
                        continue;
113✔
128

129
                found = true;
119✔
130

131
                if (address_is_ready(address)) {
119✔
132
                        ready = true;
133
                        break;
134
                }
135
        }
136

137
        if (found && !ready) {
117✔
138
                SET_FOREACH(address, link->addresses)
160✔
139
                        if (address->source == NETWORK_CONFIG_SOURCE_NDISC)
92✔
140
                                address->callback = ndisc_address_ready_callback;
58✔
141

142
                log_link_debug(link, "%s(): no SLAAC address is ready.", __func__);
34✔
143
                return 0;
34✔
144
        }
145

146
        link->ndisc_configured = true;
83✔
147
        log_link_debug(link, "SLAAC addresses and routes set.");
83✔
148

149
        link_check_ready(link);
83✔
150
        return 0;
151
}
152

153
static int ndisc_remove_unused_nexthop(Link *link, NextHop *nexthop) {
632✔
154
        int r;
632✔
155

156
        assert(link);
632✔
157
        assert(link->manager);
632✔
158
        assert(link->ifindex > 0);
632✔
159
        assert(nexthop);
632✔
160

161
        if (nexthop->source != NETWORK_CONFIG_SOURCE_NDISC)
632✔
162
                return 0;
632✔
163

164
        if (nexthop->ifindex != link->ifindex)
433✔
165
                return 0;
166

167
        Route *route;
153✔
168
        SET_FOREACH(route, nexthop->routes)
236✔
169
                if (route_exists(route) || route_is_requesting(route))
149✔
170
                        return 0;
66✔
171

172
        Request *req;
87✔
173
        ORDERED_SET_FOREACH(req, link->manager->request_queue) {
153✔
174
                if (req->type != REQUEST_TYPE_ROUTE)
69✔
175
                        continue;
66✔
176

177
                route = ASSERT_PTR(req->userdata);
3✔
178
                if (route->nexthop_id == nexthop->id)
3✔
179
                        return 0;
3✔
180
        }
181

182
        r = nexthop_remove_and_cancel(nexthop, link->manager);
84✔
183
        if (r < 0)
84✔
184
                return log_link_debug_errno(link, r, "Failed to remove unused nexthop: %m");
×
185

186
        return 0;
187
}
188

189
static int ndisc_remove_unused_nexthop_by_id(Link *link, uint32_t id) {
23✔
190
        assert(link);
23✔
191
        assert(link->manager);
23✔
192

193
        if (id == 0)
23✔
194
                return 0;
23✔
195

196
        NextHop *nexthop;
18✔
197
        if (nexthop_get_by_id(link->manager, id, &nexthop) < 0)
18✔
198
                return 0;
199

200
        return ndisc_remove_unused_nexthop(link, nexthop);
18✔
201
}
202

203
static int ndisc_remove_unused_nexthops(Link *link) {
9,425✔
204
        int ret = 0;
9,425✔
205

206
        assert(link);
9,425✔
207
        assert(link->manager);
9,425✔
208

209
        NextHop *nexthop;
9,425✔
210
        HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id)
10,039✔
211
                RET_GATHER(ret, ndisc_remove_unused_nexthop(link, nexthop));
614✔
212

213
        return ret;
9,425✔
214
}
215

216
#define NDISC_NEXTHOP_APP_ID SD_ID128_MAKE(76,d2,0f,1f,76,1e,44,d1,97,3a,52,5c,05,68,b5,0d)
217

218
static uint32_t ndisc_generate_nexthop_id(const NextHop *nexthop, Link *link, sd_id128_t app_id, uint64_t trial) {
186✔
219
        assert(nexthop);
186✔
220
        assert(link);
186✔
221

222
        struct siphash state;
186✔
223
        siphash24_init(&state, app_id.bytes);
186✔
224
        siphash24_compress_typesafe(nexthop->protocol, &state);
186✔
225
        siphash24_compress_string(link->ifname, &state);
186✔
226
        siphash24_compress_typesafe(nexthop->gw.address.in6, &state);
186✔
227
        siphash24_compress_typesafe(nexthop->provider.in6, &state);
186✔
228
        uint64_t n = htole64(trial);
186✔
229
        siphash24_compress_typesafe(n, &state);
186✔
230

231
        uint64_t result = htole64(siphash24_finalize(&state));
186✔
232
        return (uint32_t) ((result & 0xffffffff) ^ (result >> 32));
186✔
233
}
234

235
static bool ndisc_nexthop_equal(const NextHop *a, const NextHop *b) {
81✔
236
        assert(a);
81✔
237
        assert(b);
81✔
238

239
        if (a->source != b->source)
81✔
240
                return false;
241
        if (a->protocol != b->protocol)
81✔
242
                return false;
243
        if (a->ifindex != b->ifindex)
81✔
244
                return false;
245
        if (!in6_addr_equal(&a->provider.in6, &b->provider.in6))
79✔
246
                return false;
247
        if (!in6_addr_equal(&a->gw.address.in6, &b->gw.address.in6))
64✔
248
                return false;
×
249

250
        return true;
251
}
252

253
static bool ndisc_take_nexthop_id(NextHop *nexthop, const NextHop *existing, Manager *manager) {
81✔
254
        assert(nexthop);
81✔
255
        assert(nexthop->id == 0);
81✔
256
        assert(existing);
81✔
257
        assert(existing->id > 0);
81✔
258
        assert(manager);
81✔
259

260
        if (!ndisc_nexthop_equal(nexthop, existing))
81✔
261
                return false;
262

263
        log_nexthop_debug(existing, "Found matching", manager);
64✔
264
        nexthop->id = existing->id;
64✔
265
        return true;
64✔
266
}
267

268
static int ndisc_nexthop_find_id(NextHop *nexthop, Link *link) {
127✔
269
        NextHop *n;
127✔
270
        Request *req;
127✔
271
        int r;
127✔
272

273
        assert(nexthop);
127✔
274
        assert(link);
127✔
275
        assert(link->manager);
127✔
276

277
        sd_id128_t app_id;
127✔
278
        r = sd_id128_get_machine_app_specific(NDISC_NEXTHOP_APP_ID, &app_id);
127✔
279
        if (r < 0)
127✔
280
                return r;
127✔
281

282
        uint32_t id = ndisc_generate_nexthop_id(nexthop, link, app_id, 0);
127✔
283
        if (nexthop_get_by_id(link->manager, id, &n) >= 0 &&
180✔
284
            ndisc_take_nexthop_id(nexthop, n, link->manager))
53✔
285
                return true;
286
        if (nexthop_get_request_by_id(link->manager, id, &req) >= 0 &&
85✔
287
            ndisc_take_nexthop_id(nexthop, req->userdata, link->manager))
11✔
288
                return true;
289

290
        HASHMAP_FOREACH(n, link->manager->nexthops_by_id)
69✔
291
                if (ndisc_take_nexthop_id(nexthop, n, link->manager))
6✔
292
                        return true;
×
293

294
        ORDERED_SET_FOREACH(req, link->manager->request_queue) {
116✔
295
                if (req->type != REQUEST_TYPE_NEXTHOP)
53✔
296
                        continue;
42✔
297

298
                if (ndisc_take_nexthop_id(nexthop, req->userdata, link->manager))
11✔
299
                        return true;
×
300
        }
301

302
        return false;
63✔
303
}
304

305
static int ndisc_nexthop_new(const Route *route, Link *link, NextHop **ret) {
127✔
306
        _cleanup_(nexthop_unrefp) NextHop *nexthop = NULL;
127✔
307
        int r;
127✔
308

309
        assert(route);
127✔
310
        assert(link);
127✔
311
        assert(ret);
127✔
312

313
        r = nexthop_new(&nexthop);
127✔
314
        if (r < 0)
127✔
315
                return r;
316

317
        nexthop->source = NETWORK_CONFIG_SOURCE_NDISC;
127✔
318
        nexthop->provider = route->provider;
127✔
319
        nexthop->protocol = route->protocol == RTPROT_REDIRECT ? RTPROT_REDIRECT : RTPROT_RA;
127✔
320
        nexthop->family = AF_INET6;
127✔
321
        nexthop->gw.address = route->nexthop.gw;
127✔
322
        nexthop->ifindex = link->ifindex;
127✔
323

324
        r = ndisc_nexthop_find_id(nexthop, link);
127✔
325
        if (r < 0)
127✔
326
                return r;
327

328
        *ret = TAKE_PTR(nexthop);
127✔
329
        return 0;
127✔
330
}
331

332
static int ndisc_nexthop_acquire_id(NextHop *nexthop, Link *link) {
59✔
333
        int r;
59✔
334

335
        assert(nexthop);
59✔
336
        assert(nexthop->id == 0);
59✔
337
        assert(link);
59✔
338
        assert(link->manager);
59✔
339

340
        sd_id128_t app_id;
59✔
341
        r = sd_id128_get_machine_app_specific(NDISC_NEXTHOP_APP_ID, &app_id);
59✔
342
        if (r < 0)
59✔
343
                return r;
59✔
344

345
        for (uint64_t trial = 0; trial < 100; trial++) {
59✔
346
                uint32_t id = ndisc_generate_nexthop_id(nexthop, link, app_id, trial);
59✔
347
                if (id == 0)
59✔
348
                        continue;
×
349

350
                if (set_contains(link->manager->nexthop_ids, UINT32_TO_PTR(id)))
59✔
351
                        continue; /* The ID is already used in a .network file. */
×
352

353
                if (nexthop_get_by_id(link->manager, id, NULL) >= 0)
59✔
354
                        continue; /* The ID is already used by an existing nexthop. */
×
355

356
                if (nexthop_get_request_by_id(link->manager, id, NULL) >= 0)
59✔
357
                        continue; /* The ID is already used by a nexthop being requested. */
×
358

359
                log_link_debug(link, "Generated new ndisc nexthop ID for %s with trial %"PRIu64": %"PRIu32,
59✔
360
                               IN6_ADDR_TO_STRING(&nexthop->gw.address.in6), trial, id);
361
                nexthop->id = id;
59✔
362
                return 0;
59✔
363
        }
364

365
        return log_link_debug_errno(link, SYNTHETIC_ERRNO(EBUSY), "Cannot find free nexthop ID for %s.",
×
366
                                    IN6_ADDR_TO_STRING(&nexthop->gw.address.in6));
367
}
368

369
static int ndisc_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, NextHop *nexthop) {
59✔
370
        int r;
59✔
371

372
        assert(link);
59✔
373

374
        r = nexthop_configure_handler_internal(m, link, "Could not set NDisc route");
59✔
375
        if (r <= 0)
59✔
376
                return r;
377

378
        r = ndisc_check_ready(link);
59✔
379
        if (r < 0)
59✔
380
                link_enter_failed(link);
×
381

382
        return 1;
383
}
384

385
static int ndisc_request_nexthop(NextHop *nexthop, Link *link) {
123✔
386
        int r;
123✔
387

388
        assert(nexthop);
123✔
389
        assert(link);
123✔
390

391
        if (nexthop->id > 0)
123✔
392
                return 0;
393

394
        r = ndisc_nexthop_acquire_id(nexthop, link);
59✔
395
        if (r < 0)
59✔
396
                return r;
397

398
        r = link_request_nexthop(link, nexthop, &link->ndisc_messages, ndisc_nexthop_handler);
59✔
399
        if (r < 0)
59✔
400
                return r;
401
        if (r > 0)
59✔
402
                link->ndisc_configured = false;
59✔
403

404
        return 0;
405
}
406

407
static int ndisc_set_route_nexthop(Route *route, Link *link, bool request) {
225✔
408
        _cleanup_(nexthop_unrefp) NextHop *nexthop = NULL;
225✔
409
        int r;
225✔
410

411
        assert(route);
225✔
412
        assert(link);
225✔
413
        assert(link->manager);
225✔
414

415
        if (!link->manager->manage_foreign_nexthops)
225✔
416
                goto finalize;
4✔
417

418
        if (route->nexthop.family != AF_INET6 || in6_addr_is_null(&route->nexthop.gw.in6))
221✔
419
                goto finalize;
94✔
420

421
        r = ndisc_nexthop_new(route, link, &nexthop);
127✔
422
        if (r < 0)
127✔
423
                return r;
424

425
        if (nexthop->id == 0 && !request)
127✔
426
                goto finalize;
4✔
427

428
        r = ndisc_request_nexthop(nexthop, link);
123✔
429
        if (r < 0)
123✔
430
                return r;
431

432
        route->nexthop = (RouteNextHop) {};
123✔
433
        route->nexthop_id = nexthop->id;
123✔
434

435
finalize:
225✔
436
        return route_adjust_nexthops(route, link);
225✔
437
}
438

439
static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
196✔
440
        int r;
196✔
441

442
        assert(req);
196✔
443
        assert(link);
196✔
444

445
        r = route_configure_handler_internal(rtnl, m, req, "Could not set NDisc route");
196✔
446
        if (r <= 0)
196✔
447
                return r;
448

449
        r = ndisc_check_ready(link);
196✔
450
        if (r < 0)
196✔
451
                link_enter_failed(link);
×
452

453
        return 1;
454
}
455

456
static void ndisc_set_route_priority(Link *link, Route *route) {
1,559✔
457
        assert(link);
1,559✔
458
        assert(route);
1,559✔
459

460
        if (route->priority_set)
1,559✔
461
                return; /* explicitly configured. */
462

463
        switch (route->pref) {
1,559✔
464
        case SD_NDISC_PREFERENCE_LOW:
448✔
465
                route->priority = link->network->ndisc_route_metric_low;
448✔
466
                break;
448✔
467
        case SD_NDISC_PREFERENCE_MEDIUM:
660✔
468
                route->priority = link->network->ndisc_route_metric_medium;
660✔
469
                break;
660✔
470
        case SD_NDISC_PREFERENCE_HIGH:
451✔
471
                route->priority = link->network->ndisc_route_metric_high;
451✔
472
                break;
451✔
473
        default:
×
474
                assert_not_reached();
×
475
        }
476
}
477

478
static int ndisc_request_route(Route *route, Link *link) {
202✔
479
        int r;
202✔
480

481
        assert(route);
202✔
482
        assert(link);
202✔
483
        assert(link->manager);
202✔
484
        assert(link->network);
202✔
485

486
        r = route_metric_set(&route->metric, RTAX_QUICKACK, link->network->ndisc_quickack);
202✔
487
        if (r < 0)
202✔
488
                return r;
489

490
        r = ndisc_set_route_nexthop(route, link, /* request = */ true);
202✔
491
        if (r < 0)
202✔
492
                return r;
493

494
        uint8_t pref, pref_original = route->pref;
202✔
495
        FOREACH_ARGUMENT(pref, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH) {
805✔
496
                Route *existing;
604✔
497
                Request *req;
604✔
498

499
                /* If the preference is specified by the user config (that is, for semi-static routes),
500
                 * rather than RA, then only search conflicting routes that have the same preference. */
501
                if (route->pref_set && pref != pref_original)
604✔
502
                        continue;
×
503

504
                route->pref = pref;
604✔
505
                ndisc_set_route_priority(link, route);
604✔
506

507
                /* Note, here do not call route_remove_and_cancel() with 'route' directly, otherwise
508
                 * existing route(s) may be removed needlessly. */
509

510
                /* First, check if a conflicting route is already requested. If there is an existing route,
511
                 * and also an existing pending request, then the source may be updated by the request. So,
512
                 * we first need to check the source of the requested route. */
513
                if (route_get_request(link->manager, route, &req) >= 0) {
604✔
514
                        route->pref = pref_original;
5✔
515
                        ndisc_set_route_priority(link, route);
5✔
516

517
                        existing = ASSERT_PTR(req->userdata);
5✔
518
                        if (!route_can_update(link->manager, existing, route)) {
5✔
519
                                if (existing->source == NETWORK_CONFIG_SOURCE_STATIC) {
4✔
520
                                        log_link_debug(link, "Found a pending route request that conflicts with new request based on a received RA, ignoring request.");
×
521
                                        return 0;
1✔
522
                                }
523

524
                                log_link_debug(link, "Found a pending route request that conflicts with new request based on a received RA, cancelling.");
4✔
525
                                r = route_remove_and_cancel(existing, link->manager);
4✔
526
                                if (r < 0)
4✔
527
                                        return r;
528
                        }
529
                }
530

531
                route->pref = pref;
604✔
532
                ndisc_set_route_priority(link, route);
604✔
533

534
                /* Then, check if a conflicting route exists. */
535
                if (route_get(link->manager, route, &existing) >= 0) {
604✔
536
                        route->pref = pref_original;
76✔
537
                        ndisc_set_route_priority(link, route);
76✔
538

539
                        if (!route_can_update(link->manager, existing, route)) {
76✔
540
                                if (existing->source == NETWORK_CONFIG_SOURCE_STATIC) {
7✔
541
                                        log_link_debug(link, "Found an existing route that conflicts with new route based on a received RA, ignoring request.");
1✔
542
                                        return 0;
1✔
543
                                }
544

545
                                log_link_debug(link, "Found an existing route that conflicts with new route based on a received RA, removing.");
6✔
546
                                r = route_remove_and_cancel(existing, link->manager);
6✔
547
                                if (r < 0)
6✔
548
                                        return r;
549
                        }
550
                }
551
        }
552

553
        /* The preference (and priority) may be changed in the above loop. Restore it. */
554
        route->pref = pref_original;
201✔
555
        ndisc_set_route_priority(link, route);
201✔
556

557
        bool is_new = route_get(link->manager, route, NULL) < 0;
201✔
558

559
        r = link_request_route(link, route, &link->ndisc_messages, ndisc_route_handler);
201✔
560
        if (r < 0)
201✔
561
                return r;
562
        if (r > 0 && is_new)
201✔
563
                link->ndisc_configured = false;
132✔
564

565
        return 0;
566
}
567

568
static void ndisc_route_prepare(Route *route, Link *link) {
225✔
569
        assert(route);
225✔
570
        assert(link);
225✔
571

572
        route->source = NETWORK_CONFIG_SOURCE_NDISC;
225✔
573

574
        if (!route->table_set)
225✔
575
                route->table = link_get_ndisc_route_table(link);
225✔
576
}
225✔
577

578
static int ndisc_router_route_prepare(Route *route, Link *link, sd_ndisc_router *rt) {
225✔
579
        assert(route);
225✔
580
        assert(link);
225✔
581
        assert(rt);
225✔
582

583
        ndisc_route_prepare(route, link);
225✔
584

585
        if (!route->protocol_set)
225✔
586
                route->protocol = RTPROT_RA;
225✔
587

588
        return sd_ndisc_router_get_sender_address(rt, &route->provider.in6);
225✔
589
}
590

591
static int ndisc_request_router_route(Route *route, Link *link, sd_ndisc_router *rt) {
202✔
592
        int r;
202✔
593

594
        assert(route);
202✔
595
        assert(link);
202✔
596
        assert(rt);
202✔
597

598
        r = ndisc_router_route_prepare(route, link, rt);
202✔
599
        if (r < 0)
202✔
600
                return r;
601

602
        return ndisc_request_route(route, link);
202✔
603
}
604

605
static int ndisc_remove_route(Route *route, Link *link) {
23✔
606
        int r, ret = 0;
23✔
607

608
        assert(route);
23✔
609
        assert(link);
23✔
610
        assert(link->manager);
23✔
611

612
        r = ndisc_set_route_nexthop(route, link, /* request = */ false);
23✔
613
        if (r < 0)
23✔
614
                return r;
23✔
615

616
        uint8_t pref, pref_original = route->pref;
23✔
617
        FOREACH_ARGUMENT(pref, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH) {
92✔
618
                Route *existing;
69✔
619
                Request *req;
69✔
620

621
                /* If the preference is specified by the user config (that is, for semi-static routes),
622
                 * rather than RA, then only search conflicting routes that have the same preference. */
623
                if (route->pref_set && pref != pref_original)
69✔
624
                        continue;
3✔
625

626
                route->pref = pref;
69✔
627
                ndisc_set_route_priority(link, route);
69✔
628

629
                /* Unfortunately, we cannot directly pass 'route' to route_remove_and_cancel() here, as the
630
                 * same or similar route may be configured or requested statically. */
631

632
                /* First, check if the route is already requested. If there is an existing route, and also an
633
                 * existing pending request, then the source may be updated by the request. So, we first need
634
                 * to check the source of the requested route. */
635
                if (route_get_request(link->manager, route, &req) >= 0) {
69✔
636
                        existing = ASSERT_PTR(req->userdata);
×
637
                        if (existing->source == NETWORK_CONFIG_SOURCE_STATIC)
×
638
                                continue;
×
639

640
                        RET_GATHER(ret, route_remove_and_cancel(existing, link->manager));
×
641
                }
642

643
                /* Then, check if the route exists. */
644
                if (route_get(link->manager, route, &existing) >= 0) {
69✔
645
                        if (existing->source == NETWORK_CONFIG_SOURCE_STATIC)
23✔
646
                                continue;
3✔
647

648
                        RET_GATHER(ret, route_remove_and_cancel(existing, link->manager));
20✔
649
                }
650
        }
651

652
        return RET_GATHER(ret, ndisc_remove_unused_nexthop_by_id(link, route->nexthop_id));
23✔
653
}
654

655
static int ndisc_remove_router_route(Route *route, Link *link, sd_ndisc_router *rt) {
23✔
656
        int r;
23✔
657

658
        assert(route);
23✔
659
        assert(link);
23✔
660
        assert(rt);
23✔
661

662
        r = ndisc_router_route_prepare(route, link, rt);
23✔
663
        if (r < 0)
23✔
664
                return r;
665

666
        return ndisc_remove_route(route, link);
23✔
667
}
668

669
static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
87✔
670
        int r;
87✔
671

672
        assert(link);
87✔
673

674
        r = address_configure_handler_internal(rtnl, m, link, "Could not set NDisc address");
87✔
675
        if (r <= 0)
87✔
676
                return r;
677

678
        r = ndisc_check_ready(link);
87✔
679
        if (r < 0)
87✔
680
                link_enter_failed(link);
×
681

682
        return 1;
683
}
684

685
static int ndisc_request_address(Address *address, Link *link) {
87✔
686
        bool is_new;
87✔
687
        int r;
87✔
688

689
        assert(address);
87✔
690
        assert(link);
87✔
691

692
        address->source = NETWORK_CONFIG_SOURCE_NDISC;
87✔
693

694
        r = free_and_strdup_warn(&address->netlabel, link->network->ndisc_netlabel);
87✔
695
        if (r < 0)
87✔
696
                return r;
87✔
697

698
        Address *existing;
87✔
699
        if (address_get_harder(link, address, &existing) < 0)
87✔
700
                is_new = true;
701
        else if (address_can_update(existing, address))
26✔
702
                is_new = false;
703
        else if (existing->source == NETWORK_CONFIG_SOURCE_DHCP6) {
×
704
                /* SLAAC address is preferred over DHCPv6 address. */
705
                log_link_debug(link, "Conflicting DHCPv6 address %s exists, removing.",
×
706
                               IN_ADDR_PREFIX_TO_STRING(existing->family, &existing->in_addr, existing->prefixlen));
707
                r = address_remove(existing, link);
×
708
                if (r < 0)
×
709
                        return r;
710

711
                is_new = true;
712
        } else {
713
                /* Conflicting static address is configured?? */
714
                log_link_debug(link, "Conflicting address %s exists, ignoring request.",
×
715
                               IN_ADDR_PREFIX_TO_STRING(existing->family, &existing->in_addr, existing->prefixlen));
716
                return 0;
×
717
        }
718

719
        r = link_request_address(link, address, &link->ndisc_messages,
87✔
720
                                 ndisc_address_handler, NULL);
721
        if (r < 0)
87✔
722
                return r;
723
        if (r > 0 && is_new)
87✔
724
                link->ndisc_configured = false;
61✔
725

726
        return 0;
727
}
728

729
int ndisc_reconfigure_address(Address *address, Link *link) {
5✔
730
        int r;
5✔
731

732
        assert(address);
5✔
733
        assert(address->source == NETWORK_CONFIG_SOURCE_NDISC);
5✔
734
        assert(link);
5✔
735

736
        r = regenerate_address(address, link);
5✔
737
        if (r <= 0)
5✔
738
                return r;
739

740
        r = ndisc_request_address(address, link);
5✔
741
        if (r < 0)
5✔
742
                return r;
743

744
        if (!link->ndisc_configured)
5✔
745
                link_set_state(link, LINK_STATE_CONFIGURING);
4✔
746

747
        link_check_ready(link);
5✔
748
        return 0;
5✔
749
}
750

751
static int ndisc_redirect_route_new(sd_ndisc_redirect *rd, Route **ret) {
×
752
        _cleanup_(route_unrefp) Route *route = NULL;
×
753
        struct in6_addr gateway, destination;
×
754
        int r;
×
755

756
        assert(rd);
×
757
        assert(ret);
×
758

759
        r = sd_ndisc_redirect_get_target_address(rd, &gateway);
×
760
        if (r < 0)
×
761
                return r;
762

763
        r = sd_ndisc_redirect_get_destination_address(rd, &destination);
×
764
        if (r < 0)
×
765
                return r;
766

767
        r = route_new(&route);
×
768
        if (r < 0)
×
769
                return r;
770

771
        route->family = AF_INET6;
×
772
        if (!in6_addr_equal(&gateway, &destination)) {
×
773
                route->nexthop.gw.in6 = gateway;
×
774
                route->nexthop.family = AF_INET6;
×
775
        }
776
        route->dst.in6 = destination;
×
777
        route->dst_prefixlen = 128;
×
778
        route->protocol = RTPROT_REDIRECT;
×
779

780
        r = sd_ndisc_redirect_get_sender_address(rd, &route->provider.in6);
×
781
        if (r < 0)
×
782
                return r;
783

784
        *ret = TAKE_PTR(route);
×
785
        return 0;
×
786
}
787

788
static int ndisc_remove_redirect_route(Link *link, sd_ndisc_redirect *rd) {
×
789
        _cleanup_(route_unrefp) Route *route = NULL;
×
790
        int r;
×
791

792
        assert(link);
×
793
        assert(rd);
×
794

795
        r = ndisc_redirect_route_new(rd, &route);
×
796
        if (r < 0)
×
797
                return r;
798

799
        ndisc_route_prepare(route, link);
×
800

801
        return ndisc_remove_route(route, link);
×
802
}
803

804
static void ndisc_redirect_hash_func(const sd_ndisc_redirect *x, struct siphash *state) {
×
805
        struct in6_addr dest = {};
×
806

807
        assert(x);
×
808
        assert(state);
×
809

810
        (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest);
×
811

812
        siphash24_compress_typesafe(dest, state);
×
813
}
×
814

815
static int ndisc_redirect_compare_func(const sd_ndisc_redirect *x, const sd_ndisc_redirect *y) {
×
816
        struct in6_addr dest_x = {}, dest_y = {};
×
817

818
        assert(x);
×
819
        assert(y);
×
820

821
        (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest_x);
×
822
        (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) y, &dest_y);
×
823

824
        return memcmp(&dest_x, &dest_y, sizeof(dest_x));
×
825
}
826

827
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
828
                ndisc_redirect_hash_ops,
829
                sd_ndisc_redirect,
830
                ndisc_redirect_hash_func,
831
                ndisc_redirect_compare_func,
832
                sd_ndisc_redirect_unref);
833

834
static int ndisc_redirect_equal(sd_ndisc_redirect *x, sd_ndisc_redirect *y) {
×
835
        struct in6_addr a, b;
×
836
        int r;
×
837

838
        assert(x);
×
839
        assert(y);
×
840

841
        r = sd_ndisc_redirect_get_destination_address(x, &a);
×
842
        if (r < 0)
×
843
                return r;
×
844

845
        r = sd_ndisc_redirect_get_destination_address(y, &b);
×
846
        if (r < 0)
×
847
                return r;
848

849
        if (!in6_addr_equal(&a, &b))
×
850
                return false;
851

852
        r = sd_ndisc_redirect_get_target_address(x, &a);
×
853
        if (r < 0)
×
854
                return r;
855

856
        r = sd_ndisc_redirect_get_target_address(y, &b);
×
857
        if (r < 0)
×
858
                return r;
859

860
        return in6_addr_equal(&a, &b);
×
861
}
862

863
static int ndisc_redirect_drop_conflict(Link *link, sd_ndisc_redirect *rd) {
×
864
        _cleanup_(sd_ndisc_redirect_unrefp) sd_ndisc_redirect *existing = NULL;
×
865
        int r;
×
866

867
        assert(link);
×
868
        assert(rd);
×
869

870
        existing = set_remove(link->ndisc_redirects, rd);
×
871
        if (!existing)
×
872
                return 0;
873

874
        r = ndisc_redirect_equal(rd, existing);
×
875
        if (r != 0)
×
876
                return r;
877

878
        return ndisc_remove_redirect_route(link, existing);
×
879
}
880

881
static int ndisc_redirect_verify_sender(Link *link, sd_ndisc_redirect *rd) {
×
882
        int r;
×
883

884
        assert(link);
×
885
        assert(rd);
×
886

887
        /* RFC 4861 section 8.1
888
        * The IP source address of the Redirect is the same as the current first-hop router for the specified
889
        * ICMP Destination Address. */
890

891
        struct in6_addr sender;
×
892
        r = sd_ndisc_redirect_get_sender_address(rd, &sender);
×
893
        if (r < 0)
×
894
                return r;
×
895

896
        /* We will reuse the sender's router lifetime as the lifetime of the redirect route. Hence, if we
897
         * have not remembered an RA from the sender, refuse the Redirect message. */
898
        sd_ndisc_router *router = hashmap_get(link->ndisc_routers_by_sender, &sender);
×
899
        if (!router)
×
900
                return false;
901

902
        sd_ndisc_redirect *existing = set_get(link->ndisc_redirects, rd);
×
903
        if (existing) {
×
904
                struct in6_addr target, dest;
×
905

906
                /* If we have received Redirect message for the host, the sender must be the previous target. */
907

908
                r = sd_ndisc_redirect_get_target_address(existing, &target);
×
909
                if (r < 0)
×
910
                        return r;
×
911

912
                if (in6_addr_equal(&sender, &target))
×
913
                        return true;
914

915
                /* If the existing redirect route is on-link, that is, the destination and target address are
916
                 * equivalent, then also accept Redirect message from the current default router. This is not
917
                 * mentioned by the RFC, but without this, we cannot update on-link redirect route. */
918
                r = sd_ndisc_redirect_get_destination_address(existing, &dest);
×
919
                if (r < 0)
×
920
                        return r;
921

922
                if (!in6_addr_equal(&dest, &target))
×
923
                        return false;
924
        }
925

926
        /* Check if the sender is one of the known router with highest priority. */
927
        uint8_t preference;
×
928
        r = sd_ndisc_router_get_preference(router, &preference);
×
929
        if (r < 0)
×
930
                return r;
931

932
        if (preference == SD_NDISC_PREFERENCE_HIGH)
×
933
                return true;
934

935
        sd_ndisc_router *rt;
×
936
        HASHMAP_FOREACH(rt, link->ndisc_routers_by_sender) {
×
937
                if (rt == router)
×
938
                        continue;
×
939

940
                uint8_t pref;
×
941
                if (sd_ndisc_router_get_preference(rt, &pref) < 0)
×
942
                        continue;
×
943

944
                if (pref == SD_NDISC_PREFERENCE_HIGH ||
×
945
                    (pref == SD_NDISC_PREFERENCE_MEDIUM && preference == SD_NDISC_PREFERENCE_LOW))
×
946
                        return false;
×
947
        }
948

949
        return true;
×
950
}
951

952
static int ndisc_redirect_handler(Link *link, sd_ndisc_redirect *rd) {
×
953
        int r;
×
954

955
        assert(link);
×
956
        assert(link->network);
×
957
        assert(rd);
×
958

959
        if (!link->network->ndisc_use_redirect)
×
960
                return 0;
×
961

962
        usec_t now_usec;
×
963
        r = sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec);
×
964
        if (r < 0)
×
965
                return r;
966

967
        r = ndisc_drop_outdated(link, /* router = */ NULL, now_usec);
×
968
        if (r < 0)
×
969
                return r;
970

971
        r = ndisc_redirect_verify_sender(link, rd);
×
972
        if (r <= 0)
×
973
                return r;
974

975
        /* First, drop conflicting redirect route, if exists. */
976
        r = ndisc_redirect_drop_conflict(link, rd);
×
977
        if (r < 0)
×
978
                return r;
979

980
        /* Then, remember the received message. */
981
        r = set_ensure_put(&link->ndisc_redirects, &ndisc_redirect_hash_ops, rd);
×
982
        if (r < 0)
×
983
                return r;
984

985
        sd_ndisc_redirect_ref(rd);
×
986

987
        /* Finally, request the corresponding route. */
988
        _cleanup_(route_unrefp) Route *route = NULL;
×
989
        r = ndisc_redirect_route_new(rd, &route);
×
990
        if (r < 0)
×
991
                return r;
992

993
        sd_ndisc_router *rt = hashmap_get(link->ndisc_routers_by_sender, &route->provider.in6);
×
994
        if (!rt)
×
995
                return -EADDRNOTAVAIL;
996

997
        r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &route->lifetime_usec);
×
998
        if (r < 0)
×
999
                return r;
1000

1001
        ndisc_route_prepare(route, link);
×
1002

1003
        return ndisc_request_route(route, link);
×
1004
}
1005

1006
static int ndisc_drop_redirect(Link *link, const struct in6_addr *router) {
9,368✔
1007
        int r, ret = 0;
9,368✔
1008

1009
        assert(link);
9,368✔
1010

1011
        sd_ndisc_redirect *rd;
9,368✔
1012
        SET_FOREACH(rd, link->ndisc_redirects) {
9,368✔
1013
                if (router) {
×
1014
                        struct in6_addr a;
×
1015

1016
                        if (!(sd_ndisc_redirect_get_sender_address(rd, &a) >= 0 && in6_addr_equal(&a, router)) &&
×
1017
                            !(sd_ndisc_redirect_get_target_address(rd, &a) >= 0 && in6_addr_equal(&a, router)))
×
1018
                                continue;
×
1019
                }
1020

1021
                r = ndisc_remove_redirect_route(link, rd);
×
1022
                if (r < 0)
×
1023
                        RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove redirect route, ignoring: %m"));
×
1024

1025
                sd_ndisc_redirect_unref(set_remove(link->ndisc_redirects, rd));
×
1026
        }
1027

1028
        return ret;
9,368✔
1029
}
1030

1031
static int ndisc_update_redirect_sender(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
×
1032
        int r;
×
1033

1034
        assert(link);
×
1035
        assert(original_address);
×
1036
        assert(current_address);
×
1037

1038
        sd_ndisc_redirect *rd;
×
1039
        SET_FOREACH(rd, link->ndisc_redirects) {
×
1040
                struct in6_addr sender;
×
1041

1042
                r = sd_ndisc_redirect_get_sender_address(rd, &sender);
×
1043
                if (r < 0)
×
1044
                        return r;
×
1045

1046
                if (!in6_addr_equal(&sender, original_address))
×
1047
                        continue;
×
1048

1049
                r = sd_ndisc_redirect_set_sender_address(rd, current_address);
×
1050
                if (r < 0)
×
1051
                        return r;
1052
        }
1053

1054
        return 0;
×
1055
}
1056

1057
static int ndisc_router_drop_default(Link *link, sd_ndisc_router *rt) {
19✔
1058
        _cleanup_(route_unrefp) Route *route = NULL;
19✔
1059
        struct in6_addr gateway;
19✔
1060
        int r;
19✔
1061

1062
        assert(link);
19✔
1063
        assert(link->network);
19✔
1064
        assert(rt);
19✔
1065

1066
        r = sd_ndisc_router_get_sender_address(rt, &gateway);
19✔
1067
        if (r < 0)
19✔
1068
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
1069

1070
        r = route_new(&route);
19✔
1071
        if (r < 0)
19✔
1072
                return log_oom();
×
1073

1074
        route->family = AF_INET6;
19✔
1075
        route->nexthop.family = AF_INET6;
19✔
1076
        route->nexthop.gw.in6 = gateway;
19✔
1077

1078
        r = ndisc_remove_router_route(route, link, rt);
19✔
1079
        if (r < 0)
19✔
1080
                return log_link_warning_errno(link, r, "Failed to remove the default gateway configured by RA: %m");
×
1081

1082
        Route *route_gw;
19✔
1083
        HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
21✔
1084
                _cleanup_(route_unrefp) Route *tmp = NULL;
×
1085

1086
                if (route_gw->source != NETWORK_CONFIG_SOURCE_NDISC)
2✔
1087
                        continue;
2✔
1088

1089
                assert(route_gw->nexthop.family == AF_INET6);
×
1090

1091
                r = route_dup(route_gw, NULL, &tmp);
×
1092
                if (r < 0)
×
1093
                        return r;
1094

1095
                tmp->nexthop.gw.in6 = gateway;
×
1096

1097
                r = ndisc_remove_router_route(tmp, link, rt);
×
1098
                if (r < 0)
×
1099
                        return log_link_warning_errno(link, r, "Could not remove semi-static gateway: %m");
×
1100
        }
1101

1102
        return 0;
19✔
1103
}
1104

1105
static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
108✔
1106
        usec_t lifetime_usec;
108✔
1107
        struct in6_addr gateway;
108✔
1108
        uint8_t preference;
108✔
1109
        int r;
108✔
1110

1111
        assert(link);
108✔
1112
        assert(link->network);
108✔
1113
        assert(rt);
108✔
1114

1115
        /* If the router lifetime is zero, the router should not be used as the default gateway. */
1116
        r = sd_ndisc_router_get_lifetime(rt, NULL);
108✔
1117
        if (r < 0)
108✔
1118
                return r;
108✔
1119
        if (r == 0)
108✔
1120
                return ndisc_router_drop_default(link, rt);
19✔
1121

1122
        if (!link->network->ndisc_use_gateway &&
89✔
1123
            hashmap_isempty(link->network->routes_by_section))
×
1124
                return 0;
1125

1126
        r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
89✔
1127
        if (r < 0)
89✔
1128
                return log_link_warning_errno(link, r, "Failed to get gateway lifetime from RA: %m");
×
1129

1130
        r = sd_ndisc_router_get_sender_address(rt, &gateway);
89✔
1131
        if (r < 0)
89✔
1132
                return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
×
1133

1134
        r = sd_ndisc_router_get_preference(rt, &preference);
89✔
1135
        if (r < 0)
89✔
1136
                return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
×
1137

1138
        if (link->network->ndisc_use_gateway) {
89✔
1139
                _cleanup_(route_unrefp) Route *route = NULL;
89✔
1140

1141
                r = route_new(&route);
89✔
1142
                if (r < 0)
89✔
1143
                        return log_oom();
×
1144

1145
                route->family = AF_INET6;
89✔
1146
                route->pref = preference;
89✔
1147
                route->nexthop.family = AF_INET6;
89✔
1148
                route->nexthop.gw.in6 = gateway;
89✔
1149
                route->lifetime_usec = lifetime_usec;
89✔
1150

1151
                r = ndisc_request_router_route(route, link, rt);
89✔
1152
                if (r < 0)
89✔
1153
                        return log_link_warning_errno(link, r, "Could not request default route: %m");
×
1154
        }
1155

1156
        Route *route_gw;
89✔
1157
        HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
101✔
1158
                _cleanup_(route_unrefp) Route *route = NULL;
10✔
1159

1160
                if (route_gw->source != NETWORK_CONFIG_SOURCE_NDISC)
12✔
1161
                        continue;
2✔
1162

1163
                assert(route_gw->nexthop.family == AF_INET6);
10✔
1164

1165
                r = route_dup(route_gw, NULL, &route);
10✔
1166
                if (r < 0)
10✔
1167
                        return r;
1168

1169
                route->nexthop.gw.in6 = gateway;
10✔
1170
                if (!route->pref_set)
10✔
1171
                        route->pref = preference;
10✔
1172
                route->lifetime_usec = lifetime_usec;
10✔
1173

1174
                r = ndisc_request_router_route(route, link, rt);
10✔
1175
                if (r < 0)
10✔
1176
                        return log_link_warning_errno(link, r, "Could not request gateway: %m");
×
1177
        }
1178

1179
        return 0;
89✔
1180
}
1181

1182
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
×
1183
                ndisc_router_hash_ops,
1184
                struct in6_addr,
1185
                in6_addr_hash_func,
1186
                in6_addr_compare_func,
1187
                sd_ndisc_router,
1188
                sd_ndisc_router_unref);
1189

1190
static int ndisc_update_router_address(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
×
1191
        _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
×
1192
        int r;
×
1193

1194
        assert(link);
×
1195
        assert(original_address);
×
1196
        assert(current_address);
×
1197

1198
        rt = hashmap_remove(link->ndisc_routers_by_sender, original_address);
×
1199
        if (!rt)
×
1200
                return 0;
1201

1202
        /* If we already received an RA from the new address, then forget the RA from the old address. */
1203
        if (hashmap_contains(link->ndisc_routers_by_sender, current_address))
×
1204
                return 0;
1205

1206
        /* Otherwise, update the sender address of the previously received RA. */
1207
        r = sd_ndisc_router_set_sender_address(rt, current_address);
×
1208
        if (r < 0)
×
1209
                return r;
1210

1211
        r = hashmap_put(link->ndisc_routers_by_sender, &rt->packet->sender_address, rt);
×
1212
        if (r < 0)
×
1213
                return r;
1214

1215
        TAKE_PTR(rt);
1216
        return 0;
1217
}
1218

1219
static int ndisc_drop_router_one(Link *link, sd_ndisc_router *rt, usec_t timestamp_usec) {
112✔
1220
        usec_t lifetime_usec;
112✔
1221
        int r;
112✔
1222

1223
        assert(link);
112✔
1224
        assert(rt);
112✔
1225
        assert(rt->packet);
112✔
1226

1227
        r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
112✔
1228
        if (r < 0)
112✔
1229
                return r;
112✔
1230

1231
        if (lifetime_usec > timestamp_usec)
112✔
1232
                return 0;
1233

1234
        r = ndisc_drop_redirect(link, &rt->packet->sender_address);
42✔
1235

1236
        sd_ndisc_router_unref(hashmap_remove(link->ndisc_routers_by_sender, &rt->packet->sender_address));
42✔
1237

1238
        return r;
1239
}
1240

1241
static int ndisc_drop_routers(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
9,423✔
1242
        sd_ndisc_router *rt;
9,423✔
1243
        int ret = 0;
9,423✔
1244

1245
        assert(link);
9,423✔
1246

1247
        if (router) {
9,423✔
1248
                rt = hashmap_get(link->ndisc_routers_by_sender, router);
×
1249
                if (!rt)
×
1250
                        return 0;
9,423✔
1251

1252
                return ndisc_drop_router_one(link, rt, timestamp_usec);
×
1253
        }
1254

1255
        HASHMAP_FOREACH_KEY(rt, router, link->ndisc_routers_by_sender)
9,535✔
1256
                RET_GATHER(ret, ndisc_drop_router_one(link, rt, timestamp_usec));
112✔
1257

1258
        return ret;
9,423✔
1259
}
1260

1261
static int ndisc_remember_router(Link *link, sd_ndisc_router *rt) {
108✔
1262
        int r;
108✔
1263

1264
        assert(link);
108✔
1265
        assert(rt);
108✔
1266
        assert(rt->packet);
108✔
1267

1268
        sd_ndisc_router_unref(hashmap_remove(link->ndisc_routers_by_sender, &rt->packet->sender_address));
108✔
1269

1270
        /* Remember RAs with non-zero lifetime. */
1271
        r = sd_ndisc_router_get_lifetime(rt, NULL);
108✔
1272
        if (r <= 0)
108✔
1273
                return r;
1274

1275
        r = hashmap_ensure_put(&link->ndisc_routers_by_sender, &ndisc_router_hash_ops, &rt->packet->sender_address, rt);
89✔
1276
        if (r < 0)
89✔
1277
                return r;
1278

1279
        sd_ndisc_router_ref(rt);
89✔
1280
        return 0;
89✔
1281
}
1282

1283
static int ndisc_router_process_reachable_time(Link *link, sd_ndisc_router *rt) {
108✔
1284
        usec_t reachable_time, msec;
108✔
1285
        int r;
108✔
1286

1287
        assert(link);
108✔
1288
        assert(link->manager);
108✔
1289
        assert(link->network);
108✔
1290
        assert(rt);
108✔
1291

1292
        if (!link->network->ndisc_use_reachable_time)
108✔
1293
                return 0;
108✔
1294

1295
        r = sd_ndisc_router_get_reachable_time(rt, &reachable_time);
108✔
1296
        if (r < 0)
108✔
1297
                return log_link_warning_errno(link, r, "Failed to get reachable time from RA: %m");
×
1298

1299
        /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4) */
1300
        if (!timestamp_is_set(reachable_time))
108✔
1301
                return 0;
1302

1303
        msec = DIV_ROUND_UP(reachable_time, USEC_PER_MSEC);
15✔
1304
        if (msec <= 0 || msec > UINT32_MAX) {
15✔
1305
                log_link_debug(link, "Failed to get reachable time from RA - out of range (%"PRIu64"), ignoring", msec);
×
1306
                return 0;
×
1307
        }
1308

1309
        /* Set the reachable time for Neighbor Solicitations. */
1310
        r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "base_reachable_time_ms", (uint32_t) msec, manager_get_sysctl_shadow(link->manager));
15✔
1311
        if (r < 0)
15✔
1312
                log_link_warning_errno(link, r, "Failed to apply neighbor reachable time (%"PRIu64"), ignoring: %m", msec);
×
1313

1314
        return 0;
1315
}
1316

1317
static int ndisc_router_process_retransmission_time(Link *link, sd_ndisc_router *rt) {
108✔
1318
        usec_t retrans_time, msec;
108✔
1319
        int r;
108✔
1320

1321
        assert(link);
108✔
1322
        assert(link->manager);
108✔
1323
        assert(link->network);
108✔
1324
        assert(rt);
108✔
1325

1326
        if (!link->network->ndisc_use_retransmission_time)
108✔
1327
                return 0;
108✔
1328

1329
        r = sd_ndisc_router_get_retransmission_time(rt, &retrans_time);
108✔
1330
        if (r < 0)
108✔
1331
                return log_link_warning_errno(link, r, "Failed to get retransmission time from RA: %m");
×
1332

1333
        /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4) */
1334
        if (!timestamp_is_set(retrans_time))
108✔
1335
                return 0;
1336

1337
        msec = DIV_ROUND_UP(retrans_time, USEC_PER_MSEC);
15✔
1338
        if (msec <= 0 || msec > UINT32_MAX) {
15✔
1339
                log_link_debug(link, "Failed to get retransmission time from RA - out of range (%"PRIu64"), ignoring", msec);
×
1340
                return 0;
×
1341
        }
1342

1343
        /* Set the retransmission time for Neighbor Solicitations. */
1344
        r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", (uint32_t) msec, manager_get_sysctl_shadow(link->manager));
15✔
1345
        if (r < 0)
15✔
1346
                log_link_warning_errno(link, r, "Failed to apply neighbor retransmission time (%"PRIu64"), ignoring: %m", msec);
×
1347

1348
        return 0;
1349
}
1350

1351
static int ndisc_router_process_hop_limit(Link *link, sd_ndisc_router *rt) {
108✔
1352
        uint8_t hop_limit;
108✔
1353
        int r;
108✔
1354

1355
        assert(link);
108✔
1356
        assert(link->manager);
108✔
1357
        assert(link->network);
108✔
1358
        assert(rt);
108✔
1359

1360
        if (!link->network->ndisc_use_hop_limit)
108✔
1361
                return 0;
108✔
1362

1363
        r = sd_ndisc_router_get_hop_limit(rt, &hop_limit);
108✔
1364
        if (r < 0)
108✔
1365
                return log_link_warning_errno(link, r, "Failed to get hop limit from RA: %m");
×
1366

1367
        /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4):
1368
         *
1369
         * A Router Advertisement field (e.g., Cur Hop Limit, Reachable Time, and Retrans Timer) may contain
1370
         * a value denoting that it is unspecified. In such cases, the parameter should be ignored and the
1371
         * host should continue using whatever value it is already using. In particular, a host MUST NOT
1372
         * interpret the unspecified value as meaning change back to the default value that was in use before
1373
         * the first Router Advertisement was received.
1374
         *
1375
         * If the received Cur Hop Limit value is non-zero, the host SHOULD set
1376
         * its CurHopLimit variable to the received value. */
1377
        if (hop_limit <= 0)
108✔
1378
                return 0;
1379

1380
        r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "hop_limit", (uint32_t) hop_limit, manager_get_sysctl_shadow(link->manager));
46✔
1381
        if (r < 0)
46✔
1382
                log_link_warning_errno(link, r, "Failed to apply hop_limit (%u), ignoring: %m", hop_limit);
×
1383

1384
        return 0;
1385
}
1386

1387
static int ndisc_router_process_mtu(Link *link, sd_ndisc_router *rt) {
108✔
1388
        uint32_t mtu;
108✔
1389
        int r;
108✔
1390

1391
        assert(link);
108✔
1392
        assert(link->network);
108✔
1393
        assert(rt);
108✔
1394

1395
        if (!link->network->ndisc_use_mtu)
108✔
1396
                return 0;
108✔
1397

1398
        r = sd_ndisc_router_get_mtu(rt, &mtu);
108✔
1399
        if (r == -ENODATA)
108✔
1400
                return 0;
1401
        if (r < 0)
32✔
1402
                return log_link_warning_errno(link, r, "Failed to get MTU from RA: %m");
×
1403

1404
        link->ndisc_mtu = mtu;
32✔
1405

1406
        (void) link_set_ipv6_mtu(link, LOG_DEBUG);
32✔
1407

1408
        return 0;
1409
}
1410

1411
static int ndisc_address_set_lifetime(Address *address, Link *link, sd_ndisc_router *rt) {
82✔
1412
        Address *existing;
82✔
1413
        usec_t t;
82✔
1414
        int r;
82✔
1415

1416
        assert(address);
82✔
1417
        assert(link);
82✔
1418
        assert(rt);
82✔
1419

1420
        /* This is mostly based on RFC 4862 section 5.5.3 (e). However, the definition of 'RemainingLifetime'
1421
         * is ambiguous, and there is no clear explanation when the address is not assigned yet. If we assume
1422
         * that 'RemainingLifetime' is zero in that case, then IPv6 Core Conformance test [v6LC.3.2.5 Part C]
1423
         * fails. So, in such case, we skip the conditions about 'RemainingLifetime'. */
1424

1425
        r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_valid_usec);
82✔
1426
        if (r < 0)
82✔
1427
                return r;
82✔
1428

1429
        r = sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_preferred_usec);
82✔
1430
        if (r < 0)
82✔
1431
                return r;
1432

1433
        /* RFC 4862 section 5.5.3 (e)
1434
         * 1. If the received Valid Lifetime is greater than 2 hours or greater than RemainingLifetime,
1435
         *    set the valid lifetime of the corresponding address to the advertised Valid Lifetime. */
1436
        r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &t);
82✔
1437
        if (r < 0)
82✔
1438
                return r;
1439

1440
        if (t > 2 * USEC_PER_HOUR)
82✔
1441
                return 0;
1442

1443
        if (address_get(link, address, &existing) < 0 || existing->source != NETWORK_CONFIG_SOURCE_NDISC)
70✔
1444
                return 0;
1445

1446
        if (address->lifetime_valid_usec > existing->lifetime_valid_usec)
19✔
1447
                return 0;
1448

1449
        /* 2. If RemainingLifetime is less than or equal to 2 hours, ignore the Prefix Information option
1450
         *    with regards to the valid lifetime, unless the Router Advertisement from which this option was
1451
         *    obtained has been authenticated (e.g., via Secure Neighbor Discovery [RFC3971]). If the Router
1452
         *    Advertisement was authenticated, the valid lifetime of the corresponding address should be set
1453
         *    to the Valid Lifetime in the received option.
1454
         *
1455
         * Currently, authentication is not supported. So check the lifetime of the existing address. */
1456
        r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &t);
3✔
1457
        if (r < 0)
3✔
1458
                return r;
1459

1460
        if (existing->lifetime_valid_usec <= usec_add(t, 2 * USEC_PER_HOUR)) {
3✔
1461
                address->lifetime_valid_usec = existing->lifetime_valid_usec;
3✔
1462
                return 0;
3✔
1463
        }
1464

1465
        /* 3. Otherwise, reset the valid lifetime of the corresponding address to 2 hours. */
1466
        address->lifetime_valid_usec = usec_add(t, 2 * USEC_PER_HOUR);
×
1467
        return 0;
×
1468
}
1469

1470
static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
66✔
1471
        usec_t lifetime_valid_usec, lifetime_preferred_usec;
66✔
1472
        struct in6_addr prefix, router;
66✔
1473
        uint8_t prefixlen;
66✔
1474
        int r;
66✔
1475

1476
        assert(link);
66✔
1477
        assert(link->network);
66✔
1478
        assert(rt);
66✔
1479

1480
        if (!link->network->ndisc_use_autonomous_prefix)
66✔
1481
                return 0;
66✔
1482

1483
        r = sd_ndisc_router_get_sender_address(rt, &router);
66✔
1484
        if (r < 0)
66✔
1485
                return log_link_warning_errno(link, r, "Failed to get router address: %m");
×
1486

1487
        r = sd_ndisc_router_prefix_get_address(rt, &prefix);
66✔
1488
        if (r < 0)
66✔
1489
                return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
×
1490

1491
        r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
66✔
1492
        if (r < 0)
66✔
1493
                return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
×
1494

1495
        /* ndisc_generate_addresses() below requires the prefix length <= 64. */
1496
        if (prefixlen > 64) {
66✔
1497
                log_link_debug(link, "Prefix is longer than 64, ignoring autonomous prefix %s.",
×
1498
                               IN6_ADDR_PREFIX_TO_STRING(&prefix, prefixlen));
1499
                return 0;
×
1500
        }
1501

1502
        r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid_usec);
66✔
1503
        if (r < 0)
66✔
1504
                return log_link_warning_errno(link, r, "Failed to get prefix valid lifetime: %m");
×
1505

1506
        r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred_usec);
66✔
1507
        if (r < 0)
66✔
1508
                return log_link_warning_errno(link, r, "Failed to get prefix preferred lifetime: %m");
×
1509

1510
        /* RFC 4862 section 5.5.3 (c)
1511
         * If the preferred lifetime is greater than the valid lifetime, silently ignore the Prefix
1512
         * Information option. */
1513
        if (lifetime_preferred_usec > lifetime_valid_usec)
66✔
1514
                return 0;
1515

1516
        _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
66✔
1517
        r = ndisc_generate_addresses(link, &prefix, prefixlen, &tokens_by_address);
66✔
1518
        if (r < 0)
66✔
1519
                return log_link_warning_errno(link, r, "Failed to generate SLAAC addresses: %m");
×
1520

1521
        IPv6Token *token;
66✔
1522
        struct in6_addr *a;
66✔
1523
        HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
148✔
1524
                _cleanup_(address_unrefp) Address *address = NULL;
82✔
1525

1526
                r = address_new(&address);
82✔
1527
                if (r < 0)
82✔
1528
                        return log_oom();
×
1529

1530
                address->provider.in6 = router;
82✔
1531
                address->family = AF_INET6;
82✔
1532
                address->in_addr.in6 = *a;
82✔
1533
                address->prefixlen = prefixlen;
82✔
1534
                address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
82✔
1535
                address->token = ipv6_token_ref(token);
82✔
1536

1537
                r = ndisc_address_set_lifetime(address, link, rt);
82✔
1538
                if (r < 0)
82✔
1539
                        return log_link_warning_errno(link, r, "Failed to set lifetime of SLAAC address: %m");
×
1540

1541
                assert(address->lifetime_preferred_usec <= address->lifetime_valid_usec);
82✔
1542

1543
                r = ndisc_request_address(address, link);
82✔
1544
                if (r < 0)
82✔
1545
                        return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
×
1546
        }
1547

1548
        return 0;
66✔
1549
}
1550

1551
static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
96✔
1552
        _cleanup_(route_unrefp) Route *route = NULL;
96✔
1553
        uint8_t prefixlen, preference;
96✔
1554
        usec_t lifetime_usec;
96✔
1555
        struct in6_addr prefix;
96✔
1556
        int r;
96✔
1557

1558
        assert(link);
96✔
1559
        assert(link->network);
96✔
1560
        assert(rt);
96✔
1561

1562
        if (!link->network->ndisc_use_onlink_prefix)
96✔
1563
                return 0;
1564

1565
        r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
96✔
1566
        if (r < 0)
96✔
1567
                return log_link_warning_errno(link, r, "Failed to get prefix lifetime: %m");
×
1568

1569
        r = sd_ndisc_router_prefix_get_address(rt, &prefix);
96✔
1570
        if (r < 0)
96✔
1571
                return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
×
1572

1573
        r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
96✔
1574
        if (r < 0)
96✔
1575
                return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
×
1576

1577
        /* Prefix Information option does not have preference, hence we use the 'main' preference here */
1578
        r = sd_ndisc_router_get_preference(rt, &preference);
96✔
1579
        if (r < 0)
96✔
1580
                return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
×
1581

1582
        r = route_new(&route);
96✔
1583
        if (r < 0)
96✔
1584
                return log_oom();
×
1585

1586
        route->family = AF_INET6;
96✔
1587
        route->dst.in6 = prefix;
96✔
1588
        route->dst_prefixlen = prefixlen;
96✔
1589
        route->pref = preference;
96✔
1590
        route->lifetime_usec = lifetime_usec;
96✔
1591

1592
        /* RFC 4861 section 6.3.4:
1593
         * - If the prefix is not already present in the Prefix List, and the Prefix Information option's
1594
         *   Valid Lifetime field is non-zero, create a new entry for the prefix and initialize its
1595
         *   invalidation timer to the Valid Lifetime value in the Prefix Information option.
1596
         *
1597
         * - If the prefix is already present in the host's Prefix List as the result of a previously
1598
         *   received advertisement, reset its invalidation timer to the Valid Lifetime value in the Prefix
1599
         *   Information option. If the new Lifetime value is zero, timeout the prefix immediately. */
1600
        if (lifetime_usec == 0) {
96✔
1601
                r = ndisc_remove_router_route(route, link, rt);
×
1602
                if (r < 0)
×
1603
                        return log_link_warning_errno(link, r, "Failed to remove prefix route: %m");
×
1604
        } else {
1605
                r = ndisc_request_router_route(route, link, rt);
96✔
1606
                if (r < 0)
96✔
1607
                        return log_link_warning_errno(link, r, "Failed to request prefix route: %m");
×
1608
        }
1609

1610
        return 0;
1611
}
1612

1613
static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
212✔
1614
        uint8_t flags, prefixlen;
212✔
1615
        struct in6_addr a;
212✔
1616
        int r;
212✔
1617

1618
        assert(link);
212✔
1619
        assert(link->network);
212✔
1620
        assert(rt);
212✔
1621

1622
        usec_t lifetime_usec;
212✔
1623
        r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_usec);
212✔
1624
        if (r < 0)
212✔
1625
                return log_link_warning_errno(link, r, "Failed to get prefix lifetime: %m");
×
1626

1627
        if ((lifetime_usec == 0) != zero_lifetime)
212✔
1628
                return 0;
1629

1630
        r = sd_ndisc_router_prefix_get_address(rt, &a);
106✔
1631
        if (r < 0)
106✔
1632
                return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
×
1633

1634
        /* RFC 4861 Section 4.6.2:
1635
         * A router SHOULD NOT send a prefix option for the link-local prefix and a host SHOULD ignore such
1636
         * a prefix option. */
1637
        if (in6_addr_is_link_local(&a)) {
106✔
1638
                log_link_debug(link, "Received link-local prefix, ignoring prefix.");
×
1639
                return 0;
×
1640
        }
1641

1642
        r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
106✔
1643
        if (r < 0)
106✔
1644
                return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
×
1645

1646
        if (in6_prefix_is_filtered(&a, prefixlen, link->network->ndisc_allow_listed_prefix, link->network->ndisc_deny_listed_prefix)) {
106✔
1647
                if (set_isempty(link->network->ndisc_allow_listed_prefix))
10✔
1648
                        log_link_debug(link, "Prefix '%s' is in deny list, ignoring.",
1✔
1649
                                       IN6_ADDR_PREFIX_TO_STRING(&a, prefixlen));
1650
                else
1651
                        log_link_debug(link, "Prefix '%s' is not in allow list, ignoring.",
9✔
1652
                                       IN6_ADDR_PREFIX_TO_STRING(&a, prefixlen));
1653
                return 0;
10✔
1654
        }
1655

1656
        r = sd_ndisc_router_prefix_get_flags(rt, &flags);
96✔
1657
        if (r < 0)
96✔
1658
                return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
×
1659

1660
        if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
96✔
1661
                r = ndisc_router_process_onlink_prefix(link, rt);
96✔
1662
                if (r < 0)
96✔
1663
                        return r;
1664
        }
1665

1666
        if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
96✔
1667
                r = ndisc_router_process_autonomous_prefix(link, rt);
66✔
1668
                if (r < 0)
66✔
1669
                        return r;
×
1670
        }
1671

1672
        return 0;
1673
}
1674

1675
static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
28✔
1676
        _cleanup_(route_unrefp) Route *route = NULL;
28✔
1677
        uint8_t preference, prefixlen;
28✔
1678
        struct in6_addr gateway, dst;
28✔
1679
        usec_t lifetime_usec;
28✔
1680
        int r;
28✔
1681

1682
        assert(link);
28✔
1683

1684
        if (!link->network->ndisc_use_route_prefix)
28✔
1685
                return 0;
1686

1687
        r = sd_ndisc_router_route_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
28✔
1688
        if (r < 0)
28✔
1689
                return log_link_warning_errno(link, r, "Failed to get route lifetime from RA: %m");
×
1690

1691
        if ((lifetime_usec == 0) != zero_lifetime)
28✔
1692
                return 0;
1693

1694
        r = sd_ndisc_router_route_get_address(rt, &dst);
14✔
1695
        if (r < 0)
14✔
1696
                return log_link_warning_errno(link, r, "Failed to get route destination address: %m");
×
1697

1698
        r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
14✔
1699
        if (r < 0)
14✔
1700
                return log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
×
1701

1702
        if (in6_prefix_is_filtered(&dst, prefixlen,
14✔
1703
                                   link->network->ndisc_allow_listed_route_prefix,
1704
                                   link->network->ndisc_deny_listed_route_prefix)) {
14✔
1705
                if (set_isempty(link->network->ndisc_allow_listed_route_prefix))
3✔
1706
                        log_link_debug(link, "Route prefix '%s' is in deny list, ignoring.",
3✔
1707
                                       IN6_ADDR_PREFIX_TO_STRING(&dst, prefixlen));
1708
                else
1709
                        log_link_debug(link, "Route prefix '%s' is not in allow list, ignoring.",
×
1710
                                       IN6_ADDR_PREFIX_TO_STRING(&dst, prefixlen));
1711
                return 0;
3✔
1712
        }
1713

1714
        r = sd_ndisc_router_get_sender_address(rt, &gateway);
11✔
1715
        if (r < 0)
11✔
1716
                return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
×
1717

1718
        if (link_get_ipv6_address(link, &gateway, NULL) >= 0) {
11✔
1719
                if (DEBUG_LOGGING)
×
1720
                        log_link_debug(link, "Advertised route gateway %s is local to the link, ignoring route",
×
1721
                                       IN6_ADDR_TO_STRING(&gateway));
1722
                return 0;
×
1723
        }
1724

1725
        r = sd_ndisc_router_route_get_preference(rt, &preference);
11✔
1726
        if (r < 0)
11✔
1727
                return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
×
1728

1729
        r = route_new(&route);
11✔
1730
        if (r < 0)
11✔
1731
                return log_oom();
×
1732

1733
        route->family = AF_INET6;
11✔
1734
        route->pref = preference;
11✔
1735
        route->nexthop.gw.in6 = gateway;
11✔
1736
        route->nexthop.family = AF_INET6;
11✔
1737
        route->dst.in6 = dst;
11✔
1738
        route->dst_prefixlen = prefixlen;
11✔
1739
        route->lifetime_usec = lifetime_usec;
11✔
1740

1741
        if (lifetime_usec != 0) {
11✔
1742
                r = ndisc_request_router_route(route, link, rt);
7✔
1743
                if (r < 0)
7✔
1744
                        return log_link_warning_errno(link, r, "Could not request additional route: %m");
×
1745
        } else {
1746
                r = ndisc_remove_router_route(route, link, rt);
4✔
1747
                if (r < 0)
4✔
1748
                        return log_link_warning_errno(link, r, "Could not remove additional route with zero lifetime: %m");
×
1749
        }
1750

1751
        return 0;
1752
}
1753

1754
static void ndisc_rdnss_hash_func(const NDiscRDNSS *x, struct siphash *state) {
82✔
1755
        siphash24_compress_typesafe(x->address, state);
82✔
1756
}
82✔
1757

1758
static int ndisc_rdnss_compare_func(const NDiscRDNSS *a, const NDiscRDNSS *b) {
57✔
1759
        return memcmp(&a->address, &b->address, sizeof(a->address));
57✔
1760
}
1761

1762
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
1763
                ndisc_rdnss_hash_ops,
1764
                NDiscRDNSS,
1765
                ndisc_rdnss_hash_func,
1766
                ndisc_rdnss_compare_func,
1767
                free);
1768

1769
static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
66✔
1770
        usec_t lifetime_usec;
66✔
1771
        const struct in6_addr *a;
66✔
1772
        struct in6_addr router;
66✔
1773
        bool updated = false, logged_about_too_many = false;
66✔
1774
        int n, r;
66✔
1775

1776
        assert(link);
66✔
1777
        assert(link->network);
66✔
1778
        assert(rt);
66✔
1779

1780
        if (!link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC))
66✔
1781
                return 0;
66✔
1782

1783
        r = sd_ndisc_router_get_sender_address(rt, &router);
48✔
1784
        if (r < 0)
48✔
1785
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
1786

1787
        r = sd_ndisc_router_rdnss_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
48✔
1788
        if (r < 0)
48✔
1789
                return log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
×
1790

1791
        if ((lifetime_usec == 0) != zero_lifetime)
48✔
1792
                return 0;
1793

1794
        n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
24✔
1795
        if (n < 0)
24✔
1796
                return log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m");
×
1797

1798
        for (int j = 0; j < n; j++) {
63✔
1799
                _cleanup_free_ NDiscRDNSS *x = NULL;
8✔
1800
                NDiscRDNSS *rdnss, d = {
39✔
1801
                        .address = a[j],
39✔
1802
                };
1803

1804
                if (lifetime_usec == 0) {
39✔
1805
                        /* The entry is outdated. */
1806
                        free(set_remove(link->ndisc_rdnss, &d));
×
1807
                        updated = true;
×
1808
                        continue;
×
1809
                }
1810

1811
                rdnss = set_get(link->ndisc_rdnss, &d);
39✔
1812
                if (rdnss) {
39✔
1813
                        rdnss->router = router;
8✔
1814
                        rdnss->lifetime_usec = lifetime_usec;
8✔
1815
                        continue;
8✔
1816
                }
1817

1818
                if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) {
31✔
1819
                        if (!logged_about_too_many)
×
1820
                                log_link_warning(link, "Too many RDNSS records per link. Only first %u records will be used.", NDISC_RDNSS_MAX);
×
1821
                        logged_about_too_many = true;
×
1822
                        continue;
×
1823
                }
1824

1825
                x = new(NDiscRDNSS, 1);
31✔
1826
                if (!x)
31✔
1827
                        return log_oom();
×
1828

1829
                *x = (NDiscRDNSS) {
31✔
1830
                        .address = a[j],
31✔
1831
                        .router = router,
1832
                        .lifetime_usec = lifetime_usec,
1833
                };
1834

1835
                r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x));
31✔
1836
                if (r < 0)
31✔
1837
                        return log_oom();
×
1838
                assert(r > 0);
31✔
1839

1840
                updated = true;
31✔
1841
        }
1842

1843
        if (updated)
24✔
1844
                link_dirty(link);
19✔
1845

1846
        return 0;
1847
}
1848

1849
static void ndisc_dnssl_hash_func(const NDiscDNSSL *x, struct siphash *state) {
8✔
1850
        siphash24_compress_string(NDISC_DNSSL_DOMAIN(x), state);
8✔
1851
}
8✔
1852

1853
static int ndisc_dnssl_compare_func(const NDiscDNSSL *a, const NDiscDNSSL *b) {
4✔
1854
        return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b));
4✔
1855
}
1856

1857
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
1858
                ndisc_dnssl_hash_ops,
1859
                NDiscDNSSL,
1860
                ndisc_dnssl_hash_func,
1861
                ndisc_dnssl_compare_func,
1862
                free);
1863

1864
static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
48✔
1865
        char **l;
48✔
1866
        usec_t lifetime_usec;
48✔
1867
        struct in6_addr router;
48✔
1868
        bool updated = false, logged_about_too_many = false;
48✔
1869
        int r;
48✔
1870

1871
        assert(link);
48✔
1872
        assert(link->network);
48✔
1873
        assert(rt);
48✔
1874

1875
        if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_NDISC) <= 0)
48✔
1876
                return 0;
48✔
1877

1878
        r = sd_ndisc_router_get_sender_address(rt, &router);
8✔
1879
        if (r < 0)
8✔
1880
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
1881

1882
        r = sd_ndisc_router_dnssl_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
8✔
1883
        if (r < 0)
8✔
1884
                return log_link_warning_errno(link, r, "Failed to get DNSSL lifetime: %m");
×
1885

1886
        if ((lifetime_usec == 0) != zero_lifetime)
8✔
1887
                return 0;
1888

1889
        r = sd_ndisc_router_dnssl_get_domains(rt, &l);
4✔
1890
        if (r < 0)
4✔
1891
                return log_link_warning_errno(link, r, "Failed to get DNSSL addresses: %m");
×
1892

1893
        STRV_FOREACH(j, l) {
8✔
UNCOV
1894
                _cleanup_free_ NDiscDNSSL *s = NULL;
×
1895
                NDiscDNSSL *dnssl;
4✔
1896

1897
                s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1);
4✔
1898
                if (!s)
4✔
1899
                        return log_oom();
×
1900

1901
                strcpy(NDISC_DNSSL_DOMAIN(s), *j);
4✔
1902

1903
                if (lifetime_usec == 0) {
4✔
1904
                        /* The entry is outdated. */
1905
                        free(set_remove(link->ndisc_dnssl, s));
×
1906
                        updated = true;
×
1907
                        continue;
×
1908
                }
1909

1910
                dnssl = set_get(link->ndisc_dnssl, s);
4✔
1911
                if (dnssl) {
4✔
UNCOV
1912
                        dnssl->router = router;
×
UNCOV
1913
                        dnssl->lifetime_usec = lifetime_usec;
×
UNCOV
1914
                        continue;
×
1915
                }
1916

1917
                if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) {
4✔
1918
                        if (!logged_about_too_many)
×
1919
                                log_link_warning(link, "Too many DNSSL records per link. Only first %u records will be used.", NDISC_DNSSL_MAX);
×
1920
                        logged_about_too_many = true;
×
1921
                        continue;
×
1922
                }
1923

1924
                s->router = router;
4✔
1925
                s->lifetime_usec = lifetime_usec;
4✔
1926

1927
                r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s));
4✔
1928
                if (r < 0)
4✔
1929
                        return log_oom();
×
1930
                assert(r > 0);
4✔
1931

1932
                updated = true;
1933
        }
1934

1935
        if (updated)
4✔
1936
                link_dirty(link);
4✔
1937

1938
        return 0;
1939
}
1940

1941
static NDiscCaptivePortal* ndisc_captive_portal_free(NDiscCaptivePortal *x) {
2✔
1942
        if (!x)
2✔
1943
                return NULL;
1944

1945
        free(x->captive_portal);
2✔
1946
        return mfree(x);
2✔
1947
}
1948

1949
DEFINE_TRIVIAL_CLEANUP_FUNC(NDiscCaptivePortal*, ndisc_captive_portal_free);
6✔
1950

1951
static void ndisc_captive_portal_hash_func(const NDiscCaptivePortal *x, struct siphash *state) {
4✔
1952
        assert(x);
4✔
1953
        siphash24_compress_string(x->captive_portal, state);
4✔
1954
}
4✔
1955

1956
static int ndisc_captive_portal_compare_func(const NDiscCaptivePortal *a, const NDiscCaptivePortal *b) {
2✔
1957
        assert(a);
2✔
1958
        assert(b);
2✔
1959
        return strcmp_ptr(a->captive_portal, b->captive_portal);
2✔
1960
}
1961

1962
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
1963
                ndisc_captive_portal_hash_ops,
1964
                NDiscCaptivePortal,
1965
                ndisc_captive_portal_hash_func,
1966
                ndisc_captive_portal_compare_func,
1967
                ndisc_captive_portal_free);
1968

1969
static int ndisc_router_process_captive_portal(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
6✔
1970
        _cleanup_(ndisc_captive_portal_freep) NDiscCaptivePortal *new_entry = NULL;
×
1971
        _cleanup_free_ char *captive_portal = NULL;
6✔
1972
        const char *uri;
6✔
1973
        usec_t lifetime_usec;
6✔
1974
        NDiscCaptivePortal *exist;
6✔
1975
        struct in6_addr router;
6✔
1976
        int r;
6✔
1977

1978
        assert(link);
6✔
1979
        assert(link->network);
6✔
1980
        assert(rt);
6✔
1981

1982
        if (!link->network->ndisc_use_captive_portal)
6✔
1983
                return 0;
1984

1985
        r = sd_ndisc_router_get_sender_address(rt, &router);
6✔
1986
        if (r < 0)
6✔
1987
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
1988

1989
        /* RFC 4861 section 4.2. states that the lifetime in the message header should be used only for the
1990
         * default gateway, but the captive portal option does not have a lifetime field, hence, we use the
1991
         * main lifetime for the portal. */
1992
        r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
6✔
1993
        if (r < 0)
6✔
1994
                return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m");
×
1995

1996
        if ((lifetime_usec == 0) != zero_lifetime)
6✔
1997
                return 0;
1998

1999
        r = sd_ndisc_router_get_captive_portal(rt, &uri);
3✔
2000
        if (r < 0)
3✔
2001
                return log_link_warning_errno(link, r, "Failed to get captive portal from RA: %m");
×
2002

2003
        captive_portal = strdup(uri);
3✔
2004
        if (!captive_portal)
3✔
2005
                return log_oom();
×
2006

2007
        if (lifetime_usec == 0) {
3✔
2008
                /* Drop the portal with zero lifetime. */
2009
                ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals,
1✔
2010
                                                     &(const NDiscCaptivePortal) {
1✔
2011
                                                             .captive_portal = captive_portal,
2012
                                                     }));
2013
                return 0;
1✔
2014
        }
2015

2016
        exist = set_get(link->ndisc_captive_portals,
4✔
2017
                        &(const NDiscCaptivePortal) {
2✔
2018
                                .captive_portal = captive_portal,
2019
                        });
2020
        if (exist) {
2✔
2021
                /* update existing entry */
2022
                exist->router = router;
×
2023
                exist->lifetime_usec = lifetime_usec;
×
2024
                return 1;
×
2025
        }
2026

2027
        if (set_size(link->ndisc_captive_portals) >= NDISC_CAPTIVE_PORTAL_MAX) {
2✔
2028
                NDiscCaptivePortal *c, *target = NULL;
×
2029

2030
                /* Find the portal who has the minimal lifetime and drop it to store new one. */
2031
                SET_FOREACH(c, link->ndisc_captive_portals)
×
2032
                        if (!target || c->lifetime_usec < target->lifetime_usec)
×
2033
                                target = c;
×
2034

2035
                assert(target);
×
2036
                assert(set_remove(link->ndisc_captive_portals, target) == target);
×
2037
                ndisc_captive_portal_free(target);
×
2038
        }
2039

2040
        new_entry = new(NDiscCaptivePortal, 1);
2✔
2041
        if (!new_entry)
2✔
2042
                return log_oom();
×
2043

2044
        *new_entry = (NDiscCaptivePortal) {
2✔
2045
                .router = router,
2046
                .lifetime_usec = lifetime_usec,
2047
                .captive_portal = TAKE_PTR(captive_portal),
2✔
2048
        };
2049

2050
        r = set_ensure_put(&link->ndisc_captive_portals, &ndisc_captive_portal_hash_ops, new_entry);
2✔
2051
        if (r < 0)
2✔
2052
                return log_oom();
×
2053
        assert(r > 0);
2✔
2054
        TAKE_PTR(new_entry);
2✔
2055

2056
        link_dirty(link);
2✔
2057
        return 1;
2058
}
2059

2060
static void ndisc_pref64_hash_func(const NDiscPREF64 *x, struct siphash *state) {
2✔
2061
        assert(x);
2✔
2062

2063
        siphash24_compress_typesafe(x->prefix_len, state);
2✔
2064
        siphash24_compress_typesafe(x->prefix, state);
2✔
2065
}
2✔
2066

2067
static int ndisc_pref64_compare_func(const NDiscPREF64 *a, const NDiscPREF64 *b) {
1✔
2068
        int r;
1✔
2069

2070
        assert(a);
1✔
2071
        assert(b);
1✔
2072

2073
        r = CMP(a->prefix_len, b->prefix_len);
1✔
2074
        if (r != 0)
1✔
2075
                return r;
×
2076

2077
        return memcmp(&a->prefix, &b->prefix, sizeof(a->prefix));
1✔
2078
}
2079

2080
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
2081
                ndisc_pref64_hash_ops,
2082
                NDiscPREF64,
2083
                ndisc_pref64_hash_func,
2084
                ndisc_pref64_compare_func,
2085
                mfree);
2086

2087
static int ndisc_router_process_pref64(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
4✔
2088
        _cleanup_free_ NDiscPREF64 *new_entry = NULL;
4✔
2089
        usec_t lifetime_usec;
4✔
2090
        struct in6_addr a, router;
4✔
2091
        uint8_t prefix_len;
4✔
2092
        NDiscPREF64 *exist;
4✔
2093
        int r;
4✔
2094

2095
        assert(link);
4✔
2096
        assert(link->network);
4✔
2097
        assert(rt);
4✔
2098

2099
        if (!link->network->ndisc_use_pref64)
4✔
2100
                return 0;
2101

2102
        r = sd_ndisc_router_get_sender_address(rt, &router);
2✔
2103
        if (r < 0)
2✔
2104
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
2105

2106
        r = sd_ndisc_router_prefix64_get_prefix(rt, &a);
2✔
2107
        if (r < 0)
2✔
2108
                return log_link_warning_errno(link, r, "Failed to get pref64 prefix: %m");
×
2109

2110
        r = sd_ndisc_router_prefix64_get_prefixlen(rt, &prefix_len);
2✔
2111
        if (r < 0)
2✔
2112
                return log_link_warning_errno(link, r, "Failed to get pref64 prefix length: %m");
×
2113

2114
        r = sd_ndisc_router_prefix64_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
2✔
2115
        if (r < 0)
2✔
2116
                return log_link_warning_errno(link, r, "Failed to get pref64 prefix lifetime: %m");
×
2117

2118
        if ((lifetime_usec == 0) != zero_lifetime)
2✔
2119
                return 0;
2120

2121
        if (lifetime_usec == 0) {
1✔
2122
                free(set_remove(link->ndisc_pref64,
×
2123
                                &(NDiscPREF64) {
×
2124
                                        .prefix = a,
2125
                                        .prefix_len = prefix_len
2126
                                }));
2127
                return 0;
×
2128
        }
2129

2130
        exist = set_get(link->ndisc_pref64,
2✔
2131
                        &(NDiscPREF64) {
1✔
2132
                                .prefix = a,
2133
                                .prefix_len = prefix_len
2134
                });
2135
        if (exist) {
1✔
2136
                /* update existing entry */
2137
                exist->router = router;
×
2138
                exist->lifetime_usec = lifetime_usec;
×
2139
                return 0;
×
2140
        }
2141

2142
        if (set_size(link->ndisc_pref64) >= NDISC_PREF64_MAX) {
1✔
2143
                log_link_debug(link, "Too many PREF64 records received. Only first %u records will be used.", NDISC_PREF64_MAX);
×
2144
                return 0;
×
2145
        }
2146

2147
        new_entry = new(NDiscPREF64, 1);
1✔
2148
        if (!new_entry)
1✔
2149
                return log_oom();
×
2150

2151
        *new_entry = (NDiscPREF64) {
1✔
2152
                .router = router,
2153
                .lifetime_usec = lifetime_usec,
2154
                .prefix = a,
2155
                .prefix_len = prefix_len,
2156
        };
2157

2158
        r = set_ensure_put(&link->ndisc_pref64, &ndisc_pref64_hash_ops, new_entry);
1✔
2159
        if (r < 0)
1✔
2160
                return log_oom();
×
2161

2162
        assert(r > 0);
1✔
2163
        TAKE_PTR(new_entry);
2164

2165
        return 0;
2166
}
2167

2168
static NDiscDNR* ndisc_dnr_free(NDiscDNR *x) {
×
2169
        if (!x)
×
2170
                return NULL;
2171

2172
        sd_dns_resolver_done(&x->resolver);
×
2173
        return mfree(x);
×
2174
}
2175

2176
DEFINE_TRIVIAL_CLEANUP_FUNC(NDiscDNR*, ndisc_dnr_free);
×
2177

2178
static int ndisc_dnr_compare_func(const NDiscDNR *a, const NDiscDNR *b) {
×
2179
        return CMP(a->resolver.priority, b->resolver.priority) ||
×
2180
                strcmp_ptr(a->resolver.auth_name, b->resolver.auth_name) ||
×
2181
                CMP(a->resolver.transports, b->resolver.transports) ||
×
2182
                CMP(a->resolver.port, b->resolver.port) ||
×
2183
                strcmp_ptr(a->resolver.dohpath, b->resolver.dohpath) ||
×
2184
                CMP(a->resolver.family, b->resolver.family) ||
×
2185
                CMP(a->resolver.n_addrs, b->resolver.n_addrs) ||
×
2186
                memcmp(a->resolver.addrs, b->resolver.addrs, sizeof(a->resolver.addrs[0]) * a->resolver.n_addrs);
×
2187
}
2188

2189
static void ndisc_dnr_hash_func(const NDiscDNR *x, struct siphash *state) {
×
2190
        assert(x);
×
2191

2192
        siphash24_compress_resolver(&x->resolver, state);
×
2193
}
×
2194

2195
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
2196
                ndisc_dnr_hash_ops,
2197
                NDiscDNR,
2198
                ndisc_dnr_hash_func,
2199
                ndisc_dnr_compare_func,
2200
                ndisc_dnr_free);
2201

2202
static int sd_dns_resolver_copy(const sd_dns_resolver *a, sd_dns_resolver *b) {
×
2203
        int r;
×
2204

2205
        assert(a);
×
2206
        assert(b);
×
2207

2208
        _cleanup_(sd_dns_resolver_done) sd_dns_resolver c = {
×
2209
                .priority = a->priority,
×
2210
                .transports = a->transports,
×
2211
                .port = a->port,
×
2212
                /* .auth_name */
2213
                .family = a->family,
×
2214
                /* .addrs */
2215
                /* .n_addrs */
2216
                /* .dohpath */
2217
        };
2218

2219
        /* auth_name */
2220
        r = strdup_to(&c.auth_name, a->auth_name);
×
2221
        if (r < 0)
×
2222
                return r;
2223

2224
        /* addrs, n_addrs */
2225
        c.addrs = newdup(union in_addr_union, a->addrs, a->n_addrs);
×
2226
        if (!c.addrs)
×
2227
                return r;
2228
        c.n_addrs = a->n_addrs;
×
2229

2230
        /* dohpath */
2231
        r = strdup_to(&c.dohpath, a->dohpath);
×
2232
        if (r < 0)
×
2233
                return r;
2234

2235
        *b = TAKE_STRUCT(c);
×
2236
        return 0;
×
2237
}
2238

2239
static int ndisc_router_process_encrypted_dns(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
×
2240
        int r;
×
2241

2242
        assert(link);
×
2243
        assert(link->network);
×
2244
        assert(rt);
×
2245

2246
        struct in6_addr router;
×
2247
        usec_t lifetime_usec;
×
2248
        sd_dns_resolver *res;
×
2249
        _cleanup_(ndisc_dnr_freep) NDiscDNR *new_entry = NULL;
×
2250

2251
        if (!link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_NDISC))
×
2252
                return 0;
2253

2254
        r = sd_ndisc_router_get_sender_address(rt, &router);
×
2255
        if (r < 0)
×
2256
                return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
×
2257

2258
        r = sd_ndisc_router_encrypted_dns_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
×
2259
        if (r < 0)
×
2260
                return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m");
×
2261

2262
        if ((lifetime_usec == 0) != zero_lifetime)
×
2263
                return 0;
2264

2265
        r = sd_ndisc_router_encrypted_dns_get_resolver(rt, &res);
×
2266
        if (r < 0)
×
2267
                return log_link_warning_errno(link, r, "Failed to get encrypted dns resolvers: %m");
×
2268

2269
        NDiscDNR *dnr, d = { .resolver = *res };
×
2270
        if (lifetime_usec == 0) {
×
2271
                dnr = set_remove(link->ndisc_dnr, &d);
×
2272
                if (dnr) {
×
2273
                        ndisc_dnr_free(dnr);
×
2274
                        link_dirty(link);
×
2275
                }
2276
                return 0;
×
2277
        }
2278

2279
        dnr = set_get(link->ndisc_dnr, &d);
×
2280
        if (dnr) {
×
2281
                dnr->router = router;
×
2282
                dnr->lifetime_usec = lifetime_usec;
×
2283
                return 0;
×
2284
        }
2285

2286
        if (set_size(link->ndisc_dnr) >= NDISC_ENCRYPTED_DNS_MAX) {
×
2287
                log_link_warning(link, "Too many Encrypted DNS records received. Only first %u records will be used.", NDISC_ENCRYPTED_DNS_MAX);
×
2288
                return 0;
×
2289
        }
2290

2291
        new_entry = new(NDiscDNR, 1);
×
2292
        if (!new_entry)
×
2293
                return log_oom();
×
2294

2295
        *new_entry = (NDiscDNR) {
×
2296
                .router = router,
2297
                /* .resolver, */
2298
                .lifetime_usec = lifetime_usec,
2299
        };
2300
        r = sd_dns_resolver_copy(res, &new_entry->resolver);
×
2301
        if (r < 0)
×
2302
                return log_oom();
×
2303

2304
        /* Not sorted by priority */
2305
        r = set_ensure_put(&link->ndisc_dnr, &ndisc_dnr_hash_ops, new_entry);
×
2306
        if (r < 0)
×
2307
                return log_oom();
×
2308

2309
        assert(r > 0);
×
2310
        TAKE_PTR(new_entry);
×
2311

2312
        link_dirty(link);
×
2313

2314
        return 0;
2315
}
2316

2317
static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
216✔
2318
        size_t n_captive_portal = 0;
216✔
2319
        int r;
216✔
2320

2321
        assert(link);
216✔
2322
        assert(link->network);
216✔
2323
        assert(rt);
216✔
2324

2325
        for (r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
216✔
2326
                uint8_t type;
860✔
2327

2328
                if (r < 0)
860✔
2329
                        return log_link_warning_errno(link, r, "Failed to iterate through options: %m");
216✔
2330
                if (r == 0) /* EOF */
860✔
2331
                        return 0;
2332

2333
                r = sd_ndisc_router_option_get_type(rt, &type);
644✔
2334
                if (r < 0)
644✔
2335
                        return log_link_warning_errno(link, r, "Failed to get RA option type: %m");
×
2336

2337
                switch (type) {
644✔
2338
                case SD_NDISC_OPTION_PREFIX_INFORMATION:
212✔
2339
                        r = ndisc_router_process_prefix(link, rt, zero_lifetime);
212✔
2340
                        break;
2341

2342
                case SD_NDISC_OPTION_ROUTE_INFORMATION:
28✔
2343
                        r = ndisc_router_process_route(link, rt, zero_lifetime);
28✔
2344
                        break;
2345

2346
                case SD_NDISC_OPTION_RDNSS:
66✔
2347
                        r = ndisc_router_process_rdnss(link, rt, zero_lifetime);
66✔
2348
                        break;
2349

2350
                case SD_NDISC_OPTION_DNSSL:
48✔
2351
                        r = ndisc_router_process_dnssl(link, rt, zero_lifetime);
48✔
2352
                        break;
2353
                case SD_NDISC_OPTION_CAPTIVE_PORTAL:
6✔
2354
                        if (n_captive_portal > 0) {
6✔
2355
                                if (n_captive_portal == 1)
×
2356
                                        log_link_notice(link, "Received RA with multiple captive portals, only using the first one.");
×
2357

2358
                                n_captive_portal++;
×
2359
                                continue;
×
2360
                        }
2361
                        r = ndisc_router_process_captive_portal(link, rt, zero_lifetime);
6✔
2362
                        if (r > 0)
6✔
2363
                                n_captive_portal++;
2✔
2364
                        break;
2365
                case SD_NDISC_OPTION_PREF64:
4✔
2366
                        r = ndisc_router_process_pref64(link, rt, zero_lifetime);
4✔
2367
                        break;
2368
                case SD_NDISC_OPTION_ENCRYPTED_DNS:
×
2369
                        r = ndisc_router_process_encrypted_dns(link, rt, zero_lifetime);
×
2370
                        break;
2371
                }
2372
                if (r < 0 && r != -EBADMSG)
644✔
2373
                        return r;
2374
        }
2375
}
2376

2377
static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
9,423✔
2378
        bool updated = false;
9,423✔
2379
        NDiscDNSSL *dnssl;
9,423✔
2380
        NDiscRDNSS *rdnss;
9,423✔
2381
        NDiscCaptivePortal *cp;
9,423✔
2382
        NDiscPREF64 *p64;
9,423✔
2383
        NDiscDNR *dnr;
9,423✔
2384
        Address *address;
9,423✔
2385
        Route *route;
9,423✔
2386
        int r, ret = 0;
9,423✔
2387

2388
        assert(link);
9,423✔
2389
        assert(link->manager);
9,423✔
2390

2391
        /* If an address or friends is already assigned, but not valid anymore, then refuse to update it,
2392
         * and let's immediately remove it.
2393
         * See RFC4862, section 5.5.3.e. But the following logic is deviated from RFC4862 by honoring all
2394
         * valid lifetimes to improve the reaction of SLAAC to renumbering events.
2395
         * See draft-ietf-6man-slaac-renum-02, section 4.2. */
2396

2397
        r = ndisc_drop_routers(link, router, timestamp_usec);
9,423✔
2398
        if (r < 0)
9,423✔
2399
                RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to drop outdated default router, ignoring: %m"));
×
2400

2401
        SET_FOREACH(route, link->manager->routes) {
70,509✔
2402
                if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
61,086✔
2403
                        continue;
60,310✔
2404

2405
                if (!route_is_bound_to_link(route, link))
776✔
2406
                        continue;
506✔
2407

2408
                if (route->protocol == RTPROT_REDIRECT)
270✔
2409
                        continue; /* redirect route will be dropped by ndisc_drop_redirect(). */
×
2410

2411
                if (route->lifetime_usec > timestamp_usec)
270✔
2412
                        continue; /* the route is still valid */
153✔
2413

2414
                if (router && !in6_addr_equal(&route->provider.in6, router))
117✔
2415
                        continue;
×
2416

2417
                r = route_remove_and_cancel(route, link->manager);
117✔
2418
                if (r < 0)
117✔
2419
                        RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC route, ignoring: %m"));
×
2420
        }
2421

2422
        RET_GATHER(ret, ndisc_remove_unused_nexthops(link));
9,423✔
2423

2424
        SET_FOREACH(address, link->addresses) {
15,228✔
2425
                if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
5,805✔
2426
                        continue;
5,620✔
2427

2428
                if (address->lifetime_valid_usec > timestamp_usec)
185✔
2429
                        continue; /* the address is still valid */
129✔
2430

2431
                if (router && !in6_addr_equal(&address->provider.in6, router))
56✔
2432
                        continue;
×
2433

2434
                r = address_remove_and_cancel(address, link);
56✔
2435
                if (r < 0)
56✔
2436
                        RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC address, ignoring: %m"));
×
2437
        }
2438

2439
        SET_FOREACH(rdnss, link->ndisc_rdnss) {
9,466✔
2440
                if (rdnss->lifetime_usec > timestamp_usec)
43✔
2441
                        continue; /* the DNS server is still valid */
12✔
2442

2443
                if (router && !in6_addr_equal(&rdnss->router, router))
31✔
2444
                        continue;
×
2445

2446
                free(set_remove(link->ndisc_rdnss, rdnss));
31✔
2447
                updated = true;
31✔
2448
        }
2449

2450
        SET_FOREACH(dnssl, link->ndisc_dnssl) {
9,427✔
2451
                if (dnssl->lifetime_usec > timestamp_usec)
4✔
UNCOV
2452
                        continue; /* the DNS domain is still valid */
×
2453

2454
                if (router && !in6_addr_equal(&dnssl->router, router))
4✔
2455
                        continue;
×
2456

2457
                free(set_remove(link->ndisc_dnssl, dnssl));
4✔
2458
                updated = true;
4✔
2459
        }
2460

2461
        SET_FOREACH(cp, link->ndisc_captive_portals) {
9,425✔
2462
                if (cp->lifetime_usec > timestamp_usec)
2✔
2463
                        continue; /* the captive portal is still valid */
1✔
2464

2465
                if (router && !in6_addr_equal(&cp->router, router))
1✔
2466
                        continue;
×
2467

2468
                ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals, cp));
1✔
2469
                updated = true;
1✔
2470
        }
2471

2472
        SET_FOREACH(p64, link->ndisc_pref64) {
9,424✔
2473
                if (p64->lifetime_usec > timestamp_usec)
1✔
2474
                        continue; /* the pref64 prefix is still valid */
×
2475

2476
                if (router && !in6_addr_equal(&p64->router, router))
1✔
2477
                        continue;
×
2478

2479
                free(set_remove(link->ndisc_pref64, p64));
1✔
2480
                /* The pref64 prefix is not exported through the state file, hence it is not necessary to set
2481
                 * the 'updated' flag. */
2482
        }
2483

2484
        SET_FOREACH(dnr, link->ndisc_dnr) {
9,423✔
2485
                if (dnr->lifetime_usec > timestamp_usec)
×
2486
                        continue; /* The resolver is still valid */
×
2487

2488
                ndisc_dnr_free(set_remove(link->ndisc_dnr, dnr));
×
2489
                updated = true;
2490
        }
2491

2492
        RET_GATHER(ret, link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_SLAAC));
9,423✔
2493

2494
        if (updated)
9,423✔
2495
                link_dirty(link);
20✔
2496

2497
        return ret;
9,423✔
2498
}
2499

2500
static int ndisc_setup_expire(Link *link);
2501

2502
static int ndisc_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
8✔
2503
        Link *link = ASSERT_PTR(userdata);
8✔
2504
        usec_t now_usec;
8✔
2505

2506
        assert(link->manager);
8✔
2507

2508
        assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
8✔
2509

2510
        (void) ndisc_drop_outdated(link, /* router = */ NULL, now_usec);
8✔
2511
        (void) ndisc_setup_expire(link);
8✔
2512
        return 0;
8✔
2513
}
2514

2515
static int ndisc_setup_expire(Link *link) {
116✔
2516
        usec_t lifetime_usec = USEC_INFINITY;
116✔
2517
        NDiscCaptivePortal *cp;
116✔
2518
        NDiscDNSSL *dnssl;
116✔
2519
        NDiscRDNSS *rdnss;
116✔
2520
        NDiscPREF64 *p64;
116✔
2521
        NDiscDNR *dnr;
116✔
2522
        Address *address;
116✔
2523
        Route *route;
116✔
2524
        int r;
116✔
2525

2526
        assert(link);
116✔
2527
        assert(link->manager);
116✔
2528

2529
        sd_ndisc_router *rt;
116✔
2530
        HASHMAP_FOREACH(rt, link->ndisc_routers_by_sender) {
228✔
2531
                usec_t t;
112✔
2532

2533
                if (sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &t) < 0)
112✔
2534
                        continue;
×
2535

2536
                lifetime_usec = MIN(lifetime_usec, t);
112✔
2537
        }
2538

2539
        SET_FOREACH(route, link->manager->routes) {
2,456✔
2540
                if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
2,340✔
2541
                        continue;
2,161✔
2542

2543
                if (!route_is_bound_to_link(route, link))
179✔
2544
                        continue;
24✔
2545

2546
                if (!route_exists(route))
155✔
2547
                        continue;
32✔
2548

2549
                lifetime_usec = MIN(lifetime_usec, route->lifetime_usec);
123✔
2550
        }
2551

2552
        SET_FOREACH(address, link->addresses) {
401✔
2553
                if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
285✔
2554
                        continue;
153✔
2555

2556
                if (!address_exists(address))
132✔
2557
                        continue;
3✔
2558

2559
                lifetime_usec = MIN(lifetime_usec, address->lifetime_valid_usec);
129✔
2560
        }
2561

2562
        SET_FOREACH(rdnss, link->ndisc_rdnss)
159✔
2563
                lifetime_usec = MIN(lifetime_usec, rdnss->lifetime_usec);
43✔
2564

2565
        SET_FOREACH(dnssl, link->ndisc_dnssl)
120✔
2566
                lifetime_usec = MIN(lifetime_usec, dnssl->lifetime_usec);
4✔
2567

2568
        SET_FOREACH(cp, link->ndisc_captive_portals)
118✔
2569
                lifetime_usec = MIN(lifetime_usec, cp->lifetime_usec);
2✔
2570

2571
        SET_FOREACH(p64, link->ndisc_pref64)
117✔
2572
                lifetime_usec = MIN(lifetime_usec, p64->lifetime_usec);
1✔
2573

2574
        SET_FOREACH(dnr, link->ndisc_dnr)
116✔
2575
                lifetime_usec = MIN(lifetime_usec, dnr->lifetime_usec);
×
2576

2577
        if (lifetime_usec == USEC_INFINITY)
116✔
2578
                return 0;
116✔
2579

2580
        r = event_reset_time(link->manager->event, &link->ndisc_expire, CLOCK_BOOTTIME,
116✔
2581
                             lifetime_usec, 0, ndisc_expire_handler, link, 0, "ndisc-expiration", true);
2582
        if (r < 0)
116✔
2583
                return log_link_warning_errno(link, r, "Failed to update expiration timer for ndisc: %m");
×
2584

2585
        return 0;
2586
}
2587

2588
static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) {
108✔
2589
        int r;
108✔
2590

2591
        assert(link);
108✔
2592
        assert(link->network);
108✔
2593

2594
        switch (link->network->ndisc_start_dhcp6_client) {
108✔
2595
        case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO:
2596
                return 0;
2597

2598
        case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES: {
108✔
2599
                uint64_t flags;
108✔
2600

2601
                r = sd_ndisc_router_get_flags(rt, &flags);
108✔
2602
                if (r < 0)
108✔
2603
                        return log_link_warning_errno(link, r, "Failed to get RA flags: %m");
76✔
2604

2605
                if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) == 0)
108✔
2606
                        return 0;
2607

2608
                /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags.
2609
                 * Note, if both "managed" and "other configuration" bits are set, then ignore
2610
                 * "other configuration" bit. See RFC 4861. */
2611
                r = dhcp6_start_on_ra(link, !(flags & ND_RA_FLAG_MANAGED));
32✔
2612
                break;
32✔
2613
        }
2614
        case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS:
×
2615
                /* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in solicit mode
2616
                 * even if the router flags have neither M nor O flags. */
2617
                r = dhcp6_start_on_ra(link, /* information_request = */ false);
×
2618
                break;
×
2619

2620
        default:
×
2621
                assert_not_reached();
×
2622
        }
2623

2624
        if (r < 0)
32✔
2625
                return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
×
2626

2627
        log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
32✔
2628
        return 0;
2629
}
2630

2631
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
108✔
2632
        struct in6_addr router;
108✔
2633
        usec_t timestamp_usec;
108✔
2634
        int r;
108✔
2635

2636
        assert(link);
108✔
2637
        assert(link->network);
108✔
2638
        assert(link->manager);
108✔
2639
        assert(rt);
108✔
2640

2641
        r = sd_ndisc_router_get_sender_address(rt, &router);
108✔
2642
        if (r == -ENODATA) {
108✔
2643
                log_link_debug(link, "Received RA without router address, ignoring.");
×
2644
                return 0;
×
2645
        }
2646
        if (r < 0)
108✔
2647
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
2648

2649
        if (in6_prefix_is_filtered(&router, 128, link->network->ndisc_allow_listed_router, link->network->ndisc_deny_listed_router)) {
108✔
2650
                if (!set_isempty(link->network->ndisc_allow_listed_router))
×
2651
                        log_link_debug(link, "Router %s is not in allow list, ignoring.", IN6_ADDR_TO_STRING(&router));
×
2652
                else
2653
                        log_link_debug(link, "Router %s is in deny list, ignoring.", IN6_ADDR_TO_STRING(&router));
×
2654
                return 0;
×
2655
        }
2656

2657
        r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &timestamp_usec);
108✔
2658
        if (r == -ENODATA) {
108✔
2659
                log_link_debug(link, "Received RA without timestamp, ignoring.");
×
2660
                return 0;
×
2661
        }
2662
        if (r < 0)
108✔
2663
                return r;
2664

2665
        r = ndisc_drop_outdated(link, /* router = */ NULL, timestamp_usec);
108✔
2666
        if (r < 0)
108✔
2667
                return r;
2668

2669
        r = ndisc_remember_router(link, rt);
108✔
2670
        if (r < 0)
108✔
2671
                return r;
2672

2673
        r = ndisc_start_dhcp6_client(link, rt);
108✔
2674
        if (r < 0)
108✔
2675
                return r;
2676

2677
        r = ndisc_router_process_reachable_time(link, rt);
108✔
2678
        if (r < 0)
108✔
2679
                return r;
2680

2681
        r = ndisc_router_process_retransmission_time(link, rt);
108✔
2682
        if (r < 0)
108✔
2683
                return r;
2684

2685
        r = ndisc_router_process_hop_limit(link, rt);
108✔
2686
        if (r < 0)
108✔
2687
                return r;
2688

2689
        r = ndisc_router_process_mtu(link, rt);
108✔
2690
        if (r < 0)
108✔
2691
                return r;
2692

2693
        r = ndisc_router_process_options(link, rt, /* zero_lifetime = */ true);
108✔
2694
        if (r < 0)
108✔
2695
                return r;
2696

2697
        r = ndisc_router_process_default(link, rt);
108✔
2698
        if (r < 0)
108✔
2699
                return r;
2700

2701
        r = ndisc_router_process_options(link, rt, /* zero_lifetime = */ false);
108✔
2702
        if (r < 0)
108✔
2703
                return r;
2704

2705
        r = ndisc_setup_expire(link);
108✔
2706
        if (r < 0)
108✔
2707
                return r;
2708

2709
        if (sd_ndisc_router_get_lifetime(rt, NULL) <= 0)
108✔
2710
                (void) ndisc_drop_redirect(link, &router);
19✔
2711

2712
        if (link->ndisc_messages == 0)
108✔
2713
                link->ndisc_configured = true;
15✔
2714
        else
2715
                log_link_debug(link, "Setting SLAAC addresses and router.");
93✔
2716

2717
        if (!link->ndisc_configured)
108✔
2718
                link_set_state(link, LINK_STATE_CONFIGURING);
75✔
2719

2720
        link_check_ready(link);
108✔
2721
        return 0;
2722
}
2723

2724
static int ndisc_neighbor_handle_non_router_message(Link *link, sd_ndisc_neighbor *na) {
×
2725
        struct in6_addr address;
×
2726
        int r;
×
2727

2728
        assert(link);
×
2729
        assert(na);
×
2730

2731
        /* Received Neighbor Advertisement message without Router flag. The node might have been a router,
2732
         * and now it is not. Let's drop all configurations based on RAs sent from the node. */
2733

2734
        r = sd_ndisc_neighbor_get_target_address(na, &address);
×
2735
        if (r == -ENODATA)
×
2736
                return 0;
×
2737
        if (r < 0)
×
2738
                return r;
2739

2740
        (void) ndisc_drop_outdated(link, /* router = */ &address, /* timestamp_usec = */ USEC_INFINITY);
×
2741
        (void) ndisc_drop_redirect(link, &address);
×
2742

2743
        return 0;
2744
}
2745

2746
static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *na) {
7✔
2747
        struct in6_addr current_address, original_address;
7✔
2748
        int r;
7✔
2749

2750
        assert(link);
7✔
2751
        assert(link->manager);
7✔
2752
        assert(na);
7✔
2753

2754
        /* Received Neighbor Advertisement message with Router flag. If the router address is changed, update
2755
         * the provider field of configurations. */
2756

2757
        r = sd_ndisc_neighbor_get_sender_address(na, &current_address);
7✔
2758
        if (r == -ENODATA)
7✔
2759
                return 0;
7✔
2760
        if (r < 0)
7✔
2761
                return r;
2762

2763
        r = sd_ndisc_neighbor_get_target_address(na, &original_address);
7✔
2764
        if (r == -ENODATA)
7✔
2765
                return 0;
2766
        if (r < 0)
7✔
2767
                return r;
2768

2769
        if (in6_addr_equal(&current_address, &original_address))
7✔
2770
                return 0; /* the router address is not changed */
2771

2772
        r = ndisc_update_router_address(link, &original_address, &current_address);
×
2773
        if (r < 0)
×
2774
                return r;
2775

2776
        r = ndisc_update_redirect_sender(link, &original_address, &current_address);
×
2777
        if (r < 0)
×
2778
                return r;
2779

2780
        Route *route;
×
2781
        SET_FOREACH(route, link->manager->routes) {
×
2782
                if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
×
2783
                        continue;
×
2784

2785
                if (!route_is_bound_to_link(route, link))
×
2786
                        continue;
×
2787

2788
                if (!in6_addr_equal(&route->provider.in6, &original_address))
×
2789
                        continue;
×
2790

2791
                route->provider.in6 = current_address;
×
2792
        }
2793

2794
        Address *address;
×
2795
        SET_FOREACH(address, link->addresses) {
×
2796
                if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
×
2797
                        continue;
×
2798

2799
                if (!in6_addr_equal(&address->provider.in6, &original_address))
×
2800
                        continue;
×
2801

2802
                address->provider.in6 = current_address;
×
2803
        }
2804

2805
        NDiscRDNSS *rdnss;
×
2806
        SET_FOREACH(rdnss, link->ndisc_rdnss) {
×
2807
                if (!in6_addr_equal(&rdnss->router, &original_address))
×
2808
                        continue;
×
2809

2810
                rdnss->router = current_address;
×
2811
        }
2812

2813
        NDiscDNSSL *dnssl;
×
2814
        SET_FOREACH(dnssl, link->ndisc_dnssl) {
×
2815
                if (!in6_addr_equal(&dnssl->router, &original_address))
×
2816
                        continue;
×
2817

2818
                dnssl->router = current_address;
×
2819
        }
2820

2821
        NDiscCaptivePortal *cp;
×
2822
        SET_FOREACH(cp, link->ndisc_captive_portals) {
×
2823
                if (!in6_addr_equal(&cp->router, &original_address))
×
2824
                        continue;
×
2825

2826
                cp->router = current_address;
×
2827
        }
2828

2829
        NDiscPREF64 *p64;
×
2830
        SET_FOREACH(p64, link->ndisc_pref64) {
×
2831
                if (!in6_addr_equal(&p64->router, &original_address))
×
2832
                        continue;
×
2833

2834
                p64->router = current_address;
×
2835
        }
2836

2837
        NDiscDNR *dnr;
×
2838
        SET_FOREACH(dnr, link->ndisc_dnr) {
×
2839
                if (!in6_addr_equal(&dnr->router, &original_address))
×
2840
                        continue;
×
2841

2842
                dnr->router = current_address;
×
2843
        }
2844

2845
        return 0;
×
2846
}
2847

2848
static int ndisc_neighbor_handler(Link *link, sd_ndisc_neighbor *na) {
7✔
2849
        int r;
7✔
2850

2851
        assert(link);
7✔
2852
        assert(na);
7✔
2853

2854
        r = sd_ndisc_neighbor_is_router(na);
7✔
2855
        if (r < 0)
7✔
2856
                return r;
2857
        if (r == 0)
7✔
2858
                r = ndisc_neighbor_handle_non_router_message(link, na);
×
2859
        else
2860
                r = ndisc_neighbor_handle_router_message(link, na);
7✔
2861
        if (r < 0)
7✔
2862
                return r;
×
2863

2864
        return 0;
2865
}
2866

2867
static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
117✔
2868
        Link *link = ASSERT_PTR(userdata);
117✔
2869
        int r;
117✔
2870

2871
        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
117✔
2872
                return;
2873

2874
        switch (event) {
117✔
2875

2876
        case SD_NDISC_EVENT_ROUTER:
108✔
2877
                r = ndisc_router_handler(link, ASSERT_PTR(message));
108✔
2878
                if (r < 0 && r != -EBADMSG) {
108✔
2879
                        link_enter_failed(link);
×
2880
                        return;
×
2881
                }
2882
                break;
2883

2884
        case SD_NDISC_EVENT_NEIGHBOR:
7✔
2885
                r = ndisc_neighbor_handler(link, ASSERT_PTR(message));
7✔
2886
                if (r < 0 && r != -EBADMSG) {
7✔
2887
                        link_enter_failed(link);
×
2888
                        return;
×
2889
                }
2890
                break;
2891

2892
        case SD_NDISC_EVENT_REDIRECT:
×
2893
                r = ndisc_redirect_handler(link, ASSERT_PTR(message));
×
2894
                if (r < 0 && r != -EBADMSG) {
×
2895
                        log_link_warning_errno(link, r, "Failed to process Redirect message: %m");
×
2896
                        link_enter_failed(link);
×
2897
                        return;
×
2898
                }
2899
                break;
2900

2901
        case SD_NDISC_EVENT_TIMEOUT:
2✔
2902
                log_link_debug(link, "NDisc handler get timeout event");
2✔
2903
                if (link->ndisc_messages == 0) {
2✔
2904
                        link->ndisc_configured = true;
2✔
2905
                        link_check_ready(link);
2✔
2906
                }
2907
                break;
2908

2909
        default:
×
2910
                log_link_debug(link, "Received unsupported NDisc event, ignoring.");
×
2911
        }
2912
}
2913

2914
static int ndisc_configure(Link *link) {
77✔
2915
        int r;
77✔
2916

2917
        assert(link);
77✔
2918

2919
        if (!link_ndisc_enabled(link))
77✔
2920
                return 0;
2921

2922
        if (link->ndisc)
77✔
2923
                return -EBUSY; /* Already configured. */
2924

2925
        r = sd_ndisc_new(&link->ndisc);
77✔
2926
        if (r < 0)
77✔
2927
                return r;
2928

2929
        r = sd_ndisc_attach_event(link->ndisc, link->manager->event, 0);
77✔
2930
        if (r < 0)
77✔
2931
                return r;
2932

2933
        if (link->hw_addr.length == ETH_ALEN) {
77✔
2934
                r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.ether);
77✔
2935
                if (r < 0)
77✔
2936
                        return r;
2937
        }
2938

2939
        r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex);
77✔
2940
        if (r < 0)
77✔
2941
                return r;
2942

2943
        r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link);
77✔
2944
        if (r < 0)
77✔
2945
                return r;
×
2946

2947
        return 0;
2948
}
2949

2950
int ndisc_start(Link *link) {
877✔
2951
        int r;
877✔
2952

2953
        assert(link);
877✔
2954

2955
        if (!link->ndisc || !link->dhcp6_client)
877✔
2956
                return 0;
2957

2958
        if (!link_has_carrier(link))
119✔
2959
                return 0;
2960

2961
        if (in6_addr_is_null(&link->ipv6ll_address))
119✔
2962
                return 0;
2963

2964
        r = sd_ndisc_set_link_local_address(link->ndisc, &link->ipv6ll_address);
79✔
2965
        if (r < 0)
79✔
2966
                return r;
2967

2968
        log_link_debug(link, "Discovering IPv6 routers");
79✔
2969

2970
        r = sd_ndisc_start(link->ndisc);
79✔
2971
        if (r < 0)
79✔
2972
                return r;
×
2973

2974
        return 1;
2975
}
2976

2977
static int ndisc_process_request(Request *req, Link *link, void *userdata) {
1,387✔
2978
        int r;
1,387✔
2979

2980
        assert(link);
1,387✔
2981

2982
        if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
1,387✔
2983
                return 0;
2984

2985
        r = ndisc_configure(link);
77✔
2986
        if (r < 0)
77✔
2987
                return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Discovery: %m");
×
2988

2989
        r = ndisc_start(link);
77✔
2990
        if (r < 0)
77✔
2991
                return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
×
2992

2993
        log_link_debug(link, "IPv6 Router Discovery is configured%s.",
117✔
2994
                       r > 0 ? " and started" : "");
2995
        return 1;
2996
}
2997

2998
int link_request_ndisc(Link *link) {
752✔
2999
        int r;
752✔
3000

3001
        assert(link);
752✔
3002

3003
        if (!link_ndisc_enabled(link))
752✔
3004
                return 0;
3005

3006
        if (link->ndisc)
77✔
3007
                return 0;
3008

3009
        r = link_queue_request(link, REQUEST_TYPE_NDISC, ndisc_process_request, NULL);
77✔
3010
        if (r < 0)
77✔
3011
                return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Discovery: %m");
×
3012

3013
        log_link_debug(link, "Requested configuring of the IPv6 Router Discovery.");
77✔
3014
        return 0;
3015
}
3016

3017
int link_drop_ndisc_config(Link *link, Network *network) {
722✔
3018
        int r, ret = 0;
722✔
3019

3020
        assert(link);
722✔
3021
        assert(link->network);
722✔
3022

3023
        if (link->network == network)
722✔
3024
                return 0; /* .network file is unchanged. It is not necessary to reconfigure the client. */
722✔
3025

3026
        if (!link_ndisc_enabled(link)) {
722✔
3027
                /* NDisc is disabled. Stop the client if it is running and flush configs. */
3028
                ret = ndisc_stop(link);
656✔
3029
                ndisc_flush(link);
656✔
3030
                link->ndisc = sd_ndisc_unref(link->ndisc);
656✔
3031
                return ret;
656✔
3032
        }
3033

3034
        /* Even if the client was previously enabled and also enabled in the new .network file, detailed
3035
         * settings for the client may be different. Let's unref() the client. */
3036
        link->ndisc = sd_ndisc_unref(link->ndisc);
66✔
3037

3038
        /* Get if NDisc was enabled or not. */
3039
        Network *current = link->network;
66✔
3040
        link->network = network;
66✔
3041
        bool enabled = link_ndisc_enabled(link);
66✔
3042
        link->network = current;
66✔
3043

3044
        /* If previously explicitly disabled, there should be nothing to drop.
3045
         * If we do not know the previous setting of the client, e.g. when networkd is restarted, in that
3046
         * case we do not have the previous .network file assigned to the interface, then  let's assume no
3047
         * detailed configuration is changed. Hopefully, unmatching configurations will be dropped after
3048
         * their lifetime. */
3049
        if (!enabled)
66✔
3050
                return 0;
3051

3052
        assert(network);
20✔
3053

3054
        /* Redirect messages will be ignored. Drop configurations based on the previously received redirect
3055
         * messages. */
3056
        if (!network->ndisc_use_redirect)
20✔
3057
                (void) ndisc_drop_redirect(link, /* router = */ NULL);
×
3058

3059
        /* If one of the route setting is changed, drop all routes. */
3060
        if (link->network->ndisc_use_gateway != network->ndisc_use_gateway ||
20✔
3061
            link->network->ndisc_use_route_prefix != network->ndisc_use_route_prefix ||
20✔
3062
            link->network->ndisc_use_onlink_prefix != network->ndisc_use_onlink_prefix ||
20✔
3063
            link->network->ndisc_quickack != network->ndisc_quickack ||
20✔
3064
            link->network->ndisc_route_metric_high != network->ndisc_route_metric_high ||
20✔
3065
            link->network->ndisc_route_metric_medium != network->ndisc_route_metric_medium ||
19✔
3066
            link->network->ndisc_route_metric_low != network->ndisc_route_metric_low ||
19✔
3067
            !set_equal(link->network->ndisc_deny_listed_router, network->ndisc_deny_listed_router) ||
38✔
3068
            !set_equal(link->network->ndisc_allow_listed_router, network->ndisc_allow_listed_router) ||
38✔
3069
            !set_equal(link->network->ndisc_deny_listed_prefix, network->ndisc_deny_listed_prefix) ||
38✔
3070
            !set_equal(link->network->ndisc_allow_listed_prefix, network->ndisc_allow_listed_prefix) ||
37✔
3071
            !set_equal(link->network->ndisc_deny_listed_route_prefix, network->ndisc_deny_listed_route_prefix) ||
36✔
3072
            !set_equal(link->network->ndisc_allow_listed_route_prefix, network->ndisc_allow_listed_route_prefix)) {
18✔
3073
                Route *route;
2✔
3074
                SET_FOREACH(route, link->manager->routes) {
37✔
3075
                        if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
35✔
3076
                                continue;
28✔
3077

3078
                        if (!route_is_bound_to_link(route, link))
7✔
3079
                                continue;
×
3080

3081
                        if (route->protocol == RTPROT_REDIRECT)
7✔
3082
                                continue; /* redirect route is handled by ndisc_drop_redirect(). */
×
3083

3084
                        r = route_remove_and_cancel(route, link->manager);
7✔
3085
                        if (r < 0)
7✔
3086
                                RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove SLAAC route, ignoring: %m"));
×
3087
                }
3088

3089
                RET_GATHER(ret, ndisc_remove_unused_nexthops(link));
2✔
3090
        }
3091

3092
        /* If SLAAC address is disabled, drop all addresses. */
3093
        if (!network->ndisc_use_autonomous_prefix ||
40✔
3094
            !set_equal(link->network->ndisc_tokens, network->ndisc_tokens) ||
40✔
3095
            !set_equal(link->network->ndisc_deny_listed_prefix, network->ndisc_deny_listed_prefix) ||
40✔
3096
            !set_equal(link->network->ndisc_allow_listed_prefix, network->ndisc_allow_listed_prefix)) {
20✔
3097
                Address *address;
1✔
3098
                SET_FOREACH(address, link->addresses) {
4✔
3099
                        if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
3✔
3100
                                continue;
1✔
3101

3102
                        r = address_remove_and_cancel(address, link);
2✔
3103
                        if (r < 0)
2✔
3104
                                RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove SLAAC address, ignoring: %m"));
×
3105
                }
3106
        }
3107

3108
        if (!network->ndisc_use_mtu)
20✔
3109
                link->ndisc_mtu = 0;
×
3110

3111
        return ret;
3112
}
3113

3114
int ndisc_stop(Link *link) {
4,865✔
3115
        assert(link);
4,865✔
3116

3117
        link->ndisc_expire = sd_event_source_disable_unref(link->ndisc_expire);
4,865✔
3118

3119
        return sd_ndisc_stop(link->ndisc);
4,865✔
3120
}
3121

3122
void ndisc_flush(Link *link) {
9,307✔
3123
        assert(link);
9,307✔
3124

3125
        /* Remove all addresses, routes, RDNSS, DNSSL, DNR, and Captive Portal entries, without exception. */
3126
        (void) ndisc_drop_outdated(link, /* router = */ NULL, /* timestamp_usec = */ USEC_INFINITY);
9,307✔
3127
        (void) ndisc_drop_redirect(link, /* router = */ NULL);
9,307✔
3128

3129
        link->ndisc_routers_by_sender = hashmap_free(link->ndisc_routers_by_sender);
9,307✔
3130
        link->ndisc_rdnss = set_free(link->ndisc_rdnss);
9,307✔
3131
        link->ndisc_dnssl = set_free(link->ndisc_dnssl);
9,307✔
3132
        link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);
9,307✔
3133
        link->ndisc_pref64 = set_free(link->ndisc_pref64);
9,307✔
3134
        link->ndisc_redirects = set_free(link->ndisc_redirects);
9,307✔
3135
        link->ndisc_dnr = set_free(link->ndisc_dnr);
9,307✔
3136
        link->ndisc_mtu = 0;
9,307✔
3137
}
9,307✔
3138

3139
static const char* const ndisc_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
3140
        [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO]     = "no",
3141
        [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always",
3142
        [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES]    = "yes",
3143
};
3144

3145
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(ndisc_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES);
×
3146

3147
DEFINE_CONFIG_PARSE_ENUM(config_parse_ndisc_start_dhcp6_client, ndisc_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client);
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc