C CIDR к списку адресов
Я пишу программу, в которой мне нужно перебрать список адресов, полученных из заданного пользователем 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. Некоторое время назад я изучил сетевую математику, но с момента получения сертификата я использую онлайн-калькуляторы.
1 ответ
Предполагая, что у вас есть 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-битное значение должно быть прямым. Я помечаю свои литералы как unsigned long, чтобы удостовериться, что результаты не усечены перед назначением, если мы находимся на маленькой битовой машине.
Идея выражения для настройки mask
в том, что количество битов в CIDR указывает, сколько старших битов указывают на сеть, поэтому, если мы вычтем это из 32, это то, что нам нужно, чтобы сдвинуть полный набор битов, чтобы получить эту маску (после усечения). Например, маска из 32 будет иметь все биты, а 32-32 = 0, поэтому мы не будем сдвигаться вообще, давая все 32 бита. Число битов в 24 (как в вашем примере) даст 32-24=8, а 0xFFFFFFFF << 8 будет 0xFFFFFF00 после усечения (или 255.255.255.0 в десятичной записи)
Наконец, чтобы получить начальный IP-адрес, я просто применяю маску к IP-адресу с побитовым AND. Просто!
Поскольку ваш титульный вопрос обсуждает весь список, вы можете получить окончательный адрес, применив дополнение маски к базе:
uint32_t finalIP = first_ip | ~mask;
Это также должно быть равно широковещательному адресу. Затем вы можете переходить от первого IP к финальному IP по порядку, включая или исключая finalIP в зависимости от того, нужен ли вам широковещательный адрес или нет (и включая или исключая first 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;
}