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