mirror of https://github.com/ARMmbed/mbed-os.git
1920 lines
69 KiB
C
1920 lines
69 KiB
C
/*
|
|
* Copyright (c) 2018-2019, Arm Limited and affiliates.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "nsconfig.h"
|
|
#include <string.h>
|
|
#include "ns_types.h"
|
|
#include "ns_list.h"
|
|
#include "ns_trace.h"
|
|
#include "nsdynmemLIB.h"
|
|
#include "mac_common_defines.h"
|
|
#include "mac_api.h"
|
|
#include "mac_mcps.h"
|
|
#include "common_functions.h"
|
|
#include "NWK_INTERFACE/Include/protocol.h"
|
|
#include "6LoWPAN/MAC/mac_helper.h"
|
|
#include "6LoWPAN/MAC/mpx_api.h"
|
|
#include "6LoWPAN/MAC/mac_ie_lib.h"
|
|
#include "6LoWPAN/ws/ws_common_defines.h"
|
|
#include "6LoWPAN/ws/ws_common.h"
|
|
#include "6LoWPAN/ws/ws_bootstrap.h"
|
|
#include "6LoWPAN/ws/ws_ie_lib.h"
|
|
#include "6LoWPAN/ws/ws_llc.h"
|
|
#include "6LoWPAN/ws/ws_mpx_header.h"
|
|
#include "6LoWPAN/ws/ws_pae_controller.h"
|
|
#include "6LoWPAN/ws/ws_cfg_settings.h"
|
|
#include "Security/PANA/pana_eap_header.h"
|
|
#include "Security/eapol/eapol_helper.h"
|
|
#include "Service_Libs/etx/etx.h"
|
|
#include "fhss_ws_extension.h"
|
|
|
|
#ifdef HAVE_WS
|
|
|
|
#define TRACE_GROUP "wllc"
|
|
|
|
#define LLC_MESSAGE_QUEUE_LIST_SIZE_MAX 16 //Do not config over 30 never
|
|
#define MPX_USER_SIZE 2
|
|
|
|
typedef struct {
|
|
uint16_t user_id; /**< User ID for identify MPX User */
|
|
mpx_data_confirm *data_confirm; /**< User registred MPX Data confirmation call back */
|
|
mpx_data_indication *data_ind; /**< User registred MPX Data indication call back */
|
|
} mpx_user_t;
|
|
|
|
|
|
typedef struct {
|
|
mpx_api_t mpx_api; /**< API for MPX user like Stack and EAPOL */
|
|
mpx_user_t mpx_user_table[MPX_USER_SIZE]; /**< MPX user list include registered call back pointers and user id's */
|
|
unsigned mpx_id: 4; /**< MPX class sequence number */
|
|
} mpx_class_t;
|
|
|
|
|
|
typedef struct {
|
|
uint16_t supported_channels; /**< Configured Channel count. This will define Channel infor mask length to some information element */
|
|
uint16_t network_name_length; /**< Network name length */
|
|
uint16_t vendor_payload_length; /**< Vendor spesific payload length */
|
|
uint8_t vendor_header_length; /**< Vendor spesific header length */
|
|
uint8_t gtkhash_length; /**< GTK hash length */
|
|
ws_pan_information_t *pan_congiguration; /**< Pan configururation */
|
|
struct ws_hopping_schedule_s *hopping_schedule;/**< Channel hopping schedule */
|
|
uint8_t *gtkhash; /**< Pointer to GTK HASH user must give pointer which include 4 64-bit HASH array */
|
|
uint8_t *network_name; /**< Network name */
|
|
uint8_t *vendor_header_data; /**< Vendor spesific header data */
|
|
uint8_t *vendor_payload; /**< Vendor spesific payload data */
|
|
} llc_ie_params_t;
|
|
|
|
typedef struct {
|
|
uint8_t dst_address[8]; /**< Destination address */
|
|
uint16_t pan_id; /**< Destination Pan-Id */
|
|
unsigned messsage_type: 3; /**< Frame type to UTT */
|
|
unsigned mpx_id: 5; /**< MPX sequence */
|
|
bool ack_requested: 1; /**< ACK requested */
|
|
bool eapol_temporary: 1; /**< EAPOL TX entry index used */
|
|
unsigned dst_address_type: 2; /**< Destination address type */
|
|
unsigned src_address_type: 2; /**< Source address type */
|
|
uint8_t msg_handle; /**< LLC genetaed unique MAC handle */
|
|
uint8_t mpx_user_handle; /**< This MPX user defined handle */
|
|
ns_ie_iovec_t ie_vector_list[3]; /**< IE vectors: 1 for Header's, 1 for Payload and for MPX payload */
|
|
mcps_data_req_ie_list_t ie_ext;
|
|
ns_list_link_t link; /**< List link entry */
|
|
uint8_t ie_buffer[]; /**< Trailing buffer data */
|
|
} llc_message_t;
|
|
|
|
/** get pointer to Mac header start point*/
|
|
#define ws_message_buffer_ptr_get(x) (&(x)->ie_buffer[0])
|
|
|
|
typedef NS_LIST_HEAD(llc_message_t, link) llc_message_list_t;
|
|
|
|
#define MAX_NEIGH_TEMPORARY_MULTICAST_SIZE 5
|
|
#define MAX_NEIGH_TEMPORRY_EAPOL_SIZE 20
|
|
#define MAX_NEIGH_TEMPORAY_LIST_SIZE (MAX_NEIGH_TEMPORARY_MULTICAST_SIZE + MAX_NEIGH_TEMPORRY_EAPOL_SIZE)
|
|
|
|
typedef struct {
|
|
ws_neighbor_temp_class_t neighbour_temporary_table[MAX_NEIGH_TEMPORAY_LIST_SIZE];
|
|
ws_neighbor_temp_list_t active_multicast_temp_neigh;
|
|
ws_neighbor_temp_list_t active_eapol_temp_neigh;
|
|
ws_neighbor_temp_list_t free_temp_neigh;
|
|
llc_message_list_t llc_eap_pending_list; /**< Active Message list */
|
|
bool active_eapol_session: 1; /**< Indicating active EAPOL message */
|
|
} temp_entriest_t;
|
|
|
|
/** EDFE response and Enhanced ACK data length */
|
|
|
|
#define ENHANCED_FRAME_RESPONSE (WH_IE_ELEMENT_HEADER_LENGTH + 2 + WH_IE_ELEMENT_HEADER_LENGTH + 4 + WH_IE_ELEMENT_HEADER_LENGTH + 1 + WH_IE_ELEMENT_HEADER_LENGTH + 5)
|
|
|
|
typedef struct {
|
|
uint8_t mac_handle_base; /**< Mac handle id base this will be updated by 1 after use */
|
|
uint8_t llc_message_list_size; /**< llc_message_list list size */
|
|
uint16_t edfe_rx_wait_timer;
|
|
mpx_class_t mpx_data_base; /**< MPX data be including USER API Class and user call backs */
|
|
|
|
llc_message_list_t llc_message_list; /**< Active Message list */
|
|
llc_ie_params_t ie_params; /**< LLC IE header and Payload data configuration */
|
|
temp_entriest_t *temp_entries;
|
|
|
|
ws_asynch_ind *asynch_ind; /**< LLC Asynch data indication call back configured by user */
|
|
ws_asynch_confirm *asynch_confirm; /**< LLC Asynch data confirmation call back configured by user */
|
|
ws_neighbor_info_request *ws_neighbor_info_request_cb; /**< LLC Neighbour discover API*/
|
|
uint8_t ws_enhanced_response_elements[ENHANCED_FRAME_RESPONSE];
|
|
ns_ie_iovec_t ws_header_vector;
|
|
protocol_interface_info_entry_t *interface_ptr; /**< List link entry */
|
|
|
|
ns_list_link_t link; /**< List link entry */
|
|
} llc_data_base_t;
|
|
|
|
static NS_LIST_DEFINE(llc_data_base_list, llc_data_base_t, link);
|
|
|
|
static uint16_t ws_wp_nested_message_length(wp_nested_ie_sub_list_t requested_list, llc_ie_params_t *params);
|
|
static uint16_t ws_wh_headers_length(wh_ie_sub_list_t requested_list, llc_ie_params_t *params);
|
|
|
|
/** LLC message local functions */
|
|
static llc_message_t *llc_message_discover_by_mac_handle(uint8_t handle, llc_message_list_t *list);
|
|
static llc_message_t *llc_message_discover_by_mpx_id(uint8_t handle, llc_message_list_t *list);
|
|
static llc_message_t *llc_message_discover_mpx_user_id(uint8_t handle, uint16_t user_id, llc_message_list_t *list);
|
|
static void llc_message_free(llc_message_t *message, llc_data_base_t *llc_base);
|
|
static void llc_message_id_allocate(llc_message_t *message, llc_data_base_t *llc_base, bool mpx_user);
|
|
static llc_message_t *llc_message_allocate(uint16_t ie_buffer_size, llc_data_base_t *llc_base);
|
|
|
|
/** LLC interface sepesific local functions */
|
|
static llc_data_base_t *ws_llc_discover_by_interface(struct protocol_interface_info_entry *interface);
|
|
static llc_data_base_t *ws_llc_discover_by_mac(const mac_api_t *api);
|
|
static llc_data_base_t *ws_llc_discover_by_mpx(const mpx_api_t *api);
|
|
|
|
static mpx_user_t *ws_llc_mpx_user_discover(mpx_class_t *mpx_class, uint16_t user_id);
|
|
static llc_data_base_t *ws_llc_base_allocate(void);
|
|
static void ws_llc_mac_confirm_cb(const mac_api_t *api, const mcps_data_conf_t *data, const mcps_data_conf_payload_t *conf_data);
|
|
static void ws_llc_mac_indication_cb(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext);
|
|
static uint16_t ws_mpx_header_size_get(llc_data_base_t *base, uint16_t user_id);
|
|
static void ws_llc_mpx_data_request(const mpx_api_t *api, const struct mcps_data_req_s *data, uint16_t user_id);
|
|
static int8_t ws_llc_mpx_data_cb_register(const mpx_api_t *api, mpx_data_confirm *confirm_cb, mpx_data_indication *indication_cb, uint16_t user_id);
|
|
static uint16_t ws_llc_mpx_header_size_get(const mpx_api_t *api, uint16_t user_id);
|
|
static uint8_t ws_llc_mpx_data_purge_request(const mpx_api_t *api, struct mcps_purge_s *purge, uint16_t user_id);
|
|
static void ws_llc_mpx_init(mpx_class_t *mpx_class);
|
|
|
|
static void ws_llc_temp_neigh_info_table_reset(temp_entriest_t *base);
|
|
static ws_neighbor_temp_class_t *ws_allocate_multicast_temp_entry(temp_entriest_t *base, const uint8_t *mac64);
|
|
static ws_neighbor_temp_class_t *ws_llc_discover_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64);
|
|
static void ws_llc_release_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64);
|
|
static ws_neighbor_temp_class_t *ws_allocate_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64);
|
|
|
|
static void ws_llc_mpx_eapol_send(llc_data_base_t *base, llc_message_t *message);
|
|
|
|
static bool test_skip_first_init_response = false;
|
|
static uint8_t test_drop_data_message = 0;
|
|
|
|
|
|
int8_t ws_test_skip_edfe_data_send(int8_t interface_id, bool skip)
|
|
{
|
|
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
if (!cur || !ws_info(cur)) {
|
|
return -1;
|
|
}
|
|
test_skip_first_init_response = skip;
|
|
return 0;
|
|
}
|
|
|
|
int8_t ws_test_drop_edfe_data_frames(int8_t interface_id, uint8_t number_of_dropped_frames)
|
|
{
|
|
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
|
|
if (!cur || !ws_info(cur)) {
|
|
return -1;
|
|
}
|
|
test_drop_data_message = number_of_dropped_frames;
|
|
return 0;
|
|
}
|
|
|
|
/** Discover Message by message handle id */
|
|
static llc_message_t *llc_message_discover_by_mac_handle(uint8_t handle, llc_message_list_t *list)
|
|
{
|
|
ns_list_foreach(llc_message_t, message, list) {
|
|
if (message->msg_handle == handle) {
|
|
return message;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static llc_message_t *llc_message_discover_by_mpx_id(uint8_t handle, llc_message_list_t *list)
|
|
{
|
|
ns_list_foreach(llc_message_t, message, list) {
|
|
if ((message->messsage_type == WS_FT_DATA || message->messsage_type == WS_FT_EAPOL) && message->mpx_id == handle) {
|
|
return message;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static llc_message_t *llc_message_discover_mpx_user_id(uint8_t handle, uint16_t user_id, llc_message_list_t *list)
|
|
{
|
|
uint8_t message_type;
|
|
if (user_id == MPX_LOWPAN_ENC_USER_ID) {
|
|
message_type = WS_FT_DATA;
|
|
} else {
|
|
message_type = WS_FT_EAPOL;
|
|
}
|
|
|
|
ns_list_foreach(llc_message_t, message, list) {
|
|
if (message->messsage_type == message_type && message->mpx_user_handle == handle) {
|
|
return message;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//Free message and delete from list
|
|
static void llc_message_free(llc_message_t *message, llc_data_base_t *llc_base)
|
|
{
|
|
ns_list_remove(&llc_base->llc_message_list, message);
|
|
ns_dyn_mem_free(message);
|
|
llc_base->llc_message_list_size--;
|
|
}
|
|
|
|
static void llc_message_id_allocate(llc_message_t *message, llc_data_base_t *llc_base, bool mpx_user)
|
|
{
|
|
//Guarantee
|
|
while (1) {
|
|
if (llc_message_discover_by_mac_handle(llc_base->mac_handle_base, &llc_base->llc_message_list)) {
|
|
llc_base->mac_handle_base++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (mpx_user) {
|
|
while (1) {
|
|
if (llc_message_discover_by_mpx_id(llc_base->mpx_data_base.mpx_id, &llc_base->llc_message_list)) {
|
|
llc_base->mpx_data_base.mpx_id++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Storage handle and update base
|
|
message->msg_handle = llc_base->mac_handle_base++;
|
|
if (mpx_user) {
|
|
message->mpx_id = llc_base->mpx_data_base.mpx_id++;
|
|
}
|
|
}
|
|
|
|
static llc_message_t *llc_message_allocate(uint16_t ie_buffer_size, llc_data_base_t *llc_base)
|
|
{
|
|
if (llc_base->llc_message_list_size >= LLC_MESSAGE_QUEUE_LIST_SIZE_MAX) {
|
|
return NULL;
|
|
}
|
|
|
|
llc_message_t *message = ns_dyn_mem_temporary_alloc(sizeof(llc_message_t) + ie_buffer_size);
|
|
if (!message) {
|
|
return NULL;
|
|
}
|
|
message->ack_requested = false;
|
|
message->eapol_temporary = false;
|
|
return message;
|
|
}
|
|
|
|
static llc_data_base_t *ws_llc_discover_by_interface(struct protocol_interface_info_entry *interface)
|
|
{
|
|
ns_list_foreach(llc_data_base_t, base, &llc_data_base_list) {
|
|
if (base->interface_ptr == interface) {
|
|
return base;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static llc_data_base_t *ws_llc_discover_by_mac(const mac_api_t *api)
|
|
{
|
|
ns_list_foreach(llc_data_base_t, base, &llc_data_base_list) {
|
|
if (base->interface_ptr->mac_api == api) {
|
|
return base;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static llc_data_base_t *ws_llc_discover_by_mpx(const mpx_api_t *api)
|
|
{
|
|
ns_list_foreach(llc_data_base_t, base, &llc_data_base_list) {
|
|
if (&base->mpx_data_base.mpx_api == api) {
|
|
return base;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static uint16_t ws_wh_headers_length(wh_ie_sub_list_t requested_list, llc_ie_params_t *params)
|
|
{
|
|
uint16_t length = 0;
|
|
if (requested_list.utt_ie) {
|
|
//Static 4 bytes allways UTT
|
|
length += WH_IE_ELEMENT_HEADER_LENGTH + 4;
|
|
}
|
|
|
|
if (requested_list.bt_ie) {
|
|
//Static 5 bytes allways
|
|
length += WH_IE_ELEMENT_HEADER_LENGTH + 5;
|
|
}
|
|
|
|
if (requested_list.fc_ie) {
|
|
//Static 1 bytes allways
|
|
length += WH_IE_ELEMENT_HEADER_LENGTH + 2;
|
|
}
|
|
|
|
if (requested_list.rsl_ie) {
|
|
//Static 1 bytes allways
|
|
length += WH_IE_ELEMENT_HEADER_LENGTH + 1;
|
|
}
|
|
|
|
if (requested_list.vh_ie) {
|
|
//Dynamic length
|
|
length += WH_IE_ELEMENT_HEADER_LENGTH + params->vendor_header_length;
|
|
}
|
|
|
|
if (requested_list.ea_ie) {
|
|
length += WH_IE_ELEMENT_HEADER_LENGTH + 8;
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
static uint16_t ws_wp_nested_message_length(wp_nested_ie_sub_list_t requested_list, llc_ie_params_t *params)
|
|
{
|
|
uint16_t length = 0;
|
|
if (requested_list.gtkhash_ie) {
|
|
//Static 32 bytes allways
|
|
length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + params->gtkhash_length;
|
|
}
|
|
|
|
if (requested_list.net_name_ie) {
|
|
//Dynamic length
|
|
length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + params->network_name_length;
|
|
}
|
|
|
|
if (requested_list.vp_ie && params->vendor_payload_length) {
|
|
//Dynamic length
|
|
length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + params->vendor_payload_length;
|
|
}
|
|
|
|
if (requested_list.pan_ie) {
|
|
//Static 5 bytes allways
|
|
length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH;
|
|
if (params->pan_congiguration) {
|
|
length += 5;
|
|
}
|
|
}
|
|
|
|
if (requested_list.pan_version_ie) {
|
|
//Static 2 bytes allways
|
|
length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH;
|
|
if (params->pan_congiguration) {
|
|
length += 2;
|
|
}
|
|
}
|
|
|
|
if (requested_list.bs_ie) {
|
|
///Dynamic length
|
|
length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(params->hopping_schedule, false);
|
|
}
|
|
|
|
if (requested_list.us_ie) {
|
|
//Dynamic length
|
|
length += WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(params->hopping_schedule, true);
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
static mpx_user_t *ws_llc_mpx_user_discover(mpx_class_t *mpx_class, uint16_t user_id)
|
|
{
|
|
for (int i = 0; i < MPX_USER_SIZE; i++) {
|
|
if (mpx_class->mpx_user_table[i].user_id == user_id) {
|
|
return &mpx_class->mpx_user_table[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static llc_data_base_t *ws_llc_base_allocate(void)
|
|
{
|
|
llc_data_base_t *base = ns_dyn_mem_alloc(sizeof(llc_data_base_t));
|
|
temp_entriest_t *temp_entries = ns_dyn_mem_alloc(sizeof(temp_entriest_t));
|
|
if (!base || !temp_entries) {
|
|
ns_dyn_mem_free(base);
|
|
ns_dyn_mem_free(temp_entries);
|
|
return NULL;
|
|
}
|
|
memset(base, 0, sizeof(llc_data_base_t));
|
|
memset(temp_entries, 0, sizeof(temp_entriest_t));
|
|
ns_list_init(&temp_entries->active_multicast_temp_neigh);
|
|
ns_list_init(&temp_entries->active_eapol_temp_neigh);
|
|
ns_list_init(&temp_entries->free_temp_neigh);
|
|
ns_list_init(&temp_entries->llc_eap_pending_list);
|
|
base->temp_entries = temp_entries;
|
|
|
|
ns_list_init(&base->llc_message_list);
|
|
|
|
ns_list_add_to_end(&llc_data_base_list, base);
|
|
return base;
|
|
}
|
|
|
|
/** WS LLC MAC data extension confirmation */
|
|
static void ws_llc_mac_confirm_cb(const mac_api_t *api, const mcps_data_conf_t *data, const mcps_data_conf_payload_t *conf_data)
|
|
{
|
|
(void) conf_data;
|
|
llc_data_base_t *base = ws_llc_discover_by_mac(api);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
|
|
protocol_interface_info_entry_t *interface = base->interface_ptr;
|
|
llc_message_t *message = llc_message_discover_by_mac_handle(data->msduHandle, &base->llc_message_list);
|
|
if (!message) {
|
|
return;
|
|
}
|
|
|
|
uint8_t messsage_type = message->messsage_type;
|
|
uint8_t mpx_user_handle = message->mpx_user_handle;
|
|
if (message->eapol_temporary) {
|
|
//Clear
|
|
ws_bootstrap_eapol_tx_temporary_clear(interface);
|
|
|
|
if (data->status == MLME_SUCCESS || data->status == MLME_NO_DATA) {
|
|
//Update timeout
|
|
ws_neighbor_temp_class_t *temp_entry = ws_llc_discover_eapol_temp_entry(base->temp_entries, message->dst_address);
|
|
if (temp_entry) {
|
|
//Update Temporary Lifetime
|
|
temp_entry->eapol_temp_info.eapol_timeout = interface->ws_info->cfg->timing.temp_eapol_min_timeout + 1;
|
|
}
|
|
}
|
|
}
|
|
//ETX update
|
|
llc_neighbour_req_t neighbor_info;
|
|
neighbor_info.ws_neighbor = NULL;
|
|
neighbor_info.neighbor = NULL;
|
|
if (message->ack_requested && messsage_type == WS_FT_DATA) {
|
|
|
|
bool success = false;
|
|
|
|
if (message->dst_address_type == MAC_ADDR_MODE_64_BIT) {
|
|
base->ws_neighbor_info_request_cb(interface, message->dst_address, &neighbor_info, false);
|
|
}
|
|
switch (data->status) {
|
|
case MLME_SUCCESS:
|
|
case MLME_TX_NO_ACK:
|
|
case MLME_NO_DATA:
|
|
if (data->status == MLME_SUCCESS || data->status == MLME_NO_DATA) {
|
|
success = true;
|
|
}
|
|
|
|
if (neighbor_info.ws_neighbor && neighbor_info.neighbor && neighbor_info.neighbor->link_lifetime == WS_NEIGHBOR_LINK_TIMEOUT) {
|
|
etx_transm_attempts_update(interface->id, 1 + data->tx_retries, success, neighbor_info.neighbor->index, neighbor_info.neighbor->mac64);
|
|
//TODO discover RSL from Enchanced ACK Header IE elements
|
|
ws_utt_ie_t ws_utt;
|
|
if (ws_wh_utt_read(conf_data->headerIeList, conf_data->headerIeListLength, &ws_utt)) {
|
|
//UTT header
|
|
if (success) {
|
|
neighbor_info.neighbor->lifetime = neighbor_info.neighbor->link_lifetime;
|
|
}
|
|
|
|
ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, &ws_utt, data->timestamp, neighbor_info.neighbor->mac64);
|
|
}
|
|
|
|
int8_t rsl;
|
|
if (ws_wh_rsl_read(conf_data->headerIeList, conf_data->headerIeListLength, &rsl)) {
|
|
ws_neighbor_class_rsl_out_calculate(neighbor_info.ws_neighbor, rsl);
|
|
}
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
//Free message
|
|
llc_message_free(message, base);
|
|
|
|
if (messsage_type == WS_FT_DATA || messsage_type == WS_FT_EAPOL) {
|
|
mpx_user_t *user_cb;
|
|
uint16_t mpx_user_id;
|
|
if (messsage_type == WS_FT_DATA) {
|
|
mpx_user_id = MPX_LOWPAN_ENC_USER_ID;
|
|
} else {
|
|
mpx_user_id = MPX_KEY_MANAGEMENT_ENC_USER_ID;
|
|
base->temp_entries->active_eapol_session = false;
|
|
}
|
|
|
|
user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, mpx_user_id);
|
|
if (user_cb && user_cb->data_confirm) {
|
|
//Call MPX registered call back
|
|
mcps_data_conf_t data_conf = *data;
|
|
data_conf.msduHandle = mpx_user_handle;
|
|
user_cb->data_confirm(&base->mpx_data_base.mpx_api, &data_conf);
|
|
}
|
|
|
|
if (messsage_type == WS_FT_EAPOL) {
|
|
message = ns_list_get_first(&base->temp_entries->llc_eap_pending_list);
|
|
if (message) {
|
|
//Start A pending EAPOL
|
|
ns_list_remove(&base->temp_entries->llc_eap_pending_list, message);
|
|
ws_llc_mpx_eapol_send(base, message);
|
|
}
|
|
} else {
|
|
if (neighbor_info.ws_neighbor && neighbor_info.neighbor && neighbor_info.neighbor->link_lifetime < WS_NEIGHBOUR_TEMPORARY_NEIGH_MAX_LIFETIME) {
|
|
//Remove temp neighbour
|
|
tr_debug("Remove Temp Entry by TX confirm");
|
|
mac_neighbor_table_neighbor_remove(mac_neighbor_info(interface), neighbor_info.neighbor);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
//Async message Confirmation
|
|
base->asynch_confirm(base->interface_ptr, messsage_type);
|
|
|
|
}
|
|
|
|
static void ws_llc_ack_data_req_ext(const mac_api_t *api, mcps_ack_data_payload_t *data, int8_t rssi, uint8_t lqi)
|
|
{
|
|
(void) lqi;
|
|
llc_data_base_t *base = ws_llc_discover_by_mac(api);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
/* Init all by zero */
|
|
memset(data, 0, sizeof(mcps_ack_data_payload_t));
|
|
//Add just 2 header elements to inside 1 block
|
|
data->ie_elements.headerIeVectorList = &base->ws_header_vector;
|
|
base->ws_header_vector.ieBase = base->ws_enhanced_response_elements;
|
|
|
|
data->ie_elements.headerIovLength = 1;
|
|
|
|
//Write Data to block
|
|
uint8_t *ptr = base->ws_enhanced_response_elements;
|
|
ptr = ws_wh_utt_write(ptr, WS_FT_ACK);
|
|
ptr = ws_wh_rsl_write(ptr, ws_neighbor_class_rsl_from_dbm_calculate(rssi));
|
|
base->ws_header_vector.iovLen = ptr - base->ws_enhanced_response_elements;
|
|
}
|
|
|
|
|
|
static llc_data_base_t *ws_llc_mpx_frame_common_validates(const mac_api_t *api, const mcps_data_ind_t *data, ws_utt_ie_t ws_utt)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_mac(api);
|
|
if (!base) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!base->ie_params.gtkhash && ws_utt.message_type == WS_FT_DATA) {
|
|
return NULL;
|
|
}
|
|
|
|
if (data->SrcAddrMode != ADDR_802_15_4_LONG) {
|
|
return NULL;
|
|
}
|
|
|
|
protocol_interface_info_entry_t *interface = base->interface_ptr;
|
|
|
|
if (interface->mac_parameters->pan_id != 0xffff && data->SrcPANId != interface->mac_parameters->pan_id) {
|
|
//Drop wrong PAN-id messages in this phase.
|
|
return NULL;
|
|
}
|
|
|
|
return base;
|
|
|
|
}
|
|
|
|
static mpx_user_t *ws_llc_mpx_header_parse(llc_data_base_t *base, const mcps_data_ie_list_t *ie_ext, mpx_msg_t *mpx_frame, mac_payload_IE_t *mpx_ie)
|
|
{
|
|
|
|
mpx_ie->id = MAC_PAYLOAD_MPX_IE_GROUP_ID;
|
|
if (mac_ie_payload_discover(ie_ext->payloadIeList, ie_ext->payloadIeListLength, mpx_ie) < 1) {
|
|
// NO MPX
|
|
return NULL;
|
|
}
|
|
//Validate MPX header
|
|
if (!ws_llc_mpx_header_frame_parse(mpx_ie->content_ptr, mpx_ie->length, mpx_frame)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (mpx_frame->transfer_type != MPX_FT_FULL_FRAME) {
|
|
return NULL; //Support only FULL Frame's
|
|
}
|
|
|
|
// Discover MPX handler
|
|
mpx_user_t *user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, mpx_frame->multiplex_id);
|
|
if (!user_cb || !user_cb->data_ind) {
|
|
return NULL;
|
|
}
|
|
|
|
return user_cb;
|
|
}
|
|
|
|
|
|
static void ws_llc_data_indication_cb(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext, ws_utt_ie_t ws_utt)
|
|
{
|
|
llc_data_base_t *base = ws_llc_mpx_frame_common_validates(api, data, ws_utt);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
|
|
//Discover MPX header and handler
|
|
mac_payload_IE_t mpx_ie;
|
|
mpx_msg_t mpx_frame;
|
|
mpx_user_t *user_cb = ws_llc_mpx_header_parse(base, ie_ext, &mpx_frame, &mpx_ie);
|
|
if (!user_cb) {
|
|
return;
|
|
}
|
|
|
|
mac_payload_IE_t ws_wp_nested;
|
|
ws_us_ie_t us_ie;
|
|
bool us_ie_inline = false;
|
|
bool bs_ie_inline = false;
|
|
ws_wp_nested.id = WS_WP_NESTED_IE;
|
|
ws_bs_ie_t ws_bs_ie;
|
|
if (mac_ie_payload_discover(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_wp_nested) > 2) {
|
|
us_ie_inline = ws_wp_nested_us_read(ws_wp_nested.content_ptr, ws_wp_nested.length, &us_ie);
|
|
bs_ie_inline = ws_wp_nested_bs_read(ws_wp_nested.content_ptr, ws_wp_nested.length, &ws_bs_ie);
|
|
}
|
|
|
|
protocol_interface_info_entry_t *interface = base->interface_ptr;
|
|
|
|
//Validate Unicast shedule Channel Plan
|
|
if (us_ie_inline &&
|
|
(!ws_bootstrap_validate_channel_plan(&us_ie, interface) ||
|
|
!ws_bootstrap_validate_channel_function(&us_ie, NULL))) {
|
|
//Channel plan or channel function configuration mismatch
|
|
return;
|
|
}
|
|
|
|
if (bs_ie_inline && !ws_bootstrap_validate_channel_function(NULL, &ws_bs_ie)) {
|
|
return;
|
|
}
|
|
|
|
//Free Old temporary entry
|
|
if (data->Key.SecurityLevel) {
|
|
ws_llc_release_eapol_temp_entry(base->temp_entries, data->SrcAddr);
|
|
}
|
|
|
|
llc_neighbour_req_t neighbor_info;
|
|
bool multicast;
|
|
bool request_new_entry;
|
|
if (data->DstAddrMode == ADDR_802_15_4_LONG) {
|
|
multicast = false;
|
|
request_new_entry = us_ie_inline;
|
|
} else {
|
|
multicast = true;
|
|
request_new_entry = false;
|
|
}
|
|
|
|
if (!base->ws_neighbor_info_request_cb(interface, data->SrcAddr, &neighbor_info, request_new_entry)) {
|
|
if (!multicast) {
|
|
//tr_debug("Drop message no neighbor");
|
|
return;
|
|
} else {
|
|
//Allocate temporary entry
|
|
ws_neighbor_temp_class_t *temp_entry = ws_allocate_multicast_temp_entry(base->temp_entries, data->SrcAddr);
|
|
neighbor_info.ws_neighbor = &temp_entry->neigh_info_list;
|
|
//Storage Signal info for future ETX update possibility
|
|
temp_entry->mpduLinkQuality = data->mpduLinkQuality;
|
|
temp_entry->signal_dbm = data->signal_dbm;
|
|
}
|
|
}
|
|
|
|
if (!multicast && !data->DSN_suppressed && !ws_neighbor_class_neighbor_duplicate_packet_check(neighbor_info.ws_neighbor, data->DSN, data->timestamp)) {
|
|
tr_info("Drop duplicate message");
|
|
return;
|
|
}
|
|
|
|
ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, &ws_utt, data->timestamp, (uint8_t *) data->SrcAddr);
|
|
if (us_ie_inline) {
|
|
ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, &us_ie, &interface->ws_info->hopping_schdule);
|
|
}
|
|
//Update BS if it is part of message
|
|
if (bs_ie_inline) {
|
|
ws_neighbor_class_neighbor_broadcast_schedule_set(neighbor_info.ws_neighbor, &ws_bs_ie);
|
|
}
|
|
|
|
//Update BT if it is part of message
|
|
ws_bt_ie_t ws_bt;
|
|
if (ws_wh_bt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_bt)) {
|
|
ws_neighbor_class_neighbor_broadcast_time_info_update(neighbor_info.ws_neighbor, &ws_bt, data->timestamp);
|
|
if (neighbor_info.neighbor && neighbor_info.neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) {
|
|
ns_fhss_ws_set_parent(interface->ws_info->fhss_api, neighbor_info.neighbor->mac64, &neighbor_info.ws_neighbor->fhss_data.bc_timing_info, false);
|
|
}
|
|
}
|
|
|
|
if (data->DstAddrMode == ADDR_802_15_4_LONG) {
|
|
neighbor_info.ws_neighbor->unicast_data_rx = true;
|
|
}
|
|
|
|
// Calculate RSL for all UDATA packets heard
|
|
ws_neighbor_class_rf_sensitivity_calculate(interface->ws_info->device_min_sens, data->signal_dbm);
|
|
ws_neighbor_class_rsl_in_calculate(neighbor_info.ws_neighbor, data->signal_dbm);
|
|
|
|
if (neighbor_info.neighbor) {
|
|
//Refresh ETX dbm
|
|
etx_lqi_dbm_update(interface->id, data->mpduLinkQuality, data->signal_dbm, neighbor_info.neighbor->index, neighbor_info.neighbor->mac64);
|
|
if (data->Key.SecurityLevel) {
|
|
//SET trusted state
|
|
mac_neighbor_table_trusted_neighbor(mac_neighbor_info(interface), neighbor_info.neighbor, true);
|
|
}
|
|
}
|
|
|
|
mcps_data_ind_t data_ind = *data;
|
|
if (!neighbor_info.neighbor) {
|
|
data_ind.Key.SecurityLevel = 0; //Mark unknow device
|
|
}
|
|
data_ind.msdu_ptr = mpx_frame.frame_ptr;
|
|
data_ind.msduLength = mpx_frame.frame_length;
|
|
user_cb->data_ind(&base->mpx_data_base.mpx_api, &data_ind);
|
|
|
|
}
|
|
|
|
static void ws_llc_eapol_indication_cb(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext, ws_utt_ie_t ws_utt)
|
|
{
|
|
llc_data_base_t *base = ws_llc_mpx_frame_common_validates(api, data, ws_utt);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
|
|
if (data->DstAddrMode != ADDR_802_15_4_LONG) {
|
|
return;
|
|
}
|
|
|
|
//Discover MPX header and handler
|
|
mac_payload_IE_t mpx_ie;
|
|
mpx_msg_t mpx_frame;
|
|
mpx_user_t *user_cb = ws_llc_mpx_header_parse(base, ie_ext, &mpx_frame, &mpx_ie);
|
|
if (!user_cb) {
|
|
return;
|
|
}
|
|
|
|
mac_payload_IE_t ws_wp_nested;
|
|
ws_us_ie_t us_ie;
|
|
bool us_ie_inline = false;
|
|
bool bs_ie_inline = false;
|
|
ws_wp_nested.id = WS_WP_NESTED_IE;
|
|
ws_bs_ie_t ws_bs_ie;
|
|
if (mac_ie_payload_discover(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_wp_nested) > 2) {
|
|
us_ie_inline = ws_wp_nested_us_read(ws_wp_nested.content_ptr, ws_wp_nested.length, &us_ie);
|
|
bs_ie_inline = ws_wp_nested_bs_read(ws_wp_nested.content_ptr, ws_wp_nested.length, &ws_bs_ie);
|
|
}
|
|
|
|
protocol_interface_info_entry_t *interface = base->interface_ptr;
|
|
|
|
//Validate Unicast shedule Channel Plan
|
|
if (us_ie_inline &&
|
|
(!ws_bootstrap_validate_channel_plan(&us_ie, interface) ||
|
|
!ws_bootstrap_validate_channel_function(&us_ie, NULL))) {
|
|
//Channel plan or channel function configuration mismatch
|
|
return;
|
|
}
|
|
|
|
if (bs_ie_inline && !ws_bootstrap_validate_channel_function(NULL, &ws_bs_ie)) {
|
|
return;
|
|
}
|
|
|
|
llc_neighbour_req_t neighbor_info;
|
|
|
|
if (!base->ws_neighbor_info_request_cb(interface, data->SrcAddr, &neighbor_info, false)) {
|
|
//Allocate temporary entry
|
|
ws_neighbor_temp_class_t *temp_entry = ws_allocate_eapol_temp_entry(base->temp_entries, data->SrcAddr);
|
|
if (!temp_entry) {
|
|
tr_warn("EAPOL temp pool empty");
|
|
return;
|
|
}
|
|
//Update Temporary Lifetime
|
|
temp_entry->eapol_temp_info.eapol_timeout = interface->ws_info->cfg->timing.temp_eapol_min_timeout + 1;
|
|
|
|
neighbor_info.ws_neighbor = &temp_entry->neigh_info_list;
|
|
//Storage Signal info for future ETX update possibility
|
|
temp_entry->mpduLinkQuality = data->mpduLinkQuality;
|
|
temp_entry->signal_dbm = data->signal_dbm;
|
|
}
|
|
uint8_t auth_eui64[8];
|
|
ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, &ws_utt, data->timestamp, (uint8_t *) data->SrcAddr);
|
|
if (us_ie_inline) {
|
|
ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, &us_ie, &interface->ws_info->hopping_schdule);
|
|
}
|
|
//Update BS if it is part of message
|
|
if (bs_ie_inline) {
|
|
ws_neighbor_class_neighbor_broadcast_schedule_set(neighbor_info.ws_neighbor, &ws_bs_ie);
|
|
}
|
|
|
|
//Discover and write Auhtenticator EUI-64
|
|
if (ws_wh_ea_read(ie_ext->headerIeList, ie_ext->headerIeListLength, auth_eui64)) {
|
|
ws_pae_controller_border_router_addr_write(base->interface_ptr, auth_eui64);
|
|
}
|
|
|
|
//Update BT if it is part of message
|
|
ws_bt_ie_t ws_bt;
|
|
if (ws_wh_bt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_bt)) {
|
|
ws_neighbor_class_neighbor_broadcast_time_info_update(neighbor_info.ws_neighbor, &ws_bt, data->timestamp);
|
|
if (neighbor_info.neighbor) {
|
|
ws_bootstrap_eapol_parent_synch(interface, &neighbor_info);
|
|
}
|
|
}
|
|
|
|
|
|
mcps_data_ind_t data_ind = *data;
|
|
data_ind.msdu_ptr = mpx_frame.frame_ptr;
|
|
data_ind.msduLength = mpx_frame.frame_length;
|
|
user_cb->data_ind(&base->mpx_data_base.mpx_api, &data_ind);
|
|
}
|
|
|
|
static void ws_llc_asynch_indication(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext, ws_utt_ie_t ws_utt)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_mac(api);
|
|
if (!base || !base->asynch_ind) {
|
|
return;
|
|
}
|
|
|
|
//Asynch Message
|
|
|
|
mac_payload_IE_t ws_wp_nested;
|
|
|
|
ws_wp_nested.id = WS_WP_NESTED_IE;
|
|
if (mac_ie_payload_discover(ie_ext->payloadIeList, ie_ext->payloadIeListLength, &ws_wp_nested) < 2) {
|
|
// NO WS_WP_NESTED_IE Payload
|
|
return;
|
|
}
|
|
|
|
switch (ws_utt.message_type) {
|
|
case WS_FT_PAN_ADVERT:
|
|
case WS_FT_PAN_CONF:
|
|
case WS_FT_PAN_CONF_SOL:
|
|
ws_llc_release_eapol_temp_entry(base->temp_entries, data->SrcAddr);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
mcps_data_ie_list_t asynch_ie_list;
|
|
asynch_ie_list.headerIeList = ie_ext->headerIeList,
|
|
asynch_ie_list.headerIeListLength = ie_ext->headerIeListLength;
|
|
asynch_ie_list.payloadIeList = ws_wp_nested.content_ptr;
|
|
asynch_ie_list.payloadIeListLength = ws_wp_nested.length;
|
|
base->asynch_ind(base->interface_ptr, data, &asynch_ie_list, ws_utt.message_type);
|
|
}
|
|
|
|
/** WS LLC MAC data extension indication */
|
|
static void ws_llc_mac_indication_cb(const mac_api_t *api, const mcps_data_ind_t *data, const mcps_data_ie_list_t *ie_ext)
|
|
{
|
|
|
|
|
|
//Discover Header WH_IE_UTT_TYPE
|
|
ws_utt_ie_t ws_utt;
|
|
if (!ws_wh_utt_read(ie_ext->headerIeList, ie_ext->headerIeListLength, &ws_utt)) {
|
|
// NO UTT header
|
|
return;
|
|
}
|
|
|
|
if (ws_utt.message_type < WS_FT_DATA) {
|
|
ws_llc_asynch_indication(api, data, ie_ext, ws_utt);
|
|
return;
|
|
}
|
|
|
|
if (ws_utt.message_type == WS_FT_DATA) {
|
|
ws_llc_data_indication_cb(api, data, ie_ext, ws_utt);
|
|
return;
|
|
}
|
|
|
|
if (ws_utt.message_type == WS_FT_EAPOL) {
|
|
ws_llc_eapol_indication_cb(api, data, ie_ext, ws_utt);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static uint16_t ws_mpx_header_size_get(llc_data_base_t *base, uint16_t user_id)
|
|
{
|
|
//TODO add WS_WP_NESTED_IE support
|
|
uint16_t header_size = 0;
|
|
if (user_id == MPX_LOWPAN_ENC_USER_ID) {
|
|
header_size += 7 + 8 + 5 + 2; //UTT+BTT+ MPX + Padding
|
|
if (base->ie_params.vendor_header_length) {
|
|
header_size += base->ie_params.vendor_header_length + 3;
|
|
}
|
|
|
|
if (base->ie_params.vendor_payload_length) {
|
|
header_size += base->ie_params.vendor_payload_length + 2;
|
|
}
|
|
|
|
//Dynamic length
|
|
header_size += 2 + WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(base->ie_params.hopping_schedule, true) + ws_wp_nested_hopping_schedule_length(base->ie_params.hopping_schedule, false);
|
|
|
|
} else if (MPX_KEY_MANAGEMENT_ENC_USER_ID) {
|
|
header_size += 7 + 5 + 2;
|
|
//Dynamic length
|
|
header_size += 2 + WS_WP_SUB_IE_ELEMENT_HEADER_LENGTH + ws_wp_nested_hopping_schedule_length(base->ie_params.hopping_schedule, true);
|
|
}
|
|
return header_size;
|
|
}
|
|
|
|
static bool ws_eapol_handshake_first_msg(uint8_t *pdu, uint16_t length, protocol_interface_info_entry_t *cur)
|
|
{
|
|
if (!ws_eapol_relay_state_active(cur)) {
|
|
return false;
|
|
}
|
|
|
|
eapol_pdu_t eapol_pdu;
|
|
uint8_t kmp_type = *pdu++;
|
|
length--;
|
|
if (!eapol_parse_pdu_header(pdu, length, &eapol_pdu)) {
|
|
return false;
|
|
}
|
|
if (eapol_pdu.packet_type == EAPOL_EAP_TYPE) {
|
|
if (eapol_pdu.msg.eap.eap_code == EAP_REQ && eapol_pdu.msg.eap.type == EAP_IDENTITY) {
|
|
return true;
|
|
}
|
|
} else {
|
|
|
|
uint8_t key_mask = eapol_pdu_key_mask_get(&eapol_pdu);
|
|
if (kmp_type == 6 && key_mask == KEY_INFO_KEY_ACK) {
|
|
//FWK first message validation
|
|
return true;
|
|
} else if (kmp_type == 7 && key_mask == (KEY_INFO_KEY_ACK | KEY_INFO_KEY_MIC | KEY_INFO_SECURED_KEY_FRAME)) {
|
|
//GWK first message validation
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void ws_llc_lowpan_mpx_header_set(llc_message_t *message, uint16_t user_id)
|
|
{
|
|
uint8_t *ptr = (uint8_t *)message->ie_vector_list[1].ieBase;
|
|
ptr += message->ie_vector_list[1].iovLen;
|
|
ptr = mac_ie_payload_base_write(ptr, MAC_PAYLOAD_MPX_IE_GROUP_ID, message->ie_vector_list[2].iovLen + 3);
|
|
mpx_msg_t mpx_header;
|
|
mpx_header.transfer_type = MPX_FT_FULL_FRAME;
|
|
mpx_header.transaction_id = message->mpx_id;
|
|
mpx_header.multiplex_id = user_id;
|
|
ptr = ws_llc_mpx_header_write(ptr, &mpx_header);
|
|
message->ie_vector_list[1].iovLen = ptr - (uint8_t *)message->ie_vector_list[1].ieBase;
|
|
}
|
|
|
|
static void ws_llc_lowpan_mpx_data_request(llc_data_base_t *base, mpx_user_t *user_cb, const struct mcps_data_req_s *data)
|
|
{
|
|
wh_ie_sub_list_t ie_header_mask;
|
|
memset(&ie_header_mask, 0, sizeof(wh_ie_sub_list_t));
|
|
|
|
wp_nested_ie_sub_list_t nested_wp_id;
|
|
memset(&nested_wp_id, 0, sizeof(wp_nested_ie_sub_list_t));
|
|
ie_header_mask.utt_ie = true;
|
|
|
|
ie_header_mask.bt_ie = true;
|
|
if (base->ie_params.vendor_header_length) {
|
|
ie_header_mask.vh_ie = true;
|
|
}
|
|
|
|
if (base->ie_params.vendor_payload_length) {
|
|
nested_wp_id.vp_ie = true;
|
|
}
|
|
|
|
if (data->ExtendedFrameExchange && data->TxAckReq) {
|
|
ie_header_mask.fc_ie = true;
|
|
}
|
|
if (!data->TxAckReq) {
|
|
nested_wp_id.bs_ie = true;
|
|
}
|
|
|
|
nested_wp_id.us_ie = true;
|
|
|
|
uint16_t ie_header_length = ws_wh_headers_length(ie_header_mask, &base->ie_params);
|
|
uint16_t nested_ie_length = ws_wp_nested_message_length(nested_wp_id, &base->ie_params);
|
|
|
|
uint16_t over_head_size = ie_header_length;
|
|
if (nested_ie_length) {
|
|
over_head_size += nested_ie_length + 2;
|
|
}
|
|
//Mpx header size
|
|
over_head_size += 5; //MPX FuLL frame 3 bytes + IE header 2 bytes
|
|
|
|
//Allocate Message
|
|
llc_message_t *message = llc_message_allocate(over_head_size, base);
|
|
if (!message) {
|
|
mcps_data_conf_t data_conf;
|
|
memset(&data_conf, 0, sizeof(mcps_data_conf_t));
|
|
data_conf.msduHandle = data->msduHandle;
|
|
data_conf.status = MLME_TRANSACTION_OVERFLOW;
|
|
user_cb->data_confirm(&base->mpx_data_base.mpx_api, &data_conf);
|
|
return;
|
|
}
|
|
|
|
//Add To active list
|
|
llc_message_id_allocate(message, base, true);
|
|
base->llc_message_list_size++;
|
|
ns_list_add_to_end(&base->llc_message_list, message);
|
|
|
|
mcps_data_req_t data_req;
|
|
message->mpx_user_handle = data->msduHandle;
|
|
message->ack_requested = data->TxAckReq;
|
|
if (data->TxAckReq) {
|
|
message->dst_address_type = data->DstAddrMode;
|
|
memcpy(message->dst_address, data->DstAddr, 8);
|
|
}
|
|
data_req = *data;
|
|
data_req.msdu = NULL;
|
|
data_req.msduLength = 0;
|
|
data_req.msduHandle = message->msg_handle;
|
|
|
|
if (data->ExtendedFrameExchange && data->TxAckReq) {
|
|
data_req.SeqNumSuppressed = true;
|
|
data_req.PanIdSuppressed = true;
|
|
data_req.TxAckReq = true; // This will be changed inside MAC
|
|
} else {
|
|
data_req.ExtendedFrameExchange = false; //Do not accept EDFE for non unicast traffic
|
|
if (!data->TxAckReq) {
|
|
data_req.PanIdSuppressed = false;
|
|
data_req.DstAddrMode = MAC_ADDR_MODE_NONE;
|
|
} else {
|
|
data_req.PanIdSuppressed = true;
|
|
}
|
|
}
|
|
|
|
uint8_t *ptr = ws_message_buffer_ptr_get(message);
|
|
message->messsage_type = WS_FT_DATA;
|
|
|
|
message->ie_vector_list[0].ieBase = ptr;
|
|
if (ie_header_mask.fc_ie) {
|
|
ws_fc_ie_t fc_ie;
|
|
fc_ie.tx_flow_ctrl = 50;//No data at initial frame
|
|
fc_ie.rx_flow_ctrl = 255;
|
|
//Write Flow control for 1 packet send this will be modified at real data send
|
|
ptr = ws_wh_fc_write(ptr, &fc_ie);
|
|
}
|
|
//Write UTT
|
|
|
|
ptr = ws_wh_utt_write(ptr, message->messsage_type);
|
|
if (ie_header_mask.bt_ie) {
|
|
ptr = ws_wh_bt_write(ptr);
|
|
}
|
|
|
|
if (ie_header_mask.vh_ie) {
|
|
ptr = ws_wh_vh_write(ptr, base->ie_params.vendor_header_data, base->ie_params.vendor_header_length);
|
|
}
|
|
|
|
message->ie_vector_list[0].iovLen = ie_header_length;
|
|
message->ie_ext.headerIeVectorList = &message->ie_vector_list[0];
|
|
message->ie_ext.headerIovLength = 1;
|
|
message->ie_ext.payloadIeVectorList = &message->ie_vector_list[1];
|
|
message->ie_ext.payloadIovLength = 2;
|
|
message->ie_vector_list[1].ieBase = ptr;
|
|
|
|
if (nested_ie_length) {
|
|
ptr = ws_wp_base_write(ptr, nested_ie_length);
|
|
//Write unicast schedule
|
|
ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, true);
|
|
|
|
if (nested_wp_id.bs_ie) {
|
|
//Write Broadcastcast schedule
|
|
ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, false);
|
|
}
|
|
}
|
|
// SET Payload IE Length
|
|
message->ie_vector_list[1].iovLen = ptr - (uint8_t *)message->ie_vector_list[1].ieBase;
|
|
message->ie_vector_list[2].ieBase = data->msdu;
|
|
message->ie_vector_list[2].iovLen = data->msduLength;
|
|
|
|
ws_llc_lowpan_mpx_header_set(message, MPX_LOWPAN_ENC_USER_ID);
|
|
if (data->ExtendedFrameExchange) {
|
|
message->ie_ext.payloadIovLength = 0; //Set Back 2 at response handler
|
|
}
|
|
|
|
base->interface_ptr->mac_api->mcps_data_req_ext(base->interface_ptr->mac_api, &data_req, &message->ie_ext, NULL);
|
|
}
|
|
|
|
static bool ws_llc_eapol_temp_entry_set(llc_data_base_t *base, const uint8_t *mac64)
|
|
{
|
|
//Discover Temporary entry
|
|
ws_neighbor_temp_class_t *temp_neigh = ws_llc_discover_eapol_temp_entry(base->temp_entries, mac64);
|
|
|
|
if (!temp_neigh) {
|
|
return false;
|
|
}
|
|
ws_neighbor_class_entry_t *entry = ws_bootstrap_eapol_tx_temporary_set(base->interface_ptr, mac64);
|
|
if (!entry) {
|
|
return false;
|
|
}
|
|
*entry = temp_neigh->neigh_info_list;
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
static void ws_llc_eapol_data_req_init(mcps_data_req_t *data_req, llc_message_t *message)
|
|
{
|
|
memset(data_req, 0, sizeof(mcps_data_req_t));
|
|
data_req->TxAckReq = message->ack_requested;
|
|
data_req->DstPANId = message->pan_id;
|
|
data_req->SrcAddrMode = message->src_address_type;
|
|
data_req->ExtendedFrameExchange = false;
|
|
if (!data_req->TxAckReq) {
|
|
data_req->PanIdSuppressed = false;
|
|
data_req->DstAddrMode = MAC_ADDR_MODE_NONE;
|
|
} else {
|
|
data_req->PanIdSuppressed = true;
|
|
data_req->DstAddrMode = message->dst_address_type;
|
|
memcpy(data_req->DstAddr, message->dst_address, 8);
|
|
}
|
|
|
|
|
|
data_req->msdu = NULL;
|
|
data_req->msduLength = 0;
|
|
data_req->msduHandle = message->msg_handle;
|
|
|
|
ws_llc_lowpan_mpx_header_set(message, MPX_KEY_MANAGEMENT_ENC_USER_ID);
|
|
}
|
|
|
|
static void ws_llc_mpx_eapol_send(llc_data_base_t *base, llc_message_t *message)
|
|
{
|
|
mcps_data_req_t data_req;
|
|
llc_message_id_allocate(message, base, true);
|
|
base->llc_message_list_size++;
|
|
ns_list_add_to_end(&base->llc_message_list, message);
|
|
message->eapol_temporary = ws_llc_eapol_temp_entry_set(base, message->dst_address);
|
|
ws_llc_eapol_data_req_init(&data_req, message);
|
|
base->temp_entries->active_eapol_session = true;
|
|
base->interface_ptr->mac_api->mcps_data_req_ext(base->interface_ptr->mac_api, &data_req, &message->ie_ext, NULL);
|
|
}
|
|
|
|
|
|
static void ws_llc_mpx_eapol_request(llc_data_base_t *base, mpx_user_t *user_cb, const struct mcps_data_req_s *data)
|
|
{
|
|
wh_ie_sub_list_t ie_header_mask;
|
|
memset(&ie_header_mask, 0, sizeof(wh_ie_sub_list_t));
|
|
|
|
wp_nested_ie_sub_list_t nested_wp_id;
|
|
memset(&nested_wp_id, 0, sizeof(wp_nested_ie_sub_list_t));
|
|
ie_header_mask.utt_ie = true;
|
|
ie_header_mask.bt_ie = ws_eapol_relay_state_active(base->interface_ptr);
|
|
ie_header_mask.ea_ie = ws_eapol_handshake_first_msg(data->msdu, data->msduLength, base->interface_ptr);
|
|
nested_wp_id.bs_ie = ie_header_mask.ea_ie;
|
|
|
|
|
|
nested_wp_id.us_ie = true;
|
|
|
|
uint16_t ie_header_length = ws_wh_headers_length(ie_header_mask, &base->ie_params);
|
|
uint16_t nested_ie_length = ws_wp_nested_message_length(nested_wp_id, &base->ie_params);
|
|
|
|
uint16_t over_head_size = ie_header_length;
|
|
if (nested_ie_length) {
|
|
over_head_size += nested_ie_length + 2;
|
|
}
|
|
//Mpx header size
|
|
over_head_size += 5; //MPX FuLL frame 3 bytes + IE header 2 bytes
|
|
|
|
//Allocate Message
|
|
llc_message_t *message = llc_message_allocate(over_head_size, base);
|
|
if (!message) {
|
|
mcps_data_conf_t data_conf;
|
|
memset(&data_conf, 0, sizeof(mcps_data_conf_t));
|
|
data_conf.msduHandle = data->msduHandle;
|
|
data_conf.status = MLME_TRANSACTION_OVERFLOW;
|
|
user_cb->data_confirm(&base->mpx_data_base.mpx_api, &data_conf);
|
|
return;
|
|
}
|
|
message->mpx_user_handle = data->msduHandle;
|
|
message->ack_requested = data->TxAckReq;
|
|
|
|
message->src_address_type = data->SrcAddrMode;
|
|
memcpy(message->dst_address, data->DstAddr, 8);
|
|
message->dst_address_type = data->DstAddrMode;
|
|
message->pan_id = data->DstPANId;
|
|
message->messsage_type = WS_FT_EAPOL;
|
|
|
|
uint8_t *ptr = ws_message_buffer_ptr_get(message);
|
|
|
|
message->ie_vector_list[0].ieBase = ptr;
|
|
//Write UTT
|
|
ptr = ws_wh_utt_write(ptr, message->messsage_type);
|
|
if (ie_header_mask.bt_ie) {
|
|
ptr = ws_wh_bt_write(ptr);
|
|
}
|
|
|
|
if (ie_header_mask.ea_ie) {
|
|
uint8_t eapol_auth_eui64[8];
|
|
ws_pae_controller_border_router_addr_read(base->interface_ptr, eapol_auth_eui64);
|
|
ptr = ws_wh_ea_write(ptr, eapol_auth_eui64);
|
|
}
|
|
|
|
message->ie_vector_list[0].iovLen = ie_header_length;
|
|
message->ie_ext.headerIeVectorList = &message->ie_vector_list[0];
|
|
message->ie_ext.headerIovLength = 1;
|
|
message->ie_ext.payloadIeVectorList = &message->ie_vector_list[1];
|
|
message->ie_ext.payloadIovLength = 2;
|
|
message->ie_vector_list[1].ieBase = ptr;
|
|
|
|
if (nested_ie_length) {
|
|
ptr = ws_wp_base_write(ptr, nested_ie_length);
|
|
//Write unicast schedule
|
|
ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, true);
|
|
|
|
if (nested_wp_id.bs_ie) {
|
|
//Write Broadcastcast schedule
|
|
ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, false);
|
|
}
|
|
}
|
|
|
|
// SET Payload IE Length
|
|
message->ie_vector_list[1].iovLen = ptr - (uint8_t *)message->ie_vector_list[1].ieBase;
|
|
message->ie_vector_list[2].ieBase = data->msdu;
|
|
message->ie_vector_list[2].iovLen = data->msduLength;
|
|
|
|
if (base->temp_entries->active_eapol_session) {
|
|
//Move to pending list
|
|
ns_list_add_to_end(&base->temp_entries->llc_eap_pending_list, message);
|
|
} else {
|
|
ws_llc_mpx_eapol_send(base, message);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static void ws_llc_mpx_data_request(const mpx_api_t *api, const struct mcps_data_req_s *data, uint16_t user_id)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_mpx(api);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
|
|
mpx_user_t *user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, user_id);
|
|
if (!user_cb || !user_cb->data_confirm || !user_cb->data_ind) {
|
|
return;
|
|
}
|
|
|
|
if (user_id == MPX_KEY_MANAGEMENT_ENC_USER_ID) {
|
|
ws_llc_mpx_eapol_request(base, user_cb, data);
|
|
} else if (user_id == MPX_LOWPAN_ENC_USER_ID) {
|
|
ws_llc_lowpan_mpx_data_request(base, user_cb, data);
|
|
}
|
|
}
|
|
|
|
|
|
static int8_t ws_llc_mpx_data_cb_register(const mpx_api_t *api, mpx_data_confirm *confirm_cb, mpx_data_indication *indication_cb, uint16_t user_id)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_mpx(api);
|
|
if (!base) {
|
|
return -1;
|
|
}
|
|
|
|
mpx_user_t *user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, user_id);
|
|
if (!user_cb) {
|
|
return -1;
|
|
}
|
|
user_cb->data_confirm = confirm_cb;
|
|
user_cb->data_ind = indication_cb;
|
|
return 0;
|
|
}
|
|
|
|
static uint16_t ws_llc_mpx_header_size_get(const mpx_api_t *api, uint16_t user_id)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_mpx(api);
|
|
if (!base) {
|
|
return 0;
|
|
}
|
|
|
|
return ws_mpx_header_size_get(base, user_id);
|
|
}
|
|
|
|
static uint8_t ws_llc_mpx_data_purge_request(const mpx_api_t *api, struct mcps_purge_s *purge, uint16_t user_id)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_mpx(api);
|
|
if (!base) {
|
|
return MLME_INVALID_HANDLE;
|
|
}
|
|
llc_message_t *message = llc_message_discover_mpx_user_id(purge->msduHandle, user_id, &base->llc_message_list);
|
|
if (!message) {
|
|
return MLME_INVALID_HANDLE;
|
|
}
|
|
|
|
mcps_purge_t purge_req;
|
|
uint8_t purge_status;
|
|
purge_req.msduHandle = message->msg_handle;
|
|
purge_status = base->interface_ptr->mac_api->mcps_purge_req(base->interface_ptr->mac_api, &purge_req);
|
|
if (purge_status == 0) {
|
|
llc_message_free(message, base);
|
|
}
|
|
|
|
return purge_status;
|
|
}
|
|
|
|
static void ws_llc_mpx_init(mpx_class_t *mpx_class)
|
|
{
|
|
//Init Mbed Class and API
|
|
mpx_class->mpx_user_table[0].user_id = MPX_LOWPAN_ENC_USER_ID;
|
|
mpx_class->mpx_user_table[1].user_id = MPX_KEY_MANAGEMENT_ENC_USER_ID;
|
|
mpx_class->mpx_api.mpx_headroom_size_get = &ws_llc_mpx_header_size_get;
|
|
mpx_class->mpx_api.mpx_user_registration = &ws_llc_mpx_data_cb_register;
|
|
mpx_class->mpx_api.mpx_data_request = &ws_llc_mpx_data_request;
|
|
mpx_class->mpx_api.mpx_data_purge = &ws_llc_mpx_data_purge_request;
|
|
}
|
|
|
|
static void ws_llc_clean(llc_data_base_t *base)
|
|
{
|
|
//Clean Message queue's
|
|
mcps_purge_t purge_req;
|
|
ns_list_foreach_safe(llc_message_t, message, &base->llc_message_list) {
|
|
purge_req.msduHandle = message->msg_handle;
|
|
llc_message_free(message, base);
|
|
base->interface_ptr->mac_api->mcps_purge_req(base->interface_ptr->mac_api, &purge_req);
|
|
|
|
}
|
|
|
|
ns_list_foreach_safe(llc_message_t, message, &base->temp_entries->llc_eap_pending_list) {
|
|
ns_list_remove(&base->temp_entries->llc_eap_pending_list, message);
|
|
ns_dyn_mem_free(message);
|
|
}
|
|
base->temp_entries->active_eapol_session = false;
|
|
memset(&base->ie_params, 0, sizeof(llc_ie_params_t));
|
|
|
|
ws_llc_temp_neigh_info_table_reset(base->temp_entries);
|
|
}
|
|
|
|
static void ws_llc_temp_neigh_info_table_reset(temp_entriest_t *base)
|
|
{
|
|
//Empty active list
|
|
ns_list_init(&base->active_multicast_temp_neigh);
|
|
ns_list_init(&base->active_eapol_temp_neigh);
|
|
ns_list_init(&base->free_temp_neigh);
|
|
|
|
//Add to free list to full
|
|
for (int i = 0; i < MAX_NEIGH_TEMPORAY_LIST_SIZE; i++) {
|
|
ns_list_add_to_end(&base->free_temp_neigh, &base->neighbour_temporary_table[i]);
|
|
}
|
|
}
|
|
|
|
static ws_neighbor_temp_class_t *ws_llc_discover_mc_temp_entry(temp_entriest_t *base, const uint8_t *mac64)
|
|
{
|
|
ns_list_foreach(ws_neighbor_temp_class_t, entry, &base->active_multicast_temp_neigh) {
|
|
if (memcmp(entry->mac64, mac64, 8) == 0) {
|
|
return entry;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static ws_neighbor_temp_class_t *ws_llc_discover_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64)
|
|
{
|
|
ns_list_foreach(ws_neighbor_temp_class_t, entry, &base->active_eapol_temp_neigh) {
|
|
if (memcmp(entry->mac64, mac64, 8) == 0) {
|
|
return entry;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void ws_llc_release_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64)
|
|
{
|
|
ws_neighbor_temp_class_t *neighbor = ws_llc_discover_eapol_temp_entry(base, mac64);
|
|
if (!neighbor) {
|
|
return;
|
|
}
|
|
|
|
ns_list_remove(&base->active_eapol_temp_neigh, neighbor);
|
|
ns_list_add_to_end(&base->free_temp_neigh, neighbor);
|
|
}
|
|
|
|
ws_neighbor_temp_class_t *ws_llc_get_multicast_temp_entry(protocol_interface_info_entry_t *interface, const uint8_t *mac64)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return NULL;
|
|
}
|
|
|
|
return ws_llc_discover_mc_temp_entry(base->temp_entries, mac64);
|
|
}
|
|
|
|
|
|
static void ws_init_temporary_neigh_data(ws_neighbor_temp_class_t *entry, const uint8_t *mac64)
|
|
{
|
|
//Clear Old data
|
|
memset(&entry->neigh_info_list, 0, sizeof(ws_neighbor_class_entry_t));
|
|
entry->neigh_info_list.rsl_in = RSL_UNITITIALIZED;
|
|
entry->neigh_info_list.rsl_out = RSL_UNITITIALIZED;
|
|
memcpy(entry->mac64, mac64, 8);
|
|
entry->eapol_temp_info.eapol_rx_relay_filter = 0;
|
|
}
|
|
|
|
|
|
|
|
static ws_neighbor_temp_class_t *ws_allocate_multicast_temp_entry(temp_entriest_t *base, const uint8_t *mac64)
|
|
{
|
|
|
|
ws_neighbor_temp_class_t *entry = ws_llc_discover_mc_temp_entry(base, mac64);
|
|
if (entry) {
|
|
ns_list_remove(&base->active_multicast_temp_neigh, entry);
|
|
ns_list_add_to_start(&base->active_multicast_temp_neigh, entry);
|
|
return entry;
|
|
}
|
|
|
|
if (ns_list_count(&base->active_multicast_temp_neigh) < MAX_NEIGH_TEMPORARY_MULTICAST_SIZE) {
|
|
entry = ns_list_get_first(&base->free_temp_neigh);
|
|
}
|
|
|
|
if (entry) {
|
|
ns_list_remove(&base->free_temp_neigh, entry);
|
|
} else {
|
|
//Replace last entry and put it to first
|
|
entry = ns_list_get_last(&base->active_multicast_temp_neigh);
|
|
ns_list_remove(&base->active_multicast_temp_neigh, entry);
|
|
}
|
|
//Add to list
|
|
ns_list_add_to_start(&base->active_multicast_temp_neigh, entry);
|
|
//Clear Old data
|
|
ws_init_temporary_neigh_data(entry, mac64);
|
|
return entry;
|
|
}
|
|
|
|
static ws_neighbor_temp_class_t *ws_allocate_eapol_temp_entry(temp_entriest_t *base, const uint8_t *mac64)
|
|
{
|
|
|
|
ws_neighbor_temp_class_t *entry = ws_llc_discover_eapol_temp_entry(base, mac64);
|
|
if (entry) {
|
|
//TODO referesh Timer here
|
|
return entry;
|
|
}
|
|
|
|
if (ns_list_count(&base->active_eapol_temp_neigh) < MAX_NEIGH_TEMPORRY_EAPOL_SIZE) {
|
|
entry = ns_list_get_first(&base->free_temp_neigh);
|
|
}
|
|
|
|
if (!entry) {
|
|
return NULL;
|
|
}
|
|
|
|
ns_list_remove(&base->free_temp_neigh, entry);
|
|
//Add to list
|
|
ns_list_add_to_start(&base->active_eapol_temp_neigh, entry);
|
|
//Clear Old data
|
|
ws_init_temporary_neigh_data(entry, mac64);
|
|
return entry;
|
|
}
|
|
|
|
void ws_llc_free_multicast_temp_entry(protocol_interface_info_entry_t *cur, ws_neighbor_temp_class_t *neighbor)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(cur);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
ns_list_remove(&base->temp_entries->active_multicast_temp_neigh, neighbor);
|
|
ns_list_add_to_end(&base->temp_entries->free_temp_neigh, neighbor);
|
|
}
|
|
|
|
|
|
static void ws_llc_build_edfe_response(llc_data_base_t *base, mcps_edfe_response_t *response_message, ws_fc_ie_t fc_ie)
|
|
{
|
|
memset(&response_message->ie_response, 0, sizeof(mcps_data_req_ie_list_t));
|
|
response_message->ie_response.headerIeVectorList = &base->ws_header_vector;
|
|
base->ws_header_vector.ieBase = base->ws_enhanced_response_elements;
|
|
response_message->ie_response.headerIovLength = 1;
|
|
|
|
//Write Data to block
|
|
uint8_t *ptr = base->ws_header_vector.ieBase;
|
|
ptr = ws_wh_fc_write(ptr, &fc_ie);
|
|
ptr = ws_wh_utt_write(ptr, WS_FT_DATA);
|
|
ptr = ws_wh_bt_write(ptr);
|
|
ptr = ws_wh_rsl_write(ptr, ws_neighbor_class_rsl_from_dbm_calculate(response_message->rssi));
|
|
base->ws_header_vector.iovLen = ptr - base->ws_enhanced_response_elements;
|
|
response_message->SrcAddrMode = MAC_ADDR_MODE_NONE;
|
|
response_message->wait_response = false;
|
|
response_message->PanIdSuppressed = true;
|
|
}
|
|
|
|
static void ws_llc_build_edfe_frame(llc_message_t *message, mcps_edfe_response_t *response_message, ws_fc_ie_t fc_ie)
|
|
{
|
|
memset(&response_message->ie_response, 0, sizeof(mcps_data_req_ie_list_t));
|
|
uint8_t *ptr = message->ie_vector_list[0].ieBase;
|
|
fc_ie.tx_flow_ctrl = 0;//Put Data with Handshake
|
|
fc_ie.rx_flow_ctrl = 255;
|
|
//Write Flow control for 1 packet send this will be modified at real data send
|
|
ptr = ws_wh_fc_write(ptr, &fc_ie);
|
|
response_message->ie_response.headerIeVectorList = &message->ie_vector_list[0];
|
|
response_message->ie_response.headerIovLength = 1;
|
|
response_message->ie_response.payloadIeVectorList = &message->ie_vector_list[1];
|
|
response_message->ie_response.payloadIovLength = 2;
|
|
response_message->SrcAddrMode = MAC_ADDR_MODE_NONE;
|
|
response_message->wait_response = true;
|
|
response_message->PanIdSuppressed = true;
|
|
//tr_debug("FC:Send Data frame");
|
|
response_message->edfe_message_status = MCPS_EDFE_TX_FRAME;
|
|
}
|
|
|
|
static void ws_llc_mcps_edfe_handler(const mac_api_t *api, mcps_edfe_response_t *response_message)
|
|
{
|
|
// INSIDE this shuold not print anything
|
|
response_message->edfe_message_status = MCPS_EDFE_NORMAL_FRAME;
|
|
llc_data_base_t *base = ws_llc_discover_by_mac(api);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
//Discover Here header FC-IE element
|
|
ws_fc_ie_t fc_ie;
|
|
if (!ws_wh_fc_read(response_message->ie_elements.headerIeList, response_message->ie_elements.headerIeListLength, &fc_ie)) {
|
|
return;
|
|
}
|
|
//tr_debug("Flow ctrl(%u TX,%u RX)", fc_ie.tx_flow_ctrl, fc_ie.rx_flow_ctrl);
|
|
if (fc_ie.tx_flow_ctrl == 0 && fc_ie.rx_flow_ctrl) {
|
|
|
|
llc_message_t *message = NULL;
|
|
if (response_message->use_message_handle_to_discover) {
|
|
message = llc_message_discover_by_mac_handle(response_message->message_handle, &base->llc_message_list);
|
|
}
|
|
|
|
if (!message) {
|
|
//tr_debug("FC:Send a Final Frame");
|
|
if (test_drop_data_message) {
|
|
test_drop_data_message--;
|
|
base->edfe_rx_wait_timer += 99;
|
|
response_message->edfe_message_status = MCPS_EDFE_MALFORMED_FRAME;
|
|
return;
|
|
}
|
|
fc_ie.rx_flow_ctrl = 0;
|
|
base->edfe_rx_wait_timer = 0;
|
|
ws_llc_build_edfe_response(base, response_message, fc_ie);
|
|
response_message->edfe_message_status = MCPS_EDFE_FINAL_FRAME_TX;
|
|
} else {
|
|
if (test_skip_first_init_response) {
|
|
//Skip data send and test timeout at Slave side
|
|
test_skip_first_init_response = false;
|
|
response_message->edfe_message_status = MCPS_EDFE_FINAL_FRAME_RX;
|
|
return;
|
|
}
|
|
ws_llc_build_edfe_frame(message, response_message, fc_ie);
|
|
}
|
|
|
|
} else if (fc_ie.tx_flow_ctrl == 0 && fc_ie.rx_flow_ctrl == 0) {
|
|
//tr_debug("FC:Received a Final Frame");
|
|
base->edfe_rx_wait_timer = 0;
|
|
response_message->edfe_message_status = MCPS_EDFE_FINAL_FRAME_RX;
|
|
} else if (fc_ie.tx_flow_ctrl && fc_ie.rx_flow_ctrl) {
|
|
base->edfe_rx_wait_timer = fc_ie.tx_flow_ctrl + 99;
|
|
fc_ie.tx_flow_ctrl = 0;
|
|
fc_ie.rx_flow_ctrl = 255;
|
|
//tr_debug("FC:Send a response");
|
|
//Enable or refesh timeout timer
|
|
ws_llc_build_edfe_response(base, response_message, fc_ie);
|
|
response_message->edfe_message_status = MCPS_EDFE_RESPONSE_FRAME;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int8_t ws_llc_create(struct protocol_interface_info_entry *interface, ws_asynch_ind *asynch_ind_cb, ws_asynch_confirm *asynch_cnf_cb, ws_neighbor_info_request *ws_neighbor_info_request_cb)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (base) {
|
|
ws_llc_clean(base);
|
|
return 0;
|
|
}
|
|
|
|
//Allocate Data base
|
|
base = ws_llc_base_allocate();
|
|
if (!base) {
|
|
return -2;
|
|
}
|
|
|
|
base->interface_ptr = interface;
|
|
base->asynch_ind = asynch_ind_cb;
|
|
base->asynch_confirm = asynch_cnf_cb;
|
|
base->ws_neighbor_info_request_cb = ws_neighbor_info_request_cb;
|
|
//Register MAC Extensions
|
|
base->interface_ptr->mac_api->mac_mcps_extension_enable(base->interface_ptr->mac_api, &ws_llc_mac_indication_cb, &ws_llc_mac_confirm_cb, &ws_llc_ack_data_req_ext);
|
|
base->interface_ptr->mac_api->mac_mcps_edfe_enable(base->interface_ptr->mac_api, &ws_llc_mcps_edfe_handler);
|
|
//Init MPX class
|
|
ws_llc_mpx_init(&base->mpx_data_base);
|
|
ws_llc_temp_neigh_info_table_reset(base->temp_entries);
|
|
return 0;
|
|
}
|
|
|
|
int8_t ws_llc_delete(struct protocol_interface_info_entry *interface)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return -1;
|
|
}
|
|
|
|
ws_llc_clean(base);
|
|
|
|
ns_list_remove(&llc_data_base_list, base);
|
|
//Disable Mac extension
|
|
base->interface_ptr->mac_api->mac_mcps_extension_enable(base->interface_ptr->mac_api, NULL, NULL, NULL);
|
|
ns_dyn_mem_free(base->temp_entries);
|
|
ns_dyn_mem_free(base);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void ws_llc_reset(struct protocol_interface_info_entry *interface)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
ws_llc_clean(base);
|
|
}
|
|
|
|
mpx_api_t *ws_llc_mpx_api_get(struct protocol_interface_info_entry *interface)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return NULL;
|
|
}
|
|
return &base->mpx_data_base.mpx_api;
|
|
}
|
|
|
|
int8_t ws_llc_asynch_request(struct protocol_interface_info_entry *interface, asynch_request_t *request)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return -1;
|
|
}
|
|
|
|
//Calculate IE Buffer size
|
|
request->wh_requested_ie_list.fc_ie = false; //Never should not be a part Asynch message
|
|
request->wh_requested_ie_list.rsl_ie = false; //Never should not be a part Asynch message
|
|
request->wh_requested_ie_list.vh_ie = false;
|
|
uint16_t header_buffer_length = ws_wh_headers_length(request->wh_requested_ie_list, &base->ie_params);
|
|
uint16_t wp_nested_payload_length = ws_wp_nested_message_length(request->wp_requested_nested_ie_list, &base->ie_params);
|
|
|
|
//Allocated
|
|
uint16_t total_length = header_buffer_length;
|
|
if (wp_nested_payload_length) {
|
|
total_length += 2 + wp_nested_payload_length;
|
|
}
|
|
//Allocate LLC message pointer
|
|
|
|
llc_message_t *message = llc_message_allocate(total_length, base);
|
|
if (!message) {
|
|
if (base->asynch_confirm) {
|
|
base->asynch_confirm(interface, request->message_type);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//Add To active list
|
|
llc_message_id_allocate(message, base, false);
|
|
base->llc_message_list_size++;
|
|
ns_list_add_to_end(&base->llc_message_list, message);
|
|
message->messsage_type = request->message_type;
|
|
|
|
|
|
mcps_data_req_t data_req;
|
|
memset(&data_req, 0, sizeof(mcps_data_req_t));
|
|
data_req.SeqNumSuppressed = true;
|
|
data_req.SrcAddrMode = MAC_ADDR_MODE_64_BIT;
|
|
data_req.Key = request->security;
|
|
data_req.msduHandle = message->msg_handle;
|
|
data_req.ExtendedFrameExchange = false;
|
|
if (request->message_type == WS_FT_PAN_ADVERT_SOL) {
|
|
// PANID not know yet must be supressed
|
|
data_req.PanIdSuppressed = true;
|
|
}
|
|
|
|
uint8_t *ptr = ws_message_buffer_ptr_get(message);
|
|
|
|
message->ie_vector_list[0].ieBase = ptr;
|
|
message->ie_vector_list[0].iovLen = header_buffer_length;
|
|
|
|
message->ie_ext.headerIeVectorList = &message->ie_vector_list[0];
|
|
message->ie_ext.headerIovLength = 1;
|
|
|
|
|
|
//Write UTT
|
|
if (request->wh_requested_ie_list.utt_ie) {
|
|
ptr = ws_wh_utt_write(ptr, message->messsage_type);
|
|
}
|
|
|
|
if (request->wh_requested_ie_list.bt_ie) {
|
|
//Static 5 bytes allways
|
|
ptr = ws_wh_bt_write(ptr);
|
|
}
|
|
|
|
if (wp_nested_payload_length) {
|
|
message->ie_vector_list[1].ieBase = ptr;
|
|
message->ie_vector_list[1].iovLen = 2 + wp_nested_payload_length;
|
|
message->ie_ext.payloadIeVectorList = &message->ie_vector_list[1];
|
|
message->ie_ext.payloadIovLength = 1;
|
|
ptr = ws_wp_base_write(ptr, wp_nested_payload_length);
|
|
|
|
if (request->wp_requested_nested_ie_list.us_ie) {
|
|
//Write unicast schedule
|
|
ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, true);
|
|
}
|
|
|
|
if (request->wp_requested_nested_ie_list.bs_ie) {
|
|
//Write Broadcastcast schedule
|
|
ptr = ws_wp_nested_hopping_schedule_write(ptr, base->ie_params.hopping_schedule, false);
|
|
}
|
|
|
|
if (request->wp_requested_nested_ie_list.pan_ie) {
|
|
//Write Pan information
|
|
ptr = ws_wp_nested_pan_info_write(ptr, base->ie_params.pan_congiguration);
|
|
}
|
|
|
|
if (request->wp_requested_nested_ie_list.net_name_ie) {
|
|
//Write network name
|
|
ptr = ws_wp_nested_netname_write(ptr, base->ie_params.network_name, base->ie_params.network_name_length);
|
|
}
|
|
|
|
if (request->wp_requested_nested_ie_list.pan_version_ie) {
|
|
//Write pan version
|
|
ptr = ws_wp_nested_pan_ver_write(ptr, base->ie_params.pan_congiguration);
|
|
}
|
|
|
|
if (request->wp_requested_nested_ie_list.gtkhash_ie) {
|
|
//Write GTKHASH
|
|
ptr = ws_wp_nested_gtkhash_write(ptr, base->ie_params.gtkhash, base->ie_params.gtkhash_length);
|
|
}
|
|
|
|
if (request->wp_requested_nested_ie_list.vp_ie) {
|
|
//Write Vendor spesific payload
|
|
ptr = ws_wp_nested_vp_write(ptr, base->ie_params.vendor_payload, base->ie_params.vendor_payload_length);
|
|
}
|
|
}
|
|
|
|
base->interface_ptr->mac_api->mcps_data_req_ext(base->interface_ptr->mac_api, &data_req, &message->ie_ext, &request->channel_list);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void ws_llc_set_vendor_header_data(struct protocol_interface_info_entry *interface, uint8_t *vendor_header, uint8_t vendor_header_length)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
base->ie_params.vendor_header_data = vendor_header;
|
|
base->ie_params.vendor_header_length = vendor_header_length;
|
|
}
|
|
|
|
|
|
void ws_llc_set_vendor_payload_data(struct protocol_interface_info_entry *interface, uint8_t *vendor_payload, uint8_t vendor_payload_length)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
|
|
base->ie_params.vendor_payload = vendor_payload;
|
|
base->ie_params.vendor_payload_length = vendor_payload_length;
|
|
}
|
|
|
|
|
|
void ws_llc_set_network_name(struct protocol_interface_info_entry *interface, uint8_t *name, uint8_t name_length)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
|
|
base->ie_params.network_name = name;
|
|
base->ie_params.network_name_length = name_length;
|
|
}
|
|
|
|
void ws_llc_set_gtkhash(struct protocol_interface_info_entry *interface, uint8_t *gtkhash)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
|
|
base->ie_params.gtkhash = gtkhash;
|
|
if (base->ie_params.gtkhash) {
|
|
base->ie_params.gtkhash_length = 32;
|
|
} else {
|
|
base->ie_params.gtkhash_length = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ws_llc_set_pan_information_pointer(struct protocol_interface_info_entry *interface, struct ws_pan_information_s *pan_information_pointer)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
|
|
base->ie_params.pan_congiguration = pan_information_pointer;
|
|
}
|
|
|
|
void ws_llc_hopping_schedule_config(struct protocol_interface_info_entry *interface, struct ws_hopping_schedule_s *hopping_schedule)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
base->ie_params.hopping_schedule = hopping_schedule;
|
|
}
|
|
|
|
void ws_llc_fast_timer(struct protocol_interface_info_entry *interface, uint16_t ticks)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base || !base->edfe_rx_wait_timer) {
|
|
return;
|
|
}
|
|
|
|
if (ticks > 0xffff / 100) {
|
|
ticks = 0xffff;
|
|
} else if (ticks == 0) {
|
|
ticks = 1;
|
|
} else {
|
|
ticks *= 100;
|
|
}
|
|
|
|
if (base->edfe_rx_wait_timer > ticks) {
|
|
base->edfe_rx_wait_timer -= ticks;
|
|
} else {
|
|
base->edfe_rx_wait_timer = 0;
|
|
tr_debug("EDFE Data Wait Timeout");
|
|
//MAC edfe wait data timeout
|
|
if (interface->mac_api && interface->mac_api->mlme_req) {
|
|
mlme_set_t set_req;
|
|
uint8_t value = 0;
|
|
set_req.attr = macEdfeForceStop;
|
|
set_req.attr_index = 0;
|
|
set_req.value_pointer = &value;
|
|
set_req.value_size = 1;
|
|
interface->mac_api->mlme_req(interface->mac_api, MLME_SET, &set_req);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ws_llc_timer_seconds(struct protocol_interface_info_entry *interface, uint16_t seconds_update)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return;
|
|
}
|
|
|
|
ns_list_foreach_safe(ws_neighbor_temp_class_t, entry, &base->temp_entries->active_eapol_temp_neigh) {
|
|
if (entry->eapol_temp_info.eapol_timeout <= seconds_update) {
|
|
ns_list_remove(&base->temp_entries->active_eapol_temp_neigh, entry);
|
|
ns_list_add_to_end(&base->temp_entries->free_temp_neigh, entry);
|
|
} else {
|
|
entry->eapol_temp_info.eapol_timeout -= seconds_update;
|
|
if (entry->eapol_temp_info.eapol_rx_relay_filter == 0) {
|
|
//No active filter period
|
|
continue;
|
|
}
|
|
|
|
//Update filter time
|
|
if (entry->eapol_temp_info.eapol_rx_relay_filter <= seconds_update) {
|
|
entry->eapol_temp_info.eapol_rx_relay_filter = 0;
|
|
} else {
|
|
entry->eapol_temp_info.eapol_rx_relay_filter -= seconds_update;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ws_llc_eapol_relay_forward_filter(struct protocol_interface_info_entry *interface, const uint8_t *joiner_eui64, uint8_t mac_sequency, uint32_t rx_timestamp)
|
|
{
|
|
llc_data_base_t *base = ws_llc_discover_by_interface(interface);
|
|
if (!base) {
|
|
return false;
|
|
}
|
|
|
|
ws_neighbor_temp_class_t *neighbor = ws_llc_discover_eapol_temp_entry(base->temp_entries, joiner_eui64);
|
|
if (!neighbor) {
|
|
llc_neighbour_req_t neighbor_info;
|
|
//Discover here Normal Neighbour
|
|
if (!base->ws_neighbor_info_request_cb(interface, joiner_eui64, &neighbor_info, false)) {
|
|
return false;
|
|
}
|
|
return ws_neighbor_class_neighbor_duplicate_packet_check(neighbor_info.ws_neighbor, mac_sequency, rx_timestamp);
|
|
}
|
|
|
|
if (neighbor->eapol_temp_info.eapol_rx_relay_filter && neighbor->eapol_temp_info.last_rx_mac_sequency == mac_sequency) {
|
|
return false;
|
|
}
|
|
neighbor->eapol_temp_info.last_rx_mac_sequency = mac_sequency;
|
|
neighbor->eapol_temp_info.eapol_rx_relay_filter = 6; //Activate 5-5.99 seconds filter time
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
#endif
|