chiark / gitweb /
api: in constructor function calls, always put the returned object pointer first...
[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, &req, RTM_DELADDR,
109                                      link->ifindex, address->family);
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, &req, RTM_NEWADDR,
154                                      link->ifindex, address->family);
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         if (n->family == AF_INET6) {
279                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
280                            "Broadcast is not valid for IPv6 addresses, "
281                            "ignoring assignment: %s", address);
282                 return 0;
283         }
284
285         r = net_parse_inaddr(address, &n->family, &n->broadcast);
286         if (r < 0) {
287                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
288                            "Broadcast is invalid, ignoring assignment: %s", address);
289                 return 0;
290         }
291
292         n = NULL;
293
294         return 0;
295 }
296
297 int config_parse_address(const char *unit,
298                 const char *filename,
299                 unsigned line,
300                 const char *section,
301                 unsigned section_line,
302                 const char *lvalue,
303                 int ltype,
304                 const char *rvalue,
305                 void *data,
306                 void *userdata) {
307         Network *network = userdata;
308         _cleanup_address_free_ Address *n = NULL;
309         _cleanup_free_ char *address = NULL;
310         const char *e;
311         int r;
312
313         assert(filename);
314         assert(section);
315         assert(lvalue);
316         assert(rvalue);
317         assert(data);
318
319         if (streq(section, "Network")) {
320                 /* we are not in an Address section, so treat
321                  * this as the special '0' section */
322                 section_line = 0;
323         }
324
325         r = address_new_static(network, section_line, &n);
326         if (r < 0)
327                 return r;
328
329         /* Address=address/prefixlen */
330
331         /* prefixlen */
332         e = strchr(rvalue, '/');
333         if (e) {
334                 unsigned i;
335                 r = safe_atou(e + 1, &i);
336                 if (r < 0) {
337                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
338                                    "Interface prefix length is invalid, "
339                                    "ignoring assignment: %s", e + 1);
340                         return 0;
341                 }
342
343                 n->prefixlen = (unsigned char) i;
344
345                 address = strndup(rvalue, e - rvalue);
346                 if (!address)
347                         return log_oom();
348         } else {
349                 address = strdup(rvalue);
350                 if (!address)
351                         return log_oom();
352         }
353
354         r = net_parse_inaddr(address, &n->family, &n->in_addr);
355         if (r < 0) {
356                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
357                            "Address is invalid, ignoring assignment: %s", address);
358                 return 0;
359         }
360
361         if (n->family == AF_INET && !n->broadcast.s_addr)
362                 n->broadcast.s_addr = n->in_addr.in.s_addr |
363                                       htonl(0xfffffffflu >> n->prefixlen);
364
365         n = NULL;
366
367         return 0;
368 }
369
370 int config_parse_label(const char *unit,
371                 const char *filename,
372                 unsigned line,
373                 const char *section,
374                 unsigned section_line,
375                 const char *lvalue,
376                 int ltype,
377                 const char *rvalue,
378                 void *data,
379                 void *userdata) {
380         Network *network = userdata;
381         _cleanup_address_free_ Address *n = NULL;
382         char *label;
383         int r;
384
385         assert(filename);
386         assert(section);
387         assert(lvalue);
388         assert(rvalue);
389         assert(data);
390
391         r = address_new_static(network, section_line, &n);
392         if (r < 0)
393                 return r;
394
395         label = strdup(rvalue);
396         if (!label)
397                 return log_oom();
398
399         if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
400                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
401                            "Interface label is not ASCII clean or is too"
402                            " long, ignoring assignment: %s", rvalue);
403                 free(label);
404                 return 0;
405         }
406
407         free(n->label);
408         if (*label)
409                 n->label = label;
410         else {
411                 free(label);
412                 n->label = NULL;
413         }
414
415         n = NULL;
416
417         return 0;
418 }