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
);
96 int sd_ipv4ll_new(sd_ipv4ll
**ret
) {
97 _cleanup_(sd_ipv4ll_unrefp
) sd_ipv4ll
*ll
= NULL
;
100 assert_return(ret
, -EINVAL
);
102 ll
= new0(sd_ipv4ll
, 1);
108 r
= sd_ipv4acd_new(&ll
->acd
);
112 r
= sd_ipv4acd_set_callback(ll
->acd
, ipv4ll_on_acd
, ll
);
122 int sd_ipv4ll_stop(sd_ipv4ll
*ll
) {
123 assert_return(ll
, -EINVAL
);
125 return sd_ipv4acd_stop(ll
->acd
);
128 int sd_ipv4ll_set_ifindex(sd_ipv4ll
*ll
, int ifindex
) {
129 assert_return(ll
, -EINVAL
);
130 assert_return(ifindex
> 0, -EINVAL
);
131 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
133 return sd_ipv4acd_set_ifindex(ll
->acd
, ifindex
);
136 int sd_ipv4ll_set_mac(sd_ipv4ll
*ll
, const struct ether_addr
*addr
) {
139 assert_return(ll
, -EINVAL
);
140 assert_return(addr
, -EINVAL
);
141 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
143 r
= sd_ipv4acd_set_mac(ll
->acd
, addr
);
151 int sd_ipv4ll_detach_event(sd_ipv4ll
*ll
) {
152 assert_return(ll
, -EINVAL
);
154 return sd_ipv4acd_detach_event(ll
->acd
);
157 int sd_ipv4ll_attach_event(sd_ipv4ll
*ll
, sd_event
*event
, int64_t priority
) {
158 assert_return(ll
, -EINVAL
);
160 return sd_ipv4acd_attach_event(ll
->acd
, event
, priority
);
163 int sd_ipv4ll_set_callback(sd_ipv4ll
*ll
, sd_ipv4ll_callback_t cb
, void *userdata
) {
164 assert_return(ll
, -EINVAL
);
167 ll
->userdata
= userdata
;
172 int sd_ipv4ll_get_address(sd_ipv4ll
*ll
, struct in_addr
*address
) {
173 assert_return(ll
, -EINVAL
);
174 assert_return(address
, -EINVAL
);
176 if (ll
->claimed_address
== 0)
179 address
->s_addr
= ll
->claimed_address
;
184 int sd_ipv4ll_set_address_seed(sd_ipv4ll
*ll
, uint64_t seed
) {
185 assert_return(ll
, -EINVAL
);
186 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
188 ll
->seed
.value
= htole64(seed
);
194 int sd_ipv4ll_is_running(sd_ipv4ll
*ll
) {
195 assert_return(ll
, false);
197 return sd_ipv4acd_is_running(ll
->acd
);
200 static bool ipv4ll_address_is_valid(const struct in_addr
*address
) {
203 if (!in_addr_is_link_local(AF_INET
, (const union in_addr_union
*) address
))
206 return !IN_SET(be32toh(address
->s_addr
) & 0x0000FF00U
, 0x0000U
, 0xFF00U
);
209 int sd_ipv4ll_set_address(sd_ipv4ll
*ll
, const struct in_addr
*address
) {
212 assert_return(ll
, -EINVAL
);
213 assert_return(address
, -EINVAL
);
214 assert_return(ipv4ll_address_is_valid(address
), -EINVAL
);
216 r
= sd_ipv4acd_set_address(ll
->acd
, address
);
220 ll
->address
= address
->s_addr
;
225 #define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
227 static int ipv4ll_pick_address(sd_ipv4ll
*ll
) {
228 _cleanup_free_
char *address
= NULL
;
236 h
= siphash24(&ll
->seed
, sizeof(ll
->seed
), PICK_HASH_KEY
.bytes
);
238 /* Increase the generation counter by one */
239 ll
->seed
.generation
= htole64(le64toh(ll
->seed
.generation
) + 1);
241 addr
= htobe32((h
& UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK
);
242 } while (addr
== ll
->address
||
243 IN_SET(be32toh(addr
) & 0x0000FF00U
, 0x0000U
, 0xFF00U
));
245 (void) in_addr_to_string(AF_INET
, &(union in_addr_union
) { .in
.s_addr
= addr
}, &address
);
246 log_ipv4ll(ll
, "Picked new IP address %s.", strna(address
));
248 return sd_ipv4ll_set_address(ll
, &(struct in_addr
) { addr
});
251 #define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
253 int sd_ipv4ll_start(sd_ipv4ll
*ll
) {
255 bool picked_address
= false;
257 assert_return(ll
, -EINVAL
);
258 assert_return(!ether_addr_is_null(&ll
->mac
), -EINVAL
);
259 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
261 /* If no random seed is set, generate some from the MAC address */
263 ll
->seed
.value
= htole64(siphash24(ll
->mac
.ether_addr_octet
, ETH_ALEN
, MAC_HASH_KEY
.bytes
));
265 /* Restart the generation counter. */
266 ll
->seed
.generation
= 0;
268 if (ll
->address
== 0) {
269 r
= ipv4ll_pick_address(ll
);
273 picked_address
= true;
276 r
= sd_ipv4acd_start(ll
->acd
);
279 /* We couldn't start? If so, let's forget the picked address again, the user might make a change and
280 * retry, and we want the new data to take effect when picking an address. */
290 static void ipv4ll_client_notify(sd_ipv4ll
*ll
, int event
) {
294 ll
->callback(ll
, event
, ll
->userdata
);
297 void ipv4ll_on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
) {
298 sd_ipv4ll
*ll
= userdata
;
299 IPV4LL_DONT_DESTROY(ll
);
307 case SD_IPV4ACD_EVENT_STOP
:
308 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);
309 ll
->claimed_address
= 0;
312 case SD_IPV4ACD_EVENT_BIND
:
313 ll
->claimed_address
= ll
->address
;
314 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_BIND
);
317 case SD_IPV4ACD_EVENT_CONFLICT
:
318 /* if an address was already bound we must call up to the
319 user to handle this, otherwise we just try again */
320 if (ll
->claimed_address
!= 0) {
321 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_CONFLICT
);
323 ll
->claimed_address
= 0;
325 r
= ipv4ll_pick_address(ll
);
329 r
= sd_ipv4acd_start(ll
->acd
);
337 assert_not_reached("Invalid IPv4ACD event.");
343 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);