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