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