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