首页 文章

如何在C / C中减去两个IPv6地址(128位数)?

提问于
浏览
6

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

我正在尝试计算给定IPv6范围内的IP数量(两者之间的IP数) . 所以这是使用两个长度为4的数组从一个减去另一个的问题 .

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

万分感谢!

2 回答

  • 1

    您可以使用某种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 答案 .

    如果您有perf问题,可以使用编译器内在函数来利用某些体系结构,例如amd64,如果您希望它在该处理器上是最佳的 . _subborrow_u64 是必要的减法工作的amd64内在函数 .

  • 3

    in6_addr 结构以网络字节顺序存储地址 - 或'big endian' - 使用最高有效字节@ s6_addr[0] . 您不能指望其他联盟成员一致地命名或定义 . 即使您通过(非可移植) uint32_t 字段访问了联合,也必须使用 ntohl 转换值 . 因此,找到差异的便携方法需要一些工作 .

    您可以将 in6_addr 转换为 uint64_t[2] . 坚持典型的'bignum'约定,我们使用[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) */
    }
    

相关问题