chiark / gitweb /
560605408f30553996171a79eec3a7d6d4ac7c78
[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 "net-util.h"
30
31 int address_new_static(Network *network, unsigned section, Address **ret) {
32         _cleanup_address_free_ Address *address = NULL;
33
34         if (section) {
35                 uint64_t key = section;
36                 address = hashmap_get(network->addresses_by_section, &key);
37                 if (address) {
38                         *ret = address;
39                         address = NULL;
40
41                         return 0;
42                 }
43         }
44
45         address = new0(Address, 1);
46         if (!address)
47                 return -ENOMEM;
48
49         address->family = AF_UNSPEC;
50
51         address->network = network;
52
53         LIST_PREPEND(static_addresses, network->static_addresses, address);
54
55         if (section) {
56                 address->section = section;
57                 hashmap_put(network->addresses_by_section, &address->section, address);
58         }
59
60         *ret = address;
61         address = NULL;
62
63         return 0;
64 }
65
66 int address_new_dynamic(Address **ret) {
67         _cleanup_address_free_ Address *address = NULL;
68
69         address = new0(Address, 1);
70         if (!address)
71                 return -ENOMEM;
72
73         address->family = AF_UNSPEC;
74
75         *ret = address;
76         address = NULL;
77
78         return 0;
79 }
80
81 void address_free(Address *address) {
82         if (!address)
83                 return;
84
85         if (address->network) {
86                 LIST_REMOVE(static_addresses, address->network->static_addresses, address);
87
88                 if (address->section)
89                         hashmap_remove(address->network->addresses_by_section,
90                                        &address->section);
91         }
92
93         free(address);
94 }
95
96 int address_drop(Address *address, Link *link,
97                  sd_rtnl_message_handler_t callback) {
98         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
99         int r;
100
101         assert(address);
102         assert(address->family == AF_INET || address->family == AF_INET6);
103         assert(link);
104         assert(link->ifindex > 0);
105         assert(link->manager);
106         assert(link->manager->rtnl);
107
108         r = sd_rtnl_message_new_addr(link->manager->rtnl, RTM_DELADDR,
109                                      link->ifindex, address->family, &req);
110         if (r < 0) {
111                 log_error("Could not allocate RTM_DELADDR message: %s",
112                           strerror(-r));
113                 return r;
114         }
115
116         r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
117         if (r < 0) {
118                 log_error("Could not set prefixlen: %s", strerror(-r));
119                 return r;
120         }
121
122         if (address->family == AF_INET)
123                 r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
124         else if (address->family == AF_INET6)
125                 r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
126         if (r < 0) {
127                 log_error("Could not append IFA_LOCAL attribute: %s",
128                           strerror(-r));
129                 return r;
130         }
131
132         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
133         if (r < 0) {
134                 log_error("Could not send rtnetlink message: %s", strerror(-r));
135                 return r;
136         }
137
138         return 0;
139 }
140
141 int address_configure(Address *address, Link *link,
142                       sd_rtnl_message_handler_t callback) {
143         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
144         int r;
145
146         assert(address);
147         assert(address->family == AF_INET || address->family == AF_INET6);
148         assert(link);
149         assert(link->ifindex > 0);
150         assert(link->manager);
151         assert(link->manager->rtnl);
152
153         r = sd_rtnl_message_new_addr(link->manager->rtnl, RTM_NEWADDR,
154                                      link->ifindex, address->family, &req);
155         if (r < 0) {
156                 log_error("Could not allocate RTM_NEWADDR message: %s",
157                           strerror(-r));
158                 return r;
159         }
160
161         r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
162         if (r < 0) {
163                 log_error("Could not set prefixlen: %s", strerror(-r));
164                 return r;
165         }
166
167         r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
168         if (r < 0) {
169                 log_error("Could not set flags: %s", strerror(-r));
170                 return r;
171         }
172
173         r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_UNIVERSE);
174         if (r < 0) {
175                 log_error("Could not set scope: %s", strerror(-r));
176                 return r;
177         }
178
179         if (address->family == AF_INET)
180                 r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
181         else if (address->family == AF_INET6)
182                 r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
183         if (r < 0) {
184                 log_error("Could not append IFA_LOCAL attribute: %s",
185                           strerror(-r));
186                 return r;
187         }
188
189         if (address->family == AF_INET) {
190                 r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
191                 if (r < 0) {
192                         log_error("Could not append IFA_BROADCAST attribute: %s",
193                                   strerror(-r));
194                         return r;
195                 }
196         }
197
198         if (address->label) {
199                 r = sd_rtnl_message_append_string(req, IFA_LABEL, address->label);
200                 if (r < 0) {
201                         log_error("Could not append IFA_LABEL attribute: %s",
202                                   strerror(-r));
203                         return r;
204                 }
205         }
206
207         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
208         if (r < 0) {
209                 log_error("Could not send rtnetlink message: %s", strerror(-r));
210                 return r;
211         }
212
213         return 0;
214 }
215
216 int config_parse_dns(const char *unit,
217                 const char *filename,
218                 unsigned line,
219                 const char *section,
220                 unsigned section_line,
221                 const char *lvalue,
222                 int ltype,
223                 const char *rvalue,
224                 void *data,
225                 void *userdata) {
226         Address **dns = data;
227         _cleanup_address_free_ Address *n = NULL;
228         int r;
229
230         assert(filename);
231         assert(section);
232         assert(lvalue);
233         assert(rvalue);
234         assert(data);
235
236         r = address_new_dynamic(&n);
237         if (r < 0)
238                 return r;
239
240         r = net_parse_inaddr(rvalue, &n->family, &n->in_addr);
241         if (r < 0) {
242                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
243                            "DNS address is invalid, ignoring assignment: %s", rvalue);
244                 return 0;
245         }
246
247         *dns = n;
248         n = NULL;
249
250         return 0;
251 }
252
253 int config_parse_broadcast(const char *unit,
254                 const char *filename,
255                 unsigned line,
256                 const char *section,
257                 unsigned section_line,
258                 const char *lvalue,
259                 int ltype,
260                 const char *rvalue,
261                 void *data,
262                 void *userdata) {
263         Network *network = userdata;
264         _cleanup_address_free_ Address *n = NULL;
265         _cleanup_free_ char *address = NULL;
266         int r;
267
268         assert(filename);
269         assert(section);
270         assert(lvalue);
271         assert(rvalue);
272         assert(data);
273
274         r = address_new_static(network, section_line, &n);
275         if (r < 0)
276                 return r;
277
278         r = net_parse_inaddr(address, &n->family, &n->broadcast);
279         if (r < 0) {
280                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
281                            "Broadcast is invalid, ignoring assignment: %s", address);
282                 return 0;
283         }
284
285         n = NULL;
286
287         return 0;
288 }
289
290 int config_parse_address(const char *unit,
291                 const char *filename,
292                 unsigned line,
293                 const char *section,
294                 unsigned section_line,
295                 const char *lvalue,
296                 int ltype,
297                 const char *rvalue,
298                 void *data,
299                 void *userdata) {
300         Network *network = userdata;
301         _cleanup_address_free_ Address *n = NULL;
302         _cleanup_free_ char *address = NULL;
303         const char *e;
304         int r;
305
306         assert(filename);
307         assert(section);
308         assert(lvalue);
309         assert(rvalue);
310         assert(data);
311
312         if (streq(section, "Network")) {
313                 /* we are not in an Address section, so treat
314                  * this as the special '0' section */
315                 section_line = 0;
316         }
317
318         r = address_new_static(network, section_line, &n);
319         if (r < 0)
320                 return r;
321
322         /* Address=address/prefixlen */
323
324         /* prefixlen */
325         e = strchr(rvalue, '/');
326         if (e) {
327                 unsigned i;
328                 r = safe_atou(e + 1, &i);
329                 if (r < 0) {
330                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
331                                    "Interface prefix length is invalid, "
332                                    "ignoring assignment: %s", e + 1);
333                         return 0;
334                 }
335
336                 n->prefixlen = (unsigned char) i;
337
338                 address = strndup(rvalue, e - rvalue);
339                 if (!address)
340                         return log_oom();
341         } else {
342                 address = strdup(rvalue);
343                 if (!address)
344                         return log_oom();
345         }
346
347         r = net_parse_inaddr(address, &n->family, &n->in_addr);
348         if (r < 0) {
349                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
350                            "Address is invalid, ignoring assignment: %s", address);
351                 return 0;
352         }
353
354         if (n->family == AF_INET && !n->broadcast.s_addr)
355                 n->broadcast.s_addr = n->in_addr.in.s_addr |
356                                       htonl(0xfffffffflu >> n->prefixlen);
357
358         n = NULL;
359
360         return 0;
361 }
362
363 int config_parse_label(const char *unit,
364                 const char *filename,
365                 unsigned line,
366                 const char *section,
367                 unsigned section_line,
368                 const char *lvalue,
369                 int ltype,
370                 const char *rvalue,
371                 void *data,
372                 void *userdata) {
373         Network *network = userdata;
374         _cleanup_address_free_ Address *n = NULL;
375         char *label;
376         int r;
377
378         assert(filename);
379         assert(section);
380         assert(lvalue);
381         assert(rvalue);
382         assert(data);
383
384         r = address_new_static(network, section_line, &n);
385         if (r < 0)
386                 return r;
387
388         label = strdup(rvalue);
389         if (!label)
390                 return log_oom();
391
392         if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
393                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
394                            "Interface label is not ASCII clean or is too"
395                            " long, ignoring assignment: %s", rvalue);
396                 free(label);
397                 return 0;
398         }
399
400         free(n->label);
401         if (*label)
402                 n->label = label;
403         else {
404                 free(label);
405                 n->label = NULL;
406         }
407
408         n = NULL;
409
410         return 0;
411 }