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

systemd / systemd / 20151578145

11 Dec 2025 05:38AM UTC coverage: 72.698% (-0.02%) from 72.713%
20151578145

push

github

web-flow
core: gracefully skip unknown policy designators in RootImagePolicy et al (#40060)

Usually we gracefully ignore unknown configuration parameters, so that
service files can be written by upstreams and used across a variegated
range of distributions with various versions of systemd, to avoid
forcing users to the minimum common denominator and only adding settings
that are supported by the oldest distro supported.

Image policies do not behave like this, and any unknown partition or
policy designator causes the whole unit to fail to parse and a hard
error.

Change it so that parsing RootImagePolicy and friends via unit file or
D-Bus logs but otherwise ignores unknown specifiers, like other options
do.

This allows us to add new specifiers in the future, and users to adopt
them immediately.

Follow-up for d452335aa

44 of 49 new or added lines in 7 files covered. (89.8%)

297 existing lines in 37 files now uncovered.

309479 of 425707 relevant lines covered (72.7%)

1150153.67 hits per line

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

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

3
#include <linux/if_arp.h>
4

5
#include "sd-messages.h"
6

7
#include "af-list.h"
8
#include "bpf-dlopen.h"
9
#include "conf-parser.h"
10
#include "alloc-util.h"
11
#include "cgroup-util.h"
12
#include "errno-util.h"
13
#include "event-util.h"
14
#include "fd-util.h"
15
#include "format-util.h"
16
#include "hashmap.h"
17
#include "networkd-link.h"
18
#include "networkd-lldp-tx.h"
19
#include "networkd-manager.h"
20
#include "networkd-ndisc.h"
21
#include "networkd-network.h"
22
#include "networkd-sysctl.h"
23
#include "path-util.h"
24
#include "set.h"
25
#include "socket-util.h"
26
#include "string-table.h"
27
#include "string-util.h"
28
#include "sysctl-util.h"
29

30
#if ENABLE_SYSCTL_BPF
31

32
#include "bpf-link.h"
33
#include "bpf/sysctl-monitor/sysctl-monitor-skel.h"
34
#include "bpf/sysctl-monitor/sysctl-write-event.h"
35

36
static struct sysctl_monitor_bpf* sysctl_monitor_bpf_free(struct sysctl_monitor_bpf *obj) {
471✔
37
        sysctl_monitor_bpf__destroy(obj);
471✔
38
        return NULL;
471✔
39
}
40

41
DEFINE_TRIVIAL_CLEANUP_FUNC(struct sysctl_monitor_bpf *, sysctl_monitor_bpf_free);
446✔
42

43
static int sysctl_event_handler(void *ctx, void *data, size_t data_sz) {
17✔
44
        struct sysctl_write_event *we = ASSERT_PTR(data);
17✔
45
        Hashmap **sysctl_shadow = ASSERT_PTR(ctx);
17✔
46
        _cleanup_free_ char *path = NULL;
17✔
47
        char *value;
17✔
48

49
        /* Returning a negative value interrupts the ring buffer polling,
50
         * so do it only in case of a fatal error like a version mismatch. */
51
        if (we->version != 1)
17✔
52
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
×
53
                                         "Unexpected sysctl event, disabling sysctl monitoring: %d", we->version);
54

55
        if (we->errorcode != 0) {
17✔
56
                /* The log message is checked in test-network/systemd-networkd-tests.py. Please update the
57
                 * test when the log message is changed. */
UNCOV
58
                log_warning_errno(we->errorcode, "Sysctl monitor BPF returned error: %m");
×
UNCOV
59
                return 0;
×
60
        }
61

62
        path = path_join("/proc/sys", we->path);
17✔
63
        if (!path) {
17✔
UNCOV
64
                log_oom_warning();
×
65
                return 0;
66
        }
67

68
        /* If we never managed this handle, ignore it. */
69
        value = hashmap_get(*sysctl_shadow, path);
17✔
70
        if (!value)
17✔
71
                return 0;
72

73
        if (!strneq(value, we->newvalue, sizeof(we->newvalue)))
4✔
74
                /* The log message is checked in test-network/systemd-networkd-tests.py. Please update the
75
                 * test when the log message is changed. */
76
                log_struct(LOG_WARNING,
4✔
77
                           LOG_MESSAGE_ID(SD_MESSAGE_SYSCTL_CHANGED_STR),
78
                           LOG_ITEM("OBJECT_PID=" PID_FMT, we->pid),
79
                           LOG_ITEM("OBJECT_COMM=%s", we->comm),
80
                           LOG_ITEM("SYSCTL=%s", path),
81
                           LOG_ITEM("OLDVALUE=%s", we->current),
82
                           LOG_ITEM("NEWVALUE=%s", we->newvalue),
83
                           LOG_ITEM("OURVALUE=%s", value),
84
                           LOG_MESSAGE("Foreign process '%s[" PID_FMT "]' changed sysctl '%s' from '%s' to '%s', conflicting with our setting to '%s'.",
85
                                       we->comm, we->pid, path, we->current, we->newvalue, value));
86

87
        return 0;
88
}
89

90
static int on_ringbuf_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
16✔
91
        struct ring_buffer *rb = ASSERT_PTR(userdata);
16✔
92
        int r;
16✔
93

94
        r = sym_ring_buffer__poll(rb, /* timeout_msec= */ 0);
16✔
95
        if (r < 0 && errno != EINTR)
16✔
UNCOV
96
                log_error_errno(errno, "Error polling ring buffer: %m");
×
97

98
        return 0;
16✔
99
}
100

101
int manager_install_sysctl_monitor(Manager *manager) {
446✔
102
        _cleanup_(sysctl_monitor_bpf_freep) struct sysctl_monitor_bpf *obj = NULL;
446✔
103
        _cleanup_(bpf_link_freep) struct bpf_link *sysctl_link = NULL;
446✔
UNCOV
104
        _cleanup_(bpf_ring_buffer_freep) struct ring_buffer *sysctl_buffer = NULL;
×
105
        _cleanup_close_ int cgroup_fd = -EBADF, root_cgroup_fd = -EBADF;
892✔
106
        _cleanup_free_ char *cgroup = NULL;
446✔
107
        int idx = 0, r, fd;
446✔
108

109
        assert(manager);
446✔
110

111
        r = dlopen_bpf();
446✔
112
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
446✔
UNCOV
113
                return log_debug_errno(r, "sysctl monitor disabled, as BPF support is not available.");
×
114
        if (r < 0)
446✔
115
                return log_warning_errno(r, "Failed to load libbpf, not installing sysctl monitor: %m");
×
116

117
        r = cg_pid_get_path(0, &cgroup);
446✔
118
        if (r < 0)
446✔
119
                return log_warning_errno(r, "Failed to get cgroup path, ignoring: %m.");
×
120

121
        root_cgroup_fd = cg_path_open("/");
446✔
122
        if (root_cgroup_fd < 0)
446✔
UNCOV
123
                return log_warning_errno(root_cgroup_fd, "Failed to open cgroup, ignoring: %m");
×
124

125
        obj = sysctl_monitor_bpf__open_and_load();
446✔
126
        if (!obj)
446✔
127
                return log_full_errno(errno == EINVAL ? LOG_DEBUG : LOG_INFO, errno,
59✔
128
                                      "Unable to load sysctl monitor BPF program, ignoring: %m");
129

130
        cgroup_fd = cg_path_open(cgroup);
387✔
131
        if (cgroup_fd < 0)
387✔
UNCOV
132
                return log_warning_errno(cgroup_fd, "Failed to open cgroup: %m");
×
133

134
        if (sym_bpf_map_update_elem(sym_bpf_map__fd(obj->maps.cgroup_map), &idx, &cgroup_fd, BPF_ANY))
387✔
UNCOV
135
                return log_warning_errno(errno, "Failed to update cgroup map: %m");
×
136

137
        sysctl_link = sym_bpf_program__attach_cgroup(obj->progs.sysctl_monitor, root_cgroup_fd);
387✔
138
        r = bpf_get_error_translated(sysctl_link);
387✔
139
        if (r < 0)
387✔
140
                return log_warning_errno(r, "Unable to attach sysctl monitor BPF program to cgroup, ignoring: %m");
×
141

142
        fd = sym_bpf_map__fd(obj->maps.written_sysctls);
387✔
143
        if (fd < 0)
387✔
144
                return log_warning_errno(fd, "Failed to get fd of sysctl maps: %m");
×
145

146
        sysctl_buffer = sym_ring_buffer__new(fd, sysctl_event_handler, &manager->sysctl_shadow, NULL);
387✔
147
        if (!sysctl_buffer)
387✔
148
                return log_warning_errno(errno, "Failed to create ring buffer: %m");
×
149

150
        fd = sym_ring_buffer__epoll_fd(sysctl_buffer);
387✔
151
        if (fd < 0)
387✔
UNCOV
152
                return log_warning_errno(fd, "Failed to get poll fd of ring buffer: %m");
×
153

154
        r = sd_event_add_io(manager->event, &manager->sysctl_event_source,
387✔
155
                            fd, EPOLLIN, on_ringbuf_io, sysctl_buffer);
156
        if (r < 0)
387✔
UNCOV
157
                return log_warning_errno(r, "Failed to watch sysctl event ringbuffer: %m");
×
158

159
        manager->sysctl_link = TAKE_PTR(sysctl_link);
387✔
160
        manager->sysctl_skel = TAKE_PTR(obj);
387✔
161
        manager->sysctl_buffer = TAKE_PTR(sysctl_buffer);
387✔
162
        manager->cgroup_fd = TAKE_FD(cgroup_fd);
387✔
163

164
        return 0;
387✔
165
}
166

167
void manager_remove_sysctl_monitor(Manager *manager) {
471✔
168
        assert(manager);
471✔
169

170
        manager->sysctl_event_source = sd_event_source_disable_unref(manager->sysctl_event_source);
471✔
171
        manager->sysctl_buffer = bpf_ring_buffer_free(manager->sysctl_buffer);
471✔
172
        manager->sysctl_link = bpf_link_free(manager->sysctl_link);
471✔
173
        manager->sysctl_skel = sysctl_monitor_bpf_free(manager->sysctl_skel);
471✔
174
        manager->cgroup_fd = safe_close(manager->cgroup_fd);
471✔
175
        manager->sysctl_shadow = hashmap_free(manager->sysctl_shadow);
471✔
176
}
471✔
177

178
int link_clear_sysctl_shadows(Link *link) {
2,817✔
179
        _cleanup_free_ char *ipv4 = NULL, *ipv6 = NULL;
2,817✔
180
        char *key = NULL, *value = NULL;
2,817✔
181

182
        assert(link);
2,817✔
183
        assert(link->manager);
2,817✔
184

185
        ipv4 = path_join("/proc/sys/net/ipv4/conf", link->ifname);
2,817✔
186
        if (!ipv4)
2,817✔
187
                return log_oom();
×
188

189
        ipv6 = path_join("/proc/sys/net/ipv6/conf", link->ifname);
2,817✔
190
        if (!ipv6)
2,817✔
UNCOV
191
                return log_oom();
×
192

193
        HASHMAP_FOREACH_KEY(value, key, link->manager->sysctl_shadow)
9,735✔
194
                if (path_startswith(key, ipv4) || path_startswith(key, ipv6)) {
6,918✔
195
                        assert_se(hashmap_remove_value(link->manager->sysctl_shadow, key, value));
3,115✔
196
                        free(key);
3,115✔
197
                        free(value);
3,115✔
198
                }
199

200
        return 0;
2,817✔
201
}
202
#endif
203

204
static void manager_set_ip_forwarding(Manager *manager, int family) {
896✔
205
        int r, t;
896✔
206

207
        assert(manager);
896✔
208
        assert(IN_SET(family, AF_INET, AF_INET6));
896✔
209

210
        if (family == AF_INET6 && !socket_ipv6_is_supported())
896✔
211
                return;
212

213
        t = manager->ip_forwarding[family == AF_INET6];
896✔
214
        if (t < 0)
896✔
215
                return; /* keep */
216

217
        /* First, set the default value. */
218
        r = sysctl_write_ip_property_boolean(family, "default", "forwarding", t, manager_get_sysctl_shadow(manager));
4✔
219
        if (r < 0)
4✔
UNCOV
220
                log_warning_errno(r, "Failed to %s the default %s forwarding: %m",
×
221
                                  enable_disable(t), af_to_ipv4_ipv6(family));
222

223
        /* Then, set the value to all interfaces. */
224
        r = sysctl_write_ip_property_boolean(family, "all", "forwarding", t, manager_get_sysctl_shadow(manager));
4✔
225
        if (r < 0)
4✔
UNCOV
226
                log_warning_errno(r, "Failed to %s %s forwarding for all interfaces: %m",
×
227
                                  enable_disable(t), af_to_ipv4_ipv6(family));
228
}
229

230
void manager_set_sysctl(Manager *manager) {
446✔
231
        assert(manager);
446✔
232
        assert(!manager->test_mode);
446✔
233

234
        manager_set_ip_forwarding(manager, AF_INET);
446✔
235
        manager_set_ip_forwarding(manager, AF_INET6);
446✔
236
}
446✔
237

238
static bool link_is_configured_for_family(Link *link, int family) {
18,029✔
239
        assert(link);
18,029✔
240

241
        if (!link->network)
18,029✔
242
                return false;
243

244
        if (link->flags & IFF_LOOPBACK)
18,029✔
245
                return false;
246

247
        /* CAN devices do not support IP layer. Most of the functions below are never called for CAN devices,
248
         * but link_set_ipv6_mtu() may be called after setting interface MTU, and warn about the failure. For
249
         * safety, let's unconditionally check if the interface is not a CAN device. */
250
        if (IN_SET(family, AF_INET, AF_INET6, AF_MPLS) && link->iftype == ARPHRD_CAN)
18,029✔
251
                return false;
252

253
        if (family == AF_INET6 && !socket_ipv6_is_supported())
18,025✔
UNCOV
254
                return false;
×
255

256
        return true;
257
}
258

259
static int link_update_ipv6_sysctl(Link *link) {
864✔
260
        assert(link);
864✔
261
        assert(link->manager);
864✔
262

263
        if (!link_is_configured_for_family(link, AF_INET6))
864✔
264
                return 0;
265

266
        if (!link_ipv6_enabled(link))
864✔
267
                return 0;
268

269
        return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false, manager_get_sysctl_shadow(link->manager));
792✔
270
}
271

272
static int link_set_proxy_arp(Link *link) {
864✔
273
        assert(link);
864✔
274
        assert(link->manager);
864✔
275

276
        if (!link_is_configured_for_family(link, AF_INET))
864✔
277
                return 0;
278

279
        if (link->network->proxy_arp < 0)
864✔
280
                return 0;
281

282
        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0, manager_get_sysctl_shadow(link->manager));
2✔
283
}
284

285
static int link_set_proxy_arp_pvlan(Link *link) {
864✔
286
        assert(link);
864✔
287
        assert(link->manager);
864✔
288

289
        if (!link_is_configured_for_family(link, AF_INET))
864✔
290
                return 0;
291

292
        if (link->network->proxy_arp_pvlan < 0)
864✔
293
                return 0;
294

295
        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp_pvlan", link->network->proxy_arp_pvlan > 0, manager_get_sysctl_shadow(link->manager));
2✔
296
}
297

298
int link_get_ip_forwarding(Link *link, int family) {
2,301✔
299
        assert(link);
2,301✔
300
        assert(link->manager);
2,301✔
301
        assert(link->network);
2,301✔
302
        assert(IN_SET(family, AF_INET, AF_INET6));
2,301✔
303

304
        /* If it is explicitly specified, then honor the setting. */
305
        int t = link->network->ip_forwarding[family == AF_INET6];
2,301✔
306
        if (t >= 0)
2,301✔
307
                return t;
308

309
        /* If IPMasquerade= is enabled, also enable IP forwarding. */
310
        if (FLAGS_SET(link->network->ip_masquerade, AF_TO_ADDRESS_FAMILY(family)))
3,610✔
311
                return true;
312

313
        /* If IPv6SendRA= is enabled, also enable IPv6 forwarding. */
314
        if (family == AF_INET6 && link_radv_enabled(link))
2,200✔
315
                return true;
316

317
        /* Otherwise, use the global setting. */
318
        return link->manager->ip_forwarding[family == AF_INET6];
2,167✔
319
}
320

321
static int link_set_ip_forwarding_impl(Link *link, int family) {
1,730✔
322
        int r, t;
1,730✔
323

324
        assert(link);
1,730✔
325
        assert(link->manager);
1,730✔
326
        assert(IN_SET(family, AF_INET, AF_INET6));
1,730✔
327

328
        if (!link_is_configured_for_family(link, family))
1,730✔
329
                return 0;
330

331
        t = link_get_ip_forwarding(link, family);
1,730✔
332
        if (t < 0)
1,730✔
333
                return 0; /* keep */
334

335
        r = sysctl_write_ip_property_boolean(family, link->ifname, "forwarding", t, manager_get_sysctl_shadow(link->manager));
123✔
336
        if (r < 0)
123✔
UNCOV
337
                return log_link_warning_errno(link, r, "Failed to %s %s forwarding, ignoring: %m",
×
338
                                              enable_disable(t), af_to_ipv4_ipv6(family));
339

340
        return 0;
341
}
342

343
static int link_reapply_ip_forwarding(Link *link, int family) {
10✔
344
        int r, ret = 0;
10✔
345

346
        assert(link);
10✔
347
        assert(IN_SET(family, AF_INET, AF_INET6));
10✔
348

349
        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
10✔
350
                return 0;
10✔
351

352
        (void) link_set_ip_forwarding_impl(link, family);
6✔
353

354
        r = link_lldp_tx_update_capabilities(link);
6✔
355
        if (r < 0)
6✔
UNCOV
356
                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not update LLDP capabilities, ignoring: %m"));
×
357

358
        if (family == AF_INET6 && !link_ndisc_enabled(link)) {
6✔
359
                r = ndisc_stop(link);
3✔
360
                if (r < 0)
3✔
UNCOV
361
                        RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery, ignoring: %m"));
×
362

363
                ndisc_flush(link);
3✔
364
        }
365

366
        return ret;
367
}
368

369
static int link_set_ip_forwarding(Link *link, int family) {
1,728✔
370
        int r;
1,728✔
371

372
        assert(link);
1,728✔
373
        assert(link->manager);
1,728✔
374
        assert(link->network);
1,728✔
375
        assert(IN_SET(family, AF_INET, AF_INET6));
1,728✔
376

377
        if (!link_is_configured_for_family(link, family))
1,728✔
378
                return 0;
379

380
        /* When IPMasquerade= is enabled and the global setting is unset, enable _global_ IP forwarding, and
381
         * re-apply per-link setting for all links. */
382
        if (FLAGS_SET(link->network->ip_masquerade, AF_TO_ADDRESS_FAMILY(family)) &&
2,592✔
383
            link->manager->ip_forwarding[family == AF_INET6] < 0) {
30✔
384

385
                log_link_notice(link, "IPMasquerade= is enabled on the interface, enabling the global IPv6Forwarding= setting, which may affect NDisc and DHCPv6 client on other interfaces.");
4✔
386

387
                link->manager->ip_forwarding[family == AF_INET6] = true;
4✔
388
                manager_set_ip_forwarding(link->manager, family);
4✔
389

390
                Link *other;
4✔
391
                HASHMAP_FOREACH(other, link->manager->links_by_index) {
18✔
392
                        r = link_reapply_ip_forwarding(other, family);
10✔
393
                        if (r < 0)
10✔
UNCOV
394
                                link_enter_failed(other);
×
395
                }
396

397
                return 0;
4✔
398
        }
399

400
        /* Otherwise, apply per-link setting for _this_ link. */
401
        return link_set_ip_forwarding_impl(link, family);
1,724✔
402
}
403

404
static int link_set_ipv4_rp_filter(Link *link) {
864✔
405
        assert(link);
864✔
406
        assert(link->manager);
864✔
407

408
        if (!link_is_configured_for_family(link, AF_INET))
864✔
409
                return 0;
410

411
        if (link->network->ipv4_rp_filter < 0)
864✔
412
                return 0;
413

414
        return sysctl_write_ip_property_int(AF_INET, link->ifname, "rp_filter", link->network->ipv4_rp_filter, manager_get_sysctl_shadow(link->manager));
2✔
415
}
416

417
static int link_set_ipv4_force_igmp_version(Link *link) {
864✔
418
        assert(link);
864✔
419

420
        if (!link_is_configured_for_family(link, AF_INET))
864✔
421
                return 0;
422

423
        if (link->network->ipv4_force_igmp_version < 0)
864✔
424
                return 0;
425

426
        return sysctl_write_ip_property_int(AF_INET, link->ifname, "force_igmp_version", link->network->ipv4_force_igmp_version, manager_get_sysctl_shadow(link->manager));
2✔
427
}
428

429
static int link_set_ipv6_privacy_extensions(Link *link) {
864✔
430
        IPv6PrivacyExtensions val;
864✔
431

432
        assert(link);
864✔
433
        assert(link->manager);
864✔
434

435
        if (!link_is_configured_for_family(link, AF_INET6))
864✔
436
                return 0;
437

438
        val = link->network->ipv6_privacy_extensions;
864✔
439
        if (val < 0) /* If not specified, then use the global setting. */
864✔
440
                val = link->manager->ipv6_privacy_extensions;
839✔
441

442
        /* When "kernel", do not update the setting. */
443
        if (val == IPV6_PRIVACY_EXTENSIONS_KERNEL)
864✔
444
                return 0;
445

446
        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) val, manager_get_sysctl_shadow(link->manager));
864✔
447
}
448

449
static int link_set_ipv6_accept_ra(Link *link) {
864✔
450
        assert(link);
864✔
451
        assert(link->manager);
864✔
452

453
        if (!link_is_configured_for_family(link, AF_INET6))
864✔
454
                return 0;
455

456
        return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0", manager_get_sysctl_shadow(link->manager));
864✔
457
}
458

459
static int link_set_ipv6_dad_transmits(Link *link) {
864✔
460
        assert(link);
864✔
461
        assert(link->manager);
864✔
462

463
        if (!link_is_configured_for_family(link, AF_INET6))
864✔
464
                return 0;
465

466
        if (link->network->ipv6_dad_transmits < 0)
864✔
467
                return 0;
468

469
        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits, manager_get_sysctl_shadow(link->manager));
2✔
470
}
471

472
static int link_set_ipv6_hop_limit(Link *link) {
864✔
473
        assert(link);
864✔
474
        assert(link->manager);
864✔
475

476
        if (!link_is_configured_for_family(link, AF_INET6))
864✔
477
                return 0;
478

479
        if (link->network->ipv6_hop_limit <= 0)
864✔
480
                return 0;
481

482
        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit, manager_get_sysctl_shadow(link->manager));
2✔
483
}
484

485
static int link_set_ipv6_retransmission_time(Link *link) {
864✔
486
        usec_t retrans_time_ms;
864✔
487

488
        assert(link);
864✔
489
        assert(link->manager);
864✔
490

491
        if (!link_is_configured_for_family(link, AF_INET6))
864✔
492
                return 0;
493

494
        if (!timestamp_is_set(link->network->ipv6_retransmission_time))
864✔
495
                return 0;
496

497
        retrans_time_ms = DIV_ROUND_UP(link->network->ipv6_retransmission_time, USEC_PER_MSEC);
2✔
498
         if (retrans_time_ms <= 0 || retrans_time_ms > UINT32_MAX)
2✔
499
                return 0;
500

501
        return sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", retrans_time_ms, manager_get_sysctl_shadow(link->manager));
2✔
502
}
503

504
static int link_set_ipv6_proxy_ndp(Link *link) {
864✔
505
        bool v;
864✔
506

507
        assert(link);
864✔
508
        assert(link->manager);
864✔
509

510
        if (!link_is_configured_for_family(link, AF_INET6))
864✔
511
                return 0;
512

513
        if (link->network->ipv6_proxy_ndp >= 0)
864✔
514
                v = link->network->ipv6_proxy_ndp;
2✔
515
        else
516
                v = !set_isempty(link->network->ipv6_proxy_ndp_addresses);
862✔
517

518
        return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v, manager_get_sysctl_shadow(link->manager));
864✔
519
}
520

521
int link_set_ipv6_mtu(Link *link, int log_level) {
1,611✔
522
        uint32_t mtu = 0;
1,611✔
523
        int r;
1,611✔
524

525
        assert(link);
1,611✔
526
        assert(link->manager);
1,611✔
527

528
        if (!link_is_configured_for_family(link, AF_INET6))
1,611✔
529
                return 0;
530

531
        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
1,607✔
532
                return 0;
533

534
        if (sd_event_source_get_enabled(link->ipv6_mtu_wait_synced_event_source, /* ret = */ NULL) > 0) {
1,607✔
535
                log_link_debug(link, "Waiting for IPv6 MTU is synced to link MTU, delaying to set IPv6 MTU.");
2✔
536
                return 0;
2✔
537
        }
538

539
        assert(link->network);
1,605✔
540

541
        if (link->network->ndisc_use_mtu)
1,605✔
542
                mtu = link->ndisc_mtu;
1,605✔
543
        if (mtu == 0)
1,605✔
544
                mtu = link->network->ipv6_mtu;
1,550✔
545
        if (mtu == 0)
1,550✔
546
                return 0;
547

548
        if (mtu > link->mtu) {
93✔
549
                log_link_full(link, log_level,
12✔
550
                              "Reducing requested IPv6 MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
551
                              mtu, link->mtu);
552
                mtu = link->mtu;
12✔
553
        }
554

555
        r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu, manager_get_sysctl_shadow(link->manager));
93✔
556
        if (r < 0)
93✔
UNCOV
557
                return log_link_warning_errno(link, r, "Failed to set IPv6 MTU to %"PRIu32": %m", mtu);
×
558

559
        return 0;
560
}
561

562
static int ipv6_mtu_wait_synced_handler(sd_event_source *s, uint64_t usec, void *userdata);
563

564
static int link_set_ipv6_mtu_async_impl(Link *link) {
49✔
565
        uint32_t current_mtu;
49✔
566
        int r;
49✔
567

568
        assert(link);
49✔
569

570
        /* When the link MTU is updated, it seems that the kernel IPv6 MTU of the interface is asynchronously
571
         * reset to the link MTU. Hence, we need to check if it is already reset, and wait for a while if not. */
572

573
        if (++link->ipv6_mtu_wait_trial_count >= 10) {
49✔
UNCOV
574
                log_link_debug(link, "Timed out waiting for IPv6 MTU being synced to link MTU, proceeding anyway.");
×
575
                r = link_set_ipv6_mtu(link, LOG_INFO);
×
UNCOV
576
                if (r < 0)
×
577
                        return r;
49✔
578

UNCOV
579
                return 1; /* done */
×
580
        }
581

582
        /* Check if IPv6 MTU is synced. */
583
        r = sysctl_read_ip_property_uint32(AF_INET6, link->ifname, "mtu", &current_mtu);
49✔
584
        if (r < 0)
49✔
585
                return log_link_warning_errno(link, r, "Failed to read IPv6 MTU: %m");
1✔
586

587
        if (current_mtu == link->mtu) {
48✔
588
                /* Already synced. Update IPv6 MTU now. */
589
                r = link_set_ipv6_mtu(link, LOG_INFO);
47✔
590
                if (r < 0)
47✔
591
                        return r;
592

593
                return 1; /* done */
47✔
594
        }
595

596
        /* If not, set up a timer event source. */
597
        r = event_reset_time_relative(
2✔
598
                        link->manager->event, &link->ipv6_mtu_wait_synced_event_source,
1✔
599
                        CLOCK_BOOTTIME, 100 * USEC_PER_MSEC, 0,
600
                        ipv6_mtu_wait_synced_handler, link,
601
                        /* priority = */ 0, "ipv6-mtu-wait-synced", /* force_reset = */ true);
602
        if (r < 0)
1✔
603
                return log_link_warning_errno(link, r, "Failed to configure timer event source for waiting for IPv6 MTU being synced: %m");
×
604

605
        /* Check again. */
606
        r = sysctl_read_ip_property_uint32(AF_INET6, link->ifname, "mtu", &current_mtu);
1✔
607
        if (r < 0)
1✔
608
                return log_link_warning_errno(link, r, "Failed to read IPv6 MTU: %m");
×
609

610
        if (current_mtu == link->mtu) {
1✔
611
                /* Synced while setting up the timer event source. Disable it and update IPv6 MTU now. */
612
                r = sd_event_source_set_enabled(link->ipv6_mtu_wait_synced_event_source, SD_EVENT_OFF);
×
613
                if (r < 0)
×
UNCOV
614
                        log_link_debug_errno(link, r, "Failed to disable timer event source for IPv6 MTU, ignoring: %m");
×
615

616
                r = link_set_ipv6_mtu(link, LOG_INFO);
×
UNCOV
617
                if (r < 0)
×
618
                        return r;
619

UNCOV
620
                return 1; /* done */
×
621
        }
622

623
        log_link_debug(link, "IPv6 MTU is not synced to the link MTU after it is changed. Waiting for a while.");
1✔
624
        return 0; /* waiting */
625
}
626

627
static int ipv6_mtu_wait_synced_handler(sd_event_source *s, uint64_t usec, void *userdata) {
1✔
628
        (void) link_set_ipv6_mtu_async_impl(ASSERT_PTR(userdata));
1✔
629
        return 0;
1✔
630
}
631

632
int link_set_ipv6_mtu_async(Link *link) {
48✔
633
        assert(link);
48✔
634

635
        link->ipv6_mtu_wait_trial_count = 0;
48✔
636
        return link_set_ipv6_mtu_async_impl(link);
48✔
637
}
638

639
static int link_set_ipv4_accept_local(Link *link) {
864✔
640
        assert(link);
864✔
641
        assert(link->manager);
864✔
642

643
        if (!link_is_configured_for_family(link, AF_INET))
864✔
644
                return 0;
645

646
        if (link->network->ipv4_accept_local < 0)
864✔
647
                return 0;
648

649
        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local > 0, manager_get_sysctl_shadow(link->manager));
2✔
650
}
651

652
static int link_set_ipv4_route_localnet(Link *link) {
864✔
653
        assert(link);
864✔
654
        assert(link->manager);
864✔
655

656
        if (!link_is_configured_for_family(link, AF_INET))
864✔
657
                return 0;
658

659
        if (link->network->ipv4_route_localnet < 0)
864✔
660
                return 0;
661

UNCOV
662
        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0, manager_get_sysctl_shadow(link->manager));
×
663
}
664

665
static int link_set_ipv4_promote_secondaries(Link *link) {
864✔
666
        assert(link);
864✔
667
        assert(link->manager);
864✔
668

669
        if (!link_is_configured_for_family(link, AF_INET))
864✔
670
                return 0;
671

672
        /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not
673
         * changes between leases. The kernel will remove all secondary IP addresses of an interface
674
         * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
675
         * secondary IP and when the primary one expires it relies on the kernel to promote the
676
         * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
677
        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true, manager_get_sysctl_shadow(link->manager));
864✔
678
}
679

680
static int link_set_mpls_input(Link *link) {
864✔
681
        assert(link);
864✔
682
        assert(link->manager);
864✔
683

684
        if (!link_is_configured_for_family(link, AF_MPLS))
864✔
685
                return 0;
686

687
        if (link->network->mpls_input < 0)
864✔
688
                return 0;
689

690
        return sysctl_write_ip_property_boolean(AF_MPLS, link->ifname, "input", link->network->mpls_input > 0, manager_get_sysctl_shadow(link->manager));
1✔
691
}
692

693
int link_set_sysctl(Link *link) {
864✔
694
        int r;
864✔
695

696
        assert(link);
864✔
697

698
        /* If IPv6 configured that is static IPv6 address and IPv6LL autoconfiguration is enabled
699
         * for this interface, then enable IPv6 */
700
        r = link_update_ipv6_sysctl(link);
864✔
701
        if (r < 0)
864✔
702
                log_link_warning_errno(link, r, "Cannot enable IPv6, ignoring: %m");
×
703

704
        r = link_set_proxy_arp(link);
864✔
705
        if (r < 0)
864✔
706
               log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface, ignoring: %m");
×
707

708
        r = link_set_proxy_arp_pvlan(link);
864✔
709
        if (r < 0)
864✔
UNCOV
710
                log_link_warning_errno(link, r, "Cannot configure proxy ARP private VLAN for interface, ignoring: %m");
×
711

712
        (void) link_set_ip_forwarding(link, AF_INET);
864✔
713
        (void) link_set_ip_forwarding(link, AF_INET6);
864✔
714

715
        r = link_set_ipv6_privacy_extensions(link);
864✔
716
        if (r < 0)
864✔
717
                log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extensions for interface, ignoring: %m");
2✔
718

719
        r = link_set_ipv6_accept_ra(link);
864✔
720
        if (r < 0)
864✔
721
                log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m");
2✔
722

723
        r = link_set_ipv6_dad_transmits(link);
864✔
724
        if (r < 0)
864✔
725
                log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface, ignoring: %m");
×
726

727
        r = link_set_ipv6_hop_limit(link);
864✔
728
        if (r < 0)
864✔
729
                log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface, ignoring: %m");
×
730

731
        r = link_set_ipv6_retransmission_time(link);
864✔
732
        if (r < 0)
864✔
UNCOV
733
                log_link_warning_errno(link, r, "Cannot set IPv6 retransmission time for interface, ignoring: %m");
×
734

735
        r = link_set_ipv6_proxy_ndp(link);
864✔
736
        if (r < 0)
864✔
737
                log_link_warning_errno(link, r, "Cannot set IPv6 proxy NDP, ignoring: %m");
2✔
738

739
        (void) link_set_ipv6_mtu(link, LOG_INFO);
864✔
740

741
        r = link_set_ipv6ll_stable_secret(link);
864✔
742
        if (r < 0)
864✔
743
                log_link_warning_errno(link, r, "Cannot set stable secret address for IPv6 link-local address: %m");
×
744

745
        r = link_set_ipv4_accept_local(link);
864✔
746
        if (r < 0)
864✔
747
                log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface, ignoring: %m");
×
748

749
        r = link_set_ipv4_route_localnet(link);
864✔
750
        if (r < 0)
864✔
751
                log_link_warning_errno(link, r, "Cannot set IPv4 route_localnet flag for interface, ignoring: %m");
×
752

753
        r = link_set_ipv4_rp_filter(link);
864✔
754
        if (r < 0)
864✔
755
                log_link_warning_errno(link, r, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m");
×
756

757
        r = link_set_ipv4_force_igmp_version(link);
864✔
758
        if (r < 0)
864✔
UNCOV
759
                log_link_warning_errno(link, r, "Cannot set IPv4 force igmp version, ignoring: %m");
×
760

761
        r = link_set_ipv4_promote_secondaries(link);
864✔
762
        if (r < 0)
864✔
763
                log_link_warning_errno(link, r, "Cannot enable promote_secondaries for interface, ignoring: %m");
2✔
764

765
        r = link_set_mpls_input(link);
864✔
766
        if (r < 0)
864✔
UNCOV
767
                log_link_warning_errno(link, r, "Cannot set MPLS input, ignoring: %m");
×
768

769
        return 0;
864✔
770
}
771

772
static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
773
        [IPV6_PRIVACY_EXTENSIONS_NO]            = "no",
774
        [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
775
        [IPV6_PRIVACY_EXTENSIONS_YES]           = "yes",
776
        [IPV6_PRIVACY_EXTENSIONS_KERNEL]        = "kernel",
777
};
778

779
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions,
76✔
780
                                        IPV6_PRIVACY_EXTENSIONS_YES);
781
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_privacy_extensions, ipv6_privacy_extensions, IPv6PrivacyExtensions);
22✔
782

783
static const char* const ip_reverse_path_filter_table[_IP_REVERSE_PATH_FILTER_MAX] = {
784
        [IP_REVERSE_PATH_FILTER_NO]     = "no",
785
        [IP_REVERSE_PATH_FILTER_STRICT] = "strict",
786
        [IP_REVERSE_PATH_FILTER_LOOSE]  = "loose",
787
};
788

789
DEFINE_STRING_TABLE_LOOKUP(ip_reverse_path_filter, IPReversePathFilter);
2✔
790
DEFINE_CONFIG_PARSE_ENUM(config_parse_ip_reverse_path_filter, ip_reverse_path_filter, IPReversePathFilter);
2✔
791

UNCOV
792
int config_parse_ip_forward_deprecated(
×
793
                const char *unit,
794
                const char *filename,
795
                unsigned line,
796
                const char *section,
797
                unsigned section_line,
798
                const char *lvalue,
799
                int ltype,
800
                const char *rvalue,
801
                void *data,
802
                void *userdata) {
803

UNCOV
804
        assert(filename);
×
805

806
        log_syntax(unit, LOG_WARNING, filename, line, 0,
×
807
                   "IPForward= setting is deprecated. "
808
                   "Please use IPv4Forwarding= and/or IPv6Forwarding= in networkd.conf for global setting, "
809
                   "and the same settings in .network files for per-interface setting.");
UNCOV
810
        return 0;
×
811
}
812

813
static const char* const ipv4_force_igmp_version_table[_IPV4_FORCE_IGMP_VERSION_MAX] = {
814
        [IPV4_FORCE_IGMP_VERSION_NO] = "no",
815
        [IPV4_FORCE_IGMP_VERSION_1]  = "v1",
816
        [IPV4_FORCE_IGMP_VERSION_2]  = "v2",
817
        [IPV4_FORCE_IGMP_VERSION_3]  = "v3",
818
};
819

820
DEFINE_STRING_TABLE_LOOKUP(ipv4_force_igmp_version, IPv4ForceIgmpVersion);
2✔
821
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv4_force_igmp_version, ipv4_force_igmp_version, IPv4ForceIgmpVersion);
2✔
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