mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2025-02-18 08:50:54 +01:00
core: Track wiphy devices and its network interfaces
This commit is contained in:
parent
d04ecde554
commit
32c415e4e8
264
src/wiphy.c
264
src/wiphy.c
@ -25,6 +25,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <linux/if.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
#include <ell/ell.h>
|
#include <ell/ell.h>
|
||||||
|
|
||||||
#include "linux/nl80211.h"
|
#include "linux/nl80211.h"
|
||||||
@ -33,6 +36,21 @@
|
|||||||
static struct l_genl *genl = NULL;
|
static struct l_genl *genl = NULL;
|
||||||
static struct l_genl_family *nl80211 = NULL;
|
static struct l_genl_family *nl80211 = NULL;
|
||||||
|
|
||||||
|
struct netdev {
|
||||||
|
uint32_t index;
|
||||||
|
char name[IFNAMSIZ];
|
||||||
|
uint32_t type;
|
||||||
|
uint8_t addr[ETH_ALEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wiphy {
|
||||||
|
uint32_t id;
|
||||||
|
char name[20];
|
||||||
|
struct l_queue *netdev_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct l_queue *wiphy_list = NULL;
|
||||||
|
|
||||||
static void do_debug(const char *str, void *user_data)
|
static void do_debug(const char *str, void *user_data)
|
||||||
{
|
{
|
||||||
const char *prefix = user_data;
|
const char *prefix = user_data;
|
||||||
@ -40,20 +58,253 @@ static void do_debug(const char *str, void *user_data)
|
|||||||
l_info("%s%s", prefix, str);
|
l_info("%s%s", prefix, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void netdev_free(void *data)
|
||||||
|
{
|
||||||
|
struct netdev *netdev = data;
|
||||||
|
|
||||||
|
l_debug("Freeing interface %s", netdev->name);
|
||||||
|
|
||||||
|
l_free(netdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool netdev_match(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct netdev *netdev = a;
|
||||||
|
uint32_t index = L_PTR_TO_UINT(b);
|
||||||
|
|
||||||
|
return (netdev->index == index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wiphy_free(void *data)
|
||||||
|
{
|
||||||
|
struct wiphy *wiphy = data;
|
||||||
|
|
||||||
|
l_debug("Freeing wiphy %s", wiphy->name);
|
||||||
|
|
||||||
|
l_queue_destroy(wiphy->netdev_list, netdev_free);
|
||||||
|
|
||||||
|
l_free(wiphy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool wiphy_match(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct wiphy *wiphy = a;
|
||||||
|
uint32_t id = L_PTR_TO_UINT(b);
|
||||||
|
|
||||||
|
return (wiphy->id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wiphy_interface_dump_callback(struct l_genl_msg *msg,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct wiphy *wiphy = user_data;
|
||||||
|
struct netdev *netdev;
|
||||||
|
struct l_genl_attr attr;
|
||||||
|
uint16_t type, len;
|
||||||
|
const void *data;
|
||||||
|
char ifname[IFNAMSIZ];
|
||||||
|
uint8_t ifaddr[ETH_ALEN];
|
||||||
|
uint32_t ifindex, iftype;
|
||||||
|
|
||||||
|
if (!l_genl_attr_init(&attr, msg))
|
||||||
|
return;
|
||||||
|
|
||||||
|
memset(ifname, 0, sizeof(ifname));
|
||||||
|
memset(ifaddr, 0, sizeof(ifaddr));
|
||||||
|
iftype = NL80211_IFTYPE_UNSPECIFIED;
|
||||||
|
ifindex = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The interface index and interface name attributes are normally
|
||||||
|
* listed before the wiphy attribute. This handling assumes that
|
||||||
|
* all attributes are included in the same message.
|
||||||
|
*
|
||||||
|
* If any required attribute is missing, the whole message will
|
||||||
|
* be ignored.
|
||||||
|
*/
|
||||||
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
||||||
|
switch (type) {
|
||||||
|
case NL80211_ATTR_IFINDEX:
|
||||||
|
if (len != sizeof(uint32_t)) {
|
||||||
|
l_warn("Invalid interface index attribute");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifindex = *((uint32_t *) data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NL80211_ATTR_IFNAME:
|
||||||
|
if (len > sizeof(ifname)) {
|
||||||
|
l_warn("Invalid interface name attribute");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ifname, data, len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NL80211_ATTR_WIPHY:
|
||||||
|
if (len != sizeof(uint32_t)) {
|
||||||
|
l_warn("Invalid wiphy attribute");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wiphy->id != *((uint32_t *) data)) {
|
||||||
|
l_warn("Mismatching wiphy attribute");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NL80211_ATTR_IFTYPE:
|
||||||
|
if (len != sizeof(uint32_t)) {
|
||||||
|
l_warn("Invalid interface type attribute");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iftype = *((uint32_t *) data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NL80211_ATTR_MAC:
|
||||||
|
if (len != sizeof(ifaddr)) {
|
||||||
|
l_warn("Invalid interface address attribute");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ifaddr, data, len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ifindex) {
|
||||||
|
l_warn("Missing interface index attribute");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
netdev = l_queue_find(wiphy->netdev_list, netdev_match,
|
||||||
|
L_UINT_TO_PTR(ifindex));
|
||||||
|
if (!netdev) {
|
||||||
|
netdev = l_new(struct netdev, 1);
|
||||||
|
l_queue_push_tail(wiphy->netdev_list, netdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(netdev->name, ifname, sizeof(netdev->name));
|
||||||
|
memcpy(netdev->addr, ifaddr, sizeof(netdev->addr));
|
||||||
|
netdev->index = ifindex;
|
||||||
|
netdev->type = iftype;
|
||||||
|
|
||||||
|
l_debug("Found interface %s", netdev->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wiphy_get_interfaces(struct wiphy *wiphy)
|
||||||
|
{
|
||||||
|
struct l_genl_msg *msg;
|
||||||
|
|
||||||
|
msg = l_genl_msg_new_sized(NL80211_CMD_GET_INTERFACE, 8);
|
||||||
|
|
||||||
|
if (!l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY,
|
||||||
|
sizeof(uint32_t), &wiphy->id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
l_genl_family_dump(nl80211, msg, wiphy_interface_dump_callback,
|
||||||
|
wiphy, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wiphy_dump_callback(struct l_genl_msg *msg, void *user_data)
|
||||||
|
{
|
||||||
|
struct wiphy *wiphy = NULL;
|
||||||
|
struct l_genl_attr attr;
|
||||||
|
uint16_t type, len;
|
||||||
|
const void *data;
|
||||||
|
uint32_t id;
|
||||||
|
bool created = false;
|
||||||
|
|
||||||
|
if (!l_genl_attr_init(&attr, msg))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The wiphy attribute is always the first attribute in the
|
||||||
|
* list. If not then error out with a warning and ignore the
|
||||||
|
* whole message.
|
||||||
|
*
|
||||||
|
* In most cases multiple of these message will be send
|
||||||
|
* since the information included can not fit into a single
|
||||||
|
* message.
|
||||||
|
*/
|
||||||
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
||||||
|
switch (type) {
|
||||||
|
case NL80211_ATTR_WIPHY:
|
||||||
|
if (wiphy) {
|
||||||
|
l_warn("Duplicate wiphy attribute");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len != sizeof(uint32_t)) {
|
||||||
|
l_warn("Invalid wiphy attribute");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = *((uint32_t *) data);
|
||||||
|
|
||||||
|
wiphy = l_queue_find(wiphy_list, wiphy_match,
|
||||||
|
L_UINT_TO_PTR(id));
|
||||||
|
if (!wiphy) {
|
||||||
|
wiphy = l_new(struct wiphy, 1);
|
||||||
|
wiphy->id = id;
|
||||||
|
wiphy->netdev_list = l_queue_new();
|
||||||
|
l_queue_push_tail(wiphy_list, wiphy);
|
||||||
|
created = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NL80211_ATTR_WIPHY_NAME:
|
||||||
|
if (!wiphy) {
|
||||||
|
l_warn("No wiphy structure found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > sizeof(wiphy->name)) {
|
||||||
|
l_warn("Invalid wiphy name attribute");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(wiphy->name, data, len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (created) {
|
||||||
|
l_debug("Found wiphy %s", wiphy->name);
|
||||||
|
wiphy_get_interfaces(wiphy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void nl80211_appeared(void *user_data)
|
static void nl80211_appeared(void *user_data)
|
||||||
{
|
{
|
||||||
struct l_genl_msg *msg;
|
struct l_genl_msg *msg;
|
||||||
|
|
||||||
l_debug("Found nl80211 interface");
|
l_debug("Found nl80211 interface");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an extra sanity check so that no memory is leaked
|
||||||
|
* in case the generic netlink handling gets confused.
|
||||||
|
*/
|
||||||
|
if (wiphy_list) {
|
||||||
|
l_warn("Destroying existing list of wiphy devices");
|
||||||
|
l_queue_destroy(wiphy_list, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
wiphy_list = l_queue_new();
|
||||||
|
|
||||||
msg = l_genl_msg_new(NL80211_CMD_GET_WIPHY);
|
msg = l_genl_msg_new(NL80211_CMD_GET_WIPHY);
|
||||||
|
|
||||||
l_genl_family_dump(nl80211, msg, NULL, NULL, NULL);
|
l_genl_family_dump(nl80211, msg, wiphy_dump_callback, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nl80211_vanished(void *user_data)
|
static void nl80211_vanished(void *user_data)
|
||||||
{
|
{
|
||||||
l_debug("Lost nl80211 interface");
|
l_debug("Lost nl80211 interface");
|
||||||
|
|
||||||
|
l_queue_destroy(wiphy_list, wiphy_free);
|
||||||
|
wiphy_list = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wiphy_init(void)
|
bool wiphy_init(void)
|
||||||
@ -104,5 +355,16 @@ bool wiphy_exit(void)
|
|||||||
l_genl_unref(genl);
|
l_genl_unref(genl);
|
||||||
genl = NULL;
|
genl = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an extra sanity check so that no memory is leaked
|
||||||
|
* in case the generic netlink handling forgets to call the
|
||||||
|
* vanished callback.
|
||||||
|
*/
|
||||||
|
if (wiphy_list) {
|
||||||
|
l_warn("Found leftover list of wiphy devices");
|
||||||
|
l_queue_destroy(wiphy_list, wiphy_free);
|
||||||
|
wiphy_list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user