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/SdOptionParser.h"
12 :
13 : #include "someip/SdConstants.h"
14 : #include "someip/SdOptions.h"
15 : #include "someip/SomeIpConstants.h"
16 : #include "someip/logger.h"
17 :
18 : #include <ip/IPAddress.h>
19 :
20 : #include <etl/unaligned_type.h>
21 :
22 : // Logger API uses printf-style varargs for fixed diagnostic messages in this module.
23 : // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg)
24 :
25 : namespace someip
26 : {
27 : using ::util::logger::SOMEIP;
28 :
29 : namespace
30 : {
31 : enum class OptionCategory : uint8_t
32 : {
33 : IpEndpointOption,
34 : IpMulticastOption
35 : };
36 :
37 25 : bool isOptionCategory(OptionCategory const category, uint8_t const type)
38 : {
39 25 : if (OptionCategory::IpEndpointOption == category)
40 : {
41 : return (type == static_cast<uint8_t>(ServiceDiscoveryOptionType::OPTION_TYPE_IP4_ENDPOINT))
42 23 : || (type
43 23 : == static_cast<uint8_t>(ServiceDiscoveryOptionType::OPTION_TYPE_IP6_ENDPOINT));
44 : }
45 2 : if (OptionCategory::IpMulticastOption == category)
46 : {
47 : return (type == static_cast<uint8_t>(ServiceDiscoveryOptionType::OPTION_TYPE_IP4_MULTICAST))
48 2 : || (type
49 2 : == static_cast<uint8_t>(ServiceDiscoveryOptionType::OPTION_TYPE_IP6_MULTICAST));
50 : }
51 0 : return false;
52 : }
53 :
54 39 : bool isIp4OptionType(uint8_t const type)
55 : {
56 : return (type == static_cast<uint8_t>(ServiceDiscoveryOptionType::OPTION_TYPE_IP4_ENDPOINT))
57 39 : || (type == static_cast<uint8_t>(ServiceDiscoveryOptionType::OPTION_TYPE_IP4_MULTICAST));
58 : }
59 :
60 0 : bool isIp6OptionType(uint8_t const type)
61 : {
62 : return (type == static_cast<uint8_t>(ServiceDiscoveryOptionType::OPTION_TYPE_IP6_ENDPOINT))
63 0 : || (type == static_cast<uint8_t>(ServiceDiscoveryOptionType::OPTION_TYPE_IP6_MULTICAST));
64 : }
65 :
66 : SdEndpoint
67 19 : parseIpOptionType(::etl::span<uint8_t const> const buffer, uint16_t offset, uint8_t const type)
68 : {
69 19 : offset += static_cast<uint16_t>(static_cast<uint16_t>(SdConstants::SD_OPTION_ADDRESS_OFFSET));
70 :
71 19 : ::ip::IPAddress ipAddr{};
72 19 : if (isIp4OptionType(type))
73 : {
74 19 : ipAddr = ::ip::make_ip4(
75 19 : ::etl::span<uint8_t const>(buffer).subspan(offset).first(::ip::IPAddress::IP4LENGTH));
76 19 : offset += ::ip::IPAddress::IP4LENGTH;
77 : }
78 : #ifdef PLATFORM_SUPPORT_IPV6
79 : else if (isIp6OptionType(type))
80 : {
81 : ipAddr = ::ip::make_ip6(
82 : ::etl::span<uint8_t const>(buffer).subspan(offset).first(::ip::IPAddress::IP6LENGTH));
83 : offset += ::ip::IPAddress::IP6LENGTH;
84 : }
85 : #endif
86 : else
87 : {
88 0 : return SdOptionParser::INVALID_ENDPOINT;
89 : }
90 :
91 19 : offset++; // reserved
92 :
93 19 : uint8_t const proto = buffer[offset];
94 19 : offset++;
95 19 : uint16_t const port = ::etl::be_uint16_t(buffer.data() + offset);
96 :
97 19 : return SdEndpoint(ipAddr, port, proto);
98 : }
99 :
100 43 : bool goToOption(
101 : uint32_t const targetOptionsIndex, /* in */
102 : uint8_t const*& option, /* inout */
103 : uint32_t const optionsLength, /* in */
104 : uint8_t& optionCounter, /* inout */
105 : uint32_t& offset) /* inout */
106 : {
107 48 : while (optionCounter < targetOptionsIndex)
108 : {
109 5 : if ((optionsLength - offset)
110 : < static_cast<uint32_t>(SdConstants::SD_OPTION_LENGTH_FIELD_LENGTH))
111 : {
112 0 : return false;
113 : }
114 5 : uint16_t const optionLength = ::etl::be_uint16_t(option);
115 5 : if ((optionsLength - offset)
116 5 : < (static_cast<uint32_t>(optionLength)
117 : + static_cast<uint32_t>(SdConstants::SD_OPTION_LENGTH_FIELD_LENGTH)
118 5 : + 1U)) // +1 because length does not cover type field
119 : {
120 0 : return false;
121 : }
122 5 : offset += static_cast<uint32_t>(optionLength) + 3U;
123 5 : option += static_cast<uint8_t>(optionLength + 3U);
124 5 : ++optionCounter;
125 : }
126 43 : return true;
127 : }
128 :
129 43 : bool searchOptions(
130 : OptionCategory const& optionCategory, /* in */
131 : uint8_t const*& option, /* inout */
132 : uint32_t const optionsLength, /* in */
133 : uint8_t const numAvailableOptions, /* in */
134 : uint8_t& optionCounter, /* inout */
135 : uint32_t& offset, /* inout */
136 : uint8_t const*& foundOption, /* out */
137 : uint8_t& foundOptionType, /* out */
138 : uint8_t& numUdpOptions, /* out */
139 : uint8_t& numTcpOptions) /* out */
140 : {
141 68 : for (uint8_t i = 0U; i < numAvailableOptions; ++i)
142 : {
143 26 : if ((optionsLength - offset)
144 : < static_cast<uint32_t>(SdConstants::SD_OPTION_LENGTH_FIELD_LENGTH))
145 : {
146 1 : return false;
147 : }
148 :
149 25 : uint16_t const optionLength = ::etl::be_uint16_t(option);
150 25 : if ((optionsLength - offset)
151 25 : < (static_cast<uint32_t>(optionLength)
152 : + static_cast<uint32_t>(SdConstants::SD_OPTION_LENGTH_FIELD_LENGTH)
153 25 : + 1U)) // +1 because length does not cover type field
154 : {
155 0 : return false;
156 : }
157 :
158 25 : uint8_t const optionType
159 25 : = option[static_cast<uint16_t>(SdConstants::SD_OPTION_TYPE_OFFSET)];
160 25 : if (!isOptionCategory(optionCategory, optionType))
161 : {
162 5 : offset += static_cast<uint32_t>(optionLength) + 3U;
163 5 : option += static_cast<uint8_t>(optionLength + 3U);
164 5 : ++optionCounter;
165 5 : continue; // not the one we are looking for
166 : }
167 :
168 : uint16_t payloadLength;
169 : uint16_t protoOffset;
170 :
171 20 : if (isIp4OptionType(optionType))
172 : {
173 20 : payloadLength = static_cast<uint16_t>(SdConstants::SD_IP4_OPTION_LENGTH);
174 20 : protoOffset = static_cast<uint16_t>(
175 : static_cast<uint16_t>(SdConstants::SD_OPTION_ADDRESS_OFFSET)
176 : + ::ip::IPAddress::IP4LENGTH + 1U);
177 : }
178 0 : else if (isIp6OptionType(optionType))
179 : {
180 0 : payloadLength = static_cast<uint16_t>(SdConstants::SD_IP6_OPTION_LENGTH);
181 0 : protoOffset = static_cast<uint16_t>(
182 : static_cast<uint16_t>(SdConstants::SD_OPTION_ADDRESS_OFFSET)
183 : + ::ip::IPAddress::IP6LENGTH + 1U);
184 : }
185 : else
186 : {
187 0 : WARN_LOG(SOMEIP, "SdOptionParser::searchOptions(type: %d) invalid type", optionType);
188 0 : return false;
189 : }
190 :
191 20 : if ((optionLength + 3U) != payloadLength)
192 : {
193 0 : WARN_LOG(
194 : SOMEIP,
195 : "SdOptionParser::searchOptions(type: %d, length: %d) invalid payload",
196 : optionType,
197 : payloadLength);
198 0 : return false;
199 : }
200 :
201 20 : if (proto::SD_L4_PROTO_UDP == option[protoOffset])
202 : {
203 20 : ++numUdpOptions;
204 20 : if ((numUdpOptions + numTcpOptions) == 1U)
205 : { // use first found option
206 19 : foundOption = option;
207 19 : foundOptionType = optionType;
208 : }
209 : }
210 0 : else if (proto::SD_L4_PROTO_TCP == option[protoOffset])
211 : {
212 0 : ++numTcpOptions;
213 0 : if ((numUdpOptions + numTcpOptions) == 1U)
214 : { // use first found option
215 0 : foundOption = option;
216 0 : foundOptionType = optionType;
217 : }
218 : }
219 : else
220 : {
221 0 : WARN_LOG(
222 : SOMEIP,
223 : "SdOptionParser::searchOptions(type: %d, proto: 0x%X) invalid proto",
224 : optionType,
225 : option[protoOffset]);
226 0 : return false;
227 : }
228 :
229 20 : offset += static_cast<uint32_t>(optionLength) + 3U;
230 20 : option += static_cast<uint8_t>(optionLength + 3U);
231 20 : ++optionCounter;
232 : }
233 :
234 42 : return true;
235 : }
236 :
237 22 : SdEndpoint parseIpOption(
238 : OptionCategory const& optionCategory,
239 : SdOptions const& options,
240 : uint8_t& numUdpOptions,
241 : uint8_t& numTcpOptions)
242 : {
243 22 : ::etl::span<uint8_t const> const buffer = options.getOptionsData();
244 :
245 22 : uint8_t const* foundOption = nullptr;
246 22 : uint8_t foundOptionType = 0U;
247 :
248 : {
249 22 : uint8_t const* option = buffer.data();
250 22 : uint8_t optionCounter = 0U;
251 22 : uint32_t offset = 0U;
252 :
253 : // go to options1
254 22 : if (!goToOption(
255 22 : options.getOptions1Index(),
256 : option,
257 22 : static_cast<uint32_t>(buffer.size()),
258 : optionCounter,
259 : offset))
260 : {
261 0 : return SdOptionParser::INVALID_ENDPOINT;
262 : }
263 :
264 : // look at options1
265 22 : if (!searchOptions(
266 : optionCategory,
267 : option,
268 22 : static_cast<uint32_t>(buffer.size()),
269 22 : options.getOptions1Num(),
270 : optionCounter,
271 : offset,
272 : foundOption,
273 : foundOptionType,
274 : numUdpOptions,
275 : numTcpOptions))
276 : {
277 1 : return SdOptionParser::INVALID_ENDPOINT;
278 : }
279 : }
280 :
281 : {
282 21 : uint8_t const* option = buffer.data();
283 21 : uint8_t optionCounter = 0U;
284 21 : uint32_t offset = 0U;
285 :
286 : // go to options2
287 21 : if (!goToOption(
288 21 : options.getOptions2Index(),
289 : option,
290 21 : static_cast<uint32_t>(buffer.size()),
291 : optionCounter,
292 : offset))
293 : {
294 0 : return SdOptionParser::INVALID_ENDPOINT;
295 : }
296 :
297 : // look at options2
298 21 : if (!searchOptions(
299 : optionCategory,
300 : option,
301 21 : static_cast<uint32_t>(buffer.size()),
302 21 : options.getOptions2Num(),
303 : optionCounter,
304 : offset,
305 : foundOption,
306 : foundOptionType,
307 : numUdpOptions,
308 : numTcpOptions))
309 : {
310 0 : return SdOptionParser::INVALID_ENDPOINT;
311 : }
312 : }
313 :
314 21 : if (foundOption == nullptr)
315 : {
316 2 : return SdOptionParser::INVALID_ENDPOINT;
317 : }
318 :
319 19 : return parseIpOptionType(
320 19 : buffer, static_cast<uint16_t>(foundOption - buffer.data()), foundOptionType);
321 : }
322 : } // namespace
323 :
324 : // NOLINTNEXTLINE(cert-err58-cpp): Fine, since default value shall be used.
325 : SdEndpoint const SdOptionParser::INVALID_ENDPOINT;
326 :
327 20 : SdEndpoint SdOptionParser::parseIpEndpointOption(
328 : SdOptions const& options, uint8_t& numUdpOptions, uint8_t& numTcpOptions)
329 : {
330 40 : return parseIpOption(OptionCategory::IpEndpointOption, options, numUdpOptions, numTcpOptions);
331 : }
332 :
333 2 : SdEndpoint SdOptionParser::parseIpMulticastOption(
334 : SdOptions const& options, uint8_t& numUdpOptions, uint8_t& numTcpOptions)
335 : {
336 4 : return parseIpOption(OptionCategory::IpMulticastOption, options, numUdpOptions, numTcpOptions);
337 : }
338 :
339 : } // namespace someip
340 :
341 : // NOLINTEND(cppcoreguidelines-pro-type-vararg)
|