LCOV - code coverage report
Current view: top level - libs/bsw/cpp2ethernet/include/ip - IPAddress.h (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 131 131
Test Date: 2026-02-24 11:21:15 Functions: 100.0 % 23 23

            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
        

Generated by: LCOV version 2.0-1