]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-ipv4acd.c
ipv4acd: add "_event_source" suffix to event source objects
[thirdparty/systemd.git] / src / libsystemd-network / sd-ipv4acd.c
CommitLineData
e3dca008
TG
1/***
2 This file is part of systemd.
3
4 Copyright (C) 2014 Axis Communications AB. All rights reserved.
5 Copyright (C) 2015 Tom Gundersen
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <arpa/inet.h>
22#include <errno.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
07630cea
LP
27#include "sd-ipv4acd.h"
28
b5efdb8a 29#include "alloc-util.h"
07630cea 30#include "arp-util.h"
e78f9587 31#include "ether-addr-util.h"
3ffd4af2 32#include "fd-util.h"
e3dca008
TG
33#include "in-addr-util.h"
34#include "list.h"
e3dca008
TG
35#include "random-util.h"
36#include "siphash24.h"
37#include "util.h"
38
e3dca008
TG
39/* Constants from the RFC */
40#define PROBE_WAIT 1
41#define PROBE_NUM 3
42#define PROBE_MIN 1
43#define PROBE_MAX 2
44#define ANNOUNCE_WAIT 2
45#define ANNOUNCE_NUM 2
46#define ANNOUNCE_INTERVAL 2
47#define MAX_CONFLICTS 10
48#define RATE_LIMIT_INTERVAL 60
49#define DEFEND_INTERVAL 10
50
73e94c0d
LP
51#define IPV4ACD_NETWORK 0xA9FE0000UL
52#define IPV4ACD_NETMASK 0xFFFF0000UL
e3dca008 53
a48fc60a
LP
54#define log_ipv4acd_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__)
55#define log_ipv4acd(ll, fmt, ...) log_ipv4acd_errno(ll, 0, fmt, ##__VA_ARGS__)
e3dca008
TG
56
57typedef enum IPv4ACDState {
58 IPV4ACD_STATE_INIT,
59 IPV4ACD_STATE_WAITING_PROBE,
60 IPV4ACD_STATE_PROBING,
61 IPV4ACD_STATE_WAITING_ANNOUNCE,
62 IPV4ACD_STATE_ANNOUNCING,
63 IPV4ACD_STATE_RUNNING,
64 _IPV4ACD_STATE_MAX,
65 _IPV4ACD_STATE_INVALID = -1
66} IPv4ACDState;
67
68struct sd_ipv4acd {
c116f526 69 unsigned n_ref;
e3dca008
TG
70
71 IPv4ACDState state;
2f8e7633 72 int ifindex;
e3dca008 73 int fd;
784cdc2d
LP
74
75 unsigned n_iteration;
76 unsigned n_conflict;
77
4dbf7b3a
LP
78 sd_event_source *receive_message_event_source;
79 sd_event_source *timer_event_source;
784cdc2d 80
e3dca008
TG
81 usec_t defend_window;
82 be32_t address;
784cdc2d 83
e3dca008
TG
84 /* External */
85 struct ether_addr mac_addr;
784cdc2d 86
e3dca008
TG
87 sd_event *event;
88 int event_priority;
45aa74c7 89 sd_ipv4acd_callback_t callback;
e3dca008
TG
90 void* userdata;
91};
92
93sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) {
c116f526
LP
94 if (!ll)
95 return NULL;
96
97 assert_se(ll->n_ref >= 1);
98 ll->n_ref++;
e3dca008
TG
99
100 return ll;
101}
102
103sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
c116f526
LP
104 if (!ll)
105 return NULL;
106
107 assert_se(ll->n_ref >= 1);
108 ll->n_ref--;
109
110 if (ll->n_ref > 0)
e3dca008
TG
111 return NULL;
112
4dbf7b3a
LP
113 ll->timer_event_source = sd_event_source_unref(ll->timer_event_source);
114 ll->receive_message_event_source = sd_event_source_unref(ll->receive_message_event_source);
e3dca008 115
4dbf7b3a 116 ll->fd = safe_close(ll->fd);
e3dca008
TG
117
118 sd_ipv4acd_detach_event(ll);
119
120 free(ll);
121
122 return NULL;
123}
124
e3dca008 125int sd_ipv4acd_new(sd_ipv4acd **ret) {
4afd3348 126 _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *ll = NULL;
e3dca008
TG
127
128 assert_return(ret, -EINVAL);
129
130 ll = new0(sd_ipv4acd, 1);
131 if (!ll)
132 return -ENOMEM;
133
c116f526 134 ll->n_ref = 1;
e3dca008 135 ll->state = IPV4ACD_STATE_INIT;
2f8e7633 136 ll->ifindex = -1;
e3dca008
TG
137 ll->fd = -1;
138
139 *ret = ll;
140 ll = NULL;
141
142 return 0;
143}
144
145static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
e3dca008
TG
146 assert(ll);
147 assert(st < _IPV4ACD_STATE_MAX);
148
149 if (st == ll->state && !reset_counter)
784cdc2d 150 ll->n_iteration++;
e3dca008
TG
151 else {
152 ll->state = st;
784cdc2d 153 ll->n_iteration = 0;
e3dca008
TG
154 }
155}
156
157static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
158 assert(ll);
159
45aa74c7 160 if (!ll->callback)
e095f51d
LP
161 return;
162
45aa74c7 163 ll->callback(ll, event, ll->userdata);
e3dca008
TG
164}
165
166static void ipv4acd_stop(sd_ipv4acd *ll) {
167 assert(ll);
168
4dbf7b3a 169 ll->timer_event_source = sd_event_source_unref(ll->timer_event_source);
e3dca008 170
4dbf7b3a
LP
171 ll->receive_message_event_source = sd_event_source_unref(ll->receive_message_event_source);
172 ll->fd = safe_close(ll->fd);
e3dca008 173
a48fc60a 174 log_ipv4acd(ll, "STOPPED");
e3dca008 175
4dbf7b3a 176 ipv4acd_set_state(ll, IPV4ACD_STATE_INIT, true);
e3dca008
TG
177}
178
179int sd_ipv4acd_stop(sd_ipv4acd *ll) {
180 assert_return(ll, -EINVAL);
181
182 ipv4acd_stop(ll);
2237aa02 183 ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_STOP);
e3dca008
TG
184
185 return 0;
186}
187
188static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
189
190static int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, int sec, int random_sec) {
4afd3348 191 _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
e3dca008
TG
192 usec_t next_timeout;
193 usec_t time_now;
194 int r;
195
196 assert(sec >= 0);
197 assert(random_sec >= 0);
198 assert(ll);
199
200 next_timeout = sec * USEC_PER_SEC;
201
202 if (random_sec)
203 next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
204
205 assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
206
207 r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(),
208 time_now + next_timeout, 0, ipv4acd_on_timeout, ll);
209 if (r < 0)
210 return r;
211
212 r = sd_event_source_set_priority(timer, ll->event_priority);
213 if (r < 0)
214 return r;
215
4dbf7b3a 216 (void) sd_event_source_set_description(timer, "ipv4acd-timer");
e3dca008 217
4dbf7b3a
LP
218 sd_event_source_unref(ll->timer_event_source);
219 ll->timer_event_source = timer;
e3dca008
TG
220 timer = NULL;
221
222 return 0;
223}
224
225static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
226 assert(ll);
227 assert(arp);
228
229 /* see the BPF */
230 if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
231 return true;
232
233 /* the TPA matched instead of the SPA, this is not a conflict */
234 return false;
235}
236
237static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
238 sd_ipv4acd *ll = userdata;
239 int r = 0;
240
241 assert(ll);
242
243 switch (ll->state) {
e3dca008 244
4dbf7b3a 245 case IPV4ACD_STATE_INIT:
e3dca008
TG
246 ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true);
247
784cdc2d 248 if (ll->n_conflict >= MAX_CONFLICTS) {
a48fc60a
LP
249 log_ipv4acd(ll, "Max conflicts reached, delaying by %us", RATE_LIMIT_INTERVAL);
250
e3dca008
TG
251 r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
252 if (r < 0)
253 goto out;
254
784cdc2d 255 ll->n_conflict = 0;
e3dca008
TG
256 } else {
257 r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT);
258 if (r < 0)
259 goto out;
260 }
261
262 break;
4dbf7b3a 263
e3dca008
TG
264 case IPV4ACD_STATE_WAITING_PROBE:
265 case IPV4ACD_STATE_PROBING:
266 /* Send a probe */
2f8e7633 267 r = arp_send_probe(ll->fd, ll->ifindex, ll->address, &ll->mac_addr);
e3dca008 268 if (r < 0) {
a48fc60a 269 log_ipv4acd_errno(ll, r, "Failed to send ARP probe: %m");
e3dca008
TG
270 goto out;
271 } else {
272 _cleanup_free_ char *address = NULL;
273 union in_addr_union addr = { .in.s_addr = ll->address };
274
275 r = in_addr_to_string(AF_INET, &addr, &address);
276 if (r >= 0)
a48fc60a 277 log_ipv4acd(ll, "Probing %s", address);
e3dca008
TG
278 }
279
784cdc2d 280 if (ll->n_iteration < PROBE_NUM - 2) {
e3dca008
TG
281 ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
282
283 r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
284 if (r < 0)
285 goto out;
286 } else {
287 ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
288
289 r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
290 if (r < 0)
291 goto out;
292 }
293
294 break;
295
296 case IPV4ACD_STATE_ANNOUNCING:
784cdc2d 297 if (ll->n_iteration >= ANNOUNCE_NUM - 1) {
e3dca008 298 ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false);
e3dca008
TG
299 break;
300 }
4dbf7b3a
LP
301
302 /* fall through */
303
e3dca008
TG
304 case IPV4ACD_STATE_WAITING_ANNOUNCE:
305 /* Send announcement packet */
2f8e7633 306 r = arp_send_announcement(ll->fd, ll->ifindex, ll->address, &ll->mac_addr);
e3dca008 307 if (r < 0) {
a48fc60a 308 log_ipv4acd_errno(ll, r, "Failed to send ARP announcement: %m");
e3dca008
TG
309 goto out;
310 } else
a48fc60a 311 log_ipv4acd(ll, "ANNOUNCE");
e3dca008
TG
312
313 ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
314
315 r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
316 if (r < 0)
317 goto out;
318
784cdc2d
LP
319 if (ll->n_iteration == 0) {
320 ll->n_conflict = 0;
2237aa02 321 ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_BIND);
e3dca008
TG
322 }
323
324 break;
4dbf7b3a 325
e3dca008
TG
326 default:
327 assert_not_reached("Invalid state.");
328 }
329
330out:
331 if (r < 0)
332 sd_ipv4acd_stop(ll);
333
334 return 1;
335}
336
337static void ipv4acd_on_conflict(sd_ipv4acd *ll) {
338 _cleanup_free_ char *address = NULL;
339 union in_addr_union addr = { .in.s_addr = ll->address };
340 int r;
341
342 assert(ll);
343
784cdc2d 344 ll->n_conflict++;
e3dca008
TG
345
346 r = in_addr_to_string(AF_INET, &addr, &address);
347 if (r >= 0)
784cdc2d 348 log_ipv4acd(ll, "Conflict on %s (%u)", address, ll->n_conflict);
e3dca008
TG
349
350 ipv4acd_stop(ll);
351
2237aa02 352 ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_CONFLICT);
e3dca008
TG
353}
354
e095f51d
LP
355static int ipv4acd_on_packet(
356 sd_event_source *s,
357 int fd,
358 uint32_t revents,
359 void *userdata) {
360
e3dca008
TG
361 sd_ipv4acd *ll = userdata;
362 struct ether_arp packet;
e095f51d 363 ssize_t n;
e3dca008
TG
364 int r;
365
e095f51d 366 assert(s);
e3dca008
TG
367 assert(ll);
368 assert(fd >= 0);
369
e095f51d
LP
370 n = recv(fd, &packet, sizeof(struct ether_arp), 0);
371 if (n < 0) {
004845d1
LP
372 if (errno == EAGAIN || errno == EINTR)
373 return 0;
374
a48fc60a 375 r = log_ipv4acd_errno(ll, errno, "Failed to read ARP packet: %m");
e3dca008 376 goto out;
e095f51d
LP
377 }
378 if ((size_t) n != sizeof(struct ether_arp)) {
a48fc60a 379 log_ipv4acd(ll, "Ignoring too short ARP packet.");
e095f51d
LP
380 return 0;
381 }
e3dca008
TG
382
383 switch (ll->state) {
e095f51d 384
e3dca008
TG
385 case IPV4ACD_STATE_ANNOUNCING:
386 case IPV4ACD_STATE_RUNNING:
e095f51d 387
e3dca008
TG
388 if (ipv4acd_arp_conflict(ll, &packet)) {
389 usec_t ts;
390
391 assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
392
393 /* Defend address */
394 if (ts > ll->defend_window) {
395 ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC;
2f8e7633 396 r = arp_send_announcement(ll->fd, ll->ifindex, ll->address, &ll->mac_addr);
e3dca008 397 if (r < 0) {
a48fc60a 398 log_ipv4acd_errno(ll, r, "Failed to send ARP announcement: %m");
e3dca008
TG
399 goto out;
400 } else
a48fc60a 401 log_ipv4acd(ll, "DEFEND");
e3dca008
TG
402
403 } else
404 ipv4acd_on_conflict(ll);
405 }
e3dca008 406 break;
e095f51d 407
e3dca008
TG
408 case IPV4ACD_STATE_WAITING_PROBE:
409 case IPV4ACD_STATE_PROBING:
410 case IPV4ACD_STATE_WAITING_ANNOUNCE:
411 /* BPF ensures this packet indicates a conflict */
412 ipv4acd_on_conflict(ll);
e3dca008 413 break;
e095f51d 414
e3dca008
TG
415 default:
416 assert_not_reached("Invalid state.");
417 }
418
419out:
420 if (r < 0)
421 sd_ipv4acd_stop(ll);
422
423 return 1;
424}
425
2f8e7633 426int sd_ipv4acd_set_ifindex(sd_ipv4acd *ll, int ifindex) {
e3dca008 427 assert_return(ll, -EINVAL);
2f8e7633 428 assert_return(ifindex > 0, -EINVAL);
e3dca008
TG
429 assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
430
2f8e7633 431 ll->ifindex = ifindex;
e3dca008
TG
432
433 return 0;
434}
435
436int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
437 assert_return(ll, -EINVAL);
438 assert_return(addr, -EINVAL);
439 assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
440
441 memcpy(&ll->mac_addr, addr, ETH_ALEN);
442
443 return 0;
444}
445
446int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
447 assert_return(ll, -EINVAL);
448
449 ll->event = sd_event_unref(ll->event);
450
451 return 0;
452}
453
32d20645 454int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority) {
e3dca008
TG
455 int r;
456
457 assert_return(ll, -EINVAL);
458 assert_return(!ll->event, -EBUSY);
459
460 if (event)
461 ll->event = sd_event_ref(event);
462 else {
463 r = sd_event_default(&ll->event);
464 if (r < 0)
465 return r;
466 }
467
468 ll->event_priority = priority;
469
470 return 0;
471}
472
ccf86354 473int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata) {
e3dca008
TG
474 assert_return(ll, -EINVAL);
475
45aa74c7 476 ll->callback = cb;
e3dca008
TG
477 ll->userdata = userdata;
478
479 return 0;
480}
481
9ed794a3 482int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address) {
e3dca008
TG
483 assert_return(ll, -EINVAL);
484 assert_return(address, -EINVAL);
485 assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
486
487 ll->address = address->s_addr;
488
489 return 0;
490}
491
04c01369 492int sd_ipv4acd_is_running(sd_ipv4acd *ll) {
e3dca008
TG
493 assert_return(ll, false);
494
495 return ll->state != IPV4ACD_STATE_INIT;
496}
497
e3dca008
TG
498int sd_ipv4acd_start(sd_ipv4acd *ll) {
499 int r;
500
501 assert_return(ll, -EINVAL);
502 assert_return(ll->event, -EINVAL);
2f8e7633 503 assert_return(ll->ifindex > 0, -EINVAL);
e3dca008 504 assert_return(ll->address != 0, -EINVAL);
e78f9587 505 assert_return(!ether_addr_is_null(&ll->mac_addr), -EINVAL);
e3dca008
TG
506 assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
507
508 ll->defend_window = 0;
509
2f8e7633 510 r = arp_network_bind_raw_socket(ll->ifindex, ll->address, &ll->mac_addr);
e3dca008
TG
511 if (r < 0)
512 goto out;
513
514 ll->fd = safe_close(ll->fd);
515 ll->fd = r;
516
4dbf7b3a 517 r = sd_event_add_io(ll->event, &ll->receive_message_event_source, ll->fd, EPOLLIN, ipv4acd_on_packet, ll);
e3dca008
TG
518 if (r < 0)
519 goto out;
520
4dbf7b3a 521 r = sd_event_source_set_priority(ll->receive_message_event_source, ll->event_priority);
e3dca008
TG
522 if (r < 0)
523 goto out;
524
4dbf7b3a 525 (void) sd_event_source_set_description(ll->receive_message_event_source, "ipv4acd-receive-message");
e3dca008
TG
526
527 r = ipv4acd_set_next_wakeup(ll, 0, 0);
528 if (r < 0)
529 goto out;
530out:
531 if (r < 0) {
532 ipv4acd_stop(ll);
533 return r;
534 }
535
536 return 0;
537}