hwsim: Refactor radio, wiphy and interface tracking

Read wiphy addresses from sysfs and perform the wiphy name to wiphy idx
mapping using sysfs.  Do this directly on a new radio notification and
stop using new wiphy notifications except for updating the radio names.
Having the wiphy index available synchronously when parsing a new radio
event we store the wiphy index in the radio_info_rec struct directly and
drop struct wiphy_info_rec as there was a 1:1 mapping.  With this, and
knowing that all radio_info data is available when new interface
notifications are received, the tracking is simplified because dbus
objects can be created and destroyed within the notification handlers.
We also now store both the wiphy hardware address data and separately
the interface MAC addresses and can use them more appropriately in the
medium implementation.
This commit is contained in:
Andrew Zaborowski 2017-03-03 12:42:25 +01:00 committed by Denis Kenzior
parent 21a9b064d3
commit d03eeb398c
2 changed files with 205 additions and 174 deletions

View File

@ -109,7 +109,10 @@ monitor_iwmon_LDADD = ell/libell-internal.la
noinst_PROGRAMS = tools/hwsim noinst_PROGRAMS = tools/hwsim
noinst_PROGRAMS += tools/test-runner noinst_PROGRAMS += tools/test-runner
tools_hwsim_SOURCES = tools/hwsim.c src/util.h src/util.c src/mpdu.h tools_hwsim_SOURCES = tools/hwsim.c src/mpdu.h \
src/util.h src/util.c \
src/storage.h src/storage.c \
src/common.h src/common.c
tools_hwsim_LDADD = ell/libell-internal.la tools_hwsim_LDADD = ell/libell-internal.la
tools_test_runner_LDADD = ell/libell-internal.la tools_test_runner_LDADD = ell/libell-internal.la

View File

@ -28,12 +28,14 @@
#include <stdlib.h> #include <stdlib.h>
#include <getopt.h> #include <getopt.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <ctype.h>
#include <ell/ell.h> #include <ell/ell.h>
#include "linux/nl80211.h" #include "linux/nl80211.h"
#include "src/util.h" #include "src/util.h"
#include "src/storage.h"
enum { enum {
HWSIM_CMD_UNSPEC, HWSIM_CMD_UNSPEC,
@ -241,37 +243,46 @@ static void list_callback(struct l_genl_msg *msg, void *user_data)
struct radio_info_rec { struct radio_info_rec {
uint32_t id; uint32_t id;
uint32_t wiphy_id;
char alpha2[2]; char alpha2[2];
bool p2p; bool p2p;
uint32_t regdom; uint32_t regdom;
int channels; int channels;
bool ready; /* Whether we have radio, wiphy and interface data */ uint8_t addrs[ETH_ALEN * 2];
char wiphy_name[0]; char *name;
};
struct wiphy_info_rec {
uint32_t id;
char name[0];
}; };
struct interface_info_rec { struct interface_info_rec {
uint32_t id; uint32_t id;
uint32_t wiphy_id; struct radio_info_rec *radio_rec;
uint8_t addr[ETH_ALEN]; uint8_t addr[ETH_ALEN];
char name[0]; char *name;
}; };
static struct l_queue *radio_info; static struct l_queue *radio_info;
static struct l_queue *wiphy_info;
static struct l_queue *interface_info; static struct l_queue *interface_info;
static void radio_free(void *user_data)
{
struct radio_info_rec *rec = user_data;
l_free(rec->name);
l_free(rec);
}
static void interface_free(void *user_data)
{
struct interface_info_rec *rec = user_data;
l_free(rec->name);
l_free(rec);
}
static void hwsim_radio_cache_cleanup(void) static void hwsim_radio_cache_cleanup(void)
{ {
l_queue_destroy(radio_info, l_free); l_queue_destroy(radio_info, radio_free);
l_queue_destroy(wiphy_info, l_free); l_queue_destroy(interface_info, interface_free);
l_queue_destroy(interface_info, l_free);
radio_info = NULL; radio_info = NULL;
wiphy_info = NULL;
interface_info = NULL; interface_info = NULL;
} }
@ -283,26 +294,12 @@ static bool radio_info_match_id(const void *a, const void *b)
return rec->id == id; return rec->id == id;
} }
static bool radio_info_match_name(const void *a, const void *b) static bool radio_info_match_wiphy_id(const void *a, const void *b)
{ {
const struct radio_info_rec *rec = a; const struct radio_info_rec *rec = a;
return !strcmp(rec->wiphy_name, b);
}
static bool wiphy_info_match_id(const void *a, const void *b)
{
const struct wiphy_info_rec *rec = a;
uint32_t id = L_PTR_TO_UINT(b); uint32_t id = L_PTR_TO_UINT(b);
return rec->id == id; return rec->wiphy_id == id;
}
static bool wiphy_info_match_name(const void *a, const void *b)
{
const struct wiphy_info_rec *rec = a;
return !strcmp(rec->name, b);
} }
static bool interface_info_match_id(const void *a, const void *b) static bool interface_info_match_id(const void *a, const void *b)
@ -313,58 +310,51 @@ static bool interface_info_match_id(const void *a, const void *b)
return rec->id == id; return rec->id == id;
} }
static bool interface_info_match_wiphy_id(const void *a, const void *b) static bool parse_addresses(const uint8_t *buf, size_t len,
struct radio_info_rec *rec)
{ {
const struct interface_info_rec *rec = a; unsigned int pos = 0, addr_idx = 0;
uint32_t id = L_PTR_TO_UINT(b);
return rec->wiphy_id == id; while (pos < len) {
} int start_pos = pos;
char addr[20];
/* while (pos < len && !isspace(buf[pos]))
* See if we have any radios that should become "ready", i.e. where matching pos++;
* wiphy or interface record was missing and is now available.
*/
static void process_new_radios(void)
{
const struct l_queue_entry *radio_entry;
for (radio_entry = l_queue_get_entries(radio_info); radio_entry; if (pos - start_pos > sizeof(addr) - 1) {
radio_entry = radio_entry->next) { l_error("Can't parse a %s address from sysfs",
struct radio_info_rec *radio = radio_entry->data; rec->name);
const struct wiphy_info_rec *wiphy; return false;
const struct interface_info_rec *interface; }
if (radio->ready) memcpy(addr, buf + start_pos, pos - start_pos);
continue; addr[pos - start_pos] = '\0';
wiphy = l_queue_find(wiphy_info, wiphy_info_match_name, if (addr_idx >= 2) {
radio->wiphy_name); l_error("Hwsim wiphy %s has too many addresses listed "
if (!wiphy) " in sysfs - only 2 supported", rec->name);
continue; return false;
}
interface = l_queue_find(interface_info, if (!util_string_to_address(addr, rec->addrs +
interface_info_match_wiphy_id, (addr_idx++ * ETH_ALEN))) {
L_UINT_TO_PTR(wiphy->id)); l_error("Can't parse hwsim wiphy %s address from sysfs",
if (!interface) rec->name);
continue; return false;
}
radio->ready = true; while (pos < len && isspace(buf[pos]))
pos++;
/* TODO: Create DBus object */
/* TODO: insert into address cache */
} }
}
static void process_del_radio(struct radio_info_rec *radio) if (addr_idx < 2) {
{ l_error("Hwsim wiphy %s has too few addresses listed "
if (!radio->ready) " in sysfs - only 2 supported", rec->name);
return; return false;
}
radio->ready = false; return true;
/* TODO: unregister DBus object */
/* TODO: remove from address cache */
} }
static void get_radio_callback(struct l_genl_msg *msg, void *user_data) static void get_radio_callback(struct l_genl_msg *msg, void *user_data)
@ -376,6 +366,10 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data)
const uint32_t *id = NULL; const uint32_t *id = NULL;
size_t name_len = 0; size_t name_len = 0;
struct radio_info_rec *rec; struct radio_info_rec *rec;
uint8_t file_buffer[128];
int bytes, consumed;
unsigned int uintval;
bool old;
if (!l_genl_attr_init(&attr, msg)) if (!l_genl_attr_init(&attr, msg))
return; return;
@ -399,14 +393,20 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data)
if (!id || !name) if (!id || !name)
return; return;
l_free(l_queue_remove_if(radio_info, radio_info_match_id, rec = l_queue_find(radio_info, radio_info_match_id, L_UINT_TO_PTR(*id));
L_UINT_TO_PTR(*id))); if (rec) {
old = true;
rec = l_malloc(sizeof(struct radio_info_rec) + name_len + 1); l_free(rec->name);
memset(rec, 0, sizeof(struct radio_info_rec) + name_len + 1); } else {
old = false;
rec->id = *id; rec = l_new(struct radio_info_rec, 1);
memcpy(rec->wiphy_name, name, name_len);
rec->id = *id;
}
rec->name = l_strndup(name, name_len);
l_genl_attr_init(&attr, msg); l_genl_attr_init(&attr, msg);
@ -440,12 +440,54 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data)
} }
} }
/*
* Assuming that the radio name is the wiphy name read the wiphy index
* associated with the radio and the wiphy's hardware addresses from
* sysfs. The index could be obtained through NL80211_CMD_GET_WIPHY
* but that is costly and reading the index synchronously simplifies
* the job a lot. We have to resort to sysfs anyway to obtain the
* radio addresses.
*/
bytes = read_file((char *) file_buffer, sizeof(file_buffer) - 1,
"/sys/class/ieee80211/%s/index", rec->name);
if (bytes < 0) {
l_error("Error reading index for %s from sysfs", rec->name);
goto free_radio;
}
file_buffer[bytes] = '\0';
if (sscanf((char *) file_buffer, "%u %n", &uintval, &consumed) != 1 ||
consumed != bytes) {
l_error("Error parsing index for %s from sysfs", rec->name);
goto free_radio;
}
rec->wiphy_id = uintval;
bytes = read_file(file_buffer, sizeof(file_buffer),
"/sys/class/ieee80211/%s/addresses", rec->name);
if (bytes < 0) {
l_error("Error reading addresses for %s from sysfs", rec->name);
goto free_radio;
}
if (!parse_addresses(file_buffer, bytes, rec))
goto free_radio;
if (!radio_info) if (!radio_info)
radio_info = l_queue_new(); radio_info = l_queue_new();
l_queue_push_tail(radio_info, rec); if (!old)
l_queue_push_tail(radio_info, rec);
process_new_radios(); /* TODO: Create DBus object */
return;
free_radio:
if (!old)
radio_free(rec);
} }
static void get_wiphy_callback(struct l_genl_msg *msg, void *user_data) static void get_wiphy_callback(struct l_genl_msg *msg, void *user_data)
@ -456,7 +498,7 @@ static void get_wiphy_callback(struct l_genl_msg *msg, void *user_data)
const char *name = NULL; const char *name = NULL;
uint16_t name_len = 0; uint16_t name_len = 0;
const uint32_t *id = NULL; const uint32_t *id = NULL;
struct wiphy_info_rec *rec; struct radio_info_rec *rec;
if (!l_genl_attr_init(&attr, msg)) if (!l_genl_attr_init(&attr, msg))
return; return;
@ -476,21 +518,17 @@ static void get_wiphy_callback(struct l_genl_msg *msg, void *user_data)
if (!name || !id) if (!name || !id)
return; return;
l_free(l_queue_remove_if(wiphy_info, wiphy_info_match_id, rec = l_queue_find(radio_info, radio_info_match_wiphy_id,
L_UINT_TO_PTR(*id))); L_UINT_TO_PTR(*id));
if (!rec)
return;
rec = l_malloc(sizeof(struct wiphy_info_rec) + name_len + 1); if (strlen(rec->name) == name_len && !memcmp(rec->name, name, name_len))
memset(rec, 0, sizeof(struct wiphy_info_rec) + name_len + 1); return;
memcpy(rec->name, name, name_len); l_free(rec->name);
rec->id = *id;
if (!wiphy_info) rec->name = l_strndup(name, name_len);
wiphy_info = l_queue_new();
l_queue_push_tail(wiphy_info, rec);
process_new_radios();
} }
static void get_interface_callback(struct l_genl_msg *msg, void *user_data) static void get_interface_callback(struct l_genl_msg *msg, void *user_data)
@ -504,6 +542,8 @@ static void get_interface_callback(struct l_genl_msg *msg, void *user_data)
const char *ifname = NULL; const char *ifname = NULL;
size_t ifname_len = 0; size_t ifname_len = 0;
struct interface_info_rec *rec; struct interface_info_rec *rec;
struct radio_info_rec *radio_rec;
bool old;
if (!l_genl_attr_init(&attr, msg)) if (!l_genl_attr_init(&attr, msg))
return; return;
@ -541,24 +581,52 @@ static void get_interface_callback(struct l_genl_msg *msg, void *user_data)
if (!addr || !wiphy_id || !ifindex || !ifname) if (!addr || !wiphy_id || !ifindex || !ifname)
return; return;
l_free(l_queue_remove_if(interface_info, interface_info_match_id, radio_rec = l_queue_find(radio_info, radio_info_match_wiphy_id,
L_UINT_TO_PTR(*ifindex))); L_UINT_TO_PTR(*wiphy_id));
if (!radio_rec)
/* This is not a hwsim interface, don't track it */
return;
rec = l_malloc(sizeof(struct interface_info_rec) + ifname_len + 1); rec = l_queue_find(interface_info, interface_info_match_id,
memset(rec, 0, sizeof(struct interface_info_rec) + ifname_len + 1); L_UINT_TO_PTR(*ifindex));
if (rec) {
old = true;
l_free(rec->name);
} else {
old = false;
rec = l_new(struct interface_info_rec, 1);
rec->id = *ifindex;
rec->radio_rec = radio_rec;
}
rec->id = *ifindex;
rec->wiphy_id = *wiphy_id;
memcpy(rec->addr, addr, ETH_ALEN); memcpy(rec->addr, addr, ETH_ALEN);
memcpy(rec->name, ifname, ifname_len); rec->name = l_strndup(ifname, ifname_len);
rec->name[ifname_len] = '\0';
if (!interface_info) if (!interface_info)
interface_info = l_queue_new(); interface_info = l_queue_new();
l_queue_push_tail(interface_info, rec); if (!old)
l_queue_push_tail(interface_info, rec);
process_new_radios(); /* TODO: Create DBus object */
}
static bool interface_info_destroy_by_radio(void *data, void *user_data)
{
struct interface_info_rec *rec = data;
struct radio_info_rec *radio_rec = user_data;
if (rec->radio_rec != radio_rec)
return false;
/* TODO: Drop DBus object */
interface_free(rec);
return true;
} }
static void del_radio_event(struct l_genl_msg *msg) static void del_radio_event(struct l_genl_msg *msg)
@ -592,51 +660,18 @@ static void del_radio_event(struct l_genl_msg *msg)
if (!radio) if (!radio)
return; return;
process_del_radio(radio); l_queue_foreach_remove(interface_info, interface_info_destroy_by_radio,
radio);
l_free(radio); /* TODO: Drop DBus object */
radio_free(radio);
l_queue_remove(radio_info, radio); l_queue_remove(radio_info, radio);
} }
static void del_wiphy_event(struct l_genl_msg *msg)
{
struct wiphy_info_rec *wiphy;
struct radio_info_rec *radio;
struct l_genl_attr attr;
uint16_t type, len;
const void *data;
uint32_t id;
if (!l_genl_attr_init(&attr, msg))
return;
if (!l_genl_attr_next(&attr, &type, &len, &data))
return;
if (type != NL80211_ATTR_WIPHY || len != 4)
return;
id = *((uint32_t *) data);
wiphy = l_queue_find(wiphy_info, wiphy_info_match_id,
L_UINT_TO_PTR(id));
if (!wiphy)
return;
radio = l_queue_find(radio_info, radio_info_match_name, wiphy->name);
if (radio)
process_del_radio(radio);
l_free(wiphy);
l_queue_remove(wiphy_info, wiphy);
}
static void del_interface_event(struct l_genl_msg *msg) static void del_interface_event(struct l_genl_msg *msg)
{ {
struct interface_info_rec *interface; struct interface_info_rec *interface;
struct wiphy_info_rec *wiphy;
struct radio_info_rec *radio;
struct l_genl_attr attr; struct l_genl_attr attr;
uint16_t type, len; uint16_t type, len;
const void *data; const void *data;
@ -664,18 +699,9 @@ static void del_interface_event(struct l_genl_msg *msg)
if (!interface) if (!interface)
return; return;
wiphy = l_queue_find(wiphy_info, wiphy_info_match_id, /* TODO: Drop DBus object */
L_UINT_TO_PTR(interface->wiphy_id));
if (wiphy)
radio = l_queue_find(radio_info, radio_info_match_name,
wiphy->name);
else
radio = NULL;
if (radio) interface_free(interface);
process_del_radio(radio);
l_free(interface);
l_queue_remove(interface_info, interface); l_queue_remove(interface_info, interface);
} }
@ -718,9 +744,6 @@ static void nl80211_config_notify(struct l_genl_msg *msg, void *user_data)
case NL80211_CMD_NEW_WIPHY: case NL80211_CMD_NEW_WIPHY:
get_wiphy_callback(msg, NULL); get_wiphy_callback(msg, NULL);
break; break;
case NL80211_CMD_DEL_WIPHY:
del_wiphy_event(msg);
break;
case NL80211_CMD_NEW_INTERFACE: case NL80211_CMD_NEW_INTERFACE:
get_interface_callback(msg, NULL); get_interface_callback(msg, NULL);
break; break;
@ -730,17 +753,15 @@ static void nl80211_config_notify(struct l_genl_msg *msg, void *user_data)
} }
} }
static void nl80211_ready(void *user_data) static void get_radio_done_initial(void *user_data)
{ {
struct l_genl_msg *msg; struct l_genl_msg *msg;
msg = l_genl_msg_new(NL80211_CMD_GET_WIPHY); /*
if (!l_genl_family_dump(nl80211, msg, get_wiphy_callback, * Query interfaces now that we know we have all the radio data
NULL, NULL)) { * for radio lookups inside get_interface_callback, and we know
l_error("Getting nl80211 wiphy information failed"); * nl80211_ready has already been called.
goto error; */
}
msg = l_genl_msg_new(NL80211_CMD_GET_INTERFACE); msg = l_genl_msg_new(NL80211_CMD_GET_INTERFACE);
if (!l_genl_family_dump(nl80211, msg, get_interface_callback, if (!l_genl_family_dump(nl80211, msg, get_interface_callback,
NULL, NULL)) { NULL, NULL)) {
@ -762,6 +783,24 @@ error:
l_main_quit(); l_main_quit();
} }
static void nl80211_ready(void *user_data)
{
struct l_genl_msg *msg;
msg = l_genl_msg_new(HWSIM_CMD_GET_RADIO);
if (!l_genl_family_dump(hwsim, msg, get_radio_callback,
NULL, get_radio_done_initial)) {
l_error("Getting hwsim radio information failed");
goto error;
}
return;
error:
exit_status = EXIT_FAILURE;
l_main_quit();
}
static void hwsim_ready(void *user_data) static void hwsim_ready(void *user_data)
{ {
struct l_genl_msg *msg; struct l_genl_msg *msg;
@ -846,13 +885,6 @@ static void hwsim_ready(void *user_data)
break; break;
case ACTION_NONE: case ACTION_NONE:
msg = l_genl_msg_new(HWSIM_CMD_GET_RADIO);
if (!l_genl_family_dump(hwsim, msg, get_radio_callback,
NULL, NULL)) {
l_error("Getting hwsim radio information failed");
goto error;
}
l_genl_family_set_watches(nl80211, nl80211_ready, NULL, l_genl_family_set_watches(nl80211, nl80211_ready, NULL,
NULL, NULL); NULL, NULL);
@ -860,10 +892,6 @@ static void hwsim_ready(void *user_data)
} }
return; return;
error:
exit_status = EXIT_FAILURE;
l_main_quit();
} }
static void hwsim_disappeared(void *user_data) static void hwsim_disappeared(void *user_data)