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 #define STABLE_SECRET_APP_ID_1 SD_ID128_MAKE(aa,05,1d,94,43,68,45,07,b9,73,f1,e8,e4,b7,34,52)
15 #define STABLE_SECRET_APP_ID_2 SD_ID128_MAKE(52,c4,40,a0,9f,2f,48,58,a9,3a,f6,29,25,ba,7a,7d)
17 static int link_update_ipv6_sysctl(Link
*link
) {
20 if (link
->flags
& IFF_LOOPBACK
)
23 if (!link_ipv6_enabled(link
))
26 return sysctl_write_ip_property_boolean(AF_INET6
, link
->ifname
, "disable_ipv6", false);
29 static int link_set_proxy_arp(Link
*link
) {
32 if (link
->flags
& IFF_LOOPBACK
)
38 if (link
->network
->proxy_arp
< 0)
41 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "proxy_arp", link
->network
->proxy_arp
> 0);
44 static bool link_ip_forward_enabled(Link
*link
, int family
) {
46 assert(IN_SET(family
, AF_INET
, AF_INET6
));
48 if (family
== AF_INET6
&& !socket_ipv6_is_supported())
51 if (link
->flags
& IFF_LOOPBACK
)
57 return link
->network
->ip_forward
& (family
== AF_INET
? ADDRESS_FAMILY_IPV4
: ADDRESS_FAMILY_IPV6
);
60 static int link_set_ipv4_forward(Link
*link
) {
63 if (!link_ip_forward_enabled(link
, AF_INET
))
66 /* We propagate the forwarding flag from one interface to the
67 * global setting one way. This means: as long as at least one
68 * interface was configured at any time that had IP forwarding
69 * enabled the setting will stay on for good. We do this
70 * primarily to keep IPv4 and IPv6 packet forwarding behaviour
71 * somewhat in sync (see below). */
73 return sysctl_write_ip_property(AF_INET
, NULL
, "ip_forward", "1");
76 static int link_set_ipv6_forward(Link
*link
) {
79 if (!link_ip_forward_enabled(link
, AF_INET6
))
82 /* On Linux, the IPv6 stack does not know a per-interface
83 * packet forwarding setting: either packet forwarding is on
84 * for all, or off for all. We hence don't bother with a
85 * per-interface setting, but simply propagate the interface
86 * flag, if it is set, to the global flag, one-way. Note that
87 * while IPv4 would allow a per-interface flag, we expose the
88 * same behaviour there and also propagate the setting from
89 * one to all, to keep things simple (see above). */
91 return sysctl_write_ip_property(AF_INET6
, "all", "forwarding", "1");
94 static int link_set_ipv6_privacy_extensions(Link
*link
) {
97 if (!socket_ipv6_is_supported())
100 if (link
->flags
& IFF_LOOPBACK
)
106 // this is the special "kernel" value
107 if (link
->network
->ipv6_privacy_extensions
== _IPV6_PRIVACY_EXTENSIONS_INVALID
)
110 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "use_tempaddr", (int) link
->network
->ipv6_privacy_extensions
);
113 static int link_set_ipv6_accept_ra(Link
*link
) {
116 /* Make this a NOP if IPv6 is not available */
117 if (!socket_ipv6_is_supported())
120 if (link
->flags
& IFF_LOOPBACK
)
126 return sysctl_write_ip_property(AF_INET6
, link
->ifname
, "accept_ra", "0");
129 static int link_set_ipv6_dad_transmits(Link
*link
) {
132 /* Make this a NOP if IPv6 is not available */
133 if (!socket_ipv6_is_supported())
136 if (link
->flags
& IFF_LOOPBACK
)
142 if (link
->network
->ipv6_dad_transmits
< 0)
145 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "dad_transmits", link
->network
->ipv6_dad_transmits
);
148 static int link_set_ipv6_hop_limit(Link
*link
) {
151 /* Make this a NOP if IPv6 is not available */
152 if (!socket_ipv6_is_supported())
155 if (link
->flags
& IFF_LOOPBACK
)
161 if (link
->network
->ipv6_hop_limit
< 0)
164 return sysctl_write_ip_property_int(AF_INET6
, link
->ifname
, "hop_limit", link
->network
->ipv6_hop_limit
);
167 static int link_set_ipv6_proxy_ndp(Link
*link
) {
172 if (!socket_ipv6_is_supported())
175 if (link
->flags
& IFF_LOOPBACK
)
181 if (link
->network
->ipv6_proxy_ndp
>= 0)
182 v
= link
->network
->ipv6_proxy_ndp
;
184 v
= !set_isempty(link
->network
->ipv6_proxy_ndp_addresses
);
186 return sysctl_write_ip_property_boolean(AF_INET6
, link
->ifname
, "proxy_ndp", v
);
189 int link_set_ipv6_mtu(Link
*link
) {
194 /* Make this a NOP if IPv6 is not available */
195 if (!socket_ipv6_is_supported())
198 if (link
->flags
& IFF_LOOPBACK
)
204 if (link
->network
->ipv6_mtu
== 0)
207 mtu
= link
->network
->ipv6_mtu
;
208 if (mtu
> link
->max_mtu
) {
209 log_link_warning(link
, "Reducing requested IPv6 MTU %"PRIu32
" to the interface's maximum MTU %"PRIu32
".",
214 return sysctl_write_ip_property_uint32(AF_INET6
, link
->ifname
, "mtu", mtu
);
217 static int link_set_ipv6ll_stable_secret(Link
*link
) {
218 _cleanup_free_
char *str
= NULL
;
223 assert(link
->network
);
225 if (link
->network
->ipv6ll_address_gen_mode
!= IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY
)
228 if (in6_addr_is_set(&link
->network
->ipv6ll_stable_secret
))
229 a
= link
->network
->ipv6ll_stable_secret
;
234 /* Generate a stable secret address from machine-ID and the interface name. */
236 r
= sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_1
, &key
);
238 return log_link_debug_errno(link
, r
, "Failed to generate key: %m");
240 v
= htole64(siphash24_string(link
->ifname
, key
.bytes
));
241 memcpy(a
.s6_addr
, &v
, sizeof(v
));
243 r
= sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_2
, &key
);
245 return log_link_debug_errno(link
, r
, "Failed to generate key: %m");
247 v
= htole64(siphash24_string(link
->ifname
, key
.bytes
));
248 assert_cc(sizeof(v
) * 2 == sizeof(a
.s6_addr
));
249 memcpy(a
.s6_addr
+ sizeof(v
), &v
, sizeof(v
));
252 r
= in6_addr_to_string(&a
, &str
);
256 return sysctl_write_ip_property(AF_INET6
, link
->ifname
, "stable_secret", str
);
259 static int link_set_ipv4_accept_local(Link
*link
) {
262 if (link
->flags
& IFF_LOOPBACK
)
265 if (link
->network
->ipv4_accept_local
< 0)
268 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "accept_local", link
->network
->ipv4_accept_local
> 0);
271 static int link_set_ipv4_route_localnet(Link
*link
) {
274 if (link
->flags
& IFF_LOOPBACK
)
277 if (link
->network
->ipv4_route_localnet
< 0)
280 return sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "route_localnet", link
->network
->ipv4_route_localnet
> 0);
283 int link_set_sysctl(Link
*link
) {
288 /* If IPv6 configured that is static IPv6 address and IPv6LL autoconfiguration is enabled
289 * for this interface, then enable IPv6 */
290 r
= link_update_ipv6_sysctl(link
);
292 log_link_warning_errno(link
, r
, "Cannot enable IPv6, ignoring: %m");
294 r
= link_set_proxy_arp(link
);
296 log_link_warning_errno(link
, r
, "Cannot configure proxy ARP for interface, ignoring: %m");
298 r
= link_set_ipv4_forward(link
);
300 log_link_warning_errno(link
, r
, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
302 r
= link_set_ipv6_forward(link
);
304 log_link_warning_errno(link
, r
, "Cannot configure IPv6 packet forwarding, ignoring: %m");;
306 r
= link_set_ipv6_privacy_extensions(link
);
308 log_link_warning_errno(link
, r
, "Cannot configure IPv6 privacy extensions for interface, ignoring: %m");
310 r
= link_set_ipv6_accept_ra(link
);
312 log_link_warning_errno(link
, r
, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m");
314 r
= link_set_ipv6_dad_transmits(link
);
316 log_link_warning_errno(link
, r
, "Cannot set IPv6 dad transmits for interface, ignoring: %m");
318 r
= link_set_ipv6_hop_limit(link
);
320 log_link_warning_errno(link
, r
, "Cannot set IPv6 hop limit for interface, ignoring: %m");
322 r
= link_set_ipv6_proxy_ndp(link
);
324 log_link_warning_errno(link
, r
, "Cannot set IPv6 proxy NDP, ignoring: %m");
326 r
= link_set_ipv6_mtu(link
);
328 log_link_warning_errno(link
, r
, "Cannot set IPv6 MTU, ignoring: %m");
330 r
= link_set_ipv6ll_stable_secret(link
);
332 log_link_warning_errno(link
, r
, "Cannot set stable secret address for IPv6 link local address: %m");
334 r
= link_set_ipv4_accept_local(link
);
336 log_link_warning_errno(link
, r
, "Cannot set IPv4 accept_local flag for interface, ignoring: %m");
338 r
= link_set_ipv4_route_localnet(link
);
340 log_link_warning_errno(link
, r
, "Cannot set IPv4 route_localnet flag for interface, ignoring: %m");
342 /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not
343 * changes between leases. The kernel will remove all secondary IP addresses of an interface
344 * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
345 * secondary IP and when the primary one expires it relies on the kernel to promote the
346 * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
347 r
= sysctl_write_ip_property_boolean(AF_INET
, link
->ifname
, "promote_secondaries", true);
349 log_link_warning_errno(link
, r
, "Cannot enable promote_secondaries for interface, ignoring: %m");
354 static const char* const ipv6_privacy_extensions_table
[_IPV6_PRIVACY_EXTENSIONS_MAX
] = {
355 [IPV6_PRIVACY_EXTENSIONS_NO
] = "no",
356 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC
] = "prefer-public",
357 [IPV6_PRIVACY_EXTENSIONS_YES
] = "yes",
360 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions
, IPv6PrivacyExtensions
,
361 IPV6_PRIVACY_EXTENSIONS_YES
);
363 int config_parse_ipv6_privacy_extensions(
365 const char *filename
,
368 unsigned section_line
,
375 IPv6PrivacyExtensions s
, *ipv6_privacy_extensions
= data
;
380 assert(ipv6_privacy_extensions
);
382 s
= ipv6_privacy_extensions_from_string(rvalue
);
384 if (streq(rvalue
, "kernel"))
385 s
= _IPV6_PRIVACY_EXTENSIONS_INVALID
;
387 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
388 "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue
);
393 *ipv6_privacy_extensions
= s
;