Line data Source code
1 : // Copyright 2025 Accenture.
2 :
3 : #pragma once
4 :
5 : #include <etl/error_handler.h>
6 : #include <etl/span.h>
7 : #include <etl/unaligned_type.h>
8 :
9 : #include <platform/estdint.h>
10 :
11 : #include <cstring>
12 :
13 : namespace ip
14 : {
15 : struct IPAddress
16 : {
17 : enum Family
18 : {
19 : FAMILY_UNKNOWN = 0U, ///< \deprecated FAMILY_UNKNOWN will be removed in future
20 : IPV4 = 4U,
21 : IPV6 = 6U
22 : };
23 :
24 : static constexpr uint8_t IP4LENGTH = 4U;
25 : static constexpr uint8_t IP6LENGTH = 16U;
26 :
27 : #ifndef OPENBSW_NO_IPV6
28 : static constexpr size_t MAX_IP_LENGTH = IP6LENGTH;
29 : #else
30 : static constexpr size_t MAX_IP_LENGTH = IP4LENGTH;
31 : #endif
32 :
33 4478 : uint32_t be_uint32_at(size_t index) const
34 : {
35 4478 : return ::etl::be_uint32_t(&raw[index * sizeof(uint32_t)]);
36 : }
37 :
38 6 : ::etl::be_uint32_ext_t be_uint32_at(size_t index)
39 : {
40 6 : return ::etl::be_uint32_ext_t(&raw[index * sizeof(uint32_t)]);
41 : }
42 :
43 : uint8_t raw[MAX_IP_LENGTH];
44 : };
45 :
46 : constexpr IPAddress make_ip4(uint32_t ip4addr);
47 :
48 : IPAddress make_ip4(::etl::span<uint8_t const> const& ip4addr);
49 :
50 : #ifndef OPENBSW_NO_IPV6
51 : constexpr IPAddress make_ip6(uint32_t addr0, uint32_t addr1, uint32_t addr2, uint32_t addr3);
52 :
53 : constexpr IPAddress make_ip6(uint32_t const ip6addr[IPAddress::IP6LENGTH / sizeof(uint32_t)]);
54 :
55 : IPAddress make_ip6(::etl::span<uint8_t const> const& ip6addr);
56 : #endif
57 :
58 : /** \deprecated Use one of the other make_ip4 factory functions instead. */
59 : constexpr IPAddress make_ip4(uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3);
60 :
61 : ::etl::span<uint8_t const> packed(IPAddress const& ipAddr);
62 :
63 : uint32_t ip4_to_u32(IPAddress const& ipAddr);
64 :
65 : #ifndef OPENBSW_NO_IPV6
66 : uint32_t ip6_to_u32(IPAddress const& ipAddr, size_t offset);
67 : #endif
68 :
69 : bool isUnspecified(IPAddress const& ipAddr);
70 :
71 : /** \deprecated Test (addressFamilyOf(ipAddr) != IPAddress::IPV4) instead. */
72 : bool isIp4Address(IPAddress const& ipAddr);
73 :
74 : /** \deprecated Test (addressFamilyOf(ipAddr) != IPAddress::IPV6) instead. */
75 : bool isIp6Address(IPAddress const& ipAddr);
76 :
77 : bool isMulticastAddress(IPAddress const& ipAddr);
78 :
79 : bool isLinkLocalAddress(IPAddress const& ipAddr);
80 :
81 : bool isLoopbackAddress(IPAddress const& ipAddr);
82 :
83 : bool isNetworkLocal(IPAddress const& ipAddr1, IPAddress const& ipAddr2, uint8_t networkId);
84 :
85 : IPAddress::Family addressFamilyOf(IPAddress const& ipAddr);
86 :
87 : bool operator==(IPAddress const& ip1, IPAddress const& ip2);
88 :
89 : bool operator!=(IPAddress const& ip1, IPAddress const& ip2);
90 :
91 : /** \deprecated Implement your own comparator logic if needed */
92 : struct IPAddressCompareLess
93 : {
94 : bool operator()(IPAddress const& ipAddr1, IPAddress const& ipAddr2) const;
95 : };
96 :
97 : namespace internal
98 : {
99 : static constexpr size_t RAW_IP4_IDX = IPAddress::MAX_IP_LENGTH - 1U * sizeof(uint32_t);
100 : static constexpr size_t IP4_IDX = IPAddress::MAX_IP_LENGTH / sizeof(uint32_t) - 1U;
101 : } // namespace internal
102 :
103 362 : inline constexpr IPAddress make_ip4(uint32_t const ip4addr)
104 : {
105 : // clang-format off
106 : return {{
107 : #ifndef OPENBSW_NO_IPV6
108 : 0x00U, 0x00U, 0x00U, 0x00U,
109 : 0x00U, 0x00U, 0x00U, 0x00U,
110 : 0x00U, 0x00U, 0xFFU, 0xFFU,
111 : #endif
112 362 : static_cast<uint8_t>(ip4addr >> 24),
113 362 : static_cast<uint8_t>(ip4addr >> 16),
114 362 : static_cast<uint8_t>(ip4addr >> 8),
115 362 : static_cast<uint8_t>(ip4addr)}};
116 : // clang-format on
117 : }
118 :
119 : inline constexpr IPAddress
120 268 : make_ip4(uint8_t const byte0, uint8_t const byte1, uint8_t const byte2, uint8_t const byte3)
121 : {
122 : // clang-format off
123 : return {{
124 : #ifndef OPENBSW_NO_IPV6
125 : 0x00U, 0x00U, 0x00U, 0x00U,
126 : 0x00U, 0x00U, 0x00U, 0x00U,
127 : 0x00U, 0x00U, 0xFFU, 0xFFU,
128 : #endif
129 268 : byte0, byte1, byte2, byte3}};
130 : // clang-format on
131 : }
132 :
133 6 : inline IPAddress make_ip4(::etl::span<uint8_t const> const& ip4addr)
134 : {
135 6 : ETL_ASSERT(
136 : ip4addr.size() == IPAddress::IP4LENGTH,
137 : ETL_ERROR_GENERIC("ipv4 addres must be of correct length"));
138 :
139 : // clang-format off
140 5 : IPAddress const newAddr = {{
141 : #ifndef OPENBSW_NO_IPV6
142 : 0x00U, 0x00U, 0x00U, 0x00U,
143 : 0x00U, 0x00U, 0x00U, 0x00U,
144 : 0x00U, 0x00U, 0xFFU, 0xFFU,
145 : #endif
146 5 : ip4addr[0U], ip4addr[1U], ip4addr[2U], ip4addr[3U]
147 5 : }};
148 : // clang-format on
149 :
150 5 : return newAddr;
151 : }
152 :
153 : #ifndef OPENBSW_NO_IPV6
154 : inline constexpr IPAddress
155 : make_ip6(uint32_t const addr0, uint32_t const addr1, uint32_t const addr2, uint32_t const addr3)
156 : {
157 : return {
158 : {static_cast<uint8_t>(addr0 >> 24),
159 : static_cast<uint8_t>(addr0 >> 16),
160 : static_cast<uint8_t>(addr0 >> 8),
161 : static_cast<uint8_t>(addr0),
162 : static_cast<uint8_t>(addr1 >> 24),
163 : static_cast<uint8_t>(addr1 >> 16),
164 : static_cast<uint8_t>(addr1 >> 8),
165 : static_cast<uint8_t>(addr1),
166 : static_cast<uint8_t>(addr2 >> 24),
167 : static_cast<uint8_t>(addr2 >> 16),
168 : static_cast<uint8_t>(addr2 >> 8),
169 : static_cast<uint8_t>(addr2),
170 : static_cast<uint8_t>(addr3 >> 24),
171 : static_cast<uint8_t>(addr3 >> 16),
172 : static_cast<uint8_t>(addr3 >> 8),
173 : static_cast<uint8_t>(addr3)}};
174 : }
175 :
176 24 : inline constexpr IPAddress make_ip6(uint32_t const ip6addr[IPAddress::IP6LENGTH / sizeof(uint32_t)])
177 : {
178 : return {
179 24 : {static_cast<uint8_t>(ip6addr[0] >> 24),
180 24 : static_cast<uint8_t>(ip6addr[0] >> 16),
181 24 : static_cast<uint8_t>(ip6addr[0] >> 8),
182 24 : static_cast<uint8_t>(ip6addr[0]),
183 24 : static_cast<uint8_t>(ip6addr[1] >> 24),
184 24 : static_cast<uint8_t>(ip6addr[1] >> 16),
185 24 : static_cast<uint8_t>(ip6addr[1] >> 8),
186 24 : static_cast<uint8_t>(ip6addr[1]),
187 24 : static_cast<uint8_t>(ip6addr[2] >> 24),
188 24 : static_cast<uint8_t>(ip6addr[2] >> 16),
189 24 : static_cast<uint8_t>(ip6addr[2] >> 8),
190 24 : static_cast<uint8_t>(ip6addr[2]),
191 24 : static_cast<uint8_t>(ip6addr[3] >> 24),
192 24 : static_cast<uint8_t>(ip6addr[3] >> 16),
193 24 : static_cast<uint8_t>(ip6addr[3] >> 8),
194 24 : static_cast<uint8_t>(ip6addr[3])}};
195 : }
196 :
197 9 : inline IPAddress make_ip6(::etl::span<uint8_t const> const& ip6addr)
198 : {
199 9 : ETL_ASSERT(
200 : ip6addr.size() == IPAddress::IP6LENGTH,
201 : ETL_ERROR_GENERIC("ipv6 address must be of correct length"));
202 :
203 : IPAddress newAddr;
204 8 : (void)memcpy(&newAddr.raw[0U], ip6addr.data(), IPAddress::IP6LENGTH);
205 8 : return newAddr;
206 : }
207 : #endif
208 :
209 2 : inline ::etl::span<uint8_t const, IPAddress::IP4LENGTH> ip4_bytes(IPAddress const& ipAddr)
210 : {
211 : return ::etl::span<uint8_t const, IPAddress::IP4LENGTH>(
212 2 : &ipAddr.raw[internal::RAW_IP4_IDX], IPAddress::IP4LENGTH);
213 : }
214 :
215 2 : inline ::etl::span<uint8_t const, IPAddress::IP6LENGTH> ip6_bytes(IPAddress const& ipAddr)
216 : {
217 2 : return ::etl::span<uint8_t const, IPAddress::IP6LENGTH>(&ipAddr.raw[0U], IPAddress::IP6LENGTH);
218 : }
219 :
220 2 : inline ::etl::span<uint8_t const> packed(IPAddress const& ipAddr)
221 : {
222 2 : if (addressFamilyOf(ipAddr) == IPAddress::IPV4)
223 : {
224 1 : return ip4_bytes(ipAddr);
225 : }
226 1 : return ip6_bytes(ipAddr);
227 : }
228 :
229 8 : inline uint32_t ip4_to_u32(IPAddress const& ipAddr)
230 : {
231 8 : return ipAddr.be_uint32_at(internal::IP4_IDX);
232 : }
233 :
234 : #ifndef OPENBSW_NO_IPV6
235 5 : inline uint32_t ip6_to_u32(IPAddress const& ipAddr, size_t const offset)
236 : {
237 5 : ETL_ASSERT(offset <= 3U, ETL_ERROR_GENERIC("offset must be smaller than 3"));
238 :
239 4 : return ipAddr.be_uint32_at(offset);
240 : }
241 : #endif
242 :
243 343 : inline bool isUnspecified(IPAddress const& ipAddr)
244 : {
245 343 : if (addressFamilyOf(ipAddr) == IPAddress::IPV4)
246 : {
247 77 : return ipAddr.be_uint32_at(internal::IP4_IDX) == 0U;
248 : }
249 :
250 329 : for (auto const i : ipAddr.raw)
251 : {
252 326 : if (0U != i)
253 : {
254 263 : return false;
255 : }
256 : }
257 3 : return true;
258 : }
259 :
260 2 : inline bool isIp4Address(IPAddress const& ipAddr)
261 : {
262 2 : return (IPAddress::IPV4 == addressFamilyOf(ipAddr));
263 : }
264 :
265 2 : inline bool isIp6Address(IPAddress const& ipAddr)
266 : {
267 2 : return (IPAddress::IPV6 == addressFamilyOf(ipAddr));
268 : }
269 :
270 4 : inline bool isMulticastAddress(IPAddress const& ipAddr)
271 : {
272 4 : IPAddress::Family const family = addressFamilyOf(ipAddr);
273 :
274 4 : if (IPAddress::IPV6 == family)
275 : {
276 2 : uint8_t const IP6_MULTICAST_PREFIX = 0xFFU;
277 2 : return (ipAddr.raw[0U] == IP6_MULTICAST_PREFIX);
278 : }
279 :
280 : // IPAddress::IPv4
281 2 : uint32_t const IP4_MULTICAST_MASK = 0xF0000000U;
282 2 : uint32_t const IP4_MULTICAST_PREFIX = 0xE0000000U;
283 2 : uint32_t const addressPrefix = (ipAddr.be_uint32_at(internal::IP4_IDX) & IP4_MULTICAST_MASK);
284 2 : return (addressPrefix == IP4_MULTICAST_PREFIX);
285 : }
286 :
287 6 : inline bool isLinkLocalAddress(IPAddress const& ipAddr)
288 : {
289 6 : IPAddress::Family const family = addressFamilyOf(ipAddr);
290 :
291 6 : if (IPAddress::IPV6 == family)
292 : {
293 3 : uint32_t const IP6_LINK_LOCAL_MASK = 0xFFC00000U;
294 3 : uint32_t const IP6_LINK_LOCAL_PREFIX = 0xFE800000U;
295 3 : uint32_t const addressPrefix = (ipAddr.be_uint32_at(0U) & IP6_LINK_LOCAL_MASK);
296 3 : return (addressPrefix == IP6_LINK_LOCAL_PREFIX);
297 : }
298 :
299 : // IPAddress::IPv4
300 3 : uint32_t const IP4_LINK_LOCAL_MASK = 0xFFFF0000U;
301 3 : uint32_t const IP4_LINK_LOCAL_PREFIX = 0xA9FE0000U;
302 3 : uint32_t const addressPrefix = (ipAddr.be_uint32_at(internal::IP4_IDX) & IP4_LINK_LOCAL_MASK);
303 3 : return (addressPrefix == IP4_LINK_LOCAL_PREFIX);
304 : }
305 :
306 259 : inline bool isLoopbackAddress(IPAddress const& ipAddr)
307 : {
308 259 : IPAddress::Family const family = addressFamilyOf(ipAddr);
309 :
310 259 : if (IPAddress::IPV6 == family)
311 : {
312 6 : return (ipAddr.be_uint32_at(0U) == 0U) && (ipAddr.be_uint32_at(1U) == 0U)
313 6 : && (ipAddr.be_uint32_at(2U) == 0U) && (ipAddr.be_uint32_at(3U) == 1U);
314 : }
315 :
316 : // IPAddress::IPv4
317 256 : uint32_t const IP4_LOOPBACK_PREFIX = 0x7F000000U;
318 256 : uint32_t const IP4_LOOPBACK_MASK = 0xFFFFFF00U;
319 256 : uint32_t const addressPrefix = (ipAddr.be_uint32_at(internal::IP4_IDX) & IP4_LOOPBACK_MASK);
320 256 : bool const hasLastByteSet = (ipAddr.raw[internal::RAW_IP4_IDX + 3U] != 0U);
321 256 : return ((addressPrefix == IP4_LOOPBACK_PREFIX) && hasLastByteSet);
322 : }
323 :
324 : inline bool
325 172 : isNetworkLocal(IPAddress const& ipAddr1, IPAddress const& ipAddr2, uint8_t const networkId)
326 : {
327 172 : IPAddress::Family const family1 = addressFamilyOf(ipAddr1);
328 172 : IPAddress::Family const family2 = addressFamilyOf(ipAddr2);
329 :
330 172 : if ((family1 != family2) || isUnspecified(ipAddr1) || isUnspecified(ipAddr2))
331 : {
332 5 : return false; // invalid IPs or IPv4/6 mix
333 : }
334 :
335 167 : if (networkId == 0U)
336 : {
337 4 : return true; // don't care
338 : }
339 :
340 163 : size_t const ip4PrefixLengthInBits = internal::IP4_IDX * sizeof(uint32_t) * 8U;
341 163 : size_t const netMaskBits
342 163 : = (IPAddress::IPV6 == family1) ? networkId : (networkId + ip4PrefixLengthInBits);
343 :
344 163 : if (netMaskBits > (IPAddress::MAX_IP_LENGTH * 8U))
345 : {
346 2 : return false; // invalid networkId
347 : }
348 :
349 161 : size_t const netMaskFullBytes = netMaskBits / 8U;
350 161 : if (0 != memcmp(&ipAddr1.raw[0U], &ipAddr2.raw[0U], netMaskFullBytes))
351 : {
352 1 : return false;
353 : }
354 :
355 160 : uint8_t const mask = ~(0xFFU >> (netMaskBits % 8U));
356 160 : if (mask == 0U)
357 : {
358 20 : return true;
359 : }
360 :
361 140 : return ((ipAddr1.raw[netMaskFullBytes] & mask) == (ipAddr2.raw[netMaskFullBytes] & mask));
362 : }
363 :
364 1000 : inline IPAddress::Family addressFamilyOf(IPAddress const& ipAddr)
365 : {
366 : #ifndef OPENBSW_NO_IPV6
367 1000 : uint32_t const IP4_PREFIX[] = {0U, 0U, 0xFFFFU};
368 :
369 1000 : bool const isIp4MappedIp6 = (IP4_PREFIX[0U] == ipAddr.be_uint32_at(0U))
370 451 : && (IP4_PREFIX[1U] == ipAddr.be_uint32_at(1U))
371 1451 : && (IP4_PREFIX[2U] == ipAddr.be_uint32_at(2U));
372 :
373 1000 : return isIp4MappedIp6 ? IPAddress::IPV4 : IPAddress::IPV6;
374 : #else
375 : return IPAddress::IPV4;
376 : #endif
377 : }
378 :
379 288 : inline bool operator==(IPAddress const& ip1, IPAddress const& ip2)
380 : {
381 : #ifndef OPENBSW_NO_IPV6
382 : return (
383 550 : (ip1.be_uint32_at(3) == ip2.be_uint32_at(3)) && (ip1.be_uint32_at(2) == ip2.be_uint32_at(2))
384 262 : && (ip1.be_uint32_at(1) == ip2.be_uint32_at(1))
385 550 : && (ip1.be_uint32_at(0) == ip2.be_uint32_at(0)));
386 : #else
387 : return (ip1.raw[0] == ip2.raw[0]);
388 : #endif
389 : }
390 :
391 15 : inline bool operator!=(IPAddress const& ip1, IPAddress const& ip2) { return !(ip1 == ip2); }
392 :
393 : inline bool
394 9 : IPAddressCompareLess::operator()(IPAddress const& ipAddr1, IPAddress const& ipAddr2) const
395 : {
396 9 : IPAddress::Family const family1 = addressFamilyOf(ipAddr1);
397 9 : IPAddress::Family const family2 = addressFamilyOf(ipAddr2);
398 :
399 9 : if (family1 != family2)
400 : {
401 2 : return (static_cast<uint8_t>(family1) < static_cast<uint8_t>(family2));
402 : }
403 :
404 24 : for (uint8_t i = 0U; i < (IPAddress::MAX_IP_LENGTH / sizeof(uint32_t)); ++i)
405 : {
406 22 : if (ipAddr1.be_uint32_at(i) != ipAddr2.be_uint32_at(i))
407 : {
408 5 : return ipAddr1.be_uint32_at(i) < ipAddr2.be_uint32_at(i);
409 : }
410 : }
411 :
412 2 : return false;
413 : }
414 :
415 : } // namespace ip
|