LCOV - code coverage report
Current view: top level - libs/bsw/cpp2someip/src - SdMessageParser.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 90.6 % 170 154
Test Date: 2026-09-11 12:05:06 Functions: 100.0 % 12 12

            Line data    Source code
       1              : /********************************************************************************
       2              :  * Copyright (c) 2026 Accenture
       3              :  *
       4              :  * This program and the accompanying materials are made available under the
       5              :  * terms of the Apache License Version 2.0 which is available at
       6              :  * https://www.apache.org/licenses/LICENSE-2.0
       7              :  *
       8              :  * SPDX-License-Identifier: Apache-2.0
       9              :  ********************************************************************************/
      10              : 
      11              : #include "someip/SdMessageParser.h"
      12              : 
      13              : #include "someip/SdConstants.h"
      14              : #include "someip/SdOptionParser.h"
      15              : #include "someip/SomeIpConstants.h"
      16              : #include "someip/SomeIpMessage.h"
      17              : #include "someip/Statistics.h"
      18              : #include "someip/logger.h"
      19              : 
      20              : #include <ip/IPAddress.h>
      21              : 
      22              : #include <etl/algorithm.h>
      23              : #include <etl/unaligned_type.h>
      24              : 
      25              : // Logger API uses printf-style varargs for fixed diagnostic messages in this module.
      26              : // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg)
      27              : 
      28              : namespace someip
      29              : {
      30              : using ::util::logger::SOMEIP;
      31              : 
      32              : namespace
      33              : {
      34              : // ETL has no 24 bit unaligned type, so the SOME/IP-SD TTL field uses the lower 3 bytes of an
      35              : // ::etl::be_uint32_t (same approach as ::uds::PositiveResponse::appendUint24).
      36           23 : uint32_t readBe24(uint8_t const* const ptr)
      37              : {
      38           23 :     ::etl::be_uint32_t be(static_cast<uint32_t>(0));
      39           23 :     (void)::etl::copy_n(ptr, 3U, be.data() + 1);
      40           46 :     return be;
      41              : }
      42              : 
      43            2 : SdEndpoint searchIPMulticastOption(SdOptions const& options)
      44              : {
      45            2 :     uint8_t numUdpOptions = 0U;
      46            2 :     uint8_t numTcpOptions = 0U;
      47              : 
      48              :     SdEndpoint const endpoint
      49            2 :         = SdOptionParser::parseIpMulticastOption(options, numUdpOptions, numTcpOptions);
      50              : 
      51            2 :     if (!endpoint.isValid())
      52              :     {
      53            0 :         return SdOptionParser::INVALID_ENDPOINT;
      54              :     }
      55              : 
      56            2 :     if ((numUdpOptions > 1U) || (numTcpOptions > 1U))
      57              :     {
      58            0 :         return SdOptionParser::INVALID_ENDPOINT;
      59              :     }
      60              : 
      61            2 :     if (!::ip::isMulticastAddress(endpoint.getAddress()))
      62              :     {
      63            0 :         return SdOptionParser::INVALID_ENDPOINT;
      64              :     }
      65              : 
      66            2 :     return endpoint;
      67              : }
      68              : 
      69           15 : SdEndpoint searchIPEndpointOption(
      70              :     SdOptions const& options, ::ip::IPAddress const& localIp, uint8_t const subnetId)
      71              : {
      72           15 :     uint8_t numUdpOptions = 0U;
      73           15 :     uint8_t numTcpOptions = 0U;
      74              : 
      75              :     SdEndpoint const endpoint
      76           15 :         = SdOptionParser::parseIpEndpointOption(options, numUdpOptions, numTcpOptions);
      77              : 
      78           15 :     if (!endpoint.isValid())
      79              :     {
      80            3 :         return SdOptionParser::INVALID_ENDPOINT;
      81              :     }
      82              : 
      83           12 :     if ((numUdpOptions > 1U) || (numTcpOptions > 1U))
      84              :     {
      85            1 :         return SdOptionParser::INVALID_ENDPOINT;
      86              :     }
      87              : 
      88           11 :     if ((!::ip::isNetworkLocal(endpoint.getAddress(), localIp, subnetId))
      89           11 :         || (endpoint.getAddress() == localIp))
      90              :     {
      91            2 :         return SdOptionParser::INVALID_ENDPOINT;
      92              :     }
      93              : 
      94            9 :     return endpoint;
      95              : }
      96              : 
      97              : } // namespace
      98              : 
      99           29 : SdMessageParser::SdMessageParser(
     100              :     IServiceRegistry& serviceRegistry,
     101              :     IServiceAnnouncer& serviceAnnouncer,
     102              :     RebootTracker& rebootTracker,
     103              :     uint8_t const subnetId,
     104              :     ::ip::IPAddress const& localIp,
     105           29 :     AdditionalSDCheck const additionalSDCheck)
     106           29 : : _serviceRegistry(serviceRegistry)
     107           29 : , _serviceAnnouncer(serviceAnnouncer)
     108           29 : , _subnetId(subnetId)
     109           29 : , _localIp(localIp)
     110           29 : , _rebootTracker(rebootTracker)
     111           29 : , _additionalSDCheck(additionalSDCheck)
     112           29 : {}
     113              : 
     114            1 : void SdMessageParser::init() { _rebootTracker.init(); }
     115              : 
     116              : // virtual
     117           25 : void SdMessageParser::handleMessage(
     118              :     SomeIpMessage const& message, ::ip::IPEndpoint const& sourceEndpoint, bool const isMulticast)
     119              : {
     120           25 :     if (message.getProtocolVersion() != configuration::PROTOCOL_VERSION)
     121              :     {
     122            0 :         WARN_LOG(
     123              :             SOMEIP,
     124              :             "SdMessageParser::handleMessage() invalid protocol: %d",
     125              :             message.getProtocolVersion());
     126            0 :         Statistics::incCounter(Statistics::Counter::SD_MALFORMED_MESSAGE_RX);
     127            0 :         return;
     128              :     }
     129              : 
     130           25 :     if (message.getInterfaceVersion() != configuration::INTERFACE_VERSION)
     131              :     {
     132            1 :         WARN_LOG(
     133              :             SOMEIP,
     134              :             "SdMessageParser::handleMessage() invalid interface: %d",
     135              :             message.getInterfaceVersion());
     136            1 :         Statistics::incCounter(Statistics::Counter::SD_MALFORMED_MESSAGE_RX);
     137            1 :         return;
     138              :     }
     139              : 
     140           24 :     if (message.getMessageType() != SomeIpMessage::MessageType::NOTIFICATION)
     141              :     {
     142            0 :         WARN_LOG(
     143              :             SOMEIP,
     144              :             "SdMessageParser::handleMessage() invalid message type: %d",
     145              :             message.getMessageType());
     146            0 :         Statistics::incCounter(Statistics::Counter::SD_MALFORMED_MESSAGE_RX);
     147            0 :         return;
     148              :     }
     149              : 
     150           24 :     if (message.getClientId() != 0U)
     151              :     {
     152            1 :         WARN_LOG(
     153              :             SOMEIP, "SdMessageParser::handleMessage() invalid client: %d", message.getClientId());
     154            1 :         Statistics::incCounter(Statistics::Counter::SD_MALFORMED_MESSAGE_RX);
     155            1 :         return;
     156              :     }
     157              : 
     158           23 :     uint16_t const sessionId = static_cast<uint16_t>(message.getSessionId());
     159              :     bool const sdFlagReboot
     160           23 :         = ((message.getFlags() & static_cast<uint8_t>(SdFlags::SD_FLAG_REBOOT)) != 0U);
     161              : 
     162              :     SessionInfo const session
     163           23 :         = SessionInfo(sourceEndpoint.getAddress(), isMulticast, sessionId, sdFlagReboot);
     164              : 
     165           23 :     if (_rebootTracker.evaluate(session))
     166              :     {
     167           22 :         Statistics::incCounter(Statistics::Counter::SD_REBOOT);
     168           22 :         _serviceRegistry.rebootDetected(sourceEndpoint.getAddress());
     169              :     }
     170              : 
     171           23 :     if (parseMessage(message.getBufferPayload(), sourceEndpoint, isMulticast))
     172              :     {
     173           22 :         _rebootTracker.apply(session);
     174              :     }
     175              :     else
     176              :     {
     177            1 :         Statistics::incCounter(Statistics::Counter::SD_MALFORMED_MESSAGE_RX);
     178              :     }
     179              : }
     180              : 
     181              : // private
     182           23 : bool SdMessageParser::parseMessage(
     183              :     ::etl::span<uint8_t const> const& payload,
     184              :     ::ip::IPEndpoint const& sourceEndpoint,
     185              :     bool const receivedByMulticast)
     186              : {
     187           23 :     size_t offset = 0U;
     188              : 
     189           23 :     if (payload.size() < static_cast<uint16_t>(SdConstants::SD_ENTRIES_LENGTH_OFFSET)
     190              :                              + static_cast<uint16_t>(SdConstants::SD_ENTRIES_LENGTH_FIELD_LENGTH))
     191              :     {
     192            0 :         return false;
     193              :     }
     194              : 
     195           23 :     uint8_t const flags      = payload[offset];
     196           23 :     bool const sdFlagUnicast = ((flags & static_cast<uint8_t>(SdFlags::SD_FLAG_UNICAST)) != 0U);
     197           23 :     offset += static_cast<uint16_t>(SdConstants::SD_ENTRIES_LENGTH_OFFSET);
     198              : 
     199           23 :     uint32_t const entriesLength = ::etl::be_uint32_t(&payload[offset]);
     200           23 :     if (payload.size() < (offset + static_cast<size_t>(entriesLength)))
     201              :     {
     202            0 :         return false;
     203              :     }
     204           23 :     offset += static_cast<uint16_t>(SdConstants::SD_ENTRIES_LENGTH_FIELD_LENGTH);
     205           23 :     uint32_t const entriesOffset = static_cast<uint32_t>(offset);
     206              : 
     207           23 :     SdOptions options;
     208              : 
     209           23 :     if (options.init(payload, entriesLength) == false)
     210              :     {
     211            0 :         return false;
     212              :     }
     213              : 
     214           23 :     bool containsRelevantService = false;
     215           23 :     while ((((offset + static_cast<uint16_t>(SdConstants::SD_ENTRY_LENGTH)) - entriesOffset)
     216           46 :             <= entriesLength)
     217           46 :            && ((offset + static_cast<uint16_t>(SdConstants::SD_ENTRY_LENGTH)) <= payload.size()))
     218              :     {
     219           23 :         bool const parseEntryResult = parseEntry(
     220           23 :             payload.subspan(offset), sourceEndpoint, receivedByMulticast, sdFlagUnicast, options);
     221              : 
     222           23 :         containsRelevantService = (parseEntryResult || containsRelevantService);
     223           23 :         offset += static_cast<uint16_t>(SdConstants::SD_ENTRY_LENGTH);
     224              :     }
     225              : 
     226           23 :     return containsRelevantService;
     227              : }
     228              : 
     229              : // private
     230           23 : bool SdMessageParser::parseEntry(
     231              :     ::etl::span<uint8_t const> const& entry,
     232              :     ::ip::IPEndpoint const& sourceEndpoint,
     233              :     bool const receivedByMulticast,
     234              :     bool const sdFlagUnicast,
     235              :     SdOptions& options)
     236              : {
     237           23 :     uint8_t const entryType = entry[static_cast<uint16_t>(SdConstants::SD_ENTRY_TYPE_OFFSET)];
     238              :     service_id::type const serviceId
     239           23 :         = ::etl::be_uint16_t(&entry[static_cast<uint16_t>(SdConstants::SD_SERVICE_ID_OFFSET)]);
     240              :     instance_id::type const instanceId
     241           23 :         = ::etl::be_uint16_t(&entry[static_cast<uint16_t>(SdConstants::SD_INSTANCE_ID_OFFSET)]);
     242              :     major_version::type const majorVersion
     243           23 :         = entry[static_cast<uint16_t>(SdConstants::SD_MAJOR_VERSION_OFFSET)];
     244           23 :     ttl::type const ttl = readBe24(&entry[static_cast<uint16_t>(SdConstants::SD_TTL_OFFSET)]);
     245              :     minor_version::type const minorVersion
     246           23 :         = ::etl::be_uint32_t(&entry[static_cast<uint16_t>(SdConstants::SD_MINOR_VERSION_OFFSET)]);
     247              :     uint16_t const reserved
     248           23 :         = ::etl::be_uint16_t(&entry[static_cast<uint16_t>(SdConstants::SD_RESERVED_OFFSET)]);
     249              :     eventgroup_id::type const eventgroup
     250           23 :         = ::etl::be_uint16_t(&entry[static_cast<uint16_t>(SdConstants::SD_EVENTGROUP_OFFSET)]);
     251              : 
     252              :     // A SUBSCRIBE might contain invalid service or event group which we have to reject with Nack!
     253           23 :     if ((ENTRY_TYPE_SUBSCRIBE != entryType)
     254           23 :         && (!_serviceRegistry.interestedInService(serviceId, instanceId, majorVersion)))
     255              :     {
     256            1 :         return false;
     257              :     }
     258              : 
     259           22 :     switch (entryType)
     260              :     {
     261            1 :         case ENTRY_TYPE_FIND:
     262              :         {
     263            1 :             Statistics::incCounter(Statistics::Counter::SD_FIND_RX);
     264            1 :             handleEntryFind(
     265              :                 serviceId,
     266              :                 instanceId,
     267              :                 majorVersion,
     268              :                 ttl,
     269              :                 minorVersion,
     270              :                 sourceEndpoint.getAddress(),
     271              :                 sdFlagUnicast);
     272            1 :             break;
     273              :         }
     274            7 :         case ENTRY_TYPE_OFFER:
     275              :         {
     276            7 :             Statistics::incCounter(Statistics::Counter::SD_OFFER_RX);
     277            7 :             options.readIndexValues(entry);
     278            7 :             handleEntryOffer(
     279              :                 options, serviceId, instanceId, majorVersion, ttl, minorVersion, sourceEndpoint);
     280            7 :             break;
     281              :         }
     282            1 :         case ENTRY_TYPE_FIND_EVENTGROUP:
     283              :         {
     284            1 :             Statistics::incCounter(Statistics::Counter::SD_FIND_EVENTGROUP_RX);
     285            1 :             DEBUG_LOG(SOMEIP, "SdMessageParser::parseEntry() unsupported type: %d", entryType);
     286            1 :             break;
     287              :         }
     288            0 :         case ENTRY_TYPE_PUBLISH:
     289              :         {
     290            0 :             Statistics::incCounter(Statistics::Counter::SD_PUBLISH_RX);
     291            0 :             DEBUG_LOG(SOMEIP, "SdMessageParser::parseEntry() unsupported type: %d", entryType);
     292            0 :             break;
     293              :         }
     294            9 :         case ENTRY_TYPE_SUBSCRIBE:
     295              :         {
     296            9 :             Statistics::incCounter(Statistics::Counter::SD_SUBSCRIBE_RX);
     297            9 :             if (receivedByMulticast)
     298              :             {
     299            1 :                 WARN_LOG(
     300              :                     SOMEIP,
     301              :                     "SdMessageParser::parseEntry() discard subscribe received by multicast");
     302            1 :                 break; // SIP_SD_818 : discard
     303              :             }
     304            8 :             options.readIndexValues(entry);
     305            8 :             handleEntrySubscribe(
     306              :                 options,
     307              :                 serviceId,
     308              :                 instanceId,
     309              :                 majorVersion,
     310              :                 eventgroup,
     311              :                 ttl,
     312              :                 reserved,
     313              :                 sourceEndpoint);
     314            8 :             break;
     315              :         }
     316            3 :         case ENTRY_TYPE_SUBSCRIBE_ACK:
     317              :         {
     318            3 :             if (receivedByMulticast)
     319              :             {
     320            1 :                 break; // SIP_SD_818 : discard
     321              :             }
     322            2 :             options.readIndexValues(entry);
     323            2 :             handleEntrySubscribeAck(
     324              :                 options,
     325              :                 serviceId,
     326              :                 instanceId,
     327              :                 majorVersion,
     328              :                 eventgroup,
     329              :                 ttl,
     330              :                 sourceEndpoint.getAddress());
     331            2 :             break;
     332              :         }
     333            1 :         default:
     334              :         {
     335            1 :             Statistics::incCounter(Statistics::Counter::SD_UNKNOWN_RX);
     336            1 :             WARN_LOG(SOMEIP, "SdMessageParser::parseEntry() invalid type: %d", entryType);
     337            1 :             break;
     338              :         }
     339              :     }
     340              : 
     341           22 :     return true;
     342              : }
     343              : 
     344              : // private
     345            1 : void SdMessageParser::handleEntryFind(
     346              :     service_id::type const serviceId,
     347              :     instance_id::type const instanceId,
     348              :     major_version::type const majorVersion,
     349              :     ttl::type const ttl,
     350              :     minor_version::type const minorVersion,
     351              :     ::ip::IPAddress const& sourceAddress,
     352              :     bool const sdFlagUnicast)
     353              : {
     354            1 :     if (ttl != 0U)
     355              :     {
     356            1 :         _serviceAnnouncer.respondToFindService(
     357              :             serviceId, instanceId, majorVersion, minorVersion, ttl, sourceAddress, sdFlagUnicast);
     358              :     }
     359              :     else
     360              :     {
     361              :         // StopFind -> nothing to do
     362              :     }
     363            1 : }
     364              : 
     365              : // private
     366            7 : void SdMessageParser::handleEntryOffer(
     367              :     SdOptions const& options,
     368              :     service_id::type const serviceId,
     369              :     instance_id::type const instanceId,
     370              :     major_version::type const majorVersion,
     371              :     ttl::type const ttl,
     372              :     minor_version::type const minorVersion,
     373              :     ::ip::IPEndpoint const& sourceEndpoint)
     374              : {
     375            7 :     SdEndpoint const endpoint = searchIPEndpointOption(options, _localIp, _subnetId);
     376              : 
     377            7 :     if ((endpoint.isValid()) && (!::ip::isMulticastAddress(endpoint.getAddress())))
     378              :     {
     379            5 :         if (_additionalSDCheck && ((*_additionalSDCheck)(endpoint)))
     380              :         {
     381            1 :             return;
     382              :         }
     383              : 
     384            4 :         ServiceDescription const receivedService
     385              :             = {minorVersion,
     386              :                ttl,
     387              :                serviceId,
     388              :                instanceId,
     389              :                eventgroup_id::ALL,
     390            4 :                endpoint.getAddress(),
     391            4 :                endpoint.getPort(),
     392            4 :                endpoint.getProto(),
     393            4 :                majorVersion};
     394              : 
     395            4 :         _serviceRegistry.offerReceived(receivedService, sourceEndpoint.getAddress());
     396              :     }
     397              : }
     398              : 
     399              : // private
     400            8 : void SdMessageParser::handleEntrySubscribe(
     401              :     SdOptions const& options,
     402              :     service_id::type const serviceId,
     403              :     instance_id::type const instanceId,
     404              :     major_version::type const majorVersion,
     405              :     eventgroup_id::type const eventgroup,
     406              :     ttl::type const ttl,
     407              :     uint16_t const reserved,
     408              :     ::ip::IPEndpoint const& sourceEndpoint)
     409              : {
     410            8 :     SdEndpoint const endpoint = searchIPEndpointOption(options, _localIp, _subnetId);
     411              : 
     412            8 :     if (endpoint.isValid())
     413              :     {
     414            4 :         if (_additionalSDCheck && ((*_additionalSDCheck)(endpoint)))
     415              :         {
     416            1 :             _serviceAnnouncer.sendSubscribeNack(
     417              :                 serviceId, instanceId, eventgroup, majorVersion, reserved, endpoint.getAddress());
     418            1 :             return;
     419              :         }
     420              : 
     421            3 :         _serviceAnnouncer.respondToSubscribe(
     422              :             serviceId,
     423              :             instanceId,
     424              :             majorVersion,
     425              :             reserved,
     426              :             eventgroup,
     427              :             ttl,
     428              :             sourceEndpoint.getAddress(),
     429              :             endpoint.getAddress(),
     430            3 :             endpoint.getPort(),
     431            3 :             endpoint.getProto());
     432              :     }
     433            4 :     else if (ttl != 0U) // don't Nack on StopSubscribe
     434              :     {
     435            3 :         _serviceAnnouncer.sendSubscribeNack(
     436              :             serviceId, instanceId, eventgroup, majorVersion, reserved, sourceEndpoint.getAddress());
     437              :     }
     438              :     else
     439              :     {
     440              :         ; // nothing else to do
     441              :     }
     442              : }
     443              : 
     444              : // private
     445            2 : void SdMessageParser::handleEntrySubscribeAck(
     446              :     SdOptions const& options,
     447              :     service_id::type const serviceId,
     448              :     instance_id::type const instanceId,
     449              :     major_version::type const majorVersion,
     450              :     eventgroup_id::type const eventgroup,
     451              :     ttl::type const ttl,
     452              :     ::ip::IPAddress const& sourceAddress)
     453              : {
     454            2 :     SdEndpoint const endpoint = searchIPMulticastOption(options);
     455            2 :     if (ttl != 0U)
     456              :     {
     457            1 :         Statistics::incCounter(Statistics::Counter::SD_SUBSCRIBE_ACK_RX);
     458            1 :         _serviceRegistry.subscribeAckReceived(
     459              :             serviceId, instanceId, eventgroup, majorVersion, endpoint, sourceAddress);
     460              :     }
     461              :     else
     462              :     {
     463            1 :         Statistics::incCounter(Statistics::Counter::SD_SUBSCRIBE_NACK_RX);
     464            1 :         _serviceRegistry.subscribeNackReceived(
     465              :             serviceId, instanceId, eventgroup, majorVersion, sourceAddress);
     466              :     }
     467            2 : }
     468              : 
     469              : } // namespace someip
     470              : 
     471              : // NOLINTEND(cppcoreguidelines-pro-type-vararg)
        

Generated by: LCOV version 2.0-1