chiark / gitweb /
e6e3f8f06788bff8f58abb36fe7b19345ea3d398
[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         Set **dns = data;
317         _cleanup_address_free_ Address *n = NULL;
318         int r;
319
320         assert(filename);
321         assert(section);
322         assert(lvalue);
323         assert(rvalue);
324         assert(data);
325
326         r = address_new_dynamic(&n);
327         if (r < 0)
328                 return r;
329
330         r = net_parse_inaddr(rvalue, &n->family, &n->in_addr);
331         if (r < 0) {
332                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
333                            "DNS address is invalid, ignoring assignment: %s", rvalue);
334                 return 0;
335         }
336
337         set_put(*dns, n);
338         n = NULL;
339
340         return 0;
341 }
342
343 int config_parse_broadcast(const char *unit,
344                 const char *filename,
345                 unsigned line,
346                 const char *section,
347                 unsigned section_line,
348                 const char *lvalue,
349                 int ltype,
350                 const char *rvalue,
351                 void *data,
352                 void *userdata) {
353         Network *network = userdata;
354         _cleanup_address_free_ Address *n = NULL;
355         _cleanup_free_ char *address = NULL;
356         int r;
357
358         assert(filename);
359         assert(section);
360         assert(lvalue);
361         assert(rvalue);
362         assert(data);
363
364         r = address_new_static(network, section_line, &n);
365         if (r < 0)
366                 return r;
367
368         if (n->family == AF_INET6) {
369                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
370                            "Broadcast is not valid for IPv6 addresses, "
371                            "ignoring assignment: %s", address);
372                 return 0;
373         }
374
375         r = net_parse_inaddr(address, &n->family, &n->broadcast);
376         if (r < 0) {
377                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
378                            "Broadcast is invalid, ignoring assignment: %s", address);
379                 return 0;
380         }
381
382         n = NULL;
383
384         return 0;
385 }
386
387 int config_parse_address(const char *unit,
388                 const char *filename,
389                 unsigned line,
390                 const char *section,
391                 unsigned section_line,
392                 const char *lvalue,
393                 int ltype,
394                 const char *rvalue,
395                 void *data,
396                 void *userdata) {
397         Network *network = userdata;
398         _cleanup_address_free_ Address *n = NULL;
399         _cleanup_free_ char *address = NULL;
400         const char *e;
401         int r;
402
403         assert(filename);
404         assert(section);
405         assert(lvalue);
406         assert(rvalue);
407         assert(data);
408
409         if (streq(section, "Network")) {
410                 /* we are not in an Address section, so treat
411                  * this as the special '0' section */
412                 section_line = 0;
413         }
414
415         r = address_new_static(network, section_line, &n);
416         if (r < 0)
417                 return r;
418
419         /* Address=address/prefixlen */
420
421         /* prefixlen */
422         e = strchr(rvalue, '/');
423         if (e) {
424                 unsigned i;
425                 r = safe_atou(e + 1, &i);
426                 if (r < 0) {
427                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
428                                    "Interface prefix length is invalid, "
429                                    "ignoring assignment: %s", e + 1);
430                         return 0;
431                 }
432
433                 n->prefixlen = (unsigned char) i;
434
435                 address = strndup(rvalue, e - rvalue);
436                 if (!address)
437                         return log_oom();
438         } else {
439                 address = strdup(rvalue);
440                 if (!address)
441                         return log_oom();
442         }
443
444         r = net_parse_inaddr(address, &n->family, &n->in_addr);
445         if (r < 0) {
446                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
447                            "Address is invalid, ignoring assignment: %s", address);
448                 return 0;
449         }
450
451         if (n->family == AF_INET && !n->broadcast.s_addr)
452                 n->broadcast.s_addr = n->in_addr.in.s_addr |
453                                       htonl(0xfffffffflu >> n->prefixlen);
454
455         n = NULL;
456
457         return 0;
458 }
459
460 int config_parse_label(const char *unit,
461                 const char *filename,
462                 unsigned line,
463                 const char *section,
464                 unsigned section_line,
465                 const char *lvalue,
466                 int ltype,
467                 const char *rvalue,
468                 void *data,
469                 void *userdata) {
470         Network *network = userdata;
471         _cleanup_address_free_ Address *n = NULL;
472         char *label;
473         int r;
474
475         assert(filename);
476         assert(section);
477         assert(lvalue);
478         assert(rvalue);
479         assert(data);
480
481         r = address_new_static(network, section_line, &n);
482         if (r < 0)
483                 return r;
484
485         label = strdup(rvalue);
486         if (!label)
487                 return log_oom();
488
489         if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
490                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
491                            "Interface label is not ASCII clean or is too"
492                            " long, ignoring assignment: %s", rvalue);
493                 free(label);
494                 return 0;
495         }
496
497         free(n->label);
498         if (*label)
499                 n->label = label;
500         else {
501                 free(label);
502                 n->label = NULL;
503         }
504
505         n = NULL;
506
507         return 0;
508 }