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