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