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

systemd / systemd / 19996191435

06 Dec 2025 12:24AM UTC coverage: 72.546% (-0.2%) from 72.765%
19996191435

push

github

web-flow
Add 82-net-auto-link-local.{hwdb,rules} to build system and add BMC USB-to-USB links to hwdb (#40006)

Using systems with ADLINK COM-HPC-ALT, ASRock Rack ALTRAD8UD-1L2T and
AMPONED8-2T/BCM boards, there's an issue due to the internal network
connection between the BMC and host, which runs over USB (i.e. Linux
running on the BMC configures a USB gadget ethernet device, with a link
local address).

With the default configuration on Ubuntu (I'm using 25.10),
NetworkManager repeatedly tries to get an address for the interface
using DHCP, resulting in an "Activation of network connection failed"
notification every minute or two.

Add 82-net-auto-link-local.hwdb and 82-net-auto-link-local.rules to the
build system and update 82-net-auto-link-local.hwdb to add the USB
device vendor/product pairs I've seen on the various systems I have with
Ampere Altra and AmpereOne CPUs.

308773 of 425625 relevant lines covered (72.55%)

1159320.34 hits per line

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

81.77
/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) {
468✔
37
        sysctl_monitor_bpf__destroy(obj);
468✔
38
        return NULL;
468✔
39
}
40

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

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

55
        if (we->errorcode != 0) {
11✔
56
                log_warning_errno(we->errorcode, "Sysctl monitor BPF returned error: %m");
×
57
                return 0;
×
58
        }
59

60
        path = path_join("/proc/sys", we->path);
11✔
61
        if (!path) {
11✔
62
                log_oom_warning();
×
63
                return 0;
64
        }
65

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

71
        if (!strneq(value, we->newvalue, sizeof(we->newvalue)))
×
72
                log_struct(LOG_WARNING,
×
73
                           LOG_MESSAGE_ID(SD_MESSAGE_SYSCTL_CHANGED_STR),
74
                           LOG_ITEM("OBJECT_PID=" PID_FMT, we->pid),
75
                           LOG_ITEM("OBJECT_COMM=%s", we->comm),
76
                           LOG_ITEM("SYSCTL=%s", path),
77
                           LOG_ITEM("OLDVALUE=%s", we->current),
78
                           LOG_ITEM("NEWVALUE=%s", we->newvalue),
79
                           LOG_ITEM("OURVALUE=%s", value),
80
                           LOG_MESSAGE("Foreign process '%s[" PID_FMT "]' changed sysctl '%s' from '%s' to '%s', conflicting with our setting to '%s'.",
81
                                       we->comm, we->pid, path, we->current, we->newvalue, value));
82

83
        return 0;
84
}
85

86
static int on_ringbuf_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
8✔
87
        struct ring_buffer *rb = ASSERT_PTR(userdata);
8✔
88
        int r;
8✔
89

90
        r = sym_ring_buffer__poll(rb, /* timeout_msec= */ 0);
8✔
91
        if (r < 0 && errno != EINTR)
8✔
92
                log_error_errno(errno, "Error polling ring buffer: %m");
×
93

94
        return 0;
8✔
95
}
96

97
int manager_install_sysctl_monitor(Manager *manager) {
443✔
98
        _cleanup_(sysctl_monitor_bpf_freep) struct sysctl_monitor_bpf *obj = NULL;
443✔
99
        _cleanup_(bpf_link_freep) struct bpf_link *sysctl_link = NULL;
443✔
100
        _cleanup_(bpf_ring_buffer_freep) struct ring_buffer *sysctl_buffer = NULL;
×
101
        _cleanup_close_ int cgroup_fd = -EBADF, root_cgroup_fd = -EBADF;
886✔
102
        _cleanup_free_ char *cgroup = NULL;
443✔
103
        int idx = 0, r, fd;
443✔
104

105
        assert(manager);
443✔
106

107
        r = dlopen_bpf();
443✔
108
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
443✔
109
                return log_debug_errno(r, "sysctl monitor disabled, as BPF support is not available.");
×
110
        if (r < 0)
443✔
111
                return log_warning_errno(r, "Failed to load libbpf, not installing sysctl monitor: %m");
×
112

113
        r = cg_pid_get_path(0, &cgroup);
443✔
114
        if (r < 0)
443✔
115
                return log_warning_errno(r, "Failed to get cgroup path, ignoring: %m.");
×
116

117
        root_cgroup_fd = cg_path_open("/");
443✔
118
        if (root_cgroup_fd < 0)
443✔
119
                return log_warning_errno(root_cgroup_fd, "Failed to open cgroup, ignoring: %m");
×
120

121
        obj = sysctl_monitor_bpf__open_and_load();
443✔
122
        if (!obj)
443✔
123
                return log_full_errno(errno == EINVAL ? LOG_DEBUG : LOG_INFO, errno,
59✔
124
                                      "Unable to load sysctl monitor BPF program, ignoring: %m");
125

126
        cgroup_fd = cg_path_open(cgroup);
384✔
127
        if (cgroup_fd < 0)
384✔
128
                return log_warning_errno(cgroup_fd, "Failed to open cgroup: %m");
×
129

130
        if (sym_bpf_map_update_elem(sym_bpf_map__fd(obj->maps.cgroup_map), &idx, &cgroup_fd, BPF_ANY))
384✔
131
                return log_warning_errno(errno, "Failed to update cgroup map: %m");
×
132

133
        sysctl_link = sym_bpf_program__attach_cgroup(obj->progs.sysctl_monitor, root_cgroup_fd);
384✔
134
        r = bpf_get_error_translated(sysctl_link);
384✔
135
        if (r < 0)
384✔
136
                return log_warning_errno(r, "Unable to attach sysctl monitor BPF program to cgroup, ignoring: %m");
×
137

138
        fd = sym_bpf_map__fd(obj->maps.written_sysctls);
384✔
139
        if (fd < 0)
384✔
140
                return log_warning_errno(fd, "Failed to get fd of sysctl maps: %m");
×
141

142
        sysctl_buffer = sym_ring_buffer__new(fd, sysctl_event_handler, &manager->sysctl_shadow, NULL);
384✔
143
        if (!sysctl_buffer)
384✔
144
                return log_warning_errno(errno, "Failed to create ring buffer: %m");
×
145

146
        fd = sym_ring_buffer__epoll_fd(sysctl_buffer);
384✔
147
        if (fd < 0)
384✔
148
                return log_warning_errno(fd, "Failed to get poll fd of ring buffer: %m");
×
149

150
        r = sd_event_add_io(manager->event, &manager->sysctl_event_source,
384✔
151
                            fd, EPOLLIN, on_ringbuf_io, sysctl_buffer);
152
        if (r < 0)
384✔
153
                return log_warning_errno(r, "Failed to watch sysctl event ringbuffer: %m");
×
154

155
        manager->sysctl_link = TAKE_PTR(sysctl_link);
384✔
156
        manager->sysctl_skel = TAKE_PTR(obj);
384✔
157
        manager->sysctl_buffer = TAKE_PTR(sysctl_buffer);
384✔
158
        manager->cgroup_fd = TAKE_FD(cgroup_fd);
384✔
159

160
        return 0;
384✔
161
}
162

163
void manager_remove_sysctl_monitor(Manager *manager) {
468✔
164
        assert(manager);
468✔
165

166
        manager->sysctl_event_source = sd_event_source_disable_unref(manager->sysctl_event_source);
468✔
167
        manager->sysctl_buffer = bpf_ring_buffer_free(manager->sysctl_buffer);
468✔
168
        manager->sysctl_link = bpf_link_free(manager->sysctl_link);
468✔
169
        manager->sysctl_skel = sysctl_monitor_bpf_free(manager->sysctl_skel);
468✔
170
        manager->cgroup_fd = safe_close(manager->cgroup_fd);
468✔
171
        manager->sysctl_shadow = hashmap_free(manager->sysctl_shadow);
468✔
172
}
468✔
173

174
int link_clear_sysctl_shadows(Link *link) {
2,785✔
175
        _cleanup_free_ char *ipv4 = NULL, *ipv6 = NULL;
2,785✔
176
        char *key = NULL, *value = NULL;
2,785✔
177

178
        assert(link);
2,785✔
179
        assert(link->manager);
2,785✔
180

181
        ipv4 = path_join("/proc/sys/net/ipv4/conf", link->ifname);
2,785✔
182
        if (!ipv4)
2,785✔
183
                return log_oom();
×
184

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

189
        HASHMAP_FOREACH_KEY(value, key, link->manager->sysctl_shadow)
9,633✔
190
                if (path_startswith(key, ipv4) || path_startswith(key, ipv6)) {
6,848✔
191
                        assert_se(hashmap_remove_value(link->manager->sysctl_shadow, key, value));
3,076✔
192
                        free(key);
3,076✔
193
                        free(value);
3,076✔
194
                }
195

196
        return 0;
2,785✔
197
}
198
#endif
199

200
static void manager_set_ip_forwarding(Manager *manager, int family) {
890✔
201
        int r, t;
890✔
202

203
        assert(manager);
890✔
204
        assert(IN_SET(family, AF_INET, AF_INET6));
890✔
205

206
        if (family == AF_INET6 && !socket_ipv6_is_supported())
890✔
207
                return;
208

209
        t = manager->ip_forwarding[family == AF_INET6];
890✔
210
        if (t < 0)
890✔
211
                return; /* keep */
212

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

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

226
void manager_set_sysctl(Manager *manager) {
443✔
227
        assert(manager);
443✔
228
        assert(!manager->test_mode);
443✔
229

230
        manager_set_ip_forwarding(manager, AF_INET);
443✔
231
        manager_set_ip_forwarding(manager, AF_INET6);
443✔
232
}
443✔
233

234
static bool link_is_configured_for_family(Link *link, int family) {
17,845✔
235
        assert(link);
17,845✔
236

237
        if (!link->network)
17,845✔
238
                return false;
239

240
        if (link->flags & IFF_LOOPBACK)
17,845✔
241
                return false;
242

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

249
        if (family == AF_INET6 && !socket_ipv6_is_supported())
17,841✔
250
                return false;
×
251

252
        return true;
253
}
254

255
static int link_update_ipv6_sysctl(Link *link) {
855✔
256
        assert(link);
855✔
257
        assert(link->manager);
855✔
258

259
        if (!link_is_configured_for_family(link, AF_INET6))
855✔
260
                return 0;
261

262
        if (!link_ipv6_enabled(link))
855✔
263
                return 0;
264

265
        return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false, manager_get_sysctl_shadow(link->manager));
790✔
266
}
267

268
static int link_set_proxy_arp(Link *link) {
855✔
269
        assert(link);
855✔
270
        assert(link->manager);
855✔
271

272
        if (!link_is_configured_for_family(link, AF_INET))
855✔
273
                return 0;
274

275
        if (link->network->proxy_arp < 0)
855✔
276
                return 0;
277

278
        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0, manager_get_sysctl_shadow(link->manager));
2✔
279
}
280

281
static int link_set_proxy_arp_pvlan(Link *link) {
855✔
282
        assert(link);
855✔
283
        assert(link->manager);
855✔
284

285
        if (!link_is_configured_for_family(link, AF_INET))
855✔
286
                return 0;
287

288
        if (link->network->proxy_arp_pvlan < 0)
855✔
289
                return 0;
290

291
        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✔
292
}
293

294
int link_get_ip_forwarding(Link *link, int family) {
2,294✔
295
        assert(link);
2,294✔
296
        assert(link->manager);
2,294✔
297
        assert(link->network);
2,294✔
298
        assert(IN_SET(family, AF_INET, AF_INET6));
2,294✔
299

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

305
        /* If IPMasquerade= is enabled, also enable IP forwarding. */
306
        if (FLAGS_SET(link->network->ip_masquerade, AF_TO_ADDRESS_FAMILY(family)))
3,606✔
307
                return true;
308

309
        /* If IPv6SendRA= is enabled, also enable IPv6 forwarding. */
310
        if (family == AF_INET6 && link_radv_enabled(link))
2,193✔
311
                return true;
312

313
        /* Otherwise, use the global setting. */
314
        return link->manager->ip_forwarding[family == AF_INET6];
2,160✔
315
}
316

317
static int link_set_ip_forwarding_impl(Link *link, int family) {
1,712✔
318
        int r, t;
1,712✔
319

320
        assert(link);
1,712✔
321
        assert(link->manager);
1,712✔
322
        assert(IN_SET(family, AF_INET, AF_INET6));
1,712✔
323

324
        if (!link_is_configured_for_family(link, family))
1,712✔
325
                return 0;
326

327
        t = link_get_ip_forwarding(link, family);
1,712✔
328
        if (t < 0)
1,712✔
329
                return 0; /* keep */
330

331
        r = sysctl_write_ip_property_boolean(family, link->ifname, "forwarding", t, manager_get_sysctl_shadow(link->manager));
123✔
332
        if (r < 0)
123✔
333
                return log_link_warning_errno(link, r, "Failed to %s %s forwarding, ignoring: %m",
×
334
                                              enable_disable(t), af_to_ipv4_ipv6(family));
335

336
        return 0;
337
}
338

339
static int link_reapply_ip_forwarding(Link *link, int family) {
10✔
340
        int r, ret = 0;
10✔
341

342
        assert(link);
10✔
343
        assert(IN_SET(family, AF_INET, AF_INET6));
10✔
344

345
        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
10✔
346
                return 0;
10✔
347

348
        (void) link_set_ip_forwarding_impl(link, family);
6✔
349

350
        r = link_lldp_tx_update_capabilities(link);
6✔
351
        if (r < 0)
6✔
352
                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not update LLDP capabilities, ignoring: %m"));
×
353

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

359
                ndisc_flush(link);
3✔
360
        }
361

362
        return ret;
363
}
364

365
static int link_set_ip_forwarding(Link *link, int family) {
1,710✔
366
        int r;
1,710✔
367

368
        assert(link);
1,710✔
369
        assert(link->manager);
1,710✔
370
        assert(link->network);
1,710✔
371
        assert(IN_SET(family, AF_INET, AF_INET6));
1,710✔
372

373
        if (!link_is_configured_for_family(link, family))
1,710✔
374
                return 0;
375

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

381
                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✔
382

383
                link->manager->ip_forwarding[family == AF_INET6] = true;
4✔
384
                manager_set_ip_forwarding(link->manager, family);
4✔
385

386
                Link *other;
4✔
387
                HASHMAP_FOREACH(other, link->manager->links_by_index) {
18✔
388
                        r = link_reapply_ip_forwarding(other, family);
10✔
389
                        if (r < 0)
10✔
390
                                link_enter_failed(other);
×
391
                }
392

393
                return 0;
4✔
394
        }
395

396
        /* Otherwise, apply per-link setting for _this_ link. */
397
        return link_set_ip_forwarding_impl(link, family);
1,706✔
398
}
399

400
static int link_set_ipv4_rp_filter(Link *link) {
855✔
401
        assert(link);
855✔
402
        assert(link->manager);
855✔
403

404
        if (!link_is_configured_for_family(link, AF_INET))
855✔
405
                return 0;
406

407
        if (link->network->ipv4_rp_filter < 0)
855✔
408
                return 0;
409

410
        return sysctl_write_ip_property_int(AF_INET, link->ifname, "rp_filter", link->network->ipv4_rp_filter, manager_get_sysctl_shadow(link->manager));
2✔
411
}
412

413
static int link_set_ipv4_force_igmp_version(Link *link) {
855✔
414
        assert(link);
855✔
415

416
        if (!link_is_configured_for_family(link, AF_INET))
855✔
417
                return 0;
418

419
        if (link->network->ipv4_force_igmp_version < 0)
855✔
420
                return 0;
421

422
        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✔
423
}
424

425
static int link_set_ipv6_privacy_extensions(Link *link) {
855✔
426
        IPv6PrivacyExtensions val;
855✔
427

428
        assert(link);
855✔
429
        assert(link->manager);
855✔
430

431
        if (!link_is_configured_for_family(link, AF_INET6))
855✔
432
                return 0;
433

434
        val = link->network->ipv6_privacy_extensions;
855✔
435
        if (val < 0) /* If not specified, then use the global setting. */
855✔
436
                val = link->manager->ipv6_privacy_extensions;
830✔
437

438
        /* When "kernel", do not update the setting. */
439
        if (val == IPV6_PRIVACY_EXTENSIONS_KERNEL)
855✔
440
                return 0;
441

442
        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) val, manager_get_sysctl_shadow(link->manager));
855✔
443
}
444

445
static int link_set_ipv6_accept_ra(Link *link) {
855✔
446
        assert(link);
855✔
447
        assert(link->manager);
855✔
448

449
        if (!link_is_configured_for_family(link, AF_INET6))
855✔
450
                return 0;
451

452
        return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0", manager_get_sysctl_shadow(link->manager));
855✔
453
}
454

455
static int link_set_ipv6_dad_transmits(Link *link) {
855✔
456
        assert(link);
855✔
457
        assert(link->manager);
855✔
458

459
        if (!link_is_configured_for_family(link, AF_INET6))
855✔
460
                return 0;
461

462
        if (link->network->ipv6_dad_transmits < 0)
855✔
463
                return 0;
464

465
        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits, manager_get_sysctl_shadow(link->manager));
2✔
466
}
467

468
static int link_set_ipv6_hop_limit(Link *link) {
855✔
469
        assert(link);
855✔
470
        assert(link->manager);
855✔
471

472
        if (!link_is_configured_for_family(link, AF_INET6))
855✔
473
                return 0;
474

475
        if (link->network->ipv6_hop_limit <= 0)
855✔
476
                return 0;
477

478
        return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit, manager_get_sysctl_shadow(link->manager));
2✔
479
}
480

481
static int link_set_ipv6_retransmission_time(Link *link) {
855✔
482
        usec_t retrans_time_ms;
855✔
483

484
        assert(link);
855✔
485
        assert(link->manager);
855✔
486

487
        if (!link_is_configured_for_family(link, AF_INET6))
855✔
488
                return 0;
489

490
        if (!timestamp_is_set(link->network->ipv6_retransmission_time))
855✔
491
                return 0;
492

493
        retrans_time_ms = DIV_ROUND_UP(link->network->ipv6_retransmission_time, USEC_PER_MSEC);
2✔
494
         if (retrans_time_ms <= 0 || retrans_time_ms > UINT32_MAX)
2✔
495
                return 0;
496

497
        return sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", retrans_time_ms, manager_get_sysctl_shadow(link->manager));
2✔
498
}
499

500
static int link_set_ipv6_proxy_ndp(Link *link) {
855✔
501
        bool v;
855✔
502

503
        assert(link);
855✔
504
        assert(link->manager);
855✔
505

506
        if (!link_is_configured_for_family(link, AF_INET6))
855✔
507
                return 0;
508

509
        if (link->network->ipv6_proxy_ndp >= 0)
855✔
510
                v = link->network->ipv6_proxy_ndp;
2✔
511
        else
512
                v = !set_isempty(link->network->ipv6_proxy_ndp_addresses);
853✔
513

514
        return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v, manager_get_sysctl_shadow(link->manager));
855✔
515
}
516

517
int link_set_ipv6_mtu(Link *link, int log_level) {
1,598✔
518
        uint32_t mtu = 0;
1,598✔
519
        int r;
1,598✔
520

521
        assert(link);
1,598✔
522
        assert(link->manager);
1,598✔
523

524
        if (!link_is_configured_for_family(link, AF_INET6))
1,598✔
525
                return 0;
526

527
        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
1,594✔
528
                return 0;
529

530
        if (sd_event_source_get_enabled(link->ipv6_mtu_wait_synced_event_source, /* ret = */ NULL) > 0) {
1,594✔
531
                log_link_debug(link, "Waiting for IPv6 MTU is synced to link MTU, delaying to set IPv6 MTU.");
×
532
                return 0;
×
533
        }
534

535
        assert(link->network);
1,594✔
536

537
        if (link->network->ndisc_use_mtu)
1,594✔
538
                mtu = link->ndisc_mtu;
1,594✔
539
        if (mtu == 0)
1,594✔
540
                mtu = link->network->ipv6_mtu;
1,541✔
541
        if (mtu == 0)
1,541✔
542
                return 0;
543

544
        if (mtu > link->mtu) {
88✔
545
                log_link_full(link, log_level,
11✔
546
                              "Reducing requested IPv6 MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
547
                              mtu, link->mtu);
548
                mtu = link->mtu;
11✔
549
        }
550

551
        r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu, manager_get_sysctl_shadow(link->manager));
88✔
552
        if (r < 0)
88✔
553
                return log_link_warning_errno(link, r, "Failed to set IPv6 MTU to %"PRIu32": %m", mtu);
×
554

555
        return 0;
556
}
557

558
static int ipv6_mtu_wait_synced_handler(sd_event_source *s, uint64_t usec, void *userdata);
559

560
static int link_set_ipv6_mtu_async_impl(Link *link) {
46✔
561
        uint32_t current_mtu;
46✔
562
        int r;
46✔
563

564
        assert(link);
46✔
565

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

569
        if (++link->ipv6_mtu_wait_trial_count >= 10) {
46✔
570
                log_link_debug(link, "Timed out waiting for IPv6 MTU being synced to link MTU, proceeding anyway.");
×
571
                r = link_set_ipv6_mtu(link, LOG_INFO);
×
572
                if (r < 0)
×
573
                        return r;
46✔
574

575
                return 1; /* done */
×
576
        }
577

578
        /* Check if IPv6 MTU is synced. */
579
        r = sysctl_read_ip_property_uint32(AF_INET6, link->ifname, "mtu", &current_mtu);
46✔
580
        if (r < 0)
46✔
581
                return log_link_warning_errno(link, r, "Failed to read IPv6 MTU: %m");
1✔
582

583
        if (current_mtu == link->mtu) {
45✔
584
                /* Already synced. Update IPv6 MTU now. */
585
                r = link_set_ipv6_mtu(link, LOG_INFO);
45✔
586
                if (r < 0)
45✔
587
                        return r;
588

589
                return 1; /* done */
45✔
590
        }
591

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

601
        /* Check again. */
602
        r = sysctl_read_ip_property_uint32(AF_INET6, link->ifname, "mtu", &current_mtu);
×
603
        if (r < 0)
×
604
                return log_link_warning_errno(link, r, "Failed to read IPv6 MTU: %m");
×
605

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

612
                r = link_set_ipv6_mtu(link, LOG_INFO);
×
613
                if (r < 0)
×
614
                        return r;
615

616
                return 1; /* done */
×
617
        }
618

619
        log_link_debug(link, "IPv6 MTU is not synced to the link MTU after it is changed. Waiting for a while.");
×
620
        return 0; /* waiting */
621
}
622

623
static int ipv6_mtu_wait_synced_handler(sd_event_source *s, uint64_t usec, void *userdata) {
×
624
        (void) link_set_ipv6_mtu_async_impl(ASSERT_PTR(userdata));
×
625
        return 0;
×
626
}
627

628
int link_set_ipv6_mtu_async(Link *link) {
46✔
629
        assert(link);
46✔
630

631
        link->ipv6_mtu_wait_trial_count = 0;
46✔
632
        return link_set_ipv6_mtu_async_impl(link);
46✔
633
}
634

635
static int link_set_ipv4_accept_local(Link *link) {
855✔
636
        assert(link);
855✔
637
        assert(link->manager);
855✔
638

639
        if (!link_is_configured_for_family(link, AF_INET))
855✔
640
                return 0;
641

642
        if (link->network->ipv4_accept_local < 0)
855✔
643
                return 0;
644

645
        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✔
646
}
647

648
static int link_set_ipv4_route_localnet(Link *link) {
855✔
649
        assert(link);
855✔
650
        assert(link->manager);
855✔
651

652
        if (!link_is_configured_for_family(link, AF_INET))
855✔
653
                return 0;
654

655
        if (link->network->ipv4_route_localnet < 0)
855✔
656
                return 0;
657

658
        return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0, manager_get_sysctl_shadow(link->manager));
×
659
}
660

661
static int link_set_ipv4_promote_secondaries(Link *link) {
855✔
662
        assert(link);
855✔
663
        assert(link->manager);
855✔
664

665
        if (!link_is_configured_for_family(link, AF_INET))
855✔
666
                return 0;
667

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

676
static int link_set_mpls_input(Link *link) {
855✔
677
        assert(link);
855✔
678
        assert(link->manager);
855✔
679

680
        if (!link_is_configured_for_family(link, AF_MPLS))
855✔
681
                return 0;
682

683
        if (link->network->mpls_input < 0)
855✔
684
                return 0;
685

686
        return sysctl_write_ip_property_boolean(AF_MPLS, link->ifname, "input", link->network->mpls_input > 0, manager_get_sysctl_shadow(link->manager));
1✔
687
}
688

689
int link_set_sysctl(Link *link) {
855✔
690
        int r;
855✔
691

692
        assert(link);
855✔
693

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

700
        r = link_set_proxy_arp(link);
855✔
701
        if (r < 0)
855✔
702
               log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface, ignoring: %m");
×
703

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

708
        (void) link_set_ip_forwarding(link, AF_INET);
855✔
709
        (void) link_set_ip_forwarding(link, AF_INET6);
855✔
710

711
        r = link_set_ipv6_privacy_extensions(link);
855✔
712
        if (r < 0)
855✔
713
                log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extensions for interface, ignoring: %m");
×
714

715
        r = link_set_ipv6_accept_ra(link);
855✔
716
        if (r < 0)
855✔
717
                log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m");
×
718

719
        r = link_set_ipv6_dad_transmits(link);
855✔
720
        if (r < 0)
855✔
721
                log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface, ignoring: %m");
×
722

723
        r = link_set_ipv6_hop_limit(link);
855✔
724
        if (r < 0)
855✔
725
                log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface, ignoring: %m");
×
726

727
        r = link_set_ipv6_retransmission_time(link);
855✔
728
        if (r < 0)
855✔
729
                log_link_warning_errno(link, r, "Cannot set IPv6 retransmission time for interface, ignoring: %m");
×
730

731
        r = link_set_ipv6_proxy_ndp(link);
855✔
732
        if (r < 0)
855✔
733
                log_link_warning_errno(link, r, "Cannot set IPv6 proxy NDP, ignoring: %m");
×
734

735
        (void) link_set_ipv6_mtu(link, LOG_INFO);
855✔
736

737
        r = link_set_ipv6ll_stable_secret(link);
855✔
738
        if (r < 0)
855✔
739
                log_link_warning_errno(link, r, "Cannot set stable secret address for IPv6 link-local address: %m");
×
740

741
        r = link_set_ipv4_accept_local(link);
855✔
742
        if (r < 0)
855✔
743
                log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface, ignoring: %m");
×
744

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

749
        r = link_set_ipv4_rp_filter(link);
855✔
750
        if (r < 0)
855✔
751
                log_link_warning_errno(link, r, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m");
×
752

753
        r = link_set_ipv4_force_igmp_version(link);
855✔
754
        if (r < 0)
855✔
755
                log_link_warning_errno(link, r, "Cannot set IPv4 force igmp version, ignoring: %m");
×
756

757
        r = link_set_ipv4_promote_secondaries(link);
855✔
758
        if (r < 0)
855✔
759
                log_link_warning_errno(link, r, "Cannot enable promote_secondaries for interface, ignoring: %m");
×
760

761
        r = link_set_mpls_input(link);
855✔
762
        if (r < 0)
855✔
763
                log_link_warning_errno(link, r, "Cannot set MPLS input, ignoring: %m");
×
764

765
        return 0;
855✔
766
}
767

768
static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
769
        [IPV6_PRIVACY_EXTENSIONS_NO]            = "no",
770
        [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
771
        [IPV6_PRIVACY_EXTENSIONS_YES]           = "yes",
772
        [IPV6_PRIVACY_EXTENSIONS_KERNEL]        = "kernel",
773
};
774

775
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions,
76✔
776
                                        IPV6_PRIVACY_EXTENSIONS_YES);
777
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_privacy_extensions, ipv6_privacy_extensions, IPv6PrivacyExtensions);
22✔
778

779
static const char* const ip_reverse_path_filter_table[_IP_REVERSE_PATH_FILTER_MAX] = {
780
        [IP_REVERSE_PATH_FILTER_NO]     = "no",
781
        [IP_REVERSE_PATH_FILTER_STRICT] = "strict",
782
        [IP_REVERSE_PATH_FILTER_LOOSE]  = "loose",
783
};
784

785
DEFINE_STRING_TABLE_LOOKUP(ip_reverse_path_filter, IPReversePathFilter);
2✔
786
DEFINE_CONFIG_PARSE_ENUM(config_parse_ip_reverse_path_filter, ip_reverse_path_filter, IPReversePathFilter);
2✔
787

788
int config_parse_ip_forward_deprecated(
×
789
                const char *unit,
790
                const char *filename,
791
                unsigned line,
792
                const char *section,
793
                unsigned section_line,
794
                const char *lvalue,
795
                int ltype,
796
                const char *rvalue,
797
                void *data,
798
                void *userdata) {
799

800
        assert(filename);
×
801

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

809
static const char* const ipv4_force_igmp_version_table[_IPV4_FORCE_IGMP_VERSION_MAX] = {
810
        [IPV4_FORCE_IGMP_VERSION_NO] = "no",
811
        [IPV4_FORCE_IGMP_VERSION_1]  = "v1",
812
        [IPV4_FORCE_IGMP_VERSION_2]  = "v2",
813
        [IPV4_FORCE_IGMP_VERSION_3]  = "v3",
814
};
815

816
DEFINE_STRING_TABLE_LOOKUP(ipv4_force_igmp_version, IPv4ForceIgmpVersion);
2✔
817
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