chiark / gitweb /
de5566dfdb44b96b50bf749fc2a4667a4f68de07
[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_sd_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_addr_new(RTM_DELADDR, link->ifindex, address->family, &req);
109         if (r < 0) {
110                 log_error("Could not allocate RTM_DELADDR message: %s",
111                           strerror(-r));
112                 return r;
113         }
114
115         r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
116         if (r < 0) {
117                 log_error("Could not set prefixlen: %s", strerror(-r));
118                 return r;
119         }
120
121         if (address->family == AF_INET)
122                 r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
123         else if (address->family == AF_INET6)
124                 r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
125         if (r < 0) {
126                 log_error("Could not append IFA_LOCAL attribute: %s",
127                           strerror(-r));
128                 return r;
129         }
130
131         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
132         if (r < 0) {
133                 log_error("Could not send rtnetlink message: %s", strerror(-r));
134                 return r;
135         }
136
137         return 0;
138 }
139
140 int address_configure(Address *address, Link *link,
141                       sd_rtnl_message_handler_t callback) {
142         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
143         int r;
144
145         assert(address);
146         assert(address->family == AF_INET || address->family == AF_INET6);
147         assert(link);
148         assert(link->ifindex > 0);
149         assert(link->manager);
150         assert(link->manager->rtnl);
151
152         r = sd_rtnl_message_addr_new(RTM_NEWADDR, link->ifindex,
153                         address->family, &req);
154         if (r < 0) {
155                 log_error("Could not allocate RTM_NEWADDR message: %s",
156                           strerror(-r));
157                 return r;
158         }
159
160         r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
161         if (r < 0) {
162                 log_error("Could not set prefixlen: %s", strerror(-r));
163                 return r;
164         }
165
166         r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
167         if (r < 0) {
168                 log_error("Could not set flags: %s", strerror(-r));
169                 return r;
170         }
171
172         r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_UNIVERSE);
173         if (r < 0) {
174                 log_error("Could not set scope: %s", strerror(-r));
175                 return r;
176         }
177
178         if (address->family == AF_INET)
179                 r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in);
180         else if (address->family == AF_INET6)
181                 r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6);
182         if (r < 0) {
183                 log_error("Could not append IFA_LOCAL attribute: %s",
184                           strerror(-r));
185                 return r;
186         }
187
188         if (address->family == AF_INET) {
189                 struct in_addr broadcast;
190
191                 broadcast.s_addr = address->in_addr.in.s_addr | address->netmask.s_addr;
192
193                 r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &broadcast);
194                 if (r < 0) {
195                         log_error("Could not append IFA_BROADCAST attribute: %s",
196                                   strerror(-r));
197                         return r;
198                 }
199         }
200
201         if (address->label) {
202                 r = sd_rtnl_message_append_string(req, IFA_LABEL, address->label);
203                 if (r < 0) {
204                         log_error("Could not append IFA_LABEL attribute: %s",
205                                   strerror(-r));
206                         return r;
207                 }
208         }
209
210         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
211         if (r < 0) {
212                 log_error("Could not send rtnetlink message: %s", strerror(-r));
213                 return r;
214         }
215
216         return 0;
217 }
218
219 int config_parse_dns(const char *unit,
220                 const char *filename,
221                 unsigned line,
222                 const char *section,
223                 unsigned section_line,
224                 const char *lvalue,
225                 int ltype,
226                 const char *rvalue,
227                 void *data,
228                 void *userdata) {
229         Address **dns = data;
230         _cleanup_address_free_ Address *n = NULL;
231         int r;
232
233         assert(filename);
234         assert(section);
235         assert(lvalue);
236         assert(rvalue);
237         assert(data);
238
239         r = address_new_dynamic(&n);
240         if (r < 0)
241                 return r;
242
243         r = net_parse_inaddr(rvalue, &n->family, &n->in_addr);
244         if (r < 0) {
245                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
246                            "DNS address is invalid, ignoring assignment: %s", rvalue);
247                 return 0;
248         }
249
250         *dns = n;
251         n = NULL;
252
253         return 0;
254 }
255
256 int config_parse_address(const char *unit,
257                 const char *filename,
258                 unsigned line,
259                 const char *section,
260                 unsigned section_line,
261                 const char *lvalue,
262                 int ltype,
263                 const char *rvalue,
264                 void *data,
265                 void *userdata) {
266         Network *network = userdata;
267         _cleanup_address_free_ Address *n = NULL;
268         _cleanup_free_ char *address = NULL;
269         const char *e;
270         int r;
271
272         assert(filename);
273         assert(section);
274         assert(lvalue);
275         assert(rvalue);
276         assert(data);
277
278         if (streq(section, "Network")) {
279                 /* we are not in an Address section, so treat
280                  * this as the special '0' section */
281                 section_line = 0;
282         }
283
284         r = address_new_static(network, section_line, &n);
285         if (r < 0)
286                 return r;
287
288         /* Address=address/prefixlen */
289
290         /* prefixlen */
291         e = strchr(rvalue, '/');
292         if (e) {
293                 unsigned i;
294                 r = safe_atou(e + 1, &i);
295                 if (r < 0) {
296                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
297                                    "Interface prefix length is invalid, "
298                                    "ignoring assignment: %s", e + 1);
299                         return 0;
300                 }
301
302                 n->prefixlen = (unsigned char) i;
303                 n->netmask.s_addr = htonl(0xfffffffflu >> n->prefixlen);
304
305                 address = strndup(rvalue, e - rvalue);
306                 if (!address)
307                         return log_oom();
308         } else {
309                 address = strdup(rvalue);
310                 if (!address)
311                         return log_oom();
312         }
313
314         r = net_parse_inaddr(address, &n->family, &n->in_addr);
315         if (r < 0) {
316                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
317                            "Address is invalid, ignoring assignment: %s", address);
318                 return 0;
319         }
320
321         n = NULL;
322
323         return 0;
324 }
325
326 int config_parse_label(const char *unit,
327                 const char *filename,
328                 unsigned line,
329                 const char *section,
330                 unsigned section_line,
331                 const char *lvalue,
332                 int ltype,
333                 const char *rvalue,
334                 void *data,
335                 void *userdata) {
336         Network *network = userdata;
337         _cleanup_address_free_ Address *n = NULL;
338         char *label;
339         int r;
340
341         assert(filename);
342         assert(section);
343         assert(lvalue);
344         assert(rvalue);
345         assert(data);
346
347         r = address_new_static(network, section_line, &n);
348         if (r < 0)
349                 return r;
350
351         label = strdup(rvalue);
352         if (!label)
353                 return log_oom();
354
355         if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
356                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
357                            "Interface label is not ASCII clean or is too"
358                            " long, ignoring assignment: %s", rvalue);
359                 free(label);
360                 return 0;
361         }
362
363         free(n->label);
364         if (*label)
365                 n->label = label;
366         else {
367                 free(label);
368                 n->label = NULL;
369         }
370
371         n = NULL;
372
373         return 0;
374 }