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 "event-util.h"
32 #include "in-addr-util.h"
34 #include "random-util.h"
36 #include "siphash24.h"
37 #include "sparse-endian.h"
40 #define IPV4LL_NETWORK 0xA9FE0000L
41 #define IPV4LL_NETMASK 0xFFFF0000L
43 #define IPV4LL_DONT_DESTROY(ll) \
44 _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
50 be32_t address
; /* the address pushed to ACD */
51 struct random_data
*random_data
;
52 char *random_data_state
;
55 be32_t claimed_address
;
60 sd_ipv4ll
*sd_ipv4ll_ref(sd_ipv4ll
*ll
) {
64 assert(ll
->n_ref
>= 1);
70 sd_ipv4ll
*sd_ipv4ll_unref(sd_ipv4ll
*ll
) {
74 assert(ll
->n_ref
>= 1);
80 sd_ipv4acd_unref(ll
->acd
);
82 free(ll
->random_data
);
83 free(ll
->random_data_state
);
89 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll
*, sd_ipv4ll_unref
);
90 #define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp)
92 static void ipv4ll_on_acd(sd_ipv4acd
*ll
, int event
, void *userdata
);
94 int sd_ipv4ll_new(sd_ipv4ll
**ret
) {
95 _cleanup_ipv4ll_unref_ sd_ipv4ll
*ll
= NULL
;
98 assert_return(ret
, -EINVAL
);
100 ll
= new0(sd_ipv4ll
, 1);
104 r
= sd_ipv4acd_new(&ll
->acd
);
108 r
= sd_ipv4acd_set_callback(ll
->acd
, ipv4ll_on_acd
, ll
);
120 int sd_ipv4ll_stop(sd_ipv4ll
*ll
) {
123 assert_return(ll
, -EINVAL
);
125 r
= sd_ipv4acd_stop(ll
->acd
);
132 int sd_ipv4ll_set_index(sd_ipv4ll
*ll
, int interface_index
) {
133 assert_return(ll
, -EINVAL
);
135 return sd_ipv4acd_set_index(ll
->acd
, interface_index
);
138 #define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
140 int sd_ipv4ll_set_mac(sd_ipv4ll
*ll
, const struct ether_addr
*addr
) {
143 assert_return(ll
, -EINVAL
);
145 if (!ll
->random_data
) {
148 /* If no random data is set, generate some from the MAC */
149 siphash24(&seed
, &addr
->ether_addr_octet
,
150 ETH_ALEN
, HASH_KEY
.bytes
);
152 assert_cc(sizeof(unsigned) <= 8);
154 r
= sd_ipv4ll_set_address_seed(ll
, (unsigned)seed
);
159 return sd_ipv4acd_set_mac(ll
->acd
, addr
);
162 int sd_ipv4ll_detach_event(sd_ipv4ll
*ll
) {
163 assert_return(ll
, -EINVAL
);
165 return sd_ipv4acd_detach_event(ll
->acd
);
168 int sd_ipv4ll_attach_event(sd_ipv4ll
*ll
, sd_event
*event
, int priority
) {
171 assert_return(ll
, -EINVAL
);
173 r
= sd_ipv4acd_attach_event(ll
->acd
, event
, priority
);
180 int sd_ipv4ll_set_callback(sd_ipv4ll
*ll
, sd_ipv4ll_cb_t cb
, void *userdata
) {
181 assert_return(ll
, -EINVAL
);
184 ll
->userdata
= userdata
;
189 int sd_ipv4ll_get_address(sd_ipv4ll
*ll
, struct in_addr
*address
){
190 assert_return(ll
, -EINVAL
);
191 assert_return(address
, -EINVAL
);
193 if (ll
->claimed_address
== 0)
196 address
->s_addr
= ll
->claimed_address
;
201 int sd_ipv4ll_set_address_seed(sd_ipv4ll
*ll
, unsigned seed
) {
202 _cleanup_free_
struct random_data
*random_data
= NULL
;
203 _cleanup_free_
char *random_data_state
= NULL
;
206 assert_return(ll
, -EINVAL
);
208 random_data
= new0(struct random_data
, 1);
212 random_data_state
= new0(char, 128);
213 if (!random_data_state
)
216 r
= initstate_r(seed
, random_data_state
, 128, random_data
);
220 free(ll
->random_data
);
221 ll
->random_data
= random_data
;
224 free(ll
->random_data_state
);
225 ll
->random_data_state
= random_data_state
;
226 random_data_state
= NULL
;
231 int sd_ipv4ll_is_running(sd_ipv4ll
*ll
) {
232 assert_return(ll
, false);
234 return sd_ipv4acd_is_running(ll
->acd
);
237 static bool ipv4ll_address_is_valid(const struct in_addr
*address
) {
242 if (!in_addr_is_link_local(AF_INET
, (const union in_addr_union
*) address
))
245 addr
= be32toh(address
->s_addr
);
247 if ((addr
& 0x0000FF00) == 0x0000 ||
248 (addr
& 0x0000FF00) == 0xFF00)
254 int sd_ipv4ll_set_address(sd_ipv4ll
*ll
, const struct in_addr
*address
) {
257 assert_return(ll
, -EINVAL
);
258 assert_return(address
, -EINVAL
);
259 assert_return(ipv4ll_address_is_valid(address
), -EINVAL
);
261 r
= sd_ipv4acd_set_address(ll
->acd
, address
);
265 ll
->address
= address
->s_addr
;
270 static int ipv4ll_pick_address(sd_ipv4ll
*ll
) {
271 struct in_addr in_addr
;
277 assert(ll
->random_data
);
280 r
= random_r(ll
->random_data
, &random
);
283 addr
= htonl((random
& 0x0000FFFF) | IPV4LL_NETWORK
);
284 } while (addr
== ll
->address
||
285 (ntohl(addr
) & 0x0000FF00) == 0x0000 ||
286 (ntohl(addr
) & 0x0000FF00) == 0xFF00);
288 in_addr
.s_addr
= addr
;
290 r
= sd_ipv4ll_set_address(ll
, &in_addr
);
297 int sd_ipv4ll_start(sd_ipv4ll
*ll
) {
300 assert_return(ll
, -EINVAL
);
301 assert_return(ll
->random_data
, -EINVAL
);
303 if (ll
->address
== 0) {
304 r
= ipv4ll_pick_address(ll
);
309 r
= sd_ipv4acd_start(ll
->acd
);
316 static void ipv4ll_client_notify(sd_ipv4ll
*ll
, int event
) {
320 ll
->cb(ll
, event
, ll
->userdata
);
323 void ipv4ll_on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
) {
324 sd_ipv4ll
*ll
= userdata
;
325 IPV4LL_DONT_DESTROY(ll
);
332 case SD_IPV4ACD_EVENT_STOP
:
333 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);
335 ll
->claimed_address
= 0;
338 case SD_IPV4ACD_EVENT_BIND
:
339 ll
->claimed_address
= ll
->address
;
340 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_BIND
);
343 case SD_IPV4ACD_EVENT_CONFLICT
:
344 /* if an address was already bound we must call up to the
345 user to handle this, otherwise we just try again */
346 if (ll
->claimed_address
!= 0) {
347 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_CONFLICT
);
349 ll
->claimed_address
= 0;
351 r
= ipv4ll_pick_address(ll
);
355 r
= sd_ipv4acd_start(ll
->acd
);
362 assert_not_reached("Invalid IPv4ACD event.");
368 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);