]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-network.c
update TODO
[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),
7eb08da4 228 ifname, false)) {
97578344 229 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
505f8da7 230 network->filename);
f579559b
TG
231 *ret = network;
232 return 0;
233 }
234 }
235
236 *ret = NULL;
237
238 return -ENOENT;
239}
240
241int network_apply(Manager *manager, Network *network, Link *link) {
f579559b
TG
242 int r;
243
f579559b
TG
244 link->network = network;
245
bfa695b5
TG
246 if (network->ipv4ll_route) {
247 Route *route;
248
249 r = route_new_static(network, 0, &route);
250 if (r < 0)
251 return r;
252
253 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
254 if (r == 0)
255 return -EINVAL;
256 if (r < 0)
257 return -errno;
258
259 route->family = AF_INET;
260 route->dst_prefixlen = 16;
261 route->scope = RT_SCOPE_LINK;
262 route->metrics = IPV4LL_ROUTE_METRIC;
263 route->protocol = RTPROT_STATIC;
264 }
265
bcb7a07e 266 if (network->dns || network->ntp) {
091a364c 267 r = link_save(link);
3bef724f
TG
268 if (r < 0)
269 return r;
270 }
271
f579559b
TG
272 return 0;
273}
02b59d57 274
69a93e7d 275int config_parse_netdev(const char *unit,
02b59d57
TG
276 const char *filename,
277 unsigned line,
278 const char *section,
279 unsigned section_line,
280 const char *lvalue,
281 int ltype,
282 const char *rvalue,
283 void *data,
284 void *userdata) {
285 Network *network = userdata;
31d0ac36
TG
286 _cleanup_free_ char *kind_string = NULL;
287 char *p;
1a436809 288 NetDev *netdev;
69a93e7d 289 NetDevKind kind;
02b59d57
TG
290 int r;
291
292 assert(filename);
293 assert(lvalue);
294 assert(rvalue);
295 assert(data);
296
69a93e7d
TG
297 kind_string = strdup(lvalue);
298 if (!kind_string)
299 return log_oom();
52433f6b 300
69a93e7d
TG
301 /* the keys are CamelCase versions of the kind */
302 for (p = kind_string; *p; p++)
303 *p = tolower(*p);
52433f6b 304
69a93e7d
TG
305 kind = netdev_kind_from_string(kind_string);
306 if (kind == _NETDEV_KIND_INVALID) {
52433f6b 307 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 308 "Invalid NetDev kind: %s", lvalue);
52433f6b
TG
309 return 0;
310 }
311
54abf461
TG
312 r = netdev_get(network->manager, rvalue, &netdev);
313 if (r < 0) {
314 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 315 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
316 return 0;
317 }
318
69a93e7d 319 if (netdev->kind != kind) {
54abf461 320 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 321 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
322 return 0;
323 }
324
69a93e7d
TG
325 switch (kind) {
326 case NETDEV_KIND_BRIDGE:
327 network->bridge = netdev;
54abf461 328
69a93e7d
TG
329 break;
330 case NETDEV_KIND_BOND:
331 network->bond = netdev;
fe6b2d55 332
69a93e7d
TG
333 break;
334 case NETDEV_KIND_VLAN:
69a93e7d 335 case NETDEV_KIND_MACVLAN:
326cb406 336 case NETDEV_KIND_VXLAN:
6a0a2f86 337 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
326cb406
SS
338 if (r < 0) {
339 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
6a0a2f86 340 "Can not add VLAN '%s' to network: %s",
aa9f1140 341 rvalue, strerror(-r));
326cb406
SS
342 return 0;
343 }
344
69a93e7d
TG
345 break;
346 default:
347 assert_not_reached("Can not parse NetDev");
fe6b2d55
TG
348 }
349
47e2dc31
TG
350 netdev_ref(netdev);
351
fe6b2d55
TG
352 return 0;
353}
7951dea2 354
6192b846
TG
355int config_parse_domains(const char *unit,
356 const char *filename,
357 unsigned line,
358 const char *section,
359 unsigned section_line,
360 const char *lvalue,
361 int ltype,
362 const char *rvalue,
363 void *data,
364 void *userdata) {
67272d15 365 Network *network = userdata;
6192b846
TG
366 char ***domains = data;
367 char **domain;
368 int r;
369
370 r = config_parse_strv(unit, filename, line, section, section_line,
371 lvalue, ltype, rvalue, domains, userdata);
372 if (r < 0)
373 return r;
374
375 strv_uniq(*domains);
f15b6e5a 376 network->wildcard_domain = !!strv_find(*domains, "*");
67272d15 377
40274ed6
LP
378 STRV_FOREACH(domain, *domains) {
379 if (is_localhost(*domain))
380 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
381 else if (!hostname_is_valid(*domain)) {
382 if (!streq(*domain, "*"))
383 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
384 } else
385 continue;
386
387 strv_remove(*domains, *domain);
388
389 /* We removed one entry, make sure we don't skip the next one */
390 domain--;
391 }
f15b6e5a 392
6192b846
TG
393 return 0;
394}
395
7951dea2
SS
396int config_parse_tunnel(const char *unit,
397 const char *filename,
398 unsigned line,
399 const char *section,
400 unsigned section_line,
401 const char *lvalue,
402 int ltype,
403 const char *rvalue,
404 void *data,
405 void *userdata) {
406 Network *network = userdata;
407 NetDev *netdev;
408 int r;
409
410 assert(filename);
411 assert(lvalue);
412 assert(rvalue);
413 assert(data);
414
415 r = netdev_get(network->manager, rvalue, &netdev);
416 if (r < 0) {
417 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
418 "Tunnel is invalid, ignoring assignment: %s", rvalue);
419 return 0;
420 }
421
422 if (netdev->kind != NETDEV_KIND_IPIP &&
423 netdev->kind != NETDEV_KIND_SIT &&
a613382b
SS
424 netdev->kind != NETDEV_KIND_GRE &&
425 netdev->kind != NETDEV_KIND_VTI) {
7951dea2
SS
426 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
427 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
428 return 0;
429 }
430
6a0a2f86
TG
431 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
432 if (r < 0) {
433 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
434 "Can not add VLAN '%s' to network: %s",
435 rvalue, strerror(-r));
436 return 0;
437 }
438
439 netdev_ref(netdev);
7951dea2
SS
440
441 return 0;
442}
bd8f6538
TG
443
444static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
445 [DHCP_SUPPORT_NONE] = "none",
446 [DHCP_SUPPORT_BOTH] = "both",
447 [DHCP_SUPPORT_V4] = "v4",
448 [DHCP_SUPPORT_V6] = "v6",
449};
450
451DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
452
453int config_parse_dhcp(
454 const char* unit,
455 const char *filename,
456 unsigned line,
457 const char *section,
458 unsigned section_line,
459 const char *lvalue,
460 int ltype,
461 const char *rvalue,
462 void *data,
463 void *userdata) {
464
465 DHCPSupport *dhcp = data;
466 int k;
467
468 assert(filename);
469 assert(lvalue);
470 assert(rvalue);
471 assert(data);
472
473 /* Our enum shall be a superset of booleans, hence first try
474 * to parse as boolean, and then as enum */
475
476 k = parse_boolean(rvalue);
477 if (k > 0)
478 *dhcp = DHCP_SUPPORT_BOTH;
479 else if (k == 0)
480 *dhcp = DHCP_SUPPORT_NONE;
481 else {
482 DHCPSupport s;
483
484 s = dhcp_support_from_string(rvalue);
485 if (s < 0){
486 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
487 return 0;
488 }
489
490 *dhcp = s;
491 }
492
493 return 0;
494}
495
496static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
497 [LLMNR_SUPPORT_NO] = "no",
498 [LLMNR_SUPPORT_YES] = "yes",
499 [LLMNR_SUPPORT_RESOLVE] = "resolve",
500};
501
502DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
503
504int config_parse_llmnr(
505 const char* unit,
506 const char *filename,
507 unsigned line,
508 const char *section,
509 unsigned section_line,
510 const char *lvalue,
511 int ltype,
512 const char *rvalue,
513 void *data,
514 void *userdata) {
515
516 LLMNRSupport *llmnr = data;
517 int k;
518
519 assert(filename);
520 assert(lvalue);
521 assert(rvalue);
522 assert(data);
523
524 /* Our enum shall be a superset of booleans, hence first try
525 * to parse as boolean, and then as enum */
526
527 k = parse_boolean(rvalue);
528 if (k > 0)
529 *llmnr = LLMNR_SUPPORT_YES;
530 else if (k == 0)
531 *llmnr = LLMNR_SUPPORT_NO;
532 else {
533 LLMNRSupport s;
534
535 s = llmnr_support_from_string(rvalue);
536 if (s < 0){
537 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
538 return 0;
539 }
540
541 *llmnr = s;
542 }
543
544 return 0;
545}