问题描述
我正在尝试创建一个函数,该函数将能够解析任何IP / CIDR范围,并选择该特定范围内的随机IP作为C中的字符串(包括/32
,它将仅返回每次使用一个IP地址)。截至目前,我可以接受保留IP(例如广播),如果以后在排除这些IP时遇到问题,我会提出一个单独的问题。
由于在整数位上使用按位运算符还没有很多经验(我了解按位运算符本身,但是我试图弄清楚如何将它们与网络配合使用, IPs)。我也阅读了this的大部分问题,该问题给出了很多很好的建议/指导(感谢罗恩·莫普因(Ron Maupin)向我提供了此建议),但我仍在努力使此功能完全正常工作。 >
我几乎可以使用代码,但是由于某种原因,使用/8
CIDR或小于/24
的任何东西都会导致异常行为。使用/16
和/24
可以按预期工作(到目前为止,我已经测试了所有这些)。
这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <arpa/inet.h>
#include <time.h>
int main()
{
for (int i = 0; i < 25; i++)
{
// IP/CIDR.
char *sip = "10.0.0.0";
uint8_t cidr = 8;
// Randomize the rand() seed.
time_t t;
srand((unsigned) time(&t) + i);
// Create in_addr and convert the IP string to a 32-bit integer.
struct in_addr inaddr;
inet_aton(sip,&inaddr);
uint32_t ipaddr = inaddr.s_addr;
// Get the mask (the complement of 2 to the power of the CIDR minus one).
uint32_t mask = ((1 << cidr) - 1);
// Generate a random number using rand().
uint32_t randnum = rand(); // Also tried rand() % 256.
// Attempt to pick a random IP from the CIDR range. We shift left by the CIDR range since it's big endian.
uint32_t newIP = ipaddr & mask | ((0x0000ffff & randnum) << cidr);
// Convert the new IP to a string and print it.
struct in_addr ip;
ip.s_addr = newIP;
fprintf(stdout,"%s\n",inet_ntoa(ip));
}
return 0;
}
这只是从给定IP / CIDR中随机选择25次IP。使用/8
(例如10.0.0.0/8
)时,这是我收到的输出:
10.220.186.0
10.180.229.0
10.231.159.0
10.24.70.0
10.217.108.0
10.50.250.0
10.170.108.0
10.48.139.0
10.183.205.0
10.61.48.0
10.3.221.0
10.161.252.0
10.48.1.0
10.146.183.0
10.138.139.0
10.33.27.0
10.19.70.0
10.109.253.0
10.5.8.0
10.124.154.0
10.109.145.0
10.53.29.0
10.223.111.0
10.18.229.0
10.255.99.0
最后一个八位位组始终为0
。我想我在创建随机IP 32位整数时向CIDR范围左移时,我做错了什么。但是,我不确定我应该在这里做什么。
使用/30
范围(例如192.168.90.4/30
)时,这是我收到的输出:
192.168.90.68
192.168.90.196
192.168.90.68
192.168.90.68
192.168.90.68
192.168.90.4
192.168.90.196
192.168.90.68
192.168.90.196
192.168.90.68
192.168.90.132
192.168.90.4
192.168.90.196
192.168.90.68
192.168.90.196
192.168.90.196
192.168.90.4
192.168.90.68
192.168.90.132
192.168.90.4
192.168.90.68
192.168.90.68
192.168.90.132
192.168.90.196
192.168.90.196
它会在正确的时间选择192.168.90.4
,但其他三个随机IP不在/30
范围内,但在192.168.90.0/24
内。
使用/16
时(例如172.16.0.0/16
),这是预期的输出:
172.16.35.154
172.16.97.234
172.16.31.37
172.16.201.87
172.16.57.212
172.16.254.128
172.16.183.172
172.16.54.210
172.16.248.145
172.16.186.83
172.16.250.34
172.16.250.160
172.16.23.185
172.16.125.238
172.16.206.16
172.16.57.32
172.16.65.137
172.16.202.94
172.16.164.138
172.16.241.182
172.16.154.186
172.16.197.103
172.16.184.21
172.16.96.172
172.16.195.86
这同样适用于/24
(例如192.168.90.0/24
):
192.168.90.253
192.168.90.156
192.168.90.65
192.168.90.189
192.168.90.22
192.168.90.238
192.168.90.150
192.168.90.106
192.168.90.63
192.168.90.64
192.168.90.64
192.168.90.54
192.168.90.104
192.168.90.110
192.168.90.34
192.168.90.187
192.168.90.202
192.168.90.73
192.168.90.206
192.168.90.13
192.168.90.15
192.168.90.220
192.168.90.114
192.168.90.125
192.168.90.70
我想知道是否有人知道我在这里做错了什么。如果我也缺少明显的东西,我深表歉意。
我也在Linux(5.4.0
内核上的Ubuntu 20.04)上进行开发。
我们将不胜感激,感谢您的宝贵时间!
解决方法
我使用host-endian计算对它进行了重新设计,并且将很多本来不应该存在的事情从循环中移出了
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <arpa/inet.h>
#include <time.h>
int main(int argc,char** argv)
{
if (argc < 3) {
printf("Usage: cidrrand net cidr_size\n");
exit(-1);
}
char *sip = argv[1];
uint8_t cidr = atoi(argv[2]);
srand(time(NULL));
struct in_addr inaddr;
inet_aton(sip,&inaddr);
uint32_t ipaddr = ntohl(inaddr.s_addr);
uint32_t host_mask = (1 << (32 - cidr)) - 1;
for (int i = 0; i < 25; i++)
{
uint32_t host_rand = rand();
// Attempt to pick a random IP from the CIDR range. We shift left by the CIDR range since it's big endian.
uint32_t newIP = (ipaddr & ~host_mask) | (host_mask & host_rand);
// Convert the new IP to a string and print it.
struct in_addr ip;
ip.s_addr = htonl(newIP);
fprintf(stdout,"%s\n",inet_ntoa(ip));
}
return 0;
}
播种随机数时,请尝试一次播种一次。除非您有与生成多个可重复序列相关的特定目标,否则请不要混淆它。
,这里的主题非常有用 - 感谢 Christian,感谢 Scott 重新审视初始代码版本。
以防万一有人对 IPv6 提出类似的问题,我为 IPv4 和 IPv6 生成器 here 创建了一个简短示例。
它包括地址和掩码的二进制表示,以便更好地理解。
我目前在 IPv6 中遇到的问题是重复使用来自单个 32 位随机值的所有可用八位字节,同时迭代需要根据网络掩码更改的 IPv6 八位字节:
https://github.com/defanator/cidr-random/blob/master/cidr_random6.c#L133-L150
无论如何,希望它可以作为任何感兴趣的人的起点。