network: allow graceful failure on creation of Ipv[4,6]Address from string
This commit is contained in:
@@ -19,6 +19,8 @@ Changes from ns-3.37 to ns-3.38
|
|||||||
### New API
|
### New API
|
||||||
|
|
||||||
### Changes to existing API
|
### Changes to existing API
|
||||||
|
* (network) **Ipv4Address** and **Ipv6Address** now do not raise an exception if built from an invalid string. Instead the address is marked as not initialized.
|
||||||
|
|
||||||
|
|
||||||
### Changes to build system
|
### Changes to build system
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ Release 3-dev
|
|||||||
-------------
|
-------------
|
||||||
|
|
||||||
### New user-visible features
|
### New user-visible features
|
||||||
|
- (network) !1163 Initializing an Ipv[4,6]Address from an invalid string do not raise an exception anymore. Instead the address is marked as not initialized.
|
||||||
|
|
||||||
|
|
||||||
### Bugs fixed
|
### Bugs fixed
|
||||||
|
|
||||||
|
|||||||
@@ -24,63 +24,17 @@
|
|||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#ifdef __WIN32__
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
#else
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ns3
|
namespace ns3
|
||||||
{
|
{
|
||||||
|
|
||||||
NS_LOG_COMPONENT_DEFINE("Ipv4Address");
|
NS_LOG_COMPONENT_DEFINE("Ipv4Address");
|
||||||
|
|
||||||
#define ASCII_DOT (0x2e)
|
|
||||||
#define ASCII_ZERO (0x30)
|
|
||||||
#define ASCII_SLASH (0x2f)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Converts a string representing an IP address into the address
|
|
||||||
* \param address the address string
|
|
||||||
* \returns the address
|
|
||||||
*/
|
|
||||||
static uint32_t
|
|
||||||
AsciiToIpv4Host(const char* address)
|
|
||||||
{
|
|
||||||
NS_LOG_FUNCTION(&address);
|
|
||||||
uint32_t host = 0;
|
|
||||||
uint8_t numberOfDots = 0;
|
|
||||||
const char* ptr = address;
|
|
||||||
|
|
||||||
NS_ASSERT_MSG(*ptr != ASCII_DOT,
|
|
||||||
"Error, can not build an IPv4 address from an invalid string: " << address);
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
uint8_t byte = 0;
|
|
||||||
while (*ptr != ASCII_DOT && *ptr != 0)
|
|
||||||
{
|
|
||||||
byte *= 10;
|
|
||||||
byte += *ptr - ASCII_ZERO;
|
|
||||||
ptr++;
|
|
||||||
}
|
|
||||||
host <<= 8;
|
|
||||||
host |= byte;
|
|
||||||
if (*ptr == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ptr++;
|
|
||||||
NS_ASSERT_MSG(*ptr != ASCII_DOT,
|
|
||||||
"Error, can not build an IPv4 address from an invalid string: " << address);
|
|
||||||
numberOfDots++;
|
|
||||||
}
|
|
||||||
NS_ASSERT_MSG(*(ptr - 1) != ASCII_DOT,
|
|
||||||
"Error, can not build an IPv4 address from an invalid string: " << address);
|
|
||||||
NS_ASSERT_MSG(numberOfDots == 3,
|
|
||||||
"Error, can not build an IPv4 address from an invalid string: " << address);
|
|
||||||
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ns3
|
|
||||||
|
|
||||||
namespace ns3
|
|
||||||
{
|
|
||||||
|
|
||||||
Ipv4Mask::Ipv4Mask()
|
Ipv4Mask::Ipv4Mask()
|
||||||
: m_mask(0x66666666)
|
: m_mask(0x66666666)
|
||||||
{
|
{
|
||||||
@@ -96,7 +50,7 @@ Ipv4Mask::Ipv4Mask(uint32_t mask)
|
|||||||
Ipv4Mask::Ipv4Mask(const char* mask)
|
Ipv4Mask::Ipv4Mask(const char* mask)
|
||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this << mask);
|
NS_LOG_FUNCTION(this << mask);
|
||||||
if (*mask == ASCII_SLASH)
|
if (*mask == '/')
|
||||||
{
|
{
|
||||||
uint32_t plen = static_cast<uint32_t>(std::atoi(++mask));
|
uint32_t plen = static_cast<uint32_t>(std::atoi(++mask));
|
||||||
NS_ASSERT(plen <= 32);
|
NS_ASSERT(plen <= 32);
|
||||||
@@ -111,7 +65,11 @@ Ipv4Mask::Ipv4Mask(const char* mask)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_mask = AsciiToIpv4Host(mask);
|
if (inet_pton(AF_INET, mask, &m_mask) <= 0)
|
||||||
|
{
|
||||||
|
NS_ABORT_MSG("Error, can not build an IPv4 mask from an invalid string: " << mask);
|
||||||
|
}
|
||||||
|
m_mask = ntohl(m_mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,8 +177,16 @@ Ipv4Address::Ipv4Address(uint32_t address)
|
|||||||
Ipv4Address::Ipv4Address(const char* address)
|
Ipv4Address::Ipv4Address(const char* address)
|
||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this << address);
|
NS_LOG_FUNCTION(this << address);
|
||||||
m_address = AsciiToIpv4Host(address);
|
|
||||||
|
if (inet_pton(AF_INET, address, &m_address) <= 0)
|
||||||
|
{
|
||||||
|
NS_LOG_LOGIC("Error, can not build an IPv4 address from an invalid string: " << address);
|
||||||
|
m_address = 0;
|
||||||
|
m_initialized = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
|
m_address = ntohl(m_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
@@ -242,8 +208,15 @@ void
|
|||||||
Ipv4Address::Set(const char* address)
|
Ipv4Address::Set(const char* address)
|
||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this << address);
|
NS_LOG_FUNCTION(this << address);
|
||||||
m_address = AsciiToIpv4Host(address);
|
if (inet_pton(AF_INET, address, &m_address) <= 0)
|
||||||
|
{
|
||||||
|
NS_LOG_LOGIC("Error, can not build an IPv4 address from an invalid string: " << address);
|
||||||
|
m_address = 0;
|
||||||
|
m_initialized = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
|
m_address = ntohl(m_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ipv4Address
|
Ipv4Address
|
||||||
|
|||||||
@@ -17,28 +17,6 @@
|
|||||||
* Author: Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
|
* Author: Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Part of the Ipv6Address::Print function has been adapted from inet_ntop6 Linux function.
|
|
||||||
// See http://www.net-snmp.org/dev/agent/inet__ntop_8c_source.html
|
|
||||||
// Author: Paul Vixie, 1996.
|
|
||||||
// The inet_ntop6 function was under the copyright below, which is
|
|
||||||
// compatible with GPLv2, see http://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses.
|
|
||||||
|
|
||||||
/* Copyright (c) 1996 by Internet Software Consortium.
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
|
|
||||||
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
|
||||||
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
|
|
||||||
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
|
||||||
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
|
||||||
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ipv6-address.h"
|
#include "ipv6-address.h"
|
||||||
|
|
||||||
#include "mac16-address.h"
|
#include "mac16-address.h"
|
||||||
@@ -49,7 +27,13 @@
|
|||||||
#include "ns3/log.h"
|
#include "ns3/log.h"
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <memory.h>
|
#include <memory>
|
||||||
|
|
||||||
|
#ifdef __WIN32__
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
#else
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ns3
|
namespace ns3
|
||||||
{
|
{
|
||||||
@@ -165,151 +149,6 @@ extern "C"
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Convert an IPv6 C-string into a 128-bit representation.
|
|
||||||
*
|
|
||||||
* \param address pointer to the char buffer with the address ascii representation
|
|
||||||
* \param addr the buffer to store the IPv6 address
|
|
||||||
*
|
|
||||||
* \return true if success, false otherwise (bad format, ...)
|
|
||||||
*
|
|
||||||
* \note This function is strongly inspired by inet_pton6() from Paul Vixie.
|
|
||||||
* \todo Handle IPv6 address with decimal value for last four bytes.
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
AsciiToIpv6Host(const char* address, uint8_t addr[16])
|
|
||||||
{
|
|
||||||
NS_LOG_FUNCTION(address << &addr);
|
|
||||||
static const char xdigits_l[] = "0123456789abcdef";
|
|
||||||
static const char xdigits_u[] = "0123456789ABCDEF";
|
|
||||||
unsigned char tmp[16];
|
|
||||||
unsigned char* tp = tmp;
|
|
||||||
unsigned char* const endp = tp + 16;
|
|
||||||
unsigned char* colonp = nullptr;
|
|
||||||
const char* xdigits = nullptr;
|
|
||||||
#if 0
|
|
||||||
const char* curtok = 0;
|
|
||||||
#endif
|
|
||||||
int ch = 0;
|
|
||||||
int seen_xdigits = 0;
|
|
||||||
unsigned int val = 0;
|
|
||||||
|
|
||||||
memset(tp, 0x00, 16);
|
|
||||||
|
|
||||||
/* Leading :: requires some special handling. */
|
|
||||||
if (*address == ':')
|
|
||||||
{
|
|
||||||
if (*++address != ':')
|
|
||||||
{
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
curtok = address;
|
|
||||||
#endif
|
|
||||||
while ((ch = *address++) != '\0')
|
|
||||||
{
|
|
||||||
const char* pch = nullptr;
|
|
||||||
|
|
||||||
if ((pch = strchr((xdigits = xdigits_l), ch)) == nullptr)
|
|
||||||
{
|
|
||||||
pch = strchr((xdigits = xdigits_u), ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pch != nullptr)
|
|
||||||
{
|
|
||||||
val <<= 4;
|
|
||||||
val |= (pch - xdigits);
|
|
||||||
|
|
||||||
if (++seen_xdigits > 4)
|
|
||||||
{
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ch == ':')
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
curtok = address;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!seen_xdigits)
|
|
||||||
{
|
|
||||||
if (colonp)
|
|
||||||
{
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
colonp = tp;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endp - tp < 2)
|
|
||||||
{
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
*tp++ = (unsigned char)(val >> 8) & 0xff;
|
|
||||||
*tp++ = (unsigned char)val & 0xff;
|
|
||||||
seen_xdigits = 0;
|
|
||||||
val = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* \todo Handle IPv4 mapped address (2001::192.168.0.1) */
|
|
||||||
#if 0
|
|
||||||
if (ch == '.' && (endp - tp > 3 /* NS_INADDRSZ - 1 */)) &&
|
|
||||||
inet_pton4 (curtok, tp) > 0)
|
|
||||||
{
|
|
||||||
tp += 4 /*NS_INADDRSZ*/;
|
|
||||||
seen_xdigits = 0;
|
|
||||||
break; /* '\0' was seen by inet_pton4(). */
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (seen_xdigits)
|
|
||||||
{
|
|
||||||
if (endp - tp < 2)
|
|
||||||
{
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
*tp++ = (unsigned char)(val >> 8) & 0xff;
|
|
||||||
*tp++ = (unsigned char)val & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colonp != nullptr)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Since some memmove ()'s erroneously fail to handle
|
|
||||||
* overlapping regions, we'll do the shift by hand.
|
|
||||||
*/
|
|
||||||
const int n = tp - colonp;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if (tp == endp)
|
|
||||||
{
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 1; i <= n; i++)
|
|
||||||
{
|
|
||||||
endp[-i] = colonp[n - i];
|
|
||||||
colonp[n - i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
tp = endp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tp != endp)
|
|
||||||
{
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(addr, tmp, 16);
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ipv6Address::Ipv6Address()
|
Ipv6Address::Ipv6Address()
|
||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this);
|
NS_LOG_FUNCTION(this);
|
||||||
@@ -334,7 +173,14 @@ Ipv6Address::Ipv6Address(const Ipv6Address* addr)
|
|||||||
Ipv6Address::Ipv6Address(const char* address)
|
Ipv6Address::Ipv6Address(const char* address)
|
||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this << address);
|
NS_LOG_FUNCTION(this << address);
|
||||||
AsciiToIpv6Host(address, m_address);
|
|
||||||
|
if (inet_pton(AF_INET6, address, m_address) <= 0)
|
||||||
|
{
|
||||||
|
memset(m_address, 0x00, 16);
|
||||||
|
NS_LOG_LOGIC("Error, can not build an IPv6 address from an invalid string: " << address);
|
||||||
|
m_initialized = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,7 +202,13 @@ void
|
|||||||
Ipv6Address::Set(const char* address)
|
Ipv6Address::Set(const char* address)
|
||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this << address);
|
NS_LOG_FUNCTION(this << address);
|
||||||
AsciiToIpv6Host(address, m_address);
|
if (inet_pton(AF_INET6, address, m_address) <= 0)
|
||||||
|
{
|
||||||
|
memset(m_address, 0x00, 16);
|
||||||
|
NS_LOG_LOGIC("Error, can not build an IPv6 address from an invalid string: " << address);
|
||||||
|
m_initialized = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,93 +530,12 @@ Ipv6Address::Print(std::ostream& os) const
|
|||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this << &os);
|
NS_LOG_FUNCTION(this << &os);
|
||||||
|
|
||||||
// note: part of this function has been adapted from inet_ntop6 Linux function.
|
char str[INET6_ADDRSTRLEN];
|
||||||
// See http://www.net-snmp.org/dev/agent/inet__ntop_8c_source.html
|
|
||||||
// Author: Paul Vixie, 1996.
|
|
||||||
|
|
||||||
if (IsIpv4MappedAddress())
|
if (inet_ntop(AF_INET6, m_address, str, INET6_ADDRSTRLEN))
|
||||||
{
|
{
|
||||||
os << "::ffff:" << (unsigned int)m_address[12] << "." << (unsigned int)m_address[13] << "."
|
os << str;
|
||||||
<< (unsigned int)m_address[14] << "." << (unsigned int)m_address[15];
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t address[8];
|
|
||||||
uint8_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
address[i] = (uint16_t(m_address[2 * i]) << 8) | uint16_t(m_address[2 * i + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t bestBase = -1;
|
|
||||||
int8_t bestLen = 0;
|
|
||||||
int8_t curBase = -1;
|
|
||||||
int8_t curLen = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
if (address[i] == 0)
|
|
||||||
{
|
|
||||||
if (curBase == -1)
|
|
||||||
{
|
|
||||||
curBase = i;
|
|
||||||
curLen = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
curLen++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (curBase != -1)
|
|
||||||
{
|
|
||||||
if (bestBase == -1 || curLen > bestLen)
|
|
||||||
{
|
|
||||||
bestBase = curBase;
|
|
||||||
bestLen = curLen;
|
|
||||||
}
|
|
||||||
curBase = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (curBase != -1)
|
|
||||||
{
|
|
||||||
if (bestBase == -1 || curLen > bestLen)
|
|
||||||
{
|
|
||||||
bestBase = curBase;
|
|
||||||
bestLen = curLen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bestBase != -1 && bestLen < 2)
|
|
||||||
{
|
|
||||||
bestBase = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 8;)
|
|
||||||
{
|
|
||||||
// Are we inside the best run of 0x00's?
|
|
||||||
if (i == bestBase)
|
|
||||||
{
|
|
||||||
os << ':';
|
|
||||||
i += bestLen;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Are we following an initial run of 0x00s or any real hex?
|
|
||||||
if (i != 0)
|
|
||||||
{
|
|
||||||
os << ':';
|
|
||||||
}
|
|
||||||
os << std::hex << (unsigned int)address[i];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
// Was it a trailing run of 0x00's?
|
|
||||||
if (bestBase != -1 && (bestBase + bestLen) == 8)
|
|
||||||
{
|
|
||||||
os << ':';
|
|
||||||
}
|
|
||||||
os << std::dec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -1044,7 +815,10 @@ Ipv6Prefix::Ipv6Prefix()
|
|||||||
Ipv6Prefix::Ipv6Prefix(const char* prefix)
|
Ipv6Prefix::Ipv6Prefix(const char* prefix)
|
||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this << prefix);
|
NS_LOG_FUNCTION(this << prefix);
|
||||||
AsciiToIpv6Host(prefix, m_prefix);
|
if (inet_pton(AF_INET6, prefix, m_prefix) <= 0)
|
||||||
|
{
|
||||||
|
NS_ABORT_MSG("Error, can not build an IPv6 prefix from an invalid string: " << prefix);
|
||||||
|
}
|
||||||
m_prefixLength = GetMinimumPrefixLength();
|
m_prefixLength = GetMinimumPrefixLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1058,8 +832,10 @@ Ipv6Prefix::Ipv6Prefix(uint8_t prefix[16])
|
|||||||
Ipv6Prefix::Ipv6Prefix(const char* prefix, uint8_t prefixLength)
|
Ipv6Prefix::Ipv6Prefix(const char* prefix, uint8_t prefixLength)
|
||||||
{
|
{
|
||||||
NS_LOG_FUNCTION(this << prefix);
|
NS_LOG_FUNCTION(this << prefix);
|
||||||
AsciiToIpv6Host(prefix, m_prefix);
|
if (inet_pton(AF_INET6, prefix, m_prefix) <= 0)
|
||||||
|
{
|
||||||
|
NS_ABORT_MSG("Error, can not build an IPv6 prefix from an invalid string: " << prefix);
|
||||||
|
}
|
||||||
uint8_t autoLength = GetMinimumPrefixLength();
|
uint8_t autoLength = GetMinimumPrefixLength();
|
||||||
NS_ASSERT_MSG(autoLength <= prefixLength,
|
NS_ASSERT_MSG(autoLength <= prefixLength,
|
||||||
"Ipv6Prefix: address and prefix are not compatible: " << Ipv6Address(prefix)
|
"Ipv6Prefix: address and prefix are not compatible: " << Ipv6Address(prefix)
|
||||||
|
|||||||
Reference in New Issue
Block a user