]>
Commit | Line | Data |
---|---|---|
5c1d3fc9 UTL |
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" | |
b5db00e5 | 27 | #include "siphash24.h" |
5c1d3fc9 | 28 | #include "list.h" |
3df3e884 | 29 | #include "random-util.h" |
5c1d3fc9 UTL |
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, | |
4d978a46 | 64 | IPV4LL_STATE_STOPPED, |
5c1d3fc9 UTL |
65 | _IPV4LL_STATE_MAX, |
66 | _IPV4LL_STATE_INVALID = -1 | |
67 | } IPv4LLState; | |
68 | ||
69 | struct sd_ipv4ll { | |
9c8e3101 | 70 | unsigned n_ref; |
56cd007a | 71 | |
5c1d3fc9 UTL |
72 | IPv4LLState state; |
73 | int index; | |
74 | int fd; | |
75 | union sockaddr_union link; | |
76 | int iteration; | |
77 | int conflict; | |
78 | sd_event_source *receive_message; | |
79 | sd_event_source *timer; | |
80 | usec_t next_wakeup; | |
81 | usec_t defend_window; | |
82 | int next_wakeup_valid; | |
83 | be32_t address; | |
b5db00e5 UTL |
84 | struct random_data *random_data; |
85 | char *random_data_state; | |
5c1d3fc9 UTL |
86 | /* External */ |
87 | be32_t claimed_address; | |
88 | struct ether_addr mac_addr; | |
89 | sd_event *event; | |
90 | int event_priority; | |
91 | sd_ipv4ll_cb_t cb; | |
92 | void* userdata; | |
93 | }; | |
94 | ||
95 | static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data); | |
96 | ||
97 | static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, int reset_counter) { | |
98 | ||
99 | assert(ll); | |
100 | assert(st < _IPV4LL_STATE_MAX); | |
101 | ||
102 | if (st == ll->state && !reset_counter) { | |
103 | ll->iteration++; | |
104 | } else { | |
105 | ll->state = st; | |
106 | ll->iteration = 0; | |
107 | } | |
108 | } | |
109 | ||
56cd007a | 110 | static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) { |
5c1d3fc9 UTL |
111 | assert(ll); |
112 | ||
56cd007a PF |
113 | if (ll->cb) { |
114 | ll = sd_ipv4ll_ref(ll); | |
5c1d3fc9 | 115 | ll->cb(ll, event, ll->userdata); |
56cd007a PF |
116 | ll = sd_ipv4ll_unref(ll); |
117 | } | |
5c1d3fc9 | 118 | |
56cd007a | 119 | return ll; |
5c1d3fc9 UTL |
120 | } |
121 | ||
56cd007a | 122 | static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) { |
5c1d3fc9 UTL |
123 | assert(ll); |
124 | ||
125 | ll->receive_message = sd_event_source_unref(ll->receive_message); | |
03e334a1 | 126 | ll->fd = safe_close(ll->fd); |
5c1d3fc9 UTL |
127 | |
128 | ll->timer = sd_event_source_unref(ll->timer); | |
129 | ||
56cd007a | 130 | log_ipv4ll(ll, "STOPPED"); |
5c1d3fc9 | 131 | |
56cd007a | 132 | ll = ipv4ll_client_notify(ll, event); |
5c1d3fc9 | 133 | |
56cd007a PF |
134 | if (ll) { |
135 | ll->claimed_address = 0; | |
136 | ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1); | |
137 | } | |
5c1d3fc9 | 138 | |
56cd007a | 139 | return ll; |
5c1d3fc9 UTL |
140 | } |
141 | ||
b5db00e5 | 142 | static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) { |
5c1d3fc9 | 143 | be32_t addr; |
b5db00e5 UTL |
144 | int r; |
145 | int32_t random; | |
5c1d3fc9 UTL |
146 | |
147 | assert(ll); | |
b5db00e5 UTL |
148 | assert(address); |
149 | assert(ll->random_data); | |
5c1d3fc9 | 150 | |
b5db00e5 UTL |
151 | do { |
152 | r = random_r(ll->random_data, &random); | |
153 | if (r < 0) | |
154 | return r; | |
155 | addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK); | |
156 | } while (addr == ll->address || | |
157 | (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK || | |
158 | (ntohl(addr) & 0x0000FF00) == 0x0000 || | |
159 | (ntohl(addr) & 0x0000FF00) == 0xFF00); | |
5c1d3fc9 | 160 | |
b5db00e5 UTL |
161 | *address = addr; |
162 | return 0; | |
5c1d3fc9 UTL |
163 | } |
164 | ||
165 | static int ipv4ll_timer(sd_event_source *s, uint64_t usec, void *userdata) { | |
166 | sd_ipv4ll *ll = (sd_ipv4ll*)userdata; | |
167 | ||
168 | assert(ll); | |
169 | ||
170 | ll->next_wakeup_valid = 0; | |
171 | ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_TIMEOUT, NULL); | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
6a0f1f6d | 176 | static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) { |
5c1d3fc9 UTL |
177 | usec_t next_timeout = 0; |
178 | usec_t time_now = 0; | |
179 | ||
180 | assert(sec >= 0); | |
181 | assert(random_sec >= 0); | |
182 | assert(ll); | |
183 | ||
184 | next_timeout = sec * USEC_PER_SEC; | |
185 | ||
186 | if (random_sec) | |
187 | next_timeout += random_u32() % (random_sec * USEC_PER_SEC); | |
188 | ||
38a03f06 | 189 | assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0); |
5c1d3fc9 UTL |
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; | |
56cd007a | 266 | ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND); |
4d978a46 | 267 | if (!ll || ll->state == IPV4LL_STATE_STOPPED) |
56cd007a PF |
268 | goto out; |
269 | ||
5c1d3fc9 UTL |
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 | ||
fa94c34b | 291 | r = sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now); |
5c1d3fc9 UTL |
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"); | |
56cd007a | 313 | ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT); |
4d978a46 | 314 | if (!ll || ll->state == IPV4LL_STATE_STOPPED) |
56cd007a PF |
315 | goto out; |
316 | ||
5c1d3fc9 UTL |
317 | ll->claimed_address = 0; |
318 | ||
319 | /* Pick a new address */ | |
b5db00e5 UTL |
320 | r = ipv4ll_pick_address(ll, &ll->address); |
321 | if (r < 0) | |
322 | goto out; | |
5c1d3fc9 UTL |
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); | |
fa94c34b | 346 | r = sd_event_add_time(ll->event, &ll->timer, clock_boottime_or_monotonic(), |
6a0f1f6d | 347 | ll->next_wakeup, 0, ipv4ll_timer, ll); |
5c1d3fc9 UTL |
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; | |
9021bb9f | 354 | |
356779df | 355 | r = sd_event_source_set_description(ll->timer, "ipv4ll-timer"); |
9021bb9f TG |
356 | if (r < 0) |
357 | goto out; | |
5c1d3fc9 UTL |
358 | } |
359 | ||
360 | out: | |
56cd007a | 361 | if (r < 0 && ll) |
5c1d3fc9 UTL |
362 | ipv4ll_stop(ll, r); |
363 | } | |
364 | ||
365 | static int ipv4ll_receive_message(sd_event_source *s, int fd, | |
366 | uint32_t revents, void *userdata) { | |
367 | int r; | |
368 | struct ether_arp arp; | |
369 | sd_ipv4ll *ll = (sd_ipv4ll*)userdata; | |
370 | ||
371 | assert(ll); | |
372 | ||
373 | r = read(fd, &arp, sizeof(struct ether_arp)); | |
374 | if (r < (int) sizeof(struct ether_arp)) | |
375 | return 0; | |
376 | ||
377 | r = arp_packet_verify_headers(&arp); | |
378 | if (r < 0) | |
379 | return 0; | |
380 | ||
381 | ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_PACKET, &arp); | |
382 | ||
383 | return 0; | |
384 | } | |
385 | ||
386 | int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) { | |
387 | assert_return(ll, -EINVAL); | |
d9bf4f8c | 388 | assert_return(interface_index > 0, -EINVAL); |
4d978a46 PF |
389 | assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT, |
390 | IPV4LL_STATE_STOPPED), -EBUSY); | |
5c1d3fc9 UTL |
391 | |
392 | ll->index = interface_index; | |
393 | ||
394 | return 0; | |
395 | } | |
396 | ||
397 | int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { | |
4644fee0 TG |
398 | bool need_restart = false; |
399 | ||
5c1d3fc9 | 400 | assert_return(ll, -EINVAL); |
4644fee0 TG |
401 | assert_return(addr, -EINVAL); |
402 | ||
403 | if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0) | |
404 | return 0; | |
405 | ||
4d978a46 | 406 | if (!IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED)) { |
4644fee0 TG |
407 | log_ipv4ll(ll, "Changing MAC address on running IPv4LL " |
408 | "client, restarting"); | |
56cd007a | 409 | ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP); |
4644fee0 TG |
410 | need_restart = true; |
411 | } | |
412 | ||
56cd007a PF |
413 | if (!ll) |
414 | return 0; | |
415 | ||
4644fee0 | 416 | memcpy(&ll->mac_addr, addr, ETH_ALEN); |
5c1d3fc9 | 417 | |
4644fee0 TG |
418 | if (need_restart) |
419 | sd_ipv4ll_start(ll); | |
5c1d3fc9 UTL |
420 | |
421 | return 0; | |
422 | } | |
423 | ||
424 | int sd_ipv4ll_detach_event(sd_ipv4ll *ll) { | |
425 | assert_return(ll, -EINVAL); | |
426 | ||
427 | ll->event = sd_event_unref(ll->event); | |
428 | ||
429 | return 0; | |
430 | } | |
431 | ||
432 | int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) { | |
433 | int r; | |
434 | ||
435 | assert_return(ll, -EINVAL); | |
436 | assert_return(!ll->event, -EBUSY); | |
437 | ||
438 | if (event) | |
439 | ll->event = sd_event_ref(event); | |
440 | else { | |
441 | r = sd_event_default(&ll->event); | |
442 | if (r < 0) { | |
443 | ipv4ll_stop(ll, IPV4LL_EVENT_STOP); | |
444 | return r; | |
445 | } | |
446 | } | |
447 | ||
448 | ll->event_priority = priority; | |
449 | ||
450 | return 0; | |
451 | } | |
452 | ||
453 | int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) { | |
454 | assert_return(ll, -EINVAL); | |
455 | ||
456 | ll->cb = cb; | |
457 | ll->userdata = userdata; | |
458 | ||
459 | return 0; | |
460 | } | |
461 | ||
462 | int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){ | |
463 | assert_return(ll, -EINVAL); | |
464 | assert_return(address, -EINVAL); | |
465 | ||
466 | if (ll->claimed_address == 0) { | |
467 | return -ENOENT; | |
468 | } | |
469 | ||
470 | address->s_addr = ll->claimed_address; | |
471 | return 0; | |
472 | } | |
473 | ||
b5db00e5 | 474 | int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint8_t seed[8]) { |
d9bf4f8c | 475 | unsigned int entropy; |
b5db00e5 UTL |
476 | int r; |
477 | ||
478 | assert_return(ll, -EINVAL); | |
d9bf4f8c UTL |
479 | assert_return(seed, -EINVAL); |
480 | ||
481 | entropy = *seed; | |
b5db00e5 UTL |
482 | |
483 | free(ll->random_data); | |
484 | free(ll->random_data_state); | |
485 | ||
486 | ll->random_data = new0(struct random_data, 1); | |
487 | ll->random_data_state = new0(char, 128); | |
488 | ||
489 | if (!ll->random_data || !ll->random_data_state) { | |
490 | r = -ENOMEM; | |
491 | goto error; | |
492 | } | |
493 | ||
494 | r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data); | |
495 | if (r < 0) | |
496 | goto error; | |
497 | ||
498 | error: | |
499 | if (r < 0){ | |
500 | free(ll->random_data); | |
501 | free(ll->random_data_state); | |
502 | ll->random_data = NULL; | |
503 | ll->random_data_state = NULL; | |
504 | } | |
505 | return r; | |
506 | } | |
507 | ||
aba496a5 | 508 | bool sd_ipv4ll_is_running(sd_ipv4ll *ll) { |
75677581 | 509 | assert_return(ll, false); |
aba496a5 | 510 | |
4d978a46 | 511 | return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED); |
aba496a5 UTL |
512 | } |
513 | ||
b5db00e5 UTL |
514 | #define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) |
515 | ||
5c1d3fc9 UTL |
516 | int sd_ipv4ll_start (sd_ipv4ll *ll) { |
517 | int r; | |
518 | ||
519 | assert_return(ll, -EINVAL); | |
520 | assert_return(ll->event, -EINVAL); | |
521 | assert_return(ll->index > 0, -EINVAL); | |
4d978a46 PF |
522 | assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT, |
523 | IPV4LL_STATE_STOPPED), -EBUSY); | |
524 | ||
525 | ll->state = IPV4LL_STATE_INIT; | |
5c1d3fc9 UTL |
526 | |
527 | r = arp_network_bind_raw_socket(ll->index, &ll->link); | |
528 | ||
529 | if (r < 0) | |
530 | goto out; | |
531 | ||
532 | ll->fd = r; | |
533 | ll->conflict = 0; | |
534 | ll->defend_window = 0; | |
535 | ll->claimed_address = 0; | |
536 | ||
b5db00e5 UTL |
537 | if (!ll->random_data) { |
538 | uint8_t seed[8]; | |
539 | ||
540 | /* Fallback to mac */ | |
541 | siphash24(seed, &ll->mac_addr.ether_addr_octet, | |
542 | ETH_ALEN, HASH_KEY.bytes); | |
543 | ||
544 | r = sd_ipv4ll_set_address_seed(ll, seed); | |
545 | if (r < 0) | |
546 | goto out; | |
547 | } | |
548 | ||
549 | if (ll->address == 0) { | |
550 | r = ipv4ll_pick_address(ll, &ll->address); | |
551 | if (r < 0) | |
552 | goto out; | |
553 | } | |
5c1d3fc9 UTL |
554 | |
555 | ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1); | |
556 | ||
557 | r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd, | |
558 | EPOLLIN, ipv4ll_receive_message, ll); | |
559 | if (r < 0) | |
560 | goto out; | |
561 | ||
562 | r = sd_event_source_set_priority(ll->receive_message, ll->event_priority); | |
563 | if (r < 0) | |
564 | goto out; | |
565 | ||
356779df | 566 | r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message"); |
9021bb9f TG |
567 | if (r < 0) |
568 | goto out; | |
569 | ||
6a0f1f6d LP |
570 | r = sd_event_add_time(ll->event, |
571 | &ll->timer, | |
fa94c34b TG |
572 | clock_boottime_or_monotonic(), |
573 | now(clock_boottime_or_monotonic()), 0, | |
6a0f1f6d | 574 | ipv4ll_timer, ll); |
5c1d3fc9 UTL |
575 | |
576 | if (r < 0) | |
577 | goto out; | |
578 | ||
579 | r = sd_event_source_set_priority(ll->timer, ll->event_priority); | |
9021bb9f TG |
580 | if (r < 0) |
581 | goto out; | |
5c1d3fc9 | 582 | |
356779df | 583 | r = sd_event_source_set_description(ll->timer, "ipv4ll-timer"); |
5c1d3fc9 UTL |
584 | out: |
585 | if (r < 0) | |
586 | ipv4ll_stop(ll, IPV4LL_EVENT_STOP); | |
587 | ||
588 | return 0; | |
589 | } | |
590 | ||
591 | int sd_ipv4ll_stop(sd_ipv4ll *ll) { | |
56cd007a | 592 | ipv4ll_stop(ll, IPV4LL_EVENT_STOP); |
4d978a46 PF |
593 | if (ll) |
594 | ipv4ll_set_state(ll, IPV4LL_STATE_STOPPED, 1); | |
56cd007a PF |
595 | |
596 | return 0; | |
5c1d3fc9 UTL |
597 | } |
598 | ||
56cd007a | 599 | sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { |
9c8e3101 LP |
600 | |
601 | if (!ll) | |
602 | return NULL; | |
603 | ||
604 | assert(ll->n_ref >= 1); | |
605 | ll->n_ref++; | |
5c1d3fc9 | 606 | |
56cd007a PF |
607 | return ll; |
608 | } | |
5c1d3fc9 | 609 | |
56cd007a | 610 | sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { |
56cd007a | 611 | |
9c8e3101 LP |
612 | if (!ll) |
613 | return NULL; | |
56cd007a | 614 | |
9c8e3101 LP |
615 | assert(ll->n_ref >= 1); |
616 | ll->n_ref--; | |
56cd007a | 617 | |
9c8e3101 LP |
618 | if (ll->n_ref > 0) |
619 | return ll; | |
56cd007a | 620 | |
9c8e3101 LP |
621 | ll->receive_message = sd_event_source_unref(ll->receive_message); |
622 | ll->fd = safe_close(ll->fd); | |
56cd007a | 623 | |
9c8e3101 LP |
624 | ll->timer = sd_event_source_unref(ll->timer); |
625 | ||
626 | sd_ipv4ll_detach_event(ll); | |
627 | ||
628 | free(ll->random_data); | |
629 | free(ll->random_data_state); | |
630 | free(ll); | |
631 | ||
632 | return NULL; | |
5c1d3fc9 UTL |
633 | } |
634 | ||
56cd007a PF |
635 | DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref); |
636 | #define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp) | |
5c1d3fc9 UTL |
637 | |
638 | int sd_ipv4ll_new(sd_ipv4ll **ret) { | |
639 | _cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL; | |
640 | ||
641 | assert_return(ret, -EINVAL); | |
642 | ||
643 | ll = new0(sd_ipv4ll, 1); | |
644 | if (!ll) | |
645 | return -ENOMEM; | |
646 | ||
9c8e3101 | 647 | ll->n_ref = 1; |
5c1d3fc9 UTL |
648 | ll->state = IPV4LL_STATE_INIT; |
649 | ll->index = -1; | |
650 | ll->fd = -1; | |
651 | ||
652 | *ret = ll; | |
653 | ll = NULL; | |
654 | ||
655 | return 0; | |
656 | } |