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"
39 #define IPV4LL_NETWORK UINT32_C(0xA9FE0000)
40 #define IPV4LL_NETMASK UINT32_C(0xFFFF0000)
42 #define IPV4LL_DONT_DESTROY(ll) \
43 _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
50 be32_t address
; /* the address pushed to ACD */
51 struct ether_addr mac
;
60 be32_t claimed_address
;
62 sd_ipv4ll_callback_t callback
;
66 static void ipv4ll_on_acd(sd_ipv4acd
*ll
, int event
, void *userdata
);
68 sd_ipv4ll
*sd_ipv4ll_ref(sd_ipv4ll
*ll
) {
72 assert(ll
->n_ref
>= 1);
78 sd_ipv4ll
*sd_ipv4ll_unref(sd_ipv4ll
*ll
) {
82 assert(ll
->n_ref
>= 1);
88 sd_ipv4acd_unref(ll
->acd
);
94 int sd_ipv4ll_new(sd_ipv4ll
**ret
) {
95 _cleanup_(sd_ipv4ll_unrefp
) sd_ipv4ll
*ll
= NULL
;
98 assert_return(ret
, -EINVAL
);
100 ll
= new0(sd_ipv4ll
, 1);
106 r
= sd_ipv4acd_new(&ll
->acd
);
110 r
= sd_ipv4acd_set_callback(ll
->acd
, ipv4ll_on_acd
, ll
);
120 int sd_ipv4ll_stop(sd_ipv4ll
*ll
) {
121 assert_return(ll
, -EINVAL
);
123 return sd_ipv4acd_stop(ll
->acd
);
126 int sd_ipv4ll_set_ifindex(sd_ipv4ll
*ll
, int ifindex
) {
127 assert_return(ll
, -EINVAL
);
128 assert_return(ifindex
> 0, -EINVAL
);
129 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
131 return sd_ipv4acd_set_ifindex(ll
->acd
, ifindex
);
134 int sd_ipv4ll_set_mac(sd_ipv4ll
*ll
, const struct ether_addr
*addr
) {
137 assert_return(ll
, -EINVAL
);
138 assert_return(addr
, -EINVAL
);
139 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
141 r
= sd_ipv4acd_set_mac(ll
->acd
, addr
);
149 int sd_ipv4ll_detach_event(sd_ipv4ll
*ll
) {
150 assert_return(ll
, -EINVAL
);
152 return sd_ipv4acd_detach_event(ll
->acd
);
155 int sd_ipv4ll_attach_event(sd_ipv4ll
*ll
, sd_event
*event
, int64_t priority
) {
156 assert_return(ll
, -EINVAL
);
158 return sd_ipv4acd_attach_event(ll
->acd
, event
, priority
);
161 int sd_ipv4ll_set_callback(sd_ipv4ll
*ll
, sd_ipv4ll_callback_t cb
, void *userdata
) {
162 assert_return(ll
, -EINVAL
);
165 ll
->userdata
= userdata
;
170 int sd_ipv4ll_get_address(sd_ipv4ll
*ll
, struct in_addr
*address
) {
171 assert_return(ll
, -EINVAL
);
172 assert_return(address
, -EINVAL
);
174 if (ll
->claimed_address
== 0)
177 address
->s_addr
= ll
->claimed_address
;
182 int sd_ipv4ll_set_address_seed(sd_ipv4ll
*ll
, uint64_t seed
) {
183 assert_return(ll
, -EINVAL
);
184 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
186 ll
->seed
.value
= htole64(seed
);
192 int sd_ipv4ll_is_running(sd_ipv4ll
*ll
) {
193 assert_return(ll
, false);
195 return sd_ipv4acd_is_running(ll
->acd
);
198 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 addr
= be32toh(address
->s_addr
);
208 if ((addr
& 0x0000FF00) == 0x0000 ||
209 (addr
& 0x0000FF00) == 0xFF00)
215 int sd_ipv4ll_set_address(sd_ipv4ll
*ll
, const struct in_addr
*address
) {
218 assert_return(ll
, -EINVAL
);
219 assert_return(address
, -EINVAL
);
220 assert_return(ipv4ll_address_is_valid(address
), -EINVAL
);
222 r
= sd_ipv4acd_set_address(ll
->acd
, address
);
226 ll
->address
= address
->s_addr
;
231 #define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
233 static int ipv4ll_pick_address(sd_ipv4ll
*ll
) {
241 h
= siphash24(&ll
->seed
, sizeof(ll
->seed
), PICK_HASH_KEY
.bytes
);
243 /* Increase the generation counter by one */
244 ll
->seed
.generation
= htole64(le64toh(ll
->seed
.generation
) + 1);
246 addr
= htobe32((h
& UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK
);
247 } while (addr
== ll
->address
||
248 (be32toh(addr
) & 0x0000FF00) == 0x0000 ||
249 (be32toh(addr
) & 0x0000FF00) == 0xFF00);
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 int sd_ipv4ll_start(sd_ipv4ll
*ll
) {
258 bool picked_address
= false;
260 assert_return(ll
, -EINVAL
);
261 assert_return(!ether_addr_is_null(&ll
->mac
), -EINVAL
);
262 assert_return(sd_ipv4ll_is_running(ll
) == 0, -EBUSY
);
264 /* If no random seed is set, generate some from the MAC address */
266 ll
->seed
.value
= htole64(siphash24(ll
->mac
.ether_addr_octet
, ETH_ALEN
, MAC_HASH_KEY
.bytes
));
268 /* Restart the generation counter. */
269 ll
->seed
.generation
= 0;
271 if (ll
->address
== 0) {
272 r
= ipv4ll_pick_address(ll
);
276 picked_address
= true;
279 r
= sd_ipv4acd_start(ll
->acd
);
282 /* We couldn't start? If so, let's forget the picked address again, the user might make a change and
283 * retry, and we want the new data to take effect when picking an address. */
293 static void ipv4ll_client_notify(sd_ipv4ll
*ll
, int event
) {
297 ll
->callback(ll
, event
, ll
->userdata
);
300 void ipv4ll_on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
) {
301 sd_ipv4ll
*ll
= userdata
;
302 IPV4LL_DONT_DESTROY(ll
);
310 case SD_IPV4ACD_EVENT_STOP
:
311 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);
312 ll
->claimed_address
= 0;
315 case SD_IPV4ACD_EVENT_BIND
:
316 ll
->claimed_address
= ll
->address
;
317 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_BIND
);
320 case SD_IPV4ACD_EVENT_CONFLICT
:
321 /* if an address was already bound we must call up to the
322 user to handle this, otherwise we just try again */
323 if (ll
->claimed_address
!= 0) {
324 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_CONFLICT
);
326 ll
->claimed_address
= 0;
328 r
= ipv4ll_pick_address(ll
);
332 r
= sd_ipv4acd_start(ll
->acd
);
340 assert_not_reached("Invalid IPv4ACD event.");
346 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);