]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
5e0534f1 YW |
2 | |
3 | #include <netinet/in.h> | |
4 | #include <linux/if.h> | |
5 | ||
6 | #include "missing_network.h" | |
7 | #include "networkd-link.h" | |
932ef6ec | 8 | #include "networkd-manager.h" |
5e0534f1 YW |
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" | |
14 | ||
15 | static int link_update_ipv6_sysctl(Link *link) { | |
16 | assert(link); | |
17 | ||
18 | if (link->flags & IFF_LOOPBACK) | |
19 | return 0; | |
20 | ||
21 | if (!link_ipv6_enabled(link)) | |
22 | return 0; | |
23 | ||
24 | return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false); | |
25 | } | |
26 | ||
27 | static int link_set_proxy_arp(Link *link) { | |
28 | assert(link); | |
29 | ||
30 | if (link->flags & IFF_LOOPBACK) | |
31 | return 0; | |
32 | ||
33 | if (!link->network) | |
34 | return 0; | |
35 | ||
36 | if (link->network->proxy_arp < 0) | |
37 | return 0; | |
38 | ||
39 | return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0); | |
40 | } | |
41 | ||
3773eb54 | 42 | static bool link_ip_forward_enabled(Link *link, int family) { |
5e0534f1 YW |
43 | assert(link); |
44 | assert(IN_SET(family, AF_INET, AF_INET6)); | |
45 | ||
46 | if (family == AF_INET6 && !socket_ipv6_is_supported()) | |
47 | return false; | |
48 | ||
49 | if (link->flags & IFF_LOOPBACK) | |
50 | return false; | |
51 | ||
52 | if (!link->network) | |
53 | return false; | |
54 | ||
55 | return link->network->ip_forward & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6); | |
56 | } | |
57 | ||
58 | static int link_set_ipv4_forward(Link *link) { | |
59 | assert(link); | |
60 | ||
61 | if (!link_ip_forward_enabled(link, AF_INET)) | |
62 | return 0; | |
63 | ||
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). */ | |
70 | ||
71 | return sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1"); | |
72 | } | |
73 | ||
74 | static int link_set_ipv6_forward(Link *link) { | |
75 | assert(link); | |
76 | ||
77 | if (!link_ip_forward_enabled(link, AF_INET6)) | |
78 | return 0; | |
79 | ||
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). */ | |
88 | ||
89 | return sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1"); | |
90 | } | |
91 | ||
9c72e8f8 SS |
92 | static int link_set_ipv4_rp_filter(Link *link) { |
93 | assert(link); | |
94 | ||
95 | if (link->flags & IFF_LOOPBACK) | |
96 | return 0; | |
97 | ||
98 | if (!link->network) | |
99 | return 0; | |
100 | ||
101 | if (link->network->ipv4_rp_filter < 0) | |
102 | return 0; | |
103 | ||
104 | return sysctl_write_ip_property_int(AF_INET, link->ifname, "rp_filter", link->network->ipv4_rp_filter); | |
105 | } | |
106 | ||
5e0534f1 | 107 | static int link_set_ipv6_privacy_extensions(Link *link) { |
932ef6ec YW |
108 | IPv6PrivacyExtensions val; |
109 | ||
5e0534f1 | 110 | assert(link); |
932ef6ec | 111 | assert(link->manager); |
5e0534f1 YW |
112 | |
113 | if (!socket_ipv6_is_supported()) | |
114 | return 0; | |
115 | ||
116 | if (link->flags & IFF_LOOPBACK) | |
117 | return 0; | |
118 | ||
119 | if (!link->network) | |
120 | return 0; | |
121 | ||
932ef6ec YW |
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; | |
125 | ||
7cab7850 | 126 | /* When "kernel", do not update the setting. */ |
932ef6ec | 127 | if (val == IPV6_PRIVACY_EXTENSIONS_KERNEL) |
d3ccb1b4 FK |
128 | return 0; |
129 | ||
932ef6ec | 130 | return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) val); |
5e0534f1 YW |
131 | } |
132 | ||
133 | static int link_set_ipv6_accept_ra(Link *link) { | |
134 | assert(link); | |
135 | ||
136 | /* Make this a NOP if IPv6 is not available */ | |
137 | if (!socket_ipv6_is_supported()) | |
138 | return 0; | |
139 | ||
140 | if (link->flags & IFF_LOOPBACK) | |
141 | return 0; | |
142 | ||
143 | if (!link->network) | |
144 | return 0; | |
145 | ||
146 | return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0"); | |
147 | } | |
148 | ||
149 | static int link_set_ipv6_dad_transmits(Link *link) { | |
150 | assert(link); | |
151 | ||
152 | /* Make this a NOP if IPv6 is not available */ | |
153 | if (!socket_ipv6_is_supported()) | |
154 | return 0; | |
155 | ||
156 | if (link->flags & IFF_LOOPBACK) | |
157 | return 0; | |
158 | ||
159 | if (!link->network) | |
160 | return 0; | |
161 | ||
162 | if (link->network->ipv6_dad_transmits < 0) | |
163 | return 0; | |
164 | ||
165 | return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits); | |
166 | } | |
167 | ||
93e583aa | 168 | static int link_set_ipv6_hop_limit(Link *link) { |
5e0534f1 YW |
169 | assert(link); |
170 | ||
171 | /* Make this a NOP if IPv6 is not available */ | |
172 | if (!socket_ipv6_is_supported()) | |
173 | return 0; | |
174 | ||
175 | if (link->flags & IFF_LOOPBACK) | |
176 | return 0; | |
177 | ||
178 | if (!link->network) | |
179 | return 0; | |
180 | ||
986e1823 | 181 | if (link->network->ipv6_hop_limit <= 0) |
5e0534f1 YW |
182 | return 0; |
183 | ||
184 | return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit); | |
185 | } | |
186 | ||
d8350b60 YW |
187 | static int link_set_ipv6_proxy_ndp(Link *link) { |
188 | bool v; | |
189 | ||
190 | assert(link); | |
191 | ||
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_proxy_ndp >= 0) | |
202 | v = link->network->ipv6_proxy_ndp; | |
203 | else | |
204 | v = !set_isempty(link->network->ipv6_proxy_ndp_addresses); | |
205 | ||
206 | return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v); | |
207 | } | |
208 | ||
fa283812 | 209 | int link_set_ipv6_mtu(Link *link) { |
e56e1a15 YW |
210 | uint32_t mtu; |
211 | ||
fa283812 YW |
212 | assert(link); |
213 | ||
214 | /* Make this a NOP if IPv6 is not available */ | |
215 | if (!socket_ipv6_is_supported()) | |
216 | return 0; | |
217 | ||
218 | if (link->flags & IFF_LOOPBACK) | |
219 | return 0; | |
220 | ||
221 | if (!link->network) | |
222 | return 0; | |
223 | ||
224 | if (link->network->ipv6_mtu == 0) | |
225 | return 0; | |
226 | ||
e56e1a15 YW |
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); | |
fa283812 YW |
235 | } |
236 | ||
5e0534f1 YW |
237 | static int link_set_ipv4_accept_local(Link *link) { |
238 | assert(link); | |
239 | ||
240 | if (link->flags & IFF_LOOPBACK) | |
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 | ||
d75bf6cf SS |
249 | static int link_set_ipv4_route_localnet(Link *link) { |
250 | assert(link); | |
251 | ||
252 | if (link->flags & IFF_LOOPBACK) | |
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 | ||
5e0534f1 YW |
261 | int link_set_sysctl(Link *link) { |
262 | int r; | |
263 | ||
264 | assert(link); | |
265 | ||
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); | |
269 | if (r < 0) | |
270 | log_link_warning_errno(link, r, "Cannot enable IPv6, ignoring: %m"); | |
271 | ||
272 | r = link_set_proxy_arp(link); | |
273 | if (r < 0) | |
274 | log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface, ignoring: %m"); | |
275 | ||
276 | r = link_set_ipv4_forward(link); | |
277 | if (r < 0) | |
278 | log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m"); | |
279 | ||
280 | r = link_set_ipv6_forward(link); | |
281 | if (r < 0) | |
891fc28b | 282 | log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m"); |
5e0534f1 YW |
283 | |
284 | r = link_set_ipv6_privacy_extensions(link); | |
285 | if (r < 0) | |
7eeaf72b | 286 | log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extensions for interface, ignoring: %m"); |
5e0534f1 YW |
287 | |
288 | r = link_set_ipv6_accept_ra(link); | |
289 | if (r < 0) | |
290 | log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface, ignoring: %m"); | |
291 | ||
292 | r = link_set_ipv6_dad_transmits(link); | |
293 | if (r < 0) | |
294 | log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface, ignoring: %m"); | |
295 | ||
296 | r = link_set_ipv6_hop_limit(link); | |
297 | if (r < 0) | |
298 | log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface, ignoring: %m"); | |
299 | ||
d8350b60 YW |
300 | r = link_set_ipv6_proxy_ndp(link); |
301 | if (r < 0) | |
302 | log_link_warning_errno(link, r, "Cannot set IPv6 proxy NDP, ignoring: %m"); | |
303 | ||
fa283812 YW |
304 | r = link_set_ipv6_mtu(link); |
305 | if (r < 0) | |
306 | log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m"); | |
307 | ||
9e1432d5 YW |
308 | r = link_set_ipv6ll_stable_secret(link); |
309 | if (r < 0) | |
f81ac115 | 310 | log_link_warning_errno(link, r, "Cannot set stable secret address for IPv6 link-local address: %m"); |
9e1432d5 | 311 | |
5e0534f1 YW |
312 | r = link_set_ipv4_accept_local(link); |
313 | if (r < 0) | |
314 | log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface, ignoring: %m"); | |
315 | ||
d75bf6cf SS |
316 | r = link_set_ipv4_route_localnet(link); |
317 | if (r < 0) | |
318 | log_link_warning_errno(link, r, "Cannot set IPv4 route_localnet flag for interface, ignoring: %m"); | |
319 | ||
9c72e8f8 SS |
320 | r = link_set_ipv4_rp_filter(link); |
321 | if (r < 0) | |
322 | log_link_warning_errno(link, r, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m"); | |
323 | ||
a4176853 YW |
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); | |
330 | if (r < 0) | |
331 | log_link_warning_errno(link, r, "Cannot enable promote_secondaries for interface, ignoring: %m"); | |
332 | ||
5e0534f1 YW |
333 | return 0; |
334 | } | |
335 | ||
5e0534f1 | 336 | static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = { |
7cab7850 | 337 | [IPV6_PRIVACY_EXTENSIONS_NO] = "no", |
5e0534f1 | 338 | [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public", |
7cab7850 YW |
339 | [IPV6_PRIVACY_EXTENSIONS_YES] = "yes", |
340 | [IPV6_PRIVACY_EXTENSIONS_KERNEL] = "kernel", | |
5e0534f1 YW |
341 | }; |
342 | ||
343 | DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_privacy_extensions, IPv6PrivacyExtensions, | |
344 | IPV6_PRIVACY_EXTENSIONS_YES); | |
7cab7850 YW |
345 | DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_privacy_extensions, ipv6_privacy_extensions, IPv6PrivacyExtensions, |
346 | "Failed to parse IPv6 privacy extensions option"); | |
9c72e8f8 SS |
347 | |
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", | |
352 | }; | |
353 | ||
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"); |