]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-address.c
networkd: split out networkd-link.h
[thirdparty/systemd.git] / src / network / networkd-address.c
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
24 #include "networkd.h"
25 #include "networkd-link.h"
26
27 #include "utf8.h"
28 #include "util.h"
29 #include "conf-parser.h"
30 #include "network-internal.h"
31
32 static void address_init(Address *address) {
33 assert(address);
34
35 address->family = AF_UNSPEC;
36 address->scope = RT_SCOPE_UNIVERSE;
37 address->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME;
38 address->cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME;
39 }
40
41 int address_new_static(Network *network, unsigned section, Address **ret) {
42 _cleanup_address_free_ Address *address = NULL;
43
44 if (section) {
45 address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section));
46 if (address) {
47 *ret = address;
48 address = NULL;
49
50 return 0;
51 }
52 }
53
54 address = new0(Address, 1);
55 if (!address)
56 return -ENOMEM;
57
58 address_init(address);
59
60 address->network = network;
61
62 LIST_PREPEND(addresses, network->static_addresses, address);
63
64 if (section) {
65 address->section = section;
66 hashmap_put(network->addresses_by_section,
67 UINT_TO_PTR(address->section), address);
68 }
69
70 *ret = address;
71 address = NULL;
72
73 return 0;
74 }
75
76 int address_new_dynamic(Address **ret) {
77 _cleanup_address_free_ Address *address = NULL;
78
79 address = new0(Address, 1);
80 if (!address)
81 return -ENOMEM;
82
83 address_init(address);
84
85 *ret = address;
86 address = NULL;
87
88 return 0;
89 }
90
91 void address_free(Address *address) {
92 if (!address)
93 return;
94
95 if (address->network) {
96 LIST_REMOVE(addresses, address->network->static_addresses, address);
97
98 if (address->section)
99 hashmap_remove(address->network->addresses_by_section,
100 UINT_TO_PTR(address->section));
101 }
102
103 free(address);
104 }
105
106 int address_drop(Address *address, Link *link,
107 sd_rtnl_message_handler_t callback) {
108 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
109 int r;
110
111 assert(address);
112 assert(address->family == AF_INET || address->family == AF_INET6);
113 assert(link);
114 assert(link->ifindex > 0);
115 assert(link->manager);
116 assert(link->manager->rtnl);
117
118 r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR,
119 link->ifindex, address->family);
120 if (r < 0) {
121 log_error("Could not allocate RTM_DELADDR message: %s",
122 strerror(-r));
123 return r;
124 }
125
126 r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
127 if (r < 0) {
128 log_error("Could not set prefixlen: %s", strerror(-r));
129 return r;
130 }
131
132 if (address->family == AF_INET)
133 r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
134 else if (address->family == AF_INET6)
135 r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
136 if (r < 0) {
137 log_error("Could not append IFA_LOCAL attribute: %s",
138 strerror(-r));
139 return r;
140 }
141
142 r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
143 if (r < 0) {
144 log_error("Could not send rtnetlink message: %s", strerror(-r));
145 return r;
146 }
147
148 link_ref(link);
149
150 return 0;
151 }
152
153 int address_update(Address *address, Link *link,
154 sd_rtnl_message_handler_t callback) {
155 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
156 int r;
157
158 assert(address);
159 assert(address->family == AF_INET || address->family == AF_INET6);
160 assert(link->ifindex > 0);
161 assert(link->manager);
162 assert(link->manager->rtnl);
163
164 r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
165 link->ifindex, address->family);
166 if (r < 0) {
167 log_error("Could not allocate RTM_NEWADDR message: %s",
168 strerror(-r));
169 return r;
170 }
171
172 r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
173 if (r < 0) {
174 log_error("Could not set prefixlen: %s", strerror(-r));
175 return r;
176 }
177
178 r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
179 if (r < 0) {
180 log_error("Could not set flags: %s", strerror(-r));
181 return r;
182 }
183
184 r = sd_rtnl_message_addr_set_scope(req, address->scope);
185 if (r < 0) {
186 log_error("Could not set scope: %s", strerror(-r));
187 return r;
188 }
189
190 if (address->family == AF_INET)
191 r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
192 else if (address->family == AF_INET6)
193 r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
194 if (r < 0) {
195 log_error("Could not append IFA_LOCAL attribute: %s",
196 strerror(-r));
197 return r;
198 }
199
200 if (address->family == AF_INET) {
201 r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
202 if (r < 0) {
203 log_error("Could not append IFA_BROADCAST attribute: %s",
204 strerror(-r));
205 return r;
206 }
207 }
208
209 if (address->label) {
210 r = sd_rtnl_message_append_string(req, IFA_LABEL, address->label);
211 if (r < 0) {
212 log_error("Could not append IFA_LABEL attribute: %s",
213 strerror(-r));
214 return r;
215 }
216 }
217
218 r = sd_rtnl_message_append_cache_info(req, IFA_CACHEINFO, &address->cinfo);
219 if (r < 0) {
220 log_error("Could not append IFA_CACHEINFO attribute: %s",
221 strerror(-r));
222 return r;
223 }
224
225 r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
226 if (r < 0) {
227 log_error("Could not send rtnetlink message: %s", strerror(-r));
228 return r;
229 }
230
231 link_ref(link);
232
233 return 0;
234 }
235
236 static int address_acquire(Link *link, Address *original, Address **ret) {
237 union in_addr_union in_addr = {};
238 struct in_addr broadcast = {};
239 _cleanup_address_free_ Address *na = NULL;
240 int r;
241
242 assert(link);
243 assert(original);
244 assert(ret);
245
246 /* Something useful was configured? just use it */
247 if (in_addr_is_null(original->family, &original->in_addr) <= 0)
248 return 0;
249
250 /* The address is configured to be 0.0.0.0 or [::] by the user?
251 * Then let's acquire something more useful from the pool. */
252 r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr);
253 if (r < 0) {
254 log_error_link(link, "Failed to acquire address from pool: %s", strerror(-r));
255 return r;
256 }
257 if (r == 0) {
258 log_error_link(link, "Couldn't find free address for interface, all taken.");
259 return -EBUSY;
260 }
261
262 if (original->family == AF_INET) {
263 /* Pick first address in range for ourselves ...*/
264 in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1);
265
266 /* .. and use last as broadcast address */
267 broadcast.s_addr = in_addr.in.s_addr | htobe32(0xFFFFFFFFUL >> original->prefixlen);
268 } else if (original->family == AF_INET6)
269 in_addr.in6.s6_addr[15] |= 1;
270
271 r = address_new_dynamic(&na);
272 if (r < 0)
273 return r;
274
275 na->family = original->family;
276 na->prefixlen = original->prefixlen;
277 na->scope = original->scope;
278 na->cinfo = original->cinfo;
279
280 if (original->label) {
281 na->label = strdup(original->label);
282 if (!na->label)
283 return -ENOMEM;
284 }
285
286 na->broadcast = broadcast;
287 na->in_addr = in_addr;
288
289 LIST_PREPEND(addresses, link->pool_addresses, na);
290
291 *ret = na;
292 na = NULL;
293
294 return 0;
295 }
296
297 int address_configure(Address *address, Link *link,
298 sd_rtnl_message_handler_t callback) {
299 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
300 int r;
301
302 assert(address);
303 assert(address->family == AF_INET || address->family == AF_INET6);
304 assert(link);
305 assert(link->ifindex > 0);
306 assert(link->manager);
307 assert(link->manager->rtnl);
308
309 r = address_acquire(link, address, &address);
310 if (r < 0)
311 return r;
312
313 r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
314 link->ifindex, address->family);
315 if (r < 0) {
316 log_error("Could not allocate RTM_NEWADDR message: %s",
317 strerror(-r));
318 return r;
319 }
320
321 r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
322 if (r < 0) {
323 log_error("Could not set prefixlen: %s", strerror(-r));
324 return r;
325 }
326
327 r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
328 if (r < 0) {
329 log_error("Could not set flags: %s", strerror(-r));
330 return r;
331 }
332
333 r = sd_rtnl_message_addr_set_scope(req, address->scope);
334 if (r < 0) {
335 log_error("Could not set scope: %s", strerror(-r));
336 return r;
337 }
338
339 if (address->family == AF_INET)
340 r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
341 else if (address->family == AF_INET6)
342 r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
343 if (r < 0) {
344 log_error("Could not append IFA_LOCAL attribute: %s",
345 strerror(-r));
346 return r;
347 }
348
349 if (!in_addr_is_null(address->family, &address->in_addr_peer)) {
350 if (address->family == AF_INET)
351 r = sd_rtnl_message_append_in_addr(req, IFA_ADDRESS, &address->in_addr_peer.in);
352 else if (address->family == AF_INET6)
353 r = sd_rtnl_message_append_in6_addr(req, IFA_ADDRESS, &address->in_addr_peer.in6);
354 if (r < 0) {
355 log_error("Could not append IFA_ADDRESS attribute: %s",
356 strerror(-r));
357 return r;
358 }
359 } else {
360 if (address->family == AF_INET) {
361 r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
362 if (r < 0) {
363 log_error("Could not append IFA_BROADCAST attribute: %s",
364 strerror(-r));
365 return r;
366 }
367 }
368 }
369
370 if (address->label) {
371 r = sd_rtnl_message_append_string(req, IFA_LABEL, address->label);
372 if (r < 0) {
373 log_error("Could not append IFA_LABEL attribute: %s",
374 strerror(-r));
375 return r;
376 }
377 }
378
379 r = sd_rtnl_message_append_cache_info(req, IFA_CACHEINFO,
380 &address->cinfo);
381 if (r < 0) {
382 log_error("Could not append IFA_CACHEINFO attribute: %s",
383 strerror(-r));
384 return r;
385 }
386
387 r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
388 if (r < 0) {
389 log_error("Could not send rtnetlink message: %s", strerror(-r));
390 return r;
391 }
392
393 link_ref(link);
394
395 return 0;
396 }
397
398 int config_parse_broadcast(
399 const char *unit,
400 const char *filename,
401 unsigned line,
402 const char *section,
403 unsigned section_line,
404 const char *lvalue,
405 int ltype,
406 const char *rvalue,
407 void *data,
408 void *userdata) {
409
410 Network *network = userdata;
411 _cleanup_address_free_ Address *n = NULL;
412 int r;
413
414 assert(filename);
415 assert(section);
416 assert(lvalue);
417 assert(rvalue);
418 assert(data);
419
420 r = address_new_static(network, section_line, &n);
421 if (r < 0)
422 return r;
423
424 if (n->family == AF_INET6) {
425 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
426 "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue);
427 return 0;
428 }
429
430 r = in_addr_from_string(AF_INET, rvalue, (union in_addr_union*) &n->broadcast);
431 if (r < 0) {
432 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
433 "Broadcast is invalid, ignoring assignment: %s", rvalue);
434 return 0;
435 }
436
437 n->family = AF_INET;
438 n = NULL;
439
440 return 0;
441 }
442
443 int config_parse_address(const char *unit,
444 const char *filename,
445 unsigned line,
446 const char *section,
447 unsigned section_line,
448 const char *lvalue,
449 int ltype,
450 const char *rvalue,
451 void *data,
452 void *userdata) {
453
454 Network *network = userdata;
455 _cleanup_address_free_ Address *n = NULL;
456 const char *address, *e;
457 union in_addr_union buffer;
458 int r, f;
459
460 assert(filename);
461 assert(section);
462 assert(lvalue);
463 assert(rvalue);
464 assert(data);
465
466 if (streq(section, "Network")) {
467 /* we are not in an Address section, so treat
468 * this as the special '0' section */
469 section_line = 0;
470 }
471
472 r = address_new_static(network, section_line, &n);
473 if (r < 0)
474 return r;
475
476 /* Address=address/prefixlen */
477
478 /* prefixlen */
479 e = strchr(rvalue, '/');
480 if (e) {
481 unsigned i;
482 r = safe_atou(e + 1, &i);
483 if (r < 0) {
484 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
485 "Interface prefix length is invalid, ignoring assignment: %s", e + 1);
486 return 0;
487 }
488
489 n->prefixlen = (unsigned char) i;
490
491 address = strndupa(rvalue, e - rvalue);
492 } else
493 address = rvalue;
494
495 r = in_addr_from_string_auto(address, &f, &buffer);
496 if (r < 0) {
497 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
498 "Address is invalid, ignoring assignment: %s", address);
499 return 0;
500 }
501
502 if (n->family != AF_UNSPEC && f != n->family) {
503 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
504 "Address is incompatible, ignoring assignment: %s", address);
505 return 0;
506 }
507
508 n->family = f;
509
510 if (streq(lvalue, "Address"))
511 n->in_addr = buffer;
512 else
513 n->in_addr_peer = buffer;
514
515 if (n->family == AF_INET && n->broadcast.s_addr == 0)
516 n->broadcast.s_addr = n->in_addr.in.s_addr | htonl(0xfffffffflu >> n->prefixlen);
517
518 n = NULL;
519
520 return 0;
521 }
522
523 int config_parse_label(const char *unit,
524 const char *filename,
525 unsigned line,
526 const char *section,
527 unsigned section_line,
528 const char *lvalue,
529 int ltype,
530 const char *rvalue,
531 void *data,
532 void *userdata) {
533 Network *network = userdata;
534 _cleanup_address_free_ Address *n = NULL;
535 char *label;
536 int r;
537
538 assert(filename);
539 assert(section);
540 assert(lvalue);
541 assert(rvalue);
542 assert(data);
543
544 r = address_new_static(network, section_line, &n);
545 if (r < 0)
546 return r;
547
548 label = strdup(rvalue);
549 if (!label)
550 return log_oom();
551
552 if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
553 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
554 "Interface label is not ASCII clean or is too"
555 " long, ignoring assignment: %s", rvalue);
556 free(label);
557 return 0;
558 }
559
560 free(n->label);
561 if (*label)
562 n->label = label;
563 else {
564 free(label);
565 n->label = NULL;
566 }
567
568 n = NULL;
569
570 return 0;
571 }
572
573 bool address_equal(Address *a1, Address *a2) {
574 /* same object */
575 if (a1 == a2)
576 return true;
577
578 /* one, but not both, is NULL */
579 if (!a1 || !a2)
580 return false;
581
582 if (a1->family != a2->family)
583 return false;
584
585 switch (a1->family) {
586 /* use the same notion of equality as the kernel does */
587 case AF_UNSPEC:
588 return true;
589
590 case AF_INET:
591 if (a1->prefixlen != a2->prefixlen)
592 return false;
593 else {
594 uint32_t b1, b2;
595
596 b1 = be32toh(a1->in_addr.in.s_addr);
597 b2 = be32toh(a2->in_addr.in.s_addr);
598
599 return (b1 >> (32 - a1->prefixlen)) == (b2 >> (32 - a1->prefixlen));
600 }
601
602 case AF_INET6:
603 {
604 uint64_t *b1, *b2;
605
606 b1 = (uint64_t*)&a1->in_addr.in6;
607 b2 = (uint64_t*)&a2->in_addr.in6;
608
609 return (((b1[0] ^ b2[0]) | (b1[1] ^ b2[1])) == 0UL);
610 }
611 default:
612 assert_not_reached("Invalid address family");
613 }
614 }