mirror of https://github.com/ARMmbed/mbed-os.git
368 lines
9.9 KiB
C
368 lines
9.9 KiB
C
/*
|
|
* Copyright (c) 2014-2017, 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 "ns_trace.h"
|
|
#include "string.h"
|
|
#include "MLE/mle.h"
|
|
#include "MLE/mle_tlv.h"
|
|
#include "common_functions.h"
|
|
#include "Service_Libs/mle_service/mle_service_api.h"
|
|
|
|
int mle_message_malformed_check(uint8_t *ptr, uint16_t data_len)
|
|
{
|
|
uint8_t *dptr;
|
|
uint16_t length;
|
|
dptr = ptr;
|
|
while (data_len) {
|
|
if (data_len >= 2) {
|
|
dptr += 1; //Skip TLV Type
|
|
length = *dptr++;
|
|
if (length == 0xff) {
|
|
// Long length format
|
|
data_len -= 2;
|
|
if (data_len < 2) {
|
|
return -1;
|
|
}
|
|
length = common_read_16_bit(dptr);
|
|
dptr += 2;
|
|
}
|
|
data_len -= 2;
|
|
if (data_len >= length) {
|
|
if (length) {
|
|
data_len -= length;
|
|
dptr += length;
|
|
}
|
|
} else {
|
|
// buffer is overrun this is malformed.
|
|
return -1;
|
|
}
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int mle_tlv_option_discover(uint8_t *ptr, uint16_t data_len, mle_tlv_type_t discovered_type, mle_tlv_info_t *option_info)
|
|
{
|
|
uint8_t *dptr;
|
|
uint16_t length;
|
|
mle_tlv_type_t type;
|
|
|
|
option_info->tlvLen = 0;
|
|
option_info->tlvType = MLE_TYPE_UNASSIGNED;
|
|
option_info->dataPtr = NULL;
|
|
dptr = ptr;
|
|
while (data_len) {
|
|
type = (mle_tlv_type_t) * dptr++;
|
|
length = *dptr++;
|
|
if (length == 0xff) {
|
|
// Long length format
|
|
data_len -= 2;
|
|
if (data_len < 2) {
|
|
return -1;
|
|
}
|
|
length = common_read_16_bit(dptr);
|
|
dptr += 2;
|
|
}
|
|
data_len -= 2;
|
|
if (data_len >= length) {
|
|
if (type == discovered_type) {
|
|
option_info->tlvLen = length;
|
|
option_info->tlvType = type;
|
|
option_info->dataPtr = dptr;
|
|
return length;
|
|
} else {
|
|
data_len -= length;
|
|
dptr += length;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool mle_tlv_type_requested(uint8_t reqType, uint8_t *ptr, uint16_t data_len)
|
|
{
|
|
mle_tlv_info_t tlv_info;
|
|
bool retVal = false;
|
|
if (mle_tlv_option_discover(ptr, data_len, MLE_TYPE_TLV_REQUEST, &tlv_info) > 0) {
|
|
uint16_t i;
|
|
uint8_t *t_ptr;
|
|
t_ptr = tlv_info.dataPtr;
|
|
for (i = 0; i < tlv_info.tlvLen; i++) {
|
|
if (*t_ptr++ == reqType) {
|
|
retVal = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
bool mle_tlv_read_8_bit_tlv(mle_tlv_type_t reqType, uint8_t *ptr, uint16_t data_len, uint8_t *buffer)
|
|
{
|
|
mle_tlv_info_t tlv_info;
|
|
if (mle_tlv_option_discover(ptr, data_len, reqType, &tlv_info) >= 1) {
|
|
uint8_t *t_ptr;
|
|
t_ptr = tlv_info.dataPtr;
|
|
*buffer = *t_ptr;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool mle_tlv_read_16_bit_tlv(mle_tlv_type_t reqType, uint8_t *ptr, uint16_t data_len, uint16_t *buffer)
|
|
{
|
|
mle_tlv_info_t tlv_info;
|
|
if (mle_tlv_option_discover(ptr, data_len, reqType, &tlv_info) >= 2) {
|
|
*buffer = common_read_16_bit(tlv_info.dataPtr);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool mle_tlv_read_32_bit_tlv(mle_tlv_type_t reqType, uint8_t *ptr, uint16_t data_len, uint32_t *buffer)
|
|
{
|
|
mle_tlv_info_t tlv_info;
|
|
if (mle_tlv_option_discover(ptr, data_len, reqType, &tlv_info) >= 4) {
|
|
*buffer = common_read_32_bit(tlv_info.dataPtr);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool mle_tlv_read_64_bit_tlv(mle_tlv_type_t reqType, uint8_t *ptr, uint16_t data_len, uint64_t *buffer)
|
|
{
|
|
mle_tlv_info_t tlv_info;
|
|
if (mle_tlv_option_discover(ptr, data_len, reqType, &tlv_info) >= 8) {
|
|
*buffer = common_read_64_bit(tlv_info.dataPtr);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool mle_registerd_address_data_check(uint8_t *ptr, uint8_t length)
|
|
{
|
|
uint8_t context;
|
|
if (length < 9) {
|
|
return false;
|
|
}
|
|
|
|
while (length) {
|
|
context = *ptr++;
|
|
if (context & 0x80) {
|
|
if (length < 9) {
|
|
return false;
|
|
}
|
|
ptr += 8;
|
|
length -= 9;
|
|
} else {
|
|
if (length < 17) {
|
|
return false;
|
|
}
|
|
ptr += 16;
|
|
length -= 17;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool mle_tlv_read_tlv(mle_tlv_type_t reqType, uint8_t *ptr, uint16_t data_len, mle_tlv_info_t *tlv_info)
|
|
{
|
|
int length;
|
|
length = mle_tlv_option_discover(ptr, data_len, reqType, tlv_info);
|
|
if (length != -1) {
|
|
if (reqType == MLE_TYPE_CHALLENGE && length > 3) { //32-bit challenge MIN which is accepted
|
|
return true;
|
|
} else if (reqType == MLE_TYPE_RESPONSE && length > 3) { //32-bit challenge MIN which is accepted
|
|
return true;
|
|
} else if (reqType == MLE_TYPE_ADDRESS_REGISTRATION && length) {
|
|
return mle_registerd_address_data_check(tlv_info->dataPtr, length);
|
|
} else if (reqType == MLE_TYPE_ROUTE && length >= 9) {
|
|
return true;
|
|
} else if (reqType == MLE_TYPE_LINK_METRICS_REPORT) {
|
|
return true;
|
|
}else if (reqType == MLE_TYPE_TLV_REQUEST || reqType == MLE_TYPE_NETWORK_DATA ||
|
|
reqType == MLE_TYPE_PENDING_OPERATIONAL_DATASET || reqType == MLE_TYPE_OPERATIONAL_DATASET) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint8_t *mle_tlv_write_response(uint8_t *ptr, uint8_t *response_ptr, uint8_t responseLen)
|
|
{
|
|
*ptr++ = MLE_TYPE_RESPONSE;
|
|
*ptr++ = responseLen;
|
|
memcpy(ptr, response_ptr, responseLen);
|
|
ptr += responseLen;
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *mle_tlv_write_source_address(uint8_t *ptr, uint16_t shortAddress)
|
|
{
|
|
*ptr++ = MLE_TYPE_SRC_ADDRESS;
|
|
*ptr++ = 2;
|
|
ptr = common_write_16_bit(shortAddress, ptr);
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *mle_tlv_write_short_address(uint8_t *ptr, uint16_t shortAddress)
|
|
{
|
|
*ptr++ = MLE_TYPE_ADDRESS16;
|
|
*ptr++ = 2;
|
|
ptr = common_write_16_bit(shortAddress, ptr);
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *mle_tlv_write_mode(uint8_t *ptr, uint8_t mode)
|
|
{
|
|
*ptr++ = MLE_TYPE_MODE;
|
|
*ptr++ = 1;
|
|
*ptr++ = mode;
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *mle_tlv_write_timeout(uint8_t *ptr, uint32_t timeOut)
|
|
{
|
|
*ptr++ = MLE_TYPE_TIMEOUT;
|
|
*ptr++ = 4;
|
|
ptr = common_write_32_bit(timeOut, ptr);
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *mle_tlv_write_challenge(uint8_t *ptr, uint8_t *challengePtr, uint8_t challenLen)
|
|
{
|
|
*ptr++ = MLE_TYPE_CHALLENGE;
|
|
*ptr++ = challenLen;
|
|
memcpy(ptr, challengePtr, challenLen);
|
|
ptr += challenLen;
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *mle_tlv_write_link_layer_framecount(uint8_t *ptr, uint32_t frameCount)
|
|
{
|
|
*ptr++ = MLE_TYPE_LL_FRAME_COUNTER;
|
|
*ptr++ = 4;
|
|
ptr = common_write_32_bit(frameCount, ptr);
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *mle_tlv_write_framecounter(uint8_t *ptr, uint32_t frameCount)
|
|
{
|
|
*ptr++ = MLE_TYPE_MLE_FRAME_COUNTER;
|
|
*ptr++ = 4;
|
|
ptr = common_write_32_bit(frameCount, ptr);
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *mle_tlv_write_scan_mask(uint8_t *ptr, uint8_t scanMask)
|
|
{
|
|
*ptr++ = MLE_TYPE_SCAN_MASK;
|
|
*ptr++ = 1;
|
|
*ptr++ = scanMask;
|
|
return ptr;
|
|
}
|
|
uint8_t *mle_tlv_req_tlv(uint8_t *ptr, uint8_t *mle_req_tlv_list, uint8_t req_list_len)
|
|
{
|
|
*ptr++ = MLE_TYPE_TLV_REQUEST;
|
|
*ptr++ = req_list_len;
|
|
memcpy(ptr, mle_req_tlv_list, req_list_len);
|
|
ptr += req_list_len;
|
|
return ptr;
|
|
}
|
|
uint8_t *mle_tlv_rssi_tlv(uint8_t *ptr, uint8_t linkMargin)
|
|
{
|
|
*ptr++ = MLE_TYPE_RSSI;
|
|
*ptr++ = 1;
|
|
*ptr++ = linkMargin;
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *mle_tlv_write_version(uint8_t *ptr, uint16_t version)
|
|
{
|
|
*ptr++ = MLE_TYPE_VERSION;
|
|
*ptr++ = 2;
|
|
ptr = common_write_16_bit(version, ptr);
|
|
return ptr;
|
|
}
|
|
|
|
uint8_t *mle_tlv_write_link_quality(uint8_t *ptr, uint8_t incoming_idr, uint8_t *mac64, uint16_t short_address, uint8_t priority_flag)
|
|
{
|
|
*ptr++ = MLE_TYPE_LINK_QUALITY;
|
|
if (mac64) {
|
|
*ptr++ = 11;
|
|
*ptr++ = 0x07; /* 8 bytes long address */
|
|
} else {
|
|
*ptr++ = 5;
|
|
*ptr++ = 0x01; /* 2 bytes long address */
|
|
}
|
|
|
|
if (priority_flag) {
|
|
*ptr++ = MLE_NEIGHBOR_PRIORITY_LINK | MLE_NEIGHBOR_INCOMING_LINK | MLE_NEIGHBOR_OUTGOING_LINK;
|
|
} else {
|
|
*ptr++ = MLE_NEIGHBOR_INCOMING_LINK | MLE_NEIGHBOR_OUTGOING_LINK;
|
|
}
|
|
|
|
*ptr++ = incoming_idr;
|
|
|
|
if (mac64) {
|
|
memcpy(ptr, mac64, 8);
|
|
ptr += 8;
|
|
} else {
|
|
ptr = common_write_16_bit(short_address, ptr);
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/**
|
|
* This function will be used for validate response from ACCEPT or ACCEPT Request message
|
|
*/
|
|
uint16_t mle_tlv_validate_response(uint8_t *ptr, uint16_t data_len)
|
|
{
|
|
mle_tlv_info_t tlv_info;
|
|
uint16_t msgId = 0;
|
|
if (mle_tlv_option_discover(ptr, data_len, MLE_TYPE_RESPONSE, &tlv_info) > 3) {
|
|
mle_service_transaction_buffer_get_for_response(tlv_info.dataPtr, tlv_info.tlvLen, &msgId);
|
|
return msgId;
|
|
}
|
|
return msgId;
|
|
}
|
|
|
|
bool mle_tlv_requested(uint8_t *tlv_ptr, uint16_t tlv_len, uint8_t tlv_type)
|
|
{
|
|
for (uint16_t i=0; i < tlv_len; i++) {
|
|
if (tlv_ptr[i] == tlv_type) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void mle_tlv_ignore(uint8_t *tlv_ptr, uint16_t tlv_len, uint8_t tlv_type)
|
|
{
|
|
for (uint16_t i=0; i < tlv_len; i++) {
|
|
if (tlv_ptr[i] == tlv_type) {
|
|
tlv_ptr[i] = MLE_TYPE_UNASSIGNED;
|
|
}
|
|
}
|
|
}
|
|
|