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