diff --git a/src/include/ubus.h b/src/include/ubus.h index d436912..17833b1 100644 --- a/src/include/ubus.h +++ b/src/include/ubus.h @@ -133,6 +133,16 @@ int send_set_probe(struct dawn_mac client_addr); */ int wnm_disassoc_imminent(uint32_t id, const struct dawn_mac client_addr, struct kicking_nr* neighbor_list, int threshold, uint32_t duration); +/** + * Function to ask a client to move to another AP, but not enforce it. + * @param id + * @param client_addr + * @param dest_ap + * @param duration + * @return - 0 = asynchronous (client has been told to remove itself, and caller should manage arrays); 1 = synchronous (caller should assume arrays are updated) + */ +int bss_transition_request(uint32_t id, const struct dawn_mac client_addr, struct kicking_nr* neighbor_list, uint32_t duration); + /** * Send control message to all hosts to add the mac to a don't control list. * @param client_addr diff --git a/src/storage/datastorage.c b/src/storage/datastorage.c index 41a51b3..b847d42 100644 --- a/src/storage/datastorage.c +++ b/src/storage/datastorage.c @@ -479,11 +479,11 @@ int kick_clients(struct dawn_mac bssid_mac, uint32_t id) { // Seach for BSSID client *j = *client_find_first_bc_entry(kicking_ap->bssid_addr, dawn_mac_null, false); - // Go through clients - while (j != NULL && mac_is_equal_bb(j->bssid_addr, kicking_ap->bssid_addr)) { + // Go through clients: only kick one each time (not sure why, was in original algorithm...) + while (kicked_clients == 0 && j != NULL && mac_is_equal_bb(j->bssid_addr, kicking_ap->bssid_addr)) { struct kicking_nr *kick_nr_list = NULL; - int do_kick = 0; + int kick_type = 0; int own_score = 0; if (mac_find_entry(j->client_addr)) { @@ -492,29 +492,54 @@ int kick_clients(struct dawn_mac bssid_mac, uint32_t id) { else { dawn_mutex_require(&probe_array_mutex); probe_entry* own_probe = probe_array_get_entry(j->client_addr, kicking_ap->bssid_addr); + if (own_probe == NULL) { // no entry for own ap - may happen if DAWN is started after client has connected, and then "sleeps" so sends no BEACON / PROBE dawnlog_info("Current AP " MACSTR " for client " MACSTR " not found in probe array!\n", MAC2STR(kicking_ap->bssid_addr.u8), MAC2STR(j->client_addr.u8)); print_probe_array(); + + // TODO: Work out a way to handle clients that are reluctant to share a probe / beacon that doesn't become DoS for them + // do_kick = -1; } - else { + + if ((kick_type == 0) && own_probe && (dawn_metric.kicking & 1) == 1) { own_score = eval_probe_metric(own_probe, kicking_ap); dawnlog_trace("Current AP score = %d for:\n", own_score); print_probe_entry(DAWNLOG_TRACE, own_probe); - do_kick = better_ap_available(kicking_ap, own_probe, own_score, &kick_nr_list); + kick_type = better_ap_available(kicking_ap, own_probe, own_score, &kick_nr_list); + + // If we found any candidates by evaluating PROBEs (even if too low to kick to) add the highest scoring one to local AP NR set + if (kick_type != 0 && dawn_metric.set_hostapd_nr == 2 && kick_nr_list) + { + dawnlog_trace("Adding " MACSTR "as local NR entry candidate\n", MAC2STR(kick_nr_list->nr_ap->bssid_addr.u8)); + insert_kicking_nr_by_bssid(&ap_nr_list, kick_nr_list->nr_ap); + } + } + + if ((kick_type == 0) && own_probe && (dawn_metric.kicking & 2) == 2) { + int band = get_band(own_probe->freq); + + if (own_probe->signal < dawn_metric.rssi_center[band]) + { + dawnlog_info("Client " MACSTR ": Low asolute RSSI - proposing other APs\n", MAC2STR(j->client_addr.u8)); + dawn_mutex_require(&ap_array_mutex); + + // FIXME: Using all AP is OK for a small network, but should ideally use the local-to-current-AP set + for (ap* candidate_ap = ap_set; candidate_ap != NULL; candidate_ap = candidate_ap->next_ap) { + // Check is same SSID< but is not the current AP + if (candidate_ap != kicking_ap && strcmp((char*)kicking_ap->ssid, (char*)candidate_ap->ssid) == 0) { + insert_kicking_nr_by_bssid(&kick_nr_list, candidate_ap); + } + } + + kick_type = 2; + } } } - // If we found any candidates (even if too low to kick to) add the highest scoring one to local AP NR set - if (dawn_metric.set_hostapd_nr == 2 && kick_nr_list) - { - dawnlog_trace("Adding " MACSTR "as local NR entry candidate\n", MAC2STR(kick_nr_list->nr_ap->bssid_addr.u8)); - insert_kicking_nr_by_bssid(&ap_nr_list, kick_nr_list->nr_ap); - } - // better ap available - if (do_kick > 0) { + if (kick_type > 0) { // kick after algorithm decided to kick several times // + rssi is changing a lot @@ -562,28 +587,31 @@ int kick_clients(struct dawn_mac bssid_mac, uint32_t id) { // don't deauth station? <- deauth is better! // maybe we can use handovers... //del_client_interface(id, client_array[j].client_addr, NO_MORE_STAS, 1, 1000); - int sync_kick = wnm_disassoc_imminent(id, j->client_addr, kick_nr_list, own_score + dawn_metric.kicking_threshold, 12); - - // Synchronous kick is a test harness feature to indicate arrays have been updated, so don't change further - if (sync_kick) + if (kick_type == 1) { - kicked_clients++; + int sync_kick = wnm_disassoc_imminent(id, j->client_addr, kick_nr_list, own_score + dawn_metric.kicking_threshold, 12); + + if (!sync_kick) + { + client_array_delete(j, false); + + // don't delete clients in a row. use update function again... + // -> chan_util update, ... + //FIXME: Why / 4? + add_client_update_timer(timeout_config.update_client * 1000 / 4); + } } else { - client_array_delete(j, false); - - // don't delete clients in a row. use update function again... - // -> chan_util update, ... - //FIXME: Why / 4? - add_client_update_timer(timeout_config.update_client * 1000 / 4); - break; + bss_transition_request(id, j->client_addr, kick_nr_list, 12); } + + kicked_clients++; } } } } - else if (do_kick == -1) { + else if (kick_type == -1) { // FIXME: Causes clients to be kicked until first probe is received, which is a bit brutal for pre-802.11k clients. dawnlog_info("Client " MACSTR ": No Information about client. Force reconnect:\n", MAC2STR(j->client_addr.u8)); print_client_entry(DAWNLOG_TRACE, j); @@ -605,6 +633,7 @@ int kick_clients(struct dawn_mac bssid_mac, uint32_t id) { if (dawn_metric.set_hostapd_nr == 2) ubus_set_nr_from_clients(ap_nr_list); + // FIXME: Consider retaining this so it can be used for kick type 2 - low absolute RSSI remove_kicking_nr_list(ap_nr_list); ap_nr_list = NULL; @@ -634,7 +663,7 @@ void update_iw_info(struct dawn_mac bssid_mac) { iee80211_calculate_expected_throughput_mbit(get_expected_throughput_iwinfo(j->client_addr))); if (rssi != INT_MIN) { - if (!probe_array_update_rssi(j->client_addr, j->bssid_addr, rssi, true)) { + if (probe_array_update_rssi(j->client_addr, j->bssid_addr, rssi, true) == NULL) { dawnlog_info("Failed to update rssi!\n"); } else { @@ -795,6 +824,7 @@ probe_entry* probe_array_update_rssi(struct dawn_mac client_addr, struct dawn_ma dawn_mutex_require(&probe_array_mutex); probe_entry* probe_req_new = dawn_malloc(sizeof(probe_entry)); + probe_entry* probe_req_updated = NULL; if (probe_req_new) { // Fields we will update @@ -812,7 +842,7 @@ probe_entry* probe_array_update_rssi(struct dawn_mac client_addr, struct dawn_ma probe_req_new->next_probe = NULL; probe_req_new->next_probe_skip = NULL; - probe_entry* probe_req_updated = insert_to_probe_array(probe_req_new, false, false, false, time(0)); + probe_req_updated = insert_to_probe_array(probe_req_new, false, false, false, time(0)); if (probe_req_new != probe_req_updated) { dawnlog_info("RSSI PROBE used to update client / BSSID = " MACSTR " / " MACSTR " \n", MAC2STR(probe_req_updated->client_addr.u8), MAC2STR(probe_req_updated->bssid_addr.u8)); @@ -829,7 +859,7 @@ probe_entry* probe_array_update_rssi(struct dawn_mac client_addr, struct dawn_ma ubus_send_probe_via_network(probe_req_updated, false); } - return probe_req_new; + return probe_req_updated; } probe_entry* probe_array_update_rcpi_rsni(struct dawn_mac client_addr, struct dawn_mac bssid_addr, uint32_t rcpi, uint32_t rsni, int send_network) diff --git a/src/test/test_storage.c b/src/test/test_storage.c index 41377c0..14e7fdf 100644 --- a/src/test/test_storage.c +++ b/src/test/test_storage.c @@ -34,6 +34,34 @@ int send_set_probe(struct dawn_mac client_addr) return 0; } +int bss_transition_request(uint32_t id, const struct dawn_mac client_addr, struct kicking_nr* neighbor_list, uint32_t duration) +{ + int ret = 0; + + printf("bss_transition_request() was called...\n"); + + if (neighbor_list != NULL) + { + // Fake a client being disassociated and then rejoining on the recommended neighbor + client* mc = client_array_get_client(client_addr); + mc = client_array_delete(mc, true); + // Originally, there was only one AP, not a list of them; that AP is at the tail of the list + // Use it to keep the results the same as before + while (neighbor_list && neighbor_list->next) + neighbor_list = neighbor_list->next; + for (int n = 0; n < ETH_ALEN; n++) + sscanf(neighbor_list->nr_ap->neighbor_report + n * 2, "%2hhx", mc->bssid_addr.u8 + n); + insert_client_to_array(mc, 0); + printf("BSS TRANSITION TO " NR_MACSTR "\n", NR_MAC2STR(neighbor_list->nr_ap->neighbor_report)); + + // Tell caller not to change the arrays any further + ret = 1; + } + + return ret; +} + + int wnm_disassoc_imminent(uint32_t id, const struct dawn_mac client_addr, struct kicking_nr* neighbor_list, int threshold, uint32_t duration) { int ret = 0; diff --git a/src/utils/ubus.c b/src/utils/ubus.c index 77e5911..ed4c992 100644 --- a/src/utils/ubus.c +++ b/src/utils/ubus.c @@ -1149,6 +1149,46 @@ void del_client_interface(uint32_t id, const struct dawn_mac client_addr, uint32 dawn_unregmem(&b); } +int bss_transition_request(uint32_t id, const struct dawn_mac client_addr, struct kicking_nr* neighbor_list, uint32_t duration) { + struct hostapd_sock_entry* sub; + struct blob_buf b = { 0 }; + + dawnlog_debug_func("Entering..."); + + blob_buf_init(&b, 0); + dawn_regmem(&b); + blobmsg_add_macaddr(&b, "addr", client_addr); + blobmsg_add_u32(&b, "disassociation_timer", duration); + blobmsg_add_u32(&b, "validity_period", duration); + blobmsg_add_u8(&b, "abridged", 1); // prefer aps in neighborlist + blobmsg_add_u8(&b, "disassociation_imminent", 0); // Just a friendly request - no disassociation + + void* nbs = blobmsg_open_array(&b, "neighbors"); + + // Add the first N AP + int neighbors_added = 0; + while (neighbors_added < dawn_metric.disassoc_nr_length && neighbor_list != NULL) { + dawnlog_info("BSS TRANSITION NEIGHBOR " NR_MACSTR "\n", NR_MAC2STR(neighbor_list->nr_ap->neighbor_report)); + blobmsg_add_string(&b, NULL, neighbor_list->nr_ap->neighbor_report); + neighbor_list = neighbor_list->next; + neighbors_added++; + } + + blobmsg_close_array(&b, nbs); + list_for_each_entry(sub, &hostapd_sock_list, list) + { + if (sub->subscribed) { + int timeout = 1; //TDO: Maybe ID is wrong?! OR CHECK HERE ID + ubus_invoke(ctx, id, "bss_transition_request", b.head, NULL, NULL, timeout * 1000); + } + } + + blob_buf_free(&b); + dawn_unregmem(&b); + + return 0; +} + int wnm_disassoc_imminent(uint32_t id, const struct dawn_mac client_addr, struct kicking_nr* neighbor_list, int threshold, uint32_t duration) { struct hostapd_sock_entry *sub; struct blob_buf b = {0}; @@ -1168,7 +1208,7 @@ int wnm_disassoc_imminent(uint32_t id, const struct dawn_mac client_addr, struct // Add the first N AP that reach threshold, where list order id high->low score int neighbors_added = 0; while(neighbors_added < dawn_metric.disassoc_nr_length && neighbor_list != NULL && neighbor_list->score >= threshold) { - dawnlog_info("BSS TRANSITION NEIGHBOR " NR_MACSTR ", Score=%d\n", NR_MAC2STR(neighbor_list->nr_ap->neighbor_report), neighbor_list->score); + dawnlog_info("WNM DISASSOC NEIGHBOR " NR_MACSTR ", Score=%d\n", NR_MAC2STR(neighbor_list->nr_ap->neighbor_report), neighbor_list->score); blobmsg_add_string(&b, NULL, neighbor_list->nr_ap->neighbor_report); neighbor_list = neighbor_list->next; neighbors_added++;