]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-ipv4ll.c
util: split out escaping code into escape.[ch]
[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
21#include <stdlib.h>
22#include <errno.h>
23#include <string.h>
24#include <stdio.h>
25#include <arpa/inet.h>
26
e3dca008 27#include "event-util.h"
129dc1b4 28#include "in-addr-util.h"
5c1d3fc9 29#include "list.h"
3df3e884 30#include "random-util.h"
e3dca008
TG
31#include "refcnt.h"
32#include "siphash24.h"
33#include "sparse-endian.h"
34#include "util.h"
5c1d3fc9 35
e3dca008 36#include "sd-ipv4acd.h"
5c1d3fc9
UTL
37#include "sd-ipv4ll.h"
38
5c1d3fc9
UTL
39#define IPV4LL_NETWORK 0xA9FE0000L
40#define IPV4LL_NETMASK 0xFFFF0000L
41
b45e4eb6
TG
42#define IPV4LL_DONT_DESTROY(ll) \
43 _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
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;
5c1d3fc9
UTL
55 sd_ipv4ll_cb_t cb;
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
88DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
89#define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp)
90
e3dca008
TG
91static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
92
b45e4eb6
TG
93int sd_ipv4ll_new(sd_ipv4ll **ret) {
94 _cleanup_ipv4ll_unref_ sd_ipv4ll *ll = NULL;
e3dca008 95 int r;
b45e4eb6
TG
96
97 assert_return(ret, -EINVAL);
98
99 ll = new0(sd_ipv4ll, 1);
100 if (!ll)
101 return -ENOMEM;
102
e3dca008
TG
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
b45e4eb6 111 ll->n_ref = 1;
b45e4eb6
TG
112
113 *ret = ll;
114 ll = NULL;
115
116 return 0;
117}
118
b45e4eb6 119int sd_ipv4ll_stop(sd_ipv4ll *ll) {
94a355a1 120 int r;
5c1d3fc9 121
e3dca008 122 assert_return(ll, -EINVAL);
94a355a1 123
e3dca008 124 r = sd_ipv4acd_stop(ll->acd);
94a355a1
TG
125 if (r < 0)
126 return r;
127
94a355a1 128 return 0;
5c1d3fc9
UTL
129}
130
e3dca008
TG
131int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
132 assert_return(ll, -EINVAL);
5c1d3fc9 133
e3dca008 134 return sd_ipv4acd_set_index(ll->acd, interface_index);
5c1d3fc9
UTL
135}
136
e3dca008 137#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
5c1d3fc9 138
e3dca008 139int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
b26f7e8e
TG
140 int r;
141
e3dca008 142 assert_return(ll, -EINVAL);
b26f7e8e 143
e3dca008
TG
144 if (!ll->random_data) {
145 uint8_t seed[8];
b26f7e8e 146
e3dca008
TG
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);
5c1d3fc9 150
e3dca008 151 assert_cc(sizeof(unsigned) <= 8);
5c1d3fc9 152
e3dca008 153 r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed);
028e0b20 154 if (r < 0)
e3dca008 155 return r;
5c1d3fc9
UTL
156 }
157
e3dca008 158 return sd_ipv4acd_set_mac(ll->acd, addr);
5c1d3fc9
UTL
159}
160
161int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
162 assert_return(ll, -EINVAL);
163
e3dca008 164 return sd_ipv4acd_detach_event(ll->acd);
5c1d3fc9
UTL
165}
166
167int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
168 int r;
169
170 assert_return(ll, -EINVAL);
5c1d3fc9 171
e3dca008
TG
172 r = sd_ipv4acd_attach_event(ll->acd, event, priority);
173 if (r < 0)
174 return r;
5c1d3fc9
UTL
175
176 return 0;
177}
178
179int 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
188int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
189 assert_return(ll, -EINVAL);
190 assert_return(address, -EINVAL);
191
ece174c5 192 if (ll->claimed_address == 0)
5c1d3fc9 193 return -ENOENT;
5c1d3fc9
UTL
194
195 address->s_addr = ll->claimed_address;
e3dca008 196
5c1d3fc9
UTL
197 return 0;
198}
199
e3dca008
TG
200int 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;
b5db00e5
UTL
203 int r;
204
205 assert_return(ll, -EINVAL);
d9bf4f8c 206
e3dca008
TG
207 random_data = new0(struct random_data, 1);
208 if (!random_data)
209 return -ENOMEM;
b5db00e5 210
e3dca008
TG
211 random_data_state = new0(char, 128);
212 if (!random_data_state)
213 return -ENOMEM;
b5db00e5 214
e3dca008
TG
215 r = initstate_r(seed, random_data_state, 128, random_data);
216 if (r < 0)
217 return r;
b5db00e5 218
e3dca008
TG
219 free(ll->random_data);
220 ll->random_data = random_data;
221 random_data = NULL;
b5db00e5 222
e3dca008
TG
223 free(ll->random_data_state);
224 ll->random_data_state = random_data_state;
225 random_data_state = NULL;
b5db00e5 226
e3dca008 227 return 0;
b5db00e5
UTL
228}
229
aba496a5 230bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
75677581 231 assert_return(ll, false);
aba496a5 232
e3dca008 233 return sd_ipv4acd_is_running(ll->acd);
aba496a5
UTL
234}
235
129dc1b4
TG
236static 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
253int 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
e3dca008
TG
269static int ipv4ll_pick_address(sd_ipv4ll *ll) {
270 struct in_addr in_addr;
271 be32_t addr;
5c1d3fc9 272 int r;
e3dca008 273 int32_t random;
5c1d3fc9 274
e3dca008
TG
275 assert(ll);
276 assert(ll->random_data);
4d978a46 277
e3dca008
TG
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 ||
e3dca008
TG
284 (ntohl(addr) & 0x0000FF00) == 0x0000 ||
285 (ntohl(addr) & 0x0000FF00) == 0xFF00);
5c1d3fc9 286
e3dca008 287 in_addr.s_addr = addr;
5c1d3fc9 288
129dc1b4 289 r = sd_ipv4ll_set_address(ll, &in_addr);
e3dca008
TG
290 if (r < 0)
291 return r;
b5db00e5 292
e3dca008
TG
293 return 0;
294}
295
296int sd_ipv4ll_start(sd_ipv4ll *ll) {
297 int r;
298
299 assert_return(ll, -EINVAL);
300 assert_return(ll->random_data, -EINVAL);
b5db00e5
UTL
301
302 if (ll->address == 0) {
e3dca008 303 r = ipv4ll_pick_address(ll);
b5db00e5 304 if (r < 0)
e3dca008 305 return r;
b5db00e5 306 }
5c1d3fc9 307
e3dca008 308 r = sd_ipv4acd_start(ll->acd);
996d1697 309 if (r < 0)
e3dca008 310 return r;
996d1697 311
e3dca008
TG
312 return 0;
313}
996d1697 314
e3dca008
TG
315static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
316 assert(ll);
5c1d3fc9 317
e3dca008
TG
318 if (ll->cb)
319 ll->cb(ll, event, ll->userdata);
320}
5c1d3fc9 321
e3dca008
TG
322void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
323 sd_ipv4ll *ll = userdata;
324 IPV4LL_DONT_DESTROY(ll);
325 int r;
5c1d3fc9 326
e3dca008
TG
327 assert(acd);
328 assert(ll);
9021bb9f 329
e3dca008 330 switch (event) {
2237aa02 331 case SD_IPV4ACD_EVENT_STOP:
be19c5b5 332 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
5c1d3fc9 333
e3dca008
TG
334 ll->claimed_address = 0;
335
336 break;
2237aa02 337 case SD_IPV4ACD_EVENT_BIND:
e3dca008 338 ll->claimed_address = ll->address;
be19c5b5 339 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
e3dca008
TG
340
341 break;
2237aa02 342 case SD_IPV4ACD_EVENT_CONFLICT:
e3dca008
TG
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) {
be19c5b5 346 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT);
e3dca008
TG
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
366error:
be19c5b5 367 ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
5c1d3fc9 368}