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