]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-ipv4acd.c
Merge pull request #10618 from yuwata/fix-10615
[thirdparty/systemd.git] / src / libsystemd-network / sd-ipv4acd.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
e3dca008 2/***
810adae9 3 Copyright © 2014 Axis Communications AB. All rights reserved.
e3dca008
TG
4***/
5
6#include <arpa/inet.h>
7#include <errno.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
07630cea
LP
12#include "sd-ipv4acd.h"
13
b5efdb8a 14#include "alloc-util.h"
07630cea 15#include "arp-util.h"
e78f9587 16#include "ether-addr-util.h"
3ffd4af2 17#include "fd-util.h"
e3dca008
TG
18#include "in-addr-util.h"
19#include "list.h"
e3dca008
TG
20#include "random-util.h"
21#include "siphash24.h"
d246e77a 22#include "string-util.h"
e3dca008
TG
23#include "util.h"
24
e3dca008 25/* Constants from the RFC */
e3f4eedb
LP
26#define PROBE_WAIT_USEC (1U * USEC_PER_SEC)
27#define PROBE_NUM 3U
28#define PROBE_MIN_USEC (1U * USEC_PER_SEC)
29#define PROBE_MAX_USEC (2U * USEC_PER_SEC)
30#define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC)
31#define ANNOUNCE_NUM 2U
32#define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC)
33#define MAX_CONFLICTS 10U
34#define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC)
35#define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC)
e3dca008 36
e3dca008
TG
37typedef enum IPv4ACDState {
38 IPV4ACD_STATE_INIT,
c9e458a4 39 IPV4ACD_STATE_STARTED,
e3dca008
TG
40 IPV4ACD_STATE_WAITING_PROBE,
41 IPV4ACD_STATE_PROBING,
42 IPV4ACD_STATE_WAITING_ANNOUNCE,
43 IPV4ACD_STATE_ANNOUNCING,
44 IPV4ACD_STATE_RUNNING,
45 _IPV4ACD_STATE_MAX,
46 _IPV4ACD_STATE_INVALID = -1
47} IPv4ACDState;
48
49struct sd_ipv4acd {
c116f526 50 unsigned n_ref;
e3dca008
TG
51
52 IPv4ACDState state;
2f8e7633 53 int ifindex;
e3dca008 54 int fd;
784cdc2d
LP
55
56 unsigned n_iteration;
57 unsigned n_conflict;
58
4dbf7b3a
LP
59 sd_event_source *receive_message_event_source;
60 sd_event_source *timer_event_source;
784cdc2d 61
e3dca008
TG
62 usec_t defend_window;
63 be32_t address;
784cdc2d 64
e3dca008
TG
65 /* External */
66 struct ether_addr mac_addr;
784cdc2d 67
e3dca008
TG
68 sd_event *event;
69 int event_priority;
45aa74c7 70 sd_ipv4acd_callback_t callback;
e3dca008
TG
71 void* userdata;
72};
73
b24ef049
LP
74#define log_ipv4acd_errno(acd, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__)
75#define log_ipv4acd(acd, fmt, ...) log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__)
3aacc173 76
b24ef049
LP
77static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) {
78 assert(acd);
d246e77a
LP
79 assert(st < _IPV4ACD_STATE_MAX);
80
b24ef049
LP
81 if (st == acd->state && !reset_counter)
82 acd->n_iteration++;
d246e77a 83 else {
b24ef049
LP
84 acd->state = st;
85 acd->n_iteration = 0;
d246e77a
LP
86 }
87}
88
b24ef049
LP
89static void ipv4acd_reset(sd_ipv4acd *acd) {
90 assert(acd);
d246e77a 91
b24ef049
LP
92 acd->timer_event_source = sd_event_source_unref(acd->timer_event_source);
93 acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source);
d246e77a 94
b24ef049 95 acd->fd = safe_close(acd->fd);
d246e77a 96
b24ef049 97 ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true);
d246e77a
LP
98}
99
8301aa0b
YW
100static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) {
101 assert(acd);
e3dca008 102
b24ef049
LP
103 ipv4acd_reset(acd);
104 sd_ipv4acd_detach_event(acd);
e3dca008 105
6b430fdb 106 return mfree(acd);
e3dca008
TG
107}
108
8301aa0b
YW
109DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4acd, sd_ipv4acd, ipv4acd_free);
110
e3dca008 111int sd_ipv4acd_new(sd_ipv4acd **ret) {
b24ef049 112 _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
e3dca008
TG
113
114 assert_return(ret, -EINVAL);
115
b24ef049
LP
116 acd = new0(sd_ipv4acd, 1);
117 if (!acd)
e3dca008
TG
118 return -ENOMEM;
119
b24ef049
LP
120 acd->n_ref = 1;
121 acd->state = IPV4ACD_STATE_INIT;
122 acd->ifindex = -1;
123 acd->fd = -1;
e3dca008 124
1cc6c93a 125 *ret = TAKE_PTR(acd);
e3dca008
TG
126
127 return 0;
128}
129
b24ef049
LP
130static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) {
131 assert(acd);
e3dca008 132
b24ef049 133 if (!acd->callback)
e095f51d
LP
134 return;
135
b24ef049 136 acd->callback(acd, event, acd->userdata);
e3dca008
TG
137}
138
b24ef049
LP
139int sd_ipv4acd_stop(sd_ipv4acd *acd) {
140 assert_return(acd, -EINVAL);
e3dca008 141
b24ef049 142 ipv4acd_reset(acd);
e3dca008 143
b24ef049 144 log_ipv4acd(acd, "STOPPED");
e3dca008 145
b24ef049 146 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP);
e3dca008
TG
147
148 return 0;
149}
150
151static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
152
b24ef049 153static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) {
4afd3348 154 _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
e3f4eedb 155 usec_t next_timeout, time_now;
e3dca008
TG
156 int r;
157
b24ef049 158 assert(acd);
e3dca008 159
e3f4eedb 160 next_timeout = usec;
e3dca008 161
e3f4eedb
LP
162 if (random_usec > 0)
163 next_timeout += (usec_t) random_u64() % random_usec;
e3dca008 164
b24ef049 165 assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
e3dca008 166
b24ef049 167 r = sd_event_add_time(acd->event, &timer, clock_boottime_or_monotonic(), time_now + next_timeout, 0, ipv4acd_on_timeout, acd);
e3dca008
TG
168 if (r < 0)
169 return r;
170
b24ef049 171 r = sd_event_source_set_priority(timer, acd->event_priority);
e3dca008
TG
172 if (r < 0)
173 return r;
174
4dbf7b3a 175 (void) sd_event_source_set_description(timer, "ipv4acd-timer");
e3dca008 176
b24ef049 177 sd_event_source_unref(acd->timer_event_source);
1cc6c93a 178 acd->timer_event_source = TAKE_PTR(timer);
e3dca008
TG
179
180 return 0;
181}
182
b24ef049
LP
183static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) {
184 assert(acd);
e3dca008
TG
185 assert(arp);
186
187 /* see the BPF */
b24ef049 188 if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0)
e3dca008
TG
189 return true;
190
191 /* the TPA matched instead of the SPA, this is not a conflict */
192 return false;
193}
194
195static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
b24ef049 196 sd_ipv4acd *acd = userdata;
e3dca008
TG
197 int r = 0;
198
b24ef049 199 assert(acd);
e3dca008 200
b24ef049 201 switch (acd->state) {
e3dca008 202
c9e458a4 203 case IPV4ACD_STATE_STARTED:
b24ef049 204 ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
e3dca008 205
b24ef049 206 if (acd->n_conflict >= MAX_CONFLICTS) {
e3f4eedb 207 char ts[FORMAT_TIMESPAN_MAX];
b24ef049 208 log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0));
a48fc60a 209
b24ef049 210 r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
e3dca008 211 if (r < 0)
ff0c5ebd 212 goto fail;
e3dca008 213 } else {
b24ef049 214 r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
e3dca008 215 if (r < 0)
ff0c5ebd 216 goto fail;
e3dca008
TG
217 }
218
219 break;
4dbf7b3a 220
e3dca008
TG
221 case IPV4ACD_STATE_WAITING_PROBE:
222 case IPV4ACD_STATE_PROBING:
223 /* Send a probe */
b24ef049 224 r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
e3dca008 225 if (r < 0) {
b24ef049 226 log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
ff0c5ebd 227 goto fail;
e3dca008
TG
228 } else {
229 _cleanup_free_ char *address = NULL;
b24ef049 230 union in_addr_union addr = { .in.s_addr = acd->address };
e3dca008 231
d246e77a 232 (void) in_addr_to_string(AF_INET, &addr, &address);
b24ef049 233 log_ipv4acd(acd, "Probing %s", strna(address));
e3dca008
TG
234 }
235
b24ef049
LP
236 if (acd->n_iteration < PROBE_NUM - 2) {
237 ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
e3dca008 238
b24ef049 239 r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC));
e3dca008 240 if (r < 0)
ff0c5ebd 241 goto fail;
e3dca008 242 } else {
b24ef049 243 ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
e3dca008 244
b24ef049 245 r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0);
e3dca008 246 if (r < 0)
ff0c5ebd 247 goto fail;
e3dca008
TG
248 }
249
250 break;
251
252 case IPV4ACD_STATE_ANNOUNCING:
b24ef049
LP
253 if (acd->n_iteration >= ANNOUNCE_NUM - 1) {
254 ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false);
e3dca008
TG
255 break;
256 }
4dbf7b3a 257
4831981d 258 _fallthrough_;
e3dca008
TG
259 case IPV4ACD_STATE_WAITING_ANNOUNCE:
260 /* Send announcement packet */
b24ef049 261 r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
e3dca008 262 if (r < 0) {
b24ef049 263 log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
ff0c5ebd 264 goto fail;
e3dca008 265 } else
b24ef049 266 log_ipv4acd(acd, "ANNOUNCE");
e3dca008 267
b24ef049 268 ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
e3dca008 269
b24ef049 270 r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0);
e3dca008 271 if (r < 0)
ff0c5ebd 272 goto fail;
e3dca008 273
b24ef049
LP
274 if (acd->n_iteration == 0) {
275 acd->n_conflict = 0;
276 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND);
e3dca008
TG
277 }
278
279 break;
4dbf7b3a 280
e3dca008
TG
281 default:
282 assert_not_reached("Invalid state.");
283 }
284
ff0c5ebd 285 return 0;
e3dca008 286
ff0c5ebd 287fail:
b24ef049 288 sd_ipv4acd_stop(acd);
ff0c5ebd 289 return 0;
e3dca008
TG
290}
291
b24ef049 292static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
e3dca008 293 _cleanup_free_ char *address = NULL;
b24ef049 294 union in_addr_union addr = { .in.s_addr = acd->address };
e3dca008 295
b24ef049 296 assert(acd);
e3dca008 297
b24ef049 298 acd->n_conflict++;
e3dca008 299
d246e77a 300 (void) in_addr_to_string(AF_INET, &addr, &address);
b24ef049 301 log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict);
e3dca008 302
b24ef049
LP
303 ipv4acd_reset(acd);
304 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
e3dca008
TG
305}
306
e095f51d
LP
307static int ipv4acd_on_packet(
308 sd_event_source *s,
309 int fd,
310 uint32_t revents,
311 void *userdata) {
312
b24ef049 313 sd_ipv4acd *acd = userdata;
e3dca008 314 struct ether_arp packet;
e095f51d 315 ssize_t n;
e3dca008
TG
316 int r;
317
e095f51d 318 assert(s);
b24ef049 319 assert(acd);
e3dca008
TG
320 assert(fd >= 0);
321
e095f51d
LP
322 n = recv(fd, &packet, sizeof(struct ether_arp), 0);
323 if (n < 0) {
3742095b 324 if (IN_SET(errno, EAGAIN, EINTR))
004845d1
LP
325 return 0;
326
b24ef049 327 log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m");
ff0c5ebd 328 goto fail;
e095f51d
LP
329 }
330 if ((size_t) n != sizeof(struct ether_arp)) {
b24ef049 331 log_ipv4acd(acd, "Ignoring too short ARP packet.");
e095f51d
LP
332 return 0;
333 }
e3dca008 334
b24ef049 335 switch (acd->state) {
e095f51d 336
e3dca008
TG
337 case IPV4ACD_STATE_ANNOUNCING:
338 case IPV4ACD_STATE_RUNNING:
e095f51d 339
b24ef049 340 if (ipv4acd_arp_conflict(acd, &packet)) {
e3dca008
TG
341 usec_t ts;
342
b24ef049 343 assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
e3dca008
TG
344
345 /* Defend address */
b24ef049
LP
346 if (ts > acd->defend_window) {
347 acd->defend_window = ts + DEFEND_INTERVAL_USEC;
348 r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
e3dca008 349 if (r < 0) {
b24ef049 350 log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
ff0c5ebd 351 goto fail;
e3dca008 352 } else
b24ef049 353 log_ipv4acd(acd, "DEFEND");
e3dca008
TG
354
355 } else
b24ef049 356 ipv4acd_on_conflict(acd);
e3dca008 357 }
e3dca008 358 break;
e095f51d 359
e3dca008
TG
360 case IPV4ACD_STATE_WAITING_PROBE:
361 case IPV4ACD_STATE_PROBING:
362 case IPV4ACD_STATE_WAITING_ANNOUNCE:
363 /* BPF ensures this packet indicates a conflict */
b24ef049 364 ipv4acd_on_conflict(acd);
e3dca008 365 break;
e095f51d 366
e3dca008
TG
367 default:
368 assert_not_reached("Invalid state.");
369 }
370
ff0c5ebd 371 return 0;
e3dca008 372
ff0c5ebd 373fail:
b24ef049 374 sd_ipv4acd_stop(acd);
ff0c5ebd 375 return 0;
e3dca008
TG
376}
377
b24ef049
LP
378int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) {
379 assert_return(acd, -EINVAL);
2f8e7633 380 assert_return(ifindex > 0, -EINVAL);
b24ef049 381 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca008 382
b24ef049 383 acd->ifindex = ifindex;
e3dca008
TG
384
385 return 0;
386}
387
b24ef049
LP
388int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
389 assert_return(acd, -EINVAL);
e3dca008 390 assert_return(addr, -EINVAL);
b24ef049 391 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca008 392
b24ef049 393 acd->mac_addr = *addr;
e3dca008
TG
394
395 return 0;
396}
397
b24ef049
LP
398int sd_ipv4acd_detach_event(sd_ipv4acd *acd) {
399 assert_return(acd, -EINVAL);
e3dca008 400
b24ef049 401 acd->event = sd_event_unref(acd->event);
e3dca008
TG
402
403 return 0;
404}
405
b24ef049 406int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) {
e3dca008
TG
407 int r;
408
b24ef049
LP
409 assert_return(acd, -EINVAL);
410 assert_return(!acd->event, -EBUSY);
e3dca008
TG
411
412 if (event)
b24ef049 413 acd->event = sd_event_ref(event);
e3dca008 414 else {
b24ef049 415 r = sd_event_default(&acd->event);
e3dca008
TG
416 if (r < 0)
417 return r;
418 }
419
b24ef049 420 acd->event_priority = priority;
e3dca008
TG
421
422 return 0;
423}
424
b24ef049
LP
425int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) {
426 assert_return(acd, -EINVAL);
e3dca008 427
b24ef049
LP
428 acd->callback = cb;
429 acd->userdata = userdata;
e3dca008
TG
430
431 return 0;
432}
433
b24ef049
LP
434int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
435 assert_return(acd, -EINVAL);
e3dca008 436 assert_return(address, -EINVAL);
b24ef049 437 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca008 438
b24ef049 439 acd->address = address->s_addr;
e3dca008
TG
440
441 return 0;
442}
443
b24ef049
LP
444int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
445 assert_return(acd, false);
e3dca008 446
b24ef049 447 return acd->state != IPV4ACD_STATE_INIT;
e3dca008
TG
448}
449
b24ef049 450int sd_ipv4acd_start(sd_ipv4acd *acd) {
e3dca008
TG
451 int r;
452
b24ef049
LP
453 assert_return(acd, -EINVAL);
454 assert_return(acd->event, -EINVAL);
455 assert_return(acd->ifindex > 0, -EINVAL);
456 assert_return(acd->address != 0, -EINVAL);
457 assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
458 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca008 459
b24ef049 460 r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr);
e3dca008 461 if (r < 0)
d246e77a 462 return r;
e3dca008 463
b24ef049
LP
464 safe_close(acd->fd);
465 acd->fd = r;
466 acd->defend_window = 0;
467 acd->n_conflict = 0;
e3dca008 468
b24ef049 469 r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd);
e3dca008 470 if (r < 0)
d246e77a 471 goto fail;
e3dca008 472
b24ef049 473 r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority);
e3dca008 474 if (r < 0)
d246e77a 475 goto fail;
e3dca008 476
b24ef049 477 (void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message");
e3dca008 478
b24ef049 479 r = ipv4acd_set_next_wakeup(acd, 0, 0);
e3dca008 480 if (r < 0)
d246e77a 481 goto fail;
e3dca008 482
b24ef049 483 ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
e3dca008 484 return 0;
d246e77a
LP
485
486fail:
b24ef049 487 ipv4acd_reset(acd);
d246e77a 488 return r;
e3dca008 489}