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
;
156 *ret
= TAKE_PTR(acd
);
161 static void ipv4acd_client_notify(sd_ipv4acd
*acd
, int event
) {
167 acd
->callback(acd
, event
, acd
->userdata
);
170 int sd_ipv4acd_stop(sd_ipv4acd
*acd
) {
171 assert_return(acd
, -EINVAL
);
175 log_ipv4acd(acd
, "STOPPED");
177 ipv4acd_client_notify(acd
, SD_IPV4ACD_EVENT_STOP
);
182 static int ipv4acd_on_timeout(sd_event_source
*s
, uint64_t usec
, void *userdata
);
184 static int ipv4acd_set_next_wakeup(sd_ipv4acd
*acd
, usec_t usec
, usec_t random_usec
) {
185 _cleanup_(sd_event_source_unrefp
) sd_event_source
*timer
= NULL
;
186 usec_t next_timeout
, time_now
;
194 next_timeout
+= (usec_t
) random_u64() % random_usec
;
196 assert_se(sd_event_now(acd
->event
, clock_boottime_or_monotonic(), &time_now
) >= 0);
198 r
= sd_event_add_time(acd
->event
, &timer
, clock_boottime_or_monotonic(), time_now
+ next_timeout
, 0, ipv4acd_on_timeout
, acd
);
202 r
= sd_event_source_set_priority(timer
, acd
->event_priority
);
206 (void) sd_event_source_set_description(timer
, "ipv4acd-timer");
208 sd_event_source_unref(acd
->timer_event_source
);
209 acd
->timer_event_source
= TAKE_PTR(timer
);
214 static bool ipv4acd_arp_conflict(sd_ipv4acd
*acd
, struct ether_arp
*arp
) {
219 if (memcmp(arp
->arp_spa
, &acd
->address
, sizeof(acd
->address
)) == 0)
222 /* the TPA matched instead of the SPA, this is not a conflict */
226 static int ipv4acd_on_timeout(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
227 sd_ipv4acd
*acd
= userdata
;
232 switch (acd
->state
) {
234 case IPV4ACD_STATE_STARTED
:
235 ipv4acd_set_state(acd
, IPV4ACD_STATE_WAITING_PROBE
, true);
237 if (acd
->n_conflict
>= MAX_CONFLICTS
) {
238 char ts
[FORMAT_TIMESPAN_MAX
];
239 log_ipv4acd(acd
, "Max conflicts reached, delaying by %s", format_timespan(ts
, sizeof(ts
), RATE_LIMIT_INTERVAL_USEC
, 0));
241 r
= ipv4acd_set_next_wakeup(acd
, RATE_LIMIT_INTERVAL_USEC
, PROBE_WAIT_USEC
);
245 r
= ipv4acd_set_next_wakeup(acd
, 0, PROBE_WAIT_USEC
);
252 case IPV4ACD_STATE_WAITING_PROBE
:
253 case IPV4ACD_STATE_PROBING
:
255 r
= arp_send_probe(acd
->fd
, acd
->ifindex
, acd
->address
, &acd
->mac_addr
);
257 log_ipv4acd_errno(acd
, r
, "Failed to send ARP probe: %m");
260 _cleanup_free_
char *address
= NULL
;
261 union in_addr_union addr
= { .in
.s_addr
= acd
->address
};
263 (void) in_addr_to_string(AF_INET
, &addr
, &address
);
264 log_ipv4acd(acd
, "Probing %s", strna(address
));
267 if (acd
->n_iteration
< PROBE_NUM
- 2) {
268 ipv4acd_set_state(acd
, IPV4ACD_STATE_PROBING
, false);
270 r
= ipv4acd_set_next_wakeup(acd
, PROBE_MIN_USEC
, (PROBE_MAX_USEC
-PROBE_MIN_USEC
));
274 ipv4acd_set_state(acd
, IPV4ACD_STATE_WAITING_ANNOUNCE
, true);
276 r
= ipv4acd_set_next_wakeup(acd
, ANNOUNCE_WAIT_USEC
, 0);
283 case IPV4ACD_STATE_ANNOUNCING
:
284 if (acd
->n_iteration
>= ANNOUNCE_NUM
- 1) {
285 ipv4acd_set_state(acd
, IPV4ACD_STATE_RUNNING
, false);
290 case IPV4ACD_STATE_WAITING_ANNOUNCE
:
291 /* Send announcement packet */
292 r
= arp_send_announcement(acd
->fd
, acd
->ifindex
, acd
->address
, &acd
->mac_addr
);
294 log_ipv4acd_errno(acd
, r
, "Failed to send ARP announcement: %m");
297 log_ipv4acd(acd
, "ANNOUNCE");
299 ipv4acd_set_state(acd
, IPV4ACD_STATE_ANNOUNCING
, false);
301 r
= ipv4acd_set_next_wakeup(acd
, ANNOUNCE_INTERVAL_USEC
, 0);
305 if (acd
->n_iteration
== 0) {
307 ipv4acd_client_notify(acd
, SD_IPV4ACD_EVENT_BIND
);
313 assert_not_reached("Invalid state.");
319 sd_ipv4acd_stop(acd
);
323 static void ipv4acd_on_conflict(sd_ipv4acd
*acd
) {
324 _cleanup_free_
char *address
= NULL
;
325 union in_addr_union addr
= { .in
.s_addr
= acd
->address
};
331 (void) in_addr_to_string(AF_INET
, &addr
, &address
);
332 log_ipv4acd(acd
, "Conflict on %s (%u)", strna(address
), acd
->n_conflict
);
335 ipv4acd_client_notify(acd
, SD_IPV4ACD_EVENT_CONFLICT
);
338 static int ipv4acd_on_packet(
344 sd_ipv4acd
*acd
= userdata
;
345 struct ether_arp packet
;
353 n
= recv(fd
, &packet
, sizeof(struct ether_arp
), 0);
355 if (IN_SET(errno
, EAGAIN
, EINTR
))
358 log_ipv4acd_errno(acd
, errno
, "Failed to read ARP packet: %m");
361 if ((size_t) n
!= sizeof(struct ether_arp
)) {
362 log_ipv4acd(acd
, "Ignoring too short ARP packet.");
366 switch (acd
->state
) {
368 case IPV4ACD_STATE_ANNOUNCING
:
369 case IPV4ACD_STATE_RUNNING
:
371 if (ipv4acd_arp_conflict(acd
, &packet
)) {
374 assert_se(sd_event_now(acd
->event
, clock_boottime_or_monotonic(), &ts
) >= 0);
377 if (ts
> acd
->defend_window
) {
378 acd
->defend_window
= ts
+ DEFEND_INTERVAL_USEC
;
379 r
= arp_send_announcement(acd
->fd
, acd
->ifindex
, acd
->address
, &acd
->mac_addr
);
381 log_ipv4acd_errno(acd
, r
, "Failed to send ARP announcement: %m");
384 log_ipv4acd(acd
, "DEFEND");
387 ipv4acd_on_conflict(acd
);
391 case IPV4ACD_STATE_WAITING_PROBE
:
392 case IPV4ACD_STATE_PROBING
:
393 case IPV4ACD_STATE_WAITING_ANNOUNCE
:
394 /* BPF ensures this packet indicates a conflict */
395 ipv4acd_on_conflict(acd
);
399 assert_not_reached("Invalid state.");
405 sd_ipv4acd_stop(acd
);
409 int sd_ipv4acd_set_ifindex(sd_ipv4acd
*acd
, int ifindex
) {
410 assert_return(acd
, -EINVAL
);
411 assert_return(ifindex
> 0, -EINVAL
);
412 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
414 acd
->ifindex
= ifindex
;
419 int sd_ipv4acd_set_mac(sd_ipv4acd
*acd
, const struct ether_addr
*addr
) {
420 assert_return(acd
, -EINVAL
);
421 assert_return(addr
, -EINVAL
);
422 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
424 acd
->mac_addr
= *addr
;
429 int sd_ipv4acd_detach_event(sd_ipv4acd
*acd
) {
430 assert_return(acd
, -EINVAL
);
432 acd
->event
= sd_event_unref(acd
->event
);
437 int sd_ipv4acd_attach_event(sd_ipv4acd
*acd
, sd_event
*event
, int64_t priority
) {
440 assert_return(acd
, -EINVAL
);
441 assert_return(!acd
->event
, -EBUSY
);
444 acd
->event
= sd_event_ref(event
);
446 r
= sd_event_default(&acd
->event
);
451 acd
->event_priority
= priority
;
456 int sd_ipv4acd_set_callback(sd_ipv4acd
*acd
, sd_ipv4acd_callback_t cb
, void *userdata
) {
457 assert_return(acd
, -EINVAL
);
460 acd
->userdata
= userdata
;
465 int sd_ipv4acd_set_address(sd_ipv4acd
*acd
, const struct in_addr
*address
) {
466 assert_return(acd
, -EINVAL
);
467 assert_return(address
, -EINVAL
);
468 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
470 acd
->address
= address
->s_addr
;
475 int sd_ipv4acd_is_running(sd_ipv4acd
*acd
) {
476 assert_return(acd
, false);
478 return acd
->state
!= IPV4ACD_STATE_INIT
;
481 int sd_ipv4acd_start(sd_ipv4acd
*acd
) {
484 assert_return(acd
, -EINVAL
);
485 assert_return(acd
->event
, -EINVAL
);
486 assert_return(acd
->ifindex
> 0, -EINVAL
);
487 assert_return(acd
->address
!= 0, -EINVAL
);
488 assert_return(!ether_addr_is_null(&acd
->mac_addr
), -EINVAL
);
489 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
491 r
= arp_network_bind_raw_socket(acd
->ifindex
, acd
->address
, &acd
->mac_addr
);
497 acd
->defend_window
= 0;
500 r
= sd_event_add_io(acd
->event
, &acd
->receive_message_event_source
, acd
->fd
, EPOLLIN
, ipv4acd_on_packet
, acd
);
504 r
= sd_event_source_set_priority(acd
->receive_message_event_source
, acd
->event_priority
);
508 (void) sd_event_source_set_description(acd
->receive_message_event_source
, "ipv4acd-receive-message");
510 r
= ipv4acd_set_next_wakeup(acd
, 0, 0);
514 ipv4acd_set_state(acd
, IPV4ACD_STATE_STARTED
, true);