chiark / gitweb /
networkd: distinguish between static and dynamic addresses/routes
[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_address(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         Network *network = userdata;
204         _cleanup_address_free_ Address *n = NULL;
205         _cleanup_free_ char *address = NULL;
206         const char *e;
207         int r;
208
209         assert(filename);
210         assert(section);
211         assert(lvalue);
212         assert(rvalue);
213         assert(data);
214
215         if (streq(section, "Network")) {
216                 /* we are not in an Address section, so treat
217                  * this as the special '0' section */
218                 section_line = 0;
219         }
220
221         r = address_new_static(network, section_line, &n);
222         if (r < 0)
223                 return r;
224
225         /* Address=address/prefixlen */
226
227         /* prefixlen */
228         e = strchr(rvalue, '/');
229         if (e) {
230                 unsigned i;
231                 r = safe_atou(e + 1, &i);
232                 if (r < 0) {
233                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
234                                    "Interface prefix length is invalid, "
235                                    "ignoring assignment: %s", e + 1);
236                         return 0;
237                 }
238
239                 n->prefixlen = (unsigned char) i;
240                 n->netmask.s_addr = htonl(0xfffffffflu >> n->prefixlen);
241
242                 address = strndup(rvalue, e - rvalue);
243                 if (!address)
244                         return log_oom();
245         } else {
246                 address = strdup(rvalue);
247                 if (!address)
248                         return log_oom();
249         }
250
251         r = net_parse_inaddr(address, &n->family, &n->in_addr);
252         if (r < 0) {
253                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
254                            "Address is invalid, ignoring assignment: %s", address);
255                 return 0;
256         }
257
258         n = NULL;
259
260         return 0;
261 }
262
263 int config_parse_label(const char *unit,
264                 const char *filename,
265                 unsigned line,
266                 const char *section,
267                 unsigned section_line,
268                 const char *lvalue,
269                 int ltype,
270                 const char *rvalue,
271                 void *data,
272                 void *userdata) {
273         Network *network = userdata;
274         _cleanup_address_free_ Address *n = NULL;
275         char *label;
276         int r;
277
278         assert(filename);
279         assert(section);
280         assert(lvalue);
281         assert(rvalue);
282         assert(data);
283
284         r = address_new_static(network, section_line, &n);
285         if (r < 0)
286                 return r;
287
288         label = strdup(rvalue);
289         if (!label)
290                 return log_oom();
291
292         if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) {
293                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
294                            "Interface label is not ASCII clean or is too"
295                            " long, ignoring assignment: %s", rvalue);
296                 free(label);
297                 return 0;
298         }
299
300         free(n->label);
301         if (*label)
302                 n->label = label;
303         else {
304                 free(label);
305                 n->label = NULL;
306         }
307
308         n = NULL;
309
310         return 0;
311 }