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