Pregunta
He estado usando la gema de la dirección IP y no parece tener la capacidad de convertir desde una máscara de red del formulario
255.255.255.0
en el formulario CIDR
/24
¿Alguien tiene una idea de cómo convertir rápidamente la primera a la segunda?
Solución
Aquí está la manera rápida y sucia
require 'ipaddr'
puts IPAddr.new("255.255.255.0").to_i.to_s(2).count("1")
Debería haber una función adecuada para eso, no pude encontrar eso, así que solo cuento " 1 "
Si va a utilizar la función en varios lugares y no le importa el parche de mono, esto podría ayudar:
IPAddr.class_eval
def to_cidr
"/" + self.to_i.to_s(2).count("1")
end
end
Entonces obtienes
IPAddr.new('255.255.255.0').to_cidr
# => "/24"
Otros consejos
Al igual que para su información, y para mantener la información fácilmente accesible para aquellos que están buscando ...
Aquí hay una manera simple de convertir de CIDR a formato de máscara de red:
def cidr_to_netmask(cidr)
IPAddr.new('255.255.255.255').mask(cidr).to_s
end
Por ejemplo:
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"
Aquí hay un enfoque más matemático, evitando cadenas a toda costa:
def cidr_mask
Integer(32-Math.log2((IPAddr.new(mask,Socket::AF_INET).to_i^0xffffffff)+1))
end
con " máscara " siendo una cadena como 255.255.255.0. Puede modificarlo y cambiar el primer argumento a solo "máscara". si " máscara " ya es una representación entera de una dirección IP.
Entonces, por ejemplo, si la máscara fuera " 255.255.255.0 " ;, IPAddr.new (mask, Socket :: AF_INET) .to_i se convertiría en 0xffffff00, que luego se modifica con 0xffffffff, que es igual a 255.
Agregamos 1 a eso para convertirlo en un rango completo de 256 hosts, luego buscamos la base de registro 2 de 256, que es igual a 8 (los bits utilizados para la dirección del host), luego restamos 8 de 32, lo que equivale a 24 (los bits utilizados para la dirección de red).
Luego lo convertimos a entero porque Math.log2 devuelve un flotante.
Conversión rápida y sucia:
" 255.255.255.0 " .split (". "). map {| e | e.to_i.to_s (2) .rjust (8, " 0 ")} .join.count (" 1 "). split (". ")
= > Partí la máscara en una matriz
= > Para cada elemento en la matriz:
.to_i
= > Convierte en entero
.to_s(2)
= > Convertir entero en binario
.rjust (8, " 0 ")
= > Añadir relleno
= > El mapa devuelve una matriz con la misma cardinalidad
.join
= > Convertir matriz en una cadena completa
.count("1")
= > Contar " 1 " caracteres = > Dar máscara 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"
Si no necesita usar la gema de la dirección IP, puede hacerlo con el netaddr gema
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"
El código logra el enmascaramiento accediendo a la variable de instancia privada * @ mask_addr) de la instancia de IPAddr (dirección, pasada a serialize_ipaddr). Esta no es la forma recomendada (ya que las variables de instancia no son parte de la API pública de clases, pero aquí es mejor que analizar la cadena de #inspect en mi opinión.
Entonces el proceso es el siguiente:
- Obtenga la variable de instancia @mask_addr que representa netmask
- Obtenga su representación binaria, p.
255.255.255.0 - > 4294967040 - > 11111111111111111111111100000000
- Cuente los 1-s en el número base-2 para obtener la máscara CIDR (24)
- Crea una cadena que consiste en la dirección & amp; máscara
EDITAR: Se agregó una explicación a la implementación según lo solicitado por NathanOliver
Aquí hay una manera de hacerlo sin la gema IPAddr
(('1'*cidr)+('0'*(32-cidr))).scan(/.{8}/m).map{|e|e.to_i(2)}.join('.')