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