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