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/>.
25 #include <arpa/inet.h>
27 #include "event-util.h"
29 #include "random-util.h"
31 #include "siphash24.h"
32 #include "sparse-endian.h"
35 #include "sd-ipv4acd.h"
36 #include "sd-ipv4ll.h"
38 #define IPV4LL_NETWORK 0xA9FE0000L
39 #define IPV4LL_NETMASK 0xFFFF0000L
41 #define IPV4LL_DONT_DESTROY(ll) \
42 _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
48 be32_t address
; /* the address pushed to ACD */
49 struct random_data
*random_data
;
50 char *random_data_state
;
53 be32_t claimed_address
;
58 sd_ipv4ll
*sd_ipv4ll_ref(sd_ipv4ll
*ll
) {
62 assert(ll
->n_ref
>= 1);
68 sd_ipv4ll
*sd_ipv4ll_unref(sd_ipv4ll
*ll
) {
72 assert(ll
->n_ref
>= 1);
78 sd_ipv4acd_unref(ll
->acd
);
80 free(ll
->random_data
);
81 free(ll
->random_data_state
);
87 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll
*, sd_ipv4ll_unref
);
88 #define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp)
90 static void ipv4ll_on_acd(sd_ipv4acd
*ll
, int event
, void *userdata
);
92 int sd_ipv4ll_new(sd_ipv4ll
**ret
) {
93 _cleanup_ipv4ll_unref_ sd_ipv4ll
*ll
= NULL
;
96 assert_return(ret
, -EINVAL
);
98 ll
= new0(sd_ipv4ll
, 1);
102 r
= sd_ipv4acd_new(&ll
->acd
);
106 r
= sd_ipv4acd_set_callback(ll
->acd
, ipv4ll_on_acd
, ll
);
118 int sd_ipv4ll_stop(sd_ipv4ll
*ll
) {
121 assert_return(ll
, -EINVAL
);
123 r
= sd_ipv4acd_stop(ll
->acd
);
130 int sd_ipv4ll_set_index(sd_ipv4ll
*ll
, int interface_index
) {
131 assert_return(ll
, -EINVAL
);
133 return sd_ipv4acd_set_index(ll
->acd
, interface_index
);
136 #define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
138 int sd_ipv4ll_set_mac(sd_ipv4ll
*ll
, const struct ether_addr
*addr
) {
141 assert_return(ll
, -EINVAL
);
143 if (!ll
->random_data
) {
146 /* If no random data is set, generate some from the MAC */
147 siphash24(seed
, &addr
->ether_addr_octet
,
148 ETH_ALEN
, HASH_KEY
.bytes
);
150 assert_cc(sizeof(unsigned) <= 8);
152 r
= sd_ipv4ll_set_address_seed(ll
, *(unsigned*)seed
);
157 return sd_ipv4acd_set_mac(ll
->acd
, addr
);
160 int sd_ipv4ll_detach_event(sd_ipv4ll
*ll
) {
161 assert_return(ll
, -EINVAL
);
163 return sd_ipv4acd_detach_event(ll
->acd
);
166 int sd_ipv4ll_attach_event(sd_ipv4ll
*ll
, sd_event
*event
, int priority
) {
169 assert_return(ll
, -EINVAL
);
171 r
= sd_ipv4acd_attach_event(ll
->acd
, event
, priority
);
178 int sd_ipv4ll_set_callback(sd_ipv4ll
*ll
, sd_ipv4ll_cb_t cb
, void *userdata
) {
179 assert_return(ll
, -EINVAL
);
182 ll
->userdata
= userdata
;
187 int sd_ipv4ll_get_address(sd_ipv4ll
*ll
, struct in_addr
*address
){
188 assert_return(ll
, -EINVAL
);
189 assert_return(address
, -EINVAL
);
191 if (ll
->claimed_address
== 0)
194 address
->s_addr
= ll
->claimed_address
;
199 int sd_ipv4ll_set_address_seed(sd_ipv4ll
*ll
, unsigned seed
) {
200 _cleanup_free_
struct random_data
*random_data
= NULL
;
201 _cleanup_free_
char *random_data_state
= NULL
;
204 assert_return(ll
, -EINVAL
);
206 random_data
= new0(struct random_data
, 1);
210 random_data_state
= new0(char, 128);
211 if (!random_data_state
)
214 r
= initstate_r(seed
, random_data_state
, 128, random_data
);
218 free(ll
->random_data
);
219 ll
->random_data
= random_data
;
222 free(ll
->random_data_state
);
223 ll
->random_data_state
= random_data_state
;
224 random_data_state
= NULL
;
229 bool sd_ipv4ll_is_running(sd_ipv4ll
*ll
) {
230 assert_return(ll
, false);
232 return sd_ipv4acd_is_running(ll
->acd
);
235 static int ipv4ll_pick_address(sd_ipv4ll
*ll
) {
236 struct in_addr in_addr
;
242 assert(ll
->random_data
);
245 r
= random_r(ll
->random_data
, &random
);
248 addr
= htonl((random
& 0x0000FFFF) | IPV4LL_NETWORK
);
249 } while (addr
== ll
->address
||
250 (ntohl(addr
) & IPV4LL_NETMASK
) != IPV4LL_NETWORK
||
251 (ntohl(addr
) & 0x0000FF00) == 0x0000 ||
252 (ntohl(addr
) & 0x0000FF00) == 0xFF00);
254 in_addr
.s_addr
= addr
;
256 r
= sd_ipv4acd_set_address(ll
->acd
, &in_addr
);
265 int sd_ipv4ll_start(sd_ipv4ll
*ll
) {
268 assert_return(ll
, -EINVAL
);
269 assert_return(ll
->random_data
, -EINVAL
);
271 if (ll
->address
== 0) {
272 r
= ipv4ll_pick_address(ll
);
277 r
= sd_ipv4acd_start(ll
->acd
);
284 static void ipv4ll_client_notify(sd_ipv4ll
*ll
, int event
) {
288 ll
->cb(ll
, event
, ll
->userdata
);
291 void ipv4ll_on_acd(sd_ipv4acd
*acd
, int event
, void *userdata
) {
292 sd_ipv4ll
*ll
= userdata
;
293 IPV4LL_DONT_DESTROY(ll
);
300 case SD_IPV4ACD_EVENT_STOP
:
301 ipv4ll_client_notify(ll
, IPV4LL_EVENT_STOP
);
303 ll
->claimed_address
= 0;
306 case SD_IPV4ACD_EVENT_BIND
:
307 ll
->claimed_address
= ll
->address
;
308 ipv4ll_client_notify(ll
, IPV4LL_EVENT_BIND
);
311 case SD_IPV4ACD_EVENT_CONFLICT
:
312 /* if an address was already bound we must call up to the
313 user to handle this, otherwise we just try again */
314 if (ll
->claimed_address
!= 0) {
315 ipv4ll_client_notify(ll
, IPV4LL_EVENT_CONFLICT
);
317 ll
->claimed_address
= 0;
319 r
= ipv4ll_pick_address(ll
);
323 r
= sd_ipv4acd_start(ll
->acd
);
330 assert_not_reached("Invalid IPv4ACD event.");
336 ipv4ll_client_notify(ll
, IPV4LL_EVENT_STOP
);