]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-sysctl.c
IPv6 RA: Support the Retrans Timer field (IPv6 Conformance Test: v6LC.2.1.5)
[thirdparty/systemd.git] / src / network / networkd-sysctl.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <netinet/in.h>
4 #include <linux/if.h>
5 #include <linux/if_arp.h>
6
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"
15
16 static bool link_is_configured_for_family(Link *link, int family) {
17 assert(link);
18
19 if (!link->network)
20 return false;
21
22 if (link->flags & IFF_LOOPBACK)
23 return false;
24
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)
29 return false;
30
31 if (family == AF_INET6 && !socket_ipv6_is_supported())
32 return false;
33
34 return true;
35 }
36
37 static int link_update_ipv6_sysctl(Link *link) {
38 assert(link);
39
40 if (!link_is_configured_for_family(link, AF_INET6))
41 return 0;
42
43 if (!link_ipv6_enabled(link))
44 return 0;
45
46 return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false);
47 }
48
49 static int link_set_proxy_arp(Link *link) {
50 assert(link);
51
52 if (!link_is_configured_for_family(link, AF_INET))
53 return 0;
54
55 if (link->network->proxy_arp < 0)
56 return 0;
57
58 return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0);
59 }
60
61 static int link_set_proxy_arp_pvlan(Link *link) {
62 assert(link);
63
64 if (!link_is_configured_for_family(link, AF_INET))
65 return 0;
66
67 if (link->network->proxy_arp_pvlan < 0)
68 return 0;
69
70 return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp_pvlan", link->network->proxy_arp_pvlan > 0);
71 }
72
73 static bool link_ip_forward_enabled(Link *link, int family) {
74 assert(link);
75 assert(IN_SET(family, AF_INET, AF_INET6));
76
77 if (!link_is_configured_for_family(link, family))
78 return false;
79
80 return link->network->ip_forward & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6);
81 }
82
83 static int link_set_ipv4_forward(Link *link) {
84 assert(link);
85
86 if (!link_ip_forward_enabled(link, AF_INET))
87 return 0;
88
89 /* We propagate the forwarding flag from one interface to the
90 * global setting one way. This means: as long as at least one
91 * interface was configured at any time that had IP forwarding
92 * enabled the setting will stay on for good. We do this
93 * primarily to keep IPv4 and IPv6 packet forwarding behaviour
94 * somewhat in sync (see below). */
95
96 return sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1");
97 }
98
99 static int link_set_ipv6_forward(Link *link) {
100 assert(link);
101
102 if (!link_ip_forward_enabled(link, AF_INET6))
103 return 0;
104
105 /* On Linux, the IPv6 stack does not know a per-interface
106 * packet forwarding setting: either packet forwarding is on
107 * for all, or off for all. We hence don't bother with a
108 * per-interface setting, but simply propagate the interface
109 * flag, if it is set, to the global flag, one-way. Note that
110 * while IPv4 would allow a per-interface flag, we expose the
111 * same behaviour there and also propagate the setting from
112 * one to all, to keep things simple (see above). */
113
114 return sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1");
115 }
116
117 static int link_set_ipv4_rp_filter(Link *link) {
118 assert(link);
119
120 if (!link_is_configured_for_family(link, AF_INET))
121 return 0;
122
123 if (link->network->ipv4_rp_filter < 0)
124 return 0;
125
126 return sysctl_write_ip_property_int(AF_INET, link->ifname, "rp_filter", link->network->ipv4_rp_filter);
127 }
128
129 static int link_set_ipv6_privacy_extensions(Link *link) {
130 IPv6PrivacyExtensions val;
131
132 assert(link);
133 assert(link->manager);
134
135 if (!link_is_configured_for_family(link, AF_INET6))
136 return 0;
137
138 val = link->network->ipv6_privacy_extensions;
139 if (val < 0) /* If not specified, then use the global setting. */
140 val = link->manager->ipv6_privacy_extensions;
141
142 /* When "kernel", do not update the setting. */
143 if (val == IPV6_PRIVACY_EXTENSIONS_KERNEL)
144 return 0;
145
146 return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) val);
147 }
148
149 static int link_set_ipv6_accept_ra(Link *link) {
150 assert(link);
151
152 if (!link_is_configured_for_family(link, AF_INET6))
153 return 0;
154
155 return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0");
156 }
157
158 static int link_set_ipv6_dad_transmits(Link *link) {
159 assert(link);
160
161 if (!link_is_configured_for_family(link, AF_INET6))
162 return 0;
163
164 if (link->network->ipv6_dad_transmits < 0)
165 return 0;
166
167 return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits);
168 }
169
170 static int link_set_ipv6_hop_limit(Link *link) {
171 assert(link);
172
173 if (!link_is_configured_for_family(link, AF_INET6))
174 return 0;
175
176 if (link->network->ipv6_hop_limit <= 0)
177 return 0;
178
179 return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit);
180 }
181
182 static int link_set_ipv6_retransmission_time(Link *link) {
183 usec_t retrans_time_ms;
184
185 assert(link);
186
187 if (!link_is_configured_for_family(link, AF_INET6))
188 return 0;
189
190 if (!timestamp_is_set(link->network->ipv6_retransmission_time))
191 return 0;
192
193 retrans_time_ms = DIV_ROUND_UP(link->network->ipv6_retransmission_time, USEC_PER_MSEC);
194 if (retrans_time_ms <= 0 || retrans_time_ms > UINT32_MAX)
195 return 0;
196
197 return sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", retrans_time_ms);
198 }
199
200 static int link_set_ipv6_proxy_ndp(Link *link) {
201 bool v;
202
203 assert(link);
204
205 if (!link_is_configured_for_family(link, AF_INET6))
206 return 0;
207
208 if (link->network->ipv6_proxy_ndp >= 0)
209 v = link->network->ipv6_proxy_ndp;
210 else
211 v = !set_isempty(link->network->ipv6_proxy_ndp_addresses);
212
213 return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v);
214 }
215
216 int link_set_ipv6_mtu(Link *link) {
217 uint32_t mtu;
218
219 assert(link);
220
221 if (!link_is_configured_for_family(link, AF_INET6))
222 return 0;
223
224 if (link->network->ipv6_mtu == 0)
225 return 0;
226
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".",
230 mtu, link->max_mtu);
231 mtu = link->max_mtu;
232 }
233
234 return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu);
235 }
236
237 static int link_set_ipv4_accept_local(Link *link) {
238 assert(link);
239
240 if (!link_is_configured_for_family(link, AF_INET))
241 return 0;
242
243 if (link->network->ipv4_accept_local < 0)
244 return 0;
245
246 return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local > 0);
247 }
248
249 static int link_set_ipv4_route_localnet(Link *link) {
250 assert(link);
251
252 if (!link_is_configured_for_family(link, AF_INET))
253 return 0;
254
255 if (link->network->ipv4_route_localnet < 0)
256 return 0;
257
258 return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0);
259 }
260
261 static int link_set_ipv4_promote_secondaries(Link *link) {
262 assert(link);
263
264 if (!link_is_configured_for_family(link, AF_INET))
265 return 0;
266
267 /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not
268 * changes between leases. The kernel will remove all secondary IP addresses of an interface
269 * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
270 * secondary IP and when the primary one expires it relies on the kernel to promote the
271 * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
272 return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true);
273 }
274
275 int link_set_sysctl(Link *link) {
276 int r;
277
278 assert(link);
279
280 /* If IPv6 configured that is static IPv6 address and IPv6LL autoconfiguration is enabled
281 * for this interface, then enable IPv6 */
282 r = link_update_ipv6_sysctl(link);
283 if (r < 0)
284 log_link_warning_errno(link, r, "Cannot enable IPv6, ignoring: %m");
285
286 r = link_set_proxy_arp(link);
287 if (r < 0)
288 log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface, ignoring: %m");
289
290 r = link_set_proxy_arp_pvlan(link);
291 if (r < 0)
292 log_link_warning_errno(link, r, "Cannot configure proxy ARP private VLAN for interface, ignoring: %m");
293
294 r = link_set_ipv4_forward(link);
295 if (r < 0)
296 log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
297
298 r = link_set_ipv6_forward(link);
299 if (r < 0)
300 log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m");
301
302 r = link_set_ipv6_privacy_extensions(link);
303 if (r < 0)
304 log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extensions for interface, ignoring: %m");
305
306 r = link_set_ipv6_accept_ra(link);
307 if (r < 0)
308 log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m");
309
310 r = link_set_ipv6_dad_transmits(link);
311 if (r < 0)
312 log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface, ignoring: %m");
313
314 r = link_set_ipv6_hop_limit(link);
315 if (r < 0)
316 log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface, ignoring: %m");
317
318 r = link_set_ipv6_retransmission_time(link);
319 if (r < 0)
320 log_link_warning_errno(link, r, "Cannot set IPv6 retransmission time for interface, ignoring: %m");
321
322 r = link_set_ipv6_proxy_ndp(link);
323 if (r < 0)
324 log_link_warning_errno(link, r, "Cannot set IPv6 proxy NDP, ignoring: %m");
325
326 r = link_set_ipv6_mtu(link);
327 if (r < 0)
328 log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
329
330 r = link_set_ipv6ll_stable_secret(link);
331 if (r < 0)
332 log_link_warning_errno(link, r, "Cannot set stable secret address for IPv6 link-local address: %m");
333
334 r = link_set_ipv4_accept_local(link);
335 if (r < 0)
336 log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface, ignoring: %m");
337
338 r = link_set_ipv4_route_localnet(link);
339 if (r < 0)
340 log_link_warning_errno(link, r, "Cannot set IPv4 route_localnet flag for interface, ignoring: %m");
341
342 r = link_set_ipv4_rp_filter(link);
343 if (r < 0)
344 log_link_warning_errno(link, r, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m");
345
346 r = link_set_ipv4_promote_secondaries(link);
347 if (r < 0)
348 log_link_warning_errno(link, r, "Cannot enable promote_secondaries for interface, ignoring: %m");
349
350 return 0;
351 }
352
353 static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
354 [IPV6_PRIVACY_EXTENSIONS_NO] = "no",
355 [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
356 [IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
357 [IPV6_PRIVACY_EXTENSIONS_KERNEL] = "kernel",
358 };
359
360 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions,
361 IPV6_PRIVACY_EXTENSIONS_YES);
362 DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_privacy_extensions, ipv6_privacy_extensions, IPv6PrivacyExtensions,
363 "Failed to parse IPv6 privacy extensions option");
364
365 static const char* const ip_reverse_path_filter_table[_IP_REVERSE_PATH_FILTER_MAX] = {
366 [IP_REVERSE_PATH_FILTER_NO] = "no",
367 [IP_REVERSE_PATH_FILTER_STRICT] = "strict",
368 [IP_REVERSE_PATH_FILTER_LOOSE] = "loose",
369 };
370
371 DEFINE_STRING_TABLE_LOOKUP(ip_reverse_path_filter, IPReversePathFilter);
372 DEFINE_CONFIG_PARSE_ENUM(config_parse_ip_reverse_path_filter, ip_reverse_path_filter, IPReversePathFilter,
373 "Failed to parse IP reverse path filter option");