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