chiark / gitweb /
sd-ipv4ll: Add reference counting for IPv4LL
[elogind.git] / src / libsystemd-network / sd-ipv4ll.c
1 /***
2   This file is part of systemd.
3
4   Copyright (C) 2014 Axis Communications AB. All rights reserved.
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <arpa/inet.h>
25
26 #include "util.h"
27 #include "siphash24.h"
28 #include "list.h"
29 #include "refcnt.h"
30
31 #include "ipv4ll-internal.h"
32 #include "sd-ipv4ll.h"
33
34 /* Constants from the RFC */
35 #define PROBE_WAIT 1
36 #define PROBE_NUM 3
37 #define PROBE_MIN 1
38 #define PROBE_MAX 2
39 #define ANNOUNCE_WAIT 2
40 #define ANNOUNCE_NUM 2
41 #define ANNOUNCE_INTERVAL 2
42 #define MAX_CONFLICTS 10
43 #define RATE_LIMIT_INTERVAL 60
44 #define DEFEND_INTERVAL 10
45
46 #define IPV4LL_NETWORK 0xA9FE0000L
47 #define IPV4LL_NETMASK 0xFFFF0000L
48
49 typedef enum IPv4LLTrigger{
50         IPV4LL_TRIGGER_NULL,
51         IPV4LL_TRIGGER_PACKET,
52         IPV4LL_TRIGGER_TIMEOUT,
53         _IPV4LL_TRIGGER_MAX,
54         _IPV4LL_TRIGGER_INVALID = -1
55 } IPv4LLTrigger;
56
57 typedef enum IPv4LLState {
58         IPV4LL_STATE_INIT,
59         IPV4LL_STATE_WAITING_PROBE,
60         IPV4LL_STATE_PROBING,
61         IPV4LL_STATE_WAITING_ANNOUNCE,
62         IPV4LL_STATE_ANNOUNCING,
63         IPV4LL_STATE_RUNNING,
64         _IPV4LL_STATE_MAX,
65         _IPV4LL_STATE_INVALID = -1
66 } IPv4LLState;
67
68 struct sd_ipv4ll {
69         RefCount n_ref;
70
71         IPv4LLState state;
72         int index;
73         int fd;
74         union sockaddr_union link;
75         int iteration;
76         int conflict;
77         sd_event_source *receive_message;
78         sd_event_source *timer;
79         usec_t next_wakeup;
80         usec_t defend_window;
81         int next_wakeup_valid;
82         be32_t address;
83         struct random_data *random_data;
84         char *random_data_state;
85         /* External */
86         be32_t claimed_address;
87         struct ether_addr mac_addr;
88         sd_event *event;
89         int event_priority;
90         sd_ipv4ll_cb_t cb;
91         void* userdata;
92 };
93
94 static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data);
95
96 static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, int reset_counter) {
97
98         assert(ll);
99         assert(st < _IPV4LL_STATE_MAX);
100
101         if (st == ll->state && !reset_counter) {
102                 ll->iteration++;
103         } else {
104                 ll->state = st;
105                 ll->iteration = 0;
106         }
107 }
108
109 static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
110         assert(ll);
111
112         if (ll->cb) {
113                 ll = sd_ipv4ll_ref(ll);
114                 ll->cb(ll, event, ll->userdata);
115                 ll = sd_ipv4ll_unref(ll);
116         }
117
118         return ll;
119 }
120
121 static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) {
122         assert(ll);
123
124         ll->receive_message = sd_event_source_unref(ll->receive_message);
125         ll->fd = safe_close(ll->fd);
126
127         ll->timer = sd_event_source_unref(ll->timer);
128
129         log_ipv4ll(ll, "STOPPED");
130
131         ll = ipv4ll_client_notify(ll, event);
132
133         if (ll) {
134                 ll->claimed_address = 0;
135                 ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
136         }
137
138         return ll;
139 }
140
141 static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) {
142         be32_t addr;
143         int r;
144         int32_t random;
145
146         assert(ll);
147         assert(address);
148         assert(ll->random_data);
149
150         do {
151                 r = random_r(ll->random_data, &random);
152                 if (r < 0)
153                         return r;
154                 addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
155         } while (addr == ll->address ||
156                 (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
157                 (ntohl(addr) & 0x0000FF00) == 0x0000 ||
158                 (ntohl(addr) & 0x0000FF00) == 0xFF00);
159
160         *address = addr;
161         return 0;
162 }
163
164 static int ipv4ll_timer(sd_event_source *s, uint64_t usec, void *userdata) {
165         sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
166
167         assert(ll);
168
169         ll->next_wakeup_valid = 0;
170         ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_TIMEOUT, NULL);
171
172         return 0;
173 }
174
175 static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) {
176         usec_t next_timeout = 0;
177         usec_t time_now = 0;
178
179         assert(sec >= 0);
180         assert(random_sec >= 0);
181         assert(ll);
182
183         next_timeout = sec * USEC_PER_SEC;
184
185         if (random_sec)
186                 next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
187
188         if (sd_event_now(ll->event, CLOCK_MONOTONIC, &time_now) < 0)
189                 time_now = now(CLOCK_MONOTONIC);
190
191         ll->next_wakeup = time_now + next_timeout;
192         ll->next_wakeup_valid = 1;
193 }
194
195 static bool ipv4ll_arp_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
196         assert(ll);
197         assert(arp);
198
199         if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0 &&
200             memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN) != 0)
201                 return true;
202
203         return false;
204 }
205
206 static bool ipv4ll_arp_probe_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
207         assert(ll);
208         assert(arp);
209
210         if (ipv4ll_arp_conflict(ll, arp))
211                 return true;
212
213         if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0 &&
214             memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN))
215                 return true;
216
217         return false;
218 }
219
220 static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data) {
221         struct ether_arp out_packet;
222         int out_packet_ready = 0;
223         int r = 0;
224
225         assert(ll);
226         assert(trigger < _IPV4LL_TRIGGER_MAX);
227
228         if (ll->state == IPV4LL_STATE_INIT) {
229
230                 log_ipv4ll(ll, "PROBE");
231                 ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
232                 ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
233
234         } else if ((ll->state == IPV4LL_STATE_WAITING_PROBE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
235                 (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < PROBE_NUM-2)) {
236
237                 /* Send a probe */
238                 arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
239                 out_packet_ready = 1;
240                 ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, 0);
241
242                 ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
243
244         } else if (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration >= PROBE_NUM-2) {
245
246                 /* Send the last probe */
247                 arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
248                 out_packet_ready = 1;
249                 ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, 1);
250
251                 ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
252
253         } else if ((ll->state == IPV4LL_STATE_WAITING_ANNOUNCE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
254                 (ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < ANNOUNCE_NUM-1)) {
255
256                 /* Send announcement packet */
257                 arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
258                 out_packet_ready = 1;
259                 ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, 0);
260
261                 ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
262
263                 if (ll->iteration == 0) {
264                         log_ipv4ll(ll, "ANNOUNCE");
265                         ll->claimed_address = ll->address;
266                         ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
267                         if (!ll)
268                                 goto out;
269
270                         ll->conflict = 0;
271                 }
272
273         } else if ((ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT &&
274                     ll->iteration >= ANNOUNCE_NUM-1)) {
275
276                 ipv4ll_set_state(ll, IPV4LL_STATE_RUNNING, 0);
277                 ll->next_wakeup_valid = 0;
278
279         } else if (trigger == IPV4LL_TRIGGER_PACKET) {
280
281                 int conflicted = 0;
282                 usec_t time_now;
283                 struct ether_arp* in_packet = (struct ether_arp*)trigger_data;
284
285                 assert(in_packet);
286
287                 if (IN_SET(ll->state, IPV4LL_STATE_ANNOUNCING, IPV4LL_STATE_RUNNING)) {
288
289                         if (ipv4ll_arp_conflict(ll, in_packet)) {
290
291                                 r = sd_event_now(ll->event, CLOCK_MONOTONIC, &time_now);
292                                 if (r < 0)
293                                         goto out;
294
295                                 /* Defend address */
296                                 if (time_now > ll->defend_window) {
297                                         ll->defend_window = time_now + DEFEND_INTERVAL * USEC_PER_SEC;
298                                         arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
299                                         out_packet_ready = 1;
300                                 } else
301                                         conflicted = 1;
302                         }
303
304                 } else if (IN_SET(ll->state, IPV4LL_STATE_WAITING_PROBE,
305                                              IPV4LL_STATE_PROBING,
306                                              IPV4LL_STATE_WAITING_ANNOUNCE)) {
307
308                         conflicted = ipv4ll_arp_probe_conflict(ll, in_packet);
309                 }
310
311                 if (conflicted) {
312                         log_ipv4ll(ll, "CONFLICT");
313                         ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
314                         if (!ll)
315                                 goto out;
316
317                         ll->claimed_address = 0;
318
319                         /* Pick a new address */
320                         r = ipv4ll_pick_address(ll, &ll->address);
321                         if (r < 0)
322                                 goto out;
323                         ll->conflict++;
324                         ll->defend_window = 0;
325                         ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
326
327                         if (ll->conflict >= MAX_CONFLICTS) {
328                                 log_ipv4ll(ll, "MAX_CONFLICTS");
329                                 ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
330                         } else
331                                 ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
332
333                 }
334         }
335
336         if (out_packet_ready) {
337                 r = arp_network_send_raw_socket(ll->fd, &ll->link, &out_packet);
338                 if (r < 0) {
339                         log_ipv4ll(ll, "failed to send arp packet out");
340                         goto out;
341                 }
342         }
343
344         if (ll->next_wakeup_valid) {
345                 ll->timer = sd_event_source_unref(ll->timer);
346                 r = sd_event_add_time(ll->event, &ll->timer, CLOCK_MONOTONIC,
347                                       ll->next_wakeup, 0, ipv4ll_timer, ll);
348                 if (r < 0)
349                         goto out;
350
351                 r = sd_event_source_set_priority(ll->timer, ll->event_priority);
352                 if (r < 0)
353                         goto out;
354         }
355
356 out:
357         if (r < 0 && ll)
358                 ipv4ll_stop(ll, r);
359 }
360
361 static int ipv4ll_receive_message(sd_event_source *s, int fd,
362                                   uint32_t revents, void *userdata) {
363         int r;
364         struct ether_arp arp;
365         sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
366
367         assert(ll);
368
369         r = read(fd, &arp, sizeof(struct ether_arp));
370         if (r < (int) sizeof(struct ether_arp))
371                 return 0;
372
373         r = arp_packet_verify_headers(&arp);
374         if (r < 0)
375                 return 0;
376
377         ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_PACKET, &arp);
378
379         return 0;
380 }
381
382 int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
383         assert_return(ll, -EINVAL);
384         assert_return(interface_index >= -1, -EINVAL);
385         assert_return(ll->state == IPV4LL_STATE_INIT, -EBUSY);
386
387         ll->index = interface_index;
388
389         return 0;
390 }
391
392 int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
393         bool need_restart = false;
394
395         assert_return(ll, -EINVAL);
396         assert_return(addr, -EINVAL);
397
398         if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0)
399                 return 0;
400
401         if (ll->state != IPV4LL_STATE_INIT) {
402                 log_ipv4ll(ll, "Changing MAC address on running IPv4LL "
403                            "client, restarting");
404                 ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
405                 need_restart = true;
406         }
407
408         if (!ll)
409                 return 0;
410
411         memcpy(&ll->mac_addr, addr, ETH_ALEN);
412
413         if (need_restart)
414                 sd_ipv4ll_start(ll);
415
416         return 0;
417 }
418
419 int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
420         assert_return(ll, -EINVAL);
421
422         ll->event = sd_event_unref(ll->event);
423
424         return 0;
425 }
426
427 int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
428         int r;
429
430         assert_return(ll, -EINVAL);
431         assert_return(!ll->event, -EBUSY);
432
433         if (event)
434                 ll->event = sd_event_ref(event);
435         else {
436                 r = sd_event_default(&ll->event);
437                 if (r < 0) {
438                         ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
439                         return r;
440                 }
441         }
442
443         ll->event_priority = priority;
444
445         return 0;
446 }
447
448 int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) {
449         assert_return(ll, -EINVAL);
450
451         ll->cb = cb;
452         ll->userdata = userdata;
453
454         return 0;
455 }
456
457 int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
458         assert_return(ll, -EINVAL);
459         assert_return(address, -EINVAL);
460
461         if (ll->claimed_address == 0) {
462                 return -ENOENT;
463         }
464
465         address->s_addr = ll->claimed_address;
466         return 0;
467 }
468
469 int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint8_t seed[8]) {
470         unsigned int entropy = *seed;
471         int r;
472
473         assert_return(ll, -EINVAL);
474
475         free(ll->random_data);
476         free(ll->random_data_state);
477
478         ll->random_data = new0(struct random_data, 1);
479         ll->random_data_state = new0(char, 128);
480
481         if (!ll->random_data || !ll->random_data_state) {
482                 r = -ENOMEM;
483                 goto error;
484         }
485
486         r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data);
487         if (r < 0)
488                 goto error;
489
490 error:
491         if (r < 0){
492                 free(ll->random_data);
493                 free(ll->random_data_state);
494                 ll->random_data = NULL;
495                 ll->random_data_state = NULL;
496         }
497         return r;
498 }
499
500 bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
501         assert_return(ll, -EINVAL);
502
503         return ll->state != IPV4LL_STATE_INIT;
504 }
505
506 #define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
507
508 int sd_ipv4ll_start (sd_ipv4ll *ll) {
509         int r;
510
511         assert_return(ll, -EINVAL);
512         assert_return(ll->event, -EINVAL);
513         assert_return(ll->index > 0, -EINVAL);
514         assert_return(ll->state == IPV4LL_STATE_INIT, -EBUSY);
515
516         r = arp_network_bind_raw_socket(ll->index, &ll->link);
517
518         if (r < 0)
519                 goto out;
520
521         ll->fd = r;
522         ll->conflict = 0;
523         ll->defend_window = 0;
524         ll->claimed_address = 0;
525
526         if (!ll->random_data) {
527                 uint8_t seed[8];
528
529                 /* Fallback to mac */
530                 siphash24(seed, &ll->mac_addr.ether_addr_octet,
531                           ETH_ALEN, HASH_KEY.bytes);
532
533                 r = sd_ipv4ll_set_address_seed(ll, seed);
534                 if (r < 0)
535                         goto out;
536         }
537
538         if (ll->address == 0) {
539                 r = ipv4ll_pick_address(ll, &ll->address);
540                 if (r < 0)
541                         goto out;
542         }
543
544         ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
545
546         r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
547                             EPOLLIN, ipv4ll_receive_message, ll);
548         if (r < 0)
549                 goto out;
550
551         r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
552         if (r < 0)
553                 goto out;
554
555         r = sd_event_add_time(ll->event,
556                               &ll->timer,
557                               CLOCK_MONOTONIC,
558                               now(CLOCK_MONOTONIC), 0,
559                               ipv4ll_timer, ll);
560
561         if (r < 0)
562                 goto out;
563
564         r = sd_event_source_set_priority(ll->timer, ll->event_priority);
565
566 out:
567         if (r < 0)
568                 ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
569
570         return 0;
571 }
572
573 int sd_ipv4ll_stop(sd_ipv4ll *ll) {
574         ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
575
576         return 0;
577 }
578
579 sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
580         if (ll)
581                 assert_se(REFCNT_INC(ll->n_ref) >= 2);
582
583         return ll;
584 }
585
586 sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
587         if (ll && REFCNT_DEC(ll->n_ref) <= 0) {
588                 ll->receive_message =
589                         sd_event_source_unref(ll->receive_message);
590                 ll->fd = safe_close(ll->fd);
591
592                 ll->timer = sd_event_source_unref(ll->timer);
593
594                 sd_ipv4ll_detach_event(ll);
595
596                 free(ll->random_data);
597                 free(ll->random_data_state);
598                 free(ll);
599
600                 return NULL;
601         }
602
603         return ll;
604 }
605
606 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
607 #define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp)
608
609 int sd_ipv4ll_new(sd_ipv4ll **ret) {
610         _cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL;
611
612         assert_return(ret, -EINVAL);
613
614         ll = new0(sd_ipv4ll, 1);
615         if (!ll)
616                 return -ENOMEM;
617
618         ll->n_ref = REFCNT_INIT;
619         ll->state = IPV4LL_STATE_INIT;
620         ll->index = -1;
621         ll->fd = -1;
622
623         *ret = ll;
624         ll = NULL;
625
626         return 0;
627 }