networkd-network.c revision 60c35566600f45350c37f152c1093018972bd9a5
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe/***
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe This file is part of systemd.
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe Copyright 2013 Tom Gundersen <teg@jklm.no>
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe systemd is free software; you can redistribute it and/or modify it
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe under the terms of the GNU Lesser General Public License as published by
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe the Free Software Foundation; either version 2.1 of the License, or
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe (at your option) any later version.
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe systemd is distributed in the hope that it will be useful, but
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe WITHOUT ANY WARRANTY; without even the implied warranty of
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe Lesser General Public License for more details.
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe You should have received a copy of the GNU Lesser General Public License
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe along with systemd; If not, see <http://www.gnu.org/licenses/>.
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe***/
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe#include <ctype.h>
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe#include <net/if.h>
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe#include "path-util.h"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe#include "conf-files.h"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe#include "conf-parser.h"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe#include "util.h"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe#include "networkd.h"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe#include "networkd-netdev.h"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe#include "networkd-link.h"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe#include "network-internal.h"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowestatic int network_load_one(Manager *manager, const char *filename) {
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe _cleanup_network_free_ Network *network = NULL;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe _cleanup_fclose_ FILE *file = NULL;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe char *d;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe Route *route;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe Address *address;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe int r;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe assert(manager);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe assert(filename);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe file = fopen(filename, "re");
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (!file) {
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (errno == ENOENT)
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe return 0;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe else
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe return -errno;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe }
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe if (null_or_empty_fd(fileno(file))) {
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe log_debug("Skipping empty file: %s", filename);
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe return 0;
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe }
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe network = new0(Network, 1);
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe if (!network)
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe return log_oom();
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe network->manager = manager;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe LIST_HEAD_INIT(network->static_addresses);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe LIST_HEAD_INIT(network->static_routes);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe LIST_HEAD_INIT(network->static_fdb_entries);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->stacked_netdevs = hashmap_new(&string_hash_ops);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (!network->stacked_netdevs)
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe return log_oom();
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->addresses_by_section = hashmap_new(NULL);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (!network->addresses_by_section)
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe return log_oom();
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->routes_by_section = hashmap_new(NULL);
26b9ccb55ff33097af4914f2e4bd36fec99a039dwrowe if (!network->routes_by_section)
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe return log_oom();
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->fdb_entries_by_section = hashmap_new(NULL);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (!network->fdb_entries_by_section)
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe return log_oom();
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->filename = strdup(filename);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (!network->filename)
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe return log_oom();
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->name = strdup(basename(filename));
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (!network->name)
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe return log_oom();
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe d = strrchr(network->name, '.');
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (!d)
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe return -EINVAL;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe assert(streq(d, ".network"));
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe *d = '\0';
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->dhcp = ADDRESS_FAMILY_NO;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->dhcp_ntp = true;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->dhcp_dns = true;
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe network->dhcp_hostname = true;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->dhcp_routes = true;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->dhcp_sendhost = true;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->dhcp_route_metric = DHCP_ROUTE_METRIC;
0bcc003d275c6b0a9060d43be89762b218cbc2c7wrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->llmnr = LLMNR_SUPPORT_YES;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->link_local = ADDRESS_FAMILY_IPV6;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe r = config_parse(NULL, filename, file,
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe "Match\0"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe "Link\0"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe "Network\0"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe "Address\0"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe "Route\0"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe "DHCP\0"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe "DHCPv4\0"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe "Bridge\0"
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe "BridgeFDB\0",
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe config_item_perf_lookup, network_network_gperf_lookup,
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe false, false, true, network);
2b976b4e693d0fe6df3909182a5e36b06575bbc4wrowe if (r < 0)
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe return r;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe /* IPMasquerade=yes implies IPForward=yes */
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (network->ip_masquerade)
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe network->ip_forward |= ADDRESS_FAMILY_IPV4;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe LIST_PREPEND(networks, manager->networks, network);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (r < 0)
2b976b4e693d0fe6df3909182a5e36b06575bbc4wrowe return r;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe r = hashmap_put(manager->networks_by_name, network->name, network);
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (r < 0)
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe return r;
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe LIST_FOREACH(routes, route, network->static_routes) {
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe if (!route->family) {
cc08604ef06281d6375ed35ea3076ab65f3a0c3bwrowe 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)
return log_error_errno(r, "Failed to enumerate network files: %m");
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;
FdbEntry *fdb_entry;
Iterator i;
if (!network)
return;
free(network->filename);
free(network->match_mac);
strv_free(network->match_path);
strv_free(network->match_driver);
strv_free(network->match_type);
strv_free(network->match_name);
free(network->description);
free(network->dhcp_vendor_class_identifier);
free(network->mac);
strv_free(network->ntp);
strv_free(network->dns);
strv_free(network->domains);
netdev_unref(network->bridge);
netdev_unref(network->bond);
HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
hashmap_remove(network->stacked_netdevs, netdev->ifname);
netdev_unref(netdev);
}
hashmap_free(network->stacked_netdevs);
while ((route = network->static_routes))
route_free(route);
while ((address = network->static_addresses))
address_free(address);
while ((fdb_entry = network->static_fdb_entries))
fdb_entry_free(fdb_entry);
hashmap_free(network->addresses_by_section);
hashmap_free(network->routes_by_section);
hashmap_free(network->fdb_entries_by_section);
if (network->manager) {
if (network->manager->networks)
LIST_REMOVE(networks, network->manager->networks, network);
if (network->manager->networks_by_name)
hashmap_remove(network->manager->networks_by_name, network->name);
}
free(network->name);
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_by_name(Manager *manager, const char *name, Network **ret) {
Network *network;
assert(manager);
assert(name);
assert(ret);
network = hashmap_get(manager->networks_by_name, name);
if (!network)
return -ENOENT;
*ret = network;
return 0;
}
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)) {
if (network->match_name) {
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_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: %s",
rvalue, strerror(-r));
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 if (!hostname_is_valid(*domain)) {
if (!streq(*domain, "*"))
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "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, 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_GRETAP &&
netdev->kind != NETDEV_KIND_IP6GRE &&
netdev->kind != NETDEV_KIND_IP6GRETAP &&
netdev->kind != NETDEV_KIND_VTI &&
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, EINVAL,
"Can not add VLAN '%s' to network: %s",
rvalue, strerror(-r));
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 llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
[LLMNR_SUPPORT_NO] = "no",
[LLMNR_SUPPORT_YES] = "yes",
[LLMNR_SUPPORT_RESOLVE] = "resolve",
};
DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
int config_parse_llmnr(
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) {
LLMNRSupport *llmnr = data;
int k;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(llmnr);
/* 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)
*llmnr = LLMNR_SUPPORT_YES;
else if (k == 0)
*llmnr = LLMNR_SUPPORT_NO;
else {
LLMNRSupport s;
s = llmnr_support_from_string(rvalue);
if (s < 0){
log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
return 0;
}
*llmnr = s;
}
return 0;
}
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;
}