Merge branch 'network-wifi'

This commit is contained in:
Daniel Eklöf 2021-09-01 19:11:37 +02:00
commit 0da24198b3
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
4 changed files with 775 additions and 51 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 doesnt 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,
};