]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-ipv4ll.c
Merge pull request #1947 from phomes/sort-includes2
[thirdparty/systemd.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 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
27 #include "sd-ipv4acd.h"
28 #include "sd-ipv4ll.h"
29
30 #include "alloc-util.h"
31 #include "event-util.h"
32 #include "in-addr-util.h"
33 #include "list.h"
34 #include "random-util.h"
35 #include "refcnt.h"
36 #include "siphash24.h"
37 #include "sparse-endian.h"
38 #include "util.h"
39
40 #define IPV4LL_NETWORK 0xA9FE0000L
41 #define IPV4LL_NETMASK 0xFFFF0000L
42
43 #define IPV4LL_DONT_DESTROY(ll) \
44 _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
45
46 struct sd_ipv4ll {
47 unsigned n_ref;
48
49 sd_ipv4acd *acd;
50 be32_t address; /* the address pushed to ACD */
51 struct random_data *random_data;
52 char *random_data_state;
53
54 /* External */
55 be32_t claimed_address;
56 sd_ipv4ll_cb_t cb;
57 void* userdata;
58 };
59
60 sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
61 if (!ll)
62 return NULL;
63
64 assert(ll->n_ref >= 1);
65 ll->n_ref++;
66
67 return ll;
68 }
69
70 sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
71 if (!ll)
72 return NULL;
73
74 assert(ll->n_ref >= 1);
75 ll->n_ref--;
76
77 if (ll->n_ref > 0)
78 return NULL;
79
80 sd_ipv4acd_unref(ll->acd);
81
82 free(ll->random_data);
83 free(ll->random_data_state);
84 free(ll);
85
86 return NULL;
87 }
88
89 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
90 #define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp)
91
92 static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
93
94 int sd_ipv4ll_new(sd_ipv4ll **ret) {
95 _cleanup_ipv4ll_unref_ sd_ipv4ll *ll = NULL;
96 int r;
97
98 assert_return(ret, -EINVAL);
99
100 ll = new0(sd_ipv4ll, 1);
101 if (!ll)
102 return -ENOMEM;
103
104 ll->n_ref = 1;
105
106 r = sd_ipv4acd_new(&ll->acd);
107 if (r < 0)
108 return r;
109
110 r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
111 if (r < 0)
112 return r;
113
114 *ret = ll;
115 ll = NULL;
116
117 return 0;
118 }
119
120 int sd_ipv4ll_stop(sd_ipv4ll *ll) {
121 int r;
122
123 assert_return(ll, -EINVAL);
124
125 r = sd_ipv4acd_stop(ll->acd);
126 if (r < 0)
127 return r;
128
129 return 0;
130 }
131
132 int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
133 assert_return(ll, -EINVAL);
134
135 return sd_ipv4acd_set_index(ll->acd, interface_index);
136 }
137
138 #define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
139
140 int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
141 int r;
142
143 assert_return(ll, -EINVAL);
144
145 if (!ll->random_data) {
146 uint64_t seed;
147
148 /* If no random data is set, generate some from the MAC */
149 seed = siphash24(&addr->ether_addr_octet, ETH_ALEN, HASH_KEY.bytes);
150
151 assert_cc(sizeof(unsigned) <= 8);
152
153 r = sd_ipv4ll_set_address_seed(ll, (unsigned) htole64(seed));
154 if (r < 0)
155 return r;
156 }
157
158 return sd_ipv4acd_set_mac(ll->acd, addr);
159 }
160
161 int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
162 assert_return(ll, -EINVAL);
163
164 return sd_ipv4acd_detach_event(ll->acd);
165 }
166
167 int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
168 int r;
169
170 assert_return(ll, -EINVAL);
171
172 r = sd_ipv4acd_attach_event(ll->acd, event, priority);
173 if (r < 0)
174 return r;
175
176 return 0;
177 }
178
179 int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) {
180 assert_return(ll, -EINVAL);
181
182 ll->cb = cb;
183 ll->userdata = userdata;
184
185 return 0;
186 }
187
188 int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
189 assert_return(ll, -EINVAL);
190 assert_return(address, -EINVAL);
191
192 if (ll->claimed_address == 0)
193 return -ENOENT;
194
195 address->s_addr = ll->claimed_address;
196
197 return 0;
198 }
199
200 int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) {
201 _cleanup_free_ struct random_data *random_data = NULL;
202 _cleanup_free_ char *random_data_state = NULL;
203 int r;
204
205 assert_return(ll, -EINVAL);
206
207 random_data = new0(struct random_data, 1);
208 if (!random_data)
209 return -ENOMEM;
210
211 random_data_state = new0(char, 128);
212 if (!random_data_state)
213 return -ENOMEM;
214
215 r = initstate_r(seed, random_data_state, 128, random_data);
216 if (r < 0)
217 return r;
218
219 free(ll->random_data);
220 ll->random_data = random_data;
221 random_data = NULL;
222
223 free(ll->random_data_state);
224 ll->random_data_state = random_data_state;
225 random_data_state = NULL;
226
227 return 0;
228 }
229
230 int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
231 assert_return(ll, false);
232
233 return sd_ipv4acd_is_running(ll->acd);
234 }
235
236 static bool ipv4ll_address_is_valid(const struct in_addr *address) {
237 uint32_t addr;
238
239 assert(address);
240
241 if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address))
242 return false;
243
244 addr = be32toh(address->s_addr);
245
246 if ((addr & 0x0000FF00) == 0x0000 ||
247 (addr & 0x0000FF00) == 0xFF00)
248 return false;
249
250 return true;
251 }
252
253 int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
254 int r;
255
256 assert_return(ll, -EINVAL);
257 assert_return(address, -EINVAL);
258 assert_return(ipv4ll_address_is_valid(address), -EINVAL);
259
260 r = sd_ipv4acd_set_address(ll->acd, address);
261 if (r < 0)
262 return r;
263
264 ll->address = address->s_addr;
265
266 return 0;
267 }
268
269 static int ipv4ll_pick_address(sd_ipv4ll *ll) {
270 struct in_addr in_addr;
271 be32_t addr;
272 int r;
273 int32_t random;
274
275 assert(ll);
276 assert(ll->random_data);
277
278 do {
279 r = random_r(ll->random_data, &random);
280 if (r < 0)
281 return r;
282 addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
283 } while (addr == ll->address ||
284 (ntohl(addr) & 0x0000FF00) == 0x0000 ||
285 (ntohl(addr) & 0x0000FF00) == 0xFF00);
286
287 in_addr.s_addr = addr;
288
289 r = sd_ipv4ll_set_address(ll, &in_addr);
290 if (r < 0)
291 return r;
292
293 return 0;
294 }
295
296 int sd_ipv4ll_start(sd_ipv4ll *ll) {
297 int r;
298
299 assert_return(ll, -EINVAL);
300 assert_return(ll->random_data, -EINVAL);
301
302 if (ll->address == 0) {
303 r = ipv4ll_pick_address(ll);
304 if (r < 0)
305 return r;
306 }
307
308 r = sd_ipv4acd_start(ll->acd);
309 if (r < 0)
310 return r;
311
312 return 0;
313 }
314
315 static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
316 assert(ll);
317
318 if (ll->cb)
319 ll->cb(ll, event, ll->userdata);
320 }
321
322 void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
323 sd_ipv4ll *ll = userdata;
324 IPV4LL_DONT_DESTROY(ll);
325 int r;
326
327 assert(acd);
328 assert(ll);
329
330 switch (event) {
331 case SD_IPV4ACD_EVENT_STOP:
332 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
333
334 ll->claimed_address = 0;
335
336 break;
337 case SD_IPV4ACD_EVENT_BIND:
338 ll->claimed_address = ll->address;
339 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
340
341 break;
342 case SD_IPV4ACD_EVENT_CONFLICT:
343 /* if an address was already bound we must call up to the
344 user to handle this, otherwise we just try again */
345 if (ll->claimed_address != 0) {
346 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT);
347
348 ll->claimed_address = 0;
349 } else {
350 r = ipv4ll_pick_address(ll);
351 if (r < 0)
352 goto error;
353
354 r = sd_ipv4acd_start(ll->acd);
355 if (r < 0)
356 goto error;
357 }
358
359 break;
360 default:
361 assert_not_reached("Invalid IPv4ACD event.");
362 }
363
364 return;
365
366 error:
367 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
368 }