From 79b4428a7d01029aa3785acde9747254b28f9fd4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Jan 2019 20:16:18 +0900 Subject: [PATCH 1/3] ethtool: introduce ethtool_get_permanent_macaddr() Will be used in later commits. --- src/shared/ethtool-util.c | 41 +++++++++++++++++++++++++++++++++++++++ src/shared/ethtool-util.h | 2 ++ 2 files changed, 43 insertions(+) diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c index 3119b2b92e..e10948d612 100644 --- a/src/shared/ethtool-util.c +++ b/src/shared/ethtool-util.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "conf-parser.h" @@ -217,6 +218,46 @@ int ethtool_get_link_info(int *fd, const char *ifname, return 0; } +int ethtool_get_permanent_macaddr(int *fd, const char *ifname, struct ether_addr *ret) { + _cleanup_free_ struct ethtool_perm_addr *epaddr = NULL; + struct ifreq ifr; + int r; + + assert(fd); + assert(ifname); + assert(ret); + + if (*fd < 0) { + r = ethtool_connect_or_warn(fd, false); + if (r < 0) + return r; + } + + epaddr = malloc(offsetof(struct ethtool_perm_addr, data) + MAX_ADDR_LEN); + if (!epaddr) + return -ENOMEM; + + epaddr->cmd = ETHTOOL_GPERMADDR; + epaddr->size = MAX_ADDR_LEN; + + ifr = (struct ifreq) { + .ifr_data = (caddr_t) epaddr, + }; + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + + if (epaddr->size != 6) + return -EOPNOTSUPP; + + for (size_t i = 0; i < epaddr->size; i++) + ret->ether_addr_octet[i] = epaddr->data[i]; + + return 0; +} + int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex) { struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h index d408bcd90a..80bfffd6e9 100644 --- a/src/shared/ethtool-util.h +++ b/src/shared/ethtool-util.h @@ -2,6 +2,7 @@ #pragma once #include +#include #include #include "conf-parser.h" @@ -91,6 +92,7 @@ int ethtool_get_driver(int *fd, const char *ifname, char **ret); int ethtool_get_link_info(int *fd, const char *ifname, int *ret_autonegotiation, size_t *ret_speed, Duplex *ret_duplex, NetDevPort *ret_port); +int ethtool_get_permanent_macaddr(int *fd, const char *ifname, struct ether_addr *ret); int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex); int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol); int ethtool_set_nic_buffer_size(int *fd, const char *ifname, netdev_ring_param *ring); From 4bb7cc828706348de3efce7368c14fc994cebb20 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Jan 2019 20:16:19 +0900 Subject: [PATCH 2/3] network, udev: introduce PermanentMACAddress= setting in [Match] section Closes #13983. --- man/systemd.link.xml | 11 ++++++++++ man/systemd.network.xml | 11 ++++++++++ src/libsystemd-network/network-internal.c | 8 ++++++++ src/libsystemd-network/network-internal.h | 2 ++ src/network/networkd-link.c | 10 ++++++++-- src/network/networkd-link.h | 1 + src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 18 ++++++++++------- src/network/networkd-network.h | 4 +++- src/network/test-network.c | 2 +- src/udev/net/link-config-gperf.gperf | 1 + src/udev/net/link-config.c | 20 +++++++++++++++---- src/udev/net/link-config.h | 1 + test/fuzz/fuzz-link-parser/directives.link | 1 + .../fuzz-network-parser/directives.network | 1 + 15 files changed, 77 insertions(+), 15 deletions(-) diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 2f231d4978..1a405e989d 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -80,6 +80,17 @@ MACAddress=01:23:45:67:89:ab 00-11-22-33-44-55 AABB.CCDD.EEFF + + PermanentMACAddress= + + A whitespace-separated list of hardware's permanent addresses. While + MACAddress= matches the device's current MAC address, this matches the + device's permanent MAC address, which may be different from the current one. Use full + colon-, hyphen- or dot-delimited hexadecimal. This option may appear more than once, in + which case the lists are merged. If the empty string is assigned to this option, the list + of hardware addresses defined prior to this is reset. + + OriginalName= diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 4cd2520173..de1a854494 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -97,6 +97,17 @@ MACAddress=01:23:45:67:89:ab 00-11-22-33-44-55 AABB.CCDD.EEFF + + PermanentMACAddress= + + A whitespace-separated list of hardware's permanent addresses. While + MACAddress= matches the device's current MAC address, this matches the + device's permanent MAC address, which may be different from the current one. Use full + colon-, hyphen- or dot-delimited hexadecimal. This option may appear more than once, in + which case the lists are merged. If the empty string is assigned to this option, the list + of hardware addresses defined prior to this is reset. + + Path= diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 7198fe4775..0bf0b0e552 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -167,6 +167,7 @@ static const char *const wifi_iftype_table[NL80211_IFTYPE_MAX+1] = { DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(wifi_iftype, enum nl80211_iftype); bool net_match_config(Set *match_mac, + Set *match_permanent_mac, char * const *match_paths, char * const *match_drivers, char * const *match_types, @@ -177,6 +178,7 @@ bool net_match_config(Set *match_mac, Set *match_bssid, sd_device *device, const struct ether_addr *dev_mac, + const struct ether_addr *dev_permanent_mac, const char *dev_name, char * const *alternative_names, enum nl80211_iftype wifi_iftype, @@ -200,6 +202,12 @@ bool net_match_config(Set *match_mac, if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac))) return false; + if (match_permanent_mac && + (!dev_permanent_mac || + ether_addr_is_null(dev_permanent_mac) || + !set_contains(match_permanent_mac, dev_permanent_mac))) + return false; + if (!net_condition_test_strv(match_paths, dev_path)) return false; diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index a940b24a42..dff6c8831a 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -16,6 +16,7 @@ #define LINK_BRIDGE_PORT_PRIORITY_MAX 63 bool net_match_config(Set *match_mac, + Set *match_permanent_mac, char * const *match_path, char * const *match_driver, char * const *match_type, @@ -26,6 +27,7 @@ bool net_match_config(Set *match_mac, Set *match_bssid, sd_device *device, const struct ether_addr *dev_mac, + const struct ether_addr *dev_permanent_mac, const char *dev_name, char * const *alternative_names, enum nl80211_iftype wifi_iftype, diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 23d0ee675b..cd370bef57 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -12,6 +12,7 @@ #include "dhcp-identifier.h" #include "dhcp-lease-internal.h" #include "env-file.h" +#include "ethtool-util.h" #include "fd-util.h" #include "fileio.h" #include "ipvlan.h" @@ -617,6 +618,11 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { if (r < 0) log_link_debug_errno(link, r, "MAC address not found for new device, continuing without"); + _cleanup_close_ int fd = -1; + r = ethtool_get_permanent_macaddr(&fd, link->ifname, &link->permanent_mac); + if (r < 0) + log_link_debug_errno(link, r, "Permanent MAC address not found for new device, continuing without: %m"); + r = sd_netlink_message_read_strv(message, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &link->alternative_names); if (r < 0 && r != -ENODATA) return r; @@ -2961,7 +2967,7 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for } r = network_get(link->manager, link->sd_device, link->ifname, link->alternative_names, - &link->mac, link->wlan_iftype, link->ssid, &link->bssid, &network); + &link->mac, &link->permanent_mac, link->wlan_iftype, link->ssid, &link->bssid, &network); if (r == -ENOENT) { link_enter_unmanaged(link); return 0; @@ -3093,7 +3099,7 @@ static int link_initialized_and_synced(Link *link) { return r; r = network_get(link->manager, link->sd_device, link->ifname, link->alternative_names, - &link->mac, link->wlan_iftype, link->ssid, &link->bssid, &network); + &link->mac, &link->permanent_mac, link->wlan_iftype, link->ssid, &link->bssid, &network); if (r == -ENOENT) { link_enter_unmanaged(link); return 0; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 172e483383..33be661828 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -53,6 +53,7 @@ typedef struct Link { unsigned short iftype; char *state_file; struct ether_addr mac; + struct ether_addr permanent_mac; struct in6_addr ipv6ll_address; uint32_t mtu; sd_device *sd_device; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index ecb82c237f..67b3789ee6 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -28,6 +28,7 @@ struct ConfigPerfItem; %includes %% Match.MACAddress, config_parse_hwaddrs, 0, offsetof(Network, match_mac) +Match.PermanentMACAddress, config_parse_hwaddrs, 0, offsetof(Network, match_permanent_mac) Match.Path, config_parse_match_strv, 0, offsetof(Network, match_path) Match.Driver, config_parse_match_strv, 0, offsetof(Network, match_driver) Match.Type, config_parse_match_strv, 0, offsetof(Network, match_type) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 40394eb766..4fd48be52a 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -160,10 +160,10 @@ int network_verify(Network *network) { assert(network); assert(network->filename); - if (set_isempty(network->match_mac) && strv_isempty(network->match_path) && - strv_isempty(network->match_driver) && strv_isempty(network->match_type) && - strv_isempty(network->match_name) && strv_isempty(network->match_property) && - strv_isempty(network->match_ssid) && !network->conditions) + if (set_isempty(network->match_mac) && set_isempty(network->match_permanent_mac) && + strv_isempty(network->match_path) && strv_isempty(network->match_driver) && + strv_isempty(network->match_type) && strv_isempty(network->match_name) && + strv_isempty(network->match_property) && strv_isempty(network->match_ssid) && !network->conditions) log_warning("%s: No valid settings found in the [Match] section. " "The file will match all interfaces. " "If that is intended, please add Name=* in the [Match] section.", @@ -601,6 +601,7 @@ static Network *network_free(Network *network) { free(network->filename); set_free_free(network->match_mac); + set_free_free(network->match_permanent_mac); strv_free(network->match_path); strv_free(network->match_driver); strv_free(network->match_type); @@ -721,7 +722,8 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret) { } int network_get(Manager *manager, sd_device *device, - const char *ifname, char * const *alternative_names, const struct ether_addr *address, + const char *ifname, char * const *alternative_names, + const struct ether_addr *address, const struct ether_addr *permanent_address, enum nl80211_iftype wlan_iftype, const char *ssid, const struct ether_addr *bssid, Network **ret) { Network *network; @@ -731,10 +733,12 @@ int network_get(Manager *manager, sd_device *device, assert(ret); ORDERED_HASHMAP_FOREACH(network, manager->networks, i) - if (net_match_config(network->match_mac, network->match_path, network->match_driver, + if (net_match_config(network->match_mac, network->match_permanent_mac, + network->match_path, network->match_driver, network->match_type, network->match_name, network->match_property, network->match_wlan_iftype, network->match_ssid, network->match_bssid, - device, address, ifname, alternative_names, wlan_iftype, ssid, bssid)) { + device, address, permanent_address, + ifname, alternative_names, wlan_iftype, ssid, bssid)) { if (network->match_name && device) { const char *attr; uint8_t name_assign_type = NET_NAME_UNKNOWN; diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 783f23c5dd..e1c1c17241 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -64,6 +64,7 @@ struct Network { unsigned n_ref; Set *match_mac; + Set *match_permanent_mac; char **match_path; char **match_driver; char **match_type; @@ -302,7 +303,8 @@ int network_verify(Network *network); int network_get_by_name(Manager *manager, const char *name, Network **ret); int network_get(Manager *manager, sd_device *device, const char *ifname, char * const *alternative_names, - const struct ether_addr *mac, enum nl80211_iftype wlan_iftype, const char *ssid, + const struct ether_addr *mac, const struct ether_addr *permanent_mac, + enum nl80211_iftype wlan_iftype, const char *ssid, const struct ether_addr *bssid, Network **ret); int network_apply(Network *network, Link *link); void network_apply_anonymize_if_set(Network *network); diff --git a/src/network/test-network.c b/src/network/test-network.c index 9c06860699..7c37563ac2 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -125,7 +125,7 @@ static void test_network_get(Manager *manager, sd_device *loopback) { /* let's assume that the test machine does not have a .network file that applies to the loopback device... */ - assert_se(network_get(manager, loopback, "lo", NULL, &mac, 0, NULL, NULL, &network) == -ENOENT); + assert_se(network_get(manager, loopback, "lo", NULL, &mac, &mac, 0, NULL, NULL, &network) == -ENOENT); assert_se(!network); } diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index a1105fa674..686ff1bc5c 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -20,6 +20,7 @@ struct ConfigPerfItem; %includes %% Match.MACAddress, config_parse_hwaddrs, 0, offsetof(link_config, match_mac) +Match.PermanentMACAddress, config_parse_hwaddrs, 0, offsetof(link_config, match_permanent_mac) Match.OriginalName, config_parse_match_ifnames, 0, offsetof(link_config, match_name) Match.Path, config_parse_match_strv, 0, offsetof(link_config, match_path) Match.Driver, config_parse_match_strv, 0, offsetof(link_config, match_driver) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index d199d83411..4a44edfc01 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -47,6 +47,7 @@ static void link_config_free(link_config *link) { free(link->filename); set_free_free(link->match_mac); + set_free_free(link->match_permanent_mac); strv_free(link->match_path); strv_free(link->match_driver); strv_free(link->match_type); @@ -162,8 +163,8 @@ int link_load_one(link_config_ctx *ctx, const char *filename) { if (link->speed > UINT_MAX) return -ERANGE; - if (set_isempty(link->match_mac) && strv_isempty(link->match_path) && - strv_isempty(link->match_driver) && strv_isempty(link->match_type) && + if (set_isempty(link->match_mac) && set_isempty(link->match_permanent_mac) && + strv_isempty(link->match_path) && strv_isempty(link->match_driver) && strv_isempty(link->match_type) && strv_isempty(link->match_name) && strv_isempty(link->match_property) && !link->conditions) log_warning("%s: No valid settings found in the [Match] section. " "The file will match all interfaces. " @@ -236,16 +237,27 @@ bool link_config_should_reload(link_config_ctx *ctx) { } int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) { + struct ether_addr permanent_mac = {}; link_config *link; + const char *name; + int r; assert(ctx); assert(device); assert(ret); + r = sd_device_get_sysname(device, &name); + if (r < 0) + return r; + + r = ethtool_get_permanent_macaddr(&ctx->ethtool_fd, name, &permanent_mac); + if (r < 0) + log_device_debug_errno(device, r, "Failed to get permanent MAC address, ignoring: %m"); + LIST_FOREACH(links, link, ctx->links) { - if (net_match_config(link->match_mac, link->match_path, link->match_driver, + if (net_match_config(link->match_mac, link->match_permanent_mac, link->match_path, link->match_driver, link->match_type, link->match_name, link->match_property, NULL, NULL, NULL, - device, NULL, NULL, NULL, 0, NULL, NULL)) { + device, NULL, &permanent_mac, NULL, NULL, 0, NULL, NULL)) { if (link->match_name && !strv_contains(link->match_name, "*")) { unsigned name_assign_type = NET_NAME_UNKNOWN; diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 1c3a3f7fd4..496a8bccb7 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -36,6 +36,7 @@ struct link_config { char *filename; Set *match_mac; + Set *match_permanent_mac; char **match_path; char **match_driver; char **match_type; diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link index abf21f678a..ba8760f12b 100644 --- a/test/fuzz/fuzz-link-parser/directives.link +++ b/test/fuzz/fuzz-link-parser/directives.link @@ -1,5 +1,6 @@ [Match] MACAddress= +PermanentMACAddress= OriginalName= Path= Driver= diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 37d31e3a92..0e3adac5ce 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -28,6 +28,7 @@ Virtualization= KernelCommandLine= Host= MACAddress= +PermanentMACAddress= [Link] RequiredForOnline= ARP= From caa8538a22b6d81c8074ed1d667006ba33c92f98 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Jan 2019 20:16:20 +0900 Subject: [PATCH 3/3] networkctl: show permanent mac address if it is not used now --- src/network/networkctl.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 668ec1045d..244fb0848c 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -134,6 +134,7 @@ typedef struct LinkInfo { int ifindex; unsigned short iftype; struct ether_addr mac_address; + struct ether_addr permanent_mac_address; uint32_t mtu; uint32_t min_mtu; uint32_t max_mtu; @@ -173,6 +174,7 @@ typedef struct LinkInfo { struct ether_addr bssid; bool has_mac_address:1; + bool has_permanent_mac_address:1; bool has_tx_queues:1; bool has_rx_queues:1; bool has_stats64:1; @@ -318,6 +320,12 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) { sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 && memcmp(&info->mac_address, ÐER_ADDR_NULL, sizeof(struct ether_addr)) != 0; + _cleanup_close_ int fd = -1; + info->has_permanent_mac_address = + ethtool_get_permanent_macaddr(&fd, info->name, &info->permanent_mac_address) >= 0 && + memcmp(&info->permanent_mac_address, ÐER_ADDR_NULL, sizeof(struct ether_addr)) != 0 && + memcmp(&info->permanent_mac_address, &info->mac_address, sizeof(struct ether_addr)) != 0; + (void) sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu); (void) sd_netlink_message_read_u32(m, IFLA_MIN_MTU, &info->min_mtu); (void) sd_netlink_message_read_u32(m, IFLA_MAX_MTU, &info->max_mtu); @@ -1228,6 +1236,26 @@ static int link_status_one( return r; } + if (info->has_permanent_mac_address) { + _cleanup_free_ char *description = NULL; + char ea[ETHER_ADDR_TO_STRING_MAX]; + + (void) ieee_oui(hwdb, &info->permanent_mac_address, &description); + + r = table_add_many(table, + TABLE_EMPTY, + TABLE_STRING, "HW Permanent Address:"); + if (r < 0) + return r; + r = table_add_cell_stringf(table, NULL, "%s%s%s%s", + ether_addr_to_string(&info->permanent_mac_address, ea), + description ? " (" : "", + strempty(description), + description ? ")" : ""); + if (r < 0) + return r; + } + if (info->mtu > 0) { char min_str[DECIMAL_STR_MAX(uint32_t)], max_str[DECIMAL_STR_MAX(uint32_t)];