Ruby中的子网掩码转CIDR

12

我一直在使用ip-address gem,但它似乎没有将形式为

/24

的网络掩码转换的能力。

255.255.255.0 

转换为CIDR形式

/24

有人知道如何快速地将前者转换为后者吗?


一个解决方案可能是使用ipadmin gem,它看起来更完整。不过我仍然很想从代码角度看到这个问题的解决方案。 - Dean Smith
7个回答

14

这里有一个快速而簡單的方法

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"

我不确定有什么比使用count("1")更合适的了。也许可以像这样做?32 - (2**32 - 1 - IPAddr.new("255.255.255.0").to_i).to_s(2).length - Chuck Vose
在C语言中,我可能会使用右移操作,并通过与1进行按位与运算来测试,直到达到1为止。例如:bits=32;unsigned int ipaddr=0xFFFFFF00;while(ipaddr&1==0){ipaddr=ipaddr>>1;bits--;} 对于上述情况,我认为只需要向右移动8次即可。 - YOU

12

提供一个FYI(供参考信息),并为那些正在搜索的人保持易于访问的信息...

这里有一种简单的方法将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"

5

快速而简单的转换:

"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"

5

以下是更加数学化的方法,完全避免使用字符串:

def cidr_mask
    Integer(32-Math.log2((IPAddr.new(mask,Socket::AF_INET).to_i^0xffffffff)+1))
end

如果"mask"是一个字符串,例如255.255.255.0。您可以修改并更改第一个参数为"mask",但前提是"mask"已经是IP地址的整数表示形式。

例如,如果mask是"255.255.255.0",则IPAddr.new(mask,Socket::AF_INET).to_i将变成0xffffff00,然后与0xffffffff异或,结果为255。

我们加1以使其成为256个主机的完整范围,然后找到以2为底的对数256,其等于8(用于主机地址的位),然后从32减去这8,其等于24(用于网络地址的位)。

我们然后进行整数转换,因为Math.log2返回浮点数。


这个不支持IPv6,而被接受的答案支持IPv6。 - Alnitak

2
如果您不需要使用ip-address gem,可以使用netaddr gem来完成这个任务。
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" 

1
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"

该代码通过访问IPAddr实例(传递到serialize_ipaddr的地址)的私有实例变量*@mask_addr来实现掩码。这不是推荐的方法(因为实例变量不是类的公共API的一部分,但在我看来比从#inspect中解析字符串要好)。因此,该过程如下所示:
1.获取表示子网掩码的实例变量@mask_addr 2.获取其二进制表示,例如255.255.255.0 -> 4294967040 -> 11111111111111111111111100000000 3.计算基于二进制数字中的1-s以获取CIDR掩码(24)
4.组成一个由地址和掩码组成的字符串
编辑:根据NathanOliver的要求添加了实现说明

虽然这段代码片段可能解决了问题,但包括解释真的有助于提高您的帖子质量。请记住,您正在为未来的读者回答问题,而这些人可能不知道您的代码建议的原因。 - NathanOliver

0
这是一种无需使用IPAddr gem的方法。
(('1'*cidr)+('0'*(32-cidr))).scan(/.{8}/m).map{|e|e.to_i(2)}.join('.')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接