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