From e36967f2194d5bcc8c1485c3df7bd565cb3fd0e3 Mon Sep 17 00:00:00 2001 From: David Bauer Date: Mon, 20 Dec 2021 15:40:54 +0100 Subject: [PATCH] local-node: handle BSS transition queries Make usteer handle incoming BSS-transition requests. Update with the most active roaming neighbors like we already do for imminent disassociations. Create a new ubus method which calls hostapds bss_transition_request method. This does not set the disassoc imminent bit, thus will not automatically disassoc the requesting STA. Signed-off-by: David Bauer --- local_node.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ node.h | 3 ++ ubus.c | 18 ++++++++++ usteer.h | 13 ++++++++ 4 files changed, 126 insertions(+) diff --git a/local_node.c b/local_node.c index eb57bfb..6f2d6a7 100644 --- a/local_node.c +++ b/local_node.c @@ -46,6 +46,17 @@ usteer_local_node_state_reset(struct usteer_local_node *ln) ln->req_state = REQ_IDLE; } +static void +usteer_local_node_pending_bss_tm_free(struct usteer_local_node *ln) +{ + struct usteer_bss_tm_query *query, *tmp; + + list_for_each_entry_safe(query, tmp, &ln->bss_tm_queries, list) { + list_del(&query->list); + free(query); + } +} + static void usteer_free_node(struct ubus_context *ctx, struct usteer_local_node *ln) { @@ -57,9 +68,11 @@ usteer_free_node(struct ubus_context *ctx, struct usteer_local_node *ln) h->free_node(&ln->node); } + usteer_local_node_pending_bss_tm_free(ln); usteer_local_node_state_reset(ln); usteer_sta_node_cleanup(&ln->node); uloop_timeout_cancel(&ln->update); + uloop_timeout_cancel(&ln->bss_tm_queries_timeout); avl_delete(&local_nodes, &ln->node.avl); ubus_unregister_subscriber(ctx, &ln->ev); kvlist_free(&ln->node_info); @@ -75,6 +88,47 @@ usteer_handle_remove(struct ubus_context *ctx, struct ubus_subscriber *s, usteer_free_node(ctx, ln); } +static int +usteer_handle_bss_tm_query(struct usteer_local_node *ln, struct blob_attr *msg) +{ + enum { + BSS_TM_QUERY_ADDRESS, + BSS_TM_QUERY_DIALOG_TOKEN, + BSS_TM_QUERY_CANDIDATE_LIST, + __BSS_TM_QUERY_MAX + }; + struct blobmsg_policy policy[__BSS_TM_QUERY_MAX] = { + [BSS_TM_QUERY_ADDRESS] = { .name = "address", .type = BLOBMSG_TYPE_STRING }, + [BSS_TM_QUERY_DIALOG_TOKEN] = { .name = "dialog-token", .type = BLOBMSG_TYPE_INT8 }, + [BSS_TM_QUERY_CANDIDATE_LIST] = { .name = "candidate-list", .type = BLOBMSG_TYPE_STRING }, + }; + struct blob_attr *tb[__BSS_TM_QUERY_MAX]; + struct usteer_bss_tm_query *query; + uint8_t *sta_addr; + + blobmsg_parse(policy, __BSS_TM_QUERY_MAX, tb, blob_data(msg), blob_len(msg)); + + if (!tb[BSS_TM_QUERY_ADDRESS] || !tb[BSS_TM_QUERY_DIALOG_TOKEN]) + return 0; + + query = calloc(1, sizeof(*query)); + if (!query) + return 0; + + query->dialog_token = blobmsg_get_u8(tb[BSS_TM_QUERY_DIALOG_TOKEN]); + + sta_addr = (uint8_t *) ether_aton(blobmsg_get_string(tb[BSS_TM_QUERY_ADDRESS])); + if (!sta_addr) + return 0; + + memcpy(query->sta_addr, sta_addr, 6); + + list_add(&query->list, &ln->bss_tm_queries); + uloop_timeout_set(&ln->bss_tm_queries_timeout, 1); + + return 1; +} + static int usteer_handle_event(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, @@ -106,6 +160,12 @@ usteer_handle_event(struct ubus_context *ctx, struct ubus_object *obj, usteer_update_time(); + ln = container_of(obj, struct usteer_local_node, ev.obj); + + if(!strcmp(method, "bss-transition-query")) { + return usteer_handle_bss_tm_query(ln, msg); + } + for (i = 0; i < ARRAY_SIZE(event_types); i++) { if (strcmp(method, event_types[i]) != 0) continue; @@ -417,6 +477,36 @@ usteer_local_node_update(struct uloop_timeout *timeout) uloop_timeout_set(timeout, config.local_sta_update); } +static void +usteer_local_node_process_bss_tm_queries(struct uloop_timeout *timeout) +{ + struct usteer_bss_tm_query *query, *tmp; + struct usteer_local_node *ln; + struct usteer_node *node; + struct sta_info *si; + struct sta *sta; + + uint8_t validity_period = 100; /* ~ 10 seconds */ + + ln = container_of(timeout, struct usteer_local_node, bss_tm_queries_timeout); + node = &ln->node; + + list_for_each_entry_safe(query, tmp, &ln->bss_tm_queries, list) { + sta = usteer_sta_get(query->sta_addr, false); + if (!sta) + continue; + + si = usteer_sta_info_get(sta, node, false); + if (!si) + continue; + + usteer_ubus_bss_transition_request(si, query->dialog_token, false, false, validity_period); + } + + /* Free pending queries we can not handle */ + usteer_local_node_pending_bss_tm_free(ln); +} + static struct usteer_local_node * usteer_get_node(struct ubus_context *ctx, const char *name) { @@ -442,6 +532,8 @@ usteer_get_node(struct ubus_context *ctx, const char *name) kvlist_init(&ln->node_info, kvlist_blob_len); INIT_LIST_HEAD(&node->sta_info); + ln->bss_tm_queries_timeout.cb = usteer_local_node_process_bss_tm_queries; + INIT_LIST_HEAD(&ln->bss_tm_queries); return ln; } diff --git a/node.h b/node.h index d177ced..05c32ae 100644 --- a/node.h +++ b/node.h @@ -53,6 +53,9 @@ struct usteer_local_node { struct kvlist node_info; + struct uloop_timeout bss_tm_queries_timeout; + struct list_head bss_tm_queries; + struct { bool present; struct uloop_timeout update; diff --git a/ubus.c b/ubus.c index 4ff52bc..eb5d0ff 100644 --- a/ubus.c +++ b/ubus.c @@ -540,6 +540,24 @@ usteer_ubus_disassoc_add_neighbors(struct sta_info *si) blobmsg_close_array(&b, c); } +int usteer_ubus_bss_transition_request(struct sta_info *si, + uint8_t dialog_token, + bool disassoc_imminent, + bool abridged, + uint8_t validity_period) +{ + struct usteer_local_node *ln = container_of(si->node, struct usteer_local_node, node); + + blob_buf_init(&b, 0); + blobmsg_printf(&b, "addr", MAC_ADDR_FMT, MAC_ADDR_DATA(si->sta->addr)); + blobmsg_add_u32(&b, "dialog_token", dialog_token); + blobmsg_add_u8(&b, "disassociation_imminent", disassoc_imminent); + blobmsg_add_u8(&b, "abridged", abridged); + blobmsg_add_u32(&b, "validity_period", validity_period); + usteer_ubus_disassoc_add_neighbors(si); + return ubus_invoke(ubus_ctx, ln->obj_id, "bss_transition_request", b.head, NULL, 0, 100); +} + int usteer_ubus_notify_client_disassoc(struct sta_info *si) { struct usteer_local_node *ln = container_of(si->node, struct usteer_local_node, node); diff --git a/usteer.h b/usteer.h index f47f53e..31ba1e4 100644 --- a/usteer.h +++ b/usteer.h @@ -194,6 +194,14 @@ struct usteer_config { struct blob_attr *ssid_list; }; +struct usteer_bss_tm_query { + struct list_head list; + + /* Can't use sta_info here, as the STA might already be deleted */ + uint8_t sta_addr[6]; + uint8_t dialog_token; +}; + struct sta_info_stats { uint32_t requests; uint32_t blocked_cur; @@ -273,6 +281,11 @@ void usteer_ubus_init(struct ubus_context *ctx); void usteer_ubus_kick_client(struct sta_info *si); int usteer_ubus_trigger_client_scan(struct sta_info *si); int usteer_ubus_notify_client_disassoc(struct sta_info *si); +int usteer_ubus_bss_transition_request(struct sta_info *si, + uint8_t dialog_token, + bool disassoc_imminent, + bool abridged, + uint8_t validity_period); struct sta *usteer_sta_get(const uint8_t *addr, bool create); struct sta_info *usteer_sta_info_get(struct sta *sta, struct usteer_node *node, bool *create); -- 2.30.2