1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2014 Axis Communications AB. All rights reserved.
12 #include "sd-ipv4acd.h"
13 #include "sd-ipv4ll.h"
15 #include "alloc-util.h"
16 #include "ether-addr-util.h"
17 #include "in-addr-util.h"
18 #include "network-common.h"
19 #include "random-util.h"
20 #include "siphash24.h"
21 #include "sparse-endian.h"
22 #include "string-util.h"
24 #define IPV4LL_NETWORK UINT32_C(0xA9FE0000)
25 #define IPV4LL_NETMASK UINT32_C(0xFFFF0000)
27 #define IPV4LL_DONT_DESTROY(ll) \
28 _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
35 be32_t address
; /* the address pushed to ACD */
36 struct ether_addr mac
;
45 be32_t claimed_address
;
47 sd_ipv4ll_callback_t callback
;
50 sd_ipv4ll_check_mac_callback_t check_mac_callback
;
51 void *check_mac_userdata
;
54 #define log_ipv4ll_errno(ll, error, fmt, ...) \
55 log_interface_prefix_full_errno( \
58 error, fmt, ##__VA_ARGS__)
59 #define log_ipv4ll(ll, fmt, ...) \
60 log_interface_prefix_full_errno_zerook( \
63 0, fmt, ##__VA_ARGS__)
65 static void ipv4ll_on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
);
66 static int ipv4ll_check_mac(sd_ipv4acd
*acd
, const struct ether_addr
*mac
, void *userdata
);
68 static sd_ipv4ll
*ipv4ll_free(sd_ipv4ll
*ll
) {
71 sd_ipv4acd_unref(ll
->acd
);
75 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4ll
, sd_ipv4ll
, ipv4ll_free
);
77 int sd_ipv4ll_new(sd_ipv4ll
**ret
) {
78 _cleanup_(sd_ipv4ll_unrefp
) sd_ipv4ll
*ll
= NULL
;
81 assert_return(ret
, -EINVAL
);
83 ll
= new0(sd_ipv4ll
, 1);
89 r
= sd_ipv4acd_new(&ll
->acd
);
93 r
= sd_ipv4acd_set_callback(ll
->acd
, ipv4ll_on_acd
, ll
);
97 r
= sd_ipv4acd_set_check_mac_callback(ll
->acd
, ipv4ll_check_mac
, ll
);
106 int sd_ipv4ll_stop(sd_ipv4ll
*ll
) {
110 return sd_ipv4acd_stop(ll
->acd
);
113 int sd_ipv4ll_set_ifindex(sd_ipv4ll
*ll
, int ifindex
) {
114 assert_return(ll
, -EINVAL
);
115 assert_return(ifindex
> 0, -EINVAL
);
116 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
118 return sd_ipv4acd_set_ifindex(ll
->acd
, ifindex
);
121 int sd_ipv4ll_get_ifindex(sd_ipv4ll
*ll
) {
125 return sd_ipv4acd_get_ifindex(ll
->acd
);
128 int sd_ipv4ll_set_ifname(sd_ipv4ll
*ll
, const char *ifname
) {
129 assert_return(ll
, -EINVAL
);
130 assert_return(ifname
, -EINVAL
);
132 return sd_ipv4acd_set_ifname(ll
->acd
, ifname
);
135 int sd_ipv4ll_get_ifname(sd_ipv4ll
*ll
, const char **ret
) {
136 assert_return(ll
, -EINVAL
);
138 return sd_ipv4acd_get_ifname(ll
->acd
, ret
);
141 int sd_ipv4ll_set_mac(sd_ipv4ll
*ll
, const struct ether_addr
*addr
) {
144 assert_return(ll
, -EINVAL
);
145 assert_return(addr
, -EINVAL
);
146 assert_return(!ether_addr_is_null(addr
), -EINVAL
);
148 r
= sd_ipv4acd_set_mac(ll
->acd
, addr
);
156 int sd_ipv4ll_detach_event(sd_ipv4ll
*ll
) {
157 assert_return(ll
, -EINVAL
);
159 return sd_ipv4acd_detach_event(ll
->acd
);
162 int sd_ipv4ll_attach_event(sd_ipv4ll
*ll
, sd_event
*event
, int64_t priority
) {
163 assert_return(ll
, -EINVAL
);
165 return sd_ipv4acd_attach_event(ll
->acd
, event
, priority
);
168 int sd_ipv4ll_set_callback(sd_ipv4ll
*ll
, sd_ipv4ll_callback_t cb
, void *userdata
) {
169 assert_return(ll
, -EINVAL
);
172 ll
->userdata
= userdata
;
177 int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll
*ll
, sd_ipv4ll_check_mac_callback_t cb
, void *userdata
) {
178 assert_return(ll
, -EINVAL
);
180 ll
->check_mac_callback
= cb
;
181 ll
->check_mac_userdata
= userdata
;
186 int sd_ipv4ll_get_address(sd_ipv4ll
*ll
, struct in_addr
*address
) {
187 assert_return(ll
, -EINVAL
);
188 assert_return(address
, -EINVAL
);
190 if (ll
->claimed_address
== 0)
193 address
->s_addr
= ll
->claimed_address
;
198 int sd_ipv4ll_set_address_seed(sd_ipv4ll
*ll
, uint64_t seed
) {
199 assert_return(ll
, -EINVAL
);
200 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
202 ll
->seed
.value
= htole64(seed
);
208 int sd_ipv4ll_is_running(sd_ipv4ll
*ll
) {
209 assert_return(ll
, false);
211 return sd_ipv4acd_is_running(ll
->acd
);
214 int sd_ipv4ll_set_address(sd_ipv4ll
*ll
, const struct in_addr
*address
) {
217 assert_return(ll
, -EINVAL
);
218 assert_return(address
, -EINVAL
);
219 assert_return(in4_addr_is_link_local_dynamic(address
), -EINVAL
);
221 r
= sd_ipv4acd_set_address(ll
->acd
, address
);
225 ll
->address
= address
->s_addr
;
230 #define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
232 static int ipv4ll_pick_address(sd_ipv4ll
*ll
) {
240 h
= siphash24(&ll
->seed
, sizeof(ll
->seed
), PICK_HASH_KEY
.bytes
);
242 /* Increase the generation counter by one */
243 ll
->seed
.generation
= htole64(le64toh(ll
->seed
.generation
) + 1);
245 addr
= htobe32((h
& UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK
);
246 } while (addr
== ll
->address
||
247 IN_SET(be32toh(addr
) & 0x0000FF00U
, 0x0000U
, 0xFF00U
));
249 log_ipv4ll(ll
, "Picked new IP address %s.", IN4_ADDR_TO_STRING((const struct in_addr
*) &addr
));
251 return sd_ipv4ll_set_address(ll
, &(struct in_addr
) { addr
});
254 #define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
256 static int ipv4ll_start_internal(sd_ipv4ll
*ll
, bool reset_generation
) {
258 bool picked_address
= false;
260 assert_return(ll
, -EINVAL
);
261 assert_return(!ether_addr_is_null(&ll
->mac
), -EINVAL
);
263 /* If no random seed is set, generate some from the MAC address */
265 ll
->seed
.value
= htole64(siphash24(ll
->mac
.ether_addr_octet
, ETH_ALEN
, MAC_HASH_KEY
.bytes
));
267 if (reset_generation
)
268 ll
->seed
.generation
= 0;
270 if (ll
->address
== 0) {
271 r
= ipv4ll_pick_address(ll
);
275 picked_address
= true;
278 r
= sd_ipv4acd_start(ll
->acd
, reset_generation
);
281 /* We couldn't start? If so, let's forget the picked address again, the user might make a change and
282 * retry, and we want the new data to take effect when picking an address. */
292 int sd_ipv4ll_start(sd_ipv4ll
*ll
) {
293 assert_return(ll
, -EINVAL
);
295 if (sd_ipv4ll_is_running(ll
))
298 return ipv4ll_start_internal(ll
, true);
301 int sd_ipv4ll_restart(sd_ipv4ll
*ll
) {
304 return ipv4ll_start_internal(ll
, false);
307 static void ipv4ll_client_notify(sd_ipv4ll
*ll
, int event
) {
311 ll
->callback(ll
, event
, ll
->userdata
);
314 void ipv4ll_on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
) {
315 sd_ipv4ll
*ll
= ASSERT_PTR(userdata
);
316 IPV4LL_DONT_DESTROY(ll
);
323 case SD_IPV4ACD_EVENT_STOP
:
324 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);
325 ll
->claimed_address
= 0;
328 case SD_IPV4ACD_EVENT_BIND
:
329 ll
->claimed_address
= ll
->address
;
330 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_BIND
);
333 case SD_IPV4ACD_EVENT_CONFLICT
:
334 /* if an address was already bound we must call up to the
335 user to handle this, otherwise we just try again */
336 if (ll
->claimed_address
!= 0) {
337 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_CONFLICT
);
339 ll
->claimed_address
= 0;
341 r
= sd_ipv4ll_restart(ll
);
349 assert_not_reached();
355 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);
358 static int ipv4ll_check_mac(sd_ipv4acd
*acd
, const struct ether_addr
*mac
, void *userdata
) {
359 sd_ipv4ll
*ll
= ASSERT_PTR(userdata
);
361 if (ll
->check_mac_callback
)
362 return ll
->check_mac_callback(ll
, mac
, ll
->check_mac_userdata
);