]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-ipv4ll.c
util-lib: split our string related calls from util.[ch] into its own file string...
[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 "event-util.h"
31 #include "in-addr-util.h"
32 #include "list.h"
33 #include "random-util.h"
34 #include "refcnt.h"
35 #include "siphash24.h"
36 #include "sparse-endian.h"
37 #include "util.h"
38
39 #define IPV4LL_NETWORK 0xA9FE0000L
40 #define IPV4LL_NETMASK 0xFFFF0000L
41
42 #define IPV4LL_DONT_DESTROY(ll) \
43 _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
44
45 struct sd_ipv4ll {
46 unsigned n_ref;
47
48 sd_ipv4acd *acd;
49 be32_t address; /* the address pushed to ACD */
50 struct random_data *random_data;
51 char *random_data_state;
52
53 /* External */
54 be32_t claimed_address;
55 sd_ipv4ll_cb_t cb;
56 void* userdata;
57 };
58
59 sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
60 if (!ll)
61 return NULL;
62
63 assert(ll->n_ref >= 1);
64 ll->n_ref++;
65
66 return ll;
67 }
68
69 sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
70 if (!ll)
71 return NULL;
72
73 assert(ll->n_ref >= 1);
74 ll->n_ref--;
75
76 if (ll->n_ref > 0)
77 return NULL;
78
79 sd_ipv4acd_unref(ll->acd);
80
81 free(ll->random_data);
82 free(ll->random_data_state);
83 free(ll);
84
85 return NULL;
86 }
87
88 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
89 #define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp)
90
91 static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
92
93 int sd_ipv4ll_new(sd_ipv4ll **ret) {
94 _cleanup_ipv4ll_unref_ sd_ipv4ll *ll = NULL;
95 int r;
96
97 assert_return(ret, -EINVAL);
98
99 ll = new0(sd_ipv4ll, 1);
100 if (!ll)
101 return -ENOMEM;
102
103 r = sd_ipv4acd_new(&ll->acd);
104 if (r < 0)
105 return r;
106
107 r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
108 if (r < 0)
109 return r;
110
111 ll->n_ref = 1;
112
113 *ret = ll;
114 ll = NULL;
115
116 return 0;
117 }
118
119 int sd_ipv4ll_stop(sd_ipv4ll *ll) {
120 int r;
121
122 assert_return(ll, -EINVAL);
123
124 r = sd_ipv4acd_stop(ll->acd);
125 if (r < 0)
126 return r;
127
128 return 0;
129 }
130
131 int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
132 assert_return(ll, -EINVAL);
133
134 return sd_ipv4acd_set_index(ll->acd, interface_index);
135 }
136
137 #define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
138
139 int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
140 int r;
141
142 assert_return(ll, -EINVAL);
143
144 if (!ll->random_data) {
145 uint8_t seed[8];
146
147 /* If no random data is set, generate some from the MAC */
148 siphash24(seed, &addr->ether_addr_octet,
149 ETH_ALEN, HASH_KEY.bytes);
150
151 assert_cc(sizeof(unsigned) <= 8);
152
153 r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)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 bool 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 }