LCOV - code coverage report
Current view: top level - libs/bsw/cpp2ethernet/include/ip - IPAddress.h (source / functions) Coverage Total Hit
Test: coverage.info Lines: 83.5 % 97 81
Test Date: 2026-10-23 12:52:11 Functions: 94.7 % 19 18

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

Generated by: LCOV version 2.0-1