]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-ipv4ll.c
Drop the text argument from assert_not_reached()
[thirdparty/systemd.git] / src / libsystemd-network / sd-ipv4ll.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2014 Axis Communications AB. All rights reserved.
4 ***/
5
6 #include <arpa/inet.h>
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #include "sd-id128.h"
12 #include "sd-ipv4acd.h"
13 #include "sd-ipv4ll.h"
14
15 #include "alloc-util.h"
16 #include "ether-addr-util.h"
17 #include "in-addr-util.h"
18 #include "log-link.h"
19 #include "random-util.h"
20 #include "siphash24.h"
21 #include "sparse-endian.h"
22 #include "string-util.h"
23 #include "util.h"
24
25 #define IPV4LL_NETWORK UINT32_C(0xA9FE0000)
26 #define IPV4LL_NETMASK UINT32_C(0xFFFF0000)
27
28 #define IPV4LL_DONT_DESTROY(ll) \
29 _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
30
31 struct sd_ipv4ll {
32 unsigned n_ref;
33
34 sd_ipv4acd *acd;
35
36 be32_t address; /* the address pushed to ACD */
37 struct ether_addr mac;
38
39 struct {
40 le64_t value;
41 le64_t generation;
42 } seed;
43 bool seed_set;
44
45 /* External */
46 be32_t claimed_address;
47
48 sd_ipv4ll_callback_t callback;
49 void *userdata;
50
51 sd_ipv4ll_check_mac_callback_t check_mac_callback;
52 void *check_mac_userdata;
53 };
54
55 #define log_ipv4ll_errno(ll, error, fmt, ...) \
56 log_interface_prefix_full_errno( \
57 "IPv4LL: ", \
58 sd_ipv4ll_get_ifname(ll), \
59 error, fmt, ##__VA_ARGS__)
60 #define log_ipv4ll(ll, fmt, ...) \
61 log_interface_prefix_full_errno_zerook( \
62 "IPv4LL: ", \
63 sd_ipv4ll_get_ifname(ll), \
64 0, fmt, ##__VA_ARGS__)
65
66 static void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata);
67 static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata);
68
69 static sd_ipv4ll *ipv4ll_free(sd_ipv4ll *ll) {
70 assert(ll);
71
72 sd_ipv4acd_unref(ll->acd);
73 return mfree(ll);
74 }
75
76 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4ll, sd_ipv4ll, ipv4ll_free);
77
78 int sd_ipv4ll_new(sd_ipv4ll **ret) {
79 _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL;
80 int r;
81
82 assert_return(ret, -EINVAL);
83
84 ll = new0(sd_ipv4ll, 1);
85 if (!ll)
86 return -ENOMEM;
87
88 ll->n_ref = 1;
89
90 r = sd_ipv4acd_new(&ll->acd);
91 if (r < 0)
92 return r;
93
94 r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
95 if (r < 0)
96 return r;
97
98 r = sd_ipv4acd_set_check_mac_callback(ll->acd, ipv4ll_check_mac, ll);
99 if (r < 0)
100 return r;
101
102 *ret = TAKE_PTR(ll);
103
104 return 0;
105 }
106
107 int sd_ipv4ll_stop(sd_ipv4ll *ll) {
108 if (!ll)
109 return 0;
110
111 return sd_ipv4acd_stop(ll->acd);
112 }
113
114 int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int ifindex) {
115 assert_return(ll, -EINVAL);
116 assert_return(ifindex > 0, -EINVAL);
117 assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
118
119 return sd_ipv4acd_set_ifindex(ll->acd, ifindex);
120 }
121
122 int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll) {
123 if (!ll)
124 return -EINVAL;
125
126 return sd_ipv4acd_get_ifindex(ll->acd);
127 }
128
129 int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *ifname) {
130 assert_return(ll, -EINVAL);
131 assert_return(ifname, -EINVAL);
132
133 return sd_ipv4acd_set_ifname(ll->acd, ifname);
134 }
135
136 const char *sd_ipv4ll_get_ifname(sd_ipv4ll *ll) {
137 if (!ll)
138 return NULL;
139
140 return sd_ipv4acd_get_ifname(ll->acd);
141 }
142
143 int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
144 int r;
145
146 assert_return(ll, -EINVAL);
147 assert_return(addr, -EINVAL);
148 assert_return(!ether_addr_is_null(addr), -EINVAL);
149
150 r = sd_ipv4acd_set_mac(ll->acd, addr);
151 if (r < 0)
152 return r;
153
154 ll->mac = *addr;
155 return 0;
156 }
157
158 int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
159 assert_return(ll, -EINVAL);
160
161 return sd_ipv4acd_detach_event(ll->acd);
162 }
163
164 int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) {
165 assert_return(ll, -EINVAL);
166
167 return sd_ipv4acd_attach_event(ll->acd, event, priority);
168 }
169
170 int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) {
171 assert_return(ll, -EINVAL);
172
173 ll->callback = cb;
174 ll->userdata = userdata;
175
176 return 0;
177 }
178
179 int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata) {
180 assert_return(ll, -EINVAL);
181
182 ll->check_mac_callback = cb;
183 ll->check_mac_userdata = userdata;
184
185 return 0;
186 }
187
188 int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
189 assert_return(ll, -EINVAL);
190 assert_return(address, -EINVAL);
191
192 if (ll->claimed_address == 0)
193 return -ENOENT;
194
195 address->s_addr = ll->claimed_address;
196
197 return 0;
198 }
199
200 int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) {
201 assert_return(ll, -EINVAL);
202 assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
203
204 ll->seed.value = htole64(seed);
205 ll->seed_set = true;
206
207 return 0;
208 }
209
210 int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
211 assert_return(ll, false);
212
213 return sd_ipv4acd_is_running(ll->acd);
214 }
215
216 static bool ipv4ll_address_is_valid(const struct in_addr *address) {
217 assert(address);
218
219 if (!in4_addr_is_link_local(address))
220 return false;
221
222 return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
223 }
224
225 int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
226 int r;
227
228 assert_return(ll, -EINVAL);
229 assert_return(address, -EINVAL);
230 assert_return(ipv4ll_address_is_valid(address), -EINVAL);
231
232 r = sd_ipv4acd_set_address(ll->acd, address);
233 if (r < 0)
234 return r;
235
236 ll->address = address->s_addr;
237
238 return 0;
239 }
240
241 #define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
242
243 static int ipv4ll_pick_address(sd_ipv4ll *ll) {
244 _cleanup_free_ char *address = NULL;
245 be32_t addr;
246
247 assert(ll);
248
249 do {
250 uint64_t h;
251
252 h = siphash24(&ll->seed, sizeof(ll->seed), PICK_HASH_KEY.bytes);
253
254 /* Increase the generation counter by one */
255 ll->seed.generation = htole64(le64toh(ll->seed.generation) + 1);
256
257 addr = htobe32((h & UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK);
258 } while (addr == ll->address ||
259 IN_SET(be32toh(addr) & 0x0000FF00U, 0x0000U, 0xFF00U));
260
261 (void) in_addr_to_string(AF_INET, &(union in_addr_union) { .in.s_addr = addr }, &address);
262 log_ipv4ll(ll, "Picked new IP address %s.", strna(address));
263
264 return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr });
265 }
266
267 #define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
268
269 static int ipv4ll_start_internal(sd_ipv4ll *ll, bool reset_generation) {
270 int r;
271 bool picked_address = false;
272
273 assert_return(ll, -EINVAL);
274 assert_return(!ether_addr_is_null(&ll->mac), -EINVAL);
275
276 /* If no random seed is set, generate some from the MAC address */
277 if (!ll->seed_set)
278 ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes));
279
280 if (reset_generation)
281 ll->seed.generation = 0;
282
283 if (ll->address == 0) {
284 r = ipv4ll_pick_address(ll);
285 if (r < 0)
286 return r;
287
288 picked_address = true;
289 }
290
291 r = sd_ipv4acd_start(ll->acd, reset_generation);
292 if (r < 0) {
293
294 /* We couldn't start? If so, let's forget the picked address again, the user might make a change and
295 * retry, and we want the new data to take effect when picking an address. */
296 if (picked_address)
297 ll->address = 0;
298
299 return r;
300 }
301
302 return 1;
303 }
304
305 int sd_ipv4ll_start(sd_ipv4ll *ll) {
306 assert_return(ll, -EINVAL);
307
308 if (sd_ipv4ll_is_running(ll))
309 return 0;
310
311 return ipv4ll_start_internal(ll, true);
312 }
313
314 int sd_ipv4ll_restart(sd_ipv4ll *ll) {
315 ll->address = 0;
316
317 return ipv4ll_start_internal(ll, false);
318 }
319
320 static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
321 assert(ll);
322
323 if (ll->callback)
324 ll->callback(ll, event, ll->userdata);
325 }
326
327 void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
328 sd_ipv4ll *ll = userdata;
329 IPV4LL_DONT_DESTROY(ll);
330 int r;
331
332 assert(acd);
333 assert(ll);
334
335 switch (event) {
336
337 case SD_IPV4ACD_EVENT_STOP:
338 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
339 ll->claimed_address = 0;
340 break;
341
342 case SD_IPV4ACD_EVENT_BIND:
343 ll->claimed_address = ll->address;
344 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
345 break;
346
347 case SD_IPV4ACD_EVENT_CONFLICT:
348 /* if an address was already bound we must call up to the
349 user to handle this, otherwise we just try again */
350 if (ll->claimed_address != 0) {
351 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT);
352
353 ll->claimed_address = 0;
354 } else {
355 r = sd_ipv4ll_restart(ll);
356 if (r < 0)
357 goto error;
358 }
359
360 break;
361
362 default:
363 assert_not_reached();
364 }
365
366 return;
367
368 error:
369 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
370 }
371
372 static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) {
373 sd_ipv4ll *ll = userdata;
374
375 assert(ll);
376
377 if (ll->check_mac_callback)
378 return ll->check_mac_callback(ll, mac, ll->check_mac_userdata);
379
380 return 0;
381 }