From 08091e08440353aaf17979ba81086df8f0198de0 Mon Sep 17 00:00:00 2001 From: Engin Akca Date: Thu, 17 Oct 2024 09:26:45 +0300 Subject: [PATCH] RDKBDEV-2960 : EasyMesh Controller Improvements Reason for Change: New features like WiFi-7, improvements and bug fixes done by Airties Easymesh team, are merged. SetSSID() sync with Device.WiFi, SteerWiFiBackhaul() methods added. Many new TLV handling support added. Added support for AP MLDs, bSTA MLDs and STA MLDs. DataElements items APMLD (APMLDConfig, AffiliatedAP), STAMLD (STAMLDConfig, WiFi7Capabilities, AffiliatedSTA) and bSTAMLD (bSTAMLDConfig) added. Modification of BTMSteeringDisallowedSTAList, LocalSteeringDisallowedSTAList and DisAllowedOpClassChannels added. Added encryption rx/tx counters to prevent replay attacks. Missing OpenSSL v3 support for new items (CMAC) added. Added support for TLV boundary fragmentation on receive side. Added support for octet boundary fragmentation on transmit side. Unit tests added. LICENSE and NOTICE updated. Known Issues: MAX_OBJECT_NAME_LENGTH of rbus, which is bound to RTMSG_HEADER_MAX_TOPIC_LENGTH (128) is not enough for some of the elements of NeighborBSS. So they are truncated as for now. Test Procedure: Use recommended setup and check for onboarding of devices with Easymesh agent. Use CLI or GUI to modify. Risks: Moderate Signed-off-by: Engin Akca --- LICENSE | 34 + NOTICE | 3 + source/controller/CMakeLists.txt | 2 + source/controller/include/map_ctrl_chan_sel.h | 6 +- .../include/map_ctrl_cmdu_handler.h | 15 + source/controller/include/map_ctrl_cmdu_tx.h | 14 +- .../include/map_ctrl_cmdu_validator.h | 19 + source/controller/include/map_ctrl_defines.h | 1 + .../include/map_ctrl_emex_tlv_handler.h | 17 +- .../map_ctrl_post_onboarding_handler.h | 4 + source/controller/include/map_ctrl_sta.h | 14 + .../controller/include/map_ctrl_tlv_helper.h | 6 +- .../controller/include/map_ctrl_tlv_parser.h | 27 + source/controller/include/map_ctrl_utils.h | 90 +- source/controller/include/map_ctrl_vendor.h | 20 + source/controller/src/Makefile.am | 2 + source/controller/src/map_ctrl_chan_sel.c | 593 +- source/controller/src/map_ctrl_cli.c | 268 +- source/controller/src/map_ctrl_cmdu_handler.c | 385 +- source/controller/src/map_ctrl_cmdu_rx.c | 27 + source/controller/src/map_ctrl_cmdu_tx.c | 466 +- .../controller/src/map_ctrl_cmdu_validator.c | 255 +- source/controller/src/map_ctrl_config.c | 22 +- .../src/map_ctrl_emex_tlv_handler.c | 282 +- source/controller/src/map_ctrl_main.c | 30 +- source/controller/src/map_ctrl_nbapi.c | 303 +- .../src/map_ctrl_onboarding_handler.c | 4 +- .../src/map_ctrl_post_onboarding_handler.c | 95 +- source/controller/src/map_ctrl_sta.c | 192 + source/controller/src/map_ctrl_tlv_helper.c | 88 +- source/controller/src/map_ctrl_tlv_parser.c | 832 +- source/controller/src/map_ctrl_utils.c | 566 +- source/controller/src/map_ctrl_vendor.c | 179 + source/controller/src/map_ctrl_wfa_capi.c | 4 +- .../ieee1905/src/al/internal_interfaces/al.h | 2 +- .../al/internal_interfaces/platform_crypto.h | 19 + .../internal_interfaces/platform_interfaces.h | 2 + .../src/al/internal_interfaces/platform_os.h | 17 +- .../src/al/src_independent/al_entity.c | 261 +- .../ieee1905/src/al/src_independent/al_send.c | 283 +- .../src/al/src_independent/al_utils.c | 37 + .../src/al/src_independent/al_utils.h | 16 + .../ieee1905/src/al/src_independent/al_wsc.c | 5 +- .../al/src_independent/extensions/map/i1905.c | 24 +- .../al/src_independent/extensions/map/i1905.h | 39 +- .../src/al/src_linux/platform_crypto.c | 396 + .../src/al/src_linux/platform_interfaces.c | 3 +- .../ieee1905/src/al/src_linux/platform_os.c | 276 +- .../ieee1905/src/common/interfaces/platform.h | 1 + source/ieee1905/src/factory/CMakeLists.txt | 2 + source/ieee1905/src/factory/Makefile.am | 5 +- .../src/factory/interfaces/1905_cmdus.h | 19 + .../src/factory/interfaces/1905_tlvs.h | 16 +- .../interfaces/extensions/map/map_emex_tlvs.h | 9 + .../interfaces/extensions/map/map_tlvs.h | 517 +- .../src/factory/src_independent/1905_cmdus.c | 337 +- .../src/factory/src_independent/1905_tlvs.c | 53 +- .../extensions/map/map_r1_tlvs.c | 231 +- .../extensions/map/map_r2_tlvs.c | 128 +- .../extensions/map/map_r3_tlvs.c | 420 +- .../extensions/map/map_r4_tlvs.c | 130 + .../extensions/map/map_r6_tlvs.c | 985 +++ source/libplatform/include/1905_platform.h | 46 +- source/libplatform/include/acu_utils.h | 10 +- source/libplatform/include/map_80211.h | 4 +- source/libplatform/include/map_channel_set.h | 4 +- .../libplatform/include/map_common_defines.h | 38 +- source/libplatform/include/map_config.h | 25 +- source/libplatform/include/map_data_model.h | 480 +- .../include/map_data_model_dumper.h | 5 + source/libplatform/include/map_info.h | 35 +- .../libplatform/include/map_timer_handler.h | 18 +- source/libplatform/include/map_utils.h | 5 + source/libplatform/src/acu_utils.c | 65 + source/libplatform/src/map_80211.c | 503 +- source/libplatform/src/map_config.c | 819 +- source/libplatform/src/map_data_model.c | 805 +- .../libplatform/src/map_data_model_dumper.c | 171 +- source/libplatform/src/map_dm_rbus.c | 7222 ++++++++++++----- source/libplatform/src/map_info.c | 205 +- source/libplatform/src/map_timer_handler.c | 62 +- source/libplatform/src/map_utils.c | 9 +- source/test/CMakeLists.txt | 112 + source/test/README.md | 63 + source/test/cmake/CodeCoverage.cmake | 200 + source/test/cmake/FindCHECK.cmake | 44 + source/test/cmake/FindJSONC.cmake | 41 + source/test/cmake/FindLIBUBOX.cmake | 41 + source/test/cmake/ValgrindSupport.cmake | 27 + source/test/common/CMakeLists.txt | 59 + source/test/common/CheckUnitTest.cmake | 75 + source/test/common/main.c | 77 + source/test/common/stub/stub_acutils.c | 17 + source/test/common/test.h | 81 + source/test/common/utils.c | 137 + source/test/controller/CMakeLists.txt | 115 + .../data/cmdu_1905_encap_eapol.pcap | Bin 0 -> 100 bytes ...du_6G_320MHz_operating_channel_report.pcap | Bin 0 -> 100 bytes .../data/cmdu_6G_ap_metrics_response.pcap | Bin 0 -> 756 bytes .../cmdu_6G_operating_channel_report.pcap | Bin 0 -> 100 bytes source/test/controller/data/cmdu_ack.pcap | Bin 0 -> 452 bytes .../cmdu_ap_autoconfiguration_search.pcap | Bin 0 -> 126 bytes .../cmdu_ap_autoconfiguration_wsc_2G.pcap | Bin 0 -> 567 bytes .../cmdu_ap_autoconfiguration_wsc_5G.pcap | Bin 0 -> 615 bytes .../cmdu_ap_autoconfiguration_wsc_6G.pcap | Bin 0 -> 591 bytes .../data/cmdu_ap_capability_report.pcap | Bin 0 -> 667 bytes .../data/cmdu_ap_capability_report_no_he.pcap | Bin 0 -> 852 bytes .../data/cmdu_ap_metrics_response.pcap | Bin 0 -> 678 bytes .../cmdu_assoc_sta_link_metrics_response.pcap | Bin 0 -> 516 bytes .../data/cmdu_assoc_status_notification.pcap | Bin 0 -> 452 bytes .../data/cmdu_available_spectrum_inquiry.pcap | Bin 0 -> 5595 bytes .../cmdu_backhaul_sta_capability_report.pcap | Bin 0 -> 100 bytes ..._backhaul_sta_capability_report_slave.pcap | Bin 0 -> 97 bytes .../data/cmdu_backhaul_steering_response.pcap | Bin 0 -> 452 bytes ...cmdu_backhaul_steering_response_error.pcap | Bin 0 -> 452 bytes .../data/cmdu_beacon_metrics_response.pcap | Bin 0 -> 138 bytes .../data/cmdu_channel_preference_report.pcap | Bin 0 -> 487 bytes .../data/cmdu_channel_scan_report.pcap | Bin 0 -> 798 bytes .../data/cmdu_channel_selection_response.pcap | Bin 0 -> 100 bytes .../data/cmdu_chirp_notification.pcap | Bin 0 -> 100 bytes .../data/cmdu_client_capability_report.pcap | Bin 0 -> 321 bytes .../cmdu_client_disassociation_stats.pcap | Bin 0 -> 116 bytes .../data/cmdu_client_steering_btm_report.pcap | Bin 0 -> 100 bytes .../data/cmdu_direct_encap_dpp.pcap | Bin 0 -> 100 bytes .../data/cmdu_early_ap_capability_report.pcap | Bin 0 -> 1066 bytes .../data/cmdu_failed_connection.pcap | Bin 0 -> 100 bytes .../data/cmdu_higher_layer_data.pcap | Bin 0 -> 452 bytes ...du_legacy_ap_autoconfiguration_search.pcap | Bin 0 -> 100 bytes .../data/cmdu_legacy_topology_discovery.pcap | Bin 0 -> 108 bytes .../data/cmdu_legacy_topology_response.pcap | Bin 0 -> 260 bytes .../data/cmdu_link_metric_query.pcap | Bin 0 -> 452 bytes .../data/cmdu_link_metric_response.pcap | Bin 0 -> 311 bytes .../data/cmdu_mlo_ap_capability_report.pcap | Bin 0 -> 1404 bytes .../cmdu_mlo_client_capability_report.pcap | Bin 0 -> 495 bytes ...o_client_capability_report_frag_ml_ie.pcap | Bin 0 -> 561 bytes ..._capability_report_frag_ml_ie_frag_sp.pcap | Bin 0 -> 817 bytes .../data/cmdu_mlo_topology_response.pcap | Bin 0 -> 1044 bytes ...ple_backhaul_topology_response_master.pcap | Bin 0 -> 469 bytes ...iple_backhaul_topology_response_slave.pcap | Bin 0 -> 493 bytes .../data/cmdu_operating_channel_report.pcap | Bin 0 -> 100 bytes .../data/cmdu_proxied_encap_dpp.pcap | Bin 0 -> 101 bytes .../data/cmdu_steering_completed.pcap | Bin 0 -> 452 bytes .../data/cmdu_topology_discovery.pcap | Bin 0 -> 108 bytes .../data/cmdu_topology_discovery_slave.pcap | Bin 0 -> 108 bytes .../cmdu_topology_notification_assoc.pcap | Bin 0 -> 100 bytes ..._topology_notification_bhsta_assoc_5G.pcap | Bin 0 -> 100 bytes ...pology_notification_bhsta_disassoc_5G.pcap | Bin 0 -> 100 bytes ...pology_notification_bhsta_disassoc_6G.pcap | Bin 0 -> 100 bytes .../cmdu_topology_notification_disassoc.pcap | Bin 0 -> 100 bytes .../controller/data/cmdu_topology_query.pcap | Bin 0 -> 100 bytes .../data/cmdu_topology_response.pcap | Bin 0 -> 410 bytes .../cmdu_triband_ap_capability_report.pcap | Bin 0 -> 474 bytes .../data/cmdu_triband_topology_response.pcap | Bin 0 -> 375 bytes ...mdu_unassoc_sta_link_metrics_response.pcap | Bin 0 -> 100 bytes .../data/emex_tlv_0002_feature_profile.pcap | Bin 0 -> 100 bytes .../data/emex_tlv_0003_device_info.pcap | Bin 0 -> 100 bytes .../data/emex_tlv_0004_device_metrics.pcap | Bin 0 -> 106 bytes .../data/emex_tlv_000F_eth_interfaces.pcap | Bin 0 -> 116 bytes .../data/emex_tlv_0010_eth_stats_v2.pcap | Bin 0 -> 234 bytes .../emex_tlv_0011_eth_non1905_nb_devs.pcap | Bin 0 -> 100 bytes .../data/emex_tlv_0012_eth_1905_nb_devs.pcap | Bin 0 -> 100 bytes source/test/controller/data/emex_tlvs.dat | 8 + .../controller/stub/stub_map_ctrl_chan_sel.c | 23 + .../controller/stub/stub_map_ctrl_cmdu_tx.c | 251 + .../controller/stub/stub_map_ctrl_cmdu_tx.h | 57 + .../stub/stub_map_ctrl_emex_tlv_handler.c | 42 + .../stub_map_ctrl_post_onboarding_handler.c | 17 + .../stub/stub_map_ctrl_tlv_helper.c | 51 + .../stub/stub_map_ctrl_topology_tree.c | 48 + .../controller/stub/stub_map_ctrl_utils.c | 17 + .../controller/stub/stub_map_ctrl_vendor.c | 20 + .../controller/stub/stub_map_ctrl_vendor.h | 17 + .../controller/stub/stub_map_ctrl_wfa_capi.c | 12 + source/test/controller/test_chan_sel.c | 1359 ++++ source/test/controller/test_cli.c | 1325 +++ source/test/controller/test_cmdu_rx.c | 2160 +++++ source/test/controller/test_cmdu_tx.c | 1676 ++++ source/test/controller/test_emex.c | 339 + source/test/controller/test_tlv_helper.c | 163 + source/test/controller/test_utils.c | 531 ++ source/test/ieee1905/CMakeLists.txt | 70 + .../data/cmdu_1514_bytes_excl_eom_1_tlv.pcap | Bin 0 -> 1630 bytes .../data/cmdu_1514_bytes_excl_eom_2_tlv.pcap | Bin 0 -> 1630 bytes .../data/cmdu_1514_bytes_incl_eom_1_tlv.pcap | Bin 0 -> 1554 bytes .../data/cmdu_1514_bytes_incl_eom_2_tlv.pcap | Bin 0 -> 1554 bytes source/test/ieee1905/data/cmdu_500_tlv.pcap | Bin 0 -> 4141 bytes .../cmdu_ap_autoconfiguration_search.pcap | Bin 0 -> 96 bytes .../data/cmdu_fragmented_eom_in_all_frag.pcap | Bin 0 -> 5409 bytes .../ieee1905/data/cmdu_fragmented_mixed.pcap | Bin 0 -> 16560 bytes .../data/cmdu_fragmented_ordered.pcap | Bin 0 -> 5397 bytes .../data/cmdu_fragmented_reversed.pcap | Bin 0 -> 5397 bytes .../data/cmdu_fragmented_scrambled.pcap | Bin 0 -> 5397 bytes .../data/cmdu_multiple_fragment_tlv.pcap | Bin 0 -> 12396 bytes .../data/cmdu_topology_discovery.pcap | Bin 0 -> 108 bytes .../ieee1905/data/cmdu_topology_response.pcap | Bin 0 -> 352 bytes .../ieee1905/data/tlv_00_end_of_message.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_01_al_mac_address.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_02_mac_address.pcap | Bin 0 -> 100 bytes .../data/tlv_03_device_information.pcap | Bin 0 -> 141 bytes .../tlv_04_device_bridging_capability.pcap | Bin 0 -> 100 bytes .../tlv_06_non_1905_neighbor_device_list.pcap | Bin 0 -> 100 bytes .../data/tlv_07_neighbor_device_list.pcap | Bin 0 -> 100 bytes .../data/tlv_08_link_metric_query.pcap | Bin 0 -> 100 bytes .../data/tlv_09_transmitter_link_metric.pcap | Bin 0 -> 109 bytes .../data/tlv_0A_receiver_link_metric.pcap | Bin 0 -> 103 bytes .../ieee1905/data/tlv_0B_vendor_specific.pcap | Bin 0 -> 100 bytes .../data/tlv_0C_link_metric_result_code.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_0D_searched_role.pcap | Bin 0 -> 100 bytes .../data/tlv_0E_autoconfig_freq_band.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_0F_supported_role.pcap | Bin 0 -> 100 bytes .../data/tlv_10_supported_freq_band.pcap | Bin 0 -> 100 bytes source/test/ieee1905/data/tlv_11_wsc.pcap | Bin 0 -> 494 bytes ...tlv_12_push_button_event_notification.pcap | Bin 0 -> 100 bytes .../tlv_13_push_button_join_notification.pcap | Bin 0 -> 100 bytes ...tlv_14_generic_phy_device_information.pcap | Bin 0 -> 139 bytes .../data/tlv_15_device_identification.pcap | Bin 0 -> 260 bytes .../ieee1905/data/tlv_16_control_url.pcap | Bin 0 -> 100 bytes source/test/ieee1905/data/tlv_17_ipv4.pcap | Bin 0 -> 100 bytes source/test/ieee1905/data/tlv_18_ipv6.pcap | Bin 0 -> 125 bytes ...tlv_19_generic_phy_event_notification.pcap | Bin 0 -> 100 bytes .../data/tlv_1A_1905_profile_version.pcap | Bin 0 -> 100 bytes .../data/tlv_1B_power_off_interface.pcap | Bin 0 -> 100 bytes ...1C_interface_power_change_information.pcap | Bin 0 -> 100 bytes .../tlv_1D_interface_power_change_status.pcap | Bin 0 -> 100 bytes .../data/tlv_1E_l2_neighbor_device.pcap | Bin 0 -> 100 bytes .../data/tlv_80_supported_service.pcap | Bin 0 -> 100 bytes .../data/tlv_81_searched_service.pcap | Bin 0 -> 100 bytes .../data/tlv_82_ap_radio_identifier.pcap | Bin 0 -> 100 bytes .../data/tlv_83_ap_operational_bss.pcap | Bin 0 -> 137 bytes .../data/tlv_84_associated_clients.pcap | Bin 0 -> 100 bytes .../tlv_85_ap_radio_basic_capabilities.pcap | Bin 0 -> 139 bytes .../data/tlv_86_ap_ht_capabilities.pcap | Bin 0 -> 100 bytes .../data/tlv_87_ap_vht_capabilities.pcap | Bin 0 -> 100 bytes .../data/tlv_88_ap_he_capabilities.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_89_steering_policy.pcap | Bin 0 -> 100 bytes .../data/tlv_89_steering_policy_2.pcap | Bin 0 -> 100 bytes .../data/tlv_8A_metric_reporting_policy.pcap | Bin 0 -> 100 bytes .../data/tlv_8B_channel_preference.pcap | Bin 0 -> 144 bytes .../tlv_8C_radio_operation_restriction.pcap | Bin 0 -> 100 bytes .../data/tlv_8D_transmit_power_limit.pcap | Bin 0 -> 100 bytes .../tlv_8E_channel_selection_response.pcap | Bin 0 -> 100 bytes .../data/tlv_8F_operating_channel_report.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_90_client_info.pcap | Bin 0 -> 100 bytes .../data/tlv_91_client_capability_report.pcap | Bin 0 -> 337 bytes .../data/tlv_92_client_association_event.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_93_ap_metric_query.pcap | Bin 0 -> 100 bytes .../test/ieee1905/data/tlv_94_ap_metrics.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_95_sta_mac_address.pcap | Bin 0 -> 100 bytes .../tlv_96_associated_sta_link_metrics.pcap | Bin 0 -> 100 bytes ...7_unassociated_sta_link_metrics_query.pcap | Bin 0 -> 100 bytes ...nassociated_sta_link_metrics_response.pcap | Bin 0 -> 100 bytes .../data/tlv_99_beacon_metrics_query.pcap | Bin 0 -> 103 bytes .../data/tlv_9A_beacon_metrics_response.pcap | Bin 0 -> 138 bytes .../data/tlv_9B_steering_request.pcap | Bin 0 -> 100 bytes .../data/tlv_9C_steering_btm_report.pcap | Bin 0 -> 100 bytes ...9D_client_association_control_request.pcap | Bin 0 -> 100 bytes .../tlv_9E_backhaul_steering_request.pcap | Bin 0 -> 100 bytes .../tlv_9F_backhaul_steering_response.pcap | Bin 0 -> 100 bytes .../data/tlv_A0_higher_layer_data.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_A1_ap_capability.pcap | Bin 0 -> 100 bytes .../tlv_A2_associated_sta_traffic_stats.pcap | Bin 0 -> 102 bytes .../test/ieee1905/data/tlv_A3_error_code.pcap | Bin 0 -> 100 bytes .../tlv_A4_channel_scan_reporting_policy.pcap | Bin 0 -> 100 bytes .../tlv_A5_channel_scan_capabilities.pcap | Bin 0 -> 109 bytes .../data/tlv_A6_channel_scan_request.pcap | Bin 0 -> 100 bytes .../data/tlv_A6_channel_scan_request_2.pcap | Bin 0 -> 100 bytes .../data/tlv_A7_channel_scan_result.pcap | Bin 0 -> 167 bytes .../tlv_A7_channel_scan_result_malformed.pcap | Bin 0 -> 167 bytes .../test/ieee1905/data/tlv_A8_timestamp.pcap | Bin 0 -> 100 bytes ...tlv_A9_1905_layer_security_capability.pcap | Bin 0 -> 100 bytes .../data/tlv_AA_ap_wifi6_capabilities.pcap | Bin 0 -> 103 bytes source/test/ieee1905/data/tlv_AB_mic.pcap | Bin 0 -> 115 bytes .../data/tlv_AC_encrypted_payload.pcap | Bin 0 -> 108 bytes .../ieee1905/data/tlv_AD_cac_request.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_AE_cac_termination.pcap | Bin 0 -> 100 bytes .../data/tlv_AF_cac_completion_report.pcap | Bin 0 -> 100 bytes ...B0_associated_wifi6_sta_status_report.pcap | Bin 0 -> 100 bytes .../data/tlv_B1_cac_status_report.pcap | Bin 0 -> 409 bytes .../data/tlv_B2_cac_capabilities.pcap | Bin 0 -> 131 bytes .../ieee1905/data/tlv_B3_multiap_profile.pcap | Bin 0 -> 100 bytes .../data/tlv_B4_profile2_ap_capability.pcap | Bin 0 -> 100 bytes .../data/tlv_B5_default_8021q_settings.pcap | Bin 0 -> 100 bytes .../tlv_B6_traffic_separation_policy.pcap | Bin 0 -> 100 bytes .../data/tlv_B7_bss_configuration_report.pcap | Bin 0 -> 140 bytes source/test/ieee1905/data/tlv_B8_bssid.pcap | Bin 0 -> 100 bytes .../data/tlv_BC_profile2_error_code.pcap | Bin 0 -> 100 bytes ...tlv_BE_ap_radio_advanced_capabilities.pcap | Bin 0 -> 100 bytes ...lv_BF_association_status_notification.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_C0_source_info.pcap | Bin 0 -> 100 bytes .../data/tlv_C1_tunneled_message_type.pcap | Bin 0 -> 100 bytes .../test/ieee1905/data/tlv_C2_tunneled.pcap | Bin 0 -> 314 bytes .../tlv_C3_profile2_steering_request.pcap | Bin 0 -> 100 bytes ...lv_C4_unsuccessful_association_policy.pcap | Bin 0 -> 100 bytes .../tlv_C5_metric_collection_interval.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_C6_radio_metrics.pcap | Bin 0 -> 100 bytes .../data/tlv_C7_ap_extended_metrics.pcap | Bin 0 -> 100 bytes ..._associated_sta_extended_link_metrics.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_C9_status_code.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_CA_reason_code.pcap | Bin 0 -> 100 bytes ...lv_CB_backhaul_sta_radio_capabilities.pcap | Bin 0 -> 100 bytes .../data/tlv_CC_akm_suite_capabilities.pcap | Bin 0 -> 110 bytes .../ieee1905/data/tlv_CD_1905_encap_dpp.pcap | Bin 0 -> 100 bytes ...tlv_CD_1905_encap_dpp_no_enrollee_mac.pcap | Bin 0 -> 100 bytes .../data/tlv_CE_1905_encap_eapol.pcap | Bin 0 -> 100 bytes .../tlv_D0_backhaul_bss_configuration.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_D1_dpp_message.pcap | Bin 0 -> 100 bytes .../data/tlv_D2_dpp_cce_indication.pcap | Bin 0 -> 100 bytes .../ieee1905/data/tlv_D3_dpp_chirp_value.pcap | Bin 0 -> 100 bytes ...lv_D3_dpp_chirp_value_no_enrollee_mac.pcap | Bin 0 -> 100 bytes .../data/tlv_D4_device_inventory.pcap | Bin 0 -> 139 bytes .../test/ieee1905/data/tlv_D5_agent_list.pcap | Bin 0 -> 100 bytes .../data/tlv_DD_controller_capability.pcap | Bin 0 -> 100 bytes .../data/tlv_DF_wifi7_agent_capabilities.pcap | Bin 0 -> 261 bytes .../tlv_E0_agent_ap_mld_configuration.pcap | Bin 0 -> 314 bytes ...tlv_E1_backhaul_sta_mld_configuration.pcap | Bin 0 -> 164 bytes ...v_E2_associated_sta_mld_configuration.pcap | Bin 0 -> 193 bytes .../data/tlv_E4_affiliated_sta_metrics.pcap | Bin 0 -> 1092 bytes .../data/tlv_E5_affiliated_ap_metrics.pcap | Bin 0 -> 1098 bytes ...E8_available_spectrum_inquiry_request.pcap | Bin 0 -> 1554 bytes ...9_available_spectrum_inquiry_response.pcap | Bin 0 -> 4272 bytes source/test/ieee1905/data/tlv_FE_unknown.pcap | Bin 0 -> 100 bytes source/test/ieee1905/data/tlvs.dat | 143 + source/test/ieee1905/stub/stub_i1905.c | 205 + source/test/ieee1905/stub/stub_i1905.h | 28 + source/test/ieee1905/stub/stub_platform_os.c | 137 + source/test/ieee1905/stub/stub_platform_os.h | 19 + source/test/ieee1905/test_al_send.c | 335 + source/test/ieee1905/test_al_wsc.c | 179 + source/test/ieee1905/test_cmdus.c | 824 ++ source/test/ieee1905/test_lldp_payload.c | 99 + source/test/ieee1905/test_lldp_tlvs.c | 124 + source/test/ieee1905/test_tlvs.c | 3036 +++++++ source/test/libutils/CMakeLists.txt | 56 + .../test/libutils/stub/stub_map_blocklist.c | 20 + source/test/libutils/stub/stub_map_cli.c | 101 + source/test/libutils/stub/stub_map_cli.h | 24 + source/test/libutils/stub/stub_map_config.c | 43 + source/test/libutils/stub/stub_map_config.h | 18 + .../test/libutils/stub/stub_map_data_model.c | 23 + .../stub/stub_map_dm_eth_device_list.c | 43 + source/test/libutils/stub/stub_map_dm_rbus.c | 25 + .../libutils/stub/stub_map_retry_handler.c | 61 + .../libutils/stub/stub_map_timer_handler.c | 64 + .../libutils/stub/stub_map_topology_tree.c | 52 + source/test/libutils/stub/stub_map_utils.c | 91 + source/test/libutils/test_arraylist.c | 186 + source/test/libutils/test_map_80211.c | 246 + source/test/libutils/test_map_blocklist.c | 104 + source/test/libutils/test_map_channel_set.c | 227 + .../test/libutils/test_map_cli_subscription.c | 99 + source/test/libutils/test_map_datamodel.c | 320 + .../libutils/test_map_dm_eth_device_list.c | 209 + source/test/libutils/test_map_info.c | 386 + source/test/project/CMakeLists.txt | 79 + source/test/run.sh | 109 + source/test/setup.sh | 10 + source/test/suppressions.vg | 67 + 357 files changed, 32745 insertions(+), 3951 deletions(-) create mode 100644 source/controller/include/map_ctrl_sta.h create mode 100644 source/controller/include/map_ctrl_vendor.h create mode 100644 source/controller/src/map_ctrl_sta.c create mode 100644 source/controller/src/map_ctrl_vendor.c create mode 100644 source/ieee1905/src/factory/src_independent/extensions/map/map_r4_tlvs.c create mode 100644 source/ieee1905/src/factory/src_independent/extensions/map/map_r6_tlvs.c create mode 100644 source/test/CMakeLists.txt create mode 100644 source/test/README.md create mode 100644 source/test/cmake/CodeCoverage.cmake create mode 100644 source/test/cmake/FindCHECK.cmake create mode 100644 source/test/cmake/FindJSONC.cmake create mode 100644 source/test/cmake/FindLIBUBOX.cmake create mode 100644 source/test/cmake/ValgrindSupport.cmake create mode 100644 source/test/common/CMakeLists.txt create mode 100644 source/test/common/CheckUnitTest.cmake create mode 100644 source/test/common/main.c create mode 100644 source/test/common/stub/stub_acutils.c create mode 100644 source/test/common/test.h create mode 100644 source/test/common/utils.c create mode 100644 source/test/controller/CMakeLists.txt create mode 100644 source/test/controller/data/cmdu_1905_encap_eapol.pcap create mode 100644 source/test/controller/data/cmdu_6G_320MHz_operating_channel_report.pcap create mode 100644 source/test/controller/data/cmdu_6G_ap_metrics_response.pcap create mode 100644 source/test/controller/data/cmdu_6G_operating_channel_report.pcap create mode 100644 source/test/controller/data/cmdu_ack.pcap create mode 100644 source/test/controller/data/cmdu_ap_autoconfiguration_search.pcap create mode 100644 source/test/controller/data/cmdu_ap_autoconfiguration_wsc_2G.pcap create mode 100644 source/test/controller/data/cmdu_ap_autoconfiguration_wsc_5G.pcap create mode 100644 source/test/controller/data/cmdu_ap_autoconfiguration_wsc_6G.pcap create mode 100644 source/test/controller/data/cmdu_ap_capability_report.pcap create mode 100644 source/test/controller/data/cmdu_ap_capability_report_no_he.pcap create mode 100644 source/test/controller/data/cmdu_ap_metrics_response.pcap create mode 100644 source/test/controller/data/cmdu_assoc_sta_link_metrics_response.pcap create mode 100644 source/test/controller/data/cmdu_assoc_status_notification.pcap create mode 100644 source/test/controller/data/cmdu_available_spectrum_inquiry.pcap create mode 100644 source/test/controller/data/cmdu_backhaul_sta_capability_report.pcap create mode 100644 source/test/controller/data/cmdu_backhaul_sta_capability_report_slave.pcap create mode 100644 source/test/controller/data/cmdu_backhaul_steering_response.pcap create mode 100644 source/test/controller/data/cmdu_backhaul_steering_response_error.pcap create mode 100644 source/test/controller/data/cmdu_beacon_metrics_response.pcap create mode 100644 source/test/controller/data/cmdu_channel_preference_report.pcap create mode 100644 source/test/controller/data/cmdu_channel_scan_report.pcap create mode 100644 source/test/controller/data/cmdu_channel_selection_response.pcap create mode 100644 source/test/controller/data/cmdu_chirp_notification.pcap create mode 100644 source/test/controller/data/cmdu_client_capability_report.pcap create mode 100644 source/test/controller/data/cmdu_client_disassociation_stats.pcap create mode 100644 source/test/controller/data/cmdu_client_steering_btm_report.pcap create mode 100644 source/test/controller/data/cmdu_direct_encap_dpp.pcap create mode 100644 source/test/controller/data/cmdu_early_ap_capability_report.pcap create mode 100644 source/test/controller/data/cmdu_failed_connection.pcap create mode 100644 source/test/controller/data/cmdu_higher_layer_data.pcap create mode 100644 source/test/controller/data/cmdu_legacy_ap_autoconfiguration_search.pcap create mode 100644 source/test/controller/data/cmdu_legacy_topology_discovery.pcap create mode 100644 source/test/controller/data/cmdu_legacy_topology_response.pcap create mode 100644 source/test/controller/data/cmdu_link_metric_query.pcap create mode 100644 source/test/controller/data/cmdu_link_metric_response.pcap create mode 100644 source/test/controller/data/cmdu_mlo_ap_capability_report.pcap create mode 100644 source/test/controller/data/cmdu_mlo_client_capability_report.pcap create mode 100644 source/test/controller/data/cmdu_mlo_client_capability_report_frag_ml_ie.pcap create mode 100644 source/test/controller/data/cmdu_mlo_client_capability_report_frag_ml_ie_frag_sp.pcap create mode 100644 source/test/controller/data/cmdu_mlo_topology_response.pcap create mode 100644 source/test/controller/data/cmdu_multiple_backhaul_topology_response_master.pcap create mode 100644 source/test/controller/data/cmdu_multiple_backhaul_topology_response_slave.pcap create mode 100644 source/test/controller/data/cmdu_operating_channel_report.pcap create mode 100644 source/test/controller/data/cmdu_proxied_encap_dpp.pcap create mode 100644 source/test/controller/data/cmdu_steering_completed.pcap create mode 100644 source/test/controller/data/cmdu_topology_discovery.pcap create mode 100644 source/test/controller/data/cmdu_topology_discovery_slave.pcap create mode 100644 source/test/controller/data/cmdu_topology_notification_assoc.pcap create mode 100644 source/test/controller/data/cmdu_topology_notification_bhsta_assoc_5G.pcap create mode 100644 source/test/controller/data/cmdu_topology_notification_bhsta_disassoc_5G.pcap create mode 100644 source/test/controller/data/cmdu_topology_notification_bhsta_disassoc_6G.pcap create mode 100644 source/test/controller/data/cmdu_topology_notification_disassoc.pcap create mode 100644 source/test/controller/data/cmdu_topology_query.pcap create mode 100644 source/test/controller/data/cmdu_topology_response.pcap create mode 100644 source/test/controller/data/cmdu_triband_ap_capability_report.pcap create mode 100644 source/test/controller/data/cmdu_triband_topology_response.pcap create mode 100644 source/test/controller/data/cmdu_unassoc_sta_link_metrics_response.pcap create mode 100644 source/test/controller/data/emex_tlv_0002_feature_profile.pcap create mode 100644 source/test/controller/data/emex_tlv_0003_device_info.pcap create mode 100644 source/test/controller/data/emex_tlv_0004_device_metrics.pcap create mode 100644 source/test/controller/data/emex_tlv_000F_eth_interfaces.pcap create mode 100644 source/test/controller/data/emex_tlv_0010_eth_stats_v2.pcap create mode 100644 source/test/controller/data/emex_tlv_0011_eth_non1905_nb_devs.pcap create mode 100644 source/test/controller/data/emex_tlv_0012_eth_1905_nb_devs.pcap create mode 100644 source/test/controller/data/emex_tlvs.dat create mode 100644 source/test/controller/stub/stub_map_ctrl_chan_sel.c create mode 100644 source/test/controller/stub/stub_map_ctrl_cmdu_tx.c create mode 100644 source/test/controller/stub/stub_map_ctrl_cmdu_tx.h create mode 100644 source/test/controller/stub/stub_map_ctrl_emex_tlv_handler.c create mode 100644 source/test/controller/stub/stub_map_ctrl_post_onboarding_handler.c create mode 100644 source/test/controller/stub/stub_map_ctrl_tlv_helper.c create mode 100644 source/test/controller/stub/stub_map_ctrl_topology_tree.c create mode 100644 source/test/controller/stub/stub_map_ctrl_utils.c create mode 100644 source/test/controller/stub/stub_map_ctrl_vendor.c create mode 100644 source/test/controller/stub/stub_map_ctrl_vendor.h create mode 100644 source/test/controller/stub/stub_map_ctrl_wfa_capi.c create mode 100644 source/test/controller/test_chan_sel.c create mode 100644 source/test/controller/test_cli.c create mode 100644 source/test/controller/test_cmdu_rx.c create mode 100644 source/test/controller/test_cmdu_tx.c create mode 100644 source/test/controller/test_emex.c create mode 100644 source/test/controller/test_tlv_helper.c create mode 100644 source/test/controller/test_utils.c create mode 100644 source/test/ieee1905/CMakeLists.txt create mode 100644 source/test/ieee1905/data/cmdu_1514_bytes_excl_eom_1_tlv.pcap create mode 100644 source/test/ieee1905/data/cmdu_1514_bytes_excl_eom_2_tlv.pcap create mode 100644 source/test/ieee1905/data/cmdu_1514_bytes_incl_eom_1_tlv.pcap create mode 100644 source/test/ieee1905/data/cmdu_1514_bytes_incl_eom_2_tlv.pcap create mode 100644 source/test/ieee1905/data/cmdu_500_tlv.pcap create mode 100644 source/test/ieee1905/data/cmdu_ap_autoconfiguration_search.pcap create mode 100644 source/test/ieee1905/data/cmdu_fragmented_eom_in_all_frag.pcap create mode 100644 source/test/ieee1905/data/cmdu_fragmented_mixed.pcap create mode 100644 source/test/ieee1905/data/cmdu_fragmented_ordered.pcap create mode 100644 source/test/ieee1905/data/cmdu_fragmented_reversed.pcap create mode 100644 source/test/ieee1905/data/cmdu_fragmented_scrambled.pcap create mode 100644 source/test/ieee1905/data/cmdu_multiple_fragment_tlv.pcap create mode 100644 source/test/ieee1905/data/cmdu_topology_discovery.pcap create mode 100644 source/test/ieee1905/data/cmdu_topology_response.pcap create mode 100644 source/test/ieee1905/data/tlv_00_end_of_message.pcap create mode 100644 source/test/ieee1905/data/tlv_01_al_mac_address.pcap create mode 100644 source/test/ieee1905/data/tlv_02_mac_address.pcap create mode 100644 source/test/ieee1905/data/tlv_03_device_information.pcap create mode 100644 source/test/ieee1905/data/tlv_04_device_bridging_capability.pcap create mode 100644 source/test/ieee1905/data/tlv_06_non_1905_neighbor_device_list.pcap create mode 100644 source/test/ieee1905/data/tlv_07_neighbor_device_list.pcap create mode 100644 source/test/ieee1905/data/tlv_08_link_metric_query.pcap create mode 100644 source/test/ieee1905/data/tlv_09_transmitter_link_metric.pcap create mode 100644 source/test/ieee1905/data/tlv_0A_receiver_link_metric.pcap create mode 100644 source/test/ieee1905/data/tlv_0B_vendor_specific.pcap create mode 100644 source/test/ieee1905/data/tlv_0C_link_metric_result_code.pcap create mode 100644 source/test/ieee1905/data/tlv_0D_searched_role.pcap create mode 100644 source/test/ieee1905/data/tlv_0E_autoconfig_freq_band.pcap create mode 100644 source/test/ieee1905/data/tlv_0F_supported_role.pcap create mode 100644 source/test/ieee1905/data/tlv_10_supported_freq_band.pcap create mode 100644 source/test/ieee1905/data/tlv_11_wsc.pcap create mode 100644 source/test/ieee1905/data/tlv_12_push_button_event_notification.pcap create mode 100644 source/test/ieee1905/data/tlv_13_push_button_join_notification.pcap create mode 100644 source/test/ieee1905/data/tlv_14_generic_phy_device_information.pcap create mode 100644 source/test/ieee1905/data/tlv_15_device_identification.pcap create mode 100644 source/test/ieee1905/data/tlv_16_control_url.pcap create mode 100644 source/test/ieee1905/data/tlv_17_ipv4.pcap create mode 100644 source/test/ieee1905/data/tlv_18_ipv6.pcap create mode 100644 source/test/ieee1905/data/tlv_19_generic_phy_event_notification.pcap create mode 100644 source/test/ieee1905/data/tlv_1A_1905_profile_version.pcap create mode 100644 source/test/ieee1905/data/tlv_1B_power_off_interface.pcap create mode 100644 source/test/ieee1905/data/tlv_1C_interface_power_change_information.pcap create mode 100644 source/test/ieee1905/data/tlv_1D_interface_power_change_status.pcap create mode 100644 source/test/ieee1905/data/tlv_1E_l2_neighbor_device.pcap create mode 100644 source/test/ieee1905/data/tlv_80_supported_service.pcap create mode 100644 source/test/ieee1905/data/tlv_81_searched_service.pcap create mode 100644 source/test/ieee1905/data/tlv_82_ap_radio_identifier.pcap create mode 100644 source/test/ieee1905/data/tlv_83_ap_operational_bss.pcap create mode 100644 source/test/ieee1905/data/tlv_84_associated_clients.pcap create mode 100644 source/test/ieee1905/data/tlv_85_ap_radio_basic_capabilities.pcap create mode 100644 source/test/ieee1905/data/tlv_86_ap_ht_capabilities.pcap create mode 100644 source/test/ieee1905/data/tlv_87_ap_vht_capabilities.pcap create mode 100644 source/test/ieee1905/data/tlv_88_ap_he_capabilities.pcap create mode 100644 source/test/ieee1905/data/tlv_89_steering_policy.pcap create mode 100644 source/test/ieee1905/data/tlv_89_steering_policy_2.pcap create mode 100644 source/test/ieee1905/data/tlv_8A_metric_reporting_policy.pcap create mode 100644 source/test/ieee1905/data/tlv_8B_channel_preference.pcap create mode 100644 source/test/ieee1905/data/tlv_8C_radio_operation_restriction.pcap create mode 100644 source/test/ieee1905/data/tlv_8D_transmit_power_limit.pcap create mode 100644 source/test/ieee1905/data/tlv_8E_channel_selection_response.pcap create mode 100644 source/test/ieee1905/data/tlv_8F_operating_channel_report.pcap create mode 100644 source/test/ieee1905/data/tlv_90_client_info.pcap create mode 100644 source/test/ieee1905/data/tlv_91_client_capability_report.pcap create mode 100644 source/test/ieee1905/data/tlv_92_client_association_event.pcap create mode 100644 source/test/ieee1905/data/tlv_93_ap_metric_query.pcap create mode 100644 source/test/ieee1905/data/tlv_94_ap_metrics.pcap create mode 100644 source/test/ieee1905/data/tlv_95_sta_mac_address.pcap create mode 100644 source/test/ieee1905/data/tlv_96_associated_sta_link_metrics.pcap create mode 100644 source/test/ieee1905/data/tlv_97_unassociated_sta_link_metrics_query.pcap create mode 100644 source/test/ieee1905/data/tlv_98_unassociated_sta_link_metrics_response.pcap create mode 100644 source/test/ieee1905/data/tlv_99_beacon_metrics_query.pcap create mode 100644 source/test/ieee1905/data/tlv_9A_beacon_metrics_response.pcap create mode 100644 source/test/ieee1905/data/tlv_9B_steering_request.pcap create mode 100644 source/test/ieee1905/data/tlv_9C_steering_btm_report.pcap create mode 100644 source/test/ieee1905/data/tlv_9D_client_association_control_request.pcap create mode 100644 source/test/ieee1905/data/tlv_9E_backhaul_steering_request.pcap create mode 100644 source/test/ieee1905/data/tlv_9F_backhaul_steering_response.pcap create mode 100644 source/test/ieee1905/data/tlv_A0_higher_layer_data.pcap create mode 100644 source/test/ieee1905/data/tlv_A1_ap_capability.pcap create mode 100644 source/test/ieee1905/data/tlv_A2_associated_sta_traffic_stats.pcap create mode 100644 source/test/ieee1905/data/tlv_A3_error_code.pcap create mode 100644 source/test/ieee1905/data/tlv_A4_channel_scan_reporting_policy.pcap create mode 100644 source/test/ieee1905/data/tlv_A5_channel_scan_capabilities.pcap create mode 100644 source/test/ieee1905/data/tlv_A6_channel_scan_request.pcap create mode 100644 source/test/ieee1905/data/tlv_A6_channel_scan_request_2.pcap create mode 100644 source/test/ieee1905/data/tlv_A7_channel_scan_result.pcap create mode 100644 source/test/ieee1905/data/tlv_A7_channel_scan_result_malformed.pcap create mode 100644 source/test/ieee1905/data/tlv_A8_timestamp.pcap create mode 100644 source/test/ieee1905/data/tlv_A9_1905_layer_security_capability.pcap create mode 100644 source/test/ieee1905/data/tlv_AA_ap_wifi6_capabilities.pcap create mode 100644 source/test/ieee1905/data/tlv_AB_mic.pcap create mode 100644 source/test/ieee1905/data/tlv_AC_encrypted_payload.pcap create mode 100644 source/test/ieee1905/data/tlv_AD_cac_request.pcap create mode 100644 source/test/ieee1905/data/tlv_AE_cac_termination.pcap create mode 100644 source/test/ieee1905/data/tlv_AF_cac_completion_report.pcap create mode 100644 source/test/ieee1905/data/tlv_B0_associated_wifi6_sta_status_report.pcap create mode 100644 source/test/ieee1905/data/tlv_B1_cac_status_report.pcap create mode 100644 source/test/ieee1905/data/tlv_B2_cac_capabilities.pcap create mode 100644 source/test/ieee1905/data/tlv_B3_multiap_profile.pcap create mode 100644 source/test/ieee1905/data/tlv_B4_profile2_ap_capability.pcap create mode 100644 source/test/ieee1905/data/tlv_B5_default_8021q_settings.pcap create mode 100644 source/test/ieee1905/data/tlv_B6_traffic_separation_policy.pcap create mode 100644 source/test/ieee1905/data/tlv_B7_bss_configuration_report.pcap create mode 100644 source/test/ieee1905/data/tlv_B8_bssid.pcap create mode 100644 source/test/ieee1905/data/tlv_BC_profile2_error_code.pcap create mode 100644 source/test/ieee1905/data/tlv_BE_ap_radio_advanced_capabilities.pcap create mode 100644 source/test/ieee1905/data/tlv_BF_association_status_notification.pcap create mode 100644 source/test/ieee1905/data/tlv_C0_source_info.pcap create mode 100644 source/test/ieee1905/data/tlv_C1_tunneled_message_type.pcap create mode 100644 source/test/ieee1905/data/tlv_C2_tunneled.pcap create mode 100644 source/test/ieee1905/data/tlv_C3_profile2_steering_request.pcap create mode 100644 source/test/ieee1905/data/tlv_C4_unsuccessful_association_policy.pcap create mode 100644 source/test/ieee1905/data/tlv_C5_metric_collection_interval.pcap create mode 100644 source/test/ieee1905/data/tlv_C6_radio_metrics.pcap create mode 100644 source/test/ieee1905/data/tlv_C7_ap_extended_metrics.pcap create mode 100644 source/test/ieee1905/data/tlv_C8_associated_sta_extended_link_metrics.pcap create mode 100644 source/test/ieee1905/data/tlv_C9_status_code.pcap create mode 100644 source/test/ieee1905/data/tlv_CA_reason_code.pcap create mode 100644 source/test/ieee1905/data/tlv_CB_backhaul_sta_radio_capabilities.pcap create mode 100644 source/test/ieee1905/data/tlv_CC_akm_suite_capabilities.pcap create mode 100644 source/test/ieee1905/data/tlv_CD_1905_encap_dpp.pcap create mode 100644 source/test/ieee1905/data/tlv_CD_1905_encap_dpp_no_enrollee_mac.pcap create mode 100644 source/test/ieee1905/data/tlv_CE_1905_encap_eapol.pcap create mode 100644 source/test/ieee1905/data/tlv_D0_backhaul_bss_configuration.pcap create mode 100644 source/test/ieee1905/data/tlv_D1_dpp_message.pcap create mode 100644 source/test/ieee1905/data/tlv_D2_dpp_cce_indication.pcap create mode 100644 source/test/ieee1905/data/tlv_D3_dpp_chirp_value.pcap create mode 100644 source/test/ieee1905/data/tlv_D3_dpp_chirp_value_no_enrollee_mac.pcap create mode 100644 source/test/ieee1905/data/tlv_D4_device_inventory.pcap create mode 100644 source/test/ieee1905/data/tlv_D5_agent_list.pcap create mode 100644 source/test/ieee1905/data/tlv_DD_controller_capability.pcap create mode 100644 source/test/ieee1905/data/tlv_DF_wifi7_agent_capabilities.pcap create mode 100644 source/test/ieee1905/data/tlv_E0_agent_ap_mld_configuration.pcap create mode 100644 source/test/ieee1905/data/tlv_E1_backhaul_sta_mld_configuration.pcap create mode 100644 source/test/ieee1905/data/tlv_E2_associated_sta_mld_configuration.pcap create mode 100644 source/test/ieee1905/data/tlv_E4_affiliated_sta_metrics.pcap create mode 100644 source/test/ieee1905/data/tlv_E5_affiliated_ap_metrics.pcap create mode 100644 source/test/ieee1905/data/tlv_E8_available_spectrum_inquiry_request.pcap create mode 100644 source/test/ieee1905/data/tlv_E9_available_spectrum_inquiry_response.pcap create mode 100644 source/test/ieee1905/data/tlv_FE_unknown.pcap create mode 100644 source/test/ieee1905/data/tlvs.dat create mode 100644 source/test/ieee1905/stub/stub_i1905.c create mode 100644 source/test/ieee1905/stub/stub_i1905.h create mode 100644 source/test/ieee1905/stub/stub_platform_os.c create mode 100644 source/test/ieee1905/stub/stub_platform_os.h create mode 100644 source/test/ieee1905/test_al_send.c create mode 100644 source/test/ieee1905/test_al_wsc.c create mode 100644 source/test/ieee1905/test_cmdus.c create mode 100644 source/test/ieee1905/test_lldp_payload.c create mode 100644 source/test/ieee1905/test_lldp_tlvs.c create mode 100644 source/test/ieee1905/test_tlvs.c create mode 100644 source/test/libutils/CMakeLists.txt create mode 100644 source/test/libutils/stub/stub_map_blocklist.c create mode 100644 source/test/libutils/stub/stub_map_cli.c create mode 100644 source/test/libutils/stub/stub_map_cli.h create mode 100644 source/test/libutils/stub/stub_map_config.c create mode 100644 source/test/libutils/stub/stub_map_config.h create mode 100644 source/test/libutils/stub/stub_map_data_model.c create mode 100644 source/test/libutils/stub/stub_map_dm_eth_device_list.c create mode 100644 source/test/libutils/stub/stub_map_dm_rbus.c create mode 100644 source/test/libutils/stub/stub_map_retry_handler.c create mode 100644 source/test/libutils/stub/stub_map_timer_handler.c create mode 100644 source/test/libutils/stub/stub_map_topology_tree.c create mode 100644 source/test/libutils/stub/stub_map_utils.c create mode 100644 source/test/libutils/test_arraylist.c create mode 100644 source/test/libutils/test_map_80211.c create mode 100644 source/test/libutils/test_map_blocklist.c create mode 100644 source/test/libutils/test_map_channel_set.c create mode 100644 source/test/libutils/test_map_cli_subscription.c create mode 100644 source/test/libutils/test_map_datamodel.c create mode 100644 source/test/libutils/test_map_dm_eth_device_list.c create mode 100644 source/test/libutils/test_map_info.c create mode 100644 source/test/project/CMakeLists.txt create mode 100755 source/test/run.sh create mode 100755 source/test/setup.sh create mode 100644 source/test/suppressions.vg diff --git a/LICENSE b/LICENSE index cf063e5..0239d96 100644 --- a/LICENSE +++ b/LICENSE @@ -80,3 +80,37 @@ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +------------------------------------------------------------------------------- + +BSD-3 License + +Copyright (c) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/NOTICE b/NOTICE index a405697..89fdd10 100644 --- a/NOTICE +++ b/NOTICE @@ -15,3 +15,6 @@ Licensed under the BSD+Patent License Extracts from RFC3526: Copyright (c) The Internet Society (2003). All Rights Reserved. + +Copyright (c) 2012 - 2015, Lars Bilke +Licensed under the BSD-3 License diff --git a/source/controller/CMakeLists.txt b/source/controller/CMakeLists.txt index f1d4d8c..e2628b3 100644 --- a/source/controller/CMakeLists.txt +++ b/source/controller/CMakeLists.txt @@ -21,10 +21,12 @@ set(EXE_SOURCES map_ctrl_nbapi.c map_ctrl_onboarding_handler.c map_ctrl_post_onboarding_handler.c + map_ctrl_sta.c map_ctrl_tlv_helper.c map_ctrl_tlv_parser.c map_ctrl_topology_tree.c map_ctrl_utils.c + map_ctrl_vendor.c map_ctrl_wfa_capi.c ) diff --git a/source/controller/include/map_ctrl_chan_sel.h b/source/controller/include/map_ctrl_chan_sel.h index 70575c2..88b73ab 100644 --- a/source/controller/include/map_ctrl_chan_sel.h +++ b/source/controller/include/map_ctrl_chan_sel.h @@ -11,13 +11,15 @@ #include "i1905.h" /* Update controller preference and send channel selection request */ -int map_ctrl_chan_sel_set(map_radio_info_t *radio, bool *acs_enable, map_channel_set_t *acs_channels, - int *channel, int *bandwidth); +int map_ctrl_chan_sel_set(map_radio_info_t *radio, bool *cloud_mgmt_enable, bool *acs_enable, + map_channel_set_t *acs_channels, int *channel, int *bandwidth); int map_ctrl_chan_sel_set_channel(map_radio_info_t *radio, int channel); int map_ctrl_chan_sel_set_bandwidth(map_radio_info_t *radio, int bandwidth); +int map_ctrl_chan_sel_set_cloud_mgmt_enable(map_radio_info_t *radio, bool enable); + /* Update controller preference but do not send channel selection request To be used when ctl_channels are updated. */ diff --git a/source/controller/include/map_ctrl_cmdu_handler.h b/source/controller/include/map_ctrl_cmdu_handler.h index c3805ed..2acedfb 100644 --- a/source/controller/include/map_ctrl_cmdu_handler.h +++ b/source/controller/include/map_ctrl_cmdu_handler.h @@ -135,4 +135,19 @@ int map_handle_chirp_notification(map_ale_info_t *ale, i1905_cmdu_t *cmdu); /* MAP_R3 17.1.56 (type 0x802a) */ int map_handle_direct_encap_dpp(map_ale_info_t *ale, i1905_cmdu_t *cmdu); +/* MAP_R3 17.1.53 (type 0x802c) */ +int map_handle_bss_configuration_request(map_ale_info_t *ale, i1905_cmdu_t *cmdu); + +/* MAP_R3 17.1.55 (type 0x802e) */ +int map_handle_bss_configuration_result(map_ale_info_t *ale, i1905_cmdu_t *cmdu); + +/*####################################################################### +# MAP R6 CMDU HANDLERS # +########################################################################*/ +/* MAP_R6 17.1.62 (type 0x8043) */ +int map_handle_early_ap_capability_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu); + +/* MAP_R6 17.1.67 (type 0x8049) */ +int map_handle_available_spectrum_inquiry(map_ale_info_t *ale, i1905_cmdu_t *cmdu); + #endif /* MAP_CTRL_CMDU_HANDLER_H_ */ diff --git a/source/controller/include/map_ctrl_cmdu_tx.h b/source/controller/include/map_ctrl_cmdu_tx.h index 2e0eb56..5f1f255 100644 --- a/source/controller/include/map_ctrl_cmdu_tx.h +++ b/source/controller/include/map_ctrl_cmdu_tx.h @@ -136,6 +136,9 @@ int map_send_link_metric_response(map_ale_info_t *ale, uint16_t mid, i1905_trans /* 1905.1 6.3.6 (type 0x0006) - MID must be the same as what was used in the request */ int map_send_link_metric_response_error(map_ale_info_t *ale, uint16_t mid, uint8_t error_code); +/* 1905.1 6.3.7 (type 0x0007) */ +int map_send_autoconfig_search(void); + /* 1905.1 6.3.8 (type 0x0008) */ int map_send_autoconfig_response(i1905_cmdu_t *recv_cmdu, bool ale_is_agent); @@ -143,10 +146,10 @@ int map_send_autoconfig_response(i1905_cmdu_t *recv_cmdu, bool ale_is_agent); int map_send_autoconfig_wsc_m2(map_ale_info_t *ale, map_radio_info_t *radio, i1905_cmdu_t *recv_cmdu, uint16_t *mid); /* 1905.1 6.3.10 (type 0x000A) */ -int map_send_autoconfig_renew(uint8_t freq_band, uint16_t *mid); +int map_send_autoconfig_renew(uint8_t freq_band, uint16_t *mid, bool reset_onboarding); /* 1905.1 6.3.10 (type 0x000A) */ -int map_send_autoconfig_renew_ucast(map_ale_info_t *ale, uint8_t freq_band, uint16_t *mid); +int map_send_autoconfig_renew_ucast(map_ale_info_t *ale, uint8_t freq_band, uint16_t *mid, bool reset_onboarding); /* 1905.1 6.3.13 (type 0x0004) - args is of type map_vendor_specific_t */ int map_send_vendor_specific(void *args, uint16_t *mid); @@ -177,6 +180,7 @@ int map_send_channel_selection_request(void *args, uint16_t *mid); /* MAP_R2 17.1.14 (type 0x8009) - args is of type map_sta_info_t */ int map_send_client_capability_query(void *args, uint16_t *mid); +int map_send_mld_client_capability_query(void *args, uint16_t *mid); /* MAP_R2 17.1.16 (type 0x800B) */ int map_send_ap_metrics_query(map_ale_info_t *ale, mac_addr *bssids, uint8_t bssid_nr, uint16_t *mid); @@ -235,9 +239,15 @@ int map_send_dpp_cce_indication(map_ale_info_t *ale, uint8_t advertise, uint16_t /* MAP_R3 17.1.52 (type 0x802F) */ int map_send_dpp_chirp_notification(map_dpp_chirp_value_tlv_t *chirp_value_tlv_list, int num_chirp_tlv, uint16_t *mid); +/* MAP_R3 17.1.54 (type 0x802D) */ +int map_send_bss_config_response(map_ale_info_t *ale, map_bss_configuration_response_tlv_t *bss_config_resp_tlv, uint16_t *mid); + /* MAP_R3 17.1.56 (type 0x802A) */ int map_send_direct_encap_dpp(map_ale_info_t *ale, map_dpp_message_tlv_t *dpp_message_tlv, uint16_t *mid); +/* MAP_R3 17.1.58 (type 0x8035) */ +int map_send_agent_list_message(map_ale_info_t *ale, uint16_t *mid); + /*####################################################################### # RAW # ########################################################################*/ diff --git a/source/controller/include/map_ctrl_cmdu_validator.h b/source/controller/include/map_ctrl_cmdu_validator.h index 4735c43..bcb504c 100644 --- a/source/controller/include/map_ctrl_cmdu_validator.h +++ b/source/controller/include/map_ctrl_cmdu_validator.h @@ -44,6 +44,9 @@ int map_validate_link_metrics_response(map_ale_info_t *ale, i1905_cmdu_t *cmdu); /* 1905.1 6.3.7 (type 0x0007) */ int map_validate_ap_autoconfig_search(i1905_cmdu_t *cmdu); +/* 1905.1 6.3.8 (type 0x0008) */ +int map_validate_ap_autoconfig_response(i1905_cmdu_t *cmdu); + /* 1905.1 6.3.9 (type 0x0009) */ int map_validate_ap_autoconfig_wsc(i1905_cmdu_t *cmdu); @@ -128,6 +131,22 @@ int map_validate_1905_encap_eapol(map_ale_info_t *ale, i1905_cmdu_t *cmdu); /* MAP_R3 17.1.52 (type 0x802f) */ int map_validate_chirp_notification(map_ale_info_t *ale, i1905_cmdu_t *cmdu); +/* MAP_R3 17.1.53 (type 0x802c) */ +int map_validate_bss_configuration_request(UNUSED map_ale_info_t *ale, i1905_cmdu_t *cmdu); + +/* MAP_R3 17.1.55 (type 0x802e) */ +int map_validate_bss_configuration_result(UNUSED map_ale_info_t *ale, i1905_cmdu_t *cmdu); + /* MAP_R3 17.1.56 (type 0x802a) */ int map_validate_direct_encap_dpp(UNUSED map_ale_info_t *ale, i1905_cmdu_t *cmdu); + +/*####################################################################### +# MAP R6 CMDU VALIDATORS # +########################################################################*/ +/* MAP_R6 17.1.62 (type 0x8043) */ +int map_validate_early_ap_capability_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu); + +/* MAP_R6 17.1.67 (type 0x8049) */ +int map_validate_available_spectrum_inquiry(map_ale_info_t *ale, i1905_cmdu_t *cmdu); + #endif /* MAP_CTRL_CMDU_VALIDATOR_H_ */ diff --git a/source/controller/include/map_ctrl_defines.h b/source/controller/include/map_ctrl_defines.h index 7e4895f..f3b0db5 100644 --- a/source/controller/include/map_ctrl_defines.h +++ b/source/controller/include/map_ctrl_defines.h @@ -27,6 +27,7 @@ #define CONFIG_RENEW_TIMER_ID "CONFIG_RENEW_TIMER" #define TOPOLOGY_STABLE_CHECK_TIMER_ID "TOPOLOGY_STABLE_CHECK_TIMER" #define AUTO_CONFIG_SEARCH_TIMER_ID "AUTO-CONFIG_SEARCH-TIMER" +#define CHAN_SEL_MULTIAP_ALIGN_TIMER_ID "CHAN-SEL-MULTIAP-ALIGN" #define MAX_TOPOLOGY_QUERY_RETRY 5 #define ALE_KEEP_ALIVE_THRESHOLD_IN_SEC 30 diff --git a/source/controller/include/map_ctrl_emex_tlv_handler.h b/source/controller/include/map_ctrl_emex_tlv_handler.h index 7b9e9c6..234fe94 100644 --- a/source/controller/include/map_ctrl_emex_tlv_handler.h +++ b/source/controller/include/map_ctrl_emex_tlv_handler.h @@ -12,21 +12,30 @@ /* Feature IDs reported in Feature Profile */ enum emex_feature_ids { - MAP_EMEX_FEATURE_UNUSED = 0x0000, + MAP_EMEX_FEATURE_UNUSED = 0x0000, MAP_EMEX_FEATURE_DEVICE_METRICS, /* 0x0001 */ MAP_EMEX_FEATURE_IEEE1905_1_14, /* 0x0002 */ MAP_EMEX_FEATURE_DEVICE_INFO, /* 0x0003 */ MAP_EMEX_FEATURE_ETH_STATS, /* 0x0004 */ + MAP_EMEX_FEATURE_REBOOT_RESET = 0x0006, /* 0x0006 */ +}; + +enum emex_reboot_request_action_ids { + MAP_EMEX_REBOOT_ACTION_REBOOT = 0x00, + MAP_EMEX_REBOOT_ACTION_RESET = 0x01, +}; + +enum emex_reboot_request_reset_types { + MAP_EMEX_RESET_SOFT_RESET = 0x00, + MAP_EMEX_RESET_FACTORY_RESET = 0x01, }; map_emex_common_feature_list_t *controller_get_emex_common_feature_list(void); + bool map_emex_agent_is_feature_supported(map_ale_info_t *ale, uint16_t id); bool map_emex_common_is_feature_supported(uint16_t id); - bool map_emex_is_valid_tlv(i1905_vendor_specific_tlv_t* vendor_tlv); int8_t map_emex_parse_tlv(map_ale_info_t* ale, i1905_vendor_specific_tlv_t* vendor_tlv); -int8_t map_emex_get_emex_tlv(map_ale_info_t *ale, uint16_t emex_tlv_type, - i1905_vendor_specific_tlv_t *vendor_specific_tlv); int map_emex_handle_cmdu_pre(map_ale_info_t *ale, i1905_cmdu_t *cmdu); int map_emex_handle_cmdu_post(map_ale_info_t *ale, i1905_cmdu_t *cmdu); diff --git a/source/controller/include/map_ctrl_post_onboarding_handler.h b/source/controller/include/map_ctrl_post_onboarding_handler.h index f2cd853..b1a2457 100644 --- a/source/controller/include/map_ctrl_post_onboarding_handler.h +++ b/source/controller/include/map_ctrl_post_onboarding_handler.h @@ -41,4 +41,8 @@ int map_agent_handle_channel_selection(map_ale_info_t *ale, map_radio_info_t *ra int map_agent_cancel_channel_selection(map_ale_info_t *ale); +int map_agent_start_delayed_channel_selection(map_radio_info_t *radio, uint32_t delay_sec); + +int map_agent_cancel_delayed_channel_selection(map_radio_info_t *radio); + #endif /* MAP_CTRL_POST_ONBOARDING_HANDLER_H_ */ diff --git a/source/controller/include/map_ctrl_sta.h b/source/controller/include/map_ctrl_sta.h new file mode 100644 index 0000000..32b6886 --- /dev/null +++ b/source/controller/include/map_ctrl_sta.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +#ifndef MAP_CTRL_STA_H +#define MAP_CTRL_STA_H + +int map_ctrl_sta_init(void); + +void map_ctrl_sta_fini(void); + +#endif /* MAP_CTRL_STA_H */ diff --git a/source/controller/include/map_ctrl_tlv_helper.h b/source/controller/include/map_ctrl_tlv_helper.h index c47ec72..203e178 100644 --- a/source/controller/include/map_ctrl_tlv_helper.h +++ b/source/controller/include/map_ctrl_tlv_helper.h @@ -32,15 +32,15 @@ i1905_receiver_link_metric_tlv_t *map_get_receiver_link_metric_tlv(mac_addr loca i1905_transmitter_link_metric_tlv_t *map_get_transmitter_link_metric_tlv(mac_addr local_al_mac, map_neighbor_link_metric_t *neighbor_lm); -void map_fill_channel_preference_tlv(map_channel_preference_tlv_t *tlv, map_radio_info_t *radio, uint8_t pref_type); +int map_fill_channel_preference_tlv(map_channel_preference_tlv_t *tlv, map_radio_info_t *radio, uint8_t pref_type); void map_fill_transmit_power_tlv(map_transmit_power_limit_tlv_t *tlv, map_radio_info_t *radio); /* Fill scan request tlv with all 20MHz operating class received in scan capability tlv. Optionally set channels != NULL to add only those. */ -void map_fill_channel_scan_request_tlv(map_channel_scan_request_tlv_t *tlv, map_radio_info_t *radio, - bool fresh_scan, map_channel_set_t *channels); +int map_fill_channel_scan_request_tlv(map_channel_scan_request_tlv_t *tlv, map_radio_info_t *radio, + bool fresh_scan, map_channel_set_t *channels); void map_fill_default_8021q_settings_tlv(map_cfg_t *cfg, map_default_8021q_settings_tlv_t *tlv); diff --git a/source/controller/include/map_ctrl_tlv_parser.h b/source/controller/include/map_ctrl_tlv_parser.h index 9b492f9..d562782 100644 --- a/source/controller/include/map_ctrl_tlv_parser.h +++ b/source/controller/include/map_ctrl_tlv_parser.h @@ -131,6 +131,9 @@ int map_parse_ap_wifi6_cap_tlv(map_ale_info_t *ale, map_ap_wifi6_cap_tlv_t *tlv) /* MAP_R3 17.2.73 */ int map_parse_assoc_wifi6_sta_status_tlv(map_ale_info_t *ale, map_assoc_wifi6_sta_status_tlv_t *tlv); +/* MAP_R3 17.2.75 */ +int map_parse_bss_configuration_report_tlv(map_ale_info_t *ale, map_bss_configuration_report_tlv_t *tlv); + /* MAP_R3 17.2.76 */ int map_parse_device_inventory_tlv(map_ale_info_t *ale, map_device_inventory_tlv_t *tlv); @@ -143,7 +146,31 @@ int map_parse_1905_encap_eapol_tlv(map_ale_info_t *ale, map_1905_encap_eapol_tlv /* MAP_R3 17.2.83 */ int map_parse_dpp_chirp_value_tlv(map_ale_info_t *ale, map_dpp_chirp_value_tlv_t *tlv); +/* MAP_R3 17.2.84 */ +int map_parse_bss_configuration_request_tlv(map_ale_info_t *ale, map_bss_configuration_request_tlv_t *tlv); + /* MAP_R3 17.2.86 */ int map_parse_dpp_message_tlv(map_ale_info_t *ale, map_dpp_message_tlv_t *tlv); +/*####################################################################### +# MAP R6 TLV HANDLERS # +########################################################################*/ +/* MAP_R6 17.2.95 */ +int map_parse_wifi7_agent_capability_tlv(map_ale_info_t *ale, map_wifi7_agent_cap_tlv_t *tlv, bool *ret_changed); + +/* MAP_R6 17.2.96 */ +int map_parse_agent_ap_mld_conf_tlv(map_ale_info_t *ale, map_agent_ap_mld_conf_tlv_t *tlv); + +/* MAP_R6 17.2.97 */ +int map_parse_bsta_mld_conf_tlv(map_ale_info_t *ale, map_bsta_mld_conf_tlv_t *tlv); + +/* MAP_R6 17.2.98 */ +int map_parse_assoc_sta_mld_conf_tlv(map_ale_info_t *ale, map_assoc_sta_mld_conf_tlv_t *tlv); + +/* MAP_R6 17.2.100 */ +int map_parse_aff_sta_metrics_tlv(map_ale_info_t *ale, map_aff_sta_metrics_tlv_t *tlv); + +/* MAP_R6 17.2.103 */ +int map_parse_eht_operations_tlv(map_ale_info_t *ale, map_eht_operations_tlv_t *tlv); + #endif /* MAP_CTRL_TLV_PARSER_H_ */ diff --git a/source/controller/include/map_ctrl_utils.h b/source/controller/include/map_ctrl_utils.h index a4d23a3..b429019 100644 --- a/source/controller/include/map_ctrl_utils.h +++ b/source/controller/include/map_ctrl_utils.h @@ -21,7 +21,7 @@ #include "map_data_model.h" #include "map_config.h" -#define IS_ANY_SUBBAND_CHANNEL_SET(channel_set, channel, opclass) (!map_is_no_subband_channel_set(channel_set, channel, opclass)) +#define IS_ANY_SUBBAND_CHANNEL_SET(channel_set, op_class, channel) (!map_is_no_subband_channel_set(channel_set, op_class, channel)) /** @brief Get controller configuration * @@ -29,6 +29,12 @@ */ map_controller_cfg_t* get_controller_cfg(); +/** @brief Get global allowed bandwidth per band + * + * @return bandwidth + */ +uint16_t map_get_allowed_bandwidth(uint8_t band); + /** @brief Parse client assoc frame * * @param sta: station for which assoc frame needs to be parsed @@ -38,6 +44,24 @@ map_controller_cfg_t* get_controller_cfg(); */ int parse_update_client_capability(map_sta_info_t *sta, uint16_t assoc_frame_len, uint8_t* assoc_frame); +/** @brief Parse client assoc frame for each affiliated STA + * + * @param sta_mld: sta mld for which assoc frame needs to be parsed + * @param assoc_frame_len: length of assoc frame + * @param assoc_frame: assoc frame + * @return 0 success + */ +int parse_update_mld_client_capability(map_sta_mld_info_t *sta_mld, uint16_t assoc_frame_len, uint8_t* assoc_frame); + + +/** @brief Parse stored client assoc frame for each affiliated STA + * + * @param sta_mld: sta mld for which assoc frame needs to be parsed + * @force when true update when affiliated sta already has capabiliteis + * @return 0 success + */ +int parse_update_mld_aff_client_capability(map_sta_mld_info_t *sta_mld, bool force); + /** @brief This function will update the source mac used for CMDU received from the ALE * * @param ale: pointer to ALE node to be updated @@ -110,6 +134,15 @@ uint16_t map_get_freq_bands(map_radio_info_t *radio); */ bool map_is_5g_low_high(map_radio_info_t *radio); +/** + * @brief Return true when radio is ap mld capable + */ +bool map_is_radio_ap_mld_capable(map_ale_info_t *ale, map_radio_info_t *radio); + +/** + * @brief Return true when radio is bsta mld capable + */ +bool map_is_radio_bsta_mld_capable(map_ale_info_t *ale, map_radio_info_t *radio); /** * @brief Guess profile used to configure bss. */ @@ -128,7 +161,12 @@ uint8_t *map_get_wsc_attr(uint8_t *message, uint16_t message_size, uint16_t attr /** * @brief Check if channel is in op class and not its non operable list */ -bool map_is_channel_in_cap_op_class(map_op_class_t *cap_op_class, uint8_t channel); +bool map_is_channel_in_cap_op_class(map_op_class_t *cap_op_class, uint8_t ctl_channel); + +/** + * @brief Check if channel is in 6G 320MHz op class and not its non operable list + */ +bool map_is_channel_in_cap_op_class_6G_320MHz(map_op_class_t *cap_op_class, bool upper, uint8_t ctl_channel); /** * @brief Get channel preference in op_class list @@ -144,7 +182,8 @@ void map_update_radio_channels(map_radio_info_t *radio); * @brief Merge 2 op_class lists into 1 (taking lowest preference for each op_class/channel). */ int map_merge_pref_op_class_list(map_op_class_list_t *merged_list, map_op_class_list_t *cap_list, - map_op_class_list_t *list1, map_op_class_list_t *list2); + map_op_class_list_t *list1, map_op_class_list_t *list2, + map_op_class_list_t *disallowed_list); /** * @brief Optimize op_class list by removing channel lists that contain all channels of an op_class. @@ -152,14 +191,24 @@ int map_merge_pref_op_class_list(map_op_class_list_t *merged_list, map_op_class_ void map_optimize_pref_op_class_list(map_op_class_list_t *list, map_op_class_list_t *cap_list); /** - * @brief Check if none of the subband channels from chan/op_class are present in channels list + * @brief Check if none of the subband channels from op_class/channel are present in channels list */ -bool map_is_no_subband_channel_set(map_channel_set_t *channels, uint8_t chan, uint8_t op_class); +bool map_is_no_subband_channel_set(map_channel_set_t *channels, uint8_t op_class, uint8_t channel); /** - * @brief Check if all of the subband channels from chan/op_class are present in channels list + * @brief Check if none of the subband channels from 6G 320MHz op_class/channel are present in channels list */ -bool map_is_all_subband_channel_set(map_channel_set_t *channels, uint8_t chan, uint8_t op_class); +bool map_is_no_subband_channel_set_6G_320MHz(map_channel_set_t *channels, uint8_t op_class, bool upper, uint8_t channel); + +/** + * @brief Check if all of the subband channels from op_class/channel are present in channels list + */ +bool map_is_all_subband_channel_set(map_channel_set_t *channels, uint8_t op_class, uint8_t channel); + +/** + * @brief Check if all of the subband channels from 6G 320MHz op_class/channel are present in channels list + */ +bool map_is_all_subband_channel_set_6G_320MHz(map_channel_set_t *channels, uint8_t op_class, bool upper, uint8_t channel); /** * @brief Sort op_classes and channels in op_class list. @@ -176,6 +225,11 @@ bool map_is_cac_request_valid(map_radio_info_t *radio, uint8_t cac_method, uint8 */ map_local_iface_t *map_find_local_iface(map_ale_info_t *ale, mac_addr mac); +/** + * @brief Check if radio is bsta capable + */ +bool map_is_radio_bsta_capable(map_ale_info_t *ale, mac_addr radio_id); + /** * @brief Find backhaul sta iface of ale based on mac */ @@ -187,13 +241,33 @@ map_backhaul_sta_iface_t *map_find_bhsta_iface_from_ale(map_ale_info_t *ale, mac map_backhaul_sta_iface_t *map_find_bhsta_iface_gbl(mac_addr sta_mac, map_ale_info_t **ret_ale); /** - * @brief Delete ht/vht/he/wifi6 capability tlv + * @brief Delete ht/vht/he/wifi6 capabilities */ void map_free_ht_vht_he_wifi6_caps(map_radio_info_t *radio); +/** + * @brief Delete wifi7 capability + */ +void map_free_wifi7_caps(map_radio_info_t *radio); + /** * @brief Update global radio caps from ht/vht/he capability tlv */ void map_update_radio_caps(map_radio_info_t *radio); +/** + * @brief Return true when bssid must be an AP_MLD or sta_mac is a STA_MLD + */ +bool map_is_non_bss_ap_mld_or_sta_mld(map_ale_info_t *ale, mac_addr sta_mac, mac_addr bssid); + +/** + * @brief Return affiliated sta operating on band + */ +map_sta_info_t *map_get_aff_sta_from_band(map_sta_mld_info_t *sta_mld, uint8_t band); + +/** + * @brief Return first affiliated sta + */ +map_sta_info_t *map_get_aff_sta_first(map_sta_mld_info_t *sta_mld); + #endif /* MAP_CTRL_UTILS_H_ */ diff --git a/source/controller/include/map_ctrl_vendor.h b/source/controller/include/map_ctrl_vendor.h new file mode 100644 index 0000000..966ccc1 --- /dev/null +++ b/source/controller/include/map_ctrl_vendor.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019-2022 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +#ifndef MAP_CTRL_VENDOR_H +#define MAP_CTRL_VENDOR_H + +#include "map_info.h" +#include "map_config.h" +#include "map_ctrl_cmdu_tx.h" + +int map_ctrl_vendor_send_message(map_ale_info_t *ale, map_vendor_tlv_tuple_t tlvs[], + uint8_t tlvs_cnt, uint16_t *mid); +int map_ctrl_vendor_send_reboot_request(map_ale_info_t *ale, uint8_t action_type, uint8_t reset_type); +void map_ctrl_vendor_fini(void); +int map_ctrl_vendor_init(void); + +#endif /* MAP_CTRL_VENDOR_H */ diff --git a/source/controller/src/Makefile.am b/source/controller/src/Makefile.am index 0f08caf..9a8c63e 100644 --- a/source/controller/src/Makefile.am +++ b/source/controller/src/Makefile.am @@ -34,9 +34,11 @@ libcontroller_a_SOURCES = map_ctrl_chan_sel.c \ map_ctrl_nbapi.c \ map_ctrl_onboarding_handler.c \ map_ctrl_post_onboarding_handler.c \ + map_ctrl_sta.c \ map_ctrl_tlv_helper.c \ map_ctrl_tlv_parser.c \ map_ctrl_topology_tree.c \ map_ctrl_utils.c \ + map_ctrl_vendor.c \ map_ctrl_wfa_capi.c diff --git a/source/controller/src/map_ctrl_chan_sel.c b/source/controller/src/map_ctrl_chan_sel.c index 83438d9..fb94c40 100644 --- a/source/controller/src/map_ctrl_chan_sel.c +++ b/source/controller/src/map_ctrl_chan_sel.c @@ -20,6 +20,7 @@ #include "map_ctrl_chan_sel.h" #include "map_ctrl_utils.h" #include "map_ctrl_post_onboarding_handler.h" +#include "map_ctrl_defines.h" /*####################################################################### # TYPEDEFS # @@ -38,11 +39,23 @@ typedef struct { chan_sel_multiap_band_t band_unknown; } chan_sel_multiap_t; +/*####################################################################### +# PROTOTYPES # +########################################################################*/ +static void multiap_radio_remove_cb(map_radio_info_t *radio); + +static int set_def_pref_channels(map_radio_info_t *radio); +static int set_pref_channels(map_radio_info_t *radio); + /*####################################################################### # GLOBALS # ########################################################################*/ static chan_sel_multiap_t g_chan_sel_multiap; +static map_dm_cbs_t g_dm_cbs = { + .radio_remove_cb = multiap_radio_remove_cb +}; + /*####################################################################### # HELP FUNCTIONS # ########################################################################*/ @@ -96,6 +109,20 @@ static void print_op_class_list(map_printf_cb_t print_cb, map_op_class_list_t *l } } +static void set_added_removed_channels(map_channel_set_t *added_channels, map_channel_set_t *removed_channels, + map_channel_set_t *prev_channels, map_channel_set_t *channels) +{ + if (added_channels) { + map_cs_copy(added_channels, channels); + map_cs_and_not(added_channels, prev_channels); + } + + if (removed_channels) { + map_cs_copy(removed_channels, prev_channels); + map_cs_and_not(removed_channels, channels); + } +} + static bool is_backhaul_radio(map_radio_info_t *radio) { return map_radio_has_profile_with_bss_state(radio, MAP_BACKHAUL_BSS); @@ -104,6 +131,11 @@ static bool is_backhaul_radio(map_radio_info_t *radio) /*####################################################################### # MULTIAP CHANNEL SELECTION # ########################################################################*/ +static void get_multiap_timer_id(timer_id_t timer_id, uint8_t band) +{ + snprintf(timer_id, sizeof(timer_id_t), "%s_%s", CHAN_SEL_MULTIAP_ALIGN_TIMER_ID, map_get_freq_band_str(band)); +} + static chan_sel_multiap_band_t *get_multiap_band(uint8_t band) { switch (band) { @@ -119,11 +151,336 @@ static void print_multiap_band(map_printf_cb_t print_cb, uint8_t band, char *ind { chan_sel_multiap_band_t *b = get_multiap_band(band); char buf[MAP_CS_BUF_LEN]; + timer_id_t timer_id; + uint32_t align_backoff = 0; + + get_multiap_timer_id(timer_id, band); + if (map_is_timer_registered(timer_id)) { + map_timer_remaining(timer_id, &align_backoff); + } print_cb("%sctl_channels : [%s]\n", indent, map_cs_to_string(&b->ctl_channels, ',', buf, sizeof(buf))); print_cb("%salign_ctl_channels: [%s]\n", indent, map_cs_to_string(&b->align_ctl_channels, ',', buf, sizeof(buf))); print_cb("%slpr_channels : [%s]\n", indent, map_cs_to_string(&b->bad_channels, ',', buf, sizeof(buf))); print_cb("%salign_lpr_channels: [%s]\n", indent, map_cs_to_string(&b->align_bad_channels, ',', buf, sizeof(buf))); + print_cb("%salign_backoff : %"PRIu32"s\n", indent, align_backoff); +} + +/* Remove globally unsupported and bad channels from channel set. + + Keep original if resulting channel set would be empty +*/ +static int multiap_align_channels(map_channel_set_t *channels, uint8_t band) +{ + chan_sel_multiap_band_t *multiap_band = get_multiap_band(band); + map_channel_set_t *align_ctl_channels = &multiap_band->align_ctl_channels; + map_channel_set_t *align_bad_channels = &multiap_band->align_bad_channels; + map_channel_set_t channels_copy; + + /* Remove not supported channels */ + map_cs_copy(&channels_copy, channels); + + map_cs_and(&channels_copy, align_ctl_channels); + + if (map_cs_nr(&channels_copy) > 0) { + map_cs_copy(channels, &channels_copy); + } + + /* Remove bad channels */ + if (map_cs_nr(align_bad_channels) > 0) { + map_cs_and_not(&channels_copy, align_bad_channels); + + if (map_cs_nr(&channels_copy) > 0) { + map_cs_copy(channels, &channels_copy); + } + } + + return 0; +} + +/* Test if multiap alignment would result in a different preference for + other radios than the one that is being updated + If yes, do device update and trigger channel selection +*/ +static int multiap_test_align(map_radio_info_t *updated_radio, uint8_t band) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + map_dm_foreach_agent_ale(ale) { + map_dm_foreach_radio(ale, radio) { + map_radio_chan_sel_t *chan_sel = &radio->chan_sel; + map_channel_set_t def_pref_channels_copy; + map_channel_set_t pref_channels_copy; + + /* Skip if: + - radio is the one that is being updated + - radio is other band + - radio did not yet receive a preference report + - radio is no backhaul radio + - radio has fixed channel + */ + if ((updated_radio && radio == updated_radio) || + radio->supported_freq != band || + !is_radio_state_channel_pref_report_received(radio->state) || + !is_backhaul_radio(radio) || + !chan_sel->acs_enable) { + continue; + } + + /* Test if (default) preference would change. This will modify radio object + but that is ok as on any change map_ctrl_chan_sel_update is called to + do complete update. + */ + map_cs_copy(&def_pref_channels_copy, &chan_sel->def_pref_channels); + map_cs_copy(&pref_channels_copy, &chan_sel->pref_channels); + + set_def_pref_channels(radio); + set_pref_channels(radio); + + /* Depending on what changed, do update and channel selection */ + if (map_cs_compare(&pref_channels_copy, &chan_sel->pref_channels)) { + /* Update and send channel selection request */ + log_ctrl_i("[multiap_align] preferred channels changed for ale[%s] radio[%s] band[%s] -> update and select", + ale->al_mac_str, radio->radio_id_str, map_get_freq_band_str(band)); + + map_ctrl_chan_sel_update(radio); + map_agent_cancel_channel_selection(radio->ale); + map_agent_handle_channel_selection(radio->ale, radio, MAP_CHAN_SEL_REQUEST); + } else if (map_cs_compare(&def_pref_channels_copy, &chan_sel->def_pref_channels)) { + /* Update only */ + log_ctrl_i("[multiap_align] default preferred channels changed for ale[%s] radio[%s] band[%s] -> update", + ale->al_mac_str, radio->radio_id_str, map_get_freq_band_str(band)); + + map_ctrl_chan_sel_update(radio); + } + } + } + + return 0; +} + +static uint8_t multiap_timer_cb(UNUSED char *timer_id, void *arg) +{ + uint8_t band = (uintptr_t)arg; + chan_sel_multiap_band_t *b = get_multiap_band(band); + map_channel_set_t added_ctl_channels; + map_channel_set_t removed_bad_channels; + char cs_buf1[MAP_CS_BUF_LEN]; + char cs_buf2[MAP_CS_BUF_LEN]; + + /* Sync align channel sets for this band */ + set_added_removed_channels(&added_ctl_channels, NULL, &b->align_ctl_channels, &b->ctl_channels); + set_added_removed_channels(NULL, &removed_bad_channels, &b->align_bad_channels, &b->bad_channels); + + log_ctrl_i("[multiap align] timer expired for band[%s] remove channel restriction ctl_channels[%s] bad_channels[%s]", + map_get_freq_band_str(band), + map_cs_to_string(&added_ctl_channels, ',', cs_buf1, sizeof(cs_buf1)), + map_cs_to_string(&removed_bad_channels, ',', cs_buf2, sizeof(cs_buf2))); + + map_cs_copy(&b->align_ctl_channels, &b->ctl_channels); + map_cs_copy(&b->align_bad_channels, &b->bad_channels); + + /* Check if any radio needs to be updated and needs channel selection */ + if ((map_cs_nr(&added_ctl_channels) > 0) || (map_cs_nr(&removed_bad_channels) > 0)) { + multiap_test_align(NULL, band); + } + + return 1; /* remove timer */ +} + +/* Modify channel sets for 5G radio that: + - only support the low or high channels + - is bandlocked + + ctl_channels: set all channels in the not supported band so they do + not affect the global ctl_channels + + bad_channels: unset all channels in the not supported band so they do + not affect the global bad_channels +*/ +static void check_5g_low_high_bandlocked(map_radio_info_t *radio, map_channel_set_t *ctl_channels, + map_channel_set_t *bad_channels) +{ + map_channel_set_t other_subband_ctl_channels; + uint16_t bands = map_get_freq_bands(radio); + bool band_5g_low = false; + bool band_5g_high = false; + + if (bands == MAP_M2_BSS_RADIO5GL) { + band_5g_low = true; + } else if (bands == MAP_M2_BSS_RADIO5GU) { + band_5g_high = true; + } else if (bands == (MAP_M2_BSS_RADIO5GL | MAP_M2_BSS_RADIO5GU)) { + bandlock_5g_t bandlock_5g = get_controller_cfg()->chan_sel.bandlock_5g; + + if (bandlock_5g == MAP_BANDLOCK_5G_LOW) { + band_5g_low = true; + } else if (bandlock_5g == MAP_BANDLOCK_5G_HIGH) { + band_5g_high = true; + } + } + + if (band_5g_low || band_5g_high) { + /* Get ctl channel set of the not supported subband */ + if (band_5g_low) { + map_get_5G_high_ctl_channel_set(&other_subband_ctl_channels); + } else { + map_get_5G_low_ctl_channel_set(&other_subband_ctl_channels); + } + + /* ctl_channels: set all channels in the not supported subband */ + map_cs_or(ctl_channels, &other_subband_ctl_channels); + + /* bad_channels: unset all channels in the not supported subband */ + map_cs_and_not(bad_channels, &other_subband_ctl_channels); + } +} + +/* Perform multiap channel selection */ +static int multiap_update(map_radio_info_t *updated_radio, bool remove) +{ + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; + map_ale_info_t *ale; + map_radio_info_t *radio; + uint8_t band = updated_radio->supported_freq; + chan_sel_multiap_band_t *multiap_band = get_multiap_band(band); + map_channel_set_t *prev_ctl_channels = &multiap_band->ctl_channels; + map_channel_set_t *prev_bad_channels = &multiap_band->bad_channels; + map_channel_set_t *align_ctl_channels = &multiap_band->align_ctl_channels; + map_channel_set_t *align_bad_channels = &multiap_band->align_bad_channels; + map_channel_set_t ctl_channels; + map_channel_set_t bad_channels; + map_channel_set_t added_ctl_channels; + map_channel_set_t removed_ctl_channels; + map_channel_set_t added_bad_channels; + map_channel_set_t removed_bad_channels; + timer_id_t timer_id; + char cs_buf1[MAP_CS_BUF_LEN]; + char cs_buf2[MAP_CS_BUF_LEN]; + + /* Derive common set of allowed ctl and bad channels. + - ctl_channels: start from all and "AND" with each radio + use cap_ctl_channels which are not affected by configuration + - bad_channels: start from empty and "OR" with each radio + */ + map_get_ctl_channel_set(&ctl_channels, band); + map_cs_unset_all(&bad_channels); + + map_dm_foreach_agent_ale(ale) { + map_dm_foreach_radio(ale, radio) { + map_channel_set_t cap_ctl_channels_copy; + map_channel_set_t bad_channels_copy; + + /* Skip when: + - radio is being removed + - radio is other band + */ + if ((remove && radio == updated_radio) || + radio->supported_freq != band) { + continue; + } + + map_cs_copy(&cap_ctl_channels_copy, &radio->cap_ctl_channels); + map_cs_copy(&bad_channels_copy, &radio->bad_channels); + + /* For dual 5G band and single 5G band with bandlock + only the allowed subband must be considered + */ + if (band == BAND_5G) { + check_5g_low_high_bandlocked(radio, &cap_ctl_channels_copy, &bad_channels_copy); + } + + map_cs_and(&ctl_channels, &cap_ctl_channels_copy); + map_cs_or(&bad_channels, &bad_channels_copy); + } + } + + /* Check if ctl or bad channels changed. */ + if (!map_cs_compare(prev_ctl_channels, &ctl_channels) && !map_cs_compare(prev_bad_channels, &bad_channels)) { + /* Nothing to do... */ + return 0; + } + + /* Check if channels are added and/or removed */ + set_added_removed_channels(&added_ctl_channels, &removed_ctl_channels, prev_ctl_channels, &ctl_channels); + set_added_removed_channels(&added_bad_channels, &removed_bad_channels, prev_bad_channels, &bad_channels); + + /* Update last channel sets */ + map_cs_copy(prev_ctl_channels, &ctl_channels); + map_cs_copy(prev_bad_channels, &bad_channels); + + /* Two types of actions are possible: + - When there is a new restriction (removed ctl channels or added bad channels), + align_channels is updated and channel selection of all radios in this band + is checked. + - When a restriction is removed (added ctl channels or removed bad channels), + a long timer is started. When that expires, align_channels is updated and + channel selection of all radios in this band is checked. + + This is done to avoid toggling channel preferences. + */ + if ((map_cs_nr(&removed_ctl_channels) > 0) || (map_cs_nr(&added_bad_channels) > 0)) { + log_ctrl_i("[multiap_align] add channel restriction ale[%s] radio[%s] band[%s] ctl_channels[%s] bad_channels[%s]", + updated_radio->ale->al_mac_str, updated_radio->radio_id_str, + map_get_freq_band_str(band), + map_cs_to_string(&removed_ctl_channels, ',', cs_buf1, sizeof(cs_buf1)), + map_cs_to_string(&added_bad_channels, ',', cs_buf2, sizeof(cs_buf2))); + + map_cs_and_not(align_ctl_channels, &removed_ctl_channels); + map_cs_or(align_bad_channels, &added_bad_channels); + + multiap_test_align(updated_radio, band); + } + + if ((map_cs_nr(&added_ctl_channels) > 0) || (map_cs_nr(&removed_bad_channels) > 0)) { + log_ctrl_i("[multiap_align] remove channel restriction ale[%s] radio[%s] band[%s] ctl_channels[%s] bad_channels[%s]", + updated_radio->ale->al_mac_str, updated_radio->radio_id_str, + map_get_freq_band_str(band), + map_cs_to_string(&added_ctl_channels, ',', cs_buf1, sizeof(cs_buf1)), + map_cs_to_string(&removed_bad_channels, ',', cs_buf2, sizeof(cs_buf2))); + } + + /* Start or restart timer when aligned set if not the same as latest set + NOTE: Currently there is only one timer per band. It is (re)started when + a restriction is removed and restarted when a restriction is added. + The result is that the state must be "stable" for the complete + timer period before restricted channels are allowed again + */ + get_multiap_timer_id(timer_id, band); + if (map_cs_compare(align_ctl_channels, &ctl_channels) || map_cs_compare(align_bad_channels, &bad_channels)) { + /* Timer needs to run: start or restart */ + if (map_is_timer_registered(timer_id)) { + log_ctrl_i("[multiap_align] restarting timer for band[%s]", map_get_freq_band_str(band)); + + if (map_timer_restart_callback(timer_id)) { + log_ctrl_e("failed restarting timer[%s]", timer_id); + } + } else { + uint32_t backoff_time = cfg->align_multiap_backoff_time; + + log_ctrl_i("[multiap_align] starting timer for band[%s] backoff[%"PRIu32"]", + map_get_freq_band_str(band), backoff_time); + + if (map_timer_register_callback(backoff_time, timer_id, (void*)(uintptr_t)band, multiap_timer_cb)) { + log_ctrl_e("failed registering timer[%s]", timer_id); + } + } + } else { + /* Timer does not need to run */ + if (map_is_timer_registered(timer_id)) { + log_ctrl_i("[multiap_align] stopping timer for band[%s]", map_get_freq_band_str(band)); + + map_timer_unregister_callback(timer_id); + } + } + + return 0; +} + +static void multiap_radio_remove_cb(map_radio_info_t *radio) +{ + multiap_update(radio, true); } /*####################################################################### @@ -217,12 +574,17 @@ static int set_def_pref_channels(map_radio_info_t *radio) } } + /* Do multiap alignment */ + if (cfg->align_multiap && is_backhaul_radio(radio)) { + multiap_align_channels(channels, radio->supported_freq); + } + return 0; } /* Set control channel channel list (aka acs list). - - Start from what has been configured (by cloud). + - Start from what has been configured (by cloud) when cloud_mgmt_enable is true - Remove all control channels that are not supported (in ap capability report). - Remove all bad channels - If enabled, remove channels that cannot be used because of multiap alignment @@ -234,10 +596,16 @@ static int set_def_pref_channels(map_radio_info_t *radio) */ static int set_pref_channels(map_radio_info_t *radio) { + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; map_radio_chan_sel_t *chan_sel = &radio->chan_sel; map_channel_set_t *channels = &radio->chan_sel.pref_channels; - map_cs_copy(channels, &chan_sel->acs_channels); + /* When cloud mgmt is enabled, start from channels set by cloud */ + if (chan_sel->cloud_mgmt_enable) { + map_cs_copy(channels, &chan_sel->acs_channels); + } else { + map_cs_copy(channels, &chan_sel->def_pref_channels); + } /* Remove not supported channels */ map_cs_and(channels, &radio->ctl_channels); @@ -245,6 +613,11 @@ static int set_pref_channels(map_radio_info_t *radio) /* Remove bad channels */ map_cs_and_not(channels, &radio->bad_channels); + /* Do multiap alignment */ + if ((map_cs_nr(channels) > 0) && cfg->align_multiap && is_backhaul_radio(radio)) { + multiap_align_channels(channels, radio->supported_freq); + } + /* If empty -> revert to default preferred channel list */ if (map_cs_nr(channels) == 0) { map_cs_copy(channels, &chan_sel->def_pref_channels); @@ -376,13 +749,13 @@ static int set_eu_weatherband_preference(map_radio_info_t *radio) */ static int set_controller_pref_op_class_list(map_radio_info_t *radio, map_channel_set_t *channels) { - map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; - bool is_6g_psc = (radio->supported_freq == BAND_6G) && cfg->allowed_channel_6g_psc; - uint16_t bandwidth = radio->chan_sel.bandwidth; - uint16_t global_bandwidth = 0; + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; + bool is_6g_psc = (radio->supported_freq == BAND_6G) && cfg->allowed_channel_6g_psc; + uint16_t allowed_bandwidth = map_get_allowed_bandwidth(radio->supported_freq); + uint16_t bandwidth = radio->chan_sel.bandwidth; + int pref_idx = 0; + int extra_alloc_nr = radio->cac_caps.has_eu_weatherband ? map_get_5G_weatherband_op_class_nr() : 0; int i; - int pref_idx = 0; - int extra_alloc_nr = radio->cac_caps.has_eu_weatherband ? map_get_5G_weatherband_op_class_nr() : 0; /* Max number of operation classes is radio->cap_op_class_count + extra_alloc_nr ( + 1 to avoid malloc(0) )*/ int alloc_nr = radio->cap_op_class_list.op_classes_nr + extra_alloc_nr; @@ -394,27 +767,18 @@ static int set_controller_pref_op_class_list(map_radio_info_t *radio, map_channe } radio->ctrl_pref_op_class_list.op_classes = new_list; - /* Get global max bandwidth */ - switch(radio->supported_freq) { - case BAND_2G: global_bandwidth = cfg->allowed_bandwidth_2g; break; - case BAND_5G: global_bandwidth = cfg->allowed_bandwidth_5g; break; - case BAND_6G: global_bandwidth = cfg->allowed_bandwidth_6g; break; - default: global_bandwidth = 0; break; /* Should not happen */ - } - /* Implementation version 1: - Base on list of oper classes from AP Basic capability TLV - Make all channels not in channels not operable */ for (i = 0; i < radio->cap_op_class_list.op_classes_nr; i++) { - map_op_class_t *cap_op_class = &radio->cap_op_class_list.op_classes[i]; - map_op_class_t *pref_op_class = &radio->ctrl_pref_op_class_list.op_classes[pref_idx]; + map_op_class_t *cap_op_class = &radio->cap_op_class_list.op_classes[i]; + map_op_class_t *pref_op_class = &radio->ctrl_pref_op_class_list.op_classes[pref_idx]; + uint8_t op_class = cap_op_class->op_class; + bool op_class_added = false; map_channel_set_t ch_set; - bool add_op_class = false; - bool add_all_chan = false; bool is_center_chan; - uint8_t op_class = cap_op_class->op_class; uint8_t chan; uint8_t center_chan; uint16_t bw; @@ -429,90 +793,82 @@ static int set_controller_pref_op_class_list(map_radio_info_t *radio, map_channe continue; } - /* Add opclass with empty channel list if bw is too high */ - if ((global_bandwidth > 0 && bw > global_bandwidth) || - (bandwidth > 0 && bw > bandwidth)) { - add_op_class = true; - add_all_chan = true; +#define ADD_OP_CLASS \ + if (!op_class_added) { \ + memset(pref_op_class, 0, sizeof(map_op_class_t)); \ + pref_op_class->op_class = op_class; \ + pref_op_class->pref = 0; /* Unoperable */ \ + pref_op_class->reason = 0; /* Unspecified */ \ + op_class_added = true; \ + pref_idx++; \ } - map_cs_foreach(&ch_set, chan) { - if (!map_is_channel_in_cap_op_class(cap_op_class, chan)) { - continue; - } +#define ADD_CHANNEL(channel) \ + ADD_OP_CLASS \ + map_cs_set(&pref_op_class->channels, channel); - if (bw != 20) { - /* check if ALL subband channels are allowed (according to radio allowed list) - exception: when 6G psc channels have been configured subbands are allowed - */ - if (!is_6g_psc && !map_is_all_subband_channel_set(&radio->ctl_channels, chan, op_class)) { - add_op_class = true; - break; - } - } - - /* For 20 and 40MHz (2G and 5G), check "this" channel is allowed, - For 40MHz (6G), 80, 160 and 320MHz, check if any subband channel is allowed. - */ - if (!is_center_chan) { - if (!map_cs_is_set(channels, chan)) { - add_op_class = true; - break; - } - } else { - if (map_is_no_subband_channel_set(channels, chan, op_class)) { - add_op_class = true; - break; - } - } + /* Unconditionally reject 80 + 80 operating classes */ + if (map_is_80p80_op_class(op_class)) { + /* Add op_class with 0 channels */ + ADD_OP_CLASS + continue; } - if (!add_op_class) { + /* Add opclass with empty channel list if bw is too high */ + if ((allowed_bandwidth > 0 && bw > allowed_bandwidth) || + (bandwidth > 0 && bw > bandwidth)) { + /* Add op_class with 0 channels */ + ADD_OP_CLASS continue; } - memset(pref_op_class, 0, sizeof(map_op_class_t)); - pref_op_class->op_class = op_class; - pref_op_class->pref = 0; /* Unoperable */ - pref_op_class->reason = 0; /* Unspecified */ - - /* Loop again and add channels... */ + /* Loop over all channels in op_class */ map_cs_foreach(&ch_set, chan) { - /* Checks also if channel is in non operable list */ - if (!map_is_channel_in_cap_op_class(cap_op_class, chan)) { - continue; - } - - if (add_all_chan) { - /* channel count remains zero */ - continue; - } + if (!map_is_6G_320MHz_op_class(op_class)) { + /* Checks also if channel is in non operable list */ + if (!map_is_channel_in_cap_op_class(cap_op_class, chan)) { + continue; + } - /* Add channel */ - if (!is_center_chan) { - if (!map_cs_is_set(channels, chan)) { - /* block because primary channel is not set */ - map_cs_set(&pref_op_class->channels, chan); - } else if (bw != 20 && !is_6g_psc && !map_is_all_subband_channel_set(&radio->ctl_channels, chan, op_class)) { - /* block because secondary is not set (40MHz only) */ - map_cs_set(&pref_op_class->channels, chan); + if (!is_center_chan) { + if (!map_cs_is_set(channels, chan)) { + /* block because primary channel is not set */ + ADD_CHANNEL(chan); + } else if (bw > 20 && !is_6g_psc && !map_is_all_subband_channel_set(&radio->ctl_channels, op_class, chan)) { + /* block because secondary is not set (40MHz only) */ + ADD_CHANNEL(chan); + } + } else { + /* For 40 (6G), 80, 160 and 320MHz, the center channel needs to be added */ + if (!map_get_center_channel(op_class, chan, ¢er_chan)) { + if (map_is_no_subband_channel_set(channels, op_class, chan)) { + /* block because none of the subband channels are preferred */ + ADD_CHANNEL(center_chan); + } else if (!is_6g_psc && !map_is_all_subband_channel_set(&radio->ctl_channels, op_class, chan)) { + /* block because one of the subband channels is allowed */ + ADD_CHANNEL(center_chan); + } + } } } else { - /* For 40 (6G), 80, 160 and 320MHz, the center channel needs to be added (and only once). - Code below assumes the channels in the set are sorted. - */ - if (!map_get_center_channel(op_class, chan, ¢er_chan)) { - if (map_is_no_subband_channel_set(channels, chan, op_class)) { - /* block because none of the subband channels are set */ - map_cs_set(&pref_op_class->channels, center_chan); - } else if (!is_6g_psc && !map_is_all_subband_channel_set(&radio->ctl_channels, chan, op_class)) { - /* block because one of the subband channels are not set */ - map_cs_set(&pref_op_class->channels, center_chan); + /* 6G 320MHz is special because it has 2 overlapping sets of channels -> check both */ + foreach_bool(upper) { + if (map_is_channel_in_cap_op_class_6G_320MHz(cap_op_class, upper, chan)) { + if (!map_get_center_channel_6G_320MHz(op_class, upper, chan, ¢er_chan)) { + if (map_is_no_subband_channel_set_6G_320MHz(channels, op_class, upper, chan)) { + /* block because none of the subband channels are preferred */ + ADD_CHANNEL(center_chan); + } else if (!is_6g_psc && !map_is_all_subband_channel_set_6G_320MHz(&radio->ctl_channels, op_class, upper, chan)) { + /* block because one of the subband channels is not allowed */ + ADD_CHANNEL(center_chan); + } + } } } } } - pref_idx++; +#undef ADD_OP_CLASS +#undef ADD_CHANNEL } radio->ctrl_pref_op_class_list.op_classes_nr = pref_idx; @@ -539,7 +895,8 @@ static int set_merged_pref_op_class_list(map_radio_info_t *radio) radio->merged_pref_op_class_list.op_classes_nr = 0; return map_merge_pref_op_class_list(&radio->merged_pref_op_class_list, &radio->cap_op_class_list, - &radio->ctrl_pref_op_class_list, &radio->pref_op_class_list); + &radio->ctrl_pref_op_class_list, &radio->pref_op_class_list, + &radio->disallowed_op_class_list); } /* Update all preferred control channel and operating class lists. */ @@ -595,20 +952,22 @@ static int update_pref_channel_op_class_list(map_ale_info_t *ale, map_radio_info /*####################################################################### # PUBLIC FUNCTIONS # ########################################################################*/ -int map_ctrl_chan_sel_set(map_radio_info_t *radio, bool *acs_enable, map_channel_set_t *acs_channels, - int *channel, int *bandwidth) +int map_ctrl_chan_sel_set(map_radio_info_t *radio, bool *cloud_mgmt_enable, bool *acs_enable, + map_channel_set_t *acs_channels, int *channel, int *bandwidth) { map_ale_info_t *ale = radio->ale; map_radio_chan_sel_t *chan_sel = &radio->chan_sel; bool new_acs_enable = acs_enable ? *acs_enable : chan_sel->acs_enable; int new_channel = channel ? *channel : chan_sel->channel; char cs_str[MAP_CS_BUF_LEN]; + char cloud_mgmt_enable_str[16]; char acs_enable_str[16]; char channel_str[16]; char bandwidth_str[16]; - log_ctrl_i("set ale[%s] radio[%s]: acs_enable[%s] acs_channels[%s] channel[%s] bandwidth[%s]", + log_ctrl_i("set ale[%s] radio[%s]: cloud_mgmt_enable[%s] acs_enable[%s] acs_channels[%s] channel[%s] bandwidth[%s]", radio->ale->al_mac_str, radio->radio_id_str, + bool_to_str(cloud_mgmt_enable_str, sizeof(cloud_mgmt_enable_str), cloud_mgmt_enable), bool_to_str(acs_enable_str, sizeof(acs_enable_str), acs_enable), acs_channels ? map_cs_to_string(acs_channels, ',', cs_str, sizeof(cs_str)) : "-", int_to_str(channel_str, sizeof(channel_str), channel), @@ -637,10 +996,11 @@ int map_ctrl_chan_sel_set(map_radio_info_t *radio, bool *acs_enable, map_channel } map_dm_radio_set_chan_sel(radio, + cloud_mgmt_enable ? *cloud_mgmt_enable : chan_sel->cloud_mgmt_enable, new_acs_enable, - acs_channels ? acs_channels : &chan_sel->acs_channels, + acs_channels ? acs_channels : &chan_sel->acs_channels, new_channel, - bandwidth ? *bandwidth : chan_sel->bandwidth); + bandwidth ? *bandwidth : chan_sel->bandwidth); /* Update preferred channels */ if (update_pref_channel_op_class_list(ale, radio)) { @@ -661,16 +1021,22 @@ int map_ctrl_chan_sel_set(map_radio_info_t *radio, bool *acs_enable, map_channel int map_ctrl_chan_sel_set_channel(map_radio_info_t *radio, int channel) { - return map_ctrl_chan_sel_set(radio, NULL, NULL, &channel, NULL); + return map_ctrl_chan_sel_set(radio, NULL, NULL, NULL, &channel, NULL); } int map_ctrl_chan_sel_set_bandwidth(map_radio_info_t *radio, int bandwidth) { - return map_ctrl_chan_sel_set(radio, NULL, NULL, NULL, &bandwidth); + return map_ctrl_chan_sel_set(radio, NULL, NULL, NULL, NULL, &bandwidth); +} + +int map_ctrl_chan_sel_set_cloud_mgmt_enable(map_radio_info_t *radio, bool enable) +{ + return map_ctrl_chan_sel_set(radio, &enable, NULL, NULL, NULL, NULL); } int map_ctrl_chan_sel_update(map_radio_info_t *radio) { + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; map_ale_info_t *ale = radio->ale; map_radio_chan_sel_t *chan_sel = &radio->chan_sel; @@ -683,7 +1049,7 @@ int map_ctrl_chan_sel_update(map_radio_info_t *radio) /* Check if fixed channel still possible, switch to auto if not */ if (!chan_sel->acs_enable && chan_sel->channel > 0) { if (!map_cs_is_set(&radio->ctl_channels, chan_sel->channel)) { - map_dm_radio_set_chan_sel(radio, true, &chan_sel->acs_channels, 0, chan_sel->bandwidth); + map_dm_radio_set_chan_sel(radio, chan_sel->cloud_mgmt_enable, true, &chan_sel->acs_channels, 0, chan_sel->bandwidth); } } @@ -693,6 +1059,14 @@ int map_ctrl_chan_sel_update(map_radio_info_t *radio) goto fail; } + /* multiap_update */ + if (cfg->align_multiap) { + if (multiap_update(radio, false)) { + log_ctrl_e("failed updating multiap channel selection"); + goto fail; + } + } + return 0; fail: @@ -701,6 +1075,7 @@ int map_ctrl_chan_sel_update(map_radio_info_t *radio) void map_ctrl_chan_sel_dump(map_printf_cb_t print_cb, map_ale_info_t *req_ale, bool extended) { + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; map_ale_info_t *ale; map_radio_info_t *radio; bool first_ale = true; @@ -708,6 +1083,10 @@ void map_ctrl_chan_sel_dump(map_printf_cb_t print_cb, map_ale_info_t *req_ale, b if (req_ale == NULL) { print_cb("GLOBAL\n"); + print_cb(" config:\n"); + print_cb(" align_multiap : %s\n", bool_to_str(buf, sizeof(buf), &cfg->align_multiap)); + print_cb(" align_multiap_backoff: %"PRIu32"s\n", cfg->align_multiap_backoff_time); + print_cb("\n"); print_cb(" BAND[2G]\n"); print_multiap_band(print_cb, BAND_2G, " "); print_cb("\n"); @@ -744,11 +1123,12 @@ void map_ctrl_chan_sel_dump(map_printf_cb_t print_cb, map_ale_info_t *req_ale, b print_cb(" RADIO[%s][%s]\n", radio->radio_id_str, band_to_str(buf, sizeof(buf), radio)); print_cb(" config:\n"); - print_cb(" acs_enable : %s\n", bool_to_str(buf, sizeof(buf), &chan_sel->acs_enable)); - print_cb(" acs_channels: [%s]\n", map_cs_to_string(&chan_sel->acs_channels, ',', buf, sizeof(buf))); - print_cb(" channel : %d\n", chan_sel->channel); - print_cb(" bandwidth : %d\n", chan_sel->bandwidth); - print_cb(" backhaul : %s\n", bool_to_str(buf, sizeof(buf), &is_backhaul)); + print_cb(" cloud_mgmt_enable: %s\n", bool_to_str(buf, sizeof(buf), &chan_sel->cloud_mgmt_enable)); + print_cb(" acs_enable : %s\n", bool_to_str(buf, sizeof(buf), &chan_sel->acs_enable)); + print_cb(" acs_channels : [%s]\n", map_cs_to_string(&chan_sel->acs_channels, ',', buf, sizeof(buf))); + print_cb(" channel : %d\n", chan_sel->channel); + print_cb(" bandwidth : %d\n", chan_sel->bandwidth); + print_cb(" backhaul : %s\n", bool_to_str(buf, sizeof(buf), &is_backhaul)); print_cb(" state:\n"); print_cb(" channel : %d\n", radio->current_op_channel); print_cb(" bandwidth : %d\n", radio->current_bw); @@ -768,6 +1148,8 @@ void map_ctrl_chan_sel_dump(map_printf_cb_t print_cb, map_ale_info_t *req_ale, b print_op_class_list(print_cb, &radio->pref_op_class_list, " "); print_cb(" ctrl_pref:\n"); print_op_class_list(print_cb, &radio->ctrl_pref_op_class_list, " "); + print_cb(" disallowed:\n"); + print_op_class_list(print_cb, &radio->disallowed_op_class_list, " "); print_cb(" merged_pref:\n"); print_op_class_list(print_cb, &radio->merged_pref_op_class_list, " "); } @@ -780,6 +1162,8 @@ int map_ctrl_chan_sel_init(void) static const uint8_t bands[] = {BAND_2G, BAND_5G, BAND_6G, BAND_UNKNOWN}; uint8_t i; + map_dm_register_cbs(&g_dm_cbs); + /* Initialise multiap align channel sets (no restriction) */ for (i = 0; i < ARRAY_SIZE(bands); i++) { uint8_t band = bands[i]; @@ -801,4 +1185,7 @@ int map_ctrl_chan_sel_init(void) void map_ctrl_chan_sel_fini(void) { + map_timer_unregister_callback_prefix(CHAN_SEL_MULTIAP_ALIGN_TIMER_ID); + + map_dm_unregister_cbs(&g_dm_cbs); } diff --git a/source/controller/src/map_ctrl_cli.c b/source/controller/src/map_ctrl_cli.c index edf5ab1..8dab9d6 100644 --- a/source/controller/src/map_ctrl_cli.c +++ b/source/controller/src/map_ctrl_cli.c @@ -21,6 +21,8 @@ #include "map_ctrl_utils.h" #include "map_ctrl_cmdu_tx.h" #include "map_ctrl_wfa_capi.h" +#include "map_ctrl_emex_tlv_handler.h" +#include "map_ctrl_vendor.h" #include "map_ctrl_chan_sel.h" #include "map_cli.h" @@ -85,6 +87,7 @@ static const char *g_help = "map_cli --command dumpInterfaces\n" "map_cli --command dumpBlockList\n" "map_cli --command dumpOpClasses\n" + "map_cli --command dumpMLD\n" "map_cli --command dumpChanSel "PAYLOAD_HELP"\n" "map_cli --command dumpTunneledMessage --payload '{\"mac\": \"AA:BB:CC:DD:EE:FF\",\"msgtype\":\"assoc|reassoc|btm|wnm|anqp\"}'\n" "map_cli --command dumpAPMetrics --payload '{\"bssid\":\"AA:BB:CC:DD:EE:FF\"}'\n" @@ -118,6 +121,7 @@ static const char *g_help = "map_cli --command sendChScanReportPolicyConf "PAYLOAD_HELP"\n" "map_cli --command sendDPPCCEIndication "PAYLOAD_HELP"\n" "map_cli --command sendProxiedEncapDPP "PAYLOAD_HELP"\n" + "map_cli --command sendRebootRequestMessage "PAYLOAD_HELP"\n" "map_cli --command sendRawMessage --payload '$ifname|MSB raw message bytes in hex separated by ws (network byte order) LSB'\n" /* VARIOUS */ @@ -469,6 +473,19 @@ static const char *g_help_send_proxied_encap_dpp = "-Example-\n" "map_cli --command sendProxiedEncapDPP --payload '{\"almac\":\"AA:BB:CC:DD:EE:FF\",\"encap\":{\"stamac\":\"AA:BB:CC:DD:EE:FF\",\"frame_indicator\":0,\"frame_type\":10,\"frame\":\"AABBCCDDEEFF\"},\"chirp\":{\"stamac\":\"AA:BB:CC:DD:EE:FF\",\"hash_validity\":1,\"hash\":\"AABBCCDDEEFF\"}}'\n"; +static const char *g_help_send_reboot_request_message = + "~Send Reboot Request Message Help~\n" + "This function sends an Airties reboot request message. Field 'factory_reset' is required when 'action=reset'\n\n" + "-Payload format-\n" + " {\n" + " \"almac\": \"AA:BB:CC:DD:EE:FF\",\n" + " \"action\": reboot|reset,\n" + " \"factory_reset\": true|false,\n" + " }\n\n" + "-Example-\n" + "map_cli --command sendRebootRequestMessage --payload '{\"almac\": \"AA:BB:CC:DD:EE:FF\", \"action\": \"reboot\"}'\n" + "map_cli --command sendRebootRequestMessage --payload '{\"almac\": \"AA:BB:CC:DD:EE:FF\", \"action\": \"reset\", \"factory_reset\": true}'\n"; + static const char *g_help_send_wfa_capi = "~Send WFA CAPI Help~\n" "This command is used for WFA certification to send Control API commands as specified in the Wi-Fi Testsuite Control API Specification.\n" @@ -541,14 +558,7 @@ static void cli_version(UNUSED const char *event, UNUSED const char *payload, UN static void cli_dump_info(UNUSED const char *event, UNUSED const char *payload, UNUSED void *context) { - mac_addr_str gw_mac_str; - mac_addr gw_mac = {0}; - map_dm_dump_agent_info_tree(map_cli_printf); - - i1905_get_gateway_mac_address(gw_mac); - map_cli_printf("|---- GATEWAY MAC : [%s] -------|\n", mac_to_string(gw_mac, gw_mac_str)); - } static void cli_dump_interfaces(UNUSED const char *event, UNUSED const char *payload, UNUSED void *context) @@ -604,6 +614,11 @@ static void cli_dump_op_classes(UNUSED const char *event, UNUSED const char *pay } } +static void cli_dump_mld(UNUSED const char *event, UNUSED const char *payload, UNUSED void *context) +{ + map_dm_dump_mld(map_cli_printf); +} + static void cli_dump_chan_sel(UNUSED const char *event, const char *payload, UNUSED void *context) { /* @@ -750,12 +765,12 @@ static void cli_dump_ap_metrics(UNUSED const char *event, const char *payload, U } } map_cli_printf(" -----------------------------------------------\n"); - map_cli_printf(" -unicast bytes tx : %"PRIu64"\n", bss->extended_metrics.ucast_bytes_tx); - map_cli_printf(" -unicast bytes rx : %"PRIu64"\n", bss->extended_metrics.ucast_bytes_rx); - map_cli_printf(" -multicast bytes tx : %"PRIu64"\n", bss->extended_metrics.mcast_bytes_tx); - map_cli_printf(" -multicast bytes rx : %"PRIu64"\n", bss->extended_metrics.mcast_bytes_rx); - map_cli_printf(" -broadcast bytes tx : %"PRIu64"\n", bss->extended_metrics.bcast_bytes_tx); - map_cli_printf(" -broadcast bytes rx : %"PRIu64"\n", bss->extended_metrics.bcast_bytes_rx); + map_cli_printf(" -unicast bytes tx : %"PRIu64"\n", bss->extended_metrics.tx_ucast_bytes); + map_cli_printf(" -unicast bytes rx : %"PRIu64"\n", bss->extended_metrics.rx_ucast_bytes); + map_cli_printf(" -multicast bytes tx : %"PRIu64"\n", bss->extended_metrics.tx_mcast_bytes); + map_cli_printf(" -multicast bytes rx : %"PRIu64"\n", bss->extended_metrics.rx_mcast_bytes); + map_cli_printf(" -broadcast bytes tx : %"PRIu64"\n", bss->extended_metrics.tx_bcast_bytes); + map_cli_printf(" -broadcast bytes rx : %"PRIu64"\n", bss->extended_metrics.rx_bcast_bytes); out: JSON_PUT_CHECK_ARGS_OK @@ -863,7 +878,7 @@ static void cli_dump_sta_metrics(UNUSED const char *event, const char *payload, if (link_metrics) { map_cli_printf(" dl_mac_datarate: %u Mbps\n", link_metrics->dl_mac_datarate); map_cli_printf(" ul_mac_datarate: %u Mbps\n", link_metrics->ul_mac_datarate); - map_cli_printf(" rssi: %d dBm\n", RCPI_TO_RSSI(link_metrics->rssi)); + map_cli_printf(" rssi: %d dBm\n", link_metrics->rssi); } } else if (!strcmp(type, "extended_metrics")) { map_cli_printf("--Associated STA Extended Link Metrics--\n\n"); @@ -1057,7 +1072,7 @@ static void cli_set_channel(UNUSED const char *event, const char *payload, UNUSE } args_ok = true; - map_ctrl_chan_sel_set(radio, NULL, NULL, channel >= 0 ? &channel : NULL, bw >= 0 ? &bw : NULL); + map_ctrl_chan_sel_set(radio, NULL, NULL, NULL, channel >= 0 ? &channel : NULL, bw >= 0 ? &bw : NULL); map_cli_printf("OK\n"); @@ -1206,12 +1221,12 @@ static void cli_send_autoconfig_renew(UNUSED const char *event, const char *payl args_ok = true; if (ale) { - if (map_send_autoconfig_renew_ucast(ale, IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA)) { + if (map_send_autoconfig_renew_ucast(ale, IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA, true)) { map_cli_printf("map_send_autoconfig_renew_ucast() failed\n"); goto out; } } else { - if (map_send_autoconfig_renew(IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA)) { + if (map_send_autoconfig_renew(IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA, true)) { map_cli_printf("map_send_autoconfig_renew() failed\n"); goto out; } @@ -1303,10 +1318,11 @@ static void cli_send_client_capability_query(UNUSED const char *event, const cha * } */ - map_ale_info_t *ale = NULL; - map_sta_info_t *sta = NULL; - mac_addr sta_mac; - bool args_ok = false; + map_ale_info_t *ale = NULL; + map_sta_mld_info_t *sta_mld = NULL; + map_sta_info_t *sta = NULL; + mac_addr sta_mac; + bool args_ok = false; struct { struct json_object *object; @@ -1321,16 +1337,31 @@ static void cli_send_client_capability_query(UNUSED const char *event, const cha } /* Get sta */ - if (json_get_mac(json.object, "stamac", sta_mac) || - !(sta = map_dm_get_sta_from_ale(ale, sta_mac))) { - map_cli_printf("STA not found\n"); + if (json_get_mac(json.object, "stamac", sta_mac)) { + map_cli_printf("STA MAC missing\n"); goto out; } + /* Check if this MLD or regular sta */ + if ((sta_mld = map_dm_get_sta_mld_from_ale(ale, sta_mac))) { + /* OK */ + } else if ((sta = map_dm_get_sta_from_ale(ale, sta_mac))) { + /* Still send to sta_mld if this an affiliated sta */ + sta_mld = sta->sta_mld; + } else { + map_cli_printf("STA not found\n"); + goto out; + } args_ok = true; - if (map_send_client_capability_query(sta, MID_NA)) { - goto out; + if (sta_mld) { + if (map_send_mld_client_capability_query(sta_mld, MID_NA)) { + goto out; + } + } else { + if (map_send_client_capability_query(sta, MID_NA)) { + goto out; + } } map_cli_printf("OK\n"); @@ -1402,12 +1433,10 @@ static void cli_send_unassoc_sta_link_metrics_query(UNUSED const char *event, co * } */ - map_ale_info_t *ale = NULL; - map_unassoc_sta_link_metrics_query_tlv_t tlv = { 0 }; - mac_addr sta_macs[64]; /* array over all channels */ - size_t sta_macs_idx = 0; - unsigned int i, j; - bool args_ok = false; + map_ale_info_t *ale = NULL; + map_unassoc_sta_link_metrics_query_tlv_t tlv = {.tlv_type = TLV_TYPE_UNASSOCIATED_STA_LINK_METRICS_QUERY}; + uint8_t i, j; + bool args_ok = false; struct { struct json_object *object; @@ -1438,11 +1467,17 @@ static void cli_send_unassoc_sta_link_metrics_query(UNUSED const char *event, co /* Channels */ if (!json_object_object_get_ex(json.object, "channels", &json.channels.object) || - !json_object_is_type(json.channels.object, json_type_array)) { + !json_object_is_type(json.channels.object, json_type_array) || + json_object_array_length(json.channels.object) > UINT8_MAX) { + goto out; + } + + tlv.channels_nr = json_object_array_length(json.channels.object); + if (!(tlv.channels = calloc(tlv.channels_nr, sizeof(*tlv.channels)))) { goto out; } - for (i = 0; i < json_object_array_length(json.channels.object) && i < MAX_CHANNEL_PER_OP_CLASS; i++) { + for (i = 0; i < tlv.channels_nr; i++) { struct json_object *obj = json_object_array_get_idx(json.channels.object, i); if (!obj || !json_object_is_type(obj, json_type_object)) { @@ -1458,26 +1493,28 @@ static void cli_send_unassoc_sta_link_metrics_query(UNUSED const char *event, co /* STA macs */ if (!json_object_object_get_ex(obj, "stamacs", &json.channels.stamacs) || - !json_object_is_type(json.channels.stamacs, json_type_array)) { + !json_object_is_type(json.channels.stamacs, json_type_array) || + json_object_array_length(json.channels.stamacs) > UINT8_MAX) { + goto out; + } + + tlv.channels[i].sta_macs_nr = json_object_array_length(json.channels.stamacs); + if (!(tlv.channels[i].sta_macs = calloc(tlv.channels[i].sta_macs_nr, sizeof(*tlv.channels[i].sta_macs)))) { goto out; } - tlv.channels[i].sta_macs = &sta_macs[sta_macs_idx]; - for (j = 0; j < json_object_array_length(json.channels.stamacs) && sta_macs_idx < ARRAY_SIZE(sta_macs); j++) { + for (j = 0; j < tlv.channels[i].sta_macs_nr; j++) { struct json_object *obj2 = json_object_array_get_idx(json.channels.stamacs, j); if (!obj2 || !json_object_is_type(obj2, json_type_string)) { goto out; } - if (mac_from_string(json_object_get_string(obj2), sta_macs[sta_macs_idx])) { + if (mac_from_string(json_object_get_string(obj2), tlv.channels[i].sta_macs[j])) { goto out; } - sta_macs_idx++; } - tlv.channels[i].sta_macs_nr = j; } - tlv.channels_nr = i; args_ok = true; @@ -1489,6 +1526,8 @@ static void cli_send_unassoc_sta_link_metrics_query(UNUSED const char *event, co map_cli_printf("OK\n"); out: + free_1905_TLV_structure2((uint8_t *)&tlv); + JSON_PUT_CHECK_ARGS_OK } @@ -1515,9 +1554,10 @@ static void cli_send_beacon_metrics_query(UNUSED const char *event, const char * */ map_ale_info_t *ale = NULL; - map_beacon_metrics_query_tlv_t tlv = { 0 }; + map_beacon_metrics_query_tlv_t tlv = {.tlv_type = TLV_TYPE_BEACON_METRICS_QUERY}; const char *rep_det, *ssid; - unsigned int i, j; + uint8_t i; + int j; bool args_ok = false; struct { @@ -1596,11 +1636,17 @@ static void cli_send_beacon_metrics_query(UNUSED const char *event, const char * /* Get ap_channel_reports */ if (!json_object_object_get_ex(json.object, "ap_channel_reports", &json.ap_channel_reports.object) || - !json_object_is_type(json.ap_channel_reports.object, json_type_array)) { + !json_object_is_type(json.ap_channel_reports.object, json_type_array) || + json_object_array_length(json.ap_channel_reports.object) > UINT8_MAX) { + goto out; + } + + tlv.ap_channel_reports_nr = json_object_array_length(json.ap_channel_reports.object); + if (!(tlv.ap_channel_reports = calloc(tlv.ap_channel_reports_nr, sizeof(*tlv.ap_channel_reports)))) { goto out; } - for (i = 0; i < json_object_array_length(json.ap_channel_reports.object) && i < MAX_OP_CLASS; i++) { + for (i = 0; i < tlv.ap_channel_reports_nr; i++) { struct json_object *obj = json_object_array_get_idx(json.ap_channel_reports.object, i); if (!obj || !json_object_is_type(obj, json_type_object)) { @@ -1620,7 +1666,7 @@ static void cli_send_beacon_metrics_query(UNUSED const char *event, const char * goto out; } - for (j = 0; j < json_object_array_length(json.ap_channel_reports.channels); j++) { + for (j = 0; j < (int)json_object_array_length(json.ap_channel_reports.channels); j++) { struct json_object *obj2 = json_object_array_get_idx(json.ap_channel_reports.channels, j); if (!obj2 || !json_object_is_type(obj2, json_type_int)) { @@ -1629,7 +1675,6 @@ static void cli_send_beacon_metrics_query(UNUSED const char *event, const char * map_cs_set(&tlv.ap_channel_reports[i].channels, json_object_get_int(obj2)); } } - tlv.ap_channel_reports_nr = i; args_ok = true; @@ -1641,6 +1686,8 @@ static void cli_send_beacon_metrics_query(UNUSED const char *event, const char * map_cli_printf("OK\n"); out: + free_1905_TLV_structure2((uint8_t *)&tlv); + JSON_PUT_CHECK_ARGS_OK } @@ -1703,7 +1750,7 @@ static void cli_send_client_steering_request(UNUSED const char *event, const cha map_steer_t *steer = (map_steer_t *)steer_buf; const char* mode; bool args_ok = false; - unsigned int i; + int i; struct { struct json_object *object; @@ -1787,7 +1834,7 @@ static void cli_send_client_steering_request(UNUSED const char *event, const cha goto out; } - for (i = 0; i < json_object_array_length(json.targets.object) && i < MAX_STEER_TARGET; i++) { + for (i = 0; i < (int)json_object_array_length(json.targets.object) && i < MAX_STEER_TARGET; i++) { struct json_object *obj = json_object_array_get_idx(json.targets.object, i); if (!obj || !json_object_is_type(obj, json_type_object)) { @@ -1857,7 +1904,7 @@ static void cli_send_client_assoc_control_request(UNUSED const char *event, cons map_client_assoc_control_request_tlv_t tlv = {0}; bool block; bool args_ok = false; - unsigned int i; + int i; struct { struct json_object *object; @@ -1906,7 +1953,7 @@ static void cli_send_client_assoc_control_request(UNUSED const char *event, cons goto out; } - for (i = 0; i < json_object_array_length(json.stamacs) && i < MAX_STATION_PER_BSS; i++) { + for (i = 0; i < (int)json_object_array_length(json.stamacs) && i < MAX_STATION_PER_BSS; i++) { struct json_object *obj = json_object_array_get_idx(json.stamacs, i); if (!obj || !json_object_is_type(obj, json_type_string)) { @@ -2263,7 +2310,7 @@ static void cli_send_channel_scan_request(UNUSED const char *event, const char * * } */ - map_channel_scan_request_tlv_t channel_scan_req_tlv = {0}; + map_channel_scan_request_tlv_t channel_scan_req_tlv = {.tlv_type = TLV_TYPE_CHANNEL_SCAN_REQUEST}; map_ale_info_t *ale; int l, i, j, channel_nr, channel_len, k, channel; bool args_ok = false; @@ -2336,12 +2383,14 @@ static void cli_send_channel_scan_request(UNUSED const char *event, const char * /* get number of opclass */ if (!json_object_object_get_ex(radio_obj, "no_of_opclass", &json.radio_list.no_of_opclass) || - !json_object_is_type(json.radio_list.no_of_opclass, json_type_int)) { + !json_object_is_type(json.radio_list.no_of_opclass, json_type_int) || + json_object_get_int(json.radio_list.no_of_opclass) < 0 || + json_object_get_int(json.radio_list.no_of_opclass) > UINT8_MAX) { goto out; } channel_scan_req_tlv.radios[i].op_classes_nr = json_object_get_int(json.radio_list.no_of_opclass); - if (errno == EINVAL || (int8_t)channel_scan_req_tlv.radios[i].op_classes_nr < 0) { + if (!(channel_scan_req_tlv.radios[i].op_classes = calloc(channel_scan_req_tlv.radios[i].op_classes_nr, sizeof(*channel_scan_req_tlv.radios[i].op_classes)))) { goto out; } @@ -2420,6 +2469,8 @@ static void cli_send_channel_scan_request(UNUSED const char *event, const char * map_cli_printf("OK\n"); out: + free_1905_TLV_structure2((uint8_t *)&channel_scan_req_tlv); + JSON_PUT_CHECK_ARGS_OK } @@ -3130,6 +3181,113 @@ static void cli_send_wfa_capi(UNUSED const char *event, const char *payload, UNU JSON_PUT_CHECK_ARGS_OK } +static void cli_send_reboot_request_message(UNUSED const char *event, const char *payload, UNUSED void *context) +{ + /* + * payload: + * { + * "almac": "AA:BB:CC:DD:EE:FF", + * "action": reboot|reset, + * "factory_reset": true|false, + * } + */ + struct { + struct json_object *object; + struct json_object *action; + struct json_object *factory_reset; + } json; + + map_vendor_tlv_tuple_t tlvs[2]; + map_ale_info_t *ale = NULL; + uint16_t tlv_type, msg_type; + uint8_t buf_msg_type_tlv[4] = {0}; + uint8_t *buf_reboot_request_tlv = NULL, *p; + uint8_t action_type = 0, reset_type = 0, reboot_request_tlv_len = 0; + bool args_ok = false; + const char *action; + + JSON_PARSE + + CHECK_PRINT_HELP(g_help_send_reboot_request_message); + + if (!(ale = json_get_ale(json.object, "almac"))) { + map_cli_printf("ALE not found\n"); + goto out; + } + + if (!map_emex_agent_is_feature_supported(ale, MAP_EMEX_FEATURE_REBOOT_RESET)) { + args_ok = true; + map_cli_printf("Reboot request is not supported by the agent\n"); + goto out; + } + + /* AirTies Message Type TLV */ + tlv_type = EMEX_TLV_MESSAGE_TYPE; + p = buf_msg_type_tlv; + _I2B(&tlv_type, &p); + msg_type = EMEX_MESSAGE_REBOOT_REQUEST; + _I2B(&msg_type, &p); + + /* AirTies Reboot Request TLV */ + tlv_type = EMEX_TLV_REBOOT_REQUEST; + if (!json_object_object_get_ex(json.object, "action", &json.action) || + !json_object_is_type(json.action, json_type_string)) { + goto out; + } + if (NULL == (action = json_object_get_string(json.action))) { + goto out; + } + + if (!strcmp(action, "reboot")) { + action_type = MAP_EMEX_REBOOT_ACTION_REBOOT; + reboot_request_tlv_len = 3; /* 2 (tlv type) + 1 (action) */ + } else if (!strcmp(action, "reset")) { + action_type = MAP_EMEX_REBOOT_ACTION_RESET; + reboot_request_tlv_len = 4; /* 2 (tlv type) + 1 (action) + 1 (reset type) */ + } else { + map_cli_printf("invalid action type\n"); + goto out; + } + + buf_reboot_request_tlv = calloc(1, reboot_request_tlv_len); + if (!buf_reboot_request_tlv) { + map_cli_printf("Failed to allocate memory"); + goto out; + } + + p = buf_reboot_request_tlv; + _I2B(&tlv_type, &p); + _I1B(&action_type, &p); + + if (action_type == MAP_EMEX_REBOOT_ACTION_RESET) { + if (!json_object_object_get_ex(json.object, "factory_reset", &json.factory_reset) || + !json_object_is_type(json.factory_reset, json_type_boolean)) { + goto out; + } + + reset_type = json_object_get_boolean(json.factory_reset) ? MAP_EMEX_RESET_FACTORY_RESET : MAP_EMEX_RESET_SOFT_RESET; + _I1B(&reset_type, &p); + } + + tlvs[0].len = 4; /* 2 (tlv type) + 2 (msg type) */ + tlvs[0].data = buf_msg_type_tlv; + tlvs[1].len = reboot_request_tlv_len; + tlvs[1].data = buf_reboot_request_tlv; + + args_ok = true; + + if (map_ctrl_vendor_send_message(ale, tlvs, 2, MID_NA)) { + goto out; + } + + map_cli_printf("OK\n"); + +out: + SFREE(buf_reboot_request_tlv); + + JSON_PUT_CHECK_ARGS_OK +} + /*####################################################################### # CLI SUBSCRIPTIONS # ########################################################################*/ @@ -3141,6 +3299,7 @@ static map_subscription_t g_cli_subscriptions[] = { { "dumpInterfaces", cli_dump_interfaces, (SUBS_FLAG_MODE_FULL | SUBS_FLAG_MODE_REDUCED) }, { "dumpBlockList", cli_dump_blocklist, (SUBS_FLAG_MODE_FULL | SUBS_FLAG_MODE_REDUCED) }, { "dumpOpClasses", cli_dump_op_classes, (SUBS_FLAG_MODE_FULL | SUBS_FLAG_MODE_REDUCED) }, + { "dumpMLD", cli_dump_mld, (SUBS_FLAG_MODE_FULL) }, { "dumpChanSel", cli_dump_chan_sel, (SUBS_FLAG_MODE_FULL) }, { "dumpTunneledMessage", cli_dump_tunneled_msg, (SUBS_FLAG_MODE_FULL) }, { "dumpAPMetrics", cli_dump_ap_metrics, (SUBS_FLAG_MODE_FULL) }, @@ -3174,6 +3333,7 @@ static map_subscription_t g_cli_subscriptions[] = { { "sendChScanReportPolicyConf", cli_send_ch_scan_reporting_policy_config, (SUBS_FLAG_MODE_FULL) }, { "sendDPPCCEIndication", cli_send_dpp_cce_indication, (SUBS_FLAG_MODE_FULL) }, { "sendProxiedEncapDPP", cli_send_proxied_encap_dpp, (SUBS_FLAG_MODE_FULL) }, + { "sendRebootRequestMessage", cli_send_reboot_request_message, (SUBS_FLAG_MODE_FULL) }, { "sendRawMessage", cli_send_raw_message, (SUBS_FLAG_MODE_FULL | SUBS_FLAG_MODE_REDUCED) }, /* VARIOUS */ diff --git a/source/controller/src/map_ctrl_cmdu_handler.c b/source/controller/src/map_ctrl_cmdu_handler.c index accb470..c98ce5d 100644 --- a/source/controller/src/map_ctrl_cmdu_handler.c +++ b/source/controller/src/map_ctrl_cmdu_handler.c @@ -93,6 +93,17 @@ static void get_m1_attributes(map_ale_info_t *ale, uint8_t *m1, uint16_t m1_size map_dm_ale_set_device_info(ale, &d); } +static void get_wsc_auth_modes(map_radio_info_t *radio, uint8_t *wsc, uint16_t wsc_len) +{ + uint16_t wsc_attr_len = 0; + uint8_t *wsc_attr = NULL; + + /* Get regular supported auth modes */ + if ((wsc_attr = map_get_wsc_attr(wsc, wsc_len, WSC_ATTR_AUTH_TYPE_FLAGS, &wsc_attr_len)) && wsc_attr_len == 2) { + _E2B(&wsc_attr, &radio->auth_modes_flag); + } +} + static void refresh_radio_data(map_ale_info_t *ale, map_radio_info_t *radio) { timer_id_t retry_id; @@ -405,16 +416,21 @@ static int handle_1905_dev(i1905_cmdu_t *cmdu, bool topo_discovery) static int map_handle_topology_response_ale(map_ale_info_t *ale, i1905_cmdu_t *cmdu) { - i1905_device_information_tlv_t *dev_info_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_DEVICE_INFORMATION, cmdu); /* Mandatory */ - map_ap_operational_bss_tlv_t *op_bss_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AP_OPERATIONAL_BSS, cmdu); /* Optional */ - map_assoc_clients_tlv_t *assoc_clients_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_ASSOCIATED_CLIENTS, cmdu); /* Optional */ - map_multiap_profile_tlv_t *profile_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_MULTIAP_PROFILE, cmdu); /* Optional */ - i1905_neighbor_device_list_tlv_t *neighbor_dev_tlv; - i1905_non_1905_neighbor_device_list_tlv_t *non_1905_neighbor_dev_tlv; + i1905_device_information_tlv_t *dev_info_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_DEVICE_INFORMATION, cmdu); /* Mandatory */ + map_ap_operational_bss_tlv_t *op_bss_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AP_OPERATIONAL_BSS, cmdu); /* Optional */ + map_assoc_clients_tlv_t *assoc_clients_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_ASSOCIATED_CLIENTS, cmdu); /* Optional */ + map_multiap_profile_tlv_t *profile_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_MULTIAP_PROFILE, cmdu); /* Optional */ + map_agent_ap_mld_conf_tlv_t *ap_mld_conf_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AGENT_AP_MLD_CONFIGURATION, cmdu); /* Optional */ + map_bsta_mld_conf_tlv_t *bsta_mld_conf_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_BACKHAUL_STA_MLD_CONFIGURATION, cmdu); /* Optional */ + map_assoc_sta_mld_conf_tlv_t *assoc_sta_mld_conf_tlv = NULL; + map_backhaul_sta_radio_cap_tlv_t *bhsta_radio_cap_tlv = NULL; + map_backhaul_sta_radio_cap_tlv_t *bhsta_radio_cap_tlvs[MAX_RADIO_PER_AGENT] = {NULL}; + i1905_neighbor_device_list_tlv_t *neighbor_dev_tlv = NULL; + i1905_non_1905_neighbor_device_list_tlv_t *non_1905_neighbor_dev_tlv = NULL; i1905_neighbor_device_list_tlv_t *neighbor_dev_tlvs[MAX_ALE_NEIGHBOR_COUNT] = {NULL}; i1905_non_1905_neighbor_device_list_tlv_t *non_1905_neighbor_dev_tlvs[MAX_ALE_NEIGHBOR_COUNT] = {NULL}; - i1905_vendor_specific_tlv_t *vendor_tlv; - size_t neighbor_dev_tlvs_nr = 0, non_1905_neighbor_dev_tlvs_nr = 0, tlv_idx; + i1905_vendor_specific_tlv_t *vendor_tlv = NULL; + size_t neighbor_dev_tlvs_nr = 0, non_1905_neighbor_dev_tlvs_nr = 0, bhsta_radio_cap_tlvs_nr = 0, tlv_idx; bool update_dm = false; if (!dev_info_tlv) { @@ -435,6 +451,13 @@ static int map_handle_topology_response_ale(map_ale_info_t *ale, i1905_cmdu_t *c } } + /* Get all bhsta radio capability tlvs */ + i1905_foreach_tlv_type_in_cmdu(TLV_TYPE_BACKHAUL_STA_RADIO_CAPABILITIES, bhsta_radio_cap_tlv, cmdu, tlv_idx) { + if (bhsta_radio_cap_tlvs_nr < MAX_RADIO_PER_AGENT) { + bhsta_radio_cap_tlvs[bhsta_radio_cap_tlvs_nr++] = bhsta_radio_cap_tlv; + } + } + /* Parse map_profile of ale first as it is used in the onboarding flow */ if (profile_tlv) { map_parse_multiap_profile_tlv(ale, profile_tlv); @@ -464,18 +487,47 @@ static int map_handle_topology_response_ale(map_ale_info_t *ale, i1905_cmdu_t *c /* Store non 1905 neighbors. Must be done after parsing device information tlv */ map_parse_non_1905_neighbor_device_list_tlv(ale, non_1905_neighbor_dev_tlvs, non_1905_neighbor_dev_tlvs_nr); + /* Parse MLD config TLVs */ + if (ap_mld_conf_tlv) { + map_parse_agent_ap_mld_conf_tlv(ale, ap_mld_conf_tlv); + } else { + /* Parse "empty" one to remove all the ap_mld */ + map_agent_ap_mld_conf_tlv_t dummy = {.ap_mld_nr = 0}; + map_parse_agent_ap_mld_conf_tlv(ale, &dummy); + } + + if (bsta_mld_conf_tlv) { + map_parse_bsta_mld_conf_tlv(ale, bsta_mld_conf_tlv); + } else { + /* Parse "empty" one to remove the bsta_mld */ + map_bsta_mld_conf_tlv_t dummy = {.bsta_mld_mac_valid = 0}; + map_parse_bsta_mld_conf_tlv(ale, &dummy); + } + /* Parse and update the connected clients + - MLD clients are done first (this includes affiliated stas) - Assoc_clients_tlv is only mandatory when at least one client is connected - Before parsing, mark all stas. - After parsing, remove all sta that are still marked. Only remove sta that where connected for some time to avoid race between topology response and topology notification. */ + map_dm_mark_sta_mlds(ale); map_dm_mark_stas(ale); + + i1905_foreach_tlv_type_in_cmdu(TLV_TYPE_ASSOCIATED_STA_MLD_CONFIGURATION, assoc_sta_mld_conf_tlv, cmdu, tlv_idx) { + map_parse_assoc_sta_mld_conf_tlv(ale, assoc_sta_mld_conf_tlv); + } + if (assoc_clients_tlv) { map_parse_assoc_clients_tlv(ale, assoc_clients_tlv); } map_dm_remove_marked_stas(ale, 30 /* seconds */); + map_dm_remove_marked_sta_mlds(ale); + + if (bhsta_radio_cap_tlvs_nr > 0) { + map_parse_backhaul_sta_radio_capability_tlv(ale, bhsta_radio_cap_tlvs, bhsta_radio_cap_tlvs_nr); + } /* Handle emex tlvs */ map_emex_handle_cmdu_pre(ale, cmdu); @@ -721,6 +773,9 @@ int map_handle_ap_autoconfig_search(i1905_cmdu_t *cmdu) if (topo_discovery_is_rcvd) { map_handle_topology_discovery_ale(ale, cmdu->interface_name, cmdu->cmdu_stream.src_mac_addr, mac_tlv_mac); } + } else { + /* if agent is already known, delete existing keys first before onboarding */ + map_dm_remove_ale_key_info(ale); } if (profile_tlv) { @@ -736,6 +791,27 @@ int map_handle_ap_autoconfig_search(i1905_cmdu_t *cmdu) return map_send_autoconfig_response(cmdu, ale_is_agent); } +/* 1905.1 6.3.8 (type 0x0008) */ +int map_handle_ap_autoconfig_response(i1905_cmdu_t *cmdu) +{ + map_supported_service_tlv_t *ss_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_SUPPORTED_SERVICE, cmdu); + map_ale_info_t *ale = NULL; + bool ale_is_controller = false, ale_is_agent = false, ale_is_em_plus = false; + + map_parse_ap_supported_service_tlv(NULL, ss_tlv, &ale_is_controller, &ale_is_agent, &ale_is_em_plus); + + /* Check if it is an airties controller */ + if (ale_is_controller && ale_is_em_plus) { + } else { + ale = map_dm_get_ale_from_src_mac(cmdu->cmdu_stream.src_mac_addr); + if (ale && ale_is_agent && ale->easymesh_plus) { + ale->easymesh_plus = false; + } + } + + return 0; +} + /* 1905.1 6.3.9 (type 0x0009) */ int map_handle_ap_autoconfig_wsc(i1905_cmdu_t *cmdu) { @@ -743,10 +819,12 @@ int map_handle_ap_autoconfig_wsc(i1905_cmdu_t *cmdu) map_ap_radio_basic_cap_tlv_t *ap_basic_cap_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AP_RADIO_BASIC_CAPABILITIES, cmdu); /* Mandatory */ map_profile2_ap_cap_tlv_t *profile2_ap_cap_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_PROFILE2_AP_CAPABILITY, cmdu); /* Optional */ /* TODO: map_ap_radio_advanced_cap_tlv_t *adv_cap_tlv_t = i1905_get_tlv_from_cmdu(TLV_TYPE_AP_RADIO_ADVANCED_CAPABILITIES, cmdu);*/ /* Optional */ + i1905_vendor_specific_tlv_t *vendor_tlv; uint16_t mac_len; uint8_t *al_mac; map_ale_info_t *ale; map_radio_info_t *radio; + size_t idx; if (!wsc_tlv || !ap_basic_cap_tlv) { return -1; /* Not possble - checked during validate */ @@ -802,6 +880,15 @@ int map_handle_ap_autoconfig_wsc(i1905_cmdu_t *cmdu) return -1; } + /* Handle vendor specific TLVs */ + i1905_foreach_tlv_type_in_cmdu(TLV_TYPE_VENDOR_SPECIFIC, vendor_tlv, cmdu, idx) { + if (map_emex_is_valid_tlv(vendor_tlv)) { + map_emex_parse_tlv(ale, vendor_tlv); + } + } + + get_wsc_auth_modes(radio, wsc_tlv->wsc_frame, wsc_tlv->wsc_frame_size); + set_radio_state_M1_receive(&radio->state); map_recompute_radio_state_and_update_ale_state(ale); @@ -850,8 +937,9 @@ int map_handle_ap_capability_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu) map_radio_info_t *radio; uint8_t *tlv; size_t idx; + bool do_config_renew = false; - /* Remove existing ht/vht/he/wifi6 capabilities... */ + /* Remove existing ht/vht/he/wifi6/wifi7 capabilities... */ map_dm_foreach_radio(ale, radio) { map_free_ht_vht_he_wifi6_caps(radio); } @@ -871,6 +959,12 @@ int map_handle_ap_capability_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu) case TLV_TYPE_AP_WIFI6_CAPABILITIES: map_parse_ap_wifi6_cap_tlv(ale, (map_ap_wifi6_cap_tlv_t *)tlv); break; + case TLV_TYPE_WIFI7_AGENT_CAPABILITIES: + map_parse_wifi7_agent_capability_tlv(ale, (map_wifi7_agent_cap_tlv_t *)tlv, &do_config_renew); + break; + case TLV_TYPE_EHT_OPERATIONS: + map_parse_eht_operations_tlv(ale, (map_eht_operations_tlv_t *)tlv); + break; default: break; } @@ -924,6 +1018,11 @@ int map_handle_ap_capability_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu) map_dm_radio_set_capabilities(radio); } + if (do_config_renew) { + log_ctrl_n("AP Capabilities changed. Do config renew"); + map_send_autoconfig_renew_ucast(ale, IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA, true); + } + return 0; } @@ -943,9 +1042,8 @@ int map_handle_channel_preference_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu /* Remove existing preference for all radios */ map_dm_foreach_radio(ale, radio) { SFREE(radio->pref_op_class_list.op_classes); - SFREE(radio->op_restriction_list.op_classes); radio->pref_op_class_list.op_classes_nr = 0; - radio->op_restriction_list.op_classes_nr = 0; + map_dm_free_op_restriction_list(radio); } /* Mark channel preference report as received for all radios @@ -979,6 +1077,9 @@ int map_handle_channel_preference_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu map_parse_cac_status_report_tlv(ale, cac_status_tlv); break; } + case TLV_TYPE_EHT_OPERATIONS: + map_parse_eht_operations_tlv(ale, (map_eht_operations_tlv_t *)tlv); + break; default: break; } @@ -995,20 +1096,32 @@ int map_handle_channel_preference_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu /* MAP_R1 17.1.12 (type 0x8007) */ int map_handle_channel_selection_response(UNUSED map_ale_info_t *ale, i1905_cmdu_t *cmdu) { - map_channel_selection_response_tlv_t *tlv; - size_t idx; - - i1905_foreach_tlv_type_in_cmdu(TLV_TYPE_CHANNEL_SELECTION_RESPONSE, tlv, cmdu, idx) { - if (tlv->channel_selection_response == MAP_CHAN_SEL_RESPONSE_ACCEPTED) { - log_ctrl_n("channel selection for radio[%s] completed", mac_string(tlv->radio_id)); - } else { - log_ctrl_e("channel selection for radio[%s] failed. Reason[%d]", - mac_string(tlv->radio_id), tlv->channel_selection_response); + uint8_t *tlv; + size_t idx; - /* TODO: If reason == 3 (rejected by BH STA) the requests keep failing. - Should add exp backoff or a maximum number of retries - */ - } + i1905_foreach_tlv_in_cmdu(tlv, cmdu, idx) { + switch (*tlv) { + case TLV_TYPE_CHANNEL_SELECTION_RESPONSE: { + map_channel_selection_response_tlv_t *chan_sel_resp_tlv = (map_channel_selection_response_tlv_t *)tlv; + + if (chan_sel_resp_tlv->channel_selection_response == MAP_CHAN_SEL_RESPONSE_ACCEPTED) { + log_ctrl_n("channel selection for radio[%s] completed", mac_string(chan_sel_resp_tlv->radio_id)); + } else { + log_ctrl_e("channel selection for radio[%s] failed. Reason[%d]", + mac_string(chan_sel_resp_tlv->radio_id), chan_sel_resp_tlv->channel_selection_response); + + /* TODO: If reason == 3 (rejected by BH STA) the requests keep failing. + Should add exp backoff or a maximum number of retries + */ + } + break; + } + case TLV_TYPE_EHT_OPERATIONS: + map_parse_eht_operations_tlv(ale, (map_eht_operations_tlv_t *)tlv); + break; + default: + break; + } } return 0; @@ -1017,7 +1130,7 @@ int map_handle_channel_selection_response(UNUSED map_ale_info_t *ale, i1905_cmdu /* MAP_R1 17.1.13 (type 0x8008) */ int map_handle_operating_channel_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu) { - map_operating_channel_report_tlv_t *tlv; + uint8_t *tlv; map_radio_info_t *radio; size_t tlv_idx; @@ -1027,55 +1140,70 @@ int map_handle_operating_channel_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu) return -1; } - i1905_foreach_tlv_type_in_cmdu(TLV_TYPE_OPERATING_CHANNEL_REPORT, tlv, cmdu, tlv_idx) { - if (tlv->op_classes_nr > 0 && NULL != (radio = map_dm_get_radio(ale, tlv->radio_id))) { - uint8_t idx = 0, min_bw_idx = 0, max_bw_idx = 0; - uint16_t bw, min_bw = 320, max_bw = 20; + i1905_foreach_tlv_in_cmdu(tlv, cmdu, tlv_idx) { + switch (*tlv) { + case TLV_TYPE_OPERATING_CHANNEL_REPORT: { + map_operating_channel_report_tlv_t *op_chan_report_tlv = (map_operating_channel_report_tlv_t *)tlv; - SFREE(radio->curr_op_class_list.op_classes); - if (!(radio->curr_op_class_list.op_classes = calloc(tlv->op_classes_nr, sizeof(map_op_class_t)))) { - log_ctrl_e("%s: memory allocation failed", __FUNCTION__); - radio->curr_op_class_list.op_classes_nr = 0; - return -1; - } - radio->curr_op_class_list.op_classes_nr = tlv->op_classes_nr; + if (op_chan_report_tlv->op_classes_nr > 0 && NULL != (radio = map_dm_get_radio(ale, op_chan_report_tlv->radio_id))) { + uint8_t idx = 0, min_bw_idx = 0, max_bw_idx = 0; + uint16_t bw, min_bw = 320, max_bw = 20; - for (idx = 0; idx < tlv->op_classes_nr; idx++) { - map_op_class_t *op_class = &radio->curr_op_class_list.op_classes[idx]; + SFREE(radio->curr_op_class_list.op_classes); + radio->curr_op_class_list.op_classes_nr = 0; - /* One channel per operating class, transmit power is common */ - op_class->op_class = tlv->op_classes[idx].op_class; - op_class->eirp = tlv->transmit_power_eirp; - map_cs_set(&op_class->channels, tlv->op_classes[idx].channel); + if (!(radio->curr_op_class_list.op_classes = calloc(op_chan_report_tlv->op_classes_nr, sizeof(*radio->curr_op_class_list.op_classes)))) { + log_ctrl_e("%s: memory allocation failed", __FUNCTION__); + return -1; + } - if (!map_get_bw_from_op_class(tlv->op_classes[idx].op_class, &bw)) { - if (bw < min_bw) { - min_bw = bw; - min_bw_idx = idx; + radio->curr_op_class_list.op_classes_nr = op_chan_report_tlv->op_classes_nr; + + for (idx = 0; idx < op_chan_report_tlv->op_classes_nr; idx++) { + map_operating_channel_report_tlv_op_class_t *tlv_op_class = &op_chan_report_tlv->op_classes[idx]; + map_op_class_t *op_class = &radio->curr_op_class_list.op_classes[idx]; + + /* One channel per operating class, transmit power is common */ + op_class->op_class = tlv_op_class->op_class; + op_class->eirp = op_chan_report_tlv->transmit_power_eirp; + map_cs_set(&op_class->channels, tlv_op_class->channel); + + if (!map_get_bw_from_op_class(op_chan_report_tlv->op_classes[idx].op_class, &bw)) { + if (bw < min_bw) { + min_bw = bw; + min_bw_idx = idx; + } + if (bw > max_bw) { + max_bw = bw; + max_bw_idx = idx; + } + } } - if (bw > max_bw) { - max_bw = bw; - max_bw_idx = idx; + + /* Update channel related parameters and current operating classes in DM + - use op_class with the maximum bw to be able to get operating bw correctly + - use op_class with the minimum bw to get control channel + */ + map_dm_radio_set_channel(radio, op_chan_report_tlv->op_classes[max_bw_idx].op_class, + op_chan_report_tlv->op_classes[min_bw_idx].channel, op_chan_report_tlv->op_classes[max_bw_idx].channel, + max_bw, op_chan_report_tlv->transmit_power_eirp); + + log_ctrl_n("updated channel/bw for radio[%s] op_class[%d] channel[%d] highest_bw_channel[%d] bw[%d] from %s", + mac_string(radio->radio_id), radio->current_op_class, radio->current_op_channel, radio->highest_bw_channel, + max_bw, i1905_tlv_type_to_string(op_chan_report_tlv->tlv_type)); + + if (is_radio_operating_chan_report_received(radio->state) == 0 ) { + set_radio_state_oper_chan_report_received(&radio->state); + map_recompute_radio_state_and_update_ale_state(radio->ale); } } + break; } - - /* Update channel related parameters and current operating classes in DM - - use op_class with the maximum bw to be able to get operating bw correctly - - use op_class with the minimum bw to get control channel - */ - map_dm_radio_set_channel(radio, tlv->op_classes[max_bw_idx].op_class, - tlv->op_classes[min_bw_idx].channel, - max_bw, tlv->transmit_power_eirp); - - log_ctrl_n("updated channel/bw for radio[%s] op_class[%d] channel[%d] bw[%d] from %s", - mac_string(radio->radio_id), radio->current_op_class, radio->current_op_channel, max_bw, - i1905_tlv_type_to_string(tlv->tlv_type)); - - if (is_radio_operating_chan_report_received(radio->state) == 0 ) { - set_radio_state_oper_chan_report_received(&radio->state); - map_recompute_radio_state_and_update_ale_state(radio->ale); - } + case TLV_TYPE_EHT_OPERATIONS: + map_parse_eht_operations_tlv(ale, (map_eht_operations_tlv_t *)tlv); + break; + default: + break; } } @@ -1087,23 +1215,35 @@ int map_handle_client_capability_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu) { map_client_info_tlv_t *client_info_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_CLIENT_INFO, cmdu); /* Mandatory */ map_client_cap_report_tlv_t *cap_report_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_CLIENT_CAPABILITY_REPORT, cmdu); /* Mandatory */ - map_sta_info_t *sta; + map_sta_mld_info_t *sta_mld = NULL; + map_sta_info_t *sta = NULL; + int ret = 0; if (!client_info_tlv || !cap_report_tlv) { return -1; /* Not possble - checked during validate */ } - if (!(sta = map_dm_get_sta_from_ale(ale, client_info_tlv->sta_mac))) { - log_ctrl_e("handle[%s]: sta[%s] not found", i1905_cmdu_type_to_string(cmdu->message_type), mac_string(client_info_tlv->sta_mac)); - return 0; - } - if (cap_report_tlv->result_code == MAP_CLIENT_CAP_FAILURE) { log_ctrl_e("handle[%s]: sta[%s] result code failure", i1905_cmdu_type_to_string(cmdu->message_type), mac_string(client_info_tlv->sta_mac)); return 0; } - return parse_update_client_capability(sta, cap_report_tlv->assoc_frame_body_len, cap_report_tlv->assoc_frame_body); + /* First search for mld sta then for regular STA */ + if (!map_dm_ale_has_mld(ale) || !(sta_mld = map_dm_get_sta_mld_from_ale(ale, client_info_tlv->sta_mac))) { + if (!(sta = map_dm_get_sta_from_ale(ale, client_info_tlv->sta_mac))) { + log_ctrl_e("handle[%s]: sta[%s] not found", i1905_cmdu_type_to_string(cmdu->message_type), mac_string(client_info_tlv->sta_mac)); + return 0; + } + } + + /* For sta_mld we need to parse the result for every affiliated STA */ + if (sta_mld) { + ret = parse_update_mld_client_capability(sta_mld, cap_report_tlv->assoc_frame_body_len, cap_report_tlv->assoc_frame_body); + } else { + ret = parse_update_client_capability(sta, cap_report_tlv->assoc_frame_body_len, cap_report_tlv->assoc_frame_body); + } + + return ret; } /* MAP_R1 17.1.17 (type 0x800C) */ @@ -1144,16 +1284,19 @@ int map_handle_ap_metrics_response(map_ale_info_t *ale, i1905_cmdu_t *cmdu) case TLV_TYPE_ASSOCIATED_STA_EXTENDED_LINK_METRICS: map_parse_assoc_sta_ext_link_metrics_tlv(ale, (map_assoc_sta_ext_link_metrics_tlv_t *)tlv); break; + case TLV_TYPE_ASSOCIATED_WIFI6_STA_STATUS_REPORT: + map_parse_assoc_wifi6_sta_status_tlv(ale, (map_assoc_wifi6_sta_status_tlv_t *)tlv); + break; + case TLV_TYPE_AFFILIATED_STA_METRICS: + map_parse_aff_sta_metrics_tlv(ale, (map_aff_sta_metrics_tlv_t *)tlv); + break; case TLV_TYPE_VENDOR_SPECIFIC: { i1905_vendor_specific_tlv_t *vs_tlv = (i1905_vendor_specific_tlv_t *)tlv; if (map_emex_is_valid_tlv(vs_tlv)) { map_emex_parse_tlv(ale, vs_tlv); } - break; } - case TLV_TYPE_ASSOCIATED_WIFI6_STA_STATUS_REPORT: - map_parse_assoc_wifi6_sta_status_tlv(ale, (map_assoc_wifi6_sta_status_tlv_t *)tlv); - break; + break; default: break; } @@ -1557,6 +1700,9 @@ int map_handle_client_disassoc_stats(map_ale_info_t *ale, i1905_cmdu_t *cmdu) return -1; /* Not possble - checked during validate */ } + /* TODO: Report event before lookup up sta as it might already have + been removed by topology notification... */ + if(!(sta = map_dm_get_sta_from_ale(ale, stats_tlv->sta_mac))) { log_ctrl_e("%s: sta[%s] not found", __FUNCTION__, mac_string(stats_tlv->sta_mac)); return -1; @@ -1611,17 +1757,17 @@ int map_handle_failed_connection(map_ale_info_t *ale, i1905_cmdu_t *cmdu) return -1; /* Not possible - checked during validate */ } - log_ctrl_d("--------------------------"); - log_ctrl_d("client has failed connection attempt."); - log_ctrl_d("status code: 0x%02x", status_tlv->status_code); + log_ctrl_n("--------------------------"); + log_ctrl_n("client has failed connection attempt."); + log_ctrl_n("status code: 0x%02x", status_tlv->status_code); if (reason_tlv) { - log_ctrl_d("reason code: 0x%02x", reason_tlv->reason_code); + log_ctrl_n("reason code: 0x%02x", reason_tlv->reason_code); } - log_ctrl_d("sta mac: %s", mac_string(sta_mac_tlv->sta_mac)); + log_ctrl_n("sta mac: %s", mac_string(sta_mac_tlv->sta_mac)); if (bssid_tlv) { - log_ctrl_d("bssid: %s", mac_string(bssid_tlv->bssid)); + log_ctrl_n("bssid: %s", mac_string(bssid_tlv->bssid)); } - log_ctrl_d("--------------------------"); + log_ctrl_n("--------------------------"); map_dm_create_failconn(sta_mac_tlv->sta_mac, bssid_tlv ? bssid_tlv->bssid : NULL, status_tlv->status_code, reason_tlv ? reason_tlv->reason_code : 0); @@ -1676,6 +1822,39 @@ int map_handle_chirp_notification(map_ale_info_t *ale, i1905_cmdu_t *cmdu) return map_parse_dpp_chirp_value_tlv(ale, chirp_tlv); } +/* MAP_R3 17.1.53 (type 0x802c) */ +int map_handle_bss_configuration_request(map_ale_info_t *ale, i1905_cmdu_t *cmdu) +{ + map_multiap_profile_tlv_t *profile_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_MULTIAP_PROFILE, cmdu); /* Mandatory */ + map_supported_service_tlv_t *service_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_SUPPORTED_SERVICE, cmdu); /* Mandatory */ + map_ap_radio_basic_cap_tlv_t *radio_basic_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AP_RADIO_BASIC_CAPABILITIES, cmdu); /* Mandatory */ + //map_backhaul_sta_radio_cap_tlv_t *backhaul_sta_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_BACKHAUL_STA_RADIO_CAPABILITIES, cmdu); /* Optional */ + map_profile2_ap_cap_tlv_t *profile2_ap_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_PROFILE2_AP_CAPABILITY, cmdu); /* Mandatory */ + map_ap_radio_advanced_cap_tlv_t *radio_advanced_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AP_RADIO_ADVANCED_CAPABILITIES, cmdu); /* Mandatory */ + map_bss_configuration_request_tlv_t *bss_config_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_BSS_CONFIGURATION_REQUEST, cmdu); /* Mandatory */ + //map_eht_operations_tlv_t *eht_operations_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_EHT_OPERATIONS, cmdu); /* Optional */ + + if (!profile_tlv || !service_tlv || !radio_basic_tlv || !profile2_ap_tlv || !radio_advanced_tlv || !bss_config_tlv) { + return -1; + } + + return map_parse_bss_configuration_request_tlv(ale, bss_config_tlv); +} + +/* MAP_R3 17.1.55 (type 0x802e) */ +int map_handle_bss_configuration_result(map_ale_info_t *ale, i1905_cmdu_t *cmdu) +{ + map_bss_configuration_report_tlv_t *bss_cfg_report_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_BSS_CONFIGURATION_REPORT, cmdu); /* Mandatory */ + + if (!bss_cfg_report_tlv) { + return -1; + } + + map_parse_bss_configuration_report_tlv(ale, bss_cfg_report_tlv); + + return map_send_agent_list_message(ale, MID_NA); +} + /* MAP_R3 17.1.56 (type 0x802a) */ int map_handle_direct_encap_dpp(map_ale_info_t *ale, i1905_cmdu_t *cmdu) { @@ -1688,3 +1867,41 @@ int map_handle_direct_encap_dpp(map_ale_info_t *ale, i1905_cmdu_t *cmdu) return map_parse_dpp_message_tlv(ale, dpp_message_tlv); } + +/*####################################################################### +# MAP R6 CMDU HANDLERS # +########################################################################*/ + +/* MAP_R6 17.1.62 (type 0x8043) */ +int map_handle_early_ap_capability_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu) +{ + return map_handle_ap_capability_report(ale, cmdu); +} + +/* MAP_R6 17.1.67 (type 0x8049) */ +int map_handle_available_spectrum_inquiry(map_ale_info_t *ale, i1905_cmdu_t *cmdu) +{ + //map_channel_preference_tlv_t *channel_preference_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_CHANNEL_PREFERENCE, cmdu); /* Optional */ + map_available_spec_inq_req_tlv_t *available_spec_inq_req_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_REQUEST, cmdu); /* Mandatory */ + map_available_spec_inq_resp_tlv_t *available_spec_inq_resp_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_RESPONSE, cmdu); /* Mandatory */ + + if (!available_spec_inq_req_tlv) { + log_ctrl_e("Cannot get a valid Available Spectrum Inquiry Request TLV!"); + return -1; + } + + if (!available_spec_inq_resp_tlv) { + log_ctrl_e("Cannot get a valid Available Spectrum Inquiry Response TLV!"); + return -1; + } + + /* Send 1905 Ack */ + if (map_send_ack(ale, cmdu)) { + log_ctrl_e("%s: map_send_ack failed", __FUNCTION__); + return -1; + } + + return 0; +} + + diff --git a/source/controller/src/map_ctrl_cmdu_rx.c b/source/controller/src/map_ctrl_cmdu_rx.c index 3695ddb..1aed70c 100644 --- a/source/controller/src/map_ctrl_cmdu_rx.c +++ b/source/controller/src/map_ctrl_cmdu_rx.c @@ -80,6 +80,11 @@ static cmdu_cbs_t g_cmdu_cbs[]={ .validate_cb = map_validate_ap_autoconfig_search, .handle_cb = map_handle_ap_autoconfig_search, }, + { + .cmdu_type = CMDU_TYPE_AP_AUTOCONFIGURATION_RESPONSE, /* 1905.1 6.3.8 (type 0x0008) */ + .validate_cb = map_validate_ap_autoconfig_response, + .handle_cb = map_handle_ap_autoconfig_response, + }, { .cmdu_type = CMDU_TYPE_AP_AUTOCONFIGURATION_WSC, /* 1905.1 6.3.9 (type 0x0009) */ .validate_cb = map_validate_ap_autoconfig_wsc, @@ -213,11 +218,33 @@ static cmdu_cbs_t g_cmdu_cbs[]={ .validate_ale_cb = map_validate_direct_encap_dpp, .handle_ale_cb = map_handle_direct_encap_dpp, }, + { + .cmdu_type = CMDU_TYPE_MAP_BSS_CONFIGURATION_REQUEST, /* MAP_R3 17.1.53 (type 0x802c) */ + .validate_ale_cb = map_validate_bss_configuration_request, + .handle_ale_cb = map_handle_bss_configuration_request, + }, + { + .cmdu_type = CMDU_TYPE_MAP_BSS_CONFIGURATION_RESULT, /* MAP_R3 17.1.55 (type 0x802e) */ + .validate_ale_cb = map_validate_bss_configuration_result, + .handle_ale_cb = map_handle_bss_configuration_result, + }, { .cmdu_type = CMDU_TYPE_MAP_CHIRP_NOTIFICATION, /* MAP_R3 17.1.52 (type 0x802f) */ .validate_ale_cb = map_validate_chirp_notification, .handle_ale_cb = map_handle_chirp_notification, }, + + /* MAP R6 */ + { + .cmdu_type = CMDU_TYPE_MAP_EARLY_AP_CAPABILITY_REPORT, /* MAP_R6 17.1.62 (type 0x8043) */ + .validate_ale_cb = map_validate_early_ap_capability_report, + .handle_ale_cb = map_handle_early_ap_capability_report, + }, + { + .cmdu_type = CMDU_TYPE_MAP_AVAILABLE_SPECTRUM_INQUIRY, /* MAP_R6 17.1.67 (type 0x8049) */ + .validate_ale_cb = map_validate_available_spectrum_inquiry, + .handle_ale_cb = map_handle_available_spectrum_inquiry, + }, }; /*####################################################################### diff --git a/source/controller/src/map_ctrl_cmdu_tx.c b/source/controller/src/map_ctrl_cmdu_tx.c index 5b7dfc0..0162207 100644 --- a/source/controller/src/map_ctrl_cmdu_tx.c +++ b/source/controller/src/map_ctrl_cmdu_tx.c @@ -47,17 +47,17 @@ static const map_profile_cfg_t g_teardown_profile; ########################################################################*/ static int get_m2_profiles(map_ale_info_t *ale, map_radio_info_t *radio, i1905_wsc_m2_cfg_t *m2_profiles, uint8_t max_m2_profiles, uint8_t *ret_m2_profiles_nr, - map_traffic_separation_policy_tlv_t *tsp_tlv) + map_traffic_separation_policy_tlv_t *tsp_tlv, bool suppress_logs) { - map_controller_cfg_t *cfg = get_controller_cfg(); - uint16_t freq_bands = map_get_freq_bands(radio); - bool is_gateway = map_is_local_agent(ale); /* Note: at this moment, the controller is expected to run on a gateway */ - bool is_extender = !is_gateway; - int prim_vid = map_cfg_get()->primary_vlan_id; - uint8_t m2_profiles_nr = 0; - unsigned int i, j; - - log_ctrl_n("[get_m2_profiles] configure ale[%s] gw[%d] ext[%d] radio[%s] max_bss[%d] max_vid[%d] band[%s%s%s%s]", + map_controller_cfg_t *cfg = get_controller_cfg(); + uint16_t freq_bands = map_get_freq_bands(radio); + bool is_gateway = map_is_local_agent(ale); /* Note: at this moment, the controller is expected to run on a gateway */ + bool is_extender = !is_gateway; + int prim_vid = map_cfg_get()->primary_vlan_id; + uint8_t m2_profiles_nr = 0; + unsigned int i, j; + + log_ctrl_n_cond(!suppress_logs, "[get_m2_profiles] configure ale[%s] gw[%d] ext[%d] radio[%s] max_bss[%d] max_vid[%d] band[%s%s%s%s]", ale->al_mac_str, is_gateway ? 1 : 0, is_extender ? 1 : 0, radio->radio_id_str, radio->max_bss, ale->agent_capability.max_vid_count, freq_bands & MAP_M2_BSS_RADIO2G ? "2G " : "", @@ -126,7 +126,7 @@ static int get_m2_profiles(map_ale_info_t *ale, map_radio_info_t *radio, m2_cfg->map_ext |= (bss_state & MAP_FRONTHAUL_BSS) ? WSC_WFA_MAP_ATTR_FLAG_FRONTHAUL_BSS : 0; m2_cfg->map_ext |= (bss_state & MAP_BACKHAUL_BSS) ? WSC_WFA_MAP_ATTR_FLAG_BACKHAUL_BSS : 0; - log_ctrl_n("[get_m2_profiles] use profile idx[%d] and map_ext[0x%02x] for bss[%d]", + log_ctrl_n_cond(!suppress_logs, "[get_m2_profiles] use profile idx[%d] and map_ext[0x%02x] for bss[%d]", profile->profile_idx, m2_cfg->map_ext, m2_profiles_nr); m2_profiles_nr++; @@ -146,6 +146,7 @@ static int get_m2_profiles(map_ale_info_t *ale, map_radio_info_t *radio, return 0; teardown: + set_radio_state_teardown_sent(&radio->state); m2_profiles[0].profile = &g_teardown_profile; m2_profiles[0].map_ext = WSC_WFA_MAP_ATTR_FLAG_TEARDOWN; *ret_m2_profiles_nr = 1; @@ -169,6 +170,144 @@ static int get_wsc_m2_tlv(char *iface_name, i1905_wsc_data_t *wsc_params, i1905_ return 0; } +static bool find_aff_ap(map_agent_ap_mld_conf_tlv_t *ap_mld_conf_tlv, uint8_t mld_idx, mac_addr radio_id) +{ + uint8_t i; + + for (i = 0; i < MAX_RADIO_PER_AGENT; i++) { + if (!maccmp(ap_mld_conf_tlv->ap_mlds[mld_idx].aff_aps[i].radio_id, radio_id)) { + return true; + } + } + return false; +} + +static bool find_mld(map_agent_ap_mld_conf_tlv_t *ap_mld_conf_tlv, const char *ssid, uint8_t ssid_len, uint8_t *mld_idx) +{ + uint8_t i; + + for (i = 0; i < MAX_MLD_PER_AGENT; i++) { + if (ssid_len == ap_mld_conf_tlv->ap_mlds[i].ssid_len && !memcmp(ssid, ap_mld_conf_tlv->ap_mlds[i].ssid, ssid_len)) { + *mld_idx = i; + return true; + } + } + return false; +} + +static int get_ap_mld_config_tlv(map_ale_info_t *ale, map_agent_ap_mld_conf_tlv_t *ap_mld_conf_tlv) +{ + map_radio_info_t *radio; + map_mld_modes_t *mld_modes; + uint8_t i; + uint8_t mld_idx; + uint8_t profile_count = 0; + bool new_mld; + i1905_wsc_m2_cfg_t profiles[MAX_BSS_PER_RADIO]; + + ap_mld_conf_tlv->tlv_type = TLV_TYPE_AGENT_AP_MLD_CONFIGURATION; + + map_dm_foreach_radio(ale, radio) { + if (!radio->wifi7_caps) { + continue; + } + + mld_modes = &radio->wifi7_caps->ap_mld_modes; + profile_count = 0; + memset(profiles, 0, sizeof(profiles)); + + /* Get matching profiles for the radio*/ + if (get_m2_profiles(ale, radio, profiles, MAX_BSS_PER_RADIO, &profile_count, NULL, true)) { + continue; + } + + for (i = 0; i < profile_count; i++) { + const map_profile_cfg_t *profile = profiles[i].profile; + if(profile->mld_id < 0) { + /* non-mld profile */ + continue; + } + + new_mld = !find_mld(ap_mld_conf_tlv, profile->bss_ssid, strlen(profile->bss_ssid), &mld_idx); + if (new_mld) { + /* New MLD*/ + mld_idx = ap_mld_conf_tlv->ap_mld_nr; + + ap_mld_conf_tlv->ap_mlds[mld_idx].ssid_len = strlen(profile->bss_ssid); + memcpy(ap_mld_conf_tlv->ap_mlds[mld_idx].ssid, profile->bss_ssid, sizeof(profile->bss_ssid)); + + ap_mld_conf_tlv->ap_mlds[mld_idx].str = mld_modes->str; + ap_mld_conf_tlv->ap_mlds[mld_idx].nstr = mld_modes->nstr; + ap_mld_conf_tlv->ap_mlds[mld_idx].emlsr = mld_modes->emlsr; + ap_mld_conf_tlv->ap_mlds[mld_idx].emlmr = mld_modes->emlmr; + + ap_mld_conf_tlv->ap_mld_nr++; + } else { + /* Existing MLD*/ + ap_mld_conf_tlv->ap_mlds[mld_idx].str &= mld_modes->str; + ap_mld_conf_tlv->ap_mlds[mld_idx].nstr &= mld_modes->nstr; + ap_mld_conf_tlv->ap_mlds[mld_idx].emlsr &= mld_modes->emlsr; + ap_mld_conf_tlv->ap_mlds[mld_idx].emlmr &= mld_modes->emlmr; + } + + if (!find_aff_ap(ap_mld_conf_tlv, mld_idx, radio->radio_id)) { + maccpy(ap_mld_conf_tlv->ap_mlds[mld_idx].aff_aps[ap_mld_conf_tlv->ap_mlds[mld_idx].aff_ap_nr].radio_id, radio->radio_id); + ap_mld_conf_tlv->ap_mlds[mld_idx].aff_ap_nr++; + } + } + } + + return 0; +} + +static int get_bsta_mld_config_tlv(map_ale_info_t *ale, map_bsta_mld_conf_tlv_t *bsta_mld_conf_tlv) +{ + map_radio_info_t *radio; + map_mld_modes_t *mld_modes; + uint8_t i; + uint8_t profile_count = 0; + i1905_wsc_m2_cfg_t profiles[MAX_BSS_PER_RADIO]; + + bsta_mld_conf_tlv->tlv_type = TLV_TYPE_BACKHAUL_STA_MLD_CONFIGURATION; + + bsta_mld_conf_tlv->bsta_mld_mac_valid = 0; + bsta_mld_conf_tlv->ap_mld_mac_valid = 0; + + map_dm_foreach_radio(ale, radio) { + if (!radio->wifi7_caps) { + continue; + } + + mld_modes = &radio->wifi7_caps->bsta_mld_modes; + + bsta_mld_conf_tlv->str |= mld_modes->str; + bsta_mld_conf_tlv->nstr |= mld_modes->nstr; + bsta_mld_conf_tlv->emlsr |= mld_modes->emlsr; + bsta_mld_conf_tlv->emlmr |= mld_modes->emlmr; + + profile_count = 0; + memset(profiles, 0, sizeof(profiles)); + /* Get matching profiles for the radio*/ + if (get_m2_profiles(ale, radio, profiles, MAX_BSS_PER_RADIO, &profile_count, NULL, true)) { + continue; + } + + for (i = 0; i < profile_count; i++) { + const map_profile_cfg_t *profile = profiles[i].profile; + if(profile->mld_id < 0 || !(profile->bss_state & MAP_BACKHAUL_BSS)) { + /* non-mld profile */ + continue; + } + + bsta_mld_conf_tlv->aff_bstas[bsta_mld_conf_tlv->aff_bsta_nr].aff_bsta_mac_valid = 0; + maccpy(bsta_mld_conf_tlv->aff_bstas[bsta_mld_conf_tlv->aff_bsta_nr].radio_id, radio->radio_id); + bsta_mld_conf_tlv->aff_bsta_nr++; + } + } + + return 0; +} + static int fill_upstream_iface_link_metrics(map_ale_info_t *ale, uint8_t **tlvs) { map_ale_info_t *root_ale = get_root_ale_node(); @@ -193,7 +332,7 @@ static int fill_upstream_iface_link_metrics(map_ale_info_t *ale, uint8_t **tlvs) return 2; } -static int fill_iface_link_metrics(map_ale_info_t *ale, uint8_t **tlvs, array_list_t *list) +static int fill_iface_link_metrics(map_ale_info_t *ale, uint8_t **tlvs, int tlvs_len, array_list_t *list) { map_ale_info_t *root_ale = get_root_ale_node(); map_neighbor_link_metric_t *neigh_lm; @@ -203,6 +342,10 @@ static int fill_iface_link_metrics(map_ale_info_t *ale, uint8_t **tlvs, array_li bind_list_iterator(&iterator, list); while ((neigh_lm = get_next_list_object(&iterator))) { + if ((tlvs_nr + 2) > tlvs_len) { + return -1; + } + /* Ignore link to controller */ if (!maccmp(neigh_lm->al_mac, root_ale->al_mac)) { continue; @@ -576,19 +719,88 @@ int map_send_link_metric_response_error(map_ale_info_t *ale, uint16_t mid, uint8 return map_send_cmdu(ale->al_mac, &cmdu, &mid); } +/* 1905.1 6.3.7 (type 0x0007) */ +int map_send_autoconfig_search(void) +{ +#define NUM_AUTOCONFIG_SEARCH_TLVS 7 /* al_mac, searched_role, freq_band, supp_service, searched_service, map_profile, eom */ + i1905_cmdu_t cmdu = {0}; + uint8_t *tlvs[NUM_AUTOCONFIG_SEARCH_TLVS] = {0}; + i1905_al_mac_address_tlv_t al_mac_addr_tlv = {0}; + i1905_searched_role_tlv_t searched_role_tlv = {0}; + i1905_autoconfig_freq_band_tlv_t autoconfig_freq_band_tlv = {0}; + map_supported_service_tlv_t supported_service_tlv = {0}; + map_searched_service_tlv_t searched_service_tlv = {0}; + map_multiap_profile_tlv_t map_profile_tlv = {0}; + mac_addr mcast_address = {0}; + + i1905_get_mcast_mac(mcast_address); + + /* Get al_mac_address_tlv */ + if (i1905_get(NULL, I1905_GET_ALMAC_TLV, &al_mac_addr_tlv, NULL)) { + log_ctrl_e("AL_MAC_TLV i1905_get failed"); + return -1; + } + + /* Get searched_role_tlv */ + if (i1905_get(NULL, I1905_GET_SEARCHEDROLE_TLV, &searched_role_tlv, NULL)) { + log_ctrl_e("SEARCHED_ROLE_TLV i1905_get failed"); + return -1; + } + + /* Get autoconfig_freq_band_tlv*/ + autoconfig_freq_band_tlv.tlv_type = TLV_TYPE_AUTOCONFIG_FREQ_BAND; + autoconfig_freq_band_tlv.freq_band = IEEE80211_FREQUENCY_BAND_2_4_GHZ; + + /* Get supported_service_tlv */ + supported_service_tlv.tlv_type = TLV_TYPE_SUPPORTED_SERVICE; + supported_service_tlv.services_nr = 1; + supported_service_tlv.services[0] = MAP_SERVICE_CONTROLLER; + + /* Get searched_service_tlv */ + searched_service_tlv.tlv_type = TLV_TYPE_SEARCHED_SERVICE; + searched_service_tlv.services_nr = 1; + searched_service_tlv.services[0] = MAP_SERVICE_CONTROLLER; + + /* Get map_profile_tlv */ + map_profile_tlv.tlv_type = TLV_TYPE_MULTIAP_PROFILE; + map_profile_tlv.map_profile = get_controller_cfg()->map_profile; + + /* Create CMDU */ + cmdu.message_version = CMDU_MESSAGE_VERSION_1905_1_2013; + cmdu.message_type = CMDU_TYPE_AP_AUTOCONFIGURATION_SEARCH; + cmdu.message_id = 0; + cmdu.relay_indicator = RELAY_INDICATOR_ON; + cmdu.list_of_TLVs = tlvs; + map_strlcpy(cmdu.interface_name, "all", sizeof(cmdu.interface_name)); + + tlvs[0] = (uint8_t *)&al_mac_addr_tlv; + tlvs[1] = (uint8_t *)&searched_role_tlv; + tlvs[2] = (uint8_t *)&autoconfig_freq_band_tlv; + tlvs[3] = (uint8_t *)&supported_service_tlv; + tlvs[4] = (uint8_t *)&searched_service_tlv; + tlvs[5] = (uint8_t *)&map_profile_tlv; + + return map_send_cmdu(mcast_address, &cmdu, NULL); +} + /* 1905.1 6.3.8 (type 0x0008) */ int map_send_autoconfig_response(i1905_cmdu_t *recv_cmdu, bool ale_is_agent) { i1905_al_mac_address_tlv_t *al_mac_tlv; i1905_autoconfig_freq_band_tlv_t *autoconfig_freq_band_tlv; i1905_cmdu_t cmdu = {0}; - uint8_t *tlvs[5] = {0}; /* supp_role, supp_freq_band, supp_service, map_profile, NULL */ + uint8_t *tlvs[8] = {0}; /* supp_role, supp_freq_band, supp_service, 1905_sec_cap, map_profile, controller_cap + EOM + optional chirp value TLV */ i1905_supported_role_tlv_t supported_role_tlv = {0}; i1905_supported_freq_band_data_t supp_freq_band_data = {0}; i1905_supported_freq_band_tlv_t supported_freq_band_tlv = {0}; map_supported_service_tlv_t supported_service_tlv = {0}; map_multiap_profile_tlv_t map_profile_tlv = {0}; + map_controller_capability_tlv_t controller_cap_tlv = {0}; + map_1905_security_cap_tlv_t i1905_security_cap_tlv = {0}; + map_dpp_chirp_value_tlv_t *rx_chirp_value_tlv = NULL; + map_dpp_chirp_value_tlv_t tx_chirp_value_tlv = {0}; int8_t status = 0; + map_dpp_chirp_value_tlv_t *chirp_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_DPP_CHIRP_VALUE, recv_cmdu); do { if (!recv_cmdu) { @@ -639,6 +851,41 @@ int map_send_autoconfig_response(i1905_cmdu_t *recv_cmdu, bool ale_is_agent) map_profile_tlv.tlv_type = TLV_TYPE_MULTIAP_PROFILE; map_profile_tlv.map_profile = get_controller_cfg()->map_profile; + /* Controller Capability TLV */ + controller_cap_tlv.tlv_type = TLV_TYPE_CONTROLLER_CAPABILITY; + controller_cap_tlv.capability |= MAP_CONTROLLER_CAP_KIBMIB_COUNTER; + if (MLD_ENABLED() && !chirp_tlv) { + controller_cap_tlv.capability |= MAP_CONTROLLER_CAP_EARLY_AP_CAP; + } + + /* 1905 Layer Security Capability TLV */ + i1905_security_cap_tlv.tlv_type = TLV_TYPE_1905_LAYER_SECURITY_CAPABILITY; + i1905_security_cap_tlv.onboarding_protocol = MAP_1905_ONBOARDING_PROTOCOL_DPP; + i1905_security_cap_tlv.mic_algorithm = MAP_1905_MIC_ALGO_HMAC_SHA256; + i1905_security_cap_tlv.encryption_algorithm = MAP_1905_ENCRPYT_ALGO_AES_SIV; + + /* Get Chirp Value TLV from received cmdu */ + rx_chirp_value_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_DPP_CHIRP_VALUE, recv_cmdu); + if (rx_chirp_value_tlv) { + /* look for a matching dpp chirp hash value from the list */ + if (map_dm_get_dpp_hash(rx_chirp_value_tlv->hash)) { + log_ctrl_d("DPP Chirp Value TLV hash found in the list"); + + tx_chirp_value_tlv.tlv_type = TLV_TYPE_DPP_CHIRP_VALUE; + tx_chirp_value_tlv.hash_len = rx_chirp_value_tlv->hash_len; + + tx_chirp_value_tlv.hash = calloc(1, tx_chirp_value_tlv.hash_len); + if (!tx_chirp_value_tlv.hash) { + ERROR_EXIT(status) + } + + memcpy(tx_chirp_value_tlv.hash, rx_chirp_value_tlv->hash, rx_chirp_value_tlv->hash_len); + tx_chirp_value_tlv.hash_validity = 1; + } else { + rx_chirp_value_tlv = NULL; + } + } + /* Create CMDU */ cmdu.message_version = CMDU_MESSAGE_VERSION_1905_1_2013; cmdu.message_type = CMDU_TYPE_AP_AUTOCONFIGURATION_RESPONSE; @@ -651,6 +898,11 @@ int map_send_autoconfig_response(i1905_cmdu_t *recv_cmdu, bool ale_is_agent) tlvs[1] = (uint8_t *)&supported_freq_band_tlv; tlvs[2] = (uint8_t *)&supported_service_tlv; tlvs[3] = (uint8_t *)&map_profile_tlv; + tlvs[4] = (uint8_t *)&controller_cap_tlv; + tlvs[5] = (uint8_t *)&i1905_security_cap_tlv; + if (rx_chirp_value_tlv) { + tlvs[7] = (uint8_t *)&tx_chirp_value_tlv; + } /* The spec implies (certification test plan) the messages to be sent to the AL MAC address of the 1905 device intead the source mac address, CMDUs that has ALMAC tlv can be handled this way @@ -660,6 +912,10 @@ int map_send_autoconfig_response(i1905_cmdu_t *recv_cmdu, bool ale_is_agent) } } while (0); + if (tx_chirp_value_tlv.hash) { + free(tx_chirp_value_tlv.hash); + } + return status; } @@ -669,12 +925,16 @@ int map_send_autoconfig_wsc_m2(map_ale_info_t *ale, map_radio_info_t *radio, i19 i1905_cmdu_t cmdu = {0}; i1905_wsc_tlv_t *wsc_m1_tlv; i1905_wsc_tlv_t wsc_m2_tlv[MAX_BSS_PER_RADIO] = {{0}}; + map_agent_ap_mld_conf_tlv_t ap_mld_conf_tlv = {0}; + map_bsta_mld_conf_tlv_t bsta_mld_conf_tlv = {0}; map_ap_radio_identifier_tlv_t radio_id_tlv = {0}; map_default_8021q_settings_tlv_t default_8021q_settings_tlv = {0}; map_traffic_separation_policy_tlv_t traffic_separation_policy_tlv = {0}; map_cfg_t *cfg = map_cfg_get(); bool add_ts_tlv = ale->agent_capability.profile_2_ap_cap_valid; bool add_def_8021q_tlv = TS_ENABLED() && add_ts_tlv; + bool add_ap_mld_conf_tlv = MLD_ENABLED() && map_is_radio_ap_mld_capable(ale, radio); + bool add_bsta_mld_conf_tlv = MLD_ENABLED() && map_is_radio_bsta_mld_capable(ale, radio); int8_t status = 0; uint8_t tlvs_nr = 0; uint8_t wsc_tlv_count = 0; @@ -710,13 +970,17 @@ int map_send_autoconfig_wsc_m2(map_ale_info_t *ale, map_radio_info_t *radio, i19 /* Get to be configured profiles */ if (get_m2_profiles(ale, radio, m2_profiles, MAX_BSS_PER_RADIO, &wsc_tlv_count, - add_ts_tlv ? &traffic_separation_policy_tlv : NULL)) { + add_ts_tlv ? &traffic_separation_policy_tlv : NULL, false)) { ERROR_EXIT(status) } + add_ap_mld_conf_tlv &= !is_radio_teardown_sent(radio->state); + add_bsta_mld_conf_tlv &= !is_radio_teardown_sent(radio->state); + /* Get total number of TLVs required and allocate memory accordingly */ tlvs_nr = wsc_tlv_count + /* radio_id */ 1 + /* eom */ 1 + - (add_ts_tlv ? 1 : 0) + (add_def_8021q_tlv ? 1 : 0); + (add_ts_tlv ? 1 : 0) + (add_def_8021q_tlv ? 1 : 0) + + (add_ap_mld_conf_tlv ? 1 : 0) + (add_bsta_mld_conf_tlv ? 1 : 0); if (!(cmdu.list_of_TLVs = calloc(tlvs_nr, sizeof(uint8_t *)))) { log_ctrl_e("calloc failed"); @@ -755,6 +1019,22 @@ int map_send_autoconfig_wsc_m2(map_ale_info_t *ale, map_radio_info_t *radio, i19 cmdu.list_of_TLVs[current_tlv++] = (uint8_t *)&wsc_m2_tlv[i]; } + if (add_ap_mld_conf_tlv) { + if (get_ap_mld_config_tlv(ale, &ap_mld_conf_tlv)) { + ERROR_EXIT(status) + } + + cmdu.list_of_TLVs[current_tlv++] = (uint8_t *)&ap_mld_conf_tlv; + } + + if (add_bsta_mld_conf_tlv) { + if (get_bsta_mld_config_tlv(ale, &bsta_mld_conf_tlv)) { + ERROR_EXIT(status) + } + + cmdu.list_of_TLVs[current_tlv++] = (uint8_t *)&bsta_mld_conf_tlv; + } + if (0 != status) { break; } @@ -824,19 +1104,38 @@ static int send_autoconfig_renew(const char *ifname, mac_addr dest_mac, uint8_t } /* 1905.1 6.3.10 (type 0x000A) */ -int map_send_autoconfig_renew(uint8_t freq_band, uint16_t *mid) +int map_send_autoconfig_renew(uint8_t freq_band, uint16_t *mid, bool reset_onboarding) { mac_addr dest_mac; + int ret; i1905_get_mcast_mac(dest_mac); - return send_autoconfig_renew("all", dest_mac, freq_band, RELAY_INDICATOR_ON, mid); + ret = send_autoconfig_renew("all", dest_mac, freq_band, RELAY_INDICATOR_ON, mid); + + if (ret == 0 && reset_onboarding) { + map_reset_all_agent_nodes_onboarding_status(); + } + + return ret; } /* 1905.1 6.3.10 (type 0x000A) */ -int map_send_autoconfig_renew_ucast(map_ale_info_t *ale, uint8_t freq_band, uint16_t *mid) +int map_send_autoconfig_renew_ucast(map_ale_info_t *ale, uint8_t freq_band, uint16_t *mid, bool reset_onboarding) { - return send_autoconfig_renew(ale->iface_name, ale->al_mac, freq_band, RELAY_INDICATOR_OFF, mid); + int ret; + + if (ale == NULL) { + return -1; + } + + ret = send_autoconfig_renew(ale->iface_name, ale->al_mac, freq_band, RELAY_INDICATOR_OFF, mid); + + if (ret == 0 && reset_onboarding) { + map_reset_agent_node_onboarding_status(ale); + } + + return ret; } /* 1905.1 6.3.13 (type 0x0004) */ @@ -1107,6 +1406,7 @@ int map_send_channel_selection_request(void *args, uint16_t *mid) uint8_t idx = 0; map_ale_info_t *ale; map_radio_info_t *radio; + int ret = -1; memset(chan_pref_tlvs, 0, sizeof(chan_pref_tlvs)); memset(transmit_pwr_tlvs, 0, sizeof(transmit_pwr_tlvs)); @@ -1122,7 +1422,10 @@ int map_send_channel_selection_request(void *args, uint16_t *mid) } /* Channel preference TLV */ - map_fill_channel_preference_tlv(&chan_pref_tlvs[idx], radio, pref_type->pref); + if (map_fill_channel_preference_tlv(&chan_pref_tlvs[idx], radio, pref_type->pref)) { + log_ctrl_e("map_fill_channel_preference_tlv failed"); + goto cleanup; + } tlvs[tlvs_nr++] = (uint8_t *)&chan_pref_tlvs[idx]; /* Optional transmit power TLV */ @@ -1141,7 +1444,14 @@ int map_send_channel_selection_request(void *args, uint16_t *mid) cmdu.list_of_TLVs = tlvs; map_strlcpy(cmdu.interface_name, ale->iface_name, sizeof(cmdu.interface_name)); - return map_send_cmdu(ale->al_mac, &cmdu, mid); + ret = map_send_cmdu(ale->al_mac, &cmdu, mid); + +cleanup: + for (idx = 0; idx < tlvs_nr; idx++) { + free_1905_TLV_structure2(tlvs[idx]); + } + + return ret; } /* MAP_R2 17.1.14 (type 0x8009) */ @@ -1164,6 +1474,25 @@ int map_send_client_capability_query(void *args, uint16_t *mid) return send_cmdu_one_tlv(ale, CMDU_TYPE_MAP_CLIENT_CAPABILITY_QUERY, TLV_TYPE_CLIENT_INFO, &tlv, mid); } +/* TODO: find better way if there will be many CMDU to be sent using sta or mld_sta */ +int map_send_mld_client_capability_query(void *args, uint16_t *mid) +{ + map_sta_mld_info_t *sta_mld = args; + map_ap_mld_info_t *ap_mld; + map_ale_info_t *ale; + map_client_info_tlv_t tlv = {0}; + + if (!sta_mld || !(ap_mld = sta_mld->ap_mld) || !(ale = ap_mld->ale)) { + return -1; + } + + /* Client info tlv */ + maccpy(tlv.bssid, ap_mld->mac); + maccpy(tlv.sta_mac, sta_mld->mac); + + return send_cmdu_one_tlv(ale, CMDU_TYPE_MAP_CLIENT_CAPABILITY_QUERY, TLV_TYPE_CLIENT_INFO, &tlv, mid); +} + /* MAP_R2 17.1.16 (type 0x800B) */ int map_send_ap_metrics_query(map_ale_info_t *ale, mac_addr *bssids, uint8_t bssid_nr, uint16_t *mid) { @@ -1232,7 +1561,7 @@ int map_send_combined_infrastructure_metrics(map_ale_info_t *ale, uint16_t *mid) tlvs_nr += cnt; /* Ethernet links */ - if ((cnt = fill_iface_link_metrics(foreach_ale, &tlvs[tlvs_nr], foreach_ale->eth_neigh_link_metric_list)) < 0) { + if ((cnt = fill_iface_link_metrics(foreach_ale, &tlvs[tlvs_nr], MAX_TLVS_IN_COMBINED_INFRA - tlvs_nr, foreach_ale->eth_neigh_link_metric_list)) < 0) { goto cleanup; } tlvs_nr += cnt; @@ -1241,11 +1570,18 @@ int map_send_combined_infrastructure_metrics(map_ale_info_t *ale, uint16_t *mid) map_dm_foreach_radio(foreach_ale, foreach_radio) { map_dm_foreach_bss(foreach_radio, foreach_bss) { + if (tlvs_nr >= MAX_TLVS_IN_COMBINED_INFRA) { + goto cleanup; + } + /* Wireless links */ - if ((cnt = fill_iface_link_metrics(foreach_ale, &tlvs[tlvs_nr], foreach_bss->neigh_link_metric_list)) < 0) { + if ((cnt = fill_iface_link_metrics(foreach_ale, &tlvs[tlvs_nr], MAX_TLVS_IN_COMBINED_INFRA - tlvs_nr, foreach_bss->neigh_link_metric_list)) < 0) { goto cleanup; } tlvs_nr += cnt; + if (tlvs_nr >= MAX_TLVS_IN_COMBINED_INFRA) { + goto cleanup; + } if ((ap_metrics_tlv = map_get_ap_metrics_tlv(foreach_bss))) { tlvs[tlvs_nr++] = (uint8_t *)ap_metrics_tlv; @@ -1521,12 +1857,96 @@ int map_send_dpp_chirp_notification(map_dpp_chirp_value_tlv_t *chirp_value_tlv_l return ret; } +/* MAP_R3 17.1.54 (type 0x802D) */ +int map_send_bss_config_response(map_ale_info_t *ale, map_bss_configuration_response_tlv_t *bss_config_resp_tlv, uint16_t *mid) +{ + i1905_cmdu_t cmdu = {0}; + map_cfg_t *cfg = map_cfg_get(); + map_default_8021q_settings_tlv_t default_8021q_settings_tlv = {0}; + map_traffic_separation_policy_tlv_t traffic_separation_policy_tlv = {0}; + bool add_ts_tlv = ale->agent_capability.profile_2_ap_cap_valid; + bool add_def_8021q_tlv = TS_ENABLED() && add_ts_tlv; + int8_t status = 0; + uint8_t tlvs_nr = 0; + uint8_t current_tlv = 0; + + do { + /* Create cmdu */ + cmdu.message_version = CMDU_MESSAGE_VERSION_1905_1_2013; + cmdu.message_type = CMDU_TYPE_MAP_BSS_CONFIGURATION_RESPONSE; + cmdu.message_id = 0; + cmdu.relay_indicator = RELAY_INDICATOR_OFF; + map_strlcpy(cmdu.interface_name, ale->iface_name, sizeof(cmdu.interface_name)); + + /* Traffic separation tlvs */ + if (add_def_8021q_tlv) { + map_fill_default_8021q_settings_tlv(cfg, &default_8021q_settings_tlv); + } + if (add_ts_tlv) { + if (TS_ENABLED()) { + map_fill_traffic_separation_policy_tlv(get_controller_cfg(), cfg->primary_vlan_id, + ale->agent_capability.max_vid_count, + &traffic_separation_policy_tlv); + } else { + /* If TS is disabled add an empty ts tlv so agent can remove any existing vlan */ + map_fill_empty_traffic_separation_policy_tlv(&traffic_separation_policy_tlv); + } + } + + /* Get total number of TLVs required and allocate memory accordingly */ + tlvs_nr = (add_ts_tlv ? 1 : 0) + (add_def_8021q_tlv ? 1 : 0) + /* bss cfg resp*/ 1; + + if (!(cmdu.list_of_TLVs = calloc(tlvs_nr, sizeof(uint8_t *)))) { + log_ctrl_e("calloc failed"); + ERROR_EXIT(status) + } + + /* Default 8021q settings TLV and traffic separation policy TLV */ + if (add_def_8021q_tlv) { + cmdu.list_of_TLVs[current_tlv++] = (uint8_t *)&default_8021q_settings_tlv; + } + if (add_ts_tlv) { + cmdu.list_of_TLVs[current_tlv++] = (uint8_t *)&traffic_separation_policy_tlv; + } + + cmdu.list_of_TLVs[current_tlv++] = (uint8_t *)bss_config_resp_tlv; + + if (map_send_cmdu(ale->al_mac, &cmdu, mid)) { + ERROR_EXIT(status) + } + } while (0); + + free(cmdu.list_of_TLVs); + + return status; +} + /* MAP_R3 17.1.56 (type 0x802A) */ int map_send_direct_encap_dpp(map_ale_info_t *ale, map_dpp_message_tlv_t *dpp_message_tlv, uint16_t *mid) { return send_cmdu_one_tlv(ale, CMDU_TYPE_MAP_DIRECT_ENCAP_DPP, TLV_TYPE_DPP_MESSAGE, dpp_message_tlv, mid); } +/* MAP_R3 17.1.58 (type 0x8035) */ +int map_send_agent_list_message(map_ale_info_t *ale, uint16_t *mid) +{ + map_agent_list_tlv_t tlv; + map_ale_info_t *tmp; + + tlv.agent_nr = 0; + map_dm_foreach_ale(tmp) { + map_agent_entry_t *agent = &tlv.entries[tlv.agent_nr]; + maccpy(agent->al_mac, tmp->al_mac); + agent->map_profile = tmp->map_profile; + + /* security field needs to be set ONLY for Multi-AP Profile-3 devices onboarded with DPP Onboarding */ + agent->security = (tmp->dpp_info.onboarding_status && tmp->map_profile >= MAP_PROFILE_3) ? 0x01 : 0x00; + tlv.agent_nr++; + } + + return send_cmdu_one_tlv(ale, CMDU_TYPE_MAP_AGENT_LIST, TLV_TYPE_AGENT_LIST, &tlv, mid); +} + /*####################################################################### # RAW # ########################################################################*/ diff --git a/source/controller/src/map_ctrl_cmdu_validator.c b/source/controller/src/map_ctrl_cmdu_validator.c index 033d6e4..6b0f4f3 100644 --- a/source/controller/src/map_ctrl_cmdu_validator.c +++ b/source/controller/src/map_ctrl_cmdu_validator.c @@ -126,13 +126,14 @@ int map_validate_ap_autoconfig_search(i1905_cmdu_t *cmdu) int supp_service_tlv_nr = 0; int searched_service_tlv_nr = 0; int multiap_profile_tlv_nr = 0; + int chirp_value_tlv_nr = 0; int i; i1905_foreach_tlv_in_cmdu(tlv, cmdu, idx) { switch (*tlv) { case TLV_TYPE_AL_MAC_ADDRESS: al_mac_tlv_nr++; - break; + break; case TLV_TYPE_SEARCHED_ROLE: { i1905_searched_role_tlv_t *sr_tlv = (i1905_searched_role_tlv_t *)tlv; searched_role_tlv_nr++; @@ -147,7 +148,7 @@ int map_validate_ap_autoconfig_search(i1905_cmdu_t *cmdu) } case TLV_TYPE_AUTOCONFIG_FREQ_BAND: freq_band_tlv_nr++; - break; + break; case TLV_TYPE_SUPPORTED_SERVICE: { supp_service_tlv_nr++; break; @@ -171,10 +172,13 @@ int map_validate_ap_autoconfig_search(i1905_cmdu_t *cmdu) } case TLV_TYPE_MULTIAP_PROFILE: multiap_profile_tlv_nr++; - break; + break; + case TLV_TYPE_DPP_CHIRP_VALUE: + chirp_value_tlv_nr++; + break; default: log_unexpected_tlv(cmdu, *tlv); - break; + break; } } @@ -184,6 +188,69 @@ int map_validate_ap_autoconfig_search(i1905_cmdu_t *cmdu) CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_SUPPORTED_SERVICE, supp_service_tlv_nr ); CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_SEARCHED_SERVICE, searched_service_tlv_nr); CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_MULTIAP_PROFILE, multiap_profile_tlv_nr ); + CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_DPP_CHIRP_VALUE, chirp_value_tlv_nr ); + + return 0; +} + +/* 1905.1 6.3.7 (type 0x0007) */ +int map_validate_ap_autoconfig_response(i1905_cmdu_t *cmdu) +{ + uint8_t *tlv; + size_t idx; + int supported_role_tlv_nr = 0; + int supported_freq_band_tlv_nr = 0; + int supp_service_tlv_nr = 0; + int multiap_profile_tlv_nr = 0; + int i; + + i1905_foreach_tlv_in_cmdu(tlv, cmdu, idx) { + switch (*tlv) { + case TLV_TYPE_SUPPORTED_ROLE: { + i1905_supported_role_tlv_t *sr_tlv = (i1905_supported_role_tlv_t *)tlv; + supported_role_tlv_nr++; + + /* Supported role must be registrar */ + if (sr_tlv->role != IEEE80211_ROLE_REGISTRAR) { + log_ctrl_e("invalid supported role[%d] tlv[%s] cmdu[%s]", + sr_tlv->role, i1905_tlv_type_to_string(*tlv), i1905_cmdu_type_to_string(cmdu->message_type)); + return -1; + } + break; + } + case TLV_TYPE_SUPPORTED_FREQ_BAND: + supported_freq_band_tlv_nr++; + break; + case TLV_TYPE_SUPPORTED_SERVICE: { + map_supported_service_tlv_t* ss_tlv = (map_supported_service_tlv_t*)tlv; + supp_service_tlv_nr++; + + /* One service must be agent */ + for (i = 0; i < ss_tlv->services_nr; i++) { + if (ss_tlv->services[i] == MAP_SERVICE_CONTROLLER) { + break; + } + } + + if (i == ss_tlv->services_nr) { + log_ctrl_e("no controller in supported services tlv[%s] cmdu[%s]", + i1905_tlv_type_to_string(*tlv), i1905_cmdu_type_to_string(cmdu->message_type)); + } + break; + } + case TLV_TYPE_MULTIAP_PROFILE: + multiap_profile_tlv_nr++; + break; + default: + log_unexpected_tlv(cmdu, *tlv); + break; + } + } + + CHECK_ONE_TLV (TLV_TYPE_SUPPORTED_ROLE, supported_role_tlv_nr ); + CHECK_ONE_TLV (TLV_TYPE_SUPPORTED_FREQ_BAND, supported_freq_band_tlv_nr ); + CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_SUPPORTED_SERVICE, supp_service_tlv_nr ); + CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_MULTIAP_PROFILE, multiap_profile_tlv_nr ); return 0; } @@ -303,14 +370,17 @@ int map_validate_topology_query(i1905_cmdu_t *cmdu) /* 1905.1 6.3.3 (type 0x0002) */ int map_validate_topology_response(i1905_cmdu_t *cmdu) { - uint8_t *src_mac = cmdu->cmdu_stream.src_mac_addr; - uint8_t *tlv = NULL; - map_ale_info_t *ale = NULL; + uint8_t *src_mac = cmdu->cmdu_stream.src_mac_addr; + uint8_t *tlv = NULL; + map_ale_info_t *ale = NULL; size_t idx; - int dev_info_tlv_nr = 0; - int supp_service_tlv_nr = 0; - int ap_op_bss_nr = 0; - int assoc_clients_tlv_nr = 0; + int dev_info_tlv_nr = 0; + int supp_service_tlv_nr = 0; + int ap_op_bss_nr = 0; + int assoc_clients_tlv_nr = 0; + int bss_cfg_report_tlv_nr = 0; + int ap_mld_cfg_tlv_nr = 0; + int bsta_mld_cfg_tlv_nr = 0; i1905_foreach_tlv_in_cmdu(tlv, cmdu, idx) { switch (*tlv) { @@ -353,16 +423,34 @@ int map_validate_topology_response(i1905_cmdu_t *cmdu) case TLV_TYPE_MULTIAP_PROFILE: /* Optional */ break; + case TLV_TYPE_BACKHAUL_STA_RADIO_CAPABILITIES: + /* Optional */ + break; + case TLV_TYPE_BSS_CONFIGURATION_REPORT: + bss_cfg_report_tlv_nr++; + break; + case TLV_TYPE_AGENT_AP_MLD_CONFIGURATION: + ap_mld_cfg_tlv_nr++; + break; + case TLV_TYPE_BACKHAUL_STA_MLD_CONFIGURATION: + bsta_mld_cfg_tlv_nr++; + break; + case TLV_TYPE_ASSOCIATED_STA_MLD_CONFIGURATION: + /* Optional */ + break; default: log_unexpected_tlv(cmdu, *tlv); break; } } - CHECK_ONE_TLV (TLV_TYPE_DEVICE_INFORMATION, dev_info_tlv_nr ); - CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_SUPPORTED_SERVICE, supp_service_tlv_nr ); - CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_AP_OPERATIONAL_BSS, ap_op_bss_nr ); - CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_ASSOCIATED_CLIENTS, assoc_clients_tlv_nr); + CHECK_ONE_TLV (TLV_TYPE_DEVICE_INFORMATION, dev_info_tlv_nr ); + CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_SUPPORTED_SERVICE, supp_service_tlv_nr ); + CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_AP_OPERATIONAL_BSS, ap_op_bss_nr ); + CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_ASSOCIATED_CLIENTS, assoc_clients_tlv_nr ); + CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_BSS_CONFIGURATION_REPORT, bss_cfg_report_tlv_nr); + CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_AGENT_AP_MLD_CONFIGURATION, ap_mld_cfg_tlv_nr ); + CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_BACKHAUL_STA_MLD_CONFIGURATION, bsta_mld_cfg_tlv_nr ); return 0; } @@ -545,12 +633,27 @@ int map_validate_ap_capability_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu) case TLV_TYPE_METRIC_COLLECTION_INTERVAL: /* Profile 2 */ metric_coll_int_tlv_nr++; break; + case TLV_TYPE_AP_RADIO_ADVANCED_CAPABILITIES: + /* Optional */ + break; case TLV_TYPE_AP_WIFI6_CAPABILITIES: /* Profile 3 */ /* Optional */ break; case TLV_TYPE_DEVICE_INVENTORY: /* Profile 3 */ /* Optional */ break; + case TLV_TYPE_1905_LAYER_SECURITY_CAPABILITY: /* Profile 3 */ + /* Optional */ + break; + case TLV_TYPE_AKM_SUITE_CAPABILITIES: /* Profile 3 */ + /* Optional */ + break; + case TLV_TYPE_WIFI7_AGENT_CAPABILITIES: + /* Optional */ + break; + case TLV_TYPE_EHT_OPERATIONS: + /* Optional */ + break; default: log_unexpected_tlv(cmdu, *tlv); break; @@ -563,10 +666,12 @@ int map_validate_ap_capability_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu) /* Profile 2 requirements */ if (ale->map_profile >= MAP_PROFILE_2) { - CHECK_ONE_TLV(TLV_TYPE_CHANNEL_SCAN_CAPABILITIES, channel_scan_cap_tlv_nr); - CHECK_ONE_TLV(TLV_TYPE_CAC_CAPABILITIES, cac_cap_tlv_nr ); CHECK_ONE_TLV(TLV_TYPE_PROFILE2_AP_CAPABILITY, profile2_ap_cap_tlv_nr ); - CHECK_ONE_TLV(TLV_TYPE_METRIC_COLLECTION_INTERVAL, metric_coll_int_tlv_nr ); + if (cmdu->message_type == CMDU_TYPE_MAP_AP_CAPABILITY_REPORT) { + CHECK_ONE_TLV(TLV_TYPE_CHANNEL_SCAN_CAPABILITIES, channel_scan_cap_tlv_nr); + CHECK_ONE_TLV(TLV_TYPE_CAC_CAPABILITIES, cac_cap_tlv_nr ); + CHECK_ONE_TLV(TLV_TYPE_METRIC_COLLECTION_INTERVAL, metric_coll_int_tlv_nr ); + } } return 0; @@ -593,6 +698,9 @@ int map_validate_channel_preference_report(UNUSED map_ale_info_t *ale, i1905_cmd case TLV_TYPE_CAC_STATUS_REPORT: cac_status_report_tlv_nr++; break; + case TLV_TYPE_EHT_OPERATIONS: + /* Optional */ + break; default: log_unexpected_tlv(cmdu, *tlv); break; @@ -691,6 +799,12 @@ int map_validate_ap_metrics_response(UNUSED map_ale_info_t *ale, i1905_cmdu_t *c case TLV_TYPE_ASSOCIATED_WIFI6_STA_STATUS_REPORT: /* Profile 3 */ /* Optional */ break; + case TLV_TYPE_AFFILIATED_AP_METRICS: + /* Optional */ + break; + case TLV_TYPE_AFFILIATED_STA_METRICS: + /* Optional */ + break; default: log_unexpected_tlv(cmdu, *tlv); break; @@ -1020,3 +1134,108 @@ int map_validate_direct_encap_dpp(UNUSED map_ale_info_t *ale, i1905_cmdu_t *cmdu { return expect_one_tlv_type(cmdu, TLV_TYPE_DPP_MESSAGE); } + +/* MAP_R3 17.1.53 (type 0x802c) */ +int map_validate_bss_configuration_request(UNUSED map_ale_info_t *ale, i1905_cmdu_t *cmdu) +{ + uint8_t *tlv; + size_t idx; + int map_profile_tlv_nr = 0; + int supported_service_tlv_nr = 0; + int ap_radio_basic_cap_tlv_nr = 0; + int bh_sta_radio_cap_tlv_nr = 0; + int profile2_ap_cap_tlv_nr = 0; + int ap_radio_advanced_cap_tlv_nr = 0; + int bss_config_request_tlv_nr = 0; + + i1905_foreach_tlv_in_cmdu(tlv, cmdu, idx) { + switch (*tlv) { + case TLV_TYPE_MULTIAP_PROFILE: + map_profile_tlv_nr++; + break; + case TLV_TYPE_SUPPORTED_SERVICE: + supported_service_tlv_nr++; + break; + case TLV_TYPE_AP_RADIO_BASIC_CAPABILITIES: + ap_radio_basic_cap_tlv_nr++; + break; + case TLV_TYPE_BACKHAUL_STA_RADIO_CAPABILITIES: + bh_sta_radio_cap_tlv_nr++; + break; + case TLV_TYPE_PROFILE2_AP_CAPABILITY: + profile2_ap_cap_tlv_nr++; + break; + case TLV_TYPE_AP_RADIO_ADVANCED_CAPABILITIES: + ap_radio_advanced_cap_tlv_nr++; + break; + case TLV_TYPE_BSS_CONFIGURATION_REQUEST: + bss_config_request_tlv_nr++; + break; + case TLV_TYPE_AKM_SUITE_CAPABILITIES: + /* TODO It is mandatory according to standard. But it is not implemented in older bcm agents. + * Made it optional to not break flow. + */ + break; + default: + log_unexpected_tlv(cmdu, *tlv); + break; + } + } + CHECK_ONE_TLV(TLV_TYPE_MULTIAP_PROFILE, map_profile_tlv_nr); + CHECK_ONE_TLV(TLV_TYPE_SUPPORTED_SERVICE, supported_service_tlv_nr); + CHECK_ONE_OR_MORE_TLV(TLV_TYPE_AP_RADIO_BASIC_CAPABILITIES, ap_radio_basic_cap_tlv_nr); + CHECK_ONE_TLV(TLV_TYPE_PROFILE2_AP_CAPABILITY, profile2_ap_cap_tlv_nr); + CHECK_ONE_OR_MORE_TLV(TLV_TYPE_AP_RADIO_ADVANCED_CAPABILITIES, ap_radio_advanced_cap_tlv_nr); + CHECK_ONE_TLV(TLV_TYPE_BSS_CONFIGURATION_REQUEST, bss_config_request_tlv_nr); + + return 0; +} + +/* MAP_R3 17.1.55 (type 0x802e) */ +int map_validate_bss_configuration_result(UNUSED map_ale_info_t *ale, i1905_cmdu_t *cmdu) +{ + return expect_one_tlv_type(cmdu, TLV_TYPE_BSS_CONFIGURATION_REPORT); +} + +/*####################################################################### +# MAP R6 CMDU VALIDATION # +########################################################################*/ +/* MAP_R6 17.1.62 (type 0x8043) */ +int map_validate_early_ap_capability_report(map_ale_info_t *ale, i1905_cmdu_t *cmdu) +{ + return map_validate_ap_capability_report(ale, cmdu); +} + +/* MAP_R6 17.1.67 (type 0x8049) */ +int map_validate_available_spectrum_inquiry(UNUSED map_ale_info_t *ale, i1905_cmdu_t *cmdu) +{ + uint8_t *tlv; + size_t idx; + int channel_preference_tlv_nr = 0; + int available_spec_inq_req_tlv_nr = 0; + int available_spec_inq_resp_tlv_nr = 0; + + i1905_foreach_tlv_in_cmdu(tlv, cmdu, idx) { + switch (*tlv) { + case TLV_TYPE_CHANNEL_PREFERENCE: + channel_preference_tlv_nr++; + break; + case TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_REQUEST: + available_spec_inq_req_tlv_nr++; + break; + case TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_RESPONSE: + available_spec_inq_resp_tlv_nr++; + break; + default: + log_unexpected_tlv(cmdu, *tlv); + break; + } + } + + CHECK_ZERO_OR_ONE_TLV(TLV_TYPE_CHANNEL_PREFERENCE, channel_preference_tlv_nr ); + CHECK_ONE_TLV(TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_REQUEST, available_spec_inq_req_tlv_nr); + CHECK_ONE_TLV(TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_RESPONSE, available_spec_inq_resp_tlv_nr); + + return 0; +} + diff --git a/source/controller/src/map_ctrl_config.c b/source/controller/src/map_ctrl_config.c index f43d895..4a5c47d 100644 --- a/source/controller/src/map_ctrl_config.c +++ b/source/controller/src/map_ctrl_config.c @@ -121,15 +121,11 @@ static void profile_update_cb(void) as per section 7.1 in the Multiap specification. */ - if (map_send_autoconfig_renew(IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA)) { + if (map_send_autoconfig_renew(IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA, true)) { log_ctrl_e("map_send_autoconfig_renew failed"); break; } - /* This will also cause resending of policy config request which is required to update - traffic separation policy - */ - map_reset_all_agent_nodes_onboarding_status(); } while(0); } @@ -223,14 +219,14 @@ static map_cfg_cbs_t g_monitor_cfg_cbs = { }; static map_cfg_cbs_t g_running_cfg_cbs = { - .enable_cb = enable_running_cb, - .update_cb = update_cb, - .profile_update_cb = profile_update_cb, - .allowed_channel_list_update_cb = allowed_channel_list_update_cb, - .allowed_bandwidth_update_cb = allowed_bandwidth_update_cb, - .bandlock_5g_update_cb = bandlock_5g_update_cb, - .radio_channel_cb = radio_channel_cb, - .radio_bandwidth_cb = radio_bandwidth_cb, + .enable_cb = enable_running_cb, + .update_cb = update_cb, + .profile_update_cb = profile_update_cb, + .allowed_channel_list_update_cb = allowed_channel_list_update_cb, + .allowed_bandwidth_update_cb = allowed_bandwidth_update_cb, + .bandlock_5g_update_cb = bandlock_5g_update_cb, + .radio_channel_cb = radio_channel_cb, + .radio_bandwidth_cb = radio_bandwidth_cb, }; /*####################################################################### diff --git a/source/controller/src/map_ctrl_emex_tlv_handler.c b/source/controller/src/map_ctrl_emex_tlv_handler.c index 8d5b233..4372910 100644 --- a/source/controller/src/map_ctrl_emex_tlv_handler.c +++ b/source/controller/src/map_ctrl_emex_tlv_handler.c @@ -28,9 +28,217 @@ #define max(a, b) ((a) > (b) ? (a) : (b)) +/*####################################################################### +# GLOBALS # +########################################################################*/ +static map_emex_common_feature_list_t g_common_feature_list; + /*####################################################################### # LOCAL FUNCTIONS # ########################################################################*/ +map_emex_common_feature_list_t *controller_get_emex_common_feature_list(void) +{ + return &g_common_feature_list; +} + +static int8_t map_emex_calculate_common_feature_list(void) +{ + map_ale_info_t *ale_info; + uint16_t common_feature_count = 0; + uint16_t i, j; + + /* Temporary array to manipulate */ + map_emex_supported_feature_t common_feature_list[MAX_EMEX_FEATURE_COUNT] = {{0}}; + + /* Clear previous common feature list */ + controller_get_emex_common_feature_list()->feature_count = 0; + SFREE(controller_get_emex_common_feature_list()->feature_list); + + map_dm_foreach_agent_ale(ale_info) { + /* Do not calculate third party agents */ +#ifndef UNIT_TEST + if (!ale_info->easymesh_plus) { + continue; + } +#endif + if (ale_info->emex.feature_profile.feature_count > 0) { + + /* If common feature list is not calculated before, pick first ale's list as common feature list. */ + if (common_feature_count == 0) { + for (i = 0; i < ale_info->emex.feature_profile.feature_count; i++) { + common_feature_list[i].id = ale_info->emex.feature_profile.feature_list[i].id; + common_feature_list[i].version = ale_info->emex.feature_profile.feature_list[i].version; + } + common_feature_count = ale_info->emex.feature_profile.feature_count; + continue; + } + + /* Check common features that is not exist in incoming ALE's feature list. Remove from common list if it is not exist. */ + for (i = 0; i < common_feature_count; i++) { + bool found = false; + + /* Search for feature existency */ + for (j = 0; j < ale_info->emex.feature_profile.feature_count; j++) { + if (common_feature_list[i].id == ale_info->emex.feature_profile.feature_list[j].id) { + if (common_feature_list[i].version > ale_info->emex.feature_profile.feature_list[j].version) { + /* ALE has an older version of this feature. Use older version for feature list. */ + common_feature_list[i].version = ale_info->emex.feature_profile.feature_list[j].version; + } + found = true; + break; + } + } + + /* Remove if it is not exist in upcoming ale */ + if (found == false) { + /* Shift all elements and reduce feature list size */ + for (j=i; jfeature_count = common_feature_count; + controller_get_emex_common_feature_list()->feature_list = calloc(1, + common_feature_count * sizeof(map_emex_supported_feature_t)); + if (controller_get_emex_common_feature_list()->feature_list == NULL) { + log_ctrl_e("Cannot allocate memory!"); + return 0; + } + + for (i = 0; i < common_feature_count; i++) { + controller_get_emex_common_feature_list()->feature_list[i].id = common_feature_list[i].id; + controller_get_emex_common_feature_list()->feature_list[i].version = common_feature_list[i].version; + } + + return 1; +} + +static int8_t parse_emex_feature_profile(struct vendorSpecificTLV* vendor_tlv, map_ale_info_t *ale, bool *changed) +{ + int i; + int8_t ret = 0; + + do { + /* TLV len: emex_tlv_id(2) + agent_version(4) + feature_count(2) */ + unsigned short min_tlv_len = 2 + 4 + 2; + if (vendor_tlv->m_nr < min_tlv_len) { + log_ctrl_e("Minimal TLV size check failed!"); + break; + } + if (ale == NULL) { + log_ctrl_e("ale is not exist"); + break; + } + + /* Iterate 2-byte AirTies EM+ TLV ID to get payload data. */ + uint8_t *data = vendor_tlv->m + sizeof(uint16_t); + uint8_t *buf = data; + map_emex_feature_profile_t *fp = &ale->emex.feature_profile; + uint16_t feature_count; + + _E4B(&buf, &fp->agent_version); + _E2B(&buf, &feature_count); + + /* Compare incoming variable payload size and calculated one. */ + if (vendor_tlv->m_nr != min_tlv_len + + feature_count * sizeof(map_emex_supported_feature_t)) { + log_ctrl_e("Size mismatch on payload!"); + break; + } + + /* Feature indication will show that we have extensions on board. */ + ale->emex.enabled = true; + + /* Allocate feature list if feature count has changed. */ + if (feature_count != fp->feature_count) { + *changed = true; + SFREE(fp->feature_list); + fp->feature_count = feature_count; + /* Check incoming feature size and break if 0. */ + if (feature_count == 0) { + log_ctrl_w("No incoming feature list."); + /* EM+ spec notation: k >= 0 */ + ret = 1; + break; + } + fp->feature_list = calloc(feature_count, + sizeof(map_emex_supported_feature_t)); + if (fp->feature_list == NULL) { + log_ctrl_e("Cannot allocate memory!"); + break; + } + } + + /* Store incoming agent feature id and version. */ + for (i = 0; i < feature_count; i++) { + _E2B(&buf, &fp->feature_list[i].id); + _E2B(&buf, &fp->feature_list[i].version); + } + + ret = 1; + } while (0); + + return ret; +} + +static int8_t parse_emex_device_info(struct vendorSpecificTLV* vendor_tlv, map_ale_info_t *ale) +{ + int8_t ret = 0; + + do { + /* TLV len: emex_tlv_id(2) + boot_id(4) + clientid_len(1) + clientsec_len(1) + product(1) + role(1) */ + unsigned short min_tlv_len = 2 + 4 + 1 + 1 + 1 + 1; + if (vendor_tlv->m_nr < min_tlv_len) { + log_ctrl_e("Minimal TLV size check failed!"); + break; + } + if (ale == NULL) { + log_ctrl_e("ale is not exist"); + break; + } + + /* Iterate 2-byte AirTies EM+ TLV ID to get payload data. */ + uint8_t *data = vendor_tlv->m + sizeof(uint16_t); + uint8_t *buf = data; + map_emex_device_info_t *di = &ale->emex.device_info; + + _E4B(&buf, &di->boot_id); + _E1B(&buf, &di->client_id_len); + + /* Check buffer over flow before fetching client id: + * Diff between iterated (buf) and base address of the data + client id len + */ + if (vendor_tlv->m_nr <= (buf - data) + di->client_id_len) { + log_ctrl_e("Possible buffer overflow before getting client ID!"); + break; + } + _EnB(&buf, di->client_id, di->client_id_len); + _E1B(&buf, &di->client_secret_len); + + /* Check exact buffer size after gathering all variable length elements */ + if (vendor_tlv->m_nr != min_tlv_len + di->client_id_len + di->client_secret_len) { + log_ctrl_e("Size mismatch on payload!"); + break; + } + _EnB(&buf, di->client_secret, di->client_secret_len); + _E1B(&buf, &di->product_class); + _E1B(&buf, &di->device_role); + di->received = true; + + ret = 1; + } while (0); + + return ret; +} + static int8_t parse_emex_device_metrics(struct vendorSpecificTLV* vendor_tlv, map_ale_info_t *ale) { int i; @@ -50,8 +258,6 @@ static int8_t parse_emex_device_metrics(struct vendorSpecificTLV* vendor_tlv, ma break; } - ale->emex.enabled = true; - /* Iterate 2-byte AirTies EM+ TLV ID to get payload data. */ uint8_t *data = vendor_tlv->m + sizeof(uint16_t); uint8_t *buf = data; @@ -131,8 +337,6 @@ static int parse_emex_eth_interfaces(i1905_vendor_specific_tlv_t *vendor_tlv, ma goto out; } - ale->emex.enabled = true; - _E2B(&p, &tlv_id); _E1B(&p, &iface_nr); @@ -213,8 +417,6 @@ static int parse_emex_eth_stats_v2(i1905_vendor_specific_tlv_t *vendor_tlv, map_ goto out; } - ale->emex.enabled = true; - _E2B(&p, &tlv_id); _E2B(&p, &list->supported_stats_mask); _E1B(&p, &iface_nr); @@ -320,8 +522,6 @@ static int parse_emex_eth_neighbor_devices(i1905_vendor_specific_tlv_t *vendor_t goto out; } - ale->emex.enabled = true; - _E2B(&p, &tlv_id); _E1B(&p, &iface_nr); @@ -414,6 +614,7 @@ bool map_emex_is_valid_tlv(i1905_vendor_specific_tlv_t* vendor_tlv) int8_t map_emex_parse_tlv(map_ale_info_t *ale, i1905_vendor_specific_tlv_t* vendor_tlv) { int8_t ret = 0; + bool feature_changed = false; if (!ale) { return ret; @@ -427,9 +628,28 @@ int8_t map_emex_parse_tlv(map_ale_info_t *ale, i1905_vendor_specific_tlv_t* vend /* Convert incoming 2-byte AirTies TLV id into short integer for ease of comparison. */ uint16_t emex_tlv_id = (vendor_tlv->m[0] << 8) | vendor_tlv->m[1]; - log_ctrl_d("Received AirTies EM+ TLV (%d)", emex_tlv_id); + log_ctrl_t("Received AirTies EM+ TLV (%d)", emex_tlv_id); switch (emex_tlv_id) { + case EMEX_TLV_MESSAGE_TYPE: + { + /* Currently not handled */ + break; + } + case EMEX_TLV_FEATURE_PROFILE: + { + ret = parse_emex_feature_profile(vendor_tlv, ale, &feature_changed); + + if (feature_changed) { + map_emex_calculate_common_feature_list(); + } + break; + } + case EMEX_TLV_DEVICE_INFO: + { + ret = parse_emex_device_info(vendor_tlv, ale); + break; + } case EMEX_TLV_DEVICE_METRICS: { ret = parse_emex_device_metrics(vendor_tlv, ale); @@ -465,30 +685,32 @@ int8_t map_emex_parse_tlv(map_ale_info_t *ale, i1905_vendor_specific_tlv_t* vend return ret; } -int8_t map_emex_get_emex_tlv(UNUSED map_ale_info_t *ale, uint16_t emex_tlv_type, - i1905_vendor_specific_tlv_t *vendor_specific_tlv) +bool map_emex_agent_is_feature_supported(map_ale_info_t *ale, uint16_t id) { - uint8_t *payload = NULL; - uint16_t payload_len = 0; - int8_t ret = -1; - - switch (emex_tlv_type) - { - default: - log_ctrl_e("Unsupported AirTies EM+ TLV type (%d)", emex_tlv_type); - break; + map_emex_supported_feature_t *f_list; + int i, f_count; + + f_list = ale->emex.feature_profile.feature_list; + f_count = ale->emex.feature_profile.feature_count; + for (i = 0; i < f_count; i++) { + if (id == f_list[i].id) + return true; } + return false; +} - if (!ret) { - vendor_specific_tlv->tlv_type = TLV_TYPE_VENDOR_SPECIFIC; - vendor_specific_tlv->vendorOUI[0] = AIRTIES_VENDOR_OUI_1; - vendor_specific_tlv->vendorOUI[1] = AIRTIES_VENDOR_OUI_2; - vendor_specific_tlv->vendorOUI[2] = AIRTIES_VENDOR_OUI_3; - vendor_specific_tlv->m_nr = payload_len; - vendor_specific_tlv->m = (uint8_t *)payload; +bool map_emex_common_is_feature_supported(uint16_t id) +{ + map_emex_common_feature_list_t *cf_list; + int i, f_count; + + cf_list = controller_get_emex_common_feature_list(); + f_count = cf_list->feature_count; + for (i = 0; i < f_count; i++) { + if (id == cf_list->feature_list[i].id) + return true; } - - return ret; + return false; } int map_emex_handle_cmdu_pre(map_ale_info_t *ale, i1905_cmdu_t *cmdu) @@ -541,4 +763,6 @@ int8_t map_emex_init(void) void map_emex_fini(void) { + SFREE(controller_get_emex_common_feature_list()->feature_list); + controller_get_emex_common_feature_list()->feature_count = 0; } diff --git a/source/controller/src/map_ctrl_main.c b/source/controller/src/map_ctrl_main.c index a107cdd..f053ebc 100644 --- a/source/controller/src/map_ctrl_main.c +++ b/source/controller/src/map_ctrl_main.c @@ -33,12 +33,16 @@ #include "map_ctrl_onboarding_handler.h" #include "map_ctrl_config.h" #include "map_ctrl_cli.h" +#include "map_ctrl_emex_tlv_handler.h" #include "map_ctrl_topology_tree.h" #include "map_ctrl_chan_sel.h" +#include "map_ctrl_sta.h" +#include "map_data_model.h" #include "map_ctrl_nbapi.h" #include "map_timer_handler.h" #include "map_retry_handler.h" +#include "map_ctrl_vendor.h" #include "map_staging_list.h" #include "map_blocklist.h" @@ -145,7 +149,7 @@ int map_ctrl_main(bool ebtables, bool wfa_cert) } /* 1905 stack */ - if (i1905_init(get_controller_cfg()->al_mac, interface_cb, map_cmdu_rx_cb)) { + if (i1905_init(get_controller_cfg()->al_mac, interface_cb, map_cmdu_rx_cb, map_dm_get_ale_key_info)) { log_ctrl_e("init_map_controller_callback failed"); break; } @@ -193,6 +197,24 @@ int map_ctrl_main(bool ebtables, bool wfa_cert) break; } + /* Sta handling */ + if (map_ctrl_sta_init()) { + log_ctrl_e("map_ctrl_sta_init failed"); + break; + } + + /* EMEX */ + if (map_emex_init()) { + log_ctrl_e("map_emex_init failed"); + break; + } + + /* Vendor */ + if (map_ctrl_vendor_init()) { + log_ctrl_e("map_ctrl_vendor_init failed"); + break; + } + /* Northbound API */ if (map_ctrl_nbapi_init()) { log_ctrl_e("map_ctrl_nbapi_init failed"); @@ -216,6 +238,12 @@ int map_ctrl_main(bool ebtables, bool wfa_cert) map_ctrl_nbapi_fini(); + map_ctrl_vendor_fini(); + + map_emex_fini(); + + map_ctrl_sta_fini(); + map_ctrl_chan_sel_fini(); map_onboarding_handler_fini(); diff --git a/source/controller/src/map_ctrl_nbapi.c b/source/controller/src/map_ctrl_nbapi.c index b18e30a..8034261 100644 --- a/source/controller/src/map_ctrl_nbapi.c +++ b/source/controller/src/map_ctrl_nbapi.c @@ -14,7 +14,11 @@ #define LOG_TAG "nbapi" #include "map_ctrl_cmdu_tx.h" +#include "map_ctrl_post_onboarding_handler.h" #include "map_ctrl_tlv_helper.h" +#include "map_ctrl_utils.h" +#include "map_ctrl_emex_tlv_handler.h" +#include "map_ctrl_vendor.h" #include "map_ctrl_nbapi.h" /*####################################################################### @@ -38,36 +42,148 @@ typedef struct client_assoc_control_s { # API FUNCTIONS # ########################################################################*/ -static void nb_channel_scan(map_ale_info_t *ale, map_nb_ch_scan_param_t *payload) +static int nb_steer_wifi_backhaul(map_ale_info_t *ale, map_nb_steer_wifi_bh_param_t *payload) { - map_channel_scan_request_tlv_t tlv = {0}; + map_backhaul_steering_request_tlv_t tlv = {0}; + + if (!ale || !payload) { + return NB_EINVAL; + } + + maccpy(&tlv.bsta_mac, &payload->bsta_mac); + maccpy(&tlv.target_bssid, &payload->target_bssid); + tlv.target_op_class = payload->op_class; + tlv.target_channel = payload->channel; + + if (map_send_backhaul_steering_request(ale, &tlv, MID_NA)) { + log_ctrl_e("send steer wifi backhaul request failed"); + return NB_EFAIL; + } + + return NB_OK; +} + +static int nb_set_steering_policy(map_ale_info_t *ale) +{ + map_steering_policy_tlv_t steering_policy_tlv = {0}; + map_policy_config_tlvs_t tlvs = {0}; + + if (!ale) { + return NB_EINVAL; + } + + steering_policy_tlv.btm_steering_dis_macs = ale->btm_steering_disallow_macs; + steering_policy_tlv.btm_steering_dis_macs_nr = ale->btm_steering_disallow_macs_nr; + steering_policy_tlv.local_steering_dis_macs = ale->local_steering_disallow_macs; + steering_policy_tlv.local_steering_dis_macs_nr = ale->local_steering_disallow_macs_nr; + + tlvs.steering_policy_tlv = &steering_policy_tlv; + + if (map_send_policy_config_request(ale, &tlvs, MID_NA)) { + log_ctrl_e("send policy config request failed"); + return NB_EFAIL; + } + + return NB_OK; +} + +static int nb_reset_reboot(map_ale_info_t *ale, map_nb_reset_reboot_param_t *payload) +{ + uint8_t action_type; + uint8_t reset_type; + + if (!ale || !payload) { + return NB_EINVAL; + } + + action_type = payload->is_reset ? MAP_EMEX_REBOOT_ACTION_RESET : MAP_EMEX_REBOOT_ACTION_REBOOT; + reset_type = payload->factory_reset ? MAP_EMEX_RESET_FACTORY_RESET : MAP_EMEX_RESET_SOFT_RESET; + + if (map_ctrl_vendor_send_reboot_request(ale, action_type, reset_type)) { + log_ctrl_e("send reboot/reset request failed"); + return NB_EFAIL; + } + + return NB_OK; +} + +static int nb_channel_scan(map_ale_info_t *ale, map_nb_ch_scan_param_t *payload) +{ + map_channel_scan_request_tlv_t tlv = {.tlv_type = TLV_TYPE_CHANNEL_SCAN_REQUEST}; unsigned int i; + int ret = NB_OK; if (!ale || !payload) { - return; + ret = NB_EINVAL; + goto fail; } tlv.fresh_scan_performed = true; tlv.radios_nr = 1; maccpy(&tlv.radios[0].radio_id, &payload->radio_id); tlv.radios[0].op_classes_nr = payload->op_classes_nr; +#if 0 + tlv.radios[0].op_classes = payload->op_classes; +#else + tlv.radios[0].op_classes = calloc(tlv.radios[0].op_classes_nr, sizeof(*tlv.radios[0].op_classes)); for (i = 0; i < payload->op_classes_nr; i++) { tlv.radios[0].op_classes[i].op_class = payload->op_classes[i].op_class; tlv.radios[0].op_classes[i].channels = payload->op_classes[i].channels; } +#endif if (map_send_channel_scan_request(ale, &tlv, MID_NA)) { log_ctrl_e("send channel scan request failed"); + ret = NB_EFAIL; } + +fail: + free_1905_TLV_structure2((uint8_t *)&tlv); + + return ret; } -static void nb_beacon_metrics_query(map_ale_info_t *ale, map_nb_bmquery_param_t *payload) +static int nb_channel_selection(map_ale_info_t *ale, map_nb_ch_selection_param_t *payload) { - map_beacon_metrics_query_tlv_t tlv = {0}; + map_radio_info_t *radio; + int rc; + + if (!ale || !payload) { + return NB_EINVAL; + } + + radio = map_dm_get_radio(ale, payload->radio_id); + if (!radio) { + log_ctrl_e("get radio failed"); + return NB_EINVAL; + } + + /* Update preferred channels */ + SFREE(radio->merged_pref_op_class_list.op_classes); + radio->merged_pref_op_class_list.op_classes_nr = 0; + rc = map_merge_pref_op_class_list(&radio->merged_pref_op_class_list, + &radio->cap_op_class_list, &radio->ctrl_pref_op_class_list, + &radio->pref_op_class_list, &radio->disallowed_op_class_list); + + if (rc) { + log_ctrl_e("update preferred channel and operating class list failed"); + return NB_EFAIL; + } + + /* Do channel selection */ + map_agent_cancel_channel_selection(ale); + map_agent_handle_channel_selection(ale, radio, MAP_CHAN_SEL_REQUEST); + + return NB_OK; +} + +static int nb_beacon_metrics_query(map_ale_info_t *ale, map_nb_bmquery_param_t *payload) +{ + map_beacon_metrics_query_tlv_t tlv = {0}; unsigned int i; if (!ale || !payload) { - return; + return NB_EINVAL; } maccpy(&tlv.sta_mac, &payload->sta_mac); @@ -78,126 +194,117 @@ static void nb_beacon_metrics_query(map_ale_info_t *ale, map_nb_bmquery_param_t tlv.ssid_len = strlen(payload->ssid); memcpy(tlv.ssid, payload->ssid, tlv.ssid_len); tlv.ap_channel_reports_nr = payload->ap_chan_reports_nr; +#if 0 + tlv.ap_channel_reports = payload->ap_chan_reports; +#else + tlv.ap_channel_reports = calloc(tlv.ap_channel_reports_nr, sizeof(*tlv.ap_channel_reports)); for (i = 0; i < payload->ap_chan_reports_nr; i++) { tlv.ap_channel_reports[i].op_class = payload->ap_chan_reports[i].op_class; tlv.ap_channel_reports[i].channels = payload->ap_chan_reports[i].channels; } +#endif tlv.element_ids_nr = payload->element_ids_nr; memcpy(tlv.element_ids, payload->element_ids, tlv.element_ids_nr); if (map_send_beacon_metrics_query(ale, &tlv, MID_NA)) { log_ctrl_e("send beacon metrics query failed"); + return NB_EFAIL; } + + return NB_OK; } -static nb_retcode_t send_steering_request(map_ale_info_t *ale, mac_addr bssid, mac_addr sta_mac, - mac_addr t_bssid, uint8_t t_channel, uint8_t t_op_class, - bool mandate, - bool abridged, - bool disassoc_imminent, - int disassoc_timer, - uint8_t reason) +static int nb_client_steer(map_ale_info_t *ale, map_nb_client_steer_params_t *payload) { - nb_retcode_t ret = NB_EFAIL; - map_steer_t steer; - - memset(&steer, 0, sizeof(map_steer_t)); - maccpy(steer.bssid, bssid); + map_steer_t steer = {0}; - steer.sta_bssid_nr = 1; - maccpy(steer.sta_bssid[0].sta_mac, sta_mac); - maccpy(steer.sta_bssid[0].target_bssid, t_bssid); - steer.sta_bssid[0].channel = t_channel; - steer.sta_bssid[0].op_class = t_op_class; - steer.sta_bssid[0].reason = reason; - - steer.flags |= mandate ? MAP_STEERING_REQUEST_FLAG_MANDATE : 0; - steer.flags |= abridged ? MAP_STEERING_REQUEST_FLAG_BTM_ABRIDGED : 0; - steer.flags |= disassoc_imminent ? MAP_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT : 0; - steer.disassociation_timer = disassoc_timer; + if (!ale || !payload) { + return NB_EINVAL; + } - ret = map_send_client_steering_request(ale, &steer, MID_NA) ? - NB_EFAIL : NB_OK; + maccpy(steer.bssid, payload->bssid); + steer.sta_bssid_nr = 1; + maccpy(steer.sta_bssid[0].sta_mac, payload->target.sta_mac); + maccpy(steer.sta_bssid[0].target_bssid, payload->target.bssid); + steer.sta_bssid[0].channel = payload->target.channel; + steer.sta_bssid[0].op_class = payload->target.op_class; + steer.sta_bssid[0].reason = payload->target.reason; + steer.flags |= payload->flags & NB_STEERING_REQUEST_FLAG_MANDATE ? + MAP_STEERING_REQUEST_FLAG_MANDATE : 0; + steer.flags |= payload->flags & NB_STEERING_REQUEST_FLAG_BTM_ABRIDGED ? + MAP_STEERING_REQUEST_FLAG_BTM_ABRIDGED : 0; + steer.flags |= payload->flags & NB_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT ? + MAP_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT : 0; + steer.disassociation_timer = payload->disassociation_timer; + + if (map_send_client_steering_request(ale, &steer, MID_NA)) { + log_ctrl_e("send_steering_request failed"); + return NB_EFAIL; + } - return ret; + return NB_OK; } -static nb_retcode_t set_btm_steering_disallowed_list(map_ale_info_t *ale, int num_sta_mac, mac_addr *sta_mac_list) +static int nb_mapsta_disassociate(map_ale_info_t *ale, map_nb_sta_disassociate_params_t *payload) { - nb_retcode_t ret = NB_EFAIL; - map_policy_config_tlvs_t tlvs = {0}; - map_steering_policy_tlv_t steering_policy_tlv; - int i; - mac_addr *btm_steering_macs = NULL; + bool remove = false; + map_nb_client_steer_params_t steer = {0}; + int rc = NB_OK; - do { + if (!ale || !payload) { + return NB_EINVAL; + } - if (num_sta_mac > 0) { - btm_steering_macs = malloc(num_sta_mac * sizeof(mac_addr)); - if (NULL == btm_steering_macs) { - break; - } - for (i=0; ista_mac, ale->btm_steering_disallow_macs, + ale->btm_steering_disallow_macs_nr)) { + if (ale->btm_steering_disallow_macs_nr == UINT8_MAX) { + log_ctrl_e("btm steering disallow list maxed out"); + return NB_EFAIL; + } + if (acu_mac_add_to_array(payload->sta_mac, &ale->btm_steering_disallow_macs, + &ale->btm_steering_disallow_macs_nr)) { + log_ctrl_e("mac add to array failed"); + return NB_ENOMEM; + } + remove = true; + if ((rc = nb_set_steering_policy(ale)) != NB_OK) { + log_ctrl_e("set steering policy failed"); + goto fail; } - - memset(&steering_policy_tlv, 0, sizeof(map_steering_policy_tlv_t)); - steering_policy_tlv.btm_steering_dis_macs_nr = num_sta_mac; - steering_policy_tlv.local_steering_dis_macs = NULL; - steering_policy_tlv.btm_steering_dis_macs = btm_steering_macs; - - tlvs.steering_policy_tlv = &steering_policy_tlv; - ret = map_send_policy_config_request(ale, &tlvs, MID_NA) ? - NB_EFAIL : NB_OK; - } while(0); - - free(btm_steering_macs); - - return ret; -} - -static void nb_client_steer(map_ale_info_t *ale, map_nb_client_steer_params_t *payload) -{ - if (NB_OK != send_steering_request(ale, payload->bssid, payload->target.sta_mac, - payload->target.bssid, payload->target.channel, payload->target.op_class, - payload->flags & NB_STEERING_REQUEST_FLAG_MANDATE, - payload->flags & NB_STEERING_REQUEST_FLAG_BTM_ABRIDGED, - payload->flags & NB_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT, - payload->disassociation_timer, payload->target.reason)) { - log_ctrl_e("send_steering_request failed"); } -} -static void nb_mapsta_disassociate(map_ale_info_t *ale, map_nb_sta_disassociate_params_t *payload) -{ - nb_retcode_t ret = NB_EFAIL; - bool rem_btm_dis = false; - mac_addr_str mac_str; - - if (!payload) - return; + maccpy(steer.bssid, payload->bssid); + maccpy(steer.target.sta_mac, payload->sta_mac); + maccpy(steer.target.bssid, g_wildcard_mac); + steer.target.reason = payload->reason_code; + steer.flags = NB_STEERING_REQUEST_FLAG_MANDATE; + steer.disassociation_timer = payload->disassociation_timer; + if ((rc = nb_client_steer(ale, &steer)) != NB_OK) { + log_ctrl_e("client steer failed"); + goto fail; + } - do { - mac_to_string(payload->sta_mac, mac_str); - if (NB_OK != (ret = set_btm_steering_disallowed_list(ale, 1, &payload->sta_mac))) { - log_ctrl_e("set_btm_steering_disallowed_list failed"); - break; + if (remove) { + if (acu_mac_del_from_array(payload->sta_mac, &ale->btm_steering_disallow_macs, + &ale->btm_steering_disallow_macs_nr)) { + log_ctrl_e("mac delete from array failed"); + return NB_ENOMEM; } - rem_btm_dis = true; - - if (NB_OK != (ret = send_steering_request(ale, payload->bssid, payload->sta_mac, g_wildcard_mac, 0, 0, - true, false, false, 0, 0))) { - log_ctrl_e("send_steering_request failed"); - break; + if (nb_set_steering_policy(ale)) { + log_ctrl_e("set steering policy failed"); + return NB_EFAIL; } - } while(0); + } + + return NB_OK; - /* Also in case of error, attempt to remove from steering disallowed list */ - if (rem_btm_dis) { - ;//msg_lib_remove_from_btm_steering_disallowed_list(ap_mac, sta_mac); +fail: + if (remove) { + acu_mac_del_from_array(payload->sta_mac, &ale->btm_steering_disallow_macs, + &ale->btm_steering_disallow_macs_nr); } + return rc; } static int @@ -344,7 +451,11 @@ static void nb_assoc_control(map_ale_info_t *ale, map_nb_assoc_control_params_t } static map_dm_nbapi_t g_nbapi_cbs = { + .steer_wifi_backhaul = nb_steer_wifi_backhaul, + .set_steering_policy = nb_set_steering_policy, + .reset_reboot = nb_reset_reboot, .channel_scan = nb_channel_scan, + .channel_selection = nb_channel_selection, .beacon_metrics_query = nb_beacon_metrics_query, .client_steer = nb_client_steer, .mapsta_disassociate = nb_mapsta_disassociate, diff --git a/source/controller/src/map_ctrl_onboarding_handler.c b/source/controller/src/map_ctrl_onboarding_handler.c index a72e0ae..48a0ca5 100644 --- a/source/controller/src/map_ctrl_onboarding_handler.c +++ b/source/controller/src/map_ctrl_onboarding_handler.c @@ -131,13 +131,11 @@ static uint8_t map_periodic_ap_capability_query_timer_cb(UNUSED char* timer_id, /* Send config renew */ static uint8_t map_config_renew_timer_cb(UNUSED char* timer_id, UNUSED void *arg) { - if (map_send_autoconfig_renew(IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA)){ + if (map_send_autoconfig_renew(IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA, true)){ log_ctrl_e("map_send_autoconfig_renew failed"); return 0; /* Restart timer */ } - map_reset_all_agent_nodes_onboarding_status(); - return 1; /* Remove timer */ } diff --git a/source/controller/src/map_ctrl_post_onboarding_handler.c b/source/controller/src/map_ctrl_post_onboarding_handler.c index 66f2265..11e6e54 100644 --- a/source/controller/src/map_ctrl_post_onboarding_handler.c +++ b/source/controller/src/map_ctrl_post_onboarding_handler.c @@ -66,7 +66,7 @@ static uint8_t onboarding_status_check_timer_cb(UNUSED char* timer_id, void *ale /* Send autoconfig renew if not all M1 where received */ if (ale->radios_nr > 0 && !map_is_all_radio_M1_received(ale)) { log_ctrl_i("sending config renew to ALE[%s]", ale->al_mac_str); - if (map_send_autoconfig_renew_ucast(ale, IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA)) { + if (map_send_autoconfig_renew_ucast(ale, IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA, false)) { log_ctrl_e("map_send_autoconfig_renew failed"); } } @@ -79,6 +79,15 @@ static uint8_t onboarding_status_check_timer_cb(UNUSED char* timer_id, void *ale return 0; /* Keep running */ } +static uint8_t delayed_channnel_selection_timer_cb(UNUSED char* timer_id, void *arg) +{ + map_radio_info_t *radio = arg; + + map_agent_handle_channel_selection(radio->ale, radio, MAP_CHAN_SEL_REQUEST); + + return 1; /* Remove timer */ +} + /*####################################################################### # CALLBACKS # ########################################################################*/ @@ -94,6 +103,7 @@ int map_build_and_send_policy_config(void *args, uint16_t *mid) map_unsuccessful_assoc_policy_tlv_t unsuccess_assoc_policy_tlv; map_default_8021q_settings_tlv_t default_8021q_settings_tlv; map_traffic_separation_policy_tlv_t traffic_separation_policy_tlv; + int radios_nr; int idx; if (ale == NULL) { @@ -106,17 +116,23 @@ int map_build_and_send_policy_config(void *args, uint16_t *mid) /* See TRS "WLAN-Controller_M4" for the used values below */ + if ((radios_nr = ale->radios_nr) > MAX_RADIO_PER_AGENT) { + log_ctrl_w("ALE[%s] has more radios[%d] than expected", ale->al_mac_str, radios_nr); + radios_nr = MAX_RADIO_PER_AGENT; + } + /* Update Metric reporting policy TLV */ - metric_policy_tlv.radios_nr = ale->radios_nr; + metric_policy_tlv.radios_nr = radios_nr; metric_policy_tlv.metric_reporting_interval = 1; tlvs.metric_policy_tlv = &metric_policy_tlv; /* Update steering policy TLV */ - steering_policy_tlv.radios_nr = ale->radios_nr; - steering_policy_tlv.local_steering_dis_macs_nr = 0; - steering_policy_tlv.btm_steering_dis_macs_nr = 0; - steering_policy_tlv.local_steering_dis_macs = NULL; - steering_policy_tlv.btm_steering_dis_macs = NULL; + steering_policy_tlv.radios_nr = radios_nr; + steering_policy_tlv.local_steering_dis_macs_nr = 0; //ale->local_steering_disallow_macs_nr; + steering_policy_tlv.btm_steering_dis_macs_nr = 0; //ale->btm_steering_disallow_macs_nr; + steering_policy_tlv.local_steering_dis_macs = NULL; //ale->local_steering_disallow_macs; + steering_policy_tlv.btm_steering_dis_macs = NULL; //ale->btm_steering_disallow_macs; + tlvs.steering_policy_tlv = &steering_policy_tlv; idx = 0; map_dm_foreach_radio(ale, radio) { @@ -135,13 +151,14 @@ int map_build_and_send_policy_config(void *args, uint16_t *mid) set_radio_state_policy_config_ack_not_received(&radio->state); idx++; + if (idx == radios_nr) { + break; + } } - tlvs.steering_policy_tlv = &steering_policy_tlv; - /* For profile 2 and higher */ /* Add Channel scan reporting policy TLV: report independent channel scans */ - channel_scan_report_policy_tlv.report_independent_ch_scans = 0; /* TODO: wbd_slave crashes when reporting independant scan results */ + channel_scan_report_policy_tlv.report_independent_ch_scans = 0; /* TODO: some agents crash when reporting independant scan results */ tlvs.channel_scan_report_policy_tlv = &channel_scan_report_policy_tlv; /* Add Unsuccessfull association policy TLV: report maximum 60 per minute */ @@ -190,7 +207,8 @@ int map_handle_policy_config_sent(int status, void *args, UNUSED void *compl_use int map_build_and_send_initial_channel_scan_req(void *args, UNUSED uint16_t *mid) { map_radio_info_t *radio = args; - map_channel_scan_request_tlv_t channel_scan_req_tlv = {0}; + map_channel_scan_request_tlv_t channel_scan_req_tlv = {.tlv_type = TLV_TYPE_CHANNEL_SCAN_REQUEST}; + int ret = -1; if (!is_initial_channel_scan_required()) { return 0; @@ -205,16 +223,22 @@ int map_build_and_send_initial_channel_scan_req(void *args, UNUSED uint16_t *mid */ bool fresh_scan = !radio->scan_caps.boot_only && radio->last_scan_info.last_scan_status_failed; - map_fill_channel_scan_request_tlv(&channel_scan_req_tlv, radio, fresh_scan, /* all channels */ NULL); + if (map_fill_channel_scan_request_tlv(&channel_scan_req_tlv, radio, fresh_scan, /* all channels */ NULL)) { + log_ctrl_e("map_fill_channel_scan_request_tlv failed"); + goto cleanup; + } - if (map_send_channel_scan_request(radio->ale, &channel_scan_req_tlv, MID_NA)) { + if ((ret = map_send_channel_scan_request(radio->ale, &channel_scan_req_tlv, MID_NA))) { log_ctrl_e("map_send_channel_scan_request failed"); - return -1; + goto cleanup; } radio->last_scan_info.last_scan_req_time = acu_get_timestamp_sec(); - return 0; +cleanup: + free_1905_TLV_structure2((uint8_t *)&channel_scan_req_tlv); + + return ret; } int map_handle_initial_channel_scan_request_sent(int status, void *args, UNUSED void *compl_user_data) @@ -336,13 +360,52 @@ int map_agent_cancel_channel_selection(map_ale_info_t *ale) map_unregister_retry(retry_id); } - /* Stop per RADIO select timer */ + /* Stop per RADIO select and delayed timer */ map_dm_foreach_radio(ale, radio) { map_dm_get_radio_timer_id(retry_id, radio, CHAN_SELECT_REQ_RETRY_ID); if (map_is_timer_registered(retry_id)) { map_unregister_retry(retry_id); } + map_agent_cancel_delayed_channel_selection(radio); + } + + return 0; +} + +int map_agent_start_delayed_channel_selection(map_radio_info_t *radio, uint32_t delay_sec) +{ + timer_id_t timer_id; + int ret = 0; + + if (WFA_CERT()) { + return 0; + } + + map_dm_get_radio_timer_id(timer_id, radio, DELAYED_CHAN_SELECT_REQ_TIMER_ID); + + if (map_is_timer_registered(timer_id)) { + if ((ret = map_timer_restart_callback(timer_id))) { + log_ctrl_e("failed restarting timer[%s]", timer_id); + } + } else { + if ((ret = map_timer_register_callback(delay_sec, timer_id, radio, delayed_channnel_selection_timer_cb))) { + log_ctrl_e("failed starting timer[%s]", timer_id); + } + } + + return ret; +} + +int map_agent_cancel_delayed_channel_selection(map_radio_info_t *radio) +{ + timer_id_t timer_id; + + map_dm_get_radio_timer_id(timer_id, radio, DELAYED_CHAN_SELECT_REQ_TIMER_ID); + + if (map_is_timer_registered(timer_id)) { + map_timer_unregister_callback(timer_id); } return 0; } + diff --git a/source/controller/src/map_ctrl_sta.c b/source/controller/src/map_ctrl_sta.c new file mode 100644 index 0000000..c17365f --- /dev/null +++ b/source/controller/src/map_ctrl_sta.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2024-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/* Perform actions when a sta or mld sta connects + - request or update client capabilities + - check backhaul sta connection to + - start/stop channel selection when backhaul disconnects/connects + - mark channel configurable when backhaul disconnects +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#define LOG_TAG "sta" + +#include "map_ctrl_sta.h" +#include "map_data_model.h" +#include "map_ctrl_utils.h" +#include "map_ctrl_post_onboarding_handler.h" +#include "map_ctrl_cmdu_tx.h" + +/*####################################################################### +# DEFINES # +########################################################################*/ +/* Delay before sending channel selection request when backhaul station disconnects. + A delay is used because in the agent might start onboarding again which + already includes channel selection -> avoid multiple channel selections. + + In case the agent does not do that controller will send a channel selection + after this delay. +*/ +#define BHSTA_DISCONNECT_CHAN_SEL_DELAY 15 + +/*####################################################################### +# HELP FUNCTION # +########################################################################*/ +static void set_bhsta_iface_active(map_ale_info_t *bhsta_ale, map_backhaul_sta_iface_t *bhsta_iface, bool active) +{ + map_radio_info_t *bhsta_radio; + + bhsta_iface->active = active; + + log_ctrl_i("ALE[%s]: setting bSTA[%s] %sactive", bhsta_ale->al_mac_str, + mac_string(bhsta_iface->mac_address), active ? "" : "in"); + + if ((bhsta_radio = map_dm_get_radio(bhsta_ale, bhsta_iface->radio_id))) { + if (active) { + map_agent_cancel_delayed_channel_selection(bhsta_radio); + } else { + map_agent_start_delayed_channel_selection(bhsta_radio, BHSTA_DISCONNECT_CHAN_SEL_DELAY); + } + + map_dm_radio_set_channel_configurable(bhsta_radio, !active); + } +} + +static void check_backhaul_connected(map_sta_info_t *sta) +{ + map_ale_info_t *bhsta_ale; + map_backhaul_sta_iface_t *bhsta_iface; + map_bss_info_t *bss = sta->bss; + + if (!(bhsta_iface = map_find_bhsta_iface_gbl(sta->mac, &bhsta_ale))) { + return; + } + + log_ctrl_i("ALE[%s]: bSTA[%s] band[%s] connected to BSS[%s]", bhsta_ale->al_mac_str, sta->mac_str, + map_get_freq_band_str(bss->radio->supported_freq), bss->bssid_str); + + set_bhsta_iface_active(bhsta_ale, bhsta_iface, true); +} + +static void check_backhaul_disconnected(map_sta_info_t *sta) +{ + map_ale_info_t *bhsta_ale; + map_backhaul_sta_iface_t *bhsta_iface; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss = sta->bss; + + if (!(bhsta_iface = map_find_bhsta_iface_gbl(sta->mac, &bhsta_ale))) { + return; + } + + log_ctrl_i("ALE[%s]: bSTA[%s] band[%s] disconnected from BSS[%s]", bhsta_ale->al_mac_str, sta->mac_str, + map_get_freq_band_str(bss->radio->supported_freq), bss->bssid_str); + + /* Check if this sta is connected anywhere else - cannot use map_dm_get_sta_gbl */ + map_dm_foreach_ale(ale) { + map_dm_foreach_radio(ale, radio) { + map_dm_foreach_bss(radio, bss) { + if (bss != sta->bss && map_dm_get_sta(bss, sta->mac)) { + /* Sta is connected somewhere else -> stop */ + return; + } + } + } + } + + set_bhsta_iface_active(bhsta_ale, bhsta_iface, false); +} + +/*####################################################################### +# CALLBACKS # +########################################################################*/ +void create_sta_cb(map_sta_info_t *sta) +{ + map_bss_info_t *bss = sta->bss; + + log_ctrl_i("ALE[%s]: %sSTA[%s] connected to BSS[%s]", bss->radio->ale->al_mac_str, sta->sta_mld ? "AFF_" : "", sta->mac_str, bss->bssid_str); + + /* 1. Update client capabilities: + - regular STA: send query + - affiliated STA: update sta_mld + */ + + if (!sta->sta_mld) { + timer_id_t retry_id; + + map_dm_get_sta_timer_id(retry_id, sta, CLIENT_CAPS_QUERY_RETRY_ID); + if (!map_is_timer_registered(retry_id)) { + if (map_register_retry(retry_id, 15, 20, sta, NULL, map_send_client_capability_query)) { + log_ctrl_e("failed registering retry timer[%s]", retry_id); + } + } + } else { + parse_update_mld_aff_client_capability(sta->sta_mld, false); + } + + /* 2. Check backhaul sta connection */ + check_backhaul_connected(sta); +} + +void remove_sta_cb(map_sta_info_t *sta) +{ + map_bss_info_t *bss = sta->bss; + + log_ctrl_i("ALE[%s]: %sSTA[%s] disconnected from BSS[%s]", bss->radio->ale->al_mac_str, sta->sta_mld ? "AFF_" : "", sta->mac_str, bss->bssid_str); + + /* Check backhaul sta connection */ + check_backhaul_disconnected(sta); +} + +void create_sta_mld_cb(map_sta_mld_info_t *sta_mld) +{ + map_ap_mld_info_t *ap_mld = sta_mld->ap_mld; + + log_ctrl_i("ALE[%s]: STA_MLD[%s] connected to AP_MLD[%s]", ap_mld->ale->al_mac_str, sta_mld->mac_str, ap_mld->mac_str); + + /* Update client capabilities */ + timer_id_t retry_id; + + map_dm_get_sta_mld_timer_id(retry_id, sta_mld, CLIENT_CAPS_QUERY_RETRY_ID); + if (!map_is_timer_registered(retry_id)) { + if (map_register_retry(retry_id, 15, 20, sta_mld, NULL, map_send_mld_client_capability_query)) { + log_ctrl_e("failed registering retry timer[%s]", retry_id); + } + } +} + +void remove_sta_mld_cb(map_sta_mld_info_t *sta_mld) +{ + map_ap_mld_info_t *ap_mld = sta_mld->ap_mld; + + log_ctrl_i("ALE[%s]: STA_MLD[%s] disconnected from AP_MLD[%s]", ap_mld->ale->al_mac_str, sta_mld->mac_str, ap_mld->mac_str); +} + +/*####################################################################### +# PUBLIC FUNCTIONS # +########################################################################*/ +static map_dm_cbs_t g_dm_cbs = { + .sta_create_cb = create_sta_cb, + .sta_remove_cb = remove_sta_cb, + + .sta_mld_create_cb = create_sta_mld_cb, + .sta_mld_remove_cb = remove_sta_mld_cb, +}; + +int map_ctrl_sta_init(void) +{ + map_dm_register_cbs(&g_dm_cbs); + + return 0; +} + +void map_ctrl_sta_fini() +{ + map_dm_unregister_cbs(&g_dm_cbs); +} diff --git a/source/controller/src/map_ctrl_tlv_helper.c b/source/controller/src/map_ctrl_tlv_helper.c index a31cf7d..f80dcbe 100644 --- a/source/controller/src/map_ctrl_tlv_helper.c +++ b/source/controller/src/map_ctrl_tlv_helper.c @@ -310,7 +310,7 @@ i1905_transmitter_link_metric_tlv_t *map_get_transmitter_link_metric_tlv(mac_add - MAP_CHAN_SEL_PREF_AGENT: use agent preference "pref_op_class_list" - MAP_CHAN_SEL_PERF_MERGED: use merged preference "merged_pref_op_class_list" */ -void map_fill_channel_preference_tlv(map_channel_preference_tlv_t *tlv, map_radio_info_t *radio, uint8_t pref_type) +int map_fill_channel_preference_tlv(map_channel_preference_tlv_t *tlv, map_radio_info_t *radio, uint8_t pref_type) { map_op_class_list_t *op_class_list = &radio->merged_pref_op_class_list; /* default and MAP_CHAN_SEL_PREF_MERGED */ uint8_t i; @@ -323,18 +323,27 @@ void map_fill_channel_preference_tlv(map_channel_preference_tlv_t *tlv, map_radi tlv->tlv_type = TLV_TYPE_CHANNEL_PREFERENCE; maccpy(tlv->radio_id, radio->radio_id); + tlv->op_classes_nr = 0; - for (i = 0; i < op_class_list->op_classes_nr && i < MAX_OP_CLASS; i++) { - map_op_class_t *op_class = &op_class_list->op_classes[i]; - map_channel_preference_tlv_op_class_t *tlv_op_class = &tlv->op_classes[i]; + if (op_class_list->op_classes_nr > 0) { + if (!(tlv->op_classes = calloc(op_class_list->op_classes_nr, sizeof(*tlv->op_classes)))) { + return -1; + } + + tlv->op_classes_nr = op_class_list->op_classes_nr; + + for (i = 0; i < tlv->op_classes_nr; i++) { + map_op_class_t *op_class = &op_class_list->op_classes[i]; + map_channel_preference_tlv_op_class_t *tlv_op_class = &tlv->op_classes[i]; - tlv_op_class->op_class = op_class->op_class; - tlv_op_class->pref = op_class->pref; - tlv_op_class->reason = op_class->reason; - map_cs_copy(&tlv_op_class->channels, &op_class->channels); + tlv_op_class->op_class = op_class->op_class; + tlv_op_class->pref = op_class->pref; + tlv_op_class->reason = op_class->reason; + map_cs_copy(&tlv_op_class->channels, &op_class->channels); + } } - tlv->op_classes_nr = i; + return 0; } void map_fill_transmit_power_tlv(map_transmit_power_limit_tlv_t *tlv, map_radio_info_t *radio) @@ -344,7 +353,7 @@ void map_fill_transmit_power_tlv(map_transmit_power_limit_tlv_t *tlv, map_radio_ tlv->transmit_power_eirp = radio->tx_pwr_limit; } -void map_fill_channel_scan_request_tlv(map_channel_scan_request_tlv_t *tlv, map_radio_info_t *radio, +int map_fill_channel_scan_request_tlv(map_channel_scan_request_tlv_t *tlv, map_radio_info_t *radio, bool fresh_scan, map_channel_set_t *channels) { map_channel_scan_request_tlv_radio_t *rsri = &tlv->radios[0]; @@ -359,43 +368,52 @@ void map_fill_channel_scan_request_tlv(map_channel_scan_request_tlv_t *tlv, map_ rsri->op_classes_nr = 0; if (!fresh_scan) { - return; + return 0; } /* Add all 20MHz op classes from scan cap tlv */ - for (i = 0; i < radio->scan_caps.op_class_list.op_classes_nr && rsri->op_classes_nr < MAX_OP_CLASS; i++) { - map_op_class_t *op_class = &radio->scan_caps.op_class_list.op_classes[i]; - map_tlv_op_class_t *tlv_op_class = &rsri->op_classes[rsri->op_classes_nr]; - uint16_t bw; - - if (0 != map_get_bw_from_op_class(op_class->op_class, &bw) || 20 != bw) { - continue; + if (radio->scan_caps.op_class_list.op_classes_nr > 0) { + /* Allocate room for all op classes */ + if (!(rsri->op_classes = calloc(radio->scan_caps.op_class_list.op_classes_nr, sizeof(*rsri->op_classes)))) { + return -1; } - /* Start with what we received from channel scan capabilities */ - tlv_op_class->op_class = op_class->op_class; - map_cs_copy(&tlv_op_class->channels, &op_class->channels); + for (i = 0; i < radio->scan_caps.op_class_list.op_classes_nr; i++) { + map_op_class_t *op_class = &radio->scan_caps.op_class_list.op_classes[i]; + map_tlv_op_class_t *tlv_op_class = &rsri->op_classes[rsri->op_classes_nr]; + uint16_t bw; - if (channels) { - /* If 0 channels then add all supported channels in op_class */ - if (map_cs_nr(&tlv_op_class->channels) == 0) { - if (0 != map_get_channel_set_from_op_class(op_class->op_class, &tlv_op_class->channels)) { - continue; - } - map_cs_and(&tlv_op_class->channels, &radio->ctl_channels); + if (0 != map_get_bw_from_op_class(op_class->op_class, &bw) || 20 != bw) { + continue; } - /* Only keep requested channels */ - map_cs_and(&tlv_op_class->channels, channels); + /* Start with what we received from channel scan capabilities */ + tlv_op_class->op_class = op_class->op_class; + map_cs_copy(&tlv_op_class->channels, &op_class->channels); - /* Skip op_class if no channels set */ - if (map_cs_nr(&tlv_op_class->channels) == 0) { - continue; + if (channels) { + /* If 0 channels then add all supported channels in op_class */ + if (map_cs_nr(&tlv_op_class->channels) == 0) { + if (0 != map_get_channel_set_from_op_class(op_class->op_class, &tlv_op_class->channels)) { + continue; + } + map_cs_and(&tlv_op_class->channels, &radio->ctl_channels); + } + + /* Only keep requested channels */ + map_cs_and(&tlv_op_class->channels, channels); + + /* Skip op_class if no channels set */ + if (map_cs_nr(&tlv_op_class->channels) == 0) { + continue; + } } - } - rsri->op_classes_nr++; + rsri->op_classes_nr++; + } } + + return 0; } void map_fill_default_8021q_settings_tlv(map_cfg_t *cfg, map_default_8021q_settings_tlv_t *tlv) diff --git a/source/controller/src/map_ctrl_tlv_parser.c b/source/controller/src/map_ctrl_tlv_parser.c index 048fd77..2e6732e 100644 --- a/source/controller/src/map_ctrl_tlv_parser.c +++ b/source/controller/src/map_ctrl_tlv_parser.c @@ -118,7 +118,7 @@ static bool get_bss_type(map_bss_info_t *bss, int *type) static int update_radio_bsss(map_radio_info_t *radio, map_ap_operational_bss_tlv_radio_t *tlv_radio) { - bool radio_configured = false; + bool matching_bss_found = false; int bss_type; uint8_t i; @@ -147,16 +147,19 @@ static int update_radio_bsss(map_radio_info_t *radio, map_ap_operational_bss_tlv /* get bss configured and type */ bss_type = 0; - radio_configured |= get_bss_type(bss, &bss_type); + matching_bss_found |= get_bss_type(bss, &bss_type); map_dm_bss_set_ssid(bss, tlv_bss->ssid_len, tlv_bss->ssid, bss_type); } /* Set the radio state to CONFIGURED on below case to handle controller re-boot after onboarding. => If at least one BSS is matching the configuration + => If teardown sent to radio and no bss reported(as expected) */ - if (radio_configured) { - if (!is_radio_configured(radio->state)) { + + if (!is_radio_configured(radio->state)) { + if((matching_bss_found) || + (tlv_radio->bsss_nr == 0 && is_radio_teardown_sent(radio->state))) { set_radio_state_configured(&radio->state); map_recompute_radio_state_and_update_ale_state(radio->ale); } @@ -244,7 +247,7 @@ static void update_radio_channel_from_iface(map_ale_info_t *ale, map_local_iface /* Set op_class and channel in dm (keep current tx_pwr) */ map_dm_radio_set_channel(radio, current_op_class > 0 ? current_op_class : map_get_op_class(channel, bw, supported_freq), - channel, bw, radio->current_tx_pwr); + channel, 0, bw, radio->current_tx_pwr); log_ctrl_i("updated channel/bw for radio[%s] op_class[%d] channel[%d] bw[%d] from %s", mac_string(radio->radio_id), radio->current_op_class, radio->current_op_channel, bw, @@ -252,8 +255,43 @@ static void update_radio_channel_from_iface(map_ale_info_t *ale, map_local_iface } } +static void update_i1905_interfaces(map_ale_info_t *ale) +{ + char **interfaces; + uint8_t interfaces_nr, i, j; + mac_addr mac; + + if (!(interfaces = i1905_get_list_of_interfaces(&interfaces_nr))) { + return; + } + + for (i = 0; i < interfaces_nr; i++) { + if (i1905_get_interface_mac(interfaces[i], mac) == 0) { + /* Find MAC in local interfaces */ + for (j = 0; j < ale->local_iface_count; j++) { + map_local_iface_t *iface = &ale->local_iface_list[j]; + + if (!maccmp(iface->mac_address, mac)) { + /* Update info in i1905 layer */ + i1905_set_interface_type(interfaces[i], iface->media_type); + + if (INTERFACE_TYPE_GROUP_GET(iface->media_type) == INTERFACE_TYPE_GROUP_WLAN && iface->ieee80211_valid) { + i1905_set_interface_80211_media_specific_info(interfaces[i], iface->ieee80211_network_membership, + iface->ieee80211_role, iface->ieee80211_ap_channel_band, + iface->ieee80211_ap_channel_center_freq_1, + iface->ieee80211_ap_channel_center_freq_2); + } + } + } + } + } + + i1905_free_list_of_interfaces(interfaces, interfaces_nr); +} + static int update_radio_op_classes(map_radio_info_t *radio, map_ap_radio_basic_cap_tlv_t *tlv) { + bool fill_disallowed = false; int i; if (radio->cap_op_class_list.op_classes) { @@ -265,6 +303,16 @@ static int update_radio_op_classes(map_radio_info_t *radio, map_ap_radio_basic_c return -1; } + if (radio->disallowed_op_class_list.op_classes_nr == 0) { + if (!(radio->disallowed_op_class_list.op_classes = calloc(tlv->op_classes_nr, sizeof(map_op_class_t)))) { + radio->disallowed_op_class_list.op_classes_nr = 0; + return -1; + } + + radio->disallowed_op_class_list.op_classes_nr = tlv->op_classes_nr; + fill_disallowed = true; + } + radio->cap_op_class_list.op_classes_nr = tlv->op_classes_nr; for (i = 0; iop_classes_nr; i++) { @@ -273,6 +321,11 @@ static int update_radio_op_classes(map_radio_info_t *radio, map_ap_radio_basic_c op_class->op_class = tlv->op_classes[i].op_class; op_class->eirp = tlv->op_classes[i].eirp; map_cs_copy(&op_class->channels, &tlv->op_classes[i].channels); + + if (fill_disallowed) { + op_class = &radio->disallowed_op_class_list.op_classes[i]; + op_class->op_class = tlv->op_classes[i].op_class; + } } /* Update allowed channels based on config and cap_op_class_list */ @@ -284,31 +337,18 @@ static int update_radio_op_classes(map_radio_info_t *radio, map_ap_radio_basic_c return 0; } -static void update_radio_channel_configurable(map_ale_info_t *bhsta_ale, map_backhaul_sta_iface_t *bhsta_iface) -{ - map_radio_info_t *bhsta_radio = map_dm_get_radio(bhsta_ale, bhsta_iface->radio_id); - - if (bhsta_radio) { - bhsta_radio->channel_configurable = !bhsta_iface->active; - map_dm_radio_set_capabilities(bhsta_radio); - } -} - static map_sta_info_t *handle_sta_connect(map_bss_info_t *bss, mac_addr mac, uint16_t assoc_time) { - map_ale_info_t *bhsta_ale; - map_backhaul_sta_iface_t *bhsta_iface = map_find_bhsta_iface_gbl(mac, &bhsta_ale); - map_sta_info_t *sta; - bool do_cap_query = false; + map_sta_info_t *sta; /* Currently a sta can only be linked to one BSS -> move if it already existed */ - if (!(sta = map_dm_get_sta_gbl(mac))) { + if (!(sta = map_dm_get_sta_from_ale(bss->radio->ale, mac))) { if (!(sta = map_dm_create_sta(bss, mac))) { log_ctrl_e("failed creating sta[%s]", mac_string(mac)); return NULL; } + sta->assoc_ts = map_dm_get_sta_assoc_ts(assoc_time); - do_cap_query = true; /* If there is a BTM steering request for this station and there was no BTM response yet finalize the steering @@ -322,61 +362,13 @@ static map_sta_info_t *handle_sta_connect(map_bss_info_t *bss, mac_addr mac, uin log_ctrl_i("sta[%s] moved from bss[%s] to bss[%s]", sta->mac_str, sta->bss->bssid_str, bss->bssid_str); sta->assoc_ts = map_dm_get_sta_assoc_ts(assoc_time); - do_cap_query = true; map_dm_update_sta_bss(bss, sta); } } - /* For connected bh_sta: change active state of backhaul sta iface */ - if (bhsta_iface) { - bhsta_iface->active = true; - update_radio_channel_configurable(bhsta_ale, bhsta_iface); - } - - if (do_cap_query) { - /* Do new client capability query */ - timer_id_t retry_id; - - map_dm_get_sta_timer_id(retry_id, sta, CLIENT_CAPS_QUERY_RETRY_ID); - if (!map_is_timer_registered(retry_id)) { - if (map_register_retry(retry_id, 15, 20, sta, NULL, map_send_client_capability_query)) { - log_ctrl_e("failed Registering retry timer[%s]", retry_id); - } - } - } - return sta; } -static void handle_sta_disconnect(map_bss_info_t *bss, mac_addr mac) -{ - map_sta_info_t *sta = map_dm_get_sta(bss, mac); /* will only find sta when it was really connected to this bss */ - - if (sta) { - map_ale_info_t *bhsta_ale; - map_backhaul_sta_iface_t *bhsta_iface = map_find_bhsta_iface_gbl(mac, &bhsta_ale); - - log_ctrl_d("%s: sta[%s] has left bss[%s]", __FUNCTION__, sta->mac_str, bss->bssid_str); - - map_dm_create_disassoc(sta); - - /* For disconnected bh_sta: - - change active state of backhaul sta iface - */ - if (bhsta_iface) { - log_ctrl_i("backhaul sta[%s] band[%s] disconnected from bss[%s]", sta->mac_str, - map_get_freq_band_str(bss->radio->supported_freq), bss->bssid_str); - - bhsta_iface->active = false; - update_radio_channel_configurable(bhsta_ale, bhsta_iface); - } - - map_dm_remove_sta(sta); - } else { - log_ctrl_d("%s: sta[%s] was not connected to bss[%s]", __FUNCTION__, mac_string(mac), bss->bssid_str); - } -} - static int compare_expired_scan_result(void* obj_old, void* obj_new) { map_scan_result_t *scan_result_old = obj_old; @@ -439,8 +431,8 @@ int map_parse_device_information_tlv(map_ale_info_t *ale, i1905_device_informati update_radio_channel_from_iface(ale, iface); } } else { - /* Allowed if media type was INTERFACE_TYPE_IEEE_802_11AX */ - if (iface->media_type != INTERFACE_TYPE_IEEE_802_11AX) { + /* Allowed if media type was INTERFACE_TYPE_IEEE_802_11AX or INTERFACE_TYPE_IEEE_802_11BE */ + if (!(iface->media_type == INTERFACE_TYPE_IEEE_802_11AX || iface->media_type == INTERFACE_TYPE_IEEE_802_11BE)) { log_ctrl_e("dev info for ale[%s] has wlan interface with unexpected media_specific_data_size[%d]", ale->al_mac_str, tlv_iface->media_specific_data_size); } @@ -450,6 +442,11 @@ int map_parse_device_information_tlv(map_ale_info_t *ale, i1905_device_informati ale->local_iface_count = tlv->local_interfaces_nr; + if (ale->is_local && ale->is_local_colocated) { + /* Update our interfaces with data from colocated local agent */ + update_i1905_interfaces(ale); + } + return 0; } @@ -677,11 +674,26 @@ int map_parse_assoc_clients_tlv(map_ale_info_t *ale, map_assoc_clients_tlv_t* tl { size_t i, j; + /* For MLO: + - tlv->bssid can be a regular BSSID or an AP MLD MAC + - tlv->sta_mac can be a regular MAC or an STA MLD MAC + + At this point, MLD stations should already have been added in the DM so we need + to ignore those. + */ + for (i = 0; i < tlv->bsss_nr; i++) { map_assoc_clients_tlv_bss_t *tlv_bss = &tlv->bsss[i]; - map_bss_info_t *bss = map_dm_get_bss_from_ale(ale, tlv_bss->bssid); + map_bss_info_t *bss; + + if (!(bss = map_dm_get_bss_from_ale(ale, tlv_bss->bssid))) { + /* Only print error when this is no ap_mld either */ + if (map_dm_ale_has_mld(ale)) { + if (map_dm_get_ap_mld(ale, tlv_bss->bssid)) { + continue; + } + } - if (!bss) { log_ctrl_e("%s: unknown bss[%s]", __FUNCTION__, mac_string(tlv_bss->bssid)); continue; } @@ -690,6 +702,12 @@ int map_parse_assoc_clients_tlv(map_ale_info_t *ale, map_assoc_clients_tlv_t* tl map_assoc_clients_tlv_sta_t *tlv_sta = &tlv_bss->stas[j]; map_sta_info_t *sta; + if (map_dm_ale_has_mld(ale)) { + if (map_dm_get_sta_mld_from_ale(ale, tlv_sta->mac)) { + continue; + } + } + if ((sta = handle_sta_connect(bss, tlv_sta->mac, tlv_sta->assoc_time))) { /* Unmark so sta is not removed */ map_dm_unmark_sta(sta); @@ -907,7 +925,7 @@ int map_parse_channel_preference_tlv(map_ale_info_t *ale, map_channel_preference int map_parse_radio_operation_restriction_tlv(map_ale_info_t *ale, map_radio_operation_restriction_tlv_t *tlv) { map_radio_info_t *radio = map_dm_get_radio(ale, tlv->radio_id); - int i, j; + uint8_t i, j; if (!radio) { log_ctrl_e("%s: radio[%s] not found", __FUNCTION__, mac_string(tlv->radio_id)); @@ -915,8 +933,7 @@ int map_parse_radio_operation_restriction_tlv(map_ale_info_t *ale, map_radio_ope } /* Remove old list */ - SFREE(radio->op_restriction_list.op_classes); - radio->op_restriction_list.op_classes_nr = 0; + map_dm_free_op_restriction_list(radio); if (tlv->op_classes_nr == 0) { return 0; @@ -927,43 +944,74 @@ int map_parse_radio_operation_restriction_tlv(map_ale_info_t *ale, map_radio_ope return -1; } + radio->op_restriction_list.op_classes_nr = tlv->op_classes_nr; + for (i = 0; i < tlv->op_classes_nr; i++) { map_radio_operation_restriction_tlv_op_class_t *tlv_op_class = &tlv->op_classes[i]; map_op_restriction_t *op_class = &radio->op_restriction_list.op_classes[i]; - if (tlv_op_class->channels_nr > MAX_CHANNEL_PER_OP_CLASS) { - log_ctrl_w("%s: too many channels[%d] in op_class for radio[%s]", __FUNCTION__, - tlv_op_class->channels_nr, mac_string(tlv->radio_id)); - } + op_class->op_class = tlv_op_class->op_class; + op_class->channels_nr = 0; + + if (tlv_op_class->channels_nr > 0) { + if (!(op_class->channels = calloc(tlv_op_class->channels_nr, sizeof(*op_class->channels)))) { + map_dm_free_op_restriction_list(radio); + return -1; + } - op_class->op_class = tlv_op_class->op_class ; - op_class->channel_count = min(tlv_op_class->channels_nr, MAX_CHANNEL_PER_OP_CLASS); + op_class->channels_nr = tlv_op_class->channels_nr; - for (j = 0; j < op_class->channel_count; j++) { - op_class->channel_list[j].channel = tlv_op_class->channels[j].channel; - op_class->channel_list[j].freq_restriction = tlv_op_class->channels[j].freq_restriction; + for (j = 0; j < op_class->channels_nr; j++) { + op_class->channels[j].channel = tlv_op_class->channels[j].channel; + op_class->channels[j].freq_restriction = tlv_op_class->channels[j].freq_restriction; + } } } - radio->op_restriction_list.op_classes_nr = tlv->op_classes_nr; - return 0; } /* MAP_R1 17.2.20 */ int map_parse_client_assoc_event_tlv(map_ale_info_t *ale, map_client_assoc_event_tlv_t *tlv) { - map_bss_info_t *bss = map_dm_get_bss_from_ale(ale, tlv->bssid); + /* tlv->bssid can be both ap_mld and bss */ + map_ap_mld_info_t *ap_mld = map_dm_ale_has_mld(ale) ? map_dm_get_ap_mld(ale, tlv->bssid) : NULL; + map_bss_info_t *bss = map_dm_get_bss_from_ale(ale, tlv->bssid); + map_sta_mld_info_t *sta_mld; + map_sta_info_t *sta; - if (!bss) { + if (!ap_mld && !bss) { log_ctrl_e("%s: unknown bss[%s]", __FUNCTION__, mac_string(tlv->bssid)); return -1; } - if (tlv->association_event == MAP_CLIENT_ASSOC_EVENT_DISCONNECTED) { - handle_sta_disconnect(bss, tlv->sta_mac); + /* Handle connect/disconnect seperatly */ + if (tlv->association_event == MAP_CLIENT_ASSOC_EVENT_CONNECTED) { + /* For MLO: + - tlv->bssid can be a regular BSSID or an AP MLD MAC + - tlv->sta_mac can be a regular MAC or an STA MLD MAC + + Because the BSSID and the AP MLD MAC can be the same at this point we cannot + know if this is a regular or MLD station + + -> wait for topology response to resolve + */ + + if (ap_mld) { + /* Regular or MLD STA -> need more information */ + map_send_topology_query(ale, MID_NA); + /* map_dm_sta_steering_finalize(sta); TODO:??? */ + /* map_dm_create_assoc(sta); TODO:??? */ + } else if (bss) { + handle_sta_connect(bss, tlv->sta_mac, 0); + } } else { - handle_sta_connect(bss, tlv->sta_mac, 0); + if (ap_mld && (sta_mld = map_dm_get_sta_mld(ap_mld, tlv->sta_mac))) { + map_dm_remove_sta_mld(sta_mld); + } else if (bss && (sta = map_dm_get_sta(bss, tlv->sta_mac))) { + map_dm_create_disassoc(sta); + map_dm_remove_sta(sta); + } } return 0; @@ -990,7 +1038,7 @@ int map_parse_assoc_sta_link_metrics_tlv(map_ale_info_t *ale, map_assoc_sta_link link_metrics->age = tlv_bss->report_time_interval; link_metrics->dl_mac_datarate = tlv_bss->downlink_data_rate; link_metrics->ul_mac_datarate = tlv_bss->uplink_data_rate; - link_metrics->rssi = tlv_bss->uplink_rssi; + link_metrics->rssi = RCPI_TO_RSSI(tlv_bss->uplink_rcpi); map_update_assoc_sta_link_metrics(sta, link_metrics); } } else { @@ -1009,29 +1057,31 @@ int map_parse_assoc_sta_link_metrics_tlv(map_ale_info_t *ale, map_assoc_sta_link /* MAP_R1 17.2.35 */ int map_parse_assoc_sta_traffic_stats_tlv(map_ale_info_t *ale, map_assoc_sta_traffic_stats_tlv_t* tlv) { - map_sta_info_t *sta = map_dm_get_sta_from_ale(ale, tlv->sta_mac); - uint8_t byte_counter_unit = ale->map_profile >= MAP_PROFILE_2 ? - ale->agent_capability.byte_counter_unit : MAP_BYTE_COUNTER_UNIT_BYTES; - uint64_t txbytes = map_convert_mapunits_to_bytes(tlv->txbytes, byte_counter_unit); - uint64_t rxbytes = map_convert_mapunits_to_bytes(tlv->rxbytes, byte_counter_unit); - - if (!sta) { + map_sta_mld_info_t *sta_mld; + map_sta_info_t *sta; + map_sta_traffic_stats_t *traffic_stats; + uint8_t byte_counter_unit = ale->map_profile >= MAP_PROFILE_2 ? + ale->agent_capability.byte_counter_unit : MAP_BYTE_COUNTER_UNIT_BYTES; + uint64_t tx_bytes = map_convert_mapunits_to_bytes(tlv->tx_bytes, byte_counter_unit); + uint64_t rx_bytes = map_convert_mapunits_to_bytes(tlv->rx_bytes, byte_counter_unit); + + /* tlv->sta_mac can be a STA_MLD or a STA */ + if (map_dm_ale_has_mld(ale) && (sta_mld = map_dm_get_sta_mld_from_ale(ale, tlv->sta_mac))) { + traffic_stats = &sta_mld->traffic_stats; + } else if ((sta = map_dm_get_sta_from_ale(ale, tlv->sta_mac))) { + traffic_stats = &sta->traffic_stats; + } else { log_ctrl_e("%s: sta[%s] not found", __FUNCTION__, mac_string(tlv->sta_mac)); return -1; } - if (!sta->traffic_stats && !(sta->traffic_stats = calloc(1, sizeof(*sta->traffic_stats)))) { - log_ctrl_e("%s: calloc failed", __FUNCTION__); - return -1; - } - - sta->traffic_stats->txbytes = txbytes; - sta->traffic_stats->rxbytes = rxbytes; - sta->traffic_stats->txpkts = tlv->txpkts; - sta->traffic_stats->rxpkts = tlv->rxpkts; - sta->traffic_stats->txpkterrors = tlv->txpkterrors; - sta->traffic_stats->rxpkterrors = tlv->rxpkterrors; - sta->traffic_stats->retransmission_cnt = tlv->retransmission_cnt; + traffic_stats->tx_bytes = tx_bytes; + traffic_stats->rx_bytes = rx_bytes; + traffic_stats->tx_packets = tlv->tx_packets; + traffic_stats->rx_packets = tlv->rx_packets; + traffic_stats->tx_packet_errors = tlv->tx_packet_errors; + traffic_stats->rx_packet_errors = tlv->rx_packet_errors; + traffic_stats->retransmissions = tlv->retransmissions; return 0; } @@ -1357,6 +1407,7 @@ int map_parse_cac_status_report_tlv(map_ale_info_t *ale, map_cac_status_report_t } ale->cac_status_report.valid = true; + map_dm_ale_set_cac_status(ale); return 0; @@ -1486,7 +1537,8 @@ int map_parse_cac_cap_tlv(map_ale_info_t *ale, map_cac_cap_tlv_t* tlv) /* MAP_R2 17.2.47 */ int map_parse_multiap_profile_tlv(map_ale_info_t *ale, map_multiap_profile_tlv_t* tlv) { - ale->map_profile = (tlv->map_profile == 2) ? MAP_PROFILE_2 : MAP_PROFILE_1; + /* If multiap profile tlv exists and set to 1, we assume that relevant agent supports release 4 or higher */ + ale->map_profile = (tlv->map_profile == 1) ? MAP_PROFILE_4P : tlv->map_profile; return 0; } @@ -1569,12 +1621,12 @@ int map_parse_ap_ext_metrics_response_tlv(map_ale_info_t *ale, map_ap_ext_metric } bss->extended_metrics.valid = true; - bss->extended_metrics.ucast_bytes_tx = map_convert_mapunits_to_bytes(tlv->ucast_bytes_tx, byte_counter_unit); - bss->extended_metrics.ucast_bytes_rx = map_convert_mapunits_to_bytes(tlv->ucast_bytes_rx, byte_counter_unit); - bss->extended_metrics.mcast_bytes_tx = map_convert_mapunits_to_bytes(tlv->mcast_bytes_tx, byte_counter_unit); - bss->extended_metrics.mcast_bytes_rx = map_convert_mapunits_to_bytes(tlv->mcast_bytes_rx, byte_counter_unit); - bss->extended_metrics.bcast_bytes_tx = map_convert_mapunits_to_bytes(tlv->bcast_bytes_tx, byte_counter_unit); - bss->extended_metrics.bcast_bytes_rx = map_convert_mapunits_to_bytes(tlv->bcast_bytes_rx, byte_counter_unit); + bss->extended_metrics.tx_ucast_bytes = map_convert_mapunits_to_bytes(tlv->tx_ucast_bytes, byte_counter_unit); + bss->extended_metrics.rx_ucast_bytes = map_convert_mapunits_to_bytes(tlv->rx_ucast_bytes, byte_counter_unit); + bss->extended_metrics.tx_mcast_bytes = map_convert_mapunits_to_bytes(tlv->tx_mcast_bytes, byte_counter_unit); + bss->extended_metrics.rx_mcast_bytes = map_convert_mapunits_to_bytes(tlv->rx_mcast_bytes, byte_counter_unit); + bss->extended_metrics.tx_bcast_bytes = map_convert_mapunits_to_bytes(tlv->tx_bcast_bytes, byte_counter_unit); + bss->extended_metrics.rx_bcast_bytes = map_convert_mapunits_to_bytes(tlv->rx_bcast_bytes, byte_counter_unit); return 0; } @@ -1643,15 +1695,19 @@ int map_parse_backhaul_sta_radio_capability_tlv(map_ale_info_t *ale, map_backhau ale->backhaul_sta_iface_count = tlvs_nr; for (i = 0; i < tlvs_nr; i++) { - map_backhaul_sta_radio_cap_tlv_t *tlv = tlvs[i]; - map_backhaul_sta_iface_t *bhsta_iface = &ale->backhaul_sta_iface_list[i]; + map_backhaul_sta_radio_cap_tlv_t *tlv = tlvs[i]; + map_backhaul_sta_iface_t *bhsta_iface = &ale->backhaul_sta_iface_list[i]; + map_radio_info_t *bhsta_radio; maccpy(bhsta_iface->radio_id, tlv->radio_id); if (tlv->bsta_mac_present) { maccpy(bhsta_iface->mac_address, tlv->bsta_mac); /* Check if it is connected(active) */ bhsta_iface->active = !!map_dm_get_sta_gbl(bhsta_iface->mac_address); - update_radio_channel_configurable(ale, bhsta_iface); + + if ((bhsta_radio = map_dm_get_radio(ale, bhsta_iface->radio_id))) { + map_dm_radio_set_channel_configurable(bhsta_radio, !bhsta_iface->active); + } } } @@ -1698,19 +1754,70 @@ int map_parse_ap_wifi6_cap_tlv(map_ale_info_t *ale, map_ap_wifi6_cap_tlv_t *tlv) /* MAP_R3 17.2.73 */ int map_parse_assoc_wifi6_sta_status_tlv(map_ale_info_t *ale, map_assoc_wifi6_sta_status_tlv_t *tlv) { - map_sta_info_t *sta = map_dm_get_sta_from_ale(ale, tlv->sta_mac); + map_sta_mld_info_t *sta_mld; + map_sta_info_t *sta = map_dm_get_sta_from_ale(ale, tlv->sta_mac); + map_wifi6_sta_tid_info_t *tid_info; uint8_t i; - if (!sta) { + /* tlv->sta_mac can be a STA_MLD or a STA */ + if (map_dm_ale_has_mld(ale) && (sta_mld = map_dm_get_sta_mld_from_ale(ale, tlv->sta_mac))) { + tid_info = &sta_mld->wifi6_sta_tid_info; + } else if ((sta = map_dm_get_sta_from_ale(ale, tlv->sta_mac))) { + tid_info = &sta->wifi6_sta_tid_info; + } else { log_ctrl_e("%s: sta[%s] not found", __FUNCTION__, mac_string(tlv->sta_mac)); return -1; } - sta->wifi6_sta_tid_info.TID_nr = tlv->TID_nr; + tid_info->TID_nr = tlv->TID_nr; + + for (i = 0; i < tid_info->TID_nr; i++) { + tid_info->TID[i] = tlv->TID[i]; + tid_info->queue_size[i] = tlv->queue_size[i]; + } + + return 0; +} + +/* MAP_R3 17.2.75 */ +int map_parse_bss_configuration_report_tlv(map_ale_info_t *ale, map_bss_configuration_report_tlv_t *tlv) +{ + int i, j; + + if (tlv->radios_nr > MAX_RADIO_PER_AGENT) { + log_ctrl_e("invalid radio number[%d] for ale[%s]", tlv->radios_nr, ale->al_mac_str); + return -1; + } + + for (i = 0; i < tlv->radios_nr; i++) { + map_bss_configuration_radio_t *tlv_radio = &tlv->radios[i]; + if (tlv_radio->bss_nr > MAX_BSS_PER_RADIO) { + log_ctrl_e("invalid bss number[%d] for radio[%s]", tlv_radio->bss_nr, mac_string(tlv_radio->ruid)); + return -1; + } + + map_radio_info_t *radio = map_dm_get_radio(ale, tlv_radio->ruid); + if (!radio) { + log_ctrl_w("radio[%s] not found", mac_string(tlv_radio->ruid)); + continue; + } + + for (j = 0; j < tlv_radio->bss_nr; j++) { + map_bss_configuration_bss_t *tlv_bss = &tlv_radio->bss[j]; + + map_bss_info_t *bss = map_dm_get_bss(radio, tlv_bss->bssid); + if (!bss) { + log_ctrl_w("bss [%s] not found", mac_string(tlv_bss->bssid)); + continue; + } - for (i=0; i < sta->wifi6_sta_tid_info.TID_nr; i++) { - sta->wifi6_sta_tid_info.TID[i] = tlv->TID[i]; - sta->wifi6_sta_tid_info.queue_size[i] = tlv->queue_size[i]; + bss->flags.backhaul_bss = tlv_bss->backhaul_bss; + bss->flags.fronthaul_bss = tlv_bss->fronthaul_bss; + bss->flags.r1_disallowed_status = tlv_bss->r1_disallowed_status; + bss->flags.r2_disallowed_status = tlv_bss->r2_disallowed_status; + bss->flags.multiple_bssid = tlv_bss->multiple_bssid; + bss->flags.transmitted_bssid = tlv_bss->transmitted_bssid; + } } return 0; @@ -1783,6 +1890,25 @@ int map_parse_dpp_chirp_value_tlv(map_ale_info_t *ale, map_dpp_chirp_value_tlv_t return ret; } +/* MAP_R3 17.2.84 */ +int map_parse_bss_configuration_request_tlv(map_ale_info_t *ale, map_bss_configuration_request_tlv_t *tlv) +{ + int ret = -1; + + if (tlv->obj_len && tlv->obj) { + free(ale->dpp_info.bss_config_req.obj); + ale->dpp_info.bss_config_req.obj_len = tlv->obj_len; + ale->dpp_info.bss_config_req.obj = calloc(tlv->obj_len, sizeof(uint8_t)); + if (ale->dpp_info.bss_config_req.obj == NULL) { + return ret; + } + memcpy(ale->dpp_info.bss_config_req.obj, tlv->obj, tlv->obj_len); + ret = 0; + } + + return ret; +} + /* MAP_R3 17.2.86 */ int map_parse_dpp_message_tlv(map_ale_info_t *ale, map_dpp_message_tlv_t *tlv) { @@ -1826,3 +1952,453 @@ int map_parse_device_inventory_tlv(map_ale_info_t *ale, map_device_inventory_tlv return 0; } + +/*####################################################################### +# MAP R6 TLV HANDLERS # +########################################################################*/ +/* MAP_R6 17.2.95 */ +int map_parse_wifi7_agent_capability_tlv(map_ale_info_t *ale, map_wifi7_agent_cap_tlv_t *tlv, bool *ret_changed) +{ + int i, j; + bool changed = false; + + log_ctrl_t("WI-FI 7 CAPABILITIES:"); + log_ctrl_t("*****************************"); + ale->agent_capability.max_mlds = tlv->max_mlds; + ale->agent_capability.ap_max_links = tlv->ap_max_links; + ale->agent_capability.bsta_max_links = tlv->bsta_max_links; + ale->agent_capability.tid_to_link_map_cap = tlv->tid_to_link_map_cap; + + log_ctrl_t("ALE[%s]", ale->al_mac_str); + log_ctrl_t(" max_mlds: %d, ap_max_links: %d, bsta_max_links: %d, tid_to_link_map: %d", ale->agent_capability.max_mlds, + ale->agent_capability.ap_max_links, ale->agent_capability.bsta_max_links, ale->agent_capability.tid_to_link_map_cap); + + for (i = 0; i < tlv->radios_nr; i++) { + map_radio_info_t *radio = map_dm_get_radio(ale, tlv->radios[i].ruid); + map_radio_wifi7_caps_t *tlv_wifi7_caps = &tlv->radios[i].cap; + map_radio_wifi7_caps_t *wifi7_caps; + + if (!radio) { + log_ctrl_e("%s: radio[%s] not found", __FUNCTION__, mac_string(tlv->radios[i].ruid)); + continue; + } + + log_ctrl_t(" Radio[%s]", mac_string(radio->radio_id)); + + /* TODO: Remove it when EHT operations TLV implemented by agent. + * Until that time this is the most deterministic eht capability indicator. + */ + if (!radio->eht_caps && !(radio->eht_caps = calloc(1, sizeof(*radio->eht_caps)))) { + log_ctrl_e("%s: calloc failed", __FUNCTION__); + return -1; + } + + /* Check if wifi7 capabilities are changed */ + if (radio->wifi7_caps) { + if (memcmp(&radio->wifi7_caps->ap_mld_modes, &tlv_wifi7_caps->ap_mld_modes, sizeof(map_mld_modes_t)) || + memcmp(&radio->wifi7_caps->bsta_mld_modes, &tlv_wifi7_caps->bsta_mld_modes, sizeof(map_mld_modes_t)) || + radio->wifi7_caps->ap_str_records_nr != tlv_wifi7_caps->ap_str_records_nr || + radio->wifi7_caps->ap_nstr_records_nr != tlv_wifi7_caps->ap_nstr_records_nr || + radio->wifi7_caps->ap_emlsr_records_nr != tlv_wifi7_caps->ap_emlsr_records_nr || + radio->wifi7_caps->ap_emlmr_records_nr != tlv_wifi7_caps->ap_emlmr_records_nr) { + changed = true; + } + + map_free_wifi7_caps(radio); + + } else if (is_radio_M2_sent(radio->state)) { + changed = true; + } + + if (!radio->wifi7_caps && !(radio->wifi7_caps = calloc(1, sizeof(*radio->wifi7_caps)))) { + log_ctrl_e("%s: calloc failed", __FUNCTION__); + return -1; + } + + wifi7_caps = radio->wifi7_caps; + wifi7_caps->ap_mld_modes = tlv_wifi7_caps->ap_mld_modes; + wifi7_caps->bsta_mld_modes = tlv_wifi7_caps->bsta_mld_modes; + + log_ctrl_t(" ap_str_support: %s, ap_nstr_support: %s, ap_emlsr_support: %s, ap_emlmr_support: %s", + wifi7_caps->ap_mld_modes.str ? "true" : "false", wifi7_caps->ap_mld_modes.nstr ? "true" : "false", + wifi7_caps->ap_mld_modes.emlsr ? "true" : "false", wifi7_caps->ap_mld_modes.emlmr ? "true" : "false"); + log_ctrl_t(" bsta_str_support: %s, bsta_nstr_support: %s, bsta_emlsr_support: %s, bsta_emlmr_support: %s", + wifi7_caps->bsta_mld_modes.str ? "true" : "false", wifi7_caps->bsta_mld_modes.nstr ? "true" : "false", + wifi7_caps->bsta_mld_modes.emlsr ? "true" : "false", wifi7_caps->bsta_mld_modes.emlmr ? "true" : "false"); + + wifi7_caps->ap_str_records_nr = tlv_wifi7_caps->ap_str_records_nr; + log_ctrl_t(" ap_str_records_nr: %d", wifi7_caps->ap_str_records_nr); + if (wifi7_caps->ap_str_records_nr > 0) { + wifi7_caps->ap_str_records = calloc(wifi7_caps->ap_str_records_nr, sizeof(*wifi7_caps->ap_str_records)); + for (j=0; j < wifi7_caps->ap_str_records_nr; j++) { + maccpy(wifi7_caps->ap_str_records[j].ruid, tlv_wifi7_caps->ap_str_records[j].ruid); + wifi7_caps->ap_str_records[j].freq_separation = tlv_wifi7_caps->ap_str_records[j].freq_separation; + log_ctrl_t(" ap_str_record[%d] ruid[%s] freq_separation[%d] ", + j, mac_string(wifi7_caps->ap_str_records[j].ruid), wifi7_caps->ap_str_records[j].freq_separation); + } + } + + wifi7_caps->ap_nstr_records_nr = tlv_wifi7_caps->ap_nstr_records_nr; + log_ctrl_t(" ap_nstr_records_nr: %d", wifi7_caps->ap_nstr_records_nr); + if (wifi7_caps->ap_nstr_records_nr > 0) { + wifi7_caps->ap_nstr_records = calloc(wifi7_caps->ap_nstr_records_nr, sizeof(*wifi7_caps->ap_nstr_records)); + for (j=0; j < wifi7_caps->ap_nstr_records_nr; j++) { + maccpy(wifi7_caps->ap_nstr_records[j].ruid, tlv_wifi7_caps->ap_nstr_records[j].ruid); + wifi7_caps->ap_nstr_records[j].freq_separation = tlv_wifi7_caps->ap_nstr_records[j].freq_separation; + log_ctrl_t(" ap_nstr_record[%d] ruid[%s] freq_separation[%d] ", + j, mac_string(wifi7_caps->ap_nstr_records[j].ruid), wifi7_caps->ap_nstr_records[j].freq_separation); + } + } + + wifi7_caps->ap_emlsr_records_nr = tlv_wifi7_caps->ap_emlsr_records_nr; + log_ctrl_t(" ap_emlsr_records_nr: %d", wifi7_caps->ap_emlsr_records_nr); + if (wifi7_caps->ap_emlsr_records_nr > 0) { + wifi7_caps->ap_emlsr_records = calloc(wifi7_caps->ap_emlsr_records_nr, sizeof(*wifi7_caps->ap_emlsr_records)); + for (j=0; j < wifi7_caps->ap_emlsr_records_nr; j++) { + maccpy(wifi7_caps->ap_emlsr_records[j].ruid, tlv_wifi7_caps->ap_emlsr_records[j].ruid); + wifi7_caps->ap_emlsr_records[j].freq_separation = tlv_wifi7_caps->ap_emlsr_records[j].freq_separation; + log_ctrl_t(" ap_emlsr_record[%d] ruid[%s] freq_separation[%d] ", + j, mac_string(wifi7_caps->ap_emlsr_records[j].ruid), wifi7_caps->ap_emlsr_records[j].freq_separation); + } + } + + wifi7_caps->ap_emlmr_records_nr = tlv_wifi7_caps->ap_emlmr_records_nr; + log_ctrl_t(" ap_emlmr_records_nr: %d", wifi7_caps->ap_emlmr_records_nr); + if (wifi7_caps->ap_emlmr_records_nr > 0) { + wifi7_caps->ap_emlmr_records = calloc(wifi7_caps->ap_emlmr_records_nr, sizeof(*wifi7_caps->ap_emlmr_records)); + for (j=0; j < wifi7_caps->ap_emlmr_records_nr; j++) { + maccpy(wifi7_caps->ap_emlmr_records[j].ruid, tlv_wifi7_caps->ap_emlmr_records[j].ruid); + wifi7_caps->ap_emlmr_records[j].freq_separation = tlv_wifi7_caps->ap_emlmr_records[j].freq_separation; + log_ctrl_t(" ap_emlmr_record[%d] ruid[%s] freq_separation[%d] ", + j, mac_string(wifi7_caps->ap_emlmr_records[j].ruid), wifi7_caps->ap_emlmr_records[j].freq_separation); + } + } + + wifi7_caps->bsta_str_records_nr = tlv_wifi7_caps->bsta_str_records_nr; + log_ctrl_t(" bsta_str_records_nr: %d", wifi7_caps->bsta_str_records_nr); + if (wifi7_caps->bsta_str_records_nr > 0) { + wifi7_caps->bsta_str_records = calloc(wifi7_caps->bsta_str_records_nr, sizeof(*wifi7_caps->bsta_str_records)); + for (j=0; j < wifi7_caps->bsta_str_records_nr; j++) { + maccpy(wifi7_caps->bsta_str_records[j].ruid, tlv_wifi7_caps->bsta_str_records[j].ruid); + wifi7_caps->bsta_str_records[j].freq_separation = tlv_wifi7_caps->bsta_str_records[j].freq_separation; + log_ctrl_t(" bsta_str_record[%d] ruid[%s] freq_separation[%d] ", + j, mac_string(wifi7_caps->bsta_str_records[j].ruid), wifi7_caps->bsta_str_records[j].freq_separation); + } + } + + wifi7_caps->bsta_nstr_records_nr = tlv_wifi7_caps->bsta_nstr_records_nr; + log_ctrl_t(" bsta_nstr_records_nr: %d", wifi7_caps->bsta_nstr_records_nr); + if (wifi7_caps->bsta_nstr_records_nr > 0) { + wifi7_caps->bsta_nstr_records = calloc(wifi7_caps->bsta_nstr_records_nr, sizeof(*wifi7_caps->bsta_nstr_records)); + for (j=0; j < wifi7_caps->bsta_nstr_records_nr; j++) { + maccpy(wifi7_caps->bsta_nstr_records[j].ruid, tlv_wifi7_caps->bsta_nstr_records[j].ruid); + wifi7_caps->bsta_nstr_records[j].freq_separation = tlv_wifi7_caps->bsta_nstr_records[j].freq_separation; + log_ctrl_t(" bsta_nstr_record[%d] ruid[%s] freq_separation[%d] ", + j, mac_string(wifi7_caps->bsta_nstr_records[j].ruid), wifi7_caps->bsta_nstr_records[j].freq_separation); + } + } + + wifi7_caps->bsta_emlsr_records_nr = tlv_wifi7_caps->bsta_emlsr_records_nr; + log_ctrl_t(" bsta_emlsr_records_nr: %d", wifi7_caps->bsta_emlsr_records_nr); + if (wifi7_caps->bsta_emlsr_records_nr > 0) { + wifi7_caps->bsta_emlsr_records = calloc(wifi7_caps->bsta_emlsr_records_nr, sizeof(*wifi7_caps->bsta_emlsr_records)); + for (j=0; j < wifi7_caps->bsta_emlsr_records_nr; j++) { + maccpy(wifi7_caps->bsta_emlsr_records[j].ruid, tlv_wifi7_caps->bsta_emlsr_records[j].ruid); + wifi7_caps->bsta_emlsr_records[j].freq_separation = tlv_wifi7_caps->bsta_emlsr_records[j].freq_separation; + log_ctrl_t(" bsta_emlsr_record[%d] ruid[%s] freq_separation[%d] ", + j, mac_string(wifi7_caps->bsta_emlsr_records[j].ruid), wifi7_caps->bsta_emlsr_records[j].freq_separation); + } + } + + wifi7_caps->bsta_emlmr_records_nr = tlv_wifi7_caps->bsta_emlmr_records_nr; + log_ctrl_t(" bsta_emlmr_records_nr: %d", wifi7_caps->bsta_emlmr_records_nr); + if (wifi7_caps->bsta_emlmr_records_nr > 0) { + wifi7_caps->bsta_emlmr_records = calloc(wifi7_caps->bsta_emlmr_records_nr, sizeof(*wifi7_caps->bsta_emlmr_records)); + for (j=0; j < wifi7_caps->bsta_emlmr_records_nr; j++) { + maccpy(wifi7_caps->bsta_emlmr_records[j].ruid, tlv_wifi7_caps->bsta_emlmr_records[j].ruid); + wifi7_caps->bsta_emlmr_records[j].freq_separation = tlv_wifi7_caps->bsta_emlmr_records[j].freq_separation; + log_ctrl_t(" bsta_emlmr_record[%d] ruid[%s] freq_separation[%d] ", + j, mac_string(wifi7_caps->bsta_emlmr_records[j].ruid), wifi7_caps->bsta_emlmr_records[j].freq_separation); + } + } + } + log_ctrl_t("*****************************"); + + if (ret_changed) { + *ret_changed = changed; + } + + return 0; +} + +/* MAP_R6 17.2.96 */ +int map_parse_agent_ap_mld_conf_tlv(map_ale_info_t *ale, map_agent_ap_mld_conf_tlv_t *tlv) +{ + map_ap_mld_info_t *mld, *next; + map_agent_ap_mld_conf_tlv_ap_mld_t *tlv_mld; + mac_addr_str mac_str; + int i, j; + + /* First remove no longer present ap_mld */ + map_dm_foreach_ap_mld_safe(ale, mld, next) { + bool found = false; + + for (i = 0; i < tlv->ap_mld_nr; i++) { + tlv_mld = &tlv->ap_mlds[i]; + + if (tlv_mld->ap_mld_mac_valid && !maccmp(mld->mac, tlv_mld->ap_mld_mac)) { + found = true; + break; + } + } + + if (!found) { + log_ctrl_i("Removing AP MLD[%s] from ALE[%s]", mld->mac_str, ale->al_mac_str); + map_dm_remove_ap_mld(mld); + } + } + + for (i = 0; i < tlv->ap_mld_nr; i++) { + tlv_mld = &tlv->ap_mlds[i]; + + if (!tlv_mld->ap_mld_mac_valid) { + log_ctrl_e("AP MLD from ale[%s] has ap_mld_mac_valid set to false -> ignore", ale->al_mac_str); + continue; + } + + if (!(mld = map_dm_get_ap_mld(ale, tlv_mld->ap_mld_mac))) { + mac_to_string(tlv_mld->ap_mld_mac, mac_str); + + log_ctrl_i("Creating AP MLD[%s] on ALE[%s]", mac_str, ale->al_mac_str); + if (!(mld = map_dm_create_ap_mld(ale, tlv_mld->ap_mld_mac))) { + log_ctrl_i("Could not create AP MLD[%s] on ALE[%s]", mac_str, ale->al_mac_str); + continue; + } + } + + /* Get affiliated BSS */ + /* TODO: detect if BSS is part more than one MLD?? */ + map_aff_ap_cfg_t aff_aps[MAX_MLD_AFF_APSTA]; + size_t aff_ap_nr = 0; + + for (j = 0; j < tlv_mld->aff_ap_nr && aff_ap_nr < MAX_MLD_AFF_APSTA; j++) { + map_agent_ap_mld_conf_tlv_aff_ap_t *tlv_aff_ap = &tlv_mld->aff_aps[j]; + map_radio_info_t *radio; + map_bss_info_t *bss; + + if (!(radio = map_dm_get_radio(ale, tlv_aff_ap->radio_id))) { + mac_to_string(tlv_aff_ap->radio_id, mac_str); + log_ctrl_e("Could not find affiliated AP radio[%s] on ALE[%s]", mac_str, ale->al_mac_str); + continue; + } + + if (!(bss = map_dm_get_bss(radio, tlv_aff_ap->aff_ap_mac))) { + mac_to_string(tlv_aff_ap->aff_ap_mac, mac_str); + log_ctrl_e("Could not find affiliated AP[%s] on radio[%s] on ALE[%s]", mac_str, radio->radio_id_str, ale->al_mac_str); + continue; + } + aff_aps[aff_ap_nr].bss = bss; + aff_aps[aff_ap_nr].link_id = tlv_aff_ap->link_id_valid ? tlv_aff_ap->link_id : 255; + aff_ap_nr++; + } + + /* Update parameters */ + map_dm_ap_mld_set(mld, tlv_mld->ssid_len, tlv_mld->ssid, + tlv_mld->str, tlv_mld->nstr, tlv_mld->emlsr, tlv_mld->emlmr, + aff_aps, aff_ap_nr); + } + + return 0; +} + +/* MAP_R6 17.2.97 */ +int map_parse_bsta_mld_conf_tlv(map_ale_info_t *ale, map_bsta_mld_conf_tlv_t *tlv) +{ + int i; + + if (!tlv->bsta_mld_mac_valid) { + map_dm_bsta_mld_set(&ale->bsta_mld, false, NULL, NULL, false, false, false, false, NULL, 0); + } else { + mac_addr aff_sta_macs[MAX_MLD_AFF_APSTA]; + size_t aff_sta_mac_nr = 0; + + for (i = 0; i < tlv->aff_bsta_nr && aff_sta_mac_nr < MAX_MLD_AFF_APSTA; i++) { + map_bsta_mld_conf_tlv_aff_bsta_t *tlv_aff_bsta = &tlv->aff_bstas[i]; + + if (tlv_aff_bsta->aff_bsta_mac_valid) { + maccpy(aff_sta_macs[aff_sta_mac_nr], tlv_aff_bsta->aff_bsta_mac); + aff_sta_mac_nr++; + } + } + + /* Sort array in case order would change... */ + acu_sort_mac_array(aff_sta_macs, aff_sta_mac_nr); + + map_dm_bsta_mld_set(&ale->bsta_mld, true, tlv->bsta_mld_mac, tlv->ap_mld_mac, + tlv->str, tlv->nstr, tlv->emlsr, tlv->emlmr, + aff_sta_macs, aff_sta_mac_nr); + } + + return 0; +} + +/* MAP_R6 17.2.97 */ +int map_parse_assoc_sta_mld_conf_tlv(map_ale_info_t *ale, map_assoc_sta_mld_conf_tlv_t *tlv) +{ + map_ap_mld_info_t *ap_mld; + map_sta_mld_info_t *sta_mld; + map_bss_info_t *bss; + map_sta_info_t *sta; + mac_addr_str sta_mld_mac_str; + mac_addr_str ap_mld_mac_str; + size_t i; + + mac_to_string(tlv->sta_mld_mac, sta_mld_mac_str); + mac_to_string(tlv->ap_mld_mac, ap_mld_mac_str); + + /* Is the AP_MLD known? */ + if (!(ap_mld = map_dm_get_ap_mld(ale, tlv->ap_mld_mac))) { + log_ctrl_e("ALE[%s]: could not find AP_MLD[%s]", ale->al_mac_str, ap_mld_mac_str); + return -1; + } + + /* Is this STA connected (to this or another ap_mld) */ + if ((sta_mld = map_dm_get_sta_mld_from_ale(ale, tlv->sta_mld_mac))) { + if (sta_mld->ap_mld != ap_mld) { + /* This would mean a roam from e.g home to guest and is not that likely + -> Just remove and re-create + */ + log_ctrl_i("ALE[%s]: removing STA_MLD[%s] part of the wrong AP_MLD[%s <-> %s]", + ale->al_mac_str, sta_mld_mac_str, ap_mld_mac_str, sta_mld->ap_mld ? sta_mld->ap_mld->mac_str : "-"); + map_dm_remove_sta_mld(sta_mld); + sta_mld = NULL; + } + } + + /* Create if needed */ + if (!sta_mld) { + log_ctrl_i("ALE[%s]: creating STA_MLD[%s] on AP_MLD[%s]", ale->al_mac_str, sta_mld_mac_str, ap_mld_mac_str); + if (!(sta_mld = map_dm_create_sta_mld(ap_mld, tlv->sta_mld_mac))) { + log_ctrl_e("ALE[%s]: could not create STA_MLD[%s] on AP_MLD[%s]", ale->al_mac_str, sta_mld_mac_str, ap_mld_mac_str); + return -1; + } + } + + /* Update */ + sta_mld->enabled_mld_modes.str = tlv->str; + sta_mld->enabled_mld_modes.nstr = tlv->nstr; + sta_mld->enabled_mld_modes.emlsr = tlv->emlsr; + sta_mld->enabled_mld_modes.emlmr = tlv->emlmr; + + /* Create/update affiliated stas + NOTE: No longer "referenced" STA will be deleted automatically as they remain "marked" + */ + for (i = 0; i < tlv->aff_sta_nr; i++) { + map_assoc_sta_mld_conf_tlv_aff_sta_t *tlv_aff_sta = &tlv->aff_stas[i]; + mac_addr_str aff_sta_mac_str; + mac_addr_str bssid_str; + + mac_to_string(tlv_aff_sta->aff_sta_mac, aff_sta_mac_str); + mac_to_string(tlv_aff_sta->bssid, bssid_str); + + if (!(bss = map_dm_get_bss_from_ale(ale, tlv_aff_sta->bssid))) { + mac_to_string(tlv_aff_sta->bssid, bssid_str); + log_ctrl_e("ALE[%s]: could not find BSSID[%s] to add AFF_STA[%s]", ale->al_mac_str, bssid_str, aff_sta_mac_str); + continue; + } + + /* Can we assume that affiliated sta never roam? + For now - just delete mismatching STAs + */ + if ((sta = map_dm_get_sta_from_ale(ale, tlv_aff_sta->aff_sta_mac))) { + if (sta->sta_mld != sta_mld) { + log_ctrl_i("ALE[%s]: removing AFF_STA[%s] part of the wrong STA_MLD[%s <-> %s]", + ale->al_mac_str, aff_sta_mac_str, sta_mld_mac_str, sta->sta_mld ? sta->sta_mld->mac_str : "-"); + map_dm_remove_sta(sta); + sta = NULL; + } else if (sta->bss != bss) { + log_ctrl_i("ALE[%s]: removing AFF_STA[%s] part of the wrong BSS[%s <-> %s]", + ale->al_mac_str, aff_sta_mac_str, bssid_str, sta->bss->bssid_str); + map_dm_remove_sta(sta); + sta = NULL; + } + } + + if (!sta) { + log_ctrl_i("ALE[%s]: creating AFF_STA[%s] on STA_MLD[%s] and BSSID[%s]", ale->al_mac_str, aff_sta_mac_str, sta_mld_mac_str, bssid_str); + if (!(sta = map_dm_create_aff_sta(bss, sta_mld, tlv_aff_sta->aff_sta_mac))) { + log_ctrl_e("could not create AFF_STA[%s] on BSSID[%s] ALE[%s]", aff_sta_mac_str, bssid_str, ale->al_mac_str); + continue; + } + } + + map_dm_unmark_sta(sta); + } + + map_dm_unmark_sta_mld(sta_mld); + + return 0; +} + +/* MAP_R6 17.2.100 */ +int map_parse_aff_sta_metrics_tlv(map_ale_info_t *ale, map_aff_sta_metrics_tlv_t *tlv) +{ + map_sta_info_t *sta; + uint8_t byte_counter_unit = ale->map_profile >= MAP_PROFILE_2 ? + ale->agent_capability.byte_counter_unit : MAP_BYTE_COUNTER_UNIT_BYTES; + uint64_t tx_bytes = map_convert_mapunits_to_bytes(tlv->tx_bytes, byte_counter_unit); + uint64_t rx_bytes = map_convert_mapunits_to_bytes(tlv->rx_bytes, byte_counter_unit); + + if (!(sta = map_dm_get_sta_from_ale(ale, tlv->sta_mac))) { + log_ctrl_e("%s: sta[%s] not found", __FUNCTION__, mac_string(tlv->sta_mac)); + return -1; + } + + sta->traffic_stats.tx_bytes = tx_bytes; + sta->traffic_stats.rx_bytes = rx_bytes; + sta->traffic_stats.tx_packets = tlv->tx_packets; + sta->traffic_stats.rx_packets = tlv->rx_packets; + sta->traffic_stats.tx_packet_errors = tlv->tx_packet_errors; + sta->traffic_stats.rx_packet_errors = 0; /* Not available */ + sta->traffic_stats.retransmissions = 0; /* Not available */ + + return 0; +} + +/* MAP_R6 17.2.103 */ +int map_parse_eht_operations_tlv(map_ale_info_t *ale, map_eht_operations_tlv_t *tlv) +{ + int i, j; + + if (tlv->radios_nr == 0 || tlv->radios_nr > MAX_RADIO_PER_AGENT) { + return -1; + } + + for (i = 0; i < tlv->radios_nr; i++) { + map_radio_info_t *radio = map_dm_get_radio(ale, tlv->radios[i].ruid); + if (!radio) { + log_ctrl_e("%s: radio[%s] not found", __FUNCTION__, mac_string(tlv->radios[i].ruid)); + continue; + } + + if (!radio->eht_caps && !(radio->eht_caps = calloc(1, sizeof(*radio->eht_caps)))) { + log_ctrl_e("%s: calloc failed", __FUNCTION__); + return -1; + } + + if (tlv->radios[i].bsss_nr == 0 || tlv->radios[i].bsss_nr > MAX_BSS_PER_AGENT) { + return -1; + } + + for (j = 0; j < tlv->radios[i].bsss_nr; j++) { + map_bss_info_t *bss = map_dm_get_bss(radio, tlv->radios[i].bsss[j].bssid); + if (!bss) { + log_ctrl_e("%s: bss[%s] not found", __FUNCTION__, mac_string(tlv->radios[i].bsss[j].bssid)); + continue; + } + bss->eht_ops = tlv->radios[i].bsss[j].eht_ops; + } + } + + return 0; +} diff --git a/source/controller/src/map_ctrl_utils.c b/source/controller/src/map_ctrl_utils.c index d010f4f..cbf4221 100644 --- a/source/controller/src/map_ctrl_utils.c +++ b/source/controller/src/map_ctrl_utils.c @@ -75,11 +75,33 @@ static bool is_channel_operable(map_op_class_list_t *cap_list, uint8_t op_class_ return find_op_class(cap_list, op_class_nr, channel, false); } +/* Check if op_class_nr/channel is disallowed */ +static bool is_channel_disallowed(map_op_class_list_t *disallow_list, uint8_t op_class_nr, uint8_t channel) +{ + uint8_t i; + + for (i = 0; i < disallow_list->op_classes_nr; i++) { + map_op_class_t *op_class = &disallow_list->op_classes[i]; + + if (!op_class->enable) { + continue; + } + + /* Channel matches when channel list is empty or it is in the channel list */ + if (op_class->op_class == op_class_nr && map_is_channel_in_op_class(op_class->op_class, channel)) { + return map_cs_is_set(&op_class->channels, channel); + } + } + + return false; +} + /* Add op_class with pref and channel to list (if not present yet) Note: list is guaranteed to be big enough */ static void check_add_op_class_channel(map_op_class_list_t *merged_list, map_op_class_list_t *cap_list, - map_op_class_list_t *other_list, map_op_class_t *op_class, uint8_t channel) + map_op_class_list_t *other_list, map_op_class_list_t *disallowed_list, + map_op_class_t *op_class, uint8_t channel) { map_op_class_t *find_op_class = NULL; /* eliminate warning */ uint8_t pref, other_pref; @@ -90,6 +112,10 @@ static void check_add_op_class_channel(map_op_class_list_t *merged_list, map_op_ return; } + if (is_channel_disallowed(disallowed_list, op_class->op_class, channel)) { + return; + } + /* Get pref in other list, and use minimum */ other_pref = get_channel_pref(other_list, op_class->op_class, channel); pref = min(op_class->pref, other_pref); @@ -122,7 +148,8 @@ static void check_add_op_class_channel(map_op_class_list_t *merged_list, map_op_ /* Add all op_classes/channels from add_list to merged_list, using lowest pref from add_list or other_list */ static void merge_pref_op_class_list_add(map_op_class_list_t *merged_list, map_op_class_list_t *cap_list, - map_op_class_list_t *add_list, map_op_class_list_t *other_list) + map_op_class_list_t *add_list, map_op_class_list_t *other_list, + map_op_class_list_t *disallowed_list) { map_channel_set_t ch_set; uint8_t channel, i; @@ -136,27 +163,71 @@ static void merge_pref_op_class_list_add(map_op_class_list_t *merged_list, map_o if (0 != map_get_is_center_channel_from_op_class(op_class->op_class, &is_center_channel)) { continue; } - if (0 != map_get_channel_set_from_op_class(op_class->op_class, &ch_set)) { + + if ((is_center_channel && map_get_center_channel_set_from_op_class(op_class->op_class, &ch_set)) || + (!is_center_channel && map_get_channel_set_from_op_class(op_class->op_class, &ch_set))) { continue; } map_cs_foreach(&ch_set, channel) { - /* Use center channel for 40(6G)/80/160/320 MHz */ - uint8_t channel2 = channel; + check_add_op_class_channel(merged_list, cap_list, other_list, disallowed_list, op_class, channel); + } + } else { + map_cs_foreach(&op_class->channels, channel) { + check_add_op_class_channel(merged_list, cap_list, other_list, disallowed_list, op_class, channel); + } + } + } +} - if (is_center_channel && map_get_center_channel(op_class->op_class, channel, &channel2)) { - continue; - } +/* Add all op_classes/channels from disallowed_list to merged_list */ +static void merge_disallowed_op_class_list_add(map_op_class_list_t *merged_list, map_op_class_list_t *cap_list, + map_op_class_list_t *disallowed_list) +{ + uint8_t i; + + for (i = 0; i < disallowed_list->op_classes_nr; i++) { + map_op_class_t *op_class = &disallowed_list->op_classes[i]; + uint8_t channel, j; + + if (op_class->enable == false) { + continue; + } - check_add_op_class_channel(merged_list, cap_list, other_list, op_class, channel2); + if (map_cs_nr(&op_class->channels) == 0) { + continue; + } + + map_cs_foreach(&op_class->channels, channel) { + map_op_class_t *find_op_class = NULL; + + /* Do not add static non operable channels */ + if (!is_channel_operable(cap_list, op_class->op_class, channel)) { + continue; } - } else { - map_cs_foreach(&op_class->channels, channel) { - check_add_op_class_channel(merged_list, cap_list, other_list, op_class, channel); + /* Find op_class/pref... */ + for (j = 0; j < merged_list->op_classes_nr; j++) { + find_op_class = &merged_list->op_classes[j]; + + if (find_op_class->op_class == op_class->op_class && find_op_class->pref == 0) { + break; + } } + + /* ...op_class/pref not found */ + if (j == merged_list->op_classes_nr) { + find_op_class = &merged_list->op_classes[merged_list->op_classes_nr++]; + find_op_class->op_class = op_class->op_class; + find_op_class->pref = 0; + find_op_class->reason = 0; /* TODO... */ + } + + /* Add channel */ + map_cs_set(&find_op_class->channels, channel); } } + } static int comp_op_class(const void *obj1, const void *obj2) @@ -168,39 +239,124 @@ static int comp_op_class(const void *obj1, const void *obj2) return (a->op_class == b->op_class) ? a->pref - b->pref : a->op_class - b->op_class; } -static void map_cs_bw_set_with_bandlock_check(map_channel_bw_set_t *s, uint8_t bw, uint8_t c, bool is_5g_low_high, uint8_t bandlock_action) +static void map_update_radio_ctl_channels(map_radio_info_t *radio) { - if (!is_5g_low_high || - (bandlock_action == MAP_BANDLOCK_5G_DISABLED) || - ((bandlock_action == MAP_BANDLOCK_5G_LOW) && map_is_5G_low_ctl_channel(c)) || - ((bandlock_action == MAP_BANDLOCK_5G_HIGH) && map_is_5G_high_ctl_channel(c))) { - map_cs_bw_set(s, bw, c); + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; + bandlock_5g_t bandlock_5g = cfg->bandlock_5g; + bool is_5g_low_high = map_is_5g_low_high(radio); + map_op_class_list_t *cap_op_class_list = &radio->cap_op_class_list; + int i; + + /* Note: + - cap_ctl_channels: all ctl channels from cap op_class list + - ctl_channels : all ctl channels from op_class list that are allowed after applying config + (bandlock and allowed channel list) + */ + + map_cs_unset_all(&radio->cap_ctl_channels); + map_cs_unset_all(&radio->ctl_channels); + + /* Fill supported channel set based on 20MHz operating classes */ + for (i = 0; i < cap_op_class_list->op_classes_nr; i++) { + map_op_class_t *cap_op_class = &cap_op_class_list->op_classes[i]; + uint8_t op_class = cap_op_class->op_class; + map_channel_set_t ch_set; + uint16_t bw; + uint8_t band, channel; + bool bandlock_skip_op_class = false; + + if (map_get_bw_from_op_class(op_class, &bw) || bw != 20) { + continue; + } + + if (map_get_band_from_op_class(op_class, &band)) { + continue; + } + + if (map_get_channel_set_from_op_class(op_class, &ch_set)) { + continue; + } + + /* Unset non operable channels */ + map_cs_and_not(&ch_set, &cap_op_class->channels); + + /* Check if op class must be ignored because of of 5G bandlock */ + if (is_5g_low_high && bandlock_5g != MAP_BANDLOCK_5G_DISABLED) { + if ((bandlock_5g == MAP_BANDLOCK_5G_LOW && !map_is_5g_low_op_class(op_class)) || + (bandlock_5g == MAP_BANDLOCK_5G_HIGH && !map_is_5g_high_op_class(op_class))) { + bandlock_skip_op_class = true; + } + } + + /* Set all channels from operclass that are allowed by config... */ + map_cs_foreach(&ch_set, channel) { + map_cs_set(&radio->cap_ctl_channels, channel); + + if (!bandlock_skip_op_class && + ((band == IEEE80211_FREQUENCY_BAND_2_4_GHZ && map_cs_is_set(&cfg->allowed_channel_set_2g, channel)) || + (band == IEEE80211_FREQUENCY_BAND_5_GHZ && map_cs_is_set(&cfg->allowed_channel_set_5g, channel)) || + (band == IEEE80211_FREQUENCY_BAND_6_GHZ && map_cs_is_set(&cfg->allowed_channel_set_6g, channel)))) { + map_cs_set(&radio->ctl_channels, channel); + } + } } } -static void map_cs_bw_unset_range(map_channel_bw_set_t *s, uint8_t bw, uint8_t c, uint8_t op_class) +static void map_update_radio_channels_with_bandwidth(map_radio_info_t *radio) { - bool is_center_channel; + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; + bool is_6g_psc = (radio->supported_freq == BAND_6G) && cfg->allowed_channel_6g_psc; + map_op_class_list_t *cap_op_class_list = &radio->cap_op_class_list; + uint16_t allowed_bandwidth = map_get_allowed_bandwidth(radio->supported_freq); + int i; - if (map_get_is_center_channel_from_op_class(op_class, &is_center_channel)) { - return; - } + /* Create channel set per bandwidth using same logic as in set_controller_pref_op_class_list + - check global allowed bandwidth + - check global allowed channels and bandlock (use radio->ctl_channels set by map_update_radio_ctl_channels) + - for bw > 20MHz: only allowed if all subband channels are set unless 6G PSC is configured + */ + + map_cs_bw_unset_all(&radio->channels_with_bandwidth); + + for (i = 0; i < cap_op_class_list->op_classes_nr; i++) { + map_op_class_t *cap_op_class = &cap_op_class_list->op_classes[i]; + uint8_t op_class = cap_op_class->op_class; + map_channel_set_t ch_set; + uint16_t bw; + uint8_t channel; - if (is_center_channel) { - /* unset range of channels in this operating class */ - uint8_t ctl_c, sub_chan, sub_from, sub_to; + if (map_get_bw_from_op_class(op_class, &bw)) { + continue; + } - if (map_get_first_ctl_channel(op_class, c, &ctl_c) || - map_get_subband_channel_range(op_class, ctl_c, &sub_from, &sub_to)) { - return; + if (map_get_channel_set_from_op_class(op_class, &ch_set)) { + continue; } - for (sub_chan = sub_from; sub_chan <= sub_to; sub_chan++) { - map_cs_bw_unset(s, bw, sub_chan); + if (allowed_bandwidth > 0 && bw > allowed_bandwidth) { + continue; + } + + map_cs_and(&ch_set, &radio->ctl_channels); + + map_cs_foreach(&ch_set, channel) { + if (!map_is_6G_320MHz_op_class(op_class)) { + if (map_is_channel_in_cap_op_class(cap_op_class, channel)) { + if (bw == 20 || is_6g_psc || map_is_all_subband_channel_set(&radio->ctl_channels, op_class, channel)) { + map_cs_bw_set(&radio->channels_with_bandwidth, bw, channel); + } + } + } else { + /* 6G 320MHz is special because it has 2 overlapping sets of channels -> check both */ + foreach_bool(upper) { + if (map_is_channel_in_cap_op_class_6G_320MHz(cap_op_class, upper, channel)) { + if (is_6g_psc || map_is_all_subband_channel_set_6G_320MHz(&radio->ctl_channels, op_class, upper, channel)) { + map_cs_bw_set(&radio->channels_with_bandwidth, bw, channel); + } + } + } + } } - } else { - /* unset "this" channel only */ - map_cs_bw_unset(s, bw, c); } } @@ -212,6 +368,18 @@ map_controller_cfg_t* get_controller_cfg() return &map_cfg_get()->controller_cfg; } +uint16_t map_get_allowed_bandwidth(uint8_t band) +{ + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; + + switch (band) { + case BAND_2G: return cfg->allowed_bandwidth_2g; + case BAND_5G: return cfg->allowed_bandwidth_5g; + case BAND_6G: return cfg->allowed_bandwidth_6g; + default: return 0; /* Should not happen */ + } +} + void map_update_ale_receiving_iface(map_ale_info_t *ale, char* if_name) { map_strlcpy(ale->iface_name, if_name, sizeof(ale->iface_name)); @@ -255,7 +423,61 @@ int parse_update_client_capability(map_sta_info_t *sta, uint16_t assoc_frame_len /* Fill in sta capabilities */ map_80211_parse_assoc_body(&sta->sta_caps, sta->assoc_frame, sta->assoc_frame_len, - sta->bss->radio->supported_freq, (uint8_t*)sta->bss->ssid, sta->bss->ssid_len); + sta->bss->radio->supported_freq, (uint8_t*)sta->bss->ssid, sta->bss->ssid_len, + sta->sta_mld ? sta->mac : NULL); + return 0; +} + +/* Store assoc frame and parse it for each affiliated STA */ +int parse_update_mld_client_capability(map_sta_mld_info_t *sta_mld, uint16_t assoc_frame_len, uint8_t* assoc_frame) +{ + if (sta_mld == NULL || assoc_frame_len == 0 || assoc_frame == NULL) { + return -1; + } + + free(sta_mld->assoc_frame); + sta_mld->assoc_frame_len = 0; + + if (!(sta_mld->assoc_frame = malloc(assoc_frame_len))) { + log_ctrl_e("failed to allocate assoc frame"); + return -1; + } + + + sta_mld->assoc_frame_len = assoc_frame_len; + memcpy(sta_mld->assoc_frame, assoc_frame, assoc_frame_len); + + return parse_update_mld_aff_client_capability(sta_mld, true); +} + +/* Parse stored assoc frame for each affiliated STA (for all when force is true) */ +int parse_update_mld_aff_client_capability(map_sta_mld_info_t *sta_mld, bool force) +{ + map_sta_info_t *sta; + + if (sta_mld && sta_mld->assoc_frame) { + map_mld_modes_t *m = &sta_mld->supported_mld_modes; + /* Update capabilities for each affiliated STA and derive combined supported MLD modes + NOTE: mark NSTR and !STR if at least one aff STA has NSTR + */ + m->str = m->nstr = m->emlsr = m->emlmr = false; + + map_dm_foreach_aff_sta(sta_mld, sta) { + if (force || !sta->assoc_frame) { + parse_update_client_capability(sta, sta_mld->assoc_frame_len, sta_mld->assoc_frame); + } + + m->str |= sta->sta_caps.mld_modes.str; + m->nstr |= sta->sta_caps.mld_modes.nstr; + m->emlsr |= sta->sta_caps.mld_modes.emlsr; + m->emlmr |= sta->sta_caps.mld_modes.emlmr; + } + + if (m->nstr) { + m->str = false; + } + } + return 0; } @@ -267,6 +489,7 @@ void map_recompute_radio_state_and_update_ale_state(map_ale_info_t *ale) { map_radio_info_t *radio; map_onboard_status_t onboard_status = ALE_NODE_ONBOARDING; + uint16_t onboard_dep_bitmask; if (ale == NULL) { log_ctrl_e("invalid ale node onboarding state computation"); @@ -274,7 +497,8 @@ void map_recompute_radio_state_and_update_ale_state(map_ale_info_t *ale) } map_dm_foreach_radio(ale, radio) { - if ((radio->state & MAP_ONBOARD_DEP_BITMASK) == MAP_ONBOARD_DEP_BITMASK) { + onboard_dep_bitmask = is_radio_teardown_sent(radio->state) ? MAP_ONBOARD_DEP_BITMASK_TEARDOWN : MAP_ONBOARD_DEP_BITMASK; + if ((radio->state & onboard_dep_bitmask) == onboard_dep_bitmask) { onboard_status = ALE_NODE_ONBOARDED; break; } @@ -397,6 +621,25 @@ bool map_is_5g_low_high(map_radio_info_t *radio) (radio->band_type_5G & MAP_M2_BSS_RADIO5GU); } +bool map_is_radio_ap_mld_capable(map_ale_info_t *ale, map_radio_info_t *radio) +{ + map_mld_modes_t *m; + + return ((ale->agent_capability.max_mlds > 0) && (ale->agent_capability.ap_max_links > 0) && + ((m = radio->wifi7_caps ? &radio->wifi7_caps->ap_mld_modes : NULL)) && + (m->str || m->nstr || m->emlsr || m->emlsr)); +} + +bool map_is_radio_bsta_mld_capable(map_ale_info_t *ale, map_radio_info_t *radio) +{ + map_mld_modes_t *m; + + return ((ale->agent_capability.max_mlds > 0) && (ale->agent_capability.bsta_max_links > 0) && + (map_is_radio_bsta_capable(ale, radio->radio_id)) && + ((m = radio->wifi7_caps ? &radio->wifi7_caps->bsta_mld_modes : NULL)) && + (m->str || m->nstr || m->emlsr || m->emlsr)); +} + /* Guess which profile was used to configure this bss - using same logic as in map_get_m2_config */ @@ -517,9 +760,9 @@ uint8_t *map_get_wsc_attr(uint8_t *message, uint16_t message_size, uint16_t attr } /* Check if channel is in op class and not its non operable list */ -bool map_is_channel_in_cap_op_class(map_op_class_t *cap_op_class, uint8_t channel) +bool map_is_channel_in_cap_op_class(map_op_class_t *cap_op_class, uint8_t ctl_channel) { - uint8_t check_channel = channel; + uint8_t check_channel = ctl_channel; bool is_center_channel; if (map_get_is_center_channel_from_op_class(cap_op_class->op_class, &is_center_channel)) { @@ -529,7 +772,7 @@ bool map_is_channel_in_cap_op_class(map_op_class_t *cap_op_class, uint8_t channe /* For 20 and 40MHz (2G and 5G) op classes -> beacon channel For for 40MHz (6G), 80, 160 and 320MHz op classes (128, 129, 130, 132, 133, 134, 137) -> center channel */ - if (is_center_channel && map_get_center_channel(cap_op_class->op_class, channel, &check_channel)) { + if (is_center_channel && map_get_center_channel(cap_op_class->op_class, ctl_channel, &check_channel)) { return false; } @@ -540,6 +783,25 @@ bool map_is_channel_in_cap_op_class(map_op_class_t *cap_op_class, uint8_t channe return false; } +bool map_is_channel_in_cap_op_class_6G_320MHz(map_op_class_t *cap_op_class, bool upper, uint8_t ctl_channel) +{ + uint8_t center_channel; + + if (!map_is_6G_320MHz_op_class(cap_op_class->op_class)) { + return false; + } + + if (map_get_center_channel_6G_320MHz(cap_op_class->op_class, upper, ctl_channel, ¢er_channel)) { + return false; + } + + if (map_is_channel_in_op_class(cap_op_class->op_class, center_channel) && + !map_cs_is_set(&cap_op_class->channels, center_channel)) { + return true; + } + return false; +} + uint8_t map_get_channel_pref(map_op_class_list_t *list, uint8_t op_class, uint8_t channel) { return get_channel_pref(list, op_class, channel); @@ -547,90 +809,42 @@ uint8_t map_get_channel_pref(map_op_class_list_t *list, uint8_t op_class, uint8_ void map_update_radio_channels(map_radio_info_t *radio) { + map_update_radio_ctl_channels(radio); + map_update_radio_channels_with_bandwidth(radio); +} - map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; - bandlock_5g_t bandlock_5g = cfg->bandlock_5g; - bool is_5g_low_high = map_is_5g_low_high(radio); - map_op_class_list_t *cap_op_class_list = &radio->cap_op_class_list; - int i; - - map_cs_unset_all(&radio->cap_ctl_channels); - map_cs_unset_all(&radio->ctl_channels); - map_cs_bw_unset_all(&radio->channels_with_bandwidth); - - /* Go over all capable operating classes: - - fill capable control channel set based on 20MHz operating classes - - fill allowed control channel set based on 20MHz operating classes + config restrictions - - fill channels_with_bandwidth based on any operating class - */ - for (i = 0; i < cap_op_class_list->op_classes_nr; i++) { - map_op_class_t *op_class = &cap_op_class_list->op_classes[i]; - bandlock_5g_t bandlock_action = MAP_BANDLOCK_5G_DISABLED; - map_channel_set_t ch_set; - uint16_t bw; - uint8_t band, channel; - - if (0 != map_get_bw_from_op_class(op_class->op_class, &bw)) { - continue; - } - - if (0 != map_get_band_from_op_class(op_class->op_class, &band)) { - continue; - } - if (0 != map_get_channel_set_from_op_class(op_class->op_class, &ch_set)) { - continue; - } - - /* Skip any op class that is not allowed because of 5G bandlock */ - if (is_5g_low_high && bandlock_5g != MAP_BANDLOCK_5G_DISABLED) { - if ((bandlock_5g == MAP_BANDLOCK_5G_LOW && !map_is_5g_low_op_class(op_class->op_class)) || - (bandlock_5g == MAP_BANDLOCK_5G_HIGH && !map_is_5g_high_op_class(op_class->op_class))) { - bandlock_action = bandlock_5g; - } - } +int map_merge_pref_op_class_list(map_op_class_list_t *merged_list, map_op_class_list_t *cap_list, + map_op_class_list_t *list1, map_op_class_list_t *list2, + map_op_class_list_t *disallowed_list) +{ + uint8_t disallowed_cnt = 0; + uint8_t i; - /* Set all channels from operclass that are allowed by config... */ - map_cs_foreach(&ch_set, channel) { - if (bw == 20) { - map_cs_set(&radio->cap_ctl_channels, channel); - } - if ((band == IEEE80211_FREQUENCY_BAND_2_4_GHZ && map_cs_is_set(&cfg->allowed_channel_set_2g, channel)) || - (band == IEEE80211_FREQUENCY_BAND_5_GHZ && map_cs_is_set(&cfg->allowed_channel_set_5g, channel)) || - (band == IEEE80211_FREQUENCY_BAND_6_GHZ && map_cs_is_set(&cfg->allowed_channel_set_6g, channel))) { - if (bw == 20 && (!is_5g_low_high || bandlock_action == MAP_BANDLOCK_5G_DISABLED)) { - map_cs_set(&radio->ctl_channels, channel); - } - map_cs_bw_set_with_bandlock_check(&radio->channels_with_bandwidth, bw, channel, is_5g_low_high, bandlock_action); - } - } + for (i = 0; i < disallowed_list->op_classes_nr; i++) { + map_op_class_t *op_class = &disallowed_list->op_classes[i]; - /* ...and unset non operable channels */ - map_cs_foreach(&op_class->channels, channel) { - if (bw == 20) { - map_cs_unset(&radio->cap_ctl_channels, channel); - map_cs_unset(&radio->ctl_channels, channel); - } - map_cs_bw_unset_range(&radio->channels_with_bandwidth, bw, channel, op_class->op_class); + if (op_class->enable == true && op_class->channels.nr > 0) { + ++disallowed_cnt; } } -} -int map_merge_pref_op_class_list(map_op_class_list_t *merged_list, map_op_class_list_t *cap_list, - map_op_class_list_t *list1, map_op_class_list_t *list2) -{ merged_list->op_classes_nr = 0; /* Result cannot have more op_classes than the sum of list1 and list2 */ - merged_list->op_classes = calloc(list1->op_classes_nr + list2->op_classes_nr, sizeof(map_op_class_t)); + merged_list->op_classes = calloc(list1->op_classes_nr + list2->op_classes_nr + disallowed_cnt, sizeof(map_op_class_t)); if (!merged_list->op_classes) { return -1; } /* Add list 1 */ - merge_pref_op_class_list_add(merged_list, cap_list, list1, list2); + merge_pref_op_class_list_add(merged_list, cap_list, list1, list2, disallowed_list); /* Add list 2 */ - merge_pref_op_class_list_add(merged_list, cap_list, list2, list1); + merge_pref_op_class_list_add(merged_list, cap_list, list2, list1, disallowed_list); + + if (disallowed_cnt) { + merge_disallowed_op_class_list_add(merged_list, cap_list, disallowed_list); + } /* Clear channel lists when they contain all channels of an op class */ map_optimize_pref_op_class_list(merged_list, cap_list); @@ -651,27 +865,22 @@ void map_optimize_pref_op_class_list(map_op_class_list_t *list, map_op_class_lis map_op_class_t *op_class = &list->op_classes[i]; bool all = true; - if (0 != map_get_is_center_channel_from_op_class(op_class->op_class, &is_center_channel)) { + if (map_get_is_center_channel_from_op_class(op_class->op_class, &is_center_channel)) { continue; } - if (0 != map_get_channel_set_from_op_class(op_class->op_class, &ch_set)) { + + if ((is_center_channel && map_get_center_channel_set_from_op_class(op_class->op_class, &ch_set)) || + (!is_center_channel && map_get_channel_set_from_op_class(op_class->op_class, &ch_set))) { continue; } map_cs_foreach(&ch_set, channel) { - /* Use center channel for 40(6G)/80/160/320 MHz */ - uint8_t channel2 = channel; - - if (is_center_channel && map_get_center_channel(op_class->op_class, channel, &channel2)) { - continue; - } - /* Skip static non operable channels */ - if (!is_channel_operable(cap_list, op_class->op_class, channel2)) { + if (!is_channel_operable(cap_list, op_class->op_class, channel)) { continue; - } + } - if (!map_cs_is_set(&op_class->channels, channel2)) { + if (!map_cs_is_set(&op_class->channels, channel)) { all = false; } } @@ -682,15 +891,15 @@ void map_optimize_pref_op_class_list(map_op_class_list_t *list, map_op_class_lis } } -/* Given a channel/opclass combo, check if ALL of the corresponding 20MHz subband channels are UNSET in list +/* Given a op_class/channel combo, check if ALL of the corresponding 20MHz subband channels are UNSET in list * return TRUE if all 20MHz subband channels are UNSET set */ -/* Given a channel/opclass combo, check if ALL of the corresponding 20MHz subband channels are UNSET in list +/* Given a op_class/channel combo, check if ALL of the corresponding 20MHz subband channels are UNSET in list * return TRUE if all 20MHz subband channels are UNSET set */ -bool map_is_no_subband_channel_set(map_channel_set_t *channels, uint8_t chan, uint8_t op_class) +bool map_is_no_subband_channel_set(map_channel_set_t *channels, uint8_t op_class, uint8_t channel) { uint8_t sub_chan, sub_from, sub_to; - if (!map_get_subband_channel_range(op_class, chan, &sub_from, &sub_to)) { + if (!map_get_subband_channel_range(op_class, channel, &sub_from, &sub_to)) { for (sub_chan = sub_from; sub_chan <= sub_to; sub_chan += 4) { if (map_cs_is_set(channels, sub_chan)) { return false; @@ -701,14 +910,31 @@ bool map_is_no_subband_channel_set(map_channel_set_t *channels, uint8_t chan, ui return true; } -/* Given a channel/opclass combo, check if ALL corresponding 20MHz subband channels are SET in list */ +/* Same as above but for 6G 320MHz */ +bool map_is_no_subband_channel_set_6G_320MHz(map_channel_set_t *channels, uint8_t op_class, bool upper, uint8_t channel) +{ + uint8_t sub_chan, sub_from, sub_to; + + if (map_is_6G_320MHz_op_class(op_class) && + !map_get_subband_channel_range_6G_320MHz(op_class, upper, channel, &sub_from, &sub_to)) { + for (sub_chan = sub_from; sub_chan <= sub_to; sub_chan += 4) { + if (map_cs_is_set(channels, sub_chan)) { + return false; + } + } + } + + return true; +} + +/* Given a op_class/channel combo, check if ALL corresponding 20MHz subband channels are SET in list */ /* return true if ALL 20MHz subband channels are SET */ /* NOTE: not applicable for 20MHz op_class */ -bool map_is_all_subband_channel_set(map_channel_set_t *channels, uint8_t chan, uint8_t op_class) +bool map_is_all_subband_channel_set(map_channel_set_t *channels, uint8_t op_class, uint8_t channel) { uint8_t sub_chan, sub_from, sub_to; - if (!map_get_subband_channel_range(op_class, chan, &sub_from, &sub_to)) { + if (!map_get_subband_channel_range(op_class, channel, &sub_from, &sub_to)) { /* iterate over all subband channels */ /* for 2.4GHz: primary and secondary channel are also 4 channels separated from each other */ for (sub_chan = sub_from; sub_chan <= sub_to; sub_chan += 4) { @@ -721,6 +947,23 @@ bool map_is_all_subband_channel_set(map_channel_set_t *channels, uint8_t chan, u return true; } +/* Same as above but for 6G 320MHz */ +bool map_is_all_subband_channel_set_6G_320MHz(map_channel_set_t *channels, uint8_t op_class, bool upper, uint8_t channel) +{ + uint8_t sub_chan, sub_from, sub_to; + + if (map_is_6G_320MHz_op_class(op_class) && + !map_get_subband_channel_range_6G_320MHz(op_class, upper, channel, &sub_from, &sub_to)) { + for (sub_chan = sub_from; sub_chan <= sub_to; sub_chan += 4) { + if (!map_cs_is_set(channels, sub_chan)) { + return false; + } + } + } + + return true; +} + void map_sort_op_class_list(map_op_class_list_t *list) { qsort(list->op_classes, list->op_classes_nr, sizeof(map_op_class_t), comp_op_class); @@ -783,6 +1026,20 @@ map_local_iface_t *map_find_local_iface(map_ale_info_t *ale, mac_addr mac) return NULL; } +bool map_is_radio_bsta_capable(map_ale_info_t *ale, mac_addr radio_id) +{ + size_t i; + + for (i = 0; i < ale->backhaul_sta_iface_count; i++) { + map_backhaul_sta_iface_t *bhsta_iface = &ale->backhaul_sta_iface_list[i]; + if (bhsta_iface && !maccmp(bhsta_iface->radio_id, radio_id)) { + return true; + } + } + + return false; +} + map_backhaul_sta_iface_t *map_find_bhsta_iface_from_ale(map_ale_info_t *ale, mac_addr sta_mac) { size_t i; @@ -820,6 +1077,23 @@ void map_free_ht_vht_he_wifi6_caps(map_radio_info_t *radio) SFREE(radio->vht_caps); SFREE(radio->he_caps); SFREE(radio->wifi6_caps); + +} + +void map_free_wifi7_caps(map_radio_info_t *radio) +{ + if (radio->wifi7_caps) { + SFREE(radio->wifi7_caps->ap_str_records); + SFREE(radio->wifi7_caps->ap_nstr_records); + SFREE(radio->wifi7_caps->ap_emlsr_records); + SFREE(radio->wifi7_caps->ap_emlmr_records); + SFREE(radio->wifi7_caps->bsta_str_records); + SFREE(radio->wifi7_caps->bsta_nstr_records); + SFREE(radio->wifi7_caps->bsta_emlsr_records); + SFREE(radio->wifi7_caps->bsta_emlmr_records); + SFREE(radio->wifi7_caps); + } + } void map_update_radio_caps(map_radio_info_t *radio) @@ -829,6 +1103,7 @@ void map_update_radio_caps(map_radio_info_t *radio) map_radio_vht_capability_t *vht_caps = radio->vht_caps; map_radio_ht_capability_t *ht_caps = radio->ht_caps; map_radio_he_capability_t *he_caps = radio->he_caps; + map_radio_eht_capability_t *eht_caps = radio->eht_caps; bool is_2g = radio->supported_freq == IEEE80211_FREQUENCY_BAND_2_4_GHZ; bool is_5g = radio->supported_freq == IEEE80211_FREQUENCY_BAND_5_GHZ; bool is_6g = radio->supported_freq == IEEE80211_FREQUENCY_BAND_6_GHZ; @@ -836,8 +1111,7 @@ void map_update_radio_caps(map_radio_info_t *radio) /* Standard (forget about 11B) */ if (is_6g) { - /* hardcoded 11ax for 6GHz */ - caps->supported_standard = STD_80211_AX; + caps->supported_standard = eht_caps ? STD_80211_BE : STD_80211_AX; } else if (is_5g) { caps->supported_standard = (he_caps && vht_caps && ht_caps) ? STD_80211_ANACAX : (he_caps && vht_caps) ? STD_80211_ACAX : @@ -885,6 +1159,12 @@ void map_update_radio_caps(map_radio_info_t *radio) caps->max_bandwidth = ht_caps->ht_support_40mhz ? 40 : 20; } + if (is_6g && eht_caps) { + caps->max_bandwidth = 320; /* TODO this is an assumption for eth capable radios + * Fill caps properly when eht_caps are filled. + */ + } + /* Set SGI from HE, VHT/HT */ if (!is_2g && vht_caps) { caps->sgi_support = vht_caps->gi_support_160mhz || vht_caps->gi_support_80mhz; @@ -892,3 +1172,33 @@ void map_update_radio_caps(map_radio_info_t *radio) caps->sgi_support = ht_caps->gi_support_40mhz || ht_caps->gi_support_20mhz; } } + +bool map_is_non_bss_ap_mld_or_sta_mld(map_ale_info_t *ale, mac_addr bssid, mac_addr sta_mac) +{ + /* Return true when + - MLD AP found and it is no regular BSS + - OR MLD STA found + */ + + return map_dm_ale_has_mld(ale) && + ((bssid && map_dm_get_ap_mld(ale, bssid) && !map_dm_get_bss_from_ale(ale, bssid)) || + (sta_mac && map_dm_get_sta_mld_from_ale(ale, sta_mac))); +} + +map_sta_info_t *map_get_aff_sta_from_band(map_sta_mld_info_t *sta_mld, uint8_t band) +{ + map_sta_info_t *sta; + + map_dm_foreach_aff_sta(sta_mld, sta) { + if (sta->bss->radio->supported_freq == band) { + return sta; + } + } + + return NULL; +} + +map_sta_info_t *map_get_aff_sta_first(map_sta_mld_info_t *sta_mld) +{ + return (sta_mld->aff_sta_nr > 0) ? list_first_entry(&sta_mld->aff_sta_list, map_sta_info_t, aff_sta_list) : NULL; +} diff --git a/source/controller/src/map_ctrl_vendor.c b/source/controller/src/map_ctrl_vendor.c new file mode 100644 index 0000000..6fee6c4 --- /dev/null +++ b/source/controller/src/map_ctrl_vendor.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2019-2022 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "vendor" + +#include "map_ctrl_defines.h" +#include "map_ctrl_utils.h" +#include "map_ctrl_vendor.h" +#include "map_ctrl_emex_tlv_handler.h" + +/*####################################################################### +# DEFINES # +########################################################################*/ + +/*####################################################################### +# TYPEDEFS # +########################################################################*/ +typedef struct vendor_reboot_request_s { + map_ale_info_t *ale; + uint8_t action_type; + uint8_t reset_type; +} vendor_reboot_request_t; + +/*####################################################################### +# PROTOTYPES # +########################################################################*/ + +/*####################################################################### +# GLOBALS # +########################################################################*/ + +/*####################################################################### +# LOCAL FUNCTIONS # +########################################################################*/ +static int vendor_send_compl_cb(UNUSED int status, UNUSED void *args, UNUSED void *opaque_cmdu) +{ + return 0; +} + +static int vendor_send_reboot_request_retry_cb(void *args, uint16_t *mid) +{ + vendor_reboot_request_t *reboot_req = (vendor_reboot_request_t *)args; + + int ret = -1; + map_vendor_tlv_tuple_t tlvs[2]; + uint16_t tlv_type, msg_type; + uint8_t buf_msg_type_tlv[4] = {0}; + uint8_t *buf_reboot_request_tlv = NULL, *p; + uint8_t reboot_request_tlv_len = 0; + + /* AirTies Message Type TLV */ + tlv_type = EMEX_TLV_MESSAGE_TYPE; + p = buf_msg_type_tlv; + _I2B(&tlv_type, &p); + msg_type = EMEX_MESSAGE_REBOOT_REQUEST; + _I2B(&msg_type, &p); + + /* AirTies Reboot Request TLV */ + tlv_type = EMEX_TLV_REBOOT_REQUEST; + + if (reboot_req->action_type == MAP_EMEX_REBOOT_ACTION_REBOOT) { + reboot_request_tlv_len = 3; /* 2 (tlv type) + 1 (action) */ + } else if (reboot_req->action_type == MAP_EMEX_REBOOT_ACTION_RESET) { + reboot_request_tlv_len = 4; /* 2 (tlv type) + 1 (action) + 1 (reset type) */ + } else { + log_ctrl_e("[%s-%d] Failed to parse action type", __func__, __LINE__); + goto out; + } + + buf_reboot_request_tlv = calloc(1, reboot_request_tlv_len); + if (!buf_reboot_request_tlv) { + log_ctrl_e("[%s-%d] Failed to allocate memory", __func__, __LINE__); + goto out; + } + + p = buf_reboot_request_tlv; + _I2B(&tlv_type, &p); + _I1B(&reboot_req->action_type, &p); + + if (reboot_req->action_type == MAP_EMEX_REBOOT_ACTION_RESET) { + _I1B(&reboot_req->reset_type, &p); + } + + tlvs[0].len = 4; /* 2 (tlv type) + 2 (msg type) */ + tlvs[0].data = buf_msg_type_tlv; + tlvs[1].len = reboot_request_tlv_len; + tlvs[1].data = buf_reboot_request_tlv; + + if (map_ctrl_vendor_send_message(reboot_req->ale, tlvs, 2, mid)) { + goto out; + } + + ret = 0; + +out: + SFREE(buf_reboot_request_tlv); + + return ret; + +} + +/*####################################################################### +# PUBLIC FUNCTIONS # +########################################################################*/ +int map_ctrl_vendor_send_reboot_request(map_ale_info_t *ale, uint8_t action_type, uint8_t reset_type) +{ + vendor_reboot_request_t *reboot_req; + timer_id_t retry_id; + + if (WFA_CERT()) { + return 0; + } + + if (!map_emex_agent_is_feature_supported(ale, MAP_EMEX_FEATURE_REBOOT_RESET)) { + log_ctrl_i("Reboot request message is not supported by the agent [%s]", ale->al_mac_str); + return 0; + } + + map_dm_get_ale_timer_id(retry_id, ale, VENDOR_REBOOT_REQ_RETRY_ID); + + if (map_is_timer_registered(retry_id)) { + log_ctrl_i("Reboot request for agent [%s] already ongoing", ale->al_mac_str); + return 0; + } + + if (!(reboot_req = calloc(1, sizeof(vendor_reboot_request_t)))) { + log_ctrl_e("[%s-%d] Failed to allocate memory", __func__, __LINE__); + return -1; + } + + reboot_req->ale = ale; + reboot_req->action_type = action_type; + reboot_req->reset_type = reset_type; + + if (map_register_retry(retry_id, 5/*interval*/, 6/*retry*/, reboot_req, vendor_send_compl_cb, vendor_send_reboot_request_retry_cb)) { + log_ctrl_e("failed Registering retry timer[%s]", retry_id); + SFREE(reboot_req); + return -1; + } + + return 0; +} + +int map_ctrl_vendor_send_message(map_ale_info_t *ale, map_vendor_tlv_tuple_t tlvs[], + uint8_t tlvs_cnt, uint16_t *mid) +{ + map_vendor_specific_mult_tlv_t vs; + + /* Send the message */ + vs.ale = ale; + vs.oui[0] = AIRTIES_VENDOR_OUI_1; + vs.oui[1] = AIRTIES_VENDOR_OUI_2; + vs.oui[2] = AIRTIES_VENDOR_OUI_3; + vs.tlvs_cnt = tlvs_cnt; + vs.tlvs = tlvs; + return map_send_vendor_specific_mult_tlvs(&vs, mid); +} + +void map_ctrl_vendor_fini(void) +{ +} + +int map_ctrl_vendor_init(void) +{ + return 0; +} diff --git a/source/controller/src/map_ctrl_wfa_capi.c b/source/controller/src/map_ctrl_wfa_capi.c index 403dcad..828087d 100644 --- a/source/controller/src/map_ctrl_wfa_capi.c +++ b/source/controller/src/map_ctrl_wfa_capi.c @@ -530,7 +530,7 @@ static int capi_dev_set_config(capi_params_t *params, map_printf_cb_t print_cb) free(tlv); } - map_profile_dump(cfg); + map_profile_dump(); capi_response(print_cb, CAPI_STATUS_COMPLETE, ""); return 0; @@ -653,7 +653,7 @@ static int capi_dev_send_1905(capi_params_t *params, map_printf_cb_t print_cb) log_ctrl_i(" Send Autoconfig Renew"); i1905_get_mcast_mac(mcast_mac); - if (map_send_autoconfig_renew(IEEE80211_FREQUENCY_BAND_2_4_GHZ, &mid)) { + if (map_send_autoconfig_renew(IEEE80211_FREQUENCY_BAND_2_4_GHZ, &mid, false)) { goto fail; } } diff --git a/source/ieee1905/src/al/internal_interfaces/al.h b/source/ieee1905/src/al/internal_interfaces/al.h index c2b2dfc..0576c2a 100644 --- a/source/ieee1905/src/al/internal_interfaces/al.h +++ b/source/ieee1905/src/al/internal_interfaces/al.h @@ -134,7 +134,7 @@ typedef bool (*al1905_cmdu_cb_t)(i1905_cmdu_t *cmdu); */ uint8_t start1905AL(mac_addr al_mac_address, uint8_t map_whole_network_flag, char *registrar_interface, i1905_interface_cb_t interface_cb, - al1905_cmdu_cb_t cmdu_cb); + al1905_cmdu_cb_t cmdu_cb, i1905_key_info_cb_t key_info_cb); /* Stop 1905 AL layer */ void stop1905AL(void); diff --git a/source/ieee1905/src/al/internal_interfaces/platform_crypto.h b/source/ieee1905/src/al/internal_interfaces/platform_crypto.h index f5b0ffe..f309e73 100644 --- a/source/ieee1905/src/al/internal_interfaces/platform_crypto.h +++ b/source/ieee1905/src/al/internal_interfaces/platform_crypto.h @@ -148,4 +148,23 @@ uint8_t PLATFORM_AES_ENCRYPT(uint8_t *key, uint8_t *iv, uint8_t *data, uint32_t */ uint8_t PLATFORM_AES_DECRYPT(uint8_t *key, uint8_t *iv, uint8_t *data, uint32_t data_len); +/* Encrypt the provided 'data' (which is a pointer to a buffer of variable size) +* according to AES-SIV encryption (RFC 5297) with provided 'params' and 'key'. +* +* The result is written to 'out' buffer and has the length of ('data_len' + AES_BLOCK_SIZE). +* +* Return "0" if there was a problem, "1" otherwise. +*/ +uint8_t PLATFORM_AES_SIV_ENCRYPT(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len, + size_t num_params, uint8_t *params[], size_t *params_lens, uint8_t *out); + +/* Works similarly with "PLATFORM_AES_SIV_ENCRYPT". +* +* The result (decrypted data) is written to 'out' buffer. +* +* Return "0" if there was a problem, "1" otherwise. +*/ +uint8_t PLATFORM_AES_SIV_DECRYPT(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len, + size_t num_params, uint8_t *params[], size_t *params_lens, uint8_t *out); + #endif /* PLATFORM_CRYPTO_H_ */ diff --git a/source/ieee1905/src/al/internal_interfaces/platform_interfaces.h b/source/ieee1905/src/al/internal_interfaces/platform_interfaces.h index 52ef090..6dda2f1 100644 --- a/source/ieee1905/src/al/internal_interfaces/platform_interfaces.h +++ b/source/ieee1905/src/al/internal_interfaces/platform_interfaces.h @@ -76,6 +76,8 @@ #include "media_specific_blobs.h" /* struct genericInterfaceType */ +#include "map_utils.h" + /* Return a list of strings (each one representing an "interface name", such * as "eth0", "eth1", etc...). * diff --git a/source/ieee1905/src/al/internal_interfaces/platform_os.h b/source/ieee1905/src/al/internal_interfaces/platform_os.h index 129408b..eaa20b1 100644 --- a/source/ieee1905/src/al/internal_interfaces/platform_os.h +++ b/source/ieee1905/src/al/internal_interfaces/platform_os.h @@ -116,7 +116,7 @@ typedef struct deviceInfo { typedef void (*i1905_packet_cb_t)(char *if_name, uint8_t *packet, uint16_t packet_len); /* Initialize platform os layer */ -uint8_t PLATFORM_OS_INIT(i1905_interface_cb_t interface_cb, i1905_packet_cb_t packet_cb); +uint8_t PLATFORM_OS_INIT(i1905_interface_cb_t interface_cb, i1905_packet_cb_t packet_cb, i1905_key_info_cb_t key_info_cb); /* Fini platform os layer */ void PLATFORM_OS_FINI(void); @@ -133,6 +133,18 @@ void PLATFORM_OS_FREE_LIST_OF_1905_INTERFACES(char **interfaces, uint8_t nr); /* Get info about a specific interface */ void PLATFORM_OS_GET_1905_INTERFACE_INFO(char *if_name, i1905_interface_info_t *info); +/* Get interface mac address */ +int PLATFORM_OS_GET_1905_INTERFACE_MAC(char *if_name, mac_addr mac); + +/* Set interface type */ +void PLATFORM_OS_SET_1905_INTERFACE_TYPE(char *if_name, uint16_t type); + +/* Set 802.11 media specific info */ +void PLATFORM_OS_SET_1905_INTERFACE_80211_MEDIA_SPECIFIC_INFO(char *if_name, mac_addr network_membership, + uint8_t role, uint8_t ap_channel_band, + uint8_t ap_channel_center_freq_1, + uint8_t ap_channel_center_freq_2); + /* Check if interface is up (to avoid expesive interface info call) */ bool PLATFORM_OS_IS_INTERFACE_UP(char *if_name); @@ -151,4 +163,7 @@ mac_addr* PLATFORM_OS_GET_GATEWAY_MAC(void); /* Check if log level is at least trace */ bool PLATFORM_OS_LOG_LEVEL_TRACE(void); +/* Get secure 1905 key information for ale */ +int PLATFORM_OS_GET_KEY_INFO(uint8_t *al_mac, map_1905_sec_key_info_t *key_info); + #endif /* PLATFORM_OS_H_ */ diff --git a/source/ieee1905/src/al/src_independent/al_entity.c b/source/ieee1905/src/al/src_independent/al_entity.c index 180665a..f9d3a24 100644 --- a/source/ieee1905/src/al/src_independent/al_entity.c +++ b/source/ieee1905/src/al/src_independent/al_entity.c @@ -104,6 +104,7 @@ #include "platform_interfaces.h" #include "platform_os.h" +#include "platform_crypto.h" /*####################################################################### # DEFINES # @@ -196,6 +197,177 @@ static check_duplicates_t g_check_dup; /*####################################################################### # PRIVATE FUNCTIONS # ########################################################################*/ +static int decrypt_encrypted_payload_tlv(map_1905_sec_key_info_t *key_info, uint8_t *cmdu_header, uint8_t *tlv, + uint8_t *src_mac, uint8_t *dst_mac, uint8_t *out, uint16_t *out_len) +{ +#define NUM_PARAMS 4 +#define CMDU_OCTETS_LEN 6 + + uint8_t *decrypted_payload = NULL; + uint16_t decrypted_payload_len = 0; + uint8_t *params[NUM_PARAMS] = {0}; + size_t params_lens[NUM_PARAMS] = {0}; + map_encrypted_payload_tlv_t ep_tlv = {0}; + uint8_t *p = tlv; + + _E1B(&p, &ep_tlv.tlv_type); + p += 2; /* p now shows encr_tx_counter (skip tlv_length) ! */ + _EnB(&p, ep_tlv.encr_tx_counter, ENCRYPTION_TX_COUNTER_LEN); + _EnB(&p, ep_tlv.src_al_mac, MAC_ADDR_LEN); + _EnB(&p, ep_tlv.dst_al_mac, MAC_ADDR_LEN); + _E2B(&p, &ep_tlv.siv_len); /* p now shows siv_output ! */ + + /* tx counter (from incoming frame) should be greater than the rx counter (which we increase for each decrypted packet) */ + if (compare_counter_48(ep_tlv.encr_tx_counter, key_info->encr_rx_counter) != 1) { + log_i1905_d("possible replay attack, dropping packet"); + return -1; + } + + decrypted_payload_len = ep_tlv.siv_len - AES_BLOCK_SIZE; + decrypted_payload = calloc(1, decrypted_payload_len); + if (!decrypted_payload) { + log_i1905_e("failed to allocate buffer for decrypted payload"); + return -1; + } + + /* MAP R4 13.3.2 Decryption Requirements + * K = 1905 TK (256 bit) corresponding to the sender + * Z = value of the AES-SIV Encryption Output field. + * AD1 = The first six octets of the received 1905 CMDU. + * AD2 = The Encryption Transmission Counter value in the Encrypted Payload TLV. + * AD3 = Source 1905 AL MAC Address value in the Encrypted Payload TLV. + * AD4 = Destination 1905 AL MAC Address value in the Encrypted Payload TLV. + */ + uint8_t cmdu_octets[CMDU_OCTETS_LEN] = {0}; + + memcpy(cmdu_octets, cmdu_header, sizeof(cmdu_octets)); + params[0] = cmdu_octets; + params[1] = ep_tlv.encr_tx_counter; + params[2] = src_mac; + params[3] = dst_mac; + params_lens[0] = CMDU_OCTETS_LEN; + params_lens[1] = ENCRYPTION_TX_COUNTER_LEN; + params_lens[2] = ETHER_ADDR_LEN; + params_lens[3] = ETHER_ADDR_LEN; + + if (PLATFORM_AES_SIV_DECRYPT(key_info->ptk, key_info->ptk_len, p, ep_tlv.siv_len, NUM_PARAMS, params, params_lens, decrypted_payload) != 1) { + log_i1905_e("AES-SIV decryption failed during 1905 decryption"); + free(decrypted_payload); + return -1; + } + + memcpy(out, decrypted_payload, decrypted_payload_len); + *out_len = decrypted_payload_len; + + free(decrypted_payload); + return 0; +} + +static int calculate_mic(uint8_t *stream, uint16_t stream_len, + uint8_t *key, size_t key_len, + map_mic_tlv_t *mic_tlv, i1905_cmdu_t *cmdu) +{ + #define CMDU_OCTETS_LEN 6 + #define MIC_TLV_OCTETS_LEN 13 + + uint8_t mic_value[SHA256_MAC_LEN] = {0}; + uint8_t *params[3] = {0}; + uint32_t params_lens[3] = {0}; + uint8_t cmdu_octets[CMDU_OCTETS_LEN] = {0}; + uint8_t *p = cmdu_octets; + uint8_t reserved = 0; + + _I1B(&cmdu->message_version, &p); + _I1B(&reserved, &p); + _I2B(&cmdu->message_type, &p); + _I2B(&cmdu->message_id, &p); + + uint8_t mic_tlv_octets[MIC_TLV_OCTETS_LEN] = {0}; + + /* skip tlv_type while copying */ + memcpy(mic_tlv_octets, (&mic_tlv->tlv_type) + 1, MIC_TLV_OCTETS_LEN); + + params[0] = cmdu_octets; + params[1] = mic_tlv_octets; + params[2] = stream; + params_lens[0] = CMDU_OCTETS_LEN; + params_lens[1] = MIC_TLV_OCTETS_LEN; + params_lens[2] = stream_len; + + /* calculate MIC (using HMAC-SHA256) */ + if (PLATFORM_HMAC_SHA256(key, key_len, 3, params, params_lens, mic_value) == 0) { + log_i1905_e("HMAC-SHA256 failed during MIC calculation!"); + return -1; + } + + mic_tlv->mic_len = SHA256_MAC_LEN; + memcpy(mic_tlv->mic, mic_value, SHA256_MAC_LEN); + + return 0; +} + +static bool mic_check(i1905_cmdu_t *cmdu, uint8_t *stream, uint16_t stream_len, map_1905_sec_key_info_t *key_info) +{ + map_mic_tlv_t *mic_tlv = NULL; + map_mic_tlv_t calculated = {0}; + + uint8_t *tlvs_start = stream + CMDU_HDR_SIZE; + uint16_t buf_len = 0; + uint8_t *buf = NULL; + + mic_tlv = (map_mic_tlv_t *) i1905_get_tlv_from_cmdu(TLV_TYPE_MIC, cmdu); + if (mic_tlv == NULL) { + return false; + } + + calculated.mic = calloc(1, SHA256_MAC_LEN); + if (!calculated.mic) { + free(mic_tlv->mic); + return false; + } + + if (mic_tlv->integrity_tx_counter <= key_info->integrity_rx_counter) { + log_i1905_e("possible replay attack, dropping packet"); + free(mic_tlv->mic); + free(calculated.mic); + return false; + } + + /** keep the size of EOM TLV as it is included in MIC calculation and + * copy rest of the TLVs upto MIC TLV into a buffer for MIC calculation + */ + buf_len = stream_len - (CMDU_HDR_SIZE + SIZEOF_MIC_TLV + SHA256_MAC_LEN); + + buf = calloc(1, buf_len); + if (!buf) { + free(mic_tlv->mic); + free(calculated.mic); + return false; + } + memcpy(buf, tlvs_start, buf_len - TLV_HDR_SIZE); /* leave last 3 bytes as zero for EOM TLV */ + + if (calculate_mic(buf, buf_len, key_info->gtk, key_info->gtk_len, &calculated, cmdu) != 0) { + log_i1905_e("failed to calculate MIC"); + free(buf); + free(mic_tlv->mic); + free(calculated.mic); + return false; + } + + if (memcmp(mic_tlv->mic, calculated.mic, SHA256_MAC_LEN)) { + log_i1905_e("MIC mismatch"); + free(buf); + free(mic_tlv->mic); + free(calculated.mic); + return false; + } + + free(buf); + free(mic_tlv->mic); + free(calculated.mic); + return true; +} + /* CMDUs can be received in multiple fragments/packets when they are too big to * fit in a single "network transmission unit" (which is never bigger than * MAX_NETWORK_SEGMENT_SIZE). @@ -235,29 +407,6 @@ static check_duplicates_t g_check_dup; * - 'len' is the length of this 'packet_buffer' in bytes */ -/* FRV: Notes about fragmentation -1) The original 1905 stack only keeps the payload (called stream) and does not - use the length in the parser. Also it did expect an end of message TLV in - every fragment. - - EM R2 specifically states that there should only be an end of message TLV in - the last fragment. - - To handle this: - - the length is stored also and checked by the parser. - - end of message cmdu in stream that does not have the last fragment bit set - is ignored - -2) FUTURE: In EMR1/2, fragmentation is on a TLV boundary. In EMR3, this restriction - is removed. - - Currently, all packets are stored in the streams array. To handle EM3 case, - the fragments must first be concatenated and then handled to the parser - as one big packet. - - In this case unwanted end of message TLV's must be removed. -*/ - static i1905_cmdu_t *reassemble_fragmented_cmdu(uint8_t *packet_buffer, uint16_t len) { struct ether_header *eh = (struct ether_header*) packet_buffer; @@ -271,6 +420,14 @@ static i1905_cmdu_t *reassemble_fragmented_cmdu(uint8_t *packet_buffer, uint16_t uint8_t i, j; uint8_t *p = &packet_buffer[sizeof(struct ether_header)]; /* After ethernet header */ + map_1905_sec_key_info_t key_info; + + memset(&key_info, 0, sizeof(key_info)); + + if (PLATFORM_OS_GET_KEY_INFO(src_addr, &key_info)) { + log_i1905_t("Key Info for ale (%s) does not exist...", mac_string(src_addr)); + } + len -= sizeof(struct ether_header); log_i1905_t("Parse Stream Length From Reassembly:%d", len); @@ -427,6 +584,42 @@ static i1905_cmdu_t *reassemble_fragmented_cmdu(uint8_t *packet_buffer, uint16_t } } + /* --- 1905 decrypt start --- */ + do { + /* only decrypt unfragmented frames for phase-1 as per our talk with Koen => (fragment id = 0 and is last fragment) */ + if (fragment_id == 0 && last_fragment_indicator == 1) { + uint8_t decrypted_tlvs[MAX_TLV_SIZE] = {0}; + uint16_t decrypted_tlvs_len = 0; + + uint8_t *cmdu_header = g_reassemble.mids_in_flight[i].streams[0]; + uint8_t *tlv = cmdu_header + CMDU_HDR_SIZE; + + /* only try to decrypt messages iff; + * 1. it contains an Encrypted Payload TLV + * 2. we have a valid key for that ale + */ + if (*tlv == TLV_TYPE_ENCRYPTED_PAYLOAD) { + if (key_info.ptk_len > 0) { + if (decrypt_encrypted_payload_tlv(&key_info, cmdu_header, tlv, src_addr, dst_addr, decrypted_tlvs, &decrypted_tlvs_len) < 0) { + log_i1905_e("Failed to decrypt message"); + return NULL; + } + increment_counter_48(key_info.encr_rx_counter); + + /* replace the encrypted payload with the decrypted payload: + * 1. clean up the old tlvs buffer (excluding the cmdu header) + * 2. copy the decrypted payload into the buffer + * 3. update the lengths array + */ + memset(tlv, 0, g_reassemble.mids_in_flight[i].lengths[0] - CMDU_HDR_SIZE); + memcpy(tlv, decrypted_tlvs, decrypted_tlvs_len); + g_reassemble.mids_in_flight[i].lengths[0] = decrypted_tlvs_len + CMDU_HDR_SIZE + TLV_HDR_SIZE; /* 3 bytes for EOM TLV */ + } + } + } + } while(0); + /* --- 1905 decrypt end --- */ + c = parse_1905_CMDU_from_packets(g_reassemble.mids_in_flight[i].streams, g_reassemble.mids_in_flight[i].lengths); if (NULL == c) { @@ -436,6 +629,24 @@ static i1905_cmdu_t *reassemble_fragmented_cmdu(uint8_t *packet_buffer, uint16_t maccpy(c->cmdu_stream.src_mac_addr, src_addr); } + /* MIC calculation and check start*/ + do { + if (c && fragment_id == 0 && last_fragment_indicator == 1) { + bool is_multicast = is_multicast_ether_addr(dst_addr); + + if (is_multicast && key_info.gtk_len > 0) { + if (!mic_check(c, g_reassemble.mids_in_flight[i].streams[0], g_reassemble.mids_in_flight[i].lengths[0], &key_info)) { + log_i1905_e("MIC check failed"); + free(c); + c = NULL; + break; + } + increment_counter_48(key_info.integrity_rx_counter); + } + } + } while (0); + /* MIC calculation and check end */ + for (j = 0; j <= g_reassemble.mids_in_flight[i].last_fragment; j++) { free(g_reassemble.mids_in_flight[i].streams[j]); } @@ -791,7 +1002,7 @@ static void packet_cb(char *if_name, uint8_t *packet, uint16_t packet_len) ########################################################################*/ uint8_t start1905AL(mac_addr al_mac_address, uint8_t map_whole_network_flag, UNUSED char *registrar_interface, i1905_interface_cb_t interface_cb, - al1905_cmdu_cb_t cmdu_cb) + al1905_cmdu_cb_t cmdu_cb, i1905_key_info_cb_t key_info_cb) { /* Initialize platform-specific code */ if (0 == PLATFORM_INIT()) { @@ -814,7 +1025,7 @@ uint8_t start1905AL(mac_addr al_mac_address, uint8_t map_whole_network_flag, g_cmdu_cb = cmdu_cb; /* Must be after DMinit as call below adds interfaces */ - if (0 == PLATFORM_OS_INIT(interface_cb, packet_cb)) { + if (0 == PLATFORM_OS_INIT(interface_cb, packet_cb, key_info_cb)) { log_i1905_e("Failed to initialize platform os"); return AL_ERROR_OS; } diff --git a/source/ieee1905/src/al/src_independent/al_send.c b/source/ieee1905/src/al/src_independent/al_send.c index fd30f26..033e06f 100644 --- a/source/ieee1905/src/al/src_independent/al_send.c +++ b/source/ieee1905/src/al/src_independent/al_send.c @@ -90,6 +90,7 @@ #include "1905_platform.h" #include "platform_os.h" +#include "platform_crypto.h" #include "platform_interfaces.h" /*####################################################################### @@ -152,8 +153,7 @@ static int _obtainLocalDeviceInfoTLV(i1905_device_information_tlv_t *device_info case INTERFACE_TYPE_IEEE_802_11N_5_GHZ: case INTERFACE_TYPE_IEEE_802_11AC_5_GHZ: case INTERFACE_TYPE_IEEE_802_11AD_60_GHZ: - case INTERFACE_TYPE_IEEE_802_11AF: - case INTERFACE_TYPE_IEEE_802_11AX: { + case INTERFACE_TYPE_IEEE_802_11AF: { interface->media_specific_data_size = 10; maccpy(interface->media_specific_data.ieee80211.network_membership, x->interface_type_data.ieee80211.bssid); interface->media_specific_data.ieee80211.role = x->interface_type_data.ieee80211.role; @@ -167,6 +167,8 @@ static int _obtainLocalDeviceInfoTLV(i1905_device_information_tlv_t *device_info memcpy(interface->media_specific_data.ieee1901.network_identifier, x->interface_type_data.ieee1901.network_identifier, 7); break; } + case INTERFACE_TYPE_IEEE_802_11AX: /* Do not add media specific info for 11AX */ + case INTERFACE_TYPE_IEEE_802_11BE: /* Do not add media specific info for 11BE */ default: { interface->media_specific_data_size = 0; interface->media_specific_data.dummy = 0; @@ -254,6 +256,7 @@ static int _obtainAutoconfigFreqBandTLV(i1905_autoconfig_freq_band_tlv_t *ac_fre INTERFACE_TYPE_IEEE_802_11N_5_GHZ == x->interface_type || INTERFACE_TYPE_IEEE_802_11AC_5_GHZ == x->interface_type || INTERFACE_TYPE_IEEE_802_11AX == x->interface_type || + INTERFACE_TYPE_IEEE_802_11BE == x->interface_type || INTERFACE_TYPE_IEEE_802_11AD_60_GHZ == x->interface_type) && IEEE80211_ROLE_AP == x->interface_type_data.ieee80211.role) { @@ -274,7 +277,7 @@ static int _obtainAutoconfigFreqBandTLV(i1905_autoconfig_freq_band_tlv_t *ac_fre /* MAP R2 defined media type 11AX (wifi6) is not band specific. Frequency band is irrelevant for MAP anyhow. */ - else if (INTERFACE_TYPE_IEEE_802_11AX == x->interface_type) { + else if (INTERFACE_TYPE_IEEE_802_11AX == x->interface_type || INTERFACE_TYPE_IEEE_802_11BE == x->interface_type) { unconfigured_ap_band = IEEE80211_FREQUENCY_BAND_5_GHZ; } #endif @@ -306,15 +309,185 @@ static int _obtainAutoconfigFreqBandTLV(i1905_autoconfig_freq_band_tlv_t *ac_fre return 0; } +static bool pre_filter_encryption(uint16_t message_type) { + if (message_type == CMDU_TYPE_MAP_DIRECT_ENCAP_DPP || + message_type == CMDU_TYPE_MAP_1905_ENCAP_EAPOL || + message_type == CMDU_TYPE_MAP_ACK) { + return false; + } + + return true; +} + +static int calculate_mic(uint8_t *stream, uint16_t stream_len, + uint8_t *key, size_t key_len, + map_mic_tlv_t *mic_tlv, i1905_cmdu_t *cmdu) +{ + #define CMDU_OCTETS_LEN 6 + #define MIC_TLV_OCTETS_LEN 13 + + uint8_t mic_value[SHA256_MAC_LEN] = {0}; + uint8_t *params[3] = {0}; + uint32_t params_lens[3] = {0}; + uint8_t cmdu_octets[CMDU_OCTETS_LEN] = {0}; + uint8_t *p = cmdu_octets; + uint8_t reserved = 0; + + _I1B(&cmdu->message_version, &p); + _I1B(&reserved, &p); + _I2B(&cmdu->message_type, &p); + _I2B(&cmdu->message_id, &p); + + uint8_t mic_tlv_octets[MIC_TLV_OCTETS_LEN] = {0}; + + /* skip tlv_type while copying */ + memcpy(mic_tlv_octets, (&mic_tlv->tlv_type) + 1, MIC_TLV_OCTETS_LEN); + + params[0] = cmdu_octets; + params[1] = mic_tlv_octets; + params[2] = stream; + params_lens[0] = CMDU_OCTETS_LEN; + params_lens[1] = MIC_TLV_OCTETS_LEN; + params_lens[2] = stream_len; + + /* calculate MIC (using HMAC-SHA256) */ + if (PLATFORM_HMAC_SHA256(key, key_len, 3, params, params_lens, mic_value) == 0) { + log_i1905_e("HMAC-SHA256 failed during MIC calculation!"); + return -1; + } + + mic_tlv->mic_len = SHA256_MAC_LEN; + memcpy(mic_tlv->mic, mic_value, SHA256_MAC_LEN); + + return 0; +} + +static map_encrypted_payload_tlv_t *encrypt1905RawPacket(uint8_t *stream, uint16_t stream_len, + uint8_t *src_mac, uint8_t *dest_mac, + i1905_cmdu_t *cmdu, uint8_t *encr_tx_counter, + uint8_t *key, size_t key_len) +{ +#define CMDU_OCTETS_LEN 6 + + uint8_t *encrypted_payload = NULL; + uint16_t encrypted_payload_len = 0; + uint8_t *params[4] = {0}; + size_t params_lens[4] = {0}; + + /* exclude End Of Message TLV from the stream */ + stream_len -= TLV_HDR_SIZE; + + encrypted_payload_len = stream_len + AES_BLOCK_SIZE; + encrypted_payload = calloc(1, encrypted_payload_len); + if (!encrypted_payload) { + log_i1905_e("failed to allocate buffer for encrypted payload"); + return NULL; + } + + /* MAP R4 13.3.2 Encryption Requirements + * K = 1905 TK (256 bit) corresponding to the receiver + * P = all of the TLVs in the message concatenated (except the End of Message TLV) + * AD1 = The first six octets of the 1905 CMDU. + * AD2 = The Encryption Transmission Counter value at the sender from the field in Encrypted Payload TLV. + * AD3 = Source 1905 AL MAC Address from the field in Encrypted Payload TLV. + * AD4 = Destination 1905 AL MAC Address from the field in Encrypted Payload TLV. + */ + uint8_t cmdu_octets[CMDU_OCTETS_LEN] = {0}; + uint8_t *p = cmdu_octets; + uint8_t reserved = 0; + + _I1B(&cmdu->message_version, &p); + _I1B(&reserved, &p); + _I2B(&cmdu->message_type, &p); + _I2B(&cmdu->message_id, &p); + + params[0] = cmdu_octets; + params[1] = encr_tx_counter; + params[2] = src_mac; + params[3] = dest_mac; + params_lens[0] = CMDU_OCTETS_LEN; + params_lens[1] = ENCRYPTION_TX_COUNTER_LEN; + params_lens[2] = ETHER_ADDR_LEN; + params_lens[3] = ETHER_ADDR_LEN; + + if (PLATFORM_AES_SIV_ENCRYPT(key, key_len, stream, stream_len, 4, params, params_lens, encrypted_payload) == 0) { + log_i1905_e("AES-SIV encryption failed during 1905 encryption!"); + free(encrypted_payload); + return NULL; + } + + map_encrypted_payload_tlv_t *tlv = calloc(1, sizeof(map_encrypted_payload_tlv_t)); + if (!tlv) { + log_i1905_e("failed to allocate buffer for Encrypted Payload TLV"); + free(encrypted_payload); + return NULL; + } + + tlv->tlv_type = TLV_TYPE_ENCRYPTED_PAYLOAD; + memcpy(tlv->encr_tx_counter, encr_tx_counter, ENCRYPTION_TX_COUNTER_LEN); + maccpy(tlv->src_al_mac, src_mac); + maccpy(tlv->dst_al_mac, dest_mac); + tlv->siv_len = encrypted_payload_len; + tlv->siv_output = encrypted_payload; + + return tlv; +} + /*####################################################################### # GLOBAL FUNCTIONS # ########################################################################*/ +static int insert_mic_tlv(i1905_cmdu_t *cmdu, map_mic_tlv_t *mic_tlv, uint8_t *tlv_buf, uint16_t tlv_buf_len, + uint8_t *src_mac, map_1905_sec_key_info_t *key_info) +{ + mic_tlv->tlv_type = TLV_TYPE_MIC; + mic_tlv->gtk_key_id = 1; /* according to BCM implementation */ + mic_tlv->mic_version = 0; /* according to EM-R4 spec 17.2.68 */ + memcpy(mic_tlv->integrity_tx_counter, key_info->integrity_tx_counter, INTEGRITY_TX_COUNTER_LEN); + maccpy(mic_tlv->src_al_mac, src_mac); + mic_tlv->mic = calloc(1, SHA256_MAC_LEN); + if (!mic_tlv->mic) { + log_i1905_e("failed to allocate buffer for MIC"); + return -1; + } + + /* calculate MIC value for MIC TLV using HMAC-SHA256 */ + if (calculate_mic(tlv_buf, tlv_buf_len, key_info->gtk, key_info->gtk_len, mic_tlv, cmdu) != 0) { + log_i1905_e("failed to calculate MIC"); + free(mic_tlv->mic); + return -1; + } + + /* find number of TLVs in list_of_TLVs */ + int num_tlvs = i1905_count_tlvs_in_cmdu(cmdu); + if (num_tlvs <= 0) { + log_i1905_e("failed to count TLVs"); + free(mic_tlv->mic); + return -1; + } + + /* reallocate list_of_TLVs to insert MIC TLV */ + uint8_t **new_list_of_TLVs = realloc(cmdu->list_of_TLVs, sizeof(uint8_t *) * (num_tlvs + 1)); + if (!new_list_of_TLVs) { + log_i1905_e("failed to reallocate list of TLVs"); + free(mic_tlv->mic); + return -1; + } + + /* insert MIC TLV right before EOM TLV */ + cmdu->list_of_TLVs = new_list_of_TLVs; + cmdu->list_of_TLVs[num_tlvs - 1] = (uint8_t *)mic_tlv; + cmdu->list_of_TLVs[num_tlvs] = NULL; + + return 0; +} + uint8_t forward1905RawPacket(char *interface_name, uint16_t mid, uint8_t *dest_mac, i1905_cmdu_t *cmdu, int get_src_mac_frm_stream) { uint8_t **streams; uint16_t *streams_lens; uint8_t *src_mac; uint8_t total_streams, x; + map_1905_sec_key_info_t key_info; log_i1905_d("Contents of CMDU to send:0x%x", cmdu->message_type); @@ -341,6 +514,110 @@ uint8_t forward1905RawPacket(char *interface_name, uint16_t mid, uint8_t *dest_m return 0; } + do + { + bool is_multicast = is_multicast_ether_addr(dest_mac); + + /* do not encrypt specific 1905 cmdus */ + if (!pre_filter_encryption(cmdu->message_type)) { + log_i1905_d("this cmdu type should not be encrypted, skipping encryption... type: %08X", cmdu->message_type); + break; + } + + /** encrypt frames iff; + * key information exists for that ale + * the frame is not fragmented (for phase-1 as per our talk with Koen) + */ + memset(&key_info, 0, sizeof(key_info)); + + if (PLATFORM_OS_GET_KEY_INFO(dest_mac, &key_info)) { + break; + } + + uint8_t *tlv_buf = streams[0] + CMDU_HDR_SIZE; /* skip cmdu header */ + uint16_t tlv_buf_len = streams_lens[0] - CMDU_HDR_SIZE; /* skip cmdu header */ + i1905_cmdu_t new_cmdu = {0}; + + /* messages transmitted with CMDU unicast transmission procedures are protected by TLV encryption */ + if (!is_multicast && key_info.ptk_len > 0 && 1 == total_streams) { + map_encrypted_payload_tlv_t *encrypted_payload_tlv = NULL; + + encrypted_payload_tlv = encrypt1905RawPacket(tlv_buf, tlv_buf_len, src_mac, dest_mac, cmdu, key_info.encr_tx_counter, + key_info.ptk, key_info.ptk_len); + if (NULL == encrypted_payload_tlv) { + log_i1905_e("failed to encrypt 1905 raw packet"); + free_1905_CMDU_packets(streams); + free(streams_lens); + return 0; + } + + /* clean up previous streams */ + free_1905_CMDU_packets(streams); + free(streams_lens); + streams_lens = NULL; + + uint8_t *tlvs[2] = {0}; + + tlvs[0] = (uint8_t *)encrypted_payload_tlv; + new_cmdu.message_version = cmdu->message_version; + new_cmdu.message_type = cmdu->message_type; + new_cmdu.message_id = cmdu->message_id; + new_cmdu.relay_indicator = cmdu->relay_indicator; + new_cmdu.list_of_TLVs = tlvs; + map_strlcpy(new_cmdu.interface_name, cmdu->interface_name, sizeof(new_cmdu.interface_name)); + + /* forge new stream from the cmdu */ + streams = forge_1905_CMDU_from_structure(&new_cmdu, &streams_lens); + if (NULL == streams) { + /* Could not forge the packet. Error? */ + log_i1905_w("forge_1905_CMDU_from_structure() returned 0 streams!"); + free_1905_CMDU_packets(streams); + free(streams_lens); + free(encrypted_payload_tlv->siv_output); + free(encrypted_payload_tlv); + return 0; + } + total_streams = 1; + free(encrypted_payload_tlv->siv_output); + free(encrypted_payload_tlv); + increment_counter_48(key_info.encr_tx_counter); + } + /** Messages transmitted with: + * - CMDU neighbor multicast transmission procedures or + * - CMDU relayed multicast transmission procedures + * have the transmission of TLVs protected by a message integrity code (MIC) */ + else if (is_multicast && key_info.gtk_len > 0 && 1 == total_streams) { + map_mic_tlv_t mic_tlv = {0}; + + /* calculate MIC value and insert MIC TLV in to the cmdu->list_of_TLVs before forging new streams */ + if (insert_mic_tlv(cmdu, &mic_tlv, tlv_buf, tlv_buf_len, src_mac, &key_info)) { + log_i1905_e("failed to insert MIC TLV"); + free_1905_CMDU_packets(streams); + free(streams_lens); + return 0; + } + + /* clean up previous streams */ + free_1905_CMDU_packets(streams); + free(streams_lens); + streams_lens = NULL; + + /* forge new stream from the cmdu */ + streams = forge_1905_CMDU_from_structure(&new_cmdu, &streams_lens); + if (NULL == streams) { + /* Could not forge the packet. Error? */ + log_i1905_w("forge_1905_CMDU_from_structure() returned 0 streams!"); + free_1905_CMDU_packets(streams); + free(streams_lens); + free(mic_tlv.mic); + return 0; + } + total_streams = 1; + free(mic_tlv.mic); + increment_counter_48(key_info.integrity_tx_counter); + } + } while (0); + x = 0; while (streams[x]) { log_i1905_t("Sending 1905 message on interface %s, MID %d, fragment %d/%d", interface_name, mid, x + 1, total_streams); diff --git a/source/ieee1905/src/al/src_independent/al_utils.c b/source/ieee1905/src/al/src_independent/al_utils.c index 0bc5ab9..acdb78f 100644 --- a/source/ieee1905/src/al/src_independent/al_utils.c +++ b/source/ieee1905/src/al/src_independent/al_utils.c @@ -108,3 +108,40 @@ uint16_t getNextMid(void) return mid; } + +/** + * @brief increment_counter_48 increments a 48-bit counter + * @param array counter + * @return void + * @note counter is incremented in place +*/ +void increment_counter_48(uint8_t *array) { + int i = 5; /* 6 - 1 */ + while (i >= 0) { + if (array[i] == UINT8_MAX) { + array[i] = 0; + i--; + } else { + array[i]++; + break; + } + } +} + +/** + * @brief compare_counter_48 compares two 48-bit counters + * @param array1 first counter + * @param array2 second counter + * @return 1 if array1 > array2, -1 if array1 < array2, 0 if array1 == array2 + */ +int compare_counter_48(const uint8_t *array1, const uint8_t *array2) { + int i; + for (i = 0; i < 6; i++) { + if (array1[i] > array2[i]) { + return 1; + } else if (array1[i] < array2[i]) { + return -1; + } + } + return 0; +} diff --git a/source/ieee1905/src/al/src_independent/al_utils.h b/source/ieee1905/src/al/src_independent/al_utils.h index e5215a2..b997ef3 100644 --- a/source/ieee1905/src/al/src_independent/al_utils.h +++ b/source/ieee1905/src/al/src_independent/al_utils.h @@ -82,6 +82,22 @@ extern mac_addr g_mcast_mac_lldp; #define is_multicast_ether_addr(addr) \ memcmp(addr, g_mcast_mac_1905, ETHER_ADDR_LEN) == 0 +/** + * @brief increment_counter_48 increments a 48-bit counter + * @param array counter + * @return void + * @note counter is incremented in place +*/ +void increment_counter_48(uint8_t *array); + +/** + * @brief compare_counter_48 compares two 48-bit counters + * @param array1 first counter + * @param array2 second counter + * @return 1 if array1 > array2, -1 if array1 < array2, 0 if array1 == array2 + */ +int compare_counter_48(const uint8_t* array1, const uint8_t* array2); + // "MIDs" are "message IDs" used inside 1905 protocol messages. They must be // monotonically increased as explained in "Section 7.8" // diff --git a/source/ieee1905/src/al/src_independent/al_wsc.c b/source/ieee1905/src/al/src_independent/al_wsc.c index 05513df..8bab894 100644 --- a/source/ieee1905/src/al/src_independent/al_wsc.c +++ b/source/ieee1905/src/al/src_independent/al_wsc.c @@ -108,7 +108,9 @@ #define WPS_AUTH_WPA (0x0008) #define WPS_AUTH_WPA2 (0x0010) #define WPS_AUTH_WPA2PSK (0x0020) - #define WPS_AUTH_WEP (0x0040) + #define WPS_AUTH_SAE (0x0040) + #define WPS_AUTH_DPP (0x0080) + #define WPS_AUTH_SAE_24 (0x0100) /* SAE with AKM24 */ #define ATTR_ENCR_TYPE_FLAGS (0x1010) #define WPS_ENCR_NONE (0x0001) #define WPS_ENCR_WEP (0x0002) /* deprecated */ @@ -204,6 +206,7 @@ #define WPS_RF_24GHZ (0x01) #define WPS_RF_5GHZ (0x02) #define WPS_RF_60GHZ (0x04) + #define WPS_RF_6GHZ (0x08) #define ATTR_ASSOC_STATE (0x1002) #define WPS_ASSOC_NOT_ASSOC (0) #define WPS_ASSOC_CONN_SUCCESS (1) diff --git a/source/ieee1905/src/al/src_independent/extensions/map/i1905.c b/source/ieee1905/src/al/src_independent/extensions/map/i1905.c index 2d643dd..49d2491 100644 --- a/source/ieee1905/src/al/src_independent/extensions/map/i1905.c +++ b/source/ieee1905/src/al/src_independent/extensions/map/i1905.c @@ -44,9 +44,10 @@ /*####################################################################### # PUBLIC FUNCTIONS # ########################################################################*/ -int i1905_init(uint8_t *al_mac, i1905_interface_cb_t interface_cb, i1905_cmdu_cb_t cmdu_cb) +int i1905_init(uint8_t *al_mac, i1905_interface_cb_t interface_cb, + i1905_cmdu_cb_t cmdu_cb, i1905_key_info_cb_t key_info_cb) { - return start1905AL(al_mac, 0, NULL, interface_cb, cmdu_cb); + return start1905AL(al_mac, 0, NULL, interface_cb, cmdu_cb, key_info_cb); } void i1905_fini(void) @@ -74,6 +75,25 @@ void i1905_free_interface_info(i1905_interface_info_t *info) PLATFORM_FREE_1905_INTERFACE_INFO(info); } +int i1905_get_interface_mac(char *if_name, mac_addr mac) +{ + return PLATFORM_OS_GET_1905_INTERFACE_MAC(if_name, mac); +} + +void i1905_set_interface_type(char *if_name, uint16_t type) +{ + PLATFORM_OS_SET_1905_INTERFACE_TYPE(if_name, type); +} + +void i1905_set_interface_80211_media_specific_info(char *if_name, mac_addr network_membership, + uint8_t role, uint8_t ap_channel_band, + uint8_t ap_channel_center_freq_1, + uint8_t ap_channel_center_freq_2) +{ + PLATFORM_OS_SET_1905_INTERFACE_80211_MEDIA_SPECIFIC_INFO(if_name, network_membership, role, ap_channel_band, + ap_channel_center_freq_1, ap_channel_center_freq_2); +} + i1905_bridge_t *i1905_get_list_of_bridges(uint8_t *nr) { return PLATFORM_GET_LIST_OF_BRIDGES(nr); diff --git a/source/ieee1905/src/al/src_independent/extensions/map/i1905.h b/source/ieee1905/src/al/src_independent/extensions/map/i1905.h index 2ea3135..8e77eb0 100644 --- a/source/ieee1905/src/al/src_independent/extensions/map/i1905.h +++ b/source/ieee1905/src/al/src_independent/extensions/map/i1905.h @@ -112,11 +112,12 @@ typedef enum { * @param[in] al_mac : al mac address * @param[in] interface_cb: interface callback * @param[in] cmdu_cb : cmdu callback + * @param[in] key_info_cb : key information callback * * @retval 0 For successful get operation */ typedef bool (*i1905_cmdu_cb_t)(i1905_cmdu_t *cmdu); -int i1905_init(uint8_t *al_mac, i1905_interface_cb_t interface_cb, i1905_cmdu_cb_t cmdu_cb); +int i1905_init(uint8_t *al_mac, i1905_interface_cb_t interface_cb, i1905_cmdu_cb_t cmdu_cb, i1905_key_info_cb_t key_info_cb); /** @@ -166,6 +167,42 @@ i1905_interface_info_t *i1905_get_interface_info(char *ifname); void i1905_free_interface_info(i1905_interface_info_t *info); +/** + * Get interface mac + * + * @param[in] if_name: interface name + * @param[out] mac: mac address of interface + * + */ +int i1905_get_interface_mac(char *if_name, mac_addr mac); + + +/** + * Set interface type + * + * @param[in] if_name: interface name + * @param[in] type: interface type (INTERFACE_TYPE_IEEE_XXX) + * + */ +void i1905_set_interface_type(char *if_name, uint16_t type); + + +/** + * Set interface 802.11 media specific info + * + * @param[in] if_name: interface name + * @param[in] network_membership: network_membsership (bssid) + * @param[in] role: network_membsership (IEEE80211_ROLE_XXX) + * @param[in] ap_channel_band: frequency band (IEEE80211_AP_CHANNEL_BAND_XXX) + * @param[in] ap_channel_center_freq_1: center frequency + * @param[in] ap_channel_center_freq_2: center frequency in case of 80P80 + * + */ +void i1905_set_interface_80211_media_specific_info(char *if_name, mac_addr network_membership, + uint8_t role, uint8_t ap_channel_band, + uint8_t ap_channel_center_freq_1, + uint8_t ap_channel_center_freq_2); + /** * Get list of bridges * diff --git a/source/ieee1905/src/al/src_linux/platform_crypto.c b/source/ieee1905/src/al/src_linux/platform_crypto.c index ce8166c..34ddf55 100644 --- a/source/ieee1905/src/al/src_linux/platform_crypto.c +++ b/source/ieee1905/src/al/src_linux/platform_crypto.c @@ -68,6 +68,8 @@ #include #include #include +#include +#include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L #include @@ -106,6 +108,306 @@ static uint8_t g_dh1536_p[] = { static uint8_t g_dh1536_g[] = { 0x02 }; +/*####################################################################### +# PRIVATE FUNCTIONS # +########################################################################*/ + +static void dbl(uint8_t *pad) +{ + int i, carry; + + carry = pad[0] & 0x80; + for (i = 0; i < AES_BLOCK_SIZE - 1; i++) { + pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); + } + + pad[AES_BLOCK_SIZE - 1] <<= 1; + + if (carry) { + pad[AES_BLOCK_SIZE - 1] ^= 0x87; + } +} + + +static void xor(uint8_t *a, const uint8_t *b) +{ + int i; + + for (i = 0; i < AES_BLOCK_SIZE; i++) { + *a++ ^= *b++; + } +} + +static void xorend(uint8_t *a, int alen, const uint8_t *b, int blen) +{ + int i; + + if (alen < blen) { + return; + } + + for (i = 0; i < blen; i++) { + a[alen - blen + i] ^= b[i]; + } +} + +static void pad_block(uint8_t *pad, const uint8_t *addr, size_t len) +{ + memset(pad, 0, AES_BLOCK_SIZE); + memcpy(pad, addr, len); + + if (len < AES_BLOCK_SIZE) { + pad[len] = 0x80; + } +} + +static void bin_clear_free(void *bin, size_t len) +{ + if (bin) { + memset(bin, 0, len); + free(bin); + } +} + +static int omac1_aes_vector(const uint8_t *key, size_t key_len, size_t num_elem, + const uint8_t *addr[], size_t *len, uint8_t *out) +{ + int ret = -1; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MAC *mac = NULL; + EVP_MAC_CTX *ctx = NULL; + OSSL_PARAM params[2]; + char algo[32]; // MAX_ALGO_NAME +#else + CMAC_CTX *ctx = NULL; + const EVP_CIPHER *cipher; +#endif + size_t outlen = 0, i; + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + mac = EVP_MAC_fetch(NULL, OSSL_MAC_NAME_CMAC, NULL); + if (mac == NULL) { + goto fail; + } + + ctx = EVP_MAC_CTX_new(mac); +#else + ctx = CMAC_CTX_new(); +#endif + if (ctx == NULL) { + goto fail; + } + + if (key_len == 32) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + memcpy(algo, SN_aes_256_cbc, sizeof(SN_aes_256_cbc)); +#else + cipher = EVP_aes_256_cbc(); +#endif + } else if (key_len == 16) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + memcpy(algo, SN_aes_128_cbc, sizeof(SN_aes_128_cbc)); +#else + cipher = EVP_aes_128_cbc(); +#endif + } else { + goto fail; + } + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, algo, 0); + params[1] = OSSL_PARAM_construct_end(); + if (EVP_MAC_init(ctx, key, key_len, params) != 1) { + goto fail; + } +#else + if (CMAC_Init(ctx, key, key_len, cipher, NULL) != 1) { + goto fail; + } +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + for (i = 0; i < num_elem; i++) { + if (!EVP_MAC_update(ctx, addr[i], len[i])) { + goto fail; + } + } + if (!EVP_MAC_final(ctx, out, &outlen, AES_BLOCK_SIZE) || outlen != AES_BLOCK_SIZE) { + goto fail; + } +#else + for (i = 0; i < num_elem; i++) { + if (!CMAC_Update(ctx, addr[i], len[i])) + goto fail; + } + if (!CMAC_Final(ctx, out, &outlen) || outlen != AES_BLOCK_SIZE) + goto fail; +#endif + + ret = 0; + +fail: +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MAC_CTX_free(ctx); + EVP_MAC_free(mac); +#else + CMAC_CTX_free(ctx); +#endif + + return ret; +} + +static const EVP_CIPHER *aes_get_evp_cipher(size_t keylen) +{ + switch (keylen) { + case 16: + return EVP_aes_128_ecb(); + case 24: + return EVP_aes_192_ecb(); + case 32: + return EVP_aes_256_ecb(); + } + + return NULL; +} + +static void *memdup(const void *src, size_t len) +{ + void *r = calloc(1, len); + + if (r && src) { + memcpy(r, src, len); + } + return r; +} + +static int aes_s2v(const uint8_t *key, size_t key_len, size_t num_elem, const uint8_t *addr[], size_t *len, uint8_t *mac) +{ + int ret; + size_t i; + uint8_t tmp[AES_BLOCK_SIZE] = {0}; + uint8_t tmp2[AES_BLOCK_SIZE] = {0}; + uint8_t zero[AES_BLOCK_SIZE] = {0}; + uint8_t *buf = NULL; + const uint8_t *data[1] = {0}; + size_t data_len[1] = {0}; + + if (!num_elem) { + memcpy(tmp, zero, sizeof(zero)); + tmp[AES_BLOCK_SIZE - 1] = 1; + data[0] = tmp; + data_len[0] = sizeof(tmp); + return omac1_aes_vector(key, key_len, 1, data, data_len, mac); + } + + data[0] = zero; + data_len[0] = sizeof(zero); + ret = omac1_aes_vector(key, key_len, 1, data, data_len, tmp); + if (ret) { + return ret; + } + + for (i = 0; i < num_elem - 1; i++) { + ret = omac1_aes_vector(key, key_len, 1, &addr[i], &len[i], tmp2); + if (ret) { + return ret; + } + + dbl(tmp); + xor(tmp, tmp2); + } + if (len[i] >= AES_BLOCK_SIZE) { + buf = memdup(addr[i], len[i]); + if (!buf) { + return -ENOMEM; + } + + xorend(buf, len[i], tmp, AES_BLOCK_SIZE); + data[0] = buf; + ret = omac1_aes_vector(key, key_len, 1, data, &len[i], mac); + bin_clear_free(buf, len[i]); + return ret; + } + + dbl(tmp); + pad_block(tmp2, addr[i], len[i]); + xor(tmp, tmp2); + + data[0] = tmp; + data_len[0] = sizeof(tmp); + return omac1_aes_vector(key, key_len, 1, data, data_len, mac); +} + +static int aes_ctr_encrypt(const uint8_t *key, size_t key_len, const uint8_t *nonce, uint8_t *data, size_t data_len) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + EVP_CIPHER_CTX _ctx; +#endif + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *type; + + size_t left = data_len; + int j, len, clen; + int i; + uint8_t *pos = data; + uint8_t counter[AES_BLOCK_SIZE] = {0}; + uint8_t buf[AES_BLOCK_SIZE] = {0}; + + type = aes_get_evp_cipher(key_len); + if (!type) { + return 0; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + EVP_CIPHER_CTX_init(&_ctx); + ctx = &_ctx; +#else + ctx = EVP_CIPHER_CTX_new(); + if (!ctx) { + return 0; + } +#endif + if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + return 0; + } + + EVP_CIPHER_CTX_set_padding(ctx, 0); + + memcpy(counter, nonce, AES_BLOCK_SIZE); + + clen = 16; + while (left > 0) { + if (EVP_EncryptUpdate(ctx, buf, &clen, counter, 16) != 1) { + return 0; + } + + len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; + for (j = 0; j < len; j++) + pos[j] ^= buf[j]; + pos += len; + left -= len; + + for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { + counter[i]++; + if (counter[i]) + break; + } + } + + len = sizeof(buf); + if (EVP_EncryptFinal_ex(ctx, buf, &len) != 1 || len != 0) { + return 0; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + EVP_CIPHER_CTX_cleanup(ctx); +#else + EVP_CIPHER_CTX_free(ctx); +#endif + + return 1; +} + /*####################################################################### # PUBLIC FUNCTIONS # ########################################################################*/ @@ -741,3 +1043,97 @@ uint8_t PLATFORM_AES_DECRYPT(uint8_t *key, uint8_t *iv, uint8_t *data, uint32_t return 1; } + +uint8_t PLATFORM_AES_SIV_ENCRYPT(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len, + size_t num_params, uint8_t *params[], size_t *params_lens, uint8_t *out) +{ + size_t i; + size_t _params_lens[6] = {0}; + const uint8_t *_params[6] = {0}; + const uint8_t *k1 = NULL; + const uint8_t *k2 = NULL; + uint8_t v[AES_BLOCK_SIZE] = {0}; + uint8_t *iv = NULL; + uint8_t *crypt_data = NULL; + + if (num_params > ARRAY_SIZE(_params) - 1 || (key_len != 32 && key_len != 48 && key_len != 64)) { + return 0; + } + + key_len /= 2; + k1 = key; + k2 = key + key_len; + + for (i = 0; i < num_params; i++) { + _params[i] = params[i]; + _params_lens[i] = params_lens[i]; + } + _params[num_params] = data; + _params_lens[num_params] = data_len; + + if (aes_s2v(k1, key_len, num_params + 1, _params, _params_lens, v)) { + return 0; + } + + iv = out; + crypt_data = out + AES_BLOCK_SIZE; + + memcpy(iv, v, AES_BLOCK_SIZE); + memcpy(crypt_data, data, data_len); + + /* zero out 63rd and 31st bits of ctr (from right) */ + v[8] &= 0x7f; + v[12] &= 0x7f; + + return aes_ctr_encrypt(k2, key_len, v, crypt_data, data_len); +} + +uint8_t PLATFORM_AES_SIV_DECRYPT(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len, + size_t num_params, uint8_t *params[], size_t *params_lens, uint8_t *out) +{ + const uint8_t *_params[6] = {0}; + uint8_t iv[AES_BLOCK_SIZE] = {0}; + uint8_t check[AES_BLOCK_SIZE] = {0}; + size_t _params_lens[6] = {0}; + const uint8_t *k1 = NULL; + const uint8_t *k2 = NULL; + size_t i = 0, crypt_len = 0; + + if (data_len < AES_BLOCK_SIZE || + num_params > ARRAY_SIZE(_params) - 1 || + (key_len != 32 && key_len != 48 && key_len != 64)) { + return 0; + } + + crypt_len = data_len - AES_BLOCK_SIZE; + key_len /= 2; + k1 = key; + k2 = key + key_len; + + for (i = 0; i < num_params; i++) { + _params[i] = params[i]; + _params_lens[i] = params_lens[i]; + } + _params[num_params] = out; + _params_lens[num_params] = crypt_len; + + memcpy(iv, data, AES_BLOCK_SIZE); + memcpy(out, data + AES_BLOCK_SIZE, crypt_len); + + iv[8] &= 0x7f; + iv[12] &= 0x7f; + + if (!aes_ctr_encrypt(k2, key_len, iv, out, crypt_len)) { + return 0; + } + + if (aes_s2v(k1, key_len, num_params + 1, _params, _params_lens, check)) { + return 0; + } + + if (memcmp(check, data, AES_BLOCK_SIZE) != 0) { + return 0; + } + + return 1; +} diff --git a/source/ieee1905/src/al/src_linux/platform_interfaces.c b/source/ieee1905/src/al/src_linux/platform_interfaces.c index 5bb9bf5..f6004b6 100644 --- a/source/ieee1905/src/al/src_linux/platform_interfaces.c +++ b/source/ieee1905/src/al/src_linux/platform_interfaces.c @@ -378,7 +378,8 @@ i1905_interface_info_t *PLATFORM_GET_1905_INTERFACE_INFO(char *interface_name) INTERFACE_TYPE_IEEE_802_11AC_5_GHZ == m->interface_type || INTERFACE_TYPE_IEEE_802_11AD_60_GHZ == m->interface_type || INTERFACE_TYPE_IEEE_802_11AF == m->interface_type || - INTERFACE_TYPE_IEEE_802_11AX == m->interface_type) + INTERFACE_TYPE_IEEE_802_11AX == m->interface_type || + INTERFACE_TYPE_IEEE_802_11BE == m->interface_type) { log_i1905_t(" ieee80211 data"); log_i1905_t(" bssid : %02x:%02x:%02x:%02x:%02x:%02x", m->interface_type_data.ieee80211.bssid[0], m->interface_type_data.ieee80211.bssid[1], m->interface_type_data.ieee80211.bssid[2], m->interface_type_data.ieee80211.bssid[3], m->interface_type_data.ieee80211.bssid[4], m->interface_type_data.ieee80211.bssid[5]); diff --git a/source/ieee1905/src/al/src_linux/platform_os.c b/source/ieee1905/src/al/src_linux/platform_os.c index 73ade57..1b6390c 100644 --- a/source/ieee1905/src/al/src_linux/platform_os.c +++ b/source/ieee1905/src/al/src_linux/platform_os.c @@ -79,12 +79,13 @@ #include #include #include -#include +#include +#include #include #include -#include #include #include +#include #include #include #include @@ -105,24 +106,19 @@ /*####################################################################### # DEFINES # ########################################################################*/ +/* SOL_NETLINK is not defined on older toolchains - see libmnl.h */ +#ifndef SOL_NETLINK +#pragma message "SOL_NETLINK NOT DEFINED" +#define SOL_NETLINK 270 +#endif + +#define NETLINK_RCVBUF_SIZE (1024 * 1024) + #define PACKET_SOCKET_TYPE_1905 1 #define PACKET_SOCKET_TYPE_LLDP 2 #define TS_ENABLED_VLAN_PATTERN() (TS_ENABLED() && (strlen(map_cfg_get()->primary_vlan_pattern)>0)) -#define xstr(s) str(s) -#define str(s) #s - -#define ARP_CACHE "/proc/net/arp" -#define ARP_STRING_LEN 1023 -#define ARP_BUFFER_LEN (ARP_STRING_LEN + 1) - -#define ARP_LINE_FORMAT "%" xstr(ARP_STRING_LEN) "s %*s %*s " \ - "%" xstr(ARP_STRING_LEN) "s %*s " \ - "%" xstr(ARP_STRING_LEN) "s" - -#define ZERO_MAC "00:00:00:00:00:00" - /*####################################################################### # TYPEDEFS # ########################################################################*/ @@ -146,10 +142,15 @@ typedef struct interface_s { i1905_interface_info_t if_info; } interface_t; -typedef struct netlink_req_s { - struct nlmsghdr hdr; - struct rtgenmsg gen; -} netlink_req_t; +typedef struct gateway_info_s { + mac_addr mac; + char ip[128]; + char ifname[IFNAMSIZ]; /* Real(physical) interface that connected to gateway */ + char sock_ifname[IFNAMSIZ]; /* It will be used to bind sockets. + * It doesn't point real(physical) interface if there is a bridge on the system. + */ + int ifa_family; /* Address family. AF_INET, AF_INET6, .. */ +} gateway_info_t; /*####################################################################### # GLOBALS # @@ -164,12 +165,10 @@ static int g_send_ioctl_fd = -1; static int g_netlink_fd = -1; static acu_evloop_fd_t *g_netlink_evloop_fd; -static acu_evloop_timer_t *g_rt_query_timer = NULL; - -/* Gateway mac. a.k.a DHCP server mac address*/ -static mac_addr g_gateway_mac = {0}; +static gateway_info_t g_gateway; static i1905_interface_cb_t g_interface_cb; +static i1905_key_info_cb_t g_key_info_cb; /*####################################################################### # SOCKET LIST # @@ -317,7 +316,7 @@ static int packet_socket_create(const char *if_name, int if_index, int type) mr.mr_alen = ETH_ALEN; memcpy(mr.mr_address, mcast_mac, ETH_ALEN); if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) { - log_i1905_e("setsockopt failed (%s)", strerror(errno)); + log_i1905_e("setsockopt PACKET_ADD_MEMBERSHIP failed (%s)", strerror(errno)); break; } @@ -676,109 +675,7 @@ static void interfaces_fini(void) close(g_send_ioctl_fd); } } -/*####################################################################### -# ROUTE # -########################################################################*/ -static int route_get_arp_entry (char *ip, size_t ip_len, char *mac, int mac_len) -{ - FILE *arp_table; - - char header[ARP_BUFFER_LEN]; - char ip_addr[ARP_BUFFER_LEN]; - char hw_addr[ARP_BUFFER_LEN]; - char device[ARP_BUFFER_LEN]; - - arp_table = NULL; - if (ip == NULL) { - goto bail; - } - if (mac == NULL) { - goto bail; - } - - arp_table = fopen(ARP_CACHE, "r"); - if (arp_table == NULL) { - goto bail; - } - - if (!fgets(header, sizeof(header), arp_table)) { - goto bail; - } - - while (fscanf(arp_table, ARP_LINE_FORMAT, ip_addr, hw_addr, device) == 3) { - if (strcasecmp(ip_addr, ip) == 0 && strlen(ip_addr) == ip_len) { - if (strncasecmp(hw_addr, ZERO_MAC, mac_len) != 0) { - int ret = snprintf(mac, mac_len, "%s", hw_addr); - if (ret >= mac_len) { - log_i1905_e("mac too long [%d]", ret); - goto bail; - } - fclose(arp_table); - return 0; - } - } - } - -bail: - if (arp_table != NULL) { - fclose(arp_table); - } - return -1; -} - -static void periodic_nl_route_query_cb(void *userdata) -{ - (void) userdata; - - struct sockaddr_nl kernel; - struct msghdr rtnl_msg; - struct iovec io; - netlink_req_t req; - - memset(&rtnl_msg, 0, sizeof(rtnl_msg)); - memset(&kernel, 0, sizeof(kernel)); - memset(&req, 0, sizeof(req)); - - kernel.nl_family = AF_NETLINK; - kernel.nl_groups = 0; - - req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); - req.hdr.nlmsg_type = RTM_GETROUTE; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = 1; - req.hdr.nlmsg_pid = getpid(); - req.gen.rtgen_family = AF_INET; - - io.iov_base = &req; - io.iov_len = req.hdr.nlmsg_len; - rtnl_msg.msg_iov = &io; - rtnl_msg.msg_iovlen = 1; - rtnl_msg.msg_name = &kernel; - rtnl_msg.msg_namelen = sizeof(kernel); - - if (sendmsg(g_netlink_fd, (struct msghdr *) &rtnl_msg, 0) < 0) { - log_i1905_e("sendmsg() failed:"); - } - -} - -static int route_init() -{ - g_rt_query_timer = acu_evloop_timer_add(0, 10000, periodic_nl_route_query_cb, NULL); - if (NULL == g_rt_query_timer) { - return -1; - } - - return 0; -} - -static void route_fini() -{ - if (g_rt_query_timer) { - acu_evloop_timer_delete(g_rt_query_timer); - } -} /*####################################################################### # NETLINK # ########################################################################*/ @@ -878,40 +775,6 @@ static void netlink_event_dellink(struct nlmsghdr *h, size_t len) interface_removed_event(if_name); } -static void netlink_event_newroute(struct nlmsghdr *h, size_t len) -{ - int rc = 0; - struct rtmsg *rtmsg; - struct rtattr *rtattr[__RTA_MAX]; - char gw_ip[32] = {0}; - char gw_mac_str[32]; - - if (len < sizeof(*rtmsg)) { - return; - } - - if (NULL == (rtmsg = NLMSG_DATA(h))) { - log_i1905_e("netlink: header contains no data"); - return; - } - - parse_rtattr_flags(rtattr, RTA_MAX, RTM_RTA(rtmsg), RTM_PAYLOAD(h), 0); - - if (rtattr[RTA_GATEWAY]) { - inet_ntop(AF_INET, RTA_DATA(rtattr[RTA_GATEWAY]), gw_ip, sizeof(gw_ip)); - if(strlen(gw_ip) > 0) { - rc = route_get_arp_entry(gw_ip, strlen(gw_ip), gw_mac_str, sizeof(gw_mac_str)); - if (!rc) { - log_i1905_d("gateway: default ip: %s mac: %s", gw_ip, gw_mac_str); - mac_from_string(gw_mac_str, g_gateway_mac); - } else { - log_i1905_e("can not get mac entry for %s", gw_ip); - } - } - } - -} - static void netlink_socket_cb(int fd, UNUSED void *userdata) { char buf[8192]; @@ -921,7 +784,7 @@ static void netlink_socket_cb(int fd, UNUSED void *userdata) left = recv(fd, buf, sizeof(buf), MSG_DONTWAIT); if (left < 0) { if (errno != EINTR && errno != EAGAIN) { - log_i1905_e("netlink: recv failed"); + log_i1905_e("netlink: recv failed[%d - %s]", errno, strerror(errno)); } return; } @@ -945,8 +808,6 @@ static void netlink_socket_cb(int fd, UNUSED void *userdata) case RTM_DELLINK: netlink_event_dellink(h, plen); break; - case RTM_NEWROUTE: - netlink_event_newroute(h, plen); break; default: break; @@ -966,6 +827,8 @@ static int netlink_init(void) { struct sockaddr_nl local; int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + int val; + if (fd < 0) { log_i1905_e("could not create netlink socket"); return -1; @@ -980,9 +843,33 @@ static int netlink_init(void) return -1; } + /* Force receive buffer size to a big value and disable reporting of ENOBUFS. + NOTE: + - To be completely correct: when ENOBUFS is received, we should: + - close and reopen netlink socket + - read all interfaces to check for missed events + - By setting NETLINK_NO_ENOBUFS event reception keeps working + but we don't know if an event was missed + */ + + val = NETLINK_RCVBUF_SIZE; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &val, sizeof(val)) < 0) { + log_i1905_e("could not set netlink receive buffer size (%s)", strerror(errno)); + close(fd); + return -1; + } + + val = 1; + if (setsockopt(fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &val, sizeof(val)) < 0) { + log_i1905_e("could not set NETLINK_NO_ENOBUFS"); + close(fd); + return -1; + } + g_netlink_evloop_fd = acu_evloop_fd_add(fd, netlink_socket_cb, NULL); if (NULL == g_netlink_evloop_fd) { log_i1905_e("failed register netlink socket"); + close(fd); return -1; } @@ -1014,9 +901,10 @@ void PLATFORM_OS_DUMP_INTERFACES(map_printf_cb_t print_cb) print_cb("Interfaces:\n"); list_for_each_entry(interface, &g_interface_list, list) { acu_mac_to_string(interface->if_info.mac_address, mac_str); - print_cb(" ifname[%s] ifidx[%d] mac[%s] state[%d] vlan[%d]\n", + print_cb(" ifname[%s] ifidx[%d] mac[%s] state[%d] type[%s] vlan[%d]\n", interface->if_name, interface->if_info.interface_index, mac_str, interface->if_info.power_state == INTERFACE_POWER_STATE_ON ? 1 : 0, + convert_interface_type_to_string(interface->if_info.interface_type), interface->is_vlan ? 1 : 0); } @@ -1083,6 +971,46 @@ void PLATFORM_OS_GET_1905_INTERFACE_INFO(char *if_name, i1905_interface_info_t * } } +int PLATFORM_OS_GET_1905_INTERFACE_MAC(char *if_name, mac_addr mac) +{ + interface_t *interface = interface_get(if_name); + + if (interface) { + maccpy(mac, interface->if_info.mac_address); + } + + return interface ? 0 : -1; +} + +void PLATFORM_OS_SET_1905_INTERFACE_TYPE(char *if_name, uint16_t type) +{ + interface_t *interface = interface_get(if_name); + + if (interface) { + interface->if_info.interface_type = type; + } +} + +void PLATFORM_OS_SET_1905_INTERFACE_80211_MEDIA_SPECIFIC_INFO(char *if_name, mac_addr network_membership, + uint8_t role, uint8_t ap_channel_band, + uint8_t ap_channel_center_freq_1, + uint8_t ap_channel_center_freq_2) +{ + interface_t *interface = interface_get(if_name); + + if (interface) { + i1905_interface_info_t *if_info = &interface->if_info; + + if (INTERFACE_TYPE_GROUP_GET(if_info->interface_type) == INTERFACE_TYPE_GROUP_WLAN) { + maccpy(if_info->interface_type_data.ieee80211.bssid, network_membership); + if_info->interface_type_data.ieee80211.role = role; + if_info->interface_type_data.ieee80211.ap_channel_band = ap_channel_band; + if_info->interface_type_data.ieee80211.ap_channel_center_frequency_index_1 = ap_channel_center_freq_1; + if_info->interface_type_data.ieee80211.ap_channel_center_frequency_index_2 = ap_channel_center_freq_2; + } + } +} + bool PLATFORM_OS_IS_INTERFACE_UP(char *if_name) { interface_t *interface = interface_get(if_name); @@ -1126,13 +1054,14 @@ bool PLATFORM_OS_LOG_LEVEL_TRACE(void) mac_addr* PLATFORM_OS_GET_GATEWAY_MAC(void) { - return &g_gateway_mac; + return &g_gateway.mac; } -uint8_t PLATFORM_OS_INIT(i1905_interface_cb_t interface_cb, i1905_packet_cb_t packet_cb) +uint8_t PLATFORM_OS_INIT(i1905_interface_cb_t interface_cb, i1905_packet_cb_t packet_cb, i1905_key_info_cb_t key_info_cb) { g_interface_cb = interface_cb; g_packet_cb = packet_cb; + g_key_info_cb = key_info_cb; if (netlink_init()) { return 0; @@ -1142,10 +1071,6 @@ uint8_t PLATFORM_OS_INIT(i1905_interface_cb_t interface_cb, i1905_packet_cb_t pa return 0; } - if (route_init()) { - return 0; - } - return 1; } @@ -1153,5 +1078,14 @@ void PLATFORM_OS_FINI(void) { netlink_fini(); interfaces_fini(); - route_fini(); } + +int PLATFORM_OS_GET_KEY_INFO(uint8_t *al_mac, map_1905_sec_key_info_t *key_info) +{ + if (g_key_info_cb) { + return g_key_info_cb(al_mac, key_info); + } + + return -1; +} + diff --git a/source/ieee1905/src/common/interfaces/platform.h b/source/ieee1905/src/common/interfaces/platform.h index d9639a4..fb4eed3 100644 --- a/source/ieee1905/src/common/interfaces/platform.h +++ b/source/ieee1905/src/common/interfaces/platform.h @@ -94,6 +94,7 @@ #define MAX_CMDU_SIZE (MAX_NETWORK_SEGMENT_SIZE - ETH_8021Q_HDR_SIZE) #define MAX_TLV_SIZE (MAX_CMDU_SIZE - CMDU_HDR_SIZE) +#define MAX_LAST_TLV_SIZE (MAX_TLV_SIZE - 3) #define MAX_TLV_PAYLOAD_SIZE (MAX_TLV_SIZE - TLV_HDR_SIZE) /*####################################################################### diff --git a/source/ieee1905/src/factory/CMakeLists.txt b/source/ieee1905/src/factory/CMakeLists.txt index b509b0f..b74a826 100644 --- a/source/ieee1905/src/factory/CMakeLists.txt +++ b/source/ieee1905/src/factory/CMakeLists.txt @@ -21,6 +21,8 @@ set(FACTORY_INDEPENDENT_SOURCES src_independent/extensions/map/map_r1_tlvs.c src_independent/extensions/map/map_r2_tlvs.c src_independent/extensions/map/map_r3_tlvs.c + src_independent/extensions/map/map_r4_tlvs.c + src_independent/extensions/map/map_r6_tlvs.c ) set(FACTORY_INDEPENDENT_INCLUDES diff --git a/source/ieee1905/src/factory/Makefile.am b/source/ieee1905/src/factory/Makefile.am index 1bb19cd..b685046 100644 --- a/source/ieee1905/src/factory/Makefile.am +++ b/source/ieee1905/src/factory/Makefile.am @@ -26,5 +26,6 @@ libfactory_a_SOURCES = $(srcdir)/src_independent/1905_cmdus.c \ $(srcdir)/src_independent/media_specific_blobs.c \ $(srcdir)/src_independent/extensions/map/map_r1_tlvs.c \ $(srcdir)/src_independent/extensions/map/map_r2_tlvs.c \ - $(srcdir)/src_independent/extensions/map/map_r3_tlvs.c - + $(srcdir)/src_independent/extensions/map/map_r3_tlvs.c \ + $(srcdir)/src_independent/extensions/map/map_r4_tlvs.c \ + $(srcdir)/src_independent/extensions/map/map_r6_tlvs.c diff --git a/source/ieee1905/src/factory/interfaces/1905_cmdus.h b/source/ieee1905/src/factory/interfaces/1905_cmdus.h index 07735aa..78d0e60 100644 --- a/source/ieee1905/src/factory/interfaces/1905_cmdus.h +++ b/source/ieee1905/src/factory/interfaces/1905_cmdus.h @@ -153,8 +153,16 @@ #define CMDU_TYPE_MAP_DPP_CCE_INDICATION 0x801d #define CMDU_TYPE_MAP_PROXIED_ENCAP_DPP 0x8029 #define CMDU_TYPE_MAP_DIRECT_ENCAP_DPP 0x802a +#define CMDU_TYPE_MAP_BSS_CONFIGURATION_REQUEST 0x802c +#define CMDU_TYPE_MAP_BSS_CONFIGURATION_RESPONSE 0x802d +#define CMDU_TYPE_MAP_BSS_CONFIGURATION_RESULT 0x802e #define CMDU_TYPE_MAP_CHIRP_NOTIFICATION 0x802f #define CMDU_TYPE_MAP_1905_ENCAP_EAPOL 0x8030 +#define CMDU_TYPE_MAP_AGENT_LIST 0x8035 + +/* MAP R6 */ +#define CMDU_TYPE_MAP_EARLY_AP_CAPABILITY_REPORT 0x8043 +#define CMDU_TYPE_MAP_AVAILABLE_SPECTRUM_INQUIRY 0x8049 /*####################################################################### # CMDU message version # @@ -219,6 +227,17 @@ static inline void *i1905_get_tlv_from_cmdu(uint8_t tlv_type, i1905_cmdu_t *cmdu return NULL; } +static inline int i1905_count_tlvs_in_cmdu(i1905_cmdu_t *cmdu) +{ + int idx = 0; + + for (idx = 0; cmdu->list_of_TLVs[idx]; idx++) { + /* do nothing */ + } + + return idx; +} + /*####################################################################### # Main API functions # ########################################################################*/ diff --git a/source/ieee1905/src/factory/interfaces/1905_tlvs.h b/source/ieee1905/src/factory/interfaces/1905_tlvs.h index 6f087fb..6120213 100644 --- a/source/ieee1905/src/factory/interfaces/1905_tlvs.h +++ b/source/ieee1905/src/factory/interfaces/1905_tlvs.h @@ -140,6 +140,7 @@ #define MEDIA_TYPE_IEEE_802_11AD_60_GHZ (0x0106) #define MEDIA_TYPE_IEEE_802_11AF (0x0107) #define MEDIA_TYPE_IEEE_802_11AX (0x0108) /* MAP R2 extension. One value all bands */ +#define MEDIA_TYPE_IEEE_802_11BE (0x0109) /* MAP R6 extension. One value all bands */ #define MEDIA_TYPE_IEEE_1901_WAVELET (0x0200) #define MEDIA_TYPE_IEEE_1901_FFT (0x0201) #define MEDIA_TYPE_MOCA_V1_1 (0x0300) @@ -153,12 +154,12 @@ /*####################################################################### # IEEE802.11 frequency bands used in "Tables 6-23 and 6-25" # ########################################################################*/ -/* TODO: also defined in platform_map.h */ +/* TODO: also defined in map_common_defines.h */ #ifndef IEEE80211_FREQUENCY_BAND_2_4_GHZ #define IEEE80211_FREQUENCY_BAND_2_4_GHZ (0x00) #define IEEE80211_FREQUENCY_BAND_5_GHZ (0x01) #define IEEE80211_FREQUENCY_BAND_60_GHZ (0x02) - #define IEEE80211_FREQUENCY_BAND_6_GHZ (0x04) + #define IEEE80211_FREQUENCY_BAND_6_GHZ (0x03) #define IEEE80211_FREQUENCY_BAND_UNKNOWN (0xFF) #endif @@ -579,6 +580,7 @@ typedef struct supportedFreqBandTLV { # WSC TLV associated structures ("Section 6.4.18") # ########################################################################*/ /* Attributes used outside al_wsc.c */ +#define WSC_ATTR_AUTH_TYPE_FLAGS (0x1004) #define WSC_ATTR_MAC_ADDR (0x1020) #define WSC_ATTR_MANUFACTURER (0x1021) #define WSC_ATTR_MODEL_NAME (0x1023) @@ -1067,6 +1069,13 @@ do { \ } +#define FORGE_RESERVE(p, n) \ +do { \ + memset(p, 0, n); \ + p += n; \ +} while(0); + + #define FORGE_RETURN \ *len = TLV_HDR_SIZE + tlv_length; \ return ret; @@ -1121,6 +1130,9 @@ uint8_t *forge_1905_TLV_from_structure(uint8_t *memory_structure, uint16_t *len) */ void free_1905_TLV_structure(uint8_t *memory_structure); +/* Same as above but do not free memory_structure itself (e.g could be on stack) */ +void free_1905_TLV_structure2(uint8_t *memory_structure); + /* 'forge_1905_TLV_from_structure()' returns a regular buffer which can be freed * using this macro defined to be free */ diff --git a/source/ieee1905/src/factory/interfaces/extensions/map/map_emex_tlvs.h b/source/ieee1905/src/factory/interfaces/extensions/map/map_emex_tlvs.h index 9f1abd0..78b86ee 100644 --- a/source/ieee1905/src/factory/interfaces/extensions/map/map_emex_tlvs.h +++ b/source/ieee1905/src/factory/interfaces/extensions/map/map_emex_tlvs.h @@ -15,7 +15,11 @@ /* AirTies EM+ Extension TLV IDs */ enum emex_tlv_type { EMEX_TLV_UNUSED = 0x0000, + EMEX_TLV_MESSAGE_TYPE = 0x0001, + EMEX_TLV_FEATURE_PROFILE = 0x0002, + EMEX_TLV_DEVICE_INFO = 0x0003, EMEX_TLV_DEVICE_METRICS = 0x0004, + EMEX_TLV_REBOOT_REQUEST = 0x0005, EMEX_TLV_ETH_STATS = 0x000A, EMEX_TLV_ETH_INTERFACES = 0x000F, EMEX_TLV_ETH_STATS_V2 = 0x0010, @@ -23,6 +27,11 @@ enum emex_tlv_type { EMEX_TLV_TYPE_ETH_1905_NEIGHBOR_DEVICES = 0x0012, }; +enum emex_message_type { + EMEX_MESSAGE_UNUSED = 0x0000, + EMEX_MESSAGE_REBOOT_REQUEST = 0x0001, +}; + /* Ethernet interfaces TLV */ #define EMEX_ETH_LINK_TYPE_UNDEFINED 0 #define EMEX_ETH_LINK_TYPE_10MBPS 1 diff --git a/source/ieee1905/src/factory/interfaces/extensions/map/map_tlvs.h b/source/ieee1905/src/factory/interfaces/extensions/map/map_tlvs.h index 2f7dea6..cf81530 100644 --- a/source/ieee1905/src/factory/interfaces/extensions/map/map_tlvs.h +++ b/source/ieee1905/src/factory/interfaces/extensions/map/map_tlvs.h @@ -156,16 +156,42 @@ /*####################################################################### # MAP R3 TLV types # ########################################################################*/ +#define TLV_TYPE_1905_LAYER_SECURITY_CAPABILITY 0xA9 #define TLV_TYPE_AP_WIFI6_CAPABILITIES 0xAA -#define TLV_TYPE_ASSOCIATED_WIFI6_STA_STATUS_REPORT 0xB0 +#define TLV_TYPE_MIC 0xAB #define TLV_TYPE_ENCRYPTED_PAYLOAD 0xAC +#define TLV_TYPE_ASSOCIATED_WIFI6_STA_STATUS_REPORT 0xB0 +#define TLV_TYPE_BSS_CONFIGURATION_REPORT 0xB7 #define TLV_TYPE_BSSID 0xB8 -#define TLV_TYPE_DPP_CCE_INDICATION 0xD2 +#define TLV_TYPE_BSS_CONFIGURATION_REQUEST 0xBB +#define TLV_TYPE_AKM_SUITE_CAPABILITIES 0xCC #define TLV_TYPE_1905_ENCAP_DPP 0xCD #define TLV_TYPE_1905_ENCAP_EAPOL 0xCE #define TLV_TYPE_DPP_MESSAGE 0xD1 +#define TLV_TYPE_DPP_CCE_INDICATION 0xD2 #define TLV_TYPE_DPP_CHIRP_VALUE 0xD3 #define TLV_TYPE_DEVICE_INVENTORY 0xD4 +#define TLV_TYPE_AGENT_LIST 0xD5 + +/*####################################################################### +# MAP R4 TLV types # +########################################################################*/ +#define TLV_TYPE_CONTROLLER_CAPABILITY 0xDD + +/*####################################################################### +# MAP R6 TLV types # +########################################################################*/ +#define TLV_TYPE_WIFI7_AGENT_CAPABILITIES 0xDF +#define TLV_TYPE_AGENT_AP_MLD_CONFIGURATION 0xE0 +#define TLV_TYPE_BACKHAUL_STA_MLD_CONFIGURATION 0xE1 +#define TLV_TYPE_ASSOCIATED_STA_MLD_CONFIGURATION 0xE2 +#define TLV_TYPE_MLD_STRUCTURE 0xE3 +#define TLV_TYPE_AFFILIATED_STA_METRICS 0xE4 +#define TLV_TYPE_AFFILIATED_AP_METRICS 0xE5 +#define TLV_TYPE_TID_TO_LINK_MAPPING_POLICY 0xE6 +#define TLV_TYPE_EHT_OPERATIONS 0xE7 +#define TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_REQUEST 0xE8 +#define TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_RESPONSE 0xE9 /*####################################################################### # Types used in other TLV # @@ -182,7 +208,7 @@ typedef struct { #define MAP_SERVICE_AGENT 0x01 #define MAP_SERVICE_EMEX_CONTROLLER 0xA0 #define MAP_SERVICE_EMEX_AGENT 0xA1 -#define MAX_SERVICE 8 +#define MAX_SERVICE 8 typedef struct mapSupportedServiceTLV { uint8_t tlv_type; @@ -269,11 +295,11 @@ typedef struct { } map_ap_radio_basic_cap_tlv_op_class_t; typedef struct mapApRadioBasicCapabilitiesTLV { - uint8_t tlv_type; - mac_addr radio_id; - uint8_t max_bss; - uint8_t op_classes_nr; - map_ap_radio_basic_cap_tlv_op_class_t op_classes[MAX_OP_CLASS]; + uint8_t tlv_type; + mac_addr radio_id; + uint8_t max_bss; + uint8_t op_classes_nr; + map_ap_radio_basic_cap_tlv_op_class_t *op_classes; } map_ap_radio_basic_cap_tlv_t; /*####################################################################### @@ -403,10 +429,10 @@ typedef struct { } map_channel_preference_tlv_op_class_t; typedef struct mapChannelPreferenceTLV { - uint8_t tlv_type; - mac_addr radio_id; - uint8_t op_classes_nr; - map_channel_preference_tlv_op_class_t op_classes[MAX_OP_CLASS]; + uint8_t tlv_type; + mac_addr radio_id; + uint8_t op_classes_nr; + map_channel_preference_tlv_op_class_t *op_classes; } map_channel_preference_tlv_t; /*########################################################################### @@ -418,16 +444,16 @@ typedef struct { } map_radio_operation_restriction_tlv_channel_t; typedef struct { - uint8_t op_class; - uint8_t channels_nr; - map_radio_operation_restriction_tlv_channel_t channels[MAX_CHANNEL_PER_OP_CLASS]; + uint8_t op_class; + uint8_t channels_nr; + map_radio_operation_restriction_tlv_channel_t *channels; } map_radio_operation_restriction_tlv_op_class_t; typedef struct mapRadioOperationRestrictionTLV { - uint8_t tlv_type; - mac_addr radio_id; - uint8_t op_classes_nr; - map_radio_operation_restriction_tlv_op_class_t op_classes[MAX_OP_CLASS]; + uint8_t tlv_type; + mac_addr radio_id; + uint8_t op_classes_nr; + map_radio_operation_restriction_tlv_op_class_t *op_classes; } map_radio_operation_restriction_tlv_t; /*####################################################################### @@ -462,11 +488,11 @@ typedef struct { } map_operating_channel_report_tlv_op_class_t; typedef struct mapOperatingChannelReportTLV { - uint8_t tlv_type; - mac_addr radio_id; - uint8_t op_classes_nr; - map_operating_channel_report_tlv_op_class_t op_classes[MAX_OP_CLASS]; - uint8_t transmit_power_eirp; + uint8_t tlv_type; + mac_addr radio_id; + uint8_t op_classes_nr; + map_operating_channel_report_tlv_op_class_t *op_classes; + uint8_t transmit_power_eirp; } map_operating_channel_report_tlv_t; /*####################################################################### @@ -550,7 +576,7 @@ typedef struct { uint32_t report_time_interval; uint32_t downlink_data_rate; uint32_t uplink_data_rate; - uint8_t uplink_rssi; + uint8_t uplink_rcpi; } map_assoc_sta_link_metrics_tlv_bss_t; typedef struct mapAssociatedStaLinkMetricsTLV { @@ -570,10 +596,10 @@ typedef struct { } map_unassoc_sta_link_metrics_query_tlv_channel_t; typedef struct mapUnassocStaMetricsQueryTLV { - uint8_t tlv_type; - uint8_t op_class; - uint8_t channels_nr; - map_unassoc_sta_link_metrics_query_tlv_channel_t channels[MAX_CHANNEL_PER_OP_CLASS]; + uint8_t tlv_type; + uint8_t op_class; + uint8_t channels_nr; + map_unassoc_sta_link_metrics_query_tlv_channel_t *channels; } map_unassoc_sta_link_metrics_query_tlv_t; /*###################################################################################### @@ -601,18 +627,18 @@ typedef struct mapUnassocStaMetricsResponseTLV { #define MAP_BEACON_REPORT_DETAIL_ALL 2 typedef struct mapBeaconMetricsQueryTLV { - uint8_t tlv_type; - mac_addr sta_mac; - uint8_t op_class; - uint8_t channel; - mac_addr bssid; - uint8_t reporting_detail; - uint8_t ssid_len; - uint8_t ssid[MAX_SSID_LEN]; - uint8_t element_ids_nr; - uint8_t element_ids[255]; - uint8_t ap_channel_reports_nr; - map_tlv_op_class_t ap_channel_reports[MAX_OP_CLASS]; + uint8_t tlv_type; + mac_addr sta_mac; + uint8_t op_class; + uint8_t channel; + mac_addr bssid; + uint8_t reporting_detail; + uint8_t ssid_len; + uint8_t ssid[MAX_SSID_LEN]; + uint8_t element_ids_nr; + uint8_t element_ids[255]; + uint8_t ap_channel_reports_nr; + map_tlv_op_class_t *ap_channel_reports; } map_beacon_metrics_query_tlv_t; /*####################################################################### @@ -749,13 +775,13 @@ typedef struct mapHigherLayerDataTLV { typedef struct mapAssocStaTrafficStatsTLV { uint8_t tlv_type; mac_addr sta_mac; - uint32_t txbytes; - uint32_t rxbytes; - uint32_t txpkts; - uint32_t rxpkts; - uint32_t txpkterrors; - uint32_t rxpkterrors; - uint32_t retransmission_cnt; + uint32_t tx_bytes; + uint32_t rx_bytes; + uint32_t tx_packets; + uint32_t rx_packets; + uint32_t tx_packet_errors; + uint32_t rx_packet_errors; + uint32_t retransmissions; } map_assoc_sta_traffic_stats_tlv_t; /*####################################################################### @@ -792,12 +818,12 @@ typedef struct mapChannelScanReportPolicyTLV { #define MAP_SCAN_IMPACT_RADIO_UNAVAILABLE 0x03 /* Radio unavailable for >=2 seconds */ typedef struct { - mac_addr radio_id; - uint8_t boot_only: 1; - uint8_t scan_impact: 2; - uint32_t min_scan_interval; - uint8_t op_classes_nr; - map_tlv_op_class_t op_classes[MAX_OP_CLASS]; + mac_addr radio_id; + uint8_t boot_only: 1; + uint8_t scan_impact: 2; + uint32_t min_scan_interval; + uint8_t op_classes_nr; + map_tlv_op_class_t *op_classes; } map_channel_scan_cap_tlv_radio_t; typedef struct mapChannelScanCapabilitiesTLV { @@ -810,9 +836,9 @@ typedef struct mapChannelScanCapabilitiesTLV { # Channel scan request TLV associated structures ("Section 17.2.39") # ########################################################################*/ typedef struct { - mac_addr radio_id; - uint8_t op_classes_nr; - map_tlv_op_class_t op_classes[MAX_OP_CLASS]; + mac_addr radio_id; + uint8_t op_classes_nr; + map_tlv_op_class_t *op_classes; } map_channel_scan_request_tlv_radio_t; typedef struct mapChannelScanRequestTLV { @@ -921,7 +947,7 @@ typedef struct mapCACCompletionReportTLV { } map_cac_completion_report_tlv_t; /*####################################################################### -# CAC statiu report TLV associated structures ("Section 17.2.45") # +# CAC status report TLV associated structures ("Section 17.2.45") # ########################################################################*/ typedef struct mapCACStatusReportTLV { uint8_t tlv_type; @@ -943,10 +969,10 @@ typedef struct mapCACStatusReportTLV { #define MAX_CAC_METHODS 4 typedef struct { - uint8_t cac_method; - uint32_t cac_duration; - uint8_t op_classes_nr; - map_tlv_op_class_t op_classes[MAX_OP_CLASS]; + uint8_t cac_method; + uint32_t cac_duration; + uint8_t op_classes_nr; + map_tlv_op_class_t *op_classes; } map_cac_cap_tlv_method_t; typedef struct { @@ -1116,12 +1142,12 @@ typedef struct mapRadioMetricsTLV { typedef struct mapAPExtendedMetricsTLV { uint8_t tlv_type; mac_addr bssid; - uint32_t ucast_bytes_tx; - uint32_t ucast_bytes_rx; - uint32_t mcast_bytes_tx; - uint32_t mcast_bytes_rx; - uint32_t bcast_bytes_tx; - uint32_t bcast_bytes_rx; + uint32_t tx_ucast_bytes; + uint32_t rx_ucast_bytes; + uint32_t tx_mcast_bytes; + uint32_t rx_mcast_bytes; + uint32_t tx_bcast_bytes; + uint32_t rx_bcast_bytes; } map_ap_ext_metrics_tlv_t; /*#################################################################################### @@ -1168,9 +1194,9 @@ typedef struct mapBackhaulSTARadioCapabilitiesTLV { mac_addr bsta_mac; } map_backhaul_sta_radio_cap_tlv_t; -/*####################################################################### -# Backhaul BSS configuration associated structures ("Section 17.2.66") # -########################################################################*/ +/*########################################################################### +# Backhaul BSS configuration TLV associated structures ("Section 17.2.66") # +############################################################################*/ typedef struct mapBhBssConfigTLV { uint8_t tlv_type; mac_addr bssid; @@ -1179,6 +1205,35 @@ typedef struct mapBhBssConfigTLV { uint8_t reserved: 6; } map_backhaul_bss_configuration_tlv_t; +/*############################################################################### +# 1905 Layer Security Capability TLV associated structures ("Section 17.2.67") # +################################################################################*/ +#define MAP_1905_ONBOARDING_PROTOCOL_DPP 0x00 +#define MAP_1905_MIC_ALGO_HMAC_SHA256 0x00 +#define MAP_1905_ENCRPYT_ALGO_AES_SIV 0x00 + +typedef struct map1905SecurityCapTLV { + uint8_t tlv_type; + uint8_t onboarding_protocol; + uint8_t mic_algorithm; + uint8_t encryption_algorithm; +} map_1905_security_cap_tlv_t; + +/*####################################################################### +# MIC TLV associated structures ("Section 17.2.68") # +########################################################################*/ +typedef struct micTLV { + uint8_t tlv_type; + uint8_t gtk_key_id : 2; + uint8_t mic_version : 2; + uint8_t reserved : 4; + uint8_t integrity_tx_counter[INTEGRITY_TX_COUNTER_LEN]; + mac_addr src_al_mac; + uint16_t mic_len; + uint8_t *mic; +} map_mic_tlv_t; +#define SIZEOF_MIC_TLV (sizeof(map_mic_tlv_t) - sizeof(uint8_t *)) + /*####################################################################### # Encrypted Payload TLV associated structures ("Section 17.2.69") # ########################################################################*/ @@ -1192,7 +1247,7 @@ typedef struct mapEncryptedPayloadTLV { } map_encrypted_payload_tlv_t; /*####################################################################### -# AP Wi-Fi 6 Capabilities associated structures ("Section 17.2.72") # +# AP Wi-Fi 6 Capabilities TLV associated structures ("Section 17.2.72") # ########################################################################*/ typedef struct mapAPWiFi6CapTLV { uint8_t tlv_type; @@ -1202,10 +1257,9 @@ typedef struct mapAPWiFi6CapTLV { } map_ap_wifi6_cap_tlv_t; -/*################################################################################ -# Associated Wi-Fi 6 STA Status Report associated structures ("Section 17.2.73") # -#################################################################################*/ - +/*#################################################################################### +# Associated Wi-Fi 6 STA Status Report TLV associated structures ("Section 17.2.73") # +#####################################################################################*/ typedef struct mapAssocWiFi6STAStatusTLV { uint8_t tlv_type; mac_addr sta_mac; @@ -1222,8 +1276,89 @@ typedef struct mapBSSIDTLV { mac_addr bssid; } map_bssid_tlv_t; +/*######################################################################## +# BSS Configuration Report TLV associated structures("Section 17.2.75") # +#########################################################################*/ +typedef struct { + mac_addr bssid; + uint8_t backhaul_bss: 1; /* 0: in use, 1: not in use */ + uint8_t fronthaul_bss: 1; /* 0: in use, 1: not in use */ + uint8_t r1_disallowed_status: 1; /* 0: allowed, 1: disallowed */ + uint8_t r2_disallowed_status: 1; /* 0: allowed, 1: disallowed */ + uint8_t multiple_bssid: 1; /* 0: not configured, 1: configured */ + uint8_t transmitted_bssid: 1; /* 0: non transmitted, 1: transmitted */ + uint8_t reserved1: 2; + uint8_t reserved2; + uint8_t ssid_len; + char ssid[MAX_SSID_LEN]; +} map_bss_configuration_bss_t; + +typedef struct { + mac_addr ruid; + uint8_t bss_nr; + map_bss_configuration_bss_t bss[MAX_BSS_PER_RADIO]; +} map_bss_configuration_radio_t; + +typedef struct mapBSSConfigurationReportTLV { + uint8_t tlv_type; + uint8_t radios_nr; + map_bss_configuration_radio_t radios[MAX_RADIO_PER_AGENT]; +} map_bss_configuration_report_tlv_t; + +/*####################################################################### +# Device Inventory TLV associated structures ("Section 17.2.76") # +########################################################################*/ +typedef struct { + mac_addr ruid; + uint8_t vendor_len; + uint8_t vendor[MAP_INVENTORY_ITEM_LEN]; +} map_radio_vendor_t; +typedef struct mapDeviceInventoryTLV { + uint8_t tlv_type; + uint8_t serial_len; + uint8_t serial[MAP_INVENTORY_ITEM_LEN]; + uint8_t version_len; + uint8_t version[MAP_INVENTORY_ITEM_LEN]; + uint8_t environment_len; + uint8_t environment[MAP_INVENTORY_ITEM_LEN]; + uint8_t radios_nr; + map_radio_vendor_t radios[MAX_RADIO_PER_AGENT]; +} map_device_inventory_tlv_t; + +/*####################################################################### +# Agent List TLV associated structures ("Section 17.2.77") # +########################################################################*/ +typedef struct { + mac_addr al_mac; + uint8_t map_profile; + uint8_t security; +} map_agent_entry_t; + +typedef struct { +#define MAX_AGENT 16 + uint8_t tlv_type; + uint8_t agent_nr; + map_agent_entry_t entries[MAX_AGENT]; +} map_agent_list_tlv_t; + +/*####################################################################### +# AKM Suite Capabilities TLV associated structures ("Section 17.2.78") # +########################################################################*/ +typedef struct { + uint8_t oui[3]; + uint8_t akm_suite_type; +} map_akm_suite_t; + +typedef struct mapAKMSuiteCapTLV { + uint8_t tlv_type; + uint8_t bh_akm_suites_nr; + map_akm_suite_t *bh_akm_suites; + uint8_t fh_akm_suites_nr; + map_akm_suite_t *fh_akm_suites; +} map_akm_suite_cap_tlv_t; + /*####################################################################### -# 1905 Encap DPP TLV ("Section 17.2.79") # +# 1905 Encap DPP TLV associated structures ("Section 17.2.79") # ########################################################################*/ typedef struct map1905EncapDPPTLV { uint8_t tlv_type; @@ -1238,7 +1373,7 @@ typedef struct map1905EncapDPPTLV { } map_1905_encap_dpp_tlv_t; /*####################################################################### -# DPP Message TLV ("Section 17.2.80") # +# DPP Message TLV associated structures ("Section 17.2.80") # ########################################################################*/ typedef struct map1905EncapEapolTLV { uint8_t tlv_type; @@ -1247,7 +1382,7 @@ typedef struct map1905EncapEapolTLV { } map_1905_encap_eapol_tlv_t; /*####################################################################### -# DPP CCE Indication TLV ("Section 17.2.82") # +# DPP CCE Indication TLV associated structures ("Section 17.2.82") # ########################################################################*/ typedef struct mapDPPCCEIndicationTLV { uint8_t tlv_type; @@ -1255,7 +1390,7 @@ typedef struct mapDPPCCEIndicationTLV { } map_dpp_cce_indication_tlv_t; /*####################################################################### -# DPP Chirp Value TLV ("Section 17.2.83") # +# DPP Chirp Value TLV associated structures ("Section 17.2.83") # ########################################################################*/ typedef struct mapDPPChirpValueTLV { uint8_t tlv_type; @@ -1267,8 +1402,26 @@ typedef struct mapDPPChirpValueTLV { uint8_t *hash; } map_dpp_chirp_value_tlv_t; +/*######################################################################## +# BSS Configuration Request TLV associated structures("Section 17.2.84") # +#########################################################################*/ +typedef struct mapBSSConfigurationRequestTLV { + uint8_t tlv_type; + uint16_t obj_len; + uint8_t *obj; +} map_bss_configuration_request_tlv_t; + +/*######################################################################### +# BSS Configuration Response TLV associated structures("Section 17.2.85") # +##########################################################################*/ +typedef struct mapBSSConfigurationResponseTLV { + uint8_t tlv_type; + uint16_t obj_len; + uint8_t *obj; +} map_bss_configuration_response_tlv_t; + /*####################################################################### -# DPP Message TLV ("Section 17.2.86") # +# DPP Message TLV associated structures ("Section 17.2.86") # ########################################################################*/ typedef struct mapDPPMessageTLV { uint8_t tlv_type; @@ -1277,24 +1430,194 @@ typedef struct mapDPPMessageTLV { } map_dpp_message_tlv_t; /*####################################################################### -# Device Inventory TLV ("Section 17.2.76") # +# Controller Capability TLV associated structures ("Section 17.2.94") # ########################################################################*/ +#define MAP_CONTROLLER_CAP_KIBMIB_COUNTER 0x80 /* Bit 7 */ +#define MAP_CONTROLLER_CAP_EARLY_AP_CAP 0x40 /* Bit 6 */ + +typedef struct mapControllerCapabilityTLV { + uint8_t tlv_type; + uint8_t capability; +} map_controller_capability_tlv_t; + +/*########################################################################## +# Wi-Fi 7 Agent Capabilities TLV associated structures ("Section 17.2.95") # +###########################################################################*/ typedef struct { - mac_addr ruid; - uint8_t vendor_len; - uint8_t vendor[MAP_INVENTORY_ITEM_LEN]; -} map_radio_vendor_t; -typedef struct mapDeviceInventoryTLV { + mac_addr ruid; + uint8_t reserved[24]; + map_radio_wifi7_caps_t cap; +} map_wifi7_agent_cap_tlv_radio_t; + +typedef struct mapWiFi7AgentCapTLV { + uint8_t tlv_type; + uint8_t max_mlds; + uint8_t ap_max_links: 4; + uint8_t bsta_max_links: 4; + uint8_t tid_to_link_map_cap: 2; + uint8_t reserved1: 6; + uint8_t reserved2[13]; + uint8_t radios_nr; + map_wifi7_agent_cap_tlv_radio_t radios[MAX_RADIO_PER_AGENT]; +} map_wifi7_agent_cap_tlv_t; + +/*########################################################################## +# Agent AP MLD Configuration TLV associated structures ("Section 17.2.96") # +###########################################################################*/ +typedef struct { + uint8_t aff_ap_mac_valid: 1; + uint8_t link_id_valid: 1; + mac_addr radio_id; + mac_addr aff_ap_mac; + uint8_t link_id; +} map_agent_ap_mld_conf_tlv_aff_ap_t; + +typedef struct { + uint8_t ap_mld_mac_valid: 1; + uint8_t ssid_len; + uint8_t ssid[MAX_SSID_LEN]; + mac_addr ap_mld_mac; + uint8_t str: 1; + uint8_t nstr: 1; + uint8_t emlsr: 1; + uint8_t emlmr: 1; + uint8_t aff_ap_nr; + map_agent_ap_mld_conf_tlv_aff_ap_t aff_aps[MAX_MLD_AFF_APSTA]; +} map_agent_ap_mld_conf_tlv_ap_mld_t; + +typedef struct mapAgentAPMLDConfTLV { + uint8_t tlv_type; + uint8_t ap_mld_nr; + map_agent_ap_mld_conf_tlv_ap_mld_t ap_mlds[MAX_BSS_PER_RADIO]; +} map_agent_ap_mld_conf_tlv_t; + +/*############################################################################## +# Backhaul STA MLD Configuration TLV associated structures ("Section 17.2.97") # +###############################################################################*/ +typedef struct { + uint8_t aff_bsta_mac_valid: 1; + mac_addr radio_id; + mac_addr aff_bsta_mac; +} map_bsta_mld_conf_tlv_aff_bsta_t; + +typedef struct mapBSTAMLDConfTLV { + uint8_t tlv_type; + uint8_t bsta_mld_mac_valid: 1; + uint8_t ap_mld_mac_valid: 1; + mac_addr bsta_mld_mac; + mac_addr ap_mld_mac; + uint8_t str: 1; + uint8_t nstr: 1; + uint8_t emlsr: 1; + uint8_t emlmr: 1; + uint8_t aff_bsta_nr; + map_bsta_mld_conf_tlv_aff_bsta_t aff_bstas[MAX_MLD_AFF_APSTA]; +} map_bsta_mld_conf_tlv_t; + +/*################################################################################ +# Associated STA MLD Configuration TLV associated structures ("Section 17.2.98") # +#################################################################################*/ +typedef struct { + mac_addr bssid; + mac_addr aff_sta_mac; +} map_assoc_sta_mld_conf_tlv_aff_sta_t; + +typedef struct mapAssocSTAMLDConfTLV { + uint8_t tlv_type; + mac_addr sta_mld_mac; + mac_addr ap_mld_mac; + uint8_t str: 1; + uint8_t nstr: 1; + uint8_t emlsr: 1; + uint8_t emlmr: 1; + uint8_t aff_sta_nr; + map_assoc_sta_mld_conf_tlv_aff_sta_t aff_stas[MAX_MLD_AFF_APSTA]; +} map_assoc_sta_mld_conf_tlv_t; + +/*################################################################################ +# Affiliated STA Metrics TLV associated structures ("Section 17.2.100") # +#################################################################################*/ +/* NOTE: EM R6 adds 998 reserved bytes for this TLV. That is odd given that this TLV + can be part of the AP Metrics Response multiple times -> do not check on this. +*/ +#define MAP_AFF_STA_METRICS_TLV_RESERVED_LEN_ZERO 0 +#define MAP_AFF_STA_METRICS_TLV_RESERVED_LEN_R6 998 + +typedef struct mapAffSTAMetricsTLV { + uint8_t tlv_type; + mac_addr sta_mac; + uint32_t tx_bytes; + uint32_t rx_bytes; + uint32_t tx_packets; + uint32_t rx_packets; + uint32_t tx_packet_errors; + uint16_t reserved_len; +} map_aff_sta_metrics_tlv_t; + +/*################################################################################ +# Affiliated AP Metrics TLV associated structures ("Section 17.2.101") # +#################################################################################*/ +/* NOTE: EM R6 adds 988 reserved bytes for this TLV. That is odd given that this TLV + can be part of the AP Metrics Response multiple times -> do not check on this. +*/ +#define MAP_AFF_AP_METRICS_TLV_RESERVED_LEN_ZERO 0 +#define MAP_AFF_AP_METRICS_TLV_RESERVED_LEN_R6 988 + +typedef struct mapAffAPMetricsTLV { + uint8_t tlv_type; + mac_addr bssid; + uint32_t tx_packets; + uint32_t rx_packets; + uint32_t tx_packet_errors; + uint32_t tx_ucast_bytes; + uint32_t rx_ucast_bytes; + uint32_t tx_mcast_bytes; + uint32_t rx_mcast_bytes; + uint32_t tx_bcast_bytes; + uint32_t rx_bcast_bytes; + uint16_t reserved_len; +} map_aff_ap_metrics_tlv_t; + +/*####################################################################### +# EHT Operations TLV associated structures ("Section 17.2.103") # +########################################################################*/ +typedef struct { + mac_addr bssid; + map_ap_eht_operations_t eht_ops; + uint8_t reserved[16]; +} map_eht_operations_tlv_bss_t; + +typedef struct { + mac_addr ruid; + uint8_t bsss_nr; + map_eht_operations_tlv_bss_t bsss[MAX_BSS_PER_RADIO]; + uint8_t reserved[25]; +} map_eht_operations_tlv_radio_t; + +typedef struct mapEHTOperationsTLV { + uint8_t tlv_type; + uint8_t reserved[32]; + uint8_t radios_nr; + map_eht_operations_tlv_radio_t radios[MAX_RADIO_PER_AGENT]; +} map_eht_operations_tlv_t; + +/*############################################################################################## +# Available Spectrum Inquiry Request TLV associated structures ("Section 17.2.104") # +###############################################################################################*/ +typedef struct mapAvailableSpecInqReqTLV { uint8_t tlv_type; - uint8_t serial_len; - uint8_t serial[MAP_INVENTORY_ITEM_LEN]; - uint8_t version_len; - uint8_t version[MAP_INVENTORY_ITEM_LEN]; - uint8_t environment_len; - uint8_t environment[MAP_INVENTORY_ITEM_LEN]; - uint8_t radios_nr; - map_radio_vendor_t radios[MAX_RADIO_PER_AGENT]; -} map_device_inventory_tlv_t; + uint16_t req_len; + uint8_t *req; +} map_available_spec_inq_req_tlv_t; + +/*############################################################################################## +# Available Spectrum Inquiry Response TLV associated structures ("Section 17.2.105") # +###############################################################################################*/ +typedef struct mapAvailableSpecInqRespTLV { + uint8_t tlv_type; + uint16_t resp_len; + uint8_t *resp; +} map_available_spec_inq_resp_tlv_t; /*####################################################################### # PUBLIC FUNCTIONS # @@ -1308,5 +1631,7 @@ void map_free_p1_p2_steering_request_tlv(void *memory_structure, bool profile2); void map_r1_register_tlvs(void); void map_r2_register_tlvs(void); void map_r3_register_tlvs(void); +void map_r4_register_tlvs(void); +void map_r6_register_tlvs(void); #endif /* MAP_TLVS_H_ */ diff --git a/source/ieee1905/src/factory/src_independent/1905_cmdus.c b/source/ieee1905/src/factory/src_independent/1905_cmdus.c index 6d5584e..9b59c39 100644 --- a/source/ieee1905/src/factory/src_independent/1905_cmdus.c +++ b/source/ieee1905/src/factory/src_independent/1905_cmdus.c @@ -187,7 +187,7 @@ static uint8_t _relayed_CMDU[] = { /* CMDU_TYPE_AP_AUTOCONFIGURATION_SEARCH */ 1, /* CMDU_TYPE_AP_AUTOCONFIGURATION_RESPONSE */ 0, /* CMDU_TYPE_AP_AUTOCONFIGURATION_WSC */ 0, - /* CMDU_TYPE_AP_AUTOCONFIGURATION_RENEW */ 1, + /* CMDU_TYPE_AP_AUTOCONFIGURATION_RENEW */ 0xff, /* CMDU_TYPE_PUSH_BUTTON_EVENT_NOTIFICATION */ 1, /* CMDU_TYPE_PUSH_BUTTON_JOIN_NOTIFICATION */ 1, /* CMDU_TYPE_HIGHER_LAYER_QUERY */ 0, @@ -395,6 +395,10 @@ i1905_cmdu_t *parse_1905_CMDU_from_packets(uint8_t **packet_streams, uint16_t *p uint8_t current_fragment; uint16_t tlvs_nr; uint8_t error; + uint8_t *message = NULL; + uint16_t message_len = 0; + uint16_t message_offset = 0; + uint16_t last_fragment_len = 0; if (NULL == packet_streams) { /* Invalid arguments */ @@ -413,17 +417,29 @@ i1905_cmdu_t *parse_1905_CMDU_from_packets(uint8_t **packet_streams, uint16_t *p return NULL; } + /* Calculate the message length */ + for (current_fragment = 0; current_fragmentlist_of_TLVs = malloc(sizeof(uint8_t *) * 1); if (!ret->list_of_TLVs) { free(ret); + free(message); return NULL; } ret->list_of_TLVs[0] = NULL; @@ -433,7 +449,7 @@ i1905_cmdu_t *parse_1905_CMDU_from_packets(uint8_t **packet_streams, uint16_t *p error = 0; for (current_fragment = 0; current_fragment 0) { + parsed_tlv = parse_1905_TLV_from_packet(p, bytes_left); + if (NULL == parsed_tlv) { + /* Error while parsing a TLV + * Dump TLV for visual inspection + */ - _E1B(&p2, &aux); - _E2B(&p2, &len); + char aux1[200]; + char aux2[10]; - log_i1905_w("Parsing error. Dumping bytes: %d ", error); + uint8_t *p2 = p; + uint16_t len; - /* Limit dump length */ - if (len > 200) { - len = 200; - } + uint8_t first_time; + uint8_t j; + uint8_t aux; - aux1[0] = 0x0; - aux2[0] = 0x0; - first_time = 1; - for (j = 0; j < len + 3; j++) { - snprintf(aux2, 6, "0x%02x ", p[j]); - strncat(aux1, aux2, 200 - strlen(aux1)-1); - - if (0 != j && 0 == (j + 1) % 8) { - if (1 == first_time) { - log_i1905_t("[PLATFORM] - Payload = %s", aux1); - first_time = 0; - } else { - log_i1905_t("[PLATFORM] %s", aux1); - } - aux1[0] = 0x0; - } - } + _E1B(&p2, &aux); + _E2B(&p2, &len); - error = 6; - break; + log_i1905_w("Parsing error. Dumping bytes: %d ", error); + + /* Do not allow TLV to go beyond CMDU */ + if (len > message_offset) { + len = message_offset; } - /* FRV: End_of_message_TLV should only be present at the end of the last fragment, - * some ale send it in every packet. - * - * Parsing error when - * "no end_of_message_TLV" and "bytes_left == 0" and "last_fragment bit set" - * Advance to next fragment if: - * "end_of_message_TLV present" or "bytes_left == 0" - */ - if (TLV_TYPE_END_OF_MESSAGE == *parsed) { - /* No more TLVs */ - free_1905_TLV_structure(parsed); - break; + /* Limit dump length */ + if (len > 200) { + len = 200; + } + + aux1[0] = 0x0; + aux2[0] = 0x0; + first_time = 1; + for (j = 0; j < len + 3; j++) { + snprintf(aux2, 6, "0x%02x ", p[j]); + strncat(aux1, aux2, 200 - strlen(aux1)-1); + + if (0 != j && 0 == (j + 1) % 8) { + if (1 == first_time) { + log_i1905_t("[PLATFORM] - Payload = %s", aux1); + first_time = 0; + } else { + log_i1905_t("[PLATFORM] %s", aux1); + } + aux1[0] = 0x0; + } } - /* Advance 'p' to the next TLV */ - uint8_t tlv_type; - uint16_t tlv_len; + error = 6; + break; + } - _E1B(&p, &tlv_type); - _E2B(&p, &tlv_len); + /* Advance 'p' to the next TLV */ + uint8_t tlv_type; + uint16_t tlv_len; - p += tlv_len; - bytes_left -= (3 + tlv_len); + _E1B(&p, &tlv_type); + _E2B(&p, &tlv_len); - /* Add this new TLV to the list (the list needs to be re-allocated - * with more space first) - */ - tlvs_nr++; - uint8_t **new_list_of_TLVs = realloc(ret->list_of_TLVs, sizeof(uint8_t *) * (tlvs_nr + 1)); - if (!new_list_of_TLVs) { - error = 10; - break; - } - ret->list_of_TLVs = new_list_of_TLVs; - ret->list_of_TLVs[tlvs_nr - 1] = parsed; - ret->list_of_TLVs[tlvs_nr] = NULL; - - if (0 == bytes_left) { - /* Advance to next packet, except last fragment should have had an end_of_message_TLV */ - if (1 == last_fragment_indicator) { - error = 9; - } + p += tlv_len; + bytes_left -= (3 + tlv_len); + + if (TLV_TYPE_END_OF_MESSAGE == tlv_type) { + if(bytes_left >= last_fragment_len) { + /* + * We didn't reached the last fragment yet. + * End of Message TLV should only be present at the end of the last fragment. Skip it + */ + free_1905_TLV_structure(parsed_tlv); + continue; + } else { + /* No more TLVs end of message */ + free_1905_TLV_structure(parsed_tlv); break; } } - if (0 != error) { + + /* Add this new TLV to the list (the list needs to be re-allocated + * with more space first) + */ + tlvs_nr++; + uint8_t **new_list_of_TLVs = realloc(ret->list_of_TLVs, sizeof(uint8_t *) * (tlvs_nr + 1)); + if (!new_list_of_TLVs) { + error = 9; break; } + ret->list_of_TLVs = new_list_of_TLVs; + ret->list_of_TLVs[tlvs_nr - 1] = parsed_tlv; + ret->list_of_TLVs[tlvs_nr] = NULL; } + SFREE(message); if (0 == error) { /* Ok then... we now have our output structure properly filled. @@ -716,13 +736,10 @@ uint8_t **forge_1905_CMDU_from_structure(i1905_cmdu_t *memory_structure, uint16_ { uint8_t **ret; - uint16_t tlv_start; - uint16_t tlv_stop; - - uint8_t fragments_nr; - - uint32_t max_tlvs_block_size; - uint32_t max_last_tlvs_block_size; + uint16_t tlv_start = 0; + uint16_t tlv_stop = 0; + uint16_t jumbo_tlv_offset = 0; + uint8_t fragments_nr = 0; uint8_t error = 0; @@ -758,8 +775,6 @@ uint8_t **forge_1905_CMDU_from_structure(i1905_cmdu_t *memory_structure, uint16_ } (*lens)[0] = 0; - fragments_nr = 0; - /* Let's create as many streams as needed so that all of them fit in * MAX_NETWORK_SEGMENT_SIZE bytes. * @@ -782,9 +797,7 @@ uint8_t **forge_1905_CMDU_from_structure(i1905_cmdu_t *memory_structure, uint16_ * - X bytes (size of all TLVs contained in the fragment) * - 3 bytes (TLV_TYPE_END_OF_MESSAGE TLV) - only in last fragment * - * In other words, X (the size of all the TLVs that are going to be inside - * this fragmen) can not be greater than MAX_NETWORK_SEGMENT_SIZE - 6 - 6 - - * - 4- 2 - 1 - 1 - 2 - 2 - 1 - 1 = MAX_NETWORK_SEGMENT_SIZE - 26 bytes. + * Jumbo TLVs can partially involve into multiple fragment since EM R3.(section 15.2) * * And another 3 bytes need to be reserved in for the last fragment * @@ -795,67 +808,16 @@ uint8_t **forge_1905_CMDU_from_structure(i1905_cmdu_t *memory_structure, uint16_ #error Invalid MAX_NETWORK_SEGMENT_SIZE #endif - max_tlvs_block_size = MAX_NETWORK_SEGMENT_SIZE - ETH_8021Q_HDR_SIZE - CMDU_HDR_SIZE; - max_last_tlvs_block_size = max_tlvs_block_size - 3; - tlv_start = 0; - tlv_stop = 0; do { - uint8_t *s; - uint16_t i; + uint8_t *s, *s2; uint16_t current_X_size = 0; - uint8_t no_space = 0; uint8_t reserved_field; uint8_t fragment_id; uint8_t indicators; - uint16_t tlv_stream_size = 0; - - while (memory_structure->list_of_TLVs[tlv_stop]) { - uint8_t *p; - uint8_t *tlv_stream; - /* Max block size is different for last fragment (no next TLV) */ - uint32_t break_size = memory_structure->list_of_TLVs[tlv_stop + 1] ? - max_tlvs_block_size : max_last_tlvs_block_size; - - p = memory_structure->list_of_TLVs[tlv_stop]; - - tlv_stream = forge_1905_TLV_from_structure(p, &tlv_stream_size); - free(tlv_stream); - - if (current_X_size + tlv_stream_size <= break_size) { - tlv_stop++; - } else { - /* There is no space for more TLVs */ - no_space = 1; - break; - } - - current_X_size += tlv_stream_size; - } - if (tlv_start == tlv_stop) { - if (1 == no_space) { - /* One *single* TLV does not fit in a fragment! - * This is an error... there is no way to split one single TLV into - * several fragments according to the standard. - */ - log_i1905_e("single TLV does not fit (length %d)", tlv_stream_size); - error = 1; - break; - } else { - /* If we end up here, it means tlv_start = tlv_stop = 0 --> this - * CMDU contains no TLVs (which is something that can happen... - * for example, in the "topology query" CMDU). - * Just keep executing... - */ - } - } - - /* Now that we know how many TLVs are going to be embedded inside this - * fragment (from 'tlv_start' up to -and not including- 'tlv_stop'), - * let's build it - */ + /* Create a new fragment */ fragments_nr++; /* Allocate memory for next fragment, realloc ret and lens arrays */ @@ -881,17 +843,12 @@ uint8_t **forge_1905_CMDU_from_structure(i1905_cmdu_t *memory_structure, uint16_ (*lens)[fragments_nr-1] = 0; /* To be updated a few lines later */ (*lens)[fragments_nr] = 0; - s = ret[fragments_nr-1]; + s2 = s = ret[fragments_nr-1]; /* Save a backup pointer to update fields later */ reserved_field = 0; fragment_id = fragments_nr-1; indicators = 0; - /* Set 'last_fragment_indicator' flag (bit #7) */ - if (NULL == memory_structure->list_of_TLVs[tlv_stop]) { - indicators |= 1 << 7; - } - /* Set 'relay_indicator' flag (bit #6) */ /* Again this hack as relayed CMDU is only handled for 1905 TLV Avoid array out of bounds for the MultiAP TLV types @@ -913,26 +870,63 @@ uint8_t **forge_1905_CMDU_from_structure(i1905_cmdu_t *memory_structure, uint16_ _I1B(&fragment_id, &s); _I1B(&indicators, &s); - for (i = tlv_start; i < tlv_stop; i++) { - uint8_t *tlv_stream = NULL; + while (memory_structure->list_of_TLVs[tlv_stop]) { + uint8_t *tlv_stream = NULL; uint16_t tlv_stream_size = 0; + uint16_t bytes_copied = 0; - tlv_stream = forge_1905_TLV_from_structure(memory_structure->list_of_TLVs[i], &tlv_stream_size); + /* Max block size is different for last fragment (no next TLV) */ + uint32_t break_size = memory_structure->list_of_TLVs[tlv_stop + 1] ? MAX_TLV_SIZE : MAX_LAST_TLV_SIZE; - if(tlv_stream == NULL) { + tlv_stream = forge_1905_TLV_from_structure(memory_structure->list_of_TLVs[tlv_stop], &tlv_stream_size); + if (!tlv_stream) { log_i1905_e("forged NULL tlv stream out of memory structure"); + tlv_stop++; /* Advance to the next tlv */ + break; + } + + if (current_X_size + tlv_stream_size <= break_size) { + bytes_copied = tlv_stream_size; + memcpy(s, tlv_stream, bytes_copied); + s += bytes_copied; + tlv_stop++; /* Advance to the next tlv */ + } else if (tlv_stream_size > break_size) { /* Jumbo TLV */ + uint16_t bytes_left_fragment = break_size - current_X_size; + uint16_t bytes_left_jumbo_tlv = tlv_stream_size - jumbo_tlv_offset; + + if (bytes_left_jumbo_tlv > bytes_left_fragment) { + memcpy(s, tlv_stream + jumbo_tlv_offset, bytes_left_fragment); + s += bytes_left_fragment; + bytes_copied = bytes_left_fragment; + jumbo_tlv_offset += bytes_copied; + SFREE(tlv_stream); + break; /* Loop over to create a new fragment */ + } else { + memcpy(s, tlv_stream + jumbo_tlv_offset, bytes_left_jumbo_tlv); + s += bytes_left_jumbo_tlv; + bytes_copied = bytes_left_jumbo_tlv; + jumbo_tlv_offset = 0; + tlv_stop++; /* Advance to the next tlv */ + } } else { - memcpy(s, tlv_stream, tlv_stream_size); + /* There is no space for more TLVs */ + SFREE(tlv_stream); + break; } SFREE(tlv_stream); - - s += tlv_stream_size; + current_X_size += bytes_copied; } - /* Don't forget to add the last three octects representing the - * TLV_TYPE_END_OF_MESSAGE message for the last fragment + /* For the last fragment add TLV_TYPE_END_OF_MESSAGE + * and set 'last fragment indicator' */ if (NULL == memory_structure->list_of_TLVs[tlv_stop]) { + + /* Set 'last_fragment_indicator' flag (bit #7) */ + indicators |= 1 << 7; + s2 +=7; + _I1B(&indicators, &s2); /* Override indicators */ + *s = 0x0; s++; *s = 0x0; s++; *s = 0x0; s++; @@ -945,7 +939,6 @@ uint8_t **forge_1905_CMDU_from_structure(i1905_cmdu_t *memory_structure, uint16_ * the next one starts where we have stopped. */ tlv_start = tlv_stop; - } while (memory_structure->list_of_TLVs[tlv_start]); /* Finally! If we get this far without errors we are already done, otherwise @@ -1161,6 +1154,14 @@ char *convert_1905_CMDU_type_to_string(uint16_t cmdu_type) CMDU_STR(CMDU_TYPE_MAP_DIRECT_ENCAP_DPP) CMDU_STR(CMDU_TYPE_MAP_CHIRP_NOTIFICATION) CMDU_STR(CMDU_TYPE_MAP_1905_ENCAP_EAPOL) + CMDU_STR(CMDU_TYPE_MAP_BSS_CONFIGURATION_REQUEST) + CMDU_STR(CMDU_TYPE_MAP_BSS_CONFIGURATION_RESPONSE) + CMDU_STR(CMDU_TYPE_MAP_BSS_CONFIGURATION_RESULT) + CMDU_STR(CMDU_TYPE_MAP_AGENT_LIST) + + /* MAP R6 */ + CMDU_STR(CMDU_TYPE_MAP_EARLY_AP_CAPABILITY_REPORT) + CMDU_STR(CMDU_TYPE_MAP_AVAILABLE_SPECTRUM_INQUIRY) default: return "CMDU_TYPE_UNKNOWN"; } diff --git a/source/ieee1905/src/factory/src_independent/1905_tlvs.c b/source/ieee1905/src/factory/src_independent/1905_tlvs.c index 553c0a3..33132c5 100644 --- a/source/ieee1905/src/factory/src_independent/1905_tlvs.c +++ b/source/ieee1905/src/factory/src_independent/1905_tlvs.c @@ -316,12 +316,14 @@ static uint8_t* parse_device_information_tlv(uint8_t *packet_stream, uint16_t le (MEDIA_TYPE_IEEE_802_11AC_5_GHZ == ret->local_interfaces[i].media_type) || (MEDIA_TYPE_IEEE_802_11AD_60_GHZ == ret->local_interfaces[i].media_type) || (MEDIA_TYPE_IEEE_802_11AF == ret->local_interfaces[i].media_type) || - (MEDIA_TYPE_IEEE_802_11AX == ret->local_interfaces[i].media_type)) + (MEDIA_TYPE_IEEE_802_11AX == ret->local_interfaces[i].media_type) || + (MEDIA_TYPE_IEEE_802_11BE == ret->local_interfaces[i].media_type)) { uint8_t aux; - /* For 11AX, EM R2 standard says size should be 0 but some agents do provide data -> accept both */ + /* For 11AX and 11BE, EM R2 standard says size should be 0 but some agents do provide data -> accept both */ bool ok = (10 == ret->local_interfaces[i].media_specific_data_size) || - (( 0 == ret->local_interfaces[i].media_specific_data_size) && (MEDIA_TYPE_IEEE_802_11AX == ret->local_interfaces[i].media_type)); + (( 0 == ret->local_interfaces[i].media_specific_data_size) && + (MEDIA_TYPE_IEEE_802_11AX == ret->local_interfaces[i].media_type || MEDIA_TYPE_IEEE_802_11BE == ret->local_interfaces[i].media_type)); if (!ok) { /* Malformed packet */ @@ -391,6 +393,9 @@ static uint8_t* forge_device_information_tlv(void *memory_structure, uint16_t *l _I2B(&m->local_interfaces[i].media_type, &p); _I1B(&m->local_interfaces[i].media_specific_data_size, &p); + /* Note: for 11AX and 11BE the standard does not require to add media specific info + -> add only when set + */ if ((MEDIA_TYPE_IEEE_802_11B_2_4_GHZ == m->local_interfaces[i].media_type) || (MEDIA_TYPE_IEEE_802_11G_2_4_GHZ == m->local_interfaces[i].media_type) || (MEDIA_TYPE_IEEE_802_11A_5_GHZ == m->local_interfaces[i].media_type) || @@ -399,7 +404,8 @@ static uint8_t* forge_device_information_tlv(void *memory_structure, uint16_t *l (MEDIA_TYPE_IEEE_802_11AC_5_GHZ == m->local_interfaces[i].media_type) || (MEDIA_TYPE_IEEE_802_11AD_60_GHZ == m->local_interfaces[i].media_type) || (MEDIA_TYPE_IEEE_802_11AF == m->local_interfaces[i].media_type) || - (MEDIA_TYPE_IEEE_802_11AX == m->local_interfaces[i].media_type)) + (MEDIA_TYPE_IEEE_802_11AX == m->local_interfaces[i].media_type && m->local_interfaces[i].media_specific_data_size > 0) || + (MEDIA_TYPE_IEEE_802_11BE == m->local_interfaces[i].media_type && m->local_interfaces[i].media_specific_data_size > 0)) { uint8_t aux; @@ -1091,6 +1097,7 @@ static uint8_t* forge_autoconfig_freq_band_tlv(void *memory_structure, uint16_t if ((m->freq_band != IEEE80211_FREQUENCY_BAND_2_4_GHZ) && (m->freq_band != IEEE80211_FREQUENCY_BAND_5_GHZ) && + (m->freq_band != IEEE80211_FREQUENCY_BAND_6_GHZ) && (m->freq_band != IEEE80211_FREQUENCY_BAND_60_GHZ)) { /* Malformed structure */ free(ret); @@ -1181,7 +1188,7 @@ static uint8_t* forge_supported_freq_band_tlv(void *memory_structure, uint16_t * if ((m->freq_band != IEEE80211_FREQUENCY_BAND_2_4_GHZ) && (m->freq_band != IEEE80211_FREQUENCY_BAND_5_GHZ) && - /* (m->freq_band != IEEE80211_FREQUENCY_BAND_6_GHZ) && TODO: add when it is defined in the standard */ + (m->freq_band != IEEE80211_FREQUENCY_BAND_6_GHZ) && (m->freq_band != IEEE80211_FREQUENCY_BAND_60_GHZ)) { /* Malformed structure */ free(ret); @@ -1296,7 +1303,8 @@ static uint8_t* parse_push_button_event_notification_tlv(uint8_t *packet_stream, (MEDIA_TYPE_IEEE_802_11AC_5_GHZ == ret->media_types[i].media_type) || (MEDIA_TYPE_IEEE_802_11AD_60_GHZ == ret->media_types[i].media_type) || (MEDIA_TYPE_IEEE_802_11AF == ret->media_types[i].media_type) || - (MEDIA_TYPE_IEEE_802_11AX == ret->media_types[i].media_type)) + (MEDIA_TYPE_IEEE_802_11AX == ret->media_types[i].media_type) || + (MEDIA_TYPE_IEEE_802_11BE == ret->media_types[i].media_type)) { uint8_t aux; @@ -1363,7 +1371,8 @@ static uint8_t* forge_push_button_event_notification_tlv(void *memory_structure, (MEDIA_TYPE_IEEE_802_11AC_5_GHZ == m->media_types[i].media_type) || (MEDIA_TYPE_IEEE_802_11AD_60_GHZ == m->media_types[i].media_type) || (MEDIA_TYPE_IEEE_802_11AF == m->media_types[i].media_type) || - (MEDIA_TYPE_IEEE_802_11AX == m->media_types[i].media_type)) + (MEDIA_TYPE_IEEE_802_11AX == m->media_types[i].media_type) || + (MEDIA_TYPE_IEEE_802_11BE == m->media_types[i].media_type)) { uint8_t aux; @@ -2487,6 +2496,8 @@ static void register_tlvs() map_r1_register_tlvs(); map_r2_register_tlvs(); map_r3_register_tlvs(); + map_r4_register_tlvs(); + map_r6_register_tlvs(); tlvs_registered = true; } @@ -2522,12 +2533,6 @@ uint8_t *parse_1905_TLV_from_packet(uint8_t *packet_stream, uint16_t bytes_left) return NULL; } - /* FRV: For EMR3 this check needs to be removed */ - if (len > MAX_TLV_PAYLOAD_SIZE) { - log_i1905_e("malformed packet TLV size is too big size = %d\n", len); - return NULL; - } - if (NULL == (ret = g_tlv_table[tlv_type].parse_cb(p, len))) { log_i1905_e("failed parsing %s", convert_1905_TLV_type_to_string(tlv_type)); } @@ -2573,6 +2578,16 @@ void free_1905_TLV_structure(uint8_t *memory_structure) free(memory_structure); } +void free_1905_TLV_structure2(uint8_t *memory_structure) +{ + register_tlvs(); + + if (NULL == memory_structure) { + return; + } + + g_tlv_table[*memory_structure].free_cb(memory_structure); +} uint8_t compare_1905_TLV_structures(uint8_t *memory_structure_1, uint8_t *memory_structure_2) { @@ -2657,7 +2672,8 @@ uint8_t compare_1905_TLV_structures(uint8_t *memory_structure_1, uint8_t *memory (MEDIA_TYPE_IEEE_802_11AC_5_GHZ == p1->local_interfaces[i].media_type) || (MEDIA_TYPE_IEEE_802_11AD_60_GHZ == p1->local_interfaces[i].media_type) || (MEDIA_TYPE_IEEE_802_11AF == p1->local_interfaces[i].media_type) || - (MEDIA_TYPE_IEEE_802_11AX == p1->local_interfaces[i].media_type)) { + (MEDIA_TYPE_IEEE_802_11AX == p1->local_interfaces[i].media_type) || + (MEDIA_TYPE_IEEE_802_11BE == p1->local_interfaces[i].media_type)) { if (maccmp(p1->local_interfaces[i].media_specific_data.ieee80211.network_membership, p2->local_interfaces[i].media_specific_data.ieee80211.network_membership) !=0 || p1->local_interfaces[i].media_specific_data.ieee80211.role != p2->local_interfaces[i].media_specific_data.ieee80211.role || p1->local_interfaces[i].media_specific_data.ieee80211.ap_channel_band != p2->local_interfaces[i].media_specific_data.ieee80211.ap_channel_band || @@ -2887,7 +2903,8 @@ uint8_t compare_1905_TLV_structures(uint8_t *memory_structure_1, uint8_t *memory (MEDIA_TYPE_IEEE_802_11AC_5_GHZ == p1->media_types[i].media_type) || (MEDIA_TYPE_IEEE_802_11AD_60_GHZ == p1->media_types[i].media_type) || (MEDIA_TYPE_IEEE_802_11AF == p1->media_types[i].media_type) || - (MEDIA_TYPE_IEEE_802_11AX == p1->media_types[i].media_type)) { + (MEDIA_TYPE_IEEE_802_11AX == p1->media_types[i].media_type) || + (MEDIA_TYPE_IEEE_802_11BE == p1->media_types[i].media_type)) { if (maccmp(p1->media_types[i].media_specific_data.ieee80211.network_membership, p2->media_types[i].media_specific_data.ieee80211.network_membership) !=0 || p1->media_types[i].media_specific_data.ieee80211.role != p2->media_types[i].media_specific_data.ieee80211.role || p1->media_types[i].media_specific_data.ieee80211.ap_channel_band != p2->media_types[i].media_specific_data.ieee80211.ap_channel_band || @@ -3250,7 +3267,8 @@ void visit_1905_TLV_structure(uint8_t *memory_structure, void (*callback)(void ( (MEDIA_TYPE_IEEE_802_11AC_5_GHZ == p->local_interfaces[i].media_type) || (MEDIA_TYPE_IEEE_802_11AD_60_GHZ == p->local_interfaces[i].media_type) || (MEDIA_TYPE_IEEE_802_11AF == p->local_interfaces[i].media_type) || - (MEDIA_TYPE_IEEE_802_11AX == p->local_interfaces[i].media_type)) { + (MEDIA_TYPE_IEEE_802_11AX == p->local_interfaces[i].media_type) || + (MEDIA_TYPE_IEEE_802_11BE == p->local_interfaces[i].media_type)) { callback(write_function, new_prefix, sizeof(p->local_interfaces[i].media_specific_data.ieee80211.network_membership), "network_membership", "0x%02x", p->local_interfaces[i].media_specific_data.ieee80211.network_membership); callback(write_function, new_prefix, sizeof(p->local_interfaces[i].media_specific_data.ieee80211.role), "role", "%d", &p->local_interfaces[i].media_specific_data.ieee80211.role); callback(write_function, new_prefix, sizeof(p->local_interfaces[i].media_specific_data.ieee80211.ap_channel_band), "ap_channel_band", "%d", &p->local_interfaces[i].media_specific_data.ieee80211.ap_channel_band); @@ -3440,7 +3458,8 @@ void visit_1905_TLV_structure(uint8_t *memory_structure, void (*callback)(void ( (MEDIA_TYPE_IEEE_802_11AC_5_GHZ == p->media_types[i].media_type) || (MEDIA_TYPE_IEEE_802_11AD_60_GHZ == p->media_types[i].media_type) || (MEDIA_TYPE_IEEE_802_11AF == p->media_types[i].media_type) || - (MEDIA_TYPE_IEEE_802_11AX == p->media_types[i].media_type)) { + (MEDIA_TYPE_IEEE_802_11AX == p->media_types[i].media_type) || + (MEDIA_TYPE_IEEE_802_11BE == p->media_types[i].media_type)) { callback(write_function, new_prefix, sizeof(p->media_types[i].media_specific_data.ieee80211.network_membership), "network_membership", "0x%02x", p->media_types[i].media_specific_data.ieee80211.network_membership); callback(write_function, new_prefix, sizeof(p->media_types[i].media_specific_data.ieee80211.role), "role", "%d", &p->media_types[i].media_specific_data.ieee80211.role); callback(write_function, new_prefix, sizeof(p->media_types[i].media_specific_data.ieee80211.ap_channel_band), "ap_channel_band", "%d", &p->media_types[i].media_specific_data.ieee80211.ap_channel_band); diff --git a/source/ieee1905/src/factory/src_independent/extensions/map/map_r1_tlvs.c b/source/ieee1905/src/factory/src_independent/extensions/map/map_r1_tlvs.c index 1f8a65f..302d6d0 100644 --- a/source/ieee1905/src/factory/src_independent/extensions/map/map_r1_tlvs.c +++ b/source/ieee1905/src/factory/src_independent/extensions/map/map_r1_tlvs.c @@ -395,7 +395,10 @@ static uint8_t* forge_ap_cap_tlv(void *memory_structure, uint16_t *len) /*####################################################################### # AP radio basic capabilities TLV ("Section 17.2.7") # ########################################################################*/ -TLV_FREE_FUNCTION(ap_radio_basic_cap) {} +TLV_FREE_FUNCTION(ap_radio_basic_cap) +{ + SFREE(m->op_classes); +} static uint8_t* parse_ap_radio_basic_cap_tlv(uint8_t *packet_stream, uint16_t len) { @@ -412,17 +415,22 @@ static uint8_t* parse_ap_radio_basic_cap_tlv(uint8_t *packet_stream, uint16_t le _EnB(&p, ret->radio_id, 6); _E1B(&p, &ret->max_bss); _E1B(&p, &ret->op_classes_nr); - PARSE_LIMIT(ret->op_classes_nr, MAX_OP_CLASS); - for (i = 0; i < ret->op_classes_nr; i++) { - _E1B(&p, &ret->op_classes[i].op_class); - _E1B(&p, &ret->op_classes[i].eirp); - _E1B(&p, &channels_nr); - PARSE_LIMIT(channels_nr, MAX_CHANNEL_PER_OP_CLASS); + if (ret->op_classes_nr > 0) { + ret->op_classes = calloc(ret->op_classes_nr, sizeof(*ret->op_classes)); + if (ret->op_classes == NULL) { + PARSE_FREE_RET_RETURN(ap_radio_basic_cap) + } + + for (i = 0; i < ret->op_classes_nr; i++) { + _E1B(&p, &ret->op_classes[i].op_class); + _E1B(&p, &ret->op_classes[i].eirp); + _E1B(&p, &channels_nr); - for (j = 0; j < channels_nr; j++) { - _E1B(&p, &channel); - map_cs_set(&ret->op_classes[i].channels, channel); + for (j = 0; j < channels_nr; j++) { + _E1B(&p, &channel); + map_cs_set(&ret->op_classes[i].channels, channel); + } } } @@ -816,7 +824,10 @@ static uint8_t* forge_metric_reporting_policy_tlv(void *memory_structure, uint16 /*####################################################################### # Channel preference TLV ("Section 17.2.13") # ########################################################################*/ -TLV_FREE_FUNCTION(channel_preference) {} +TLV_FREE_FUNCTION(channel_preference) +{ + SFREE(m->op_classes); +} static uint8_t* parse_channel_preference_tlv(uint8_t *packet_stream, uint16_t len) { @@ -832,21 +843,26 @@ static uint8_t* parse_channel_preference_tlv(uint8_t *packet_stream, uint16_t le _EnB(&p, ret->radio_id, 6); _E1B(&p, &ret->op_classes_nr); - PARSE_LIMIT(ret->op_classes_nr, MAX_OP_CLASS); - - for (i = 0; i < ret->op_classes_nr; i++) { - _E1B(&p, &ret->op_classes[i].op_class); - _E1B(&p, &channels_nr); - PARSE_LIMIT(channels_nr, MAX_CHANNEL_PER_OP_CLASS); - for (j = 0; j < channels_nr; j++) { - _E1B(&p, &channel); - map_cs_set(&ret->op_classes[i].channels, channel); + if (ret->op_classes_nr > 0) { + ret->op_classes = calloc(ret->op_classes_nr, sizeof(*ret->op_classes)); + if (ret->op_classes == NULL) { + PARSE_FREE_RET_RETURN(channel_preference) } - _E1B(&p, &byte); - ret->op_classes[i].pref = (byte & (BIT_MASK_7 | BIT_MASK_6 | BIT_MASK_5 | BIT_MASK_4)) >> BIT_SHIFT_4; - ret->op_classes[i].reason = (byte & (BIT_MASK_3 | BIT_MASK_2 | BIT_MASK_1 | BIT_MASK_0)); + for (i = 0; i < ret->op_classes_nr; i++) { + _E1B(&p, &ret->op_classes[i].op_class); + _E1B(&p, &channels_nr); + + for (j = 0; j < channels_nr; j++) { + _E1B(&p, &channel); + map_cs_set(&ret->op_classes[i].channels, channel); + } + + _E1B(&p, &byte); + ret->op_classes[i].pref = (byte & (BIT_MASK_7 | BIT_MASK_6 | BIT_MASK_5 | BIT_MASK_4)) >> BIT_SHIFT_4; + ret->op_classes[i].reason = (byte & (BIT_MASK_3 | BIT_MASK_2 | BIT_MASK_1 | BIT_MASK_0)); + } } PARSE_CHECK_INTEGRITY(channel_preference) @@ -891,7 +907,18 @@ static uint8_t* forge_channel_preference_tlv(void *memory_structure, uint16_t *l /*####################################################################### # Radio operation restriction TLV ("Section 17.2.14") # ########################################################################*/ -TLV_FREE_FUNCTION(radio_operation_restriction) {} +TLV_FREE_FUNCTION(radio_operation_restriction) +{ + uint8_t i; + + if (m->op_classes) { + for (i = 0; i < m->op_classes_nr; i++) { + SFREE(m->op_classes[i].channels); + } + + SFREE(m->op_classes); + } +} static uint8_t* parse_radio_operation_restriction_tlv(uint8_t *packet_stream, uint16_t len) { @@ -906,16 +933,28 @@ static uint8_t* parse_radio_operation_restriction_tlv(uint8_t *packet_stream, ui _EnB(&p, ret->radio_id, 6); _E1B(&p, &ret->op_classes_nr); - PARSE_LIMIT(ret->op_classes_nr, MAX_OP_CLASS); - for (i = 0; i < ret->op_classes_nr; i++) { - _E1B(&p, &ret->op_classes[i].op_class); - _E1B(&p, &ret->op_classes[i].channels_nr); - PARSE_LIMIT(ret->op_classes[i].channels_nr, MAX_CHANNEL_PER_OP_CLASS); + if (ret->op_classes_nr > 0) { + ret->op_classes = calloc(ret->op_classes_nr, sizeof(*ret->op_classes)); + if (ret->op_classes == NULL) { + PARSE_FREE_RET_RETURN(radio_operation_restriction) + } + + for (i = 0; i < ret->op_classes_nr; i++) { + _E1B(&p, &ret->op_classes[i].op_class); + _E1B(&p, &ret->op_classes[i].channels_nr); + + if (ret->op_classes[i].channels_nr > 0) { + ret->op_classes[i].channels = calloc(ret->op_classes[i].channels_nr, sizeof(*ret->op_classes[i].channels)); + if (ret->op_classes[i].channels == NULL) { + PARSE_FREE_RET_RETURN(radio_operation_restriction) + } - for(j = 0; j < ret->op_classes[i].channels_nr; j++) { - _E1B(&p,&ret->op_classes[i].channels[j].channel); - _E1B(&p,&ret->op_classes[i].channels[j].freq_restriction); + for (j = 0; j < ret->op_classes[i].channels_nr; j++) { + _E1B(&p,&ret->op_classes[i].channels[j].channel); + _E1B(&p,&ret->op_classes[i].channels[j].freq_restriction); + } + } } } @@ -1036,7 +1075,10 @@ static uint8_t* forge_channel_selection_response_tlv(void *memory_structure, uin /*####################################################################### # Operating channel report TLV ("Section 17.2.17") # ########################################################################*/ -TLV_FREE_FUNCTION(operating_channel_report) {} +TLV_FREE_FUNCTION(operating_channel_report) +{ + SFREE(m->op_classes); +} static uint8_t* parse_operating_channel_report_tlv(uint8_t *packet_stream, uint16_t len) { @@ -1050,11 +1092,17 @@ static uint8_t* parse_operating_channel_report_tlv(uint8_t *packet_stream, uint1 ret->tlv_type = TLV_TYPE_OPERATING_CHANNEL_REPORT; _EnB(&p, ret->radio_id, 6); _E1B(&p, &ret->op_classes_nr); - PARSE_LIMIT(ret->op_classes_nr, MAX_OP_CLASS); - for (i = 0; i < ret->op_classes_nr; i++) { - _E1B(&p, &ret->op_classes[i].op_class); - _E1B(&p, &ret->op_classes[i].channel); + if (ret->op_classes_nr > 0) { + ret->op_classes = calloc(ret->op_classes_nr, sizeof(*ret->op_classes)); + if (ret->op_classes == NULL) { + PARSE_FREE_RET_RETURN(operating_channel_report) + } + + for (i = 0; i < ret->op_classes_nr; i++) { + _E1B(&p, &ret->op_classes[i].op_class); + _E1B(&p, &ret->op_classes[i].channel); + } } _E1B(&p, &ret->transmit_power_eirp); @@ -1393,7 +1441,7 @@ static uint8_t* parse_assoc_sta_link_metrics_tlv(uint8_t *packet_stream, uint16_ _E4B(&p, &ret->bsss[i].report_time_interval); _E4B(&p, &ret->bsss[i].downlink_data_rate); _E4B(&p, &ret->bsss[i].uplink_data_rate); - _E1B(&p, &ret->bsss[i].uplink_rssi); + _E1B(&p, &ret->bsss[i].uplink_rcpi); } PARSE_CHECK_INTEGRITY(assoc_sta_link_metrics) @@ -1418,7 +1466,7 @@ static uint8_t* forge_assoc_sta_link_metrics_tlv(void *memory_structure, uint16_ _I4B(&m->bsss[i].report_time_interval, &p); _I4B(&m->bsss[i].downlink_data_rate, &p); _I4B(&m->bsss[i].uplink_data_rate, &p); - _I1B(&m->bsss[i].uplink_rssi, &p); + _I1B(&m->bsss[i].uplink_rcpi, &p); } FORGE_RETURN @@ -1431,8 +1479,12 @@ TLV_FREE_FUNCTION(unassoc_sta_link_metrics_query) { uint8_t i; - for(i = 0; i < m->channels_nr; i++){ - SFREE(m->channels[i].sta_macs); + if (m->channels) { + for (i = 0; i < m->channels_nr; i++){ + SFREE(m->channels[i].sta_macs); + } + + SFREE(m->channels); } } @@ -1449,15 +1501,25 @@ static uint8_t* parse_unassoc_sta_link_metrics_query_tlv(uint8_t *packet_stream, _E1B(&p, &ret->op_class); _E1B(&p, &ret->channels_nr); - PARSE_LIMIT(ret->channels_nr, MAX_CHANNEL_PER_OP_CLASS); - - for(i = 0; i < ret->channels_nr; i++){ - _E1B(&p, &ret->channels[i].channel); - _E1B(&p, &ret->channels[i].sta_macs_nr); - ret->channels[i].sta_macs = calloc(ret->channels[i].sta_macs_nr, sizeof(*ret->channels[i].sta_macs)); - if (ret->channels[i].sta_macs) { - for(j = 0; j < ret->channels[i].sta_macs_nr; j++) { - _EnB(&p, ret->channels[i].sta_macs[j], 6); + + if (ret->channels_nr > 0) { + ret->channels = calloc(ret->channels_nr, sizeof(*ret->channels)); + if (ret->channels == NULL) { + PARSE_FREE_RET_RETURN(unassoc_sta_link_metrics_query) + } + + for (i = 0; i < ret->channels_nr; i++){ + _E1B(&p, &ret->channels[i].channel); + _E1B(&p, &ret->channels[i].sta_macs_nr); + ret->channels[i].sta_macs = calloc(ret->channels[i].sta_macs_nr, sizeof(*ret->channels[i].sta_macs)); + if (ret->channels[i].sta_macs == NULL) { + PARSE_FREE_RET_RETURN(unassoc_sta_link_metrics_query) + } + + if (ret->channels[i].sta_macs) { + for(j = 0; j < ret->channels[i].sta_macs_nr; j++) { + _EnB(&p, ret->channels[i].sta_macs[j], 6); + } } } } @@ -1559,7 +1621,10 @@ static uint8_t* forge_unassoc_sta_link_metrics_response_tlv(void *memory_structu /*####################################################################### # Beacon metrics query TLV ("Section 17.2.27") # ########################################################################*/ -TLV_FREE_FUNCTION(beacon_metrics_query) {} +TLV_FREE_FUNCTION(beacon_metrics_query) +{ + SFREE(m->ap_channel_reports); +} static uint8_t* parse_beacon_metrics_query_tlv(uint8_t *packet_stream, uint16_t len) { @@ -1583,19 +1648,24 @@ static uint8_t* parse_beacon_metrics_query_tlv(uint8_t *packet_stream, uint16_t _EnB(&p, ret->ssid, ret->ssid_len); _E1B(&p, &ret->ap_channel_reports_nr); - PARSE_LIMIT(ret->ap_channel_reports_nr, MAX_OP_CLASS); - for(i = 0; i < ret->ap_channel_reports_nr; i++) { - _E1B(&p, &op_class_channels_nr); - if (op_class_channels_nr == 0) { - free(ret); - return NULL; + if (ret->ap_channel_reports_nr > 0) { + ret->ap_channel_reports = calloc(ret->ap_channel_reports_nr, sizeof(*ret->ap_channel_reports)); + if (ret->ap_channel_reports == NULL) { + PARSE_FREE_RET_RETURN(beacon_metrics_query) } - _E1B(&p, &ret->ap_channel_reports[i].op_class); - /* Copy channel list - length includes the operating_class byte */ - for (j = 0; j < op_class_channels_nr - 1; j++) { - _E1B(&p, &channel); - map_cs_set(&ret->ap_channel_reports[i].channels, channel); + + for (i = 0; i < ret->ap_channel_reports_nr; i++) { + _E1B(&p, &op_class_channels_nr); + if (op_class_channels_nr == 0) { + PARSE_FREE_RET_RETURN(beacon_metrics_query) + } + _E1B(&p, &ret->ap_channel_reports[i].op_class); + /* Copy channel list - length includes the operating_class byte */ + for (j = 0; j < op_class_channels_nr - 1; j++) { + _E1B(&p, &channel); + map_cs_set(&ret->ap_channel_reports[i].channels, channel); + } } } @@ -1701,8 +1771,7 @@ static uint8_t* parse_beacon_metrics_response_tlv(uint8_t *packet_stream, uint16 ret->elements[i].subelements = calloc(1, subelem_len); if (ret->elements[i].subelements == NULL) { - free(ret); - return NULL; + PARSE_FREE_RET_RETURN(beacon_metrics_response) } memcpy(ret->elements[i].subelements, p + copy_len, subelem_len); @@ -2093,13 +2162,13 @@ static uint8_t* parse_assoc_sta_traffic_stats_tlv(uint8_t *packet_stream, uint16 ret->tlv_type = TLV_TYPE_ASSOCIATED_STA_TRAFFIC_STATS; _EnB(&p, ret->sta_mac, 6); - _E4B(&p, &ret->txbytes); - _E4B(&p, &ret->rxbytes); - _E4B(&p, &ret->txpkts); - _E4B(&p, &ret->rxpkts); - _E4B(&p, &ret->txpkterrors); - _E4B(&p, &ret->rxpkterrors); - _E4B(&p, &ret->retransmission_cnt); + _E4B(&p, &ret->tx_bytes); + _E4B(&p, &ret->rx_bytes); + _E4B(&p, &ret->tx_packets); + _E4B(&p, &ret->rx_packets); + _E4B(&p, &ret->tx_packet_errors); + _E4B(&p, &ret->rx_packet_errors); + _E4B(&p, &ret->retransmissions); PARSE_CHECK_INTEGRITY(assoc_sta_traffic_stats) PARSE_RETURN @@ -2113,16 +2182,16 @@ static uint8_t* forge_assoc_sta_traffic_stats_tlv(void *memory_structure, uint16 FORGE_MALLOC_RET - _I1B(&m->tlv_type, &p); - _I2B(&tlv_length, &p); - _InB(m->sta_mac, &p, 6); - _I4B(&m->txbytes, &p); - _I4B(&m->rxbytes, &p); - _I4B(&m->txpkts, &p); - _I4B(&m->rxpkts, &p); - _I4B(&m->txpkterrors, &p); - _I4B(&m->rxpkterrors, &p); - _I4B(&m->retransmission_cnt, &p); + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _InB(m->sta_mac, &p, 6); + _I4B(&m->tx_bytes, &p); + _I4B(&m->rx_bytes, &p); + _I4B(&m->tx_packets, &p); + _I4B(&m->rx_packets, &p); + _I4B(&m->tx_packet_errors, &p); + _I4B(&m->rx_packet_errors, &p); + _I4B(&m->retransmissions, &p); FORGE_RETURN } diff --git a/source/ieee1905/src/factory/src_independent/extensions/map/map_r2_tlvs.c b/source/ieee1905/src/factory/src_independent/extensions/map/map_r2_tlvs.c index 272412c..eaba401 100644 --- a/source/ieee1905/src/factory/src_independent/extensions/map/map_r2_tlvs.c +++ b/source/ieee1905/src/factory/src_independent/extensions/map/map_r2_tlvs.c @@ -126,7 +126,14 @@ static uint8_t* forge_channel_scan_reporting_policy_tlv(void *memory_structure, /*####################################################################### # Channel scan capabilities TLV ("Section 17.2.38") # ########################################################################*/ -TLV_FREE_FUNCTION(channel_scan_cap) {} +TLV_FREE_FUNCTION(channel_scan_cap) +{ + uint8_t i; + + for (i=0; i < m->radios_nr; i++) { + SFREE(m->radios[i].op_classes); + } +} static uint8_t* parse_channel_scan_cap_tlv(uint8_t *packet_stream, uint16_t len) { @@ -151,15 +158,20 @@ static uint8_t* parse_channel_scan_cap_tlv(uint8_t *packet_stream, uint16_t len) _E4B(&p, &ret->radios[i].min_scan_interval); _E1B(&p, &ret->radios[i].op_classes_nr); - PARSE_LIMIT(ret->radios[i].op_classes_nr, MAX_OP_CLASS); - - for (j = 0; j < ret->radios[i].op_classes_nr; j++) { - _E1B(&p, &ret->radios[i].op_classes[j].op_class); - _E1B(&p, &channels_nr); - PARSE_LIMIT(channels_nr, MAX_CHANNEL_PER_OP_CLASS); - for (k = 0; k < channels_nr; k++) { - _E1B(&p, &channel); - map_cs_set(&ret->radios[i].op_classes[j].channels, channel); + + if (ret->radios[i].op_classes_nr > 0) { + ret->radios[i].op_classes = calloc(ret->radios[i].op_classes_nr, sizeof(*ret->radios[i].op_classes)); + if (ret->radios[i].op_classes == NULL) { + PARSE_FREE_RET_RETURN(channel_scan_cap) + } + + for (j = 0; j < ret->radios[i].op_classes_nr; j++) { + _E1B(&p, &ret->radios[i].op_classes[j].op_class); + _E1B(&p, &channels_nr); + for (k = 0; k < channels_nr; k++) { + _E1B(&p, &channel); + map_cs_set(&ret->radios[i].op_classes[j].channels, channel); + } } } } @@ -215,7 +227,14 @@ static uint8_t* forge_channel_scan_cap_tlv(void *memory_structure, uint16_t *len /*####################################################################### # Channel scan request TLV ("Section 17.2.39") # ########################################################################*/ -TLV_FREE_FUNCTION(channel_scan_request) {} +TLV_FREE_FUNCTION(channel_scan_request) +{ + uint8_t i; + + for (i=0; i < m->radios_nr; i++) { + SFREE(m->radios[i].op_classes); + } +} static uint8_t* parse_channel_scan_request_tlv(uint8_t *packet_stream, uint16_t len) { @@ -237,15 +256,20 @@ static uint8_t* parse_channel_scan_request_tlv(uint8_t *packet_stream, uint16_t for (i = 0; i < ret->radios_nr; i++) { _EnB(&p, ret->radios[i].radio_id, 6); _E1B(&p, &ret->radios[i].op_classes_nr); - PARSE_LIMIT(ret->radios[i].op_classes_nr, MAX_OP_CLASS); - - for (j = 0; j < ret->radios[i].op_classes_nr; j++) { - _E1B(&p, &ret->radios[i].op_classes[j].op_class); - _E1B(&p, &channels_nr); - PARSE_LIMIT(channels_nr, MAX_CHANNEL_PER_OP_CLASS); - for (k = 0; k < channels_nr; k++) { - _E1B(&p, &channel); - map_cs_set(&ret->radios[i].op_classes[j].channels, channel); + + if (ret->radios[i].op_classes_nr > 0) { + ret->radios[i].op_classes = calloc(ret->radios[i].op_classes_nr, sizeof(*ret->radios[i].op_classes)); + if (ret->radios[i].op_classes == NULL) { + PARSE_FREE_RET_RETURN(channel_scan_request) + } + + for (j = 0; j < ret->radios[i].op_classes_nr; j++) { + _E1B(&p, &ret->radios[i].op_classes[j].op_class); + _E1B(&p, &channels_nr); + for (k = 0; k < channels_nr; k++) { + _E1B(&p, &channel); + map_cs_set(&ret->radios[i].op_classes[j].channels, channel); + } } } } @@ -762,7 +786,16 @@ static uint8_t* forge_cac_status_report_tlv(void *memory_structure, uint16_t *le /*####################################################################### # CAC capabilities TLV ("Section 17.2.46") # ########################################################################*/ -TLV_FREE_FUNCTION(cac_cap) {} +TLV_FREE_FUNCTION(cac_cap) +{ + uint8_t i, j; + + for (i = 0; i < m->radios_nr; i++) { + for (j = 0; j < m->radios[i].cac_methods_nr; j++) { + SFREE(m->radios[i].cac_methods[j].op_classes); + } + } +} static uint8_t* parse_cac_cap_tlv(uint8_t *packet_stream, uint16_t len) { @@ -785,18 +818,25 @@ static uint8_t* parse_cac_cap_tlv(uint8_t *packet_stream, uint16_t len) _E1B(&p, &ret->radios[i].cac_methods_nr); for (j = 0; j < ret->radios[i].cac_methods_nr; j++) { - _E1B(&p, &ret->radios[i].cac_methods[j].cac_method); - _E3B(&p, &ret->radios[i].cac_methods[j].cac_duration); - _E1B(&p, &ret->radios[i].cac_methods[j].op_classes_nr); - PARSE_LIMIT(ret->radios[i].cac_methods[j].op_classes_nr, MAX_OP_CLASS); + map_cac_cap_tlv_method_t *cac_method = &ret->radios[i].cac_methods[j]; - for (k = 0; k < ret->radios[i].cac_methods[j].op_classes_nr; k++) { - _E1B(&p, &ret->radios[i].cac_methods[j].op_classes[k].op_class); - _E1B(&p, &channels_nr); - PARSE_LIMIT(channels_nr, MAX_CHANNEL_PER_OP_CLASS); - for (l = 0; l < channels_nr; l++) { - _E1B(&p, &channel); - map_cs_set(&ret->radios[i].cac_methods[j].op_classes[k].channels, channel); + _E1B(&p, &cac_method->cac_method); + _E3B(&p, &cac_method->cac_duration); + _E1B(&p, &cac_method->op_classes_nr); + + if (cac_method->op_classes_nr > 0) { + cac_method->op_classes = calloc(cac_method->op_classes_nr, sizeof(*cac_method->op_classes)); + if (cac_method->op_classes == NULL) { + PARSE_FREE_RET_RETURN(cac_cap) + } + + for (k = 0; k < cac_method->op_classes_nr; k++) { + _E1B(&p, &cac_method->op_classes[k].op_class); + _E1B(&p, &channels_nr); + for (l = 0; l < channels_nr; l++) { + _E1B(&p, &channel); + map_cs_set(&cac_method->op_classes[k].channels, channel); + } } } } @@ -1463,12 +1503,12 @@ static uint8_t* parse_ap_ext_metrics_tlv(uint8_t *packet_stream, uint16_t len) ret->tlv_type = TLV_TYPE_AP_EXTENDED_METRICS; _EnB(&p, &ret->bssid, 6); - _E4B(&p, &ret->ucast_bytes_tx); - _E4B(&p, &ret->ucast_bytes_rx); - _E4B(&p, &ret->mcast_bytes_tx); - _E4B(&p, &ret->mcast_bytes_rx); - _E4B(&p, &ret->bcast_bytes_tx); - _E4B(&p, &ret->bcast_bytes_rx); + _E4B(&p, &ret->tx_ucast_bytes); + _E4B(&p, &ret->rx_ucast_bytes); + _E4B(&p, &ret->tx_mcast_bytes); + _E4B(&p, &ret->rx_mcast_bytes); + _E4B(&p, &ret->tx_bcast_bytes); + _E4B(&p, &ret->rx_bcast_bytes); PARSE_CHECK_INTEGRITY(ap_ext_metrics) PARSE_RETURN @@ -1485,12 +1525,12 @@ static uint8_t* forge_ap_ext_metrics_tlv(void *memory_structure, uint16_t *len) _I1B(&m->tlv_type, &p); _I2B(&tlv_length, &p); _InB(&m->bssid, &p, 6); - _I4B(&m->ucast_bytes_tx, &p); - _I4B(&m->ucast_bytes_rx, &p); - _I4B(&m->mcast_bytes_tx, &p); - _I4B(&m->mcast_bytes_rx, &p); - _I4B(&m->bcast_bytes_tx, &p); - _I4B(&m->bcast_bytes_rx, &p); + _I4B(&m->tx_ucast_bytes, &p); + _I4B(&m->rx_ucast_bytes, &p); + _I4B(&m->tx_mcast_bytes, &p); + _I4B(&m->rx_mcast_bytes, &p); + _I4B(&m->tx_bcast_bytes, &p); + _I4B(&m->rx_bcast_bytes, &p); FORGE_RETURN } diff --git a/source/ieee1905/src/factory/src_independent/extensions/map/map_r3_tlvs.c b/source/ieee1905/src/factory/src_independent/extensions/map/map_r3_tlvs.c index fcac00f..69147df 100644 --- a/source/ieee1905/src/factory/src_independent/extensions/map/map_r3_tlvs.c +++ b/source/ieee1905/src/factory/src_independent/extensions/map/map_r3_tlvs.c @@ -84,7 +84,121 @@ # TLV HANDLERS # ########################################################################*/ /*####################################################################### -# Encrypted Payload TLV associated structures ("Section 17.2.69") # +# 1905 Layer Security Capability TLV ("Section 17.2.67") # +########################################################################*/ +TLV_FREE_FUNCTION(1905_security_cap) {} + +static uint8_t* parse_1905_security_cap_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_1905_security_cap_tlv_t *ret; + uint8_t *p = packet_stream; + + PARSE_CHECK_EXP_LEN(3); + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_1905_LAYER_SECURITY_CAPABILITY; + + _E1B(&p, &ret->onboarding_protocol); + _E1B(&p, &ret->mic_algorithm); + _E1B(&p, &ret->encryption_algorithm); + + PARSE_CHECK_INTEGRITY(1905_security_cap) + PARSE_RETURN +} + +static uint8_t* forge_1905_security_cap_tlv(void *memory_structure, uint16_t *len) +{ + map_1905_security_cap_tlv_t *m = memory_structure; + uint16_t tlv_length = 3; + uint8_t *ret, *p; + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + + _I1B(&m->onboarding_protocol, &p); + _I1B(&m->mic_algorithm, &p); + _I1B(&m->encryption_algorithm, &p); + + FORGE_RETURN +} + +/*####################################################################### +# MIC TLV ("Section 17.2.68") # +########################################################################*/ +TLV_FREE_FUNCTION(mic) +{ + SFREE(m->mic); +} + +static uint8_t *parse_mic_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_mic_tlv_t *ret; + uint8_t *p = packet_stream; + uint8_t byte; + + PARSE_CHECK_MIN_LEN(1 + INTEGRITY_TX_COUNTER_LEN + ETHER_ADDR_LEN + 2 + 32); /* 32 is SHA256 MAC Length */ + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_MIC; + + /** + * bits 7-6: 1905 GTK Key Id + * bits 5-4: MIC Version + * bits 3-0: Reserved + */ + _E1B(&p, &byte); + ret->gtk_key_id = (byte & (BIT_MASK_7 | BIT_MASK_6)) >> BIT_SHIFT_6; + ret->mic_version = (byte & (BIT_MASK_5 | BIT_MASK_4)) >> BIT_SHIFT_4; + ret->reserved = byte & (BIT_MASK_3 | BIT_MASK_2 | BIT_MASK_1 | BIT_MASK_0); + + _EnB(&p, ret->integrity_tx_counter, INTEGRITY_TX_COUNTER_LEN); + _EnB(&p, ret->src_al_mac, ETHER_ADDR_LEN); + _E2B(&p, &ret->mic_len); + + if (ret->mic_len > 0) { + ret->mic = calloc(1, (ret->mic_len * sizeof(uint8_t))); + if (NULL == ret->mic) { + free(ret); + return NULL; + } + _EnB(&p, ret->mic, ret->mic_len); + } + + PARSE_CHECK_INTEGRITY(mic) + PARSE_RETURN +} + +static uint8_t *forge_mic_tlv(void *memory_structure, uint16_t *len) +{ + map_mic_tlv_t *m = (map_mic_tlv_t *)memory_structure; + uint8_t *ret, *p, byte; + + uint16_t tlv_length = 1 + INTEGRITY_TX_COUNTER_LEN + ETHER_ADDR_LEN + 2 + m->mic_len; + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + + byte = ((m->gtk_key_id << BIT_SHIFT_6) | + (m->mic_version << BIT_SHIFT_4) | + m->reserved); + _I1B(&byte, &p); + + _InB(m->integrity_tx_counter, &p, INTEGRITY_TX_COUNTER_LEN); + _InB(m->src_al_mac, &p, ETHER_ADDR_LEN); + _I2B(&m->mic_len, &p); + _InB(m->mic, &p, m->mic_len); + + FORGE_RETURN +} + +/*####################################################################### +# Encrypted Payload TLV ("Section 17.2.69") # ########################################################################*/ TLV_FREE_FUNCTION(encrypted_payload) { SFREE(m->siv_output); @@ -147,7 +261,7 @@ static uint8_t *forge_encrypted_payload_tlv(void *memory_structure, uint16_t *le } /*####################################################################### -# AP Wi-Fi 6 Capabilities associated structures ("Section 17.2.72") # +# AP Wi-Fi 6 Capabilities TLV ("Section 17.2.72") # ########################################################################*/ TLV_FREE_FUNCTION(ap_wifi6_cap) {} @@ -359,6 +473,152 @@ static uint8_t* forge_bssid_tlv(void *memory_structure, uint16_t *len) FORGE_RETURN } +/*####################################################################### +# BSS Configuration Report TLV ("Section 17.2.75") # +########################################################################*/ +TLV_FREE_FUNCTION(bss_configuration_report) {} + +static uint8_t *parse_bss_configuration_report_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_bss_configuration_report_tlv_t *ret; + uint8_t *p = packet_stream; + uint8_t byte; + int i, j; + + PARSE_CHECK_MIN_LEN(1); + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_BSS_CONFIGURATION_REPORT; + + _E1B(&p, &ret->radios_nr); + PARSE_LIMIT(ret->radios_nr, MAX_RADIO_PER_AGENT); + for (i = 0; i < ret->radios_nr; i++) { + _EnB(&p, ret->radios[i].ruid, ETHER_ADDR_LEN); + _E1B(&p, &ret->radios[i].bss_nr); + + PARSE_LIMIT(ret->radios[i].bss_nr, MAX_BSS_PER_RADIO); + for (j = 0; j < ret->radios[i].bss_nr; j++) { + _EnB(&p, ret->radios[i].bss[j].bssid, ETHER_ADDR_LEN); + _E1B(&p, &byte); + ret->radios[i].bss[j].backhaul_bss = (byte & BIT_MASK_7) ? SET_BIT : RESET_BIT; + ret->radios[i].bss[j].fronthaul_bss = (byte & BIT_MASK_6) ? SET_BIT : RESET_BIT; + ret->radios[i].bss[j].r1_disallowed_status = (byte & BIT_MASK_5) ? SET_BIT : RESET_BIT; + ret->radios[i].bss[j].r2_disallowed_status = (byte & BIT_MASK_4) ? SET_BIT : RESET_BIT; + ret->radios[i].bss[j].multiple_bssid = (byte & BIT_MASK_3) ? SET_BIT : RESET_BIT; + ret->radios[i].bss[j].transmitted_bssid = (byte & BIT_MASK_2) ? SET_BIT : RESET_BIT; + ret->radios[i].bss[j].reserved1 = byte & (BIT_MASK_1 | BIT_MASK_0); + _E1B(&p, &ret->radios[i].bss[j].reserved2); + _E1B(&p, &ret->radios[i].bss[j].ssid_len); + PARSE_LIMIT(ret->radios[i].bss[j].ssid_len, MAX_SSID_LEN); + _EnB(&p, ret->radios[i].bss[j].ssid, ret->radios[i].bss[j].ssid_len); + } + } + + PARSE_CHECK_INTEGRITY(bss_configuration_report) + PARSE_RETURN +} + +static uint8_t *forge_bss_configuration_report_tlv(void *memory_structure, uint16_t *len) +{ + uint16_t tlv_length = 1; + uint8_t *ret, *p; + map_bss_configuration_report_tlv_t *m = memory_structure; + int i, j; + + if (m->radios_nr > 0 ) { + for (i = 0; i < m->radios_nr; i++) { + tlv_length += ETHER_ADDR_LEN; + if (m->radios[i].bss_nr > 0) { + tlv_length += 1; + for (j = 0; j < m->radios[i].bss_nr; j++) { + tlv_length += ETHER_ADDR_LEN + 1 + 1 + 1 + m->radios[i].bss[j].ssid_len; + } + } + } + } + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _I1B(&m->radios_nr, &p); + for (i = 0; i < m->radios_nr; i++) { + _InB(m->radios[i].ruid, &p, ETHER_ADDR_LEN); + _I1B(&m->radios[i].bss_nr, &p); + for (j = 0; j < m->radios[i].bss_nr; j++) { + _InB(m->radios[i].bss[j].bssid, &p, ETHER_ADDR_LEN); + + uint8_t byte = ((m->radios[i].bss[j].backhaul_bss << BIT_SHIFT_7) | + (m->radios[i].bss[j].fronthaul_bss << BIT_SHIFT_6) | + (m->radios[i].bss[j].r1_disallowed_status << BIT_SHIFT_5) | + (m->radios[i].bss[j].r2_disallowed_status << BIT_SHIFT_4) | + (m->radios[i].bss[j].multiple_bssid << BIT_SHIFT_3) | + (m->radios[i].bss[j].transmitted_bssid << BIT_SHIFT_2) | + m->radios[i].bss[j].reserved1); + _I1B(&byte, &p); + _I1B(&m->radios[i].bss[j].reserved2, &p); + _I1B(&m->radios[i].bss[j].ssid_len, &p); + _InB(m->radios[i].bss[j].ssid, &p, m->radios[i].bss[j].ssid_len); + } + } + + FORGE_RETURN +} + +/*####################################################################### +# Agent List TLV ("Section 17.2.77") # +########################################################################*/ +TLV_FREE_FUNCTION(agent_list) {} + +static uint8_t *parse_agent_list_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_agent_list_tlv_t *ret; + uint8_t *p = packet_stream; + int i; + + PARSE_CHECK_MIN_LEN(1); + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_AGENT_LIST; + + _E1B(&p, &ret->agent_nr); + for (i = 0; i < ret->agent_nr; i++) { + _EnB(&p, ret->entries[i].al_mac, ETHER_ADDR_LEN); + _E1B(&p, &ret->entries[i].map_profile); + _E1B(&p, &ret->entries[i].security); + } + + PARSE_CHECK_INTEGRITY(agent_list) + PARSE_RETURN +} + +static uint8_t *forge_agent_list_tlv(void *memory_structure, uint16_t *len) +{ + uint16_t tlv_length = 1; + uint8_t *ret, *p; + map_agent_list_tlv_t *m = memory_structure; + int i; + + if (m->agent_nr > 0 ) { + tlv_length += m->agent_nr * (ETHER_ADDR_LEN + 1 + 1); + } + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _I1B(&m->agent_nr, &p); + for (i = 0; i < m->agent_nr; i++) { + _InB(m->entries[i].al_mac, &p, ETHER_ADDR_LEN); + _I1B(&m->entries[i].map_profile, &p); + _I1B(&m->entries[i].security, &p); + } + + FORGE_RETURN +} + /*####################################################################### # 1905 Encap DPP TLV ("Section 17.2.79") # ########################################################################*/ @@ -593,6 +853,48 @@ static uint8_t* forge_1905_encap_eapol_tlv(void *memory_structure, uint16_t *len FORGE_RETURN } +/*####################################################################### +# BSS Configuration Request TLV ("Section 17.2.84") # +########################################################################*/ +TLV_FREE_FUNCTION(bss_configuration_request) {} + +static uint8_t* parse_bss_configuration_request_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_bss_configuration_request_tlv_t *ret; + uint8_t *p = packet_stream; + + PARSE_CHECK_MIN_LEN(1) + + /* Allocate struct and frame_body */ + ret = calloc(1, sizeof(*ret) + len); + if (NULL == ret) { + return NULL; + } + + ret->tlv_type = TLV_TYPE_BSS_CONFIGURATION_REQUEST; + ret->obj_len = len; + ret->obj = (uint8_t *)(ret + 1); + _EnB(&p, ret->obj, ret->obj_len); + + PARSE_CHECK_INTEGRITY(bss_configuration_request) + PARSE_RETURN +} + +static uint8_t* forge_bss_configuration_request_tlv(void *memory_structure, uint16_t *len) +{ + map_bss_configuration_request_tlv_t *m = memory_structure; + uint16_t tlv_length = m->obj_len; + uint8_t *ret, *p; + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _InB(m->obj, &p, m->obj_len); + + FORGE_RETURN +} + /*####################################################################### # DPP Message TLV ("Section 17.2.86") # ########################################################################*/ @@ -752,19 +1054,113 @@ static uint8_t* forge_device_inventory_tlv(void *memory_structure, uint16_t *len FORGE_RETURN } +/*####################################################################### +# AKM Suite Capabilities TLV ("Section 17.2.78") # +########################################################################*/ +TLV_FREE_FUNCTION(akm_suite_cap) { + SFREE(m->bh_akm_suites); + SFREE(m->fh_akm_suites); +} + +static uint8_t* parse_akm_suite_cap_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_akm_suite_cap_tlv_t *ret; + uint8_t *p = packet_stream, i; + + /* Min TLV len: bh_akm_suites_nr (1) + fh_akm_suites_nr (1) */ + PARSE_CHECK_MIN_LEN(2) + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_AKM_SUITE_CAPABILITIES; + + _E1B(&p, &ret->bh_akm_suites_nr); + if (ret->bh_akm_suites_nr > 0) { + ret->bh_akm_suites = calloc(ret->bh_akm_suites_nr, sizeof(*ret->bh_akm_suites)); + if (ret->bh_akm_suites == NULL) { + PARSE_FREE_RET_RETURN(akm_suite_cap) + } + + for (i = 0; i < ret->bh_akm_suites_nr; i++) { + _E1B(&p, &ret->bh_akm_suites[i].oui[0]); + _E1B(&p, &ret->bh_akm_suites[i].oui[1]); + _E1B(&p, &ret->bh_akm_suites[i].oui[2]); + _E1B(&p, &ret->bh_akm_suites[i].akm_suite_type); + } + } + + _E1B(&p, &ret->fh_akm_suites_nr); + if (ret->fh_akm_suites_nr > 0) { + ret->fh_akm_suites = calloc(ret->fh_akm_suites_nr, sizeof(*ret->fh_akm_suites)); + if (ret->fh_akm_suites == NULL) { + PARSE_FREE_RET_RETURN(akm_suite_cap) + } + + for (i = 0; i < ret->fh_akm_suites_nr; i++) { + _E1B(&p, &ret->fh_akm_suites[i].oui[0]); + _E1B(&p, &ret->fh_akm_suites[i].oui[1]); + _E1B(&p, &ret->fh_akm_suites[i].oui[2]); + _E1B(&p, &ret->fh_akm_suites[i].akm_suite_type); + } + } + + PARSE_CHECK_INTEGRITY(akm_suite_cap) + PARSE_RETURN +} + +static uint8_t* forge_akm_suite_cap_tlv(void *memory_structure, uint16_t *len) +{ + map_akm_suite_cap_tlv_t *m = memory_structure; + uint8_t *ret, *p, i; + + /* Calculate TLV length */ + uint16_t tlv_length = 2; /* bh_akm_suites_nr + fh_akm_suites_nr */ + tlv_length += m->bh_akm_suites_nr * sizeof(map_akm_suite_t); + tlv_length += m->fh_akm_suites_nr * sizeof(map_akm_suite_t); + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + + _I1B(&m->bh_akm_suites_nr, &p); + for (i = 0; i < m->bh_akm_suites_nr; i++) { + _I1B(&m->bh_akm_suites[i].oui[0], &p); + _I1B(&m->bh_akm_suites[i].oui[1], &p); + _I1B(&m->bh_akm_suites[i].oui[2], &p); + _I1B(&m->bh_akm_suites[i].akm_suite_type, &p); + } + + _I1B(&m->fh_akm_suites_nr, &p); + for (i = 0; i < m->fh_akm_suites_nr; i++) { + _I1B(&m->fh_akm_suites[i].oui[0], &p); + _I1B(&m->fh_akm_suites[i].oui[1], &p); + _I1B(&m->fh_akm_suites[i].oui[2], &p); + _I1B(&m->fh_akm_suites[i].akm_suite_type, &p); + } + + FORGE_RETURN +} + /*####################################################################### # PUBLIC FUNCTIONS # ########################################################################*/ void map_r3_register_tlvs(void) { - I1905_REGISTER_TLV(TLV_TYPE_ENCRYPTED_PAYLOAD, encrypted_payload ); - I1905_REGISTER_TLV(TLV_TYPE_AP_WIFI6_CAPABILITIES, ap_wifi6_cap ); - I1905_REGISTER_TLV(TLV_TYPE_ASSOCIATED_WIFI6_STA_STATUS_REPORT, assoc_wifi6_sta_status); - I1905_REGISTER_TLV(TLV_TYPE_BSSID, bssid ); - I1905_REGISTER_TLV(TLV_TYPE_1905_ENCAP_DPP, 1905_encap_dpp ); - I1905_REGISTER_TLV(TLV_TYPE_1905_ENCAP_EAPOL, 1905_encap_eapol ); - I1905_REGISTER_TLV(TLV_TYPE_DPP_MESSAGE, dpp_message ); - I1905_REGISTER_TLV(TLV_TYPE_DPP_CCE_INDICATION, dpp_cce_indication ); - I1905_REGISTER_TLV(TLV_TYPE_DPP_CHIRP_VALUE, dpp_chirp_value ); - I1905_REGISTER_TLV(TLV_TYPE_DEVICE_INVENTORY, device_inventory ); + I1905_REGISTER_TLV(TLV_TYPE_1905_LAYER_SECURITY_CAPABILITY, 1905_security_cap ); + I1905_REGISTER_TLV(TLV_TYPE_MIC, mic ); + I1905_REGISTER_TLV(TLV_TYPE_ENCRYPTED_PAYLOAD, encrypted_payload ); + I1905_REGISTER_TLV(TLV_TYPE_AP_WIFI6_CAPABILITIES, ap_wifi6_cap ); + I1905_REGISTER_TLV(TLV_TYPE_ASSOCIATED_WIFI6_STA_STATUS_REPORT, assoc_wifi6_sta_status ); + I1905_REGISTER_TLV(TLV_TYPE_BSSID, bssid ); + I1905_REGISTER_TLV(TLV_TYPE_1905_ENCAP_DPP, 1905_encap_dpp ); + I1905_REGISTER_TLV(TLV_TYPE_1905_ENCAP_EAPOL, 1905_encap_eapol ); + I1905_REGISTER_TLV(TLV_TYPE_DPP_MESSAGE, dpp_message ); + I1905_REGISTER_TLV(TLV_TYPE_DPP_CCE_INDICATION, dpp_cce_indication ); + I1905_REGISTER_TLV(TLV_TYPE_DPP_CHIRP_VALUE, dpp_chirp_value ); + I1905_REGISTER_TLV(TLV_TYPE_DEVICE_INVENTORY, device_inventory ); + I1905_REGISTER_TLV(TLV_TYPE_BSS_CONFIGURATION_REQUEST, bss_configuration_request); + I1905_REGISTER_TLV(TLV_TYPE_BSS_CONFIGURATION_REPORT, bss_configuration_report ); + I1905_REGISTER_TLV(TLV_TYPE_AGENT_LIST, agent_list ); + I1905_REGISTER_TLV(TLV_TYPE_AKM_SUITE_CAPABILITIES, akm_suite_cap ); } diff --git a/source/ieee1905/src/factory/src_independent/extensions/map/map_r4_tlvs.c b/source/ieee1905/src/factory/src_independent/extensions/map/map_r4_tlvs.c new file mode 100644 index 0000000..c12366c --- /dev/null +++ b/source/ieee1905/src/factory/src_independent/extensions/map/map_r4_tlvs.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019-2023 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/************* COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************ +** Copyright (c) [2019] – [Technicolor Delivery Technologies, SAS] +** All Rights Reserved +** The source code form of this Open Source Project components +** is subject to the terms of the BSD-2-Clause-Patent. +** You can redistribute it and/or modify it under the terms of +** the BSD-2-Clause-Patent. (https://opensource.org/licenses/BSDplusPatent) +** See COPYING file/LICENSE file for more details. +****************************************************************************/ + +/* + * Broadband Forum IEEE 1905.1/1a stack + * + * Copyright (c) 2017, Broadband Forum + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Subject to the terms and conditions of this license, each copyright + * holder and contributor hereby grants to those receiving rights under + * this license a perpetual, worldwide, non-exclusive, no-charge, + * royalty-free, irrevocable (except for failure to satisfy the + * conditions of this license) patent license to make, have made, use, + * offer to sell, sell, import, and otherwise transfer this software, + * where such license applies only to those patent claims, already + * acquired or hereafter acquired, licensable by such copyright holder or + * contributor that are necessarily infringed by: + * + * (a) their Contribution(s) (the licensed copyrights of copyright holders + * and non-copyrightable additions of contributors, in source or binary + * form) alone; or + * + * (b) combination of their Contribution(s) with the work of authorship to + * which such Contribution(s) was added by such copyright holder or + * contributor, if, at the time the Contribution is added, such addition + * causes such combination to be necessarily infringed. The patent + * license shall not apply to any other combinations which include the + * Contribution. + * + * Except as expressly stated above, no rights or licenses from any + * copyright holder or contributor is granted under this license, whether + * expressly, by implication, estoppel or otherwise. + * + * DISCLAIMER + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#define TLV_STRUCT_NAME_PREFIX TLV_STRUCT_NAME_PREFIX_MAP + +#include "1905_tlvs.h" +#include "packet_tools.h" +#include "map_tlvs.h" + +/*####################################################################### +# TLV HANDLERS # +########################################################################*/ + +/*####################################################################### +# Controller Capability TLV ("Section 17.2.94") # +########################################################################*/ +TLV_FREE_FUNCTION(controller_capability) {} + +static uint8_t* parse_controller_capability_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_controller_capability_tlv_t *ret; + uint8_t *p = packet_stream; + + PARSE_CHECK_EXP_LEN(1); + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_CONTROLLER_CAPABILITY; + + _E1B(&p, &ret->capability); + + PARSE_CHECK_INTEGRITY(controller_capability) + PARSE_RETURN +} + +static uint8_t* forge_controller_capability_tlv(void *memory_structure, uint16_t *len) +{ + map_controller_capability_tlv_t *m = memory_structure; + uint16_t tlv_length = 1; + uint8_t *ret, *p; + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _I1B(&m->capability, &p); + + FORGE_RETURN +} + +/*####################################################################### +# PUBLIC FUNCTIONS # +########################################################################*/ +void map_r4_register_tlvs(void) +{ + I1905_REGISTER_TLV(TLV_TYPE_CONTROLLER_CAPABILITY, controller_capability ); +} diff --git a/source/ieee1905/src/factory/src_independent/extensions/map/map_r6_tlvs.c b/source/ieee1905/src/factory/src_independent/extensions/map/map_r6_tlvs.c new file mode 100644 index 0000000..8ef8e46 --- /dev/null +++ b/source/ieee1905/src/factory/src_independent/extensions/map/map_r6_tlvs.c @@ -0,0 +1,985 @@ +/* + * Copyright (c) 2019-2023 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/************* COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************ +** Copyright (c) [2019] – [Technicolor Delivery Technologies, SAS] +** All Rights Reserved +** The source code form of this Open Source Project components +** is subject to the terms of the BSD-2-Clause-Patent. +** You can redistribute it and/or modify it under the terms of +** the BSD-2-Clause-Patent. (https://opensource.org/licenses/BSDplusPatent) +** See COPYING file/LICENSE file for more details. +****************************************************************************/ + +/* + * Broadband Forum IEEE 1905.1/1a stack + * + * Copyright (c) 2017, Broadband Forum + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Subject to the terms and conditions of this license, each copyright + * holder and contributor hereby grants to those receiving rights under + * this license a perpetual, worldwide, non-exclusive, no-charge, + * royalty-free, irrevocable (except for failure to satisfy the + * conditions of this license) patent license to make, have made, use, + * offer to sell, sell, import, and otherwise transfer this software, + * where such license applies only to those patent claims, already + * acquired or hereafter acquired, licensable by such copyright holder or + * contributor that are necessarily infringed by: + * + * (a) their Contribution(s) (the licensed copyrights of copyright holders + * and non-copyrightable additions of contributors, in source or binary + * form) alone; or + * + * (b) combination of their Contribution(s) with the work of authorship to + * which such Contribution(s) was added by such copyright holder or + * contributor, if, at the time the Contribution is added, such addition + * causes such combination to be necessarily infringed. The patent + * license shall not apply to any other combinations which include the + * Contribution. + * + * Except as expressly stated above, no rights or licenses from any + * copyright holder or contributor is granted under this license, whether + * expressly, by implication, estoppel or otherwise. + * + * DISCLAIMER + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#define TLV_STRUCT_NAME_PREFIX TLV_STRUCT_NAME_PREFIX_MAP + +#include "1905_tlvs.h" +#include "packet_tools.h" +#include "map_tlvs.h" + +/*####################################################################### +# TLV HANDLERS # +########################################################################*/ + +/*####################################################################### +# Wi-Fi 7 Agent Capabilities TLV ("Section 17.2.95") # +########################################################################*/ +TLV_FREE_FUNCTION(wifi7_agent_cap) +{ + uint8_t i; + + for (i = 0; i < m->radios_nr; i++) { + SFREE(m->radios[i].cap.ap_str_records); + SFREE(m->radios[i].cap.ap_nstr_records); + SFREE(m->radios[i].cap.ap_emlsr_records); + SFREE(m->radios[i].cap.ap_emlmr_records); + SFREE(m->radios[i].cap.bsta_str_records); + SFREE(m->radios[i].cap.bsta_nstr_records); + SFREE(m->radios[i].cap.bsta_emlsr_records); + SFREE(m->radios[i].cap.bsta_emlmr_records); + } +} + +static uint8_t* parse_wifi7_agent_cap_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_wifi7_agent_cap_tlv_t *ret; + uint8_t *p = packet_stream, byte, i, j; + + /* Min TLV len: MaxMLD + Maxlinks + TIDtoLinkMap + Reserved(13) + radio_nr */ + PARSE_CHECK_MIN_LEN(17); + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_WIFI7_AGENT_CAPABILITIES; + + _E1B(&p, &ret->max_mlds); + _E1B(&p, &byte); + ret->ap_max_links = (byte & (BIT_MASK_7 | BIT_MASK_6 | BIT_MASK_5 | BIT_MASK_4)) >> BIT_SHIFT_4; + ret->bsta_max_links = byte & (BIT_MASK_3 | BIT_MASK_2 | BIT_MASK_1 | BIT_MASK_0); + _E1B(&p, &byte); + ret->tid_to_link_map_cap = (byte & (BIT_MASK_7 | BIT_MASK_6)) >> BIT_SHIFT_6; + _EnB(&p, ret->reserved2, 13); + + _E1B(&p, &ret->radios_nr); + PARSE_LIMIT(ret->radios_nr, MAX_RADIO_PER_AGENT); + for (i = 0; i < ret->radios_nr; i++) { + _EnB(&p, ret->radios[i].ruid, ETHER_ADDR_LEN); + _EnB(&p, ret->radios[i].reserved, 24); + + _E1B(&p, &byte); + ret->radios[i].cap.ap_mld_modes.str = (byte & BIT_MASK_7); + ret->radios[i].cap.ap_mld_modes.nstr = (byte & BIT_MASK_6); + ret->radios[i].cap.ap_mld_modes.emlsr = (byte & BIT_MASK_5); + ret->radios[i].cap.ap_mld_modes.emlmr = (byte & BIT_MASK_4); + + _E1B(&p, &byte); + ret->radios[i].cap.bsta_mld_modes.str = (byte & BIT_MASK_7); + ret->radios[i].cap.bsta_mld_modes.nstr = (byte & BIT_MASK_6); + ret->radios[i].cap.bsta_mld_modes.emlsr = (byte & BIT_MASK_5); + ret->radios[i].cap.bsta_mld_modes.emlmr = (byte & BIT_MASK_4); + + _E1B(&p, &ret->radios[i].cap.ap_str_records_nr); + if (ret->radios[i].cap.ap_str_records_nr > 0) { + ret->radios[i].cap.ap_str_records = calloc(ret->radios[i].cap.ap_str_records_nr, sizeof(*ret->radios[i].cap.ap_str_records)); + if (ret->radios[i].cap.ap_str_records == NULL) { + PARSE_FREE_RET_RETURN(wifi7_agent_cap) + } + + for (j = 0; j < ret->radios[i].cap.ap_str_records_nr; j++) { + _EnB(&p, ret->radios[i].cap.ap_str_records[j].ruid, ETHER_ADDR_LEN); + _E1B(&p, &byte); + ret->radios[i].cap.ap_str_records[j].freq_separation = (byte & (BIT_MASK_7 | BIT_MASK_6 | BIT_MASK_5 | BIT_MASK_4 | BIT_MASK_3)) >> BIT_SHIFT_3; + } + } + + _E1B(&p, &ret->radios[i].cap.ap_nstr_records_nr); + if (ret->radios[i].cap.ap_nstr_records_nr > 0) { + ret->radios[i].cap.ap_nstr_records = calloc(ret->radios[i].cap.ap_nstr_records_nr, sizeof(*ret->radios[i].cap.ap_nstr_records)); + if (ret->radios[i].cap.ap_nstr_records == NULL) { + PARSE_FREE_RET_RETURN(wifi7_agent_cap) + } + + for (j = 0; j < ret->radios[i].cap.ap_nstr_records_nr; j++) { + _EnB(&p, ret->radios[i].cap.ap_nstr_records[j].ruid, ETHER_ADDR_LEN); + _E1B(&p, &byte); + ret->radios[i].cap.ap_nstr_records[j].freq_separation = (byte & (BIT_MASK_7 | BIT_MASK_6 | BIT_MASK_5 | BIT_MASK_4 | BIT_MASK_3)) >> BIT_SHIFT_3; + } + } + + _E1B(&p, &ret->radios[i].cap.ap_emlsr_records_nr); + if (ret->radios[i].cap.ap_emlsr_records_nr > 0) { + ret->radios[i].cap.ap_emlsr_records = calloc(ret->radios[i].cap.ap_emlsr_records_nr, sizeof(*ret->radios[i].cap.ap_emlsr_records)); + if (ret->radios[i].cap.ap_emlsr_records == NULL) { + PARSE_FREE_RET_RETURN(wifi7_agent_cap) + } + + for (j = 0; j < ret->radios[i].cap.ap_emlsr_records_nr; j++) { + _EnB(&p, ret->radios[i].cap.ap_emlsr_records[j].ruid, ETHER_ADDR_LEN); + _E1B(&p, &byte); + ret->radios[i].cap.ap_emlsr_records[j].freq_separation = (byte & (BIT_MASK_7 | BIT_MASK_6 | BIT_MASK_5 | BIT_MASK_4 | BIT_MASK_3)) >> BIT_SHIFT_3; + } + } + + _E1B(&p, &ret->radios[i].cap.ap_emlmr_records_nr); + if (ret->radios[i].cap.ap_emlmr_records_nr > 0) { + ret->radios[i].cap.ap_emlmr_records = calloc(ret->radios[i].cap.ap_emlmr_records_nr, sizeof(*ret->radios[i].cap.ap_emlmr_records)); + if (ret->radios[i].cap.ap_emlmr_records == NULL) { + PARSE_FREE_RET_RETURN(wifi7_agent_cap) + } + + for (j = 0; j < ret->radios[i].cap.ap_emlmr_records_nr; j++) { + _EnB(&p, ret->radios[i].cap.ap_emlmr_records[j].ruid, ETHER_ADDR_LEN); + _E1B(&p, &byte); + ret->radios[i].cap.ap_emlmr_records[j].freq_separation = (byte & (BIT_MASK_7 | BIT_MASK_6 | BIT_MASK_5 | BIT_MASK_4 | BIT_MASK_3)) >> BIT_SHIFT_3; + } + } + + _E1B(&p, &ret->radios[i].cap.bsta_str_records_nr); + if (ret->radios[i].cap.bsta_str_records_nr > 0) { + ret->radios[i].cap.bsta_str_records = calloc(ret->radios[i].cap.bsta_str_records_nr, sizeof(*ret->radios[i].cap.ap_str_records)); + if (ret->radios[i].cap.bsta_str_records == NULL) { + PARSE_FREE_RET_RETURN(wifi7_agent_cap) + } + + for (j = 0; j < ret->radios[i].cap.bsta_str_records_nr; j++) { + _EnB(&p, ret->radios[i].cap.bsta_str_records[j].ruid, ETHER_ADDR_LEN); + _E1B(&p, &byte); + ret->radios[i].cap.bsta_str_records[j].freq_separation = (byte & (BIT_MASK_7 | BIT_MASK_6 | BIT_MASK_5 | BIT_MASK_4 | BIT_MASK_3)) >> BIT_SHIFT_3; + } + } + + _E1B(&p, &ret->radios[i].cap.bsta_nstr_records_nr); + if (ret->radios[i].cap.bsta_nstr_records_nr > 0) { + ret->radios[i].cap.bsta_nstr_records = calloc(ret->radios[i].cap.bsta_nstr_records_nr, sizeof(*ret->radios[i].cap.bsta_nstr_records)); + if (ret->radios[i].cap.bsta_nstr_records == NULL) { + PARSE_FREE_RET_RETURN(wifi7_agent_cap) + } + + for (j = 0; j < ret->radios[i].cap.bsta_nstr_records_nr; j++) { + _EnB(&p, ret->radios[i].cap.bsta_nstr_records[j].ruid, ETHER_ADDR_LEN); + _E1B(&p, &byte); + ret->radios[i].cap.bsta_nstr_records[j].freq_separation = (byte & (BIT_MASK_7 | BIT_MASK_6 | BIT_MASK_5 | BIT_MASK_4 | BIT_MASK_3)) >> BIT_SHIFT_3; + } + } + + _E1B(&p, &ret->radios[i].cap.bsta_emlsr_records_nr); + if (ret->radios[i].cap.bsta_emlsr_records_nr > 0) { + ret->radios[i].cap.bsta_emlsr_records = calloc(ret->radios[i].cap.bsta_emlsr_records_nr, sizeof(*ret->radios[i].cap.bsta_emlsr_records)); + if (ret->radios[i].cap.bsta_emlsr_records == NULL) { + PARSE_FREE_RET_RETURN(wifi7_agent_cap) + } + + for (j = 0; j < ret->radios[i].cap.bsta_emlsr_records_nr; j++) { + _EnB(&p, ret->radios[i].cap.bsta_emlsr_records[j].ruid, ETHER_ADDR_LEN); + _E1B(&p, &byte); + ret->radios[i].cap.bsta_emlsr_records[j].freq_separation = (byte & (BIT_MASK_7 | BIT_MASK_6 | BIT_MASK_5 | BIT_MASK_4 | BIT_MASK_3)) >> BIT_SHIFT_3; + } + } + + _E1B(&p, &ret->radios[i].cap.bsta_emlmr_records_nr); + if (ret->radios[i].cap.bsta_emlmr_records_nr > 0) { + ret->radios[i].cap.bsta_emlmr_records = calloc(ret->radios[i].cap.bsta_emlmr_records_nr, sizeof(*ret->radios[i].cap.bsta_emlmr_records)); + if (ret->radios[i].cap.bsta_emlmr_records == NULL) { + PARSE_FREE_RET_RETURN(wifi7_agent_cap) + } + + for (j = 0; j < ret->radios[i].cap.bsta_emlmr_records_nr; j++) { + _EnB(&p, ret->radios[i].cap.bsta_emlmr_records[j].ruid, ETHER_ADDR_LEN); + _E1B(&p, &byte); + ret->radios[i].cap.bsta_emlmr_records[j].freq_separation = (byte & (BIT_MASK_7 | BIT_MASK_6 | BIT_MASK_5 | BIT_MASK_4 | BIT_MASK_3)) >> BIT_SHIFT_3; + } + } + + } + + PARSE_CHECK_INTEGRITY(wifi7_agent_cap) + PARSE_RETURN +} + +static uint8_t* forge_wifi7_agent_cap_tlv(void *memory_structure, uint16_t *len) +{ + map_wifi7_agent_cap_tlv_t *m = memory_structure; + uint8_t *ret, *p, i, j, byte; + + /* Calculate TLV length */ + uint16_t tlv_length = 17; /* MaxMLD + Maxlinks + TIDtoLinkMap + Reserved(13) + radio_nr */ + for (i = 0; i < m->radios_nr; i++) { + tlv_length += 6 + 24 + 1 + 1; /* ruid, reserved, ap_cap, bsta_cap */ + tlv_length += 1 + (m->radios[i].cap.ap_str_records_nr * 7); /* ruid, freq_sep */ + tlv_length += 1 + (m->radios[i].cap.ap_nstr_records_nr * 7); + tlv_length += 1 + (m->radios[i].cap.ap_emlsr_records_nr * 7); + tlv_length += 1 + (m->radios[i].cap.ap_emlmr_records_nr * 7); + tlv_length += 1 + (m->radios[i].cap.bsta_str_records_nr * 7); + tlv_length += 1 + (m->radios[i].cap.bsta_nstr_records_nr * 7); + tlv_length += 1 + (m->radios[i].cap.bsta_emlsr_records_nr * 7); + tlv_length += 1 + (m->radios[i].cap.bsta_emlmr_records_nr * 7); + } + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _I1B(&m->max_mlds, &p); + + byte = (m->ap_max_links << BIT_SHIFT_4) | m->bsta_max_links; + _I1B(&byte, &p); + byte = m->tid_to_link_map_cap << BIT_SHIFT_6; + _I1B(&byte, &p); + _InB(&m->reserved2, &p, 13); + + _I1B(&m->radios_nr, &p); + for (i = 0; i < m->radios_nr; i++) { + _InB(m->radios[i].ruid, &p, ETHER_ADDR_LEN); + _InB(m->radios[i].reserved, &p, 24); + byte = (m->radios[i].cap.ap_mld_modes.str << BIT_SHIFT_7) | + (m->radios[i].cap.ap_mld_modes.nstr << BIT_SHIFT_6) | + (m->radios[i].cap.ap_mld_modes.emlsr << BIT_SHIFT_5) | + (m->radios[i].cap.ap_mld_modes.emlmr << BIT_SHIFT_4); + _I1B(&byte, &p); + byte = (m->radios[i].cap.bsta_mld_modes.str << BIT_SHIFT_7) | + (m->radios[i].cap.bsta_mld_modes.nstr << BIT_SHIFT_6) | + (m->radios[i].cap.bsta_mld_modes.emlsr << BIT_SHIFT_5) | + (m->radios[i].cap.bsta_mld_modes.emlmr << BIT_SHIFT_4); + _I1B(&byte, &p); + + _I1B(&m->radios[i].cap.ap_str_records_nr, &p); + for (j = 0; j < m->radios[i].cap.ap_str_records_nr; j++) { + _InB(m->radios[i].cap.ap_str_records[j].ruid, &p, ETHER_ADDR_LEN); + byte = m->radios[i].cap.ap_str_records[j].freq_separation << BIT_SHIFT_3; + _I1B(&byte, &p); + } + + _I1B(&m->radios[i].cap.ap_nstr_records_nr, &p); + for (j = 0; j < m->radios[i].cap.ap_nstr_records_nr; j++) { + _InB(m->radios[i].cap.ap_nstr_records[j].ruid, &p, ETHER_ADDR_LEN); + byte = m->radios[i].cap.ap_nstr_records[j].freq_separation << BIT_SHIFT_3; + _I1B(&byte, &p); + } + + _I1B(&m->radios[i].cap.ap_emlsr_records_nr, &p); + for (j = 0; j < m->radios[i].cap.ap_emlsr_records_nr; j++) { + _InB(m->radios[i].cap.ap_emlsr_records[j].ruid, &p, ETHER_ADDR_LEN); + byte = m->radios[i].cap.ap_emlsr_records[j].freq_separation << BIT_SHIFT_3; + _I1B(&byte, &p); + } + + _I1B(&m->radios[i].cap.ap_emlmr_records_nr, &p); + for (j = 0; j < m->radios[i].cap.ap_emlmr_records_nr; j++) { + _InB(m->radios[i].cap.ap_emlmr_records[j].ruid, &p, ETHER_ADDR_LEN); + byte = m->radios[i].cap.ap_emlmr_records[j].freq_separation << BIT_SHIFT_3; + _I1B(&byte, &p); + } + + _I1B(&m->radios[i].cap.bsta_str_records_nr, &p); + for (j = 0; j < m->radios[i].cap.bsta_str_records_nr; j++) { + _InB(m->radios[i].cap.bsta_str_records[j].ruid, &p, ETHER_ADDR_LEN); + byte = m->radios[i].cap.bsta_str_records[j].freq_separation << BIT_SHIFT_3; + _I1B(&byte, &p); + } + + _I1B(&m->radios[i].cap.bsta_nstr_records_nr, &p); + for (j = 0; j < m->radios[i].cap.bsta_nstr_records_nr; j++) { + _InB(m->radios[i].cap.bsta_nstr_records[j].ruid, &p, ETHER_ADDR_LEN); + byte = m->radios[i].cap.bsta_nstr_records[j].freq_separation << BIT_SHIFT_3; + _I1B(&byte, &p); + } + + _I1B(&m->radios[i].cap.bsta_emlsr_records_nr, &p); + for (j = 0; j < m->radios[i].cap.bsta_emlsr_records_nr; j++) { + _InB(m->radios[i].cap.bsta_emlsr_records[j].ruid, &p, ETHER_ADDR_LEN); + byte = m->radios[i].cap.bsta_emlsr_records[j].freq_separation << BIT_SHIFT_3; + _I1B(&byte, &p); + } + + _I1B(&m->radios[i].cap.bsta_emlmr_records_nr, &p); + for (j = 0; j < m->radios[i].cap.bsta_emlmr_records_nr; j++) { + _InB(m->radios[i].cap.bsta_emlmr_records[j].ruid, &p, ETHER_ADDR_LEN); + byte = m->radios[i].cap.bsta_emlmr_records[j].freq_separation << BIT_SHIFT_3; + _I1B(&byte, &p); + } + } + + FORGE_RETURN +} + +/*####################################################################### +# Agent AP MLD Configuration TLV ("Section 17.2.96") # +########################################################################*/ +TLV_FREE_FUNCTION(agent_ap_mld_conf) {} + +static uint8_t* parse_agent_ap_mld_conf_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_agent_ap_mld_conf_tlv_t *ret; + uint8_t *p = packet_stream, byte, i, j; + + /* Min TLV len: Num_APMLD */ + PARSE_CHECK_MIN_LEN(1); + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_AGENT_AP_MLD_CONFIGURATION; + + _E1B(&p, &ret->ap_mld_nr); + PARSE_LIMIT(ret->ap_mld_nr, MAX_BSS_PER_RADIO); + + for (i = 0; i < ret->ap_mld_nr; i++) { + _E1B(&p, &byte); + ret->ap_mlds[i].ap_mld_mac_valid = (byte & BIT_MASK_7) ? SET_BIT : RESET_BIT; + + _E1B(&p, &ret->ap_mlds[i].ssid_len); + PARSE_LIMIT_N_DROP(agent_ap_mld_conf, ret->ap_mlds[i].ssid_len, (MAX_SSID_LEN - 1)); + _EnB(&p, ret->ap_mlds[i].ssid, ret->ap_mlds[i].ssid_len); + + _EnB(&p, ret->ap_mlds[i].ap_mld_mac, ETHER_ADDR_LEN); + + _E1B(&p, &byte); + ret->ap_mlds[i].str = (byte & BIT_MASK_7) ? SET_BIT : RESET_BIT; + ret->ap_mlds[i].nstr = (byte & BIT_MASK_6) ? SET_BIT : RESET_BIT; + ret->ap_mlds[i].emlsr = (byte & BIT_MASK_5) ? SET_BIT : RESET_BIT; + ret->ap_mlds[i].emlmr = (byte & BIT_MASK_4) ? SET_BIT : RESET_BIT; + + /* Skip 20 reserved bytes */ + p += 20; + + _E1B(&p, &ret->ap_mlds[i].aff_ap_nr); + PARSE_LIMIT(ret->ap_mlds[i].aff_ap_nr, MAX_MLD_AFF_APSTA); + + for (j = 0; j < ret->ap_mlds[i].aff_ap_nr; j++) { + _E1B(&p, &byte); + ret->ap_mlds[i].aff_aps[j].aff_ap_mac_valid = (byte & BIT_MASK_7) ? SET_BIT : RESET_BIT; + ret->ap_mlds[i].aff_aps[j].link_id_valid = (byte & BIT_MASK_6) ? SET_BIT : RESET_BIT; + + _EnB(&p, ret->ap_mlds[i].aff_aps[j].radio_id, ETHER_ADDR_LEN); + _EnB(&p, ret->ap_mlds[i].aff_aps[j].aff_ap_mac, ETHER_ADDR_LEN); + + _E1B(&p, &ret->ap_mlds[i].aff_aps[j].link_id); + + /* Skip 18 reserved bytes */ + p += 18; + } + } + + PARSE_CHECK_INTEGRITY(agent_ap_mld_conf) + PARSE_RETURN +} + +static uint8_t* forge_agent_ap_mld_conf_tlv(void *memory_structure, uint16_t *len) +{ + map_agent_ap_mld_conf_tlv_t *m = memory_structure; + uint8_t *ret, *p, i, j, byte; + + /* Calculate TLV length */ + uint16_t tlv_length = 1; /* ap_mld_nr */ + for (i = 0; i < m->ap_mld_nr; i++) { + tlv_length += 1 + 1 + m->ap_mlds[i].ssid_len + 6 + 1 + 20 + 1; /* flags, ssid_len, ssid, ap_mld_mac, flags2, reserved, aff_ap_nr */ + for (j = 0; j < m->ap_mlds[i].aff_ap_nr; j++) { + tlv_length += 1 + 6 + 6 + 1 + 18; /* flags, radio_id, aff_ap_mac, link_id, reserved */ + } + } + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _I1B(&m->ap_mld_nr, &p); + + for (i = 0; i < m->ap_mld_nr; i++) { + byte = m->ap_mlds[i].ap_mld_mac_valid << BIT_SHIFT_7; + _I1B(&byte, &p); + + _I1B(&m->ap_mlds[i].ssid_len, &p); + _InB(m->ap_mlds[i].ssid, &p, m->ap_mlds[i].ssid_len); + + _InB(m->ap_mlds[i].ap_mld_mac, &p, ETHER_ADDR_LEN); + + byte = (m->ap_mlds[i].str << BIT_SHIFT_7) | + (m->ap_mlds[i].nstr << BIT_SHIFT_6) | + (m->ap_mlds[i].emlsr << BIT_SHIFT_5) | + (m->ap_mlds[i].emlmr << BIT_SHIFT_4); + _I1B(&byte, &p); + + FORGE_RESERVE(p, 20); + + _I1B(&m->ap_mlds[i].aff_ap_nr, &p); + + for (j = 0; j < m->ap_mlds[i].aff_ap_nr; j++) { + byte = (m->ap_mlds[i].aff_aps[j].aff_ap_mac_valid << BIT_SHIFT_7) | + (m->ap_mlds[i].aff_aps[j].link_id_valid << BIT_SHIFT_6); + _I1B(&byte, &p); + + _InB(m->ap_mlds[i].aff_aps[j].radio_id, &p, ETHER_ADDR_LEN); + _InB(m->ap_mlds[i].aff_aps[j].aff_ap_mac, &p, ETHER_ADDR_LEN); + + _I1B(&m->ap_mlds[i].aff_aps[j].link_id, &p); + + FORGE_RESERVE(p, 18); + } + } + + FORGE_RETURN +} + +/*####################################################################### +# Backhaul STA MLD Configuration TLV ("Section 17.2.97") # +########################################################################*/ +TLV_FREE_FUNCTION(bsta_mld_conf) {} + +static uint8_t* parse_bsta_mld_conf_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_bsta_mld_conf_tlv_t *ret; + uint8_t *p = packet_stream, byte, i; + + /* Min TLV len: flags, bsta_mld_mac, ap_mld_mac, flags2, reserved, aff_bsta_nr */ + PARSE_CHECK_MIN_LEN(1 + 6 + 6 + 1 + 17 + 1); + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_BACKHAUL_STA_MLD_CONFIGURATION; + + _E1B(&p, &byte); + ret->bsta_mld_mac_valid = (byte & BIT_MASK_7) ? SET_BIT : RESET_BIT; + ret->ap_mld_mac_valid = (byte & BIT_MASK_6) ? SET_BIT : RESET_BIT; + + _EnB(&p, ret->bsta_mld_mac, ETHER_ADDR_LEN); + _EnB(&p, ret->ap_mld_mac, ETHER_ADDR_LEN); + + _E1B(&p, &byte); + ret->str = (byte & BIT_MASK_7) ? SET_BIT : RESET_BIT; + ret->nstr = (byte & BIT_MASK_6) ? SET_BIT : RESET_BIT; + ret->emlsr = (byte & BIT_MASK_5) ? SET_BIT : RESET_BIT; + ret->emlmr = (byte & BIT_MASK_4) ? SET_BIT : RESET_BIT; + + /* Skip 17 reserved bytes */ + p += 17; + + _E1B(&p, &ret->aff_bsta_nr); + PARSE_LIMIT(ret->aff_bsta_nr, MAX_MLD_AFF_APSTA); + + for (i = 0; i < ret->aff_bsta_nr; i++) { + _E1B(&p, &byte); + ret->aff_bstas[i].aff_bsta_mac_valid = (byte & BIT_MASK_7) ? SET_BIT : RESET_BIT; + + _EnB(&p, ret->aff_bstas[i].radio_id, ETHER_ADDR_LEN); + _EnB(&p, ret->aff_bstas[i].aff_bsta_mac, ETHER_ADDR_LEN); + + /* Skip 19 reserved bytes */ + p += 19; + } + + PARSE_CHECK_INTEGRITY(bsta_mld_conf) + PARSE_RETURN +} + +static uint8_t* forge_bsta_mld_conf_tlv(void *memory_structure, uint16_t *len) +{ + map_bsta_mld_conf_tlv_t *m = memory_structure; + uint8_t *ret, *p, i, byte; + + /* Calculate TLV length */ + uint16_t tlv_length = 6 + 6 + 1 + 18 + 1; /* flags, bsta_mld_mac, ap_mld_mac, flags2, reserved, aff_bsta_nr */ + for (i = 0; i < m->aff_bsta_nr; i++) { + tlv_length += 1 + 6 + 6 + 19; /* flags, radio_id, aff_bsta_mac, reserved */ + } + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + + byte = (m->bsta_mld_mac_valid << BIT_SHIFT_7) | + (m->ap_mld_mac_valid << BIT_SHIFT_6); + _I1B(&byte, &p); + + _InB(m->bsta_mld_mac, &p, ETHER_ADDR_LEN); + _InB(m->ap_mld_mac, &p, ETHER_ADDR_LEN); + + byte = (m->str << BIT_SHIFT_7) | + (m->nstr << BIT_SHIFT_6) | + (m->emlsr << BIT_SHIFT_5) | + (m->emlmr << BIT_SHIFT_4); + _I1B(&byte, &p); + + FORGE_RESERVE(p, 17); + + _I1B(&m->aff_bsta_nr, &p); + + for (i = 0; i < m->aff_bsta_nr; i++) { + byte = m->aff_bstas[i].aff_bsta_mac_valid << BIT_SHIFT_7; + _I1B(&byte, &p); + + _InB(m->aff_bstas[i].radio_id, &p, ETHER_ADDR_LEN); + _InB(m->aff_bstas[i].aff_bsta_mac, &p, ETHER_ADDR_LEN); + + FORGE_RESERVE(p, 19); + } + + FORGE_RETURN +} + +/*####################################################################### +# Associated STA MLD Configuration TLV ("Section 17.2.98") # +########################################################################*/ +TLV_FREE_FUNCTION(assoc_sta_mld_conf) {} + +static uint8_t* parse_assoc_sta_mld_conf_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_assoc_sta_mld_conf_tlv_t *ret; + uint8_t *p = packet_stream, byte, i; + + /* Min TLV len: sta_mld_mac, ap_mld_mac, flags, reserved, aff_sta_nr */ + PARSE_CHECK_MIN_LEN(6 + 6 + 1 + 18 + 1); + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_ASSOCIATED_STA_MLD_CONFIGURATION; + + _EnB(&p, ret->sta_mld_mac, ETHER_ADDR_LEN); + _EnB(&p, ret->ap_mld_mac, ETHER_ADDR_LEN); + + _E1B(&p, &byte); + ret->str = (byte & BIT_MASK_7) ? SET_BIT : RESET_BIT; + ret->nstr = (byte & BIT_MASK_6) ? SET_BIT : RESET_BIT; + ret->emlsr = (byte & BIT_MASK_5) ? SET_BIT : RESET_BIT; + ret->emlmr = (byte & BIT_MASK_4) ? SET_BIT : RESET_BIT; + + /* Skip 18 reserved bytes */ + p += 18; + + _E1B(&p, &ret->aff_sta_nr); + PARSE_LIMIT(ret->aff_sta_nr, MAX_MLD_AFF_APSTA); + + for (i = 0; i < ret->aff_sta_nr; i++) { + _EnB(&p, ret->aff_stas[i].bssid, ETHER_ADDR_LEN); + _EnB(&p, ret->aff_stas[i].aff_sta_mac, ETHER_ADDR_LEN); + + /* Skip 19 reserved bytes */ + p += 19; + } + + PARSE_CHECK_INTEGRITY(assoc_sta_mld_conf) + PARSE_RETURN +} + +static uint8_t* forge_assoc_sta_mld_conf_tlv(void *memory_structure, uint16_t *len) +{ + map_assoc_sta_mld_conf_tlv_t *m = memory_structure; + uint8_t *ret, *p, i, byte; + + /* Calculate TLV length */ + uint16_t tlv_length = 6 + 6 + 1 + 18 + 1; /* sta_mld_mac, ap_mld_mac, flags, reserved, aff_sta_nr */ + for (i = 0; i < m->aff_sta_nr; i++) { + tlv_length += 6 + 6 + 19; /* bssid, aff_sta_mac, reserved */ + } + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + + _InB(m->sta_mld_mac, &p, ETHER_ADDR_LEN); + _InB(m->ap_mld_mac, &p, ETHER_ADDR_LEN); + + byte = (m->str << BIT_SHIFT_7) | + (m->nstr << BIT_SHIFT_6) | + (m->emlsr << BIT_SHIFT_5) | + (m->emlmr << BIT_SHIFT_4); + _I1B(&byte, &p); + + FORGE_RESERVE(p, 18); + + _I1B(&m->aff_sta_nr, &p); + + for (i = 0; i < m->aff_sta_nr; i++) { + _InB(m->aff_stas[i].bssid, &p, ETHER_ADDR_LEN); + _InB(m->aff_stas[i].aff_sta_mac, &p, ETHER_ADDR_LEN); + + FORGE_RESERVE(p, 19); + } + + FORGE_RETURN +} + +/*####################################################################### +# Affiliated STA Metrics TLV ("Section 17.2.100") # +########################################################################*/ +TLV_FREE_FUNCTION(aff_sta_metrics) {} + +static uint8_t* parse_aff_sta_metrics_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_aff_sta_metrics_tlv_t *ret; + uint8_t *p = packet_stream; + + /* Min TLV len: sta_mac and 5 4-byte fields */ + PARSE_CHECK_MIN_LEN(6 + 5 * 4); + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_AFFILIATED_STA_METRICS; + + _EnB(&p, ret->sta_mac, ETHER_ADDR_LEN); + _E4B(&p, &ret->tx_bytes); + _E4B(&p, &ret->rx_bytes); + _E4B(&p, &ret->tx_packets); + _E4B(&p, &ret->rx_packets); + _E4B(&p, &ret->tx_packet_errors); + + /* According to R6 standard, now 998 reserved bytes follow. We are not checking that + as it looks like a standard oversight + */ + ret->reserved_len = len - (p - packet_stream); + p += ret->reserved_len; + + PARSE_CHECK_INTEGRITY(aff_sta_metrics) + PARSE_RETURN +} + +static uint8_t* forge_aff_sta_metrics_tlv(void *memory_structure, uint16_t *len) +{ + map_aff_sta_metrics_tlv_t *m = memory_structure; + uint8_t *ret, *p; + + /* Calculate TLV length */ + uint16_t tlv_length = 6 + 5 * 4 + m->reserved_len; /* sta_mac, 5 4-byte fields, reserved */ + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _InB(m->sta_mac, &p, ETHER_ADDR_LEN); + _I4B(&m->tx_bytes, &p); + _I4B(&m->rx_bytes, &p); + _I4B(&m->tx_packets, &p); + _I4B(&m->rx_packets, &p); + _I4B(&m->tx_packet_errors, &p); + + FORGE_RESERVE(p, m->reserved_len); + + FORGE_RETURN +} + +/*####################################################################### +# Affiliated AP Metrics TLV ("Section 17.2.101") # +########################################################################*/ +TLV_FREE_FUNCTION(aff_ap_metrics) {} + +static uint8_t* parse_aff_ap_metrics_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_aff_ap_metrics_tlv_t *ret; + uint8_t *p = packet_stream; + + /* Min TLV len: bssid and 9 4-byte fields */ + PARSE_CHECK_MIN_LEN(6 + 9 * 4); + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_AFFILIATED_AP_METRICS; + + _EnB(&p, ret->bssid, ETHER_ADDR_LEN); + _E4B(&p, &ret->tx_packets); + _E4B(&p, &ret->rx_packets); + _E4B(&p, &ret->tx_packet_errors); + _E4B(&p, &ret->tx_ucast_bytes); + _E4B(&p, &ret->rx_ucast_bytes); + _E4B(&p, &ret->tx_mcast_bytes); + _E4B(&p, &ret->rx_mcast_bytes); + _E4B(&p, &ret->tx_bcast_bytes); + _E4B(&p, &ret->rx_bcast_bytes); + + /* According to R6 standard, now 988 reserved bytes follow. We are not checking that + as it looks like a standard oversight + */ + ret->reserved_len = len - (p - packet_stream); + p += ret->reserved_len; + + PARSE_CHECK_INTEGRITY(aff_ap_metrics) + PARSE_RETURN +} + +static uint8_t* forge_aff_ap_metrics_tlv(void *memory_structure, uint16_t *len) +{ + map_aff_ap_metrics_tlv_t *m = memory_structure; + uint8_t *ret, *p; + + /* Calculate TLV length */ + uint16_t tlv_length = 6 + 9 * 4 + m->reserved_len; /* bssid, 9 4-byte fields, reserved */ + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _InB(m->bssid, &p, ETHER_ADDR_LEN); + _I4B(&m->tx_packets, &p); + _I4B(&m->rx_packets, &p); + _I4B(&m->tx_packet_errors, &p); + _I4B(&m->tx_ucast_bytes, &p); + _I4B(&m->rx_ucast_bytes, &p); + _I4B(&m->tx_mcast_bytes, &p); + _I4B(&m->rx_mcast_bytes, &p); + _I4B(&m->tx_bcast_bytes, &p); + _I4B(&m->rx_bcast_bytes, &p); + + FORGE_RESERVE(p, m->reserved_len); + + FORGE_RETURN +} + +/*####################################################################### +# EHT Operations TLV ("Section 17.2.103") # +########################################################################*/ +TLV_FREE_FUNCTION(eht_operations) {} + +static uint8_t* parse_eht_operations_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_eht_operations_tlv_t *ret; + uint8_t *p = packet_stream, byte, i, j; + + /* Min TLV len: Reserved(32) + radio_nr */ + PARSE_CHECK_MIN_LEN(33); + + PARSE_CALLOC_RET + + ret->tlv_type = TLV_TYPE_EHT_OPERATIONS; + + _EnB(&p, &ret->reserved, 32); + + _E1B(&p, &ret->radios_nr); + PARSE_LIMIT(ret->radios_nr, MAX_RADIO_PER_AGENT); + for (i = 0; i < ret->radios_nr; i++) { + _EnB(&p, ret->radios[i].ruid, ETHER_ADDR_LEN); + + _E1B(&p, &ret->radios[i].bsss_nr); + for (j = 0; j < ret->radios[i].bsss_nr; j++) { + _EnB(&p, ret->radios[i].bsss[j].bssid, ETHER_ADDR_LEN); + + _E1B(&p, &byte); + ret->radios[i].bsss[j].eht_ops.eht_op_info_valid = (byte & BIT_MASK_7) ? SET_BIT : RESET_BIT; + ret->radios[i].bsss[j].eht_ops.disabled_subchan_valid = (byte & BIT_MASK_6) ? SET_BIT : RESET_BIT; + ret->radios[i].bsss[j].eht_ops.eht_default_pe_duration = (byte & BIT_MASK_5) ? SET_BIT : RESET_BIT; + ret->radios[i].bsss[j].eht_ops.group_addr_bu_indication_limit = (byte & BIT_MASK_4) ? SET_BIT : RESET_BIT; + ret->radios[i].bsss[j].eht_ops.group_addr_bu_indication_exp = (byte & (BIT_MASK_3 | BIT_MASK_2)) >> BIT_SHIFT_2; + + _EnB(&p, &ret->radios[i].bsss[j].eht_ops.basic_eht_mcs_nss, 4); + _E1B(&p, &ret->radios[i].bsss[j].eht_ops.control); + _E1B(&p, &ret->radios[i].bsss[j].eht_ops.ccfs0); + _E1B(&p, &ret->radios[i].bsss[j].eht_ops.ccfs1); + _E2B(&p, &ret->radios[i].bsss[j].eht_ops.disabled_subchan_bitmap); + _EnB(&p, &ret->radios[i].bsss[j].reserved, 16); + } + _EnB(&p, &ret->radios[i].reserved, 25); + } + + PARSE_CHECK_INTEGRITY(eht_operations) + PARSE_RETURN +} + +static uint8_t* forge_eht_operations_tlv(void *memory_structure, uint16_t *len) +{ + map_eht_operations_tlv_t *m = memory_structure; + uint8_t *ret, *p, i, j, byte; + + /* Calculate TLV length */ + uint16_t tlv_length = 33; /* Reserved(32) + radios_nr */ + for (i = 0; i < m->radios_nr; i++) { + tlv_length += 6 + 1 + 25; /* ruid, bsss_br, reserved */ + for (j = 0; j < m->radios[i].bsss_nr; j++) { + tlv_length += 6 + 1 + 4 + 1 + 1 + 1 + 2 + 16; /* bssid, eht_op, mcs_nss , control, ccfs0, ccfs1, subchan_bitmap, reserved */ + } + } + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _InB(&m->reserved, &p, 32); + + _I1B(&m->radios_nr, &p); + for (i = 0; i < m->radios_nr; i++) { + _InB(&m->radios[i].ruid, &p, ETHER_ADDR_LEN); + + _I1B(&m->radios[i].bsss_nr, &p); + for (j = 0; j < m->radios[i].bsss_nr; j++) { + _InB(&m->radios[i].bsss[j].bssid, &p, ETHER_ADDR_LEN); + + byte = (m->radios[i].bsss[j].eht_ops.eht_op_info_valid << BIT_SHIFT_7) | + (m->radios[i].bsss[j].eht_ops.disabled_subchan_valid << BIT_SHIFT_6) | + (m->radios[i].bsss[j].eht_ops.eht_default_pe_duration << BIT_SHIFT_5) | + (m->radios[i].bsss[j].eht_ops.group_addr_bu_indication_limit << BIT_SHIFT_4) | + (m->radios[i].bsss[j].eht_ops.group_addr_bu_indication_exp << BIT_SHIFT_2); + _I1B(&byte, &p); + + _I4B(&m->radios[i].bsss[j].eht_ops.basic_eht_mcs_nss, &p); + _I1B(&m->radios[i].bsss[j].eht_ops.control, &p); + _I1B(&m->radios[i].bsss[j].eht_ops.ccfs0, &p); + _I1B(&m->radios[i].bsss[j].eht_ops.ccfs1, &p); + _I2B(&m->radios[i].bsss[j].eht_ops.disabled_subchan_bitmap, &p); + + _InB(&m->radios[i].bsss[j].reserved, &p, 16); + } + + } + + FORGE_RETURN +} + +/*################################################################################# +# AVAILABLE SPECTRUM INQUIRY REQUEST TLV ("Section 17.2.104") # +##################################################################################*/ +TLV_FREE_FUNCTION(available_spec_inq_req) {} + +static uint8_t* parse_available_spec_inq_req_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_available_spec_inq_req_tlv_t *ret; + uint8_t *p = packet_stream; + + PARSE_CHECK_MIN_LEN(1) + + /* Allocate struct and request frame */ + ret = calloc(1, sizeof(*ret) + len); + if (NULL == ret) { + return NULL; + } + + ret->tlv_type = TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_REQUEST; + ret->req_len = len; + ret->req = (uint8_t *)(ret + 1); + _EnB(&p, ret->req, ret->req_len); + + PARSE_CHECK_INTEGRITY(available_spec_inq_req) + PARSE_RETURN +} + +static uint8_t* forge_available_spec_inq_req_tlv(void *memory_structure, uint16_t *len) +{ + map_available_spec_inq_req_tlv_t *m = memory_structure; + uint16_t tlv_length = m->req_len; + uint8_t *ret, *p; + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _InB(m->req, &p, m->req_len); + + FORGE_RETURN +} + +/*################################################################################# +# AVAILABLE SPECTRUM INQUIRY RESPONSE TLV ("Section 17.2.105") # +##################################################################################*/ +TLV_FREE_FUNCTION(available_spec_inq_resp) {} + +static uint8_t* parse_available_spec_inq_resp_tlv(uint8_t *packet_stream, uint16_t len) +{ + map_available_spec_inq_resp_tlv_t *ret; + uint8_t *p = packet_stream; + + PARSE_CHECK_MIN_LEN(1) + + /* Allocate struct and response frame */ + ret = calloc(1, sizeof(*ret) + len); + if (NULL == ret) { + return NULL; + } + + ret->tlv_type = TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_RESPONSE; + ret->resp_len = len; + ret->resp = (uint8_t *)(ret + 1); + _EnB(&p, ret->resp, ret->resp_len); + + PARSE_CHECK_INTEGRITY(available_spec_inq_resp) + PARSE_RETURN +} + +static uint8_t* forge_available_spec_inq_resp_tlv(void *memory_structure, uint16_t *len) +{ + map_available_spec_inq_resp_tlv_t *m = memory_structure; + uint16_t tlv_length = m->resp_len; + uint8_t *ret, *p; + + FORGE_MALLOC_RET + + _I1B(&m->tlv_type, &p); + _I2B(&tlv_length, &p); + _InB(m->resp, &p, m->resp_len); + + FORGE_RETURN +} + +/*####################################################################### +# PUBLIC FUNCTIONS # +########################################################################*/ +void map_r6_register_tlvs(void) +{ + I1905_REGISTER_TLV(TLV_TYPE_WIFI7_AGENT_CAPABILITIES, wifi7_agent_cap ); + I1905_REGISTER_TLV(TLV_TYPE_AGENT_AP_MLD_CONFIGURATION, agent_ap_mld_conf ); + I1905_REGISTER_TLV(TLV_TYPE_BACKHAUL_STA_MLD_CONFIGURATION, bsta_mld_conf ); + I1905_REGISTER_TLV(TLV_TYPE_ASSOCIATED_STA_MLD_CONFIGURATION, assoc_sta_mld_conf ); + I1905_REGISTER_TLV(TLV_TYPE_AFFILIATED_STA_METRICS, aff_sta_metrics ); + I1905_REGISTER_TLV(TLV_TYPE_AFFILIATED_AP_METRICS, aff_ap_metrics ); + I1905_REGISTER_TLV(TLV_TYPE_EHT_OPERATIONS, eht_operations ); + I1905_REGISTER_TLV(TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_REQUEST, available_spec_inq_req ); + I1905_REGISTER_TLV(TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_RESPONSE, available_spec_inq_resp ); +} diff --git a/source/libplatform/include/1905_platform.h b/source/libplatform/include/1905_platform.h index 9e90202..a03daf4 100644 --- a/source/libplatform/include/1905_platform.h +++ b/source/libplatform/include/1905_platform.h @@ -24,7 +24,19 @@ #include "map_common_defines.h" +typedef struct map_1905_sec_key_info_s { + uint8_t ptk[MAX_PTK_LEN]; + size_t ptk_len; + uint8_t gtk[MAX_GTK_LEN]; + size_t gtk_len; + uint8_t encr_tx_counter[ENCRYPTION_TX_COUNTER_LEN]; + uint8_t encr_rx_counter[ENCRYPTION_TX_COUNTER_LEN]; + uint8_t integrity_tx_counter[INTEGRITY_TX_COUNTER_LEN]; + uint8_t integrity_rx_counter[INTEGRITY_TX_COUNTER_LEN]; +} map_1905_sec_key_info_t; + typedef void (*i1905_interface_cb_t)(const char *ifname, bool added); +typedef int (*i1905_key_info_cb_t)(uint8_t *al_mac, map_1905_sec_key_info_t *key_info); /******************************************************************************* * The 1905 standard originally only recognized a limited set of interface @@ -132,6 +144,7 @@ typedef struct interfaceInfo { #define INTERFACE_TYPE_IEEE_802_11AD_60_GHZ (0x0106) #define INTERFACE_TYPE_IEEE_802_11AF (0x0107) #define INTERFACE_TYPE_IEEE_802_11AX (0x0108) /* MAP R2 extension. One value all bands */ + #define INTERFACE_TYPE_IEEE_802_11BE (0x0109) /* MAP R6 extension. One value all bands */ #define INTERFACE_TYPE_IEEE_1901_WAVELET (0x0200) #define INTERFACE_TYPE_IEEE_1901_FFT (0x0201) #define INTERFACE_TYPE_MOCA_V1_1 (0x0300) @@ -198,6 +211,8 @@ typedef struct interfaceInfo { #define IEEE80211_AUTH_MODE_WPA2 (0x0010) #define IEEE80211_AUTH_MODE_WPA2PSK (0x0020) #define IEEE80211_AUTH_MODE_SAE (0x0040) + #define IEEE80211_AUTH_MODE_DPP (0x0080) + #define IEEE80211_AUTH_MODE_SAE_24 (0x0100) /* SAE with AKM24 */ uint16_t authentication_mode; @@ -210,6 +225,7 @@ typedef struct interfaceInfo { #define IEEE80211_ENCRYPTION_MODE_NONE (0x0001) #define IEEE80211_ENCRYPTION_MODE_TKIP (0x0004) #define IEEE80211_ENCRYPTION_MODE_AES (0x0008) + #define IEEE80211_ENCRYPTION_MODE_GCMP256 (0x0010) uint16_t encryption_mode; /* For APs: list of supported modes that * clients can use (OR'ed list of flags) @@ -418,27 +434,23 @@ static inline char *convert_interface_type_to_string(uint16_t iface_type) { switch (iface_type) { case INTERFACE_TYPE_IEEE_802_3U_FAST_ETHERNET: - case INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET: - return "ETHERNET"; - case INTERFACE_TYPE_IEEE_802_11B_2_4_GHZ: - case INTERFACE_TYPE_IEEE_802_11G_2_4_GHZ: - case INTERFACE_TYPE_IEEE_802_11N_2_4_GHZ: - return "2_4_GHZ_WIRELESS"; - case INTERFACE_TYPE_IEEE_802_11N_5_GHZ: - case INTERFACE_TYPE_IEEE_802_11A_5_GHZ: - case INTERFACE_TYPE_IEEE_802_11AC_5_GHZ: - return "5_GHZ_WIRELESS"; - case INTERFACE_TYPE_IEEE_802_11AD_60_GHZ: - return "60_GHZ_WIRELESS"; - case INTERFACE_TYPE_IEEE_802_11AX: - return "WIRELESS"; + case INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET: return "ETHERNET"; + + case INTERFACE_TYPE_IEEE_802_11B_2_4_GHZ: return "802.11B 2.4GHz"; + case INTERFACE_TYPE_IEEE_802_11G_2_4_GHZ: return "802.11G 2.4GHz"; + case INTERFACE_TYPE_IEEE_802_11N_2_4_GHZ: return "802.11N 2.4GHz"; + case INTERFACE_TYPE_IEEE_802_11A_5_GHZ: return "802.11A 5GHz"; + case INTERFACE_TYPE_IEEE_802_11N_5_GHZ: return "802.11N 5GHz"; + case INTERFACE_TYPE_IEEE_802_11AC_5_GHZ: return "802.11AC 5GHz"; + case INTERFACE_TYPE_IEEE_802_11AD_60_GHZ: return "802.11AD 60GHz"; + case INTERFACE_TYPE_IEEE_802_11AX: return "802.11AX"; + case INTERFACE_TYPE_IEEE_802_11BE: return "802.11BE"; + case INTERFACE_TYPE_IEEE_1901_WAVELET: case INTERFACE_TYPE_IEEE_1901_FFT: case INTERFACE_TYPE_MOCA_V1_1: case INTERFACE_TYPE_UNKNOWN: - return "UNKNOWN"; - default: - return "UNKNOWN"; + default: return "UNKNOWN"; } } diff --git a/source/libplatform/include/acu_utils.h b/source/libplatform/include/acu_utils.h index 2780641..f8cfed6 100644 --- a/source/libplatform/include/acu_utils.h +++ b/source/libplatform/include/acu_utils.h @@ -236,6 +236,10 @@ bool acu_mac_in_sorted_array(mac_addr mac, mac_addr *macs, size_t macs_nr); bool acu_mac_array_equal(mac_addr *macs1, size_t macs_nr1, mac_addr *macs2, size_t macs_nr2); +int acu_mac_add_to_array(mac_addr mac, mac_addr **macs, size_t *macs_nr); + +int acu_mac_del_from_array(mac_addr mac, mac_addr **macs, size_t *macs_nr); + /*####################################################################### # STRING OPERATIONS # ########################################################################*/ @@ -284,12 +288,16 @@ acu_retcode_t acu_evloop_timer_remaining(acu_evloop_timer_t *evloop_timer, extern void acu_evloop_fd_delete(acu_evloop_fd_t *evloop_fd); extern acu_evloop_fd_t *acu_evloop_fd_add_ex(int fd, unsigned flags, acu_evloop_fd_cb_t cb, void *userdata); - +#ifndef UNIT_TEST static inline acu_evloop_fd_t *acu_evloop_fd_add(int fd, acu_evloop_fd_cb_t cb, void *userdata) { return acu_evloop_fd_add_ex(fd, ACU_EVLOOP_READ, cb, userdata); } +#else +extern acu_evloop_fd_t *acu_evloop_fd_add(int fd, acu_evloop_fd_cb_t cb, + void *userdata); +#endif // UNIT_TEST extern int acu_evloop_run(void); extern void acu_evloop_end(void); diff --git a/source/libplatform/include/map_80211.h b/source/libplatform/include/map_80211.h index 222da20..e543240 100644 --- a/source/libplatform/include/map_80211.h +++ b/source/libplatform/include/map_80211.h @@ -29,10 +29,12 @@ * @param is_2g Indicate if station is connected to 2G or not * @param match_ssid SSID used to validate body * @param match_ssid_len Length of SSID used to validate body + * @param aff_sta_mac mac address of station in case this is an affiliated STA * @return The status code 0-success, -ve for failure */ -int map_80211_parse_assoc_body(map_sta_capability_t *caps, uint8_t *body, int body_len, int supported_freq, uint8_t *match_ssid, int match_ssid_len); +int map_80211_parse_assoc_body(map_sta_capability_t *caps, uint8_t *body, int body_len, int supported_freq, + uint8_t *match_ssid, int match_ssid_len, mac_addr aff_sta_mac); /** @brief This function derives max phy rate in kbps from capabilities diff --git a/source/libplatform/include/map_channel_set.h b/source/libplatform/include/map_channel_set.h index 9b8ffff..84b1719 100644 --- a/source/libplatform/include/map_channel_set.h +++ b/source/libplatform/include/map_channel_set.h @@ -20,7 +20,9 @@ #define MAP_MAX_CHANNEL 240 #define MAP_CS_BUF_LEN (MAP_MAX_CHANNEL * 4) -#define MAP_CHANS_W_BW_BUF_LEN (MAP_MAX_CHANNEL * 8) + +/* Buf length for channels with bandwidth: practical use case = 6G (59 channels and 5 bandwidths) */ +#define MAP_CHANS_W_BW_BUF_LEN (60 * 5 * 8) #if CHAR_BIT != 8 #error Unexpected value of CHAR_BIT diff --git a/source/libplatform/include/map_common_defines.h b/source/libplatform/include/map_common_defines.h index 2b10e2c..a0956e9 100644 --- a/source/libplatform/include/map_common_defines.h +++ b/source/libplatform/include/map_common_defines.h @@ -29,7 +29,6 @@ #define MAP_DEFAULT_AP_CAPABILITY_QUERY_INTERVAL 60 #define MAP_DEFAULT_DEAD_AGENT_DETECTION_INTERVAL 30 #define MAP_DEFAULT_LLDP_BRIDGE_DISCOVERY_INTERVAL 3 -#define MAP_DEFAULT_UCI_MGMT_IPC_REPORT_INTERVAL 5 #define MAP_DEFAULT_CHANNEL_SELECTION_ENABLED 1 #define MAP_DEFAULT_MULTIAP_PROFILE 2 /****************************************************/ @@ -49,12 +48,11 @@ #define MAX_BSS_PER_AGENT MAX_RADIO_PER_AGENT * MAX_BSS_PER_RADIO #define MAX_STATION_PER_BSS 128 #define MAX_STATION_PER_AGENT (MAX_RADIO_PER_AGENT * MAX_STATION_PER_BSS) /* Should be per bss but that is ridicoulous */ +#define MAX_MLD_PER_AGENT MAX_BSS_PER_RADIO +#define MAX_MLD_AFF_APSTA MAX_RADIO_PER_AGENT #define MAX_INACTIVE_STA 128 -#define MAX_OP_CLASS 48 -#define MAX_CHANNEL_PER_OP_CLASS 59 - #define MAX_TRAFFIC_SEP_SSID 8 #define MAX_ACCESS_CATEGORY 4 #define MAX_WIFI_SSID_LEN 33 /* Length = 32 + 1 for Null character at the end */ @@ -109,9 +107,11 @@ #define MAP_ASSOC_STA_TRAFFIC_STA_INCLUSION_POLICY (1<<7) /* Use below keys prepended with ALE MAC address of the agent */ -#define ONBOARDING_STATUS_TIMER_ID "ONBOARDING-STATUS-TIMER" -#define ETH_DEVICE_TIMER_ID "ETH-DEVICE-TIMER" -#define BLOCKLIST_AGE_TIMER_ID "BLOCKLIST-AGE-TIMER" +#define ONBOARDING_STATUS_TIMER_ID "ONBOARDING-STATUS-TIMER" +#define ETH_DEVICE_TIMER_ID "ETH-DEVICE-TIMER" +#define BLOCKLIST_AGE_TIMER_ID "BLOCKLIST-AGE-TIMER" +#define DELAYED_CHAN_SELECT_REQ_TIMER_ID "DEL-CHAN-SEL_REQ-TIMER" +#define STEER_WIFIBH_TIMER_ID "STEER-WIFIBH-TIMER" #define AP_CAPS_QUERY_RETRY_ID "AP-CAPS-QUERY" #define BHSTA_CAP_QUERY_RETRY_ID "BHSTA-CAP-QUERY" @@ -119,6 +119,7 @@ #define CHAN_SELECT_REQ_RETRY_ID "CHAN-SEL_REQ" #define TOPOLOGY_QUERY_RETRY_ID "TOPOLOGY-QUERY" #define ASSOC_CONTROL_RETRY_ID "ASSOC-CONTROL" +#define VENDOR_REBOOT_REQ_RETRY_ID "VENDOR-REBOOT-REQUEST" /* Use below keys prepended with RADIO MAC address */ #define POLICY_CONFIG_RETRY_ID "POLICY-CONFIG" @@ -145,14 +146,21 @@ enum { #define MAP_ASSOC_TS_DELTA 65536 /* Max assoc time in Associated Clients TLV */ +#ifndef SHA256_MAC_LEN +#define SHA256_MAC_LEN 32 +#endif + +#define MAX_PTK_LEN 32 +#define MAX_GTK_LEN 32 #define ENCRYPTION_TX_COUNTER_LEN 6 +#define INTEGRITY_TX_COUNTER_LEN 6 /* TODO: also defines in 1905_tlvs.h */ #ifndef IEEE80211_FREQUENCY_BAND_2_4_GHZ #define IEEE80211_FREQUENCY_BAND_2_4_GHZ 0x00 #define IEEE80211_FREQUENCY_BAND_5_GHZ 0x01 #define IEEE80211_FREQUENCY_BAND_60_GHZ 0x02 - #define IEEE80211_FREQUENCY_BAND_6_GHZ 0x04 + #define IEEE80211_FREQUENCY_BAND_6_GHZ 0x03 #define IEEE80211_FREQUENCY_BAND_UNKNOWN 0xFF #endif @@ -165,7 +173,7 @@ enum { #define MAX_NUM_TID 16 /* Number of possible TID values */ -enum map_m2_bss_freq_band { +enum map_m2_freq_band { MAP_M2_BSS_RADIO2G = 0x10, MAP_M2_BSS_RADIO5GU = 0x20, MAP_M2_BSS_RADIO5GL = 0x40, @@ -174,7 +182,17 @@ enum map_m2_bss_freq_band { #define MAP_FREQ_BAND_2G MAP_M2_BSS_RADIO2G #define MAP_FREQ_BAND_5G (MAP_M2_BSS_RADIO5GL | MAP_M2_BSS_RADIO5GU) -#define MAP_FREQ_BANDS_ALL (MAP_FREQ_BAND_2G | MAP_FREQ_BAND_5G) +#define MAP_FREQ_BANDS_ALL (MAP_FREQ_BAND_2G | MAP_FREQ_BAND_5G | MAP_M2_BSS_RADIO6G) + +enum map_m2_radio_standard { + MAP_M2_RADIO_80211A = 0x8000, + MAP_M2_RADIO_80211B = 0x4000, + MAP_M2_RADIO_80211G = 0x2000, + MAP_M2_RADIO_80211N = 0x1000, + MAP_M2_RADIO_80211AC = 0x0800, + MAP_M2_RADIO_80211AX = 0x0400, + MAP_M2_RADIO_80211BE = 0x0200, +}; enum ieee80211_btm_status { IEEE80211_BTM_STATUS_UNKNOWN = -1, diff --git a/source/libplatform/include/map_config.h b/source/libplatform/include/map_config.h index a4d023d..f80f7e3 100644 --- a/source/libplatform/include/map_config.h +++ b/source/libplatform/include/map_config.h @@ -27,6 +27,7 @@ #define WFA_CERT() (map_cfg_get()->wfa_cert) #define WFA_CERT_R1_COMPATIBLE() (map_cfg_get()->wfa_cert_r1_compatible) #define MONITOR_MODE() (!map_cfg_get()->is_master) +#define MLD_ENABLED() (map_cfg_get()->controller_cfg.mld_enabled) typedef enum bandlock_5g_e { MAP_BANDLOCK_5G_DISABLED, @@ -47,16 +48,16 @@ typedef void (*map_cfg_radio_channel_cb_t)(int ale_idx, int radio_idx, int chann typedef void (*map_cfg_radio_bandwidth_cb_t)(int ale_idx, int radio_idx, int bw); typedef struct { - map_cfg_enable_cb_t enable_cb; - map_cfg_master_state_cb_t master_state_cb; - map_cfg_update_cb_t update_cb; - map_cfg_profile_update_cb_t profile_update_cb; - map_cfg_backhaul_profile_update_cb_t backhaul_profile_update_cb; - map_cfg_allowed_channel_list_update_cb_t allowed_channel_list_update_cb; - map_cfg_allowed_bandwidth_update_cb_t allowed_bandwidth_update_cb; - map_cfg_bandlock_5g_cb_t bandlock_5g_update_cb; - map_cfg_radio_channel_cb_t radio_channel_cb; - map_cfg_radio_bandwidth_cb_t radio_bandwidth_cb; + map_cfg_enable_cb_t enable_cb; + map_cfg_master_state_cb_t master_state_cb; + map_cfg_update_cb_t update_cb; + map_cfg_profile_update_cb_t profile_update_cb; + map_cfg_backhaul_profile_update_cb_t backhaul_profile_update_cb; + map_cfg_allowed_channel_list_update_cb_t allowed_channel_list_update_cb; + map_cfg_allowed_bandwidth_update_cb_t allowed_bandwidth_update_cb; + map_cfg_bandlock_5g_cb_t bandlock_5g_update_cb; + map_cfg_radio_channel_cb_t radio_channel_cb; + map_cfg_radio_bandwidth_cb_t radio_bandwidth_cb; } map_cfg_cbs_t; typedef enum { @@ -92,6 +93,7 @@ typedef struct map_profile_cfg_s { uint8_t extender; int vlan_id; /* -1: untagged */ bool hide; + int mld_id; /* -1: non mld */ } map_profile_cfg_t; typedef struct map_chan_sel_cfg_s { @@ -106,6 +108,8 @@ typedef struct map_chan_sel_cfg_s { uint16_t allowed_bandwidth_6g; bool allowed_channel_6g_psc; bandlock_5g_t bandlock_5g; + bool align_multiap; + uint32_t align_multiap_backoff_time; } map_chan_sel_cfg_t; typedef struct map_controller_cfg_s { @@ -120,6 +124,7 @@ typedef struct map_controller_cfg_s { uint8_t local_agent_al_mac[MAC_ADDR_LEN]; uint8_t map_profile; + bool mld_enabled; unsigned int lldp_interval; unsigned int topology_discovery_interval; unsigned int topology_query_interval; diff --git a/source/libplatform/include/map_data_model.h b/source/libplatform/include/map_data_model.h index 49a7064..7736f99 100644 --- a/source/libplatform/include/map_data_model.h +++ b/source/libplatform/include/map_data_model.h @@ -23,12 +23,15 @@ #include "map_channel_set.h" #include "map_timer_handler.h" #include "map_utils.h" +#include "1905_platform.h" /* Forward declarations */ struct map_ale_info_s; struct map_radio_info_s; struct map_bss_info_s; struct map_sta_info_s; +struct map_ap_mld_info_s; +struct map_sta_mld_info_s; /*####################################################################### # RADIO CAPABILITY TYPES # @@ -73,24 +76,29 @@ typedef struct map_radio_he_capability_s { uint8_t reserved: 7; } map_radio_he_capability_t; +typedef struct map_radio_eht_capability_s { + /* TODO */ + int dummy; +} map_radio_eht_capability_t; + /*####################################################################### # STA INFO TYPES # ########################################################################*/ typedef struct map_client_traffic_stats_s { - uint64_t txbytes; - uint64_t rxbytes; - uint32_t txpkts; - uint32_t rxpkts; - uint32_t txpkterrors; - uint32_t rxpkterrors; - uint32_t retransmission_cnt; + uint64_t tx_bytes; + uint64_t rx_bytes; + uint32_t tx_packets; + uint32_t rx_packets; + uint32_t tx_packet_errors; + uint32_t rx_packet_errors; + uint32_t retransmissions; } map_sta_traffic_stats_t; typedef struct map_client_link_metrics_s { uint32_t age; uint32_t dl_mac_datarate; uint32_t ul_mac_datarate; - uint8_t rssi; + int8_t rssi; } map_sta_link_metrics_t; typedef struct map_client_metrics_s { @@ -117,27 +125,39 @@ typedef struct map_beacon_metrics_s { uint8_t subelements[0]; } STRUCT_PACKED map_sta_beacon_metrics_t; +typedef struct map_mld_modes_s { + bool str; + bool nstr; + bool emlsr; + bool emlmr; +} map_mld_modes_t; + typedef struct map_sta_capability_s { - bool valid; - uint8_t max_tx_spatial_streams; - uint8_t max_rx_spatial_streams; - uint16_t max_bandwidth; - uint8_t supported_standard; - uint8_t he_support: 1; - uint8_t vht_support: 1; - uint8_t ht_support: 1; - uint8_t erp_support: 1; /* ofdm rates, false for 11B */ - uint8_t sgi_support: 1; - uint8_t dot11k_support: 1; - uint8_t dot11k_brp_support: 1; - uint8_t dot11k_bra_support: 1; - uint8_t dot11v_btm_support: 1; - uint8_t backhaul_sta: 1; - uint8_t mbo_support: 1; - uint32_t max_phy_rate; /* in kbps */ - map_radio_ht_capability_t ht_caps; + bool valid; + uint8_t max_tx_spatial_streams; + uint8_t max_rx_spatial_streams; + uint16_t max_bandwidth; + uint8_t supported_standard; + uint8_t eht_support: 1; + uint8_t he_support: 1; + uint8_t vht_support: 1; + uint8_t ht_support: 1; + uint8_t erp_support: 1; /* ofdm rates, false for 11B */ + uint8_t sgi_support: 1; + uint8_t dot11k_support: 1; + uint8_t dot11k_brp_support: 1; + uint8_t dot11k_bra_support: 1; + uint8_t dot11v_btm_support: 1; + uint8_t backhaul_sta: 1; + uint8_t mbo_support: 1; + uint32_t max_phy_rate; /* in kbps */ + + map_mld_modes_t mld_modes; + + map_radio_ht_capability_t ht_caps; map_radio_vht_capability_t vht_caps; - map_radio_he_capability_t he_caps; + map_radio_he_capability_t he_caps; + map_radio_eht_capability_t eht_caps; } map_sta_capability_t; typedef struct map_tunneled_msg_s { @@ -170,7 +190,7 @@ typedef struct map_wifi6_sta_tid_info_s { uint8_t TID_nr; uint8_t TID[MAX_NUM_TID]; uint8_t queue_size[MAX_NUM_TID]; -} map_wifi6_sta_tid_info_s; +} map_wifi6_sta_tid_info_t; typedef enum map_steering_history_trigger_event_s { MAP_STEERING_TRIGGER_EVENT_UNKNOWN = 0x0, @@ -230,17 +250,24 @@ typedef struct map_sta_info_s { array_list_t *metrics; array_list_t *beacon_metrics; uint8_t bmquery_status; - map_sta_traffic_stats_t *traffic_stats; + map_sta_traffic_stats_t traffic_stats; struct map_bss_info_s *bss; uint16_t assoc_frame_len; uint8_t *assoc_frame; map_tunneled_msg_t *tunneled_msg; uint16_t last_disassoc_reason_code; map_sta_ext_link_metrics_t last_sta_ext_metrics; - map_wifi6_sta_tid_info_s wifi6_sta_tid_info; + map_wifi6_sta_tid_info_t wifi6_sta_tid_info; array_list_t *steering_history; uint32_t steering_history_size_delta; map_sta_steering_stats_t steering_stats; + + /* MLO */ + int dm_as_idx; + bool dm_as_removed; + list_head_t aff_sta_list; + struct map_sta_mld_info_s *sta_mld; /* Set in case this is an affiliated AP */ + uint8_t link_id; } map_sta_info_t; /*####################################################################### @@ -265,14 +292,28 @@ typedef struct map_ap_metric_s { typedef struct map_ap_extended_metrics_s { bool valid; - uint64_t ucast_bytes_tx; - uint64_t ucast_bytes_rx; - uint64_t mcast_bytes_tx; - uint64_t mcast_bytes_rx; - uint64_t bcast_bytes_tx; - uint64_t bcast_bytes_rx; + uint64_t tx_ucast_bytes; + uint64_t rx_ucast_bytes; + uint64_t tx_mcast_bytes; + uint64_t rx_mcast_bytes; + uint64_t tx_bcast_bytes; + uint64_t rx_bcast_bytes; } map_ap_extended_metrics_t; +typedef struct map_ap_eht_operations_s { + uint8_t eht_op_info_valid: 1; + uint8_t disabled_subchan_valid: 1; + uint8_t eht_default_pe_duration: 1; + uint8_t group_addr_bu_indication_limit: 1; + uint8_t group_addr_bu_indication_exp: 2; + uint8_t reserved: 2; + uint32_t basic_eht_mcs_nss; + uint8_t control; + uint8_t ccfs0; + uint8_t ccfs1; + uint16_t disabled_subchan_bitmap; +} map_ap_eht_operations_t; + /*####################################################################### # BSS INFO # ########################################################################*/ @@ -287,6 +328,7 @@ typedef struct map_bss_info_s { int dm_idx; bool dm_removed; + uint8_t ssid_len; char ssid[MAX_SSID_LEN]; uint16_t state; /* active/configured/wps_enabled */ @@ -296,7 +338,25 @@ typedef struct map_bss_info_s { array_list_t *neigh_link_metric_list; map_ap_metric_t metrics; map_ap_extended_metrics_t extended_metrics; + map_ap_eht_operations_t eht_ops; + struct { + uint8_t backhaul_bss: 1; + uint8_t fronthaul_bss: 1; + uint8_t r1_disallowed_status: 1; + uint8_t r2_disallowed_status: 1; + uint8_t multiple_bssid: 1; + uint8_t transmitted_bssid: 1; + uint8_t reserved: 2; + } flags; struct map_radio_info_s *radio; + + /* MLO */ + int dm_aa_idx; + bool dm_aa_removed; + list_head_t aff_ap_list; + struct map_ap_mld_info_s *ap_mld; /* Set in case this is an affiliated AP */ + struct map_ap_mld_info_s *prev_ap_mld; /* Needed for dm update */ + uint8_t link_id; } map_bss_info_t; /*####################################################################### @@ -308,16 +368,16 @@ typedef struct map_bss_info_s { - Controller channel preference - Scan capability op class list - CAC method op clas list - - Wi-Fi 6 capabilities - Fields op_class, channel_count, channel_list are used for all - Fields eirp, pref, reason for some + Fields op_class and channels are used for all + Fields eirp, pref, reason, enable for some */ typedef struct map_op_class_s { uint8_t op_class; uint8_t eirp; /* Radio basic capabilities */ uint8_t pref; /* Agent and controller channel preference */ uint8_t reason; /* Agent and controller channel preference */ + bool enable; /* Used only for disallowed op class list */ map_channel_set_t channels; } map_op_class_t; @@ -332,9 +392,9 @@ typedef struct map_channel_restriction_s { } map_channel_restriction_t; typedef struct map_op_restriction_s { - uint8_t op_class; - uint8_t channel_count; - map_channel_restriction_t channel_list[MAX_CHANNEL_PER_OP_CLASS]; + uint8_t op_class; + uint8_t channels_nr; + map_channel_restriction_t *channels; } map_op_restriction_t; typedef struct map_op_restriction_list_s { @@ -371,6 +431,12 @@ typedef struct map_agent_capablity_s { /* Metric collection interval TLV */ uint32_t metric_collection_interval; + + /* Wi-Fi 7 Agent Capability TLV */ + uint8_t max_mlds; + uint8_t ap_max_links; + uint8_t bsta_max_links; + uint8_t tid_to_link_map_cap; } map_agent_capability_t; typedef struct map_radio_metrics_s { @@ -480,6 +546,7 @@ typedef struct map_radio_wifi6_capabilities_s { } map_radio_wifi6_caps_t; typedef struct { + bool cloud_mgmt_enable; /* Cloud channel managment (apply acs_channels) */ bool acs_enable; /* ACS enabled or not */ map_channel_set_t acs_channels; /* List of channels ACS may use */ uint8_t channel; /* Fixed channel to be used when acs_enable = false */ @@ -489,6 +556,33 @@ typedef struct { map_channel_set_t pref_channels; } map_radio_chan_sel_t; +typedef struct { + mac_addr ruid; + uint8_t freq_separation: 5; + uint8_t reserved: 3; +} map_radio_wifi7_records_t; + +typedef struct map_radio_wifi7_capabilities_s { + map_mld_modes_t ap_mld_modes; + map_mld_modes_t bsta_mld_modes; + uint8_t ap_str_records_nr; + map_radio_wifi7_records_t *ap_str_records; + uint8_t ap_nstr_records_nr; + map_radio_wifi7_records_t *ap_nstr_records; + uint8_t ap_emlsr_records_nr; + map_radio_wifi7_records_t *ap_emlsr_records; + uint8_t ap_emlmr_records_nr; + map_radio_wifi7_records_t *ap_emlmr_records; + uint8_t bsta_str_records_nr; + map_radio_wifi7_records_t *bsta_str_records; + uint8_t bsta_nstr_records_nr; + map_radio_wifi7_records_t *bsta_nstr_records; + uint8_t bsta_emlsr_records_nr; + map_radio_wifi7_records_t *bsta_emlsr_records; + uint8_t bsta_emlmr_records_nr; + map_radio_wifi7_records_t *bsta_emlmr_records; +} map_radio_wifi7_caps_t; + /*####################################################################### # RADIO INFO # ########################################################################*/ @@ -502,6 +596,7 @@ typedef struct map_unassociated_sta_info_s { typedef struct map_radio_info_s { mac_addr radio_id; mac_addr_str radio_id_str; + char radio_id_base64[9]; /* This is used in Data elements (len = 6 * 4 / 3 + 1) */ list_head_t list; list_head_t bss_list; @@ -509,11 +604,14 @@ typedef struct map_radio_info_s { int dm_idx; bool dm_removed; + uint8_t supported_freq; + uint16_t auth_modes_flag; /* Regular supported auth modes*/ uint16_t band_type_5G; uint8_t max_bss; uint8_t current_op_class; uint8_t current_op_channel; + uint8_t highest_bw_channel; /* Used to obtain secondary channel offset in 320MHz */ uint16_t current_bw; uint8_t current_tx_pwr; /* From operating channel report */ uint8_t tx_pwr_limit; /* Used in channel selection request when not 0 */ @@ -524,15 +622,18 @@ typedef struct map_radio_info_s { map_radio_ht_capability_t *ht_caps; map_radio_vht_capability_t *vht_caps; map_radio_he_capability_t *he_caps; + map_radio_eht_capability_t *eht_caps; map_radio_scan_capability_t scan_caps; map_radio_cac_capability_t cac_caps; map_radio_wifi6_caps_t *wifi6_caps; + map_radio_wifi7_caps_t *wifi7_caps; char vendor[MAP_INVENTORY_ITEM_LEN + 1]; map_op_class_list_t curr_op_class_list; /* Current operating classes (operating channel report TLV) */ map_op_class_list_t cap_op_class_list; /* Radio basic capabilities TLV */ map_op_class_list_t pref_op_class_list; /* Agent preference from channel preference report */ map_op_class_list_t ctrl_pref_op_class_list; /* Controller preference to be set in channel selection request */ + map_op_class_list_t disallowed_op_class_list; /* Disallowed opclass list, empty channel list means all allowed */ map_op_class_list_t merged_pref_op_class_list; /* Controller and agent merged preference */ map_op_restriction_list_t op_restriction_list; /* Radio operating restriction */ @@ -553,12 +654,92 @@ typedef struct map_radio_info_s { uint16_t unassoc_sta_list_idx; } map_radio_info_t; +/*####################################################################### +# STA MLD INFO # +########################################################################*/ +typedef struct map_sta_mld_info_s { + mac_addr mac; + mac_addr_str mac_str; + + int dm_idx; + bool dm_removed; + bool marked; + + list_head_t list; + list_head_t hlist; + list_head_t aff_sta_list; /* map_sta_info_t */ + size_t aff_sta_nr; + + struct map_ap_mld_info_s *ap_mld; + + map_mld_modes_t supported_mld_modes; + map_mld_modes_t enabled_mld_modes; + + uint64_t assoc_ts; + uint16_t assoc_frame_len; + uint8_t *assoc_frame; + + map_sta_traffic_stats_t traffic_stats; + map_wifi6_sta_tid_info_t wifi6_sta_tid_info; +} map_sta_mld_info_t; + +/*####################################################################### +# AP MLD INFO # +########################################################################*/ +typedef struct map_ap_mld_info_s { + mac_addr mac; + mac_addr_str mac_str; + + struct map_ale_info_s *ale; + + list_head_t list; + list_head_t aff_ap_list; /* map_bss_info_t */ + size_t aff_ap_nr; + + list_head_t sta_mld_list; /* map_mld_sta_info_t */ + list_head_t sta_mld_hlist[MAP_MAX_MAC_HASH]; /* map_mld_sta_info_t */ + size_t sta_mld_nr; + + int dm_idx; + bool dm_removed; + + uint8_t ssid_len; + char ssid[MAX_SSID_LEN]; + + map_mld_modes_t enabled_mld_modes; +} map_ap_mld_info_t; + +/*####################################################################### +# BSTA MLD INFO # +########################################################################*/ +/* Notes: + - there can only be one per ALE + - this struct is less important at the moment -> just storing mac addresses +*/ +typedef struct map_bsta_mld_info_s { + bool valid; + + mac_addr mac; + mac_addr_str mac_str; + + struct map_ale_info_s *ale; + + mac_addr ap_mld_mac; /* ap_mld to which this bsta mld is connected. TODO: link? */ + + mac_addr aff_bsta_macs[MAX_MLD_AFF_APSTA]; + size_t aff_bsta_mac_nr; + + map_mld_modes_t enabled_mld_modes; +} map_bsta_mld_info_t; + /*####################################################################### # ALE INFO TYPES # ########################################################################*/ typedef enum { - MAP_PROFILE_1 = 1, /* Multiap Profile-1 */ - MAP_PROFILE_2 = 2 /* Multiap Profile-2 */ + MAP_PROFILE_1 = 1, /* Multiap Profile-1 */ + MAP_PROFILE_2 = 2, /* Multiap Profile-2 */ + MAP_PROFILE_3 = 3, /* Multiap Profile-3 */ + MAP_PROFILE_4P = 4 /* Multiap Profile-4+ */ } map_profile_t; typedef struct map_tx_metric_params_s { @@ -814,8 +995,15 @@ typedef struct map_dpp_message_s { uint8_t *frame; } map_dpp_message_t; +typedef struct map_bss_configuration_request_s { + uint16_t obj_len; + uint8_t *obj; +} map_bss_configuration_request_t; + typedef struct map_dpp_info_s { + bool onboarding_status; bool cce_advertised; + map_bss_configuration_request_t bss_config_req; map_dpp_chirp_t chirp; map_dpp_encap_msg_t encap_msg; map_dpp_encap_eapol_t encap_eapol; @@ -844,6 +1032,11 @@ typedef struct map_ale_info_s { list_head_t radio_list; uint8_t radios_nr; + list_head_t ap_mld_list; + uint8_t ap_mld_nr; + + map_bsta_mld_info_t bsta_mld; + mac_addr src_mac; /* Source mac used by ALE (mostly same as al_mac) */ bool removing; int dm_idx; @@ -855,6 +1048,7 @@ typedef struct map_ale_info_s { uint16_t state; uint8_t ale_bcu_status; map_onboard_status_t ale_onboard_status; /* onboarding status: holds flags to define different onboarding state */ + bool ale_onboarded_first_time; /* Indicator of being onboarded first time. Once it is set, never changes */ uint64_t ale_onboarding_time; /* Timestamp in seconds of ALE onboarding */ uint64_t keep_alive_time; /* Timestamp in seconds of last received topology response */ uint8_t first_chan_sel_req_done; @@ -884,10 +1078,16 @@ typedef struct map_ale_info_s { map_cac_status_report_t cac_status_report; map_emex_t emex; map_dpp_info_t dpp_info; + map_1905_sec_key_info_t key_info; map_device_inventory_t inventory; bool inventory_exists; map_eth_device_list_t eth_device_list; + mac_addr *btm_steering_disallow_macs; + size_t btm_steering_disallow_macs_nr; + mac_addr *local_steering_disallow_macs; + size_t local_steering_disallow_macs_nr; + map_sta_info_t *last_sta_steered; } map_ale_info_t; @@ -990,10 +1190,13 @@ int map_dm_remove_bss(map_bss_info_t *bss); /** Create new STA if not exist. */ map_sta_info_t *map_dm_create_sta(map_bss_info_t *bss, mac_addr mac); +/** Create new affiliated STA if not exist. */ +map_sta_info_t *map_dm_create_aff_sta(map_bss_info_t *bss, map_sta_mld_info_t *sta_mld, mac_addr mac); + /** Get STA from bss */ map_sta_info_t* map_dm_get_sta(map_bss_info_t *bss, mac_addr mac); -/** Get STA from ale */ +/** Get STA from ale */ map_sta_info_t* map_dm_get_sta_from_ale(map_ale_info_t *ale, mac_addr mac); /** Get sta global */ @@ -1002,6 +1205,39 @@ map_sta_info_t *map_dm_get_sta_gbl(mac_addr mac); /** Remove sta. */ int map_dm_remove_sta(map_sta_info_t *sta); +/** Create AP MLD */ +map_ap_mld_info_t* map_dm_create_ap_mld(map_ale_info_t *ale, mac_addr mld_mac); + +/** Get AP MLD from ale */ +map_ap_mld_info_t* map_dm_get_ap_mld(map_ale_info_t* ale, mac_addr mld_mac); + +/* Remove AP MLD */ +int map_dm_remove_ap_mld(map_ap_mld_info_t *ap_mld); + +/* Create STA MLD */ +map_sta_mld_info_t *map_dm_create_sta_mld(map_ap_mld_info_t *ap_mld, mac_addr mac); + +/* Get STA MLD from AP MLD */ +map_sta_mld_info_t *map_dm_get_sta_mld(map_ap_mld_info_t *ap_mld, mac_addr mac); + +/* Get STA MLD from ALE */ +map_sta_mld_info_t *map_dm_get_sta_mld_from_ale(map_ale_info_t *ale, mac_addr mac); + +/* Get STA MLD global */ +map_sta_mld_info_t *map_dm_get_sta_mld_gbl(mac_addr mac); + +/* Get AFF STA from STA MLD */ +map_sta_info_t *map_dm_get_aff_sta(map_sta_mld_info_t *sta_mld, mac_addr mac); + +/* Remove STA MLD */ +int map_dm_remove_sta_mld(map_sta_mld_info_t *sta_mld); + +/* Clean-up stored key information of ale */ +void map_dm_remove_ale_key_info(map_ale_info_t *ale); + +/* Get secure 1905 key information of ale */ +int map_dm_get_ale_key_info(mac_addr al_mac, map_1905_sec_key_info_t *key_info); + /** Link sta to other bss * * DEPRECATED. Should keep sta per BSS and not move between @@ -1021,6 +1257,8 @@ map_failconn_data_t *map_dm_create_failconn(mac_addr sta_mac, mac_addr bssid, ui # VARIOUS # ########################################################################*/ /** Remove cac methods info if exist. */ +void map_dm_free_op_restriction_list(map_radio_info_t *radio); + void map_dm_free_cac_methods(map_cac_method_t *cac_method, uint8_t count); void map_dm_free_non_1905_neighbor_list(map_ale_info_t *ale); @@ -1035,8 +1273,16 @@ void map_dm_get_radio_timer_id(timer_id_t id, map_radio_info_t *radio, const cha void map_dm_get_sta_timer_id(timer_id_t id, map_sta_info_t *sta, const char *type); +void map_dm_get_sta_mld_timer_id(timer_id_t id, map_sta_mld_info_t *sta, const char *type); + bool map_dm_is_inactive_sta(mac_addr mac); +void map_dm_clear_dpp_hash_list(void); + +int map_dm_add_dpp_hash(const uint8_t *hash); + +bool map_dm_get_dpp_hash(const uint8_t *hash); + #define map_dm_is_bh_sta(sta) (sta->sta_caps.backhaul_sta) #define map_dm_is_mbo_sta(sta) (sta->sta_caps.mbo_support) @@ -1045,9 +1291,13 @@ bool map_dm_is_inactive_sta(mac_addr mac); #define map_dm_get_sta_assoc_ts_delta2(ts, assoc_ts) (ts + MAP_ASSOC_TS_DELTA - assoc_ts) -#define map_dm_mark_sta(sta) do { sta->marked = true; } while(0) -#define map_dm_unmark_sta(sta) do { sta->marked = false; } while(0) -#define map_dm_is_marked_sta(sta) (sta->marked) +#define map_dm_mark_sta(sta) do { sta->marked = true; } while(0) +#define map_dm_unmark_sta(sta) do { sta->marked = false; } while(0) +#define map_dm_is_marked_sta(sta) (sta->marked) + +#define map_dm_mark_sta_mld(sta_mld) do { sta_mld->marked = true; } while(0) +#define map_dm_unmark_sta_mld(sta_mld) do { sta_mld->marked = false; } while(0) +#define map_dm_is_marked_sta_mld(sta_mld) (sta_mld->marked) /* Mark all sta of an ale */ void map_dm_mark_stas(map_ale_info_t *ale); @@ -1055,42 +1305,67 @@ void map_dm_mark_stas(map_ale_info_t *ale); /* Remove sta that are marked and where associated longer than "min_assoc_time */ void map_dm_remove_marked_stas(map_ale_info_t *ale, unsigned int min_assoc_time); +/* Mark all sta mld of an ale */ +void map_dm_mark_sta_mlds(map_ale_info_t *ale); + +/* Remove sta mld that are marked */ +void map_dm_remove_marked_sta_mlds(map_ale_info_t *ale); + +/* Check if this als has MLD devices */ +#define map_dm_ale_has_mld(ale) (ale->ap_mld_nr > 0) + /*####################################################################### # ITERATORS # ########################################################################*/ extern map_ale_info_t* get_root_ale_node(); /* ALEs */ -#define map_dm_foreach_ale(ale) list_for_each_entry(ale, map_dm_get(), list) -#define map_dm_foreach_ale_safe(ale, next) list_for_each_entry_safe(ale, next, map_dm_get(), list) +#define map_dm_foreach_ale(ale) list_for_each_entry(ale, map_dm_get(), list) +#define map_dm_foreach_ale_safe(ale, next) list_for_each_entry_safe(ale, next, map_dm_get(), list) /* Agent ALEs (so skip controller) */ -#define map_dm_foreach_agent_ale(ale) list_for_each_entry(ale, map_dm_get(), list) if (ale != get_root_ale_node()) -#define map_dm_foreach_agent_ale_safe(ale, next) list_for_each_entry_safe(ale, next, map_dm_get(), list) if (ale != get_root_ale_node()) +#define map_dm_foreach_agent_ale(ale) list_for_each_entry(ale, map_dm_get(), list) if (ale != get_root_ale_node()) +#define map_dm_foreach_agent_ale_safe(ale, next) list_for_each_entry_safe(ale, next, map_dm_get(), list) if (ale != get_root_ale_node()) /* Radios */ -#define map_dm_foreach_radio(ale, radio) list_for_each_entry(radio, &ale->radio_list, list) -#define map_dm_foreach_radio_safe(ale, radio, next) list_for_each_entry_safe(radio, next, &ale->radio_list, list) +#define map_dm_foreach_radio(ale, radio) list_for_each_entry(radio, &ale->radio_list, list) +#define map_dm_foreach_radio_safe(ale, radio, next) list_for_each_entry_safe(radio, next, &ale->radio_list, list) /* Bsss */ -#define map_dm_foreach_bss(radio, bss) list_for_each_entry(bss, &radio->bss_list, list) -#define map_dm_foreach_bss_safe(radio, bss, next) list_for_each_entry_safe(bss, next, &radio->bss_list, list) +#define map_dm_foreach_bss(radio, bss) list_for_each_entry(bss, &radio->bss_list, list) +#define map_dm_foreach_bss_safe(radio, bss, next) list_for_each_entry_safe(bss, next, &radio->bss_list, list) /* Stas */ -#define map_dm_foreach_sta(bss, sta) list_for_each_entry(sta, &bss->sta_list, list) -#define map_dm_foreach_sta_safe(bss, sta, next) list_for_each_entry_safe(sta, next, &bss->sta_list, list) +#define map_dm_foreach_sta(bss, sta) list_for_each_entry(sta, &bss->sta_list, list) +#define map_dm_foreach_sta_safe(bss, sta, next) list_for_each_entry_safe(sta, next, &bss->sta_list, list) + +/* AP MLD's */ +#define map_dm_foreach_ap_mld(ale, ap_mld) list_for_each_entry(ap_mld, &ale->ap_mld_list, list) +#define map_dm_foreach_ap_mld_safe(ale, ap_mld, next) list_for_each_entry_safe(ap_mld, next, &ale->ap_mld_list, list) + +/* STA MLD's */ +#define map_dm_foreach_sta_mld(ap_mld, sta_mld) list_for_each_entry(sta_mld, &ap_mld->sta_mld_list, list) +#define map_dm_foreach_sta_mld_safe(ap_mld, sta_mld, next) list_for_each_entry_safe(sta_mld, next, &ap_mld->sta_mld_list, list) + +/* Affiliated AP's from AP MLD */ +#define map_dm_foreach_aff_ap(ap_mld, bss) list_for_each_entry(bss, &ap_mld->aff_ap_list, aff_ap_list) +#define map_dm_foreach_aff_ap_safe(ap_mld, bss, next) list_for_each_entry_safe(bss, next, &ap_mld->aff_ap_list, aff_ap_list) + +/* Affiliated STA's from STA MLD */ +#define map_dm_foreach_aff_sta(sta_mld, sta) list_for_each_entry(sta, &sta_mld->aff_sta_list, aff_sta_list) +#define map_dm_foreach_aff_sta_safe(sta_mld, sta, next) list_for_each_entry_safe(sta, next, &sta_mld->aff_sta_list, aff_sta_list) /* Assoc Events */ -#define map_dm_foreach_assoc(assoc) list_for_each_entry(assoc, &map_dm_get_events()->assoc_list, list) -#define map_dm_foreach_assoc_safe(assoc, next) list_for_each_entry_safe(assoc, next, &map_dm_get_events()->assoc_list, list) +#define map_dm_foreach_assoc(assoc) list_for_each_entry(assoc, &map_dm_get_events()->assoc_list, list) +#define map_dm_foreach_assoc_safe(assoc, next) list_for_each_entry_safe(assoc, next, &map_dm_get_events()->assoc_list, list) /* Disassoc Events */ -#define map_dm_foreach_disassoc(disasoc) list_for_each_entry(disasoc, &map_dm_get_events()->disassoc_list, list) -#define map_dm_foreach_disassoc_safe(disasoc, next) list_for_each_entry_safe(disasoc, next, &map_dm_get_events()->disassoc_list, list) +#define map_dm_foreach_disassoc(disasoc) list_for_each_entry(disasoc, &map_dm_get_events()->disassoc_list, list) +#define map_dm_foreach_disassoc_safe(disasoc, next) list_for_each_entry_safe(disasoc, next, &map_dm_get_events()->disassoc_list, list) /* Failconn Events */ -#define map_dm_foreach_failconn(failcon) list_for_each_entry(failconn, &map_dm_get_events()->failconn_list, list) -#define map_dm_foreach_failconn_safe(failcon, next) list_for_each_entry_safe(failcon, next, &map_dm_get_events()->failconn_list, list) +#define map_dm_foreach_failconn(failcon) list_for_each_entry(failconn, &map_dm_get_events()->failconn_list, list) +#define map_dm_foreach_failconn_safe(failcon, next) list_for_each_entry_safe(failcon, next, &map_dm_get_events()->failconn_list, list) /*####################################################################### # DM UPDATE CALLBACKS AND FUNCTIONS # @@ -1117,10 +1392,19 @@ typedef struct { void (*bss_update_cb)(map_bss_info_t *bss); void (*bss_remove_cb)(map_bss_info_t *bss); + void (*ap_mld_create_cb)(map_ap_mld_info_t *ap_mld); + void (*ap_mld_update_cb)(map_ap_mld_info_t *ap_mld); + void (*ap_mld_remove_cb)(map_ap_mld_info_t *ap_mld); + + void (*bsta_mld_update_cb)(map_bsta_mld_info_t *bsta_mld); + void (*sta_create_cb)(map_sta_info_t *sta); void (*sta_update_cb)(map_sta_info_t *sta); void (*sta_remove_cb)(map_sta_info_t *sta); + void (*sta_mld_create_cb)(map_sta_mld_info_t *sta_mld); + void (*sta_mld_remove_cb)(map_sta_mld_info_t *sta_mld); + void (*assoc_create_cb)(map_assoc_data_t *assoc); void (*assoc_remove_cb)(map_assoc_data_t *assoc); @@ -1161,11 +1445,15 @@ void map_dm_radio_set_capabilities(map_radio_info_t *radio); /* Update scan results */ void map_dm_radio_scan_result(map_radio_info_t *radio); +/* Update channel configurable */ +void map_dm_radio_set_channel_configurable(map_radio_info_t *radio, bool channel_configurable); + /* Update channel related parameters */ -void map_dm_radio_set_channel(map_radio_info_t *radio, uint8_t op_class, uint8_t channel, uint16_t bw, uint8_t tx_pwr); +void map_dm_radio_set_channel(map_radio_info_t *radio, uint8_t op_class, uint8_t channel, uint8_t highest_bw_channel, uint16_t bw, uint8_t tx_pwr); /* Update channel selection data */ -void map_dm_radio_set_chan_sel(map_radio_info_t *radio, bool acs_enable, map_channel_set_t *acs_channels, uint8_t channel, uint16_t bw); +void map_dm_radio_set_chan_sel(map_radio_info_t *radio, bool cloud_mgmt_enable, bool acs_enable, + map_channel_set_t *acs_channels, uint8_t channel, uint16_t bw); /* Update ssid and bss type */ void map_dm_bss_set_ssid(map_bss_info_t *bss, size_t ssid_len, uint8_t *ssid, int bss_type); @@ -1179,9 +1467,49 @@ void map_dm_sta_steering_btm_report(map_sta_info_t *sta, uint8_t status_code, ma void map_dm_sta_steering_completed(map_ale_info_t *ale); +/* Update bss ap_mld link */ +void map_dm_bss_set_ap_mld(map_bss_info_t *bss, map_ap_mld_info_t *ap_mld); + +/* Update MLD */ +typedef struct { + map_bss_info_t *bss; + uint8_t link_id; +} map_aff_ap_cfg_t; + +void map_dm_ap_mld_set(map_ap_mld_info_t *ap_mld, size_t ssid_len, uint8_t *ssid, + bool str, bool nstr, bool emlsr, bool emlmr, + map_aff_ap_cfg_t *aff_aps, size_t aff_ap_nr); + +void map_dm_bsta_mld_set(map_bsta_mld_info_t *bsta_mld, bool valid, + mac_addr bsta_mld_mac, mac_addr ap_mld_mac, + bool str, bool nstr, bool emlsr, bool emlmr, + mac_addr *aff_bsta_macs, size_t aff_bsta_mac_nr); + /*####################################################################### # DM NBAPI CALLBACKS AND FUNCTIONS # ########################################################################*/ +#define MAX_OP_CLASS 48 + +/* Steer WiFi backhaul parameters */ +typedef struct map_nb_steer_wifi_bh_param_s { + mac_addr bsta_mac; + mac_addr target_bssid; + uint32_t timeout; + uint8_t op_class; + uint8_t channel; +} map_nb_steer_wifi_bh_param_t; + +typedef int (*map_nb_steer_wifi_bh_cb)(map_ale_info_t *ale, map_nb_steer_wifi_bh_param_t *payload); + +typedef int (*map_nb_set_steering_policy_cb)(map_ale_info_t *ale); + +/* Reset reboot parameters */ +typedef struct map_nb_reset_reboot_param_s { + uint8_t is_reset; + uint8_t factory_reset; +} map_nb_reset_reboot_param_t; + +typedef int (*map_nb_reset_reboot_cb)(map_ale_info_t *ale, map_nb_reset_reboot_param_t *payload); /* Channel scan parameters */ typedef struct map_nb_ch_scan_param_s { @@ -1194,7 +1522,14 @@ typedef struct map_nb_ch_scan_param_s { map_op_class_t op_classes[MAX_OP_CLASS]; } map_nb_ch_scan_param_t; -typedef void (*map_nb_ch_scan_cb)(map_ale_info_t *ale, map_nb_ch_scan_param_t *payload); +typedef int (*map_nb_ch_scan_cb)(map_ale_info_t *ale, map_nb_ch_scan_param_t *payload); + +/* Channel selection parameters */ +typedef struct map_nb_ch_selection_param_s { + mac_addr radio_id; +} map_nb_ch_selection_param_t; + +typedef int (*map_nb_ch_selection_cb)(map_ale_info_t *ale, map_nb_ch_selection_param_t *payload); /* Query beacon metrics parameters */ typedef struct map_nb_bmquery_param_s { @@ -1210,7 +1545,7 @@ typedef struct map_nb_bmquery_param_s { uint8_t element_ids[255]; } map_nb_bmquery_param_t; -typedef void (*map_nb_sta_bmquery_cb)(map_ale_info_t *ale, map_nb_bmquery_param_t *payload); +typedef int (*map_nb_sta_bmquery_cb)(map_ale_info_t *ale, map_nb_bmquery_param_t *payload); /* ClientSteer() parameters */ @@ -1275,18 +1610,23 @@ typedef struct map_nb_assoc_control_params_s { int period; } map_nb_assoc_control_params_t; -typedef void (*map_nb_sta_client_steer_cb)(map_ale_info_t *ale, map_nb_client_steer_params_t *payload); +typedef int (*map_nb_sta_client_steer_cb)(map_ale_info_t *ale, map_nb_client_steer_params_t *payload); -typedef void (*map_nb_mapsta_disassociate_cb)(map_ale_info_t *ale, map_nb_sta_disassociate_params_t *payload); +typedef int (*map_nb_mapsta_disassociate_cb)(map_ale_info_t *ale, map_nb_sta_disassociate_params_t *payload); typedef int (*map_nb_unassoc_sta_link_metrics_query_cb)(map_ale_info_t *ale, map_nb_unassoc_sta_link_metrics_query_params_t *payload); typedef int (*map_nb_unassoc_sta_link_metrics_response_cb)(map_ale_info_t *ale, map_nb_unassoc_sta_link_metrics_response_t *payload); typedef void (*map_nb_assoc_control_cb)(map_ale_info_t *ale, map_nb_assoc_control_params_t *payload); + /* Northbound API callbacks */ typedef struct { + map_nb_steer_wifi_bh_cb steer_wifi_backhaul; + map_nb_set_steering_policy_cb set_steering_policy; + map_nb_reset_reboot_cb reset_reboot; map_nb_ch_scan_cb channel_scan; + map_nb_ch_selection_cb channel_selection; map_nb_sta_bmquery_cb beacon_metrics_query; map_nb_sta_client_steer_cb client_steer; map_nb_mapsta_disassociate_cb mapsta_disassociate; diff --git a/source/libplatform/include/map_data_model_dumper.h b/source/libplatform/include/map_data_model_dumper.h index f20fb66..67e200f 100644 --- a/source/libplatform/include/map_data_model_dumper.h +++ b/source/libplatform/include/map_data_model_dumper.h @@ -36,4 +36,9 @@ void map_dm_dump_agent_info_tree(map_printf_cb_t print_cb); */ void map_dm_dump_tunneled_messages(map_printf_cb_t print_cb, uint8_t *sta_mac, uint8_t type); +/** @brief Dump mld structure + * + */ +void map_dm_dump_mld(map_printf_cb_t print_cb); + #endif /* MAP_DATA_MODEL_DUMPER_H_ */ diff --git a/source/libplatform/include/map_info.h b/source/libplatform/include/map_info.h index 12d43ae..bb98513 100644 --- a/source/libplatform/include/map_info.h +++ b/source/libplatform/include/map_info.h @@ -28,6 +28,7 @@ #define MAP_RADIO_OOB_UNASSOC_MEASUREMENT_SUPPORTED 0x02 #define MAP_ONBOARD_DEP_BITMASK (MAP_RADIO_M1_RECEIVED | MAP_RADIO_CONFIGURED | MAP_RADIO_OPER_CHAN_REPORT_RECEIVED | MAP_RADIO_POLICY_CONFIG_ACK_RECEIVED | MAP_RADIO_AP_CAPABILITY_REPORT_RECEIVED) +#define MAP_ONBOARD_DEP_BITMASK_TEARDOWN (MAP_RADIO_M1_RECEIVED | MAP_RADIO_CONFIGURED | MAP_RADIO_POLICY_CONFIG_ACK_RECEIVED | MAP_RADIO_AP_CAPABILITY_REPORT_RECEIVED) typedef enum _map_bss_states { MAP_BSS_ACTIVE = 0x0001, @@ -47,6 +48,7 @@ typedef enum _map_radio_states { MAP_RADIO_AP_CAPABILITY_REPORT_RECEIVED = 0x0400, MAP_RADIO_INITIAL_SCAN_RESULTS_RECEIVED = 0x0800, MAP_RADIO_CHANNEL_PREFERENCE_QUERY_SENT = 0x1000, + MAP_RADIO_TEARDOWN_SENT = 0x2000, } map_radio_states_t; typedef enum _map_ale_states { @@ -78,18 +80,14 @@ static inline int is_state_bit_set(uint16_t state, uint16_t bit) #define is_bss_active(bss_state) (is_state_bit_set(bss_state,MAP_BSS_ACTIVE)) /* Radio state macros */ -#define MUTUALLY_EXCLUSIVE_RADIO_STATES (MAP_RADIO_CONFIGURED | MAP_RADIO_M1_SENT | \ - MAP_RADIO_M1_RECEIVED | MAP_RADIO_M2_SENT) +#define MUTUALLY_EXCLUSIVE_RADIO_STATES (MAP_RADIO_CONFIGURED | MAP_RADIO_M1_RECEIVED | \ + MAP_RADIO_M2_SENT | MAP_RADIO_TEARDOWN_SENT) #define set_radio_state_on(radio_state) (set_state_bit(radio_state,MAP_RADIO_ON)) #define set_radio_state_configured(radio_state) {\ set_state_bit(radio_state,MAP_RADIO_CONFIGURED);\ } -#define set_radio_state_M1_sent(radio_state) { \ - reset_state_bit(radio_state, MUTUALLY_EXCLUSIVE_RADIO_STATES);\ - set_state_bit(radio_state,MAP_RADIO_M1_SENT); \ - } #define set_radio_state_M1_receive(radio_state) { \ reset_state_bit(radio_state, MUTUALLY_EXCLUSIVE_RADIO_STATES);\ @@ -100,6 +98,10 @@ static inline int is_state_bit_set(uint16_t state, uint16_t bit) set_state_bit(radio_state,MAP_RADIO_M2_SENT); \ } +#define set_radio_state_teardown_sent(radio_state) { \ + set_state_bit(radio_state,MAP_RADIO_TEARDOWN_SENT); \ + } + #define set_radio_state_policy_config_ack_received(radio_state) \ (set_state_bit(radio_state, MAP_RADIO_POLICY_CONFIG_ACK_RECEIVED)) #define set_radio_state_oper_chan_report_received(radio_state) \ @@ -140,9 +142,9 @@ static inline int is_state_bit_set(uint16_t state, uint16_t bit) #define is_radio_on(radio_state) (is_state_bit_set(radio_state,MAP_RADIO_ON)) #define is_radio_freq_supported(radio_state) (is_state_bit_set(radio_state,MAP_RADIO_FREQUENCY_SUPPORTED)) #define is_radio_configured(radio_state) (is_state_bit_set(radio_state,MAP_RADIO_CONFIGURED)) -#define is_radio_M1_sent(radio_state) (is_state_bit_set(radio_state,MAP_RADIO_M1_SENT)) #define is_radio_M1_received(radio_state) (is_state_bit_set(radio_state,MAP_RADIO_M1_RECEIVED)) #define is_radio_M2_sent(radio_state) (is_state_bit_set(radio_state,MAP_RADIO_M2_SENT)) +#define is_radio_teardown_sent(radio_state) (is_state_bit_set(radio_state,MAP_RADIO_TEARDOWN_SENT)) #define is_unassoc_measurement_inprogress(radio_state) (is_state_bit_set(radio_state,MAP_RADIO_UNASSOC_MEASUREMENT_IN_PROGRESS)) #define is_unassoc_measurement_supported(radio_state) (is_ib_unassoc_measurement_supported(radio_state) || is_oob_unassoc_measurement_supported(radio_state)) @@ -198,20 +200,29 @@ bool map_is_5g_low_op_class(uint8_t op_class); /* Check if operating class is 5G high band */ bool map_is_5g_high_op_class(uint8_t op_class); +/* Check if operating class is 80 + 80 */ +bool map_is_80p80_op_class(uint8_t op_class); + +/* Check if op_class is 6G 320MHz */ +bool map_is_6G_320MHz_op_class(uint8_t op_class); + /* Check if channel is in operating class */ -bool map_is_channel_in_op_class(uint8_t op_class, uint8_t channel); +bool map_is_channel_in_op_class(uint8_t op_class, uint8_t ctl_or_center_channel); /* Get center channel from ctl_channel for operating classes that use center channels */ int map_get_center_channel(uint8_t op_class, uint8_t ctl_channel, uint8_t *center_channel); -/* Get first ctl channel from center_channel for op_class that uses center channels */ -int map_get_first_ctl_channel(uint8_t op_class, uint8_t center_channel, uint8_t *ctl_channel); +/* Get center channel from ctl_channel for 6G 320MHz operating class */ +int map_get_center_channel_6G_320MHz(uint8_t op_class, bool upper, uint8_t ctl_channel, uint8_t *center_channel); /* Get extension channel type */ int map_get_ext_channel_type(uint8_t op_class); -/* Get 20MHz subband range (only works for 80 and 160MHz) */ -int map_get_subband_channel_range(uint8_t op_class, uint8_t channel, uint8_t *from, uint8_t *to); +/* Get 20MHz subband range */ +int map_get_subband_channel_range(uint8_t op_class, uint8_t ctl_channel, uint8_t *from, uint8_t *to); + +/* Get 20MHz subband range for 6G 320MHz operating class */ +int map_get_subband_channel_range_6G_320MHz(uint8_t op_class, bool upper, uint8_t ctl_channel, uint8_t *from, uint8_t *to); /* Get bandwidth from operating class */ int map_get_bw_from_op_class(uint8_t op_class, uint16_t *bw); diff --git a/source/libplatform/include/map_timer_handler.h b/source/libplatform/include/map_timer_handler.h index 0dd2e90..10faeef 100644 --- a/source/libplatform/include/map_timer_handler.h +++ b/source/libplatform/include/map_timer_handler.h @@ -38,7 +38,7 @@ int map_timer_handler_init(void); * This will register a new timer call back * * @param - * frequency_ms - Frequency of the callback + * frequency_sec - Frequency of the callback in seconds * timer_id - pointer to char will be filled unique timer id for later usage. * args - Callback args * cb - Function call back to be called upon timer expiry @@ -50,6 +50,22 @@ int map_timer_register_callback(uint32_t frequency_sec, void *args, timer_cb_t cb); +/** @brief Register timer callback with more precision + * + * This will register a new timer call back with more precision + * + * @param + * frequency_ms - Frequency of the callback in miliseconds + * timer_id - pointer to char will be filled unique timer id for later usage. + * args - Callback args + * cb - Function call back to be called upon timer expiry + * + * @return -1 or error, 0 on success + */ +int map_timer_register_callback_ms(uint32_t frequency_ms, + const char *timer_id, + void *args, + timer_cb_t cb); /** @brief Check if the timer id is registered. * diff --git a/source/libplatform/include/map_utils.h b/source/libplatform/include/map_utils.h index a25876f..042ad52 100644 --- a/source/libplatform/include/map_utils.h +++ b/source/libplatform/include/map_utils.h @@ -50,6 +50,7 @@ #define STRUCT_PACKED __attribute__ ((packed)) +#define foreach_bool(b) bool b; int __##b; for (__##b = 0, b = false; __##b < 2; __##b++, b = __##b) /* BIT MASKs */ #define BIT_MASK_7 0x80 @@ -189,6 +190,10 @@ void map_vlog_ext(int module, int level, bool check_level, const char *format, v #define log_test_d(...) map_log(MAP_TEST, LOG_DEBUG, "[test]" _LOG_TAG " " __VA_ARGS__); #define log_test_t(...) map_log(MAP_TEST, LOG_TRACE, "[test]" _LOG_TAG " " __VA_ARGS__); +#define log_ctrl_n_cond(cond, ...) \ + if ((cond)) { \ + log_ctrl_n(__VA_ARGS__) \ + } /* Ebtables */ int map_set_ebtables_rules(mac_addr al_mac); diff --git a/source/libplatform/src/acu_utils.c b/source/libplatform/src/acu_utils.c index f6da7ba..78de751 100644 --- a/source/libplatform/src/acu_utils.c +++ b/source/libplatform/src/acu_utils.c @@ -172,6 +172,69 @@ bool acu_mac_array_equal(mac_addr *macs1, size_t macs_nr1, mac_addr *macs2, size return (macs_nr1 == macs_nr2) && !memcmp(macs1, macs2, macs_nr1 * sizeof(mac_addr)); } +int acu_mac_add_to_array(mac_addr mac, mac_addr **macs, size_t *macs_nr) +{ + mac_addr *array; + size_t count; + + if (!macs || !macs_nr || *macs_nr == SIZE_MAX) { + return ACU_EINVAL; + } + + count = *macs_nr + 1; + array = (mac_addr *)reallocarray(*macs, count, sizeof(mac_addr)); + if (array == NULL) { + return ACU_ENOMEM; + } + + acu_maccpy(&array[count - 1], mac); + *macs = array; + *macs_nr = count; + + return ACU_OK; +} + +int acu_mac_del_from_array(mac_addr mac, mac_addr **macs, size_t *macs_nr) +{ + mac_addr *array; + size_t count; + size_t i = 0; + bool found = false; + + if (!macs || !macs_nr || *macs_nr == 0) { + return ACU_EINVAL; + } + + array = *macs; + count = *macs_nr - 1; + do { + if (!acu_maccmp(mac, array[i])) { + found = true; + break; + } + ++i; + } while (i < *macs_nr); + + if (!found) { + return ACU_OK; + } + + while (i < count) { + acu_maccpy(array[i], array[i + 1]); + ++i; + } + + array = (mac_addr *)reallocarray(*macs, count, sizeof(mac_addr)); + if (array == NULL && count > 0) { + return ACU_ENOMEM; + } + + *macs = array; + *macs_nr = count; + + return ACU_OK; +} + /*####################################################################### # STRING OPERATIONS # ########################################################################*/ @@ -498,6 +561,7 @@ acu_retcode_t acu_evloop_timer_remaining(acu_evloop_timer_t *evloop_timer, return (r >= 0) ? ACU_OK : ACU_EINVAL; } +#ifndef UNIT_TEST void acu_evloop_fd_delete(acu_evloop_fd_t *evloop_fd) { if (NULL != evloop_fd) { @@ -505,6 +569,7 @@ void acu_evloop_fd_delete(acu_evloop_fd_t *evloop_fd) SFREE(evloop_fd); } } +#endif // UNIT_TEST acu_evloop_fd_t *acu_evloop_fd_add_ex(int fd, UNUSED unsigned flags, acu_evloop_fd_cb_t cb, void *userdata) diff --git a/source/libplatform/src/map_80211.c b/source/libplatform/src/map_80211.c index 0a34aa0..5b7718c 100644 --- a/source/libplatform/src/map_80211.c +++ b/source/libplatform/src/map_80211.c @@ -64,56 +64,72 @@ /* Information element Id's */ -#define IEEE80211_EID_SSID 0 -#define IEEE80211_EID_SUPP_RATES 1 -#define IEEE80211_EID_HT_CAP 45 -#define IEEE80211_EID_SUPP_EXT_RATES 50 -#define IEEE80211_EID_RRM_ENABLED_CAP 70 -#define IEEE80211_EID_EXT_CAP 127 -#define IEEE80211_EID_VHT_CAP 191 -#define IEEE80211_EID_VENDOR_SPECIFIC 221 -#define IEEE80211_EXTID_CAP 255 - -#define IEEE80211_EXTID_HE_CAP 35 - -#define IEEE80211_EID_HT_CAP_LEN sizeof(ieee80211_ht_cap) -#define IEEE80211_EID_RRM_ENABLED_CAP_LEN 5 -#define IEEE80211_EID_EXT_CAP_MIN_LEN 3 -#define IEEE80211_EID_VHT_CAP_LEN sizeof(ieee80211_vht_cap) -#define IEEE80211_EID_VENDOR_SPECIFIC_MIN_LEN 3 -#define IEEE80211_EXTID_MIN_LEN 1 - -#define IEEE80211_EXTID_HE_CAP_MIN_LEN sizeof(ieee80211_he_cap) +#define IEEE80211_EID_SSID 0 +#define IEEE80211_EID_SUPP_RATES 1 +#define IEEE80211_EID_HT_CAP 45 +#define IEEE80211_EID_SUPP_EXT_RATES 50 +#define IEEE80211_EID_RRM_ENABLED_CAP 70 +#define IEEE80211_EID_EXT_CAP 127 +#define IEEE80211_EID_VHT_CAP 191 +#define IEEE80211_EID_VENDOR_SPECIFIC 221 +#define IEEE80211_EID_FRAGMENT 242 +#define IEEE80211_EXTID_CAP 255 + +#define IEEE80211_EXTID_HE_CAP 35 +#define IEEE80211_EXTID_MULTI_LINK 107 +#define IEEE80211_EXTID_EHT_CAP 108 + +#define IEEE80211_MULTI_LINK_SEID_PER_STA_PROFILE 0 +#define IEEE80211_MULTI_LINK_SEID_FRAGMENT 254 + + +/* Minimum length of IEs */ +#define IEEE80211_EID_HT_CAP_LEN sizeof(ieee80211_ht_cap) +#define IEEE80211_EID_RRM_ENABLED_CAP_LEN 5 +#define IEEE80211_EID_EXT_CAP_MIN_LEN 3 +#define IEEE80211_EID_VHT_CAP_LEN sizeof(ieee80211_vht_cap) +#define IEEE80211_EID_VENDOR_SPECIFIC_MIN_LEN 3 +#define IEEE80211_EXTID_MIN_LEN 1 + +#define IEEE80211_EXTID_HE_CAP_MIN_LEN sizeof(ieee80211_he_cap) +#define IEEE80211_EXTID_MULTI_LINK_CTRL_LEN 2 +#define IEEE80211_EXTID_MULTI_LINK_MIN_LEN (IEEE80211_EXTID_MULTI_LINK_CTRL_LEN + 1) /* Control + info.len */ +#define IEEE80211_EXTID_EHT_CAP_MIN_LEN sizeof(ieee80211_eht_cap) + +#define IEEE80211_MULTI_LINK_SEID_PER_STA_PROFILE_MIN_LEN (IEEE80211_EXTID_MULTI_LINK_CTRL_LEN + 1) /* Control + info.len */ + + +/* Bit fields for various IEs */ /* Fixed capabiltiy bits */ -#define IEEE80211_CAP_RRM BIT(12) +#define IEEE80211_CAP_RRM BIT(12) /* RRM Enabled Capabilities IE */ /* Byte 1 */ -#define IEEE80211_RRM_CAPS_BEACON_REQUEST_PASSIVE BIT(4) -#define IEEE80211_RRM_CAPS_BEACON_REQUEST_ACTIVE BIT(5) +#define IEEE80211_RRM_CAPS_BEACON_REQUEST_PASSIVE BIT(4) +#define IEEE80211_RRM_CAPS_BEACON_REQUEST_ACTIVE BIT(5) /* Ext cap */ /* Byte 3 */ -#define IEEE80211_EXT_CAPS_BTM BIT(3) +#define IEEE80211_EXT_CAPS_BTM BIT(3) /* HT Cap */ -#define IEEE80211_HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET BIT(1) -#define IEEE80211_HT_CAP_INFO_SHORT_GI20MHZ BIT(5) -#define IEEE80211_HT_CAP_INFO_SHORT_GI40MHZ BIT(6) +#define IEEE80211_HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET BIT(1) +#define IEEE80211_HT_CAP_INFO_SHORT_GI20MHZ BIT(5) +#define IEEE80211_HT_CAP_INFO_SHORT_GI40MHZ BIT(6) /* VHT Cap */ -#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ BIT(2) -#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ BIT(3) -#define IEEE80211_VHT_CAP_SHORT_GI_80 BIT(5) -#define IEEE80211_VHT_CAP_SHORT_GI_160 BIT(6) -#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE BIT(11) -#define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE BIT(19) +#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ BIT(2) +#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ BIT(3) +#define IEEE80211_VHT_CAP_SHORT_GI_80 BIT(5) +#define IEEE80211_VHT_CAP_SHORT_GI_160 BIT(6) +#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE BIT(11) +#define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE BIT(19) /* HE Cap */ -#define IEEE80211_HE_CAP_PHY_CAP_40MHZ_24G BIT(1) -#define IEEE80211_HE_CAP_PHY_CAP_40MHZ_80MGHZ_5G_6G BIT(2) -#define IEEE80211_HE_CAP_PHY_CAP_160MHZ_5G_6G BIT(3) -#define IEEE80211_HE_CAP_PHY_CAP_8080MHZ_5G_6G BIT(4) +#define IEEE80211_HE_CAP_PHY_CAP_40MHZ_24G BIT(1) +#define IEEE80211_HE_CAP_PHY_CAP_40MHZ_80MGHZ_5G_6G BIT(2) +#define IEEE80211_HE_CAP_PHY_CAP_160MHZ_5G_6G BIT(3) +#define IEEE80211_HE_CAP_PHY_CAP_8080MHZ_5G_6G BIT(4) #define IEEE80211_HE_CAP_PHY_CAP_FULL_BANDWIDTH_UL_MU_MIMO BIT(6) /* PHY_CAP B22 - Byte 2 Bit 6 */ #define IEEE80211_HE_CAP_PHY_CAP_PARTIAL_BANDWIDTH_UL_MU_MIMO BIT(7) /* PHY_CAP B23 - Byte 2 Bit 7 */ @@ -121,11 +137,31 @@ #define IEEE80211_HE_CAP_PHY_CAP_MU_BEAMFORMER BIT(1) /* PHY_CAP B33 - Byte 4 Bit 1 */ #define IEEE80211_HE_CAP_PHY_CAP_PARTIAL_BANDWIDTH_DL_MU_MIMO BIT(6) /* PHY_CAP B54 - Byte 6 Bit 6 */ -/* MAP IE */ +/* MULTI LINK IE */ +#define IEEE80211_MULTI_LINK_CTRL_TYPE_MASK (BIT(2) | BIT(1) | BIT(0)) +#define IEEE80211_MULTI_LINK_CTRL_TYPE_BASIC 0 +#define IEEE80211_MULTI_LINK_CTRL_LINK_ID_PRESENT BIT(4) +#define IEEE80211_MULTI_LINK_CTRL_BSS_PARAM_CH_COUNT_PRESENT BIT(5) +#define IEEE80211_MULTI_LINK_CTRL_MSD_INFO_PRESENT BIT(6) +#define IEEE80211_MULTI_LINK_CTRL_EML_CAP_PRESENT BIT(7) +#define IEEE80211_MULTI_LINK_CTRL_MLD_CAP_PRESENT BIT(8) + +#define IEEE80211_MULTI_LINK_EML_CAP_EMLSR BIT(1) +#define IEEE80211_MULTI_LINK_EML_CAP_EMLMR BIT(7) + +#define IEEE80211_MULTI_LINK_MLD_CAP_MAX_SYM_LINKS_MASK (BIT(3) | BIT(2) | BIT(1) | BIT(0)) + +#define IEEE80211_MULTI_LINK_STA_PROFILE_CTRL_COMPLETE BIT(4) +#define IEEE80211_MULTI_LINK_STA_PROFILE_CTRL_MAC_PRESENT BIT(5) +#define IEEE80211_MULTI_LINK_STA_PROFILE_CTRL_NSTR_LP_PRESENT BIT(9) + +/* WFA Vendor specific IEs */ #define WFA_OUI_BYTE_0 0x50 #define WFA_OUI_BYTE_1 0x6F #define WFA_OUI_BYTE_2 0x9A #define WFA_VENDOR_IE_MIN_LEN 4 + +/* MAP IE */ #define WFA_EID_MAP 27 #define WFA_SUB_EID_MAP_EXTENSION 6 #define WFA_SUB_EID_MAP_EXTENSION_LEN 1 @@ -142,23 +178,32 @@ typedef struct ieee802_11_elems { uint8_t *rates; uint8_t *ext_rates; uint8_t *ht_cap; - uint8_t *he_cap; uint8_t *rrm_enabled_cap; uint8_t *ext_cap; uint8_t *vht_cap; + uint8_t *he_cap; + uint8_t *eht_cap; uint8_t *map; uint8_t *mbo; - - uint8_t ssid_len; - uint8_t rates_len; - uint8_t ext_rates_len; - uint8_t ht_cap_len; - uint8_t rrm_enabled_cap_len; - uint8_t ext_cap_len; - uint8_t vht_cap_len; - uint8_t he_cap_len; - uint8_t map_len; - uint8_t mbo_len; + uint8_t *multi_link; + uint8_t *multi_link_defrag; + uint8_t *multi_link_sta_profile; + + uint8_t ssid_len; + uint8_t rates_len; + uint8_t ext_rates_len; + uint8_t ht_cap_len; + uint8_t rrm_enabled_cap_len; + uint8_t ext_cap_len; + uint8_t vht_cap_len; + uint8_t he_cap_len; + uint8_t eht_cap_len; + uint8_t map_len; + uint8_t mbo_len; + uint8_t multi_link_len; + uint16_t multi_link_left; /* length of assoc ies after end of multi link event (could contain fragments) */ + uint16_t multi_link_defrag_len; + uint16_t multi_link_sta_profile_len; } ieee802_11_elems; typedef struct { @@ -197,6 +242,33 @@ typedef struct { //uint16_t tx_mcs_map_8080; } STRUCT_PACKED ieee80211_he_cap; +typedef struct { + uint8_t mac_cap_info[2]; + uint8_t phy_cap_info[9]; + + /* TODO: mcs map */ +} STRUCT_PACKED ieee80211_eht_cap; + +typedef struct { + uint16_t ctrl; + struct { + uint8_t len; + mac_addr mld_mac; + } info; + + /* Sub elements follow after skipping common len after "ctrl" */ +} STRUCT_PACKED ieee80211_multi_link; + +typedef struct { + uint16_t ctrl; + struct { + uint8_t len; + mac_addr aff_mac; + } info; + + /* Capabilities and tags follow */ +} STRUCT_PACKED ieee80211_multi_link_sta_profile; + /*####################################################################### # ENDIAN CONVERSION # ########################################################################*/ @@ -227,6 +299,11 @@ static inline uint32_t map_le_to_host32(uint32_t v) /*####################################################################### # IE PARSING # ########################################################################*/ +static void free_elems(ieee802_11_elems *elems) +{ + SFREE(elems->multi_link_defrag); +} + static int parse_ies(ieee802_11_elems *elems, uint8_t *ies, int len) { uint8_t *pos = ies; @@ -301,6 +378,18 @@ static int parse_ies(ieee802_11_elems *elems, uint8_t *ies, int len) elems->he_cap_len = ext_id_elen; } break; + case IEEE80211_EXTID_EHT_CAP: + if (NULL == elems->eht_cap && ext_id_elen >= IEEE80211_EXTID_EHT_CAP_MIN_LEN) { + elems->eht_cap = &pos[1]; + elems->eht_cap_len = ext_id_elen; + } + break; + case IEEE80211_EXTID_MULTI_LINK: + if (NULL == elems->multi_link && ext_id_elen >= IEEE80211_EXTID_MULTI_LINK_MIN_LEN) { + elems->multi_link = &pos[1]; + elems->multi_link_len = ext_id_elen; + elems->multi_link_left = left - elen; + } default: break; } @@ -339,6 +428,187 @@ static int parse_ies(ieee802_11_elems *elems, uint8_t *ies, int len) return ok; } +/* Defrag IE. Note: left = length of ies after this ie */ +static uint8_t *defrag_ie(uint8_t *ie, uint8_t len, uint16_t left, bool is_ext, uint16_t *defrag_len) +{ + uint8_t max_len = is_ext ? 254 : 255; + uint8_t *pos = ie; + uint8_t *defrag_ie; + uint8_t *defrag_pos; + uint8_t elen; + + /* Check if IE is fragmented and can be de-fragmented */ + if (len < max_len || left < 2 || ie[len] != IEEE80211_EID_FRAGMENT) { + return NULL; + } + + /* Allocate length of IE + remainder of IES (defragmented IE can never be bigger) */ + if (!(defrag_ie = defrag_pos = malloc(len + left))) { + return NULL; + } + + /* Copy first fragment */ + memcpy(defrag_pos, pos, len); + pos += len; + defrag_pos += len; + + /* Copy next fragments */ + do { + elen = pos[1]; + + pos += 2; + left -= 2; + + if (elen > left) { + break; + } + + memcpy(defrag_pos, pos, elen); + pos += elen; + defrag_pos += elen; + left -= elen; + + /* Continue if this fragment has max size and another is following */ + } while (left >= 2 && elen == 255 && pos[0] == IEEE80211_EID_FRAGMENT); + + *defrag_len = defrag_pos - defrag_ie; + + return defrag_ie; +} + +static void parse_multi_link_ie(ieee802_11_elems *elems, mac_addr aff_sta_mac) +{ + /* This function parses the multi link ie and searches in the sub elements for the aff_sta_mac + + If that is found, some IE are replaced. + + NOTE: + - the multi_link IE can be fragmented and the station profile inside the multi_link IE can be fragmented again + - to test fragmented IE, dummy vendor TLV can be added in function wlc_mlo_filter_ie_for_assocreq (wlc_mlo.c) + */ + + int ok = 1; + uint8_t *pos; + uint16_t left; + uint8_t info_len; + ieee80211_multi_link *ml; + + /* Defragment */ + elems->multi_link_defrag = defrag_ie(elems->multi_link, elems->multi_link_len, elems->multi_link_left, true, &elems->multi_link_defrag_len); + + pos = elems->multi_link_defrag ? elems->multi_link_defrag : elems->multi_link; + left = elems->multi_link_defrag ? elems->multi_link_defrag_len : elems->multi_link_len; + + /* Find Start of first sub element */ + ml = (ieee80211_multi_link *)pos; + info_len = IEEE80211_EXTID_MULTI_LINK_CTRL_LEN + ml->info.len; + + if (left < info_len) { + return; + } + + /* Only parse the basic type */ + if ((map_le_to_host16(ml->ctrl) & IEEE80211_MULTI_LINK_CTRL_TYPE_MASK) != IEEE80211_MULTI_LINK_CTRL_TYPE_BASIC) { + return; + } + + pos += info_len; + left -= info_len; + + while (left >= 2) { + ieee80211_multi_link_sta_profile *sta_profile; + ieee802_11_elems sp_elems; + uint8_t sub_id = *pos++; + uint16_t sub_elen = *pos++; + + left -= 2; + if (sub_elen > left) { + break; + } + + /* Only parse Sta profile sub element */ + if (sub_id != IEEE80211_MULTI_LINK_SEID_PER_STA_PROFILE) { + goto next; + } + + if (sub_elen < IEEE80211_MULTI_LINK_SEID_PER_STA_PROFILE_MIN_LEN) { + goto next; + } + + sta_profile = (ieee80211_multi_link_sta_profile *)pos; + info_len = IEEE80211_EXTID_MULTI_LINK_CTRL_LEN + sta_profile->info.len; + + if (sub_elen < info_len) { + /* Invalid */ + break; + } + + /* MAC must be present */ + if (!(map_le_to_host16(sta_profile->ctrl) & IEEE80211_MULTI_LINK_STA_PROFILE_CTRL_MAC_PRESENT)) { + break; + } + + /* Check if it is for affiliated MAC we are looking for */ + if (maccmp(sta_profile->info.aff_mac, aff_sta_mac)) { + goto next; + } + + /* Check if sta profile is fragmented + Defragmentation is "in place", destroying original multi link IE + + Which is ok because we break out of the parsing anyhow + */ + if (elems->multi_link_defrag) { + uint8_t *frag_pos = pos; /* pos still points at the start of the defragmented IE */ + uint8_t frag_len = sub_elen; + uint8_t frag_left = left; + + while (frag_len == 255 && left >= (frag_len + 2) && frag_pos[frag_len] == IEEE80211_MULTI_LINK_SEID_FRAGMENT) { + /* Skip fragment */ + frag_pos += frag_len; + frag_left -= frag_len; + + frag_len = frag_pos[1]; + frag_left -= 2; + + /* Move remainder 2 bytes back */ + memmove(frag_pos, frag_pos + 2, frag_left); + frag_left -= frag_len; + + sub_elen += frag_len; + } + } + + /* Store pointer */ + elems->multi_link_sta_profile = (uint8_t *)sta_profile; + elems->multi_link_sta_profile_len = sub_elen; + + /* Parse elements again (skip 2 byte fixed capabilities) */ + info_len += 2; + if (sub_elen < info_len) { + /* Invalid */ + break; + } + + if ((ok = parse_ies(&sp_elems, pos + info_len, sub_elen - info_len))) { + /* Replace ies */ + /* TODO: 80211 spec contains special inheritance rules -> to be checked when we have more MLO clients */ + #define REPLACE_IE(ie) if (sp_elems.ie) {elems->ie = sp_elems.ie; elems->ie##_len = sp_elems.ie##_len;} + REPLACE_IE(rates) + REPLACE_IE(ext_rates) + REPLACE_IE(ht_cap) + REPLACE_IE(vht_cap) + REPLACE_IE(he_cap) + REPLACE_IE(eht_cap) + } + break; + +next: + left -= sub_elen; + pos += sub_elen; + } +} + static int parse_ies_check_ssid(ieee802_11_elems *elems, uint8_t *ies, int len, uint8_t *match_ssid, int match_ssid_len) { int ok = parse_ies(elems, ies, len); @@ -350,7 +620,6 @@ static int parse_ies_check_ssid(ieee802_11_elems *elems, uint8_t *ies, int len, } return ok; - } static int parse_ies_check_ssid_offset(ieee802_11_elems *elems, uint8_t *body, int body_len, int offset, uint8_t *match_ssid, int match_ssid_len) @@ -408,6 +677,14 @@ static bool has_ofdm(uint8_t *rates, int len) /* 11AX has 3 SGI options - use 0.8 us */ /* 1 SS, 2 SS, 3 SS, 4 SS */ +/* TODO: get real 11BE table... */ +static uint32_t pr_11be [/* bw */ 5][/* ss */ 4] = {{ 143400, 286800, 430100, 573500}, /* 20MHz - MCS 11 */ + { 286800, 573500, 860300, 1147100}, /* 40MHz - MCS 11 */ + { 600500, 1201000, 1801500, 2402000}, /* 80MHz - MCS 11 */ + { 1201000, 2402000, 3602900, 4803900}, /* 160MHz - MCS 11 */ + { 2402000, 4804000, 7205800, 9607800}, /* 320MHz - MCS 11 */ + }; + static uint32_t pr_11ax [/* bw */ 4][/* ss */ 4] = {{ 143400, 286800, 430100, 573500}, /* 20MHz - MCS 11 */ { 286800, 573500, 860300, 1147100}, /* 40MHz - MCS 11 */ { 600500, 1201000, 1801500, 2402000}, /* 80MHz - MCS 11 */ @@ -442,23 +719,29 @@ uint32_t map_get_max_phy_rate(map_sta_capability_t *caps) /* Limit to 4 SS */ int ss = min(4, caps->max_tx_spatial_streams) - 1; int b = caps->max_bandwidth; - int bw = b >= 160 ? 3 : b >= 80 ? 2 : b >= 40 ? 1 : 0; + int bw = b >= 320 ? 4 : b >= 160 ? 3 : b >= 80 ? 2 : b >= 40 ? 1 : 0; switch(caps->supported_standard) { + case STD_80211_BE: + bw = min(bw, 4); + return pr_11be[bw][ss]; + break; case STD_80211_ANACAX: case STD_80211_ACAX: case STD_80211_ANAX: case STD_80211_NAX: case STD_80211_AX: + bw = min(bw, 3); return pr_11ax[bw][ss]; break; case STD_80211_ANAC: case STD_80211_AC: + bw = min(bw, 3); return caps->sgi_support ? pr_11ac_sgi[bw][ss] : pr_11ac[bw][ss]; break; case STD_80211_AN: case STD_80211_N: - bw = min(1, bw); + bw = min(bw, 1); return caps->sgi_support ? pr_11n_sgi[bw][ss] : pr_11n[bw][ss]; break; case STD_80211_A: @@ -479,7 +762,8 @@ uint32_t map_get_max_phy_rate(map_sta_capability_t *caps) /*####################################################################### # PARSE ASSOC BODY # ########################################################################*/ -int map_80211_parse_assoc_body(map_sta_capability_t *caps, uint8_t *body, int body_len, int supported_freq, uint8_t *match_ssid, int match_ssid_len) +int map_80211_parse_assoc_body(map_sta_capability_t *caps, uint8_t *body, int body_len, int supported_freq, + uint8_t *match_ssid, int match_ssid_len, mac_addr aff_sta_mac) { ieee802_11_elems elems = {0}; uint16_t fixed_cap = 0; @@ -537,9 +821,20 @@ int map_80211_parse_assoc_body(map_sta_capability_t *caps, uint8_t *body, int bo return -1; } while(0); + /* For affiliated STA: parse nested multi link IE */ + if (aff_sta_mac) { + if (elems.multi_link) { + parse_multi_link_ie(&elems, aff_sta_mac); + } else { + log_lib_w("parsing ies for affiliated STA but multi_link IE was not found"); + } + } + + /* Fill in capability */ /* Caps from he/vht/he ie */ + ieee80211_eht_cap *eht_cap = (ieee80211_eht_cap *)elems.eht_cap; ieee80211_he_cap *he_cap = (ieee80211_he_cap *)elems.he_cap; ieee80211_vht_cap *vht_cap = (ieee80211_vht_cap *)elems.vht_cap; ieee80211_ht_cap *ht_cap = (ieee80211_ht_cap *)elems.ht_cap; @@ -556,24 +851,20 @@ int map_80211_parse_assoc_body(map_sta_capability_t *caps, uint8_t *body, int bo /* Standard */ if (supported_freq == IEEE80211_FREQUENCY_BAND_6_GHZ) { - caps->supported_standard = STD_80211_AX; - } else if (he_cap) { - if (supported_freq != IEEE80211_FREQUENCY_BAND_2_4_GHZ) { - caps->supported_standard = (vht_cap && ht_cap)? STD_80211_ANACAX: - (vht_cap)? STD_80211_ACAX:STD_80211_ANAX; - } else { - caps->supported_standard = STD_80211_NAX; - } - } else if (supported_freq != IEEE80211_FREQUENCY_BAND_2_4_GHZ && vht_cap) { - caps->supported_standard = STD_80211_AC; - } else if (ht_cap) { - caps->supported_standard = STD_80211_N; - } else if (supported_freq == IEEE80211_FREQUENCY_BAND_2_4_GHZ) { - caps->supported_standard = is_erp ? STD_80211_G : STD_80211_B; - } else { - caps->supported_standard = STD_80211_A; + caps->supported_standard = eht_cap ? STD_80211_BE : STD_80211_AX; + } else if (supported_freq == IEEE80211_FREQUENCY_BAND_5_GHZ) { + caps->supported_standard = (he_cap && vht_cap && ht_cap) ? STD_80211_ANACAX : + (he_cap && vht_cap) ? STD_80211_ACAX : + (he_cap && ht_cap) ? STD_80211_ANAX : + (vht_cap) ? STD_80211_AC : + (ht_cap) ? STD_80211_N : STD_80211_A; + } else { /* 2.4ghz */ + caps->supported_standard = he_cap ? STD_80211_NAX : + ht_cap ? STD_80211_N : + is_erp ? STD_80211_G : STD_80211_B; } + caps->eht_support = eht_cap ? 1 : 0; caps->he_support = he_cap ? 1 : 0; caps->vht_support = vht_cap ? 1 : 0; caps->ht_support = ht_cap ? 1 : 0; @@ -599,6 +890,11 @@ int map_80211_parse_assoc_body(map_sta_capability_t *caps, uint8_t *body, int bo caps->max_rx_spatial_streams = ht_mcs_set_to_ss(ht_cap->supported_mcs_set); } + /* TODO: parse eht_cap - for now just overrule the bandwith */ + if (supported_freq == IEEE80211_FREQUENCY_BAND_6_GHZ && eht_cap) { + caps->max_bandwidth = 320; + } + /* SGI from HE, VHT and HT */ if (he_cap) { /* 11ax sgi field must be marked as False */ @@ -684,7 +980,74 @@ int map_80211_parse_assoc_body(map_sta_capability_t *caps, uint8_t *body, int bo /* Max phy rate */ caps->max_phy_rate = map_get_max_phy_rate(caps); + /* MLD Modes */ + if (elems.multi_link) { + /* - EMLSR and EMLR: EML_CAP field from the MLD common info. + - STR and NSTR: MLD_CAP field from the MLD common info + sta profile control field + */ + + /* Common */ + ieee80211_multi_link *ml = (ieee80211_multi_link *)elems.multi_link; + uint16_t ctrl = map_le_to_host16(ml->ctrl); + uint8_t *pos = elems.multi_link + sizeof(ieee80211_multi_link); /* = After MLD MAC */ + uint8_t *end = elems.multi_link + IEEE80211_EXTID_MULTI_LINK_CTRL_LEN + ml->info.len; + uint16_t eml_cap = 0; + uint16_t mld_cap = 0; + + /* Length check */ + if (end > (elems.multi_link + elems.multi_link_len)) { + goto skip_mld_modes; + } + + /* Expect basic type */ + if ((ctrl & IEEE80211_MULTI_LINK_CTRL_TYPE_MASK) != IEEE80211_MULTI_LINK_CTRL_TYPE_BASIC) { + goto skip_mld_modes; + } + + /* Bits set in the ctrl field determine the locaction of eml and mld cap */ + if (ctrl & IEEE80211_MULTI_LINK_CTRL_LINK_ID_PRESENT) { + pos++; /* 1 byte - should not be present according to hostapd */ + } + if (ctrl & IEEE80211_MULTI_LINK_CTRL_BSS_PARAM_CH_COUNT_PRESENT) { + pos++; /* 1 byte - should not be present according to hostapd */ + } + if (ctrl & IEEE80211_MULTI_LINK_CTRL_MSD_INFO_PRESENT) { + pos += 2; /* 2 bytes - should not be present according to hostapd */ + } + if (ctrl & IEEE80211_MULTI_LINK_CTRL_EML_CAP_PRESENT) { + eml_cap = (pos[1] << 8) + pos[0]; /* Little endian... */ + pos += 2; + } + if (ctrl & IEEE80211_MULTI_LINK_CTRL_MLD_CAP_PRESENT) { + mld_cap = (pos[1] << 8) + pos[0]; /* Little endian... */ + pos += 2; + } + /* Check that we are still in the control field */ + if (pos > end) { + goto skip_mld_modes; + } + + /* Fill in supported mld modes */ + caps->mld_modes.emlsr = eml_cap & IEEE80211_MULTI_LINK_EML_CAP_EMLSR; + caps->mld_modes.emlmr = eml_cap & IEEE80211_MULTI_LINK_EML_CAP_EMLMR; + caps->mld_modes.str = mld_cap & IEEE80211_MULTI_LINK_MLD_CAP_MAX_SYM_LINKS_MASK; + + /* For STR/NSTR: also check sta profile control */ + if (caps->mld_modes.str && elems.multi_link_sta_profile) { + ieee80211_multi_link_sta_profile *sp = (ieee80211_multi_link_sta_profile *)elems.multi_link_sta_profile; + uint16_t sta_ctrl = map_le_to_host16(sp->ctrl); + + caps->mld_modes.nstr = sta_ctrl & IEEE80211_MULTI_LINK_STA_PROFILE_CTRL_NSTR_LP_PRESENT; + caps->mld_modes.str = !caps->mld_modes.nstr; + } + +skip_mld_modes: + ; + } + caps->valid = true; + free_elems(&elems); + return 0; } diff --git a/source/libplatform/src/map_config.c b/source/libplatform/src/map_config.c index 7d98bda..cee1570 100644 --- a/source/libplatform/src/map_config.c +++ b/source/libplatform/src/map_config.c @@ -80,6 +80,7 @@ typedef struct profile_ssid_map_s { int profile_idx; map_profile_type_t type; char label[65]; + uint16_t bss_freq_bands; uint8_t bss_state; } profile_ssid_map_t; @@ -115,6 +116,7 @@ static map_profile_cfg_t g_default_profiles[] = { .gateway = true, .extender = true, .hide = false, + .mld_id = 0, .vlan_id = -1 }, { @@ -131,6 +133,7 @@ static map_profile_cfg_t g_default_profiles[] = { .gateway = true, .extender = true, .hide = false, + .mld_id = 1, .vlan_id = -1 }, { @@ -147,39 +150,42 @@ static map_profile_cfg_t g_default_profiles[] = { .gateway = true, .extender = true, .hide = true, + .mld_id = 2, .vlan_id = -1 } }; -static profile_ssid_map_t g_dynamic_mapping[8] = { 0 }; - -static profile_ssid_map_t g_static_mapping[8] = { +static profile_ssid_map_t g_default_mappings[] = { { .profile_idx = 1, .ssid_idx = 1, .type = MAP_PROFILE_TYPE_HOME, - .label = "Home", + .label = "Home2G", + .bss_freq_bands = MAP_FREQ_BAND_2G, .bss_state = MAP_FRONTHAUL_BSS }, { .profile_idx = 2, .ssid_idx = 2, .type = MAP_PROFILE_TYPE_HOME, - .label = "Home", + .label = "Home5G", + .bss_freq_bands = MAP_FREQ_BAND_5G, .bss_state = MAP_FRONTHAUL_BSS }, { .profile_idx = 3, .ssid_idx = 3, .type = MAP_PROFILE_TYPE_GUEST, - .label = "Guest", + .label = "Guest2G", + .bss_freq_bands = MAP_FREQ_BAND_2G, .bss_state = MAP_FRONTHAUL_BSS }, { .profile_idx = 4, .ssid_idx = 4, .type = MAP_PROFILE_TYPE_GUEST, - .label = "Guest", + .label = "Guest5G", + .bss_freq_bands = MAP_FREQ_BAND_5G, .bss_state = MAP_FRONTHAUL_BSS }, { @@ -187,12 +193,13 @@ static profile_ssid_map_t g_static_mapping[8] = { .ssid_idx = 6, .type = MAP_PROFILE_TYPE_BACKHAUL, .label = "Backhaul", + .bss_freq_bands = MAP_FREQ_BAND_5G, .bss_state = MAP_BACKHAUL_BSS } }; +/* Set to false if Wi-Fi on host is not relevant */ static bool g_sync_with_wifi = true; -static bool g_use_dynamic_mapping = true; /*####################################################################### # DATA FUNCTIONS # @@ -305,22 +312,6 @@ static int setpsm_ssid(const char *name, int index, const char *value) return setpsm_val(rname, value); } -static int rmpsm_ssid(const char *name, int index) -{ - int rc; - char rname[128]; - - snprintf(rname, sizeof(rname), MAP_PSM_SSID_PREFIX "%s", index, name); - - rc = PSM_Del_Record(bus_handle, g_Subsystem, name); - if (rc != CCSP_SUCCESS) { - log_lib_e("Remove record failed"); - return -1; - } - - return 0; -} - static int getpsm_ssid_str(const char *name, int index, char *buf, size_t len) { char rname[128]; @@ -417,7 +408,7 @@ static int setparam_val(const char *name, enum dataType_e type, const char *valu rc = CcspBaseIf_setParameterValues(bus_handle, comps[0]->componentName, comps[0]->dbusPath, 0, 0, &val, 1, TRUE, &fault); if (rc != CCSP_SUCCESS) { - log_lib_e("Set parameter values failed"); + log_lib_e("Set parameter values failed: '%s'", fault ? fault : "Unknown"); goto bail; } retval = 0; @@ -490,9 +481,9 @@ static uint16_t bandwidth_from_string(const char *s) static int convert_log_level(const char *level_str) { -#define NUM_LEVEL 8 - char *strs[NUM_LEVEL] = {"emerge", "alert", "crit", "error", "warn", "notice", "info", "debug"}; - int levels[NUM_LEVEL] = {LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG}; +#define NUM_LEVEL 9 + char *strs[NUM_LEVEL] = {"emerge", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace"}; + int levels[NUM_LEVEL] = {LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG, LOG_TRACE}; int i; for (i = 0; iextender); log_lib_i("| Hide : %d", profile->hide); log_lib_i("| VLAN ID : %d", profile->vlan_id); + log_lib_i("| MLD ID : %d", profile->mld_id); log_lib_i("----------------------------------------------"); } @@ -678,7 +670,7 @@ static int set_chan_list(map_channel_set_t *ch_set, char *list, uint8_t freq_ban } static void get_iface_security_mode(const char *supported_security_modes, uint16_t *auth_mode, - uint16_t *encryption_mode, UNUSED uint8_t profile_idx) + uint16_t *encryption_mode) { *auth_mode = 0; *encryption_mode = 0; @@ -693,6 +685,14 @@ static void get_iface_security_mode(const char *supported_security_modes, uint16 *auth_mode |= IEEE80211_AUTH_MODE_SAE; } else if (strcmp(supported_security_modes,"wpa2-psk-wpa3-sae") == 0) { *auth_mode |= IEEE80211_AUTH_MODE_WPA2PSK | IEEE80211_AUTH_MODE_SAE; + } else if (strcmp(supported_security_modes,"wpa3-sae-24") == 0) { + *auth_mode |= IEEE80211_AUTH_MODE_SAE_24; + } else if (strcmp(supported_security_modes,"wpa3-sae-8-24") == 0) { + *auth_mode |= IEEE80211_AUTH_MODE_SAE | IEEE80211_AUTH_MODE_SAE_24; + } else if (strcmp(supported_security_modes,"wpa2-psk-wpa3-24") == 0) { + *auth_mode |= IEEE80211_AUTH_MODE_WPA2PSK | IEEE80211_AUTH_MODE_SAE_24; + } else if (strcmp(supported_security_modes,"wpa2-psk-wpa3-8-24") == 0) { + *auth_mode |= IEEE80211_AUTH_MODE_WPA2PSK | IEEE80211_AUTH_MODE_SAE | IEEE80211_AUTH_MODE_SAE_24; } if (*auth_mode == 0) { @@ -713,18 +713,33 @@ static void get_iface_security_mode(const char *supported_security_modes, uint16 (*auth_mode & IEEE80211_AUTH_MODE_SAE)) { *encryption_mode |= IEEE80211_ENCRYPTION_MODE_AES; } + if (*auth_mode & IEEE80211_AUTH_MODE_SAE_24) { + *encryption_mode |= IEEE80211_ENCRYPTION_MODE_GCMP256; + } } static char *get_sec_mode_str(uint16_t auth_mode, char *buf) { if (auth_mode & IEEE80211_AUTH_MODE_OPEN) { strcpy(buf, "none"); + } else if ((auth_mode & IEEE80211_AUTH_MODE_WPA2PSK) && + (auth_mode & IEEE80211_AUTH_MODE_SAE) && + (auth_mode & IEEE80211_AUTH_MODE_SAE_24)) { + strcpy(buf, "wpa2-psk-wpa3-8-24"); + } else if ((auth_mode & IEEE80211_AUTH_MODE_WPA2PSK) && + (auth_mode & IEEE80211_AUTH_MODE_SAE_24)) { + strcpy(buf, "wpa2-psk-wpa3-24"); + } else if ((auth_mode & IEEE80211_AUTH_MODE_SAE) && + (auth_mode & IEEE80211_AUTH_MODE_SAE_24)) { + strcpy(buf, "wpa3-sae-8-24"); } else if ((auth_mode & IEEE80211_AUTH_MODE_WPA2PSK) && (auth_mode & IEEE80211_AUTH_MODE_SAE)) { strcpy(buf, "wpa2-psk-wpa3-sae"); - } else if ((auth_mode & IEEE80211_AUTH_MODE_WPAPSK) && + } else if ((auth_mode & IEEE80211_AUTH_MODE_WPAPSK) && (auth_mode & IEEE80211_AUTH_MODE_WPA2PSK)) { strcpy(buf, "wpa-wpa2-psk"); + } else if (auth_mode & IEEE80211_AUTH_MODE_SAE_24) { + strcpy(buf, "wpa3-sae-24"); } else if (auth_mode & IEEE80211_AUTH_MODE_SAE) { strcpy(buf, "wpa3-sae"); } else if (auth_mode & IEEE80211_AUTH_MODE_WPA2PSK) { @@ -734,25 +749,25 @@ static char *get_sec_mode_str(uint16_t auth_mode, char *buf) return buf; } -static void get_frequency_bands(char* frequency_bands, uint16_t* bss_freq_bands) +static void get_frequency_bands(char* frequency_bands, uint16_t* freq_bands) { char *save_ptr; char *p; - *bss_freq_bands = 0; + *freq_bands = 0; p = strtok_r(frequency_bands, ", ", &save_ptr); while(p) { if (!strcmp(p, "2") || !strcasecmp(p, "2G")) { - *bss_freq_bands |= MAP_M2_BSS_RADIO2G; + *freq_bands |= MAP_M2_BSS_RADIO2G; } else if (!strcmp(p, "5") || !strcasecmp(p, "5G")) { - *bss_freq_bands |= MAP_M2_BSS_RADIO5GL | MAP_M2_BSS_RADIO5GU; + *freq_bands |= MAP_M2_BSS_RADIO5GL | MAP_M2_BSS_RADIO5GU; } else if (!strcasecmp(p, "5L") || !strcasecmp(p, "5GL")) { - *bss_freq_bands |= MAP_M2_BSS_RADIO5GL; + *freq_bands |= MAP_M2_BSS_RADIO5GL; } else if (!strcasecmp(p, "5H") || !strcasecmp(p, "5GH") || !strcasecmp(p, "5U") || !strcasecmp(p, "5GU")) { - *bss_freq_bands |= MAP_M2_BSS_RADIO5GU; + *freq_bands |= MAP_M2_BSS_RADIO5GU; } else if (!strcmp(p, "6") || !strcasecmp(p, "6G")) { - *bss_freq_bands |= MAP_M2_BSS_RADIO6G; + *freq_bands |= MAP_M2_BSS_RADIO6G; } p = strtok_r(NULL, ", ", &save_ptr); } @@ -814,11 +829,8 @@ static int get_mac_addresses(map_controller_cfg_t *cfg) return -1; } - mac[0] |= 1 << 1; maccpy(cfg->al_mac, mac); - ++mac[5]; - // if (!mac[5]) ++mac[4]; - // if (!mac[4]) ++mac[3]; + mac[0] |= (1 << 1); maccpy(cfg->local_agent_al_mac, mac); return 0; @@ -930,7 +942,7 @@ static int set_wifi_ssid_ssid(unsigned int ssid_idx, char *ssid) sprintf(path, "Device.WiFi.SSID.%d.SSID", ssid_idx); rc = setparam_val(path, ccsp_string, value); if (rc != 0) { - log_lib_e("Failed to get SSID"); + log_lib_e("Failed to set SSID"); return -1; } @@ -1028,7 +1040,7 @@ static int get_wifi_security_mode(unsigned int ap_idx, char *mode) } else if (strcmp(value, "WPA-WPA2-Personal") == 0) { strcpy(mode, "wpa-wpa2-psk"); } else { - log_lib_e("Unsopperted security mode: %s", value); + log_lib_e("Unsupported security mode: %s", value); return -1; } @@ -1050,7 +1062,7 @@ static int set_wifi_security_mode(unsigned int ap_idx, char *mode) } else if (strcmp(mode, "wpa-wpa2-psk") == 0) { strcpy(value, "WPA-WPA2-Personal"); } else { - log_lib_e("Unsopperted security mode: %s", value); + log_lib_e("Unsupported security mode: %s", value); return -1; } sprintf(path, "Device.WiFi.AccessPoint.%d.Security.ModeEnabled", ap_idx); @@ -1066,12 +1078,17 @@ static int set_wifi_security_mode(unsigned int ap_idx, char *mode) static int set_wifi_apply_settings(unsigned int radio_idx) { char path[128] = {0}; - char value[8] = {0}; int rc; +#if 0 + char value[8] = {0}; value[0] = '1'; sprintf(path, "Device.WiFi.Radio.%u.X_RDK_ApplySettingSSID", radio_idx); rc = setparam_val(path, ccsp_int, value); +#else + sprintf(path, "Device.WiFi.Radio.%u.X_RDK_ApplySetting", radio_idx); + rc = setparam_val(path, ccsp_boolean, "true"); +#endif if (rc != 0) { log_lib_e("Failed to set X_RDK_ApplySetting"); return -1; @@ -1083,9 +1100,59 @@ static int set_wifi_apply_settings(unsigned int radio_idx) /*####################################################################### # PROFILE SYNC # ########################################################################*/ +static profile_ssid_map_t *mapping_get_by_pidx(int profile_idx) +{ + unsigned int mapping_cnt; + profile_ssid_map_t *mapping; + unsigned int i; + + mapping = g_default_mappings; + mapping_cnt = ARRAY_SIZE(g_default_mappings); + + for (i = 0; i < mapping_cnt; i++) { + if (profile_idx == mapping[i].profile_idx) { + return &mapping[i]; + } + } + + return NULL; +} + +static profile_ssid_map_t *mapping_get_by_profile(map_profile_cfg_t *profile) +{ + map_controller_cfg_t *cfg = &map_cfg_get()->controller_cfg; + profile_ssid_map_t *mapping; + unsigned int profile_cnt; + unsigned int i, j; + bool skip; + + profile_cnt = cfg->num_profiles; + for (i = 0; i < ARRAY_SIZE(g_default_mappings); i++) { + mapping = &g_default_mappings[i]; + if (mapping->bss_freq_bands != profile->bss_freq_bands) { + continue; + } + skip = false; + for (j = 0; j < profile_cnt; j++) { + if (mapping->profile_idx == cfg->profiles[j].profile_idx) { + skip = true; + break; + } + } + if (skip) { + continue; + } + + return mapping; + } + + return NULL; +} + static int profile_load(map_profile_cfg_t *profile, uint8_t index) { int rc = -1; + int profile_idx; bool fh, bh, enabled; char *type = NULL; char *freq_bands = NULL; @@ -1100,6 +1167,12 @@ static int profile_load(map_profile_cfg_t *profile, uint8_t index) /* Fill in profiles */ memset(profile, 0, sizeof(map_profile_cfg_t)); + if (getpsm_ssid("Index", index, &val) == 0) { + profile_idx = atoi(val); + SFREE(val); + } else { + profile_idx = -1; + } if (getpsm_ssid("Type", index, &type) != 0) { log_lib_e("Get Type[%d] failed", index); goto bail; @@ -1151,6 +1224,13 @@ static int profile_load(map_profile_cfg_t *profile, uint8_t index) } profile->vlan_id = atoi(val); SFREE(val); + if (getpsm_ssid("MLDID", index, &val) != 0) { + log_lib_n("Get NLDID[%d] failed", index); + profile->mld_id = -1; + } else { + profile->mld_id = atoi(val); + } + SFREE(val); if (getpsm_ssid("Hide", index, &val) != 0) { log_lib_e("Get Hide[%d] failed", index); return -1; @@ -1159,14 +1239,14 @@ static int profile_load(map_profile_cfg_t *profile, uint8_t index) SFREE(val); rc = 0; - profile->profile_idx = index + 1; + profile->profile_idx = profile_idx > 0 ? profile_idx : index + 1; profile->enabled = enabled; profile->type = profile_type_from_string(type); get_frequency_bands(freq_bands, &profile->bss_freq_bands); get_iface_security_mode(security_mode, &profile->supported_auth_modes, - &profile->supported_encryption_types, index); + &profile->supported_encryption_types); profile->bss_state |= fh ? MAP_FRONTHAUL_BSS : 0; profile->bss_state |= bh ? MAP_BACKHAUL_BSS : 0; @@ -1190,6 +1270,10 @@ static int profile_save(map_profile_cfg_t *profile, uint8_t index) return -1; } + snprintf(buf, sizeof(buf), "%d", profile->profile_idx); + if (setpsm_ssid("Index", index, buf) != 0) { + log_lib_e("Set Index[%d] failed", index); + } strcpy(buf, profile_type_to_string(profile->type)); if (setpsm_ssid("Type", index, buf) != 0) { log_lib_e("Set Type[%d] failed", index); @@ -1229,6 +1313,10 @@ static int profile_save(map_profile_cfg_t *profile, uint8_t index) if (setpsm_ssid("VLANID", index, buf) != 0) { log_lib_e("Set VLANID[%d] failed", index); } + snprintf(buf, sizeof(buf), "%d", profile->mld_id); + if (setpsm_ssid("MLDID", index, buf) != 0) { + log_lib_e("Set MLDID[%d] failed", index); + } snprintf(buf, sizeof(buf), "%d", profile->hide); if (setpsm_ssid("Hide", index, buf) != 0) { log_lib_e("Set Hide[%d] failed", index); @@ -1237,107 +1325,294 @@ static int profile_save(map_profile_cfg_t *profile, uint8_t index) return 0; } -static int profile_remove(uint8_t index) +static int profile_del(uint8_t index) { - if (rmpsm_ssid("Type", index) != 0) { - log_lib_e("Remove Type[%d] failed", index); + int rc; + char rname[128]; + + snprintf(rname, sizeof(rname), MAP_PSM_SSID_PREFIX, index); + + /* PSM APIs accept partial paths for delete operation, so let's use that. + The other option is to delete each record using full paths, which seems + to be greatly inefficient */ + rc = PSM_Del_Record(bus_handle, g_Subsystem, rname); + if (rc != CCSP_SUCCESS) { + log_lib_e("Delete record failed"); + return -1; } - if (rmpsm_ssid("Enabled", index) != 0) { - log_lib_e("Remove Enabled[%d] failed", index); + + return 0; +} + +static int profile_add(map_profile_cfg_t *profile, bool sync) +{ + map_controller_cfg_t *cfg = &map_cfg_get()->controller_cfg; + unsigned int profile_cnt, i; + map_profile_cfg_t *cp; + int profile_idx; + profile_ssid_map_t *mapping = NULL; + unsigned int ssid_idx; + unsigned int radio_idx; + char mode[24]; + bool send_apply = false; + + if (profile->bss_state & MAP_BACKHAUL_BSS) { + log_lib_e("Creating backhaul is illegal"); + return -1; } - if (rmpsm_ssid("Label", index) != 0) { - log_lib_e("Remove Label[%d] failed", index); + + profile_cnt = cfg->num_profiles; + for (i = 0; i < profile_cnt; i++) { + uint16_t curr_auth_modes; + bool curr_enabled; + char curr_wpa_key[65]; + + cp = &cfg->profiles[i]; + + /* Check if the request is for modification */ + if (profile->bss_freq_bands != cp->bss_freq_bands || + strcmp(profile->bss_ssid, cp->bss_ssid) != 0) { + continue; + } + + /* Modification request */ + if (cp->bss_state & MAP_BACKHAUL_BSS) { + log_lib_e("Modifying backhaul is illegal"); + return -1; + } + + if (profile->supported_auth_modes == 0) { + profile->supported_auth_modes = cp->supported_auth_modes; + profile->supported_encryption_types = cp->supported_encryption_types; + if (profile->wpa_key[0] == '\0') { + strcpy(profile->wpa_key, cp->wpa_key); + } + } else if (profile->supported_auth_modes != IEEE80211_AUTH_MODE_OPEN) { + if (profile->wpa_key[0] == '\0') { + if (cp->wpa_key[0] == '\0') { + log_lib_e("Invalid password"); + return -1; + } + strcpy(profile->wpa_key, cp->wpa_key); + } + } else { + profile->wpa_key[0] = '\0'; + } + + profile->profile_idx = cp->profile_idx; + if (profile->type == MAP_PROFILE_TYPE_OTHER) { + profile->type = cp->type; + } + if (profile->label[0] == '\0') { + strcpy(profile->label, cp->label); + } + profile->bss_state = cp->bss_state; + if ((int8_t)(profile->extender) == -1) { + profile->extender = cp->extender; + } + if ((int8_t)(profile->gateway) == -1) { + profile->gateway = cp->gateway; + } + if (profile->vlan_id < -1) { + profile->vlan_id = cp->vlan_id; + } + + if (sync) { + curr_auth_modes = cp->supported_auth_modes; + curr_enabled = cp->enabled; + strcpy(curr_wpa_key, cp->wpa_key); + } + + profile_save(profile, i); + + if (g_map_cfg_cbs.profile_update_cb) { + g_map_cfg_cbs.profile_update_cb(); + } + + if (!sync) { + goto update; + } + + mapping = mapping_get_by_pidx(cp->profile_idx); + if (mapping == NULL) { + log_lib_e("Mapping not found"); + return 0; + } + ssid_idx = mapping->ssid_idx; + + if (profile->supported_auth_modes != curr_auth_modes) { + get_sec_mode_str(profile->supported_auth_modes, mode); + set_wifi_security_mode(ssid_idx, mode); + send_apply = true; + } + if (strcmp(profile->wpa_key, curr_wpa_key) != 0) { + set_wifi_security_key(ssid_idx, profile->wpa_key); + send_apply = true; + } + if (profile->enabled != curr_enabled) { + set_wifi_ssid_enable(ssid_idx, profile->enabled); + send_apply = true; + } + + if (send_apply) { + radio_idx = profile->bss_freq_bands == MAP_FREQ_BAND_2G ? 1 : 2; + set_wifi_apply_settings(radio_idx); + } + + return 0; } - if (rmpsm_ssid("SSID", index) != 0) { - log_lib_e("Remove SSID[%d] failed", index); + + if (profile->supported_auth_modes == 0) { + if (profile->wpa_key[0] != '\0') { + log_lib_e("Invalid authorization mode"); + return -1; + } + profile->supported_auth_modes = IEEE80211_AUTH_MODE_OPEN; + profile->supported_encryption_types = IEEE80211_ENCRYPTION_MODE_NONE; + } else if (profile->supported_auth_modes != IEEE80211_AUTH_MODE_OPEN) { + if (profile->wpa_key[0] == '\0') { + log_lib_e("Invalid password"); + return -1; + } + } else { + profile->wpa_key[0] = '\0'; } - if (rmpsm_ssid("FrequencyBands", index) != 0) { - log_lib_e("Remove FrequencyBands[%d] failed", index); + + mapping = NULL; + profile_idx = profile_cnt; + if (sync) { + mapping = mapping_get_by_profile(profile); + profile_idx = mapping ? mapping->profile_idx : (int)profile_cnt; } - if (rmpsm_ssid("SecurityMode", index) != 0) { - log_lib_e("Remove SecurityMode[%d] failed", index); + profile->profile_idx = profile_idx; + + if (profile->type == MAP_PROFILE_TYPE_OTHER) { + profile->type = mapping ? mapping->type : profile->type; } - if (rmpsm_ssid("Keypassphrase", index) != 0) { - log_lib_e("Remove Keypassphrase[%d] failed", index); + if (profile->label[0] == '\0') { + if (mapping) { + strcpy(profile->label, mapping->label); + } else { + snprintf(profile->label, sizeof(profile->label), "ssid%d", profile_idx); + } } - if (rmpsm_ssid("Fronthaul", index) != 0) { - log_lib_e("Remove Fronthaul[%d] failed", index); + if (profile->bss_state == 0) { + profile->bss_state = MAP_FRONTHAUL_BSS; } - if (rmpsm_ssid("Backhaul", index) != 0) { - log_lib_e("Remove Backhaul[%d] failed", index); + if ((int8_t)(profile->extender) == -1) { + profile->extender = true; } - if (rmpsm_ssid("VLANID", index) != 0) { - log_lib_e("Remove VLANID[%d] failed", index); + if ((int8_t)(profile->gateway) == -1) { + profile->gateway = true; } - if (rmpsm_ssid("Hide", index) != 0) { - log_lib_e("Remove Hide[%d] failed", index); + if (profile->vlan_id < -1) { + profile->vlan_id = -1; } - return 0; -} + /* Add new profile and update PSM */ + profile_save(profile, profile_cnt); + profile_cnt = cfg->num_profiles + 1; + setpsm_int(MAP_PSM_SSIDNOE, profile_cnt); -static int mapping_get_by_pidx(int profile_idx, unsigned int *ssid_idx) -{ - unsigned int mapping_cnt; - profile_ssid_map_t *mapping; - unsigned int i; +update: + if (g_map_cfg_cbs.profile_update_cb) { + g_map_cfg_cbs.profile_update_cb(); + } - if (g_use_dynamic_mapping) { - mapping = g_dynamic_mapping; - mapping_cnt = ARRAY_SIZE(g_dynamic_mapping); - } else { - mapping = g_static_mapping; - mapping_cnt = ARRAY_SIZE(g_static_mapping); + if (!sync) { + return 0; } - *ssid_idx = -1; - for (i = 0; i < mapping_cnt; i++) { - if (profile_idx == mapping[i].profile_idx) { - *ssid_idx = mapping[i].ssid_idx; + cp = NULL; + for (i = 0; i < profile_cnt; i++) { + if (profile->bss_freq_bands == cfg->profiles[i].bss_freq_bands && + strcmp(profile->bss_ssid, cfg->profiles[i].bss_ssid) == 0) { + cp = &cfg->profiles[i]; break; } } + if (cp == NULL) { + log_lib_e("Profile not found"); + return -1; + } + + mapping = mapping_get_by_pidx(cp->profile_idx); + if (mapping == NULL) { + log_lib_e("Mapping not found"); + return 0; + } + ssid_idx = mapping->ssid_idx; + + set_wifi_ssid_enable(ssid_idx, true); + set_wifi_ssid_ssid(ssid_idx, profile->bss_ssid); + get_sec_mode_str(profile->supported_auth_modes, mode); + set_wifi_security_mode(ssid_idx, mode); + set_wifi_security_key(ssid_idx, profile->wpa_key); + + radio_idx = profile->bss_freq_bands == MAP_FREQ_BAND_2G ? 1 : 2; + set_wifi_apply_settings(radio_idx); + return 0; } -static int profile_match(map_profile_cfg_t *profile) +static int profile_remove(map_profile_cfg_t *profile, bool sync) { - profile_ssid_map_t *mapping; - unsigned int ssid_cnt = 0; - unsigned int ssid_idx; - bool ssid_enable; - char ssid[32]; - - if (g_use_dynamic_mapping) { - mapping = &g_dynamic_mapping[profile->profile_idx - 1]; - } else { - mapping = &g_static_mapping[profile->profile_idx - 1]; - } - mapping->profile_idx = profile->profile_idx; - mapping->ssid_idx = -1; + map_controller_cfg_t *cfg = &map_cfg_get()->controller_cfg; + unsigned int profile_cnt, i; + int update = 0; + unsigned int ssid_idx = -1; + unsigned int radio_idx; - if (get_wifi_ssid_count(&ssid_cnt) < 0) { - log_lib_e("Invalid SSID count: %d", ssid_cnt); - return 0; - } + profile_cnt = cfg->num_profiles; + for (i = 0; i < profile_cnt; i++) { + map_profile_cfg_t *cp = &cfg->profiles[i]; - for (ssid_idx = 1; ssid_idx <= ssid_cnt; ssid_idx++) { - if (get_wifi_ssid_enable(ssid_idx, &ssid_enable) < 0) { - log_lib_e("Get SSID enable failed"); + if (profile->bss_freq_bands != cp->bss_freq_bands || + strcmp(profile->bss_ssid, cp->bss_ssid) != 0) { continue; } - if (!ssid_enable) { - continue; + + if (cp->bss_state & MAP_BACKHAUL_BSS) { + log_lib_e("Modifying backhaul is illegal"); + return -1; } - if (get_wifi_ssid_ssid(ssid_idx, ssid) < 0) { - log_lib_e("Get SSID failed"); - continue; + + if (sync) { + profile_ssid_map_t *mapping = mapping_get_by_pidx(cp->profile_idx); + if (mapping != NULL) { + ssid_idx = mapping->ssid_idx; + } } - if (strcmp(profile->bss_ssid, ssid) == 0) { - mapping->ssid_idx = ssid_idx; - break; + + /* Remove profile */ + for (; i < profile_cnt - 1; i++) { + map_profile_cfg_t tmp_profile = cfg->profiles[i + 1]; + + profile_save(&tmp_profile, i); } + profile_del(profile_cnt - 1); + + profile_cnt = cfg->num_profiles - 1; + setpsm_int(MAP_PSM_SSIDNOE, profile_cnt); + + update = 1; + break; + } + + if (update && g_map_cfg_cbs.profile_update_cb) { + g_map_cfg_cbs.profile_update_cb(); + } + + if (sync) { + return 0; + } + + if (update && (int)ssid_idx > 0) { + set_wifi_ssid_enable(ssid_idx, false); + + radio_idx = profile->bss_freq_bands == MAP_FREQ_BAND_2G ? 1 : 2; + set_wifi_apply_settings(radio_idx); } return 0; @@ -1348,19 +1623,29 @@ static void profile_create_backhaul(void) unsigned int profile_cnt; map_controller_cfg_t *cfg; map_profile_cfg_t *profile; + profile_ssid_map_t *mapping; + unsigned int i; cfg = &g_map_cfg.controller_cfg; profile_cnt = cfg->num_profiles; map_profile_realloc(++profile_cnt); profile = &cfg->profiles[profile_cnt - 1]; profile->profile_idx = profile_cnt; + /* If mapping table has dedicated nackhaul, use its index instead */ + mapping = g_default_mappings; + for (i = 0; i < ARRAY_SIZE(g_default_mappings); i++) { + if (mapping[i].bss_state == MAP_BACKHAUL_BSS) { + profile->profile_idx = mapping[i].profile_idx; + break; + } + } profile->enabled = TRUE; profile->type = MAP_PROFILE_TYPE_BACKHAUL; strncpy(profile->label, "Backhaul", sizeof(profile->label)); strncpy(profile->bss_ssid, "Backhaul", sizeof(profile->bss_ssid)); strncpy(profile->wpa_key, "rdk@1234", sizeof(profile->wpa_key)); get_iface_security_mode("wpa2-psk", &profile->supported_auth_modes, - &profile->supported_encryption_types, profile->profile_idx); + &profile->supported_encryption_types); profile->bss_freq_bands = MAP_FREQ_BAND_5G; profile->bss_state = MAP_BACKHAUL_BSS; profile->extender = true; @@ -1374,115 +1659,6 @@ static void profile_create_backhaul(void) return; } -static void wifi_dynamic_mapper(void) -{ - bool ssid_enable = false; - bool dedicated_backhaul = true; - unsigned int ap_idx = 0; - unsigned int ssid_cnt = 0; - unsigned int ssid_idx; - unsigned int profile_cnt; - unsigned int profile_5GHz_cnt = 0; - char key[64]; - char ssid[32]; - char mode[32]; - uint16_t freqband; - map_controller_cfg_t *cfg; - map_profile_cfg_t *profile; - profile_ssid_map_t *mapping; - unsigned int i; - - if (get_wifi_ssid_count(&ssid_cnt) < 0) { - log_lib_e("Invalid SSID count: %d", ssid_cnt); - return; - } - - cfg = &g_map_cfg.controller_cfg; - profile_cnt = cfg->num_profiles; - for (ssid_idx = 1; ssid_idx <= ssid_cnt; ssid_idx++) { - if (get_wifi_ssid_enable(ssid_idx, &ssid_enable) < 0) { - log_lib_e("Get SSID enable failed"); - continue; - } - if (!ssid_enable) { - continue; - } - if (get_wifi_radio_freqband(ssid_idx, &freqband) < 0) { - log_lib_e("Get radio freqband failed"); - continue; - } - if (get_wifi_ssid_ssid(ssid_idx, ssid) < 0) { - log_lib_e("Get SSID failed"); - continue; - } - if (get_wifi_security_index(ssid_idx, &ap_idx) < 0) { - log_lib_e("Get security index failed"); - continue; - } - if (get_wifi_security_mode(ap_idx, mode) < 0) { - log_lib_e("Get security mode failed"); - continue; - } - if (get_wifi_security_key(ap_idx, key) < 0) { - log_lib_e("Get security key failed"); - continue; - } - - map_profile_realloc(++profile_cnt); - profile = &cfg->profiles[profile_cnt - 1]; - profile->profile_idx = profile_cnt; - profile->enabled = true; - profile->type = MAP_PROFILE_TYPE_OTHER; - strcpy(profile->bss_ssid, ssid); - strcpy(profile->wpa_key, key); - get_iface_security_mode(mode, &profile->supported_auth_modes, - &profile->supported_encryption_types, profile->profile_idx); - profile->bss_freq_bands = freqband; - profile->bss_state = MAP_FRONTHAUL_BSS; - profile->gateway = true; - profile->extender = true; - profile->hide = false; - profile->vlan_id = -1; - - mapping = &g_dynamic_mapping[profile_cnt - 1]; - mapping->ssid_idx = ssid_idx; - mapping->profile_idx = profile->profile_idx; - - if (freqband == MAP_FREQ_BAND_5G) { - profile_5GHz_cnt++; - } - - profile_save(profile, profile_cnt - 1); - cfg->num_profiles = profile_cnt; - } - - if (dedicated_backhaul == true || profile_5GHz_cnt == 0) { - profile_create_backhaul(); - profile = &cfg->profiles[cfg->num_profiles - 1]; - - mapping = &g_dynamic_mapping[cfg->num_profiles - 1]; - mapping->ssid_idx = -1; - mapping->profile_idx = profile->profile_idx; - } else { - if (profile_cnt == 1) { - profile = &cfg->profiles[0]; - profile->bss_state |= MAP_BACKHAUL_BSS; - profile_save(profile, 0); - } else { - for (i = 0; i < profile_cnt; i++) { - profile = &cfg->profiles[i]; - if (profile->bss_freq_bands == MAP_FREQ_BAND_5G) { - profile->bss_state |= MAP_BACKHAUL_BSS; - profile_save(profile, i); - break; - } - } - } - } - - return; -} - static void wifi_static_mapper(void) { bool ssid_enable; @@ -1509,9 +1685,9 @@ static void wifi_static_mapper(void) profile_cnt = cfg->num_profiles; for (ssid_idx = 1; ssid_idx <= ssid_cnt; ssid_idx++) { mapping = NULL; - for (i = 0; i < ARRAY_SIZE(g_static_mapping); i++) { - if (g_static_mapping[i].ssid_idx == ssid_idx) { - mapping = &g_static_mapping[i]; + for (i = 0; i < ARRAY_SIZE(g_default_mappings); i++) { + if (g_default_mappings[i].ssid_idx == ssid_idx) { + mapping = &g_default_mappings[i]; break; } } @@ -1556,7 +1732,7 @@ static void wifi_static_mapper(void) strcpy(profile->bss_ssid, ssid); strcpy(profile->wpa_key, key); get_iface_security_mode(mode, &profile->supported_auth_modes, - &profile->supported_encryption_types, profile->profile_idx); + &profile->supported_encryption_types); profile->bss_freq_bands = freqband; profile->bss_state = mapping->bss_state; profile->gateway = true; @@ -1686,6 +1862,9 @@ static int chan_sel_cfg_load(map_chan_sel_cfg_t *cfg) getpsm_val(MAP_PSM_BANDLOCK5G, value, sizeof(value)); cfg->bandlock_5g = bandlock_type_from_string(value); + cfg->align_multiap = false; + cfg->align_multiap_backoff_time = 60; + return 0; } @@ -1842,14 +2021,13 @@ int map_profile_load(bool *ret_changed, bool dump_profiles) if (!profile_cnt) { /* Either sync with Device.WiFi.SSID or use defaults */ if (g_sync_with_wifi) { - if (g_use_dynamic_mapping) { - wifi_dynamic_mapper(); - } else { - wifi_static_mapper(); - } + wifi_static_mapper(); } if (!cfg->num_profiles) { + /* No valid Device.WiFi.SSID entries, disable sync */ + g_sync_with_wifi = 0; + profile_cnt = ARRAY_SIZE(g_default_profiles); /* Reallocate cfg->profiles */ @@ -1858,6 +2036,10 @@ int map_profile_load(bool *ret_changed, bool dump_profiles) for (i = 0; i < profile_cnt; i++) { cfg->profiles[i] = g_default_profiles[i]; profile_save(&cfg->profiles[i], i); + + if (cfg->mld_enabled == false && cfg->profiles[i].mld_id >= 0) { + cfg->mld_enabled = true; + } } cfg->num_profiles = profile_cnt; @@ -1884,9 +2066,8 @@ int map_profile_load(bool *ret_changed, bool dump_profiles) } } - if (!ret_changed) { - /* Init call, not config change */ - profile_match(&profile); + if(cfg->mld_enabled == false && profile.mld_id >= 0) { + cfg->mld_enabled = true; } idx++; @@ -1938,151 +2119,52 @@ int map_profile_realloc(unsigned int num_alloc_profiles) int map_profile_add(map_profile_cfg_t *profile) { - map_controller_cfg_t *cfg = &map_cfg_get()->controller_cfg; - unsigned int profile_cnt, i; - unsigned int ssid_idx; - char mode[24]; - bool send_apply = false; - - profile_cnt = cfg->num_profiles; - - for (i = 0; i < profile_cnt; i++) { - map_profile_cfg_t bp; - map_profile_cfg_t *pp = &cfg->profiles[i]; - - if (strcmp(profile->bss_ssid, pp->bss_ssid) == 0) { - if (pp->bss_state & MAP_BACKHAUL_BSS) { - log_lib_e("Modifying backhaul is illegal"); - return -1; - } - - if (profile->supported_auth_modes != IEEE80211_AUTH_MODE_OPEN && - profile->wpa_key[0] == '\0') { - log_lib_e("Invalid password"); - return -1; - } + map_profile_cfg_t bp = {0}; + uint16_t bss_freq_bands; + int rc = 0; - if (profile->label[0] == '\0') { - if (pp->label[0] != '\0') { - strcpy(profile->label, pp->label); - } else { - snprintf(profile->label, sizeof(profile->label), "ssid%d", i); - } - } - - if (profile->type == MAP_PROFILE_TYPE_OTHER) { - if (pp->type != MAP_PROFILE_TYPE_OTHER) { - profile->type = pp->type; - } - } - - map_profile_clone(&bp, pp); - profile_save(profile, i); - - if (g_map_cfg_cbs.profile_update_cb) { - g_map_cfg_cbs.profile_update_cb(); - } - - mapping_get_by_pidx(pp->profile_idx, &ssid_idx); - if ((int)ssid_idx <= 0) { - return 0; - } - - if (bp.supported_auth_modes != pp->supported_auth_modes) { - get_sec_mode_str(pp->supported_auth_modes, mode); - set_wifi_security_mode(ssid_idx, mode); - send_apply = true; - } - - if (strcmp(bp.wpa_key, pp->wpa_key) != 0) { - set_wifi_security_key(ssid_idx, pp->wpa_key); - send_apply = true; - } - - if (send_apply) { - /* Super hack; we need to store radio idx */ - set_wifi_apply_settings((ssid_idx % 2) + 1); - } - - return 0; - } + if (!g_sync_with_wifi) { + return profile_add(profile, false); } - if (profile->label[0] == '\0') { - snprintf(profile->label, sizeof(profile->label), "ssid%d", i); - } - /* Add new profile */ - profile_save(profile, profile_cnt); + bss_freq_bands = profile->bss_freq_bands; - profile_cnt = cfg->num_profiles + 1; - setpsm_int(MAP_PSM_SSIDNOE, profile_cnt); - - if (g_map_cfg_cbs.profile_update_cb) { - g_map_cfg_cbs.profile_update_cb(); + if (bss_freq_bands & MAP_FREQ_BAND_2G) { + map_profile_clone(&bp, profile); + bp.bss_freq_bands = MAP_FREQ_BAND_2G; + rc = profile_add(&bp, true); } - if (0) { - /* TODO */ - ssid_idx = -1; - set_wifi_ssid_enable(ssid_idx, true); - set_wifi_ssid_ssid(ssid_idx, profile->bss_ssid); - get_sec_mode_str(profile->supported_auth_modes, mode); - set_wifi_security_mode(ssid_idx, mode); - set_wifi_security_key(ssid_idx, profile->wpa_key); - set_wifi_apply_settings((ssid_idx % 2) + 1); + if (bss_freq_bands & MAP_FREQ_BAND_5G) { + profile->bss_freq_bands = MAP_FREQ_BAND_5G; + rc |= profile_add(profile, true); } - return 0; + return rc; } int map_profile_remove(map_profile_cfg_t *profile) { - map_controller_cfg_t *cfg = &map_cfg_get()->controller_cfg; - unsigned int profile_cnt, i; - int update = 0; - unsigned int ssid_idx = -1; + uint16_t bss_freq_bands; + int rc = 0; - profile_cnt = cfg->num_profiles; - - for (i = 0; i < profile_cnt; i++) { - map_profile_cfg_t *pp = &cfg->profiles[i]; - - if (strcmp(profile->bss_ssid, pp->bss_ssid) == 0) { - if (pp->bss_state & MAP_BACKHAUL_BSS) { - log_lib_e("Modifying backhaul is illegal"); - return -1; - } - - mapping_get_by_pidx(pp->profile_idx, &ssid_idx); - - /* Remove profile */ - for (; i < profile_cnt - 1; i++) { - map_profile_cfg_t tmp_profile = cfg->profiles[i + 1]; - - profile_save(&tmp_profile, i); - } - profile_remove(profile_cnt - 1); - - profile_cnt = cfg->num_profiles - 1;; - setpsm_int(MAP_PSM_SSIDNOE, profile_cnt); - - update = 1; - break; - } + if (!g_sync_with_wifi) { + return profile_remove(profile, false); } - if (update && g_map_cfg_cbs.profile_update_cb) { - g_map_cfg_cbs.profile_update_cb(); + bss_freq_bands = profile->bss_freq_bands; + + if (bss_freq_bands & MAP_FREQ_BAND_2G) { + profile->bss_freq_bands = MAP_FREQ_BAND_2G; + rc = profile_remove(profile, true); } - if (0 && update && (int)ssid_idx > 0) { - /* TODO */ - set_wifi_ssid_enable(ssid_idx, false); - /* Super hack; we need to store radio idx */ - set_wifi_apply_settings((ssid_idx % 2) + 1); + if (bss_freq_bands & MAP_FREQ_BAND_5G) { + profile->bss_freq_bands = MAP_FREQ_BAND_5G; + rc |= profile_remove(profile, true); } - return 0; + return rc; } int map_profile_get_by_sidx(unsigned int ssid_idx, map_profile_cfg_t **profile) @@ -2094,13 +2176,8 @@ int map_profile_get_by_sidx(unsigned int ssid_idx, map_profile_cfg_t **profile) int profile_idx; unsigned int i; - if (g_use_dynamic_mapping) { - mapping = g_dynamic_mapping; - mapping_cnt = ARRAY_SIZE(g_dynamic_mapping); - } else { - mapping = g_static_mapping; - mapping_cnt = ARRAY_SIZE(g_static_mapping); - } + mapping = g_default_mappings; + mapping_cnt = ARRAY_SIZE(g_default_mappings); profile_idx = -1; for (i = 0; i < mapping_cnt; i++) { diff --git a/source/libplatform/src/map_data_model.c b/source/libplatform/src/map_data_model.c index 500deb2..e2dc73e 100644 --- a/source/libplatform/src/map_data_model.c +++ b/source/libplatform/src/map_data_model.c @@ -20,6 +20,8 @@ #include #include +#include + #define LOG_TAG "dm" #include "map_data_model.h" @@ -43,6 +45,11 @@ /*####################################################################### # TYPEDEFS # ########################################################################*/ +typedef struct { + list_head_t list; + uint8_t hash[SHA256_MAC_LEN]; +} map_dm_dpp_enrollee_hash_t; + typedef struct { list_head_t list; list_head_t hlist; @@ -57,7 +64,7 @@ static map_event_lists_t g_event_lists; static LIST_HEAD(g_dm_cbs_list); static LIST_HEAD(g_dm_unassociated_station_list); - +static LIST_HEAD(g_dpp_hash_list); /* Inactive sta list */ static LIST_HEAD(g_inactive_sta_list); @@ -156,6 +163,34 @@ static void call_bss_remove_cbs(map_bss_info_t *bss) list_for_each_entry(cbs, &g_dm_cbs_list, list) if (cbs->bss_remove_cb) cbs->bss_remove_cb(bss); } +static void call_ap_mld_create_cbs(map_ap_mld_info_t *ap_mld) +{ + map_dm_cbs_t *cbs; + + list_for_each_entry(cbs, &g_dm_cbs_list, list) if (cbs->ap_mld_create_cb) cbs->ap_mld_create_cb(ap_mld); +} + +static void call_ap_mld_update_cbs(map_ap_mld_info_t *ap_mld) +{ + map_dm_cbs_t *cbs; + + list_for_each_entry(cbs, &g_dm_cbs_list, list) if (cbs->ap_mld_update_cb) cbs->ap_mld_update_cb(ap_mld); +} + +static void call_ap_mld_remove_cbs(map_ap_mld_info_t *ap_mld) +{ + map_dm_cbs_t *cbs; + + list_for_each_entry(cbs, &g_dm_cbs_list, list) if (cbs->ap_mld_remove_cb) cbs->ap_mld_remove_cb(ap_mld); +} + +static void call_bsta_mld_update_cbs(map_bsta_mld_info_t *bsta_mld) +{ + map_dm_cbs_t *cbs; + + list_for_each_entry(cbs, &g_dm_cbs_list, list) if (cbs->bsta_mld_update_cb) cbs->bsta_mld_update_cb(bsta_mld); +} + static void call_sta_create_cbs(map_sta_info_t *sta) { map_dm_cbs_t *cbs; @@ -177,6 +212,20 @@ static void call_sta_remove_cbs(map_sta_info_t *sta) list_for_each_entry(cbs, &g_dm_cbs_list, list) if (cbs->sta_remove_cb) cbs->sta_remove_cb(sta); } +static void call_sta_mld_create_cbs(map_sta_mld_info_t *sta_mld) +{ + map_dm_cbs_t *cbs; + + list_for_each_entry(cbs, &g_dm_cbs_list, list) if (cbs->sta_mld_create_cb) cbs->sta_mld_create_cb(sta_mld); +} + +static void call_sta_mld_remove_cbs(map_sta_mld_info_t *sta_mld) +{ + map_dm_cbs_t *cbs; + + list_for_each_entry(cbs, &g_dm_cbs_list, list) if (cbs->sta_mld_remove_cb) cbs->sta_mld_remove_cb(sta_mld); +} + static void call_assoc_create_cbs(map_assoc_data_t *assoc) { map_dm_cbs_t *cbs; @@ -219,6 +268,50 @@ static void call_failconn_remove_cbs(map_failconn_data_t *failconn) list_for_each_entry(cbs, &g_dm_cbs_list, list) if (cbs->failconn_remove_cb) cbs->failconn_remove_cb(failconn); } +/*####################################################################### +# DPP ENROLLEE HASH LIST # +########################################################################*/ +static void map_dm_dpp_hash_list_fini(void) { + map_dm_clear_dpp_hash_list(); +} + +void map_dm_clear_dpp_hash_list(void) { + map_dm_dpp_enrollee_hash_t *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, &g_dpp_hash_list, list) { + list_del(&entry->list); + free(entry); + } +} + +int map_dm_add_dpp_hash(const uint8_t *hash) { + if (map_dm_get_dpp_hash(hash)) { + /* already exists */ + return 0; + } + + map_dm_dpp_enrollee_hash_t *new_entry = calloc(1, sizeof(map_dm_dpp_enrollee_hash_t)); + if (!new_entry) { + return -1; + } + + memcpy(new_entry->hash, hash, sizeof(new_entry->hash)); + list_add_tail(&new_entry->list, &g_dpp_hash_list); + return 0; +} + +bool map_dm_get_dpp_hash(const uint8_t *hash) { + map_dm_dpp_enrollee_hash_t *entry; + + list_for_each_entry(entry, &g_dpp_hash_list, list) { + if (memcmp(entry->hash, hash, sizeof(entry->hash)) == 0) { + return true; + } + } + + return false; +} + /*####################################################################### # INACTIVE STA LIST # ########################################################################*/ @@ -375,6 +468,23 @@ static inline void cleanup_sta_timers(map_sta_info_t *sta) map_timer_unregister_callback_prefix(timer_id); } +static inline void cleanup_sta_mld_timers(map_sta_mld_info_t *sta_mld) +{ + timer_id_t timer_id; + + map_dm_get_sta_mld_timer_id(timer_id, sta_mld, ""); + map_unregister_retry_prefix(timer_id); + map_timer_unregister_callback_prefix(timer_id); +} + +static void set_counter_48(uint8_t *array, uint64_t value) { + int i; + for (i = 5; i >= 0; i--) { + array[i] = (uint8_t)(value & 0xFF); // Extract the least significant byte + value >>= 8; // Shift the value right by 8 bits + } +} + /*####################################################################### # ALE # ########################################################################*/ @@ -418,9 +528,19 @@ map_ale_info_t *map_dm_create_ale(mac_addr al_mac) return NULL; } + /* Init key info counters */ + set_counter_48(ale->key_info.encr_rx_counter, 0); + set_counter_48(ale->key_info.encr_tx_counter, 1); + set_counter_48(ale->key_info.integrity_rx_counter, 0); + set_counter_48(ale->key_info.integrity_tx_counter, 1); + + /* Set back pointer for bsta_mld */ + ale->bsta_mld.ale = ale; + /* Init lists */ INIT_LIST_HEAD(&ale->list); INIT_LIST_HEAD(&ale->radio_list); + INIT_LIST_HEAD(&ale->ap_mld_list); /* Add to linked list */ list_add_tail(&ale->list, &g_ale_list); @@ -429,12 +549,45 @@ map_ale_info_t *map_dm_create_ale(mac_addr al_mac) call_ale_create_cbs(ale); log_lib_d("-----------------------------------------------------"); - log_lib_d("| New MAP Agent %s ", ale->al_mac_str); + if (maccmp(al_mac, map_cfg_get()->controller_cfg.al_mac)) { + log_lib_d("| %s MAP Agent %s", ale->is_local ? "Local" : "New", ale->al_mac_str); + } else { + log_lib_d("| MAP Controller %s", ale->al_mac_str); + } log_lib_d("-----------------------------------------------------"); return ale; } +void map_dm_remove_ale_key_info(map_ale_info_t *ale) +{ + if (!ale) + return; + + memset(ale->key_info.ptk, 0, sizeof(ale->key_info.ptk)); + ale->key_info.ptk_len = 0; + memset(ale->key_info.gtk, 0, sizeof(ale->key_info.gtk)); + ale->key_info.gtk_len = 0; + set_counter_48(ale->key_info.encr_rx_counter, 0); + set_counter_48(ale->key_info.encr_tx_counter, 1); + set_counter_48(ale->key_info.integrity_rx_counter, 0); + set_counter_48(ale->key_info.integrity_tx_counter, 1); +} + +int map_dm_get_ale_key_info(mac_addr al_mac, map_1905_sec_key_info_t *key_info) +{ + map_ale_info_t *ale; + + map_dm_foreach_ale(ale) { + if (!maccmp(ale->al_mac, al_mac)) { + memcpy(key_info, &ale->key_info, sizeof(*key_info)); + return 0; + } + } + + return -1; +} + map_ale_info_t* map_dm_get_ale(mac_addr al_mac) { map_ale_info_t *ale; @@ -481,36 +634,48 @@ map_ale_info_t *map_dm_get_ale_from_src_mac(mac_addr src_mac) int map_dm_remove_ale(map_ale_info_t *ale) { - map_radio_info_t *radio, *next; + map_ap_mld_info_t *ap_mld, *next_ap_mld; + map_radio_info_t *radio, *next_radio; int i; ale->removing = true; - /* Cleanup all the timers */ - cleanup_ale_timers(ale); - - /* Remove local interface list */ - free(ale->local_iface_list); - - /* Free non 1905 neighbors */ - map_dm_free_non_1905_neighbor_list(ale); - - /* Remove bachaul sta interface list */ - free(ale->backhaul_sta_iface_list); - /* Call remove callbacks before removing radios to avoid that they need to be remove one by one. This is ok for current use cases. */ call_ale_remove_cbs(ale); + /* cleanup the ap mld */ + map_dm_foreach_ap_mld_safe(ale, ap_mld, next_ap_mld) { + if (map_dm_remove_ap_mld(ap_mld)) { + log_lib_e("failed Removing ap_mld"); + } + } + /* cleanup the radios */ - map_dm_foreach_radio_safe(ale, radio, next) { + map_dm_foreach_radio_safe(ale, radio, next_radio) { if (map_dm_remove_radio(radio)) { log_lib_e("failed Removing radio"); /* procceed cleaning other resources Event if cleanup of one radio failed. */ } } + /* Cleanup all the timers */ + cleanup_ale_timers(ale); + + /* Remove steering disallowed macs lists */ + free(ale->btm_steering_disallow_macs); + free(ale->local_steering_disallow_macs); + + /* Remove local interface list */ + free(ale->local_iface_list); + + /* Free non 1905 neighbors */ + map_dm_free_non_1905_neighbor_list(ale); + + /* Remove bachaul sta interface list */ + free(ale->backhaul_sta_iface_list); + /* cleanup neighbor link metric references */ if (ale->eth_neigh_link_metric_list != NULL) { while (list_get_size(ale->eth_neigh_link_metric_list) > 0) { @@ -539,6 +704,9 @@ int map_dm_remove_ale(map_ale_info_t *ale) free(ale->dpp_info.message.frame); free(ale->dpp_info.encap_eapol.frame); + /* Clean 1905 security specific allocations */ + map_dm_remove_ale_key_info(ale); + /* Free list of ethernet device macs */ free(ale->eth_device_list.macs); for (i = 0; i < ETH_DEVICE_HISTORY_LEN; i++) { @@ -577,6 +745,8 @@ map_radio_info_t *map_dm_create_radio(map_ale_info_t *ale, mac_addr radio_id) /* Update radio_id */ maccpy(radio->radio_id, radio_id); mac_to_string(radio->radio_id, radio->radio_id_str); + b64_encode(radio->radio_id, sizeof(radio->radio_id), radio->radio_id_base64, sizeof(radio->radio_id_base64)); + //TODO: acu_base64_encode_to_buf(radio->radio_id, sizeof(mac_addr), radio->radio_id_base64, sizeof(radio->radio_id_base64)); /* Set state */ set_radio_state_on(&radio->state); @@ -640,9 +810,6 @@ int map_dm_remove_radio(map_radio_info_t *radio) { map_bss_info_t *bss, *next; - /* Cleanup all the retry timers associated with this radio */ - cleanup_radio_timers(radio); - /* Call remove callbacks before removing bss to avoid that they need to be remove one by one. This is ok for current use cases. */ @@ -656,6 +823,9 @@ int map_dm_remove_radio(map_radio_info_t *radio) } } + /* Cleanup all the retry timers associated with this radio */ + cleanup_radio_timers(radio); + /* Cleanup scan results list */ if (radio->scanned_bssid_list) { while (list_get_size(radio->scanned_bssid_list) > 0) { @@ -676,19 +846,32 @@ int map_dm_remove_radio(map_radio_info_t *radio) free(radio->ht_caps); free(radio->vht_caps); free(radio->he_caps); + free(radio->eht_caps); free(radio->cap_op_class_list.op_classes); free(radio->pref_op_class_list.op_classes); free(radio->ctrl_pref_op_class_list.op_classes); + free(radio->disallowed_op_class_list.op_classes); free(radio->merged_pref_op_class_list.op_classes); free(radio->curr_op_class_list.op_classes); - free(radio->op_restriction_list.op_classes); free(radio->scan_caps.op_class_list.op_classes); + map_dm_free_op_restriction_list(radio); map_dm_free_cac_methods(radio->cac_caps.cac_method, radio->cac_caps.cac_method_count); free(radio->cac_completion_info.detected_pairs); free(radio->wifi6_caps); + if (radio->wifi7_caps) { + SFREE(radio->wifi7_caps->ap_str_records); + SFREE(radio->wifi7_caps->ap_nstr_records); + SFREE(radio->wifi7_caps->ap_emlsr_records); + SFREE(radio->wifi7_caps->ap_emlmr_records); + SFREE(radio->wifi7_caps->bsta_str_records); + SFREE(radio->wifi7_caps->bsta_nstr_records); + SFREE(radio->wifi7_caps->bsta_emlsr_records); + SFREE(radio->wifi7_caps->bsta_emlmr_records); + SFREE(radio->wifi7_caps); + } /* Unlink */ radio->ale->radios_nr--; @@ -739,6 +922,7 @@ map_bss_info_t *map_dm_create_bss(map_radio_info_t *radio, mac_addr bssid) for (i = 0; i < MAP_MAX_MAC_HASH; i++) { INIT_LIST_HEAD(&bss->sta_hlist[i]); } + INIT_LIST_HEAD(&bss->aff_ap_list); /* Add to linked list */ bss->radio = radio; @@ -820,6 +1004,10 @@ int map_dm_remove_bss(map_bss_info_t *bss) /* Unlink */ bss->radio->bsss_nr--; list_del(&bss->list); + if (bss->ap_mld) { + list_del(&bss->aff_ap_list); + call_ap_mld_update_cbs(bss->ap_mld); + } log_lib_d("Bss[%s] removed", bss->bssid_str); @@ -831,8 +1019,8 @@ int map_dm_remove_bss(map_bss_info_t *bss) /*####################################################################### # STA # ########################################################################*/ - -int map_dm_sta_init_payload(map_sta_info_t *sta, void *payload) { +int map_dm_sta_init_payload(map_sta_info_t *sta, void *payload) +{ if (!sta) { return -1; } @@ -846,7 +1034,7 @@ int map_dm_sta_init_payload(map_sta_info_t *sta, void *payload) { return 0; } -map_sta_info_t *map_dm_create_sta(map_bss_info_t *bss, mac_addr mac) +static map_sta_info_t *create_sta(map_bss_info_t *bss, map_sta_mld_info_t *sta_mld, mac_addr mac) { map_sta_info_t *sta = NULL; @@ -895,14 +1083,23 @@ map_sta_info_t *map_dm_create_sta(map_bss_info_t *bss, mac_addr mac) return NULL; } + /* Init lists */ + INIT_LIST_HEAD(&sta->list); + INIT_LIST_HEAD(&sta->hlist); + /* Link with bss */ sta->bss = bss; bss->stas_nr++; - INIT_LIST_HEAD(&sta->list); - INIT_LIST_HEAD(&sta->hlist); list_add_tail(&sta->list, &bss->sta_list); list_add_tail(&sta->hlist, &bss->sta_hlist[mac_hash(sta->mac)]); + /* Link to sta_mld when this is affiliated sta */ + if (sta_mld) { + sta_mld->aff_sta_nr++; + sta->sta_mld = sta_mld; + list_add_tail(&sta->aff_sta_list, &sta_mld->aff_sta_list); + } + /* Remove from inactive sta list */ inactive_sta_remove(mac); @@ -910,23 +1107,38 @@ map_sta_info_t *map_dm_create_sta(map_bss_info_t *bss, mac_addr mac) call_sta_create_cbs(sta); log_lib_d("-----------------------------------------------------"); - log_lib_d("| New STA %s ", sta->mac_str); + log_lib_d("| New %sSTA %s", sta_mld ? "Affiliated " : "", sta->mac_str); log_lib_d("-----------------------------------------------------"); return sta; } +map_sta_info_t *map_dm_create_sta(map_bss_info_t *bss, mac_addr mac) +{ + return create_sta(bss, NULL, mac); +} + +map_sta_info_t *map_dm_create_aff_sta(map_bss_info_t *bss, map_sta_mld_info_t *sta_mld, mac_addr mac) +{ + return create_sta(bss, sta_mld, mac); +} + int map_dm_update_sta_bss(map_bss_info_t *bss, map_sta_info_t *sta) { - int h = mac_hash(sta->mac); + int h = mac_hash(sta->mac); + bool roamed = sta->bss; + + if (sta->bss == bss) { + return 0; + } /* Remove from old bss (can only be NULL when called from create_bss above) */ if (sta->bss) { - if (sta->bss == bss) { - return 0; + if (roamed) { + /* Call remove callbacks (for old bss) */ + call_sta_remove_cbs(sta); } - call_sta_remove_cbs(sta); /* Cleanup retry timers associated with STA */ cleanup_sta_timers(sta); @@ -941,7 +1153,10 @@ int map_dm_update_sta_bss(map_bss_info_t *bss, map_sta_info_t *sta) list_add_tail(&sta->list, &bss->sta_list); list_add_tail(&sta->hlist, &bss->sta_hlist[h]); - call_sta_create_cbs(sta); + if (roamed) { + /* Call create callbacks (for new bss) */ + call_sta_create_cbs(sta); + } return 0; } @@ -1030,9 +1245,6 @@ int map_dm_remove_sta(map_sta_info_t *sta) sta->beacon_metrics = NULL; } - /* Cleanup STA traffic stats */ - SFREE(sta->traffic_stats); - /* Cleanup STA assoc frame */ SFREE(sta->assoc_frame); @@ -1056,6 +1268,10 @@ int map_dm_remove_sta(map_sta_info_t *sta) sta->bss->stas_nr--; list_del_init(&sta->list); list_del_init(&sta->hlist); + if (sta->sta_mld) { + sta->sta_mld->aff_sta_nr--; + list_del(&sta->aff_sta_list); + } sta->bss = NULL; unassociated_station_list_add(sta); @@ -1066,6 +1282,235 @@ int map_dm_remove_sta(map_sta_info_t *sta) return 0; } +/*####################################################################### +# AP MLD # +########################################################################*/ +map_ap_mld_info_t* map_dm_create_ap_mld(map_ale_info_t *ale, mac_addr mld_mac) +{ + map_ap_mld_info_t *ap_mld; + int i; + + if ((ap_mld = map_dm_get_ap_mld(ale, mld_mac))) { + return ap_mld; + } + + if (!(ap_mld = calloc(1, sizeof(map_ap_mld_info_t)))) { + log_lib_e("failed to allocating memory"); + return NULL; + } + + /* Update mac address */ + maccpy(ap_mld->mac, mld_mac); + mac_to_string(ap_mld->mac, ap_mld->mac_str); + + /* Init lists */ + INIT_LIST_HEAD(&ap_mld->list); + INIT_LIST_HEAD(&ap_mld->aff_ap_list); + INIT_LIST_HEAD(&ap_mld->sta_mld_list); + for (i = 0; i < MAP_MAX_MAC_HASH; i++) { + INIT_LIST_HEAD(&ap_mld->sta_mld_hlist[i]); + } + + /* Add to linked list */ + ap_mld->ale = ale; + ale->ap_mld_nr++; + list_add_tail(&ap_mld->list, &ale->ap_mld_list); + + /* Call create callbacks */ + call_ap_mld_create_cbs(ap_mld); + + log_lib_d("-----------------------------------------------------"); + log_lib_d("| New AP_MLD %s", ap_mld->mac_str); + log_lib_d("-----------------------------------------------------"); + + return ap_mld; +} + +map_ap_mld_info_t* map_dm_get_ap_mld(map_ale_info_t *ale, mac_addr mld_mac) +{ + map_ap_mld_info_t *ap_mld; + + map_dm_foreach_ap_mld(ale, ap_mld) { + if (!maccmp(ap_mld->mac, mld_mac)) { + return ap_mld; + } + } + + return NULL; +} + +int map_dm_remove_ap_mld(map_ap_mld_info_t *ap_mld) +{ + map_bss_info_t *bss, *next_bss; + map_sta_mld_info_t *sta_mld, *next_sta_mld; + + /* Call remove callbacks */ + call_ap_mld_remove_cbs(ap_mld); + + /* Remove affiliated aps */ + map_dm_foreach_aff_ap_safe(ap_mld, bss, next_bss) { + map_dm_bss_set_ap_mld(bss, NULL); + } + + /* Remove sta_mld */ + map_dm_foreach_sta_mld_safe(ap_mld, sta_mld, next_sta_mld) { + map_dm_remove_sta_mld(sta_mld); + } + + /* Unlink */ + ap_mld->ale->ap_mld_nr--; + list_del(&ap_mld->list); + + log_lib_d("AP_MLD[%s] removed", ap_mld->mac_str); + + free(ap_mld); + + return 0; +} + +/*####################################################################### +# STA_MLD # +########################################################################*/ +map_sta_mld_info_t *map_dm_create_sta_mld(map_ap_mld_info_t *ap_mld, mac_addr mac) +{ + map_sta_mld_info_t *sta_mld; + + if ((sta_mld = map_dm_get_sta_mld(ap_mld, mac))) { + return sta_mld; + } + + if (!(sta_mld = calloc(1, sizeof(map_sta_mld_info_t)))) { + log_lib_e("failed to allocating memory"); + return NULL; + } + + /* Update mac address */ + maccpy(sta_mld->mac, mac); + mac_to_string(sta_mld->mac, sta_mld->mac_str); + + sta_mld->assoc_ts = acu_get_timestamp_sec(); + + /* Init lists */ + INIT_LIST_HEAD(&sta_mld->list); + INIT_LIST_HEAD(&sta_mld->hlist); + INIT_LIST_HEAD(&sta_mld->aff_sta_list); + + /* Link with ap_mld */ + sta_mld->ap_mld = ap_mld; + ap_mld->sta_mld_nr++; + list_add_tail(&sta_mld->list, &ap_mld->sta_mld_list); + list_add_tail(&sta_mld->hlist, &ap_mld->sta_mld_hlist[mac_hash(sta_mld->mac)]); + + /* Remove from inactive sta list */ + inactive_sta_remove(mac); + + /* Call create callbacks */ + call_sta_mld_create_cbs(sta_mld); + + log_lib_d("-----------------------------------------------------"); + log_lib_d("| New STA MLD %s", sta_mld->mac_str); + log_lib_d("-----------------------------------------------------"); + + return sta_mld; +} + +static map_sta_mld_info_t *get_sta_mld_h(map_ap_mld_info_t *ap_mld, mac_addr mac, int h) +{ + map_sta_mld_info_t *sta_mld; + + list_for_each_entry(sta_mld, &ap_mld->sta_mld_hlist[h], hlist) { + if (!maccmp(sta_mld->mac, mac)) { + return sta_mld; + } + } + + return NULL; +} + +static map_sta_mld_info_t *get_sta_mld_from_ale_h(map_ale_info_t *ale, mac_addr mac, int h) +{ + map_ap_mld_info_t *ap_mld; + map_sta_mld_info_t *sta_mld; + + map_dm_foreach_ap_mld(ale, ap_mld) { + if ((sta_mld = get_sta_mld_h(ap_mld, mac, h))) { + return sta_mld; + } + } + + return NULL; +} + +map_sta_mld_info_t *map_dm_get_sta_mld(map_ap_mld_info_t *ap_mld, mac_addr mac) +{ + return get_sta_mld_h(ap_mld, mac, mac_hash(mac)); +} + +map_sta_mld_info_t* map_dm_get_sta_mld_from_ale(map_ale_info_t *ale, mac_addr mac) +{ + return get_sta_mld_from_ale_h(ale, mac, mac_hash(mac)); +} + +map_sta_mld_info_t *map_dm_get_sta_mld_gbl(mac_addr mac) +{ + map_ale_info_t *ale; + map_sta_mld_info_t *sta_mld; + int h = mac_hash(mac); + + map_dm_foreach_agent_ale(ale) { + if ((sta_mld = get_sta_mld_from_ale_h(ale, mac, h))) { + return sta_mld; + } + } + + return NULL; +} + +map_sta_info_t *map_dm_get_aff_sta(map_sta_mld_info_t *sta_mld, mac_addr mac) +{ + map_sta_info_t *sta; + + map_dm_foreach_aff_sta(sta_mld, sta) { + if (!maccmp(sta->mac, mac)) { + return sta; + } + } + + return NULL; +} + +int map_dm_remove_sta_mld(map_sta_mld_info_t *sta_mld) +{ + map_sta_info_t *sta, *next; + + /* Remove affiliated sta */ + map_dm_foreach_aff_sta_safe(sta_mld, sta, next) { + map_dm_remove_sta(sta); + } + + /* Call remove callbacks */ + call_sta_mld_remove_cbs(sta_mld); + + /* Cleanup retry timers associated with STA */ + cleanup_sta_mld_timers(sta_mld); + + /* Cleanup STA assoc frame */ + free(sta_mld->assoc_frame); + + /* Unlink */ + sta_mld->ap_mld->sta_mld_nr--; + list_del(&sta_mld->list); + list_del(&sta_mld->hlist); + + /* Add to inactive sta list */ + inactive_sta_add(sta_mld->mac); + + log_lib_d("STA_MLD[%s] removed", sta_mld->mac_str); + + free(sta_mld); + return 0; +} + /*####################################################################### # CONNECTION EVENTS # ########################################################################*/ @@ -1227,6 +1672,9 @@ void map_dm_ale_set_onboard_status(map_ale_info_t *ale, map_onboard_status_t sta ale->ale_onboard_status = status; if (ale->ale_onboard_status == ALE_NODE_ONBOARDED) { ale->ale_onboarding_time = acu_get_timestamp_sec(); + if(!ale->ale_onboarded_first_time) { + ale->ale_onboarded_first_time = true; + } } update = true; } @@ -1264,6 +1712,24 @@ void map_dm_ale_set_upstream_info(map_ale_info_t *ale, mac_addr us_al_mac, mac_a } if (update) { + if (0 && !maccmp(get_root_ale_node()->al_mac, us_al_mac) && !ale->is_local) { + map_ale_info_t *local; + bool local_found = false; + + log_lib_e("ale %s has controller as us_al_mac", ale->al_mac_str); + map_dm_foreach_agent_ale(local) { + if (local->is_local) { + local_found = true; + break; + } + } + + if (!local_found) { + log_lib_e("no local agent, set ale %s as local", ale->al_mac_str); + ale->is_local = true; + } + } + call_ale_update_cbs(ale); } } @@ -1289,7 +1755,21 @@ void map_dm_radio_set_capabilities(map_radio_info_t *radio) call_radio_update_cbs(radio); } -void map_dm_radio_set_channel(map_radio_info_t *radio, uint8_t op_class, uint8_t channel, uint16_t bw, uint8_t tx_pwr) +void map_dm_radio_set_channel_configurable(map_radio_info_t *radio, bool channel_configurable) +{ + bool update = false; + + if (radio->channel_configurable != channel_configurable) { + radio->channel_configurable = channel_configurable; + update = true; + } + + if (update) { + call_radio_update_cbs(radio); + } +} + +void map_dm_radio_set_channel(map_radio_info_t *radio, uint8_t op_class, uint8_t channel, uint8_t highest_bw_channel, uint16_t bw, uint8_t tx_pwr) { bool update = false; @@ -1308,6 +1788,12 @@ void map_dm_radio_set_channel(map_radio_info_t *radio, uint8_t op_class, uint8_t update = true; } + if (highest_bw_channel && radio->highest_bw_channel != highest_bw_channel) { + /* highest_bw_channel=0 when called from topo response */ + radio->highest_bw_channel = highest_bw_channel; + update = true; + } + if (radio->current_tx_pwr != tx_pwr) { radio->current_tx_pwr = tx_pwr; update = true; @@ -1318,13 +1804,14 @@ void map_dm_radio_set_channel(map_radio_info_t *radio, uint8_t op_class, uint8_t } } -void map_dm_radio_set_chan_sel(map_radio_info_t *radio, bool acs_enable, map_channel_set_t *acs_channels, uint8_t channel, uint16_t bw) +void map_dm_radio_set_chan_sel(map_radio_info_t *radio, bool cloud_mgmt_enable, bool acs_enable, + map_channel_set_t *acs_channels, uint8_t channel, uint16_t bw) { map_radio_chan_sel_t *chan_sel = &radio->chan_sel; bool update = false; - if (map_cs_compare(&chan_sel->acs_channels, acs_channels)) { - map_cs_copy(&chan_sel->acs_channels, acs_channels); + if (chan_sel->cloud_mgmt_enable != cloud_mgmt_enable) { + chan_sel->cloud_mgmt_enable = cloud_mgmt_enable; update = true; } @@ -1333,6 +1820,11 @@ void map_dm_radio_set_chan_sel(map_radio_info_t *radio, bool acs_enable, map_cha update = true; } + if (map_cs_compare(&chan_sel->acs_channels, acs_channels)) { + map_cs_copy(&chan_sel->acs_channels, acs_channels); + update = true; + } + if (chan_sel->channel != channel) { chan_sel->channel = channel; update = true; @@ -1465,9 +1957,213 @@ void map_dm_sta_steering_completed(map_ale_info_t *ale) steering_history->btm_response = IEEE80211_BTM_STATUS_UNKNOWN; } +void map_dm_bss_set_ap_mld(map_bss_info_t *bss, map_ap_mld_info_t *ap_mld) +{ + if (bss->ap_mld == ap_mld) { + return; + } + + if (bss->ap_mld) { + /* Remove from other ap_mld */ + bss->ap_mld->aff_ap_nr--; + list_del(&bss->aff_ap_list); + + call_ap_mld_update_cbs(bss->ap_mld); + } + bss->prev_ap_mld = bss->ap_mld; + + /* Add to new ap_mld */ + if (ap_mld) { + ap_mld->aff_ap_nr++; + list_add_tail(&bss->aff_ap_list, &ap_mld->aff_ap_list); + } + bss->ap_mld = ap_mld; + + call_bss_update_cbs(bss); + bss->prev_ap_mld = NULL; +} + +void map_dm_ap_mld_set(map_ap_mld_info_t *ap_mld, size_t ssid_len, uint8_t *ssid, + bool str, bool nstr, bool emlsr, bool emlmr, + map_aff_ap_cfg_t *aff_aps, size_t aff_ap_nr) +{ + map_bss_info_t *old_aff_ap, *new_aff_ap, *next; + bool update = false; + size_t i; + + ssid_len = min(ssid_len, MAX_SSID_LEN - 1); + + if (ap_mld->ssid_len != ssid_len || memcmp(ap_mld->ssid, ssid, ssid_len)) { + ap_mld->ssid_len = ssid_len; + memcpy(ap_mld->ssid, ssid, ssid_len); + ap_mld->ssid[ap_mld->ssid_len] = 0; + update = true; + } + + if (ap_mld->enabled_mld_modes.str != str) { + ap_mld->enabled_mld_modes.str = str; + update = true; + } + + if (ap_mld->enabled_mld_modes.nstr != nstr) { + ap_mld->enabled_mld_modes.nstr = nstr; + update = true; + } + + if (ap_mld->enabled_mld_modes.emlsr != emlsr) { + ap_mld->enabled_mld_modes.emlsr = emlsr; + update = true; + } + + if (ap_mld->enabled_mld_modes.emlmr != emlmr) { + ap_mld->enabled_mld_modes.emlmr = emlmr; + update = true; + } + + /* Check affiliated aps */ + /* Remove? */ + map_dm_foreach_aff_ap_safe(ap_mld, old_aff_ap, next) { + bool found = false; + + for (i = 0; i < aff_ap_nr; i++) { + new_aff_ap = aff_aps[i].bss; + if (new_aff_ap == old_aff_ap) { + found = true; + break; + } + } + + if (!found) { + map_dm_bss_set_ap_mld(old_aff_ap, NULL); + update = true; + } + } + + /* Add ? */ + for (i = 0; i < aff_ap_nr; i++) { + bool found = false; + new_aff_ap = aff_aps[i].bss; + + map_dm_foreach_aff_ap(ap_mld, old_aff_ap) { + if (new_aff_ap == old_aff_ap) { + found = true; + break; + } + } + + if (!found) { + map_dm_bss_set_ap_mld(new_aff_ap, ap_mld); + update = true; + } + + /* Check link_id */ + if (new_aff_ap->link_id != aff_aps[i].link_id) { + new_aff_ap->link_id = aff_aps[i].link_id; + update = true; + } + } + + if (update) { + call_ap_mld_update_cbs(ap_mld); + } +} + +void map_dm_bsta_mld_set(map_bsta_mld_info_t *bsta_mld, bool valid, + mac_addr bsta_mld_mac, mac_addr ap_mld_mac, + bool str, bool nstr, bool emlsr, bool emlmr, + mac_addr *aff_bsta_macs, size_t aff_bsta_mac_nr) +{ + bool update = false; + + if (bsta_mld->valid != valid) { + bsta_mld->valid = valid; + update = true; + + if (!valid) { + /* Clear fields */ + maccpy(bsta_mld->mac, g_zero_mac); + maccpy(bsta_mld->ap_mld_mac, g_zero_mac); + + bsta_mld->aff_bsta_mac_nr = 0; + + bsta_mld->enabled_mld_modes.str = false; + bsta_mld->enabled_mld_modes.nstr = false; + bsta_mld->enabled_mld_modes.emlsr = false; + bsta_mld->enabled_mld_modes.emlmr = false; + + /* Skip all the rest */ + goto check_update; + } + } + + if (bsta_mld_mac && maccmp(bsta_mld->mac, bsta_mld_mac)) { + maccpy(bsta_mld->mac, bsta_mld_mac); + mac_to_string(bsta_mld->mac, bsta_mld->mac_str); + update = true; + } + + if (ap_mld_mac && maccmp(bsta_mld->ap_mld_mac, ap_mld_mac)) { + maccpy(bsta_mld->ap_mld_mac, ap_mld_mac); + update = true; + } + + if (bsta_mld->enabled_mld_modes.str != str) { + bsta_mld->enabled_mld_modes.str = str; + update = true; + } + + if (bsta_mld->enabled_mld_modes.nstr != nstr) { + bsta_mld->enabled_mld_modes.nstr = nstr; + update = true; + } + + if (bsta_mld->enabled_mld_modes.emlsr != emlsr) { + bsta_mld->enabled_mld_modes.emlsr = emlsr; + update = true; + } + + if (bsta_mld->enabled_mld_modes.emlmr != emlmr) { + bsta_mld->enabled_mld_modes.emlmr = emlmr; + update = true; + } + + if (aff_bsta_macs) { + if (aff_bsta_mac_nr > MAX_MLD_AFF_APSTA) { + aff_bsta_mac_nr = MAX_MLD_AFF_APSTA; + } + + size_t len = aff_bsta_mac_nr * sizeof(mac_addr); + + if (bsta_mld->aff_bsta_mac_nr != aff_bsta_mac_nr || + memcmp(bsta_mld->aff_bsta_macs, aff_bsta_macs, len)) { + + bsta_mld->aff_bsta_mac_nr = aff_bsta_mac_nr; + memcpy(bsta_mld->aff_bsta_macs, aff_bsta_macs, len); + update = true; + } + } + +check_update: + if (update) { + call_bsta_mld_update_cbs(bsta_mld); + } +} + /*####################################################################### # VARIOUS # ########################################################################*/ +void map_dm_free_op_restriction_list(map_radio_info_t *radio) +{ + uint8_t i; + + for (i = 0; i < radio->op_restriction_list.op_classes_nr; i++) { + SFREE(radio->op_restriction_list.op_classes[i].channels); + } + + SFREE(radio->op_restriction_list.op_classes); + radio->op_restriction_list.op_classes_nr = 0; +} + void map_dm_free_cac_methods(map_cac_method_t *cac_method, uint8_t count) { uint8_t i; @@ -1527,6 +2223,11 @@ void map_dm_get_sta_timer_id(timer_id_t id, map_sta_info_t *sta, const char *typ snprintf(id, sizeof(timer_id_t), "STA-%s_%s-%s", sta->bss->bssid_str, sta->mac_str, type); } +void map_dm_get_sta_mld_timer_id(timer_id_t id, map_sta_mld_info_t *sta_mld, const char *type) +{ + snprintf(id, sizeof(timer_id_t), "MSTA-%s_%s-%s", sta_mld->ap_mld->mac_str, sta_mld->mac_str, type); +} + void map_dm_mark_stas(map_ale_info_t *ale) { map_radio_info_t *radio; @@ -1563,6 +2264,32 @@ void map_dm_remove_marked_stas(map_ale_info_t *ale, unsigned int min_assoc_time) } } +void map_dm_mark_sta_mlds(map_ale_info_t *ale) +{ + map_ap_mld_info_t *ap_mld; + map_sta_mld_info_t *sta_mld; + + map_dm_foreach_ap_mld(ale, ap_mld) { + map_dm_foreach_sta_mld(ap_mld, sta_mld) { + map_dm_mark_sta_mld(sta_mld); + } + } +} + +void map_dm_remove_marked_sta_mlds(map_ale_info_t *ale) +{ + map_ap_mld_info_t *ap_mld; + map_sta_mld_info_t *sta_mld, *next_sta_mld; + + map_dm_foreach_ap_mld(ale, ap_mld) { + map_dm_foreach_sta_mld_safe(ap_mld, sta_mld, next_sta_mld) { + if (map_dm_is_marked_sta_mld(sta_mld)) { + map_dm_remove_sta_mld(sta_mld); + } + } + } +} + /*####################################################################### # INIT # ########################################################################*/ @@ -1576,6 +2303,7 @@ int map_dm_init(void) INIT_LIST_HEAD(&g_dm_cbs_list); INIT_LIST_HEAD(&g_dm_unassociated_station_list); + INIT_LIST_HEAD(&g_dpp_hash_list); map_dm_rbus_init(); map_dm_eth_device_list_init(call_ale_eth_device_list_update_cbs); @@ -1621,6 +2349,7 @@ void map_dm_fini(void) map_dm_remove_ale(ale); } + map_dm_dpp_hash_list_fini(); inactive_sta_fini(); } diff --git a/source/libplatform/src/map_data_model_dumper.c b/source/libplatform/src/map_data_model_dumper.c index 31459c7..d663fdc 100644 --- a/source/libplatform/src/map_data_model_dumper.c +++ b/source/libplatform/src/map_data_model_dumper.c @@ -86,16 +86,15 @@ static void print_sta_link_metrics(map_sta_info_t *sta, size_t print_last_n, map static void print_sta_metrics( map_sta_info_t *sta, map_printf_cb_t print_cb) { - if (sta->traffic_stats) { - print_cb(" | - TRAFFIC STATS :\n"); - print_cb(" | - Tx bytes : %"PRIu64"\n", sta->traffic_stats->txbytes); - print_cb(" | - Rx bytes : %"PRIu64"\n", sta->traffic_stats->rxbytes); - print_cb(" | - Tx pkts : %d\n", sta->traffic_stats->txpkts); - print_cb(" | - Rx pkts : %d\n", sta->traffic_stats->rxpkts); - print_cb(" | - Tx pkt errors : %d\n", sta->traffic_stats->txpkterrors); - print_cb(" | - Rx pkt errors : %d\n", sta->traffic_stats->rxpkterrors); - print_cb(" | - ReTx count : %d\n", sta->traffic_stats->retransmission_cnt); - } + print_cb(" | - TRAFFIC STATS :\n"); + print_cb(" | - Tx bytes : %"PRIu64"\n", sta->traffic_stats.tx_bytes); + print_cb(" | - Rx bytes : %"PRIu64"\n", sta->traffic_stats.rx_bytes); + print_cb(" | - Tx pkts : %d\n", sta->traffic_stats.tx_packets); + print_cb(" | - Rx pkts : %d\n", sta->traffic_stats.rx_packets); + print_cb(" | - Tx pkt errors : %d\n", sta->traffic_stats.tx_packet_errors); + print_cb(" | - Rx pkt errors : %d\n", sta->traffic_stats.rx_packet_errors); + print_cb(" | - ReTx count : %d\n", sta->traffic_stats.retransmissions); + print_sta_link_metrics(sta, 1, print_cb); } @@ -148,12 +147,12 @@ static void print_ap_metrics(map_bss_info_t *bss, map_printf_cb_t print_cb) } } print_cb(" -----------------------------------------------\n"); - print_cb(" -unicast bytes tx : %"PRIu64"\n", bss->extended_metrics.ucast_bytes_tx); - print_cb(" -unicast bytes rx : %"PRIu64"\n", bss->extended_metrics.ucast_bytes_rx); - print_cb(" -multicast bytes tx : %"PRIu64"\n", bss->extended_metrics.mcast_bytes_tx); - print_cb(" -multicast bytes rx : %"PRIu64"\n", bss->extended_metrics.mcast_bytes_rx); - print_cb(" -broadcast bytes tx : %"PRIu64"\n", bss->extended_metrics.bcast_bytes_tx); - print_cb(" -broadcast bytes rx : %"PRIu64"\n", bss->extended_metrics.bcast_bytes_rx); + print_cb(" -unicast bytes tx : %"PRIu64"\n", bss->extended_metrics.tx_ucast_bytes); + print_cb(" -unicast bytes rx : %"PRIu64"\n", bss->extended_metrics.rx_ucast_bytes); + print_cb(" -multicast bytes tx : %"PRIu64"\n", bss->extended_metrics.tx_mcast_bytes); + print_cb(" -multicast bytes rx : %"PRIu64"\n", bss->extended_metrics.rx_mcast_bytes); + print_cb(" -broadcast bytes tx : %"PRIu64"\n", bss->extended_metrics.tx_bcast_bytes); + print_cb(" -broadcast bytes rx : %"PRIu64"\n", bss->extended_metrics.rx_bcast_bytes); } static void print_bss_in_radio(map_radio_info_t *radio, map_printf_cb_t print_cb) @@ -185,10 +184,23 @@ static void print_bss_in_radio(map_radio_info_t *radio, map_printf_cb_t print_cb static void print_op_class_channel_list(map_op_class_t *op_class, map_printf_cb_t print_cb) { - map_channel_set_t *s = &op_class->channels; - char buf[MAP_CS_BUF_LEN]; + map_channel_set_t ch_set; + bool is_center_channel; + char buf[MAP_CS_BUF_LEN]; + + if (map_get_is_center_channel_from_op_class(op_class->op_class, &is_center_channel)) { + return; + } - print_cb(" -Channels : %s\n", map_cs_nr(s) > 0 ? map_cs_to_string(s, ',', buf, sizeof(buf)) : "all"); + if ((is_center_channel && map_get_center_channel_set_from_op_class(op_class->op_class, &ch_set)) || + (!is_center_channel && map_get_channel_set_from_op_class(op_class->op_class, &ch_set))) { + return; + } + + /* Unset non operable channels */ + map_cs_and_not(&ch_set, &op_class->channels); + + print_cb(" -Channels : %s\n", map_cs_to_string(&ch_set, ',', buf, sizeof(buf))); } static void print_curr_op_class_list(map_radio_info_t *radio, map_printf_cb_t print_cb) @@ -263,8 +275,8 @@ static void print_op_restriction_in_radio(map_radio_info_t *radio, map_printf_cb print_cb(" -OP Class : %d\n", op_class->op_class); buf[0] = 0; - for (j = 0; j < op_class->channel_count && pos < MAP_CS_BUF_LEN; j++) { - pos += snprintf(buf + pos, MAP_CS_BUF_LEN - pos, "%d, ", op_class->channel_list[j].channel); + for (j = 0; j < op_class->channels_nr && pos < MAP_CS_BUF_LEN; j++) { + pos += snprintf(buf + pos, MAP_CS_BUF_LEN - pos, "%d, ", op_class->channels[j].channel); } print_cb(" -Channels : %s\n", buf); } @@ -351,12 +363,13 @@ static void print_radios_in_agent(map_ale_info_t *ale, map_printf_cb_t print_cb) print_cb(" Radio Type : %s\n", map_get_freq_band_str(radio->supported_freq)); - print_cb(" Radio State : %sCONFIGURED\n", is_radio_configured(radio->state) ? "" : "UN"); + print_cb(" Radio State : %sCONFIGURED[0x%04x]\n", is_radio_configured(radio->state) ? "" : "UN", radio->state); print_cb(" Radio OP Class : %d\n",radio->current_op_class); print_cb(" Radio OP Chan : %d\n",radio->current_op_channel); print_cb(" Radio BW : %d\n",radio->current_bw); print_cb(" Radio Tx Pwr : %d\n",radio->current_tx_pwr); + print_cb(" Radio Auth Modes : 0x%04x\n",radio->auth_modes_flag); print_curr_op_class_list(radio, print_cb); print_cap_op_class_list(radio, print_cb); @@ -462,6 +475,107 @@ static void print_emex_eth_interfaces(map_ale_info_t *ale, map_printf_cb_t print print_cb("----------------------------------------------\n"); } +static char *mld_modes_to_buf(char *buf, size_t buf_len, map_mld_modes_t *m) +{ + int pos = snprintf(buf, buf_len, "%s%s%s%s", + m->str ? "STR " : "", + m->nstr ? "NSTR " : "", + m->emlsr ? "EMLSR " : "", + m->emlmr ? "EMLMR " : ""); + + if (pos > 0) { + buf[pos] = 0; + } + + return buf; +} + +static void print_mld(map_ale_info_t *ale, map_printf_cb_t print_cb, const char *indent, bool print_sep) +{ + map_ap_mld_info_t *ap_mld; + map_bsta_mld_info_t *bsta_mld = &ale->bsta_mld; + map_sta_mld_info_t *sta_mld; + map_sta_info_t *sta; + map_radio_info_t *radio; + map_radio_wifi7_caps_t *wifi7_caps; + map_bss_info_t *bss; + size_t count1 = 0; + map_mld_modes_t supp_ap_mld_modes = {0}; + map_mld_modes_t supp_bsta_mld_modes = {0}; + char mld_modes_buf[64]; + + /* Or all ap and bsta radio mld modes (normally they are all the same) */ + map_dm_foreach_radio(ale, radio) { + if ((wifi7_caps = radio->wifi7_caps)) { + supp_ap_mld_modes.str |= wifi7_caps->ap_mld_modes.str; + supp_ap_mld_modes.nstr |= wifi7_caps->ap_mld_modes.nstr; + supp_ap_mld_modes.emlsr |= wifi7_caps->ap_mld_modes.emlsr; + supp_ap_mld_modes.emlmr |= wifi7_caps->ap_mld_modes.emlmr; + + supp_bsta_mld_modes.str |= wifi7_caps->bsta_mld_modes.str; + supp_bsta_mld_modes.nstr |= wifi7_caps->bsta_mld_modes.nstr; + supp_bsta_mld_modes.emlsr |= wifi7_caps->bsta_mld_modes.emlsr; + supp_bsta_mld_modes.emlmr |= wifi7_caps->bsta_mld_modes.emlmr; + } + } + + if (ale->ap_mld_nr > 0) { + print_cb("%sAP MLDs:\n", indent); + + map_dm_foreach_ap_mld(ale, ap_mld) { + size_t count2 = 0; + + print_cb("%s MLD[%zu][%s]\n", indent, count1++, ap_mld->mac_str); + print_cb("%s SSID : %s\n", indent, ap_mld->ssid); + print_cb("%s Supp modes: %s\n", indent, mld_modes_to_buf(mld_modes_buf, sizeof(mld_modes_buf), &supp_ap_mld_modes)); + print_cb("%s Enab modes: %s\n", indent, mld_modes_to_buf(mld_modes_buf, sizeof(mld_modes_buf), &ap_mld->enabled_mld_modes)); + print_cb("%s Aff APs :\n", indent); + map_dm_foreach_aff_ap(ap_mld, bss) { + print_cb("%s AP[%zu][%s]\n", indent, count2++, bss->bssid_str); + print_cb("%s LinkID: %d\n", indent, bss->link_id); + print_cb("%s Band : %s\n", indent, map_get_freq_band_str(bss->radio->supported_freq)); + } + + if (ap_mld->sta_mld_nr > 0) { + size_t count3 = 0; + + map_dm_foreach_sta_mld(ap_mld, sta_mld) { + size_t count4 = 0; + + print_cb("%s STA MLDs:\n", indent); + print_cb("%s STA[%zu][%s]\n", indent, count3++, sta_mld->mac_str); + print_cb("%s Supp modes: %s\n", indent, mld_modes_to_buf(mld_modes_buf, sizeof(mld_modes_buf), &sta_mld->supported_mld_modes)); + print_cb("%s Enab modes: %s\n", indent, mld_modes_to_buf(mld_modes_buf, sizeof(mld_modes_buf), &sta_mld->enabled_mld_modes)); + print_cb("%s Aff STAs :\n", indent); + map_dm_foreach_aff_sta(sta_mld, sta) { + print_cb("%s STA[%zu][%s]\n", indent, count4++, sta->mac_str); + print_cb("%s Band : %s\n", indent, map_get_freq_band_str(sta->bss->radio->supported_freq)); + } + } + } + + print_cb("\n"); + } + } + + if (bsta_mld->valid) { + size_t i; + + print_cb("%sbSTA MLD[%s]:\n", indent, bsta_mld->mac_str); + print_cb("%s AP MLD MAC: %s\n", indent, mac_string(bsta_mld->ap_mld_mac)); + print_cb("%s Supp modes: %s\n", indent, mld_modes_to_buf(mld_modes_buf, sizeof(mld_modes_buf), &supp_bsta_mld_modes)); + print_cb("%s Enab modes: %s\n", indent, mld_modes_to_buf(mld_modes_buf, sizeof(mld_modes_buf), &bsta_mld->enabled_mld_modes)); + print_cb("%s Aff bSTAs :\n", indent); + for (i = 0; i < bsta_mld->aff_bsta_mac_nr; i++) { + print_cb("%s bSTA[%zu][%s]\n", indent, i, mac_string(bsta_mld->aff_bsta_macs[i])); + } + } + + if (print_sep) { + print_cb("----------------------------------------------\n"); + } +} + static void print_agent_info(map_ale_info_t *ale, map_printf_cb_t print_cb) { print_cb("***********************************************\n"); @@ -495,6 +609,8 @@ static void print_agent_info(map_ale_info_t *ale, map_printf_cb_t print_cb) print_eth_devices(ale, print_cb); print_emex_eth_interfaces(ale, print_cb); + + print_mld(ale, print_cb, "", true); } static const char *convert_tunneled_type_to_string(uint8_t type) @@ -592,3 +708,14 @@ void map_dm_dump_tunneled_messages(map_printf_cb_t print_cb, uint8_t *sta_mac, u break; } } + +void map_dm_dump_mld(map_printf_cb_t print_cb) +{ + map_ale_info_t *ale; + + map_dm_foreach_agent_ale(ale) { + print_cb("ALE[%s]\n", ale->al_mac_str); + print_mld(ale, print_cb, " ", false); + print_cb("\n"); + } +} diff --git a/source/libplatform/src/map_dm_rbus.c b/source/libplatform/src/map_dm_rbus.c index 8365c4c..21c4d81 100644 --- a/source/libplatform/src/map_dm_rbus.c +++ b/source/libplatform/src/map_dm_rbus.c @@ -87,9 +87,13 @@ #define DM_DEVICE_EXECENV DM_DEVICE_TBL "ExecutionEnv" #define DM_DEVICE_CCODE DM_DEVICE_TBL "CountryCode" #define DM_DEVICE_MAPPROFILE DM_DEVICE_TBL "MultiAPProfile" +#define DM_DEVICE_BTMSTDASTALST DM_DEVICE_TBL "BTMSteeringDisallowedSTAList" +#define DM_DEVICE_LCLSTDASTALST DM_DEVICE_TBL "LocalSteeringDisallowedSTAList" #define DM_DEVICE_RADIONOE DM_DEVICE_TBL "RadioNumberOfEntries" #define DM_DEVICE_CACSTATUSNOE DM_DEVICE_TBL "CACStatusNumberOfEntries" -#define DM_DEVICE_UNASSOC_STA_QUERY DM_DEVICE_TBL "X_AIRTIES_UnassociatedStaLinkMetricsQuery()" +#define DM_DEVICE_APMLDNOE DM_DEVICE_TBL "APMLDNumberOfEntries" +#define DM_DEVICE_UNASSOCSTAQRY DM_DEVICE_TBL "X_AIRTIES_UnassociatedStaLinkMetricsQuery()" +#define DM_DEVICE_REBOOT DM_DEVICE_TBL "X_AIRTIES_Reboot()" /* Device.WiFi.DataElements.Network.Device.MultiAPDevice */ #define DM_MULTIAPDEV_OBJ DM_DEVICE_TBL "MultiAPDevice." /* Device.WiFi.DataElements.Network.Device.MultiAPDevice.Backhaul */ @@ -98,6 +102,7 @@ #define DM_BACKHAUL_BHMACADDR DM_BACKHAUL_OBJ "BackhaulMACAddress" #define DM_BACKHAUL_BHDEVICEID DM_BACKHAUL_OBJ "BackhaulDeviceID" #define DM_BACKHAUL_MACADDRESS DM_BACKHAUL_OBJ "MACAddress" +#define DM_BACKHAUL_STEERWIFIBH DM_BACKHAUL_OBJ "SteerWiFiBackhaul()" /* Device.WiFi.DataElements.Network.Device.MultiAPDevice.Backhaul.Stats */ #define DM_BHSTATS_OBJ DM_BACKHAUL_OBJ "Stats." #define DM_BHSTATS_BYTESRCVD DM_BHSTATS_OBJ "BytesReceived" @@ -144,8 +149,9 @@ #define DM_RADIO_BSSNOE DM_RADIO_TBL "BSSNumberOfEntries" #define DM_RADIO_CURROPCLASSNOE DM_RADIO_TBL "CurrentOperatingClassProfileNumberOfEntries" #define DM_RADIO_SCANRESULTNOE DM_RADIO_TBL "ScanResultNumberOfEntries" -#define DM_RADIO_CHSCANREQUEST DM_RADIO_TBL "ChannelScanRequest()" #define DM_RADIO_UNASSOC_NOE DM_RADIO_TBL "UnassociatedSTANumberOfEntries" +#define DM_RADIO_DISOPCLASSNOE DM_RADIO_TBL "DisAllowedOpClassChannelsNumberOfEntries" +#define DM_RADIO_CHSCANREQUEST DM_RADIO_TBL "ChannelScanRequest()" /* Device.WiFi.DataElements.Network.Device.Radio.BackhaulSta */ #define DM_BACKHAULSTA_OBJ DM_RADIO_TBL "BackhaulSta." #define DM_BACKHAULSTA_MACADDR DM_BACKHAULSTA_OBJ "MACAddress" @@ -155,6 +161,104 @@ #define DM_CAPS_VHTCAPS DM_CAPS_OBJ "VHTCapabilities" #define DM_CAPS_HECAPS DM_CAPS_OBJ "HECapabilities" #define DM_CAPS_CAPOPCLASSNOE DM_CAPS_OBJ "CapableOperatingClassProfileNumberOfEntries" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi6APRole */ +#define DM_WIFI6AP_OBJ DM_CAPS_OBJ "WiFi6APRole." +#define DM_WIFI6AP_HE160 DM_WIFI6AP_OBJ "HE160" +#define DM_WIFI6AP_HE8080 DM_WIFI6AP_OBJ "HE8080" +#define DM_WIFI6AP_MCSNSS DM_WIFI6AP_OBJ "MCSNSS" +#define DM_WIFI6AP_SUBMFRMER DM_WIFI6AP_OBJ "SUBeamformer" +#define DM_WIFI6AP_SUBMFRMEE DM_WIFI6AP_OBJ "SUBeamformee" +#define DM_WIFI6AP_MUBMFRMER DM_WIFI6AP_OBJ "MUBeamformer" +#define DM_WIFI6AP_BMFRMEEL80 DM_WIFI6AP_OBJ "Beamformee80orLess" +#define DM_WIFI6AP_BMFRMEEG80 DM_WIFI6AP_OBJ "BeamformeeAbove80" +#define DM_WIFI6AP_ULMUMIMO DM_WIFI6AP_OBJ "ULMUMIMO" +#define DM_WIFI6AP_ULOFDMA DM_WIFI6AP_OBJ "ULOFDMA" +#define DM_WIFI6AP_DLOFDMA DM_WIFI6AP_OBJ "DLOFDMA" +#define DM_WIFI6AP_MXDLMUMIMO DM_WIFI6AP_OBJ "MaxDLMUMIMO" +#define DM_WIFI6AP_MXULMUMIMO DM_WIFI6AP_OBJ "MaxULMUMIMO" +#define DM_WIFI6AP_MXDLOFDMA DM_WIFI6AP_OBJ "MaxDLOFDMA" +#define DM_WIFI6AP_MXULOFDMA DM_WIFI6AP_OBJ "MaxULOFDMA" +#define DM_WIFI6AP_RTS DM_WIFI6AP_OBJ "RTS" +#define DM_WIFI6AP_MURTS DM_WIFI6AP_OBJ "MURTS" +#define DM_WIFI6AP_MBSSID DM_WIFI6AP_OBJ "MultiBSSID" +#define DM_WIFI6AP_MUEDCA DM_WIFI6AP_OBJ "MUEDCA" +#define DM_WIFI6AP_TWTREQ DM_WIFI6AP_OBJ "TWTRequestor" +#define DM_WIFI6AP_TWTRES DM_WIFI6AP_OBJ "TWTResponder" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi6bSTARole */ +#define DM_WIFI6BSTA_OBJ DM_CAPS_OBJ "WiFi6bSTARole." +#define DM_WIFI6BSTA_HE160 DM_WIFI6BSTA_OBJ "HE160" +#define DM_WIFI6BSTA_HE8080 DM_WIFI6BSTA_OBJ "HE8080" +#define DM_WIFI6BSTA_MCSNSS DM_WIFI6BSTA_OBJ "MCSNSS" +#define DM_WIFI6BSTA_SUBMFRMER DM_WIFI6BSTA_OBJ "SUBeamformer" +#define DM_WIFI6BSTA_SUBMFRMEE DM_WIFI6BSTA_OBJ "SUBeamformee" +#define DM_WIFI6BSTA_MUBMFRMER DM_WIFI6BSTA_OBJ "MUBeamformer" +#define DM_WIFI6BSTA_BMFRMEEL80 DM_WIFI6BSTA_OBJ "Beamformee80orLess" +#define DM_WIFI6BSTA_BMFRMEEG80 DM_WIFI6BSTA_OBJ "BeamformeeAbove80" +#define DM_WIFI6BSTA_ULMUMIMO DM_WIFI6BSTA_OBJ "ULMUMIMO" +#define DM_WIFI6BSTA_ULOFDMA DM_WIFI6BSTA_OBJ "ULOFDMA" +#define DM_WIFI6BSTA_DLOFDMA DM_WIFI6BSTA_OBJ "DLOFDMA" +#define DM_WIFI6BSTA_MXDLMUMIMO DM_WIFI6BSTA_OBJ "MaxDLMUMIMO" +#define DM_WIFI6BSTA_MXULMUMIMO DM_WIFI6BSTA_OBJ "MaxULMUMIMO" +#define DM_WIFI6BSTA_MXDLOFDMA DM_WIFI6BSTA_OBJ "MaxDLOFDMA" +#define DM_WIFI6BSTA_MXULOFDMA DM_WIFI6BSTA_OBJ "MaxULOFDMA" +#define DM_WIFI6BSTA_RTS DM_WIFI6BSTA_OBJ "RTS" +#define DM_WIFI6BSTA_MURTS DM_WIFI6BSTA_OBJ "MURTS" +#define DM_WIFI6BSTA_MBSSID DM_WIFI6BSTA_OBJ "MultiBSSID" +#define DM_WIFI6BSTA_MUEDCA DM_WIFI6BSTA_OBJ "MUEDCA" +#define DM_WIFI6BSTA_TWTREQ DM_WIFI6BSTA_OBJ "TWTRequestor" +#define DM_WIFI6BSTA_TWTRES DM_WIFI6BSTA_OBJ "TWTResponder" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7APRole */ +#define DM_WIFI7AP_OBJ DM_CAPS_OBJ "WiFi7APRole." +#define DM_WIFI7AP_EMLMRSUP DM_WIFI7AP_OBJ "EMLMRSupport" +#define DM_WIFI7AP_EMLSRSUP DM_WIFI7AP_OBJ "EMLSRSupport" +#define DM_WIFI7AP_STRSUP DM_WIFI7AP_OBJ "STRSupport" +#define DM_WIFI7AP_NSTRSUP DM_WIFI7AP_OBJ "NSTRSupport" +#define DM_WIFI7AP_EMLMRFSNOE DM_WIFI7AP_OBJ "EMLMRFreqSeparationNumberOfEntries" +#define DM_WIFI7AP_EMLSRFSNOE DM_WIFI7AP_OBJ "EMLSRFreqSeparationNumberOfEntries" +#define DM_WIFI7AP_STRFSNOE DM_WIFI7AP_OBJ "STRFreqSeparationNumberOfEntries" +#define DM_WIFI7AP_NSTRFSNOE DM_WIFI7AP_OBJ "NSTRFreqSeparationNumberOfEntries" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7APRole.EMLMRFreqSeparation */ +#define DM_APEMLMRFS_TBL DM_WIFI7AP_OBJ "EMLMRFreqSeparation.{i}." +#define DM_APEMLMRFS_RUID DM_APEMLMRFS_TBL "RUID" +#define DM_APEMLMRFS_FREQSEP DM_APEMLMRFS_TBL "FreqSeparation" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7APRole.EMLSRFreqSeparation */ +#define DM_APEMLSRFS_TBL DM_WIFI7AP_OBJ "EMLSRFreqSeparation.{i}." +#define DM_APEMLSRFS_RUID DM_APEMLSRFS_TBL "RUID" +#define DM_APEMLSRFS_FREQSEP DM_APEMLSRFS_TBL "FreqSeparation" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7APRole.STRFreqSeparation */ +#define DM_APSTRFS_TBL DM_WIFI7AP_OBJ "STRFreqSeparation.{i}." +#define DM_APSTRFS_RUID DM_APSTRFS_TBL "RUID" +#define DM_APSTRFS_FREQSEP DM_APSTRFS_TBL "FreqSeparation" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7APRole.NSTRFreqSeparation */ +#define DM_APNSTRFS_TBL DM_WIFI7AP_OBJ "NSTRFreqSeparation.{i}." +#define DM_APNSTRFS_RUID DM_APNSTRFS_TBL "RUID" +#define DM_APNSTRFS_FREQSEP DM_APNSTRFS_TBL "FreqSeparation" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7bSTARole */ +#define DM_WIFI7BSTA_OBJ DM_CAPS_OBJ "WiFi7bSTARole." +#define DM_WIFI7BSTA_EMLMRSUP DM_WIFI7BSTA_OBJ "EMLMRSupport" +#define DM_WIFI7BSTA_EMLSRSUP DM_WIFI7BSTA_OBJ "EMLSRSupport" +#define DM_WIFI7BSTA_STRSUP DM_WIFI7BSTA_OBJ "STRSupport" +#define DM_WIFI7BSTA_NSTRSUP DM_WIFI7BSTA_OBJ "NSTRSupport" +#define DM_WIFI7BSTA_EMLMRFSNOE DM_WIFI7BSTA_OBJ "EMLMRFreqSeparationNumberOfEntries" +#define DM_WIFI7BSTA_EMLSRFSNOE DM_WIFI7BSTA_OBJ "EMLSRFreqSeparationNumberOfEntries" +#define DM_WIFI7BSTA_STRFSNOE DM_WIFI7BSTA_OBJ "STRFreqSeparationNumberOfEntries" +#define DM_WIFI7BSTA_NSTRFSNOE DM_WIFI7BSTA_OBJ "NSTRFreqSeparationNumberOfEntries" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7bSTARole.EMLMRFreqSeparation */ +#define DM_BSTAEMLMRFS_TBL DM_WIFI7BSTA_OBJ "EMLMRFreqSeparation.{i}." +#define DM_BSTAEMLMRFS_RUID DM_BSTAEMLMRFS_TBL "RUID" +#define DM_BSTAEMLMRFS_FREQSEP DM_BSTAEMLMRFS_TBL "FreqSeparation" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7bSTARole.EMLSRFreqSeparation */ +#define DM_BSTAEMLSRFS_TBL DM_WIFI7BSTA_OBJ "EMLSRFreqSeparation.{i}." +#define DM_BSTAEMLSRFS_RUID DM_BSTAEMLSRFS_TBL "RUID" +#define DM_BSTAEMLSRFS_FREQSEP DM_BSTAEMLSRFS_TBL "FreqSeparation" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7bSTARole.STRFreqSeparation */ +#define DM_BSTASTRFS_TBL DM_WIFI7BSTA_OBJ "STRFreqSeparation.{i}." +#define DM_BSTASTRFS_RUID DM_BSTASTRFS_TBL "RUID" +#define DM_BSTASTRFS_FREQSEP DM_BSTASTRFS_TBL "FreqSeparation" +/* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7bSTARole.NSTRFreqSeparation */ +#define DM_BSTANSTRFS_TBL DM_WIFI7BSTA_OBJ "NSTRFreqSeparation.{i}." +#define DM_BSTANSTRFS_RUID DM_BSTANSTRFS_TBL "RUID" +#define DM_BSTANSTRFS_FREQSEP DM_BSTANSTRFS_TBL "FreqSeparation" /* Device.WiFi.DataElements.Network.Device.Radio.Capabilities.CapableOperatingClassProfile */ #define DM_CAPOPCLASS_TBL DM_CAPS_OBJ "CapableOperatingClassProfile.{i}." #define DM_CAPOPCLASS_CLASS DM_CAPOPCLASS_TBL "Class" @@ -171,6 +275,11 @@ #define DM_CURROPCLASS_CHANNEL DM_CURROPCLASS_TBL "Channel" #define DM_CURROPCLASS_TXPOWER DM_CURROPCLASS_TBL "TxPower" #define DM_CURROPCLASS_TSTAMP DM_CURROPCLASS_TBL "TimeStamp" +/* Device.WiFi.DataElements.Network.Device.Radio.DisAllowedOpClassChannels */ +#define DM_DISOPCLASS_TBL DM_RADIO_TBL "DisAllowedOpClassChannels.{i}." +#define DM_DISOPCLASS_ENABLE DM_DISOPCLASS_TBL "Enable" +#define DM_DISOPCLASS_OPCLASS DM_DISOPCLASS_TBL "OpClass" +#define DM_DISOPCLASS_CHLIST DM_DISOPCLASS_TBL "ChannelList" /* Device.WiFi.DataElements.Network.Device.Radio.ScanResult */ #define DM_SCANRES_TBL DM_RADIO_TBL "ScanResult.{i}." #define DM_SCANRES_TSTAMP DM_SCANRES_TBL "TimeStamp" @@ -266,8 +375,81 @@ /* Device.WiFi.DataElements.Network.Device.Radio.UnassociatedSTA */ #define DM_UNASSOC_TBL DM_RADIO_TBL "UnassociatedSTA.{i}." #define DM_UNASSOC_MAC DM_UNASSOC_TBL "MACAddress" -#define DM_UNASSOC_SIGNALSTRENGTH DM_UNASSOC_TBL "SignalStrength" +#define DM_UNASSOC_SIGNALSTR DM_UNASSOC_TBL "SignalStrength" #define DM_UNASSOC_TIMESTAMP DM_UNASSOC_TBL "TimeStamp" +/* Device.WiFi.DataElements.Network.Device.APMLD */ +#define DM_APMLD_TBL DM_DEVICE_TBL "APMLD.{i}." +#define DM_APMLD_MACADDRESS DM_APMLD_TBL "MLDMACAddress" +#define DM_APMLD_AFFAPNOE DM_APMLD_TBL "AffiliatedAPNumberOfEntries" +#define DM_APMLD_STAMLDNOE DM_APMLD_TBL "STAMLDNumberOfEntries" +/* Device.WiFi.DataElements.Network.Device.APMLD.APMLDConfig */ +#define DM_AMCONFIG_OBJ DM_APMLD_TBL "APMLDConfig." +#define DM_AMCONFIG_EMLMR DM_AMCONFIG_OBJ "EMLMREnabled" +#define DM_AMCONFIG_EMLSR DM_AMCONFIG_OBJ "EMLSREnabled" +#define DM_AMCONFIG_STR DM_AMCONFIG_OBJ "STREnabled" +#define DM_AMCONFIG_NSTR DM_AMCONFIG_OBJ "NSTREnabled" +/* Device.WiFi.DataElements.Network.Device.APMLD.AffiliatedAP */ +#define DM_AFFAP_TBL DM_APMLD_TBL "AffiliatedAP.{i}." +#define DM_AFFAP_BSSID DM_AFFAP_TBL "BSSID" +#define DM_AFFAP_LINKID DM_AFFAP_TBL "LinkID" +#define DM_AFFAP_RUID DM_AFFAP_TBL "RUID" +#define DM_AFFAP_PACKETSRCVD DM_AFFAP_TBL "PacketsReceived" +#define DM_AFFAP_PACKETSSENT DM_AFFAP_TBL "PacketsSent" +#define DM_AFFAP_ERRORSSENT DM_AFFAP_TBL "ErrorsSent" +#define DM_AFFAP_UCBYTESRCVD DM_AFFAP_TBL "UnicastBytesReceived" +#define DM_AFFAP_UCBYTESSENT DM_AFFAP_TBL "UnicastBytesSent" +#define DM_AFFAP_MCBYTESRCVD DM_AFFAP_TBL "MulticastBytesReceived" +#define DM_AFFAP_MCBYTESSENT DM_AFFAP_TBL "MulticastBytesSent" +#define DM_AFFAP_BCBYTESRCVD DM_AFFAP_TBL "BroadcastBytesReceived" +#define DM_AFFAP_BCBYTESSENT DM_AFFAP_TBL "BroadcastBytesSent" +/* Device.WiFi.DataElements.Network.Device.APMLD.STAMLD */ +#define DM_STAMLD_TBL DM_APMLD_TBL "STAMLD.{i}." +#define DM_STAMLD_MLDMACADDR DM_STAMLD_TBL "MLDMACAddress" +#define DM_STAMLD_ISBSTA DM_STAMLD_TBL "IsbSTA" +#define DM_STAMLD_LASTCONTIME DM_STAMLD_TBL "LastConnectTime" +#define DM_STAMLD_BYTESRCVD DM_STAMLD_TBL "BytesReceived" +#define DM_STAMLD_BYTESSENT DM_STAMLD_TBL "BytesSent" +#define DM_STAMLD_PACKETSRCVD DM_STAMLD_TBL "PacketsReceived" +#define DM_STAMLD_PACKETSSENT DM_STAMLD_TBL "PacketsSent" +#define DM_STAMLD_ERRORSRCVD DM_STAMLD_TBL "ErrorsReceived" +#define DM_STAMLD_ERRORSSENT DM_STAMLD_TBL "ErrorsSent" +#define DM_STAMLD_RETRANSCNT DM_STAMLD_TBL "RetransCount" +#define DM_STAMLD_AFFSTANOE DM_STAMLD_TBL "AffiliatedSTANumberOfEntries" +/* Device.WiFi.DataElements.Network.Device.APMLD.STAMLD.WiFi7Capabilities */ +#define DM_SMWF7CAP_OBJ DM_STAMLD_TBL "WiFi7Capabilities." +#define DM_SMWF7CAP_EMLMR DM_SMWF7CAP_OBJ "EMLMRSupport" +#define DM_SMWF7CAP_EMLSR DM_SMWF7CAP_OBJ "EMLSRSupport" +#define DM_SMWF7CAP_STR DM_SMWF7CAP_OBJ "STRSupport" +#define DM_SMWF7CAP_NSTR DM_SMWF7CAP_OBJ "NSTRSupport" +/* Device.WiFi.DataElements.Network.Device.APMLD.STAMLD.STAMLDConfig */ +#define DM_SMCONFIG_OBJ DM_STAMLD_TBL "STAMLDConfig." +#define DM_SMCONFIG_EMLMR DM_SMCONFIG_OBJ "EMLMREnabled" +#define DM_SMCONFIG_EMLSR DM_SMCONFIG_OBJ "EMLSREnabled" +#define DM_SMCONFIG_STR DM_SMCONFIG_OBJ "STREnabled" +#define DM_SMCONFIG_NSTR DM_SMCONFIG_OBJ "NSTREnabled" +/* Device.WiFi.DataElements.Network.Device.APMLD.STAMLD.AffiliatedSTA */ +#define DM_AFFSTA_TBL DM_STAMLD_TBL "AffiliatedSTA.{i}." +#define DM_AFFSTA_MACADDRESS DM_AFFSTA_TBL "MACAddress" +#define DM_AFFSTA_BSSID DM_AFFSTA_TBL "BSSID" +#define DM_AFFSTA_BYTESRCVD DM_AFFSTA_TBL "BytesReceived" +#define DM_AFFSTA_BYTESSENT DM_AFFSTA_TBL "BytesSent" +#define DM_AFFSTA_PACKETSRCVD DM_AFFSTA_TBL "PacketsReceived" +#define DM_AFFSTA_PACKETSSENT DM_AFFSTA_TBL "PacketsSent" +#define DM_AFFSTA_ERRORSSENT DM_AFFSTA_TBL "ErrorsSent" +#define DM_AFFSTA_SIGNALSTR DM_AFFSTA_TBL "SignalStrength" +#define DM_AFFSTA_ESTMACDRDL DM_AFFSTA_TBL "EstMACDataRateDownlink" +#define DM_AFFSTA_ESTMACDRUL DM_AFFSTA_TBL "EstMACDataRateUplink" +/* Device.WiFi.DataElements.Network.Device.bSTAMLD */ +#define DM_BSTAMLD_OBJ DM_DEVICE_TBL "bSTAMLD." +#define DM_BSTAMLD_MACADDRESS DM_BSTAMLD_OBJ "MLDMACAddress" +#define DM_BSTAMLD_BSSID DM_BSTAMLD_OBJ "BSSID" +#define DM_BSTAMLD_AFFBSTALIST DM_BSTAMLD_OBJ "AffiliatedbSTAList" +/* Device.WiFi.DataElements.Network.Device.bSTAMLD.bSTAMLDConfig */ +#define DM_BSTACFG_OBJ DM_BSTAMLD_OBJ "bSTAMLDConfig." +#define DM_BSTACFG_EMLMR DM_BSTACFG_OBJ "EMLMREnabled" +#define DM_BSTACFG_EMLSR DM_BSTACFG_OBJ "EMLSREnabled" +#define DM_BSTACFG_STR DM_BSTACFG_OBJ "STREnabled" +#define DM_BSTACFG_NSTR DM_BSTACFG_OBJ "NSTREnabled" /* Device.WiFi.DataElements.Network.Device.X_AIRTIES_Ethernet */ #define DM_ETHERNET_OBJ DM_DEVICE_TBL "X_AIRTIES_Ethernet." #define DM_ETHERNET_IFACENOE DM_ETHERNET_OBJ "InterfaceNumberOfEntries" @@ -281,6 +463,7 @@ /* Device.WiFi.DataElements.Network.Device.X_AIRTIES_DeviceInfo */ #define DM_DEVINFO_OBJ DM_DEVICE_TBL "X_AIRTIES_DeviceInfo." #define DM_DEVINFO_UPTIME DM_DEVINFO_OBJ "Uptime" +#define DM_DEVINFO_BOOTID DM_DEVINFO_OBJ "BootID" /* Device.WiFi.DataElements.Network.Device.X_AIRTIES_DeviceInfo.MemoryStatus */ #define DM_MEMSTATUS_OBJ DM_DEVINFO_OBJ "MemoryStatus." #define DM_MEMSTATUS_TOTAL DM_MEMSTATUS_OBJ "Total" @@ -331,7 +514,6 @@ #define DM_DEVICE_SETSTASTSTATE DM_DEVICE_TBL "SetSTASteeringState()" #define DM_DEVICE_SETDFSSTATE DM_DEVICE_TBL "SetDFSState()" #define DM_DEVICE_SETANTCHPREF DM_DEVICE_TBL "SetAnticipatedChannelPreference()" -#define DM_BACKHAUL_STEERWIFIBH DM_BACKHAUL_OBJ "SteerWiFiBackhaul()" #define DM_RADIO_RADIOENABLE DM_RADIO_TBL "RadioEnable()" #define DM_RADIO_SETTXPOWERLIM DM_RADIO_TBL "SetTxPowerLimit()" #define DM_RADIO_SETSPATREUSE DM_RADIO_TBL "SetSpatialReuse()" @@ -399,6 +581,10 @@ typedef struct dm_sta_payload_s { rbusMethodAsyncHandle_t rbus_steering_async_hnd; } dm_sta_payload_t; +typedef struct dm_aff_sta_table_s { + const char *aff_sta_id; +} dm_aff_sta_table_t; + typedef struct dm_sta_table_s { const char *sta_id; rbusMethodAsyncHandle_t bmquery_reply; @@ -409,10 +595,35 @@ typedef struct dm_bss_table_s { dm_sta_table_t dm_sta[MAX_STATION_PER_BSS]; } dm_bss_table_t; +typedef struct dm_sta_mld_table_s { + const char *sta_mld_id; + dm_aff_sta_table_t dm_aff_sta[MAX_STATION_PER_BSS]; +} dm_sta_mld_table_t; + +typedef struct dm_aff_ap_table_s { + const char *aff_ap_id; +} dm_aff_ap_table_t; + +typedef struct dm_ap_mld_table_s { + const char *ap_mld_id; + dm_sta_mld_table_t dm_sta_mld[MAX_STATION_PER_BSS]; + dm_aff_ap_table_t dm_aff_ap[MAX_BSS_PER_RADIO]; +} dm_ap_mld_table_t; + typedef struct dm_radio_table_s { const char *radio_id; unsigned int capops; unsigned int currops; + unsigned int disops; + + unsigned int apemlmr; + unsigned int apemlsr; + unsigned int apstr; + unsigned int apnstr; + unsigned int bstaemlmr; + unsigned int bstaemlsr; + unsigned int bstastr; + unsigned int bstanstr; dm_bss_table_t dm_bss[MAX_BSS_PER_RADIO]; @@ -436,9 +647,13 @@ typedef struct dm_dev_table_s { unsigned int cac_active; unsigned int eth_ifaces; + dm_ap_mld_table_t dm_ap_mld[MAX_RADIO_PER_AGENT]; dm_radio_table_t dm_radio[MAX_RADIO_PER_AGENT]; - dm_ethif_table_t dm_ethif[MAX_IFACE_PER_AGENT]; + + rbusMethodAsyncHandle_t steerwifibh_reply; + mac_addr steerwifibh_target; + rbusMethodAsyncHandle_t unassoc_sta_link_metrics_query_handle; } dm_dev_table_t; @@ -489,7 +704,7 @@ static void remove_all_ap_device(void) memset(&g_dm_dev_table, 0, sizeof(g_dm_dev_table)); } -static int get_dev_dm_idx(mac_addr_str al_mac_str, unsigned int *didx) +static int get_dev_dm_idx(map_ale_info_t *ale, unsigned int *didx) { unsigned int idx = 0; @@ -499,9 +714,9 @@ static int get_dev_dm_idx(mac_addr_str al_mac_str, unsigned int *didx) } } - g_dm_dev_table[idx].al_mac = strdup(al_mac_str); + g_dm_dev_table[idx].al_mac = strdup(ale->al_mac_str); - *didx = idx; + ale->dm_idx = *didx = idx; return 0; } @@ -554,6 +769,16 @@ static void get_dev_dm_eth_devs(map_ale_info_t *ale, unsigned int iidx, unsigned *eth_devs = dm_ethif->eth_devs; } +static void get_dev_dm_steerwifibh_reply(map_ale_info_t *ale, rbusMethodAsyncHandle_t *reply) +{ + *reply = g_dm_dev_table[ale->dm_idx].steerwifibh_reply; +} + +static void get_dev_dm_steerwifibh_target(map_ale_info_t *ale, mac_addr target_bssid) +{ + maccpy(target_bssid, g_dm_dev_table[ale->dm_idx].steerwifibh_target); +} + static void set_dev_dm_cac_valid(map_ale_info_t *ale, bool cac_valid) { dm_dev_table_t *dm_dev; @@ -606,6 +831,16 @@ static void set_dev_dm_eth_devs(map_ale_info_t *ale, unsigned int iidx, unsigned dm_ethif->eth_devs = eth_devs; } +static void set_dev_dm_steerwifibh_reply(map_ale_info_t *ale, rbusMethodAsyncHandle_t reply) +{ + g_dm_dev_table[ale->dm_idx].steerwifibh_reply = reply; +} + +static void set_dev_dm_steerwifibh_target(map_ale_info_t *ale, mac_addr target_bssid) +{ + maccpy(g_dm_dev_table[ale->dm_idx].steerwifibh_target, target_bssid); +} + static int check_dev_dm_idx(map_ale_info_t *ale) { if (ale->dm_idx < 0) { @@ -661,6 +896,78 @@ static void get_radio_dm_currops(map_radio_info_t *radio, unsigned int *currops) *currops = dm_radio->currops; } +static void get_radio_dm_disops(map_radio_info_t *radio, unsigned int *disops) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + *disops = dm_radio->disops; +} + +static void get_radio_dm_apemlmr(map_radio_info_t *radio, unsigned int *apemlmr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + *apemlmr = dm_radio->apemlmr; +} + +static void get_radio_dm_apemlsr(map_radio_info_t *radio, unsigned int *apemlsr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + *apemlsr = dm_radio->apemlsr; +} + +static void get_radio_dm_apstr(map_radio_info_t *radio, unsigned int *apstr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + *apstr = dm_radio->apstr; +} + +static void get_radio_dm_apnstr(map_radio_info_t *radio, unsigned int *apnstr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + *apnstr = dm_radio->apnstr; +} + +static void get_radio_dm_bstaemlmr(map_radio_info_t *radio, unsigned int *bstaemlmr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + *bstaemlmr = dm_radio->bstaemlmr; +} + +static void get_radio_dm_bstaemlsr(map_radio_info_t *radio, unsigned int *bstaemlsr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + *bstaemlsr = dm_radio->bstaemlsr; +} + +static void get_radio_dm_bstastr(map_radio_info_t *radio, unsigned int *bstastr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + *bstastr = dm_radio->bstastr; +} + +static void get_radio_dm_bstanstr(map_radio_info_t *radio, unsigned int *bstanstr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + *bstanstr = dm_radio->bstanstr; +} + static void get_radio_dm_scan_reply(map_radio_info_t *radio, rbusMethodAsyncHandle_t *scan_reply) { dm_radio_table_t *dm_radio; @@ -745,6 +1052,78 @@ static void set_radio_dm_currops(map_radio_info_t *radio, unsigned int currops) dm_radio->currops = currops; } +static void set_radio_dm_disops(map_radio_info_t *radio, unsigned int disops) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + dm_radio->disops = disops; +} + +static void set_radio_dm_apemlmr(map_radio_info_t *radio, unsigned int apemlmr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + dm_radio->apemlmr = apemlmr; +} + +static void set_radio_dm_apemlsr(map_radio_info_t *radio, unsigned int apemlsr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + dm_radio->apemlsr = apemlsr; +} + +static void set_radio_dm_apstr(map_radio_info_t *radio, unsigned int apstr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + dm_radio->apstr = apstr; +} + +static void set_radio_dm_apnstr(map_radio_info_t *radio, unsigned int apnstr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + dm_radio->apnstr = apnstr; +} + +static void set_radio_dm_bstaemlmr(map_radio_info_t *radio, unsigned int bstaemlmr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + dm_radio->bstaemlmr = bstaemlmr; +} + +static void set_radio_dm_bstaemlsr(map_radio_info_t *radio, unsigned int bstaemlsr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + dm_radio->bstaemlsr = bstaemlsr; +} + +static void set_radio_dm_bstastr(map_radio_info_t *radio, unsigned int bstastr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + dm_radio->bstastr = bstastr; +} + +static void set_radio_dm_bstanstr(map_radio_info_t *radio, unsigned int bstanstr) +{ + dm_radio_table_t *dm_radio; + + dm_radio = &g_dm_dev_table[radio->ale->dm_idx].dm_radio[radio->dm_idx]; + dm_radio->bstanstr = bstanstr; +} + static void set_radio_dm_scan_reply(map_radio_info_t *radio, rbusMethodAsyncHandle_t scan_reply) { dm_radio_table_t *dm_radio; @@ -849,60 +1228,6 @@ static inline void free_sta_dm_idx(unsigned int didx, unsigned int ridx, g_dm_dev_table[didx].dm_radio[ridx].dm_bss[bidx].dm_sta[sidx].sta_id = NULL; } -static void update_ale_idxs(map_ale_info_t *removed_ale) -{ - map_ale_info_t *ale; - unsigned int idx; - - map_dm_foreach_agent_ale(ale) { - if (!is_local_agent(ale) && ale != removed_ale) { - ale->dm_idx = -1; - get_dev_dm_idx(ale->al_mac_str, &idx); - ale->dm_idx = idx; - } - } -} - -static void update_radio_idxs(map_ale_info_t *ale, map_radio_info_t *removed_radio) -{ - map_radio_info_t *radio; - unsigned int idx; - - map_dm_foreach_radio(ale, radio) { - if (radio != removed_radio) { - radio->dm_idx = -1; - get_radio_dm_idx(ale->dm_idx, radio, &idx); - } - } -} - -static void update_bss_idxs(map_radio_info_t *radio, map_bss_info_t *removed_bss) -{ - map_bss_info_t *bss; - unsigned int idx; - - map_dm_foreach_bss(radio, bss) { - if (bss != removed_bss) { - bss->dm_idx = -1; - get_bss_dm_idx(radio->ale->dm_idx, radio->dm_idx, bss, &idx); - } - } -} - -static void update_sta_idxs(map_bss_info_t *bss, map_sta_info_t *removed_sta) -{ - map_sta_info_t *sta; - unsigned int idx; - - map_dm_foreach_sta(bss, sta) { - if (sta != removed_sta) { - sta->dm_idx = -1; - get_sta_dm_idx(bss->radio->ale->dm_idx, - bss->radio->ale->dm_idx, bss->radio->dm_idx, sta, &idx); - } - } -} - static dm_sta_table_t *get_dm_sta(map_sta_info_t *sta) { unsigned int ale_idx; @@ -928,110 +1253,290 @@ static dm_sta_table_t *get_dm_sta(map_sta_info_t *sta) return &g_dm_dev_table[ale_idx].dm_radio[radio_idx].dm_bss[bss_idx].dm_sta[sta_idx]; } -static void mark_stas_removed(map_bss_info_t *bss) +static int get_ap_mld_dm_idx(unsigned int didx, + map_ap_mld_info_t *ap_mld, unsigned int *amidx) { - map_sta_info_t *sta; + dm_dev_table_t *dm_dev; + unsigned int idx = 0; - map_dm_foreach_sta(bss, sta) { - sta->dm_removed = true; + dm_dev = &g_dm_dev_table[didx]; + while (dm_dev->dm_ap_mld[idx].ap_mld_id) { + if (++idx >= MAX_RADIO_PER_AGENT) { + return -1; + } } + + dm_dev->dm_ap_mld[idx].ap_mld_id = ap_mld->mac_str; + + ap_mld->dm_idx = *amidx = idx; + + return 0; } -static void mark_bsss_removed(map_radio_info_t *radio) +static int check_ap_mld_dm_idx(map_ap_mld_info_t *ap_mld) { - map_bss_info_t *bss; - - map_dm_foreach_bss(radio, bss) { - bss->dm_removed = true; - mark_stas_removed(bss); + if (ap_mld->dm_idx < 0) { + return -1; } + + return check_dev_dm_idx(ap_mld->ale); } -static void mark_radios_removed(map_ale_info_t *ale) +static void free_ap_mld_dm_idx(unsigned int didx, unsigned int amidx) { - map_radio_info_t *radio; - - map_dm_foreach_radio(ale, radio) { - radio->dm_removed = true; - mark_bsss_removed(radio); - } + memset(&g_dm_dev_table[didx].dm_ap_mld[amidx], 0, sizeof(dm_ap_mld_table_t)); } -static int get_sta_dm_bmquery_reply(map_sta_info_t *sta, rbusMethodAsyncHandle_t *reply) +static int get_aff_ap_dm_idx(unsigned int didx, unsigned int amidx, + map_bss_info_t *aff_ap, unsigned int *aaidx) { - dm_sta_table_t *dm_sta = NULL; + dm_ap_mld_table_t *dm_ap_mld; + unsigned int idx = 0; - dm_sta = get_dm_sta(sta); - if (dm_sta == NULL) { - log_lib_e("Failed to get dm_sta entry: %s", sta->mac_str); - *reply = NULL; - return -1; + dm_ap_mld = &g_dm_dev_table[didx].dm_ap_mld[amidx]; + while (dm_ap_mld->dm_aff_ap[idx].aff_ap_id) { + if (++idx >= MAX_BSS_PER_RADIO) { + return -1; + } } - *reply = dm_sta->bmquery_reply; + dm_ap_mld->dm_aff_ap[idx].aff_ap_id = aff_ap->bssid_str; + + aff_ap->dm_idx = *aaidx = idx; return 0; } -static int set_sta_dm_bmquery_reply(map_sta_info_t *sta, rbusMethodAsyncHandle_t reply) +static int check_aff_ap_dm_idx(map_bss_info_t *aff_ap) { - dm_sta_table_t *dm_sta = NULL; - - dm_sta = get_dm_sta(sta); - if (dm_sta == NULL) { - log_lib_e("Failed to get dm_sta entry: %s", sta->mac_str); + if (aff_ap->dm_idx < 0) { return -1; } - dm_sta->bmquery_reply = reply; + return check_ap_mld_dm_idx(aff_ap->ap_mld); +} - return 0; +static inline void free_aff_ap_dm_idx(unsigned int didx, unsigned int amidx, + unsigned int aaidx) +{ + g_dm_dev_table[didx].dm_ap_mld[amidx].dm_aff_ap[aaidx].aff_ap_id = NULL; } -static int map_dm_rbus_client_steer_async_reply_set(map_sta_info_t *sta_info, rbusMethodAsyncHandle_t reply) +static int get_sta_mld_dm_idx(unsigned int didx, unsigned int amidx, + map_sta_mld_info_t *sta_mld, unsigned int *sidx) { - dm_sta_payload_t *sta_payload = NULL; + dm_ap_mld_table_t *dm_ap_mld; + unsigned int idx = 0; - if (!sta_info) { - return -1; + dm_ap_mld = &g_dm_dev_table[didx].dm_ap_mld[amidx]; + while (dm_ap_mld->dm_sta_mld[idx].sta_mld_id) { + if (++idx >= MAX_STATION_PER_BSS) { + return -1; + } } - sta_payload = (dm_sta_payload_t *)sta_info->dm_payload; - if (!sta_payload) { - return -1; - } + dm_ap_mld->dm_sta_mld[idx].sta_mld_id = sta_mld->mac_str; - sta_payload->rbus_steering_async_hnd = reply; + sta_mld->dm_idx = *sidx = idx; return 0; } -static int map_dm_rbus_client_steer_async_reply_get(map_sta_info_t *sta_info, rbusMethodAsyncHandle_t *reply) +static int check_sta_mld_dm_idx(map_sta_mld_info_t *sta_mld) { - dm_sta_payload_t *sta_payload = NULL; - - if (!sta_info) { + if (sta_mld->dm_idx < 0) { return -1; } - sta_payload = (dm_sta_payload_t *)sta_info->dm_payload; - if (!sta_payload) { - return -1; + return check_ap_mld_dm_idx(sta_mld->ap_mld); +} + +static inline void free_sta_mld_dm_idx(unsigned int didx, unsigned int amidx, + unsigned int smidx) +{ + memset(&g_dm_dev_table[didx].dm_ap_mld[amidx].dm_sta_mld[smidx], 0, + sizeof(dm_sta_mld_table_t)); +} + + +static int get_aff_sta_dm_idx(unsigned int didx, unsigned int amidx, unsigned int smidx, + map_sta_info_t *aff_sta, unsigned int *asidx) +{ + dm_sta_mld_table_t *dm_sta_mld; + unsigned int idx = 0; + + dm_sta_mld = &g_dm_dev_table[didx].dm_ap_mld[amidx].dm_sta_mld[smidx]; + while (dm_sta_mld->dm_aff_sta[idx].aff_sta_id) { + if (++idx >= MAX_STATION_PER_BSS) { + return -1; + } } - *reply = sta_payload->rbus_steering_async_hnd; + dm_sta_mld->dm_aff_sta[idx].aff_sta_id = aff_sta->mac_str; + + aff_sta->dm_as_idx = *asidx = idx; return 0; } -/*####################################################################### -# DATA HELPERS # -########################################################################*/ -/* Strings created in functions below must be according to tr181 */ - -static char *get_timestamp_str(uint64_t timestamp, char *buf, size_t buf_len) +static int check_aff_sta_dm_idx(map_sta_info_t *aff_sta) { -#define TIME_STR_LEN 21 + if (aff_sta->dm_as_idx < 0) { + return -1; + } + + return check_sta_mld_dm_idx(aff_sta->sta_mld); +} + +static inline void free_aff_sta_dm_idx(unsigned int didx, unsigned int amidx, + unsigned int smidx, unsigned int asidx) +{ + g_dm_dev_table[didx].dm_ap_mld[amidx].dm_sta_mld[smidx].dm_aff_sta[asidx].aff_sta_id = NULL; +} + +static void mark_stas_removed(map_bss_info_t *bss) +{ + map_sta_info_t *sta; + + map_dm_foreach_sta(bss, sta) { + sta->dm_removed = true; + } +} + +static void mark_bsss_removed(map_radio_info_t *radio) +{ + map_bss_info_t *bss; + + map_dm_foreach_bss(radio, bss) { + bss->dm_removed = true; + mark_stas_removed(bss); + } +} + +static void mark_radios_removed(map_ale_info_t *ale) +{ + map_radio_info_t *radio; + + map_dm_foreach_radio(ale, radio) { + radio->dm_removed = true; + mark_bsss_removed(radio); + } +} + +static void mark_aff_stas_removed(map_sta_mld_info_t *sta_mld) +{ + map_sta_info_t *aff_sta; + + map_dm_foreach_aff_sta(sta_mld, aff_sta) { + aff_sta->dm_as_removed = true; + } +} + +static void mark_sta_mlds_removed(map_ap_mld_info_t *ap_mld) +{ + map_sta_mld_info_t *sta_mld; + + map_dm_foreach_sta_mld(ap_mld, sta_mld) { + sta_mld->dm_removed = true; + mark_aff_stas_removed(sta_mld); + } +} + +static void mark_aff_aps_removed(map_ap_mld_info_t *ap_mld) +{ + map_bss_info_t *aff_ap; + + map_dm_foreach_aff_ap(ap_mld, aff_ap) { + aff_ap->dm_aa_removed = true; + } +} + +static void mark_ap_mlds_removed(map_ale_info_t *ale) +{ + map_ap_mld_info_t *ap_mld; + + map_dm_foreach_ap_mld(ale, ap_mld) { + ap_mld->dm_removed = true; + mark_aff_aps_removed(ap_mld); + mark_sta_mlds_removed(ap_mld); + } +} + +static int get_sta_dm_bmquery_reply(map_sta_info_t *sta, rbusMethodAsyncHandle_t *reply) +{ + dm_sta_table_t *dm_sta = NULL; + + dm_sta = get_dm_sta(sta); + if (dm_sta == NULL) { + log_lib_e("Failed to get dm_sta entry: %s", sta->mac_str); + *reply = NULL; + return -1; + } + + *reply = dm_sta->bmquery_reply; + + return 0; +} + +static int set_sta_dm_bmquery_reply(map_sta_info_t *sta, rbusMethodAsyncHandle_t reply) +{ + dm_sta_table_t *dm_sta = NULL; + + dm_sta = get_dm_sta(sta); + if (dm_sta == NULL) { + log_lib_e("Failed to get dm_sta entry: %s", sta->mac_str); + return -1; + } + + dm_sta->bmquery_reply = reply; + + return 0; +} + +static int map_dm_rbus_client_steer_async_reply_set(map_sta_info_t *sta_info, rbusMethodAsyncHandle_t reply) +{ + dm_sta_payload_t *sta_payload = NULL; + + if (!sta_info) { + return -1; + } + + sta_payload = (dm_sta_payload_t *)sta_info->dm_payload; + if (!sta_payload) { + return -1; + } + + sta_payload->rbus_steering_async_hnd = reply; + + return 0; +} + +static int map_dm_rbus_client_steer_async_reply_get(map_sta_info_t *sta_info, rbusMethodAsyncHandle_t *reply) +{ + dm_sta_payload_t *sta_payload = NULL; + + if (!sta_info) { + return -1; + } + + sta_payload = (dm_sta_payload_t *)sta_info->dm_payload; + if (!sta_payload) { + return -1; + } + + *reply = sta_payload->rbus_steering_async_hnd; + + return 0; +} + +/*####################################################################### +# DATA HELPERS # +########################################################################*/ +/* Strings created in functions below must be according to tr181 */ + +static char *get_timestamp_str(uint64_t timestamp, char *buf, size_t buf_len) +{ +#define TIME_STR_LEN 21 #define ZONE_STR_LEN 7 char time[TIME_STR_LEN]; char zone[ZONE_STR_LEN]; @@ -1113,11 +1618,27 @@ static char *get_freq_bands_str(uint16_t freq_bands, char *buf) static const char *get_auth_mode_str(uint16_t auth_mode) { + if ((auth_mode & IEEE80211_AUTH_MODE_WPA2PSK) && + (auth_mode & IEEE80211_AUTH_MODE_SAE) && + (auth_mode & IEEE80211_AUTH_MODE_SAE_24)) { + return "psk+sae+sae24"; + } + + if ((auth_mode & IEEE80211_AUTH_MODE_WPA2PSK) && + (auth_mode & IEEE80211_AUTH_MODE_SAE_24)) { + return "psk+sae24"; + } + if ((auth_mode & IEEE80211_AUTH_MODE_WPA2PSK) && (auth_mode & IEEE80211_AUTH_MODE_SAE)) { return "psk+sae"; } + if ((auth_mode & IEEE80211_AUTH_MODE_SAE) && + (auth_mode & IEEE80211_AUTH_MODE_SAE_24)) { + return "sae+sae24"; + } + if ((auth_mode & IEEE80211_AUTH_MODE_WPAPSK) || (auth_mode & IEEE80211_AUTH_MODE_WPA2PSK)) { return "psk"; @@ -1127,6 +1648,10 @@ static const char *get_auth_mode_str(uint16_t auth_mode) return "sae"; } + if (auth_mode & IEEE80211_AUTH_MODE_SAE_24) { + return "sae24"; + } + return "none"; } @@ -1624,6 +2149,58 @@ map_sta_info_t *map_dm_rbus_get_sta(map_bss_info_t *bss, int sta_idx) return NULL; } +map_ap_mld_info_t *map_dm_rbus_get_ap_mld(map_ale_info_t *ale, int ap_mld_idx) +{ + map_ap_mld_info_t *ap_mld; + + map_dm_foreach_ap_mld(ale, ap_mld) { + if (ap_mld->dm_idx == ap_mld_idx) { + return ap_mld; + } + } + + return NULL; +} + +map_bss_info_t *map_dm_rbus_get_aff_ap(map_ap_mld_info_t *ap_mld, int aff_ap_idx) +{ + map_bss_info_t *aff_ap; + + map_dm_foreach_aff_ap(ap_mld, aff_ap) { + if (aff_ap->dm_idx == aff_ap_idx) { + return aff_ap; + } + } + + return NULL; +} + +map_sta_mld_info_t *map_dm_rbus_get_sta_mld(map_ap_mld_info_t *ap_mld, int sta_mld_idx) +{ + map_sta_mld_info_t *sta_mld; + + map_dm_foreach_sta_mld(ap_mld, sta_mld) { + if (sta_mld->dm_idx == sta_mld_idx) { + return sta_mld; + } + } + + return NULL; +} + +map_sta_info_t *map_dm_rbus_get_aff_sta(map_sta_mld_info_t *sta_mld, int aff_sta_idx) +{ + map_sta_info_t *aff_sta; + + map_dm_foreach_aff_sta(sta_mld, aff_sta) { + if (aff_sta->dm_idx == aff_sta_idx) { + return aff_sta; + } + } + + return NULL; +} + map_scan_result_t *map_dm_rbus_get_scanres(map_radio_info_t *radio, unsigned int scan_id, unsigned int opclass, unsigned int channel) { @@ -1721,13 +2298,17 @@ static const char *get_table_alias(const char *src, char *alias, size_t max_len, #define DM_RADIO_PREFIX "Radio" #define DM_BSS_PREFIX "BSS" #define DM_STA_PREFIX "STA" +#define DM_AP_MLD_PREFIX "APMLD" +#define DM_AFF_AP_PREFIX "AffiliatedAP" +#define DM_STA_MLD_PREFIX "STAMLD" +#define DM_AFF_STA_PREFIX "AffiliatedSTA" static map_ale_info_t *dm_get_ale(const char *alias, bool is_num) { map_ale_info_t *ale; if (is_num) { - return map_dm_rbus_get_ale(atoi(alias)); + return map_dm_rbus_get_ale(atoi(alias) - 1); } map_dm_foreach_agent_ale(ale) { @@ -1793,6 +2374,90 @@ static map_sta_info_t *dm_get_sta(map_bss_info_t *bss, const char *alias, bool i return NULL; } +static map_ap_mld_info_t *dm_get_ap_mld(map_ale_info_t *ale, const char *alias, bool is_num) +{ + map_ap_mld_info_t *ap_mld; + + if (is_num) { + return map_dm_rbus_get_ap_mld(ale, atoi(alias) - 1); + } + + if (!alias) { + return NULL; + } + + map_dm_foreach_ap_mld(ale, ap_mld) { + if (strcmp(alias, ap_mld->mac_str) == 0) { + return ap_mld; + } + } + + return NULL; +} + +static map_bss_info_t *dm_get_aff_ap(map_ap_mld_info_t *ap_mld, const char *alias, bool is_num) +{ + map_bss_info_t *aff_ap; + + if (is_num) { + return map_dm_rbus_get_aff_ap(ap_mld, atoi(alias) - 1); + } + + if (!alias) { + return NULL; + } + + map_dm_foreach_aff_ap(ap_mld, aff_ap) { + if (strcmp(alias, aff_ap->bssid_str) == 0) { + return aff_ap; + } + } + + return NULL; +} + +static map_sta_mld_info_t *dm_get_sta_mld(map_ap_mld_info_t *ap_mld, const char *alias, bool is_num) +{ + map_sta_mld_info_t *sta_mld; + + if (is_num) { + return map_dm_rbus_get_sta_mld(ap_mld, atoi(alias) - 1); + } + + if (!alias) { + return NULL; + } + + map_dm_foreach_sta_mld(ap_mld, sta_mld) { + if (strcmp(alias, sta_mld->mac_str) == 0) { + return sta_mld; + } + } + + return NULL; +} + +static map_sta_info_t *dm_get_aff_sta(map_sta_mld_info_t *sta_mld, const char *alias, bool is_num) +{ + map_sta_info_t *aff_sta; + + if (is_num) { + return map_dm_rbus_get_aff_sta(sta_mld, atoi(alias) - 1); + } + + if (!alias) { + return NULL; + } + + map_dm_foreach_aff_sta(sta_mld, aff_sta) { + if (strcmp(alias, aff_sta->mac_str) == 0) { + return aff_sta; + } + } + + return NULL; +} + static int cmp_unassoc_sta_mac(void *unassoc_sta, void *mac) { if (unassoc_sta && mac) { @@ -1832,6 +2497,31 @@ static map_unassociated_sta_info_t *dm_get_unassoc_sta(map_radio_info_t *radio, return NULL; } + +static map_op_class_t *dm_get_disallowed_op_class(map_radio_info_t *radio, const char *alias, + bool is_num) +{ + int idx; + + if (is_num) { + idx = atoi(alias) - 1; + if (idx < 0 || idx >= radio->disallowed_op_class_list.op_classes_nr) { + log_lib_e("Invalid disallowed operating class index: %d", idx + 1); + return NULL; + } + return &radio->disallowed_op_class_list.op_classes[idx]; + } else { + uint8_t op_class = atoi(alias); + for (idx = 0; idx < radio->disallowed_op_class_list.op_classes_nr; idx++) { + if (radio->disallowed_op_class_list.op_classes[idx].op_class == op_class) { + return &radio->disallowed_op_class_list.op_classes[idx]; + } + } + } + + return NULL; +} + /*####################################################################### # Device.WiFi.DataElements.Network # ########################################################################*/ @@ -1966,14 +2656,14 @@ static rbusError_t network_setssid_rbus(rbusHandle_t handle, char const* method, map_profile_cfg_t profile = { /* default values */ .enabled = true, .type = MAP_PROFILE_TYPE_OTHER, - .supported_auth_modes = IEEE80211_AUTH_MODE_OPEN, - .supported_encryption_types = IEEE80211_ENCRYPTION_MODE_NONE, + .supported_auth_modes = 0, + .supported_encryption_types = 0, .bss_freq_bands = MAP_FREQ_BANDS_ALL, - .bss_state = MAP_FRONTHAUL_BSS, - .gateway = true, - .extender = true, + .bss_state = 0, + .gateway = -1, + .extender = -1, .hide = false, - .vlan_id = -1 + .vlan_id = -2 }; unsigned int profile_count = 0; const char *sval; @@ -2009,12 +2699,6 @@ static rbusError_t network_setssid_rbus(rbusHandle_t handle, char const* method, } add = (strcmp(sval, "false") != 0); - /* If remove operation, then other arguments are irrelevant */ - if (add == false) { - ret = map_profile_remove(&profile); - goto result; - } - /* Optional arguments */ rc_value = rbusObject_GetPropertyString(in, "Band", &sval, &len); if (rc_value == RBUS_VALUE_ERROR_SUCCESS) { @@ -2025,11 +2709,17 @@ static rbusError_t network_setssid_rbus(rbusHandle_t handle, char const* method, } } - rc_value = rbusObject_GetPropertyString(in, "PassPhrase", &sval, &len); - if (rc_value == RBUS_VALUE_ERROR_SUCCESS) { - if (len < MIN_PASSPHRASE_LEN) { - log_lib_e("Invalid password (too short): %s", sval); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + /* If remove operation, then other arguments are irrelevant */ + if (add == false) { + ret = map_profile_remove(&profile); + goto result; + } + + rc_value = rbusObject_GetPropertyString(in, "PassPhrase", &sval, &len); + if (rc_value == RBUS_VALUE_ERROR_SUCCESS) { + if (len < MIN_PASSPHRASE_LEN) { + log_lib_e("Invalid password (too short): %s", sval); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); return RBUS_ERROR_INVALID_INPUT; } strncpy(profile.wpa_key, sval, sizeof(profile.wpa_key) - 1); @@ -2087,21 +2777,26 @@ static rbusError_t network_setssid_rbus(rbusHandle_t handle, char const* method, } if (profile_count < cfg->num_profiles) { - rc = rbusTable_registerRow(g_bus_handle, - "Device.WiFi.DataElements.Network.SSID.", cfg->num_profiles, NULL); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to create row[%d] for ssid", cfg->num_profiles); + while (++profile_count <= cfg->num_profiles) { + rc = rbusTable_registerRow(g_bus_handle, + "Device.WiFi.DataElements.Network.SSID.", profile_count, NULL); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to create row[%d] for ssid", profile_count); + break; + } } } else if (profile_count > cfg->num_profiles) { - char tname[64] = {0}; - - snprintf(tname, sizeof(tname), - "Device.WiFi.DataElements.Network.SSID.%d", profile_count); + do { + char tname[64] = {0}; - rc = rbusTable_unregisterRow(g_bus_handle, tname); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to delete row[%d] for ssid", profile_count); - } + snprintf(tname, sizeof(tname), + "Device.WiFi.DataElements.Network.SSID.%d", profile_count); + rc = rbusTable_unregisterRow(g_bus_handle, tname); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to delete row[%d] for ssid", profile_count); + break; + } + } while (--profile_count > cfg->num_profiles); } rbus_add_prop_str(out, "Status", "Success"); @@ -2177,43 +2872,22 @@ static rbusError_t ssid_get_rbus(rbusHandle_t handle, rbusProperty_t property, r /*####################################################################### # Device.WiFi.DataElements.Network.Device # ########################################################################*/ -static void create_ale_mac(mac_addr mac, int *ret_idx) +static void dm_rbus_create_ale(map_ale_info_t *ale) { - mac_addr_str mac_str; - unsigned int idx; + unsigned int ale_idx; rbusError_t rc; - mac_to_string(mac, mac_str); - - log_lib_d("create ale: %s", mac_str); - - if (get_dev_dm_idx(mac_str, &idx) < 0) { - log_lib_e("could not find free index for ale: %s", mac_str); + if (get_dev_dm_idx(ale, &ale_idx) < 0) { + log_lib_e("could not find free index for ale: %s", ale->al_mac_str); return; } - if (ret_idx) { - *ret_idx = idx; - } - - if (idx > 0) { - rc = rbusTable_registerRow(g_bus_handle, - "Device.WiFi.DataElements.Network.Device.", idx, mac_str); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to create row[%d] for ale: %s", idx, mac_str); - } - } -} - -static void dm_rbus_create_ale(map_ale_info_t *ale) -{ - /* Local agent already added */ - if (is_local_agent(ale)) { - ale->dm_idx = 0; - return; + log_lib_d("Create row Device.WiFi.DataElements.Network.Device.%d", ale_idx + 1); + rc = rbusTable_registerRow(g_bus_handle, + "Device.WiFi.DataElements.Network.Device.", ale_idx + 1, ale->al_mac_str); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to create row[%d] for ale: %s", ale_idx + 1, ale->al_mac_str); } - - create_ale_mac(ale->al_mac, &ale->dm_idx); } static void update_dm_cac_available(map_ale_info_t *ale) @@ -2236,7 +2910,7 @@ static void update_dm_cac_available(map_ale_info_t *ale) /* Add missing CAC available channels */ snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d.CACStatus.%d." - "CACAvailableChannel.", ale_idx, cac_idx); + "CACAvailableChannel.", ale_idx + 1, cac_idx); curr_count = curr_count ? curr_count : 1; for (idx = curr_count; idx <= new_count; idx++) { @@ -2250,7 +2924,7 @@ static void update_dm_cac_available(map_ale_info_t *ale) for (idx = curr_count; idx > new_count; idx--) { snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d.CACStatus.%d." - "CACAvailableChannel.%d", ale_idx, cac_idx, idx); + "CACAvailableChannel.%d", ale_idx + 1, cac_idx, idx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { @@ -2281,7 +2955,7 @@ static void update_dm_cac_nonoccupancy(map_ale_info_t *ale) /* Add missing CAC non occupancy channels */ snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d.CACStatus.%d." - "CACNonOccupancyChannel.", ale_idx, cac_idx); + "CACNonOccupancyChannel.", ale_idx + 1, cac_idx); curr_count = curr_count ? curr_count : 1; for (idx = curr_count; idx <= new_count; idx++) { @@ -2295,7 +2969,7 @@ static void update_dm_cac_nonoccupancy(map_ale_info_t *ale) for (idx = curr_count; idx > new_count; idx--) { snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d.CACStatus.%d." - "CACNonOccupancyChannel.%d", ale_idx, cac_idx, idx); + "CACNonOccupancyChannel.%d", ale_idx + 1, cac_idx, idx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { @@ -2326,7 +3000,7 @@ static void update_dm_cac_active(map_ale_info_t *ale) /* Add missing CAC active channels */ snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d.CACStatus.%d." - "CACActiveChannel.", ale_idx, cac_idx); + "CACActiveChannel.", ale_idx + 1, cac_idx); curr_count = curr_count ? curr_count : 1; for (idx = curr_count; idx <= new_count; idx++) { @@ -2340,7 +3014,7 @@ static void update_dm_cac_active(map_ale_info_t *ale) for (idx = curr_count; idx > new_count; idx--) { snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d.CACStatus.%d." - "CACActiveChannel.%d", ale_idx, cac_idx, idx); + "CACActiveChannel.%d", ale_idx + 1, cac_idx, idx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { @@ -2361,7 +3035,7 @@ static int radio_add_unassoc_sta(map_radio_info_t *radio, map_nb_unassoc_sta_met snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d.Radio.%d." "UnassociatedSTA.", - radio->ale->dm_idx, radio->dm_idx + 1); + radio->ale->dm_idx + 1, radio->dm_idx + 1); info = calloc(1, sizeof(*info)); if (!info) { @@ -2408,7 +3082,7 @@ static void radio_remove_unassoc_sta(map_radio_info_t *radio, mac_addr mac) snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d.Radio.%d." "UnassociatedSTA.%d", - radio->ale->dm_idx, radio->dm_idx + 1, old->dm_idx); + radio->ale->dm_idx + 1, radio->dm_idx + 1, old->dm_idx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { @@ -2536,7 +3210,7 @@ static void dm_rbus_update_ale(map_ale_info_t *ale) if (!cac_valid) { snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d." - "CACStatus.", ale_idx); + "CACStatus.", ale_idx + 1); /* There is only one CACStatus at a time */ cac_idx = 1; @@ -2555,7 +3229,7 @@ static void dm_rbus_update_ale(map_ale_info_t *ale) cac_idx = 1; snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d." - "CACStatus.%d.", ale_idx, cac_idx); + "CACStatus.%d.", ale_idx + 1, cac_idx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { @@ -2579,7 +3253,7 @@ static void dm_rbus_update_ale(map_ale_info_t *ale) /* Add missing ethernet interfaces */ snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d." - "X_AIRTIES_Ethernet.Interface.", ale_idx); + "X_AIRTIES_Ethernet.Interface.", ale_idx + 1); curr_count = curr_count ? curr_count : 1; for (idx = curr_count; idx <= iface_count; idx++) { @@ -2593,7 +3267,7 @@ static void dm_rbus_update_ale(map_ale_info_t *ale) for (idx = curr_count; idx > iface_count; idx--) { snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d." - "X_AIRTIES_Ethernet.Interface.%d", ale_idx, idx); + "X_AIRTIES_Ethernet.Interface.%d", ale_idx + 1, idx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { @@ -2612,7 +3286,7 @@ static void dm_rbus_update_ale(map_ale_info_t *ale) /* Add missing ethernet devices */ snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d.X_AIRTIES_Ethernet." - "Interface.%d.Device.", ale_idx, iface_idx + 1); + "Interface.%d.Device.", ale_idx + 1, iface_idx + 1); curr_count = curr_count ? curr_count : 1; for (idx = curr_count; idx <= new_count; idx++) { @@ -2626,7 +3300,7 @@ static void dm_rbus_update_ale(map_ale_info_t *ale) for (idx = curr_count; idx > new_count; idx--) { snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d.X_AIRTIES_Ethernet." - "Interface.%d.Device.%d", ale_idx, iface_idx + 1, idx); + "Interface.%d.Device.%d", ale_idx + 1, iface_idx + 1, idx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { @@ -2650,12 +3324,6 @@ static void dm_rbus_remove_ale(map_ale_info_t *ale) char tname[256] = {0}; rbusError_t rc; - log_lib_d("remove ale[%d]: %s", ale->dm_idx, ale->al_mac_str); - - if (is_local_agent(ale)) { - return; - } - if (check_dev_dm_idx(ale) < 0) { log_lib_e("Invalid indexing for ale"); return; @@ -2664,8 +3332,9 @@ static void dm_rbus_remove_ale(map_ale_info_t *ale) ale_idx = ale->dm_idx; snprintf(tname, sizeof(tname), - "Device.WiFi.DataElements.Network.Device.%d", ale_idx); + "Device.WiFi.DataElements.Network.Device.%d", ale_idx + 1); + log_lib_d("Delete row %s", tname); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { log_lib_e("Failed to delete row[%d] for ale: %s", @@ -2674,19 +3343,79 @@ static void dm_rbus_remove_ale(map_ale_info_t *ale) free_dev_dm_idx(ale_idx); - if (0) { - /* This may be enabled to shift indexes, if the data model - automatically keeps indexing consecutive. rbus does not - support it, tr069 forbids it */ - update_ale_idxs(ale); - } - /* Mark child objects are removed */ mark_radios_removed(ale); + mark_ap_mlds_removed(ale); return; } +static int parse_mac_list(char *buf, int blen, mac_addr **mac_list, uint8_t *mac_count) +{ + uint8_t count; + mac_addr *list; + mac_addr mac; + char *end; + + *mac_list = NULL; + *mac_count = 0; + + count = blen / sizeof(mac_addr_str); + if (!count) { + return 0; + } + + list = calloc(count, sizeof(mac_addr)); + if (!list) { + log_lib_e("Memory allocation failed"); + return -1; + } + + count = 0; + while (blen >= (int)sizeof(mac_addr_str)) { + end = strchr(buf, ','); + if (end) { + *end = '\0'; + } + + if (!acu_mac_from_string(buf, mac)) { + /* Regular, add to list */ + maccpy(list[count], mac); + ++count; + } + + if (end == NULL) { + break; + } + blen -= (end + 1 - buf); + buf = end + 1; + } + + *mac_list = list; + *mac_count = count; + + return 0; +} + +static void *get_mac_list_str(mac_addr *mac_list, size_t mac_count, char *buf, uint16_t blen) +{ + char *start = buf; + uint8_t idx = 0; + + while (blen >= sizeof(mac_addr_str) && idx < mac_count) { + acu_mac_to_string(mac_list[idx], buf); + buf += (sizeof(mac_addr_str) - 1); + blen -= sizeof(mac_addr_str); + if (++idx >= mac_count) { + break; + } + *buf = ','; + ++buf; + } + + return start; +} + static rbusError_t device_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { #define CCODE_LEN 3 @@ -2740,10 +3469,22 @@ static rbusError_t device_get_rbus(rbusHandle_t handle, rbusProperty_t property, } else if (strcmp(param, "CountryCode") == 0) { get_country_code_str(ale->country_code, country_code); rbusValue_SetString(value, country_code); - } else if (strcmp(param, "RadioNumberOfEntries") == 0) { - rbusValue_SetUInt32(value, ale->radios_nr); } else if (strcmp(param, "MultiAPProfile") == 0) { rbusValue_SetUInt32(value, ale->map_profile); + } else if (strcmp(param, "BTMSteeringDisallowedSTAList") == 0) { + uint16_t blen = ale->btm_steering_disallow_macs_nr * sizeof(mac_addr_str); + char *buf = calloc(blen, sizeof(char)); + get_mac_list_str(ale->btm_steering_disallow_macs, ale->btm_steering_disallow_macs_nr, buf, blen); + rbusValue_SetString(value, buf); + free(buf); + } else if (strcmp(param, "LocalSteeringDisallowedSTAList") == 0) { + uint16_t blen = ale->local_steering_disallow_macs_nr * sizeof(mac_addr_str); + char *buf = calloc(blen, sizeof(char)); + get_mac_list_str(ale->local_steering_disallow_macs, ale->local_steering_disallow_macs_nr, buf, blen); + rbusValue_SetString(value, buf); + free(buf); + } else if (strcmp(param, "RadioNumberOfEntries") == 0) { + rbusValue_SetUInt32(value, ale->radios_nr); } else if (strcmp(param, "CACStatusNumberOfEntries") == 0) { rbusValue_SetUInt32(value, ale->cac_status_report.valid ? 1 : 0); } else { @@ -2758,18 +3499,21 @@ static rbusError_t device_get_rbus(rbusHandle_t handle, rbusProperty_t property, return RBUS_ERROR_SUCCESS; } -/*####################################################################### -# Device.WiFi.DataElements.Network.Device.MultiAPDevice.Backhaul # -########################################################################*/ -static rbusError_t backhaul_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t device_set_rbus(rbusHandle_t handle, rbusProperty_t property, rbusSetHandlerOptions_t* opts) { (void) handle; (void) opts; - rbusValue_t value; char const* name = rbusProperty_GetName(property); + rbusValue_t value = rbusProperty_GetValue(property); + rbusValueType_t type = rbusValue_GetType(value); char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; map_ale_info_t *ale; + const char *strval = NULL; + mac_addr *mac_list = NULL; + uint8_t mac_count = 0; + char *buffer; + int blen; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -2779,34 +3523,126 @@ static rbusError_t backhaul_get_rbus(rbusHandle_t handle, rbusProperty_t propert return RBUS_ERROR_INVALID_INPUT; } - sscanf(name, "MultiAPDevice.Backhaul.%s", param); + sscanf(name, "%s", param); + if (strcmp(param, "BTMSteeringDisallowedSTAList") == 0) { + if (type != RBUS_STRING) { + log_lib_e("Invalid value type"); + return RBUS_ERROR_INVALID_INPUT; + } - rbusValue_Init(&value); + strval = rbusValue_GetString(value, &blen); + if (blen) { + buffer = strdup(strval); + parse_mac_list(buffer, blen + 1, &mac_list, &mac_count); + free(buffer); + } - if (strcmp(param, "LinkType") == 0) { - rbusValue_SetString(value, get_backhaul_link_type_str(ale)); - } else if (strcmp(param, "BackhaulMACAddress") == 0) { - rbusValue_SetString(value, mac_string(ale->upstream_remote_iface_mac)); - } else if (strcmp(param, "BackhaulDeviceID") == 0) { - rbusValue_SetString(value, mac_string(ale->upstream_al_mac)); - } else if (strcmp(param, "MACAddress") == 0) { - rbusValue_SetString(value, mac_string(ale->upstream_local_iface_mac)); + SFREE(ale->btm_steering_disallow_macs); + ale->btm_steering_disallow_macs = mac_list; + ale->btm_steering_disallow_macs_nr = mac_count; + + if (map_dm_get_nbapi()->set_steering_policy != NULL) { + map_dm_get_nbapi()->set_steering_policy(ale); + } else { + log_lib_e("Set steer policy is not available"); + return RBUS_ERROR_BUS_ERROR; + } + } else if (strcmp(param, "LocalSteeringDisallowedSTAList") == 0) { + if (type != RBUS_STRING) { + log_lib_e("Invalid value type"); + return RBUS_ERROR_INVALID_INPUT; + } + + strval = rbusValue_GetString(value, &blen); + if (blen) { + buffer = strdup(strval); + parse_mac_list(buffer, blen + 1, &mac_list, &mac_count); + free(buffer); + } + + SFREE(ale->local_steering_disallow_macs); + ale->local_steering_disallow_macs = mac_list; + ale->local_steering_disallow_macs_nr = mac_count; + + if (map_dm_get_nbapi()->set_steering_policy != NULL) { + map_dm_get_nbapi()->set_steering_policy(ale); + } else { + log_lib_e("Set steer policy is not available"); + return RBUS_ERROR_BUS_ERROR; + } } else { log_lib_e("Invalid param: %s", param); - rbusValue_Release(value); return RBUS_ERROR_INVALID_INPUT; } - rbusProperty_SetValue(property, value); - rbusValue_Release(value); + return RBUS_ERROR_SUCCESS; +} + +static rbusError_t device_reboot_rbus(rbusHandle_t handle, char const* method, rbusObject_t in, rbusObject_t out, rbusMethodAsyncHandle_t async) +{ + (void) handle; + (void) async; + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_nb_reset_reboot_param_t payload; + bool is_reset; + bool factory_reset; + rbusValueError_t rc; + + rbusObject_SetName(out, "Output"); + + method += sizeof(DM_DEVICE_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + goto bail; + } + + sscanf(method, "%s", param); + if (strcmp(param, "X_AIRTIES_Reboot()") != 0) { + log_lib_e("Invalid method: %s", param); + goto bail; + } + + /* Optional arguments */ + rc = rbusObject_GetPropertyBoolean(in, "Reset", &is_reset); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + is_reset = false; + } + payload.is_reset = is_reset; + if (!is_reset) { + factory_reset = false; + } else { + rc = rbusObject_GetPropertyBoolean(in, "FactoryReset", &factory_reset); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + factory_reset = false; + } + } + payload.factory_reset = factory_reset; + + /* Perform device reboot */ + if (map_dm_get_nbapi()->reset_reboot != NULL) { + map_dm_get_nbapi()->reset_reboot(ale, &payload); + rbus_add_prop_str(out, "Status", "Success"); + } else { + log_lib_e("Reboot is not available"); + rbus_add_prop_str(out, "Status", "Error_Not_Ready"); + return RBUS_ERROR_INVALID_METHOD; + } return RBUS_ERROR_SUCCESS; + +bail: + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; } /*####################################################################### -# ..DataElements.Network.Device.MultiAPDevice.Backhaul.Stats # +# Device.WiFi.DataElements.Network.Device.MultiAPDevice.Backhaul # ########################################################################*/ -static rbusError_t bhstats_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t backhaul_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { (void) handle; (void) opts; @@ -2815,13 +3651,7 @@ static rbusError_t bhstats_get_rbus(rbusHandle_t handle, rbusProperty_t property char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; map_ale_info_t *ale; - map_ale_info_t *us_ale; - int8_t iface_group; - map_sta_info_t *sta; - map_sta_traffic_stats_t *ts; - map_sta_link_metrics_t *lm; - map_sta_ext_bss_metrics_t *ebm; - char timestamp[MAX_TS_STR_LEN] = {0}; + map_ale_info_t *us_ale = NULL; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -2831,81 +3661,45 @@ static rbusError_t bhstats_get_rbus(rbusHandle_t handle, rbusProperty_t property return RBUS_ERROR_INVALID_INPUT; } - sscanf(name, "MultiAPDevice.Backhaul.Stats.%s", param); - - sta = NULL; - ts = NULL; - lm = NULL; - iface_group = INTERFACE_TYPE_GROUP_GET(ale->upstream_iface_type); - if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { + if (!ale->is_local) { us_ale = map_dm_get_ale(ale->upstream_al_mac); - if (us_ale) { - sta = map_dm_get_sta_from_ale(us_ale, ale->upstream_local_iface_mac); - ts = sta ? sta->traffic_stats : NULL; - lm = (sta && sta->metrics) ? first_object(sta->metrics) : NULL; - } else { - log_lib_w("Upstream ale not found"); + if (us_ale == get_root_ale_node()) { + us_ale = map_dm_get_ale(map_cfg_get()->controller_cfg.local_agent_al_mac); + if (us_ale && !us_ale->is_local_colocated) { + log_lib_e("local agent for controller is not colocated"); + us_ale = NULL; + } } - } else { - log_lib_w("Stats not supported for %s", get_backhaul_link_type_str(ale)); } + sscanf(name, "MultiAPDevice.Backhaul.%s", param); + rbusValue_Init(&value); - if (strcmp(param, "BytesReceived") == 0) { - if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { - rbusValue_SetUInt64(value, ts ? ts->rxbytes : 0); - } else { - rbusValue_SetUInt64(value, 0); - } - } else if (strcmp(param, "BytesSent") == 0) { - if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { - rbusValue_SetUInt64(value, ts ? ts->txbytes : 0); - } else { - rbusValue_SetUInt64(value, 0); - } - } else if (strcmp(param, "PacketsReceived") == 0) { - if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { - rbusValue_SetUInt64(value, ts ? ts->rxpkts : 0); - } else { - rbusValue_SetUInt64(value, 0); - } - } else if (strcmp(param, "PacketsSent") == 0) { - if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { - rbusValue_SetUInt64(value, ts ? ts->txpkts : 0); - } else { - rbusValue_SetUInt64(value, 0); - } - } else if (strcmp(param, "ErrorsReceived") == 0) { - if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { - rbusValue_SetUInt64(value, ts ? ts->rxpkterrors : 0); + if (strcmp(param, "LinkType") == 0) { + if (ale->is_local || !us_ale) { + rbusValue_SetString(value, "None"); } else { - rbusValue_SetUInt64(value, 0); + rbusValue_SetString(value, get_backhaul_link_type_str(ale)); } - } else if (strcmp(param, "ErrorsSent") == 0) { - if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { - rbusValue_SetUInt64(value, ts ? ts->txpkterrors : 0); + } else if (strcmp(param, "BackhaulMACAddress") == 0) { + if (ale->is_local || !us_ale) { + rbusValue_SetString(value, ""); } else { - rbusValue_SetUInt64(value, 0); + rbusValue_SetString(value, mac_string(ale->upstream_remote_iface_mac)); } - } else if (strcmp(param, "SignalStrength") == 0) { - /* Indeterminate other than Wi-Fi */ - rbusValue_SetUInt32(value, lm ? lm->rssi : 0); - } else if (strcmp(param, "LastDataDownlinkRate") == 0) { - if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { - rbusValue_SetUInt32(value, ebm->last_data_dl_rate); + } else if (strcmp(param, "BackhaulDeviceID") == 0) { + if (ale->is_local || !us_ale) { + rbusValue_SetString(value, ""); } else { - rbusValue_SetUInt32(value, 0); + rbusValue_SetString(value, us_ale->al_mac_str); } - } else if (strcmp(param, "LastDataUplinkRate") == 0) { - if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { - rbusValue_SetUInt32(value, ebm->last_data_ul_rate); + } else if (strcmp(param, "MACAddress") == 0) { + if (ale->is_local || !us_ale) { + rbusValue_SetString(value, ""); } else { - rbusValue_SetUInt32(value, 0); + rbusValue_SetString(value, mac_string(ale->upstream_local_iface_mac)); } - } else if (strcmp(param, "TimeStamp") == 0) { - get_timestamp_str(0, timestamp, sizeof(timestamp)); - rbusValue_SetString(value, timestamp); } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -2919,126 +3713,239 @@ static rbusError_t bhstats_get_rbus(rbusHandle_t handle, rbusProperty_t property } /*####################################################################### -# Device.WiFi.DataElements.Network.Device.CACStatus # +# ..Network.Device.MultiAPDevice.Backhaul.SteerWiFiBackhaul() # ########################################################################*/ -static rbusError_t cacstatus_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static int steerwifibh_set_channel(map_bss_info_t *bss, int32_t channel, map_nb_steer_wifi_bh_param_t *payload) { - (void) handle; - (void) opts; - rbusValue_t value; - char const* name = rbusProperty_GetName(property); - char param[MAX_PROP_PARAM_LEN] = {0}; - bool is_num; - unsigned int cac_idx; - map_ale_info_t *ale; - char timestamp[MAX_TS_STR_LEN] = {0}; - map_cac_status_report_t *csr; + map_radio_info_t *radio; - name += sizeof(DM_DEVICE_PREFIX); - name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); - ale = dm_get_ale(param, is_num); - if (ale == NULL) { - log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); - return RBUS_ERROR_INVALID_INPUT; + radio = bss->radio; + payload->op_class = radio->current_op_class; + if (channel < 0) { + payload->channel = radio->current_op_channel; } - sscanf(name, "CACStatus.%d.%s", &cac_idx, param); + return 0; +} - /* There is only one CACStatus at a time */ - if (cac_idx != 1) { - log_lib_e("Invalid CAC status index: %d", cac_idx); - return RBUS_ERROR_INVALID_INPUT; +static void steerwifibh_send_result(map_sta_info_t *sta) +{ + map_ale_info_t *ale; + rbusMethodAsyncHandle_t async; + mac_addr target_bssid; + timer_id_t timer_id; + rbusObject_t out; + rbusError_t rc; + int found = 0; + + /* Check if this STA is an AP */ + map_dm_foreach_agent_ale(ale) { + if (!maccmp(sta->mac, ale->upstream_local_iface_mac)) { + found = 1; + break; + } + } + if (!found) { + log_lib_i("sta is not an AP"); + return; } - csr = ale->cac_status_report.valid ? &ale->cac_status_report : NULL; - rbusValue_Init(&value); + /* Check for pending steer wifi backhaul reply */ + get_dev_dm_steerwifibh_reply(ale, &async); + if (!async) { + log_lib_i("no pending backhaul steer"); + return; + } + get_dev_dm_steerwifibh_target(ale, target_bssid); - if (strcmp(param, "TimeStamp") == 0) { - get_timestamp_str(0, timestamp, sizeof(timestamp)); - rbusValue_SetString(value, timestamp); - } else if (strcmp(param, "CACAvailableChannelNumberOfEntries") == 0) { - rbusValue_SetUInt32(value, csr ? csr->available_pairs_nr : 0); - } else if (strcmp(param, "CACNonOccupancyChannelNumberOfEntries") == 0) { - rbusValue_SetUInt32(value, csr ? csr->non_occupancy_pairs_nr : 0); - } else if (strcmp(param, "CACActiveChannelNumberOfEntries") == 0) { - rbusValue_SetUInt32(value, csr ? csr->ongoing_cac_pairs_nr : 0); + /* Unregister timeout timer */ + map_dm_get_ale_timer_id(timer_id, ale, STEER_WIFIBH_TIMER_ID); + if (map_is_timer_registered(timer_id)) { + map_timer_unregister_callback(timer_id); + } + + /* Prepare and send response */ + rbusObject_Init(&out, "Output"); + + if (!maccmp(sta->bss->bssid, target_bssid)) { + rbus_add_prop_str(out, "Status", "Success"); + rc = RBUS_ERROR_SUCCESS; } else { - log_lib_e("Invalid param: %s", param); - rbusValue_Release(value); - return RBUS_ERROR_INVALID_INPUT; + rbus_add_prop_str(out, "Status", "Error_Other"); + rc = RBUS_ERROR_BUS_ERROR; } - rbusProperty_SetValue(property, value); - rbusValue_Release(value); + rc = rbusMethod_SendAsyncResponse(async, rc, out); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Steer WiFi backhaul response failed: %d", rc); + } - return RBUS_ERROR_SUCCESS; + rbusObject_Release(out); + + /* Reset steer wifi backhaul status */ + set_dev_dm_steerwifibh_target(ale, g_zero_mac); + set_dev_dm_steerwifibh_reply(ale, NULL); } -/*####################################################################### -# ..DataElements.Network.Device.CACStatus.CACAvailableChannel # -########################################################################*/ -static rbusError_t cacavail_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static uint8_t steerwifibh_async_cb(char *timer_id, void *arg) +{ + (void) arg; + mac_addr_str ale_mac_str = {0}; + mac_addr ale_mac; + map_ale_info_t *ale; + rbusMethodAsyncHandle_t async; + rbusObject_t out; + rbusError_t rc; + + /* Try fo find ALE via timer_id */ + memcpy(ale_mac_str, &timer_id[4], sizeof(mac_addr_str) - 1); + if (acu_mac_from_string(ale_mac_str, ale_mac)) { + log_lib_e("Invalid mac address"); + return 1; + } + ale = map_dm_get_ale(ale_mac); + if (!ale) { + log_lib_e("Ale not found"); + return 1; + } + + /* Check for pending steer wifi backhaul reply */ + get_dev_dm_steerwifibh_reply(ale, &async); + if (!async) { + log_lib_i("no pending backhaul steer"); + return 1; + } + + /* Prepare and send response */ + rbusObject_Init(&out, "Output"); + + rbus_add_prop_str(out, "Status", "Timeout"); + + rc = rbusMethod_SendAsyncResponse(async, RBUS_ERROR_TIMEOUT, out); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Steer WiFi backhaul response failed: %d", rc); + } + + rbusObject_Release(out); + + /* Reset steer wifi backhaul status */ + set_dev_dm_steerwifibh_target(ale, g_zero_mac); + set_dev_dm_steerwifibh_reply(ale, NULL); + + return 1; +} + +static rbusError_t backhaul_steerwifibh_rbus(rbusHandle_t handle, char const* method, rbusObject_t in, rbusObject_t out, rbusMethodAsyncHandle_t async) { (void) handle; - (void) opts; - rbusValue_t value; - char const* name = rbusProperty_GetName(property); + (void) async; char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; - unsigned int cac_idx; - unsigned int avail_idx; map_ale_info_t *ale; - map_cac_status_report_t *csr; - map_cac_available_pair_t *avp; + map_bss_info_t *cbss; + map_bss_info_t *tbss; + rbusMethodAsyncHandle_t prev; + map_nb_steer_wifi_bh_param_t payload = {0}; + uint32_t timeout; + uint32_t channel; + timer_id_t timer_id; + rbusValueError_t rc; + const char *sval; + int len; - name += sizeof(DM_DEVICE_PREFIX); - name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + rbusObject_SetName(out, "Output"); + + method += sizeof(DM_DEVICE_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); ale = dm_get_ale(param, is_num); if (ale == NULL) { log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); - return RBUS_ERROR_INVALID_INPUT; + goto bail; + } + if (INTERFACE_TYPE_GROUP_GET(ale->upstream_iface_type) != INTERFACE_TYPE_GROUP_WLAN) { + log_lib_e("Invalid backhaul type"); + goto bail; } - sscanf(name, "CACStatus.%d.CACAvailableChannel.%d.%s", - &cac_idx, &avail_idx, param); + sscanf(method, "MultiAPDevice.Backhaul.%s", param); + if (strcmp(param, "SteerWiFiBackhaul()") != 0) { + log_lib_e("Invalid method: %s", param); + goto bail; + } - /* There is only one CACStatus at a time */ - if (cac_idx != 1) { - log_lib_e("Invalid CAC status index: %d", cac_idx); - return RBUS_ERROR_INVALID_INPUT; + get_dev_dm_steerwifibh_reply(ale, &prev); + if (prev) { + log_lib_e("SteerWiFiBackhaul is not finished yet"); + rbus_add_prop_str(out, "Status", "Error_Not_Ready"); + return RBUS_ERROR_INVALID_METHOD; } - csr = ale->cac_status_report.valid ? &ale->cac_status_report : NULL; - if (csr && avail_idx > csr->available_pairs_nr) { - log_lib_e("Invalid available channel index: %d", avail_idx); - return RBUS_ERROR_INVALID_INPUT; + /* Mandatory arguments */ + rc = rbusObject_GetPropertyString(in, "TargetBSS", &sval, &len); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("TargetBSS is mandatory"); + goto bail; + } + if (acu_mac_from_string(sval, payload.target_bssid)) { + log_lib_e("Invalid TargetBSS"); + goto bail; + } + tbss = map_dm_get_bss_gbl(payload.target_bssid); + if (tbss == NULL || !(tbss->type & MAP_BACKHAUL_BSS)) { + log_lib_e("TargetBSS is invalid or not a backhaul"); + goto bail; + } + cbss = map_dm_get_bss_gbl(ale->upstream_remote_iface_mac); + if (tbss == cbss) { + log_lib_n("TargetBSS is same with current"); + rbus_add_prop_str(out, "Status", "Success"); + return RBUS_ERROR_SUCCESS; } - avp = &csr->available_pairs[avail_idx - 1]; - rbusValue_Init(&value); + rc = rbusObject_GetPropertyUInt32(in, "TimeOut", &timeout); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("TimeOut is mandatory"); + goto bail; + } + payload.timeout = timeout; - if (strcmp(param, "OpClass") == 0) { - rbusValue_SetUInt32(value, avp ? avp->op_class : 0); - } else if (strcmp(param, "Channel") == 0) { - rbusValue_SetUInt32(value, avp ? avp->channel : 0); - } else if (strcmp(param, "Minutes") == 0) { - rbusValue_SetUInt32(value, avp ? avp->minutes_since_cac_completion : 0); + /* Optional arguments */ + rc = rbusObject_GetPropertyUInt32(in, "Channel", &channel); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + channel = -1; + } + if (steerwifibh_set_channel(tbss, channel, &payload) < 0) { + goto bail; + } + + acu_maccpy(payload.bsta_mac, ale->upstream_local_iface_mac); + + /* Perform steer wifi backhaul request */ + if (map_dm_get_nbapi()->steer_wifi_backhaul != NULL) { + map_dm_get_nbapi()->steer_wifi_backhaul(ale, &payload); + set_dev_dm_steerwifibh_reply(ale, async); + set_dev_dm_steerwifibh_target(ale, payload.target_bssid); } else { - log_lib_e("Invalid param: %s", param); - rbusValue_Release(value); - return RBUS_ERROR_INVALID_INPUT; + log_lib_e("Steer WiFi backhaul is not available"); + rbus_add_prop_str(out, "Status", "Error_Not_Ready"); + return RBUS_ERROR_INVALID_METHOD; } - rbusProperty_SetValue(property, value); - rbusValue_Release(value); + /* Create timer for timeout */ + map_dm_get_ale_timer_id(timer_id, ale, STEER_WIFIBH_TIMER_ID); + map_timer_register_callback_ms(timeout, timer_id, NULL, steerwifibh_async_cb); - return RBUS_ERROR_SUCCESS; + return RBUS_ERROR_ASYNC_RESPONSE; + +bail: + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; } /*####################################################################### -# ..DataElements.Network.Device.CACStatus.CACNonOccupancyChannel # +# ..DataElements.Network.Device.MultiAPDevice.Backhaul.Stats # ########################################################################*/ -static rbusError_t cacnonocc_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t bhstats_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { (void) handle; (void) opts; @@ -3046,11 +3953,15 @@ static rbusError_t cacnonocc_get_rbus(rbusHandle_t handle, rbusProperty_t proper char const* name = rbusProperty_GetName(property); char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; - unsigned int cac_idx; - unsigned int non_occ_idx; map_ale_info_t *ale; - map_cac_status_report_t *csr; - map_cac_non_occupancy_pair_t *nop; + map_ale_info_t *us_ale; + int16_t iface_group; + map_sta_info_t *sta; + map_sta_traffic_stats_t *ts; + map_sta_link_metrics_t *lm; + map_sta_ext_bss_metrics_t *ebm; + map_neighbor_link_metric_t *ulm; + char timestamp[MAX_TS_STR_LEN] = {0}; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -3060,30 +3971,111 @@ static rbusError_t cacnonocc_get_rbus(rbusHandle_t handle, rbusProperty_t proper return RBUS_ERROR_INVALID_INPUT; } - sscanf(name, "CACStatus.%d.CACNonOccupancyChannel.%d.%s", - &cac_idx, &non_occ_idx, param); + sscanf(name, "MultiAPDevice.Backhaul.Stats.%s", param); - /* There is only one CACStatus at a time */ - if (cac_idx != 1) { - log_lib_e("Invalid CAC status index: %d", cac_idx); - return RBUS_ERROR_INVALID_INPUT; - } - csr = ale->cac_status_report.valid ? &ale->cac_status_report : NULL; + sta = NULL; + ts = NULL; + lm = NULL; + ulm = NULL; + us_ale = NULL; + if (ale->is_local) { + /* no stats for local agent */ + iface_group = INTERFACE_TYPE_UNKNOWN; + } else { + iface_group = INTERFACE_TYPE_GROUP_GET(ale->upstream_iface_type); - if (csr && non_occ_idx > csr->non_occupancy_pairs_nr) { - log_lib_e("Invalid non occupancy channel index: %d", non_occ_idx); - return RBUS_ERROR_INVALID_INPUT; + us_ale = map_dm_get_ale(ale->upstream_al_mac); + if (us_ale == get_root_ale_node()) { + /* replace controller with (colocated) local agent */ + us_ale = map_dm_get_ale(map_cfg_get()->controller_cfg.local_agent_al_mac); + if (us_ale && !us_ale->is_local_colocated) { + /* no stats for not colocated local agent */ + us_ale = NULL; + } + } + } + if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { + if (us_ale) { + sta = map_dm_get_sta_from_ale(us_ale, ale->upstream_local_iface_mac); + ts = sta ? &sta->traffic_stats : NULL; + lm = (sta && sta->metrics) ? first_object(sta->metrics) : NULL; + } else { + log_lib_w("Upstream ale not found"); + } + } else if (iface_group == INTERFACE_TYPE_GROUP_ETHERNET) { + if (us_ale) { + ulm = &ale->upstream_link_metrics; + } } - nop = &csr->non_occupancy_pairs[non_occ_idx - 1]; rbusValue_Init(&value); - if (strcmp(param, "OpClass") == 0) { - rbusValue_SetUInt32(value, nop ? nop->op_class : 0); - } else if (strcmp(param, "Channel") == 0) { - rbusValue_SetUInt32(value, nop ? nop->channel : 0); - } else if (strcmp(param, "Seconds") == 0) { - rbusValue_SetUInt32(value, nop ? nop->seconds_remaining_non_occupancy_duration : 0); + if (strcmp(param, "BytesReceived") == 0) { + if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { + rbusValue_SetUInt64(value, ts ? ts->rx_bytes : 0); + } else { + rbusValue_SetUInt64(value, 0); + } + } else if (strcmp(param, "BytesSent") == 0) { + if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { + rbusValue_SetUInt64(value, ts ? ts->tx_bytes : 0); + } else { + rbusValue_SetUInt64(value, 0); + } + } else if (strcmp(param, "PacketsReceived") == 0) { + if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { + rbusValue_SetUInt64(value, ts ? ts->rx_packets : 0); + } else if (iface_group == INTERFACE_TYPE_GROUP_ETHERNET) { + rbusValue_SetUInt64(value, ulm ? ulm->rx_metric.packets_received : 0); + } else { + rbusValue_SetUInt64(value, 0); + } + } else if (strcmp(param, "PacketsSent") == 0) { + if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { + rbusValue_SetUInt64(value, ts ? ts->tx_packets : 0); + } else if (iface_group == INTERFACE_TYPE_GROUP_ETHERNET) { + rbusValue_SetUInt64(value, ulm ? ulm->tx_metric.transmitted_packets : 0); + } else { + rbusValue_SetUInt64(value, 0); + } + } else if (strcmp(param, "ErrorsReceived") == 0) { + if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { + rbusValue_SetUInt64(value, ts ? ts->rx_packet_errors : 0); + } else if (iface_group == INTERFACE_TYPE_GROUP_ETHERNET) { + rbusValue_SetUInt64(value, ulm ? ulm->rx_metric.packet_errors : 0); + } else { + rbusValue_SetUInt64(value, 0); + } + } else if (strcmp(param, "ErrorsSent") == 0) { + if (iface_group == INTERFACE_TYPE_GROUP_WLAN) { + rbusValue_SetUInt64(value, ts ? ts->tx_packet_errors : 0); + } else if (iface_group == INTERFACE_TYPE_GROUP_ETHERNET) { + rbusValue_SetUInt64(value, ulm ? ulm->tx_metric.packet_errors : 0); + } else { + rbusValue_SetUInt64(value, 0); + } + } else if (strcmp(param, "SignalStrength") == 0) { + /* Indeterminate other than Wi-Fi */ + rbusValue_SetUInt32(value, lm ? RSSI_TO_RCPI(lm->rssi) : 0); + } else if (strcmp(param, "LastDataDownlinkRate") == 0) { + if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { + rbusValue_SetUInt32(value, ebm->last_data_dl_rate); + } else if (ulm != NULL) { + rbusValue_SetUInt32(value, ulm->tx_metric.mac_throughput_capacity); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "LastDataUplinkRate") == 0) { + if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { + rbusValue_SetUInt32(value, ebm->last_data_ul_rate); + } else if (ulm != NULL) { + rbusValue_SetUInt32(value, ulm->tx_metric.mac_throughput_capacity); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "TimeStamp") == 0) { + get_timestamp_str(0, timestamp, sizeof(timestamp)); + rbusValue_SetString(value, timestamp); } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -3097,9 +4089,187 @@ static rbusError_t cacnonocc_get_rbus(rbusHandle_t handle, rbusProperty_t proper } /*####################################################################### -# ..DataElements.Network.Device.CACStatus.CACActiveChannel # +# Device.WiFi.DataElements.Network.Device.CACStatus # ########################################################################*/ -static rbusError_t cacactive_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t cacstatus_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int cac_idx; + map_ale_info_t *ale; + char timestamp[MAX_TS_STR_LEN] = {0}; + map_cac_status_report_t *csr; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + sscanf(name, "CACStatus.%d.%s", &cac_idx, param); + + /* There is only one CACStatus at a time */ + if (cac_idx != 1) { + log_lib_e("Invalid CAC status index: %d", cac_idx); + return RBUS_ERROR_INVALID_INPUT; + } + csr = ale->cac_status_report.valid ? &ale->cac_status_report : NULL; + + rbusValue_Init(&value); + + if (strcmp(param, "TimeStamp") == 0) { + get_timestamp_str(0, timestamp, sizeof(timestamp)); + rbusValue_SetString(value, timestamp); + } else if (strcmp(param, "CACAvailableChannelNumberOfEntries") == 0) { + rbusValue_SetUInt32(value, csr ? csr->available_pairs_nr : 0); + } else if (strcmp(param, "CACNonOccupancyChannelNumberOfEntries") == 0) { + rbusValue_SetUInt32(value, csr ? csr->non_occupancy_pairs_nr : 0); + } else if (strcmp(param, "CACActiveChannelNumberOfEntries") == 0) { + rbusValue_SetUInt32(value, csr ? csr->ongoing_cac_pairs_nr : 0); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# ..DataElements.Network.Device.CACStatus.CACAvailableChannel # +########################################################################*/ +static rbusError_t cacavail_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int cac_idx; + unsigned int avail_idx; + map_ale_info_t *ale; + map_cac_status_report_t *csr; + map_cac_available_pair_t *avp; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + sscanf(name, "CACStatus.%d.CACAvailableChannel.%d.%s", + &cac_idx, &avail_idx, param); + + /* There is only one CACStatus at a time */ + if (cac_idx != 1) { + log_lib_e("Invalid CAC status index: %d", cac_idx); + return RBUS_ERROR_INVALID_INPUT; + } + csr = ale->cac_status_report.valid ? &ale->cac_status_report : NULL; + + if (csr && avail_idx > csr->available_pairs_nr) { + log_lib_e("Invalid available channel index: %d", avail_idx); + return RBUS_ERROR_INVALID_INPUT; + } + avp = &csr->available_pairs[avail_idx - 1]; + + rbusValue_Init(&value); + + if (strcmp(param, "OpClass") == 0) { + rbusValue_SetUInt32(value, avp ? avp->op_class : 0); + } else if (strcmp(param, "Channel") == 0) { + rbusValue_SetUInt32(value, avp ? avp->channel : 0); + } else if (strcmp(param, "Minutes") == 0) { + rbusValue_SetUInt32(value, avp ? avp->minutes_since_cac_completion : 0); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# ..DataElements.Network.Device.CACStatus.CACNonOccupancyChannel # +########################################################################*/ +static rbusError_t cacnonocc_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int cac_idx; + unsigned int non_occ_idx; + map_ale_info_t *ale; + map_cac_status_report_t *csr; + map_cac_non_occupancy_pair_t *nop; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + sscanf(name, "CACStatus.%d.CACNonOccupancyChannel.%d.%s", + &cac_idx, &non_occ_idx, param); + + /* There is only one CACStatus at a time */ + if (cac_idx != 1) { + log_lib_e("Invalid CAC status index: %d", cac_idx); + return RBUS_ERROR_INVALID_INPUT; + } + csr = ale->cac_status_report.valid ? &ale->cac_status_report : NULL; + + if (csr && non_occ_idx > csr->non_occupancy_pairs_nr) { + log_lib_e("Invalid non occupancy channel index: %d", non_occ_idx); + return RBUS_ERROR_INVALID_INPUT; + } + nop = &csr->non_occupancy_pairs[non_occ_idx - 1]; + + rbusValue_Init(&value); + + if (strcmp(param, "OpClass") == 0) { + rbusValue_SetUInt32(value, nop ? nop->op_class : 0); + } else if (strcmp(param, "Channel") == 0) { + rbusValue_SetUInt32(value, nop ? nop->channel : 0); + } else if (strcmp(param, "Seconds") == 0) { + rbusValue_SetUInt32(value, nop ? nop->seconds_remaining_non_occupancy_duration : 0); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# ..DataElements.Network.Device.CACStatus.CACActiveChannel # +########################################################################*/ +static rbusError_t cacactive_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { (void) handle; (void) opts; @@ -3162,16 +4332,11 @@ static rbusError_t cacactive_get_rbus(rbusHandle_t handle, rbusProperty_t proper ########################################################################*/ static void dm_rbus_create_radio(map_radio_info_t *radio) { -#define MAX_RUID_STR_LEN 10 unsigned int ale_idx; unsigned int radio_idx; - unsigned int idx; - char radio_id[MAX_RUID_STR_LEN] = {0}; char tname[256] = {0}; rbusError_t rc; - log_lib_d("create radio: %s", radio->radio_id_str); - if (check_dev_dm_idx(radio->ale) < 0) { log_lib_e("invalid indexing for radio"); return; @@ -3183,45 +4348,16 @@ static void dm_rbus_create_radio(map_radio_info_t *radio) return; } - if (b64_encode(radio->radio_id, sizeof(mac_addr), radio_id, sizeof(radio_id)) < 0) { - log_lib_e("b64_encode failed"); - } - /* Register new radio to data model */ snprintf(tname, sizeof(tname), - "Device.WiFi.DataElements.Network.Device.%d.Radio.", ale_idx); + "Device.WiFi.DataElements.Network.Device.%d.Radio.", ale_idx + 1); - rc = rbusTable_registerRow(g_bus_handle, tname, radio_idx + 1, radio_id); + log_lib_d("Create row %s%d", tname, radio_idx + 1); + rc = rbusTable_registerRow(g_bus_handle, tname, radio_idx + 1, radio->radio_id_base64); if (rc != RBUS_ERROR_SUCCESS) { log_lib_e("Failed to create row[%d] for radio: %s", radio_idx + 1, radio->radio_id_str); } - - /* Register current operating class profiles to data model */ - snprintf(tname, sizeof(tname), - "Device.WiFi.DataElements.Network.Device.%d.Radio.%d." - "CurrentOperatingClassProfile.", ale_idx, radio_idx + 1); - - for (idx = 1; idx <= radio->curr_op_class_list.op_classes_nr; idx++) { - rc = rbusTable_registerRow(g_bus_handle, tname, idx, NULL); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to create row[%d] for curr_op_class", idx); - } - } - set_radio_dm_currops(radio, radio->curr_op_class_list.op_classes_nr); - - /* Register capable operating class profiles to data model */ - snprintf(tname, sizeof(tname), - "Device.WiFi.DataElements.Network.Device.%d.Radio.%d." - "Capabilities.CapableOperatingClassProfile.", ale_idx, radio_idx + 1); - - for (idx = 1; idx <= radio->cap_op_class_list.op_classes_nr; idx++) { - rc = rbusTable_registerRow(g_bus_handle, tname, idx, NULL); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to create row[%d] for cap_op_classes", idx); - } - } - set_radio_dm_capops(radio, radio->cap_op_class_list.op_classes_nr); } static void scan_send_results(map_radio_info_t *radio) @@ -3360,7 +4496,7 @@ static dm_nbss_table_t *dm_rbus_create_nbss( snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network." "Device.%d.Radio.%d.ScanResult.%d.OpClassScan.%d." "ChannelScan.%d.NeighborBSS.", - didx, ridx + 1, sidx, oidx, cidx); + didx + 1, ridx + 1, sidx, oidx, cidx); rc = rbusTable_registerRow(g_bus_handle, tname, nbss_idx, NULL); if (rc != RBUS_ERROR_SUCCESS) { @@ -3392,7 +4528,7 @@ static int dm_rbus_remove_nbss(dm_nbss_table_t *dm_nbss, bool rm_row, unsigned i snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network." "Device.%d.Radio.%d.ScanResult.%d.OpClassScan.%d." "ChannelScan.%d.NeighborBSS.%d", - didx, ridx + 1, sidx, oidx, cidx, nidx); + didx + 1, ridx + 1, sidx, oidx, cidx, nidx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { @@ -3449,7 +4585,7 @@ static dm_chan_table_t *dm_rbus_create_chan( snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network." "Device.%d.Radio.%d.ScanResult.%d.OpClassScan.%d.ChannelScan.", - didx, ridx + 1, sidx, oidx); + didx + 1, ridx + 1, sidx, oidx); rc = rbusTable_registerRow(g_bus_handle, tname, chan_idx, NULL); if (rc != RBUS_ERROR_SUCCESS) { @@ -3482,7 +4618,7 @@ static int dm_rbus_remove_chan(dm_chan_table_t *dm_chan, bool rm_row, unsigned i /* Delete channelscan row with index */ snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network." "Device.%d.Radio.%d.ScanResult.%d.OpClassScan.%d.ChannelScan.%d", - didx, ridx + 1, sidx, oidx, cidx); + didx + 1, ridx + 1, sidx, oidx, cidx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { @@ -3539,7 +4675,7 @@ static dm_opcl_table_t *dm_rbus_create_opcl( dm_opcl->id = opclass; snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network." - "Device.%d.Radio.%d.ScanResult.%d.OpClassScan.", didx, ridx + 1, sidx); + "Device.%d.Radio.%d.ScanResult.%d.OpClassScan.", didx + 1, ridx + 1, sidx); rc = rbusTable_registerRow(g_bus_handle, tname, opcl_idx, NULL); if (rc != RBUS_ERROR_SUCCESS) { @@ -3571,7 +4707,7 @@ static int dm_rbus_remove_opcl(dm_opcl_table_t *dm_opcl, bool rm_row, unsigned i /* Delete opclassscan row with index */ snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network." "Device.%d.Radio.%d.ScanResult.%d.OpClassScan.%d", - didx, ridx + 1, sidx, oidx); + didx + 1, ridx + 1, sidx, oidx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { @@ -3622,7 +4758,7 @@ static dm_scan_table_t *dm_rbus_create_scan( dm_scan->id = scan->last_scan_cnt; snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network." - "Device.%d.Radio.%d.ScanResult.", didx, ridx + 1); + "Device.%d.Radio.%d.ScanResult.", didx + 1, ridx + 1); rc = rbusTable_registerRow(g_bus_handle, tname, scan_idx, NULL); if (rc != RBUS_ERROR_SUCCESS) { @@ -3652,7 +4788,7 @@ static int dm_rbus_remove_scan(dm_scan_table_t *dm_scan, bool rm_row, unsigned i /* Delete scanres row with index */ snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network." - "Device.%d.Radio.%d.ScanResult.%d", didx, ridx + 1, sidx); + "Device.%d.Radio.%d.ScanResult.%d", didx + 1, ridx + 1, sidx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { @@ -3860,86 +4996,124 @@ static void scan_remove_results(map_radio_info_t *radio) free_list_iterator(it); } -static void dm_rbus_update_radio(map_radio_info_t *radio) +typedef void (radio_count_getter_cb)(map_radio_info_t *, unsigned int *); +typedef void (radio_count_setter_cb)(map_radio_info_t *, unsigned int); +typedef void (radio_alias_cb)(map_radio_info_t *, char *, size_t, unsigned int); + +static int radio_row_ops(map_radio_info_t *radio, radio_count_getter_cb getter_cb, + radio_count_setter_cb setter_cb, radio_alias_cb alias_cb, unsigned int new_count, + const char *rname, const char *lname) { unsigned int ale_idx; unsigned int radio_idx; - unsigned int idx; unsigned int curr_count; - unsigned int new_count; + unsigned int idx; char tname[256] = {0}; rbusError_t rc; - - log_lib_d("update radio[%d]: %s", radio->dm_idx, radio->radio_id_str); - - if (check_radio_dm_idx(radio) < 0) { - log_lib_e("Invalid indexing for radio"); - return; - } + int ret = 0; radio_idx = radio->dm_idx; ale_idx = radio->ale->dm_idx; - get_radio_dm_currops(radio, &curr_count); - new_count = radio->curr_op_class_list.op_classes_nr; + getter_cb(radio, &curr_count); if (curr_count < new_count) { - /* Add missing current operating class profiles */ + /* Add missing rows */ snprintf(tname, sizeof(tname), - "Device.WiFi.DataElements.Network.Device.%d.Radio.%d." - "CurrentOperatingClassProfile.", ale_idx, radio_idx + 1); + "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.%s.", + ale_idx + 1, radio_idx + 1, rname); curr_count = curr_count ? curr_count : 1; for (idx = curr_count; idx <= new_count; idx++) { - rc = rbusTable_registerRow(g_bus_handle, tname, idx, NULL); + if (alias_cb) { + char alias[8] = {0}; + + alias_cb(radio, alias, sizeof(alias), idx); + rc = rbusTable_registerRow(g_bus_handle, tname, idx, alias); + } else { + rc = rbusTable_registerRow(g_bus_handle, tname, idx, NULL); + } if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to create row[%d] for curr_op_class", idx); + log_lib_e("Failed to create row[%d] for %s", idx, lname); + ret = -1; } } } else { - /* Remove excess current operating class profiles */ + /* Remove excess rows */ for (idx = curr_count; idx > new_count; idx--) { snprintf(tname, sizeof(tname), - "Device.WiFi.DataElements.Network.Device.%d.Radio.%d." - "CurrentOperatingClassProfile.%d", ale_idx, radio_idx + 1, idx); + "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.%s.%d", + ale_idx + 1, radio_idx + 1, rname, idx); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to delete row[%d] for curr_op_class", idx); + log_lib_e("Failed to delete row[%d] for %s", idx, lname); + ret = -1; } } } - set_radio_dm_currops(radio, new_count); + setter_cb(radio, new_count); - get_radio_dm_capops(radio, &curr_count); - new_count = radio->cap_op_class_list.op_classes_nr; - if (curr_count < new_count) { - /* Add missing capable operating class profiles */ - snprintf(tname, sizeof(tname), - "Device.WiFi.DataElements.Network.Device.%d.Radio.%d." - "Capabilities.CapableOperatingClassProfile.", ale_idx, radio_idx + 1); + return ret; +} - curr_count = curr_count ? curr_count : 1; - for (idx = curr_count; idx <= new_count; idx++) { - rc = rbusTable_registerRow(g_bus_handle, tname, idx, NULL); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to create row[%d] for cap_op_class", idx); - } - } - } else { - /* Remove excess capable operating class profiles */ - for (idx = curr_count; idx > new_count; idx--) { - snprintf(tname, sizeof(tname), - "Device.WiFi.DataElements.Network.Device.%d.Radio.%d." - "Capabilities.CapableOperatingClassProfile.%d", - ale_idx, radio_idx + 1, idx); +static void get_disop_alias(map_radio_info_t *radio, char *buf, size_t buf_size, unsigned int idx) +{ + map_op_class_t *op_class; - rc = rbusTable_unregisterRow(g_bus_handle, tname); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to delete row[%d] for cap_op_class", idx); - } - } + op_class = &radio->disallowed_op_class_list.op_classes[idx - 1]; + snprintf(buf, buf_size, "%u", op_class->op_class); +} + +static void dm_rbus_update_radio(map_radio_info_t *radio) +{ + log_lib_d("update radio[%d]: %s", radio->dm_idx, radio->radio_id_str); + + if (check_radio_dm_idx(radio) < 0) { + log_lib_e("Invalid indexing for radio"); + return; } - set_radio_dm_capops(radio, new_count); + + /* Add or remove capable operating class profiles */ + radio_row_ops(radio, get_radio_dm_capops, set_radio_dm_capops, NULL, + radio->cap_op_class_list.op_classes_nr, + "Capabilities.CapableOperatingClassProfile", "cap_op_class"); + + if (radio->wifi7_caps) { + radio_row_ops(radio, get_radio_dm_apemlmr, set_radio_dm_apemlmr, NULL, + radio->wifi7_caps->ap_emlmr_records_nr, + "Capabilities.WiFi7APRole.EMLMRFreqSeparation", "ap_emlmr_fs"); + radio_row_ops(radio, get_radio_dm_apemlsr, set_radio_dm_apemlsr, NULL, + radio->wifi7_caps->ap_emlsr_records_nr, + "Capabilities.WiFi7APRole.EMLSRFreqSeparation", "ap_emlsr_fs"); + radio_row_ops(radio, get_radio_dm_apstr, set_radio_dm_apstr, NULL, + radio->wifi7_caps->ap_str_records_nr, + "Capabilities.WiFi7APRole.STRFreqSeparation", "ap_str_fs"); + radio_row_ops(radio, get_radio_dm_apnstr, set_radio_dm_apnstr, NULL, + radio->wifi7_caps->ap_nstr_records_nr, + "Capabilities.WiFi7APRole.NSTRFreqSeparation", "ap_nstr_fs"); + radio_row_ops(radio, get_radio_dm_bstaemlmr, set_radio_dm_bstaemlmr, NULL, + radio->wifi7_caps->bsta_emlmr_records_nr, + "Capabilities.WiFi7bSTARole.EMLMRFreqSeparation", "bsta_emlmr_fs"); + radio_row_ops(radio, get_radio_dm_bstaemlsr, set_radio_dm_bstaemlsr, NULL, + radio->wifi7_caps->bsta_emlsr_records_nr, + "Capabilities.WiFi7bSTARole.EMLSRFreqSeparation", "bsta_emlsr_fs"); + radio_row_ops(radio, get_radio_dm_bstastr, set_radio_dm_bstastr, NULL, + radio->wifi7_caps->bsta_str_records_nr, + "Capabilities.WiFi7bSTARole.STRFreqSeparation", "bsta_str_fs"); + radio_row_ops(radio, get_radio_dm_bstanstr, set_radio_dm_bstanstr, NULL, + radio->wifi7_caps->bsta_nstr_records_nr, + "Capabilities.WiFi7bSTARole.NSTRFreqSeparation", "bsta_nstr_fs"); + } + + /* Add or remove current operating class profiles */ + radio_row_ops(radio, get_radio_dm_currops, set_radio_dm_currops, NULL, + radio->curr_op_class_list.op_classes_nr, + "CurrentOperatingClassProfile", "curr_op_class"); + + /* Add missing disallowed operating class profiles */ + radio_row_ops(radio, get_radio_dm_disops, set_radio_dm_disops, get_disop_alias, + radio->disallowed_op_class_list.op_classes_nr, + "DisAllowedOpClassChannels", "disallowed_op_class"); if (radio->update_scan_results) { scan_send_results(radio); @@ -3956,8 +5130,6 @@ static void dm_rbus_remove_radio(map_radio_info_t *radio) char tname[256] = {0}; rbusError_t rc; - log_lib_d("remove radio[%d]: %s", radio->dm_idx, radio->radio_id_str); - if (radio->dm_removed) { return; } @@ -3973,8 +5145,9 @@ static void dm_rbus_remove_radio(map_radio_info_t *radio) /* Delete radio row with index */ snprintf(tname, sizeof(tname), "Device.WiFi.DataElements.Network.Device.%d.Radio.%d", - ale_idx, radio_idx + 1); + ale_idx + 1, radio_idx + 1); + log_lib_d("Delete row %s", tname); rc = rbusTable_unregisterRow(g_bus_handle, tname); if (rc != RBUS_ERROR_SUCCESS) { log_lib_e("Failed to delete row[%d] for radio: %s", @@ -3986,13 +5159,6 @@ static void dm_rbus_remove_radio(map_radio_info_t *radio) free_radio_dm_idx(ale_idx, radio_idx); - if (0) { - /* This may be enabled to shift indexes, if the data model - automatically keeps indexing consecutive. rbus does not support - it, tr069 forbids it */ - update_radio_idxs(radio->ale, radio); - } - /* Mark child objects are removed */ mark_bsss_removed(radio); } @@ -4100,6 +5266,8 @@ static rbusError_t radio_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusValue_SetUInt32(value, radio->last_scan_info.last_scan_cnt); } else if (strcmp(param, "UnassociatedSTANumberOfEntries") == 0) { rbusValue_SetUInt32(value, list_get_size(radio->unassoc_sta_list)); + } else if (strcmp(param, "DisAllowedOpClassChannelsNumberOfEntries") == 0) { + rbusValue_SetUInt32(value, list_get_size(radio->unassoc_sta_list)); } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -4199,6 +5367,10 @@ static void parse_channel_list(const char *buffer, unsigned int blen, map_channe char chbuf[4] = {0}; uint32_t channel; + if (!buffer) { + return; + } + pos = 0; while (blen) { if (*buffer >= '0' && *buffer <= '9') { @@ -4371,20 +5543,6 @@ static rbusError_t radio_chscan_rbus(rbusHandle_t handle, char const* method, rb return RBUS_ERROR_INVALID_METHOD; } -#if 0 //Do we still need to set a timer for timeout? - /* Prepare response timer */ - reply = calloc(1, sizeof(method_chscan_async_t)); - if (!reply) { - rbus_add_prop_str(out, "Status", "Error_Other"); - return RBUS_ERROR_OUT_OF_RESOURCES; - } - reply->ale_idx = ale->dm_idx; - reply->radio_idx = radio->dm_idx; - reply->async = async; - map_timer_register_callback(CHSAN_RESPONSE_TIMEOUT, - CHSAN_RESPONSE_TIMER_ID, reply, chscan_async_cb); -#endif - return RBUS_ERROR_ASYNC_RESPONSE; bail: @@ -4526,21 +5684,21 @@ static rbusError_t caps_get_rbus(rbusHandle_t handle, rbusProperty_t property, r } /*####################################################################### -# ..Network.Device.Radio.Capabilities.CapableOperatingClassProfile # +# ..WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi6APRole # ########################################################################*/ -static rbusError_t capop_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t wifi6ap_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { +#define MAX_MCSNSS_STR_LEN 17 (void) handle; (void) opts; rbusValue_t value; char const* name = rbusProperty_GetName(property); char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; - unsigned int cap_op_idx; map_ale_info_t *ale; map_radio_info_t *radio; - map_op_class_t *op_class; - char nonoper_str[MAP_CS_BUF_LEN] = {0}; + map_radio_wifi6_cap_data_t *wifi6_cap = NULL; + char buf[MAX_MCSNSS_STR_LEN] = {0}; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -4558,26 +5716,143 @@ static rbusError_t capop_get_rbus(rbusHandle_t handle, rbusProperty_t property, return RBUS_ERROR_INVALID_INPUT; } - sscanf(name, "Capabilities.CapableOperatingClassProfile.%d.%s", - &cap_op_idx, param); - - if (cap_op_idx > (unsigned int)(radio->cap_op_class_list.op_classes_nr + 1)) { - log_lib_e("Invalid capable operating class index: %d", cap_op_idx - 1); - return RBUS_ERROR_INVALID_INPUT; + if (radio->wifi6_caps) { + wifi6_cap = &radio->wifi6_caps->cap_data[MAP_AP_ROLE_AP]; } - op_class = &radio->cap_op_class_list.op_classes[cap_op_idx - 1]; + + sscanf(name, "Capabilities.WiFi6APRole.%s", param); rbusValue_Init(&value); - if (strcmp(param, "Class") == 0) { - rbusValue_SetUInt32(value, op_class->op_class); - } else if (strcmp(param, "MaxTxPower") == 0) { - rbusValue_SetInt32(value, op_class->eirp); - } else if (strcmp(param, "NonOperable") == 0) { - map_cs_to_string(&op_class->channels, ',', nonoper_str, sizeof(nonoper_str)); - rbusValue_SetString(value, nonoper_str); - } else if (strcmp(param, "NumberOfNonOperChan") == 0) { - rbusValue_SetUInt32(value, op_class->channels.nr); + if (strcmp(param, "HE160") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->he160); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "HE8080") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->he8080); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MCSNSS") == 0) { + if (wifi6_cap && wifi6_cap->mcs_nss_nr) { + if (b64_encode(wifi6_cap->mcs_nss, wifi6_cap->mcs_nss_nr, buf, sizeof(buf)) < 0) { + log_lib_e("b64_encode failed"); + } + rbusValue_SetString(value, buf); + } else { + rbusValue_SetString(value, ""); + } + } else if (strcmp(param, "SUBeamformer") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->su_beamformer); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "SUBeamformee") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->su_beamformee); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MUBeamformer") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->mu_beamformer); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "Beamformee80orLess") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->beamformee_sts_l80); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "BeamformeeAbove80") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->beamformee_sts_g80); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "ULMUMIMO") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->ul_mu_mimo); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "ULOFDMA") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->ul_ofdma); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "DLOFDMA") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->dl_ofdma); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MaxDLMUMIMO") == 0) { + if (wifi6_cap) { + rbusValue_SetUInt32(value, wifi6_cap->max_dl_mu_mimo_tx); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "MaxULMUMIMO") == 0) { + if (wifi6_cap) { + rbusValue_SetUInt32(value, wifi6_cap->max_ul_mu_mimo_rx); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "MaxDLOFDMA") == 0) { + if (wifi6_cap) { + rbusValue_SetUInt32(value, wifi6_cap->max_dl_ofdma_tx); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "MaxULOFDMA") == 0) { + if (wifi6_cap) { + rbusValue_SetUInt32(value, wifi6_cap->max_ul_ofdma_rx); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "RTS") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->rts); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MURTS") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->mu_rts); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MultiBSSID") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->multi_bssid); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MUEDCA") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->mu_edca); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "TWTRequestor") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->twt_requester); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "TWTResponder") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->twt_responder); + } else { + rbusValue_SetBoolean(value, false); + } } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -4591,23 +5866,21 @@ static rbusError_t capop_get_rbus(rbusHandle_t handle, rbusProperty_t property, } /*####################################################################### -# ..DataElements.Network.Device.Radio.CurrentOperatingClassProfile # +# ..WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi6bSTARole # ########################################################################*/ -static rbusError_t currop_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t wifi6bsta_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { -#define MAX_CH_STR_LEN 6 +#define MAX_MCSNSS_STR_LEN 17 (void) handle; (void) opts; rbusValue_t value; char const* name = rbusProperty_GetName(property); char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; - unsigned int curr_op_idx; map_ale_info_t *ale; map_radio_info_t *radio; - map_op_class_t *op_class; - char ch_str[MAX_CH_STR_LEN] = {0}; - char timestamp[MAX_TS_STR_LEN] = {0}; + map_radio_wifi6_cap_data_t *wifi6_cap = NULL; + char buf[MAX_MCSNSS_STR_LEN] = {0}; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -4625,31 +5898,143 @@ static rbusError_t currop_get_rbus(rbusHandle_t handle, rbusProperty_t property, return RBUS_ERROR_INVALID_INPUT; } - sscanf(name, "CurrentOperatingClassProfile.%d.%s", &curr_op_idx, param); - - if (curr_op_idx > (unsigned int)(radio->curr_op_class_list.op_classes_nr + 1)) { - log_lib_e("Invalid current operating class index: %d", curr_op_idx - 1); - return RBUS_ERROR_INVALID_INPUT; + if (radio->wifi6_caps) { + wifi6_cap = &radio->wifi6_caps->cap_data[MAP_AP_ROLE_STA]; } - op_class = &radio->curr_op_class_list.op_classes[curr_op_idx - 1]; + + sscanf(name, "Capabilities.WiFi6bSTARole.%s", param); rbusValue_Init(&value); - if (strcmp(param, "Class") == 0) { - rbusValue_SetUInt32(value, op_class->op_class); - } else if (strcmp(param, "Channel") == 0) { - if (op_class->channels.nr == 1) { - map_cs_to_string(&op_class->channels, ',', ch_str, sizeof(ch_str)); - rbusValue_SetUInt32(value, atoi(ch_str)); + if (strcmp(param, "HE160") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->he160); } else { - log_lib_w("Internal error"); - rbusValue_SetUInt32(value, radio->current_op_channel); + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "HE8080") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->he8080); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MCSNSS") == 0) { + if (wifi6_cap && wifi6_cap->mcs_nss_nr) { + if (b64_encode(wifi6_cap->mcs_nss, wifi6_cap->mcs_nss_nr, buf, sizeof(buf)) < 0) { + log_lib_e("b64_encode failed"); + } + rbusValue_SetString(value, buf); + } else { + rbusValue_SetString(value, ""); + } + } else if (strcmp(param, "SUBeamformer") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->su_beamformer); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "SUBeamformee") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->su_beamformee); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MUBeamformer") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->mu_beamformer); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "Beamformee80orLess") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->beamformee_sts_l80); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "BeamformeeAbove80") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->beamformee_sts_g80); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "ULMUMIMO") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->ul_mu_mimo); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "ULOFDMA") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->ul_ofdma); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "DLOFDMA") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->dl_ofdma); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MaxDLMUMIMO") == 0) { + if (wifi6_cap) { + rbusValue_SetUInt32(value, wifi6_cap->max_dl_mu_mimo_tx); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "MaxULMUMIMO") == 0) { + if (wifi6_cap) { + rbusValue_SetUInt32(value, wifi6_cap->max_ul_mu_mimo_rx); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "MaxDLOFDMA") == 0) { + if (wifi6_cap) { + rbusValue_SetUInt32(value, wifi6_cap->max_dl_ofdma_tx); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "MaxULOFDMA") == 0) { + if (wifi6_cap) { + rbusValue_SetUInt32(value, wifi6_cap->max_ul_ofdma_rx); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "RTS") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->rts); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MURTS") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->mu_rts); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MultiBSSID") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->multi_bssid); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "MUEDCA") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->mu_edca); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "TWTRequestor") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->twt_requester); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "TWTResponder") == 0) { + if (wifi6_cap) { + rbusValue_SetBoolean(value, wifi6_cap->twt_responder); + } else { + rbusValue_SetBoolean(value, false); } - } else if (strcmp(param, "TxPower") == 0) { - rbusValue_SetInt32(value, op_class->eirp); - } else if (strcmp(param, "TimeStamp") == 0) { - get_timestamp_str(0, timestamp, sizeof(timestamp)); - rbusValue_SetString(value, timestamp); } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -4663,9 +6048,9 @@ static rbusError_t currop_get_rbus(rbusHandle_t handle, rbusProperty_t property, } /*####################################################################### -# Device.WiFi.DataElements.Network.Device.Radio.ScanResult # +# ..WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7APRole # ########################################################################*/ -static rbusError_t scanres_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t wifi7ap_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { (void) handle; (void) opts; @@ -4673,11 +6058,9 @@ static rbusError_t scanres_get_rbus(rbusHandle_t handle, rbusProperty_t property char const* name = rbusProperty_GetName(property); char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; - unsigned int scan_idx; map_ale_info_t *ale; map_radio_info_t *radio; - map_scan_info_t *si; - dm_scan_table_t *dm_scan; + map_radio_wifi7_caps_t *wifi7_caps; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -4695,23 +6078,60 @@ static rbusError_t scanres_get_rbus(rbusHandle_t handle, rbusProperty_t property return RBUS_ERROR_INVALID_INPUT; } - sscanf(name, "ScanResult.%d.%s", &scan_idx, param); - - dm_scan = get_radio_dm_scan(radio, scan_idx); - if (dm_scan == NULL) { - log_lib_e("Invalid scan index: %d", scan_idx); - return RBUS_ERROR_INVALID_INPUT; - } + wifi7_caps = radio->wifi7_caps; - si = &radio->last_scan_info; + sscanf(name, "Capabilities.WiFi7APRole.%s", param); rbusValue_Init(&value); - if (strcmp(param, "TimeStamp") == 0) { - /* Copy ts to dm_scan */ - rbusValue_SetString(value, (char *)si->last_scan_ts); - } else if (strcmp(param, "OpClassScanNumberOfEntries") == 0) { - rbusValue_SetUInt32(value, dm_scan->opcl_cnt); + if (strcmp(param, "EMLMRSupport") == 0) { + if (wifi7_caps) { + rbusValue_SetBoolean(value, wifi7_caps->ap_mld_modes.emlmr); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "EMLSRSupport") == 0) { + if (wifi7_caps) { + rbusValue_SetBoolean(value, wifi7_caps->ap_mld_modes.emlsr); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "STRSupport") == 0) { + if (wifi7_caps) { + rbusValue_SetBoolean(value, wifi7_caps->ap_mld_modes.str); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "NSTRSupport") == 0) { + if (wifi7_caps) { + rbusValue_SetBoolean(value, wifi7_caps->ap_mld_modes.nstr); + } else { + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "EMLMRFreqSeparationNumberOfEntries") == 0) { + if (wifi7_caps) { + rbusValue_SetUInt32(value, wifi7_caps->ap_emlmr_records_nr); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "EMLSRFreqSeparationNumberOfEntries") == 0) { + if (wifi7_caps) { + rbusValue_SetUInt32(value, wifi7_caps->ap_emlsr_records_nr); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "STRFreqSeparationNumberOfEntries") == 0) { + if (wifi7_caps) { + rbusValue_SetUInt32(value, wifi7_caps->ap_str_records_nr); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "NSTRFreqSeparationNumberOfEntries") == 0) { + if (wifi7_caps) { + rbusValue_SetUInt32(value, wifi7_caps->ap_nstr_records_nr); + } else { + rbusValue_SetUInt32(value, 0); + } } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -4725,22 +6145,23 @@ static rbusError_t scanres_get_rbus(rbusHandle_t handle, rbusProperty_t property } /*####################################################################### -# ..DataElements.Network.Device.Radio.ScanResult.OpClassScan # +# ..Device.Radio.Capabilities.WiFi7APRole.EMLMRFreqSeparation # ########################################################################*/ -static rbusError_t opclscan_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t apemlmr_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { +#define MAX_RUID_STR_LEN 10 (void) handle; (void) opts; rbusValue_t value; char const* name = rbusProperty_GetName(property); char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; - unsigned int scan_idx; - unsigned int opcl_idx; + unsigned int emlmrfs_idx; map_ale_info_t *ale; map_radio_info_t *radio; - dm_scan_table_t *dm_scan; - dm_opcl_table_t *dm_opcl; + map_radio_wifi7_caps_t *wifi7_caps; + map_radio_wifi7_records_t *records; + char buf[MAX_RUID_STR_LEN] = {0}; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -4758,27 +6179,30 @@ static rbusError_t opclscan_get_rbus(rbusHandle_t handle, rbusProperty_t propert return RBUS_ERROR_INVALID_INPUT; } - sscanf(name, "ScanResult.%d.OpClassScan.%d.%s", - &scan_idx, &opcl_idx, param); + sscanf(name, "Capabilities.WiFi7APRole.EMLMRFreqSeparation.%d.%s", + &emlmrfs_idx, param); - dm_scan = get_radio_dm_scan(radio, scan_idx); - if (dm_scan == NULL) { - log_lib_e("Invalid scan index: %d", scan_idx); + wifi7_caps = radio->wifi7_caps; + if (!wifi7_caps || emlmrfs_idx > wifi7_caps->ap_emlmr_records_nr) { + log_lib_e("Invalid wifi7ap emlmrfs index: %d", emlmrfs_idx); return RBUS_ERROR_INVALID_INPUT; } - dm_opcl = get_dm_scan_opcl(dm_scan, opcl_idx); - if (dm_opcl == NULL) { - log_lib_e("Invalid op class index: %d", opcl_idx); + records = &wifi7_caps->ap_emlmr_records[emlmrfs_idx]; + if (!records) { + log_lib_e("Invalid wifi7ap emlmrfs index: %d", emlmrfs_idx); return RBUS_ERROR_INVALID_INPUT; } rbusValue_Init(&value); - if (strcmp(param, "OperatingClass") == 0) { - rbusValue_SetUInt32(value, dm_opcl->id); - } else if (strcmp(param, "ChannelScanNumberOfEntries") == 0) { - rbusValue_SetUInt32(value, dm_opcl->chan_cnt); + if (strcmp(param, "RUID") == 0) { + if (b64_encode(records->ruid, sizeof(mac_addr), buf, sizeof(buf)) < 0) { + log_lib_e("b64_encode failed"); + } + rbusValue_SetString(value, buf); + } else if (strcmp(param, "FreqSeparation") == 0) { + rbusValue_SetUInt32(value, records->freq_separation); } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -4792,25 +6216,23 @@ static rbusError_t opclscan_get_rbus(rbusHandle_t handle, rbusProperty_t propert } /*####################################################################### -# ..Network.Device.Radio.ScanResult.OpClassScan.ChannelScan # +# ..Device.Radio.Capabilities.WiFi7APRole.EMLSRFreqSeparation # ########################################################################*/ -static rbusError_t chscan_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t apemlsr_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { +#define MAX_RUID_STR_LEN 10 (void) handle; (void) opts; rbusValue_t value; char const* name = rbusProperty_GetName(property); char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; - unsigned int scan_idx; - unsigned int opcl_idx; - unsigned int chan_idx; + unsigned int emlsrfs_idx; map_ale_info_t *ale; map_radio_info_t *radio; - dm_scan_table_t *dm_scan; - dm_opcl_table_t *dm_opcl; - dm_chan_table_t *dm_chan; - map_scan_result_t *sr; + map_radio_wifi7_caps_t *wifi7_caps; + map_radio_wifi7_records_t *records; + char buf[MAX_RUID_STR_LEN] = {0}; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -4828,45 +6250,30 @@ static rbusError_t chscan_get_rbus(rbusHandle_t handle, rbusProperty_t property, return RBUS_ERROR_INVALID_INPUT; } - sscanf(name, "ScanResult.%d.OpClassScan.%d.ChannelScan.%d.%s", - &scan_idx, &opcl_idx, &chan_idx, param); - - dm_scan = get_radio_dm_scan(radio, scan_idx); - if (dm_scan == NULL) { - log_lib_e("Invalid scan index: %d", scan_idx); - return RBUS_ERROR_INVALID_INPUT; - } - - dm_opcl = get_dm_scan_opcl(dm_scan, opcl_idx); - if (dm_opcl == NULL) { - log_lib_e("Invalid op class index: %d", opcl_idx); - return RBUS_ERROR_INVALID_INPUT; - } + sscanf(name, "Capabilities.WiFi7APRole.EMLSRFreqSeparation.%d.%s", + &emlsrfs_idx, param); - dm_chan = get_dm_opcl_chan(dm_opcl, chan_idx); - if (dm_chan == NULL) { - log_lib_e("Invalid channel index: %d", chan_idx); + wifi7_caps = radio->wifi7_caps; + if (!wifi7_caps || emlsrfs_idx > wifi7_caps->ap_emlsr_records_nr) { + log_lib_e("Invalid wifi7ap emlsrfs index: %d", emlsrfs_idx); return RBUS_ERROR_INVALID_INPUT; } - sr = map_dm_rbus_get_scanres(radio, dm_scan->id, dm_opcl->id, dm_chan->id); - if (sr == NULL) { - log_lib_e("Invalid scan id"); + records = &wifi7_caps->ap_emlsr_records[emlsrfs_idx]; + if (!records) { + log_lib_e("Invalid wifi7ap emlsrfs index: %d", emlsrfs_idx); return RBUS_ERROR_INVALID_INPUT; } rbusValue_Init(&value); - if (strcmp(param, "Channel") == 0) { - rbusValue_SetUInt32(value, dm_chan->id); - } else if (strcmp(param, "TimeStamp") == 0) { - rbusValue_SetString(value, (char *)sr->channel_scan_ts); - } else if (strcmp(param, "Utilization") == 0) { - rbusValue_SetUInt32(value, 0); - } else if (strcmp(param, "Noise") == 0) { - rbusValue_SetInt32(value, 0); - } else if (strcmp(param, "NeighborBSSNumberOfEntries") == 0) { - rbusValue_SetInt32(value, dm_chan->nbss_cnt); + if (strcmp(param, "RUID") == 0) { + if (b64_encode(records->ruid, sizeof(mac_addr), buf, sizeof(buf)) < 0) { + log_lib_e("b64_encode failed"); + } + rbusValue_SetString(value, buf); + } else if (strcmp(param, "FreqSeparation") == 0) { + rbusValue_SetUInt32(value, records->freq_separation); } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -4880,27 +6287,23 @@ static rbusError_t chscan_get_rbus(rbusHandle_t handle, rbusProperty_t property, } /*####################################################################### -# ..Device.Radio.ScanResult.OpClassScan.ChannelScan.NeighborBSS # +# ..Device.Radio.Capabilities.WiFi7APRole.STRFreqSeparation # ########################################################################*/ -static rbusError_t nbss_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t apstr_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { +#define MAX_RUID_STR_LEN 10 (void) handle; (void) opts; rbusValue_t value; char const* name = rbusProperty_GetName(property); char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; - unsigned int scan_idx; - unsigned int opcl_idx; - unsigned int chan_idx; - unsigned int nbss_idx; + unsigned int strfs_idx; map_ale_info_t *ale; map_radio_info_t *radio; - dm_scan_table_t *dm_scan; - dm_opcl_table_t *dm_opcl; - dm_chan_table_t *dm_chan; - dm_nbss_table_t *dm_nbss; - map_channel_scan_neighbor_t *ni; + map_radio_wifi7_caps_t *wifi7_caps; + map_radio_wifi7_records_t *records; + char buf[MAX_RUID_STR_LEN] = {0}; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -4918,62 +6321,30 @@ static rbusError_t nbss_get_rbus(rbusHandle_t handle, rbusProperty_t property, r return RBUS_ERROR_INVALID_INPUT; } - sscanf(name, "ScanResult.%d.OpClassScan.%d.ChannelScan.%d.NeighborBSS.%d.%s", - &scan_idx, &opcl_idx, &chan_idx, &nbss_idx, param); - - dm_scan = get_radio_dm_scan(radio, scan_idx); - if (dm_scan == NULL) { - log_lib_e("Invalid scan index: %d", scan_idx); - return RBUS_ERROR_INVALID_INPUT; - } - - dm_opcl = get_dm_scan_opcl(dm_scan, opcl_idx); - if (dm_opcl == NULL) { - log_lib_e("Invalid op class index: %d", opcl_idx); - return RBUS_ERROR_INVALID_INPUT; - } - - dm_chan = get_dm_opcl_chan(dm_opcl, chan_idx); - if (dm_chan == NULL) { - log_lib_e("Invalid channel index: %d", chan_idx); - return RBUS_ERROR_INVALID_INPUT; - } + sscanf(name, "Capabilities.WiFi7APRole.STRFreqSeparation.%d.%s", + &strfs_idx, param); - dm_nbss = get_dm_chan_nbss(dm_chan, nbss_idx); - if (dm_nbss == NULL) { - log_lib_e("Invalid neighbour info index: %d", nbss_idx); + wifi7_caps = radio->wifi7_caps; + if (!wifi7_caps || strfs_idx > wifi7_caps->ap_str_records_nr) { + log_lib_e("Invalid wifi7ap strfs index: %d", strfs_idx); return RBUS_ERROR_INVALID_INPUT; } - ni = map_dm_rbus_get_nb_info(radio, dm_scan->id, dm_opcl->id, - dm_chan->id, dm_nbss->id); - if (ni == NULL) { - log_lib_e("Invalid scan credentials"); + records = &wifi7_caps->ap_str_records[strfs_idx]; + if (!records) { + log_lib_e("Invalid wifi7ap strfs index: %d", strfs_idx); return RBUS_ERROR_INVALID_INPUT; } rbusValue_Init(&value); - if (strcmp(param, "BSSID") == 0) { - rbusValue_SetString(value, mac_string(ni->bssid)); - } else if (strcmp(param, "SSID") == 0) { - rbusValue_SetString(value, (char *)ni->ssid); - } else if (strcmp(param, "SignalStre") == 0) { - rbusValue_SetInt32(value, ni->rcpi); - } else if (strcmp(param, "ChannelBan") == 0) { - rbusValue_SetString(value, (char *)ni->ch_bw); - } else if (strcmp(param, "ChannelUti") == 0) { - if (ni->bss_load_elem_present == 1) { - rbusValue_SetUInt32(value, ni->channel_utilization); - } else { - rbusValue_SetUInt32(value, 0); - } - } else if (strcmp(param, "StationCou") == 0) { - if (ni->bss_load_elem_present == 1) { - rbusValue_SetUInt32(value, ni->stas_nr); - } else { - rbusValue_SetUInt32(value, 0); + if (strcmp(param, "RUID") == 0) { + if (b64_encode(records->ruid, sizeof(mac_addr), buf, sizeof(buf)) < 0) { + log_lib_e("b64_encode failed"); } + rbusValue_SetString(value, buf); + } else if (strcmp(param, "FreqSeparation") == 0) { + rbusValue_SetUInt32(value, records->freq_separation); } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -4987,99 +6358,80 @@ static rbusError_t nbss_get_rbus(rbusHandle_t handle, rbusProperty_t property, r } /*####################################################################### -# Device.WiFi.DataElements.Network.Device.Radio.BSS # +# ..Device.Radio.Capabilities.WiFi7APRole.NSTRFreqSeparation # ########################################################################*/ -static void dm_rbus_create_bss(map_bss_info_t *bss) +static rbusError_t apnstr_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { - unsigned int ale_idx; - unsigned int radio_idx; - unsigned int bss_idx; - char tname[256] = {0}; - rbusError_t rc; - - log_lib_d("create bss: %s", bss->bssid_str); - - if (check_radio_dm_idx(bss->radio) < 0) { - log_lib_e("Invalid indexing for bss"); - return; - } - - radio_idx = bss->radio->dm_idx; - ale_idx = bss->radio->ale->dm_idx; - if (get_bss_dm_idx(ale_idx, radio_idx, bss, &bss_idx) < 0) { - log_lib_e("No free index for bss: %s", bss->bssid_str); - return; - } - - snprintf(tname, sizeof(tname) - 1, - "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.BSS.", - ale_idx, radio_idx + 1); +#define MAX_RUID_STR_LEN 10 + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int nstrfs_idx; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_radio_wifi7_caps_t *wifi7_caps; + map_radio_wifi7_records_t *records; + char buf[MAX_RUID_STR_LEN] = {0}; - rc = rbusTable_registerRow(g_bus_handle, tname, bss_idx + 1, bss->bssid_str); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to create row[%d] for bss: %s", - bss_idx + 1, bss->bssid_str); + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - return; -} - -static void dm_rbus_update_bss(map_bss_info_t *bss) -{ - log_lib_d("update bss[%d]: %s", bss->dm_idx, bss->bssid_str); - - if (check_bss_dm_idx(bss) < 0) { - log_lib_e("Invalid indexing for bss"); + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } -} -static void dm_rbus_remove_bss(map_bss_info_t *bss) -{ - unsigned int ale_idx; - unsigned int radio_idx; - unsigned int bss_idx; - char tname[256] = {0}; - rbusError_t rc; - - log_lib_d("remove bss[%d]: %s", bss->dm_idx, bss->bssid_str); + sscanf(name, "Capabilities.WiFi7APRole.NSTRFreqSeparation.%d.%s", + &nstrfs_idx, param); - if (bss->dm_removed) { - return; + wifi7_caps = radio->wifi7_caps; + if (!wifi7_caps || nstrfs_idx > wifi7_caps->ap_nstr_records_nr) { + log_lib_e("Invalid wifi7ap nstrfs index: %d", nstrfs_idx); + return RBUS_ERROR_INVALID_INPUT; } - if (check_bss_dm_idx(bss) < 0) { - log_lib_e("Invalid indexing for bss"); - return; + records = &wifi7_caps->ap_nstr_records[nstrfs_idx]; + if (!records) { + log_lib_e("Invalid wifi7ap nstrfs index: %d", nstrfs_idx); + return RBUS_ERROR_INVALID_INPUT; } - bss_idx = bss->dm_idx; - radio_idx = bss->radio->dm_idx; - ale_idx = bss->radio->ale->dm_idx; - - snprintf(tname, sizeof(tname) - 1, - "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.BSS.%d", - ale_idx, radio_idx + 1, bss_idx + 1); + rbusValue_Init(&value); - rc = rbusTable_unregisterRow(g_bus_handle, tname); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to delete row[%d] for bss: %s", - bss_idx + 1, bss->bssid_str); + if (strcmp(param, "RUID") == 0) { + if (b64_encode(records->ruid, sizeof(mac_addr), buf, sizeof(buf)) < 0) { + log_lib_e("b64_encode failed"); + } + rbusValue_SetString(value, buf); + } else if (strcmp(param, "FreqSeparation") == 0) { + rbusValue_SetUInt32(value, records->freq_separation); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; } - free_bss_dm_idx(ale_idx, radio_idx, bss_idx); - - if (0) { - /* This may be enabled to shift indexes, if the data model - automatically keeps indexing consecutive. rbus does not - support it, tr069 forbids it */ - update_bss_idxs(bss->radio, bss); - } + rbusProperty_SetValue(property, value); + rbusValue_Release(value); - /* Mark child objects are removed */ - mark_stas_removed(bss); + return RBUS_ERROR_SUCCESS; } -static rbusError_t bss_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +/*####################################################################### +# ..WiFi.DataElements.Network.Device.Radio.Capabilities.WiFi7bSTARole # +########################################################################*/ +static rbusError_t wifi7bsta_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { (void) handle; (void) opts; @@ -5089,12 +6441,7 @@ static rbusError_t bss_get_rbus(rbusHandle_t handle, rbusProperty_t property, rb bool is_num; map_ale_info_t *ale; map_radio_info_t *radio; - map_bss_info_t *bss; - uint32_t last_change; - char timestamp[MAX_TS_STR_LEN] = {0}; - map_profile_cfg_t *profile = NULL; - map_controller_cfg_t *cfg; - unsigned int i; + map_radio_wifi7_caps_t *wifi7_caps; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -5112,76 +6459,60 @@ static rbusError_t bss_get_rbus(rbusHandle_t handle, rbusProperty_t property, rb return RBUS_ERROR_INVALID_INPUT; } - name += sizeof(DM_BSS_PREFIX); - name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); - bss = dm_get_bss(radio, param, is_num); - if (bss == NULL) { - log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); - return RBUS_ERROR_INVALID_INPUT; - } + wifi7_caps = radio->wifi7_caps; - sscanf(name, "%s", param); + sscanf(name, "Capabilities.WiFi7bSTARole.%s", param); rbusValue_Init(&value); - if (strcmp(param, "BSSID") == 0) { - rbusValue_SetString(value, bss->bssid_str); - } else if (strcmp(param, "SSID") == 0) { - rbusValue_SetString(value, bss->ssid); - } else if (strcmp(param, "Enabled") == 0) { - rbusValue_SetBoolean(value, is_bss_active(bss->state) ? true : false); - } else if (strcmp(param, "LastChange") == 0) { - last_change = acu_get_timestamp_sec() - bss->change_ts; - rbusValue_SetUInt32(value, last_change); - } else if (strcmp(param, "TimeStamp") == 0) { - get_timestamp_str(0, timestamp, sizeof(timestamp)); - rbusValue_SetString(value, timestamp); - } else if (strcmp(param, "UnicastBytesReceived") == 0) { - rbusValue_SetUInt64(value, bss->extended_metrics.ucast_bytes_rx); - } else if (strcmp(param, "UnicastBytesSent") == 0) { - rbusValue_SetUInt64(value, bss->extended_metrics.ucast_bytes_tx); - } else if (strcmp(param, "MulticastBytesReceived") == 0) { - rbusValue_SetUInt64(value, bss->extended_metrics.mcast_bytes_rx); - } else if (strcmp(param, "MulticastBytesSent") == 0) { - rbusValue_SetUInt64(value, bss->extended_metrics.mcast_bytes_tx); - } else if (strcmp(param, "BroadcastBytesReceived") == 0) { - rbusValue_SetUInt64(value, bss->extended_metrics.bcast_bytes_rx); - } else if (strcmp(param, "BroadcastBytesSent") == 0) { - rbusValue_SetUInt64(value, bss->extended_metrics.bcast_bytes_tx); - } else if (strcmp(param, "BackhaulUse") == 0) { - rbusValue_SetBoolean(value, (bss->type & MAP_BACKHAUL_BSS) ? true : false); - } else if (strcmp(param, "FronthaulUse") == 0) { - rbusValue_SetBoolean(value, (bss->type & MAP_FRONTHAUL_BSS) ? true : false); - } else if (strcmp(param, "FronthaulAKMsAllowed") == 0) { - cfg = &map_cfg_get()->controller_cfg; - for (i = 0; i < cfg->num_profiles; i++) { - if (strcmp(bss->ssid, cfg->profiles[i].bss_ssid) == 0) { - profile = &cfg->profiles[i]; - break; - } + if (strcmp(param, "EMLMRSupport") == 0) { + if (wifi7_caps) { + rbusValue_SetBoolean(value, wifi7_caps->bsta_mld_modes.emlmr); + } else { + rbusValue_SetBoolean(value, false); } - if (profile && (profile->bss_state & MAP_FRONTHAUL_BSS)) { - rbusValue_SetString(value, - get_auth_mode_str(profile->supported_auth_modes)); + } else if (strcmp(param, "EMLSRSupport") == 0) { + if (wifi7_caps) { + rbusValue_SetBoolean(value, wifi7_caps->bsta_mld_modes.emlsr); } else { - rbusValue_SetString(value, "none"); + rbusValue_SetBoolean(value, false); } - } else if (strcmp(param, "BackhaulAKMsAllowed") == 0) { - cfg = &map_cfg_get()->controller_cfg; - for (i = 0; i < cfg->num_profiles; i++) { - if (strcmp(bss->ssid, cfg->profiles[i].bss_ssid) == 0) { - profile = &cfg->profiles[i]; - break; - } + } else if (strcmp(param, "STRSupport") == 0) { + if (wifi7_caps) { + rbusValue_SetBoolean(value, wifi7_caps->bsta_mld_modes.str); + } else { + rbusValue_SetBoolean(value, false); } - if (profile && (profile->bss_state & MAP_BACKHAUL_BSS)) { - rbusValue_SetString(value, - get_auth_mode_str(profile->supported_auth_modes)); + } else if (strcmp(param, "NSTRSupport") == 0) { + if (wifi7_caps) { + rbusValue_SetBoolean(value, wifi7_caps->bsta_mld_modes.nstr); } else { - rbusValue_SetString(value, "none"); + rbusValue_SetBoolean(value, false); + } + } else if (strcmp(param, "EMLMRFreqSeparationNumberOfEntries") == 0) { + if (wifi7_caps) { + rbusValue_SetUInt32(value, wifi7_caps->bsta_emlmr_records_nr); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "EMLSRFreqSeparationNumberOfEntries") == 0) { + if (wifi7_caps) { + rbusValue_SetUInt32(value, wifi7_caps->bsta_emlsr_records_nr); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "STRFreqSeparationNumberOfEntries") == 0) { + if (wifi7_caps) { + rbusValue_SetUInt32(value, wifi7_caps->bsta_str_records_nr); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "NSTRFreqSeparationNumberOfEntries") == 0) { + if (wifi7_caps) { + rbusValue_SetUInt32(value, wifi7_caps->bsta_nstr_records_nr); + } else { + rbusValue_SetUInt32(value, 0); } - } else if (strcmp(param, "STANumberOfEntries") == 0) { - rbusValue_SetUInt32(value, bss->stas_nr); } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -5194,422 +6525,2563 @@ static rbusError_t bss_get_rbus(rbusHandle_t handle, rbusProperty_t property, rb return RBUS_ERROR_SUCCESS; } -static rbusError_t rbus_method_client_assoc_control(rbusHandle_t handle, char const* method, rbusObject_t in, rbusObject_t out, rbusMethodAsyncHandle_t async) +/*####################################################################### +# ..Device.Radio.Capabilities.WiFi7bSTARole.EMLMRFreqSeparation # +########################################################################*/ +static rbusError_t bstaemlmr_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { +#define MAX_RUID_STR_LEN 10 (void) handle; - (void) async; - rbusError_t ret; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; + unsigned int emlmrfs_idx; map_ale_info_t *ale; map_radio_info_t *radio; - map_bss_info_t *bss; - map_nb_assoc_control_params_t payload = {0}; - const char *sta_mac_list; - rbusValueError_t rc; - int len; - bool block; - uint32_t period; - - rbusObject_SetName(out, "Output"); + map_radio_wifi7_caps_t *wifi7_caps; + map_radio_wifi7_records_t *records; + char buf[MAX_RUID_STR_LEN] = {0}; - method += sizeof(DM_DEVICE_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); ale = dm_get_ale(param, is_num); if (ale == NULL) { log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - ret = RBUS_ERROR_INVALID_INPUT; - goto bail; + return RBUS_ERROR_INVALID_INPUT; } - method += sizeof(DM_RADIO_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); radio = dm_get_radio(ale, param, is_num); if (radio == NULL) { log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - ret = RBUS_ERROR_INVALID_INPUT; - goto bail; + return RBUS_ERROR_INVALID_INPUT; } - method += sizeof(DM_BSS_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); - bss = dm_get_bss(radio, param, is_num); - if (bss == NULL) { - log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - ret = RBUS_ERROR_INVALID_INPUT; - goto bail; + sscanf(name, "Capabilities.WiFi7bSTARole.EMLMRFreqSeparation.%d.%s", + &emlmrfs_idx, param); + + wifi7_caps = radio->wifi7_caps; + if (!wifi7_caps || emlmrfs_idx > wifi7_caps->bsta_emlmr_records_nr) { + log_lib_e("Invalid wifi7bsta emlmrfs index: %d", emlmrfs_idx); + return RBUS_ERROR_INVALID_INPUT; } - maccpy(payload.bssid, bss->bssid); - sscanf(method, "%s", param); - rc = rbusObject_GetPropertyString(in, "StationsList", &sta_mac_list, &len); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("StationsList is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - ret = RBUS_ERROR_INVALID_INPUT; - goto bail; + records = &wifi7_caps->bsta_emlmr_records[emlmrfs_idx]; + if (!records) { + log_lib_e("Invalid wifi7bsta emlmrfs index: %d", emlmrfs_idx); + return RBUS_ERROR_INVALID_INPUT; } - else { - char *token = NULL; - uint32_t num_sta_mac = 0; - uint32_t i = 0; - char *list, *tofree; - tofree = list = strdup(sta_mac_list); - if ( !list ) { - log_lib_e("Can't create a copy of StationsList parameter!"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - ret = RBUS_ERROR_INVALID_INPUT; - goto bail; - } + rbusValue_Init(&value); - while ( (token = strsep(&list, ",")) ) { - mac_addr mac = {0}; - if (mac_from_string(token, mac) == 0) { - num_sta_mac++; - } - else { - num_sta_mac = 0; - break; - } + if (strcmp(param, "RUID") == 0) { + if (b64_encode(records->ruid, sizeof(mac_addr), buf, sizeof(buf)) < 0) { + log_lib_e("b64_encode failed"); } - free(tofree); + rbusValue_SetString(value, buf); + } else if (strcmp(param, "FreqSeparation") == 0) { + rbusValue_SetUInt32(value, records->freq_separation); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } - if (num_sta_mac && num_sta_mac <= MAX_STATION_PER_BSS) { - payload.sta_mac_list = calloc(num_sta_mac, sizeof(mac_addr)); - if (!payload.sta_mac_list) { - log_lib_e("No Memory!"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - return RBUS_ERROR_INVALID_INPUT; - } - payload.num_sta_mac = num_sta_mac; + rbusProperty_SetValue(property, value); + rbusValue_Release(value); - i = 0; - tofree = list = strdup(sta_mac_list); - if ( !list ) { - log_lib_e("Can't create a copy of StationsList parameter!"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - ret = RBUS_ERROR_INVALID_INPUT; - goto bail; - } + return RBUS_ERROR_SUCCESS; +} - while ( (token = strsep(&list, ",")) ) { - mac_from_string(token, payload.sta_mac_list[i]); - i++; - } - free(tofree); - } - else { - log_lib_e("StationsList is not valid MAC address list"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - ret = RBUS_ERROR_INVALID_INPUT; - goto bail; - } +/*####################################################################### +# ..Device.Radio.Capabilities.WiFi7bSTARole.EMLSRFreqSeparation # +########################################################################*/ +static rbusError_t bstaemlsr_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ +#define MAX_RUID_STR_LEN 10 + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int emlsrfs_idx; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_radio_wifi7_caps_t *wifi7_caps; + map_radio_wifi7_records_t *records; + char buf[MAX_RUID_STR_LEN] = {0}; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - rc = rbusObject_GetPropertyBoolean(in, "Block", &block); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("Block is mandatory"); - ret = RBUS_ERROR_INVALID_INPUT; - goto bail; + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - payload.block = block; - if (payload.block) { - rc = rbusObject_GetPropertyUInt32(in, "Period", &period); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("Period is mandatory"); - ret = RBUS_ERROR_INVALID_INPUT; - goto bail; - } - payload.period = period; + sscanf(name, "Capabilities.WiFi7bSTARole.EMLSRFreqSeparation.%d.%s", + &emlsrfs_idx, param); + + wifi7_caps = radio->wifi7_caps; + if (!wifi7_caps || emlsrfs_idx > wifi7_caps->bsta_emlsr_records_nr) { + log_lib_e("Invalid wifi7bsta emlsrfs index: %d", emlsrfs_idx); + return RBUS_ERROR_INVALID_INPUT; } - if (map_dm_get_nbapi()->assoc_control != NULL) { - map_dm_get_nbapi()->assoc_control(ale, &payload); + records = &wifi7_caps->bsta_emlsr_records[emlsrfs_idx]; + if (!records) { + log_lib_e("Invalid wifi7bsta emlsrfs index: %d", emlsrfs_idx); + return RBUS_ERROR_INVALID_INPUT; + } - rbus_add_prop_str(out, "Status", "Success"); - ret = RBUS_ERROR_SUCCESS; + rbusValue_Init(&value); + + if (strcmp(param, "RUID") == 0) { + if (b64_encode(records->ruid, sizeof(mac_addr), buf, sizeof(buf)) < 0) { + log_lib_e("b64_encode failed"); + } + rbusValue_SetString(value, buf); + } else if (strcmp(param, "FreqSeparation") == 0) { + rbusValue_SetUInt32(value, records->freq_separation); } else { - log_lib_e("ClientAssocControl method is not implemented yet"); - rbus_add_prop_str(out, "Status", "Error_Not_Ready"); - ret = RBUS_ERROR_INVALID_INPUT; - goto bail; + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; } -bail: - SFREE(payload.sta_mac_list); + rbusProperty_SetValue(property, value); + rbusValue_Release(value); - return ret; + return RBUS_ERROR_SUCCESS; } /*####################################################################### -# Device.WiFi.DataElements.Network.Device.Radio.BSS.STA # +# ..Device.Radio.Capabilities.WiFi7bSTARole.STRFreqSeparation # ########################################################################*/ -static void map_dm_rbus_steering_history_add(map_sta_info_t *sta, uint32_t row_index) +static rbusError_t bstastr_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { - unsigned int ale_idx; - unsigned int radio_idx; - unsigned int bss_idx; - char tname[256] = {0}; - rbusError_t rc; - - log_lib_d("Add SteeringHistory row for: %s", sta->mac_str); +#define MAX_RUID_STR_LEN 10 + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int strfs_idx; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_radio_wifi7_caps_t *wifi7_caps; + map_radio_wifi7_records_t *records; + char buf[MAX_RUID_STR_LEN] = {0}; - if (check_bss_dm_idx(sta->bss) < 0) { - log_lib_e("Invalid indexing for sta"); - return; + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - bss_idx = sta->bss->dm_idx; - radio_idx = sta->bss->radio->dm_idx; - ale_idx = sta->bss->radio->ale->dm_idx; + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } - snprintf(tname, sizeof(tname) - 1, - "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.BSS.%d.STA.%d.MultiAPSTA.SteeringHistory.", - ale_idx, radio_idx + 1, bss_idx + 1, sta->dm_idx + 1); + sscanf(name, "Capabilities.WiFi7bSTARole.STRFreqSeparation.%d.%s", + &strfs_idx, param); - rc = rbusTable_registerRow(g_bus_handle, tname, row_index, NULL); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to create row %s %d rowindex: %d", tname, rc, row_index); - return; + wifi7_caps = radio->wifi7_caps; + if (!wifi7_caps || strfs_idx > wifi7_caps->bsta_str_records_nr) { + log_lib_e("Invalid wifi7bsta strfs index: %d", strfs_idx); + return RBUS_ERROR_INVALID_INPUT; } - log_lib_d("Added a row %s%d", tname, row_index); -} + records = &wifi7_caps->bsta_str_records[strfs_idx]; + if (!records) { + log_lib_e("Invalid wifi7bsta strfs index: %d", strfs_idx); + return RBUS_ERROR_INVALID_INPUT; + } -static void map_dm_rbus_steering_history_update(map_sta_info_t *sta) -{ - uint32_t index = 0; + rbusValue_Init(&value); - if (sta->steering_history_size_delta == 0) { - return; + if (strcmp(param, "RUID") == 0) { + if (b64_encode(records->ruid, sizeof(mac_addr), buf, sizeof(buf)) < 0) { + log_lib_e("b64_encode failed"); + } + rbusValue_SetString(value, buf); + } else if (strcmp(param, "FreqSeparation") == 0) { + rbusValue_SetUInt32(value, records->freq_separation); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; } - index = list_get_size(sta->steering_history) - sta->steering_history_size_delta + 1; // wrong - while (sta->steering_history_size_delta) { - map_dm_rbus_steering_history_add(sta, index); - index++; - sta->steering_history_size_delta--; - } + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; } -static void map_dm_rbus_steering_history_reinit(map_sta_info_t *sta) +/*####################################################################### +# ..Device.Radio.Capabilities.WiFi7bSTARole.NSTRFreqSeparation # +########################################################################*/ +static rbusError_t bstanstr_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { - uint32_t index = 1; - uint32_t list_size = list_get_size(sta->steering_history); +#define MAX_RUID_STR_LEN 10 + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int nstrfs_idx; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_radio_wifi7_caps_t *wifi7_caps; + map_radio_wifi7_records_t *records; + char buf[MAX_RUID_STR_LEN] = {0}; - while (list_size) { - map_dm_rbus_steering_history_add(sta, index); - index++; - list_size--; + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } -} -static void map_dm_rbus_client_steer_send_result(map_sta_info_t *sta_info) -{ - mac_addr_str target_bssid; - rbusMethodAsyncHandle_t async = NULL; - rbusObject_t out; - rbusError_t rc = RBUS_ERROR_SUCCESS; - map_sta_steering_history_t *steering_history = NULL; - - map_dm_rbus_client_steer_async_reply_get(sta_info, &async); - if (async == NULL) { - log_lib_e("Can't find async handle!"); - return; + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - rbusObject_Init(&out, "Output"); + sscanf(name, "Capabilities.WiFi7bSTARole.NSTRFreqSeparation.%d.%s", + &nstrfs_idx, param); - steering_history = last_object(sta_info->steering_history); - if (!steering_history) { - rbus_add_prop_str(out, "Status", "Error_Other"); - goto bail; - } + wifi7_caps = radio->wifi7_caps; + if (!wifi7_caps || nstrfs_idx > wifi7_caps->bsta_nstr_records_nr) { + log_lib_e("Invalid wifi7bsta nstrfs index: %d", nstrfs_idx); + return RBUS_ERROR_INVALID_INPUT; + } - rbus_add_prop_str(out, "Status", - (steering_history->btm_response == IEEE80211_BTM_STATUS_ACCEPT) ? "Success" : "Error_Other"); - rbus_add_prop_uint32(out, "BTMStatusCode", steering_history->btm_response); - mac_to_string(steering_history->ap_dest, target_bssid); - rbus_add_prop_str(out, "TargetBSSID", target_bssid); + records = &wifi7_caps->bsta_nstr_records[nstrfs_idx]; + if (!records) { + log_lib_e("Invalid wifi7bsta nstrfs index: %d", nstrfs_idx); + return RBUS_ERROR_INVALID_INPUT; + } - bail: - rc = rbusMethod_SendAsyncResponse(async, rc, out); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("ClientSteer() response failed: %d", rc); + rbusValue_Init(&value); + + if (strcmp(param, "RUID") == 0) { + if (b64_encode(records->ruid, sizeof(mac_addr), buf, sizeof(buf)) < 0) { + log_lib_e("b64_encode failed"); + } + rbusValue_SetString(value, buf); + } else if (strcmp(param, "FreqSeparation") == 0) { + rbusValue_SetUInt32(value, records->freq_separation); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; } - rbusObject_Release(out); + rbusProperty_SetValue(property, value); + rbusValue_Release(value); - map_dm_rbus_client_steer_async_reply_set(sta_info, NULL); + return RBUS_ERROR_SUCCESS; } -static void dm_rbus_create_sta(map_sta_info_t *sta) +/*####################################################################### +# ..Network.Device.Radio.Capabilities.CapableOperatingClassProfile # +########################################################################*/ +static rbusError_t capop_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { - unsigned int ale_idx; - unsigned int radio_idx; - unsigned int bss_idx; - unsigned int sta_idx; - char tname[256] = {0}; - rbusError_t rc; - map_radio_info_t *radio = sta->bss->radio; - - log_lib_d("create sta: %s", sta->mac_str); + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int cap_op_idx; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_op_class_t *op_class; + char nonoper_str[MAP_CS_BUF_LEN] = {0}; - if (check_bss_dm_idx(sta->bss) < 0) { - log_lib_e("Invalid indexing for sta"); - return; + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - bss_idx = sta->bss->dm_idx; - radio_idx = sta->bss->radio->dm_idx; - ale_idx = sta->bss->radio->ale->dm_idx; - if (get_sta_dm_idx(ale_idx, radio_idx, bss_idx, sta, &sta_idx) < 0) { - log_lib_e("No free index for sta: %s", sta->mac_str); - return; + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - snprintf(tname, sizeof(tname) - 1, - "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.BSS.%d.STA.", - ale_idx, radio_idx + 1, bss_idx + 1); - - log_lib_d("Create row[%d] at %s", sta_idx + 1, tname); + sscanf(name, "Capabilities.CapableOperatingClassProfile.%d.%s", + &cap_op_idx, param); - rc = rbusTable_registerRow(g_bus_handle, tname, sta_idx + 1, sta->mac_str); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to create row[%d] for sta: %s", - sta_idx + 1, sta->mac_str); + if (cap_op_idx > (unsigned int)(radio->cap_op_class_list.op_classes_nr + 1)) { + log_lib_e("Invalid capable operating class index: %d", cap_op_idx - 1); + return RBUS_ERROR_INVALID_INPUT; } + op_class = &radio->cap_op_class_list.op_classes[cap_op_idx - 1]; - map_dm_rbus_steering_history_reinit(sta); + rbusValue_Init(&value); - if (!sta->dm_payload) { - sta->dm_payload = calloc(1, sizeof(dm_sta_payload_t)); + if (strcmp(param, "Class") == 0) { + rbusValue_SetUInt32(value, op_class->op_class); + } else if (strcmp(param, "MaxTxPower") == 0) { + rbusValue_SetInt32(value, op_class->eirp); + } else if (strcmp(param, "NonOperable") == 0) { + map_cs_to_string(&op_class->channels, ',', nonoper_str, sizeof(nonoper_str)); + rbusValue_SetString(value, nonoper_str); + } else if (strcmp(param, "NumberOfNonOperChan") == 0) { + rbusValue_SetUInt32(value, op_class->channels.nr); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; } - map_dm_rbus_client_steer_send_result(sta); + rbusProperty_SetValue(property, value); + rbusValue_Release(value); - radio_remove_unassoc_sta(radio, sta->mac); + return RBUS_ERROR_SUCCESS; } -static void bmquery_send_result(map_sta_info_t *sta) +/*####################################################################### +# ..DataElements.Network.Device.Radio.CurrentOperatingClassProfile # +########################################################################*/ +static rbusError_t currop_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { - rbusMethodAsyncHandle_t async = NULL; - rbusObject_t out; - char *report_str; - uint32_t report_cnt; - rbusError_t rc = RBUS_ERROR_SUCCESS; +#define MAX_CH_STR_LEN 6 + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int curr_op_idx; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_op_class_t *op_class; + char ch_str[MAX_CH_STR_LEN] = {0}; + char timestamp[MAX_TS_STR_LEN] = {0}; - get_sta_dm_bmquery_reply(sta, &async); - if (async == NULL) { - log_lib_i("No ongoing measurement"); - return; + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - report_cnt = list_get_size(sta->beacon_metrics); - if (report_cnt == 0 && sta->bmquery_status == 0) { - log_lib_i("Measurement in progress"); - return; + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - rbusObject_Init(&out, "Output"); + sscanf(name, "CurrentOperatingClassProfile.%d.%s", &curr_op_idx, param); - if (report_cnt == 0 || sta->bmquery_status != 0) { - log_lib_w("Measurement complete with no report"); - rbus_add_prop_str(out, "Status", "Error_Other"); - goto reply; + if (curr_op_idx > (unsigned int)(radio->curr_op_class_list.op_classes_nr + 1)) { + log_lib_e("Invalid current operating class index: %d", curr_op_idx - 1); + return RBUS_ERROR_INVALID_INPUT; } + op_class = &radio->curr_op_class_list.op_classes[curr_op_idx - 1]; - report_str = get_beacon_metrics_str(sta->beacon_metrics); - - rbus_add_prop_str(out, "Status", "Success"); - rbus_add_prop_uint32(out, "NumberOfMeasureReports", report_cnt); - rbus_add_prop_str(out, "MeasurementReport", report_str); - - free(report_str); + rbusValue_Init(&value); -reply: - rc = rbusMethod_SendAsyncResponse(async, rc, out); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("BeaconMetricsQuery() response failed: %d", rc); + if (strcmp(param, "Class") == 0) { + rbusValue_SetUInt32(value, op_class->op_class); + } else if (strcmp(param, "Channel") == 0) { + if (op_class->channels.nr == 1) { + map_cs_to_string(&op_class->channels, ',', ch_str, sizeof(ch_str)); + rbusValue_SetUInt32(value, atoi(ch_str)); + } else { + log_lib_w("Internal error"); + rbusValue_SetUInt32(value, radio->current_op_channel); + } + } else if (strcmp(param, "TxPower") == 0) { + rbusValue_SetInt32(value, op_class->eirp); + } else if (strcmp(param, "TimeStamp") == 0) { + get_timestamp_str(0, timestamp, sizeof(timestamp)); + rbusValue_SetString(value, timestamp); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; } - rbusObject_Release(out); + rbusProperty_SetValue(property, value); + rbusValue_Release(value); - set_sta_dm_bmquery_reply(sta, NULL); + return RBUS_ERROR_SUCCESS; } -static void dm_rbus_update_sta(map_sta_info_t *sta) +/*####################################################################### +# ..WiFi.DataElements.Network.Device.Radio.DisAllowedOpClassChannels # +########################################################################*/ +static rbusError_t disop_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { - log_lib_d("update sta[%d]: %s", sta->dm_idx, sta->mac_str); + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_op_class_t *op_class; + char disallowed_str[MAP_CS_BUF_LEN] = {0}; - if (check_sta_dm_idx(sta) < 0) { - log_lib_e("Invalid indexing for sta"); + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - map_dm_rbus_steering_history_update(sta); + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } - /* Not proper, need a way to distinguish */ - bmquery_send_result(sta); + name += sizeof("DisAllowedOpClassChannels"); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + op_class = dm_get_disallowed_op_class(radio, param, is_num); + if (op_class == NULL) { + log_lib_e("Can't find opclass %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } - map_dm_rbus_client_steer_send_result(sta); -} + sscanf(name, "%s", param); -static void dm_rbus_remove_sta(map_sta_info_t *sta) -{ - unsigned int ale_idx; - unsigned int radio_idx; + rbusValue_Init(&value); + + if (strcmp(param, "Enable") == 0) { + rbusValue_SetBoolean(value, op_class->enable); + } else if (strcmp(param, "OpClass") == 0) { + rbusValue_SetUInt32(value, op_class->op_class); + } else if (strcmp(param, "ChannelList") == 0) { + map_cs_to_string(&op_class->channels, ',', disallowed_str, sizeof(disallowed_str)); + rbusValue_SetString(value, disallowed_str); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +static rbusError_t disop_set_rbus(rbusHandle_t handle, rbusProperty_t property, rbusSetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + char const* name = rbusProperty_GetName(property); + rbusValue_t value = rbusProperty_GetValue(property); + rbusValueType_t type = rbusValue_GetType(value); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_op_class_t *op_class; + map_channel_set_t channels = {0}; + uint8_t channel; + const char *buffer; + int blen; + bool bval; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof("DisAllowedOpClassChannels"); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + op_class = dm_get_disallowed_op_class(radio, param, is_num); + if (op_class == NULL) { + log_lib_e("Can't find opclass %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + sscanf(name, "%s", param); + + if (strcmp(param, "Enable") == 0) { + if (type != RBUS_BOOLEAN) { + log_lib_e("Invalid value type"); + return RBUS_ERROR_INVALID_INPUT; + } + + bval = rbusValue_GetBoolean(value); + if (bval == op_class->enable) { + return RBUS_ERROR_SUCCESS; + } + + op_class->enable = bval; + } else if (strcmp(param, "ChannelList") == 0) { + if (type != RBUS_STRING) { + log_lib_e("Invalid value type"); + return RBUS_ERROR_INVALID_INPUT; + } + buffer = rbusValue_GetString(value, &blen); + + parse_channel_list(buffer, blen, &channels); + /* Remove channels not valid for op_class */ + map_cs_foreach_safe(&channels, channel) { + if (!map_is_channel_in_op_class(op_class->op_class, channel)) { + map_cs_unset(&channels, channel); + } + } + if (map_cs_compare(&channels, &op_class->channels) == 0) { + return RBUS_ERROR_SUCCESS; + } + map_cs_copy(&op_class->channels, &channels); + + if (op_class->enable == false) { + return RBUS_ERROR_SUCCESS; + } + } else { + log_lib_e("Invalid param: %s", param); + return RBUS_ERROR_INVALID_INPUT; + } + + if (map_dm_get_nbapi()->channel_selection) { + map_nb_ch_selection_param_t payload; + + maccpy(payload.radio_id, radio->radio_id); + map_dm_get_nbapi()->channel_selection(ale, &payload); + } + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# Device.WiFi.DataElements.Network.Device.Radio.ScanResult # +########################################################################*/ +static rbusError_t scanres_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int scan_idx; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_scan_info_t *si; + dm_scan_table_t *dm_scan; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + sscanf(name, "ScanResult.%d.%s", &scan_idx, param); + + dm_scan = get_radio_dm_scan(radio, scan_idx); + if (dm_scan == NULL) { + log_lib_e("Invalid scan index: %d", scan_idx); + return RBUS_ERROR_INVALID_INPUT; + } + + si = &radio->last_scan_info; + + rbusValue_Init(&value); + + if (strcmp(param, "TimeStamp") == 0) { + /* Copy ts to dm_scan */ + rbusValue_SetString(value, (char *)si->last_scan_ts); + } else if (strcmp(param, "OpClassScanNumberOfEntries") == 0) { + rbusValue_SetUInt32(value, dm_scan->opcl_cnt); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# ..DataElements.Network.Device.Radio.ScanResult.OpClassScan # +########################################################################*/ +static rbusError_t opclscan_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int scan_idx; + unsigned int opcl_idx; + map_ale_info_t *ale; + map_radio_info_t *radio; + dm_scan_table_t *dm_scan; + dm_opcl_table_t *dm_opcl; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + sscanf(name, "ScanResult.%d.OpClassScan.%d.%s", + &scan_idx, &opcl_idx, param); + + dm_scan = get_radio_dm_scan(radio, scan_idx); + if (dm_scan == NULL) { + log_lib_e("Invalid scan index: %d", scan_idx); + return RBUS_ERROR_INVALID_INPUT; + } + + dm_opcl = get_dm_scan_opcl(dm_scan, opcl_idx); + if (dm_opcl == NULL) { + log_lib_e("Invalid op class index: %d", opcl_idx); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusValue_Init(&value); + + if (strcmp(param, "OperatingClass") == 0) { + rbusValue_SetUInt32(value, dm_opcl->id); + } else if (strcmp(param, "ChannelScanNumberOfEntries") == 0) { + rbusValue_SetUInt32(value, dm_opcl->chan_cnt); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# ..Network.Device.Radio.ScanResult.OpClassScan.ChannelScan # +########################################################################*/ +static rbusError_t chscan_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int scan_idx; + unsigned int opcl_idx; + unsigned int chan_idx; + map_ale_info_t *ale; + map_radio_info_t *radio; + dm_scan_table_t *dm_scan; + dm_opcl_table_t *dm_opcl; + dm_chan_table_t *dm_chan; + map_scan_result_t *sr; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + sscanf(name, "ScanResult.%d.OpClassScan.%d.ChannelScan.%d.%s", + &scan_idx, &opcl_idx, &chan_idx, param); + + dm_scan = get_radio_dm_scan(radio, scan_idx); + if (dm_scan == NULL) { + log_lib_e("Invalid scan index: %d", scan_idx); + return RBUS_ERROR_INVALID_INPUT; + } + + dm_opcl = get_dm_scan_opcl(dm_scan, opcl_idx); + if (dm_opcl == NULL) { + log_lib_e("Invalid op class index: %d", opcl_idx); + return RBUS_ERROR_INVALID_INPUT; + } + + dm_chan = get_dm_opcl_chan(dm_opcl, chan_idx); + if (dm_chan == NULL) { + log_lib_e("Invalid channel index: %d", chan_idx); + return RBUS_ERROR_INVALID_INPUT; + } + + sr = map_dm_rbus_get_scanres(radio, dm_scan->id, dm_opcl->id, dm_chan->id); + if (sr == NULL) { + log_lib_e("Invalid scan id"); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusValue_Init(&value); + + if (strcmp(param, "Channel") == 0) { + rbusValue_SetUInt32(value, dm_chan->id); + } else if (strcmp(param, "TimeStamp") == 0) { + rbusValue_SetString(value, (char *)sr->channel_scan_ts); + } else if (strcmp(param, "Utilization") == 0) { + rbusValue_SetUInt32(value, 0); + } else if (strcmp(param, "Noise") == 0) { + rbusValue_SetInt32(value, 0); + } else if (strcmp(param, "NeighborBSSNumberOfEntries") == 0) { + rbusValue_SetInt32(value, dm_chan->nbss_cnt); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# ..Device.Radio.ScanResult.OpClassScan.ChannelScan.NeighborBSS # +########################################################################*/ +static rbusError_t nbss_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + unsigned int scan_idx; + unsigned int opcl_idx; + unsigned int chan_idx; + unsigned int nbss_idx; + map_ale_info_t *ale; + map_radio_info_t *radio; + dm_scan_table_t *dm_scan; + dm_opcl_table_t *dm_opcl; + dm_chan_table_t *dm_chan; + dm_nbss_table_t *dm_nbss; + map_channel_scan_neighbor_t *ni; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + sscanf(name, "ScanResult.%d.OpClassScan.%d.ChannelScan.%d.NeighborBSS.%d.%s", + &scan_idx, &opcl_idx, &chan_idx, &nbss_idx, param); + + dm_scan = get_radio_dm_scan(radio, scan_idx); + if (dm_scan == NULL) { + log_lib_e("Invalid scan index: %d", scan_idx); + return RBUS_ERROR_INVALID_INPUT; + } + + dm_opcl = get_dm_scan_opcl(dm_scan, opcl_idx); + if (dm_opcl == NULL) { + log_lib_e("Invalid op class index: %d", opcl_idx); + return RBUS_ERROR_INVALID_INPUT; + } + + dm_chan = get_dm_opcl_chan(dm_opcl, chan_idx); + if (dm_chan == NULL) { + log_lib_e("Invalid channel index: %d", chan_idx); + return RBUS_ERROR_INVALID_INPUT; + } + + dm_nbss = get_dm_chan_nbss(dm_chan, nbss_idx); + if (dm_nbss == NULL) { + log_lib_e("Invalid neighbour info index: %d", nbss_idx); + return RBUS_ERROR_INVALID_INPUT; + } + + ni = map_dm_rbus_get_nb_info(radio, dm_scan->id, dm_opcl->id, + dm_chan->id, dm_nbss->id); + if (ni == NULL) { + log_lib_e("Invalid scan credentials"); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusValue_Init(&value); + + if (strcmp(param, "BSSID") == 0) { + rbusValue_SetString(value, mac_string(ni->bssid)); + } else if (strcmp(param, "SSID") == 0) { + rbusValue_SetString(value, (char *)ni->ssid); + } else if (strcmp(param, "SignalStre") == 0) { + rbusValue_SetInt32(value, ni->rcpi); + } else if (strcmp(param, "ChannelBan") == 0) { + rbusValue_SetString(value, (char *)ni->ch_bw); + } else if (strcmp(param, "ChannelUti") == 0) { + if (ni->bss_load_elem_present == 1) { + rbusValue_SetUInt32(value, ni->channel_utilization); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "StationCou") == 0) { + if (ni->bss_load_elem_present == 1) { + rbusValue_SetUInt32(value, ni->stas_nr); + } else { + rbusValue_SetUInt32(value, 0); + } + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# Device.WiFi.DataElements.Network.Device.Radio.BSS # +########################################################################*/ +static void dm_rbus_create_bss(map_bss_info_t *bss) +{ + unsigned int ale_idx; + unsigned int radio_idx; + unsigned int bss_idx; + char tname[256] = {0}; + rbusError_t rc; + + if (check_radio_dm_idx(bss->radio) < 0) { + log_lib_e("Invalid indexing for bss"); + return; + } + + radio_idx = bss->radio->dm_idx; + ale_idx = bss->radio->ale->dm_idx; + if (get_bss_dm_idx(ale_idx, radio_idx, bss, &bss_idx) < 0) { + log_lib_e("No free index for bss: %s", bss->bssid_str); + return; + } + + snprintf(tname, sizeof(tname) - 1, + "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.BSS.", + ale_idx + 1, radio_idx + 1); + + log_lib_d("Create row %s%d", tname, bss_idx + 1); + rc = rbusTable_registerRow(g_bus_handle, tname, bss_idx + 1, bss->bssid_str); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to create row[%d] for bss: %s", + bss_idx + 1, bss->bssid_str); + } +} + +static void dm_rbus_update_bss(map_bss_info_t *bss) +{ + unsigned int ale_idx; + unsigned int ap_mld_idx; + unsigned int aff_ap_idx; + char tname[256] = {0}; + rbusError_t rc; + + log_lib_d("update bss[%d]: %s", bss->dm_idx, bss->bssid_str); + + if (bss->prev_ap_mld == NULL) { + goto add_row; + } + +#if 0 + if (check_aff_ap_dm_idx(bss) < 0) { + log_lib_e("Invalid indexing for aff_ap"); + return; + } +#endif + + aff_ap_idx = bss->dm_aa_idx; + ap_mld_idx = bss->prev_ap_mld->dm_idx; + ale_idx = bss->prev_ap_mld->ale->dm_idx; + + snprintf(tname, sizeof(tname) - 1, + "Device.WiFi.DataElements.Network.Device.%d.APMLD.%d.AffiliatedAP.%d", + ale_idx + 1, ap_mld_idx + 1, aff_ap_idx + 1); + + log_lib_d("Delete row %s", tname); + rc = rbusTable_unregisterRow(g_bus_handle, tname); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to delete row[%d] for aff_app: %s", + aff_ap_idx + 1, bss->bssid_str); + } + + free_aff_ap_dm_idx(ale_idx, ap_mld_idx, aff_ap_idx); + +add_row: + if (bss->ap_mld == NULL) { + return; + } + + if (check_aff_ap_dm_idx(bss) < 0) { + log_lib_e("Invalid indexing for aff_ap"); + return; + } + + ap_mld_idx = bss->ap_mld->dm_idx; + ale_idx = bss->ap_mld->ale->dm_idx; + if (get_aff_ap_dm_idx(ale_idx, ap_mld_idx, bss, &aff_ap_idx) < 0) { + log_lib_e("No free index for aff_ap: %s", bss->bssid_str); + return; + } + + snprintf(tname, sizeof(tname) - 1, + "Device.WiFi.DataElements.Network.Device.%d.APMLD.%d.AffiliatedAP.", + ale_idx + 1, ap_mld_idx + 1); + + log_lib_d("Create row %s%d", tname, aff_ap_idx + 1); + rc = rbusTable_registerRow(g_bus_handle, tname, aff_ap_idx + 1, bss->bssid_str); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to create row[%d] for aff_app: %s", + aff_ap_idx + 1, bss->bssid_str); + } +} + +static void dm_rbus_remove_bss(map_bss_info_t *bss) +{ + unsigned int ale_idx; + unsigned int radio_idx; + unsigned int bss_idx; + unsigned int ap_mld_idx; + unsigned int aff_ap_idx; + char tname[256] = {0}; + rbusError_t rc; + + if (bss->dm_removed) { + goto aff_ap; + } + + if (check_bss_dm_idx(bss) < 0) { + log_lib_e("Invalid indexing for bss"); + goto aff_ap; + } + + bss_idx = bss->dm_idx; + radio_idx = bss->radio->dm_idx; + ale_idx = bss->radio->ale->dm_idx; + + snprintf(tname, sizeof(tname) - 1, + "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.BSS.%d", + ale_idx + 1, radio_idx + 1, bss_idx + 1); + + log_lib_d("Delete row %s", tname); + rc = rbusTable_unregisterRow(g_bus_handle, tname); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to delete row[%d] for bss: %s", + bss_idx + 1, bss->bssid_str); + } + + free_bss_dm_idx(ale_idx, radio_idx, bss_idx); + + /* Mark child objects are removed */ + mark_stas_removed(bss); + +aff_ap: + if (bss->ap_mld == NULL) { + return; + } + + if (bss->dm_aa_removed) { + return; + } + + if (check_aff_ap_dm_idx(bss) < 0) { + log_lib_e("Invalid indexing for aff_ap"); + return; + } + + aff_ap_idx = bss->dm_aa_idx; + ap_mld_idx = bss->ap_mld->dm_idx; + ale_idx = bss->ap_mld->ale->dm_idx; + + snprintf(tname, sizeof(tname) - 1, + "Device.WiFi.DataElements.Network.Device.%d.APMLD.%d.AffiliatedAP.%d", + ale_idx + 1, ap_mld_idx + 1, aff_ap_idx + 1); + + log_lib_d("Delete row %s", tname); + rc = rbusTable_unregisterRow(g_bus_handle, tname); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to delete row[%d] for aff_app: %s", + aff_ap_idx + 1, bss->bssid_str); + } + + free_aff_ap_dm_idx(ale_idx, ap_mld_idx, aff_ap_idx); +} + +static rbusError_t bss_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + uint32_t last_change; + char timestamp[MAX_TS_STR_LEN] = {0}; + map_profile_cfg_t *profile = NULL; + map_controller_cfg_t *cfg; + unsigned int i; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_BSS_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + bss = dm_get_bss(radio, param, is_num); + if (bss == NULL) { + log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + sscanf(name, "%s", param); + + rbusValue_Init(&value); + + if (strcmp(param, "BSSID") == 0) { + rbusValue_SetString(value, bss->bssid_str); + } else if (strcmp(param, "SSID") == 0) { + rbusValue_SetString(value, bss->ssid); + } else if (strcmp(param, "Enabled") == 0) { + rbusValue_SetBoolean(value, is_bss_active(bss->state) ? true : false); + } else if (strcmp(param, "LastChange") == 0) { + last_change = acu_get_timestamp_sec() - bss->change_ts; + rbusValue_SetUInt32(value, last_change); + } else if (strcmp(param, "TimeStamp") == 0) { + get_timestamp_str(0, timestamp, sizeof(timestamp)); + rbusValue_SetString(value, timestamp); + } else if (strcmp(param, "UnicastBytesReceived") == 0) { + rbusValue_SetUInt64(value, bss->extended_metrics.rx_ucast_bytes); + } else if (strcmp(param, "UnicastBytesSent") == 0) { + rbusValue_SetUInt64(value, bss->extended_metrics.tx_ucast_bytes); + } else if (strcmp(param, "MulticastBytesReceived") == 0) { + rbusValue_SetUInt64(value, bss->extended_metrics.rx_mcast_bytes); + } else if (strcmp(param, "MulticastBytesSent") == 0) { + rbusValue_SetUInt64(value, bss->extended_metrics.tx_mcast_bytes); + } else if (strcmp(param, "BroadcastBytesReceived") == 0) { + rbusValue_SetUInt64(value, bss->extended_metrics.rx_bcast_bytes); + } else if (strcmp(param, "BroadcastBytesSent") == 0) { + rbusValue_SetUInt64(value, bss->extended_metrics.tx_bcast_bytes); + } else if (strcmp(param, "BackhaulUse") == 0) { + rbusValue_SetBoolean(value, (bss->type & MAP_BACKHAUL_BSS) ? true : false); + } else if (strcmp(param, "FronthaulUse") == 0) { + rbusValue_SetBoolean(value, (bss->type & MAP_FRONTHAUL_BSS) ? true : false); + } else if (strcmp(param, "FronthaulAKMsAllowed") == 0) { + cfg = &map_cfg_get()->controller_cfg; + for (i = 0; i < cfg->num_profiles; i++) { + if (strcmp(bss->ssid, cfg->profiles[i].bss_ssid) == 0) { + profile = &cfg->profiles[i]; + break; + } + } + if (profile && (profile->bss_state & MAP_FRONTHAUL_BSS)) { + rbusValue_SetString(value, + get_auth_mode_str(profile->supported_auth_modes)); + } else { + rbusValue_SetString(value, "none"); + } + } else if (strcmp(param, "BackhaulAKMsAllowed") == 0) { + cfg = &map_cfg_get()->controller_cfg; + for (i = 0; i < cfg->num_profiles; i++) { + if (strcmp(bss->ssid, cfg->profiles[i].bss_ssid) == 0) { + profile = &cfg->profiles[i]; + break; + } + } + if (profile && (profile->bss_state & MAP_BACKHAUL_BSS)) { + rbusValue_SetString(value, + get_auth_mode_str(profile->supported_auth_modes)); + } else { + rbusValue_SetString(value, "none"); + } + } else if (strcmp(param, "STANumberOfEntries") == 0) { + rbusValue_SetUInt32(value, bss->stas_nr); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +static rbusError_t rbus_method_client_assoc_control(rbusHandle_t handle, char const* method, rbusObject_t in, rbusObject_t out, rbusMethodAsyncHandle_t async) +{ + (void) handle; + (void) async; + rbusError_t ret; + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_nb_assoc_control_params_t payload = {0}; + const char *sta_mac_list; + rbusValueError_t rc; + int len; + bool block; + uint32_t period; + + rbusObject_SetName(out, "Output"); + + method += sizeof(DM_DEVICE_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + ret = RBUS_ERROR_INVALID_INPUT; + goto bail; + } + + method += sizeof(DM_RADIO_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + ret = RBUS_ERROR_INVALID_INPUT; + goto bail; + } + + method += sizeof(DM_BSS_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + bss = dm_get_bss(radio, param, is_num); + if (bss == NULL) { + log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + ret = RBUS_ERROR_INVALID_INPUT; + goto bail; + } + maccpy(payload.bssid, bss->bssid); + + sscanf(method, "%s", param); + rc = rbusObject_GetPropertyString(in, "StationsList", &sta_mac_list, &len); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("StationsList is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + ret = RBUS_ERROR_INVALID_INPUT; + goto bail; + } + else { + char *token = NULL; + uint32_t num_sta_mac = 0; + uint32_t i = 0; + char *list, *tofree; + + tofree = list = strdup(sta_mac_list); + if ( !list ) { + log_lib_e("Can't create a copy of StationsList parameter!"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + ret = RBUS_ERROR_INVALID_INPUT; + goto bail; + } + + while ( (token = strsep(&list, ",")) ) { + mac_addr mac = {0}; + if (mac_from_string(token, mac) == 0) { + num_sta_mac++; + } + else { + num_sta_mac = 0; + break; + } + } + free(tofree); + + if (num_sta_mac && num_sta_mac <= MAX_STATION_PER_BSS) { + payload.sta_mac_list = calloc(num_sta_mac, sizeof(mac_addr)); + if (!payload.sta_mac_list) { + log_lib_e("No Memory!"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + payload.num_sta_mac = num_sta_mac; + + i = 0; + tofree = list = strdup(sta_mac_list); + if ( !list ) { + log_lib_e("Can't create a copy of StationsList parameter!"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + ret = RBUS_ERROR_INVALID_INPUT; + goto bail; + } + + while ( (token = strsep(&list, ",")) ) { + mac_from_string(token, payload.sta_mac_list[i]); + i++; + } + free(tofree); + } + else { + log_lib_e("StationsList is not valid MAC address list"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + ret = RBUS_ERROR_INVALID_INPUT; + goto bail; + } + } + + rc = rbusObject_GetPropertyBoolean(in, "Block", &block); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("Block is mandatory"); + ret = RBUS_ERROR_INVALID_INPUT; + goto bail; + } + payload.block = block; + + if (payload.block) { + rc = rbusObject_GetPropertyUInt32(in, "Period", &period); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("Period is mandatory"); + ret = RBUS_ERROR_INVALID_INPUT; + goto bail; + } + payload.period = period; + } + + if (map_dm_get_nbapi()->assoc_control != NULL) { + map_dm_get_nbapi()->assoc_control(ale, &payload); + + rbus_add_prop_str(out, "Status", "Success"); + ret = RBUS_ERROR_SUCCESS; + } else { + log_lib_e("ClientAssocControl method is not implemented yet"); + rbus_add_prop_str(out, "Status", "Error_Not_Ready"); + ret = RBUS_ERROR_INVALID_INPUT; + goto bail; + } + +bail: + SFREE(payload.sta_mac_list); + + return ret; +} + +/*####################################################################### +# Device.WiFi.DataElements.Network.Device.Radio.BSS.STA # +########################################################################*/ +static void map_dm_rbus_steering_history_add(map_sta_info_t *sta, uint32_t row_index) +{ + unsigned int ale_idx; + unsigned int radio_idx; + unsigned int bss_idx; + char tname[256] = {0}; + rbusError_t rc; + + if (check_bss_dm_idx(sta->bss) < 0) { + log_lib_e("Invalid indexing for sta"); + return; + } + + bss_idx = sta->bss->dm_idx; + radio_idx = sta->bss->radio->dm_idx; + ale_idx = sta->bss->radio->ale->dm_idx; + + snprintf(tname, sizeof(tname) - 1, + "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.BSS.%d.STA.%d.MultiAPSTA.SteeringHistory.", + ale_idx + 1, radio_idx + 1, bss_idx + 1, sta->dm_idx + 1); + + log_lib_d("Create row %s%d", tname, row_index); + rc = rbusTable_registerRow(g_bus_handle, tname, row_index, NULL); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to create row %s %d rowindex: %d", tname, rc, row_index); + return; + } +} + +static void map_dm_rbus_steering_history_update(map_sta_info_t *sta) +{ + uint32_t index = 0; + + if (sta->steering_history_size_delta == 0) { + return; + } + + index = list_get_size(sta->steering_history) - sta->steering_history_size_delta + 1; // wrong + while (sta->steering_history_size_delta) { + map_dm_rbus_steering_history_add(sta, index); + index++; + sta->steering_history_size_delta--; + } +} + +static void map_dm_rbus_steering_history_reinit(map_sta_info_t *sta) +{ + uint32_t index = 1; + uint32_t list_size = list_get_size(sta->steering_history); + + while (list_size) { + map_dm_rbus_steering_history_add(sta, index); + index++; + list_size--; + } +} + +static void map_dm_rbus_client_steer_send_result(map_sta_info_t *sta_info) +{ + mac_addr_str target_bssid; + rbusMethodAsyncHandle_t async = NULL; + rbusObject_t out; + rbusError_t rc = RBUS_ERROR_SUCCESS; + map_sta_steering_history_t *steering_history = NULL; + + map_dm_rbus_client_steer_async_reply_get(sta_info, &async); + if (async == NULL) { + log_lib_e("Can't find async handle!"); + return; + } + + rbusObject_Init(&out, "Output"); + + steering_history = last_object(sta_info->steering_history); + if (!steering_history) { + rbus_add_prop_str(out, "Status", "Error_Other"); + goto bail; + } + + rbus_add_prop_str(out, "Status", + (steering_history->btm_response == IEEE80211_BTM_STATUS_ACCEPT) ? "Success" : "Error_Other"); + rbus_add_prop_uint32(out, "BTMStatusCode", steering_history->btm_response); + mac_to_string(steering_history->ap_dest, target_bssid); + rbus_add_prop_str(out, "TargetBSSID", target_bssid); + + bail: + rc = rbusMethod_SendAsyncResponse(async, rc, out); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("ClientSteer() response failed: %d", rc); + } + + rbusObject_Release(out); + + map_dm_rbus_client_steer_async_reply_set(sta_info, NULL); +} + +static void dm_rbus_create_sta(map_sta_info_t *sta) +{ + unsigned int ale_idx; + unsigned int radio_idx; + unsigned int bss_idx; + unsigned int sta_idx; + unsigned int ap_mld_idx; + unsigned int sta_mld_idx; + unsigned int aff_sta_idx; + char tname[256] = {0}; + rbusError_t rc; + map_radio_info_t *radio = sta->bss->radio; + + if (check_bss_dm_idx(sta->bss) < 0) { + log_lib_e("Invalid indexing for sta"); + return; + } + + bss_idx = sta->bss->dm_idx; + radio_idx = sta->bss->radio->dm_idx; + ale_idx = sta->bss->radio->ale->dm_idx; + if (get_sta_dm_idx(ale_idx, radio_idx, bss_idx, sta, &sta_idx) < 0) { + log_lib_e("No free index for sta: %s", sta->mac_str); + return; + } + + snprintf(tname, sizeof(tname) - 1, + "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.BSS.%d.STA.", + ale_idx + 1, radio_idx + 1, bss_idx + 1); + + log_lib_d("Create row %s%d", tname, sta_idx + 1); + rc = rbusTable_registerRow(g_bus_handle, tname, sta_idx + 1, sta->mac_str); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to create row[%d] for sta: %s", + sta_idx + 1, sta->mac_str); + } + + if (sta->sta_mld == NULL) { + goto skip_aff; + } + + if (check_sta_mld_dm_idx(sta->sta_mld) < 0) { + log_lib_e("Invalid indexing for aff_sta"); + goto skip_aff; + } + + sta_mld_idx = sta->sta_mld->dm_idx; + ap_mld_idx = sta->sta_mld->ap_mld->dm_idx; + if (get_aff_sta_dm_idx(ale_idx, ap_mld_idx, sta_mld_idx, sta, &aff_sta_idx) < 0) { + log_lib_e("No free index for aff_sta: %s", sta->mac_str); + goto skip_aff; + } + + snprintf(tname, sizeof(tname) - 1, + "Device.WiFi.DataElements.Network.Device.%d.APMLD.%d.STAMLD.%d.AffiliatedSTA.", + ale_idx + 1, ap_mld_idx + 1, sta_mld_idx + 1); + + log_lib_d("Create row %s%d", tname, aff_sta_idx + 1); + rc = rbusTable_registerRow(g_bus_handle, tname, aff_sta_idx + 1, sta->mac_str); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to create row[%d] for aff_sta: %s", + aff_sta_idx + 1, sta->mac_str); + } + +skip_aff: + map_dm_rbus_steering_history_reinit(sta); + + if (!sta->dm_payload) { + sta->dm_payload = calloc(1, sizeof(dm_sta_payload_t)); + } + + map_dm_rbus_client_steer_send_result(sta); + steerwifibh_send_result(sta); + + radio_remove_unassoc_sta(radio, sta->mac); +} + +static void bmquery_send_result(map_sta_info_t *sta) +{ + rbusMethodAsyncHandle_t async = NULL; + rbusObject_t out; + char *report_str; + uint32_t report_cnt; + rbusError_t rc = RBUS_ERROR_SUCCESS; + + get_sta_dm_bmquery_reply(sta, &async); + if (async == NULL) { + log_lib_i("No ongoing measurement"); + return; + } + + report_cnt = list_get_size(sta->beacon_metrics); + if (report_cnt == 0 && sta->bmquery_status == 0) { + log_lib_i("Measurement in progress"); + return; + } + + rbusObject_Init(&out, "Output"); + + if (report_cnt == 0 || sta->bmquery_status != 0) { + log_lib_w("Measurement complete with no report"); + rbus_add_prop_str(out, "Status", "Error_Other"); + goto reply; + } + + report_str = get_beacon_metrics_str(sta->beacon_metrics); + + rbus_add_prop_str(out, "Status", "Success"); + rbus_add_prop_uint32(out, "NumberOfMeasureReports", report_cnt); + rbus_add_prop_str(out, "MeasurementReport", report_str); + + free(report_str); + +reply: + rc = rbusMethod_SendAsyncResponse(async, rc, out); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("BeaconMetricsQuery() response failed: %d", rc); + } + + rbusObject_Release(out); + + set_sta_dm_bmquery_reply(sta, NULL); +} + +static void dm_rbus_update_sta(map_sta_info_t *sta) +{ + log_lib_d("update sta[%d]: %s", sta->dm_idx, sta->mac_str); + + if (check_sta_dm_idx(sta) < 0) { + log_lib_e("Invalid indexing for sta"); + } + + map_dm_rbus_steering_history_update(sta); + + /* Not proper, need a way to distinguish */ + bmquery_send_result(sta); + + map_dm_rbus_client_steer_send_result(sta); +} + +static void dm_rbus_remove_sta(map_sta_info_t *sta) +{ + unsigned int ale_idx; + unsigned int radio_idx; unsigned int bss_idx; unsigned int sta_idx; + unsigned int ap_mld_idx; + unsigned int sta_mld_idx; + unsigned int aff_sta_idx; char tname[256] = {0}; rbusError_t rc; - log_lib_d("remove sta[%d]: %s", sta->dm_idx, sta->mac_str); + if (sta->dm_removed) { + goto aff_sta; + } + + if (check_sta_dm_idx(sta) < 0) { + log_lib_e("Invalid indexing for sta"); + goto aff_sta; + } + + sta_idx = sta->dm_idx; + bss_idx = sta->bss->dm_idx; + radio_idx = sta->bss->radio->dm_idx; + ale_idx = sta->bss->radio->ale->dm_idx; + + snprintf(tname, sizeof(tname) - 1, + "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.BSS.%d.STA.%d", + ale_idx + 1, radio_idx + 1, bss_idx + 1, sta_idx + 1); + + log_lib_d("Delete row %s", tname); + rc = rbusTable_unregisterRow(g_bus_handle, tname); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to delete row[%d], path %s for sta: %s", + sta_idx, tname, sta->mac_str); + } + + /* TODO: Does unregister row also clears leftover reply handles? + I doubt it */ + + free_sta_dm_idx(ale_idx, radio_idx, bss_idx, sta_idx); + +aff_sta: + if (sta->sta_mld == NULL) { + return; + } + + if (sta->dm_as_removed) { + return; + } + + if (check_aff_sta_dm_idx(sta) < 0) { + log_lib_e("Invalid indexing for aff_sta"); + return; + } + + aff_sta_idx = sta->dm_as_idx; + sta_mld_idx = sta->sta_mld->dm_idx; + ap_mld_idx = sta->sta_mld->ap_mld->dm_idx; + ale_idx = sta->sta_mld->ap_mld->ale->dm_idx; + + snprintf(tname, sizeof(tname) - 1, + "Device.WiFi.DataElements.Network.Device.%d.APMLD.%d.STAMLD.%d.AffiliatedSTA.%d", + ale_idx + 1, ap_mld_idx + 1, sta_mld_idx + 1, aff_sta_idx + 1); + + log_lib_d("Delete row %s", tname); + rc = rbusTable_unregisterRow(g_bus_handle, tname); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to delete row[%d], path %s for sta: %s", + aff_sta_idx, tname, sta->mac_str); + } + + free_aff_sta_dm_idx(ale_idx, ap_mld_idx, sta_mld_idx, aff_sta_idx); +} + +static rbusError_t sta_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + char timestamp[MAX_TS_STR_LEN] = {0}; + uint32_t last_connect; + map_sta_ext_bss_metrics_t *ebm; + map_sta_link_metrics_t *lm; + map_sta_traffic_stats_t *ts; + char *beacon_str; + char caps_str[MAX_CAPS_STR_LEN] = {0}; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_BSS_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + bss = dm_get_bss(radio, param, is_num); + if (bss == NULL) { + log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_STA_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + sta = dm_get_sta(bss, param, is_num); + if (sta == NULL) { + log_lib_e("Invalid sta %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + ts = &sta->traffic_stats; + lm = sta->metrics ? first_object(sta->metrics) : NULL; + + sscanf(name, "%s", param); + + rbusValue_Init(&value); + + if (strcmp(param, "MACAddress") == 0) { + rbusValue_SetString(value, sta->mac_str); + } else if (strcmp(param, "TimeStamp") == 0) { + get_timestamp_str(0, timestamp, sizeof(timestamp)); + rbusValue_SetString(value, timestamp); + } else if (strcmp(param, "HTCapabilities") == 0) { + if (sta->sta_caps.ht_support) { + get_ht_caps_str(&sta->sta_caps.ht_caps, caps_str, sizeof(caps_str)); + rbusValue_SetString(value, caps_str); + } else { + rbusValue_SetString(value, ""); + } + } else if (strcmp(param, "VHTCapabilities") == 0) { + if (sta->sta_caps.vht_support) { + get_vht_caps_str(&sta->sta_caps.vht_caps, caps_str, sizeof(caps_str)); + rbusValue_SetString(value, caps_str); + } else { + rbusValue_SetString(value, ""); + } + } else if (strcmp(param, "HECapabilities") == 0) { + if (sta->sta_caps.he_support) { + get_he_caps_str(&sta->sta_caps.he_caps, caps_str, sizeof(caps_str)); + rbusValue_SetString(value, caps_str); + } else { + rbusValue_SetString(value, ""); + } + } else if (strcmp(param, "ClientCapabilities") == 0) { + uint32_t blen = 4 * ((sta->assoc_frame_len + 4) / 3) + 1; + char *buf = calloc(blen, sizeof(char)); + if (b64_encode(sta->assoc_frame, sta->assoc_frame_len, buf, blen) < 0) { + log_lib_e("b64_encode failed"); + } + rbusValue_SetString(value, buf); + free(buf); + } else if (strcmp(param, "LastDataDownlinkRate") == 0) { + if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { + rbusValue_SetUInt32(value, ebm->last_data_dl_rate); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "LastDataUplinkRate") == 0) { + if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { + rbusValue_SetUInt32(value, ebm->last_data_ul_rate); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "UtilizationReceive") == 0) { + if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { + rbusValue_SetUInt32(value, ebm->utilization_rx); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "UtilizationTransmit") == 0) { + if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { + rbusValue_SetUInt32(value, ebm->utilization_tx); + } else { + rbusValue_SetUInt32(value, 0); + } + } else if (strcmp(param, "EstMACDataRateDownlink") == 0) { + rbusValue_SetUInt32(value, lm ? lm->dl_mac_datarate : 0); + } else if (strcmp(param, "EstMACDataRateUplink") == 0) { + rbusValue_SetUInt32(value, lm ? lm->ul_mac_datarate : 0); + } else if (strcmp(param, "SignalStrength") == 0) { + rbusValue_SetUInt32(value, lm ? RSSI_TO_RCPI(lm->rssi) : 0); + } else if (strcmp(param, "LastConnectTime") == 0) { + last_connect = map_dm_get_sta_assoc_ts_delta(sta->assoc_ts); + rbusValue_SetUInt32(value, last_connect); + } else if (strcmp(param, "BytesReceived") == 0) { + rbusValue_SetUInt64(value, ts->rx_bytes); + } else if (strcmp(param, "BytesSent") == 0) { + rbusValue_SetUInt64(value, ts->tx_bytes); + } else if (strcmp(param, "PacketsReceived") == 0) { + rbusValue_SetUInt64(value, ts->rx_packets); + } else if (strcmp(param, "PacketsSent") == 0) { + rbusValue_SetUInt64(value, ts->tx_packets); + } else if (strcmp(param, "ErrorsReceived") == 0) { + rbusValue_SetUInt64(value, ts->rx_packet_errors); + } else if (strcmp(param, "ErrorsSent") == 0) { + rbusValue_SetUInt64(value, ts->tx_packet_errors); + } else if (strcmp(param, "RetransCount") == 0) { + rbusValue_SetUInt64(value, ts->retransmissions); + } else if (strcmp(param, "MeasurementReport") == 0) { + if (list_get_size(sta->beacon_metrics)) { + beacon_str = get_beacon_metrics_str(sta->beacon_metrics); + rbusValue_SetString(value, beacon_str); + free(beacon_str); + } else { + rbusValue_SetString(value, ""); + } + } else if (strcmp(param, "NumberOfMeasureReports") == 0) { + rbusValue_SetUInt32(value, list_get_size(sta->beacon_metrics)); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# ..Network.Device.Radio.BSS.STA.X_AIRTIES_BeaconMetricsQuery() # +########################################################################*/ +static int bmquery_parse_chan_reports(rbusObject_t in, map_nb_bmquery_param_t *payload) +{ + rbusObject_t ocr; + rbusObject_t ocri; + rbusObject_t och; + rbusObject_t ochi; + map_op_class_t *ap_chan_rep; + uint32_t ap_chan_rep_cnt; + uint32_t class; + uint32_t channel; + rbusValueError_t rc; + + /* Input.APChannelReport */ + ocr = rbusObject_GetChildren(in); + if (payload->channel != 255) { + if (ocr) { + log_lib_i("APChannelReport is ignored"); + } + return 0; + } + if (!ocr) { + log_lib_e("APChannelReport object is mandatory"); + return -1; + } + /* Input.APChannelReport.{i} */ + ocri = rbusObject_GetChildren(ocr); + if (!ocri) { + log_lib_e("APChannelReport instance is mandatory"); + return -1; + } + + ap_chan_rep_cnt = 0; + do { + /* Input.APChannelReport.{i}.OperatingClass */ + rc = rbusObject_GetPropertyUInt32(ocri, "OperatingClass", &class); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("OperatingClass is mandatory"); + return -1; + } + + ++ap_chan_rep_cnt; + ap_chan_rep = &payload->ap_chan_reports[ap_chan_rep_cnt - 1]; + ap_chan_rep->op_class = class; + + /* Input.APChannelReport.{i}.Channel */ + och = rbusObject_GetChildren(ocri); + if (!och) { + log_lib_e("Channel object is mandatory"); + return -1; + } + /* Input.APChannelReport.{i}.Channel.{i} */ + ochi = rbusObject_GetChildren(och); + if (!och) { + log_lib_e("Channel instance is mandatory"); + return -1; + } + + do { + /* Input.APChannelReport.{i}.Channel.{i}.Channel */ + rc = rbusObject_GetPropertyUInt32(ochi, "Channel", &channel); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("Channel is mandatory"); + return -1; + } + map_cs_set(&ap_chan_rep->channels, channel); + + ochi = rbusObject_GetNext(ochi); + } while (ochi); + + ocri = rbusObject_GetNext(ocri); + } while (ocri) ; + payload->ap_chan_reports_nr = ap_chan_rep_cnt; + + return 0; +} + +static int bmquery_parse_elementid_list(rbusObject_t in, map_nb_bmquery_param_t *payload) +{ + const char *sval; + char buf[4] = {0}; + uint8_t pos; + int len; + rbusValueError_t rc; + + rc = rbusObject_GetPropertyString(in, "ElementIDList", &sval, &len); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + return 0; + } + if (payload->reporting_detail != 2/*MAP_BEACON_REPORT_DETAIL_ALL*/) { + log_lib_i("ElementIDList is ignored"); + return 0; + } + + pos = 0; + payload->element_ids_nr = 0; + while (len) { + if (*sval >= '0' && *sval <= '9') { + if (pos >= 4) { + /* Invalid input */ + memset(buf, 0, pos); + pos = 0; + } + buf[pos++] = *sval; + } else { + if (pos) { + payload->element_ids[payload->element_ids_nr++] = atoi(buf); + if (payload->element_ids_nr == 255) { + return 0; + } + memset(buf, 0, pos); + } + pos = 0; + } + ++sval; + --len; + } + if (pos) { + payload->element_ids[payload->element_ids_nr++] = atoi(buf); + } + + return 0; +} + +static rbusError_t sta_bmquery_rbus(rbusHandle_t handle, char const* method, rbusObject_t in, rbusObject_t out, rbusMethodAsyncHandle_t async) +{ + (void) handle; + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + rbusMethodAsyncHandle_t prev; + map_nb_bmquery_param_t payload = {0}; + rbusValueError_t rc; + const char *sval; + uint32_t uval; + int len; + + rbusObject_SetName(out, "Output"); + + method += sizeof(DM_DEVICE_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + goto bail; + } + + method += sizeof(DM_RADIO_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + goto bail; + } + + method += sizeof(DM_BSS_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + bss = dm_get_bss(radio, param, is_num); + if (bss == NULL) { + log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); + goto bail; + } + + method += sizeof(DM_STA_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + sta = dm_get_sta(bss, param, is_num); + if (sta == NULL) { + log_lib_e("Invalid sta %s: %s", is_num ? "index" : "alias", param); + goto bail; + } + maccpy(payload.sta_mac, sta->mac); + + get_sta_dm_bmquery_reply(sta, &prev); + if (prev) { + log_lib_e("BeaconMetricsQuery is not finished yet"); + rbus_add_prop_str(out, "Status", "Error_Not_Ready"); + return RBUS_ERROR_INVALID_METHOD; + } + + rc = rbusObject_GetPropertyUInt32(in, "OperatingClass", &uval); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("OperatingClass is mandatory"); + goto bail; + } + if (uval > 255) { + log_lib_e("Invalid OperatingClass: %d", uval); + goto bail; + } + payload.op_class = uval; + + rc = rbusObject_GetPropertyUInt32(in, "Channel", &uval); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("Channel is mandatory"); + goto bail; + } + if (uval > 255) { + log_lib_e("Invalid Channel: %d", uval); + goto bail; + } + payload.channel = uval; + + rc = rbusObject_GetPropertyString(in, "SSID", &sval, &len); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("SSID is mandatory"); + goto bail; + } + len = len > (MAX_SSID_LEN - 1) ? (MAX_SSID_LEN - 1) : len; + strncpy(payload.ssid, sval, len); + + /* Optional arguments */ + rc = rbusObject_GetPropertyString(in, "BSSID", &sval, &len); + if (rc == RBUS_VALUE_ERROR_SUCCESS) { + if (mac_from_string(sval, payload.bssid) < 0) { + log_lib_e("Invalid BSSID: %s", sval); + goto bail; + } + } else { + mac_from_string("ff:ff:ff:ff:ff:ff", payload.bssid); + } + + rc = rbusObject_GetPropertyUInt32(in, "ReportingDetail", &uval); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + payload.reporting_detail = 1/*MAP_BEACON_REPORT_DETAIL_REQUESTED*/; + } else { + if (uval > 2/*MAP_BEACON_REPORT_DETAIL_ALL*/) { + log_lib_e("Invalid ReportingDetail: %d", uval); + goto bail; + } + payload.reporting_detail = uval; + } + + if (bmquery_parse_chan_reports(in, &payload)) { + goto bail; + } + + if (bmquery_parse_elementid_list(in, &payload)) { + goto bail; + } + + /* Everything is fine, clear previous reports */ + if (sta->beacon_metrics != NULL) { + while (list_get_size(sta->beacon_metrics) > 0) { + free(remove_last_object(sta->beacon_metrics)); + } + } + sta->bmquery_status = 0; + + if (map_dm_get_nbapi()->beacon_metrics_query != NULL) { + map_dm_get_nbapi()->beacon_metrics_query(ale, &payload); + + set_sta_dm_bmquery_reply(sta, async); + } else { + log_lib_e("BeaconMetricsQuery is not implemented yet"); + rbus_add_prop_str(out, "Status", "Error_Not_Ready"); + return RBUS_ERROR_INVALID_METHOD; + } + + return RBUS_ERROR_ASYNC_RESPONSE; + +bail: + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; +} + +/*####################################################################### +# Device.WiFi.DataElements.Network.Device.Radio.BSS.STA.ClientSteer() # +########################################################################*/ +static rbusError_t sta_client_steer(rbusHandle_t handle, char const* method, rbusObject_t in, rbusObject_t out, rbusMethodAsyncHandle_t async) +{ + (void) handle; + rbusError_t ret = RBUS_ERROR_BUS_ERROR; + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + map_nb_client_steer_params_t payload = {0}; + const char *targetBSS; + const char *requestMode; + rbusValueError_t rc; + int len; + bool BTMDisassociationImminent; + bool BTMAbridged; + bool LinkRemovalImminent; + uint32_t steeringOpportunityWindow; + uint32_t TargetBSSChannel; + uint32_t BTMDisassociationTimer; + uint32_t TargetBSSOperatingClass; + uint32_t reasonCode; + map_sta_steering_history_t *steering_history = NULL; + + rbusObject_SetName(out, "Output"); + + method += sizeof(DM_DEVICE_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + + method += sizeof(DM_RADIO_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + + method += sizeof(DM_BSS_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + bss = dm_get_bss(radio, param, is_num); + if (bss == NULL) { + log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + + method += sizeof(DM_STA_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + sta = dm_get_sta(bss, param, is_num); + if (sta == NULL) { + log_lib_e("Invalid sta %s: %s", is_num ? "index" : "alias", param); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + maccpy(payload.target.sta_mac, sta->mac); + maccpy(payload.bssid, bss->bssid); + + sscanf(method, "%s", param); + rc = rbusObject_GetPropertyString(in, "TargetBSS", &targetBSS, &len); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("TargetBSS is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + mac_from_string(targetBSS, payload.target.bssid); + + rc = rbusObject_GetPropertyString(in, "RequestMode", &requestMode, &len); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("RequestMode is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + if (strcasecmp(requestMode, "Steering_Mandate") == 0) { + payload.flags |= NB_STEERING_REQUEST_FLAG_MANDATE; + } + else if (strcasecmp(requestMode, "Steering_Opportunity") == 0){ + rc = rbusObject_GetPropertyUInt32(in, "SteeringOpportunityWindow", &steeringOpportunityWindow); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("SteeringOpportunityWindow is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + payload.opportunity_wnd = steeringOpportunityWindow; + } + else { + log_lib_e("SteeringOpportunityWindow value is not valid"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + + rc = rbusObject_GetPropertyBoolean(in, "BTMDisassociationImminent", &BTMDisassociationImminent); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("RequestMode is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + payload.flags |= BTMDisassociationImminent ? NB_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT : 0; + + rc = rbusObject_GetPropertyBoolean(in, "BTMAbridged", &BTMAbridged); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("BTMAbridged is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + payload.flags |= BTMAbridged ? NB_STEERING_REQUEST_FLAG_BTM_ABRIDGED : 0; + + rc = rbusObject_GetPropertyBoolean(in, "LinkRemovalImminent", &LinkRemovalImminent); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("LinkRemovalImminent is optional, skipping"); + } + + rc = rbusObject_GetPropertyUInt32(in, "BTMDisassociationTimer", &BTMDisassociationTimer); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("BTMDisassociationTimer is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + // Check BTMDisassociationTimer is in limits + payload.disassociation_timer = BTMDisassociationTimer; + + rc = rbusObject_GetPropertyUInt32(in, "TargetBSSOperatingClass", &TargetBSSOperatingClass); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("TargetBSSOperatingClass is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + // Check TargetBSSOperatingClass is valid + payload.target.op_class = TargetBSSOperatingClass; + + rc = rbusObject_GetPropertyUInt32(in, "TargetBSSChannel", &TargetBSSChannel); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("TargetBSSChannel is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + payload.target.channel = TargetBSSChannel; + + rc = rbusObject_GetPropertyUInt32(in, "ReasonCode", &reasonCode); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("ReasonCode is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + payload.target.reason = reasonCode; + + steering_history = (map_sta_steering_history_t *)calloc(1, sizeof(map_sta_steering_history_t)); + if (steering_history == NULL) { + log_lib_e("Can't allocate memory for steering history"); + ret = RBUS_ERROR_OUT_OF_RESOURCES; + goto bail; + } + + insert_last_object(sta->steering_history, steering_history); + sta->steering_history_size_delta++; // This is not nice here, should be created in datamodel code. + + if (map_dm_get_nbapi()->client_steer != NULL) { + map_dm_get_nbapi()->client_steer(ale, &payload); + + if (ale->last_sta_steered) { + log_lib_e("There is another active station being steered! Will override!"); + } + ale->last_sta_steered = sta; + + steering_history->start_time = acu_get_epoch_nsec(); + maccpy(steering_history->ap_origin, bss->bssid); + steering_history->steering_approach = MAP_STEERING_APPROACH_BTM_REQUEST; + steering_history->trigger_event = MAP_STEERING_TRIGGER_EVENT_WIFI_LINK_QUALITY; + + sta->steering_stats.btm_attempts++; + + map_dm_rbus_client_steer_async_reply_set(sta, async); + } else { + log_lib_e("ClientSteer is not implemented yet"); + rbus_add_prop_str(out, "Status", "Error_Not_Ready"); + return RBUS_ERROR_INVALID_METHOD; + } + + return RBUS_ERROR_ASYNC_RESPONSE; + +bail: + return ret; +} + +/*####################################################################### +# ..Network.Device.Radio.BSS.STA.MultiAPSTA.Disassociate() # +########################################################################*/ +static rbusError_t mapsta_disassociate(rbusHandle_t handle, char const* method, rbusObject_t in, rbusObject_t out, rbusMethodAsyncHandle_t async) +{ + (void) handle; + (void) async; + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + map_nb_sta_disassociate_params_t payload = {0}; + rbusValueError_t rc; + uint32_t disassociation_timer; + uint32_t reason_code; + bool silent; + + rbusObject_SetName(out, "Output"); + + method += sizeof(DM_DEVICE_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + + method += sizeof(DM_RADIO_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } - if (sta->dm_removed) { - return; + method += sizeof(DM_BSS_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + bss = dm_get_bss(radio, param, is_num); + if (bss == NULL) { + log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; } + maccpy(payload.bssid, bss->bssid); - if (check_sta_dm_idx(sta) < 0) { - log_lib_e("Invalid indexing for sta"); - return; + method += sizeof(DM_STA_PREFIX); + method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + sta = dm_get_sta(bss, param, is_num); + if (sta == NULL) { + log_lib_e("Invalid sta %s: %s", is_num ? "index" : "alias", param); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; } + maccpy(payload.sta_mac, sta->mac); - sta_idx = sta->dm_idx; - bss_idx = sta->bss->dm_idx; - radio_idx = sta->bss->radio->dm_idx; - ale_idx = sta->bss->radio->ale->dm_idx; + sscanf(method, "%s", param); - snprintf(tname, sizeof(tname) - 1, - "Device.WiFi.DataElements.Network.Device.%d.Radio.%d.BSS.%d.STA.%d", - ale_idx, radio_idx + 1, bss_idx + 1, sta_idx + 1); + rc = rbusObject_GetPropertyUInt32(in, "DisassociationTimer", &disassociation_timer); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("DisassociationTimer is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + payload.disassociation_timer = disassociation_timer; // todo diassociation timer is not actively used - log_lib_d("Delete row %s", tname); + rc = rbusObject_GetPropertyUInt32(in, "ReasonCode", &reason_code); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("ReasonCode is mandatory"); + rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_INVALID_INPUT; + } + payload.reason_code = reason_code; - rc = rbusTable_unregisterRow(g_bus_handle, tname); - if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("Failed to delete row[%d], path %s for sta: %s", - sta_idx, tname, sta->mac_str); + rc = rbusObject_GetPropertyBoolean(in, "Silent", &silent); + if (rc != RBUS_VALUE_ERROR_SUCCESS) { + log_lib_e("Silent is optional, skipping"); + } + else + payload.silent = silent; // todo: silent is not used + + /* Perform scan */ + if (map_dm_get_nbapi()->mapsta_disassociate != NULL) { + map_dm_get_nbapi()->mapsta_disassociate(ale, &payload); + rbus_add_prop_str(out, "Status", "Success"); + // todo: process async reply + } else { + log_lib_e("Disassociate is not implemented yet"); + rbus_add_prop_str(out, "Status", "Error_Not_Ready"); + return RBUS_ERROR_INVALID_METHOD; } - /* TODO: Does unregister row also clears leftover reply handles? - I doubt it */ + return RBUS_ERROR_SUCCESS; +} - free_sta_dm_idx(ale_idx, radio_idx, bss_idx, sta_idx); +/*####################################################################### +# ..Network.Device.Radio.BSS.STA.MultiAPSTA.SteeringSummaryStats # +########################################################################*/ +static rbusError_t steering_sum_stats_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_BSS_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + bss = dm_get_bss(radio, param, is_num); + if (bss == NULL) { + log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_STA_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + sta = dm_get_sta(bss, param, is_num); + if (sta == NULL) { + log_lib_e("Invalid sta %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } - if (0) { - /* This may be enabled to shift indexes, if the data model - automatically keeps indexing consecutive. rbus does not - support it, tr069 forbids it */ - update_sta_idxs(sta->bss, sta); + sscanf(name, "MultiAPSTA.SteeringSummaryStats.%s", param); + + rbusValue_Init(&value); + + if (strcmp(param, "NoCandidateAPFailures") == 0) { + rbusValue_SetUInt64(value, sta->steering_stats.no_candidate_apfailures); + } else if (strcmp(param, "BlacklistAttempts") == 0) { + rbusValue_SetUInt64(value, sta->steering_stats.blacklist_attempts); + } else if (strcmp(param, "BlacklistSuccesses") == 0) { + rbusValue_SetUInt64(value, sta->steering_stats.blacklist_successes); + } else if (strcmp(param, "BlacklistFailures") == 0) { + rbusValue_SetUInt64(value, sta->steering_stats.blacklist_failures); + } else if (strcmp(param, "BTMAttempts") == 0) { + rbusValue_SetUInt64(value, sta->steering_stats.btm_attempts); + } else if (strcmp(param, "BTMSuccesses") == 0) { + rbusValue_SetUInt64(value, sta->steering_stats.btm_successes); + } else if (strcmp(param, "BTMFailures") == 0) { + rbusValue_SetUInt64(value, sta->steering_stats.btm_failures); + } else if (strcmp(param, "BTMQueryResponses") == 0) { + rbusValue_SetUInt64(value, sta->steering_stats.btm_query_responses); + } else if (strcmp(param, "LastSteerTime") == 0) { + rbusValue_SetUInt32(value, sta->steering_stats.last_steer_time); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# ..Network.Device.Radio.UnassociatedSTA # +########################################################################*/ +static rbusError_t unassoc_sta_get_rbus(rbusHandle_t handle, rbusProperty_t property, + rbusGetHandlerOptions_t *opts) +{ + (void)handle; + (void)opts; + rbusValue_t value; + char const *name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = { 0 }; + bool is_num; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_unassociated_sta_info_t *unassoc_sta; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_RADIO_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + radio = dm_get_radio(ale, param, is_num); + if (radio == NULL) { + log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof("UnassociatedSTA"); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + unassoc_sta = dm_get_unassoc_sta(radio, param, is_num); + if (unassoc_sta == NULL) { + log_lib_e("Invalid unassoc sta %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + sscanf(name, "%s", param); + + rbusValue_Init(&value); + + if (strcmp(param, "MACAddress") == 0) { + mac_addr_str mac_str; + mac_to_string(unassoc_sta->mac_address, mac_str); + rbusValue_SetString(value, mac_str); + } else if (strcmp(param, "SignalStrength") == 0) { + rbusValue_SetUInt32(value, unassoc_sta->signal_strength); + } else if (strcmp(param, "TimeStamp") == 0) { + rbusValue_SetString(value, unassoc_sta->timestamp); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; + } + + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# ..STA.{i}.MultiAPSTA.SteeringHistory.{i}. # +########################################################################*/ +static const char* map_dm_steering_history_trigger_event_str(map_steering_history_trigger_event_t te) +{ + switch (te) { + case MAP_STEERING_TRIGGER_EVENT_UNKNOWN: + return "Unknown"; + case MAP_STEERING_TRIGGER_EVENT_WIFI_CHANNEL_UTILIZATION: + return "Wi-Fi Utilization"; + case MAP_STEERING_TRIGGER_EVENT_WIFI_LINK_QUALITY: + return "Wi-Fi Link Quality"; + case MAP_STEERING_TRIGGER_EVENT_BACKHAUL_LINK_UTILIZATION: + return "Backhaul Link Utilization"; + default: + return NULL; + } +} + +static const char* map_dm_steering_history_approach_str(map_steering_approach_t a) +{ + switch (a) { + case MAP_STEERING_APPROACH_BLACKLIST: + return "Blacklist"; + case MAP_STEERING_APPROACH_BTM_REQUEST: + return "BTM Request"; + case MAP_STEERING_APPROACH_ASYNC_BTM_QUERY: + return "BTM Query"; + default: + return NULL; } } -static rbusError_t sta_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t steering_history_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { (void) handle; (void) opts; @@ -5621,13 +9093,7 @@ static rbusError_t sta_get_rbus(rbusHandle_t handle, rbusProperty_t property, rb map_radio_info_t *radio; map_bss_info_t *bss; map_sta_info_t *sta; - char timestamp[MAX_TS_STR_LEN] = {0}; - uint32_t last_connect; - map_sta_ext_bss_metrics_t *ebm; - map_sta_link_metrics_t *lm; - map_sta_traffic_stats_t *ts; - char *beacon_str; - char caps_str[MAX_CAPS_STR_LEN] = {0}; + uint32_t steering_history_index = 0; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -5660,681 +9126,551 @@ static rbusError_t sta_get_rbus(rbusHandle_t handle, rbusProperty_t property, rb log_lib_e("Invalid sta %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - ts = sta->traffic_stats; - lm = sta->metrics ? first_object(sta->metrics) : NULL; - sscanf(name, "%s", param); + sscanf(name, "MultiAPSTA.%s", param); rbusValue_Init(&value); - if (strcmp(param, "MACAddress") == 0) { - rbusValue_SetString(value, sta->mac_str); - } else if (strcmp(param, "TimeStamp") == 0) { - get_timestamp_str(0, timestamp, sizeof(timestamp)); - rbusValue_SetString(value, timestamp); - } else if (strcmp(param, "HTCapabilities") == 0) { - if (sta->sta_caps.ht_support) { - get_ht_caps_str(&sta->sta_caps.ht_caps, caps_str, sizeof(caps_str)); - rbusValue_SetString(value, caps_str); - } else { - rbusValue_SetString(value, ""); + if (strcmp(param, "SteeringHistoryNumberOfEntries") == 0) + { + rbusValue_SetUInt64(value, list_get_size(sta->steering_history)); + } + else + { + map_sta_steering_history_t *steering_history = NULL; + + if (sscanf(name, "MultiAPSTA.SteeringHistory.%d.%s", &steering_history_index,param) != 2) { + log_lib_e("Invalid path: %s, can't scan.", name); + goto invalid_input; } - } else if (strcmp(param, "VHTCapabilities") == 0) { - if (sta->sta_caps.vht_support) { - get_vht_caps_str(&sta->sta_caps.vht_caps, caps_str, sizeof(caps_str)); - rbusValue_SetString(value, caps_str); - } else { - rbusValue_SetString(value, ""); + + steering_history = object_at_index(sta->steering_history, steering_history_index - 1); + if (!steering_history) { + log_lib_e("Invalid index for SteeringHistory : %u", steering_history_index); + goto invalid_input; } - } else if (strcmp(param, "HECapabilities") == 0) { - if (sta->sta_caps.he_support) { - get_he_caps_str(&sta->sta_caps.he_caps, caps_str, sizeof(caps_str)); - rbusValue_SetString(value, caps_str); - } else { - rbusValue_SetString(value, ""); + + if (strcmp(param, "Time") == 0) { + char timestamp[MAX_TS_STR_LEN] = {0}; + + get_timestamp_str(steering_history->start_time, timestamp, sizeof(timestamp)); + rbusValue_SetString(value, timestamp); } - } else if (strcmp(param, "ClientCapabilities") == 0) { - uint32_t blen = 4 * ((sta->assoc_frame_len + 4) / 3) + 1; - char *buf = calloc(blen, sizeof(char)); - if (b64_encode(sta->assoc_frame, sta->assoc_frame_len, buf, blen) < 0) { - log_lib_e("b64_encode failed"); + else if (strcmp(param, "APOrigin") == 0) { + rbusValue_SetString(value, mac_string(steering_history->ap_origin)); } - rbusValue_SetString(value, buf); - free(buf); - } else if (strcmp(param, "LastDataDownlinkRate") == 0) { - if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { - rbusValue_SetUInt32(value, ebm->last_data_dl_rate); - } else { - rbusValue_SetUInt32(value, 0); + else if (strcmp(param, "APDestination") == 0) { + rbusValue_SetString(value, + maccmp(steering_history->ap_dest, g_zero_mac) ? mac_string(steering_history->ap_dest) : ""); } - } else if (strcmp(param, "LastDataUplinkRate") == 0) { - if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { - rbusValue_SetUInt32(value, ebm->last_data_ul_rate); - } else { - rbusValue_SetUInt32(value, 0); + else if (strcmp(param, "TriggerEvent") == 0) { + rbusValue_SetString(value, map_dm_steering_history_trigger_event_str(steering_history->trigger_event)); } - } else if (strcmp(param, "UtilizationReceive") == 0) { - if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { - rbusValue_SetUInt32(value, ebm->utilization_rx); - } else { - rbusValue_SetUInt32(value, 0); + else if (strcmp(param, "SteeringApproach") == 0) { + rbusValue_SetString(value, map_dm_steering_history_approach_str(steering_history->steering_approach)); } - } else if (strcmp(param, "UtilizationTransmit") == 0) { - if (get_last_sta_metrics(sta, &ebm) == 0 && ebm != NULL) { - rbusValue_SetUInt32(value, ebm->utilization_tx); - } else { - rbusValue_SetUInt32(value, 0); + else if (strcmp(param, "SteeringDuration") == 0) { + rbusValue_SetUInt64(value, steering_history->steering_duration); } - } else if (strcmp(param, "EstMACDataRateDownlink") == 0) { - rbusValue_SetUInt32(value, lm ? lm->dl_mac_datarate : 0); - } else if (strcmp(param, "EstMACDataRateUplink") == 0) { - rbusValue_SetUInt32(value, lm ? lm->ul_mac_datarate : 0); - } else if (strcmp(param, "SignalStrength") == 0) { - rbusValue_SetUInt32(value, lm ? lm->rssi : 0); - } else if (strcmp(param, "LastConnectTime") == 0) { - last_connect = map_dm_get_sta_assoc_ts_delta(sta->assoc_ts); - rbusValue_SetUInt32(value, last_connect); - } else if (strcmp(param, "BytesReceived") == 0) { - rbusValue_SetUInt64(value, ts ? ts->rxbytes : 0); - } else if (strcmp(param, "BytesSent") == 0) { - rbusValue_SetUInt64(value, ts ? ts->txbytes : 0); - } else if (strcmp(param, "PacketsReceived") == 0) { - rbusValue_SetUInt64(value, ts ? ts->rxpkts : 0); - } else if (strcmp(param, "PacketsSent") == 0) { - rbusValue_SetUInt64(value, ts ? ts->txpkts : 0); - } else if (strcmp(param, "ErrorsReceived") == 0) { - rbusValue_SetUInt64(value, ts ? ts->rxpkterrors : 0); - } else if (strcmp(param, "ErrorsSent") == 0) { - rbusValue_SetUInt64(value, ts ? ts->txpkterrors : 0); - } else if (strcmp(param, "RetransCount") == 0) { - rbusValue_SetUInt64(value, ts ? ts->retransmission_cnt : 0); - } else if (strcmp(param, "MeasurementReport") == 0) { - if (list_get_size(sta->beacon_metrics)) { - beacon_str = get_beacon_metrics_str(sta->beacon_metrics); - rbusValue_SetString(value, beacon_str); - free(beacon_str); - } else { - rbusValue_SetString(value, ""); + else { + log_lib_e("Invalid param: %s", param); + goto invalid_input; } - } else if (strcmp(param, "NumberOfMeasureReports") == 0) { - rbusValue_SetUInt32(value, list_get_size(sta->beacon_metrics)); - } else { - log_lib_e("Invalid param: %s", param); - rbusValue_Release(value); - return RBUS_ERROR_INVALID_INPUT; } - rbusProperty_SetValue(property, value); rbusValue_Release(value); return RBUS_ERROR_SUCCESS; -} - -/*####################################################################### -# ..Network.Device.Radio.BSS.STA.X_AIRTIES_BeaconMetricsQuery() # -########################################################################*/ -static int bmquery_parse_chan_reports(rbusObject_t in, map_nb_bmquery_param_t *payload) -{ - rbusObject_t ocr; - rbusObject_t ocri; - rbusObject_t och; - rbusObject_t ochi; - map_op_class_t *ap_chan_rep; - uint32_t ap_chan_rep_cnt; - uint32_t class; - uint32_t channel; - rbusValueError_t rc; - - /* Input.APChannelReport */ - ocr = rbusObject_GetChildren(in); - if (payload->channel != 255) { - if (ocr) { - log_lib_i("APChannelReport is ignored"); - } - return 0; - } - if (!ocr) { - log_lib_e("APChannelReport object is mandatory"); - return -1; - } - /* Input.APChannelReport.{i} */ - ocri = rbusObject_GetChildren(ocr); - if (!ocri) { - log_lib_e("APChannelReport instance is mandatory"); - return -1; - } - - ap_chan_rep_cnt = 0; - do { - /* Input.APChannelReport.{i}.OperatingClass */ - rc = rbusObject_GetPropertyUInt32(ocri, "OperatingClass", &class); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("OperatingClass is mandatory"); - return -1; - } - - ++ap_chan_rep_cnt; - ap_chan_rep = &payload->ap_chan_reports[ap_chan_rep_cnt - 1]; - ap_chan_rep->op_class = class; - - /* Input.APChannelReport.{i}.Channel */ - och = rbusObject_GetChildren(ocri); - if (!och) { - log_lib_e("Channel object is mandatory"); - return -1; - } - /* Input.APChannelReport.{i}.Channel.{i} */ - ochi = rbusObject_GetChildren(och); - if (!och) { - log_lib_e("Channel instance is mandatory"); - return -1; - } - - do { - /* Input.APChannelReport.{i}.Channel.{i}.Channel */ - rc = rbusObject_GetPropertyUInt32(ochi, "Channel", &channel); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("Channel is mandatory"); - return -1; - } - map_cs_set(&ap_chan_rep->channels, channel); - - ochi = rbusObject_GetNext(ochi); - } while (ochi); - - ocri = rbusObject_GetNext(ocri); - } while (ocri) ; - payload->ap_chan_reports_nr = ap_chan_rep_cnt; - - return 0; -} - -static int bmquery_parse_elementid_list(rbusObject_t in, map_nb_bmquery_param_t *payload) -{ - const char *sval; - char buf[4] = {0}; - uint8_t pos; - int len; - rbusValueError_t rc; - - rc = rbusObject_GetPropertyString(in, "ElementIDList", &sval, &len); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - return 0; - } - if (payload->reporting_detail != 2/*MAP_BEACON_REPORT_DETAIL_ALL*/) { - log_lib_i("ElementIDList is ignored"); - return 0; - } - - pos = 0; - payload->element_ids_nr = 0; - while (len) { - if (*sval >= '0' && *sval <= '9') { - if (pos >= 4) { - /* Invalid input */ - memset(buf, 0, pos); - pos = 0; - } - buf[pos++] = *sval; - } else { - if (pos) { - payload->element_ids[payload->element_ids_nr++] = atoi(buf); - if (payload->element_ids_nr == 255) { - return 0; - } - memset(buf, 0, pos); - } - pos = 0; - } - ++sval; - --len; - } - if (pos) { - payload->element_ids[payload->element_ids_nr++] = atoi(buf); - } - return 0; +invalid_input: + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; } -static rbusError_t sta_bmquery_rbus(rbusHandle_t handle, char const* method, rbusObject_t in, rbusObject_t out, rbusMethodAsyncHandle_t async) +/*####################################################################### +# Device.WiFi.DataElements.Network.Device.APMLD # +########################################################################*/ +static void dm_rbus_create_ap_mld(map_ap_mld_info_t *ap_mld) { - (void) handle; - char param[MAX_PROP_PARAM_LEN] = {0}; - bool is_num; - map_ale_info_t *ale; - map_radio_info_t *radio; - map_bss_info_t *bss; - map_sta_info_t *sta; - rbusMethodAsyncHandle_t prev; - map_nb_bmquery_param_t payload = {0}; - rbusValueError_t rc; - const char *sval; - uint32_t uval; - int len; - - rbusObject_SetName(out, "Output"); + unsigned int ale_idx; + unsigned int ap_mld_idx; + char tname[256] = {0}; + rbusError_t rc; - method += sizeof(DM_DEVICE_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); - ale = dm_get_ale(param, is_num); - if (ale == NULL) { - log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); - goto bail; + if (check_dev_dm_idx(ap_mld->ale) < 0) { + log_lib_e("invalid indexing for ap_mld"); + return; } - method += sizeof(DM_RADIO_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); - radio = dm_get_radio(ale, param, is_num); - if (radio == NULL) { - log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); - goto bail; + ale_idx = ap_mld->ale->dm_idx; + if (get_ap_mld_dm_idx(ale_idx, ap_mld, &ap_mld_idx) < 0) { + log_lib_e("could not find free index for ap_mld: %s", ap_mld->mac_str); + return; } - method += sizeof(DM_BSS_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); - bss = dm_get_bss(radio, param, is_num); - if (bss == NULL) { - log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); - goto bail; - } + /* Register new ap_mld to data model */ + snprintf(tname, sizeof(tname), + "Device.WiFi.DataElements.Network.Device.%d.APMLD.", ale_idx + 1); - method += sizeof(DM_STA_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); - sta = dm_get_sta(bss, param, is_num); - if (sta == NULL) { - log_lib_e("Invalid sta %s: %s", is_num ? "index" : "alias", param); - goto bail; + log_lib_d("Create row %s%d", tname, ap_mld_idx + 1); + rc = rbusTable_registerRow(g_bus_handle, tname, ap_mld_idx + 1, ap_mld->mac_str); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to create row[%d] for ap_mld: %s", + ap_mld_idx + 1, ap_mld->mac_str); } - maccpy(payload.sta_mac, sta->mac); +} - get_sta_dm_bmquery_reply(sta, &prev); - if (prev) { - log_lib_e("BeaconMetricsQuery is not finished yet"); - rbus_add_prop_str(out, "Status", "Error_Not_Ready"); - return RBUS_ERROR_INVALID_METHOD; - } +static void dm_rbus_remove_ap_mld(map_ap_mld_info_t *ap_mld) +{ + unsigned int ale_idx; + unsigned int ap_mld_idx; + char tname[256] = {0}; + rbusError_t rc; - rc = rbusObject_GetPropertyUInt32(in, "OperatingClass", &uval); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("OperatingClass is mandatory"); - goto bail; - } - if (uval > 255) { - log_lib_e("Invalid OperatingClass: %d", uval); - goto bail; + if (ap_mld->dm_removed) { + return; } - payload.op_class = uval; - rc = rbusObject_GetPropertyUInt32(in, "Channel", &uval); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("Channel is mandatory"); - goto bail; - } - if (uval > 255) { - log_lib_e("Invalid Channel: %d", uval); - goto bail; + if (check_ap_mld_dm_idx(ap_mld) < 0) { + log_lib_e("Invalid indexing for ap_mld"); + return; } - payload.channel = uval; - rc = rbusObject_GetPropertyString(in, "SSID", &sval, &len); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("SSID is mandatory"); - goto bail; - } - len = len > (MAX_SSID_LEN - 1) ? (MAX_SSID_LEN - 1) : len; - strncpy(payload.ssid, sval, len); + ap_mld_idx = ap_mld->dm_idx; + ale_idx = ap_mld->ale->dm_idx; - /* Optional arguments */ - rc = rbusObject_GetPropertyString(in, "BSSID", &sval, &len); - if (rc == RBUS_VALUE_ERROR_SUCCESS) { - if (mac_from_string(sval, payload.bssid) < 0) { - log_lib_e("Invalid BSSID: %s", sval); - goto bail; - } - } else { - mac_from_string("ff:ff:ff:ff:ff:ff", payload.bssid); - } + /* Delete ap_mld row with index */ + snprintf(tname, sizeof(tname), + "Device.WiFi.DataElements.Network.Device.%d.APMLD.%d", + ale_idx + 1, ap_mld_idx + 1); - rc = rbusObject_GetPropertyUInt32(in, "ReportingDetail", &uval); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - payload.reporting_detail = 1/*MAP_BEACON_REPORT_DETAIL_REQUESTED*/; - } else { - if (uval > 2/*MAP_BEACON_REPORT_DETAIL_ALL*/) { - log_lib_e("Invalid ReportingDetail: %d", uval); - goto bail; - } - payload.reporting_detail = uval; + log_lib_d("Delete row %s", tname); + rc = rbusTable_unregisterRow(g_bus_handle, tname); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to delete row[%d] for ap_mld: %s", + ap_mld_idx + 1, ap_mld->mac_str); } - if (bmquery_parse_chan_reports(in, &payload)) { - goto bail; - } + free_ap_mld_dm_idx(ale_idx, ap_mld_idx); - if (bmquery_parse_elementid_list(in, &payload)) { - goto bail; + /* Mark child objects are removed */ + mark_aff_aps_removed(ap_mld); + mark_sta_mlds_removed(ap_mld); +} + +static rbusError_t apmld_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_ap_mld_info_t *ap_mld; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - /* Everything is fine, clear previous reports */ - if (sta->beacon_metrics != NULL) { - while (list_get_size(sta->beacon_metrics) > 0) { - free(remove_last_object(sta->beacon_metrics)); - } + name += sizeof(DM_AP_MLD_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ap_mld = dm_get_ap_mld(ale, param, is_num); + if (ap_mld == NULL) { + log_lib_e("Invalid ap_mld %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - sta->bmquery_status = 0; - if (map_dm_get_nbapi()->beacon_metrics_query != NULL) { - map_dm_get_nbapi()->beacon_metrics_query(ale, &payload); + sscanf(name, "%s", param); - set_sta_dm_bmquery_reply(sta, async); + rbusValue_Init(&value); + + if (strcmp(param, "MLDMACAddress") == 0) { + rbusValue_SetString(value, ap_mld->mac_str); + } else if (strcmp(param, "AffiliatedAPNumberOfEntries") == 0) { + rbusValue_SetUInt32(value, ap_mld->aff_ap_nr); + } else if (strcmp(param, "STAMLDNumberOfEntries") == 0) { + rbusValue_SetUInt32(value, ap_mld->sta_mld_nr); } else { - log_lib_e("BeaconMetricsQuery is not implemented yet"); - rbus_add_prop_str(out, "Status", "Error_Not_Ready"); - return RBUS_ERROR_INVALID_METHOD; + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; } - return RBUS_ERROR_ASYNC_RESPONSE; + rbusProperty_SetValue(property, value); + rbusValue_Release(value); -bail: - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - return RBUS_ERROR_INVALID_INPUT; + return RBUS_ERROR_SUCCESS; } /*####################################################################### -# Device.WiFi.DataElements.Network.Device.Radio.BSS.STA.ClientSteer() # +# Device.WiFi.DataElements.Network.Device.APMLD.APMLDConfig # ########################################################################*/ -static rbusError_t sta_client_steer(rbusHandle_t handle, char const* method, rbusObject_t in, rbusObject_t out, rbusMethodAsyncHandle_t async) +static rbusError_t amconfig_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { (void) handle; - rbusError_t ret = RBUS_ERROR_BUS_ERROR; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; map_ale_info_t *ale; - map_radio_info_t *radio; - map_bss_info_t *bss; - map_sta_info_t *sta; - map_nb_client_steer_params_t payload = {0}; - const char *targetBSS; - const char *requestMode; - rbusValueError_t rc; - int len; - bool BTMDisassociationImminent; - bool BTMAbridged; - bool LinkRemovalImminent; - uint32_t steeringOpportunityWindow; - uint32_t TargetBSSChannel; - uint32_t BTMDisassociationTimer; - uint32_t TargetBSSOperatingClass; - uint32_t reasonCode; - map_sta_steering_history_t *steering_history = NULL; - - rbusObject_SetName(out, "Output"); + map_ap_mld_info_t *ap_mld; - method += sizeof(DM_DEVICE_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); ale = dm_get_ale(param, is_num); if (ale == NULL) { log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); return RBUS_ERROR_INVALID_INPUT; } - method += sizeof(DM_RADIO_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); - radio = dm_get_radio(ale, param, is_num); - if (radio == NULL) { - log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + name += sizeof(DM_AP_MLD_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ap_mld = dm_get_ap_mld(ale, param, is_num); + if (ap_mld == NULL) { + log_lib_e("Invalid ap_mld %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - method += sizeof(DM_BSS_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); - bss = dm_get_bss(radio, param, is_num); - if (bss == NULL) { - log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - return RBUS_ERROR_INVALID_INPUT; - } + sscanf(name, "APMLDConfig.%s", param); - method += sizeof(DM_STA_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); - sta = dm_get_sta(bss, param, is_num); - if (sta == NULL) { - log_lib_e("Invalid sta %s: %s", is_num ? "index" : "alias", param); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - return RBUS_ERROR_INVALID_INPUT; - } - maccpy(payload.target.sta_mac, sta->mac); - maccpy(payload.bssid, bss->bssid); + rbusValue_Init(&value); - sscanf(method, "%s", param); - rc = rbusObject_GetPropertyString(in, "TargetBSS", &targetBSS, &len); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("TargetBSS is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + if (strcmp(param, "EMLMREnabled") == 0) { + rbusValue_SetBoolean(value, ap_mld->enabled_mld_modes.emlmr); + } else if (strcmp(param, "EMLSREnabled") == 0) { + rbusValue_SetBoolean(value, ap_mld->enabled_mld_modes.emlsr); + } else if (strcmp(param, "STREnabled") == 0) { + rbusValue_SetBoolean(value, ap_mld->enabled_mld_modes.str); + } else if (strcmp(param, "NSTREnabled") == 0) { + rbusValue_SetBoolean(value, ap_mld->enabled_mld_modes.nstr); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); return RBUS_ERROR_INVALID_INPUT; } - mac_from_string(targetBSS, payload.target.bssid); - rc = rbusObject_GetPropertyString(in, "RequestMode", &requestMode, &len); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("RequestMode is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - return RBUS_ERROR_INVALID_INPUT; - } - if (strcasecmp(requestMode, "Steering_Mandate") == 0) { - payload.flags |= NB_STEERING_REQUEST_FLAG_MANDATE; - } - else if (strcasecmp(requestMode, "Steering_Opportunity") == 0){ - rc = rbusObject_GetPropertyUInt32(in, "SteeringOpportunityWindow", &steeringOpportunityWindow); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("SteeringOpportunityWindow is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - return RBUS_ERROR_INVALID_INPUT; - } - payload.opportunity_wnd = steeringOpportunityWindow; - } - else { - log_lib_e("SteeringOpportunityWindow value is not valid"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# Device.WiFi.DataElements.Network.Device.APMLD.AffiliatedAP # +########################################################################*/ +static rbusError_t affap_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_ap_mld_info_t *ap_mld; + map_bss_info_t *aff_ap; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - rc = rbusObject_GetPropertyBoolean(in, "BTMDisassociationImminent", &BTMDisassociationImminent); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("RequestMode is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + name += sizeof(DM_AP_MLD_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ap_mld = dm_get_ap_mld(ale, param, is_num); + if (ap_mld == NULL) { + log_lib_e("Invalid ap_mld %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - payload.flags |= BTMDisassociationImminent ? NB_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT : 0; - rc = rbusObject_GetPropertyBoolean(in, "BTMAbridged", &BTMAbridged); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("BTMAbridged is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + name += sizeof(DM_AFF_AP_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + aff_ap = dm_get_aff_ap(ap_mld, param, is_num); + if (aff_ap == NULL) { + log_lib_e("Invalid aff_ap %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - payload.flags |= BTMAbridged ? NB_STEERING_REQUEST_FLAG_BTM_ABRIDGED : 0; - rc = rbusObject_GetPropertyBoolean(in, "LinkRemovalImminent", &LinkRemovalImminent); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("LinkRemovalImminent is optional, skipping"); - } + sscanf(name, "%s", param); - rc = rbusObject_GetPropertyUInt32(in, "BTMDisassociationTimer", &BTMDisassociationTimer); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("BTMDisassociationTimer is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - return RBUS_ERROR_INVALID_INPUT; - } - // Check BTMDisassociationTimer is in limits - payload.disassociation_timer = BTMDisassociationTimer; + rbusValue_Init(&value); - rc = rbusObject_GetPropertyUInt32(in, "TargetBSSOperatingClass", &TargetBSSOperatingClass); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("TargetBSSOperatingClass is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + if (strcmp(param, "BSSID") == 0) { + rbusValue_SetString(value, aff_ap->bssid_str); + } else if (strcmp(param, "LinkID") == 0) { + rbusValue_SetUInt32(value, aff_ap->link_id); + } else if (strcmp(param, "RUID") == 0) { + rbusValue_SetString(value, aff_ap->radio->radio_id_base64); + } else if (strcmp(param, "PacketsReceived") == 0) { + rbusValue_SetUInt32(value, 0); /* TODO */ + } else if (strcmp(param, "PacketsSent") == 0) { + rbusValue_SetUInt32(value, 0); /* TODO */ + } else if (strcmp(param, "ErrorsSent") == 0) { + rbusValue_SetUInt32(value, 0); /* TODO */ + } else if (strcmp(param, "UnicastBytesReceived") == 0) { + rbusValue_SetUInt32(value, 0); /* TODO */ + } else if (strcmp(param, "UnicastBytesSent") == 0) { + rbusValue_SetUInt32(value, 0); /* TODO */ + } else if (strcmp(param, "MulticastBytesReceived") == 0) { + rbusValue_SetUInt32(value, 0); /* TODO */ + } else if (strcmp(param, "MulticastBytesSent") == 0) { + rbusValue_SetUInt32(value, 0); /* TODO */ + } else if (strcmp(param, "BroadcastBytesReceived") == 0) { + rbusValue_SetUInt32(value, 0); /* TODO */ + } else if (strcmp(param, "BroadcastBytesSent") == 0) { + rbusValue_SetUInt32(value, 0); /* TODO */ + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); return RBUS_ERROR_INVALID_INPUT; } - // Check TargetBSSOperatingClass is valid - payload.target.op_class = TargetBSSOperatingClass; - rc = rbusObject_GetPropertyUInt32(in, "TargetBSSChannel", &TargetBSSChannel); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("TargetBSSChannel is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - return RBUS_ERROR_INVALID_INPUT; + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# Device.WiFi.DataElements.Network.Device.APMLD.STAMLD # +########################################################################*/ +static void dm_rbus_create_sta_mld(map_sta_mld_info_t *sta_mld) +{ + unsigned int ale_idx; + unsigned int ap_mld_idx; + unsigned int sta_mld_idx; + char tname[256] = {0}; + rbusError_t rc; + + if (check_ap_mld_dm_idx(sta_mld->ap_mld) < 0) { + log_lib_e("invalid indexing for sta_mld"); + return; } - payload.target.channel = TargetBSSChannel; - rc = rbusObject_GetPropertyUInt32(in, "ReasonCode", &reasonCode); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("ReasonCode is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); - return RBUS_ERROR_INVALID_INPUT; + ap_mld_idx = sta_mld->ap_mld->dm_idx; + ale_idx = sta_mld->ap_mld->ale->dm_idx; + + if (get_sta_mld_dm_idx(ale_idx, ap_mld_idx, sta_mld, &sta_mld_idx) < 0) { + log_lib_e("could not find free index for sta_mld: %s", sta_mld->mac_str); + return; } - payload.target.reason = reasonCode; - steering_history = (map_sta_steering_history_t *)calloc(1, sizeof(map_sta_steering_history_t)); - if (steering_history == NULL) { - log_lib_e("Can't allocate memory for steering history"); - ret = RBUS_ERROR_OUT_OF_RESOURCES; - goto bail; + /* Register new ap_mld to data model */ + snprintf(tname, sizeof(tname), + "Device.WiFi.DataElements.Network.Device.%d.APMLD.%d.STAMLD.", + ale_idx + 1, ap_mld_idx + 1); + + log_lib_d("Create row %s%d", tname, sta_mld_idx + 1); + rc = rbusTable_registerRow(g_bus_handle, tname, sta_mld_idx + 1, sta_mld->mac_str); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to create row[%d] for sta_mld: %s", + sta_mld_idx + 1, sta_mld->mac_str); } +} - insert_last_object(sta->steering_history, steering_history); - sta->steering_history_size_delta++; // This is not nice here, should be created in datamodel code. +static void dm_rbus_remove_sta_mld(map_sta_mld_info_t *sta_mld) +{ + unsigned int ale_idx; + unsigned int ap_mld_idx; + unsigned int sta_mld_idx; + char tname[256] = {0}; + rbusError_t rc; - if (map_dm_get_nbapi()->client_steer != NULL) { - map_dm_get_nbapi()->client_steer(ale, &payload); + if (sta_mld->dm_removed) { + return; + } - if (ale->last_sta_steered) { - log_lib_e("There is another active station being steered! Will override!"); - } - ale->last_sta_steered = sta; + if (check_sta_mld_dm_idx(sta_mld) < 0) { + log_lib_e("Invalid indexing for sta_mld"); + return; + } - steering_history->start_time = acu_get_epoch_nsec(); - maccpy(steering_history->ap_origin, bss->bssid); - steering_history->steering_approach = MAP_STEERING_APPROACH_BTM_REQUEST; - steering_history->trigger_event = MAP_STEERING_TRIGGER_EVENT_WIFI_LINK_QUALITY; + sta_mld_idx = sta_mld->dm_idx; + ap_mld_idx = sta_mld->ap_mld->dm_idx; + ale_idx = sta_mld->ap_mld->ale->dm_idx; - sta->steering_stats.btm_attempts++; + /* Delete sta_mld row with index */ + snprintf(tname, sizeof(tname), + "Device.WiFi.DataElements.Network.Device.%d.APMLD.%d.STAMLD.%d", + ale_idx + 1, ap_mld_idx + 1, sta_mld_idx + 1); - map_dm_rbus_client_steer_async_reply_set(sta, async); - } else { - log_lib_e("ClientSteer is not implemented yet"); - rbus_add_prop_str(out, "Status", "Error_Not_Ready"); - return RBUS_ERROR_INVALID_METHOD; + log_lib_d("Delete row %s", tname); + rc = rbusTable_unregisterRow(g_bus_handle, tname); + if (rc != RBUS_ERROR_SUCCESS) { + log_lib_e("Failed to delete row[%d] for sta_mld: %s", + sta_mld_idx + 1, sta_mld->mac_str); } - return RBUS_ERROR_ASYNC_RESPONSE; + free_sta_mld_dm_idx(ale_idx, ap_mld_idx, sta_mld_idx); -bail: - return ret; + /* Mark child objects are removed */ + mark_aff_stas_removed(sta_mld); } -/*####################################################################### -# ..Network.Device.Radio.BSS.STA.MultiAPSTA.Disassociate() # -########################################################################*/ -static rbusError_t mapsta_disassociate(rbusHandle_t handle, char const* method, rbusObject_t in, rbusObject_t out, rbusMethodAsyncHandle_t async) +static rbusError_t stamld_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { (void) handle; - (void) async; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; map_ale_info_t *ale; - map_radio_info_t *radio; + map_ap_mld_info_t *ap_mld; + map_sta_mld_info_t *sta_mld; + map_sta_traffic_stats_t *ts; map_bss_info_t *bss; - map_sta_info_t *sta; - map_nb_sta_disassociate_params_t payload = {0}; - rbusValueError_t rc; - uint32_t disassociation_timer; - uint32_t reason_code; - bool silent; - - rbusObject_SetName(out, "Output"); + bool is_bsta = false; + uint32_t last_connect; - method += sizeof(DM_DEVICE_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); ale = dm_get_ale(param, is_num); if (ale == NULL) { log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); return RBUS_ERROR_INVALID_INPUT; } - method += sizeof(DM_RADIO_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); - radio = dm_get_radio(ale, param, is_num); - if (radio == NULL) { - log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + name += sizeof(DM_AP_MLD_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ap_mld = dm_get_ap_mld(ale, param, is_num); + if (ap_mld == NULL) { + log_lib_e("Invalid ap_mld %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - method += sizeof(DM_BSS_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); - bss = dm_get_bss(radio, param, is_num); - if (bss == NULL) { - log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + name += sizeof(DM_STA_MLD_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + sta_mld = dm_get_sta_mld(ap_mld, param, is_num); + if (sta_mld == NULL) { + log_lib_e("Invalid sta_mld %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - maccpy(payload.bssid, bss->bssid); + ts = &sta_mld->traffic_stats; - method += sizeof(DM_STA_PREFIX); - method = get_table_alias(method, param, MAX_PROP_PARAM_LEN, &is_num); - sta = dm_get_sta(bss, param, is_num); - if (sta == NULL) { - log_lib_e("Invalid sta %s: %s", is_num ? "index" : "alias", param); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + sscanf(name, "%s", param); + + rbusValue_Init(&value); + + if (strcmp(param, "MLDMACAddress") == 0) { + rbusValue_SetString(value, sta_mld->mac_str); + } else if (strcmp(param, "IsbSTA") == 0) { + /* Get first BSS to know if this is a backhaul */ + /* TODO: This does not work for mixed BH */ + if (!list_empty(&ap_mld->aff_ap_list)) { + bss = list_first_entry(&ap_mld->aff_ap_list, map_bss_info_t, aff_ap_list); + if (bss->type & MAP_BACKHAUL_BSS) { + is_bsta = true; + } + } + rbusValue_SetBoolean(value, is_bsta); + } else if (strcmp(param, "LastConnectTime") == 0) { + last_connect = map_dm_get_sta_assoc_ts_delta(sta_mld->assoc_ts); + rbusValue_SetUInt32(value, last_connect); + } else if (strcmp(param, "BytesReceived") == 0) { + rbusValue_SetUInt64(value, ts->rx_bytes); + } else if (strcmp(param, "BytesSent") == 0) { + rbusValue_SetUInt64(value, ts->tx_bytes); + } else if (strcmp(param, "PacketsReceived") == 0) { + rbusValue_SetUInt64(value, ts->rx_packets); + } else if (strcmp(param, "PacketsSent") == 0) { + rbusValue_SetUInt64(value, ts->tx_packets); + } else if (strcmp(param, "ErrorsReceived") == 0) { + rbusValue_SetUInt64(value, ts->rx_packet_errors); + } else if (strcmp(param, "ErrorsSent") == 0) { + rbusValue_SetUInt64(value, ts->tx_packet_errors); + } else if (strcmp(param, "RetransCount") == 0) { + rbusValue_SetUInt64(value, ts->retransmissions); + } else if (strcmp(param, "AffiliatedSTANumberOfEntries") == 0) { + rbusValue_SetUInt32(value, sta_mld->aff_sta_nr); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); return RBUS_ERROR_INVALID_INPUT; } - maccpy(payload.sta_mac, sta->mac); - sscanf(method, "%s", param); + rbusProperty_SetValue(property, value); + rbusValue_Release(value); - rc = rbusObject_GetPropertyUInt32(in, "DisassociationTimer", &disassociation_timer); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("DisassociationTimer is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# ..WiFi.DataElements.Network.Device.APMLD.STAMLD.WiFi7Capabilities # +########################################################################*/ +static rbusError_t smwf7cap_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_ap_mld_info_t *ap_mld; + map_sta_mld_info_t *sta_mld; + + name += sizeof(DM_DEVICE_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - payload.disassociation_timer = disassociation_timer; // todo diassociation timer is not actively used - rc = rbusObject_GetPropertyUInt32(in, "ReasonCode", &reason_code); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("ReasonCode is mandatory"); - rbus_add_prop_str(out, "Status", "Error_Invalid_Input"); + name += sizeof(DM_AP_MLD_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + ap_mld = dm_get_ap_mld(ale, param, is_num); + if (ap_mld == NULL) { + log_lib_e("Invalid ap_mld %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - payload.reason_code = reason_code; - rc = rbusObject_GetPropertyBoolean(in, "Silent", &silent); - if (rc != RBUS_VALUE_ERROR_SUCCESS) { - log_lib_e("Silent is optional, skipping"); + name += sizeof(DM_STA_MLD_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + sta_mld = dm_get_sta_mld(ap_mld, param, is_num); + if (sta_mld == NULL) { + log_lib_e("Invalid sta_mld %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; } - else - payload.silent = silent; // todo: silent is not used - /* Perform scan */ - if (map_dm_get_nbapi()->mapsta_disassociate != NULL) { - map_dm_get_nbapi()->mapsta_disassociate(ale, &payload); - rbus_add_prop_str(out, "Status", "Success"); - // todo: process async reply + sscanf(name, "WiFi7Capabilities.%s", param); + + rbusValue_Init(&value); + + if (strcmp(param, "EMLMRSupport") == 0) { + rbusValue_SetBoolean(value, sta_mld->supported_mld_modes.emlmr); + } else if (strcmp(param, "EMLSRSupport") == 0) { + rbusValue_SetBoolean(value, sta_mld->supported_mld_modes.emlsr); + } else if (strcmp(param, "STRSupport") == 0) { + rbusValue_SetBoolean(value, sta_mld->supported_mld_modes.str); + } else if (strcmp(param, "NSTRSupport") == 0) { + rbusValue_SetBoolean(value, sta_mld->supported_mld_modes.nstr); } else { - log_lib_e("Disassociate is not implemented yet"); - rbus_add_prop_str(out, "Status", "Error_Not_Ready"); - return RBUS_ERROR_INVALID_METHOD; + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; } + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + return RBUS_ERROR_SUCCESS; } /*####################################################################### -# ..Network.Device.Radio.BSS.STA.MultiAPSTA.SteeringSummaryStats # +# Device.WiFi.DataElements.Network.Device.APMLD.STAMLD.STAMLDConfig # ########################################################################*/ -static rbusError_t steering_sum_stats_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t smconfig_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { (void) handle; (void) opts; @@ -6343,9 +9679,8 @@ static rbusError_t steering_sum_stats_get_rbus(rbusHandle_t handle, rbusProperty char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; map_ale_info_t *ale; - map_radio_info_t *radio; - map_bss_info_t *bss; - map_sta_info_t *sta; + map_ap_mld_info_t *ap_mld; + map_sta_mld_info_t *sta_mld; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -6355,54 +9690,34 @@ static rbusError_t steering_sum_stats_get_rbus(rbusHandle_t handle, rbusProperty return RBUS_ERROR_INVALID_INPUT; } - name += sizeof(DM_RADIO_PREFIX); - name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); - radio = dm_get_radio(ale, param, is_num); - if (radio == NULL) { - log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); - return RBUS_ERROR_INVALID_INPUT; - } - - name += sizeof(DM_BSS_PREFIX); + name += sizeof(DM_AP_MLD_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); - bss = dm_get_bss(radio, param, is_num); - if (bss == NULL) { - log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); + ap_mld = dm_get_ap_mld(ale, param, is_num); + if (ap_mld == NULL) { + log_lib_e("Invalid ap_mld %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - name += sizeof(DM_STA_PREFIX); + name += sizeof(DM_STA_MLD_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); - sta = dm_get_sta(bss, param, is_num); - if (sta == NULL) { - log_lib_e("Invalid sta %s: %s", is_num ? "index" : "alias", param); + sta_mld = dm_get_sta_mld(ap_mld, param, is_num); + if (sta_mld == NULL) { + log_lib_e("Invalid sta_mld %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - log_lib_e("Address sta %p", (void *)sta); - - sscanf(name, "MultiAPSTA.SteeringSummaryStats.%s", param); + sscanf(name, "STAMLDConfig.%s", param); rbusValue_Init(&value); - if (strcmp(param, "NoCandidateAPFailures") == 0) { - rbusValue_SetUInt64(value, sta->steering_stats.no_candidate_apfailures); - } else if (strcmp(param, "BlacklistAttempts") == 0) { - rbusValue_SetUInt64(value, sta->steering_stats.blacklist_attempts); - } else if (strcmp(param, "BlacklistSuccesses") == 0) { - rbusValue_SetUInt64(value, sta->steering_stats.blacklist_successes); - } else if (strcmp(param, "BlacklistFailures") == 0) { - rbusValue_SetUInt64(value, sta->steering_stats.blacklist_failures); - } else if (strcmp(param, "BTMAttempts") == 0) { - rbusValue_SetUInt64(value, sta->steering_stats.btm_attempts); - } else if (strcmp(param, "BTMSuccesses") == 0) { - rbusValue_SetUInt64(value, sta->steering_stats.btm_successes); - } else if (strcmp(param, "BTMFailures") == 0) { - rbusValue_SetUInt64(value, sta->steering_stats.btm_failures); - } else if (strcmp(param, "BTMQueryResponses") == 0) { - rbusValue_SetUInt64(value, sta->steering_stats.btm_query_responses); - } else if (strcmp(param, "LastSteerTime") == 0) { - rbusValue_SetUInt32(value, sta->steering_stats.last_steer_time); + if (strcmp(param, "EMLMREnabled") == 0) { + rbusValue_SetBoolean(value, sta_mld->enabled_mld_modes.emlmr); + } else if (strcmp(param, "EMLSREnabled") == 0) { + rbusValue_SetBoolean(value, sta_mld->enabled_mld_modes.emlsr); + } else if (strcmp(param, "STREnabled") == 0) { + rbusValue_SetBoolean(value, sta_mld->enabled_mld_modes.str); + } else if (strcmp(param, "NSTREnabled") == 0) { + rbusValue_SetBoolean(value, sta_mld->enabled_mld_modes.nstr); } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -6416,20 +9731,22 @@ static rbusError_t steering_sum_stats_get_rbus(rbusHandle_t handle, rbusProperty } /*####################################################################### -# ..Network.Device.Radio.UnassociatedSTA # +# Device.WiFi.DataElements.Network.Device.APMLD.STAMLD.AffiliatedSTA # ########################################################################*/ -static rbusError_t unassoc_sta_get_rbus(rbusHandle_t handle, rbusProperty_t property, - rbusGetHandlerOptions_t *opts) +static rbusError_t affsta_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { - (void)handle; - (void)opts; + (void) handle; + (void) opts; rbusValue_t value; - char const *name = rbusProperty_GetName(property); - char param[MAX_PROP_PARAM_LEN] = { 0 }; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; map_ale_info_t *ale; - map_radio_info_t *radio; - map_unassociated_sta_info_t *unassoc_sta; + map_ap_mld_info_t *ap_mld; + map_sta_mld_info_t *sta_mld; + map_sta_info_t *aff_sta; + map_sta_link_metrics_t *lm; + map_sta_traffic_stats_t *ts; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -6439,34 +9756,56 @@ static rbusError_t unassoc_sta_get_rbus(rbusHandle_t handle, rbusProperty_t prop return RBUS_ERROR_INVALID_INPUT; } - name += sizeof(DM_RADIO_PREFIX); + name += sizeof(DM_AP_MLD_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); - radio = dm_get_radio(ale, param, is_num); - if (radio == NULL) { - log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); + ap_mld = dm_get_ap_mld(ale, param, is_num); + if (ap_mld == NULL) { + log_lib_e("Invalid ap_mld %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } - name += sizeof("UnassociatedSTA"); + name += sizeof(DM_STA_MLD_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); - unassoc_sta = dm_get_unassoc_sta(radio, param, is_num); - if (unassoc_sta == NULL) { - log_lib_e("Invalid unassoc sta %s: %s", is_num ? "index" : "alias", param); + sta_mld = dm_get_sta_mld(ap_mld, param, is_num); + if (sta_mld == NULL) { + log_lib_e("Invalid sta_mld %s: %s", is_num ? "index" : "alias", param); + return RBUS_ERROR_INVALID_INPUT; + } + + name += sizeof(DM_AFF_STA_PREFIX); + name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); + aff_sta = dm_get_aff_sta(sta_mld, param, is_num); + if (aff_sta == NULL) { + log_lib_e("Invalid aff_sta %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } + ts = &aff_sta->traffic_stats; + lm = aff_sta->metrics ? first_object(aff_sta->metrics) : NULL; sscanf(name, "%s", param); rbusValue_Init(&value); if (strcmp(param, "MACAddress") == 0) { - mac_addr_str mac_str; - mac_to_string(unassoc_sta->mac_address, mac_str); - rbusValue_SetString(value, mac_str); + rbusValue_SetString(value, aff_sta->mac_str); + } else if (strcmp(param, "BSSID") == 0) { + rbusValue_SetString(value, aff_sta->bss->bssid_str); + } else if (strcmp(param, "BytesReceived") == 0) { + rbusValue_SetUInt32(value, ts->rx_bytes); + } else if (strcmp(param, "BytesSent") == 0) { + rbusValue_SetUInt32(value, ts->tx_bytes); + } else if (strcmp(param, "PacketsReceived") == 0) { + rbusValue_SetUInt32(value, ts->rx_packets); + } else if (strcmp(param, "PacketsSent") == 0) { + rbusValue_SetUInt32(value, ts->tx_packets); + } else if (strcmp(param, "ErrorsSent") == 0) { + rbusValue_SetUInt32(value, ts->tx_packet_errors); } else if (strcmp(param, "SignalStrength") == 0) { - rbusValue_SetUInt32(value, unassoc_sta->signal_strength); - } else if (strcmp(param, "TimeStamp") == 0) { - rbusValue_SetString(value, unassoc_sta->timestamp); + rbusValue_SetUInt32(value, lm ? RSSI_TO_RCPI(lm->rssi) : 0); + } else if (strcmp(param, "EstMACDataRateDownlink") == 0) { + rbusValue_SetUInt32(value, lm ? lm->dl_mac_datarate : 0); + } else if (strcmp(param, "EstMACDataRateUplink") == 0) { + rbusValue_SetUInt32(value, lm ? lm->ul_mac_datarate : 0); } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -6480,40 +9819,11 @@ static rbusError_t unassoc_sta_get_rbus(rbusHandle_t handle, rbusProperty_t prop } /*####################################################################### -# ..STA.{i}.MultiAPSTA.SteeringHistory.{i}. # +# Device.WiFi.DataElements.Network.Device.bSTAMLD # ########################################################################*/ -static const char* map_dm_steering_history_trigger_event_str(map_steering_history_trigger_event_t te) -{ - switch (te) { - case MAP_STEERING_TRIGGER_EVENT_UNKNOWN: - return "Unknown"; - case MAP_STEERING_TRIGGER_EVENT_WIFI_CHANNEL_UTILIZATION: - return "Wi-Fi Utilization"; - case MAP_STEERING_TRIGGER_EVENT_WIFI_LINK_QUALITY: - return "Wi-Fi Link Quality"; - case MAP_STEERING_TRIGGER_EVENT_BACKHAUL_LINK_UTILIZATION: - return "Backhaul Link Utilization"; - default: - return NULL; - } -} - -static const char* map_dm_steering_history_approach_str(map_steering_approach_t a) -{ - switch (a) { - case MAP_STEERING_APPROACH_BLACKLIST: - return "Blacklist"; - case MAP_STEERING_APPROACH_BTM_REQUEST: - return "BTM Request"; - case MAP_STEERING_APPROACH_ASYNC_BTM_QUERY: - return "BTM Query"; - default: - return NULL; - } -} - -static rbusError_t steering_history_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +static rbusError_t bstamld_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) { +#define MAX_MAC_LIST_STR_LEN (MAX_MLD_AFF_APSTA * sizeof(mac_addr_str)) (void) handle; (void) opts; rbusValue_t value; @@ -6521,10 +9831,8 @@ static rbusError_t steering_history_get_rbus(rbusHandle_t handle, rbusProperty_t char param[MAX_PROP_PARAM_LEN] = {0}; bool is_num; map_ale_info_t *ale; - map_radio_info_t *radio; - map_bss_info_t *bss; - map_sta_info_t *sta; - uint32_t steering_history_index = 0; + map_bsta_mld_info_t *bsta_mld; + char mac_list[MAX_MAC_LIST_STR_LEN] = {0}; name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); @@ -6533,89 +9841,81 @@ static rbusError_t steering_history_get_rbus(rbusHandle_t handle, rbusProperty_t log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } + bsta_mld = &ale->bsta_mld; - name += sizeof(DM_RADIO_PREFIX); - name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); - radio = dm_get_radio(ale, param, is_num); - if (radio == NULL) { - log_lib_e("Invalid radio %s: %s", is_num ? "index" : "alias", param); - return RBUS_ERROR_INVALID_INPUT; - } + sscanf(name, "bSTAMLD.%s", param); - name += sizeof(DM_BSS_PREFIX); - name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); - bss = dm_get_bss(radio, param, is_num); - if (bss == NULL) { - log_lib_e("Invalid bss %s: %s", is_num ? "index" : "alias", param); + rbusValue_Init(&value); + + if (strcmp(param, "MLDMACAddress") == 0) { + rbusValue_SetString(value, bsta_mld->mac_str); + } else if (strcmp(param, "BSSID") == 0) { + rbusValue_SetString(value, mac_string(bsta_mld->ap_mld_mac)); + } else if (strcmp(param, "AffiliatedbSTAList") == 0) { + if (bsta_mld->aff_bsta_mac_nr) { + get_mac_list_str(bsta_mld->aff_bsta_macs, bsta_mld->aff_bsta_mac_nr, + mac_list, sizeof(mac_list)); + rbusValue_SetString(value, mac_list); + } else { + rbusValue_SetString(value, ""); + } + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); return RBUS_ERROR_INVALID_INPUT; } - name += sizeof(DM_STA_PREFIX); + rbusProperty_SetValue(property, value); + rbusValue_Release(value); + + return RBUS_ERROR_SUCCESS; +} + +/*####################################################################### +# Device.WiFi.DataElements.Network.Device.bSTAMLD.bSTAMLDConfig # +########################################################################*/ +static rbusError_t bstacfg_get_rbus(rbusHandle_t handle, rbusProperty_t property, rbusGetHandlerOptions_t* opts) +{ + (void) handle; + (void) opts; + rbusValue_t value; + char const* name = rbusProperty_GetName(property); + char param[MAX_PROP_PARAM_LEN] = {0}; + bool is_num; + map_ale_info_t *ale; + map_bsta_mld_info_t *bsta_mld; + + name += sizeof(DM_DEVICE_PREFIX); name = get_table_alias(name, param, MAX_PROP_PARAM_LEN, &is_num); - sta = dm_get_sta(bss, param, is_num); - if (sta == NULL) { - log_lib_e("Invalid sta %s: %s", is_num ? "index" : "alias", param); + ale = dm_get_ale(param, is_num); + if (ale == NULL) { + log_lib_e("Invalid device %s: %s", is_num ? "index" : "alias", param); return RBUS_ERROR_INVALID_INPUT; } + bsta_mld = &ale->bsta_mld; - sscanf(name, "MultiAPSTA.%s", param); + sscanf(name, "bSTAMLD.bSTAMLDConfig.%s", param); rbusValue_Init(&value); - if (strcmp(param, "SteeringHistoryNumberOfEntries") == 0) - { - rbusValue_SetUInt64(value, list_get_size(sta->steering_history)); + if (strcmp(param, "EMLMREnabled") == 0) { + rbusValue_SetBoolean(value, bsta_mld->enabled_mld_modes.emlmr); + } else if (strcmp(param, "EMLSREnabled") == 0) { + rbusValue_SetBoolean(value, bsta_mld->enabled_mld_modes.emlsr); + } else if (strcmp(param, "STREnabled") == 0) { + rbusValue_SetBoolean(value, bsta_mld->enabled_mld_modes.str); + } else if (strcmp(param, "NSTREnabled") == 0) { + rbusValue_SetBoolean(value, bsta_mld->enabled_mld_modes.nstr); + } else { + log_lib_e("Invalid param: %s", param); + rbusValue_Release(value); + return RBUS_ERROR_INVALID_INPUT; } - else - { - map_sta_steering_history_t *steering_history = NULL; - - if (sscanf(name, "MultiAPSTA.SteeringHistory.%d.%s", &steering_history_index,param) != 2) { - log_lib_e("Invalid path: %s, can't scan.", name); - goto invalid_input; - } - - steering_history = object_at_index(sta->steering_history, steering_history_index - 1); - if (!steering_history) { - log_lib_e("Invalid index for SteeringHistory : %u", steering_history_index); - goto invalid_input; - } - - if (strcmp(param, "Time") == 0) { - char timestamp[MAX_TS_STR_LEN] = {0}; - get_timestamp_str(steering_history->start_time, timestamp, sizeof(timestamp)); - rbusValue_SetString(value, timestamp); - } - else if (strcmp(param, "APOrigin") == 0) { - rbusValue_SetString(value, mac_string(steering_history->ap_origin)); - } - else if (strcmp(param, "APDestination") == 0) { - rbusValue_SetString(value, - maccmp(steering_history->ap_dest, g_zero_mac) ? mac_string(steering_history->ap_dest) : ""); - } - else if (strcmp(param, "TriggerEvent") == 0) { - rbusValue_SetString(value, map_dm_steering_history_trigger_event_str(steering_history->trigger_event)); - } - else if (strcmp(param, "SteeringApproach") == 0) { - rbusValue_SetString(value, map_dm_steering_history_approach_str(steering_history->steering_approach)); - } - else if (strcmp(param, "SteeringDuration") == 0) { - rbusValue_SetUInt64(value, steering_history->steering_duration); - } - else { - log_lib_e("Invalid param: %s", param); - goto invalid_input; - } - } rbusProperty_SetValue(property, value); rbusValue_Release(value); return RBUS_ERROR_SUCCESS; - -invalid_input: - rbusValue_Release(value); - return RBUS_ERROR_INVALID_INPUT; } /*####################################################################### @@ -6827,6 +10127,12 @@ static rbusError_t devinfo_get_rbus(rbusHandle_t handle, rbusProperty_t property } else { rbusValue_SetUInt32(value, 0); } + } else if (strcmp(param, "BootID") == 0) { + if (emex->enabled) { + rbusValue_SetUInt32(value, emex->device_info.boot_id); + } else { + rbusValue_SetUInt32(value, 0); + } } else { log_lib_e("Invalid param: %s", param); rbusValue_Release(value); @@ -7027,7 +10333,7 @@ static int event_pub_assoc(map_assoc_data_t *assoc) } /*####################################################################### -# ..DataElements.AssociationEvent.AssociationEventData. # +# ..DataElements.AssociationEvent.AssociationEventData # ########################################################################*/ static map_assoc_data_t *dm_get_assoc(uint16_t dm_idx) { @@ -7700,19 +11006,58 @@ static rbusError_t unassoc_sta_link_metrics_query(rbusHandle_t handle, char cons #define ssid_get_int ssid_get_rbus #define device_get_string device_get_rbus #define device_get_ulong device_get_rbus +#define device_set_string device_set_rbus +#define backhaul_get_string backhaul_get_rbus +#define bhstats_get_string bhstats_get_rbus +#define bhstats_get_ulong bhstats_get_rbus +#define cacstatus_get_string cacstatus_get_rbus +#define cacstatus_get_ulong cacstatus_get_rbus +#define cacavail_get_ulong cacavail_get_rbus +#define cacnonocc_get_ulong cacnonocc_get_rbus +#define cacactive_get_ulong cacactive_get_rbus #define radio_get_boolean radio_get_rbus #define radio_get_string radio_get_rbus #define radio_get_ulong radio_get_rbus +#define bhsta_get_string bhsta_get_rbus #define caps_get_string caps_get_rbus #define caps_get_ulong caps_get_rbus +#define wifi6ap_get_boolean wifi6ap_get_rbus +#define wifi6ap_get_string wifi6ap_get_rbus +#define wifi6ap_get_ulong wifi6ap_get_rbus +#define wifi6bsta_get_boolean wifi6bsta_get_rbus +#define wifi6bsta_get_string wifi6bsta_get_rbus +#define wifi6bsta_get_ulong wifi6bsta_get_rbus +#define wifi7ap_get_boolean wifi7ap_get_rbus +#define wifi7ap_get_ulong wifi7ap_get_rbus +#define apemlmr_get_string apemlmr_get_rbus +#define apemlmr_get_ulong apemlmr_get_rbus +#define apemlsr_get_string apemlsr_get_rbus +#define apemlsr_get_ulong apemlsr_get_rbus +#define apstr_get_string apstr_get_rbus +#define apstr_get_ulong apstr_get_rbus +#define apnstr_get_string apnstr_get_rbus +#define apnstr_get_ulong apnstr_get_rbus +#define wifi7bsta_get_boolean wifi7bsta_get_rbus +#define wifi7bsta_get_ulong wifi7bsta_get_rbus +#define bstaemlmr_get_string bstaemlmr_get_rbus +#define bstaemlmr_get_ulong bstaemlmr_get_rbus +#define bstaemlsr_get_string bstaemlsr_get_rbus +#define bstaemlsr_get_ulong bstaemlsr_get_rbus +#define bstastr_get_string bstastr_get_rbus +#define bstastr_get_ulong bstastr_get_rbus +#define bstanstr_get_string bstanstr_get_rbus +#define bstanstr_get_ulong bstanstr_get_rbus #define capop_get_ulong capop_get_rbus #define capop_get_int capop_get_rbus #define capop_get_string capop_get_rbus -#define bss_get_boolean bss_get_rbus -#define bss_get_string bss_get_rbus -#define bss_get_ulong bss_get_rbus -#define sta_get_string sta_get_rbus -#define sta_get_ulong sta_get_rbus +#define currop_get_ulong currop_get_rbus +#define currop_get_int currop_get_rbus +#define currop_get_string currop_get_rbus +#define disop_get_boolean disop_get_rbus +#define disop_get_string disop_get_rbus +#define disop_get_ulong disop_get_rbus +#define disop_set_boolean disop_set_rbus +#define disop_set_string disop_set_rbus #define scanres_get_string scanres_get_rbus #define scanres_get_ulong scanres_get_rbus #define opclscan_get_ulong opclscan_get_rbus @@ -7722,18 +11067,25 @@ static rbusError_t unassoc_sta_link_metrics_query(rbusHandle_t handle, char cons #define nbss_get_int nbss_get_rbus #define nbss_get_string nbss_get_rbus #define nbss_get_ulong nbss_get_rbus -#define currop_get_ulong currop_get_rbus -#define currop_get_int currop_get_rbus -#define currop_get_string currop_get_rbus -#define bhsta_get_string bhsta_get_rbus -#define backhaul_get_string backhaul_get_rbus -#define bhstats_get_string bhstats_get_rbus -#define bhstats_get_ulong bhstats_get_rbus -#define cacstatus_get_string cacstatus_get_rbus -#define cacstatus_get_ulong cacstatus_get_rbus -#define cacavail_get_ulong cacavail_get_rbus -#define cacnonocc_get_ulong cacnonocc_get_rbus -#define cacactive_get_ulong cacactive_get_rbus +#define bss_get_boolean bss_get_rbus +#define bss_get_string bss_get_rbus +#define bss_get_ulong bss_get_rbus +#define sta_get_string sta_get_rbus +#define sta_get_ulong sta_get_rbus +#define apmld_get_string apmld_get_rbus +#define apmld_get_ulong apmld_get_rbus +#define amconfig_get_boolean amconfig_get_rbus +#define affap_get_string affap_get_rbus +#define affap_get_ulong affap_get_rbus +#define stamld_get_boolean stamld_get_rbus +#define stamld_get_string stamld_get_rbus +#define stamld_get_ulong stamld_get_rbus +#define smwf7cap_get_boolean smwf7cap_get_rbus +#define smconfig_get_boolean smconfig_get_rbus +#define affsta_get_string affsta_get_rbus +#define affsta_get_ulong affsta_get_rbus +#define bstamld_get_string bstamld_get_rbus +#define bstacfg_get_boolean bstacfg_get_rbus #define ethernet_get_ulong ethernet_get_rbus #define ethiface_get_string ethiface_get_rbus #define ethiface_get_ulong ethiface_get_rbus @@ -7780,13 +11132,17 @@ static rbusError_t register_data_elements(rbusHandle_t handle) {DM_DEVICE_EXECENV, RBUS_ELEMENT_TYPE_PROPERTY, {device_get_string, NULL, NULL, NULL, NULL, NULL}}, {DM_DEVICE_CCODE, RBUS_ELEMENT_TYPE_PROPERTY, {device_get_string, NULL, NULL, NULL, NULL, NULL}}, {DM_DEVICE_MAPPROFILE, RBUS_ELEMENT_TYPE_PROPERTY, {device_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_DEVICE_BTMSTDASTALST, RBUS_ELEMENT_TYPE_PROPERTY, {device_get_string, device_set_string, NULL, NULL, NULL, NULL}}, + {DM_DEVICE_LCLSTDASTALST, RBUS_ELEMENT_TYPE_PROPERTY, {device_get_string, device_set_string, NULL, NULL, NULL, NULL}}, {DM_DEVICE_RADIONOE, RBUS_ELEMENT_TYPE_PROPERTY, {device_get_ulong, NULL, NULL, NULL, NULL, NULL}}, {DM_DEVICE_CACSTATUSNOE, RBUS_ELEMENT_TYPE_PROPERTY, {device_get_ulong, NULL, NULL, NULL, NULL, NULL}}, - {DM_DEVICE_UNASSOC_STA_QUERY,RBUS_ELEMENT_TYPE_METHOD, {NULL, NULL, NULL, NULL, NULL, unassoc_sta_link_metrics_query}}, + {DM_DEVICE_UNASSOCSTAQRY, RBUS_ELEMENT_TYPE_METHOD, {NULL, NULL, NULL, NULL, NULL, unassoc_sta_link_metrics_query}}, + {DM_DEVICE_REBOOT, RBUS_ELEMENT_TYPE_METHOD, {NULL, NULL, NULL, NULL, NULL, device_reboot_rbus}}, {DM_BACKHAUL_LINKTYPE, RBUS_ELEMENT_TYPE_PROPERTY, {backhaul_get_string, NULL, NULL, NULL, NULL, NULL}}, {DM_BACKHAUL_BHMACADDR, RBUS_ELEMENT_TYPE_PROPERTY, {backhaul_get_string, NULL, NULL, NULL, NULL, NULL}}, {DM_BACKHAUL_BHDEVICEID, RBUS_ELEMENT_TYPE_PROPERTY, {backhaul_get_string, NULL, NULL, NULL, NULL, NULL}}, {DM_BACKHAUL_MACADDRESS, RBUS_ELEMENT_TYPE_PROPERTY, {backhaul_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_BACKHAUL_STEERWIFIBH, RBUS_ELEMENT_TYPE_METHOD, {NULL, NULL, NULL, NULL, NULL, backhaul_steerwifibh_rbus}}, {DM_BHSTATS_BYTESRCVD, RBUS_ELEMENT_TYPE_PROPERTY, {bhstats_get_ulong, NULL, NULL, NULL, NULL, NULL}}, {DM_BHSTATS_BYTESSENT, RBUS_ELEMENT_TYPE_PROPERTY, {bhstats_get_ulong, NULL, NULL, NULL, NULL, NULL}}, {DM_BHSTATS_PACKETSRCVD, RBUS_ELEMENT_TYPE_PROPERTY, {bhstats_get_ulong, NULL, NULL, NULL, NULL, NULL}}, @@ -7835,6 +11191,88 @@ static rbusError_t register_data_elements(rbusHandle_t handle) {DM_CAPS_VHTCAPS, RBUS_ELEMENT_TYPE_PROPERTY, {caps_get_string, NULL, NULL, NULL, NULL, NULL}}, {DM_CAPS_HECAPS, RBUS_ELEMENT_TYPE_PROPERTY, {caps_get_string, NULL, NULL, NULL, NULL, NULL}}, {DM_CAPS_CAPOPCLASSNOE, RBUS_ELEMENT_TYPE_PROPERTY, {caps_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_HE160, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_HE8080, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_MCSNSS, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_SUBMFRMER, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_SUBMFRMEE, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_MUBMFRMER, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_BMFRMEEL80, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_BMFRMEEG80, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_ULMUMIMO, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_ULOFDMA, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_DLOFDMA, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_MXDLMUMIMO, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_MXULMUMIMO, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_MXDLOFDMA, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_MXULOFDMA, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_RTS, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_MURTS, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_MBSSID, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_MUEDCA, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_TWTREQ, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6AP_TWTRES, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_HE160, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_HE8080, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_MCSNSS, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_SUBMFRMER, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_SUBMFRMEE, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_MUBMFRMER, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_BMFRMEEL80, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_BMFRMEEG80, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_ULMUMIMO, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_ULOFDMA, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_DLOFDMA, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_MXDLMUMIMO, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_MXULMUMIMO, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_MXDLOFDMA, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_MXULOFDMA, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_RTS, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_MURTS, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_MBSSID, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_MUEDCA, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_TWTREQ, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI6BSTA_TWTRES, RBUS_ELEMENT_TYPE_PROPERTY, {wifi6bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7AP_EMLMRSUP, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7AP_EMLSRSUP, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7AP_STRSUP, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7AP_NSTRSUP, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7ap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7AP_EMLMRFSNOE, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7ap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7AP_EMLSRFSNOE, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7ap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7AP_STRFSNOE, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7ap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7AP_NSTRFSNOE, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7ap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_APEMLMRFS_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_APEMLMRFS_RUID, RBUS_ELEMENT_TYPE_PROPERTY, {apemlmr_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_APEMLMRFS_FREQSEP, RBUS_ELEMENT_TYPE_PROPERTY, {apemlmr_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_APEMLSRFS_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_APEMLSRFS_RUID, RBUS_ELEMENT_TYPE_PROPERTY, {apemlsr_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_APEMLSRFS_FREQSEP, RBUS_ELEMENT_TYPE_PROPERTY, {apemlsr_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_APSTRFS_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_APSTRFS_RUID, RBUS_ELEMENT_TYPE_PROPERTY, {apstr_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_APSTRFS_FREQSEP, RBUS_ELEMENT_TYPE_PROPERTY, {apstr_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_APNSTRFS_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_APNSTRFS_RUID, RBUS_ELEMENT_TYPE_PROPERTY, {apnstr_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_APNSTRFS_FREQSEP, RBUS_ELEMENT_TYPE_PROPERTY, {apnstr_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7BSTA_EMLMRSUP, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7BSTA_EMLSRSUP, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7BSTA_STRSUP, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7BSTA_NSTRSUP, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7bsta_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7BSTA_EMLMRFSNOE, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7bsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7BSTA_EMLSRFSNOE, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7bsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7BSTA_STRFSNOE, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7bsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_WIFI7BSTA_NSTRFSNOE, RBUS_ELEMENT_TYPE_PROPERTY, {wifi7bsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTAEMLMRFS_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTAEMLMRFS_RUID, RBUS_ELEMENT_TYPE_PROPERTY, {bstaemlmr_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTAEMLMRFS_FREQSEP, RBUS_ELEMENT_TYPE_PROPERTY, {bstaemlmr_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTAEMLSRFS_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTAEMLSRFS_RUID, RBUS_ELEMENT_TYPE_PROPERTY, {bstaemlsr_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTAEMLSRFS_FREQSEP, RBUS_ELEMENT_TYPE_PROPERTY, {bstaemlsr_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTASTRFS_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTASTRFS_RUID, RBUS_ELEMENT_TYPE_PROPERTY, {bstastr_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTASTRFS_FREQSEP, RBUS_ELEMENT_TYPE_PROPERTY, {bstastr_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTANSTRFS_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTANSTRFS_RUID, RBUS_ELEMENT_TYPE_PROPERTY, {bstanstr_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTANSTRFS_FREQSEP, RBUS_ELEMENT_TYPE_PROPERTY, {bstanstr_get_ulong, NULL, NULL, NULL, NULL, NULL}}, {DM_CAPOPCLASS_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, {DM_CAPOPCLASS_CLASS, RBUS_ELEMENT_TYPE_PROPERTY, {capop_get_ulong, NULL, NULL, NULL, NULL, NULL}}, {DM_CAPOPCLASS_MAXTSPOW, RBUS_ELEMENT_TYPE_PROPERTY, {capop_get_int, NULL, NULL, NULL, NULL, NULL}}, @@ -7845,6 +11283,10 @@ static rbusError_t register_data_elements(rbusHandle_t handle) {DM_CURROPCLASS_CHANNEL, RBUS_ELEMENT_TYPE_PROPERTY, {currop_get_ulong, NULL, NULL, NULL, NULL, NULL}}, {DM_CURROPCLASS_TXPOWER, RBUS_ELEMENT_TYPE_PROPERTY, {currop_get_int, NULL, NULL, NULL, NULL, NULL}}, {DM_CURROPCLASS_TSTAMP, RBUS_ELEMENT_TYPE_PROPERTY, {currop_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_DISOPCLASS_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_DISOPCLASS_ENABLE, RBUS_ELEMENT_TYPE_PROPERTY, {disop_get_boolean, disop_set_boolean, NULL, NULL, NULL, NULL}}, + {DM_DISOPCLASS_OPCLASS, RBUS_ELEMENT_TYPE_PROPERTY, {disop_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_DISOPCLASS_CHLIST, RBUS_ELEMENT_TYPE_PROPERTY, {disop_get_string, disop_set_string, NULL, NULL, NULL, NULL}}, {DM_SCANRES_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, {DM_SCANRES_TSTAMP, RBUS_ELEMENT_TYPE_PROPERTY, {scanres_get_string, NULL, NULL, NULL, NULL, NULL}}, {DM_SCANRES_OPCLASSNOE, RBUS_ELEMENT_TYPE_PROPERTY, {scanres_get_ulong, NULL, NULL, NULL, NULL, NULL}}, @@ -7928,8 +11370,67 @@ static rbusError_t register_data_elements(rbusHandle_t handle) {DM_STEERSUM_LASTSTEERTM, RBUS_ELEMENT_TYPE_PROPERTY, {steering_sum_stats_get_rbus, NULL, NULL, NULL, NULL, NULL}}, {DM_UNASSOC_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, {DM_UNASSOC_MAC, RBUS_ELEMENT_TYPE_PROPERTY, {unassoc_sta_get_rbus, NULL, NULL, NULL, NULL, NULL}}, - {DM_UNASSOC_SIGNALSTRENGTH, RBUS_ELEMENT_TYPE_PROPERTY, {unassoc_sta_get_rbus, NULL, NULL, NULL, NULL, NULL}}, + {DM_UNASSOC_SIGNALSTR, RBUS_ELEMENT_TYPE_PROPERTY, {unassoc_sta_get_rbus, NULL, NULL, NULL, NULL, NULL}}, {DM_UNASSOC_TIMESTAMP, RBUS_ELEMENT_TYPE_PROPERTY, {unassoc_sta_get_rbus, NULL, NULL, NULL, NULL, NULL}}, + {DM_APMLD_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_APMLD_MACADDRESS, RBUS_ELEMENT_TYPE_PROPERTY, {apmld_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_APMLD_AFFAPNOE, RBUS_ELEMENT_TYPE_PROPERTY, {apmld_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_APMLD_STAMLDNOE, RBUS_ELEMENT_TYPE_PROPERTY, {apmld_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AMCONFIG_EMLMR, RBUS_ELEMENT_TYPE_PROPERTY, {amconfig_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_AMCONFIG_EMLSR, RBUS_ELEMENT_TYPE_PROPERTY, {amconfig_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_AMCONFIG_STR, RBUS_ELEMENT_TYPE_PROPERTY, {amconfig_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_AMCONFIG_NSTR, RBUS_ELEMENT_TYPE_PROPERTY, {amconfig_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_BSSID, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_LINKID, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_RUID, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_PACKETSRCVD, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_PACKETSSENT, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_ERRORSSENT, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_UCBYTESRCVD, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_UCBYTESSENT, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_MCBYTESRCVD, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_MCBYTESSENT, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_BCBYTESRCVD, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFAP_BCBYTESSENT, RBUS_ELEMENT_TYPE_PROPERTY, {affap_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_MLDMACADDR, RBUS_ELEMENT_TYPE_PROPERTY, {stamld_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_ISBSTA, RBUS_ELEMENT_TYPE_PROPERTY, {stamld_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_LASTCONTIME, RBUS_ELEMENT_TYPE_PROPERTY, {stamld_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_BYTESRCVD, RBUS_ELEMENT_TYPE_PROPERTY, {stamld_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_BYTESSENT, RBUS_ELEMENT_TYPE_PROPERTY, {stamld_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_PACKETSRCVD, RBUS_ELEMENT_TYPE_PROPERTY, {stamld_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_PACKETSSENT, RBUS_ELEMENT_TYPE_PROPERTY, {stamld_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_ERRORSRCVD, RBUS_ELEMENT_TYPE_PROPERTY, {stamld_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_ERRORSSENT, RBUS_ELEMENT_TYPE_PROPERTY, {stamld_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_RETRANSCNT, RBUS_ELEMENT_TYPE_PROPERTY, {stamld_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_STAMLD_AFFSTANOE, RBUS_ELEMENT_TYPE_PROPERTY, {stamld_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_SMWF7CAP_EMLMR, RBUS_ELEMENT_TYPE_PROPERTY, {smwf7cap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_SMWF7CAP_EMLSR, RBUS_ELEMENT_TYPE_PROPERTY, {smwf7cap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_SMWF7CAP_STR, RBUS_ELEMENT_TYPE_PROPERTY, {smwf7cap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_SMWF7CAP_NSTR, RBUS_ELEMENT_TYPE_PROPERTY, {smwf7cap_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_SMCONFIG_EMLMR, RBUS_ELEMENT_TYPE_PROPERTY, {smconfig_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_SMCONFIG_EMLSR, RBUS_ELEMENT_TYPE_PROPERTY, {smconfig_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_SMCONFIG_STR, RBUS_ELEMENT_TYPE_PROPERTY, {smconfig_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_SMCONFIG_NSTR, RBUS_ELEMENT_TYPE_PROPERTY, {smconfig_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFSTA_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFSTA_MACADDRESS, RBUS_ELEMENT_TYPE_PROPERTY, {affsta_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFSTA_BSSID, RBUS_ELEMENT_TYPE_PROPERTY, {affsta_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFSTA_BYTESRCVD, RBUS_ELEMENT_TYPE_PROPERTY, {affsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFSTA_BYTESSENT, RBUS_ELEMENT_TYPE_PROPERTY, {affsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFSTA_PACKETSRCVD, RBUS_ELEMENT_TYPE_PROPERTY, {affsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFSTA_PACKETSSENT, RBUS_ELEMENT_TYPE_PROPERTY, {affsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFSTA_ERRORSSENT, RBUS_ELEMENT_TYPE_PROPERTY, {affsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFSTA_SIGNALSTR, RBUS_ELEMENT_TYPE_PROPERTY, {affsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFSTA_ESTMACDRDL, RBUS_ELEMENT_TYPE_PROPERTY, {affsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_AFFSTA_ESTMACDRUL, RBUS_ELEMENT_TYPE_PROPERTY, {affsta_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTAMLD_MACADDRESS, RBUS_ELEMENT_TYPE_PROPERTY, {bstamld_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTAMLD_BSSID, RBUS_ELEMENT_TYPE_PROPERTY, {bstamld_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTAMLD_AFFBSTALIST, RBUS_ELEMENT_TYPE_PROPERTY, {bstamld_get_string, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTACFG_EMLMR, RBUS_ELEMENT_TYPE_PROPERTY, {bstacfg_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTACFG_EMLSR, RBUS_ELEMENT_TYPE_PROPERTY, {bstacfg_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTACFG_STR, RBUS_ELEMENT_TYPE_PROPERTY, {bstacfg_get_boolean, NULL, NULL, NULL, NULL, NULL}}, + {DM_BSTACFG_NSTR, RBUS_ELEMENT_TYPE_PROPERTY, {bstacfg_get_boolean, NULL, NULL, NULL, NULL, NULL}}, {DM_ETHERNET_IFACENOE, RBUS_ELEMENT_TYPE_PROPERTY, {ethernet_get_ulong, NULL, NULL, NULL, NULL, NULL}}, {DM_ETHIFACE_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, {DM_ETHIFACE_MACADDR, RBUS_ELEMENT_TYPE_PROPERTY, {ethiface_get_string, NULL, NULL, NULL, NULL, NULL}}, @@ -7937,6 +11438,7 @@ static rbusError_t register_data_elements(rbusHandle_t handle) {DM_ETHDEVICE_TBL, RBUS_ELEMENT_TYPE_TABLE, {NULL, NULL, NULL, NULL, NULL, NULL}}, {DM_ETHDEVICE_MACADDR, RBUS_ELEMENT_TYPE_PROPERTY, {ethdevice_get_string, NULL, NULL, NULL, NULL, NULL}}, {DM_DEVINFO_UPTIME, RBUS_ELEMENT_TYPE_PROPERTY, {devinfo_get_ulong, NULL, NULL, NULL, NULL, NULL}}, + {DM_DEVINFO_BOOTID, RBUS_ELEMENT_TYPE_PROPERTY, {devinfo_get_ulong, NULL, NULL, NULL, NULL, NULL}}, {DM_MEMSTATUS_TOTAL, RBUS_ELEMENT_TYPE_PROPERTY, {memstat_get_ulong, NULL, NULL, NULL, NULL, NULL}}, {DM_MEMSTATUS_FREE, RBUS_ELEMENT_TYPE_PROPERTY, {memstat_get_ulong, NULL, NULL, NULL, NULL, NULL}}, {DM_MEMSTATUS_CACHED, RBUS_ELEMENT_TYPE_PROPERTY, {memstat_get_ulong, NULL, NULL, NULL, NULL, NULL}}, @@ -7969,7 +11471,7 @@ static rbusError_t register_data_elements(rbusHandle_t handle) cnt = sizeof(elements) / sizeof(rbusDataElement_t); rc = rbus_regDataElements(handle, cnt, elements); if (rc != RBUS_ERROR_SUCCESS) { - log_lib_e("rbus_regDataElements failed"); + log_lib_e("rbus_regDataElements failed[%d]: %s", rc, rbusError_ToString(rc)); } return rc; @@ -7995,6 +11497,12 @@ static map_dm_cbs_t g_dm_cbs = { .sta_update_cb = dm_rbus_update_sta, .sta_remove_cb = dm_rbus_remove_sta, + .ap_mld_create_cb = dm_rbus_create_ap_mld, + .ap_mld_remove_cb = dm_rbus_remove_ap_mld, + + .sta_mld_create_cb = dm_rbus_create_sta_mld, + .sta_mld_remove_cb = dm_rbus_remove_sta_mld, + .assoc_create_cb = dm_rbus_create_assoc, .assoc_remove_cb = dm_rbus_remove_assoc, @@ -8009,7 +11517,6 @@ int map_dm_rbus_init(void) { rbusError_t rc; rbusHandle_t handle; - uint8_t* mac = map_cfg_get()->controller_cfg.local_agent_al_mac; remove_all_ap_device(); @@ -8024,11 +11531,6 @@ int map_dm_rbus_init(void) } g_bus_handle = handle; - /* Create local agent so it gets index 0 */ - if (maccmp(mac, g_zero_mac) && maccmp(mac, g_wildcard_mac)) { - create_ale_mac(mac, NULL); - } - /* Register dm callbacks */ map_dm_register_cbs(&g_dm_cbs); diff --git a/source/libplatform/src/map_info.c b/source/libplatform/src/map_info.c index 087871f..a2c7fba 100644 --- a/source/libplatform/src/map_info.c +++ b/source/libplatform/src/map_info.c @@ -53,10 +53,14 @@ #define OP_CLASS_5G_HIGH_END OP_CLASS_5G_END #define OP_CLASS_6G_BEGIN 131 #define OP_CLASS_6G_END 137 -#define OP_CLASS_20MHZ_5G_LOW_BEGIN 115 -#define OP_CLASS_20MHZ_5G_LOW_END 120 -#define OP_CLASS_20MHZ_5G_HIGH_BEGIN 121 -#define OP_CLASS_20MHZ_5G_HIGH_END 127 +#define OP_CLASS_5G_20MHZ_LOW_BEGIN 115 +#define OP_CLASS_5G_20MHZ_LOW_END 120 +#define OP_CLASS_5G_20MHZ_HIGH_BEGIN 121 +#define OP_CLASS_5G_20MHZ_HIGH_END 127 + +#define OP_CLASS_5G_80P80 130 +#define OP_CLASS_6G_80P80 135 +#define OP_CLASS_6G_320MHZ 137 #define OP_CLASS_MIN OP_CLASS_2G_BEGIN #define OP_CLASS_MAX OP_CLASS_6G_END @@ -77,6 +81,7 @@ typedef struct _wifi_op_class_table { bool center_channel; uint8_t ext_channel; map_channel_set_t map_ch_set; + map_channel_set_t map_center_ch_set; } wifi_op_class_t; /*####################################################################### @@ -84,54 +89,54 @@ typedef struct _wifi_op_class_table { ########################################################################*/ /* NOTE: All channels in this table are control channels. */ static wifi_op_class_t g_wifi_op_class_table[] = { - { 81, {{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, IEEE80211_FREQUENCY_BAND_2_4_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}}, - { 82, {{ 14}, 1}, IEEE80211_FREQUENCY_BAND_2_4_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}}, - { 83, {{ 1, 2, 3, 4, 5, 6, 7, 8, 9}, 9}, IEEE80211_FREQUENCY_BAND_2_4_GHZ, 40, false, MAP_EXT_CHANNEL_ABOVE, {0}}, - { 84, {{ 5, 6, 7, 8, 9, 10, 11, 12, 13}, 9}, IEEE80211_FREQUENCY_BAND_2_4_GHZ, 40, false, MAP_EXT_CHANNEL_BELOW, {0}}, - {115, {{ 36, 40, 44, 48}, 4}, IEEE80211_FREQUENCY_BAND_5_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}}, - {116, {{ 36, 44}, 2}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_ABOVE, {0}}, - {117, {{ 40, 48}, 2}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_BELOW, {0}}, - {118, {{ 52, 56, 60, 64}, 4}, IEEE80211_FREQUENCY_BAND_5_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}}, - {119, {{ 52, 60}, 2}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_ABOVE, {0}}, - {120, {{ 56, 64}, 2}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_BELOW, {0}}, - {121, {{ 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144}, 12}, IEEE80211_FREQUENCY_BAND_5_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}}, - {122, {{ 100, 108, 116, 124, 132, 140}, 6}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_ABOVE, {0}}, - {123, {{ 104, 112, 120, 128, 136, 144}, 6}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_BELOW, {0}}, - {124, {{ 149, 153, 157, 161}, 4}, IEEE80211_FREQUENCY_BAND_5_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}}, - {125, {{ 149, 153, 157, 161, 165, 169, 173, 177}, 8}, IEEE80211_FREQUENCY_BAND_5_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}}, - {126, {{ 149, 157, 165, 173}, 4}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_ABOVE, {0}}, - {127, {{ 153, 161, 169, 177}, 4}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_BELOW, {0}}, + { 81, {{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, IEEE80211_FREQUENCY_BAND_2_4_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}, {0}}, + { 82, {{ 14}, 1}, IEEE80211_FREQUENCY_BAND_2_4_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}, {0}}, + { 83, {{ 1, 2, 3, 4, 5, 6, 7, 8, 9}, 9}, IEEE80211_FREQUENCY_BAND_2_4_GHZ, 40, false, MAP_EXT_CHANNEL_ABOVE, {0}, {0}}, + { 84, {{ 5, 6, 7, 8, 9, 10, 11, 12, 13}, 9}, IEEE80211_FREQUENCY_BAND_2_4_GHZ, 40, false, MAP_EXT_CHANNEL_BELOW, {0}, {0}}, + {115, {{ 36, 40, 44, 48}, 4}, IEEE80211_FREQUENCY_BAND_5_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}, {0}}, + {116, {{ 36, 44}, 2}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_ABOVE, {0}, {0}}, + {117, {{ 40, 48}, 2}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_BELOW, {0}, {0}}, + {118, {{ 52, 56, 60, 64}, 4}, IEEE80211_FREQUENCY_BAND_5_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}, {0}}, + {119, {{ 52, 60}, 2}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_ABOVE, {0}, {0}}, + {120, {{ 56, 64}, 2}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_BELOW, {0}, {0}}, + {121, {{ 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144}, 12}, IEEE80211_FREQUENCY_BAND_5_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}, {0}}, + {122, {{ 100, 108, 116, 124, 132, 140}, 6}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_ABOVE, {0}, {0}}, + {123, {{ 104, 112, 120, 128, 136, 144}, 6}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_BELOW, {0}, {0}}, + {124, {{ 149, 153, 157, 161}, 4}, IEEE80211_FREQUENCY_BAND_5_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}, {0}}, + {125, {{ 149, 153, 157, 161, 165, 169, 173, 177}, 8}, IEEE80211_FREQUENCY_BAND_5_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}, {0}}, + {126, {{ 149, 157, 165, 173}, 4}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_ABOVE, {0}, {0}}, + {127, {{ 153, 161, 169, 177}, 4}, IEEE80211_FREQUENCY_BAND_5_GHZ, 40, false, MAP_EXT_CHANNEL_BELOW, {0}, {0}}, {128, {{ 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, - 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173, 177}, 28}, IEEE80211_FREQUENCY_BAND_5_GHZ, 80, true, MAP_EXT_CHANNEL_NONE, {0}}, + 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173, 177}, 28}, IEEE80211_FREQUENCY_BAND_5_GHZ, 80, true, MAP_EXT_CHANNEL_NONE, {0}, {0}}, {129, {{ 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, - 149, 153, 157, 161, 165, 169, 173, 177}, 24}, IEEE80211_FREQUENCY_BAND_5_GHZ, 160, true, MAP_EXT_CHANNEL_NONE, {0}}, + 149, 153, 157, 161, 165, 169, 173, 177}, 24}, IEEE80211_FREQUENCY_BAND_5_GHZ, 160, true, MAP_EXT_CHANNEL_NONE, {0}, {0}}, {130, {{ 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, - 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173, 177}, 28}, IEEE80211_FREQUENCY_BAND_5_GHZ, 80+80+1, true, MAP_EXT_CHANNEL_NONE, {0}}, + 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173, 177}, 28}, IEEE80211_FREQUENCY_BAND_5_GHZ, 80+80+1, true, MAP_EXT_CHANNEL_NONE, {0}, {0}}, {131, {{ 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, 189, - 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233}, 59}, IEEE80211_FREQUENCY_BAND_6_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}}, + 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233}, 59}, IEEE80211_FREQUENCY_BAND_6_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}, {0}}, {132, {{ 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, 189, - 193, 197, 201, 205, 209, 213, 217, 221, 225, 229}, 58}, IEEE80211_FREQUENCY_BAND_6_GHZ, 40, true, MAP_EXT_CHANNEL_NONE, {0}}, + 193, 197, 201, 205, 209, 213, 217, 221, 225, 229}, 58}, IEEE80211_FREQUENCY_BAND_6_GHZ, 40, true, MAP_EXT_CHANNEL_NONE, {0}, {0}}, {133, {{ 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, 189, - 193, 197, 201, 205, 209, 213, 217, 221}, 56}, IEEE80211_FREQUENCY_BAND_6_GHZ, 80, true, MAP_EXT_CHANNEL_NONE, {0}}, + 193, 197, 201, 205, 209, 213, 217, 221}, 56}, IEEE80211_FREQUENCY_BAND_6_GHZ, 80, true, MAP_EXT_CHANNEL_NONE, {0}, {0}}, {134, {{ 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, 189, - 193, 197, 201, 205, 209, 213, 217, 221}, 56}, IEEE80211_FREQUENCY_BAND_6_GHZ, 160, true, MAP_EXT_CHANNEL_NONE, {0}}, + 193, 197, 201, 205, 209, 213, 217, 221}, 56}, IEEE80211_FREQUENCY_BAND_6_GHZ, 160, true, MAP_EXT_CHANNEL_NONE, {0}, {0}}, {135, {{ 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, 189, - 193, 197, 201, 205, 209, 213, 217, 221}, 56}, IEEE80211_FREQUENCY_BAND_6_GHZ, 80+80+1, true, MAP_EXT_CHANNEL_NONE, {0}}, - {136, {{ 2}, 1}, IEEE80211_FREQUENCY_BAND_6_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}}, + 193, 197, 201, 205, 209, 213, 217, 221}, 56}, IEEE80211_FREQUENCY_BAND_6_GHZ, 80+80+1, true, MAP_EXT_CHANNEL_NONE, {0}, {0}}, + {136, {{ 2}, 1}, IEEE80211_FREQUENCY_BAND_6_GHZ, 20, false, MAP_EXT_CHANNEL_NONE, {0}, {0}}, {137, {{ 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, 189, - 193, 197, 201, 205, 209, 213, 217, 221}, 56}, IEEE80211_FREQUENCY_BAND_6_GHZ, 320, true, MAP_EXT_CHANNEL_NONE, {0}}, + 193, 197, 201, 205, 209, 213, 217, 221}, 56}, IEEE80211_FREQUENCY_BAND_6_GHZ, 320, true, MAP_EXT_CHANNEL_NONE, {0}, {0}}, /* Not relevant operating classes: {85, {{ 1,2,3,4,5,6,7,8,9,10,11,12,13}, 13}, 7, 6}, @@ -202,9 +207,30 @@ int map_info_init(void) wifi_op_class_t *t = &g_wifi_op_class_table[i]; map_cs_unset_all(&t->map_ch_set); + map_cs_unset_all(&t->map_center_ch_set); for (j = 0; j < t->set.length; j++) { - map_cs_set(&t->map_ch_set, t->set.ch[j]); + uint8_t ctl_channel = t->set.ch[j]; + uint8_t center_channel; + + /* Control channels */ + map_cs_set(&t->map_ch_set, ctl_channel); + + /* Center channels */ + if (t->center_channel) { + if (map_is_6G_320MHz_op_class(t->op_class)) { + /* Add lower and higher center channels */ + foreach_bool(upper) { + if (!map_get_center_channel_6G_320MHz(t->op_class, upper, ctl_channel, ¢er_channel)) { + map_cs_set(&t->map_center_ch_set, center_channel); + } + } + } else { + if (!map_get_center_channel(t->op_class, ctl_channel, ¢er_channel)) { + map_cs_set(&t->map_center_ch_set, center_channel); + } + } + } } } @@ -257,21 +283,6 @@ static int get_channel_index(wifi_op_class_t *op_class, uint8_t channel) return -1; } -static uint8_t get_primary_channel_for_midfreq(uint8_t channel, uint16_t bw) -{ - if (bw == 40) { - return channel - 2; - } else if (bw == 80) { - return channel - 6; - } else if (bw == 160) { - return channel- 14; - } else if (bw == 320) { - return channel - 30; - } else { - return channel; - } -} - /*####################################################################### # PUBLIC FUNCTIONS # ########################################################################*/ @@ -289,9 +300,9 @@ int8_t map_get_frequency_type(uint8_t op_class, map_channel_set_t *channels, } else if (p_op_class->ch_freq == IEEE80211_FREQUENCY_BAND_5_GHZ) { *freq_type = IEEE80211_FREQUENCY_BAND_5_GHZ; - if (op_class >= OP_CLASS_20MHZ_5G_LOW_BEGIN && op_class <= OP_CLASS_20MHZ_5G_LOW_END) { + if (op_class >= OP_CLASS_5G_20MHZ_LOW_BEGIN && op_class <= OP_CLASS_5G_20MHZ_LOW_END) { *band_type_5G |= MAP_M2_BSS_RADIO5GL; - } else if (op_class >= OP_CLASS_20MHZ_5G_HIGH_BEGIN && op_class <= OP_CLASS_20MHZ_5G_HIGH_END) { + } else if (op_class >= OP_CLASS_5G_20MHZ_HIGH_BEGIN && op_class <= OP_CLASS_5G_20MHZ_HIGH_END) { *band_type_5G |= MAP_M2_BSS_RADIO5GU; } else if (op_class >= 128 && op_class <= 130) { uint8_t upper_band = 0; @@ -354,12 +365,22 @@ uint8_t map_get_op_class_20MHz(uint8_t channel, uint8_t ch_freq) bool map_is_5g_low_op_class(uint8_t op_class) { - return op_class >= OP_CLASS_20MHZ_5G_LOW_BEGIN && op_class <= OP_CLASS_20MHZ_5G_LOW_END; + return op_class >= OP_CLASS_5G_20MHZ_LOW_BEGIN && op_class <= OP_CLASS_5G_20MHZ_LOW_END; } bool map_is_5g_high_op_class(uint8_t op_class) { - return op_class >= OP_CLASS_20MHZ_5G_HIGH_BEGIN && op_class <= OP_CLASS_20MHZ_5G_HIGH_END; + return op_class >= OP_CLASS_5G_20MHZ_HIGH_BEGIN && op_class <= OP_CLASS_5G_20MHZ_HIGH_END; +} + +bool map_is_80p80_op_class(uint8_t op_class) +{ + return op_class == OP_CLASS_5G_80P80 || op_class == OP_CLASS_6G_80P80; +} + +bool map_is_6G_320MHz_op_class(uint8_t op_class) +{ + return op_class == OP_CLASS_6G_320MHZ; } int map_get_center_channel(uint8_t op_class, uint8_t ctl_channel, uint8_t *center_channel) @@ -405,17 +426,26 @@ int map_get_center_channel(uint8_t op_class, uint8_t ctl_channel, uint8_t *cente return 0; } -int map_get_first_ctl_channel(uint8_t op_class, uint8_t center_channel, uint8_t *ctl_channel) +int map_get_center_channel_6G_320MHz(uint8_t op_class, bool upper, uint8_t ctl_channel, uint8_t *center_channel) { wifi_op_class_t *p_op_class = get_op_class_ptr(op_class); + int channel_index, set_no, position; + uint8_t shift = upper ? 8 : 0; - /* Only for center channel operating classes */ - if (!map_is_channel_in_op_class(op_class, center_channel) || - !p_op_class || !p_op_class->center_channel) { + /* Check if center channel exists + - lowest ctl_channel for upper center_channel is 33 + - highest ctl_channel for lower center_channel is 189 + */ + + if (!p_op_class || !map_is_6G_320MHz_op_class(op_class) || + (channel_index = get_channel_index(p_op_class, ctl_channel)) < 0 || + (upper && ctl_channel < 33) || (!upper && ctl_channel > 189)) { return -1; } - *ctl_channel = get_primary_channel_for_midfreq(center_channel, p_op_class->bw); + set_no = (channel_index - shift) / 16; + position = set_no * 16 + shift; + *center_channel = p_op_class->set.ch[position] + 30; return 0; } @@ -433,7 +463,7 @@ int map_get_ext_channel_type(uint8_t op_class) * for 40MHz: primary channel + secondary channel * for 80MHz + 160MHz: all 20MHz channels a 80/160MHz channel exists of */ -int map_get_subband_channel_range(uint8_t op_class, uint8_t channel, uint8_t *from, uint8_t *to) +int map_get_subband_channel_range(uint8_t op_class, uint8_t ctl_channel, uint8_t *from, uint8_t *to) { wifi_op_class_t *p_op_class = get_op_class_ptr(op_class); uint16_t bw; @@ -446,7 +476,7 @@ int map_get_subband_channel_range(uint8_t op_class, uint8_t channel, uint8_t *fr bw = p_op_class->bw; if (p_op_class->center_channel) { - int channel_index = get_channel_index(p_op_class, channel); + int channel_index = get_channel_index(p_op_class, ctl_channel); if (channel_index < 0) { return -1; @@ -460,18 +490,18 @@ int map_get_subband_channel_range(uint8_t op_class, uint8_t channel, uint8_t *fr *from = p_op_class->set.ch[position]; *to = p_op_class->set.ch[position + sub_channels - 1]; } else if (bw == 20) { - *from = channel; - *to = channel; + *from = ctl_channel; + *to = ctl_channel; } else if (bw == 40) { int channel_type = map_get_ext_channel_type(op_class); - int secondary_channel = (channel_type == MAP_EXT_CHANNEL_BELOW) ? channel - 4 : channel + 4; + int secondary_channel = (channel_type == MAP_EXT_CHANNEL_BELOW) ? ctl_channel - 4 : ctl_channel + 4; if (channel_type == MAP_EXT_CHANNEL_NONE) { return -1; } - *from = (channel_type == MAP_EXT_CHANNEL_BELOW) ? secondary_channel : channel; - *to = (channel_type == MAP_EXT_CHANNEL_BELOW) ? channel : secondary_channel; + *from = (channel_type == MAP_EXT_CHANNEL_BELOW) ? secondary_channel : ctl_channel; + *to = (channel_type == MAP_EXT_CHANNEL_BELOW) ? ctl_channel : secondary_channel; } else { /* should not be possible */ return -1; @@ -480,7 +510,22 @@ int map_get_subband_channel_range(uint8_t op_class, uint8_t channel, uint8_t *fr return 0; } -bool map_is_channel_in_op_class(uint8_t op_class, uint8_t channel) +int map_get_subband_channel_range_6G_320MHz(uint8_t op_class, bool upper, uint8_t ctl_channel, uint8_t *from, uint8_t *to) +{ + uint8_t center_channel; + + if (!map_is_6G_320MHz_op_class(op_class) || + map_get_center_channel_6G_320MHz(op_class, upper, ctl_channel, ¢er_channel)) { + return -1; + } + + *from = center_channel - 30; + *to = center_channel + 30; + + return 0; +} + +bool map_is_channel_in_op_class(uint8_t op_class, uint8_t ctl_or_center_channel) { wifi_op_class_t *p_op_class = get_op_class_ptr(op_class); @@ -489,10 +534,10 @@ bool map_is_channel_in_op_class(uint8_t op_class, uint8_t channel) } if (p_op_class->center_channel) { - channel = get_primary_channel_for_midfreq(channel, p_op_class->bw); + return map_cs_is_set(&p_op_class->map_center_ch_set, ctl_or_center_channel); + } else { + return map_cs_is_set(&p_op_class->map_ch_set, ctl_or_center_channel); } - - return map_cs_is_set(&p_op_class->map_ch_set, channel); } int map_get_bw_from_op_class(uint8_t op_class, uint16_t *bw) @@ -532,35 +577,27 @@ int map_get_channel_set_from_op_class(uint8_t op_class, map_channel_set_t *ch_se { wifi_op_class_t *p_op_class = get_op_class_ptr(op_class); - if (p_op_class) { - map_cs_copy(ch_set, &p_op_class->map_ch_set); - } else { + if (!p_op_class) { map_cs_unset_all(ch_set); + return -EINVAL; } - return p_op_class ? 0 : -EINVAL; + map_cs_copy(ch_set, &p_op_class->map_ch_set); + + return 0; } int map_get_center_channel_set_from_op_class(uint8_t op_class, map_channel_set_t *ch_set) { - map_channel_set_t ch_set2; - bool is_center_channel; - uint8_t ctl_channel, center_channel; - - map_cs_unset_all(ch_set); + wifi_op_class_t *p_op_class = get_op_class_ptr(op_class); /* Only for center channel op classes */ - if (map_get_is_center_channel_from_op_class(op_class, &is_center_channel) || - !is_center_channel || - map_get_channel_set_from_op_class(op_class, &ch_set2)) { + if (!p_op_class || !p_op_class->center_channel) { + map_cs_unset_all(ch_set); return -EINVAL; } - map_cs_foreach(&ch_set2, ctl_channel) { - if (!map_get_center_channel(op_class, ctl_channel, ¢er_channel)) { - map_cs_set(ch_set, center_channel); - } - } + map_cs_copy(ch_set, &p_op_class->map_center_ch_set); return 0; } diff --git a/source/libplatform/src/map_timer_handler.c b/source/libplatform/src/map_timer_handler.c index ecf22da..1e84b76 100644 --- a/source/libplatform/src/map_timer_handler.c +++ b/source/libplatform/src/map_timer_handler.c @@ -34,6 +34,7 @@ typedef struct timer_cb_data_s { acu_evloop_timer_t *evloop_timer; timer_cb_t cb; void *args; + uint32_t frequency_ms; } timer_cb_data_t; /*####################################################################### @@ -56,7 +57,11 @@ static void timer_callback(void *userdata) { timer_cb_data_t *timer_data = userdata; - if (timer_data->cb(timer_data->timer_id, timer_data->args)) { + /* Remove timer if + * it removed itself from callback or + * it is a non-periodical timer(frequency = 0) + */ + if (timer_data->cb(timer_data->timer_id, timer_data->args) || timer_data->frequency_ms == 0) { /* Timer might have removed itself from callback */ if (remove_object(g_registered_callbacks, timer_data)) { log_lib_d("removed unregistered timer [%s] from list", timer_data->timer_id); @@ -129,6 +134,7 @@ int map_timer_register_callback(uint32_t frequency_sec, map_strlcpy(timer_data->timer_id, timer_id, sizeof(timer_data->timer_id)); timer_data->cb = cb; timer_data->args = args; + timer_data->frequency_ms = SEC_TO_MSEC(frequency_sec); timer_data->evloop_timer = acu_evloop_timer_add(SEC_TO_MSEC(frequency_sec), SEC_TO_MSEC(frequency_sec), timer_callback, timer_data); if (timer_data->evloop_timer == NULL) { @@ -148,6 +154,57 @@ int map_timer_register_callback(uint32_t frequency_sec, return status; } +int map_timer_register_callback_ms(uint32_t frequency_ms, + const char *timer_id, + void *args, + timer_cb_t cb) +{ + int status = 0; + timer_cb_data_t *timer_data = NULL; + + do { + if (cb == NULL || timer_id == NULL) { + ERROR_EXIT(status) + } + + size_t str_len = strlen(timer_id); + if (str_len >= MAX_TIMER_ID_STRING_LENGTH) { + ERROR_EXIT(status) + } + + /* If there is already a timer registered with the same ID return error */ + if (map_is_timer_registered(timer_id)) { + ERROR_EXIT(status) + } + + timer_data = calloc(1,sizeof(timer_cb_data_t)); + if (timer_data == NULL) { + ERROR_EXIT(status) + } + + map_strlcpy(timer_data->timer_id, timer_id, sizeof(timer_data->timer_id)); + timer_data->cb = cb; + timer_data->args = args; + timer_data->frequency_ms = frequency_ms; + + timer_data->evloop_timer = acu_evloop_timer_add(frequency_ms, frequency_ms, timer_callback, timer_data); + if (timer_data->evloop_timer == NULL) { + log_lib_e("failed to add timer"); + free_timer_data(timer_data); + ERROR_EXIT(status) + } + + if (push_object(g_registered_callbacks, (void*)timer_data) == -1) { + log_lib_e("failed to register timer callback"); + free_timer_data(timer_data); + ERROR_EXIT(status) + } + + } while (0); + + return status; +} + bool map_is_timer_registered(const char *timer_id) { return find_object(g_registered_callbacks, (void*)timer_id, compare_timer_node) != NULL; @@ -228,6 +285,9 @@ int map_timer_change_callback(const char *timer_id, uint32_t frequency_sec, void if (!(timer_data = find_object(g_registered_callbacks, (void*)timer_id, compare_timer_node))) { log_lib_e("timer [%s] isn't registered yet", timer_id); ERROR_EXIT(status) + } else if (timer_data->frequency_ms == 0) { + log_lib_e("timer [%s] isn't periodical", timer_id); + ERROR_EXIT(status) } else { acu_evloop_timer_change_period(timer_data->evloop_timer, SEC_TO_MSEC(frequency_sec)); timer_data->args = args; diff --git a/source/libplatform/src/map_utils.c b/source/libplatform/src/map_utils.c index 655e7f3..48d8877 100644 --- a/source/libplatform/src/map_utils.c +++ b/source/libplatform/src/map_utils.c @@ -33,7 +33,7 @@ #include -#include "ccsp_trace.h" +//#include #include "map_utils.h" #include "map_config.h" @@ -68,6 +68,10 @@ static int get_module_loglevel(map_cfg_t *cfg, int module) static void map_vlog_file(int level, const char *format, va_list args) { + (void) level; + (void) format; + (void) args; + /* char buffer[1024]; vsnprintf(buffer, sizeof(buffer), format, args); @@ -91,6 +95,7 @@ static void map_vlog_file(int level, const char *format, va_list args) default: break; } + */ } void map_vlog_ext(int module, int level, bool check_level, const char *format, va_list args) @@ -115,7 +120,7 @@ void map_vlog_ext(int module, int level, bool check_level, const char *format, v vfprintf(stderr, format, args); fprintf(stderr, "\n"); } else { - vsyslog(level, format, args); + vsyslog(level > LOG_DEBUG ? LOG_DEBUG : level, format, args); } } diff --git a/source/test/CMakeLists.txt b/source/test/CMakeLists.txt new file mode 100644 index 0000000..2b2c0ed --- /dev/null +++ b/source/test/CMakeLists.txt @@ -0,0 +1,112 @@ +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +cmake_minimum_required(VERSION 2.8.12) + +project(multiap-controller_test) + +set(SCHEMA_PATH "dummy" CACHE STRING "JSON schema path") + +set(CMAKE_BUILD_TYPE "Debug") + +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) + +find_package(OpenSSL REQUIRED) +find_package(Threads REQUIRED) +find_package(CHECK REQUIRED) +find_package(LIBUBOX REQUIRED) +find_package(JSONC REQUIRED) + +add_definitions(-O0 -Wall -Werror -D_GNU_SOURCE -DUNIT_TEST) + +if(OPENSSL_VERSION VERSION_GREATER "3.0.0") + add_definitions(-Wno-deprecated-declarations) +endif() + +find_program(JSONSCHEMA_TOOL jsonschema) +if(JSONSCHEMA_TOOL) + add_definitions(-DJSONSCHEMA_TOOL=\"${JSONSCHEMA_TOOL}\") +endif() + + +# Module paths +set(CONTROLLER_DIR ${CMAKE_SOURCE_DIR}/../controller) +set(CONTROLLER_SRC_DIR ${CONTROLLER_DIR}/src) +set(CONTROLLER_INCLUDE_DIR ${CONTROLLER_DIR}/include) +set(LIBUTILS_DIR ${CMAKE_SOURCE_DIR}/../libplatform) +set(LIBUTILS_SRC_DIR ${LIBUTILS_DIR}/src) +set(LIBUTILS_INCLUDE_DIR ${LIBUTILS_DIR}/include) +set(IEEE1905_DIR ${CMAKE_SOURCE_DIR}/../ieee1905) +set(IEEE1905_SRC_DIR ${IEEE1905_DIR}/src) +set(IEEE1905_INCLUDE_DIRS + ${IEEE1905_SRC_DIR}/al/internal_interfaces + ${IEEE1905_SRC_DIR}/al/src_independent + ${IEEE1905_DIR}/src/al/src_independent/extensions/map/ + ${IEEE1905_DIR}/src/common/interfaces + ${IEEE1905_DIR}/src/factory/interfaces + ${IEEE1905_DIR}/src/factory/interfaces/extensions/map +) + + +# ieee1905 factory sources +set(IEEE1905_FACTORY_SRCS + ${IEEE1905_SRC_DIR}/factory/src_independent/1905_tlvs.c + ${IEEE1905_SRC_DIR}/factory/src_independent/lldp_tlvs.c + ${IEEE1905_SRC_DIR}/factory/src_independent/extensions/map/map_r1_tlvs.c + ${IEEE1905_SRC_DIR}/factory/src_independent/extensions/map/map_r2_tlvs.c + ${IEEE1905_SRC_DIR}/factory/src_independent/extensions/map/map_r3_tlvs.c + ${IEEE1905_SRC_DIR}/factory/src_independent/extensions/map/map_r4_tlvs.c + ${IEEE1905_SRC_DIR}/factory/src_independent/extensions/map/map_r6_tlvs.c + ${IEEE1905_SRC_DIR}/factory/src_independent/1905_cmdus.c + ${IEEE1905_SRC_DIR}/factory/src_independent/lldp_payload.c + ${IEEE1905_SRC_DIR}/factory/src_independent/media_specific_blobs.c +) + + +# Common include dirs +set(UNIT_TEST_INCLUDES + ${CMAKE_SOURCE_DIR}/common + ${CMAKE_SOURCE_DIR}/common/stub + ${LIBUTILS_INCLUDE_DIR} + ${IEEE1905_INCLUDE_DIRS} + ${CONTROLLER_INCLUDE_DIR} + ${OPENSSL_INCLUDE_DIR} + ${LIBUBOX_INCLUDE_DIRS} + ${JSONC_INCLUDE_DIRS} + ${CHECK_INCLUDE_DIRS} +) + + +# Common libraries +set(UNIT_TEST_LIBS + unit_test + ${CHECK_LIBRARIES} + ${LIBUBOX_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${JSONC_LIBRARIES} + pcap + pthread rt m +) + + +# Tests +include(ValgrindSupport) # needs to configured before CTest include +include(CTest) # needs to be in top-level CMakeLists.txt + + +# Support for coverage reporting +include(CodeCoverage) +setup_target_for_coverage(test_coverage "${CMAKE_CTEST_COMMAND}" coverage_report "-VV;-R;unittest") +setup_target_for_coverage_cobertura(test_coverage_cobertura "${CMAKE_CTEST_COMMAND}" cobertura_report "-VV;-R;unittest") +setup_target_for_coverage_cobertura(test_coverage_cobertura_collect_only "" cobertura_report) + + +# Actual tests +enable_testing() +add_subdirectory(common) +add_subdirectory(libutils) +add_subdirectory(ieee1905) +add_subdirectory(controller) diff --git a/source/test/README.md b/source/test/README.md new file mode 100644 index 0000000..6f943e1 --- /dev/null +++ b/source/test/README.md @@ -0,0 +1,63 @@ +Unit Tests +========== + +# Introduction + +This tool attempts to verify the functionality of various parts of the EasyMesh controller. For this purpose, the "check" library is used, which is a unit test framework for C projects. The tool is designed to run on PC, so it is not cross-compiled for the target. + +# Prerequisites +In order to compile and run the tool the following packages must be installed, as well as the basic development tools like gcc, make and cmake. + +- check (Unit Testing Framework for C) +- valgrind (Instrumentation Framework for Building Dynamic Analysis Tools) +- gcov (a Test Coverage Program) + +# Structure +Unit test tool has the following folders. + +- cmake, contains files with directives needed for different packages cmake uses +- common, contains common code used by main controller modules +- libplatform, contains unit tests for libplatform module +- ieee1905, contains unit tests for ieee1905 module +- controller, contains unit tests for controller module +- project, contains external source packets and all build data + +All controller modules contain data and stub folders and their respective unit test codes focusing on that modules functionalities. "data" folder contains necessary data for unit tests to operate. Most of them are generated from actual network captures. "stub" folder contains code that take the place of the actual code which is not in the focus of the unit tests. + +# Usage +There are 2 shell scripts in the root folder to ease the usage of the tool. + +The shell script "setup.sh" will download and compile all necessary external sources (cJSON, json-c, libubox, uthash) needed for the unit test tool. After external sources are ready unit tests will be compiled. After successful build, the tool will be ready to use. + +"run.sh" is used to conduct unit tests. It is possible to run a specific tests as well as all tests at once. Tool also provides code coverage and memory analysis options while performing unit tests. + +# Tests + +Every item in the following list have their own unit tests covering that topic. + +## libplatform +- arraylist +- map_80211 +- map_blocklist +- map_channel_set +- map_cli_subscription +- map_datamodel +- map_dm_eth_device_list +- map_info + +## ieee1905 +- al_send +- al_wsc +- cmdus +- lldp_payload +- lldp_tlvs +- tlvs + +## controller +- chan_sel +- cli +- cmdu_rx +- cmdu_tx +- emex +- tlv_helper +- utils diff --git a/source/test/cmake/CodeCoverage.cmake b/source/test/cmake/CodeCoverage.cmake new file mode 100644 index 0000000..d49e666 --- /dev/null +++ b/source/test/cmake/CodeCoverage.cmake @@ -0,0 +1,200 @@ +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +# Copyright (c) 2012 - 2015, Lars Bilke +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim Söderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# USAGE: + +# 0. (Mac only) If you use Xcode 5.1 make sure to patch geninfo as described here: +# http://stackoverflow.com/a/22404544/80480 +# +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt: +# INCLUDE(CodeCoverage) +# +# 3. Set compiler flags to turn off optimization and enable coverage: +# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") +# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") +# +# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target +# which runs your test executable and produces a lcov code coverage report: +# Example: +# SETUP_TARGET_FOR_COVERAGE( +# my_coverage_target # Name for custom target. +# test_driver # Name of the test driver executable that runs the tests. +# # NOTE! This should always have a ZERO as exit code +# # otherwise the coverage generation will not complete. +# coverage # Name of output directory. +# ) +# +# 4. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# +# + +# Check prereqs +FIND_PROGRAM( GCOV_PATH gcov ) +FIND_PROGRAM( LCOV_PATH lcov ) +FIND_PROGRAM( GENHTML_PATH genhtml ) +FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests) +FIND_PROGRAM( PYTHON_EXECUTABLE python ) + +IF(NOT GCOV_PATH) + MESSAGE(FATAL_ERROR "gcov not found! Aborting...") +ENDIF() # NOT GCOV_PATH + +IF(NOT CMAKE_COMPILER_IS_GNUCC) + # Clang version 3.0.0 and greater now supports gcov as well. + MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.") + + IF(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") + ENDIF() +ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX + +SET(CMAKE_CXX_FLAGS_COVERAGE + "-g -O0 --coverage -fprofile-arcs -ftest-coverage" + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE ) +SET(CMAKE_C_FLAGS_COVERAGE + "-g -O0 --coverage -fprofile-arcs -ftest-coverage" + CACHE STRING "Flags used by the C compiler during coverage builds." + FORCE ) +SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE ) +SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE ) +MARK_AS_ADVANCED( + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) + +IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage")) + MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" ) +ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" + + +# Param _targetname The name of new the custom make target +# Param _testrunner The name of the target which runs the tests. +# MUST return ZERO always, even on errors. +# If not, no coverage report will be created! +# Param _outputname lcov output is generated as _outputname.info +# HTML report is generated in _outputname/index.html +# Optional fourth parameter is passed as arguments to _testrunner +# Pass them in list form, e.g.: "-j;2" for -j 2 +FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname) + + IF(NOT LCOV_PATH) + MESSAGE(FATAL_ERROR "lcov not found! Aborting...") + ENDIF() # NOT LCOV_PATH + + IF(NOT GENHTML_PATH) + MESSAGE(FATAL_ERROR "genhtml not found! Aborting...") + ENDIF() # NOT GENHTML_PATH + + # Setup target + ADD_CUSTOM_TARGET(${_targetname} + + # Cleanup lcov + ${LCOV_PATH} --directory . --zerocounters + + # Run tests + COMMAND ${_testrunner} ${ARGV3} + + # Capturing lcov counters and generating report + COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info + COMMAND ${LCOV_PATH} --remove ${_outputname}.info '/usr/include/*' --output-file ${_outputname}.info.cleaned + COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned + COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned + + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show info where to find the report + ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD + COMMAND ; + COMMENT "Open ${CMAKE_BINARY_DIR}/${_outputname}/index.html in your browser to view the coverage report." + ) + +ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE + +# Param _targetname The name of new the custom make target +# Param _testrunner The name of the target which runs the tests +# Param _outputname cobertura output is generated as _outputname.xml +# Optional fourth parameter is passed as arguments to _testrunner +# Pass them in list form, e.g.: "-j;2" for -j 2 +FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname) + + IF(NOT PYTHON_EXECUTABLE) + MESSAGE(FATAL_ERROR "Python not found! Aborting...") + ENDIF() # NOT PYTHON_EXECUTABLE + + IF(NOT GCOVR_PATH) + MESSAGE(FATAL_ERROR "gcovr not found! Aborting...") + ENDIF() # NOT GCOVR_PATH + + ADD_CUSTOM_TARGET(${_targetname} + + # Run tests + ${_testrunner} ${ARGV3} + + # Running gcovr + COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) + + # Show info where to find the report + ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${_outputname}.xml." + ) + +ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA diff --git a/source/test/cmake/FindCHECK.cmake b/source/test/cmake/FindCHECK.cmake new file mode 100644 index 0000000..6ab07c8 --- /dev/null +++ b/source/test/cmake/FindCHECK.cmake @@ -0,0 +1,44 @@ +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +# +# - Find Check unit test framnework +# +# CHECK_INCLUDE_DIRS - Where to find check headers, etc. +# CHECK_LIBRARIES - List of libraries when using Check. +# CHECK_FOUND - True if Check found. + + +if(CHECK_INCLUDE_DIRS) + # Already in cache, be silent + set(CHECK_FIND_QUIETLY TRUE) +endif(CHECK_INCLUDE_DIRS) + +find_path(CHECKC_INCLUDE_DIR check.h) +find_library(CHECK_LIBRARY NAMES check) + +# handle the QUIETLY and REQUIRED arguments and set CHECK_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CHECK DEFAULT_MSG CHECK_LIBRARY CHECKC_INCLUDE_DIR) + +if(CHECK_FOUND) + set(CHECK_LIBRARIES ${CHECK_LIBRARY}) + + # Check might depend on libsubunit.so + find_library(SUBUNIT_LIBRARY NAMES subunit) + + if (SUBUNIT_LIBRARY) + set(CHECK_LIBRARIES ${CHECK_LIBRARIES} ${SUBUNIT_LIBRARY}) + endif(SUBUNIT_LIBRARY) + + set(CHECK_INCLUDE_DIRS ${CHECKC_INCLUDE_DIR}) +else(CHECK_FOUND) + set(CHECK_LIBRARIES) + set(CHECK_INCLUDE_DIRS) +endif(CHECK_FOUND) + +mark_as_advanced(CHECK_INCLUDE_DIRS CHECK_LIBRARIES) diff --git a/source/test/cmake/FindJSONC.cmake b/source/test/cmake/FindJSONC.cmake new file mode 100644 index 0000000..171203d --- /dev/null +++ b/source/test/cmake/FindJSONC.cmake @@ -0,0 +1,41 @@ +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +# +# Find jsonc-c +# +# JSONC_INCLUDE_DIRS - where to find json-c/json.h, etc. +# JSONC_LIBRARIES - List of libraries when using json-c +# JSONC_FOUND - True if json-c found. + +if (NOT JSONC_INCLUDE_DIR) + find_path(JSONC_INCLUDE_DIR json-c/json.h) +endif() + +if (NOT JSONC_LIBRARY) + find_library( + JSONC_LIBRARY + NAMES libjson-c.so) +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args( + JSONC DEFAULT_MSG + JSONC_LIBRARY JSONC_INCLUDE_DIR) + +if(JSONC_FOUND) + set(JSONC_LIBRARIES ${JSONC_LIBRARY}) + set(JSONC_INCLUDE_DIRS ${JSONC_INCLUDE_DIR}) +else(JSONC_FOUND) + set(JSONC_LIBRARIES) + set(JSONC_INCLUDE_DIRS) +endif(JSONC_FOUND) + +message(STATUS "json-c include dir: ${JSONC_INCLUDE_DIRS}") +message(STATUS "json-c: ${JSONC_LIBRARIES}") + +mark_as_advanced(JSONC_INCLUDE_DIRS JSONC_LIBRARIES) diff --git a/source/test/cmake/FindLIBUBOX.cmake b/source/test/cmake/FindLIBUBOX.cmake new file mode 100644 index 0000000..e65c9db --- /dev/null +++ b/source/test/cmake/FindLIBUBOX.cmake @@ -0,0 +1,41 @@ +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +# +# Find libubox +# +# LIBUBOX_INCLUDE_DIRS - where to find libubox/uloop.h, etc. +# LIBUBOX_LIBRARIES - List of libraries when using libubox +# LIBUBOX_FOUND - True if libubox found. + +if (NOT LIBUBOX_INCLUDE_DIR) + find_path(LIBUBOX_INCLUDE_DIR libubox/uloop.h) +endif() + +if (NOT LIBUBOX_LIBRARY) + find_library( + LIBUBOX_LIBRARY + NAMES libubox.so) +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args( + LIBUBOX DEFAULT_MSG + LIBUBOX_LIBRARY LIBUBOX_INCLUDE_DIR) + +if(LIBUBOX_FOUND) + set(LIBUBOX_LIBRARIES ${LIBUBOX_LIBRARY}) + set(LIBUBOX_INCLUDE_DIRS ${LIBUBOX_INCLUDE_DIR}) +else(LIBUBOX_FOUND) + set(LIBUBOX_LIBRARIES) + set(LIBUBOX_INCLUDE_DIRS) +endif(LIBUBOX_FOUND) + +message(STATUS "libubox include dir: ${LIBUBOX_INCLUDE_DIRS}") +message(STATUS "libubox: ${LIBUBOX_LIBRARIES}") + +mark_as_advanced(LIBUBOX_INCLUDE_DIRS LIBUBOX_LIBRARIES) diff --git a/source/test/cmake/ValgrindSupport.cmake b/source/test/cmake/ValgrindSupport.cmake new file mode 100644 index 0000000..a33e607 --- /dev/null +++ b/source/test/cmake/ValgrindSupport.cmake @@ -0,0 +1,27 @@ +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +# The source code form of this Open Source Project components +# is subject to the terms of the Clear BSD license. +# You can redistribute it and/or modify it under the terms of the +# Clear BSD License (http://directory.fsf.org/wiki/License:ClearBSD) +# See COPYING file/LICENSE file for more details. +# This software project does also include third party Open Source Software: +# See COPYING file/LICENSE file for more details. + +# Support for Valgrind +find_program(VALGRIND_EXEC valgrind) +if (DEFINED VALGRIND_EXEC) + message(STATUS "Found Valgrind: ${VALGRIND_EXEC}") + set(CTEST_MEMORYCHECK_COMMAND ${VALGRIND_EXEC}) + set(SUPPRESSIONS_FILE "${PROJECT_SOURCE_DIR}/suppressions.vg" ) + # Valgrind settings for interactive use + set(MEMORYCHECK_DEV_COMMAND_OPTIONS --trace-children=no --num-callers=50 --leak-check=full --show-reachable=yes --track-origins=yes --suppressions=${SUPPRESSIONS_FILE}) + # Valgrind settings for Jenkins' Valgrind plug-in + set(MEMORYCHECK_COMMAND_OPTIONS "--trace-children=no --num-callers=50 --leak-check=full --show-reachable=yes --track-origins=yes --xml=yes --xml-file=valgrind-%p.xml --suppressions=${SUPPRESSIONS_FILE}") +else () + message(WARNING "Valgrind not found") +endif () diff --git a/source/test/common/CMakeLists.txt b/source/test/common/CMakeLists.txt new file mode 100644 index 0000000..f04593e --- /dev/null +++ b/source/test/common/CMakeLists.txt @@ -0,0 +1,59 @@ +########################################################################## +# Copyright (c) 2021-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +include(CheckUnitTest.cmake) + +# Shared library including all stubs. +# Linker will only use them if the real source file is not included +# in a test. + +set(STUB_COMMON + stub_acutils.c +) +list(TRANSFORM STUB_COMMON PREPEND ../common/stub/) + +set(STUB_LIBUTILS + stub_map_cli.c + stub_map_data_model.c + stub_map_dm_rbus.c + stub_map_retry_handler.c + stub_map_topology_tree.c + stub_map_blocklist.c + stub_map_config.c + stub_map_dm_eth_device_list.c + stub_map_timer_handler.c + stub_map_utils.c +) +list(TRANSFORM STUB_LIBUTILS PREPEND ../libutils/stub/) + +set(STUB_IEEE1905 + stub_i1905.c + stub_platform_os.c +) +list(TRANSFORM STUB_IEEE1905 PREPEND ../ieee1905/stub/) + +set(STUB_CONTROLLER + stub_map_ctrl_emex_tlv_handler.c + stub_map_ctrl_post_onboarding_handler.c + stub_map_ctrl_utils.c + stub_map_ctrl_chan_sel.c + stub_map_ctrl_tlv_helper.c + stub_map_ctrl_vendor.c + stub_map_ctrl_cmdu_tx.c + stub_map_ctrl_topology_tree.c + stub_map_ctrl_wfa_capi.c +) +list(TRANSFORM STUB_CONTROLLER PREPEND ../controller/stub/) + +SETUP_UNIT_TEST_LIB( + main.c + utils.c + + ${STUB_COMMON} + ${STUB_LIBUTILS} + ${STUB_IEEE1905} + ${STUB_CONTROLLER} +) diff --git a/source/test/common/CheckUnitTest.cmake b/source/test/common/CheckUnitTest.cmake new file mode 100644 index 0000000..b89b1a6 --- /dev/null +++ b/source/test/common/CheckUnitTest.cmake @@ -0,0 +1,75 @@ +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +function(SETUP_UNIT_TEST_LIB) + add_library(unit_test + STATIC + ${ARGN} + ) + + target_compile_definitions(unit_test + PRIVATE ${CONDUCTOR_UNIT_TEST_BUILD_FLAGS} + ) + + target_include_directories(unit_test + PRIVATE ${UNIT_TEST_INCLUDES} + ) +endfunction() # SETUP_UNIT_TEST_LIB + +function(SETUP_UNIT_TEST _name) + set(SRCS + test_${_name}.c + ${ARGN} + ) + + get_filename_component(UNIT_TEST_DIR ${CMAKE_CURRENT_LIST_DIR} NAME) + set(UNIT_TEST_NAME unittest_${UNIT_TEST_DIR}_${_name}) + + add_executable(${UNIT_TEST_NAME} + ${SRCS} + ) + + target_compile_definitions(${UNIT_TEST_NAME} + PRIVATE DATA_DIR="${CMAKE_CURRENT_LIST_DIR}/data" + ${UNIT_TEST_EXTRA_DEFINES} + ) + + target_include_directories(${UNIT_TEST_NAME} + PRIVATE ${UNIT_TEST_INCLUDES} + ) + + target_link_libraries(${UNIT_TEST_NAME} + ${UNIT_TEST_LIBS} + ) + + #For coverage only include files outside test dir + foreach(SRC ${SRCS}) + if (${SRC} MATCHES test/../) + set(COVERAGE_SRCS ${COVERAGE_SRCS} ${SRC}) + endif() + endforeach(SRC) + + set_source_files_properties( + ${COVERAGE_SRCS} + PROPERTIES + COMPILE_FLAGS ${CMAKE_C_FLAGS_COVERAGE} + ) + + set_target_properties(${UNIT_TEST_NAME} + PROPERTIES + LINK_FLAGS_DEBUG --coverage + ) + + add_test(NAME ${UNIT_TEST_NAME} COMMAND ${UNIT_TEST_NAME}) + + setup_target_for_coverage(${UNIT_TEST_NAME}_coverage "${CMAKE_CTEST_COMMAND}" coverage_report_${_name} "-VV;-R;${UNIT_TEST_NAME}") + if(VALGRIND_EXEC) + add_custom_target(${UNIT_TEST_NAME}_memcheck + ${VALGRIND_EXEC} ${MEMORYCHECK_DEV_COMMAND_OPTIONS} ./${UNIT_TEST_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + endif() +endfunction() # SETUP_UNIT_TEST diff --git a/source/test/common/main.c b/source/test/common/main.c new file mode 100644 index 0000000..8ac5aeb --- /dev/null +++ b/source/test/common/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include +#include +#include + +#include "test.h" + +/*####################################################################### +# GLOBALS # +########################################################################*/ +__attribute__((weak)) enum fork_status desired_fork_mode = CK_NOFORK; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static void teardown(void) +{ +} + +static Suite* create_suite(void) +{ + Suite *s; + /* to test only one function, pass function name to below parameter */ + char *test_name = getenv("SINGLE_TEST_LOG"); + s = suite_create(test_suite_name); + for (test_case_t *t = test_cases; NULL != t->name; t++) { + if (test_name) { + if (strcmp(test_name, t->name) != 0) { + continue; + } + } + TCase *tc = tcase_create(t->name); + if (0 != t->timeout) { + tcase_set_timeout(tc, t->timeout); + } +#ifdef CHECK_HAS_TTEST + _tcase_add_test(tc, *t->test, 0, 0, 0, 1); +#else + _tcase_add_test(tc, t->function, t->name, 0, 0, 0, 1); +#endif + tcase_add_checked_fixture(tc, NULL, teardown); + suite_add_tcase(s, tc); + } + return s; +} + +/*####################################################################### +# MAIN # +########################################################################*/ +int main(void) +{ + int number_failed; + SRunner *sr; + Suite *s; + char fname[128]; + + s = create_suite(); + sr = srunner_create(s); + + snprintf(fname, sizeof(fname), "%s-testresults.xml", test_suite_name); + srunner_set_xml(sr, fname); + srunner_set_fork_status(sr, desired_fork_mode); + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/source/test/common/stub/stub_acutils.c b/source/test/common/stub/stub_acutils.c new file mode 100644 index 0000000..171dba6 --- /dev/null +++ b/source/test/common/stub/stub_acutils.c @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2021-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +#include "acu_utils.h" + +/* Overrule some functions of libacutils.so */ +acu_evloop_fd_t *acu_evloop_fd_add(int fd, acu_evloop_fd_cb_t cb, void *userdata) +{ + return (acu_evloop_fd_t *)0xdeadbeef; +} + +void acu_evloop_fd_delete(acu_evloop_fd_t *evloop_fd) +{ +} diff --git a/source/test/common/test.h b/source/test/common/test.h new file mode 100644 index 0000000..4fb78a0 --- /dev/null +++ b/source/test/common/test.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +#ifndef UNITTEST_TEST_H_ +#define UNITTEST_TEST_H_ + +/*####################################################################### +# TEST FRAMEWORK # +########################################################################*/ +#include + +/* Prototype of _tcase_add_test changed from check 0.13 */ +#if (CHECK_MAJOR_VERSION > 0) || (CHECK_MINOR_VERSION >= 13) +#define CHECK_HAS_TTEST +#endif +#ifdef CHECK_HAS_TTEST +#define TEST(_name, _test) {.name = _name, .test = &_test} +#else +#define TEST(_name, _test) {.name = _name, .function = _test} +#endif +#define TEST_CASES_END {.name = NULL} + +typedef struct { + const char *name; +#ifdef CHECK_HAS_TTEST + const TTest **test; +#else + TFun function; +#endif + double timeout; // 0 = use default Check timeout of 4s; otherwise use provided value +} test_case_t; + +extern const char *test_suite_name; +extern test_case_t test_cases[]; + +/*####################################################################### +# USEFUL DEFINES AND TYPEDEFS # +########################################################################*/ +#include +#include + +#define STRUCT_PACKED __attribute__ ((packed)) +#define ETHERTYPE_1905 0x893a +#define MAX_PACKET_LEN 1514 + +typedef struct ether_header eth_hdr_t; + +typedef struct { + uint8_t message_version; + uint8_t resved_field; + uint16_t message_type; + uint16_t message_id; + uint8_t fragment_id; + uint8_t indicators; +} STRUCT_PACKED cmdu_hdr_t; + +typedef struct { + uint8_t type; + uint16_t len; +} STRUCT_PACKED tlv_hdr_t; + +typedef struct { + char if_name[IFNAMSIZ]; + uint16_t len; + uint8_t data[MAX_PACKET_LEN]; +} packet_t; + +/* Validate json message against schema */ +void validate_schema(const char *msg, const char *schema); + +/* Get first packet from pcap file */ +packet_t *pcap_read_first_packet(const char *file); + +packet_t **pcap_read_all_packets(const char *file, size_t *packets_nr); + +void free_packets(packet_t **p, size_t nr); + +#endif /* UNITTEST_TEST_H_ */ diff --git a/source/test/common/utils.c b/source/test/common/utils.c new file mode 100644 index 0000000..15339ff --- /dev/null +++ b/source/test/common/utils.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2017-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include +#include +#include + +#include + +#include "test.h" + +/*####################################################################### +# DEFINES # +########################################################################*/ +#define MILLISEC 1000 + +/*####################################################################### +# LOCAL FUNCTIONS # +########################################################################*/ +#ifdef JSONSCHEMA_TOOL +static uint64_t get_time_msec(void) +{ + struct timespec ts; + + if (0 != clock_gettime(CLOCK_BOOTTIME, &ts)) { + return 0; + } + return (((uint64_t)ts.tv_sec) * MILLISEC) + ((uint64_t)ts.tv_nsec / (MILLISEC * MILLISEC)); +} +#endif /* !JSONSCHEMA_TOOL */ + +/*####################################################################### +# PUBLIC FUNCTIONS # +########################################################################*/ +void validate_schema(const char *msg, const char *schema) +{ +#ifdef JSONSCHEMA_TOOL + uint64_t start_ts = get_time_msec(); + char filename[] = "/tmp/cr_schema_test.XXXXXX"; + int fd; + int rc; + char cmd[512]; + + if (NULL == getenv("UNITTEST_VALIDATE_SCHEMA")) { + return; + } + + fd = mkstemp(filename); + fail_unless(0 <= fd); + + write(fd, msg, strlen(msg)); + close(fd); + + sprintf(cmd, "%s -i %s %s 2> /dev/null", JSONSCHEMA_TOOL, filename, schema); + rc = system(cmd); + unlink(filename); + + if (0 != rc) { + fprintf(stderr,"JSON schena validation failed. Schema[%s] msg[%s].\n", schema, msg); + fail(); + } + + fprintf(stderr, "JSON schema validation ok. Schema[%s] time[%"PRIu64" ms].\n", schema, get_time_msec()-start_ts); +#else + fail_unless(NULL == getenv("UNITTEST_VALIDATE_SCHEMA"), "JSON schema validation not supported."); +#endif /* !JSONSCHEMA_TOOL */ +} + +packet_t *pcap_read_first_packet(const char *file) +{ + pcap_t *pcap = NULL; + packet_t *packet = NULL; + struct pcap_pkthdr pcap_hdr; + char err_buf[PCAP_ERRBUF_SIZE]; + const uint8_t *p; + + fail_unless(!!(pcap = pcap_open_offline(file, err_buf))); + fail_unless(!!(p = pcap_next(pcap, &pcap_hdr))); + fail_unless(pcap_hdr.caplen <= MAX_PACKET_LEN); + fail_unless(!!(packet = malloc(sizeof(packet_t)))); + + packet->len = pcap_hdr.caplen; + memcpy(packet->data, p, pcap_hdr.caplen); + + pcap_close(pcap); + + return packet; +} + +packet_t **pcap_read_all_packets(const char *file, size_t *packets_nr) +{ + pcap_t *pcap = NULL; + packet_t *packet = NULL; + packet_t **packets = NULL; + struct pcap_pkthdr pcap_hdr; + char err_buf[PCAP_ERRBUF_SIZE]; + const uint8_t *p; + size_t idx = 0; + + fail_unless(!!(pcap = pcap_open_offline(file, err_buf))); + + while ((p = pcap_next(pcap, &pcap_hdr))) { + fail_unless(pcap_hdr.caplen <= MAX_PACKET_LEN); + fail_unless(!!(packet = malloc(sizeof(packet_t)))); + + packet->len = pcap_hdr.caplen; + memcpy(packet->data, p, pcap_hdr.caplen); + + fail_unless(!!(packets = realloc(packets, (idx + 1) * sizeof(*packets)))); + packets[idx++] = packet; + } + + pcap_close(pcap); + + *packets_nr = idx; + return packets; +} + +void free_packets(packet_t **p, size_t nr) +{ + size_t i; + + for (i = 0; i < nr; i++) { + free(p[i]); + } + + free(p); +} + diff --git a/source/test/controller/CMakeLists.txt b/source/test/controller/CMakeLists.txt new file mode 100644 index 0000000..a8953f0 --- /dev/null +++ b/source/test/controller/CMakeLists.txt @@ -0,0 +1,115 @@ +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +set(SRC_DIR ${CONTROLLER_SRC_DIR}) + +include(../common/CheckUnitTest.cmake) + +SETUP_UNIT_TEST(cmdu_rx + ${SRC_DIR}/map_ctrl_cmdu_rx.c + ${SRC_DIR}/map_ctrl_cmdu_validator.c + ${SRC_DIR}/map_ctrl_cmdu_handler.c + ${SRC_DIR}/map_ctrl_tlv_parser.c + ${SRC_DIR}/map_ctrl_sta.c + ${SRC_DIR}/map_ctrl_metrics_handler.c + ${SRC_DIR}/map_ctrl_onboarding_handler.c + ${SRC_DIR}/map_ctrl_post_onboarding_handler.c + ${SRC_DIR}/map_ctrl_emex_tlv_handler.c + ${SRC_DIR}/map_ctrl_utils.c + ${IEEE1905_FACTORY_SRCS} + ${LIBUTILS_SRC_DIR}/map_data_model.c + ${LIBUTILS_SRC_DIR}/map_data_model_dumper.c + ${LIBUTILS_SRC_DIR}/map_topology_tree.c + ${LIBUTILS_SRC_DIR}/kwaytree.c + ${LIBUTILS_SRC_DIR}/arraylist.c + ${LIBUTILS_SRC_DIR}/map_80211.c + ${LIBUTILS_SRC_DIR}/map_channel_set.c + ${LIBUTILS_SRC_DIR}/map_info.c + ${LIBUTILS_SRC_DIR}/map_staging_list.c + ${LIBUTILS_SRC_DIR}/map_blocklist.c + ${LIBUTILS_SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(cmdu_tx + ${SRC_DIR}/map_ctrl_cmdu_tx.c + ${SRC_DIR}/map_ctrl_cmdu_rx.c + ${SRC_DIR}/map_ctrl_cmdu_validator.c + ${SRC_DIR}/map_ctrl_cmdu_handler.c + ${SRC_DIR}/map_ctrl_tlv_parser.c + ${SRC_DIR}/map_ctrl_metrics_handler.c + ${SRC_DIR}/map_ctrl_onboarding_handler.c + ${SRC_DIR}/map_ctrl_post_onboarding_handler.c + ${SRC_DIR}/map_ctrl_tlv_helper.c + ${SRC_DIR}/map_ctrl_utils.c + ${IEEE1905_FACTORY_SRCS} + ${LIBUTILS_SRC_DIR}/map_data_model.c + ${LIBUTILS_SRC_DIR}/map_topology_tree.c + ${LIBUTILS_SRC_DIR}/kwaytree.c + ${LIBUTILS_SRC_DIR}/arraylist.c + ${LIBUTILS_SRC_DIR}/map_80211.c + ${LIBUTILS_SRC_DIR}/map_channel_set.c + ${LIBUTILS_SRC_DIR}/map_info.c + ${LIBUTILS_SRC_DIR}/map_staging_list.c + ${LIBUTILS_SRC_DIR}/map_blocklist.c + ${LIBUTILS_SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(emex + ${SRC_DIR}/map_ctrl_emex_tlv_handler.c + ${IEEE1905_FACTORY_SRCS} + ${LIBUTILS_SRC_DIR}/map_data_model.c + ${LIBUTILS_SRC_DIR}/map_channel_set.c + ${LIBUTILS_SRC_DIR}/arraylist.c + ${LIBUTILS_SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(utils + ${SRC_DIR}/map_ctrl_utils.c + ${LIBUTILS_SRC_DIR}/map_channel_set.c + ${LIBUTILS_SRC_DIR}/map_info.c + ${LIBUTILS_SRC_DIR}/map_data_model.c + ${LIBUTILS_SRC_DIR}/arraylist.c + ${LIBUTILS_SRC_DIR}/map_80211.c + ${LIBUTILS_SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(tlv_helper + ${SRC_DIR}/map_ctrl_tlv_helper.c + ${SRC_DIR}/map_ctrl_utils.c + ${LIBUTILS_SRC_DIR}/map_channel_set.c + ${LIBUTILS_SRC_DIR}/map_info.c + ${LIBUTILS_SRC_DIR}/map_data_model.c + ${LIBUTILS_SRC_DIR}/arraylist.c + ${LIBUTILS_SRC_DIR}/map_80211.c + ${LIBUTILS_SRC_DIR}/kwaytree.c + ${LIBUTILS_SRC_DIR}/acu_utils.c + ${IEEE1905_FACTORY_SRCS} +) + +SETUP_UNIT_TEST(cli + ${SRC_DIR}/map_ctrl_cli.c + ${LIBUTILS_SRC_DIR}/map_cli_subscription.c + ${LIBUTILS_SRC_DIR}/map_channel_set.c + ${LIBUTILS_SRC_DIR}/map_info.c + ${LIBUTILS_SRC_DIR}/map_data_model.c + ${LIBUTILS_SRC_DIR}/map_data_model_dumper.c + ${LIBUTILS_SRC_DIR}/arraylist.c + ${LIBUTILS_SRC_DIR}/kwaytree.c + ${LIBUTILS_SRC_DIR}/acu_utils.c + ${IEEE1905_FACTORY_SRCS} +) + +SETUP_UNIT_TEST(chan_sel + ${SRC_DIR}/map_ctrl_chan_sel.c + ${SRC_DIR}/map_ctrl_utils.c + ${LIBUTILS_SRC_DIR}/map_data_model.c + ${LIBUTILS_SRC_DIR}/arraylist.c + ${LIBUTILS_SRC_DIR}/kwaytree.c + ${LIBUTILS_SRC_DIR}/map_channel_set.c + ${LIBUTILS_SRC_DIR}/map_info.c + ${LIBUTILS_SRC_DIR}/map_80211.c + ${LIBUTILS_SRC_DIR}/acu_utils.c +) diff --git a/source/test/controller/data/cmdu_1905_encap_eapol.pcap b/source/test/controller/data/cmdu_1905_encap_eapol.pcap new file mode 100644 index 0000000000000000000000000000000000000000..c036b7b4b2a227c5fcdc279fa936550c503f1152 GIT binary patch literal 100 zcmca|c+)~A1{MYcU}0bca)c-RjStG>W3U0TL70h=iJ67 literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_6G_320MHz_operating_channel_report.pcap b/source/test/controller/data/cmdu_6G_320MHz_operating_channel_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..3efe7946318b254940e80c2299e4083f6983793f GIT binary patch literal 100 zcmca|c+)~A1{MYcU}0bcat`JdCW6FlpqPn~iJ67|(vtZe20C+ZBtxkA^g+$v;D9x5YNkck}!ci7W=3n`t0Wha4ydw{hl);+F0 zr0I06^bAbf03QWEodZH?0!R&VuwC?P5NkJ6+txf_RvXtC3>0+%$sJcl1ViZV6Te#s z)kV^15$9)35)mg6VG=P8SdWD0ptJ}A{SXlfOA$>i!e7({BpVeOei42Y$XYc7$U5y~ z9IS1f&i}D)|3R4B13+$#nnb~CF0EU-qnf348pPVo)V8roi?uHx|| z;JtpaCyJDJh2j`}4t6JBU$GW}!RTmchFuH{as d#m;&(wx7lh(9r++o8}NPeFK3!Gpqzj_yWAzw;%uj literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_6G_operating_channel_report.pcap b/source/test/controller/data/cmdu_6G_operating_channel_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..73619b2b6af0caa1a48fc63c7d1a8d40584b16b4 GIT binary patch literal 100 zcmca|c+)~A1{MYcU}0bcat`JdCW6FlpqPn~iJ67lJKz`)>ZqbzlQMRUnv7Wh}fr3UEm@Y~-&^0u&Fx5>rPqQ#G zNH$3{FfvO9n#{leax2K49#EPAXd)8>8$)VdfkCka0|O^emJ=w)gU1a(X|R4aARPnZ z0KvO`&f-&f9`o4%*&xis$i&RT`b~UCTX;sN6$3*90~;el!(s+@#)P~V$4)&1>A?j+ zX0ift4phPFFRTp7Dbo%yGRQDE=a&LQwWwI3peVmAGbJ@eA*oU!1ri`k3>*v~wSTl3 Y4lzU7DnNZKKz0MvELI>J8G!5n0O$-~x&QzG literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_ap_autoconfiguration_search.pcap b/source/test/controller/data/cmdu_ap_autoconfiguration_search.pcap new file mode 100644 index 0000000000000000000000000000000000000000..3f966d677f470a04e8d39bc797dfee07d4c8e88f GIT binary patch literal 126 zcmca|c+)~A1{MYcU}0bca&mkA#ura#V+aGXL71`O5CenoH}M^9;TfG)Kmqno4;cU}WNEknV8&!@va6543|BNP-|c5OV@C0|NkmK^3|H literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_ap_autoconfiguration_wsc_2G.pcap b/source/test/controller/data/cmdu_ap_autoconfiguration_wsc_2G.pcap new file mode 100644 index 0000000000000000000000000000000000000000..4a28c2296ecfc1fc6e56213eccf273adb624a8ed GIT binary patch literal 567 zcmca|c+)~A1{MYcU}0bca)g5Z#_Ni(GVn7ofH4yz6Eh3zH}M^9;TfG)Kn0x5jtmX0 z4B}rvLfQPxf#OU&yusp39GoE_mLTIQ0WStd0RbfjMiv2g27yTh2d4iQp75!$kgcHj zy4fWG1qL>VaRO2d0`IlI^4yx#e)6Q)j@G5GLoEc17!HV5O}6I$exTCuy?fJ-D9hzX z7E2Xuu6o~M#dybtWAT0igV2VEqz9Y6OkR5H*LsY{%{ny)_)`{mWrxelA8x_;2;Z zw&i|HE^T=HV^is=8D-p_uXo&9>}RaG^Pk`kL#rjRLZX-C_UJu*vW-)be~zbLf;G4H z-C2UIadY-Rf8TIiBIDB;zJ(c{PH{)>O0;VApZ+Sq!ob9kEFb_Rc?EbG7#RgP7?{`^ z1YAH&MFvU7%%YIY)MAD3%%aqs)Z$_VztocQ{G#k)0c8euprnbVnSp=`0}GgTV&HcS zH!?Fev@|g?00JWmfe;1`1~!I(PYeu90)hw~HX!4ffR-`{2mwhJ0ZuSmmx1N~zrTM3 zJb`-5wHO!`fGl;`8ZY&XJC>Lj_5@#x@sS{^fy0%oDp+cOY zTAZn(pv2q-ZyvIw{{2q<3*da+Z#ez%y(g6oxO zFMo_Q_bq5d^t&yo4(7np05mkCY$`ACCvy6I() zr^o(zX6;ctci6e|$e~;MzYp0Q&@sAux^;%k-;!rX%FAB2Z~b>A%%DwzNpbb^rN^Cb zIWbCfMFsT;L{0V!g@o9A!#mRv6T z@yIp&K*#w#Ka$@be)j3||MSc5T>tz$e&eNoQUWXtObp2a0zi^ifR}-hQGkPiiLF7v z1;kWjkaWx}3du|@RtV25O3g_vE>`eMEh*10$}SdAW?%d0VM`T76Eq#0pr*jW;x!B`C5&;%~mX6 za}rQsV1pPZAjKe%n!yuUo!ao_U4Z-5)I-}(3m7pR*e3Dg=EP^woDDi*iy8m&uKRtw zN+u&FYnf5?n_v5#RyIqXmGf+NN$EfR?wIJSLPgEwd;LDYeqaB-yS%Ko?PK6=+1Cfu zKUm%hn`Pu8yT^Fy(z&gnv)_I&ZQ_x8eU>wFYtf;}Av^SH4_UBr(*6B$!(z51H{Ih??@irw0wD|>3~USmpBNaJ1OyQ}Y#10h1ek!9G6)C(NfrT4Fk6>_g{1~)u_sWE zxfTPX0+7YdARxiS#K61-m;e|ZnD#NSLt^s)H-mDA;~$_tMj&KkU4F6oKu8>!2y^k7y!T%tP%hK literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_ap_capability_report.pcap b/source/test/controller/data/cmdu_ap_capability_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..912fb6ea7ed301a902607498150bce375b26eb42 GIT binary patch literal 667 zcmc&wze_?<6#njc&(u_;K@QZ?5D`rg4G|JHHQNu`s=-0BACjhKS}!ttMUYEFLxY5a zMnlrj9yK;ae@R40^}6@Hq}HbH;hcNEk9)pz&w09QRtY+Ojt(I~XzF9(&2k|{u$=^H zy6cs1%X9I=F~FebZWvVvV;_?*ie3r^Q;ZB_leJvIC@_kw)vvD8j4UJ1lrC&D)vh)e zImQ-K&e&n9UmHyM%U!1Ri!$7f(&qvEPG&W%##+)umjgWtD_ePNAmngjpiS$>W_-SzA6a-4NV zC07a=YG7mlF|@Izqg^p9Oa55l!-+ZZ4o&s~D3rJFe+&d4 Udeo29^g=F0lTn#lx>q{z1+{9ZU;qFB literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_ap_capability_report_no_he.pcap b/source/test/controller/data/cmdu_ap_capability_report_no_he.pcap new file mode 100644 index 0000000000000000000000000000000000000000..07463c84f0f0b6efe268bafd5219516370e14848 GIT binary patch literal 852 zcmb7CO=uHQ5T1S6{3qt(YoT~};3ZMAYDAP9;A0~9>tRwXR}dy5}e2E%Y= z1jz?rf-$(2D^?pUD2RgSmqY#)rKo7RGyspdbJaC9ol==MnbVYVHl^i#OcpSfd22(d zrA2>G_ODFK_vGdD%ycR!FC(r_daZh$^CPJv60m?$RK;=#z>Z!!`dt600o}uV3(9Xg z7}AG#Cu7G?&c1v?TW7)%6L0=8{&p*weKZTuAulGNvkTn&9>l-vzgIn;GKCt2CN(wM zoi>FHYT7?Ms!*s?Xi-vW-=}2nuu7ppVV#mj=K&@AM;%I9uQw^tUTuNZK>GC%T!uC{ z7#x(;9=L~Q-oq`96PNo>U>@7&fs2qmxJHbG2tIe$T!tDn?U`&rt#td_J8jr7KYjjk zch5k<80=@!Ic&*dwb>ockB9>&&yJy(SmF#Mf>B{xjSqyvkva*48zdAthuPvYh3@t` zpH=%i9nf|d!sUMSCr*sRnOSgCxZ$zl*M_W5&k5Wq#68iZG$l{Y1SAnj z3ONG8q=YO4=3K^Z=|)f*J%jDKR!U{kr8T4Dzb5u%%-D>wFth+pKbs*|xOjzwOAsqn uam%$bQ?*hpmr*ioxydrdf)cDrkB#=e`b;DJ0_O3kyGDCsM*TeLcl-v7U)kXR literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_ap_metrics_response.pcap b/source/test/controller/data/cmdu_ap_metrics_response.pcap new file mode 100644 index 0000000000000000000000000000000000000000..7deb260463dad05b58f1ce35fc988ae9b2fd3fab GIT binary patch literal 678 zcmaJY5PiG%MZA*;1{KkuQG#G$Y%F4BW2=p5XJI2b5G)0etHOz$#E>>t7Lq~~ zgTyqV@dpSZ{s=!1K~QjZCy8eS7v`3kc{B6w&E8#|lqF33920_^v4yYI>oXTS656DY zhH1U`SM$s3yICOcf>8uF=z3G3T-uUO0o^&}_;JWUJbyHhWFI0k4IDoJqCvLRnsta2 zRaf#az^o6*ame#Uz)2B6aZpDTF~}g%Y^KBZ5@3~Uw;1k7asr8iP??Jn^b8n(TNt{F z8p9+mFX}`hP9$PPViK^Q2+>9{2?G5h;lvb)I+KVdIe|p2q%2AziULWij{%a7b&Nwa zrDOje>5d)^YkvsHZc-BuvJ%q0#~t-oNyi}3Y^KBJ23__xvF=E60tllJKz`)>ZqbzlQMRUnv7Wh}fr3UEm@Y~-&^0u&Fx5>rPqQ#G zNH$3{FfvO9n#{leax2K49#EPAXd)8>8$)VdfkCka0|O^emJ=w)gU1a(X|R4aAUy}f z0fK-7&f*i-s<&4H*&xis$i&RT`b~UCTX;sN6$3*9-;Yd&hG`5^D{eEhPLg5#0+PuB z=>!5tAZ9E(!61t+36@}JlTZL*ZUzMg0SRVIBb330APA6~Sb;bPDzxScD}(M%ZCOSJ z83yP4QednU6)O}J<(Flqq^2k&RVt)F0+fk?g8`)0yZ@jJGnBn*hqf#WkgWlJKz`)>ZqbzlQMRUnv7Wh}fr3UEm@Y~-&^0u&Fx5>rPqQ#G zNH$3{FfvO9n#{leax2K49#EPAXd)8>8$)VdfkCka0|O^emJ=w)gU1a(X|R4aARPnZ z00HA>XYr|p%i3&!Y!GH*WMXDv{U*MnEj**sih-d)^*|3p10w?)L})()2jdrzWHv}M z8UUHe3dA{3@fBZK8O)CgZe?VUVQ|hb1%_%-u|h#nepzNpYKlTqr9uiMK$sXf7(i-k b*38??3}t^lBDj?W$o>vBixtR51|T~CqhVwh literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_available_spectrum_inquiry.pcap b/source/test/controller/data/cmdu_available_spectrum_inquiry.pcap new file mode 100644 index 0000000000000000000000000000000000000000..5be6bb08f269c73495e3e02bea572ee7e3447a6a GIT binary patch literal 5595 zcmbtYO>f&q5S5*zmmYd-kAiLmVwJN?F3H>y#R&`)ZD2c>7J;G2w9SGd6-sh!6e)7; z&*(MBUfo+SJrp_hKeXvj=&A25B}M#jEVhP4NVDAcX5P-sF8Rl=KmWAdx!rmHce}HN zAD{p5#mO(9Zgu{=)3KlJH*eltZEs&)UH!iGSct;0MAK0(Egov$6j|PLihdT@4?yK&z z?kkKXS%F-*5hY1HnMTErQIyWCH}EgX($jc0KjzSmulu3zY8@{B0s_Io#SO00_q|Z9 zja#bm@VhL(e-TqLaU9GU$LY%A(U9daNNuRN$@T2KD2dLOCb)7I#iwU83~Nj%ejHv- z=veRm<1ZO6)^G;FlBX%f8^-DE5~Iq3#p!XD`@{5< zs8;SId$}5b4bgdIhbLuo;j$I1GQn+{U|eNmtWPjrBZCQiOz1uXd2G`#Iv$)2(=FFRDPOaivFlkiWJUEGMWH7<4@NQQirucWe3KkM8*feY!HWxN} z@51K6=D}uQGq4%heAs;00@woB0@y;>LfFKCQ5;ysfmtjMiG)l-Dj}DUOvomr6Y>cO zg^WT;{z;M9lfX{(|10e>Kg8@ZG zv=UsDf3w$^QC)Muk(#8WBax*bx)4|xh%gFa2txmb&MWb5W_0U}442{AaI9jKNvMr- zHVDGhAr&35Wq=-nX3ENalD)L;O1VXMW7~g+w*NQUK0i%uf2;HElS3@~^-X>{$_#i-MWODcC)u*CRLT*8Sp6vu z<;I5^!S-Kk5StIcnrdx>G7H2id~Hd;#Kz*tf1Pu2@37_^7bY zz`{N%O6$cq@&T6OiVVVcT(w8bavtu55T)xHD()?2yuYrG*1oS2xv~UIOlCgQ9n#%Y z(`!|{UcevLjjg1lTM;p%Q~jk%uGL2^uGB$UHI$9B77xZ(w(DYJP7w%1rONkiSZB;B z`)h@vr^>fJ73<`2EekKM^PtoTTy$L^Uz=V~r9`V8CJwHQf#h!gec!Gu;|GB;w*efrlwtjrsN7NO z&MuB!$k4*s)~H;#ny*I6zKZVqtt1E=l{IMg#c~$T0ygNf^Z#>0=ilzM(LZF))M5%N z`b*F%CgZTpG{BllOaWrr+*{*td#%EftO9HL%fsg;NoX8RQr*^ZqlO2l;5N+$jaVg z$!v$(G!wMb2yvt&iuDtub%C(D%CCActKN%<8izaQ@btPpwBhL1hN`zt9&uKDPKTVgKcx* zFh(~uB#>2L+w~(qY;;!t&wk{GP0oU>?5wDE+h}0lQB}Jq$jYz{S@^;H3Yw0H24rPe zX?K*sd!dH(0#_NeaMqr?^Qd^V#c>p6u>3X1>^nZ@>hvtjXUi9ri+7?2^U)~6`y`8+ Ww|ED+$=lJz6Mf|sZ1)vj;rU^6 literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_backhaul_sta_capability_report_slave.pcap b/source/test/controller/data/cmdu_backhaul_sta_capability_report_slave.pcap new file mode 100644 index 0000000000000000000000000000000000000000..8418fbec520b376398ec668b2211706ccb95eb0c GIT binary patch literal 97 zcmca|c+)~A1{MYcU}0bca?C}Ql0agXP|U>0#LU8aZq=Dp3=ExC3=9n#5*`clJKz`)>ZqbzlQMRUnv7Wh}fr3UEm@Y~-&^0u&Fx5>rPqQ#G zNH$3{FfvO9n#{leax2K49#EPAXd)8>8$)VdfkCka0|O^emJ=w)gU1a(X|R4aARPnZ z00G}VXK|MfUkw`|8-$q{nV4BvzlrZ?3(x4ZVqj>H`Xs^7FrR_<3rHwy(N-J#=ZzqZ zSOCaORv^xS%B}vw${^<#+R4Zu!{D4>3JlevVugaD{IblH)D(rJN`(|ifG{y|Fo4w7 dPn_Su3}x^14eewBvROf%1_D+f8ySG?0055PV8Q?Z literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_backhaul_steering_response_error.pcap b/source/test/controller/data/cmdu_backhaul_steering_response_error.pcap new file mode 100644 index 0000000000000000000000000000000000000000..d8b5d7250df88c1c2c42eb81da729c640a81bbb2 GIT binary patch literal 452 zcmd<$<>lJKz`)>ZqbzlQMRUnv7Wh}fr3UEm@Y~-&^0u&Fx5>rPqQ#G zNH$3{FfvO9n#{leax2K49#EPAXd)8>8$)VdfkCka0|O^emJ=w)gU1a(X|R4aARPnZ z0KtpB&f;F7{ed<>HV88@GBLBTeiPr(7M{^*#lX-Y_0fW%VLk)z7m!faqOCUe&zl$* zGqAHl1VCoM0LV;MAkKjbtp38v@cp22JtKn*gL8fjfop=PlH*~kE72LP7CW_17n literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_beacon_metrics_response.pcap b/source/test/controller/data/cmdu_beacon_metrics_response.pcap new file mode 100644 index 0000000000000000000000000000000000000000..c1c6f266229cb1e74755029327533a346150b322 GIT binary patch literal 138 zcmca|c+)~A1{MYcU}0bca$dFnjeqrvks%4l24N;fCT14aZ{j=J!ZSLp7#JFa9-m=o zn8o0>;x;quBpC)Kby*n()2*QrpD?d4o+?kA{~l!D8<3QP!e#|Aw#Gg0&UY?V%ii$)OHZTK}4a0 zvnUkY9Q+H8{(@{maT9!E2Llg$?|biicU(T+U#40t`Ts0Yh}qH4;lrnvH$;wBx?mN* zH(t*U{ENCsTD+b~I+l2zTg}Z-RkKjlU=Q{)qdeOvA6gJV2k#WlVFFiKk4}$#L^dK9 zu^zG3XP-WM#PnmjF`bxvs_M|RQ+1h*)NVEw`|tE)d6IRtxJc?L%Xr2&EfYM;SEaor z(%yzLR75)H4s#c;i$1x5Teyd*NPncaM*=hAcOx%5iL6EQxtAP|Yr5p#?eqthlo aC#;H^hc!`G6Iok4gidu?1$v}BPLyAD?Rs1Q literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_channel_scan_report.pcap b/source/test/controller/data/cmdu_channel_scan_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..e975b42a1820cdc4dc63be9d57ac7c475dd75c28 GIT binary patch literal 798 zcmca|c+)~A1{MYcU}0bca^}|jjpv`k!tjlW0gRa#nV4BvzlrZ?3(x4ZVqj>HxqgG8 zVFiP%jFEwnp{{|UuAxzgfsvJoft8WDo+? zT80;skwJqHI8@{s5MjcO-z+Sl$U(9WPLg$S5v^klG)%bh>i~uc>z&}5z3m3PC8;^7 zd8sA3E*9oSP71(KVb~?UWACmFoS?u;F9n9y5l~Q>GB5y*Uf1>sIS`M5WGopNi$F4; W(R5gXb?n`>A43PwEFh3;01^OFhrm<- literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_channel_selection_response.pcap b/source/test/controller/data/cmdu_channel_selection_response.pcap new file mode 100644 index 0000000000000000000000000000000000000000..7953775e36929dfb9eb56884e1986e125523752d GIT binary patch literal 100 zcmca|c+)~A1{MYcU}0bca_VaS#)nMfWUv9UL70h=iJ67W3U0TL70h=iJ67W3U0TL70h=iJ67zX*7iN6+zYdrT3rK6uu~ks>vl+nxzmd(!2AU%QHYbOl zwONkwWs4j`s~kg{9789Ngj$vZcX19Vta2D&F3v%5u`0%V95}>Qk2+z{tMB$}-lSvh$HMI~hwRW)@DO)YI5 zT|IpRLnC7oQ!{f5ODk&|TRVFvf(-=5@7A4r4<0>v_Ttr>cOO1I3Q9-99h(>$0u31< z4q*a1#D=4c#l*tKp`6LYrh>`Bp^_&hBd4IGqNbsxqh~@DTS`tzO-oNTTSh@eL&t;$ z7OSkPu0|%KBA{M!wcckqD4+!L5CT9$rU4;>!fI$>f`ktPBs4&h5I=yU79!Vxq6NW$ zY0kopyxjAqyH-mA9;~xelAY@?R0wy0uFk}ak96-zo#N0s42E;r-%mP%$52P6w E0Kl9{+5i9m literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_failed_connection.pcap b/source/test/controller/data/cmdu_failed_connection.pcap new file mode 100644 index 0000000000000000000000000000000000000000..9b1e58f8593d85671d85963151a0915776d6e312 GIT binary patch literal 100 zcmca|c+)~A1{MYcU}0bca`v?Sjpr!gVXy(RL70h=iJ67lJKz`)>ZqbzlQMRUnv7Wh}fr3UEm@Y~-&^0u&Fx5>rPqQ#G zNH$3{FfvO9n#{leax2K49#EPAXd)8>8$)VdfkCka0|O^emJ=w)gU1a(X|R4aARPnZ z0KvO`&f-&f9`o4%*&xis$i&RT`b~UCTX;sN6$3+q1REnm!vY3wMnNTGmsPvZ+GnaWRPKS&MyUqYEiL5K~a8LW=d*`LQjjKmY*9Vis8d literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_legacy_topology_response.pcap b/source/test/controller/data/cmdu_legacy_topology_response.pcap new file mode 100644 index 0000000000000000000000000000000000000000..f5f9110efc5cadb7db9bbbf09fb9b95552f6a943 GIT binary patch literal 260 zcmca|c+)~A1{MZnzzF1g2!9t361xM%OpHv-EUe$eceI6PbXoxgnGJ3-G%z!GLWEep zfMl{5S-Bt#2Bx_TSs$mRs8ouH-GwUQ7m|(_*^rr^@PM!E3;G;Q}oq-pk5$LdWZSNV7`FnTm0r4S5 NDs5ogzi2JUj{pyWR|Nn7 literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_link_metric_query.pcap b/source/test/controller/data/cmdu_link_metric_query.pcap new file mode 100644 index 0000000000000000000000000000000000000000..3eff9f83611eedd496101eedcbfc3ed4e01c62d3 GIT binary patch literal 452 zcmd<$<>lJKz`)>ZqbzlQMRUnv7Wh}fr3UEm@Y~-&^0u&Fx5>rPqQ#G zNH$3{FfvO9n#{leax2K49#EPAXd)8>8$)VdfkCka0|O^emJ=w)gU1a(X|R4aARPnZ z00Hx6XK~YjJP{ip8-$q{nV4BvzlrZ?3(x4Z0t&GH@MUP=VBi4S1_BTg%)}r-X0ift z4pe5v7gh%Iqk>x*8Dtoo^GkuDT2!o1P?TSmnUb2KkW{IV0tpZ%py?pBHEZT=W`?pq U9}(Qj0%U)On#BraBLk2f0I>&KcK`qY literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_link_metric_response.pcap b/source/test/controller/data/cmdu_link_metric_response.pcap new file mode 100644 index 0000000000000000000000000000000000000000..effe197c8702b96dfbaca5bad608fb4439519fc2 GIT binary patch literal 311 zcmca|c+)~A1{MYcU}0bca=w)Ojh9nqVc-XHK$wYnO01Tx+dXFP?-5ChdkVurq@PMr2J$gyjBKxDbuVBiN14B`) z^mHZ?sjh(@oWa{;@%X^tB#u-SvVUZxKN;H=&j>DHq4GrW(b`7;%Lo6`PtlVwC7fb{^~Q&UMFoj*@tzJE8<4P`niD zphKxg1YVGJBVJ4a_=(m}bb&WuAZpv*OK3=-9KeVCr4CiCjy~i8=#MzW3m&w%Z||MU z3%vjqdY%UBG;me}K6%oyGIw~qy}c+wMTEKtQxZ%|{F$@oOA=H?m=xhF2`!y1OHdQx zxCmcM=*)Rb;(nQtxS!{sg*jL|2?4fRUtV7>81t}!u|CSvZ{qecgqe(sxWe@i;Ja6w zP8!f*?@-(MVF?9-BNFoY573nxNNomEsSg?`$V>#vYd4miz$#}8p8q$1y4C>E-mSHJ zPDtFpvl28Un3G_E%nk~z89}4P)WA(edqpbR%c(qZQ_Vf>2W|V4E*6jSL#Yt~oD^1OwI_@1aYS#RsCG?I@elvr%nZ)*O zO&`Foq4zVnr-r!7(VtYpsf_iSN>~GAY1aP&OXaRlQKv@d#=AtcE`?PrTC<6^@pwSyRS`WHamv`)hKpTv7@; z?$_NMTXyvgCAZgV4Esx*=5lTDDTKsuvvYPkLqOr?C8r_*!cHe?0e6orC%sMweFr>* z+V_{xa~r*IkAL)-gU!%aD3eFfEMmhfRFu4&$D?(eXO9>HpS^eMwcj7`&U%j631t1u Opq{B;A6qyxjqw6?GQQXV literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_mlo_client_capability_report.pcap b/source/test/controller/data/cmdu_mlo_client_capability_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..1054ceabb8dc6af26e87c43dabb14c93ecc11299 GIT binary patch literal 495 zcmca|c+)~A1{MYcU}0bca`YxVOf_H0#&8_S0bwRaCT12^rU^dYm-cj8F)%c6)zvXH zOkm()nBe2nQUxI=GR|QTWaMIC;7%(li!VtnE{RXdVC3i#l2F;;k&vUv^ixcQiP6eH z3TPnz8Wtpy;{eD3{A<{(*_xYMTH8A7xf>Wf8Wf zD#37uVby;Ktd==m2uJ^!t@pBhVO01pn)8U^UGD7!hvy7@3@$M6|7A7Bb&|NkHdp@aQA-zsPGzXHkuu>v+{z4{MfD#37uVby;Ktd==m2uJ^!t@pBhVO01pn)8U^UGD7!hvy7@3@$M6|9>_EBPYbI zkJuUJC^E2vI8)>eSs6Kagd|i9fUeY)de12S|NnoGgV4c#o^O@2`CkF$fEcT@Uj2ta zRA&hRo%4oQ8sZ#dFt{tX3NK)&RRX)t*YPXRbu3IQ++skN2?AY)?kF(-zbqt(7?glP z#LEm0qF?`i{g;M>&42!!MGS8kKWQ;T+@PeSM92YmIT-{bm>3uZ7#QxdGX#7BGC?*0 E0Jsab`~Uy| literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_mlo_client_capability_report_frag_ml_ie_frag_sp.pcap b/source/test/controller/data/cmdu_mlo_client_capability_report_frag_ml_ie_frag_sp.pcap new file mode 100644 index 0000000000000000000000000000000000000000..c8893ad3fcea6f58f9c2c830970a51e81adbe9e0 GIT binary patch literal 817 zcmca|c+)~A1{MYcU}0bca+;c+rrtN^W#D9H0AnUbCT12^rd7Mo+-2;vVqj?CVmr>z zFoA)G=@n3jg<*n^PfOKArh5#6j9d&1+-XH+@g=FnCGklaj2t~eGgLNsB;+VE{ghB) zVze@l0@}d8h6Rb_H~?}0{~9)Hw$A33*0#=iZUzRA21W)42L=X(|5D0~91H>s96kn4 z2XduZB^Y`bR{e*-|IF5V*}gC;{1?r6#PBZnc7nrm20jKC82JA`n}LxN;=D)f4F44w z*g>2r@`kK;H?10SfRTeoNJ7N`7?`?J?-|Aa|NjpPH*~O{=Ue4${#QV`SN}i#M-9J! zDzFfI^&bNN3jss!4X-pLuPP}i-Id&p3*6;o5RhPEU=(0rxXaEE K@CnESnGOJ>2x#p9 literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_mlo_topology_response.pcap b/source/test/controller/data/cmdu_mlo_topology_response.pcap new file mode 100644 index 0000000000000000000000000000000000000000..552ae81b7a6f031b4ed2cf96f53cb39e146cad00 GIT binary patch literal 1044 zcmah{ze~eV5We?PE82>pVC_`FNpO(bMJKgxji{4bD-}bG6-0|e>kw)XU8IPAfUXV- zIw;hk&_%(;qT-++>gXc)102Nna)~dd0Uxxv`|f*p-@Ci3%Hb{rABf`v!kqH--3-TG zB>YiAC0`va%8Baf#sCXQ-z%U?Q1}R)tWB!*Z~QYy8XD06DGR2h+K|d2jkD$%ga(iR zvNAR+^9yMw9<4@9v%7+0h2m9^ezyKs!^W9$cq4GHF5$`R0F~UYXQXv z1}o3Ap=4|f;21Xl3e-VdwIvQZFqg<0I+N)^w}q}dGTBmP2d1b;K*$4Wqq>lxn4L0m zI$u8bk;>R#d>~&_*U@q4W}|Yh7FN2Tn+>CQid#$@?4-0gaHx)tIC3@%3@$nfv5r* z3Raa35@KOcV}w|i0nrMv5265K1jITv1|BdY<45TdPOEL~47?Ea=T@Cr1$2G`12Yrj z!e)jA%n$<@A(WtFW|4`dg+Y9Bep*^;YP?Z8#55*|W+^mjv-EhU#N=#`#L^syI&}SJ z5dB#Y{n>={XQAsiO>bckWQ17Gz$gZCJ1c|xW(GzkZU!+10f~gs9$ IiZLJn0Pj+i1#&=`iIIt!h4tL3GpiUFI<0^b%qltz4a^Kv zAwnEyKr+mX99$3v0|O5On8g4zO$;Q*3Y5zd-_aJH>%hDeBE$|90?V;8z=YUfLTn%* zun8 literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_operating_channel_report.pcap b/source/test/controller/data/cmdu_operating_channel_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..6563eaedaacbce07caeb8ac5c446e654585c6c7b GIT binary patch literal 100 zcmca|c+)~A1{MYcU}0bca>RrF#)pV9GS~pwAk4(b#LU9_O?*dNct)oc149Exvj;;% ZKZC#*kWdy&!|b}bwR3CciX#kyvjEW3UCXL70h=iJ67Gw?M)gxFT?K6Cfof1qXn#p)l; literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_steering_completed.pcap b/source/test/controller/data/cmdu_steering_completed.pcap new file mode 100644 index 0000000000000000000000000000000000000000..b987a624b05b0b9781a559791284d32cdc290d11 GIT binary patch literal 452 zcmd<$<>lJKz`)>ZqbzlQMRUnv7Wh}fr3UEm@Y~-&^0u&Fx5>rPqQ#G zNH$3{FfvO9n#{leax2K49#EPAXd)8>8$)VdfkCka0|O^emJ=w)gU1a(X|R4aARPnZ z0KvO`&f-&f9`o4%*&xis$i&RT`b~UCTX;sN6$3+qI2$8F15knp05X#mh;yK7SASt; zNKTn{h><~t!8yMa7^+3Z3I#>^Wtl0dDGEuI3Mr5PVPfE50IB_>&2Wer%2omDV*#=o Npk}cG*~kE72LL?STKWJ0 literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_topology_discovery.pcap b/source/test/controller/data/cmdu_topology_discovery.pcap new file mode 100644 index 0000000000000000000000000000000000000000..e44959d66786757a38efad18500229343782aad0 GIT binary patch literal 108 zcmca|c+)~A1{MYcU}0bcatt;8#IH@}W^e(rL71`O5CenoH}M^9;TfG)Al0I73=ND7 mY!D$P2DUF?Ic^3q1_6nW47_e;hUQKd7G|dAW)>jjKmY*9Vis8d literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_topology_discovery_slave.pcap b/source/test/controller/data/cmdu_topology_discovery_slave.pcap new file mode 100644 index 0000000000000000000000000000000000000000..2fa5ef4aab3d89c1a45962d82775202efbb9103f GIT binary patch literal 108 zcmca|c+)~A1{MYcU}0bcawdo>C4t0TpqR1Y5Cenoxm9OYF)(ymfn-b)7#bKE*dRho h3~XW`IaY24F$MvNj|{xdu8xk*&W?@-Kmbw>1OPIz6cYdd literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_topology_notification_assoc.pcap b/source/test/controller/data/cmdu_topology_notification_assoc.pcap new file mode 100644 index 0000000000000000000000000000000000000000..b9af79715adc57d53c50d9d73ed2b40cc7905dc1 GIT binary patch literal 100 zcmca|c+)~A1{MYcU}0bca&}hyjZb>W%wPj#gD?{#6Eh3zH}M^9;TfG)Kmo?f+Zh@d a8Q35~lNfkc+-7E-B=ZHNBC7$Y0097BH5Q!! literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_topology_notification_bhsta_assoc_5G.pcap b/source/test/controller/data/cmdu_topology_notification_bhsta_assoc_5G.pcap new file mode 100644 index 0000000000000000000000000000000000000000..b32554bcffc34b7e7fac153998ed14d0674427c8 GIT binary patch literal 100 zcmca|c+)~A1{MYcU}0bcavtaDrhvq3pqPn~iJ670#LU9_O?*dNct)ocP>`|dF+&3*0~7|K$wYtmQVSt3dlG%*R5LMX>3~Ugg44|DX3{s3xV<5^P z+Q94#HU=Sx!G8VL9bdy&+-7E-Bm)!7xRCzT;NPhe-vfL!r?NBfLNo#$wyy0x12TW_ zu00?=#7LzLjQba@WoQ6;jj@@*l?h@#6GSt2T2WbiNosLPd{PEP0wIzH5y@r*OJpNU zv@mEw4PXG7x329Yt2dC|yK5h77{nL`Mu^)PG&eIaGI2AAF$hR}WZ-o(GcH741BDE)V!rwaF~s^_!uImFJD$&!n9ez4S(1asJN6r@$mD4$oJ GRSti-5|x4g literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_triband_topology_response.pcap b/source/test/controller/data/cmdu_triband_topology_response.pcap new file mode 100644 index 0000000000000000000000000000000000000000..39acf2d1d222f9456ef8e5c126b94600c8ec3822 GIT binary patch literal 375 zcmca|c+)~A1{MYcU}0bcax!!a6G3ABKn@5qF)}f;uznNY(H5T3X$6#Eo_m&|fteu& zBEP60Ygfe~VI z2H3D1h~Z!|3t|`>0}sR=zkchEui-Fm){5KAtdnHe8F(SG5a%;A0R6$(%;3umF^CaD zvF8@&$ERf&fq6Mh5DF@22DJvHBnje*EQq!&s0z~-20^F@10%#*hVz>l7@4>k#25r5 WJ~Hq+yBZj{0fDQls{trXfdBx*&2(J= literal 0 HcmV?d00001 diff --git a/source/test/controller/data/cmdu_unassoc_sta_link_metrics_response.pcap b/source/test/controller/data/cmdu_unassoc_sta_link_metrics_response.pcap new file mode 100644 index 0000000000000000000000000000000000000000..ba401534b7a15fd43e7a974c8d9f12ec162717da GIT binary patch literal 100 zcmca|c+)~A1{MYcU}0bca)c-RjStG>W3U0TL70h=iJ67 literal 0 HcmV?d00001 diff --git a/source/test/controller/data/emex_tlv_0002_feature_profile.pcap b/source/test/controller/data/emex_tlv_0002_feature_profile.pcap new file mode 100644 index 0000000000000000000000000000000000000000..8a564fbc1dc0237a20741327d277000f271864e7 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW;ARj83NbJ- SGBL9-FflL#1vx*w*J`9l3J2s@W7Z6!81Hy$b{e-H8=px0|2?`Aano# literal 0 HcmV?d00001 diff --git a/source/test/controller/data/emex_tlv_0010_eth_stats_v2.pcap b/source/test/controller/data/emex_tlv_0010_eth_stats_v2.pcap new file mode 100644 index 0000000000000000000000000000000000000000..13617cfe27b6d65219ae5af2fed0f840c876d7e7 GIT binary patch literal 234 zcmca|c+)~A1{MZnzzF1g2!9t35<3LN9gcq%E?NvEH*VV8X~n?MzyMU$z|F7>D8wLO z&&UMQ#IU6nNU$_00!b$K=RlHmpBIp104W9nCNMb-M4H_Lvzfsp*m_qJAe%AQ5-h^N oBxDj+F<~{Bvr5@Dy_0JXn4JibMk4vaY!@UE2y+dX5`~Kc0I1g>k^lez literal 0 HcmV?d00001 diff --git a/source/test/controller/data/emex_tlv_0011_eth_non1905_nb_devs.pcap b/source/test/controller/data/emex_tlv_0011_eth_non1905_nb_devs.pcap new file mode 100644 index 0000000000000000000000000000000000000000..837a33535971307cb0fdbd717cfb0db5febd75bc GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW;AW5k3NZ*W gF)(eYR;|3!dLjL(!M{_CjH`B^x%=)v$N(S!0QPnrd;kCd literal 0 HcmV?d00001 diff --git a/source/test/controller/data/emex_tlv_0012_eth_1905_nb_devs.pcap b/source/test/controller/data/emex_tlv_0012_eth_1905_nb_devs.pcap new file mode 100644 index 0000000000000000000000000000000000000000..ec707ef0477b3b2ae16dbda8b5e2a33393464d81 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW;AW5k3NZ*V gF)}XNYGZ%AkcsKl>o;%Tz5n(5&)controller_cfg; +} + +bool map_is_channel_in_cap_op_class(map_op_class_t *cap_op_class, uint8_t channel) +{ + return true; +} diff --git a/source/test/controller/stub/stub_map_ctrl_vendor.c b/source/test/controller/stub/stub_map_ctrl_vendor.c new file mode 100644 index 0000000..aec2eea --- /dev/null +++ b/source/test/controller/stub/stub_map_ctrl_vendor.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +#include "stub_map_ctrl_vendor.h" + +static stub_vendor_send_message_cb_t g_send_message_cb; + +int map_ctrl_vendor_send_message(map_ale_info_t *ale, map_vendor_tlv_tuple_t tlvs[], + uint8_t tlvs_cnt, uint16_t *mid) +{ + return g_send_message_cb ? g_send_message_cb(ale, tlvs, tlvs_cnt, mid) : 0; +} + +void stub_vendor_register_send_message_cb(stub_vendor_send_message_cb_t cb) +{ + g_send_message_cb = cb; +} diff --git a/source/test/controller/stub/stub_map_ctrl_vendor.h b/source/test/controller/stub/stub_map_ctrl_vendor.h new file mode 100644 index 0000000..c1a669a --- /dev/null +++ b/source/test/controller/stub/stub_map_ctrl_vendor.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2021-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +#ifndef STUB_MAP_CTRL_VENDOR_H_ +#define STUB_MAP_CTRL_VENDOR_H_ + +#include "map_ctrl_vendor.h" + +typedef int (*stub_vendor_send_message_cb_t)(map_ale_info_t *ale, map_vendor_tlv_tuple_t tlvs[], + uint8_t tlvs_cnt, uint16_t *mid); + +void stub_vendor_register_send_message_cb(stub_vendor_send_message_cb_t cb); + +#endif /* STUB_MAP_CTRL_VENDOR_H_ */ diff --git a/source/test/controller/stub/stub_map_ctrl_wfa_capi.c b/source/test/controller/stub/stub_map_ctrl_wfa_capi.c new file mode 100644 index 0000000..1339952 --- /dev/null +++ b/source/test/controller/stub/stub_map_ctrl_wfa_capi.c @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2021-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +#include "map_ctrl_wfa_capi.h" + +int map_ctrl_wfa_capi(char *line, map_printf_cb_t print_cb) +{ + return 0; +} diff --git a/source/test/controller/test_chan_sel.c b/source/test/controller/test_chan_sel.c new file mode 100644 index 0000000..2ef53aa --- /dev/null +++ b/source/test/controller/test_chan_sel.c @@ -0,0 +1,1359 @@ +/* + * Copyright (c) 2022-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include +#include + +#include "test.h" + +#include "map_ctrl_chan_sel.h" +#include "map_ctrl_utils.h" +#include "map_config.h" + +/*####################################################################### +# DEFINES # +########################################################################*/ + +/*####################################################################### +# GLOBALS # +########################################################################*/ +static mac_addr g_al_mac = {0x02, 0x01, 0x02, 0x03, 0x04, 0x05}; +static mac_addr g_al2_mac = {0x02, 0x01, 0x02, 0x03, 0x04, 0x06}; +static mac_addr g_radio_id = {0x12, 0x11, 0x12, 0x13, 0x14, 0x15}; +static mac_addr g_radio2_id = {0x12, 0x11, 0x12, 0x13, 0x14, 0x16}; +static mac_addr g_radio3_id = {0x12, 0x11, 0x12, 0x13, 0x14, 0x17}; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ + +static void set_cs(map_channel_set_t *set, int c_nr, int *c) +{ + int i; + + map_cs_unset_all(set); + + for (i = 0; i < c_nr; i++) { + map_cs_set(set, c[i]); + } +} + +static int check_cs(map_channel_set_t *set, int c_nr, int *c) +{ + int i; + + if (map_cs_nr(set) != c_nr) { + log_test_e("check_cs: map_cs_nr: %d vs %d\n", map_cs_nr(set), c_nr); + return -1; + } + + for (i = 0; i < c_nr; i++) { + if (!map_cs_is_set(set, c[i])) { + log_test_e("check_cs: map_cs_is_set: %d not set", c[i]); + return -1; + } + } + + return 0; +} + +static void test_init() +{ + map_controller_cfg_t *cfg = get_controller_cfg(); + map_chan_sel_cfg_t *cs_cfg = &cfg->chan_sel; + + /* Allow all channels */ + map_cs_set_all(&cs_cfg->allowed_channel_set_2g); + map_cs_set_all(&cs_cfg->allowed_channel_set_5g); + map_cs_set_all(&cs_cfg->allowed_channel_set_6g); + + /* Set some default preferences */ + set_cs(&cs_cfg->default_pref_channel_set_2g, 11, (int[]){1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}); + set_cs(&cs_cfg->default_pref_channel_set_5g, 7, (int[]){36, 40, 44, 52, 56, 60, 100}); + set_cs(&cs_cfg->default_pref_channel_set_6g, 6, (int[]){5, 21, 37, 53, 69, 85}); /* PSC only */ + cs_cfg->allowed_channel_6g_psc = true; + + cs_cfg->align_multiap = false; + cs_cfg->bandlock_5g = MAP_BANDLOCK_5G_DISABLED; + + /* Add a backhaul profile (multiap_align only works on backhaul radios) */ + cfg->num_profiles = 1; + fail_unless(!!(cfg->profiles = calloc(cfg->num_profiles, sizeof(map_profile_cfg_t)))); + strcpy(cfg->profiles[0].bss_ssid, "ssid0"); + cfg->profiles[0].enabled = true; + cfg->profiles[0].bss_freq_bands = MAP_M2_BSS_RADIO2G | MAP_M2_BSS_RADIO5GL | MAP_M2_BSS_RADIO5GU | MAP_M2_BSS_RADIO6G; + cfg->profiles[0].bss_state = MAP_BACKHAUL_BSS; + cfg->profiles[0].gateway = true; + cfg->profiles[0].extender = true; + cfg->profiles[0].vlan_id = -1; + + fail_unless(!map_info_init()); + fail_unless(!map_dm_init()); + fail_unless(!map_ctrl_chan_sel_init()); +} + +static void test_fini(void) +{ + map_controller_cfg_t *cfg = get_controller_cfg(); + + free(cfg->profiles); + + map_ctrl_chan_sel_fini(); + map_dm_fini(); + map_info_fini(); +} + +static void print_cb(const char *fmt, ...) +{ + char buf[1024]; + va_list args; + int len; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + /* Remove newline because log_test_i also adds one */ + len = strlen(buf); + if ((len > 0) && (buf[len - 1] == '\n')) { + buf[len - 1] = 0; + } + + log_test_i("%s", buf); +} + +static void print_op_class_list(const char *name, map_op_class_list_t *list) +{ + int i; + char buf[MAP_CS_BUF_LEN]; + + log_test_i("op_class_list[%s]", name); + + for (i = 0; i < list->op_classes_nr; i++) { + map_op_class_t *op_class = &list->op_classes[i]; + + log_test_i(" op_class[%d] pref[%d] reason[%d] channels[%s]", + op_class->op_class, op_class->pref, op_class->reason, + map_cs_to_string(&op_class->channels, ',', buf, sizeof(buf))); + } +} + +/*####################################################################### +# TEST_CHAN_SEL # +########################################################################*/ +START_TEST(test_chan_sel) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + map_channel_set_t channels; + map_op_class_list_t *agent_cap_list; + map_op_class_list_t *agent_pref_list; + map_op_class_list_t *ctrl_pref_list; + map_op_class_list_t *merged_pref_list; + bool acs_enable = true; + int channel = 0; + int bandwidth = 0; + + /* INIT */ + log_test_i("INIT TEST_CHAN_SEL"); + test_init(); + + fail_unless(!!(ale = map_dm_create_ale(g_al_mac))); + fail_unless(!!(radio = map_dm_create_radio(ale, g_radio_id))); + radio->chan_sel.acs_enable = radio->chan_sel.cloud_mgmt_enable = true; + + agent_cap_list = &radio->cap_op_class_list; + agent_pref_list = &radio->pref_op_class_list; + ctrl_pref_list = &radio->ctrl_pref_op_class_list; + merged_pref_list = &radio->merged_pref_op_class_list; + + radio->supported_freq = IEEE80211_FREQUENCY_BAND_5_GHZ; + /* Add radio operating classes (20MHz: 115, 118, 40MHz: 116, 117, 119, 120, 80MHz: 128) */ + agent_cap_list->op_classes_nr = 7; + agent_cap_list->op_classes = calloc(7, sizeof(map_op_class_t)); + agent_cap_list->op_classes[0].op_class = 115; + agent_cap_list->op_classes[1].op_class = 118; + agent_cap_list->op_classes[2].op_class = 116; + agent_cap_list->op_classes[3].op_class = 117; + agent_cap_list->op_classes[4].op_class = 119; + agent_cap_list->op_classes[5].op_class = 120; + agent_cap_list->op_classes[6].op_class = 128; + print_op_class_list("agent_cap", agent_cap_list); + + set_cs(&radio->ctl_channels, 8, (int[]){36, 40, 44, 48, 52, 56, 60, 64}); + + /* Add agent preference + - bad prio for channels 36/20 and 40/20 + - low prio for channels 60/20, 64/20, 56/40 and 64/40 + */ + agent_pref_list->op_classes_nr = 3; + agent_pref_list->op_classes = calloc(3, sizeof(map_op_class_t)); + agent_pref_list->op_classes[0].op_class = 115; + agent_pref_list->op_classes[0].pref = 1; + agent_pref_list->op_classes[0].reason = 6; + map_cs_set(&agent_pref_list->op_classes[0].channels, 36); + map_cs_set(&agent_pref_list->op_classes[0].channels, 40); + agent_pref_list->op_classes[1].op_class = 118; + agent_pref_list->op_classes[1].pref = 3; + map_cs_set(&agent_pref_list->op_classes[1].channels, 60); + map_cs_set(&agent_pref_list->op_classes[1].channels, 64); + agent_pref_list->op_classes[2].op_class = 120; + agent_pref_list->op_classes[2].pref = 4; + print_op_class_list("agent_pref", agent_pref_list); + + + /* 1. MAP_CTRL_CHAN_SEL_UPDATE */ + log_test_i("1. MAP_CTRL_CHAN_SEL_UPDATE"); + fail_unless(!map_ctrl_chan_sel_update(radio)); + + print_op_class_list("ctrl_pref", ctrl_pref_list); + print_op_class_list("merged_pref", merged_pref_list); + + /* Check prefered channel list. + - 36/40 are not allowed because of agent + - 48 not allowed because of controller config + */ + fail_unless(!check_cs(&radio->chan_sel.pref_channels, 4, (int[]){44, 52, 56, 60})); + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 4, (int[]){44, 52, 56, 60})); + fail_unless(!check_cs(&radio->bad_channels, 2, (int[]){36, 40})); + + /* Check controller preference: channels 36, 40, 48 and 64 not allowed */ + fail_unless(ctrl_pref_list->op_classes_nr == 6); + fail_unless(ctrl_pref_list->op_classes[0].op_class == 115); + fail_unless(ctrl_pref_list->op_classes[0].pref == 0); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[0].channels) == 3); /* 36, 40, 48 */ + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[0].channels, 36)); + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[0].channels, 40)); + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[0].channels, 48)); + + fail_unless(ctrl_pref_list->op_classes[1].op_class == 118); + fail_unless(ctrl_pref_list->op_classes[1].pref == 0); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[1].channels) == 1); /* 64 */ + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[1].channels, 64)); + + fail_unless(ctrl_pref_list->op_classes[2].op_class == 116); + fail_unless(ctrl_pref_list->op_classes[2].pref == 0); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[2].channels) == 1); /* 36 */ + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[2].channels, 36)); + + fail_unless(ctrl_pref_list->op_classes[3].op_class == 117); + fail_unless(ctrl_pref_list->op_classes[3].pref == 0); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[3].channels) == 2); /* 40, 48 */ + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[3].channels, 40)); + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[3].channels, 48)); + + fail_unless(ctrl_pref_list->op_classes[4].op_class == 120); + fail_unless(ctrl_pref_list->op_classes[4].pref == 0); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[4].channels) == 1); /* 64 */ + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[4].channels, 64)); + + fail_unless(ctrl_pref_list->op_classes[5].op_class == 128); + fail_unless(ctrl_pref_list->op_classes[5].pref == 0); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[5].channels) == 5); /* 106, 122, ... because map_is_channel_in_cap_op_class returns true for all */ + fail_unless(!map_cs_is_set(&ctrl_pref_list->op_classes[5].channels, 42)); + fail_unless(!map_cs_is_set(&ctrl_pref_list->op_classes[5].channels, 58)); + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[5].channels, 106)); + + /* Check merged preference: channels 36, 40, 48 and 64 not allowed, channel 60/20 and 56/40 have low prio */ + /* NOTE: order is different as above because the list is optimized and sorted... */ + fail_unless(merged_pref_list->op_classes_nr == 8); + fail_unless(merged_pref_list->op_classes[0].op_class == 115); + fail_unless(merged_pref_list->op_classes[0].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[0].channels) == 3); /* 36, 40, 48 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[0].channels, 36)); + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[0].channels, 40)); + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[0].channels, 48)); + + fail_unless(merged_pref_list->op_classes[1].op_class == 116); + fail_unless(merged_pref_list->op_classes[1].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[1].channels) == 1); /* 36 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[1].channels, 36)); + + fail_unless(merged_pref_list->op_classes[2].op_class == 117); + fail_unless(merged_pref_list->op_classes[2].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[2].channels) == 0); /* 40, 48 */ + + fail_unless(merged_pref_list->op_classes[3].op_class == 118); + fail_unless(merged_pref_list->op_classes[3].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[3].channels) == 1); /* 64 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[3].channels, 64)); + + fail_unless(merged_pref_list->op_classes[4].op_class == 118); + fail_unless(merged_pref_list->op_classes[4].pref == 3); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[4].channels) == 1); /* 60 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[4].channels, 60)); + + fail_unless(merged_pref_list->op_classes[5].op_class == 120); + fail_unless(merged_pref_list->op_classes[5].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[5].channels) == 1); /* 64 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[5].channels, 64)); + + fail_unless(merged_pref_list->op_classes[6].op_class == 120); + fail_unless(merged_pref_list->op_classes[6].pref == 4); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[6].channels) == 1); /* 60 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[6].channels, 56)); + + fail_unless(merged_pref_list->op_classes[7].op_class == 128); + fail_unless(merged_pref_list->op_classes[7].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[7].channels) == 5); /* 106, 122, ... because map_is_channel_in_cap_op_class returns true for all */ + fail_unless(!map_cs_is_set(&merged_pref_list->op_classes[7].channels, 42)); + fail_unless(!map_cs_is_set(&merged_pref_list->op_classes[7].channels, 58)); + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[7].channels, 106)); + + + /* UNSET AGENT PREFERENCE FOR EVERYTHING BELOW */ + agent_pref_list->op_classes_nr = 0; + + + /* 2. CHANGE ACS CHANNEL LIST */ + log_test_i("2. CHANGE ACS CHANNEL LIST"); + acs_enable = true; + set_cs(&channels, 4, (int[]){36, 40, 44, 48}); + fail_unless(!map_ctrl_chan_sel_set(radio, NULL, &acs_enable, &channels, NULL, NULL)); + fail_unless(!check_cs(&radio->chan_sel.pref_channels, 4, (int[]){36,40, 44, 48})); + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 6, (int[]){36, 40, 44, 52, 56, 60})); + + /* Check controller preference: only 36, 40, 44, 48 is allowed */ + fail_unless(ctrl_pref_list->op_classes_nr == 4); + fail_unless(ctrl_pref_list->op_classes[0].op_class == 118); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[0].channels) == 4); + + fail_unless(ctrl_pref_list->op_classes[1].op_class == 119); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[1].channels) == 2); + + fail_unless(ctrl_pref_list->op_classes[2].op_class == 120); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[2].channels) == 2); + + fail_unless(ctrl_pref_list->op_classes[3].op_class == 128); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[3].channels) == 6); + + + /* 3. DISABLE CHANNEL MGMT */ + log_test_i("4. DISABLE CLOUD CHANNEL MGMT"); + fail_unless(!map_ctrl_chan_sel_set_cloud_mgmt_enable(radio, false)); + fail_unless(!check_cs(&radio->chan_sel.pref_channels, 6, (int[]){36, 40, 44, 52, 56, 60})); + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 6, (int[]){36, 40, 44, 52, 56, 60})); + + /* Enable again... */ + fail_unless(!map_ctrl_chan_sel_set_cloud_mgmt_enable(radio, true)); + fail_unless(!check_cs(&radio->chan_sel.pref_channels, 4, (int[]){36,40, 44, 48})); + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 6, (int[]){36, 40, 44, 52, 56, 60})); + + + /* 4. FIXED CHANNEL */ + log_test_i("4. FIXED CHANNEL"); + acs_enable = false; + channel = 40; + bandwidth = 40; + fail_unless(!map_ctrl_chan_sel_set(radio, NULL, &acs_enable, NULL, &channel, &bandwidth)); + fail_unless(!check_cs(&radio->chan_sel.pref_channels, 4, (int[]){36, 40, 44, 48})); + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 6, (int[]){36, 40, 44, 52, 56, 60})); + + fail_unless(radio->chan_sel.acs_enable == false); + fail_unless(radio->chan_sel.channel == 40); + fail_unless(radio->chan_sel.bandwidth == 40); + + /* Check controller preference: only 40 is allowed in all 20/40 */ + fail_unless(ctrl_pref_list->op_classes_nr == 7); + fail_unless(ctrl_pref_list->op_classes[0].op_class == 115); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[0].channels) == 3); + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[0].channels, 36)); + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[0].channels, 44)); + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[0].channels, 48)); + + fail_unless(ctrl_pref_list->op_classes[1].op_class == 118); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[1].channels) == 4); + + fail_unless(ctrl_pref_list->op_classes[2].op_class == 116); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[2].channels) == 2); + + fail_unless(ctrl_pref_list->op_classes[3].op_class == 117); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[3].channels) == 1); + fail_unless(map_cs_is_set(&ctrl_pref_list->op_classes[3].channels, 48)); + + fail_unless(ctrl_pref_list->op_classes[4].op_class == 119); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[4].channels) == 2); + + fail_unless(ctrl_pref_list->op_classes[5].op_class == 120); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[5].channels) == 2); + + fail_unless(ctrl_pref_list->op_classes[6].op_class == 128); + fail_unless(map_cs_nr(&ctrl_pref_list->op_classes[6].channels) == 0); + + + /* 5. BACK TO AUTO */ + log_test_i("5. BACK TO AUTO"); + fail_unless(!map_ctrl_chan_sel_set_channel(radio, 0)); + fail_unless(radio->chan_sel.acs_enable == true); + fail_unless(radio->chan_sel.channel == 0); + + + /* 6. INVALID FIXED CHANNEL */ + log_test_i("6. INVALID FIXED CHANNEL"); + fail_unless(!map_ctrl_chan_sel_set_channel(radio, 40)); + fail_unless(radio->chan_sel.acs_enable == false); + fail_unless(radio->chan_sel.channel == 40); + + fail_unless(!map_ctrl_chan_sel_set_channel(radio, 41)); + fail_unless(!map_ctrl_chan_sel_set_bandwidth(radio, 20)); + fail_unless(radio->chan_sel.acs_enable == true); + fail_unless(radio->chan_sel.channel == 0); + fail_unless(radio->chan_sel.bandwidth == 20); + + + /* 7. VALID FIXED CHANNEL BECOMES INVALID */ + log_test_i("7. VALID FIXED CHANNEL BECOMES INVALID"); + fail_unless(!map_ctrl_chan_sel_set_channel(radio, 40)); + fail_unless(radio->chan_sel.acs_enable == false); + fail_unless(radio->chan_sel.channel == 40); + + map_cs_unset(&radio->ctl_channels, 40); + + fail_unless(!map_ctrl_chan_sel_update(radio)); + fail_unless(radio->chan_sel.acs_enable == true); + fail_unless(radio->chan_sel.channel == 0); + + + /* 8. FOR CODE COVERAGE */ + log_test_i("8. MAP_CTRL_CHAN_SEL_DUMP"); + map_ctrl_chan_sel_dump(print_cb, NULL, false); + map_ctrl_chan_sel_dump(print_cb, NULL, true); + + + /* FINI */ + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CHAN_SEL_WEATHERBAND # +########################################################################*/ +/* EU weatherband channels get a lower preference when they are not cleared */ +START_TEST(test_chan_sel_weatherband) +{ + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; + map_ale_info_t *ale; + map_radio_info_t *radio; + map_op_class_list_t *agent_cap_list; + map_op_class_list_t *agent_pref_list; + map_op_class_list_t *ctrl_pref_list; + map_op_class_list_t *merged_pref_list; + map_cac_available_pair_t *pairs; + + /* INIT */ + log_test_i("INIT TEST_CHAN_SEL_WEATHERBAND"); + test_init(); + + /* Allow all channels except 100 and 144*/ + map_cs_set_all(&cfg->default_pref_channel_set_5g); + map_cs_unset(&cfg->default_pref_channel_set_5g, 140); + map_cs_unset(&cfg->default_pref_channel_set_5g, 144); + + fail_unless(!!(ale = map_dm_create_ale(g_al_mac))); + fail_unless(!!(radio = map_dm_create_radio(ale, g_radio_id))); + radio->chan_sel.acs_enable = radio->chan_sel.cloud_mgmt_enable = true; + + agent_cap_list = &radio->cap_op_class_list; + agent_pref_list = &radio->pref_op_class_list; + ctrl_pref_list = &radio->ctrl_pref_op_class_list; + merged_pref_list = &radio->merged_pref_op_class_list; + + radio->supported_freq = IEEE80211_FREQUENCY_BAND_5_GHZ; + /* Add radio operating classes (20MHz: 121, 40MHz: 122, 123, 80MHz: 128, 160MHz: 129) */ + agent_cap_list->op_classes_nr = 5; + agent_cap_list->op_classes = calloc(7, sizeof(map_op_class_t)); + agent_cap_list->op_classes[0].op_class = 121; + agent_cap_list->op_classes[1].op_class = 122; + agent_cap_list->op_classes[2].op_class = 123; + agent_cap_list->op_classes[3].op_class = 128; + set_cs(&agent_cap_list->op_classes[3].channels, 4, (int[]){42, 58, 155, 171}); + agent_cap_list->op_classes[4].op_class = 129; + set_cs(&agent_cap_list->op_classes[4].channels, 2, (int[]){50, 163}); + print_op_class_list("agent_cap", agent_cap_list); + + map_update_radio_channels(radio); + + /* Set lower agent preference for op_class 129 */ + agent_pref_list->op_classes_nr = 1; + agent_pref_list->op_classes = calloc(1, sizeof(map_op_class_t)); + agent_pref_list->op_classes[0].op_class = 129; + agent_pref_list->op_classes[0].pref = 5; + agent_pref_list->op_classes[0].reason = 0; + print_op_class_list("agent_pref", agent_pref_list); + + + /* 1. MAP_CTRL_CHAN_SEL_UPDATE WITHOUT EU WEATHERBAND */ + log_test_i("1. MAP_CTRL_CHAN_SEL_UPDATE WITHOUT EU WEATHERBAND"); + radio->cac_caps.has_eu_weatherband = false; + fail_unless(!map_ctrl_chan_sel_update(radio)); + + print_op_class_list("ctrl_pref", ctrl_pref_list); + print_op_class_list("merged_pref", merged_pref_list); + + /* Preferred channels: 100, 104, 108, 112, 116, 120, 124, 128, 132, 136 */ + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 10, (int[]){100, 104, 108, 112, 116, 120, 124, 128, 132, 136})); + + /* Check merged preference */ + fail_unless(merged_pref_list->op_classes_nr == 4); + + fail_unless(merged_pref_list->op_classes[0].op_class == 121); + fail_unless(merged_pref_list->op_classes[0].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[0].channels) == 2); /* 140, 144 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[0].channels, 140)); + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[0].channels, 144)); + + fail_unless(merged_pref_list->op_classes[1].op_class == 122); + fail_unless(merged_pref_list->op_classes[1].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[1].channels) == 1); /* 140 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[1].channels, 140)); + + fail_unless(merged_pref_list->op_classes[2].op_class == 123); + fail_unless(merged_pref_list->op_classes[2].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[2].channels) == 1); /* 144 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[2].channels, 144)); + + fail_unless(merged_pref_list->op_classes[3].op_class == 129); + fail_unless(merged_pref_list->op_classes[3].pref == 5); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[3].channels) == 0); + + + /* 2. MAP_CTRL_CHAN_SEL_UPDATE WITH EU WEATHERBAND */ + log_test_i("2. MAP_CTRL_CHAN_SEL_UPDATE WITH EU WEATHERBAND"); + radio->cac_caps.has_eu_weatherband = true; + fail_unless(!map_ctrl_chan_sel_update(radio)); + + print_op_class_list("ctrl_pref", ctrl_pref_list); + print_op_class_list("merged_pref", merged_pref_list); + + /* Preferred channels: 100, 104, 108, 112, 116, 132, 136 */ + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 10, (int[]){100, 104, 108, 112, 116, 120, 124, 128, 132, 136})); + + /* Check merged preference */ + fail_unless(merged_pref_list->op_classes_nr == 8); + + fail_unless(merged_pref_list->op_classes[0].op_class == 121); + fail_unless(merged_pref_list->op_classes[0].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[0].channels) == 2); /* 140, 144 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[0].channels, 140)); + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[0].channels, 144)); + fail_unless(merged_pref_list->op_classes[1].op_class == 121); + fail_unless(merged_pref_list->op_classes[1].pref == 14); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[1].channels) == 3); /* 120, 124, 128 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[1].channels, 120)); + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[1].channels, 124)); + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[1].channels, 128)); + + fail_unless(merged_pref_list->op_classes[2].op_class == 122); + fail_unless(merged_pref_list->op_classes[2].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[2].channels) == 1); /* 140 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[2].channels, 140)); + fail_unless(merged_pref_list->op_classes[3].op_class == 122); + fail_unless(merged_pref_list->op_classes[3].pref == 14); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[3].channels) == 2); /* 116, 124 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[3].channels, 116)); + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[3].channels, 124)); + + fail_unless(merged_pref_list->op_classes[4].op_class == 123); + fail_unless(merged_pref_list->op_classes[4].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[4].channels) == 1); /* 144 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[4].channels, 144)); + fail_unless(merged_pref_list->op_classes[5].op_class == 123); + fail_unless(merged_pref_list->op_classes[5].pref == 14); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[5].channels) == 2); /* 120, 128 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[5].channels, 120)); + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[5].channels, 128)); + + fail_unless(merged_pref_list->op_classes[6].op_class == 128); + fail_unless(merged_pref_list->op_classes[6].pref == 14); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[6].channels) == 1); /* 122 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[6].channels, 122)); + + fail_unless(merged_pref_list->op_classes[7].op_class == 129); + fail_unless(merged_pref_list->op_classes[7].pref == 5); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[7].channels) == 0); + + + /* 3. MAP_CTRL_CHAN_SEL_UPDATE WITH EU WEATHERBAND AND 120, 124 ALLOWED */ + log_test_i("3. MAP_CTRL_CHAN_SEL_UPDATE WITH EU WEATHERBAND AND 120, 124 ALLOWED"); + radio->cac_caps.has_eu_weatherband = true; + pairs = calloc(20, sizeof(*pairs)); + ale->cac_status_report.available_pairs = pairs; + ale->cac_status_report.available_pairs_nr = 4; + + pairs[0].op_class = 121; pairs[0].channel = 120; + pairs[1].op_class = 121; pairs[1].channel = 124; + pairs[2].op_class = 122; pairs[2].channel = 116; + pairs[3].op_class = 123; pairs[3].channel = 120; + + fail_unless(!map_ctrl_chan_sel_update(radio)); + + print_op_class_list("ctrl_pref", ctrl_pref_list); + print_op_class_list("merged_pref", merged_pref_list); + + /* Preferred channels: 100, 104, 108, 112, 116, 132, 136 */ + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 10, (int[]){100, 104, 108, 112, 116, 120, 124, 128, 132, 136})); + + /* Check merged preference */ + fail_unless(merged_pref_list->op_classes_nr == 8); + + fail_unless(merged_pref_list->op_classes[0].op_class == 121); + fail_unless(merged_pref_list->op_classes[0].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[0].channels) == 2); /* 140, 144 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[0].channels, 140)); + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[0].channels, 144)); + fail_unless(merged_pref_list->op_classes[1].op_class == 121); + fail_unless(merged_pref_list->op_classes[1].pref == 14); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[1].channels) == 1); /* 128 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[1].channels, 128)); + + fail_unless(merged_pref_list->op_classes[2].op_class == 122); + fail_unless(merged_pref_list->op_classes[2].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[2].channels) == 1); /* 140 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[2].channels, 140)); + fail_unless(merged_pref_list->op_classes[3].op_class == 122); + fail_unless(merged_pref_list->op_classes[3].pref == 14); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[3].channels) == 1); /* 124 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[3].channels, 124)); + + fail_unless(merged_pref_list->op_classes[4].op_class == 123); + fail_unless(merged_pref_list->op_classes[4].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[4].channels) == 1); /* 144 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[4].channels, 144)); + fail_unless(merged_pref_list->op_classes[5].op_class == 123); + fail_unless(merged_pref_list->op_classes[5].pref == 14); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[5].channels) == 1); /* 128 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[5].channels, 128)); + + fail_unless(merged_pref_list->op_classes[6].op_class == 128); + fail_unless(merged_pref_list->op_classes[6].pref == 14); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[6].channels) == 1); /* 122 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[6].channels, 122)); + + fail_unless(merged_pref_list->op_classes[7].op_class == 129); + fail_unless(merged_pref_list->op_classes[7].pref == 5); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[7].channels) == 0); + + + /* 4. MAP_CTRL_CHAN_SEL_UPDATE WITH EU WEATHERBAND AND 120, 124, 128 ALLOWED */ + log_test_i("4. MAP_CTRL_CHAN_SEL_UPDATE WITH EU WEATHERBAND AND 120, 124, 128 ALLOWED"); + radio->cac_caps.has_eu_weatherband = true; + ale->cac_status_report.available_pairs_nr = 9; + + pairs[0].op_class = 121; pairs[0].channel = 120; + pairs[1].op_class = 121; pairs[1].channel = 124; + pairs[2].op_class = 121; pairs[2].channel = 128; + pairs[3].op_class = 122; pairs[3].channel = 116; + pairs[4].op_class = 122; pairs[4].channel = 124; + pairs[5].op_class = 123; pairs[5].channel = 120; + pairs[6].op_class = 123; pairs[6].channel = 128; + pairs[7].op_class = 128; pairs[7].channel = 122; + pairs[8].op_class = 129; pairs[8].channel = 114; + + fail_unless(!map_ctrl_chan_sel_update(radio)); + + print_op_class_list("ctrl_pref", ctrl_pref_list); + print_op_class_list("merged_pref", merged_pref_list); + + /* Preferred channels: 100, 104, 108, 112, 116, 132, 136 */ + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 10, (int[]){100, 104, 108, 112, 116, 120, 124, 128, 132, 136})); + + /* Check merged preference (= same as in step 1) */ + fail_unless(merged_pref_list->op_classes_nr == 4); + + fail_unless(merged_pref_list->op_classes[0].op_class == 121); + fail_unless(merged_pref_list->op_classes[0].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[0].channels) == 2); /* 140, 144 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[0].channels, 140)); + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[0].channels, 144)); + + fail_unless(merged_pref_list->op_classes[1].op_class == 122); + fail_unless(merged_pref_list->op_classes[1].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[1].channels) == 1); /* 140 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[1].channels, 140)); + + fail_unless(merged_pref_list->op_classes[2].op_class == 123); + fail_unless(merged_pref_list->op_classes[2].pref == 0); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[2].channels) == 1); /* 144 */ + fail_unless(map_cs_is_set(&merged_pref_list->op_classes[2].channels, 144)); + + fail_unless(merged_pref_list->op_classes[3].op_class == 129); + fail_unless(merged_pref_list->op_classes[3].pref == 5); + fail_unless(map_cs_nr(&merged_pref_list->op_classes[3].channels) == 0); + + + /* FINI */ + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_MULTIAP_ALIGN # +########################################################################*/ +/* Note the test below is only using op class 131. */ +static int check_op_class_131(map_op_class_list_t *list, int excl_nr, int *excl) +{ + return (list->op_classes_nr == 1 ) && + (list->op_classes[0].op_class == 131 ) && + (list->op_classes[0].pref == 0 ) && + (map_cs_nr(&list->op_classes[0].channels) == excl_nr) && + (check_cs(&list->op_classes[0].channels, excl_nr, excl) == 0 ) ? 0 : -1; +} + +START_TEST(test_multiap_align) +{ + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; + map_ale_info_t *ale; + map_ale_info_t *ale2; + map_radio_info_t *radio; + map_radio_info_t *radio2; + map_op_class_list_t *agent_cap_list; + map_op_class_list_t *agent_cap_list2; + map_op_class_list_t *agent_pref_list2; + map_op_class_list_t *ctrl_pref_list; + map_op_class_list_t *ctrl_pref_list2; + map_channel_set_t ctl_channels; + + /* INIT */ + log_test_i("INIT TEST_MULTIAP_ALIGN"); + test_init(DATA_DIR"/does_not_exist"); + + fail_unless(!!(ale = map_dm_create_ale(g_al_mac))); + fail_unless(!!(ale2 = map_dm_create_ale(g_al2_mac))); + fail_unless(!!(radio = map_dm_create_radio(ale, g_radio_id))); + radio->chan_sel.acs_enable = radio->chan_sel.cloud_mgmt_enable = true; + fail_unless(!!(radio2 = map_dm_create_radio(ale2, g_radio2_id))); + radio2->chan_sel.acs_enable = radio2->chan_sel.cloud_mgmt_enable = true; + + set_radio_state_channel_pref_report_received(&radio->state); + set_radio_state_channel_pref_report_received(&radio2->state); + + agent_cap_list = &radio->cap_op_class_list; + agent_cap_list2 = &radio2->cap_op_class_list; + agent_pref_list2 = &radio2->pref_op_class_list; + ctrl_pref_list = &radio->ctrl_pref_op_class_list; + ctrl_pref_list2 = &radio2->ctrl_pref_op_class_list; + + radio->supported_freq = IEEE80211_FREQUENCY_BAND_6_GHZ; + radio2->supported_freq = IEEE80211_FREQUENCY_BAND_6_GHZ; + + /* Add radio operating classes (20MHz: 131 - supported channels 1, 5, 9, 13, 17, 21, 37, 53, 69, 85, 101, 117) */ + agent_cap_list->op_classes_nr = 1; + agent_cap_list->op_classes = calloc(1, sizeof(map_op_class_t)); + agent_cap_list->op_classes[0].op_class = 131; + set_cs(&ctl_channels, 12, (int[]){1, 5, 9, 13, 17, 21, 37, 53, 69, 85, 101, 117}); + map_get_channel_set_from_op_class(131, &agent_cap_list->op_classes[0].channels); + map_cs_and_not(&agent_cap_list->op_classes[0].channels, &ctl_channels); + map_update_radio_channels(radio); + print_op_class_list("radio agent_cap", agent_cap_list); + + + /* 1. MAP_CTRL_CHAN_SEL_UPDATE */ + log_test_i("1. MAP_CTRL_CHAN_SEL_UPDATE"); + fail_unless(!map_ctrl_chan_sel_update(radio)); + + print_op_class_list("radio ctrl_pref", ctrl_pref_list); + + /* Check preferences: 5, 21, 37, 53, 69, 85 are allowed */ + fail_unless(!check_cs(&radio->chan_sel.pref_channels, 6, (int[]){5, 21, 37, 53, 69, 85})); + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 6, (int[]){5, 21, 37, 53, 69, 85})); + fail_unless(!check_op_class_131(ctrl_pref_list, 6, (int[]){1, 9, 13, 17, 101, 117})); + + /* 2. MAP_CTRL_CHAN_SEL_UPDATE RADIO_2 */ + log_test_i("2. MAP_CTRL_CHAN_SEL_UPDATE RADIO_2"); + + /* Configure radio2 same as radio except that it does not support channels 5 and 21 */ + /* Add radio operating classes (20MHz: 131 - supported channels 1, 9, 13, 17, 37, 53, 69, 85, 101, 117) */ + agent_cap_list2->op_classes_nr = 1; + agent_cap_list2->op_classes = calloc(1, sizeof(map_op_class_t)); + agent_cap_list2->op_classes[0].op_class = 131; + set_cs(&ctl_channels, 10, (int[]){1, 9, 13, 17, 37, 53, 69, 85, 101, 117}); + map_get_channel_set_from_op_class(131, &agent_cap_list2->op_classes[0].channels); + map_cs_and_not(&agent_cap_list2->op_classes[0].channels, &ctl_channels); + map_update_radio_channels(radio2); + print_op_class_list("radio_2 agent_cap", agent_cap_list2); + + fail_unless(!map_ctrl_chan_sel_update(radio2)); + + print_op_class_list("radio ctrl_pref", ctrl_pref_list); + print_op_class_list("radio_2 ctrl_pref", ctrl_pref_list2); + + /* Check preferences for RADIO: 5, 21, 37, 53, 69, 85 are allowed (same as above) */ + fail_unless(!check_cs(&radio->chan_sel.pref_channels, 6, (int[]){5, 21, 37, 53, 69, 85})); + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 6, (int[]){5, 21, 37, 53, 69, 85})); + fail_unless(!check_op_class_131(ctrl_pref_list, 6, (int[]){1, 9, 13, 17, 101, 117})); + + /* Check preferences for RADIO_2: 37, 53, 69, 85 are allowed */ + fail_unless(!check_cs(&radio2->chan_sel.pref_channels, 4, (int[]){37, 53, 69, 85})); + fail_unless(!check_cs(&radio2->chan_sel.def_pref_channels, 4, (int[]){37, 53, 69, 85})); + fail_unless(!check_op_class_131(ctrl_pref_list2, 6, (int[]){1, 9, 13, 17, 101, 117})); + + + /* 3. ENABLE ALIGN_MULTIAP AND DO MAP_CTRL_CHAN_SEL_UPDATE RADIO_2 AGAIN */ + log_test_i("3. ENABLE ALIGN_MULTIAP AND DO MAP_CTRL_CHAN_SEL_UPDATE RADIO_2 AGAIN"); + cfg->align_multiap = true; + cfg->align_multiap_backoff_time = 60; + fail_unless(!map_ctrl_chan_sel_update(radio2)); + + print_op_class_list("radio ctrl_pref", ctrl_pref_list); + print_op_class_list("radio_2 ctrl_pref", ctrl_pref_list2); + + /* Check preferences for RADIO: 37, 53, 69, 85 are allowed */ + fail_unless(!check_cs(&radio->chan_sel.pref_channels, 4, (int[]){37, 53, 69, 85})); + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 4, (int[]){37, 53, 69, 85})); + fail_unless(!check_op_class_131(ctrl_pref_list, 8, (int[]){1, 5, 9, 13, 17, 21, 101, 117})); + + /* Check preferences for RADIO_2: 37, 53, 69, 85 are allowed */ + fail_unless(!check_cs(&radio2->chan_sel.pref_channels, 4, (int[]){37, 53, 69, 85})); + fail_unless(!check_cs(&radio2->chan_sel.def_pref_channels, 4, (int[]){37, 53, 69, 85})); + /* Note: 5 and 21 are not excluded because they are not in the capabilities... */ + fail_unless(!check_op_class_131(ctrl_pref_list2, 6, (int[]){1, 9, 13, 17, 101, 117})); + + + /* 4. ADD BAD CHANNELS AND DO MAP_CTRL_CHAN_SEL_UPDATE RADIO_2 AGAIN */ + log_test_i("4. ADD BAD CHANNELS AND DO MAP_CTRL_CHAN_SEL_UPDATE RADIO_2 AGAIN"); + + /* Mark channels 69 and 85 as bad */ + agent_pref_list2->op_classes_nr = 1; + agent_pref_list2->op_classes = calloc(1, sizeof(map_op_class_t)); + agent_pref_list2->op_classes[0].op_class = 131; + agent_pref_list2->op_classes[0].pref = 1; + agent_pref_list2->op_classes[0].reason = 1; + set_cs(&agent_pref_list2->op_classes[0].channels, 2, (int[]){69, 85}); + print_op_class_list("radio_2 agent_pref", agent_pref_list2); + + fail_unless(!map_ctrl_chan_sel_update(radio2)); + + print_op_class_list("radio ctrl_pref", ctrl_pref_list); + print_op_class_list("radio_2 ctrl_pref", ctrl_pref_list2); + + /* Check preferences for RADIO: 37, 53 are allowed */ + fail_unless(!check_cs(&radio->chan_sel.pref_channels, 2, (int[]){37, 53})); + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 2, (int[]){37, 53})); + fail_unless(!check_op_class_131(ctrl_pref_list, 10, (int[]){1, 5, 9, 13, 17, 21, 69, 85, 101, 117})); + + /* Check preferences for RADIO_2: 37, 53 are allowed */ + fail_unless(!check_cs(&radio2->chan_sel.pref_channels, 2, (int[]){37, 53})); + fail_unless(!check_cs(&radio2->chan_sel.def_pref_channels, 2, (int[]){37, 53})); + /* Note: 5 and 21 are not excluded because they are not in the capabilities... */ + fail_unless(!check_op_class_131(ctrl_pref_list2, 8, (int[]){1, 9, 13, 17, 69, 85, 101, 117})); + + + /* 5. ADD CTL AND REMOVE BAD CHANNEL AND DO MAP_CTRL_CHAN_SEL_UPDATE RADIO_2 AGAIN */ + log_test_i("5. ADD CTL AND REMOVE BAD CHANNEL AND DO MAP_CTRL_CHAN_SEL_UPDATE RADIO_2 AGAIN"); + + /* Radio2: add channel 5 to cap channels and remove 85 from bad channels */ + map_cs_unset(&agent_cap_list2->op_classes[0].channels, 5); + map_update_radio_channels(radio2); + map_cs_unset(&agent_pref_list2->op_classes[0].channels, 85); + + fail_unless(!map_ctrl_chan_sel_update(radio2)); + + print_op_class_list("radio ctrl_pref", ctrl_pref_list); + print_op_class_list("radio_2 ctrl_pref", ctrl_pref_list2); + + /* Check preferences for RADIO: 5, 37, 53, 85 are allowed */ + fail_unless(!check_cs(&radio->chan_sel.pref_channels, 4, (int[]){5, 37, 53, 85})); + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 4, (int[]){5, 37, 53, 85})); + fail_unless(!check_op_class_131(ctrl_pref_list, 8, (int[]){1, 9, 13, 17, 21, 69, 101, 117})); + + /* Check preferences for RADIO_2: 37, 53 are allowed */ + fail_unless(!check_cs(&radio2->chan_sel.pref_channels, 4, (int[]){5, 37, 53, 85})); + fail_unless(!check_cs(&radio2->chan_sel.def_pref_channels, 4, (int[]){5, 37, 53, 85})); + /* Note: 21 is not excluded because it not in the capabilities... */ + fail_unless(!check_op_class_131(ctrl_pref_list2, 7, (int[]){1, 9, 13, 17, 69, 101, 117})); + + + /* 6. REMOVE RADIO_2 */ + log_test_i("6. REMOVE RADIO_2"); + map_dm_remove_radio(radio2); + + print_op_class_list("radio ctrl_pref", ctrl_pref_list); + + /* Check preferences for RADIO: 5, 21, 37, 53, 69, 85 are allowed */ + fail_unless(!check_cs(&radio->chan_sel.pref_channels, 6, (int[]){5, 21, 37, 53, 69, 85})); + fail_unless(!check_cs(&radio->chan_sel.def_pref_channels, 6, (int[]){5, 21, 37, 53, 69, 85})); + fail_unless(!check_op_class_131(ctrl_pref_list, 6, (int[]){1, 9, 13, 17, 101, 117})); + + + /* FINI */ + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DUAL_5G_BANDLOCK # +########################################################################*/ +START_TEST(test_dual_5g_bandlock) +{ + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; + map_ale_info_t *ale1; + map_ale_info_t *ale2; + map_radio_info_t *radio1_1; /* ale, low_high */ + map_radio_info_t *radio2_1; /* ale2, low */ + map_radio_info_t *radio2_2; /* ale3, high */ + map_op_class_list_t *agent_cap_list; + map_op_class_list_t *agent_pref_list; + + /* INIT */ + log_test_i("INIT TEST_DUAL_5G_BANDLOCK"); + test_init(); + + /* Set defaults. Allow all channels except 100 */ + map_cs_set_all(&cfg->default_pref_channel_set_5g); + map_cs_unset(&cfg->default_pref_channel_set_5g, 100); + + fail_unless(!!(ale1 = map_dm_create_ale(g_al_mac))); + fail_unless(!!(ale2 = map_dm_create_ale(g_al2_mac))); + fail_unless(!!(radio1_1 = map_dm_create_radio(ale1, g_radio_id))); + radio1_1->chan_sel.acs_enable = radio1_1->chan_sel.cloud_mgmt_enable = true; + fail_unless(!!(radio2_1 = map_dm_create_radio(ale2, g_radio2_id))); + radio2_1->chan_sel.acs_enable = radio2_1->chan_sel.cloud_mgmt_enable = true; + fail_unless(!!(radio2_2 = map_dm_create_radio(ale2, g_radio3_id))); + radio2_2->chan_sel.acs_enable = radio2_2->chan_sel.cloud_mgmt_enable = true; + + radio1_1->supported_freq = IEEE80211_FREQUENCY_BAND_5_GHZ; + radio1_1->band_type_5G = MAP_M2_BSS_RADIO5GL | MAP_M2_BSS_RADIO5GU; + radio2_1->supported_freq = IEEE80211_FREQUENCY_BAND_5_GHZ; + radio2_1->band_type_5G = MAP_M2_BSS_RADIO5GL; + radio2_2->supported_freq = IEEE80211_FREQUENCY_BAND_5_GHZ; + radio2_2->band_type_5G = MAP_M2_BSS_RADIO5GU; + + set_radio_state_channel_pref_report_received(&radio1_1->state); + set_radio_state_channel_pref_report_received(&radio2_1->state); + set_radio_state_channel_pref_report_received(&radio2_2->state); + + /* Radio1_1: 36->44 and 100->144, bad_channels 36 and 144 */ + agent_cap_list = &radio1_1->cap_op_class_list; + agent_cap_list->op_classes_nr = 2; + agent_cap_list->op_classes = calloc(2, sizeof(map_op_class_t)); + agent_cap_list->op_classes[0].op_class = 115; + set_cs(&agent_cap_list->op_classes[0].channels, 1, (int[]){48}); + agent_cap_list->op_classes[1].op_class = 121; + print_op_class_list("radio1_1 agent_cap", agent_cap_list); + + agent_pref_list = &radio1_1->pref_op_class_list; + agent_pref_list->op_classes_nr = 2; + agent_pref_list->op_classes = calloc(2, sizeof(map_op_class_t)); + agent_pref_list->op_classes[0].op_class = 115; + agent_pref_list->op_classes[0].pref = 1; + agent_pref_list->op_classes[0].reason = 6; + set_cs(&agent_pref_list->op_classes[0].channels, 1, (int[]){36}); + agent_pref_list->op_classes[1].op_class = 121; + agent_pref_list->op_classes[1].pref = 1; + agent_pref_list->op_classes[1].reason = 6; + set_cs(&agent_pref_list->op_classes[1].channels, 1, (int[]){144}); + print_op_class_list("radio1_1 agent_pref", agent_cap_list); + + /* Radio2_1: 36->48, bad channel 40 */ + agent_cap_list = &radio2_1->cap_op_class_list; + agent_cap_list->op_classes_nr = 1; + agent_cap_list->op_classes = calloc(1, sizeof(map_op_class_t)); + agent_cap_list->op_classes[0].op_class = 115; + print_op_class_list("radio2_1 agent_cap", agent_cap_list); + + agent_pref_list = &radio2_1->pref_op_class_list; + agent_pref_list->op_classes_nr = 1; + agent_pref_list->op_classes = calloc(1, sizeof(map_op_class_t)); + agent_pref_list->op_classes[0].op_class = 115; + agent_pref_list->op_classes[0].pref = 1; + agent_pref_list->op_classes[0].reason = 6; + set_cs(&agent_pref_list->op_classes[0].channels, 1, (int[]){40}); + print_op_class_list("radio2_1 agent_pref", agent_cap_list); + + /* Radio2_2: 100->144 but no weatherband, bad_channel 140 */ + agent_cap_list = &radio2_2->cap_op_class_list; + agent_cap_list->op_classes_nr = 1; + agent_cap_list->op_classes = calloc(1, sizeof(map_op_class_t)); + agent_cap_list->op_classes[0].op_class = 121; + set_cs(&agent_cap_list->op_classes[0].channels, 3, (int[]){120, 124, 128}); + print_op_class_list("radio2_2 agent_cap", agent_cap_list); + + agent_pref_list = &radio2_2->pref_op_class_list; + agent_pref_list->op_classes_nr = 1; + agent_pref_list->op_classes = calloc(1, sizeof(map_op_class_t)); + agent_pref_list->op_classes[0].op_class = 121; + agent_pref_list->op_classes[0].pref = 1; + agent_pref_list->op_classes[0].reason = 6; + set_cs(&agent_pref_list->op_classes[0].channels, 1, (int[]){140}); + print_op_class_list("radio2_2 agent_pref", agent_cap_list); + + /* Create ctl channels */ + map_update_radio_channels(radio1_1); + map_update_radio_channels(radio2_1); + map_update_radio_channels(radio2_2); + + fail_unless(!check_cs(&radio1_1->ctl_channels, 15, (int[]){36, 40, 44, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144})); + fail_unless(!check_cs(&radio2_1->ctl_channels, 4, (int[]){36, 40, 44, 48})); + fail_unless(!check_cs(&radio2_2->ctl_channels, 9, (int[]){100, 104, 108, 112, 116, 132, 136, 140, 144})); + + + /* 1. CHANNEL SELECTION (ALIGN_MULTIAP=FALSE) */ + log_test_i("1. CHANNEL SELECTION (ALIGN_MULTIAP=FALSE)"); + + fail_unless(!map_ctrl_chan_sel_update(radio1_1)); + fail_unless(!map_ctrl_chan_sel_update(radio2_1)); + fail_unless(!map_ctrl_chan_sel_update(radio2_2)); + + fail_unless(!check_cs(&radio1_1->chan_sel.pref_channels, 12, (int[]){40, 44, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140})); + fail_unless(!check_cs(&radio2_1->chan_sel.pref_channels, 3, (int[]){36, 44, 48})); + fail_unless(!check_cs(&radio2_2->chan_sel.pref_channels, 7, (int[]){104, 108, 112, 116, 132, 136, 144})); + + + /* 2. CHANNEL SELECTION (ALIGN_MULTIAP=TRUE) */ + log_test_i("2. CHANNEL SELECTION (ALIGN_MULTIAP=TRUE)"); + cfg->align_multiap = true; + + /* Note: because align multiap is enabled "in the middle", the first + update was not tracked and we are currently not multiap_aliging the + preference of the radio that is being updated. + + -> call 2 times + */ + fail_unless(!map_ctrl_chan_sel_update(radio1_1)); + fail_unless(!map_ctrl_chan_sel_update(radio2_1)); + fail_unless(!map_ctrl_chan_sel_update(radio2_2)); + + fail_unless(!map_ctrl_chan_sel_update(radio1_1)); + fail_unless(!map_ctrl_chan_sel_update(radio2_1)); + fail_unless(!map_ctrl_chan_sel_update(radio2_2)); + + fail_unless(!check_cs(&radio1_1->chan_sel.pref_channels, 7, (int[]){44, 104, 108, 112, 116, 132, 136})); + fail_unless(!check_cs(&radio2_1->chan_sel.pref_channels, 1, (int[]){44})); + fail_unless(!check_cs(&radio2_2->chan_sel.pref_channels, 6, (int[]){104, 108, 112, 116, 132, 136})); + + + /* 3. ENABLE BANDLOCK */ + log_test_i("3. ENABLE BANDLOCK"); + + cfg->bandlock_5g = MAP_BANDLOCK_5G_HIGH; + map_update_radio_channels(radio1_1); + map_update_radio_channels(radio2_1); + map_update_radio_channels(radio2_2); + + fail_unless(!check_cs(&radio1_1->ctl_channels, 12, (int[]){100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144})); + fail_unless(!check_cs(&radio2_1->ctl_channels, 4, (int[]){36, 40, 44, 48})); + fail_unless(!check_cs(&radio2_2->ctl_channels, 9, (int[]){100, 104, 108, 112, 116, 132, 136, 140, 144})); + + /* Run channel selection again. + - radio1_1 is bandlocked + - radio2_1 can use channel 48 again + */ + fail_unless(!map_ctrl_chan_sel_update(radio1_1)); + fail_unless(!map_ctrl_chan_sel_update(radio2_1)); + fail_unless(!map_ctrl_chan_sel_update(radio2_2)); + + fail_unless(!check_cs(&radio1_1->chan_sel.pref_channels, 6, (int[]){104, 108, 112, 116, 132, 136})); + fail_unless(!check_cs(&radio2_1->chan_sel.pref_channels, 3, (int[]){36, 44, 48})); + fail_unless(!check_cs(&radio2_2->chan_sel.pref_channels, 6, (int[]){104, 108, 112, 116, 132, 136})); + + /* FINI */ + test_fini(); +} +END_TEST + + +/*####################################################################### +# TEST_REMOVED_STRICT_ALLOWED_CHANNELS # +########################################################################*/ +START_TEST(test_removed_strict_allowed_channels) +{ + map_chan_sel_cfg_t *cfg = &get_controller_cfg()->chan_sel; + map_ale_info_t *ale; + map_radio_info_t *radio_5g; + map_radio_info_t *radio_2g; + map_radio_info_t *radio_6g; + map_op_class_list_t *pref_list_5g; + map_op_class_list_t *pref_list_2g; + map_op_class_list_t *pref_list_6g; + //char buf[MAP_CS_BUF_LEN]; + + log_test_i("INIT TEST_REMOVED_STRICT_ALLOWED_CHANNELS"); + test_init(); + + /* Change 5G and 6G default preferred channels */ + set_cs(&cfg->default_pref_channel_set_5g, 12, (int[]){36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112}); + set_cs(&cfg->default_pref_channel_set_6g, 4, (int[]){5, 21, 37, 53}); /* PSC only */ + + fail_unless(!!(ale = map_dm_create_ale(g_al_mac))); + fail_unless(!!(radio_5g = map_dm_create_radio(ale, g_radio_id))); + radio_5g->chan_sel.acs_enable = radio_5g->chan_sel.cloud_mgmt_enable = true; + fail_unless(!!(radio_2g = map_dm_create_radio(ale, g_radio2_id))); + radio_2g->chan_sel.acs_enable = radio_2g->chan_sel.cloud_mgmt_enable = true; + fail_unless(!!(radio_6g = map_dm_create_radio(ale, g_radio3_id))); + radio_6g->chan_sel.acs_enable = radio_6g->chan_sel.cloud_mgmt_enable = true; + + pref_list_5g = &radio_5g->ctrl_pref_op_class_list; + pref_list_2g = &radio_2g->ctrl_pref_op_class_list; + pref_list_6g = &radio_6g->ctrl_pref_op_class_list; + + radio_5g->supported_freq = IEEE80211_FREQUENCY_BAND_5_GHZ; + radio_2g->supported_freq = IEEE80211_FREQUENCY_BAND_2_4_GHZ; + radio_6g->supported_freq = IEEE80211_FREQUENCY_BAND_6_GHZ; + + /* add radio operating classes 20MHz: 115, 118, 40MHz: 116, 117, 119, 120, 121, 122, 123 80MHz: 128 */ + radio_5g->cap_op_class_list.op_classes_nr = 10; + radio_5g->cap_op_class_list.op_classes = calloc(10, sizeof(map_op_class_t)); + radio_5g->cap_op_class_list.op_classes[0].op_class = 115; + radio_5g->cap_op_class_list.op_classes[1].op_class = 118; + radio_5g->cap_op_class_list.op_classes[2].op_class = 116; + radio_5g->cap_op_class_list.op_classes[3].op_class = 117; + radio_5g->cap_op_class_list.op_classes[4].op_class = 119; + radio_5g->cap_op_class_list.op_classes[5].op_class = 120; + radio_5g->cap_op_class_list.op_classes[6].op_class = 121; + radio_5g->cap_op_class_list.op_classes[7].op_class = 122; + radio_5g->cap_op_class_list.op_classes[8].op_class = 123; + radio_5g->cap_op_class_list.op_classes[9].op_class = 128; + + /* add radio operating classes 20MHz: 81, 40MHz: 83, 84 */ + radio_2g->cap_op_class_list.op_classes_nr = 3; + radio_2g->cap_op_class_list.op_classes = calloc(3, sizeof(map_op_class_t)); + radio_2g->cap_op_class_list.op_classes[0].op_class = 81; + radio_2g->cap_op_class_list.op_classes[1].op_class = 83; + radio_2g->cap_op_class_list.op_classes[2].op_class = 84; + + /* add radio operating classes 20MHz: 131, 40MHz: 132, 80MHz: 133, 160MHz: 134 */ + radio_6g->cap_op_class_list.op_classes_nr = 4; + radio_6g->cap_op_class_list.op_classes = calloc(4, sizeof(map_op_class_t)); + radio_6g->cap_op_class_list.op_classes[0].op_class = 131; + radio_6g->cap_op_class_list.op_classes[1].op_class = 132; + radio_6g->cap_op_class_list.op_classes[2].op_class = 133; + radio_6g->cap_op_class_list.op_classes[3].op_class = 134; + + // 5GHz radio + set_cs(&radio_5g->ctl_channels, 9, (int[]){36, 40, 44, 48, 52, 60, 64, 104, 108}); + + // 2.4GHz radio + set_cs(&radio_2g->ctl_channels, 6, (int[]){1, 2, 3, 4, 5, 6}); + + // 6GHz radio + set_cs(&radio_6g->ctl_channels, 2, (int[]){5, 21}); /* PSC only */ + + /* map_ctrl_chan_sel_update */ + fail_unless(!map_ctrl_chan_sel_update(radio_5g)); + fail_unless(!map_ctrl_chan_sel_update(radio_2g)); + fail_unless(!map_ctrl_chan_sel_update(radio_6g)); + + /* CHECK 5GHz RADIO */ + fail_unless(!check_cs(&radio_5g->chan_sel.pref_channels, 9, (int[]){36, 40, 44, 48, 52, 60, 64, 104, 108})); + fail_unless(!check_cs(&radio_5g->chan_sel.def_pref_channels, 9, (int[]){36, 40, 44, 48, 52, 60, 64, 104, 108})); + + fail_unless(pref_list_5g->op_classes_nr == 7); + /* + for(int i = 0; i < pref_list_5g->op_classes_nr; i++) { + log_test_i("i=%d class=%d #chan=%d %s\n", pref_list_5g->op_classes_nr, pref_list_5g->op_classes[i].op_class, map_cs_nr(&pref_list_5g->op_classes[i].channels), + map_cs_to_string(&pref_list_5g->op_classes[i].channels, ',', buf, sizeof(buf))); + } + */ + + /* 20MHz */ + fail_unless(pref_list_5g->op_classes[0].op_class == 118); + fail_unless(pref_list_5g->op_classes[0].pref == 0); + fail_unless(map_cs_nr(&pref_list_5g->op_classes[0].channels) == 1); /* 56 */ + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[0].channels, 56)); + + /* 40MHz lower */ + fail_unless(pref_list_5g->op_classes[1].op_class == 119); + fail_unless(pref_list_5g->op_classes[1].pref == 0); + fail_unless(map_cs_nr(&pref_list_5g->op_classes[1].channels) == 1); /* 52 */ + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[1].channels, 52)); + + /* 40MHz upper */ + fail_unless(pref_list_5g->op_classes[2].op_class == 120); + fail_unless(pref_list_5g->op_classes[2].pref == 0); + fail_unless(map_cs_nr(&pref_list_5g->op_classes[2].channels) == 1); /* 56 */ + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[2].channels, 56)); + + /* 20MHz */ + fail_unless(pref_list_5g->op_classes[3].op_class == 121); + fail_unless(pref_list_5g->op_classes[3].pref == 0); + fail_unless(map_cs_nr(&pref_list_5g->op_classes[3].channels) == 10); /* 100, 112, 116, 120, 124, 128, 132, 136, 140, 144 */ + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[3].channels, 100)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[3].channels, 112)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[3].channels, 116)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[3].channels, 120)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[3].channels, 124)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[3].channels, 128)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[3].channels, 136)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[3].channels, 140)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[3].channels, 144)); + + /* 40MHz lower */ + fail_unless(pref_list_5g->op_classes[4].op_class == 122); + fail_unless(pref_list_5g->op_classes[4].pref == 0); + fail_unless(map_cs_nr(&pref_list_5g->op_classes[4].channels) == 6); /* 100, 108, 116, 124, 132, 140 */ + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[4].channels, 100)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[4].channels, 108)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[4].channels, 116)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[4].channels, 124)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[4].channels, 132)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[4].channels, 140)); + + /* 40MHz upper */ + fail_unless(pref_list_5g->op_classes[5].op_class == 123); + fail_unless(pref_list_5g->op_classes[5].pref == 0); + fail_unless(map_cs_nr(&pref_list_5g->op_classes[5].channels) == 6); /* 104, 112, 120, 128, 136, 144 */ + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[5].channels, 104)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[5].channels, 112)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[5].channels, 120)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[5].channels, 128)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[5].channels, 136)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[5].channels, 144)); + + /* 80MHz */ + fail_unless(pref_list_5g->op_classes[6].op_class == 128); + fail_unless(pref_list_5g->op_classes[6].pref == 0); + fail_unless(map_cs_nr(&pref_list_5g->op_classes[6].channels) == 6); /* 58, 106, 122, 138, 155, 171 */ + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[6].channels, 58)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[6].channels, 106)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[6].channels, 122)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[6].channels, 138)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[6].channels, 155)); + fail_unless(map_cs_is_set(&pref_list_5g->op_classes[6].channels, 171)); + + /* CHECK 2.4GHz RADIO */ + fail_unless(!check_cs(&radio_2g->chan_sel.pref_channels, 6, (int[]){1, 2, 3, 4, 5, 6})); + fail_unless(!check_cs(&radio_2g->chan_sel.def_pref_channels, 6, (int[]){1, 2, 3, 4, 5, 6})); + + fail_unless(pref_list_2g->op_classes_nr == 3); + /* + for(int i = 0; i < pref_list_2g->op_classes_nr; i++) { + log_test_i("i=%d class=%d #chan=%d %s\n", pref_list_2g->op_classes_nr, pref_list_2g->op_classes[i].op_class, map_cs_nr(&pref_list_2g->op_classes[i].channels), + map_cs_to_string(&pref_list_2g->op_classes[i].channels, ',', buf, sizeof(buf))); + }*/ + + /* 20MHz */ + fail_unless(pref_list_2g->op_classes[0].op_class == 81); + fail_unless(pref_list_2g->op_classes[0].pref == 0); + fail_unless(map_cs_nr(&pref_list_2g->op_classes[0].channels) == 7); /* 7, 8, 9, 10, 11, 12, 13 */ + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[0].channels, 7)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[0].channels, 8)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[0].channels, 9)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[0].channels, 10)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[0].channels, 11)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[0].channels, 12)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[0].channels, 13)); + + /* 40MHz lower */ + fail_unless(pref_list_2g->op_classes[1].op_class == 83); + fail_unless(pref_list_2g->op_classes[1].pref == 0); + fail_unless(map_cs_nr(&pref_list_2g->op_classes[1].channels) == 7); /* 3, 4, 5, 6, 7, 8, 9 */ + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[1].channels, 3)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[1].channels, 4)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[1].channels, 5)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[1].channels, 6)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[1].channels, 7)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[1].channels, 8)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[1].channels, 9)); + + /* 40MHz upper */ + fail_unless(pref_list_2g->op_classes[2].op_class == 84); + fail_unless(pref_list_2g->op_classes[2].pref == 0); + fail_unless(map_cs_nr(&pref_list_2g->op_classes[2].channels) == 7); /* 7, 8, 9, 10, 11, 12, 13 */ + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[2].channels, 7)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[2].channels, 8)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[2].channels, 9)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[2].channels, 10)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[2].channels, 11)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[2].channels, 12)); + fail_unless(map_cs_is_set(&pref_list_2g->op_classes[2].channels, 13)); + + /* CHECK 6GHz RADIO (with PSC set) */ + fail_unless(!check_cs(&radio_6g->chan_sel.pref_channels, 2, (int[]){5, 21})); + fail_unless(!check_cs(&radio_6g->chan_sel.def_pref_channels, 2, (int[]){5, 21})); + + fail_unless(pref_list_6g->op_classes_nr == 4); + + /* 20MHz */ + fail_unless(pref_list_6g->op_classes[0].op_class == 131); + fail_unless(pref_list_6g->op_classes[0].pref == 0); + fail_unless(map_cs_nr(&pref_list_6g->op_classes[0].channels) == 59 - 2); /* all except 5 and 21 (1, 9, 13, 17, 25, ..., 233) */ + fail_unless(!map_cs_is_set(&pref_list_6g->op_classes[0].channels, 5)); + fail_unless(!map_cs_is_set(&pref_list_6g->op_classes[0].channels, 21)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[0].channels, 1)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[0].channels, 9)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[0].channels, 13)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[0].channels, 17)); + + /* 40 MHz */ + fail_unless(pref_list_6g->op_classes[1].op_class == 132); + fail_unless(pref_list_6g->op_classes[1].pref == 0); + fail_unless(map_cs_nr(&pref_list_6g->op_classes[1].channels) == 29 - 2); /* all except 3 and 19 (11, 27, 35, 43, 51, ..., 227) */ + fail_unless(!map_cs_is_set(&pref_list_6g->op_classes[1].channels, 3)); + fail_unless(!map_cs_is_set(&pref_list_6g->op_classes[1].channels, 19)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[1].channels, 11)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[1].channels, 27)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[1].channels, 35)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[1].channels, 227)); + + /* 80 MHz */ + fail_unless(pref_list_6g->op_classes[2].op_class == 133); + fail_unless(pref_list_6g->op_classes[2].pref == 0); + fail_unless(map_cs_nr(&pref_list_6g->op_classes[2].channels) == 14 - 2); /* all except 7 and 23 (39, 55, 71, 87, ..., 215) */ + fail_unless(!map_cs_is_set(&pref_list_6g->op_classes[2].channels, 7)); + fail_unless(!map_cs_is_set(&pref_list_6g->op_classes[2].channels, 32)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[2].channels, 39)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[2].channels, 55)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[2].channels, 71)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[2].channels, 215)); + + /* 160 MHz */ + fail_unless(pref_list_6g->op_classes[3].op_class == 134); + fail_unless(pref_list_6g->op_classes[3].pref == 0); + fail_unless(map_cs_nr(&pref_list_6g->op_classes[3].channels) == 7 - 1); /* all except 15 (47, 79, 111, 143, ..., 207) */ + fail_unless(!map_cs_is_set(&pref_list_6g->op_classes[3].channels, 15)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[3].channels, 47)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[3].channels, 79)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[3].channels, 111)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[3].channels, 207)); + + /* CHECK 6GHz RADIO (without PSC set) -> 40/80/160 are no longer allowed */ + cfg->allowed_channel_6g_psc = false; + fail_unless(!map_ctrl_chan_sel_update(radio_6g)); + + /* 20MHz */ + fail_unless(pref_list_6g->op_classes[0].op_class == 131); + fail_unless(pref_list_6g->op_classes[0].pref == 0); + fail_unless(map_cs_nr(&pref_list_6g->op_classes[0].channels) == 59 - 2); /* all except 5 and 21 (1, 9, 13, 17, 25, ..., 233) */ + fail_unless(!map_cs_is_set(&pref_list_6g->op_classes[0].channels, 5)); + fail_unless(!map_cs_is_set(&pref_list_6g->op_classes[0].channels, 21)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[0].channels, 1)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[0].channels, 9)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[0].channels, 13)); + fail_unless(map_cs_is_set(&pref_list_6g->op_classes[0].channels, 17)); + + /* 40MHz */ + fail_unless(pref_list_6g->op_classes[1].op_class == 132); + fail_unless(pref_list_6g->op_classes[1].pref == 0); + fail_unless(map_cs_nr(&pref_list_6g->op_classes[1].channels) == 29); /* all (3, 11, 19, ..., 227) */ + + /* 80MHz */ + fail_unless(pref_list_6g->op_classes[2].op_class == 133); + fail_unless(pref_list_6g->op_classes[2].pref == 0); + fail_unless(map_cs_nr(&pref_list_6g->op_classes[2].channels) == 14); /* all (7, 23, 39, ..., 227) */ + + /* 160MHz */ + fail_unless(pref_list_6g->op_classes[3].op_class == 134); + fail_unless(pref_list_6g->op_classes[3].pref == 0); + fail_unless(map_cs_nr(&pref_list_6g->op_classes[3].channels) == 7); /* all (15, 47, 79, ..., 207) */ + + test_fini(); +} +END_TEST + +const char *test_suite_name = "chan_sel"; +test_case_t test_cases[] = { + TEST("chan_sel", test_chan_sel ), + TEST("chan_sel_weatherband", test_chan_sel_weatherband ), + TEST("multiap_align", test_multiap_align ), + TEST("dual_5g_bandlock", test_dual_5g_bandlock ), + TEST("removed_strict_allowed_channels", test_removed_strict_allowed_channels ), + TEST_CASES_END +}; diff --git a/source/test/controller/test_cli.c b/source/test/controller/test_cli.c new file mode 100644 index 0000000..ef2670d --- /dev/null +++ b/source/test/controller/test_cli.c @@ -0,0 +1,1325 @@ +/* + * Copyright (c) 2021-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include +#include + +#include "test.h" + +#include "map_ctrl_cli.h" +#include "map_ctrl_emex_tlv_handler.h" +#include "map_data_model.h" +#include "map_config.h" +#include "map_tlvs.h" + +#include "stub/stub_map_ctrl_cmdu_tx.h" +#include "../libutils/stub/stub_map_cli.h" + +/*####################################################################### +# DEFINES # +########################################################################*/ +#define AL_MAC_STR "F6:17:B8:86:57:61" +#define AL_MAC_QSTR "\"F6:17:B8:86:57:61\"" + +#define RADIO_ID_STR "F4:17:B8:86:57:61" +#define RADIO_ID_QSTR "\"F4:17:B8:86:57:61\"" + +#define RADIO2_ID_STR "F4:17:B8:86:57:62" +#define RADIO2_ID_QSTR "\"F4:17:B8:86:57:62\"" + +#define BSSID_STR "F2:17:B8:86:57:61" +#define BSSID_QSTR "\"F2:17:B8:86:57:61\"" + +#define BSSID2_STR "F2:17:B8:86:57:62" +#define BSSID2_QSTR "\"F2:17:B8:86:57:62\"" + +#define STA_MAC_STR "A8:DB:03:05:92:C1" +#define STA_MAC_QSTR "\"A8:DB:03:05:92:C1\"" + +#define STA2_MAC_STR "A8:DB:03:05:92:C2" +#define STA2_MAC_QSTR "\"A8:DB:03:05:92:C2\"" + +#define STA3_MAC_STR "A8:DB:03:05:92:C3" +#define STA3_MAC_QSTR "\"A8:DB:03:05:92:C3\"" + +#define PBUF_CONTAINS(s) !!strcasestr(g_print_buf, s) + +/*####################################################################### +# GLOBALS # +########################################################################*/ +static mac_addr g_al_mac = {0xF6, 0x17, 0xB8, 0x86, 0x57, 0x61}; +static mac_addr g_radio_id = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x61}; +static mac_addr g_radio2_id = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x62}; +static mac_addr g_bssid = {0xF2, 0x17, 0xB8, 0x86, 0x57, 0x61}; +static mac_addr g_bssid2 = {0xF2, 0x17, 0xB8, 0x86, 0x57, 0x62}; +static mac_addr g_sta_mac = {0xA8, 0xDB, 0x03, 0x05, 0x92, 0xC1}; +static mac_addr g_sta2_mac = {0xA8, 0xDB, 0x03, 0x05, 0x92, 0xC2}; +static mac_addr g_sta3_mac = {0xA8, 0xDB, 0x03, 0x05, 0x92, 0xC3}; + +static map_ale_info_t *g_ale; +static map_radio_info_t *g_radio; +static map_radio_info_t *g_radio2; +static map_bss_info_t *g_bss; +static map_bss_info_t *g_bss2; +static map_sta_info_t *g_sta; +static map_sta_info_t *g_sta2; +static map_sta_info_t *g_sta3; + +static bool g_cb_called; +static char *g_print_buf; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static void print_buf_init(void) +{ + free(g_print_buf); + fail_unless(!!(g_print_buf = strdup(""))); +} + +static void test_init(void) +{ + map_cfg_get()->is_master = true; + + fail_unless(!map_info_init()); + fail_unless(!map_dm_init()); + fail_unless(!map_cli_init()); + + /* Add some data */ + fail_unless(!!(g_ale = map_dm_create_ale (g_al_mac))); + fail_unless(!!(g_radio = map_dm_create_radio(g_ale, g_radio_id))); + fail_unless(!!(g_radio2 = map_dm_create_radio(g_ale, g_radio2_id))); + fail_unless(!!(g_bss = map_dm_create_bss (g_radio, g_bssid))); + fail_unless(!!(g_bss2 = map_dm_create_bss (g_radio2, g_bssid2))); + fail_unless(!!(g_sta = map_dm_create_sta (g_bss, g_sta_mac))); + fail_unless(!!(g_sta2 = map_dm_create_sta (g_bss, g_sta_mac))); + fail_unless(!!(g_sta3 = map_dm_create_sta (g_bss2, g_sta_mac))); + + print_buf_init(); + stub_map_cli_set_print_buf(&g_print_buf); + + g_cb_called = false; +} + +static void test_fini(void) +{ + stub_map_cli_set_print_buf(NULL); + stub_cmdu_tx_register_send_topology_query_cb(NULL); + stub_cmdu_tx_register_send_link_metric_query_cb(NULL); + stub_cmdu_tx_register_send_autoconfig_renew_cb(NULL); + stub_cmdu_tx_register_send_autoconfig_renew_ucast_cb(NULL); + stub_cmdu_tx_register_send_ap_capability_query_cb(NULL); + stub_cmdu_tx_register_send_channel_preference_query_cb(NULL); + stub_cmdu_tx_register_send_client_capability_query_cb(NULL); + stub_cmdu_tx_register_send_assoc_sta_link_metrics_query_cb(NULL); + stub_cmdu_tx_register_send_unassoc_sta_link_metrics_query_cb(NULL); + stub_cmdu_tx_register_send_beacon_metrics_query_cb(NULL); + stub_cmdu_tx_register_send_combined_infrastructure_metrics_cb(NULL); + stub_cmdu_tx_register_send_client_steering_request_cb(NULL); + stub_cmdu_tx_register_send_client_acl_request_cb(NULL); + stub_cmdu_tx_register_send_backhaul_sta_capability_query_cb(NULL); + stub_cmdu_tx_register_send_backhaul_steering_request_cb(NULL); + stub_cmdu_tx_register_send_policy_config_request_cb(NULL); + stub_cmdu_tx_register_send_channel_scan_request_cb(NULL); + stub_cmdu_tx_register_send_cac_request_cb(NULL); + stub_cmdu_tx_register_send_cac_termination_cb(NULL); + stub_cmdu_tx_register_send_dpp_cce_indication_cb(NULL); + stub_cmdu_tx_register_send_proxied_encap_dpp_cb(NULL); + stub_cmdu_tx_register_send_raw_cb(NULL); + + SFREE(g_print_buf); + + map_cli_fini(); + map_dm_fini(); + map_info_fini(); +} + +static int map_cli_exec(char *cmd, char *payload) +{ + print_buf_init(); + + return stub_map_cli_exec(cmd, payload); +} + +static void test_cmd_help(char *cmd) +{ + fail_unless(!map_cli_exec(cmd, "{\"args\":\"help\"}")); + + fail_unless(PBUF_CONTAINS("Help")); + fail_unless(PBUF_CONTAINS("Example")); + fail_unless(PBUF_CONTAINS(cmd)); +} + +/*####################################################################### +# TEST_HELP # +########################################################################*/ +START_TEST(test_help) +{ + test_init(); + + fail_unless(!map_cli_exec("help", NULL)); + fail_unless(PBUF_CONTAINS("dumpCtrlInfo")); + fail_unless(PBUF_CONTAINS("sendTopologyQuery")); + fail_unless(PBUF_CONTAINS("sendWFACAPI")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_INVALID # +########################################################################*/ +START_TEST(test_invalid) +{ + test_init(); + + fail_unless(map_cli_exec("invalid", NULL)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_VERSION # +########################################################################*/ +START_TEST(test_version) +{ + test_init(); + + map_cfg_get()->version = "test"; + + fail_unless(!map_cli_exec("version", NULL)); + fail_unless(PBUF_CONTAINS("test")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CASE_INSENSITIVE # +########################################################################*/ +START_TEST(test_case_insensitive) +{ + test_init(); + + map_cfg_get()->version = "test"; + + fail_unless(!map_cli_exec("VeRsIoN", NULL)); + fail_unless(PBUF_CONTAINS("test")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DUMP_CTRL_INFO # +########################################################################*/ +START_TEST(test_dump_ctrl_info) +{ + test_init(); + + fail_unless(!map_cli_exec("dumpCtrlInfo", NULL)); + fail_unless(PBUF_CONTAINS(AL_MAC_STR)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DUMP_INTERFACES # +########################################################################*/ +START_TEST(test_dump_interfaces) +{ + test_init(); + + fail_unless(!map_cli_exec("dumpInterfaces", NULL)); + fail_unless(PBUF_CONTAINS("eth0")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DUMP_BLOCKLIST # +########################################################################*/ +START_TEST(test_dump_blocklist) +{ + test_init(); + + fail_unless(!map_cli_exec("dumpBlockList", NULL)); + fail_unless(PBUF_CONTAINS("dump blocklist")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DUMP_OP_CLASSES # +########################################################################*/ +START_TEST(test_dump_op_classes) +{ + test_init(); + + fail_unless(!map_cli_exec("dumpOpClasses", NULL)); + fail_unless(PBUF_CONTAINS(" 81 2.4GHz 20MHz no 13 1,2,3,4,5,6,7,8,9,10,11,12,13")); + fail_unless(PBUF_CONTAINS(" 137 6GHz 320MHz yes 6 31,63,95,127,159,191")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DUMP_CHAN_SEL # +########################################################################*/ +START_TEST(test_dump_chan_sel) +{ + test_init(); + + print_buf_init(); + fail_unless(!map_cli_exec("dumpChanSel", NULL)); + fail_unless(PBUF_CONTAINS("dump chan_sel") && !PBUF_CONTAINS("ALE") && !PBUF_CONTAINS("EXT")); + + print_buf_init(); + fail_unless(!map_cli_exec("dumpChanSel", "{\"extended\":true}")); + fail_unless(PBUF_CONTAINS("dump chan_sel") && !PBUF_CONTAINS("ALE") && PBUF_CONTAINS("EXT")); + + print_buf_init(); + fail_unless(!map_cli_exec("dumpChanSel", "{\"almac\":"AL_MAC_QSTR"}")); + fail_unless(PBUF_CONTAINS("dump chan_sel") && PBUF_CONTAINS("ALE") && !PBUF_CONTAINS("EXT")); + + print_buf_init(); + fail_unless(!map_cli_exec("dumpChanSel", "{\"almac\":"AL_MAC_QSTR",\"extended\":true}")); + fail_unless(PBUF_CONTAINS("dump chan_sel") && PBUF_CONTAINS("ALE") && PBUF_CONTAINS("EXT")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DUMP_TUNNELED_MESSAGE # +########################################################################*/ +START_TEST(test_dump_tunneled_message) +{ + map_tunneled_msg_t *tm; + int i; + + test_init(); + + /* Add some tunneled message */ + fail_unless(!!(tm = calloc(1, sizeof(*tm)))); + fail_unless(!!(tm->assoc_req_body = malloc(8))); + tm->assoc_req_body_len = 8; + for (i = 0; i < 8; i++) { + tm->assoc_req_body[i] = i; + } + + g_sta->tunneled_msg = tm; + fail_unless(!map_cli_exec("dumpTunneledMessage", "{\"mac\":"STA_MAC_QSTR",\"msgtype\":\"assoc\"}")); + fail_unless(PBUF_CONTAINS("ASSOC_REQ BODY")); + fail_unless(PBUF_CONTAINS("0001020304050607")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DUMP_AP_METRICS # +########################################################################*/ +START_TEST(test_dump_ap_metrics) +{ + test_init(); + + g_bss->metrics.channel_utilization = 10; + g_bss->metrics.esp_present = 0xf0; + g_bss->extended_metrics.tx_ucast_bytes = 100; + + fail_unless(!map_cli_exec("dumpAPMetrics", "{\"bssid\":"BSSID_QSTR"}")); + fail_unless(PBUF_CONTAINS("Channel util : 10")); + fail_unless(PBUF_CONTAINS("AC-BE")); + fail_unless(PBUF_CONTAINS("-unicast bytes tx : 100")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DUMP_RADIO_METRICS # +########################################################################*/ +START_TEST(test_dump_radio_metrics) +{ + test_init(); + + g_radio->radio_metrics.transmit = 10; + g_radio->radio_metrics.receive_self = 15; + g_radio->radio_metrics.receive_other = 20; + + fail_unless(!map_cli_exec("dumpRadioMetrics", "{\"almac\":"AL_MAC_QSTR",\"radio_id\":"RADIO_ID_QSTR"}")); + fail_unless(PBUF_CONTAINS("Transmit: 3.92 %")); + fail_unless(PBUF_CONTAINS("Receive Self: 5.88 %")); + fail_unless(PBUF_CONTAINS("Receive Other: 7.84 %")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DUMP_STA_METRICS # +########################################################################*/ +START_TEST(test_dump_sta_metrics) +{ + map_sta_link_metrics_t *lm; + map_sta_ext_link_metrics_t *elm; + + test_init(); + + test_cmd_help("dumpStaMetrics"); + + /* Link metrics */ + lm = calloc(1, sizeof(*lm)); + lm->dl_mac_datarate = 1000; + lm->rssi = -50; + push_object(g_sta->metrics, lm); + + fail_unless(!map_cli_exec("dumpStaMetrics", "{\"stamac\":"STA_MAC_QSTR",\"type\":\"metrics\"}")); + fail_unless(PBUF_CONTAINS(BSSID_STR)); + fail_unless(PBUF_CONTAINS("dl_mac_datarate: 1000 Mbps")); + fail_unless(PBUF_CONTAINS("rssi: -50 dBm")); + + /* Extended link metrics */ + elm = &g_sta->last_sta_ext_metrics; + elm->no_of_bss_metrics = 1; + fail_unless(!!(elm->ext_bss_metrics_list = calloc(1, sizeof(*elm->ext_bss_metrics_list)))); + maccpy(elm->ext_bss_metrics_list[0].bssid, g_bssid); + elm->ext_bss_metrics_list[0].last_data_dl_rate = 6000; + elm->ext_bss_metrics_list[0].utilization_tx = 40; + + fail_unless(!map_cli_exec("dumpStaMetrics", "{\"stamac\":"STA_MAC_QSTR",\"type\":\"extended_metrics\"}")); + fail_unless(PBUF_CONTAINS(BSSID_STR)); + fail_unless(PBUF_CONTAINS("last_data_dl_rate: 6000 Kbps")); + fail_unless(PBUF_CONTAINS("utilization_tx: 40 ms")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_GET_CHANNEL_SCAN_RESULTS # +########################################################################*/ +START_TEST(test_get_channel_scan_results) +{ + map_scan_result_t *sr; + + test_init(); + + test_cmd_help("getChannelScanResults"); + + fail_unless(!!(sr = calloc(1, sizeof(*sr)))); + sr->opclass = 115; + sr->channel = 36; + strcpy((char*)sr->neighbor_info.ssid, "test_ssid"); + sr->neighbor_info.rcpi = 40; + sr->neighbor_info.bss_load_elem_present = 1; + sr->neighbor_info.stas_nr = 4; + push_object(g_radio->scanned_bssid_list, sr); + + fail_unless(!map_cli_exec("getChannelScanResults", "{\"almac\":"AL_MAC_QSTR",\"radio_id\":"RADIO_ID_QSTR",\"type\":\"lastRequest\"}")); + + fail_unless(PBUF_CONTAINS("Opclass: 115")); + fail_unless(PBUF_CONTAINS("Channel: 36")); + fail_unless(PBUF_CONTAINS("SSID: test_ssid")); + fail_unless(PBUF_CONTAINS("RSSI: -90 dBm")); + fail_unless(PBUF_CONTAINS("STA Count: 4")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SET_CHANNEL # +########################################################################*/ +START_TEST(test_set_channel) +{ + test_init(); + + test_cmd_help("setChannel"); + + fail_unless(!map_cli_exec("setChannel", "{\"almac\":"AL_MAC_QSTR",\"radio_id\":"RADIO_ID_QSTR",\"channel\":100}")); + fail_unless(PBUF_CONTAINS("OK")); + + g_cb_called = false; + fail_unless(!map_cli_exec("setChannel", "{\"almac\":"AL_MAC_QSTR",\"radio_id\":"RADIO_ID_QSTR",\"channel\":100, \"bandwidth\":20}")); + fail_unless(PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_TOPOLOGY_QUERY # +########################################################################*/ +static int send_topology_query_cb(void *args, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(args == g_ale); + + return 0; +} + +START_TEST(test_send_topology_query) +{ + test_init(); + + stub_cmdu_tx_register_send_topology_query_cb(send_topology_query_cb); + fail_unless(!map_cli_exec("sendTopologyQuery", "{\"almac\":"AL_MAC_QSTR"}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_LINK_METRIC_QUERY # +########################################################################*/ +static int send_link_metric_query_1_cb(map_ale_info_t *ale, i1905_link_metric_query_tlv_t *tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(tlv->destination == LINK_METRIC_QUERY_TLV_ALL_NEIGHBORS); + fail_unless(tlv->link_metrics_type == LINK_METRIC_QUERY_TLV_BOTH_TX_AND_RX_LINK_METRICS); + + return 0; +} + +static int send_link_metric_query_2_cb(map_ale_info_t *ale, i1905_link_metric_query_tlv_t *tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(tlv->destination == LINK_METRIC_QUERY_TLV_SPECIFIC_NEIGHBOR); + fail_unless(tlv->link_metrics_type == LINK_METRIC_QUERY_TLV_RX_LINK_METRICS_ONLY); + + return 0; +} + +START_TEST(test_send_link_metric_query) +{ + test_init(); + + test_cmd_help("sendLinkMetricQuery"); + + stub_cmdu_tx_register_send_link_metric_query_cb(send_link_metric_query_1_cb); + fail_unless(!map_cli_exec("sendLinkMetricQuery", "{\"almac\":"AL_MAC_QSTR"}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + g_cb_called = false; + stub_cmdu_tx_register_send_link_metric_query_cb(send_link_metric_query_2_cb); + fail_unless(!map_cli_exec("sendLinkMetricQuery", "{\"almac\":"AL_MAC_QSTR",\"neighbor\":\"00:01:02:03:04:05\",\"type\":\"rx\"}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_AUTOCONFIG_RENEW # +########################################################################*/ +static int send_autoconfig_renew_cb(uint8_t freq_band, uint16_t *mid, bool reset_onboarding) +{ + g_cb_called = true; + + return 0; +} + +static int send_autoconfig_renew_ucast_cb(map_ale_info_t *ale, uint8_t freq_band, uint16_t *mid, bool reset_onboarding) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + + return 0; +} + +START_TEST(test_send_autoconfig_renew) +{ + test_init(); + + test_cmd_help("sendAutoconfigRenew"); + + stub_cmdu_tx_register_send_autoconfig_renew_cb(send_autoconfig_renew_cb); + fail_unless(!map_cli_exec("sendAutoconfigRenew", "{}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + stub_cmdu_tx_register_send_autoconfig_renew_cb(NULL); + stub_cmdu_tx_register_send_autoconfig_renew_ucast_cb(send_autoconfig_renew_ucast_cb); + fail_unless(!map_cli_exec("sendAutoConfigRenew", "{\"almac\":"AL_MAC_QSTR"}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_AP_CAPABILITY_QUERY # +########################################################################*/ +static int send_ap_capability_query_cb(void *args, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(args == g_ale); + + return 0; +} + +START_TEST(test_send_ap_capability_query) +{ + test_init(); + + stub_cmdu_tx_register_send_ap_capability_query_cb(send_ap_capability_query_cb); + fail_unless(!map_cli_exec("sendAPCapabilityQuery", "{\"almac\":"AL_MAC_QSTR"}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_CHANNEL_PREFERENCE_QUERY # +########################################################################*/ +static int send_channel_preference_query_cb(void *args, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(args == g_ale); + + return 0; +} + +START_TEST(test_send_channel_preference_query) +{ + test_init(); + + stub_cmdu_tx_register_send_channel_preference_query_cb(send_channel_preference_query_cb); + fail_unless(!map_cli_exec("sendChannelPreferenceQuery", "{\"almac\":"AL_MAC_QSTR"}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_CLIENT_CAPABILITY_QUERY # +########################################################################*/ +static int send_client_capability_query_cb(void *args, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(args == g_sta); + + return 0; +} + +START_TEST(test_send_client_capability_query) +{ + test_init(); + + stub_cmdu_tx_register_send_client_capability_query_cb(send_client_capability_query_cb); + fail_unless(!map_cli_exec("sendClientCapabilityQuery", "{\"almac\":"AL_MAC_QSTR",\"stamac\":"STA_MAC_QSTR"}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_ASSOC_STA_LINK_METRICS_QUERY # +########################################################################*/ +static int send_assoc_sta_link_metrics_query_cb(void *args, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(args == g_sta); + + return 0; +} + +START_TEST(test_send_assoc_sta_link_metrics_query) +{ + test_init(); + + stub_cmdu_tx_register_send_assoc_sta_link_metrics_query_cb(send_assoc_sta_link_metrics_query_cb); + fail_unless(!map_cli_exec("sendAssocStaLinkMetricsQuery", "{\"almac\":"AL_MAC_QSTR",\"stamac\":"STA_MAC_QSTR"}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_UNASSOC_STA_LINK_METRICS_QUERY # +########################################################################*/ +static int send_unassoc_sta_link_metrics_query_cb(map_ale_info_t *ale, map_unassoc_sta_link_metrics_query_tlv_t *tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(tlv->op_class == 115); + fail_unless(tlv->channels_nr == 2); + fail_unless(tlv->channels[0].channel == 36); + fail_unless(tlv->channels[0].sta_macs_nr == 2); + fail_unless(!maccmp(tlv->channels[0].sta_macs[0], g_sta_mac)); + fail_unless(!maccmp(tlv->channels[0].sta_macs[1], g_sta2_mac)); + fail_unless(tlv->channels[1].channel == 40); + fail_unless(tlv->channels[1].sta_macs_nr == 1); + fail_unless(!maccmp(tlv->channels[1].sta_macs[0], g_sta3_mac)); + + return 0; +} + +START_TEST(test_send_unassoc_sta_link_metrics_query) +{ + test_init(); + + test_cmd_help("sendUnassocStaLinkMetricsQuery"); + + stub_cmdu_tx_register_send_unassoc_sta_link_metrics_query_cb(send_unassoc_sta_link_metrics_query_cb); + fail_unless(!map_cli_exec("sendUnassocStaLinkMetricsQuery", "{\"almac\":"AL_MAC_QSTR",\"opclass\":115,\"channels\":[{\"channel\":36,\"stamacs\":["STA_MAC_QSTR","STA2_MAC_QSTR"]},{\"channel\":40,\"stamacs\":["STA3_MAC_QSTR"]}]}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_BEACON_METRICS_QUERY # +########################################################################*/ +static int send_beacon_metrics_query_1_cb(map_ale_info_t *ale, map_beacon_metrics_query_tlv_t *tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(tlv->op_class == 115); + fail_unless(tlv->channel == 36); + fail_unless(!maccmp(tlv->bssid, g_bssid)); + fail_unless(tlv->reporting_detail == MAP_BEACON_REPORT_DETAIL_NONE); + fail_unless(tlv->ssid_len == strlen("test_ssid")); + fail_unless(!memcmp(tlv->ssid, (uint8_t*)"test_ssid", tlv->ssid_len)); + fail_unless(tlv->ap_channel_reports_nr == 0); + + return 0; +} + +static int send_beacon_metrics_query_2_cb(map_ale_info_t *ale, map_beacon_metrics_query_tlv_t *tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(tlv->op_class == 0); + fail_unless(tlv->channel == 255); + fail_unless(!maccmp(tlv->bssid, g_bssid)); + fail_unless(tlv->reporting_detail == MAP_BEACON_REPORT_DETAIL_ALL); + fail_unless(tlv->ssid_len == 0); + fail_unless(tlv->ap_channel_reports_nr == 2); + fail_unless(tlv->ap_channel_reports[0].op_class == 115); + fail_unless(map_cs_nr(&tlv->ap_channel_reports[0].channels) == 3); + fail_unless(map_cs_is_set(&tlv->ap_channel_reports[0].channels, 36)); + fail_unless(map_cs_is_set(&tlv->ap_channel_reports[0].channels, 40)); + fail_unless(map_cs_is_set(&tlv->ap_channel_reports[0].channels, 44)); + fail_unless(tlv->ap_channel_reports[1].op_class == 118); + fail_unless(map_cs_nr(&tlv->ap_channel_reports[1].channels) == 2); + fail_unless(map_cs_is_set(&tlv->ap_channel_reports[1].channels, 52)); + fail_unless(map_cs_is_set(&tlv->ap_channel_reports[1].channels, 64)); + + return 0; +} + +START_TEST(test_send_beacon_metrics_query) +{ + test_init(); + + test_cmd_help("sendBeaconMetricsQuery"); + + stub_cmdu_tx_register_send_beacon_metrics_query_cb(send_beacon_metrics_query_1_cb); + fail_unless(!map_cli_exec("sendBeaconMetricsQuery", "{\"almac\":"AL_MAC_QSTR",\"stamac\":"STA_MAC_QSTR",\"opclass\":115,\"channel\":36,\"bssid\":"BSSID_QSTR",\"reporting_detail\":\"none\",\"ssid\":\"test_ssid\",\"ap_channel_reports\":[]}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + g_cb_called = false; + stub_cmdu_tx_register_send_beacon_metrics_query_cb(send_beacon_metrics_query_2_cb); + fail_unless(!map_cli_exec("sendBeaconMetricsQuery", "{\"almac\":"AL_MAC_QSTR",\"stamac\":"STA_MAC_QSTR",\"opclass\":0,\"channel\":255,\"bssid\":"BSSID_QSTR",\"reporting_detail\":\"all\",\"ssid\":\"\",\"ap_channel_reports\":[{\"opclass\":115,\"channels\":[36,40,44]},{\"opclass\":118,\"channels\":[52,64]}]}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_COMBINED_INFRASTRUCTURE_METRICS # +########################################################################*/ +static int send_combined_infrastructure_metrics_cb(map_ale_info_t *ale, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + + return 0; +} + +START_TEST(test_send_combined_infrastructure_metrics) +{ + test_init(); + + stub_cmdu_tx_register_send_combined_infrastructure_metrics_cb(send_combined_infrastructure_metrics_cb); + fail_unless(!map_cli_exec("sendCombinedInfrastructureMetrics", "{\"almac\":"AL_MAC_QSTR"}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_CLIENT_STEERING_REQUEST # +########################################################################*/ +static int send_client_steering_request_cb(map_ale_info_t *ale, map_steer_t *steer, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(steer->flags == (MAP_STEERING_REQUEST_FLAG_MANDATE | MAP_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT | MAP_STEERING_REQUEST_FLAG_BTM_ABRIDGED)); + fail_unless(!maccmp(steer->bssid, g_bssid)); + fail_unless(steer->opportunity_wnd == 100); + fail_unless(steer->disassociation_timer == 6000); + fail_unless(steer->sta_bssid_nr == 1); + fail_unless(!maccmp(steer->sta_bssid[0].sta_mac, g_sta_mac)); + fail_unless(!maccmp(steer->sta_bssid[0].target_bssid, g_bssid2)); + fail_unless(steer->sta_bssid[0].op_class == 115); + fail_unless(steer->sta_bssid[0].channel == 36); + fail_unless(steer->sta_bssid[0].reason == 2); + + return 0; +} + +START_TEST(test_send_client_steering_request) +{ + test_init(); + + test_cmd_help("sendClientSteeringRequest"); + + stub_cmdu_tx_register_send_client_steering_request_cb(send_client_steering_request_cb); + fail_unless(!map_cli_exec("sendClientSteeringRequest", "{\"almac\":"AL_MAC_QSTR",\"bssid\":"BSSID_QSTR",\"mode\":\"mandate\",\"disassoc_imminent\":true, \"abridged\":true,\"opp_window\":100,\"disassoc_timer\":6000,\"targets\":[{\"stamac\":"STA_MAC_QSTR",\"bssid\":"BSSID2_QSTR",\"opclass\":115,\"channel\":36,\"reason\":2}]}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_CLIENT_ASSOC_CONTROL_REQUEST # +########################################################################*/ +static int send_client_acl_request_cb(map_ale_info_t *ale, map_client_assoc_control_request_tlv_t *tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(!maccmp(tlv->bssid, g_bssid)); + fail_unless(tlv->association_control == MAP_CLIENT_ASSOC_CONTROL_BLOCK); + fail_unless(tlv->validity_period == 30); + fail_unless(tlv->sta_macs_nr == 2); + fail_unless(!maccmp(tlv->sta_macs[0], g_sta_mac)); + fail_unless(!maccmp(tlv->sta_macs[1], g_sta2_mac)); + + return 0; +} + +START_TEST(test_send_client_assoc_control_request) +{ + test_init(); + + test_cmd_help("sendClientAssocControlRequest"); + + stub_cmdu_tx_register_send_client_acl_request_cb(send_client_acl_request_cb); + fail_unless(!map_cli_exec("sendClientAssocControlRequest", "{\"almac\":"AL_MAC_QSTR",\"bssid\":"BSSID_QSTR",\"block\":true,\"period\":30,\"stamacs\":["STA_MAC_QSTR","STA2_MAC_QSTR"]}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_BACKHAUL_STA_CAPABILITY_QUERY # +########################################################################*/ +static int send_backhaul_sta_capability_query_cb(map_ale_info_t *ale, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + + return 0; +} + +START_TEST(test_send_backhaul_sta_capability_query) +{ + test_init(); + + stub_cmdu_tx_register_send_backhaul_sta_capability_query_cb(send_backhaul_sta_capability_query_cb); + fail_unless(!map_cli_exec("sendBackhaulStaCapabilityQuery", "{\"almac\":"AL_MAC_QSTR"}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_BACKHAUL_STEERING_REQUEST # +########################################################################*/ +static int send_backhaul_steering_request_cb(map_ale_info_t *ale, map_backhaul_steering_request_tlv_t *tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(!maccmp(tlv->bsta_mac, g_sta_mac)); + fail_unless(!maccmp(tlv->target_bssid, g_bssid)); + fail_unless(tlv->target_op_class == 115); + fail_unless(tlv->target_channel == 36); + + return 0; +} + +START_TEST(test_send_backhaul_steering_request) +{ + test_init(); + + test_cmd_help("sendBackhaulSteeringRequest"); + + stub_cmdu_tx_register_send_backhaul_steering_request_cb(send_backhaul_steering_request_cb); + fail_unless(!map_cli_exec("sendBackhaulSteeringRequest", "{\"almac\":"AL_MAC_QSTR",\"stamac\":"STA_MAC_QSTR",\"bssid\":"BSSID_QSTR",\"opclass\":115,\"channel\":36}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_UNSUCCESS_ASSOC_POLICY_CONF # +########################################################################*/ +static int send_assoc_unsuccess_policy_config_request_cb(map_ale_info_t *ale, map_policy_config_tlvs_t *tlvs, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(!!tlvs->unsuccess_assoc_policy_tlv); + fail_unless(tlvs->unsuccess_assoc_policy_tlv->report_flag == MAP_UNSUCCESSFUL_ASSOC_REPORT); + fail_unless(tlvs->unsuccess_assoc_policy_tlv->max_reporting_rate == 10); + + return 0; +} + +START_TEST(test_send_unsuccess_assoc_policy_conf) +{ + test_init(); + + test_cmd_help("sendUnsuccessAssocPolicyConf"); + + stub_cmdu_tx_register_send_policy_config_request_cb(send_assoc_unsuccess_policy_config_request_cb); + fail_unless(!map_cli_exec("sendUnsuccessAssocPolicyConf", "{\"almac\":"AL_MAC_QSTR",\"report\":true,\"max_reporting_rate\":10}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_BH_BSS_POLICY_CONF # +########################################################################*/ +static int send_bh_bss_policy_config_request_cb(map_ale_info_t *ale, map_policy_config_tlvs_t *tlvs, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(tlvs->bh_bss_config_tlvs_nr == 2); + fail_unless(!maccmp(tlvs->bh_bss_config_tlvs[0].bssid, g_bssid)); + fail_unless(tlvs->bh_bss_config_tlvs[0].p1_bsta_disallowed == 1); + fail_unless(tlvs->bh_bss_config_tlvs[0].p2_bsta_disallowed == 0); + fail_unless(!maccmp(tlvs->bh_bss_config_tlvs[1].bssid, g_bssid2)); + fail_unless(tlvs->bh_bss_config_tlvs[1].p1_bsta_disallowed == 0); + fail_unless(tlvs->bh_bss_config_tlvs[1].p2_bsta_disallowed == 1); + + return 0; +} + +START_TEST(test_send_bh_bss_policy_conf) +{ + test_init(); + + test_cmd_help("sendBhBssPolicyConf"); + + stub_cmdu_tx_register_send_policy_config_request_cb(send_bh_bss_policy_config_request_cb); + fail_unless(!map_cli_exec("sendBhBssPolicyConf", "{\"no_of_bssid\":2,\"bssid_list\":[{\"bssid\":"BSSID_QSTR",\"p1_bsta_disallowed\":true,\"p2_bsta_disallowed\":false},{\"bssid\":"BSSID2_QSTR",\"p1_bsta_disallowed\":false,\"p2_bsta_disallowed\":true}]}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_CHANNEL_SCAN_REQUEST # +########################################################################*/ +static int send_channel_scan_request_1_cb(map_ale_info_t *ale, map_channel_scan_request_tlv_t *tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(tlv->fresh_scan_performed == 0); + fail_unless(tlv->radios_nr == 1); + fail_unless(!maccmp(tlv->radios[0].radio_id, g_radio_id)); + fail_unless(tlv->radios[0].op_classes_nr == 0); + + return 0; +} + +static int send_channel_scan_request_2_cb(map_ale_info_t *ale, map_channel_scan_request_tlv_t *tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(tlv->fresh_scan_performed == 1); + fail_unless(tlv->radios_nr == 2); + fail_unless(!maccmp(tlv->radios[0].radio_id, g_radio_id)); + fail_unless(tlv->radios[0].op_classes_nr == 2); + fail_unless(tlv->radios[0].op_classes[0].op_class == 115); + fail_unless(map_cs_nr(&tlv->radios[0].op_classes[0].channels) == 2); + fail_unless(map_cs_is_set(&tlv->radios[0].op_classes[0].channels, 36)); + fail_unless(map_cs_is_set(&tlv->radios[0].op_classes[0].channels, 40)); + fail_unless(tlv->radios[0].op_classes[1].op_class == 118); + fail_unless(map_cs_nr(&tlv->radios[0].op_classes[1].channels) == 0); + fail_unless(!maccmp(tlv->radios[1].radio_id, g_radio2_id)); + fail_unless(tlv->radios[1].op_classes_nr == 1); + fail_unless(tlv->radios[1].op_classes[0].op_class == 81); + fail_unless(map_cs_nr(&tlv->radios[1].op_classes[0].channels) == 2); + fail_unless(map_cs_is_set(&tlv->radios[1].op_classes[0].channels, 1)); + fail_unless(map_cs_is_set(&tlv->radios[1].op_classes[0].channels, 11)); + + return 0; +} + +START_TEST(test_send_channel_scan_request) +{ + test_init(); + + test_cmd_help("sendChannelScanRequest"); + + stub_cmdu_tx_register_send_channel_scan_request_cb(send_channel_scan_request_1_cb); + fail_unless(!map_cli_exec("sendChannelScanRequest", "{\"almac\":"AL_MAC_QSTR",\"fresh_scan\":false,\"no_of_radios\":1,\"radio_list\":[{\"radio_id\":"RADIO_ID_QSTR",\"no_of_opclass\":0,\"opclass_list\":[]}]}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + g_cb_called = false; + stub_cmdu_tx_register_send_channel_scan_request_cb(send_channel_scan_request_2_cb); + fail_unless(!map_cli_exec("sendChannelScanRequest", "{\"almac\":"AL_MAC_QSTR",\"fresh_scan\":true,\"no_of_radios\":2,\"radio_list\":[{\"radio_id\":"RADIO_ID_QSTR",\"no_of_opclass\":2,\"opclass_list\":[{\"opclass\":115,\"no_of_channels\":2,\"channel_list\":[36,40]},{\"opclass\":118,\"no_of_channels\":0,\"channel_list\":[]}]},{\"radio_id\":"RADIO2_ID_QSTR",\"no_of_opclass\":1,\"opclass_list\":[{\"opclass\":81,\"no_of_channels\":2,\"channel_list\":[1,11]}]}]}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_CAC_REQUEST # +########################################################################*/ +static int send_cac_request_cb(map_ale_info_t *ale, map_cac_request_tlv_t *tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(tlv->radios_nr == 2); + fail_unless(!maccmp(tlv->radios[0].radio_id, g_radio_id)); + fail_unless(tlv->radios[0].op_class == 122); + fail_unless(tlv->radios[0].channel == 132); + fail_unless(tlv->radios[0].cac_method == MAP_CAC_METHOD_TIME_SLICED); + fail_unless(tlv->radios[0].cac_completion_action == MAP_CAC_ACTION_RETURN_PREV_OP_CONF); + fail_unless(!maccmp(tlv->radios[1].radio_id, g_radio2_id)); + fail_unless(tlv->radios[1].op_class == 115); + fail_unless(tlv->radios[1].channel == 100); + fail_unless(tlv->radios[1].cac_method == MAP_CAC_METHOD_MIMO_DIM_REDUCED); + fail_unless(tlv->radios[1].cac_completion_action == MAP_CAC_ACTION_REMAIN_AND_CONT_TO_MON); + + return 0; +} + +START_TEST(test_send_cac_request) +{ + test_init(); + + test_cmd_help("sendCACRequest"); + + stub_cmdu_tx_register_send_cac_request_cb(send_cac_request_cb); + fail_unless(!map_cli_exec("sendCACRequest", "{\"almac\":"AL_MAC_QSTR",\"no_of_reqs\":2,\"cac_req_list\":[{\"radio_id\":"RADIO_ID_QSTR",\"opclass\":122,\"channel\":132,\"cac_method\":\"time_sliced\",\"cac_completion_act\":\"return_prev_conf\"},{\"radio_id\":"RADIO2_ID_QSTR",\"opclass\":115,\"channel\":100,\"cac_method\":\"mimo_dim_reduced\",\"cac_completion_act\":\"remain_continue_mon\"}]}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_CAC_TERMINATION # +########################################################################*/ +static int send_cac_termination_cb(map_ale_info_t *ale, map_cac_termination_tlv_t *tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(tlv->radios_nr == 1); + fail_unless(!maccmp(tlv->radios[0].radio_id, g_radio_id)); + fail_unless(tlv->radios[0].op_class == 122); + fail_unless(tlv->radios[0].channel == 132); + + return 0; +} + +START_TEST(test_send_cac_termination) +{ + test_init(); + + test_cmd_help("sendCACTermination"); + + stub_cmdu_tx_register_send_cac_termination_cb(send_cac_termination_cb); + fail_unless(!map_cli_exec("sendCACTermination", "{\"almac\":"AL_MAC_QSTR",\"no_of_radios\":1,\"cac_radio_list\":[{\"radio_id\":"RADIO_ID_QSTR",\"opclass\":122,\"channel\":132}]}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_CH_SCAN_REPORT_POLICY_CONF # +########################################################################*/ +static int send_ch_scan_report_policy_config_request_cb(map_ale_info_t *ale, map_policy_config_tlvs_t *tlvs, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(!!tlvs->channel_scan_report_policy_tlv); + fail_unless(tlvs->channel_scan_report_policy_tlv->report_independent_ch_scans == 1); + + return 0; +} + +START_TEST(test_send_ch_scan_report_policy_conf) +{ + test_init(); + + test_cmd_help("sendChScanReportPolicyConf"); + + stub_cmdu_tx_register_send_policy_config_request_cb(send_ch_scan_report_policy_config_request_cb); + fail_unless(!map_cli_exec("sendChScanReportPolicyConf", "{\"almac\":"AL_MAC_QSTR",\"report_indep_scans\":true}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_DPP_CCE_INDICATION # +########################################################################*/ +static int send_dpp_cce_indication_cb(map_ale_info_t *ale, uint8_t advertise, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(advertise); + + return 0; +} + +START_TEST(test_send_dpp_cce_indication) +{ + test_init(); + + test_cmd_help("sendDPPCCEIndication"); + + stub_cmdu_tx_register_send_dpp_cce_indication_cb(send_dpp_cce_indication_cb); + fail_unless(!map_cli_exec("sendDPPCCEIndication", "{\"almac\":"AL_MAC_QSTR",\"advertise\":true}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_PROXIED_ENCAP_DPP # +########################################################################*/ +static int send_proxied_encap_dpp_1_cb(map_ale_info_t *ale, map_1905_encap_dpp_tlv_t *encap_tlv, map_dpp_chirp_value_tlv_t *chirp_tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(!!encap_tlv); + fail_unless(!chirp_tlv); + fail_unless(encap_tlv->enrollee_mac_present == 1); + fail_unless(!maccmp(encap_tlv->sta_mac, g_sta_mac)); + fail_unless(encap_tlv->dpp_frame_indicator == 1); + fail_unless(encap_tlv->frame_type == 10); + fail_unless(encap_tlv->frame_len == 4); + fail_unless(!memcmp(encap_tlv->frame, (uint8_t[]){0xAA, 0xBB, 0xCC, 0xDD}, 4)); + + return 0; +} + +static int send_proxied_encap_dpp_2_cb(map_ale_info_t *ale, map_1905_encap_dpp_tlv_t *encap_tlv, map_dpp_chirp_value_tlv_t *chirp_tlv, uint16_t *mid) +{ + g_cb_called = true; + fail_unless(ale == g_ale); + fail_unless(!!encap_tlv); + fail_unless(!!chirp_tlv); + fail_unless(encap_tlv->enrollee_mac_present == 0); + fail_unless(encap_tlv->dpp_frame_indicator == 1); + fail_unless(encap_tlv->frame_type == 10); + fail_unless(encap_tlv->frame_len == 4); + fail_unless(!memcmp(encap_tlv->frame, (uint8_t[]){0xAA, 0xBB, 0xCC, 0xDD}, 4)); + fail_unless(!maccmp(chirp_tlv->sta_mac, g_sta_mac)); + fail_unless(chirp_tlv->hash_validity == 1); + fail_unless(chirp_tlv->hash_len == 4); + fail_unless(!memcmp(chirp_tlv->hash, (uint8_t[]){0x01, 0x02, 0x03, 0x04}, 4)); + + return 0; +} + +START_TEST(test_send_proxied_encap_dpp) +{ + test_init(); + + test_cmd_help("sendProxiedEncapDPP"); + + stub_cmdu_tx_register_send_proxied_encap_dpp_cb(send_proxied_encap_dpp_1_cb); + fail_unless(!map_cli_exec("sendProxiedEncapDPP", "{\"almac\":"AL_MAC_QSTR",\"encap\":{\"stamac\":"STA_MAC_QSTR",\"frame_indicator\":1,\"frame_type\":10,\"frame\":\"AABBCCDD\"}}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + g_cb_called = false; + stub_cmdu_tx_register_send_proxied_encap_dpp_cb(send_proxied_encap_dpp_2_cb); + fail_unless(!map_cli_exec("sendProxiedEncapDPP", "{\"almac\":"AL_MAC_QSTR",\"encap\":{\"frame_indicator\":1,\"frame_type\":10,\"frame\":\"AABBCCDD\"},\"chirp\":{\"stamac\":"STA_MAC_QSTR",\"hash_validity\":1,\"hash\":\"01020304\"}}")); + fail_unless(g_cb_called && PBUF_CONTAINS("OK")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_RAW_MESSAGE # +########################################################################*/ +static int send_raw_1_cb(char *ifname, mac_addr dmac, mac_addr smac, uint16_t eth_type, uint8_t *data, uint16_t data_len) +{ + int i; + + g_cb_called = true; + fail_unless(!memcmp(dmac, (mac_addr){0xA1,0xA2,0xA3,0xA4,0xA5,0xA6}, sizeof(mac_addr))); + fail_unless(!memcmp(smac, (mac_addr){0xB1,0xB2,0xB3,0xB4,0xB5,0xB6}, sizeof(mac_addr))); + fail_unless(eth_type == 0x893A); + fail_unless(data_len == 46); /* Padding: 64 - 14(HDR) - 4(CRC) */ + fail_unless(!memcmp(data, (uint8_t[]){0x00,0x01,0x02,0x03}, 4)); + + for (i = 4; i < data_len; i++) { + fail_unless(data[i] == 0); + } + + return 0; +} + +static int send_raw_2_cb(char *ifname, mac_addr dmac, mac_addr smac, uint16_t eth_type, uint8_t *data, uint16_t data_len) +{ + int i; + + g_cb_called = true; + fail_unless(!memcmp(dmac, (mac_addr){0xA1,0xA2,0xA3,0xA4,0xA5,0xA6}, sizeof(mac_addr))); + fail_unless(!memcmp(smac, (mac_addr){0xB1,0xB2,0xB3,0xB4,0xB5,0xB6}, sizeof(mac_addr))); + fail_unless(eth_type == 0x893A); + fail_unless(data_len == 256); + + for (i = 0; i < data_len; i++) { + fail_unless(data[i] == (i % 256)); + } + + return 0; +} + + +static void fill_raw_buf(char *buf, int count) +{ + int i, pos = sprintf(buf, "eth0|A1 A2 A3 A4 A5 A6 B1 B2 B3 B4 B5 B6 89 3A "); + + for (i = 0; i < count; i++) { + pos += sprintf(&buf[pos], "%02X ", (i % 256)); + } +} + +START_TEST(test_send_raw_message) +{ + char *buf; + + test_init(); + + fail_unless(!!(buf=malloc(8192))); + + /* Shorter than 64 bytes */ + stub_cmdu_tx_register_send_raw_cb(send_raw_1_cb); + fill_raw_buf(buf, 4); + fail_unless(!map_cli_exec("sendRawMessage", buf)); + fail_unless(g_cb_called); + + /* Longer than 64 bytes */ + g_cb_called = false; + stub_cmdu_tx_register_send_raw_cb(send_raw_2_cb); + fill_raw_buf(buf, 256); + fail_unless(!map_cli_exec("sendRawMessage", buf)); + fail_unless(g_cb_called); + + /* Too long */ + g_cb_called = false; + fill_raw_buf(buf, 1515); + fail_unless(!map_cli_exec("sendRawMessage", buf)); + fail_unless(!g_cb_called); + + free(buf); + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_WFA_CAPI # +########################################################################*/ +START_TEST(test_send_wfa_capi) +{ + test_init(); + + test_cmd_help("sendWFACAPI"); + + fail_unless(!map_cli_exec("sendWFACAPI", "{\"args\":\"dev_get_parameter,program,map,parameter,ALid\"}")); + + test_fini(); +} +END_TEST + + +const char *test_suite_name = "cli"; +test_case_t test_cases[] = { + TEST("help", test_help ), + TEST("invalid", test_invalid ), + TEST("version", test_version ), + TEST("case_insensitive", test_case_insensitive ), + TEST("dump_ctrl_info", test_dump_ctrl_info ), + TEST("dump_interfaces", test_dump_interfaces ), + TEST("dump_blocklist", test_dump_blocklist ), + TEST("dump_op_classes", test_dump_op_classes ), + TEST("dump_chan_sel", test_dump_chan_sel ), + TEST("dump_tunneled_message", test_dump_tunneled_message ), + TEST("dump_ap_metrics", test_dump_ap_metrics ), + TEST("dump_radio_metrics", test_dump_radio_metrics ), + TEST("dump_sta_metrics", test_dump_sta_metrics ), + TEST("get_channel_scan_results", test_get_channel_scan_results ), + TEST("set_channel", test_set_channel ), + TEST("send_topology_query", test_send_topology_query ), + TEST("send_link_metric_query", test_send_link_metric_query ), + TEST("send_autoconfig_renew", test_send_autoconfig_renew ), + TEST("send_ap_capability_query", test_send_ap_capability_query ), + TEST("send_channel_preference_query", test_send_channel_preference_query ), + TEST("send_client_capability_query", test_send_client_capability_query ), + TEST("send_assoc_sta_link_metrics_query", test_send_assoc_sta_link_metrics_query ), + TEST("send_unassoc_sta_link_metrics_query", test_send_unassoc_sta_link_metrics_query ), + TEST("send_beacon_metrics_query", test_send_beacon_metrics_query ), + TEST("send_combined_infrastructure_metrics", test_send_combined_infrastructure_metrics ), + TEST("send_client_steering_request", test_send_client_steering_request ), + TEST("send_client_assoc_control_request", test_send_client_assoc_control_request ), + TEST("send_backhaul_sta_capability_query", test_send_backhaul_sta_capability_query ), + TEST("send_backhaul_steering_request", test_send_backhaul_steering_request ), + TEST("send_unsuccess_assoc_policy_conf", test_send_unsuccess_assoc_policy_conf ), + TEST("send_bh_bss_policy_conf", test_send_bh_bss_policy_conf ), + TEST("send_channel_scan_request", test_send_channel_scan_request ), + TEST("send_cac_request", test_send_cac_request ), + TEST("send_cac_termination", test_send_cac_termination ), + TEST("send_ch_scan_report_policy_conf", test_send_ch_scan_report_policy_conf ), + TEST("send_dpp_cce_indication", test_send_dpp_cce_indication ), + TEST("send_proxied_encap_dpp", test_send_proxied_encap_dpp ), + TEST("send_raw_message", test_send_raw_message ), + TEST("send_wfa_capi", test_send_wfa_capi ), + TEST_CASES_END +}; diff --git a/source/test/controller/test_cmdu_rx.c b/source/test/controller/test_cmdu_rx.c new file mode 100644 index 0000000..7544c3c --- /dev/null +++ b/source/test/controller/test_cmdu_rx.c @@ -0,0 +1,2160 @@ +/* + * Copyright (c) 2020-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include +#include +#include + + +#include "test.h" + +#include "map_ctrl_cmdu_rx.h" + +#include "1905_cmdus.h" +#include "1905_tlvs.h" +#include "map_tlvs.h" +#include "map_data_model.h" +#include "map_data_model_dumper.h" +#include "map_topology_tree.h" +#include "map_staging_list.h" +#include "map_blocklist.h" +#include "map_ctrl_onboarding_handler.h" +#include "map_ctrl_cmdu_handler.h" +#include "map_ctrl_utils.h" +#include "map_ctrl_sta.h" +#include "map_ctrl_emex_tlv_handler.h" + +/*####################################################################### +# DEFINES # +########################################################################*/ +#define MAX_FRAGMENTS 26 + +/*####################################################################### +# GLOBALS # +########################################################################*/ +static mac_addr g_ctrl_al_mac = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + +/* All frames are captured from the same device + * Frames that captured later are manipulated to have same mac address as much as possible +*/ +static char *g_src_if_name = "eth0"; +static mac_addr g_al_mac = {0xF6, 0x17, 0xB8, 0x86, 0x57, 0x68}; +static mac_addr g_src_mac = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x68}; +static mac_addr g_eth_mac = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x68}; + +static mac_addr g_radio_id_2G = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}; +static mac_addr g_radio_id_5G = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}; +static mac_addr g_radio_id_6G = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6C}; + +static mac_addr g_bssid_2G_fh = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}; +static mac_addr g_bssid_5G_fh = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}; +static mac_addr g_bssid_5G_bh = {0x6A, 0x17, 0xB8, 0x86, 0x57, 0x6B}; +static mac_addr g_bssid_6G_fh = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6C}; +static mac_addr g_bssid_6G_bh = {0x6A, 0x17, 0xB8, 0x86, 0x57, 0x6D}; + +static mac_addr g_slave_al_mac = {0xCE, 0xAA, 0xCC, 0xAA, 0x00, 0x00}; +//static mac_addr g_slave_src_mac = {0xCE, 0xAA, 0xCC, 0xAA, 0x00, 0x00}; +static mac_addr g_slave_eth_mac = {0xCC, 0xAA, 0xCC, 0xAA, 0x00, 0x00}; + +static mac_addr g_slave_radio_id_2G = {0xCC, 0xAA, 0xCC, 0xAA, 0x00, 0x03}; +static mac_addr g_slave_radio_id_5G = {0xCC, 0xAA, 0xCC, 0xAA, 0x00, 0x04}; +static mac_addr g_slave_radio_id_6G = {0x16, 0xAA, 0xCC, 0xAA, 0x00, 0x05}; + +static mac_addr g_slave_bssid_2G_fh = {0xCC, 0xAA, 0xCC, 0xAA, 0x00, 0x03}; +static mac_addr g_slave_bssid_5G_fh = {0x82, 0xAA, 0xCC, 0xAA, 0x00, 0x05}; +static mac_addr g_slave_bssid_5G_bh = {0x82, 0xAA, 0xCC, 0xAA, 0x00, 0x06}; +static mac_addr g_slave_bssid_6G_fh = {0x16, 0xAA, 0xCC, 0xAA, 0x00, 0x06}; +static mac_addr g_slave_bssid_6G_bh = {0x16, 0xAA, 0xCC, 0xAA, 0x00, 0x07}; + +static mac_addr g_5g_bh_sta_mac = {0xCC, 0xAA, 0xCC, 0xAA, 0x00, 0x04}; +static mac_addr g_6g_bh_sta_mac = {0x16, 0xAA, 0xCC, 0xAA, 0x00, 0x05}; + +static mac_addr g_bh_sta_mac = {0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}; +static mac_addr g_S10_mac = {0xA8, 0xDB, 0x03, 0x05, 0x92, 0x1C}; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static void dummy_printf(const char *fmt, ...) +{ +} + + +static void test_init(void) +{ + map_cfg_get()->is_master = true; + + fail_unless(!map_info_init()); + fail_unless(!map_dm_init()); + fail_unless(init_topology_tree(g_ctrl_al_mac)); + fail_unless(!map_stglist_init()); + fail_unless(!map_blocklist_init()); + fail_unless(!map_emex_init()); + fail_unless(!map_ctrl_sta_init()); +} + +static void test_fini(void) +{ + /* For code coverage... */ + map_dm_dump_agent_info_tree(dummy_printf); + + map_ctrl_sta_fini(); + map_emex_fini(); + map_blocklist_fini(); + map_stglist_fini(); + map_dm_fini(); + map_info_fini(); +} + +static i1905_cmdu_t *parse_cmdu(packet_t **p, size_t packets_nr) +{ + uint8_t *streams[MAX_FRAGMENTS + 1]; + uint16_t lengths[MAX_FRAGMENTS + 1]; + uint16_t length; + i1905_cmdu_t *c; + size_t i; + + for (i = 0; i <= packets_nr ; i++) { + streams[i] = NULL; + lengths[i] = 0; + } + + fail_unless(packets_nr < MAX_FRAGMENTS); + + for (i = 0; i < packets_nr; i++) { + length = p[i]->len - sizeof(eth_hdr_t); /* Remove eth header */ + streams[i] = (uint8_t *)malloc((sizeof(uint8_t) * length)); + memcpy(streams[i], p[i]->data + sizeof(eth_hdr_t), length); + lengths[i] = length; + } + + c = parse_1905_CMDU_from_packets(streams, lengths); + + for (i = 0; i < packets_nr; i++) { + free(streams[i]); + } + + return c; +} + +/* TODO: + - pcap with more than one CMDU +*/ +static void read_parse(const char *file, mac_addr src_mac) +{ + packet_t **packets; + size_t packets_nr; + i1905_cmdu_t *cmdu; + char file_path[PATH_MAX]; + + snprintf(file_path, sizeof(file_path), DATA_DIR"/%s", file); + + fail_unless(!!(packets = pcap_read_all_packets(file_path, &packets_nr))); + fail_unless(!!(cmdu = parse_cmdu(packets, packets_nr))); + + strcpy(cmdu->interface_name, g_src_if_name); + maccpy(cmdu->cmdu_stream.src_mac_addr, src_mac); + + fail_unless(!!map_cmdu_rx_cb(cmdu)); + + /* Test has stubbed i1905_cmdu_cleanup */ + free_1905_CMDU_structure(cmdu); + free_packets(packets, packets_nr); +} +/*####################################################################### +# TEST_TOPOLOGY_DISCOVERY # +########################################################################*/ +START_TEST(test_topology_discovery) +{ + map_1905_dev_info_t *dev; + map_ale_info_t *ale; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + + fail_unless(!!(dev = map_stglist_get_1905_dev(g_al_mac))); + fail_unless(!!(dev = map_stglist_get_1905_dev_from_src_mac(g_al_mac))); + fail_unless(!!(ale = map_handle_new_agent_onboarding(g_al_mac, g_src_if_name, true))); + fail_unless(!map_handle_topology_discovery_ale(ale, g_src_if_name, g_al_mac, map_stglist_get_1905_dev_mac_tlv_mac(dev))); + fail_unless(!map_stglist_remove_1905_dev(dev)); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!maccmp(ale->src_mac, g_al_mac)); + fail_unless(!maccmp(ale->upstream_local_iface_mac, g_src_mac)); + fail_unless(!strcmp(ale->iface_name, g_src_if_name)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_TOPOLOGY_QUERY # +########################################################################*/ +START_TEST(test_topology_query) +{ + test_init(); + + read_parse("cmdu_topology_query.pcap", g_al_mac); + + /* Cannot validate anything yet */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_TOPOLOGY_RESPONSE # +########################################################################*/ +START_TEST(test_topology_response) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!maccmp(ale->src_mac, g_al_mac)); + fail_unless(!maccmp(ale->upstream_local_iface_mac, g_src_mac)); + fail_unless(!strcmp(ale->iface_name, g_src_if_name)); + fail_unless(ale->easymesh == true); + fail_unless(ale->map_profile == MAP_PROFILE_2); + + fail_unless(ale->radios_nr == 2); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->bsss_nr == 1); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_bssid_2G_fh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->bsss_nr == 2); + + fail_unless((bss = map_dm_get_bss(radio, g_bssid_5G_fh)) && bss->radio == radio); + fail_unless((sta = map_dm_get_sta(bss, g_S10_mac)) && sta->bss == bss); + + fail_unless((bss = map_dm_get_bss(radio, g_bssid_5G_bh)) && bss->radio == radio); + fail_unless((sta = map_dm_get_sta(bss, g_bh_sta_mac)) && sta->bss == bss); + + /* Local interface and non 1905 neighbors */ + fail_unless(ale->local_iface_count == 4); + fail_unless(!maccmp(ale->local_iface_list[0].mac_address, g_bssid_5G_fh)); + fail_unless(ale->local_iface_list[0].media_type == INTERFACE_TYPE_IEEE_802_11AC_5_GHZ); + fail_unless(!maccmp(ale->local_iface_list[3].mac_address, g_eth_mac)); + fail_unless(ale->local_iface_list[3].media_type == INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET); + + fail_unless(ale->non_1905_neighbor_count == 2); + fail_unless(!maccmp(ale->non_1905_neighbor_list[0].local_iface_mac, g_bssid_5G_fh)); + fail_unless(ale->non_1905_neighbor_list[0].media_type == INTERFACE_TYPE_IEEE_802_11AC_5_GHZ); + fail_unless(ale->non_1905_neighbor_list[0].macs_nr == 2); + fail_unless(!memcmp(ale->non_1905_neighbor_list[0].macs[0], (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + fail_unless(!maccmp(ale->non_1905_neighbor_list[0].macs[1], g_S10_mac)); + + fail_unless(!maccmp(ale->non_1905_neighbor_list[1].local_iface_mac, g_eth_mac)); + fail_unless(ale->non_1905_neighbor_list[1].media_type == INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET); + fail_unless(ale->non_1905_neighbor_list[1].macs_nr == 2); + /* List is sorted */ + fail_unless(!memcmp(ale->non_1905_neighbor_list[1].macs[0], (mac_addr){0xC8, 0xF7, 0x50, 0x4C, 0x29, 0x95}, sizeof(mac_addr))); + fail_unless(!memcmp(ale->non_1905_neighbor_list[1].macs[1], (mac_addr){0xD0, 0x67, 0xE5, 0x30, 0xFE, 0xCA}, sizeof(mac_addr))); + + /* Channel learning from "device information tlv" requires band to be known + -> read ap cap report and again topology response + */ + read_parse("cmdu_ap_capability_report.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->current_op_class == 81); + fail_unless(radio->current_op_channel == 6); + fail_unless(radio->current_bw == 20); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->bsss_nr == 2); + fail_unless(radio->current_op_class == 128); + fail_unless(radio->current_op_channel == 157); + fail_unless(radio->current_bw == 80); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_LEGACY_TOPOLOGY_RESPONSE # +########################################################################*/ +START_TEST(test_legacy_topology_response) +{ + map_ale_info_t *ale; + + test_init(); + + read_parse("cmdu_legacy_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_legacy_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!maccmp(ale->src_mac, g_al_mac)); + fail_unless(!maccmp(ale->upstream_local_iface_mac, g_src_mac)); + fail_unless(!strcmp(ale->iface_name, g_src_if_name)); + fail_unless(ale->easymesh == false); + + fail_unless(ale->radios_nr == 0); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_TRIBAND_TOPOLOGY_RESPONSE # +########################################################################*/ +START_TEST(test_triband_topology_response) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_triband_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!maccmp(ale->src_mac, g_al_mac)); + fail_unless(!maccmp(ale->upstream_local_iface_mac, g_src_mac)); + fail_unless(!strcmp(ale->iface_name, g_src_if_name)); + fail_unless(ale->easymesh == true); + fail_unless(ale->map_profile == MAP_PROFILE_2); + + fail_unless(ale->radios_nr == 3); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->bsss_nr == 1); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_bssid_2G_fh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->bsss_nr == 1); + + fail_unless((bss = map_dm_get_bss(radio, g_bssid_5G_fh)) && bss->radio == radio); + fail_unless((sta = map_dm_get_sta(bss, g_S10_mac)) && sta->bss == bss); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + fail_unless(radio->bsss_nr == 2); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_bssid_6G_fh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_bssid_6G_bh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + /* Local interface and non 1905 neighbors */ + fail_unless(ale->local_iface_count == 5); + fail_unless(!maccmp(ale->local_iface_list[0].mac_address, g_bssid_2G_fh)); + fail_unless(ale->local_iface_list[0].media_type == INTERFACE_TYPE_IEEE_802_11AX); + fail_unless(!maccmp(ale->local_iface_list[1].mac_address, g_eth_mac)); + fail_unless(ale->local_iface_list[1].media_type == INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET); + fail_unless(!maccmp(ale->local_iface_list[3].mac_address, g_bssid_6G_fh)); + fail_unless(ale->local_iface_list[3].media_type == INTERFACE_TYPE_IEEE_802_11AX); + + fail_unless(ale->non_1905_neighbor_count == 2); + + fail_unless(!maccmp(ale->non_1905_neighbor_list[0].local_iface_mac, g_eth_mac)); + fail_unless(ale->non_1905_neighbor_list[0].media_type == INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET); + fail_unless(ale->non_1905_neighbor_list[0].macs_nr == 1); + fail_unless(!memcmp(ale->non_1905_neighbor_list[0].macs[0], (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + + fail_unless(!maccmp(ale->non_1905_neighbor_list[1].local_iface_mac, g_bssid_5G_fh)); + fail_unless(ale->non_1905_neighbor_list[1].media_type == INTERFACE_TYPE_IEEE_802_11AX); + fail_unless(ale->non_1905_neighbor_list[1].macs_nr == 1); + fail_unless(!maccmp(ale->non_1905_neighbor_list[1].macs[0], g_S10_mac)); + + /* Channel learning from "device information tlv" requires band to be known + -> read ap cap report and again topology response + */ + read_parse("cmdu_triband_ap_capability_report.pcap", g_al_mac); + read_parse("cmdu_triband_topology_response.pcap", g_al_mac); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->current_op_class == 81); + fail_unless(radio->current_op_channel == 12); + fail_unless(radio->current_bw == 20); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->current_op_class == 129); + fail_unless(radio->current_op_channel == 100); + fail_unless(radio->current_bw == 160); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + fail_unless(radio->current_op_class == 134); + fail_unless(radio->current_op_channel == 165); + fail_unless(radio->current_bw == 160); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_MULTIPLE_BACKHAUL_TOPOLOGY_RESPONSE # +########################################################################*/ +START_TEST(test_multiple_backhaul_topology_response) +{ + map_ale_info_t *ale; + map_ale_info_t *slave_ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_multiple_backhaul_topology_response_master.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!maccmp(ale->src_mac, g_al_mac)); + fail_unless(!maccmp(ale->upstream_local_iface_mac, g_src_mac)); + + fail_unless(ale->radios_nr == 3); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->bsss_nr == 1); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_bssid_2G_fh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->bsss_nr == 2); + + fail_unless((bss = map_dm_get_bss(radio, g_bssid_5G_fh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + fail_unless((bss = map_dm_get_bss(radio, g_bssid_5G_bh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + fail_unless(radio->bsss_nr == 2); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_bssid_6G_fh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_bssid_6G_bh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + fail_unless((sta = map_dm_get_sta(bss, g_6g_bh_sta_mac)) && sta->bss == bss); + + /* Local interfaces */ + fail_unless(ale->local_iface_count == 6); + + fail_unless(ale->local_iface_list[1].media_type == INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET); + fail_unless(!maccmp(ale->local_iface_list[3].mac_address, g_bssid_6G_bh)); + fail_unless(ale->local_iface_list[3].media_type == INTERFACE_TYPE_IEEE_802_11AX); + fail_unless(ale->local_iface_list[3].ieee80211_role == IEEE80211_SPECIFIC_INFO_ROLE_AP); + + fail_unless(!maccmp(ale->local_iface_list[5].mac_address, g_bssid_5G_bh)); + fail_unless(ale->local_iface_list[5].media_type == INTERFACE_TYPE_IEEE_802_11AX); + fail_unless(ale->local_iface_list[5].ieee80211_role == IEEE80211_SPECIFIC_INFO_ROLE_AP); + + /* Onboard slave node */ + read_parse("cmdu_topology_discovery_slave.pcap", g_slave_al_mac); + read_parse("cmdu_multiple_backhaul_topology_response_slave.pcap", g_slave_al_mac); + + fail_unless(!!(slave_ale = map_dm_get_ale(g_slave_al_mac))); + fail_unless(!maccmp(slave_ale->src_mac, g_slave_al_mac)); + fail_unless(!maccmp(slave_ale->upstream_local_iface_mac, g_6g_bh_sta_mac)); + + fail_unless(slave_ale->radios_nr == 3); + + fail_unless(!!(radio = map_dm_get_radio(slave_ale, g_slave_radio_id_2G))); + fail_unless(radio->bsss_nr == 1); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_slave_bssid_2G_fh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + fail_unless(!!(radio = map_dm_get_radio(slave_ale, g_slave_radio_id_5G))); + fail_unless(radio->bsss_nr == 2); + + fail_unless((bss = map_dm_get_bss(radio, g_slave_bssid_5G_fh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + fail_unless((bss = map_dm_get_bss(radio, g_slave_bssid_5G_bh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + fail_unless(!!(radio = map_dm_get_radio(slave_ale, g_slave_radio_id_6G))); + fail_unless(radio->bsss_nr == 2); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_slave_bssid_6G_fh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_slave_bssid_6G_bh)) && bss->radio == radio); + fail_unless(bss->radio == radio); + + /* Local interfaces */ + fail_unless(slave_ale->local_iface_count == 8); + fail_unless(!maccmp(slave_ale->local_iface_list[1].mac_address, g_slave_eth_mac)); + fail_unless(slave_ale->local_iface_list[1].media_type == INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET); + + fail_unless(!maccmp(slave_ale->local_iface_list[2].mac_address, g_6g_bh_sta_mac)); + fail_unless(!maccmp(slave_ale->local_iface_list[2].ieee80211_network_membership, g_bssid_6G_bh)); + fail_unless(slave_ale->local_iface_list[2].media_type == INTERFACE_TYPE_IEEE_802_11AX); + fail_unless(slave_ale->local_iface_list[2].ieee80211_role == IEEE80211_SPECIFIC_INFO_ROLE_NON_AP_NON_PCP_STA); + + fail_unless(!maccmp(slave_ale->local_iface_list[5].mac_address, g_5g_bh_sta_mac)); + fail_unless(!maccmp(slave_ale->local_iface_list[5].ieee80211_network_membership, g_zero_mac)); + fail_unless(slave_ale->local_iface_list[5].media_type == INTERFACE_TYPE_IEEE_802_11AX); + fail_unless(slave_ale->local_iface_list[5].ieee80211_role == IEEE80211_SPECIFIC_INFO_ROLE_NON_AP_NON_PCP_STA); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_MLO_TOPOLOGY_RESPONSE # +########################################################################*/ +START_TEST(test_mlo_topology_response) +{ + map_ale_info_t *ale; + map_ap_mld_info_t *ap_mld; + map_sta_mld_info_t *sta_mld; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + int count = 0; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_mlo_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!maccmp(ale->src_mac, g_al_mac)); + + /* Check AP MLD */ + fail_unless(ale->ap_mld_nr == 2); + fail_unless(!!(ap_mld = map_dm_get_ap_mld(ale, (mac_addr){0x00, 0x90, 0x4C, 0x4C, 0x84, 0x7A}))); + fail_unless(ap_mld->ssid_len == 11); + fail_unless(!memcmp(ap_mld->ssid, (char*)"frv_test_fh", 11)); + + map_dm_foreach_aff_ap(ap_mld, bss) { + if (count == 0) { + fail_unless(bss->ap_mld == ap_mld); + fail_unless(!memcmp(bss->bssid, (mac_addr){0x00, 0x90, 0x4C, 0x4C, 0x94, 0x1F}, sizeof(mac_addr))); + fail_unless(bss->link_id == 1); + } + count++; + } + fail_unless(count == 3); + + /* Check STA MLD */ + fail_unless(ap_mld->sta_mld_nr == 1); + fail_unless(!!(sta_mld = map_dm_get_sta_mld(ap_mld, (mac_addr){0xE4, 0x60, 0x17, 0x5E, 0x34, 0x4F}))); + fail_unless(sta_mld == map_dm_get_sta_mld_from_ale(ale, (mac_addr){0xE4, 0x60, 0x17, 0x5E, 0x34, 0x4F})); + + count = 0; + map_dm_foreach_aff_sta(sta_mld, sta) { + if (count == 0) { + fail_unless(sta->sta_mld == sta_mld); + fail_unless(!memcmp(sta->mac, (mac_addr){0xFA, 0x43, 0xD1, 0x0F, 0x33, 0xD6}, sizeof(mac_addr))); + fail_unless(!memcmp(sta->bss->bssid, (mac_addr){0x00, 0x90, 0x4C, 0x4D, 0x41, 0xF5}, sizeof(mac_addr))); + fail_unless(sta == map_dm_get_sta_from_ale(ale, (mac_addr){0xFA, 0x43, 0xD1, 0x0F, 0x33, 0xD6})); + } + count++; + } + fail_unless(count == 2); + + /* Check normal STA (there should be 4 in total) */ + fail_unless(!!map_dm_get_sta_from_ale(ale, (mac_addr){0x70, 0xD8, 0x23, 0xD2, 0xE3, 0x62})); + count = 0; + map_dm_foreach_radio(ale, radio) { + map_dm_foreach_bss(radio, bss) { + map_dm_foreach_sta(bss, sta) { + count++; + } + } + } + fail_unless(count == 4); + + /* Remove all MLD */ + read_parse("cmdu_topology_response.pcap", g_al_mac); + fail_unless(ale->ap_mld_nr == 0); + + /* Add again to test cleanup */ + read_parse("cmdu_mlo_topology_response.pcap", g_al_mac); + fail_unless(ale->ap_mld_nr == 2); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_TOPOLOGY_NOTIFICATION # +########################################################################*/ +START_TEST(test_topology_notification) +{ + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + /* Sta is connected at this moment */ + fail_unless(!!map_dm_get_sta_gbl(g_S10_mac)); + + read_parse("cmdu_topology_notification_disassoc.pcap", g_al_mac); + fail_unless(!map_dm_get_sta_gbl(g_S10_mac)); + + read_parse("cmdu_topology_notification_assoc.pcap", g_al_mac); + fail_unless(!!map_dm_get_sta_gbl(g_S10_mac)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_TOPOLOGY_NOTIFICATION_BACKHAUL_SWITCH # +########################################################################*/ +START_TEST(topology_notification_backhaul_switch) +{ + map_ale_info_t *master_ale; + map_ale_info_t *slave_ale; + map_radio_info_t *master_radio_5g, *master_radio_6g, *slave_radio_5g, *slave_radio_6g; + map_backhaul_sta_iface_t *bhsta_iface_5g, *bhsta_iface_6g; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_multiple_backhaul_topology_response_master.pcap", g_al_mac); + read_parse("cmdu_topology_discovery_slave.pcap", g_slave_al_mac); + read_parse("cmdu_multiple_backhaul_topology_response_slave.pcap", g_slave_al_mac); + read_parse("cmdu_backhaul_sta_capability_report_slave.pcap", g_slave_al_mac); + + fail_unless(!!(master_ale = map_dm_get_ale(g_al_mac))); + fail_unless(!!(slave_ale = map_dm_get_ale(g_slave_al_mac))); + + fail_unless(!!(master_radio_5g = map_dm_get_radio(master_ale, g_radio_id_5G))); + fail_unless(!!(master_radio_6g = map_dm_get_radio(master_ale, g_radio_id_6G))); + + fail_unless(!!(slave_radio_5g = map_dm_get_radio(slave_ale, g_slave_radio_id_5G))); + fail_unless(!!(slave_radio_6g = map_dm_get_radio(slave_ale, g_slave_radio_id_6G))); + + fail_unless(!!(bhsta_iface_5g = map_find_bhsta_iface_from_ale(slave_ale, g_5g_bh_sta_mac))); + fail_unless(!!(bhsta_iface_6g = map_find_bhsta_iface_from_ale(slave_ale, g_6g_bh_sta_mac))); + + /* 6G Backhaul connection */ + fail_unless(master_radio_5g->channel_configurable == true); + fail_unless(master_radio_5g->channel_configurable == true); + fail_unless(slave_radio_5g->channel_configurable == true); + fail_unless(slave_radio_6g->channel_configurable == false); + + read_parse("cmdu_topology_notification_bhsta_disassoc_6G.pcap", g_al_mac); + read_parse("cmdu_topology_notification_bhsta_assoc_5G.pcap", g_al_mac); + + /* 5G Backhaul connection */ + fail_unless(master_radio_5g->channel_configurable == true); + fail_unless(master_radio_5g->channel_configurable == true); + fail_unless(slave_radio_5g->channel_configurable == false); + fail_unless(slave_radio_6g->channel_configurable == true); + + read_parse("cmdu_topology_notification_bhsta_disassoc_5G.pcap", g_al_mac); + + /* Ethernet Backhaul connection */ + fail_unless(master_radio_5g->channel_configurable == true); + fail_unless(master_radio_5g->channel_configurable == true); + fail_unless(slave_radio_5g->channel_configurable == true); + fail_unless(slave_radio_6g->channel_configurable == true); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_LINK_METRIC_QUERY # +########################################################################*/ +START_TEST(test_link_metric_query) +{ + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_link_metric_query.pcap", g_al_mac); + + /* Nothing to test */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_LINK_METRIC_RESPONSE # +########################################################################*/ +START_TEST(test_link_metric_response) +{ + map_ale_info_t *ale; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + /* Add other ale, otherwise "links" will not be stored */ + fail_unless(!!map_dm_create_ale((mac_addr){0xF6, 0x17, 0xB8, 0xAE, 0x86, 0xEF})); + fail_unless(!!map_dm_create_ale((mac_addr){0xF6, 0x17, 0xB8, 0xBD, 0xBA, 0xBC})); + + read_parse("cmdu_link_metric_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); +#if 0 + fail_unless(ale->neighbor_link_count == 2); + fail_unless(!memcmp(ale->neighbor_link_list[0].neighbor_al_mac, (mac_addr){0xF6, 0x17, 0xB8, 0xAE, 0x86, 0xEF}, sizeof(mac_addr))); + fail_unless(!memcmp(ale->neighbor_link_list[0].local_iface_mac, (mac_addr){0x6A, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(!memcmp(ale->neighbor_link_list[0].neighbor_iface_mac, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}, sizeof(mac_addr))); + fail_unless(!memcmp(ale->neighbor_link_list[1].neighbor_al_mac, (mac_addr){0xF6, 0x17, 0xB8, 0xBD, 0xBA, 0xBC}, sizeof(mac_addr))); +#else + fail_unless(ale->neighbor_link_count == 0); +#endif + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_SEARCH_1 # +########################################################################*/ +/* Autoconfig search after topology response */ +START_TEST(test_autoconfig_search_1) +{ + map_ale_info_t *ale; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_ap_autoconfiguration_search.pcap", g_al_mac); + + /* Nothing extra happened */ + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(ale->easymesh == true); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_SEARCH_2 # +########################################################################*/ +/* Autoconfig search as first onboarding message -> ale created */ +START_TEST(test_autoconfig_search_2) +{ + map_ale_info_t *ale; + + test_init(); + + read_parse("cmdu_ap_autoconfiguration_search.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(ale->map_profile == MAP_PROFILE_2); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_LEGACY_AUTOCONFIG_SEARCH_1 # +########################################################################*/ +/* Autoconfig search after topology response */ +START_TEST(test_legacy_autoconfig_search_1) +{ + map_ale_info_t *ale; + + test_init(); + + read_parse("cmdu_legacy_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_legacy_topology_response.pcap", g_al_mac); + read_parse("cmdu_legacy_ap_autoconfiguration_search.pcap", g_al_mac); + + /* Nothing extra happened */ + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(ale->easymesh == false); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_LEGACY_AUTOCONFIG_SEARCH_2 # +########################################################################*/ +/* Autoconfig search as first onboarding message -> ale not created */ +START_TEST(test_legacy_autoconfig_search_2) +{ + test_init(); + + read_parse("cmdu_legacy_ap_autoconfiguration_search.pcap", g_al_mac); + + /* No ale created but added to staging list */ + fail_unless(!map_dm_get_ale(g_al_mac)); + fail_unless(!!map_stglist_get_1905_dev(g_al_mac)); + fail_unless(!!map_stglist_get_1905_dev_from_src_mac(g_al_mac)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_WSC_1 # +########################################################################*/ +/* Autoconfig WSC after topology response */ +START_TEST(test_autoconfig_wsc_1) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->max_bss == 0); + fail_unless(radio->cap_op_class_list.op_classes_nr == 0); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->max_bss == 0); + fail_unless(radio->cap_op_class_list.op_classes_nr == 0); + + read_parse("cmdu_ap_autoconfiguration_wsc_2G.pcap", g_al_mac); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->max_bss == 15); + fail_unless(radio->cap_op_class_list.op_classes_nr == 3); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->max_bss == 0); + fail_unless(radio->cap_op_class_list.op_classes_nr == 0); + + read_parse("cmdu_ap_autoconfiguration_wsc_5G.pcap", g_al_mac); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->max_bss == 15); + fail_unless(radio->cap_op_class_list.op_classes_nr == 3); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->max_bss == 15); + fail_unless(radio->cap_op_class_list.op_classes_nr == 15); + + /* Profile 2 AP cap */ + fail_unless(ale->agent_capability.profile_2_ap_cap_valid == true); + fail_unless(ale->agent_capability.max_vid_count == 2); + fail_unless(ale->agent_capability.byte_counter_unit == MAP_BYTE_COUNTER_UNIT_KIBI_BYTES); + + /* M1 attributes */ + fail_unless(!strcmp(ale->device_info.manufacturer_name, "AirTies Wireless Networks")); + fail_unless(!strcmp(ale->device_info.model_name, "Air4960")); + fail_unless(!strcmp(ale->device_info.model_number, "4960")); + fail_unless(!strcmp(ale->device_info.serial_number, "AW2631942000028")); + fail_unless(ale->device_info.os_version == 0xFFFEFDFC); + fail_unless(!strcmp(ale->device_info.os_version_str, "127.254.253.252")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_WSC_2 # +########################################################################*/ +/* Autoconfig WSC as first onboarding message -> ale and radio created from M1 */ +START_TEST(test_autoconfig_wsc_2) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + + read_parse("cmdu_ap_autoconfiguration_wsc_2G.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(!map_dm_get_radio(ale, g_radio_id_5G)); + fail_unless(!map_dm_get_radio(ale, g_radio_id_6G)); + + read_parse("cmdu_ap_autoconfiguration_wsc_5G.pcap", g_al_mac); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(!map_dm_get_radio(ale, g_radio_id_6G)); + + read_parse("cmdu_ap_autoconfiguration_wsc_6G.pcap", g_al_mac); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_WSC_TRIBAND # +########################################################################*/ +/* Autoconfig WSC after topology response */ +START_TEST(test_autoconfig_wsc_triband) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_triband_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->max_bss == 0); + fail_unless(radio->cap_op_class_list.op_classes_nr == 0); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->max_bss == 0); + fail_unless(radio->cap_op_class_list.op_classes_nr == 0); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + fail_unless(radio->max_bss == 0); + fail_unless(radio->cap_op_class_list.op_classes_nr == 0); + + read_parse("cmdu_ap_autoconfiguration_wsc_2G.pcap", g_al_mac); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->max_bss == 15); + fail_unless(radio->cap_op_class_list.op_classes_nr == 3); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->max_bss == 0); + fail_unless(radio->cap_op_class_list.op_classes_nr == 0); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + fail_unless(radio->max_bss == 0); + fail_unless(radio->cap_op_class_list.op_classes_nr == 0); + + read_parse("cmdu_ap_autoconfiguration_wsc_5G.pcap", g_al_mac); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->max_bss == 15); + fail_unless(radio->cap_op_class_list.op_classes_nr == 3); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->max_bss == 15); + fail_unless(radio->cap_op_class_list.op_classes_nr == 15); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + fail_unless(radio->max_bss == 0); + fail_unless(radio->cap_op_class_list.op_classes_nr == 0); + + read_parse("cmdu_ap_autoconfiguration_wsc_6G.pcap", g_al_mac); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->max_bss == 15); + fail_unless(radio->cap_op_class_list.op_classes_nr == 3); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->max_bss == 15); + fail_unless(radio->cap_op_class_list.op_classes_nr == 15); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + fail_unless(radio->max_bss == 15); + fail_unless(radio->cap_op_class_list.op_classes_nr == 5); + + /* Profile 2 AP cap */ + fail_unless(ale->agent_capability.profile_2_ap_cap_valid == true); + fail_unless(ale->agent_capability.max_vid_count == 2); + fail_unless(ale->agent_capability.byte_counter_unit == MAP_BYTE_COUNTER_UNIT_KIBI_BYTES); + + /* M1 attributes */ + fail_unless(!strcmp(ale->device_info.manufacturer_name, "AirTies Wireless Networks")); + fail_unless(!strcmp(ale->device_info.model_name, "Air4980")); + fail_unless(!strcmp(ale->device_info.model_number, "4980")); + fail_unless(!strcmp(ale->device_info.serial_number, "ATT498000COFFEE")); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_WSC_TOO_MANY_RADIO # +########################################################################*/ +/* WCS-6239: Adding too many radios resulted in a stack corruption in map_build_and_send_policy_config + Why appearantly extra radio MAC appear is not known + */ +START_TEST(test_autoconfig_wsc_too_many_radio) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_triband_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + + /* Modify radio_id's and read WSC captures that will add more radio and call map_build_and_send_policy_config */ + for (int i = 1; i < 32; i++) { + fail_unless(ale->radios_nr == 3 * i); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + radio->radio_id[0] = 0x00; + radio->radio_id[1] = i; + acu_mac_to_string(radio->radio_id, radio->radio_id_str); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + radio->radio_id[0] = 0x01; + radio->radio_id[1] = i; + acu_mac_to_string(radio->radio_id, radio->radio_id_str); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + radio->radio_id[0] = 0x02; + radio->radio_id[1] = i; + acu_mac_to_string(radio->radio_id, radio->radio_id_str); + + read_parse("cmdu_ap_autoconfiguration_wsc_2G.pcap", g_al_mac); + read_parse("cmdu_ap_autoconfiguration_wsc_5G.pcap", g_al_mac); + read_parse("cmdu_ap_autoconfiguration_wsc_6G.pcap", g_al_mac); + } + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AP_CAP_REPORT # +########################################################################*/ +START_TEST(test_ap_cap_report) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_ap_capability_report.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + + /* AP cap tlv */ + fail_unless(ale->agent_capability.ib_unassociated_sta_link_metrics_supported); + fail_unless(!ale->agent_capability.oob_unassociated_sta_link_metrics_supported); + + /* AP radio basic cap tlv */ + fail_unless(radio->max_bss == 15); + fail_unless(radio->cap_op_class_list.op_classes_nr == 15); + fail_unless(radio->cap_op_class_list.op_classes[14].op_class == 129); + fail_unless(radio->cap_op_class_list.op_classes[14].eirp == 23); + fail_unless(map_cs_nr(&radio->cap_op_class_list.op_classes[14].channels) == 2); + fail_unless(map_cs_is_set(&radio->cap_op_class_list.op_classes[14].channels, 114)); + fail_unless(map_cs_is_set(&radio->cap_op_class_list.op_classes[14].channels, 163)); + + /* AP HT cap tlv */ + fail_unless(!!radio->ht_caps); + fail_unless(radio->ht_caps->max_supported_tx_streams == 4); + + /* AP VHT cap tlv */ + fail_unless(!!radio->vht_caps); + fail_unless(radio->vht_caps->max_supported_tx_streams == 4); + + /* AP HE cap tlv */ + fail_unless(!!radio->he_caps); + fail_unless(radio->he_caps->max_supported_tx_streams == 4); + fail_unless(radio->he_caps->support_160mhz == 1); + + /* Combined caps */ + fail_unless(radio->radio_caps.supported_standard == STD_80211_ANACAX); + fail_unless(radio->radio_caps.max_tx_spatial_streams == 4); + fail_unless(radio->radio_caps.max_bandwidth == 160); + + /* Profile 2 AP cap */ + fail_unless(ale->agent_capability.profile_2_ap_cap_valid == true); + fail_unless(ale->agent_capability.max_vid_count == 2); + fail_unless(ale->agent_capability.byte_counter_unit == MAP_BYTE_COUNTER_UNIT_KIBI_BYTES); + + /* scan cap */ + fail_unless(radio->scan_caps.valid == true); + fail_unless(radio->scan_caps.boot_only == 0); + fail_unless(radio->scan_caps.scan_impact == MAP_SCAN_IMPACT_TIME_SLICING); + fail_unless(radio->scan_caps.min_scan_interval == 900); + fail_unless(radio->scan_caps.op_class_list.op_classes_nr == 5); + fail_unless(radio->scan_caps.op_class_list.op_classes[4].op_class == 125); + fail_unless(map_cs_nr(&radio->scan_caps.op_class_list.op_classes[4].channels) == 5); + fail_unless(map_cs_is_set(&radio->scan_caps.op_class_list.op_classes[4].channels, 149)); + fail_unless(map_cs_is_set(&radio->scan_caps.op_class_list.op_classes[4].channels, 165)); + + /* metric collection interval tlv */ + fail_unless(ale->agent_capability.metric_collection_interval == 2108000); /* weird value... */ + + /* cac capabilities */ + fail_unless(ale->country_code == 0x5553); + fail_unless(radio->cac_caps.cac_method_count == 2); + fail_unless(radio->cac_caps.cac_method[0].cac_method == MAP_CAC_METHOD_CONTINUOUS); + fail_unless(radio->cac_caps.cac_method[0].cac_duration == 60); + fail_unless(radio->cac_caps.cac_method[0].op_class_list.op_classes_nr == 8); + fail_unless(radio->cac_caps.cac_method[0].op_class_list.op_classes[7].op_class == 129); + fail_unless(map_cs_nr(&radio->cac_caps.cac_method[0].op_class_list.op_classes[7].channels) == 1); + fail_unless(map_cs_is_set(&radio->cac_caps.cac_method[0].op_class_list.op_classes[7].channels, 50)); + fail_unless(radio->cac_caps.cac_method[1].cac_method == MAP_CAC_METHOD_MIMO_DIM_REDUCED); + + test_fini(); +} +END_TEST + + +/*####################################################################### +# TEST_AP_CAP_REPORT_CHANGE # +########################################################################*/ +static void check_cap_change(map_radio_info_t *radio, bool ht, bool vht, bool he, int std, int ss, int bw) +{ + fail_unless((ht && radio->ht_caps) || !radio->ht_caps); + fail_unless((vht && radio->vht_caps) || !radio->vht_caps); + fail_unless((he && radio->he_caps) || !radio->he_caps); + fail_unless(radio->radio_caps.supported_standard == std); + fail_unless(radio->radio_caps.max_tx_spatial_streams == ss); + fail_unless(radio->radio_caps.max_bandwidth == bw); +} + +START_TEST(test_ap_cap_report_change) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_ap_capability_report.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + + /* HT/VHT/HE on 2.4G */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + check_cap_change(radio, true, true, true, STD_80211_NAX, 2, 40); + + /* HT/VHT/HE on 5G */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + check_cap_change(radio, true, true, true, STD_80211_ANACAX, 4, 160); + + + /* Read pcap with some cap removed. */ + read_parse("cmdu_ap_capability_report_no_he.pcap", g_al_mac); + + /* HT on 2.4G */ + /* Note: nmode was 0, brcm still adds HT cap TLV with 11G cap?? */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + check_cap_change(radio, true, false, false, STD_80211_N, 1, 20); + + /* HT/VHT on 5G */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + check_cap_change(radio, true, true, false, STD_80211_AC, 4, 160); + + + /* Read with some cap added again. */ + read_parse("cmdu_ap_capability_report.pcap", g_al_mac); + + /* HT/VHT/HE on 2.4G */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + check_cap_change(radio, true, true, true, STD_80211_NAX, 2, 40); + + /* HT/VHT/HE on 5G */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + check_cap_change(radio, true, true, true, STD_80211_ANACAX, 4, 160); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_TRIBAND_AP_CAP_REPORT # +########################################################################*/ +START_TEST(test_triband_ap_cap_report) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_triband_topology_response.pcap", g_al_mac); + read_parse("cmdu_triband_ap_capability_report.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + + /* AP cap tlv */ + fail_unless(ale->agent_capability.ib_unassociated_sta_link_metrics_supported); + fail_unless(ale->agent_capability.oob_unassociated_sta_link_metrics_supported); + + /* AP radio basic cap tlv */ + fail_unless(radio->max_bss == 15); + fail_unless(radio->cap_op_class_list.op_classes_nr == 5); + fail_unless(radio->cap_op_class_list.op_classes[4].op_class == 136); + fail_unless(radio->cap_op_class_list.op_classes[4].eirp == 23); + fail_unless(map_cs_nr(&radio->cap_op_class_list.op_classes[4].channels) == 0); + + /* AP HT cap tlv */ + fail_unless(!!radio->ht_caps); + fail_unless(radio->ht_caps->max_supported_tx_streams == 4); + + /* AP VHT cap tlv */ + fail_unless(!!radio->vht_caps); + fail_unless(radio->vht_caps->max_supported_tx_streams == 4); + + /* AP HE cap tlv */ + fail_unless(!!radio->he_caps); + fail_unless(radio->he_caps->max_supported_tx_streams == 4); + fail_unless(radio->he_caps->support_160mhz == 1); + + /* Combined caps */ + fail_unless(radio->radio_caps.supported_standard == STD_80211_AX); + fail_unless(radio->radio_caps.max_tx_spatial_streams == 4); + fail_unless(radio->radio_caps.max_bandwidth == 160); + + /* Profile 2 AP cap */ + fail_unless(ale->agent_capability.profile_2_ap_cap_valid == true); + fail_unless(ale->agent_capability.max_vid_count == 2); + fail_unless(ale->agent_capability.byte_counter_unit == MAP_BYTE_COUNTER_UNIT_KIBI_BYTES); + + /* scan cap */ + fail_unless(radio->scan_caps.valid == true); + fail_unless(radio->scan_caps.boot_only == 0); + fail_unless(radio->scan_caps.scan_impact == MAP_SCAN_IMPACT_TIME_SLICING); + fail_unless(radio->scan_caps.min_scan_interval == 900); + fail_unless(radio->scan_caps.op_class_list.op_classes_nr == 2); + fail_unless(radio->scan_caps.op_class_list.op_classes[1].op_class == 136); + fail_unless(map_cs_nr(&radio->scan_caps.op_class_list.op_classes[1].channels) == 0); + + /* metric collection interval tlv */ + fail_unless(ale->agent_capability.metric_collection_interval == 12000); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_EARLY_AP_CAP_REPORT # +########################################################################*/ +START_TEST(test_early_ap_cap_report) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_mlo_topology_response.pcap", g_al_mac); + read_parse("cmdu_early_ap_capability_report.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + + /* Other TLVs are handled same in regular ap capability report */ + + /* wifi7 agent capabilities tlv */ + fail_unless(ale->agent_capability.max_mlds == 16); + fail_unless(ale->agent_capability.ap_max_links == 2); + fail_unless(ale->agent_capability.bsta_max_links == 2); + fail_unless(ale->agent_capability.tid_to_link_map_cap == 1); + + fail_unless(!!radio->eht_caps); + fail_unless(!!radio->wifi7_caps); + + fail_unless(radio->wifi7_caps->ap_mld_modes.str == true); + fail_unless(radio->wifi7_caps->ap_mld_modes.nstr == false); + fail_unless(radio->wifi7_caps->ap_mld_modes.emlsr == false); + fail_unless(radio->wifi7_caps->ap_mld_modes.emlmr == false); + fail_unless(radio->wifi7_caps->bsta_mld_modes.str == true); + fail_unless(radio->wifi7_caps->bsta_mld_modes.nstr == false); + fail_unless(radio->wifi7_caps->bsta_mld_modes.emlsr == false); + fail_unless(radio->wifi7_caps->bsta_mld_modes.emlmr == false); + + fail_unless(radio->wifi7_caps->ap_str_records_nr == 2); + fail_unless(!maccmp(radio->wifi7_caps->ap_str_records[0].ruid, g_radio_id_5G)); + fail_unless(!maccmp(radio->wifi7_caps->ap_str_records[1].ruid, g_radio_id_2G)); + fail_unless(radio->wifi7_caps->ap_str_records[0].freq_separation == 0); + fail_unless(radio->wifi7_caps->ap_str_records[1].freq_separation == 0); + fail_unless(radio->wifi7_caps->ap_nstr_records_nr == 0); + fail_unless(radio->wifi7_caps->ap_emlsr_records_nr == 0); + fail_unless(radio->wifi7_caps->ap_emlmr_records_nr == 0); + + fail_unless(radio->wifi7_caps->bsta_str_records_nr == 0); + fail_unless(radio->wifi7_caps->bsta_nstr_records_nr == 0); + fail_unless(radio->wifi7_caps->bsta_emlsr_records_nr == 0); + fail_unless(radio->wifi7_caps->bsta_emlmr_records_nr == 0); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CHANNEL_PREFERENCE_REPORT # +########################################################################*/ +/* NOTE: emulated radar to generate this cmdu */ +START_TEST(test_channel_preference_report) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(ale->cac_status_report.available_pairs_nr == 0); + fail_unless(ale->cac_status_report.non_occupancy_pairs_nr == 0); + fail_unless(ale->cac_status_report.ongoing_cac_pairs_nr == 0); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + fail_unless(radio->pref_op_class_list.op_classes_nr == 0); + fail_unless(radio->cac_completion_info.detected_pairs_nr == 0); + + read_parse("cmdu_channel_preference_report.pcap", g_al_mac); + + /* channel preference report */ + fail_unless(radio->pref_op_class_list.op_classes_nr == 29); + fail_unless(!!radio->pref_op_class_list.op_classes); + fail_unless(radio->pref_op_class_list.op_classes[28].op_class == 129); + fail_unless(radio->pref_op_class_list.op_classes[28].pref == 0xE); + fail_unless(radio->pref_op_class_list.op_classes[28].reason == 0xA); + fail_unless(map_cs_nr(&radio->pref_op_class_list.op_classes[28].channels) == 1); + fail_unless(map_cs_is_set(&radio->pref_op_class_list.op_classes[28].channels, 50)); + + + /* radio operation restriction */ + fail_unless(radio->op_restriction_list.op_classes_nr == 2); + fail_unless(radio->op_restriction_list.op_classes[0].op_class == 128); + fail_unless(radio->op_restriction_list.op_classes[0].channels_nr == 2); + fail_unless(radio->op_restriction_list.op_classes[0].channels[0].channel == 44); + fail_unless(radio->op_restriction_list.op_classes[0].channels[0].freq_restriction == 0x01); + fail_unless(radio->op_restriction_list.op_classes[0].channels[1].channel == 112); + fail_unless(radio->op_restriction_list.op_classes[0].channels[1].freq_restriction == 0x02); + fail_unless(radio->op_restriction_list.op_classes[1].op_class == 129); + fail_unless(radio->op_restriction_list.op_classes[1].channels_nr == 1); + fail_unless(radio->op_restriction_list.op_classes[1].channels[0].channel == 100); + fail_unless(radio->op_restriction_list.op_classes[1].channels[0].freq_restriction == 0x03); + + + /* cac status report */ + fail_unless(ale->cac_status_report.available_pairs_nr == 48); + fail_unless(ale->cac_status_report.available_pairs[47].op_class == 84); + fail_unless(ale->cac_status_report.available_pairs[47].channel == 11); + + fail_unless(ale->cac_status_report.non_occupancy_pairs_nr == 9); + fail_unless(ale->cac_status_report.non_occupancy_pairs[8].op_class == 128); + fail_unless(ale->cac_status_report.non_occupancy_pairs[8].channel == 106); + + fail_unless(ale->cac_status_report.ongoing_cac_pairs_nr == 2); + fail_unless(ale->cac_status_report.ongoing_cac_pairs[0].op_class == 118); + fail_unless(ale->cac_status_report.ongoing_cac_pairs[0].channel == 52); + fail_unless(ale->cac_status_report.ongoing_cac_pairs[0].seconds_remaining_cac_completion == 500); + fail_unless(ale->cac_status_report.ongoing_cac_pairs[1].op_class == 118); + fail_unless(ale->cac_status_report.ongoing_cac_pairs[1].channel == 56); + fail_unless(ale->cac_status_report.ongoing_cac_pairs[1].seconds_remaining_cac_completion == 400); + + + /* cac completion report */ + fail_unless(radio->cac_completion_info.op_class == 128); + fail_unless(radio->cac_completion_info.channel == 100); + fail_unless(radio->cac_completion_info.status == 0x01); + + fail_unless(radio->cac_completion_info.detected_pairs_nr == 4); + fail_unless(radio->cac_completion_info.detected_pairs[3].opclass_detected == 121); + fail_unless(radio->cac_completion_info.detected_pairs[3].channel_detected == 112); + + + /* 2G radio has nothing... */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->pref_op_class_list.op_classes_nr == 0); + fail_unless(radio->op_restriction_list.op_classes_nr == 0); + fail_unless(radio->cac_completion_info.detected_pairs_nr == 0); + + + /* Read again (triggers radar detection code) */ + read_parse("cmdu_channel_preference_report.pcap", g_al_mac); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CHANNEL_SELECTION_RESPONSE # +########################################################################*/ +START_TEST(test_channel_selection_response) +{ + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_channel_selection_response.pcap", g_al_mac); + + /* Can't validate anything yet */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_OPERATING_CHANNEL_REPORT # +########################################################################*/ +START_TEST(test_operating_channel_report) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + + /* Reset op_class/channel learned from topology response */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_5G))); + radio->current_op_class = 0; + radio->current_op_channel = 0; + + read_parse("cmdu_operating_channel_report.pcap", g_al_mac); + + fail_unless(radio->current_op_class == 128); + fail_unless(radio->current_op_channel == 157); + fail_unless(radio->current_bw == 80); + fail_unless(radio->current_tx_pwr == 23); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_6G_OPERATING_CHANNEL_REPORT # +########################################################################*/ +START_TEST(test_6g_operating_channel_report) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_triband_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + + /* Reset op_class/channel learned from topology response */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + radio->current_op_class = 0; + radio->current_op_channel = 0; + + read_parse("cmdu_6G_operating_channel_report.pcap", g_al_mac); + + fail_unless(radio->current_op_class == 134); + fail_unless(radio->current_op_channel == 165); + fail_unless(radio->current_bw == 160); + fail_unless(radio->current_tx_pwr == 21); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_6G_320MHZ_OPERATING_CHANNEL_REPORT # +########################################################################*/ +START_TEST(test_6g_320mhz_operating_channel_report) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_triband_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + + /* Reset op_class/channel learned from topology response */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + radio->current_op_class = 0; + radio->current_op_channel = 0; + + read_parse("cmdu_6G_320MHz_operating_channel_report.pcap", g_al_mac); + + fail_unless(radio->current_op_class == 137); + fail_unless(radio->current_op_channel == 165); + fail_unless(radio->current_bw == 320); + fail_unless(radio->current_tx_pwr == 21); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CLIENT_CAP_REPORT # +########################################################################*/ +START_TEST(test_client_cap_report) +{ + map_sta_info_t *sta; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_ap_capability_report.pcap", g_al_mac); + + fail_unless(!!(sta = map_dm_get_sta_gbl(g_S10_mac))); + fail_unless(!sta->assoc_frame && sta->assoc_frame_len == 0); + + read_parse("cmdu_client_capability_report.pcap", g_al_mac); + fail_unless(sta->assoc_frame && sta->assoc_frame_len > 0); + + /* STA is 5GHz */ + fail_unless(sta->bss->radio->supported_freq == IEEE80211_FREQUENCY_BAND_5_GHZ); + + map_sta_capability_t *c = &sta->sta_caps; + fail_unless(c->he_support && c->vht_support && c->ht_support && c->erp_support); + fail_unless(c->supported_standard == STD_80211_ANACAX); + fail_unless(c->max_tx_spatial_streams == 2); + fail_unless(c->dot11k_bra_support && c->dot11v_btm_support && c->mbo_support); + fail_unless(!c->backhaul_sta); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_MLO_CLIENT_CAP_REPORT # +########################################################################*/ +/* These are 3 tests: + - not fragmented multi link IE + - fragemented multi link IE + - fragmented multi link IE and fragmented per station profile sub element +*/ + +static void test_mlo_client_cap_report_common(const char *client_cap_report_pcap) +{ + map_ale_info_t *ale; + map_radio_info_t *radio, *radio_2G = NULL, *radio_5G = NULL, *radio_6G = NULL; + map_bss_info_t *bss_2G, *bss_5G, *bss_6G; + map_ap_mld_info_t *ap_mld; + map_sta_mld_info_t *sta_mld; + map_sta_info_t *sta_2G, *sta_5G, *sta_6G; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_mlo_topology_response.pcap", g_al_mac); + read_parse("cmdu_mlo_ap_capability_report.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!!(ap_mld = map_dm_get_ap_mld(ale, (mac_addr){0x00, 0x90, 0x4C, 0x4C, 0x84, 0x7A}))); + map_dm_foreach_radio(ale, radio) { + if (radio->supported_freq == IEEE80211_FREQUENCY_BAND_6_GHZ) { + radio_6G = radio; + } else if (radio->supported_freq == IEEE80211_FREQUENCY_BAND_5_GHZ) { + radio_5G = radio; + } else if (radio->supported_freq == IEEE80211_FREQUENCY_BAND_2_4_GHZ) { + radio_2G = radio; + } + } + + fail_unless(radio_6G && radio_5G && radio_2G); + fail_unless(!!(bss_6G = list_first_entry(&radio_6G->bss_list, map_bss_info_t, list))); + fail_unless(!!(bss_5G = list_first_entry(&radio_5G->bss_list, map_bss_info_t, list))); + fail_unless(!!(bss_2G = list_first_entry(&radio_2G->bss_list, map_bss_info_t, list))); + + /* Add sta and mld sta present in cmdu_client_capability_report.pcap */ + fail_unless(!!(sta_mld = map_dm_create_sta_mld(ap_mld, (mac_addr){0x00, 0x90 , 0x4c, 0x4c, 0x84, 0x7a}))); + fail_unless(!!(sta_6G = map_dm_create_aff_sta(bss_6G, sta_mld, (mac_addr){0x00, 0x90 , 0x4c, 0x4c, 0x84, 0x7a}))); /* 6G */ + fail_unless(!!(sta_5G = map_dm_create_aff_sta(bss_5G, sta_mld, (mac_addr){0x00, 0x90 , 0x4c, 0x4c, 0x94, 0x1f}))); /* 5G */ + fail_unless(!!(sta_2G = map_dm_create_aff_sta(bss_2G, sta_mld, (mac_addr){0x00, 0x90 , 0x4c, 0x4d, 0x41, 0xf5}))); /* 2G */ + + read_parse(client_cap_report_pcap, g_al_mac); + fail_unless(sta_6G->assoc_frame && sta_6G->assoc_frame_len > 0); + fail_unless(sta_5G->assoc_frame && sta_5G->assoc_frame_len > 0); + fail_unless(sta_2G->assoc_frame && sta_2G->assoc_frame_len > 0); + + fail_unless(sta_6G->sta_caps.eht_support && sta_6G->sta_caps.he_support && !sta_6G->sta_caps.vht_support); + fail_unless(sta_5G->sta_caps.eht_support && sta_5G->sta_caps.he_support && sta_5G->sta_caps.vht_support); + fail_unless(sta_2G->sta_caps.eht_support && sta_2G->sta_caps.he_support && !sta_2G->sta_caps.vht_support); + + fail_unless(sta_6G->sta_caps.max_tx_spatial_streams == 4); + fail_unless(sta_5G->sta_caps.max_tx_spatial_streams == 3); + fail_unless(sta_2G->sta_caps.max_tx_spatial_streams == 2); + + fail_unless(sta_mld->supported_mld_modes.str); + fail_unless(!sta_mld->supported_mld_modes.nstr); + fail_unless(!sta_mld->supported_mld_modes.emlsr); + fail_unless(!sta_mld->supported_mld_modes.emlmr); + + test_fini(); +} + +START_TEST(test_mlo_client_cap_report) +{ + test_mlo_client_cap_report_common("cmdu_mlo_client_capability_report.pcap"); +} +END_TEST + +START_TEST(test_mlo_client_cap_report_frag_ml_ie) +{ + test_mlo_client_cap_report_common("cmdu_mlo_client_capability_report_frag_ml_ie.pcap"); +} +END_TEST + +START_TEST(test_mlo_client_cap_report_frag_ml_ie_frag_sp) +{ + test_mlo_client_cap_report_common("cmdu_mlo_client_capability_report_frag_ml_ie_frag_sp.pcap"); +} +END_TEST + +/*####################################################################### +# TEST_AP_METRICS_RESPONSE # +########################################################################*/ +START_TEST(test_ap_metrics_response) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + map_sta_link_metrics_t *lm; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_ap_metrics_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->radio_metrics.valid); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_bssid_2G_fh))); + fail_unless(bss->metrics.valid); + fail_unless(bss->extended_metrics.valid); + + fail_unless(!!(sta = map_dm_get_sta_gbl(g_S10_mac))); + fail_unless(!!(lm = first_object(sta->metrics))); + fail_unless(lm->dl_mac_datarate == 1134); + fail_unless(lm->rssi == -48); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_6G_AP_METRICS_RESPONSE # +########################################################################*/ +START_TEST(test_6g_ap_metrics_response) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + map_sta_link_metrics_t *lm; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_triband_topology_response.pcap", g_al_mac); + read_parse("cmdu_6G_ap_metrics_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + fail_unless(radio->radio_metrics.valid); + + fail_unless(!!(bss = map_dm_get_bss(radio, g_bssid_6G_fh))); + fail_unless(bss->metrics.valid); + fail_unless(bss->extended_metrics.valid); + + fail_unless(!!(sta = map_dm_get_sta_gbl(g_S10_mac))); + fail_unless(!!(lm = first_object(sta->metrics))); + fail_unless(lm->dl_mac_datarate == 1134); + fail_unless(lm->rssi == -48); + fail_unless(sta->wifi6_sta_tid_info.TID_nr == 3); + fail_unless(sta->wifi6_sta_tid_info.TID[0] == 0); + fail_unless(sta->wifi6_sta_tid_info.queue_size[0] == 65); + fail_unless(sta->wifi6_sta_tid_info.TID[2] == 6); + fail_unless(sta->wifi6_sta_tid_info.queue_size[2] == 19); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_ASSOC_STA_LINK_METRICS_RESPONSE # +########################################################################*/ +START_TEST(test_assoc_sta_link_metrics_response) +{ + map_sta_info_t *sta; + map_sta_link_metrics_t *lm; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_assoc_sta_link_metrics_response.pcap", g_al_mac); + + fail_unless(!!(sta = map_dm_get_sta_gbl(g_S10_mac))); + fail_unless(!!(lm = first_object(sta->metrics))); + fail_unless(lm->dl_mac_datarate == 65); + fail_unless(lm->rssi == -51); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_UNASSOC_STA_LINK_METRICS_RESPONSE # +########################################################################*/ +START_TEST(test_unassoc_sta_link_metrics_response) +{ + map_ale_info_t *ale; + map_unassoc_sta_link_metrics_response_tlv_t *tlv; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_unassoc_sta_link_metrics_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!!(tlv = ale->unassoc_metrics)); + fail_unless(tlv->op_class == 121); + fail_unless(tlv->stas_nr == 1); + fail_unless(!maccmp(tlv->stas[0].mac, g_S10_mac)); + fail_unless(tlv->stas[0].channel == 112); + fail_unless(tlv->stas[0].rcpi_uplink == 128); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_BEACON_METRICS_RESPONSE # +########################################################################*/ +START_TEST(test_beacon_metrics_response) +{ + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_beacon_metrics_response.pcap", g_al_mac); + + /* Cannot validate anything yet */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CLIENT_STEERING_BTM_REPORT # +########################################################################*/ +START_TEST(test_client_steering_btm_report) +{ + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_client_steering_btm_report.pcap", g_al_mac); + + /* Cannot validate anything yet */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CHANNEL_SCAN_REPORT # +########################################################################*/ +START_TEST(test_channel_scan_report) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + map_scan_result_t *sr; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + fail_unless(radio->last_scan_info.last_scan_cnt == 0); + + read_parse("cmdu_channel_scan_report.pcap", g_al_mac); + + fail_unless(radio->last_scan_info.last_scan_cnt == 1); + fail_unless(list_get_size(radio->scanned_bssid_list) == 6); + + /* Check last bss (they are added in front of the list) */ + fail_unless(!!(sr = first_object(radio->scanned_bssid_list))); + fail_unless(!memcmp(sr->neighbor_info.bssid, (mac_addr){0xF4, 0x17, 0xB8, 0xBD, 0xBA, 0xBF}, sizeof(mac_addr))); + fail_unless(sr->neighbor_info.ssid_len == 11); + fail_unless(!memcmp(sr->neighbor_info.ssid, (char*)"frv_test_fh", 11)); + fail_unless(sr->neighbor_info.rcpi == 198); + fail_unless(sr->neighbor_info.ch_bw_len == 2); + fail_unless(!memcmp(sr->neighbor_info.ch_bw, (char*)"20", 2)); + fail_unless(sr->neighbor_info.bss_load_elem_present); + fail_unless(sr->neighbor_info.channel_utilization == 53); + fail_unless(sr->neighbor_info.stas_nr == 0); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_PROXIED_ENCAP_DPP # +########################################################################*/ +START_TEST(test_proxied_encap_dpp) +{ + map_ale_info_t *ale; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + + read_parse("cmdu_proxied_encap_dpp.pcap", g_al_mac); + + mac_addr enrollee_test = {0xF4, 0x17, 0xB8, 0x86, 0x59, 0x47}; + uint8_t frame_test[6] = {0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6}; + uint8_t hash_test[6] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + + fail_unless(ale->dpp_info.encap_msg.frame_len > 0); + fail_unless(ale->dpp_info.encap_msg.frame_type == 255); + fail_unless(ale->dpp_info.encap_msg.frame_indicator == 1); + fail_unless(!memcmp(ale->dpp_info.encap_msg.enrollee, enrollee_test, sizeof(mac_addr))); + fail_unless(!memcmp(ale->dpp_info.encap_msg.frame, frame_test, ale->dpp_info.encap_msg.frame_len)); + + fail_unless(ale->dpp_info.chirp.hash_validity == 0); + fail_unless(!memcmp(ale->dpp_info.chirp.enrollee, enrollee_test, sizeof(mac_addr))); + fail_unless(!memcmp(ale->dpp_info.chirp.hash, hash_test, ale->dpp_info.chirp.hash_len)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CHIRP_NOTIFICATION # +########################################################################*/ +START_TEST(test_chirp_notification) +{ + map_ale_info_t *ale; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + + read_parse("cmdu_chirp_notification.pcap", g_al_mac); + + fail_unless(ale->dpp_info.chirp.hash_validity == 0); + mac_addr enrollee_test = {0xF4, 0x17, 0xB8, 0x86, 0x59, 0x47}; + fail_unless(!memcmp(ale->dpp_info.chirp.enrollee, enrollee_test, sizeof(mac_addr))); + uint8_t hash_test[6] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + fail_unless(!memcmp(ale->dpp_info.chirp.hash, hash_test, ale->dpp_info.chirp.hash_len)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_1905_ENCAP_EAPOL # +########################################################################*/ +START_TEST(test_1905_encap_eapol) +{ + map_ale_info_t *ale; + uint8_t msg[] = {0x00, 0x01, 0x00, 0x0A, 0x11, 0x22, 0x33, 0x44, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + + read_parse("cmdu_1905_encap_eapol.pcap", g_al_mac); + + fail_unless(ale->dpp_info.encap_eapol.frame_len == sizeof(msg)); + fail_unless(!memcmp(ale->dpp_info.encap_eapol.frame, msg, sizeof(msg))); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DIRECT_ENCAP_DPP # +########################################################################*/ +START_TEST(test_direct_encap_dpp) +{ + map_ale_info_t *ale; + uint8_t msg[] = {0x11, 0x22, 0x33, 0x44, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + + read_parse("cmdu_direct_encap_dpp.pcap", g_al_mac); + + fail_unless(ale->dpp_info.message.frame_len == sizeof(msg)); + fail_unless(!memcmp(ale->dpp_info.message.frame, msg, sizeof(msg))); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CLIENT_DISASSOC_STATS # +########################################################################*/ +START_TEST(test_client_disassoc_stats) +{ + map_sta_info_t *sta; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_client_disassociation_stats.pcap", g_al_mac); + + fail_unless(!!(sta = map_dm_get_sta_gbl(g_S10_mac))); + fail_unless(sta->last_disassoc_reason_code == 0x08); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_ASSOC_STATUS_NOTIFICATION # +########################################################################*/ +START_TEST(test_assoc_status_notification) +{ + map_bss_info_t *bss_2G; + map_bss_info_t *bss_5G; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + + fail_unless(!!(bss_2G = map_dm_get_bss_gbl(g_bssid_2G_fh))); + fail_unless(!!(bss_5G = map_dm_get_bss_gbl(g_bssid_5G_fh))); + + bss_2G->assoc_allowance_status = MAP_ASSOC_STATUS_ALLOWED; + bss_5G->assoc_allowance_status = MAP_ASSOC_STATUS_ALLOWED; + + /* Read assoc status notification, will disallow assoc on 2G bss */ + read_parse("cmdu_assoc_status_notification.pcap", g_al_mac); + + fail_unless(bss_2G->assoc_allowance_status == MAP_ASSOC_STATUS_DISALLOWED); + fail_unless(bss_5G->assoc_allowance_status == MAP_ASSOC_STATUS_ALLOWED); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_BACKHAUL_STA_CAPABILITY_REPORT # +########################################################################*/ +START_TEST(test_backhaul_sta_capability_report) +{ + map_ale_info_t *ale; + map_ale_info_t *slave_ale; + map_backhaul_sta_iface_t *bhsta_iface; + + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_multiple_backhaul_topology_response_master.pcap", g_al_mac); + read_parse("cmdu_topology_discovery_slave.pcap", g_slave_al_mac); + read_parse("cmdu_multiple_backhaul_topology_response_slave.pcap", g_slave_al_mac); + read_parse("cmdu_backhaul_sta_capability_report_slave.pcap", g_slave_al_mac); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + fail_unless(!!(slave_ale = map_dm_get_ale(g_slave_al_mac))); + + fail_unless(!!(bhsta_iface = map_find_bhsta_iface_from_ale(slave_ale, g_5g_bh_sta_mac))); + fail_unless(bhsta_iface->active == false); + fail_unless(!maccmp(bhsta_iface->mac_address, g_5g_bh_sta_mac)); + fail_unless(!maccmp(bhsta_iface->radio_id, g_slave_radio_id_5G)); + + fail_unless(!!(bhsta_iface = map_find_bhsta_iface_from_ale(slave_ale, g_6g_bh_sta_mac))); + fail_unless(bhsta_iface->active == true); + fail_unless(!maccmp(bhsta_iface->mac_address, g_6g_bh_sta_mac)); + fail_unless(!maccmp(bhsta_iface->radio_id, g_slave_radio_id_6G)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_BACKHAUL_STEERING_RESPONSE # +########################################################################*/ +START_TEST(test_backhaul_steering_response) +{ + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_backhaul_steering_response.pcap", g_al_mac); + + /* Cannot validate anything yet */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_BACKHAUL_STEERING_RESPONSE_ERROR # +########################################################################*/ +START_TEST(test_backhaul_steering_response_error) +{ + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_backhaul_steering_response_error.pcap", g_al_mac); + + /* Cannot validate anything yet */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_FAILED_CONNECTION # +########################################################################*/ +START_TEST(test_failed_connection) +{ + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_failed_connection.pcap", g_al_mac); + + /* Cannot validate anything yet */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_ACK # +########################################################################*/ +START_TEST(test_ack) +{ + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_ack.pcap", g_al_mac); + + /* Cannot validate anything yet */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_STEERING_COMPLETED # +########################################################################*/ +START_TEST(test_steering_completed) +{ + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_steering_completed.pcap", g_al_mac); + + /* Cannot validate anything yet */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_HIGHER_LAYER_DATA # +########################################################################*/ +START_TEST(test_higher_layer_data) +{ + test_init(); + + read_parse("cmdu_topology_discovery.pcap", g_al_mac); + read_parse("cmdu_topology_response.pcap", g_al_mac); + read_parse("cmdu_higher_layer_data.pcap", g_al_mac); + + /* Cannot validate anything yet */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AVAILABLE_SPECTRUM_INQUIRY # +########################################################################*/ +START_TEST(test_available_spectrum_inquiry) +{ + test_init(); + + read_parse("cmdu_available_spectrum_inquiry.pcap", g_al_mac); + + /* Cannot validate anything yet */ + + test_fini(); +} +END_TEST + +const char *test_suite_name = "cmdu_rx"; +test_case_t test_cases[] = { + TEST("topology_discovery", test_topology_discovery ), + TEST("topology_query", test_topology_query ), + TEST("topology_response", test_topology_response ), + TEST("legacy_topology_response", test_legacy_topology_response ), + TEST("triband_topology_response", test_triband_topology_response ), + TEST("multi_backhaul_topology_response", test_multiple_backhaul_topology_response ), + TEST("mlo_topology_response", test_mlo_topology_response ), + TEST("topology_notification", test_topology_notification ), + TEST("topology_notification_backhaul_switch", topology_notification_backhaul_switch ), + TEST("link_metric_query", test_link_metric_query ), + TEST("link_metric_response", test_link_metric_response ), + TEST("autoconfig_search_1", test_autoconfig_search_1 ), + TEST("autoconfig_search_2", test_autoconfig_search_2 ), + TEST("legacy_autoconfig_search_1", test_legacy_autoconfig_search_1 ), + TEST("legacy_autoconfig_search_2", test_legacy_autoconfig_search_2 ), + TEST("autoconfig_wsc_1", test_autoconfig_wsc_1 ), + TEST("autoconfig_wsc_2", test_autoconfig_wsc_2 ), + TEST("autoconfig_wsc_triband", test_autoconfig_wsc_triband ), + TEST("autoconfig_wsc_too_many_radio", test_autoconfig_wsc_too_many_radio ), + TEST("ap_cap_report", test_ap_cap_report ), + TEST("ap_cap_report_change", test_ap_cap_report_change ), + TEST("triband_ap_cap_report", test_triband_ap_cap_report ), + TEST("early_ap_cap_report", test_early_ap_cap_report ), + TEST("channel_preference_report", test_channel_preference_report ), + TEST("channel_selection_response", test_channel_selection_response ), + TEST("operating_channel_report", test_operating_channel_report ), + TEST("6g_operating_channel_report", test_6g_operating_channel_report ), + TEST("6g_320mhz_operating_channel_report", test_6g_320mhz_operating_channel_report ), + TEST("client_cap_report", test_client_cap_report ), + TEST("mlo_client_cap_report", test_mlo_client_cap_report ), + TEST("mlo_client_cap_report_frag_ml_ie", test_mlo_client_cap_report_frag_ml_ie ), + TEST("mlo_client_cap_report_frag_ml_ie_frag_sp", test_mlo_client_cap_report_frag_ml_ie_frag_sp ), + TEST("ap_metrics_response", test_ap_metrics_response ), + TEST("6g_ap_metrics_response", test_6g_ap_metrics_response ), + TEST("assoc_sta_link_metrics_response", test_assoc_sta_link_metrics_response ), + TEST("unassoc_sta_link_metrics_response", test_unassoc_sta_link_metrics_response ), + TEST("beacon_metrics_response", test_beacon_metrics_response ), + TEST("client_steering_btm_report", test_client_steering_btm_report ), + TEST("channel_scan_report", test_channel_scan_report ), + TEST("chirp_notification", test_chirp_notification ), + TEST("proxied_encap_dpp", test_proxied_encap_dpp ), + TEST("1905_encap_eapol", test_1905_encap_eapol ), + TEST("direct_encap_dpp", test_direct_encap_dpp ), + TEST("client_disassoc_stats", test_client_disassoc_stats ), + TEST("assoc_status_notification", test_assoc_status_notification ), + TEST("backhaul_sta_capability_report", test_backhaul_sta_capability_report ), + TEST("backhaul_steering_response", test_backhaul_steering_response ), + TEST("backhaul_steering_response_error", test_backhaul_steering_response_error ), + TEST("failed_connection", test_failed_connection ), + TEST("ack", test_ack ), + TEST("steering_completed", test_steering_completed ), + TEST("higher_layer_data", test_higher_layer_data ), + TEST("available_spectrum_inquiry", test_available_spectrum_inquiry ), + TEST_CASES_END +}; diff --git a/source/test/controller/test_cmdu_tx.c b/source/test/controller/test_cmdu_tx.c new file mode 100644 index 0000000..c7de2ef --- /dev/null +++ b/source/test/controller/test_cmdu_tx.c @@ -0,0 +1,1676 @@ +/* + * Copyright (c) 2020-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include +#include +#include + +#include "test.h" + +#include "map_ctrl_cmdu_rx.h" +#include "map_ctrl_cmdu_tx.h" +#include "map_ctrl_defines.h" + +#include "map_data_model.h" +#include "map_topology_tree.h" + +#include "../ieee1905/stub/stub_i1905.h" + +/*####################################################################### +# DEFINES # +########################################################################*/ + +/*####################################################################### +# TYPEDEFS # +########################################################################*/ +typedef struct { + bool mld_enabled; + bool bsta_cap_report_received; +} mld_test_t; + +/*####################################################################### +# GLOBALS # +########################################################################*/ +static mac_addr g_ctrl_al_mac = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + +static mac_addr g_mcast_mac = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x13}; + +/* All frames are captured from the same device */ +static char *g_src_ifname = "eth0"; +static mac_addr g_al_mac = {0xF6, 0x17, 0xB8, 0x86, 0x57, 0x68}; +static mac_addr g_radio_id_2G = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}; +static mac_addr g_radio_id_5G = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}; +static mac_addr g_radio_id_6G = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6C}; +static mac_addr g_bssid_2G_fh = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}; +static mac_addr g_bssid_5G_fh = {0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}; +static mac_addr g_S10_mac = {0xA8, 0xDB, 0x03, 0x05, 0x92, 0x1C}; + +static mac_addr_oui g_oui = {0xAA, 0xBB, 0xCC}; +static bool g_mlo_test = false; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static i1905_cmdu_t *parse_cmdu(packet_t *p) +{ + uint8_t *streams[2] = {p->data + sizeof(eth_hdr_t), NULL}; + uint16_t lengths[2] = {p->len, 0}; + + fail_unless(p->len >= sizeof(eth_hdr_t)); + + return parse_1905_CMDU_from_packets(streams, lengths); +} + +static void free_cmdu(i1905_cmdu_t *cmdu) +{ + free_1905_CMDU_structure(cmdu); +} + +static i1905_cmdu_t *read_parse(const char *file) +{ + packet_t *packet; + i1905_cmdu_t *cmdu; + char file_path[PATH_MAX]; + + snprintf(file_path, sizeof(file_path), DATA_DIR"/%s", file); + + fail_unless(!!(packet = pcap_read_first_packet(file_path))); + fail_unless(!!(cmdu = parse_cmdu(packet))); + + strcpy(cmdu->interface_name, g_src_ifname); + maccpy(cmdu->cmdu_stream.src_mac_addr, g_al_mac); + + free(packet); + + return cmdu; +} + +static void read_parse_rx(const char *file) +{ + i1905_cmdu_t *cmdu; + + fail_unless(!!(cmdu = read_parse(file))); + fail_unless(!!map_cmdu_rx_cb(cmdu)); + + free_cmdu(cmdu); +} + +static void test_init(stub_i1905_cmdu_send_cb_t send_cb, void *send_args, map_ale_info_t **ret_ale) +{ + map_controller_cfg_t *cfg = &map_cfg_get()->controller_cfg; + map_ale_info_t *ale; + + stub_i1905_register_lldp_send_cb(NULL, NULL); + stub_i1905_register_cmdu_send_cb(NULL, NULL); + stub_i1905_register_raw_send_cb(NULL, NULL); + + maccpy(cfg->al_mac, g_ctrl_al_mac); + map_cfg_get()->is_master = true; + + fail_unless(!map_info_init()); + fail_unless(!map_dm_init()); + fail_unless(init_topology_tree(g_ctrl_al_mac)); + + /* Put some data in DM */ + read_parse_rx("cmdu_topology_discovery.pcap"); + if (g_mlo_test) { + read_parse_rx("cmdu_mlo_topology_response.pcap"); + read_parse_rx("cmdu_early_ap_capability_report.pcap"); + } else { + read_parse_rx("cmdu_topology_response.pcap"); + read_parse_rx("cmdu_ap_capability_report.pcap"); + } + read_parse_rx("cmdu_channel_preference_report.pcap"); + + /* Add other ale, otherwise "links" will not be stored */ + fail_unless(!!map_dm_create_ale((mac_addr){0xF6, 0x17, 0xB8, 0xAE, 0x86, 0xEF})); + fail_unless(!!map_dm_create_ale((mac_addr){0xF6, 0x17, 0xB8, 0xBD, 0xBA, 0xBC})); + read_parse_rx("cmdu_link_metric_response.pcap"); + + fail_unless(!!(ale = map_dm_get_ale(g_al_mac))); + if (ret_ale) { + *ret_ale = ale; + } + + stub_i1905_register_cmdu_send_cb(send_cb, send_args); + stub_i1905_reset_send_nr(); +} + +static void test_fini(void) +{ + map_controller_cfg_t *cfg = &map_cfg_get()->controller_cfg; + + stub_i1905_register_cmdu_send_cb(NULL, NULL); + + map_dm_fini(); + map_info_fini(); + + free(cfg->profiles); + memset(cfg, 0, sizeof(map_controller_cfg_t)); +} + +static int count_tlvs_type(i1905_cmdu_t *cmdu, uint8_t tlv_type) +{ + uint8_t *tlv; + int idx, count = 0; + + i1905_foreach_tlv_type_in_cmdu(tlv_type, tlv, cmdu, idx) count++; + + return count; +} + +static void check_cmdu(mac_addr dmac, mac_addr exp_dmac, i1905_cmdu_t *cmdu, uint16_t message_type, + uint8_t relay_indicator, const char *interface_name, int tlvs_nr) +{ + fail_unless(!maccmp(dmac, exp_dmac)); + fail_unless(cmdu->message_type == message_type); + fail_unless(cmdu->relay_indicator == relay_indicator); + fail_unless(!strcmp(cmdu->interface_name, interface_name)); + fail_unless(i1905_count_tlvs_in_cmdu(cmdu) == tlvs_nr); + + /* Check EOM TLV. This should trigger valgrind error if array is not big enough */ + fail_unless(cmdu->list_of_TLVs[tlvs_nr] == NULL); +} + + +/*####################################################################### +# TEST_BRIDGE_DISCOVERY # +########################################################################*/ +static void bridge_discovery_send_cb(char *ifname, mac_addr smac, i1905_lldp_payload_t *payload, void *args) +{ + lldp_chassis_id_tlv_t *chassis_id_tlv = NULL; + lldp_port_id_tlv_t *port_id_tlv = NULL; + lldp_time_to_live_tlv_t *time_to_live_tlv = NULL; + uint8_t *tlv; + int idx = 0; + + while((tlv = payload->list_of_TLVs[idx])) { + switch(*tlv) { + case TLV_TYPE_CHASSIS_ID: + chassis_id_tlv = (lldp_chassis_id_tlv_t *)tlv; + break; + case TLV_TYPE_PORT_ID: + port_id_tlv = (lldp_port_id_tlv_t *)tlv; + break; + case TLV_TYPE_TIME_TO_LIVE: + time_to_live_tlv = (lldp_time_to_live_tlv_t *)tlv; + break; + default: + fail_unless(false, "unexpected LLDP TLV"); + break; + } + idx++; + } + + fail_unless(idx == 3 && chassis_id_tlv && port_id_tlv && time_to_live_tlv); + + fail_unless(chassis_id_tlv->chassis_id_subtype == CHASSIS_ID_TLV_SUBTYPE_MAC_ADDRESS); + fail_unless(!maccmp(chassis_id_tlv->chassis_id, g_ctrl_al_mac)); + + fail_unless(port_id_tlv->port_id_subtype == PORT_ID_TLV_SUBTYPE_MAC_ADDRESS); + fail_unless(!memcmp(port_id_tlv->port_id, (mac_addr){0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, sizeof(mac_addr))); + + fail_unless(time_to_live_tlv->ttl == TIME_TO_LIVE_TLV_1905_DEFAULT_VALUE); +} + +START_TEST(test_bridge_discovery) +{ + i1905_interface_info_t interface = {.name = "eth1", .mac_address = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}; + + g_mlo_test = false; + test_init(NULL, NULL, NULL); + + stub_i1905_register_lldp_send_cb(bridge_discovery_send_cb, NULL); + + fail_unless(!map_send_lldp_bridge_discovery(&interface)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_TOPOLOGY_DISCOVERY # +########################################################################*/ +static void topology_discovery_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_mcast_mac, cmdu, CMDU_TYPE_TOPOLOGY_DISCOVERY, RELAY_INDICATOR_OFF, "eth1", 2); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_AL_MAC_ADDRESS, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_MAC_ADDRESS, cmdu)); +} + +START_TEST(test_topology_discovery) +{ + i1905_interface_info_t interface = {.name = "eth1", .mac_address = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}; + + g_mlo_test = false; + test_init(topology_discovery_send_cb, NULL, NULL); + + fail_unless(!map_send_topology_discovery(&interface, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_TOPOLOGY_QUERY # +########################################################################*/ +static void topology_query_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_TOPOLOGY_QUERY, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_MULTIAP_PROFILE, cmdu)); +} + +START_TEST(test_topology_query) +{ + map_ale_info_t *ale; + + g_mlo_test = false; + test_init(topology_query_send_cb, NULL, &ale); + + fail_unless(!map_send_topology_query(ale, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_TOPOLOGY_RESPONSE # +########################################################################*/ +static void topology_response_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_TOPOLOGY_RESPONSE, RELAY_INDICATOR_OFF, g_src_ifname, 4); + + /* With current test data, there is not bridging tlv and no neighbor tlv */ + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_DEVICE_INFORMATION, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_SUPPORTED_SERVICE, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_AP_OPERATIONAL_BSS, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_MULTIAP_PROFILE, cmdu)); +} + +START_TEST(test_topology_response) +{ + i1905_cmdu_t *cmdu; + + g_mlo_test = false; + test_init(NULL, NULL, NULL); + + /* Read topology query */ + fail_unless(!!(cmdu = read_parse("cmdu_topology_query.pcap"))); + + stub_i1905_register_cmdu_send_cb(topology_response_send_cb, NULL); + + fail_unless(!map_send_topology_response(g_al_mac, cmdu)); + fail_unless(stub_i1905_get_send_nr() == 1); + + free_cmdu(cmdu); + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_LINK_METRIC_QUERY # +########################################################################*/ +static void link_metric_query_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_LINK_METRIC_QUERY, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_LINK_METRIC_QUERY, cmdu)); +} + +START_TEST(test_link_metric_query) +{ + map_ale_info_t *ale; + i1905_link_metric_query_tlv_t tlv = {0}; + + g_mlo_test = false; + test_init(link_metric_query_send_cb, NULL, &ale); + + fail_unless(!map_send_link_metric_query(ale, &tlv, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_LINK_METRIC_RESPONSE # +########################################################################*/ +static void link_metric_response_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_LINK_METRIC_RESPONSE, RELAY_INDICATOR_OFF, g_src_ifname, 4); + fail_unless(cmdu->message_id == 123); + + fail_unless(count_tlvs_type(cmdu, TLV_TYPE_TRANSMITTER_LINK_METRIC) == 2); + fail_unless(count_tlvs_type(cmdu, TLV_TYPE_RECEIVER_LINK_METRIC) == 2); +} + +START_TEST(test_link_metric_response) +{ + map_ale_info_t *ale; + i1905_transmitter_link_metric_tlv_t tx_tlvs[2]; + i1905_receiver_link_metric_tlv_t rx_tlvs[2]; + int i; + + g_mlo_test = false; + test_init(link_metric_response_send_cb, NULL, &ale); + + for (i = 0; i < 2; i++) { + tx_tlvs[i].tlv_type = TLV_TYPE_TRANSMITTER_LINK_METRIC; + rx_tlvs[i].tlv_type = TLV_TYPE_RECEIVER_LINK_METRIC; + } + + fail_unless(!map_send_link_metric_response(ale, 123, tx_tlvs, 2, rx_tlvs, 2)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_LINK_METRIC_RESPONSE_ERROR # +########################################################################*/ +static void link_metric_response_error_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + i1905_link_metric_result_code_tlv_t *tlv; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_LINK_METRIC_RESPONSE, RELAY_INDICATOR_OFF, g_src_ifname, 1); + fail_unless(cmdu->message_id == 123); + + fail_unless(!!(tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_LINK_METRIC_RESULT_CODE, cmdu))); + fail_unless(tlv->result_code == 111); +} + +START_TEST(test_link_metric_response_error) +{ + map_ale_info_t *ale; + + g_mlo_test = false; + test_init(link_metric_response_error_send_cb, NULL, &ale); + + fail_unless(!map_send_link_metric_response_error(ale, 123, 111)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_SEARCH # +########################################################################*/ +static void autoconfig_search_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + map_supported_service_tlv_t *tlv; + + check_cmdu(dmac, g_mcast_mac, cmdu, CMDU_TYPE_AP_AUTOCONFIGURATION_SEARCH, RELAY_INDICATOR_ON, "all", 6); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_AL_MAC_ADDRESS, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_SEARCHED_ROLE, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_AUTOCONFIG_FREQ_BAND, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_SEARCHED_SERVICE, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_MULTIAP_PROFILE, cmdu)); + + fail_unless(!!(tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_SUPPORTED_SERVICE, cmdu))); + fail_unless(tlv->services_nr == 1); + +} + +START_TEST(test_autoconfig_search) +{ + g_mlo_test = false; + test_init(autoconfig_search_send_cb, NULL, NULL); + + fail_unless(!map_send_autoconfig_search()); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_RESPONSE # +########################################################################*/ +static void autoconfig_response_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + map_supported_service_tlv_t *ss_tlv; + map_controller_capability_tlv_t *controller_cap_tlv; + map_1905_security_cap_tlv_t *i1905_security_cap_tlv; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_AP_AUTOCONFIGURATION_RESPONSE, RELAY_INDICATOR_OFF, g_src_ifname, 6); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_SUPPORTED_ROLE, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_SUPPORTED_FREQ_BAND, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_MULTIAP_PROFILE, cmdu)); + + fail_unless(!!(ss_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_SUPPORTED_SERVICE, cmdu))); + fail_unless(ss_tlv->services_nr == 1); + + fail_unless(!!(controller_cap_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_CONTROLLER_CAPABILITY, cmdu))); + fail_unless(controller_cap_tlv->capability == (MAP_CONTROLLER_CAP_KIBMIB_COUNTER /*| MAP_CONTROLLER_CAP_EARLY_AP_CAP */)); + + fail_unless(!!(i1905_security_cap_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_1905_LAYER_SECURITY_CAPABILITY, cmdu))); + fail_unless(i1905_security_cap_tlv->onboarding_protocol == MAP_1905_ONBOARDING_PROTOCOL_DPP); + fail_unless(i1905_security_cap_tlv->mic_algorithm == MAP_1905_MIC_ALGO_HMAC_SHA256); + fail_unless(i1905_security_cap_tlv->encryption_algorithm == MAP_1905_ENCRPYT_ALGO_AES_SIV); +} + +START_TEST(test_autoconfig_response) +{ + i1905_cmdu_t *cmdu; + + g_mlo_test = false; + test_init(NULL, NULL, NULL); + + /* Read topology query */ + fail_unless(!!(cmdu = read_parse("cmdu_ap_autoconfiguration_search.pcap"))); + + stub_i1905_register_cmdu_send_cb(autoconfig_response_send_cb, NULL); + + fail_unless(!map_send_autoconfig_response(cmdu, true)); + fail_unless(stub_i1905_get_send_nr() == 1); + + free_cmdu(cmdu); + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_WSC_M2 # +########################################################################*/ +static void autoconfig_wsc_m2_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + map_ap_radio_identifier_tlv_t *radio_id_tlv; + map_traffic_separation_policy_tlv_t *ts_tlv; + map_default_8021q_settings_tlv_t *def_8021q_tlv; + bool ts = *(bool *)args; + + /* Expected TLVS: + - 1 AP Radio Identifier TLV + - 2 WSC TLV + - 1 TS Policy TLV + - When TS enabled, 1 default 8021Q settings tlv + */ + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_AP_AUTOCONFIGURATION_WSC, RELAY_INDICATOR_OFF, g_src_ifname, ts ? 5 : 4); + + fail_unless(!!(radio_id_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AP_RADIO_IDENTIFIER, cmdu))); + fail_unless(!maccmp(radio_id_tlv->radio_id, g_radio_id_2G)); + + if (!ts) { + /* Only empty ts policy TLV */ + fail_unless(!!(ts_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_TRAFFIC_SEPARATION_POLICY, cmdu))); + fail_unless(ts_tlv->ssids_nr == 0); + } else { + /* TS and default 8021Q settings TLV */ + fail_unless(!!(ts_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_TRAFFIC_SEPARATION_POLICY, cmdu))); + fail_unless(ts_tlv->ssids_nr == 3); + + fail_unless(ts_tlv->ssids[0].ssid_len == 5); + fail_unless(!memcmp(ts_tlv->ssids[0].ssid, "ssid0", 5)); + fail_unless(ts_tlv->ssids[0].vlan_id == 10); + + fail_unless(ts_tlv->ssids[1].ssid_len == 5); + fail_unless(!memcmp(ts_tlv->ssids[1].ssid, "ssid1", 5)); + fail_unless(ts_tlv->ssids[1].vlan_id == 20); + + fail_unless(ts_tlv->ssids[2].ssid_len == 5); + fail_unless(!memcmp(ts_tlv->ssids[2].ssid, "ssid2", 5)); + fail_unless(ts_tlv->ssids[2].vlan_id == 20); + + fail_unless(!!(def_8021q_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_DEFAULT_8021Q_SETTINGS, cmdu))); + fail_unless(def_8021q_tlv->primary_vlan_id == 10); + fail_unless(def_8021q_tlv->default_pcp == 3); + } + + fail_unless(count_tlvs_type(cmdu, TLV_TYPE_WSC) == 2); +} + +START_TEST(test_autoconfig_wsc_m2) +{ + map_controller_cfg_t *cfg = &map_cfg_get()->controller_cfg; + map_ale_info_t *ale; + map_radio_info_t *radio; + i1905_cmdu_t *cmdu; + bool ts = false; + + g_mlo_test = false; + test_init(NULL, NULL, &ale); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + + /* Read topology query */ + fail_unless(!!(cmdu = read_parse("cmdu_ap_autoconfiguration_wsc_2G.pcap"))); + + stub_i1905_register_cmdu_send_cb(autoconfig_wsc_m2_send_cb, &ts); + + /* Configure some profiles */ + cfg->num_profiles = 3; + fail_unless(!!(cfg->profiles = calloc(cfg->num_profiles, sizeof(map_profile_cfg_t)))); + strcpy(cfg->profiles[0].bss_ssid, "ssid0"); + cfg->profiles[0].enabled = true; + cfg->profiles[0].bss_freq_bands = MAP_M2_BSS_RADIO2G; + cfg->profiles[0].bss_state = MAP_FRONTHAUL_BSS; + cfg->profiles[0].gateway = true; + cfg->profiles[0].extender = true; + cfg->profiles[0].vlan_id = -1; + + strcpy(cfg->profiles[1].bss_ssid, "ssid1"); + cfg->profiles[1].enabled = true; + cfg->profiles[1].bss_freq_bands = MAP_M2_BSS_RADIO2G | MAP_M2_BSS_RADIO5GU; + cfg->profiles[1].bss_state = MAP_FRONTHAUL_BSS; + cfg->profiles[1].gateway = true; + cfg->profiles[1].extender = true; + cfg->profiles[1].vlan_id = 20; + + strcpy(cfg->profiles[2].bss_ssid, "ssid2"); + cfg->profiles[2].enabled = true; + cfg->profiles[2].bss_freq_bands = MAP_M2_BSS_RADIO5GU; /* will not be used except in ts tlv */ + cfg->profiles[2].bss_state = MAP_FRONTHAUL_BSS; + cfg->profiles[2].gateway = true; + cfg->profiles[2].extender = true; + cfg->profiles[2].vlan_id = 20; + + /* No traffic separation */ + ts = false; map_cfg_get()->primary_vlan_id = -1; + fail_unless(!map_send_autoconfig_wsc_m2(ale, radio, cmdu, MID_NA)); + + /* With traffic separation */ + ts = true; map_cfg_get()->primary_vlan_id = 10; map_cfg_get()->default_pcp = 3; + fail_unless(!map_send_autoconfig_wsc_m2(ale, radio, cmdu, MID_NA)); + + fail_unless(stub_i1905_get_send_nr() == 2); + + free_cmdu(cmdu); + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_WSC_M2_MLD # +########################################################################*/ +static void autoconfig_wsc_m2_mld_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + map_ap_radio_identifier_tlv_t *radio_id_tlv; + map_agent_ap_mld_conf_tlv_t *ap_mld_conf_tlv; + map_bsta_mld_conf_tlv_t *bsta_mld_conf_tlv; + mld_test_t *mld = (mld_test_t *)args; + int tlvs_expected; + + tlvs_expected = 6 + (mld->mld_enabled ? 1 : 0) + (mld->bsta_cap_report_received ? 1 : 0); + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_AP_AUTOCONFIGURATION_WSC, RELAY_INDICATOR_OFF, g_src_ifname, tlvs_expected); + + fail_unless(!!(radio_id_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AP_RADIO_IDENTIFIER, cmdu))); + fail_unless(!maccmp(radio_id_tlv->radio_id, g_radio_id_6G)); + + if (!mld->mld_enabled) { + fail_unless(!(ap_mld_conf_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AGENT_AP_MLD_CONFIGURATION, cmdu))); + fail_unless(!(bsta_mld_conf_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_BACKHAUL_STA_MLD_CONFIGURATION, cmdu))); + } else { + fail_unless(!!(ap_mld_conf_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AGENT_AP_MLD_CONFIGURATION, cmdu))); + + fail_unless(ap_mld_conf_tlv->ap_mld_nr == 2); + + fail_unless(ap_mld_conf_tlv->ap_mlds[0].ssid_len == 5); + fail_unless(!memcmp(ap_mld_conf_tlv->ap_mlds[0].ssid, "ssid0", 5)); + fail_unless(ap_mld_conf_tlv->ap_mlds[0].str == 1); + fail_unless(ap_mld_conf_tlv->ap_mlds[0].nstr == 0); + fail_unless(ap_mld_conf_tlv->ap_mlds[0].emlsr == 0); + fail_unless(ap_mld_conf_tlv->ap_mlds[0].emlmr == 0); + + fail_unless(ap_mld_conf_tlv->ap_mlds[0].aff_ap_nr == 3); + fail_unless(!maccmp(ap_mld_conf_tlv->ap_mlds[0].aff_aps[0].radio_id, g_radio_id_2G)); + fail_unless(!maccmp(ap_mld_conf_tlv->ap_mlds[0].aff_aps[1].radio_id, g_radio_id_6G)); + fail_unless(!maccmp(ap_mld_conf_tlv->ap_mlds[0].aff_aps[2].radio_id, g_radio_id_5G)); + + fail_unless(ap_mld_conf_tlv->ap_mlds[1].ssid_len == 5); + fail_unless(!memcmp(ap_mld_conf_tlv->ap_mlds[1].ssid, "ssid1", 5)); + + fail_unless(ap_mld_conf_tlv->ap_mlds[1].aff_ap_nr == 2); + fail_unless(!maccmp(ap_mld_conf_tlv->ap_mlds[1].aff_aps[0].radio_id, g_radio_id_6G)); + fail_unless(!maccmp(ap_mld_conf_tlv->ap_mlds[1].aff_aps[1].radio_id, g_radio_id_5G)); + + if (mld->bsta_cap_report_received) { + fail_unless(!!(bsta_mld_conf_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_BACKHAUL_STA_MLD_CONFIGURATION, cmdu))); + + fail_unless(bsta_mld_conf_tlv->str == 1); + fail_unless(bsta_mld_conf_tlv->nstr == 0); + fail_unless(bsta_mld_conf_tlv->emlsr == 0); + fail_unless(bsta_mld_conf_tlv->emlmr == 0); + + fail_unless(bsta_mld_conf_tlv->aff_bsta_nr == 2); + fail_unless(!maccmp(bsta_mld_conf_tlv->aff_bstas[0].radio_id, g_radio_id_6G)); + fail_unless(!maccmp(bsta_mld_conf_tlv->aff_bstas[1].radio_id, g_radio_id_5G)); + } else { + fail_unless(!(bsta_mld_conf_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_BACKHAUL_STA_MLD_CONFIGURATION, cmdu))); + } + } + + fail_unless(count_tlvs_type(cmdu, TLV_TYPE_WSC) == 3); +} + +START_TEST(test_autoconfig_wsc_m2_mld) +{ + map_controller_cfg_t *cfg = &map_cfg_get()->controller_cfg; + map_ale_info_t *ale; + map_radio_info_t *radio; + i1905_cmdu_t *cmdu; + mld_test_t mld = {0}; + + g_mlo_test = true; + test_init(NULL, NULL, &ale); + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_6G))); + + /* Read WSC M1 */ + fail_unless(!!(cmdu = read_parse("cmdu_ap_autoconfiguration_wsc_6G.pcap"))); + + stub_i1905_register_cmdu_send_cb(autoconfig_wsc_m2_mld_send_cb, &mld); + + /* Configure some profiles */ + cfg->num_profiles = 3; + fail_unless(!!(cfg->profiles = calloc(cfg->num_profiles, sizeof(map_profile_cfg_t)))); + strcpy(cfg->profiles[0].bss_ssid, "ssid0"); + cfg->profiles[0].profile_idx = 0; + cfg->profiles[0].enabled = true; + cfg->profiles[0].bss_freq_bands = MAP_M2_BSS_RADIO2G | MAP_M2_BSS_RADIO5GU | MAP_M2_BSS_RADIO5GL | MAP_M2_BSS_RADIO6G; + cfg->profiles[0].bss_state = MAP_FRONTHAUL_BSS; + cfg->profiles[0].gateway = true; + cfg->profiles[0].extender = true; + cfg->profiles[0].vlan_id = -1; + cfg->profiles[0].mld_id = 0; + + strcpy(cfg->profiles[1].bss_ssid, "ssid1"); + cfg->profiles[1].profile_idx = 1; + cfg->profiles[1].enabled = true; + cfg->profiles[1].bss_freq_bands = MAP_M2_BSS_RADIO5GU | MAP_M2_BSS_RADIO5GL | MAP_M2_BSS_RADIO6G; + cfg->profiles[1].bss_state = MAP_BACKHAUL_BSS; + cfg->profiles[1].gateway = true; + cfg->profiles[1].extender = true; + cfg->profiles[1].vlan_id = -1; + cfg->profiles[1].mld_id = 1; + + strcpy(cfg->profiles[2].bss_ssid, "ssid2"); + cfg->profiles[2].profile_idx = 2; + cfg->profiles[2].enabled = true; + cfg->profiles[2].bss_freq_bands = MAP_M2_BSS_RADIO6G; + cfg->profiles[2].bss_state = MAP_FRONTHAUL_BSS; + cfg->profiles[2].gateway = true; + cfg->profiles[2].extender = true; + cfg->profiles[2].vlan_id = -1; + cfg->profiles[2].mld_id = -1; + + /* No MLD */ + mld.mld_enabled = false; + mld.bsta_cap_report_received = false; + cfg->mld_enabled = false; + fail_unless(!map_send_autoconfig_wsc_m2(ale, radio, cmdu, MID_NA)); + + /* MLD Enabled, No bsta */ + mld.mld_enabled = true; + mld.bsta_cap_report_received = false; + cfg->mld_enabled = true; + fail_unless(!map_send_autoconfig_wsc_m2(ale, radio, cmdu, MID_NA)); + + /* MLD Enabled, bsta exists(slave ap) */ + mld.mld_enabled = true; + mld.bsta_cap_report_received = true; + cfg->mld_enabled = true; + read_parse_rx("cmdu_backhaul_sta_capability_report.pcap"); + fail_unless(!map_send_autoconfig_wsc_m2(ale, radio, cmdu, MID_NA)); + + fail_unless(stub_i1905_get_send_nr() == 3); + + g_mlo_test = false; + + free_cmdu(cmdu); + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_RENEW # +########################################################################*/ +static void autoconfig_renew_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_mcast_mac, cmdu, CMDU_TYPE_AP_AUTOCONFIGURATION_RENEW, RELAY_INDICATOR_ON, "all", 3); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_AL_MAC_ADDRESS, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_SUPPORTED_ROLE, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_SUPPORTED_FREQ_BAND, cmdu)); +} + +START_TEST(test_autoconfig_renew) +{ + g_mlo_test = false; + test_init(autoconfig_renew_send_cb, NULL, NULL); + + fail_unless(!map_send_autoconfig_renew(IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA, true)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AUTOCONFIG_RENEW_UCAST # +########################################################################*/ +static void autoconfig_renew_ucast_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_AP_AUTOCONFIGURATION_RENEW, RELAY_INDICATOR_OFF, g_src_ifname, 3); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_AL_MAC_ADDRESS, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_SUPPORTED_ROLE, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_SUPPORTED_FREQ_BAND, cmdu)); +} + +START_TEST(test_autoconfig_renew_ucast) +{ + map_ale_info_t *ale; + + g_mlo_test = false; + test_init(autoconfig_renew_ucast_send_cb, NULL, &ale); + + fail_unless(!map_send_autoconfig_renew_ucast(ale, IEEE80211_FREQUENCY_BAND_2_4_GHZ, MID_NA, true)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_VENDOR_SPECIFIC # +########################################################################*/ +static void vendor_specific_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + i1905_vendor_specific_tlv_t *tlv; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_VENDOR_SPECIFIC, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!(tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_VENDOR_SPECIFIC, cmdu))); + fail_unless(!memcmp(tlv->vendorOUI, g_oui, sizeof(tlv->vendorOUI))); + fail_unless(tlv->m_nr == 4); + fail_unless(!memcmp(tlv->m, "test", 4)); +} + +START_TEST(test_vendor_specific) +{ + map_ale_info_t *ale; + map_vendor_specific_t vs = {0}; + + g_mlo_test = false; + test_init(vendor_specific_send_cb, NULL, &ale); + + vs.ale = ale; + memcpy(vs.oui, g_oui, sizeof(vs.oui)); + vs.len = 4; + vs.data = (uint8_t *)"test"; + + fail_unless(!map_send_vendor_specific(&vs, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_VENDOR_SPECIFIC_MULT_TLVS # +########################################################################*/ +static void vendor_specific_mult_tlvs_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + i1905_vendor_specific_tlv_t *tlv; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_VENDOR_SPECIFIC, RELAY_INDICATOR_OFF, g_src_ifname, 2); + + tlv = (i1905_vendor_specific_tlv_t *)cmdu->list_of_TLVs[0]; + fail_unless(!memcmp(tlv->vendorOUI, g_oui, sizeof(tlv->vendorOUI))); + fail_unless(tlv->m_nr == 5); + fail_unless(!memcmp(tlv->m, "test1", 5)); + + tlv = (i1905_vendor_specific_tlv_t *)cmdu->list_of_TLVs[1]; + fail_unless(!memcmp(tlv->vendorOUI, g_oui, sizeof(tlv->vendorOUI))); + fail_unless(tlv->m_nr == 6); + fail_unless(!memcmp(tlv->m, "test22", 6)); +} + +START_TEST(test_vendor_specific_mult_tlvs) +{ + map_ale_info_t *ale; + map_vendor_specific_mult_tlv_t vs = {0}; + map_vendor_tlv_tuple_t tlvs[2] = {0}; + + g_mlo_test = false; + test_init(vendor_specific_mult_tlvs_send_cb, NULL, &ale); + + vs.ale = ale; + memcpy(vs.oui, g_oui, sizeof(vs.oui)); + vs.tlvs = tlvs; + vs.tlvs_cnt = 2; + tlvs[0].len = 5; + tlvs[0].data = (uint8_t *)"test1"; + tlvs[1].len = 6; + tlvs[1].data = (uint8_t *)"test22"; + + fail_unless(!map_send_vendor_specific_mult_tlvs(&vs, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_ACK # +########################################################################*/ +static void ack_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_ACK, RELAY_INDICATOR_OFF, g_src_ifname, 0); +} + +START_TEST(test_ack) +{ + map_ale_info_t *ale; + i1905_cmdu_t *cmdu; + + g_mlo_test = false; + test_init(NULL, NULL, &ale); + + /* Read some cmdu */ + fail_unless(!!(cmdu = read_parse("cmdu_channel_scan_report.pcap"))); + + stub_i1905_register_cmdu_send_cb(ack_send_cb, NULL); + + fail_unless(!map_send_ack(ale, cmdu)); + + free_cmdu(cmdu); + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_ACK_STA_ERROR # +########################################################################*/ +static void ack_sta_error_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + map_error_code_tlv_t *tlv; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_ACK, RELAY_INDICATOR_OFF, g_src_ifname, 2); + + fail_unless(count_tlvs_type(cmdu, TLV_TYPE_ERROR_CODE) == 2); + + tlv = (map_error_code_tlv_t *)cmdu->list_of_TLVs[0]; + fail_unless(!maccmp(tlv->sta_mac, g_bssid_2G_fh)); + fail_unless(tlv->reason_code == MAP_ERROR_CODE_STA_ASSOCIATED); + + tlv = (map_error_code_tlv_t *)cmdu->list_of_TLVs[1]; + fail_unless(!maccmp(tlv->sta_mac, g_bssid_5G_fh)); + fail_unless(tlv->reason_code == MAP_ERROR_CODE_STA_ASSOCIATED); +} + +START_TEST(test_ack_sta_error) +{ + map_ale_info_t *ale; + i1905_cmdu_t *cmdu; + mac_addr stas[2]; + + g_mlo_test = false; + test_init(NULL, NULL, &ale); + + /* Read some cmdu */ + fail_unless(!!(cmdu = read_parse("cmdu_channel_scan_report.pcap"))); + + stub_i1905_register_cmdu_send_cb(ack_sta_error_send_cb, NULL); + + maccpy(stas[0], g_bssid_2G_fh); + maccpy(stas[1], g_bssid_5G_fh); + + fail_unless(!map_send_ack_sta_error(ale, cmdu, stas, 2, MAP_ERROR_CODE_STA_ASSOCIATED)); + + free_cmdu(cmdu); + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AP_CAPABILITY_QUERY # +########################################################################*/ +static void ap_capability_query_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_AP_CAPABILITY_QUERY, RELAY_INDICATOR_OFF, g_src_ifname, 0); +} + +START_TEST(test_ap_capability_query) +{ + map_ale_info_t *ale; + + g_mlo_test = false; + test_init(ap_capability_query_send_cb, NULL, &ale); + + fail_unless(!map_send_ap_capability_query(ale, MID_NA)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_POLICY_CONFIG_REQUEST # +########################################################################*/ +static void policy_config_request_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_MULTI_AP_POLICY_CONFIG_REQUEST, RELAY_INDICATOR_OFF, g_src_ifname, 8); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_METRIC_REPORTING_POLICY, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_STEERING_POLICY, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_UNSUCCESSFUL_ASSOCIATION_POLICY, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_CHANNEL_SCAN_REPORTING_POLICY, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_DEFAULT_8021Q_SETTINGS, cmdu)); + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_TRAFFIC_SEPARATION_POLICY, cmdu)); + fail_unless(count_tlvs_type(cmdu, TLV_TYPE_BACKHAUL_BSS_CONFIGURATION) == 2); +} + +START_TEST(test_policy_config_request) +{ + map_ale_info_t *ale; + map_metric_reporting_policy_tlv_t metric_policy_tlv = {0}; + map_steering_policy_tlv_t steering_policy_tlv = {0}; + map_unsuccessful_assoc_policy_tlv_t unsuccess_assoc_policy_tlv = {0}; + map_channel_scan_reporting_policy_tlv_t channel_scan_report_policy_tlv = {0}; + map_default_8021q_settings_tlv_t default_8021q_settings_tlv = {0}; + map_traffic_separation_policy_tlv_t traffic_separation_policy_tlv = {0}; + map_backhaul_bss_configuration_tlv_t bh_bss_config_tlvs[2] = {0}; + + map_policy_config_tlvs_t tlvs = { .metric_policy_tlv = &metric_policy_tlv, + .steering_policy_tlv = &steering_policy_tlv, + .unsuccess_assoc_policy_tlv = &unsuccess_assoc_policy_tlv, + .channel_scan_report_policy_tlv = &channel_scan_report_policy_tlv, + .default_8021q_settings_tlv = &default_8021q_settings_tlv, + .traffic_separation_policy_tlv = &traffic_separation_policy_tlv, + .bh_bss_config_tlvs_nr = 2, + .bh_bss_config_tlvs = bh_bss_config_tlvs + }; + + g_mlo_test = false; + test_init(policy_config_request_send_cb, NULL, &ale); + + fail_unless(!map_send_policy_config_request(ale, &tlvs, MID_NA)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CHANNEL_PREFERENCE_QUERY # +########################################################################*/ +static void channel_preference_query_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_CHANNEL_PREFERENCE_QUERY, RELAY_INDICATOR_OFF, g_src_ifname, 0); +} + +START_TEST(test_channel_preference_query) +{ + map_ale_info_t *ale; + + g_mlo_test = false; + test_init(channel_preference_query_send_cb, NULL, &ale); + + fail_unless(!map_send_channel_preference_query(ale, MID_NA)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CHANNEL_SELECTION_REQUEST # +########################################################################*/ +static void channel_selection_request_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + map_channel_preference_tlv_t *c_tlv; + map_transmit_power_limit_tlv_t *p_tlv; + bool *all_radios = args; + int c_tlv_nr = *all_radios ? 2 : 1; + int idx; + bool twoG_ok = false, fiveG_ok = false; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_CHANNEL_SELECTION_REQUEST, RELAY_INDICATOR_OFF, g_src_ifname, c_tlv_nr + 1); + + fail_unless(count_tlvs_type(cmdu, TLV_TYPE_CHANNEL_PREFERENCE) == c_tlv_nr); + i1905_foreach_tlv_type_in_cmdu(TLV_TYPE_CHANNEL_PREFERENCE, c_tlv, cmdu, idx) { + if (!maccmp(c_tlv->radio_id, g_radio_id_2G)) { + twoG_ok = true; + } else if (!maccmp(c_tlv->radio_id, g_radio_id_5G)) { + fiveG_ok = true; + } + } + fail_unless(twoG_ok && (c_tlv_nr == 1 || fiveG_ok)); + + fail_unless(!!(p_tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_TRANSMIT_POWER_LIMIT, cmdu))); + fail_unless(!maccmp(p_tlv->radio_id, g_radio_id_2G)); + fail_unless(p_tlv->transmit_power_eirp == 20); +} + +START_TEST(test_channel_selection_request) +{ + map_ale_info_t *ale; + map_radio_info_t *radio; + map_chan_select_pref_type_t pref = {0}; + bool all_radios = true; + + g_mlo_test = false; + test_init(channel_selection_request_send_cb, &all_radios, &ale); + + /* Set transmit power limit for one radio */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + radio->tx_pwr_limit = 20; + + /* Set tx power limit for one radio */ + fail_unless(!!(radio = map_dm_get_radio(ale, g_radio_id_2G))); + radio->tx_pwr_limit = 20; + + /* Request for all radios */ + pref.ale = ale; + pref.radio = NULL; + pref.pref = MAP_CHAN_SEL_PREF_AGENT; + fail_unless(!map_send_channel_selection_request(&pref, MID_NA)); + + /* Request for one radio */ + all_radios = false; + pref.ale = ale; + pref.radio = radio; + pref.pref = MAP_CHAN_SEL_PREF_MERGED; + fail_unless(!map_send_channel_selection_request(&pref, MID_NA)); + + fail_unless(stub_i1905_get_send_nr() == 2); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CLIENT_CAPABILITY_QUERY # +########################################################################*/ +static void client_capability_query_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + map_client_info_tlv_t *tlv; + map_sta_info_t *sta = *(map_sta_info_t **)args; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_CLIENT_CAPABILITY_QUERY, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!(tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_CLIENT_INFO, cmdu))); + fail_unless(!maccmp(tlv->bssid, sta->bss->bssid)); + fail_unless(!maccmp(tlv->sta_mac, sta->mac)); +} + +START_TEST(test_client_capability_query) +{ + map_ale_info_t *ale; + map_sta_info_t *sta; + + g_mlo_test = false; + test_init(client_capability_query_send_cb, &sta, &ale); + + fail_unless(!!(sta = map_dm_get_sta_from_ale(ale, g_S10_mac))); + + fail_unless(!map_send_client_capability_query(sta, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_AP_METRICS_QUERY # +########################################################################*/ +static void ap_metrics_query_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + map_ap_metric_query_tlv_t *tlv; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_AP_METRICS_QUERY, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!(tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_AP_METRIC_QUERY, cmdu))); + fail_unless(tlv->bssids_nr == 2); + fail_unless(!maccmp(tlv->bssids[0], g_bssid_2G_fh)); + fail_unless(!maccmp(tlv->bssids[1], g_bssid_5G_fh)); +} + +START_TEST(test_ap_metrics_query) +{ + map_ale_info_t *ale; + mac_addr bssids[2]; + + g_mlo_test = false; + test_init(ap_metrics_query_send_cb, NULL, &ale); + + maccpy(bssids[0], g_bssid_2G_fh); + maccpy(bssids[1], g_bssid_5G_fh); + + fail_unless(!map_send_ap_metrics_query(ale, bssids, 2, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_ASSOC_STA_LINK_METRICS # +########################################################################*/ +static void assoc_sta_link_metrics_query_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + map_sta_mac_address_tlv_t *tlv; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_ASSOCIATED_STA_LINK_METRICS_QUERY, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!(tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_STA_MAC_ADDRESS, cmdu))); + fail_unless(!maccmp(tlv->sta_mac, g_S10_mac)); +} + +START_TEST(test_assoc_sta_link_metrics_query) +{ + map_ale_info_t *ale; + map_sta_info_t *sta; + + g_mlo_test = false; + test_init(assoc_sta_link_metrics_query_send_cb, NULL, &ale); + + fail_unless(!!(sta = map_dm_get_sta_from_ale(ale, g_S10_mac))); + + fail_unless(!map_send_assoc_sta_link_metrics_query(sta, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_UNASSOC_STA_LINK_METRICS_QUERY # +########################################################################*/ +static void unassoc_sta_link_metrics_query_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_UNASSOCIATED_STA_LINK_METRICS_QUERY, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_UNASSOCIATED_STA_LINK_METRICS_QUERY, cmdu)); +} + +START_TEST(test_unassoc_sta_link_metrics_query) +{ + map_ale_info_t *ale; + map_unassoc_sta_link_metrics_query_tlv_t tlv = {0}; + + g_mlo_test = false; + test_init(unassoc_sta_link_metrics_query_send_cb, NULL, &ale); + + fail_unless(!map_send_unassoc_sta_link_metrics_query(ale, &tlv, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_COMBINED_INFRASTRUCTURE_METRICS # +########################################################################*/ +static void combined_infrastructure_metrics_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_COMBINED_INFRASTRUCTURE_METRICS, RELAY_INDICATOR_OFF, g_src_ifname, 7); + + /* With current data: + 2 TLV_TYPE_RECEIVER_LINK_METRIC, 2 TLV_TYPE_TRANSMITTER_LINK_METRIC and 3 TLV_TYPE_AP_METRICS + */ + fail_unless(count_tlvs_type(cmdu, TLV_TYPE_TRANSMITTER_LINK_METRIC) == 2); + fail_unless(count_tlvs_type(cmdu, TLV_TYPE_RECEIVER_LINK_METRIC) == 2); + fail_unless(count_tlvs_type(cmdu, TLV_TYPE_AP_METRICS) == 3); +} + +START_TEST(test_combined_infrastructure_metrics) +{ + map_ale_info_t *ale; + + g_mlo_test = false; + test_init(combined_infrastructure_metrics_send_cb, NULL, &ale); + + /* TODO: add more data */ + + fail_unless(!map_send_combined_infrastructure_metrics(ale, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CLIENT_STEERING_REQUEST # +########################################################################*/ +static void client_steering_request_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + bool *mbo = args; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_CLIENT_STEERING_REQUEST, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + if (*mbo) { + map_profile2_steering_request_tlv_t *tlv; + + fail_unless(!!(tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_PROFILE2_STEERING_REQUEST, cmdu))); + + fail_unless(!maccmp(tlv->bssid, g_bssid_2G_fh)); + fail_unless(tlv->flag = MAP_STEERING_REQUEST_FLAG_MANDATE | MAP_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT); + fail_unless(tlv->disassociation_timer == 1234); + fail_unless(tlv->opportunity_wnd == 5678); + fail_unless(tlv->sta_macs_nr == 1); + fail_unless(tlv->target_bsss_nr == 1); + fail_unless(!maccmp(tlv->sta_macs[0], g_S10_mac)); + fail_unless(!maccmp(tlv->target_bsss[0].bssid, g_bssid_5G_fh)); + fail_unless(tlv->target_bsss[0].op_class == 121); + fail_unless(tlv->target_bsss[0].channel == 100); + fail_unless(tlv->target_bsss[0].reason == 3); + } else { + map_steering_request_tlv_t *tlv; + + fail_unless(!!(tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_STEERING_REQUEST, cmdu))); + + fail_unless(!maccmp(tlv->bssid, g_bssid_2G_fh)); + fail_unless(tlv->flag = MAP_STEERING_REQUEST_FLAG_MANDATE | MAP_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT); + fail_unless(tlv->disassociation_timer == 1234); + fail_unless(tlv->opportunity_wnd == 5678); + fail_unless(tlv->sta_macs_nr == 1); + fail_unless(tlv->target_bsss_nr == 1); + fail_unless(!maccmp(tlv->sta_macs[0], g_S10_mac)); + fail_unless(!maccmp(tlv->target_bsss[0].bssid, g_bssid_5G_fh)); + fail_unless(tlv->target_bsss[0].op_class == 121); + fail_unless(tlv->target_bsss[0].channel == 100); + } +} + +START_TEST(test_client_steering_request) +{ + map_ale_info_t *ale; + map_sta_info_t *sta; + map_steer_t steer = {0}; + bool mbo = false; + + g_mlo_test = false; + test_init(client_steering_request_send_cb, &mbo, &ale); + + fail_unless(!!(sta = map_dm_get_sta_from_ale(ale, g_S10_mac))); + + /* Fill steering request */ + maccpy(steer.bssid, g_bssid_2G_fh); + steer.disassociation_timer = 1234; + steer.opportunity_wnd = 5678; + steer.flags = MAP_STEERING_REQUEST_FLAG_MANDATE | MAP_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT; + steer.sta_bssid_nr = 1; + maccpy(steer.sta_bssid[0].sta_mac, sta->mac); + maccpy(steer.sta_bssid[0].target_bssid, g_bssid_5G_fh); + steer.sta_bssid[0].op_class = 121; + steer.sta_bssid[0].channel = 100; + steer.sta_bssid[0].reason = 3; + + /* No mbo -> P1 TLV */ + mbo = false; sta->sta_caps.mbo_support = false; + fail_unless(!map_send_client_steering_request(ale, &steer, MID_NA)); + + /* Mbo -> P2 TLV */ + mbo = true; sta->sta_caps.mbo_support = true; + fail_unless(!map_send_client_steering_request(ale, &steer, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 2); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CLIENT_ACL_REQUEST # +########################################################################*/ +static void client_acl_request_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_CLIENT_ASSOCIATION_CONTROL_REQUEST, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_CLIENT_ASSOCIATION_CONTROL_REQUEST, cmdu)); +} + +START_TEST(test_client_acl_request) +{ + map_ale_info_t *ale; + map_client_assoc_control_request_tlv_t tlv = {0}; + + g_mlo_test = false; + test_init(client_acl_request_send_cb, NULL, &ale); + + fail_unless(!map_send_client_acl_request(ale, &tlv, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_BEACON_METRICS_QUERY # +########################################################################*/ +static void beacon_metrics_query_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_BEACON_METRICS_QUERY, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_BEACON_METRICS_QUERY, cmdu)); +} + +START_TEST(test_beacon_metrics_query) +{ + map_ale_info_t *ale; + map_beacon_metrics_query_tlv_t tlv = {0}; + + g_mlo_test = false; + test_init(beacon_metrics_query_send_cb, NULL, &ale); + + fail_unless(!map_send_beacon_metrics_query(ale, &tlv, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_BACKHAUL_STEERING_REQUEST # +########################################################################*/ +static void backhaul_steering_request_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_BACKHAUL_STEERING_REQUEST, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_BACKHAUL_STEERING_REQUEST, cmdu)); +} + +START_TEST(test_backhaul_steering_request) +{ + map_ale_info_t *ale; + map_backhaul_steering_request_tlv_t tlv = {0}; + + g_mlo_test = false; + test_init(backhaul_steering_request_send_cb, NULL, &ale); + + fail_unless(!map_send_backhaul_steering_request(ale, &tlv, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_HIGHER_LAYER_DATA_MSG # +########################################################################*/ +static void higher_layer_data_msg_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + map_higher_layer_data_tlv_t *tlv; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_HIGHER_LAYER_DATA, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!(tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_HIGHER_LAYER_DATA, cmdu))); + fail_unless(tlv->protocol == 123); + fail_unless(!memcmp(tlv->payload, "test", 4)); + fail_unless(tlv->payload_len == 4); +} + +START_TEST(test_higher_layer_data_msg) +{ + map_ale_info_t *ale; + + g_mlo_test = false; + test_init(higher_layer_data_msg_send_cb, NULL, &ale); + + fail_unless(!map_send_higher_layer_data_msg(ale, 123, (uint8_t*)"test", 4, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CHANNEL_SCAN_REQUEST # +########################################################################*/ +static void channel_scan_request_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_CHANNEL_SCAN_REQUEST, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_CHANNEL_SCAN_REQUEST, cmdu)); +} + +START_TEST(test_channel_scan_request) +{ + map_ale_info_t *ale; + map_channel_scan_request_tlv_t tlv = {0}; + + g_mlo_test = false; + test_init(channel_scan_request_send_cb, NULL, &ale); + + fail_unless(!map_send_channel_scan_request(ale, &tlv, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CAC_REQUEST # +########################################################################*/ +static void cac_request_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_CAC_REQUEST, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_CAC_REQUEST, cmdu)); +} + +START_TEST(test_cac_request) +{ + map_ale_info_t *ale; + map_cac_request_tlv_t tlv = {0}; + + g_mlo_test = false; + test_init(cac_request_send_cb, NULL, &ale); + + /* Fill in something valid as this is checked */ + tlv.radios_nr = 1; + maccpy(tlv.radios[0].radio_id, g_radio_id_5G); + tlv.radios[0].cac_method = MAP_CAC_METHOD_MIMO_DIM_REDUCED; + tlv.radios[0].op_class = 121; + tlv.radios[0].channel = 200; /* Invalid */ + fail_unless(map_send_cac_request(ale, &tlv, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 0); + + tlv.radios[0].channel = 100; /* Valid */ + fail_unless(!map_send_cac_request(ale, &tlv, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CAC_TERMINATION # +########################################################################*/ +static void cac_termination_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_CAC_TERMINATION, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_CAC_TERMINATION, cmdu)); +} + +START_TEST(test_cac_termination) +{ + map_ale_info_t *ale; + map_cac_termination_tlv_t tlv = {0}; + + g_mlo_test = false; + test_init(cac_termination_send_cb, NULL, &ale); + + fail_unless(!map_send_cac_termination(ale, &tlv, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_PROXIED_ENCAP_DPP # +########################################################################*/ +static void proxied_encap_dpp_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + bool *chirp = args; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_PROXIED_ENCAP_DPP, RELAY_INDICATOR_OFF, g_src_ifname, *chirp ? 2 : 1); + + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_1905_ENCAP_DPP, cmdu)); + if (*chirp) { + fail_unless(!!i1905_get_tlv_from_cmdu(TLV_TYPE_DPP_CHIRP_VALUE, cmdu)); + } +} + +START_TEST(test_proxied_encap_dpp) +{ + map_ale_info_t *ale; + map_1905_encap_dpp_tlv_t encap_tlv = {0}; + map_dpp_chirp_value_tlv_t chirp_tlv = {0}; + bool chirp = false; + + g_mlo_test = false; + test_init(proxied_encap_dpp_send_cb, &chirp, &ale); + + fail_unless(!map_send_proxied_encap_dpp(ale, &encap_tlv, NULL, MID_NA)); + chirp = true; + fail_unless(!map_send_proxied_encap_dpp(ale, &encap_tlv, &chirp_tlv, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 2); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_DPP_CCE_INDICATION # +########################################################################*/ +static void dpp_cce_indication_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + map_dpp_cce_indication_tlv_t *tlv; + uint8_t *advertise = args; + + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_DPP_CCE_INDICATION, RELAY_INDICATOR_OFF, g_src_ifname, 1); + + fail_unless(!!(tlv = i1905_get_tlv_from_cmdu(TLV_TYPE_DPP_CCE_INDICATION, cmdu))); + fail_unless(tlv->advertise == *advertise); +} + +START_TEST(test_dpp_cce_indication) +{ + map_ale_info_t *ale; + uint8_t advertise = 1; + + g_mlo_test = false; + test_init(dpp_cce_indication_send_cb, &advertise, &ale); + fail_unless(!map_send_dpp_cce_indication(ale, advertise, MID_NA)); + advertise = 0; + fail_unless(!map_send_dpp_cce_indication(ale, advertise, MID_NA)); + fail_unless(stub_i1905_get_send_nr() == 2); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_BACKHAUL_STA_CAPABILITY_QUERY # +########################################################################*/ +static void backhaul_sta_capability_query_send_cb(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args) +{ + check_cmdu(dmac, g_al_mac, cmdu, CMDU_TYPE_MAP_BACKHAUL_STA_CAPABILITY_QUERY, RELAY_INDICATOR_OFF, g_src_ifname, 0); +} + +START_TEST(test_backhaul_sta_capability_query) +{ + map_ale_info_t *ale; + + g_mlo_test = false; + test_init(backhaul_sta_capability_query_send_cb, NULL, &ale); + + fail_unless(!map_send_backhaul_sta_capability_query(ale, MID_NA)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_RAW # +########################################################################*/ +static void raw_cb(char *ifname, mac_addr dmac, mac_addr smac, uint16_t eth_type, uint8_t *data, uint16_t data_len, void *args) +{ + fail_unless(!strcmp(ifname, "eth0")); + fail_unless(!maccmp(dmac, g_bssid_2G_fh)); + fail_unless(!maccmp(smac, g_bssid_5G_fh)); + fail_unless(eth_type = 0x1234); + fail_unless(data_len == 4); + fail_unless(!memcmp(data, (uint8_t[]){0x00,0x01,0x02,0x03}, 4)); +} + +START_TEST(test_raw) +{ + g_mlo_test = false; + test_init(NULL, NULL, NULL); + + stub_i1905_register_raw_send_cb(raw_cb, NULL); + + fail_unless(!map_send_raw("eth0", g_bssid_2G_fh, g_bssid_5G_fh, 0x1234, (uint8_t[]){0x00,0x01,0x02,0x03}, 4)); + fail_unless(stub_i1905_get_send_nr() == 1); + + test_fini(); +} +END_TEST + + +const char *test_suite_name = "cmdu_tx"; +test_case_t test_cases[] = { + /* lldp */ + TEST("bridge_discovery", test_bridge_discovery ), + /* 1905.1 */ + TEST("topology_discovery", test_topology_discovery ), + TEST("topology_query", test_topology_query ), + TEST("topology_response", test_topology_response ), + TEST("link_metric_query", test_link_metric_query ), + TEST("link_metric_response", test_link_metric_response ), + TEST("link_metric_response_error", test_link_metric_response_error ), + TEST("autoconfig_search", test_autoconfig_search ), + TEST("autoconfig_response", test_autoconfig_response ), + TEST("autoconfig_wsc_m2", test_autoconfig_wsc_m2 ), + TEST("autoconfig_wsc_m2_mld", test_autoconfig_wsc_m2_mld ), + TEST("autoconfig_renew", test_autoconfig_renew ), + TEST("autoconfig_renew_ucast", test_autoconfig_renew_ucast ), + TEST("vendor_specific", test_vendor_specific ), + TEST("vendor_specific_mult_tlvs", test_vendor_specific_mult_tlvs ), + /* MAP R1 */ + TEST("ack", test_ack ), + TEST("ack_sta_error", test_ack_sta_error ), + TEST("ap_capability_query", test_ap_capability_query ), + TEST("test_policy_config_request", test_policy_config_request ), + TEST("channel_preference_query", test_channel_preference_query ), + TEST("channel_selection_request", test_channel_selection_request ), + TEST("client_capability_query", test_client_capability_query ), + TEST("ap_metrics_query", test_ap_metrics_query ), + TEST("assoc_sta_link_metrics_query", test_assoc_sta_link_metrics_query ), + TEST("unassoc_sta_link_metrics_query", test_unassoc_sta_link_metrics_query ), + TEST("combined_infrastructure_metrics", test_combined_infrastructure_metrics ), + TEST("client_steering_request", test_client_steering_request ), + TEST("client_acl_request", test_client_acl_request ), + TEST("beacon_metrics_query", test_beacon_metrics_query ), + TEST("backhaul_steering_request", test_backhaul_steering_request ), + TEST("higher_layer_data_msg", test_higher_layer_data_msg ), + /* MAP R2 */ + TEST("channel_scan_request", test_channel_scan_request ), + TEST("cac_request", test_cac_request ), + TEST("cac_termination", test_cac_termination ), + TEST("backhaul_sta_capability_query", test_backhaul_sta_capability_query ), + /* MAP R3 */ + TEST("proxied_encap_dpp", test_proxied_encap_dpp ), + TEST("dpp_cce_indication", test_dpp_cce_indication ), + /* RAW */ + TEST("raw", test_raw ), + { NULL}, +}; diff --git a/source/test/controller/test_emex.c b/source/test/controller/test_emex.c new file mode 100644 index 0000000..e35506c --- /dev/null +++ b/source/test/controller/test_emex.c @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2019-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "test.h" + +#include "map_ctrl_emex_tlv_handler.h" + +#include "1905_cmdus.h" +#include "1905_tlvs.h" +#include "map_tlvs.h" +#include "map_data_model.h" +#include "map_config.h" + +/*####################################################################### +# DEFINES # +########################################################################*/ +#define VARS \ + map_ale_info_t *ale = NULL; \ + UNUSED packet_t *packet = NULL; \ + UNUSED i1905_cmdu_t *cmdu = NULL; + +#define INIT \ + fail_unless(!map_dm_init()); \ + fail_unless(!map_emex_init()); \ + fail_unless(!!(ale = map_dm_create_ale(g_al_mac))); + +#define READ_PARSE(file) \ + fail_unless(!!(packet = pcap_read_first_packet(DATA_DIR "/" file))); \ + fail_unless(!!(cmdu = parse_cmdu(packet))); \ + process_emex_tlv(ale, cmdu); \ + free_1905_CMDU_structure(cmdu); \ + free(packet); + +#define CLEANUP \ + map_dm_remove_ale(ale); \ + map_emex_fini(); \ + map_dm_fini(); + +/*####################################################################### +# GLOBALS # +########################################################################*/ +static mac_addr g_al_mac = {0x02, 0x01, 0x02, 0x03, 0x04, 0x05}; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static i1905_cmdu_t *parse_cmdu(packet_t *p) +{ + uint8_t *streams[2] = {p->data + sizeof(eth_hdr_t), NULL}; + uint16_t lengths[2] = {p->len, 0}; + + fail_unless(p->len >= sizeof(eth_hdr_t)); + + return parse_1905_CMDU_from_packets(streams, lengths); +} + +static void process_emex_tlv(map_ale_info_t *ale, i1905_cmdu_t *cmdu) +{ + uint8_t *tlv; + size_t idx; + size_t count = 0; + + i1905_foreach_tlv_type_in_cmdu(TLV_TYPE_VENDOR_SPECIFIC, tlv, cmdu, idx) { + i1905_vendor_specific_tlv_t *t = (i1905_vendor_specific_tlv_t *)tlv; + + count++; + fail_unless(map_emex_is_valid_tlv(t)); + fail_unless(map_emex_parse_tlv(ale, t) == 1); + } + fail_unless(count == 1); +} + +/*####################################################################### +# TEST_0002_FEATURE_PROFILE_TLV # +########################################################################*/ +START_TEST(test_0002_feature_profile_tlv) +{ + VARS + INIT + READ_PARSE("emex_tlv_0002_feature_profile.pcap"); + + /* Check datamodel */ + fail_unless(ale->emex.enabled == true); + fail_unless(ale->emex.feature_profile.agent_version == 0x01020304); + fail_unless(ale->emex.feature_profile.feature_count == 2); + fail_unless(ale->emex.feature_profile.feature_list[0].id == 3); + fail_unless(ale->emex.feature_profile.feature_list[0].version == 1); + fail_unless(ale->emex.feature_profile.feature_list[1].id == 9); + fail_unless(ale->emex.feature_profile.feature_list[1].version == 1); + + /* With one agent, common feature list should be equal */ + map_emex_common_feature_list_t *f = controller_get_emex_common_feature_list(); + fail_unless(f->feature_count == 2); + fail_unless(f->feature_list[0].id == 3); + fail_unless(f->feature_list[0].version == 1); + fail_unless(f->feature_list[1].id == 9); + fail_unless(f->feature_list[1].version == 1); + + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_0003_DEVICE_INFO_TLV # +########################################################################*/ +START_TEST(test_0003_device_info_tlv) +{ + VARS + INIT + READ_PARSE("emex_tlv_0003_device_info.pcap"); + + /* Check datamodel */ + fail_unless(!memcmp(ale->emex.device_info.client_id, (char*)"test", 4)); + fail_unless(!memcmp(ale->emex.device_info.client_secret, (char*)"topsecret", 9)); + fail_unless(ale->emex.device_info.boot_id == 0x6FEF8CCF); + fail_unless(ale->emex.device_info.product_class & EMEX_PRODUCT_CLASS_EXTENDER); + fail_unless(ale->emex.device_info.device_role & EMEX_DEVICE_ROLE_CONTROLLER); + + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_0004_DEVICE_METRICS_TLV # +########################################################################*/ +START_TEST(test_0004_device_metrics_tlv) +{ + VARS + INIT + READ_PARSE("emex_tlv_0004_device_metrics.pcap"); + + /* Check datamodel */ + fail_unless(ale->emex.device_metrics.uptime == 60132); + fail_unless(ale->emex.device_metrics.cpu_load == 0); + fail_unless(ale->emex.device_metrics.cpu_temp == 53); + fail_unless(ale->emex.device_metrics.mem_total == 148500); + fail_unless(ale->emex.device_metrics.mem_free == 40304); + fail_unless(ale->emex.device_metrics.mem_cached == 64612); + fail_unless(ale->emex.radios.count == 2); + fail_unless(!memcmp(ale->emex.radios.info[0].id, (mac_addr){0xf4, 0x17, 0xb8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(ale->emex.radios.info[0].temp == 45); + fail_unless(!memcmp(ale->emex.radios.info[1].id, (mac_addr){0xf4, 0x17, 0xb8, 0x86, 0x57, 0x6A}, sizeof(mac_addr))); + fail_unless(ale->emex.radios.info[1].temp == 50); + + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_000F_ETH_INTERFACES_TLV # +########################################################################*/ +START_TEST(test_000F_eth_interfaces_tlv) +{ + VARS + INIT + READ_PARSE("emex_tlv_000F_eth_interfaces.pcap"); + + map_emex_eth_iface_list_t *list = &ale->emex.eth_iface_list; + mac_addr mac = {0xa0, 0x2d, 0x13, 0x3b, 0xf1, 0x8f}; + + /* Check datamodel */ + fail_unless(list->iface_nr == 3); + fail_unless(list->ifaces[0].port_id == 0); + fail_unless(!maccmp(list->ifaces[0].mac, mac)); + fail_unless(!strcmp(list->ifaces[0].name, "eth0")); + fail_unless(list->ifaces[0].admin_state == 1); + fail_unless(list->ifaces[0].oper_state == 1); + fail_unless(list->ifaces[0].full_duplex == 1); + fail_unless(list->ifaces[0].supported_link_speed == 1000); + fail_unless(list->ifaces[0].link_speed == 1000); + + fail_unless(list->ifaces[1].port_id == 1); + fail_unless(!maccmp(list->ifaces[1].mac, mac)); + fail_unless(!strcmp(list->ifaces[1].name, "eth1")); + fail_unless(list->ifaces[1].admin_state == 1); + fail_unless(list->ifaces[1].oper_state == 1); + fail_unless(list->ifaces[1].full_duplex == 0); + fail_unless(list->ifaces[1].supported_link_speed == 1000); + fail_unless(list->ifaces[1].link_speed == 10); + + fail_unless(list->ifaces[2].port_id == 2); + fail_unless(!maccmp(list->ifaces[2].mac, mac)); + fail_unless(!strcmp(list->ifaces[2].name, "eth2")); + fail_unless(list->ifaces[2].admin_state == 1); + fail_unless(list->ifaces[2].oper_state == 0); + fail_unless(list->ifaces[2].full_duplex == 0); + fail_unless(list->ifaces[2].supported_link_speed == 2500); + fail_unless(list->ifaces[2].link_speed == 0); + + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_0010_ETH_STATS_V2_TLV # +########################################################################*/ +START_TEST(test_0010_eth_stats_v2_tlv) +{ + VARS + INIT + + /* Read first before adding interfaces to make sure nothing goes wrong... */ + READ_PARSE("emex_tlv_0010_eth_stats_v2.pcap"); + READ_PARSE("emex_tlv_000F_eth_interfaces.pcap"); + READ_PARSE("emex_tlv_0010_eth_stats_v2.pcap"); + + map_emex_eth_iface_list_t *list = &ale->emex.eth_iface_list; + fail_unless(list->iface_nr == 3); + fail_unless(list->supported_stats_mask == 0x3F01); + + /* Interface 1 and 2 have stats, interface 0 has no stats */ + map_emex_eth_stats_t zero_stats = { 0 }; + fail_unless(!memcmp(&list->ifaces[0].stats, &zero_stats, sizeof(map_emex_eth_stats_t))); + + fail_unless(list->ifaces[1].stats.tx_bytes == 47313920); + fail_unless(list->ifaces[1].stats.rx_bytes == 302023680); + fail_unless(list->ifaces[1].stats.tx_packets == 149479); + fail_unless(list->ifaces[1].stats.rx_packets == 376394); + fail_unless(list->ifaces[1].stats.tx_errors == 1); + fail_unless(list->ifaces[1].stats.rx_errors == 2); + fail_unless(list->ifaces[1].stats.tx_bcast_bytes == 0); + fail_unless(list->ifaces[1].stats.rx_bcast_bytes == 0); + fail_unless(list->ifaces[1].stats.tx_bcast_packets == 150); + fail_unless(list->ifaces[1].stats.rx_bcast_packets == 14046); + fail_unless(list->ifaces[1].stats.tx_mcast_bytes == 3072); + fail_unless(list->ifaces[1].stats.rx_mcast_bytes == 4096); + fail_unless(list->ifaces[1].stats.tx_mcast_packets == 17716); + fail_unless(list->ifaces[1].stats.rx_mcast_packets == 93497); + fail_unless(list->ifaces[1].stats.tx_ucast_bytes == 0); + fail_unless(list->ifaces[1].stats.rx_ucast_bytes == 0); + fail_unless(list->ifaces[1].stats.tx_ucast_packets == 149479 - 150 - 17716); + fail_unless(list->ifaces[1].stats.rx_ucast_packets == 376394 - 14046 - 93497); + + fail_unless(list->ifaces[2].stats.tx_bytes == 20496382301481984ULL); + fail_unless(list->ifaces[2].stats.tx_packets == 38780996750012ULL); + + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_0011_ETH_NON1905_NB_DEVS_TLV # +########################################################################*/ +START_TEST(test_0011_eth_non1905_nb_devs_tlv) +{ + VARS + INIT + + /* Read first before adding interfaces to make sure nothing goes wrong... */ + READ_PARSE("emex_tlv_0011_eth_non1905_nb_devs.pcap"); + READ_PARSE("emex_tlv_000F_eth_interfaces.pcap"); + READ_PARSE("emex_tlv_0011_eth_non1905_nb_devs.pcap"); + + map_emex_eth_iface_list_t *list = &ale->emex.eth_iface_list; + fail_unless(list->iface_nr == 3); + fail_unless(list->ifaces[0].non_i1905_neighbor_macs_nr == 2); + fail_unless(!memcmp(list->ifaces[0].non_i1905_neighbor_macs[0], (uint8_t []) {0xB0, 0x7B, 0x25, 0x79, 0xD4, 0x85}, 6)); + fail_unless(!memcmp(list->ifaces[0].non_i1905_neighbor_macs[1], (uint8_t []) {0xD0, 0x67, 0xE5, 0x30, 0xFE, 0xCA}, 6)); + fail_unless(list->ifaces[1].non_i1905_neighbor_macs_nr == 1); + fail_unless(!memcmp(list->ifaces[1].non_i1905_neighbor_macs[0], (uint8_t []) {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}, 6)); + fail_unless(list->ifaces[2].non_i1905_neighbor_macs_nr == 0); + + /* Test if macs are removed when receiving topology response without TLV */ + i1905_cmdu_t cmdu2 = {.message_type = CMDU_TYPE_TOPOLOGY_RESPONSE}; + map_emex_handle_cmdu_pre(ale, &cmdu2); + map_emex_handle_cmdu_post(ale, &cmdu2); + + fail_unless(list->ifaces[0].non_i1905_neighbor_macs_nr == 0); + fail_unless(list->ifaces[1].non_i1905_neighbor_macs_nr == 0); + fail_unless(list->ifaces[2].non_i1905_neighbor_macs_nr == 0); + + /* Reload to test cleanup */ + READ_PARSE("emex_tlv_0011_eth_non1905_nb_devs.pcap"); + fail_unless(list->ifaces[0].non_i1905_neighbor_macs_nr == 2); + + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_0012_ETH_1905_NB_DEVS_TLV # +########################################################################*/ +START_TEST(test_0012_eth_1905_nb_devs_tlv) +{ + VARS + INIT + + /* Read first before adding interfaces to make sure nothing goes wrong... */ + READ_PARSE("emex_tlv_0012_eth_1905_nb_devs.pcap"); + READ_PARSE("emex_tlv_000F_eth_interfaces.pcap"); + READ_PARSE("emex_tlv_0012_eth_1905_nb_devs.pcap"); + + map_emex_eth_iface_list_t *list = &ale->emex.eth_iface_list; + fail_unless(list->iface_nr == 3); + fail_unless(list->ifaces[0].i1905_neighbor_macs_nr == 0); + fail_unless(list->ifaces[1].i1905_neighbor_macs_nr == 1); + fail_unless(!memcmp(list->ifaces[1].i1905_neighbor_macs[0], (uint8_t []) {0xA2, 0xB5, 0x3C, 0x3F, 0xC7, 0x71}, 6)); + fail_unless(list->ifaces[2].i1905_neighbor_macs_nr == 2); + fail_unless(!memcmp(list->ifaces[2].i1905_neighbor_macs[0], (uint8_t []) {0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF}, 6)); + fail_unless(!memcmp(list->ifaces[2].i1905_neighbor_macs[1], (uint8_t []) {0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF}, 6)); + + /* Test if macs are removed when receiving topology response without TLV */ + i1905_cmdu_t cmdu2 = {.message_type = CMDU_TYPE_TOPOLOGY_RESPONSE}; + map_emex_handle_cmdu_pre(ale, &cmdu2); + map_emex_handle_cmdu_post(ale, &cmdu2); + + fail_unless(list->ifaces[0].i1905_neighbor_macs_nr == 0); + fail_unless(list->ifaces[1].i1905_neighbor_macs_nr == 0); + fail_unless(list->ifaces[2].i1905_neighbor_macs_nr == 0); + + /* Reload to test cleanup */ + READ_PARSE("emex_tlv_0012_eth_1905_nb_devs.pcap"); + fail_unless(list->ifaces[2].i1905_neighbor_macs_nr == 2); + + CLEANUP +} +END_TEST + +const char *test_suite_name = "emex"; +test_case_t test_cases[] = { + TEST("0002_feature_profile_tlv", test_0002_feature_profile_tlv ), + TEST("0003_device_info_tlv", test_0003_device_info_tlv ), + TEST("0004_device_metrics_tlv", test_0004_device_metrics_tlv ), + TEST("000F_eth_interfaces_tlv", test_000F_eth_interfaces_tlv ), + TEST("0010_eth_stats_v2_tlv", test_0010_eth_stats_v2_tlv ), + TEST("0011_eth_non1905_nb_devs_tlv", test_0011_eth_non1905_nb_devs_tlv ), + TEST("0012_eth_1905_nb_devs_tlv", test_0012_eth_1905_nb_devs_tlv ), + TEST_CASES_END +}; diff --git a/source/test/controller/test_tlv_helper.c b/source/test/controller/test_tlv_helper.c new file mode 100644 index 0000000..ddb4133 --- /dev/null +++ b/source/test/controller/test_tlv_helper.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2021-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "test.h" + +#include "map_ctrl_tlv_helper.h" + +/*####################################################################### +# DEFINES # +########################################################################*/ + +/*####################################################################### +# GLOBALS # +########################################################################*/ +static mac_addr g_radio_id = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static void test_init(void) +{ + fail_unless(!map_info_init()); +} + +static void test_fini(void) +{ + map_info_fini(); +} + +/*####################################################################### +# TEST_FILL_CHANNEL_SCAN_REQUEST_TLV # +########################################################################*/ +START_TEST(test_fill_channel_scan_request_tlv) +{ + map_channel_scan_request_tlv_t tlv = {0}; + map_radio_info_t radio = {0}; + map_op_class_t op_classes[5] = {0}; + map_channel_set_t channels; + int i; + + test_init(); + + /* Set radio data */ + maccpy(radio.radio_id, g_radio_id); + + /* Set channels 36, 40, 44, 48, 52, 56, 60, 100, 104 */ + for (i = 36; i <= 60; i++) { + map_cs_set(&radio.ctl_channels, i); + } + map_cs_set(&radio.ctl_channels, 100); + map_cs_set(&radio.ctl_channels, 104); + + /* Scan caps */ + op_classes[0].op_class = 115; /* 36, 40, 44, 48 */ + map_cs_set(&op_classes[0].channels, 36); + map_cs_set(&op_classes[0].channels, 40); + map_cs_set(&op_classes[0].channels, 44); + map_cs_set(&op_classes[0].channels, 48); + op_classes[1].op_class = 118; /* 52, 56, 60, 64 */ + op_classes[2].op_class = 121; /* 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144 */ + map_cs_set(&op_classes[2].channels, 100); + map_cs_set(&op_classes[2].channels, 104); + map_cs_set(&op_classes[2].channels, 108); + map_cs_set(&op_classes[2].channels, 112); + op_classes[3].op_class = 124; /* 149, 153, 157, 161 */ + op_classes[4].op_class = 128; /* 80MHz */ + + radio.scan_caps.op_class_list.op_classes_nr = ARRAY_SIZE(op_classes); + radio.scan_caps.op_class_list.op_classes = op_classes; + + free_1905_TLV_structure2((uint8_t *)&tlv); + + + /* No fresh scan */ + map_fill_channel_scan_request_tlv(&tlv, &radio, false, NULL); + + fail_unless(tlv.tlv_type == TLV_TYPE_CHANNEL_SCAN_REQUEST); + fail_unless(tlv.fresh_scan_performed == 0); + fail_unless(tlv.radios_nr == 1); + fail_unless(tlv.radios[0].op_classes_nr == 0); + fail_unless(!maccmp(tlv.radios[0].radio_id, g_radio_id)); + + free_1905_TLV_structure2((uint8_t *)&tlv); + + + /* Fresh scan all channels */ + map_fill_channel_scan_request_tlv(&tlv, &radio, true, NULL); + + fail_unless(tlv.tlv_type == TLV_TYPE_CHANNEL_SCAN_REQUEST); + fail_unless(tlv.fresh_scan_performed == 1); + fail_unless(tlv.radios_nr == 1); + fail_unless(tlv.radios[0].op_classes_nr == 4); + fail_unless(!maccmp(tlv.radios[0].radio_id, g_radio_id)); + fail_unless( tlv.radios[0].op_classes[0].op_class == 115); + fail_unless(map_cs_nr( &tlv.radios[0].op_classes[0].channels) == 4); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[0].channels, 36)); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[0].channels, 40)); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[0].channels, 44)); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[0].channels, 48)); + fail_unless( tlv.radios[0].op_classes[1].op_class == 118); + fail_unless(map_cs_nr( &tlv.radios[0].op_classes[1].channels) == 0); + fail_unless( tlv.radios[0].op_classes[2].op_class == 121); + fail_unless(map_cs_nr( &tlv.radios[0].op_classes[2].channels) == 4); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[2].channels, 100)); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[2].channels, 104)); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[2].channels, 108)); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[2].channels, 112)); + fail_unless( tlv.radios[0].op_classes[3].op_class == 124); + + free_1905_TLV_structure2((uint8_t *)&tlv); + + + /* Fresh scan with channels */ + map_cs_unset_all(&channels); + map_cs_set(&channels, 36); + map_cs_set(&channels, 40); + map_cs_set(&channels, 52); + map_cs_set(&channels, 56); + map_cs_set(&channels, 100); + map_cs_set(&channels, 104); + + map_fill_channel_scan_request_tlv(&tlv, &radio, true, &channels); + + fail_unless(tlv.tlv_type == TLV_TYPE_CHANNEL_SCAN_REQUEST); + fail_unless(tlv.fresh_scan_performed == 1); + fail_unless(tlv.radios_nr == 1); + fail_unless(tlv.radios[0].op_classes_nr == 3); + fail_unless(!maccmp(tlv.radios[0].radio_id, g_radio_id)); + fail_unless( tlv.radios[0].op_classes[0].op_class == 115); + fail_unless(map_cs_nr( &tlv.radios[0].op_classes[0].channels) == 2); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[0].channels, 36)); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[0].channels, 40)); + fail_unless( tlv.radios[0].op_classes[1].op_class == 118); + fail_unless(map_cs_nr( &tlv.radios[0].op_classes[1].channels) == 2); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[1].channels, 52)); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[1].channels, 56)); + fail_unless( tlv.radios[0].op_classes[2].op_class == 121); + fail_unless(map_cs_nr( &tlv.radios[0].op_classes[2].channels) == 2); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[2].channels, 100)); + fail_unless(map_cs_is_set(&tlv.radios[0].op_classes[2].channels, 104)); + + free_1905_TLV_structure2((uint8_t *)&tlv); + + test_fini(); +} +END_TEST + + +const char *test_suite_name = "tlv_helper"; +test_case_t test_cases[] = { + TEST("fill_channel_scan_request_tlv", test_fill_channel_scan_request_tlv ), + TEST_CASES_END +}; diff --git a/source/test/controller/test_utils.c b/source/test/controller/test_utils.c new file mode 100644 index 0000000..e4fae7a --- /dev/null +++ b/source/test/controller/test_utils.c @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2021-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "test.h" + +#include "map_ctrl_utils.h" + +/*####################################################################### +# DEFINES # +########################################################################*/ +#define Z_1 0 +#define Z_2 Z_1,Z_1 +#define Z_4 Z_2,Z_2 +#define Z_8 Z_4,Z_4 +#define Z_16 Z_8,Z_8 + +#define H08_1 0x08 +#define H08_2 H08_1,H08_1 +#define H08_4 H08_2,H08_2 +#define H08_8 H08_4,H08_4 +#define H08_16 H08_8,H08_8 + +#define H22_1 0x22 +#define H22_2 H22_1,H22_1 +#define H22_4 H22_2,H22_2 +#define H22_8 H22_4,H22_4 +#define H22_16 H22_8,H22_8 + +#define MERGE_OP_CLASS_LIST(m, c, a, b, d, r) do { \ + log_test_i("---- TEST ----"); \ + dump_op_class_list(#c, c); \ + dump_op_class_list(#a, a); \ + dump_op_class_list(#b, b); \ + dump_op_class_list(#d, d); \ + fail_unless(!map_merge_pref_op_class_list(m, c, a, b, d)); \ + dump_op_class_list("merged", m); \ + dump_op_class_list("expect", r); \ + fail_unless(compare_op_class_list(m, r)); \ + free((m)->op_classes); \ +} while(0) + +/*####################################################################### +# GLOBALS # +########################################################################*/ +/* Cap list contains NOT allowed channels. Pref list contains allowed channels */ +static map_op_class_t g_op_cap[] = {{.op_class = 81, .channels = {.nr = 2, .channels = {Z_1, 0x30}}}, /* 12, 13 */ + {.op_class = 115, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 116, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 117, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 118, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 119, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 120, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 121, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 122, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 123, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 124, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 125, .channels = {.nr = 2, .channels = {Z_16,Z_4,Z_1,0x20,0x02}}}, /* 173, 177 */ + {.op_class = 126, .channels = {.nr = 1, .channels = {Z_16,Z_4,Z_1,0x20}}}, /* 173 */ + {.op_class = 127, .channels = {.nr = 1, .channels = {Z_16,Z_4,Z_2,0x02}}}, /* 177 */ + {.op_class = 128, .channels = {.nr = 1, .channels = {Z_16,Z_4,Z_1,0x08}}}, /* 171 */ + {.op_class = 129, .channels = {.nr = 1, .channels = {Z_16,Z_4,0x08}}}, /* 163 */ + {.op_class = 131, .channels = {.nr = 35, .channels = {Z_8,Z_4,H22_16,H22_1,0x02}}}, /* 6G_EU: 97, 101, ..., 233 */ + {.op_class = 132, .channels = {.nr = 17, .channels = {Z_8,Z_4,H08_16,H08_1}}}, /* 6G_EU: 99, 107, ..., 227 */ + {.op_class = 133, .channels = {.nr = 8, .channels = {Z_8,Z_4,0x80,Z_1,0x80,Z_1,0x80,Z_1,0x80,Z_1,0x80,Z_1,0x80,Z_1,0x80,Z_1,0x80}}}, /* 6G_EU: 103, 119, ..., 215 */ + {.op_class = 134, .channels = {.nr = 4, .channels = {Z_8,Z_4,Z_1,0x80,Z_2,Z_1,0x80,Z_2,Z_1,0x80,Z_2,Z_1,0x80}}}, /* 6G_EU: 111, 143, 175, 207 */ + {.op_class = 137, .channels = {.nr = 4, .channels = {Z_8,Z_2,Z_1,0x80,Z_2,Z_1,0x80,Z_2,Z_1,0x80,Z_2,Z_1,0x80}}}, /* 6G_EU: 95, 127, 159, 191 */ + }; + +static map_op_class_list_t g_opl_cap = {.op_classes = g_op_cap, .op_classes_nr = ARRAY_SIZE(g_op_cap )}; + +static map_op_class_list_t g_opl_in_1a = {.op_classes = NULL, .op_classes_nr = 0}; +static map_op_class_list_t g_opl_in_1b = {.op_classes = NULL, .op_classes_nr = 0}; +static map_op_class_list_t g_opl_merged_1 = {.op_classes = NULL, .op_classes_nr = 0}; + +static map_op_class_t g_op_in_2a[] = {{.op_class = 115, .pref = 0, .channels = {.nr = 2, .channels = {Z_4,0x10,0x01}}}, /* 36, 40 */ + {.op_class = 118, .pref = 0, .channels = {.nr = 4, .channels = {Z_4,Z_2,0x10,0x11,0x01}}}, /* 52, 56, 60, 64 */ + {.op_class = 121, .pref = 0, .channels = {.nr = 2, .channels = {Z_16,Z_1,0x10,0x01}}}, /* 140, 144 */ + {.op_class = 116, .pref = 0, .channels = {.nr = 2, .channels = {Z_4,0x10,0x10}}}, /* 36, 44 */ + {.op_class = 119, .pref = 0, .channels = {.nr = 2, .channels = {Z_4,Z_2,0x10,0x10}}}, /* 52, 60 */ + {.op_class = 122, .pref = 0, .channels = {.nr = 1, .channels = {Z_16,Z_1,0x10}}}, /* 140 */ + {.op_class = 117, .pref = 0, .channels = {.nr = 2, .channels = {Z_4,Z_1,0x01,0x01}}}, /* 40, 48 */ + {.op_class = 120, .pref = 0, .channels = {.nr = 2, .channels = {Z_4,Z_2,Z_1,0x01,0x01}}}, /* 56, 64 */ + {.op_class = 123, .pref = 0, .channels = {.nr = 1, .channels = {Z_16,Z_2,0x01}}}, /* 144 */ + {.op_class = 128, .pref = 0, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 129, .pref = 0, .channels = {.nr = 1, .channels = {Z_4,Z_2,0x04}}}, /* 50 */ + {.op_class = 131, .pref = 0, .channels = {.nr = 2, .channels = {0x22}}}, /* 6G: 1, 5 */ + {.op_class = 132, .pref = 0, .channels = {.nr = 2, .channels = {0x08,0x08}}}, /* 6G: 3, 11 */ + {.op_class = 133, .pref = 0, .channels = {.nr = 2, .channels = {0x80,Z_1,0x80}}}, /* 6G: 7, 23 */ + {.op_class = 134, .pref = 0, .channels = {.nr = 2, .channels = {Z_1,0x80,Z_2,Z_1,0x80}}}, /* 6G: 15, 47 */ + {.op_class = 137, .pref = 0, .channels = {.nr = 1, .channels = {Z_2,Z_1,0x80}}}, /* 6G: 31 */ + }; + +static map_op_class_t g_op_in_2b[] = {{.op_class = 118, .pref = 14, .channels = {.nr = 1, .channels = {Z_4,Z_2,0x10}}}, /* 52 */ + {.op_class = 118, .pref = 14, .channels = {.nr = 1, .channels = {Z_4,Z_2,Z_1,0x01}}}, /* 56 */ + {.op_class = 118, .pref = 14, .channels = {.nr = 1, .channels = {Z_4,Z_2,Z_1,0x10}}}, /* 60 */ + {.op_class = 118, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,0x01}}}, /* 64 */ + {.op_class = 121, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,Z_4,0x10}}}, /* 100 */ + {.op_class = 121, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,Z_4,Z_1,0x01}}}, /* 104 */ + {.op_class = 121, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,Z_4,Z_1,0x10}}}, /* 108 */ + {.op_class = 121, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,Z_4,Z_2,0x01}}}, /* 112 */ + {.op_class = 121, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,Z_4,Z_2,0x10}}}, /* 116 */ + {.op_class = 121, .pref = 14, .channels = {.nr = 1, .channels = {Z_16,0x10}}}, /* 132 */ + {.op_class = 121, .pref = 14, .channels = {.nr = 1, .channels = {Z_16,Z_1,0x01}}}, /* 136 */ + {.op_class = 121, .pref = 14, .channels = {.nr = 1, .channels = {Z_16,Z_1,0x10}}}, /* 140 */ + {.op_class = 121, .pref = 14, .channels = {.nr = 1, .channels = {Z_16,Z_2,0x01}}}, /* 144 */ + {.op_class = 119, .pref = 14, .channels = {.nr = 1, .channels = {Z_4,Z_2,0x10}}}, /* 52 */ + {.op_class = 119, .pref = 14, .channels = {.nr = 1, .channels = {Z_4,Z_2,Z_1,0x10}}}, /* 60 */ + {.op_class = 122, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,Z_4,0x10}}}, /* 100 */ + {.op_class = 122, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,Z_4,Z_1,0x10}}}, /* 108 */ + {.op_class = 122, .pref = 14, .channels = {.nr = 1, .channels = {Z_16,0x10}}}, /* 132 */ + {.op_class = 122, .pref = 14, .channels = {.nr = 1, .channels = {Z_16,Z_1,0x10}}}, /* 140 */ + {.op_class = 120, .pref = 14, .channels = {.nr = 1, .channels = {Z_4,Z_2,Z_1,0x01}}}, /* 56 */ + {.op_class = 120, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,0x01}}}, /* 64 */ + {.op_class = 123, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,Z_4,Z_1,0x01}}}, /* 104 */ + {.op_class = 123, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,Z_4,Z_2,0x01}}}, /* 112 */ + {.op_class = 123, .pref = 14, .channels = {.nr = 1, .channels = {Z_16,Z_1,0x01}}}, /* 136 */ + {.op_class = 123, .pref = 14, .channels = {.nr = 1, .channels = {Z_16,Z_2,0x01}}}, /* 144 */ + {.op_class = 128, .pref = 14, .channels = {.nr = 1, .channels = {Z_8,Z_4,Z_1,0x04}}}, /* 106 */ + {.op_class = 128, .pref = 14, .channels = {.nr = 2, .channels = {Z_4,Z_2,Z_1,0x04,Z_8,Z_1,0x04}}}, /* 58, 138 */ + {.op_class = 129, .pref = 14, .channels = {.nr = 1, .channels = {Z_4,Z_2,0x04}}}, /* 50 */ + {.op_class = 131, .pref = 0, .channels = {.nr = 2, .channels = {Z_1,0x02}}}, /* 6G: 9 */ + {.op_class = 132, .pref = 0, .channels = {.nr = 1, .channels = {Z_2,0x08}}}, /* 6G: 19 */ + {.op_class = 133, .pref = 0, .channels = {.nr = 1, .channels = {Z_4,0x80}}}, /* 6G: 39 */ + {.op_class = 134, .pref = 0, .channels = {.nr = 1, .channels = {Z_8,Z_1,0x80}}}, /* 6G: 79 */ + {.op_class = 137, .pref = 0, .channels = {.nr = 1, .channels = {Z_2,Z_1,0x80}}}, /* 6G: 31 */ + }; + +static map_op_class_t g_op_merged_2[] = {{.op_class = 115, .pref = 0, .channels = {.nr = 2, .channels = {Z_4,0x10,0x01}}}, /* 36, 40 */ + {.op_class = 116, .pref = 0, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 117, .pref = 0, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 118, .pref = 0, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 119, .pref = 0, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 120, .pref = 0, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 121, .pref = 0, .channels = {.nr = 2, .channels = {Z_16,Z_1,0x10,0x01}}}, /* 140, 144 */ + {.op_class = 121, .pref = 14, .channels = {.nr = 7, .channels = {Z_8,Z_4,0x10,0x11,0x11,Z_1,0x10,0x01}}}, /* 100, 104, 108, 112, 116, 132, 136 */ + {.op_class = 122, .pref = 0, .channels = {.nr = 1, .channels = {Z_16,Z_1,0x10}}}, /* 140 */ + {.op_class = 122, .pref = 14, .channels = {.nr = 3, .channels = {Z_8,Z_4,0x10,0x10,Z_2,0x10}}}, /* 100, 108, 132 */ + {.op_class = 123, .pref = 0, .channels = {.nr = 1, .channels = {Z_16,Z_2,0x01}}}, /* 144 */ + {.op_class = 123, .pref = 14, .channels = {.nr = 3, .channels = {Z_8,Z_4,Z_1,0x01,0x01,Z_2,0x01}}}, /* 104, 112, 136 */ + {.op_class = 128, .pref = 0, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 129, .pref = 0, .channels = {.nr = 1, .channels = {Z_4,Z_2,0x04}}}, /* 50 */ + {.op_class = 131, .pref = 0, .channels = {.nr = 3, .channels = {0x22,0x02}}}, /* 6G: 1, 5, 9 */ + {.op_class = 132, .pref = 0, .channels = {.nr = 3, .channels = {0x08,0x08,0x08}}}, /* 6G: 3, 11, 19 */ + {.op_class = 133, .pref = 0, .channels = {.nr = 3, .channels = {0x80,Z_1,0x80,Z_1,0x80}}}, /* 6G: 7, 23, 39 */ + {.op_class = 134, .pref = 0, .channels = {.nr = 0, .channels = {0}}}, /* 6G: 15, 47, 79 -> nothing anymore */ + {.op_class = 137, .pref = 0, .channels = {.nr = 1, .channels = {Z_2,Z_1,0x80}}}, /* 6G: 31 */ + }; + +static map_op_class_list_t g_opl_in_2a = {.op_classes = g_op_in_2a, .op_classes_nr = ARRAY_SIZE(g_op_in_2a)}; +static map_op_class_list_t g_opl_in_2b = {.op_classes = g_op_in_2b, .op_classes_nr = ARRAY_SIZE(g_op_in_2b)}; +static map_op_class_list_t g_opl_merged_2 = {.op_classes = g_op_merged_2, .op_classes_nr = ARRAY_SIZE(g_op_merged_2)}; + +static map_op_class_t g_op_in_3a[] = {{.op_class = 81, .pref = 0, .channels = {.nr = 3, .channels = {0x0e}}}, /* 1,2,3 */ + {.op_class = 115, .pref = 0, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 121, .pref = 4, .channels = {.nr = 2, .channels = {Z_8,Z_4,Z_1,0x10,0x01}}}, /* 108, 112 */ + {.op_class = 121, .pref = 0, .channels = {.nr = 2, .channels = {Z_8,Z_4,0x10,0x01}}}, /* 100, 104 */ + {.op_class = 124, .pref = 7, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 125, .pref = 9, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 126, .pref = 8, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 128, .pref = 13, .channels = {.nr = 2, .channels = {Z_4,Z_1,0x04,Z_1,0x04}}}, /* 42, 58 */ + }; + +static map_op_class_t g_op_in_3b[] = {{.op_class = 81, .pref = 0, .channels = {.nr = 1, .channels = {0x08}}}, /* 3 */ + {.op_class = 115, .pref = 14, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 121, .pref = 6, .channels = {.nr = 6, .channels = {Z_8,Z_4,0x10,0x11,0x11,Z_1,0x10}}}, /* 100, 104, 108, 112, 116, 132 */ + {.op_class = 128, .pref = 12, .channels = {.nr = 2, .channels = {Z_4,Z_2,Z_1,0x04,Z_4,Z_1,0x04}}}, /* 58, 106 */ + {.op_class = 124, .pref = 8, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 125, .pref = 8, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 126, .pref = 8, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 127, .pref = 8, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 129, .pref = 11, .channels = {.nr = 0, .channels = {0}}} + }; + + +static map_op_class_t g_op_merged_3[] = {{.op_class = 81, .pref = 0, .channels = {.nr = 3, .channels = {0x0e}}}, /* 1,2,3 */ + {.op_class = 115, .pref = 0, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 121, .pref = 0, .channels = {.nr = 2, .channels = {Z_8,Z_4,0x10,0x01}}}, /* 100, 104 */ + {.op_class = 121, .pref = 4, .channels = {.nr = 2, .channels = {Z_8,Z_4,Z_1,0x10,0x01}}}, /* 108, 112 */ + {.op_class = 121, .pref = 6, .channels = {.nr = 2, .channels = {Z_8,Z_4,Z_2,0x10,Z_1,0x10}}}, /* 116, 132 */ + {.op_class = 124, .pref = 7, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 125, .pref = 8, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 126, .pref = 8, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 127, .pref = 8, .channels = {.nr = 0, .channels = {0}}}, + {.op_class = 128, .pref = 12, .channels = {.nr = 2, .channels = {Z_4,Z_2,Z_1,0x04,Z_4,Z_1,0x04}}}, /* 58, 106 */ + {.op_class = 128, .pref = 13, .channels = {.nr = 1, .channels = {Z_4,Z_1,0x04}}}, /* 42 */ + {.op_class = 129, .pref = 11, .channels = {.nr = 0, .channels = {0}}} + }; + +static map_op_class_list_t g_opl_in_3a = {.op_classes = g_op_in_3a, .op_classes_nr = ARRAY_SIZE(g_op_in_3a)}; +static map_op_class_list_t g_opl_in_3b = {.op_classes = g_op_in_3b, .op_classes_nr = ARRAY_SIZE(g_op_in_3b)}; +static map_op_class_list_t g_opl_merged_3 = {.op_classes = g_op_merged_3, .op_classes_nr = ARRAY_SIZE(g_op_merged_3)}; + +static map_op_class_list_t g_opl_in_d = {.op_classes = NULL, .op_classes_nr = 0}; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static void test_init(void) +{ + fail_unless(!map_info_init()); +} + +static void test_fini(void) +{ + map_info_fini(); +} + +static void dump_op_class_list(char *txt, map_op_class_list_t *opl) +{ + int i; + + log_test_i("op_class list[%s] op_classes[%d]", txt, opl->op_classes_nr); + + for (i = 0; i < opl->op_classes_nr; i++) { + map_op_class_t *op_class = &opl->op_classes[i]; + char buf[256]; + + log_test_i("idx[%d] op_class[%d] pref[%d] channels[%s]", i, op_class->op_class, op_class->pref, + map_cs_to_string(&op_class->channels, ' ', buf, sizeof(buf))); + } + log_test_i("======================================================="); +} + +static bool compare_op_class_list(map_op_class_list_t *list1, map_op_class_list_t *list2) +{ + int i; + + if (list1->op_classes_nr != list2->op_classes_nr) { + return false; + } + + for (i = 0; i < list1->op_classes_nr; i++) { + map_op_class_t *op_class1 = &list1->op_classes[i]; + map_op_class_t *op_class2 = &list2->op_classes[i]; + + if (memcmp(op_class1, op_class2, sizeof(map_op_class_t))) { + return false; + } + } + + return true; +} + +static bool check_channel_set(map_channel_set_t *s, int nr, int *c) +{ + int i; + + if (map_cs_nr(s) != nr) { + return false; + } + + for (i = 0; i < nr; i++) { + if (!map_cs_is_set(s, c[i])) { + return false; + } + } + + return true; +} + +static void set_cs(map_channel_set_t *set, int c_nr, int *c) +{ + int i; + + map_cs_unset_all(set); + + for (i = 0; i < c_nr; i++) { + map_cs_set(set, c[i]); + } +} + +/*####################################################################### +# TEST_UPDATE_RADIO_CHANNELS # +########################################################################*/ +START_TEST(test_update_radio_channels) +{ + map_chan_sel_cfg_t *cfg = &map_cfg_get()->controller_cfg.chan_sel; + map_radio_info_t radio = {0}; + map_channel_set_t *cap_ctl_channels = &radio.cap_ctl_channels; + map_channel_set_t *ctl_channels = &radio.ctl_channels; + map_channel_bw_set_t *cbw = &radio.channels_with_bandwidth; + map_op_class_t op_classes[13] = {0}; + + test_init(); + + radio.supported_freq = IEEE80211_FREQUENCY_BAND_5_GHZ; + + /* Add some op_classes */ + /* 20MHz */ + op_classes[0].op_class = 115; + op_classes[1].op_class = 118; + op_classes[2].op_class = 124; + map_cs_set(&op_classes[2].channels, 161); + /* 40 MHz */ + op_classes[3].op_class = 116; + op_classes[4].op_class = 117; + op_classes[5].op_class = 119; + op_classes[6].op_class = 120; + op_classes[7].op_class = 122; + op_classes[8].op_class = 123; + op_classes[9].op_class = 126; + map_cs_set(&op_classes[9].channels, 149); + map_cs_set(&op_classes[9].channels, 165); + map_cs_set(&op_classes[9].channels, 173); + op_classes[10].op_class = 127; + map_cs_set(&op_classes[10].channels, 161); + map_cs_set(&op_classes[10].channels, 169); + map_cs_set(&op_classes[10].channels, 177); + /* 80 MHz */ + op_classes[11].op_class = 128; + map_cs_set(&op_classes[11].channels, 171); + /* 160 MHz */ + op_classes[12].op_class = 129; + map_cs_set(&op_classes[12].channels, 114); + map_cs_set(&op_classes[12].channels, 163); + radio.cap_op_class_list.op_classes_nr = ARRAY_SIZE(op_classes); + radio.cap_op_class_list.op_classes = op_classes; + + /* Allow all channels */ + cfg->bandlock_5g = MAP_BANDLOCK_5G_DISABLED; + map_cs_set_all(&cfg->allowed_channel_set_2g); + map_cs_set_all(&cfg->allowed_channel_set_5g); + map_cs_set_all(&cfg->allowed_channel_set_6g); + + map_update_radio_channels(&radio); + fail_unless(check_channel_set(cap_ctl_channels, 11, (int[]){36, 40, 44, 48, 52, 56, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(ctl_channels, 11, (int[]){36, 40, 44, 48, 52, 56, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(&cbw->channel_set_20, 11, (int[]){36, 40, 44, 48, 52, 56, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(&cbw->channel_set_40, 9, (int[]){36, 40, 44, 48, 52, 56, 60, 64, 153})); + fail_unless(check_channel_set(&cbw->channel_set_80, 8, (int[]){36, 40, 44, 48, 52, 56, 60, 64})); + fail_unless(check_channel_set(&cbw->channel_set_160, 8, (int[]){36, 40, 44, 48, 52, 56, 60, 64})); + + /* Disallow channel 36 in operating classes */ + map_cs_set(&op_classes[0].channels, 36); + map_cs_set(&op_classes[3].channels, 36); + + map_update_radio_channels(&radio); + + map_update_radio_channels(&radio); + fail_unless(check_channel_set(cap_ctl_channels, 10, (int[]){40, 44, 48, 52, 56, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(ctl_channels, 10, (int[]){40, 44, 48, 52, 56, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(&cbw->channel_set_20, 10, (int[]){40, 44, 48, 52, 56, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(&cbw->channel_set_40, 7, (int[]){44, 48, 52, 56, 60, 64, 153})); + fail_unless(check_channel_set(&cbw->channel_set_80, 4, (int[]){52, 56, 60, 64})); + fail_unless(check_channel_set(&cbw->channel_set_160, 0, (int[]){})); + + + /* Disallow some channels */ + map_cs_unset(&cfg->allowed_channel_set_5g, 52); + map_cs_unset(&cfg->allowed_channel_set_5g, 56); + + map_update_radio_channels(&radio); + fail_unless(check_channel_set(cap_ctl_channels, 10, (int[]){40, 44, 48, 52, 56, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(ctl_channels, 8, (int[]){40, 44, 48, 60, 64, 153, 149, 157})); + fail_unless(check_channel_set(&cbw->channel_set_20, 8, (int[]){40, 44, 48, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(&cbw->channel_set_40, 5, (int[]){44, 48, 60, 64, 153})); + fail_unless(check_channel_set(&cbw->channel_set_80, 0, (int[]){})); + fail_unless(check_channel_set(&cbw->channel_set_160, 0, (int[]){})); + + /* 5G bandlock low (but radio not low_high band) */ + cfg->bandlock_5g = MAP_BANDLOCK_5G_LOW; + + map_update_radio_channels(&radio); + fail_unless(check_channel_set(cap_ctl_channels, 10, (int[]){40, 44, 48, 52, 56, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(ctl_channels, 8, (int[]){40, 44, 48, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(&cbw->channel_set_20, 8, (int[]){40, 44, 48, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(&cbw->channel_set_40, 5, (int[]){44,48,60,64,153})); + fail_unless(check_channel_set(&cbw->channel_set_80, 0, (int[]){})); + fail_unless(check_channel_set(&cbw->channel_set_160, 0, (int[]){})); + + /* 5G bandlock low (and radio low_high band) */ + cfg->bandlock_5g = MAP_BANDLOCK_5G_LOW; + radio.band_type_5G = MAP_M2_BSS_RADIO5GL | MAP_M2_BSS_RADIO5GU; + + map_update_radio_channels(&radio); + + fail_unless(check_channel_set(cap_ctl_channels, 10, (int[]){40, 44, 48, 52, 56, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(ctl_channels, 5, (int[]){40, 44, 48, 60, 64})); + fail_unless(check_channel_set(&cbw->channel_set_20, 5, (int[]){40, 44, 48, 60, 64})); + fail_unless(check_channel_set(&cbw->channel_set_40, 4, (int[]){44, 48, 60, 64})); + fail_unless(check_channel_set(&cbw->channel_set_80, 0, (int[]){})); + fail_unless(check_channel_set(&cbw->channel_set_160, 0, (int[]){})); + + /* 5G bandlock high */ + cfg->bandlock_5g = MAP_BANDLOCK_5G_HIGH; + + map_update_radio_channels(&radio); + + fail_unless(check_channel_set(cap_ctl_channels, 10, (int[]){40, 44, 48, 52, 56, 60, 64, 149, 153, 157})); + fail_unless(check_channel_set(ctl_channels, 3, (int[]){149, 153, 157})); + fail_unless(check_channel_set(&cbw->channel_set_20, 3, (int[]){149, 153, 157})); + fail_unless(check_channel_set(&cbw->channel_set_40, 1, (int[]){153})); + fail_unless(check_channel_set(&cbw->channel_set_80, 0, (int[]){})); + fail_unless(check_channel_set(&cbw->channel_set_160, 0, (int[]){})); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_MERGE_OP_CLASS_LIST # +########################################################################*/ +START_TEST(test_merge_op_class_list) +{ + map_op_class_list_t merged_list = {0}; + + test_init(); + + MERGE_OP_CLASS_LIST(&merged_list, &g_opl_cap, &g_opl_in_1a, &g_opl_in_1b, &g_opl_in_d, &g_opl_merged_1); + MERGE_OP_CLASS_LIST(&merged_list, &g_opl_cap, &g_opl_in_2a, &g_opl_in_2b, &g_opl_in_d, &g_opl_merged_2); + MERGE_OP_CLASS_LIST(&merged_list, &g_opl_cap, &g_opl_in_3a, &g_opl_in_3b, &g_opl_in_d, &g_opl_merged_3); + + /* Reversed */ + MERGE_OP_CLASS_LIST(&merged_list, &g_opl_cap, &g_opl_in_1b, &g_opl_in_1a, &g_opl_in_d, &g_opl_merged_1); + MERGE_OP_CLASS_LIST(&merged_list, &g_opl_cap, &g_opl_in_2b, &g_opl_in_2a, &g_opl_in_d, &g_opl_merged_2); + MERGE_OP_CLASS_LIST(&merged_list, &g_opl_cap, &g_opl_in_3b, &g_opl_in_3a, &g_opl_in_d, &g_opl_merged_3); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_NO_SUBBAND_CHANNEL_SET # +########################################################################*/ +START_TEST(test_no_subband_channel_set) +{ + map_channel_set_t channels; + + test_init(); + + /* subbands of ch[100]/opclass[128] are part of channels list */ + set_cs(&channels, 4, (int[]){100, 104, 108, 112}); + fail_unless(map_is_no_subband_channel_set(&channels, 128, 100) == false); + + /* subbands of ch[112]/opclass[123] are not part of channels list */ + set_cs(&channels, 1, (int[]){100}); + fail_unless(map_is_no_subband_channel_set(&channels, 123, 112) == true); + + /* subbands of ch[112]/opclass[128] are part of channels list */ + set_cs(&channels, 1, (int[]){100}); + fail_unless(map_is_no_subband_channel_set(&channels, 128, 112) == false); + + /* subbands of ch[100]/opclass[128] are not part of channels list */ + set_cs(&channels, 2, (int[]){36, 40}); + fail_unless(map_is_no_subband_channel_set(&channels, 128, 100) == true); + + /* 6G 320MHz */ + set_cs(&channels, 4, (int[]){9, 13, 29, 193}); + fail_unless(map_is_no_subband_channel_set_6G_320MHz(&channels, 137, false, 5) == false); + fail_unless(map_is_no_subband_channel_set_6G_320MHz(&channels, 137, false, 33) == false); + fail_unless(map_is_no_subband_channel_set_6G_320MHz(&channels, 137, false, 65) == true); + fail_unless(map_is_no_subband_channel_set_6G_320MHz(&channels, 137, false, 189) == true); + + fail_unless(map_is_no_subband_channel_set_6G_320MHz(&channels, 137, true, 33) == true); + fail_unless(map_is_no_subband_channel_set_6G_320MHz(&channels, 137, true, 189) == false); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_ALL_SUBBAND_CHANNEL_SET # +########################################################################*/ +START_TEST(test_all_subband_channel_set) +{ + map_channel_set_t channels; + + test_init(); + + /* subbands channels of ch[100]/opclass[128] are part of channels list */ + set_cs(&channels, 4, (int[]){100, 104, 108, 112}); + fail_unless(map_is_all_subband_channel_set(&channels, 128, 100) == true); + + /* subbands channels of ch[100]/opclass[128] are part of channels list */ + set_cs(&channels, 3, (int[]){100, 104, 108 }); + fail_unless(map_is_all_subband_channel_set(&channels, 128, 100) == false); + + /* subbands channels of ch[112]/opclass[123] are not part of channels list */ + set_cs(&channels, 1, (int[]){100}); + fail_unless(map_is_all_subband_channel_set(&channels, 123, 112) == false); + + /* subbands channels of ch[112]/opclass[123] are not part of channels list */ + set_cs(&channels, 2, (int[]){108, 112}); + fail_unless(map_is_all_subband_channel_set(&channels, 123, 112) == true); + + /* subbands channels of ch[112]/opclass[123] are not part of channels list */ + set_cs(&channels, 1, (int[]){108}); + fail_unless(map_is_all_subband_channel_set(&channels, 123, 112) == false); + + /* subbands channels of ch[112]/opclass[128] are part of channels list */ + set_cs(&channels, 1, (int[]){100}); + fail_unless(map_is_all_subband_channel_set(&channels, 128, 112) == false); + + /* subbands channels of ch[100]/opclass[128] are not part of channels list */ + set_cs(&channels, 2, (int[]){36, 40}); + fail_unless(map_is_all_subband_channel_set(&channels, 128, 100) == false); + + /* 6G 320MHz */ + set_cs(&channels, 32, (int[]){ 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, + 161, 165, 169, 173, 177, 181, 185, 189, 193, 197, 201, 205, 209, 213, 217, 221}); + fail_unless(map_is_all_subband_channel_set_6G_320MHz(&channels, 137, false, 13) == true); + fail_unless(map_is_all_subband_channel_set_6G_320MHz(&channels, 137, false, 65) == false); + fail_unless(map_is_all_subband_channel_set_6G_320MHz(&channels, 137, false, 161) == false); + fail_unless(map_is_all_subband_channel_set_6G_320MHz(&channels, 137, false, 189) == false); + + fail_unless(map_is_all_subband_channel_set_6G_320MHz(&channels, 137, true, 65) == false); + fail_unless(map_is_all_subband_channel_set_6G_320MHz(&channels, 137, true, 161) == true); + fail_unless(map_is_all_subband_channel_set_6G_320MHz(&channels, 137, true, 221) == true); + + test_fini(); +} +END_TEST + +const char *test_suite_name = "utils"; +test_case_t test_cases[] = { + TEST("update_radio_channels", test_update_radio_channels ), + TEST("merge_op_class_list", test_merge_op_class_list ), + TEST("no_subband_channel_set", test_no_subband_channel_set ), + TEST("all_subband_channel_set", test_all_subband_channel_set ), + TEST_CASES_END +}; diff --git a/source/test/ieee1905/CMakeLists.txt b/source/test/ieee1905/CMakeLists.txt new file mode 100644 index 0000000..d453955 --- /dev/null +++ b/source/test/ieee1905/CMakeLists.txt @@ -0,0 +1,70 @@ +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +set(SRC_DIR ${IEEE1905_SRC_DIR}) + +include(../common/CheckUnitTest.cmake) + +set(AL_SRCS + ${SRC_DIR}/al/src_independent/al_entity.c + ${SRC_DIR}/al/src_independent/al_datamodel.c + ${SRC_DIR}/al/src_independent/al_recv.c + ${SRC_DIR}/al/src_independent/al_send.c + ${SRC_DIR}/al/src_independent/al_wsc.c + ${SRC_DIR}/al/src_independent/al_utils.c + ${SRC_DIR}/al/src_linux/platform_interfaces.c + ${SRC_DIR}/al/src_linux/platform_crypto.c +) + +set(COMMON_SRCS + ${SRC_DIR}/common/src_independent/utils.c + ${SRC_DIR}/common/src_linux/platform.c +) + +set(UNIT_TEST_EXTRA_DEFINES + _FLAVOUR_AIRTIES_ + MULTIAP +) + +SETUP_UNIT_TEST(tlvs + ${COMMON_SRCS} + ${IEEE1905_FACTORY_SRCS} + ${LIBUTILS_SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(cmdus + ${AL_SRCS} + ${COMMON_SRCS} + ${IEEE1905_FACTORY_SRCS} + ${LIBUTILS_SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(lldp_tlvs + ${SRC_DIR}/factory/src_independent/lldp_tlvs.c + ${COMMON_SRCS} + ${LIBUTILS_SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(lldp_payload + ${SRC_DIR}/factory/src_independent/lldp_payload.c + ${SRC_DIR}/factory/src_independent/lldp_tlvs.c + ${COMMON_SRCS} + ${LIBUTILS_SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(al_wsc + ${AL_SRCS} + ${COMMON_SRCS} + ${IEEE1905_FACTORY_SRCS} + ${LIBUTILS_SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(al_send + ${AL_SRCS} + ${COMMON_SRCS} + ${IEEE1905_FACTORY_SRCS} + ${LIBUTILS_SRC_DIR}/acu_utils.c +) diff --git a/source/test/ieee1905/data/cmdu_1514_bytes_excl_eom_1_tlv.pcap b/source/test/ieee1905/data/cmdu_1514_bytes_excl_eom_1_tlv.pcap new file mode 100644 index 0000000000000000000000000000000000000000..f9db4f516da5cb880c373fabfe8efff45442a547 GIT binary patch literal 1630 zcmca|c+)~A1{MZnzzF1g2!9t35_`o8!5xl&7A{&0BsXr_+-b$Y(7*sv#m#zg=_nWt gfzc2c4S~@R7#<-2EBS4JgNdkA8G#?w+|jcV>Tf z?(DsEaN~lYfZzfDUV$Hy*EL?&`*`*+#HScu=>39)i$3fdHf|b`J0PHcz{jd^0U-i| zg8gHlJcz*fCD**gE@plIgG@LS&Gcl@5Wc$;^4m-l#|5BQLe_?S=ll+XB_ zFZhzL_?mC{mhbojf8I<=o`3L9{`JYv?ib_&JYaAPz=p549jo~ z&j^gjNQ}%VjLK+?&KQizSd7g$jLUe8&jd`!L`=*iOv+?T&J;|^R7}k@Ov`jk&o7vP z8JUThnT1)IjbAc5b1)}!F*oxtFY_@!3$P#yu`r9UD2uT;ORywMu{6uDEX%PxE3hIf zu`;W$Dyy+NYp^D3u{P`QD}K$otjGFnz=mwZ#%#i-Y{uqn!Io^r)@;MJY{&NOz>e(n zx$DRIFa~^#|N83(^;g}QUD%b~*quGtlfBrReb|@%`1zgz@_`)0!5qS&9LC`s!I2!r z(Hz6E9LMpTz=@p1$(+KeoW|*#!I_-J*_^|G!IfOa)m+21 zT*vj?z>VC*&D_GR+{W$P!JXX2-Ta1oxR?95p9gr5hj^Grc$CL@oF{mar+AuYc$VjQ zo)>tLmw1_1c$L?9oj3R`Z}K~S&s)6BJG{$#yw3-G$VYt4Cw$6he9jkq$ya>MH+;)? IAIAN^0aye`z5oCK literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/cmdu_ap_autoconfiguration_search.pcap b/source/test/ieee1905/data/cmdu_ap_autoconfiguration_search.pcap new file mode 100644 index 0000000000000000000000000000000000000000..c92116dd0db8e10eb82c8531e3b0016182771814 GIT binary patch literal 96 zcmca|c+)~A1{MYcU}0bcax79-#q+DNGgyGwK+M>1h=D$!a>qscJb*ZFQWku6pWgprJ+@YvK%NYO0yDG}l5)XKUpg zt)1&Una+2CHZF9LEZN$+SUZZZG^_0UrNdj+H^S}iaHqSBG|FgqyT=&!y3g3)I_@{#10FQN zL=Tx{vMHu|*dreGn8!^s-4mYll&8(`jG1P6)^ncsf)~w}YmPiGnQNYx&9}fpuXxpK z7J1zp7Ry&)i8sAvskgo3UCS)@o)zBrfe)=zXqAtA>=U2*%xY__wa$8<`@)yLvO$rJ zHrecJTWs}>Z*8;P4&V9S4|dvRw>^IJlb`+KSHJmPvAs&{v)>;M_|sqh_K$xZbV#Yg zjwC3E0})b?v=judAD2d#!t$jNDU(Jt#3htQkTODPBys9UNMrWINV%0pv`iX7B@*VC zq^~qmLkf~PbtI%w(ji)|G-74as1V|k%(x`JClx~ql9_^pG_o>c` QOM-&5kb-2UAb9=%0Lvvb4*&oF literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/cmdu_fragmented_mixed.pcap b/source/test/ieee1905/data/cmdu_fragmented_mixed.pcap new file mode 100644 index 0000000000000000000000000000000000000000..82013e4859db1e21083d9d0a52c589ede661f400 GIT binary patch literal 16560 zcmeI233n4!7>1uDMW9$^RThB}t+YU6Td*twf}o&Kfzk>hifJ24sMeS^SQRS@XhFfX zf-96A1z81m!3{(DD-|v30adELv z0O?)ZHg&1(Nqg~d4*dfK;bJSzo9azYYTr32&tKpV1#%#U!KC{`dHx(<&`KB*2>A;~ z`9k@LRtImgH_b}u6DYFARPG0TIr+Xkf3D>#to$}9rNmzxOeqOxhYO0rscLBT!;)ao zmy)Qi4*Fj7eKfSU`f7Ur47K)-Q6FmAu(}597*y6u9qYBU=dtoxv(@LaQ|+`x^D;3j-zlS3|kMv})U0*oe~ zo4JJoZY9WVgeatlFk`r#V(#Eh?qV!=Q^Gy!aoo#&jAsH9xt|9pWfGHlkcXJUR37FL z9_29}SC77oCz-}{p5keq;aQ&Jd1f$^a%M4`Im~4q^I5<`Uf@L*v6z=w!cvy8oE5C( zWmd78S9q1zc%3(Rlec)AHLT?w-sL^s=L6QUo)6i;M{Hyho7uuvKIRiXakT&TU223 zn?zHik{?yQ$iNvHj$Xuttsu7=KsaJLN3>J2-qcueZX6G60KdNS#nc0pEQH4}nJ-Ut}3^AP{ zst_+KFt@rM-G^A^jto&%5St-NJfa?R)nltb=4K?zRP!OFdmS0#5+5S4J7ptqt#=pKN-dhisqc z$Pkyz?Mjx3$TBjwD-5Y%NMx^Sk3*SGy{bsBsxTyiA=0aAI79T*isnbv`Q0_Gvz%%o zZmFvZM(k+mKXBsYp^HYlJ;Xuu-atDygiBs;a3j%^?m| z!(k40gd-iLrlZwTTOG$ZR$a%b=XfWm??fkQprJ-ic8XIqcA6%dI^7x0)J$_NwA9L3 z&UTJ;t+jEk^R(4YhVxzELhW7TVwdRPQXO^DSr=Vpy3FOexk7hW>Y=AzuF_i{SL>^v zYxFn3K-ap?AlDo021DFvs9|n0+|6z=!mVy|yF1+JE+gG-l+o^SuQ9=KjCG%J?)QN4 z9yGy351C}LDIPY}BOdjbX{LMJ6P`4~OtZ{3$6WI~C0mYMd7k!+XFX@W1r~bV3l@3N zOJ0_*K%vE6@v7G>vD7lJd&8TSTj4Eldq5wSb7|AkYRE$}PGApI8l1ecJQKld?M!U4620{K1*8ls*sE}34O68c9 RD6>*ZL6w+-C{qyJ{=X+LGxq=h literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/cmdu_fragmented_reversed.pcap b/source/test/ieee1905/data/cmdu_fragmented_reversed.pcap new file mode 100644 index 0000000000000000000000000000000000000000..5c1dc7ee02738390fc49920262d53d42498df436 GIT binary patch literal 5397 zcmeI$`IpaC9LMp;_xr_=7EuXl8BInT6_QqkD21{lWsQh7Lc6J`Q5iz3wwWPOwy~s0 zWvo+{M3z=6OW9jgmec2Z&Ueo7LudYh&*z@=zUSO~&%Hm~^E}VBowsF?5ga8eyV1eiy!>FEUNB==L$btTrLxM$6R~(g+2BGeS!{`=mMQYNFD$phN~?Tn zwXdwP*4NfqZ-b4#@vZNC?*~8HWV4_A>=#>Xwas?F`pphI?Xue*d+qbPKm6%0`~B@7 z|2p8HLk_1XC>K!>W(rc$XwWz&JrG8+{!bbSDO}1#xP%#(6a{G!1!1Nj88foe;w2g* zUTloOC{fG^eat8wQ4n@xtS>6r7_nkw#3NS1%t|U_Bq9pJOhGbZWacDF%ovJ|5ug%f RjL>I{WCtv2Xoa?+^&ikLH3|R# literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/cmdu_fragmented_scrambled.pcap b/source/test/ieee1905/data/cmdu_fragmented_scrambled.pcap new file mode 100644 index 0000000000000000000000000000000000000000..84662f92ab69f7e2d67ae62fbe0e01530cb7c004 GIT binary patch literal 5397 zcmeI$`IpvH9Ki9{^E^}27%4?WR6}G7i8Mk}Dhg2usS#;?n%L z#e%_(uI0HaV=-C5R5J2&_7%)24Ep&+#pANc6N|@W#uKr4LOfUy3;IN`Q599Ct0uz% zG99S8gVa#d!D^}P5QnPcFo&z_2uG^tC`YTWfrhdi<5-Ox=Xi~s;6x`m*(sVh)oD)G z)ESyN(^;B3TMOCF(bBojbG}w`wARK2+G?l03tgmxjxKhIOLcOY%XQYp6|QuZuC8{C zYjty-?s~{|y`FAxqh4;(TOWP(bF*9YH^8k18f38B+-`_F+-a!0+-;b9-0ME~8y+0T z14bBWl+nf*E8jQ|8gGJ!JZz##9`UHjrg+R$(@gicCp_sXGt4y0Yz5{h^t5L@Yp!{o z^Sl?l=p`?E#jEBkQtUMgEcCiX-teZymRM?;x0HC>J4!8A=3Vc3-v?Is&__P@iBGMx z%4(lkW3A79;Y(jx=WFYI<6GbP-Ub`};76PMWV0=Pw$(Pj_*J>>cGzi`-~8?myZz}e zd+hbMeg5%pih@K$L6|8>8lyp3<;GB9jHGTNV}#up$wU)T5Lyb7#u$_zue8QUR2ZXb z#7da6ORC05k0=N;1xaI+bWBugjCh4Hf>%qFT|)mVsS;5Tc4O46k)}~#gJk^QKSo+! XD!WvRSP3&LDGD+o3c^f5aQpiKqf|5> literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/cmdu_multiple_fragment_tlv.pcap b/source/test/ieee1905/data/cmdu_multiple_fragment_tlv.pcap new file mode 100644 index 0000000000000000000000000000000000000000..6553ce31cd118f1725693f2bb500c659bb4a2f4e GIT binary patch literal 12396 zcmeI2S5Q;$x`#s*=}kmRXo4VBkuJT7bV89{L_!m!7b&4gi*!(m(m|vbkrFy0kVuoz z1*F7K0s$h@!_jkdZuac|?3;7p=3Db#teG{luD)mH_q;!3Uqc}wfEe(9hZsQc`^B@w z#A#rK2(U>4_}?iKW`HLFDqw$}c-h|{5bx3g02DN3amr@^5<(R3HUBPYns4k-2W^SB zBfI<{n@BfGvf*@=Mi4TLw6&O3^s%vmqSsJiJ6V| z*F=`cH!iw5=JB*^Qk$|f!>O*qsNFa1ZTqy;J88hLZL%oFxrW0EPDhu6TE7pf^Nu?r zTn^oxz}E-;V+XJfyyE!G-e4rX@Ya=Yb}-KSoYbz$&u(8N_51{-ZqVqX^*Fl}H`aafL-X0a1f>pDuJT=ZVg-AzIEIEdJvrw(iF=jdrZRlv;O^w}Z8@#gU z@40>Z-g(MFcs`mF3siyQdHK$)y%Q0Z>K$#wkfz%asWECbDST_#}@MPm=IL zg24=m3-8ejGjIP&LSog`T52vwrqWOO;&;W0q-#}W0;bghv>5ygUSM=$caz-;@?H{u zxHD+%cp6PAF0SQX$yLYg+LuM4^}=k-?}yyA=m8d)qm@Lf1v&>>wW<*8un?WpZiR}B zX!g|-h^i0VHp%|Iy^Q5dGBZf^@^v);ZEEteH|0AzA&_jo#FsK>}mz1$J3UZls}EX_;Z(ORLNaT-*iETx$g zLq=-T+&UuZt;e;uFUmJ>*urO58BC3G-`FvKB&qHlxgag$1|gd$haDxqP;sH$w>k~* z*NU?12^*ZrY;RB5iiJc(8Av9x=YHNOM~^UmO>`V9y5a)5$_k@h2Rce8}+I`cmi%j#Xk zG8R?#0N5^K>|D=mD>OI0%0VBL{b0c9$;pRJTv+!pTzDWapvaglg~*5NL-%0BT|||o zOnEq?Z(h9fBkR}^v%cY#*P!94$?Ki@+CjzSp?bCH+eN4$Mv9UZTzcI@voN8Z@#9bL z=%O^N&Nje7(o9smt@}H}=V`Tje0Fl7i_UJf_F|^qjc-P4V+c34fqLt-z=!sL~Z7SWQ(4_Y!Y-(& zoE1DI+O2UypR>mSxf(CAse>K6sP`ha4L#&~mi^0e?EV7%tDEi6zJOk4Y?q%uT z%|2-;^_)eoLnSTZbg}iLT~IgCIq%l23}Kh$>Jj@e%;eFKSOYZ8CfC|Lh>3PCWo#kw z*PxRqoG+moSROokK2aq6Lbz*nkX>F`T$iy__Nz|5l`lwbWre#qLtCqDteeWTY1%iH?QUqlhNKQ z;{NBVZ*h1;O|#t1AF(B4gf^bjIQ6p9g?h%u_cmVZ22vS9-D!s&55?2O+9Uz(nVuPO zA7q77x0Ka0@;`>|Zqu<K=+hHu??8krL>ArkwdD`c#Sw z8ZfGgM9VltS%QrQ)}(W@s-H|WI&lSgAA1%$mG$id(F*Al>*mQ+-;wyFdos+KcXwM^nOK<_K!J-6%t{A=iGRB_@b z)R@(H$O|KWFq)$7eui4I{0?)~Y+x0kb^5G|q|+5P$;aCb8;WxVd?yPCr%R}!zu_u80LMvk98-jI}PVOh6~pP*3VznShs2o>32qoFhk_OA(%plue-A!+b4 zR}s#ZXz>B#j){{qf^iLSGgq{;U|IZ_$396W$JzgI@|Ni^N!~IJ_rS8t zO0g?q9;^6#%*XF1l1SLYt@w!&97Z)}j_uQx;-E~oK`_d+88<-(ZinXzS6$84_n2i7 zKyOA)RgY4AnJ*B0*W_q_^#*24)y>W8x-U6+Wi^5V?v_}Qlx68<)x zt*KH6)4BduEUWy4NFtXl?EwMe6*?I8xg36!CMYdzQLR&^n-a!c$vE?k>Zxj zYNQQAdu9vdv%T$w8yvjW+3Ez9)CE11v$!{wd`G^nc)oksP`VS-iH}F^lk6-W4y-KC zVDllFqc?UB=p5~65;E{)rAIb_wWjx?ee`fsu2dSl>wRs_-zuxOX{gR}3QvkOaDdJt zvwc3e@{NK@KJ*w(1@v=_q>qm}#B+(uB?4>}$%~(5<}Ef?P$PaKi1eMbm9@;Q8JQQ6 zZ!;*n-%QlGK=As2Pgd_qm=XLvpF&tn;4CZOyI&@2^`+2ltE5VqWEGK*At9`%BdC%C z9-im9XRmro60gT8v@-}Y7fu=7VTPbH&Iywd=zHDdW^$#Vui!)5Fvwfk^g)nBUa8YZ zUfqY>`g1#y)HO;^Hc>6@x^=7wu@=%ki1`r^Z-eBi1Ztcd-4i2SdFJ-T^k!T!ve<*z z(sy8fxce9Z9LB;i5}(#w4z{$Pm@>nH3vBe%hK=%-S)S9(`OY1w4E2iOW9h*m=Z(w8 zh_@qdr|h+Zp8DkR@Eea6a8{5GU=6Iahli_kiip0jV3{%(-|qO7!sBo<)`B7UV}{hE z3NDz4=}j*tADO7wls(sIqQGTW#VjH2tyLqifWmXk1oG@KqUIV~!TX*B!`!A6s?9By zn5_|s>_htSgS%!=O*A59IVEs${0{L4FO~p{6u zE8$Qfhb@zS-SVrlY)$EPDckIwh%0x|m6PNH(c({`4ad#b)+p!hG_AUgXW=9GxAM@< zr8n{ncFM1N&%ZcN3TrBM=m|~tx5H~5pAT}aJCWs6aLZfK{_u(FIfCf>OYF}adb&Lw zb8Q*|hi>Yx;jq|iLfX9aUFFDT0Ui+>CQ_3Z-hC$D0)eUrSJP(CB8KQ{7$v(WmNKAZ zEv-%?UG1Pd;qR#q2MjP{tDnC(CNIzzGfo8Hz=Wq9F#GLbcIB!!o2}{fi+Vglb?o?R>4d7J5_7@q;>m{p987;JPoT{l|)puAW zty`}2J$|n@2Tz74-vJJXSJRkYVCaEpQ-`Ts!~OA^iWx7e$S$;wUi~~|wEQBZ+2naK zI2+n|W^b+w5(sBXVa zgUR}O%mc9?gTa)Kya#z-ZdJG1zmm*b%JZ69o#?}LS@pMut24wAj0_zsr2@Z%AYLrk z!$OoSwwdI+=fohAzhs)^bmZ@|T#>NI0`XnrsN9EuJ$ibqrtxg`qHoZ=lW*;~=HALJ zf6WZ$gC(njBl<^nW0G}j5}j$1wg03xJuBJY%sG^-L>XFF7vMk5~=)@)3u$yGW7+xzaVz5ny&&w&)GJF4}cDg5p?%Jm4 za|#>bvuy~iyXb?QaLM8@q&x?E#^8o(ekHQ5yK_knuHf=naAWEwDMz{K)X z2R&bOe7rE>q_fITTz=n*L6^=BL$%_qC>q5rhL9dIg+$|eypkBn4R6;-+qHsQdNynM z#jy!|uZHN}YfRKf+mT4vYB)LC&Wx-`HALD1zN_X(A#PXL<6smrN$nljUYs78c?0+k zM9cXuVnDo#;;ZP?*{sxVriuX11)9ER&TdGYUd%Bq_)EWf#?f9roXc7q_mX5>wJXN9JI;Z%gN)K7>>g0fI<@-C!*Ey6bRJXnR{b;-J5v4nM!73Wi66(v) z|JQ~7Kjh!|2l5jEF4e%L8n{#g|HW$HGW7p-q5lv0c~Sq~_7DRu)xf11xKsoG#cJR( z^#66C{}1_ff0G~md&B!@{?&$;1aPSaF4e%L8o2BOT=oI}l0Lv6><9e=_DKPkt%1wd zz<<;lxKsmwof^0d{eMyD|AYPJf583~z-4RTvNiCZwgxWMz+a~ZE<^v{68isO|Lbq; WhyHHt|CxU!dB`CDAFYAkKmQ+9$0v9I literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/cmdu_topology_discovery.pcap b/source/test/ieee1905/data/cmdu_topology_discovery.pcap new file mode 100644 index 0000000000000000000000000000000000000000..e51dd851018b12848564d76f5195532ee9b7baad GIT binary patch literal 108 zcmca|c+)~A1{MYcU}0bcax79-#RsUeGq?cRAk5fsh=DyEGC7t)^^{5!?Lpx&ENtgwM`|DrV~z6bbdPGx7{g(!nK zmjTXa;Bod+5oBm!U}9u!X7FMHsRB9|Lh-s4<>!@nB$nm`2Yb2z#elA5g4oXEl$e~2 kDub$eGXoXJ>OGpvS-f0Mle&<^TWy literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_00_end_of_message.pcap b/source/test/ieee1905/data/tlv_00_end_of_message.pcap new file mode 100644 index 0000000000000000000000000000000000000000..e472f7a1e03e252fe68ecbf2f9ea5a3aeab6008f GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW0I47h0O#iq ANB{r; literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_01_al_mac_address.pcap b/source/test/ieee1905/data/tlv_01_al_mac_address.pcap new file mode 100644 index 0000000000000000000000000000000000000000..5b0a0d4a79aafae6e7337ddde33b56c11f6e4039 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWU}RwXCca}` K+k22woB#l4SQ9q@ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_02_mac_address.pcap b/source/test/ieee1905/data/tlv_02_mac_address.pcap new file mode 100644 index 0000000000000000000000000000000000000000..de38df57876dc839a5bd6def0937ddaae4fb83e0 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWU}9k7U{LSP JC=@F5a$2@ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_03_device_information.pcap b/source/test/ieee1905/data/tlv_03_device_information.pcap new file mode 100644 index 0000000000000000000000000000000000000000..649fb683016f5eda88d36bf54e4788b2e08cb289 GIT binary patch literal 141 zcmca|c+)~A1{MZnzzF1g2!9t35=({R4#z(W7cB;o8#it4v|?arU;wIWU}o_ACcdLB tJcH#6m;wrd8QF}?To48W0~yEGC1sdH-%9Brg5Ae~P3epS(01jFkP5=M^ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_07_neighbor_device_list.pcap b/source/test/ieee1905/data/tlv_07_neighbor_device_list.pcap new file mode 100644 index 0000000000000000000000000000000000000000..974c92bc835b15712e60011daa9c08e4eef372c6 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWU}xa{BEDl? Q+sAL>JKDlCKpLh($ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_0B_vendor_specific.pcap b/source/test/ieee1905/data/tlv_0B_vendor_specific.pcap new file mode 100644 index 0000000000000000000000000000000000000000..2563d4b202d7423821054aebdda640f3bc0f8f1b GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW;AT)@5RhPI ZkoqFNV_n-v#w_t2ZQ(gUEg&P{0053<79;=w literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_0C_link_metric_result_code.pcap b/source/test/ieee1905/data/tlv_0C_link_metric_result_code.pcap new file mode 100644 index 0000000000000000000000000000000000000000..9c6418b56056677fdec2fdc8291c83dab8ab7218 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW-~o{Y0RZXK B4^;pF literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_0D_searched_role.pcap b/source/test/ieee1905/data/tlv_0D_searched_role.pcap new file mode 100644 index 0000000000000000000000000000000000000000..7e28d0b4529456ab594e6f962b798f9a247f7046 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW;02Kc0RZXw B4^{vG literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_0E_autoconfig_freq_band.pcap b/source/test/ieee1905/data/tlv_0E_autoconfig_freq_band.pcap new file mode 100644 index 0000000000000000000000000000000000000000..faf54c08a09611b8715d321dc3ee32acdcb6dc20 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW-~*8a0RZYB B4_5#H literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_0F_supported_role.pcap b/source/test/ieee1905/data/tlv_0F_supported_role.pcap new file mode 100644 index 0000000000000000000000000000000000000000..57f7cce077e1d674bff4b4ab36e4fa8edc883683 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW;0KWe0RZYn B4_E*I literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_10_supported_freq_band.pcap b/source/test/ieee1905/data/tlv_10_supported_freq_band.pcap new file mode 100644 index 0000000000000000000000000000000000000000..08c9f912b70818a6f6961a2fbbeb48ae567c6103 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW5MW>gDJB2_ D>W&Xu literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_11_wsc.pcap b/source/test/ieee1905/data/tlv_11_wsc.pcap new file mode 100644 index 0000000000000000000000000000000000000000..ecc51ea47efab286a5391835101203036d6badb7 GIT binary patch literal 494 zcmca|c+)~A1{MZnzzF1g2!9t35<3RufN+Q7pM{GS1Idk>Hg{SvFf=d#1senzR|$AA zFbW7LF)*?SxHAa6mJOS?-n?^v3}^ejmz~#n1r!+AzKQQ>3(pXcVh}j1%F1)mrDR69 zTF;voYY(Xk7%?1p@0XQg(|+NcLEf&T`^=WlF`9ST*Fb_N$FDZ<-9?q=A4dXL_c$dO zoip3NhIiT-jTdDFd@TXTI{mim-OfE|et2mm6X&syTB@?U7r#nlRX^XUHf{PBU2ogq zz?o|k_uP}1|59m((BgC9s#fJI{-oDDx7@hzjl{kKh3+;nr7kY=8&1w#FRtH~mHkuT z&Vyz3hJPOldtT73;Q6h-^5|I$b>*16)rAb(rFnmFb^V+m6?209&BVY$u_PUbB{$1v zvOZan#U{YQz{HR&AOIwJ1$Y@483i~PnAjQwTtG}k21&=vqL9qgVukR`qSTz!;$j8A z)ROZ2qU>S;Wd?Sjq=}`Ofq)7F3z&9d;CBo+GBY-`G%+#&0wW875C#qgHim#t3=B*H zf(RWp42(t;@jD0E{&OPoN%iEe1vfAd8(rK!S-06kiMghnbr_ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_12_push_button_event_notification.pcap b/source/test/ieee1905/data/tlv_12_push_button_event_notification.pcap new file mode 100644 index 0000000000000000000000000000000000000000..1f60de03e1532646cde7d10968bc91b65d43695d GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW5MtnCWMt<0 RBEF+7Jez@m4Wt7T002hS5{Li* literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_13_push_button_join_notification.pcap b/source/test/ieee1905/data/tlv_13_push_button_join_notification.pcap new file mode 100644 index 0000000000000000000000000000000000000000..260d1e216449748e8282fd6ea39e53978992c07a GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW5M~fzVq{`w UVHGlA0MiUy+&sK|AdN@>0Q~C^5dZ)H literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_14_generic_phy_device_information.pcap b/source/test/ieee1905/data/tlv_14_generic_phy_device_information.pcap new file mode 100644 index 0000000000000000000000000000000000000000..8b63b0edfe35af749b6d0ec018bf3bb370733c2e GIT binary patch literal 139 zcmca|c+)~A1{MZnzzF1g2!9t35=(~S4#z(W7cB;o8#it4v|?arU;wIW5MgjC!36;CH4l9N literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_1D_interface_power_change_status.pcap b/source/test/ieee1905/data/tlv_1D_interface_power_change_status.pcap new file mode 100644 index 0000000000000000000000000000000000000000..359442bb34b10ac222d0e7d46c6e8e7bc2c590f8 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWkY(UtWME`s LW?^LlDaHW+?{^P> literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_1E_l2_neighbor_device.pcap b/source/test/ieee1905/data/tlv_1E_l2_neighbor_device.pcap new file mode 100644 index 0000000000000000000000000000000000000000..4ae71defa04b4b438ee434345e8048f0af545ad0 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWkYf;MWME`s YW?=<#xwv_F`GB;Pw2Z7ANG}Wk0RJ-(R{#J2 literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_80_supported_service.pcap b/source/test/ieee1905/data/tlv_80_supported_service.pcap new file mode 100644 index 0000000000000000000000000000000000000000..d1edde6dacd94742bf559aa5132c06babc484830 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWXkcJs1gXXk E0Q+>ypJ005k~6}|uf literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_85_ap_radio_basic_capabilities.pcap b/source/test/ieee1905/data/tlv_85_ap_radio_basic_capabilities.pcap new file mode 100644 index 0000000000000000000000000000000000000000..f59eebb9b49a45dbfee63d04acf71281839c1b06 GIT binary patch literal 139 zcmca|c+)~A1{MZnzzF1g2!9t35=(~S4#z(W7cB;o8#it4v|?arU;wIWXk~C$+Q7Jf z(N-p|Vo_!d9fJ~4Mx9Dg?v#w2l8Ty!mX4kY)uOB!6%8E|YD5{Q)`~JuUAnRk$f_4* VXb@%7vdXIJn%yYMWK;yS1^^+1C#nDd literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_86_ap_ht_capabilities.pcap b/source/test/ieee1905/data/tlv_86_ap_ht_capabilities.pcap new file mode 100644 index 0000000000000000000000000000000000000000..982d84f0ad1f6185479e5ed92418936536b78bce GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWXk%diBEDl? L+ow2?T3i4Ch4d60 literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_87_ap_vht_capabilities.pcap b/source/test/ieee1905/data/tlv_87_ap_vht_capabilities.pcap new file mode 100644 index 0000000000000000000000000000000000000000..0d89226b79acd10dda9a1801f0ab8aba04ea133f GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWXlLO0BEDl? Q+o%7({{K=l0BOJi0Px}%3;+NC literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_88_ap_he_capabilities.pcap b/source/test/ieee1905/data/tlv_88_ap_he_capabilities.pcap new file mode 100644 index 0000000000000000000000000000000000000000..1a67f6fa217eed468bff8efa0b73e581eaf28b1b GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW=wJ~1BEDl? R+eeQ7s~|A<97q=`0021!8?XQX literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_89_steering_policy.pcap b/source/test/ieee1905/data/tlv_89_steering_policy.pcap new file mode 100644 index 0000000000000000000000000000000000000000..eb6a93d49e3bf517d0471529ba77d50edf12b241 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW=wuLOU|{+p RzN0NX8>kS>$O36Y0RWk`75xAJ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_89_steering_policy_2.pcap b/source/test/ieee1905/data/tlv_89_steering_policy_2.pcap new file mode 100644 index 0000000000000000000000000000000000000000..a9182704f088be62ed3e0f8622f3699d08d77a97 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW=w#qzVD#&^ M?)VxGQj7xt09E=D#{d8T literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_8A_metric_reporting_policy.pcap b/source/test/ieee1905/data/tlv_8A_metric_reporting_policy.pcap new file mode 100644 index 0000000000000000000000000000000000000000..04d0924a20e302f709a5a647e1f2184f6ae2a7dc GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW=wc9KWcnh$ Tqb)oesO|xnl?7q}F#-SpzHb-6 literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_8B_channel_preference.pcap b/source/test/ieee1905/data/tlv_8B_channel_preference.pcap new file mode 100644 index 0000000000000000000000000000000000000000..39fd1928f713ca0c505340ab88b5ce82ed2159dc GIT binary patch literal 144 zcmca|c+)~A1{MZnzzF1g2!9t363c+%4#z(W7cB;o8#it4v|?arU;wIW=w|TwBEDnq z-hU#246lM17=jrXLKql|nKg6_7)ls*7)lu!$`}~R85k-U7%CYUsu&om85n997-|_9 Z>KGX6fhMq8WmR>}W@uz!XaZTs000lKA)WvL literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_8C_radio_operation_restriction.pcap b/source/test/ieee1905/data/tlv_8C_radio_operation_restriction.pcap new file mode 100644 index 0000000000000000000000000000000000000000..4551fc471f778aced00461eb017c11807652ce75 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW=wT52BEF+7 VJe#S3Nr$n3sgW^-8Kesp005Kv6#M`H literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_8D_transmit_power_limit.pcap b/source/test/ieee1905/data/tlv_8D_transmit_power_limit.pcap new file mode 100644 index 0000000000000000000000000000000000000000..77621b17f2b76b5840882f2615e685c5dfe29ff2 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW=w)F4BEF+7 LJX;*378d{jWN;Gx literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_8E_channel_selection_response.pcap b/source/test/ieee1905/data/tlv_8E_channel_selection_response.pcap new file mode 100644 index 0000000000000000000000000000000000000000..8c7be0e39f5dffa2f0b6718a0d1665cc5095ff8e GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW=wo32BEF+7 LJevum78d{jVl@)# literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_8F_operating_channel_report.pcap b/source/test/ieee1905/data/tlv_8F_operating_channel_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..99de098ab18ffe79515040982edb445606aca727 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW=x5;jBEDl? S+ehYx)pcuY*NTI*U;qHv&=+R_ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_90_client_info.pcap b/source/test/ieee1905/data/tlv_90_client_info.pcap new file mode 100644 index 0000000000000000000000000000000000000000..3a69ef4c7231aa171c62fa3b2310388d2f708f29 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWn83i3CBCCA QJo}6Ij&*GxK^m|C0JjGgO#lD@ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_91_client_capability_report.pcap b/source/test/ieee1905/data/tlv_91_client_capability_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..03e7c46bc650538181818220da9deae8f4c9c7e8 GIT binary patch literal 337 zcmca|c+)~A1{MZnzzF1g2!9t364L~7K)A#4&%#BEf#k+bn>(!-7#bLWf(;WHc^L!+ zxfmF@(~8RCOHzwV;*&BMIeLUMpmx4q_nKOqOz*GrnauWp;1@rJ)`)4AONXF z1NEE?3>*zW!2^u@c{Wyd^REJ`0%C>#3d+nJ3_=VHE(S~o_?=rN7tNvTd94~~R zyK)Q@e2j4$eU}v|&mw-8i$OqwiGe`?Xvtl6hJa5@z)-l$6_7tmnvJo6oe?O-003pb BPn-Y% literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_92_client_association_event.pcap b/source/test/ieee1905/data/tlv_92_client_association_event.pcap new file mode 100644 index 0000000000000000000000000000000000000000..eccacf2db859bd97abfb5031d7430531b0bf7fc2 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWn8d(apwX?Q QJo$_Gj<)b@kOnLO0Fi4H?EnA( literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_93_ap_metric_query.pcap b/source/test/ieee1905/data/tlv_93_ap_metric_query.pcap new file mode 100644 index 0000000000000000000000000000000000000000..4b28dba4f68d24d427346188599a0bcaa601b238 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWn9RV-^hJC} OTXvVn{N0LTX*Qvd(} literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_9A_beacon_metrics_response.pcap b/source/test/ieee1905/data/tlv_9A_beacon_metrics_response.pcap new file mode 100644 index 0000000000000000000000000000000000000000..655a85552c70f0212f264e4390705fe07560301d GIT binary patch literal 138 zcmca|c+)~A1{MZnzzF1g2!9t35=(;O4#z(W7cB;o8#it4v|?arU;wIWn8o1c*KghN zHJpJ-U6zA^wX$IQq|H822yQ3gR+=r5o6gq}!sti0^0%&jw2a2>@G_ BA{_t# literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_9B_steering_request.pcap b/source/test/ieee1905/data/tlv_9B_steering_request.pcap new file mode 100644 index 0000000000000000000000000000000000000000..e82aaf53161bfa78c3d44846c3b9eba6fe5c39ae GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWn9U%qw1IK| fqOA`Y7{m)0{rastzJ@b?5#P}ko*l>r(hmXv?*ba0 literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_9C_steering_btm_report.pcap b/source/test/ieee1905/data/tlv_9C_steering_btm_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..78b78e2e89ae1119fa927d7cf235b43d6b55b80a GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWn8P5fw1IK| XqOE@Y)*WBN8NP_`XbaB<=|ctp=c5?c literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_9D_client_association_control_request.pcap b/source/test/ieee1905/data/tlv_9D_client_association_control_request.pcap new file mode 100644 index 0000000000000000000000000000000000000000..946a8a663d063afd2b17f96d95ccdba637328187 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWn9CsWMSMqF Ucs2ur9HU>qb;s9mkR~($0I8)G3jhEB literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_9E_backhaul_steering_request.pcap b/source/test/ieee1905/data/tlv_9E_backhaul_steering_request.pcap new file mode 100644 index 0000000000000000000000000000000000000000..f133bc63ba9ae1aa690859ecd5c3cd66ee0ad7bc GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWn8(2PMSRD) SwvS31822yQT3GEpck$H literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_9F_backhaul_steering_response.pcap b/source/test/ieee1905/data/tlv_9F_backhaul_steering_response.pcap new file mode 100644 index 0000000000000000000000000000000000000000..f2bfe620d1abce35a803e4aeb16657841b5ca5d7 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWn9sobMSRD) RwvS31822yQ3etcD007G)7R3Mn literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_A0_higher_layer_data.pcap b/source/test/ieee1905/data/tlv_A0_higher_layer_data.pcap new file mode 100644 index 0000000000000000000000000000000000000000..53d2563b8391caea5a6abbfaa40e5f67221b4d6d GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWSir!_%gDrx KsvV>a1poj8aS#Lm literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_A1_ap_capability.pcap b/source/test/ieee1905/data/tlv_A1_ap_capability.pcap new file mode 100644 index 0000000000000000000000000000000000000000..21fd2012ac2ceb39c14311d4ff53c41aa070e786 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWSjfQG08)h? F000qy5NZGb literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_A2_associated_sta_traffic_stats.pcap b/source/test/ieee1905/data/tlv_A2_associated_sta_traffic_stats.pcap new file mode 100644 index 0000000000000000000000000000000000000000..9366418d444579b88e19496c8127a85057b83607 GIT binary patch literal 102 zcmca|c+)~A1{MZnzzF1g2!9t360?Ki4#z(W7cB;o8#it4v|?arU;wIWSj3>@*KghN aHJpKI%M}LZ?p+KFjExKo9LqskkpKYdEEfL& literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_A3_error_code.pcap b/source/test/ieee1905/data/tlv_A3_error_code.pcap new file mode 100644 index 0000000000000000000000000000000000000000..cf82be76f928e699a3ad25290ff41064c31ee115 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWSj@oARG`tV Lq&yj<78d{jKd}+- literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_A4_channel_scan_reporting_policy.pcap b/source/test/ieee1905/data/tlv_A4_channel_scan_reporting_policy.pcap new file mode 100644 index 0000000000000000000000000000000000000000..e27a1f99d3ae95aa55fe97873dac5fadd8cbef29 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWSOOvm0ssJV B5A6T| literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_A5_channel_scan_capabilities.pcap b/source/test/ieee1905/data/tlv_A5_channel_scan_capabilities.pcap new file mode 100644 index 0000000000000000000000000000000000000000..40af6ccb004c84429831dc75806234d3f32bbd72 GIT binary patch literal 109 zcmca|c+)~A1{MZnzzF1g2!9t35_5&(4#z(W7cB;o8#it4v|?arU;wIWSjwQu^hJEf ly0%Xa3=GUIOo0qR3}F68FrT%Up^TxDp@yNBY3WL!K>+oz9o_%{ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_A6_channel_scan_request.pcap b/source/test/ieee1905/data/tlv_A6_channel_scan_request.pcap new file mode 100644 index 0000000000000000000000000000000000000000..bc6201481c6146c9afedb1650e0ea72df63afead GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWSjND~!1zUc M$GWypAjLQU0C=Vo;Q#;t literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_A6_channel_scan_request_2.pcap b/source/test/ieee1905/data/tlv_A6_channel_scan_request_2.pcap new file mode 100644 index 0000000000000000000000000000000000000000..2aedf0531aac3fffda4620861df9ae7ac0fe38ac GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWSjHgS!1zUc W$GWzU%w^0DDH*klD;q%CPyhhW))$!o literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_A7_channel_scan_result.pcap b/source/test/ieee1905/data/tlv_A7_channel_scan_result.pcap new file mode 100644 index 0000000000000000000000000000000000000000..8a2c80a8a7a0eb16120a61b4552045fdcbcf3861 GIT binary patch literal 167 zcmca|c+)~A1{MZnzzF1g2!9t3603*e4#z(W7cB;o8#it4v|?arU;wIWSk93AMSRD) zwvV-I8DxwMj0|)QjdYC+LktY9OpUBe4D}46bPY_c3=9~SGB5#EwuNVLrxlgOm!uY# e#HVF!V6rf1U}j**0?A|}WRhSqAeYEB00{sfQZEz$ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_A7_channel_scan_result_malformed.pcap b/source/test/ieee1905/data/tlv_A7_channel_scan_result_malformed.pcap new file mode 100644 index 0000000000000000000000000000000000000000..c76892c89aabfd4c5d2bdf387d670afc2fe0940c GIT binary patch literal 167 zcmca|c+)~A1{MYcU}0bcaxO%tCW6H3p}52G&%#BEf#k+bn>(!-7#cvT8kRF8e-YoY zuI*#(S_T;-10w@nLnB=y!w>^QD^nvY6GJ_NC|v_nD+2?Dr3_3!m2Kf!+-XH+@g=Fn fCGlw)8<;E%K$c{IWU>)5Nnn`-gH|5Xs!4APZ>!UGBb0LNb<8UO$Q literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_AB_mic.pcap b/source/test/ieee1905/data/tlv_AB_mic.pcap new file mode 100644 index 0000000000000000000000000000000000000000..c27599708fd6c0dd8cf301eabf1df8ff43a5887e GIT binary patch literal 115 zcmca|c+)~A1{MZnzzF1g2!9t367z=Q4#z(W7cB;o8#it4v|?arU;wIWSk0jC08+)k e$i&FR%)-i`0AvAaHg*n9E^Z!PK7I^70|Nji?-8N^ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_AC_encrypted_payload.pcap b/source/test/ieee1905/data/tlv_AC_encrypted_payload.pcap new file mode 100644 index 0000000000000000000000000000000000000000..d8cea78af1d5517b25e0fd025b9d1e4d402dedf1 GIT binary patch literal 108 zcmca|c+)~A1{MZnzzF1g2!9t35_5s#4#z(W7cB;o8#it4v|?arU;wIWSi_(JQpCW> q#K^?V!um~o$GXnN3?hGgmM%2Be}3opZ`Zp2T&=jkRq}Z&P!#~N^&zAH literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_AD_cac_request.pcap b/source/test/ieee1905/data/tlv_AD_cac_request.pcap new file mode 100644 index 0000000000000000000000000000000000000000..fd3e0ef7dc8656260729b76bd57973d0c4575dde GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWSj)i0_(goj O-o5`S95_JAu>t^`4HWwT literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_AE_cac_termination.pcap b/source/test/ieee1905/data/tlv_AE_cac_termination.pcap new file mode 100644 index 0000000000000000000000000000000000000000..d30e276fcdf0a1a9f383ada141533e15d07c9c34 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWSjWK0_(goj N-o5`S96+kE0|1*p6!QQ8 literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_AF_cac_completion_report.pcap b/source/test/ieee1905/data/tlv_AF_cac_completion_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..363dd519e53dd5f8f1e34a835cb7050d1a190463 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWSkEBL_(gn2 XTXg Rbpd7uM@AMlVUP|?003ia6Al0X literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_B1_cac_status_report.pcap b/source/test/ieee1905/data/tlv_B1_cac_status_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..b393adf319da0faa7f41fce15a6ef5e1aae42639 GIT binary patch literal 409 zcmYk&IZnes7>41`W(m7;gcKAgqM)EahyspaB$P5u3JawGWlWrzcqu3nL_tG=5QT#% z5TdYi1g^k25C@>(b(fJIYrcP(!rxz2;cHoo$0Pi9e!6w-noygZ{S~uUzGXF^9!t3> z=B0NL1Y80iOW>hk1*>Ra4eQvzCbqCGo$pA4UF=~W2a+Kh?h&Mv&>lfmY(Ac$G&6NvFF%v%#R=OgclT2LQcpD zIjQp--tmc2O6jNcQtBzSjMNYKij?m#bF# literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_B2_cac_capabilities.pcap b/source/test/ieee1905/data/tlv_B2_cac_capabilities.pcap new file mode 100644 index 0000000000000000000000000000000000000000..97042c8104e21ffe18ee8c8b1bed91144c5a3b1a GIT binary patch literal 131 zcmca|c+)~A1{MZnzzF1g2!9t35{ri74#z(W7cB;o8#it4v|?arU;wIW*u-ET8qD}b zd`DY&79$e_gAIEbi;0DeLnTj2MovLVMNLCXN6&mhOjcDu;{o4=CD{M~ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_B3_multiap_profile.pcap b/source/test/ieee1905/data/tlv_B3_multiap_profile.pcap new file mode 100644 index 0000000000000000000000000000000000000000..e17a758469f6162edb4aec899cf7f9af96e0b51b GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW*v!Dl1X6_` F0008=5B>lE literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_B4_profile2_ap_capability.pcap b/source/test/ieee1905/data/tlv_B4_profile2_ap_capability.pcap new file mode 100644 index 0000000000000000000000000000000000000000..b558be6f5dbc84fc373d63f97801bb082fa33c01 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW*aFh&zywl= G2LJ#Iz7RqH literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_B5_default_8021q_settings.pcap b/source/test/ieee1905/data/tlv_B5_default_8021q_settings.pcap new file mode 100644 index 0000000000000000000000000000000000000000..63e10f23bd740292e114ef0b17795d3e60617c11 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW*vi1nz?A?} HhYtV%5cCjg literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_B6_traffic_separation_policy.pcap b/source/test/ieee1905/data/tlv_B6_traffic_separation_policy.pcap new file mode 100644 index 0000000000000000000000000000000000000000..fc9575d92a4e93801cc4c3726585c86aa25c9df6 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW*v25s#GO`D Z7GIKDToRv_!N7&!C1o&(fJ|Uu007LS8G!%* literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_B7_bss_configuration_report.pcap b/source/test/ieee1905/data/tlv_B7_bss_configuration_report.pcap new file mode 100644 index 0000000000000000000000000000000000000000..0dd79adafd074ff0e9794fbf72e7a8cf6ee02474 GIT binary patch literal 140 zcmca|c+)~A1{MZnzzF1g2!9t35=(*N4#z(W7cB;o8#it4v|?arU;wIW*v{a=^hJC} xTX;4jgbHBbDlX1Ui8o4*Ps;!cWid6aI3Ya literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_BE_ap_radio_advanced_capabilities.pcap b/source/test/ieee1905/data/tlv_BE_ap_radio_advanced_capabilities.pcap new file mode 100644 index 0000000000000000000000000000000000000000..b86e42d8ea278ceba1bb8fc9eb71e70de473977e GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW*vG*BMSRD) LwvP=UwYUHPk^mHS literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_BF_association_status_notification.pcap b/source/test/ieee1905/data/tlv_BF_association_status_notification.pcap new file mode 100644 index 0000000000000000000000000000000000000000..68633dc22b039a46a023acaf3f9bb5577b13a746 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIW*w4VvlqJ5S PEj*jy3y8`BX~h5lpyU;# literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_C0_source_info.pcap b/source/test/ieee1905/data/tlv_C0_source_info.pcap new file mode 100644 index 0000000000000000000000000000000000000000..4fe6f8d3e754795cb5ff861414cc44f63ccd0b92 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWIKaT>*KghN KH5{ZACjbCrZW8(c literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_C1_tunneled_message_type.pcap b/source/test/ieee1905/data/tlv_C1_tunneled_message_type.pcap new file mode 100644 index 0000000000000000000000000000000000000000..74aadc7b59c33a53ff4c17450b2efef199d0c149 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWILN>VQcM5< E00iR@3jhEB literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_C2_tunneled.pcap b/source/test/ieee1905/data/tlv_C2_tunneled.pcap new file mode 100644 index 0000000000000000000000000000000000000000..7b07ee51e7205234b32d61d3c79f6de0912bf341 GIT binary patch literal 314 zcmca|c+)~A1{MZnzzF1g2!9t35)%S)K)A#4&%#BEf#k+bn>(!-7#bLWf(?flz9}$R zF@6!>(H5TV*KghNH5|g;P-!U2#ULbXm>2V#fjh0JEWRYQxFkL;gOQ`DrF)u+fk%!J z3y+XQf+Ev*VHGAuUIP)JdHicwkVvKm1~=A14xos&XkbBcNoh@OU427iQ(1XMWmR=x zaEPvyE`#*{|Hw{OgLpMZ)TftZbf-P-_+fgA<~0K7F9T>t<8 literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_C9_status_code.pcap b/source/test/ieee1905/data/tlv_C9_status_code.pcap new file mode 100644 index 0000000000000000000000000000000000000000..cf6cd21331d00dbcaf008c2e11236eb74befed9a GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWILW}ozzb4_ G7XSbU&JZ8~ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_CA_reason_code.pcap b/source/test/ieee1905/data/tlv_CA_reason_code.pcap new file mode 100644 index 0000000000000000000000000000000000000000..fa4fa03ba7d6b11337eb0f1a30833c632a7c89c5 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWIK{xkzzkA` G7XSbTpb!@T literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_CB_backhaul_sta_radio_capabilities.pcap b/source/test/ieee1905/data/tlv_CB_backhaul_sta_radio_capabilities.pcap new file mode 100644 index 0000000000000000000000000000000000000000..8321d6af28a2ee4823f91a805ea64a6d2b1b0473 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWIL*NOMSRD) PwvP?pK-4FY4om<5_!k&+ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_CC_akm_suite_capabilities.pcap b/source/test/ieee1905/data/tlv_CC_akm_suite_capabilities.pcap new file mode 100644 index 0000000000000000000000000000000000000000..2a05143b031bc0873ad76dc7c58dd8f85a03bcfd GIT binary patch literal 110 zcmca|c+)~A1{MZnzzF1g2!9t35_5y%4#z(W7cB;o8#it4v|?arU;wIWIK!aD!oa_V c35eN%m?I#6788g85&%hn1%WgtNCIdQ0DTP^7XSbN literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_CD_1905_encap_dpp.pcap b/source/test/ieee1905/data/tlv_CD_1905_encap_dpp.pcap new file mode 100644 index 0000000000000000000000000000000000000000..4edf8abbd9053dba3019201dd137a131166c2c1a GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWILpA(@I`z_ QTST1%15=|JNCOrC0FgixkpKVy literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_CD_1905_encap_dpp_no_enrollee_mac.pcap b/source/test/ieee1905/data/tlv_CD_1905_encap_dpp_no_enrollee_mac.pcap new file mode 100644 index 0000000000000000000000000000000000000000..267bf7ed35cfe9842cc8c9d48c9ae474786159bd GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWILpAL@SlN= Oi<^g+j~}EQD*ymD5)qF8 literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_CE_1905_encap_eapol.pcap b/source/test/ieee1905/data/tlv_CE_1905_encap_eapol.pcap new file mode 100644 index 0000000000000000000000000000000000000000..97023c1eab6500f8c3f5e919d83d65a5194e474b GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWIL9Esz{teR T!pg?Z!O6wV!^_7H(u4*86j~6T literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_D0_backhaul_bss_configuration.pcap b/source/test/ieee1905/data/tlv_D0_backhaul_bss_configuration.pcap new file mode 100644 index 0000000000000000000000000000000000000000..092db0ed6280b16cb0ee23c6c581bf5fbea4a119 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWxWK@kCBCCA LJi7s;78d{jXK524 literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_D1_dpp_message.pcap b/source/test/ieee1905/data/tlv_D1_dpp_message.pcap new file mode 100644 index 0000000000000000000000000000000000000000..6f1984313c1cf8c296fe2d43bdc23643276ef8ef GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWxX2*Dz{teR T!pg?Z!O6wV!^_7H(u4*86wDBz literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_D2_dpp_cce_indication.pcap b/source/test/ieee1905/data/tlv_D2_dpp_cce_indication.pcap new file mode 100644 index 0000000000000000000000000000000000000000..91efe8e57b1164a29bf6a7d4000dcb0a955240e4 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWxWvE+QcM5< E00(Ih8~^|S literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_D3_dpp_chirp_value.pcap b/source/test/ieee1905/data/tlv_D3_dpp_chirp_value.pcap new file mode 100644 index 0000000000000000000000000000000000000000..99239c8cc205fefd7cb7d79a023f0f77e0c5d09f GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWxXd7N;EVW< Mwum|bkYXGF0EyQWA^-pY literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_D3_dpp_chirp_value_no_enrollee_mac.pcap b/source/test/ieee1905/data/tlv_D3_dpp_chirp_value_no_enrollee_mac.pcap new file mode 100644 index 0000000000000000000000000000000000000000..1b415dfc2283c5f1935af635de1a69af0e46a649 GIT binary patch literal 100 zcmca|c+)~A1{MZnzzF1g2!9t360?Eg4#z(W7cB;o8#it4v|?arU;wIWxXd8rAOKQ^ G7XSbhun>I! literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_D4_device_inventory.pcap b/source/test/ieee1905/data/tlv_D4_device_inventory.pcap new file mode 100644 index 0000000000000000000000000000000000000000..0c17e4c3d7ad8f597151657305a7c7dcaeaaa651 GIT binary patch literal 139 zcmca|c+)~A1{MZnzzF1g2!9t35=(~S4#z(W7cB;o8#it4v|?arU;wIWxWeGh@91n~ zW^8C_VqsupW@u!|Wuj+jWT_ZNxqWr{^TaFhN8aKX=QC=YiIAosXzlEnt`U{)C@GSfsKKoVCziA9qKc_gG>j3 QnN2eoQ8WXc1(5-h0O49kFaQ7m literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_E0_agent_ap_mld_configuration.pcap b/source/test/ieee1905/data/tlv_E0_agent_ap_mld_configuration.pcap new file mode 100644 index 0000000000000000000000000000000000000000..3f984c53adf4ac4794d473d1ae13bd696fbe2b64 GIT binary patch literal 314 zcmca|c+)~A1{MZnzzF1g2!9t35)%S)K)A#4&%#BEf#k+bn>(!-7#bLWf(;KCzA-iM zx)tT;m3SnU<^%_Ox-c*@F|)8Xfb^mRW(I}{K0Yl~5OF4SX%K&ckFVobhy+kICIIT6 pA`cNpbu&ba$0;#68}1UYSOY``l?2+2IQCFB4A literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_E1_backhaul_sta_mld_configuration.pcap b/source/test/ieee1905/data/tlv_E1_backhaul_sta_mld_configuration.pcap new file mode 100644 index 0000000000000000000000000000000000000000..1ded638350c6a55cacceca9e08964111851ef241 GIT binary patch literal 164 zcmca|c+)~A1{MZnzzF1g2!9t3603pY4#z(W7cB;o8#it4v|?arU;wIWc*u|dWH2$a lu!7WrM9~3L1H-D_XYMjD0P0?{6kQg?XL<#cM|A+2G5`d)9L@j$ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_E2_associated_sta_mld_configuration.pcap b/source/test/ieee1905/data/tlv_E2_associated_sta_mld_configuration.pcap new file mode 100644 index 0000000000000000000000000000000000000000..7093853e232566a90668d0bc1a54b7117a6959c4 GIT binary patch literal 193 zcmca|c+)~A1{MZnzzF1g2!9t35}OId9gcq%E?NvEH*VV8X~n?MzyMU$@Q9)INrHHs zi9f>xAD@=$29OR^z+45CnIf?)d(qOfb5Nxqya_(Oj$gI3tu`he!7dFn7itGKc?JNc CP9=Q+ literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_E4_affiliated_sta_metrics.pcap b/source/test/ieee1905/data/tlv_E4_affiliated_sta_metrics.pcap new file mode 100644 index 0000000000000000000000000000000000000000..d843b7f47904a4235d2900bfbbcda4257b3a43cf GIT binary patch literal 1092 zcmca|c+)~A1{MZnzzF1g2!9t35|aT+fpCZ8pM{GS1Idk>Hg{SvFf=d#RW&?eVR(`t j9%tguz`zLPGco}&GY|tcFtQ>WIm#Xlfzc2crXc_TB8d`6 literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_E5_affiliated_ap_metrics.pcap b/source/test/ieee1905/data/tlv_E5_affiliated_ap_metrics.pcap new file mode 100644 index 0000000000000000000000000000000000000000..7980807779e4f92fbaea4fd6b54ca294d128f4f2 GIT binary patch literal 1098 zcmca|c+)~A1{MZnzzF1g2!9t35>o<7fpCZ8pM{GS1Idk>Hg{SvFf=d#RW&?iVSADw t9%tguz`zLPGco}&GY|tcFtP$M8xXStF$WNHB3m)a9u0xf5Exb=003g_5`6#w literal 0 HcmV?d00001 diff --git a/source/test/ieee1905/data/tlv_E8_available_spectrum_inquiry_request.pcap b/source/test/ieee1905/data/tlv_E8_available_spectrum_inquiry_request.pcap new file mode 100644 index 0000000000000000000000000000000000000000..0c08233401f41306c609d18fa28e20cfbdc7240c GIT binary patch literal 1554 zcma)+%}(1u6omtVRbRr&8j$b!pB)6KP^%UNSXC%8$q=S;>|i_4s6t{}^(ne#*=?5H zR;>CERiC2gjDb=@MJ*-DoICe^=iZ6u$G5Lv7F$cLKhIKY0gKPauTH4_T5djz*Vosx z#l_id_I=^gdaHG{`{Jf`^>g{=qtiauWo3%Iz3#MS&2O(b?esh~S$dr5{ju)XHit<~GY&ZTqYTq2egP36Xh&N4HubhGH|ytZ!OFDvq)sV4)5RwHjM ziG0sX?w**1VDNT>@2*8bq8`L8YLvb&%8d_(6BE}(&dB8VNVb0;DCa zc&d7^W6v?~PrGS8WK}!vthl%jKtp!{>2UJ5+*q~)dz9cml@R*M_Bfvq22T>gI6{JR z1h}z(Lp|u6rg^Tj`~TrED~{9b?O2x>T0ZP#X=Nww36lEF=FW++ok4=FWW^B-Q=%0| zK_OT{^PqXqd}wsEwpM-I^}0h|Yr4=sQeKntNo&|+u_v;>;CG%WP0n9f5Hj{%>7fPs*Kh=GWKn1KWV zf*p7*KipG#;3JH_pqqYy5}l-ZY&|x%L=wJ(-#5qkB;FgE+o0hP zC*t$L?3cV!{VJaKsKDpXkFGkOe*W(t|Geus^sJtpot>|)o}Zupy7J??utn5PPzO?i`y^gKx+!_NZWwq~w{pZZI^oS#@(X>$tb`Z5D@z(- zV?2wt*NqJXiLQhr;u(UqE=a#2&gfI+i%!rR97I@fD&rJTEK6puW5CgZbP7R%`)`m5 z^81(%Z`vMQ_m2CR0=J)pi9{QUd%mzV7YD^4pqltqnEMzx4w8*4L4#c1KN zz^d9PwWnK%rx>kaby#v%gH>J;dt~-9F;OhA#DttRU<>}s!g7Xfq{%j@Y1?uVDNYMt z0~XGzY_%uaNXwN?1Nf?UVtL0f4Bvr4NAZVSsE zYnNYww&Dg`IID^vnAa-51f6r5u)MxPDeN!HF9}M7Hf`ao$`+YWtXa!uf@D_Mu&ynR z2W{9&Yu>_Hm2KNhkaQ|)XbWdGSk`K*Qa+tto3K$WWEQp;d%<5)R)w|X<)!(hZO2FjXH{6$=F*r%@Kr@E$rdMK zT-}T7Z+Y6C|LcwR>HJG0&ksl0q@fGufn5Z|Cv$ zt{?999&GJCy1TWr%Y)tR$M=fr2Hlj419m=Z%f!9n)9W!@VsTl}TJQ*<1+in3tlv_type = TLV_TYPE_SEARCHED_ROLE; + } + break; + case I1905_GET_ALMAC_TLV: { + i1905_al_mac_address_tlv_t *tlv = data; + memset(tlv, 0, sizeof(*tlv)); + tlv->tlv_type = TLV_TYPE_AL_MAC_ADDRESS; + } + break; + case I1905_GET_SUPPORTEDROLE_TLV: { + i1905_supported_role_tlv_t *tlv = data; + memset(tlv, 0, sizeof(*tlv)); + tlv->tlv_type = TLV_TYPE_SUPPORTED_ROLE; + } + break; + case I1905_GET_SUPPORTEDFREQBAND_TLV: { + i1905_supported_freq_band_data_t *d = data; + i1905_supported_freq_band_tlv_t *tlv = d->supported_freq_band_tlv; + memset(tlv, 0, sizeof(*tlv)); + tlv->tlv_type = TLV_TYPE_SUPPORTED_FREQ_BAND; + } + break; + case I1905_GET_DEVICEINFO_TLV: { + i1905_device_information_tlv_t *tlv = data; + memset(tlv, 0, sizeof(*tlv)); + tlv->tlv_type = TLV_TYPE_DEVICE_INFORMATION; + } + break; + case I1905_GET_WSCM2_TLV: { + i1905_wsc_data_t *wsc_params = data; + wsc_params->m2.tlv_type = TLV_TYPE_WSC; + wsc_params->m2.wsc_frame_size = 0; + wsc_params->m2.wsc_frame = NULL; + } + default: + break; + } + + return 0; +} + +i1905_bridge_t *i1905_get_list_of_bridges(uint8_t *nr) +{ + *nr = 0; + + return NULL; +} + +void i1905_free_list_of_bridges(i1905_bridge_t *br, uint8_t nr) +{ +} + +void i1905_dump_interfaces(map_printf_cb_t print_cb) +{ + print_cb("eth0\n"); +} diff --git a/source/test/ieee1905/stub/stub_i1905.h b/source/test/ieee1905/stub/stub_i1905.h new file mode 100644 index 0000000..e323424 --- /dev/null +++ b/source/test/ieee1905/stub/stub_i1905.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +#ifndef STUB_I1905_H_ +#define STUB_I1905_H_ + +#include "i1905.h" + +typedef void (*stub_i1905_lldp_send_cb_t)(char *ifname, mac_addr smac, i1905_lldp_payload_t *payload, void *args); + +typedef void (*stub_i1905_cmdu_send_cb_t)(i1905_cmdu_t *cmdu, mac_addr dmac, uint16_t *mid, void *args); + +typedef void (*stub_i1905_raw_send_cb_t)(char *ifname, mac_addr dmac, mac_addr smac, uint16_t eth_type, uint8_t *data, uint16_t data_len, void *args); + +void stub_i1905_register_lldp_send_cb(stub_i1905_lldp_send_cb_t cb, void *args); + +void stub_i1905_register_cmdu_send_cb(stub_i1905_cmdu_send_cb_t cb, void *args); + +void stub_i1905_register_raw_send_cb(stub_i1905_raw_send_cb_t cb, void *args); + +void stub_i1905_reset_send_nr(void); + +int stub_i1905_get_send_nr(void); + +#endif /* STUB_I1905_H_ */ diff --git a/source/test/ieee1905/stub/stub_platform_os.c b/source/test/ieee1905/stub/stub_platform_os.c new file mode 100644 index 0000000..54f70b8 --- /dev/null +++ b/source/test/ieee1905/stub/stub_platform_os.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2020-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +#include "platform_os.h" +#include "stub_platform_os.h" + +#include "al_datamodel.h" + +static mac_addr g_lo_mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static mac_addr g_eth0_mac = {0x00, 0x11, 0x11, 0x11, 0x11, 0x11}; +static mac_addr g_eth1_mac = {0x00, 0x22, 0x22, 0x22, 0x22, 0x22}; +static mac_addr g_eth2_mac = {0x00, 0x33, 0x33, 0x33, 0x33, 0x33}; +static mac_addr g_eth3_mac = {0x00, 0x44, 0x44, 0x44, 0x44, 0x44}; +static mac_addr g_wl0_mac = {0x00, 0x55, 0x55, 0x55, 0x55, 0x55}; + +i1905_packet_cb_t g_stub_platform_os_packet_cb; + +uint8_t PLATFORM_OS_INIT(i1905_interface_cb_t interface_cb, i1905_packet_cb_t packet_cb, i1905_key_info_cb_t key_info_cb) +{ + g_stub_platform_os_packet_cb = packet_cb; + + /* Add some interfaces */ + DMinsertInterface("lo", g_lo_mac); + DMinsertInterface("eth0", g_eth0_mac); + DMinsertInterface("eth1", g_eth1_mac); + DMinsertInterface("eth2", g_eth2_mac); + DMinsertInterface("eth3", g_eth3_mac); + DMinsertInterface("wl0", g_wl0_mac); + + return 1; +} + +void PLATFORM_OS_FINI(void) +{ + g_stub_platform_os_packet_cb = NULL; +} + +void PLATFORM_OS_DUMP_INTERFACES(map_printf_cb_t print_cb) +{ +} + +char **PLATFORM_OS_GET_LIST_OF_1905_INTERFACES(uint8_t *nr) +{ + static char *interfaces[] = {"lo", "eth0", "eth1", "eth2", "eth3", "wl0"}; + *nr = 6; + + return interfaces; +} + +void PLATFORM_OS_FREE_LIST_OF_1905_INTERFACES(char **interfaces, uint8_t nr) +{ +} + +void PLATFORM_OS_GET_1905_INTERFACE_INFO(char *if_name, i1905_interface_info_t *info) +{ + memset(info, 0, sizeof(i1905_interface_info_t)); + + info->interface_type = INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET; + info->power_state = INTERFACE_POWER_STATE_ON; + + if (!strcmp(if_name, "lo")) { + maccpy(info->mac_address, g_lo_mac); + } else if (!strcmp(if_name, "eth0")) { + maccpy(info->mac_address, g_eth0_mac); + } else if (!strcmp(if_name, "eth1")) { + maccpy(info->mac_address, g_eth1_mac); + } else if (!strcmp(if_name, "eth2")) { + maccpy(info->mac_address, g_eth2_mac); + } else if (!strcmp(if_name, "eth3")) { + maccpy(info->mac_address, g_eth3_mac); + } else if (!strcmp(if_name, "wl0")) { + maccpy(info->mac_address, g_wl0_mac); + info->interface_type = INTERFACE_TYPE_IEEE_802_11AC_5_GHZ; + } + + strcpy(info->manufacturer_name, TEST_MANUFACTURER); + strcpy(info->model_name, TEST_MODEL_NAME); + strcpy(info->model_number, TEST_MODEL_NUMBER); + strcpy(info->serial_number, TEST_SERIAL_NUMBER); + + info->is_secured = 1; +} + +bool PLATFORM_OS_IS_INTERFACE_UP(char *if_name) +{ + return true; +} + +int PLATFORM_OS_GET_IFINDEX(char *if_name) +{ + return 123; +} + +void PLATFORM_OS_PACKET_SENT(char *if_name, uint16_t ether_type) +{ +} + +int PLATFORM_OS_GET_RAW_SEND_FD(void) +{ + return 123; +} + +bool PLATFORM_OS_LOG_LEVEL_TRACE(void) +{ + return true; +} + +void PLATFORM_OS_SET_LEADER_SELECTED(bool leader_selected) +{ +} + +/** + * Note: Caller is responsible of freeing 'key_info->ptk' for this stub function !!! + * Below values are taken from a real example, please do not edit unless you have to. + */ +int PLATFORM_OS_GET_KEY_INFO(uint8_t *al_mac, map_1905_sec_key_info_t *key_info) +{ + uint8_t stub_mac[] = {0xf6, 0x17, 0xb8, 0xae, 0x89, 0xa3}; + uint8_t stub_tx_counter[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + uint8_t stub_ptk[] = { + 0x5f, 0xd7, 0x89, 0xa0, 0x42, 0x82, 0xbf, 0xaf, 0xbe, 0x3e, 0x0d, 0x0f, 0x83, 0x0c, 0xf5, 0x33, + 0xd1, 0x03, 0x1b, 0x8d, 0x86, 0xe5, 0x49, 0x95, 0x3e, 0x62, 0x1e, 0x02, 0x91, 0x18, 0x0b, 0x83 + }; + size_t stub_ptk_len = sizeof(stub_ptk); + if (memcmp(al_mac, stub_mac, ETHER_ADDR_LEN) == 0) { + key_info->ptk_len = stub_ptk_len; + memcpy(key_info->ptk, stub_ptk, stub_ptk_len); + memcpy(key_info->encr_tx_counter, stub_tx_counter, ENCRYPTION_TX_COUNTER_LEN); + + return 0; + } + + return -1; +} diff --git a/source/test/ieee1905/stub/stub_platform_os.h b/source/test/ieee1905/stub/stub_platform_os.h new file mode 100644 index 0000000..9bc6a69 --- /dev/null +++ b/source/test/ieee1905/stub/stub_platform_os.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +#ifndef STUB_PLATFORM_OS_H_ +#define STUB_PLATFORM_OS_H_ + +#include "platform_os.h" + +#define TEST_MANUFACTURER "Airties" +#define TEST_MODEL_NAME "Air4960" +#define TEST_MODEL_NUMBER "4960" +#define TEST_SERIAL_NUMBER "12345670" + +extern i1905_packet_cb_t g_stub_platform_os_packet_cb; + +#endif /* STUB_PLATFORM_OS_H_ */ diff --git a/source/test/ieee1905/test_al_send.c b/source/test/ieee1905/test_al_send.c new file mode 100644 index 0000000..375a9e2 --- /dev/null +++ b/source/test/ieee1905/test_al_send.c @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2019-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "test.h" + +#include "i1905.h" +#include "al_send.h" +#include "al_datamodel.h" + +#include "stub/stub_platform_os.h" + +/*####################################################################### +# GLOBALS # +########################################################################*/ +static mac_addr g_al_mac = {0x02, 0x01, 0x02, 0x03, 0x04, 0x05}; +static mac_addr g_src_mac = {0x00, 0x11, 0x12, 0x13, 0x14, 0x15}; +static mac_addr g_dst_mac = {0x00, 0x21, 0x22, 0x23, 0x24, 0x25}; +static mac_addr g_lldp_mcast_mac = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e}; + +static char *g_ifname = "wl0"; +static int g_cb_count; + +static uint8_t g_lldp_payload[24] = {/* chassid_id */ 0x02, 0x07, 0x04, 0x02, 0x01, 0x02, 0x03, 0x04, 0x05, + /* port_id */ 0x04, 0x07, 0x03, 0x12, 0x11, 0x12, 0x13, 0x14, 0x15, + /* time_to_live */ 0x06, 0x02, 0x00, 0xb4, + /* end_of_lldp */ 0x00, 0x00 + }; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static void test_init(i1905_unit_test_send_cb_t send_cb) +{ + DMinit(); + DMalMacSet(g_al_mac); + PLATFORM_OS_INIT(NULL, NULL, NULL); + PLATFORM_REGISTER_UNIT_TEST_SEND_CB(send_cb); + + g_cb_count = 0; +} + +static void test_fini(void) +{ + PLATFORM_REGISTER_UNIT_TEST_SEND_CB(NULL); + PLATFORM_OS_FINI(); + DMfini(); +} + +/*####################################################################### +# TEST_OBTAIN_TLV_FROM_1905 # +########################################################################*/ +static void check_local_interface(char *ifname, i1905_local_interface_entry_t *local_interface) +{ + i1905_interface_info_t info; + PLATFORM_OS_GET_1905_INTERFACE_INFO(ifname, &info); + + fail_unless(!maccmp(local_interface->mac_address, info.mac_address)); + fail_unless(local_interface->media_type == info.interface_type); + + if (INTERFACE_TYPE_GROUP_WLAN == INTERFACE_TYPE_GROUP_GET(local_interface->media_type)) { + fail_unless(local_interface->media_specific_data_size == 10); + } +} + +START_TEST(test_obtain_tlv_from_1905) +{ + i1905_al_mac_address_tlv_t *al_mac_address_tlv = NULL; + i1905_searched_role_tlv_t *searched_role_tlv = NULL; + i1905_autoconfig_freq_band_tlv_t *autoconfig_freq_band_tlv = NULL; + i1905_supported_role_tlv_t *supported_role_tlv = NULL; + i1905_device_information_tlv_t *device_information_tlv = NULL; + i1905_supported_freq_band_data_t s_data = { 0 }; + i1905_wsc_data_t w_data = { 0 }; + map_profile_cfg_t profile = { 0 }; + i1905_wsc_m2_cfg_t m2_cfg = {.profile = &profile}; + + test_init(NULL); + + /* INVALID */ + fail_unless(obtainTLVFrom1905(g_ifname, I1905_GET_ALMAC_TLV, NULL)); + fail_unless(obtainTLVFrom1905(g_ifname, 9999, (void*)0xdeadbeef)); + + + /* I1905_GET_ALMAC_TLV */ + fail_unless(!!(al_mac_address_tlv = calloc(1, sizeof(*al_mac_address_tlv)))); + fail_unless(!obtainTLVFrom1905(g_ifname, I1905_GET_ALMAC_TLV, al_mac_address_tlv)); + fail_unless(al_mac_address_tlv->tlv_type == TLV_TYPE_AL_MAC_ADDRESS); + fail_unless(!maccmp(al_mac_address_tlv->al_mac_address, g_al_mac)); + free_1905_TLV_structure((uint8_t*)al_mac_address_tlv); + + + /* I1905_GET_SEARCHEDROLE_TLV */ + fail_unless(!!(searched_role_tlv = calloc(1, sizeof(*searched_role_tlv)))); + fail_unless(!obtainTLVFrom1905(g_ifname, I1905_GET_SEARCHEDROLE_TLV, searched_role_tlv)); + fail_unless(searched_role_tlv->tlv_type == TLV_TYPE_SEARCHED_ROLE); + fail_unless(searched_role_tlv->role == IEEE80211_ROLE_AP); + free_1905_TLV_structure((uint8_t*)searched_role_tlv); + + + /* I1905_GET_FREQUENCYBAND_TLV */ + fail_unless(!!(autoconfig_freq_band_tlv = calloc(1, sizeof(*autoconfig_freq_band_tlv)))); + fail_unless(!obtainTLVFrom1905(g_ifname, I1905_GET_FREQUENCYBAND_TLV, autoconfig_freq_band_tlv)); + fail_unless(autoconfig_freq_band_tlv->tlv_type == TLV_TYPE_AUTOCONFIG_FREQ_BAND); + fail_unless(autoconfig_freq_band_tlv->freq_band == IEEE80211_FREQUENCY_BAND_5_GHZ); /* Derived from interface list - see stub_platform_os.c */ + free_1905_TLV_structure((uint8_t*)autoconfig_freq_band_tlv); + + + /* I1905_GET_SUPPORTEDROLEBAND_TLV */ + fail_unless(!!(supported_role_tlv = calloc(1, sizeof(*supported_role_tlv)))); + fail_unless(!obtainTLVFrom1905(g_ifname, I1905_GET_SUPPORTEDROLE_TLV, supported_role_tlv)); + fail_unless(supported_role_tlv->tlv_type == TLV_TYPE_SUPPORTED_ROLE); + fail_unless(supported_role_tlv->role == IEEE80211_ROLE_AP); + free_1905_TLV_structure((uint8_t*)supported_role_tlv); + + + /* I1905_GET_SUPPORTEDFREQBAND_TLV */ + fail_unless(!!(s_data.supported_freq_band_tlv = calloc(1, sizeof(*s_data.supported_freq_band_tlv)))); + s_data.freq_band = IEEE80211_FREQUENCY_BAND_2_4_GHZ; + fail_unless(!obtainTLVFrom1905(g_ifname, I1905_GET_SUPPORTEDFREQBAND_TLV, &s_data)); + fail_unless(s_data.supported_freq_band_tlv->tlv_type == TLV_TYPE_SUPPORTED_FREQ_BAND); + fail_unless(s_data.supported_freq_band_tlv->freq_band == IEEE80211_FREQUENCY_BAND_2_4_GHZ); + free_1905_TLV_structure((uint8_t*)s_data.supported_freq_band_tlv); + + + /* I1905_GET_DEVICEINFO_TLV */ + /* See stub_platform_os.c for interface list */ + fail_unless(!!(device_information_tlv = calloc(1, sizeof(*device_information_tlv)))); + fail_unless(!obtainTLVFrom1905(g_ifname, I1905_GET_DEVICEINFO_TLV, device_information_tlv)); + fail_unless(device_information_tlv->tlv_type == TLV_TYPE_DEVICE_INFORMATION); + fail_unless(!maccmp(device_information_tlv->al_mac_address, g_al_mac)); + fail_unless(device_information_tlv->local_interfaces_nr == 6); + check_local_interface("eth0", &device_information_tlv->local_interfaces[1]); + check_local_interface("wl0", &device_information_tlv->local_interfaces[5]); + free_1905_TLV_structure((uint8_t*)device_information_tlv); + + + /* I1905_GET_WSCM1_TLV */ + fail_unless(!obtainTLVFrom1905(g_ifname, I1905_GET_WSCM1_TLV, &w_data)); + fail_unless(!!(w_data.m1.tlv_type == TLV_TYPE_WSC)); + fail_unless(!!w_data.m1.wsc_frame); + fail_unless(w_data.m1.wsc_frame_size > 0); + + + /* I1905_GET_WSCM2_TLV */ + w_data.m2_cfg = &m2_cfg; + fail_unless(!obtainTLVFrom1905(g_ifname, I1905_GET_WSCM2_TLV, &w_data)); + fail_unless(!!(w_data.m2.tlv_type == TLV_TYPE_WSC)); + fail_unless(!!w_data.m2.wsc_frame); + fail_unless(w_data.m2.wsc_frame_size > 0); + free(w_data.m1.wsc_frame); + free(w_data.m2.wsc_frame); + free(w_data.wsc_key->key); + free(w_data.wsc_key); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_1905_RAW_PACKET # +########################################################################*/ +static void raw_send_cb(char *ifname, uint8_t *payload, uint16_t payload_len) +{ + g_cb_count++; + + fail_unless(!strcmp(ifname, g_ifname)); + + fail_unless(payload_len == /* eth */ 14 + /* cmdu */ 8 + /* al_mac_tlv*/ 9 + /* eom_tlv */ 3); + + /* ETH header */ + fail_unless(!maccmp(&payload[0], g_dst_mac)); + fail_unless(!maccmp(&payload[6], g_cb_count == 1 ? g_al_mac : g_src_mac)); + fail_unless((payload[12] << 8) + payload[13] == 0x893a); + + /* CMDU header */ + cmdu_hdr_t *cmdu_hdr = (cmdu_hdr_t *)&payload[14]; + fail_unless(cmdu_hdr->message_version == CMDU_MESSAGE_VERSION_1905_1_2013); + fail_unless(cmdu_hdr->message_type == htons(CMDU_TYPE_TOPOLOGY_NOTIFICATION)); + fail_unless(cmdu_hdr->message_id == htons(0x123)); + fail_unless(cmdu_hdr->fragment_id == 0); + fail_unless(cmdu_hdr->indicators == 0x80); /* last fragment */ + + /* TLV */ + tlv_hdr_t *tlv_hdr = (tlv_hdr_t*)&payload[22]; + fail_unless(tlv_hdr->type == TLV_TYPE_AL_MAC_ADDRESS); + fail_unless(tlv_hdr->len == htons(6)); + fail_unless(!maccmp(&tlv_hdr[1], g_al_mac)); + + tlv_hdr = (tlv_hdr_t*)&payload[31]; + fail_unless(tlv_hdr->type == TLV_TYPE_END_OF_MESSAGE); + fail_unless(tlv_hdr->len == htons(0)); +} + +START_TEST(test_send_1905_raw_packet) +{ + i1905_cmdu_t cmdu = {0}; + i1905_al_mac_address_tlv_t al_mac_addr_tlv = {.tlv_type = TLV_TYPE_AL_MAC_ADDRESS}; + uint8_t *tlvs[2] = {(uint8_t*)&al_mac_addr_tlv, NULL}; + + test_init(raw_send_cb); + + /* Create a cmdu */ + maccpy(al_mac_addr_tlv.al_mac_address, g_al_mac); + + cmdu.message_version = CMDU_MESSAGE_VERSION_1905_1_2013; + cmdu.message_type = CMDU_TYPE_TOPOLOGY_NOTIFICATION; + cmdu.message_id = 0x123; + cmdu.relay_indicator = RELAY_INDICATOR_OFF; + cmdu.list_of_TLVs = tlvs; + maccpy(cmdu.cmdu_stream.src_mac_addr, g_src_mac); + strcpy(cmdu.interface_name, "eth0"); + + fail_unless(send1905RawPacket(g_ifname, 0x123, g_dst_mac, &cmdu) == 1); + fail_unless(g_cb_count == 1); + fail_unless(forward1905RawPacket(g_ifname, 0x123, g_dst_mac, &cmdu, /* cmdu mac as src mac */true) == 1); + fail_unless(g_cb_count == 2); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_ENCRYPTED_PACKET # +########################################################################*/ +static void encrypted_send_cb(char *ifname, uint8_t *payload, uint16_t payload_len) +{ + uint16_t siv_len = 0x0014; + uint8_t siv_output[] = {0xfc, 0x4c, 0xa5, 0xa1, 0x31, 0xdf, 0xcf, 0xb9, 0xf7, 0xf6, 0xd6, 0x8b, 0xfc, 0xd5, 0x78, 0xd0, 0x0a, 0x74, 0xf3, 0xb5}; + uint8_t encr_tx_counter[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + mac_addr dst_mac = {0xf6, 0x17, 0xb8, 0xae, 0x89, 0xa3}; + fail_unless(!strcmp(ifname, g_ifname)); + fail_unless(payload_len == /* eth */ 14 + /* cmdu */ 8 + /* encrypted_payload_tlv */ 43 + /* eom_tlv */ 3); + /* ETH header */ + fail_unless(!maccmp(&payload[0], dst_mac)); + fail_unless(!maccmp(&payload[6], g_al_mac)); + fail_unless((payload[12] << 8) + payload[13] == 0x893a); + /* CMDU header */ + cmdu_hdr_t *cmdu_hdr = (cmdu_hdr_t *)&payload[14]; + fail_unless(cmdu_hdr->message_version == CMDU_MESSAGE_VERSION_1905_1_2013); + fail_unless(cmdu_hdr->message_type == htons(CMDU_TYPE_TOPOLOGY_QUERY)); + fail_unless(cmdu_hdr->message_id == htons(0x79cd)); + fail_unless(cmdu_hdr->fragment_id == 0); + fail_unless(cmdu_hdr->indicators == 0x80); /* last fragment */ + /* TLV */ + tlv_hdr_t *tlv_hdr = (tlv_hdr_t*)&payload[22]; + uint8_t *p = (uint8_t *)&tlv_hdr[1]; + fail_unless(tlv_hdr->type == TLV_TYPE_ENCRYPTED_PAYLOAD); + fail_unless(tlv_hdr->len == htons(40)); + fail_unless(!memcmp(p, encr_tx_counter, ENCRYPTION_TX_COUNTER_LEN)); + p += ENCRYPTION_TX_COUNTER_LEN; + fail_unless(!memcmp(p, g_al_mac, ETHER_ADDR_LEN)); + p += ETHER_ADDR_LEN; + fail_unless(!memcmp(p, dst_mac, ETHER_ADDR_LEN)); + p += ETHER_ADDR_LEN; + fail_unless((*p << 8) + p[1] == siv_len); + p += 2; + fail_unless(!memcmp(p, siv_output, siv_len)); + tlv_hdr = (tlv_hdr_t*)&payload[65]; + fail_unless(tlv_hdr->type == TLV_TYPE_END_OF_MESSAGE); + fail_unless(tlv_hdr->len == htons(0)); +} +START_TEST(test_send_encrypted_packet) +{ + i1905_cmdu_t cmdu = {0}; + map_multiap_profile_tlv_t profile_tlv = {.tlv_type = TLV_TYPE_MULTIAP_PROFILE, .map_profile = MAP_PROFILE_2}; + uint8_t *tlvs[2] = {(uint8_t*)&profile_tlv, NULL}; + mac_addr dst_mac = {0xf6, 0x17, 0xb8, 0xae, 0x89, 0xa3}; + test_init(encrypted_send_cb); + /* Create a cmdu */ + cmdu.message_version = CMDU_MESSAGE_VERSION_1905_1_2013; + cmdu.message_type = CMDU_TYPE_TOPOLOGY_QUERY; + cmdu.message_id = 0x79cd; + cmdu.relay_indicator = RELAY_INDICATOR_OFF; + cmdu.list_of_TLVs = tlvs; + maccpy(cmdu.cmdu_stream.src_mac_addr, g_al_mac); + strcpy(cmdu.interface_name, "eth0"); + fail_unless(forward1905RawPacket(g_ifname, 0x123, dst_mac, &cmdu, true) == 1); + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SEND_LLDP_BRIDGE_DISCOVERY_PACKET # +########################################################################*/ +static void lldp_send_cb(char *ifname, uint8_t *payload, uint16_t payload_len) +{ + g_cb_count++; + + fail_unless(!strcmp(ifname, g_ifname)); + + fail_unless(payload_len == /* eth */ 14 + /* tlvs */ sizeof(g_lldp_payload)); + + /* ETH header */ + fail_unless(!maccmp(&payload[0], g_lldp_mcast_mac)); + fail_unless(!maccmp(&payload[6], g_src_mac)); + fail_unless((payload[12] << 8) + payload[13] == 0x88cc); + + /* TLVS */ + fail_unless(!memcmp(&payload[14], g_lldp_payload, sizeof(g_lldp_payload))); +} + +START_TEST(test_send_lldp_bridge_discovery_packet) +{ + i1905_lldp_payload_t *p_payload; + + test_init(lldp_send_cb); + + fail_unless(!!(p_payload = parse_lldp_PAYLOAD_from_packet(g_lldp_payload))); + + fail_unless(sendLLDPBridgeDiscoveryPacket(g_ifname, g_src_mac, p_payload) == 1); + + free_lldp_PAYLOAD_structure(p_payload); + test_fini(); +} +END_TEST + + +const char *test_suite_name = "al_send"; +test_case_t test_cases[] = { + TEST("obtain_tlv_from_1905", test_obtain_tlv_from_1905 ), + TEST("send_1905_raw_packet", test_send_1905_raw_packet ), + TEST("send_encrypted_packet", test_send_encrypted_packet ), + TEST("send_lldp_bridge_discovery_packet", test_send_lldp_bridge_discovery_packet ), + TEST_CASES_END +}; diff --git a/source/test/ieee1905/test_al_wsc.c b/source/test/ieee1905/test_al_wsc.c new file mode 100644 index 0000000..913972a --- /dev/null +++ b/source/test/ieee1905/test_al_wsc.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2019-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include +#include + +#include "test.h" + +#include "al_wsc.h" +#include "al_datamodel.h" +#include "1905_tlvs.h" +#include "map_emex_tlvs.h" +#include "map_config.h" +#include "platform_os.h" + +#include "stub/stub_platform_os.h" + +/*####################################################################### +# GLOBALS # +########################################################################*/ +static mac_addr g_controller_al_mac = {0x02, 0x01, 0x02, 0x03, 0x04, 0x05}; +static mac_addr g_agent_al_mac = {0x02, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5}; + +static map_profile_cfg_t g_profile = { + .type = MAP_PROFILE_TYPE_GUEST, + .label = "test_label", + .bss_ssid = "test_ssid", + .wpa_key = "test_wpa_key", + .supported_auth_modes = IEEE80211_AUTH_MODE_WPA2PSK, + .supported_encryption_types = IEEE80211_ENCRYPTION_MODE_AES, + .bss_state = MAP_FRONTHAUL_BSS, + .hide = true +}; + +/* Set in configure_ap_cb */ +static char g_ssid[MAX_SSID_LEN]; +static char g_network_key[MAX_WIFI_PASSWORD_LEN]; +static uint16_t g_auth_mode; +static uint16_t g_encryption_mode; +static uint8_t g_map_ext; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static void configure_ap_cb(char *if_name, uint8_t *ssid, uint8_t *bssid, + uint16_t auth_mode, uint16_t encryption_mode, uint8_t *network_key, + uint8_t map_ext) +{ + snprintf(g_ssid, sizeof(g_ssid), "%s", ssid); + snprintf(g_network_key, sizeof(g_network_key), "%s", network_key); + g_auth_mode = auth_mode; + g_encryption_mode = encryption_mode; + g_map_ext = map_ext; +} + +static uint8_t *get_attr(uint8_t *m, uint16_t size, uint16_t attr_type, uint16_t *attr_len) +{ + uint8_t *p = m; + + while (p - m < size) { + uint16_t t; + + _E2B(&p, &t); + _E2B(&p, attr_len); + + if (t == attr_type) { + return p; + } + p += *attr_len; + } + + return NULL; +} + +/* Attributes that are both in M1 and M2 */ +static void check_m1_m2(uint8_t *m, uint16_t size) +{ + uint8_t *a; + uint16_t len; + + fail_unless(!!(a = get_attr(m, size, WSC_ATTR_MANUFACTURER, &len))); + fail_unless(len == strlen(TEST_MANUFACTURER)); + fail_unless(!memcmp(a, (char*)TEST_MANUFACTURER, strlen(TEST_MANUFACTURER))); + + fail_unless(!!(a = get_attr(m, size, WSC_ATTR_MODEL_NAME, &len))); + fail_unless(len == strlen(TEST_MODEL_NAME)); + fail_unless(!memcmp(a, (char*)TEST_MODEL_NAME, strlen(TEST_MODEL_NAME))); + + fail_unless(!!(a = get_attr(m, size, WSC_ATTR_MODEL_NUMBER, &len))); + fail_unless(len == strlen(TEST_MODEL_NUMBER)); + fail_unless(!memcmp(a, (char*)TEST_MODEL_NUMBER, strlen(TEST_MODEL_NUMBER))); + + fail_unless(!!(a = get_attr(m, size, WSC_ATTR_SERIAL_NUMBER, &len))); + fail_unless(len == strlen(TEST_SERIAL_NUMBER)); + fail_unless(!memcmp(a, (char*)TEST_SERIAL_NUMBER, strlen(TEST_SERIAL_NUMBER))); + + /* TODO: check all attributes */ +} + +static void check_m1(uint8_t *m, uint16_t size) +{ + uint8_t *a; + uint16_t len; + + check_m1_m2(m, size); + + fail_unless(!!(a = get_attr(m, size, WSC_ATTR_MAC_ADDR, &len))); + fail_unless(len == sizeof(mac_addr)); + fail_unless(!maccmp(a, DMalMacGet())); +} + +static void check_m2(uint8_t *m, uint16_t size) +{ + check_m1_m2(m, size); +} + +/*####################################################################### +# TEST_WSC # +########################################################################*/ +START_TEST(test_wsc) +{ + uint8_t *m1, *m2; + uint16_t m1_size, m2_size; + i1905_wsc_key_t *key; + + /* Init */ + DMinit(); + PLATFORM_OS_INIT(NULL, NULL, NULL); + PLATFORM_REGISTER_UNIT_TEST_CONFIGURE_AP_CB(configure_ap_cb); + + + /* Build M1 (on agent) */ + DMalMacSet(g_agent_al_mac); + fail_unless(wscBuildM1("eth0", &m1, &m1_size, (void**)&key) == 1); + check_m1(m1, m1_size); + + + /* Build M2 (on controller) */ + DMalMacSet(g_controller_al_mac); + fail_unless(wscBuildM2(m1, m1_size, &m2, &m2_size, &g_profile, WSC_WFA_MAP_ATTR_FLAG_BACKHAUL_BSS, "eth1") == 1); + check_m2(m2, m2_size); + + + /* Process M2 (on agent) */ + DMalMacSet(g_agent_al_mac); + fail_unless(wscProcessM2(key, m1, m1_size, m2, m2_size) == 1); + + /* Validate received config */ + fail_unless(!strcmp(g_ssid, g_profile.bss_ssid)); + fail_unless(!strcmp(g_network_key, g_profile.wpa_key)); + fail_unless(g_auth_mode == g_profile.supported_auth_modes); + fail_unless(g_encryption_mode == g_profile.supported_encryption_types); + fail_unless(g_map_ext == WSC_WFA_MAP_ATTR_FLAG_BACKHAUL_BSS); + + + /* Cleanup */ + free(m1); + free(key->key); + free(key); + wscFreeM2(m2, m2_size); + DMfini(); + PLATFORM_OS_FINI(); +} +END_TEST + + +const char *test_suite_name = "al_wsc"; +test_case_t test_cases[] = { + TEST("wsc", test_wsc ), + TEST_CASES_END +}; diff --git a/source/test/ieee1905/test_cmdus.c b/source/test/ieee1905/test_cmdus.c new file mode 100644 index 0000000..f8c7c45 --- /dev/null +++ b/source/test/ieee1905/test_cmdus.c @@ -0,0 +1,824 @@ +/* + * Copyright (c) 2019-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include +#include + +#include "test.h" + +#include "1905_cmdus.h" +#include "1905_tlvs.h" +#include "map_tlvs.h" +#include "al.h" +#include "al_utils.h" +#include "al_send.h" + +#include "utils.h" /* print_callback */ +#include "platform.h" /* PLATFORM_PRINTF */ + +#include "stub/stub_platform_os.h" + +/*####################################################################### +# DEFINES # +########################################################################*/ +#define MAX_CMDU 256 +#define MAX_PACKETS 256 + +#define VARS \ + packet_t **packets; \ + size_t packets_nr; + +#define READ(file) \ + fail_unless(!!(packets = pcap_read_all_packets(DATA_DIR "/" file, &packets_nr))); \ + set_src_dest_mac(packets, packets_nr); + +#define READ_PARSE(file) \ + READ(file) \ + process_packets(packets, packets_nr); + +#define INIT \ + log_test_i("######## %s", __FUNCTION__); \ + start1905AL(g_al_mac, 0, "eth0", NULL, cmdu_cb, NULL); \ + PLATFORM_REGISTER_UNIT_TEST_SEND_CB(send_cb); + +#define SEND_COMPARE \ + send_cmdus(); \ + compare_cmdu_packets(packets, packets_nr, g_tx_packets, g_tx_packets_nr); + +#define CLEANUP \ + free_packets(packets, packets_nr); \ + free_rx_cmdus(); \ + free_tx_packets(); \ + stop1905AL(); + +/*####################################################################### +# GLOBALS # +########################################################################*/ +static mac_addr g_al_mac = {0x02, 0x01, 0x02, 0x03, 0x04, 0x05}; + +static mac_addr g_dest_mac = {0x02, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5}; + +static i1905_cmdu_t *g_rx_cmdus[MAX_CMDU]; +static size_t g_rx_cmdus_nr; + +static packet_t *g_tx_packets[MAX_PACKETS]; +static size_t g_tx_packets_nr; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +/* Set dest mac to ale mac unless it is 1905 or lldp mcast */ +static void set_src_dest_mac(packet_t **p, size_t nr) +{ + size_t i; + + for (i = 0; i < nr; i++) { + eth_hdr_t *eh = (eth_hdr_t *)p[i]->data; + + if (maccmp(eh->ether_dhost, g_mcast_mac_1905) && maccmp(eh->ether_dhost, g_mcast_mac_lldp)) { + maccpy(eh->ether_dhost, g_al_mac); + } + } +} + +static void send_cb(char *if_name, uint8_t *payload, uint16_t payload_len) +{ + packet_t *p = calloc(1, sizeof(packet_t)); + + fail_unless(payload_len <= sizeof(p->data)); + + strcpy(p->if_name, if_name); + p->len = payload_len; + memcpy(p->data, payload, payload_len); + + g_tx_packets[g_tx_packets_nr++] = p; +} + +static bool cmdu_cb(i1905_cmdu_t *cmdu) +{ + g_rx_cmdus[g_rx_cmdus_nr++] = cmdu; + return true; +} + +static void process_packets(packet_t **p, size_t nr) +{ + size_t i; + + fail_unless(NULL != g_stub_platform_os_packet_cb); + + for (i = 0; i < nr; i++) { + g_stub_platform_os_packet_cb("eth0", p[i]->data, p[i]->len); + } +} + +static void check_tlv_sequence(i1905_cmdu_t *cmdu, size_t tlv_len, size_t exp_tlv_nr) +{ + uint8_t *tlv; + size_t idx; + + i1905_foreach_tlv_in_cmdu(tlv, cmdu, idx) { + i1905_vendor_specific_tlv_t *t = (i1905_vendor_specific_tlv_t *)tlv; + uint8_t *p = t->m; + uint16_t v; + + fail_unless(t->tlv_type == TLV_TYPE_VENDOR_SPECIFIC); + fail_unless(t->m_nr == tlv_len); + + /* Payload is tlv index in 16 bit */ + _E2B(&p, &v); + fail_unless(idx == v); + } + fail_unless(idx == exp_tlv_nr); +} + +static void send_cmdus(void) +{ + size_t i; + + for (i = 0; i < g_rx_cmdus_nr; i++) { + i1905_cmdu_t *cmdu = g_rx_cmdus[i]; + fail_unless(send1905RawPacket("eth0", cmdu->message_id, g_dest_mac, cmdu) == 1); + } +} + +static void set_mid(packet_t **p, size_t nr, uint16_t mid) +{ + size_t i; + + for (i = 0; i < nr; i++) { + cmdu_hdr_t *ch1 = (cmdu_hdr_t *)(p[i]->data + sizeof(eth_hdr_t)); + ch1->message_id = htons(mid); + } +} + +/* Compare 2 arrays of packets containing CMDU. Can be fragmented and in the wrong order */ +static void compare_cmdu_packets(packet_t **p1, size_t p1_nr, packet_t **p2, size_t p2_nr) +{ + size_t i, j; + + fail_unless(p1_nr == p2_nr); + + /* Fail if there are too short or non CMDU packets */ + for (i = 0; i < p1_nr; i++) { + eth_hdr_t *eh = (eth_hdr_t *)p1[i]->data; + fail_unless(p1[i]->len >= sizeof(eth_hdr_t) + sizeof(cmdu_hdr_t)); + fail_unless(eh->ether_type == htons(ETHERTYPE_1905)); + + eh = (eth_hdr_t *)p2[i]->data; + fail_unless(p2[i]->len >= sizeof(eth_hdr_t) + sizeof(cmdu_hdr_t)); + fail_unless(eh->ether_type == htons(ETHERTYPE_1905)); + } + + /* Compare but skip eth source and dest mac. */ + for (i = 0; i < p1_nr; i++) { + cmdu_hdr_t *ch1 = (cmdu_hdr_t *)(p1[i]->data + sizeof(eth_hdr_t)); + + log_test_i("compare cmdu: find packet[%zu] mid[0x%02X] frag_id[%d] len[%d]", i, htons(ch1->message_id), ch1->fragment_id, p1[i]->len); + for (j = 0; j < p1_nr; j++) { + cmdu_hdr_t *ch2 = (cmdu_hdr_t *)(p2[j]->data + sizeof(eth_hdr_t)); + + log_test_i(" check packet[%zu] mid[0x%02X] frag_id[%d] len[%d]", j, htons(ch2->message_id), ch2->fragment_id, p2[j]->len); + if (ch1->message_id == ch2->message_id && ch1->fragment_id == ch2->fragment_id) { + log_test_i(" found!"); + fail_unless(p1[i]->len == p2[j]->len); + fail_unless(!memcmp(ch1, ch2, p1[i]->len - sizeof(eth_hdr_t))); + break; + } + } + fail_unless(j < p1_nr); + } +} + +static void free_rx_cmdus(void) +{ + size_t i; + + for (i = 0; i < g_rx_cmdus_nr; i++) { + free_1905_CMDU_structure(g_rx_cmdus[i]); + g_rx_cmdus[i] = NULL; + } + + g_rx_cmdus_nr = 0; +} + +static void free_tx_packets(void) +{ + size_t i; + + for (i = 0; i < g_tx_packets_nr; i++) { + free(g_tx_packets[i]); + g_tx_packets[i] = NULL; + } + + g_tx_packets_nr = 0; +} + +/*####################################################################### +# TEST_UNFRAGMENTED # +########################################################################*/ +START_TEST(test_unfragmented) +{ + VARS + INIT + READ_PARSE("cmdu_topology_response.pcap") + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_type == CMDU_TYPE_TOPOLOGY_RESPONSE); + fail_unless(*cmdu->list_of_TLVs[0] == TLV_TYPE_DEVICE_INFORMATION); + fail_unless(*cmdu->list_of_TLVs[1] == TLV_TYPE_DEVICE_BRIDGING_CAPABILITY); + fail_unless(*cmdu->list_of_TLVs[2] == TLV_TYPE_NON_1905_NEIGHBOR_DEVICE_LIST); + fail_unless(*cmdu->list_of_TLVs[3] == TLV_TYPE_NEIGHBOR_DEVICE_LIST); + fail_unless(*cmdu->list_of_TLVs[4] == TLV_TYPE_NEIGHBOR_DEVICE_LIST); + fail_unless(*cmdu->list_of_TLVs[5] == TLV_TYPE_SUPPORTED_SERVICE); + fail_unless(*cmdu->list_of_TLVs[6] == TLV_TYPE_AP_OPERATIONAL_BSS); + fail_unless(*cmdu->list_of_TLVs[7] == TLV_TYPE_MULTIAP_PROFILE); + fail_unless(*cmdu->list_of_TLVs[8] == TLV_TYPE_VENDOR_SPECIFIC); + fail_unless(cmdu->list_of_TLVs[9] == NULL); + + SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_FRAGMENTED_ORDERED # +########################################################################*/ +/* Fragments are in normal order */ +START_TEST(test_fragmented_ordered) +{ + VARS + INIT + READ_PARSE("cmdu_fragmented_ordered.pcap") + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_id == 0x100); + check_tlv_sequence(cmdu, 512, 10); + + SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_FRAGMENTED_REVERSED # +########################################################################*/ +/* Fragments are in reverse order */ +START_TEST(test_fragmented_reversed) +{ + VARS + INIT + READ_PARSE("cmdu_fragmented_reversed.pcap") + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_id == 0x101); + check_tlv_sequence(cmdu, 512, 10); + + SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_FRAGMENTED_SCRAMBLED # +########################################################################*/ +/* Fragments are scrambled */ +START_TEST(test_fragmented_scrambled) +{ + VARS + INIT + READ_PARSE("cmdu_fragmented_scrambled.pcap") + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_id == 0x102); + check_tlv_sequence(cmdu, 512, 10); + + SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_FRAGMENTED_MIXED # +########################################################################*/ +/* 3 fragmented CMDU with fragments mixed */ +START_TEST(test_fragmented_mixed) +{ + VARS + INIT + READ_PARSE("cmdu_fragmented_mixed.pcap") + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 3); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_id == 0x100); + check_tlv_sequence(cmdu, 512, 10); + + cmdu = g_rx_cmdus[1]; + fail_unless(cmdu->message_id == 0x101); + check_tlv_sequence(cmdu, 512, 10); + + cmdu = g_rx_cmdus[2]; + fail_unless(cmdu->message_id == 0x102); + check_tlv_sequence(cmdu, 512, 10); + + SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_FRAGMENTED_EOM_IN_ALL_FRAG # +########################################################################*/ +/* Some agents add EOM in all fragments */ +START_TEST(test_fragmented_eom_in_all_frag) +{ + VARS + INIT + READ_PARSE("cmdu_fragmented_eom_in_all_frag.pcap") + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_id == 0x100); + check_tlv_sequence(cmdu, 512, 10); + + /* Can't do SEND_COMPARE, it will fail... */ + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_FRAGMENTED_LOST # +########################################################################*/ +/* Fragmented with lost packets, test that they are discarded... + Currently 8 sets of fragments are stored (not enough??) +*/ +#define MAX_CMDU_IN_FLIGHT 8 +START_TEST(test_fragmented_lost) +{ + VARS + INIT + READ("cmdu_fragmented_ordered.pcap"); + + /* Process 10 times without last packet, incremented MID */ + uint16_t mid = 1000; + int i; + for (i = 0; i < MAX_CMDU_IN_FLIGHT + 2; i++) { + set_mid(packets, packets_nr, mid++); + process_packets(packets, packets_nr - 1); + fail_unless(g_rx_cmdus_nr == 0); + } + + /* Complete one of the packets by sending the last one */ + set_mid(packets, packets_nr, mid - 3); + process_packets(packets + packets_nr - 1, 1); + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_id == mid - 3); + + /* Complete another (too old) one. Fragments are already discarded. */ + set_mid(packets, packets_nr, mid - MAX_CMDU_IN_FLIGHT - 1); + process_packets(packets + packets_nr - 1, 1); + fail_unless(g_rx_cmdus_nr == 1); + + + /* Process again 10 times , incremented MID */ + for (i = 0; i < 10; i++) { + set_mid(packets, packets_nr, mid++); + process_packets(packets, packets_nr - 1); + fail_unless(g_rx_cmdus_nr == 1); + } + + /* Process a complete set of fragments. + This cmdu should get through + */ + set_mid(packets, packets_nr, mid); + process_packets(packets, packets_nr); + + fail_unless(g_rx_cmdus_nr == 2); + cmdu = g_rx_cmdus[1]; + fail_unless(cmdu->message_id == mid); + + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_FRAGMENTED_DUPLICATE # +########################################################################*/ +/* One fragment is received 2 times */ +START_TEST(test_fragmented_duplicate) +{ + VARS + INIT + READ("cmdu_fragmented_ordered.pcap"); + + /* Process all but last packet */ + set_mid(packets, packets_nr, 1000); + process_packets(packets, packets_nr - 1); + fail_unless(g_rx_cmdus_nr == 0); + + /* Process first packet again (= duplicate) */ + process_packets(packets, 1); + fail_unless(g_rx_cmdus_nr == 0); + + /* Process last packet */ + process_packets(packets + packets_nr - 1, 1); + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_id == 1000); + + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_MULTIPLE_FRAGMENT_TLV # +########################################################################*/ +/* Big TLV fragmented into multiple packet */ +START_TEST(test_multiple_fragment_tlv) +{ + VARS + INIT + READ_PARSE("cmdu_multiple_fragment_tlv.pcap") + + i1905_vendor_specific_tlv_t *tlv; + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_id == 0xf010); + + fail_unless(*cmdu->list_of_TLVs[0] == TLV_TYPE_VENDOR_SPECIFIC); + fail_unless(*cmdu->list_of_TLVs[1] == TLV_TYPE_VENDOR_SPECIFIC); + fail_unless(*cmdu->list_of_TLVs[2] == TLV_TYPE_VENDOR_SPECIFIC); + + tlv = (i1905_vendor_specific_tlv_t *)cmdu->list_of_TLVs[2]; + + fail_unless(tlv->m && tlv->m_nr == 4003); + fail_unless(!memcmp(tlv->vendorOUI, (uint8_t[]){0x88, 0x41, 0xFC}, 3)); + + SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_500_TLV # +########################################################################*/ +START_TEST(test_500_tlv) +{ + VARS + INIT + READ_PARSE("cmdu_500_tlv.pcap") + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_id == 0x100); + check_tlv_sequence(cmdu, 2, 500); + + SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_1514_BYTES_INCL_EOM_1_TLV # +########################################################################*/ +/* Max size cmdu with eom in same packet (tlv payload = 1486 bytes) */ +START_TEST(test_1514_bytes_incl_eom_1_tlv) +{ + VARS + INIT + READ_PARSE("cmdu_1514_bytes_incl_eom_1_tlv.pcap") + fail_unless(packets[0]->len == 1514); + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + uint8_t *tlv; + size_t idx; + + i1905_foreach_tlv_in_cmdu(tlv, cmdu, idx) { + i1905_vendor_specific_tlv_t *t = (i1905_vendor_specific_tlv_t *)tlv; + + fail_unless(t->tlv_type == TLV_TYPE_VENDOR_SPECIFIC); + fail_unless(t->m_nr == 1486 - 3 /* oui */); + } + fail_unless(idx == 1); + + SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_1514_BYTES_EXCL_EOM_1_TLV # +########################################################################*/ +/* Max size cmdu with eom in next packet (tlv payload = 1489 bytes) */ +START_TEST(test_1514_bytes_excl_eom_1_tlv) +{ + VARS + INIT + READ_PARSE("cmdu_1514_bytes_excl_eom_1_tlv.pcap") + fail_unless(packets[0]->len == 1514); + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + uint8_t *tlv; + size_t idx; + + i1905_foreach_tlv_in_cmdu(tlv, cmdu, idx) { + i1905_vendor_specific_tlv_t *t = (i1905_vendor_specific_tlv_t *)tlv; + + fail_unless(t->tlv_type == TLV_TYPE_VENDOR_SPECIFIC); + fail_unless(t->m_nr == 1489 - 3 /* oui */); + } + fail_unless(idx == 1); + + /* TODO: cmdu forge cannot handle this */ + // SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_1514_BYTES_INCL_EOM_2_TLV # +########################################################################*/ +/* Max size cmdu with eom in same packet (tlv payload = 1486 bytes) */ +START_TEST(test_1514_bytes_incl_eom_2_tlv) +{ + VARS + INIT + READ_PARSE("cmdu_1514_bytes_incl_eom_2_tlv.pcap") + fail_unless(packets[0]->len == 1514); + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + uint8_t *tlv; + size_t idx; + + i1905_foreach_tlv_in_cmdu(tlv, cmdu, idx) { + i1905_vendor_specific_tlv_t *t = (i1905_vendor_specific_tlv_t *)tlv; + + fail_unless(t->tlv_type == TLV_TYPE_VENDOR_SPECIFIC); + fail_unless(t->m_nr == (idx == 0 ? 1280 : 203) - 3 /* oui */); + } + fail_unless(idx == 2); + + SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_1514_BYTES_EXCL_EOM_2_TLV # +########################################################################*/ +/* Max size cmdu with eom in next packet (tlv payload = 1489 bytes) */ +START_TEST(test_1514_bytes_excl_eom_2_tlv) +{ + VARS + INIT + READ_PARSE("cmdu_1514_bytes_excl_eom_2_tlv.pcap") + fail_unless(packets[0]->len == 1514); + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + uint8_t *tlv; + size_t idx; + + i1905_foreach_tlv_in_cmdu(tlv, cmdu, idx) { + i1905_vendor_specific_tlv_t *t = (i1905_vendor_specific_tlv_t *)tlv; + + fail_unless(t->tlv_type == TLV_TYPE_VENDOR_SPECIFIC); + fail_unless(t->m_nr == (idx == 0 ? 1280 : 206) - 3 /* oui */); + } + fail_unless(idx == 2); + + /* TODO: cmdu forge fragments differently */ + // SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_DUPLICATE # +########################################################################*/ +/* Check if duplicate packets (MID) are discarded. + Currently a new packet is compared with the 16 last ones +*/ +#define MAX_DUP_CMDU 16 +START_TEST(test_duplicate) +{ + VARS + INIT + READ("cmdu_topology_response.pcap") + + /* Double loop: + - process packets with different MID + - process packets with same MID (should be dropped) + - If i == 17, then the duplicate detection will fail + */ + size_t i, j, exp_cmdus_nr = 0; + i1905_cmdu_t *cmdu; + for (i = 0; i <= (MAX_DUP_CMDU + 1); i++) { + uint16_t mid = (i + 1) * 100; + + /* Process i packets with different MID */ + for (j = 0; j < i; j++) { + set_mid(packets, packets_nr, mid + j); + process_packets(packets, packets_nr); + fail_unless(g_rx_cmdus_nr == ++exp_cmdus_nr); + cmdu = g_rx_cmdus[exp_cmdus_nr - 1]; + fail_unless(cmdu->message_id == mid + j); + } + + /* Process same packets again */ + for (j = 0; j < i; j++) { + set_mid(packets, packets_nr, mid + j); + process_packets(packets, packets_nr); + if (i <= MAX_DUP_CMDU) { + /* Detection succeeded */ + fail_unless(g_rx_cmdus_nr == exp_cmdus_nr); + } else { + /* Detection failed */ + fail_unless(g_rx_cmdus_nr == ++exp_cmdus_nr); + cmdu = g_rx_cmdus[exp_cmdus_nr - 1]; + fail_unless(cmdu->message_id == mid + j); + } + } + } + + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_MULTICAST # +########################################################################*/ +START_TEST(test_multicast) +{ + VARS + INIT + READ_PARSE("cmdu_topology_discovery.pcap") + + fail_unless(!maccmp(packets[0]->data, g_mcast_mac_1905)); + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_type == CMDU_TYPE_TOPOLOGY_DISCOVERY); + fail_unless(cmdu->relay_indicator == 0); + + SEND_COMPARE + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_MULTICAST_RELAYED # +########################################################################*/ +START_TEST(test_multicast_relayed) +{ + VARS + INIT + READ_PARSE("cmdu_ap_autoconfiguration_search.pcap") + + fail_unless(!maccmp(packets[0]->data, g_mcast_mac_1905)); + + /* Check result */ + fail_unless(g_rx_cmdus_nr == 1); + + i1905_cmdu_t *cmdu = g_rx_cmdus[0]; + fail_unless(cmdu->message_type == CMDU_TYPE_AP_AUTOCONFIGURATION_SEARCH); + fail_unless(cmdu->relay_indicator == 1); + + /* Should have been forwarded on eth1, eth2, eth3 and wl0 but not on eth0 and lo */ + fail_unless(g_tx_packets_nr == 4); + fail_unless(!strcmp(g_tx_packets[0]->if_name, "eth1")); + fail_unless(!strcmp(g_tx_packets[1]->if_name, "eth2")); + fail_unless(!strcmp(g_tx_packets[2]->if_name, "eth3")); + fail_unless(!strcmp(g_tx_packets[3]->if_name, "wl0")); + + /* Send and compare first packet only */ + send_cmdus(); + compare_cmdu_packets(packets, packets_nr, g_tx_packets, 1); + + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_VISIT # +########################################################################*/ +START_TEST(test_visit) +{ + i1905_cmdu_t cmdu = {0}; + i1905_al_mac_address_tlv_t al_mac_addr_tlv = {.tlv_type = TLV_TYPE_AL_MAC_ADDRESS}; + uint8_t *tlvs[2] = {(uint8_t*)&al_mac_addr_tlv, NULL}; + + cmdu.message_version = CMDU_MESSAGE_VERSION_1905_1_2013; + cmdu.message_type = CMDU_TYPE_TOPOLOGY_NOTIFICATION; + cmdu.message_id = 0x123; + cmdu.relay_indicator = RELAY_INDICATOR_OFF; + cmdu.list_of_TLVs = tlvs; + strcpy(cmdu.interface_name, "eth0"); + + visit_1905_CMDU_structure(&cmdu, print_callback, PLATFORM_PRINTF, "prefix"); +} +END_TEST + +/*####################################################################### +# TEST_COMPARE # +########################################################################*/ +START_TEST(test_compare) +{ + i1905_cmdu_t cmdu = {0}; + i1905_cmdu_t cmdu2 = {0}; + i1905_cmdu_t cmdu3 = {0}; + i1905_al_mac_address_tlv_t al_mac_addr_tlv = {.tlv_type = TLV_TYPE_AL_MAC_ADDRESS}; + i1905_al_mac_address_tlv_t al_mac_addr_tlv2 = {.tlv_type = TLV_TYPE_AL_MAC_ADDRESS, .al_mac_address = {0x01,}}; + uint8_t *tlvs[2] = {(uint8_t*)&al_mac_addr_tlv, NULL}; + uint8_t *tlvs2[2] = {(uint8_t*)&al_mac_addr_tlv2, NULL}; + + cmdu.message_version = CMDU_MESSAGE_VERSION_1905_1_2013; + cmdu.message_type = CMDU_TYPE_TOPOLOGY_NOTIFICATION; + cmdu.message_id = 0x123; + cmdu.relay_indicator = RELAY_INDICATOR_OFF; + cmdu.list_of_TLVs = tlvs; + strcpy(cmdu.interface_name, "eth0"); + + /* cmdu2 has different TLV */ + cmdu2 = cmdu; + cmdu2.list_of_TLVs = tlvs2; + + /* cmdu3 has different header */ + cmdu3 = cmdu; + cmdu3.message_id = 0x124; + + fail_unless(!compare_1905_CMDU_structures(&cmdu, &cmdu)); + fail_unless(compare_1905_CMDU_structures(&cmdu, &cmdu2)); + fail_unless(compare_1905_CMDU_structures(&cmdu, &cmdu3)); + fail_unless(compare_1905_CMDU_structures(&cmdu, NULL)); +} +END_TEST + + +const char *test_suite_name = "cmdus"; +test_case_t test_cases[] = { + TEST("unfragmented", test_unfragmented ), + TEST("fragmented_ordered", test_fragmented_ordered ), + TEST("fragmented_reversed", test_fragmented_reversed ), + TEST("fragmented_scrambled", test_fragmented_scrambled ), + TEST("fragmented_mixed", test_fragmented_mixed ), + TEST("fragmented_eom_in_all_frag", test_fragmented_eom_in_all_frag ), + TEST("fragmented_lost", test_fragmented_lost ), + TEST("fragmented_duplicate", test_fragmented_duplicate ), + TEST("multiple_fragment_tlv", test_multiple_fragment_tlv ), + TEST("500_tlv", test_500_tlv ), + TEST("1514_bytes_incl_eom_1_tlv", test_1514_bytes_incl_eom_1_tlv ), + TEST("1514_bytes_excl_eom_1_tlv", test_1514_bytes_excl_eom_1_tlv ), + TEST("1514_bytes_incl_eom_2_tlv", test_1514_bytes_incl_eom_2_tlv ), + TEST("1514_bytes_excl_eom_2_tlv", test_1514_bytes_excl_eom_2_tlv ), + TEST("duplicate", test_duplicate ), + TEST("multicast", test_multicast ), + TEST("multicast_relayed", test_multicast_relayed ), + TEST("visit", test_visit ), + TEST("compare", test_compare ), + TEST_CASES_END +}; diff --git a/source/test/ieee1905/test_lldp_payload.c b/source/test/ieee1905/test_lldp_payload.c new file mode 100644 index 0000000..9fea655 --- /dev/null +++ b/source/test/ieee1905/test_lldp_payload.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include "test.h" + +#include "lldp_payload.h" +#include "lldp_tlvs.h" + +#include "utils.h" /* print_callback */ +#include "platform.h" /* PLATFORM_PRINTF */ + +/*####################################################################### +# GLOBALS # +########################################################################*/ +/* Note: BIT0 of first byte is MSB of TLV length -> tlv_type is shifted one bit */ +static uint8_t g_lldp_payload[24] = {/* chassid_id */ 0x02, 0x07, 0x04, 0x02, 0x01, 0x02, 0x03, 0x04, 0x05, + /* port_id */ 0x04, 0x07, 0x03, 0x12, 0x11, 0x12, 0x13, 0x14, 0x15, + /* time_to_live */ 0x06, 0x02, 0x00, 0xb4, + /* end_of_lldp */ 0x00, 0x00 + }; + +/*####################################################################### +# TEST_PAYLOAD # +########################################################################*/ +static void check_tlv(uint8_t *p_tlv) +{ + switch(*p_tlv) { + case TLV_TYPE_CHASSIS_ID: { + lldp_chassis_id_tlv_t *tlv = (lldp_chassis_id_tlv_t *)p_tlv; + + fail_unless(tlv->chassis_id_subtype == CHASSIS_ID_TLV_SUBTYPE_MAC_ADDRESS); + fail_unless(!memcmp(tlv->chassis_id, (mac_addr){0x02, 0x01, 0x02, 0x03, 0x04, 0x05}, sizeof(mac_addr))); + + break; + } + case TLV_TYPE_PORT_ID: { + lldp_port_id_tlv_t *tlv = (lldp_port_id_tlv_t *)p_tlv; + + fail_unless(tlv->port_id_subtype == PORT_ID_TLV_SUBTYPE_MAC_ADDRESS); + fail_unless(!memcmp(tlv->port_id, (mac_addr){0x12, 0x11, 0x12, 0x13, 0x14, 0x15}, sizeof(mac_addr))); + + break; + } + case TLV_TYPE_TIME_TO_LIVE: { + lldp_time_to_live_tlv_t *tlv = (lldp_time_to_live_tlv_t *)p_tlv; + + fail_unless(tlv->ttl == 180); + + break; + } + default: { + fail_unless(false, "unexpected tlv"); + + break; + } + } +} + +START_TEST(test_payload) +{ + i1905_lldp_payload_t *p_payload; + uint8_t *f_payload = NULL; + uint16_t f_payload_len; + int c; + + /* Parse */ + fail_unless(!!(p_payload = parse_lldp_PAYLOAD_from_packet(g_lldp_payload))); + + /* Check and count tlv */ + for (c = 0; p_payload->list_of_TLVs[c]; c++) { + check_tlv(p_payload->list_of_TLVs[c]); + } + fail_unless(c == 3); + + /* Visit and compare */ + visit_lldp_PAYLOAD_structure(p_payload, print_callback, PLATFORM_PRINTF, "prefix"); + fail_unless(compare_lldp_PAYLOAD_structures(p_payload, p_payload) == 0); + + /* Forge */ + fail_unless(!!(f_payload = forge_lldp_PAYLOAD_from_structure(p_payload, &f_payload_len))); + + /* Cleanup */ + free_lldp_PAYLOAD_structure(p_payload); + free(f_payload); +} +END_TEST + + +const char *test_suite_name = "lldp_payload"; +test_case_t test_cases[] = { + TEST("payload", test_payload ), + TEST_CASES_END +}; diff --git a/source/test/ieee1905/test_lldp_tlvs.c b/source/test/ieee1905/test_lldp_tlvs.c new file mode 100644 index 0000000..d779e3e --- /dev/null +++ b/source/test/ieee1905/test_lldp_tlvs.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2021-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include "test.h" + +#include "lldp_tlvs.h" + +#include "utils.h" /* print_callback */ +#include "platform.h" /* PLATFORM_PRINTF */ + +/*####################################################################### +# DEFINES # +########################################################################*/ +#define VARS \ + uint16_t f_tlv_len; \ + uint8_t *p_tlv = NULL; /* parsed tlv */ \ + uint8_t *f_tlv = NULL; /* forged tlv */ + +/* For code coverage (dump and compare with yourself)... */ +#define VISIT_COMPARE(p_tlv) \ + visit_lldp_TLV_structure(p_tlv, print_callback, PLATFORM_PRINTF, "prefix"); \ + fail_unless(compare_lldp_TLV_structures(p_tlv, p_tlv) == 0); \ + +#define PARSE(g_f_tlv, tlv_type) \ + fail_unless(!!(p_tlv = parse_lldp_TLV_from_packet(g_f_tlv))); \ + fail_unless(*p_tlv == tlv_type); \ + fail_unless(!strcmp(convert_lldp_TLV_type_to_string(*p_tlv), #tlv_type)); \ + VISIT_COMPARE(p_tlv); + +#define FORGE(g_f_tlv) \ + fail_unless(!!(f_tlv = forge_lldp_TLV_from_structure(p_tlv, &f_tlv_len))); \ + fail_unless(f_tlv_len == sizeof(g_f_tlv)); \ + fail_unless(!memcmp(f_tlv, g_f_tlv, f_tlv_len)); + +#define CLEANUP \ + free_lldp_TLV_structure(p_tlv); \ + free(f_tlv); + +/*####################################################################### +# GLOBALS # +########################################################################*/ +/* Note: BIT0 of first byte is MSB of TLV length -> tlv_type is shifted one bit */ +static uint8_t g_end_of_lldp_tlv[2] = {0x00, 0x00}; +static uint8_t g_chassis_id_tlv[9] = {0x02, 0x07, 0x04, 0x02, 0x01, 0x02, 0x03, 0x04, 0x05}; +static uint8_t g_port_id_tlv[9] = {0x04, 0x07, 0x03, 0x12, 0x11, 0x12, 0x13, 0x14, 0x15}; +static uint8_t g_time_to_live_tlv[4] = {0x06, 0x02, 0x00, 0xb4}; + +/*####################################################################### +# TEST_00_END_OF_LLDP # +########################################################################*/ +START_TEST(test_00_end_of_lldp) +{ + VARS + PARSE(g_end_of_lldp_tlv, TLV_TYPE_END_OF_LLDPPDU) + FORGE(g_end_of_lldp_tlv) + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_01_CHASSIS_ID # +########################################################################*/ +START_TEST(test_01_chassis_id) +{ + VARS + PARSE(g_chassis_id_tlv, TLV_TYPE_CHASSIS_ID) + + lldp_chassis_id_tlv_t *tlv = (lldp_chassis_id_tlv_t *)p_tlv; + fail_unless(tlv->chassis_id_subtype == CHASSIS_ID_TLV_SUBTYPE_MAC_ADDRESS); + fail_unless(!memcmp(tlv->chassis_id, (mac_addr){0x02, 0x01, 0x02, 0x03, 0x04, 0x05}, sizeof(mac_addr))); + + FORGE(g_chassis_id_tlv) + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_02_PORT_ID # +########################################################################*/ +START_TEST(test_02_port_id) +{ + VARS + PARSE(g_port_id_tlv, TLV_TYPE_PORT_ID) + + lldp_port_id_tlv_t *tlv = (lldp_port_id_tlv_t *)p_tlv; + fail_unless(tlv->port_id_subtype == PORT_ID_TLV_SUBTYPE_MAC_ADDRESS); + fail_unless(!memcmp(tlv->port_id, (mac_addr){0x12, 0x11, 0x12, 0x13, 0x14, 0x15}, sizeof(mac_addr))); + + FORGE(g_port_id_tlv) + CLEANUP +} +END_TEST + +/*####################################################################### +# TEST_03_TIME_TO_LIVE # +########################################################################*/ +START_TEST(test_03_time_to_live) +{ + VARS + PARSE(g_time_to_live_tlv, TLV_TYPE_TIME_TO_LIVE) + + lldp_time_to_live_tlv_t *tlv = (lldp_time_to_live_tlv_t *)p_tlv; + fail_unless(tlv->ttl == 180); + + FORGE(g_time_to_live_tlv) + CLEANUP +} +END_TEST + + +const char *test_suite_name = "lldp_tlvs"; +test_case_t test_cases[] = { + TEST("00_end_of_lldp", test_00_end_of_lldp ), + TEST("01_chassis_id", test_01_chassis_id ), + TEST("02_port_id", test_02_port_id ), + TEST("03_time_to_live", test_03_time_to_live ), + TEST_CASES_END +}; diff --git a/source/test/ieee1905/test_tlvs.c b/source/test/ieee1905/test_tlvs.c new file mode 100644 index 0000000..356aeac --- /dev/null +++ b/source/test/ieee1905/test_tlvs.c @@ -0,0 +1,3036 @@ +/* + * Copyright (c) 2019-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include +#include + +#include "test.h" + +#include "1905_tlvs.h" +#include "map_tlvs.h" + +#include "utils.h" /* print_callback */ +#include "platform.h" /* PLATFORM_PRINTF */ + +/*####################################################################### +# DEFINES # +########################################################################*/ +#define ETH_CMDU_HDR_LEN (sizeof(struct ether_header) + sizeof(cmdu_hdr_t)) +#define TLVS_START &packet->data[ETH_CMDU_HDR_LEN] +#define TLVS_LEN (packet->len - ETH_CMDU_HDR_LEN) + +#define MAX_TLV_STRUCT_SIZE 4*1024 + +#define VARS \ + packet_t *packet; \ + tlv_hdr_t *tlv; \ + uint16_t tlv_len, f_tlv_len; \ + uint8_t *p_tlv, *f_tlv = NULL; + +/* For code coverage (dump and compare with yourself)... */ +#define VISIT_COMPARE(p_tlv) \ + if (*p_tlv <= TLV_TYPE_L2_NEIGHBOR_DEVICE) { \ + visit_1905_TLV_structure(p_tlv, print_callback, PLATFORM_PRINTF, "prefix"); \ + fail_unless(compare_1905_TLV_structures(p_tlv, p_tlv) == 0); \ + } + +#define READ_PARSE(file) \ + fail_unless(!!(packet = pcap_read_first_packet(DATA_DIR "/" file))); \ + tlv = (tlv_hdr_t *)TLVS_START; \ + tlv_len = htons(tlv->len) + sizeof(tlv_hdr_t); \ + fail_unless(!!(p_tlv = parse_1905_TLV_from_packet(TLVS_START, TLVS_LEN))); \ + VISIT_COMPARE(p_tlv); + +//TODO adapt compare function +#define READ_PARSE_FRAGMENTED(file) \ + fail_unless(!!(p_tlv = read_parse_fragmented_tlv(DATA_DIR "/" file))); \ + +#define CHECK_TLV_STRUCT_SIZE \ + log_test_i("%s struct size: %zu bytes", __FUNCTION__, sizeof(*t)); \ + fail_unless(sizeof(*t) <= MAX_TLV_STRUCT_SIZE, "struct size %zu bytes", sizeof(*t)); + +#define FORGE \ + fail_unless(!!(f_tlv = forge_1905_TLV_from_structure(p_tlv, &f_tlv_len))); \ + fail_unless(f_tlv_len == tlv_len); \ + fail_unless(!memcmp(tlv, f_tlv, tlv_len)); + +#define CLEANUP \ + free_1905_TLV_structure(p_tlv); \ + free(f_tlv); \ + free(packet); + +uint8_t *read_parse_fragmented_tlv(const char *file) +{ + packet_t **packets; + size_t packets_nr; + uint8_t *message = NULL; + uint16_t message_offset = 0; + uint16_t packet_len; + uint8_t *parsed_tlv; + + fail_unless(!!(packets = pcap_read_all_packets(file, &packets_nr))); + + for (size_t i = 0; i < packets_nr; i++) { + packet_len = packets[i]->len - ETH_CMDU_HDR_LEN; + message = realloc(message, packet_len + message_offset); + memcpy(message + message_offset, packets[i]->data + ETH_CMDU_HDR_LEN, packet_len); + message_offset += packet_len; + } + + uint8_t *p = message; + fail_unless(!!(parsed_tlv = parse_1905_TLV_from_packet(p, message_offset))); + + free(message); + free_packets(packets, packets_nr); + + return parsed_tlv; +} + +/*####################################################################### +# 1905 TLVS # +########################################################################*/ +START_TEST(test_00_end_of_message) +{ + VARS + READ_PARSE("tlv_00_end_of_message.pcap") + + i1905_end_of_message_tlv_t *t = (i1905_end_of_message_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_END_OF_MESSAGE); + + FORGE + CLEANUP + +} +END_TEST + +START_TEST(test_01_al_mac_address) +{ + VARS + READ_PARSE("tlv_01_al_mac_address.pcap") + + i1905_al_mac_address_tlv_t *t = (i1905_al_mac_address_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AL_MAC_ADDRESS); + fail_unless(!memcmp(t->al_mac_address, (mac_addr){0xf6, 0x17, 0xb8, 0xae, 0x86, 0xef}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_02_mac_address) +{ + VARS + READ_PARSE("tlv_02_mac_address.pcap") + + i1905_mac_address_tlv_t *t = (i1905_mac_address_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_MAC_ADDRESS); + fail_unless(!memcmp(t->mac_address, (mac_addr){0x08, 0x00, 0x27, 0x8d, 0x68, 0x73}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_03_device_information) +{ + VARS + READ_PARSE("tlv_03_device_information.pcap") + + i1905_device_information_tlv_t *t = (i1905_device_information_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_DEVICE_INFORMATION); + fail_unless(!memcmp(t->al_mac_address, (mac_addr){0xf6, 0x17, 0xb8, 0x86, 0x57, 0x68}, sizeof(mac_addr))); + fail_unless(t->local_interfaces_nr == 4); + + /* Interface 0 */ + i1905_local_interface_entry_t *e = t->local_interfaces; + fail_unless(!memcmp(e[0].mac_address, (mac_addr){0xf4, 0x17, 0xb8, 0x86, 0x57, 0x68}, sizeof(mac_addr))); + fail_unless(e[0].media_type == MEDIA_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET); + fail_unless(e[0].media_specific_data_size == 0); + + /* Interface 1 */ + fail_unless(!memcmp(e[1].mac_address, (mac_addr){0xf4, 0x17, 0xb8, 0x86, 0x57, 0x6b}, sizeof(mac_addr))); + fail_unless(e[1].media_type == MEDIA_TYPE_IEEE_802_11N_2_4_GHZ); + fail_unless(e[1].media_specific_data_size == 10); + + i1905_ieee80211_specific_information_t *s = &e[1].media_specific_data.ieee80211; + fail_unless(!memcmp(s->network_membership, (mac_addr){0xf4, 0x17, 0xb8, 0x86, 0x57, 0x6b}, sizeof(mac_addr))); + fail_unless(s->role == IEEE80211_SPECIFIC_INFO_ROLE_AP); + fail_unless(s->ap_channel_band == 0); + fail_unless(s->ap_channel_center_frequency_index_1 == 6); + fail_unless(s->ap_channel_center_frequency_index_2 == 0); + + /* Interface 2 */ + fail_unless(!memcmp(e[2].mac_address, (mac_addr){0xf4, 0x17, 0xb8, 0x86, 0x57, 0x6a}, sizeof(mac_addr))); + fail_unless(e[2].media_type == MEDIA_TYPE_IEEE_802_11AC_5_GHZ); + fail_unless(e[2].media_specific_data_size == 10); + + s = &e[2].media_specific_data.ieee80211; + fail_unless(!memcmp(s->network_membership, (mac_addr){0xf4, 0x17, 0xb8, 0x86, 0x57, 0x6a}, sizeof(mac_addr))); + fail_unless(s->role == IEEE80211_SPECIFIC_INFO_ROLE_AP); + fail_unless(s->ap_channel_band == 2); + fail_unless(s->ap_channel_center_frequency_index_1 == 173); + fail_unless(s->ap_channel_center_frequency_index_2 == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_04_device_bridging_capability) +{ + VARS + READ_PARSE("tlv_04_device_bridging_capability.pcap") + + i1905_device_bridging_cap_tlv_t *t = (i1905_device_bridging_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_DEVICE_BRIDGING_CAPABILITY); + fail_unless(t->bridging_tuples_nr == 1); + + i1905_bridging_tuple_entry_t *e = t->bridging_tuples; + fail_unless(e->bridging_tuple_macs_nr == 4); + + i1905_bridging_tuple_mac_entry_t *m = e[0].bridging_tuple_macs; + fail_unless(!memcmp(m[0].mac_address, (mac_addr){0xf4, 0x17, 0xb8, 0xae, 0x86, 0xf1}, sizeof(mac_addr))); + fail_unless(!memcmp(m[1].mac_address, (mac_addr){0xf4, 0x17, 0xb8, 0xae, 0x86, 0xf2}, sizeof(mac_addr))); + fail_unless(!memcmp(m[2].mac_address, (mac_addr){0x7a, 0x17, 0xb8, 0xae, 0x86, 0xf2}, sizeof(mac_addr))); + fail_unless(!memcmp(m[3].mac_address, (mac_addr){0x7a, 0x17, 0xb8, 0xae, 0x86, 0xf3}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_06_non_1905_neighbor_device_list) +{ + VARS + READ_PARSE("tlv_06_non_1905_neighbor_device_list.pcap") + + i1905_non_1905_neighbor_device_list_tlv_t *t = (i1905_non_1905_neighbor_device_list_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_NON_1905_NEIGHBOR_DEVICE_LIST); + fail_unless(!memcmp(t->local_mac_address, (mac_addr){0xf4, 0x17, 0xb8, 0xae, 0x86, 0xf1}, sizeof(mac_addr))); + fail_unless(t->non_1905_neighbors_nr == 3); + + i1905_non_1905_neighbor_entry_t *e = t->non_1905_neighbors; + fail_unless(!memcmp(e[0].mac_address, (mac_addr){0x4e, 0x8f, 0x3b, 0x88, 0xf5, 0x57}, sizeof(mac_addr))); + fail_unless(!memcmp(e[1].mac_address, (mac_addr){0x70, 0x28, 0x8b, 0x22, 0x23, 0x93}, sizeof(mac_addr))); + fail_unless(!memcmp(e[2].mac_address, (mac_addr){0xc8, 0xf7, 0x50, 0x4c, 0x29, 0x95}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_07_neighbor_device_list) +{ + VARS + READ_PARSE("tlv_07_neighbor_device_list.pcap") + + i1905_neighbor_device_list_tlv_t *t = (i1905_neighbor_device_list_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_NEIGHBOR_DEVICE_LIST); + fail_unless(!memcmp(t->local_mac_address, (mac_addr){0xf4, 0x17, 0xb8, 0xae, 0x86, 0xf1}, sizeof(mac_addr))); + fail_unless(t->neighbors_nr == 1); + + i1905_neighbor_entry_t *e = t->neighbors; + fail_unless(!memcmp(e[0].mac_address, (mac_addr){0xf6, 0x17, 0xb8, 0x86, 0x57, 0x68}, sizeof(mac_addr))); + fail_unless(e[0].bridge_flag == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_08_link_metric_query) +{ + VARS + READ_PARSE("tlv_08_link_metric_query.pcap") + + i1905_link_metric_query_tlv_t *t = (i1905_link_metric_query_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_LINK_METRIC_QUERY); + fail_unless(t->destination == LINK_METRIC_QUERY_TLV_ALL_NEIGHBORS); + fail_unless(t->link_metrics_type == LINK_METRIC_QUERY_TLV_BOTH_TX_AND_RX_LINK_METRICS); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_09_transmitter_link_metric) +{ + VARS + READ_PARSE("tlv_09_transmitter_link_metric.pcap") + + i1905_transmitter_link_metric_tlv_t *t = (i1905_transmitter_link_metric_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_TRANSMITTER_LINK_METRIC); + fail_unless(!memcmp(t->local_al_address, (mac_addr){0x22, 0xb0, 0x01, 0xbf, 0xa2, 0xad}, sizeof(mac_addr))); + fail_unless(!memcmp(t->neighbor_al_address, (mac_addr){0xf6, 0x17, 0xb8, 0x86, 0x57, 0x68}, sizeof(mac_addr))); + fail_unless(t->transmitter_link_metrics_nr == 1); + + i1905_transmitter_link_metric_entry_t *e = t->transmitter_link_metrics; + fail_unless(!memcmp(e[0].local_interface_address, (mac_addr){0x20, 0xb0, 0x01, 0xbf, 0xa2, 0xac}, sizeof(mac_addr))); + fail_unless(!memcmp(e[0].neighbor_interface_address, (mac_addr){0xf4, 0x17, 0xb8, 0x86, 0x57, 0x68}, sizeof(mac_addr))); + fail_unless(e[0].intf_type == MEDIA_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET); + fail_unless(e[0].bridge_flag == 0); + fail_unless(e[0].packet_errors == 0); + fail_unless(e[0].transmitted_packets == 145436); + fail_unless(e[0].mac_throughput_capacity == 1000); + fail_unless(e[0].link_availability == 100); + fail_unless(e[0].phy_rate == 1000); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_0A_receiver_link_metric) +{ + VARS + READ_PARSE("tlv_0A_receiver_link_metric.pcap") + + i1905_receiver_link_metric_tlv_t *t = (i1905_receiver_link_metric_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_RECEIVER_LINK_METRIC); + fail_unless(!memcmp(t->local_al_address, (mac_addr){0x22, 0xb0, 0x01, 0xbf, 0xa2, 0xad}, sizeof(mac_addr))); + fail_unless(!memcmp(t->neighbor_al_address, (mac_addr){0xf6, 0x17, 0xb8, 0x86, 0x57, 0x68}, sizeof(mac_addr))); + fail_unless(t->receiver_link_metrics_nr == 1); + + i1905_receiver_link_metric_entry_t *e = t->receiver_link_metrics; + fail_unless(!memcmp(e[0].local_interface_address, (mac_addr){0x20, 0xb0, 0x01, 0xbf, 0xa2, 0xac}, sizeof(mac_addr))); + fail_unless(!memcmp(e[0].neighbor_interface_address, (mac_addr){0xf4, 0x17, 0xb8, 0x86, 0x57, 0x68}, sizeof(mac_addr))); + fail_unless(e[0].intf_type == MEDIA_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET); + fail_unless(e[0].packet_errors == 0); + fail_unless(e[0].packets_received == 158684); + fail_unless(e[0].rssi == 255); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_0B_vendor_specific) +{ + VARS + READ_PARSE("tlv_0B_vendor_specific.pcap") + + i1905_vendor_specific_tlv_t *t = (i1905_vendor_specific_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_VENDOR_SPECIFIC); + fail_unless(t->vendorOUI[0] == 0x00 && t->vendorOUI[1] == 0x10 && t->vendorOUI[2] == 0x18); + fail_unless(!memcmp(t->vendorOUI, (uint8_t[]){0x00, 0x10, 0x18}, 3)); + fail_unless(t->m && t->m_nr == 29); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_0C_link_metric_result_code) +{ + VARS + READ_PARSE("tlv_0C_link_metric_result_code.pcap") + + i1905_link_metric_result_code_tlv_t *t = (i1905_link_metric_result_code_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_LINK_METRIC_RESULT_CODE); + fail_unless(t->result_code == LINK_METRIC_RESULT_CODE_TLV_INVALID_NEIGHBOR); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_0D_searched_role) +{ + VARS + READ_PARSE("tlv_0D_searched_role.pcap") + + i1905_searched_role_tlv_t *t = (i1905_searched_role_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_SEARCHED_ROLE); + fail_unless(t->role == IEEE80211_ROLE_REGISTRAR); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_0E_autoconfig_freq_band) +{ + VARS + READ_PARSE("tlv_0E_autoconfig_freq_band.pcap") + + i1905_autoconfig_freq_band_tlv_t *t = ( i1905_autoconfig_freq_band_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AUTOCONFIG_FREQ_BAND); + fail_unless(t->freq_band == IEEE80211_FREQUENCY_BAND_2_4_GHZ); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_0F_supported_role) +{ + VARS + READ_PARSE("tlv_0F_supported_role.pcap") + + i1905_supported_role_tlv_t *t = (i1905_supported_role_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_SUPPORTED_ROLE); + fail_unless(t->role == IEEE80211_ROLE_REGISTRAR); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_10_supported_freq_band) +{ + VARS + READ_PARSE("tlv_10_supported_freq_band.pcap") + + i1905_supported_freq_band_tlv_t *t = (i1905_supported_freq_band_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_SUPPORTED_FREQ_BAND); + fail_unless(t->freq_band == IEEE80211_FREQUENCY_BAND_5_GHZ); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_11_wsc) +{ + VARS + READ_PARSE("tlv_11_wsc.pcap") + + i1905_wsc_tlv_t *t = (i1905_wsc_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_WSC); + fail_unless(t->wsc_frame != NULL); + fail_unless(t->wsc_frame_size == 426); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_12_push_button_event_notification) +{ + VARS + READ_PARSE("tlv_12_push_button_event_notification.pcap") + + i1905_push_button_event_notification_tlv_t *t = (i1905_push_button_event_notification_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_PUSH_BUTTON_EVENT_NOTIFICATION); + fail_unless(t->media_types_nr == 1); + fail_unless(t->media_types[0].media_type == MEDIA_TYPE_IEEE_802_11N_2_4_GHZ); + fail_unless(t->media_types[0].media_specific_data_size == 10); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_13_push_button_join_notification) +{ + VARS + READ_PARSE("tlv_13_push_button_join_notification.pcap") + + i1905_push_button_join_notification_tlv_t *t = (i1905_push_button_join_notification_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_PUSH_BUTTON_JOIN_NOTIFICATION); + fail_unless(!memcmp(t->al_mac_address, (mac_addr){0x02, 0x01, 0x02, 0x03, 0x04, 0x05}, sizeof(mac_addr))); + fail_unless(!memcmp(t->mac_address, (mac_addr){0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, sizeof(mac_addr))); + fail_unless(!memcmp(t->new_mac_address, (mac_addr){0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E}, sizeof(mac_addr))); + fail_unless(t->message_identifier == 0x1234); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_14_generic_phy_device_information) +{ + VARS + READ_PARSE("tlv_14_generic_phy_device_information.pcap") + + i1905_generic_phy_device_information_tlv_t *t = (i1905_generic_phy_device_information_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_GENERIC_PHY_DEVICE_INFORMATION); + fail_unless(!memcmp(t->al_mac_address, (mac_addr){0x02, 0x01, 0x02, 0x03, 0x04, 0x05}, sizeof(mac_addr))); + fail_unless(t->local_interfaces_nr == 1); + fail_unless(!memcmp(t->local_interfaces[0].local_interface_address, (mac_addr){0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, sizeof(mac_addr))); + fail_unless(!strcmp((char *)t->local_interfaces[0].variant_name, "wireless")); + fail_unless(t->local_interfaces[0].generic_phy_description_xml_url_len == 16); + fail_unless(!strcmp(t->local_interfaces[0].generic_phy_description_xml_url, "http://test.com")); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_15_device_identification) +{ + VARS + READ_PARSE("tlv_15_device_identification.pcap") + + i1905_device_identification_tlv_t *t = (i1905_device_identification_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_DEVICE_IDENTIFICATION); + fail_unless(!strcmp(t->friendly_name, "Airties Air4960")); + fail_unless(!strcmp(t->manufacturer_name, "Airties")); + fail_unless(!strcmp(t->manufacturer_model, "Air4960")); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_16_control_url) +{ + VARS + READ_PARSE("tlv_16_control_url.pcap") + + i1905_control_url_tlv_t *t = (i1905_control_url_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CONTROL_URL); + fail_unless(!strcmp(t->url, "http://test.com")); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_17_ipv4) +{ + VARS + READ_PARSE("tlv_17_ipv4.pcap") + + i1905_ipv4_tlv_t *t = (i1905_ipv4_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_IPV4); + fail_unless(t->ipv4_interfaces_nr == 1); + fail_unless(!memcmp(t->ipv4_interfaces[0].mac_address, (mac_addr){0x02, 0x01, 0x02, 0x03, 0x04, 0x05}, sizeof(mac_addr))); + fail_unless(t->ipv4_interfaces[0].ipv4_nr == 1); + fail_unless(t->ipv4_interfaces[0].ipv4[0].type == IPV4_TYPE_DHCP); + fail_unless(!memcmp(t->ipv4_interfaces[0].ipv4[0].ipv4_address, (uint8_t[]){0xC0,0xA8,0x01,0x02}, 4)); + fail_unless(!memcmp(t->ipv4_interfaces[0].ipv4[0].ipv4_dhcp_server, (uint8_t[]){0xC0,0xA8,0x01,0x01}, 4)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_18_ipv6) +{ + VARS + READ_PARSE("tlv_18_ipv6.pcap") + + i1905_ipv6_tlv_t *t = (i1905_ipv6_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_IPV6); + fail_unless(t->ipv6_interfaces_nr == 1); + fail_unless(!memcmp(t->ipv6_interfaces[0].mac_address, (mac_addr){0x02, 0x01, 0x02, 0x03, 0x04, 0x05}, sizeof(mac_addr))); + fail_unless(t->ipv6_interfaces[0].ipv6_nr == 1); + fail_unless(t->ipv6_interfaces[0].ipv6[0].type == IPV4_TYPE_DHCP); + fail_unless(!memcmp(t->ipv6_interfaces[0].ipv6_link_local_address, (uint8_t[]){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}, 16)); + fail_unless(!memcmp(t->ipv6_interfaces[0].ipv6[0].ipv6_address, (uint8_t[]){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02}, 16)); + fail_unless(!memcmp(t->ipv6_interfaces[0].ipv6[0].ipv6_address_origin, (uint8_t[]){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03}, 16)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_19_generic_phy_event_notification) +{ + VARS + READ_PARSE("tlv_19_generic_phy_event_notification.pcap") + + i1905_generic_phy_event_notification_tlv_t *t = (i1905_generic_phy_event_notification_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_GENERIC_PHY_EVENT_NOTIFICATION); + /* TODO */ + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_1A_1905_profile_version) +{ + VARS + READ_PARSE("tlv_1A_1905_profile_version.pcap") + + i1905_profile_version_tlv_t *t = (i1905_profile_version_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_1905_PROFILE_VERSION); + fail_unless(t->profile == PROFILE_1905_1A); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_1B_power_off_interface) +{ + VARS + READ_PARSE("tlv_1B_power_off_interface.pcap") + + i1905_power_off_interface_tlv_t *t = (i1905_power_off_interface_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_POWER_OFF_INTERFACE); + /* TODO */ + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_1C_interface_power_change_information) +{ + VARS + READ_PARSE("tlv_1C_interface_power_change_information.pcap") + + i1905_interface_power_change_information_tlv_t *t = (i1905_interface_power_change_information_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_INTERFACE_POWER_CHANGE_INFORMATION); + /* TODO */ + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_1D_interface_power_change_status) +{ + VARS + READ_PARSE("tlv_1D_interface_power_change_status.pcap") + + i1905_interface_power_change_status_tlv_t *t = (i1905_interface_power_change_status_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_INTERFACE_POWER_CHANGE_STATUS); + /* TODO */ + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_1E_l2_neighbor_device) +{ + VARS + READ_PARSE("tlv_1E_l2_neighbor_device.pcap"); + + i1905_l2_neighbor_device_tlv_t *t = (i1905_l2_neighbor_device_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_L2_NEIGHBOR_DEVICE); + fail_unless(t->local_interfaces_nr == 1); + fail_unless(!memcmp(t->local_interfaces[0].local_mac_address, (mac_addr){0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, sizeof(mac_addr))); + fail_unless(t->local_interfaces[0].l2_neighbors_nr == 1); + fail_unless(!memcmp(t->local_interfaces[0].l2_neighbors[0].l2_neighbor_mac_address, (mac_addr){0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E}, sizeof(mac_addr))); + fail_unless(t->local_interfaces[0].l2_neighbors[0].behind_mac_addresses_nr == 1); + fail_unless(!memcmp(t->local_interfaces[0].l2_neighbors[0].behind_mac_addresses[0], (mac_addr){0x00, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +/*####################################################################### +# MAP R1 TLVS # +########################################################################*/ +START_TEST(test_80_supported_service) +{ + VARS + READ_PARSE("tlv_80_supported_service.pcap") + + map_supported_service_tlv_t *t = (map_supported_service_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_SUPPORTED_SERVICE); + fail_unless(t->services_nr == 1); + fail_unless(t->services[0] == MAP_SERVICE_AGENT); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_81_searched_service) +{ + VARS + READ_PARSE("tlv_81_searched_service.pcap") + + map_searched_service_tlv_t *t = (map_searched_service_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_SEARCHED_SERVICE); + fail_unless(t->services_nr == 1); + fail_unless(t->services[0] == MAP_SERVICE_CONTROLLER); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_82_ap_radio_identifier) +{ + VARS + READ_PARSE("tlv_82_ap_radio_identifier.pcap") + + map_ap_radio_identifier_tlv_t *t = (map_ap_radio_identifier_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_RADIO_IDENTIFIER); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_83_ap_operational_bss) +{ + VARS + READ_PARSE("tlv_83_ap_operational_bss.pcap") + + map_ap_operational_bss_tlv_t *t = (map_ap_operational_bss_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_OPERATIONAL_BSS); + fail_unless(t->radios_nr == 2); + + fail_unless(!memcmp(t->radios[0].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->radios[0].bsss_nr == 1); + fail_unless(!memcmp(t->radios[0].bsss[0].bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->radios[0].bsss[0].ssid_len == 11); + fail_unless(!memcmp(t->radios[0].bsss[0].ssid, (char *)"frv_test_fh", 11)); + + fail_unless(!memcmp(t->radios[1].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}, sizeof(mac_addr))); + fail_unless(t->radios[1].bsss_nr == 2); + fail_unless(!memcmp(t->radios[1].bsss[0].bssid, (mac_addr){0x6A, 0x17, 0xB8, 0x86, 0x57, 0x6C}, sizeof(mac_addr))); + fail_unless(t->radios[1].bsss[0].ssid_len == 11); + fail_unless(!memcmp(t->radios[1].bsss[0].ssid, (char *)"frv_test_bh", 11)); + fail_unless(!memcmp(t->radios[1].bsss[1].bssid, (mac_addr){0x6A, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->radios[1].bsss[1].ssid_len == 11); + fail_unless(!memcmp(t->radios[1].bsss[1].ssid, (char *)"frv_test_fh", 11)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_84_associated_clients) +{ + VARS + READ_PARSE("tlv_84_associated_clients.pcap") + + map_assoc_clients_tlv_t *t = (map_assoc_clients_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_ASSOCIATED_CLIENTS); + fail_unless(t->bsss_nr == 1); + fail_unless(!memcmp(t->bsss[0].bssid, (mac_addr){0x22, 0xB0, 0x01, 0xBF, 0xA2, 0xB5}, sizeof(mac_addr))); + fail_unless(t->bsss[0].stas_nr == 1); + fail_unless(!memcmp(t->bsss[0].stas[0].mac, (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + fail_unless(t->bsss[0].stas[0].assoc_time == 775); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_85_ap_radio_basic_capabilities) +{ + VARS + READ_PARSE("tlv_85_ap_radio_basic_capabilities.pcap") + + map_ap_radio_basic_cap_tlv_t *t = (map_ap_radio_basic_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_RADIO_BASIC_CAPABILITIES); + fail_unless(!memcmp(t->radio_id, (mac_addr){0x22, 0xB0, 0x01, 0xBF, 0xA2, 0xB5}, sizeof(mac_addr))); + fail_unless(t->max_bss == 2); + fail_unless(t->op_classes_nr == 10); + fail_unless(t->op_classes[0].op_class == 115); + fail_unless(t->op_classes[0].eirp == 21); + fail_unless(map_cs_nr(&t->op_classes[0].channels) == 3); + fail_unless(map_cs_is_set(&t->op_classes[0].channels, 40)); + fail_unless(map_cs_is_set(&t->op_classes[0].channels, 44)); + fail_unless(map_cs_is_set(&t->op_classes[0].channels, 48)); + + fail_unless(t->op_classes[9].op_class == 129); + fail_unless(t->op_classes[9].eirp == 21); + fail_unless(map_cs_nr(&t->op_classes[9].channels) == 2); + fail_unless(map_cs_is_set(&t->op_classes[9].channels, 50)); + fail_unless(map_cs_is_set(&t->op_classes[9].channels, 114)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_86_ap_ht_capabilities) +{ + VARS + READ_PARSE("tlv_86_ap_ht_capabilities.pcap") + + map_ap_ht_cap_tlv_t *t = (map_ap_ht_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_HT_CAPABILITIES); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF2}, sizeof(mac_addr))); + fail_unless(t->max_supported_tx_streams == 1); + fail_unless(t->max_supported_rx_streams == 1); + fail_unless(t->gi_support_20mhz == 1); + fail_unless(t->gi_support_40mhz == 1); + fail_unless(t->ht_support_40mhz == 1); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_87_ap_vht_capabilities) +{ + VARS + READ_PARSE("tlv_87_ap_vht_capabilities.pcap") + + map_ap_vht_cap_tlv_t *t = (map_ap_vht_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_VHT_CAPABILITIES); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF2}, sizeof(mac_addr))); + fail_unless(t->supported_tx_mcs == 0xfffa); + fail_unless(t->supported_rx_mcs == 0xfffa); + fail_unless(t->max_supported_tx_streams == 1); + fail_unless(t->max_supported_rx_streams == 1); + fail_unless(t->gi_support_80mhz == 1); + fail_unless(t->gi_support_160mhz == 0); + fail_unless(t->support_80_80_mhz == 0); + fail_unless(t->support_160mhz == 0); + fail_unless(t->su_beamformer_capable == 1); + fail_unless(t->mu_beamformer_capable == 1); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_88_ap_he_capabilities) +{ + VARS + READ_PARSE("tlv_88_ap_he_capabilities.pcap") + + map_ap_he_cap_tlv_t *t = (map_ap_he_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_HE_CAPABILITIES); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}, sizeof(mac_addr))); + fail_unless(t->supported_mcs_length == 8); + fail_unless(!memcmp(t->supported_tx_rx_mcs, (uint16_t[]){0xFFAA, 0xFFAA, 0xFFAA, 0xFFAA}, 8)); + fail_unless(t->max_supported_tx_streams == 3); + fail_unless(t->max_supported_rx_streams == 3); + fail_unless(t->support_80_80_mhz == 0); + fail_unless(t->support_160mhz == 1); + fail_unless(t->su_beamformer_capable == 1); + fail_unless(t->mu_beamformer_capable == 1); + fail_unless(t->ul_mimo_capable == 0); + fail_unless(t->ul_mimo_ofdma_capable == 0); + fail_unless(t->dl_mimo_ofdma_capable == 1); + fail_unless(t->ul_ofdma_capable == 1); + fail_unless(t->dl_ofdma_capable == 1); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_89_steering_policy) +{ + VARS + READ_PARSE("tlv_89_steering_policy.pcap") + + map_steering_policy_tlv_t *t = (map_steering_policy_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_STEERING_POLICY); + fail_unless(t->local_steering_dis_macs_nr == 0); + fail_unless(t->btm_steering_dis_macs_nr == 0); + fail_unless(t->radios_nr == 2); + + fail_unless(!memcmp(t->radios[0].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->radios[0].steering_policy == 0); + fail_unless(t->radios[0].channel_utilization_threshold == 0); + fail_unless(t->radios[0].rssi_steering_threshold == 0); + + fail_unless(!memcmp(t->radios[1].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}, sizeof(mac_addr))); + fail_unless(t->radios[1].steering_policy == 0); + fail_unless(t->radios[1].channel_utilization_threshold == 0); + fail_unless(t->radios[1].rssi_steering_threshold == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_89_steering_policy_2) +{ + VARS + READ_PARSE("tlv_89_steering_policy_2.pcap") + + map_steering_policy_tlv_t *t = (map_steering_policy_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_STEERING_POLICY); + fail_unless(t->local_steering_dis_macs_nr == 0); + fail_unless(t->btm_steering_dis_macs_nr == 1); + fail_unless(t->radios_nr == 0); + fail_unless(!memcmp(&t->btm_steering_dis_macs[0], (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_8A_metric_reporting_policy) +{ + VARS + READ_PARSE("tlv_8A_metric_reporting_policy.pcap") + + map_metric_reporting_policy_tlv_t *t = (map_metric_reporting_policy_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_METRIC_REPORTING_POLICY); + fail_unless(t->metric_reporting_interval == 1); + fail_unless(t->radios_nr == 2); + + fail_unless(!memcmp(t->radios[0].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->radios[0].reporting_rssi_threshold == 0); + fail_unless(t->radios[0].reporting_rssi_margin_override == 0); + fail_unless(t->radios[0].channel_utilization_reporting_threshold == 0); + fail_unless(t->radios[0].associated_sta_policy == (MAP_METRIC_POLICY_TRAFFIC_STATS | + MAP_METRIC_POLICY_LINK_METRICS | + MAP_METRIC_POLICY_WIFI_6_STATS)); + + fail_unless(!memcmp(t->radios[1].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}, sizeof(mac_addr))); + fail_unless(t->radios[1].reporting_rssi_threshold == 0); + fail_unless(t->radios[1].reporting_rssi_margin_override == 0); + fail_unless(t->radios[1].channel_utilization_reporting_threshold == 0); + fail_unless(t->radios[1].associated_sta_policy == (MAP_METRIC_POLICY_TRAFFIC_STATS | + MAP_METRIC_POLICY_LINK_METRICS | + MAP_METRIC_POLICY_WIFI_6_STATS)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_8B_channel_preference) +{ + VARS + READ_PARSE("tlv_8B_channel_preference.pcap") + + map_channel_preference_tlv_t *t = (map_channel_preference_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CHANNEL_PREFERENCE); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xBD, 0xBD, 0xFE}, sizeof(mac_addr))); + fail_unless(t->op_classes_nr == 20); + + fail_unless(t->op_classes[0].op_class == 81); + fail_unless(map_cs_nr(&t->op_classes[0].channels) == 0); + fail_unless(t->op_classes[0].pref == 0xE); + fail_unless(t->op_classes[0].reason == 0xA); + + fail_unless(t->op_classes[5].op_class == 116); + fail_unless(map_cs_nr(&t->op_classes[5].channels) == 1); + fail_unless(map_cs_is_set(&t->op_classes[5].channels, 44)); + fail_unless(t->op_classes[5].pref == 0); + fail_unless(t->op_classes[5].reason == 0); + + fail_unless(t->op_classes[19].op_class == 130); + fail_unless(map_cs_nr(&t->op_classes[19].channels) == 0); + fail_unless(t->op_classes[19].pref == 0); + fail_unless(t->op_classes[19].reason == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_8C_radio_operation_restriction) +{ + VARS + READ_PARSE("tlv_8C_radio_operation_restriction.pcap") + + map_radio_operation_restriction_tlv_t *t = (map_radio_operation_restriction_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_RADIO_OPERATION_RESTRICTION); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->op_classes_nr == 2); + + fail_unless(t->op_classes[0].op_class == 128); + fail_unless(t->op_classes[0].channels_nr == 2); + fail_unless(t->op_classes[0].channels[0].channel == 44); + fail_unless(t->op_classes[0].channels[0].freq_restriction == 1); + fail_unless(t->op_classes[0].channels[1].channel == 112); + fail_unless(t->op_classes[0].channels[1].freq_restriction == 2); + + fail_unless(t->op_classes[1].op_class == 129); + fail_unless(t->op_classes[1].channels_nr == 1); + fail_unless(t->op_classes[1].channels[0].channel == 100); + fail_unless(t->op_classes[1].channels[0].freq_restriction == 3); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_8D_transmit_power_limit) +{ + VARS + READ_PARSE("tlv_8D_transmit_power_limit.pcap") + + map_transmit_power_limit_tlv_t *t = (map_transmit_power_limit_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_TRANSMIT_POWER_LIMIT); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->transmit_power_eirp == 23); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_8E_channel_selection_response) +{ + VARS + READ_PARSE("tlv_8E_channel_selection_response.pcap") + + map_channel_selection_response_tlv_t *t = (map_channel_selection_response_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CHANNEL_SELECTION_RESPONSE); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->channel_selection_response == 0x02); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_8F_operating_channel_report) +{ + VARS + READ_PARSE("tlv_8F_operating_channel_report.pcap") + + map_operating_channel_report_tlv_t *t = (map_operating_channel_report_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_OPERATING_CHANNEL_REPORT); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}, sizeof(mac_addr))); + fail_unless(t->transmit_power_eirp == 23); + fail_unless(t->op_classes_nr == 3); + + fail_unless(t->op_classes[0].op_class == 128); + fail_unless(t->op_classes[0].channel == 171); + + fail_unless(t->op_classes[1].op_class == 126); + fail_unless(t->op_classes[1].channel == 173); + + fail_unless(t->op_classes[2].op_class == 125); + fail_unless(t->op_classes[2].channel == 173); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_90_client_info) +{ + VARS + READ_PARSE("tlv_90_client_info.pcap") + + map_client_info_tlv_t *t = (map_client_info_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CLIENT_INFO); + fail_unless(!memcmp(t->bssid, (mac_addr){0x6A, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(!memcmp(t->sta_mac,(mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_91_client_capability_report) +{ + VARS + READ_PARSE("tlv_91_client_capability_report.pcap") + + map_client_cap_report_tlv_t *t = (map_client_cap_report_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CLIENT_CAPABILITY_REPORT); + fail_unless(t->result_code == 0); + fail_unless(t->assoc_frame_body_len == 268); + fail_unless(t->assoc_frame_body != NULL); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_92_client_association_event) +{ + VARS + READ_PARSE("tlv_92_client_association_event.pcap") + + map_client_assoc_event_tlv_t *t = (map_client_assoc_event_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CLIENT_ASSOCIATION_EVENT); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x70, 0x28, 0x8B, 0x22, 0x23, 0x93}, sizeof(mac_addr))); + fail_unless(!memcmp(t->bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->association_event == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_93_ap_metric_query) +{ + VARS + READ_PARSE("tlv_93_ap_metric_query.pcap") + + map_ap_metric_query_tlv_t *t = (map_ap_metric_query_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_METRIC_QUERY); + fail_unless(t->bssids_nr == 2); + fail_unless(!memcmp(t->bssids[0], (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}, sizeof(mac_addr))); + fail_unless(!memcmp(t->bssids[1], (mac_addr){0x6A, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_94_ap_metrics) +{ + VARS + READ_PARSE("tlv_94_ap_metrics.pcap") + + map_ap_metrics_tlv_t *t = (map_ap_metrics_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_METRICS); + fail_unless(!memcmp(t->bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->channel_util == 40); + fail_unless(t->stas_nr == 0); + fail_unless(t->esp_present == 0x80); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_95_sta_mac_address) +{ + VARS + READ_PARSE("tlv_95_sta_mac_address.pcap") + + map_sta_mac_address_tlv_t *t = (map_sta_mac_address_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_STA_MAC_ADDRESS); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x70, 0x28, 0x8B, 0x22, 0x23, 0x93}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_96_associated_sta_link_metrics) +{ + VARS + READ_PARSE("tlv_96_associated_sta_link_metrics.pcap") + + map_assoc_sta_link_metrics_tlv_t *t = (map_assoc_sta_link_metrics_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_ASSOCIATED_STA_LINK_METRICS); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + fail_unless(t->bsss_nr == 1); + fail_unless(!memcmp(t->bsss[0].bssid, (mac_addr){0x22, 0xB0, 0x01, 0xBF, 0xA2, 0xB5}, sizeof(mac_addr))); + fail_unless(t->bsss[0].report_time_interval == 593); + fail_unless(t->bsss[0].downlink_data_rate == 541); + fail_unless(t->bsss[0].uplink_data_rate == 6); + fail_unless(t->bsss[0].uplink_rcpi == 112); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_97_unassociated_sta_link_metrics_query) +{ + VARS + READ_PARSE("tlv_97_unassociated_sta_link_metrics_query.pcap") + + map_unassoc_sta_link_metrics_query_tlv_t *t = (map_unassoc_sta_link_metrics_query_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_UNASSOCIATED_STA_LINK_METRICS_QUERY); + fail_unless(t->op_class == 128); + fail_unless(t->channels_nr == 1); + fail_unless(t->channels[0].channel == 112); + fail_unless(t->channels[0].sta_macs_nr == 1); + fail_unless(!memcmp(t->channels[0].sta_macs[0], (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_98_unassociated_sta_link_metrics_response) +{ + VARS + READ_PARSE("tlv_98_unassociated_sta_link_metrics_response.pcap") + + map_unassoc_sta_link_metrics_response_tlv_t *t = (map_unassoc_sta_link_metrics_response_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_UNASSOCIATED_STA_LINK_METRICS_RESPONSE); + fail_unless(t->op_class == 81); + fail_unless(t->stas_nr == 1); + fail_unless(!memcmp(t->stas[0].mac, (mac_addr){0x70, 0x28, 0x8B, 0x22, 0x23, 0x93}, sizeof(mac_addr))); + fail_unless(t->stas[0].channel == 6); + fail_unless(t->stas[0].time_delta == 0); + fail_unless(t->stas[0].rcpi_uplink == 134); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_99_beacon_metrics_query) +{ + VARS + READ_PARSE("tlv_99_beacon_metrics_query.pcap") + + map_beacon_metrics_query_tlv_t *t = (map_beacon_metrics_query_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_BEACON_METRICS_QUERY); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + fail_unless(!memcmp(t->bssid, (mac_addr){0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, sizeof(mac_addr))); + fail_unless(t->op_class == 121); + fail_unless(t->channel == 255); + fail_unless(t->reporting_detail == 0); + fail_unless(t->element_ids_nr == 0); + fail_unless(t->ssid_len == 11); + fail_unless(!memcmp(t->ssid, (char *)"frv_test_fh", 11)); + fail_unless(t->ap_channel_reports_nr == 2); + fail_unless(t->ap_channel_reports[0].op_class == 121); + fail_unless(map_cs_nr(&t->ap_channel_reports[0].channels) == 1); + fail_unless(map_cs_is_set(&t->ap_channel_reports[0].channels, 112)); + fail_unless(t->ap_channel_reports[1].op_class == 81); + fail_unless(map_cs_nr(&t->ap_channel_reports[1].channels) == 1); + fail_unless(map_cs_is_set(&t->ap_channel_reports[1].channels, 6)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_9A_beacon_metrics_response) +{ + VARS + READ_PARSE("tlv_9A_beacon_metrics_response.pcap") + + map_beacon_metrics_response_tlv_t *t = (map_beacon_metrics_response_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_BEACON_METRICS_RESPONSE); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + fail_unless(t->status_code == 0); + fail_unless(t->elements_nr == 2); + fail_unless(t->elements[0].rcpi == 112); + fail_unless(!memcmp(t->elements[0].bssid, (mac_addr){0x22, 0xB0, 0x01, 0xBF, 0xA2, 0xB5}, sizeof(mac_addr))); + fail_unless(t->elements[1].rcpi == 134); + fail_unless(!memcmp(t->elements[1].bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_9B_steering_request) +{ + VARS + READ_PARSE("tlv_9B_steering_request.pcap") + + map_steering_request_tlv_t *t = (map_steering_request_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_STEERING_REQUEST); + fail_unless(!memcmp(t->bssid, (mac_addr){0x22, 0xB0, 0x01, 0xBF, 0xA2, 0xB5}, sizeof(mac_addr))); + fail_unless(t->flag == (MAP_STEERING_REQUEST_FLAG_MANDATE | MAP_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT | MAP_STEERING_REQUEST_FLAG_BTM_ABRIDGED)); + fail_unless(t->opportunity_wnd == 0); + fail_unless(t->disassociation_timer == 6000); + fail_unless(t->sta_macs_nr == 1); + fail_unless(!memcmp(t->sta_macs[0], (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + fail_unless(t->target_bsss_nr == 1); + fail_unless(!memcmp(t->target_bsss[0].bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->target_bsss[0].op_class == 81); + fail_unless(t->target_bsss[0].channel == 6); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_9C_steering_btm_report) +{ + VARS + READ_PARSE("tlv_9C_steering_btm_report.pcap") + + map_steering_btm_report_tlv_t *t = (map_steering_btm_report_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_STEERING_BTM_REPORT); + fail_unless(!memcmp(t->bssid, (mac_addr){0x22, 0xB0, 0x01, 0xBF, 0xA2, 0xB5}, sizeof(mac_addr))); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + fail_unless(t->btm_status_code == 0); + fail_unless(t->target_bssid_present == 1); + fail_unless(!memcmp(t->target_bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_9D_client_association_control_request) +{ + VARS + READ_PARSE("tlv_9D_client_association_control_request.pcap") + + map_client_assoc_control_request_tlv_t *t = (map_client_assoc_control_request_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CLIENT_ASSOCIATION_CONTROL_REQUEST); + fail_unless(!memcmp(t->bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->association_control == 0); + fail_unless(t->validity_period == 30); + fail_unless(t->sta_macs_nr == 1); + fail_unless(!memcmp(t->sta_macs[0], (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_9E_backhaul_steering_request) +{ + VARS + READ_PARSE("tlv_9E_backhaul_steering_request.pcap") + + map_backhaul_steering_request_tlv_t *t = (map_backhaul_steering_request_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_BACKHAUL_STEERING_REQUEST); + fail_unless(!memcmp(t->bsta_mac, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}, sizeof(mac_addr))); + fail_unless(!memcmp(t->target_bssid, (mac_addr){0x22, 0xB0, 0x01, 0xBF, 0xA2, 0xB5}, sizeof(mac_addr))); + fail_unless(t->target_op_class == 121); + fail_unless(t->target_channel == 112); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_9F_backhaul_steering_response) +{ + VARS + READ_PARSE("tlv_9F_backhaul_steering_response.pcap") + + map_backhaul_steering_response_tlv_t *t = (map_backhaul_steering_response_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_BACKHAUL_STEERING_RESPONSE); + fail_unless(!memcmp(t->bsta_mac, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}, sizeof(mac_addr))); + fail_unless(!memcmp(t->target_bssid, (mac_addr){0x22, 0xB0, 0x01, 0xBF, 0xA2, 0xB5}, sizeof(mac_addr))); + fail_unless(t->result == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_A0_higher_layer_data) +{ + VARS + READ_PARSE("tlv_A0_higher_layer_data.pcap") + + map_higher_layer_data_tlv_t *t = (map_higher_layer_data_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_HIGHER_LAYER_DATA); + fail_unless(t->protocol == 0x0D); + fail_unless(t->payload_len == 4); + fail_unless(t->payload && !memcmp(t->payload, (uint8_t[]){0x01, 0x02, 0x03, 0x04}, 4)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_A1_ap_capability) +{ + VARS + READ_PARSE("tlv_A1_ap_capability.pcap") + + map_ap_cap_tlv_t *t = (map_ap_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_CAPABILITY); + fail_unless(t->operating_unsupported_link_metrics == 1); + fail_unless(t->non_operating_unsupported_link_metrics == 0); + fail_unless(t->agent_initiated_steering == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_A2_associated_sta_traffic_stats) +{ + VARS + READ_PARSE("tlv_A2_associated_sta_traffic_stats.pcap") + + map_assoc_sta_traffic_stats_tlv_t *t = (map_assoc_sta_traffic_stats_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_ASSOCIATED_STA_TRAFFIC_STATS); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + fail_unless(t->tx_bytes == 177364); + fail_unless(t->rx_bytes == 232378); + fail_unless(t->tx_packets == 385); + fail_unless(t->rx_packets == 2215); + fail_unless(t->tx_packet_errors == 0); + fail_unless(t->rx_packet_errors == 0); + fail_unless(t->retransmissions == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_A3_error_code) +{ + VARS + READ_PARSE("tlv_A3_error_code.pcap") + + map_error_code_tlv_t *t = (map_error_code_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_ERROR_CODE); + fail_unless(t->reason_code == 2); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x70, 0x28, 0x8B, 0x22, 0x23, 0x93}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +/*####################################################################### +# MAP R2 TLVS # +########################################################################*/ +START_TEST(test_A4_channel_scan_reporting_policy) +{ + VARS + READ_PARSE("tlv_A4_channel_scan_reporting_policy.pcap") + + map_channel_scan_reporting_policy_tlv_t *t = (map_channel_scan_reporting_policy_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CHANNEL_SCAN_REPORTING_POLICY); + fail_unless(t->report_independent_ch_scans == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_A5_channel_scan_capabilities) +{ + VARS + READ_PARSE("tlv_A5_channel_scan_capabilities.pcap") + + map_channel_scan_cap_tlv_t *t = (map_channel_scan_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CHANNEL_SCAN_CAPABILITIES); + fail_unless(t->radios_nr == 2); + + fail_unless(!memcmp(t->radios[0].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF2}, sizeof(mac_addr))); + fail_unless(t->radios[0].boot_only == 0x0); + fail_unless(t->radios[0].scan_impact == MAP_SCAN_IMPACT_TIME_SLICING); + fail_unless(t->radios[0].min_scan_interval == 900); + fail_unless(t->radios[0].op_classes_nr == 2); + fail_unless(t->radios[0].op_classes[0].op_class == 81); + fail_unless(map_cs_nr(&t->radios[0].op_classes[0].channels) == 0); + fail_unless(t->radios[0].op_classes[1].op_class == 82); + fail_unless(map_cs_nr(&t->radios[0].op_classes[1].channels) == 0); + + fail_unless(!memcmp(t->radios[1].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}, sizeof(mac_addr))); + fail_unless(t->radios[0].boot_only == 0x0); + fail_unless(t->radios[0].scan_impact == MAP_SCAN_IMPACT_TIME_SLICING); + fail_unless(t->radios[1].min_scan_interval == 900); + fail_unless(t->radios[1].op_classes_nr == 5); + fail_unless(t->radios[1].op_classes[0].op_class == 115); + fail_unless(map_cs_nr(&t->radios[1].op_classes[0].channels) == 0); + fail_unless(t->radios[1].op_classes[4].op_class == 125); + fail_unless(map_cs_nr(&t->radios[1].op_classes[4].channels) == 2); + fail_unless(map_cs_is_set(&t->radios[1].op_classes[4].channels, 165)); + fail_unless(map_cs_is_set(&t->radios[1].op_classes[4].channels, 169)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_A6_channel_scan_request) +{ + VARS + READ_PARSE("tlv_A6_channel_scan_request.pcap"); + + map_channel_scan_request_tlv_t *t = (map_channel_scan_request_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CHANNEL_SCAN_REQUEST); + fail_unless(t->fresh_scan_performed == 0); + fail_unless(t->radios_nr == 1); + fail_unless(!memcmp(t->radios[0].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF2}, sizeof(mac_addr))); + fail_unless(t->radios[0].op_classes_nr == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_A6_channel_scan_request_2) +{ + VARS + READ_PARSE("tlv_A6_channel_scan_request_2.pcap"); + + map_channel_scan_request_tlv_t *t = (map_channel_scan_request_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CHANNEL_SCAN_REQUEST); + fail_unless(t->fresh_scan_performed == 1); + fail_unless(t->radios_nr == 1); + fail_unless(!memcmp(t->radios[0].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}, sizeof(mac_addr))); + fail_unless(t->radios[0].op_classes_nr == 3); + + fail_unless(t->radios[0].op_classes[0].op_class == 118); + fail_unless(map_cs_nr(&t->radios[0].op_classes[0].channels) == 3); + fail_unless(map_cs_is_set(&t->radios[0].op_classes[0].channels, 64)); + fail_unless(map_cs_is_set(&t->radios[0].op_classes[0].channels, 100)); + fail_unless(map_cs_is_set(&t->radios[0].op_classes[0].channels, 104)); + + fail_unless(t->radios[0].op_classes[1].op_class == 125); + fail_unless(map_cs_nr(&t->radios[0].op_classes[1].channels) == 1); + fail_unless(map_cs_is_set(&t->radios[0].op_classes[1].channels, 169)); + + fail_unless(t->radios[0].op_classes[2].op_class == 128); + fail_unless(map_cs_nr(&t->radios[0].op_classes[2].channels) == 0); + + FORGE + CLEANUP +} +END_TEST + + +START_TEST(test_A7_channel_scan_result) +{ + VARS + READ_PARSE("tlv_A7_channel_scan_result.pcap") + + map_channel_scan_result_tlv_t *t = (map_channel_scan_result_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CHANNEL_SCAN_RESULT); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}, sizeof(mac_addr))); + fail_unless(t->op_class == 125); + fail_unless(t->channel == 173); + fail_unless(t->scan_status == 0); + fail_unless(t->timestamp_len == 28); + fail_unless(!memcmp(t->timestamp, (char *)"2020-12-21T01:52:41.0Z-05:00", 28)); + fail_unless(t->utilization == 0); + fail_unless(t->noise == 165); + fail_unless(t->aggregate_scan_duration == 30); + fail_unless(t->scan_type == 1); + fail_unless(t->neighbors_nr == 2); + + fail_unless(!memcmp(t->neighbors[0].bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}, sizeof(mac_addr))); + fail_unless(t->neighbors[0].ssid_len == 11); + fail_unless(t->neighbors[0].rcpi == 176); + fail_unless(t->neighbors[0].ch_bw_len == 2); + fail_unless(t->neighbors[0].bss_load_elem_present == 1); + fail_unless(t->neighbors[0].channel_utilization == 3); + fail_unless(t->neighbors[0].stas_nr == 0); + fail_unless(!memcmp(t->neighbors[0].ssid, (char *)"frv_test_fh", 11)); + fail_unless(!memcmp(t->neighbors[0].ch_bw, (char *)"80", 2)); + + fail_unless(!memcmp(t->neighbors[1].bssid, (mac_addr){0x6A, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->neighbors[1].ssid_len == 11); + fail_unless(t->neighbors[1].rcpi == 176); + fail_unless(t->neighbors[1].ch_bw_len == 2); + fail_unless(t->neighbors[1].bss_load_elem_present == 1); + fail_unless(t->neighbors[1].channel_utilization == 3); + fail_unless(t->neighbors[1].stas_nr == 1); + fail_unless(!memcmp(t->neighbors[1].ssid, (char *)"frv_test_bh", 11)); + fail_unless(!memcmp(t->neighbors[1].ch_bw, (char *)"80", 2)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_A7_channel_scan_result_malformed) +{ + packet_t *packet; + uint8_t *p_tlv = NULL; + + fail_unless(!!(packet = pcap_read_first_packet(DATA_DIR "/" "tlv_A7_channel_scan_result_malformed.pcap"))); + p_tlv = parse_1905_TLV_from_packet(TLVS_START, TLVS_LEN); + + /* We should deny malformed TLVs */ + fail_unless(p_tlv == NULL); + + free(packet); +} +END_TEST + +START_TEST(test_A8_timestamp) +{ + VARS + READ_PARSE("tlv_A8_timestamp.pcap") + + map_timestamp_tlv_t *t = (map_timestamp_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_TIMESTAMP); + fail_unless(t->timestamp_len == 28); + fail_unless(!memcmp(t->timestamp, (char *)"2020-12-21T01:58:38.0Z-05:00", 28)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_AD_cac_request) +{ + VARS + READ_PARSE("tlv_AD_cac_request.pcap") + + map_cac_request_tlv_t *t = (map_cac_request_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CAC_REQUEST); + fail_unless(t->radios_nr == 1); + fail_unless(!memcmp(t->radios[0].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xBD, 0xBD, 0xFE}, sizeof(mac_addr))); + fail_unless(t->radios[0].op_class == 120); + fail_unless(t->radios[0].channel == 64); + fail_unless(t->radios[0].cac_method == MAP_CAC_METHOD_CONTINUOUS); + fail_unless(t->radios[0].cac_completion_action == MAP_CAC_ACTION_RETURN_PREV_OP_CONF); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_AE_cac_termination) +{ + VARS + READ_PARSE("tlv_AE_cac_termination.pcap") + + map_cac_termination_tlv_t *t = (map_cac_termination_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CAC_TERMINATION); + fail_unless(t->radios_nr == 1); + fail_unless(!memcmp(t->radios[0].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xBD, 0xBD, 0xFE}, sizeof(mac_addr))); + fail_unless(t->radios[0].op_class == 120); + fail_unless(t->radios[0].channel == 64); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_AF_cac_completion_report) +{ + VARS + READ_PARSE("tlv_AF_cac_completion_report.pcap") + + map_cac_completion_report_tlv_t *t = (map_cac_completion_report_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CAC_COMPLETION_REPORT); + fail_unless(t->radios_nr == 1); + + fail_unless(!memcmp(t->radios[0].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}, sizeof(mac_addr))); + fail_unless(t->radios[0].op_class == 128); + fail_unless(t->radios[0].channel == 106); + fail_unless(t->radios[0].status == 0); + fail_unless(t->radios[0].detected_pairs_nr == 4); + fail_unless(t->radios[0].detected_pairs[0].op_class == 121); + fail_unless(t->radios[0].detected_pairs[0].channel == 100); + fail_unless(t->radios[0].detected_pairs[1].op_class == 121); + fail_unless(t->radios[0].detected_pairs[1].channel == 104); + fail_unless(t->radios[0].detected_pairs[2].op_class == 121); + fail_unless(t->radios[0].detected_pairs[2].channel == 108); + fail_unless(t->radios[0].detected_pairs[3].op_class == 121); + fail_unless(t->radios[0].detected_pairs[3].channel == 112); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_B1_cac_status_report) +{ + VARS + READ_PARSE("tlv_B1_cac_status_report.pcap") + + map_cac_status_report_tlv_t *t = (map_cac_status_report_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CAC_STATUS_REPORT); + fail_unless(t->available_pairs_nr == 81); + fail_unless(t->available_pairs[0].op_class == 81); + fail_unless(t->available_pairs[0].channel == 1); + fail_unless(t->available_pairs[0].minutes_since_cac_completion == 0); + fail_unless(t->available_pairs[80].op_class == 128); + fail_unless(t->available_pairs[80].channel == 171); + fail_unless(t->available_pairs[80].minutes_since_cac_completion == 0); + fail_unless(t->non_occupancy_pairs_nr == 1); + fail_unless(t->non_occupancy_pairs[0].op_class == 115); + fail_unless(t->non_occupancy_pairs[0].channel == 100); + fail_unless(t->non_occupancy_pairs[0].seconds_remaining_non_occupancy_duration == 256); + fail_unless(t->ongoing_cac_pairs_nr == 2); + fail_unless(t->ongoing_cac_pairs[0].op_class == 116); + fail_unless(t->ongoing_cac_pairs[0].channel == 104); + fail_unless(t->ongoing_cac_pairs[0].seconds_remaining_cac_completion == 4096); + fail_unless(t->ongoing_cac_pairs[1].op_class == 117); + fail_unless(t->ongoing_cac_pairs[1].channel == 108); + fail_unless(t->ongoing_cac_pairs[1].seconds_remaining_cac_completion == 8192); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_B2_cac_capabilities) +{ + VARS + READ_PARSE("tlv_B2_cac_capabilities.pcap") + + map_cac_cap_tlv_t *t = (map_cac_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CAC_CAPABILITIES); + fail_unless(t->country_code == 0x5553 /* US */); + fail_unless(t->radios_nr == 1); + fail_unless(!memcmp(t->radios[0].radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}, sizeof(mac_addr))); + fail_unless(t->radios[0].cac_methods_nr == 1); + fail_unless(t->radios[0].cac_methods[0].cac_method == MAP_CAC_METHOD_MIMO_DIM_REDUCED); + fail_unless(t->radios[0].cac_methods[0].cac_duration == 60); + fail_unless(t->radios[0].cac_methods[0].op_classes_nr == 7); + + fail_unless(t->radios[0].cac_methods[0].op_classes[0].op_class == 118); + fail_unless(map_cs_nr(&t->radios[0].cac_methods[0].op_classes[0].channels) == 4); + fail_unless(map_cs_is_set(&t->radios[0].cac_methods[0].op_classes[0].channels, 52)); + fail_unless(map_cs_is_set(&t->radios[0].cac_methods[0].op_classes[0].channels, 64)); + + fail_unless(t->radios[0].cac_methods[0].op_classes[6].op_class == 128); + fail_unless(map_cs_nr(&t->radios[0].cac_methods[0].op_classes[6].channels) == 2); + fail_unless(map_cs_is_set(&t->radios[0].cac_methods[0].op_classes[6].channels, 58)); + fail_unless(map_cs_is_set(&t->radios[0].cac_methods[0].op_classes[6].channels, 122)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_B3_multiap_profile) +{ + VARS + READ_PARSE("tlv_B3_multiap_profile.pcap") + + map_multiap_profile_tlv_t *t = (map_multiap_profile_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_MULTIAP_PROFILE); + fail_unless(t->map_profile == 2); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_B4_profile2_ap_capability) +{ + VARS + READ_PARSE("tlv_B4_profile2_ap_capability.pcap") + + map_profile2_ap_cap_tlv_t *t = (map_profile2_ap_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_PROFILE2_AP_CAPABILITY); + fail_unless(t->byte_counter_unit == MAP_BYTE_COUNTER_UNIT_KIBI_BYTES); + fail_unless(t->max_vid_count == 2); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_B5_default_8021q_settings) +{ + VARS + READ_PARSE("tlv_B5_default_8021q_settings.pcap") + + map_default_8021q_settings_tlv_t *t = (map_default_8021q_settings_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_DEFAULT_8021Q_SETTINGS); + fail_unless(t->primary_vlan_id == 10); + fail_unless(t->default_pcp == 3); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_B6_traffic_separation_policy) +{ + VARS + READ_PARSE("tlv_B6_traffic_separation_policy.pcap") + + map_traffic_separation_policy_tlv_t *t = (map_traffic_separation_policy_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_TRAFFIC_SEPARATION_POLICY); + fail_unless(t->ssids_nr == 2); + + fail_unless(t->ssids[0].ssid_len == 11); + fail_unless(!memcmp(t->ssids[0].ssid, (char *)"frv_test_fh", 11)); + fail_unless(t->ssids[0].vlan_id == 10); + + fail_unless(t->ssids[1].ssid_len == 11); + fail_unless(!memcmp(t->ssids[1].ssid, (char *)"frv_test_bh", 11)); + fail_unless(t->ssids[1].vlan_id == 20); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_BC_profile2_error_code) +{ + VARS + READ_PARSE("tlv_BC_profile2_error_code.pcap") + + map_profile2_error_code_tlv_t *t = (map_profile2_error_code_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_PROFILE2_ERROR_CODE); + fail_unless(t->reason_code == MAP_ERROR_CODE2_TS_COMBINED_FH_PROFILE1_BH_UNSUPPORTED); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x70, 0x28, 0x8B, 0x22, 0x23, 0x93}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_BE_ap_radio_advanced_capabilities) +{ + VARS + READ_PARSE("tlv_BE_ap_radio_advanced_capabilities.pcap") + + map_ap_radio_advanced_cap_tlv_t *t = (map_ap_radio_advanced_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_RADIO_ADVANCED_CAPABILITIES); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}, sizeof(mac_addr))); + fail_unless(t->combined_fh_profile2_bh == 1); + fail_unless(t->combined_profile1_bh_profile2_bh == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_BF_association_status_notification) +{ + VARS + READ_PARSE("tlv_BF_association_status_notification.pcap") + + map_assoc_status_notification_tlv_t *t = (map_assoc_status_notification_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_ASSOCIATION_STATUS_NOTIFICATION); + fail_unless(t->bsss_nr == 2); + fail_unless(!memcmp(t->bsss[0].bssid, (mac_addr){0x6A, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->bsss[0].assoc_allowance_status == 0); + fail_unless(!memcmp(t->bsss[1].bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}, sizeof(mac_addr))); + fail_unless(t->bsss[1].assoc_allowance_status == 1); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_C0_source_info) +{ + VARS + READ_PARSE("tlv_C0_source_info.pcap") + + map_source_info_tlv_t *t = (map_source_info_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_SOURCE_INFO); + fail_unless(!memcmp(t->src_mac, (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_C1_tunneled_message_type) +{ + VARS + READ_PARSE("tlv_C1_tunneled_message_type.pcap") + + map_tunneled_message_type_tlv_t *t = (map_tunneled_message_type_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_TUNNELED_MESSAGE_TYPE); + fail_unless(t->message_type == TUNNELED_MSG_PAYLOAD_REASSOC_REQ); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_C2_tunneled) +{ + VARS + READ_PARSE("tlv_C2_tunneled.pcap") + + map_tunneled_tlv_t *t = (map_tunneled_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_TUNNELED); + fail_unless(t->frame_body_len == 246); + fail_unless(t->frame_body != NULL); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_C3_profile2_steering_request) +{ + VARS + READ_PARSE("tlv_C3_profile2_steering_request.pcap") + + map_profile2_steering_request_tlv_t *t = (map_profile2_steering_request_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_PROFILE2_STEERING_REQUEST); + fail_unless(!memcmp(t->bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->flag == (MAP_STEERING_REQUEST_FLAG_MANDATE | MAP_STEERING_REQUEST_FLAG_BTM_DISASSOC_IMMINENT | MAP_STEERING_REQUEST_FLAG_BTM_ABRIDGED)); + fail_unless(t->opportunity_wnd == 0); + fail_unless(t->disassociation_timer == 6000); + fail_unless(t->sta_macs_nr == 1); + fail_unless(!memcmp(t->sta_macs[0], (mac_addr){0x4E, 0x8F, 0x3B, 0x88, 0xF5, 0x57}, sizeof(mac_addr))); + fail_unless(t->target_bsss_nr == 1); + fail_unless(!memcmp(t->target_bsss[0].bssid, (mac_addr){0x22, 0xB0, 0x01, 0xBf, 0xA2, 0xB5}, sizeof(mac_addr))); + fail_unless(t->target_bsss[0].op_class == 121); + fail_unless(t->target_bsss[0].channel == 112); + fail_unless(t->target_bsss[0].reason == 3); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_C4_unsuccessful_association_policy) +{ + VARS + READ_PARSE("tlv_C4_unsuccessful_association_policy.pcap") + + map_unsuccessful_assoc_policy_tlv_t *t = (map_unsuccessful_assoc_policy_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_UNSUCCESSFUL_ASSOCIATION_POLICY); + fail_unless(t->report_flag == MAP_UNSUCCESSFUL_ASSOC_REPORT); + fail_unless(t->max_reporting_rate == 60); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_C5_metric_collection_interval) +{ + VARS + READ_PARSE("tlv_C5_metric_collection_interval.pcap") + + map_metric_collection_interval_tlv_t *t = (map_metric_collection_interval_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_METRIC_COLLECTION_INTERVAL); + fail_unless(t->metric_collection_interval == 2219074296); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_C6_radio_metrics) +{ + VARS + READ_PARSE("tlv_C6_radio_metrics.pcap") + + map_radio_metrics_tlv_t *t = (map_radio_metrics_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_RADIO_METRICS); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->noise == 28); + fail_unless(t->transmit == 10); + fail_unless(t->receive_self == 0); + fail_unless(t->receive_other == 7); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_C7_ap_extended_metrics) +{ + VARS + READ_PARSE("tlv_C7_ap_extended_metrics.pcap") + + map_ap_ext_metrics_tlv_t *t = (map_ap_ext_metrics_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_EXTENDED_METRICS); + fail_unless(!memcmp(t->bssid, (mac_addr){0x6A, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->tx_ucast_bytes == 256); + fail_unless(t->rx_ucast_bytes == 257); + fail_unless(t->tx_mcast_bytes == 258); + fail_unless(t->rx_mcast_bytes == 259); + fail_unless(t->tx_bcast_bytes == 260); + fail_unless(t->rx_bcast_bytes == 261); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_C8_associated_sta_extended_link_metrics) +{ + VARS + READ_PARSE("tlv_C8_associated_sta_extended_link_metrics.pcap") + + map_assoc_sta_ext_link_metrics_tlv_t *t = (map_assoc_sta_ext_link_metrics_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_ASSOCIATED_STA_EXTENDED_LINK_METRICS); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x70, 0x28, 0x8B, 0x22, 0x23, 0x93}, sizeof(mac_addr))); + fail_unless(t->bsss_nr == 1); + fail_unless(!memcmp(t->bsss[0].bssid, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF2}, sizeof(mac_addr))); + fail_unless(t->bsss[0].last_data_dl_rate == 65); + fail_unless(t->bsss[0].last_data_ul_rate == 6); + fail_unless(t->bsss[0].utilization_rx == 478000); + fail_unless(t->bsss[0].utilization_tx == 478000); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_C9_status_code) +{ + VARS + READ_PARSE("tlv_C9_status_code.pcap") + + map_status_code_tlv_t *t = (map_status_code_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_STATUS_CODE); + fail_unless(t->status_code == 0x000D); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_CA_reason_code) +{ + VARS + READ_PARSE("tlv_CA_reason_code.pcap") + + map_reason_code_tlv_t *t = (map_reason_code_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_REASON_CODE); + fail_unless(t->reason_code == 0x0003); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_CB_backhaul_sta_radio_capabilities) +{ + VARS + READ_PARSE("tlv_CB_backhaul_sta_radio_capabilities.pcap") + + map_backhaul_sta_radio_cap_tlv_t *t = (map_backhaul_sta_radio_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_BACKHAUL_STA_RADIO_CAPABILITIES); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0xAE, 0x86, 0xF1}, sizeof(mac_addr))); + fail_unless(t->bsta_mac_present == 1); + fail_unless(!memcmp(t->bsta_mac, (mac_addr){0xF6, 0x17, 0xB8, 0xAE, 0x86, 0xF2}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_D0_backhaul_bss_configuration) +{ + VARS + READ_PARSE("tlv_D0_backhaul_bss_configuration.pcap") + + map_backhaul_bss_configuration_tlv_t *t = (map_backhaul_bss_configuration_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_BACKHAUL_BSS_CONFIGURATION); + fail_unless(!memcmp(t->bssid, (mac_addr){0x6A, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->p1_bsta_disallowed == 1); + fail_unless(t->p2_bsta_disallowed == 0); + + FORGE + CLEANUP +} +END_TEST + +/*####################################################################### +# MAP R3 TLVS # +########################################################################*/ +START_TEST(test_A9_1905_security_capability) +{ + VARS + READ_PARSE("tlv_A9_1905_layer_security_capability.pcap") + + map_1905_security_cap_tlv_t *t = (map_1905_security_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_1905_LAYER_SECURITY_CAPABILITY); + fail_unless(t->onboarding_protocol == 0); + fail_unless(t->mic_algorithm == 0); + fail_unless(t->encryption_algorithm == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_AA_ap_wifi6_capabilities) +{ + VARS + READ_PARSE("tlv_AA_ap_wifi6_capabilities.pcap") + int i, j; + + map_ap_wifi6_cap_tlv_t *t = (map_ap_wifi6_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AP_WIFI6_CAPABILITIES); + fail_unless(!memcmp(t->radio_id, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x58, 0x7E}, sizeof(mac_addr))); + fail_unless(t->roles_nr == 2); + for (i = 0; i < t->roles_nr; i++) { + fail_unless(t->cap_data[i].agent_role == i); + fail_unless(t->cap_data[i].he160 == 1); + fail_unless(t->cap_data[i].he8080 == 0); + fail_unless(t->cap_data[i].mcs_nss_nr == 8); + for (j = 0; j < t->cap_data[i].mcs_nss_nr / 2; j++) { + fail_unless(t->cap_data[i].mcs_nss[j] == 0xFFAA); + } + fail_unless(t->cap_data[i].su_beamformer == 1); + fail_unless(t->cap_data[i].su_beamformee == 0); + fail_unless(t->cap_data[i].mu_beamformer == 0); + fail_unless(t->cap_data[i].beamformee_sts_l80 == 0); + fail_unless(t->cap_data[i].beamformee_sts_g80 == 0); + fail_unless(t->cap_data[i].ul_mu_mimo == 0); + fail_unless(t->cap_data[i].ul_ofdma == 1); + fail_unless(t->cap_data[i].dl_ofdma == 1); + } + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_AB_mic) +{ + VARS + READ_PARSE("tlv_AB_mic.pcap") + uint8_t integrity_tx_ctr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + uint8_t mic[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + }; + uint16_t mic_len = sizeof(mic); + map_mic_tlv_t *t = (map_mic_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + fail_unless(t->tlv_type == TLV_TYPE_MIC); + fail_unless(t->gtk_key_id == 1); + fail_unless(t->mic_version == 0); + fail_unless(t->reserved == 0); + fail_unless(!memcmp(t->integrity_tx_counter, integrity_tx_ctr, INTEGRITY_TX_COUNTER_LEN)); + fail_unless(!memcmp(t->src_al_mac, (mac_addr){0x02, 0x01, 0x02, 0x03, 0x04, 0x05}, ETHER_ADDR_LEN)); + fail_unless(t->mic_len == mic_len); + fail_unless(!memcmp(t->mic, mic, t->mic_len)); + FORGE + CLEANUP +} +END_TEST +START_TEST(test_AC_encrypted_payload) +{ + VARS + READ_PARSE("tlv_AC_encrypted_payload.pcap") + uint8_t encryption_tx_counter[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + uint8_t siv_output[] = {0xfc, 0x4c, 0xa5, 0xa1, 0x31, 0xdf, 0xcf, 0xb9, 0xf7, 0xf6, 0xd6, 0x8b, 0xfc, 0xd5, 0x78, 0xd0, 0x0a, 0x74, 0xf3, 0xb5}; + uint16_t siv_len = sizeof(siv_output); + map_encrypted_payload_tlv_t *t = (map_encrypted_payload_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + fail_unless(t->tlv_type == TLV_TYPE_ENCRYPTED_PAYLOAD); + fail_unless(!memcmp(t->encr_tx_counter, encryption_tx_counter, 6)); + fail_unless(!memcmp(t->src_al_mac, (mac_addr){0x02, 0x01, 0x02, 0x03, 0x04, 0x05}, ETHER_ADDR_LEN)); + fail_unless(!memcmp(t->dst_al_mac, (mac_addr){0xf6, 0x17, 0xb8, 0xae, 0x89, 0xa3}, ETHER_ADDR_LEN)); + fail_unless(t->siv_len == siv_len); + fail_unless(!memcmp(t->siv_output, siv_output, t->siv_len)); + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_B0_associated_wifi6_sta_status) +{ + VARS + READ_PARSE("tlv_B0_associated_wifi6_sta_status_report.pcap") + + map_assoc_wifi6_sta_status_tlv_t *t = (map_assoc_wifi6_sta_status_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_ASSOCIATED_WIFI6_STA_STATUS_REPORT); + + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x02, 0x7F, 0x9F, 0xA0, 0xD7, 0x10}, sizeof(mac_addr))); + fail_unless(t->TID_nr == 3); + + fail_unless(t->TID[0] == 0); + fail_unless(t->queue_size[0] == 65); + + fail_unless(t->TID[1] == 1); + fail_unless(t->queue_size[1] == 4); + + fail_unless(t->TID[2] == 6); + fail_unless(t->queue_size[2] == 19); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_B7_bss_configuration_report) +{ + VARS + READ_PARSE("tlv_B7_bss_configuration_report.pcap") + + map_bss_configuration_report_tlv_t *t = (map_bss_configuration_report_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_BSS_CONFIGURATION_REPORT); + fail_unless(t->radios_nr == 2); + + fail_unless(!memcmp(t->radios[0].ruid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->radios[0].bss_nr == 1); + + fail_unless(!memcmp(t->radios[0].bss[0].bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6B}, sizeof(mac_addr))); + fail_unless(t->radios[0].bss[0].backhaul_bss == 0); + fail_unless(t->radios[0].bss[0].fronthaul_bss == 1); + fail_unless(t->radios[0].bss[0].r1_disallowed_status == 0); + fail_unless(t->radios[0].bss[0].r2_disallowed_status == 1); + fail_unless(t->radios[0].bss[0].multiple_bssid == 0); + fail_unless(t->radios[0].bss[0].transmitted_bssid == 0); + fail_unless(t->radios[0].bss[0].reserved2 == 0); + fail_unless(t->radios[0].bss[0].ssid_len == 10); + fail_unless(!strcmp(t->radios[0].bss[0].ssid, "ssid_2g_fh")); + + fail_unless(!memcmp(t->radios[1].ruid, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x57, 0x6A}, sizeof(mac_addr))); + fail_unless(t->radios[1].bss_nr == 2); + + fail_unless(!memcmp(t->radios[1].bss[0].bssid, (mac_addr){0x82, 0xAA, 0xCC, 0xAA, 0x00, 0x05}, sizeof(mac_addr))); + fail_unless(t->radios[1].bss[0].backhaul_bss == 0); + fail_unless(t->radios[1].bss[0].fronthaul_bss == 1); + fail_unless(t->radios[1].bss[0].r1_disallowed_status == 0); + fail_unless(t->radios[1].bss[0].r2_disallowed_status == 0); + fail_unless(t->radios[1].bss[0].multiple_bssid == 1); + fail_unless(t->radios[1].bss[0].transmitted_bssid == 0); + fail_unless(t->radios[1].bss[0].reserved2 == 0); + fail_unless(t->radios[1].bss[0].ssid_len == 10); + fail_unless(!strcmp(t->radios[1].bss[0].ssid, "ssid_5g_fh")); + + fail_unless(!memcmp(t->radios[1].bss[1].bssid, (mac_addr){0x82, 0xAA, 0xCC, 0xAA, 0x00, 0x06}, sizeof(mac_addr))); + fail_unless(t->radios[1].bss[1].backhaul_bss == 1); + fail_unless(t->radios[1].bss[1].fronthaul_bss == 0); + fail_unless(t->radios[1].bss[1].r1_disallowed_status == 0); + fail_unless(t->radios[1].bss[1].r2_disallowed_status == 0); + fail_unless(t->radios[1].bss[1].multiple_bssid == 0); + fail_unless(t->radios[1].bss[1].transmitted_bssid == 1); + fail_unless(t->radios[1].bss[1].reserved2 == 0); + fail_unless(t->radios[1].bss[1].ssid_len == 10); + fail_unless(!strcmp(t->radios[1].bss[1].ssid, "ssid_5g_bh")); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_B8_bssid) +{ + VARS + READ_PARSE("tlv_B8_bssid.pcap") + + map_bssid_tlv_t *t = (map_bssid_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_BSSID); + fail_unless(!memcmp(t->bssid, (mac_addr){0xF4, 0x17, 0xB8, 0x00, 0x01, 0x02}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_CC_akm_suite_capabilities) +{ + VARS + READ_PARSE("tlv_CC_akm_suite_capabilities.pcap") + + uint8_t ieee80211_oui[3] = {0x00, 0x0F, 0xAC}; + uint8_t wfa_oui[3] = {0x50, 0x6F, 0x9A}; + + map_akm_suite_cap_tlv_t *t = (map_akm_suite_cap_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AKM_SUITE_CAPABILITIES); + + fail_unless(t->bh_akm_suites_nr == 4); + fail_unless(!memcmp(t->bh_akm_suites[2].oui, ieee80211_oui, sizeof(t->bh_akm_suites[2].oui))); + fail_unless(t->bh_akm_suites[2].akm_suite_type == 8); + fail_unless(!memcmp(t->bh_akm_suites[3].oui, wfa_oui, sizeof(t->bh_akm_suites[3].oui))); + fail_unless(t->bh_akm_suites[3].akm_suite_type == 2); + + fail_unless(t->fh_akm_suites_nr == 6); + fail_unless(!memcmp(t->fh_akm_suites[0].oui, ieee80211_oui, sizeof(t->fh_akm_suites[0].oui))); + fail_unless(t->fh_akm_suites[0].akm_suite_type == 2); + fail_unless(!memcmp(t->fh_akm_suites[3].oui, ieee80211_oui, sizeof(t->fh_akm_suites[3].oui))); + fail_unless(t->fh_akm_suites[3].akm_suite_type == 8); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_CE_1905_encap_eapol) +{ + VARS + READ_PARSE("tlv_CE_1905_encap_eapol.pcap") + + map_1905_encap_eapol_tlv_t *t = (map_1905_encap_eapol_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_1905_ENCAP_EAPOL); + fail_unless(t->frame_len == 16); + fail_unless(t->frame != NULL); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_D1_dpp_message) +{ + VARS + READ_PARSE("tlv_D1_dpp_message.pcap") + + map_dpp_message_tlv_t *t = (map_dpp_message_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_DPP_MESSAGE); + fail_unless(t->frame_len == 16); + fail_unless(t->frame != NULL); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_D2_dpp_cce_indication) +{ + VARS + READ_PARSE("tlv_D2_dpp_cce_indication.pcap") + + map_dpp_cce_indication_tlv_t *t = (map_dpp_cce_indication_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_DPP_CCE_INDICATION); + fail_unless(t->advertise == 1); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_CD_1905_encap_dpp) +{ + VARS + READ_PARSE("tlv_CD_1905_encap_dpp.pcap") + + map_1905_encap_dpp_tlv_t *t = (map_1905_encap_dpp_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_1905_ENCAP_DPP); + fail_unless(t->enrollee_mac_present == 1); + fail_unless(t->dpp_frame_indicator == 0); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x58, 0x7E}, sizeof(mac_addr))); + fail_unless(t->frame_type == 0x18); + fail_unless(t->frame_len == 2); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_CD_1905_encap_dpp_no_enrollee_mac) +{ + VARS + READ_PARSE("tlv_CD_1905_encap_dpp_no_enrollee_mac.pcap") + + map_1905_encap_dpp_tlv_t *t = (map_1905_encap_dpp_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_1905_ENCAP_DPP); + fail_unless(t->enrollee_mac_present == 0); + fail_unless(t->dpp_frame_indicator == 1); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, sizeof(mac_addr))); + fail_unless(t->frame_type == 0xFF); + fail_unless(t->frame_len == 6); + + FORGE + CLEANUP +} +END_TEST + + +START_TEST(test_D3_dpp_chirp_value) +{ + VARS + READ_PARSE("tlv_D3_dpp_chirp_value.pcap") + + map_dpp_chirp_value_tlv_t *t = (map_dpp_chirp_value_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_DPP_CHIRP_VALUE); + fail_unless(t->enrollee_mac_present == 1); + fail_unless(t->hash_validity == 1); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0xF4, 0x17, 0xB8, 0x86, 0x58, 0x7E}, sizeof(mac_addr))); + fail_unless(t->hash_len == 16); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_D3_dpp_chirp_value_no_enrollee_mac) +{ + VARS + READ_PARSE("tlv_D3_dpp_chirp_value_no_enrollee_mac.pcap") + + map_dpp_chirp_value_tlv_t *t = (map_dpp_chirp_value_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_DPP_CHIRP_VALUE); + fail_unless(t->enrollee_mac_present == 0); + fail_unless(t->hash_validity == 1); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, sizeof(mac_addr))); + fail_unless(t->hash_len == 16); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_D4_device_inventory) +{ + VARS + READ_PARSE("tlv_D4_device_inventory.pcap") + + map_device_inventory_tlv_t *t = (map_device_inventory_tlv_t *)p_tlv; + int i; + CHECK_TLV_STRUCT_SIZE + + char *test_serial = "AC2631948026129"; + char *test_version = "4.121.1.3D"; + char *test_env = "Linux 4.1.52"; + char *test_vendor = "Broadcom"; + uint8_t test_mac_list[2][6] = {{0xf4, 0x17, 0xb8, 0xa2, 0x5d, 0xe1}, + {0xf4, 0x17, 0xb8, 0xa2, 0x5d, 0xe2}}; + + fail_unless(t->tlv_type == TLV_TYPE_DEVICE_INVENTORY); + fail_unless(t->serial_len > 0); + fail_unless(memcmp(t->serial, test_serial, t->serial_len) == 0); + fail_unless(t->version_len > 0); + fail_unless(memcmp(t->version, test_version, t->version_len) == 0); + fail_unless(t->environment_len > 0); + fail_unless(memcmp(t->environment, test_env, t->environment_len) == 0); + fail_unless(t->radios_nr == 2); + + for (i = 0; i < t->radios_nr; i++) { + fail_unless(t->radios[i].vendor_len > 0); + fail_unless(memcmp(t->radios[i].ruid, test_mac_list[i], sizeof(mac_addr)) == 0); + fail_unless(memcmp(t->radios[i].vendor, test_vendor, t->radios[i].vendor_len) == 0); + } + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_D5_agent_list) +{ + VARS + READ_PARSE("tlv_D5_agent_list.pcap") + + map_agent_list_tlv_t *t = (map_agent_list_tlv_t *)p_tlv; + // int i; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AGENT_LIST); + fail_unless(t->agent_nr == 2); + + fail_unless(!memcmp(t->entries[0].al_mac, (mac_addr){0xF6, 0x17, 0xB8, 0x86, 0x57, 0x68}, sizeof(mac_addr))); + fail_unless(t->entries[0].map_profile == MAP_PROFILE_3); + fail_unless(t->entries[0].security == 0x01); + + fail_unless(!memcmp(t->entries[1].al_mac, (mac_addr){0xCE, 0xAA, 0xCC, 0xAA, 0x00, 0x00}, sizeof(mac_addr))); + fail_unless(t->entries[1].map_profile == MAP_PROFILE_2); + fail_unless(t->entries[1].security == 0x00); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_DD_controller_capability) +{ + VARS + READ_PARSE("tlv_DD_controller_capability.pcap") + + map_controller_capability_tlv_t *t = (map_controller_capability_tlv_t *)p_tlv; + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_CONTROLLER_CAPABILITY); + fail_unless(t->capability == MAP_CONTROLLER_CAP_KIBMIB_COUNTER); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_DF_wifi7_agent_capabilities) +{ + VARS + READ_PARSE("tlv_DF_wifi7_agent_capabilities.pcap") + + map_wifi7_agent_cap_tlv_t *t = (map_wifi7_agent_cap_tlv_t *)p_tlv; + + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_WIFI7_AGENT_CAPABILITIES); + fail_unless(t->max_mlds == 0); + fail_unless(t->ap_max_links == 0); + fail_unless(t->bsta_max_links == 0); + fail_unless(t->tid_to_link_map_cap == 0); + + fail_unless(t->radios_nr == 3); + + fail_unless(memcmp(t->radios[0].ruid, (mac_addr){0x00, 0x90, 0x4C, 0x4C, 0x94, 0x0D}, sizeof(mac_addr)) == 0); + fail_unless(t->radios[0].cap.ap_mld_modes.str == true); + fail_unless(t->radios[0].cap.ap_mld_modes.nstr == false); + fail_unless(t->radios[0].cap.ap_mld_modes.emlsr == false); + fail_unless(t->radios[0].cap.ap_mld_modes.emlmr == false); + fail_unless(t->radios[0].cap.bsta_mld_modes.str == true); + fail_unless(t->radios[0].cap.bsta_mld_modes.nstr == false); + fail_unless(t->radios[0].cap.bsta_mld_modes.emlsr == false); + fail_unless(t->radios[0].cap.bsta_mld_modes.emlmr == false); + + fail_unless(t->radios[0].cap.ap_str_records_nr == 2); + fail_unless(memcmp(t->radios[0].cap.ap_str_records[0].ruid, (mac_addr){0x00, 0x90, 0x4C, 0x4D, 0x41, 0x34}, sizeof(mac_addr)) == 0); + fail_unless(t->radios[0].cap.ap_str_records[0].freq_separation == 0); + fail_unless(memcmp(t->radios[0].cap.ap_str_records[1].ruid, (mac_addr){0x00, 0x90, 0x4C, 0x4C, 0x84, 0x72}, sizeof(mac_addr)) == 0); + fail_unless(t->radios[0].cap.ap_str_records[1].freq_separation == 0); + + fail_unless(t->radios[0].cap.ap_nstr_records_nr == 0); + fail_unless(t->radios[0].cap.ap_emlsr_records == 0); + fail_unless(t->radios[0].cap.ap_emlmr_records == 0); + + fail_unless(t->radios[0].cap.bsta_str_records_nr == 1); + fail_unless(memcmp(t->radios[0].cap.bsta_str_records[0].ruid, (mac_addr){0x00, 0x90, 0x4C, 0x4C, 0x84, 0x72}, sizeof(mac_addr)) == 0); + fail_unless(t->radios[0].cap.bsta_str_records[0].freq_separation == 0); + + fail_unless(t->radios[0].cap.bsta_nstr_records_nr == 0); + fail_unless(t->radios[0].cap.bsta_emlsr_records == 0); + fail_unless(t->radios[0].cap.bsta_emlmr_records == 0); + + fail_unless(memcmp(t->radios[1].ruid, (mac_addr){0x00, 0x90, 0x4C, 0x4D, 0x41, 0x34}, sizeof(mac_addr)) == 0); + + fail_unless(memcmp(t->radios[2].ruid, (mac_addr){0x00, 0x90, 0x4C, 0x4C, 0x84, 0x72}, sizeof(mac_addr)) == 0); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_E0_agent_ap_mld_configuration) +{ + VARS + READ_PARSE("tlv_E0_agent_ap_mld_configuration.pcap") + + map_agent_ap_mld_conf_tlv_t *t = (map_agent_ap_mld_conf_tlv_t *)p_tlv; + + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AGENT_AP_MLD_CONFIGURATION); + fail_unless(t->ap_mld_nr == 2); + fail_unless(t->ap_mlds[0].ap_mld_mac_valid == 1); + fail_unless(t->ap_mlds[0].ssid_len == 13); + fail_unless(!memcmp(t->ap_mlds[0].ssid, (char *)"FrontHaulSSID", 13)); + fail_unless(!memcmp(t->ap_mlds[0].ap_mld_mac, (mac_addr){0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, sizeof(mac_addr))); + fail_unless(t->ap_mlds[0].str == 1); + fail_unless(t->ap_mlds[0].emlmr == 0); + fail_unless(t->ap_mlds[0].aff_ap_nr == 3); + fail_unless(!memcmp(t->ap_mlds[0].aff_aps[0].radio_id, (mac_addr){0x00, 0x90, 0x4c, 0x4c, 0x84, 0x7a}, sizeof(mac_addr))); + fail_unless(!memcmp(t->ap_mlds[0].aff_aps[0].aff_ap_mac, (mac_addr){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, sizeof(mac_addr))); + fail_unless(t->ap_mlds[0].aff_aps[0].link_id == 2); + fail_unless(!memcmp(t->ap_mlds[0].aff_aps[2].radio_id, (mac_addr){0x00, 0x90, 0x4c, 0x4c, 0x94, 0x1f}, sizeof(mac_addr))); + fail_unless(!memcmp(t->ap_mlds[0].aff_aps[2].aff_ap_mac, (mac_addr){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, sizeof(mac_addr))); + fail_unless(t->ap_mlds[0].aff_aps[2].link_id == 4); + fail_unless(t->ap_mlds[1].ssid_len == 12); + fail_unless(!memcmp(t->ap_mlds[1].ssid, (char *)"BackHaulSSID", 12)); + fail_unless(t->ap_mlds[1].aff_ap_nr == 2); + fail_unless(t->ap_mlds[1].aff_aps[1].link_id == 6); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_E1_backhaul_sta_mld_configuration) +{ + VARS + READ_PARSE("tlv_E1_backhaul_sta_mld_configuration.pcap") + + map_bsta_mld_conf_tlv_t *t = (map_bsta_mld_conf_tlv_t *)p_tlv; + + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_BACKHAUL_STA_MLD_CONFIGURATION); + fail_unless(t->bsta_mld_mac_valid == 1); + fail_unless(t->ap_mld_mac_valid == 0); + fail_unless(!memcmp(t->bsta_mld_mac, (mac_addr){0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, sizeof(mac_addr))); + fail_unless(!memcmp(t->ap_mld_mac, (mac_addr){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, sizeof(mac_addr))); + fail_unless(t->str == 1); + fail_unless(t->emlmr == 0); + fail_unless(t->aff_bsta_nr == 2); + fail_unless(t->aff_bstas[0].aff_bsta_mac_valid == 1); + fail_unless(!memcmp(t->aff_bstas[0].radio_id, (mac_addr){0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0x03}, sizeof(mac_addr))); + fail_unless(!memcmp(t->aff_bstas[0].aff_bsta_mac, (mac_addr){0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, sizeof(mac_addr))); + fail_unless(t->aff_bstas[1].aff_bsta_mac_valid == 0); + fail_unless(!memcmp(t->aff_bstas[1].radio_id, (mac_addr){0x02, 0xea, 0xbb, 0xcc, 0xdd, 0x04}, sizeof(mac_addr))); + fail_unless(!memcmp(t->aff_bstas[1].aff_bsta_mac, (mac_addr){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_E2_associated_sta_mld_configuration) +{ + VARS + READ_PARSE("tlv_E2_associated_sta_mld_configuration.pcap") + + map_assoc_sta_mld_conf_tlv_t *t = (map_assoc_sta_mld_conf_tlv_t *)p_tlv; + + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_ASSOCIATED_STA_MLD_CONFIGURATION); + fail_unless(!memcmp(t->sta_mld_mac, (mac_addr){0xe4, 0x60, 0x17, 0x5e, 0x34, 0x4f}, sizeof(mac_addr))); + fail_unless(!memcmp(t->ap_mld_mac, (mac_addr){0x00, 0x90, 0x4c, 0x4c, 0x84, 0x7b}, sizeof(mac_addr))); + fail_unless(t->str == 1); + fail_unless(t->emlmr == 0); + fail_unless(t->aff_sta_nr == 3); + fail_unless(!memcmp(t->aff_stas[0].bssid, (mac_addr){0x7a, 0x90, 0x4c, 0x4c, 0x94, 0x18}, sizeof(mac_addr))); + fail_unless(!memcmp(t->aff_stas[0].aff_sta_mac, (mac_addr){0xa6, 0x6b, 0xa2, 0xa5, 0xcd, 0x9c}, sizeof(mac_addr))); + fail_unless(!memcmp(t->aff_stas[2].bssid, (mac_addr){0x00, 0x90, 0x4c, 0x4c, 0x84, 0x7b}, sizeof(mac_addr))); + fail_unless(!memcmp(t->aff_stas[2].aff_sta_mac, (mac_addr){0xe4, 0x60, 0x17, 0x5e, 0x34, 0x4f}, sizeof(mac_addr))); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_E4_affiliated_sta_metrics) +{ + VARS + READ_PARSE("tlv_E4_affiliated_sta_metrics.pcap") + + map_aff_sta_metrics_tlv_t *t = (map_aff_sta_metrics_tlv_t *)p_tlv; + + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AFFILIATED_STA_METRICS); + fail_unless(!memcmp(t->sta_mac, (mac_addr){0xe4, 0x60, 0x17, 0x5e, 0x34, 0x4f}, sizeof(mac_addr))); + fail_unless(t->tx_bytes == 257); + fail_unless(t->rx_bytes == 258); + fail_unless(t->tx_packets == 259); + fail_unless(t->rx_packets == 260); + fail_unless(t->tx_packet_errors == 261); + fail_unless(t->reserved_len == 998); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_E5_affiliated_ap_metrics) +{ + VARS + READ_PARSE("tlv_E5_affiliated_ap_metrics.pcap") + + map_aff_ap_metrics_tlv_t *t = (map_aff_ap_metrics_tlv_t *)p_tlv; + + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AFFILIATED_AP_METRICS); + fail_unless(!memcmp(t->bssid, (mac_addr){0xe4, 0x60, 0x17, 0x5e, 0x34, 0x4f}, sizeof(mac_addr))); + fail_unless(t->tx_packets == 257); + fail_unless(t->rx_packets == 258); + fail_unless(t->tx_packet_errors == 259); + fail_unless(t->tx_ucast_bytes == 260); + fail_unless(t->rx_ucast_bytes == 261); + fail_unless(t->tx_mcast_bytes == 262); + fail_unless(t->rx_mcast_bytes == 263); + fail_unless(t->tx_bcast_bytes == 264); + fail_unless(t->rx_bcast_bytes == 265); + fail_unless(t->reserved_len == 988); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_E8_available_spectrum_inquiry_request) +{ + + uint8_t *p_tlv; + READ_PARSE_FRAGMENTED("tlv_E8_available_spectrum_inquiry_request.pcap") + + map_available_spec_inq_req_tlv_t *t = (map_available_spec_inq_req_tlv_t *)p_tlv; + + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_REQUEST); + fail_unless(t->req_len == 1486); + fail_unless(!memcmp(t->req, (char *)"{ \"version\": \"1.4\", \"availableSpectrumInquiryRequests\"", 54)); + + free_1905_TLV_structure(p_tlv); +} +END_TEST + +START_TEST(test_E9_available_spectrum_inquiry_response) +{ + + uint8_t *p_tlv; + READ_PARSE_FRAGMENTED("tlv_E9_available_spectrum_inquiry_response.pcap") + + map_available_spec_inq_resp_tlv_t *t = (map_available_spec_inq_resp_tlv_t *)p_tlv; + + CHECK_TLV_STRUCT_SIZE + + fail_unless(t->tlv_type == TLV_TYPE_AVAILABLE_SPECTRUM_INQUIRY_RESPONSE); + fail_unless(t->resp_len == 3924); + fail_unless(!memcmp(t->resp, (char *)"{ \"availableSpectrumInquiryResponses\"", 37)); + + free_1905_TLV_structure(p_tlv); +} +END_TEST + +/*####################################################################### +# VARIOUS # +########################################################################*/ +START_TEST(test_FE_unknown) +{ + VARS + READ_PARSE("tlv_FE_unknown.pcap") + + i1905_unknown_tlv_t *t = (i1905_unknown_tlv_t *)p_tlv; + fail_unless(t->tlv_type == TLV_TYPE_UNKNOWN); + fail_unless(t->real_tlv_type == 0xFE); + fail_unless(t->v_nr == 16); + fail_unless(!memcmp(t->v, (uint8_t[]){0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 16)); + + FORGE + CLEANUP +} +END_TEST + +START_TEST(test_too_short) +{ + uint8_t tlv[] = {0x1, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; /* al mac address type */ + uint8_t *p_tlv; + size_t i; + + fail_unless(!(parse_1905_TLV_from_packet(NULL, 10))); + /* - 0, 1, 2: too short tlv header + - 3, 4, 5, 6, 7, 8: too short payload + - 9: ok + */ + for (i = 0; i < sizeof(tlv); i++) { + fail_unless(!(parse_1905_TLV_from_packet(tlv, i))); + } + + fail_unless(!!(p_tlv = parse_1905_TLV_from_packet(tlv, i))); + + free_1905_TLV_structure(p_tlv); +} +END_TEST + +const char *test_suite_name = "tlvs"; +test_case_t test_cases[] = { + /* 1905 */ + TEST("00_end_of_message", test_00_end_of_message ), + TEST("01_al_mac_address", test_01_al_mac_address ), + TEST("02_mac_address", test_02_mac_address ), + TEST("03_device_information", test_03_device_information ), + TEST("04_device_bridging_capability", test_04_device_bridging_capability ), + TEST("06_non_1905_neighbor_device_list", test_06_non_1905_neighbor_device_list ), + TEST("07_neighbor_device_list", test_07_neighbor_device_list ), + TEST("08_link_metric_query", test_08_link_metric_query ), + TEST("09_transmitter_link_metric", test_09_transmitter_link_metric ), + TEST("0A_receiver_link_metric", test_0A_receiver_link_metric ), + TEST("0B_vendor_specific", test_0B_vendor_specific ), + TEST("0C_link_metric_result_code", test_0C_link_metric_result_code ), + TEST("0D_searched_role", test_0D_searched_role ), + TEST("0E_autoconfig_freq_band", test_0E_autoconfig_freq_band ), + TEST("0F_supported_role", test_0F_supported_role ), + TEST("10_supported_freq_band", test_10_supported_freq_band ), + TEST("11_wsc", test_11_wsc ), + TEST("12_push_button_event_notification", test_12_push_button_event_notification ), + TEST("13_push_button_join_notification", test_13_push_button_join_notification ), + TEST("14_generic_phy_device_information", test_14_generic_phy_device_information ), + TEST("1A_1905_profile_version", test_1A_1905_profile_version ), + TEST("15_device_identification", test_15_device_identification ), + TEST("16_control_url", test_16_control_url ), + TEST("17_ipv4", test_17_ipv4 ), + TEST("18_ipv6 ", test_18_ipv6 ), + TEST("19_generic_phy_event_notification", test_19_generic_phy_event_notification ), + TEST("1B_power_off_interface", test_1B_power_off_interface ), + TEST("1C_interface_power_change_information", test_1C_interface_power_change_information ), + TEST("1D_interface_power_change_status", test_1D_interface_power_change_status ), + TEST("1E_l2_neighbor_device", test_1E_l2_neighbor_device ), + + /* MAP R1 */ + TEST("80_supported_service", test_80_supported_service ), + TEST("81_searched_service", test_81_searched_service ), + TEST("82_ap_radio_identifier", test_82_ap_radio_identifier ), + TEST("83_ap_operational_bss", test_83_ap_operational_bss ), + TEST("84_associated_clients", test_84_associated_clients ), + TEST("85_ap_radio_basic_capabilities", test_85_ap_radio_basic_capabilities ), + TEST("86_ap_ht_capabilities", test_86_ap_ht_capabilities ), + TEST("87_ap_vht_capabilities", test_87_ap_vht_capabilities ), + TEST("88_ap_he_capabilities", test_88_ap_he_capabilities ), + TEST("89_steering_policy", test_89_steering_policy ), + TEST("89_steering_policy_2", test_89_steering_policy_2 ), + TEST("8A_metric_reporting_policy", test_8A_metric_reporting_policy ), + TEST("8B_channel_preference", test_8B_channel_preference ), + TEST("8C_radio_operation_restriction", test_8C_radio_operation_restriction ), + TEST("8D_transmit_power_limit", test_8D_transmit_power_limit ), + TEST("8E_channel_selection_response", test_8E_channel_selection_response ), + TEST("8F_operating_channel_report ", test_8F_operating_channel_report ), + TEST("90_client_info", test_90_client_info ), + TEST("91_client_capability_report", test_91_client_capability_report ), + TEST("92_client_association_event", test_92_client_association_event ), + TEST("93_ap_metric_query", test_93_ap_metric_query ), + TEST("94_ap_metrics", test_94_ap_metrics ), + TEST("95_sta_mac_address", test_95_sta_mac_address ), + TEST("96_associated_sta_link_metrics", test_96_associated_sta_link_metrics ), + TEST("97_unassociated_sta_link_metrics_query", test_97_unassociated_sta_link_metrics_query ), + TEST("98_unassociated_sta_link_metrics_response", test_98_unassociated_sta_link_metrics_response ), + TEST("99_beacon_metrics_query", test_99_beacon_metrics_query ), + TEST("9A_beacon_metrics_response", test_9A_beacon_metrics_response ), + TEST("9B_steering_request", test_9B_steering_request ), + TEST("9C_steering_btm_report", test_9C_steering_btm_report ), + TEST("9D_client_association_control_request", test_9D_client_association_control_request ), + TEST("9E_backhaul_steering_request", test_9E_backhaul_steering_request ), + TEST("9F_backhaul_steering_response", test_9F_backhaul_steering_response ), + TEST("A0_higher_layer_data", test_A0_higher_layer_data ), + TEST("A1_ap_capability", test_A1_ap_capability ), + TEST("A2_associated_sta_traffic_stats", test_A2_associated_sta_traffic_stats ), + TEST("A3_error_code", test_A3_error_code ), + + /* MAP R2 */ + TEST("A4_channel_scan_reporting_policy", test_A4_channel_scan_reporting_policy ), + TEST("A5_channel_scan_capabilities", test_A5_channel_scan_capabilities ), + TEST("A6_channel_scan_request", test_A6_channel_scan_request ), + TEST("A6_channel_scan_request_2", test_A6_channel_scan_request_2 ), + TEST("A7_channel_scan_result", test_A7_channel_scan_result ), + TEST("A7_channel_scan_result_malformed", test_A7_channel_scan_result_malformed ), + TEST("A8_timestamp", test_A8_timestamp ), + TEST("AD_cac_request", test_AD_cac_request ), + TEST("AE_cac_termination", test_AE_cac_termination ), + TEST("AF_cac_completion_report", test_AF_cac_completion_report ), + TEST("B1_cac_status_report", test_B1_cac_status_report ), + TEST("B2_cac_capabilities", test_B2_cac_capabilities ), + TEST("B3_multiap_profile", test_B3_multiap_profile ), + TEST("B4_profile2_ap_capability", test_B4_profile2_ap_capability ), + TEST("B5_default_8021q_settings", test_B5_default_8021q_settings ), + TEST("B6_traffic_separation_policy", test_B6_traffic_separation_policy ), + TEST("BC_profile2_error_code", test_BC_profile2_error_code ), + TEST("BE_ap_radio_advanced_capabilities", test_BE_ap_radio_advanced_capabilities ), + TEST("BF_association_status_notification", test_BF_association_status_notification ), + TEST("C0_source_info", test_C0_source_info ), + TEST("C1_tunneled_message_type", test_C1_tunneled_message_type ), + TEST("C2_tunneled", test_C2_tunneled ), + TEST("C3_profile2_steering_request", test_C3_profile2_steering_request ), + TEST("C4_unsuccessful_association_policy", test_C4_unsuccessful_association_policy ), + TEST("C5_metric_collection_interval", test_C5_metric_collection_interval ), + TEST("C6_radio_metrics", test_C6_radio_metrics ), + TEST("C7_ap_extended_metrics", test_C7_ap_extended_metrics ), + TEST("C8_associated_sta_extended_link_metrics", test_C8_associated_sta_extended_link_metrics ), + TEST("C9_status_code", test_C9_status_code ), + TEST("CA_reason_code", test_CA_reason_code ), + TEST("CB_backhaul_sta_radio_capabilities", test_CB_backhaul_sta_radio_capabilities ), + TEST("D0_backhaul_bss_configuration", test_D0_backhaul_bss_configuration ), + + /* MAP R3 */ + TEST("A9_1905_security_capability", test_A9_1905_security_capability ), + TEST("AA_ap_wifi6_capabilities", test_AA_ap_wifi6_capabilities ), + TEST("AB_mic", test_AB_mic ), + TEST("AC_encrypted_payload", test_AC_encrypted_payload ), + TEST("B0_associated_wifi6_sta_status", test_B0_associated_wifi6_sta_status ), + TEST("B7_bss_configuration_report", test_B7_bss_configuration_report ), + TEST("B8_bssid", test_B8_bssid ), + TEST("CC_akm_suite_capabilities", test_CC_akm_suite_capabilities ), + TEST("CE_1905_encap_eapol", test_CE_1905_encap_eapol ), + TEST("D1_dpp_message", test_D1_dpp_message ), + TEST("D2_dpp_cce_indication", test_D2_dpp_cce_indication ), + TEST("CD_1905_encap_dpp", test_CD_1905_encap_dpp ), + TEST("CD_1905_encap_dpp_no_enrollee_mac", test_CD_1905_encap_dpp_no_enrollee_mac ), + TEST("D3_dpp_chirp", test_D3_dpp_chirp_value ), + TEST("D3_dpp_chirp_value_no_enrollee_mac", test_D3_dpp_chirp_value_no_enrollee_mac ), + TEST("D4_device_inventory", test_D4_device_inventory ), + TEST("D5_agent_list", test_D5_agent_list ), + + /* MAP R4 */ + TEST("DD_controller_capability", test_DD_controller_capability ), + + /* MAP R6 */ + TEST("DF_wifi7_agent_capabilities", test_DF_wifi7_agent_capabilities ), + TEST("E0_agent_ap_mld_configuration", test_E0_agent_ap_mld_configuration ), + TEST("E1_backhaul_sta_mld_configuration", test_E1_backhaul_sta_mld_configuration ), + TEST("E2_associated_sta_mld_configuration", test_E2_associated_sta_mld_configuration ), + TEST("E4_affiliated_sta_metrics", test_E4_affiliated_sta_metrics ), + TEST("E5_affiliated_ap_metrics", test_E5_affiliated_ap_metrics ), + /* TODO Implement when the tlv implemented in the agent side */ + /* TEST("E7_eht_operations", test_E7_eht_operations ), */ + TEST("E8_available_spectrum_inquiry_request", test_E8_available_spectrum_inquiry_request ), + TEST("E9_available_spectrum_inquiry_response", test_E9_available_spectrum_inquiry_response ), + + /* Various */ + TEST("FE_unknown", test_FE_unknown ), + TEST("too_short", test_too_short ), + TEST_CASES_END +}; diff --git a/source/test/libutils/CMakeLists.txt b/source/test/libutils/CMakeLists.txt new file mode 100644 index 0000000..f38d2f4 --- /dev/null +++ b/source/test/libutils/CMakeLists.txt @@ -0,0 +1,56 @@ +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +set(SRC_DIR ${LIBUTILS_SRC_DIR}) + +include(../common/CheckUnitTest.cmake) + +SETUP_UNIT_TEST(map_datamodel + ${SRC_DIR}/map_data_model.c + ${SRC_DIR}/map_data_model_dumper.c + ${SRC_DIR}/map_channel_set.c + ${SRC_DIR}/map_info.c + ${SRC_DIR}/arraylist.c + ${SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(map_dm_eth_device_list + ${SRC_DIR}/map_dm_eth_device_list.c + ${SRC_DIR}/map_data_model.c + ${SRC_DIR}/arraylist.c + ${SRC_DIR}/kwaytree.c + ${SRC_DIR}/map_channel_set.c + ${SRC_DIR}/map_info.c + ${SRC_DIR}/map_topology_tree.c + ${SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(map_info + ${SRC_DIR}/map_info.c + ${SRC_DIR}/map_channel_set.c +) + +SETUP_UNIT_TEST(map_channel_set + ${SRC_DIR}/map_channel_set.c +) + +SETUP_UNIT_TEST(map_80211 + ${SRC_DIR}/map_80211.c + ${SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(map_cli_subscription + ${SRC_DIR}/map_cli_subscription.c +) + +SETUP_UNIT_TEST(map_blocklist + ${SRC_DIR}/map_blocklist.c + ${SRC_DIR}/acu_utils.c +) + +SETUP_UNIT_TEST(arraylist + ${SRC_DIR}/arraylist.c +) diff --git a/source/test/libutils/stub/stub_map_blocklist.c b/source/test/libutils/stub/stub_map_blocklist.c new file mode 100644 index 0000000..289ff48 --- /dev/null +++ b/source/test/libutils/stub/stub_map_blocklist.c @@ -0,0 +1,20 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2022-2022 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#include "map_blocklist.h" + +void map_blocklist_dump(map_printf_cb_t print_cb) +{ + print_cb("dump blocklist\n"); +} diff --git a/source/test/libutils/stub/stub_map_cli.c b/source/test/libutils/stub/stub_map_cli.c new file mode 100644 index 0000000..3b600d2 --- /dev/null +++ b/source/test/libutils/stub/stub_map_cli.c @@ -0,0 +1,101 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2021-2021 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#include + +#include "map_cli.h" +#include "map_utils.h" +#include "map_cli_subscription.h" + +#include "stub_map_cli.h" + +struct cli_s { + subscriptions_t *subscriptions; +}; + +static cli_t g_cli; +static char **g_print_buf; + +int cli_fd(cli_t *cli) +{ + return 123; +} + +void cli_vprintf(cli_t *cli, const char *fmt, va_list args) +{ + /* Concatenate print results until cb is (un)registered */ + if (g_print_buf) { + char *p = NULL, *q = NULL; + + vasprintf(&p, fmt, args); + + asprintf(&q, "%s%s", *g_print_buf ? *g_print_buf : "", p); + free(p); + free(*g_print_buf); + + *g_print_buf = q; + } +} + +void cli_printf(cli_t *cli, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + cli_vprintf(cli, fmt, args); + va_end(args); +} + +void cli_run(cli_t *cli) +{ +} + +cli_t *cli_create(cli_options_t *options) +{ + if (!(g_cli.subscriptions = subscriptions_create())) { + return NULL; + } + + return &g_cli; +} + +void cli_destroy(cli_t *cli) +{ + subscriptions_destroy(cli->subscriptions); + cli->subscriptions = NULL; +} + +int cli_subscribe(cli_t *cli, const char *event, cli_function_t function, uint32_t flags, void *context) +{ + return subscriptions_add(cli->subscriptions, event, function, flags, context); +} + +void stub_map_cli_set_print_buf(char **buf) +{ + g_print_buf = buf; +} + +int stub_map_cli_exec(char *cmd, char *payload) +{ + subscription_t *subscription = subscriptions_get(g_cli.subscriptions, cmd); + + if (subscription) { + log_test_d("found subscription for: '%s'", cmd); + subscription_function(subscription)(cmd, payload, subscription_context(subscription)); + + return 0; + } + + log_test_e("could not find subscription for: '%s'", cmd); + return -1; +} diff --git a/source/test/libutils/stub/stub_map_cli.h b/source/test/libutils/stub/stub_map_cli.h new file mode 100644 index 0000000..520615f --- /dev/null +++ b/source/test/libutils/stub/stub_map_cli.h @@ -0,0 +1,24 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2021-2021 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#ifndef STUB_MAP_CLI_H_ +#define STUB_MAP_CLI_H_ + +typedef void (*stub_map_cli_print_cb_t)(const char *buf); + +void stub_map_cli_set_print_buf(char **buf); + +int stub_map_cli_exec(char *cmd, char *payload); + +#endif /* STUB_MAP_CLI_H_ */ diff --git a/source/test/libutils/stub/stub_map_config.c b/source/test/libutils/stub/stub_map_config.c new file mode 100644 index 0000000..4193c40 --- /dev/null +++ b/source/test/libutils/stub/stub_map_config.c @@ -0,0 +1,43 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2020-2020 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#include +#include +#include + +#include "map_config.h" + +static map_cfg_t g_cfg; + +map_cfg_t *map_cfg_get(void) +{ + return &g_cfg; +} + +map_controller_cfg_t *map_controller_cfg_get(void) +{ + return &g_cfg.controller_cfg; +} + +int map_cfg_set_master_state(bool master) +{ + map_cfg_get()->is_master = master; + + return 0; +} + +void map_profile_clone(map_profile_cfg_t *dst, map_profile_cfg_t *src) +{ + *dst = *src; +} diff --git a/source/test/libutils/stub/stub_map_config.h b/source/test/libutils/stub/stub_map_config.h new file mode 100644 index 0000000..18f340c --- /dev/null +++ b/source/test/libutils/stub/stub_map_config.h @@ -0,0 +1,18 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2021-2021 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#ifndef STUB_MAP_CONFIG_H_ +#define STUB_MAP_CONFIG_H_ + +#endif /* STUB_MAP_CONFIG_H_ */ diff --git a/source/test/libutils/stub/stub_map_data_model.c b/source/test/libutils/stub/stub_map_data_model.c new file mode 100644 index 0000000..a1ee40f --- /dev/null +++ b/source/test/libutils/stub/stub_map_data_model.c @@ -0,0 +1,23 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2022-2022 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#include "map_data_model.h" + +void map_dm_register_cbs(map_dm_cbs_t *cbs) +{ +} + +void map_dm_unregister_cbs(map_dm_cbs_t *cbs) +{ +} diff --git a/source/test/libutils/stub/stub_map_dm_eth_device_list.c b/source/test/libutils/stub/stub_map_dm_eth_device_list.c new file mode 100644 index 0000000..0d9f6d1 --- /dev/null +++ b/source/test/libutils/stub/stub_map_dm_eth_device_list.c @@ -0,0 +1,43 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2022-2022 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#include "map_dm_eth_device_list.h" + +#include "test.h" + +int map_dm_eth_device_list_schedule_update(void) +{ + map_ale_info_t *ale; + mac_addr macs[2] = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, {0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}}; + + /* Add some dummy macs */ + map_dm_foreach_agent_ale(ale) { + if (!ale->eth_device_list.macs) { + fail_unless(!!(ale->eth_device_list.macs = malloc(2 * sizeof(mac_addr)))); + memcpy(ale->eth_device_list.macs, macs, sizeof(macs)); + ale->eth_device_list.macs_nr = 2; + } + } + + return 0; +} + +int map_dm_eth_device_list_init(map_dm_eth_device_list_update_cb_t update_cb) +{ + return 0; +} + +void map_dm_eth_device_list_fini(void) +{ +} diff --git a/source/test/libutils/stub/stub_map_dm_rbus.c b/source/test/libutils/stub/stub_map_dm_rbus.c new file mode 100644 index 0000000..68ef8f1 --- /dev/null +++ b/source/test/libutils/stub/stub_map_dm_rbus.c @@ -0,0 +1,25 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2021-2021 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#include "map_dm_rbus.h" + + +int map_dm_rbus_init(void) +{ + return 0; +} + +void map_dm_rbus_fini(void) +{ +} diff --git a/source/test/libutils/stub/stub_map_retry_handler.c b/source/test/libutils/stub/stub_map_retry_handler.c new file mode 100644 index 0000000..d11a65e --- /dev/null +++ b/source/test/libutils/stub/stub_map_retry_handler.c @@ -0,0 +1,61 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2020-2020 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#include +#include +#include + +#include "map_retry_handler.h" + +int map_register_retry(const char* retry_id, + uint8_t retry_intervel, + uint8_t max_retry_count, + void *args, + map_compl_cb_t compl_cb, + map_retry_cb_t retry_cb) +{ + uint16_t mid; + + /* Call once... */ + if (retry_cb) { + retry_cb(args, &mid); + } + + /* ...and completed */ + if (compl_cb != NULL) { + compl_cb(MAP_RETRY_STATUS_SUCCESS, args, NULL); + } + return 0; +} + +int map_update_retry_handler(uint16_t mid, void *compl_user_data) +{ + return 0; +} + +int map_cleanup_retry_args(int status, void *args, void *compl_user_data) +{ + free(args); + return 0; +} + +int map_unregister_retry(const char* retry_id) +{ + return 0; +} + +int map_unregister_retry_prefix(const char* retry_id_prefix) +{ + return 0; +} diff --git a/source/test/libutils/stub/stub_map_timer_handler.c b/source/test/libutils/stub/stub_map_timer_handler.c new file mode 100644 index 0000000..7cdb76f --- /dev/null +++ b/source/test/libutils/stub/stub_map_timer_handler.c @@ -0,0 +1,64 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2020-2020 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#include +#include +#include + +#include "map_timer_handler.h" + +bool map_is_timer_registered(const char *timer_id) +{ + return 0; +} + +int map_timer_register_callback(uint32_t frequency_sec, + const char *timer_id, + void *args, + timer_cb_t cb) +{ + /* Call immediatly */ + if (cb) { + cb((char*)timer_id, args); + } + + return 0; +} + +int map_timer_unregister_callback(const char *timer_id) +{ + return 0; +} + +int map_timer_unregister_callback_prefix(const char *timer_id_prefix) +{ + return 0; +} + +int map_timer_restart_callback(const char* timer_id) +{ + return 0; +} + +int map_timer_change_callback(const char *timer_id, uint32_t frequency_sec, void *args) +{ + return 0; +} + +int map_timer_remaining(const char *timer_id, uint32_t *remaining_sec) +{ + *remaining_sec = 0; + + return 0; +} diff --git a/source/test/libutils/stub/stub_map_topology_tree.c b/source/test/libutils/stub/stub_map_topology_tree.c new file mode 100644 index 0000000..f655124 --- /dev/null +++ b/source/test/libutils/stub/stub_map_topology_tree.c @@ -0,0 +1,52 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2020-2020 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#include +#include +#include + +#include "map_topology_tree.h" + +int8_t create_topology_tree_node(map_ale_info_t *ale_node, tree_node_type_t type) +{ + return 0; +} + +void remove_topology_tree_node(map_ale_info_t *ale_node) +{ +} + +map_ale_info_t* get_root_ale_node() +{ + return NULL; +} + +int8_t is_parent_of(map_ale_info_t *parent, map_ale_info_t *child) +{ + return 0; +} + +void dump_topology_tree(map_printf_cb_t print_cb) +{ +} + +map_ale_info_t* get_parent_ale_node(map_ale_info_t* child_ale) +{ + return NULL; +} + +map_ale_info_t* fetch_and_free_child_iter(list_iterator_t* iter) +{ + return NULL; +} \ No newline at end of file diff --git a/source/test/libutils/stub/stub_map_utils.c b/source/test/libutils/stub/stub_map_utils.c new file mode 100644 index 0000000..bcd23b5 --- /dev/null +++ b/source/test/libutils/stub/stub_map_utils.c @@ -0,0 +1,91 @@ +/********** COPYRIGHT AND CONFIDENTIALITY INFORMATION NOTICE ************* +** Copyright (c) 2020-2020 - AirTies Wireless Networks ** +** - All Rights Reserved ** +** AirTies hereby informs you that certain portions ** +** of this software module and/or Work are owned by AirTies ** +** and/or its software providers. ** +** Distribution copying and modification of all such work are reserved ** +** to AirTies and/or its affiliates, and are not permitted without ** +** express written authorization from AirTies. ** +** AirTies is registered trademark and trade name of AirTies, ** +** and shall not be used in any manner without express written ** +** authorization from AirTies ** +*************************************************************************/ + +#include +#include +#include + +#include "map_utils.h" + +mac_addr g_zero_mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +mac_addr g_wildcard_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +void map_vlog(int module, int level, const char *format, va_list args) +{ + int log_level = 0; + char *p = getenv("UNITTEST_LOG_LEVEL"); + + if (p) { + char *strs[] = {"emerge", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace"}; + int levels[] = {LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG, LOG_TRACE}; + int i; + + for (i = 0; i < ARRAY_SIZE(strs); i++) { + if (strcasestr(p, strs[i])) { + log_level = levels[i]; + break; + } + } + } + + if (level <= log_level) { + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + } +} + +struct timespec get_current_time() +{ + struct timespec boottime = {0}; + + return boottime; +} + +uint64_t get_clock_diff_secs(struct timespec new_time, struct timespec old_time) +{ + return 0; +} + +void map_log(int module, int level, const char *format, ...) +{ + va_list args; + va_start(args, format); + map_vlog(module, level, format, args); + va_end(args); +} + +bool map_is_loopback_iface(const char *ifname) +{ + return !strcmp(ifname, "lo"); +} + +bool map_is_auth_mode_enterprise_supported(uint16_t auth_modes) +{ + /* All enterprise security modes will advertise auth mode as WPA2-enterprise + * since there (is/will be) no enterprise security mode for WPA3 defined in WSC standard. + * We will discriminate enterprise modes using airties attributes. + */ + return (auth_modes & 0x0010/* IEEE80211_AUTH_MODE_WPA2 */); +} + +uint8_t map_count_bits_16(uint16_t n) +{ + uint8_t c = 0; + + for (; n; ++c) { + n &= n - 1; + } + + return c; +} diff --git a/source/test/libutils/test_arraylist.c b/source/test/libutils/test_arraylist.c new file mode 100644 index 0000000..f56e612 --- /dev/null +++ b/source/test/libutils/test_arraylist.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2019-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "test.h" +#include "arraylist.h" + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static int test_equal(void* obj, void* object_to_find) +{ + return obj == object_to_find; +} + +static int test_before(void* obj, void* object_to_find) +{ + int *a = obj; + int *b = object_to_find; + + return *a > *b; +} + +/*####################################################################### +# TEST_ARRAYLIST # +########################################################################*/ +/* TODO: test all functions */ +START_TEST(test_arraylist) +{ + array_list_t *list; + list_iterator_t *iterator; + int a = 1; + int b = 2; + int c = 3; + int *p; + + fail_unless(!!(list = new_array_list())); + + /* Add objects in front */ + fail_unless(!push_object(list, &a)); + fail_unless(!push_object(list, &b)); + fail_unless(!!push_object_ex(list, &c)); + + /* Check content */ + fail_unless(3 == list_get_size(list)); + fail_unless((p = object_at_index(list, 0)) && *p == 3); + fail_unless((p = object_at_index(list, 1)) && *p == 2); + fail_unless((p = object_at_index(list, 2)) && *p == 1); + fail_unless(!(p = object_at_index(list, 3))); + fail_unless((p = first_object(list)) && *p == 3); + fail_unless((p = last_object(list)) && *p == 1); + fail_unless((p = find_object(list, &c, test_equal)) && *p == 3); + + + /* Iterator */ + fail_unless(!!(iterator = new_list_iterator(list))); + fail_unless((p = get_next_list_object(iterator)) && *p == 3); + fail_unless((p = get_next_list_object(iterator)) && *p == 2); + fail_unless((p = get_next_list_object(iterator)) && *p == 1); + fail_unless(!(p = get_next_list_object(iterator))); + free_list_iterator(iterator); + + /* Remove */ + fail_unless((p = remove_object_at_index(list, 1)) && *p == 2); + fail_unless(2 == list_get_size(list)); + fail_unless((p = object_at_index(list, 0)) && *p == 3); + fail_unless((p = object_at_index(list, 1)) && *p == 1); + + fail_unless((p = pop_object(list)) && *p == 3); + fail_unless(1 == list_get_size(list)); + fail_unless((p = pop_object(list)) && *p == 1); + fail_unless(0 == list_get_size(list)); + + + /* Add tail */ + fail_unless(!insert_last_object(list, &a)); + fail_unless(!insert_last_object(list, &b)); + fail_unless(!insert_last_object(list, &c)); + + /* Check content */ + fail_unless(3 == list_get_size(list)); + fail_unless((p = object_at_index(list, 0)) && *p == 1); + fail_unless((p = object_at_index(list, 1)) && *p == 2); + fail_unless((p = object_at_index(list, 2)) && *p == 3); + + /* Remove */ + fail_unless((p = remove_last_object(list)) && *p == 3); + fail_unless((p = remove_object_at_index(list, 1)) && *p == 2); + fail_unless((p = remove_last_object(list)) && *p == 1); + + + /* insert_at_index */ + fail_unless(!push_object(list, &c)); + fail_unless(!push_object(list, &a)); + fail_unless(!insert_at_index(list, &b, 1)); + + /* Check and remove */ + fail_unless((p = pop_object(list)) && *p == 1); + fail_unless((p = pop_object(list)) && *p == 2); + fail_unless((p = pop_object(list)) && *p == 3); + + + /* compare_and_insert */ + fail_unless(!push_object(list, &c)); + fail_unless(!push_object(list, &a)); + compare_and_insert(list, &b, test_before); + + /* Check and remove */ + fail_unless((p = pop_object(list)) && *p == 1); + fail_unless((p = pop_object(list)) && *p == 2); + fail_unless((p = pop_object(list)) && *p == 3); + + /* Cleanup */ + delete_array_list(list); +} +END_TEST + +/*####################################################################### +# TEST_ITERATOR_REMOVE # +########################################################################*/ +/* Remove object while iterating */ +START_TEST(test_iterator_remove) +{ + array_list_t *list; + list_iterator_t iterator = {0}; +#define NUM_VALS 5 + int vals[NUM_VALS] = {0, 1, 2, 3, 4}; + int i; + int *p; + + fail_unless(!!(list = new_array_list())); + + /* Add objects */ + for (i = 0; i < NUM_VALS; i++) { + insert_last_object(list, &vals[i]); + } + + bind_list_iterator(&iterator, list); + + /* find all 5 */ + for (i = 0; i < NUM_VALS; i++) { + fail_unless((p = get_next_list_object(&iterator)) && *p == i); + } + + reset_list_iterator(&iterator); + + /* Remove third object and check will still get 4 and 5 */ + for (i = 0; i < NUM_VALS; i++) { + fail_unless((p = get_next_list_object(&iterator)) && *p == i); + if (i == 2) { + remove_object(list, p); + fail_unless(4 == list_get_size(list)); + } + } + + /* Check if correct 4 elements are left */ + for (i = 0; i < NUM_VALS - 1; i++) { + fail_unless((p = get_next_list_object(&iterator)) && *p == (i < 2) ? i : i + 1); + } + + /* Flush list */ + for (i = 0; i < NUM_VALS - 1; i++) { + fail_unless(!!remove_last_object(list)); + } + + /* Cleanup */ + delete_array_list(list); +} +END_TEST + + +const char *test_suite_name = "arraylist"; +test_case_t test_cases[] = { + TEST("arraylist", test_arraylist ), + TEST("iterator_remove", test_iterator_remove ), + TEST_CASES_END +}; diff --git a/source/test/libutils/test_map_80211.c b/source/test/libutils/test_map_80211.c new file mode 100644 index 0000000..9431721 --- /dev/null +++ b/source/test/libutils/test_map_80211.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2019-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "test.h" +#include "map_80211.h" + +/*####################################################################### +# TEST_PARSE_ASSOC_BODY # +########################################################################*/ +/* Valid frame bodies - type is autodetected */ +static char *g_ssid = "frv_test_bh"; + +/* Assoc frame body */ +static char *g_assoc = "11110A00000B6672765F746573745F626801088C129824B048606C2102F918240A24043404640C9504A501301A0100000FAC040100000FAC040100000FAC0280000000000FAC06460532000000003B1D000102030405161718191B1C1D1E737475767778797A7B7C7D7E7F80812D1AEF0117FFFFFFFF000000000000000000000000000000000000007F0900000880000000C001BF0CB1798B0FAAFF0000AAFF0020FF2023030800120000443002C00F438518008C00AAFFAAFF3B1CC7711CC7711CC771DD1E00904C33EF0117FFFFFFFF00000000000000000000000000000000000000DD0500904C0417DD0A00101802000010000000DD070050F202000100DD0A506F9A1B0601800701029704010203049806010203040506"; + +/* Reassoc frame body (header 6 bytes longer) */ +static char *g_reassoc = "11110A01020304050600000B6672765F746573745F626801088C129824B048606C2102F918240A24043404640C9504A501301A0100000FAC040100000FAC040100000FAC0280000000000FAC06460532000000003B1D000102030405161718191B1C1D1E737475767778797A7B7C7D7E7F80812D1AEF0117FFFFFFFF000000000000000000000000000000000000007F0900000880000000C001BF0CB1798B0FAAFF0000AAFF0020FF2023030800120000443002C00F438518008C00AAFFAAFF3B1CC7711CC7711CC771DD1E00904C33EF0117FFFFFFFF00000000000000000000000000000000000000DD0500904C0417DD0A00101802000010000000DD070050F202000100DD0A506F9A1B0601800701029704010203049806010203040506"; + +/* IES only */ +static char *g_ies = "000B6672765F746573745F626801088C129824B048606C2102F918240A24043404640C9504A501301A0100000FAC040100000FAC040100000FAC0280000000000FAC06460532000000003B1D000102030405161718191B1C1D1E737475767778797A7B7C7D7E7F80812D1AEF0117FFFFFFFF000000000000000000000000000000000000007F0900000880000000C001BF0CB1798B0FAAFF0000AAFF0020FF2023030800120000443002C00F438518008C00AAFFAAFF3B1CC7711CC7711CC771DD1E00904C33EF0117FFFFFFFF00000000000000000000000000000000000000DD0500904C0417DD0A00101802000010000000DD070050F202000100DD0A506F9A1B0601800701029704010203049806010203040506"; + +/* Note: an empty EHT CAP IE is added at the end - to be replaced by a captured one */ +static char *g_ies_11be = "000B6672765F746573745F626801088C129824B048606C2102F918240A24043404640C9504A501301A0100000FAC040100000FAC040100000FAC0280000000000FAC06460532000000003B1D000102030405161718191B1C1D1E737475767778797A7B7C7D7E7F80812D1AEF0117FFFFFFFF000000000000000000000000000000000000007F0900000880000000C001BF0CB1798B0FAAFF0000AAFF0020FF2023030800120000443002C00F438518008C00AAFFAAFF3B1CC7711CC7711CC771DD1E00904C33EF0117FFFFFFFF00000000000000000000000000000000000000DD0500904C0417DD0A00101802000010000000DD070050F202000100DD0A506F9A1B0601800701029704010203049806010203040506FF0D6C0B0000000000000000000000"; + + +/* Invalid (removed SSID IE so it is not found + truncated IE at the end) */ +static char *g_invalid_1 = "01088C129824B048606C2102F918240A24043404640C9504A501301A0100000FAC040100000FAC040100000FAC0280000000000FAC06460532000000003B1D000102030405161718191B1C1D1E737475767778797A7B7C7D7E7F80812D1AEF0117FFFFFFFF000000000000000000000000000000000000007F0900000880000000C001BF0CB1798B0FAAFF0000AAFF0020FF2023030800120000443002C00F438518008C00AAFFAAFF3B1CC7711CC7711CC771DD1E00904C33EF0117FFFFFFFF00000000000000000000000000000000000000DD0500904C0417DD0A00101802000010000000DD070050F202000100DD0A506F9A1B06018007010297040102030498060102030405069904010203"; + +static char *g_invalid_2 = "01088C129824B048606C2102F918240A24043404640C9504A501301A0100000FAC040100000FAC040100000FAC0280000000000FAC06460532000000003B1D000102030405161718191B1C1D1E737475767778797A7B7C7D7E7F80812D1AEF0117FFFFFFFF000000000000000000000000000000000000007F0900000880000000C001BF0CB1798B0FAAFF0000AAFF0020FF2023030800120000443002C00F438518008C00AAFFAAFF3B1CC7711CC7711CC771DD1E00904C33EF0117FFFFFFFF00000000000000000000000000000000000000DD0500904C0417DD0A00101802000010000000DD070050F202000100DD0A506F9A1B060180070102970401020304980601020304050699040102"; + +static char *g_invalid_3 = "01088C129824B048606C2102F918240A24043404640C9504A501301A0100000FAC040100000FAC040100000FAC0280000000000FAC06460532000000003B1D000102030405161718191B1C1D1E737475767778797A7B7C7D7E7F80812D1AEF0117FFFFFFFF000000000000000000000000000000000000007F0900000880000000C001BF0CB1798B0FAAFF0000AAFF0020FF2023030800120000443002C00F438518008C00AAFFAAFF3B1CC7711CC7711CC771DD1E00904C33EF0117FFFFFFFF00000000000000000000000000000000000000DD0500904C0417DD0A00101802000010000000DD070050F202000100DD0A506F9A1B0601800701029704010203049806010203040506990401"; + +static char *g_invalid_4 = "01088C129824B048606C2102F918240A24043404640C9504A501301A0100000FAC040100000FAC040100000FAC0280000000000FAC06460532000000003B1D000102030405161718191B1C1D1E737475767778797A7B7C7D7E7F80812D1AEF0117FFFFFFFF000000000000000000000000000000000000007F0900000880000000C001BF0CB1798B0FAAFF0000AAFF0020FF2023030800120000443002C00F438518008C00AAFFAAFF3B1CC7711CC7711CC771DD1E00904C33EF0117FFFFFFFF00000000000000000000000000000000000000DD0500904C0417DD0A00101802000010000000DD070050F202000100DD0A506F9A1B06018007010297040102030498060102030405069904"; + +static char *g_invalid_5 = "01088C129824B048606C2102F918240A24043404640C9504A501301A0100000FAC040100000FAC040100000FAC0280000000000FAC06460532000000003B1D000102030405161718191B1C1D1E737475767778797A7B7C7D7E7F80812D1AEF0117FFFFFFFF000000000000000000000000000000000000007F0900000880000000C001BF0CB1798B0FAAFF0000AAFF0020FF2023030800120000443002C00F438518008C00AAFFAAFF3B1CC7711CC7711CC771DD1E00904C33EF0117FFFFFFFF00000000000000000000000000000000000000DD0500904C0417DD0A00101802000010000000DD070050F202000100DD0A506F9A1B060180070102970401020304980601020304050699"; + + +static int parse_body(map_sta_capability_t *caps, char *body, int freq, char *ssid, mac_addr aff_sta_mac) +{ + uint8_t buf[1024]; + int len = strlen(body) / 2; + + memset(caps, 0, sizeof(map_sta_capability_t)); + + fail_unless(ACU_OK == acu_hex_string_to_buf(body, buf, len)); + + return map_80211_parse_assoc_body(caps, buf, len, freq, (uint8_t*)ssid, strlen(ssid), aff_sta_mac); +} + +static void check_caps(map_sta_capability_t *caps) +{ + fail_unless(caps->max_tx_spatial_streams == 4); + fail_unless(caps->max_rx_spatial_streams == 4); + fail_unless(caps->max_bandwidth == 80); + fail_unless(caps->supported_standard == STD_80211_ANACAX); + fail_unless(caps->sgi_support == 0); + fail_unless(caps->dot11k_support == 1); + fail_unless(caps->dot11k_brp_support == 1); + fail_unless(caps->dot11k_bra_support == 1); + fail_unless(caps->dot11v_btm_support == 1); + fail_unless(caps->backhaul_sta == 1); + fail_unless(caps->mbo_support == 0); + fail_unless(caps->max_phy_rate == 2402000); +} + +static void check_caps_11be(map_sta_capability_t *caps) +{ + fail_unless(caps->max_tx_spatial_streams == 4); + fail_unless(caps->max_rx_spatial_streams == 4); + fail_unless(caps->max_bandwidth == 320); + fail_unless(caps->supported_standard == STD_80211_BE); + fail_unless(caps->sgi_support == 0); + fail_unless(caps->dot11k_support == 1); + fail_unless(caps->dot11k_brp_support == 1); + fail_unless(caps->dot11k_bra_support == 1); + fail_unless(caps->dot11v_btm_support == 1); + fail_unless(caps->backhaul_sta == 1); + fail_unless(caps->mbo_support == 0); + fail_unless(caps->max_phy_rate == 9607800); +} + +START_TEST(test_parse_assoc_body) +{ + map_sta_capability_t caps; + + /* Correct parsing */ + fail_unless(!parse_body(&caps, g_assoc, IEEE80211_FREQUENCY_BAND_5_GHZ, g_ssid, NULL)); + check_caps(&caps); + + fail_unless(!parse_body(&caps, g_reassoc, IEEE80211_FREQUENCY_BAND_5_GHZ, g_ssid, NULL)); + check_caps(&caps); + + fail_unless(!parse_body(&caps, g_ies, IEEE80211_FREQUENCY_BAND_5_GHZ, g_ssid, NULL)); + check_caps(&caps); + + fail_unless(!parse_body(&caps, g_ies_11be, IEEE80211_FREQUENCY_BAND_6_GHZ, g_ssid, NULL)); + check_caps_11be(&caps); + + /* SSID not found */ + fail_unless(parse_body(&caps, g_assoc, IEEE80211_FREQUENCY_BAND_5_GHZ, "wrong_ssid", NULL)); + + /* Invalid IES */ + fail_unless(parse_body(&caps, g_invalid_1, IEEE80211_FREQUENCY_BAND_5_GHZ, g_ssid, NULL)); + fail_unless(parse_body(&caps, g_invalid_2, IEEE80211_FREQUENCY_BAND_5_GHZ, g_ssid, NULL)); + fail_unless(parse_body(&caps, g_invalid_3, IEEE80211_FREQUENCY_BAND_5_GHZ, g_ssid, NULL)); + fail_unless(parse_body(&caps, g_invalid_4, IEEE80211_FREQUENCY_BAND_5_GHZ, g_ssid, NULL)); + fail_unless(parse_body(&caps, g_invalid_5, IEEE80211_FREQUENCY_BAND_5_GHZ, g_ssid, NULL)); +} +END_TEST + +/*####################################################################### +# TEST_PARSE_MLO_ASSOC_BODY # +########################################################################*/ +static char *g_mlo_assoc = "11010a00000b6672765f746573745f626801088c121824b048606c2102f9162402013a301a0100000fac040100000fac040100000fac08c0000000000fac063b068383848586897f0b80004880010040c0010021ff27230108001000084c3042c06d1b851800cc00aaffaaffaaffaaff7b1cc7711cc7711cc7711cc771ff033bbd06f40120ff156ce200ee6ddb6040e7000e00444444444444444444ffe96b00010900904c4c847ae207007c21000700904c4c941f310501080c1218243048606c2d1aef0117ffffff0000000000000000000000000000000000000000bf0cf6799b0feaff0000eaff0020ff27230108001000084c3042c06d1b851800cc00eaffeaffeaffeaff7b1cc7711cc7711cc7711cc771ff126ce200ec0d1b6040e7000e00333333333333005d22000700904c4d41f53105010402040b162d1aef1117ffff000000000000000000000000000000000000000000ff1d23010800100000223042c00d03851800cc00fafffaff1b1cc7711cc771ff0f6ca200ec01036040e7000e00222222dd09001018020001100000dd070050f202000100"; +static char *g_mlo_assoc_frag_ml_ie = "11010a00000b6672765f746573745f626801088c129824b048606c2102f9182402013a301a0100000fac040100000fac040100000fac08c0000000000fac063b068983848586897f0b00004880010040c0010020ff27230108001000084c3042c06d1b851800cc00aaffaaffaaffaaff7b1cc7711cc7711cc7711cc771ff033bbd06f40120ff156ce200ee6ddb6040e7000e00444444444444444444ffff6b00010900904c4c847ae207009c21000700904c4c941f310501080c1218243048606c2d1aef0117ffffff0000000000000000000000000000000000000000bf0cf6799b0feaff0000eaff0020ff27230108001000084c3042c06d1b851800cc00eaffeaffeaffeaff7b1cc7711cc7711cc7711cc771ff126ce200ec0d1b6040e7000e00333333333333dd1eaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa007d22000700904c4d41f53105010402040b162d1aef1117ffff000000000000000000000000000000000000000000ff1d23010800100000223042c00d03851800cc00fafffaff1b1cc7711cc771ff0f6ca200ec01f22a036040e7000e00222222dd1eaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadd09001018020001100000dd070050f202000100"; +static char *g_mlo_assoc_frag_ml_ie_frag_sp = "11010a00000b6672765f746573745f626801088c129824b048606c2102f9182402013a301a0100000fac040100000fac040100000fac08c0000000000fac063b068983848586897f0b0000488001004040000020ff1a230108001000084c3042c06d1b0518008c00aaffaaffaaffaaffff033bbd06f40120ff156ce200ee6ddb6040e7000e00444444444444444444ffff6b00010900904c4c847ae20700ff21000700904c4c941f3105ddb2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa01080c1218243048606c2d1aef0117ffffff0000000000000000000000000000000000000000bf0cf6799b0feaff0000eafff2ff0020ff1a230108001000084c3042fe24c06d1b0518008c00eaffeaffeaffeaffff126ce200ec0d1b6040e7000e0033333333333300ff22000700904c4d41f53105dd8caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa010402040b162d1aef1117ffff000000000000000000000000000000000000000000ff1623010800100000223042c00df236030518008c00fafffaffff0f6ca200ec01036040e7000e00222222dd19bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbdd09001018020001100000dd070050f202000100"; + +static void check_mlo_caps(map_sta_capability_t *caps, uint8_t band) +{ + if (band == IEEE80211_FREQUENCY_BAND_6_GHZ) { + fail_unless(caps->eht_support && caps->he_support && !caps->vht_support); + fail_unless(caps->max_tx_spatial_streams == 4); + } else if (band == IEEE80211_FREQUENCY_BAND_5_GHZ) { + fail_unless(caps->eht_support && caps->he_support && caps->vht_support); + fail_unless(caps->max_tx_spatial_streams == 3); + } else if (band == IEEE80211_FREQUENCY_BAND_2_4_GHZ) { + fail_unless(caps->eht_support && caps->he_support && !caps->vht_support); + fail_unless(caps->max_tx_spatial_streams == 2); + } else { + fail_unless(false); + } + + fail_unless(caps->mld_modes.str); + fail_unless(!caps->mld_modes.nstr); + fail_unless(!caps->mld_modes.emlsr); + fail_unless(!caps->mld_modes.emlmr); +} + +START_TEST(test_parse_mlo_assoc_body) +{ + map_sta_capability_t caps; + char *ssid = "frv_test_bh"; + mac_addr aff_6G = {0x00, 0x90 , 0x4c, 0x4c, 0x84, 0x7a}; + mac_addr aff_5G = {0x00, 0x90 , 0x4c, 0x4c, 0x94, 0x1f}; + mac_addr aff_2G = {0x00, 0x90 , 0x4c, 0x4d, 0x41, 0xf5}; + + char *assoc[3] = {g_mlo_assoc, g_mlo_assoc_frag_ml_ie, g_mlo_assoc_frag_ml_ie_frag_sp}; + int i; + + /* Parse 3 assoc ies */ + for (i = 0; i < 3; i++) { + fail_unless(!parse_body(&caps, assoc[i], IEEE80211_FREQUENCY_BAND_6_GHZ, ssid, aff_6G)); + check_mlo_caps(&caps, IEEE80211_FREQUENCY_BAND_6_GHZ); + + fail_unless(!parse_body(&caps, assoc[i], IEEE80211_FREQUENCY_BAND_5_GHZ, ssid, aff_5G)); + check_mlo_caps(&caps, IEEE80211_FREQUENCY_BAND_5_GHZ); + + fail_unless(!parse_body(&caps, assoc[i], IEEE80211_FREQUENCY_BAND_2_4_GHZ, ssid, aff_2G)); + check_mlo_caps(&caps, IEEE80211_FREQUENCY_BAND_2_4_GHZ); + } +} +END_TEST + +/*####################################################################### +# TEST_GET_MAX_PHY_RATE # +########################################################################*/ +START_TEST(test_get_max_phy_rate) +{ + map_sta_capability_t caps = { 0 }; + + caps.max_bandwidth = 20; + caps.max_tx_spatial_streams = 1; + caps.sgi_support = 0; + + caps.supported_standard = STD_80211_B; + fail_unless(map_get_max_phy_rate(&caps) == 11000); + + caps.supported_standard = STD_80211_G; + fail_unless(map_get_max_phy_rate(&caps) == 54000); + + caps.supported_standard = STD_80211_G; + fail_unless(map_get_max_phy_rate(&caps) == 54000); + + caps.supported_standard = STD_80211_N; + fail_unless(map_get_max_phy_rate(&caps) == 65000); + + caps.sgi_support = 1; + fail_unless(map_get_max_phy_rate(&caps) == 72200); + + caps.max_tx_spatial_streams = 2; + fail_unless(map_get_max_phy_rate(&caps) == 144000); + + caps.max_bandwidth = 40; + fail_unless(map_get_max_phy_rate(&caps) == 300000); + + caps.supported_standard = STD_80211_AC; + fail_unless(map_get_max_phy_rate(&caps) == 400000); + + caps.max_tx_spatial_streams = 3; + fail_unless(map_get_max_phy_rate(&caps) == 600000); + + caps.max_bandwidth = 80; + fail_unless(map_get_max_phy_rate(&caps) == 1300000); + + + caps.supported_standard = STD_80211_ANACAX; + caps.max_bandwidth = 20; + caps.max_tx_spatial_streams = 2; + fail_unless(map_get_max_phy_rate(&caps) == 286800); + + caps.max_bandwidth = 80; + fail_unless(map_get_max_phy_rate(&caps) == 1201000); + + caps.max_bandwidth = 160; + fail_unless(map_get_max_phy_rate(&caps) == 2402000); + + caps.max_tx_spatial_streams = 4; + fail_unless(map_get_max_phy_rate(&caps) == 4803900); + + /* Invalid */ + caps.max_bandwidth = 200; + caps.max_tx_spatial_streams = 5; + fail_unless(map_get_max_phy_rate(&caps) == 4803900); + + caps.supported_standard = STD_80211_N; + fail_unless(map_get_max_phy_rate(&caps) == 600000); +} +END_TEST + +const char *test_suite_name = "map_info"; +test_case_t test_cases[] = { + TEST("parse_assoc_body", test_parse_assoc_body ), + TEST("parse_mlo_assoc_body", test_parse_mlo_assoc_body ), + TEST("get_max_phy_rate", test_get_max_phy_rate ), + TEST_CASES_END +}; diff --git a/source/test/libutils/test_map_blocklist.c b/source/test/libutils/test_map_blocklist.c new file mode 100644 index 0000000..72488a3 --- /dev/null +++ b/source/test/libutils/test_map_blocklist.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "map_blocklist.h" +#include "test.h" + +/*####################################################################### +# GLOBALS # +########################################################################*/ +static mac_addr g_mac1 = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; +static mac_addr g_mac2 = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15}; +static mac_addr g_mac3 = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25}; + +static int g_update_cb_called; +static int g_iter_cb_called; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static void dummy_printf(const char *fmt, ...) +{ +} + +static void update_cb(void) +{ + g_update_cb_called++; +} + +static void iter_cb(mac_addr mac, map_block_reason_t reason, size_t idx, void *data) +{ + fail_unless(idx >= 0 && idx <= 2); + fail_unless(data == (void*)0x123); + + if (idx == 0) { + fail_unless(!maccmp(mac, g_mac1)); + fail_unless(reason == THIRD_PARTY_CONTROLLER); + g_iter_cb_called |= 0x01; + } + + if (idx == 1) { + fail_unless(!maccmp(mac, g_mac2)); + fail_unless(reason == THIRD_PARTY_AGENT); + g_iter_cb_called |= 0x02; + } +} + +/*####################################################################### +# TEST_BLOCKLIST # +########################################################################*/ +START_TEST(test_blocklist) +{ + map_blocked_dev_info_t *dev; + + fail_unless(!map_blocklist_init()); + + map_blocklist_register_update_cb(update_cb); + + /* Add */ + fail_unless(!!(dev = map_blocklist_add_dev(g_mac1, THIRD_PARTY_CONTROLLER))); + fail_unless(map_blocklist_get_nr_dev() == 1); + fail_unless(g_update_cb_called == 1); + + fail_unless(!!(dev = map_blocklist_add_dev(g_mac2, THIRD_PARTY_AGENT))); + fail_unless(map_blocklist_get_nr_dev() == 2); + fail_unless(g_update_cb_called == 2); + + /* Dump */ + map_blocklist_dump(dummy_printf); + + /* Get */ + fail_unless(!!(dev = map_blocklist_get_dev(g_mac1))); + fail_unless(!!(dev = map_blocklist_get_dev(g_mac2))); + fail_unless(!(dev = map_blocklist_get_dev(g_mac3))); + + /* Iter */ + map_bloclist_iter_dev(iter_cb, (void*)0x123); + fail_unless(g_iter_cb_called == 0x03); + + /* Del */ + fail_unless(!!(dev = map_blocklist_get_dev(g_mac1))); + fail_unless(!map_blocklist_remove_dev(dev)); + fail_unless(g_update_cb_called == 3); + fail_unless(map_blocklist_get_nr_dev() == 1); + fail_unless(!(dev = map_blocklist_get_dev(g_mac1))); + + map_blocklist_fini(); +} +END_TEST + +const char *test_suite_name = "blocklist"; +test_case_t test_cases[] = { + TEST("blocklist", test_blocklist ), + TEST_CASES_END +}; diff --git a/source/test/libutils/test_map_channel_set.c b/source/test/libutils/test_map_channel_set.c new file mode 100644 index 0000000..eb52d72 --- /dev/null +++ b/source/test/libutils/test_map_channel_set.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2021-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "test.h" +#include "map_channel_set.h" + +/*####################################################################### +# TEST_CHANNEL_SET # +########################################################################*/ +START_TEST(test_channel_set) +{ + map_channel_set_t ch_set; + map_channel_set_t ch_set2; + map_channel_bw_set_t ch_bw_set; + + int count = 0; + int i; + char buf[128]; + + /* map_cs_unset_all */ + map_cs_unset_all(&ch_set); + + fail_unless(map_cs_nr(&ch_set) == 0); + for (i = 0; i < 255; i++) { + fail_unless(false == map_cs_is_set(&ch_set, i)); + } + + /* map_cs_set */ + map_cs_set(&ch_set, 1); + map_cs_set(&ch_set, 36); + map_cs_set(&ch_set, 100); + fail_unless(map_cs_nr(&ch_set) == 3); + for (i = 0; i < 255; i++) { + bool exp = (i == 1 || i == 36 || i == 100); + fail_unless(exp == map_cs_is_set(&ch_set, i)); + } + + /* map_cs_foreach */ + map_cs_foreach(&ch_set, i) { + fail_unless(i == 1 || i == 36 || i == 100); + count++; + } + fail_unless(count == 3); + + /* map_cs_unset */ + map_cs_unset(&ch_set, 36); + for (i = 0; i < 255; i++) { + bool exp = (i == 1 || i == 100); + fail_unless(exp == map_cs_is_set(&ch_set, i)); + } + + map_cs_unset(&ch_set, 1); + for (i = 0; i < 255; i++) { + bool exp = (i == 100); + fail_unless(exp == map_cs_is_set(&ch_set, i)); + } + + map_cs_unset(&ch_set, 100); + fail_unless(map_cs_nr(&ch_set) == 0); + for (i = 0; i < 255; i++) { + fail_unless(false == map_cs_is_set(&ch_set, i)); + } + + /* map_cs_set_all */ + map_cs_set_all(&ch_set); + fail_unless(map_cs_nr(&ch_set) == MAP_MAX_CHANNEL - 1); + fail_unless(!map_cs_is_set(&ch_set, 0)); + for (i = 1; i < MAP_MAX_CHANNEL; i++) { + fail_unless(map_cs_is_set(&ch_set, i)); + } + + for (i = MAP_MAX_CHANNEL; i < 255; i++) { + fail_unless(!map_cs_is_set(&ch_set, i)); + } + + /* map_cs_or */ + map_cs_unset_all(&ch_set); + map_cs_unset_all(&ch_set2); + map_cs_set(&ch_set, 1); + map_cs_set(&ch_set, 6); + map_cs_set(&ch_set, 100); + map_cs_set(&ch_set2, 1); + map_cs_set(&ch_set2, 6); + map_cs_set(&ch_set2, 104); + + map_cs_or(&ch_set, &ch_set2); + + fail_unless(map_cs_nr(&ch_set) == 4); + fail_unless(map_cs_is_set(&ch_set, 1)); + fail_unless(map_cs_is_set(&ch_set, 6)); + fail_unless(map_cs_is_set(&ch_set, 100)); + fail_unless(map_cs_is_set(&ch_set, 104)); + + /* map_cs_and */ + map_cs_unset_all(&ch_set); + map_cs_unset_all(&ch_set2); + map_cs_set(&ch_set, 1); + map_cs_set(&ch_set, 6); + map_cs_set(&ch_set, 100); + map_cs_set(&ch_set2, 1); + map_cs_set(&ch_set2, 6); + map_cs_set(&ch_set2, 104); + + map_cs_and(&ch_set, &ch_set2); + + fail_unless(map_cs_nr(&ch_set) == 2); + fail_unless(map_cs_is_set(&ch_set, 1)); + fail_unless(map_cs_is_set(&ch_set, 6)); + + /* map_cs_and_not */ + map_cs_unset_all(&ch_set); + map_cs_unset_all(&ch_set2); + map_cs_set(&ch_set, 1); + map_cs_set(&ch_set, 6); + map_cs_set(&ch_set, 100); + map_cs_set(&ch_set2, 1); + map_cs_set(&ch_set2, 6); + map_cs_set(&ch_set2, 104); + + map_cs_and_not(&ch_set, &ch_set2); + + fail_unless(map_cs_nr(&ch_set) == 1); + fail_unless(map_cs_is_set(&ch_set, 100)); + + /* map_cs_copy & map_cs_compare */ + map_cs_unset_all(&ch_set); + map_cs_set(&ch_set, 1); + map_cs_set(&ch_set, 6); + map_cs_set(&ch_set, 100); + map_cs_copy(&ch_set2, &ch_set); + fail_unless(!map_cs_compare(&ch_set, &ch_set2)); + map_cs_set(&ch_set, 104); + fail_unless(map_cs_compare(&ch_set, &ch_set2)); + + /* map_cs_to_string */ + map_cs_unset_all(&ch_set); + map_cs_set(&ch_set, 1); + map_cs_set(&ch_set, 36); + map_cs_set(&ch_set, 100); + fail_unless(!strcmp(map_cs_to_string(&ch_set, ' ', buf, sizeof(buf)), "1 36 100")); + + /* map_cs_from_string */ + fail_unless(!map_cs_from_string("1 4 7", ' ', &ch_set)); + fail_unless(map_cs_nr(&ch_set) == 3); + fail_unless(map_cs_is_set(&ch_set, 1) && map_cs_is_set(&ch_set, 4) && map_cs_is_set(&ch_set, 7)); + fail_unless(!map_cs_from_string("10,11,12", ',', &ch_set)); + fail_unless(map_cs_nr(&ch_set) == 3); + fail_unless(map_cs_is_set(&ch_set, 10) && map_cs_is_set(&ch_set, 11) && map_cs_is_set(&ch_set, 12)); + + /* map_cs_bw_unset_all */ + map_cs_bw_unset_all(&ch_bw_set); + + fail_unless(map_cs_nr(&ch_bw_set.channel_set_20) == 0); + fail_unless(map_cs_nr(&ch_bw_set.channel_set_40) == 0); + fail_unless(map_cs_nr(&ch_bw_set.channel_set_80) == 0); + fail_unless(map_cs_nr(&ch_bw_set.channel_set_160) == 0); + fail_unless(map_cs_nr(&ch_bw_set.channel_set_320) == 0); + + for (i = 0; i < 255; i++) { + fail_unless(false == map_cs_is_set(&ch_bw_set.channel_set_20, i)); + fail_unless(false == map_cs_is_set(&ch_bw_set.channel_set_40, i)); + fail_unless(false == map_cs_is_set(&ch_bw_set.channel_set_80, i)); + fail_unless(false == map_cs_is_set(&ch_bw_set.channel_set_160, i)); + fail_unless(false == map_cs_is_set(&ch_bw_set.channel_set_320, i)); + } + + /* map_cs_bw_set */ + map_cs_bw_set(&ch_bw_set, 20, 1); + map_cs_bw_set(&ch_bw_set, 40, 1); + map_cs_bw_set(&ch_bw_set, 80, 1); + map_cs_bw_set(&ch_bw_set, 160, 1); + map_cs_bw_set(&ch_bw_set, 320, 1); + + map_cs_bw_set(&ch_bw_set, 20, 36); + map_cs_bw_set(&ch_bw_set, 40, 36); + map_cs_bw_set(&ch_bw_set, 80, 36); + map_cs_bw_set(&ch_bw_set, 160, 36); + map_cs_bw_set(&ch_bw_set, 320, 36); + + map_cs_bw_set(&ch_bw_set, 20, 100); + map_cs_bw_set(&ch_bw_set, 40, 100); + map_cs_bw_set(&ch_bw_set, 80, 100); + map_cs_bw_set(&ch_bw_set, 160, 100); + map_cs_bw_set(&ch_bw_set, 320, 100); + + fail_unless(map_cs_nr(&ch_bw_set.channel_set_20) == 3); + fail_unless(map_cs_nr(&ch_bw_set.channel_set_40) == 3); + fail_unless(map_cs_nr(&ch_bw_set.channel_set_80) == 3); + fail_unless(map_cs_nr(&ch_bw_set.channel_set_160) == 3); + fail_unless(map_cs_nr(&ch_bw_set.channel_set_320) == 3); + + for (i = 0; i < 255; i++) { + bool exp = (i == 1 || i == 36 || i == 100); + + fail_unless(exp == map_cs_is_set(&ch_bw_set.channel_set_20, i)); + fail_unless(exp == map_cs_is_set(&ch_bw_set.channel_set_40, i)); + fail_unless(exp == map_cs_is_set(&ch_bw_set.channel_set_80, i)); + fail_unless(exp == map_cs_is_set(&ch_bw_set.channel_set_160, i)); + fail_unless(exp == map_cs_is_set(&ch_bw_set.channel_set_320, i)); + } + + /* map_cs_bw_to_string */ + map_cs_bw_unset_all(&ch_bw_set); + map_cs_bw_set(&ch_bw_set, 20, 1); + map_cs_bw_set(&ch_bw_set, 40, 4); + map_cs_bw_set(&ch_bw_set, 80, 100); + map_cs_bw_set(&ch_bw_set, 160, 157); + map_cs_bw_set(&ch_bw_set, 320, 95); + fail_unless(!strcmp(map_cs_bw_to_string(&ch_bw_set, ',', buf, sizeof(buf)), "1/20,4/40,100/80,157/160,95/320")); +} +END_TEST + +const char *test_suite_name = "channel_set"; +test_case_t test_cases[] = { + TEST("channel_set", test_channel_set ), + TEST_CASES_END +}; diff --git a/source/test/libutils/test_map_cli_subscription.c b/source/test/libutils/test_map_cli_subscription.c new file mode 100644 index 0000000..1e9af5f --- /dev/null +++ b/source/test/libutils/test_map_cli_subscription.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "test.h" +#include "map_cli_subscription.h" + +/*####################################################################### +# TEST_SUBSCRIPTION # +########################################################################*/ +static void subs1_cb(const char *event, const char *payload, void *context) +{ +} + +static void subs2_cb(const char *event, const char *payload, void *context) +{ +} + +static void subs3_cb(const char *event, const char *payload, void *context) +{ +} + +START_TEST(test_subscription) +{ + subscriptions_t *subscriptions; + subscription_t *subscription; + const char *e; + subscription_function_t f; + void *c; + + /* subscriptions_create */ + fail_unless(!!(subscriptions = subscriptions_create())); + + /* subscriptions_add */ + fail_unless( subscriptions_add(subscriptions, "subs1", NULL, SUBS_FLAG_MODE_FULL, (void*)1)); + fail_unless( subscriptions_add(subscriptions, NULL, subs1_cb, SUBS_FLAG_MODE_FULL, (void*)1)); + fail_unless( subscriptions_add(NULL, "subs1", subs1_cb, SUBS_FLAG_MODE_FULL, (void*)1)); + fail_unless(!subscriptions_add(subscriptions, "subs1", subs1_cb, SUBS_FLAG_MODE_FULL, (void*)1)); + fail_unless( subscriptions_add(subscriptions, "subs1", subs1_cb, SUBS_FLAG_MODE_FULL, (void*)1)); + fail_unless(!subscriptions_add(subscriptions, "subs2", subs2_cb, SUBS_FLAG_MODE_FULL, (void*)2)); + fail_unless(!subscriptions_add(subscriptions, "subs3", subs3_cb, (SUBS_FLAG_MODE_FULL | SUBS_FLAG_MODE_REDUCED), (void*)3)); + + /* subscriptions_get */ + fail_unless(!subscriptions_get(subscriptions, NULL)); + fail_unless(!subscriptions_get(subscriptions, "subs4")); + fail_unless(!subscriptions_get(subscriptions, "subs4")); + fail_unless(!!(subscription = subscriptions_get(subscriptions, "subs1"))); + fail_unless(!!(subscription = subscriptions_get(subscriptions, "SUBS1"))); + + /* subscription_event */ + fail_unless(!subscription_event(NULL)); + fail_unless(!!(e = subscription_event(subscription))); + fail_unless(!strcmp(e, "subs1")); + + /* subscription_function */ + fail_unless(!subscription_function(NULL)); + fail_unless(!!(f = subscription_function(subscription))); + fail_unless(f == subs1_cb); + + /* subscription_context */ + fail_unless(!subscription_context(NULL)); + fail_unless(!!(c = subscription_context(subscription))); + fail_unless(c == (void*)1); + + /* subscription_flags */ + fail_unless(!!(subscription = subscriptions_get(subscriptions, "subs3"))); + fail_unless(subscription_flags(subscription) & SUBS_FLAG_MODE_FULL); + fail_unless(subscription_flags(subscription) & SUBS_FLAG_MODE_REDUCED); + + /* subscription_del */ + fail_unless( subscriptions_del(subscriptions, NULL)); + fail_unless( subscriptions_del(NULL, "subs2")); + fail_unless(!subscriptions_del(subscriptions, "subs2")); + + fail_unless( !subscriptions_get(subscriptions, "subs2")); + fail_unless(!!subscriptions_get(subscriptions, "subs1")); + fail_unless(!!subscriptions_get(subscriptions, "subs3")); + + /* subscriptions_destroy */ + subscriptions_destroy(NULL); + subscriptions_destroy(subscriptions); + +} +END_TEST + +const char *test_suite_name = ""; +test_case_t test_cases[] = { + TEST("subscription", test_subscription ), + TEST_CASES_END +}; diff --git a/source/test/libutils/test_map_datamodel.c b/source/test/libutils/test_map_datamodel.c new file mode 100644 index 0000000..e35ea4e --- /dev/null +++ b/source/test/libutils/test_map_datamodel.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2019-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "test.h" +#include "map_data_model.h" +#include "map_data_model_dumper.h" + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static void dummy_printf(const char *fmt, ...) +{ +} + +/*####################################################################### +# TEST_DATAMODEL # +########################################################################*/ +START_TEST(test_datamodel) +{ + mac_addr ale_mac1 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + mac_addr ale_mac2 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}; + mac_addr radio_id = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}; + mac_addr bssid1 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}; + mac_addr bssid2 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}; + mac_addr sta_mac1 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}; + mac_addr sta_mac2 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}; + mac_addr sta_mac3 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}; + + map_ale_info_t *ale1, *ale2; + map_radio_info_t *radio1, *radio2; + map_bss_info_t *bss1, *bss2; + map_sta_info_t *sta, *sta1, *sta2, *sta3; + + int count; + + fail_unless(!map_dm_init()); + + fail_unless(!!(ale1 = map_dm_create_ale(ale_mac1))); + fail_unless(!!(ale2 = map_dm_create_ale(ale_mac2))); + fail_unless(!!(radio1 = map_dm_create_radio(ale1, radio_id))); + fail_unless(!!(radio2 = map_dm_create_radio(ale2, radio_id))); /* Same Id -> not unique */ + fail_unless(!!(bss1 = map_dm_create_bss(radio1, bssid1))); + fail_unless(!!(bss2 = map_dm_create_bss(radio1, bssid2))); + fail_unless(!!(sta1 = map_dm_create_sta(bss1, sta_mac1))); + fail_unless(!!(sta2 = map_dm_create_sta(bss1, sta_mac2))); + fail_unless(!!(sta3 = map_dm_create_sta(bss2, sta_mac3))); + + /* Get back */ + fail_unless(ale1->radios_nr == 1); + fail_unless(ale2->radios_nr == 1); + fail_unless(radio1->bsss_nr == 2); + fail_unless(radio2->bsss_nr == 0); + fail_unless(bss1->stas_nr == 2); + fail_unless(bss2->stas_nr == 1); + + fail_unless(ale1 == map_dm_get_ale(ale_mac1)); + fail_unless(ale2 == map_dm_get_ale(ale_mac2)); + fail_unless(radio1 == map_dm_get_radio(ale1, radio_id)); + fail_unless(!strcmp(radio1->radio_id_base64, "AAAAAAAD")); + fail_unless(radio2 == map_dm_get_radio(ale2, radio_id)); + fail_unless(bss1 == map_dm_get_bss(radio1, bssid1)); + fail_unless(bss1 == map_dm_get_bss_from_ale(ale1, bssid1)); + fail_unless(bss1 == map_dm_get_bss_gbl(bssid1)); + fail_unless(bss2 == map_dm_get_bss(radio1, bssid2)); + fail_unless(bss2 == map_dm_get_bss_from_ale(ale1, bssid2)); + fail_unless(bss2 == map_dm_get_bss_gbl(bssid2)); + fail_unless(sta1 == map_dm_get_sta(bss1, sta_mac1)); + fail_unless(sta1 == map_dm_get_sta_from_ale(ale1, sta_mac1)); + fail_unless(sta1 == map_dm_get_sta_gbl(sta_mac1)); + fail_unless(sta2 == map_dm_get_sta(bss1, sta_mac2)); + fail_unless(sta2 == map_dm_get_sta_from_ale(ale1, sta_mac2)); + fail_unless(sta2 == map_dm_get_sta_gbl(sta_mac2)); + fail_unless(sta3 == map_dm_get_sta(bss2, sta_mac3)); + fail_unless(sta3 == map_dm_get_sta_from_ale(ale1, sta_mac3)); + fail_unless(sta3 == map_dm_get_sta_gbl(sta_mac3)); + + fail_unless(NULL == map_dm_get_bss(radio2, bssid1)); + fail_unless(NULL == map_dm_get_bss_from_ale(ale2, bssid1)); + fail_unless(NULL == map_dm_get_sta(bss2, sta_mac1)); + fail_unless(NULL == map_dm_get_sta_from_ale(ale2, sta_mac1)); + + count = 0; + map_dm_foreach_sta(bss1, sta) { + fail_unless(!maccmp(sta->mac, sta_mac1) || !maccmp(sta->mac, sta_mac2)); + count++; + } + fail_unless(count == 2); + + /* Move sta */ + map_dm_update_sta_bss(bss1, sta3); + fail_unless(bss1->stas_nr == 3); + fail_unless(bss2->stas_nr == 0); + + count = 0; + map_dm_foreach_sta(bss1, sta) { + fail_unless(!maccmp(sta->mac, sta_mac1) || !maccmp(sta->mac, sta_mac2) || !maccmp(sta->mac, sta_mac3)); + count++; + } + fail_unless(count == 3); + + + /* For code coverage... */ + map_dm_dump_agent_info_tree(dummy_printf); + + map_dm_remove_sta(sta1); + map_dm_remove_sta(sta2); + map_dm_remove_sta(sta3); + map_dm_remove_bss(bss1); + map_dm_remove_bss(bss2); + map_dm_remove_radio(radio1); + map_dm_remove_radio(radio2); + map_dm_remove_ale(ale1); + map_dm_remove_ale(ale2); + + map_dm_fini(); +} +END_TEST + +/*####################################################################### +# TEST_INACTIVE_STA # +########################################################################*/ +START_TEST(test_inactive_sta) +{ +#define NUM_MACS (MAX_INACTIVE_STA + 10) + mac_addr ale_mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + mac_addr radio_id = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}; + mac_addr bssid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}; + mac_addr macs[NUM_MACS]; + uint8_t *p = (uint8_t *)macs; + + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + map_sta_info_t *stas[NUM_MACS] = { 0 }; + int i; + + fail_unless(NUM_MACS > MAX_INACTIVE_STA); + + fail_unless(!map_dm_init()); + fail_unless(!!(ale = map_dm_create_ale(ale_mac))); + fail_unless(!!(radio = map_dm_create_radio(ale, radio_id))); + fail_unless(!!(bss = map_dm_create_bss(radio, bssid))); + + /* Create random sta */ + for (i = 0; i= (NUM_MACS - MAX_INACTIVE_STA)) ? true : false); + } + + map_dm_fini(); +} +END_TEST + +/*####################################################################### +# TEST_MARK_STA # +########################################################################*/ +START_TEST(test_mark_sta) +{ + mac_addr ale_mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + mac_addr radio_id1 = {0x01, 0x00, 0x00, 0x00, 0x00, 0x01}; + mac_addr radio_id2 = {0x01, 0x00, 0x00, 0x00, 0x00, 0x02}; + mac_addr bssid1 = {0x02, 0x00, 0x00, 0x00, 0x00, 0x01}; + mac_addr bssid2 = {0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + mac_addr bssid3 = {0x02, 0x00, 0x00, 0x00, 0x00, 0x03}; + mac_addr bssid4 = {0x02, 0x00, 0x00, 0x00, 0x00, 0x04}; + mac_addr mac1 = {0x03, 0x00, 0x00, 0x00, 0x00, 0x01}; + mac_addr mac2 = {0x03, 0x00, 0x00, 0x00, 0x00, 0x02}; + mac_addr mac3 = {0x03, 0x00, 0x00, 0x00, 0x00, 0x03}; + mac_addr mac4 = {0x03, 0x00, 0x00, 0x00, 0x00, 0x04}; + mac_addr mac5 = {0x03, 0x00, 0x00, 0x00, 0x00, 0x05}; + + map_ale_info_t *ale; + map_radio_info_t *radio1, *radio2; + map_bss_info_t *bss1, *bss2, *bss3, *bss4; + map_sta_info_t *sta1, *sta2, *sta3, *sta4, *sta5; + + fail_unless(!map_dm_init()); + fail_unless(!!(ale = map_dm_create_ale(ale_mac))); + fail_unless(!!(radio1 = map_dm_create_radio(ale, radio_id1))); + fail_unless(!!(radio2 = map_dm_create_radio(ale, radio_id2))); + fail_unless(!!(bss1 = map_dm_create_bss(radio1, bssid1))); + fail_unless(!!(bss2 = map_dm_create_bss(radio1, bssid2))); + fail_unless(!!(bss3 = map_dm_create_bss(radio2, bssid3))); + fail_unless(!!(bss4 = map_dm_create_bss(radio2, bssid4))); + fail_unless(!!(sta1 = map_dm_create_sta(bss1, mac1))); + fail_unless(!!(sta2 = map_dm_create_sta(bss2, mac2))); + fail_unless(!!(sta3 = map_dm_create_sta(bss3, mac3))); + fail_unless(!!(sta4 = map_dm_create_sta(bss4, mac4))); + fail_unless(!!(sta5 = map_dm_create_sta(bss4, mac5))); + + /* Mark all stas */ + map_dm_mark_stas(ale); + fail_unless(map_dm_is_marked_sta(sta1) && map_dm_is_marked_sta(sta2) && + map_dm_is_marked_sta(sta3) && map_dm_is_marked_sta(sta4) && + map_dm_is_marked_sta(sta5)); + + /* Unmark sta1 and sta2 */ + map_dm_unmark_sta(sta1); + map_dm_unmark_sta(sta2); + + /* sta3 and sta4 associated a long time ago, sta5 just */ + sta3->assoc_ts = map_dm_get_sta_assoc_ts(5000); + sta4->assoc_ts = map_dm_get_sta_assoc_ts(5000); + sta5->assoc_ts = map_dm_get_sta_assoc_ts(5); + + /* Remove unmarked - sta3 and sta4 got removed */ + map_dm_remove_marked_stas(ale, 1000); + + fail_unless(!!map_dm_get_sta_gbl(mac1)); + fail_unless(!!map_dm_get_sta_gbl(mac2)); + fail_unless(!map_dm_get_sta_gbl(mac3)); + fail_unless(!map_dm_get_sta_gbl(mac4)); + fail_unless(!!map_dm_get_sta_gbl(mac5)); + + map_dm_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CLEANUP # +########################################################################*/ +START_TEST(test_cleanup) +{ + mac_addr ale_mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + mac_addr radio_id = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}; + mac_addr bssid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}; + mac_addr sta_mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}; + + map_ale_info_t *ale; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + + fail_unless(!map_dm_init()); + + fail_unless(!!(ale = map_dm_create_ale(ale_mac))); + fail_unless(!!(radio = map_dm_create_radio(ale, radio_id))); + fail_unless(!!(bss = map_dm_create_bss(radio, bssid))); + fail_unless(!!(sta = map_dm_create_sta(bss, sta_mac))); + + ale_mac[4]++; + radio_id[4]++; + bssid[4]++; + fail_unless(!!(ale = map_dm_create_ale(ale_mac))); + fail_unless(!!(radio = map_dm_create_radio(ale, radio_id))); + fail_unless(!!(bss = map_dm_create_bss(radio, bssid))); + fail_unless(!!(sta = map_dm_create_sta(bss, sta_mac))); + + /* map_datamodel_fini must remove all objects */ + map_dm_fini(); +} +END_TEST + +const char *test_suite_name = "map_datamodel"; +test_case_t test_cases[] = { + TEST("datamodel", test_datamodel ), + TEST("inactive_sta", test_inactive_sta ), + TEST("mark_sta", test_mark_sta ), + TEST("cleanup", test_cleanup ), + TEST_CASES_END +}; diff --git a/source/test/libutils/test_map_dm_eth_device_list.c b/source/test/libutils/test_map_dm_eth_device_list.c new file mode 100644 index 0000000..f4f830b --- /dev/null +++ b/source/test/libutils/test_map_dm_eth_device_list.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2019-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "test.h" +#include "map_data_model.h" +#include "map_dm_eth_device_list.h" +#include "map_topology_tree.h" +#include "1905_platform.h" + +/*####################################################################### +# GLOBALS # +########################################################################*/ +static mac_addr g_root_ale_mac = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; +static mac_addr g_ale_mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; +static mac_addr g_ale2_mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}; +static mac_addr g_radio_id = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}; +static mac_addr g_bssid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}; +static mac_addr g_sta_mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}; + +static mac_addr g_eth_mac = {0x01, 0x00, 0x00, 0x00, 0x00, 0x02}; +static mac_addr g_eth2_mac = {0x01, 0x00, 0x00, 0x00, 0x00, 0x03}; +static mac_addr g_eth_dev_macs[3] = {{0x81, 0x00, 0x00, 0x00, 0x00, 0x01}, {0x81, 0x00, 0x00, 0x00, 0x00, 0x02}, {0x79, 0x00, 0x00, 0x00, 0x00, 0x03}}; + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static void test_init(void) +{ + fail_unless(!map_dm_init()); + fail_unless(init_topology_tree(g_root_ale_mac) == 1); +} + +static void test_fini(void) +{ + map_dm_fini(); +} + +/*####################################################################### +# TEST_ETH_DEVICE_LIST # +########################################################################*/ +/* Create topology: + controller + - ale (eth backhaul g_eth_mac) + - ale2 (eth backhaul g_eth2_mac) +*/ +START_TEST(test_eth_device_list) +{ + map_ale_info_t *ale, *ale2; + map_radio_info_t *radio; + map_bss_info_t *bss; + map_sta_info_t *sta; + map_emex_eth_iface_t *eth_iface; + int i; + + test_init(); + + /* ALE 1 */ + fail_unless(!!(ale = map_dm_create_ale(g_ale_mac))); + fail_unless(!!(radio = map_dm_create_radio(ale, g_radio_id))); + fail_unless(!!(bss = map_dm_create_bss(radio, g_bssid))); + fail_unless(!!(sta = map_dm_create_sta(bss, g_sta_mac))); + + fail_unless(topology_tree_insert(get_root_ale_node(), ale) == 1); + + /* Add local interfaces */ + ale->local_iface_count = 2; + fail_unless(!!(ale->local_iface_list = calloc(2, sizeof(map_local_iface_t)))); + + maccpy(ale->local_iface_list[0].mac_address, g_eth_mac); + ale->local_iface_list[0].media_type = INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET; + + maccpy(ale->local_iface_list[1].mac_address, g_bssid); + ale->local_iface_list[1].media_type = INTERFACE_TYPE_IEEE_802_11AX; + + /* Add non 1905 mac */ + ale->non_1905_neighbor_count = 2; + fail_unless(!!(ale->non_1905_neighbor_list = calloc(2, sizeof(map_non_1905_neighbor_t)))); + + maccpy(ale->non_1905_neighbor_list[0].local_iface_mac, g_eth_mac); + ale->non_1905_neighbor_list[0].media_type = INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET; + ale->non_1905_neighbor_list[0].macs_nr = 2; + ale->non_1905_neighbor_list[0].macs = calloc(2, sizeof(mac_addr)); + maccpy(ale->non_1905_neighbor_list[0].macs[0], g_eth_dev_macs[0]); + maccpy(ale->non_1905_neighbor_list[0].macs[1], g_eth_dev_macs[1]); + acu_sort_mac_array(ale->non_1905_neighbor_list[0].macs, ale->non_1905_neighbor_list[0].macs_nr); + + maccpy(ale->non_1905_neighbor_list[1].local_iface_mac, g_bssid); + ale->non_1905_neighbor_list[1].media_type = INTERFACE_TYPE_IEEE_802_11AX; + ale->non_1905_neighbor_list[1].macs_nr = 1; + ale->non_1905_neighbor_list[1].macs = calloc(1, sizeof(mac_addr)); + maccpy(ale->non_1905_neighbor_list[1].macs[0], g_sta_mac); + + /* Add emex ethernet interface */ + ale->emex.eth_iface_list.iface_nr = 1; + ale->emex.eth_iface_list.ifaces = calloc(1, sizeof(map_emex_eth_iface_t)); + eth_iface = &ale->emex.eth_iface_list.ifaces[0]; + eth_iface->non_i1905_neighbor_macs_nr = 2; + eth_iface->non_i1905_neighbor_macs = calloc(2, sizeof(mac_addr)); + maccpy(eth_iface->non_i1905_neighbor_macs[0], g_eth_dev_macs[0]); + maccpy(eth_iface->non_i1905_neighbor_macs[1], g_eth_dev_macs[1]); + + + /* ALE2 */ + fail_unless(!!(ale2 = map_dm_create_ale(g_ale2_mac))); + + fail_unless(topology_tree_insert(ale, ale2) == 1); + + maccpy(ale2->upstream_local_iface_mac, g_eth2_mac); + maccpy(ale2->upstream_remote_iface_mac, g_eth_mac); + + ale2->local_iface_count = 1; + fail_unless(!!(ale2->local_iface_list = calloc(1, sizeof(map_local_iface_t)))); + + maccpy(ale2->local_iface_list[0].mac_address, g_eth2_mac); + ale2->local_iface_list[0].media_type = INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET; + + /* Add non 1905 mac */ + ale2->non_1905_neighbor_count = 1; + fail_unless(!!(ale2->non_1905_neighbor_list = calloc(2, sizeof(map_non_1905_neighbor_t)))); + + maccpy(ale2->non_1905_neighbor_list[0].local_iface_mac, g_eth2_mac); + ale2->non_1905_neighbor_list[0].media_type = INTERFACE_TYPE_IEEE_802_3AB_GIGABIT_ETHERNET; + ale2->non_1905_neighbor_list[0].macs_nr = 3; + ale2->non_1905_neighbor_list[0].macs = calloc(3, sizeof(mac_addr)); + maccpy(ale2->non_1905_neighbor_list[0].macs[0], g_eth_dev_macs[1]); + maccpy(ale2->non_1905_neighbor_list[0].macs[1], g_sta_mac); /* Needs to be ignored */ + maccpy(ale2->non_1905_neighbor_list[0].macs[2], g_eth2_mac); /* Needs to be ignored */ + acu_sort_mac_array(ale2->non_1905_neighbor_list[0].macs, ale2->non_1905_neighbor_list[0].macs_nr); + + + /* Update device list several times. Nothing happens the first "HISTORY_LEN" times. */ + for (i = 0; i < ETH_DEVICE_HISTORY_LEN; i++) { + map_dm_eth_device_list_schedule_update(); + fail_unless(ale->eth_device_list.macs_nr == 0); + fail_unless(ale2->eth_device_list.macs_nr == 0); + } + + /* Update device list again. */ + map_dm_eth_device_list_schedule_update(); + + /* Verify result */ + /* Check ALE: + - ethernet device with mac g_eth_dev_macs[0] + - emex filtered iface list is equal to device list + */ + fail_unless(ale->eth_device_list.macs_nr == 1); + fail_unless(!maccmp(ale->eth_device_list.macs[0], g_eth_dev_macs[0])); + fail_unless(eth_iface->filtered_non_i1905_neighbor_macs_nr = 1); + fail_unless(!maccmp(eth_iface->filtered_non_i1905_neighbor_macs[0], g_eth_dev_macs[0])); + + /* Check ALE2 + - ethernet device with mac g_eth_dev_macs[1] + */ + fail_unless(ale2->eth_device_list.macs_nr == 1); + fail_unless(!maccmp(ale2->eth_device_list.macs[0], g_eth_dev_macs[1])); + + + /* Add another mac to ALE2 */ + maccpy(ale2->non_1905_neighbor_list[0].macs[0], g_eth_dev_macs[1]); + maccpy(ale2->non_1905_neighbor_list[0].macs[1], g_sta_mac); + maccpy(ale2->non_1905_neighbor_list[0].macs[2], g_eth_dev_macs[2]); + acu_sort_mac_array(ale2->non_1905_neighbor_list[0].macs, ale2->non_1905_neighbor_list[0].macs_nr); + + /* Update device list several times. Nothing happens the first "HISTORY_LEN" times. */ + for (i = 0; i < ETH_DEVICE_HISTORY_LEN; i++) { + map_dm_eth_device_list_schedule_update(); + fail_unless(ale->eth_device_list.macs_nr == 1); + fail_unless(ale2->eth_device_list.macs_nr == 1); + } + + /* Update second time */ + map_dm_eth_device_list_schedule_update(); + + /* Verify result */ + /* Check ALE: + - ethernet device with mac g_eth_dev_macs[0] + */ + fail_unless(ale->eth_device_list.macs_nr == 1); + fail_unless(!maccmp(ale->eth_device_list.macs[0], g_eth_dev_macs[0])); + fail_unless(eth_iface->filtered_non_i1905_neighbor_macs_nr = 1); + fail_unless(!maccmp(eth_iface->filtered_non_i1905_neighbor_macs[0], g_eth_dev_macs[0])); + + /* Check ALE2 + - ethernet device with mac g_eth_dev_macs[1] and g_eth_dev_macs[2] + !! macs are sorted + */ + fail_unless(ale2->eth_device_list.macs_nr == 2); + fail_unless(!maccmp(ale2->eth_device_list.macs[0], g_eth_dev_macs[2])); + fail_unless(!maccmp(ale2->eth_device_list.macs[1], g_eth_dev_macs[1])); + + test_fini(); +} +END_TEST + +const char *test_suite_name = "map_dm_eth_device_list"; +test_case_t test_cases[] = { + TEST("eth_device_list", test_eth_device_list ), + TEST_CASES_END +}; diff --git a/source/test/libutils/test_map_info.c b/source/test/libutils/test_map_info.c new file mode 100644 index 0000000..bd3e8e5 --- /dev/null +++ b/source/test/libutils/test_map_info.c @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2019-2024 AirTies Wireless Networks + * + * Licensed under the BSD+Patent License. +*/ + +/*####################################################################### +# INCLUDES # +########################################################################*/ +#include +#include +#include + +#include "test.h" +#include "map_info.h" + + +/*####################################################################### +# HELP FUNCTIONS # +########################################################################*/ +static void test_init(void) +{ + fail_unless(!map_info_init()); +} + +static void test_fini(void) +{ + map_info_fini(); +} + +/*####################################################################### +# TEST_OP_CLASS # +########################################################################*/ +START_TEST(test_op_class) +{ + map_channel_set_t ch_set; + uint8_t center_op_classes[] = {128, 129, 132, 133, 134, 137}; + uint8_t not_center_op_classes[] = { 81, 82, 83, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; + bool is_center_chan; + int i; + + test_init(); + + fail_unless(map_get_op_class(6, 20, IEEE80211_FREQUENCY_BAND_2_4_GHZ) == 81); + fail_unless(map_get_op_class(6, 40, IEEE80211_FREQUENCY_BAND_2_4_GHZ) == 83); + fail_unless(map_get_op_class(11, 40, IEEE80211_FREQUENCY_BAND_2_4_GHZ) == 84); + + fail_unless(map_get_op_class_20MHz(6, IEEE80211_FREQUENCY_BAND_2_4_GHZ) == 81); + fail_unless(map_get_op_class_20MHz(11, IEEE80211_FREQUENCY_BAND_2_4_GHZ) == 81); + + fail_unless(map_get_op_class(100, 20, IEEE80211_FREQUENCY_BAND_5_GHZ) == 121); + fail_unless(map_get_op_class(100, 40, IEEE80211_FREQUENCY_BAND_5_GHZ) == 122); + fail_unless(map_get_op_class(100, 80, IEEE80211_FREQUENCY_BAND_5_GHZ) == 128); + fail_unless(map_get_op_class(100, 160, IEEE80211_FREQUENCY_BAND_5_GHZ) == 129); + + fail_unless(map_get_op_class(165, 20, IEEE80211_FREQUENCY_BAND_5_GHZ) == 125); + + fail_unless(map_get_op_class(101, 20, IEEE80211_FREQUENCY_BAND_5_GHZ) == 0); + + fail_unless(map_get_op_class(101, 20, IEEE80211_FREQUENCY_BAND_6_GHZ) == 131); + fail_unless(map_get_op_class(165, 20, IEEE80211_FREQUENCY_BAND_6_GHZ) == 131); + fail_unless(map_get_op_class(165, 40, IEEE80211_FREQUENCY_BAND_6_GHZ) == 132); + fail_unless(map_get_op_class(165, 80, IEEE80211_FREQUENCY_BAND_6_GHZ) == 133); + fail_unless(map_get_op_class(165, 320, IEEE80211_FREQUENCY_BAND_6_GHZ) == 137); + + fail_unless(map_is_channel_in_op_class(81, 13)); + fail_unless(map_is_channel_in_op_class(128, 106)); + fail_unless(!map_is_channel_in_op_class(128, 100)); + fail_unless(!map_is_channel_in_op_class(134, 95)); + fail_unless(!map_is_channel_in_op_class(134, 97)); + fail_unless(map_is_channel_in_op_class(134, 111)); + fail_unless(map_is_channel_in_op_class(137, 95)); + fail_unless(!map_is_channel_in_op_class(137, 97)); + + fail_unless(map_get_channel_set_from_op_class(81, &ch_set) == 0); + fail_unless(map_cs_nr(&ch_set) == 13); + map_cs_foreach(&ch_set, i) { + fail_unless(i >= 1 && i <= 13); + } + + fail_unless(map_get_channel_set_from_op_class(134, &ch_set) == 0); + fail_unless(map_cs_nr(&ch_set) == 56); + map_cs_foreach(&ch_set, i) { + fail_unless(i >= 1 && i <= 221 && (i % 4) == 1); + } + + fail_unless(map_get_center_channel_set_from_op_class(134, &ch_set) == 0); + fail_unless(map_cs_nr(&ch_set) == 7); + map_cs_foreach(&ch_set, i) { + fail_unless(i >= 15 && i <= 207 && (i % 32) == 15); + } + + for (i = 0; i < ARRAY_SIZE(center_op_classes); i++) { + fail_unless(map_get_is_center_channel_from_op_class(center_op_classes[i], &is_center_chan) == 0); + fail_unless(is_center_chan); + } + + for (i = 0; i < ARRAY_SIZE(not_center_op_classes); i++) { + fail_unless(map_get_is_center_channel_from_op_class(not_center_op_classes[i], &is_center_chan) == 0); + fail_unless(!is_center_chan); + } + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_CHANNELS # +########################################################################*/ +START_TEST(test_channels) +{ + map_channel_set_t ch_set = {0}; + uint8_t c; + int i; + + test_init(); + + fail_unless(map_is_ctl_channel(6, IEEE80211_FREQUENCY_BAND_2_4_GHZ)); + fail_unless(map_is_ctl_channel(14, IEEE80211_FREQUENCY_BAND_2_4_GHZ)); + fail_unless(map_is_ctl_channel(36, IEEE80211_FREQUENCY_BAND_5_GHZ)); + fail_unless(map_is_ctl_channel(177, IEEE80211_FREQUENCY_BAND_5_GHZ)); + fail_unless(map_is_ctl_channel(5, IEEE80211_FREQUENCY_BAND_6_GHZ)); + fail_unless(map_is_ctl_channel(221, IEEE80211_FREQUENCY_BAND_6_GHZ)); + + fail_unless(!map_is_ctl_channel(38, IEEE80211_FREQUENCY_BAND_5_GHZ)); + fail_unless(!map_is_ctl_channel(42, IEEE80211_FREQUENCY_BAND_5_GHZ)); + fail_unless(!map_is_ctl_channel(50, IEEE80211_FREQUENCY_BAND_5_GHZ)); + + fail_unless(map_is_2G_ctl_channel(6)); + fail_unless(!map_is_5G_ctl_channel(6)); + fail_unless(!map_is_6G_ctl_channel(6)); + + fail_unless(!map_is_2G_ctl_channel(36)); + fail_unless(map_is_5G_ctl_channel(36)); + fail_unless(!map_is_6G_ctl_channel(36)); + + fail_unless(!map_is_2G_ctl_channel(221)); + fail_unless(!map_is_5G_ctl_channel(221)); + fail_unless(map_is_6G_ctl_channel(221)); + + + map_get_2G_ctl_channel_set(&ch_set); + for (i = 0; i < 255; i++) { + bool exp = (i >= 1 && i <= 14); + fail_unless(exp == map_cs_is_set(&ch_set, i)); + } + + map_cs_foreach(&ch_set, c) { + fail_unless(map_is_2G_ctl_channel(c)); + } + + + map_get_5G_ctl_channel_set(&ch_set); + for (i = 0; i < 255; i++) { + bool exp = (((i >= 36 && i <= 64) || (i >= 100 && i <= 144)) && (i % 4 == 0)) || + ((i >= 149 && i <= 177) && (i % 4 == 1)); + fail_unless(exp == map_cs_is_set(&ch_set, i)); + } + + map_cs_foreach(&ch_set, c) { + fail_unless(map_is_5G_ctl_channel(c)); + if (c < 100) { + fail_unless(map_is_5G_low_ctl_channel(c)); + } else { + fail_unless(map_is_5G_high_ctl_channel(c)); + } + } + + + map_get_5G_low_ctl_channel_set(&ch_set); + for (i = 0; i < 255; i++) { + bool exp = ((i >= 36 && i <= 64) && (i % 4 == 0)); + fail_unless(exp == map_cs_is_set(&ch_set, i)); + } + + map_cs_foreach(&ch_set, c) { + fail_unless(map_is_5G_ctl_channel(c)); + fail_unless(map_is_5G_low_ctl_channel(c)); + } + + + map_get_5G_high_ctl_channel_set(&ch_set); + for (i = 0; i < 255; i++) { + bool exp = ((i >= 100 && i <= 144) && (i % 4 == 0)) || ((i >= 149 && i <= 177) && (i % 4 == 1)); + fail_unless(exp == map_cs_is_set(&ch_set, i)); + } + + map_cs_foreach(&ch_set, c) { + fail_unless(map_is_5G_ctl_channel(c)); + fail_unless(map_is_5G_high_ctl_channel(c)); + } + + + map_get_5G_weatherband_channel_set(&ch_set); + for (i = 0; i < 255; i++) { + bool exp = (i == 120 || i == 124 || i == 128); + fail_unless(exp == map_cs_is_set(&ch_set, i)); + } + + + map_get_6G_ctl_channel_set(&ch_set); + for (i = 0; i < 255; i++) { + bool exp = (i == 1) || (i == 2) || ((i >= 5 && i <= 233) && (i % 4 == 1)); + fail_unless(exp == map_cs_is_set(&ch_set, i)); + } + + map_cs_foreach(&ch_set, c) { + fail_unless(map_is_6G_ctl_channel(c)); + } + + + map_get_6G_psc_channel_set(&ch_set); + fail_unless(map_cs_nr(&ch_set) == 15); + for (i = 0; i < 255; i++) { + bool exp = (i >= 5) && (i <= 229) && (((i - 5) % 16) == 0); + fail_unless(exp == map_cs_is_set(&ch_set, i)); + } + + + /* Get center channels - returns error when op class does not use center channels */ + fail_unless( map_get_center_channel(81, 1, &c)); /* no center */ + fail_unless( map_get_center_channel(126, 149, &c)); /* no center */ + fail_unless( map_get_center_channel(128, 101, &c)); /* center - invalid channel */ + fail_unless( map_get_center_channel(137, 76, &c)); /* center - invalid channel */ + fail_unless(!map_get_center_channel(128, 100, &c) && c == 106); /* center */ + fail_unless(!map_get_center_channel(129, 40, &c) && c == 50); /* center */ + fail_unless(!map_get_center_channel(132, 9, &c) && c == 11); /* center */ + fail_unless(!map_get_center_channel(133, 9, &c) && c == 7); /* center */ + fail_unless(!map_get_center_channel(134, 9, &c) && c == 15); /* center */ + fail_unless(!map_get_center_channel(132, 77, &c) && c == 75); /* center */ + fail_unless(!map_get_center_channel(133, 77, &c) && c == 71); /* center */ + fail_unless(!map_get_center_channel(134, 77, &c) && c == 79); /* center */ + fail_unless(!map_get_center_channel(137, 77, &c) && c == 95); /* center */ + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_BW # +########################################################################*/ +START_TEST(test_bw) +{ + uint16_t bw; + + test_init(); + + fail_unless(!map_get_bw_from_op_class(81, &bw) && bw == 20); + fail_unless(!map_get_bw_from_op_class(126, &bw) && bw == 40); + fail_unless(!map_get_bw_from_op_class(128, &bw) && bw == 80); + fail_unless(!map_get_bw_from_op_class(128, &bw) && bw == 80); + fail_unless(!map_get_bw_from_op_class(129, &bw) && bw == 160); + fail_unless(!map_get_bw_from_op_class(131, &bw) && bw == 20); + fail_unless(!map_get_bw_from_op_class(132, &bw) && bw == 40); + fail_unless(!map_get_bw_from_op_class(133, &bw) && bw == 80); + fail_unless(!map_get_bw_from_op_class(134, &bw) && bw == 160); + fail_unless(!map_get_bw_from_op_class(137, &bw) && bw == 320); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_SUBBAND_RANGE # +########################################################################*/ +START_TEST(test_subband_range) +{ + uint8_t from, to; + + test_init(); + + fail_unless(0 == map_get_subband_channel_range(81, 1, &from, &to) && from == 1 && to == 1); + fail_unless(0 == map_get_subband_channel_range(115, 36, &from, &to) && from == 36 && to == 36); + fail_unless(0 == map_get_subband_channel_range(126, 149, &from, &to) && from == 149 && to == 153); + fail_unless(0 == map_get_subband_channel_range(131, 17, &from, &to) && from == 17 && to == 17); + fail_unless(0 == map_get_subband_channel_range(128, 40, &from, &to) && from == 36 && to == 48); + fail_unless(0 == map_get_subband_channel_range(128, 149, &from, &to) && from == 149 && to == 161); + fail_unless(0 == map_get_subband_channel_range(129, 52, &from, &to) && from == 36 && to == 64); + fail_unless(0 == map_get_subband_channel_range(129, 100, &from, &to) && from == 100 && to == 128); + fail_unless(0 == map_get_subband_channel_range(129, 153, &from, &to) && from == 149 && to == 177); + fail_unless(0 == map_get_subband_channel_range(132, 17, &from, &to) && from == 17 && to == 21); + fail_unless(0 == map_get_subband_channel_range(133, 17, &from, &to) && from == 17 && to == 29); + fail_unless(0 == map_get_subband_channel_range(134, 17, &from, &to) && from == 1 && to == 29); + fail_unless(0 == map_get_subband_channel_range(137, 17, &from, &to) && from == 1 && to == 61); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_INVALID_OP_CLASS # +########################################################################*/ +START_TEST(test_invalid_op_class) +{ + map_channel_set_t ch_set; + uint16_t dummy_16; + uint8_t dummy_8; + bool dummy_b; + + test_init(); + map_cs_unset_all(&ch_set); + + /* In order of appearance in map_info.h */ + fail_unless(map_get_frequency_type(40, &ch_set, &dummy_8, &dummy_16)); + fail_unless(!map_is_5g_low_op_class(111)); + fail_unless(!map_is_5g_high_op_class(111)); + fail_unless(!map_is_channel_in_op_class(250, 100)); + fail_unless(map_get_center_channel(40, 100, &dummy_8)); + fail_unless(map_get_ext_channel_type(250) == MAP_EXT_CHANNEL_NONE); + fail_unless(map_get_subband_channel_range(40, 100, &dummy_8, &dummy_8)); + fail_unless(map_get_bw_from_op_class(111, &dummy_16)); + fail_unless(map_get_band_from_op_class(250, &dummy_8)); + fail_unless(map_get_is_center_channel_from_op_class(40, &dummy_b)); + fail_unless(map_get_channel_set_from_op_class(111, &ch_set)); + fail_unless(map_get_center_channel_set_from_op_class(250, &ch_set)); + + test_fini(); +} +END_TEST + +/*####################################################################### +# TEST_6G_320MHz # +########################################################################*/ +START_TEST(test_6G_320MHz) +{ + uint8_t c; + uint8_t center_channel; + uint8_t from, to; + int ret; + + fail_unless(map_is_6G_320MHz_op_class(137)); + fail_unless(!map_is_6G_320MHz_op_class(134)); + + /* map_get_center_channel_6G_320MHz */ + for (c = 0; c < 255; c++) { + ret = map_get_center_channel_6G_320MHz(137, false, c, ¢er_channel); + if (c % 4 != 1 || c < 1 || c > 189) { + fail_unless(ret); + } else { + fail_unless(!ret); + fail_unless(center_channel == ((c / 64) * 64) + 31); + } + + ret = map_get_center_channel_6G_320MHz(137, true, c, ¢er_channel); + if (c % 4 != 1 || c < 33 || c > 221) { + fail_unless(ret); + } else { + fail_unless(!ret); + fail_unless(center_channel == (((c - 32) / 64) * 64) + 63); + } + + } + + /* map_get_subband_channel_range_6G_320MHz */ + for (c = 0; c < 255; c++) { + ret = map_get_subband_channel_range_6G_320MHz(137, false, c, &from, &to); + if (c % 4 != 1 || c < 1 || c > 189) { + fail_unless(ret); + } else { + fail_unless(!ret); + center_channel = ((c / 64) * 64) + 31; + fail_unless(from == center_channel - 30 && to == center_channel + 30); + } + + ret = map_get_subband_channel_range_6G_320MHz(137, true, c, &from, &to); + if (c % 4 != 1 || c < 33 || c > 221) { + fail_unless(ret); + } else { + fail_unless(!ret); + center_channel = (((c - 32) / 64) * 64) + 63; + fail_unless(from == center_channel - 30 && to == center_channel + 30); + } + } +} +END_TEST + +const char *test_suite_name = "map_info"; +test_case_t test_cases[] = { + TEST("op_class", test_op_class ), + TEST("channels", test_channels ), + TEST("bw", test_bw ), + TEST("subband_range", test_subband_range ), + TEST("invalid_op_class", test_invalid_op_class ), + TEST("6G_320MHz", test_6G_320MHz ), + TEST_CASES_END +}; diff --git a/source/test/project/CMakeLists.txt b/source/test/project/CMakeLists.txt new file mode 100644 index 0000000..97d0cf8 --- /dev/null +++ b/source/test/project/CMakeLists.txt @@ -0,0 +1,79 @@ +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +cmake_minimum_required(VERSION 2.8.12) + +project(emctl_test_project) + +option(BUILD_DEPS "Build dependencies" OFF) +set(CJSON_SRC_URL "https://github.com/DaveGamble/cJSON.git" CACHE STRING "Default cJSON repository") +set(CJSON_REVISION "v1.7.12" CACHE STRING "Default cJSON revision") +set(JSONC_SRC_URL "https://github.com/json-c/json-c.git" CACHE STRING "Default json-c repository") +set(JSONC_REVISION "json-c-0.15-20200726" CACHE STRING "Default json-c revision") +set(LIBUBOX_SRC_URL "https://github.com/openwrt/libubox.git" CACHE STRING "Default libubox repository") +set(LIBUBOX_REVISION "e85cb739766d00977a08cce98f161976da5fbefc" CACHE STRING "Default libubox revision") +set(UTHASH_SRC_URL "https://github.com/troydhanson/uthash.git" CACHE STRING "Default uthash repository") +set(UTHASH_REVISION "v2.3.0" CACHE STRING "Default uthash revision") + +include(ExternalProject) + +set(INSTALL_DIR ${PROJECT_BINARY_DIR}/install) + +ExternalProject_Add( + cJSON + PREFIX cJSON + DOWNLOAD_COMMAND git clone ${CJSON_SRC_URL} cJSON && git -C cJSON checkout ${CJSON_REVISION} + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} + -DENABLE_CJSON_TEST=OFF + BUILD_COMMAND make +) + +ExternalProject_Add( + json-c + PREFIX json-c + DOWNLOAD_COMMAND git clone ${JSONC_SRC_URL} json-c && git -C json-c checkout ${JSONC_REVISION} + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} +) + +ExternalProject_Add( + libubox + PREFIX libubox + DEPENDS json-c + DOWNLOAD_COMMAND git clone ${LIBUBOX_SRC_URL} libubox && git -C libubox checkout ${LIBUBOX_REVISION} + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} + -DJSONC_FOUND=1 + -DJSONC_INCLUDE_DIRS=${INSTALL_DIR}/include/json-c + -DBUILD_LUA=OFF + -DBUILD_EXAMPLES=OFF + -DUNIT_TESTING=OFF + BUILD_COMMAND make +) + +ExternalProject_Add( + uthash + PREFIX uthash + DOWNLOAD_COMMAND git clone ${UTHASH_SRC_URL} uthash && git -C uthash checkout ${UTHASH_REVISION} + CONFIGURE_COMMAND "" + BUILD_IN_SOURCE TRUE + BUILD_COMMAND ln -sf include uthash + INSTALL_COMMAND mkdir -p ${INSTALL_DIR}/include && cp -rfH uthash ${INSTALL_DIR}/include +) + +set(TESTS_DEPENDS libubox cJSON json-c) + +ExternalProject_Add( + tests + PREFIX tests + DEPENDS ${TESTS_DEPENDS} + SOURCE_DIR ${PROJECT_SOURCE_DIR}/.. + CMAKE_ARGS + -DCMAKE_FIND_ROOT_PATH=${INSTALL_DIR} -DSCHEMA_PATH=${INSTALL_DIR}/schemas + BUILD_COMMAND make VERBOSE=1 + INSTALL_COMMAND "" +) diff --git a/source/test/run.sh b/source/test/run.sh new file mode 100755 index 0000000..0280ac0 --- /dev/null +++ b/source/test/run.sh @@ -0,0 +1,109 @@ +#!/bin/sh + +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +set -e + +print_help_and_exit() +{ + echo "Usage: $0" + echo " or $0 -h" + echo " or $0 [-c] unit[=]|unitcov[=]|unitmem[=]" + echo "" + echo " If no arguments given recompile and run tests" + echo "" + echo " -h Show this help text." + echo " -c Don't rebuild before running." + echo " unit[=] Run all or specified unit test." + echo " is the test_.c filename in ./test/unit/" + echo " unitcov[=] Run all or specified unit tests with code coverage." + echo " Results appear in ./build/agent/src/agent-build/coverage_report*" + echo " unitmem[=] Run all or specified unit tests under Valgrind." + echo " Results are also saved in ./build/agent/src/agent-build/test/unit/valgrind-*.xml" + echo " validate_schema Validate all generated json message." + echo " log_level=level Set log level (error, warning, info, debug)" + + exit ${1:-0} +} + +# Default +UNITTARGET=test + +while [ $# -gt 0 ] +do + case $1 in + -c) + COMPILE_SKIP="YES" + ;; + -h) + print_help_and_exit + ;; + unitcov*) + UNITTEST="${1#unitcov=}" + if [ "$UNITTEST" != "unitcov" ]; then + UNITTARGET="unittest_${UNITTEST}_coverage" + else + UNITTARGET="test_coverage" + unset UNITTEST + fi + ;; + unitmem*) + UNITTEST="${1#unitmem=}" + if [ "$UNITTEST" != "unitmem" ]; then + UNITTARGET="unittest_${UNITTEST}_memcheck" + else + UNITTARGET="test" + UNITEXTRA="-D ExperimentalMemCheck" + unset UNITTEST + fi + ;; + unit*) + UNITTARGET="test" + UNITTEST="${1#unit=}" + if [ "$UNITTEST" != "unit" ]; then + UNITTEST="_$UNITTEST" + else + unset UNITTEST + fi + ;; + validate_schema) + export UNITTEST_VALIDATE_SCHEMA=1 + ;; + log_level*) + export UNITTEST_LOG_LEVEL="${1#log_level=}" + ;; + *) + echo "$1: unknown option" + print_help_and_exit 1 + ;; + esac + shift +done + +BUILD_PATH=project/build/tests/src/tests-build + +if [ -z "$COMPILE_SKIP" ]; then + make -C $BUILD_PATH VERBOSE=1 +fi + +# Remove old valgrind xml files +find $BUILD_PATH -type f -name valgrind*.xml -exec rm {} \; + +make -C $BUILD_PATH $UNITTARGET ARGS="--timeout 180 -R unittest$UNITTEST -V ${UNITEXTRA}" + +# Check valgrind xml files for errors +for XML in `find $BUILD_PATH -type f -name valgrind*.xml`; do + ERRORS=`sed -n '//,/<\/error>/p' $XML | wc -l` + + if [ "$ERRORS" != "0" ]; then + EXE=`sed -n '//,/<\/args>/p' $XML | sed -n '//,/<\/argv>/p' | sed -n '//,/<\/exe>/p' | sed -e 's/.*\(.*\)<\/exe>.*/\1/'` + TEST=`basename $EXE | sed 's/unittest_*//'` + echo + echo "WARNING: Valgrind found errors when running test $TEST" + echo " Check with ./run.sh unitmem=$TEST" + fi +done diff --git a/source/test/setup.sh b/source/test/setup.sh new file mode 100755 index 0000000..3670b24 --- /dev/null +++ b/source/test/setup.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +########################################################################## +# Copyright (c) 2019-2024 AirTies Wireless Networks +# +# Licensed under the BSD+Patent License. +########################################################################## + +mkdir -p ./project/build +cd project/build && cmake .. && make VERBOSE=1 && cd .. diff --git a/source/test/suppressions.vg b/source/test/suppressions.vg new file mode 100644 index 0000000..e4ed8ca --- /dev/null +++ b/source/test/suppressions.vg @@ -0,0 +1,67 @@ +{ + OpenSSL compression methods initialization + Memcheck:Leak + fun:malloc + fun:CRYPTO_malloc + ... + fun:SSL_COMP_get_compression_methods +} +{ + g++ leak + Memcheck:Leak + fun:malloc + ... + fun:call_init.part.0 +} +{ + Check framework leaks (emalloc) + Memcheck:Leak + fun:malloc + fun:emalloc + ... + fun:main +} +{ + Check framework leaks (erealloc) + Memcheck:Leak + fun:realloc + fun:erealloc + ... + fun:main +} +{ + Check framework leaks (receiving test results) + Memcheck:Leak + fun:malloc + ... + fun:receive_test_result + fun:srunner_run + fun:main +} +{ + Check framework leaks (init logging) + Memcheck:Leak + fun:malloc + ... + fun:srunner_init_logging + fun:srunner_run + fun:main +} +{ + Check framework leaks (pass msg) + Memcheck:Leak + fun:malloc + ... + fun:pass_msg + fun:srunner_run + fun:main +} +{ + Check framework leaks (setup pipe) + Memcheck:Leak + fun:malloc + ... + fun:setup_pipe + fun:srunner_run + fun:main +}