diff mbox series

hostapd: ubus: add BSS transtiton request method

Message ID 20211006222700.56022-1-mail@david-bauer.net
State Deferred
Delegated to: David Bauer
Headers show
Series hostapd: ubus: add BSS transtiton request method | expand

Commit Message

David Bauer Oct. 6, 2021, 10:27 p.m. UTC
The existing wnm_disassoc_imminent ubus method only supports issuing a
bss transition request with the disassoc imminent flag set.
For use-cases, where the client is requested to roam to another BSS
without a pending disassoc, this existing method is not suitable.

Add a new bss_transition_request ubus method, which provides a more
universal way to dispatch a transition request. It takes the following
arguments:

Required:
addr: String - MAC-address of the STA to send the request to (colon-seperated)

Optional:
abridged - Bool - Indicates if the abridged flag is set
disassociation_imminent: Bool - Whether or not the disassoc_imminent
                         flag is set
disassociation_timer: I32 - number of TBTTs after which the client will
                      be disassociated
validity_period: I32 - number of TBTTs after which the beacon
                 candidate list (if included) will be invalid
neighbors: blob-array - Array of strings containing neighbor reports as
           hex-string

Signed-off-by: David Bauer <mail@david-bauer.net>
---
 .../services/hostapd/src/src/ap/ubus.c        | 152 +++++++++++++-----
 1 file changed, 112 insertions(+), 40 deletions(-)

Comments

André Valentin Oct. 11, 2021, 8:33 p.m. UTC | #1
Hi!

I tried this patch and at works as expected. Thank you very much!

André


Am 07.10.21 um 00:27 schrieb David Bauer:
> The existing wnm_disassoc_imminent ubus method only supports issuing a
> bss transition request with the disassoc imminent flag set.
> For use-cases, where the client is requested to roam to another BSS
> without a pending disassoc, this existing method is not suitable.
> 
> Add a new bss_transition_request ubus method, which provides a more
> universal way to dispatch a transition request. It takes the following
> arguments:
> 
> Required:
> addr: String - MAC-address of the STA to send the request to (colon-seperated)
> 
> Optional:
> abridged - Bool - Indicates if the abridged flag is set
> disassociation_imminent: Bool - Whether or not the disassoc_imminent
>                          flag is set
> disassociation_timer: I32 - number of TBTTs after which the client will
>                       be disassociated
> validity_period: I32 - number of TBTTs after which the beacon
>                  candidate list (if included) will be invalid
> neighbors: blob-array - Array of strings containing neighbor reports as
>            hex-string
> 
> Signed-off-by: David Bauer <mail@david-bauer.net>
> ---
>  .../services/hostapd/src/src/ap/ubus.c        | 152 +++++++++++++-----
>  1 file changed, 112 insertions(+), 40 deletions(-)
> 
> diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c
> index 367e1b652b..1b5c60416f 100644
> --- a/package/network/services/hostapd/src/src/ap/ubus.c
> +++ b/package/network/services/hostapd/src/src/ap/ubus.c
> @@ -1275,60 +1275,30 @@ hostapd_rrm_beacon_req(struct ubus_context *ctx, struct ubus_object *obj,
>  
>  
>  #ifdef CONFIG_WNM_AP
> -enum {
> -	WNM_DISASSOC_ADDR,
> -	WNM_DISASSOC_DURATION,
> -	WNM_DISASSOC_NEIGHBORS,
> -	WNM_DISASSOC_ABRIDGED,
> -	__WNM_DISASSOC_MAX,
> -};
> -
> -static const struct blobmsg_policy wnm_disassoc_policy[__WNM_DISASSOC_MAX] = {
> -	[WNM_DISASSOC_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
> -	[WNM_DISASSOC_DURATION] { "duration", BLOBMSG_TYPE_INT32 },
> -	[WNM_DISASSOC_NEIGHBORS] { "neighbors", BLOBMSG_TYPE_ARRAY },
> -	[WNM_DISASSOC_ABRIDGED] { "abridged", BLOBMSG_TYPE_BOOL },
> -};
>  
>  static int
> -hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj,
> -			      struct ubus_request_data *ureq, const char *method,
> -			      struct blob_attr *msg)
> +hostapd_bss_tr_send(struct hostapd_data *hapd, u8 *addr, bool disassoc_imminent, bool abridged,
> +		    u16 disassoc_timer, u8 validity_period, struct blob_attr *neighbors)
>  {
> -	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
> -	struct blob_attr *tb[__WNM_DISASSOC_MAX];
>  	struct blob_attr *cur;
>  	struct sta_info *sta;
> -	int duration = 10;
> -	int rem;
>  	int nr_len = 0;
> +	int rem;
>  	u8 *nr = NULL;
> -	u8 req_mode = WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
> -	u8 addr[ETH_ALEN];
> -
> -	blobmsg_parse(wnm_disassoc_policy, __WNM_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
> -
> -	if (!tb[WNM_DISASSOC_ADDR])
> -		return UBUS_STATUS_INVALID_ARGUMENT;
> -
> -	if (hwaddr_aton(blobmsg_data(tb[WNM_DISASSOC_ADDR]), addr))
> -		return UBUS_STATUS_INVALID_ARGUMENT;
> -
> -	if ((cur = tb[WNM_DISASSOC_DURATION]) != NULL)
> -		duration = blobmsg_get_u32(cur);
> +	u8 req_mode = 0;
>  
>  	sta = ap_get_sta(hapd, addr);
>  	if (!sta)
>  		return UBUS_STATUS_NOT_FOUND;
>  
> -	if (tb[WNM_DISASSOC_NEIGHBORS]) {
> +	if (neighbors) {
>  		u8 *nr_cur;
>  
> -		if (blobmsg_check_array(tb[WNM_DISASSOC_NEIGHBORS],
> +		if (blobmsg_check_array(neighbors,
>  					BLOBMSG_TYPE_STRING) < 0)
>  			return UBUS_STATUS_INVALID_ARGUMENT;
>  
> -		blobmsg_for_each_attr(cur, tb[WNM_DISASSOC_NEIGHBORS], rem) {
> +		blobmsg_for_each_attr(cur, neighbors, rem) {
>  			int len = strlen(blobmsg_get_string(cur));
>  
>  			if (len % 2)
> @@ -1344,7 +1314,7 @@ hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj,
>  		}
>  
>  		nr_cur = nr;
> -		blobmsg_for_each_attr(cur, tb[WNM_DISASSOC_NEIGHBORS], rem) {
> +		blobmsg_for_each_attr(cur, neighbors, rem) {
>  			int len = strlen(blobmsg_get_string(cur)) / 2;
>  
>  			*nr_cur++ = WLAN_EID_NEIGHBOR_REPORT;
> @@ -1361,15 +1331,116 @@ hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj,
>  	if (nr)
>  		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
>  
> -	if (tb[WNM_DISASSOC_ABRIDGED] && blobmsg_get_bool(tb[WNM_DISASSOC_ABRIDGED]))
> +	if (abridged)
>  		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
>  
> -	if (wnm_send_bss_tm_req(hapd, sta, req_mode, duration, duration, NULL,
> +	if (disassoc_imminent)
> +		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
> +
> +	if (wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, validity_period, NULL,
>  				NULL, nr, nr_len, NULL, 0))
>  		return UBUS_STATUS_UNKNOWN_ERROR;
>  
>  	return 0;
>  }
> +
> +enum {
> +	BSS_TR_ADDR,
> +	BSS_TR_DA_IMMINENT,
> +	BSS_TR_DA_TIMER,
> +	BSS_TR_VALID_PERIOD,
> +	BSS_TR_NEIGHBORS,
> +	BSS_TR_ABRIDGED,
> +	__BSS_TR_DISASSOC_MAX
> +};
> +
> +static const struct blobmsg_policy bss_tr_policy[__BSS_TR_DISASSOC_MAX] = {
> +	[BSS_TR_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
> +	[BSS_TR_DA_IMMINENT] = { "disassociation_imminent", BLOBMSG_TYPE_BOOL },
> +	[BSS_TR_DA_TIMER] = { "disassociation_timer", BLOBMSG_TYPE_INT32 },
> +	[BSS_TR_VALID_PERIOD] = { "validity_period", BLOBMSG_TYPE_INT32 },
> +	[BSS_TR_NEIGHBORS] = { "neighbors", BLOBMSG_TYPE_ARRAY },
> +	[BSS_TR_ABRIDGED] = { "abridged", BLOBMSG_TYPE_BOOL },
> +};
> +
> +static int
> +hostapd_bss_transition_request(struct ubus_context *ctx, struct ubus_object *obj,
> +			       struct ubus_request_data *ureq, const char *method,
> +			       struct blob_attr *msg)
> +{
> +	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
> +	struct blob_attr *tb[__BSS_TR_DISASSOC_MAX];
> +	struct sta_info *sta;
> +	u32 da_timer = 0;
> +	u32 valid_period = 0;
> +	u8 addr[ETH_ALEN];
> +	bool abridged;
> +	bool da_imminent;
> +
> +	blobmsg_parse(bss_tr_policy, __BSS_TR_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
> +
> +	if (!tb[BSS_TR_ADDR])
> +		return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +	if (hwaddr_aton(blobmsg_data(tb[BSS_TR_ADDR]), addr))
> +		return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +	if (tb[BSS_TR_DA_TIMER])
> +		da_timer = blobmsg_get_u32(tb[BSS_TR_DA_TIMER]);
> +
> +	if (tb[BSS_TR_VALID_PERIOD])
> +		valid_period = blobmsg_get_u32(tb[BSS_TR_VALID_PERIOD]);
> +
> +	da_imminent = !!(tb[BSS_TR_DA_IMMINENT] && blobmsg_get_bool(tb[BSS_TR_DA_IMMINENT]));
> +	abridged = !!(tb[BSS_TR_ABRIDGED] && blobmsg_get_bool(tb[BSS_TR_ABRIDGED]));
> +
> +	return hostapd_bss_tr_send(hapd, addr, da_imminent, abridged, da_timer, valid_period,
> +				   tb[BSS_TR_NEIGHBORS]);
> +}
> +
> +enum {
> +	WNM_DISASSOC_ADDR,
> +	WNM_DISASSOC_DURATION,
> +	WNM_DISASSOC_NEIGHBORS,
> +	WNM_DISASSOC_ABRIDGED,
> +	__WNM_DISASSOC_MAX,
> +};
> +
> +static const struct blobmsg_policy wnm_disassoc_policy[__WNM_DISASSOC_MAX] = {
> +	[WNM_DISASSOC_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
> +	[WNM_DISASSOC_DURATION] { "duration", BLOBMSG_TYPE_INT32 },
> +	[WNM_DISASSOC_NEIGHBORS] { "neighbors", BLOBMSG_TYPE_ARRAY },
> +	[WNM_DISASSOC_ABRIDGED] { "abridged", BLOBMSG_TYPE_BOOL },
> +};
> +
> +static int
> +hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj,
> +			      struct ubus_request_data *ureq, const char *method,
> +			      struct blob_attr *msg)
> +{
> +	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
> +	struct blob_attr *tb[__WNM_DISASSOC_MAX];
> +	struct sta_info *sta;
> +	int duration = 10;
> +	u8 addr[ETH_ALEN];
> +	bool abridged;
> +
> +	blobmsg_parse(wnm_disassoc_policy, __WNM_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
> +
> +	if (!tb[WNM_DISASSOC_ADDR])
> +		return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +	if (hwaddr_aton(blobmsg_data(tb[WNM_DISASSOC_ADDR]), addr))
> +		return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +	if (tb[WNM_DISASSOC_DURATION])
> +		duration = blobmsg_get_u32(tb[WNM_DISASSOC_DURATION]);
> +
> +	abridged = !!(tb[WNM_DISASSOC_ABRIDGED] && blobmsg_get_bool(tb[WNM_DISASSOC_ABRIDGED]));
> +
> +	return hostapd_bss_tr_send(hapd, addr, true, abridged, duration, duration,
> +				   tb[WNM_DISASSOC_NEIGHBORS]);
> +}
>  #endif
>  
>  #ifdef CONFIG_AIRTIME_POLICY
> @@ -1454,6 +1525,7 @@ static const struct ubus_method bss_methods[] = {
>  	UBUS_METHOD("rrm_beacon_req", hostapd_rrm_beacon_req, beacon_req_policy),
>  #ifdef CONFIG_WNM_AP
>  	UBUS_METHOD("wnm_disassoc_imminent", hostapd_wnm_disassoc_imminent, wnm_disassoc_policy),
> +	UBUS_METHOD("bss_transition_request", hostapd_bss_transition_request, bss_tr_policy),
>  #endif
>  };
>  
>
diff mbox series

Patch

diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c
index 367e1b652b..1b5c60416f 100644
--- a/package/network/services/hostapd/src/src/ap/ubus.c
+++ b/package/network/services/hostapd/src/src/ap/ubus.c
@@ -1275,60 +1275,30 @@  hostapd_rrm_beacon_req(struct ubus_context *ctx, struct ubus_object *obj,
 
 
 #ifdef CONFIG_WNM_AP
-enum {
-	WNM_DISASSOC_ADDR,
-	WNM_DISASSOC_DURATION,
-	WNM_DISASSOC_NEIGHBORS,
-	WNM_DISASSOC_ABRIDGED,
-	__WNM_DISASSOC_MAX,
-};
-
-static const struct blobmsg_policy wnm_disassoc_policy[__WNM_DISASSOC_MAX] = {
-	[WNM_DISASSOC_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
-	[WNM_DISASSOC_DURATION] { "duration", BLOBMSG_TYPE_INT32 },
-	[WNM_DISASSOC_NEIGHBORS] { "neighbors", BLOBMSG_TYPE_ARRAY },
-	[WNM_DISASSOC_ABRIDGED] { "abridged", BLOBMSG_TYPE_BOOL },
-};
 
 static int
-hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj,
-			      struct ubus_request_data *ureq, const char *method,
-			      struct blob_attr *msg)
+hostapd_bss_tr_send(struct hostapd_data *hapd, u8 *addr, bool disassoc_imminent, bool abridged,
+		    u16 disassoc_timer, u8 validity_period, struct blob_attr *neighbors)
 {
-	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
-	struct blob_attr *tb[__WNM_DISASSOC_MAX];
 	struct blob_attr *cur;
 	struct sta_info *sta;
-	int duration = 10;
-	int rem;
 	int nr_len = 0;
+	int rem;
 	u8 *nr = NULL;
-	u8 req_mode = WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
-	u8 addr[ETH_ALEN];
-
-	blobmsg_parse(wnm_disassoc_policy, __WNM_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
-
-	if (!tb[WNM_DISASSOC_ADDR])
-		return UBUS_STATUS_INVALID_ARGUMENT;
-
-	if (hwaddr_aton(blobmsg_data(tb[WNM_DISASSOC_ADDR]), addr))
-		return UBUS_STATUS_INVALID_ARGUMENT;
-
-	if ((cur = tb[WNM_DISASSOC_DURATION]) != NULL)
-		duration = blobmsg_get_u32(cur);
+	u8 req_mode = 0;
 
 	sta = ap_get_sta(hapd, addr);
 	if (!sta)
 		return UBUS_STATUS_NOT_FOUND;
 
-	if (tb[WNM_DISASSOC_NEIGHBORS]) {
+	if (neighbors) {
 		u8 *nr_cur;
 
-		if (blobmsg_check_array(tb[WNM_DISASSOC_NEIGHBORS],
+		if (blobmsg_check_array(neighbors,
 					BLOBMSG_TYPE_STRING) < 0)
 			return UBUS_STATUS_INVALID_ARGUMENT;
 
-		blobmsg_for_each_attr(cur, tb[WNM_DISASSOC_NEIGHBORS], rem) {
+		blobmsg_for_each_attr(cur, neighbors, rem) {
 			int len = strlen(blobmsg_get_string(cur));
 
 			if (len % 2)
@@ -1344,7 +1314,7 @@  hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj,
 		}
 
 		nr_cur = nr;
-		blobmsg_for_each_attr(cur, tb[WNM_DISASSOC_NEIGHBORS], rem) {
+		blobmsg_for_each_attr(cur, neighbors, rem) {
 			int len = strlen(blobmsg_get_string(cur)) / 2;
 
 			*nr_cur++ = WLAN_EID_NEIGHBOR_REPORT;
@@ -1361,15 +1331,116 @@  hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj,
 	if (nr)
 		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
 
-	if (tb[WNM_DISASSOC_ABRIDGED] && blobmsg_get_bool(tb[WNM_DISASSOC_ABRIDGED]))
+	if (abridged)
 		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
 
-	if (wnm_send_bss_tm_req(hapd, sta, req_mode, duration, duration, NULL,
+	if (disassoc_imminent)
+		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+	if (wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, validity_period, NULL,
 				NULL, nr, nr_len, NULL, 0))
 		return UBUS_STATUS_UNKNOWN_ERROR;
 
 	return 0;
 }
+
+enum {
+	BSS_TR_ADDR,
+	BSS_TR_DA_IMMINENT,
+	BSS_TR_DA_TIMER,
+	BSS_TR_VALID_PERIOD,
+	BSS_TR_NEIGHBORS,
+	BSS_TR_ABRIDGED,
+	__BSS_TR_DISASSOC_MAX
+};
+
+static const struct blobmsg_policy bss_tr_policy[__BSS_TR_DISASSOC_MAX] = {
+	[BSS_TR_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[BSS_TR_DA_IMMINENT] = { "disassociation_imminent", BLOBMSG_TYPE_BOOL },
+	[BSS_TR_DA_TIMER] = { "disassociation_timer", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_VALID_PERIOD] = { "validity_period", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_NEIGHBORS] = { "neighbors", BLOBMSG_TYPE_ARRAY },
+	[BSS_TR_ABRIDGED] = { "abridged", BLOBMSG_TYPE_BOOL },
+};
+
+static int
+hostapd_bss_transition_request(struct ubus_context *ctx, struct ubus_object *obj,
+			       struct ubus_request_data *ureq, const char *method,
+			       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__BSS_TR_DISASSOC_MAX];
+	struct sta_info *sta;
+	u32 da_timer = 0;
+	u32 valid_period = 0;
+	u8 addr[ETH_ALEN];
+	bool abridged;
+	bool da_imminent;
+
+	blobmsg_parse(bss_tr_policy, __BSS_TR_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[BSS_TR_ADDR])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (hwaddr_aton(blobmsg_data(tb[BSS_TR_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[BSS_TR_DA_TIMER])
+		da_timer = blobmsg_get_u32(tb[BSS_TR_DA_TIMER]);
+
+	if (tb[BSS_TR_VALID_PERIOD])
+		valid_period = blobmsg_get_u32(tb[BSS_TR_VALID_PERIOD]);
+
+	da_imminent = !!(tb[BSS_TR_DA_IMMINENT] && blobmsg_get_bool(tb[BSS_TR_DA_IMMINENT]));
+	abridged = !!(tb[BSS_TR_ABRIDGED] && blobmsg_get_bool(tb[BSS_TR_ABRIDGED]));
+
+	return hostapd_bss_tr_send(hapd, addr, da_imminent, abridged, da_timer, valid_period,
+				   tb[BSS_TR_NEIGHBORS]);
+}
+
+enum {
+	WNM_DISASSOC_ADDR,
+	WNM_DISASSOC_DURATION,
+	WNM_DISASSOC_NEIGHBORS,
+	WNM_DISASSOC_ABRIDGED,
+	__WNM_DISASSOC_MAX,
+};
+
+static const struct blobmsg_policy wnm_disassoc_policy[__WNM_DISASSOC_MAX] = {
+	[WNM_DISASSOC_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[WNM_DISASSOC_DURATION] { "duration", BLOBMSG_TYPE_INT32 },
+	[WNM_DISASSOC_NEIGHBORS] { "neighbors", BLOBMSG_TYPE_ARRAY },
+	[WNM_DISASSOC_ABRIDGED] { "abridged", BLOBMSG_TYPE_BOOL },
+};
+
+static int
+hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj,
+			      struct ubus_request_data *ureq, const char *method,
+			      struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__WNM_DISASSOC_MAX];
+	struct sta_info *sta;
+	int duration = 10;
+	u8 addr[ETH_ALEN];
+	bool abridged;
+
+	blobmsg_parse(wnm_disassoc_policy, __WNM_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[WNM_DISASSOC_ADDR])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (hwaddr_aton(blobmsg_data(tb[WNM_DISASSOC_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[WNM_DISASSOC_DURATION])
+		duration = blobmsg_get_u32(tb[WNM_DISASSOC_DURATION]);
+
+	abridged = !!(tb[WNM_DISASSOC_ABRIDGED] && blobmsg_get_bool(tb[WNM_DISASSOC_ABRIDGED]));
+
+	return hostapd_bss_tr_send(hapd, addr, true, abridged, duration, duration,
+				   tb[WNM_DISASSOC_NEIGHBORS]);
+}
 #endif
 
 #ifdef CONFIG_AIRTIME_POLICY
@@ -1454,6 +1525,7 @@  static const struct ubus_method bss_methods[] = {
 	UBUS_METHOD("rrm_beacon_req", hostapd_rrm_beacon_req, beacon_req_policy),
 #ifdef CONFIG_WNM_AP
 	UBUS_METHOD("wnm_disassoc_imminent", hostapd_wnm_disassoc_imminent, wnm_disassoc_policy),
+	UBUS_METHOD("bss_transition_request", hostapd_bss_transition_request, bss_tr_policy),
 #endif
 };