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