mbed-os/source/6LoWPAN/ws/ws_llc_data_service.c

1257 lines
45 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 "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 */
unsigned messsage_type: 3; /**< Frame type to UTT */
unsigned mpx_id: 5; /**< MPX sequence */
bool ack_requested: 1; /**< ACK requested */
unsigned dst_address_type: 2; /**< Destination 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_TEMPORRY_MULTICAST_SIZE 5
typedef struct {
ws_neighbor_temp_class_t neighbour_temporary_table[MAX_NEIGH_TEMPORRY_MULTICAST_SIZE];
ws_neighbor_temp_list_t active_temp_neigh;
ws_neighbor_temp_list_t free_temp_neigh;
} temp_entriest_t;
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 */
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_ack_elements[WH_IE_ELEMENT_HEADER_LENGTH + 4 + WH_IE_ELEMENT_HEADER_LENGTH + 1];
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 llc_message_t *llc_message_allocate(uint16_t ie_buffer_size, llc_data_base_t *llc_base, bool mpx_user);
/** 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);
/** 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 llc_message_t *llc_message_allocate(uint16_t ie_buffer_size, llc_data_base_t *llc_base, bool mpx_user)
{
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;
//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++;
}
llc_base->llc_message_list_size++;
ns_list_add_to_end(&llc_base->llc_message_list, message);
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 + 1;
}
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_temp_neigh);
ns_list_init(&temp_entries->free_temp_neigh);
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;
//ETX update
if (message->ack_requested && messsage_type == WS_FT_DATA) {
llc_neighbour_req_t neighbor_info;
bool success = 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 (message->dst_address_type == MAC_ADDR_MODE_64_BIT && base->ws_neighbor_info_request_cb(interface, message->dst_address, &neighbor_info, false)) {
etx_transm_attempts_update(interface->id, 1 + data->tx_retries, success, neighbor_info.neighbor->index);
//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);
}
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;
}
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);
}
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_ack_elements;
base->ws_header_vector.iovLen = sizeof(base->ws_enhanced_ack_elements);
data->ie_elements.headerIovLength = 1;
//Write Data to block
uint8_t *ptr = base->ws_enhanced_ack_elements;
ptr = ws_wh_utt_write(ptr, WS_FT_ACK);
ws_wh_rsl_write(ptr, ws_neighbor_class_rsl_from_dbm_calculate(rssi));
}
/** 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)
{
llc_data_base_t *base = ws_llc_discover_by_mac(api);
if (!base) {
return;
}
//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;
}
protocol_interface_info_entry_t *interface = base->interface_ptr;
if (!base->ie_params.gtkhash && ws_utt.message_type == WS_FT_DATA) {
return;
}
//Discover 2 Payload Heder
if (ws_utt.message_type == WS_FT_DATA || ws_utt.message_type == WS_FT_EAPOL) {
if (data->SrcAddrMode != ADDR_802_15_4_LONG) {
return;
}
if (interface->mac_parameters->pan_id != 0xffff && data->SrcPANId != interface->mac_parameters->pan_id) {
//Drop wrong PAN-id messages in this phase.
return;
}
mpx_user_t *user_cb;
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;
}
//Validate MPX header
mpx_msg_t mpx_frame;
if (!ws_llc_mpx_header_frame_parse(mpx_ie.content_ptr, mpx_ie.length, &mpx_frame)) {
return;
}
if (mpx_frame.transfer_type != MPX_FT_FULL_FRAME) {
return; //Support only FULL Frame's
}
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);
}
//Validate Unicast shedule Channel Plan
if (us_ie_inline && !ws_bootstrap_validate_channel_plan(&us_ie, interface)) {
//Channel plan configuration mismatch
return;
}
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 || ws_utt.message_type == WS_FT_EAPOL) {
//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;
}
}
ws_neighbor_class_neighbor_unicast_time_info_update(neighbor_info.ws_neighbor, &ws_utt, data->timestamp);
if (us_ie_inline) {
ws_neighbor_class_neighbor_unicast_schedule_set(neighbor_info.ws_neighbor, &us_ie);
}
//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);
}
if (ws_utt.message_type == WS_FT_EAPOL) {
uint8_t auth_eui64[8];
//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) {
if (neighbor_info.neighbor->link_role == PRIORITY_PARENT_NEIGHBOUR) {
// We have broadcast schedule set up set the broadcast parent schedule
ns_fhss_ws_set_parent(interface->ws_info->fhss_api, neighbor_info.neighbor->mac64, &neighbor_info.ws_neighbor->fhss_data.bc_timing_info, false);
} else if (ws_utt.message_type == WS_FT_EAPOL) {
ws_bootstrap_eapol_parent_synch(interface, &neighbor_info);
}
}
}
if (ws_utt.message_type == WS_FT_DATA) {
if (data->DstAddrMode == ADDR_802_15_4_LONG) {
neighbor_info.ws_neighbor->unicast_data_rx = true;
}
// Calculate RSL for all UDATA packages heard
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);
if (data->Key.SecurityLevel) {
//SET trusted state
mac_neighbor_table_trusted_neighbor(mac_neighbor_info(interface), neighbor_info.neighbor, true);
}
}
}
// Discover MPX
user_cb = ws_llc_mpx_user_discover(&base->mpx_data_base, mpx_frame.multiplex_id);
if (user_cb && user_cb->data_ind) {
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);
}
return;
}
//Asynch Message
if (ws_utt.message_type < WS_FT_DATA && base->asynch_ind) {
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;
}
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(interface, data, &asynch_ie_list, ws_utt.message_type);
}
}
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_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;
}
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;
if (user_id == MPX_LOWPAN_ENC_USER_ID) {
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->TxAckReq) {
nested_wp_id.bs_ie = true;
}
} else if (user_id == MPX_KEY_MANAGEMENT_ENC_USER_ID) {
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, true);
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;
}
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->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);
if (user_id == MPX_LOWPAN_ENC_USER_ID) {
message->messsage_type = WS_FT_DATA;
} else {
message->messsage_type = WS_FT_EAPOL;
}
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 (user_id == MPX_LOWPAN_ENC_USER_ID) {
if (ie_header_mask.vh_ie) {
ptr = ws_wh_vh_write(ptr, base->ie_params.vendor_header_data, base->ie_params.vendor_header_length);
}
} else if (user_id == MPX_KEY_MANAGEMENT_ENC_USER_ID) {
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);
}
}
ptr = mac_ie_payload_base_write(ptr, MAC_PAYLOAD_MPX_IE_GROUP_ID, data->msduLength + 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;
message->ie_vector_list[2].ieBase = data->msdu;
message->ie_vector_list[2].iovLen = data->msduLength;
base->interface_ptr->mac_api->mcps_data_req_ext(base->interface_ptr->mac_api, &data_req, &message->ie_ext, NULL);
}
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);
}
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_temp_neigh);
ns_list_init(&base->free_temp_neigh);
//Add to free list to full
for (int i = 0; i < MAX_NEIGH_TEMPORRY_MULTICAST_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_temp_entry(temp_entriest_t *base, const uint8_t *mac64)
{
ns_list_foreach_safe(ws_neighbor_temp_class_t, entry, &base->active_temp_neigh) {
if (memcmp(entry->mac64, mac64, 8) == 0) {
return entry;
}
}
return NULL;
}
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_temp_entry(base->temp_entries, mac64);
}
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_temp_entry(base, mac64);
if (entry) {
ns_list_remove(&base->active_temp_neigh, entry);
ns_list_add_to_start(&base->active_temp_neigh, entry);
return entry;
}
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_temp_neigh);
ns_list_remove(&base->active_temp_neigh, entry);
}
//Add to list
ns_list_add_to_start(&base->active_temp_neigh, entry);
//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);
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_temp_neigh, neighbor);
ns_list_add_to_end(&base->temp_entries->free_temp_neigh, neighbor);
}
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);
//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, false);
if (!message) {
if (base->asynch_confirm) {
base->asynch_confirm(interface, request->message_type);
}
return 0;
}
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;
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;
}
#endif