chiark / gitweb /
networkd: IP address equality
[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 int address_configure(Address *address, Link *link,
232                       sd_rtnl_message_handler_t callback) {
233         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
234         int r;
235
236         assert(address);
237         assert(address->family == AF_INET || address->family == AF_INET6);
238         assert(link);
239         assert(link->ifindex > 0);
240         assert(link->manager);
241         assert(link->manager->rtnl);
242
243         r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
244                                      link->ifindex, address->family);
245         if (r < 0) {
246                 log_error("Could not allocate RTM_NEWADDR message: %s",
247                           strerror(-r));
248                 return r;
249         }
250
251         r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
252         if (r < 0) {
253                 log_error("Could not set prefixlen: %s", strerror(-r));
254                 return r;
255         }
256
257         r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
258         if (r < 0) {
259                 log_error("Could not set flags: %s", strerror(-r));
260                 return r;
261         }
262
263         r = sd_rtnl_message_addr_set_scope(req, address->scope);
264         if (r < 0) {
265                 log_error("Could not set scope: %s", strerror(-r));
266                 return r;
267         }
268
269         if (address->family == AF_INET)
270                 r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
271         else if (address->family == AF_INET6)
272                 r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
273         if (r < 0) {
274                 log_error("Could not append IFA_LOCAL attribute: %s",
275                           strerror(-r));
276                 return r;
277         }
278
279         if (address->family == AF_INET) {
280                 r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
281                 if (r < 0) {
282                         log_error("Could not append IFA_BROADCAST attribute: %s",
283                                   strerror(-r));
284                         return r;
285                 }
286         }
287
288         if (address->label) {
289                 r = sd_rtnl_message_append_string(req, IFA_LABEL, address->label);
290                 if (r < 0) {
291                         log_error("Could not append IFA_LABEL attribute: %s",
292                                   strerror(-r));
293                         return r;
294                 }
295         }
296
297         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
298         if (r < 0) {
299                 log_error("Could not send rtnetlink message: %s", strerror(-r));
300                 return r;
301         }
302
303         return 0;
304 }
305
306 int config_parse_dns(const char *unit,
307                 const char *filename,
308                 unsigned line,
309                 const char *section,
310                 unsigned section_line,
311                 const char *lvalue,
312                 int ltype,
313                 const char *rvalue,
314                 void *data,
315                 void *userdata) {
316         Network *network = userdata;
317         Address *tail;
318         _cleanup_address_free_ Address *n = NULL;
319         int r;
320
321         assert(filename);
322         assert(section);
323         assert(lvalue);
324         assert(rvalue);
325         assert(network);
326
327         r = address_new_dynamic(&n);
328         if (r < 0)
329                 return r;
330
331         r = net_parse_inaddr(rvalue, &n->family, &n->in_addr);
332         if (r < 0) {
333                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
334                            "DNS address is invalid, ignoring assignment: %s", rvalue);
335                 return 0;
336         }
337
338         LIST_FIND_TAIL(addresses, network->dns, tail);
339         LIST_INSERT_AFTER(addresses, network->dns, tail, n);
340         n = NULL;
341
342         return 0;
343 }
344
345 int config_parse_broadcast(const char *unit,
346                 const char *filename,
347                 unsigned line,
348                 const char *section,
349                 unsigned section_line,
350                 const char *lvalue,
351                 int ltype,
352                 const char *rvalue,
353                 void *data,
354                 void *userdata) {
355         Network *network = userdata;
356         _cleanup_address_free_ Address *n = NULL;
357         _cleanup_free_ char *address = NULL;
358         int r;
359
360         assert(filename);
361         assert(section);
362         assert(lvalue);
363         assert(rvalue);
364         assert(data);
365
366         r = address_new_static(network, section_line, &n);
367         if (r < 0)
368                 return r;
369
370         if (n->family == AF_INET6) {
371                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
372                            "Broadcast is not valid for IPv6 addresses, "
373                            "ignoring assignment: %s", address);
374                 return 0;
375         }
376
377         r = net_parse_inaddr(address, &n->family, &n->broadcast);
378         if (r < 0) {
379                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
380                            "Broadcast is invalid, ignoring assignment: %s", address);
381                 return 0;
382         }
383
384         n = NULL;
385
386         return 0;
387 }
388
389 int config_parse_address(const char *unit,
390                 const char *filename,
391                 unsigned line,
392                 const char *section,
393                 unsigned section_line,
394                 const char *lvalue,
395                 int ltype,
396                 const char *rvalue,
397                 void *data,
398                 void *userdata) {
399         Network *network = userdata;
400         _cleanup_address_free_ Address *n = NULL;
401         _cleanup_free_ char *address = NULL;
402         const char *e;
403         int r;
404
405         assert(filename);
406         assert(section);
407         assert(lvalue);
408         assert(rvalue);
409         assert(data);
410
411         if (streq(section, "Network")) {
412                 /* we are not in an Address section, so treat
413                  * this as the special '0' section */
414                 section_line = 0;
415         }
416
417         r = address_new_static(network, section_line, &n);
418         if (r < 0)
419                 return r;
420
421         /* Address=address/prefixlen */
422
423         /* prefixlen */
424         e = strchr(rvalue, '/');
425         if (e) {
426                 unsigned i;
427                 r = safe_atou(e + 1, &i);
428                 if (r < 0) {
429                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
430                                    "Interface prefix length is invalid, "
431                                    "ignoring assignment: %s", e + 1);
432                         return 0;
433                 }
434
435                 n->prefixlen = (unsigned char) i;
436
437                 address = strndup(rvalue, e - rvalue);
438                 if (!address)
439                         return log_oom();
440         } else {
441                 address = strdup(rvalue);
442                 if (!address)
443                         return log_oom();
444         }
445
446         r = net_parse_inaddr(address, &n->family, &n->in_addr);
447         if (r < 0) {
448                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
449                            "Address is invalid, ignoring assignment: %s", address);
450                 return 0;
451         }
452
453         if (n->family == AF_INET && !n->broadcast.s_addr)
454                 n->broadcast.s_addr = n->in_addr.in.s_addr |
455                                       htonl(0xfffffffflu >> n->prefixlen);
456
457         n = NULL;
458
459         return 0;
460 }
461
462 int config_parse_label(const char *unit,
463                 const char *filename,
464                 unsigned line,
465                 const char *section,
466                 unsigned section_line,
467                 const char *lvalue,
468                 int ltype,
469                 const char *rvalue,
470                 void *data,
471                 void *userdata) {
472         Network *network = userdata;
473         _cleanup_address_free_ Address *n = NULL;
474         char *label;
475         int r;
476
477         assert(filename);
478         assert(section);
479         assert(lvalue);
480         assert(rvalue);
481         assert(data);
482
483         r = address_new_static(network, section_line, &n);
484         if (r < 0)
485                 return r;
486
487         label = strdup(rvalue);
488         if (!label)
489                 return log_oom();
490
491         if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
492                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
493                            "Interface label is not ASCII clean or is too"
494                            " long, ignoring assignment: %s", rvalue);
495                 free(label);
496                 return 0;
497         }
498
499         free(n->label);
500         if (*label)
501                 n->label = label;
502         else {
503                 free(label);
504                 n->label = NULL;
505         }
506
507         n = NULL;
508
509         return 0;
510 }
511
512 bool address_equal(Address *a1, Address *a2) {
513         /* same object */
514         if (a1 == a2)
515                 return true;
516
517         /* one, but not both, is NULL */
518         if (!a1 || !a2)
519                 return false;
520
521         if (a1->family != a2->family)
522                 return false;
523
524         switch (a1->family) {
525         /* use the same notion of equality as the kernel does */
526         case AF_UNSPEC:
527                 return true;
528
529         case AF_INET:
530                 if (a1->prefixlen != a2->prefixlen)
531                         return false;
532                 else {
533                         uint32_t b1, b2;
534
535                         b1 = be32toh(a1->in_addr.in.s_addr);
536                         b2 = be32toh(a2->in_addr.in.s_addr);
537
538                         return (b1 >> (32 - a1->prefixlen)) == (b2 >> (32 - a1->prefixlen));
539                 }
540
541         case AF_INET6:
542         {
543                 uint64_t *b1, *b2;
544
545                 b1 = (uint64_t*)&a1->in_addr.in6;
546                 b2 = (uint64_t*)&a2->in_addr.in6;
547
548                 return (((b1[0] ^ b2[0]) | (b1[1] ^ b2[1])) == 0UL);
549         }
550         default:
551                 assert_not_reached("Invalid address family");
552         }
553 }