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-manager.h"
9 #include "networkd-network.h"
10 #include "networkd-sysctl.h"
11 #include "socket-util.h"
12 #include "string-table.h"
13 #include "sysctl-util.h"
15 static int link_update_ipv6_sysctl(Link
*link
) {
18 if (link
->flags
& IFF_LOOPBACK
)
21 if (!link_ipv6_enabled(link
))
24 return sysctl_write_ip_property_boolean(AF_INET6
, link
->ifname
, "disable_ipv6", false);
27 static int link_set_proxy_arp(Link
*link
) {
30 if (link
->flags
& IFF_LOOPBACK
)
36 if (link
->network
->proxy_arp
< 0)
39 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "proxy_arp", link
->network
->proxy_arp
> 0);
42 static bool link_ip_forward_enabled(Link
*link
, int family
) {
44 assert(IN_SET(family
, AF_INET
, AF_INET6
));
46 if (family
== AF_INET6
&& !socket_ipv6_is_supported())
49 if (link
->flags
& IFF_LOOPBACK
)
55 return link
->network
->ip_forward
& (family
== AF_INET
? ADDRESS_FAMILY_IPV4
: ADDRESS_FAMILY_IPV6
);
58 static int link_set_ipv4_forward(Link
*link
) {
61 if (!link_ip_forward_enabled(link
, AF_INET
))
64 /* We propagate the forwarding flag from one interface to the
65 * global setting one way. This means: as long as at least one
66 * interface was configured at any time that had IP forwarding
67 * enabled the setting will stay on for good. We do this
68 * primarily to keep IPv4 and IPv6 packet forwarding behaviour
69 * somewhat in sync (see below). */
71 return sysctl_write_ip_property(AF_INET
, NULL
, "ip_forward", "1");
74 static int link_set_ipv6_forward(Link
*link
) {
77 if (!link_ip_forward_enabled(link
, AF_INET6
))
80 /* On Linux, the IPv6 stack does not know a per-interface
81 * packet forwarding setting: either packet forwarding is on
82 * for all, or off for all. We hence don't bother with a
83 * per-interface setting, but simply propagate the interface
84 * flag, if it is set, to the global flag, one-way. Note that
85 * while IPv4 would allow a per-interface flag, we expose the
86 * same behaviour there and also propagate the setting from
87 * one to all, to keep things simple (see above). */
89 return sysctl_write_ip_property(AF_INET6
, "all", "forwarding", "1");
92 static int link_set_ipv4_rp_filter(Link
*link
) {
95 if (link
->flags
& IFF_LOOPBACK
)
101 if (link
->network
->ipv4_rp_filter
< 0)
104 return sysctl_write_ip_property_int(AF_INET
, link
->ifname
, "rp_filter", link
->network
->ipv4_rp_filter
);
107 static int link_set_ipv6_privacy_extensions(Link
*link
) {
108 IPv6PrivacyExtensions val
;
111 assert(link
->manager
);
113 if (!socket_ipv6_is_supported())
116 if (link
->flags
& IFF_LOOPBACK
)
122 val
= link
->network
->ipv6_privacy_extensions
;
123 if (val
< 0) /* If not specified, then use the global setting. */
124 val
= link
->manager
->ipv6_privacy_extensions
;
126 /* When "kernel", do not update the setting. */
127 if (val
== IPV6_PRIVACY_EXTENSIONS_KERNEL
)
130 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "use_tempaddr", (int) val
);
133 static int link_set_ipv6_accept_ra(Link
*link
) {
136 /* Make this a NOP if IPv6 is not available */
137 if (!socket_ipv6_is_supported())
140 if (link
->flags
& IFF_LOOPBACK
)
146 return sysctl_write_ip_property(AF_INET6
, link
->ifname
, "accept_ra", "0");
149 static int link_set_ipv6_dad_transmits(Link
*link
) {
152 /* Make this a NOP if IPv6 is not available */
153 if (!socket_ipv6_is_supported())
156 if (link
->flags
& IFF_LOOPBACK
)
162 if (link
->network
->ipv6_dad_transmits
< 0)
165 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "dad_transmits", link
->network
->ipv6_dad_transmits
);
168 int link_set_ipv6_hop_limit(Link
*link
) {
171 /* Make this a NOP if IPv6 is not available */
172 if (!socket_ipv6_is_supported())
175 if (link
->flags
& IFF_LOOPBACK
)
181 if (link
->network
->ipv6_hop_limit
< 0)
184 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "hop_limit", link
->network
->ipv6_hop_limit
);
187 static int link_set_ipv6_proxy_ndp(Link
*link
) {
192 if (!socket_ipv6_is_supported())
195 if (link
->flags
& IFF_LOOPBACK
)
201 if (link
->network
->ipv6_proxy_ndp
>= 0)
202 v
= link
->network
->ipv6_proxy_ndp
;
204 v
= !set_isempty(link
->network
->ipv6_proxy_ndp_addresses
);
206 return sysctl_write_ip_property_boolean(AF_INET6
, link
->ifname
, "proxy_ndp", v
);
209 int link_set_ipv6_mtu(Link
*link
) {
214 /* Make this a NOP if IPv6 is not available */
215 if (!socket_ipv6_is_supported())
218 if (link
->flags
& IFF_LOOPBACK
)
224 if (link
->network
->ipv6_mtu
== 0)
227 mtu
= link
->network
->ipv6_mtu
;
228 if (mtu
> link
->max_mtu
) {
229 log_link_warning(link
, "Reducing requested IPv6 MTU %"PRIu32
" to the interface's maximum MTU %"PRIu32
".",
234 return sysctl_write_ip_property_uint32(AF_INET6
, link
->ifname
, "mtu", mtu
);
237 static int link_set_ipv4_accept_local(Link
*link
) {
240 if (link
->flags
& IFF_LOOPBACK
)
243 if (link
->network
->ipv4_accept_local
< 0)
246 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "accept_local", link
->network
->ipv4_accept_local
> 0);
249 static int link_set_ipv4_route_localnet(Link
*link
) {
252 if (link
->flags
& IFF_LOOPBACK
)
255 if (link
->network
->ipv4_route_localnet
< 0)
258 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "route_localnet", link
->network
->ipv4_route_localnet
> 0);
261 int link_set_sysctl(Link
*link
) {
266 /* If IPv6 configured that is static IPv6 address and IPv6LL autoconfiguration is enabled
267 * for this interface, then enable IPv6 */
268 r
= link_update_ipv6_sysctl(link
);
270 log_link_warning_errno(link
, r
, "Cannot enable IPv6, ignoring: %m");
272 r
= link_set_proxy_arp(link
);
274 log_link_warning_errno(link
, r
, "Cannot configure proxy ARP for interface, ignoring: %m");
276 r
= link_set_ipv4_forward(link
);
278 log_link_warning_errno(link
, r
, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
280 r
= link_set_ipv6_forward(link
);
282 log_link_warning_errno(link
, r
, "Cannot configure IPv6 packet forwarding, ignoring: %m");
284 r
= link_set_ipv6_privacy_extensions(link
);
286 log_link_warning_errno(link
, r
, "Cannot configure IPv6 privacy extensions for interface, ignoring: %m");
288 r
= link_set_ipv6_accept_ra(link
);
290 log_link_warning_errno(link
, r
, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m");
292 r
= link_set_ipv6_dad_transmits(link
);
294 log_link_warning_errno(link
, r
, "Cannot set IPv6 dad transmits for interface, ignoring: %m");
296 r
= link_set_ipv6_hop_limit(link
);
298 log_link_warning_errno(link
, r
, "Cannot set IPv6 hop limit for interface, ignoring: %m");
300 r
= link_set_ipv6_proxy_ndp(link
);
302 log_link_warning_errno(link
, r
, "Cannot set IPv6 proxy NDP, ignoring: %m");
304 r
= link_set_ipv6_mtu(link
);
306 log_link_warning_errno(link
, r
, "Cannot set IPv6 MTU, ignoring: %m");
308 r
= link_set_ipv6ll_stable_secret(link
);
310 log_link_warning_errno(link
, r
, "Cannot set stable secret address for IPv6 link-local address: %m");
312 r
= link_set_ipv4_accept_local(link
);
314 log_link_warning_errno(link
, r
, "Cannot set IPv4 accept_local flag for interface, ignoring: %m");
316 r
= link_set_ipv4_route_localnet(link
);
318 log_link_warning_errno(link
, r
, "Cannot set IPv4 route_localnet flag for interface, ignoring: %m");
320 r
= link_set_ipv4_rp_filter(link
);
322 log_link_warning_errno(link
, r
, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m");
324 /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not
325 * changes between leases. The kernel will remove all secondary IP addresses of an interface
326 * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
327 * secondary IP and when the primary one expires it relies on the kernel to promote the
328 * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
329 r
= sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "promote_secondaries", true);
331 log_link_warning_errno(link
, r
, "Cannot enable promote_secondaries for interface, ignoring: %m");
336 static const char* const ipv6_privacy_extensions_table
[_IPV6_PRIVACY_EXTENSIONS_MAX
] = {
337 [IPV6_PRIVACY_EXTENSIONS_NO
] = "no",
338 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC
] = "prefer-public",
339 [IPV6_PRIVACY_EXTENSIONS_YES
] = "yes",
340 [IPV6_PRIVACY_EXTENSIONS_KERNEL
] = "kernel",
343 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions
, IPv6PrivacyExtensions
,
344 IPV6_PRIVACY_EXTENSIONS_YES
);
345 DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_privacy_extensions
, ipv6_privacy_extensions
, IPv6PrivacyExtensions
,
346 "Failed to parse IPv6 privacy extensions option");
348 static const char* const ip_reverse_path_filter_table
[_IP_REVERSE_PATH_FILTER_MAX
] = {
349 [IP_REVERSE_PATH_FILTER_NO
] = "no",
350 [IP_REVERSE_PATH_FILTER_STRICT
] = "strict",
351 [IP_REVERSE_PATH_FILTER_LOOSE
] = "loose",
354 DEFINE_STRING_TABLE_LOOKUP(ip_reverse_path_filter
, IPReversePathFilter
);
355 DEFINE_CONFIG_PARSE_ENUM(config_parse_ip_reverse_path_filter
, ip_reverse_path_filter
, IPReversePathFilter
,
356 "Failed to parse IP reverse path filter option");