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