我正在编写一个程序,其中我需要迭代从用户给定的cidr(例如75.24.64.0/24
)派生的地址列表。
我看了一些代码,但似乎过于复杂了。
最后,我决定使用一个看起来像这样的结构:
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可用的环境,这样我就可以使用显式位宽数据类型(最大限度地提高可移植性,因为您没有指定体系结构)。我还假设IPv4,因为这是您的示例字符串。
接下来,我使用sscanf将字符串转换为地址的组成部分。将字节合并为完整的32位值应该是直接的。我将我的文字标记为无符号长,以确保如果我们在一个小的位宽机器上,在赋值之前不会截断结果。
设置mask
的表达式背后的思想是,CIDR中的位计数指定了指示网络的最高有效位的数量,所以如果我们从32中减去它,这就是我们需要向上移动一整组位才能获得该掩码的数量(在截断后)。例如,32的掩码将是所有比特,并且32-32=0,所以我们根本不会移位,给出所有32比特。比特计数为24(如您的示例)将给出32-24=8,并且0xFFFFFFFF<lt;8是截断后的0xFFFFFF00(或十进制记数法中的255.255.0)
最后,为了获得初始IP,我只需使用逐位AND将掩码应用于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;
}