]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-ipv4acd.c
Merge pull request #20074 from yuwata/fix-typo
[thirdparty/systemd.git] / src / libsystemd-network / sd-ipv4acd.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
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>
8f815e8b 8#include <netinet/if_ether.h>
e3dca008
TG
9#include <stdio.h>
10#include <stdlib.h>
e3dca008 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 19#include "in-addr-util.h"
3f2c0d85 20#include "log-link.h"
61a9fa8f 21#include "network-common.h"
e3dca008
TG
22#include "random-util.h"
23#include "siphash24.h"
878c035a 24#include "string-table.h"
d246e77a 25#include "string-util.h"
ca78ad1d 26#include "time-util.h"
e3dca008 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,
2d93c20e 49 _IPV4ACD_STATE_INVALID = -EINVAL,
e3dca008
TG
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 58
61a9fa8f 59 char *ifname;
784cdc2d
LP
60 unsigned n_iteration;
61 unsigned n_conflict;
62
4dbf7b3a
LP
63 sd_event_source *receive_message_event_source;
64 sd_event_source *timer_event_source;
784cdc2d 65
e3dca008
TG
66 usec_t defend_window;
67 be32_t address;
784cdc2d 68
e3dca008
TG
69 /* External */
70 struct ether_addr mac_addr;
784cdc2d 71
e3dca008
TG
72 sd_event *event;
73 int event_priority;
45aa74c7 74 sd_ipv4acd_callback_t callback;
e3dca008
TG
75 void* userdata;
76};
77
a0c2541b 78#define log_ipv4acd_errno(acd, error, fmt, ...) \
00dd6d77 79 log_interface_prefix_full_errno( \
a0c2541b
ZJS
80 "IPv4ACD: ", \
81 sd_ipv4acd_get_ifname(acd), \
82 error, fmt, ##__VA_ARGS__)
3f2c0d85 83#define log_ipv4acd(acd, fmt, ...) \
00dd6d77
ZJS
84 log_interface_prefix_full_errno_zerook( \
85 "IPv4ACD: ", \
86 sd_ipv4acd_get_ifname(acd), \
87 0, fmt, ##__VA_ARGS__)
3aacc173 88
878c035a
YW
89static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = {
90 [IPV4ACD_STATE_INIT] = "init",
91 [IPV4ACD_STATE_STARTED] = "started",
92 [IPV4ACD_STATE_WAITING_PROBE] = "waiting-probe",
93 [IPV4ACD_STATE_PROBING] = "probing",
94 [IPV4ACD_STATE_WAITING_ANNOUNCE] = "waiting-announce",
95 [IPV4ACD_STATE_ANNOUNCING] = "announcing",
96 [IPV4ACD_STATE_RUNNING] = "running",
97};
98
99DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ipv4acd_state, IPv4ACDState);
100
b24ef049
LP
101static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) {
102 assert(acd);
d246e77a
LP
103 assert(st < _IPV4ACD_STATE_MAX);
104
878c035a
YW
105 if (st != acd->state)
106 log_ipv4acd(acd, "%s -> %s", ipv4acd_state_to_string(acd->state), ipv4acd_state_to_string(st));
107
b24ef049
LP
108 if (st == acd->state && !reset_counter)
109 acd->n_iteration++;
d246e77a 110 else {
b24ef049
LP
111 acd->state = st;
112 acd->n_iteration = 0;
d246e77a
LP
113 }
114}
115
b24ef049
LP
116static void ipv4acd_reset(sd_ipv4acd *acd) {
117 assert(acd);
d246e77a 118
32ab66c5 119 (void) event_source_disable(acd->timer_event_source);
b24ef049 120 acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source);
d246e77a 121
b24ef049 122 acd->fd = safe_close(acd->fd);
d246e77a 123
b24ef049 124 ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true);
d246e77a
LP
125}
126
8301aa0b
YW
127static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) {
128 assert(acd);
e3dca008 129
32ab66c5
YW
130 acd->timer_event_source = sd_event_source_unref(acd->timer_event_source);
131
b24ef049
LP
132 ipv4acd_reset(acd);
133 sd_ipv4acd_detach_event(acd);
61a9fa8f 134 free(acd->ifname);
6b430fdb 135 return mfree(acd);
e3dca008
TG
136}
137
8301aa0b
YW
138DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4acd, sd_ipv4acd, ipv4acd_free);
139
e3dca008 140int sd_ipv4acd_new(sd_ipv4acd **ret) {
b24ef049 141 _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
e3dca008
TG
142
143 assert_return(ret, -EINVAL);
144
4ca5acb3 145 acd = new(sd_ipv4acd, 1);
b24ef049 146 if (!acd)
e3dca008
TG
147 return -ENOMEM;
148
4ca5acb3
YW
149 *acd = (sd_ipv4acd) {
150 .n_ref = 1,
151 .state = IPV4ACD_STATE_INIT,
152 .ifindex = -1,
153 .fd = -1,
154 };
e3dca008 155
1cc6c93a 156 *ret = TAKE_PTR(acd);
e3dca008
TG
157
158 return 0;
159}
160
b24ef049
LP
161static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) {
162 assert(acd);
e3dca008 163
b24ef049 164 if (!acd->callback)
e095f51d
LP
165 return;
166
b24ef049 167 acd->callback(acd, event, acd->userdata);
e3dca008
TG
168}
169
b24ef049 170int sd_ipv4acd_stop(sd_ipv4acd *acd) {
6a776e3a
YW
171 IPv4ACDState old_state;
172
c8bae363
YW
173 if (!acd)
174 return 0;
e3dca008 175
6a776e3a
YW
176 old_state = acd->state;
177
b24ef049 178 ipv4acd_reset(acd);
e3dca008 179
6a776e3a
YW
180 if (old_state == IPV4ACD_STATE_INIT)
181 return 0;
182
b24ef049 183 log_ipv4acd(acd, "STOPPED");
e3dca008 184
b24ef049 185 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP);
e3dca008
TG
186
187 return 0;
188}
189
190static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
191
b24ef049 192static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) {
e3f4eedb 193 usec_t next_timeout, time_now;
e3dca008 194
b24ef049 195 assert(acd);
e3dca008 196
e3f4eedb 197 next_timeout = usec;
e3dca008 198
e3f4eedb
LP
199 if (random_usec > 0)
200 next_timeout += (usec_t) random_u64() % random_usec;
e3dca008 201
b24ef049 202 assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
e3dca008 203
32ab66c5
YW
204 return event_reset_time(acd->event, &acd->timer_event_source,
205 clock_boottime_or_monotonic(),
206 time_now + next_timeout, 0,
207 ipv4acd_on_timeout, acd,
208 acd->event_priority, "ipv4acd-timer", true);
e3dca008
TG
209}
210
b24ef049
LP
211static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) {
212 assert(acd);
e3dca008
TG
213 assert(arp);
214
215 /* see the BPF */
b24ef049 216 if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0)
e3dca008
TG
217 return true;
218
219 /* the TPA matched instead of the SPA, this is not a conflict */
220 return false;
221}
222
223static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
b24ef049 224 sd_ipv4acd *acd = userdata;
e3dca008
TG
225 int r = 0;
226
b24ef049 227 assert(acd);
e3dca008 228
b24ef049 229 switch (acd->state) {
e3dca008 230
c9e458a4 231 case IPV4ACD_STATE_STARTED:
b24ef049 232 ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
e3dca008 233
b24ef049 234 if (acd->n_conflict >= MAX_CONFLICTS) {
e3f4eedb 235 char ts[FORMAT_TIMESPAN_MAX];
b24ef049 236 log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0));
a48fc60a 237
b24ef049 238 r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
e3dca008 239 if (r < 0)
ff0c5ebd 240 goto fail;
e3dca008 241 } else {
b24ef049 242 r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
e3dca008 243 if (r < 0)
ff0c5ebd 244 goto fail;
e3dca008
TG
245 }
246
247 break;
4dbf7b3a 248
e3dca008
TG
249 case IPV4ACD_STATE_WAITING_PROBE:
250 case IPV4ACD_STATE_PROBING:
251 /* Send a probe */
b24ef049 252 r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
e3dca008 253 if (r < 0) {
b24ef049 254 log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
ff0c5ebd 255 goto fail;
e3dca008
TG
256 } else {
257 _cleanup_free_ char *address = NULL;
b24ef049 258 union in_addr_union addr = { .in.s_addr = acd->address };
e3dca008 259
d246e77a 260 (void) in_addr_to_string(AF_INET, &addr, &address);
b24ef049 261 log_ipv4acd(acd, "Probing %s", strna(address));
e3dca008
TG
262 }
263
b24ef049
LP
264 if (acd->n_iteration < PROBE_NUM - 2) {
265 ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
e3dca008 266
b24ef049 267 r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC));
e3dca008 268 if (r < 0)
ff0c5ebd 269 goto fail;
e3dca008 270 } else {
b24ef049 271 ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
e3dca008 272
b24ef049 273 r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0);
e3dca008 274 if (r < 0)
ff0c5ebd 275 goto fail;
e3dca008
TG
276 }
277
278 break;
279
280 case IPV4ACD_STATE_ANNOUNCING:
b24ef049
LP
281 if (acd->n_iteration >= ANNOUNCE_NUM - 1) {
282 ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false);
e3dca008
TG
283 break;
284 }
4dbf7b3a 285
4831981d 286 _fallthrough_;
e3dca008
TG
287 case IPV4ACD_STATE_WAITING_ANNOUNCE:
288 /* Send announcement packet */
b24ef049 289 r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
e3dca008 290 if (r < 0) {
b24ef049 291 log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
ff0c5ebd 292 goto fail;
e3dca008 293 } else
b24ef049 294 log_ipv4acd(acd, "ANNOUNCE");
e3dca008 295
b24ef049 296 ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
e3dca008 297
b24ef049 298 r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0);
e3dca008 299 if (r < 0)
ff0c5ebd 300 goto fail;
e3dca008 301
b24ef049
LP
302 if (acd->n_iteration == 0) {
303 acd->n_conflict = 0;
304 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND);
e3dca008
TG
305 }
306
307 break;
4dbf7b3a 308
e3dca008
TG
309 default:
310 assert_not_reached("Invalid state.");
311 }
312
ff0c5ebd 313 return 0;
e3dca008 314
ff0c5ebd 315fail:
b24ef049 316 sd_ipv4acd_stop(acd);
ff0c5ebd 317 return 0;
e3dca008
TG
318}
319
b24ef049 320static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
e3dca008 321 _cleanup_free_ char *address = NULL;
b24ef049 322 union in_addr_union addr = { .in.s_addr = acd->address };
e3dca008 323
b24ef049 324 assert(acd);
e3dca008 325
b24ef049 326 acd->n_conflict++;
e3dca008 327
d246e77a 328 (void) in_addr_to_string(AF_INET, &addr, &address);
b24ef049 329 log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict);
e3dca008 330
b24ef049
LP
331 ipv4acd_reset(acd);
332 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
e3dca008
TG
333}
334
e095f51d
LP
335static int ipv4acd_on_packet(
336 sd_event_source *s,
337 int fd,
338 uint32_t revents,
339 void *userdata) {
340
b24ef049 341 sd_ipv4acd *acd = userdata;
e3dca008 342 struct ether_arp packet;
e095f51d 343 ssize_t n;
e3dca008
TG
344 int r;
345
e095f51d 346 assert(s);
b24ef049 347 assert(acd);
e3dca008
TG
348 assert(fd >= 0);
349
e095f51d
LP
350 n = recv(fd, &packet, sizeof(struct ether_arp), 0);
351 if (n < 0) {
3742095b 352 if (IN_SET(errno, EAGAIN, EINTR))
004845d1
LP
353 return 0;
354
b24ef049 355 log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m");
ff0c5ebd 356 goto fail;
e095f51d
LP
357 }
358 if ((size_t) n != sizeof(struct ether_arp)) {
b24ef049 359 log_ipv4acd(acd, "Ignoring too short ARP packet.");
e095f51d
LP
360 return 0;
361 }
e3dca008 362
b24ef049 363 switch (acd->state) {
e095f51d 364
e3dca008
TG
365 case IPV4ACD_STATE_ANNOUNCING:
366 case IPV4ACD_STATE_RUNNING:
e095f51d 367
b24ef049 368 if (ipv4acd_arp_conflict(acd, &packet)) {
e3dca008
TG
369 usec_t ts;
370
b24ef049 371 assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
e3dca008
TG
372
373 /* Defend address */
b24ef049
LP
374 if (ts > acd->defend_window) {
375 acd->defend_window = ts + DEFEND_INTERVAL_USEC;
376 r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
e3dca008 377 if (r < 0) {
b24ef049 378 log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
ff0c5ebd 379 goto fail;
e3dca008 380 } else
b24ef049 381 log_ipv4acd(acd, "DEFEND");
e3dca008
TG
382
383 } else
b24ef049 384 ipv4acd_on_conflict(acd);
e3dca008 385 }
e3dca008 386 break;
e095f51d 387
e3dca008
TG
388 case IPV4ACD_STATE_WAITING_PROBE:
389 case IPV4ACD_STATE_PROBING:
390 case IPV4ACD_STATE_WAITING_ANNOUNCE:
391 /* BPF ensures this packet indicates a conflict */
b24ef049 392 ipv4acd_on_conflict(acd);
e3dca008 393 break;
e095f51d 394
e3dca008
TG
395 default:
396 assert_not_reached("Invalid state.");
397 }
398
ff0c5ebd 399 return 0;
e3dca008 400
ff0c5ebd 401fail:
b24ef049 402 sd_ipv4acd_stop(acd);
ff0c5ebd 403 return 0;
e3dca008
TG
404}
405
b24ef049
LP
406int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) {
407 assert_return(acd, -EINVAL);
2f8e7633 408 assert_return(ifindex > 0, -EINVAL);
b24ef049 409 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca008 410
b24ef049 411 acd->ifindex = ifindex;
e3dca008
TG
412
413 return 0;
414}
415
99b06a2f
YW
416int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd) {
417 if (!acd)
418 return -EINVAL;
419
420 return acd->ifindex;
421}
422
61a9fa8f
YW
423int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *ifname) {
424 assert_return(acd, -EINVAL);
425 assert_return(ifname, -EINVAL);
426
427 if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
428 return -EINVAL;
429
430 return free_and_strdup(&acd->ifname, ifname);
431}
432
99b06a2f
YW
433const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd) {
434 if (!acd)
435 return NULL;
436
61a9fa8f 437 return get_ifname(acd->ifindex, &acd->ifname);
99b06a2f
YW
438}
439
b24ef049
LP
440int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
441 assert_return(acd, -EINVAL);
e3dca008 442 assert_return(addr, -EINVAL);
b24ef049 443 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca008 444
b24ef049 445 acd->mac_addr = *addr;
e3dca008
TG
446
447 return 0;
448}
449
b24ef049
LP
450int sd_ipv4acd_detach_event(sd_ipv4acd *acd) {
451 assert_return(acd, -EINVAL);
e3dca008 452
b24ef049 453 acd->event = sd_event_unref(acd->event);
e3dca008
TG
454
455 return 0;
456}
457
b24ef049 458int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) {
e3dca008
TG
459 int r;
460
b24ef049
LP
461 assert_return(acd, -EINVAL);
462 assert_return(!acd->event, -EBUSY);
e3dca008
TG
463
464 if (event)
b24ef049 465 acd->event = sd_event_ref(event);
e3dca008 466 else {
b24ef049 467 r = sd_event_default(&acd->event);
e3dca008
TG
468 if (r < 0)
469 return r;
470 }
471
b24ef049 472 acd->event_priority = priority;
e3dca008
TG
473
474 return 0;
475}
476
b24ef049
LP
477int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) {
478 assert_return(acd, -EINVAL);
e3dca008 479
b24ef049
LP
480 acd->callback = cb;
481 acd->userdata = userdata;
e3dca008
TG
482
483 return 0;
484}
485
b24ef049
LP
486int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
487 assert_return(acd, -EINVAL);
e3dca008 488 assert_return(address, -EINVAL);
b24ef049 489 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca008 490
b24ef049 491 acd->address = address->s_addr;
e3dca008
TG
492
493 return 0;
494}
495
4dd6a3aa
YW
496int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) {
497 assert_return(acd, -EINVAL);
498 assert_return(address, -EINVAL);
499
500 address->s_addr = acd->address;
501
502 return 0;
503}
504
b24ef049
LP
505int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
506 assert_return(acd, false);
e3dca008 507
b24ef049 508 return acd->state != IPV4ACD_STATE_INIT;
e3dca008
TG
509}
510
e92b60b2 511int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) {
e3dca008
TG
512 int r;
513
b24ef049
LP
514 assert_return(acd, -EINVAL);
515 assert_return(acd->event, -EINVAL);
516 assert_return(acd->ifindex > 0, -EINVAL);
517 assert_return(acd->address != 0, -EINVAL);
518 assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
519 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca008 520
b24ef049 521 r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr);
e3dca008 522 if (r < 0)
d246e77a 523 return r;
e3dca008 524
0706c012 525 CLOSE_AND_REPLACE(acd->fd, r);
b24ef049 526 acd->defend_window = 0;
e92b60b2
AB
527
528 if (reset_conflicts)
529 acd->n_conflict = 0;
e3dca008 530
b24ef049 531 r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd);
e3dca008 532 if (r < 0)
d246e77a 533 goto fail;
e3dca008 534
b24ef049 535 r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority);
e3dca008 536 if (r < 0)
d246e77a 537 goto fail;
e3dca008 538
b24ef049 539 (void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message");
e3dca008 540
b24ef049 541 r = ipv4acd_set_next_wakeup(acd, 0, 0);
e3dca008 542 if (r < 0)
d246e77a 543 goto fail;
e3dca008 544
b24ef049 545 ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
e3dca008 546 return 0;
d246e77a
LP
547
548fail:
b24ef049 549 ipv4acd_reset(acd);
d246e77a 550 return r;
e3dca008 551}