1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <netinet/in.h>
4 #include <linux/if.h>
5
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"
13
link_update_ipv6_sysctl(Link * link)14 static int link_update_ipv6_sysctl(Link *link) {
15 assert(link);
16
17 if (link->flags & IFF_LOOPBACK)
18 return 0;
19
20 if (!link_ipv6_enabled(link))
21 return 0;
22
23 return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false);
24 }
25
link_set_proxy_arp(Link * link)26 static int link_set_proxy_arp(Link *link) {
27 assert(link);
28
29 if (link->flags & IFF_LOOPBACK)
30 return 0;
31
32 if (!link->network)
33 return 0;
34
35 if (link->network->proxy_arp < 0)
36 return 0;
37
38 return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0);
39 }
40
link_ip_forward_enabled(Link * link,int family)41 static bool link_ip_forward_enabled(Link *link, int family) {
42 assert(link);
43 assert(IN_SET(family, AF_INET, AF_INET6));
44
45 if (family == AF_INET6 && !socket_ipv6_is_supported())
46 return false;
47
48 if (link->flags & IFF_LOOPBACK)
49 return false;
50
51 if (!link->network)
52 return false;
53
54 return link->network->ip_forward & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6);
55 }
56
link_set_ipv4_forward(Link * link)57 static int link_set_ipv4_forward(Link *link) {
58 assert(link);
59
60 if (!link_ip_forward_enabled(link, AF_INET))
61 return 0;
62
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). */
69
70 return sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1");
71 }
72
link_set_ipv6_forward(Link * link)73 static int link_set_ipv6_forward(Link *link) {
74 assert(link);
75
76 if (!link_ip_forward_enabled(link, AF_INET6))
77 return 0;
78
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). */
87
88 return sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1");
89 }
90
link_set_ipv6_privacy_extensions(Link * link)91 static int link_set_ipv6_privacy_extensions(Link *link) {
92 assert(link);
93
94 if (!socket_ipv6_is_supported())
95 return 0;
96
97 if (link->flags & IFF_LOOPBACK)
98 return 0;
99
100 if (!link->network)
101 return 0;
102
103 // this is the special "kernel" value
104 if (link->network->ipv6_privacy_extensions == _IPV6_PRIVACY_EXTENSIONS_INVALID)
105 return 0;
106
107 return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) link->network->ipv6_privacy_extensions);
108 }
109
link_set_ipv6_accept_ra(Link * link)110 static int link_set_ipv6_accept_ra(Link *link) {
111 assert(link);
112
113 /* Make this a NOP if IPv6 is not available */
114 if (!socket_ipv6_is_supported())
115 return 0;
116
117 if (link->flags & IFF_LOOPBACK)
118 return 0;
119
120 if (!link->network)
121 return 0;
122
123 return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0");
124 }
125
link_set_ipv6_dad_transmits(Link * link)126 static int link_set_ipv6_dad_transmits(Link *link) {
127 assert(link);
128
129 /* Make this a NOP if IPv6 is not available */
130 if (!socket_ipv6_is_supported())
131 return 0;
132
133 if (link->flags & IFF_LOOPBACK)
134 return 0;
135
136 if (!link->network)
137 return 0;
138
139 if (link->network->ipv6_dad_transmits < 0)
140 return 0;
141
142 return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits);
143 }
144
link_set_ipv6_hop_limit(Link * link)145 static int link_set_ipv6_hop_limit(Link *link) {
146 assert(link);
147
148 /* Make this a NOP if IPv6 is not available */
149 if (!socket_ipv6_is_supported())
150 return 0;
151
152 if (link->flags & IFF_LOOPBACK)
153 return 0;
154
155 if (!link->network)
156 return 0;
157
158 if (link->network->ipv6_hop_limit < 0)
159 return 0;
160
161 return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit);
162 }
163
link_set_ipv6_proxy_ndp(Link * link)164 static int link_set_ipv6_proxy_ndp(Link *link) {
165 bool v;
166
167 assert(link);
168
169 if (!socket_ipv6_is_supported())
170 return 0;
171
172 if (link->flags & IFF_LOOPBACK)
173 return 0;
174
175 if (!link->network)
176 return 0;
177
178 if (link->network->ipv6_proxy_ndp >= 0)
179 v = link->network->ipv6_proxy_ndp;
180 else
181 v = !set_isempty(link->network->ipv6_proxy_ndp_addresses);
182
183 return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v);
184 }
185
link_set_ipv6_mtu(Link * link)186 int link_set_ipv6_mtu(Link *link) {
187 uint32_t mtu;
188
189 assert(link);
190
191 /* Make this a NOP if IPv6 is not available */
192 if (!socket_ipv6_is_supported())
193 return 0;
194
195 if (link->flags & IFF_LOOPBACK)
196 return 0;
197
198 if (!link->network)
199 return 0;
200
201 if (link->network->ipv6_mtu == 0)
202 return 0;
203
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".",
207 mtu, link->max_mtu);
208 mtu = link->max_mtu;
209 }
210
211 return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu);
212 }
213
link_set_ipv4_accept_local(Link * link)214 static int link_set_ipv4_accept_local(Link *link) {
215 assert(link);
216
217 if (link->flags & IFF_LOOPBACK)
218 return 0;
219
220 if (link->network->ipv4_accept_local < 0)
221 return 0;
222
223 return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local > 0);
224 }
225
link_set_ipv4_route_localnet(Link * link)226 static int link_set_ipv4_route_localnet(Link *link) {
227 assert(link);
228
229 if (link->flags & IFF_LOOPBACK)
230 return 0;
231
232 if (link->network->ipv4_route_localnet < 0)
233 return 0;
234
235 return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0);
236 }
237
link_set_sysctl(Link * link)238 int link_set_sysctl(Link *link) {
239 int r;
240
241 assert(link);
242
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);
246 if (r < 0)
247 log_link_warning_errno(link, r, "Cannot enable IPv6, ignoring: %m");
248
249 r = link_set_proxy_arp(link);
250 if (r < 0)
251 log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface, ignoring: %m");
252
253 r = link_set_ipv4_forward(link);
254 if (r < 0)
255 log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m");
256
257 r = link_set_ipv6_forward(link);
258 if (r < 0)
259 log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m");;
260
261 r = link_set_ipv6_privacy_extensions(link);
262 if (r < 0)
263 log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extensions for interface, ignoring: %m");
264
265 r = link_set_ipv6_accept_ra(link);
266 if (r < 0)
267 log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m");
268
269 r = link_set_ipv6_dad_transmits(link);
270 if (r < 0)
271 log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface, ignoring: %m");
272
273 r = link_set_ipv6_hop_limit(link);
274 if (r < 0)
275 log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface, ignoring: %m");
276
277 r = link_set_ipv6_proxy_ndp(link);
278 if (r < 0)
279 log_link_warning_errno(link, r, "Cannot set IPv6 proxy NDP, ignoring: %m");
280
281 r = link_set_ipv6_mtu(link);
282 if (r < 0)
283 log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
284
285 r = link_set_ipv6ll_stable_secret(link);
286 if (r < 0)
287 log_link_warning_errno(link, r, "Cannot set stable secret address for IPv6 link-local address: %m");
288
289 r = link_set_ipv4_accept_local(link);
290 if (r < 0)
291 log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface, ignoring: %m");
292
293 r = link_set_ipv4_route_localnet(link);
294 if (r < 0)
295 log_link_warning_errno(link, r, "Cannot set IPv4 route_localnet flag for interface, ignoring: %m");
296
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);
303 if (r < 0)
304 log_link_warning_errno(link, r, "Cannot enable promote_secondaries for interface, ignoring: %m");
305
306 return 0;
307 }
308
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",
313 };
314
315 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions,
316 IPV6_PRIVACY_EXTENSIONS_YES);
317
config_parse_ipv6_privacy_extensions(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)318 int config_parse_ipv6_privacy_extensions(
319 const char* unit,
320 const char *filename,
321 unsigned line,
322 const char *section,
323 unsigned section_line,
324 const char *lvalue,
325 int ltype,
326 const char *rvalue,
327 void *data,
328 void *userdata) {
329
330 IPv6PrivacyExtensions s, *ipv6_privacy_extensions = data;
331
332 assert(filename);
333 assert(lvalue);
334 assert(rvalue);
335 assert(ipv6_privacy_extensions);
336
337 s = ipv6_privacy_extensions_from_string(rvalue);
338 if (s < 0) {
339 if (streq(rvalue, "kernel"))
340 s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
341 else {
342 log_syntax(unit, LOG_WARNING, filename, line, 0,
343 "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
344 return 0;
345 }
346 }
347
348 *ipv6_privacy_extensions = s;
349
350 return 0;
351 }
352