]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-ipv4ll.c
Merge pull request #3162 from keszybz/alias-refusal
[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
e3dca008
TG
128int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
129 assert_return(ll, -EINVAL);
5c1d3fc9 130
e3dca008 131 return sd_ipv4acd_set_index(ll->acd, interface_index);
5c1d3fc9
UTL
132}
133
e3dca008 134#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
5c1d3fc9 135
e3dca008 136int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
b26f7e8e
TG
137 int r;
138
e3dca008 139 assert_return(ll, -EINVAL);
b26f7e8e 140
e3dca008 141 if (!ll->random_data) {
dbe81cbd 142 uint64_t seed;
b26f7e8e 143
e3dca008 144 /* If no random data is set, generate some from the MAC */
933f9cae 145 seed = siphash24(&addr->ether_addr_octet, ETH_ALEN, HASH_KEY.bytes);
5c1d3fc9 146
e3dca008 147 assert_cc(sizeof(unsigned) <= 8);
5c1d3fc9 148
933f9cae 149 r = sd_ipv4ll_set_address_seed(ll, (unsigned) htole64(seed));
028e0b20 150 if (r < 0)
e3dca008 151 return r;
5c1d3fc9
UTL
152 }
153
e3dca008 154 return sd_ipv4acd_set_mac(ll->acd, addr);
5c1d3fc9
UTL
155}
156
157int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
158 assert_return(ll, -EINVAL);
159
e3dca008 160 return sd_ipv4acd_detach_event(ll->acd);
5c1d3fc9
UTL
161}
162
32d20645 163int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) {
5c1d3fc9
UTL
164 int r;
165
166 assert_return(ll, -EINVAL);
5c1d3fc9 167
e3dca008
TG
168 r = sd_ipv4acd_attach_event(ll->acd, event, priority);
169 if (r < 0)
170 return r;
5c1d3fc9
UTL
171
172 return 0;
173}
174
ccf86354 175int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) {
5c1d3fc9
UTL
176 assert_return(ll, -EINVAL);
177
178 ll->cb = cb;
179 ll->userdata = userdata;
180
181 return 0;
182}
183
9ed794a3 184int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
5c1d3fc9
UTL
185 assert_return(ll, -EINVAL);
186 assert_return(address, -EINVAL);
187
ece174c5 188 if (ll->claimed_address == 0)
5c1d3fc9 189 return -ENOENT;
5c1d3fc9
UTL
190
191 address->s_addr = ll->claimed_address;
e3dca008 192
5c1d3fc9
UTL
193 return 0;
194}
195
e3dca008
TG
196int 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;
b5db00e5
UTL
199 int r;
200
201 assert_return(ll, -EINVAL);
d9bf4f8c 202
e3dca008
TG
203 random_data = new0(struct random_data, 1);
204 if (!random_data)
205 return -ENOMEM;
b5db00e5 206
e3dca008
TG
207 random_data_state = new0(char, 128);
208 if (!random_data_state)
209 return -ENOMEM;
b5db00e5 210
e3dca008
TG
211 r = initstate_r(seed, random_data_state, 128, random_data);
212 if (r < 0)
213 return r;
b5db00e5 214
e3dca008
TG
215 free(ll->random_data);
216 ll->random_data = random_data;
217 random_data = NULL;
b5db00e5 218
e3dca008
TG
219 free(ll->random_data_state);
220 ll->random_data_state = random_data_state;
221 random_data_state = NULL;
b5db00e5 222
e3dca008 223 return 0;
b5db00e5
UTL
224}
225
04c01369 226int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
75677581 227 assert_return(ll, false);
aba496a5 228
e3dca008 229 return sd_ipv4acd_is_running(ll->acd);
aba496a5
UTL
230}
231
129dc1b4
TG
232static 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
249int 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
e3dca008
TG
265static int ipv4ll_pick_address(sd_ipv4ll *ll) {
266 struct in_addr in_addr;
267 be32_t addr;
5c1d3fc9 268 int r;
e3dca008 269 int32_t random;
5c1d3fc9 270
e3dca008
TG
271 assert(ll);
272 assert(ll->random_data);
4d978a46 273
e3dca008
TG
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 ||
e3dca008
TG
280 (ntohl(addr) & 0x0000FF00) == 0x0000 ||
281 (ntohl(addr) & 0x0000FF00) == 0xFF00);
5c1d3fc9 282
e3dca008 283 in_addr.s_addr = addr;
5c1d3fc9 284
129dc1b4 285 r = sd_ipv4ll_set_address(ll, &in_addr);
e3dca008
TG
286 if (r < 0)
287 return r;
b5db00e5 288
e3dca008
TG
289 return 0;
290}
291
292int sd_ipv4ll_start(sd_ipv4ll *ll) {
293 int r;
294
295 assert_return(ll, -EINVAL);
296 assert_return(ll->random_data, -EINVAL);
b5db00e5
UTL
297
298 if (ll->address == 0) {
e3dca008 299 r = ipv4ll_pick_address(ll);
b5db00e5 300 if (r < 0)
e3dca008 301 return r;
b5db00e5 302 }
5c1d3fc9 303
e3dca008 304 r = sd_ipv4acd_start(ll->acd);
996d1697 305 if (r < 0)
e3dca008 306 return r;
996d1697 307
e3dca008
TG
308 return 0;
309}
996d1697 310
e3dca008
TG
311static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
312 assert(ll);
5c1d3fc9 313
e3dca008
TG
314 if (ll->cb)
315 ll->cb(ll, event, ll->userdata);
316}
5c1d3fc9 317
e3dca008
TG
318void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
319 sd_ipv4ll *ll = userdata;
320 IPV4LL_DONT_DESTROY(ll);
321 int r;
5c1d3fc9 322
e3dca008
TG
323 assert(acd);
324 assert(ll);
9021bb9f 325
e3dca008 326 switch (event) {
2237aa02 327 case SD_IPV4ACD_EVENT_STOP:
be19c5b5 328 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
5c1d3fc9 329
e3dca008
TG
330 ll->claimed_address = 0;
331
332 break;
2237aa02 333 case SD_IPV4ACD_EVENT_BIND:
e3dca008 334 ll->claimed_address = ll->address;
be19c5b5 335 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
e3dca008
TG
336
337 break;
2237aa02 338 case SD_IPV4ACD_EVENT_CONFLICT:
e3dca008
TG
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) {
be19c5b5 342 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT);
e3dca008
TG
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
362error:
be19c5b5 363 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
5c1d3fc9 364}