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