mbed-os/source/Service_Libs/mle_service/mle_service.c

1558 lines
48 KiB
C

/*
* Copyright (c) 2015-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_trace.h"
#include "eventOS_event.h"
#include "eventOS_scheduler.h"
#include "eventOS_event_timer.h"
#include "nsdynmemLIB.h"
#include "ns_list.h"
#include "randLIB.h"
#include "socket_api.h"
#include "Core/include/ns_socket.h"
#include "net_interface.h"
#include "common_functions.h"
#include "Common_Protocols/ipv6_constants.h"
#include "NWK_INTERFACE/Include/protocol.h" // just for protocol_core_monotonic_time
#include "Service_Libs/mle_service/mle_service_api.h"
#include "Service_Libs/mle_service/mle_service_security.h"
#include "Service_Libs/mle_service/mle_service_buffer.h"
#include "Service_Libs/mle_service/mle_service_interface.h"
#include "Service_Libs/mle_service/mle_service_frame_counter_table.h"
#include "MLE/mle.h"
#include "MLE/mle_tlv.h"
#include "mac_common_defines.h"
#include "6LoWPAN/MAC/mac_helper.h"
#define TRACE_GROUP "mleS"
/* Fixed-point randomisation limits for randlib_randomise_base() - RFC 3315
* says RAND is uniformly distributed between -0.1 and +0.1
*/
#define MLE_RAND_LOW 0x7333 // 1 - 0.1; minimum for "1+RAND"
#define MLE_RAND_HIGH 0x8CCD // 1 + 0.1; maximum for "1+RAND"
typedef struct {
int8_t mle_socket;
int8_t mle_socket_service_tasklet;
uint8_t mle_adata[46];
uint8_t mle_adata_length;
arm_event_storage_t *mle_service_timer_storage;
bool mle_frame_counter_check_enabled: 1;
bool mle_frag_msg_security_enabled: 1;
bool mle_accept_invalid_frame_counter: 1;
} mle_service_class_t;
#define MLE_SOCKET_SERVICE_TASKLET_INIT 1
#define MLE_SOCKET_SERVICE_TIMER 2
#define MLE_SOCKET_SERVICE_TIMER_ID 1
#define MLE_SOCKET_TIMER_UPDATE_PERIOD_IN_MS 100
mle_service_class_t *mle_service = NULL;
#ifdef MLE_TEST
static mle_service_filter_cb *receive_filter_cb = NULL;
#endif
static uint8_t *mle_security_aux_header_write(uint8_t *ptr, const mle_security_header_t *auxHeader);
static void mle_security_aux_ccm_nonce_set(uint8_t *noncePtr, uint8_t *mac64, uint32_t securityFrameCounter, uint8_t securityLevel);
static uint8_t mle_security_aux_header_size(uint8_t keyIdMode);
/**
* Enable service Timeout timer
*/
static void mle_service_timer_start(void);
/**
* Disable service Timeout timer
*/
static void mle_service_timer_stop(void);
/**
* MLE service event handler
*/
static void mle_socket_service_tasklet(arm_event_s *event);
/**
* MLE service allocated and initialized
*/
static bool mle_service_allocate(void);
/**
* Create TX buffer and add to list
*/
static mle_service_msg_buf_t *mle_tr_create(uint16_t buffer_length);
/**
* Parse Security Header
*/
static buffer_t *mle_service_parse_security_header(buffer_t *buf, mle_security_header_t *securityHeader,
uint16_t *header_len);
/**
* Decode secured message
*/
static buffer_t *mle_service_message_security_decode(buffer_t *buf, mle_security_header_t *securityHeader,
uint8_t *security_key);
/**
* Mle service generic MLE message handler
*/
static void mle_service_socket_callback(void *cb);
static int mle_service_build_packet_send(service_instance_t *srv_ptr, mle_security_components_t *sec_params, mle_service_msg_buf_t *buffer);
/**
* Check message sent tokens
*/
static bool mle_service_message_tokens_check(service_instance_t *cur_ptr, bool priority);
/**
* Service instance timeout handler
*/
static bool mle_service_instance_timeout_handler(uint16_t ticks, service_instance_t *cur_ptr);
static void mle_service_timer_start(void)
{
if (!mle_service->mle_service_timer_storage) {
arm_event_s event = {
.receiver = mle_service->mle_socket_service_tasklet,
.sender = 0,
.event_id = MLE_SOCKET_SERVICE_TIMER_ID,
.data_ptr = NULL,
.event_type = MLE_SOCKET_SERVICE_TIMER,
.priority = ARM_LIB_LOW_PRIORITY_EVENT,
};
mle_service->mle_service_timer_storage = eventOS_event_timer_request_every(&event, eventOS_event_timer_ms_to_ticks(MLE_SOCKET_TIMER_UPDATE_PERIOD_IN_MS));
if (!mle_service->mle_service_timer_storage) {
tr_error("Mle servicetimer start fail");
}
}
}
/**
* Disable service Timeout timer
*/
static void mle_service_timer_stop(void)
{
if (mle_service->mle_service_timer_storage) {
eventOS_cancel(mle_service->mle_service_timer_storage);
mle_service->mle_service_timer_storage = NULL;
}
}
static void mle_service_tr_timeout_handler(mle_service_msg_buf_t *cur_ptr)
{
uint16_t bufId = cur_ptr->msg_id;
mle_service_message_timeout_cb *timeout = NULL;
if (cur_ptr->timeout_cb && !cur_ptr->tokens_delay) {
timeout = cur_ptr->timeout_cb;
}
if (!cur_ptr->delayed_response && !cur_ptr->tokens_delay) {
//Update Retry Counter
cur_ptr->retrans++;
if (cur_ptr->retrans_max != 0) {
if (cur_ptr->retrans >= cur_ptr->retrans_max) {
// retransmission count exceeded.
if (timeout) {
timeout(cur_ptr->interfaceId, cur_ptr->msg_id, true);
}
//Cuold be call timeout cb here
mle_service_msg_free(bufId);
return;
} else {
if (timeout) {
if (!timeout(cur_ptr->interfaceId, cur_ptr->msg_id, false)) {
//User want to stop retry
mle_service_msg_free(bufId);
return;
}
}
}
} else {
mle_service_msg_free(bufId);
return;
}
}
service_instance_t *srv_ptr = mle_service_interface_find(cur_ptr->interfaceId);
mle_security_components_t *sec_params = mle_service_security_params_get(cur_ptr->interfaceId);
if (!srv_ptr || !sec_params) {
mle_service_msg_free(bufId);
return;
}
// Check if message needs to be delayed or removed because lack of message tokens
if (!mle_service_message_tokens_check(srv_ptr, cur_ptr->tokens_priority)) {
if (mle_service_buffer_tokens_delay_count() <= MLE_TOKEN_BUFFER_MAX_NBR) {
// If message timeout has occurred delay because of tokens
if (!cur_ptr->tokens_delay) {
cur_ptr->tokens_delay = true;
// Give time to wait free tokens
if (cur_ptr->timeout < MLE_TOKEN_DELAY) {
cur_ptr->timeout = MLE_TOKEN_DELAY;
}
}
// Delay has already been applied, now wait just for tokens
cur_ptr->delayed_response = MLE_NO_DELAY;
} else {
mle_service_msg_free(bufId);
tr_debug("MLE tokens message freed");
}
return;
} else {
cur_ptr->tokens_delay = false;
}
// Randomise Challenge TLV value
if (cur_ptr->challengePtr) {
randLIB_get_n_bytes_random(cur_ptr->challengePtr, cur_ptr->challengeLen);
}
//Trig Buffer to socket
mle_service_build_packet_send(srv_ptr, sec_params, cur_ptr);
if (cur_ptr->retrans_max == 0) {
mle_service_msg_free(bufId);//Remove packet which not need any retry after 1 retry
return;
}
//Trig Retry or first packet
if (cur_ptr->delayed_response) {
cur_ptr->delayed_response = MLE_NO_DELAY;
cur_ptr->timeout = cur_ptr->timeout_init;
return;
}
// RFC 3315 says:
// RT = 2*RTprev + RAND*RTprev,
// We calculate this as
// RT = RTprev + (1+RAND)*RTprev
cur_ptr->timeout = cur_ptr->timeout_init + randLIB_randomise_base(cur_ptr->timeout_init, MLE_RAND_LOW, MLE_RAND_HIGH);
// Catch 16-bit integer overflow
if (cur_ptr->timeout < cur_ptr->timeout_init) {
cur_ptr->timeout = 0xFFFF;
}
// Check against MRT
if (cur_ptr->timeout_max != 0 && cur_ptr->timeout > cur_ptr->timeout_max) {
cur_ptr->timeout = randLIB_randomise_base(cur_ptr->timeout_max, MLE_RAND_LOW, MLE_RAND_HIGH);
}
cur_ptr->timeout_init = cur_ptr->timeout;
}
/**
* MLE service timeout handling
*/
bool mle_service_timer_tick(uint16_t ticks)
{
if (!mle_service) {
tr_debug("MLE not ready");
return false;
}
bool active_timer_needed = false;
// MLE service interface instance timeout handling
if (mle_service_interface_timer_tick(ticks, mle_service_instance_timeout_handler)) {
active_timer_needed = true;
}
// MLE service transaction timeout and retry handling
if (mle_service_buffer_timer_tick(ticks, mle_service_tr_timeout_handler)) {
active_timer_needed = true;
}
return active_timer_needed;
}
static void mle_socket_service_tasklet(arm_event_s *event)
{
if (event->event_type == MLE_SOCKET_SERVICE_TIMER) {
if (!mle_service_timer_tick(1)) {
mle_service_timer_stop();
}
}
}
static bool mle_service_allocate(void)
{
if (mle_service) {
return true;
}
mle_service = ns_dyn_mem_alloc(sizeof(mle_service_class_t));
if (!mle_service) {
return false;
}
mle_service->mle_socket = -1;
mle_service->mle_socket_service_tasklet = eventOS_event_handler_create(mle_socket_service_tasklet, MLE_SOCKET_SERVICE_TASKLET_INIT);
if (mle_service->mle_socket_service_tasklet < 0) {
ns_dyn_mem_free(mle_service);
mle_service = NULL;
return false;
}
mle_service->mle_service_timer_storage = NULL;
mle_service->mle_frame_counter_check_enabled = false;
mle_service->mle_frag_msg_security_enabled = false;
mle_service->mle_accept_invalid_frame_counter = false;
return true;
}
static mle_service_msg_buf_t *mle_tr_create(uint16_t buffer_length)
{
uint16_t tr_id;
mle_service_msg_buf_t *msg_ptr;
msg_ptr = mle_service_buffer_get(buffer_length);
if (msg_ptr == NULL) {
return NULL;
}
tr_id = randLIB_get_16bit();// 16 bits for random
// Ensure a unique non-zero transaction id for each transaction
while (tr_id == 0 || mle_service_buffer_find(tr_id) != NULL) {
tr_id = tr_id + 1;
}
msg_ptr->msg_id = tr_id;
return msg_ptr;
}
static void mle_service_set_src_ll64(service_instance_t *srv_ptr, uint8_t *address)
{
memcpy(address, ADDR_LINK_LOCAL_PREFIX, 8);
address += 8;
memcpy(address, srv_ptr->mac64, 8);
*address ^= 2;
}
static buffer_t *mle_security_interface_aux_header_parse(buffer_t *b, mle_security_header_t *auxSecHeader)
{
uint8_t auxBaseHeader;
uint8_t *ptr = buffer_data_pointer(b);
auxBaseHeader = *ptr;
auxSecHeader->KeyIdMode = (auxBaseHeader >> 3) & 3;
auxSecHeader->securityLevel = auxBaseHeader & 7;
auxSecHeader->frameCounter = common_read_32_bit_inverse(ptr + 1);
ptr += 5;
switch (auxSecHeader->KeyIdMode) {
case MAC_KEY_ID_MODE_SRC8_IDX:
memcpy(auxSecHeader->Keysource, ptr, 8);
ptr += 8;
auxSecHeader->KeyIndex = *ptr++;
break;
case MAC_KEY_ID_MODE_SRC4_IDX:
memcpy(auxSecHeader->Keysource, ptr, 4);
ptr += 4;
auxSecHeader->KeyIndex = *ptr++;
break;
case MAC_KEY_ID_MODE_IDX:
auxSecHeader->KeyIndex = *ptr++;
break;
case MAC_KEY_ID_MODE_IMPLICIT:
default:
auxSecHeader->KeyIndex = 0;
break;
}
buffer_data_pointer_set(b, ptr);
if (buffer_data_length(b) < 0) {
b = buffer_free(b);
}
return b;
}
static buffer_t *mle_service_parse_security_header(buffer_t *buf, mle_security_header_t *securityHeader,
uint16_t *header_len)
{
uint16_t msg_len;
msg_len = buffer_data_length(buf);
buf = mle_security_interface_aux_header_parse(buf, securityHeader);
if (!buf) {
return NULL;
}
//Clear Headers off
*header_len = (msg_len - buffer_data_length(buf));
return buf;
}
/**
* Build MLE ADATA for CCM security
*/
static void mle_service_a_data_set(uint8_t *ptr, uint8_t *src_address, uint8_t *dest_address)
{
memcpy(ptr, src_address, 16);
ptr += 16;
memcpy(ptr, dest_address, 16);
}
static buffer_t *mle_service_message_security_decode(buffer_t *buf, mle_security_header_t *securityHeader,
uint8_t *security_key)
{
ccm_globals_t ccm_ptr;
uint16_t payload_len = buffer_data_length(buf);
if (!ccm_sec_init(&ccm_ptr, securityHeader->securityLevel, security_key, AES_CCM_DECRYPT, 2)) {
return buffer_free(buf);
} else if (ccm_ptr.mic_len >= payload_len) {
ccm_free(&ccm_ptr);
return buffer_free(buf);
}
//SET Nonce
buf->src_sa.address[8] ^= 2;
mle_security_aux_ccm_nonce_set(ccm_ptr.exp_nonce, &(buf->src_sa.address[8]),
securityHeader->frameCounter, securityHeader->securityLevel);
buf->src_sa.address[8] ^= 2;
if (ccm_ptr.mic_len) {
payload_len -= ccm_ptr.mic_len;
buf->buf_end -= ccm_ptr.mic_len;
ccm_ptr.data_ptr = buffer_data_pointer(buf);
ccm_ptr.adata_ptr = mle_service->mle_adata;
ccm_ptr.data_len = payload_len;
ccm_ptr.adata_len = mle_service->mle_adata_length;
//SET MIC
ccm_ptr.mic = ccm_ptr.data_ptr;
ccm_ptr.mic += payload_len;
} else {
ccm_ptr.data_len = payload_len;
ccm_ptr.data_ptr = buffer_data_pointer(buf);
}
if (ccm_process_run(&ccm_ptr) != 0) {
tr_error("MLE mic fail!");
buf = buffer_free(buf);
}
return buf;
}
static buffer_t *mle_message_security_header_set(buffer_t *buf, service_instance_t *srv_ptr, mle_security_components_t *sec_params, mle_security_header_t *security_header)
{
uint8_t *ptr;
//Verify first security level
if (security_header->securityLevel) {
//Get Security keys
ccm_globals_t ccm_ptr;
uint16_t header_size;
uint16_t data_len;
data_len = buffer_data_length(buf);
ptr = mle_service_security_get_key(security_header, sec_params, srv_ptr->interface_id);
if (!ptr) {
goto drop_buffer;
}
// Init
if (!ccm_sec_init(&ccm_ptr, security_header->securityLevel, ptr, AES_CCM_ENCRYPT, 2)) {
goto drop_buffer;
}
header_size = mle_security_aux_header_size(security_header->KeyIdMode);
//SET Nonce
mle_security_aux_ccm_nonce_set(ccm_ptr.exp_nonce, srv_ptr->mac64, security_header->frameCounter, security_header->securityLevel);
if (ccm_ptr.mic_len) {
buf = buffer_headroom(buf, (ccm_ptr.mic_len + 32 + header_size));
if (buf) {
uint8_t *ptr2;
//Move current data to left by mic_len bytes
ptr = buffer_data_pointer(buf);
//Set new data pointer
ptr2 = ptr;
ptr2 -= ccm_ptr.mic_len;
memmove(ptr2, ptr, data_len);
//Cut Mic len
buf->buf_end -= ccm_ptr.mic_len;
ptr -= header_size + ccm_ptr.mic_len;
ptr = mle_security_aux_header_write(ptr, security_header);
ptr -= header_size;
//Set pointer to Adata
ptr -= (32);
mle_service_a_data_set(ptr, buf->src_sa.address, buf->dst_sa.address);
//Create ADATA
ccm_ptr.adata_ptr = ptr;
ccm_ptr.adata_len = (32 + header_size);
//SET ptr to show to real payload
buf->buf_ptr -= ccm_ptr.mic_len;
} else {
tr_warn("Security header alloc fail");
ccm_free(&ccm_ptr);
buf = (buffer_t *) 0;
ccm_process_run(NULL);
return buf;
}
}
ptr = buffer_data_pointer(buf);
ccm_ptr.data_ptr = ptr;
ccm_ptr.data_len = data_len;
ccm_ptr.mic = ptr;
ccm_ptr.mic += data_len;
ccm_process_run(&ccm_ptr);
if (ccm_ptr.mic_len) {
//SET Calculated mic
buf->buf_end += ccm_ptr.mic_len;
}
buffer_data_reserve_header(buf, header_size);
}
buf = buffer_headroom(buf, 1);
if (buf) {
ptr = buffer_data_reserve_header(buf, 1);
if (security_header->securityLevel) {
*ptr++ = 0; // security
} else {
*ptr++ = 0xff; // No security
}
} else {
tr_warn("HeadRoom Fail");
}
return buf;
drop_buffer:
return buffer_free(buf);
}
static int mle_service_build_packet_send(service_instance_t *srv_ptr, mle_security_components_t *sec_params, mle_service_msg_buf_t *buffer)
{
buffer_t *buf;
uint8_t *ptr;
uint16_t header_length = 1; //Include security header
//allocate Buffer for MLE header and Message length
if (buffer->security_header.securityLevel) {
header_length += mle_security_aux_header_size(buffer->security_header.KeyIdMode);
header_length += 32; //AuthData
}
buf = buffer_get(buffer->buf_end + header_length);
if (!buf) {
return -1;
}
if (buffer->security_header.securityLevel) {
uint8_t *dada_ptr = buffer->buf + 1;
uint16_t dada_length = buffer->buf_end - 1;
mle_tlv_info_t option_info;
//Update mle frame counter evry time for every packet
buffer->security_header.frameCounter = mle_service_security_get_framecounter(sec_params, true);
buffer->security_header.KeyIndex = mle_service_security_get_default_key_id(sec_params);
//Update MAC frame Counter and MLE frame counter to current one allways
if (mle_tlv_option_discover(dada_ptr, dada_length, MLE_TYPE_LL_FRAME_COUNTER, &option_info) == 4) {
// GET frame counter from interface
uint32_t counter;
if (mac_helper_link_frame_counter_read(srv_ptr->interface_id, &counter) == 0) {
common_write_32_bit(counter, option_info.dataPtr);
}
}
if (mle_tlv_option_discover(dada_ptr, dada_length, MLE_TYPE_MLE_FRAME_COUNTER, &option_info) == 4) {
common_write_32_bit(buffer->security_header.frameCounter, option_info.dataPtr);
}
}
//XXX Everything below this point is so broken. API of socket_buffer_sendmsg says buf should not have any metadata
//Set LL64 Source (for mle_message_security_header_set, and sendmsg picks it up too, although it shouldn't)
mle_service_set_src_ll64(srv_ptr, buf->src_sa.address);
//Set Destination address (for benefit of mle_message_security_header_set, not sendmsg)
memcpy(buf->dst_sa.address, buffer->dst_address, 16);
// Normal Thread behaviour is for end devices to send multicasts as unicasts
// to parent - this overrides.
if (addr_is_ipv6_multicast(buffer->dst_address)) {
buf->options.ll_broadcast_tx = true;
}
buf->src_sa.addr_type = buf->dst_sa.addr_type = ADDR_IPV6;
buf->src_sa.port = buf->dst_sa.port = MLE_ALLOCATED_PORT;
//Copy Payload
ptr = buffer_data_pointer(buf);
memcpy(ptr, buffer->buf, buffer->buf_end);
buffer_data_length_set(buf, buffer->buf_end);
//Add security header and encode message
buf = mle_message_security_header_set(buf, srv_ptr, sec_params, &buffer->security_header);
if (!buf) {
return -1;
}
if (mle_service->mle_frag_msg_security_enabled) {
// Enable link layer security for fragmented packets
buf->options.ll_sec_bypass_frag_deny = true;
}
if (buffer->selected_channel) {
buf->link_specific.ieee802_15_4.rf_channel_switch = true;
buf->link_specific.ieee802_15_4.selected_channel = buffer->selected_rf_channel;
}
//Marks message sent
buffer->message_sent = true;
if (buffer->selected_pan_id) {
buf->link_specific.ieee802_15_4.useDefaultPanId = false;
buf->link_specific.ieee802_15_4.dstPanId = buffer->packet_panid;
}
int8_t security;
if (buffer->enable_link_layer_security) {
security = 1;
if (buffer->psk_key_id_mode_2) {
buf->link_specific.ieee802_15_4.key_id_mode = B_SECURITY_KEY_ID_2;
} else {
buf->link_specific.ieee802_15_4.key_id_mode = B_SECURITY_KEY_ID_MODE_DEFAULT;
}
} else {
security = 0;
}
socket_setsockopt(mle_service->mle_socket, SOCKET_IPPROTO_IPV6, SOCKET_LINK_LAYER_SECURITY, &security, sizeof(int8_t));
//sendmsg always takes destination from msg, not from the buffer, so must specify there
ns_address_t dst_addr;
dst_addr.type = ADDRESS_IPV6;
memcpy(dst_addr.address, buffer->dst_address, 16);
dst_addr.identifier = MLE_ALLOCATED_PORT;
ns_msghdr_t msg = {
.msg_name = &dst_addr,
.msg_namelen = sizeof dst_addr
};
//Push to socket
//XXX This is so broken. API of socket_buffer_sendmsg says buf should not have any metadata
socket_buffer_sendmsg(mle_service->mle_socket, buf, &msg, 0);
return 0;
}
static mle_neighbor_security_counter_info_t *mle_service_get_neighbour_info(protocol_interface_info_entry_t *cur_interface, uint8_t *ll64)
{
mac_neighbor_table_entry_t *neighbour = mac_neighbor_entry_get_by_ll64(mac_neighbor_info(cur_interface), ll64, false, NULL);
if (!neighbour) {
return NULL;
}
return mle_service_counter_info_get(cur_interface->id, neighbour->index);
}
static void mle_service_socket_callback(void *cb)
{
socket_buffer_callback_t *cb_buf = cb;
uint8_t mle_security_byte;
uint16_t security_header_length;
mle_security_header_t securityHeader;
mle_message_t mle_msg;
if (!cb_buf) {
tr_warn("mle sck cb without buf!");
return;
}
buffer_t *buf = cb_buf->buf;
if (cb_buf->event_type != SOCKET_DATA || !buf) {
return;
}
service_instance_t *service_handler = mle_service_interface_find(cb_buf->interface_id);
mle_security_components_t *sec_params = mle_service_security_params_get(cb_buf->interface_id);
if (!service_handler || !sec_params) {
tr_warn("service handler not registerd");
goto error_handler;
}
mle_msg.interface_ptr = service_handler->interface_ptr;
// MLE messages are only allowed to Link local address
if (!addr_is_ipv6_link_local(buf->src_sa.address) ||
addr_ipv6_scope(buf->dst_sa.address, NULL) != IPV6_SCOPE_LINK_LOCAL) {
// Security failure so drop packet
goto error_handler;
}
//Start Parsing Header
mle_security_byte = buffer_pull_uint8(buf);
if (mle_security_byte == 0xff) {
securityHeader.KeyIndex = 0;
securityHeader.KeyIdMode = MAC_KEY_ID_MODE_IDX;
securityHeader.securityLevel = 0;
} else if (mle_security_byte == 0) {
//SET adata
uint8_t *sec_header_ptr = buffer_data_pointer(buf);
buf = mle_service_parse_security_header(buf, &securityHeader, &security_header_length);
if (!buf) {
goto error_handler;
}
//SET adata and length
mle_service->mle_adata_length = (32 + security_header_length);
//copy data
mle_service_a_data_set(mle_service->mle_adata, buf->src_sa.address, buf->dst_sa.address);
memcpy(&mle_service->mle_adata[32], sec_header_ptr, security_header_length);
} else {
goto error_handler;
}
bool security_bypass;
securityHeader.invalid_frame_counter = false;
if (securityHeader.securityLevel) {
if (sec_params->sec_level == 0) {
tr_debug("Drop secured packet when security is disabled");
goto error_handler;
}
//Get A key here
uint8_t *key_ptr = mle_service_security_get_key(&securityHeader, sec_params, service_handler->interface_id);
if (!key_ptr) {
goto error_handler;
}
/* MLE neighbour table frame counter check */
mle_neighbor_security_counter_info_t *neighbour = NULL;
uint32_t keySeq;
if (securityHeader.KeyIdMode == MAC_KEY_ID_MODE_SRC4_IDX) {
keySeq = common_read_32_bit(securityHeader.Keysource);
} else {
keySeq = (uint32_t)securityHeader.KeyIndex;
}
if (mle_service->mle_frame_counter_check_enabled) {
neighbour = mle_service_get_neighbour_info(service_handler->interface_ptr, buf->src_sa.address);
if (neighbour) {
//key pending is set - incoming frame counter will be reset when new sequence is heard or lower framecounter is heard
if (neighbour->new_key_pending) {
if ((securityHeader.frameCounter < neighbour->mle_frame_counter || keySeq != neighbour->last_key_sequence)) {
neighbour->mle_frame_counter = 0;
neighbour->last_key_sequence = keySeq;
neighbour->new_key_pending = false;
goto start_decode; //Skip validation part
}
}
//key pending not set - continue normal operation
if (keySeq < neighbour->last_key_sequence) {
tr_warn("Dropping packet because key sequence smaller: %"PRIu32" < %"PRIu32,
keySeq, neighbour->last_key_sequence);
goto error_handler;
}
if (securityHeader.frameCounter <= neighbour->mle_frame_counter && keySeq == neighbour->last_key_sequence) {
if (mle_service->mle_accept_invalid_frame_counter) {
securityHeader.invalid_frame_counter = true;
} else {
tr_warn("Dropping packet MLE frame counter is not higher:%"PRIu32" <= %"PRIu32, securityHeader.frameCounter, neighbour->mle_frame_counter);
goto error_handler;
}
}
}
}
start_decode:
buf = mle_service_message_security_decode(buf, &securityHeader, key_ptr);
if (!buf) {
goto error_handler;
}
security_bypass = false;
if (mle_service->mle_frame_counter_check_enabled && !securityHeader.invalid_frame_counter) {
if (neighbour) {
neighbour->mle_frame_counter = securityHeader.frameCounter;
neighbour->last_key_sequence = keySeq;
}
}
} else {
if (sec_params->sec_level) {
if (!service_handler->recv_security_bypass_cb) {
tr_debug("Drop unsecured packet when security is enabled");
goto error_handler;
}
security_bypass = true;
} else {
security_bypass = false;
}
}
mle_msg.message_type = buffer_pull_uint8(buf);
mle_msg.data_length = buffer_data_length(buf);
mle_msg.data_ptr = buffer_data_pointer(buf);
mle_msg.dst_pan_id = buf->link_specific.ieee802_15_4.dstPanId;
mle_msg.src_pan_id = buf->link_specific.ieee802_15_4.srcPanId;
if (mle_message_malformed_check(mle_msg.data_ptr, mle_msg.data_length) != 0) {
tr_debug("Malfor");
goto error_handler;
}
mle_msg.packet_src_address = buf->src_sa.address;
mle_msg.packet_dst_address = buf->dst_sa.address;
mle_msg.dbm = buf->options.dbm;
mle_msg.lqi = buf->options.lqi;
#ifdef MLE_TEST
if (receive_filter_cb) {
if (!receive_filter_cb(service_handler->interface_id, &mle_msg, &securityHeader)) {
goto error_handler;
}
}
#endif
if (security_bypass) {
/* Security by pass message handler call */
service_handler->recv_security_bypass_cb(service_handler->interface_id, &mle_msg);
} else {
if (service_handler->recv_cb) {
service_handler->recv_cb(service_handler->interface_id, &mle_msg, &securityHeader);
}
}
error_handler:
if (buf) {
buffer_free(buf);
}
}
static bool mle_service_message_tokens_check(service_instance_t *cur_ptr, bool priority)
{
if (!cur_ptr || cur_ptr->token_bucket_size == 0) {
return true;
}
if (priority) {
if (cur_ptr->token_bucket > MLE_TOKEN_BUFFER_MIN_NBR) {
cur_ptr->token_bucket--;
}
return true;
}
if (cur_ptr->token_bucket > 0) {
cur_ptr->token_bucket--;
return true;
} else {
return false;
}
}
static bool mle_service_instance_timeout_handler(uint16_t ticks, service_instance_t *cur_ptr)
{
if (!cur_ptr || cur_ptr->token_bucket_size == 0 || cur_ptr->token_bucket_rate == 0) {
return false;
}
cur_ptr->token_bucket_ticks += ticks;
while (cur_ptr->token_bucket_ticks >= cur_ptr->token_bucket_rate) {
cur_ptr->token_bucket_ticks -= cur_ptr->token_bucket_rate;
cur_ptr->token_bucket += cur_ptr->token_bucket_count;
}
if (cur_ptr->token_bucket < cur_ptr->token_bucket_size) {
return true;
} else {
cur_ptr->token_bucket = cur_ptr->token_bucket_size;
return false;
}
}
int mle_service_interface_register(int8_t interface_id, void *interface_ptr, mle_service_receive_cb *receive_cb, uint8_t *mac64, uint8_t challengeLength)
{
service_instance_t *srv_ptr;
if (challengeLength < 4 || challengeLength > 32) {
return -1;
} else if (!receive_cb || !interface_ptr) {
return -1;
} else if (!mac64) {
return -1;
}
if (!mle_service_allocate()) {
tr_error("dhcp Sockets data base alloc fail");
return -1;
}
if (mle_service_security_instance_allocate(interface_id) != 0) {
return -1;
}
srv_ptr = mle_service_interface_get(interface_id);
if (!srv_ptr) {
goto error_handler;
}
srv_ptr->recv_cb = receive_cb;
srv_ptr->challenge_length = challengeLength;
srv_ptr->interface_ptr = interface_ptr;
memcpy(srv_ptr->mac64, mac64, 8);
if (mle_service->mle_socket < 0) {
if (socket_create(SOCKET_FAMILY_IPV6, SOCKET_TYPE_DGRAM, 0, &mle_service->mle_socket, MLE_ALLOCATED_PORT, mle_service_socket_callback, true) != eOK) {
mle_service->mle_socket = -1;
}
}
if (mle_service->mle_socket < 0) {
tr_error("Sockets not available");
goto error_handler;
} else {
const int16_t hops = 255;
const uint32_t address_pref = SOCKET_IPV6_PREFER_SRC_6LOWPAN_LONG;
const int8_t security = 0;
const bool loop = false;
socket_setsockopt(mle_service->mle_socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_UNICAST_HOPS, &hops, sizeof hops);
socket_setsockopt(mle_service->mle_socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_MULTICAST_HOPS, &hops, sizeof hops);
socket_setsockopt(mle_service->mle_socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_MULTICAST_LOOP, &loop, sizeof loop);
socket_setsockopt(mle_service->mle_socket, SOCKET_IPPROTO_IPV6, SOCKET_IPV6_ADDR_PREFERENCES, &address_pref, sizeof address_pref);
socket_setsockopt(mle_service->mle_socket, SOCKET_IPPROTO_IPV6, SOCKET_LINK_LAYER_SECURITY, &security, sizeof security);
//Set service interface id
socket_setsockopt(mle_service->mle_socket, SOCKET_IPPROTO_IPV6, SOCKET_INTERFACE_SELECT, &srv_ptr->interface_id, sizeof srv_ptr->interface_id);
}
return 0;
error_handler:
mle_service_interface_delete(interface_id);
mle_service_security_instance_delete(interface_id);
return -1;
}
bool mle_service_interface_registeration_validate(int8_t interface_id)
{
if (mle_service_interface_find(interface_id)) {
return true;
}
return false;
}
int mle_service_interface_receiver_handler_update(int8_t interface_id, mle_service_receive_cb *receive_cb)
{
service_instance_t *srv_ptr;
srv_ptr = mle_service_interface_find(interface_id);
if (!srv_ptr) {
return -1;
}
srv_ptr->recv_cb = receive_cb;
return 0;
}
int mle_service_interface_receiver_bypass_handler_update(int8_t interface_id, mle_service_receive_security_bypass_cb *receive_cb)
{
service_instance_t *srv_ptr;
srv_ptr = mle_service_interface_find(interface_id);
if (!srv_ptr) {
return -1;
}
srv_ptr->recv_security_bypass_cb = receive_cb;
return 0;
}
void mle_service_interface_unregister(int8_t interface_id)
{
//Remove service from list and free class
mle_service_interface_delete(interface_id);
//Remove Security Instance
mle_service_security_instance_delete(interface_id);
//Free Tx queue by unregistered interface id
mle_service_buffer_clean_buffers_by_interface_id(interface_id);
if (mle_service) {
if (mle_service_interface_list_empty()) {
tr_debug("Free Socket");
socket_close(mle_service->mle_socket);
mle_service->mle_socket = -1;
}
}
}
int mle_service_reset_frame_counters(int8_t interfaceId)
{
service_instance_t *srv_ptr = mle_service_interface_find(interfaceId);
if (!srv_ptr) {
return -1;
}
mle_service_security_set_frame_counter(interfaceId, 0);
mle_class_set_new_key_pending(srv_ptr->interface_ptr);
return 0;
}
void mle_service_interface_tx_queue_clean(int8_t interface_id)
{
mle_service_buffer_clean_buffers_by_interface_id(interface_id);
}
uint16_t mle_service_interface_tx_queue_size(int8_t interface_id)
{
return mle_service_buffer_count_by_interface_id(interface_id);
}
int mle_service_interface_token_bucket_settings_set(int8_t interface_id, uint8_t size, uint8_t rate, uint8_t count)
{
service_instance_t *srv_ptr;
srv_ptr = mle_service_interface_find(interface_id);
if (!srv_ptr) {
return -1;
}
srv_ptr->token_bucket = size;
srv_ptr->token_bucket_ticks = 0;
srv_ptr->token_bucket_size = size;
srv_ptr->token_bucket_rate = rate;
srv_ptr->token_bucket_count = count;
return 0;
}
uint16_t mle_service_msg_allocate(int8_t interface_id, uint16_t data_length, bool challengeAllocate, uint8_t type)
{
uint16_t temporary_length = 64;
mle_service_msg_buf_t *buf;
//Verify that Interface is registered
service_instance_t *srv_ptr = mle_service_interface_find(interface_id);
if (!srv_ptr) {
return 0;
}
mle_security_components_t *sec_params = mle_service_security_params_get(interface_id);
if (!sec_params) {
return 0;
}
if (data_length > 63) {
temporary_length = data_length + 1;
}
if (challengeAllocate) {
temporary_length += (srv_ptr->challenge_length + 2);
}
buf = mle_tr_create(temporary_length);
if (!buf) {
return 0;
}
//Set interface id to buffer
buf->interfaceId = interface_id;
mle_service_buffer_set_msg_type(buf->msg_id, type);
if (challengeAllocate) {
//SET Challenge all ways to front
uint8_t *ptr = mle_msg_data_pointer(buf);
*ptr++ = MLE_TYPE_CHALLENGE;
*ptr++ = srv_ptr->challenge_length;
buf->challengeLen = srv_ptr->challenge_length;
buf->challengePtr = ptr;
randLIB_get_n_bytes_random(ptr, srv_ptr->challenge_length);
mle_msg_length_set(buf, (srv_ptr->challenge_length + 2));
} else {
//NO retry by default
buf->timeout_init = buf->timeout = buf->timeout_max = 0;
buf->retrans_max = 0;
}
//Set default security level and key id mode
//Key id, frame counter will be updated when default need to be change
buf->security_header.securityLevel = sec_params->sec_level;
buf->security_header.KeyIdMode = MAC_KEY_ID_MODE_IDX;
return buf->msg_id;
}
uint8_t *mle_service_get_data_pointer(uint16_t msgId)
{
return mle_service_buffer_get_data_pointer(msgId);
}
uint8_t *mle_service_get_payload_start_point(uint16_t msgId)
{
mle_service_msg_buf_t *buf = mle_service_buffer_find(msgId);
if (!buf) {
return NULL;
}
uint8_t *ptr = buf->buf;
return (ptr + 1);
}
uint16_t mle_service_get_payload_length(uint16_t msgId)
{
mle_service_msg_buf_t *buf = mle_service_buffer_find(msgId);
if (!buf) {
return 0;
}
if (buf->buf_end == 0) {
return 0;
}
return (buf->buf_end - 1);
}
int mle_service_update_length(uint16_t msgId, uint16_t tail_length)
{
return mle_service_buffer_update_length(msgId, tail_length);
}
int mle_service_update_length_by_ptr(uint16_t msgId, uint8_t *data_end_ptr)
{
return mle_service_buffer_update_length_by_ptr(msgId, data_end_ptr);
}
int mle_service_msg_update_security_params(uint16_t msgId, uint8_t security_level, uint8_t key_id_mode, uint32_t keysequence)
{
mle_service_msg_buf_t *buf = mle_service_buffer_find(msgId);
if (!buf) {
return -1;
}
if (security_level > AES_SECURITY_LEVEL_ENC_MIC128) {
return -2;
} else if (security_level && key_id_mode != MAC_KEY_ID_MODE_IDX && key_id_mode != MAC_KEY_ID_MODE_SRC4_IDX) {
return -2;
}
buf->security_header.securityLevel = security_level;
buf->security_header.KeyIdMode = key_id_mode;
common_write_32_bit(keysequence, buf->security_header.Keysource);
return 0;
}
int mle_service_set_packet_callback(uint16_t msgId, mle_service_message_timeout_cb *cb)
{
return mle_service_buffer_set_timeout_cb(msgId, cb);
}
int mle_service_set_msg_response_true(uint16_t msgId)
{
return mle_service_buffer_set_response(msgId);
}
uint8_t *mle_service_get_msg_destination_address_pointer(uint16_t msgId)
{
return mle_service_buffer_get_msg_destination_address_pointer(msgId);
}
int mle_service_set_msg_destination_address(uint16_t msgId, const uint8_t *dstAddress)
{
if (!dstAddress) {
return -2;
}
uint8_t *address_ptr = mle_service_buffer_get_msg_destination_address_pointer(msgId);
if (!address_ptr) {
return -1;
}
memcpy(address_ptr, dstAddress, 16);
return 0;
}
int mle_service_set_msg_panid(uint16_t msgId, uint16_t panid)
{
mle_service_msg_buf_t *buf = mle_service_buffer_find(msgId);
if (!buf) {
return -1;
}
buf->selected_pan_id = true;
buf->packet_panid = panid;
return 0;
}
int mle_service_msg_free(uint16_t msgId)
{
return mle_service_buffer_free(mle_service_buffer_find(msgId));
}
bool mle_service_transaction_buffer_get_for_response(uint8_t *responseData, uint16_t length, uint16_t *msgId)
{
mle_service_msg_buf_t *buffer = mle_service_buffer_find_for_response(responseData, length);
if (!buffer) {
return false;
}
*msgId = buffer->msg_id;
return true;
}
bool mle_service_check_msg_sent(uint16_t msgId)
{
mle_service_msg_buf_t *buf = mle_service_buffer_find(msgId);
if (!buf) {
return false;
}
return buf->message_sent;
}
int mle_service_message_tail_get(uint16_t msgId, uint16_t tail_length)
{
return mle_service_buffer_tail_get(msgId, tail_length);
}
static int mle_service_timeout_fill(uint16_t msgId, mle_message_timeout_params_t *timeout_params, bool timeout_in_seconds)
{
mle_service_msg_buf_t *buffer = mle_service_buffer_find(msgId);
if (!buffer) {
return -1;
}
buffer->timeout_max = timeout_params->timeout_max;
buffer->retrans_max = timeout_params->retrans_max;
buffer->delayed_response = timeout_params->delay;
buffer->timeout_init = timeout_params->timeout_init;
if (timeout_in_seconds) {
buffer->timeout_max = buffer->timeout_max * 10;
buffer->timeout_init = buffer->timeout_init * 10;
}
buffer->timeout_init = randLIB_randomise_base(buffer->timeout_init, MLE_RAND_LOW, MLE_RAND_HIGH);
buffer->timeout = buffer->timeout_init;
return 0;
}
int mle_service_set_msg_timeout_parameters(uint16_t msgId, mle_message_timeout_params_t *timeout_params)
{
return mle_service_timeout_fill(msgId, timeout_params, true);
}
int mle_service_set_msg_timeout_parameters_fast(uint16_t msgId, mle_message_timeout_params_t *timeout_params)
{
return mle_service_timeout_fill(msgId, timeout_params, false);
}
int mle_service_set_msg_token_bucket_priority(uint16_t msgId)
{
mle_service_msg_buf_t *buffer = mle_service_buffer_find(msgId);
if (!buffer) {
return -1;
}
buffer->tokens_priority = true;
return 0;
}
int mle_service_send_message(uint16_t msgId)
{
mle_service_msg_buf_t *buffer = mle_service_buffer_find(msgId);
if (!buffer) {
return -1;
}
service_instance_t *srv_ptr = mle_service_interface_find(buffer->interfaceId);
mle_security_components_t *sec_params = mle_service_security_params_get(buffer->interfaceId);
if (!srv_ptr || !sec_params) {
mle_service_buffer_free(buffer);
return -1;
}
//Trig timer
mle_service_timer_start();
if (buffer->delayed_response) {
//set random jitter for response
buffer->timeout = randLIB_get_random_in_range(1, buffer->delayed_response);
return 0;
}
// Check if message needs to be delayed or removed because lack of message tokens
if (!mle_service_message_tokens_check(srv_ptr, buffer->tokens_priority)) {
if (mle_service_buffer_tokens_delay_count() <= MLE_TOKEN_BUFFER_MAX_NBR) {
buffer->tokens_delay = true;
// Give time to wait free tokens
if (buffer->timeout < MLE_TOKEN_DELAY) {
buffer->timeout = MLE_TOKEN_DELAY;
}
} else {
mle_service_buffer_free(mle_service_buffer_find(buffer->msg_id));
tr_debug("MLE tokens message freed");
}
return 0;
}
if (mle_service_build_packet_send(srv_ptr, sec_params, buffer) != 0) {
tr_warn("MLE message sending failed, ipv6=%s", trace_ipv6(buffer->dst_address));
buffer->delayed_response = 1;
buffer->timeout = 1;
buffer->retrans_max++;
} else {
if (buffer->retrans_max == 0 && !buffer->timeout) {
//Free msg
mle_service_buffer_free(mle_service_buffer_find(buffer->msg_id));
}
}
return 0;
}
int mle_service_security_init(int8_t interfaceId, uint8_t security_level, uint32_t security_frame_counter, mle_service_key_request_by_counter_cb *key_counter_req, mle_service_security_notify_cb *notify)
{
mle_security_components_t *srv_ptr = mle_service_security_params_get(interfaceId);
if (!srv_ptr) {
return -1;
}
if (security_level > AES_SECURITY_LEVEL_ENC_MIC128) {
return -2;
}
mle_service_security_parameters_init(srv_ptr);
srv_ptr->sec_level = security_level;
srv_ptr->key_req = key_counter_req;
srv_ptr->security_notify = notify;
srv_ptr->security_frame_counter = security_frame_counter;
return 0;
}
bool mle_service_security_set_frame_counter(int8_t interfaceId, uint32_t frame_counter)
{
mle_security_components_t *sec_ptr = mle_service_security_params_get(interfaceId);
if (!sec_ptr) {
return false;
}
sec_ptr->security_frame_counter = frame_counter;
return true;
}
uint32_t mle_service_security_get_frame_counter(int8_t interfaceId)
{
mle_security_components_t *sec_ptr = mle_service_security_params_get(interfaceId);
if (!sec_ptr) {
return 0;
}
return sec_ptr->security_frame_counter;
}
uint8_t *mle_service_security_default_key_get(int8_t interfaceId)
{
mle_security_components_t *sec_ptr = mle_service_security_params_get(interfaceId);
if (!sec_ptr) {
return NULL;
}
return mle_service_security_get_default_key(sec_ptr);
}
uint8_t mle_service_security_default_key_id_get(int8_t interfaceId)
{
mle_security_components_t *sec_ptr = mle_service_security_params_get(interfaceId);
if (!sec_ptr) {
return 0;
}
return mle_service_security_get_default_key_id(sec_ptr);
}
uint8_t *mle_service_security_next_key_get(int8_t interfaceId)
{
mle_security_components_t *sec_ptr = mle_service_security_params_get(interfaceId);
if (!sec_ptr) {
return NULL;
}
return mle_service_security_get_next_key(sec_ptr);
}
uint8_t mle_service_security_next_key_id_get(int8_t interfaceId)
{
mle_security_components_t *sec_ptr = mle_service_security_params_get(interfaceId);
if (!sec_ptr) {
return 0;
}
return mle_service_security_get_next_key_id(sec_ptr);
}
uint8_t mle_service_security_level_get(int8_t interfaceId)
{
mle_security_components_t *sec_ptr = mle_service_security_params_get(interfaceId);
if (!sec_ptr) {
return 0;
}
return sec_ptr->sec_level;
}
bool mle_service_security_key_trig(int8_t interfaceId, uint8_t keyId)
{
return mle_service_security_key_update_trig(interfaceId, mle_service_security_params_get(interfaceId), keyId);
}
bool mle_service_security_set_security_key(int8_t interfaceId, const uint8_t *security_key, uint8_t keyId, bool primary)
{
bool master_key_changed = mle_service_security_key_set(mle_service_security_params_get(interfaceId), security_key, keyId, primary);
if (master_key_changed && primary) {
mle_service_reset_frame_counters(interfaceId);
}
return master_key_changed;
}
int mle_service_reject_message_build(int8_t interfaceId, uint8_t *dstIpv6, bool force_unsecure)
{
uint16_t buf_id = mle_service_msg_allocate(interfaceId, 32, false, MLE_COMMAND_REJECT);
if (buf_id == 0) {
return -1;
}
tr_debug("MLE Reject MSG Build");
if (force_unsecure) {
mle_service_msg_update_security_params(buf_id, 0, 0, 0);
}
//SET Destination address
mle_service_set_msg_destination_address(buf_id, dstIpv6);
//Set message priority
mle_service_set_msg_token_bucket_priority(buf_id);
mle_service_send_message(buf_id);
return 0;
}
static uint8_t *mle_security_aux_header_write(uint8_t *ptr, const mle_security_header_t *auxHeader)
{
uint8_t auxBaseHeader;
auxBaseHeader = auxHeader->securityLevel;
auxBaseHeader |= (auxHeader->KeyIdMode << 3);
*ptr++ = auxBaseHeader;
ptr = common_write_32_bit_inverse(auxHeader->frameCounter, ptr);
switch (auxHeader->KeyIdMode) {
case MAC_KEY_ID_MODE_SRC8_IDX:
memcpy(ptr, auxHeader->Keysource, 8);
ptr += 8;
*ptr++ = auxHeader->KeyIndex;
break;
case MAC_KEY_ID_MODE_SRC4_IDX:
memcpy(ptr, auxHeader->Keysource, 4);
ptr += 4;
*ptr++ = auxHeader->KeyIndex;
break;
case MAC_KEY_ID_MODE_IDX:
*ptr++ = auxHeader->KeyIndex;
break;
case MAC_KEY_ID_MODE_IMPLICIT:
default:
break;
}
return ptr;
}
static void mle_security_aux_ccm_nonce_set(uint8_t *noncePtr, uint8_t *mac64, uint32_t securityFrameCounter, uint8_t securityLevel)
{
memcpy(noncePtr, mac64, 8);
noncePtr += 8;
noncePtr = common_write_32_bit(securityFrameCounter, noncePtr);
*noncePtr = securityLevel;
}
static uint8_t mle_security_aux_header_size(uint8_t keyIdMode)
{
uint8_t auxHeaderLength;
if (keyIdMode == 0) {
auxHeaderLength = 5; //No Key ID selected
} else if (keyIdMode == 1) {
auxHeaderLength = 6; //key ID without Source
} else if (keyIdMode == 2) {
auxHeaderLength = 10; //key ID with 32-bitSource
} else {
auxHeaderLength = 14; //key ID with 64-bitSource
}
return auxHeaderLength;
}
void mle_service_set_frame_counter_check(bool value)
{
if (mle_service) {
mle_service->mle_frame_counter_check_enabled = value;
}
}
void mle_service_set_fragmented_msg_ll_security(bool value)
{
if (mle_service) {
mle_service->mle_frag_msg_security_enabled = value;
}
}
int mle_service_set_msg_rf_channel(uint16_t msgId, uint8_t channel)
{
return mle_service_buffer_set_msg_rf_channel(msgId, channel);
}
int mle_service_set_msg_link_layer_security_mode(uint16_t msgId, bool use_key_id_mode_2)
{
return mle_service_buffer_set_msg_security_mode(msgId, use_key_id_mode_2);
}
void mle_service_set_accept_invalid_frame_counter(bool value)
{
if (mle_service) {
mle_service->mle_accept_invalid_frame_counter = value;
}
}
#ifdef MLE_TEST
void mle_service_receive_filter_cb_set(mle_service_filter_cb *filter_cb)
{
receive_filter_cb = filter_cb;
}
#endif