]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-ipv4acd.c
Merge pull request #31648 from neighbourhoodie/review-content
[thirdparty/systemd.git] / src / libsystemd-network / sd-ipv4acd.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2014 Axis Communications AB. All rights reserved.
4 ***/
5
6 #include <arpa/inet.h>
7 #include <errno.h>
8 #include <netinet/if_ether.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11
12 #include "sd-ipv4acd.h"
13
14 #include "alloc-util.h"
15 #include "arp-util.h"
16 #include "ether-addr-util.h"
17 #include "event-util.h"
18 #include "fd-util.h"
19 #include "in-addr-util.h"
20 #include "memory-util.h"
21 #include "network-common.h"
22 #include "random-util.h"
23 #include "siphash24.h"
24 #include "string-table.h"
25 #include "string-util.h"
26 #include "time-util.h"
27
28 /* Constants from the RFC */
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)
39
40 typedef enum IPv4ACDState {
41 IPV4ACD_STATE_INIT,
42 IPV4ACD_STATE_STARTED,
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 = -EINVAL,
50 } IPv4ACDState;
51
52 struct sd_ipv4acd {
53 unsigned n_ref;
54
55 IPv4ACDState state;
56 int ifindex;
57 int fd;
58
59 char *ifname;
60 unsigned n_iteration;
61 unsigned n_conflict;
62
63 sd_event_source *receive_message_event_source;
64 sd_event_source *timer_event_source;
65
66 usec_t defend_window;
67 struct in_addr address;
68
69 /* External */
70 struct ether_addr mac_addr;
71
72 sd_event *event;
73 int event_priority;
74 sd_ipv4acd_callback_t callback;
75 void *userdata;
76 sd_ipv4acd_check_mac_callback_t check_mac_callback;
77 void *check_mac_userdata;
78 };
79
80 #define log_ipv4acd_errno(acd, error, fmt, ...) \
81 log_interface_prefix_full_errno( \
82 "IPv4ACD: ", \
83 sd_ipv4acd, acd, \
84 error, fmt, ##__VA_ARGS__)
85 #define log_ipv4acd(acd, fmt, ...) \
86 log_interface_prefix_full_errno_zerook( \
87 "IPv4ACD: ", \
88 sd_ipv4acd, acd, \
89 0, fmt, ##__VA_ARGS__)
90
91 static 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
101 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ipv4acd_state, IPv4ACDState);
102
103 static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) {
104 assert(acd);
105 assert(st < _IPV4ACD_STATE_MAX);
106
107 if (st != acd->state)
108 log_ipv4acd(acd, "%s -> %s", ipv4acd_state_to_string(acd->state), ipv4acd_state_to_string(st));
109
110 if (st == acd->state && !reset_counter)
111 acd->n_iteration++;
112 else {
113 acd->state = st;
114 acd->n_iteration = 0;
115 }
116 }
117
118 static void ipv4acd_reset(sd_ipv4acd *acd) {
119 assert(acd);
120
121 (void) event_source_disable(acd->timer_event_source);
122 acd->receive_message_event_source = sd_event_source_disable_unref(acd->receive_message_event_source);
123
124 acd->fd = safe_close(acd->fd);
125
126 ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true);
127 }
128
129 static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) {
130 assert(acd);
131
132 ipv4acd_reset(acd);
133 sd_event_source_unref(acd->timer_event_source);
134 sd_ipv4acd_detach_event(acd);
135 free(acd->ifname);
136 return mfree(acd);
137 }
138
139 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4acd, sd_ipv4acd, ipv4acd_free);
140
141 int sd_ipv4acd_new(sd_ipv4acd **ret) {
142 _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
143
144 assert_return(ret, -EINVAL);
145
146 acd = new(sd_ipv4acd, 1);
147 if (!acd)
148 return -ENOMEM;
149
150 *acd = (sd_ipv4acd) {
151 .n_ref = 1,
152 .state = IPV4ACD_STATE_INIT,
153 .ifindex = -1,
154 .fd = -EBADF,
155 };
156
157 *ret = TAKE_PTR(acd);
158
159 return 0;
160 }
161
162 static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) {
163 assert(acd);
164
165 if (!acd->callback)
166 return;
167
168 acd->callback(acd, event, acd->userdata);
169 }
170
171 int sd_ipv4acd_stop(sd_ipv4acd *acd) {
172 IPv4ACDState old_state;
173
174 if (!acd)
175 return 0;
176
177 old_state = acd->state;
178
179 ipv4acd_reset(acd);
180
181 if (old_state == IPV4ACD_STATE_INIT)
182 return 0;
183
184 log_ipv4acd(acd, "STOPPED");
185
186 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP);
187
188 return 0;
189 }
190
191 static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
192
193 static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) {
194 usec_t next_timeout, time_now;
195
196 assert(acd);
197
198 next_timeout = usec;
199
200 if (random_usec > 0)
201 next_timeout += (usec_t) random_u64() % random_usec;
202
203 assert_se(sd_event_now(acd->event, CLOCK_BOOTTIME, &time_now) >= 0);
204
205 return event_reset_time(acd->event, &acd->timer_event_source,
206 CLOCK_BOOTTIME,
207 time_now + next_timeout, 0,
208 ipv4acd_on_timeout, acd,
209 acd->event_priority, "ipv4acd-timer", true);
210 }
211
212 static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
213 sd_ipv4acd *acd = ASSERT_PTR(userdata);
214 int r = 0;
215
216 switch (acd->state) {
217
218 case IPV4ACD_STATE_STARTED:
219 acd->defend_window = 0;
220
221 ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
222
223 if (acd->n_conflict >= MAX_CONFLICTS) {
224 log_ipv4acd(acd, "Max conflicts reached, delaying by %s",
225 FORMAT_TIMESPAN(RATE_LIMIT_INTERVAL_USEC, 0));
226 r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
227 } else
228 r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
229 if (r < 0)
230 goto fail;
231
232 break;
233
234 case IPV4ACD_STATE_WAITING_PROBE:
235 case IPV4ACD_STATE_PROBING:
236 /* Send a probe */
237 r = arp_send_probe(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
238 if (r < 0) {
239 log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
240 goto fail;
241 }
242
243 log_ipv4acd(acd, "Probing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
244
245 if (acd->n_iteration < PROBE_NUM - 2) {
246 ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
247
248 r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC));
249 if (r < 0)
250 goto fail;
251 } else {
252 ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
253
254 r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0);
255 if (r < 0)
256 goto fail;
257 }
258
259 break;
260
261 case IPV4ACD_STATE_ANNOUNCING:
262 if (acd->n_iteration >= ANNOUNCE_NUM - 1) {
263 ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false);
264 break;
265 }
266
267 _fallthrough_;
268 case IPV4ACD_STATE_WAITING_ANNOUNCE:
269 /* Send announcement packet */
270 r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
271 if (r < 0) {
272 log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
273 goto fail;
274 }
275
276 log_ipv4acd(acd, "Announcing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
277
278 ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
279
280 r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0);
281 if (r < 0)
282 goto fail;
283
284 if (acd->n_iteration == 0) {
285 acd->n_conflict = 0;
286 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND);
287 }
288
289 break;
290
291 default:
292 assert_not_reached();
293 }
294
295 return 0;
296
297 fail:
298 sd_ipv4acd_stop(acd);
299 return 0;
300 }
301
302 static 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. */
330 return false;
331
332 return true; /* conflict! */
333 }
334
335 static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
336 assert(acd);
337
338 acd->n_conflict++;
339
340 log_ipv4acd(acd, "Conflict on "IPV4_ADDRESS_FMT_STR" (%u)", IPV4_ADDRESS_FMT_VAL(acd->address), acd->n_conflict);
341
342 ipv4acd_reset(acd);
343 ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
344 }
345
346 static int ipv4acd_on_packet(
347 sd_event_source *s,
348 int fd,
349 uint32_t revents,
350 void *userdata) {
351
352 sd_ipv4acd *acd = ASSERT_PTR(userdata);
353 struct ether_arp packet;
354 ssize_t n;
355 int r;
356
357 assert(s);
358 assert(fd >= 0);
359
360 n = recv(fd, &packet, sizeof(struct ether_arp), 0);
361 if (n < 0) {
362 if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
363 return 0;
364
365 log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m");
366 goto fail;
367 }
368 if ((size_t) n != sizeof(struct ether_arp)) {
369 log_ipv4acd(acd, "Ignoring too short ARP packet.");
370 return 0;
371 }
372
373 switch (acd->state) {
374
375 case IPV4ACD_STATE_ANNOUNCING:
376 case IPV4ACD_STATE_RUNNING:
377
378 if (ipv4acd_arp_conflict(acd, &packet, true)) {
379 usec_t ts;
380
381 assert_se(sd_event_now(acd->event, CLOCK_BOOTTIME, &ts) >= 0);
382
383 /* Defend address */
384 if (ts > acd->defend_window) {
385 acd->defend_window = ts + DEFEND_INTERVAL_USEC;
386 r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
387 if (r < 0) {
388 log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
389 goto fail;
390 }
391
392 log_ipv4acd(acd, "Defending "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
393
394 } else
395 ipv4acd_on_conflict(acd);
396 }
397 break;
398
399 case IPV4ACD_STATE_WAITING_PROBE:
400 case IPV4ACD_STATE_PROBING:
401 case IPV4ACD_STATE_WAITING_ANNOUNCE:
402 if (ipv4acd_arp_conflict(acd, &packet, false))
403 ipv4acd_on_conflict(acd);
404 break;
405
406 default:
407 assert_not_reached();
408 }
409
410 return 0;
411
412 fail:
413 sd_ipv4acd_stop(acd);
414 return 0;
415 }
416
417 int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) {
418 assert_return(acd, -EINVAL);
419 assert_return(ifindex > 0, -EINVAL);
420 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
421
422 acd->ifindex = ifindex;
423
424 return 0;
425 }
426
427 int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd) {
428 if (!acd)
429 return -EINVAL;
430
431 return acd->ifindex;
432 }
433
434 int 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
444 int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret) {
445 int r;
446
447 assert_return(acd, -EINVAL);
448
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;
457 }
458
459 int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
460 int r;
461
462 assert_return(acd, -EINVAL);
463 assert_return(addr, -EINVAL);
464 assert_return(!ether_addr_is_null(addr), -EINVAL);
465
466 acd->mac_addr = *addr;
467
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
478 return 0;
479 }
480
481 int sd_ipv4acd_detach_event(sd_ipv4acd *acd) {
482 assert_return(acd, -EINVAL);
483
484 acd->event = sd_event_unref(acd->event);
485
486 return 0;
487 }
488
489 int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) {
490 int r;
491
492 assert_return(acd, -EINVAL);
493 assert_return(!acd->event, -EBUSY);
494
495 if (event)
496 acd->event = sd_event_ref(event);
497 else {
498 r = sd_event_default(&acd->event);
499 if (r < 0)
500 return r;
501 }
502
503 acd->event_priority = priority;
504
505 return 0;
506 }
507
508 int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) {
509 assert_return(acd, -EINVAL);
510
511 acd->callback = cb;
512 acd->userdata = userdata;
513
514 return 0;
515 }
516
517 int 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
525 int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
526 int r;
527
528 assert_return(acd, -EINVAL);
529 assert_return(address, -EINVAL);
530 assert_return(in4_addr_is_set(address), -EINVAL);
531
532 if (in4_addr_equal(&acd->address, address))
533 return 0;
534
535 acd->address = *address;
536
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);
550 return 0;
551
552 fail:
553 ipv4acd_reset(acd);
554 return r;
555 }
556
557 int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) {
558 assert_return(acd, -EINVAL);
559 assert_return(address, -EINVAL);
560
561 *address = acd->address;
562
563 return 0;
564 }
565
566 int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
567 if (!acd)
568 return false;
569
570 return acd->state != IPV4ACD_STATE_INIT;
571 }
572
573 int sd_ipv4acd_is_bound(sd_ipv4acd *acd) {
574 assert_return(acd, false);
575
576 return IN_SET(acd->state, IPV4ACD_STATE_ANNOUNCING, IPV4ACD_STATE_RUNNING);
577 }
578
579 int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) {
580 int r;
581
582 assert_return(acd, -EINVAL);
583 assert_return(acd->event, -EINVAL);
584 assert_return(acd->ifindex > 0, -EINVAL);
585 assert_return(in4_addr_is_set(&acd->address), -EINVAL);
586 assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
587 assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
588
589 r = sd_event_get_state(acd->event);
590 if (r < 0)
591 return r;
592 if (r == SD_EVENT_FINISHED)
593 return -ESTALE;
594
595 r = arp_network_bind_raw_socket(acd->ifindex, &acd->address, &acd->mac_addr);
596 if (r < 0)
597 return r;
598
599 close_and_replace(acd->fd, r);
600
601 if (reset_conflicts)
602 acd->n_conflict = 0;
603
604 r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd);
605 if (r < 0)
606 goto fail;
607
608 r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority);
609 if (r < 0)
610 goto fail;
611
612 (void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message");
613
614 r = ipv4acd_set_next_wakeup(acd, 0, 0);
615 if (r < 0)
616 goto fail;
617
618 ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
619 return 0;
620
621 fail:
622 ipv4acd_reset(acd);
623 return r;
624 }