]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-ipv4ll.c
log: minimize includes in log.h
[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 = ll;
119 ll = NULL;
120
121 return 0;
122 }
123
124 int sd_ipv4ll_stop(sd_ipv4ll *ll) {
125 assert_return(ll, -EINVAL);
126
127 return sd_ipv4acd_stop(ll->acd);
128 }
129
130 int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int ifindex) {
131 assert_return(ll, -EINVAL);
132 assert_return(ifindex > 0, -EINVAL);
133 assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
134
135 return sd_ipv4acd_set_ifindex(ll->acd, ifindex);
136 }
137
138 int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
139 int r;
140
141 assert_return(ll, -EINVAL);
142 assert_return(addr, -EINVAL);
143 assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
144
145 r = sd_ipv4acd_set_mac(ll->acd, addr);
146 if (r < 0)
147 return r;
148
149 ll->mac = *addr;
150 return 0;
151 }
152
153 int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
154 assert_return(ll, -EINVAL);
155
156 return sd_ipv4acd_detach_event(ll->acd);
157 }
158
159 int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) {
160 assert_return(ll, -EINVAL);
161
162 return sd_ipv4acd_attach_event(ll->acd, event, priority);
163 }
164
165 int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) {
166 assert_return(ll, -EINVAL);
167
168 ll->callback = cb;
169 ll->userdata = userdata;
170
171 return 0;
172 }
173
174 int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
175 assert_return(ll, -EINVAL);
176 assert_return(address, -EINVAL);
177
178 if (ll->claimed_address == 0)
179 return -ENOENT;
180
181 address->s_addr = ll->claimed_address;
182
183 return 0;
184 }
185
186 int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) {
187 assert_return(ll, -EINVAL);
188 assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
189
190 ll->seed.value = htole64(seed);
191 ll->seed_set = true;
192
193 return 0;
194 }
195
196 int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
197 assert_return(ll, false);
198
199 return sd_ipv4acd_is_running(ll->acd);
200 }
201
202 static bool ipv4ll_address_is_valid(const struct in_addr *address) {
203 assert(address);
204
205 if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address))
206 return false;
207
208 return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
209 }
210
211 int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
212 int r;
213
214 assert_return(ll, -EINVAL);
215 assert_return(address, -EINVAL);
216 assert_return(ipv4ll_address_is_valid(address), -EINVAL);
217
218 r = sd_ipv4acd_set_address(ll->acd, address);
219 if (r < 0)
220 return r;
221
222 ll->address = address->s_addr;
223
224 return 0;
225 }
226
227 #define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
228
229 static int ipv4ll_pick_address(sd_ipv4ll *ll) {
230 _cleanup_free_ char *address = NULL;
231 be32_t addr;
232
233 assert(ll);
234
235 do {
236 uint64_t h;
237
238 h = siphash24(&ll->seed, sizeof(ll->seed), PICK_HASH_KEY.bytes);
239
240 /* Increase the generation counter by one */
241 ll->seed.generation = htole64(le64toh(ll->seed.generation) + 1);
242
243 addr = htobe32((h & UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK);
244 } while (addr == ll->address ||
245 IN_SET(be32toh(addr) & 0x0000FF00U, 0x0000U, 0xFF00U));
246
247 (void) in_addr_to_string(AF_INET, &(union in_addr_union) { .in.s_addr = addr }, &address);
248 log_ipv4ll(ll, "Picked new IP address %s.", strna(address));
249
250 return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr });
251 }
252
253 int sd_ipv4ll_restart(sd_ipv4ll *ll) {
254 ll->address = 0;
255
256 return sd_ipv4ll_start(ll);
257 }
258
259 #define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
260
261 int sd_ipv4ll_start(sd_ipv4ll *ll) {
262 int r;
263 bool picked_address = false;
264
265 assert_return(ll, -EINVAL);
266 assert_return(!ether_addr_is_null(&ll->mac), -EINVAL);
267 assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
268
269 /* If no random seed is set, generate some from the MAC address */
270 if (!ll->seed_set)
271 ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes));
272
273 /* Restart the generation counter. */
274 ll->seed.generation = 0;
275
276 if (ll->address == 0) {
277 r = ipv4ll_pick_address(ll);
278 if (r < 0)
279 return r;
280
281 picked_address = true;
282 }
283
284 r = sd_ipv4acd_start(ll->acd);
285 if (r < 0) {
286
287 /* We couldn't start? If so, let's forget the picked address again, the user might make a change and
288 * retry, and we want the new data to take effect when picking an address. */
289 if (picked_address)
290 ll->address = 0;
291
292 return r;
293 }
294
295 return 0;
296 }
297
298 static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
299 assert(ll);
300
301 if (ll->callback)
302 ll->callback(ll, event, ll->userdata);
303 }
304
305 void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
306 sd_ipv4ll *ll = userdata;
307 IPV4LL_DONT_DESTROY(ll);
308 int r;
309
310 assert(acd);
311 assert(ll);
312
313 switch (event) {
314
315 case SD_IPV4ACD_EVENT_STOP:
316 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
317 ll->claimed_address = 0;
318 break;
319
320 case SD_IPV4ACD_EVENT_BIND:
321 ll->claimed_address = ll->address;
322 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
323 break;
324
325 case SD_IPV4ACD_EVENT_CONFLICT:
326 /* if an address was already bound we must call up to the
327 user to handle this, otherwise we just try again */
328 if (ll->claimed_address != 0) {
329 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT);
330
331 ll->claimed_address = 0;
332 } else {
333 r = ipv4ll_pick_address(ll);
334 if (r < 0)
335 goto error;
336
337 r = sd_ipv4acd_start(ll->acd);
338 if (r < 0)
339 goto error;
340 }
341
342 break;
343
344 default:
345 assert_not_reached("Invalid IPv4ACD event.");
346 }
347
348 return;
349
350 error:
351 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
352 }