blob: 661d0f84d65a91b6aeed6bced76cd2cd6e07dea0 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* Copyright (c) 2019 Google LLC.
* Copyright (c) 2013-2018 Nest Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file defines the class <tt>Inet::IPAddress</tt> and
* related enumerated constants. The CHIP Inet Layer uses objects
* of this class to represent Internet protocol addresses of both
* IPv4 and IPv6 address families. (IPv4 addresses are stored
* internally as IPv4-Mapped IPv6 addresses.)
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <type_traits>
#include <lib/core/CHIPError.h>
#include <lib/support/BitFlags.h>
#include <lib/support/DLLUtil.h>
#include <inet/InetConfig.h>
#include <inet/InetError.h>
#include "inet/IANAConstants.h"
#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
#include <lwip/init.h>
#include <lwip/ip_addr.h>
#if INET_CONFIG_ENABLE_IPV4
#include <lwip/ip4_addr.h>
#endif // INET_CONFIG_ENABLE_IPV4
#include <lwip/inet.h>
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
#if CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
#include <openthread/icmp6.h>
#include <openthread/ip6.h>
#endif // CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
#include <net/if.h>
#include <netinet/in.h>
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS
#include <sys/socket.h>
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS
#if CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT && INET_CONFIG_ENABLE_IPV4
#error Forbidden : native Open Thread implementation with IPV4 enabled
#endif
#include <inet/InetInterface.h>
#define NL_INET_IPV6_ADDR_LEN_IN_BYTES (16)
#define NL_INET_IPV6_MCAST_GROUP_LEN_IN_BYTES (14)
namespace chip {
namespace Inet {
/**
* Internet protocol address family
*/
enum class IPAddressType : uint8_t
{
kUnknown = 0, ///< Not used.
#if INET_CONFIG_ENABLE_IPV4
kIPv4 = 1, ///< Internet protocol version 4.
#endif // INET_CONFIG_ENABLE_IPV4
kIPv6 = 2, ///< Internet protocol version 6.
kAny = 3 ///< The unspecified internet address (independent of protocol version).
};
/**
* Internet protocol v6 multicast flags
*
* Values of the \c IPv6MulticastFlag type are used to call the <tt>IPAddress::MakeIPv6Multicast()</tt> methods.
* They indicate the type of IPv6 multicast address to create. These numbers are registered by IETF with IANA.
*/
enum class IPv6MulticastFlag : uint8_t
{
/** The multicast address is (1) transient (i.e., dynamically-assigned) rather than (0) well-known (i.e, IANA-assigned). */
kTransient = 0x01,
/** The multicast address is (1) based on a network prefix. */
kPrefix = 0x02
};
using IPv6MulticastFlags = BitFlags<IPv6MulticastFlag>;
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS
/**
* SockAddr should be used when calling any API that returns (by copying into
* it) a sockaddr, because that will need enough storage that it can hold data
* for any socket type.
*
* It can also be used when calling an API that accepts a sockaddr, to simplify
* the type-punning needed.
*/
union SockAddr
{
sockaddr any;
sockaddr_in in;
sockaddr_in6 in6;
sockaddr_storage storage;
};
/**
* SockAddrWithoutStorage can be used any time we want to do the sockaddr
* type-punning but will not store the data ourselves (e.g. we're working with
* an existing sockaddr pointer, and reintepret it as a
* pointer-to-SockAddrWithoutStorage).
*/
union SockAddrWithoutStorage
{
sockaddr any;
sockaddr_in in;
sockaddr_in6 in6;
};
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS
/**
* @brief Internet protocol address
*
* @details
* The CHIP Inet Layer uses objects of this class to represent Internet
* protocol addresses (independent of protocol version).
*
*/
class DLL_EXPORT IPAddress
{
public:
/**
* Maximum length of the string representation of an IP address, including a terminating NUL.
*/
#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
static constexpr uint16_t kMaxStringLength = IP6ADDR_STRLEN_MAX;
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
static constexpr uint16_t kMaxStringLength = INET6_ADDRSTRLEN;
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
#if CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN OT_IP6_ADDRESS_STRING_SIZE
#endif
static constexpr uint16_t kMaxStringLength = OT_IP6_ADDRESS_STRING_SIZE;
#endif
IPAddress() = default;
#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
explicit IPAddress(const ip6_addr_t & ipv6Addr);
#if INET_CONFIG_ENABLE_IPV4 || LWIP_IPV4
explicit IPAddress(const ip4_addr_t & ipv4Addr);
explicit IPAddress(const ip_addr_t & addr);
#endif // INET_CONFIG_ENABLE_IPV4
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
explicit IPAddress(const struct in6_addr & ipv6Addr);
#if INET_CONFIG_ENABLE_IPV4
explicit IPAddress(const struct in_addr & ipv4Addr);
#endif // INET_CONFIG_ENABLE_IPV4
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
#if CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
explicit IPAddress(const otIp6Address & ipv6Addr);
#endif
/**
* @brief Opaque word array to contain IP addresses (independent of protocol version)
*
* @details
* IPv6 address use all 128-bits split into four 32-bit network byte
* ordered unsigned integers. IPv4 addresses are IPv4-Mapped IPv6 addresses,
* i.e. the first two words are zero, the third word contains 0xFFFF in
* network byte order, and the fourth word contains the IPv4
* address in network byte order.
*/
uint32_t Addr[4];
/**
* @brief Test whether address is IPv6 compatible.
*
* @details
* Use this method to check if the address belongs to the IPv6 address
* family. Note well: the unspecified address is not an IPv6 address.
*
* @retval true The address is IPv6 and not the unspecified address.
* @retval false The address is IPv4 or the unspecified address.
*/
bool IsIPv6() const;
/**
* @brief Test whether address is IPv6 global unicast address.
*
* @details
* Use this method to check if the address belongs to the IPv6 address
* family and has the global unicast address prefix.
*
* @retval true Address is IPv6 global unicast
* @retval false Otherwise
*/
bool IsIPv6GlobalUnicast() const;
/**
* @brief Test whether address is IPv6 unique-local address (ULA).
*
* @details
* Use this method to check if the address belongs to the IPv6 address
* family and has the reserved IPv6 unique-local address prefix.
*
* @retval true Address is IPv6 unique-local
* @retval false Otherwise
*/
bool IsIPv6ULA() const;
/**
* @brief Test whether address is IPv6 link-local address (LL).
*
* @details
* Use this method to check if the address belongs to the IPv6 address
* family and has the reserved IPv6 link-local address prefix.
*
* @retval true Address is IPv6 link-local
* @retval false Otherwise
*/
bool IsIPv6LinkLocal() const;
/**
* @brief Test whether address is IPv6 multicast.
*
* @details
* Use this method to check if the address belongs to the IPv6 address
* family and has the reserved IPv6 multicast address prefix.
*
* @retval true Address is IPv6 multicast
* @retval false Otherwise
*/
bool IsIPv6Multicast() const;
/**
* @brief Test whether address is IPv4 or IPv6 multicast.
*
* @details
* Use this method to check if the address belongs to the IPv4 or IPv6 address
* family and has the reserved IPv4 or IPv6 multicast address prefix.
*
* @retval true Address is IPv4 or IPv6 multicast
* @retval false Otherwise
*/
bool IsMulticast() const;
/**
* @brief Extract the IID of an IPv6 ULA address.
*
* @details
* Use this method with an IPv6 unique-local address (ULA) to extract the
* identifier identifier (IID), which is the least significant 64 bits of
* the address.
*
* @return 64-bit interface identifier, or zero if the IP address is not
* an IPv6 unique-local address.
*/
uint64_t InterfaceId() const;
/**
* @brief Extract the 16-bit subnet identifier of an IPv6 ULA address.
*
* @details
* Use this method with an IPv6 unique-local address (ULA) to extract the
* subnet identifier, which is the least significant 16 bits of the
* network prefix. The network prefix is the most significant 64 bits of
* of the address. In other words, the subnet identifier is located in
* the 7th and 8th bytes of a 16-byte address.
*
* @return 16-bit subnet identifier, or zero if the IP address is not
* an IPv6 unique-local address.
*/
uint16_t Subnet() const;
/**
* @brief Extract the 16-bit global network identifier of an IPv6 ULA
* address.
*
* @details
* Use this method with an IPv6 unique-local address (ULA) to extract the
* global network identifier, which is the 40 bits immediately following
* the distinguished ULA network prefix, i.e. fd00::/8. In other words,
* the global network identifier is located in the five bytes from the 2nd
* 2nd through the 6th bytes in the address.
*
* @return 40-bit global network identifier, or zero if the IP address
* is not an IPv6 unique-local address.
*/
uint64_t GlobalId() const;
/**
* @brief Extract the type of the IP address.
*
* @details
* Use this method to return an value of the enumerated type \c
* IPAddressType to indicate the type of the IP address.
*
* @retval IPAddressType::kIPv4 The address is IPv4.
* @retval IPAddressType::kIPv6 The address is IPv6.
* @retval IPAddressType::kAny The address is the unspecified address.
*/
IPAddressType Type() const;
/**
* @brief Compare this IP address with another for equivalence.
*
* @param[in] other The address to compare.
*
* @retval true If equivalent to \c other
* @retval false Otherwise
*/
bool operator==(const IPAddress & other) const;
/**
* @brief Compare this IP address with another for inequivalence.
*
* @param[in] other The address to compare.
*
* @retval true If equivalent to \c other
* @retval false Otherwise
*/
bool operator!=(const IPAddress & other) const;
/**
* @brief Emit the IP address in conventional text presentation format.
*
* @param[out] buf The address of the emitted text.
* @param[in] bufSize The size of the buffer for the emitted text.
*
* @details
* Use <tt>ToString(char *buf, uint32_t bufSize) const</tt> to write the
* conventional text presentation form of the IP address to the memory
* located at \c buf and extending as much as \c bufSize bytes, including
* its NUL termination character.
*
* Note Well: not compliant with RFC 5952 on some platforms. Specifically,
* zero compression may not be applied according to section 4.2.
*
* @return The argument \c buf if no formatting error, or zero otherwise.
*/
char * ToString(char * buf, uint32_t bufSize) const;
/**
* A version of ToString that writes to a literal and deduces how much space
* it as to work with.
*/
template <uint32_t N>
inline char * ToString(char (&buf)[N]) const
{
return ToString(buf, N);
}
/**
* @brief Scan the IP address from its conventional presentation text.
*
* @param[in] str The address of the emitted text.
* @param[out] output The object to set to the scanned address.
*
* @details
* Use <tt>FromString(const char *str, IPAddress& output)</tt> to
* overwrite an IP address by scanning the conventional text presentation
* located at \c str.
*
* @retval true The presentation format is valid
* @retval false Otherwise
*/
static bool FromString(const char * str, IPAddress & output);
/**
* @brief Scan the IP address from its conventional presentation text.
*
* @param[in] str A pointer to the text to be scanned.
* @param[in] strLen The length of the text to be scanned.
* @param[out] output The object to set to the scanned address.
*
* @details
* Use <tt>FromString(const char *str, size_t strLen, IPAddress& output)</tt> to
* overwrite an IP address by scanning the conventional text presentation
* located at \c str.
*
* @retval true The presentation format is valid
* @retval false Otherwise
*/
static bool FromString(const char * str, size_t strLen, IPAddress & output);
/**
* @brief
* Scan the IP address from its conventional presentation text, including
* the interface ID if present. (e.g. "fe80::2%wlan0"). If no interface ID
* is present, then ifaceOutput will be set to the null interface ID.
*
* @param[in] str A pointer to the text to be scanned.
* @param[out] addrOutput The object to set to the IP address.
* @param[out] ifaceOutput The object to set to the interface ID.
*
* @retval true The presentation format is valid
* @retval false Otherwise
*/
static bool FromString(const char * str, IPAddress & addrOutput, class InterfaceId & ifaceOutput);
/**
* @brief Emit the IP address in standard network representation.
*
* @param[in,out] p Reference to the cursor to use for writing.
*
* @details
* Use <tt>WriteAddress(uint8_t *&p)</tt> to encode the IP address in
* the binary format defined by RFC 4291 for IPv6 addresses. IPv4
* addresses are encoded according to section 2.5.5.2 "IPv4-Mapped
* IPv6 Address".
*/
void WriteAddress(uint8_t *& p) const;
/**
* @brief Emit the IP address in standard network representation.
*
* @param[in,out] p Reference to the cursor to use for reading.
* @param[out] output Object to receive decoded IP address.
*
* @details
* Use <tt>ReadAddress(uint8_t *&p, IPAddress &output)</tt> to decode
* the IP address at \c p to the object \c output.
*/
static void ReadAddress(const uint8_t *& p, IPAddress & output);
/**
* @brief Test whether address is IPv4 compatible.
*
* @details
* Use this method to check if the address belongs to the IPv4 address
* family. Note well: the unspecified address is not an IPv4 address.
*
* @retval true The address is IPv4 and not the unspecified address.
* @retval false The address is IPv6 or the unspecified address.
*/
bool IsIPv4() const;
/**
* @brief Test whether address is IPv4 multicast.
*
* @details
* Use this method to check if the address is an IPv4 multicast
* address.
*
* @retval true Address is the IPv4 multicast
* @retval false Otherwise
*/
bool IsIPv4Multicast() const;
/**
* @brief Test whether address is IPv4 broadcast.
*
* @details
* Use this method to check if the address is the special purpose IPv4
* broadcast address.
*
* @retval true Address is the IPv4 broadcast
* @retval false Otherwise
*/
bool IsIPv4Broadcast() const;
/**
* @fn ToIPv4() const
*
* @brief Extract the IPv4 address as a platform data structure.
*
* @details
* Use <tt>ToIPv4() const</tt> to extract the content as an IPv4 address,
* if possible. IPv6 addresses and the unspecified address are
* extracted as <tt>0.0.0.0</tt>.
*
* The result is either of type <tt>struct in_addr</tt> (on POSIX) or
* <tt>ip4_addr_t</tt> (on LwIP).
*
* @return The encapsulated IPv4 address, or \c 0.0.0.0 if the address is
* either unspecified or not an IPv4 address.
*/
/**
* @fn ToIPv6() const
*
* @brief Extract the IPv6 address as a platform data structure.
*
* @details
* Use <tt>ToIPv6() const</tt> to extract the content as an IPv6 address,
* if possible. IPv4 addresses and the unspecified address are extracted
* as <tt>[::]</tt>.
*
* The result is either of type <tt>struct in6_addr</tt> (on POSIX) or
* <tt>ip6_addr_t</tt> (on LwIP).
*
* @return The encapsulated IPv4 address, or \c [::] if the address is
* either unspecified or not an IPv4 address.
*/
#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
/**
* @fn ToLwIPAddr() const
*
* @brief Extract the IP address as a LwIP ip_addr_t structure.
*
* @details
* Use <tt>ToLwIPAddr() const</tt> to extract the content as an IP address,
* if possible.
*
* @return An LwIP ip_addr_t structure corresponding to the IP address.
*/
ip_addr_t ToLwIPAddr(void) const;
/**
* Extract the IP address as a LwIP ip_addr_t structure.
*
* If the IP address is Any, the result is IP6_ADDR_ANY unless the requested addressType is kIPv4.
* If the requested addressType is IPAddressType::kAny, extracts the IP address as an LwIP ip_addr_t structure.
* Otherwise, returns INET_ERROR_WRONG_ADDRESS_TYPE if the requested addressType does not match the IP address.
*/
CHIP_ERROR ToLwIPAddr(IPAddressType addressType, ip_addr_t & outAddress) const;
/**
* @brief Convert the INET layer address type to its underlying LwIP type.
*
* @details
* Use <tt>ToLwIPAddrType(IPAddressType)</tt> to convert the IP address type
* to its underlying LwIP address type code.
*/
static lwip_ip_addr_type ToLwIPAddrType(IPAddressType);
ip6_addr_t ToIPv6(void) const;
#if INET_CONFIG_ENABLE_IPV4
ip4_addr_t ToIPv4(void) const;
#endif // INET_CONFIG_ENABLE_IPV4
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
struct in6_addr ToIPv6() const;
#if INET_CONFIG_ENABLE_IPV4
struct in_addr ToIPv4() const;
#endif // INET_CONFIG_ENABLE_IPV4
/**
* Get the IP address from a SockAddr.
*/
static CHIP_ERROR GetIPAddressFromSockAddr(const SockAddrWithoutStorage & sockaddr, IPAddress & outIPAddress);
static CHIP_ERROR GetIPAddressFromSockAddr(const sockaddr & sockaddr, IPAddress & outIPAddress)
{
return GetIPAddressFromSockAddr(reinterpret_cast<const SockAddrWithoutStorage &>(sockaddr), outIPAddress);
}
static IPAddress FromSockAddr(const sockaddr_in6 & sockaddr) { return IPAddress(sockaddr.sin6_addr); }
#if INET_CONFIG_ENABLE_IPV4
static IPAddress FromSockAddr(const sockaddr_in & sockaddr) { return IPAddress(sockaddr.sin_addr); }
#endif // INET_CONFIG_ENABLE_IPV4
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_USE_NETWORK_FRAMEWORK
#if CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
otIp6Address ToIPv6() const;
static IPAddress FromOtAddr(const otIp6Address & address);
#endif // CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT
/**
* @brief Construct an IPv6 unique-local address (ULA) from its parts.
*
* @details
* Use <tt>MakeULA(uint64_t globalId, uint16_t subnet, uint64_t
* interfaceId)</tt> to construct a unique-local address (ULA) with global
* network identifier \c globalId, subnet identifier \c subnet and
* interface identifier (IID) \c interfaceId.
*
* @return The constructed IP address.
*/
static IPAddress MakeULA(uint64_t globalId, uint16_t subnet, uint64_t interfaceId);
/**
* @brief Construct an IPv6 link-local address (LL) from its IID.
*
* @details
* Use <tt>MakeLLA(uint64_t interfaceId)</tt> to construct an IPv6
* link-local address (LL) with interface identifier \c interfaceId.
*
* @return The constructed IP address.
*/
static IPAddress MakeLLA(uint64_t interfaceId);
/**
* @brief Construct an IPv6 multicast address from its parts.
*
* @details
* Use <tt>MakeIPv6Multicast(uint8_t flags, uint8_t scope,
* uint8_t groupId[14])</tt> to construct an IPv6 multicast
* address with \c flags for routing scope \c scope and group
* identifier octets \c groupId.
*
* @return The constructed IP address.
*/
static IPAddress MakeIPv6Multicast(IPv6MulticastFlags aFlags, uint8_t aScope,
const uint8_t aGroupId[NL_INET_IPV6_MCAST_GROUP_LEN_IN_BYTES]);
/**
* @brief Construct an IPv6 multicast address from its parts.
*
* @details
* Use <tt>MakeIPv6Multicast(uint8_t flags, uint8_t scope,
* uint32_t groupId)</tt> to construct an IPv6 multicast
* address with \c flags for routing scope \c scope and group
* identifier \c groupId.
*
* @return The constructed IP address.
*/
static IPAddress MakeIPv6Multicast(IPv6MulticastFlags aFlags, uint8_t aScope, uint32_t aGroupId);
/**
* @brief Construct a well-known IPv6 multicast address from its parts.
*
* @details
* Use <tt>MakeIPv6WellKnownMulticast(uint8_t scope, uint32_t
* groupId)</tt> to construct an IPv6 multicast address for
* routing scope \c scope and group identifier \c groupId.
*
* @return The constructed IP address.
*/
static IPAddress MakeIPv6WellKnownMulticast(uint8_t aScope, uint32_t aGroupId);
/**
* @brief Construct a transient IPv6 multicast address from its parts.
*
* @details
* Use <tt>MakeIPv6TransientMulticast(uint8_t flags, uint8_t scope,
* uint8_t groupId[14])</tt> to construct a transient IPv6
* multicast address with \c flags for routing scope \c scope and
* group identifier octets \c groupId.
*
* @return The constructed IP address.
*/
static IPAddress MakeIPv6TransientMulticast(IPv6MulticastFlags aFlags, uint8_t aScope,
const uint8_t aGroupId[NL_INET_IPV6_MCAST_GROUP_LEN_IN_BYTES]);
/**
* @brief Construct a transient, prefix IPv6 multicast address from its parts.
*
* @details
* Use <tt>MakeIPv6PrefixMulticast(uint8_t scope, uint8_t
* prefixlen, const uint64_t prefix, uint32_t groupId)</tt> to
* construct a transient, prefix IPv6 multicast address with for
* routing scope \c scope and group identifier octets \c groupId,
* qualified by the prefix \c prefix of length \c prefixlen bits.
*
* @return The constructed IP address.
*/
static IPAddress MakeIPv6PrefixMulticast(uint8_t aScope, uint8_t aPrefixLength, const uint64_t & aPrefix, uint32_t aGroupId);
/**
* @brief Construct an IPv4 broadcast address.
*
* @return The constructed IP address.
*/
static IPAddress MakeIPv4Broadcast();
/**
* @brief The distinguished unspecified IP address object.
*
* @details
* This object is used as a constant for equivalence comparisons. It must
* not be modified by users of the CHIP Inet Layer.
*/
static IPAddress Any;
};
static_assert(std::is_trivial<IPAddress>::value, "IPAddress is not trivial");
} // namespace Inet
} // namespace chip