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);
292 case IPV4ACD_STATE_WAITING_ANNOUNCE
:
293 /* Send announcement packet */
294 r
= arp_send_announcement(acd
->fd
, acd
->ifindex
, acd
->address
, &acd
->mac_addr
);
296 log_ipv4acd_errno(acd
, r
, "Failed to send ARP announcement: %m");
299 log_ipv4acd(acd
, "ANNOUNCE");
301 ipv4acd_set_state(acd
, IPV4ACD_STATE_ANNOUNCING
, false);
303 r
= ipv4acd_set_next_wakeup(acd
, ANNOUNCE_INTERVAL_USEC
, 0);
307 if (acd
->n_iteration
== 0) {
309 ipv4acd_client_notify(acd
, SD_IPV4ACD_EVENT_BIND
);
315 assert_not_reached("Invalid state.");
321 sd_ipv4acd_stop(acd
);
325 static void ipv4acd_on_conflict(sd_ipv4acd
*acd
) {
326 _cleanup_free_
char *address
= NULL
;
327 union in_addr_union addr
= { .in
.s_addr
= acd
->address
};
333 (void) in_addr_to_string(AF_INET
, &addr
, &address
);
334 log_ipv4acd(acd
, "Conflict on %s (%u)", strna(address
), acd
->n_conflict
);
337 ipv4acd_client_notify(acd
, SD_IPV4ACD_EVENT_CONFLICT
);
340 static int ipv4acd_on_packet(
346 sd_ipv4acd
*acd
= userdata
;
347 struct ether_arp packet
;
355 n
= recv(fd
, &packet
, sizeof(struct ether_arp
), 0);
357 if (IN_SET(errno
, EAGAIN
, EINTR
))
360 log_ipv4acd_errno(acd
, errno
, "Failed to read ARP packet: %m");
363 if ((size_t) n
!= sizeof(struct ether_arp
)) {
364 log_ipv4acd(acd
, "Ignoring too short ARP packet.");
368 switch (acd
->state
) {
370 case IPV4ACD_STATE_ANNOUNCING
:
371 case IPV4ACD_STATE_RUNNING
:
373 if (ipv4acd_arp_conflict(acd
, &packet
)) {
376 assert_se(sd_event_now(acd
->event
, clock_boottime_or_monotonic(), &ts
) >= 0);
379 if (ts
> acd
->defend_window
) {
380 acd
->defend_window
= ts
+ DEFEND_INTERVAL_USEC
;
381 r
= arp_send_announcement(acd
->fd
, acd
->ifindex
, acd
->address
, &acd
->mac_addr
);
383 log_ipv4acd_errno(acd
, r
, "Failed to send ARP announcement: %m");
386 log_ipv4acd(acd
, "DEFEND");
389 ipv4acd_on_conflict(acd
);
393 case IPV4ACD_STATE_WAITING_PROBE
:
394 case IPV4ACD_STATE_PROBING
:
395 case IPV4ACD_STATE_WAITING_ANNOUNCE
:
396 /* BPF ensures this packet indicates a conflict */
397 ipv4acd_on_conflict(acd
);
401 assert_not_reached("Invalid state.");
407 sd_ipv4acd_stop(acd
);
411 int sd_ipv4acd_set_ifindex(sd_ipv4acd
*acd
, int ifindex
) {
412 assert_return(acd
, -EINVAL
);
413 assert_return(ifindex
> 0, -EINVAL
);
414 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
416 acd
->ifindex
= ifindex
;
421 int sd_ipv4acd_set_mac(sd_ipv4acd
*acd
, const struct ether_addr
*addr
) {
422 assert_return(acd
, -EINVAL
);
423 assert_return(addr
, -EINVAL
);
424 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
426 acd
->mac_addr
= *addr
;
431 int sd_ipv4acd_detach_event(sd_ipv4acd
*acd
) {
432 assert_return(acd
, -EINVAL
);
434 acd
->event
= sd_event_unref(acd
->event
);
439 int sd_ipv4acd_attach_event(sd_ipv4acd
*acd
, sd_event
*event
, int64_t priority
) {
442 assert_return(acd
, -EINVAL
);
443 assert_return(!acd
->event
, -EBUSY
);
446 acd
->event
= sd_event_ref(event
);
448 r
= sd_event_default(&acd
->event
);
453 acd
->event_priority
= priority
;
458 int sd_ipv4acd_set_callback(sd_ipv4acd
*acd
, sd_ipv4acd_callback_t cb
, void *userdata
) {
459 assert_return(acd
, -EINVAL
);
462 acd
->userdata
= userdata
;
467 int sd_ipv4acd_set_address(sd_ipv4acd
*acd
, const struct in_addr
*address
) {
468 assert_return(acd
, -EINVAL
);
469 assert_return(address
, -EINVAL
);
470 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
472 acd
->address
= address
->s_addr
;
477 int sd_ipv4acd_is_running(sd_ipv4acd
*acd
) {
478 assert_return(acd
, false);
480 return acd
->state
!= IPV4ACD_STATE_INIT
;
483 int sd_ipv4acd_start(sd_ipv4acd
*acd
) {
486 assert_return(acd
, -EINVAL
);
487 assert_return(acd
->event
, -EINVAL
);
488 assert_return(acd
->ifindex
> 0, -EINVAL
);
489 assert_return(acd
->address
!= 0, -EINVAL
);
490 assert_return(!ether_addr_is_null(&acd
->mac_addr
), -EINVAL
);
491 assert_return(acd
->state
== IPV4ACD_STATE_INIT
, -EBUSY
);
493 r
= arp_network_bind_raw_socket(acd
->ifindex
, acd
->address
, &acd
->mac_addr
);
499 acd
->defend_window
= 0;
502 r
= sd_event_add_io(acd
->event
, &acd
->receive_message_event_source
, acd
->fd
, EPOLLIN
, ipv4acd_on_packet
, acd
);
506 r
= sd_event_source_set_priority(acd
->receive_message_event_source
, acd
->event_priority
);
510 (void) sd_event_source_set_description(acd
->receive_message_event_source
, "ipv4acd-receive-message");
512 r
= ipv4acd_set_next_wakeup(acd
, 0, 0);
516 ipv4acd_set_state(acd
, IPV4ACD_STATE_STARTED
, true);