2 This file is part of systemd.
4 Copyright (C) 2014 Axis Communications AB. All rights reserved.
5 Copyright (C) 2015 Tom Gundersen
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <arpa/inet.h>
27 #include "sd-ipv4acd.h"
28 #include "sd-ipv4ll.h"
30 #include "alloc-util.h"
31 #include "ether-addr-util.h"
32 #include "in-addr-util.h"
34 #include "random-util.h"
35 #include "siphash24.h"
36 #include "sparse-endian.h"
37 #include "string-util.h"
40 #define IPV4LL_NETWORK UINT32_C(0xA9FE0000)
41 #define IPV4LL_NETMASK UINT32_C(0xFFFF0000)
43 #define IPV4LL_DONT_DESTROY(ll) \
44 _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
51 be32_t address
; /* the address pushed to ACD */
52 struct ether_addr mac
;
61 be32_t claimed_address
;
63 sd_ipv4ll_callback_t callback
;
67 #define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__)
68 #define log_ipv4ll(ll, fmt, ...) log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__)
70 static void ipv4ll_on_acd(sd_ipv4acd
*ll
, int event
, void *userdata
);
72 sd_ipv4ll
*sd_ipv4ll_ref(sd_ipv4ll
*ll
) {
76 assert(ll
->n_ref
>= 1);
82 sd_ipv4ll
*sd_ipv4ll_unref(sd_ipv4ll
*ll
) {
86 assert(ll
->n_ref
>= 1);
92 sd_ipv4acd_unref(ll
->acd
);
98 int sd_ipv4ll_new(sd_ipv4ll
**ret
) {
99 _cleanup_(sd_ipv4ll_unrefp
) sd_ipv4ll
*ll
= NULL
;
102 assert_return(ret
, -EINVAL
);
104 ll
= new0(sd_ipv4ll
, 1);
110 r
= sd_ipv4acd_new(&ll
->acd
);
114 r
= sd_ipv4acd_set_callback(ll
->acd
, ipv4ll_on_acd
, ll
);
124 int sd_ipv4ll_stop(sd_ipv4ll
*ll
) {
125 assert_return(ll
, -EINVAL
);
127 return sd_ipv4acd_stop(ll
->acd
);
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
);
135 return sd_ipv4acd_set_ifindex(ll
->acd
, ifindex
);
138 int sd_ipv4ll_set_mac(sd_ipv4ll
*ll
, const struct ether_addr
*addr
) {
141 assert_return(ll
, -EINVAL
);
142 assert_return(addr
, -EINVAL
);
143 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
145 r
= sd_ipv4acd_set_mac(ll
->acd
, addr
);
153 int sd_ipv4ll_detach_event(sd_ipv4ll
*ll
) {
154 assert_return(ll
, -EINVAL
);
156 return sd_ipv4acd_detach_event(ll
->acd
);
159 int sd_ipv4ll_attach_event(sd_ipv4ll
*ll
, sd_event
*event
, int64_t priority
) {
160 assert_return(ll
, -EINVAL
);
162 return sd_ipv4acd_attach_event(ll
->acd
, event
, priority
);
165 int sd_ipv4ll_set_callback(sd_ipv4ll
*ll
, sd_ipv4ll_callback_t cb
, void *userdata
) {
166 assert_return(ll
, -EINVAL
);
169 ll
->userdata
= userdata
;
174 int sd_ipv4ll_get_address(sd_ipv4ll
*ll
, struct in_addr
*address
) {
175 assert_return(ll
, -EINVAL
);
176 assert_return(address
, -EINVAL
);
178 if (ll
->claimed_address
== 0)
181 address
->s_addr
= ll
->claimed_address
;
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
);
190 ll
->seed
.value
= htole64(seed
);
196 int sd_ipv4ll_is_running(sd_ipv4ll
*ll
) {
197 assert_return(ll
, false);
199 return sd_ipv4acd_is_running(ll
->acd
);
202 static bool ipv4ll_address_is_valid(const struct in_addr
*address
) {
205 if (!in_addr_is_link_local(AF_INET
, (const union in_addr_union
*) address
))
208 return !IN_SET(be32toh(address
->s_addr
) & 0x0000FF00U
, 0x0000U
, 0xFF00U
);
211 int sd_ipv4ll_set_address(sd_ipv4ll
*ll
, const struct in_addr
*address
) {
214 assert_return(ll
, -EINVAL
);
215 assert_return(address
, -EINVAL
);
216 assert_return(ipv4ll_address_is_valid(address
), -EINVAL
);
218 r
= sd_ipv4acd_set_address(ll
->acd
, address
);
222 ll
->address
= address
->s_addr
;
227 #define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
229 static int ipv4ll_pick_address(sd_ipv4ll
*ll
) {
230 _cleanup_free_
char *address
= NULL
;
238 h
= siphash24(&ll
->seed
, sizeof(ll
->seed
), PICK_HASH_KEY
.bytes
);
240 /* Increase the generation counter by one */
241 ll
->seed
.generation
= htole64(le64toh(ll
->seed
.generation
) + 1);
243 addr
= htobe32((h
& UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK
);
244 } while (addr
== ll
->address
||
245 IN_SET(be32toh(addr
) & 0x0000FF00U
, 0x0000U
, 0xFF00U
));
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
));
250 return sd_ipv4ll_set_address(ll
, &(struct in_addr
) { addr
});
253 #define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
255 int sd_ipv4ll_start(sd_ipv4ll
*ll
) {
257 bool picked_address
= false;
259 assert_return(ll
, -EINVAL
);
260 assert_return(!ether_addr_is_null(&ll
->mac
), -EINVAL
);
261 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
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 /* Restart the generation counter. */
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
);
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 static void ipv4ll_client_notify(sd_ipv4ll
*ll
, int event
) {
296 ll
->callback(ll
, event
, ll
->userdata
);
299 void ipv4ll_on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
) {
300 sd_ipv4ll
*ll
= userdata
;
301 IPV4LL_DONT_DESTROY(ll
);
309 case SD_IPV4ACD_EVENT_STOP
:
310 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);
311 ll
->claimed_address
= 0;
314 case SD_IPV4ACD_EVENT_BIND
:
315 ll
->claimed_address
= ll
->address
;
316 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_BIND
);
319 case SD_IPV4ACD_EVENT_CONFLICT
:
320 /* if an address was already bound we must call up to the
321 user to handle this, otherwise we just try again */
322 if (ll
->claimed_address
!= 0) {
323 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_CONFLICT
);
325 ll
->claimed_address
= 0;
327 r
= ipv4ll_pick_address(ll
);
331 r
= sd_ipv4acd_start(ll
->acd
);
339 assert_not_reached("Invalid IPv4ACD event.");
345 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);