]>
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 | 30 | |
996d1697 | 31 | #include "arp-util.h" |
5c1d3fc9 UTL |
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 | ||
996d1697 TG |
49 | #define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__) |
50 | ||
5c1d3fc9 UTL |
51 | typedef enum IPv4LLState { |
52 | IPV4LL_STATE_INIT, | |
53 | IPV4LL_STATE_WAITING_PROBE, | |
54 | IPV4LL_STATE_PROBING, | |
55 | IPV4LL_STATE_WAITING_ANNOUNCE, | |
56 | IPV4LL_STATE_ANNOUNCING, | |
57 | IPV4LL_STATE_RUNNING, | |
4d978a46 | 58 | IPV4LL_STATE_STOPPED, |
5c1d3fc9 UTL |
59 | _IPV4LL_STATE_MAX, |
60 | _IPV4LL_STATE_INVALID = -1 | |
61 | } IPv4LLState; | |
62 | ||
63 | struct sd_ipv4ll { | |
9c8e3101 | 64 | unsigned n_ref; |
56cd007a | 65 | |
5c1d3fc9 UTL |
66 | IPv4LLState state; |
67 | int index; | |
68 | int fd; | |
5c1d3fc9 UTL |
69 | int iteration; |
70 | int conflict; | |
71 | sd_event_source *receive_message; | |
72 | sd_event_source *timer; | |
73 | usec_t next_wakeup; | |
74 | usec_t defend_window; | |
75 | int next_wakeup_valid; | |
76 | be32_t address; | |
b5db00e5 UTL |
77 | struct random_data *random_data; |
78 | char *random_data_state; | |
5c1d3fc9 UTL |
79 | /* External */ |
80 | be32_t claimed_address; | |
81 | struct ether_addr mac_addr; | |
82 | sd_event *event; | |
83 | int event_priority; | |
84 | sd_ipv4ll_cb_t cb; | |
85 | void* userdata; | |
86 | }; | |
87 | ||
5c1d3fc9 UTL |
88 | static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, int reset_counter) { |
89 | ||
90 | assert(ll); | |
91 | assert(st < _IPV4LL_STATE_MAX); | |
92 | ||
93 | if (st == ll->state && !reset_counter) { | |
94 | ll->iteration++; | |
95 | } else { | |
96 | ll->state = st; | |
97 | ll->iteration = 0; | |
98 | } | |
99 | } | |
100 | ||
56cd007a | 101 | static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) { |
5c1d3fc9 UTL |
102 | assert(ll); |
103 | ||
56cd007a PF |
104 | if (ll->cb) { |
105 | ll = sd_ipv4ll_ref(ll); | |
5c1d3fc9 | 106 | ll->cb(ll, event, ll->userdata); |
56cd007a PF |
107 | ll = sd_ipv4ll_unref(ll); |
108 | } | |
5c1d3fc9 | 109 | |
56cd007a | 110 | return ll; |
5c1d3fc9 UTL |
111 | } |
112 | ||
56cd007a | 113 | static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) { |
5c1d3fc9 UTL |
114 | assert(ll); |
115 | ||
116 | ll->receive_message = sd_event_source_unref(ll->receive_message); | |
03e334a1 | 117 | ll->fd = safe_close(ll->fd); |
5c1d3fc9 UTL |
118 | |
119 | ll->timer = sd_event_source_unref(ll->timer); | |
120 | ||
56cd007a | 121 | log_ipv4ll(ll, "STOPPED"); |
5c1d3fc9 | 122 | |
56cd007a | 123 | ll = ipv4ll_client_notify(ll, event); |
5c1d3fc9 | 124 | |
56cd007a PF |
125 | if (ll) { |
126 | ll->claimed_address = 0; | |
127 | ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1); | |
128 | } | |
5c1d3fc9 | 129 | |
56cd007a | 130 | return ll; |
5c1d3fc9 UTL |
131 | } |
132 | ||
b5db00e5 | 133 | static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) { |
5c1d3fc9 | 134 | be32_t addr; |
b5db00e5 UTL |
135 | int r; |
136 | int32_t random; | |
5c1d3fc9 UTL |
137 | |
138 | assert(ll); | |
b5db00e5 UTL |
139 | assert(address); |
140 | assert(ll->random_data); | |
5c1d3fc9 | 141 | |
b5db00e5 UTL |
142 | do { |
143 | r = random_r(ll->random_data, &random); | |
144 | if (r < 0) | |
145 | return r; | |
146 | addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK); | |
147 | } while (addr == ll->address || | |
148 | (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK || | |
149 | (ntohl(addr) & 0x0000FF00) == 0x0000 || | |
150 | (ntohl(addr) & 0x0000FF00) == 0xFF00); | |
5c1d3fc9 | 151 | |
b5db00e5 UTL |
152 | *address = addr; |
153 | return 0; | |
5c1d3fc9 UTL |
154 | } |
155 | ||
6a0f1f6d | 156 | static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) { |
5c1d3fc9 UTL |
157 | usec_t next_timeout = 0; |
158 | usec_t time_now = 0; | |
159 | ||
160 | assert(sec >= 0); | |
161 | assert(random_sec >= 0); | |
162 | assert(ll); | |
163 | ||
164 | next_timeout = sec * USEC_PER_SEC; | |
165 | ||
166 | if (random_sec) | |
167 | next_timeout += random_u32() % (random_sec * USEC_PER_SEC); | |
168 | ||
38a03f06 | 169 | assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0); |
5c1d3fc9 UTL |
170 | |
171 | ll->next_wakeup = time_now + next_timeout; | |
172 | ll->next_wakeup_valid = 1; | |
173 | } | |
174 | ||
175 | static bool ipv4ll_arp_conflict (sd_ipv4ll *ll, struct ether_arp *arp) { | |
176 | assert(ll); | |
177 | assert(arp); | |
178 | ||
996d1697 | 179 | if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0) |
5c1d3fc9 UTL |
180 | return true; |
181 | ||
182 | return false; | |
183 | } | |
184 | ||
185 | static bool ipv4ll_arp_probe_conflict (sd_ipv4ll *ll, struct ether_arp *arp) { | |
186 | assert(ll); | |
187 | assert(arp); | |
188 | ||
189 | if (ipv4ll_arp_conflict(ll, arp)) | |
190 | return true; | |
191 | ||
996d1697 | 192 | if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0) |
5c1d3fc9 UTL |
193 | return true; |
194 | ||
195 | return false; | |
196 | } | |
197 | ||
8e5787b5 TG |
198 | static int ipv4ll_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) { |
199 | sd_ipv4ll *ll = userdata; | |
5c1d3fc9 UTL |
200 | int r = 0; |
201 | ||
202 | assert(ll); | |
5c1d3fc9 UTL |
203 | |
204 | if (ll->state == IPV4LL_STATE_INIT) { | |
205 | ||
206 | log_ipv4ll(ll, "PROBE"); | |
207 | ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1); | |
208 | ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT); | |
209 | ||
8e5787b5 TG |
210 | } else if (ll->state == IPV4LL_STATE_WAITING_PROBE || |
211 | (ll->state == IPV4LL_STATE_PROBING && ll->iteration < PROBE_NUM-2)) { | |
5c1d3fc9 UTL |
212 | |
213 | /* Send a probe */ | |
996d1697 TG |
214 | r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr); |
215 | if (r < 0) { | |
216 | log_ipv4ll(ll, "Failed to send ARP probe."); | |
217 | goto out; | |
218 | } | |
219 | ||
5c1d3fc9 UTL |
220 | ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, 0); |
221 | ||
222 | ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN)); | |
223 | ||
8e5787b5 | 224 | } else if (ll->state == IPV4LL_STATE_PROBING && ll->iteration >= PROBE_NUM-2) { |
5c1d3fc9 UTL |
225 | |
226 | /* Send the last probe */ | |
996d1697 TG |
227 | r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr); |
228 | if (r < 0) { | |
229 | log_ipv4ll(ll, "Failed to send ARP probe."); | |
230 | goto out; | |
231 | } | |
232 | ||
5c1d3fc9 UTL |
233 | ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, 1); |
234 | ||
235 | ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0); | |
236 | ||
8e5787b5 TG |
237 | } else if (ll->state == IPV4LL_STATE_WAITING_ANNOUNCE || |
238 | (ll->state == IPV4LL_STATE_ANNOUNCING && ll->iteration < ANNOUNCE_NUM-1)) { | |
5c1d3fc9 UTL |
239 | |
240 | /* Send announcement packet */ | |
996d1697 TG |
241 | r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr); |
242 | if (r < 0) { | |
243 | log_ipv4ll(ll, "Failed to send ARP announcement."); | |
244 | goto out; | |
245 | } | |
246 | ||
5c1d3fc9 UTL |
247 | ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, 0); |
248 | ||
249 | ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0); | |
250 | ||
251 | if (ll->iteration == 0) { | |
252 | log_ipv4ll(ll, "ANNOUNCE"); | |
253 | ll->claimed_address = ll->address; | |
56cd007a | 254 | ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND); |
4d978a46 | 255 | if (!ll || ll->state == IPV4LL_STATE_STOPPED) |
56cd007a PF |
256 | goto out; |
257 | ||
5c1d3fc9 UTL |
258 | ll->conflict = 0; |
259 | } | |
260 | ||
8e5787b5 | 261 | } else if ((ll->state == IPV4LL_STATE_ANNOUNCING && |
5c1d3fc9 UTL |
262 | ll->iteration >= ANNOUNCE_NUM-1)) { |
263 | ||
264 | ipv4ll_set_state(ll, IPV4LL_STATE_RUNNING, 0); | |
265 | ll->next_wakeup_valid = 0; | |
8e5787b5 | 266 | } |
5c1d3fc9 | 267 | |
8e5787b5 TG |
268 | if (ll->next_wakeup_valid) { |
269 | ll->timer = sd_event_source_unref(ll->timer); | |
270 | r = sd_event_add_time(ll->event, &ll->timer, clock_boottime_or_monotonic(), | |
271 | ll->next_wakeup, 0, ipv4ll_on_timeout, ll); | |
272 | if (r < 0) | |
273 | goto out; | |
5c1d3fc9 | 274 | |
8e5787b5 TG |
275 | r = sd_event_source_set_priority(ll->timer, ll->event_priority); |
276 | if (r < 0) | |
277 | goto out; | |
5c1d3fc9 | 278 | |
8e5787b5 TG |
279 | r = sd_event_source_set_description(ll->timer, "ipv4ll-timer"); |
280 | if (r < 0) | |
281 | goto out; | |
282 | } | |
996d1697 | 283 | |
8e5787b5 TG |
284 | out: |
285 | if (r < 0 && ll) | |
286 | ipv4ll_stop(ll, r); | |
5c1d3fc9 | 287 | |
8e5787b5 TG |
288 | return 1; |
289 | } | |
5c1d3fc9 | 290 | |
8e5787b5 TG |
291 | static int ipv4ll_on_packet(sd_event_source *s, int fd, |
292 | uint32_t revents, void *userdata) { | |
293 | sd_ipv4ll *ll = userdata; | |
294 | struct ether_arp packet; | |
295 | int conflicted = 0; | |
296 | usec_t time_now; | |
297 | int r; | |
5c1d3fc9 | 298 | |
8e5787b5 TG |
299 | assert(ll); |
300 | assert(fd >= 0); | |
56cd007a | 301 | |
8e5787b5 TG |
302 | r = read(fd, &packet, sizeof(struct ether_arp)); |
303 | if (r < (int) sizeof(struct ether_arp)) | |
304 | goto out; | |
5c1d3fc9 | 305 | |
8e5787b5 | 306 | if (IN_SET(ll->state, IPV4LL_STATE_ANNOUNCING, IPV4LL_STATE_RUNNING)) { |
996d1697 | 307 | |
8e5787b5 | 308 | if (ipv4ll_arp_conflict(ll, &packet)) { |
996d1697 | 309 | |
8e5787b5 | 310 | r = sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now); |
996d1697 TG |
311 | if (r < 0) |
312 | goto out; | |
313 | ||
8e5787b5 TG |
314 | /* Defend address */ |
315 | if (time_now > ll->defend_window) { | |
316 | ll->defend_window = time_now + DEFEND_INTERVAL * USEC_PER_SEC; | |
317 | r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr); | |
318 | if (r < 0) { | |
319 | log_ipv4ll(ll, "Failed to send ARP announcement."); | |
320 | goto out; | |
321 | } | |
5c1d3fc9 | 322 | |
5c1d3fc9 | 323 | } else |
8e5787b5 | 324 | conflicted = 1; |
5c1d3fc9 | 325 | } |
5c1d3fc9 | 326 | |
8e5787b5 TG |
327 | } else if (IN_SET(ll->state, IPV4LL_STATE_WAITING_PROBE, |
328 | IPV4LL_STATE_PROBING, | |
329 | IPV4LL_STATE_WAITING_ANNOUNCE)) | |
330 | conflicted = ipv4ll_arp_probe_conflict(ll, &packet); | |
331 | ||
332 | if (conflicted) { | |
333 | log_ipv4ll(ll, "CONFLICT"); | |
334 | ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT); | |
335 | if (!ll || ll->state == IPV4LL_STATE_STOPPED) | |
5c1d3fc9 UTL |
336 | goto out; |
337 | ||
8e5787b5 TG |
338 | ll->claimed_address = 0; |
339 | ||
340 | /* Pick a new address */ | |
341 | r = ipv4ll_pick_address(ll, &ll->address); | |
5c1d3fc9 UTL |
342 | if (r < 0) |
343 | goto out; | |
9021bb9f | 344 | |
8e5787b5 TG |
345 | ll->fd = safe_close(ll->fd); |
346 | ||
347 | r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr); | |
9021bb9f TG |
348 | if (r < 0) |
349 | goto out; | |
8e5787b5 TG |
350 | |
351 | ll->fd = r; | |
352 | ||
353 | ll->conflict++; | |
354 | ll->defend_window = 0; | |
355 | ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1); | |
356 | ||
357 | if (ll->conflict >= MAX_CONFLICTS) { | |
358 | log_ipv4ll(ll, "MAX_CONFLICTS"); | |
359 | ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT); | |
360 | } else | |
361 | ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT); | |
5c1d3fc9 UTL |
362 | } |
363 | ||
364 | out: | |
56cd007a | 365 | if (r < 0 && ll) |
5c1d3fc9 | 366 | ipv4ll_stop(ll, r); |
5c1d3fc9 | 367 | |
8e5787b5 | 368 | return 1; |
5c1d3fc9 UTL |
369 | } |
370 | ||
371 | int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) { | |
372 | assert_return(ll, -EINVAL); | |
d9bf4f8c | 373 | assert_return(interface_index > 0, -EINVAL); |
4d978a46 PF |
374 | assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT, |
375 | IPV4LL_STATE_STOPPED), -EBUSY); | |
5c1d3fc9 UTL |
376 | |
377 | ll->index = interface_index; | |
378 | ||
379 | return 0; | |
380 | } | |
381 | ||
382 | int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { | |
4644fee0 TG |
383 | bool need_restart = false; |
384 | ||
5c1d3fc9 | 385 | assert_return(ll, -EINVAL); |
4644fee0 TG |
386 | assert_return(addr, -EINVAL); |
387 | ||
388 | if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0) | |
389 | return 0; | |
390 | ||
4d978a46 | 391 | if (!IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED)) { |
4644fee0 TG |
392 | log_ipv4ll(ll, "Changing MAC address on running IPv4LL " |
393 | "client, restarting"); | |
56cd007a | 394 | ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP); |
4644fee0 TG |
395 | need_restart = true; |
396 | } | |
397 | ||
56cd007a PF |
398 | if (!ll) |
399 | return 0; | |
400 | ||
4644fee0 | 401 | memcpy(&ll->mac_addr, addr, ETH_ALEN); |
5c1d3fc9 | 402 | |
4644fee0 TG |
403 | if (need_restart) |
404 | sd_ipv4ll_start(ll); | |
5c1d3fc9 UTL |
405 | |
406 | return 0; | |
407 | } | |
408 | ||
409 | int sd_ipv4ll_detach_event(sd_ipv4ll *ll) { | |
410 | assert_return(ll, -EINVAL); | |
411 | ||
412 | ll->event = sd_event_unref(ll->event); | |
413 | ||
414 | return 0; | |
415 | } | |
416 | ||
417 | int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) { | |
418 | int r; | |
419 | ||
420 | assert_return(ll, -EINVAL); | |
421 | assert_return(!ll->event, -EBUSY); | |
422 | ||
423 | if (event) | |
424 | ll->event = sd_event_ref(event); | |
425 | else { | |
426 | r = sd_event_default(&ll->event); | |
427 | if (r < 0) { | |
428 | ipv4ll_stop(ll, IPV4LL_EVENT_STOP); | |
429 | return r; | |
430 | } | |
431 | } | |
432 | ||
433 | ll->event_priority = priority; | |
434 | ||
435 | return 0; | |
436 | } | |
437 | ||
438 | int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) { | |
439 | assert_return(ll, -EINVAL); | |
440 | ||
441 | ll->cb = cb; | |
442 | ll->userdata = userdata; | |
443 | ||
444 | return 0; | |
445 | } | |
446 | ||
447 | int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){ | |
448 | assert_return(ll, -EINVAL); | |
449 | assert_return(address, -EINVAL); | |
450 | ||
ece174c5 | 451 | if (ll->claimed_address == 0) |
5c1d3fc9 | 452 | return -ENOENT; |
5c1d3fc9 UTL |
453 | |
454 | address->s_addr = ll->claimed_address; | |
455 | return 0; | |
456 | } | |
457 | ||
b5db00e5 | 458 | int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint8_t seed[8]) { |
d9bf4f8c | 459 | unsigned int entropy; |
b5db00e5 UTL |
460 | int r; |
461 | ||
462 | assert_return(ll, -EINVAL); | |
d9bf4f8c UTL |
463 | assert_return(seed, -EINVAL); |
464 | ||
465 | entropy = *seed; | |
b5db00e5 UTL |
466 | |
467 | free(ll->random_data); | |
468 | free(ll->random_data_state); | |
469 | ||
470 | ll->random_data = new0(struct random_data, 1); | |
471 | ll->random_data_state = new0(char, 128); | |
472 | ||
473 | if (!ll->random_data || !ll->random_data_state) { | |
474 | r = -ENOMEM; | |
475 | goto error; | |
476 | } | |
477 | ||
478 | r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data); | |
479 | if (r < 0) | |
480 | goto error; | |
481 | ||
482 | error: | |
483 | if (r < 0){ | |
484 | free(ll->random_data); | |
485 | free(ll->random_data_state); | |
486 | ll->random_data = NULL; | |
487 | ll->random_data_state = NULL; | |
488 | } | |
489 | return r; | |
490 | } | |
491 | ||
aba496a5 | 492 | bool sd_ipv4ll_is_running(sd_ipv4ll *ll) { |
75677581 | 493 | assert_return(ll, false); |
aba496a5 | 494 | |
4d978a46 | 495 | return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED); |
aba496a5 UTL |
496 | } |
497 | ||
b5db00e5 UTL |
498 | #define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) |
499 | ||
5c1d3fc9 UTL |
500 | int sd_ipv4ll_start (sd_ipv4ll *ll) { |
501 | int r; | |
502 | ||
503 | assert_return(ll, -EINVAL); | |
504 | assert_return(ll->event, -EINVAL); | |
505 | assert_return(ll->index > 0, -EINVAL); | |
4d978a46 PF |
506 | assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT, |
507 | IPV4LL_STATE_STOPPED), -EBUSY); | |
508 | ||
509 | ll->state = IPV4LL_STATE_INIT; | |
5c1d3fc9 | 510 | |
5c1d3fc9 UTL |
511 | ll->conflict = 0; |
512 | ll->defend_window = 0; | |
513 | ll->claimed_address = 0; | |
514 | ||
b5db00e5 UTL |
515 | if (!ll->random_data) { |
516 | uint8_t seed[8]; | |
517 | ||
518 | /* Fallback to mac */ | |
519 | siphash24(seed, &ll->mac_addr.ether_addr_octet, | |
520 | ETH_ALEN, HASH_KEY.bytes); | |
521 | ||
522 | r = sd_ipv4ll_set_address_seed(ll, seed); | |
523 | if (r < 0) | |
524 | goto out; | |
525 | } | |
526 | ||
527 | if (ll->address == 0) { | |
528 | r = ipv4ll_pick_address(ll, &ll->address); | |
529 | if (r < 0) | |
530 | goto out; | |
531 | } | |
5c1d3fc9 | 532 | |
996d1697 TG |
533 | r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr); |
534 | if (r < 0) | |
535 | goto out; | |
536 | ||
537 | safe_close(ll->fd); | |
538 | ll->fd = r; | |
539 | ||
5c1d3fc9 UTL |
540 | ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1); |
541 | ||
542 | r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd, | |
8e5787b5 | 543 | EPOLLIN, ipv4ll_on_packet, ll); |
5c1d3fc9 UTL |
544 | if (r < 0) |
545 | goto out; | |
546 | ||
547 | r = sd_event_source_set_priority(ll->receive_message, ll->event_priority); | |
548 | if (r < 0) | |
549 | goto out; | |
550 | ||
356779df | 551 | r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message"); |
9021bb9f TG |
552 | if (r < 0) |
553 | goto out; | |
554 | ||
6a0f1f6d LP |
555 | r = sd_event_add_time(ll->event, |
556 | &ll->timer, | |
fa94c34b TG |
557 | clock_boottime_or_monotonic(), |
558 | now(clock_boottime_or_monotonic()), 0, | |
8e5787b5 | 559 | ipv4ll_on_timeout, ll); |
5c1d3fc9 UTL |
560 | |
561 | if (r < 0) | |
562 | goto out; | |
563 | ||
564 | r = sd_event_source_set_priority(ll->timer, ll->event_priority); | |
9021bb9f TG |
565 | if (r < 0) |
566 | goto out; | |
5c1d3fc9 | 567 | |
356779df | 568 | r = sd_event_source_set_description(ll->timer, "ipv4ll-timer"); |
5c1d3fc9 UTL |
569 | out: |
570 | if (r < 0) | |
571 | ipv4ll_stop(ll, IPV4LL_EVENT_STOP); | |
572 | ||
573 | return 0; | |
574 | } | |
575 | ||
576 | int sd_ipv4ll_stop(sd_ipv4ll *ll) { | |
56cd007a | 577 | ipv4ll_stop(ll, IPV4LL_EVENT_STOP); |
4d978a46 PF |
578 | if (ll) |
579 | ipv4ll_set_state(ll, IPV4LL_STATE_STOPPED, 1); | |
56cd007a PF |
580 | |
581 | return 0; | |
5c1d3fc9 UTL |
582 | } |
583 | ||
56cd007a | 584 | sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { |
9c8e3101 LP |
585 | |
586 | if (!ll) | |
587 | return NULL; | |
588 | ||
589 | assert(ll->n_ref >= 1); | |
590 | ll->n_ref++; | |
5c1d3fc9 | 591 | |
56cd007a PF |
592 | return ll; |
593 | } | |
5c1d3fc9 | 594 | |
56cd007a | 595 | sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { |
56cd007a | 596 | |
9c8e3101 LP |
597 | if (!ll) |
598 | return NULL; | |
56cd007a | 599 | |
9c8e3101 LP |
600 | assert(ll->n_ref >= 1); |
601 | ll->n_ref--; | |
56cd007a | 602 | |
9c8e3101 LP |
603 | if (ll->n_ref > 0) |
604 | return ll; | |
56cd007a | 605 | |
9c8e3101 LP |
606 | ll->receive_message = sd_event_source_unref(ll->receive_message); |
607 | ll->fd = safe_close(ll->fd); | |
56cd007a | 608 | |
9c8e3101 LP |
609 | ll->timer = sd_event_source_unref(ll->timer); |
610 | ||
611 | sd_ipv4ll_detach_event(ll); | |
612 | ||
613 | free(ll->random_data); | |
614 | free(ll->random_data_state); | |
615 | free(ll); | |
616 | ||
617 | return NULL; | |
5c1d3fc9 UTL |
618 | } |
619 | ||
56cd007a PF |
620 | DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref); |
621 | #define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp) | |
5c1d3fc9 UTL |
622 | |
623 | int sd_ipv4ll_new(sd_ipv4ll **ret) { | |
624 | _cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL; | |
625 | ||
626 | assert_return(ret, -EINVAL); | |
627 | ||
628 | ll = new0(sd_ipv4ll, 1); | |
629 | if (!ll) | |
630 | return -ENOMEM; | |
631 | ||
9c8e3101 | 632 | ll->n_ref = 1; |
5c1d3fc9 UTL |
633 | ll->state = IPV4LL_STATE_INIT; |
634 | ll->index = -1; | |
635 | ll->fd = -1; | |
636 | ||
637 | *ret = ll; | |
638 | ll = NULL; | |
639 | ||
640 | return 0; | |
641 | } |