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