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