1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <netinet/in.h>
6 #include "missing_network.h"
7 #include "networkd-link.h"
8 #include "networkd-network.h"
9 #include "networkd-sysctl.h"
10 #include "socket-util.h"
11 #include "string-table.h"
12 #include "sysctl-util.h"
14 static int link_update_ipv6_sysctl(Link
*link
) {
17 if (link
->flags
& IFF_LOOPBACK
)
20 if (!link_ipv6_enabled(link
))
23 return sysctl_write_ip_property_boolean(AF_INET6
, link
->ifname
, "disable_ipv6", false);
26 static int link_set_proxy_arp(Link
*link
) {
29 if (link
->flags
& IFF_LOOPBACK
)
35 if (link
->network
->proxy_arp
< 0)
38 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "proxy_arp", link
->network
->proxy_arp
> 0);
41 static bool link_ip_forward_enabled(Link
*link
, int family
) {
43 assert(IN_SET(family
, AF_INET
, AF_INET6
));
45 if (family
== AF_INET6
&& !socket_ipv6_is_supported())
48 if (link
->flags
& IFF_LOOPBACK
)
54 return link
->network
->ip_forward
& (family
== AF_INET
? ADDRESS_FAMILY_IPV4
: ADDRESS_FAMILY_IPV6
);
57 static int link_set_ipv4_forward(Link
*link
) {
60 if (!link_ip_forward_enabled(link
, AF_INET
))
63 /* We propagate the forwarding flag from one interface to the
64 * global setting one way. This means: as long as at least one
65 * interface was configured at any time that had IP forwarding
66 * enabled the setting will stay on for good. We do this
67 * primarily to keep IPv4 and IPv6 packet forwarding behaviour
68 * somewhat in sync (see below). */
70 return sysctl_write_ip_property(AF_INET
, NULL
, "ip_forward", "1");
73 static int link_set_ipv6_forward(Link
*link
) {
76 if (!link_ip_forward_enabled(link
, AF_INET6
))
79 /* On Linux, the IPv6 stack does not know a per-interface
80 * packet forwarding setting: either packet forwarding is on
81 * for all, or off for all. We hence don't bother with a
82 * per-interface setting, but simply propagate the interface
83 * flag, if it is set, to the global flag, one-way. Note that
84 * while IPv4 would allow a per-interface flag, we expose the
85 * same behaviour there and also propagate the setting from
86 * one to all, to keep things simple (see above). */
88 return sysctl_write_ip_property(AF_INET6
, "all", "forwarding", "1");
91 static int link_set_ipv6_privacy_extensions(Link
*link
) {
94 if (!socket_ipv6_is_supported())
97 if (link
->flags
& IFF_LOOPBACK
)
103 // this is the special "kernel" value
104 if (link
->network
->ipv6_privacy_extensions
== _IPV6_PRIVACY_EXTENSIONS_INVALID
)
107 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "use_tempaddr", (int) link
->network
->ipv6_privacy_extensions
);
110 static int link_set_ipv6_accept_ra(Link
*link
) {
113 /* Make this a NOP if IPv6 is not available */
114 if (!socket_ipv6_is_supported())
117 if (link
->flags
& IFF_LOOPBACK
)
123 return sysctl_write_ip_property(AF_INET6
, link
->ifname
, "accept_ra", "0");
126 static int link_set_ipv6_dad_transmits(Link
*link
) {
129 /* Make this a NOP if IPv6 is not available */
130 if (!socket_ipv6_is_supported())
133 if (link
->flags
& IFF_LOOPBACK
)
139 if (link
->network
->ipv6_dad_transmits
< 0)
142 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "dad_transmits", link
->network
->ipv6_dad_transmits
);
145 static int link_set_ipv6_hop_limit(Link
*link
) {
148 /* Make this a NOP if IPv6 is not available */
149 if (!socket_ipv6_is_supported())
152 if (link
->flags
& IFF_LOOPBACK
)
158 if (link
->network
->ipv6_hop_limit
< 0)
161 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "hop_limit", link
->network
->ipv6_hop_limit
);
164 static int link_set_ipv6_proxy_ndp(Link
*link
) {
169 if (!socket_ipv6_is_supported())
172 if (link
->flags
& IFF_LOOPBACK
)
178 if (link
->network
->ipv6_proxy_ndp
>= 0)
179 v
= link
->network
->ipv6_proxy_ndp
;
181 v
= !set_isempty(link
->network
->ipv6_proxy_ndp_addresses
);
183 return sysctl_write_ip_property_boolean(AF_INET6
, link
->ifname
, "proxy_ndp", v
);
186 int link_set_ipv6_mtu(Link
*link
) {
191 /* Make this a NOP if IPv6 is not available */
192 if (!socket_ipv6_is_supported())
195 if (link
->flags
& IFF_LOOPBACK
)
201 if (link
->network
->ipv6_mtu
== 0)
204 mtu
= link
->network
->ipv6_mtu
;
205 if (mtu
> link
->max_mtu
) {
206 log_link_warning(link
, "Reducing requested IPv6 MTU %"PRIu32
" to the interface's maximum MTU %"PRIu32
".",
211 return sysctl_write_ip_property_uint32(AF_INET6
, link
->ifname
, "mtu", mtu
);
214 static int link_set_ipv4_accept_local(Link
*link
) {
217 if (link
->flags
& IFF_LOOPBACK
)
220 if (link
->network
->ipv4_accept_local
< 0)
223 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "accept_local", link
->network
->ipv4_accept_local
> 0);
226 static int link_set_ipv4_route_localnet(Link
*link
) {
229 if (link
->flags
& IFF_LOOPBACK
)
232 if (link
->network
->ipv4_route_localnet
< 0)
235 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "route_localnet", link
->network
->ipv4_route_localnet
> 0);
238 int link_set_sysctl(Link
*link
) {
243 /* If IPv6 configured that is static IPv6 address and IPv6LL autoconfiguration is enabled
244 * for this interface, then enable IPv6 */
245 r
= link_update_ipv6_sysctl(link
);
247 log_link_warning_errno(link
, r
, "Cannot enable IPv6, ignoring: %m");
249 r
= link_set_proxy_arp(link
);
251 log_link_warning_errno(link
, r
, "Cannot configure proxy ARP for interface, ignoring: %m");
253 r
= link_set_ipv4_forward(link
);
255 log_link_warning_errno(link
, r
, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
257 r
= link_set_ipv6_forward(link
);
259 log_link_warning_errno(link
, r
, "Cannot configure IPv6 packet forwarding, ignoring: %m");
261 r
= link_set_ipv6_privacy_extensions(link
);
263 log_link_warning_errno(link
, r
, "Cannot configure IPv6 privacy extensions for interface, ignoring: %m");
265 r
= link_set_ipv6_accept_ra(link
);
267 log_link_warning_errno(link
, r
, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m");
269 r
= link_set_ipv6_dad_transmits(link
);
271 log_link_warning_errno(link
, r
, "Cannot set IPv6 dad transmits for interface, ignoring: %m");
273 r
= link_set_ipv6_hop_limit(link
);
275 log_link_warning_errno(link
, r
, "Cannot set IPv6 hop limit for interface, ignoring: %m");
277 r
= link_set_ipv6_proxy_ndp(link
);
279 log_link_warning_errno(link
, r
, "Cannot set IPv6 proxy NDP, ignoring: %m");
281 r
= link_set_ipv6_mtu(link
);
283 log_link_warning_errno(link
, r
, "Cannot set IPv6 MTU, ignoring: %m");
285 r
= link_set_ipv6ll_stable_secret(link
);
287 log_link_warning_errno(link
, r
, "Cannot set stable secret address for IPv6 link-local address: %m");
289 r
= link_set_ipv4_accept_local(link
);
291 log_link_warning_errno(link
, r
, "Cannot set IPv4 accept_local flag for interface, ignoring: %m");
293 r
= link_set_ipv4_route_localnet(link
);
295 log_link_warning_errno(link
, r
, "Cannot set IPv4 route_localnet flag for interface, ignoring: %m");
297 /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not
298 * changes between leases. The kernel will remove all secondary IP addresses of an interface
299 * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
300 * secondary IP and when the primary one expires it relies on the kernel to promote the
301 * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
302 r
= sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "promote_secondaries", true);
304 log_link_warning_errno(link
, r
, "Cannot enable promote_secondaries for interface, ignoring: %m");
309 static const char* const ipv6_privacy_extensions_table
[_IPV6_PRIVACY_EXTENSIONS_MAX
] = {
310 [IPV6_PRIVACY_EXTENSIONS_NO
] = "no",
311 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC
] = "prefer-public",
312 [IPV6_PRIVACY_EXTENSIONS_YES
] = "yes",
315 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions
, IPv6PrivacyExtensions
,
316 IPV6_PRIVACY_EXTENSIONS_YES
);
318 int config_parse_ipv6_privacy_extensions(
320 const char *filename
,
323 unsigned section_line
,
330 IPv6PrivacyExtensions s
, *ipv6_privacy_extensions
= ASSERT_PTR(data
);
336 s
= ipv6_privacy_extensions_from_string(rvalue
);
338 if (streq(rvalue
, "kernel"))
339 s
= _IPV6_PRIVACY_EXTENSIONS_INVALID
;
341 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
342 "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue
);
347 *ipv6_privacy_extensions
= s
;