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