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 "in-addr-util.h"
33 #include "random-util.h"
35 #include "siphash24.h"
36 #include "sparse-endian.h"
39 #define IPV4LL_NETWORK 0xA9FE0000L
40 #define IPV4LL_NETMASK 0xFFFF0000L
42 #define IPV4LL_DONT_DESTROY(ll) \
43 _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
49 be32_t address
; /* the address pushed to ACD */
50 struct random_data
*random_data
;
51 char *random_data_state
;
54 be32_t claimed_address
;
55 sd_ipv4ll_callback_t cb
;
59 sd_ipv4ll
*sd_ipv4ll_ref(sd_ipv4ll
*ll
) {
63 assert(ll
->n_ref
>= 1);
69 sd_ipv4ll
*sd_ipv4ll_unref(sd_ipv4ll
*ll
) {
73 assert(ll
->n_ref
>= 1);
79 sd_ipv4acd_unref(ll
->acd
);
81 free(ll
->random_data
);
82 free(ll
->random_data_state
);
88 static void ipv4ll_on_acd(sd_ipv4acd
*ll
, int event
, void *userdata
);
90 int sd_ipv4ll_new(sd_ipv4ll
**ret
) {
91 _cleanup_(sd_ipv4ll_unrefp
) sd_ipv4ll
*ll
= NULL
;
94 assert_return(ret
, -EINVAL
);
96 ll
= new0(sd_ipv4ll
, 1);
102 r
= sd_ipv4acd_new(&ll
->acd
);
106 r
= sd_ipv4acd_set_callback(ll
->acd
, ipv4ll_on_acd
, ll
);
116 int sd_ipv4ll_stop(sd_ipv4ll
*ll
) {
119 assert_return(ll
, -EINVAL
);
121 r
= sd_ipv4acd_stop(ll
->acd
);
128 int sd_ipv4ll_set_index(sd_ipv4ll
*ll
, int interface_index
) {
129 assert_return(ll
, -EINVAL
);
131 return sd_ipv4acd_set_index(ll
->acd
, interface_index
);
134 #define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
136 int sd_ipv4ll_set_mac(sd_ipv4ll
*ll
, const struct ether_addr
*addr
) {
139 assert_return(ll
, -EINVAL
);
141 if (!ll
->random_data
) {
144 /* If no random data is set, generate some from the MAC */
145 seed
= siphash24(&addr
->ether_addr_octet
, ETH_ALEN
, HASH_KEY
.bytes
);
147 assert_cc(sizeof(unsigned) <= 8);
149 r
= sd_ipv4ll_set_address_seed(ll
, (unsigned) htole64(seed
));
154 return sd_ipv4acd_set_mac(ll
->acd
, addr
);
157 int sd_ipv4ll_detach_event(sd_ipv4ll
*ll
) {
158 assert_return(ll
, -EINVAL
);
160 return sd_ipv4acd_detach_event(ll
->acd
);
163 int sd_ipv4ll_attach_event(sd_ipv4ll
*ll
, sd_event
*event
, int priority
) {
166 assert_return(ll
, -EINVAL
);
168 r
= sd_ipv4acd_attach_event(ll
->acd
, event
, priority
);
175 int sd_ipv4ll_set_callback(sd_ipv4ll
*ll
, sd_ipv4ll_callback_t cb
, void *userdata
) {
176 assert_return(ll
, -EINVAL
);
179 ll
->userdata
= userdata
;
184 int sd_ipv4ll_get_address(sd_ipv4ll
*ll
, struct in_addr
*address
){
185 assert_return(ll
, -EINVAL
);
186 assert_return(address
, -EINVAL
);
188 if (ll
->claimed_address
== 0)
191 address
->s_addr
= ll
->claimed_address
;
196 int sd_ipv4ll_set_address_seed(sd_ipv4ll
*ll
, unsigned seed
) {
197 _cleanup_free_
struct random_data
*random_data
= NULL
;
198 _cleanup_free_
char *random_data_state
= NULL
;
201 assert_return(ll
, -EINVAL
);
203 random_data
= new0(struct random_data
, 1);
207 random_data_state
= new0(char, 128);
208 if (!random_data_state
)
211 r
= initstate_r(seed
, random_data_state
, 128, random_data
);
215 free(ll
->random_data
);
216 ll
->random_data
= random_data
;
219 free(ll
->random_data_state
);
220 ll
->random_data_state
= random_data_state
;
221 random_data_state
= NULL
;
226 int sd_ipv4ll_is_running(sd_ipv4ll
*ll
) {
227 assert_return(ll
, false);
229 return sd_ipv4acd_is_running(ll
->acd
);
232 static bool ipv4ll_address_is_valid(const struct in_addr
*address
) {
237 if (!in_addr_is_link_local(AF_INET
, (const union in_addr_union
*) address
))
240 addr
= be32toh(address
->s_addr
);
242 if ((addr
& 0x0000FF00) == 0x0000 ||
243 (addr
& 0x0000FF00) == 0xFF00)
249 int sd_ipv4ll_set_address(sd_ipv4ll
*ll
, const struct in_addr
*address
) {
252 assert_return(ll
, -EINVAL
);
253 assert_return(address
, -EINVAL
);
254 assert_return(ipv4ll_address_is_valid(address
), -EINVAL
);
256 r
= sd_ipv4acd_set_address(ll
->acd
, address
);
260 ll
->address
= address
->s_addr
;
265 static int ipv4ll_pick_address(sd_ipv4ll
*ll
) {
266 struct in_addr in_addr
;
272 assert(ll
->random_data
);
275 r
= random_r(ll
->random_data
, &random
);
278 addr
= htonl((random
& 0x0000FFFF) | IPV4LL_NETWORK
);
279 } while (addr
== ll
->address
||
280 (ntohl(addr
) & 0x0000FF00) == 0x0000 ||
281 (ntohl(addr
) & 0x0000FF00) == 0xFF00);
283 in_addr
.s_addr
= addr
;
285 r
= sd_ipv4ll_set_address(ll
, &in_addr
);
292 int sd_ipv4ll_start(sd_ipv4ll
*ll
) {
295 assert_return(ll
, -EINVAL
);
296 assert_return(ll
->random_data
, -EINVAL
);
298 if (ll
->address
== 0) {
299 r
= ipv4ll_pick_address(ll
);
304 r
= sd_ipv4acd_start(ll
->acd
);
311 static void ipv4ll_client_notify(sd_ipv4ll
*ll
, int event
) {
315 ll
->cb(ll
, event
, ll
->userdata
);
318 void ipv4ll_on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
) {
319 sd_ipv4ll
*ll
= userdata
;
320 IPV4LL_DONT_DESTROY(ll
);
327 case SD_IPV4ACD_EVENT_STOP
:
328 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);
330 ll
->claimed_address
= 0;
333 case SD_IPV4ACD_EVENT_BIND
:
334 ll
->claimed_address
= ll
->address
;
335 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_BIND
);
338 case SD_IPV4ACD_EVENT_CONFLICT
:
339 /* if an address was already bound we must call up to the
340 user to handle this, otherwise we just try again */
341 if (ll
->claimed_address
!= 0) {
342 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_CONFLICT
);
344 ll
->claimed_address
= 0;
346 r
= ipv4ll_pick_address(ll
);
350 r
= sd_ipv4acd_start(ll
->acd
);
357 assert_not_reached("Invalid IPv4ACD event.");
363 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);