networkd-network.c revision 4f2e437ad7b04bc0690d1f8202577cbf47acb215
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt/***
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt This file is part of systemd.
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt Copyright 2013 Tom Gundersen <teg@jklm.no>
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt systemd is free software; you can redistribute it and/or modify it
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt under the terms of the GNU Lesser General Public License as published by
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt the Free Software Foundation; either version 2.1 of the License, or
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt (at your option) any later version.
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt systemd is distributed in the hope that it will be useful, but
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt WITHOUT ANY WARRANTY; without even the implied warranty of
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt Lesser General Public License for more details.
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt You should have received a copy of the GNU Lesser General Public License
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt along with systemd; If not, see <http://www.gnu.org/licenses/>.
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt***/
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt#include <ctype.h>
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt#include <net/if.h>
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt#include "conf-files.h"
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt#include "conf-parser.h"
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt#include "util.h"
3ad0c5d8a4e2e2fa7ffcccd7f3457f577908494eTom Gundersen#include "hostname-util.h"
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt#include "dns-domain.h"
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt#include "network-internal.h"
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt#include "networkd.h"
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt#include "networkd-network.h"
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flyktstatic int network_load_one(Manager *manager, const char *filename) {
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt _cleanup_network_free_ Network *network = NULL;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt _cleanup_fclose_ FILE *file = NULL;
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen char *d;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt Route *route;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt Address *address;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt int r;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt assert(manager);
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt assert(filename);
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt file = fopen(filename, "re");
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt if (!file) {
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt if (errno == ENOENT)
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt return 0;
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt else
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt return -errno;
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt }
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt if (null_or_empty_fd(fileno(file))) {
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt log_debug("Skipping empty file: %s", filename);
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering return 0;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt }
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network = new0(Network, 1);
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt if (!network)
200a0868fcdf7b95f3d8d1fda3aa2aef48d84fddTom Gundersen return log_oom();
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->manager = manager;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt LIST_HEAD_INIT(network->static_addresses);
36c32f6120a0c3fe19be5aeaa1926e179e8c29baTom Gundersen LIST_HEAD_INIT(network->static_routes);
6d8f6b0b2ae14aee0b02c7e3d1edaeaa2c118056Tom Gundersen LIST_HEAD_INIT(network->static_fdb_entries);
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->stacked_netdevs = hashmap_new(&string_hash_ops);
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt if (!network->stacked_netdevs)
f0213e3796b4dd66e546e2de4d677db319f9171bTom Gundersen return log_oom();
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->addresses_by_section = hashmap_new(NULL);
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt if (!network->addresses_by_section)
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt return log_oom();
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
851c9f82736c89d423b244a292e153ec7124d309Patrik Flykt network->routes_by_section = hashmap_new(NULL);
851c9f82736c89d423b244a292e153ec7124d309Patrik Flykt if (!network->routes_by_section)
6d8f6b0b2ae14aee0b02c7e3d1edaeaa2c118056Tom Gundersen return log_oom();
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->fdb_entries_by_section = hashmap_new(NULL);
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt if (!network->fdb_entries_by_section)
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt return log_oom();
f2341e0a87cab1558c84c933956e9181d5fb6c52Lennart Poettering
4d7b83da7b78647f4ba3f1d6fa2dc8d7b9833d93Tom Gundersen network->filename = strdup(filename);
4d7b83da7b78647f4ba3f1d6fa2dc8d7b9833d93Tom Gundersen if (!network->filename)
f2341e0a87cab1558c84c933956e9181d5fb6c52Lennart Poettering return log_oom();
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
6666907869fb3bc7fe6a6025540db5b887c7a78bTom Gundersen network->name = strdup(basename(filename));
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt if (!network->name)
f2341e0a87cab1558c84c933956e9181d5fb6c52Lennart Poettering return log_oom();
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt d = strrchr(network->name, '.');
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt if (!d)
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt return -EINVAL;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt assert(streq(d, ".network"));
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt *d = '\0';
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->dhcp = ADDRESS_FAMILY_NO;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->dhcp_ntp = true;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->dhcp_dns = true;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->dhcp_hostname = true;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->dhcp_routes = true;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->dhcp_sendhost = true;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->dhcp_route_metric = DHCP_ROUTE_METRIC;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->dhcp_server_emit_dns = true;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->dhcp_server_emit_ntp = true;
6d8f6b0b2ae14aee0b02c7e3d1edaeaa2c118056Tom Gundersen network->dhcp_server_emit_timezone = true;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->use_bpdu = true;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->allow_port_to_be_root = true;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->unicast_flood = true;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->llmnr = RESOLVE_SUPPORT_YES;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt network->link_local = ADDRESS_FAMILY_IPV6;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt network->ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt network->ipv6_accept_ra = -1;
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt r = config_parse(NULL, filename, file,
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt "Match\0"
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt "Link\0"
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt "Network\0"
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt "Address\0"
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt "Route\0"
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt "DHCP\0"
10c9ce615d98e125bc520efa94aebaef250a4061David Herrmann "DHCPv4\0" /* compat */
10c9ce615d98e125bc520efa94aebaef250a4061David Herrmann "DHCPServer\0"
10c9ce615d98e125bc520efa94aebaef250a4061David Herrmann "Bridge\0"
18d29550b5fbc4b0de334b8212d05decdd131f1bPatrik Flykt "BridgeFDB\0",
18d29550b5fbc4b0de334b8212d05decdd131f1bPatrik Flykt config_item_perf_lookup, network_network_gperf_lookup,
18d29550b5fbc4b0de334b8212d05decdd131f1bPatrik Flykt false, false, true, network);
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt if (r < 0)
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt return r;
10c9ce615d98e125bc520efa94aebaef250a4061David Herrmann
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt /* IPMasquerade=yes implies IPForward=yes */
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt if (network->ip_masquerade)
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt network->ip_forward |= ADDRESS_FAMILY_IPV4;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt LIST_PREPEND(networks, manager->networks, network);
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
10c9ce615d98e125bc520efa94aebaef250a4061David Herrmann if (r < 0)
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt return r;
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt r = hashmap_put(manager->networks_by_name, network->name, network);
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt if (r < 0)
c62c4628d9dbc27effd36143c75abe528f561867Patrik Flykt return r;
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
18d29550b5fbc4b0de334b8212d05decdd131f1bPatrik Flykt LIST_FOREACH(routes, route, network->static_routes) {
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt if (!route->family) {
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt log_warning("Route section without Gateway field configured in %s. "
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt "Ignoring", filename);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt return 0;
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering }
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt }
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt LIST_FOREACH(addresses, address, network->static_addresses) {
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt if (!address->family) {
18d29550b5fbc4b0de334b8212d05decdd131f1bPatrik Flykt log_warning("Address section without Address field configured in %s. "
8012cd391932d58b44332df106d426a360faf0a6Tom Gundersen "Ignoring", filename);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt return 0;
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt }
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen }
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt network = NULL;
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt return 0;
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt}
18d29550b5fbc4b0de334b8212d05decdd131f1bPatrik Flykt
18d29550b5fbc4b0de334b8212d05decdd131f1bPatrik Flyktint network_load(Manager *manager) {
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt Network *network;
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen _cleanup_strv_free_ char **files = NULL;
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt char **f;
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering int r;
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt assert(manager);
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen while ((network = manager->networks))
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt network_free(network);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt if (r < 0)
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt return log_error_errno(r, "Failed to enumerate network files: %m");
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen STRV_FOREACH_BACKWARDS(f, files) {
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt r = network_load_one(manager, *f);
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering if (r < 0)
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt return r;
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt }
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt return 0;
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt}
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flyktvoid network_free(Network *network) {
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering NetDev *netdev;
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt Route *route;
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt Address *address;
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt FdbEntry *fdb_entry;
18d29550b5fbc4b0de334b8212d05decdd131f1bPatrik Flykt Iterator i;
18d29550b5fbc4b0de334b8212d05decdd131f1bPatrik Flykt
18d29550b5fbc4b0de334b8212d05decdd131f1bPatrik Flykt if (!network)
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt return;
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt free(network->filename);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt free(network->match_mac);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt strv_free(network->match_path);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt strv_free(network->match_driver);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt strv_free(network->match_type);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt strv_free(network->match_name);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt free(network->description);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt free(network->dhcp_vendor_class_identifier);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt free(network->hostname);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt free(network->mac);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt strv_free(network->ntp);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt strv_free(network->dns);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt strv_free(network->domains);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt strv_free(network->bind_carrier);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt netdev_unref(network->bridge);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt netdev_unref(network->bond);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen hashmap_remove(network->stacked_netdevs, netdev->ifname);
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen netdev_unref(netdev);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt }
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt hashmap_free(network->stacked_netdevs);
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt
85bd849f09aceb7f972a0697494ea22b2247a5d7Patrik Flykt while ((route = network->static_routes))
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt route_free(route);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt while ((address = network->static_addresses))
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt address_free(address);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt while ((fdb_entry = network->static_fdb_entries))
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt fdb_entry_free(fdb_entry);
e66040417b52be98d41ba1230f25dea65147e8eePatrik Flykt
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt hashmap_free(network->addresses_by_section);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt hashmap_free(network->routes_by_section);
5c79bd79839f1e50bd3c34a0670037f7965ca5a4Patrik Flykt hashmap_free(network->fdb_entries_by_section);
a13c50e7a33e2b8e0481f725c6272142e6f71751Tom Gundersen
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt if (network->manager) {
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt if (network->manager->networks)
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt LIST_REMOVE(networks, network->manager->networks, network);
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt if (network->manager->networks_by_name)
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt hashmap_remove(network->manager->networks_by_name, network->name);
de1e9928f137f4d17f463956a7612d9676c393aaTom Gundersen }
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt free(network->name);
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt condition_free_list(network->match_host);
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt condition_free_list(network->match_virt);
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt condition_free_list(network->match_kernel);
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt condition_free_list(network->match_arch);
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt
4d7b83da7b78647f4ba3f1d6fa2dc8d7b9833d93Tom Gundersen free(network->dhcp_server_timezone);
4d7b83da7b78647f4ba3f1d6fa2dc8d7b9833d93Tom Gundersen free(network->dhcp_server_dns);
f2341e0a87cab1558c84c933956e9181d5fb6c52Lennart Poettering free(network->dhcp_server_ntp);
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt free(network);
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt}
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flyktint network_get_by_name(Manager *manager, const char *name, Network **ret) {
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt Network *network;
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt
4d7b83da7b78647f4ba3f1d6fa2dc8d7b9833d93Tom Gundersen assert(manager);
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt assert(name);
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt assert(ret);
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt network = hashmap_get(manager->networks_by_name, name);
4d7b83da7b78647f4ba3f1d6fa2dc8d7b9833d93Tom Gundersen if (!network)
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt return -ENOENT;
6d8f6b0b2ae14aee0b02c7e3d1edaeaa2c118056Tom Gundersen
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt *ret = network;
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt return 0;
be3a09b7ffe62b52658e77ae4d6638d1b0dae654Patrik Flykt}
int network_get(Manager *manager, struct udev_device *device,
const char *ifname, const struct ether_addr *address,
Network **ret) {
Network *network;
struct udev_device *parent;
const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL;
assert(manager);
assert(ret);
if (device) {
path = udev_device_get_property_value(device, "ID_PATH");
parent = udev_device_get_parent(device);
if (parent)
parent_driver = udev_device_get_driver(parent);
driver = udev_device_get_property_value(device, "ID_NET_DRIVER");
devtype = udev_device_get_devtype(device);
}
LIST_FOREACH(networks, network, manager->networks) {
if (net_match_config(network->match_mac, network->match_path,
network->match_driver, network->match_type,
network->match_name, network->match_host,
network->match_virt, network->match_kernel,
network->match_arch,
address, path, parent_driver, driver,
devtype, ifname)) {
if (network->match_name && device) {
const char *attr;
uint8_t name_assign_type = NET_NAME_UNKNOWN;
attr = udev_device_get_sysattr_value(device, "name_assign_type");
if (attr)
(void) safe_atou8(attr, &name_assign_type);
if (name_assign_type == NET_NAME_ENUM)
log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname",
IFNAMSIZ, ifname, network->filename);
else
log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
} else
log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename);
*ret = network;
return 0;
}
}
*ret = NULL;
return -ENOENT;
}
int network_apply(Manager *manager, Network *network, Link *link) {
int r;
link->network = network;
if (network->ipv4ll_route) {
Route *route;
r = route_new_static(network, 0, &route);
if (r < 0)
return r;
r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
if (r == 0)
return -EINVAL;
if (r < 0)
return -errno;
route->family = AF_INET;
route->dst_prefixlen = 16;
route->scope = RT_SCOPE_LINK;
route->metrics = IPV4LL_ROUTE_METRIC;
route->protocol = RTPROT_STATIC;
}
if (network->dns || network->ntp) {
r = link_save(link);
if (r < 0)
return r;
}
return 0;
}
int config_parse_netdev(const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Network *network = userdata;
_cleanup_free_ char *kind_string = NULL;
char *p;
NetDev *netdev;
NetDevKind kind;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
kind_string = strdup(lvalue);
if (!kind_string)
return log_oom();
/* the keys are CamelCase versions of the kind */
for (p = kind_string; *p; p++)
*p = tolower(*p);
kind = netdev_kind_from_string(kind_string);
if (kind == _NETDEV_KIND_INVALID) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Invalid NetDev kind: %s", lvalue);
return 0;
}
r = netdev_get(network->manager, rvalue, &netdev);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"%s could not be found, ignoring assignment: %s", lvalue, rvalue);
return 0;
}
if (netdev->kind != kind) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
return 0;
}
switch (kind) {
case NETDEV_KIND_BRIDGE:
network->bridge = netdev;
break;
case NETDEV_KIND_BOND:
network->bond = netdev;
break;
case NETDEV_KIND_VLAN:
case NETDEV_KIND_MACVLAN:
case NETDEV_KIND_MACVTAP:
case NETDEV_KIND_IPVLAN:
case NETDEV_KIND_VXLAN:
r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Can not add VLAN '%s' to network: %m",
rvalue);
return 0;
}
break;
default:
assert_not_reached("Can not parse NetDev");
}
netdev_ref(netdev);
return 0;
}
int config_parse_domains(const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Network *network = userdata;
char ***domains = data;
char **domain;
int r;
r = config_parse_strv(unit, filename, line, section, section_line,
lvalue, ltype, rvalue, domains, userdata);
if (r < 0)
return r;
strv_uniq(*domains);
network->wildcard_domain = !!strv_find(*domains, "*");
STRV_FOREACH(domain, *domains) {
if (is_localhost(*domain))
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
else {
r = dns_name_is_valid(*domain);
if (r <= 0 && !streq(*domain, "*")) {
if (r < 0)
log_error_errno(r, "Failed to validate domain name: %s: %m", *domain);
if (r == 0)
log_warning("Domain name is not valid, ignoring assignment: %s", *domain);
} else
continue;
}
strv_remove(*domains, *domain);
/* We removed one entry, make sure we don't skip the next one */
domain--;
}
return 0;
}
int config_parse_tunnel(const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Network *network = userdata;
NetDev *netdev;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = netdev_get(network->manager, rvalue, &netdev);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue);
return 0;
}
if (netdev->kind != NETDEV_KIND_IPIP &&
netdev->kind != NETDEV_KIND_SIT &&
netdev->kind != NETDEV_KIND_GRE &&
netdev->kind != NETDEV_KIND_GRETAP &&
netdev->kind != NETDEV_KIND_IP6GRE &&
netdev->kind != NETDEV_KIND_IP6GRETAP &&
netdev->kind != NETDEV_KIND_VTI &&
netdev->kind != NETDEV_KIND_VTI6 &&
netdev->kind != NETDEV_KIND_IP6TNL
) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"NetDev is not a tunnel, ignoring assignment: %s", rvalue);
return 0;
}
r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue);
return 0;
}
netdev_ref(netdev);
return 0;
}
int config_parse_ipv4ll(
const char* unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
AddressFamilyBoolean *link_local = data;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
/* Note that this is mostly like
* config_parse_address_family_boolean(), except that it
* applies only to IPv4 */
if (parse_boolean(rvalue))
*link_local |= ADDRESS_FAMILY_IPV4;
else
*link_local &= ~ADDRESS_FAMILY_IPV4;
return 0;
}
int config_parse_dhcp(
const char* unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
AddressFamilyBoolean *dhcp = data, s;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
/* Note that this is mostly like
* config_parse_address_family_boolean(), except that it
* understands some old names for the enum values */
s = address_family_boolean_from_string(rvalue);
if (s < 0) {
/* Previously, we had a slightly different enum here,
* support its values for compatbility. */
if (streq(rvalue, "none"))
s = ADDRESS_FAMILY_NO;
else if (streq(rvalue, "v4"))
s = ADDRESS_FAMILY_IPV4;
else if (streq(rvalue, "v6"))
s = ADDRESS_FAMILY_IPV6;
else if (streq(rvalue, "both"))
s = ADDRESS_FAMILY_YES;
else {
log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse DHCP option, ignoring: %s", rvalue);
return 0;
}
}
*dhcp = s;
return 0;
}
static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
[DHCP_CLIENT_ID_MAC] = "mac",
[DHCP_CLIENT_ID_DUID] = "duid"
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier);
DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type");
int config_parse_ipv6token(
const char* unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
union in_addr_union buffer;
struct in6_addr *token = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(token);
r = in_addr_from_string(AF_INET6, rvalue, &buffer);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue);
return 0;
}
r = in_addr_is_null(AF_INET6, &buffer);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue);
return 0;
}
if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue);
return 0;
}
*token = buffer.in6;
return 0;
}
static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = {
[IPV6_PRIVACY_EXTENSIONS_NO] = "no",
[IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public",
[IPV6_PRIVACY_EXTENSIONS_YES] = "yes",
};
DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions);
int config_parse_ipv6_privacy_extensions(
const char* unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
IPv6PrivacyExtensions *ipv6_privacy_extensions = data;
int k;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(ipv6_privacy_extensions);
/* Our enum shall be a superset of booleans, hence first try
* to parse as boolean, and then as enum */
k = parse_boolean(rvalue);
if (k > 0)
*ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_YES;
else if (k == 0)
*ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO;
else {
IPv6PrivacyExtensions s;
s = ipv6_privacy_extensions_from_string(rvalue);
if (s < 0) {
if (streq(rvalue, "kernel"))
s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
else {
log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
return 0;
}
}
*ipv6_privacy_extensions = s;
}
return 0;
}
int config_parse_hostname(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char **hostname = data, *hn = NULL;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
if (r < 0)
return r;
if (!hostname_is_valid(hn, false)) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Hostname is not valid, ignoring assignment: %s", rvalue);
free(hn);
return 0;
}
free(*hostname);
*hostname = hostname_cleanup(hn);
return 0;
}
int config_parse_timezone(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char **datap = data, *tz = NULL;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
if (r < 0)
return r;
if (!timezone_is_valid(tz)) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Timezone is not valid, ignoring assignment: %s", rvalue);
free(tz);
return 0;
}
free(*datap);
*datap = tz;
return 0;
}
int config_parse_dhcp_server_dns(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Network *n = data;
const char *p = rvalue;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
for (;;) {
_cleanup_free_ char *w = NULL;
struct in_addr a, *m;
r = extract_first_word(&p, &w, NULL, 0);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue);
return 0;
}
if (r == 0)
return 0;
if (inet_pton(AF_INET, w, &a) <= 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server address, ignoring: %s", w);
continue;
}
m = realloc(n->dhcp_server_dns, (n->n_dhcp_server_dns + 1) * sizeof(struct in_addr));
if (!m)
return log_oom();
m[n->n_dhcp_server_dns++] = a;
n->dhcp_server_dns = m;
}
}
int config_parse_dhcp_server_ntp(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Network *n = data;
const char *p = rvalue;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
for (;;) {
_cleanup_free_ char *w = NULL;
struct in_addr a, *m;
r = extract_first_word(&p, &w, NULL, 0);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, r, line, "Failed to extract word, ignoring: %s", rvalue);
return 0;
}
if (r == 0)
return 0;
if (inet_pton(AF_INET, w, &a) <= 0) {
log_syntax(unit, LOG_ERR, filename, r, line, "Failed to parse NTP server address, ignoring: %s", w);
continue;
}
m = realloc(n->dhcp_server_ntp, (n->n_dhcp_server_ntp + 1) * sizeof(struct in_addr));
if (!m)
return log_oom();
m[n->n_dhcp_server_ntp++] = a;
n->dhcp_server_ntp = m;
}
}