]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-ipv6ll.c
network: introduce per-interface IP forwarding settings
[thirdparty/systemd.git] / src / network / networkd-ipv6ll.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <linux/if.h>
4 #include <linux/if_arp.h>
5
6 #include "in-addr-util.h"
7 #include "networkd-address.h"
8 #include "networkd-ipv6ll.h"
9 #include "networkd-link.h"
10 #include "networkd-network.h"
11 #include "networkd-util.h"
12 #include "socket-util.h"
13 #include "string-table.h"
14 #include "strv.h"
15 #include "sysctl-util.h"
16
17 bool link_ipv6ll_enabled(Link *link) {
18 assert(link);
19
20 if (!socket_ipv6_is_supported())
21 return false;
22
23 if (link->flags & IFF_LOOPBACK)
24 return false;
25
26 if (!link->network)
27 return false;
28
29 if (link->iftype == ARPHRD_CAN)
30 return false;
31
32 if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "sit", "vti", "nlmon"))
33 return false;
34
35 if (link->network->bond)
36 return false;
37
38 return link->network->link_local & ADDRESS_FAMILY_IPV6;
39 }
40
41 bool link_may_have_ipv6ll(Link *link, bool check_multicast) {
42 assert(link);
43
44 /*
45 * This is equivalent to link_ipv6ll_enabled() for non-WireGuard interfaces.
46 *
47 * For WireGuard interface, the kernel does not assign any IPv6LL addresses, but we can assign
48 * it manually. It is necessary to set an IPv6LL address manually to run NDisc or RADV on
49 * WireGuard interface. Note, also Multicast=yes must be set. See #17380.
50 *
51 * TODO: May be better to introduce GenerateIPv6LinkLocalAddress= setting, and use algorithms
52 * used in networkd-address-generation.c
53 */
54
55 if (link_ipv6ll_enabled(link))
56 return true;
57
58 /* IPv6LL address can be manually assigned on WireGuard interface. */
59 if (streq_ptr(link->kind, "wireguard")) {
60 Address *a;
61
62 if (!link->network)
63 return false;
64
65 if (check_multicast && !FLAGS_SET(link->flags, IFF_MULTICAST) && link->network->multicast <= 0)
66 return false;
67
68 ORDERED_HASHMAP_FOREACH(a, link->network->addresses_by_section) {
69 if (a->family != AF_INET6)
70 continue;
71 if (in6_addr_is_set(&a->in_addr_peer.in6))
72 continue;
73 if (in6_addr_is_link_local(&a->in_addr.in6))
74 return true;
75 }
76 }
77
78 return false;
79 }
80
81 IPv6LinkLocalAddressGenMode link_get_ipv6ll_addrgen_mode(Link *link) {
82 assert(link);
83
84 if (!link_ipv6ll_enabled(link))
85 return IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE;
86
87 if (link->network->ipv6ll_address_gen_mode >= 0)
88 return link->network->ipv6ll_address_gen_mode;
89
90 if (in6_addr_is_set(&link->network->ipv6ll_stable_secret))
91 return IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY;
92
93 return IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64;
94 }
95
96 int ipv6ll_addrgen_mode_fill_message(sd_netlink_message *message, IPv6LinkLocalAddressGenMode mode) {
97 int r;
98
99 assert(message);
100 assert(mode >= 0 && mode < _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX);
101
102 r = sd_netlink_message_open_container(message, IFLA_AF_SPEC);
103 if (r < 0)
104 return r;
105
106 r = sd_netlink_message_open_container(message, AF_INET6);
107 if (r < 0)
108 return r;
109
110 r = sd_netlink_message_append_u8(message, IFLA_INET6_ADDR_GEN_MODE, mode);
111 if (r < 0)
112 return r;
113
114 r = sd_netlink_message_close_container(message);
115 if (r < 0)
116 return r;
117
118 r = sd_netlink_message_close_container(message);
119 if (r < 0)
120 return r;
121
122 return 0;
123 }
124
125 int link_update_ipv6ll_addrgen_mode(Link *link, sd_netlink_message *message) {
126 uint8_t mode;
127 int family, r;
128
129 assert(link);
130 assert(message);
131
132 r = sd_rtnl_message_get_family(message, &family);
133 if (r < 0)
134 return r;
135
136 if (family != AF_UNSPEC)
137 return 0;
138
139 r = sd_netlink_message_enter_container(message, IFLA_AF_SPEC);
140 if (r == -ENODATA)
141 return 0;
142 if (r < 0)
143 return r;
144
145 r = sd_netlink_message_enter_container(message, AF_INET6);
146 if (r == -ENODATA)
147 return sd_netlink_message_exit_container(message);
148 if (r < 0)
149 return r;
150
151 mode = (uint8_t) link->ipv6ll_address_gen_mode;
152 r = sd_netlink_message_read_u8(message, IFLA_INET6_ADDR_GEN_MODE, &mode);
153 if (r < 0 && r != -ENODATA)
154 return r;
155
156 r = sd_netlink_message_exit_container(message);
157 if (r < 0)
158 return r;
159
160 r = sd_netlink_message_exit_container(message);
161 if (r < 0)
162 return r;
163
164 if (mode == (uint8_t) link->ipv6ll_address_gen_mode)
165 return 0;
166
167 if (mode >= _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX) {
168 log_link_debug(link, "Received invalid IPv6 link-local address generation mode (%u), ignoring.", mode);
169 return 0;
170 }
171
172 if (link->ipv6ll_address_gen_mode < 0)
173 log_link_debug(link, "Saved IPv6 link-local address generation mode: %s",
174 ipv6_link_local_address_gen_mode_to_string(mode));
175 else
176 log_link_debug(link, "IPv6 link-local address generation mode is changed: %s -> %s",
177 ipv6_link_local_address_gen_mode_to_string(link->ipv6ll_address_gen_mode),
178 ipv6_link_local_address_gen_mode_to_string(mode));
179
180 link->ipv6ll_address_gen_mode = mode;
181 return 0;
182 }
183
184 #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)
185 #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)
186
187 int link_set_ipv6ll_stable_secret(Link *link) {
188 struct in6_addr a;
189 int r;
190
191 assert(link);
192 assert(link->network);
193
194 if (link->network->ipv6ll_address_gen_mode != IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY)
195 return 0;
196
197 if (in6_addr_is_set(&link->network->ipv6ll_stable_secret))
198 a = link->network->ipv6ll_stable_secret;
199 else {
200 sd_id128_t key;
201 le64_t v;
202
203 /* Generate a stable secret address from machine-ID and the interface name. */
204
205 r = sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_1, &key);
206 if (r < 0)
207 return log_link_debug_errno(link, r, "Failed to generate key: %m");
208
209 v = htole64(siphash24_string(link->ifname, key.bytes));
210 memcpy(a.s6_addr, &v, sizeof(v));
211
212 r = sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_2, &key);
213 if (r < 0)
214 return log_link_debug_errno(link, r, "Failed to generate key: %m");
215
216 v = htole64(siphash24_string(link->ifname, key.bytes));
217 assert_cc(sizeof(v) * 2 == sizeof(a.s6_addr));
218 memcpy(a.s6_addr + sizeof(v), &v, sizeof(v));
219 }
220
221 return sysctl_write_ip_property(AF_INET6, link->ifname, "stable_secret",
222 IN6_ADDR_TO_STRING(&a));
223 }
224
225 int link_set_ipv6ll_addrgen_mode(Link *link, IPv6LinkLocalAddressGenMode mode) {
226 assert(link);
227 assert(mode >= 0 && mode < _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX);
228
229 if (mode == link->ipv6ll_address_gen_mode)
230 return 0;
231
232 return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "addr_gen_mode", mode);
233 }
234
235 static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX] = {
236 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64] = "eui64",
237 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE] = "none",
238 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy",
239 [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM] = "random",
240 };
241
242 DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode);
243 DEFINE_CONFIG_PARSE_ENUM(
244 config_parse_ipv6_link_local_address_gen_mode,
245 ipv6_link_local_address_gen_mode,
246 IPv6LinkLocalAddressGenMode,
247 "Failed to parse IPv6 link-local address generation mode");