1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2014 Axis Communications AB. All rights reserved.
6 Copyright (C) 2015 Tom Gundersen
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.
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.
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/>.
22 #include <arpa/inet.h>
28 #include "sd-ipv4acd.h"
29 #include "sd-ipv4ll.h"
31 #include "alloc-util.h"
32 #include "ether-addr-util.h"
33 #include "in-addr-util.h"
35 #include "random-util.h"
36 #include "siphash24.h"
37 #include "sparse-endian.h"
38 #include "string-util.h"
41 #define IPV4LL_NETWORK UINT32_C(0xA9FE0000)
42 #define IPV4LL_NETMASK UINT32_C(0xFFFF0000)
44 #define IPV4LL_DONT_DESTROY(ll) \
45 _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
52 be32_t address
; /* the address pushed to ACD */
53 struct ether_addr mac
;
62 be32_t claimed_address
;
64 sd_ipv4ll_callback_t callback
;
68 #define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__)
69 #define log_ipv4ll(ll, fmt, ...) log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__)
71 static void ipv4ll_on_acd(sd_ipv4acd
*ll
, int event
, void *userdata
);
73 sd_ipv4ll
*sd_ipv4ll_ref(sd_ipv4ll
*ll
) {
77 assert(ll
->n_ref
>= 1);
83 sd_ipv4ll
*sd_ipv4ll_unref(sd_ipv4ll
*ll
) {
87 assert(ll
->n_ref
>= 1);
93 sd_ipv4acd_unref(ll
->acd
);
97 int sd_ipv4ll_new(sd_ipv4ll
**ret
) {
98 _cleanup_(sd_ipv4ll_unrefp
) sd_ipv4ll
*ll
= NULL
;
101 assert_return(ret
, -EINVAL
);
103 ll
= new0(sd_ipv4ll
, 1);
109 r
= sd_ipv4acd_new(&ll
->acd
);
113 r
= sd_ipv4acd_set_callback(ll
->acd
, ipv4ll_on_acd
, ll
);
123 int sd_ipv4ll_stop(sd_ipv4ll
*ll
) {
124 assert_return(ll
, -EINVAL
);
126 return sd_ipv4acd_stop(ll
->acd
);
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
);
134 return sd_ipv4acd_set_ifindex(ll
->acd
, ifindex
);
137 int sd_ipv4ll_set_mac(sd_ipv4ll
*ll
, const struct ether_addr
*addr
) {
140 assert_return(ll
, -EINVAL
);
141 assert_return(addr
, -EINVAL
);
142 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
144 r
= sd_ipv4acd_set_mac(ll
->acd
, addr
);
152 int sd_ipv4ll_detach_event(sd_ipv4ll
*ll
) {
153 assert_return(ll
, -EINVAL
);
155 return sd_ipv4acd_detach_event(ll
->acd
);
158 int sd_ipv4ll_attach_event(sd_ipv4ll
*ll
, sd_event
*event
, int64_t priority
) {
159 assert_return(ll
, -EINVAL
);
161 return sd_ipv4acd_attach_event(ll
->acd
, event
, priority
);
164 int sd_ipv4ll_set_callback(sd_ipv4ll
*ll
, sd_ipv4ll_callback_t cb
, void *userdata
) {
165 assert_return(ll
, -EINVAL
);
168 ll
->userdata
= userdata
;
173 int sd_ipv4ll_get_address(sd_ipv4ll
*ll
, struct in_addr
*address
) {
174 assert_return(ll
, -EINVAL
);
175 assert_return(address
, -EINVAL
);
177 if (ll
->claimed_address
== 0)
180 address
->s_addr
= ll
->claimed_address
;
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
);
189 ll
->seed
.value
= htole64(seed
);
195 int sd_ipv4ll_is_running(sd_ipv4ll
*ll
) {
196 assert_return(ll
, false);
198 return sd_ipv4acd_is_running(ll
->acd
);
201 static bool ipv4ll_address_is_valid(const struct in_addr
*address
) {
204 if (!in_addr_is_link_local(AF_INET
, (const union in_addr_union
*) address
))
207 return !IN_SET(be32toh(address
->s_addr
) & 0x0000FF00U
, 0x0000U
, 0xFF00U
);
210 int sd_ipv4ll_set_address(sd_ipv4ll
*ll
, const struct in_addr
*address
) {
213 assert_return(ll
, -EINVAL
);
214 assert_return(address
, -EINVAL
);
215 assert_return(ipv4ll_address_is_valid(address
), -EINVAL
);
217 r
= sd_ipv4acd_set_address(ll
->acd
, address
);
221 ll
->address
= address
->s_addr
;
226 #define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
228 static int ipv4ll_pick_address(sd_ipv4ll
*ll
) {
229 _cleanup_free_
char *address
= NULL
;
237 h
= siphash24(&ll
->seed
, sizeof(ll
->seed
), PICK_HASH_KEY
.bytes
);
239 /* Increase the generation counter by one */
240 ll
->seed
.generation
= htole64(le64toh(ll
->seed
.generation
) + 1);
242 addr
= htobe32((h
& UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK
);
243 } while (addr
== ll
->address
||
244 IN_SET(be32toh(addr
) & 0x0000FF00U
, 0x0000U
, 0xFF00U
));
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
));
249 return sd_ipv4ll_set_address(ll
, &(struct in_addr
) { addr
});
252 int sd_ipv4ll_restart(sd_ipv4ll
*ll
) {
255 return sd_ipv4ll_start(ll
);
258 #define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
260 int sd_ipv4ll_start(sd_ipv4ll
*ll
) {
262 bool picked_address
= false;
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
);
268 /* If no random seed is set, generate some from the MAC address */
270 ll
->seed
.value
= htole64(siphash24(ll
->mac
.ether_addr_octet
, ETH_ALEN
, MAC_HASH_KEY
.bytes
));
272 /* Restart the generation counter. */
273 ll
->seed
.generation
= 0;
275 if (ll
->address
== 0) {
276 r
= ipv4ll_pick_address(ll
);
280 picked_address
= true;
283 r
= sd_ipv4acd_start(ll
->acd
);
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. */
297 static void ipv4ll_client_notify(sd_ipv4ll
*ll
, int event
) {
301 ll
->callback(ll
, event
, ll
->userdata
);
304 void ipv4ll_on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
) {
305 sd_ipv4ll
*ll
= userdata
;
306 IPV4LL_DONT_DESTROY(ll
);
314 case SD_IPV4ACD_EVENT_STOP
:
315 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);
316 ll
->claimed_address
= 0;
319 case SD_IPV4ACD_EVENT_BIND
:
320 ll
->claimed_address
= ll
->address
;
321 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_BIND
);
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
);
330 ll
->claimed_address
= 0;
332 r
= ipv4ll_pick_address(ll
);
336 r
= sd_ipv4acd_start(ll
->acd
);
344 assert_not_reached("Invalid IPv4ACD event.");
350 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);