@ -16,10 +16,75 @@
# include "mbo_ap.h"
void mbo_ap_sta_free ( struct sta_info * sta )
{
struct mbo_non_pref_chan_info * info , * prev ;
info = sta - > non_pref_chan ;
sta - > non_pref_chan = NULL ;
while ( info ) {
prev = info ;
info = info - > next ;
os_free ( prev ) ;
}
}
static void mbo_ap_parse_non_pref_chan ( struct sta_info * sta ,
const u8 * buf , size_t len )
{
struct mbo_non_pref_chan_info * info , * tmp ;
char channels [ 200 ] , * pos , * end ;
size_t num_chan , i ;
int ret ;
if ( len < = 4 )
return ; /* Not enough room for any channels */
num_chan = len - 4 ;
info = os_zalloc ( sizeof ( * info ) + num_chan ) ;
if ( ! info )
return ;
info - > op_class = buf [ 0 ] ;
info - > pref = buf [ len - 3 ] ;
info - > reason_code = buf [ len - 2 ] ;
info - > reason_detail = buf [ len - 1 ] ;
info - > num_channels = num_chan ;
buf + + ;
os_memcpy ( info - > channels , buf , num_chan ) ;
if ( ! sta - > non_pref_chan ) {
sta - > non_pref_chan = info ;
} else {
tmp = sta - > non_pref_chan ;
while ( tmp - > next )
tmp = tmp - > next ;
tmp - > next = info ;
}
pos = channels ;
end = pos + sizeof ( channels ) ;
* pos = ' \0 ' ;
for ( i = 0 ; i < num_chan ; i + + ) {
ret = os_snprintf ( pos , end - pos , " %s%u " ,
i = = 0 ? " " : " " , buf [ i ] ) ;
if ( os_snprintf_error ( end - pos , ret ) ) {
* pos = ' \0 ' ;
break ;
}
pos + = ret ;
}
wpa_printf ( MSG_DEBUG , " MBO: STA " MACSTR
" non-preferred channel list (op class %u, pref %u, reason code %u, reason detail %u, channels %s) " ,
MAC2STR ( sta - > addr ) , info - > op_class , info - > pref ,
info - > reason_code , info - > reason_detail , channels ) ;
}
void mbo_ap_check_sta_assoc ( struct hostapd_data * hapd , struct sta_info * sta ,
struct ieee802_11_elems * elems )
{
const u8 * pos , * attr ;
const u8 * pos , * attr , * end ;
size_t len ;
if ( ! hapd - > conf - > mbo_enabled | | ! elems - > mbo )
@ -32,20 +97,72 @@ void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
attr = get_ie ( pos , len , MBO_ATTR_ID_CELL_DATA_CAPA ) ;
if ( attr & & attr [ 1 ] > = 1 )
sta - > cell_capa = attr [ 2 ] ;
mbo_ap_sta_free ( sta ) ;
end = pos + len ;
while ( end - pos > 1 ) {
u8 ie_len = pos [ 1 ] ;
if ( 2 + ie_len > end - pos )
break ;
if ( pos [ 0 ] = = MBO_ATTR_ID_NON_PREF_CHAN_REPORT )
mbo_ap_parse_non_pref_chan ( sta , pos + 2 , ie_len ) ;
pos + = 2 + pos [ 1 ] ;
}
}
int mbo_ap_get_info ( struct sta_info * sta , char * buf , size_t buflen )
{
char * pos = buf , * end = buf + buflen ;
int ret ;
struct mbo_non_pref_chan_info * info ;
u8 i ;
unsigned int count = 0 ;
if ( ! sta - > cell_capa )
return 0 ;
ret = os_snprintf ( buf , buflen , " mbo_cell_capa=%u \n " , sta - > cell_capa ) ;
if ( os_snprintf_error ( buflen , ret ) )
return 0 ;
return ret ;
ret = os_snprintf ( pos , end - pos , " mbo_cell_capa=%u \n " , sta - > cell_capa ) ;
if ( os_snprintf_error ( end - pos , ret ) )
return pos - buf ;
pos + = ret ;
for ( info = sta - > non_pref_chan ; info ; info = info - > next ) {
char * pos2 = pos ;
ret = os_snprintf ( pos2 , end - pos2 ,
" non_pref_chan[%u]=%u:%u:%u:%u: " ,
count , info - > op_class , info - > pref ,
info - > reason_code , info - > reason_detail ) ;
count + + ;
if ( os_snprintf_error ( end - pos2 , ret ) )
break ;
pos2 + = ret ;
for ( i = 0 ; i < info - > num_channels ; i + + ) {
ret = os_snprintf ( pos2 , end - pos2 , " %u%s " ,
info - > channels [ i ] ,
i + 1 < info - > num_channels ?
" , " : " " ) ;
if ( os_snprintf_error ( end - pos2 , ret ) ) {
pos2 = NULL ;
break ;
}
pos2 + = ret ;
}
if ( ! pos2 )
break ;
ret = os_snprintf ( pos2 , end - pos2 , " \n " ) ;
if ( os_snprintf_error ( end - pos2 , ret ) )
break ;
pos2 + = ret ;
pos = pos2 ;
}
return pos - buf ;
}
@ -62,9 +179,21 @@ static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta,
static void mbo_ap_wnm_notif_req_elem ( struct sta_info * sta , u8 type ,
const u8 * buf , size_t len )
const u8 * buf , size_t len ,
int * first_non_pref_chan )
{
switch ( type ) {
case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT :
if ( * first_non_pref_chan ) {
/*
* Need to free the previously stored entries now to
* allow the update to replace all entries .
*/
* first_non_pref_chan = 0 ;
mbo_ap_sta_free ( sta ) ;
}
mbo_ap_parse_non_pref_chan ( sta , buf , len ) ;
break ;
case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA :
mbo_ap_wnm_notif_req_cell_capa ( sta , buf , len ) ;
break ;
@ -83,6 +212,7 @@ void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
const u8 * pos , * end ;
u8 ie_len ;
struct sta_info * sta ;
int first_non_pref_chan = 1 ;
if ( ! hapd - > conf - > mbo_enabled )
return ;
@ -103,7 +233,8 @@ void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
if ( pos [ 0 ] = = WLAN_EID_VENDOR_SPECIFIC & &
ie_len > = 4 & & WPA_GET_BE24 ( pos + 2 ) = = OUI_WFA )
mbo_ap_wnm_notif_req_elem ( sta , pos [ 5 ] ,
pos + 6 , ie_len - 4 ) ;
pos + 6 , ie_len - 4 ,
& first_non_pref_chan ) ;
else
wpa_printf ( MSG_DEBUG ,
" MBO: Ignore unknown WNM Notification element %u (len=%u) " ,