IP addresses
For working with IP addresses, the library provides three classes:
- ipv4_address — A class for working with IPv4 addresses, where the instance size is always 4 bytes.
- ipv6_address — A class for working with IPv6 addresses, where the instance size includes both the space allocated for the IPv6 address itself and the scope id (zone index). As a result, the instance size will be 16 bytes plus the maximum length of the scope id (plus aligned bytes if any). Read about scope ids below.
- ip_address — Combines the
ipv4_address
and ipv6_address
classes via a union. This ensures version-independent IP address manipulation. It has implicit constructors for converting from ipv4_address
and ipv6_address
. The instance size is the same as that of ipv6_address
plus IP address version (and data alignment).
- Note
- Regardless of which class you use, the internal address is stored as an array of bytes, not as an unsigned integer. This design choice ensures that the address is stored in network byte order (big-endian) regardless of the platform. This is convenient because ultimately, this address can be used in sockets or similar libraries that require passing addresses in network byte order.
- Note
- All operations on addresses support compile-time computations. If an address is specified as a constant, during compilation, we get: errors if the address was entered incorrectly, the resulting IP address represented as bytes, and any other operations on addresses.
Parsing ip strings
Let's look at examples of obtaining IP addresses from string literals and strings.
int main() {
constexpr auto ip1 = ipv4_address::parse("192.168.1.1");
constexpr auto ip2 = ipv6_address::parse("64:ff9b::192.0.2.128");
constexpr auto ip3 = ip_address::parse("fe80::1ff:fe23:4567:890a%eth2");
constexpr auto ip4 = ipv4_address::parse<"192.168.1.1">();
constexpr auto ip5 = ipv6_address::parse<"fe80::1ff:fe23:4567:890a%eth2">();
constexpr auto ip6 = ip_address::parse<"192.168.1.1">();
constexpr auto ip7 = "192.168.1.1"_ipv4;
constexpr auto ip8 = "fe80::1ff:fe23:4567:890a%eth2"_ipv6;
constexpr auto ip9 = "fe80::1ff:fe23:4567:890a%eth2"_ip;
const char* str1 = "192.168.1.1";
const std::string str2 = "fe80::1ff:fe23:4567:890a%eth2";
auto ip10 = ipv4_address::parse(str1);
auto ip11 = ipv6_address::parse(str2);
auto ip12 = ip_address::parse(str2);
return 0;
}
The main include file for the ipaddress library.
Namespace dedicated to IP addresses and networks operations.
Definition base-v4.hpp:18
Access to specific versions of IP addresses for ip_address is demonstrated below.
int main() {
constexpr auto ipv4 = ipv4_address::parse("192.168.1.1");
constexpr auto ipv6 = ipv6_address::parse("fe80::1ff:fe23:4567:890a%eth2");
constexpr auto ipv4_version = ipv4.version();
constexpr auto ipv6_version = ipv6.version();
constexpr auto ipv4_size = ipv4.size();
constexpr auto ipv6_size = ipv6.size();
constexpr auto ip1 = ip_address::parse("192.168.1.1");
constexpr auto ip2 = ip_address::parse("fe80::1ff:fe23:4567:890a%eth2");
constexpr auto ip1_version = ip1.version();
constexpr auto ip2_version = ip2.version();
constexpr auto ip1_size = ip1.size();
constexpr auto ip2_size = ip2.size();
constexpr auto ip2_is_v4 = ip2.is_v4();
constexpr auto ip2_is_v6 = ip2.is_v6();
constexpr auto ip2_get_ipv4 = ip2.v4();
constexpr auto ip2_get_ipv6 = ip2.v6();
if (ip2_get_ipv4) {
constexpr auto result = ip2_get_ipv4.value();
}
if (ip2_get_ipv6.has_value()) {
constexpr auto result = ip2_get_ipv6.value();
}
return 0;
}
From uint/To uint
Below is code demonstrating how to create IP addresses from unsigned integers and convert them back to unsigned integers.
int main() {
constexpr auto ip1 = ipv4_address::from_uint(u1);
constexpr auto ip2 = ipv6_address::from_uint(u2);
constexpr auto ip3 = ip_address::from_uint(u1);
constexpr auto value1 = ip1.to_uint();
constexpr auto value2 = ip2.to_uint();
constexpr auto value3 = ip3.to_uint32();
return 0;
}
typename Base::uint_type uint_type
Type alias for the underlying unsigned integer type.
Definition ip-address-base.hpp:59
- Note
ipaddress
defines its type uint128_t
to work with 128-bit unsigned integers. It is designed to fill the gap in the C++ standard, which does not natively support 128-bit integers across all platforms. Unlike compiler-specific extensions like __int128
, uint128_t
ensures compatibility and portability across different compilers and architectures.
The implementation is inspired by the algorithms used in the .NET framework's UInt128, providing a reliable foundation for arithmetic operations and other integer-related functionalities.
Working with bytes
Below is an example of working with bytes and pointers to bytes directly.
int main() {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 255, 255,
0, 0, 0, 0
};
constexpr auto ip1 = ipv4_address::from_bytes({ 0xC0, 0xA8, 0x00, 0x01 });
constexpr auto ip2 = ipv6_address::from_bytes(ipv6_bytes);
constexpr auto ip3 = ip_address::from_bytes(ipv6_bytes);
constexpr std::uint8_t ipv4_bytes[] = { 0xC0, 0xA8, 0x00, 0x01 };
constexpr auto iv4 = ipv4_address::from_bytes(ipv4_bytes, 3);
constexpr auto iv5 = ipv6_address::from_bytes(ipv4_bytes, 3);
constexpr auto ip6 = ip_address::from_bytes(ipv4_bytes, 3, ip_version::V4);
constexpr auto ip7 = ip_address::from_bytes(ipv6_bytes.data(), 11, ip_version::V6);
constexpr auto ip1_byte_count = ip1.bytes().size();
constexpr auto ip2_byte_count = ip2.bytes().size();
constexpr auto ip3_byte_count = ip3.is_v4()
? ip3.v4().value().bytes().size()
: ip3.v6().value().bytes().size();
constexpr auto test1 = *(ip1.data() + 1);
constexpr auto test2 = *(ip2.data() + 10);
constexpr auto test3 = *(ip3.data() + 11);
return 0;
}
typename Base::base_type base_type
Type alias for the base storage type.
Definition ip-address-base.hpp:58
Comparison
For classes for working with IP addresses, comparison operators and spaceship operator (for C++20 and newer) have been redefined.
int main() {
constexpr auto ip1 = ipv6_address::parse("fe80::1ff:fe23:4567:890a");
constexpr auto ip2 = ipv6_address::parse("fe80::1ff:fe23:4567:890b");
constexpr auto test1 = ip1 == ip2;
constexpr auto test2 = ip1 != ip2;
constexpr auto test3 = ip1 < ip2;
constexpr auto test4 = ip1 <= ip2;
constexpr auto test5 = ip1 >= ip2;
constexpr auto test6 = ip1 > ip2;
constexpr auto ip3 = ipv6_address::parse("fe80::1ff:fe23:4567:890a%eth2");
constexpr auto ip4 = ipv6_address::parse("fe80::1ff:fe23:4567:890a%eth1");
constexpr auto test7 = ip2 == ip3;
constexpr auto test8 = ip2 != ip3;
constexpr auto test9 = ip2 < ip3;
constexpr auto test10 = ip2 <= ip3;
constexpr auto test11 = ip2 >= ip3;
constexpr auto test12 = ip2 > ip3;
constexpr auto test13 = ip3 == ip4;
constexpr auto test14 = ip3 != ip4;
constexpr auto test15 = ip3 < ip4;
constexpr auto test16 = ip3 <= ip4;
constexpr auto test17 = ip3 >= ip4;
constexpr auto test18 = ip3 > ip4;
return 0;
}
Properties
The library provides many properties for determining certain features of an IP address. Let's look at the example below (all possible properties are not presented here; for a more complete picture, see the documentation for the code).
int main() {
constexpr auto test1 = ipv6_address::parse("ffff::").is_multicast();
constexpr auto test2 = ip_address::parse("fdff::").is_multicast();
constexpr auto test3 = ipv4_address::parse("240.0.0.1").is_reserved();
constexpr auto test4 = ip_address::parse("239.255.255.255").is_reserved();
constexpr auto ip = ip_address::parse("2002:ac1d:2d64::1");
constexpr auto sixtofour = ip.sixtofour();
if (sixtofour) {
constexpr auto result = sixtofour.value();
}
return 0;
}
Convert to string
There are three different formats for converting IP addresses to strings as you can see below.
int main() {
constexpr auto ip1 = ipv4_address::parse("127.240.0.1");
auto str1 = ip1.to_string();
auto str2 = ip1.to_string(format::full);
auto str3 = ip1.to_string(format::compact);
auto str4 = ip1.to_string(format::compressed);
constexpr auto ip2 = ipv6_address::parse("fe80::1ff:fe23:4567:890a%eth2");
auto str5 = ip2.to_string();
auto str6 = ip2.to_string(format::full);
auto str7 = ip2.to_string(format::compact);
auto str8 = ip2.to_string(format::compressed);
auto reverse_pointer = ip2.reverse_pointer();
return 0;
}
IP networks
- ipv4_network — A class for working with IPv4 networks.
- ipv6_network — A class for working with IPv6 networks.
- ip_network — Combines the
ipv4_network
and ipv6_network
classes via a union. This ensures version-independent IP address manipulation. It has implicit constructors for converting from ipv4_network
and ipv6_network
. The instance size is the same as that of ipv6_network
.
Networks store two IP addresses (network address and netmask), as well as the prefix length. This means that the size of a network instance will be 2 * sizeof(<ip_type>) + sizeof(size_t)
.
Working with networks is similar to working with addresses. So let's get straight to the examples.
Parsing ip network strings
Let's look at examples of obtaining networks from string literals and strings.
int main() {
constexpr auto net1 = ipv4_network::parse("1.2.3.0/24");
constexpr auto net2 = ipv6_network::parse("2001:db8::/32");
constexpr auto net3 = ip_network::parse("1.2.3.4/255.255.255.255");
constexpr auto net4 = ipv4_network::parse<"1.2.3.0/24">();
constexpr auto net5 = ipv6_network::parse<"2001:db8::/32">();
constexpr auto net6 = ip_network::parse<"1.2.3.4/255.255.255.255">();
constexpr auto net7 = "1.2.3.0/24"_ipv4_net;
constexpr auto net8 = "2001:db8::/32"_ipv6_net;
constexpr auto net9 = "1.2.3.4/255.255.255.255"_net;
const char* str1 = "1.2.3.0/24";
const std::string str2 = "2001:db8::/32";
auto net10 = ipv4_network::parse(str1);
auto net11 = ipv6_network::parse(str2);
auto net12 = ip_network::parse(str2);
return 0;
}
- Note
- If the prefixlen is not present in the input string, then the length of the prefix will be equal to the maximum length (for ipv4 it is 32, and for ipv6 it is 128)
We can request various network parameters.
int main() {
constexpr auto net1 = ip_network::parse("2001:db8::/32");
constexpr auto network_address1 = net1.network_address();
constexpr auto broadcast_address1 = net1.broadcast_address();
constexpr auto netmask1 = net1.netmask();
constexpr auto hostmask1 = net1.hostmask();
constexpr auto prefixlen1 = net1.prefixlen();
constexpr auto net2 = ip_network::parse("1.2.3.0/255.255.255.0");
constexpr auto network_address2 = net2.network_address();
constexpr auto broadcast_address2 = net2.broadcast_address();
constexpr auto netmask2 = net2.netmask();
constexpr auto hostmask2 = net2.hostmask();
constexpr auto prefixlen2 = net2.prefixlen();
constexpr auto net3 = ip_network::parse("1.2.3.0/16", false);
constexpr auto network_address3 = net3.network_address();
constexpr auto broadcast_address3 = net3.broadcast_address();
constexpr auto netmask3 = net3.netmask();
constexpr auto hostmask3 = net3.hostmask();
constexpr auto prefixlen3 = net3.prefixlen();
return 0;
}
Create from address
A network can be created from an IP address and a prefixlen.
int main() {
constexpr auto ip4 = ipv4_address::parse("192.168.0.1");
constexpr auto ip6 = ipv6_address::parse("2002:ac1d:2d64::");
constexpr auto ip_any = ip_address::parse("2002:ac1d:2d64::");
constexpr ipv4_network net1 = ipv4_network::from_address(ip4, 24,
false);
constexpr ipv6_network net2 = ipv6_network::from_address(ip6, 64);
constexpr ip_network net3 = ip_network::from_address(ip4, 32);
constexpr ip_network net4 = ip_network::from_address(ip6, 64);
constexpr ip_network net5 = ip_network::from_address(ip_any, 64);
return 0;
}
A class that encapsulates both IPv4 and IPv6 network functionalities.
Definition ip-any-network.hpp:70
Comparison
For classes for working with IP networks, comparison operators and spaceship operator (for C++20 and newer) have been redefined.
int main() {
constexpr auto net1 = ip_network::parse("2002:ac1d:2d64::/64");
constexpr auto net2 = ip_network::parse("2002:ac1d:2d64::");
constexpr auto test1 = net1 == net2;
constexpr auto test2 = net1 != net2;
constexpr auto test3 = net1 < net2;
constexpr auto test4 = net1 <= net2;
constexpr auto test5 = net1 >= net2;
constexpr auto test6 = net1 > net2;
constexpr auto net3 = ip_network::parse("2002:ac1d:2d64::/64");
constexpr auto net4 = ip_network::parse("2002:ac1d:2d64::%1/64");
constexpr auto test7 = net2 == net3;
constexpr auto test8 = net2 != net3;
constexpr auto test9 = net2 < net3;
constexpr auto test10 = net2 <= net3;
constexpr auto test11 = net2 >= net3;
constexpr auto test12 = net2 > net3;
constexpr auto test13 = net3 == net4;
constexpr auto test14 = net3 != net4;
constexpr auto test15 = net3 < net4;
constexpr auto test16 = net3 <= net4;
constexpr auto test17 = net3 >= net4;
constexpr auto test18 = net3 > net4;
return 0;
}
Properties
The library provides many properties for determining certain features of an IP network. Let's look at the example below (all possible properties are not presented here; for a more complete picture, see the documentation for the code).
As you can see, getting network properties is not much different from getting address properties.
int main() {
constexpr auto test1 = ipv6_network::parse("ffff::").is_multicast();
constexpr auto test2 = ip_network::parse("fdff::").is_multicast();
constexpr auto test3 = ipv4_network::parse("240.0.0.1").is_reserved();
constexpr auto test4 = ip_network::parse("239.255.255.255").is_reserved();
return 0;
}
Convert to string
There are three different formats for converting IP networks to strings as you can see below.
int main() {
constexpr auto net1 = ip_network::parse("1.2.3.0/0.0.0.255");
auto str1 = net1.to_string();
constexpr auto net2 = ip_network::parse("2001:db8::/32");
auto str2 = net2.to_string();
auto str3 = net2.to_string(format::full);
auto str4 = net2.to_string(format::compact);
auto str5 = net2.to_string(format::compressed);
return 0;
}
Scope Id
The library supports Scope Id both as numeric values and as strings.
By default, Scope Id are enabled, and their maximum size is 16 characters (defined by IPADDRESS_IPV6_SCOPE_MAX_LENGTH
during compilation). Yes, the scope id is internally represented as a fixed-size array, and its maximum size cannot be changed dynamically. This means that you cannot store a scope id longer than IPADDRESS_IPV6_SCOPE_MAX_LENGTH
. There are several reasons for this design choice: simplicity in storing scope id, avoiding additional allocations (all data for the IP address will be as local as possible in memory), and enabling working with scope id in a constexpr manner.
- Note
- If you are certain that you don't need Scope Id, you can completely disable them to avoid any overhead related to their maintenance. This can be done by defining
IPADDRESS_NO_IPV6_SCOPE
during compilation. In this case, the instance size of ipv6_address
and ip_address
will be 16 bytes, and all operations related to retrieving or modifying the scope id will have no effect.
For IP addresses ipv6_address
and ip_address
, functionality is supported for obtaining and changing scope id.
#include <iostream>
int main() {
constexpr auto ip1 = ipv6_address::parse("fe80::1ff:fe23:4567:890a%eth2");
constexpr auto scope_id = ip1.get_scope_id();
if (scope_id) {
constexpr auto is_integer = scope_id.has_uint32();
constexpr auto is_string = scope_id.has_string();
std::cout << "scope id is integer for ip1: " << std::boolalpha << is_integer << std::endl;
std::cout << "scope id is string for ip1: " << std::boolalpha << is_string << std::endl;
std::cout << "scope id for ip1: " << scope_id.get_string() << std::endl;
}
auto ip2 = ip_address::parse("fe80::1ff:fe23:4567:890a");
std::cout << "has scope id for ip2: " << std::boolalpha << (bool) ip2.get_scope_id() << std::endl;
ip2.set_scope_id("123");
auto test_scope_id = ip2.get_scope_id();
if (test_scope_id) {
auto is_integer = test_scope_id.has_uint32();
auto is_string = test_scope_id.has_string();
std::cout << "scope id is integer for ip2: " << std::boolalpha << is_integer << std::endl;
std::cout << "scope id is string for ip2: " << std::boolalpha << is_string << std::endl;
std::cout << "scope id for ip2: " << test_scope_id.get_uint32() << std::endl;
}
return 0;
}
Std overrides
- Note
- If, for some reason, you don't want the library to overload standard functions, you can define
IPADDRESS_NO_OVERLOAD_STD
during compilation.
The library overloads some functions from the standard library so that IP addresses and networks (ipv4_address
, ipv6_address
, ip_address
, ipv4_network
, ipv6_network
and ip_network
) can be used in hash tables and other standard operations on std::swap
, etc.
#include <iostream>
#include <unordered_map>
int main() {
constexpr auto ip1 = ip_address::parse("127.0.0.1");
constexpr auto ip2 = ip_address::parse("2001:db8::1");
constexpr auto ip3 = ip_address::parse("2001:db8::1%scope");
std::unordered_map<ip_address, int> container;
container[ip1] = 1;
container[ip2] = 2;
container[ip3] = 3;
for (const auto& [key, value] : container) {
std::cout << key << ": " << value << std::endl;
}
std::cout << ip2 << std::endl;
std::cout << full << ip2 << std::endl;
std::cout << compact << ip2 << std::endl;
std::cout << compressed << ip2 << std::endl;
std::istringstream ss1("255.0.42.42 test");
std::istringstream ss2("2001:db8:0:0:0:0:0:1%scope test");
ss1 >> r1;
ss2 >> r2;
std::cout << "has errors: " << std::boolalpha << (ss1.fail() || ss2.fail()) << std::endl;
std::cout << r1 << std::endl;
std::cout << r2 << std::endl;
std::istringstream ss3("1.2.3.0/16 non strict mode sample");
std::istringstream ss4("2001:db8::/32 test");
ss3 >> non_strict >> n1;
ss4 >> n2;
std::cout << "has errors: " << std::boolalpha << (ss3.fail() || ss4.fail()) << std::endl;
std::cout << n1 << std::endl;
std::cout << n2 << std::endl;
return 0;
}
A class that represents an IP address, supporting both IPv4 and IPv6 formats.
Definition ip-any-address.hpp:73