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

systemd / systemd / 20684862027

03 Jan 2026 10:26PM UTC coverage: 72.702% (+0.03%) from 72.677%
20684862027

push

github

web-flow
core/dynamic-user: two trivial modernizations (#40264)

2 of 4 new or added lines in 1 file covered. (50.0%)

215 existing lines in 37 files now uncovered.

310139 of 426587 relevant lines covered (72.7%)

1143601.25 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 <linux/if_arp.h>
7
#include <linux/rtnetlink.h>
8
#include <netinet/icmp6.h>
9

10
#include "sd-ndisc.h"
11

12
#include "conf-parser.h"
13
#include "errno-util.h"
14
#include "event-util.h"
15
#include "missing-network.h"
16
#include "ndisc-router-internal.h"
17
#include "networkd-address.h"
18
#include "networkd-address-generation.h"
19
#include "networkd-dhcp6.h"
20
#include "networkd-link.h"
21
#include "networkd-manager.h"
22
#include "networkd-ndisc.h"
23
#include "networkd-nexthop.h"
24
#include "networkd-queue.h"
25
#include "networkd-route.h"
26
#include "networkd-state-file.h"
27
#include "networkd-sysctl.h"
28
#include "ordered-set.h"
29
#include "set.h"
30
#include "siphash24.h"
31
#include "socket-util.h"
32
#include "string-table.h"
33
#include "string-util.h"
34
#include "strv.h"
35
#include "sysctl-util.h"
36

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

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

49
char* ndisc_dnssl_domain(const NDiscDNSSL *n) {
47✔
50
        return ((char*) n) + ALIGN(sizeof(NDiscDNSSL));
47✔
51
}
52

53
bool link_ndisc_enabled(Link *link) {
4,471✔
54
        assert(link);
4,471✔
55

56
        if (!socket_ipv6_is_supported())
4,471✔
57
                return false;
58

59
        if (link->flags & IFF_LOOPBACK)
4,471✔
60
                return false;
61

62
        if (link->iftype == ARPHRD_CAN)
4,471✔
63
                return false;
64

65
        if (!link->network)
4,465✔
66
                return false;
67

68
        if (!link_multicast_enabled(link))
4,411✔
69
                return false;
70

71
        if (!link_ipv6ll_enabled_harder(link))
2,685✔
72
                return false;
73

74
        /* Honor explicitly specified value. */
75
        if (link->network->ndisc >= 0)
2,626✔
76
                return link->network->ndisc;
2,086✔
77

78
        /* Disable if RADV is enabled. */
79
        if (link_radv_enabled(link))
540✔
80
                return false;
81

82
        /* Accept RAs if IPv6 forwarding is disabled, and ignore RAs if IPv6 forwarding is enabled. */
83
        int t = link_get_ip_forwarding(link, AF_INET6);
534✔
84
        if (t >= 0)
534✔
85
                return !t;
×
86

87
        /* Otherwise, defaults to true. */
88
        return true;
89
}
90

91
void network_adjust_ndisc(Network *network) {
6,514✔
92
        assert(network);
6,514✔
93

94
        if (!FLAGS_SET(network->link_local, ADDRESS_FAMILY_IPV6)) {
6,514✔
95
                if (network->ndisc > 0)
668✔
96
                        log_warning("%s: IPv6AcceptRA= is enabled but IPv6 link-local addressing is disabled or not supported. "
2✔
97
                                    "Disabling IPv6AcceptRA=.", network->filename);
98
                network->ndisc = false;
668✔
99
        }
100

101
        /* When RouterAllowList=, PrefixAllowList= or RouteAllowList= are specified, then
102
         * RouterDenyList=, PrefixDenyList= or RouteDenyList= are ignored, respectively. */
103
        if (!set_isempty(network->ndisc_allow_listed_router))
6,514✔
104
                network->ndisc_deny_listed_router = set_free(network->ndisc_deny_listed_router);
×
105
        if (!set_isempty(network->ndisc_allow_listed_prefix))
6,514✔
106
                network->ndisc_deny_listed_prefix = set_free(network->ndisc_deny_listed_prefix);
11✔
107
        if (!set_isempty(network->ndisc_allow_listed_route_prefix))
6,514✔
108
                network->ndisc_deny_listed_route_prefix = set_free(network->ndisc_deny_listed_route_prefix);
×
109
}
6,514✔
110

111
static int ndisc_check_ready(Link *link);
112

113
static int ndisc_address_ready_callback(Address *address) {
31✔
114
        Address *a;
31✔
115

116
        assert(address);
31✔
117
        assert(address->link);
31✔
118

119
        SET_FOREACH(a, address->link->addresses)
152✔
120
                if (a->source == NETWORK_CONFIG_SOURCE_NDISC)
90✔
121
                        a->callback = NULL;
55✔
122

123
        return ndisc_check_ready(address->link);
31✔
124
}
125

126
static int ndisc_check_ready(Link *link) {
415✔
127
        bool found = false, ready = false;
415✔
128
        Address *address;
415✔
129

130
        assert(link);
415✔
131

132
        if (link->ndisc_messages > 0) {
415✔
133
                log_link_debug(link, "%s(): SLAAC addresses and routes are not set.", __func__);
284✔
134
                return 0;
284✔
135
        }
136

137
        SET_FOREACH(address, link->addresses) {
354✔
138
                if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
276✔
139
                        continue;
145✔
140

141
                found = true;
131✔
142

143
                if (address_is_ready(address)) {
131✔
144
                        ready = true;
145
                        break;
146
                }
147
        }
148

149
        if (found && !ready) {
131✔
150
                SET_FOREACH(address, link->addresses)
177✔
151
                        if (address->source == NETWORK_CONFIG_SOURCE_NDISC)
101✔
152
                                address->callback = ndisc_address_ready_callback;
63✔
153

154
                log_link_debug(link, "%s(): no SLAAC address is ready.", __func__);
38✔
155
                return 0;
38✔
156
        }
157

158
        link->ndisc_configured = true;
93✔
159
        log_link_debug(link, "SLAAC addresses and routes set.");
93✔
160

161
        link_check_ready(link);
93✔
162
        return 0;
163
}
164

165
static int ndisc_remove_unused_nexthop(Link *link, NextHop *nexthop) {
630✔
166
        int r;
630✔
167

168
        assert(link);
630✔
169
        assert(link->manager);
630✔
170
        assert(link->ifindex > 0);
630✔
171
        assert(nexthop);
630✔
172

173
        if (nexthop->source != NETWORK_CONFIG_SOURCE_NDISC)
630✔
174
                return 0;
630✔
175

176
        if (nexthop->ifindex != link->ifindex)
431✔
177
                return 0;
178

179
        Route *route;
171✔
180
        SET_FOREACH(route, nexthop->routes)
264✔
181
                if (route_exists(route) || route_is_requesting(route))
166✔
182
                        return 0;
73✔
183

184
        Request *req;
98✔
185
        ORDERED_SET_FOREACH(req, link->manager->request_queue) {
166✔
186
                if (req->type != REQUEST_TYPE_ROUTE)
71✔
187
                        continue;
67✔
188

189
                route = ASSERT_PTR(req->userdata);
4✔
190
                if (route->nexthop_id == nexthop->id)
4✔
191
                        return 0;
3✔
192
        }
193

194
        r = nexthop_remove_and_cancel(nexthop, link->manager);
95✔
195
        if (r < 0)
95✔
196
                return log_link_debug_errno(link, r, "Failed to remove unused nexthop: %m");
×
197

198
        return 0;
199
}
200

201
static int ndisc_remove_unused_nexthop_by_id(Link *link, uint32_t id) {
23✔
202
        assert(link);
23✔
203
        assert(link->manager);
23✔
204

205
        if (id == 0)
23✔
206
                return 0;
23✔
207

208
        NextHop *nexthop;
18✔
209
        if (nexthop_get_by_id(link->manager, id, &nexthop) < 0)
18✔
210
                return 0;
211

212
        return ndisc_remove_unused_nexthop(link, nexthop);
18✔
213
}
214

215
static int ndisc_remove_unused_nexthops(Link *link) {
10,296✔
216
        int ret = 0;
10,296✔
217

218
        assert(link);
10,296✔
219
        assert(link->manager);
10,296✔
220

221
        NextHop *nexthop;
10,296✔
222
        HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id)
10,908✔
223
                RET_GATHER(ret, ndisc_remove_unused_nexthop(link, nexthop));
612✔
224

225
        return ret;
10,296✔
226
}
227

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

230
static uint32_t ndisc_generate_nexthop_id(const NextHop *nexthop, Link *link, sd_id128_t app_id, uint64_t trial) {
205✔
231
        assert(nexthop);
205✔
232
        assert(link);
205✔
233

234
        struct siphash state;
205✔
235
        siphash24_init(&state, app_id.bytes);
205✔
236
        siphash24_compress_typesafe(nexthop->protocol, &state);
205✔
237
        siphash24_compress_string(link->ifname, &state);
205✔
238
        siphash24_compress_typesafe(nexthop->gw.address.in6, &state);
205✔
239
        siphash24_compress_typesafe(nexthop->provider.in6, &state);
205✔
240
        uint64_t n = htole64(trial);
205✔
241
        siphash24_compress_typesafe(n, &state);
205✔
242

243
        uint64_t result = htole64(siphash24_finalize(&state));
205✔
244
        return (uint32_t) ((result & 0xffffffff) ^ (result >> 32));
205✔
245
}
246

247
static bool ndisc_nexthop_equal(const NextHop *a, const NextHop *b) {
87✔
248
        assert(a);
87✔
249
        assert(b);
87✔
250

251
        if (a->source != b->source)
87✔
252
                return false;
253
        if (a->protocol != b->protocol)
87✔
254
                return false;
255
        if (a->ifindex != b->ifindex)
87✔
256
                return false;
257
        if (!in6_addr_equal(&a->provider.in6, &b->provider.in6))
85✔
258
                return false;
259
        if (!in6_addr_equal(&a->gw.address.in6, &b->gw.address.in6))
69✔
260
                return false;
×
261

262
        return true;
263
}
264

265
static bool ndisc_take_nexthop_id(NextHop *nexthop, const NextHop *existing, Manager *manager) {
87✔
266
        assert(nexthop);
87✔
267
        assert(nexthop->id == 0);
87✔
268
        assert(existing);
87✔
269
        assert(existing->id > 0);
87✔
270
        assert(manager);
87✔
271

272
        if (!ndisc_nexthop_equal(nexthop, existing))
87✔
273
                return false;
274

275
        log_nexthop_debug(existing, "Found matching", manager);
69✔
276
        nexthop->id = existing->id;
69✔
277
        return true;
69✔
278
}
279

280
static int ndisc_nexthop_find_id(NextHop *nexthop, Link *link) {
139✔
281
        NextHop *n;
139✔
282
        Request *req;
139✔
283
        int r;
139✔
284

285
        assert(nexthop);
139✔
286
        assert(link);
139✔
287
        assert(link->manager);
139✔
288

289
        sd_id128_t app_id;
139✔
290
        r = sd_id128_get_machine_app_specific(NDISC_NEXTHOP_APP_ID, &app_id);
139✔
291
        if (r < 0)
139✔
292
                return r;
139✔
293

294
        uint32_t id = ndisc_generate_nexthop_id(nexthop, link, app_id, 0);
139✔
295
        if (nexthop_get_by_id(link->manager, id, &n) >= 0 &&
197✔
296
            ndisc_take_nexthop_id(nexthop, n, link->manager))
58✔
297
                return true;
298
        if (nexthop_get_request_by_id(link->manager, id, &req) >= 0 &&
92✔
299
            ndisc_take_nexthop_id(nexthop, req->userdata, link->manager))
11✔
300
                return true;
301

302
        HASHMAP_FOREACH(n, link->manager->nexthops_by_id)
76✔
303
                if (ndisc_take_nexthop_id(nexthop, n, link->manager))
6✔
304
                        return true;
×
305

306
        ORDERED_SET_FOREACH(req, link->manager->request_queue) {
129✔
307
                if (req->type != REQUEST_TYPE_NEXTHOP)
59✔
308
                        continue;
47✔
309

310
                if (ndisc_take_nexthop_id(nexthop, req->userdata, link->manager))
12✔
311
                        return true;
×
312
        }
313

314
        return false;
70✔
315
}
316

317
static int ndisc_nexthop_new(const Route *route, Link *link, NextHop **ret) {
139✔
318
        _cleanup_(nexthop_unrefp) NextHop *nexthop = NULL;
139✔
319
        int r;
139✔
320

321
        assert(route);
139✔
322
        assert(link);
139✔
323
        assert(ret);
139✔
324

325
        r = nexthop_new(&nexthop);
139✔
326
        if (r < 0)
139✔
327
                return r;
328

329
        nexthop->source = NETWORK_CONFIG_SOURCE_NDISC;
139✔
330
        nexthop->provider = route->provider;
139✔
331
        nexthop->protocol = route->protocol == RTPROT_REDIRECT ? RTPROT_REDIRECT : RTPROT_RA;
139✔
332
        nexthop->family = AF_INET6;
139✔
333
        nexthop->gw.address = route->nexthop.gw;
139✔
334
        nexthop->ifindex = link->ifindex;
139✔
335

336
        r = ndisc_nexthop_find_id(nexthop, link);
139✔
337
        if (r < 0)
139✔
338
                return r;
339

340
        *ret = TAKE_PTR(nexthop);
139✔
341
        return 0;
139✔
342
}
343

344
static int ndisc_nexthop_acquire_id(NextHop *nexthop, Link *link) {
66✔
345
        int r;
66✔
346

347
        assert(nexthop);
66✔
348
        assert(nexthop->id == 0);
66✔
349
        assert(link);
66✔
350
        assert(link->manager);
66✔
351

352
        sd_id128_t app_id;
66✔
353
        r = sd_id128_get_machine_app_specific(NDISC_NEXTHOP_APP_ID, &app_id);
66✔
354
        if (r < 0)
66✔
355
                return r;
66✔
356

357
        for (uint64_t trial = 0; trial < 100; trial++) {
66✔
358
                uint32_t id = ndisc_generate_nexthop_id(nexthop, link, app_id, trial);
66✔
359
                if (id == 0)
66✔
360
                        continue;
×
361

362
                if (set_contains(link->manager->nexthop_ids, UINT32_TO_PTR(id)))
66✔
363
                        continue; /* The ID is already used in a .network file. */
×
364

365
                if (nexthop_get_by_id(link->manager, id, NULL) >= 0)
66✔
366
                        continue; /* The ID is already used by an existing nexthop. */
×
367

368
                if (nexthop_get_request_by_id(link->manager, id, NULL) >= 0)
66✔
369
                        continue; /* The ID is already used by a nexthop being requested. */
×
370

371
                log_link_debug(link, "Generated new ndisc nexthop ID for %s with trial %"PRIu64": %"PRIu32,
66✔
372
                               IN6_ADDR_TO_STRING(&nexthop->gw.address.in6), trial, id);
373
                nexthop->id = id;
66✔
374
                return 0;
66✔
375
        }
376

377
        return log_link_debug_errno(link, SYNTHETIC_ERRNO(EBUSY), "Cannot find free nexthop ID for %s.",
×
378
                                    IN6_ADDR_TO_STRING(&nexthop->gw.address.in6));
379
}
380

381
static int ndisc_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, NextHop *nexthop) {
66✔
382
        int r;
66✔
383

384
        assert(link);
66✔
385
        assert(nexthop);
66✔
386

387
        r = nexthop_configure_handler_internal(m, link, nexthop);
66✔
388
        if (r <= 0)
66✔
389
                return r;
390

391
        r = ndisc_check_ready(link);
66✔
392
        if (r < 0)
66✔
393
                link_enter_failed(link);
×
394

395
        return 1;
396
}
397

398
static int ndisc_request_nexthop(NextHop *nexthop, Link *link) {
135✔
399
        int r;
135✔
400

401
        assert(nexthop);
135✔
402
        assert(link);
135✔
403

404
        if (nexthop->id > 0)
135✔
405
                return 0;
406

407
        r = ndisc_nexthop_acquire_id(nexthop, link);
66✔
408
        if (r < 0)
66✔
409
                return r;
410

411
        r = link_request_nexthop(link, nexthop, &link->ndisc_messages, ndisc_nexthop_handler);
66✔
412
        if (r < 0)
66✔
413
                return r;
414
        if (r > 0)
66✔
415
                link->ndisc_configured = false;
66✔
416

417
        return 0;
418
}
419

420
static int ndisc_set_route_nexthop(Route *route, Link *link, bool request) {
253✔
421
        _cleanup_(nexthop_unrefp) NextHop *nexthop = NULL;
253✔
422
        int r;
253✔
423

424
        assert(route);
253✔
425
        assert(link);
253✔
426
        assert(link->manager);
253✔
427

428
        if (!link->manager->manage_foreign_nexthops)
253✔
429
                goto finalize;
4✔
430

431
        if (route->nexthop.family != AF_INET6 || in6_addr_is_null(&route->nexthop.gw.in6))
249✔
432
                goto finalize;
110✔
433

434
        r = ndisc_nexthop_new(route, link, &nexthop);
139✔
435
        if (r < 0)
139✔
436
                return r;
437

438
        if (nexthop->id == 0 && !request)
139✔
439
                goto finalize;
4✔
440

441
        r = ndisc_request_nexthop(nexthop, link);
135✔
442
        if (r < 0)
135✔
443
                return r;
444

445
        route->nexthop = (RouteNextHop) {};
135✔
446
        route->nexthop_id = nexthop->id;
135✔
447

448
finalize:
253✔
449
        return route_adjust_nexthops(route, link);
253✔
450
}
451

452
static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
223✔
453
        int r;
223✔
454

455
        assert(req);
223✔
456
        assert(link);
223✔
457
        assert(route);
223✔
458

459
        r = route_configure_handler_internal(m, req, route);
223✔
460
        if (r <= 0)
223✔
461
                return r;
462

463
        r = ndisc_check_ready(link);
223✔
464
        if (r < 0)
223✔
465
                link_enter_failed(link);
×
466

467
        return 1;
468
}
469

470
static void ndisc_set_route_priority(Link *link, Route *route) {
1,770✔
471
        assert(link);
1,770✔
472
        assert(route);
1,770✔
473

474
        if (route->priority_set)
1,770✔
475
                return; /* explicitly configured. */
476

477
        switch (route->pref) {
1,770✔
478
        case SD_NDISC_PREFERENCE_LOW:
506✔
479
                route->priority = link->network->ndisc_route_metric_low;
506✔
480
                break;
506✔
481
        case SD_NDISC_PREFERENCE_MEDIUM:
755✔
482
                route->priority = link->network->ndisc_route_metric_medium;
755✔
483
                break;
755✔
484
        case SD_NDISC_PREFERENCE_HIGH:
509✔
485
                route->priority = link->network->ndisc_route_metric_high;
509✔
486
                break;
509✔
487
        default:
×
488
                assert_not_reached();
×
489
        }
490
}
491

492
static int ndisc_request_route(Route *route, Link *link) {
230✔
493
        int r;
230✔
494

495
        assert(route);
230✔
496
        assert(link);
230✔
497
        assert(link->manager);
230✔
498
        assert(link->network);
230✔
499

500
        r = route_metric_set(&route->metric, RTAX_QUICKACK, link->network->ndisc_quickack);
230✔
501
        if (r < 0)
230✔
502
                return r;
503

504
        r = ndisc_set_route_nexthop(route, link, /* request= */ true);
230✔
505
        if (r < 0)
230✔
506
                return r;
507

508
        uint8_t pref, pref_original = route->pref;
230✔
509
        FOREACH_ARGUMENT(pref, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH) {
917✔
510
                Route *existing;
688✔
511
                Request *req;
688✔
512

513
                /* If the preference is specified by the user config (that is, for semi-static routes),
514
                 * rather than RA, then only search conflicting routes that have the same preference. */
515
                if (route->pref_set && pref != pref_original)
688✔
516
                        continue;
×
517

518
                route->pref = pref;
688✔
519
                ndisc_set_route_priority(link, route);
688✔
520

521
                /* Note, here do not call route_remove_and_cancel() with 'route' directly, otherwise
522
                 * existing route(s) may be removed needlessly. */
523

524
                /* First, check if a conflicting route is already requested. If there is an existing route,
525
                 * and also an existing pending request, then the source may be updated by the request. So,
526
                 * we first need to check the source of the requested route. */
527
                if (route_get_request(link->manager, route, &req) >= 0) {
688✔
528
                        route->pref = pref_original;
6✔
529
                        ndisc_set_route_priority(link, route);
6✔
530

531
                        existing = ASSERT_PTR(req->userdata);
6✔
532
                        if (!route_can_update(link->manager, existing, route)) {
6✔
533
                                if (existing->source == NETWORK_CONFIG_SOURCE_STATIC) {
4✔
534
                                        log_link_debug(link, "Found a pending route request that conflicts with new request based on a received RA, ignoring request.");
×
535
                                        return 0;
1✔
536
                                }
537

538
                                log_link_debug(link, "Found a pending route request that conflicts with new request based on a received RA, cancelling.");
4✔
539
                                r = route_remove_and_cancel(existing, link->manager);
4✔
540
                                if (r < 0)
4✔
541
                                        return r;
542
                        }
543
                }
544

545
                route->pref = pref;
688✔
546
                ndisc_set_route_priority(link, route);
688✔
547

548
                /* Then, check if a conflicting route exists. */
549
                if (route_get(link->manager, route, &existing) >= 0) {
688✔
550
                        route->pref = pref_original;
90✔
551
                        ndisc_set_route_priority(link, route);
90✔
552

553
                        if (!route_can_update(link->manager, existing, route)) {
90✔
554
                                if (existing->source == NETWORK_CONFIG_SOURCE_STATIC) {
7✔
555
                                        log_link_debug(link, "Found an existing route that conflicts with new route based on a received RA, ignoring request.");
1✔
556
                                        return 0;
1✔
557
                                }
558

559
                                log_link_debug(link, "Found an existing route that conflicts with new route based on a received RA, removing.");
6✔
560
                                r = route_remove_and_cancel(existing, link->manager);
6✔
561
                                if (r < 0)
6✔
562
                                        return r;
563
                        }
564
                }
565
        }
566

567
        /* The preference (and priority) may be changed in the above loop. Restore it. */
568
        route->pref = pref_original;
229✔
569
        ndisc_set_route_priority(link, route);
229✔
570

571
        bool is_new = route_get(link->manager, route, NULL) < 0;
229✔
572

573
        r = link_request_route(link, route, &link->ndisc_messages, ndisc_route_handler);
229✔
574
        if (r < 0)
229✔
575
                return r;
576
        if (r > 0 && is_new)
229✔
577
                link->ndisc_configured = false;
146✔
578

579
        return 0;
580
}
581

582
static void ndisc_route_prepare(Route *route, Link *link) {
253✔
583
        assert(route);
253✔
584
        assert(link);
253✔
585

586
        route->source = NETWORK_CONFIG_SOURCE_NDISC;
253✔
587

588
        if (!route->table_set)
253✔
589
                route->table = link_get_ndisc_route_table(link);
253✔
590
}
253✔
591

592
static int ndisc_router_route_prepare(Route *route, Link *link, sd_ndisc_router *rt) {
253✔
593
        assert(route);
253✔
594
        assert(link);
253✔
595
        assert(rt);
253✔
596

597
        ndisc_route_prepare(route, link);
253✔
598

599
        if (!route->protocol_set)
253✔
600
                route->protocol = RTPROT_RA;
253✔
601

602
        return sd_ndisc_router_get_sender_address(rt, &route->provider.in6);
253✔
603
}
604

605
static int ndisc_request_router_route(Route *route, Link *link, sd_ndisc_router *rt) {
230✔
606
        int r;
230✔
607

608
        assert(route);
230✔
609
        assert(link);
230✔
610
        assert(rt);
230✔
611

612
        r = ndisc_router_route_prepare(route, link, rt);
230✔
613
        if (r < 0)
230✔
614
                return r;
615

616
        return ndisc_request_route(route, link);
230✔
617
}
618

619
static int ndisc_remove_route(Route *route, Link *link) {
23✔
620
        int r, ret = 0;
23✔
621

622
        assert(route);
23✔
623
        assert(link);
23✔
624
        assert(link->manager);
23✔
625

626
        r = ndisc_set_route_nexthop(route, link, /* request= */ false);
23✔
627
        if (r < 0)
23✔
628
                return r;
23✔
629

630
        uint8_t pref, pref_original = route->pref;
23✔
631
        FOREACH_ARGUMENT(pref, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH) {
92✔
632
                Route *existing;
69✔
633
                Request *req;
69✔
634

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

640
                route->pref = pref;
69✔
641
                ndisc_set_route_priority(link, route);
69✔
642

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

646
                /* First, check if the route is already requested. If there is an existing route, and also an
647
                 * existing pending request, then the source may be updated by the request. So, we first need
648
                 * to check the source of the requested route. */
649
                if (route_get_request(link->manager, route, &req) >= 0) {
69✔
650
                        existing = ASSERT_PTR(req->userdata);
×
651
                        if (existing->source == NETWORK_CONFIG_SOURCE_STATIC)
×
652
                                continue;
×
653

654
                        RET_GATHER(ret, route_remove_and_cancel(existing, link->manager));
×
655
                }
656

657
                /* Then, check if the route exists. */
658
                if (route_get(link->manager, route, &existing) >= 0) {
69✔
659
                        if (existing->source == NETWORK_CONFIG_SOURCE_STATIC)
23✔
660
                                continue;
3✔
661

662
                        RET_GATHER(ret, route_remove_and_cancel(existing, link->manager));
20✔
663
                }
664
        }
665

666
        return RET_GATHER(ret, ndisc_remove_unused_nexthop_by_id(link, route->nexthop_id));
23✔
667
}
668

669
static int ndisc_remove_router_route(Route *route, Link *link, sd_ndisc_router *rt) {
23✔
670
        int r;
23✔
671

672
        assert(route);
23✔
673
        assert(link);
23✔
674
        assert(rt);
23✔
675

676
        r = ndisc_router_route_prepare(route, link, rt);
23✔
677
        if (r < 0)
23✔
678
                return r;
679

680
        return ndisc_remove_route(route, link);
23✔
681
}
682

683
static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
95✔
684
        int r;
95✔
685

686
        assert(link);
95✔
687
        assert(address);
95✔
688

689
        r = address_configure_handler_internal(m, link, address);
95✔
690
        if (r <= 0)
95✔
691
                return r;
692

693
        r = ndisc_check_ready(link);
95✔
694
        if (r < 0)
95✔
695
                link_enter_failed(link);
×
696

697
        return 1;
698
}
699

700
static int ndisc_request_address(Address *address, Link *link) {
95✔
701
        bool is_new;
95✔
702
        int r;
95✔
703

704
        assert(address);
95✔
705
        assert(link);
95✔
706

707
        address->source = NETWORK_CONFIG_SOURCE_NDISC;
95✔
708

709
        r = free_and_strdup_warn(&address->netlabel, link->network->ndisc_netlabel);
95✔
710
        if (r < 0)
95✔
711
                return r;
95✔
712

713
        Address *existing;
95✔
714
        if (address_get_harder(link, address, &existing) < 0)
95✔
715
                is_new = true;
716
        else if (address_can_update(existing, address))
31✔
717
                is_new = false;
718
        else if (existing->source == NETWORK_CONFIG_SOURCE_DHCP6) {
×
719
                /* SLAAC address is preferred over DHCPv6 address. */
720
                log_link_debug(link, "Conflicting DHCPv6 address %s exists, removing.",
×
721
                               IN_ADDR_PREFIX_TO_STRING(existing->family, &existing->in_addr, existing->prefixlen));
722
                r = address_remove(existing, link);
×
723
                if (r < 0)
×
724
                        return r;
725

726
                is_new = true;
727
        } else {
728
                /* Conflicting static address is configured?? */
729
                log_link_debug(link, "Conflicting address %s exists, ignoring request.",
×
730
                               IN_ADDR_PREFIX_TO_STRING(existing->family, &existing->in_addr, existing->prefixlen));
731
                return 0;
×
732
        }
733

734
        r = link_request_address(link, address, &link->ndisc_messages,
95✔
735
                                 ndisc_address_handler, NULL);
736
        if (r < 0)
95✔
737
                return r;
738
        if (r > 0 && is_new)
95✔
739
                link->ndisc_configured = false;
64✔
740

741
        return 0;
742
}
743

744
int ndisc_reconfigure_address(Address *address, Link *link) {
3✔
745
        int r;
3✔
746

747
        assert(address);
3✔
748
        assert(address->source == NETWORK_CONFIG_SOURCE_NDISC);
3✔
749
        assert(link);
3✔
750

751
        r = regenerate_address(address, link);
3✔
752
        if (r <= 0)
3✔
753
                return r;
754

755
        r = ndisc_request_address(address, link);
3✔
756
        if (r < 0)
3✔
757
                return r;
758

759
        if (!link->ndisc_configured)
3✔
760
                link_set_state(link, LINK_STATE_CONFIGURING);
3✔
761

762
        link_check_ready(link);
3✔
763
        return 0;
3✔
764
}
765

766
static int ndisc_redirect_route_new(sd_ndisc_redirect *rd, Route **ret) {
×
767
        _cleanup_(route_unrefp) Route *route = NULL;
×
768
        struct in6_addr gateway, destination;
×
769
        int r;
×
770

771
        assert(rd);
×
772
        assert(ret);
×
773

774
        r = sd_ndisc_redirect_get_target_address(rd, &gateway);
×
775
        if (r < 0)
×
776
                return r;
777

778
        r = sd_ndisc_redirect_get_destination_address(rd, &destination);
×
779
        if (r < 0)
×
780
                return r;
781

782
        r = route_new(&route);
×
783
        if (r < 0)
×
784
                return r;
785

786
        route->family = AF_INET6;
×
787
        if (!in6_addr_equal(&gateway, &destination)) {
×
788
                route->nexthop.gw.in6 = gateway;
×
789
                route->nexthop.family = AF_INET6;
×
790
        }
791
        route->dst.in6 = destination;
×
792
        route->dst_prefixlen = 128;
×
793
        route->protocol = RTPROT_REDIRECT;
×
794

795
        r = sd_ndisc_redirect_get_sender_address(rd, &route->provider.in6);
×
796
        if (r < 0)
×
797
                return r;
798

799
        *ret = TAKE_PTR(route);
×
800
        return 0;
×
801
}
802

803
static int ndisc_remove_redirect_route(Link *link, sd_ndisc_redirect *rd) {
×
804
        _cleanup_(route_unrefp) Route *route = NULL;
×
805
        int r;
×
806

807
        assert(link);
×
808
        assert(rd);
×
809

810
        r = ndisc_redirect_route_new(rd, &route);
×
811
        if (r < 0)
×
812
                return r;
813

814
        ndisc_route_prepare(route, link);
×
815

816
        return ndisc_remove_route(route, link);
×
817
}
818

819
static void ndisc_redirect_hash_func(const sd_ndisc_redirect *x, struct siphash *state) {
×
820
        struct in6_addr dest = {};
×
821

822
        assert(x);
×
823
        assert(state);
×
824

825
        (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest);
×
826

827
        siphash24_compress_typesafe(dest, state);
×
828
}
×
829

830
static int ndisc_redirect_compare_func(const sd_ndisc_redirect *x, const sd_ndisc_redirect *y) {
×
831
        struct in6_addr dest_x = {}, dest_y = {};
×
832

833
        assert(x);
×
834
        assert(y);
×
835

836
        (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) x, &dest_x);
×
837
        (void) sd_ndisc_redirect_get_destination_address((sd_ndisc_redirect*) y, &dest_y);
×
838

839
        return memcmp(&dest_x, &dest_y, sizeof(dest_x));
×
840
}
841

842
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
843
                ndisc_redirect_hash_ops,
844
                sd_ndisc_redirect,
845
                ndisc_redirect_hash_func,
846
                ndisc_redirect_compare_func,
847
                sd_ndisc_redirect_unref);
848

849
static int ndisc_redirect_equal(sd_ndisc_redirect *x, sd_ndisc_redirect *y) {
×
850
        struct in6_addr a, b;
×
851
        int r;
×
852

853
        assert(x);
×
854
        assert(y);
×
855

856
        r = sd_ndisc_redirect_get_destination_address(x, &a);
×
857
        if (r < 0)
×
858
                return r;
×
859

860
        r = sd_ndisc_redirect_get_destination_address(y, &b);
×
861
        if (r < 0)
×
862
                return r;
863

864
        if (!in6_addr_equal(&a, &b))
×
865
                return false;
866

867
        r = sd_ndisc_redirect_get_target_address(x, &a);
×
868
        if (r < 0)
×
869
                return r;
870

871
        r = sd_ndisc_redirect_get_target_address(y, &b);
×
872
        if (r < 0)
×
873
                return r;
874

875
        return in6_addr_equal(&a, &b);
×
876
}
877

878
static int ndisc_redirect_drop_conflict(Link *link, sd_ndisc_redirect *rd) {
×
879
        _cleanup_(sd_ndisc_redirect_unrefp) sd_ndisc_redirect *existing = NULL;
×
880
        int r;
×
881

882
        assert(link);
×
883
        assert(rd);
×
884

885
        existing = set_remove(link->ndisc_redirects, rd);
×
886
        if (!existing)
×
887
                return 0;
888

889
        r = ndisc_redirect_equal(rd, existing);
×
890
        if (r != 0)
×
891
                return r;
892

893
        return ndisc_remove_redirect_route(link, existing);
×
894
}
895

896
static int ndisc_redirect_verify_sender(Link *link, sd_ndisc_redirect *rd) {
×
897
        int r;
×
898

899
        assert(link);
×
900
        assert(rd);
×
901

902
        /* RFC 4861 section 8.1
903
        * The IP source address of the Redirect is the same as the current first-hop router for the specified
904
        * ICMP Destination Address. */
905

906
        struct in6_addr sender;
×
907
        r = sd_ndisc_redirect_get_sender_address(rd, &sender);
×
908
        if (r < 0)
×
909
                return r;
×
910

911
        /* We will reuse the sender's router lifetime as the lifetime of the redirect route. Hence, if we
912
         * have not remembered an RA from the sender, refuse the Redirect message. */
913
        sd_ndisc_router *router = hashmap_get(link->ndisc_routers_by_sender, &sender);
×
914
        if (!router)
×
915
                return false;
916

917
        sd_ndisc_redirect *existing = set_get(link->ndisc_redirects, rd);
×
918
        if (existing) {
×
919
                struct in6_addr target, dest;
×
920

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

923
                r = sd_ndisc_redirect_get_target_address(existing, &target);
×
924
                if (r < 0)
×
925
                        return r;
×
926

927
                if (in6_addr_equal(&sender, &target))
×
928
                        return true;
929

930
                /* If the existing redirect route is on-link, that is, the destination and target address are
931
                 * equivalent, then also accept Redirect message from the current default router. This is not
932
                 * mentioned by the RFC, but without this, we cannot update on-link redirect route. */
933
                r = sd_ndisc_redirect_get_destination_address(existing, &dest);
×
934
                if (r < 0)
×
935
                        return r;
936

937
                if (!in6_addr_equal(&dest, &target))
×
938
                        return false;
939
        }
940

941
        /* Check if the sender is one of the known router with highest priority. */
942
        uint8_t preference;
×
943
        r = sd_ndisc_router_get_preference(router, &preference);
×
944
        if (r < 0)
×
945
                return r;
946

947
        if (preference == SD_NDISC_PREFERENCE_HIGH)
×
948
                return true;
949

950
        sd_ndisc_router *rt;
×
951
        HASHMAP_FOREACH(rt, link->ndisc_routers_by_sender) {
×
952
                if (rt == router)
×
953
                        continue;
×
954

955
                uint8_t pref;
×
956
                if (sd_ndisc_router_get_preference(rt, &pref) < 0)
×
957
                        continue;
×
958

959
                if (pref == SD_NDISC_PREFERENCE_HIGH ||
×
960
                    (pref == SD_NDISC_PREFERENCE_MEDIUM && preference == SD_NDISC_PREFERENCE_LOW))
×
961
                        return false;
×
962
        }
963

964
        return true;
×
965
}
966

967
static int ndisc_redirect_handler(Link *link, sd_ndisc_redirect *rd) {
×
968
        int r;
×
969

970
        assert(link);
×
971
        assert(link->network);
×
972
        assert(rd);
×
973

974
        if (!link->network->ndisc_use_redirect)
×
975
                return 0;
×
976

977
        usec_t now_usec;
×
978
        r = sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec);
×
979
        if (r < 0)
×
980
                return r;
981

982
        r = ndisc_drop_outdated(link, /* router= */ NULL, now_usec);
×
983
        if (r < 0)
×
984
                return r;
985

986
        r = ndisc_redirect_verify_sender(link, rd);
×
987
        if (r <= 0)
×
988
                return r;
989

990
        /* First, drop conflicting redirect route, if exists. */
991
        r = ndisc_redirect_drop_conflict(link, rd);
×
992
        if (r < 0)
×
993
                return r;
994

995
        /* Then, remember the received message. */
996
        r = set_ensure_put(&link->ndisc_redirects, &ndisc_redirect_hash_ops, rd);
×
997
        if (r < 0)
×
998
                return r;
999

1000
        sd_ndisc_redirect_ref(rd);
×
1001

1002
        /* Finally, request the corresponding route. */
1003
        _cleanup_(route_unrefp) Route *route = NULL;
×
1004
        r = ndisc_redirect_route_new(rd, &route);
×
1005
        if (r < 0)
×
1006
                return r;
1007

1008
        sd_ndisc_router *rt = hashmap_get(link->ndisc_routers_by_sender, &route->provider.in6);
×
1009
        if (!rt)
×
1010
                return -EADDRNOTAVAIL;
1011

1012
        r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &route->lifetime_usec);
×
1013
        if (r < 0)
×
1014
                return r;
1015

1016
        ndisc_route_prepare(route, link);
×
1017

1018
        return ndisc_request_route(route, link);
×
1019
}
1020

1021
static int ndisc_drop_redirect(Link *link, const struct in6_addr *router) {
10,233✔
1022
        int r, ret = 0;
10,233✔
1023

1024
        assert(link);
10,233✔
1025

1026
        sd_ndisc_redirect *rd;
10,233✔
1027
        SET_FOREACH(rd, link->ndisc_redirects) {
10,233✔
1028
                if (router) {
×
1029
                        struct in6_addr a;
×
1030

1031
                        if (!(sd_ndisc_redirect_get_sender_address(rd, &a) >= 0 && in6_addr_equal(&a, router)) &&
×
1032
                            !(sd_ndisc_redirect_get_target_address(rd, &a) >= 0 && in6_addr_equal(&a, router)))
×
1033
                                continue;
×
1034
                }
1035

1036
                r = ndisc_remove_redirect_route(link, rd);
×
1037
                if (r < 0)
×
1038
                        RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove redirect route, ignoring: %m"));
×
1039

1040
                sd_ndisc_redirect_unref(set_remove(link->ndisc_redirects, rd));
×
1041
        }
1042

1043
        return ret;
10,233✔
1044
}
1045

1046
static int ndisc_update_redirect_sender(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
×
1047
        int r;
×
1048

1049
        assert(link);
×
1050
        assert(original_address);
×
1051
        assert(current_address);
×
1052

1053
        sd_ndisc_redirect *rd;
×
1054
        SET_FOREACH(rd, link->ndisc_redirects) {
×
1055
                struct in6_addr sender;
×
1056

1057
                r = sd_ndisc_redirect_get_sender_address(rd, &sender);
×
1058
                if (r < 0)
×
1059
                        return r;
×
1060

1061
                if (!in6_addr_equal(&sender, original_address))
×
1062
                        continue;
×
1063

1064
                r = sd_ndisc_redirect_set_sender_address(rd, current_address);
×
1065
                if (r < 0)
×
1066
                        return r;
1067
        }
1068

1069
        return 0;
×
1070
}
1071

1072
static int ndisc_router_drop_default(Link *link, sd_ndisc_router *rt) {
19✔
1073
        _cleanup_(route_unrefp) Route *route = NULL;
19✔
1074
        struct in6_addr gateway;
19✔
1075
        int r;
19✔
1076

1077
        assert(link);
19✔
1078
        assert(link->network);
19✔
1079
        assert(rt);
19✔
1080

1081
        r = sd_ndisc_router_get_sender_address(rt, &gateway);
19✔
1082
        if (r < 0)
19✔
1083
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
1084

1085
        r = route_new(&route);
19✔
1086
        if (r < 0)
19✔
1087
                return log_oom();
×
1088

1089
        route->family = AF_INET6;
19✔
1090
        route->nexthop.family = AF_INET6;
19✔
1091
        route->nexthop.gw.in6 = gateway;
19✔
1092

1093
        r = ndisc_remove_router_route(route, link, rt);
19✔
1094
        if (r < 0)
19✔
1095
                return log_link_warning_errno(link, r, "Failed to remove the default gateway configured by RA: %m");
×
1096

1097
        Route *route_gw;
19✔
1098
        HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
21✔
1099
                _cleanup_(route_unrefp) Route *tmp = NULL;
×
1100

1101
                if (route_gw->source != NETWORK_CONFIG_SOURCE_NDISC)
2✔
1102
                        continue;
2✔
1103

1104
                assert(route_gw->nexthop.family == AF_INET6);
×
1105

1106
                r = route_dup(route_gw, NULL, &tmp);
×
1107
                if (r < 0)
×
1108
                        return r;
1109

1110
                tmp->nexthop.gw.in6 = gateway;
×
1111

1112
                r = ndisc_remove_router_route(tmp, link, rt);
×
1113
                if (r < 0)
×
1114
                        return log_link_warning_errno(link, r, "Could not remove semi-static gateway: %m");
×
1115
        }
1116

1117
        return 0;
19✔
1118
}
1119

1120
static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
122✔
1121
        usec_t lifetime_usec;
122✔
1122
        struct in6_addr gateway;
122✔
1123
        uint8_t preference;
122✔
1124
        int r;
122✔
1125

1126
        assert(link);
122✔
1127
        assert(link->network);
122✔
1128
        assert(rt);
122✔
1129

1130
        /* If the router lifetime is zero, the router should not be used as the default gateway. */
1131
        r = sd_ndisc_router_get_lifetime(rt, NULL);
122✔
1132
        if (r < 0)
122✔
1133
                return r;
122✔
1134
        if (r == 0)
122✔
1135
                return ndisc_router_drop_default(link, rt);
19✔
1136

1137
        if (!link->network->ndisc_use_gateway &&
103✔
1138
            hashmap_isempty(link->network->routes_by_section))
×
1139
                return 0;
1140

1141
        r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
103✔
1142
        if (r < 0)
103✔
1143
                return log_link_warning_errno(link, r, "Failed to get gateway lifetime from RA: %m");
×
1144

1145
        r = sd_ndisc_router_get_sender_address(rt, &gateway);
103✔
1146
        if (r < 0)
103✔
1147
                return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
×
1148

1149
        r = sd_ndisc_router_get_preference(rt, &preference);
103✔
1150
        if (r < 0)
103✔
1151
                return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
×
1152

1153
        if (link->network->ndisc_use_gateway) {
103✔
1154
                _cleanup_(route_unrefp) Route *route = NULL;
103✔
1155

1156
                r = route_new(&route);
103✔
1157
                if (r < 0)
103✔
1158
                        return log_oom();
×
1159

1160
                route->family = AF_INET6;
103✔
1161
                route->pref = preference;
103✔
1162
                route->nexthop.family = AF_INET6;
103✔
1163
                route->nexthop.gw.in6 = gateway;
103✔
1164
                route->lifetime_usec = lifetime_usec;
103✔
1165

1166
                r = ndisc_request_router_route(route, link, rt);
103✔
1167
                if (r < 0)
103✔
1168
                        return log_link_warning_errno(link, r, "Could not request default route: %m");
×
1169
        }
1170

1171
        Route *route_gw;
103✔
1172
        HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
113✔
1173
                _cleanup_(route_unrefp) Route *route = NULL;
8✔
1174

1175
                if (route_gw->source != NETWORK_CONFIG_SOURCE_NDISC)
10✔
1176
                        continue;
2✔
1177

1178
                assert(route_gw->nexthop.family == AF_INET6);
8✔
1179

1180
                r = route_dup(route_gw, NULL, &route);
8✔
1181
                if (r < 0)
8✔
1182
                        return r;
1183

1184
                route->nexthop.gw.in6 = gateway;
8✔
1185
                if (!route->pref_set)
8✔
1186
                        route->pref = preference;
8✔
1187
                route->lifetime_usec = lifetime_usec;
8✔
1188

1189
                r = ndisc_request_router_route(route, link, rt);
8✔
1190
                if (r < 0)
8✔
1191
                        return log_link_warning_errno(link, r, "Could not request gateway: %m");
×
1192
        }
1193

1194
        return 0;
103✔
1195
}
1196

1197
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
×
1198
                ndisc_router_hash_ops,
1199
                struct in6_addr,
1200
                in6_addr_hash_func,
1201
                in6_addr_compare_func,
1202
                sd_ndisc_router,
1203
                sd_ndisc_router_unref);
1204

1205
static int ndisc_update_router_address(Link *link, const struct in6_addr *original_address, const struct in6_addr *current_address) {
×
1206
        _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
×
1207
        int r;
×
1208

1209
        assert(link);
×
1210
        assert(original_address);
×
1211
        assert(current_address);
×
1212

1213
        rt = hashmap_remove(link->ndisc_routers_by_sender, original_address);
×
1214
        if (!rt)
×
1215
                return 0;
1216

1217
        /* If we already received an RA from the new address, then forget the RA from the old address. */
1218
        if (hashmap_contains(link->ndisc_routers_by_sender, current_address))
×
1219
                return 0;
1220

1221
        /* Otherwise, update the sender address of the previously received RA. */
1222
        r = sd_ndisc_router_set_sender_address(rt, current_address);
×
1223
        if (r < 0)
×
1224
                return r;
1225

1226
        r = hashmap_put(link->ndisc_routers_by_sender, &rt->packet->sender_address, rt);
×
1227
        if (r < 0)
×
1228
                return r;
1229

1230
        TAKE_PTR(rt);
1231
        return 0;
1232
}
1233

1234
static int ndisc_drop_router_one(Link *link, sd_ndisc_router *rt, usec_t timestamp_usec) {
126✔
1235
        usec_t lifetime_usec;
126✔
1236
        int r;
126✔
1237

1238
        assert(link);
126✔
1239
        assert(rt);
126✔
1240
        assert(rt->packet);
126✔
1241

1242
        r = sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
126✔
1243
        if (r < 0)
126✔
1244
                return r;
126✔
1245

1246
        if (lifetime_usec > timestamp_usec)
126✔
1247
                return 0;
1248

1249
        r = ndisc_drop_redirect(link, &rt->packet->sender_address);
49✔
1250

1251
        sd_ndisc_router_unref(hashmap_remove(link->ndisc_routers_by_sender, &rt->packet->sender_address));
49✔
1252

1253
        return r;
1254
}
1255

1256
static int ndisc_drop_routers(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
10,294✔
1257
        sd_ndisc_router *rt;
10,294✔
1258
        int ret = 0;
10,294✔
1259

1260
        assert(link);
10,294✔
1261

1262
        if (router) {
10,294✔
1263
                rt = hashmap_get(link->ndisc_routers_by_sender, router);
×
1264
                if (!rt)
×
1265
                        return 0;
10,294✔
1266

1267
                return ndisc_drop_router_one(link, rt, timestamp_usec);
×
1268
        }
1269

1270
        HASHMAP_FOREACH_KEY(rt, router, link->ndisc_routers_by_sender)
10,420✔
1271
                RET_GATHER(ret, ndisc_drop_router_one(link, rt, timestamp_usec));
126✔
1272

1273
        return ret;
10,294✔
1274
}
1275

1276
static int ndisc_remember_router(Link *link, sd_ndisc_router *rt) {
122✔
1277
        int r;
122✔
1278

1279
        assert(link);
122✔
1280
        assert(rt);
122✔
1281
        assert(rt->packet);
122✔
1282

1283
        sd_ndisc_router_unref(hashmap_remove(link->ndisc_routers_by_sender, &rt->packet->sender_address));
122✔
1284

1285
        /* Remember RAs with non-zero lifetime. */
1286
        r = sd_ndisc_router_get_lifetime(rt, NULL);
122✔
1287
        if (r <= 0)
122✔
1288
                return r;
1289

1290
        r = hashmap_ensure_put(&link->ndisc_routers_by_sender, &ndisc_router_hash_ops, &rt->packet->sender_address, rt);
103✔
1291
        if (r < 0)
103✔
1292
                return r;
1293

1294
        sd_ndisc_router_ref(rt);
103✔
1295
        return 0;
103✔
1296
}
1297

1298
static int ndisc_router_process_reachable_time(Link *link, sd_ndisc_router *rt) {
122✔
1299
        usec_t reachable_time, msec;
122✔
1300
        int r;
122✔
1301

1302
        assert(link);
122✔
1303
        assert(link->manager);
122✔
1304
        assert(link->network);
122✔
1305
        assert(rt);
122✔
1306

1307
        if (!link->network->ndisc_use_reachable_time)
122✔
1308
                return 0;
122✔
1309

1310
        r = sd_ndisc_router_get_reachable_time(rt, &reachable_time);
122✔
1311
        if (r < 0)
122✔
1312
                return log_link_warning_errno(link, r, "Failed to get reachable time from RA: %m");
×
1313

1314
        /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4) */
1315
        if (!timestamp_is_set(reachable_time))
122✔
1316
                return 0;
1317

1318
        msec = DIV_ROUND_UP(reachable_time, USEC_PER_MSEC);
20✔
1319
        if (msec <= 0 || msec > UINT32_MAX) {
20✔
1320
                log_link_debug(link, "Failed to get reachable time from RA - out of range (%"PRIu64"), ignoring", msec);
×
1321
                return 0;
×
1322
        }
1323

1324
        /* Set the reachable time for Neighbor Solicitations. */
1325
        r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "base_reachable_time_ms", (uint32_t) msec, manager_get_sysctl_shadow(link->manager));
20✔
1326
        if (r < 0)
20✔
1327
                log_link_warning_errno(link, r, "Failed to apply neighbor reachable time (%"PRIu64"), ignoring: %m", msec);
×
1328

1329
        return 0;
1330
}
1331

1332
static int ndisc_router_process_retransmission_time(Link *link, sd_ndisc_router *rt) {
122✔
1333
        usec_t retrans_time, msec;
122✔
1334
        int r;
122✔
1335

1336
        assert(link);
122✔
1337
        assert(link->manager);
122✔
1338
        assert(link->network);
122✔
1339
        assert(rt);
122✔
1340

1341
        if (!link->network->ndisc_use_retransmission_time)
122✔
1342
                return 0;
122✔
1343

1344
        r = sd_ndisc_router_get_retransmission_time(rt, &retrans_time);
122✔
1345
        if (r < 0)
122✔
1346
                return log_link_warning_errno(link, r, "Failed to get retransmission time from RA: %m");
×
1347

1348
        /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4) */
1349
        if (!timestamp_is_set(retrans_time))
122✔
1350
                return 0;
1351

1352
        msec = DIV_ROUND_UP(retrans_time, USEC_PER_MSEC);
20✔
1353
        if (msec <= 0 || msec > UINT32_MAX) {
20✔
1354
                log_link_debug(link, "Failed to get retransmission time from RA - out of range (%"PRIu64"), ignoring", msec);
×
1355
                return 0;
×
1356
        }
1357

1358
        /* Set the retransmission time for Neighbor Solicitations. */
1359
        r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", (uint32_t) msec, manager_get_sysctl_shadow(link->manager));
20✔
1360
        if (r < 0)
20✔
1361
                log_link_warning_errno(link, r, "Failed to apply neighbor retransmission time (%"PRIu64"), ignoring: %m", msec);
×
1362

1363
        return 0;
1364
}
1365

1366
static int ndisc_router_process_hop_limit(Link *link, sd_ndisc_router *rt) {
122✔
1367
        uint8_t hop_limit;
122✔
1368
        int r;
122✔
1369

1370
        assert(link);
122✔
1371
        assert(link->manager);
122✔
1372
        assert(link->network);
122✔
1373
        assert(rt);
122✔
1374

1375
        if (!link->network->ndisc_use_hop_limit)
122✔
1376
                return 0;
122✔
1377

1378
        r = sd_ndisc_router_get_hop_limit(rt, &hop_limit);
122✔
1379
        if (r < 0)
122✔
1380
                return log_link_warning_errno(link, r, "Failed to get hop limit from RA: %m");
×
1381

1382
        /* 0 is the unspecified value and must not be set (see RFC4861, 6.3.4):
1383
         *
1384
         * A Router Advertisement field (e.g., Cur Hop Limit, Reachable Time, and Retrans Timer) may contain
1385
         * a value denoting that it is unspecified. In such cases, the parameter should be ignored and the
1386
         * host should continue using whatever value it is already using. In particular, a host MUST NOT
1387
         * interpret the unspecified value as meaning change back to the default value that was in use before
1388
         * the first Router Advertisement was received.
1389
         *
1390
         * If the received Cur Hop Limit value is non-zero, the host SHOULD set
1391
         * its CurHopLimit variable to the received value. */
1392
        if (hop_limit <= 0)
122✔
1393
                return 0;
1394

1395
        r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "hop_limit", (uint32_t) hop_limit, manager_get_sysctl_shadow(link->manager));
53✔
1396
        if (r < 0)
53✔
1397
                log_link_warning_errno(link, r, "Failed to apply hop_limit (%u), ignoring: %m", hop_limit);
×
1398

1399
        return 0;
1400
}
1401

1402
static int ndisc_router_process_mtu(Link *link, sd_ndisc_router *rt) {
122✔
1403
        uint32_t mtu;
122✔
1404
        int r;
122✔
1405

1406
        assert(link);
122✔
1407
        assert(link->network);
122✔
1408
        assert(rt);
122✔
1409

1410
        if (!link->network->ndisc_use_mtu)
122✔
1411
                return 0;
122✔
1412

1413
        r = sd_ndisc_router_get_mtu(rt, &mtu);
122✔
1414
        if (r == -ENODATA)
122✔
1415
                return 0;
1416
        if (r < 0)
39✔
1417
                return log_link_warning_errno(link, r, "Failed to get MTU from RA: %m");
×
1418

1419
        link->ndisc_mtu = mtu;
39✔
1420

1421
        (void) link_set_ipv6_mtu(link, LOG_DEBUG);
39✔
1422

1423
        return 0;
1424
}
1425

1426
static int ndisc_address_set_lifetime(Address *address, Link *link, sd_ndisc_router *rt) {
92✔
1427
        Address *existing;
92✔
1428
        usec_t t;
92✔
1429
        int r;
92✔
1430

1431
        assert(address);
92✔
1432
        assert(link);
92✔
1433
        assert(rt);
92✔
1434

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

1440
        r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_valid_usec);
92✔
1441
        if (r < 0)
92✔
1442
                return r;
92✔
1443

1444
        r = sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_BOOTTIME, &address->lifetime_preferred_usec);
92✔
1445
        if (r < 0)
92✔
1446
                return r;
1447

1448
        /* RFC 4862 section 5.5.3 (e)
1449
         * 1. If the received Valid Lifetime is greater than 2 hours or greater than RemainingLifetime,
1450
         *    set the valid lifetime of the corresponding address to the advertised Valid Lifetime. */
1451
        r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &t);
92✔
1452
        if (r < 0)
92✔
1453
                return r;
1454

1455
        if (t > 2 * USEC_PER_HOUR)
92✔
1456
                return 0;
1457

1458
        if (address_get(link, address, &existing) < 0 || existing->source != NETWORK_CONFIG_SOURCE_NDISC)
80✔
1459
                return 0;
1460

1461
        if (address->lifetime_valid_usec > existing->lifetime_valid_usec)
25✔
1462
                return 0;
1463

1464
        /* 2. If RemainingLifetime is less than or equal to 2 hours, ignore the Prefix Information option
1465
         *    with regards to the valid lifetime, unless the Router Advertisement from which this option was
1466
         *    obtained has been authenticated (e.g., via Secure Neighbor Discovery [RFC3971]). If the Router
1467
         *    Advertisement was authenticated, the valid lifetime of the corresponding address should be set
1468
         *    to the Valid Lifetime in the received option.
1469
         *
1470
         * Currently, authentication is not supported. So check the lifetime of the existing address. */
1471
        r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &t);
7✔
1472
        if (r < 0)
7✔
1473
                return r;
1474

1475
        if (existing->lifetime_valid_usec <= usec_add(t, 2 * USEC_PER_HOUR)) {
7✔
1476
                address->lifetime_valid_usec = existing->lifetime_valid_usec;
7✔
1477
                return 0;
7✔
1478
        }
1479

1480
        /* 3. Otherwise, reset the valid lifetime of the corresponding address to 2 hours. */
1481
        address->lifetime_valid_usec = usec_add(t, 2 * USEC_PER_HOUR);
×
1482
        return 0;
×
1483
}
1484

1485
static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
74✔
1486
        usec_t lifetime_valid_usec, lifetime_preferred_usec;
74✔
1487
        struct in6_addr prefix, router;
74✔
1488
        uint8_t prefixlen;
74✔
1489
        int r;
74✔
1490

1491
        assert(link);
74✔
1492
        assert(link->network);
74✔
1493
        assert(rt);
74✔
1494

1495
        if (!link->network->ndisc_use_autonomous_prefix)
74✔
1496
                return 0;
74✔
1497

1498
        r = sd_ndisc_router_get_sender_address(rt, &router);
74✔
1499
        if (r < 0)
74✔
1500
                return log_link_warning_errno(link, r, "Failed to get router address: %m");
×
1501

1502
        r = sd_ndisc_router_prefix_get_address(rt, &prefix);
74✔
1503
        if (r < 0)
74✔
1504
                return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
×
1505

1506
        r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
74✔
1507
        if (r < 0)
74✔
1508
                return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
×
1509

1510
        /* ndisc_generate_addresses() below requires the prefix length <= 64. */
1511
        if (prefixlen > 64) {
74✔
1512
                log_link_debug(link, "Prefix is longer than 64, ignoring autonomous prefix %s.",
×
1513
                               IN6_ADDR_PREFIX_TO_STRING(&prefix, prefixlen));
1514
                return 0;
×
1515
        }
1516

1517
        r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid_usec);
74✔
1518
        if (r < 0)
74✔
1519
                return log_link_warning_errno(link, r, "Failed to get prefix valid lifetime: %m");
×
1520

1521
        r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred_usec);
74✔
1522
        if (r < 0)
74✔
1523
                return log_link_warning_errno(link, r, "Failed to get prefix preferred lifetime: %m");
×
1524

1525
        /* RFC 4862 section 5.5.3 (c)
1526
         * If the preferred lifetime is greater than the valid lifetime, silently ignore the Prefix
1527
         * Information option. */
1528
        if (lifetime_preferred_usec > lifetime_valid_usec)
74✔
1529
                return 0;
1530

1531
        _cleanup_hashmap_free_ Hashmap *tokens_by_address = NULL;
74✔
1532
        r = ndisc_generate_addresses(link, &prefix, prefixlen, &tokens_by_address);
74✔
1533
        if (r < 0)
74✔
1534
                return log_link_warning_errno(link, r, "Failed to generate SLAAC addresses: %m");
×
1535

1536
        IPv6Token *token;
74✔
1537
        struct in6_addr *a;
74✔
1538
        HASHMAP_FOREACH_KEY(token, a, tokens_by_address) {
166✔
1539
                _cleanup_(address_unrefp) Address *address = NULL;
92✔
1540

1541
                r = address_new(&address);
92✔
1542
                if (r < 0)
92✔
1543
                        return log_oom();
×
1544

1545
                address->provider.in6 = router;
92✔
1546
                address->family = AF_INET6;
92✔
1547
                address->in_addr.in6 = *a;
92✔
1548
                address->prefixlen = prefixlen;
92✔
1549
                address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
92✔
1550
                address->token = ipv6_token_ref(token);
92✔
1551

1552
                r = ndisc_address_set_lifetime(address, link, rt);
92✔
1553
                if (r < 0)
92✔
1554
                        return log_link_warning_errno(link, r, "Failed to set lifetime of SLAAC address: %m");
×
1555

1556
                assert(address->lifetime_preferred_usec <= address->lifetime_valid_usec);
92✔
1557

1558
                r = ndisc_request_address(address, link);
92✔
1559
                if (r < 0)
92✔
1560
                        return log_link_warning_errno(link, r, "Could not request SLAAC address: %m");
×
1561
        }
1562

1563
        return 0;
74✔
1564
}
1565

1566
static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
112✔
1567
        _cleanup_(route_unrefp) Route *route = NULL;
112✔
1568
        uint8_t prefixlen, preference;
112✔
1569
        usec_t lifetime_usec;
112✔
1570
        struct in6_addr prefix;
112✔
1571
        int r;
112✔
1572

1573
        assert(link);
112✔
1574
        assert(link->network);
112✔
1575
        assert(rt);
112✔
1576

1577
        if (!link->network->ndisc_use_onlink_prefix)
112✔
1578
                return 0;
1579

1580
        r = sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
112✔
1581
        if (r < 0)
112✔
1582
                return log_link_warning_errno(link, r, "Failed to get prefix lifetime: %m");
×
1583

1584
        r = sd_ndisc_router_prefix_get_address(rt, &prefix);
112✔
1585
        if (r < 0)
112✔
1586
                return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
×
1587

1588
        r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
112✔
1589
        if (r < 0)
112✔
1590
                return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
×
1591

1592
        /* Prefix Information option does not have preference, hence we use the 'main' preference here */
1593
        r = sd_ndisc_router_get_preference(rt, &preference);
112✔
1594
        if (r < 0)
112✔
1595
                return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
×
1596

1597
        r = route_new(&route);
112✔
1598
        if (r < 0)
112✔
1599
                return log_oom();
×
1600

1601
        route->family = AF_INET6;
112✔
1602
        route->dst.in6 = prefix;
112✔
1603
        route->dst_prefixlen = prefixlen;
112✔
1604
        route->pref = preference;
112✔
1605
        route->lifetime_usec = lifetime_usec;
112✔
1606

1607
        /* RFC 4861 section 6.3.4:
1608
         * - If the prefix is not already present in the Prefix List, and the Prefix Information option's
1609
         *   Valid Lifetime field is non-zero, create a new entry for the prefix and initialize its
1610
         *   invalidation timer to the Valid Lifetime value in the Prefix Information option.
1611
         *
1612
         * - If the prefix is already present in the host's Prefix List as the result of a previously
1613
         *   received advertisement, reset its invalidation timer to the Valid Lifetime value in the Prefix
1614
         *   Information option. If the new Lifetime value is zero, timeout the prefix immediately. */
1615
        if (lifetime_usec == 0) {
112✔
1616
                r = ndisc_remove_router_route(route, link, rt);
×
1617
                if (r < 0)
×
1618
                        return log_link_warning_errno(link, r, "Failed to remove prefix route: %m");
×
1619
        } else {
1620
                r = ndisc_request_router_route(route, link, rt);
112✔
1621
                if (r < 0)
112✔
1622
                        return log_link_warning_errno(link, r, "Failed to request prefix route: %m");
×
1623
        }
1624

1625
        return 0;
1626
}
1627

1628
static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
254✔
1629
        uint8_t flags, prefixlen;
254✔
1630
        struct in6_addr a;
254✔
1631
        int r;
254✔
1632

1633
        assert(link);
254✔
1634
        assert(link->network);
254✔
1635
        assert(rt);
254✔
1636

1637
        usec_t lifetime_usec;
254✔
1638
        r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_usec);
254✔
1639
        if (r < 0)
254✔
1640
                return log_link_warning_errno(link, r, "Failed to get prefix lifetime: %m");
×
1641

1642
        if ((lifetime_usec == 0) != zero_lifetime)
254✔
1643
                return 0;
1644

1645
        r = sd_ndisc_router_prefix_get_address(rt, &a);
127✔
1646
        if (r < 0)
127✔
1647
                return log_link_warning_errno(link, r, "Failed to get prefix address: %m");
×
1648

1649
        /* RFC 4861 Section 4.6.2:
1650
         * A router SHOULD NOT send a prefix option for the link-local prefix and a host SHOULD ignore such
1651
         * a prefix option. */
1652
        if (in6_addr_is_link_local(&a)) {
127✔
1653
                log_link_debug(link, "Received link-local prefix, ignoring prefix.");
×
1654
                return 0;
×
1655
        }
1656

1657
        r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
127✔
1658
        if (r < 0)
127✔
1659
                return log_link_warning_errno(link, r, "Failed to get prefix length: %m");
×
1660

1661
        if (in6_prefix_is_filtered(&a, prefixlen, link->network->ndisc_allow_listed_prefix, link->network->ndisc_deny_listed_prefix)) {
127✔
1662
                if (set_isempty(link->network->ndisc_allow_listed_prefix))
15✔
1663
                        log_link_debug(link, "Prefix '%s' is in deny list, ignoring.",
1✔
1664
                                       IN6_ADDR_PREFIX_TO_STRING(&a, prefixlen));
1665
                else
1666
                        log_link_debug(link, "Prefix '%s' is not in allow list, ignoring.",
14✔
1667
                                       IN6_ADDR_PREFIX_TO_STRING(&a, prefixlen));
1668
                return 0;
15✔
1669
        }
1670

1671
        r = sd_ndisc_router_prefix_get_flags(rt, &flags);
112✔
1672
        if (r < 0)
112✔
1673
                return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
×
1674

1675
        if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
112✔
1676
                r = ndisc_router_process_onlink_prefix(link, rt);
112✔
1677
                if (r < 0)
112✔
1678
                        return r;
1679
        }
1680

1681
        if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
112✔
1682
                r = ndisc_router_process_autonomous_prefix(link, rt);
74✔
1683
                if (r < 0)
74✔
1684
                        return r;
×
1685
        }
1686

1687
        return 0;
1688
}
1689

1690
static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
28✔
1691
        _cleanup_(route_unrefp) Route *route = NULL;
28✔
1692
        uint8_t preference, prefixlen;
28✔
1693
        struct in6_addr gateway, dst;
28✔
1694
        usec_t lifetime_usec;
28✔
1695
        int r;
28✔
1696

1697
        assert(link);
28✔
1698

1699
        if (!link->network->ndisc_use_route_prefix)
28✔
1700
                return 0;
1701

1702
        r = sd_ndisc_router_route_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
28✔
1703
        if (r < 0)
28✔
1704
                return log_link_warning_errno(link, r, "Failed to get route lifetime from RA: %m");
×
1705

1706
        if ((lifetime_usec == 0) != zero_lifetime)
28✔
1707
                return 0;
1708

1709
        r = sd_ndisc_router_route_get_address(rt, &dst);
14✔
1710
        if (r < 0)
14✔
1711
                return log_link_warning_errno(link, r, "Failed to get route destination address: %m");
×
1712

1713
        r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
14✔
1714
        if (r < 0)
14✔
1715
                return log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
×
1716

1717
        if (in6_prefix_is_filtered(&dst, prefixlen,
14✔
1718
                                   link->network->ndisc_allow_listed_route_prefix,
1719
                                   link->network->ndisc_deny_listed_route_prefix)) {
14✔
1720
                if (set_isempty(link->network->ndisc_allow_listed_route_prefix))
3✔
1721
                        log_link_debug(link, "Route prefix '%s' is in deny list, ignoring.",
3✔
1722
                                       IN6_ADDR_PREFIX_TO_STRING(&dst, prefixlen));
1723
                else
1724
                        log_link_debug(link, "Route prefix '%s' is not in allow list, ignoring.",
×
1725
                                       IN6_ADDR_PREFIX_TO_STRING(&dst, prefixlen));
1726
                return 0;
3✔
1727
        }
1728

1729
        r = sd_ndisc_router_get_sender_address(rt, &gateway);
11✔
1730
        if (r < 0)
11✔
1731
                return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
×
1732

1733
        if (link_get_ipv6_address(link, &gateway, NULL) >= 0) {
11✔
1734
                if (DEBUG_LOGGING)
×
1735
                        log_link_debug(link, "Advertised route gateway %s is local to the link, ignoring route",
×
1736
                                       IN6_ADDR_TO_STRING(&gateway));
1737
                return 0;
×
1738
        }
1739

1740
        r = sd_ndisc_router_route_get_preference(rt, &preference);
11✔
1741
        if (r < 0)
11✔
1742
                return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
×
1743

1744
        r = route_new(&route);
11✔
1745
        if (r < 0)
11✔
1746
                return log_oom();
×
1747

1748
        route->family = AF_INET6;
11✔
1749
        route->pref = preference;
11✔
1750
        route->nexthop.gw.in6 = gateway;
11✔
1751
        route->nexthop.family = AF_INET6;
11✔
1752
        route->dst.in6 = dst;
11✔
1753
        route->dst_prefixlen = prefixlen;
11✔
1754
        route->lifetime_usec = lifetime_usec;
11✔
1755

1756
        if (lifetime_usec != 0) {
11✔
1757
                r = ndisc_request_router_route(route, link, rt);
7✔
1758
                if (r < 0)
7✔
1759
                        return log_link_warning_errno(link, r, "Could not request additional route: %m");
×
1760
        } else {
1761
                r = ndisc_remove_router_route(route, link, rt);
4✔
1762
                if (r < 0)
4✔
1763
                        return log_link_warning_errno(link, r, "Could not remove additional route with zero lifetime: %m");
×
1764
        }
1765

1766
        return 0;
1767
}
1768

1769
static void ndisc_rdnss_hash_func(const NDiscRDNSS *x, struct siphash *state) {
103✔
1770
        siphash24_compress_typesafe(x->address, state);
103✔
1771
}
103✔
1772

1773
static int ndisc_rdnss_compare_func(const NDiscRDNSS *a, const NDiscRDNSS *b) {
57✔
1774
        return memcmp(&a->address, &b->address, sizeof(a->address));
57✔
1775
}
1776

1777
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
1778
                ndisc_rdnss_hash_ops,
1779
                NDiscRDNSS,
1780
                ndisc_rdnss_hash_func,
1781
                ndisc_rdnss_compare_func,
1782
                free);
1783

1784
static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
80✔
1785
        usec_t lifetime_usec;
80✔
1786
        const struct in6_addr *a;
80✔
1787
        struct in6_addr router;
80✔
1788
        bool updated = false, logged_about_too_many = false;
80✔
1789
        int n, r;
80✔
1790

1791
        assert(link);
80✔
1792
        assert(link->network);
80✔
1793
        assert(rt);
80✔
1794

1795
        if (!link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC))
80✔
1796
                return 0;
80✔
1797

1798
        r = sd_ndisc_router_get_sender_address(rt, &router);
62✔
1799
        if (r < 0)
62✔
1800
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
1801

1802
        r = sd_ndisc_router_rdnss_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
62✔
1803
        if (r < 0)
62✔
1804
                return log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
×
1805

1806
        if ((lifetime_usec == 0) != zero_lifetime)
62✔
1807
                return 0;
1808

1809
        n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
31✔
1810
        if (n < 0)
31✔
1811
                return log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m");
×
1812

1813
        for (int j = 0; j < n; j++) {
82✔
1814
                _cleanup_free_ NDiscRDNSS *x = NULL;
14✔
1815
                NDiscRDNSS *rdnss, d = {
51✔
1816
                        .address = a[j],
51✔
1817
                };
1818

1819
                if (lifetime_usec == 0) {
51✔
1820
                        /* The entry is outdated. */
1821
                        free(set_remove(link->ndisc_rdnss, &d));
×
1822
                        updated = true;
×
1823
                        continue;
×
1824
                }
1825

1826
                rdnss = set_get(link->ndisc_rdnss, &d);
51✔
1827
                if (rdnss) {
51✔
1828
                        rdnss->router = router;
14✔
1829
                        rdnss->lifetime_usec = lifetime_usec;
14✔
1830
                        continue;
14✔
1831
                }
1832

1833
                if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) {
37✔
1834
                        if (!logged_about_too_many)
×
1835
                                log_link_warning(link, "Too many RDNSS records per link. Only first %u records will be used.", NDISC_RDNSS_MAX);
×
1836
                        logged_about_too_many = true;
×
1837
                        continue;
×
1838
                }
1839

1840
                x = new(NDiscRDNSS, 1);
37✔
1841
                if (!x)
37✔
1842
                        return log_oom();
×
1843

1844
                *x = (NDiscRDNSS) {
37✔
1845
                        .address = a[j],
37✔
1846
                        .router = router,
1847
                        .lifetime_usec = lifetime_usec,
1848
                };
1849

1850
                r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x));
37✔
1851
                if (r < 0)
37✔
1852
                        return log_oom();
×
1853
                assert(r > 0);
37✔
1854

1855
                updated = true;
37✔
1856
        }
1857

1858
        if (updated)
31✔
1859
                link_dirty(link);
22✔
1860

1861
        return 0;
1862
}
1863

1864
static void ndisc_dnssl_hash_func(const NDiscDNSSL *x, struct siphash *state) {
8✔
1865
        siphash24_compress_string(ndisc_dnssl_domain(x), state);
8✔
1866
}
8✔
1867

1868
static int ndisc_dnssl_compare_func(const NDiscDNSSL *a, const NDiscDNSSL *b) {
4✔
1869
        return strcmp(ndisc_dnssl_domain(a), ndisc_dnssl_domain(b));
4✔
1870
}
1871

1872
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
1873
                ndisc_dnssl_hash_ops,
1874
                NDiscDNSSL,
1875
                ndisc_dnssl_hash_func,
1876
                ndisc_dnssl_compare_func,
1877
                free);
1878

1879
static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
62✔
1880
        char **l;
62✔
1881
        usec_t lifetime_usec;
62✔
1882
        struct in6_addr router;
62✔
1883
        bool updated = false, logged_about_too_many = false;
62✔
1884
        int r;
62✔
1885

1886
        assert(link);
62✔
1887
        assert(link->network);
62✔
1888
        assert(rt);
62✔
1889

1890
        if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_NDISC) <= 0)
62✔
1891
                return 0;
62✔
1892

1893
        r = sd_ndisc_router_get_sender_address(rt, &router);
8✔
1894
        if (r < 0)
8✔
1895
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
1896

1897
        r = sd_ndisc_router_dnssl_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
8✔
1898
        if (r < 0)
8✔
1899
                return log_link_warning_errno(link, r, "Failed to get DNSSL lifetime: %m");
×
1900

1901
        if ((lifetime_usec == 0) != zero_lifetime)
8✔
1902
                return 0;
1903

1904
        r = sd_ndisc_router_dnssl_get_domains(rt, &l);
4✔
1905
        if (r < 0)
4✔
1906
                return log_link_warning_errno(link, r, "Failed to get DNSSL addresses: %m");
×
1907

1908
        STRV_FOREACH(j, l) {
8✔
UNCOV
1909
                _cleanup_free_ NDiscDNSSL *s = NULL;
×
1910
                NDiscDNSSL *dnssl;
4✔
1911

1912
                s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1);
4✔
1913
                if (!s)
4✔
1914
                        return log_oom();
×
1915

1916
                strcpy(ndisc_dnssl_domain(s), *j);
4✔
1917

1918
                if (lifetime_usec == 0) {
4✔
1919
                        /* The entry is outdated. */
1920
                        free(set_remove(link->ndisc_dnssl, s));
×
1921
                        updated = true;
×
1922
                        continue;
×
1923
                }
1924

1925
                dnssl = set_get(link->ndisc_dnssl, s);
4✔
1926
                if (dnssl) {
4✔
UNCOV
1927
                        dnssl->router = router;
×
UNCOV
1928
                        dnssl->lifetime_usec = lifetime_usec;
×
UNCOV
1929
                        continue;
×
1930
                }
1931

1932
                if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) {
4✔
1933
                        if (!logged_about_too_many)
×
1934
                                log_link_warning(link, "Too many DNSSL records per link. Only first %u records will be used.", NDISC_DNSSL_MAX);
×
1935
                        logged_about_too_many = true;
×
1936
                        continue;
×
1937
                }
1938

1939
                s->router = router;
4✔
1940
                s->lifetime_usec = lifetime_usec;
4✔
1941

1942
                r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s));
4✔
1943
                if (r < 0)
4✔
1944
                        return log_oom();
×
1945
                assert(r > 0);
4✔
1946

1947
                updated = true;
1948
        }
1949

1950
        if (updated)
4✔
1951
                link_dirty(link);
4✔
1952

1953
        return 0;
1954
}
1955

1956
static NDiscCaptivePortal* ndisc_captive_portal_free(NDiscCaptivePortal *x) {
2✔
1957
        if (!x)
2✔
1958
                return NULL;
1959

1960
        free(x->captive_portal);
2✔
1961
        return mfree(x);
2✔
1962
}
1963

1964
DEFINE_TRIVIAL_CLEANUP_FUNC(NDiscCaptivePortal*, ndisc_captive_portal_free);
6✔
1965

1966
static void ndisc_captive_portal_hash_func(const NDiscCaptivePortal *x, struct siphash *state) {
4✔
1967
        assert(x);
4✔
1968
        siphash24_compress_string(x->captive_portal, state);
4✔
1969
}
4✔
1970

1971
static int ndisc_captive_portal_compare_func(const NDiscCaptivePortal *a, const NDiscCaptivePortal *b) {
2✔
1972
        assert(a);
2✔
1973
        assert(b);
2✔
1974
        return strcmp_ptr(a->captive_portal, b->captive_portal);
2✔
1975
}
1976

1977
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
1978
                ndisc_captive_portal_hash_ops,
1979
                NDiscCaptivePortal,
1980
                ndisc_captive_portal_hash_func,
1981
                ndisc_captive_portal_compare_func,
1982
                ndisc_captive_portal_free);
1983

1984
static int ndisc_router_process_captive_portal(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
6✔
1985
        _cleanup_(ndisc_captive_portal_freep) NDiscCaptivePortal *new_entry = NULL;
×
1986
        _cleanup_free_ char *captive_portal = NULL;
6✔
1987
        const char *uri;
6✔
1988
        usec_t lifetime_usec;
6✔
1989
        NDiscCaptivePortal *exist;
6✔
1990
        struct in6_addr router;
6✔
1991
        int r;
6✔
1992

1993
        assert(link);
6✔
1994
        assert(link->network);
6✔
1995
        assert(rt);
6✔
1996

1997
        if (!link->network->ndisc_use_captive_portal)
6✔
1998
                return 0;
1999

2000
        r = sd_ndisc_router_get_sender_address(rt, &router);
6✔
2001
        if (r < 0)
6✔
2002
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
2003

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

2011
        if ((lifetime_usec == 0) != zero_lifetime)
6✔
2012
                return 0;
2013

2014
        r = sd_ndisc_router_get_captive_portal(rt, &uri);
3✔
2015
        if (r < 0)
3✔
2016
                return log_link_warning_errno(link, r, "Failed to get captive portal from RA: %m");
×
2017

2018
        captive_portal = strdup(uri);
3✔
2019
        if (!captive_portal)
3✔
2020
                return log_oom();
×
2021

2022
        if (lifetime_usec == 0) {
3✔
2023
                /* Drop the portal with zero lifetime. */
2024
                ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals,
1✔
2025
                                                     &(const NDiscCaptivePortal) {
1✔
2026
                                                             .captive_portal = captive_portal,
2027
                                                     }));
2028
                return 0;
1✔
2029
        }
2030

2031
        exist = set_get(link->ndisc_captive_portals,
4✔
2032
                        &(const NDiscCaptivePortal) {
2✔
2033
                                .captive_portal = captive_portal,
2034
                        });
2035
        if (exist) {
2✔
2036
                /* update existing entry */
2037
                exist->router = router;
×
2038
                exist->lifetime_usec = lifetime_usec;
×
2039
                return 1;
×
2040
        }
2041

2042
        if (set_size(link->ndisc_captive_portals) >= NDISC_CAPTIVE_PORTAL_MAX) {
2✔
2043
                NDiscCaptivePortal *c, *target = NULL;
×
2044

2045
                /* Find the portal who has the minimal lifetime and drop it to store new one. */
2046
                SET_FOREACH(c, link->ndisc_captive_portals)
×
2047
                        if (!target || c->lifetime_usec < target->lifetime_usec)
×
2048
                                target = c;
×
2049

2050
                assert(target);
×
2051
                assert(set_remove(link->ndisc_captive_portals, target) == target);
×
2052
                ndisc_captive_portal_free(target);
×
2053
        }
2054

2055
        new_entry = new(NDiscCaptivePortal, 1);
2✔
2056
        if (!new_entry)
2✔
2057
                return log_oom();
×
2058

2059
        *new_entry = (NDiscCaptivePortal) {
2✔
2060
                .router = router,
2061
                .lifetime_usec = lifetime_usec,
2062
                .captive_portal = TAKE_PTR(captive_portal),
2✔
2063
        };
2064

2065
        r = set_ensure_put(&link->ndisc_captive_portals, &ndisc_captive_portal_hash_ops, new_entry);
2✔
2066
        if (r < 0)
2✔
2067
                return log_oom();
×
2068
        assert(r > 0);
2✔
2069
        TAKE_PTR(new_entry);
2✔
2070

2071
        link_dirty(link);
2✔
2072
        return 1;
2073
}
2074

2075
static void ndisc_pref64_hash_func(const NDiscPREF64 *x, struct siphash *state) {
2✔
2076
        assert(x);
2✔
2077

2078
        siphash24_compress_typesafe(x->prefix_len, state);
2✔
2079
        siphash24_compress_typesafe(x->prefix, state);
2✔
2080
}
2✔
2081

2082
static int ndisc_pref64_compare_func(const NDiscPREF64 *a, const NDiscPREF64 *b) {
1✔
2083
        int r;
1✔
2084

2085
        assert(a);
1✔
2086
        assert(b);
1✔
2087

2088
        r = CMP(a->prefix_len, b->prefix_len);
1✔
2089
        if (r != 0)
1✔
2090
                return r;
×
2091

2092
        return memcmp(&a->prefix, &b->prefix, sizeof(a->prefix));
1✔
2093
}
2094

2095
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
2096
                ndisc_pref64_hash_ops,
2097
                NDiscPREF64,
2098
                ndisc_pref64_hash_func,
2099
                ndisc_pref64_compare_func,
2100
                mfree);
2101

2102
static int ndisc_router_process_pref64(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
4✔
2103
        _cleanup_free_ NDiscPREF64 *new_entry = NULL;
4✔
2104
        usec_t lifetime_usec;
4✔
2105
        struct in6_addr a, router;
4✔
2106
        uint8_t prefix_len;
4✔
2107
        NDiscPREF64 *exist;
4✔
2108
        int r;
4✔
2109

2110
        assert(link);
4✔
2111
        assert(link->network);
4✔
2112
        assert(rt);
4✔
2113

2114
        if (!link->network->ndisc_use_pref64)
4✔
2115
                return 0;
2116

2117
        r = sd_ndisc_router_get_sender_address(rt, &router);
2✔
2118
        if (r < 0)
2✔
2119
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
2120

2121
        r = sd_ndisc_router_prefix64_get_prefix(rt, &a);
2✔
2122
        if (r < 0)
2✔
2123
                return log_link_warning_errno(link, r, "Failed to get pref64 prefix: %m");
×
2124

2125
        r = sd_ndisc_router_prefix64_get_prefixlen(rt, &prefix_len);
2✔
2126
        if (r < 0)
2✔
2127
                return log_link_warning_errno(link, r, "Failed to get pref64 prefix length: %m");
×
2128

2129
        r = sd_ndisc_router_prefix64_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
2✔
2130
        if (r < 0)
2✔
2131
                return log_link_warning_errno(link, r, "Failed to get pref64 prefix lifetime: %m");
×
2132

2133
        if ((lifetime_usec == 0) != zero_lifetime)
2✔
2134
                return 0;
2135

2136
        if (lifetime_usec == 0) {
1✔
2137
                free(set_remove(link->ndisc_pref64,
×
2138
                                &(NDiscPREF64) {
×
2139
                                        .prefix = a,
2140
                                        .prefix_len = prefix_len
2141
                                }));
2142
                return 0;
×
2143
        }
2144

2145
        exist = set_get(link->ndisc_pref64,
2✔
2146
                        &(NDiscPREF64) {
1✔
2147
                                .prefix = a,
2148
                                .prefix_len = prefix_len
2149
                });
2150
        if (exist) {
1✔
2151
                /* update existing entry */
2152
                exist->router = router;
×
2153
                exist->lifetime_usec = lifetime_usec;
×
2154
                return 0;
×
2155
        }
2156

2157
        if (set_size(link->ndisc_pref64) >= NDISC_PREF64_MAX) {
1✔
2158
                log_link_debug(link, "Too many PREF64 records received. Only first %u records will be used.", NDISC_PREF64_MAX);
×
2159
                return 0;
×
2160
        }
2161

2162
        new_entry = new(NDiscPREF64, 1);
1✔
2163
        if (!new_entry)
1✔
2164
                return log_oom();
×
2165

2166
        *new_entry = (NDiscPREF64) {
1✔
2167
                .router = router,
2168
                .lifetime_usec = lifetime_usec,
2169
                .prefix = a,
2170
                .prefix_len = prefix_len,
2171
        };
2172

2173
        r = set_ensure_put(&link->ndisc_pref64, &ndisc_pref64_hash_ops, new_entry);
1✔
2174
        if (r < 0)
1✔
2175
                return log_oom();
×
2176

2177
        assert(r > 0);
1✔
2178
        TAKE_PTR(new_entry);
2179

2180
        return 0;
2181
}
2182

2183
static NDiscDNR* ndisc_dnr_free(NDiscDNR *x) {
×
2184
        if (!x)
×
2185
                return NULL;
2186

2187
        sd_dns_resolver_done(&x->resolver);
×
2188
        return mfree(x);
×
2189
}
2190

2191
DEFINE_TRIVIAL_CLEANUP_FUNC(NDiscDNR*, ndisc_dnr_free);
×
2192

2193
static int ndisc_dnr_compare_func(const NDiscDNR *a, const NDiscDNR *b) {
×
2194
        return CMP(a->resolver.priority, b->resolver.priority) ||
×
2195
                strcmp_ptr(a->resolver.auth_name, b->resolver.auth_name) ||
×
2196
                CMP(a->resolver.transports, b->resolver.transports) ||
×
2197
                CMP(a->resolver.port, b->resolver.port) ||
×
2198
                strcmp_ptr(a->resolver.dohpath, b->resolver.dohpath) ||
×
2199
                CMP(a->resolver.family, b->resolver.family) ||
×
2200
                CMP(a->resolver.n_addrs, b->resolver.n_addrs) ||
×
2201
                memcmp(a->resolver.addrs, b->resolver.addrs, sizeof(a->resolver.addrs[0]) * a->resolver.n_addrs) != 0;
×
2202
}
2203

2204
static void ndisc_dnr_hash_func(const NDiscDNR *x, struct siphash *state) {
×
2205
        assert(x);
×
2206

2207
        siphash24_compress_resolver(&x->resolver, state);
×
2208
}
×
2209

2210
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
2211
                ndisc_dnr_hash_ops,
2212
                NDiscDNR,
2213
                ndisc_dnr_hash_func,
2214
                ndisc_dnr_compare_func,
2215
                ndisc_dnr_free);
2216

2217
static int sd_dns_resolver_copy(const sd_dns_resolver *a, sd_dns_resolver *b) {
×
2218
        int r;
×
2219

2220
        assert(a);
×
2221
        assert(b);
×
2222

2223
        _cleanup_(sd_dns_resolver_done) sd_dns_resolver c = {
×
2224
                .priority = a->priority,
×
2225
                .transports = a->transports,
×
2226
                .port = a->port,
×
2227
                /* .auth_name */
2228
                .family = a->family,
×
2229
                /* .addrs */
2230
                /* .n_addrs */
2231
                /* .dohpath */
2232
        };
2233

2234
        /* auth_name */
2235
        r = strdup_to(&c.auth_name, a->auth_name);
×
2236
        if (r < 0)
×
2237
                return r;
2238

2239
        /* addrs, n_addrs */
2240
        c.addrs = newdup(union in_addr_union, a->addrs, a->n_addrs);
×
2241
        if (!c.addrs)
×
2242
                return r;
2243
        c.n_addrs = a->n_addrs;
×
2244

2245
        /* dohpath */
2246
        r = strdup_to(&c.dohpath, a->dohpath);
×
2247
        if (r < 0)
×
2248
                return r;
2249

2250
        *b = TAKE_STRUCT(c);
×
2251
        return 0;
×
2252
}
2253

2254
static int ndisc_router_process_encrypted_dns(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
×
2255
        int r;
×
2256

2257
        assert(link);
×
2258
        assert(link->network);
×
2259
        assert(rt);
×
2260

2261
        struct in6_addr router;
×
2262
        usec_t lifetime_usec;
×
2263
        sd_dns_resolver *res;
×
2264
        _cleanup_(ndisc_dnr_freep) NDiscDNR *new_entry = NULL;
×
2265

2266
        if (!link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_NDISC))
×
2267
                return 0;
2268

2269
        r = sd_ndisc_router_get_sender_address(rt, &router);
×
2270
        if (r < 0)
×
2271
                return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
×
2272

2273
        r = sd_ndisc_router_encrypted_dns_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
×
2274
        if (r < 0)
×
2275
                return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m");
×
2276

2277
        if ((lifetime_usec == 0) != zero_lifetime)
×
2278
                return 0;
2279

2280
        r = sd_ndisc_router_encrypted_dns_get_resolver(rt, &res);
×
2281
        if (r < 0)
×
2282
                return log_link_warning_errno(link, r, "Failed to get encrypted dns resolvers: %m");
×
2283

2284
        NDiscDNR *dnr, d = { .resolver = *res };
×
2285
        if (lifetime_usec == 0) {
×
2286
                dnr = set_remove(link->ndisc_dnr, &d);
×
2287
                if (dnr) {
×
2288
                        ndisc_dnr_free(dnr);
×
2289
                        link_dirty(link);
×
2290
                }
2291
                return 0;
×
2292
        }
2293

2294
        dnr = set_get(link->ndisc_dnr, &d);
×
2295
        if (dnr) {
×
2296
                dnr->router = router;
×
2297
                dnr->lifetime_usec = lifetime_usec;
×
2298
                return 0;
×
2299
        }
2300

2301
        if (set_size(link->ndisc_dnr) >= NDISC_ENCRYPTED_DNS_MAX) {
×
2302
                log_link_warning(link, "Too many Encrypted DNS records received. Only first %u records will be used.", NDISC_ENCRYPTED_DNS_MAX);
×
2303
                return 0;
×
2304
        }
2305

2306
        new_entry = new(NDiscDNR, 1);
×
2307
        if (!new_entry)
×
2308
                return log_oom();
×
2309

2310
        *new_entry = (NDiscDNR) {
×
2311
                .router = router,
2312
                /* .resolver, */
2313
                .lifetime_usec = lifetime_usec,
2314
        };
2315
        r = sd_dns_resolver_copy(res, &new_entry->resolver);
×
2316
        if (r < 0)
×
2317
                return log_oom();
×
2318

2319
        /* Not sorted by priority */
2320
        r = set_ensure_put(&link->ndisc_dnr, &ndisc_dnr_hash_ops, new_entry);
×
2321
        if (r < 0)
×
2322
                return log_oom();
×
2323

2324
        assert(r > 0);
×
2325
        TAKE_PTR(new_entry);
×
2326

2327
        link_dirty(link);
×
2328

2329
        return 0;
2330
}
2331

2332
static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt, bool zero_lifetime) {
244✔
2333
        size_t n_captive_portal = 0;
244✔
2334
        int r;
244✔
2335

2336
        assert(link);
244✔
2337
        assert(link->network);
244✔
2338
        assert(rt);
244✔
2339

2340
        for (r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
244✔
2341
                uint8_t type;
1,000✔
2342

2343
                if (r < 0)
1,000✔
2344
                        return log_link_warning_errno(link, r, "Failed to iterate through options: %m");
244✔
2345
                if (r == 0) /* EOF */
1,000✔
2346
                        return 0;
2347

2348
                r = sd_ndisc_router_option_get_type(rt, &type);
756✔
2349
                if (r < 0)
756✔
2350
                        return log_link_warning_errno(link, r, "Failed to get RA option type: %m");
×
2351

2352
                switch (type) {
756✔
2353
                case SD_NDISC_OPTION_PREFIX_INFORMATION:
254✔
2354
                        r = ndisc_router_process_prefix(link, rt, zero_lifetime);
254✔
2355
                        break;
2356

2357
                case SD_NDISC_OPTION_ROUTE_INFORMATION:
28✔
2358
                        r = ndisc_router_process_route(link, rt, zero_lifetime);
28✔
2359
                        break;
2360

2361
                case SD_NDISC_OPTION_RDNSS:
80✔
2362
                        r = ndisc_router_process_rdnss(link, rt, zero_lifetime);
80✔
2363
                        break;
2364

2365
                case SD_NDISC_OPTION_DNSSL:
62✔
2366
                        r = ndisc_router_process_dnssl(link, rt, zero_lifetime);
62✔
2367
                        break;
2368
                case SD_NDISC_OPTION_CAPTIVE_PORTAL:
6✔
2369
                        if (n_captive_portal > 0) {
6✔
2370
                                if (n_captive_portal == 1)
×
2371
                                        log_link_notice(link, "Received RA with multiple captive portals, only using the first one.");
×
2372

2373
                                n_captive_portal++;
×
2374
                                continue;
×
2375
                        }
2376
                        r = ndisc_router_process_captive_portal(link, rt, zero_lifetime);
6✔
2377
                        if (r > 0)
6✔
2378
                                n_captive_portal++;
2✔
2379
                        break;
2380
                case SD_NDISC_OPTION_PREF64:
4✔
2381
                        r = ndisc_router_process_pref64(link, rt, zero_lifetime);
4✔
2382
                        break;
2383
                case SD_NDISC_OPTION_ENCRYPTED_DNS:
×
2384
                        r = ndisc_router_process_encrypted_dns(link, rt, zero_lifetime);
×
2385
                        break;
2386
                }
2387
                if (r < 0 && r != -EBADMSG)
756✔
2388
                        return r;
2389
        }
2390
}
2391

2392
static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
10,294✔
2393
        bool updated = false;
10,294✔
2394
        NDiscDNSSL *dnssl;
10,294✔
2395
        NDiscRDNSS *rdnss;
10,294✔
2396
        NDiscCaptivePortal *cp;
10,294✔
2397
        NDiscPREF64 *p64;
10,294✔
2398
        NDiscDNR *dnr;
10,294✔
2399
        Address *address;
10,294✔
2400
        Route *route;
10,294✔
2401
        int r, ret = 0;
10,294✔
2402

2403
        assert(link);
10,294✔
2404
        assert(link->manager);
10,294✔
2405

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

2412
        r = ndisc_drop_routers(link, router, timestamp_usec);
10,294✔
2413
        if (r < 0)
10,294✔
2414
                RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to drop outdated default router, ignoring: %m"));
×
2415

2416
        SET_FOREACH(route, link->manager->routes) {
84,503✔
2417
                if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
74,209✔
2418
                        continue;
73,472✔
2419

2420
                if (!route_is_bound_to_link(route, link))
737✔
2421
                        continue;
434✔
2422

2423
                if (route->protocol == RTPROT_REDIRECT)
303✔
2424
                        continue; /* redirect route will be dropped by ndisc_drop_redirect(). */
×
2425

2426
                if (route->lifetime_usec > timestamp_usec)
303✔
2427
                        continue; /* the route is still valid */
166✔
2428

2429
                if (router && !in6_addr_equal(&route->provider.in6, router))
137✔
2430
                        continue;
×
2431

2432
                r = route_remove_and_cancel(route, link->manager);
137✔
2433
                if (r < 0)
137✔
2434
                        RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC route, ignoring: %m"));
×
2435
        }
2436

2437
        RET_GATHER(ret, ndisc_remove_unused_nexthops(link));
10,294✔
2438

2439
        SET_FOREACH(address, link->addresses) {
16,519✔
2440
                if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
6,225✔
2441
                        continue;
6,027✔
2442

2443
                if (address->lifetime_valid_usec > timestamp_usec)
198✔
2444
                        continue; /* the address is still valid */
134✔
2445

2446
                if (router && !in6_addr_equal(&address->provider.in6, router))
64✔
2447
                        continue;
×
2448

2449
                r = address_remove_and_cancel(address, link);
64✔
2450
                if (r < 0)
64✔
2451
                        RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC address, ignoring: %m"));
×
2452
        }
2453

2454
        SET_FOREACH(rdnss, link->ndisc_rdnss) {
10,349✔
2455
                if (rdnss->lifetime_usec > timestamp_usec)
55✔
2456
                        continue; /* the DNS server is still valid */
18✔
2457

2458
                if (router && !in6_addr_equal(&rdnss->router, router))
37✔
2459
                        continue;
×
2460

2461
                free(set_remove(link->ndisc_rdnss, rdnss));
37✔
2462
                updated = true;
37✔
2463
        }
2464

2465
        SET_FOREACH(dnssl, link->ndisc_dnssl) {
10,298✔
2466
                if (dnssl->lifetime_usec > timestamp_usec)
4✔
UNCOV
2467
                        continue; /* the DNS domain is still valid */
×
2468

2469
                if (router && !in6_addr_equal(&dnssl->router, router))
4✔
2470
                        continue;
×
2471

2472
                free(set_remove(link->ndisc_dnssl, dnssl));
4✔
2473
                updated = true;
4✔
2474
        }
2475

2476
        SET_FOREACH(cp, link->ndisc_captive_portals) {
10,296✔
2477
                if (cp->lifetime_usec > timestamp_usec)
2✔
2478
                        continue; /* the captive portal is still valid */
1✔
2479

2480
                if (router && !in6_addr_equal(&cp->router, router))
1✔
2481
                        continue;
×
2482

2483
                ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals, cp));
1✔
2484
                updated = true;
1✔
2485
        }
2486

2487
        SET_FOREACH(p64, link->ndisc_pref64) {
10,295✔
2488
                if (p64->lifetime_usec > timestamp_usec)
1✔
2489
                        continue; /* the pref64 prefix is still valid */
×
2490

2491
                if (router && !in6_addr_equal(&p64->router, router))
1✔
2492
                        continue;
×
2493

2494
                free(set_remove(link->ndisc_pref64, p64));
1✔
2495
                /* The pref64 prefix is not exported through the state file, hence it is not necessary to set
2496
                 * the 'updated' flag. */
2497
        }
2498

2499
        SET_FOREACH(dnr, link->ndisc_dnr) {
10,294✔
2500
                if (dnr->lifetime_usec > timestamp_usec)
×
2501
                        continue; /* The resolver is still valid */
×
2502

2503
                ndisc_dnr_free(set_remove(link->ndisc_dnr, dnr));
×
2504
                updated = true;
2505
        }
2506

2507
        RET_GATHER(ret, link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_SLAAC));
10,294✔
2508

2509
        if (updated)
10,294✔
2510
                link_dirty(link);
23✔
2511

2512
        return ret;
10,294✔
2513
}
2514

2515
static int ndisc_setup_expire(Link *link);
2516

2517
static int ndisc_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
7✔
2518
        Link *link = ASSERT_PTR(userdata);
7✔
2519
        usec_t now_usec;
7✔
2520

2521
        assert(link->manager);
7✔
2522

2523
        assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
7✔
2524

2525
        (void) ndisc_drop_outdated(link, /* router= */ NULL, now_usec);
7✔
2526
        (void) ndisc_setup_expire(link);
7✔
2527
        return 0;
7✔
2528
}
2529

2530
static int ndisc_setup_expire(Link *link) {
129✔
2531
        usec_t lifetime_usec = USEC_INFINITY;
129✔
2532
        NDiscCaptivePortal *cp;
129✔
2533
        NDiscDNSSL *dnssl;
129✔
2534
        NDiscRDNSS *rdnss;
129✔
2535
        NDiscPREF64 *p64;
129✔
2536
        NDiscDNR *dnr;
129✔
2537
        Address *address;
129✔
2538
        Route *route;
129✔
2539
        int r;
129✔
2540

2541
        assert(link);
129✔
2542
        assert(link->manager);
129✔
2543

2544
        sd_ndisc_router *rt;
129✔
2545
        HASHMAP_FOREACH(rt, link->ndisc_routers_by_sender) {
255✔
2546
                usec_t t;
126✔
2547

2548
                if (sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &t) < 0)
126✔
2549
                        continue;
×
2550

2551
                lifetime_usec = MIN(lifetime_usec, t);
126✔
2552
        }
2553

2554
        SET_FOREACH(route, link->manager->routes) {
3,876✔
2555
                if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
3,747✔
2556
                        continue;
3,556✔
2557

2558
                if (!route_is_bound_to_link(route, link))
191✔
2559
                        continue;
23✔
2560

2561
                if (!route_exists(route))
168✔
2562
                        continue;
32✔
2563

2564
                lifetime_usec = MIN(lifetime_usec, route->lifetime_usec);
136✔
2565
        }
2566

2567
        SET_FOREACH(address, link->addresses) {
445✔
2568
                if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
316✔
2569
                        continue;
178✔
2570

2571
                if (!address_exists(address))
138✔
2572
                        continue;
4✔
2573

2574
                lifetime_usec = MIN(lifetime_usec, address->lifetime_valid_usec);
134✔
2575
        }
2576

2577
        SET_FOREACH(rdnss, link->ndisc_rdnss)
184✔
2578
                lifetime_usec = MIN(lifetime_usec, rdnss->lifetime_usec);
55✔
2579

2580
        SET_FOREACH(dnssl, link->ndisc_dnssl)
133✔
2581
                lifetime_usec = MIN(lifetime_usec, dnssl->lifetime_usec);
4✔
2582

2583
        SET_FOREACH(cp, link->ndisc_captive_portals)
131✔
2584
                lifetime_usec = MIN(lifetime_usec, cp->lifetime_usec);
2✔
2585

2586
        SET_FOREACH(p64, link->ndisc_pref64)
130✔
2587
                lifetime_usec = MIN(lifetime_usec, p64->lifetime_usec);
1✔
2588

2589
        SET_FOREACH(dnr, link->ndisc_dnr)
129✔
2590
                lifetime_usec = MIN(lifetime_usec, dnr->lifetime_usec);
×
2591

2592
        if (lifetime_usec == USEC_INFINITY)
129✔
2593
                return 0;
129✔
2594

2595
        r = event_reset_time(link->manager->event, &link->ndisc_expire, CLOCK_BOOTTIME,
129✔
2596
                             lifetime_usec, 0, ndisc_expire_handler, link, 0, "ndisc-expiration", true);
2597
        if (r < 0)
129✔
2598
                return log_link_warning_errno(link, r, "Failed to update expiration timer for ndisc: %m");
×
2599

2600
        return 0;
2601
}
2602

2603
static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) {
122✔
2604
        int r;
122✔
2605

2606
        assert(link);
122✔
2607
        assert(link->network);
122✔
2608

2609
        switch (link->network->ndisc_start_dhcp6_client) {
122✔
2610
        case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO:
2611
                return 0;
2612

2613
        case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES: {
122✔
2614
                uint64_t flags;
122✔
2615

2616
                r = sd_ndisc_router_get_flags(rt, &flags);
122✔
2617
                if (r < 0)
122✔
2618
                        return log_link_warning_errno(link, r, "Failed to get RA flags: %m");
83✔
2619

2620
                if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) == 0)
122✔
2621
                        return 0;
2622

2623
                /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags.
2624
                 * Note, if both "managed" and "other configuration" bits are set, then ignore
2625
                 * "other configuration" bit. See RFC 4861. */
2626
                r = dhcp6_start_on_ra(link, !(flags & ND_RA_FLAG_MANAGED));
39✔
2627
                break;
39✔
2628
        }
2629
        case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS:
×
2630
                /* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in solicit mode
2631
                 * even if the router flags have neither M nor O flags. */
2632
                r = dhcp6_start_on_ra(link, /* information_request= */ false);
×
2633
                break;
×
2634

2635
        default:
×
2636
                assert_not_reached();
×
2637
        }
2638

2639
        if (r < 0)
39✔
2640
                return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
×
2641

2642
        log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
39✔
2643
        return 0;
2644
}
2645

2646
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
122✔
2647
        struct in6_addr router;
122✔
2648
        usec_t timestamp_usec;
122✔
2649
        int r;
122✔
2650

2651
        assert(link);
122✔
2652
        assert(link->network);
122✔
2653
        assert(link->manager);
122✔
2654
        assert(rt);
122✔
2655

2656
        r = sd_ndisc_router_get_sender_address(rt, &router);
122✔
2657
        if (r == -ENODATA) {
122✔
2658
                log_link_debug(link, "Received RA without router address, ignoring.");
×
2659
                return 0;
×
2660
        }
2661
        if (r < 0)
122✔
2662
                return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
×
2663

2664
        if (in6_prefix_is_filtered(&router, 128, link->network->ndisc_allow_listed_router, link->network->ndisc_deny_listed_router)) {
122✔
2665
                if (!set_isempty(link->network->ndisc_allow_listed_router))
×
2666
                        log_link_debug(link, "Router %s is not in allow list, ignoring.", IN6_ADDR_TO_STRING(&router));
×
2667
                else
2668
                        log_link_debug(link, "Router %s is in deny list, ignoring.", IN6_ADDR_TO_STRING(&router));
×
2669
                return 0;
×
2670
        }
2671

2672
        r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &timestamp_usec);
122✔
2673
        if (r == -ENODATA) {
122✔
2674
                log_link_debug(link, "Received RA without timestamp, ignoring.");
×
2675
                return 0;
×
2676
        }
2677
        if (r < 0)
122✔
2678
                return r;
2679

2680
        r = ndisc_drop_outdated(link, /* router= */ NULL, timestamp_usec);
122✔
2681
        if (r < 0)
122✔
2682
                return r;
2683

2684
        r = ndisc_remember_router(link, rt);
122✔
2685
        if (r < 0)
122✔
2686
                return r;
2687

2688
        r = ndisc_start_dhcp6_client(link, rt);
122✔
2689
        if (r < 0)
122✔
2690
                return r;
2691

2692
        r = ndisc_router_process_reachable_time(link, rt);
122✔
2693
        if (r < 0)
122✔
2694
                return r;
2695

2696
        r = ndisc_router_process_retransmission_time(link, rt);
122✔
2697
        if (r < 0)
122✔
2698
                return r;
2699

2700
        r = ndisc_router_process_hop_limit(link, rt);
122✔
2701
        if (r < 0)
122✔
2702
                return r;
2703

2704
        r = ndisc_router_process_mtu(link, rt);
122✔
2705
        if (r < 0)
122✔
2706
                return r;
2707

2708
        r = ndisc_router_process_options(link, rt, /* zero_lifetime= */ true);
122✔
2709
        if (r < 0)
122✔
2710
                return r;
2711

2712
        r = ndisc_router_process_default(link, rt);
122✔
2713
        if (r < 0)
122✔
2714
                return r;
2715

2716
        r = ndisc_router_process_options(link, rt, /* zero_lifetime= */ false);
122✔
2717
        if (r < 0)
122✔
2718
                return r;
2719

2720
        r = ndisc_setup_expire(link);
122✔
2721
        if (r < 0)
122✔
2722
                return r;
2723

2724
        if (sd_ndisc_router_get_lifetime(rt, NULL) <= 0)
122✔
2725
                (void) ndisc_drop_redirect(link, &router);
19✔
2726

2727
        if (link->ndisc_messages == 0)
122✔
2728
                link->ndisc_configured = true;
15✔
2729
        else
2730
                log_link_debug(link, "Setting SLAAC addresses and router.");
107✔
2731

2732
        if (!link->ndisc_configured)
122✔
2733
                link_set_state(link, LINK_STATE_CONFIGURING);
88✔
2734

2735
        link_check_ready(link);
122✔
2736
        return 0;
2737
}
2738

2739
static int ndisc_neighbor_handle_non_router_message(Link *link, sd_ndisc_neighbor *na) {
×
2740
        struct in6_addr address;
×
2741
        int r;
×
2742

2743
        assert(link);
×
2744
        assert(na);
×
2745

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

2749
        r = sd_ndisc_neighbor_get_target_address(na, &address);
×
2750
        if (r == -ENODATA)
×
2751
                return 0;
×
2752
        if (r < 0)
×
2753
                return r;
2754

2755
        /* Remove the routes configured by Redirect messages. */
2756
        r = ndisc_drop_redirect(link, &address);
×
2757

2758
        /* Also remove the default gateway via the host, but keep the configurations based on the RA options. */
2759
        _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = hashmap_remove(link->ndisc_routers_by_sender, &address);
×
2760
        if (rt)
×
2761
                RET_GATHER(r, ndisc_router_drop_default(link, rt));
×
2762

2763
        return r;
×
2764
}
2765

2766
static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *na) {
5✔
2767
        struct in6_addr current_address, original_address;
5✔
2768
        int r;
5✔
2769

2770
        assert(link);
5✔
2771
        assert(link->manager);
5✔
2772
        assert(na);
5✔
2773

2774
        /* Received Neighbor Advertisement message with Router flag. If the router address is changed, update
2775
         * the provider field of configurations. */
2776

2777
        r = sd_ndisc_neighbor_get_sender_address(na, &current_address);
5✔
2778
        if (r == -ENODATA)
5✔
2779
                return 0;
5✔
2780
        if (r < 0)
5✔
2781
                return r;
2782

2783
        r = sd_ndisc_neighbor_get_target_address(na, &original_address);
5✔
2784
        if (r == -ENODATA)
5✔
2785
                return 0;
2786
        if (r < 0)
5✔
2787
                return r;
2788

2789
        if (in6_addr_equal(&current_address, &original_address))
5✔
2790
                return 0; /* the router address is not changed */
2791

2792
        r = ndisc_update_router_address(link, &original_address, &current_address);
×
2793
        if (r < 0)
×
2794
                return r;
2795

2796
        r = ndisc_update_redirect_sender(link, &original_address, &current_address);
×
2797
        if (r < 0)
×
2798
                return r;
2799

2800
        Route *route;
×
2801
        SET_FOREACH(route, link->manager->routes) {
×
2802
                if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
×
2803
                        continue;
×
2804

2805
                if (!route_is_bound_to_link(route, link))
×
2806
                        continue;
×
2807

2808
                if (!in6_addr_equal(&route->provider.in6, &original_address))
×
2809
                        continue;
×
2810

2811
                route->provider.in6 = current_address;
×
2812
        }
2813

2814
        Address *address;
×
2815
        SET_FOREACH(address, link->addresses) {
×
2816
                if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
×
2817
                        continue;
×
2818

2819
                if (!in6_addr_equal(&address->provider.in6, &original_address))
×
2820
                        continue;
×
2821

2822
                address->provider.in6 = current_address;
×
2823
        }
2824

2825
        NDiscRDNSS *rdnss;
×
2826
        SET_FOREACH(rdnss, link->ndisc_rdnss) {
×
2827
                if (!in6_addr_equal(&rdnss->router, &original_address))
×
2828
                        continue;
×
2829

2830
                rdnss->router = current_address;
×
2831
        }
2832

2833
        NDiscDNSSL *dnssl;
×
2834
        SET_FOREACH(dnssl, link->ndisc_dnssl) {
×
2835
                if (!in6_addr_equal(&dnssl->router, &original_address))
×
2836
                        continue;
×
2837

2838
                dnssl->router = current_address;
×
2839
        }
2840

2841
        NDiscCaptivePortal *cp;
×
2842
        SET_FOREACH(cp, link->ndisc_captive_portals) {
×
2843
                if (!in6_addr_equal(&cp->router, &original_address))
×
2844
                        continue;
×
2845

2846
                cp->router = current_address;
×
2847
        }
2848

2849
        NDiscPREF64 *p64;
×
2850
        SET_FOREACH(p64, link->ndisc_pref64) {
×
2851
                if (!in6_addr_equal(&p64->router, &original_address))
×
2852
                        continue;
×
2853

2854
                p64->router = current_address;
×
2855
        }
2856

2857
        NDiscDNR *dnr;
×
2858
        SET_FOREACH(dnr, link->ndisc_dnr) {
×
2859
                if (!in6_addr_equal(&dnr->router, &original_address))
×
2860
                        continue;
×
2861

2862
                dnr->router = current_address;
×
2863
        }
2864

2865
        return 0;
×
2866
}
2867

2868
static int ndisc_neighbor_handler(Link *link, sd_ndisc_neighbor *na) {
5✔
2869
        int r;
5✔
2870

2871
        assert(link);
5✔
2872
        assert(na);
5✔
2873

2874
        r = sd_ndisc_neighbor_is_router(na);
5✔
2875
        if (r < 0)
5✔
2876
                return r;
2877
        if (r == 0)
5✔
2878
                r = ndisc_neighbor_handle_non_router_message(link, na);
×
2879
        else
2880
                r = ndisc_neighbor_handle_router_message(link, na);
5✔
2881
        if (r < 0)
5✔
2882
                return r;
×
2883

2884
        return 0;
2885
}
2886

2887
static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
129✔
2888
        Link *link = ASSERT_PTR(userdata);
129✔
2889
        int r;
129✔
2890

2891
        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
129✔
2892
                return;
2893

2894
        switch (event) {
129✔
2895

2896
        case SD_NDISC_EVENT_ROUTER:
122✔
2897
                r = ndisc_router_handler(link, ASSERT_PTR(message));
122✔
2898
                if (r < 0 && r != -EBADMSG) {
122✔
2899
                        link_enter_failed(link);
×
2900
                        return;
×
2901
                }
2902
                break;
2903

2904
        case SD_NDISC_EVENT_NEIGHBOR:
5✔
2905
                r = ndisc_neighbor_handler(link, ASSERT_PTR(message));
5✔
2906
                if (r < 0 && r != -EBADMSG) {
5✔
2907
                        link_enter_failed(link);
×
2908
                        return;
×
2909
                }
2910
                break;
2911

2912
        case SD_NDISC_EVENT_REDIRECT:
×
2913
                r = ndisc_redirect_handler(link, ASSERT_PTR(message));
×
2914
                if (r < 0 && r != -EBADMSG) {
×
2915
                        log_link_warning_errno(link, r, "Failed to process Redirect message: %m");
×
2916
                        link_enter_failed(link);
×
2917
                        return;
×
2918
                }
2919
                break;
2920

2921
        case SD_NDISC_EVENT_TIMEOUT:
2922
                log_link_debug(link, "NDisc handler get timeout event");
2✔
2923
                if (link->ndisc_messages == 0) {
2✔
2924
                        link->ndisc_configured = true;
2✔
2925
                        link_check_ready(link);
2✔
2926
                }
2927
                break;
2928

2929
        default:
2930
                log_link_debug(link, "Received unsupported NDisc event, ignoring.");
×
2931
        }
2932
}
2933

2934
static int ndisc_configure(Link *link) {
93✔
2935
        int r;
93✔
2936

2937
        assert(link);
93✔
2938

2939
        if (!link_ndisc_enabled(link))
93✔
2940
                return 0;
2941

2942
        if (link->ndisc)
93✔
2943
                return -EBUSY; /* Already configured. */
2944

2945
        r = sd_ndisc_new(&link->ndisc);
93✔
2946
        if (r < 0)
93✔
2947
                return r;
2948

2949
        r = sd_ndisc_attach_event(link->ndisc, link->manager->event, 0);
93✔
2950
        if (r < 0)
93✔
2951
                return r;
2952

2953
        if (link->hw_addr.length == ETH_ALEN) {
93✔
2954
                r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.ether);
93✔
2955
                if (r < 0)
93✔
2956
                        return r;
2957
        }
2958

2959
        r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex);
93✔
2960
        if (r < 0)
93✔
2961
                return r;
2962

2963
        r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link);
93✔
2964
        if (r < 0)
93✔
2965
                return r;
×
2966

2967
        return 0;
2968
}
2969

2970
int ndisc_start(Link *link) {
982✔
2971
        int r;
982✔
2972

2973
        assert(link);
982✔
2974

2975
        if (!link->ndisc || !link->dhcp6_client)
982✔
2976
                return 0;
2977

2978
        if (!link_has_carrier(link))
141✔
2979
                return 0;
2980

2981
        if (in6_addr_is_null(&link->ipv6ll_address))
141✔
2982
                return 0;
2983

2984
        r = sd_ndisc_set_link_local_address(link->ndisc, &link->ipv6ll_address);
92✔
2985
        if (r < 0)
92✔
2986
                return r;
2987

2988
        log_link_debug(link, "Discovering IPv6 routers");
92✔
2989

2990
        r = sd_ndisc_start(link->ndisc);
92✔
2991
        if (r < 0)
92✔
2992
                return r;
×
2993

2994
        return 1;
2995
}
2996

2997
static int ndisc_process_request(Request *req, Link *link, void *userdata) {
1,790✔
2998
        int r;
1,790✔
2999

3000
        assert(link);
1,790✔
3001

3002
        if (!link_is_ready_to_configure(link, /* allow_unmanaged= */ false))
1,790✔
3003
                return 0;
3004

3005
        r = ndisc_configure(link);
93✔
3006
        if (r < 0)
93✔
3007
                return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Discovery: %m");
×
3008

3009
        r = ndisc_start(link);
93✔
3010
        if (r < 0)
93✔
3011
                return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
×
3012

3013
        log_link_debug(link, "IPv6 Router Discovery is configured%s.",
142✔
3014
                       r > 0 ? " and started" : "");
3015
        return 1;
3016
}
3017

3018
int link_request_ndisc(Link *link) {
857✔
3019
        int r;
857✔
3020

3021
        assert(link);
857✔
3022

3023
        if (!link_ndisc_enabled(link))
857✔
3024
                return 0;
3025

3026
        if (link->ndisc)
93✔
3027
                return 0;
3028

3029
        r = link_queue_request(link, REQUEST_TYPE_NDISC, ndisc_process_request, NULL);
93✔
3030
        if (r < 0)
93✔
3031
                return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Discovery: %m");
×
3032

3033
        log_link_debug(link, "Requested configuring of the IPv6 Router Discovery.");
93✔
3034
        return 0;
3035
}
3036

3037
int link_drop_ndisc_config(Link *link, Network *network) {
825✔
3038
        int r, ret = 0;
825✔
3039

3040
        assert(link);
825✔
3041
        assert(link->network);
825✔
3042

3043
        if (link->network == network)
825✔
3044
                return 0; /* .network file is unchanged. It is not necessary to reconfigure the client. */
825✔
3045

3046
        if (!link_ndisc_enabled(link)) {
825✔
3047
                /* NDisc is disabled. Stop the client if it is running and flush configs. */
3048
                ret = ndisc_stop(link);
744✔
3049
                ndisc_flush(link);
744✔
3050
                link->ndisc = sd_ndisc_unref(link->ndisc);
744✔
3051
                return ret;
744✔
3052
        }
3053

3054
        /* Even if the client was previously enabled and also enabled in the new .network file, detailed
3055
         * settings for the client may be different. Let's unref() the client. */
3056
        link->ndisc = sd_ndisc_unref(link->ndisc);
81✔
3057

3058
        /* Get if NDisc was enabled or not. */
3059
        Network *current = link->network;
81✔
3060
        link->network = network;
81✔
3061
        bool enabled = link_ndisc_enabled(link);
81✔
3062
        link->network = current;
81✔
3063

3064
        /* If previously explicitly disabled, there should be nothing to drop.
3065
         * If we do not know the previous setting of the client, e.g. when networkd is restarted, in that
3066
         * case we do not have the previous .network file assigned to the interface, then  let's assume no
3067
         * detailed configuration is changed. Hopefully, unmatching configurations will be dropped after
3068
         * their lifetime. */
3069
        if (!enabled)
81✔
3070
                return 0;
3071

3072
        assert(network);
27✔
3073

3074
        /* Redirect messages will be ignored. Drop configurations based on the previously received redirect
3075
         * messages. */
3076
        if (!network->ndisc_use_redirect)
27✔
3077
                (void) ndisc_drop_redirect(link, /* router= */ NULL);
×
3078

3079
        /* If one of the route setting is changed, drop all routes. */
3080
        if (link->network->ndisc_use_gateway != network->ndisc_use_gateway ||
27✔
3081
            link->network->ndisc_use_route_prefix != network->ndisc_use_route_prefix ||
27✔
3082
            link->network->ndisc_use_onlink_prefix != network->ndisc_use_onlink_prefix ||
27✔
3083
            link->network->ndisc_quickack != network->ndisc_quickack ||
27✔
3084
            link->network->ndisc_route_metric_high != network->ndisc_route_metric_high ||
27✔
3085
            link->network->ndisc_route_metric_medium != network->ndisc_route_metric_medium ||
26✔
3086
            link->network->ndisc_route_metric_low != network->ndisc_route_metric_low ||
52✔
3087
            !set_equal(link->network->ndisc_deny_listed_router, network->ndisc_deny_listed_router) ||
52✔
3088
            !set_equal(link->network->ndisc_allow_listed_router, network->ndisc_allow_listed_router) ||
52✔
3089
            !set_equal(link->network->ndisc_deny_listed_prefix, network->ndisc_deny_listed_prefix) ||
52✔
3090
            !set_equal(link->network->ndisc_allow_listed_prefix, network->ndisc_allow_listed_prefix) ||
51✔
3091
            !set_equal(link->network->ndisc_deny_listed_route_prefix, network->ndisc_deny_listed_route_prefix) ||
50✔
3092
            !set_equal(link->network->ndisc_allow_listed_route_prefix, network->ndisc_allow_listed_route_prefix)) {
25✔
3093
                Route *route;
2✔
3094
                SET_FOREACH(route, link->manager->routes) {
49✔
3095
                        if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
47✔
3096
                                continue;
40✔
3097

3098
                        if (!route_is_bound_to_link(route, link))
7✔
3099
                                continue;
×
3100

3101
                        if (route->protocol == RTPROT_REDIRECT)
7✔
3102
                                continue; /* redirect route is handled by ndisc_drop_redirect(). */
×
3103

3104
                        r = route_remove_and_cancel(route, link->manager);
7✔
3105
                        if (r < 0)
7✔
3106
                                RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove SLAAC route, ignoring: %m"));
×
3107
                }
3108

3109
                RET_GATHER(ret, ndisc_remove_unused_nexthops(link));
2✔
3110
        }
3111

3112
        /* If SLAAC address is disabled, drop all addresses. */
3113
        if (!network->ndisc_use_autonomous_prefix ||
54✔
3114
            !set_equal(link->network->ndisc_tokens, network->ndisc_tokens) ||
54✔
3115
            !set_equal(link->network->ndisc_deny_listed_prefix, network->ndisc_deny_listed_prefix) ||
54✔
3116
            !set_equal(link->network->ndisc_allow_listed_prefix, network->ndisc_allow_listed_prefix)) {
27✔
3117
                Address *address;
1✔
3118
                SET_FOREACH(address, link->addresses) {
4✔
3119
                        if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
3✔
3120
                                continue;
1✔
3121

3122
                        r = address_remove_and_cancel(address, link);
2✔
3123
                        if (r < 0)
2✔
3124
                                RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove SLAAC address, ignoring: %m"));
×
3125
                }
3126
        }
3127

3128
        if (!network->ndisc_use_mtu)
27✔
3129
                link->ndisc_mtu = 0;
×
3130

3131
        return ret;
3132
}
3133

3134
int ndisc_stop(Link *link) {
5,340✔
3135
        assert(link);
5,340✔
3136

3137
        link->ndisc_expire = sd_event_source_disable_unref(link->ndisc_expire);
5,340✔
3138

3139
        return sd_ndisc_stop(link->ndisc);
5,340✔
3140
}
3141

3142
void ndisc_flush(Link *link) {
10,165✔
3143
        assert(link);
10,165✔
3144

3145
        /* Remove all addresses, routes, RDNSS, DNSSL, DNR, and Captive Portal entries, without exception. */
3146
        (void) ndisc_drop_outdated(link, /* router= */ NULL, /* timestamp_usec= */ USEC_INFINITY);
10,165✔
3147
        (void) ndisc_drop_redirect(link, /* router= */ NULL);
10,165✔
3148

3149
        link->ndisc_routers_by_sender = hashmap_free(link->ndisc_routers_by_sender);
10,165✔
3150
        link->ndisc_rdnss = set_free(link->ndisc_rdnss);
10,165✔
3151
        link->ndisc_dnssl = set_free(link->ndisc_dnssl);
10,165✔
3152
        link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);
10,165✔
3153
        link->ndisc_pref64 = set_free(link->ndisc_pref64);
10,165✔
3154
        link->ndisc_redirects = set_free(link->ndisc_redirects);
10,165✔
3155
        link->ndisc_dnr = set_free(link->ndisc_dnr);
10,165✔
3156
        link->ndisc_mtu = 0;
10,165✔
3157
}
10,165✔
3158

3159
static const char* const ndisc_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
3160
        [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO]     = "no",
3161
        [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always",
3162
        [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES]    = "yes",
3163
};
3164

3165
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(ndisc_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES);
×
3166

3167
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