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);
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
) {
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 seed
= siphash24(&addr
->ether_addr_octet
, ETH_ALEN
, HASH_KEY
.bytes
);
151 assert_cc(sizeof(unsigned) <= 8);
153 r
= sd_ipv4ll_set_address_seed(ll
, (unsigned) htole64(seed
));
158 return sd_ipv4acd_set_mac(ll
->acd
, addr
);
161 int sd_ipv4ll_detach_event(sd_ipv4ll
*ll
) {
162 assert_return(ll
, -EINVAL
);
164 return sd_ipv4acd_detach_event(ll
->acd
);
167 int sd_ipv4ll_attach_event(sd_ipv4ll
*ll
, sd_event
*event
, int priority
) {
170 assert_return(ll
, -EINVAL
);
172 r
= sd_ipv4acd_attach_event(ll
->acd
, event
, priority
);
179 int sd_ipv4ll_set_callback(sd_ipv4ll
*ll
, sd_ipv4ll_cb_t cb
, void *userdata
) {
180 assert_return(ll
, -EINVAL
);
183 ll
->userdata
= userdata
;
188 int sd_ipv4ll_get_address(sd_ipv4ll
*ll
, struct in_addr
*address
){
189 assert_return(ll
, -EINVAL
);
190 assert_return(address
, -EINVAL
);
192 if (ll
->claimed_address
== 0)
195 address
->s_addr
= ll
->claimed_address
;
200 int sd_ipv4ll_set_address_seed(sd_ipv4ll
*ll
, unsigned seed
) {
201 _cleanup_free_
struct random_data
*random_data
= NULL
;
202 _cleanup_free_
char *random_data_state
= NULL
;
205 assert_return(ll
, -EINVAL
);
207 random_data
= new0(struct random_data
, 1);
211 random_data_state
= new0(char, 128);
212 if (!random_data_state
)
215 r
= initstate_r(seed
, random_data_state
, 128, random_data
);
219 free(ll
->random_data
);
220 ll
->random_data
= random_data
;
223 free(ll
->random_data_state
);
224 ll
->random_data_state
= random_data_state
;
225 random_data_state
= NULL
;
230 int sd_ipv4ll_is_running(sd_ipv4ll
*ll
) {
231 assert_return(ll
, false);
233 return sd_ipv4acd_is_running(ll
->acd
);
236 static bool ipv4ll_address_is_valid(const struct in_addr
*address
) {
241 if (!in_addr_is_link_local(AF_INET
, (const union in_addr_union
*) address
))
244 addr
= be32toh(address
->s_addr
);
246 if ((addr
& 0x0000FF00) == 0x0000 ||
247 (addr
& 0x0000FF00) == 0xFF00)
253 int sd_ipv4ll_set_address(sd_ipv4ll
*ll
, const struct in_addr
*address
) {
256 assert_return(ll
, -EINVAL
);
257 assert_return(address
, -EINVAL
);
258 assert_return(ipv4ll_address_is_valid(address
), -EINVAL
);
260 r
= sd_ipv4acd_set_address(ll
->acd
, address
);
264 ll
->address
= address
->s_addr
;
269 static int ipv4ll_pick_address(sd_ipv4ll
*ll
) {
270 struct in_addr in_addr
;
276 assert(ll
->random_data
);
279 r
= random_r(ll
->random_data
, &random
);
282 addr
= htonl((random
& 0x0000FFFF) | IPV4LL_NETWORK
);
283 } while (addr
== ll
->address
||
284 (ntohl(addr
) & 0x0000FF00) == 0x0000 ||
285 (ntohl(addr
) & 0x0000FF00) == 0xFF00);
287 in_addr
.s_addr
= addr
;
289 r
= sd_ipv4ll_set_address(ll
, &in_addr
);
296 int sd_ipv4ll_start(sd_ipv4ll
*ll
) {
299 assert_return(ll
, -EINVAL
);
300 assert_return(ll
->random_data
, -EINVAL
);
302 if (ll
->address
== 0) {
303 r
= ipv4ll_pick_address(ll
);
308 r
= sd_ipv4acd_start(ll
->acd
);
315 static void ipv4ll_client_notify(sd_ipv4ll
*ll
, int event
) {
319 ll
->cb(ll
, event
, ll
->userdata
);
322 void ipv4ll_on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
) {
323 sd_ipv4ll
*ll
= userdata
;
324 IPV4LL_DONT_DESTROY(ll
);
331 case SD_IPV4ACD_EVENT_STOP
:
332 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);
334 ll
->claimed_address
= 0;
337 case SD_IPV4ACD_EVENT_BIND
:
338 ll
->claimed_address
= ll
->address
;
339 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_BIND
);
342 case SD_IPV4ACD_EVENT_CONFLICT
:
343 /* if an address was already bound we must call up to the
344 user to handle this, otherwise we just try again */
345 if (ll
->claimed_address
!= 0) {
346 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_CONFLICT
);
348 ll
->claimed_address
= 0;
350 r
= ipv4ll_pick_address(ll
);
354 r
= sd_ipv4acd_start(ll
->acd
);
361 assert_not_reached("Invalid IPv4ACD event.");
367 ipv4ll_client_notify(ll
, SD_IPV4LL_EVENT_STOP
);