networkd-network.c revision bfa695b5cc37aeb78737c57c84e9e69ea08152c0
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher/***
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher This file is part of systemd.
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher Copyright 2013 Tom Gundersen <teg@jklm.no>
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher systemd is free software; you can redistribute it and/or modify it
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher under the terms of the GNU Lesser General Public License as published by
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher the Free Software Foundation; either version 2.1 of the License, or
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher (at your option) any later version.
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher systemd is distributed in the hope that it will be useful, but
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher WITHOUT ANY WARRANTY; without even the implied warranty of
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher Lesser General Public License for more details.
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher You should have received a copy of the GNU Lesser General Public License
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher along with systemd; If not, see <http://www.gnu.org/licenses/>.
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher***/
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher#include <ctype.h>
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher#include <net/if.h>
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher#include "networkd.h"
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher#include "networkd-netdev.h"
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher#include "network-internal.h"
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher#include "path-util.h"
72ae534f5aef6d2e5d3f2f51299aede5abf9687eJakub Hrozek#include "conf-files.h"
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley#include "conf-parser.h"
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley#include "util.h"
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elleystatic int network_load_one(Manager *manager, const char *filename) {
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley _cleanup_network_free_ Network *network = NULL;
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley _cleanup_fclose_ FILE *file = NULL;
ff4b603cc14ea6ea15caaf89a03e927920124af4Yassir Elley Route *route;
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher Address *address;
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher int r;
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher assert(manager);
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher assert(filename);
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher
file = fopen(filename, "re");
if (!file) {
if (errno == ENOENT)
return 0;
else
return -errno;
}
if (null_or_empty_fd(fileno(file))) {
log_debug("Skipping empty file: %s", filename);
return 0;
}
network = new0(Network, 1);
if (!network)
return log_oom();
network->manager = manager;
LIST_HEAD_INIT(network->static_addresses);
LIST_HEAD_INIT(network->static_routes);
network->vlans = hashmap_new(string_hash_func, string_compare_func);
if (!network->vlans)
return log_oom();
network->macvlans = hashmap_new(string_hash_func, string_compare_func);
if (!network->macvlans)
return log_oom();
network->vxlans = hashmap_new(string_hash_func, string_compare_func);
if (!network->vxlans)
return log_oom();
network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
if (!network->addresses_by_section)
return log_oom();
network->routes_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
if (!network->routes_by_section)
return log_oom();
network->filename = strdup(filename);
if (!network->filename)
return log_oom();
network->ipv4ll_route = true;
network->dhcp_ntp = true;
network->dhcp_dns = true;
network->dhcp_hostname = true;
network->dhcp_domainname = true;
network->dhcp_routes = true;
network->dhcp_sendhost = true;
r = config_parse(NULL, filename, file,
"Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0",
config_item_perf_lookup, network_network_gperf_lookup,
false, false, true, network);
if (r < 0)
return r;
LIST_PREPEND(networks, manager->networks, network);
LIST_FOREACH(routes, route, network->static_routes) {
if (!route->family) {
log_warning("Route section without Gateway field configured in %s. "
"Ignoring", filename);
return 0;
}
}
LIST_FOREACH(addresses, address, network->static_addresses) {
if (!address->family) {
log_warning("Address section without Address field configured in %s. "
"Ignoring", filename);
return 0;
}
}
network = NULL;
return 0;
}
int network_load(Manager *manager) {
Network *network;
_cleanup_strv_free_ char **files = NULL;
char **f;
int r;
assert(manager);
while ((network = manager->networks))
network_free(network);
r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
if (r < 0) {
log_error("Failed to enumerate network files: %s", strerror(-r));
return r;
}
STRV_FOREACH_BACKWARDS(f, files) {
r = network_load_one(manager, *f);
if (r < 0)
return r;
}
return 0;
}
void network_free(Network *network) {
NetDev *netdev;
Route *route;
Address *address;
Iterator i;
if (!network)
return;
free(network->filename);
free(network->match_mac);
free(network->match_path);
free(network->match_driver);
free(network->match_type);
free(network->match_name);
free(network->description);
free(network->dhcp_vendor_class_identifier);
strv_free(network->ntp);
strv_free(network->dns);
netdev_unref(network->bridge);
netdev_unref(network->bond);
netdev_unref(network->tunnel);
HASHMAP_FOREACH(netdev, network->vlans, i)
netdev_unref(netdev);
hashmap_free(network->vlans);
HASHMAP_FOREACH(netdev, network->macvlans, i)
netdev_unref(netdev);
hashmap_free(network->macvlans);
HASHMAP_FOREACH(netdev, network->vxlans, i)
netdev_unref(netdev);
hashmap_free(network->vxlans);
while ((route = network->static_routes))
route_free(route);
while ((address = network->static_addresses))
address_free(address);
hashmap_free(network->addresses_by_section);
hashmap_free(network->routes_by_section);
if (network->manager && network->manager->networks)
LIST_REMOVE(networks, network->manager->networks, network);
condition_free_list(network->match_host);
condition_free_list(network->match_virt);
condition_free_list(network->match_kernel);
condition_free_list(network->match_arch);
free(network);
}
int network_get(Manager *manager, struct udev_device *device,
const char *ifname, const struct ether_addr *address,
Network **ret) {
Network *network;
assert(manager);
assert(ret);
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,
udev_device_get_property_value(device, "ID_PATH"),
udev_device_get_driver(udev_device_get_parent(device)),
udev_device_get_property_value(device, "ID_NET_DRIVER"),
udev_device_get_devtype(device),
ifname)) {
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:
r = hashmap_put(network->vlans, netdev->ifname, netdev);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Can not add VLAN '%s' to network: %s",
rvalue, strerror(-r));
return 0;
}
break;
case NETDEV_KIND_MACVLAN:
r = hashmap_put(network->macvlans, netdev->ifname, netdev);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Can not add MACVLAN '%s' to network: %s",
rvalue, strerror(-r));
return 0;
}
break;
case NETDEV_KIND_VXLAN:
r = hashmap_put(network->vxlans, netdev->ifname, netdev);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Can not add VXLAN '%s' to network: %s",
rvalue, strerror(-r));
return 0;
}
break;
default:
assert_not_reached("Can not parse NetDev");
}
netdev_ref(netdev);
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, EINVAL,
"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_VTI) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"NetDev is not a tunnel, ignoring assignment: %s", rvalue);
return 0;
}
network->tunnel = netdev;
return 0;
}