]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-address.c
networkd: constify more things
[thirdparty/systemd.git] / src / network / networkd-address.c
CommitLineData
f579559b
TG
1/***
2 This file is part of systemd.
3
4 Copyright 2013 Tom Gundersen <teg@jklm.no>
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
20#include <net/if.h>
21
b5efdb8a 22#include "alloc-util.h"
f579559b 23#include "conf-parser.h"
12c2884c 24#include "firewall-util.h"
fc2f9534 25#include "netlink-util.h"
6bedfcbb 26#include "networkd-address.h"
07630cea 27#include "networkd.h"
6bedfcbb 28#include "parse-util.h"
3ac8e543 29#include "set.h"
d31645ad 30#include "socket-util.h"
07630cea 31#include "string-util.h"
3ac8e543
TG
32#include "utf8.h"
33#include "util.h"
f579559b 34
1b566071 35#define ADDRESSES_PER_LINK_MAX 2048U
8c34b963
LP
36#define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U
37
f0213e37
TG
38int address_new(Address **ret) {
39 _cleanup_address_free_ Address *address = NULL;
40
41 address = new0(Address, 1);
42 if (!address)
43 return -ENOMEM;
aba496a5
UTL
44
45 address->family = AF_UNSPEC;
46 address->scope = RT_SCOPE_UNIVERSE;
47 address->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME;
48 address->cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME;
f0213e37
TG
49
50 *ret = address;
51 address = NULL;
52
53 return 0;
aba496a5
UTL
54}
55
f048a16b 56int address_new_static(Network *network, unsigned section, Address **ret) {
f579559b 57 _cleanup_address_free_ Address *address = NULL;
f0213e37 58 int r;
f579559b 59
8c34b963
LP
60 assert(network);
61 assert(ret);
62
6ae115c1 63 if (section) {
16aa63a0 64 address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section));
6ae115c1
TG
65 if (address) {
66 *ret = address;
67 address = NULL;
68
69 return 0;
70 }
71 }
72
8c34b963
LP
73 if (network->n_static_addresses >= STATIC_ADDRESSES_PER_NETWORK_MAX)
74 return -E2BIG;
75
f0213e37
TG
76 r = address_new(&address);
77 if (r < 0)
78 return r;
801bd9e8 79
6ae115c1
TG
80 if (section) {
81 address->section = section;
8c34b963 82 hashmap_put(network->addresses_by_section, UINT_TO_PTR(address->section), address);
6ae115c1
TG
83 }
84
5215524d
SS
85 address->network = network;
86 LIST_APPEND(addresses, network->static_addresses, address);
8c34b963 87 network->n_static_addresses++;
5215524d 88
f579559b
TG
89 *ret = address;
90 address = NULL;
91
92 return 0;
93}
94
95void address_free(Address *address) {
96 if (!address)
97 return;
98
f048a16b 99 if (address->network) {
3d3d4255 100 LIST_REMOVE(addresses, address->network->static_addresses, address);
8c34b963
LP
101 assert(address->network->n_static_addresses > 0);
102 address->network->n_static_addresses--;
f579559b 103
f048a16b 104 if (address->section)
8c34b963 105 hashmap_remove(address->network->addresses_by_section, UINT_TO_PTR(address->section));
f048a16b 106 }
6ae115c1 107
adda1ed9 108 if (address->link) {
cf1d700d 109 set_remove(address->link->addresses, address);
adda1ed9 110 set_remove(address->link->addresses_foreign, address);
f150100a
SS
111
112 if (in_addr_equal(AF_INET6, &address->in_addr, (const union in_addr_union *) &address->link->ipv6ll_address))
113 memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr));
adda1ed9 114 }
cf1d700d 115
f579559b
TG
116 free(address);
117}
118
3ac8e543
TG
119static void address_hash_func(const void *b, struct siphash *state) {
120 const Address *a = b;
121
122 assert(a);
123
124 siphash24_compress(&a->family, sizeof(a->family), state);
125
126 switch (a->family) {
127 case AF_INET:
128 siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
129
130 /* peer prefix */
131 if (a->prefixlen != 0) {
132 uint32_t prefix;
133
134 if (a->in_addr_peer.in.s_addr != 0)
135 prefix = be32toh(a->in_addr_peer.in.s_addr) >> (32 - a->prefixlen);
136 else
137 prefix = be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen);
138
139 siphash24_compress(&prefix, sizeof(prefix), state);
140 }
141
142 /* fallthrough */
143 case AF_INET6:
144 /* local address */
145 siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state);
146
147 break;
148 default:
149 /* treat any other address family as AF_UNSPEC */
150 break;
151 }
152}
153
154static int address_compare_func(const void *c1, const void *c2) {
155 const Address *a1 = c1, *a2 = c2;
156
157 if (a1->family < a2->family)
158 return -1;
159 if (a1->family > a2->family)
160 return 1;
161
162 switch (a1->family) {
163 /* use the same notion of equality as the kernel does */
164 case AF_INET:
165 if (a1->prefixlen < a2->prefixlen)
166 return -1;
167 if (a1->prefixlen > a2->prefixlen)
168 return 1;
169
170 /* compare the peer prefixes */
171 if (a1->prefixlen != 0) {
172 /* make sure we don't try to shift by 32.
173 * See ISO/IEC 9899:TC3 ยง 6.5.7.3. */
174 uint32_t b1, b2;
175
176 if (a1->in_addr_peer.in.s_addr != 0)
177 b1 = be32toh(a1->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen);
178 else
179 b1 = be32toh(a1->in_addr.in.s_addr) >> (32 - a1->prefixlen);
180
181 if (a2->in_addr_peer.in.s_addr != 0)
182 b2 = be32toh(a2->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen);
183 else
184 b2 = be32toh(a2->in_addr.in.s_addr) >> (32 - a1->prefixlen);
185
186 if (b1 < b2)
187 return -1;
188 if (b1 > b2)
189 return 1;
190 }
191
192 /* fall-through */
193 case AF_INET6:
194 return memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family));
195 default:
196 /* treat any other address family as AF_UNSPEC */
197 return 0;
198 }
199}
200
201static const struct hash_ops address_hash_ops = {
202 .hash = address_hash_func,
203 .compare = address_compare_func
204};
205
206bool address_equal(Address *a1, Address *a2) {
207 if (a1 == a2)
208 return true;
209
210 if (!a1 || !a2)
211 return false;
212
213 return address_compare_func(a1, a2) == 0;
214}
215
91b5f997
TG
216static int address_establish(Address *address, Link *link) {
217 bool masq;
218 int r;
219
220 assert(address);
221 assert(link);
222
223 masq = link->network &&
fcf50cff
TG
224 link->network->ip_masquerade &&
225 address->family == AF_INET &&
226 address->scope < RT_SCOPE_LINK;
91b5f997
TG
227
228 /* Add firewall entry if this is requested */
229 if (address->ip_masquerade_done != masq) {
230 union in_addr_union masked = address->in_addr;
231 in_addr_mask(address->family, &masked, address->prefixlen);
232
233 r = fw_add_masquerade(masq, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0);
234 if (r < 0)
235 log_link_warning_errno(link, r, "Could not enable IP masquerading: %m");
236
237 address->ip_masquerade_done = masq;
238 }
239
240 return 0;
241}
242
adda1ed9
TG
243static int address_add_internal(Link *link, Set **addresses,
244 int family,
245 const union in_addr_union *in_addr,
246 unsigned char prefixlen,
247 Address **ret) {
054f0db4 248 _cleanup_address_free_ Address *address = NULL;
cf1d700d
TG
249 int r;
250
251 assert(link);
adda1ed9 252 assert(addresses);
054f0db4 253 assert(in_addr);
054f0db4
TG
254
255 r = address_new(&address);
256 if (r < 0)
257 return r;
258
259 address->family = family;
260 address->in_addr = *in_addr;
261 address->prefixlen = prefixlen;
63bbe5c7
TG
262 /* Consider address tentative until we get the real flags from the kernel */
263 address->flags = IFA_F_TENTATIVE;
cf1d700d 264
adda1ed9 265 r = set_ensure_allocated(addresses, &address_hash_ops);
cf1d700d
TG
266 if (r < 0)
267 return r;
268
adda1ed9 269 r = set_put(*addresses, address);
cf1d700d
TG
270 if (r < 0)
271 return r;
272
273 address->link = link;
274
adda1ed9
TG
275 if (ret)
276 *ret = address;
277
054f0db4
TG
278 address = NULL;
279
cf1d700d
TG
280 return 0;
281}
282
adda1ed9
TG
283int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
284 return address_add_internal(link, &link->addresses_foreign, family, in_addr, prefixlen, ret);
285}
286
c4a03a56 287int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) {
cab974b0 288 Address *address;
e7780c8d
TG
289 int r;
290
cab974b0
TG
291 r = address_get(link, family, in_addr, prefixlen, &address);
292 if (r == -ENOENT) {
293 /* Address does not exist, create a new one */
294 r = address_add_internal(link, &link->addresses, family, in_addr, prefixlen, &address);
295 if (r < 0)
296 return r;
297 } else if (r == 0) {
298 /* Take over a foreign address */
299 r = set_ensure_allocated(&link->addresses, &address_hash_ops);
300 if (r < 0)
301 return r;
302
303 r = set_put(link->addresses, address);
304 if (r < 0)
305 return r;
306
307 set_remove(link->addresses_foreign, address);
308 } else if (r == 1) {
309 /* Already exists, do nothing */
310 ;
311 } else
e7780c8d
TG
312 return r;
313
cab974b0
TG
314 if (ret)
315 *ret = address;
e7780c8d
TG
316
317 return 0;
adda1ed9
TG
318}
319
fcf50cff 320static int address_release(Address *address) {
5a8bcb67
LP
321 int r;
322
323 assert(address);
fcf50cff 324 assert(address->link);
5a8bcb67 325
91b5f997
TG
326 /* Remove masquerading firewall entry if it was added */
327 if (address->ip_masquerade_done) {
5a8bcb67
LP
328 union in_addr_union masked = address->in_addr;
329 in_addr_mask(address->family, &masked, address->prefixlen);
330
91b5f997 331 r = fw_add_masquerade(false, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0);
5a8bcb67 332 if (r < 0)
fcf50cff 333 log_link_warning_errno(address->link, r, "Failed to disable IP masquerading: %m");
5a8bcb67 334
91b5f997 335 address->ip_masquerade_done = false;
5a8bcb67
LP
336 }
337
338 return 0;
339}
340
889b550f
LP
341int address_update(
342 Address *address,
343 unsigned char flags,
344 unsigned char scope,
345 const struct ifa_cacheinfo *cinfo) {
346
36c32f61 347 bool ready;
e7ab854c 348 int r;
36c32f61
TG
349
350 assert(address);
351 assert(cinfo);
7209086d
SS
352 assert_return(address->link, 1);
353
354 if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
355 return 1;
36c32f61
TG
356
357 ready = address_is_ready(address);
358
359 address->flags = flags;
360 address->scope = scope;
361 address->cinfo = *cinfo;
362
7209086d
SS
363 link_update_operstate(address->link);
364
365 if (!ready && address_is_ready(address)) {
366 link_check_ready(address->link);
367
368 if (address->family == AF_INET6 &&
369 in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 &&
370 in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) {
371
372 r = link_ipv6ll_gained(address->link, &address->in_addr.in6);
373 if (r < 0)
374 return r;
e7ab854c 375 }
a3a019e1 376 }
36c32f61
TG
377
378 return 0;
379}
380
91b5f997 381int address_drop(Address *address) {
8012cd39
TG
382 Link *link;
383 bool ready;
384
5a8bcb67 385 assert(address);
91b5f997 386
8012cd39
TG
387 ready = address_is_ready(address);
388 link = address->link;
389
fcf50cff 390 address_release(address);
91b5f997
TG
391 address_free(address);
392
84de38c5
TG
393 link_update_operstate(link);
394
8012cd39
TG
395 if (link && !ready)
396 link_check_ready(link);
397
91b5f997
TG
398 return 0;
399}
400
1b566071
LP
401int address_get(Link *link,
402 int family,
403 const union in_addr_union *in_addr,
404 unsigned char prefixlen,
405 Address **ret) {
406
407 Address address, *existing;
91b5f997 408
5a8bcb67 409 assert(link);
91b5f997 410 assert(in_addr);
5a8bcb67 411
1b566071
LP
412 address = (Address) {
413 .family = family,
414 .in_addr = *in_addr,
415 .prefixlen = prefixlen,
416 };
5a8bcb67 417
91b5f997 418 existing = set_get(link->addresses, &address);
cab974b0 419 if (existing) {
1b566071
LP
420 if (ret)
421 *ret = existing;
cab974b0 422 return 1;
adda1ed9 423 }
5a8bcb67 424
1b566071
LP
425 existing = set_get(link->addresses_foreign, &address);
426 if (existing) {
427 if (ret)
428 *ret = existing;
429 return 0;
430 }
5a8bcb67 431
1b566071 432 return -ENOENT;
5a8bcb67
LP
433}
434
483d099e
ZJS
435int address_remove(
436 Address *address,
437 Link *link,
438 sd_netlink_message_handler_t callback) {
439
4afd3348 440 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
407fe036
TG
441 int r;
442
443 assert(address);
444 assert(address->family == AF_INET || address->family == AF_INET6);
445 assert(link);
446 assert(link->ifindex > 0);
447 assert(link->manager);
448 assert(link->manager->rtnl);
449
151b9b96
LP
450 r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR,
451 link->ifindex, address->family);
eb56eb9b
MS
452 if (r < 0)
453 return log_error_errno(r, "Could not allocate RTM_DELADDR message: %m");
407fe036 454
5a723174 455 r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
eb56eb9b
MS
456 if (r < 0)
457 return log_error_errno(r, "Could not set prefixlen: %m");
5a723174 458
407fe036 459 if (address->family == AF_INET)
1c4baffc 460 r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
407fe036 461 else if (address->family == AF_INET6)
1c4baffc 462 r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
eb56eb9b
MS
463 if (r < 0)
464 return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
407fe036 465
1c4baffc 466 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
eb56eb9b
MS
467 if (r < 0)
468 return log_error_errno(r, "Could not send rtnetlink message: %m");
407fe036 469
563c69c6
TG
470 link_ref(link);
471
407fe036
TG
472 return 0;
473}
474
11bf3cce
LP
475static int address_acquire(Link *link, Address *original, Address **ret) {
476 union in_addr_union in_addr = {};
477 struct in_addr broadcast = {};
0099bc15 478 _cleanup_address_free_ Address *na = NULL;
11bf3cce
LP
479 int r;
480
481 assert(link);
482 assert(original);
483 assert(ret);
484
485 /* Something useful was configured? just use it */
af93291c 486 if (in_addr_is_null(original->family, &original->in_addr) <= 0)
11bf3cce
LP
487 return 0;
488
489 /* The address is configured to be 0.0.0.0 or [::] by the user?
490 * Then let's acquire something more useful from the pool. */
491 r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr);
6a7a4e4d
LP
492 if (r < 0)
493 return log_link_error_errno(link, r, "Failed to acquire address from pool: %m");
11bf3cce 494 if (r == 0) {
79008bdd 495 log_link_error(link, "Couldn't find free address for interface, all taken.");
11bf3cce
LP
496 return -EBUSY;
497 }
498
499 if (original->family == AF_INET) {
d076c6f9 500 /* Pick first address in range for ourselves ... */
11bf3cce
LP
501 in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1);
502
503 /* .. and use last as broadcast address */
504 broadcast.s_addr = in_addr.in.s_addr | htobe32(0xFFFFFFFFUL >> original->prefixlen);
505 } else if (original->family == AF_INET6)
506 in_addr.in6.s6_addr[15] |= 1;
507
f0213e37 508 r = address_new(&na);
11bf3cce
LP
509 if (r < 0)
510 return r;
511
512 na->family = original->family;
513 na->prefixlen = original->prefixlen;
514 na->scope = original->scope;
515 na->cinfo = original->cinfo;
516
517 if (original->label) {
518 na->label = strdup(original->label);
0099bc15 519 if (!na->label)
11bf3cce 520 return -ENOMEM;
11bf3cce
LP
521 }
522
523 na->broadcast = broadcast;
524 na->in_addr = in_addr;
525
526 LIST_PREPEND(addresses, link->pool_addresses, na);
527
528 *ret = na;
0099bc15
SS
529 na = NULL;
530
11bf3cce
LP
531 return 0;
532}
533
1b566071
LP
534int address_configure(
535 Address *address,
536 Link *link,
537 sd_netlink_message_handler_t callback,
538 bool update) {
539
4afd3348 540 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
f579559b
TG
541 int r;
542
c166a070
TG
543 assert(address);
544 assert(address->family == AF_INET || address->family == AF_INET6);
545 assert(link);
546 assert(link->ifindex > 0);
f882c247 547 assert(link->manager);
c166a070 548 assert(link->manager->rtnl);
f882c247 549
1b566071
LP
550 /* If this is a new address, then refuse adding more than the limit */
551 if (address_get(link, address->family, &address->in_addr, address->prefixlen, NULL) <= 0 &&
552 set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX)
553 return -E2BIG;
554
11bf3cce
LP
555 r = address_acquire(link, address, &address);
556 if (r < 0)
557 return r;
558
66669078
TG
559 if (update)
560 r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
561 link->ifindex, address->family);
562 else
563 r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
564 link->ifindex, address->family);
eb56eb9b
MS
565 if (r < 0)
566 return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m");
f579559b 567
5a723174 568 r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
eb56eb9b
MS
569 if (r < 0)
570 return log_error_errno(r, "Could not set prefixlen: %m");
5a723174 571
851c9f82
PF
572 address->flags |= IFA_F_PERMANENT;
573
574 r = sd_rtnl_message_addr_set_flags(req, (address->flags & 0xff));
eb56eb9b
MS
575 if (r < 0)
576 return log_error_errno(r, "Could not set flags: %m");
5a723174 577
851c9f82 578 if (address->flags & ~0xff) {
1c4baffc 579 r = sd_netlink_message_append_u32(req, IFA_FLAGS, address->flags);
851c9f82
PF
580 if (r < 0)
581 return log_error_errno(r, "Could not set extended flags: %m");
582 }
583
5c1d3fc9 584 r = sd_rtnl_message_addr_set_scope(req, address->scope);
eb56eb9b
MS
585 if (r < 0)
586 return log_error_errno(r, "Could not set scope: %m");
5a723174 587
0a0dc69b 588 if (address->family == AF_INET)
1c4baffc 589 r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
0a0dc69b 590 else if (address->family == AF_INET6)
1c4baffc 591 r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
eb56eb9b
MS
592 if (r < 0)
593 return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m");
f579559b 594
af93291c 595 if (!in_addr_is_null(address->family, &address->in_addr_peer)) {
c081882f 596 if (address->family == AF_INET)
1c4baffc 597 r = sd_netlink_message_append_in_addr(req, IFA_ADDRESS, &address->in_addr_peer.in);
c081882f 598 else if (address->family == AF_INET6)
1c4baffc 599 r = sd_netlink_message_append_in6_addr(req, IFA_ADDRESS, &address->in_addr_peer.in6);
eb56eb9b
MS
600 if (r < 0)
601 return log_error_errno(r, "Could not append IFA_ADDRESS attribute: %m");
c081882f
SS
602 } else {
603 if (address->family == AF_INET) {
1c4baffc 604 r = sd_netlink_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
eb56eb9b
MS
605 if (r < 0)
606 return log_error_errno(r, "Could not append IFA_BROADCAST attribute: %m");
c081882f 607 }
f579559b
TG
608 }
609
610 if (address->label) {
1c4baffc 611 r = sd_netlink_message_append_string(req, IFA_LABEL, address->label);
eb56eb9b
MS
612 if (r < 0)
613 return log_error_errno(r, "Could not append IFA_LABEL attribute: %m");
f579559b
TG
614 }
615
1c4baffc 616 r = sd_netlink_message_append_cache_info(req, IFA_CACHEINFO,
68ceb9df 617 &address->cinfo);
eb56eb9b
MS
618 if (r < 0)
619 return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m");
68ceb9df 620
fcf50cff 621 r = address_establish(address, link);
eb56eb9b 622 if (r < 0)
fcf50cff
TG
623 return r;
624
625 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
626 if (r < 0) {
627 address_release(address);
eb56eb9b 628 return log_error_errno(r, "Could not send rtnetlink message: %m");
fcf50cff 629 }
f579559b 630
563c69c6
TG
631 link_ref(link);
632
adda1ed9
TG
633 r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL);
634 if (r < 0) {
635 address_release(address);
636 return log_error_errno(r, "Could not add address: %m");
637 }
638
f579559b
TG
639 return 0;
640}
641
44e7b949
LP
642int config_parse_broadcast(
643 const char *unit,
eb0ea358
TG
644 const char *filename,
645 unsigned line,
646 const char *section,
647 unsigned section_line,
648 const char *lvalue,
649 int ltype,
650 const char *rvalue,
651 void *data,
652 void *userdata) {
44e7b949 653
eb0ea358
TG
654 Network *network = userdata;
655 _cleanup_address_free_ Address *n = NULL;
eb0ea358
TG
656 int r;
657
658 assert(filename);
659 assert(section);
660 assert(lvalue);
661 assert(rvalue);
662 assert(data);
663
664 r = address_new_static(network, section_line, &n);
665 if (r < 0)
666 return r;
667
482e2ac1 668 if (n->family == AF_INET6) {
12ca818f 669 log_syntax(unit, LOG_ERR, filename, line, 0, "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue);
482e2ac1
TG
670 return 0;
671 }
672
44e7b949 673 r = in_addr_from_string(AF_INET, rvalue, (union in_addr_union*) &n->broadcast);
eb0ea358 674 if (r < 0) {
12ca818f 675 log_syntax(unit, LOG_ERR, filename, line, r, "Broadcast is invalid, ignoring assignment: %s", rvalue);
eb0ea358
TG
676 return 0;
677 }
678
44e7b949 679 n->family = AF_INET;
eb0ea358
TG
680 n = NULL;
681
682 return 0;
683}
684
f579559b
TG
685int config_parse_address(const char *unit,
686 const char *filename,
687 unsigned line,
688 const char *section,
71a61510 689 unsigned section_line,
f579559b
TG
690 const char *lvalue,
691 int ltype,
692 const char *rvalue,
693 void *data,
694 void *userdata) {
44e7b949 695
6ae115c1 696 Network *network = userdata;
f579559b 697 _cleanup_address_free_ Address *n = NULL;
44e7b949
LP
698 const char *address, *e;
699 union in_addr_union buffer;
700 int r, f;
f579559b
TG
701
702 assert(filename);
6ae115c1 703 assert(section);
f579559b
TG
704 assert(lvalue);
705 assert(rvalue);
706 assert(data);
707
92fe133a
TG
708 if (streq(section, "Network")) {
709 /* we are not in an Address section, so treat
710 * this as the special '0' section */
711 section_line = 0;
712 }
713
f048a16b 714 r = address_new_static(network, section_line, &n);
f579559b
TG
715 if (r < 0)
716 return r;
717
718 /* Address=address/prefixlen */
719
720 /* prefixlen */
721 e = strchr(rvalue, '/');
722 if (e) {
723 unsigned i;
12ca818f 724
f579559b
TG
725 r = safe_atou(e + 1, &i);
726 if (r < 0) {
12ca818f 727 log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length is invalid, ignoring assignment: %s", e + 1);
f579559b
TG
728 return 0;
729 }
730
731 n->prefixlen = (unsigned char) i;
8cd11a0f 732
44e7b949
LP
733 address = strndupa(rvalue, e - rvalue);
734 } else
735 address = rvalue;
f579559b 736
44e7b949 737 r = in_addr_from_string_auto(address, &f, &buffer);
f579559b 738 if (r < 0) {
12ca818f 739 log_syntax(unit, LOG_ERR, filename, line, r, "Address is invalid, ignoring assignment: %s", address);
f579559b
TG
740 return 0;
741 }
742
a2a85a22
TG
743 if (!e && f == AF_INET) {
744 r = in_addr_default_prefixlen(&buffer.in, &n->prefixlen);
745 if (r < 0) {
12ca818f 746 log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length not specified, and a default one can not be deduced for '%s', ignoring assignment", address);
a2a85a22
TG
747 return 0;
748 }
749 }
750
44e7b949 751 if (n->family != AF_UNSPEC && f != n->family) {
12ca818f 752 log_syntax(unit, LOG_ERR, filename, line, 0, "Address is incompatible, ignoring assignment: %s", address);
44e7b949
LP
753 return 0;
754 }
755
756 n->family = f;
757
758 if (streq(lvalue, "Address"))
759 n->in_addr = buffer;
760 else
761 n->in_addr_peer = buffer;
762
763 if (n->family == AF_INET && n->broadcast.s_addr == 0)
764 n->broadcast.s_addr = n->in_addr.in.s_addr | htonl(0xfffffffflu >> n->prefixlen);
eb0ea358 765
f579559b
TG
766 n = NULL;
767
768 return 0;
769}
6ae115c1 770
d31645ad
LP
771int config_parse_label(
772 const char *unit,
6ae115c1
TG
773 const char *filename,
774 unsigned line,
775 const char *section,
776 unsigned section_line,
777 const char *lvalue,
778 int ltype,
779 const char *rvalue,
780 void *data,
781 void *userdata) {
d31645ad 782
6ae115c1 783 _cleanup_address_free_ Address *n = NULL;
d31645ad 784 Network *network = userdata;
6ae115c1
TG
785 int r;
786
787 assert(filename);
788 assert(section);
789 assert(lvalue);
790 assert(rvalue);
791 assert(data);
792
f048a16b 793 r = address_new_static(network, section_line, &n);
6ae115c1
TG
794 if (r < 0)
795 return r;
796
d31645ad
LP
797 if (!ifname_valid(rvalue)) {
798 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not valid or too long, ignoring assignment: %s", rvalue);
6ae115c1
TG
799 return 0;
800 }
801
d31645ad
LP
802 r = free_and_strdup(&n->label, rvalue);
803 if (r < 0)
804 return log_oom();
6ae115c1
TG
805
806 n = NULL;
807
808 return 0;
809}
ce6c77eb 810
b5834a0b
SS
811int config_parse_lifetime(const char *unit,
812 const char *filename,
813 unsigned line,
814 const char *section,
815 unsigned section_line,
816 const char *lvalue,
817 int ltype,
818 const char *rvalue,
819 void *data,
820 void *userdata) {
821 Network *network = userdata;
822 _cleanup_address_free_ Address *n = NULL;
823 unsigned k;
824 int r;
825
826 assert(filename);
827 assert(section);
828 assert(lvalue);
829 assert(rvalue);
830 assert(data);
831
832 r = address_new_static(network, section_line, &n);
833 if (r < 0)
834 return r;
835
836 if (STR_IN_SET(rvalue, "forever", "infinity")) {
837 n->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME;
838 n = NULL;
839
840 return 0;
841 }
842
843 r = safe_atou(rvalue, &k);
844 if (r < 0) {
845 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PreferredLifetime, ignoring: %s", rvalue);
846 return 0;
847 }
848
849 if (k != 0)
850 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid PreferredLifetime value, ignoring: %d", k);
851 else {
852 n->cinfo.ifa_prefered = k;
853 n = NULL;
854 }
855
856 return 0;
857}
858
ce6c77eb
TG
859bool address_is_ready(const Address *a) {
860 assert(a);
861
63bbe5c7 862 return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED));
ce6c77eb 863}