Вопрос
Я использовал драгоценный камень ip-адреса, и, похоже, у него нет возможности конвертировать из сетевой маски вида
255.255.255.0
в форму CIDR
/24
У кого-нибудь есть идеи, как быстро преобразовать первое во второе?
Решение
Вот быстрый и грязный способ
require 'ipaddr'
puts IPAddr.new("255.255.255.0").to_i.to_s(2).count("1")
Для этого должна быть соответствующая функция, я не смог ее найти, поэтому я просто считаю "1".
Если вы собираетесь использовать функцию в нескольких местах и не возражаете против monkeypatching, это может помочь:
IPAddr.class_eval
def to_cidr
"/" + self.to_i.to_s(2).count("1")
end
end
Тогда вы получите
IPAddr.new('255.255.255.0').to_cidr
# => "/24"
Другие советы
Просто для справки, чтобы информация была легко доступна для тех, кто ищет ...
Вот простой способ преобразования из CIDR в формат маски сети:
def cidr_to_netmask(cidr)
IPAddr.new('255.255.255.255').mask(cidr).to_s
end
Например:
cidr_to_netmask(24) #=> "255.255.255.0"
cidr_to_netmask(32) #=> "255.255.255.255"
cidr_to_netmask(16) #=> "255.255.0.0"
cidr_to_netmask(22) #=> "255.255.252.0"
Вот более математический подход, позволяющий избежать строк любой ценой:
def cidr_mask
Integer(32-Math.log2((IPAddr.new(mask,Socket::AF_INET).to_i^0xffffffff)+1))
end
с " маской " будучи строкой как 255.255.255.0. Вы можете изменить его и изменить первый аргумент на «quot; mask» " если " маска " уже является целочисленным представлением IP-адреса.
Так, например, если маска была " 255.255.255.0 " ;, IPAddr.new (mask, Socket :: AF_INET) .to_i стал бы 0xffffff00, который затем xor'd с 0xffffffff, что равно 255. р>
Мы добавляем 1 к этому, чтобы сделать его полным диапазоном из 256 хостов, затем находим лог-базу 2 из 256, что равно 8 (биты, используемые для адреса хоста), затем вычитаем это 8 из 32, что равно 24 (биты, используемые для сетевого адреса).
Затем мы приводим к целому числу, потому что Math.log2 возвращает число с плавающей точкой.
Быстрое и грязное преобразование:
"255.255.255.0".split(".").map { |e| e.to_i.to_s(2).rjust(8, "0") }.join.count("1").split(".")
=> Я разделяю маску на Массив
.map { |e| e.to_i.to_s(2).rjust(8, "0") }
=> Для каждого элемента в массиве:
.to_i
=> Преобразовать в целое число
.to_s(2)
=> Преобразовать целое число в двоичное
.rjust(8, "0")
=> Добавить отступы
=> Map возвращает массив с такой же мощностью
.join
=> Преобразовать массив в полную строку
.count("1")
=> Количество символов "1" => Присвоить маску CIDR
def mask_2_ciddr mask
"/" + mask.split(".").map { |e| e.to_i.to_s(2).rjust(8, "0") }.join.count("1").to_s
end
mask_2_ciddr "255.255.255.0"
=> "/24"
mask_2_ciddr "255.255.255.128"
=> "/25"
Если вам не нужно использовать гем ip-адреса, вы можете сделать это с помощью netaddr драгоценный камень
require 'netaddr'
def to_cidr_mask(dotted_mask)
NetAddr::CIDR.create('0.0.0.0/'+dotted_mask).netmask
end
to_cidr_mask("255.224.0.0") # => "/11"
require 'ipaddr'
def serialize_ipaddr(address)
mask = address.instance_variable_get(:@mask_addr).to_s(2).count('1')
"#{address}/#{mask}"
end
serialize_ipaddr(IPAddr.new('192.168.0.1/24')) # => "192.168.0.0/24"
Код выполняет маскирование путем доступа к закрытой переменной экземпляра * @ mask_addr) экземпляра IPAddr (адрес, передаваемый в serialize_ipaddr). Это не рекомендуется (так как переменные экземпляра не являются частью открытого API классов, но здесь, на мой взгляд, это лучше, чем анализ строки из #inspect .
Итак, процесс выглядит следующим образом:
<Ол> 255.255.255.0 - > 4294967040 - > 11111111111111111111111100000000 код>
РЕДАКТИРОВАТЬ: Добавлено пояснение к реализации, запрошенное NathanOliver
Вот способ сделать это без драгоценного камня IPAddr
(('1'*cidr)+('0'*(32-cidr))).scan(/.{8}/m).map{|e|e.to_i(2)}.join('.')