Я пытаюсь создать функцию, которая сможет анализировать любой диапазон IP / CIDR и выбирать случайный IP в этом конкретном диапазоне в виде строки на C (включая /32
, который будет просто возвращать один IP-адрес каждый раз). На данный момент меня это устраивает, включая зарезервированные IP-адреса (например, широковещательные), и если у меня возникнут проблемы с их исключением в будущем, я отправлю отдельный вопрос.
Я все еще довольно новичок в этой области, так как у меня еще нет большого опыта использования побитовых операторов с целочисленными битами (я понимаю сами побитовые операторы, но я пытаюсь понять, как использовать их с сетями и IP-адресами). Я также прочитал большую часть этот вопрос, который дает много отличных советов / указаний (спасибо Рону Мопину за предоставленный мне это), но я все еще пытаюсь заставить эту функцию полностью работать.
У меня почти рабочий код, но по какой-то причине использование /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 25 раз из данного IP / CIDR. При использовании /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
. Я мог бы предположить, что делаю что-то неправильно при смещении влево по диапазону CIDR при создании случайного 32-битного целого числа IP. Однако я не уверен, что мне здесь делать.
При использовании диапазона /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 (Ubuntu 20.04 на ядре 5.4.0
).
Любая помощь будет принята с благодарностью и спасибо за ваше время!
RAND_MAX
в вашей системе? Второй вопрос: почему статическая маска для основной части? Она должна быть инверсией другой маски, как в0xFFFF0000
и0x0000FFFF
. Сетевая маска против маски хоста. - person tadman   schedule 26.10.2020uint32_t
нативного, сделайте свои вычисления, а затем верните его обратно к сетевому порядку байтов. - person tadman   schedule 26.10.2020/usr/include/stdlib.h
,RAND_MAX
определяется как 2147483647. Я также пробовал отменить определение и переопределить его в своей программе как2147483647
на всякий случай и получил те же результаты, что и выше. - person Christian Deacon   schedule 26.10.2020rand()
, а затем объединить их в случайный IPv4-адрес, который вы можете при необходимости замаскировать для другого. - person tadman   schedule 26.10.202010.0.0.0/8
, вы должны сдвинуть случайное число вправо на 8 бит (/8
), а затем добавить его к 32-битному целому числу без знака10.0.0.0
(10100000000000000000000000000000
). - person Ron Maupin   schedule 26.10.202032 - mask length
, а затем добавляться к сетевому адресу. - person Ron Maupin   schedule 26.10.2020inet_aton
выполняет правильное преобразование в форму хоста, и вы можете перевернуть это с помощьюntohl
? - person tadman   schedule 26.10.2020libc
. Так делалось десятилетиями. Если вы анализируете IP-адреса своим собственным кодом, вы ошибаетесь. IPv4 может быть выражен разными способами, а не толькоx.x.x.x
. - person tadman   schedule 27.10.2020