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