From 7418c5fd3c55f0667d5c36f8d1bfb5d2e9488787 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 28 Jun 2023 13:20:33 -0700 Subject: [PATCH] hwsim: handle ADD/DEL_MAC_ADDR events Handling these events notifies hwsim of address changes for interface creation/removal outside the initial namespace as well as address changes due to scanning address randomization. Interfaces that hwsim already knows about are still handled via nl80211. But any interfaces not known when ADD/DEL_MAC_ADDR events come will be treated specially. For ADD, a dummy interface object will be created and added to the queue. This lets the frame processing match the destination address correctly. This can happen both for scan randomization and interface creation outside of the initial namespace. For the DEL event we handle similarly and don't touch any interfaces found via nl80211 (i.e. have a 'name') but need to also be careful with the dummy interfaces that were created outside the initial namespace. We want to keep these around but scanning MAC changes can also delete them. This is why a reference count was added so scanning doesn't cause a removal. For example, the following sequence: ADD_MAC_ADDR (interface creation) ADD_MAC_ADDR (scanning started) DEL_MAC_ADDR (scanning done) --- tools/hwsim.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/tools/hwsim.c b/tools/hwsim.c index 2fee9b19..b7d637ca 100644 --- a/tools/hwsim.c +++ b/tools/hwsim.c @@ -440,6 +440,7 @@ struct interface_info_rec { uint8_t addr[ETH_ALEN]; char *name; uint32_t iftype; + int ref; }; static struct l_queue *radio_info; @@ -515,6 +516,14 @@ static bool interface_info_match_id(const void *a, const void *b) return rec->id == id; } +static bool interface_info_match_addr(const void *a, const void *b) +{ + const struct interface_info_rec *rec = a; + const uint8_t *addr = b; + + return memcmp(rec->addr, addr, ETH_ALEN) == 0; +} + static const char *radio_get_path(const struct radio_info_rec *rec) { static char path[15]; @@ -1682,6 +1691,122 @@ static void hwsim_frame_event(struct l_genl_msg *msg) process_frame(frame); } +static bool get_tx_rx_addrs(struct l_genl_msg *msg, const uint8_t **tx_out, + const uint8_t **rx_out) +{ + struct l_genl_attr attr; + uint16_t type, len; + const void *data; + const uint8_t *tx = NULL, *rx = NULL; + + if (!l_genl_attr_init(&attr, msg)) + return false; + + while (l_genl_attr_next(&attr, &type, &len, &data)) { + switch (type) { + case HWSIM_ATTR_ADDR_TRANSMITTER: + if (len != ETH_ALEN) + return false; + + tx = data; + break; + case HWSIM_ATTR_ADDR_RECEIVER: + if (len != ETH_ALEN) + return false; + + rx = data; + break; + default: + break; + } + } + + if (!tx || !rx) + return false; + + *tx_out = tx; + *rx_out = rx; + + return true; +} + +static void hwsim_add_mac_event(struct l_genl_msg *msg) +{ + const uint8_t *tx = NULL, *rx = NULL; + struct radio_info_rec *radio_rec; + struct interface_info_rec *interface_rec; + + if (!get_tx_rx_addrs(msg, &tx, &rx)) + return; + + /* No radio matches the TX address, hwsim must not have created it */ + radio_rec = l_queue_find(radio_info, radio_info_match_addr1, tx); + if (!radio_rec) + return; + + interface_rec = l_queue_find(interface_info, + interface_info_match_addr, rx); + if (interface_rec) { + /* Existing interface, address changes handled via nl80211 */ + if (interface_rec->name) + return; + + /* + * Transient/dummy interface we already know about. This likely + * was created, then a scan changed the address temporarily. + * Reflect this change and increment the ref so the following + * DEL event doesn't destroy it + */ + __atomic_fetch_add(&interface_rec->ref, 1, __ATOMIC_SEQ_CST); + return; + } + + /* + * Create a dummy interface entry for this address that only contains + * the radio and address. This is either a transient entry due to scan + * randomization or an interface created outside this namespace. + */ + interface_rec = l_new(struct interface_info_rec, 1); + interface_rec->radio_rec = radio_rec; + interface_rec->ref = 1; + memcpy(interface_rec->addr, rx, ETH_ALEN); + + l_queue_push_tail(interface_info, interface_rec); +} + +static void hwsim_del_mac_event(struct l_genl_msg *msg) +{ + const uint8_t *tx = NULL, *rx = NULL; + struct radio_info_rec *radio_rec; + struct interface_info_rec *interface_rec; + + if (!get_tx_rx_addrs(msg, &tx, &rx)) + return; + + /* No radio matches the TX address, hwsim must not have created it */ + radio_rec = l_queue_find(radio_info, radio_info_match_addr1, tx); + if (!radio_rec) + return; + + interface_rec = l_queue_find(interface_info, + interface_info_match_addr, rx); + if (!interface_rec) + return; + + /* + * This change is handled via nl80211 so we don't want to touch this + * interface here. + */ + if (interface_rec->name) + return; + + if (__atomic_sub_fetch(&interface_rec->ref, 1, __ATOMIC_SEQ_CST)) + return; + + l_queue_remove(interface_info, interface_rec); + l_free(interface_rec); +} + static void hwsim_unicast_handler(struct l_genl_msg *msg, void *user_data) { uint8_t cmd; @@ -1695,6 +1820,12 @@ static void hwsim_unicast_handler(struct l_genl_msg *msg, void *user_data) case HWSIM_CMD_FRAME: hwsim_frame_event(msg); break; + case HWSIM_CMD_ADD_MAC_ADDR: + hwsim_add_mac_event(msg); + break; + case HWSIM_CMD_DEL_MAC_ADDR: + hwsim_del_mac_event(msg); + break; default: break; }