Coverage Report

Created: 2025-12-11 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/network/networkd-nexthop.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later
2
 * Copyright © 2019 VMware, Inc.
3
 */
4
5
#include <linux/nexthop.h>
6
#include <net/if.h>
7
#include <stdio.h>
8
9
#include "sd-netlink.h"
10
11
#include "alloc-util.h"
12
#include "conf-parser.h"
13
#include "errno-util.h"
14
#include "extract-word.h"
15
#include "netlink-util.h"
16
#include "networkd-link.h"
17
#include "networkd-manager.h"
18
#include "networkd-network.h"
19
#include "networkd-nexthop.h"
20
#include "networkd-queue.h"
21
#include "networkd-route.h"
22
#include "networkd-route-util.h"
23
#include "ordered-set.h"
24
#include "parse-util.h"
25
#include "set.h"
26
#include "siphash24.h"
27
#include "string-util.h"
28
29
0
static void nexthop_detach_from_group_members(NextHop *nexthop) {
30
0
        assert(nexthop);
31
0
        assert(nexthop->manager);
32
0
        assert(nexthop->id > 0);
33
34
0
        struct nexthop_grp *nhg;
35
0
        HASHMAP_FOREACH(nhg, nexthop->group) {
36
0
                NextHop *nh;
37
38
0
                if (nexthop_get_by_id(nexthop->manager, nhg->id, &nh) < 0)
39
0
                        continue;
40
41
0
                set_remove(nh->nexthops, UINT32_TO_PTR(nexthop->id));
42
0
        }
43
0
}
44
45
0
static void nexthop_attach_to_group_members(NextHop *nexthop) {
46
0
        int r;
47
48
0
        assert(nexthop);
49
0
        assert(nexthop->manager);
50
0
        assert(nexthop->id > 0);
51
52
0
        struct nexthop_grp *nhg;
53
0
        HASHMAP_FOREACH(nhg, nexthop->group) {
54
0
                NextHop *nh;
55
56
0
                r = nexthop_get_by_id(nexthop->manager, nhg->id, &nh);
57
0
                if (r < 0) {
58
0
                        if (nexthop->manager->manage_foreign_nexthops)
59
0
                                log_debug_errno(r, "Nexthop (id=%"PRIu32") has unknown group member (%"PRIu32"), ignoring.",
60
0
                                                nexthop->id, nhg->id);
61
0
                        continue;
62
0
                }
63
64
0
                r = set_ensure_put(&nh->nexthops, NULL, UINT32_TO_PTR(nexthop->id));
65
0
                if (r < 0)
66
0
                        log_debug_errno(r, "Failed to save nexthop ID (%"PRIu32") to group member (%"PRIu32"), ignoring: %m",
67
0
                                        nexthop->id, nhg->id);
68
0
        }
69
0
}
70
71
21.7k
static NextHop* nexthop_detach_impl(NextHop *nexthop) {
72
21.7k
        assert(nexthop);
73
21.7k
        assert(!nexthop->manager || !nexthop->network);
74
75
21.7k
        if (nexthop->network) {
76
10.8k
                assert(nexthop->section);
77
10.8k
                ordered_hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
78
10.8k
                nexthop->network = NULL;
79
10.8k
                return nexthop;
80
10.8k
        }
81
82
10.8k
        if (nexthop->manager) {
83
0
                assert(nexthop->id > 0);
84
85
0
                nexthop_detach_from_group_members(nexthop);
86
87
0
                hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
88
0
                nexthop->manager = NULL;
89
0
                return nexthop;
90
0
        }
91
92
10.8k
        return NULL;
93
10.8k
}
94
95
10.8k
static void nexthop_detach(NextHop *nexthop) {
96
10.8k
        nexthop_unref(nexthop_detach_impl(nexthop));
97
10.8k
}
98
99
10.8k
static NextHop* nexthop_free(NextHop *nexthop) {
100
10.8k
        if (!nexthop)
101
0
                return NULL;
102
103
10.8k
        nexthop_detach_impl(nexthop);
104
105
10.8k
        config_section_free(nexthop->section);
106
10.8k
        hashmap_free(nexthop->group);
107
10.8k
        set_free(nexthop->nexthops);
108
10.8k
        set_free(nexthop->routes);
109
110
10.8k
        return mfree(nexthop);
111
10.8k
}
112
113
10.8k
DEFINE_TRIVIAL_REF_UNREF_FUNC(NextHop, nexthop, nexthop_free);
Unexecuted instantiation: nexthop_ref
Unexecuted instantiation: nexthop_unref
114
10.8k
115
10.8k
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
116
10.8k
                nexthop_hash_ops,
117
10.8k
                void,
118
10.8k
                trivial_hash_func,
119
10.8k
                trivial_compare_func,
120
10.8k
                NextHop,
121
10.8k
                nexthop_detach);
122
10.8k
123
10.8k
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
124
10.8k
                nexthop_section_hash_ops,
125
10.8k
                ConfigSection,
126
10.8k
                config_section_hash_func,
127
10.8k
                config_section_compare_func,
128
10.8k
                NextHop,
129
10.8k
                nexthop_detach);
130
10.8k
131
10.8k
int nexthop_new(NextHop **ret) {
132
10.8k
        _cleanup_(nexthop_unrefp) NextHop *nexthop = NULL;
133
134
10.8k
        nexthop = new(NextHop, 1);
135
10.8k
        if (!nexthop)
136
0
                return -ENOMEM;
137
138
10.8k
        *nexthop = (NextHop) {
139
10.8k
                .n_ref = 1,
140
10.8k
                .onlink = -1,
141
10.8k
        };
142
143
10.8k
        *ret = TAKE_PTR(nexthop);
144
145
10.8k
        return 0;
146
10.8k
}
147
148
15.5k
static int nexthop_new_static(Network *network, const char *filename, unsigned section_line, NextHop **ret) {
149
15.5k
        _cleanup_(config_section_freep) ConfigSection *n = NULL;
150
15.5k
        _cleanup_(nexthop_unrefp) NextHop *nexthop = NULL;
151
15.5k
        int r;
152
153
15.5k
        assert(network);
154
15.5k
        assert(ret);
155
15.5k
        assert(filename);
156
15.5k
        assert(section_line > 0);
157
158
15.5k
        r = config_section_new(filename, section_line, &n);
159
15.5k
        if (r < 0)
160
0
                return r;
161
162
15.5k
        nexthop = ordered_hashmap_get(network->nexthops_by_section, n);
163
15.5k
        if (nexthop) {
164
4.61k
                *ret = TAKE_PTR(nexthop);
165
4.61k
                return 0;
166
4.61k
        }
167
168
10.8k
        r = nexthop_new(&nexthop);
169
10.8k
        if (r < 0)
170
0
                return r;
171
172
10.8k
        nexthop->protocol = RTPROT_STATIC;
173
10.8k
        nexthop->network = network;
174
10.8k
        nexthop->section = TAKE_PTR(n);
175
10.8k
        nexthop->source = NETWORK_CONFIG_SOURCE_STATIC;
176
177
10.8k
        r = ordered_hashmap_ensure_put(&network->nexthops_by_section, &nexthop_section_hash_ops, nexthop->section, nexthop);
178
10.8k
        if (r < 0)
179
0
                return r;
180
181
10.8k
        *ret = TAKE_PTR(nexthop);
182
10.8k
        return 0;
183
10.8k
}
184
185
0
static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) {
186
0
        assert(nexthop);
187
0
        assert(state);
188
189
0
        siphash24_compress_typesafe(nexthop->id, state);
190
0
}
191
192
0
static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
193
0
        assert(a);
194
0
        assert(b);
195
196
0
        return CMP(a->id, b->id);
197
0
}
198
199
0
static int nexthop_compare_full(const NextHop *a, const NextHop *b) {
200
0
        int r;
201
202
0
        assert(a);
203
0
        assert(b);
204
205
        /* This compares detailed configs, except for ID and ifindex. */
206
207
0
        r = CMP(a->protocol, b->protocol);
208
0
        if (r != 0)
209
0
                return r;
210
211
0
        r = CMP(a->flags, b->flags);
212
0
        if (r != 0)
213
0
                return r;
214
215
0
        r = CMP(hashmap_size(a->group), hashmap_size(b->group));
216
0
        if (r != 0)
217
0
                return r;
218
219
0
        if (!hashmap_isempty(a->group)) {
220
0
                struct nexthop_grp *ga;
221
222
0
                HASHMAP_FOREACH(ga, a->group) {
223
0
                        struct nexthop_grp *gb;
224
225
0
                        gb = hashmap_get(b->group, UINT32_TO_PTR(ga->id));
226
0
                        if (!gb)
227
0
                                return CMP(ga, gb);
228
229
0
                        r = CMP(ga->weight, gb->weight);
230
0
                        if (r != 0)
231
0
                                return r;
232
0
                }
233
0
        }
234
235
0
        r = CMP(a->blackhole, b->blackhole);
236
0
        if (r != 0)
237
0
                return r;
238
239
0
        r = CMP(a->family, b->family);
240
0
        if (r != 0)
241
0
                return r;
242
243
0
        if (IN_SET(a->family, AF_INET, AF_INET6)) {
244
0
                r = memcmp(&a->gw.address, &b->gw.address, FAMILY_ADDRESS_SIZE(a->family));
245
0
                if (r != 0)
246
0
                        return r;
247
0
        }
248
249
0
        return 0;
250
0
}
251
252
0
static int nexthop_dup(const NextHop *src, NextHop **ret) {
253
0
        _cleanup_(nexthop_unrefp) NextHop *dest = NULL;
254
0
        struct nexthop_grp *nhg;
255
0
        int r;
256
257
0
        assert(src);
258
0
        assert(ret);
259
260
0
        dest = newdup(NextHop, src, 1);
261
0
        if (!dest)
262
0
                return -ENOMEM;
263
264
        /* clear the reference counter and all pointers */
265
0
        dest->n_ref = 1;
266
0
        dest->manager = NULL;
267
0
        dest->network = NULL;
268
0
        dest->section = NULL;
269
0
        dest->group = NULL;
270
0
        dest->nexthops = NULL;
271
0
        dest->routes = NULL;
272
273
0
        HASHMAP_FOREACH(nhg, src->group) {
274
0
                _cleanup_free_ struct nexthop_grp *g = NULL;
275
276
0
                g = newdup(struct nexthop_grp, nhg, 1);
277
0
                if (!g)
278
0
                        return -ENOMEM;
279
280
0
                r = hashmap_ensure_put(&dest->group, &trivial_hash_ops_value_free, UINT32_TO_PTR(g->id), g);
281
0
                if (r < 0)
282
0
                        return r;
283
0
                if (r > 0)
284
0
                        TAKE_PTR(g);
285
0
        }
286
287
0
        *ret = TAKE_PTR(dest);
288
0
        return 0;
289
0
}
290
291
0
static bool nexthop_bound_to_link(const NextHop *nexthop) {
292
0
        assert(nexthop);
293
0
        return !nexthop->blackhole && hashmap_isempty(nexthop->group);
294
0
}
295
296
0
int nexthop_get_by_id(Manager *manager, uint32_t id, NextHop **ret) {
297
0
        NextHop *nh;
298
299
0
        assert(manager);
300
301
0
        if (id == 0)
302
0
                return -EINVAL;
303
304
0
        nh = hashmap_get(manager->nexthops_by_id, UINT32_TO_PTR(id));
305
0
        if (!nh)
306
0
                return -ENOENT;
307
308
0
        if (ret)
309
0
                *ret = nh;
310
0
        return 0;
311
0
}
312
313
0
static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) {
314
0
        NextHop *nexthop;
315
0
        int ifindex;
316
317
0
        assert(link);
318
0
        assert(link->manager);
319
0
        assert(in);
320
321
0
        if (in->id > 0)
322
0
                return nexthop_get_by_id(link->manager, in->id, ret);
323
324
        /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by
325
         * nexthop_section_verify(). */
326
0
        assert(link->manager->manage_foreign_nexthops);
327
328
0
        ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0;
329
330
0
        HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
331
0
                if (nexthop->ifindex != ifindex)
332
0
                        continue;
333
0
                if (nexthop_compare_full(nexthop, in) != 0)
334
0
                        continue;
335
336
                /* Even if the configuration matches, it may be configured with another [NextHop] section
337
                 * that has an explicit ID. If so, the assigned nexthop is not the one we are looking for. */
338
0
                if (set_contains(link->manager->nexthop_ids, UINT32_TO_PTR(nexthop->id)))
339
0
                        continue;
340
341
0
                if (ret)
342
0
                        *ret = nexthop;
343
0
                return 0;
344
0
        }
345
346
0
        return -ENOENT;
347
0
}
348
349
0
int nexthop_get_request_by_id(Manager *manager, uint32_t id, Request **ret) {
350
0
        Request *req;
351
352
0
        assert(manager);
353
354
0
        if (id == 0)
355
0
                return -EINVAL;
356
357
0
        req = ordered_set_get(
358
0
                        manager->request_queue,
359
0
                        &(Request) {
360
0
                                .type = REQUEST_TYPE_NEXTHOP,
361
0
                                .userdata = (void*) &(const NextHop) { .id = id },
362
0
                                .hash_func = (hash_func_t) nexthop_hash_func,
363
0
                                .compare_func = (compare_func_t) nexthop_compare_func,
364
0
                        });
365
0
        if (!req)
366
0
                return -ENOENT;
367
368
0
        if (ret)
369
0
                *ret = req;
370
0
        return 0;
371
0
}
372
373
0
static int nexthop_get_request(Link *link, const NextHop *in, Request **ret) {
374
0
        Request *req;
375
0
        int ifindex;
376
377
0
        assert(link);
378
0
        assert(link->manager);
379
0
        assert(in);
380
381
0
        if (in->id > 0)
382
0
                return nexthop_get_request_by_id(link->manager, in->id, ret);
383
384
        /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by
385
         * nexthop_section_verify(). */
386
0
        assert(link->manager->manage_foreign_nexthops);
387
388
0
        ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0;
389
390
0
        ORDERED_SET_FOREACH(req, link->manager->request_queue) {
391
0
                if (req->type != REQUEST_TYPE_NEXTHOP)
392
0
                        continue;
393
394
0
                NextHop *nexthop = ASSERT_PTR(req->userdata);
395
0
                if (nexthop->ifindex != ifindex)
396
0
                        continue;
397
0
                if (nexthop_compare_full(nexthop, in) != 0)
398
0
                        continue;
399
400
                /* Even if the configuration matches, it may be requested by another [NextHop] section
401
                 * that has an explicit ID. If so, the request is not the one we are looking for. */
402
0
                if (set_contains(link->manager->nexthop_ids, UINT32_TO_PTR(nexthop->id)))
403
0
                        continue;
404
405
0
                if (ret)
406
0
                        *ret = req;
407
0
                return 0;
408
0
        }
409
410
0
        return -ENOENT;
411
0
}
412
413
0
static int nexthop_add_new(Manager *manager, uint32_t id, NextHop **ret) {
414
0
        _cleanup_(nexthop_unrefp) NextHop *nexthop = NULL;
415
0
        int r;
416
417
0
        assert(manager);
418
0
        assert(id > 0);
419
420
0
        r = nexthop_new(&nexthop);
421
0
        if (r < 0)
422
0
                return r;
423
424
0
        nexthop->id = id;
425
426
0
        r = hashmap_ensure_put(&manager->nexthops_by_id, &nexthop_hash_ops, UINT32_TO_PTR(nexthop->id), nexthop);
427
0
        if (r < 0)
428
0
                return r;
429
0
        assert(r > 0);
430
431
0
        nexthop->manager = manager;
432
433
0
        if (ret)
434
0
                *ret = nexthop;
435
436
0
        TAKE_PTR(nexthop);
437
0
        return 0;
438
0
}
439
440
0
static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) {
441
0
        assert(manager);
442
0
        assert(nexthop);
443
444
0
        if (nexthop->id > 0)
445
0
                return 0;
446
447
        /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by
448
         * nexthop_section_verify(). */
449
0
        assert(manager->manage_foreign_nexthops);
450
451
        /* Find the lowest unused ID. */
452
453
0
        for (uint32_t id = 1; id < UINT32_MAX; id++) {
454
0
                if (nexthop_get_by_id(manager, id, NULL) >= 0)
455
0
                        continue;
456
0
                if (nexthop_get_request_by_id(manager, id, NULL) >= 0)
457
0
                        continue;
458
0
                if (set_contains(manager->nexthop_ids, UINT32_TO_PTR(id)))
459
0
                        continue;
460
461
0
                nexthop->id = id;
462
0
                return 0;
463
0
        }
464
465
0
        return -EBUSY;
466
0
}
467
468
0
static int nexthop_to_string(const NextHop *nexthop, Manager *manager, char **ret) {
469
0
        _cleanup_free_ char *group = NULL, *flags = NULL;
470
471
0
        assert(nexthop);
472
0
        assert(manager);
473
0
        assert(ret);
474
475
0
        (void) route_flags_to_string_alloc(nexthop->flags, &flags);
476
477
0
        struct nexthop_grp *nhg;
478
0
        HASHMAP_FOREACH(nhg, nexthop->group)
479
0
                (void) strextendf_with_separator(&group, ",", "%"PRIu32":%"PRIu32, nhg->id, nhg->weight+1u);
480
481
0
        if (asprintf(ret, "id: %"PRIu32", gw: %s, blackhole: %s, group: %s, flags: %s",
482
0
                     nexthop->id,
483
0
                     IN_ADDR_TO_STRING(nexthop->family, &nexthop->gw.address),
484
0
                     yes_no(nexthop->blackhole), strna(group), strna(flags)) < 0)
485
0
                return -ENOMEM;
486
487
0
        return 0;
488
0
}
489
490
0
void log_nexthop_debug(const NextHop *nexthop, const char *str, Manager *manager) {
491
0
        _cleanup_free_ char *state = NULL, *nexthop_str = NULL;
492
0
        Link *link = NULL;
493
494
0
        assert(nexthop);
495
0
        assert(str);
496
0
        assert(manager);
497
498
0
        if (!DEBUG_LOGGING)
499
0
                return;
500
501
0
        (void) link_get_by_index(manager, nexthop->ifindex, &link);
502
0
        (void) network_config_state_to_string_alloc(nexthop->state, &state);
503
0
        (void) nexthop_to_string(nexthop, manager, &nexthop_str);
504
505
0
        log_link_debug(link, "%s %s nexthop (%s): %s",
506
0
                       str, strna(network_config_source_to_string(nexthop->source)), strna(state), strna(nexthop_str));
507
0
}
508
509
0
static void nexthop_forget_dependents(NextHop *nexthop, Manager *manager) {
510
0
        assert(nexthop);
511
0
        assert(manager);
512
513
        /* If a nexthop is removed, the kernel silently removes routes that depend on the removed nexthop.
514
         * Let's forget them. */
515
516
0
        for (;;) {
517
0
                _cleanup_(route_unrefp) Route *route = set_steal_first(nexthop->routes);
518
0
                if (!route)
519
0
                        break;
520
521
0
                Request *req;
522
0
                if (route_get_request(manager, route, &req) >= 0)
523
0
                        route_enter_removed(req->userdata);
524
525
0
                route_enter_removed(route);
526
0
                log_route_debug(route, "Forgetting silently removed", manager);
527
0
                route_detach(route);
528
0
        }
529
530
0
        nexthop->routes = set_free(nexthop->routes);
531
0
}
532
533
0
static void nexthop_forget(Manager *manager, NextHop *nexthop, const char *msg) {
534
0
        assert(manager);
535
0
        assert(nexthop);
536
0
        assert(msg);
537
538
0
        Request *req;
539
0
        if (nexthop_get_request_by_id(manager, nexthop->id, &req) >= 0)
540
0
                nexthop_enter_removed(req->userdata);
541
542
0
        if (!nexthop->manager && nexthop_get_by_id(manager, nexthop->id, &nexthop) < 0)
543
0
                return;
544
545
0
        nexthop_enter_removed(nexthop);
546
0
        log_nexthop_debug(nexthop, msg, manager);
547
0
        nexthop_forget_dependents(nexthop, nexthop->manager);
548
0
        nexthop_detach(nexthop);
549
0
}
550
551
0
static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) {
552
0
        int r;
553
554
0
        assert(m);
555
0
        assert(rreq);
556
557
0
        Manager *manager = ASSERT_PTR(rreq->manager);
558
0
        NextHop *nexthop = ASSERT_PTR(rreq->userdata);
559
560
0
        r = sd_netlink_message_get_errno(m);
561
0
        if (r < 0) {
562
0
                log_message_full_errno(m,
563
0
                                       (r == -ENOENT || !nexthop->manager) ? LOG_DEBUG : LOG_WARNING,
564
0
                                       r, "Could not drop nexthop, ignoring");
565
566
                /* If the nexthop cannot be removed, then assume the nexthop is already removed. */
567
0
                nexthop_forget(manager, nexthop, "Forgetting");
568
0
        }
569
570
0
        return 1;
571
0
}
572
573
0
int nexthop_remove(NextHop *nexthop, Manager *manager) {
574
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
575
0
        Link *link = NULL;
576
0
        int r;
577
578
0
        assert(nexthop);
579
0
        assert(nexthop->id > 0);
580
0
        assert(manager);
581
582
        /* If the nexthop is remembered, then use the remembered object. */
583
0
        (void) nexthop_get_by_id(manager, PTR_TO_UINT32(nexthop->id), &nexthop);
584
585
        /* link may be NULL. */
586
0
        (void) link_get_by_index(manager, nexthop->ifindex, &link);
587
588
0
        log_nexthop_debug(nexthop, "Removing", manager);
589
590
0
        r = sd_rtnl_message_new_nexthop(manager->rtnl, &m, RTM_DELNEXTHOP, AF_UNSPEC, RTPROT_UNSPEC);
591
0
        if (r < 0)
592
0
                return log_link_error_errno(link, r, "Could not create RTM_DELNEXTHOP message: %m");
593
594
0
        r = sd_netlink_message_append_u32(m, NHA_ID, nexthop->id);
595
0
        if (r < 0)
596
0
                return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
597
598
0
        r = manager_remove_request_add(manager, nexthop, nexthop, manager->rtnl, m, nexthop_remove_handler);
599
0
        if (r < 0)
600
0
                return log_link_error_errno(link, r, "Could not queue rtnetlink message: %m");
601
602
0
        nexthop_enter_removing(nexthop);
603
0
        return 0;
604
0
}
605
606
0
int nexthop_remove_and_cancel(NextHop *nexthop, Manager *manager) {
607
0
        _cleanup_(request_unrefp) Request *req = NULL;
608
0
        bool waiting = false;
609
610
0
        assert(nexthop);
611
0
        assert(nexthop->id > 0);
612
0
        assert(manager);
613
614
        /* If the nexthop is remembered by the manager, then use the remembered object. */
615
0
        (void) nexthop_get_by_id(manager, nexthop->id, &nexthop);
616
617
        /* Cancel the request for the nexthop. If the request is already called but we have not received the
618
         * notification about the request, then explicitly remove the nexthop. */
619
0
        if (nexthop_get_request_by_id(manager, nexthop->id, &req) >= 0) {
620
0
                request_ref(req); /* avoid the request freed by request_detach() */
621
0
                waiting = req->waiting_reply;
622
0
                request_detach(req);
623
0
                nexthop_cancel_requesting(nexthop);
624
0
        }
625
626
        /* If we know that the nexthop will come or already exists, remove it. */
627
0
        if (waiting || (nexthop->manager && nexthop_exists(nexthop)))
628
0
                return nexthop_remove(nexthop, manager);
629
630
0
        return 0;
631
0
}
632
633
0
static int nexthop_configure(NextHop *nexthop, Link *link, Request *req) {
634
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
635
0
        int r;
636
637
0
        assert(nexthop);
638
0
        assert(nexthop->id > 0);
639
0
        assert(IN_SET(nexthop->family, AF_UNSPEC, AF_INET, AF_INET6));
640
0
        assert(link);
641
0
        assert(link->manager);
642
0
        assert(link->manager->rtnl);
643
0
        assert(link->ifindex > 0);
644
0
        assert(req);
645
646
0
        log_nexthop_debug(nexthop, "Configuring", link->manager);
647
648
0
        r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &m, RTM_NEWNEXTHOP, nexthop->family, nexthop->protocol);
649
0
        if (r < 0)
650
0
                return r;
651
652
0
        r = sd_netlink_message_append_u32(m, NHA_ID, nexthop->id);
653
0
        if (r < 0)
654
0
                return r;
655
656
0
        if (!hashmap_isempty(nexthop->group)) {
657
0
                _cleanup_free_ struct nexthop_grp *group = NULL;
658
0
                struct nexthop_grp *p, *nhg;
659
660
0
                group = new(struct nexthop_grp, hashmap_size(nexthop->group));
661
0
                if (!group)
662
0
                        return log_oom();
663
664
0
                p = group;
665
0
                HASHMAP_FOREACH(nhg, nexthop->group)
666
0
                        *p++ = *nhg;
667
668
0
                r = sd_netlink_message_append_data(m, NHA_GROUP, group, sizeof(struct nexthop_grp) * hashmap_size(nexthop->group));
669
0
                if (r < 0)
670
0
                        return r;
671
672
0
        } else if (nexthop->blackhole) {
673
0
                r = sd_netlink_message_append_flag(m, NHA_BLACKHOLE);
674
0
                if (r < 0)
675
0
                        return r;
676
0
        } else {
677
0
                assert(nexthop->ifindex == link->ifindex);
678
679
0
                r = sd_netlink_message_append_u32(m, NHA_OIF, nexthop->ifindex);
680
0
                if (r < 0)
681
0
                        return r;
682
683
0
                if (in_addr_is_set(nexthop->family, &nexthop->gw.address)) {
684
0
                        r = netlink_message_append_in_addr_union(m, NHA_GATEWAY, nexthop->family, &nexthop->gw.address);
685
0
                        if (r < 0)
686
0
                                return r;
687
688
0
                        r = sd_rtnl_message_nexthop_set_flags(m, nexthop->flags & RTNH_F_ONLINK);
689
0
                        if (r < 0)
690
0
                                return r;
691
0
                }
692
0
        }
693
694
0
        return request_call_netlink_async(link->manager->rtnl, m, req);
695
0
}
696
697
0
int nexthop_configure_handler_internal(sd_netlink_message *m, Link *link, NextHop *nexthop) {
698
0
        int r;
699
700
0
        assert(m);
701
0
        assert(link);
702
0
        assert(nexthop);
703
704
0
        r = sd_netlink_message_get_errno(m);
705
0
        if (r < 0 && r != -EEXIST) {
706
0
                _cleanup_free_ char *str = NULL;
707
0
                (void) nexthop_to_string(nexthop, link->manager, &str);
708
0
                log_link_message_warning_errno(link, m, r, "Failed to set %s nexthop (%s)",
709
0
                                               network_config_source_to_string(nexthop->source), strna(str));
710
0
                link_enter_failed(link);
711
0
                return 0;
712
0
        }
713
714
0
        return 1;
715
0
}
716
717
0
static int static_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, NextHop *nexthop) {
718
0
        int r;
719
720
0
        assert(link);
721
722
0
        r = nexthop_configure_handler_internal(m, link, nexthop);
723
0
        if (r <= 0)
724
0
                return r;
725
726
0
        if (link->static_nexthop_messages == 0) {
727
0
                log_link_debug(link, "Nexthops set");
728
0
                link->static_nexthops_configured = true;
729
0
                link_check_ready(link);
730
0
        }
731
732
0
        return 1;
733
0
}
734
735
0
int nexthop_is_ready(Manager *manager, uint32_t id, NextHop **ret) {
736
0
        NextHop *nexthop;
737
738
0
        assert(manager);
739
740
0
        if (id == 0)
741
0
                return -EINVAL;
742
743
0
        if (nexthop_get_request_by_id(manager, id, NULL) >= 0)
744
0
                goto not_ready;
745
746
0
        if (nexthop_get_by_id(manager, id, &nexthop) < 0)
747
0
                goto not_ready;
748
749
0
        if (!nexthop_exists(nexthop))
750
0
                goto not_ready;
751
752
0
        if (ret)
753
0
                *ret = nexthop;
754
755
0
        return true;
756
757
0
not_ready:
758
0
        if (ret)
759
0
                *ret = NULL;
760
761
0
        return false;
762
0
}
763
764
0
static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
765
0
        struct nexthop_grp *nhg;
766
0
        int r;
767
768
0
        assert(link);
769
0
        assert(nexthop);
770
0
        assert(nexthop->id > 0);
771
772
0
        if (!link_is_ready_to_configure(link, false))
773
0
                return false;
774
775
0
        if (nexthop_bound_to_link(nexthop)) {
776
0
                assert(nexthop->ifindex == link->ifindex);
777
778
                /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated
779
                 * when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of
780
                 * kernel. */
781
0
                if (link->set_flags_messages > 0)
782
0
                        return false;
783
0
                if (!FLAGS_SET(link->flags, IFF_UP))
784
0
                        return false;
785
0
        }
786
787
        /* All group members must be configured first. */
788
0
        HASHMAP_FOREACH(nhg, nexthop->group) {
789
0
                r = nexthop_is_ready(link->manager, nhg->id, NULL);
790
0
                if (r <= 0)
791
0
                        return r;
792
0
        }
793
794
0
        return gateway_is_ready(link, FLAGS_SET(nexthop->flags, RTNH_F_ONLINK), nexthop->family, &nexthop->gw.address);
795
0
}
796
797
0
static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) {
798
0
        NextHop *existing;
799
0
        int r;
800
801
0
        assert(req);
802
0
        assert(link);
803
0
        assert(link->manager);
804
0
        assert(nexthop);
805
806
0
        if (!nexthop_is_ready_to_configure(link, nexthop))
807
0
                return 0;
808
809
0
        r = nexthop_configure(nexthop, link, req);
810
0
        if (r < 0)
811
0
                return log_link_warning_errno(link, r, "Failed to configure nexthop");
812
813
0
        nexthop_enter_configuring(nexthop);
814
0
        if (nexthop_get_by_id(link->manager, nexthop->id, &existing) >= 0)
815
0
                nexthop_enter_configuring(existing);
816
817
0
        return 1;
818
0
}
819
820
int link_request_nexthop(
821
                Link *link,
822
                const NextHop *nexthop,
823
                unsigned *message_counter,
824
0
                nexthop_netlink_handler_t netlink_handler) {
825
826
0
        _cleanup_(nexthop_unrefp) NextHop *tmp = NULL;
827
0
        NextHop *existing = NULL;
828
0
        int r;
829
830
0
        assert(link);
831
0
        assert(link->manager);
832
0
        assert(nexthop);
833
0
        assert(nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN);
834
835
0
        if (nexthop_get_request(link, nexthop, NULL) >= 0)
836
0
                return 0; /* already requested, skipping. */
837
838
0
        r = nexthop_dup(nexthop, &tmp);
839
0
        if (r < 0)
840
0
                return r;
841
842
0
        if (nexthop_get(link, nexthop, &existing) < 0) {
843
0
                r = nexthop_acquire_id(link->manager, tmp);
844
0
                if (r < 0)
845
0
                        return r;
846
0
        } else {
847
                /* Copy ID */
848
0
                assert(tmp->id == 0 || tmp->id == existing->id);
849
0
                tmp->id = existing->id;
850
851
                /* Copy state for logging below. */
852
0
                tmp->state = existing->state;
853
0
        }
854
855
0
        if (nexthop_bound_to_link(tmp))
856
0
                tmp->ifindex = link->ifindex;
857
858
0
        log_nexthop_debug(tmp, "Requesting", link->manager);
859
0
        r = link_queue_request_safe(link, REQUEST_TYPE_NEXTHOP,
860
0
                                    tmp,
861
0
                                    nexthop_unref,
862
0
                                    nexthop_hash_func,
863
0
                                    nexthop_compare_func,
864
0
                                    nexthop_process_request,
865
0
                                    message_counter,
866
0
                                    netlink_handler,
867
0
                                    NULL);
868
0
        if (r <= 0)
869
0
                return r;
870
871
0
        nexthop_enter_requesting(tmp);
872
0
        if (existing)
873
0
                nexthop_enter_requesting(existing);
874
875
0
        TAKE_PTR(tmp);
876
0
        return 1;
877
0
}
878
879
0
int link_request_static_nexthops(Link *link, bool only_ipv4) {
880
0
        NextHop *nh;
881
0
        int r;
882
883
0
        assert(link);
884
0
        assert(link->network);
885
886
0
        link->static_nexthops_configured = false;
887
888
0
        ORDERED_HASHMAP_FOREACH(nh, link->network->nexthops_by_section) {
889
0
                if (only_ipv4 && nh->family != AF_INET)
890
0
                        continue;
891
892
0
                r = link_request_nexthop(link, nh, &link->static_nexthop_messages, static_nexthop_handler);
893
0
                if (r < 0)
894
0
                        return log_link_warning_errno(link, r, "Could not request nexthop: %m");
895
0
        }
896
897
0
        if (link->static_nexthop_messages == 0) {
898
0
                link->static_nexthops_configured = true;
899
0
                link_check_ready(link);
900
0
        } else {
901
0
                log_link_debug(link, "Requesting nexthops");
902
0
                link_set_state(link, LINK_STATE_CONFIGURING);
903
0
        }
904
905
0
        return 0;
906
0
}
907
908
0
static bool nexthop_can_update(const NextHop *assigned_nexthop, const NextHop *requested_nexthop) {
909
0
        assert(assigned_nexthop);
910
0
        assert(assigned_nexthop->manager);
911
0
        assert(requested_nexthop);
912
0
        assert(requested_nexthop->network);
913
914
        /* A group nexthop cannot be replaced with a non-group nexthop, and vice versa.
915
         * See replace_nexthop_grp() and replace_nexthop_single() in net/ipv4/nexthop.c of the kernel. */
916
0
        if (hashmap_isempty(assigned_nexthop->group) != hashmap_isempty(requested_nexthop->group))
917
0
                return false;
918
919
        /* There are several more conditions if we can replace a group nexthop, e.g. hash threshold and
920
         * resilience. But, currently we do not support to modify that. Let's add checks for them in the
921
         * future when we support to configure them. */
922
923
        /* When a nexthop is replaced with a blackhole nexthop, and a group nexthop has multiple nexthops
924
         * including this nexthop, then the kernel refuses to replace the existing nexthop.
925
         * So, here, for simplicity, let's unconditionally refuse to replace a non-blackhole nexthop with
926
         * a blackhole nexthop. See replace_nexthop() in net/ipv4/nexthop.c of the kernel. */
927
0
        if (!assigned_nexthop->blackhole && requested_nexthop->blackhole)
928
0
                return false;
929
930
0
        return true;
931
0
}
932
933
0
int link_drop_nexthops(Link *link, bool only_static) {
934
0
        NextHop *nexthop;
935
0
        Link *other;
936
0
        int r = 0;
937
938
0
        assert(link);
939
0
        assert(link->manager);
940
941
        /* First, mark all nexthops. */
942
0
        HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
943
                /* do not touch nexthop created by the kernel */
944
0
                if (nexthop->protocol == RTPROT_KERNEL)
945
0
                        continue;
946
947
                /* Ignore nexthops not assigned yet or already removed. */
948
0
                if (!nexthop_exists(nexthop))
949
0
                        continue;
950
951
0
                if (!link_should_mark_config(link, only_static, nexthop->source, nexthop->protocol))
952
0
                        continue;
953
954
                /* Ignore nexthops bound to other links. */
955
0
                if (nexthop->ifindex > 0 && nexthop->ifindex != link->ifindex)
956
0
                        continue;
957
958
0
                nexthop_mark(nexthop);
959
0
        }
960
961
        /* Then, unmark all nexthops requested by active links. */
962
0
        HASHMAP_FOREACH(other, link->manager->links_by_index) {
963
0
                if (only_static && other == link)
964
0
                        continue;
965
966
0
                if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
967
0
                        continue;
968
969
0
                ORDERED_HASHMAP_FOREACH(nexthop, other->network->nexthops_by_section) {
970
0
                        NextHop *existing;
971
972
0
                        if (nexthop_get(other, nexthop, &existing) < 0)
973
0
                                continue;
974
975
0
                        if (!nexthop_can_update(existing, nexthop))
976
0
                                continue;
977
978
                        /* Found matching static configuration. Keep the existing nexthop. */
979
0
                        nexthop_unmark(existing);
980
0
                }
981
0
        }
982
983
        /* Finally, remove all marked nexthops. */
984
0
        HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
985
0
                if (!nexthop_is_marked(nexthop))
986
0
                        continue;
987
988
0
                RET_GATHER(r, nexthop_remove(nexthop, link->manager));
989
0
        }
990
991
0
        return r;
992
0
}
993
994
0
void link_forget_nexthops(Link *link) {
995
0
        assert(link);
996
0
        assert(link->manager);
997
0
        assert(link->ifindex > 0);
998
0
        assert(!FLAGS_SET(link->flags, IFF_UP));
999
1000
        /* See comments in link_forget_routes(). */
1001
1002
        /* Remove all IPv4 nexthops. */
1003
0
        NextHop *nexthop;
1004
0
        HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
1005
0
                if (nexthop->ifindex != link->ifindex)
1006
0
                        continue;
1007
0
                if (nexthop->family != AF_INET)
1008
0
                        continue;
1009
1010
0
                nexthop_forget(link->manager, nexthop, "Forgetting silently removed");
1011
0
        }
1012
1013
        /* Remove all group nexthops their all members are removed in the above. */
1014
0
        HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
1015
0
                if (hashmap_isempty(nexthop->group))
1016
0
                        continue;
1017
1018
                /* Update group members. */
1019
0
                struct nexthop_grp *nhg;
1020
0
                HASHMAP_FOREACH(nhg, nexthop->group) {
1021
0
                        if (nexthop_get_by_id(nexthop->manager, nhg->id, NULL) >= 0)
1022
0
                                continue;
1023
1024
0
                        assert_se(hashmap_remove(nexthop->group, UINT32_TO_PTR(nhg->id)) == nhg);
1025
0
                        free(nhg);
1026
0
                }
1027
1028
0
                if (!hashmap_isempty(nexthop->group))
1029
0
                        continue; /* At least one group member still exists. */
1030
1031
0
                nexthop_forget(link->manager, nexthop, "Forgetting silently removed");
1032
0
        }
1033
0
}
1034
1035
0
static int nexthop_update_group(NextHop *nexthop, sd_netlink_message *message) {
1036
0
        _cleanup_hashmap_free_ Hashmap *h = NULL;
1037
0
        _cleanup_free_ struct nexthop_grp *group = NULL;
1038
0
        size_t size = 0, n_group;
1039
0
        int r;
1040
1041
0
        assert(nexthop);
1042
0
        assert(message);
1043
1044
0
        r = sd_netlink_message_read_data(message, NHA_GROUP, &size, (void**) &group);
1045
0
        if (r < 0 && r != -ENODATA)
1046
0
                return log_debug_errno(r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
1047
1048
0
        nexthop_detach_from_group_members(nexthop);
1049
1050
0
        if (size % sizeof(struct nexthop_grp) != 0)
1051
0
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
1052
0
                                       "rtnl: received nexthop message with invalid nexthop group size, ignoring.");
1053
1054
0
        if ((uintptr_t) group % alignof(struct nexthop_grp) != 0)
1055
0
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
1056
0
                                       "rtnl: received nexthop message with invalid alignment, ignoring.");
1057
1058
0
        n_group = size / sizeof(struct nexthop_grp);
1059
0
        for (size_t i = 0; i < n_group; i++) {
1060
0
                _cleanup_free_ struct nexthop_grp *nhg = NULL;
1061
1062
0
                if (group[i].id == 0) {
1063
0
                        log_debug("rtnl: received nexthop message with invalid ID in group, ignoring.");
1064
0
                        continue;
1065
0
                }
1066
1067
0
                if (group[i].weight > 254) {
1068
0
                        log_debug("rtnl: received nexthop message with invalid weight in group, ignoring.");
1069
0
                        continue;
1070
0
                }
1071
1072
0
                nhg = newdup(struct nexthop_grp, group + i, 1);
1073
0
                if (!nhg)
1074
0
                        return log_oom();
1075
1076
0
                r = hashmap_ensure_put(&h, &trivial_hash_ops_value_free, UINT32_TO_PTR(nhg->id), nhg);
1077
0
                if (r == -ENOMEM)
1078
0
                        return log_oom();
1079
0
                if (r < 0) {
1080
0
                        log_debug_errno(r, "Failed to store nexthop group, ignoring: %m");
1081
0
                        continue;
1082
0
                }
1083
0
                if (r > 0)
1084
0
                        TAKE_PTR(nhg);
1085
0
        }
1086
1087
0
        hashmap_free_and_replace(nexthop->group, h);
1088
0
        nexthop_attach_to_group_members(nexthop);
1089
0
        return 0;
1090
0
}
1091
1092
0
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
1093
0
        int r;
1094
1095
0
        assert(rtnl);
1096
0
        assert(message);
1097
0
        assert(m);
1098
1099
0
        if (sd_netlink_message_is_error(message)) {
1100
0
                r = sd_netlink_message_get_errno(message);
1101
0
                if (r < 0)
1102
0
                        log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring");
1103
1104
0
                return 0;
1105
0
        }
1106
1107
0
        uint16_t type;
1108
0
        r = sd_netlink_message_get_type(message, &type);
1109
0
        if (r < 0) {
1110
0
                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
1111
0
                return 0;
1112
0
        } else if (!IN_SET(type, RTM_NEWNEXTHOP, RTM_DELNEXTHOP)) {
1113
0
                log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type);
1114
0
                return 0;
1115
0
        }
1116
1117
0
        uint32_t id;
1118
0
        r = sd_netlink_message_read_u32(message, NHA_ID, &id);
1119
0
        if (r == -ENODATA) {
1120
0
                log_warning_errno(r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m");
1121
0
                return 0;
1122
0
        } else if (r < 0) {
1123
0
                log_warning_errno(r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
1124
0
                return 0;
1125
0
        } else if (id == 0) {
1126
0
                log_warning("rtnl: received nexthop message with invalid nexthop ID, ignoring.");
1127
0
                return 0;
1128
0
        }
1129
1130
0
        NextHop *nexthop = NULL;
1131
0
        (void) nexthop_get_by_id(m, id, &nexthop);
1132
1133
0
        if (type == RTM_DELNEXTHOP) {
1134
0
                if (nexthop)
1135
0
                        nexthop_forget(m, nexthop, "Forgetting removed");
1136
0
                else
1137
0
                        log_nexthop_debug(&(const NextHop) { .id = id }, "Kernel removed unknown", m);
1138
1139
0
                return 0;
1140
0
        }
1141
1142
0
        Request *req = NULL;
1143
0
        (void) nexthop_get_request_by_id(m, id, &req);
1144
1145
        /* If we did not know the nexthop, then save it. */
1146
0
        bool is_new = false;
1147
0
        if (!nexthop) {
1148
0
                if (!req && !m->manage_foreign_nexthops) {
1149
0
                        log_nexthop_debug(&(const NextHop) { .id = id }, "Ignoring received", m);
1150
0
                        return 0;
1151
0
                }
1152
1153
0
                r = nexthop_add_new(m, id, &nexthop);
1154
0
                if (r < 0) {
1155
0
                        log_warning_errno(r, "Failed to add received nexthop, ignoring: %m");
1156
0
                        return 0;
1157
0
                }
1158
1159
0
                is_new = true;
1160
0
        }
1161
1162
        /* Also update information that cannot be obtained through netlink notification. */
1163
0
        if (req && req->waiting_reply) {
1164
0
                NextHop *n = ASSERT_PTR(req->userdata);
1165
1166
0
                nexthop->source = n->source;
1167
0
                nexthop->provider = n->provider;
1168
0
        }
1169
1170
0
        r = sd_rtnl_message_get_family(message, &nexthop->family);
1171
0
        if (r < 0)
1172
0
                log_debug_errno(r, "rtnl: could not get nexthop family, ignoring: %m");
1173
1174
0
        r = sd_rtnl_message_nexthop_get_protocol(message, &nexthop->protocol);
1175
0
        if (r < 0)
1176
0
                log_debug_errno(r, "rtnl: could not get nexthop protocol, ignoring: %m");
1177
1178
0
        r = sd_rtnl_message_nexthop_get_flags(message, &nexthop->flags);
1179
0
        if (r < 0)
1180
0
                log_debug_errno(r, "rtnl: could not get nexthop flags, ignoring: %m");
1181
1182
0
        (void) nexthop_update_group(nexthop, message);
1183
1184
0
        if (nexthop->family != AF_UNSPEC) {
1185
0
                r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, nexthop->family, &nexthop->gw.address);
1186
0
                if (r == -ENODATA)
1187
0
                        nexthop->gw.address = IN_ADDR_NULL;
1188
0
                else if (r < 0)
1189
0
                        log_debug_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
1190
0
        }
1191
1192
0
        r = sd_netlink_message_has_flag(message, NHA_BLACKHOLE);
1193
0
        if (r < 0)
1194
0
                log_debug_errno(r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m");
1195
0
        else
1196
0
                nexthop->blackhole = r;
1197
1198
0
        uint32_t ifindex;
1199
0
        r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex);
1200
0
        if (r == -ENODATA)
1201
0
                nexthop->ifindex = 0;
1202
0
        else if (r < 0)
1203
0
                log_debug_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
1204
0
        else if (ifindex > INT32_MAX)
1205
0
                log_debug_errno(r, "rtnl: received invalid NHA_OIF attribute, ignoring: %m");
1206
0
        else
1207
0
                nexthop->ifindex = (int) ifindex;
1208
1209
        /* The linux kernel does not set NHA_OID attribute when NHA_BLACKHOLE or NHA_GROUP is set.
1210
         * But let's check that for safety. */
1211
0
        if (!nexthop_bound_to_link(nexthop) && nexthop->ifindex != 0) {
1212
0
                log_debug("rtnl: received blackhole or group nexthop with NHA_OIF attribute, ignoring the attribute.");
1213
0
                nexthop->ifindex = 0;
1214
0
        }
1215
1216
0
        nexthop_enter_configured(nexthop);
1217
0
        if (req)
1218
0
                nexthop_enter_configured(req->userdata);
1219
1220
0
        log_nexthop_debug(nexthop, is_new ? "Remembering" : "Received remembered", m);
1221
0
        return 1;
1222
0
}
1223
1224
#define log_nexthop_section(nexthop, fmt, ...)                          \
1225
1.36k
        ({                                                              \
1226
1.36k
                const NextHop *_nexthop = (nexthop);                    \
1227
1.36k
                log_section_warning_errno(                              \
1228
1.36k
                                _nexthop ? _nexthop->section : NULL,    \
1229
1.36k
                                SYNTHETIC_ERRNO(EINVAL),                \
1230
1.36k
                                fmt " Ignoring [NextHop] section.",     \
1231
1.36k
                                ##__VA_ARGS__);                         \
1232
1.36k
        })
1233
1234
5.25k
static int nexthop_section_verify(NextHop *nh) {
1235
5.25k
        if (section_is_invalid(nh->section))
1236
434
                return -EINVAL;
1237
1238
4.81k
        if (!nh->network->manager->manage_foreign_nexthops && nh->id == 0)
1239
0
                return log_nexthop_section(nh, "Nexthop without specifying Id= is not supported if ManageForeignNextHops=no is set in networkd.conf.");
1240
1241
4.81k
        if (nh->family == AF_UNSPEC)
1242
4.22k
                nh->family = nh->gw.family;
1243
589
        else if (nh->gw.family != AF_UNSPEC && nh->gw.family != nh->family)
1244
194
                return log_nexthop_section(nh, "Family= and Gateway= settings for nexthop contradict each other.");
1245
1246
4.62k
        assert(nh->gw.family == nh->family || nh->gw.family == AF_UNSPEC);
1247
1248
4.62k
        if (!hashmap_isempty(nh->group)) {
1249
2.42k
                if (in_addr_is_set(nh->family, &nh->gw.address))
1250
194
                        return log_nexthop_section(nh, "Nexthop group cannot have gateway address.");
1251
1252
2.22k
                if (nh->family != AF_UNSPEC)
1253
194
                        return log_nexthop_section(nh, "Nexthop group cannot have Family= setting.");
1254
1255
2.03k
                if (nh->blackhole)
1256
197
                        return log_nexthop_section(nh, "Nexthop group cannot be a blackhole.");
1257
1258
1.83k
                if (nh->onlink > 0)
1259
194
                        return log_nexthop_section(nh, "Nexthop group cannot have on-link flag.");
1260
1261
2.20k
        } else if (nh->family == AF_UNSPEC)
1262
                /* When neither Family=, Gateway=, nor Group= is specified, assume IPv4. */
1263
1.18k
                nh->family = AF_INET;
1264
1265
3.84k
        if (nh->blackhole) {
1266
633
                if (in_addr_is_set(nh->family, &nh->gw.address))
1267
194
                        return log_nexthop_section(nh, "Blackhole nexthop cannot have gateway address.");
1268
1269
439
                if (nh->onlink > 0)
1270
200
                        return log_nexthop_section(nh, "Blackhole nexthop cannot have on-link flag.");
1271
439
        }
1272
1273
3.45k
        if (nh->onlink < 0 && in_addr_is_set(nh->family, &nh->gw.address) &&
1274
597
            ordered_hashmap_isempty(nh->network->addresses_by_section)) {
1275
                /* If no address is configured, in most cases the gateway cannot be reachable.
1276
                 * TODO: we may need to improve the condition above. */
1277
403
                log_section_warning(nh->section, "Nexthop with Gateway= specified, but no static address configured. Enabling OnLink= option.");
1278
403
                nh->onlink = true;
1279
403
        }
1280
1281
3.45k
        if (nh->onlink >= 0)
1282
601
                SET_FLAG(nh->flags, RTNH_F_ONLINK, nh->onlink);
1283
1284
3.45k
        return 0;
1285
3.84k
}
1286
1287
2.86k
int network_drop_invalid_nexthops(Network *network) {
1288
2.86k
        _cleanup_hashmap_free_ Hashmap *nexthops = NULL;
1289
2.86k
        _cleanup_set_free_ Set *duplicated_nexthops = NULL;
1290
2.86k
        NextHop *nh;
1291
2.86k
        int r;
1292
1293
2.86k
        assert(network);
1294
1295
5.25k
        ORDERED_HASHMAP_FOREACH(nh, network->nexthops_by_section) {
1296
5.25k
                if (nexthop_section_verify(nh) < 0) {
1297
1.80k
                        nexthop_detach(nh);
1298
1.80k
                        continue;
1299
1.80k
                }
1300
1301
3.45k
                if (nh->id == 0)
1302
3.10k
                        continue;
1303
1304
                /* Always use the setting specified later. So, remove the previously assigned setting. */
1305
343
                NextHop *dup = hashmap_remove(nexthops, UINT32_TO_PTR(nh->id));
1306
343
                if (dup) {
1307
298
                        log_warning("%s: Duplicated nexthop settings for ID %"PRIu32" is specified at line %u and %u, "
1308
298
                                    "dropping the nexthop setting specified at line %u.",
1309
298
                                    dup->section->filename,
1310
298
                                    nh->id, nh->section->line,
1311
298
                                    dup->section->line, dup->section->line);
1312
1313
                        /* Do not call nexthop_detach() for 'dup' now, as we can remove only the current
1314
                         * entry in the loop. We will drop the nexthop from nexthops_by_section later. */
1315
298
                        r = set_ensure_put(&duplicated_nexthops, &nexthop_hash_ops, dup);
1316
298
                        if (r < 0)
1317
0
                                return log_oom();
1318
298
                        assert(r > 0);
1319
298
                }
1320
1321
343
                r = hashmap_ensure_put(&nexthops, NULL, UINT32_TO_PTR(nh->id), nh);
1322
343
                if (r < 0)
1323
0
                        return log_oom();
1324
343
                assert(r > 0);
1325
343
        }
1326
1327
2.86k
        return 0;
1328
2.86k
}
1329
1330
0
int manager_build_nexthop_ids(Manager *manager) {
1331
0
        Network *network;
1332
0
        int r;
1333
1334
0
        assert(manager);
1335
1336
0
        if (!manager->manage_foreign_nexthops)
1337
0
                return 0;
1338
1339
0
        manager->nexthop_ids = set_free(manager->nexthop_ids);
1340
1341
0
        ORDERED_HASHMAP_FOREACH(network, manager->networks) {
1342
0
                NextHop *nh;
1343
1344
0
                ORDERED_HASHMAP_FOREACH(nh, network->nexthops_by_section) {
1345
0
                        if (nh->id == 0)
1346
0
                                continue;
1347
1348
0
                        r = set_ensure_put(&manager->nexthop_ids, NULL, UINT32_TO_PTR(nh->id));
1349
0
                        if (r < 0)
1350
0
                                return r;
1351
0
                }
1352
0
        }
1353
1354
0
        return 0;
1355
0
}
1356
1357
static int config_parse_nexthop_family(
1358
                const char *unit,
1359
                const char *filename,
1360
                unsigned line,
1361
                const char *section,
1362
                unsigned section_line,
1363
                const char *lvalue,
1364
                int ltype,
1365
                const char *rvalue,
1366
                void *data,
1367
1.44k
                void *userdata) {
1368
1369
1.44k
        int *family = ASSERT_PTR(data);
1370
1371
1.44k
        if (isempty(rvalue))
1372
195
                *family = AF_UNSPEC;
1373
1.25k
        else if (streq(rvalue, "ipv4"))
1374
198
                *family = AF_INET;
1375
1.05k
        else if (streq(rvalue, "ipv6"))
1376
810
                *family = AF_INET6;
1377
245
        else
1378
245
                return log_syntax_parse_error(unit, filename, line, 0, lvalue, rvalue);
1379
1380
1.20k
        return 1;
1381
1.44k
}
1382
1383
static int config_parse_nexthop_group(
1384
                const char *unit,
1385
                const char *filename,
1386
                unsigned line,
1387
                const char *section,
1388
                unsigned section_line,
1389
                const char *lvalue,
1390
                int ltype,
1391
                const char *rvalue,
1392
                void *data,
1393
8.90k
                void *userdata) {
1394
1395
8.90k
        Hashmap **group = ASSERT_PTR(data);
1396
8.90k
        int r;
1397
1398
8.90k
        if (isempty(rvalue)) {
1399
238
                *group = hashmap_free(*group);
1400
238
                return 1;
1401
238
        }
1402
1403
29.8k
        for (const char *p = rvalue;;) {
1404
29.8k
                _cleanup_free_ struct nexthop_grp *nhg = NULL;
1405
29.8k
                _cleanup_free_ char *word = NULL;
1406
29.8k
                uint32_t w;
1407
29.8k
                char *sep;
1408
1409
29.8k
                r = extract_first_word(&p, &word, NULL, 0);
1410
29.8k
                if (r < 0)
1411
194
                        return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
1412
29.6k
                if (r == 0)
1413
8.46k
                        return 1;
1414
1415
21.2k
                nhg = new0(struct nexthop_grp, 1);
1416
21.2k
                if (!nhg)
1417
0
                        return log_oom();
1418
1419
21.2k
                sep = strchr(word, ':');
1420
21.2k
                if (sep) {
1421
1.17k
                        *sep++ = '\0';
1422
1.17k
                        r = safe_atou32(sep, &w);
1423
1.17k
                        if (r < 0) {
1424
230
                                log_syntax(unit, LOG_WARNING, filename, line, r,
1425
230
                                           "Failed to parse weight for nexthop group, ignoring assignment: %s:%s",
1426
230
                                           word, sep);
1427
230
                                continue;
1428
230
                        }
1429
946
                        if (w == 0 || w > 256) {
1430
678
                                log_syntax(unit, LOG_WARNING, filename, line, 0,
1431
678
                                           "Invalid weight for nexthop group, ignoring assignment: %s:%s",
1432
678
                                           word, sep);
1433
678
                                continue;
1434
678
                        }
1435
                        /* See comments in config_parse_multipath_route(). */
1436
268
                        nhg->weight = w - 1;
1437
268
                }
1438
1439
20.3k
                r = safe_atou32(word, &nhg->id);
1440
20.3k
                if (r < 0) {
1441
1.76k
                        log_syntax(unit, LOG_WARNING, filename, line, r,
1442
1.76k
                                   "Failed to parse nexthop ID in %s=, ignoring assignment: %s%s%s",
1443
1.76k
                                   lvalue, word, sep ? ":" : "", strempty(sep));
1444
1.76k
                        continue;
1445
1.76k
                }
1446
18.5k
                if (nhg->id == 0) {
1447
479
                        log_syntax(unit, LOG_WARNING, filename, line, 0,
1448
479
                                   "Nexthop ID in %s= must be positive, ignoring assignment: %s%s%s",
1449
479
                                   lvalue, word, sep ? ":" : "", strempty(sep));
1450
479
                        continue;
1451
479
                }
1452
1453
18.0k
                r = hashmap_ensure_put(group, &trivial_hash_ops_value_free, UINT32_TO_PTR(nhg->id), nhg);
1454
18.0k
                if (r == -ENOMEM)
1455
0
                        return log_oom();
1456
18.0k
                if (r == -EEXIST) {
1457
1.52k
                        log_syntax(unit, LOG_WARNING, filename, line, r,
1458
1.52k
                                   "Nexthop ID %"PRIu32" is specified multiple times in %s=, ignoring assignment: %s%s%s",
1459
1.52k
                                   nhg->id, lvalue, word, sep ? ":" : "", strempty(sep));
1460
1.52k
                        continue;
1461
1.52k
                }
1462
16.5k
                assert(r > 0);
1463
16.5k
                TAKE_PTR(nhg);
1464
16.5k
        }
1465
8.66k
}
1466
1467
int config_parse_nexthop_section(
1468
                const char *unit,
1469
                const char *filename,
1470
                unsigned line,
1471
                const char *section,
1472
                unsigned section_line,
1473
                const char *lvalue,
1474
                int ltype,
1475
                const char *rvalue,
1476
                void *data,
1477
15.5k
                void *userdata) {
1478
1479
15.5k
        static const ConfigSectionParser table[_NEXTHOP_CONF_PARSER_MAX] = {
1480
15.5k
                [NEXTHOP_ID]        = { .parser = config_parse_uint32,         .ltype = 0, .offset = offsetof(NextHop, id),        },
1481
15.5k
                [NEXTHOP_GATEWAY]   = { .parser = config_parse_in_addr_data,   .ltype = 0, .offset = offsetof(NextHop, gw),        },
1482
15.5k
                [NEXTHOP_FAMILY]    = { .parser = config_parse_nexthop_family, .ltype = 0, .offset = offsetof(NextHop, family),    },
1483
15.5k
                [NEXTHOP_ONLINK]    = { .parser = config_parse_tristate,       .ltype = 0, .offset = offsetof(NextHop, onlink),    },
1484
15.5k
                [NEXTHOP_BLACKHOLE] = { .parser = config_parse_bool,           .ltype = 0, .offset = offsetof(NextHop, blackhole), },
1485
15.5k
                [NEXTHOP_GROUP]     = { .parser = config_parse_nexthop_group,  .ltype = 0, .offset = offsetof(NextHop, group),     },
1486
15.5k
        };
1487
1488
15.5k
        _cleanup_(nexthop_unref_or_set_invalidp) NextHop *nexthop = NULL;
1489
15.5k
        Network *network = ASSERT_PTR(userdata);
1490
15.5k
        int r;
1491
1492
15.5k
        assert(filename);
1493
1494
15.5k
        r = nexthop_new_static(network, filename, section_line, &nexthop);
1495
15.5k
        if (r < 0)
1496
0
                return log_oom();
1497
1498
15.5k
        r = config_section_parse(table, ELEMENTSOF(table),
1499
15.5k
                                 unit, filename, line, section, section_line, lvalue, ltype, rvalue, nexthop);
1500
15.5k
        if (r <= 0) /* 0 means non-critical error, but the section will be ignored. */
1501
1.50k
                return r;
1502
1503
14.0k
        TAKE_PTR(nexthop);
1504
14.0k
        return 0;
1505
15.5k
}