]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/network/networkd-network.c
treewide: yet more log_*_errno + return simplifications
[thirdparty/systemd.git] / src / network / networkd-network.c
... / ...
CommitLineData
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 <ctype.h>
23#include <net/if.h>
24
25#include "networkd.h"
26#include "networkd-netdev.h"
27#include "networkd-link.h"
28#include "network-internal.h"
29#include "path-util.h"
30#include "conf-files.h"
31#include "conf-parser.h"
32#include "util.h"
33
34static int network_load_one(Manager *manager, const char *filename) {
35 _cleanup_network_free_ Network *network = NULL;
36 _cleanup_fclose_ FILE *file = NULL;
37 Route *route;
38 Address *address;
39 int r;
40
41 assert(manager);
42 assert(filename);
43
44 file = fopen(filename, "re");
45 if (!file) {
46 if (errno == ENOENT)
47 return 0;
48 else
49 return -errno;
50 }
51
52 if (null_or_empty_fd(fileno(file))) {
53 log_debug("Skipping empty file: %s", filename);
54 return 0;
55 }
56
57 network = new0(Network, 1);
58 if (!network)
59 return log_oom();
60
61 network->manager = manager;
62
63 LIST_HEAD_INIT(network->static_addresses);
64 LIST_HEAD_INIT(network->static_routes);
65
66 network->stacked_netdevs = hashmap_new(&string_hash_ops);
67 if (!network->stacked_netdevs)
68 return log_oom();
69
70 network->addresses_by_section = hashmap_new(NULL);
71 if (!network->addresses_by_section)
72 return log_oom();
73
74 network->routes_by_section = hashmap_new(NULL);
75 if (!network->routes_by_section)
76 return log_oom();
77
78 network->filename = strdup(filename);
79 if (!network->filename)
80 return log_oom();
81
82 network->dhcp = DHCP_SUPPORT_NONE;
83 network->dhcp_ntp = true;
84 network->dhcp_dns = true;
85 network->dhcp_hostname = true;
86 network->dhcp_routes = true;
87 network->dhcp_sendhost = true;
88 network->dhcp_route_metric = DHCP_ROUTE_METRIC;
89
90 network->llmnr = LLMNR_SUPPORT_YES;
91
92 r = config_parse(NULL, filename, file,
93 "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0",
94 config_item_perf_lookup, network_network_gperf_lookup,
95 false, false, true, network);
96 if (r < 0)
97 return r;
98
99 LIST_PREPEND(networks, manager->networks, network);
100
101 LIST_FOREACH(routes, route, network->static_routes) {
102 if (!route->family) {
103 log_warning("Route section without Gateway field configured in %s. "
104 "Ignoring", filename);
105 return 0;
106 }
107 }
108
109 LIST_FOREACH(addresses, address, network->static_addresses) {
110 if (!address->family) {
111 log_warning("Address section without Address field configured in %s. "
112 "Ignoring", filename);
113 return 0;
114 }
115 }
116
117 network = NULL;
118
119 return 0;
120}
121
122int network_load(Manager *manager) {
123 Network *network;
124 _cleanup_strv_free_ char **files = NULL;
125 char **f;
126 int r;
127
128 assert(manager);
129
130 while ((network = manager->networks))
131 network_free(network);
132
133 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
134 if (r < 0)
135 return log_error_errno(r, "Failed to enumerate network files: %m");
136
137 STRV_FOREACH_BACKWARDS(f, files) {
138 r = network_load_one(manager, *f);
139 if (r < 0)
140 return r;
141 }
142
143 return 0;
144}
145
146void network_free(Network *network) {
147 NetDev *netdev;
148 Route *route;
149 Address *address;
150 Iterator i;
151
152 if (!network)
153 return;
154
155 free(network->filename);
156
157 free(network->match_mac);
158 free(network->match_path);
159 free(network->match_driver);
160 free(network->match_type);
161 free(network->match_name);
162
163 free(network->description);
164 free(network->dhcp_vendor_class_identifier);
165
166 strv_free(network->ntp);
167 strv_free(network->dns);
168 strv_free(network->domains);
169
170 netdev_unref(network->bridge);
171
172 netdev_unref(network->bond);
173
174 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
175 hashmap_remove(network->stacked_netdevs, netdev->ifname);
176 netdev_unref(netdev);
177 }
178 hashmap_free(network->stacked_netdevs);
179
180 while ((route = network->static_routes))
181 route_free(route);
182
183 while ((address = network->static_addresses))
184 address_free(address);
185
186 hashmap_free(network->addresses_by_section);
187 hashmap_free(network->routes_by_section);
188
189 if (network->manager && network->manager->networks)
190 LIST_REMOVE(networks, network->manager->networks, network);
191
192 condition_free_list(network->match_host);
193 condition_free_list(network->match_virt);
194 condition_free_list(network->match_kernel);
195 condition_free_list(network->match_arch);
196
197 free(network);
198}
199
200int network_get(Manager *manager, struct udev_device *device,
201 const char *ifname, const struct ether_addr *address,
202 Network **ret) {
203 Network *network;
204
205 assert(manager);
206 assert(ret);
207
208 LIST_FOREACH(networks, network, manager->networks) {
209 if (net_match_config(network->match_mac, network->match_path,
210 network->match_driver, network->match_type,
211 network->match_name, network->match_host,
212 network->match_virt, network->match_kernel,
213 network->match_arch,
214 address,
215 udev_device_get_property_value(device, "ID_PATH"),
216 udev_device_get_driver(udev_device_get_parent(device)),
217 udev_device_get_property_value(device, "ID_NET_DRIVER"),
218 udev_device_get_devtype(device),
219 ifname)) {
220 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
221 network->filename);
222 *ret = network;
223 return 0;
224 }
225 }
226
227 *ret = NULL;
228
229 return -ENOENT;
230}
231
232int network_apply(Manager *manager, Network *network, Link *link) {
233 int r;
234
235 link->network = network;
236
237 if (network->ipv4ll_route) {
238 Route *route;
239
240 r = route_new_static(network, 0, &route);
241 if (r < 0)
242 return r;
243
244 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
245 if (r == 0)
246 return -EINVAL;
247 if (r < 0)
248 return -errno;
249
250 route->family = AF_INET;
251 route->dst_prefixlen = 16;
252 route->scope = RT_SCOPE_LINK;
253 route->metrics = IPV4LL_ROUTE_METRIC;
254 route->protocol = RTPROT_STATIC;
255 }
256
257 if (network->dns || network->ntp) {
258 r = link_save(link);
259 if (r < 0)
260 return r;
261 }
262
263 return 0;
264}
265
266int config_parse_netdev(const char *unit,
267 const char *filename,
268 unsigned line,
269 const char *section,
270 unsigned section_line,
271 const char *lvalue,
272 int ltype,
273 const char *rvalue,
274 void *data,
275 void *userdata) {
276 Network *network = userdata;
277 _cleanup_free_ char *kind_string = NULL;
278 char *p;
279 NetDev *netdev;
280 NetDevKind kind;
281 int r;
282
283 assert(filename);
284 assert(lvalue);
285 assert(rvalue);
286 assert(data);
287
288 kind_string = strdup(lvalue);
289 if (!kind_string)
290 return log_oom();
291
292 /* the keys are CamelCase versions of the kind */
293 for (p = kind_string; *p; p++)
294 *p = tolower(*p);
295
296 kind = netdev_kind_from_string(kind_string);
297 if (kind == _NETDEV_KIND_INVALID) {
298 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
299 "Invalid NetDev kind: %s", lvalue);
300 return 0;
301 }
302
303 r = netdev_get(network->manager, rvalue, &netdev);
304 if (r < 0) {
305 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
306 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
307 return 0;
308 }
309
310 if (netdev->kind != kind) {
311 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
312 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
313 return 0;
314 }
315
316 switch (kind) {
317 case NETDEV_KIND_BRIDGE:
318 network->bridge = netdev;
319
320 break;
321 case NETDEV_KIND_BOND:
322 network->bond = netdev;
323
324 break;
325 case NETDEV_KIND_VLAN:
326 case NETDEV_KIND_MACVLAN:
327 case NETDEV_KIND_VXLAN:
328 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
329 if (r < 0) {
330 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
331 "Can not add VLAN '%s' to network: %s",
332 rvalue, strerror(-r));
333 return 0;
334 }
335
336 break;
337 default:
338 assert_not_reached("Can not parse NetDev");
339 }
340
341 netdev_ref(netdev);
342
343 return 0;
344}
345
346int config_parse_domains(const char *unit,
347 const char *filename,
348 unsigned line,
349 const char *section,
350 unsigned section_line,
351 const char *lvalue,
352 int ltype,
353 const char *rvalue,
354 void *data,
355 void *userdata) {
356 Network *network = userdata;
357 char ***domains = data;
358 char **domain;
359 int r;
360
361 r = config_parse_strv(unit, filename, line, section, section_line,
362 lvalue, ltype, rvalue, domains, userdata);
363 if (r < 0)
364 return r;
365
366 strv_uniq(*domains);
367 network->wildcard_domain = !!strv_find(*domains, "*");
368
369 STRV_FOREACH(domain, *domains) {
370 if (is_localhost(*domain))
371 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
372 else if (!hostname_is_valid(*domain)) {
373 if (!streq(*domain, "*"))
374 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
375 } else
376 continue;
377
378 strv_remove(*domains, *domain);
379
380 /* We removed one entry, make sure we don't skip the next one */
381 domain--;
382 }
383
384 return 0;
385}
386
387int config_parse_tunnel(const char *unit,
388 const char *filename,
389 unsigned line,
390 const char *section,
391 unsigned section_line,
392 const char *lvalue,
393 int ltype,
394 const char *rvalue,
395 void *data,
396 void *userdata) {
397 Network *network = userdata;
398 NetDev *netdev;
399 int r;
400
401 assert(filename);
402 assert(lvalue);
403 assert(rvalue);
404 assert(data);
405
406 r = netdev_get(network->manager, rvalue, &netdev);
407 if (r < 0) {
408 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
409 "Tunnel is invalid, ignoring assignment: %s", rvalue);
410 return 0;
411 }
412
413 if (netdev->kind != NETDEV_KIND_IPIP &&
414 netdev->kind != NETDEV_KIND_SIT &&
415 netdev->kind != NETDEV_KIND_GRE &&
416 netdev->kind != NETDEV_KIND_VTI) {
417 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
418 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
419 return 0;
420 }
421
422 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
423 if (r < 0) {
424 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
425 "Can not add VLAN '%s' to network: %s",
426 rvalue, strerror(-r));
427 return 0;
428 }
429
430 netdev_ref(netdev);
431
432 return 0;
433}
434
435static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
436 [DHCP_SUPPORT_NONE] = "none",
437 [DHCP_SUPPORT_BOTH] = "both",
438 [DHCP_SUPPORT_V4] = "v4",
439 [DHCP_SUPPORT_V6] = "v6",
440};
441
442DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
443
444int config_parse_dhcp(
445 const char* unit,
446 const char *filename,
447 unsigned line,
448 const char *section,
449 unsigned section_line,
450 const char *lvalue,
451 int ltype,
452 const char *rvalue,
453 void *data,
454 void *userdata) {
455
456 DHCPSupport *dhcp = data;
457 int k;
458
459 assert(filename);
460 assert(lvalue);
461 assert(rvalue);
462 assert(data);
463
464 /* Our enum shall be a superset of booleans, hence first try
465 * to parse as boolean, and then as enum */
466
467 k = parse_boolean(rvalue);
468 if (k > 0)
469 *dhcp = DHCP_SUPPORT_BOTH;
470 else if (k == 0)
471 *dhcp = DHCP_SUPPORT_NONE;
472 else {
473 DHCPSupport s;
474
475 s = dhcp_support_from_string(rvalue);
476 if (s < 0){
477 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
478 return 0;
479 }
480
481 *dhcp = s;
482 }
483
484 return 0;
485}
486
487static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
488 [LLMNR_SUPPORT_NO] = "no",
489 [LLMNR_SUPPORT_YES] = "yes",
490 [LLMNR_SUPPORT_RESOLVE] = "resolve",
491};
492
493DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
494
495int config_parse_llmnr(
496 const char* unit,
497 const char *filename,
498 unsigned line,
499 const char *section,
500 unsigned section_line,
501 const char *lvalue,
502 int ltype,
503 const char *rvalue,
504 void *data,
505 void *userdata) {
506
507 LLMNRSupport *llmnr = data;
508 int k;
509
510 assert(filename);
511 assert(lvalue);
512 assert(rvalue);
513 assert(data);
514
515 /* Our enum shall be a superset of booleans, hence first try
516 * to parse as boolean, and then as enum */
517
518 k = parse_boolean(rvalue);
519 if (k > 0)
520 *llmnr = LLMNR_SUPPORT_YES;
521 else if (k == 0)
522 *llmnr = LLMNR_SUPPORT_NO;
523 else {
524 LLMNRSupport s;
525
526 s = llmnr_support_from_string(rvalue);
527 if (s < 0){
528 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
529 return 0;
530 }
531
532 *llmnr = s;
533 }
534
535 return 0;
536}