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-06-18 08:29:03 Functions: 100.0 % 23 23

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

Generated by: LCOV version 2.0-1