Merge pull request #1106 from tperard/network-module-reword

Network module rework
This commit is contained in:
Alex 2021-05-21 17:02:28 +02:00 committed by GitHub
commit f78a802d11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 348 additions and 336 deletions

View File

@ -2,9 +2,7 @@
#include <arpa/inet.h>
#include <fmt/format.h>
#include <ifaddrs.h>
#include <linux/nl80211.h>
#include <net/if.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include <netlink/netlink.h>
@ -28,23 +26,20 @@ class Network : public ALabel {
static const uint8_t EPOLL_MAX = 200;
static int handleEvents(struct nl_msg*, void*);
static int handleEventsDone(struct nl_msg*, void*);
static int handleScan(struct nl_msg*, void*);
void askForStateDump(void);
void worker();
void createInfoSocket();
void createEventSocket();
int getExternalInterface(int skip_idx = -1) const;
void getInterfaceAddress();
int netlinkRequest(void*, uint32_t, uint32_t groups = 0) const;
int netlinkResponse(void*, uint32_t, uint32_t groups = 0) const;
void parseEssid(struct nlattr**);
void parseSignal(struct nlattr**);
void parseFreq(struct nlattr**);
bool associatedOrJoined(struct nlattr**);
bool checkInterface(struct ifinfomsg* rtif, std::string name);
int getPreferredIface(int skip_idx = -1, bool wait = true) const;
bool checkInterface(std::string name);
auto getInfo() -> void;
void checkNewInterface(struct ifinfomsg* rtif);
const std::string getNetworkState() const;
void clearIface();
bool wildcardMatch(const std::string& pattern, const std::string& text) const;
@ -59,11 +54,17 @@ class Network : public ALabel {
int nl80211_id_;
std::mutex mutex_;
bool want_route_dump_;
bool want_link_dump_;
bool want_addr_dump_;
bool dump_in_progress_;
unsigned long long bandwidth_down_total_;
unsigned long long bandwidth_up_total_;
std::string state_;
std::string essid_;
bool carrier_;
std::string ifname_;
std::string ipaddr_;
std::string netmask_;

View File

@ -86,13 +86,24 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
efd_(-1),
ev_fd_(-1),
cidr_(-1),
want_route_dump_(false),
want_link_dump_(false),
want_addr_dump_(false),
dump_in_progress_(false),
cidr_(0),
signal_strength_dbm_(0),
signal_strength_(0),
#ifdef WANT_RFKILL
rfkill_{RFKILL_TYPE_WLAN},
#endif
frequency_(0) {
// Start with some "text" in the module's label_, update() will then
// update it. Since the text should be different, update() will be able
// to show or hide the event_box_. This is to work around the case where
// the module start with no text, but the the event_box_ is shown.
label_.set_markup("<s></s>");
auto down_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_DOWN_TOTAL_KEY);
auto up_octets = read_netstat(BANDWIDTH_CATEGORY, BANDWIDTH_UP_TOTAL_KEY);
if (down_octets) {
@ -107,17 +118,25 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
bandwidth_up_total_ = 0;
}
if (!config_["interface"].isString()) {
// "interface" isn't configure, then try to guess the external
// interface currently used for internet.
want_route_dump_ = true;
} else {
// Look for an interface that match "interface"
// and then find the address associated with it.
want_link_dump_ = true;
want_addr_dump_ = true;
}
createEventSocket();
createInfoSocket();
auto default_iface = getPreferredIface(-1, false);
if (default_iface != -1) {
ifid_ = default_iface;
char ifname[IF_NAMESIZE];
if_indextoname(default_iface, ifname);
ifname_ = ifname;
getInterfaceAddress();
}
dp.emit();
// Ask for a dump of interfaces and then addresses to populate our
// information. First the interface dump, and once done, the callback
// will be called again which will ask for addresses dump.
askForStateDump();
worker();
}
@ -148,17 +167,29 @@ void waybar::modules::Network::createEventSocket() {
ev_sock_ = nl_socket_alloc();
nl_socket_disable_seq_check(ev_sock_);
nl_socket_modify_cb(ev_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this);
nl_socket_modify_cb(ev_sock_, NL_CB_FINISH, NL_CB_CUSTOM, handleEventsDone, this);
auto groups = RTMGRP_LINK | (family_ == AF_INET ? RTMGRP_IPV4_IFADDR : RTMGRP_IPV6_IFADDR);
nl_join_groups(ev_sock_, groups); // Deprecated
if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) {
throw std::runtime_error("Can't connect network socket");
}
if (nl_socket_set_nonblocking(ev_sock_)) {
throw std::runtime_error("Can't set non-blocking on network socket");
}
nl_socket_add_membership(ev_sock_, RTNLGRP_LINK);
if (family_ == AF_INET) {
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR);
} else {
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
}
if (!config_["interface"].isString()) {
if (family_ == AF_INET) {
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_ROUTE);
} else {
nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_ROUTE);
}
}
efd_ = epoll_create1(EPOLL_CLOEXEC);
if (efd_ < 0) {
throw std::runtime_error("Can't create epoll");
@ -228,7 +259,23 @@ void waybar::modules::Network::worker() {
int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1);
if (ec > 0) {
for (auto i = 0; i < ec; i++) {
if (events[i].data.fd != nl_socket_get_fd(ev_sock_) || nl_recvmsgs_default(ev_sock_) < 0) {
if (events[i].data.fd == nl_socket_get_fd(ev_sock_)) {
int rc = 0;
// Read as many message as possible, until the socket blocks
while (true) {
errno = 0;
rc = nl_recvmsgs_default(ev_sock_);
if (rc == -NLE_AGAIN || errno == EAGAIN) {
rc = 0;
break;
}
}
if (rc < 0) {
spdlog::error("nl_recvmsgs_default error: {}", nl_geterror(-rc));
thread_.stop();
break;
}
} else {
thread_.stop();
break;
}
@ -245,6 +292,7 @@ const std::string waybar::modules::Network::getNetworkState() const {
#endif
return "disconnected";
}
if (!carrier_) return "disconnected";
if (ipaddr_.empty()) return "linked";
if (essid_.empty()) return "ethernet";
return "wifi";
@ -342,349 +390,312 @@ auto waybar::modules::Network::update() -> void {
ALabel::update();
}
// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
int waybar::modules::Network::getExternalInterface(int skip_idx) const {
static const uint32_t route_buffer_size = 8192;
struct nlmsghdr * hdr = nullptr;
struct rtmsg * rt = nullptr;
char resp[route_buffer_size] = {0};
int ifidx = -1;
/* Prepare request. */
constexpr uint32_t reqlen = NLMSG_SPACE(sizeof(*rt));
char req[reqlen] = {0};
/* Build the RTM_GETROUTE request. */
hdr = reinterpret_cast<struct nlmsghdr *>(req);
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*rt));
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
hdr->nlmsg_type = RTM_GETROUTE;
rt = static_cast<struct rtmsg *>(NLMSG_DATA(hdr));
rt->rtm_family = family_;
rt->rtm_table = RT_TABLE_MAIN;
/* Issue the query. */
if (netlinkRequest(req, reqlen) < 0) {
goto out;
}
/* Read the response(s).
*
* WARNING: All the packets generated by the request must be consumed (as in,
* consume responses till NLMSG_DONE/NLMSG_ERROR is encountered).
*/
do {
auto len = netlinkResponse(resp, route_buffer_size);
if (len < 0) {
goto out;
}
/* Parse the response payload into netlink messages. */
for (hdr = reinterpret_cast<struct nlmsghdr *>(resp); NLMSG_OK(hdr, len);
hdr = NLMSG_NEXT(hdr, len)) {
if (hdr->nlmsg_type == NLMSG_DONE) {
goto out;
}
if (hdr->nlmsg_type == NLMSG_ERROR) {
/* Even if we found the interface index, something is broken with the
* netlink socket, so return an error.
*/
ifidx = -1;
goto out;
}
/* If we found the correct answer, skip parsing the attributes. */
if (ifidx != -1) {
continue;
}
/* Find the message(s) concerting the main routing table, each message
* corresponds to a single routing table entry.
*/
rt = static_cast<struct rtmsg *>(NLMSG_DATA(hdr));
if (rt->rtm_table != RT_TABLE_MAIN) {
continue;
}
/* Parse all the attributes for a single routing table entry. */
struct rtattr *attr = RTM_RTA(rt);
uint64_t attrlen = RTM_PAYLOAD(hdr);
bool has_gateway = false;
bool has_destination = false;
int temp_idx = -1;
for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) {
/* Determine if this routing table entry corresponds to the default
* route by seeing if it has a gateway, and if a destination addr is
* set, that it is all 0s.
*/
switch (attr->rta_type) {
case RTA_GATEWAY:
/* The gateway of the route.
*
* If someone every needs to figure out the gateway address as well,
* it's here as the attribute payload.
*/
has_gateway = true;
break;
case RTA_DST: {
/* The destination address.
* Should be either missing, or maybe all 0s. Accept both.
*/
const uint32_t nr_zeroes = (family_ == AF_INET) ? 4 : 16;
unsigned char c = 0;
size_t dstlen = RTA_PAYLOAD(attr);
if (dstlen != nr_zeroes) {
break;
}
for (uint32_t i = 0; i < dstlen; i += 1) {
c |= *((unsigned char *)RTA_DATA(attr) + i);
}
has_destination = (c == 0);
break;
}
case RTA_OIF:
/* The output interface index. */
temp_idx = *static_cast<int *>(RTA_DATA(attr));
break;
default:
break;
}
}
/* If this is the default route, and we know the interface index,
* we can stop parsing this message.
*/
if (has_gateway && !has_destination && temp_idx != -1 && temp_idx != skip_idx) {
ifidx = temp_idx;
break;
}
}
} while (true);
out:
return ifidx;
}
void waybar::modules::Network::getInterfaceAddress() {
struct ifaddrs *ifaddr, *ifa;
cidr_ = 0;
int success = getifaddrs(&ifaddr);
if (success != 0) {
return;
}
ifa = ifaddr;
while (ifa != nullptr) {
if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ &&
ifa->ifa_name == ifname_) {
char ipaddr[INET6_ADDRSTRLEN];
char netmask[INET6_ADDRSTRLEN];
unsigned int cidr = 0;
if (family_ == AF_INET) {
ipaddr_ = inet_ntop(AF_INET,
&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
ipaddr,
INET_ADDRSTRLEN);
auto net_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask);
netmask_ = inet_ntop(AF_INET, &net_addr->sin_addr, netmask, INET_ADDRSTRLEN);
unsigned int cidrRaw = net_addr->sin_addr.s_addr;
while (cidrRaw) {
cidr += cidrRaw & 1;
cidrRaw >>= 1;
}
} else {
ipaddr_ = inet_ntop(AF_INET6,
&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr,
ipaddr,
INET6_ADDRSTRLEN);
auto net_addr = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_netmask);
netmask_ = inet_ntop(AF_INET6, &net_addr->sin6_addr, netmask, INET6_ADDRSTRLEN);
for (size_t i = 0; i < sizeof(net_addr->sin6_addr.s6_addr); ++i) {
unsigned char cidrRaw = net_addr->sin6_addr.s6_addr[i];
while (cidrRaw) {
cidr += cidrRaw & 1;
cidrRaw >>= 1;
}
}
}
cidr_ = cidr;
break;
}
ifa = ifa->ifa_next;
}
freeifaddrs(ifaddr);
}
int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) const {
struct sockaddr_nl sa = {};
sa.nl_family = AF_NETLINK;
sa.nl_groups = groups;
struct iovec iov = {req, reqlen};
struct msghdr msg = {
.msg_name = &sa,
.msg_namelen = sizeof(sa),
.msg_iov = &iov,
.msg_iovlen = 1,
};
return sendmsg(nl_socket_get_fd(ev_sock_), &msg, 0);
}
int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint32_t groups) const {
struct sockaddr_nl sa = {};
sa.nl_family = AF_NETLINK;
sa.nl_groups = groups;
struct iovec iov = {resp, resplen};
struct msghdr msg = {
.msg_name = &sa,
.msg_namelen = sizeof(sa),
.msg_iov = &iov,
.msg_iovlen = 1,
};
auto ret = recvmsg(nl_socket_get_fd(ev_sock_), &msg, 0);
if (msg.msg_flags & MSG_TRUNC) {
return -1;
}
return ret;
}
bool waybar::modules::Network::checkInterface(struct ifinfomsg *rtif, std::string name) {
bool waybar::modules::Network::checkInterface(std::string name) {
if (config_["interface"].isString()) {
return config_["interface"].asString() == name ||
wildcardMatch(config_["interface"].asString(), name);
}
// getExternalInterface may need some delay to detect external interface
for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) {
auto external_iface = getExternalInterface();
if (external_iface > 0) {
return external_iface == rtif->ifi_index;
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
return false;
}
int waybar::modules::Network::getPreferredIface(int skip_idx, bool wait) const {
int ifid = -1;
if (config_["interface"].isString()) {
ifid = if_nametoindex(config_["interface"].asCString());
if (ifid > 0) {
return ifid;
} else {
// Try with wildcard
struct ifaddrs *ifaddr, *ifa;
int success = getifaddrs(&ifaddr);
if (success != 0) {
return -1;
}
ifa = ifaddr;
ifid = -1;
while (ifa != nullptr) {
if (wildcardMatch(config_["interface"].asString(), ifa->ifa_name)) {
ifid = if_nametoindex(ifa->ifa_name);
break;
}
ifa = ifa->ifa_next;
}
freeifaddrs(ifaddr);
return ifid;
}
}
// getExternalInterface may need some delay to detect external interface
for (uint8_t tries = 0; tries < MAX_RETRY; tries += 1) {
ifid = getExternalInterface(skip_idx);
if (ifid > 0) {
return ifid;
}
if (wait) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
return -1;
}
void waybar::modules::Network::clearIface() {
ifid_ = -1;
essid_.clear();
ipaddr_.clear();
netmask_.clear();
carrier_ = false;
cidr_ = 0;
signal_strength_dbm_ = 0;
signal_strength_ = 0;
frequency_ = 0;
}
void waybar::modules::Network::checkNewInterface(struct ifinfomsg *rtif) {
auto new_iface = getPreferredIface(rtif->ifi_index);
if (new_iface != -1) {
ifid_ = new_iface;
char ifname[IF_NAMESIZE];
if_indextoname(new_iface, ifname);
ifname_ = ifname;
getInterfaceAddress();
thread_timer_.wake_up();
} else {
ifid_ = -1;
dp.emit();
}
}
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
auto net = static_cast<waybar::modules::Network *>(data);
std::lock_guard<std::mutex> lock(net->mutex_);
auto nh = nlmsg_hdr(msg);
auto ifi = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
if (nh->nlmsg_type == RTM_DELADDR) {
// Check for valid interface
if (ifi->ifi_index == net->ifid_) {
net->ipaddr_.clear();
net->netmask_.clear();
net->cidr_ = 0;
if (!(ifi->ifi_flags & IFF_RUNNING)) {
net->clearIface();
// Check for a new interface and get info
net->checkNewInterface(ifi);
} else {
net->dp.emit();
}
bool is_del_event = false;
switch (nh->nlmsg_type) {
case RTM_DELLINK:
is_del_event = true;
case RTM_NEWLINK: {
struct ifinfomsg *ifi = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
ssize_t attrlen = IFLA_PAYLOAD(nh);
struct rtattr *ifla = IFLA_RTA(ifi);
const char *ifname = NULL;
size_t ifname_len = 0;
bool carrier = false;
if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) {
return NL_OK;
}
} else if (nh->nlmsg_type == RTM_NEWLINK || nh->nlmsg_type == RTM_DELLINK) {
char ifname[IF_NAMESIZE];
if_indextoname(ifi->ifi_index, ifname);
// Check for valid interface
if (ifi->ifi_index != net->ifid_ && net->checkInterface(ifi, ifname)) {
net->ifname_ = ifname;
net->ifid_ = ifi->ifi_index;
// Get Iface and WIFI info
net->getInterfaceAddress();
net->thread_timer_.wake_up();
return NL_OK;
} else if (ifi->ifi_index == net->ifid_ &&
(!(ifi->ifi_flags & IFF_RUNNING) || !(ifi->ifi_flags & IFF_UP) ||
!net->checkInterface(ifi, ifname))) {
for (; RTA_OK(ifla, attrlen); ifla = RTA_NEXT(ifla, attrlen)) {
switch (ifla->rta_type) {
case IFLA_IFNAME:
ifname = static_cast<const char *>(RTA_DATA(ifla));
ifname_len = RTA_PAYLOAD(ifla) - 1; // minus \0
break;
case IFLA_CARRIER: {
carrier = *(char*)RTA_DATA(ifla) == 1;
break;
}
}
}
if (!is_del_event && ifi->ifi_index == net->ifid_) {
// Update inferface information
if (net->ifname_.empty()) {
std::string new_ifname (ifname, ifname_len);
net->ifname_ = new_ifname;
}
net->carrier_ = carrier;
} 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);
net->ifname_ = new_ifname;
net->ifid_ = ifi->ifi_index;
net->carrier_ = carrier;
net->thread_timer_.wake_up();
/* An address for this new interface should be received via an
* RTM_NEWADDR event either because we ask for a dump of both links
* and addrs, or because this interface has just been created and
* the addr will be sent after the RTM_NEWLINK event.
* So we don't need to do anything. */
}
} else if (is_del_event && net->ifid_ >= 0) {
// Our interface has been deleted, start looking/waiting for one we care.
spdlog::debug("network: interface {}/{} deleted", net->ifname_, net->ifid_);
net->clearIface();
// Check for a new interface and get info
net->checkNewInterface(ifi);
return NL_OK;
}
} else {
char ifname[IF_NAMESIZE];
if_indextoname(ifi->ifi_index, ifname);
// Auto detected network can also be assigned here
if (ifi->ifi_index != net->ifid_ && net->checkInterface(ifi, ifname)) {
// If iface is different, clear data
if (ifi->ifi_index != net->ifid_) {
net->clearIface();
}
net->ifname_ = ifname;
net->ifid_ = ifi->ifi_index;
}
// Check for valid interface
if (ifi->ifi_index == net->ifid_) {
// Get Iface and WIFI info
net->getInterfaceAddress();
net->thread_timer_.wake_up();
return NL_OK;
net->dp.emit();
}
break;
}
return NL_SKIP;
case RTM_DELADDR:
is_del_event = true;
case RTM_NEWADDR: {
struct ifaddrmsg *ifa = static_cast<struct ifaddrmsg *>(NLMSG_DATA(nh));
ssize_t attrlen = IFA_PAYLOAD(nh);
struct rtattr *ifa_rta = IFA_RTA(ifa);
if ((int)ifa->ifa_index != net->ifid_) {
return NL_OK;
}
if (ifa->ifa_family != net->family_) {
return NL_OK;
}
// We ignore address mark as scope for the link or host,
// which should leave scope global addresses.
if (ifa->ifa_scope >= RT_SCOPE_LINK) {
return NL_OK;
}
for (; RTA_OK(ifa_rta, attrlen); ifa_rta = RTA_NEXT(ifa_rta, attrlen)) {
switch (ifa_rta->rta_type) {
case IFA_ADDRESS: {
char ipaddr[INET6_ADDRSTRLEN];
if (!is_del_event) {
net->ipaddr_ = inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta),
ipaddr, sizeof (ipaddr));
net->cidr_ = ifa->ifa_prefixlen;
switch (ifa->ifa_family) {
case AF_INET: {
struct in_addr netmask;
netmask.s_addr = htonl(~0 << (32 - ifa->ifa_prefixlen));
net->netmask_ = inet_ntop(ifa->ifa_family, &netmask,
ipaddr, sizeof (ipaddr));
}
case AF_INET6: {
struct in6_addr netmask;
for (int i = 0; i < 16; i++) {
int v = (i + 1) * 8 - ifa->ifa_prefixlen;
if (v < 0) v = 0;
if (v > 8) v = 8;
netmask.s6_addr[i] = ~0 << v;
}
net->netmask_ = inet_ntop(ifa->ifa_family, &netmask,
ipaddr, sizeof (ipaddr));
}
}
spdlog::debug("network: {}, new addr {}/{}", net->ifname_, net->ipaddr_, net->cidr_);
} else {
net->ipaddr_.clear();
net->cidr_ = 0;
net->netmask_.clear();
spdlog::debug("network: {} addr deleted {}/{}",
net->ifname_,
inet_ntop(ifa->ifa_family, RTA_DATA(ifa_rta),
ipaddr, sizeof (ipaddr)),
ifa->ifa_prefixlen);
}
net->dp.emit();
break;
}
}
}
break;
}
case RTM_DELROUTE:
is_del_event = true;
case RTM_NEWROUTE: {
// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
// to find the interface used to reach the outside world
struct rtmsg *rtm = static_cast<struct rtmsg *>(NLMSG_DATA(nh));
ssize_t attrlen = RTM_PAYLOAD(nh);
struct rtattr *attr = RTM_RTA(rtm);
bool has_gateway = false;
bool has_destination = false;
int temp_idx = -1;
/* If we found the correct answer, skip parsing the attributes. */
if (!is_del_event && net->ifid_ != -1) {
return NL_OK;
}
/* Find the message(s) concerting the main routing table, each message
* corresponds to a single routing table entry.
*/
if (rtm->rtm_table != RT_TABLE_MAIN) {
return NL_OK;
}
/* Parse all the attributes for a single routing table entry. */
for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen)) {
/* Determine if this routing table entry corresponds to the default
* route by seeing if it has a gateway, and if a destination addr is
* set, that it is all 0s.
*/
switch(attr->rta_type) {
case RTA_GATEWAY:
/* The gateway of the route.
*
* If someone every needs to figure out the gateway address as well,
* it's here as the attribute payload.
*/
has_gateway = true;
break;
case RTA_DST: {
/* The destination address.
* Should be either missing, or maybe all 0s. Accept both.
*/
const uint32_t nr_zeroes = (net->family_ == AF_INET) ? 4 : 16;
unsigned char c = 0;
size_t dstlen = RTA_PAYLOAD(attr);
if (dstlen != nr_zeroes) {
break;
}
for (uint32_t i = 0; i < dstlen; i += 1) {
c |= *((unsigned char *)RTA_DATA(attr) + i);
}
has_destination = (c == 0);
break;
}
case RTA_OIF:
/* The output interface index. */
temp_idx = *static_cast<int *>(RTA_DATA(attr));
break;
default:
break;
}
/* If this is the default route, and we know the interface index,
* we can stop parsing this message.
*/
if (has_gateway && !has_destination && temp_idx != -1) {
if (!is_del_event) {
net->ifid_ = temp_idx;
spdlog::debug("network: new default route via if{}", temp_idx);
/* Ask ifname associated with temp_idx as well as carrier status */
struct ifinfomsg ifinfo_hdr = {
.ifi_family = AF_UNSPEC,
.ifi_index = temp_idx,
};
int err;
err = nl_send_simple(net->ev_sock_, RTM_GETLINK, NLM_F_REQUEST,
&ifinfo_hdr, sizeof (ifinfo_hdr));
if (err < 0) {
spdlog::error("network: failed to ask link info: {}", err);
/* Ask for a dump of all links instead */
net->want_link_dump_ = true;
}
/* Also ask for the address. Asking for a addresses of a specific
* interface doesn't seems to work so ask for a dump of all
* addresses. */
net->want_addr_dump_ = true;
net->askForStateDump();
net->thread_timer_.wake_up();
} else if (is_del_event && temp_idx == net->ifid_) {
spdlog::debug("network: default route deleted {}/if{}",
net->ifname_, temp_idx);
net->ifname_.clear();
net->clearIface();
net->dp.emit();
/* Ask for a dump of all routes in case another one is already
* setup. If there's none, there'll be an event with new one
* later. */
net->want_route_dump_ = true;
net->askForStateDump();
}
}
}
break;
}
}
return NL_OK;
}
void waybar::modules::Network::askForStateDump(void) {
/* We need to wait until the current dump is done before sending new
* messages. handleEventsDone() is called when a dump is done. */
if (dump_in_progress_)
return;
struct rtgenmsg rt_hdr = {
.rtgen_family = AF_UNSPEC,
};
if (want_route_dump_) {
rt_hdr.rtgen_family = family_;
nl_send_simple(ev_sock_, RTM_GETROUTE, NLM_F_DUMP,
&rt_hdr, sizeof (rt_hdr));
want_route_dump_ = false;
dump_in_progress_ = true;
} else if (want_link_dump_) {
nl_send_simple(ev_sock_, RTM_GETLINK, NLM_F_DUMP,
&rt_hdr, sizeof (rt_hdr));
want_link_dump_ = false;
dump_in_progress_ = true;
} else if (want_addr_dump_) {
rt_hdr.rtgen_family = family_;
nl_send_simple(ev_sock_, RTM_GETADDR, NLM_F_DUMP,
&rt_hdr, sizeof (rt_hdr));
want_addr_dump_ = false;
dump_in_progress_ = true;
}
}
int waybar::modules::Network::handleEventsDone(struct nl_msg *msg, void *data) {
auto net = static_cast<waybar::modules::Network *>(data);
net->dump_in_progress_ = false;
net->askForStateDump();
return NL_OK;
}
int waybar::modules::Network::handleScan(struct nl_msg *msg, void *data) {