From b5093f3e583ea551efdf38e0c3ccfdba4a297f45 Mon Sep 17 00:00:00 2001 From: peelz Date: Fri, 25 Jul 2025 18:19:14 -0400 Subject: [PATCH] feat: match network interfaces by their altnames --- include/modules/network.hpp | 5 +- src/modules/network.cpp | 170 ++++++++++++++++++++++-------------- 2 files changed, 106 insertions(+), 69 deletions(-) diff --git a/include/modules/network.hpp b/include/modules/network.hpp index 8e80d5aa..4abe26bd 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -9,6 +9,7 @@ #include #include +#include #include "ALabel.hpp" #include "util/sleeper_thread.hpp" @@ -44,11 +45,11 @@ class Network : public ALabel { void parseFreq(struct nlattr**); void parseBssid(struct nlattr**); bool associatedOrJoined(struct nlattr**); - bool checkInterface(std::string name); + bool matchInterface(const std::string& ifname, const std::vector& altnames, + std::string& matched) const; auto getInfo() -> void; const std::string getNetworkState() const; void clearIface(); - bool wildcardMatch(const std::string& pattern, const std::string& text) const; std::optional> readBandwidthUsage(); int ifid_{-1}; diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 94c77a69..2a3254c0 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -1,6 +1,8 @@ #include "modules/network.hpp" #include +#include +#include #include #include @@ -8,6 +10,8 @@ #include #include #include +#include +#include #include "util/format.hpp" #ifdef WANT_RFKILL @@ -386,11 +390,65 @@ auto waybar::modules::Network::update() -> void { ALabel::update(); } -bool waybar::modules::Network::checkInterface(std::string name) { - if (config_["interface"].isString()) { - return config_["interface"].asString() == name || - wildcardMatch(config_["interface"].asString(), name); +// https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495 +static bool wildcardMatch(const std::string &pattern, const std::string &text) { + auto P = int(pattern.size()); + auto T = int(text.size()); + + auto p = 0, fallback_p = -1; + auto t = 0, fallback_t = -1; + + while (t < T) { + // Wildcard match: + if (p < P && pattern[p] == '*') { + fallback_p = p++; // starting point after failures + fallback_t = t; // starting point after failures + } + + // Simple match: + else if (p < P && (pattern[p] == '?' || pattern[p] == text[t])) { + p++; + t++; + } + + // Failure, fall back just after last matched '*': + else if (fallback_p >= 0) { + p = fallback_p + 1; // position just after last matched '*" + t = ++fallback_t; // re-try to match text from here + } + + // There were no '*' before, so we fail here: + else { + return false; + } } + + // Consume all '*' at the end of pattern: + while (p < P && pattern[p] == '*') p++; + + return p == P; +} + +bool waybar::modules::Network::matchInterface(const std::string &ifname, + const std::vector &altnames, + std::string &matched) const { + if (!config_["interface"].isString()) { + return false; + } + + auto config_ifname = config_["interface"].asString(); + if (config_ifname == ifname || wildcardMatch(config_ifname, ifname)) { + matched = ifname; + return true; + } + + for (const auto &altname : altnames) { + if (config_ifname == altname || wildcardMatch(config_ifname, altname)) { + matched = altname; + return true; + } + } + return false; } @@ -425,12 +483,16 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { is_del_event = true; case RTM_NEWLINK: { struct ifinfomsg *ifi = static_cast(NLMSG_DATA(nh)); - ssize_t attrlen = IFLA_PAYLOAD(nh); - struct rtattr *ifla = IFLA_RTA(ifi); - const char *ifname = NULL; - size_t ifname_len = 0; + struct nlattr *attrs[IFLA_MAX + 1]; + std::string ifname; + std::vector altnames; std::optional carrier; + if (nlmsg_parse(nh, sizeof(*ifi), attrs, IFLA_MAX, nullptr) < 0) { + spdlog::error("network: failed to parse netlink attributes"); + return NL_SKIP; + } + if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) { return NL_OK; } @@ -448,26 +510,33 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { return NL_OK; } - for (; RTA_OK(ifla, attrlen); ifla = RTA_NEXT(ifla, attrlen)) { - switch (ifla->rta_type) { - case IFLA_IFNAME: - ifname = static_cast(RTA_DATA(ifla)); - ifname_len = RTA_PAYLOAD(ifla) - 1; // minus \0 - if (ifi->ifi_flags & IFF_POINTOPOINT && net->checkInterface(ifname)) - net->is_p2p_ = true; - break; - case IFLA_CARRIER: { - carrier = *(char *)RTA_DATA(ifla) == 1; - break; + if (attrs[IFLA_IFNAME] != nullptr) { + const char *ifname_ptr = nla_get_string(attrs[IFLA_IFNAME]); + size_t ifname_len = nla_len(attrs[IFLA_IFNAME]) - 1; // minus \0 + ifname = std::string(ifname_ptr, ifname_len); + } + + if (attrs[IFLA_CARRIER] != nullptr) { + carrier = nla_get_u8(attrs[IFLA_CARRIER]) == 1; + } + + if (attrs[IFLA_PROP_LIST] != nullptr) { + struct nlattr *prop; + int rem; + + nla_for_each_nested(prop, attrs[IFLA_PROP_LIST], rem) { + if (nla_type(prop) == IFLA_ALT_IFNAME) { + const char *altname_ptr = nla_get_string(prop); + size_t altname_len = nla_len(prop) - 1; // minus \0 + altnames.emplace_back(altname_ptr, altname_len); } } } if (!is_del_event && ifi->ifi_index == net->ifid_) { // Update interface information - if (net->ifname_.empty() && ifname != NULL) { - std::string new_ifname(ifname, ifname_len); - net->ifname_ = new_ifname; + if (net->ifname_.empty() && !ifname.empty()) { + net->ifname_ = ifname; } if (carrier.has_value()) { if (net->carrier_ != *carrier) { @@ -488,13 +557,20 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { } } else if (!is_del_event && net->ifid_ == -1) { // Checking if it's an interface we care about. - std::string new_ifname(ifname, ifname_len); - if (net->checkInterface(new_ifname)) { - spdlog::debug("network: selecting new interface {}/{}", new_ifname, ifi->ifi_index); + std::string matched; + if (net->matchInterface(ifname, altnames, matched)) { + if (ifname == matched) { + spdlog::debug("network: selecting new interface {}/{}", ifname, ifi->ifi_index); + } else { + spdlog::debug("network: selecting new interface {}/{} (matched altname {})", ifname, + ifi->ifi_index, matched); + } - net->ifname_ = new_ifname; + net->ifname_ = ifname; net->ifid_ = ifi->ifi_index; - if (ifi->ifi_flags & IFF_POINTOPOINT) net->is_p2p_ = true; + if ((ifi->ifi_flags & IFF_POINTOPOINT) != 0) { + net->is_p2p_ = true; + } if ((ifi->ifi_flags & IFF_UP) == 0) { // With some network drivers (e.g. mt7921e), the interface may // report having a carrier even though interface is down. @@ -888,43 +964,3 @@ auto waybar::modules::Network::getInfo() -> void { } nl_send_sync(sock_, nl_msg); } - -// https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495 -bool waybar::modules::Network::wildcardMatch(const std::string &pattern, - const std::string &text) const { - auto P = int(pattern.size()); - auto T = int(text.size()); - - auto p = 0, fallback_p = -1; - auto t = 0, fallback_t = -1; - - while (t < T) { - // Wildcard match: - if (p < P && pattern[p] == '*') { - fallback_p = p++; // starting point after failures - fallback_t = t; // starting point after failures - } - - // Simple match: - else if (p < P && (pattern[p] == '?' || pattern[p] == text[t])) { - p++; - t++; - } - - // Failure, fall back just after last matched '*': - else if (fallback_p >= 0) { - p = fallback_p + 1; // position just after last matched '*" - t = ++fallback_t; // re-try to match text from here - } - - // There were no '*' before, so we fail here: - else { - return false; - } - } - - // Consume all '*' at the end of pattern: - while (p < P && pattern[p] == '*') p++; - - return p == P; -}