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)
|