简体   繁体   English

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

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

I'm storing the IP address in sockaddr_in6 which supports an array of four 32-bit, addr[4] . 我将IP地址存储在sockaddr_in6 ,它支持四个32位addr [4]的数组。 Essentially a 128 bit number. 本质上是一个128位数字。

I'm trying to calculate number of IPs in a given IPv6 range (how many IPs between). 我正在尝试计算给定IPv6范围内的IP数量(之间有多少IP)。 So it's a matter of subtracting one from another using two arrays with a length of four. 因此,这是使用两个长度为四的数组从另一个数组中减去一个数组的问题。

The problem is since there's no 128bit data type, I can't convert into decimal. 问题是因为没有128位数据类型,所以我不能转换为十进制。

Thanks a ton! 万分感谢!

You could use some kind of big-int library (if you can tolerate LGPL, GMP is the choice). 您可以使用某种big-int库(如果您可以忍受LGPL,则可以选择GMP)。 Fortunately, 128 bit subtraction is easy to simulate by hand if necessary. 幸运的是,如果需要,可以很容易地手动模拟128位减法。 Here is a quick and dirty demonstration of computing the absolute value of (ab), for 128 bit values: 这是对128位值计算(ab)绝对值的快速而肮脏的演示:

#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; 
}

This gives you the absolute value of the difference. 这为您提供了差异的绝对值。 If the range is not huge (64 bits or less), then ipAddressA.lo can be your answer as a simple unsigned long long . 如果范围不是很大(64位或更少),则ipAddressA.lo可以作为简单的unsigned long long答案。

If you have perf concerns, you can make use of compiler intrinsics for taking advantage of certain architectures, such as amd64 if you want it to be optimal on that processor. 如果您有性能方面的问题,可以使用编译器内部函数来利用某些体系结构,例如amd64(如果您希望在该处理器上实现最佳性能)。 _subborrow_u64 is the amd64 intrinsic for the necessary subtraction work. _subborrow_u64是必需减法工作的amd64内在函数。

The in6_addr structure stores the address in network byte order - or 'big endian' - with the most significant byte @ s6_addr[0] . in6_addr结构以网络字节顺序(或“大端”)存储地址,其中最高有效字节为s6_addr[0] You can't count on the other union members being consistently named, or defined. 您不能指望其他被一致命名或定义的工会成员。 Even If you accessed the union through a (non-portable) uint32_t field, the values would have to be converted with ntohl . 即使您通过(非便携式) uint32_t字段访问联合,也必须使用ntohl转换值。 So a portable method of finding the difference needs some work. 因此,找到差异的可移植方法需要一些工作。

You can convert the in6_addr to uint64_t[2] . 您可以将in6_addr转换为uint64_t[2] Sticking with typical 'bignum' conventions, we use [0] for the low 64-bits and [1] for the high 64-bits: 遵循典型的“ 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;
}

and the difference: 和区别:

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) */
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM