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_next
和 ip_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;
}
我正在编写一个程序,我需要在其中迭代从给定 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_next
和 ip_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;
}