Merge pull request #319 from Alexays/network

Network improvements
This commit is contained in:
Alex 2019-05-16 14:11:32 +02:00 committed by GitHub
commit 31f63398dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 228 additions and 117 deletions

View File

@ -28,7 +28,6 @@ class Network : public ALabel {
static int handleScan(struct nl_msg*, void*); static int handleScan(struct nl_msg*, void*);
void worker(); void worker();
void disconnected();
void createInfoSocket(); void createInfoSocket();
void createEventSocket(); void createEventSocket();
int getExternalInterface(); int getExternalInterface();
@ -38,24 +37,30 @@ class Network : public ALabel {
void parseEssid(struct nlattr**); void parseEssid(struct nlattr**);
void parseSignal(struct nlattr**); void parseSignal(struct nlattr**);
bool associatedOrJoined(struct nlattr**); bool associatedOrJoined(struct nlattr**);
bool checkInterface(int if_index, std::string name);
int getPreferredIface();
auto getInfo() -> void; auto getInfo() -> void;
bool wildcardMatch(const std::string& pattern, const std::string& text);
waybar::util::SleeperThread thread_; waybar::util::SleeperThread thread_;
waybar::util::SleeperThread thread_timer_; waybar::util::SleeperThread thread_timer_;
int ifid_; int ifid_;
int last_ext_iface_;
sa_family_t family_; sa_family_t family_;
struct sockaddr_nl nladdr_ = {0}; struct sockaddr_nl nladdr_ = {0};
struct nl_sock* sk_ = nullptr; struct nl_sock* sock_ = nullptr;
struct nl_sock* info_sock_ = nullptr; struct nl_sock* ev_sock_ = nullptr;
int efd_; int efd_;
int ev_fd_; int ev_fd_;
int nl80211_id_; int nl80211_id_;
std::mutex mutex_;
std::string essid_; std::string essid_;
std::string ifname_; std::string ifname_;
std::string ipaddr_; std::string ipaddr_;
std::string netmask_; std::string netmask_;
int cidr_; int cidr_;
bool linked_;
int32_t signal_strength_dbm_; int32_t signal_strength_dbm_;
uint8_t signal_strength_; uint8_t signal_strength_;
}; };

View File

@ -104,9 +104,10 @@
"bat": "BAT2" "bat": "BAT2"
}, },
"network": { "network": {
// "interface": "wlp2s0", // (Optional) To force the use of this interface // "interface": "wlp2*", // (Optional) To force the use of this interface
"format-wifi": "{essid} ({signalStrength}%) ", "format-wifi": "{essid} ({signalStrength}%) ",
"format-ethernet": "{ifname}: {ipaddr}/{cidr} ", "format-ethernet": "{ifname}: {ipaddr}/{cidr} ",
"format-linked": "{ifname} (No IP) ",
"format-disconnected": "Disconnected ⚠" "format-disconnected": "Disconnected ⚠"
}, },
"pulseaudio": { "pulseaudio": {

View File

@ -3,7 +3,9 @@
waybar::modules::Network::Network(const std::string &id, const Json::Value &config) waybar::modules::Network::Network(const std::string &id, const Json::Value &config)
: ALabel(config, "{ifname}", 60), : ALabel(config, "{ifname}", 60),
family_(AF_INET), ifid_(-1),
last_ext_iface_(-1),
family_(config["family"] == "ipv6" ? AF_INET6 : AF_INET),
efd_(-1), efd_(-1),
ev_fd_(-1), ev_fd_(-1),
cidr_(-1), cidr_(-1),
@ -15,19 +17,12 @@ waybar::modules::Network::Network(const std::string &id, const Json::Value &conf
} }
createInfoSocket(); createInfoSocket();
createEventSocket(); createEventSocket();
if (config_["interface"].isString()) { auto default_iface = getPreferredIface();
ifid_ = if_nametoindex(config_["interface"].asCString()); if (default_iface != -1) {
ifname_ = config_["interface"].asString();
if (ifid_ <= 0) {
throw std::runtime_error("Can't found network interface");
}
} else {
ifid_ = getExternalInterface();
if (ifid_ > 0) {
char ifname[IF_NAMESIZE]; char ifname[IF_NAMESIZE];
if_indextoname(ifid_, ifname); if_indextoname(default_iface, ifname);
ifname_ = ifname; ifname_ = ifname;
} getInterfaceAddress();
} }
dp.emit(); dp.emit();
worker(); worker();
@ -42,38 +37,42 @@ waybar::modules::Network::~Network() {
if (efd_ > -1) { if (efd_ > -1) {
close(efd_); close(efd_);
} }
if (info_sock_ != nullptr) { if (ev_sock_ != nullptr) {
nl_socket_drop_membership(info_sock_, RTMGRP_LINK); nl_socket_drop_membership(ev_sock_, RTNLGRP_LINK);
nl_socket_drop_membership(info_sock_, RTMGRP_IPV4_IFADDR); nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV4_IFADDR);
nl_close(info_sock_); nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
nl_socket_free(info_sock_); nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV4_ROUTE);
nl_socket_drop_membership(ev_sock_, RTNLGRP_IPV6_ROUTE);
nl_close(ev_sock_);
nl_socket_free(ev_sock_);
} }
if (sk_ != nullptr) { if (sock_ != nullptr) {
nl_close(sk_); nl_close(sock_);
nl_socket_free(sk_); nl_socket_free(sock_);
} }
} }
void waybar::modules::Network::createInfoSocket() { void waybar::modules::Network::createInfoSocket() {
info_sock_ = nl_socket_alloc(); ev_sock_ = nl_socket_alloc();
if (nl_connect(info_sock_, NETLINK_ROUTE) != 0) { nl_socket_disable_seq_check(ev_sock_);
nl_socket_modify_cb(ev_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this);
nl_join_groups(ev_sock_, RTMGRP_LINK);
if (nl_connect(ev_sock_, NETLINK_ROUTE) != 0) {
throw std::runtime_error("Can't connect network socket"); throw std::runtime_error("Can't connect network socket");
} }
if (nl_socket_add_membership(info_sock_, RTMGRP_LINK) != 0) { nl_socket_add_membership(ev_sock_, RTNLGRP_LINK);
throw std::runtime_error("Can't add membership"); nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_IFADDR);
} nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_IFADDR);
if (nl_socket_add_membership(info_sock_, RTMGRP_IPV4_IFADDR) != 0) { nl_socket_add_membership(ev_sock_, RTNLGRP_IPV4_ROUTE);
throw std::runtime_error("Can't add membership"); nl_socket_add_membership(ev_sock_, RTNLGRP_IPV6_ROUTE);
}
nl_socket_disable_seq_check(info_sock_);
nl_socket_modify_cb(info_sock_, NL_CB_VALID, NL_CB_CUSTOM, handleEvents, this);
efd_ = epoll_create1(EPOLL_CLOEXEC); efd_ = epoll_create1(EPOLL_CLOEXEC);
if (efd_ < 0) { if (efd_ < 0) {
throw std::runtime_error("Can't create epoll"); throw std::runtime_error("Can't create epoll");
} }
{ {
ev_fd_ = eventfd(0, EFD_NONBLOCK); ev_fd_ = eventfd(0, EFD_NONBLOCK);
struct epoll_event event = {0}; struct epoll_event event;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLET; event.events = EPOLLIN | EPOLLET;
event.data.fd = ev_fd_; event.data.fd = ev_fd_;
if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) { if (epoll_ctl(efd_, EPOLL_CTL_ADD, ev_fd_, &event) == -1) {
@ -81,8 +80,9 @@ void waybar::modules::Network::createInfoSocket() {
} }
} }
{ {
auto fd = nl_socket_get_fd(info_sock_); auto fd = nl_socket_get_fd(ev_sock_);
struct epoll_event event = {0}; struct epoll_event event;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
event.data.fd = fd; event.data.fd = fd;
if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) { if (epoll_ctl(efd_, EPOLL_CTL_ADD, fd, &event) == -1) {
@ -92,14 +92,14 @@ void waybar::modules::Network::createInfoSocket() {
} }
void waybar::modules::Network::createEventSocket() { void waybar::modules::Network::createEventSocket() {
sk_ = nl_socket_alloc(); sock_ = nl_socket_alloc();
if (genl_connect(sk_) != 0) { if (genl_connect(sock_) != 0) {
throw std::runtime_error("Can't connect to netlink socket"); throw std::runtime_error("Can't connect to netlink socket");
} }
if (nl_socket_modify_cb(sk_, NL_CB_VALID, NL_CB_CUSTOM, handleScan, this) < 0) { if (nl_socket_modify_cb(sock_, NL_CB_VALID, NL_CB_CUSTOM, handleScan, this) < 0) {
throw std::runtime_error("Can't set callback"); throw std::runtime_error("Can't set callback");
} }
nl80211_id_ = genl_ctrl_resolve(sk_, "nl80211"); nl80211_id_ = genl_ctrl_resolve(sock_, "nl80211");
if (nl80211_id_ < 0) { if (nl80211_id_ < 0) {
throw std::runtime_error("Can't resolve nl80211 interface"); throw std::runtime_error("Can't resolve nl80211 interface");
} }
@ -118,8 +118,8 @@ void waybar::modules::Network::worker() {
int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1); int ec = epoll_wait(efd_, events.data(), EPOLL_MAX, -1);
if (ec > 0) { if (ec > 0) {
for (auto i = 0; i < ec; i++) { for (auto i = 0; i < ec; i++) {
if (events[i].data.fd == nl_socket_get_fd(info_sock_)) { if (events[i].data.fd == nl_socket_get_fd(ev_sock_)) {
nl_recvmsgs_default(info_sock_); nl_recvmsgs_default(ev_sock_);
} else { } else {
thread_.stop(); thread_.stop();
break; break;
@ -135,7 +135,7 @@ auto waybar::modules::Network::update() -> void {
if (config_["tooltip-format"].isString()) { if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString(); tooltip_format = config_["tooltip-format"].asString();
} }
if (ifid_ <= 0 || ipaddr_.empty()) { if (ifid_ <= 0 || !linked_) {
if (config_["format-disconnected"].isString()) { if (config_["format-disconnected"].isString()) {
default_format_ = config_["format-disconnected"].asString(); default_format_ = config_["format-disconnected"].asString();
} }
@ -153,6 +153,14 @@ auto waybar::modules::Network::update() -> void {
tooltip_format = config_["tooltip-format-ethernet"].asString(); tooltip_format = config_["tooltip-format-ethernet"].asString();
} }
connectiontype = "ethernet"; connectiontype = "ethernet";
} else if (ipaddr_.empty()) {
if (config_["format-linked"].isString()) {
default_format_ = config_["format-linked"].asString();
}
if (config_["tooltip-format-linked"].isString()) {
tooltip_format = config_["tooltip-format-linked"].asString();
}
connectiontype = "linked";
} else { } else {
if (config_["format-wifi"].isString()) { if (config_["format-wifi"].isString()) {
default_format_ = config_["format-wifi"].asString(); default_format_ = config_["format-wifi"].asString();
@ -196,21 +204,6 @@ auto waybar::modules::Network::update() -> void {
} }
} }
void waybar::modules::Network::disconnected() {
essid_.clear();
signal_strength_dbm_ = 0;
signal_strength_ = 0;
ipaddr_.clear();
netmask_.clear();
cidr_ = 0;
if (!config_["interface"].isString()) {
ifname_.clear();
ifid_ = -1;
}
// Need to wait otherwise we'll have the same information
thread_.sleep_for(std::chrono::seconds(1));
}
// Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698 // Based on https://gist.github.com/Yawning/c70d804d4b8ae78cc698
int waybar::modules::Network::getExternalInterface() { int waybar::modules::Network::getExternalInterface() {
static const uint32_t route_buffer_size = 8192; static const uint32_t route_buffer_size = 8192;
@ -330,6 +323,7 @@ int waybar::modules::Network::getExternalInterface() {
} while (true); } while (true);
out: out:
last_ext_iface_ = ifidx;
return ifidx; return ifidx;
} }
@ -340,14 +334,23 @@ void waybar::modules::Network::getInterfaceAddress() {
netmask_.clear(); netmask_.clear();
cidr_ = 0; cidr_ = 0;
int success = getifaddrs(&ifaddr); int success = getifaddrs(&ifaddr);
if (success == 0) { if (success != 0) {
return;
}
ifa = ifaddr; ifa = ifaddr;
while (ifa != nullptr && ipaddr_.empty() && netmask_.empty()) { while (ifa != nullptr && ipaddr_.empty() && netmask_.empty()) {
if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_) { if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == family_ &&
if (strcmp(ifa->ifa_name, ifname_.c_str()) == 0) { ifa->ifa_name == ifname_) {
ipaddr_ = inet_ntoa(((struct sockaddr_in *)ifa->ifa_addr)->sin_addr); char ipaddr[INET6_ADDRSTRLEN];
netmask_ = inet_ntoa(((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr); ipaddr_ = inet_ntop(family_,
cidrRaw = ((struct sockaddr_in *)(ifa->ifa_netmask))->sin_addr.s_addr; &reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
ipaddr,
INET6_ADDRSTRLEN);
char netmask[INET6_ADDRSTRLEN];
auto net_addr = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask);
netmask_ = inet_ntop(family_, &net_addr->sin_addr, netmask, INET6_ADDRSTRLEN);
cidrRaw = net_addr->sin_addr.s_addr;
linked_ = ifa->ifa_flags & IFF_RUNNING;
unsigned int cidr = 0; unsigned int cidr = 0;
while (cidrRaw) { while (cidrRaw) {
cidr += cidrRaw & 1; cidr += cidrRaw & 1;
@ -355,11 +358,9 @@ void waybar::modules::Network::getInterfaceAddress() {
} }
cidr_ = cidr; cidr_ = cidr;
} }
}
ifa = ifa->ifa_next; ifa = ifa->ifa_next;
} }
freeifaddrs(ifaddr); freeifaddrs(ifaddr);
}
} }
int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) { int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_t groups) {
@ -368,7 +369,7 @@ int waybar::modules::Network::netlinkRequest(void *req, uint32_t reqlen, uint32_
sa.nl_groups = groups; sa.nl_groups = groups;
struct iovec iov = {req, reqlen}; struct iovec iov = {req, reqlen};
struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0}; struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0};
return sendmsg(nl_socket_get_fd(info_sock_), &msg, 0); return sendmsg(nl_socket_get_fd(ev_sock_), &msg, 0);
} }
int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint32_t groups) { int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint32_t groups) {
@ -377,54 +378,120 @@ int waybar::modules::Network::netlinkResponse(void *resp, uint32_t resplen, uint
sa.nl_groups = groups; sa.nl_groups = groups;
struct iovec iov = {resp, resplen}; struct iovec iov = {resp, resplen};
struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0}; struct msghdr msg = {&sa, sizeof(sa), &iov, 1, nullptr, 0, 0};
auto ret = recvmsg(nl_socket_get_fd(info_sock_), &msg, 0); auto ret = recvmsg(nl_socket_get_fd(ev_sock_), &msg, 0);
if (msg.msg_flags & MSG_TRUNC) { if (msg.msg_flags & MSG_TRUNC) {
return -1; return -1;
} }
return ret; return ret;
} }
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) { bool waybar::modules::Network::checkInterface(int if_index, std::string name) {
auto net = static_cast<waybar::modules::Network *>(data); if (config_["interface"].isString()) {
bool need_update = false; return config_["interface"].asString() == name ||
struct nlmsghdr *nh = nlmsg_hdr(msg); wildcardMatch(config_["interface"].asString(), name);
}
auto external_iface = getExternalInterface();
if (external_iface == -1) {
// Try with lastest working external iface
return last_ext_iface_ == if_index;
}
return external_iface == if_index;
}
if (nh->nlmsg_type == RTM_NEWADDR) { int waybar::modules::Network::getPreferredIface() {
need_update = true; if (config_["interface"].isString()) {
ifid_ = if_nametoindex(config_["interface"].asCString());
if (ifid_ > 0) {
ifname_ = config_["interface"].asString();
return ifid_;
} else {
// Try with wildcard
struct ifaddrs *ifaddr, *ifa;
int success = getifaddrs(&ifaddr);
if (success != 0) {
return -1;
} }
if (nh->nlmsg_type < RTM_NEWADDR) { ifa = ifaddr;
auto rtif = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh)); ifid_ = -1;
if (rtif->ifi_index == static_cast<int>(net->ifid_)) { while (ifa != nullptr) {
need_update = true; if (wildcardMatch(config_["interface"].asString(), ifa->ifa_name)) {
if (!(rtif->ifi_flags & IFF_RUNNING)) { ifid_ = if_nametoindex(ifa->ifa_name);
net->disconnected();
net->dp.emit();
return NL_SKIP;
}
}
}
if (net->ifid_ <= 0 && !net->config_["interface"].isString()) {
for (uint8_t i = 0; i < MAX_RETRY; i += 1) {
net->ifid_ = net->getExternalInterface();
if (net->ifid_ > 0) {
break; break;
} }
// Need to wait before get external interface ifa = ifa->ifa_next;
net->thread_.sleep_for(std::chrono::seconds(1));
} }
if (net->ifid_ > 0) { freeifaddrs(ifaddr);
return ifid_;
}
}
ifid_ = getExternalInterface();
if (ifid_ > 0) {
char ifname[IF_NAMESIZE]; char ifname[IF_NAMESIZE];
if_indextoname(net->ifid_, ifname); if_indextoname(ifid_, ifname);
ifname_ = ifname;
return ifid_;
}
return -1;
}
int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
auto net = static_cast<waybar::modules::Network *>(data);
auto nh = nlmsg_hdr(msg);
std::lock_guard<std::mutex> lock(net->mutex_);
if (nh->nlmsg_type == RTM_NEWADDR) {
auto rtif = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
char ifname[IF_NAMESIZE];
if_indextoname(rtif->ifi_index, ifname);
// Auto detected network can also be assigned here
if (net->checkInterface(rtif->ifi_index, ifname) && net->ifid_ == -1) {
net->linked_ = true;
net->ifname_ = ifname; net->ifname_ = ifname;
need_update = true; net->ifid_ = rtif->ifi_index;
net->dp.emit();
} }
// Check for valid interface
if (rtif->ifi_index == static_cast<int>(net->ifid_)) {
// Get Iface and WIFI info
net->thread_timer_.wake_up();
net->getInterfaceAddress();
net->dp.emit();
} }
if (need_update) { } else if (nh->nlmsg_type == RTM_DELADDR) {
if (net->ifid_ > 0) { auto rtif = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
net->getInfo(); // Check for valid interface
if (rtif->ifi_index == static_cast<int>(net->ifid_)) {
net->ipaddr_.clear();
net->netmask_.clear();
net->cidr_ = 0;
net->dp.emit();
}
} else if (nh->nlmsg_type < RTM_NEWADDR) {
auto rtif = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
char ifname[IF_NAMESIZE];
if_indextoname(rtif->ifi_index, ifname);
// Check for valid interface
if (net->checkInterface(rtif->ifi_index, ifname) && rtif->ifi_flags & IFF_RUNNING) {
net->linked_ = true;
net->ifname_ = ifname;
net->ifid_ = rtif->ifi_index;
net->dp.emit();
} else if (rtif->ifi_index == net->ifid_) {
net->linked_ = false;
net->ifname_.clear();
net->ifid_ = -1;
net->essid_.clear();
net->signal_strength_dbm_ = 0;
net->signal_strength_ = 0;
// Check for a new interface and get info
auto new_iface = net->getPreferredIface();
if (new_iface != -1) {
net->thread_timer_.wake_up();
net->getInterfaceAddress();
} }
net->dp.emit(); net->dp.emit();
} }
}
return NL_SKIP; return NL_SKIP;
} }
@ -515,7 +582,6 @@ bool waybar::modules::Network::associatedOrJoined(struct nlattr **bss) {
} }
auto waybar::modules::Network::getInfo() -> void { auto waybar::modules::Network::getInfo() -> void {
getInterfaceAddress();
struct nl_msg *nl_msg = nlmsg_alloc(); struct nl_msg *nl_msg = nlmsg_alloc();
if (nl_msg == nullptr) { if (nl_msg == nullptr) {
return; return;
@ -527,5 +593,44 @@ auto waybar::modules::Network::getInfo() -> void {
nlmsg_free(nl_msg); nlmsg_free(nl_msg);
return; return;
} }
nl_send_sync(sk_, nl_msg); 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) {
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;
} }