1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <netinet/in.h>
5 #include <linux/if_arp.h>
7 #include "missing_network.h"
8 #include "networkd-link.h"
9 #include "networkd-manager.h"
10 #include "networkd-network.h"
11 #include "networkd-sysctl.h"
12 #include "socket-util.h"
13 #include "string-table.h"
14 #include "sysctl-util.h"
16 static bool link_is_configured_for_family(Link
*link
, int family
) {
22 if (link
->flags
& IFF_LOOPBACK
)
25 /* CAN devices do not support IP layer. Most of the functions below are never called for CAN devices,
26 * but link_set_ipv6_mtu() may be called after setting interface MTU, and warn about the failure. For
27 * safety, let's unconditionally check if the interface is not a CAN device. */
28 if (IN_SET(family
, AF_INET
, AF_INET6
) && link
->iftype
== ARPHRD_CAN
)
31 if (family
== AF_INET6
&& !socket_ipv6_is_supported())
37 static int link_update_ipv6_sysctl(Link
*link
) {
40 if (!link_is_configured_for_family(link
, AF_INET6
))
43 if (!link_ipv6_enabled(link
))
46 return sysctl_write_ip_property_boolean(AF_INET6
, link
->ifname
, "disable_ipv6", false);
49 static int link_set_proxy_arp(Link
*link
) {
52 if (!link_is_configured_for_family(link
, AF_INET
))
55 if (link
->network
->proxy_arp
< 0)
58 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "proxy_arp", link
->network
->proxy_arp
> 0);
61 static bool link_ip_forward_enabled(Link
*link
, int family
) {
63 assert(IN_SET(family
, AF_INET
, AF_INET6
));
65 if (!link_is_configured_for_family(link
, family
))
68 return link
->network
->ip_forward
& (family
== AF_INET
? ADDRESS_FAMILY_IPV4
: ADDRESS_FAMILY_IPV6
);
71 static int link_set_ipv4_forward(Link
*link
) {
74 if (!link_ip_forward_enabled(link
, AF_INET
))
77 /* We propagate the forwarding flag from one interface to the
78 * global setting one way. This means: as long as at least one
79 * interface was configured at any time that had IP forwarding
80 * enabled the setting will stay on for good. We do this
81 * primarily to keep IPv4 and IPv6 packet forwarding behaviour
82 * somewhat in sync (see below). */
84 return sysctl_write_ip_property(AF_INET
, NULL
, "ip_forward", "1");
87 static int link_set_ipv6_forward(Link
*link
) {
90 if (!link_ip_forward_enabled(link
, AF_INET6
))
93 /* On Linux, the IPv6 stack does not know a per-interface
94 * packet forwarding setting: either packet forwarding is on
95 * for all, or off for all. We hence don't bother with a
96 * per-interface setting, but simply propagate the interface
97 * flag, if it is set, to the global flag, one-way. Note that
98 * while IPv4 would allow a per-interface flag, we expose the
99 * same behaviour there and also propagate the setting from
100 * one to all, to keep things simple (see above). */
102 return sysctl_write_ip_property(AF_INET6
, "all", "forwarding", "1");
105 static int link_set_ipv4_rp_filter(Link
*link
) {
108 if (!link_is_configured_for_family(link
, AF_INET
))
111 if (link
->network
->ipv4_rp_filter
< 0)
114 return sysctl_write_ip_property_int(AF_INET
, link
->ifname
, "rp_filter", link
->network
->ipv4_rp_filter
);
117 static int link_set_ipv6_privacy_extensions(Link
*link
) {
118 IPv6PrivacyExtensions val
;
121 assert(link
->manager
);
123 if (!link_is_configured_for_family(link
, AF_INET6
))
126 val
= link
->network
->ipv6_privacy_extensions
;
127 if (val
< 0) /* If not specified, then use the global setting. */
128 val
= link
->manager
->ipv6_privacy_extensions
;
130 /* When "kernel", do not update the setting. */
131 if (val
== IPV6_PRIVACY_EXTENSIONS_KERNEL
)
134 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "use_tempaddr", (int) val
);
137 static int link_set_ipv6_accept_ra(Link
*link
) {
140 if (!link_is_configured_for_family(link
, AF_INET6
))
143 return sysctl_write_ip_property(AF_INET6
, link
->ifname
, "accept_ra", "0");
146 static int link_set_ipv6_dad_transmits(Link
*link
) {
149 if (!link_is_configured_for_family(link
, AF_INET6
))
152 if (link
->network
->ipv6_dad_transmits
< 0)
155 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "dad_transmits", link
->network
->ipv6_dad_transmits
);
158 static int link_set_ipv6_hop_limit(Link
*link
) {
161 if (!link_is_configured_for_family(link
, AF_INET6
))
164 if (link
->network
->ipv6_hop_limit
<= 0)
167 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "hop_limit", link
->network
->ipv6_hop_limit
);
170 static int link_set_ipv6_proxy_ndp(Link
*link
) {
175 if (!link_is_configured_for_family(link
, AF_INET6
))
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 if (!link_is_configured_for_family(link
, AF_INET6
))
194 if (link
->network
->ipv6_mtu
== 0)
197 mtu
= link
->network
->ipv6_mtu
;
198 if (mtu
> link
->max_mtu
) {
199 log_link_warning(link
, "Reducing requested IPv6 MTU %"PRIu32
" to the interface's maximum MTU %"PRIu32
".",
204 return sysctl_write_ip_property_uint32(AF_INET6
, link
->ifname
, "mtu", mtu
);
207 static int link_set_ipv4_accept_local(Link
*link
) {
210 if (!link_is_configured_for_family(link
, AF_INET
))
213 if (link
->network
->ipv4_accept_local
< 0)
216 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "accept_local", link
->network
->ipv4_accept_local
> 0);
219 static int link_set_ipv4_route_localnet(Link
*link
) {
222 if (!link_is_configured_for_family(link
, AF_INET
))
225 if (link
->network
->ipv4_route_localnet
< 0)
228 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "route_localnet", link
->network
->ipv4_route_localnet
> 0);
231 static int link_set_ipv4_promote_secondaries(Link
*link
) {
234 if (!link_is_configured_for_family(link
, AF_INET
))
237 /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not
238 * changes between leases. The kernel will remove all secondary IP addresses of an interface
239 * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
240 * secondary IP and when the primary one expires it relies on the kernel to promote the
241 * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
242 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "promote_secondaries", true);
245 int link_set_sysctl(Link
*link
) {
250 /* If IPv6 configured that is static IPv6 address and IPv6LL autoconfiguration is enabled
251 * for this interface, then enable IPv6 */
252 r
= link_update_ipv6_sysctl(link
);
254 log_link_warning_errno(link
, r
, "Cannot enable IPv6, ignoring: %m");
256 r
= link_set_proxy_arp(link
);
258 log_link_warning_errno(link
, r
, "Cannot configure proxy ARP for interface, ignoring: %m");
260 r
= link_set_ipv4_forward(link
);
262 log_link_warning_errno(link
, r
, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
264 r
= link_set_ipv6_forward(link
);
266 log_link_warning_errno(link
, r
, "Cannot configure IPv6 packet forwarding, ignoring: %m");
268 r
= link_set_ipv6_privacy_extensions(link
);
270 log_link_warning_errno(link
, r
, "Cannot configure IPv6 privacy extensions for interface, ignoring: %m");
272 r
= link_set_ipv6_accept_ra(link
);
274 log_link_warning_errno(link
, r
, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m");
276 r
= link_set_ipv6_dad_transmits(link
);
278 log_link_warning_errno(link
, r
, "Cannot set IPv6 dad transmits for interface, ignoring: %m");
280 r
= link_set_ipv6_hop_limit(link
);
282 log_link_warning_errno(link
, r
, "Cannot set IPv6 hop limit for interface, ignoring: %m");
284 r
= link_set_ipv6_proxy_ndp(link
);
286 log_link_warning_errno(link
, r
, "Cannot set IPv6 proxy NDP, ignoring: %m");
288 r
= link_set_ipv6_mtu(link
);
290 log_link_warning_errno(link
, r
, "Cannot set IPv6 MTU, ignoring: %m");
292 r
= link_set_ipv6ll_stable_secret(link
);
294 log_link_warning_errno(link
, r
, "Cannot set stable secret address for IPv6 link-local address: %m");
296 r
= link_set_ipv4_accept_local(link
);
298 log_link_warning_errno(link
, r
, "Cannot set IPv4 accept_local flag for interface, ignoring: %m");
300 r
= link_set_ipv4_route_localnet(link
);
302 log_link_warning_errno(link
, r
, "Cannot set IPv4 route_localnet flag for interface, ignoring: %m");
304 r
= link_set_ipv4_rp_filter(link
);
306 log_link_warning_errno(link
, r
, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m");
308 r
= link_set_ipv4_promote_secondaries(link
);
310 log_link_warning_errno(link
, r
, "Cannot enable promote_secondaries for interface, ignoring: %m");
315 static const char* const ipv6_privacy_extensions_table
[_IPV6_PRIVACY_EXTENSIONS_MAX
] = {
316 [IPV6_PRIVACY_EXTENSIONS_NO
] = "no",
317 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC
] = "prefer-public",
318 [IPV6_PRIVACY_EXTENSIONS_YES
] = "yes",
319 [IPV6_PRIVACY_EXTENSIONS_KERNEL
] = "kernel",
322 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions
, IPv6PrivacyExtensions
,
323 IPV6_PRIVACY_EXTENSIONS_YES
);
324 DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_privacy_extensions
, ipv6_privacy_extensions
, IPv6PrivacyExtensions
,
325 "Failed to parse IPv6 privacy extensions option");
327 static const char* const ip_reverse_path_filter_table
[_IP_REVERSE_PATH_FILTER_MAX
] = {
328 [IP_REVERSE_PATH_FILTER_NO
] = "no",
329 [IP_REVERSE_PATH_FILTER_STRICT
] = "strict",
330 [IP_REVERSE_PATH_FILTER_LOOSE
] = "loose",
333 DEFINE_STRING_TABLE_LOOKUP(ip_reverse_path_filter
, IPReversePathFilter
);
334 DEFINE_CONFIG_PARSE_ENUM(config_parse_ip_reverse_path_filter
, ip_reverse_path_filter
, IPReversePathFilter
,
335 "Failed to parse IP reverse path filter option");