我如何在C/ c++中减去两个IPv6地址(128位数字)

How can I subtract two IPv6 addresses (128bit numbers) in C/C++?

本文关键字:两个 IPv6 数字 128位 地址 c++      更新时间:2023-10-16

我将IP地址存储在支持四个32位数组的sockaddr_in6中,addr[4]。本质上是一个128位的数字。

我正试图计算给定IPv6范围内的ip数量(之间有多少ip)。所以这是一个用两个长度为4的数组相减另一个数组的问题。

问题是由于没有128bit数据类型,我无法转换为十进制。

非常感谢!

您可以使用某种big-int库(如果您可以容忍LGPL,则选择GMP)。幸运的是,如果需要的话,128位减法很容易手工模拟。下面是计算128位值的(a-b)绝对值的快速演示:

#include <iostream>
#include <iomanip>
struct U128
{
    unsigned long long hi;
    unsigned long long lo;
};
bool subtract(U128& a, U128 b)
{
    unsigned long long carry = b.lo > a.lo;
    a.lo -= b.lo;
    unsigned long long carry2 = b.hi > a.hi || a.hi == b.hi && carry;
    a.hi -= carry;
    a.hi -= b.hi;
    return carry2 != 0;
}
int main()
{
    U128 ipAddressA = { 45345, 345345 };
    U128 ipAddressB = { 45345, 345346 };
    bool carry = subtract(ipAddressA, ipAddressB);
    // Carry being set means that we underflowed; that ipAddressB was > ipAddressA.
    // Lets just compute 0 - ipAddressA as a means to calculate the negation 
    // (0-x) of our current value. This gives us the absolute value of the
    // difference.
    if (carry)
    {
        ipAddressB = ipAddressA;
        ipAddressA = { 0, 0 };
        subtract(ipAddressA, ipAddressB);
    }
    // Print gigantic hex string of the 128-bit value
    std::cout.fill ('0');
    std::cout << std::hex << std::setw(16) << ipAddressA.hi << std::setw(16) << ipAddressA.lo << std::endl; 
}

给出了差值的绝对值。如果范围不是很大(64位或更少),那么ipAddressA.lo可以作为简单的unsigned long long作为您的答案。

如果您有性能方面的问题,您可以利用编译器的内在特性来利用某些体系结构,如amd64,如果您希望它在该处理器上是最优的。

_subborrow_u64是必要的减法工作所固有的amd64。

in6_addr结构体以网络字节顺序(或'大端')存储地址,最高有效字节@ s6_addr[0]。您不能指望其他联合成员被一致地命名或定义。即使通过(不可移植的)uint32_t字段访问联合,值也必须用ntohl进行转换。因此,一个可移植的方法需要一些工作来寻找差异。

可以将in6_addr转换为uint64_t[2]。按照典型的"双位数"惯例,我们使用[0]表示低64位,[1]表示高64位:

static inline void
in6_to_u64 (uint64_t dst[2], const struct in6_addr *src)
{
    uint64_t hi = 0, lo = 0;
    for (unsigned int i = 0; i < 8; i++)
    {
        hi = (hi << 8) | src->s6_addr[i];
        lo = (lo << 8) | src->s6_addr[i + 8];
    }
    dst[0] = lo, dst[1] = hi;
}

和差异:

static inline unsigned int
u64_diff (uint64_t d[2], const uint64_t x[2], const uint64_t y[2])
{
    unsigned int b = 0, bi;
    for (unsigned int i = 0; i < 2; i++)
    {
        uint64_t di, xi, yi, tmp;
        xi = x[i], yi = y[i];
        tmp = xi - yi;
        di = tmp - b, bi = tmp > xi;
        d[i] = di, b = bi | (di > tmp);
    }
    return b; /* borrow flag = (x < y) */
}