wlan_bearer/wlanengine/wlan_common/wlanengine_common_3.1/src/core_tools_parser.cpp
/*
* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Simple parser functions for core
*
*/
/*
* %version: 37 %
*/
#include "core_tools_parser.h"
#include "core_tools.h"
#include "core_frame_beacon.h"
#include "core_frame_dot11.h"
#include "core_frame_wmm_ie.h"
#include "core_frame_wmm_ie_tspec.h"
#include "core_frame_qbss_load_ie.h"
#include "am_debug.h"
// ======== MEMBER FUNCTIONS ========
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
core_connect_status_e core_tools_parser_c::is_ap_compatible_with_iap(
abs_core_wpx_adaptation_c& wpx_adaptation,
core_ap_data_c& ap_data,
core_iap_data_c& iap_data,
core_settings_c& core_settings,
bool_t is_cm_active,
bool_t ignore_channels )
{
const core_mac_address_s mac = ap_data.bssid();
DEBUG6( "core_tools_parser_c::is_ap_compatible_with_iap() - BSSID %02X:%02X:%02X:%02X:%02X:%02X",
mac.addr[0], mac.addr[1], mac.addr[2],
mac.addr[3], mac.addr[4], mac.addr[5] );
if ( iap_data.operating_mode() == core_operating_mode_infrastructure &&
!ap_data.is_infra() )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - IAP is infrastructure, AP is IBSS" );
return core_connect_mode_infra_required_but_ibss_found;
}
else if ( iap_data.operating_mode() == core_operating_mode_ibss &&
ap_data.is_infra() )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - IAP is IBSS, AP is infrastructure" );
return core_connect_mode_ibss_required_but_infra_found;
}
if ( !core_settings.is_permanent_whitelist_empty() ||
!iap_data.is_iap_whitelist_empty() )
{
if ( !core_settings.is_mac_in_permanent_whitelist(mac ) &&
!iap_data.is_mac_in_iap_whitelist( mac ) )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - whitelist defined, not in list" );
return core_connect_ap_not_whitelisted;
}
}
if ( core_settings.is_mac_in_permanent_blacklist( mac ) ||
iap_data.is_mac_in_iap_blacklist( mac ) )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - blacklisted" );
return core_connect_ap_permanently_blacklisted;
}
if ( !ignore_channels )
{
if ( !core_settings.is_valid_channel(
ap_data.band(), ap_data.channel() ) )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - invalid channel" );
return core_connect_ap_outside_defined_region;
}
}
core_connect_status_e wpx_reason( core_connect_ok );
switch ( iap_data.security_mode() )
{
case core_security_mode_allow_unsecure:
/**
* Check WPX requirements.
*/
if ( !wpx_adaptation.is_ap_wpx_compatible_with_iap(
wpx_reason,
ap_data,
iap_data ) )
{
return wpx_reason;
}
/**
* The only requirement in this mode is that the AP doesn't have privacy enabled.
*/
if ( ap_data.is_privacy_enabled() )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - AP has privacy enabled" );
return core_connect_iap_open_but_ap_requires_encryption;
}
break;
case core_security_mode_wep:
/**
* Check WPX requirements.
*/
if ( !wpx_adaptation.is_ap_wpx_compatible_with_iap(
wpx_reason,
ap_data,
iap_data ) )
{
return wpx_reason;
}
/**
* Privacy has to be enabled.
*/
if ( !ap_data.is_privacy_enabled() )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - AP doesn't have privacy enabled" );
return core_connect_iap_wep_but_ap_has_no_privacy;
}
/**
* If WPA IE or RSN IE is present, static WEP is not supported.
*/
if ( ap_data.is_wpa_ie_present() ||
ap_data.is_rsn_ie_present() )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - AP has WPA/RSN IE present" );
return core_connect_iap_wep_but_ap_has_wpa_ie;
}
break;
case core_security_mode_802dot1x_unencrypted:
/** Falls through on purpose. */
case core_security_mode_802dot1x:
/**
* Check WPX requirements.
*/
if ( !wpx_adaptation.is_ap_wpx_compatible_with_iap(
wpx_reason,
ap_data,
iap_data ) )
{
return wpx_reason;
}
/**
* If WPA IE or RSN IE is present, we must check that AP supports EAP or WPX fast-roam.
*/
if ( ap_data.is_wpa_ie_present() ||
ap_data.is_rsn_ie_present() )
{
u32_t key_management = ap_data.key_management_suites() &
( core_key_management_eap | core_key_management_wpx_fast_roam );
if ( !key_management )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no EAP as key management" );
return core_connect_wpa_eap_required_but_ap_has_no_support;
}
}
break;
case core_security_mode_wpa:
{
/**
* Check WPX requirements.
*/
if ( !wpx_adaptation.is_ap_wpx_compatible_with_iap(
wpx_reason,
ap_data,
iap_data ) )
{
return wpx_reason;
}
/**
* In this mode WPA IE or RSN IE must be present and we must have valid
* ciphers (TKIP or CCMP) and a valid key management suite (EAP or PSK).
* Privacy is required.
*/
if ( !ap_data.is_privacy_enabled() )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - AP doesn't have privacy enabled" );
return core_connect_iap_wpa_but_ap_has_no_privacy;
}
if ( !ap_data.is_wpa_ie_present() &&
!ap_data.is_rsn_ie_present() )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no WPA IE or RSN IE present" );
return core_connect_wpa_ie_required_but_ap_has_none;
}
if ( iap_data.is_psk_used() )
{
u32_t key_management = ap_data.key_management_suites() &
core_key_management_preshared;
if ( !key_management )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no PSK as key management" );
return core_connect_wpa_psk_required_but_ap_has_no_support;
}
}
else
{
u32_t key_management = ap_data.key_management_suites() &
core_key_management_eap;
if ( !key_management )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no EAP as key management" );
return core_connect_wpa_eap_required_but_ap_has_no_support;
}
}
u32_t pairwise_ciphers = ap_data.pairwise_ciphers() &
( core_cipher_suite_tkip | core_cipher_suite_ccmp );
u32_t group_cipher = ap_data.group_cipher() &
( core_cipher_suite_tkip | core_cipher_suite_ccmp );
if ( !pairwise_ciphers ||
!group_cipher )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no valid ciphers" );
return core_connect_wpa_ap_has_no_valid_ciphers;
}
if ( is_cm_active &&
( ap_data.best_pairwise_cipher() == core_cipher_suite_tkip ||
ap_data.best_group_cipher() == core_cipher_suite_tkip ) )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - WPA counter measures active" );
return core_connect_wpa_counter_measures_active;
}
break;
}
case core_security_mode_wpa2only:
{
/**
* Check WPX requirements.
*/
if ( !wpx_adaptation.is_ap_wpx_compatible_with_iap(
wpx_reason,
ap_data,
iap_data ) )
{
return wpx_reason;
}
/**
* RSN IE, CCMP cipher and valid key management suite (EAP or PSK) are required in
* this mode. Privacy is required.
*/
if ( !ap_data.is_privacy_enabled() )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - AP doesn't have privacy enabled" );
return core_connect_iap_wpa_but_ap_has_no_privacy;
}
if ( !ap_data.is_rsn_ie_present() )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no RSN IE present" );
return core_connect_wpa_ie_required_but_ap_has_none;
}
if ( iap_data.is_psk_used() )
{
u32_t key_management = ap_data.key_management_suites() &
core_key_management_preshared;
if ( !key_management )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no PSK as key management" );
return core_connect_wpa_psk_required_but_ap_has_no_support;
}
}
else
{
u32_t key_management = ap_data.key_management_suites() &
core_key_management_eap;
if ( !key_management )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no EAP as key management" );
return core_connect_wpa_eap_required_but_ap_has_no_support;
}
}
u32_t pairwise_ciphers = ap_data.pairwise_ciphers() &
core_cipher_suite_ccmp;
u32_t group_cipher = ap_data.group_cipher() &
core_cipher_suite_ccmp;
if ( !pairwise_ciphers ||
!group_cipher )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - CCMP not supported" );
return core_connect_wpa_ap_has_no_valid_ciphers;
}
break;
}
case core_security_mode_wapi:
{
/**
* Check WPX requirements.
*/
if ( !wpx_adaptation.is_ap_wpx_compatible_with_iap(
wpx_reason,
ap_data,
iap_data ) )
{
return wpx_reason;
}
/**
* In this mode WAPI IE must be present and we must have valid ciphers
* and a valid key management suite. Privacy is required.
*/
if ( !ap_data.is_privacy_enabled() )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - AP doesn't have privacy enabled" );
return core_connect_iap_wapi_but_ap_has_no_privacy;
}
if ( !ap_data.is_wapi_ie_present() )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no WAPI IE present" );
return core_connect_wapi_ie_required_but_ap_has_none;
}
if ( iap_data.is_psk_used() )
{
u32_t key_management = ap_data.key_management_suites() &
core_key_management_wapi_psk;
if ( !key_management )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no WAPI PSK as key management" );
return core_connect_wapi_psk_required_but_ap_has_no_support;
}
}
else
{
u32_t key_management = ap_data.key_management_suites() &
core_key_management_wapi_certificate;
if ( !key_management )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no WAPI certificate as key management" );
return core_connect_wapi_certificate_required_but_ap_has_no_support;
}
}
u32_t pairwise_ciphers = ap_data.pairwise_ciphers() &
core_cipher_suite_wpi;
u32_t group_cipher = ap_data.group_cipher() &
core_cipher_suite_wpi;
if ( !pairwise_ciphers ||
!group_cipher )
{
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - no valid ciphers" );
return core_connect_wapi_ap_has_no_valid_ciphers;
}
break;
}
}
DEBUG( "core_tools_parser_c::is_ap_compatible_with_iap() - OK" );
return core_connect_ok;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
core_connect_status_e core_tools_parser_c::is_ap_suitable(
abs_core_wpx_adaptation_c& wpx_adaptation,
core_ap_data_c& ap_data,
core_iap_data_c& iap_data,
core_settings_c& core_settings,
core_connection_data_c& connection_data,
u8_t min_rcpi,
const medium_time_s& min_medium_time,
bool_t is_cm_active,
const core_mac_address_s& current_bssid )
{
const core_mac_address_s bssid(
ap_data.bssid() );
if ( current_bssid == bssid )
{
DEBUG( "core_tools_parser_c::is_ap_suitable() - currently connected to this AP" );
return core_connect_unspecified_failure;
}
if ( connection_data.is_mac_in_temporary_blacklist( bssid ) )
{
DEBUG( "core_tools_parser_c::is_ap_suitable() - AP is temporarily blacklisted" );
return core_connect_ap_temporarily_blacklisted;
}
core_connect_status_e ret = core_tools_parser_c::is_ap_compatible_with_iap(
wpx_adaptation,
ap_data,
iap_data,
core_settings,
is_cm_active );
if ( ret != core_connect_ok )
{
DEBUG( "core_tools_parser_c::is_ap_suitable() - IAP doesn't match the AP" );
return ret;
}
if ( min_rcpi != RCPI_VALUE_NONE &&
ap_data.rcpi() < min_rcpi )
{
DEBUG2( "core_tools_parser_c::is_ap_suitable() - signal too weak (RCPI is %u, required RCPI is %u)",
ap_data.rcpi(), min_rcpi );
return core_connect_ap_signal_too_weak;
}
bool_t is_ac_required_for_voice =
connection_data.traffic_stream_list().is_traffic_stream_for_access_class( core_access_class_voice );
bool_t is_ac_required_for_video =
connection_data.traffic_stream_list().is_traffic_stream_for_access_class( core_access_class_video );
bool_t is_ac_required_for_best_effort =
connection_data.traffic_stream_list().is_traffic_stream_for_access_class( core_access_class_best_effort );
bool_t is_ac_required_for_background =
connection_data.traffic_stream_list().is_traffic_stream_for_access_class( core_access_class_background );
if ( min_medium_time != MEDIUM_TIME_NOT_DEFINED )
{
ASSERT( is_ac_required_for_voice ||
is_ac_required_for_video ||
is_ac_required_for_best_effort ||
is_ac_required_for_background );
if ( !ap_data.is_wmm_ie_present() )
{
DEBUG( "core_tools_parser_c::is_ap_suitable() - AP doesn't have a WMM IE" );
return core_connect_ap_has_no_admission_control;
}
if ( is_ac_required_for_voice &&
!ap_data.is_admission_control_required( core_access_class_voice ) )
{
DEBUG( "core_tools_parser_c::is_ap_suitable() - AP doesn't require admission control for Voice" );
return core_connect_ap_has_no_admission_control;
}
if ( is_ac_required_for_video &&
!ap_data.is_admission_control_required( core_access_class_video ) )
{
DEBUG( "core_tools_parser_c::is_ap_suitable() - AP doesn't require admission control for Video" );
return core_connect_ap_has_no_admission_control;
}
if ( is_ac_required_for_best_effort &&
!ap_data.is_admission_control_required( core_access_class_best_effort ) )
{
DEBUG( "core_tools_parser_c::is_ap_suitable() - AP doesn't require admission control for Background" );
return core_connect_ap_has_no_admission_control;
}
if ( is_ac_required_for_background &&
!ap_data.is_admission_control_required( core_access_class_background ) )
{
DEBUG( "core_tools_parser_c::is_ap_suitable() - AP doesn't require admission control for BestEffort" );
return core_connect_ap_has_no_admission_control;
}
bool_t is_adm_capacity_found( false_t );
medium_time_s admission_capacities(
ADMISSION_CAPACITIES_NOT_DEFINED );
ap_data.admission_capacity( admission_capacities );
/**
* Check required medium time by user priority.
*/
for( u8_t idx( 0 ); idx < MAX_QOS_USER_PRIORITY; ++idx )
{
if( min_medium_time.up[idx] )
{
if( admission_capacities.up[idx] < min_medium_time.up[idx] )
{
DEBUG3( "core_tools_parser_c::is_ap_suitable() - not enough admission capacity for UP %u: %u, required %u",
idx, admission_capacities.up[idx], min_medium_time.up[idx] );
return core_connect_ap_has_no_free_admission_capability;
}
else if( admission_capacities.up[idx] != ADMISSION_CAPACITY_NOT_DEFINED )
{
DEBUG3( "core_tools_parser_c::is_ap_suitable() - admission capacity for UP %u: %u, required %u",
idx, admission_capacities.up[idx], min_medium_time.up[idx] );
is_adm_capacity_found = true_t;
}
else
{
DEBUG1( "core_tools_parser_c::is_ap_suitable() - no admission capacity advertised for UP %u",
idx );
}
}
}
/**
* Check required medium time by access class.
*/
u16_t total_min_medium_time( 0 );
for( u8_t idx( 0 ); idx < MAX_QOS_ACCESS_CLASS; ++idx )
{
if( min_medium_time.ac[idx] )
{
total_min_medium_time += min_medium_time.ac[idx];
if( admission_capacities.ac[idx] < min_medium_time.ac[idx] )
{
DEBUG3( "core_tools_parser_c::is_ap_suitable() - not enough admission capacity for AC %u: %u, required %u",
idx, admission_capacities.ac[idx], min_medium_time.ac[idx] );
return core_connect_ap_has_no_free_admission_capability;
}
else if( admission_capacities.ac[idx] != ADMISSION_CAPACITY_NOT_DEFINED )
{
DEBUG3( "core_tools_parser_c::is_ap_suitable() - admission capacity for AC %u: %u, required %u",
idx, admission_capacities.ac[idx], min_medium_time.ac[idx] );
is_adm_capacity_found = true_t;
}
else
{
DEBUG1( "core_tools_parser_c::is_ap_suitable() - no admission capacity advertised for AC %u",
idx );
}
}
}
if( !is_adm_capacity_found )
{
const core_frame_qbss_load_ie_c* qbss_load_ie = ap_data.qbss_load_ie();
if ( qbss_load_ie &&
qbss_load_ie->admission_capacity() < total_min_medium_time )
{
is_adm_capacity_found = true_t;
DEBUG2( "core_tools_parser_c::is_ap_suitable() - not enough admission capacity (%u, required %u)",
qbss_load_ie->admission_capacity(), total_min_medium_time );
delete qbss_load_ie;
return core_connect_ap_has_no_free_admission_capability;
}
delete qbss_load_ie;
qbss_load_ie = NULL;
}
/**
* If the AP doesn't advertise any admission capacity information,
* we'll assume there's enough capacity.
*/
if( !is_adm_capacity_found )
{
DEBUG( "core_tools_parser_c::is_ap_suitable() - AP doesn't advertise any admission capacity information" );
}
}
DEBUG( "core_tools_parser_c::is_ap_suitable() - AP is suitable" );
return core_connect_ok;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
core_ap_information_s core_tools_parser_c::get_ap_info(
const core_iap_data_c& iap_data,
core_ap_data_c& ap_data )
{
core_ap_information_s info;
core_tools_c::fillz(
&info,
sizeof( info ) );
info.ssid = ap_data.ssid();
info.bssid = ap_data.bssid();
info.capabilities = ap_data.capabilities();
info.channel = ap_data.channel();
info.rcpi = ap_data.rcpi();
info.security_mode = core_tools_c::security_mode(
iap_data,
ap_data );
/**
* Parse basic and supported rates to own bitmaps.
*/
info.basic_rates = 0;
info.supported_rates = 0;
for( core_frame_dot11_ie_c* ie = ap_data.frame()->first_ie(); ie; ie = ap_data.frame()->next_ie() )
{
if ( ie->element_id() == core_frame_dot11_ie_c::core_frame_dot11_ie_element_id_supported_rates ||
ie->element_id() == core_frame_dot11_ie_c::core_frame_dot11_ie_element_id_extended_rates )
{
u32_t temp_basic_rates( 0 );
u32_t temp_supported_rates( 0 );
core_tools_parser_c::parse_rates(
ie->data() + CORE_FRAME_DOT11_IE_HEADER_LENGTH,
ie->length(),
temp_basic_rates,
temp_supported_rates );
info.basic_rates |= temp_basic_rates;
info.supported_rates |= temp_supported_rates;
}
delete ie;
}
info.is_ac_required_for_voice =
ap_data.is_admission_control_required( core_access_class_voice );
info.is_ac_required_for_video =
ap_data.is_admission_control_required( core_access_class_video );
info.is_ac_required_for_best_effort =
ap_data.is_admission_control_required( core_access_class_best_effort );
info.is_ac_required_for_background =
ap_data.is_admission_control_required( core_access_class_background );
info.is_wpx = ap_data.is_wpx();
return info;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
bool_t core_tools_parser_c::get_wmm_traffic_stream_params(
core_frame_dot11_c& frame,
u8_t tid,
core_traffic_stream_params_s& tspec )
{
for( core_frame_dot11_ie_c* ie = frame.first_ie(); ie; ie = frame.next_ie() )
{
if ( ie->element_id() == core_frame_dot11_ie_c::core_frame_dot11_ie_element_id_wmm_tspec )
{
core_frame_wmm_ie_tspec_c* tspec_ie = core_frame_wmm_ie_tspec_c::instance( *ie );
if ( tspec_ie &&
tspec_ie->tid() == tid )
{
DEBUG1( "core_tools_parser_c::get_wmm_traffic_stream_params() - TSPEC with TID %u found",
tid );
tspec.is_periodic_traffic = tspec_ie->is_periodic_traffic();
tspec.direction = tspec_ie->direction();
tspec.nominal_msdu_size = tspec_ie->nominal_msdu_size();
tspec.maximum_msdu_size = tspec_ie->maximum_msdu_size();
tspec.minimum_service_interval = tspec_ie->minimum_service_interval();
tspec.maximum_service_interval = tspec_ie->maximum_service_interval();
tspec.inactivity_interval = tspec_ie->inactivity_interval();
tspec.suspension_interval = tspec_ie->suspension_interval();
tspec.service_start_time = tspec_ie->service_start_time();
tspec.minimum_data_rate = tspec_ie->minimum_data_rate();
tspec.mean_data_rate = tspec_ie->mean_data_rate();
tspec.peak_data_rate = tspec_ie->peak_data_rate();
tspec.maximum_burst_size = tspec_ie->maximum_burst_size();
tspec.delay_bound = tspec_ie->delay_bound();
tspec.minimum_phy_rate = tspec_ie->minimum_phy_rate();
tspec.surplus_bandwidth_allowance = tspec_ie->surplus_bandwidth_allowance();
tspec.medium_time = tspec_ie->medium_time();
delete tspec_ie;
tspec_ie = NULL;
delete ie;
ie = NULL;
return true_t;
}
delete tspec_ie;
tspec_ie = NULL;
}
delete ie;
ie = NULL;
}
return false_t;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void core_tools_parser_c::parse_rates(
const u8_t* data,
u8_t data_length,
u32_t& basic_rates,
u32_t& supported_rates )
{
DEBUG( "core_tools_parser_c::parse_rates()" );
basic_rates = 0;
supported_rates = 0;
for( u8_t idx( 0 ); idx < data_length; ++idx )
{
core_tx_rate_e rate(
core_tools_c::convert_tx_rate_to_tx_rate_enum(
data[idx] & ~TX_RATE_BASIC_MASK ) );
supported_rates |= rate;
if ( data[idx] & TX_RATE_BASIC_MASK )
{
/**
* The highest bit is enabled, the rate is both a basic rate
* and a supported rate.
*/
basic_rates |= rate;
}
}
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
core_tools_parser_c::core_tools_parser_c()
{
DEBUG( "core_tools_parser_c::core_tools_parser_c()" );
}