/* * Copyright (c) 2014-2019, Pelion 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; } } }