diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2017-05-20 17:35:06 -0400 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2017-05-20 17:35:06 -0400 |
commit | f2f1f2472c5efc541bd53f51289ad58a86fa2d41 (patch) | |
tree | 42807ddc64ab0672744c716d9f952e5673762b9d /src/grp-udev/libudev-core/net | |
parent | 3eed542bbc10978aa0b68a39dd3619edf7a7d90b (diff) |
./tools/notsd-move
Diffstat (limited to 'src/grp-udev/libudev-core/net')
-rw-r--r-- | src/grp-udev/libudev-core/net/.gitignore | 1 | ||||
l--------- | src/grp-udev/libudev-core/net/GNUmakefile | 1 | ||||
-rw-r--r-- | src/grp-udev/libudev-core/net/Makefile | 29 | ||||
-rw-r--r-- | src/grp-udev/libudev-core/net/ethtool-util.c | 327 | ||||
-rw-r--r-- | src/grp-udev/libudev-core/net/ethtool-util.h | 65 | ||||
-rw-r--r-- | src/grp-udev/libudev-core/net/link-config-gperf.gperf | 44 | ||||
-rw-r--r-- | src/grp-udev/libudev-core/net/link-config.c | 517 | ||||
-rw-r--r-- | src/grp-udev/libudev-core/net/link-config.h | 100 |
8 files changed, 1084 insertions, 0 deletions
diff --git a/src/grp-udev/libudev-core/net/.gitignore b/src/grp-udev/libudev-core/net/.gitignore new file mode 100644 index 0000000000..9ca85bacc9 --- /dev/null +++ b/src/grp-udev/libudev-core/net/.gitignore @@ -0,0 +1 @@ +/link-config-gperf.c diff --git a/src/grp-udev/libudev-core/net/GNUmakefile b/src/grp-udev/libudev-core/net/GNUmakefile new file mode 120000 index 0000000000..13308a50cd --- /dev/null +++ b/src/grp-udev/libudev-core/net/GNUmakefile @@ -0,0 +1 @@ +../../../../GNUmakefile
\ No newline at end of file diff --git a/src/grp-udev/libudev-core/net/Makefile b/src/grp-udev/libudev-core/net/Makefile new file mode 100644 index 0000000000..ac615aad3b --- /dev/null +++ b/src/grp-udev/libudev-core/net/Makefile @@ -0,0 +1,29 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see <http://www.gnu.org/licenses/>. +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +sd.CPPFLAGS += $(libsystemd-shared.CPPFLAGS) +sd.CPPFLAGS += $(libsystemd-network.CPPFLAGS) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/libudev-core/net/ethtool-util.c b/src/grp-udev/libudev-core/net/ethtool-util.c new file mode 100644 index 0000000000..05d6ecb953 --- /dev/null +++ b/src/grp-udev/libudev-core/net/ethtool-util.c @@ -0,0 +1,327 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <net/if.h> +#include <sys/ioctl.h> + +#include <linux/ethtool.h> +#include <linux/sockios.h> + +#include "systemd-basic/log.h" +#include "systemd-basic/socket-util.h" +#include "systemd-basic/string-table.h" +#include "systemd-basic/strxcpyx.h" +#include "systemd-basic/util.h" +#include "systemd-shared/conf-parser.h" + +#include "ethtool-util.h" + +static const char* const duplex_table[_DUP_MAX] = { + [DUP_FULL] = "full", + [DUP_HALF] = "half" +}; + +DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex); +DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting"); + +static const char* const wol_table[_WOL_MAX] = { + [WOL_PHY] = "phy", + [WOL_MAGIC] = "magic", + [WOL_OFF] = "off" +}; + +DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan); +DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting"); + +static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = { + [NET_DEV_FEAT_GSO] = "tx-generic-segmentation", + [NET_DEV_FEAT_GRO] = "rx-gro", + [NET_DEV_FEAT_LRO] = "rx-lro", + [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation", + [NET_DEV_FEAT_UFO] = "tx-udp-fragmentation", +}; + +int ethtool_connect(int *ret) { + int fd; + + assert_return(ret, -EINVAL); + + fd = socket_ioctl_fd(); + if (fd < 0) + return fd; + *ret = fd; + + return 0; +} + +int ethtool_get_driver(int *fd, const char *ifname, char **ret) { + struct ethtool_drvinfo ecmd = { + .cmd = ETHTOOL_GDRVINFO + }; + struct ifreq ifr = { + .ifr_data = (void*) &ecmd + }; + char *d; + int r; + + if (*fd < 0) { + r = ethtool_connect(fd); + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + } + + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + + d = strdup(ecmd.driver); + if (!d) + return -ENOMEM; + + *ret = d; + return 0; +} + +int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex) { + struct ethtool_cmd ecmd = { + .cmd = ETHTOOL_GSET + }; + struct ifreq ifr = { + .ifr_data = (void*) &ecmd + }; + bool need_update = false; + int r; + + if (speed == 0 && duplex == _DUP_INVALID) + return 0; + + if (*fd < 0) { + r = ethtool_connect(fd); + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + } + + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + + if (ethtool_cmd_speed(&ecmd) != speed) { + ethtool_cmd_speed_set(&ecmd, speed); + need_update = true; + } + + switch (duplex) { + case DUP_HALF: + if (ecmd.duplex != DUPLEX_HALF) { + ecmd.duplex = DUPLEX_HALF; + need_update = true; + } + break; + case DUP_FULL: + if (ecmd.duplex != DUPLEX_FULL) { + ecmd.duplex = DUPLEX_FULL; + need_update = true; + } + break; + default: + break; + } + + if (need_update) { + ecmd.cmd = ETHTOOL_SSET; + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + } + + return 0; +} + +int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) { + struct ethtool_wolinfo ecmd = { + .cmd = ETHTOOL_GWOL + }; + struct ifreq ifr = { + .ifr_data = (void*) &ecmd + }; + bool need_update = false; + int r; + + if (wol == _WOL_INVALID) + return 0; + + if (*fd < 0) { + r = ethtool_connect(fd); + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + } + + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + + switch (wol) { + case WOL_PHY: + if (ecmd.wolopts != WAKE_PHY) { + ecmd.wolopts = WAKE_PHY; + need_update = true; + } + break; + case WOL_MAGIC: + if (ecmd.wolopts != WAKE_MAGIC) { + ecmd.wolopts = WAKE_MAGIC; + need_update = true; + } + break; + case WOL_OFF: + if (ecmd.wolopts != 0) { + ecmd.wolopts = 0; + need_update = true; + } + break; + default: + break; + } + + if (need_update) { + ecmd.cmd = ETHTOOL_SWOL; + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + } + + return 0; +} + +static int ethtool_get_stringset(int *fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) { + _cleanup_free_ struct ethtool_gstrings *strings = NULL; + struct { + struct ethtool_sset_info info; + uint32_t space; + } buffer = { + .info = { + .cmd = ETHTOOL_GSSET_INFO, + .sset_mask = UINT64_C(1) << stringset_id, + }, + }; + unsigned len; + int r; + + ifr->ifr_data = (void *) &buffer.info; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + if (!buffer.info.sset_mask) + return -EINVAL; + + len = buffer.info.data[0]; + + strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN); + if (!strings) + return -ENOMEM; + + strings->cmd = ETHTOOL_GSTRINGS; + strings->string_set = stringset_id; + strings->len = len; + + ifr->ifr_data = (void *) strings; + + r = ioctl(*fd, SIOCETHTOOL, ifr); + if (r < 0) + return -errno; + + *gstrings = strings; + strings = NULL; + + return 0; +} + +static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) { + unsigned i; + + for (i = 0; i < strings->len; i++) { + if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature)) + return i; + } + + return -1; +} + +int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) { + _cleanup_free_ struct ethtool_gstrings *strings = NULL; + struct ethtool_sfeatures *sfeatures; + int block, bit, i, r; + struct ifreq ifr = {}; + + if (*fd < 0) { + r = ethtool_connect(fd); + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + } + + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = ethtool_get_stringset(fd, &ifr, ETH_SS_FEATURES, &strings); + if (r < 0) + return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname); + + sfeatures = alloca0(sizeof(struct ethtool_gstrings) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0])); + sfeatures->cmd = ETHTOOL_SFEATURES; + sfeatures->size = DIV_ROUND_UP(strings->len, 32U); + + for (i = 0; i < _NET_DEV_FEAT_MAX; i++) { + + if (features[i] != -1) { + + r = find_feature_index(strings, netdev_feature_table[i]); + if (r < 0) { + log_warning_errno(r, "link_config: could not find feature: %s", netdev_feature_table[i]); + continue; + } + + block = r / 32; + bit = r % 32; + + sfeatures->features[block].valid |= 1 << bit; + + if (features[i]) + sfeatures->features[block].requested |= 1 << bit; + else + sfeatures->features[block].requested &= ~(1 << bit); + } + } + + ifr.ifr_data = (void *) sfeatures; + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return log_warning_errno(r, "link_config: could not set ethtool features for %s", ifname); + + return 0; +} diff --git a/src/grp-udev/libudev-core/net/ethtool-util.h b/src/grp-udev/libudev-core/net/ethtool-util.h new file mode 100644 index 0000000000..6b5dfdd513 --- /dev/null +++ b/src/grp-udev/libudev-core/net/ethtool-util.h @@ -0,0 +1,65 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "systemd-basic/macro.h" + +/* we can't use DUPLEX_ prefix, as it + * clashes with <linux/ethtool.h> */ +typedef enum Duplex { + DUP_FULL, + DUP_HALF, + _DUP_MAX, + _DUP_INVALID = -1 +} Duplex; + +typedef enum WakeOnLan { + WOL_PHY, + WOL_MAGIC, + WOL_OFF, + _WOL_MAX, + _WOL_INVALID = -1 +} WakeOnLan; + +typedef enum NetDevFeature { + NET_DEV_FEAT_GSO, + NET_DEV_FEAT_GRO, + NET_DEV_FEAT_LRO, + NET_DEV_FEAT_TSO, + NET_DEV_FEAT_UFO, + _NET_DEV_FEAT_MAX, + _NET_DEV_FEAT_INVALID = -1 +} NetDevFeature; + +int ethtool_connect(int *ret); + +int ethtool_get_driver(int *fd, const char *ifname, char **ret); +int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex); +int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol); +int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features); + +const char *duplex_to_string(Duplex d) _const_; +Duplex duplex_from_string(const char *d) _pure_; + +const char *wol_to_string(WakeOnLan wol) _const_; +WakeOnLan wol_from_string(const char *wol) _pure_; + +int config_parse_duplex(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); +int config_parse_wol(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); diff --git a/src/grp-udev/libudev-core/net/link-config-gperf.gperf b/src/grp-udev/libudev-core/net/link-config-gperf.gperf new file mode 100644 index 0000000000..89184782ed --- /dev/null +++ b/src/grp-udev/libudev-core/net/link-config-gperf.gperf @@ -0,0 +1,44 @@ +%{ +#include <stddef.h> + +#include "systemd-network/network-internal.h" +#include "systemd-shared/conf-parser.h" + +#include "ethtool-util.h" +#include "link-config.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name link_config_gperf_hash +%define lookup-function-name link_config_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Match.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, match_mac) +Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name) +Match.Path, config_parse_strv, 0, offsetof(link_config, match_path) +Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver) +Match.Type, config_parse_strv, 0, offsetof(link_config, match_type) +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch) +Link.Description, config_parse_string, 0, offsetof(link_config, description) +Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy) +Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac) +Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy) +Link.Name, config_parse_ifname, 0, offsetof(link_config, name) +Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias) +Link.MTUBytes, config_parse_iec_size, 0, offsetof(link_config, mtu) +Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed) +Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex) +Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol) +Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO]) +Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO]) +Link.UDPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_UFO]) +Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GRO]) +Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_LRO]) diff --git a/src/grp-udev/libudev-core/net/link-config.c b/src/grp-udev/libudev-core/net/link-config.c new file mode 100644 index 0000000000..df37c63169 --- /dev/null +++ b/src/grp-udev/libudev-core/net/link-config.c @@ -0,0 +1,517 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <netinet/ether.h> + +#include "libudev-private.h" +#include "sd-netlink/netlink-util.h" +#include "systemd-basic/alloc-util.h" +#include "systemd-basic/conf-files.h" +#include "systemd-basic/fd-util.h" +#include "systemd-basic/log.h" +#include "systemd-basic/missing.h" +#include "systemd-basic/parse-util.h" +#include "systemd-basic/path-util.h" +#include "systemd-basic/proc-cmdline.h" +#include "systemd-basic/random-util.h" +#include "systemd-basic/stat-util.h" +#include "systemd-basic/string-table.h" +#include "systemd-basic/string-util.h" +#include "systemd-basic/strv.h" +#include "systemd-basic/util.h" +#include "systemd-network/network-internal.h" +#include "systemd-shared/conf-parser.h" +#include "systemd-staging/sd-netlink.h" + +#include "ethtool-util.h" +#include "link-config.h" + +struct link_config_ctx { + LIST_HEAD(link_config, links); + + int ethtool_fd; + + bool enable_name_policy; + + sd_netlink *rtnl; + + usec_t link_dirs_ts_usec; +}; + +static const char* const link_dirs[] = { + "/etc/systemd/network", + "/run/systemd/network", + "/usr/lib/systemd/network", +#ifdef HAVE_SPLIT_USR + "/lib/systemd/network", +#endif + NULL}; + +static void link_config_free(link_config *link) { + if (!link) + return; + + free(link->filename); + + free(link->match_mac); + strv_free(link->match_path); + strv_free(link->match_driver); + strv_free(link->match_type); + free(link->match_name); + free(link->match_host); + free(link->match_virt); + free(link->match_kernel); + free(link->match_arch); + + free(link->description); + free(link->mac); + free(link->name_policy); + free(link->name); + free(link->alias); + + free(link); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(link_config*, link_config_free); + +static void link_configs_free(link_config_ctx *ctx) { + link_config *link, *link_next; + + if (!ctx) + return; + + LIST_FOREACH_SAFE(links, link, link_next, ctx->links) + link_config_free(link); +} + +void link_config_ctx_free(link_config_ctx *ctx) { + if (!ctx) + return; + + safe_close(ctx->ethtool_fd); + + sd_netlink_unref(ctx->rtnl); + + link_configs_free(ctx); + + free(ctx); + + return; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free); + +int link_config_ctx_new(link_config_ctx **ret) { + _cleanup_(link_config_ctx_freep) link_config_ctx *ctx = NULL; + + if (!ret) + return -EINVAL; + + ctx = new0(link_config_ctx, 1); + if (!ctx) + return -ENOMEM; + + LIST_HEAD_INIT(ctx->links); + + ctx->ethtool_fd = -1; + + ctx->enable_name_policy = true; + + *ret = ctx; + ctx = NULL; + + return 0; +} + +static int load_link(link_config_ctx *ctx, const char *filename) { + _cleanup_(link_config_freep) link_config *link = NULL; + _cleanup_fclose_ FILE *file = NULL; + int r; + + assert(ctx); + assert(filename); + + 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; + } + + link = new0(link_config, 1); + if (!link) + return log_oom(); + + link->mac_policy = _MACPOLICY_INVALID; + link->wol = _WOL_INVALID; + link->duplex = _DUP_INVALID; + + memset(&link->features, -1, _NET_DEV_FEAT_MAX); + + r = config_parse(NULL, filename, file, + "Match\0Link\0Ethernet\0", + config_item_perf_lookup, link_config_gperf_lookup, + false, false, true, link); + if (r < 0) + return r; + else + log_debug("Parsed configuration file %s", filename); + + if (link->mtu > UINT_MAX || link->speed > UINT_MAX) + return -ERANGE; + + link->filename = strdup(filename); + + LIST_PREPEND(links, ctx->links, link); + link = NULL; + + return 0; +} + +static bool enable_name_policy(void) { + _cleanup_free_ char *value = NULL; + int r; + + r = get_proc_cmdline_key("net.ifnames=", &value); + if (r > 0 && streq(value, "0")) + return false; + + return true; +} + +int link_config_load(link_config_ctx *ctx) { + int r; + _cleanup_strv_free_ char **files; + char **f; + + link_configs_free(ctx); + + if (!enable_name_policy()) { + ctx->enable_name_policy = false; + log_info("Network interface NamePolicy= disabled on kernel command line, ignoring."); + } + + /* update timestamp */ + paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true); + + r = conf_files_list_strv(&files, ".link", NULL, link_dirs); + if (r < 0) + return log_error_errno(r, "failed to enumerate link files: %m"); + + STRV_FOREACH_BACKWARDS(f, files) { + r = load_link(ctx, *f); + if (r < 0) + return r; + } + + return 0; +} + +bool link_config_should_reload(link_config_ctx *ctx) { + return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false); +} + +int link_config_get(link_config_ctx *ctx, struct udev_device *device, + link_config **ret) { + link_config *link; + + assert(ctx); + assert(device); + assert(ret); + + LIST_FOREACH(links, link, ctx->links) { + const char* attr_value; + + attr_value = udev_device_get_sysattr_value(device, "address"); + + if (net_match_config(link->match_mac, link->match_path, link->match_driver, + link->match_type, link->match_name, link->match_host, + link->match_virt, link->match_kernel, link->match_arch, + attr_value ? ether_aton(attr_value) : NULL, + 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), + udev_device_get_sysname(device))) { + if (link->match_name) { + unsigned char name_assign_type = NET_NAME_UNKNOWN; + + attr_value = udev_device_get_sysattr_value(device, "name_assign_type"); + if (attr_value) + (void) safe_atou8(attr_value, &name_assign_type); + + if (name_assign_type == NET_NAME_ENUM) { + log_warning("Config file %s applies to device based on potentially unpredictable interface name '%s'", + link->filename, udev_device_get_sysname(device)); + *ret = link; + + return 0; + } else if (name_assign_type == NET_NAME_RENAMED) { + log_warning("Config file %s matches device based on renamed interface name '%s', ignoring", + link->filename, udev_device_get_sysname(device)); + + continue; + } + } + + log_debug("Config file %s applies to device %s", + link->filename, udev_device_get_sysname(device)); + + *ret = link; + + return 0; + } + } + + *ret = NULL; + + return -ENOENT; +} + +static bool mac_is_random(struct udev_device *device) { + const char *s; + unsigned type; + int r; + + /* if we can't get the assign type, assume it is not random */ + s = udev_device_get_sysattr_value(device, "addr_assign_type"); + if (!s) + return false; + + r = safe_atou(s, &type); + if (r < 0) + return false; + + return type == NET_ADDR_RANDOM; +} + +static bool should_rename(struct udev_device *device, bool respect_predictable) { + const char *s; + unsigned type; + int r; + + /* if we can't get the assgin type, assume we should rename */ + s = udev_device_get_sysattr_value(device, "name_assign_type"); + if (!s) + return true; + + r = safe_atou(s, &type); + if (r < 0) + return true; + + switch (type) { + case NET_NAME_USER: + case NET_NAME_RENAMED: + /* these were already named by userspace, do not touch again */ + return false; + case NET_NAME_PREDICTABLE: + /* the kernel claims to have given a predictable name */ + if (respect_predictable) + return false; + /* fall through */ + case NET_NAME_ENUM: + default: + /* the name is known to be bad, or of an unknown type */ + return true; + } +} + +static int get_mac(struct udev_device *device, bool want_random, + struct ether_addr *mac) { + int r; + + if (want_random) + random_bytes(mac->ether_addr_octet, ETH_ALEN); + else { + uint64_t result; + + r = net_get_unique_predictable_data(device, &result); + if (r < 0) + return r; + + assert_cc(ETH_ALEN <= sizeof(result)); + memcpy(mac->ether_addr_octet, &result, ETH_ALEN); + } + + /* see eth_random_addr in the kernel */ + mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ + mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ + + return 0; +} + +int link_config_apply(link_config_ctx *ctx, link_config *config, + struct udev_device *device, const char **name) { + const char *old_name; + const char *new_name = NULL; + struct ether_addr generated_mac; + struct ether_addr *mac = NULL; + bool respect_predictable = false; + int r, ifindex; + + assert(ctx); + assert(config); + assert(device); + assert(name); + + old_name = udev_device_get_sysname(device); + if (!old_name) + return -EINVAL; + + r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex); + if (r < 0) + log_warning_errno(r, "Could not set speed or duplex of %s to %zu Mbps (%s): %m", + old_name, config->speed / 1024, + duplex_to_string(config->duplex)); + + r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol); + if (r < 0) + log_warning_errno(r, "Could not set WakeOnLan of %s to %s: %m", + old_name, wol_to_string(config->wol)); + + r = ethtool_set_features(&ctx->ethtool_fd, old_name, config->features); + if (r < 0) + log_warning_errno(r, "Could not set offload features of %s: %m", old_name); + + ifindex = udev_device_get_ifindex(device); + if (ifindex <= 0) { + log_warning("Could not find ifindex"); + return -ENODEV; + } + + if (ctx->enable_name_policy && config->name_policy) { + NamePolicy *policy; + + for (policy = config->name_policy; + !new_name && *policy != _NAMEPOLICY_INVALID; policy++) { + switch (*policy) { + case NAMEPOLICY_KERNEL: + respect_predictable = true; + break; + case NAMEPOLICY_DATABASE: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE"); + break; + case NAMEPOLICY_ONBOARD: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD"); + break; + case NAMEPOLICY_SLOT: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT"); + break; + case NAMEPOLICY_PATH: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH"); + break; + case NAMEPOLICY_MAC: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC"); + break; + default: + break; + } + } + } + + if (should_rename(device, respect_predictable)) { + /* if not set by policy, fall back manually set name */ + if (!new_name) + new_name = config->name; + } else + new_name = NULL; + + switch (config->mac_policy) { + case MACPOLICY_PERSISTENT: + if (mac_is_random(device)) { + r = get_mac(device, false, &generated_mac); + if (r == -ENOENT) { + log_warning_errno(r, "Could not generate persistent MAC address for %s: %m", old_name); + break; + } else if (r < 0) + return r; + mac = &generated_mac; + } + break; + case MACPOLICY_RANDOM: + if (!mac_is_random(device)) { + r = get_mac(device, true, &generated_mac); + if (r == -ENOENT) { + log_warning_errno(r, "Could not generate random MAC address for %s: %m", old_name); + break; + } else if (r < 0) + return r; + mac = &generated_mac; + } + break; + case MACPOLICY_NONE: + default: + mac = config->mac; + } + + r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac, config->mtu); + if (r < 0) + return log_warning_errno(r, "Could not set Alias, MACAddress or MTU on %s: %m", old_name); + + *name = new_name; + + return 0; +} + +int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) { + const char *name; + char *driver = NULL; + int r; + + name = udev_device_get_sysname(device); + if (!name) + return -EINVAL; + + r = ethtool_get_driver(&ctx->ethtool_fd, name, &driver); + if (r < 0) + return r; + + *ret = driver; + return 0; +} + +static const char* const mac_policy_table[_MACPOLICY_MAX] = { + [MACPOLICY_PERSISTENT] = "persistent", + [MACPOLICY_RANDOM] = "random", + [MACPOLICY_NONE] = "none" +}; + +DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy); +DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy, + "Failed to parse MAC address policy"); + +static const char* const name_policy_table[_NAMEPOLICY_MAX] = { + [NAMEPOLICY_KERNEL] = "kernel", + [NAMEPOLICY_DATABASE] = "database", + [NAMEPOLICY_ONBOARD] = "onboard", + [NAMEPOLICY_SLOT] = "slot", + [NAMEPOLICY_PATH] = "path", + [NAMEPOLICY_MAC] = "mac" +}; + +DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy); +DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy, + _NAMEPOLICY_INVALID, + "Failed to parse interface name policy"); diff --git a/src/grp-udev/libudev-core/net/link-config.h b/src/grp-udev/libudev-core/net/link-config.h new file mode 100644 index 0000000000..0040dd2b42 --- /dev/null +++ b/src/grp-udev/libudev-core/net/link-config.h @@ -0,0 +1,100 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 Tom Gundersen <teg@jklm.no> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <libudev.h> + +#include "systemd-basic/list.h" +#include "systemd-shared/condition.h" + +#include "ethtool-util.h" + +typedef struct link_config_ctx link_config_ctx; +typedef struct link_config link_config; + +typedef enum MACPolicy { + MACPOLICY_PERSISTENT, + MACPOLICY_RANDOM, + MACPOLICY_NONE, + _MACPOLICY_MAX, + _MACPOLICY_INVALID = -1 +} MACPolicy; + +typedef enum NamePolicy { + NAMEPOLICY_KERNEL, + NAMEPOLICY_DATABASE, + NAMEPOLICY_ONBOARD, + NAMEPOLICY_SLOT, + NAMEPOLICY_PATH, + NAMEPOLICY_MAC, + _NAMEPOLICY_MAX, + _NAMEPOLICY_INVALID = -1 +} NamePolicy; + +struct link_config { + char *filename; + + struct ether_addr *match_mac; + char **match_path; + char **match_driver; + char **match_type; + char **match_name; + Condition *match_host; + Condition *match_virt; + Condition *match_kernel; + Condition *match_arch; + + char *description; + struct ether_addr *mac; + MACPolicy mac_policy; + NamePolicy *name_policy; + char *name; + char *alias; + size_t mtu; + size_t speed; + Duplex duplex; + WakeOnLan wol; + NetDevFeature features[_NET_DEV_FEAT_MAX]; + + LIST_FIELDS(link_config, links); +}; + +int link_config_ctx_new(link_config_ctx **ret); +void link_config_ctx_free(link_config_ctx *ctx); + +int link_config_load(link_config_ctx *ctx); +bool link_config_should_reload(link_config_ctx *ctx); + +int link_config_get(link_config_ctx *ctx, struct udev_device *device, struct link_config **ret); +int link_config_apply(link_config_ctx *ctx, struct link_config *config, struct udev_device *device, const char **name); + +int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret); + +const char *name_policy_to_string(NamePolicy p) _const_; +NamePolicy name_policy_from_string(const char *p) _pure_; + +const char *mac_policy_to_string(MACPolicy p) _const_; +MACPolicy mac_policy_from_string(const char *p) _pure_; + +/* gperf lookup function */ +const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, GPERF_LEN_TYPE length); + +int config_parse_mac_policy(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); +int config_parse_name_policy(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); |