1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2014 Axis Communications AB. All rights reserved.
6 Copyright (C) 2015 Tom Gundersen
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <arpa/inet.h>
28 #include "sd-ipv4acd.h"
30 #include "alloc-util.h"
32 #include "ether-addr-util.h"
34 #include "in-addr-util.h"
36 #include "random-util.h"
37 #include "siphash24.h"
38 #include "string-util.h"
41 /* Constants from the RFC */
42 #define PROBE_WAIT_USEC (1U * USEC_PER_SEC)
44 #define PROBE_MIN_USEC (1U * USEC_PER_SEC)
45 #define PROBE_MAX_USEC (2U * USEC_PER_SEC)
46 #define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC)
47 #define ANNOUNCE_NUM 2U
48 #define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC)
49 #define MAX_CONFLICTS 10U
50 #define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC)
51 #define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC)
53 typedef enum IPv4ACDState
{
55 IPV4ACD_STATE_STARTED
,
56 IPV4ACD_STATE_WAITING_PROBE
,
57 IPV4ACD_STATE_PROBING
,
58 IPV4ACD_STATE_WAITING_ANNOUNCE
,
59 IPV4ACD_STATE_ANNOUNCING
,
60 IPV4ACD_STATE_RUNNING
,
62 _IPV4ACD_STATE_INVALID
= -1
75 sd_event_source
*receive_message_event_source
;
76 sd_event_source
*timer_event_source
;
82 struct ether_addr mac_addr
;
86 sd_ipv4acd_callback_t callback
;
90 #define log_ipv4acd_errno(acd, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__)
91 #define log_ipv4acd(acd, fmt, ...) log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__)
93 static void ipv4acd_set_state(sd_ipv4acd
*acd
, IPv4ACDState st
, bool reset_counter
) {
95 assert(st
< _IPV4ACD_STATE_MAX
);
97 if (st
== acd
->state
&& !reset_counter
)
101 acd
->n_iteration
= 0;
105 static void ipv4acd_reset(sd_ipv4acd
*acd
) {
108 acd
->timer_event_source
= sd_event_source_unref(acd
->timer_event_source
);
109 acd
->receive_message_event_source
= sd_event_source_unref(acd
->receive_message_event_source
);
111 acd
->fd
= safe_close(acd
->fd
);
113 ipv4acd_set_state(acd
, IPV4ACD_STATE_INIT
, true);
116 sd_ipv4acd
*sd_ipv4acd_ref(sd_ipv4acd
*acd
) {
120 assert_se(acd
->n_ref
>= 1);
126 sd_ipv4acd
*sd_ipv4acd_unref(sd_ipv4acd
*acd
) {
130 assert_se(acd
->n_ref
>= 1);
137 sd_ipv4acd_detach_event(acd
);
142 int sd_ipv4acd_new(sd_ipv4acd
**ret
) {
143 _cleanup_(sd_ipv4acd_unrefp
) sd_ipv4acd
*acd
= NULL
;
145 assert_return(ret
, -EINVAL
);
147 acd
= new0(sd_ipv4acd
, 1);
152 acd
->state
= IPV4ACD_STATE_INIT
;
162 static void ipv4acd_client_notify(sd_ipv4acd
*acd
, int event
) {
168 acd
->callback(acd
, event
, acd
->userdata
);
171 int sd_ipv4acd_stop(sd_ipv4acd
*acd
) {
172 assert_return(acd
, -EINVAL
);
176 log_ipv4acd(acd
, "STOPPED");
178 ipv4acd_client_notify(acd
, SD_IPV4ACD_EVENT_STOP
);
183 static int ipv4acd_on_timeout(sd_event_source
*s
, uint64_t usec
, void *userdata
);
185 static int ipv4acd_set_next_wakeup(sd_ipv4acd
*acd
, usec_t usec
, usec_t random_usec
) {
186 _cleanup_(sd_event_source_unrefp
) sd_event_source
*timer
= NULL
;
187 usec_t next_timeout
, time_now
;
195 next_timeout
+= (usec_t
) random_u64() % random_usec
;
197 assert_se(sd_event_now(acd
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
199 r
= sd_event_add_time(acd
->event
, &timer
, clock_boottime_or_monotonic(), time_now
+ next_timeout
, 0, ipv4acd_on_timeout
, acd
);
203 r
= sd_event_source_set_priority(timer
, acd
->event_priority
);
207 (void) sd_event_source_set_description(timer
, "ipv4acd-timer");
209 sd_event_source_unref(acd
->timer_event_source
);
210 acd
->timer_event_source
= timer
;
216 static bool ipv4acd_arp_conflict(sd_ipv4acd
*acd
, struct ether_arp
*arp
) {
221 if (memcmp(arp
->arp_spa
, &acd
->address
, sizeof(acd
->address
)) == 0)
224 /* the TPA matched instead of the SPA, this is not a conflict */
228 static int ipv4acd_on_timeout(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
229 sd_ipv4acd
*acd
= userdata
;
234 switch (acd
->state
) {
236 case IPV4ACD_STATE_STARTED
:
237 ipv4acd_set_state(acd
, IPV4ACD_STATE_WAITING_PROBE
, true);
239 if (acd
->n_conflict
>= MAX_CONFLICTS
) {
240 char ts
[FORMAT_TIMESPAN_MAX
];
241 log_ipv4acd(acd
, "Max conflicts reached, delaying by %s", format_timespan(ts
, sizeof(ts
), RATE_LIMIT_INTERVAL_USEC
, 0));
243 r
= ipv4acd_set_next_wakeup(acd
, RATE_LIMIT_INTERVAL_USEC
, PROBE_WAIT_USEC
);
247 r
= ipv4acd_set_next_wakeup(acd
, 0, PROBE_WAIT_USEC
);
254 case IPV4ACD_STATE_WAITING_PROBE
:
255 case IPV4ACD_STATE_PROBING
:
257 r
= arp_send_probe(acd
->fd
, acd
->ifindex
, acd
->address
, &acd
->mac_addr
);
259 log_ipv4acd_errno(acd
, r
, "Failed to send ARP probe: %m");
262 _cleanup_free_
char *address
= NULL
;
263 union in_addr_union addr
= { .in
.s_addr
= acd
->address
};
265 (void) in_addr_to_string(AF_INET
, &addr
, &address
);
266 log_ipv4acd(acd
, "Probing %s", strna(address
));
269 if (acd
->n_iteration
< PROBE_NUM
- 2) {
270 ipv4acd_set_state(acd
, IPV4ACD_STATE_PROBING
, false);
272 r
= ipv4acd_set_next_wakeup(acd
, PROBE_MIN_USEC
, (PROBE_MAX_USEC
-PROBE_MIN_USEC
));
276 ipv4acd_set_state(acd
, IPV4ACD_STATE_WAITING_ANNOUNCE
, true);
278 r
= ipv4acd_set_next_wakeup(acd
, ANNOUNCE_WAIT_USEC
, 0);
285 case IPV4ACD_STATE_ANNOUNCING
:
286 if (acd
->n_iteration
>= ANNOUNCE_NUM
- 1) {
287 ipv4acd_set_state(acd
, IPV4ACD_STATE_RUNNING
, false);
293 case IPV4ACD_STATE_WAITING_ANNOUNCE
:
294 /* Send announcement packet */
295 r
= arp_send_announcement(acd
->fd
, acd
->ifindex
, acd
->address
, &acd
->mac_addr
);
297 log_ipv4acd_errno(acd
, r
, "Failed to send ARP announcement: %m");
300 log_ipv4acd(acd
, "ANNOUNCE");
302 ipv4acd_set_state(acd
, IPV4ACD_STATE_ANNOUNCING
, false);
304 r
= ipv4acd_set_next_wakeup(acd
, ANNOUNCE_INTERVAL_USEC
, 0);
308 if (acd
->n_iteration
== 0) {
310 ipv4acd_client_notify(acd
, SD_IPV4ACD_EVENT_BIND
);
316 assert_not_reached("Invalid state.");
322 sd_ipv4acd_stop(acd
);
326 static void ipv4acd_on_conflict(sd_ipv4acd
*acd
) {
327 _cleanup_free_
char *address
= NULL
;
328 union in_addr_union addr
= { .in
.s_addr
= acd
->address
};
334 (void) in_addr_to_string(AF_INET
, &addr
, &address
);
335 log_ipv4acd(acd
, "Conflict on %s (%u)", strna(address
), acd
->n_conflict
);
338 ipv4acd_client_notify(acd
, SD_IPV4ACD_EVENT_CONFLICT
);
341 static int ipv4acd_on_packet(
347 sd_ipv4acd
*acd
= userdata
;
348 struct ether_arp packet
;
356 n
= recv(fd
, &packet
, sizeof(struct ether_arp
), 0);
358 if (IN_SET(errno
, EAGAIN
, EINTR
))
361 log_ipv4acd_errno(acd
, errno
, "Failed to read ARP packet: %m");
364 if ((size_t) n
!= sizeof(struct ether_arp
)) {
365 log_ipv4acd(acd
, "Ignoring too short ARP packet.");
369 switch (acd
->state
) {
371 case IPV4ACD_STATE_ANNOUNCING
:
372 case IPV4ACD_STATE_RUNNING
:
374 if (ipv4acd_arp_conflict(acd
, &packet
)) {
377 assert_se(sd_event_now(acd
->event
, clock_boottime_or_monotonic(), &ts
) >= 0);
380 if (ts
> acd
->defend_window
) {
381 acd
->defend_window
= ts
+ DEFEND_INTERVAL_USEC
;
382 r
= arp_send_announcement(acd
->fd
, acd
->ifindex
, acd
->address
, &acd
->mac_addr
);
384 log_ipv4acd_errno(acd
, r
, "Failed to send ARP announcement: %m");
387 log_ipv4acd(acd
, "DEFEND");
390 ipv4acd_on_conflict(acd
);
394 case IPV4ACD_STATE_WAITING_PROBE
:
395 case IPV4ACD_STATE_PROBING
:
396 case IPV4ACD_STATE_WAITING_ANNOUNCE
:
397 /* BPF ensures this packet indicates a conflict */
398 ipv4acd_on_conflict(acd
);
402 assert_not_reached("Invalid state.");
408 sd_ipv4acd_stop(acd
);
412 int sd_ipv4acd_set_ifindex(sd_ipv4acd
*acd
, int ifindex
) {
413 assert_return(acd
, -EINVAL
);
414 assert_return(ifindex
> 0, -EINVAL
);
415 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
417 acd
->ifindex
= ifindex
;
422 int sd_ipv4acd_set_mac(sd_ipv4acd
*acd
, const struct ether_addr
*addr
) {
423 assert_return(acd
, -EINVAL
);
424 assert_return(addr
, -EINVAL
);
425 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
427 acd
->mac_addr
= *addr
;
432 int sd_ipv4acd_detach_event(sd_ipv4acd
*acd
) {
433 assert_return(acd
, -EINVAL
);
435 acd
->event
= sd_event_unref(acd
->event
);
440 int sd_ipv4acd_attach_event(sd_ipv4acd
*acd
, sd_event
*event
, int64_t priority
) {
443 assert_return(acd
, -EINVAL
);
444 assert_return(!acd
->event
, -EBUSY
);
447 acd
->event
= sd_event_ref(event
);
449 r
= sd_event_default(&acd
->event
);
454 acd
->event_priority
= priority
;
459 int sd_ipv4acd_set_callback(sd_ipv4acd
*acd
, sd_ipv4acd_callback_t cb
, void *userdata
) {
460 assert_return(acd
, -EINVAL
);
463 acd
->userdata
= userdata
;
468 int sd_ipv4acd_set_address(sd_ipv4acd
*acd
, const struct in_addr
*address
) {
469 assert_return(acd
, -EINVAL
);
470 assert_return(address
, -EINVAL
);
471 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
473 acd
->address
= address
->s_addr
;
478 int sd_ipv4acd_is_running(sd_ipv4acd
*acd
) {
479 assert_return(acd
, false);
481 return acd
->state
!= IPV4ACD_STATE_INIT
;
484 int sd_ipv4acd_start(sd_ipv4acd
*acd
) {
487 assert_return(acd
, -EINVAL
);
488 assert_return(acd
->event
, -EINVAL
);
489 assert_return(acd
->ifindex
> 0, -EINVAL
);
490 assert_return(acd
->address
!= 0, -EINVAL
);
491 assert_return(!ether_addr_is_null(&acd
->mac_addr
), -EINVAL
);
492 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
494 r
= arp_network_bind_raw_socket(acd
->ifindex
, acd
->address
, &acd
->mac_addr
);
500 acd
->defend_window
= 0;
503 r
= sd_event_add_io(acd
->event
, &acd
->receive_message_event_source
, acd
->fd
, EPOLLIN
, ipv4acd_on_packet
, acd
);
507 r
= sd_event_source_set_priority(acd
->receive_message_event_source
, acd
->event_priority
);
511 (void) sd_event_source_set_description(acd
->receive_message_event_source
, "ipv4acd-receive-message");
513 r
= ipv4acd_set_next_wakeup(acd
, 0, 0);
517 ipv4acd_set_state(acd
, IPV4ACD_STATE_STARTED
, true);