C CIDR 到地址列表

C CIDR to address list

我正在编写一个程序,我需要在其中迭代从给定 cidr 的用户派生的地址列表(例如 75.24.64.0/24)。

我看了一些 code,但看起来太复杂了。

最后我决定使用一个看起来像这样的结构:

struct ip_iterator {
    unsigned int netmask;
    int bitcount;
    long long num_total;
    long long num_left;
    int current_ip[4];
};

然后我可以定义 ip_iterator_init ip_iterator_nextip_iterator_is_finished 函数。但是,我坚持如何从 cidr 到第一个 IP。我不久前学习了网络数学,但自从获得认证后,我一直在使用在线计算器。

假设您在一个字符串中有 CIDR,这样的事情可能会为您完成。

首先,将CIDR转换为IP和掩码的函数:

int cidr_to_ip_and_mask(const char *cidr, uint32_t *ip, uint32_t *mask)
{
    uint8_t a, b, c, d, bits;
    if (sscanf(cidr, "%hhu.%hhu.%hhu.%hhu/%hhu", a, b, c, d, bits) < 5) {
        return -1; /* didn't convert enough of CIDR */
    }
    if (bits > 32) {
        return -1; /* Invalid bit count */
    }
    *ip =
        (a << 24UL) |
        (b << 16UL) |
        (c << 8UL) |
        (d);
    *mask = (0xFFFFFFFFUL << (32 - bits)) & 0xFFFFFFFFUL;
}

接下来获取第一个IP的片段:

uint32_t ip;
uint32_t mask;
uint32_t first_ip;
if (cidr_to_ip_and_mask(cidr, &ip, &mask) < 0) {
    /* return some failure */
}
first_ip = ip & mask;

首先,我假设 C99 或 stdint.h 可用的环境,以便我可以使用显式 bit-width 数据类型(最大可移植性,因为您没有指定体系结构)。我也假设 IPv4 因为那是你的示例字符串。

接下来我使用 sscanf 将字符串转换为地址的组成部分。将字节组合成完整的 32 位值应该很简单。如果我们在小型位宽机器上,我将我的文字标记为 unsigned long 以确保结果在赋值之前不会被截断。

设置 mask 的表达式背后的想法是 CIDR 中的位计数指定了多少个最高有效位表示网络,因此如果我们从 32 中减去它,那就是我们需要向上移动多少位获取该掩码的完整位集(截断后)。例如,32 的掩码将是所有位,并且 32-32 = 0 所以我们根本不会移位,给出所有 32 位。位数为 24(如您的示例)将给出 32-24=8,截断后 0xFFFFFFFF << 8 为 0xFFFFFF00(或十进制表示法中的 255.255.255.0)

最后,为了获得初始 IP,我只需将掩码应用到 IP 地址并按位与。简单!

由于你的标题问题讨论了整个列表,你可以通过将掩码的补码与基数进行 OR 运算来获得最终地址:

uint32_t finalIP = first_ip | ~mask;

这也应该等于广播地址。然后,您可以按顺序从 firstIP 迭代到 finalIP,包括或排除 finalIP 取决于您是否需要广播地址(如果您需要网络地址,则包括或排除 firstIP)。

int cidr_to_ip_and_mask(const char *cidr, uint32_t *ip, uint32_t *mask)
{
    uint8_t a, b, c, d, bits;
    if (sscanf(cidr, "%hhu.%hhu.%hhu.%hhu/%hhu", &a, &b, &c, &d, &bits) < 5) {
        return -1; /* didn't convert enough of CIDR */
    }
    if (bits > 32) {
        return -1; /* Invalid bit count */
    }
    *ip =
        (a << 24UL) |
        (b << 16UL) |
        (c << 8UL) |
        (d);
    *mask = (0xFFFFFFFFUL << (32 - bits)) & 0xFFFFFFFFUL;
}