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

systemd / systemd / 30136241924

24 Jul 2026 07:11PM UTC coverage: 73.643% (+0.5%) from 73.111%
30136241924

push

github

yuwata
man: fix typo

349461 of 474536 relevant lines covered (73.64%)

1337141.63 hits per line

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

83.01
/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-util.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-neighbor-proxy.h"
22
#include "networkd-network.h"
23
#include "networkd-sysctl.h"
24
#include "path-util.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 "sysctl-monitor-skel.h"
34
#include "sysctl-write-event.h"
35

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

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

43
static int sysctl_event_handler(void *ctx, void *data, size_t data_sz) {
49✔
44
        struct sysctl_write_event *we = ASSERT_PTR(data);
49✔
45
        Hashmap **sysctl_shadow = ASSERT_PTR(ctx);
49✔
46
        _cleanup_free_ char *path = NULL;
49✔
47
        char *value;
49✔
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)
49✔
52
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
×
53
                                         "Unexpected sysctl event, disabling sysctl monitoring: %d", we->version);
54

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

62
        path = path_join("/proc/sys", we->path);
49✔
63
        if (!path) {
49✔
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);
49✔
70
        if (!value)
49✔
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,
8✔
77
                           LOG_MESSAGE_ID(SD_MESSAGE_SYSCTL_CHANGED_STR),
78
                           LOG_ITEM("OBJECT_PID=" PID_FMT, we->pid),
79
                           LOG_ITEM("OBJECT_COMM=%s", empty_to_na(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
                                       empty_to_na(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) {
38✔
91
        struct ring_buffer *rb = ASSERT_PTR(userdata);
38✔
92
        int r;
38✔
93

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

98
        return 0;
38✔
99
}
100

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

109
        assert(manager);
468✔
110

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

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

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

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

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

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

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

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

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

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

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

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

165
        return 0;
468✔
166
}
167

168
void manager_remove_sysctl_monitor(Manager *manager) {
496✔
169
        assert(manager);
496✔
170

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

179
int link_clear_sysctl_shadows(Link *link) {
1,875✔
180
        _cleanup_free_ char *ipv4 = NULL, *ipv6 = NULL;
1,875✔
181
        char *key = NULL, *value = NULL;
1,875✔
182

183
        assert(link);
1,875✔
184
        assert(link->manager);
1,875✔
185

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

190
        ipv6 = path_join("/proc/sys/net/ipv6/conf", link->ifname);
1,875✔
191
        if (!ipv6)
1,875✔
192
                return log_oom();
×
193

194
        HASHMAP_FOREACH_KEY(value, key, link->manager->sysctl_shadow)
8,893✔
195
                if (path_startswith(key, ipv4) || path_startswith(key, ipv6)) {
7,018✔
196
                        assert_se(hashmap_remove_value(link->manager->sysctl_shadow, key, value));
3,149✔
197
                        free(key);
3,149✔
198
                        free(value);
3,149✔
199
                }
200

201
        return 0;
1,875✔
202
}
203
#endif
204

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

208
        assert(manager);
946✔
209
        assert(IN_SET(family, AF_INET, AF_INET6));
946✔
210

211
        if (family == AF_INET6 && !socket_ipv6_is_supported())
946✔
212
                return;
213

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

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

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

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

235
        manager_set_ip_forwarding(manager, AF_INET);
468✔
236
        manager_set_ip_forwarding(manager, AF_INET6);
468✔
237
}
468✔
238

239
static bool link_is_configured_for_family(Link *link, int family) {
19,154✔
240
        assert(link);
19,154✔
241

242
        if (!link->network)
19,154✔
243
                return false;
244

245
        if (link->flags & IFF_LOOPBACK)
19,154✔
246
                return false;
247

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

254
        if (family == AF_INET6 && !socket_ipv6_is_supported())
19,150✔
255
                return false;
×
256

257
        return true;
258
}
259

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

264
        if (!link_is_configured_for_family(link, AF_INET6))
876✔
265
                return 0;
266

267
        if (!link_ipv6_enabled(link))
876✔
268
                return 0;
269

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

273
static int link_set_proxy_arp(Link *link) {
876✔
274
        bool v;
876✔
275

276
        assert(link);
876✔
277
        assert(link->manager);
876✔
278

279
        if (!link_is_configured_for_family(link, AF_INET))
876✔
280
                return 0;
281

282
        if (link->network->proxy_arp >= 0)
876✔
283
                v = link->network->proxy_arp;
3✔
284
        else if (network_has_neighbor_proxy_address(link->network, AF_INET))
873✔
285
                /* If IPv4ProxyARP= is not explicitly set, but per-address IPv4ProxyARPAddress=
286
                 * entries are configured, implicitly enable the proxy_arp sysctl. This matches
287
                 * the behavior of IPv6ProxyNDPAddress= which implies IPv6ProxyNDP=yes. */
288
                v = true;
289
        else
290
                return 0;
291

292
        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", v, manager_get_sysctl_shadow(link->manager));
4✔
293
}
294

295
static int link_set_proxy_arp_pvlan(Link *link) {
876✔
296
        assert(link);
876✔
297
        assert(link->manager);
876✔
298

299
        if (!link_is_configured_for_family(link, AF_INET))
876✔
300
                return 0;
301

302
        if (link->network->proxy_arp_pvlan < 0)
876✔
303
                return 0;
304

305
        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✔
306
}
307

308
int link_get_ip_forwarding(Link *link, int family) {
2,314✔
309
        assert(link);
2,314✔
310
        assert(link->manager);
2,314✔
311
        assert(link->network);
2,314✔
312
        assert(IN_SET(family, AF_INET, AF_INET6));
2,314✔
313

314
        /* If it is explicitly specified, then honor the setting. */
315
        int t = link->network->ip_forwarding[family == AF_INET6];
2,314✔
316
        if (t >= 0)
2,314✔
317
                return t;
318

319
        /* If IPMasquerade= is enabled, also enable IP forwarding. */
320
        if (FLAGS_SET(link->network->ip_masquerade, AF_TO_ADDRESS_FAMILY(family)))
3,628✔
321
                return true;
322

323
        /* If IPv6SendRA= is enabled, also enable IPv6 forwarding. */
324
        if (family == AF_INET6 && link_radv_enabled(link))
2,212✔
325
                return true;
326

327
        /* Otherwise, use the global setting. */
328
        return link->manager->ip_forwarding[family == AF_INET6];
2,178✔
329
}
330

331
static int link_set_ip_forwarding_impl(Link *link, int family) {
1,754✔
332
        int r, t;
1,754✔
333

334
        assert(link);
1,754✔
335
        assert(link->manager);
1,754✔
336
        assert(IN_SET(family, AF_INET, AF_INET6));
1,754✔
337

338
        if (!link_is_configured_for_family(link, family))
1,754✔
339
                return 0;
340

341
        t = link_get_ip_forwarding(link, family);
1,754✔
342
        if (t < 0)
1,754✔
343
                return 0; /* keep */
344

345
        r = sysctl_write_ip_property_boolean(family, link->ifname, "forwarding", t, manager_get_sysctl_shadow(link->manager));
123✔
346
        if (r < 0)
123✔
347
                return log_link_warning_errno(link, r, "Failed to %s %s forwarding, ignoring: %m",
×
348
                                              enable_disable(t), af_to_ipv4_ipv6(family));
349

350
        return 0;
351
}
352

353
static int link_reapply_ip_forwarding(Link *link, int family) {
22✔
354
        int r, ret = 0;
22✔
355

356
        assert(link);
22✔
357
        assert(IN_SET(family, AF_INET, AF_INET6));
22✔
358

359
        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
22✔
360
                return 0;
22✔
361

362
        (void) link_set_ip_forwarding_impl(link, family);
12✔
363

364
        r = link_lldp_tx_update_capabilities(link);
12✔
365
        if (r < 0)
12✔
366
                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not update LLDP capabilities, ignoring: %m"));
×
367

368
        if (family == AF_INET6 && !link_ndisc_enabled(link)) {
12✔
369
                r = ndisc_stop(link);
6✔
370
                if (r < 0)
6✔
371
                        RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery, ignoring: %m"));
×
372

373
                ndisc_flush(link);
6✔
374
        }
375

376
        return ret;
377
}
378

379
static int link_set_ip_forwarding(Link *link, int family) {
1,752✔
380
        int r;
1,752✔
381

382
        assert(link);
1,752✔
383
        assert(link->manager);
1,752✔
384
        assert(link->network);
1,752✔
385
        assert(IN_SET(family, AF_INET, AF_INET6));
1,752✔
386

387
        if (!link_is_configured_for_family(link, family))
1,752✔
388
                return 0;
389

390
        /* When IPMasquerade= is enabled and the global setting is unset, enable _global_ IP forwarding, and
391
         * re-apply per-link setting for all links. */
392
        if (FLAGS_SET(link->network->ip_masquerade, AF_TO_ADDRESS_FAMILY(family)) &&
2,628✔
393
            link->manager->ip_forwarding[family == AF_INET6] < 0) {
34✔
394

395
                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.");
10✔
396

397
                link->manager->ip_forwarding[family == AF_INET6] = true;
10✔
398
                manager_set_ip_forwarding(link->manager, family);
10✔
399

400
                Link *other;
10✔
401
                HASHMAP_FOREACH(other, link->manager->links_by_index) {
42✔
402
                        r = link_reapply_ip_forwarding(other, family);
22✔
403
                        if (r < 0)
22✔
404
                                link_enter_failed(other);
×
405
                }
406

407
                return 0;
10✔
408
        }
409

410
        /* Otherwise, apply per-link setting for _this_ link. */
411
        return link_set_ip_forwarding_impl(link, family);
1,742✔
412
}
413

414
static int link_set_ipv4_rp_filter(Link *link) {
876✔
415
        assert(link);
876✔
416
        assert(link->manager);
876✔
417

418
        if (!link_is_configured_for_family(link, AF_INET))
876✔
419
                return 0;
420

421
        if (link->network->ipv4_rp_filter < 0)
876✔
422
                return 0;
423

424
        return sysctl_write_ip_property_int(AF_INET, link->ifname, "rp_filter", link->network->ipv4_rp_filter, manager_get_sysctl_shadow(link->manager));
2✔
425
}
426

427
static int link_set_ipv4_force_igmp_version(Link *link) {
876✔
428
        assert(link);
876✔
429

430
        if (!link_is_configured_for_family(link, AF_INET))
876✔
431
                return 0;
432

433
        if (link->network->ipv4_force_igmp_version < 0)
876✔
434
                return 0;
435

436
        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✔
437
}
438

439
static int link_set_ipv6_privacy_extensions(Link *link) {
876✔
440
        IPv6PrivacyExtensions val;
876✔
441

442
        assert(link);
876✔
443
        assert(link->manager);
876✔
444

445
        if (!link_is_configured_for_family(link, AF_INET6))
876✔
446
                return 0;
447

448
        val = link->network->ipv6_privacy_extensions;
876✔
449
        if (val < 0) /* If not specified, then use the global setting. */
876✔
450
                val = link->manager->ipv6_privacy_extensions;
851✔
451

452
        /* When "kernel", do not update the setting. */
453
        if (val == IPV6_PRIVACY_EXTENSIONS_KERNEL)
876✔
454
                return 0;
455

456
        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) val, manager_get_sysctl_shadow(link->manager));
876✔
457
}
458

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

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

466
        return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0", manager_get_sysctl_shadow(link->manager));
876✔
467
}
468

469
static int link_set_ipv6_dad_transmits(Link *link) {
876✔
470
        assert(link);
876✔
471
        assert(link->manager);
876✔
472

473
        if (!link_is_configured_for_family(link, AF_INET6))
876✔
474
                return 0;
475

476
        if (link->network->ipv6_dad_transmits < 0)
876✔
477
                return 0;
478

479
        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits, manager_get_sysctl_shadow(link->manager));
2✔
480
}
481

482
static int link_set_ipv6_hop_limit(Link *link) {
876✔
483
        assert(link);
876✔
484
        assert(link->manager);
876✔
485

486
        if (!link_is_configured_for_family(link, AF_INET6))
876✔
487
                return 0;
488

489
        if (link->network->ipv6_hop_limit <= 0)
876✔
490
                return 0;
491

492
        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit, manager_get_sysctl_shadow(link->manager));
2✔
493
}
494

495
static int link_set_ipv6_retransmission_time(Link *link) {
876✔
496
        usec_t retrans_time_ms;
876✔
497

498
        assert(link);
876✔
499
        assert(link->manager);
876✔
500

501
        if (!link_is_configured_for_family(link, AF_INET6))
876✔
502
                return 0;
503

504
        if (!timestamp_is_set(link->network->ipv6_retransmission_time))
876✔
505
                return 0;
506

507
        retrans_time_ms = DIV_ROUND_UP(link->network->ipv6_retransmission_time, USEC_PER_MSEC);
2✔
508
         if (retrans_time_ms <= 0 || retrans_time_ms > UINT32_MAX)
2✔
509
                return 0;
510

511
        return sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", retrans_time_ms, manager_get_sysctl_shadow(link->manager));
2✔
512
}
513

514
static int link_set_ipv6_proxy_ndp(Link *link) {
876✔
515
        bool v;
876✔
516

517
        assert(link);
876✔
518
        assert(link->manager);
876✔
519

520
        if (!link_is_configured_for_family(link, AF_INET6))
876✔
521
                return 0;
522

523
        if (link->network->ipv6_proxy_ndp >= 0)
876✔
524
                v = link->network->ipv6_proxy_ndp;
2✔
525
        else
526
                v = network_has_neighbor_proxy_address(link->network, AF_INET6);
874✔
527

528
        return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v, manager_get_sysctl_shadow(link->manager));
876✔
529
}
530

531
int link_set_ipv6_mtu(Link *link, int log_level) {
1,632✔
532
        uint32_t mtu = 0;
1,632✔
533
        int r;
1,632✔
534

535
        assert(link);
1,632✔
536
        assert(link->manager);
1,632✔
537

538
        if (!link_is_configured_for_family(link, AF_INET6))
1,632✔
539
                return 0;
540

541
        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
1,628✔
542
                return 0;
543

544
        if (sd_event_source_get_enabled(link->ipv6_mtu_wait_synced_event_source, /* ret= */ NULL) > 0) {
1,628✔
545
                log_link_debug(link, "Waiting for IPv6 MTU is synced to link MTU, delaying to set IPv6 MTU.");
×
546
                return 0;
547
        }
548

549
        assert(link->network);
1,628✔
550

551
        if (link->network->ndisc_use_mtu)
1,628✔
552
                mtu = link->ndisc_mtu;
1,628✔
553
        if (mtu == 0)
1,628✔
554
                mtu = link->network->ipv6_mtu;
1,573✔
555
        if (mtu == 0)
1,573✔
556
                return 0;
557

558
        if (mtu > link->mtu) {
87✔
559
                log_link_full(link, log_level,
11✔
560
                              "Reducing requested IPv6 MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
561
                              mtu, link->mtu);
562
                mtu = link->mtu;
11✔
563
        }
564

565
        r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu, manager_get_sysctl_shadow(link->manager));
87✔
566
        if (r < 0)
87✔
567
                return log_link_warning_errno(link, r, "Failed to set IPv6 MTU to %"PRIu32": %m", mtu);
×
568

569
        return 0;
570
}
571

572
static int ipv6_mtu_wait_synced_handler(sd_event_source *s, uint64_t usec, void *userdata);
573

574
static int link_set_ipv6_mtu_async_impl(Link *link) {
48✔
575
        uint32_t current_mtu;
48✔
576
        int r;
48✔
577

578
        assert(link);
48✔
579

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

583
        if (++link->ipv6_mtu_wait_trial_count >= 10) {
48✔
584
                log_link_debug(link, "Timed out waiting for IPv6 MTU being synced to link MTU, proceeding anyway.");
×
585
                r = link_set_ipv6_mtu(link, LOG_INFO);
×
586
                if (r < 0)
×
587
                        return r;
48✔
588

589
                return 1; /* done */
×
590
        }
591

592
        /* Check if IPv6 MTU is synced. */
593
        r = sysctl_read_ip_property_uint32(AF_INET6, link->ifname, "mtu", &current_mtu);
48✔
594
        if (r < 0)
48✔
595
                return log_link_warning_errno(link, r, "Failed to read IPv6 MTU: %m");
1✔
596

597
        if (current_mtu == link->mtu) {
47✔
598
                /* Already synced. Update IPv6 MTU now. */
599
                r = link_set_ipv6_mtu(link, LOG_INFO);
47✔
600
                if (r < 0)
47✔
601
                        return r;
602

603
                return 1; /* done */
47✔
604
        }
605

606
        /* If not, set up a timer event source. */
607
        r = event_reset_time_relative(
×
608
                        link->manager->event, &link->ipv6_mtu_wait_synced_event_source,
×
609
                        CLOCK_BOOTTIME, 100 * USEC_PER_MSEC, 0,
610
                        ipv6_mtu_wait_synced_handler, link,
611
                        /* priority= */ 0, "ipv6-mtu-wait-synced", /* force_reset= */ true);
612
        if (r < 0)
×
613
                return log_link_warning_errno(link, r, "Failed to configure timer event source for waiting for IPv6 MTU being synced: %m");
×
614

615
        /* Check again. */
616
        r = sysctl_read_ip_property_uint32(AF_INET6, link->ifname, "mtu", &current_mtu);
×
617
        if (r < 0)
×
618
                return log_link_warning_errno(link, r, "Failed to read IPv6 MTU: %m");
×
619

620
        if (current_mtu == link->mtu) {
×
621
                /* Synced while setting up the timer event source. Disable it and update IPv6 MTU now. */
622
                r = sd_event_source_set_enabled(link->ipv6_mtu_wait_synced_event_source, SD_EVENT_OFF);
×
623
                if (r < 0)
×
624
                        log_link_debug_errno(link, r, "Failed to disable timer event source for IPv6 MTU, ignoring: %m");
×
625

626
                r = link_set_ipv6_mtu(link, LOG_INFO);
×
627
                if (r < 0)
×
628
                        return r;
629

630
                return 1; /* done */
×
631
        }
632

633
        log_link_debug(link, "IPv6 MTU is not synced to the link MTU after it is changed. Waiting for a while.");
×
634
        return 0; /* waiting */
635
}
636

637
static int ipv6_mtu_wait_synced_handler(sd_event_source *s, uint64_t usec, void *userdata) {
×
638
        (void) link_set_ipv6_mtu_async_impl(ASSERT_PTR(userdata));
×
639
        return 0;
×
640
}
641

642
int link_set_ipv6_mtu_async(Link *link) {
48✔
643
        assert(link);
48✔
644

645
        link->ipv6_mtu_wait_trial_count = 0;
48✔
646
        return link_set_ipv6_mtu_async_impl(link);
48✔
647
}
648

649
static int link_set_ipv4_accept_local(Link *link) {
876✔
650
        assert(link);
876✔
651
        assert(link->manager);
876✔
652

653
        if (!link_is_configured_for_family(link, AF_INET))
876✔
654
                return 0;
655

656
        if (link->network->ipv4_accept_local < 0)
876✔
657
                return 0;
658

659
        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✔
660
}
661

662
static int link_set_ipv4_route_localnet(Link *link) {
876✔
663
        assert(link);
876✔
664
        assert(link->manager);
876✔
665

666
        if (!link_is_configured_for_family(link, AF_INET))
876✔
667
                return 0;
668

669
        if (link->network->ipv4_route_localnet < 0)
876✔
670
                return 0;
671

672
        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0, manager_get_sysctl_shadow(link->manager));
×
673
}
674

675
static int link_set_ipv4_src_valid_mark(Link *link) {
876✔
676
        assert(link);
876✔
677
        assert(link->manager);
876✔
678
        assert(link->network);
876✔
679

680
        if (!link_is_configured_for_family(link, AF_INET))
876✔
681
                return 0;
682

683
        if (link->network->ipv4_src_valid_mark < 0)
876✔
684
                return 0;
685

686
        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "src_valid_mark", link->network->ipv4_src_valid_mark > 0, manager_get_sysctl_shadow(link->manager));
2✔
687
}
688

689
static int link_set_ipv4_promote_secondaries(Link *link) {
876✔
690
        assert(link);
876✔
691
        assert(link->manager);
876✔
692

693
        if (!link_is_configured_for_family(link, AF_INET))
876✔
694
                return 0;
695

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

704
static int link_set_mpls_input(Link *link) {
876✔
705
        assert(link);
876✔
706
        assert(link->manager);
876✔
707

708
        if (!link_is_configured_for_family(link, AF_MPLS))
876✔
709
                return 0;
710

711
        if (link->network->mpls_input < 0)
876✔
712
                return 0;
713

714
        return sysctl_write_ip_property_boolean(AF_MPLS, link->ifname, "input", link->network->mpls_input > 0, manager_get_sysctl_shadow(link->manager));
1✔
715
}
716

717
int link_set_sysctl(Link *link) {
876✔
718
        int r;
876✔
719

720
        assert(link);
876✔
721

722
        /* If IPv6 configured that is static IPv6 address and IPv6LL autoconfiguration is enabled
723
         * for this interface, then enable IPv6 */
724
        r = link_update_ipv6_sysctl(link);
876✔
725
        if (r < 0)
876✔
726
                log_link_warning_errno(link, r, "Cannot enable IPv6, ignoring: %m");
×
727

728
        r = link_set_proxy_arp(link);
876✔
729
        if (r < 0)
876✔
730
               log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface, ignoring: %m");
×
731

732
        r = link_set_proxy_arp_pvlan(link);
876✔
733
        if (r < 0)
876✔
734
                log_link_warning_errno(link, r, "Cannot configure proxy ARP private VLAN for interface, ignoring: %m");
×
735

736
        (void) link_set_ip_forwarding(link, AF_INET);
876✔
737
        (void) link_set_ip_forwarding(link, AF_INET6);
876✔
738

739
        r = link_set_ipv6_privacy_extensions(link);
876✔
740
        if (r < 0)
876✔
741
                log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extensions for interface, ignoring: %m");
×
742

743
        r = link_set_ipv6_accept_ra(link);
876✔
744
        if (r < 0)
876✔
745
                log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m");
×
746

747
        r = link_set_ipv6_dad_transmits(link);
876✔
748
        if (r < 0)
876✔
749
                log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface, ignoring: %m");
×
750

751
        r = link_set_ipv6_hop_limit(link);
876✔
752
        if (r < 0)
876✔
753
                log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface, ignoring: %m");
×
754

755
        r = link_set_ipv6_retransmission_time(link);
876✔
756
        if (r < 0)
876✔
757
                log_link_warning_errno(link, r, "Cannot set IPv6 retransmission time for interface, ignoring: %m");
×
758

759
        r = link_set_ipv6_proxy_ndp(link);
876✔
760
        if (r < 0)
876✔
761
                log_link_warning_errno(link, r, "Cannot set IPv6 proxy NDP, ignoring: %m");
×
762

763
        (void) link_set_ipv6_mtu(link, LOG_INFO);
876✔
764

765
        r = link_set_ipv6ll_stable_secret(link);
876✔
766
        if (r < 0)
876✔
767
                log_link_warning_errno(link, r, "Cannot set stable secret address for IPv6 link-local address: %m");
×
768

769
        r = link_set_ipv4_accept_local(link);
876✔
770
        if (r < 0)
876✔
771
                log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface, ignoring: %m");
×
772

773
        r = link_set_ipv4_route_localnet(link);
876✔
774
        if (r < 0)
876✔
775
                log_link_warning_errno(link, r, "Cannot set IPv4 route_localnet flag for interface, ignoring: %m");
×
776

777
        r = link_set_ipv4_src_valid_mark(link);
876✔
778
        if (r < 0)
876✔
779
                log_link_warning_errno(link, r, "Cannot set IPv4 src_valid_mark flag for interface, ignoring: %m");
×
780

781
        r = link_set_ipv4_rp_filter(link);
876✔
782
        if (r < 0)
876✔
783
                log_link_warning_errno(link, r, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m");
×
784

785
        r = link_set_ipv4_force_igmp_version(link);
876✔
786
        if (r < 0)
876✔
787
                log_link_warning_errno(link, r, "Cannot set IPv4 force igmp version, ignoring: %m");
×
788

789
        r = link_set_ipv4_promote_secondaries(link);
876✔
790
        if (r < 0)
876✔
791
                log_link_warning_errno(link, r, "Cannot enable promote_secondaries for interface, ignoring: %m");
×
792

793
        r = link_set_mpls_input(link);
876✔
794
        if (r < 0)
876✔
795
                log_link_warning_errno(link, r, "Cannot set MPLS input, ignoring: %m");
×
796

797
        return 0;
876✔
798
}
799

800
static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
801
        [IPV6_PRIVACY_EXTENSIONS_NO]            = "no",
802
        [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
803
        [IPV6_PRIVACY_EXTENSIONS_YES]           = "yes",
804
        [IPV6_PRIVACY_EXTENSIONS_KERNEL]        = "kernel",
805
};
806

807
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions,
76✔
808
                                        IPV6_PRIVACY_EXTENSIONS_YES);
809
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_privacy_extensions, ipv6_privacy_extensions, IPv6PrivacyExtensions);
22✔
810

811
static const char* const ip_reverse_path_filter_table[_IP_REVERSE_PATH_FILTER_MAX] = {
812
        [IP_REVERSE_PATH_FILTER_NO]     = "no",
813
        [IP_REVERSE_PATH_FILTER_STRICT] = "strict",
814
        [IP_REVERSE_PATH_FILTER_LOOSE]  = "loose",
815
};
816

817
DEFINE_STRING_TABLE_LOOKUP(ip_reverse_path_filter, IPReversePathFilter);
2✔
818
DEFINE_CONFIG_PARSE_ENUM(config_parse_ip_reverse_path_filter, ip_reverse_path_filter, IPReversePathFilter);
2✔
819

820
int config_parse_ip_forward_deprecated(
×
821
                const char *unit,
822
                const char *filename,
823
                unsigned line,
824
                const char *section,
825
                unsigned section_line,
826
                const char *lvalue,
827
                int ltype,
828
                const char *rvalue,
829
                void *data,
830
                void *userdata) {
831

832
        assert(filename);
×
833

834
        log_syntax(unit, LOG_WARNING, filename, line, 0,
×
835
                   "IPForward= setting is deprecated. "
836
                   "Please use IPv4Forwarding= and/or IPv6Forwarding= in networkd.conf for global setting, "
837
                   "and the same settings in .network files for per-interface setting.");
838
        return 0;
×
839
}
840

841
static const char* const ipv4_force_igmp_version_table[_IPV4_FORCE_IGMP_VERSION_MAX] = {
842
        [IPV4_FORCE_IGMP_VERSION_NO] = "no",
843
        [IPV4_FORCE_IGMP_VERSION_1]  = "v1",
844
        [IPV4_FORCE_IGMP_VERSION_2]  = "v2",
845
        [IPV4_FORCE_IGMP_VERSION_3]  = "v3",
846
};
847

848
DEFINE_STRING_TABLE_LOOKUP(ipv4_force_igmp_version, IPv4ForceIgmpVersion);
2✔
849
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 TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc