• 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

83.83
/src/network/networkd-route.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <linux/ipv6_route.h>
4
#include <net/if.h>
5
#include <stdio.h>
6

7
#include "sd-ndisc-protocol.h"
8
#include "sd-netlink.h"
9

10
#include "alloc-util.h"
11
#include "conf-parser.h"
12
#include "errno-util.h"
13
#include "event-util.h"
14
#include "netlink-util.h"
15
#include "networkd-address.h"
16
#include "networkd-ipv4ll.h"
17
#include "networkd-link.h"
18
#include "networkd-manager.h"
19
#include "networkd-network.h"
20
#include "networkd-nexthop.h"
21
#include "networkd-queue.h"
22
#include "networkd-route.h"
23
#include "networkd-route-util.h"
24
#include "ordered-set.h"
25
#include "parse-util.h"
26
#include "set.h"
27
#include "siphash24.h"
28
#include "string-util.h"
29
#include "vrf.h"
30
#include "wireguard.h"
31

32
static Route* route_detach_impl(Route *route) {
24,292✔
33
        assert(route);
24,292✔
34
        assert(!!route->network + !!route->manager + !!route->wireguard <= 1);
24,292✔
35

36
        if (route->network) {
24,292✔
37
                assert(route->section);
472✔
38
                hashmap_remove(route->network->routes_by_section, route->section);
472✔
39
                route->network = NULL;
472✔
40
                return route;
472✔
41
        }
42

43
        if (route->manager) {
23,820✔
44
                route_detach_from_nexthop(route);
7,869✔
45
                set_remove(route->manager->routes, route);
7,869✔
46
                route->manager = NULL;
7,869✔
47
                return route;
7,869✔
48
        }
49

50
        if (route->wireguard) {
15,951✔
51
                set_remove(route->wireguard->routes, route);
96✔
52
                route->wireguard = NULL;
96✔
53
                return route;
96✔
54
        }
55

56
        return NULL;
57
}
58

59
void route_detach(Route *route) {
8,437✔
60
        route_unref(route_detach_impl(route));
8,437✔
61
}
8,437✔
62

63
static Route* route_free(Route *route) {
15,855✔
64
        if (!route)
15,855✔
65
                return NULL;
66

67
        route_detach_impl(route);
15,855✔
68

69
        config_section_free(route->section);
15,855✔
70
        route_nexthops_done(route);
15,855✔
71
        route_metric_done(&route->metric);
15,855✔
72
        sd_event_source_disable_unref(route->expire);
15,855✔
73

74
        return mfree(route);
15,855✔
75
}
76

77
DEFINE_TRIVIAL_REF_UNREF_FUNC(Route, route, route_free);
32,642✔
78

79
static void route_hash_func(const Route *route, struct siphash *state) {
57,822✔
80
        assert(route);
57,822✔
81

82
        siphash24_compress_typesafe(route->family, state);
57,822✔
83

84
        switch (route->family) {
57,822✔
85
        case AF_INET:
27,593✔
86
                /* First, the table, destination prefix, priority, and tos (dscp), are used to find routes.
87
                 * See fib_table_insert(), fib_find_node(), and fib_find_alias() in net/ipv4/fib_trie.c of the kernel. */
88
                siphash24_compress_typesafe(route->table, state);
27,593✔
89
                in_addr_hash_func(&route->dst, route->family, state);
27,593✔
90
                siphash24_compress_typesafe(route->dst_prefixlen, state);
27,593✔
91
                siphash24_compress_typesafe(route->priority, state);
27,593✔
92
                siphash24_compress_typesafe(route->tos, state);
27,593✔
93

94
                /* Then, protocol, scope, type, flags, prefsrc, metrics (RTAX_* attributes), and nexthops (gateways)
95
                 * are used to find routes. See fib_find_info() in net/ipv4/fib_semantics.c of the kernel. */
96
                siphash24_compress_typesafe(route->protocol, state);
27,593✔
97
                siphash24_compress_typesafe(route->scope, state);
27,593✔
98
                siphash24_compress_typesafe(route->type, state);
27,593✔
99
                unsigned flags = route->flags & ~RTNH_COMPARE_MASK;
27,593✔
100
                siphash24_compress_typesafe(flags, state);
27,593✔
101
                in_addr_hash_func(&route->prefsrc, route->family, state);
27,593✔
102

103
                /* nexthops (id, number of nexthops, nexthop) */
104
                route_nexthops_hash_func(route, state);
27,593✔
105

106
                /* metrics */
107
                route_metric_hash_func(&route->metric, state);
27,593✔
108
                break;
57,822✔
109

110
        case AF_INET6:
30,229✔
111
                /* First, table and destination prefix are used for classifying routes.
112
                 * See fib6_add() and fib6_add_1() in net/ipv6/ip6_fib.c of the kernel. */
113
                siphash24_compress_typesafe(route->table, state);
30,229✔
114
                in_addr_hash_func(&route->dst, route->family, state);
30,229✔
115
                siphash24_compress_typesafe(route->dst_prefixlen, state);
30,229✔
116

117
                /* Then, source prefix is used. See fib6_add(). */
118
                in_addr_hash_func(&route->src, route->family, state);
30,229✔
119
                siphash24_compress_typesafe(route->src_prefixlen, state);
30,229✔
120

121
                /* See fib6_add_rt2node(). */
122
                siphash24_compress_typesafe(route->priority, state);
30,229✔
123

124
                /* See rt6_duplicate_nexthop() in include/net/ip6_route.h of the kernel.
125
                 * Here, we hash nexthop in a similar way as the one for IPv4. */
126
                route_nexthops_hash_func(route, state);
30,229✔
127

128
                /* Unlike IPv4 routes, metrics are not taken into account. */
129
                break;
130

131
        default:
×
132
                assert_not_reached();
×
133
        }
134
}
57,822✔
135

136
static int route_compare_func(const Route *a, const Route *b) {
28,908✔
137
        int r;
28,908✔
138

139
        r = CMP(a->family, b->family);
28,908✔
140
        if (r != 0)
24,932✔
141
                return r;
6,272✔
142

143
        switch (a->family) {
22,636✔
144
        case AF_INET:
10,090✔
145
                r = CMP(a->table, b->table);
10,090✔
146
                if (r != 0)
9,659✔
147
                        return r;
1,585✔
148

149
                r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
8,505✔
150
                if (r != 0)
8,505✔
151
                        return r;
152

153
                r = CMP(a->dst_prefixlen, b->dst_prefixlen);
5,912✔
154
                if (r != 0)
5,912✔
155
                        return r;
×
156

157
                r = CMP(a->priority, b->priority);
5,912✔
158
                if (r != 0)
5,902✔
159
                        return r;
10✔
160

161
                r = CMP(a->tos, b->tos);
5,902✔
162
                if (r != 0)
5,902✔
163
                        return r;
×
164

165
                r = CMP(a->protocol, b->protocol);
5,902✔
166
                if (r != 0)
5,902✔
167
                        return r;
1✔
168

169
                r = CMP(a->scope, b->scope);
5,901✔
170
                if (r != 0)
5,901✔
UNCOV
171
                        return r;
×
172

173
                r = CMP(a->type, b->type);
5,901✔
174
                if (r != 0)
5,895✔
175
                        return r;
6✔
176

177
                r = CMP(a->flags & ~RTNH_COMPARE_MASK, b->flags & ~RTNH_COMPARE_MASK);
5,895✔
178
                if (r != 0)
5,895✔
179
                        return r;
×
180

181
                r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
5,895✔
182
                if (r != 0)
5,895✔
183
                        return r;
184

185
                r = route_nexthops_compare_func(a, b);
5,879✔
186
                if (r != 0)
5,879✔
187
                        return r;
188

189
                return route_metric_compare_func(&a->metric, &b->metric);
5,877✔
190

191
        case AF_INET6:
12,546✔
192
                r = CMP(a->table, b->table);
12,546✔
193
                if (r != 0)
12,067✔
194
                        return r;
1,808✔
195

196
                r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
10,738✔
197
                if (r != 0)
10,738✔
198
                        return r;
199

200
                r = CMP(a->dst_prefixlen, b->dst_prefixlen);
8,936✔
201
                if (r != 0)
8,936✔
202
                        return r;
×
203

204
                r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
8,936✔
205
                if (r != 0)
8,936✔
206
                        return r;
207

208
                r = CMP(a->src_prefixlen, b->src_prefixlen);
8,936✔
209
                if (r != 0)
8,936✔
210
                        return r;
×
211

212
                r = CMP(a->priority, b->priority);
8,936✔
213
                if (r != 0)
8,902✔
214
                        return r;
48✔
215

216
                return route_nexthops_compare_func(a, b);
8,888✔
217

218
        default:
×
219
                assert_not_reached();
×
220
        }
221
}
222

223
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
2,391✔
224
                route_hash_ops,
225
                Route,
226
                route_hash_func,
227
                route_compare_func,
228
                route_detach);
229

230
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
231
                route_hash_ops_unref,
232
                Route,
233
                route_hash_func,
234
                route_compare_func,
235
                route_unref);
236

237
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
472✔
238
                route_section_hash_ops,
239
                ConfigSection,
240
                config_section_hash_func,
241
                config_section_compare_func,
242
                Route,
243
                route_detach);
244

245
int route_new(Route **ret) {
13,512✔
246
        _cleanup_(route_unrefp) Route *route = NULL;
13,512✔
247

248
        route = new(Route, 1);
13,512✔
249
        if (!route)
13,512✔
250
                return -ENOMEM;
251

252
        *route = (Route) {
13,512✔
253
                .n_ref = 1,
254
                .family = AF_UNSPEC,
255
                .scope = RT_SCOPE_UNIVERSE,
256
                .protocol = RTPROT_UNSPEC,
257
                .type = RTN_UNICAST,
258
                .table = RT_TABLE_MAIN,
259
                .lifetime_usec = USEC_INFINITY,
260
                .gateway_onlink = -1,
261
        };
262

263
        *ret = TAKE_PTR(route);
13,512✔
264

265
        return 0;
13,512✔
266
}
267

268
int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
904✔
269
        _cleanup_(config_section_freep) ConfigSection *n = NULL;
904✔
270
        _cleanup_(route_unrefp) Route *route = NULL;
904✔
271
        int r;
904✔
272

273
        assert(network);
904✔
274
        assert(ret);
904✔
275
        assert(filename);
904✔
276
        assert(section_line > 0);
904✔
277

278
        r = config_section_new(filename, section_line, &n);
904✔
279
        if (r < 0)
904✔
280
                return r;
281

282
        route = hashmap_get(network->routes_by_section, n);
904✔
283
        if (route) {
904✔
284
                *ret = TAKE_PTR(route);
432✔
285
                return 0;
432✔
286
        }
287

288
        if (hashmap_size(network->routes_by_section) >= routes_max())
472✔
289
                return -E2BIG;
290

291
        r = route_new(&route);
472✔
292
        if (r < 0)
472✔
293
                return r;
294

295
        route->protocol = RTPROT_STATIC;
472✔
296
        route->network = network;
472✔
297
        route->section = TAKE_PTR(n);
472✔
298
        route->source = NETWORK_CONFIG_SOURCE_STATIC;
472✔
299

300
        r = hashmap_ensure_put(&network->routes_by_section, &route_section_hash_ops, route->section, route);
472✔
301
        if (r < 0)
472✔
302
                return r;
303

304
        *ret = TAKE_PTR(route);
472✔
305
        return 0;
472✔
306
}
307

308
static int route_attach(Manager *manager, Route *route) {
7,869✔
309
        int r;
7,869✔
310

311
        assert(manager);
7,869✔
312
        assert(route);
7,869✔
313
        assert(!route->network);
7,869✔
314
        assert(!route->wireguard);
7,869✔
315

316
        r = set_ensure_put(&manager->routes, &route_hash_ops, route);
7,869✔
317
        if (r < 0)
7,869✔
318
                return r;
319
        if (r == 0)
7,869✔
320
                return -EEXIST;
321

322
        route->manager = manager;
7,869✔
323
        return 0;
7,869✔
324
}
325

326
int route_get(Manager *manager, const Route *route, Route **ret) {
17,757✔
327
        Route *existing;
17,757✔
328

329
        assert(manager);
17,757✔
330
        assert(route);
17,757✔
331

332
        existing = set_get(manager->routes, route);
17,757✔
333
        if (!existing)
17,757✔
334
                return -ENOENT;
335

336
        if (ret)
6,274✔
337
                *ret = existing;
6,191✔
338

339
        return 0;
340
}
341

342
static int route_get_link(Manager *manager, const Route *route, Link **ret) {
30,369✔
343
        int r;
30,369✔
344

345
        assert(manager);
30,369✔
346
        assert(route);
30,369✔
347

348
        if (route->nexthop_id != 0) {
30,369✔
349
                NextHop *nh;
1,566✔
350

351
                r = nexthop_get_by_id(manager, route->nexthop_id, &nh);
1,566✔
352
                if (r < 0)
1,566✔
353
                        return r;
1,566✔
354

355
                return link_get_by_index(manager, nh->ifindex, ret);
1,441✔
356
        }
357

358
        return route_nexthop_get_link(manager, &route->nexthop, ret);
28,803✔
359
}
360

361
bool route_is_bound_to_link(const Route *route, Link *link) {
935✔
362
        assert(route);
935✔
363
        assert(link);
935✔
364
        assert(link->manager);
935✔
365

366
        Link *route_link;
935✔
367
        if (route_get_link(link->manager, route, &route_link) < 0)
935✔
368
                return false;
935✔
369

370
        return route_link->ifindex == link->ifindex;
911✔
371
}
372

373
int route_get_request(Manager *manager, const Route *route, Request **ret) {
14,732✔
374
        Request *req;
14,732✔
375

376
        assert(manager);
14,732✔
377
        assert(route);
14,732✔
378

379
        req = ordered_set_get(manager->request_queue,
29,464✔
380
                              &(const Request) {
14,732✔
381
                                      .type = REQUEST_TYPE_ROUTE,
382
                                      .userdata = (void*) route,
383
                                      .hash_func = (hash_func_t) route_hash_func,
384
                                      .compare_func = (compare_func_t) route_compare_func,
385
                              });
386
        if (!req)
14,732✔
387
                return -ENOENT;
14,732✔
388

389
        if (ret)
708✔
390
                *ret = req;
708✔
391
        return 0;
392
}
393

394
int route_dup(const Route *src, const RouteNextHop *nh, Route **ret) {
2,343✔
395
        _cleanup_(route_unrefp) Route *dest = NULL;
2,343✔
396
        int r;
2,343✔
397

398
        assert(src);
2,343✔
399
        assert(ret);
2,343✔
400

401
        dest = newdup(Route, src, 1);
2,343✔
402
        if (!dest)
2,343✔
403
                return -ENOMEM;
404

405
        /* Unset number of reference and all pointers */
406
        dest->n_ref = 1;
2,343✔
407
        dest->manager = NULL;
2,343✔
408
        dest->network = NULL;
2,343✔
409
        dest->wireguard = NULL;
2,343✔
410
        dest->section = NULL;
2,343✔
411
        dest->nexthop = ROUTE_NEXTHOP_NULL;
2,343✔
412
        dest->nexthops = NULL;
2,343✔
413
        dest->metric = ROUTE_METRIC_NULL;
2,343✔
414
        dest->expire = NULL;
2,343✔
415

416
        r = route_nexthops_copy(src, nh, dest);
2,343✔
417
        if (r < 0)
2,343✔
418
                return r;
419

420
        r = route_metric_copy(&src->metric, &dest->metric);
2,343✔
421
        if (r < 0)
2,343✔
422
                return r;
423

424
        *ret = TAKE_PTR(dest);
2,343✔
425
        return 0;
2,343✔
426
}
427

428
static int route_to_string(const Route *route, Manager *manager, char **ret) {
15,870✔
429
        _cleanup_free_ char *nexthop = NULL, *prefsrc = NULL,
×
430
                *table = NULL, *scope = NULL, *proto = NULL, *flags = NULL;
15,870✔
431
        const char *dst, *src;
15,870✔
432

433
        assert(route);
15,870✔
434
        assert(manager);
15,870✔
435
        assert(ret);
15,870✔
436

437
        dst = in_addr_is_set(route->family, &route->dst) || route->dst_prefixlen > 0 ?
17,188✔
438
                IN_ADDR_PREFIX_TO_STRING(route->family, &route->dst, route->dst_prefixlen) : NULL;
15,893✔
439
        src = in_addr_is_set(route->family, &route->src) || route->src_prefixlen > 0 ?
31,740✔
440
                IN_ADDR_PREFIX_TO_STRING(route->family, &route->src, route->src_prefixlen) : NULL;
15,870✔
441

442
        (void) route_nexthops_to_string(route, &nexthop);
15,870✔
443

444
        if (in_addr_is_set(route->family, &route->prefsrc))
15,870✔
445
                (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
7,018✔
446
        (void) route_scope_to_string_alloc(route->scope, &scope);
15,870✔
447
        (void) manager_get_route_table_to_string(manager, route->table, /* append_num= */ true, &table);
15,870✔
448
        (void) route_protocol_full_to_string_alloc(route->protocol, &proto);
15,870✔
449
        (void) route_flags_to_string_alloc(route->flags, &flags);
15,870✔
450

451
        if (asprintf(ret,
73,627✔
452
                     "dst: %s, src: %s, %s, prefsrc: %s, "
453
                     "table: %s, priority: %"PRIu32", "
454
                     "proto: %s, scope: %s, "
455
                     "type: %s, flags: %s",
456
                     strna(dst), strna(src), strna(nexthop), strna(prefsrc),
457
                     strna(table), route->priority,
15,870✔
458
                     strna(proto), strna(scope),
459
                     strna(route_type_to_string(route->type)), strna(flags)) < 0)
15,870✔
460
                return -ENOMEM;
×
461

462
        return 0;
463
}
464

465
void log_route_debug(const Route *route, const char *str, Manager *manager) {
16,271✔
466
        _cleanup_free_ char *state = NULL, *route_str = NULL;
15,870✔
467
        Link *link = NULL;
16,271✔
468

469
        assert(route);
16,271✔
470
        assert(str);
16,271✔
471
        assert(manager);
16,271✔
472

473
        if (!DEBUG_LOGGING)
16,271✔
474
                return;
401✔
475

476
        (void) route_get_link(manager, route, &link);
15,870✔
477
        (void) network_config_state_to_string_alloc(route->state, &state);
15,870✔
478
        (void) route_to_string(route, manager, &route_str);
15,870✔
479

480
        log_link_debug(link, "%s %s route (%s): %s",
22,478✔
481
                       str, strna(network_config_source_to_string(route->source)), strna(state), strna(route_str));
482
}
483

484
static void route_forget(Manager *manager, Route *route, const char *msg) {
5,731✔
485
        assert(manager);
5,731✔
486
        assert(route);
5,731✔
487
        assert(msg);
5,731✔
488

489
        Request *req;
5,731✔
490
        if (route_get_request(manager, route, &req) >= 0)
5,731✔
491
                route_enter_removed(req->userdata);
2✔
492

493
        if (!route->manager && route_get(manager, route, &route) < 0)
5,731✔
494
                return;
166✔
495

496
        route_enter_removed(route);
5,565✔
497
        log_route_debug(route, msg, manager);
5,565✔
498
        route_detach(route);
5,565✔
499
}
500

501
static int route_set_netlink_message(const Route *route, sd_netlink_message *m) {
1,296✔
502
        int r;
1,296✔
503

504
        assert(route);
1,296✔
505
        assert(m);
1,296✔
506

507
        /* rtmsg header (and relevant attributes) */
508
        if (route->dst_prefixlen > 0) {
1,296✔
509
                r = netlink_message_append_in_addr_union(m, RTA_DST, route->family, &route->dst);
913✔
510
                if (r < 0)
913✔
511
                        return r;
512

513
                r = sd_rtnl_message_route_set_dst_prefixlen(m, route->dst_prefixlen);
913✔
514
                if (r < 0)
913✔
515
                        return r;
516
        }
517

518
        if (route->src_prefixlen > 0) {
1,296✔
519
                r = netlink_message_append_in_addr_union(m, RTA_SRC, route->family, &route->src);
×
520
                if (r < 0)
×
521
                        return r;
522

523
                r = sd_rtnl_message_route_set_src_prefixlen(m, route->src_prefixlen);
×
524
                if (r < 0)
×
525
                        return r;
526
        }
527

528
        r = sd_rtnl_message_route_set_tos(m, route->tos);
1,296✔
529
        if (r < 0)
1,296✔
530
                return r;
531

532
        r = sd_rtnl_message_route_set_scope(m, route->scope);
1,296✔
533
        if (r < 0)
1,296✔
534
                return r;
535

536
        r = sd_rtnl_message_route_set_type(m, route->type);
1,296✔
537
        if (r < 0)
1,296✔
538
                return r;
539

540
        r = sd_rtnl_message_route_set_flags(m, route->flags & ~RTNH_COMPARE_MASK);
1,296✔
541
        if (r < 0)
1,296✔
542
                return r;
543

544
        /* attributes */
545
        r = sd_netlink_message_append_u32(m, RTA_PRIORITY, route->priority);
1,296✔
546
        if (r < 0)
1,296✔
547
                return r;
548

549
        if (in_addr_is_set(route->family, &route->prefsrc)) {
1,296✔
550
                r = netlink_message_append_in_addr_union(m, RTA_PREFSRC, route->family, &route->prefsrc);
331✔
551
                if (r < 0)
331✔
552
                        return r;
553
        }
554

555
        if (route->table < 256) {
1,296✔
556
                r = sd_rtnl_message_route_set_table(m, route->table);
1,153✔
557
                if (r < 0)
1,153✔
558
                        return r;
559
        } else {
560
                r = sd_rtnl_message_route_set_table(m, RT_TABLE_UNSPEC);
143✔
561
                if (r < 0)
143✔
562
                        return r;
563

564
                /* Table attribute to allow more than 256. */
565
                r = sd_netlink_message_append_u32(m, RTA_TABLE, route->table);
143✔
566
                if (r < 0)
143✔
567
                        return r;
568
        }
569

570
        r = sd_netlink_message_append_u8(m, RTA_PREF, route->pref);
1,296✔
571
        if (r < 0)
1,296✔
572
                return r;
573

574
        /* nexthops */
575
        r = route_nexthops_set_netlink_message(route, m);
1,296✔
576
        if (r < 0)
1,296✔
577
                return r;
578

579
        /* metrics */
580
        r = route_metric_set_netlink_message(&route->metric, m);
1,296✔
581
        if (r < 0)
1,296✔
582
                return r;
×
583

584
        return 0;
585
}
586

587
static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) {
398✔
588
        int r;
398✔
589

590
        assert(m);
398✔
591
        assert(rreq);
398✔
592

593
        Manager *manager = ASSERT_PTR(rreq->manager);
398✔
594
        Route *route = ASSERT_PTR(rreq->userdata);
398✔
595

596
        r = sd_netlink_message_get_errno(m);
398✔
597
        if (r < 0) {
398✔
598
                log_message_full_errno(m,
218✔
599
                                       (r == -ESRCH || /* the route is already removed? */
600
                                        (r == -EINVAL && route->nexthop_id != 0) || /* The nexthop is already removed? */
601
                                        !route->manager) ? /* already detached? */
602
                                       LOG_DEBUG : LOG_WARNING,
603
                                       r, "Could not drop route, ignoring");
604

605
                /* If the route cannot be removed, then assume the route is already removed. */
606
                route_forget(manager, route, "Forgetting");
176✔
607
        }
608

609
        return 1;
398✔
610
}
611

612
int route_remove(Route *route, Manager *manager) {
398✔
613
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
398✔
614
        Link *link = NULL;
398✔
615
        int r;
398✔
616

617
        assert(route);
398✔
618
        assert(manager);
398✔
619

620
        if (manager->state == MANAGER_STOPPED)
398✔
621
                return 0; /* The remove request will not be queued anyway. Suppress logging below. */
622

623
        /* If the route is remembered, then use the remembered object. */
624
        (void) route_get(manager, route, &route);
398✔
625

626
        log_route_debug(route, "Removing", manager);
398✔
627

628
        /* For logging. */
629
        (void) route_get_link(manager, route, &link);
398✔
630

631
        r = sd_rtnl_message_new_route(manager->rtnl, &m, RTM_DELROUTE, route->family, route->protocol);
398✔
632
        if (r < 0)
398✔
633
                return log_link_warning_errno(link, r, "Could not create netlink message: %m");
×
634

635
        r = route_set_netlink_message(route, m);
398✔
636
        if (r < 0)
398✔
637
                return log_link_warning_errno(link, r, "Could not fill netlink message: %m");
×
638

639
        r = manager_remove_request_add(manager, route, route, manager->rtnl, m, route_remove_handler);
398✔
640
        if (r < 0)
398✔
641
                return log_link_warning_errno(link, r, "Could not queue rtnetlink message: %m");
×
642

643
        route_enter_removing(route);
398✔
644
        return 0;
398✔
645
}
646

647
int route_remove_and_cancel(Route *route, Manager *manager) {
222✔
648
        _cleanup_(request_unrefp) Request *req = NULL;
222✔
649
        bool waiting = false;
222✔
650

651
        assert(route);
222✔
652
        assert(manager);
222✔
653

654
        /* If the route is remembered by the manager, then use the remembered object. */
655
        (void) route_get(manager, route, &route);
222✔
656

657
        /* Cancel the request for the route. If the request is already called but we have not received the
658
         * notification about the request, then explicitly remove the route. */
659
        if (route_get_request(manager, route, &req) >= 0) {
222✔
660
                request_ref(req); /* avoid the request freed by request_detach() */
4✔
661
                waiting = req->waiting_reply;
4✔
662
                request_detach(req);
4✔
663
                route_cancel_requesting(route);
4✔
664
        }
665

666
        /* If we know that the route will come or already exists, remove it. */
667
        if (waiting || (route->manager && route_exists(route)))
222✔
668
                return route_remove(route, manager);
186✔
669

670
        return 0;
671
}
672

673
static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
1✔
674
        Route *route = ASSERT_PTR(userdata);
1✔
675
        int r;
1✔
676

677
        if (!route->manager)
1✔
678
                return 0; /* already detached. */
679

680
        r = route_remove(route, route->manager);
1✔
681
        if (r < 0) {
1✔
682
                Link *link = NULL;
×
683
                (void) route_get_link(route->manager, route, &link);
×
684
                log_link_warning_errno(link, r, "Could not remove route: %m");
×
685
                if (link)
×
686
                        link_enter_failed(link);
×
687
        }
688

689
        return 1;
690
}
691

692
static int route_setup_timer(Route *route, const struct rta_cacheinfo *cacheinfo) {
8,088✔
693
        int r;
8,088✔
694

695
        assert(route);
8,088✔
696

697
        if (cacheinfo && cacheinfo->rta_expires != 0)
8,088✔
698
                route->expiration_managed_by_kernel = true;
181✔
699

700
        if (route->lifetime_usec == USEC_INFINITY || /* We do not request expiration for the route. */
8,088✔
701
            route->expiration_managed_by_kernel) {   /* We have received nonzero expiration previously. The expiration is managed by the kernel. */
702
                route->expire = sd_event_source_disable_unref(route->expire);
8,084✔
703
                return 0;
8,084✔
704
        }
705

706
        Manager *manager = ASSERT_PTR(route->manager);
4✔
707
        r = event_reset_time(manager->event, &route->expire, CLOCK_BOOTTIME,
4✔
708
                             route->lifetime_usec, 0, route_expire_handler, route, 0, "route-expiration", true);
709
        if (r < 0) {
4✔
710
                Link *link = NULL;
×
711
                (void) route_get_link(manager, route, &link);
×
712
                return log_link_warning_errno(link, r, "Failed to configure expiration timer for route, ignoring: %m");
×
713
        }
714

715
        log_route_debug(route, "Configured expiration timer for", manager);
4✔
716
        return 1;
4✔
717
}
718

719
static int route_update_by_request(Route *route, Request *req) {
893✔
720
        assert(route);
893✔
721
        assert(req);
893✔
722

723
        Route *rt = ASSERT_PTR(req->userdata);
893✔
724

725
        route->provider = rt->provider;
893✔
726
        route->source = rt->source;
893✔
727
        route->lifetime_usec = rt->lifetime_usec;
893✔
728

729
        return 0;
893✔
730
}
731

732
static int route_update_on_existing_one(Request *req, Route *requested) {
197✔
733
        Manager *manager = ASSERT_PTR(ASSERT_PTR(req)->manager);
197✔
734
        Route *existing;
197✔
735
        int r;
197✔
736

737
        assert(requested);
197✔
738

739
        if (route_get(manager, requested, &existing) < 0)
197✔
740
                return 0;
197✔
741

742
        r = route_update_by_request(existing, req);
197✔
743
        if (r < 0)
197✔
744
                return r;
745

746
        r = route_setup_timer(existing, NULL);
197✔
747
        if (r < 0)
197✔
748
                return r;
749

750
        /* This may be a bug in the kernel, but the MTU of an IPv6 route can be updated only when the
751
         * route has an expiration timer managed by the kernel (not by us). See fib6_add_rt2node() in
752
         * net/ipv6/ip6_fib.c of the kernel. */
753
        if (existing->family == AF_INET6 &&
197✔
754
            existing->expiration_managed_by_kernel) {
755
                r = route_metric_set(&existing->metric, RTAX_MTU, route_metric_get(&requested->metric, RTAX_MTU));
81✔
756
                if (r < 0)
81✔
757
                        return r;
758
        }
759

760
        route_enter_configured(existing);
197✔
761
        return 0;
197✔
762
}
763

764
static int route_update_on_existing(Request *req) {
197✔
765
        Route *rt = ASSERT_PTR(ASSERT_PTR(req)->userdata);
197✔
766
        int r;
197✔
767

768
        if (!req->manager)
197✔
769
                /* Already detached? At least there are two possibilities then.
770
                 * 1) The interface is removed, and all queued requests for the interface are cancelled.
771
                 * 2) networkd is now stopping, hence all queued requests are cancelled.
772
                 * Anyway, we can ignore the request, and there is nothing we can do. */
773
                return 0;
197✔
774

775
        if (rt->family == AF_INET || ordered_set_isempty(rt->nexthops))
197✔
776
                return route_update_on_existing_one(req, rt);
197✔
777

778
        RouteNextHop *nh;
×
779
        ORDERED_SET_FOREACH(nh, rt->nexthops) {
×
780
                _cleanup_(route_unrefp) Route *dup = NULL;
×
781

782
                r = route_dup(rt, nh, &dup);
×
783
                if (r < 0)
×
784
                        return r;
785

786
                r = route_update_on_existing_one(req, dup);
×
787
                if (r < 0)
×
788
                        return r;
789
        }
790

791
        return 0;
×
792
}
793

794
int route_configure_handler_internal(sd_netlink_message *m, Request *req, Route *route) {
897✔
795
        int r;
897✔
796

797
        assert(m);
897✔
798
        assert(req);
897✔
799
        assert(route);
897✔
800

801
        Link *link = ASSERT_PTR(req->link);
897✔
802

803
        r = sd_netlink_message_get_errno(m);
897✔
804
        if (r == -EEXIST) {
897✔
805
                /* When re-configuring an existing route, kernel does not send RTM_NEWROUTE notification, so
806
                 * here we need to update the state, provider, source, timer, and so on. */
807
                r = route_update_on_existing(req);
197✔
808
                if (r < 0) {
197✔
809
                        log_link_warning_errno(link, r, "Failed to update existing route: %m");
×
810
                        link_enter_failed(link);
×
811
                        return 0;
×
812
                }
813

814
                return 1;
815
        }
816
        if (r < 0) {
700✔
817
                _cleanup_free_ char *str = NULL;
×
818
                (void) route_to_string(route, link->manager, &str);
×
819
                log_link_message_warning_errno(link, m, r, "Failed to configure %s route (%s)",
×
820
                                               network_config_source_to_string(route->source), strna(str));
821
                link_enter_failed(link);
×
822
                return 0;
×
823
        }
824

825
        return 1;
826
}
827

828
static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link, Request *req) {
898✔
829
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
898✔
830
        int r;
898✔
831

832
        assert(route);
898✔
833
        assert(link);
898✔
834
        assert(link->manager);
898✔
835
        assert(req);
898✔
836

837
        log_route_debug(route, "Configuring", link->manager);
898✔
838

839
        r = sd_rtnl_message_new_route(link->manager->rtnl, &m, RTM_NEWROUTE, route->family, route->protocol);
898✔
840
        if (r < 0)
898✔
841
                return r;
842

843
        r = route_set_netlink_message(route, m);
898✔
844
        if (r < 0)
898✔
845
                return r;
846

847
        if (lifetime_sec != UINT32_MAX) {
898✔
848
                r = sd_netlink_message_append_u32(m, RTA_EXPIRES, lifetime_sec);
236✔
849
                if (r < 0)
236✔
850
                        return r;
851
        }
852

853
        return request_call_netlink_async(link->manager->rtnl, m, req);
898✔
854
}
855

856
static int route_requeue_request(Request *req, Link *link, const Route *route) {
910✔
857
        _unused_ _cleanup_(request_unrefp) Request *req_unref = NULL;
910✔
858
        _cleanup_(route_unrefp) Route *tmp = NULL;
910✔
859
        int r;
910✔
860

861
        assert(req);
910✔
862
        assert(link);
910✔
863
        assert(link->manager);
910✔
864
        assert(route);
910✔
865

866
        /* It is not possible to adjust the Route object owned by Request, as it is used as a key to manage
867
         * Request objects in the queue. Hence, we need to re-request with the updated Route object. */
868

869
        if (!route_nexthops_needs_adjust(route))
910✔
870
                return 0; /* The Route object does not need the adjustment. Continue with it. */
871

872
        r = route_dup(route, NULL, &tmp);
168✔
873
        if (r < 0)
168✔
874
                return r;
875

876
        r = route_adjust_nexthops(tmp, link);
168✔
877
        if (r <= 0)
168✔
878
                return r;
879

880
        if (route_compare_func(route, tmp) == 0 && route->type == tmp->type)
24✔
881
                return 0; /* No effective change?? That's OK. */
882

883
        /* Avoid the request to be freed by request_detach(). */
884
        req_unref = request_ref(req);
12✔
885

886
        /* Detach the request from the queue, to make not the new request is deduped.
887
         * Why this is necessary? IPv6 routes with different type may be handled as the same,
888
         * As commented in route_adjust_nexthops(), we need to configure the adjusted type,
889
         * otherwise we cannot remove the route on reconfigure or so. If we request the new Route object
890
         * without detaching the current request, the new request is deduped, and the route is configured
891
         * with unmodified type. */
892
        request_detach(req);
12✔
893

894
        /* Request the route with the adjusted Route object combined with the same other parameters. */
895
        r = link_requeue_request(link, req, tmp, NULL);
12✔
896
        if (r < 0)
12✔
897
                return r;
898
        if (r == 0)
12✔
899
                return 1; /* Already queued?? That's OK. Maybe, [Route] section is effectively duplicated. */
900

901
        TAKE_PTR(tmp);
12✔
902
        return 1; /* New request is queued. Finish to process this request. */
12✔
903
}
904

905
static int route_is_ready_to_configure(const Route *route, Link *link) {
23,260✔
906
        assert(route);
23,260✔
907
        assert(link);
23,260✔
908

909
        if (!link_is_ready_to_configure(link, /* allow_unmanaged= */ false))
23,260✔
910
                return false;
911

912
        if (in_addr_is_set(route->family, &route->prefsrc) > 0) {
4,776✔
913
                Address *a;
1,458✔
914

915
                if (manager_get_address(link->manager, route->family, &route->prefsrc, &a) < 0)
1,458✔
916
                        return false;
883✔
917

918
                if (!address_is_ready(a))
582✔
919
                        return false;
920
        }
921

922
        return route_nexthops_is_ready_to_configure(route, link->manager);
3,893✔
923
}
924

925
static int route_process_request(Request *req, Link *link, Route *route) {
23,260✔
926
        Route *existing;
23,260✔
927
        int r;
23,260✔
928

929
        assert(req);
23,260✔
930
        assert(link);
23,260✔
931
        assert(link->manager);
23,260✔
932
        assert(route);
23,260✔
933

934
        r = route_is_ready_to_configure(route, link);
23,260✔
935
        if (r < 0)
23,260✔
936
                return log_link_warning_errno(link, r, "Failed to check if route is ready to configure: %m");
×
937
        if (r == 0)
23,260✔
938
                return 0;
939

940
        usec_t now_usec;
910✔
941
        assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
910✔
942
        uint32_t sec = usec_to_sec(route->lifetime_usec, now_usec);
910✔
943
        if (sec == 0) {
910✔
944
                log_link_debug(link, "Refuse to configure %s route with zero lifetime.",
×
945
                               network_config_source_to_string(route->source));
946

947
                route_cancel_requesting(route);
×
948
                if (route_get(link->manager, route, &existing) >= 0)
×
949
                        route_cancel_requesting(existing);
×
950
                return 1;
×
951
        }
952

953
        r = route_requeue_request(req, link, route);
910✔
954
        if (r != 0)
910✔
955
                return r;
956

957
        r = route_configure(route, sec, link, req);
898✔
958
        if (r < 0)
898✔
959
                return log_link_warning_errno(link, r, "Failed to configure route: %m");
×
960

961
        route_enter_configuring(route);
898✔
962
        if (route_get(link->manager, route, &existing) >= 0)
898✔
963
                route_enter_configuring(existing);
197✔
964
        return 1;
965
}
966

967
static int link_request_route_one(
1,180✔
968
                Link *link,
969
                const Route *route,
970
                const RouteNextHop *nh,
971
                unsigned *message_counter,
972
                route_netlink_handler_t netlink_handler) {
973

974
        _cleanup_(route_unrefp) Route *tmp = NULL;
1,180✔
975
        Route *existing = NULL;
1,180✔
976
        int r;
1,180✔
977

978
        assert(link);
1,180✔
979
        assert(link->manager);
1,180✔
980
        assert(route);
1,180✔
981

982
        r = route_dup(route, nh, &tmp);
1,180✔
983
        if (r < 0)
1,180✔
984
                return r;
985

986
        r = route_adjust_nexthops(tmp, link);
1,180✔
987
        if (r < 0)
1,180✔
988
                return r;
989

990
        if (route_get(link->manager, tmp, &existing) >= 0)
1,180✔
991
                /* Copy state for logging below. */
992
                tmp->state = existing->state;
209✔
993

994
        log_route_debug(tmp, "Requesting", link->manager);
1,180✔
995
        r = link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
1,180✔
996
                                    tmp,
997
                                    route_unref,
998
                                    route_hash_func,
999
                                    route_compare_func,
1000
                                    route_process_request,
1001
                                    message_counter,
1002
                                    netlink_handler,
1003
                                    NULL);
1004
        if (r <= 0)
1,180✔
1005
                return r;
1006

1007
        route_enter_requesting(tmp);
918✔
1008
        if (existing)
918✔
1009
                route_enter_requesting(existing);
202✔
1010

1011
        TAKE_PTR(tmp);
918✔
1012
        return 1;
918✔
1013
}
1014

1015
int link_request_route(
1,162✔
1016
                Link *link,
1017
                const Route *route,
1018
                unsigned *message_counter,
1019
                route_netlink_handler_t netlink_handler) {
1020

1021
        int r;
1,162✔
1022

1023
        assert(link);
1,162✔
1024
        assert(link->manager);
1,162✔
1025
        assert(route);
1,162✔
1026
        assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN);
1,162✔
1027

1028
        if (route->family == AF_INET || route_is_reject(route) || ordered_set_isempty(route->nexthops))
1,162✔
1029
                return link_request_route_one(link, route, NULL, message_counter, netlink_handler);
1,156✔
1030

1031
        RouteNextHop *nh;
6✔
1032
        ORDERED_SET_FOREACH(nh, route->nexthops) {
30✔
1033
                r = link_request_route_one(link, route, nh, message_counter, netlink_handler);
24✔
1034
                if (r < 0)
24✔
1035
                        return r;
×
1036
        }
1037

1038
        return 0;
6✔
1039
}
1040

1041
static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
384✔
1042
        int r;
384✔
1043

1044
        assert(req);
384✔
1045
        assert(link);
384✔
1046
        assert(route);
384✔
1047

1048
        r = route_configure_handler_internal(m, req, route);
384✔
1049
        if (r <= 0)
384✔
1050
                return r;
1051

1052
        if (link->static_route_messages == 0) {
384✔
1053
                log_link_debug(link, "Routes set");
110✔
1054
                link->static_routes_configured = true;
110✔
1055
                link_check_ready(link);
110✔
1056
        }
1057

1058
        return 1;
1059
}
1060

1061
static int link_request_wireguard_routes(Link *link, bool only_ipv4) {
1,604✔
1062
        Route *route;
1,604✔
1063
        int r;
1,604✔
1064

1065
        assert(link);
1,604✔
1066

1067
        if (!link->netdev || link->netdev->kind != NETDEV_KIND_WIREGUARD)
1,604✔
1068
                return 0;
1,604✔
1069

1070
        Wireguard *w = WIREGUARD(link->netdev);
9✔
1071

1072
        SET_FOREACH(route, w->routes) {
153✔
1073
                if (only_ipv4 && route->family != AF_INET)
144✔
1074
                        continue;
×
1075

1076
                r = link_request_route(link, route, &link->static_route_messages, static_route_handler);
144✔
1077
                if (r < 0)
144✔
1078
                        return r;
×
1079
        }
1080

1081
        return 0;
9✔
1082
}
1083

1084
int link_request_static_routes(Link *link, bool only_ipv4) {
1,604✔
1085
        Route *route;
1,604✔
1086
        int r;
1,604✔
1087

1088
        assert(link);
1,604✔
1089
        assert(link->network);
1,604✔
1090

1091
        link->static_routes_configured = false;
1,604✔
1092

1093
        HASHMAP_FOREACH(route, link->network->routes_by_section) {
2,066✔
1094
                if (route->source != NETWORK_CONFIG_SOURCE_STATIC)
462✔
1095
                        continue;
20✔
1096

1097
                if (only_ipv4 && route->family != AF_INET)
442✔
1098
                        continue;
×
1099

1100
                r = link_request_route(link, route, &link->static_route_messages, static_route_handler);
442✔
1101
                if (r < 0)
442✔
1102
                        return r;
×
1103
        }
1104

1105
        r = link_request_wireguard_routes(link, only_ipv4);
1,604✔
1106
        if (r < 0)
1,604✔
1107
                return r;
1108

1109
        if (link->static_route_messages == 0) {
1,604✔
1110
                link->static_routes_configured = true;
1,415✔
1111
                link_check_ready(link);
1,415✔
1112
        } else {
1113
                log_link_debug(link, "Requesting routes");
189✔
1114
                link_set_state(link, LINK_STATE_CONFIGURING);
189✔
1115
        }
1116

1117
        return 0;
1118
}
1119

1120
static int process_route_one(
12,380✔
1121
                Manager *manager,
1122
                uint16_t type,
1123
                Route *tmp,
1124
                const struct rta_cacheinfo *cacheinfo) {
1125

1126
        Route *route = NULL;
12,380✔
1127
        Link *link = NULL;
12,380✔
1128
        bool is_new = false, update_dhcp4;
12,380✔
1129
        int r;
12,380✔
1130

1131
        assert(manager);
12,380✔
1132
        assert(tmp);
12,380✔
1133
        assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
12,380✔
1134

1135
        (void) route_get(manager, tmp, &route);
12,380✔
1136
        (void) route_get_link(manager, tmp, &link);
12,380✔
1137

1138
        update_dhcp4 = link && tmp->family == AF_INET6 && tmp->dst_prefixlen == 0;
12,380✔
1139

1140
        switch (type) {
12,380✔
1141
        case RTM_NEWROUTE: {
8,013✔
1142
                Request *req = NULL;
8,013✔
1143
                (void) route_get_request(manager, tmp, &req);
8,013✔
1144

1145
                if (!route) {
8,013✔
1146
                        if (!manager->manage_foreign_routes && !(req && req->waiting_reply)) {
7,991✔
1147
                                route_enter_configured(tmp);
122✔
1148
                                log_route_debug(tmp, "Ignoring received", manager);
122✔
1149
                                return 0;
122✔
1150
                        }
1151

1152
                        /* If we do not know the route, then save it. */
1153
                        r = route_attach(manager, tmp);
7,869✔
1154
                        if (r < 0) {
7,869✔
1155
                                log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
×
1156
                                return 0;
×
1157
                        }
1158

1159
                        route = route_ref(tmp);
7,869✔
1160
                        is_new = true;
7,869✔
1161

1162
                } else {
1163
                        /* Update remembered route with the received notification. */
1164

1165
                        /* Here, update weight only when a non-zero weight is received. As the kernel does
1166
                         * not provide the weight of a single-path route. In such case, tmp->nexthop.weight
1167
                         * is zero, hence we should not overwrite the known weight of the route. */
1168
                        if (tmp->nexthop.weight != 0)
22✔
1169
                                route->nexthop.weight = tmp->nexthop.weight;
22✔
1170
                }
1171

1172
                /* Also update information that cannot be obtained through netlink notification. */
1173
                if (req && req->waiting_reply) {
7,891✔
1174
                        r = route_update_by_request(route, req);
696✔
1175
                        if (r < 0) {
696✔
1176
                                log_link_warning_errno(link, r, "Failed to update route by request: %m");
×
1177
                                link_enter_failed(link);
×
1178
                                return 0;
×
1179
                        }
1180

1181
                        /* We configure IPv6 multipath route separately. When the first path is configured,
1182
                         * the kernel does not provide the weight of the path. So, we need to adjust it here.
1183
                         * Hopefully, the weight is assigned correctly. */
1184
                        if (route->nexthop.weight == 0) {
696✔
1185
                                Route *rt = ASSERT_PTR(req->userdata);
686✔
1186
                                route->nexthop.weight = rt->nexthop.weight;
686✔
1187
                        }
1188
                }
1189

1190
                route_attach_to_nexthop(route);
7,891✔
1191

1192
                route_enter_configured(route);
7,891✔
1193
                log_route_debug(route, is_new ? "Received new" : "Received remembered", manager);
7,913✔
1194

1195
                (void) route_setup_timer(route, cacheinfo);
7,891✔
1196

1197
                break;
7,891✔
1198
        }
1199
        case RTM_DELROUTE:
4,367✔
1200
                if (route)
4,367✔
1201
                        route_forget(manager, route, "Forgetting removed");
4,248✔
1202
                else
1203
                        log_route_debug(tmp,
119✔
1204
                                        manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received",
119✔
1205
                                        manager);
1206
                break;
1207

1208
        default:
×
1209
                assert_not_reached();
×
1210
        }
1211

1212
        if (update_dhcp4) {
12,258✔
1213
                r = dhcp4_update_ipv6_connectivity(link);
257✔
1214
                if (r < 0) {
257✔
1215
                        log_link_warning_errno(link, r, "Failed to notify IPv6 connectivity to DHCPv4 client: %m");
×
1216
                        link_enter_failed(link);
×
1217
                }
1218
        }
1219

1220
        return 1;
1221
}
1222

1223
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
12,358✔
1224
        _cleanup_(route_unrefp) Route *tmp = NULL;
12,358✔
1225
        int r;
12,358✔
1226

1227
        assert(rtnl);
12,358✔
1228
        assert(message);
12,358✔
1229
        assert(m);
12,358✔
1230

1231
        if (sd_netlink_message_is_error(message)) {
12,358✔
1232
                r = sd_netlink_message_get_errno(message);
×
1233
                if (r < 0)
×
1234
                        log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring");
×
1235

1236
                return 0;
×
1237
        }
1238

1239
        uint16_t type;
12,358✔
1240
        r = sd_netlink_message_get_type(message, &type);
12,358✔
1241
        if (r < 0) {
12,358✔
1242
                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
×
1243
                return 0;
×
1244
        } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
12,358✔
1245
                log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type);
×
1246
                return 0;
×
1247
        }
1248

1249
        r = route_new(&tmp);
12,358✔
1250
        if (r < 0)
12,358✔
1251
                return log_oom();
×
1252

1253
        /* rtmsg header */
1254
        r = sd_rtnl_message_route_get_family(message, &tmp->family);
12,358✔
1255
        if (r < 0) {
12,358✔
1256
                log_warning_errno(r, "rtnl: received route message without family, ignoring: %m");
×
1257
                return 0;
×
1258
        } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
12,358✔
1259
                log_debug("rtnl: received route message with invalid family '%i', ignoring.", tmp->family);
×
1260
                return 0;
×
1261
        }
1262

1263
        r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
12,358✔
1264
        if (r < 0) {
12,358✔
1265
                log_warning_errno(r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
×
1266
                return 0;
×
1267
        }
1268

1269
        r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
12,358✔
1270
        if (r < 0) {
12,358✔
1271
                log_warning_errno(r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
×
1272
                return 0;
×
1273
        }
1274

1275
        r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
12,358✔
1276
        if (r < 0) {
12,358✔
1277
                log_warning_errno(r, "rtnl: received route message with invalid tos, ignoring: %m");
×
1278
                return 0;
×
1279
        }
1280

1281
        r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
12,358✔
1282
        if (r < 0) {
12,358✔
1283
                log_warning_errno(r, "rtnl: received route message without route protocol, ignoring: %m");
×
1284
                return 0;
×
1285
        }
1286

1287
        r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
12,358✔
1288
        if (r < 0) {
12,358✔
1289
                log_warning_errno(r, "rtnl: received route message with invalid scope, ignoring: %m");
×
1290
                return 0;
×
1291
        }
1292

1293
        r = sd_rtnl_message_route_get_type(message, &tmp->type);
12,358✔
1294
        if (r < 0) {
12,358✔
1295
                log_warning_errno(r, "rtnl: received route message with invalid type, ignoring: %m");
×
1296
                return 0;
×
1297
        }
1298

1299
        r = sd_rtnl_message_route_get_flags(message, &tmp->flags);
12,358✔
1300
        if (r < 0) {
12,358✔
1301
                log_warning_errno(r, "rtnl: received route message without route flags, ignoring: %m");
×
1302
                return 0;
×
1303
        }
1304

1305
        /* attributes */
1306
        r = netlink_message_read_in_addr_union(message, RTA_DST, tmp->family, &tmp->dst);
12,358✔
1307
        if (r < 0 && r != -ENODATA) {
12,358✔
1308
                log_warning_errno(r, "rtnl: received route message without valid destination, ignoring: %m");
×
1309
                return 0;
×
1310
        }
1311

1312
        r = netlink_message_read_in_addr_union(message, RTA_SRC, tmp->family, &tmp->src);
12,358✔
1313
        if (r < 0 && r != -ENODATA) {
12,358✔
1314
                log_warning_errno(r, "rtnl: received route message without valid source, ignoring: %m");
×
1315
                return 0;
×
1316
        }
1317

1318
        r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
12,358✔
1319
        if (r < 0 && r != -ENODATA) {
12,358✔
1320
                log_warning_errno(r, "rtnl: received route message with invalid priority, ignoring: %m");
×
1321
                return 0;
×
1322
        }
1323

1324
        r = netlink_message_read_in_addr_union(message, RTA_PREFSRC, tmp->family, &tmp->prefsrc);
12,358✔
1325
        if (r < 0 && r != -ENODATA) {
12,358✔
1326
                log_warning_errno(r, "rtnl: received route message without valid preferred source, ignoring: %m");
×
1327
                return 0;
×
1328
        }
1329

1330
        r = sd_netlink_message_read_u32(message, RTA_TABLE, &tmp->table);
12,358✔
1331
        if (r == -ENODATA) {
12,358✔
1332
                unsigned char table;
×
1333

1334
                r = sd_rtnl_message_route_get_table(message, &table);
×
1335
                if (r >= 0)
×
1336
                        tmp->table = table;
×
1337
        }
1338
        if (r < 0) {
12,358✔
1339
                log_warning_errno(r, "rtnl: received route message with invalid table, ignoring: %m");
×
1340
                return 0;
×
1341
        }
1342

1343
        r = sd_netlink_message_read_u8(message, RTA_PREF, &tmp->pref);
12,358✔
1344
        if (r < 0 && r != -ENODATA) {
12,358✔
1345
                log_warning_errno(r, "rtnl: received route message with invalid preference, ignoring: %m");
×
1346
                return 0;
×
1347
        }
1348

1349
        /* nexthops */
1350
        if (route_nexthops_read_netlink_message(tmp, message) < 0)
12,358✔
1351
                return 0;
1352

1353
        /* metrics */
1354
        if (route_metric_read_netlink_message(&tmp->metric, message) < 0)
12,358✔
1355
                return 0;
1356

1357
        bool has_cacheinfo;
12,358✔
1358
        struct rta_cacheinfo cacheinfo;
12,358✔
1359
        r = sd_netlink_message_read(message, RTA_CACHEINFO, sizeof(cacheinfo), &cacheinfo);
12,358✔
1360
        if (r < 0 && r != -ENODATA) {
12,358✔
1361
                log_warning_errno(r, "rtnl: failed to read RTA_CACHEINFO attribute, ignoring: %m");
×
1362
                return 0;
×
1363
        }
1364
        has_cacheinfo = r >= 0;
12,358✔
1365

1366
        if (tmp->family == AF_INET || ordered_set_isempty(tmp->nexthops))
12,358✔
1367
                return process_route_one(m, type, tmp, has_cacheinfo ? &cacheinfo : NULL);
17,909✔
1368

1369
        RouteNextHop *nh;
10✔
1370
        ORDERED_SET_FOREACH(nh, tmp->nexthops) {
42✔
1371
                _cleanup_(route_unrefp) Route *dup = NULL;
32✔
1372

1373
                r = route_dup(tmp, nh, &dup);
32✔
1374
                if (r < 0)
32✔
1375
                        return log_oom();
×
1376

1377
                r = process_route_one(m, type, dup, has_cacheinfo ? &cacheinfo : NULL);
32✔
1378
                if (r < 0)
32✔
1379
                        return r;
1380
        }
1381

1382
        return 1;
10✔
1383
}
1384

1385
void manager_mark_routes(Manager *manager, Link *link, NetworkConfigSource source) {
110✔
1386
        Route *route;
110✔
1387

1388
        assert(manager);
110✔
1389

1390
        SET_FOREACH(route, manager->routes) {
3,057✔
1391
                if (route->source != source)
2,947✔
1392
                        continue;
2,886✔
1393

1394
                if (link) {
61✔
1395
                        Link *route_link;
61✔
1396

1397
                        if (route_get_link(manager, route, &route_link) < 0)
61✔
1398
                                continue;
9✔
1399
                        if (route_link != link)
61✔
1400
                                continue;
9✔
1401
                }
1402

1403
                route_mark(route);
52✔
1404
        }
1405
}
110✔
1406

1407
static bool route_by_kernel(const Route *route) {
31,476✔
1408
        assert(route);
31,476✔
1409

1410
        if (route->protocol == RTPROT_KERNEL)
31,476✔
1411
                return true;
1412

1413
        /* The kernels older than a826b04303a40d52439aa141035fca5654ccaccd (v5.11) create the IPv6
1414
         * multicast with RTPROT_BOOT. Do not touch it. */
1415
        if (route->protocol == RTPROT_BOOT &&
1,413✔
1416
            route->family == AF_INET6 &&
5✔
1417
            route->dst_prefixlen == 8 &&
×
1418
            in6_addr_equal(&route->dst.in6, & (struct in6_addr) {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}}))
×
1419
                return true;
×
1420

1421
        return false;
1,413✔
1422
}
1423

1424
bool route_can_update(Manager *manager, const Route *existing, const Route *requesting) {
596✔
1425
        int r;
596✔
1426

1427
        assert(manager);
596✔
1428
        assert(existing);
596✔
1429
        assert(requesting);
596✔
1430

1431
        if (route_compare_func(existing, requesting) != 0) {
596✔
1432
                log_route_debug(existing, "Cannot update route, as the existing route is different", manager);
10✔
1433
                return false;
10✔
1434
        }
1435

1436
        switch (existing->family) {
586✔
1437
        case AF_INET:
248✔
1438
                if (existing->nexthop.weight != requesting->nexthop.weight) {
248✔
1439
                        log_debug("Cannot update route: existing weight: %u, requesting weight: %u",
×
1440
                                  existing->nexthop.weight, requesting->nexthop.weight);
1441
                        return false;
×
1442
                }
1443
                return true;
1444

1445
        case AF_INET6:
338✔
1446
                if (existing->protocol != requesting->protocol) {
338✔
1447
                        if (DEBUG_LOGGING) {
1✔
1448
                                _cleanup_free_ char *ex = NULL, *req = NULL;
1✔
1449

1450
                                r = route_protocol_to_string_alloc(existing->protocol, &ex);
1✔
1451
                                if (r < 0)
1✔
1452
                                        return false;
1453

1454
                                r = route_protocol_to_string_alloc(requesting->protocol, &req);
1✔
1455
                                if (r < 0)
1✔
1456
                                        return false;
1457

1458
                                log_debug("Cannot update route: existing protocol: %s, requesting protocol: %s", ex, req);
1✔
1459
                        }
1460

1461
                        return false;
1✔
1462
                }
1463
                if (existing->type != requesting->type) {
337✔
1464
                        log_debug("Cannot update route: existing type: %s, requesting type: %s",
2✔
1465
                                  route_type_to_string(existing->type),
1466
                                  route_type_to_string(requesting->type));
1467

1468
                        return false;
2✔
1469
                }
1470
                if ((existing->flags & ~RTNH_COMPARE_MASK) != (requesting->flags & ~RTNH_COMPARE_MASK)) {
335✔
1471
                        if (DEBUG_LOGGING) {
×
1472
                                _cleanup_free_ char *ex = NULL, *req = NULL;
×
1473

1474
                                r = route_flags_to_string_alloc(existing->flags, &ex);
×
1475
                                if (r < 0)
×
1476
                                        return false;
1477

1478
                                r = route_flags_to_string_alloc(requesting->flags, &req);
×
1479
                                if (r < 0)
×
1480
                                        return false;
1481

1482
                                log_debug("Cannot update route: existing flags: %s, requesting flags: %s", ex, req);
×
1483
                        }
1484

1485
                        return false;
×
1486
                }
1487
                if (!in6_addr_equal(&existing->prefsrc.in6, &requesting->prefsrc.in6)) {
335✔
1488
                        log_debug("Cannot update route: existing preferred source: %s, requesting preferred source: %s",
×
1489
                                  IN6_ADDR_TO_STRING(&existing->prefsrc.in6),
1490
                                  IN6_ADDR_TO_STRING(&requesting->prefsrc.in6));
1491
                        return false;
×
1492
                }
1493
                if (existing->pref != requesting->pref) {
335✔
1494
                        log_debug("Cannot update route: existing preference: %u, requesting preference: %u",
×
1495
                                  existing->pref, requesting->pref);
1496
                        return false;
×
1497
                }
1498
                if (existing->expiration_managed_by_kernel && requesting->lifetime_usec == USEC_INFINITY) {
335✔
1499
                        log_route_debug(existing,
×
1500
                                        "Cannot update route: the expiration is managed by the kernel and requested lifetime is infinite",
1501
                                        manager);
1502
                        return false; /* We cannot disable expiration timer in the kernel. */
×
1503
                }
1504
                if (!route_metric_can_update(&existing->metric, &requesting->metric, existing->expiration_managed_by_kernel)) {
335✔
1505
                        log_route_debug(existing,
×
1506
                                        "Cannot update route: expiration is managed by the kernel or metrics differ",
1507
                                        manager);
1508
                        return false;
×
1509
                }
1510
                if (existing->nexthop.weight != requesting->nexthop.weight) {
335✔
1511
                        log_debug("Cannot update route: existing weight: %u, requesting weight: %u",
×
1512
                                  existing->nexthop.weight, requesting->nexthop.weight);
1513
                        return false;
×
1514
                }
1515
                return true;
1516

1517
        default:
×
1518
                assert_not_reached();
×
1519
        }
1520
}
1521

1522
static int link_unmark_route(Link *link, const Route *route, const RouteNextHop *nh) {
949✔
1523
        _cleanup_(route_unrefp) Route *tmp = NULL;
949✔
1524
        Route *existing;
949✔
1525
        int r;
949✔
1526

1527
        assert(link);
949✔
1528
        assert(route);
949✔
1529

1530
        r = route_dup(route, nh, &tmp);
949✔
1531
        if (r < 0)
949✔
1532
                return r;
1533

1534
        r = route_adjust_nexthops(tmp, link);
949✔
1535
        if (r < 0)
949✔
1536
                return r;
1537

1538
        if (route_get(link->manager, tmp, &existing) < 0)
949✔
1539
                return 0;
1540

1541
        if (!route_can_update(link->manager, existing, tmp))
500✔
1542
                return 0;
1543

1544
        route_unmark(existing);
498✔
1545
        return 1;
498✔
1546
}
1547

1548
int link_drop_routes(Link *link, bool only_static) {
3,518✔
1549
        Route *route;
3,518✔
1550
        Link *other;
3,518✔
1551
        int r = 0;
3,518✔
1552

1553
        assert(link);
3,518✔
1554
        assert(link->manager);
3,518✔
1555

1556
        /* First, mark all routes. */
1557
        SET_FOREACH(route, link->manager->routes) {
34,994✔
1558
                /* Do not touch routes managed by the kernel. */
1559
                if (route_by_kernel(route))
31,476✔
1560
                        continue;
30,977✔
1561

1562
                /* Ignore routes not assigned yet or already removed. */
1563
                if (!route_exists(route))
1,413✔
1564
                        continue;
179✔
1565

1566
                if (!link_should_mark_config(link, only_static, route->source, route->protocol))
1,234✔
1567
                        continue;
509✔
1568

1569
                Link *route_link = NULL;
725✔
1570
                if (route_get_link(link->manager, route, &route_link) >= 0 && route_link != link) {
725✔
1571
                        /* When we also mark foreign routes, do not mark routes assigned to other interfaces.
1572
                         * Otherwise, routes assigned to unmanaged interfaces will be dropped.
1573
                         * Note, route_get_link() does not provide assigned link for routes with an
1574
                         * unreachable type or IPv4 multipath routes. So, the current implementation does not
1575
                         * support managing such routes by other daemon or so, unless ManageForeignRoutes=no. */
1576
                        if (!only_static)
386✔
1577
                                continue;
221✔
1578

1579
                        /* When we mark only static routes, do not mark routes assigned to links that we do
1580
                         * not know the assignment of .network files to the interfaces. Otherwise, if an
1581
                         * interface is in the pending state, even if the .network file to be assigned to the
1582
                         * interface has KeepConfiguration=yes, routes on the interface will be removed.
1583
                         * This is especially important when systemd-networkd is restarted. */
1584
                        if (!IN_SET(route_link->state, LINK_STATE_UNMANAGED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
165✔
1585
                                continue;
5✔
1586
                }
1587

1588
                route_mark(route);
499✔
1589
        }
1590

1591
        /* Then, unmark all routes requested by active links. */
1592
        HASHMAP_FOREACH(other, link->manager->links_by_index) {
26,304✔
1593
                if (only_static && other == link)
22,786✔
1594
                        continue;
2,655✔
1595

1596
                if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
20,131✔
1597
                        continue;
17,176✔
1598

1599
                HASHMAP_FOREACH(route, other->network->routes_by_section) {
3,649✔
1600
                        if (route->source != NETWORK_CONFIG_SOURCE_STATIC)
694✔
1601
                                continue;
19✔
1602

1603
                        if (route->family == AF_INET || ordered_set_isempty(route->nexthops)) {
675✔
1604
                                r = link_unmark_route(other, route, NULL);
665✔
1605
                                if (r < 0)
665✔
1606
                                        return r;
×
1607

1608
                        } else {
1609
                                RouteNextHop *nh;
10✔
1610
                                ORDERED_SET_FOREACH(nh, route->nexthops) {
50✔
1611
                                        r = link_unmark_route(other, route, nh);
40✔
1612
                                        if (r < 0)
40✔
1613
                                                return r;
×
1614
                                }
1615
                        }
1616
                }
1617

1618
                /* Also unmark routes requested in .netdev file. */
1619
                if (other->netdev && other->netdev->kind == NETDEV_KIND_WIREGUARD) {
2,955✔
1620
                        Wireguard *w = WIREGUARD(other->netdev);
18✔
1621

1622
                        SET_FOREACH(route, w->routes) {
262✔
1623
                                r = link_unmark_route(other, route, NULL);
244✔
1624
                                if (r < 0)
244✔
1625
                                        return r;
×
1626
                        }
1627
                }
1628
        }
1629

1630
        /* Finally, remove all marked routes. */
1631
        SET_FOREACH(route, link->manager->routes) {
34,994✔
1632
                if (!route_is_marked(route))
31,476✔
1633
                        continue;
31,265✔
1634

1635
                RET_GATHER(r, route_remove(route, link->manager));
211✔
1636
        }
1637

1638
        return r;
3,518✔
1639
}
1640

1641
void link_forget_routes(Link *link) {
724✔
1642
        assert(link);
724✔
1643
        assert(link->ifindex > 0);
724✔
1644
        assert(!FLAGS_SET(link->flags, IFF_UP));
724✔
1645

1646
        /* When an interface went down, IPv4 non-local routes bound to the interface are silently removed by
1647
         * the kernel, without any notifications. Let's forget them in that case. Otherwise, when the link
1648
         * goes up later, the configuration order of routes may be confused by the nonexistent routes.
1649
         * See issue #35047. */
1650

1651
        Route *route;
724✔
1652
        SET_FOREACH(route, link->manager->routes) {
12,664✔
1653
                // TODO: handle multipath routes
1654
                if (route->nexthop.ifindex != link->ifindex)
11,940✔
1655
                        continue;
7,421✔
1656
                if (route->family != AF_INET)
4,519✔
1657
                        continue;
2,651✔
1658
                // TODO: check RTN_NAT and RTN_XRESOLVE
1659
                if (!IN_SET(route->type, RTN_UNICAST, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
1,868✔
1660
                        continue;
561✔
1661

1662
                route_forget(link->manager, route, "Forgetting silently removed");
1,307✔
1663
        }
1664
}
724✔
1665

1666
int network_add_ipv4ll_route(Network *network) {
7,549✔
1667
        _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
7,549✔
1668
        unsigned section_line;
7,549✔
1669
        int r;
7,549✔
1670

1671
        assert(network);
7,549✔
1672

1673
        if (!network->ipv4ll_route)
7,549✔
1674
                return 0;
1675

1676
        r = hashmap_by_section_find_unused_line(network->routes_by_section, network->filename, &section_line);
10✔
1677
        if (r < 0)
10✔
1678
                return r;
1679

1680
        /* IPv4LLRoute= is in [Network] section. */
1681
        r = route_new_static(network, network->filename, section_line, &route);
10✔
1682
        if (r < 0)
10✔
1683
                return r;
1684

1685
        r = in_addr_from_string(AF_INET, "169.254.0.0", &route->dst);
10✔
1686
        if (r < 0)
10✔
1687
                return r;
1688

1689
        route->family = AF_INET;
10✔
1690
        route->dst_prefixlen = 16;
10✔
1691
        route->scope = RT_SCOPE_LINK;
10✔
1692
        route->scope_set = true;
10✔
1693
        route->table_set = true;
10✔
1694
        route->priority = IPV4LL_ROUTE_METRIC;
10✔
1695
        route->protocol = RTPROT_STATIC;
10✔
1696

1697
        TAKE_PTR(route);
10✔
1698
        return 0;
10✔
1699
}
1700

1701
int network_add_default_route_on_device(Network *network) {
7,549✔
1702
        _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
7,549✔
1703
        unsigned section_line;
7,549✔
1704
        int r;
7,549✔
1705

1706
        assert(network);
7,549✔
1707

1708
        if (!network->default_route_on_device)
7,549✔
1709
                return 0;
1710

1711
        r = hashmap_by_section_find_unused_line(network->routes_by_section, network->filename, &section_line);
10✔
1712
        if (r < 0)
10✔
1713
                return r;
1714

1715
        /* DefaultRouteOnDevice= is in [Network] section. */
1716
        r = route_new_static(network, network->filename, section_line, &route);
10✔
1717
        if (r < 0)
10✔
1718
                return r;
1719

1720
        route->family = AF_INET;
10✔
1721
        route->scope = RT_SCOPE_LINK;
10✔
1722
        route->scope_set = true;
10✔
1723
        route->protocol = RTPROT_STATIC;
10✔
1724

1725
        TAKE_PTR(route);
10✔
1726
        return 0;
10✔
1727
}
1728

1729
static int config_parse_preferred_src(
10✔
1730
                const char *unit,
1731
                const char *filename,
1732
                unsigned line,
1733
                const char *section,
1734
                unsigned section_line,
1735
                const char *lvalue,
1736
                int ltype,
1737
                const char *rvalue,
1738
                void *data,
1739
                void *userdata) {
1740

1741
        Route *route = ASSERT_PTR(userdata);
10✔
1742
        int r;
10✔
1743

1744
        if (isempty(rvalue)) {
10✔
1745
                route->prefsrc_set = false;
×
1746
                route->prefsrc = IN_ADDR_NULL;
×
1747
                return 1;
×
1748
        }
1749

1750
        r = parse_boolean(rvalue);
10✔
1751
        if (r == 0) {
10✔
1752
                /* Accepts only no. That prohibits prefsrc set by DHCP lease. */
1753
                route->prefsrc_set = true;
×
1754
                route->prefsrc = IN_ADDR_NULL;
×
1755
                return 1;
×
1756
        }
1757

1758
        if (route->family == AF_UNSPEC)
10✔
1759
                r = in_addr_from_string_auto(rvalue, &route->family, &route->prefsrc);
2✔
1760
        else
1761
                r = in_addr_from_string(route->family, rvalue, &route->prefsrc);
8✔
1762
        if (r < 0)
10✔
1763
                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
×
1764

1765
        route->prefsrc_set = true;
10✔
1766
        return 1;
10✔
1767
}
1768

1769
static int config_parse_route_destination(
347✔
1770
                const char *unit,
1771
                const char *filename,
1772
                unsigned line,
1773
                const char *section,
1774
                unsigned section_line,
1775
                const char *lvalue,
1776
                int ltype,
1777
                const char *rvalue,
1778
                void *data,
1779
                void *userdata) {
1780

1781
        Route *route = ASSERT_PTR(userdata);
347✔
1782
        union in_addr_union *buffer;
347✔
1783
        unsigned char *prefixlen;
347✔
1784
        int r;
347✔
1785

1786
        assert(lvalue);
347✔
1787
        assert(rvalue);
347✔
1788

1789
        if (streq(lvalue, "Destination")) {
347✔
1790
                buffer = &route->dst;
343✔
1791
                prefixlen = &route->dst_prefixlen;
343✔
1792
        } else if (streq(lvalue, "Source")) {
4✔
1793
                buffer = &route->src;
4✔
1794
                prefixlen = &route->src_prefixlen;
4✔
1795
        } else
1796
                assert_not_reached();
×
1797

1798
        if (route->family == AF_UNSPEC)
347✔
1799
                r = in_addr_prefix_from_string_auto(rvalue, &route->family, buffer, prefixlen);
343✔
1800
        else
1801
                r = in_addr_prefix_from_string(rvalue, route->family, buffer, prefixlen);
4✔
1802
        if (r < 0)
347✔
1803
                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
×
1804

1805
        (void) in_addr_mask(route->family, buffer, *prefixlen);
347✔
1806
        return 1;
347✔
1807
}
1808

1809
static int config_parse_route_priority(
15✔
1810
                const char *unit,
1811
                const char *filename,
1812
                unsigned line,
1813
                const char *section,
1814
                unsigned section_line,
1815
                const char *lvalue,
1816
                int ltype,
1817
                const char *rvalue,
1818
                void *data,
1819
                void *userdata) {
1820

1821
        Route *route = ASSERT_PTR(userdata);
15✔
1822
        int r;
15✔
1823

1824
        assert(rvalue);
15✔
1825

1826
        r = safe_atou32(rvalue, &route->priority);
15✔
1827
        if (r < 0)
15✔
1828
                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
×
1829

1830
        route->priority_set = true;
15✔
1831
        return 1;
15✔
1832
}
1833

1834
static int config_parse_route_scope(
2✔
1835
                const char *unit,
1836
                const char *filename,
1837
                unsigned line,
1838
                const char *section,
1839
                unsigned section_line,
1840
                const char *lvalue,
1841
                int ltype,
1842
                const char *rvalue,
1843
                void *data,
1844
                void *userdata) {
1845

1846
        Route *route = ASSERT_PTR(userdata);
2✔
1847
        int r;
2✔
1848

1849
        assert(rvalue);
2✔
1850

1851
        r = route_scope_from_string(rvalue);
2✔
1852
        if (r < 0)
2✔
1853
                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
×
1854

1855
        route->scope = r;
2✔
1856
        route->scope_set = true;
2✔
1857
        return 1;
2✔
1858
}
1859

1860
static int config_parse_route_table(
6✔
1861
                const char *unit,
1862
                const char *filename,
1863
                unsigned line,
1864
                const char *section,
1865
                unsigned section_line,
1866
                const char *lvalue,
1867
                int ltype,
1868
                const char *rvalue,
1869
                void *data,
1870
                void *userdata) {
1871

1872
        Route *route = ASSERT_PTR(userdata);
6✔
1873
        Manager *manager = ASSERT_PTR(ASSERT_PTR(route->network)->manager);
6✔
1874
        int r;
6✔
1875

1876
        assert(rvalue);
6✔
1877

1878
        r = manager_get_route_table_from_string(manager, rvalue, &route->table);
6✔
1879
        if (r < 0)
6✔
1880
                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
×
1881

1882
        route->table_set = true;
6✔
1883
        return 1;
6✔
1884
}
1885

1886
static int config_parse_route_preference(
×
1887
                const char *unit,
1888
                const char *filename,
1889
                unsigned line,
1890
                const char *section,
1891
                unsigned section_line,
1892
                const char *lvalue,
1893
                int ltype,
1894
                const char *rvalue,
1895
                void *data,
1896
                void *userdata) {
1897

1898
        Route *route = ASSERT_PTR(userdata);
×
1899

1900
        assert(rvalue);
×
1901

1902
        if (streq(rvalue, "low"))
×
1903
                route->pref = SD_NDISC_PREFERENCE_LOW;
×
1904
        else if (streq(rvalue, "medium"))
×
1905
                route->pref = SD_NDISC_PREFERENCE_MEDIUM;
×
1906
        else if (streq(rvalue, "high"))
×
1907
                route->pref = SD_NDISC_PREFERENCE_HIGH;
×
1908
        else
1909
                return log_syntax_parse_error(unit, filename, line, 0, lvalue, rvalue);
×
1910

1911
        route->pref_set = true;
×
1912
        return 1;
×
1913
}
1914

1915
static int config_parse_route_protocol(
×
1916
                const char *unit,
1917
                const char *filename,
1918
                unsigned line,
1919
                const char *section,
1920
                unsigned section_line,
1921
                const char *lvalue,
1922
                int ltype,
1923
                const char *rvalue,
1924
                void *data,
1925
                void *userdata) {
1926

1927
        unsigned char *p = ASSERT_PTR(data);
×
1928
        int r;
×
1929

1930
        assert(rvalue);
×
1931

1932
        r = route_protocol_from_string(rvalue);
×
1933
        if (r < 0)
×
1934
                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
×
1935

1936
        *p = (unsigned char) r;
×
1937
        return 1;
×
1938
}
1939

1940
static int config_parse_route_type(
100✔
1941
                const char *unit,
1942
                const char *filename,
1943
                unsigned line,
1944
                const char *section,
1945
                unsigned section_line,
1946
                const char *lvalue,
1947
                int ltype,
1948
                const char *rvalue,
1949
                void *data,
1950
                void *userdata) {
1951

1952
        unsigned char *p = ASSERT_PTR(data);
100✔
1953
        int r;
100✔
1954

1955
        assert(rvalue);
100✔
1956

1957
        r = route_type_from_string(rvalue);
100✔
1958
        if (r < 0)
100✔
1959
                return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
×
1960

1961
        *p = (unsigned char) r;
100✔
1962
        return 1;
100✔
1963
}
1964

1965
int config_parse_route_section(
885✔
1966
                const char *unit,
1967
                const char *filename,
1968
                unsigned line,
1969
                const char *section,
1970
                unsigned section_line,
1971
                const char *lvalue,
1972
                int ltype,
1973
                const char *rvalue,
1974
                void *data,
1975
                void *userdata) {
1976

1977
        static const ConfigSectionParser table[_ROUTE_CONF_PARSER_MAX] = {
885✔
1978
                [ROUTE_DESTINATION]               = { .parser = config_parse_route_destination,      .ltype = 0,                       .offset = 0,                                                   },
1979
                [ROUTE_PREFERRED_SOURCE]          = { .parser = config_parse_preferred_src,          .ltype = 0,                       .offset = 0,                                                   },
1980
                [ROUTE_PRIORITY]                  = { .parser = config_parse_route_priority,         .ltype = 0,                       .offset = 0,                                                   },
1981
                [ROUTE_SCOPE]                     = { .parser = config_parse_route_scope,            .ltype = 0,                       .offset = 0,                                                   },
1982
                [ROUTE_TABLE]                     = { .parser = config_parse_route_table,            .ltype = 0,                       .offset = 0,                                                   },
1983
                [ROUTE_PREFERENCE]                = { .parser = config_parse_route_preference,       .ltype = 0,                       .offset = 0,                                                   },
1984
                [ROUTE_PROTOCOL]                  = { .parser = config_parse_route_protocol,         .ltype = 0,                       .offset = offsetof(Route, protocol),                           },
1985
                [ROUTE_TYPE]                      = { .parser = config_parse_route_type,             .ltype = 0,                       .offset = offsetof(Route, type),                               },
1986
                [ROUTE_GATEWAY_NETWORK]           = { .parser = config_parse_gateway,                .ltype = 0,                       .offset = 0,                                                   },
1987
                [ROUTE_GATEWAY]                   = { .parser = config_parse_gateway,                .ltype = 1,                       .offset = 0,                                                   },
1988
                [ROUTE_GATEWAY_ONLINK]            = { .parser = config_parse_tristate,               .ltype = 0,                       .offset = offsetof(Route, gateway_onlink),                     },
1989
                [ROUTE_MULTIPATH]                 = { .parser = config_parse_multipath_route,        .ltype = 0,                       .offset = offsetof(Route, nexthops),                           },
1990
                [ROUTE_NEXTHOP]                   = { .parser = config_parse_route_nexthop,          .ltype = 0,                       .offset = offsetof(Route, nexthop_id),                         },
1991
                [ROUTE_METRIC_MTU]                = { .parser = config_parse_route_metric,           .ltype = RTAX_MTU,                .offset = 0,                                                   },
1992
                [ROUTE_METRIC_ADVMSS]             = { .parser = config_parse_route_metric,           .ltype = RTAX_ADVMSS,             .offset = 0,                                                   },
1993
                [ROUTE_METRIC_HOPLIMIT]           = { .parser = config_parse_route_metric,           .ltype = RTAX_HOPLIMIT,           .offset = 0,                                                   },
1994
                [ROUTE_METRIC_INITCWND]           = { .parser = config_parse_route_metric,           .ltype = RTAX_INITCWND,           .offset = 0,                                                   },
1995
                [ROUTE_METRIC_RTO_MIN]            = { .parser = config_parse_route_metric,           .ltype = RTAX_RTO_MIN,            .offset = 0,                                                   },
1996
                [ROUTE_METRIC_INITRWND]           = { .parser = config_parse_route_metric,           .ltype = RTAX_INITRWND,           .offset = 0,                                                   },
1997
                [ROUTE_METRIC_QUICKACK]           = { .parser = config_parse_route_metric,           .ltype = RTAX_QUICKACK,           .offset = 0,                                                   },
1998
                [ROUTE_METRIC_CC_ALGO]            = { .parser = config_parse_string,                 .ltype = 0,                       .offset = offsetof(Route, metric.tcp_congestion_control_algo), },
1999
                [ROUTE_METRIC_FASTOPEN_NO_COOKIE] = { .parser = config_parse_route_metric,           .ltype = RTAX_FASTOPEN_NO_COOKIE, .offset = 0,                                                   },
2000
        };
2001

2002
        _cleanup_(route_unref_or_set_invalidp) Route *route = NULL;
885✔
2003
        Network *network = ASSERT_PTR(userdata);
885✔
2004
        int r;
885✔
2005

2006
        assert(filename);
885✔
2007

2008
        if (streq(section, "Network")) {
885✔
2009
                assert(streq_ptr(lvalue, "Gateway"));
64✔
2010

2011
                /* Clear all previously defined routes when Gateway= (empty) is set in [Network] section */
2012
                if (isempty(rvalue)) {
64✔
2013
                        network->routes_by_section = hashmap_free(network->routes_by_section);
1✔
2014
                        return 0;
1✔
2015
                }
2016

2017
                /* we are not in an Route section, so use line number instead */
2018
                r = route_new_static(network, filename, line, &route);
63✔
2019
        } else
2020
                r = route_new_static(network, filename, section_line, &route);
821✔
2021
        if (r == -ENOMEM)
884✔
2022
                return log_oom();
×
2023
        if (r < 0) {
884✔
2024
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
2025
                           "Failed to allocate route, ignoring assignment: %m");
2026
                return 0;
×
2027
        }
2028

2029
        r = config_section_parse(table, ELEMENTSOF(table),
884✔
2030
                                 unit, filename, line, section, section_line, lvalue, ltype, rvalue, route);
2031
        if (r <= 0)
884✔
2032
                return r;
2033

2034
        TAKE_PTR(route);
884✔
2035
        return 0;
884✔
2036
}
2037

2038
int route_section_verify(Route *route) {
563✔
2039
        int r;
563✔
2040

2041
        assert(route);
563✔
2042
        assert(route->section);
563✔
2043

2044
        if (section_is_invalid(route->section))
563✔
2045
                return -EINVAL;
2046

2047
        /* Currently, we do not support static route with finite lifetime. */
2048
        assert(route->lifetime_usec == USEC_INFINITY);
563✔
2049

2050
        r = route_section_verify_nexthops(route);
563✔
2051
        if (r < 0)
563✔
2052
                return r;
2053

2054
        /* table */
2055
        if (!route->table_set && route->network && route->network->vrf) {
563✔
2056
                route->table = VRF(route->network->vrf)->table;
1✔
2057
                route->table_set = true;
1✔
2058
        }
2059

2060
        if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
563✔
2061
                route->table = RT_TABLE_LOCAL;
30✔
2062

2063
        /* scope */
2064
        if (!route->scope_set && route->family == AF_INET) {
563✔
2065
                if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
313✔
2066
                        route->scope = RT_SCOPE_HOST;
10✔
2067
                else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
303✔
2068
                        route->scope = RT_SCOPE_LINK;
30✔
2069
                else if (IN_SET(route->type, RTN_UNICAST, RTN_UNSPEC) &&
273✔
2070
                         !route->gateway_from_dhcp_or_ra &&
243✔
2071
                         !in_addr_is_set(route->nexthop.family, &route->nexthop.gw) &&
239✔
2072
                         ordered_set_isempty(route->nexthops) &&
142✔
2073
                         route->nexthop_id == 0)
112✔
2074
                        route->scope = RT_SCOPE_LINK;
70✔
2075
        }
2076

2077
        /* IPv6 route */
2078
        if (route->family == AF_INET6) {
563✔
2079
                if (route->scope != RT_SCOPE_UNIVERSE) {
230✔
2080
                        log_section_warning(route->section, "Scope= is specified for IPv6 route. It will be ignored.");
2✔
2081
                        route->scope = RT_SCOPE_UNIVERSE;
2✔
2082
                }
2083

2084
                if (route->priority == 0)
230✔
2085
                        route->priority = IP6_RT_PRIO_USER;
126✔
2086
        }
2087

2088
        return 0;
2089
}
2090

2091
void network_drop_invalid_routes(Network *network) {
6,514✔
2092
        Route *route;
6,514✔
2093

2094
        assert(network);
6,514✔
2095

2096
        HASHMAP_FOREACH(route, network->routes_by_section)
13,495✔
2097
                if (route_section_verify(route) < 0)
467✔
2098
                        route_detach(route);
×
2099
}
6,514✔
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