]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
b22d8a00 | 2 | |
9aa5d8ba | 3 | #include <netinet/in.h> |
b22d8a00 TG |
4 | #include <linux/if.h> |
5 | ||
b5cc5591 | 6 | #include "netif-util.h" |
23f53b99 | 7 | #include "networkd-address.h" |
29104ded | 8 | #include "networkd-ipv4acd.h" |
ca5ad760 | 9 | #include "networkd-ipv4ll.h" |
23f53b99 | 10 | #include "networkd-link.h" |
ca5ad760 | 11 | #include "networkd-manager.h" |
76c5a0f2 | 12 | #include "networkd-queue.h" |
ca5ad760 | 13 | #include "parse-util.h" |
b22d8a00 | 14 | |
29104ded YW |
15 | bool link_ipv4ll_enabled(Link *link) { |
16 | assert(link); | |
17 | ||
18 | if (!link_ipv4acd_supported(link)) | |
19 | return false; | |
20 | ||
21 | if (!link->network) | |
22 | return false; | |
23 | ||
24 | if (link->network->bond) | |
25 | return false; | |
26 | ||
27 | return link->network->link_local & ADDRESS_FAMILY_IPV4; | |
28 | } | |
29 | ||
76c5a0f2 | 30 | static int address_new_from_ipv4ll(Link *link, Address **ret) { |
8e766630 | 31 | _cleanup_(address_freep) Address *address = NULL; |
b22d8a00 TG |
32 | struct in_addr addr; |
33 | int r; | |
34 | ||
35 | assert(link); | |
76c5a0f2 YW |
36 | assert(link->ipv4ll); |
37 | assert(ret); | |
b22d8a00 TG |
38 | |
39 | r = sd_ipv4ll_get_address(link->ipv4ll, &addr); | |
40 | if (r < 0) | |
76c5a0f2 | 41 | return r; |
b22d8a00 | 42 | |
f0213e37 | 43 | r = address_new(&address); |
fc95c359 | 44 | if (r < 0) |
76c5a0f2 | 45 | return -ENOMEM; |
b22d8a00 | 46 | |
3b6a3bde | 47 | address->source = NETWORK_CONFIG_SOURCE_IPV4LL; |
b22d8a00 TG |
48 | address->family = AF_INET; |
49 | address->in_addr.in = addr; | |
50 | address->prefixlen = 16; | |
51 | address->scope = RT_SCOPE_LINK; | |
76c5a0f2 | 52 | address->route_metric = IPV4LL_ROUTE_METRIC; |
e680486d | 53 | address_set_broadcast(address, link); |
76c5a0f2 YW |
54 | |
55 | *ret = TAKE_PTR(address); | |
56 | return 0; | |
57 | } | |
58 | ||
59 | static int ipv4ll_address_lost(Link *link) { | |
60 | _cleanup_(address_freep) Address *address = NULL; | |
3b6a3bde | 61 | Address *existing; |
76c5a0f2 YW |
62 | int r; |
63 | ||
64 | assert(link); | |
b22d8a00 | 65 | |
76c5a0f2 YW |
66 | link->ipv4ll_address_configured = false; |
67 | ||
68 | r = address_new_from_ipv4ll(link, &address); | |
69 | if (r == -ENOENT) | |
70 | return 0; | |
807341ec YW |
71 | if (r < 0) |
72 | return r; | |
b22d8a00 | 73 | |
3b6a3bde YW |
74 | if (address_get(link, address, &existing) < 0) |
75 | return 0; | |
76 | ||
77 | if (existing->source != NETWORK_CONFIG_SOURCE_IPV4LL) | |
78 | return 0; | |
79 | ||
80 | if (!address_exists(existing)) | |
81 | return 0; | |
82 | ||
76c5a0f2 YW |
83 | log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR, |
84 | IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); | |
b22d8a00 | 85 | |
3b6a3bde | 86 | return address_remove(existing); |
b22d8a00 TG |
87 | } |
88 | ||
80d62d4f | 89 | static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) { |
b22d8a00 TG |
90 | int r; |
91 | ||
92 | assert(link); | |
1c09d84e | 93 | assert(!link->ipv4ll_address_configured); |
b22d8a00 | 94 | |
5a07fa9d YW |
95 | r = address_configure_handler_internal(rtnl, m, link, "Could not set ipv4ll address"); |
96 | if (r <= 0) | |
97 | return r; | |
b22d8a00 | 98 | |
1c09d84e | 99 | link->ipv4ll_address_configured = true; |
2aa7d367 | 100 | link_check_ready(link); |
b22d8a00 TG |
101 | |
102 | return 1; | |
103 | } | |
104 | ||
105 | static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { | |
76c5a0f2 | 106 | _cleanup_(address_freep) Address *address = NULL; |
b22d8a00 TG |
107 | int r; |
108 | ||
109 | assert(ll); | |
110 | assert(link); | |
111 | ||
1c09d84e | 112 | link->ipv4ll_address_configured = false; |
79316750 | 113 | |
76c5a0f2 | 114 | r = address_new_from_ipv4ll(link, &address); |
b22d8a00 TG |
115 | if (r == -ENOENT) |
116 | return 0; | |
b22d8a00 TG |
117 | if (r < 0) |
118 | return r; | |
119 | ||
76c5a0f2 YW |
120 | log_link_debug(link, "IPv4 link-local claim "IPV4_ADDRESS_FMT_STR, |
121 | IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); | |
b22d8a00 | 122 | |
76c5a0f2 | 123 | return link_request_address(link, TAKE_PTR(address), true, NULL, ipv4ll_address_handler, NULL); |
b22d8a00 TG |
124 | } |
125 | ||
9ed794a3 | 126 | static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { |
99534007 | 127 | Link *link = ASSERT_PTR(userdata); |
b22d8a00 TG |
128 | int r; |
129 | ||
b22d8a00 | 130 | assert(link->network); |
b22d8a00 TG |
131 | |
132 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) | |
133 | return; | |
134 | ||
89346ac6 | 135 | switch (event) { |
be19c5b5 | 136 | case SD_IPV4LL_EVENT_STOP: |
0698ff41 JR |
137 | r = ipv4ll_address_lost(link); |
138 | if (r < 0) { | |
139 | link_enter_failed(link); | |
140 | return; | |
141 | } | |
142 | break; | |
be19c5b5 | 143 | case SD_IPV4LL_EVENT_CONFLICT: |
b22d8a00 TG |
144 | r = ipv4ll_address_lost(link); |
145 | if (r < 0) { | |
146 | link_enter_failed(link); | |
147 | return; | |
148 | } | |
0698ff41 JR |
149 | |
150 | r = sd_ipv4ll_restart(ll); | |
89d8ed99 | 151 | if (r < 0) { |
aa5f4c77 | 152 | log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); |
89d8ed99 YW |
153 | link_enter_failed(link); |
154 | } | |
b22d8a00 | 155 | break; |
be19c5b5 | 156 | case SD_IPV4LL_EVENT_BIND: |
b22d8a00 TG |
157 | r = ipv4ll_address_claimed(ll, link); |
158 | if (r < 0) { | |
79316750 | 159 | log_link_error(link, "Failed to configure ipv4ll address: %m"); |
b22d8a00 TG |
160 | link_enter_failed(link); |
161 | return; | |
162 | } | |
163 | break; | |
164 | default: | |
b45e4eb6 | 165 | log_link_warning(link, "IPv4 link-local unknown event: %d", event); |
b22d8a00 TG |
166 | break; |
167 | } | |
168 | } | |
169 | ||
d7ab6ef0 | 170 | static int ipv4ll_check_mac(sd_ipv4ll *ll, const struct ether_addr *mac, void *userdata) { |
99534007 | 171 | Manager *m = ASSERT_PTR(userdata); |
d7ab6ef0 YW |
172 | struct hw_addr_data hw_addr; |
173 | ||
d7ab6ef0 YW |
174 | assert(mac); |
175 | ||
176 | hw_addr = (struct hw_addr_data) { | |
177 | .length = ETH_ALEN, | |
178 | .ether = *mac, | |
179 | }; | |
180 | ||
181 | return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0; | |
182 | } | |
183 | ||
b22d8a00 | 184 | int ipv4ll_configure(Link *link) { |
dbe81cbd | 185 | uint64_t seed; |
b22d8a00 TG |
186 | int r; |
187 | ||
188 | assert(link); | |
2ffd6d73 | 189 | |
3ca1fab7 | 190 | if (!link_ipv4ll_enabled(link)) |
2ffd6d73 | 191 | return 0; |
b22d8a00 | 192 | |
bc9e40c9 YW |
193 | if (link->ipv4ll) |
194 | return -EBUSY; | |
ca97e7cd | 195 | |
bc9e40c9 YW |
196 | r = sd_ipv4ll_new(&link->ipv4ll); |
197 | if (r < 0) | |
198 | return r; | |
199 | ||
200 | r = sd_ipv4ll_attach_event(link->ipv4ll, link->manager->event, 0); | |
201 | if (r < 0) | |
202 | return r; | |
b22d8a00 | 203 | |
8de56fb3 YW |
204 | if (link->dev && |
205 | net_get_unique_predictable_data(link->dev, true, &seed) >= 0) { | |
51517f9e YW |
206 | r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed); |
207 | if (r < 0) | |
208 | return r; | |
b22d8a00 TG |
209 | } |
210 | ||
ca2b7cd8 | 211 | r = sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.ether); |
b22d8a00 TG |
212 | if (r < 0) |
213 | return r; | |
214 | ||
2f8e7633 | 215 | r = sd_ipv4ll_set_ifindex(link->ipv4ll, link->ifindex); |
b22d8a00 TG |
216 | if (r < 0) |
217 | return r; | |
218 | ||
219 | r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link); | |
220 | if (r < 0) | |
221 | return r; | |
222 | ||
d7ab6ef0 | 223 | return sd_ipv4ll_set_check_mac_callback(link->ipv4ll, ipv4ll_check_mac, link->manager); |
b22d8a00 | 224 | } |
ca5ad760 | 225 | |
a3adb4a6 | 226 | int ipv4ll_update_mac(Link *link) { |
a3adb4a6 YW |
227 | assert(link); |
228 | ||
f8d6397a YW |
229 | if (link->hw_addr.length != ETH_ALEN) |
230 | return 0; | |
231 | if (ether_addr_is_null(&link->hw_addr.ether)) | |
232 | return 0; | |
a3adb4a6 YW |
233 | if (!link->ipv4ll) |
234 | return 0; | |
235 | ||
f8d6397a | 236 | return sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.ether); |
a3adb4a6 YW |
237 | } |
238 | ||
ca5ad760 YW |
239 | int config_parse_ipv4ll( |
240 | const char* unit, | |
241 | const char *filename, | |
242 | unsigned line, | |
243 | const char *section, | |
244 | unsigned section_line, | |
245 | const char *lvalue, | |
246 | int ltype, | |
247 | const char *rvalue, | |
248 | void *data, | |
249 | void *userdata) { | |
250 | ||
99534007 | 251 | AddressFamily *link_local = ASSERT_PTR(data); |
ca5ad760 YW |
252 | int r; |
253 | ||
254 | assert(filename); | |
255 | assert(lvalue); | |
256 | assert(rvalue); | |
ca5ad760 YW |
257 | |
258 | /* Note that this is mostly like | |
2d792895 | 259 | * config_parse_address_family(), except that it |
ca5ad760 YW |
260 | * applies only to IPv4 */ |
261 | ||
262 | r = parse_boolean(rvalue); | |
263 | if (r < 0) { | |
d96edb2c | 264 | log_syntax(unit, LOG_WARNING, filename, line, r, |
ca5ad760 YW |
265 | "Failed to parse %s=%s, ignoring assignment. " |
266 | "Note that the setting %s= is deprecated, please use LinkLocalAddressing= instead.", | |
267 | lvalue, rvalue, lvalue); | |
268 | return 0; | |
269 | } | |
270 | ||
271 | SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, r); | |
272 | ||
273 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
274 | "%s=%s is deprecated, please use LinkLocalAddressing=%s instead.", | |
2d792895 | 275 | lvalue, rvalue, address_family_to_string(*link_local)); |
ca5ad760 YW |
276 | |
277 | return 0; | |
278 | } | |
34b63c9e AK |
279 | |
280 | int config_parse_ipv4ll_address( | |
281 | const char* unit, | |
282 | const char *filename, | |
283 | unsigned line, | |
284 | const char *section, | |
285 | unsigned section_line, | |
286 | const char *lvalue, | |
287 | int ltype, | |
288 | const char *rvalue, | |
289 | void *data, | |
290 | void *userdata) { | |
291 | ||
292 | union in_addr_union a; | |
293 | struct in_addr *ipv4ll_address = ASSERT_PTR(data); | |
294 | int r; | |
295 | ||
296 | assert(filename); | |
297 | assert(lvalue); | |
298 | assert(rvalue); | |
299 | ||
300 | if (isempty(rvalue)) { | |
301 | *ipv4ll_address = (struct in_addr) {}; | |
302 | return 0; | |
303 | } | |
304 | ||
305 | r = in_addr_from_string(AF_INET, rvalue, &a); | |
306 | if (r < 0) { | |
307 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
308 | "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); | |
309 | return 0; | |
310 | } | |
d3efcd2d | 311 | if (!in4_addr_is_link_local_dynamic(&a.in)) { |
34b63c9e | 312 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
d3efcd2d YW |
313 | "Specified address cannot be used as an IPv4 link local address, ignoring assignment: %s", |
314 | rvalue); | |
34b63c9e AK |
315 | return 0; |
316 | } | |
317 | ||
318 | *ipv4ll_address = a.in; | |
319 | return 0; | |
320 | } |