]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-ipv4ll.c
util-lib: split out allocation calls into alloc-util.[ch]
[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 r = sd_ipv4acd_new(&ll->acd);
105 if (r < 0)
106 return r;
107
108 r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
109 if (r < 0)
110 return r;
111
112 ll->n_ref = 1;
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 uint8_t seed[8];
147
148 /* If no random data is set, generate some from the MAC */
149 siphash24(seed, &addr->ether_addr_octet,
150 ETH_ALEN, HASH_KEY.bytes);
151
152 assert_cc(sizeof(unsigned) <= 8);
153
154 r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed);
155 if (r < 0)
156 return r;
157 }
158
159 return sd_ipv4acd_set_mac(ll->acd, addr);
160 }
161
162 int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
163 assert_return(ll, -EINVAL);
164
165 return sd_ipv4acd_detach_event(ll->acd);
166 }
167
168 int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
169 int r;
170
171 assert_return(ll, -EINVAL);
172
173 r = sd_ipv4acd_attach_event(ll->acd, event, priority);
174 if (r < 0)
175 return r;
176
177 return 0;
178 }
179
180 int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) {
181 assert_return(ll, -EINVAL);
182
183 ll->cb = cb;
184 ll->userdata = userdata;
185
186 return 0;
187 }
188
189 int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
190 assert_return(ll, -EINVAL);
191 assert_return(address, -EINVAL);
192
193 if (ll->claimed_address == 0)
194 return -ENOENT;
195
196 address->s_addr = ll->claimed_address;
197
198 return 0;
199 }
200
201 int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) {
202 _cleanup_free_ struct random_data *random_data = NULL;
203 _cleanup_free_ char *random_data_state = NULL;
204 int r;
205
206 assert_return(ll, -EINVAL);
207
208 random_data = new0(struct random_data, 1);
209 if (!random_data)
210 return -ENOMEM;
211
212 random_data_state = new0(char, 128);
213 if (!random_data_state)
214 return -ENOMEM;
215
216 r = initstate_r(seed, random_data_state, 128, random_data);
217 if (r < 0)
218 return r;
219
220 free(ll->random_data);
221 ll->random_data = random_data;
222 random_data = NULL;
223
224 free(ll->random_data_state);
225 ll->random_data_state = random_data_state;
226 random_data_state = NULL;
227
228 return 0;
229 }
230
231 int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
232 assert_return(ll, false);
233
234 return sd_ipv4acd_is_running(ll->acd);
235 }
236
237 static bool ipv4ll_address_is_valid(const struct in_addr *address) {
238 uint32_t addr;
239
240 assert(address);
241
242 if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address))
243 return false;
244
245 addr = be32toh(address->s_addr);
246
247 if ((addr & 0x0000FF00) == 0x0000 ||
248 (addr & 0x0000FF00) == 0xFF00)
249 return false;
250
251 return true;
252 }
253
254 int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
255 int r;
256
257 assert_return(ll, -EINVAL);
258 assert_return(address, -EINVAL);
259 assert_return(ipv4ll_address_is_valid(address), -EINVAL);
260
261 r = sd_ipv4acd_set_address(ll->acd, address);
262 if (r < 0)
263 return r;
264
265 ll->address = address->s_addr;
266
267 return 0;
268 }
269
270 static int ipv4ll_pick_address(sd_ipv4ll *ll) {
271 struct in_addr in_addr;
272 be32_t addr;
273 int r;
274 int32_t random;
275
276 assert(ll);
277 assert(ll->random_data);
278
279 do {
280 r = random_r(ll->random_data, &random);
281 if (r < 0)
282 return r;
283 addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
284 } while (addr == ll->address ||
285 (ntohl(addr) & 0x0000FF00) == 0x0000 ||
286 (ntohl(addr) & 0x0000FF00) == 0xFF00);
287
288 in_addr.s_addr = addr;
289
290 r = sd_ipv4ll_set_address(ll, &in_addr);
291 if (r < 0)
292 return r;
293
294 return 0;
295 }
296
297 int sd_ipv4ll_start(sd_ipv4ll *ll) {
298 int r;
299
300 assert_return(ll, -EINVAL);
301 assert_return(ll->random_data, -EINVAL);
302
303 if (ll->address == 0) {
304 r = ipv4ll_pick_address(ll);
305 if (r < 0)
306 return r;
307 }
308
309 r = sd_ipv4acd_start(ll->acd);
310 if (r < 0)
311 return r;
312
313 return 0;
314 }
315
316 static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
317 assert(ll);
318
319 if (ll->cb)
320 ll->cb(ll, event, ll->userdata);
321 }
322
323 void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
324 sd_ipv4ll *ll = userdata;
325 IPV4LL_DONT_DESTROY(ll);
326 int r;
327
328 assert(acd);
329 assert(ll);
330
331 switch (event) {
332 case SD_IPV4ACD_EVENT_STOP:
333 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
334
335 ll->claimed_address = 0;
336
337 break;
338 case SD_IPV4ACD_EVENT_BIND:
339 ll->claimed_address = ll->address;
340 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
341
342 break;
343 case SD_IPV4ACD_EVENT_CONFLICT:
344 /* if an address was already bound we must call up to the
345 user to handle this, otherwise we just try again */
346 if (ll->claimed_address != 0) {
347 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT);
348
349 ll->claimed_address = 0;
350 } else {
351 r = ipv4ll_pick_address(ll);
352 if (r < 0)
353 goto error;
354
355 r = sd_ipv4acd_start(ll->acd);
356 if (r < 0)
357 goto error;
358 }
359
360 break;
361 default:
362 assert_not_reached("Invalid IPv4ACD event.");
363 }
364
365 return;
366
367 error:
368 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
369 }