mirror of
https://codeberg.org/dnkl/yambar.git
synced 2025-04-19 19:25:41 +02:00
Merge branch 'network-wifi'
This commit is contained in:
commit
0da24198b3
4 changed files with 775 additions and 51 deletions
|
@ -30,6 +30,9 @@
|
|||
* foreign-toplevel: Wayland module that provides information about
|
||||
currently opened windows.
|
||||
* alsa: support for capture devices.
|
||||
* network: `ssid`, `signal`, `rx-bitrate` and `rx-bitrate` tags.
|
||||
* network: `poll-interval` option (for the new `signal` and
|
||||
`*-bitrate` tags).
|
||||
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -39,6 +39,19 @@ address.
|
|||
| ipv6
|
||||
: string
|
||||
: IPv6 address assigned to the interface, or *""* if none
|
||||
| ssid
|
||||
: string
|
||||
: SSID the adapter is connected to (Wi-Fi only)
|
||||
| signal
|
||||
: int
|
||||
: Signal strength, in dBm (Wi-Fi only)
|
||||
| rx-bitrate
|
||||
: int
|
||||
: RX bitrate, in Mbit/s
|
||||
| tx-bitrate
|
||||
: int
|
||||
: TX bitrate in Mbit/s
|
||||
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
|
@ -50,6 +63,11 @@ address.
|
|||
: string
|
||||
: yes
|
||||
: Name of network interface to monitor
|
||||
| poll-interval
|
||||
: int
|
||||
: no
|
||||
: Periodically (in seconds) update the signal and rx+tx bitrate tags.
|
||||
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
|
|
|
@ -219,9 +219,13 @@ bar:
|
|||
up:
|
||||
map:
|
||||
tag: ipv4
|
||||
default: {string: {text: , font: *awesome}}
|
||||
values:
|
||||
"": {string: {text: , font: *awesome, foreground: ffffff66}}
|
||||
default:
|
||||
- string: {text: , font: *awesome}
|
||||
- string: {text: "{ssid}"}
|
||||
values:
|
||||
"":
|
||||
- string: {text: , font: *awesome, foreground: ffffff66}
|
||||
- string: {text: "{ssid}", foreground: ffffff66}
|
||||
- alsa:
|
||||
card: hw:PCH
|
||||
mixer: Master
|
||||
|
|
|
@ -2,16 +2,21 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <threads.h>
|
||||
#include <poll.h>
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/genetlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/nl80211.h>
|
||||
|
||||
#include <tllist.h>
|
||||
|
||||
|
@ -24,6 +29,8 @@
|
|||
#include "../module.h"
|
||||
#include "../plugin.h"
|
||||
|
||||
#define UNUSED __attribute__((unused))
|
||||
|
||||
struct af_addr {
|
||||
int family;
|
||||
union {
|
||||
|
@ -35,8 +42,16 @@ struct af_addr {
|
|||
struct private {
|
||||
char *iface;
|
||||
struct particle *label;
|
||||
int poll_interval;
|
||||
|
||||
int nl_sock;
|
||||
int genl_sock;
|
||||
int rt_sock;
|
||||
|
||||
struct {
|
||||
uint16_t family_id;
|
||||
uint32_t get_interface_seq_nr;
|
||||
uint32_t get_station_seq_nr;
|
||||
} nl80211;
|
||||
|
||||
bool get_addresses;
|
||||
|
||||
|
@ -47,6 +62,12 @@ struct private {
|
|||
|
||||
/* IPv4 and IPv6 addresses */
|
||||
tll(struct af_addr) addrs;
|
||||
|
||||
/* WiFi extensions */
|
||||
char *ssid;
|
||||
int signal_strength_dbm;
|
||||
uint32_t rx_bitrate;
|
||||
uint32_t tx_bitrate;
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -54,12 +75,12 @@ destroy(struct module *mod)
|
|||
{
|
||||
struct private *m = mod->private;
|
||||
|
||||
assert(m->nl_sock == -1);
|
||||
assert(m->rt_sock == -1);
|
||||
|
||||
m->label->destroy(m->label);
|
||||
|
||||
tll_free(m->addrs);
|
||||
|
||||
free(m->ssid);
|
||||
free(m->iface);
|
||||
free(m);
|
||||
|
||||
|
@ -120,8 +141,12 @@ content(struct module *mod)
|
|||
tag_new_string(mod, "mac", mac_str),
|
||||
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 = 7,
|
||||
.count = 11,
|
||||
};
|
||||
|
||||
mtx_unlock(&mod->lock);
|
||||
|
@ -140,7 +165,7 @@ nl_pid_value(void)
|
|||
|
||||
/* Connect and bind to netlink socket. Returns socket fd, or -1 on error */
|
||||
static int
|
||||
netlink_connect(void)
|
||||
netlink_connect_rt(void)
|
||||
{
|
||||
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (sock == -1) {
|
||||
|
@ -154,7 +179,31 @@ netlink_connect(void)
|
|||
.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR,
|
||||
};
|
||||
|
||||
if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
LOG_ERRNO("failed to bind netlink RT socket");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
static int
|
||||
netlink_connect_genl(void)
|
||||
{
|
||||
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
|
||||
if (sock == -1) {
|
||||
LOG_ERRNO("failed to create netlink socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct sockaddr_nl addr = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_pid = nl_pid_value(),
|
||||
/* no multicast notifications by default, will be added later */
|
||||
};
|
||||
|
||||
if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
LOG_ERRNO("failed to bind netlink socket");
|
||||
close(sock);
|
||||
return -1;
|
||||
|
@ -164,11 +213,22 @@ netlink_connect(void)
|
|||
}
|
||||
|
||||
static bool
|
||||
send_rt_request(int nl_sock, int request)
|
||||
send_nlmsg(int sock, const void *nlmsg, size_t len)
|
||||
{
|
||||
int r = sendto(
|
||||
sock, nlmsg, len, 0,
|
||||
(struct sockaddr *)&(struct sockaddr_nl){.nl_family = AF_NETLINK},
|
||||
sizeof(struct sockaddr_nl));
|
||||
|
||||
return r == len;
|
||||
}
|
||||
|
||||
static bool
|
||||
send_rt_request(struct private *m, int request)
|
||||
{
|
||||
struct {
|
||||
struct nlmsghdr hdr;
|
||||
struct rtgenmsg rt;
|
||||
struct rtgenmsg rt __attribute__((aligned(NLMSG_ALIGNTO)));
|
||||
} req = {
|
||||
.hdr = {
|
||||
.nlmsg_len = NLMSG_LENGTH(sizeof(req.rt)),
|
||||
|
@ -183,18 +243,157 @@ send_rt_request(int nl_sock, int request)
|
|||
},
|
||||
};
|
||||
|
||||
int r = sendto(
|
||||
nl_sock, &req, req.hdr.nlmsg_len, 0,
|
||||
(struct sockaddr *)&(struct sockaddr_nl){.nl_family = AF_NETLINK},
|
||||
sizeof(struct sockaddr_nl));
|
||||
|
||||
if (r == -1) {
|
||||
LOG_ERRNO("failed to send netlink request");
|
||||
if (!send_nlmsg(m->rt_sock, &req, req.hdr.nlmsg_len)) {
|
||||
LOG_ERRNO("%s: failed to send netlink RT request (%d)",
|
||||
m->iface, request);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
send_ctrl_get_family_request(struct private *m)
|
||||
{
|
||||
const struct {
|
||||
struct nlmsghdr hdr;
|
||||
struct {
|
||||
struct genlmsghdr genl;
|
||||
struct {
|
||||
struct nlattr hdr;
|
||||
char data[8] __attribute__((aligned(NLA_ALIGNTO)));
|
||||
} family_name_attr __attribute__((aligned(NLA_ALIGNTO)));
|
||||
} msg __attribute__((aligned(NLMSG_ALIGNTO)));
|
||||
} req = {
|
||||
.hdr = {
|
||||
.nlmsg_len = NLMSG_LENGTH(sizeof(req.msg)),
|
||||
.nlmsg_type = GENL_ID_CTRL,
|
||||
.nlmsg_flags = NLM_F_REQUEST,
|
||||
.nlmsg_seq = 1,
|
||||
.nlmsg_pid = nl_pid_value(),
|
||||
},
|
||||
|
||||
.msg = {
|
||||
.genl = {
|
||||
.cmd = CTRL_CMD_GETFAMILY,
|
||||
.version = 1,
|
||||
},
|
||||
|
||||
.family_name_attr = {
|
||||
.hdr = {
|
||||
.nla_type = CTRL_ATTR_FAMILY_NAME,
|
||||
.nla_len = sizeof(req.msg.family_name_attr),
|
||||
},
|
||||
|
||||
.data = NL80211_GENL_NAME,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
_Static_assert(
|
||||
sizeof(req.msg.family_name_attr) ==
|
||||
NLA_HDRLEN + NLA_ALIGN(sizeof(req.msg.family_name_attr.data)),
|
||||
"");
|
||||
|
||||
if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) {
|
||||
LOG_ERRNO("%s: failed to send netlink ctrl-get-family request",
|
||||
m->iface);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
send_nl80211_request(struct private *m, uint8_t cmd, uint16_t flags, uint32_t seq)
|
||||
{
|
||||
if (m->ifindex < 0)
|
||||
return false;
|
||||
|
||||
const struct {
|
||||
struct nlmsghdr hdr;
|
||||
struct {
|
||||
struct genlmsghdr genl;
|
||||
struct {
|
||||
struct nlattr attr;
|
||||
int index __attribute__((aligned(NLA_ALIGNTO)));
|
||||
} ifindex __attribute__((aligned(NLA_ALIGNTO)));
|
||||
} msg __attribute__((aligned(NLMSG_ALIGNTO)));
|
||||
} req = {
|
||||
.hdr = {
|
||||
.nlmsg_len = NLMSG_LENGTH(sizeof(req.msg)),
|
||||
.nlmsg_type = m->nl80211.family_id,
|
||||
.nlmsg_flags = flags,
|
||||
.nlmsg_seq = seq,
|
||||
.nlmsg_pid = nl_pid_value(),
|
||||
},
|
||||
|
||||
.msg = {
|
||||
.genl = {
|
||||
.cmd = cmd,
|
||||
.version = 1,
|
||||
},
|
||||
|
||||
.ifindex = {
|
||||
.attr = {
|
||||
.nla_type = NL80211_ATTR_IFINDEX,
|
||||
.nla_len = sizeof(req.msg.ifindex),
|
||||
},
|
||||
|
||||
.index = m->ifindex,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (!send_nlmsg(m->genl_sock, &req, req.hdr.nlmsg_len)) {
|
||||
LOG_ERRNO("%s: failed to send netlink nl80211 get-inteface request",
|
||||
m->iface);
|
||||
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)
|
||||
{
|
||||
|
@ -212,6 +411,9 @@ find_my_ifindex(struct module *mod, const struct ifinfomsg *msg, size_t len)
|
|||
mtx_lock(&mod->lock);
|
||||
m->ifindex = msg->ifi_index;
|
||||
mtx_unlock(&mod->lock);
|
||||
|
||||
send_nl80211_get_interface(m);
|
||||
send_nl80211_get_station(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -372,6 +574,319 @@ handle_address(struct module *mod, uint16_t type,
|
|||
mod->bar->refresh(mod->bar);
|
||||
}
|
||||
|
||||
static bool
|
||||
foreach_nlattr(struct module *mod, const struct genlmsghdr *genl, size_t len,
|
||||
bool (*cb)(struct module *mod, uint16_t type, bool nested,
|
||||
const void *payload, size_t len))
|
||||
{
|
||||
const uint8_t *raw = (const uint8_t *)genl + GENL_HDRLEN;
|
||||
const uint8_t *end = (const uint8_t *)genl + len;
|
||||
|
||||
for (const struct nlattr *attr = (const struct nlattr *)raw;
|
||||
raw < end;
|
||||
raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw)
|
||||
{
|
||||
uint16_t type = attr->nla_type & NLA_TYPE_MASK;
|
||||
bool nested = (attr->nla_type & NLA_F_NESTED) != 0;;
|
||||
const void *payload = raw + NLA_HDRLEN;
|
||||
|
||||
if (!cb(mod, type, nested, payload, attr->nla_len - NLA_HDRLEN))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
foreach_nlattr_nested(struct module *mod, const void *parent_payload, size_t len,
|
||||
bool (*cb)(struct module *mod, uint16_t type,
|
||||
bool nested, const void *payload, size_t len,
|
||||
void *ctx),
|
||||
void *ctx)
|
||||
{
|
||||
const uint8_t *raw = parent_payload;
|
||||
const uint8_t *end = parent_payload + len;
|
||||
|
||||
for (const struct nlattr *attr = (const struct nlattr *)raw;
|
||||
raw < end;
|
||||
raw += NLA_ALIGN(attr->nla_len), attr = (const struct nlattr *)raw)
|
||||
{
|
||||
uint16_t type = attr->nla_type & NLA_TYPE_MASK;
|
||||
bool nested = (attr->nla_type & NLA_F_NESTED) != 0;
|
||||
const void *payload = raw + NLA_HDRLEN;
|
||||
|
||||
if (!cb(mod, type, nested, payload, attr->nla_len - NLA_HDRLEN, ctx))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct mcast_group {
|
||||
uint32_t id;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static bool
|
||||
parse_mcast_group(struct module *mod, uint16_t type, bool nested,
|
||||
const void *payload, size_t len, void *_ctx)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
struct mcast_group *ctx = _ctx;
|
||||
|
||||
switch (type) {
|
||||
case CTRL_ATTR_MCAST_GRP_ID: {
|
||||
ctx->id = *(uint32_t *)payload;
|
||||
break;
|
||||
}
|
||||
|
||||
case CTRL_ATTR_MCAST_GRP_NAME: {
|
||||
ctx->name = (const char *)payload;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_WARN("%s: unrecognized GENL MCAST GRP attribute: "
|
||||
"type=%hu, nested=%d, len=%zu", m->iface, type, nested, len);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_mcast_groups(struct module *mod, uint16_t type, bool nested,
|
||||
const void *payload, size_t len, void *_ctx)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
|
||||
struct mcast_group group = {0};
|
||||
foreach_nlattr_nested(mod, payload, len, &parse_mcast_group, &group);
|
||||
|
||||
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));
|
||||
|
||||
if (r < 0)
|
||||
LOG_ERRNO("failed to joint the nl80211 MLME mcast group");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_genl_ctrl(struct module *mod, uint16_t type, bool nested,
|
||||
const void *payload, size_t len)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
|
||||
switch (type) {
|
||||
case CTRL_ATTR_FAMILY_ID: {
|
||||
m->nl80211.family_id = *(const uint16_t *)payload;
|
||||
send_nl80211_get_interface(m);
|
||||
break;
|
||||
}
|
||||
|
||||
case CTRL_ATTR_FAMILY_NAME:
|
||||
//LOG_INFO("NAME: %.*s (%zu bytes)", (int)len, (const char *)payload, len);
|
||||
break;
|
||||
|
||||
case CTRL_ATTR_MCAST_GROUPS:
|
||||
foreach_nlattr_nested(mod, payload, len, &parse_mcast_groups, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_DBG("%s: unrecognized GENL CTRL attribute: "
|
||||
"type=%hu, nested=%d, len=%zu", m->iface, type, nested, len);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
check_for_nl80211_ifindex(struct module *mod, uint16_t type, bool nested,
|
||||
const void *payload, size_t len)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
|
||||
switch (type) {
|
||||
case NL80211_ATTR_IFINDEX:
|
||||
return *(uint32_t *)payload == m->ifindex;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
nl80211_is_for_us(struct module *mod, const struct genlmsghdr *genl,
|
||||
size_t msg_size)
|
||||
{
|
||||
return foreach_nlattr(mod, genl, msg_size, &check_for_nl80211_ifindex);
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_nl80211_new_interface(struct module *mod, uint16_t type, bool nested,
|
||||
const void *payload, size_t len)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
|
||||
switch (type) {
|
||||
case NL80211_ATTR_IFINDEX:
|
||||
assert(*(uint32_t *)payload == m->ifindex);
|
||||
break;
|
||||
|
||||
case NL80211_ATTR_SSID: {
|
||||
const char *ssid = payload;
|
||||
|
||||
mtx_lock(&mod->lock);
|
||||
free(m->ssid);
|
||||
m->ssid = strndup(ssid, len);
|
||||
mtx_unlock(&mod->lock);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads at least one (possibly more) message.
|
||||
*
|
||||
|
@ -411,7 +926,7 @@ netlink_receive_messages(int sock, void **reply, size_t *len)
|
|||
}
|
||||
|
||||
static bool
|
||||
parse_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len)
|
||||
parse_rt_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len)
|
||||
{
|
||||
struct private *m = mod->private;
|
||||
|
||||
|
@ -427,7 +942,7 @@ parse_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len)
|
|||
/* Request initial list of IPv4/6 addresses */
|
||||
if (m->get_addresses && m->ifindex != -1) {
|
||||
m->get_addresses = false;
|
||||
send_rt_request(m->nl_sock, RTM_GETADDR);
|
||||
send_rt_request(m, RTM_GETADDR);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -451,14 +966,127 @@ parse_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len)
|
|||
|
||||
case NLMSG_ERROR:{
|
||||
const struct nlmsgerr *err = NLMSG_DATA(hdr);
|
||||
LOG_ERRNO_P(err->error, "netlink");
|
||||
LOG_ERRNO_P(-err->error, "%s: netlink RT reply", m->iface);
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_WARN(
|
||||
"unrecognized netlink message type: 0x%x", hdr->nlmsg_type);
|
||||
break;
|
||||
"%s: unrecognized netlink message type: 0x%x",
|
||||
m->iface, hdr->nlmsg_type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
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.get_interface_seq_nr) {
|
||||
/* Current request is now considered complete */
|
||||
m->nl80211.get_interface_seq_nr = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
else if (hdr->nlmsg_type == m->nl80211.family_id) {
|
||||
const struct genlmsghdr *genl = NLMSG_DATA(hdr);
|
||||
const size_t msg_size = NLMSG_PAYLOAD(hdr, 0);
|
||||
|
||||
switch (genl->cmd) {
|
||||
case NL80211_CMD_NEW_INTERFACE:
|
||||
if (nl80211_is_for_us(mod, genl, msg_size)) {
|
||||
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;
|
||||
|
||||
case NL80211_CMD_CONNECT:
|
||||
/*
|
||||
* Update SSID
|
||||
*
|
||||
* Unfortunately, the SSID doesn’t appear to be
|
||||
* included in *any* of the notifications sent when
|
||||
* associating, authenticating and connecting to a
|
||||
* station.
|
||||
*
|
||||
* Thus, we need to explicitly request an update.
|
||||
*/
|
||||
if (nl80211_is_for_us(mod, genl, msg_size)) {
|
||||
LOG_DBG("%s: connected, requesting interface information",
|
||||
m->iface);
|
||||
send_nl80211_get_interface(m);
|
||||
send_nl80211_get_station(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;
|
||||
m->signal_strength_dbm = 0;
|
||||
m->rx_bitrate = m->tx_bitrate = 0;
|
||||
mtx_unlock(&mod->lock);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
else if (hdr->nlmsg_type == NLMSG_ERROR) {
|
||||
const struct nlmsgerr *err = NLMSG_DATA(hdr);
|
||||
int nl_errno = -err->error;
|
||||
|
||||
if (nl_errno == ENODEV)
|
||||
; /* iface is not an nl80211 device */
|
||||
else if (nl_errno == ENOENT)
|
||||
; /* iface down? */
|
||||
else
|
||||
LOG_ERRNO_P(nl_errno, "%s: nl80211 reply", m->iface);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_WARN(
|
||||
"%s: unrecognized netlink message type: 0x%x",
|
||||
m->iface, hdr->nlmsg_type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -468,65 +1096,133 @@ parse_reply(struct module *mod, const struct nlmsghdr *hdr, size_t len)
|
|||
static int
|
||||
run(struct module *mod)
|
||||
{
|
||||
int ret = 1;
|
||||
struct private *m = mod->private;
|
||||
|
||||
m->nl_sock = netlink_connect();
|
||||
if (m->nl_sock == -1)
|
||||
return 1;
|
||||
int timer_fd = -1;
|
||||
if (m->poll_interval > 0) {
|
||||
timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
|
||||
if (timer_fd < 0) {
|
||||
LOG_ERRNO("%s: failed to create poll timer FD", m->iface);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!send_rt_request(m->nl_sock, RTM_GETLINK)) {
|
||||
close(m->nl_sock);
|
||||
m->nl_sock = -1;
|
||||
return 1;
|
||||
struct itimerspec poll_time = {
|
||||
.it_value = {.tv_sec = m->poll_interval},
|
||||
.it_interval = {.tv_sec = m->poll_interval},
|
||||
};
|
||||
|
||||
if (timerfd_settime(timer_fd, 0, &poll_time, NULL) < 0) {
|
||||
LOG_ERRNO("%s: failed to arm poll timer", m->iface);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
m->rt_sock = netlink_connect_rt();
|
||||
m->genl_sock = netlink_connect_genl();
|
||||
|
||||
if (m->rt_sock < 0 || m->genl_sock < 0)
|
||||
goto out;
|
||||
|
||||
if (!send_rt_request(m, RTM_GETLINK) ||
|
||||
!send_ctrl_get_family_request(m))
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Main loop */
|
||||
while (true) {
|
||||
struct pollfd fds[] = {
|
||||
{.fd = mod->abort_fd, .events = POLLIN},
|
||||
{.fd = m->nl_sock, .events = POLLIN}
|
||||
{.fd = m->rt_sock, .events = POLLIN},
|
||||
{.fd = m->genl_sock, .events = POLLIN},
|
||||
{.fd = timer_fd, .events = POLLIN},
|
||||
};
|
||||
|
||||
poll(fds, 2, -1);
|
||||
poll(fds, 3 + (timer_fd >= 0 ? 1 : 0), -1);
|
||||
|
||||
if (fds[0].revents & POLLIN)
|
||||
if (fds[0].revents & (POLLIN | POLLHUP))
|
||||
break;
|
||||
|
||||
if (fds[1].revents & POLLHUP) {
|
||||
LOG_ERR("disconnected from netlink socket");
|
||||
if ((fds[1].revents & POLLHUP) ||
|
||||
(fds[2].revents & POLLHUP))
|
||||
{
|
||||
LOG_ERR("%s: disconnected from netlink socket", m->iface);
|
||||
break;
|
||||
}
|
||||
|
||||
assert(fds[1].revents & POLLIN);
|
||||
|
||||
/* Read one (or more) messages */
|
||||
void *reply;
|
||||
size_t len;
|
||||
if (!netlink_receive_messages(m->nl_sock, &reply, &len))
|
||||
if (fds[3].revents & POLLHUP) {
|
||||
LOG_ERR("%s: disconnected from timer FD", m->iface);
|
||||
break;
|
||||
}
|
||||
|
||||
if (fds[1].revents & POLLIN) {
|
||||
/* Read one (or more) messages */
|
||||
void *reply;
|
||||
size_t len;
|
||||
if (!netlink_receive_messages(m->rt_sock, &reply, &len))
|
||||
break;
|
||||
|
||||
/* Parse (and act upon) the received message(s) */
|
||||
if (!parse_rt_reply(mod, (const struct nlmsghdr *)reply, len)) {
|
||||
free(reply);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Parse (and act upon) the received message(s) */
|
||||
if (!parse_reply(mod, (const struct nlmsghdr *)reply, len)) {
|
||||
free(reply);
|
||||
break;
|
||||
}
|
||||
|
||||
free(reply);
|
||||
if (fds[2].revents & POLLIN) {
|
||||
/* Read one (or more) messages */
|
||||
void *reply;
|
||||
size_t len;
|
||||
if (!netlink_receive_messages(m->genl_sock, &reply, &len))
|
||||
break;
|
||||
|
||||
if (!parse_genl_reply(mod, (const struct nlmsghdr *)reply, len)) {
|
||||
free(reply);
|
||||
break;
|
||||
}
|
||||
|
||||
free(reply);
|
||||
}
|
||||
|
||||
if (fds[3].revents & POLLIN) {
|
||||
uint64_t count;
|
||||
ssize_t amount = read(timer_fd, &count, sizeof(count));
|
||||
if (amount < 0) {
|
||||
LOG_ERRNO("failed to read from timer FD");
|
||||
break;
|
||||
}
|
||||
|
||||
send_nl80211_get_station(m);
|
||||
}
|
||||
}
|
||||
|
||||
close(m->nl_sock);
|
||||
m->nl_sock = -1;
|
||||
return 0;
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (m->rt_sock >= 0)
|
||||
close(m->rt_sock);
|
||||
if (m->genl_sock >= 0)
|
||||
close(m->genl_sock);
|
||||
if (timer_fd >= 0)
|
||||
close(timer_fd);
|
||||
m->rt_sock = m->genl_sock = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct module *
|
||||
network_new(const char *iface, struct particle *label)
|
||||
network_new(const char *iface, struct particle *label, int poll_interval)
|
||||
{
|
||||
struct private *priv = calloc(1, sizeof(*priv));
|
||||
priv->iface = strdup(iface);
|
||||
priv->label = label;
|
||||
priv->poll_interval = poll_interval;
|
||||
|
||||
priv->nl_sock = -1;
|
||||
priv->genl_sock = -1;
|
||||
priv->rt_sock = -1;
|
||||
priv->nl80211.family_id = -1;
|
||||
priv->get_addresses = true;
|
||||
priv->ifindex = -1;
|
||||
priv->state = IF_OPER_DOWN;
|
||||
|
@ -545,9 +1241,11 @@ from_conf(const struct yml_node *node, struct conf_inherit inherited)
|
|||
{
|
||||
const struct yml_node *name = yml_get_value(node, "name");
|
||||
const struct yml_node *content = yml_get_value(node, "content");
|
||||
const struct yml_node *poll = yml_get_value(node, "poll-interval");
|
||||
|
||||
return network_new(
|
||||
yml_value_as_string(name), conf_to_particle(content, inherited));
|
||||
yml_value_as_string(name), conf_to_particle(content, inherited),
|
||||
poll != NULL ? yml_value_as_int(poll) : 0);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -555,6 +1253,7 @@ verify_conf(keychain_t *chain, const struct yml_node *node)
|
|||
{
|
||||
static const struct attr_info attrs[] = {
|
||||
{"name", true, &conf_verify_string},
|
||||
{"poll-interval", false, &conf_verify_int},
|
||||
MODULE_COMMON_ATTRS,
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue