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