]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-network.c
gitignore: ignore generated systemd-bootchart.service
[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,
e1853b00 93 "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0BridgePort\0",
e9f3d2d5 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);
f647962d
MS
134 if (r < 0)
135 return log_error_errno(r, "Failed to enumerate network files: %m");
f579559b
TG
136
137 STRV_FOREACH_BACKWARDS(f, files) {
138 r = network_load_one(manager, *f);
139 if (r < 0)
140 return r;
141 }
142
f579559b
TG
143 return 0;
144}
145
f579559b 146void network_free(Network *network) {
47e2dc31 147 NetDev *netdev;
f579559b
TG
148 Route *route;
149 Address *address;
06f021a8 150 Iterator i;
f579559b
TG
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);
edb85f0d 164 free(network->dhcp_vendor_class_identifier);
f579559b 165
b0e39c82
TG
166 strv_free(network->ntp);
167 strv_free(network->dns);
486d1a81 168 strv_free(network->domains);
3bef724f 169
47e2dc31
TG
170 netdev_unref(network->bridge);
171
172 netdev_unref(network->bond);
173
2c36be2f
TG
174 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
175 hashmap_remove(network->stacked_netdevs, netdev->ifname);
326cb406 176 netdev_unref(netdev);
2c36be2f 177 }
6a0a2f86 178 hashmap_free(network->stacked_netdevs);
326cb406 179
f048a16b 180 while ((route = network->static_routes))
f579559b
TG
181 route_free(route);
182
f048a16b 183 while ((address = network->static_addresses))
f579559b
TG
184 address_free(address);
185
6ae115c1
TG
186 hashmap_free(network->addresses_by_section);
187 hashmap_free(network->routes_by_section);
188
b3070dc0 189 if (network->manager && network->manager->networks)
7384fa92 190 LIST_REMOVE(networks, network->manager->networks, network);
f579559b 191
79e16ce3
LP
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
f579559b
TG
197 free(network);
198}
199
505f8da7
TG
200int network_get(Manager *manager, struct udev_device *device,
201 const char *ifname, const struct ether_addr *address,
202 Network **ret) {
f579559b
TG
203 Network *network;
204
205 assert(manager);
f579559b
TG
206 assert(ret);
207
f579559b
TG
208 LIST_FOREACH(networks, network, manager->networks) {
209 if (net_match_config(network->match_mac, network->match_path,
505f8da7
TG
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),
7eb08da4 219 ifname, false)) {
97578344 220 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
505f8da7 221 network->filename);
f579559b
TG
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) {
f579559b
TG
233 int r;
234
f579559b
TG
235 link->network = network;
236
bfa695b5
TG
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
bcb7a07e 257 if (network->dns || network->ntp) {
091a364c 258 r = link_save(link);
3bef724f
TG
259 if (r < 0)
260 return r;
261 }
262
f579559b
TG
263 return 0;
264}
02b59d57 265
69a93e7d 266int config_parse_netdev(const char *unit,
02b59d57
TG
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;
31d0ac36
TG
277 _cleanup_free_ char *kind_string = NULL;
278 char *p;
1a436809 279 NetDev *netdev;
69a93e7d 280 NetDevKind kind;
02b59d57
TG
281 int r;
282
283 assert(filename);
284 assert(lvalue);
285 assert(rvalue);
286 assert(data);
287
69a93e7d
TG
288 kind_string = strdup(lvalue);
289 if (!kind_string)
290 return log_oom();
52433f6b 291
69a93e7d
TG
292 /* the keys are CamelCase versions of the kind */
293 for (p = kind_string; *p; p++)
294 *p = tolower(*p);
52433f6b 295
69a93e7d
TG
296 kind = netdev_kind_from_string(kind_string);
297 if (kind == _NETDEV_KIND_INVALID) {
52433f6b 298 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 299 "Invalid NetDev kind: %s", lvalue);
52433f6b
TG
300 return 0;
301 }
302
54abf461
TG
303 r = netdev_get(network->manager, rvalue, &netdev);
304 if (r < 0) {
305 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 306 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
307 return 0;
308 }
309
69a93e7d 310 if (netdev->kind != kind) {
54abf461 311 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 312 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
313 return 0;
314 }
315
69a93e7d
TG
316 switch (kind) {
317 case NETDEV_KIND_BRIDGE:
318 network->bridge = netdev;
54abf461 319
69a93e7d
TG
320 break;
321 case NETDEV_KIND_BOND:
322 network->bond = netdev;
fe6b2d55 323
69a93e7d
TG
324 break;
325 case NETDEV_KIND_VLAN:
69a93e7d 326 case NETDEV_KIND_MACVLAN:
326cb406 327 case NETDEV_KIND_VXLAN:
6a0a2f86 328 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
326cb406
SS
329 if (r < 0) {
330 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
6a0a2f86 331 "Can not add VLAN '%s' to network: %s",
aa9f1140 332 rvalue, strerror(-r));
326cb406
SS
333 return 0;
334 }
335
69a93e7d
TG
336 break;
337 default:
338 assert_not_reached("Can not parse NetDev");
fe6b2d55
TG
339 }
340
47e2dc31
TG
341 netdev_ref(netdev);
342
fe6b2d55
TG
343 return 0;
344}
7951dea2 345
6192b846
TG
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) {
67272d15 356 Network *network = userdata;
6192b846
TG
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);
f15b6e5a 367 network->wildcard_domain = !!strv_find(*domains, "*");
67272d15 368
40274ed6
LP
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 }
f15b6e5a 383
6192b846
TG
384 return 0;
385}
386
7951dea2
SS
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 &&
a613382b
SS
415 netdev->kind != NETDEV_KIND_GRE &&
416 netdev->kind != NETDEV_KIND_VTI) {
7951dea2
SS
417 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
418 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
419 return 0;
420 }
421
6a0a2f86
TG
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);
7951dea2
SS
431
432 return 0;
433}
bd8f6538
TG
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}