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