]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-ipv4acd.c
tree-wide: use -EBADF for fd initialization
[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"
7f77917c 20#include "memory-util.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 66 usec_t defend_window;
9be2ba5e 67 struct in_addr 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;
7f77917c
YW
75 void *userdata;
76 sd_ipv4acd_check_mac_callback_t check_mac_callback;
77 void *check_mac_userdata;
e3dca008
TG
78};
79
a0c2541b 80#define log_ipv4acd_errno(acd, error, fmt, ...) \
00dd6d77 81 log_interface_prefix_full_errno( \
a0c2541b 82 "IPv4ACD: ", \
5977b71f 83 sd_ipv4acd, acd, \
a0c2541b 84 error, fmt, ##__VA_ARGS__)
3f2c0d85 85#define log_ipv4acd(acd, fmt, ...) \
00dd6d77
ZJS
86 log_interface_prefix_full_errno_zerook( \
87 "IPv4ACD: ", \
5977b71f 88 sd_ipv4acd, acd, \
00dd6d77 89 0, fmt, ##__VA_ARGS__)
3aacc173 90
878c035a
YW
91static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = {
92 [IPV4ACD_STATE_INIT] = "init",
93 [IPV4ACD_STATE_STARTED] = "started",
94 [IPV4ACD_STATE_WAITING_PROBE] = "waiting-probe",
95 [IPV4ACD_STATE_PROBING] = "probing",
96 [IPV4ACD_STATE_WAITING_ANNOUNCE] = "waiting-announce",
97 [IPV4ACD_STATE_ANNOUNCING] = "announcing",
98 [IPV4ACD_STATE_RUNNING] = "running",
99};
100
101DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ipv4acd_state, IPv4ACDState);
102
b24ef049
LP
103static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) {
104 assert(acd);
d246e77a
LP
105 assert(st < _IPV4ACD_STATE_MAX);
106
878c035a
YW
107 if (st != acd->state)
108 log_ipv4acd(acd, "%s -> %s", ipv4acd_state_to_string(acd->state), ipv4acd_state_to_string(st));
109
b24ef049
LP
110 if (st == acd->state && !reset_counter)
111 acd->n_iteration++;
d246e77a 112 else {
b24ef049
LP
113 acd->state = st;
114 acd->n_iteration = 0;
d246e77a
LP
115 }
116}
117
b24ef049
LP
118static void ipv4acd_reset(sd_ipv4acd *acd) {
119 assert(acd);
d246e77a 120
32ab66c5 121 (void) event_source_disable(acd->timer_event_source);
eb2f7502 122 acd->receive_message_event_source = sd_event_source_disable_unref(acd->receive_message_event_source);
d246e77a 123
b24ef049 124 acd->fd = safe_close(acd->fd);
d246e77a 125
b24ef049 126 ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true);
d246e77a
LP
127}
128
8301aa0b
YW
129static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) {
130 assert(acd);
e3dca008 131
b24ef049 132 ipv4acd_reset(acd);
eb2f7502 133 sd_event_source_unref(acd->timer_event_source);
b24ef049 134 sd_ipv4acd_detach_event(acd);
61a9fa8f 135 free(acd->ifname);
6b430fdb 136 return mfree(acd);
e3dca008
TG
137}
138
8301aa0b
YW
139DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4acd, sd_ipv4acd, ipv4acd_free);
140
e3dca008 141int sd_ipv4acd_new(sd_ipv4acd **ret) {
b24ef049 142 _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
e3dca008
TG
143
144 assert_return(ret, -EINVAL);
145
4ca5acb3 146 acd = new(sd_ipv4acd, 1);
b24ef049 147 if (!acd)
e3dca008
TG
148 return -ENOMEM;
149
4ca5acb3
YW
150 *acd = (sd_ipv4acd) {
151 .n_ref = 1,
152 .state = IPV4ACD_STATE_INIT,
153 .ifindex = -1,
254d1313 154 .fd = -EBADF,
4ca5acb3 155 };
e3dca008 156
1cc6c93a 157 *ret = TAKE_PTR(acd);
e3dca008
TG
158
159 return 0;
160}
161
b24ef049
LP
162static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) {
163 assert(acd);
e3dca008 164
b24ef049 165 if (!acd->callback)
e095f51d
LP
166 return;
167
b24ef049 168 acd->callback(acd, event, acd->userdata);
e3dca008
TG
169}
170
b24ef049 171int sd_ipv4acd_stop(sd_ipv4acd *acd) {
6a776e3a
YW
172 IPv4ACDState old_state;
173
c8bae363
YW
174 if (!acd)
175 return 0;
e3dca008 176
6a776e3a
YW
177 old_state = acd->state;
178
b24ef049 179 ipv4acd_reset(acd);
e3dca008 180
6a776e3a
YW
181 if (old_state == IPV4ACD_STATE_INIT)
182 return 0;
183
b24ef049 184 log_ipv4acd(acd, "STOPPED");
e3dca008 185
b24ef049 186 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP);
e3dca008
TG
187
188 return 0;
189}
190
191static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
192
b24ef049 193static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) {
e3f4eedb 194 usec_t next_timeout, time_now;
e3dca008 195
b24ef049 196 assert(acd);
e3dca008 197
e3f4eedb 198 next_timeout = usec;
e3dca008 199
e3f4eedb
LP
200 if (random_usec > 0)
201 next_timeout += (usec_t) random_u64() % random_usec;
e3dca008 202
ba4e0427 203 assert_se(sd_event_now(acd->event, CLOCK_BOOTTIME, &time_now) >= 0);
e3dca008 204
32ab66c5 205 return event_reset_time(acd->event, &acd->timer_event_source,
ba4e0427 206 CLOCK_BOOTTIME,
32ab66c5
YW
207 time_now + next_timeout, 0,
208 ipv4acd_on_timeout, acd,
209 acd->event_priority, "ipv4acd-timer", true);
e3dca008
TG
210}
211
e3dca008 212static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
99534007 213 sd_ipv4acd *acd = ASSERT_PTR(userdata);
e3dca008
TG
214 int r = 0;
215
b24ef049 216 switch (acd->state) {
e3dca008 217
c9e458a4 218 case IPV4ACD_STATE_STARTED:
3d817902
YW
219 acd->defend_window = 0;
220
b24ef049 221 ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
e3dca008 222
b24ef049 223 if (acd->n_conflict >= MAX_CONFLICTS) {
3d817902 224 log_ipv4acd(acd, "Max conflicts reached, delaying by %s",
5291f26d 225 FORMAT_TIMESPAN(RATE_LIMIT_INTERVAL_USEC, 0));
b24ef049 226 r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
3d817902 227 } else
b24ef049 228 r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
3d817902
YW
229 if (r < 0)
230 goto fail;
e3dca008
TG
231
232 break;
4dbf7b3a 233
e3dca008
TG
234 case IPV4ACD_STATE_WAITING_PROBE:
235 case IPV4ACD_STATE_PROBING:
236 /* Send a probe */
ecad63f8 237 r = arp_send_probe(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
e3dca008 238 if (r < 0) {
b24ef049 239 log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
ff0c5ebd 240 goto fail;
e3dca008
TG
241 }
242
9be2ba5e
YW
243 log_ipv4acd(acd, "Probing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
244
b24ef049
LP
245 if (acd->n_iteration < PROBE_NUM - 2) {
246 ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
e3dca008 247
b24ef049 248 r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC));
e3dca008 249 if (r < 0)
ff0c5ebd 250 goto fail;
e3dca008 251 } else {
b24ef049 252 ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
e3dca008 253
b24ef049 254 r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0);
e3dca008 255 if (r < 0)
ff0c5ebd 256 goto fail;
e3dca008
TG
257 }
258
259 break;
260
261 case IPV4ACD_STATE_ANNOUNCING:
b24ef049
LP
262 if (acd->n_iteration >= ANNOUNCE_NUM - 1) {
263 ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false);
e3dca008
TG
264 break;
265 }
4dbf7b3a 266
4831981d 267 _fallthrough_;
e3dca008
TG
268 case IPV4ACD_STATE_WAITING_ANNOUNCE:
269 /* Send announcement packet */
ecad63f8 270 r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
e3dca008 271 if (r < 0) {
b24ef049 272 log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
ff0c5ebd 273 goto fail;
265b6d4e
YW
274 }
275
276 log_ipv4acd(acd, "Announcing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
e3dca008 277
b24ef049 278 ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
e3dca008 279
b24ef049 280 r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0);
e3dca008 281 if (r < 0)
ff0c5ebd 282 goto fail;
e3dca008 283
b24ef049
LP
284 if (acd->n_iteration == 0) {
285 acd->n_conflict = 0;
286 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND);
e3dca008
TG
287 }
288
289 break;
4dbf7b3a 290
e3dca008 291 default:
04499a70 292 assert_not_reached();
e3dca008
TG
293 }
294
ff0c5ebd 295 return 0;
e3dca008 296
ff0c5ebd 297fail:
b24ef049 298 sd_ipv4acd_stop(acd);
ff0c5ebd 299 return 0;
e3dca008
TG
300}
301
7f77917c
YW
302static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, const struct ether_arp *arp, bool announced) {
303 assert(acd);
304 assert(arp);
305
306 /* RFC 5227 section 2.1.1.
307 * "the host receives any ARP packet (Request *or* Reply) on the interface where the probe is
308 * being performed, where the packet's 'sender IP address' is the address being probed for,
309 * then the host MUST treat this address as being in use by some other host" */
310 if (memcmp(arp->arp_spa, &acd->address, sizeof(struct in_addr)) == 0)
311 return true;
312
313 if (announced)
314 /* the TPA matched instead of SPA, this is not a conflict */
315 return false;
316
317 /* "any ARP Probe where the packet's 'target IP address' is the address being probed for, and
318 * the packet's 'sender hardware address' is not the hardware address of any of the host's
319 * interfaces, then the host SHOULD similarly treat this as an address conflict" */
320 if (arp->ea_hdr.ar_op != htobe16(ARPOP_REQUEST))
321 return false; /* not ARP Request, ignoring. */
322 if (memeqzero(arp->arp_spa, sizeof(struct in_addr)) == 0)
323 return false; /* not ARP Probe, ignoring. */
324 if (memcmp(arp->arp_tpa, &acd->address, sizeof(struct in_addr)) != 0)
325 return false; /* target IP address does not match, BPF code is broken? */
326
327 if (acd->check_mac_callback &&
328 acd->check_mac_callback(acd, (const struct ether_addr*) arp->arp_sha, acd->check_mac_userdata) > 0)
329 /* sender hardware is one of the host's interfaces, ignoring. */
239adf03 330 return false;
7f77917c
YW
331
332 return true; /* conflict! */
333}
334
b24ef049 335static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
b24ef049 336 assert(acd);
e3dca008 337
b24ef049 338 acd->n_conflict++;
e3dca008 339
9be2ba5e 340 log_ipv4acd(acd, "Conflict on "IPV4_ADDRESS_FMT_STR" (%u)", IPV4_ADDRESS_FMT_VAL(acd->address), acd->n_conflict);
e3dca008 341
b24ef049
LP
342 ipv4acd_reset(acd);
343 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
e3dca008
TG
344}
345
e095f51d
LP
346static int ipv4acd_on_packet(
347 sd_event_source *s,
348 int fd,
349 uint32_t revents,
350 void *userdata) {
351
99534007 352 sd_ipv4acd *acd = ASSERT_PTR(userdata);
e3dca008 353 struct ether_arp packet;
e095f51d 354 ssize_t n;
e3dca008
TG
355 int r;
356
e095f51d 357 assert(s);
e3dca008
TG
358 assert(fd >= 0);
359
e095f51d
LP
360 n = recv(fd, &packet, sizeof(struct ether_arp), 0);
361 if (n < 0) {
ab8a8a4e 362 if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
004845d1
LP
363 return 0;
364
b24ef049 365 log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m");
ff0c5ebd 366 goto fail;
e095f51d
LP
367 }
368 if ((size_t) n != sizeof(struct ether_arp)) {
b24ef049 369 log_ipv4acd(acd, "Ignoring too short ARP packet.");
e095f51d
LP
370 return 0;
371 }
e3dca008 372
b24ef049 373 switch (acd->state) {
e095f51d 374
e3dca008
TG
375 case IPV4ACD_STATE_ANNOUNCING:
376 case IPV4ACD_STATE_RUNNING:
e095f51d 377
7f77917c 378 if (ipv4acd_arp_conflict(acd, &packet, true)) {
e3dca008
TG
379 usec_t ts;
380
ba4e0427 381 assert_se(sd_event_now(acd->event, CLOCK_BOOTTIME, &ts) >= 0);
e3dca008
TG
382
383 /* Defend address */
b24ef049
LP
384 if (ts > acd->defend_window) {
385 acd->defend_window = ts + DEFEND_INTERVAL_USEC;
ecad63f8 386 r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
e3dca008 387 if (r < 0) {
b24ef049 388 log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
ff0c5ebd 389 goto fail;
265b6d4e
YW
390 }
391
392 log_ipv4acd(acd, "Defending "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
e3dca008
TG
393
394 } else
b24ef049 395 ipv4acd_on_conflict(acd);
e3dca008 396 }
e3dca008 397 break;
e095f51d 398
e3dca008
TG
399 case IPV4ACD_STATE_WAITING_PROBE:
400 case IPV4ACD_STATE_PROBING:
401 case IPV4ACD_STATE_WAITING_ANNOUNCE:
7f77917c
YW
402 if (ipv4acd_arp_conflict(acd, &packet, false))
403 ipv4acd_on_conflict(acd);
e3dca008 404 break;
e095f51d 405
e3dca008 406 default:
04499a70 407 assert_not_reached();
e3dca008
TG
408 }
409
ff0c5ebd 410 return 0;
e3dca008 411
ff0c5ebd 412fail:
b24ef049 413 sd_ipv4acd_stop(acd);
ff0c5ebd 414 return 0;
e3dca008
TG
415}
416
b24ef049
LP
417int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) {
418 assert_return(acd, -EINVAL);
2f8e7633 419 assert_return(ifindex > 0, -EINVAL);
b24ef049 420 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca008 421
b24ef049 422 acd->ifindex = ifindex;
e3dca008
TG
423
424 return 0;
425}
426
99b06a2f
YW
427int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd) {
428 if (!acd)
429 return -EINVAL;
430
431 return acd->ifindex;
432}
433
61a9fa8f
YW
434int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *ifname) {
435 assert_return(acd, -EINVAL);
436 assert_return(ifname, -EINVAL);
437
438 if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
439 return -EINVAL;
440
441 return free_and_strdup(&acd->ifname, ifname);
442}
443
5977b71f
YW
444int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret) {
445 int r;
446
447 assert_return(acd, -EINVAL);
99b06a2f 448
5977b71f
YW
449 r = get_ifname(acd->ifindex, &acd->ifname);
450 if (r < 0)
451 return r;
452
453 if (ret)
454 *ret = acd->ifname;
455
456 return 0;
99b06a2f
YW
457}
458
b24ef049 459int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
fcb73459
YW
460 int r;
461
b24ef049 462 assert_return(acd, -EINVAL);
e3dca008 463 assert_return(addr, -EINVAL);
fcb73459 464 assert_return(!ether_addr_is_null(addr), -EINVAL);
e3dca008 465
b24ef049 466 acd->mac_addr = *addr;
e3dca008 467
fcb73459
YW
468 if (!sd_ipv4acd_is_running(acd))
469 return 0;
470
471 assert(acd->fd >= 0);
472 r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
473 if (r < 0) {
474 ipv4acd_reset(acd);
475 return r;
476 }
477
e3dca008
TG
478 return 0;
479}
480
b24ef049
LP
481int sd_ipv4acd_detach_event(sd_ipv4acd *acd) {
482 assert_return(acd, -EINVAL);
e3dca008 483
b24ef049 484 acd->event = sd_event_unref(acd->event);
e3dca008
TG
485
486 return 0;
487}
488
b24ef049 489int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) {
e3dca008
TG
490 int r;
491
b24ef049
LP
492 assert_return(acd, -EINVAL);
493 assert_return(!acd->event, -EBUSY);
e3dca008
TG
494
495 if (event)
b24ef049 496 acd->event = sd_event_ref(event);
e3dca008 497 else {
b24ef049 498 r = sd_event_default(&acd->event);
e3dca008
TG
499 if (r < 0)
500 return r;
501 }
502
b24ef049 503 acd->event_priority = priority;
e3dca008
TG
504
505 return 0;
506}
507
b24ef049
LP
508int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) {
509 assert_return(acd, -EINVAL);
e3dca008 510
b24ef049
LP
511 acd->callback = cb;
512 acd->userdata = userdata;
e3dca008
TG
513
514 return 0;
515}
516
7f77917c
YW
517int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata) {
518 assert_return(acd, -EINVAL);
519
520 acd->check_mac_callback = cb;
521 acd->check_mac_userdata = userdata;
522 return 0;
523}
524
b24ef049 525int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
5c35c13a
YW
526 int r;
527
b24ef049 528 assert_return(acd, -EINVAL);
e3dca008 529 assert_return(address, -EINVAL);
9be2ba5e 530 assert_return(in4_addr_is_set(address), -EINVAL);
5c35c13a
YW
531
532 if (in4_addr_equal(&acd->address, address))
533 return 0;
e3dca008 534
9be2ba5e 535 acd->address = *address;
e3dca008 536
5c35c13a
YW
537 if (!sd_ipv4acd_is_running(acd))
538 return 0;
539
540 assert(acd->fd >= 0);
541 r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
542 if (r < 0)
543 goto fail;
544
545 r = ipv4acd_set_next_wakeup(acd, 0, 0);
546 if (r < 0)
547 goto fail;
548
549 ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
e3dca008 550 return 0;
5c35c13a
YW
551
552fail:
553 ipv4acd_reset(acd);
554 return r;
e3dca008
TG
555}
556
4dd6a3aa
YW
557int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) {
558 assert_return(acd, -EINVAL);
559 assert_return(address, -EINVAL);
560
9be2ba5e 561 *address = acd->address;
4dd6a3aa
YW
562
563 return 0;
564}
565
b24ef049
LP
566int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
567 assert_return(acd, false);
e3dca008 568
b24ef049 569 return acd->state != IPV4ACD_STATE_INIT;
e3dca008
TG
570}
571
e92b60b2 572int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) {
e3dca008
TG
573 int r;
574
b24ef049
LP
575 assert_return(acd, -EINVAL);
576 assert_return(acd->event, -EINVAL);
577 assert_return(acd->ifindex > 0, -EINVAL);
9be2ba5e 578 assert_return(in4_addr_is_set(&acd->address), -EINVAL);
b24ef049
LP
579 assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
580 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
e3dca008 581
ecad63f8 582 r = arp_network_bind_raw_socket(acd->ifindex, &acd->address, &acd->mac_addr);
e3dca008 583 if (r < 0)
d246e77a 584 return r;
e3dca008 585
ee3455cf 586 close_and_replace(acd->fd, r);
e92b60b2
AB
587
588 if (reset_conflicts)
589 acd->n_conflict = 0;
e3dca008 590
b24ef049 591 r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd);
e3dca008 592 if (r < 0)
d246e77a 593 goto fail;
e3dca008 594
b24ef049 595 r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority);
e3dca008 596 if (r < 0)
d246e77a 597 goto fail;
e3dca008 598
b24ef049 599 (void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message");
e3dca008 600
b24ef049 601 r = ipv4acd_set_next_wakeup(acd, 0, 0);
e3dca008 602 if (r < 0)
d246e77a 603 goto fail;
e3dca008 604
b24ef049 605 ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
e3dca008 606 return 0;
d246e77a
LP
607
608fail:
b24ef049 609 ipv4acd_reset(acd);
d246e77a 610 return r;
e3dca008 611}