forked from external/yambar
module/network: expose signal strength and rx+tx bitrates
This commit is contained in:
parent
b27eff36f9
commit
a685dadb75
1 changed files with 215 additions and 34 deletions
|
@ -47,7 +47,8 @@ struct private {
|
|||
|
||||
struct {
|
||||
uint16_t family_id;
|
||||
uint32_t seq_nr;
|
||||
uint32_t get_interface_seq_nr;
|
||||
uint32_t get_station_seq_nr;
|
||||
} nl80211;
|
||||
|
||||
bool get_addresses;
|
||||
|
@ -62,6 +63,9 @@ struct private {
|
|||
|
||||
/* WiFi extensions */
|
||||
char *ssid;
|
||||
int signal_strength_dbm;
|
||||
uint32_t rx_bitrate;
|
||||
uint32_t tx_bitrate;
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -136,8 +140,11 @@ content(struct module *mod)
|
|||
tag_new_string(mod, "ipv4", ipv4_str),
|
||||
tag_new_string(mod, "ipv6", ipv6_str),
|
||||
tag_new_string(mod, "ssid", m->ssid),
|
||||
tag_new_int(mod, "signal", m->signal_strength_dbm),
|
||||
tag_new_int(mod, "rx-bitrate", m->rx_bitrate / 1000 / 1000),
|
||||
tag_new_int(mod, "tx-bitrate", m->tx_bitrate / 1000 / 1000),
|
||||
},
|
||||
.count = 8,
|
||||
.count = 11,
|
||||
};
|
||||
|
||||
mtx_unlock(&mod->lock);
|
||||
|
@ -296,19 +303,10 @@ send_ctrl_get_family_request(struct private *m)
|
|||
}
|
||||
|
||||
static bool
|
||||
send_nl80211_get_interface_request(struct private *m)
|
||||
send_nl80211_request(struct private *m, uint8_t cmd, uint16_t flags, uint32_t seq)
|
||||
{
|
||||
if (m->ifindex < 0)
|
||||
return true;
|
||||
|
||||
if (m->nl80211.seq_nr > 0) {
|
||||
LOG_DBG(
|
||||
"%s: nl80211 get-interface request already in progress", m->iface);
|
||||
return true;
|
||||
}
|
||||
|
||||
m->nl80211.seq_nr = time(NULL);
|
||||
LOG_DBG("%s: sending nl80211 get-interface request", m->iface);
|
||||
return false;
|
||||
|
||||
const struct {
|
||||
struct nlmsghdr hdr;
|
||||
|
@ -323,14 +321,14 @@ send_nl80211_get_interface_request(struct private *m)
|
|||
.hdr = {
|
||||
.nlmsg_len = NLMSG_LENGTH(sizeof(req.msg)),
|
||||
.nlmsg_type = m->nl80211.family_id,
|
||||
.nlmsg_flags = NLM_F_REQUEST,
|
||||
.nlmsg_seq = m->nl80211.seq_nr,
|
||||
.nlmsg_flags = flags,
|
||||
.nlmsg_seq = seq,
|
||||
.nlmsg_pid = nl_pid_value(),
|
||||
},
|
||||
|
||||
.msg = {
|
||||
.genl = {
|
||||
.cmd = NL80211_CMD_GET_INTERFACE,
|
||||
.cmd = cmd,
|
||||
.version = 1,
|
||||
},
|
||||
|
||||
|
@ -348,13 +346,52 @@ send_nl80211_get_interface_request(struct private *m)
|
|||
if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) {
|
||||
LOG_ERRNO("%s: failed to send netlink nl80211 get-inteface request",
|
||||
m->iface);
|
||||
m->nl80211.seq_nr = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
send_nl80211_get_interface(struct private *m)
|
||||
{
|
||||
if (m->nl80211.get_interface_seq_nr > 0) {
|
||||
LOG_DBG(
|
||||
"%s: nl80211 get-interface request already in progress", m->iface);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_DBG("%s: sending nl80211 get-interface request", m->iface);
|
||||
|
||||
uint32_t seq = time(NULL);
|
||||
if (send_nl80211_request(m, NL80211_CMD_GET_INTERFACE, NLM_F_REQUEST, seq)) {
|
||||
m->nl80211.get_interface_seq_nr = seq;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
send_nl80211_get_station(struct private *m)
|
||||
{
|
||||
if (m->nl80211.get_station_seq_nr > 0) {
|
||||
LOG_DBG(
|
||||
"%s: nl80211 get-station request already in progress", m->iface);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_DBG("%s: sending nl80211 get-station request", m->iface);
|
||||
|
||||
uint32_t seq = time(NULL);
|
||||
if (send_nl80211_request(
|
||||
m, NL80211_CMD_GET_STATION, NLM_F_REQUEST | NLM_F_DUMP, seq))
|
||||
{
|
||||
m->nl80211.get_station_seq_nr = seq;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len)
|
||||
{
|
||||
|
@ -373,7 +410,8 @@ find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len)
|
|||
m->ifindex = msg->ifi_index;
|
||||
mtx_unlock(&mod->lock);
|
||||
|
||||
send_nl80211_get_interface_request(m);
|
||||
send_nl80211_get_interface(m);
|
||||
send_nl80211_get_station(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -584,7 +622,7 @@ foreach_nlattr_nested(struct module *mod, const void *parent_payload, size_t len
|
|||
|
||||
struct mcast_group {
|
||||
uint32_t id;
|
||||
char *name;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static bool
|
||||
|
@ -601,15 +639,13 @@ parse_mcast_group(struct module *mod, uint16_t type, bool nested,
|
|||
}
|
||||
|
||||
case CTRL_ATTR_MCAST_GRP_NAME: {
|
||||
free(ctx->name);
|
||||
ctx->name = strndup((const char *)payload, len);
|
||||
ctx->name = (const char *)payload;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_WARN("%s: unrecognized GENL MCAST GRP attribute: "
|
||||
"%hu%s (size: %zu bytes)", m->iface,
|
||||
type, nested ? " (nested)" : "", len);
|
||||
"type=%hu, nested=%d, len=%zu", m->iface, type, nested, len);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -628,6 +664,11 @@ parse_mcast_groups(struct module *mod, uint16_t type, bool nested,
|
|||
LOG_DBG("MCAST: %s -> %u", group.name, group.id);
|
||||
|
||||
if (strcmp(group.name, NL80211_MULTICAST_GROUP_MLME) == 0) {
|
||||
/*
|
||||
* Join the nl80211 MLME multicast group - for
|
||||
* CONNECT/DISCONNECT events.
|
||||
*/
|
||||
|
||||
int r = setsockopt(
|
||||
m->genl_sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
|
||||
&group.id, sizeof(int));
|
||||
|
@ -636,7 +677,6 @@ parse_mcast_groups(struct module *mod, uint16_t type, bool nested,
|
|||
LOG_ERRNO("failed to joint the nl80211 MLME mcast group");
|
||||
}
|
||||
|
||||
free(group.name);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -649,7 +689,7 @@ handle_genl_ctrl(struct module *mod, uint16_t type, bool nested,
|
|||
switch (type) {
|
||||
case CTRL_ATTR_FAMILY_ID: {
|
||||
m->nl80211.family_id = *(const uint16_t *)payload;
|
||||
send_nl80211_get_interface_request(m);
|
||||
send_nl80211_get_interface(m);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -663,8 +703,7 @@ handle_genl_ctrl(struct module *mod, uint16_t type, bool nested,
|
|||
|
||||
default:
|
||||
LOG_DBG("%s: unrecognized GENL CTRL attribute: "
|
||||
"%hu%s (size: %zu bytes)", m->iface,
|
||||
type, nested ? " (nested)" : "", len);
|
||||
"type=%hu, nested=%d, len=%zu", m->iface, type, nested, len);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -705,7 +744,6 @@ handle_nl80211_new_interface(struct module *mod, uint16_t type, bool nested,
|
|||
|
||||
case NL80211_ATTR_SSID: {
|
||||
const char *ssid = payload;
|
||||
LOG_INFO("%s: SSID: %.*s (type=%hhu)", m->iface, (int)len, ssid, type);
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
free(m->ssid);
|
||||
|
@ -718,8 +756,129 @@ handle_nl80211_new_interface(struct module *mod, uint16_t type, bool nested,
|
|||
|
||||
default:
|
||||
LOG_DBG("%s: unrecognized nl80211 attribute: "
|
||||
"type=%hu%s, len=%zu", m->iface,
|
||||
type, nested ? " (nested)" : "", len);
|
||||
"type=%hu, nested=%d, len=%zu", m->iface, type, nested, len);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct rate_info_ctx {
|
||||
unsigned bitrate;
|
||||
};
|
||||
|
||||
static bool
|
||||
handle_nl80211_rate_info(struct module *mod, uint16_t type, bool nested,
|
||||
const void *payload, size_t len, void *_ctx)
|
||||
{
|
||||
struct private *m UNUSED = mod->private;
|
||||
struct rate_info_ctx *ctx = _ctx;
|
||||
|
||||
switch (type) {
|
||||
case NL80211_RATE_INFO_BITRATE32: {
|
||||
uint32_t bitrate_100kbit = *(uint32_t *)payload;
|
||||
ctx->bitrate = bitrate_100kbit * 100 * 1000;
|
||||
break;
|
||||
}
|
||||
|
||||
case NL80211_RATE_INFO_BITRATE:
|
||||
if (ctx->bitrate == 0) {
|
||||
uint16_t bitrate_100kbit = *(uint16_t *)payload;
|
||||
ctx->bitrate = bitrate_100kbit * 100 * 1000;
|
||||
} else {
|
||||
/* Prefer the BITRATE32 attribute */
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_DBG("%s: unrecognized nl80211 rate info attribute: "
|
||||
"type=%hu, nested=%d, len=%zu", m->iface, type, nested, len);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct station_info_ctx {
|
||||
bool update_bar;
|
||||
};
|
||||
|
||||
static bool
|
||||
handle_nl80211_station_info(struct module *mod, uint16_t type, bool nested,
|
||||
const void *payload, size_t len, void *_ctx)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
struct station_info_ctx *ctx = _ctx;
|
||||
|
||||
switch (type) {
|
||||
case NL80211_STA_INFO_SIGNAL:
|
||||
LOG_DBG("signal strength (last): %hhd dBm", *(uint8_t *)payload);
|
||||
break;
|
||||
|
||||
case NL80211_STA_INFO_SIGNAL_AVG: {
|
||||
LOG_DBG("signal strength (average): %hhd dBm", *(uint8_t *)payload);
|
||||
mtx_lock(&mod->lock);
|
||||
m->signal_strength_dbm = *(int8_t *)payload;
|
||||
mtx_unlock(&mod->lock);
|
||||
ctx->update_bar = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case NL80211_STA_INFO_TX_BITRATE: {
|
||||
struct rate_info_ctx rctx = {0};
|
||||
foreach_nlattr_nested(
|
||||
mod, payload, len, &handle_nl80211_rate_info, &rctx);
|
||||
|
||||
LOG_DBG("TX bitrate: %.1f Mbit/s", rctx.bitrate / 1000. / 1000.);
|
||||
mtx_lock(&mod->lock);
|
||||
m->tx_bitrate = rctx.bitrate;
|
||||
mtx_unlock(&mod->lock);
|
||||
ctx->update_bar = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case NL80211_STA_INFO_RX_BITRATE: {
|
||||
struct rate_info_ctx rctx = {0};
|
||||
foreach_nlattr_nested(
|
||||
mod, payload, len, &handle_nl80211_rate_info, &rctx);
|
||||
|
||||
LOG_DBG("RX bitrate: %.1f Mbit/s", rctx.bitrate / 1000. / 1000.);
|
||||
mtx_lock(&mod->lock);
|
||||
m->rx_bitrate = rctx.bitrate;
|
||||
mtx_unlock(&mod->lock);
|
||||
ctx->update_bar = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_DBG("%s: unrecognized nl80211 station info attribute: "
|
||||
"type=%hu, nested=%d, len=%zu", m->iface, type, nested, len);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_nl80211_new_station(struct module *mod, uint16_t type, bool nested,
|
||||
const void *payload, size_t len)
|
||||
{
|
||||
struct private *m UNUSED = mod->private;
|
||||
|
||||
switch (type) {
|
||||
case NL80211_ATTR_STA_INFO: {
|
||||
struct station_info_ctx ctx = {0};
|
||||
foreach_nlattr_nested(
|
||||
mod, payload, len, &handle_nl80211_station_info, &ctx);
|
||||
|
||||
if (ctx.update_bar)
|
||||
mod->bar->refresh(mod->bar);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_DBG("%s: unrecognized nl80211 attribute: "
|
||||
"type=%hu, nested=%d, len=%zu", m->iface, type, nested, len);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -826,12 +985,19 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len)
|
|||
struct private *m = mod->private;
|
||||
|
||||
for (; NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) {
|
||||
if (hdr->nlmsg_seq == m->nl80211.seq_nr) {
|
||||
if (hdr->nlmsg_seq == m->nl80211.get_interface_seq_nr) {
|
||||
/* Current request is now considered complete */
|
||||
m->nl80211.seq_nr = 0;
|
||||
m->nl80211.get_interface_seq_nr = 0;
|
||||
}
|
||||
|
||||
if (hdr->nlmsg_type == GENL_ID_CTRL) {
|
||||
if (hdr->nlmsg_type == NLMSG_DONE) {
|
||||
if (hdr->nlmsg_seq == m->nl80211.get_station_seq_nr) {
|
||||
/* Current request is now considered complete */
|
||||
m->nl80211.get_station_seq_nr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
else if (hdr->nlmsg_type == GENL_ID_CTRL) {
|
||||
const struct genlmsghdr *genl = NLMSG_DATA(hdr);
|
||||
const size_t msg_size = NLMSG_PAYLOAD(hdr, 0);
|
||||
foreach_nlattr(mod, genl, msg_size, &handle_genl_ctrl);
|
||||
|
@ -847,6 +1013,8 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len)
|
|||
LOG_DBG("%s: got interface information", m->iface);
|
||||
foreach_nlattr(
|
||||
mod, genl, msg_size, &handle_nl80211_new_interface);
|
||||
|
||||
LOG_INFO("%s: SSID: %s", m->iface, m->ssid);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -864,13 +1032,14 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len)
|
|||
if (nl80211_is_for_us(mod, genl, msg_size)) {
|
||||
LOG_DBG("%s: connected, requesting interface information",
|
||||
m->iface);
|
||||
send_nl80211_get_interface_request(m);
|
||||
send_nl80211_get_interface(m);
|
||||
}
|
||||
break;
|
||||
|
||||
case NL80211_CMD_DISCONNECT:
|
||||
if (nl80211_is_for_us(mod, genl, msg_size)) {
|
||||
LOG_DBG("%s: disconnected, resetting SSID etc", m->iface);
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
free(m->ssid);
|
||||
m->ssid = NULL;
|
||||
|
@ -878,6 +1047,18 @@ parse_genl_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len)
|
|||
}
|
||||
break;
|
||||
|
||||
case NL80211_CMD_NEW_STATION:
|
||||
if (nl80211_is_for_us(mod, genl, msg_size)) {
|
||||
LOG_DBG("%s: got station information", m->iface);
|
||||
foreach_nlattr(mod, genl, msg_size, &handle_nl80211_new_station);
|
||||
}
|
||||
|
||||
LOG_INFO("%s: signal: %d dBm, RX=%u Mbit/s, TX=%u Mbit/s",
|
||||
m->iface, m->signal_strength_dbm,
|
||||
m->rx_bitrate / 1000 / 1000,
|
||||
m->tx_bitrate / 1000 / 1000);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_DBG("unrecognized nl80211 command: %hhu", genl->cmd);
|
||||
break;
|
||||
|
|
Loading…
Add table
Reference in a new issue