]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-ipv4acd.c
sd-ipv4ll: add a bit of logging to IPv4LL too
[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
3aacc173
LP
89#define log_ipv4acd_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__)
90#define log_ipv4acd(ll, fmt, ...) log_ipv4acd_errno(ll, 0, fmt, ##__VA_ARGS__)
91
d246e77a
LP
92static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
93 assert(ll);
94 assert(st < _IPV4ACD_STATE_MAX);
95
96 if (st == ll->state && !reset_counter)
97 ll->n_iteration++;
98 else {
99 ll->state = st;
100 ll->n_iteration = 0;
101 }
102}
103
104static void ipv4acd_reset(sd_ipv4acd *ll) {
105 assert(ll);
106
107 ll->timer_event_source = sd_event_source_unref(ll->timer_event_source);
108 ll->receive_message_event_source = sd_event_source_unref(ll->receive_message_event_source);
109
110 ll->fd = safe_close(ll->fd);
111
112 ipv4acd_set_state(ll, IPV4ACD_STATE_INIT, true);
113}
114
e3dca008 115sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) {
c116f526
LP
116 if (!ll)
117 return NULL;
118
119 assert_se(ll->n_ref >= 1);
120 ll->n_ref++;
e3dca008
TG
121
122 return ll;
123}
124
125sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
c116f526
LP
126 if (!ll)
127 return NULL;
128
129 assert_se(ll->n_ref >= 1);
130 ll->n_ref--;
131
132 if (ll->n_ref > 0)
e3dca008
TG
133 return NULL;
134
d246e77a 135 ipv4acd_reset(ll);
e3dca008
TG
136 sd_ipv4acd_detach_event(ll);
137
138 free(ll);
139
140 return NULL;
141}
142
e3dca008 143int sd_ipv4acd_new(sd_ipv4acd **ret) {
4afd3348 144 _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *ll = NULL;
e3dca008
TG
145
146 assert_return(ret, -EINVAL);
147
148 ll = new0(sd_ipv4acd, 1);
149 if (!ll)
150 return -ENOMEM;
151
c116f526 152 ll->n_ref = 1;
e3dca008 153 ll->state = IPV4ACD_STATE_INIT;
2f8e7633 154 ll->ifindex = -1;
e3dca008
TG
155 ll->fd = -1;
156
157 *ret = ll;
158 ll = NULL;
159
160 return 0;
161}
162
e3dca008
TG
163static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
164 assert(ll);
165
45aa74c7 166 if (!ll->callback)
e095f51d
LP
167 return;
168
45aa74c7 169 ll->callback(ll, event, ll->userdata);
e3dca008
TG
170}
171
d246e77a
LP
172int sd_ipv4acd_stop(sd_ipv4acd *ll) {
173 assert_return(ll, -EINVAL);
e3dca008 174
d246e77a 175 ipv4acd_reset(ll);
e3dca008 176
a48fc60a 177 log_ipv4acd(ll, "STOPPED");
e3dca008 178
2237aa02 179 ipv4acd_client_notify(ll, 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
e3f4eedb 186static int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, 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
e3dca008
TG
191 assert(ll);
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
TG
197
198 assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
199
e3f4eedb 200 r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(), time_now + next_timeout, 0, ipv4acd_on_timeout, ll);
e3dca008
TG
201 if (r < 0)
202 return r;
203
204 r = sd_event_source_set_priority(timer, ll->event_priority);
205 if (r < 0)
206 return r;
207
4dbf7b3a 208 (void) sd_event_source_set_description(timer, "ipv4acd-timer");
e3dca008 209
4dbf7b3a
LP
210 sd_event_source_unref(ll->timer_event_source);
211 ll->timer_event_source = timer;
e3dca008
TG
212 timer = NULL;
213
214 return 0;
215}
216
217static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
218 assert(ll);
219 assert(arp);
220
221 /* see the BPF */
222 if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
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) {
230 sd_ipv4acd *ll = userdata;
231 int r = 0;
232
233 assert(ll);
234
235 switch (ll->state) {
e3dca008 236
c9e458a4 237 case IPV4ACD_STATE_STARTED:
e3dca008
TG
238 ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true);
239
784cdc2d 240 if (ll->n_conflict >= MAX_CONFLICTS) {
e3f4eedb
LP
241 char ts[FORMAT_TIMESPAN_MAX];
242 log_ipv4acd(ll, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0));
a48fc60a 243
e3f4eedb 244 r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
e3dca008 245 if (r < 0)
ff0c5ebd 246 goto fail;
e3dca008 247
784cdc2d 248 ll->n_conflict = 0;
e3dca008 249 } else {
e3f4eedb 250 r = ipv4acd_set_next_wakeup(ll, 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 */
2f8e7633 260 r = arp_send_probe(ll->fd, ll->ifindex, ll->address, &ll->mac_addr);
e3dca008 261 if (r < 0) {
a48fc60a 262 log_ipv4acd_errno(ll, r, "Failed to send ARP probe: %m");
ff0c5ebd 263 goto fail;
e3dca008
TG
264 } else {
265 _cleanup_free_ char *address = NULL;
266 union in_addr_union addr = { .in.s_addr = ll->address };
267
d246e77a
LP
268 (void) in_addr_to_string(AF_INET, &addr, &address);
269 log_ipv4acd(ll, "Probing %s", strna(address));
e3dca008
TG
270 }
271
784cdc2d 272 if (ll->n_iteration < PROBE_NUM - 2) {
e3dca008
TG
273 ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
274
e3f4eedb 275 r = ipv4acd_set_next_wakeup(ll, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC));
e3dca008 276 if (r < 0)
ff0c5ebd 277 goto fail;
e3dca008
TG
278 } else {
279 ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
280
e3f4eedb 281 r = ipv4acd_set_next_wakeup(ll, 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:
784cdc2d 289 if (ll->n_iteration >= ANNOUNCE_NUM - 1) {
e3dca008 290 ipv4acd_set_state(ll, 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 */
2f8e7633 298 r = arp_send_announcement(ll->fd, ll->ifindex, ll->address, &ll->mac_addr);
e3dca008 299 if (r < 0) {
a48fc60a 300 log_ipv4acd_errno(ll, r, "Failed to send ARP announcement: %m");
ff0c5ebd 301 goto fail;
e3dca008 302 } else
a48fc60a 303 log_ipv4acd(ll, "ANNOUNCE");
e3dca008
TG
304
305 ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
306
e3f4eedb 307 r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL_USEC, 0);
e3dca008 308 if (r < 0)
ff0c5ebd 309 goto fail;
e3dca008 310
784cdc2d
LP
311 if (ll->n_iteration == 0) {
312 ll->n_conflict = 0;
2237aa02 313 ipv4acd_client_notify(ll, 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
LP
324fail:
325 sd_ipv4acd_stop(ll);
326 return 0;
e3dca008
TG
327}
328
329static void ipv4acd_on_conflict(sd_ipv4acd *ll) {
330 _cleanup_free_ char *address = NULL;
331 union in_addr_union addr = { .in.s_addr = ll->address };
e3dca008
TG
332
333 assert(ll);
334
784cdc2d 335 ll->n_conflict++;
e3dca008 336
d246e77a
LP
337 (void) in_addr_to_string(AF_INET, &addr, &address);
338 log_ipv4acd(ll, "Conflict on %s (%u)", strna(address), ll->n_conflict);
e3dca008 339
d246e77a 340 ipv4acd_reset(ll);
2237aa02 341 ipv4acd_client_notify(ll, 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
e3dca008
TG
350 sd_ipv4acd *ll = userdata;
351 struct ether_arp packet;
e095f51d 352 ssize_t n;
e3dca008
TG
353 int r;
354
e095f51d 355 assert(s);
e3dca008
TG
356 assert(ll);
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
ff0c5ebd
LP
364 log_ipv4acd_errno(ll, errno, "Failed to read ARP packet: %m");
365 goto fail;
e095f51d
LP
366 }
367 if ((size_t) n != sizeof(struct ether_arp)) {
a48fc60a 368 log_ipv4acd(ll, "Ignoring too short ARP packet.");
e095f51d
LP
369 return 0;
370 }
e3dca008
TG
371
372 switch (ll->state) {
e095f51d 373
e3dca008
TG
374 case IPV4ACD_STATE_ANNOUNCING:
375 case IPV4ACD_STATE_RUNNING:
e095f51d 376
e3dca008
TG
377 if (ipv4acd_arp_conflict(ll, &packet)) {
378 usec_t ts;
379
380 assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
381
382 /* Defend address */
383 if (ts > ll->defend_window) {
e3f4eedb 384 ll->defend_window = ts + DEFEND_INTERVAL_USEC;
2f8e7633 385 r = arp_send_announcement(ll->fd, ll->ifindex, ll->address, &ll->mac_addr);
e3dca008 386 if (r < 0) {
a48fc60a 387 log_ipv4acd_errno(ll, r, "Failed to send ARP announcement: %m");
ff0c5ebd 388 goto fail;
e3dca008 389 } else
a48fc60a 390 log_ipv4acd(ll, "DEFEND");
e3dca008
TG
391
392 } else
393 ipv4acd_on_conflict(ll);
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 */
401 ipv4acd_on_conflict(ll);
e3dca008 402 break;
e095f51d 403
e3dca008
TG
404 default:
405 assert_not_reached("Invalid state.");
406 }
407
ff0c5ebd 408 return 0;
e3dca008 409
ff0c5ebd
LP
410fail:
411 sd_ipv4acd_stop(ll);
412 return 0;
e3dca008
TG
413}
414
2f8e7633 415int sd_ipv4acd_set_ifindex(sd_ipv4acd *ll, int ifindex) {
e3dca008 416 assert_return(ll, -EINVAL);
2f8e7633 417 assert_return(ifindex > 0, -EINVAL);
e3dca008
TG
418 assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
419
2f8e7633 420 ll->ifindex = ifindex;
e3dca008
TG
421
422 return 0;
423}
424
425int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
426 assert_return(ll, -EINVAL);
427 assert_return(addr, -EINVAL);
428 assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
429
d914f7a5 430 ll->mac_addr = *addr;
e3dca008
TG
431
432 return 0;
433}
434
435int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
436 assert_return(ll, -EINVAL);
437
438 ll->event = sd_event_unref(ll->event);
439
440 return 0;
441}
442
32d20645 443int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority) {
e3dca008
TG
444 int r;
445
446 assert_return(ll, -EINVAL);
447 assert_return(!ll->event, -EBUSY);
448
449 if (event)
450 ll->event = sd_event_ref(event);
451 else {
452 r = sd_event_default(&ll->event);
453 if (r < 0)
454 return r;
455 }
456
457 ll->event_priority = priority;
458
459 return 0;
460}
461
ccf86354 462int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata) {
e3dca008
TG
463 assert_return(ll, -EINVAL);
464
45aa74c7 465 ll->callback = cb;
e3dca008
TG
466 ll->userdata = userdata;
467
468 return 0;
469}
470
9ed794a3 471int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address) {
e3dca008
TG
472 assert_return(ll, -EINVAL);
473 assert_return(address, -EINVAL);
474 assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
475
476 ll->address = address->s_addr;
477
478 return 0;
479}
480
04c01369 481int sd_ipv4acd_is_running(sd_ipv4acd *ll) {
e3dca008
TG
482 assert_return(ll, false);
483
484 return ll->state != IPV4ACD_STATE_INIT;
485}
486
e3dca008
TG
487int sd_ipv4acd_start(sd_ipv4acd *ll) {
488 int r;
489
490 assert_return(ll, -EINVAL);
491 assert_return(ll->event, -EINVAL);
2f8e7633 492 assert_return(ll->ifindex > 0, -EINVAL);
e3dca008 493 assert_return(ll->address != 0, -EINVAL);
e78f9587 494 assert_return(!ether_addr_is_null(&ll->mac_addr), -EINVAL);
e3dca008
TG
495 assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
496
2f8e7633 497 r = arp_network_bind_raw_socket(ll->ifindex, ll->address, &ll->mac_addr);
e3dca008 498 if (r < 0)
d246e77a 499 return r;
e3dca008 500
d246e77a 501 safe_close(ll->fd);
e3dca008 502 ll->fd = r;
d246e77a 503 ll->defend_window = 0;
d6345845 504 ll->n_conflict = 0;
e3dca008 505
4dbf7b3a 506 r = sd_event_add_io(ll->event, &ll->receive_message_event_source, ll->fd, EPOLLIN, ipv4acd_on_packet, ll);
e3dca008 507 if (r < 0)
d246e77a 508 goto fail;
e3dca008 509
4dbf7b3a 510 r = sd_event_source_set_priority(ll->receive_message_event_source, ll->event_priority);
e3dca008 511 if (r < 0)
d246e77a 512 goto fail;
e3dca008 513
4dbf7b3a 514 (void) sd_event_source_set_description(ll->receive_message_event_source, "ipv4acd-receive-message");
e3dca008
TG
515
516 r = ipv4acd_set_next_wakeup(ll, 0, 0);
517 if (r < 0)
d246e77a 518 goto fail;
e3dca008 519
c9e458a4 520 ipv4acd_set_state(ll, IPV4ACD_STATE_STARTED, true);
e3dca008 521 return 0;
d246e77a
LP
522
523fail:
524 ipv4acd_reset(ll);
525 return r;
e3dca008 526}