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