如何使用C或C 查找给定的IPv6地址是否属于CIDR范围
How to find whether a given IPv6 address falls in the CIDR range using C or C++?
如果我有IPv6地址2001:4860:4860:0000:0000:0000:0000:0000:012D:8888,想找到它是否属于给定的CIDR范围2001:4860:4860:4860:4860:4860:4860:4860:4860:4860:4860:4860:4860:4860:4860:0000:0000:0000:0000:8888/32。我该如何在C或C ?
中执行此操作我的尝试与IPv4的尝试类似。(ip& netmask(==(范围& netmask(
unsigned int ipv6 = (ip[0]<<112) + (ip[1]<<96) + (ip[2]<<80) + (ip[3]<<64)+ (ip[4]<<48) + (ip[5]<<32) + (ip[6]<<16) + ip[7];
unsigned int range = (cidr_ip[0]<<112) + (cidr_ip[1]<<96) + (cidr_ip[2]<<80) + (cidr_ip[3]<<64)+ (cidr_ip[4]<<48) + (cidr_ip[5]<<32) + (cidr_ip[6]<<16) + cidr_ip[7];
unsigned int mask = (~0u) << (128-netmask);
if((ipv6 & mask) == (range & mask)){
printf("matchedn");
}
else
{
printf("no matchn");
}
这对我预期不起作用。以上IPv6属于该范围。但是该程序说"不匹配"。
BOOST ASIO的IP地址和范围类为您完成大部分工作。不幸的是,他们目前没有提供从字符串中解析IP地址范围的方法。
以下大多数代码是IP地址范围的解析器,该代码可与IP4和IP6一起使用:
#include <iostream>
#include <boost/asio.hpp>
using namespace boost::asio::ip;
template < typename Addr >
bool parseAddress( const std::string& str, Addr& addr )
{
boost::system::error_code ec;
addr = Addr::from_string( str, ec );
return !ec;
}
address_v4_range getRange( address_v4 address, size_t size )
{
address_v4 end = address_v4( ( address.to_ulong() + ( 1 << ( 32 - size ) ) ) & 0xFFFFFFFF );
return address_v4_range( address, end );
}
address_v6_range getRange( address_v6 address, size_t size )
{
auto bytes = address.to_bytes();
size_t offset = size >> 3;
uint8_t toAdd = 1 << ( 8 - ( size & 0x7 ) );
while ( toAdd )
{
int value = bytes[ offset ] + toAdd;
bytes[ offset ] = value & 0xFF;
toAdd = value >> 8;
if ( offset == 0 )
{
break;
}
offset--;
}
address_v6 end = address_v6( bytes );
return address_v6_range( address, end );
}
template < typename Addr >
bool parseRange( const std::string& str, basic_address_range< Addr >& range )
{
size_t pos = str.find( '/' );
if ( pos == std::string::npos )
{
return false;
}
// should only be one slash
if ( str.find( '/', pos + 1 ) != std::string::npos )
{
return false;
}
boost::system::error_code ec;
Addr address;
if ( !parseAddress( str.substr( 0, pos ), address ) )
{
return false;
}
std::string sizeStr = str.substr( pos + 1 );
size_t index;
int size = std::stoi( sizeStr, &index );
if ( index != sizeStr.size() )
{
return false;
}
if ( size > std::tuple_size< typename Addr::bytes_type >::value * 8 || size < 0 )
{
return false;
}
range = getRange( address, size );
return !ec;
}
int main()
{
address_v6 address;
if ( !parseAddress( "2001:4860:4860:0000:0000:0000:012D:8888", address ) )
{
std::cout << "invalid addressn";
return 1;
}
address_v6_range range;
if ( !parseRange( "2001:4860:4860:0000:0000:0000:0000:8888/32", range ) )
{
std::cout << "invalid rangen";
return 1;
}
bool inRange = range.find( address ) != range.end();
std::cout << "in range: " << inRange << "n";
return 0;
}
问题是unsigned int通常只有32位。有时是更多,有时是少的,但是今天没有128位的主流编译器。IPv6地址需要128位。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//Little struct containing a unsigned int array with the size of 4
//4 * 32bit = 128bit (== IPv6)
typedef struct IPv6_address{
uint32_t ip_parts[4];
} IPv6;
IPv6 and(IPv6 first, IPv6 second);
short match(IPv6 first, IPv6 second);
IPv6 mask_from_prefix(int prefix);
int main(){
int netmask = 32;
unsigned int ip[8] = {0x2001, 0x4860, 0x4860, 0, 0, 0, 0x012D, 0x8888};
unsigned int cidr_ip[8] = {0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888};
IPv6 ipv6 = {(ip[0]<<16) + (ip[1]), (ip[2]<<16) + (ip[3]), (ip[4]<<16) + (ip[5]), (ip[6]<<16) + ip[7]};
IPv6 range = {(cidr_ip[0]<<16) + (cidr_ip[1]), (cidr_ip[2]<<16) + (cidr_ip[3]), (cidr_ip[4]<<16) + (cidr_ip[5]), (cidr_ip[6]<<16) + cidr_ip[7]};
IPv6 mask = mask_from_prefix(netmask);
//if((ipv6 & mask) == (range & mask)){
if(match(and(ipv6, mask), and(range, mask))){
printf("matchedn");
}
else{
printf("no matchn");
}
return EXIT_SUCCESS;
}
//Bitwise-AND operation between two IPv6 addresses (128 bit integer as struct)
IPv6 and(IPv6 first, IPv6 second){
IPv6 toReturn = {};
for(int i = 0; i < 4; i++){
toReturn.ip_parts[i] = first.ip_parts[i] & second.ip_parts[i];
}
return toReturn;
}
//Returns 1, if the given IPv6 addresses match
//Otherwise 0
short match(IPv6 first, IPv6 second){
short matchCount = 0;
for(int i = 0; i < 4; i++){
if(first.ip_parts[i] == second.ip_parts[i]){
matchCount++;
}
}
return matchCount == 4 ? 1 : 0; //If all four parts match return 1, otherwise 0
}
//Returns an IPv6 address representing the net mask of the given prefix
IPv6 mask_from_prefix(int prefix){
IPv6 mask = {};
for(int i = 0; i < 4; i++){
int onesForThisPart = prefix - i*32;
//Check if 0 (or less), because shifting by 32 would result in an "erroneous" behavior, where nothing would happen at all
if(onesForThisPart <= 0){
mask.ip_parts[i] = 0;
}
else{
mask.ip_parts[i] = (~0u) << (32 - onesForThisPart);
}
}
return mask;
}
这不是最美丽的解决方案,但它起作用。希望我能提供帮助!
,除非您的系统上有128位未签名的INT(您不符合(,否则您在那里的范围内。之后,您的数学不再起作用。
改用不错的IP库!
将IPv6地址视为字节数组而不是整数类型,因为它们是128位,并且在网络字节顺序中。
#include <algorithm> // std::equal
#include <cassert> // assert
struct ipv6addr {
unsigned char addr[16];
bool is_subnet(const ipv6addr& prefix, unsigned int len) const {
assert(len <= sizeof(addr) * 8);
int cmp_bytes = len / 8;
if (!std::equal(addr, addr + cmp_bytes, prefix.addr)) return false;
int cmp_bits = len % 8;
if (cmp_bits) {
int bitmask = 0xff << (8 - cmp_bits);
if ((addr[cmp_bytes] & bitmask) != (prefix.addr[cmp_bytes] & bitmask))
return false;
}
return true;
}
};
int main() {
ipv6addr addr {0x20, 0x01, 0x48, 0x60, 0, 0, 0, 0, 0, 0, 0x01, 0x2D, 0x88, 0x88};
ipv6addr prefix {0x20, 0x01, 0x48, 0x60, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x88};
unsigned int len = 32;
assert(addr.is_subnet(prefix, len));
return 0;
}
如果您真的想将它们存储为整数,则可以使用struct { uint64_t hi; uint64_t lo; }
之类的东西,但是您需要在访问/修改这些字段时考虑Endianness。
编辑:开始考虑这一点,我意识到使用memcmp进行子网比较不正确。我纠正了上面的代码。另外,我已经用std::equal
替换了memcmp
,但是任何一个都可以。我并没有进行广泛的测试,因此,如果您决定使用它,则绝对应该进行更多测试。
- 当一个新对象被分配到它的地址时,对象是否必须被销毁
- 是否可以在C++中获取 CHAR 的有效十六进制地址?
- 是否通过向封闭对象的地址添加字节偏移量来访问子对象
- 是否可以仅通过将分配的指针地址存储在C++中来分析内存?
- c++ 编译器是否保护常量内存地址免受任何更改?
- 模板到函数的时刻地址是否可以作为指向某个函数的函数指针传递?
- 内存地址是否由于未对齐的内存地址而损坏?
- 在 C/C++ 中,是否可以通过使用指针更改"important"内存地址的值来创建简单的恶意软件?
- 通过引用传递是否涉及复制地址
- 是否可以在运行时为宏分配地址
- 初始化值是否保证通过其自己的地址反映,而不考虑内存顺序
- 对象地址是否保证是其类型对齐的倍数
- C++中是否有一个函数可以为您获取指向该节点的所有指针的地址空间
- 使用全局 IP 地址时,C++ winsock 2 应用程序中的代码是否必须更改?
- 是否可以在C++中更改带有指针的变量的地址
- 使用成员的地址初始化基类是否合法?
- 类实例的地址是否等于类中唯一元素的地址
- 此示例是否应该更改内存地址而不是该内存地址的值?
- 在某些情况下,指针的地址是否会更改
- 获取未初始化对象成员的地址是否定义良好?