// Copyright (C) 2019-2021 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include #include #include #include "../include/tp_message.hpp" #include "../include/tp.hpp" #include "../../utility/include/byteorder.hpp" #ifdef ANDROID #include "../../configuration/include/internal_android.hpp" #else #include "../../configuration/include/internal.hpp" #endif // ANDROID #if defined(__linux__) || defined(ANDROID) #include #else #include #endif namespace vsomeip_v3 { namespace tp { tp_message::tp_message(const byte_t* const _data, std::uint32_t _data_length, std::uint32_t _max_message_size) : timepoint_creation_(std::chrono::steady_clock::now()), max_message_size_(_max_message_size), current_message_size_(0), last_segment_received_(false) { if (_data_length < VSOMEIP_FULL_HEADER_SIZE + VSOMEIP_TP_HEADER_SIZE) { VSOMEIP_ERROR << __func__ << " received too short SOME/IP-TP message " << get_message_id(_data, _data_length); return; } // copy header message_.insert(message_.end(), _data, _data + VSOMEIP_FULL_HEADER_SIZE); // remove TP flag message_[VSOMEIP_MESSAGE_TYPE_POS] = static_cast(tp::tp_flag_unset( message_[VSOMEIP_MESSAGE_TYPE_POS])); const length_t its_segment_size = _data_length - VSOMEIP_FULL_HEADER_SIZE - VSOMEIP_TP_HEADER_SIZE; const tp_header_t its_tp_header = VSOMEIP_BYTES_TO_LONG( _data[VSOMEIP_TP_HEADER_POS_MIN], _data[VSOMEIP_TP_HEADER_POS_MIN + 1], _data[VSOMEIP_TP_HEADER_POS_MIN + 2], _data[VSOMEIP_TP_HEADER_POS_MAX]); if (check_lengths(_data, _data_length, its_segment_size, tp::more_segments(its_tp_header))) { const length_t its_offset = tp::get_offset(its_tp_header); segments_.emplace(segment_t(its_offset, its_offset + its_segment_size - 1)); if (its_offset != 0) { // segment different than the first segment was received message_.resize(VSOMEIP_FULL_HEADER_SIZE + its_offset, 0x0); if (!tp::more_segments(its_tp_header)) { // received the last segment of the segmented message first last_segment_received_ = true; } } message_.insert(message_.end(), &_data[VSOMEIP_TP_PAYLOAD_POS], &_data[VSOMEIP_TP_PAYLOAD_POS] + its_segment_size); current_message_size_ += VSOMEIP_FULL_HEADER_SIZE + its_segment_size; } } bool tp_message::add_segment(const byte_t* const _data, std::uint32_t _data_length) { if (_data_length < VSOMEIP_FULL_HEADER_SIZE + VSOMEIP_TP_HEADER_SIZE) { VSOMEIP_ERROR << __func__ << " received too short SOME/IP-TP message " << get_message_id(_data, _data_length); return false; } bool ret = false; const length_t its_segment_size = _data_length - VSOMEIP_FULL_HEADER_SIZE - VSOMEIP_TP_HEADER_SIZE; const tp_header_t its_tp_header = VSOMEIP_BYTES_TO_LONG( _data[VSOMEIP_TP_HEADER_POS_MIN], _data[VSOMEIP_TP_HEADER_POS_MIN + 1], _data[VSOMEIP_TP_HEADER_POS_MIN + 2], _data[VSOMEIP_TP_HEADER_POS_MAX]); if (check_lengths(_data, _data_length, its_segment_size, tp::more_segments(its_tp_header))) { const length_t its_offset = tp::get_offset(its_tp_header); const auto emplace_res = segments_.emplace( segment_t(its_offset, its_offset + its_segment_size - 1)); if (!emplace_res.second) { VSOMEIP_WARNING << __func__ << ":" << __LINE__ << " received duplicate segment " << get_message_id(_data, _data_length) << "TP offset: 0x" << std::hex << its_offset; } else { const auto& seg_current = emplace_res.first; const auto& seg_next = std::next(seg_current); const bool current_segment_is_last = (seg_next == segments_.end()); const bool current_segment_is_first = (seg_current == segments_.begin()); if (current_segment_is_last) { if (current_segment_is_first) { // received segment of message but the first received segment was invalid // resize + append VSOMEIP_WARNING << __func__ << ":" << __LINE__ << " received 2nd segment of message. But the " "first received segment already wasn't accepted. " "The message can't be completed anymore: " << get_message_id(_data, _data_length); if (its_offset != 0) { // segment different than the first segment was received message_.resize(VSOMEIP_FULL_HEADER_SIZE + its_offset, 0x0); if (!tp::more_segments(its_tp_header)) { // received the last segment of the segmented message first last_segment_received_ = true; } } // append to end of message message_.insert(message_.end(), &_data[VSOMEIP_TP_PAYLOAD_POS], &_data[VSOMEIP_TP_PAYLOAD_POS] + its_segment_size); current_message_size_ += its_segment_size; } else { const auto& seg_prev = std::prev(seg_current); if (seg_prev->end_ < seg_current->start_) { const bool direct_previous_segment_present = (seg_prev->end_ + 1 == seg_current->start_); if (!direct_previous_segment_present) { // received segment out of order behind the current end of received segments //resize + append message_.resize(VSOMEIP_FULL_HEADER_SIZE + its_offset, 0x0); } // append to end of message message_.insert(message_.end(), &_data[VSOMEIP_TP_PAYLOAD_POS], &_data[VSOMEIP_TP_PAYLOAD_POS] + its_segment_size); current_message_size_ += its_segment_size; } else { // this segment starts before the end of the previous and // would overwrite already received data VSOMEIP_WARNING << __func__ << ":" << __LINE__ << " completely accepting segment would overwrite previous segment " << get_message_id(_data, _data_length) << "previous segment end: " << std::dec << seg_prev->end_ + 1 << " this segment start: " << std::dec << seg_current->start_; message_.insert(message_.end(), &_data[VSOMEIP_TP_PAYLOAD_POS] + ((seg_prev->end_ + 1) - seg_current->start_), &_data[VSOMEIP_TP_PAYLOAD_POS] + its_segment_size); // update start of current segment const std::uint32_t current_end = seg_current->end_; segments_.erase(seg_current); segments_.emplace(segment_t(seg_prev->end_ + 1, current_end)); current_message_size_ += current_end - seg_prev->end_; } } } else { // received segment in wrong order and other segments afterwards were already received if ((seg_current != segments_.begin() && std::prev(seg_current)->end_ < seg_current->start_) || seg_current == segments_.begin()) { // no need to check prev_segment if current segment is the first if (seg_current->end_ < seg_next->start_) { std::memcpy(&message_[VSOMEIP_FULL_HEADER_SIZE + its_offset], &_data[VSOMEIP_TP_PAYLOAD_POS], its_segment_size); current_message_size_ += its_segment_size; } else { // this segment ends after the start of the next and // would overwrite already received data VSOMEIP_WARNING << __func__ << ":" << __LINE__ << " completely accepting segment would overwrite next segment " << get_message_id(_data, _data_length) << "next segment start: " << std::dec << seg_next->start_ << " this segment end: " << std::dec << seg_current->end_ + 1; std::memcpy(&message_[VSOMEIP_FULL_HEADER_SIZE + its_offset], &_data[VSOMEIP_TP_PAYLOAD_POS], seg_next->start_ - its_offset); // update current segment length to match size of memory std::uint32_t current_start = seg_current->start_; segments_.erase(seg_current); segments_.emplace(segment_t(current_start, seg_next->start_ - 1)); current_message_size_ += seg_next->start_ - current_start; } } else if (seg_current->end_ < seg_next->start_) { // this segment starts before the end of the previous and // would overwrite already received data. But ends before the // start of the next segment const auto& seg_prev = std::prev(seg_current); VSOMEIP_WARNING << __func__ << ":" << __LINE__ << " completely accepting segment would overwrite previous segment " << get_message_id(_data, _data_length) << "previous segment end: " << std::dec << seg_prev->end_ << " this segment start: " << std::dec << seg_current->start_; const length_t its_corrected_offset = seg_prev->end_ + 1; std::memcpy(&message_[VSOMEIP_FULL_HEADER_SIZE + its_corrected_offset], &_data[VSOMEIP_TP_PAYLOAD_POS] + its_corrected_offset - its_offset, seg_next->start_ - its_corrected_offset); // update current segment length to match size of memory std::uint32_t current_end = seg_current->end_; segments_.erase(seg_current); segments_.emplace(segment_t(seg_prev->end_ + 1, current_end)); current_message_size_ += current_end - seg_prev->end_; } else { // this segment starts before the end of the previous and // ends after the start of the next segment and would // overwrite already received data. const auto& seg_prev = std::prev(seg_current); VSOMEIP_WARNING << __func__ << ":" << __LINE__ << " completely accepting segment would overwrite " << "previous and next segment " << get_message_id(_data, _data_length) << "previous segment end: " << std::dec << seg_prev->end_ << " this segment start: " << std::dec << seg_current->start_ << " this segment end: " << std::dec << seg_current->end_ << " next segment start: " << std::dec << seg_next->start_; const length_t its_corrected_offset = seg_prev->end_ + 1; std::memcpy(&message_[VSOMEIP_FULL_HEADER_SIZE + its_corrected_offset], &_data[VSOMEIP_TP_PAYLOAD_POS] + its_corrected_offset - its_offset, seg_next->start_ - its_corrected_offset); segments_.erase(seg_current); segments_.emplace(segment_t(seg_prev->end_ + 1, seg_next->start_ - 1)); current_message_size_ += seg_next->start_ - (seg_prev->end_ + 1); } } if (!tp::more_segments(its_tp_header)) { // received the last segment last_segment_received_ = true; } if (last_segment_received_) { // check if all segments are present std::uint32_t last_end = (std::numeric_limits::max)(); bool complete(true); for (const auto& seg : segments_) { if (last_end + 1 != seg.start_) { complete = false; break; } else { last_end = seg.end_; } } if (complete) { // all segments were received -> update length field of message const length_t its_length = static_cast( message_.size() - VSOMEIP_SOMEIP_HEADER_SIZE); *(reinterpret_cast(&message_[VSOMEIP_LENGTH_POS_MIN])) = htonl(its_length); // all segments were received -> update return code field of message message_[VSOMEIP_RETURN_CODE_POS] = _data[VSOMEIP_RETURN_CODE_POS]; ret = true; } } } } return ret; } message_buffer_t tp_message::get_message() { return std::move(message_); } std::chrono::steady_clock::time_point tp_message::get_creation_time() const { return timepoint_creation_; } std::string tp_message::get_message_id(const byte_t* const _data, std::uint32_t _data_length) { std::stringstream ss; if (_data_length >= VSOMEIP_FULL_HEADER_SIZE) { const service_t its_service = VSOMEIP_BYTES_TO_WORD( _data[VSOMEIP_SERVICE_POS_MIN], _data[VSOMEIP_SERVICE_POS_MAX]); const method_t its_method = VSOMEIP_BYTES_TO_WORD( _data[VSOMEIP_METHOD_POS_MIN], _data[VSOMEIP_METHOD_POS_MAX]); const client_t its_client = VSOMEIP_BYTES_TO_WORD( _data[VSOMEIP_CLIENT_POS_MIN], _data[VSOMEIP_CLIENT_POS_MAX]); const session_t its_session = VSOMEIP_BYTES_TO_WORD( _data[VSOMEIP_SESSION_POS_MIN], _data[VSOMEIP_SESSION_POS_MAX]); const interface_version_t its_interface_version = _data[VSOMEIP_INTERFACE_VERSION_POS]; const message_type_e its_msg_type = tp::tp_flag_unset( _data[VSOMEIP_MESSAGE_TYPE_POS]); ss << "(" << std::hex << std::setw(4) << std::setfill('0') << its_client << ") [" << std::hex << std::setw(4) << std::setfill('0') << its_service << "." << std::hex << std::setw(4) << std::setfill('0') << its_method << "." << std::hex << std::setw(2) << std::setfill('0') << std::uint32_t(its_interface_version) << "." << std::hex << std::setw(2) << std::setfill('0') << std::uint32_t(its_msg_type) << "." << std::hex << std::setw(4) << std::setfill('0') << its_session << "] "; if (_data_length > VSOMEIP_TP_HEADER_POS_MAX) { const tp_header_t its_tp_header = VSOMEIP_BYTES_TO_LONG( _data[VSOMEIP_TP_HEADER_POS_MIN], _data[VSOMEIP_TP_HEADER_POS_MIN + 1], _data[VSOMEIP_TP_HEADER_POS_MIN + 2], _data[VSOMEIP_TP_HEADER_POS_MAX]); const length_t its_offset = tp::get_offset(its_tp_header); ss << " TP offset: 0x" << std::hex << its_offset << " "; } } return ss.str(); } bool tp_message::check_lengths(const byte_t* const _data, std::uint32_t _data_length, length_t _segment_size, bool _more_fragments) { const length_t its_length = VSOMEIP_BYTES_TO_LONG( _data[VSOMEIP_LENGTH_POS_MIN], _data[VSOMEIP_LENGTH_POS_MIN + 1], _data[VSOMEIP_LENGTH_POS_MIN + 2], _data[VSOMEIP_LENGTH_POS_MAX]); const tp_header_t its_tp_header = VSOMEIP_BYTES_TO_LONG( _data[VSOMEIP_TP_HEADER_POS_MIN], _data[VSOMEIP_TP_HEADER_POS_MIN + 1], _data[VSOMEIP_TP_HEADER_POS_MIN + 2], _data[VSOMEIP_TP_HEADER_POS_MAX]); bool ret(true); if (!tp::tp_flag_is_set(_data[VSOMEIP_MESSAGE_TYPE_POS])) { VSOMEIP_ERROR << __func__ << ": TP flag not set " << get_message_id(_data, _data_length); ret = false; } else if (_data_length != its_length + VSOMEIP_SOMEIP_HEADER_SIZE) { VSOMEIP_ERROR << __func__ << ": data length doesn't match header length field" << get_message_id(_data, _data_length) << " data: " << std::dec << _data_length << " header: " << std::dec << its_length; ret = false; } else if (_segment_size != its_length - VSOMEIP_TP_HEADER_SIZE - (VSOMEIP_FULL_HEADER_SIZE - VSOMEIP_SOMEIP_HEADER_SIZE)) { VSOMEIP_ERROR << __func__ << ": segment size doesn't align with header length field" << get_message_id(_data, _data_length) << "segment size: " << std::dec << _segment_size << " data: " << std::dec << _data_length << " header: " << std::dec << its_length; ret = false; } else if (_segment_size > tp::tp_max_segment_length_) { VSOMEIP_ERROR << __func__ << ": Segment exceeds allowed size " << get_message_id(_data, _data_length) << "segment size: " << std::dec << _segment_size << " (max. " << std::dec << tp::tp_max_segment_length_ << ") data: " << std::dec << _data_length << " header: " << std::dec << its_length; ret = false; } else if (_more_fragments && _segment_size % 16 > 0) { VSOMEIP_ERROR << __func__ << ": Segment size not multiple of 16 " << get_message_id(_data, _data_length) << "segment size: " << std::dec << _segment_size << " data: " << std::dec << _data_length << " header: " << std::dec << its_length; ret = false; } else if (current_message_size_ + _segment_size > max_message_size_) { VSOMEIP_ERROR << __func__ << ": Message exceeds maximum configured size: " << get_message_id(_data, _data_length) << "segment size: " << std::dec << _segment_size << " current message size: " << std::dec << current_message_size_ << " maximum message size: " << std::dec << max_message_size_; ret = false; } else if (tp::get_offset(its_tp_header) + _segment_size > max_message_size_ ) { VSOMEIP_ERROR << __func__ << ": SomeIP/TP offset field exceeds maximum configured message size: " << get_message_id(_data, _data_length) << " TP offset [bytes]: " << std::dec << tp::get_offset(its_tp_header) << " segment size: " << std::dec << _segment_size << " current message size: " << std::dec << current_message_size_ << " maximum message size: " << std::dec << max_message_size_; ret = false; } return ret; } } // namespace tp } // namespace vsomeip_v3