chiark / gitweb /
log: be a bit less wasteful when allocating buffers
[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 #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                                    "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 (!e && f == AF_INET) {
503                 r = in_addr_default_prefixlen(&buffer.in, &n->prefixlen);
504                 if (r < 0) {
505                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
506                                    "Prefix length not specified, and a default one can not be deduced for '%s', ignoring assignment", address);
507                         return 0;
508                 }
509         }
510
511         if (n->family != AF_UNSPEC && f != n->family) {
512                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
513                            "Address is incompatible, ignoring assignment: %s", address);
514                 return 0;
515         }
516
517         n->family = f;
518
519         if (streq(lvalue, "Address"))
520                 n->in_addr = buffer;
521         else
522                 n->in_addr_peer = buffer;
523
524         if (n->family == AF_INET && n->broadcast.s_addr == 0)
525                 n->broadcast.s_addr = n->in_addr.in.s_addr | htonl(0xfffffffflu >> n->prefixlen);
526
527         n = NULL;
528
529         return 0;
530 }
531
532 int config_parse_label(const char *unit,
533                 const char *filename,
534                 unsigned line,
535                 const char *section,
536                 unsigned section_line,
537                 const char *lvalue,
538                 int ltype,
539                 const char *rvalue,
540                 void *data,
541                 void *userdata) {
542         Network *network = userdata;
543         _cleanup_address_free_ Address *n = NULL;
544         char *label;
545         int r;
546
547         assert(filename);
548         assert(section);
549         assert(lvalue);
550         assert(rvalue);
551         assert(data);
552
553         r = address_new_static(network, section_line, &n);
554         if (r < 0)
555                 return r;
556
557         label = strdup(rvalue);
558         if (!label)
559                 return log_oom();
560
561         if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
562                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
563                            "Interface label is not ASCII clean or is too"
564                            " long, ignoring assignment: %s", rvalue);
565                 free(label);
566                 return 0;
567         }
568
569         free(n->label);
570         if (*label)
571                 n->label = label;
572         else {
573                 free(label);
574                 n->label = NULL;
575         }
576
577         n = NULL;
578
579         return 0;
580 }
581
582 bool address_equal(Address *a1, Address *a2) {
583         /* same object */
584         if (a1 == a2)
585                 return true;
586
587         /* one, but not both, is NULL */
588         if (!a1 || !a2)
589                 return false;
590
591         if (a1->family != a2->family)
592                 return false;
593
594         switch (a1->family) {
595         /* use the same notion of equality as the kernel does */
596         case AF_UNSPEC:
597                 return true;
598
599         case AF_INET:
600                 if (a1->prefixlen != a2->prefixlen)
601                         return false;
602                 else {
603                         uint32_t b1, b2;
604
605                         b1 = be32toh(a1->in_addr.in.s_addr);
606                         b2 = be32toh(a2->in_addr.in.s_addr);
607
608                         return (b1 >> (32 - a1->prefixlen)) == (b2 >> (32 - a1->prefixlen));
609                 }
610
611         case AF_INET6:
612         {
613                 uint64_t *b1, *b2;
614
615                 b1 = (uint64_t*)&a1->in_addr.in6;
616                 b2 = (uint64_t*)&a2->in_addr.in6;
617
618                 return (((b1[0] ^ b2[0]) | (b1[1] ^ b2[1])) == 0UL);
619         }
620         default:
621                 assert_not_reached("Invalid address family");
622         }
623 }