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