IP Range to CIDR in Ruby/Rails?

自作多情 提交于 2019-12-21 02:34:45

问题


I want to do two things: Convert IP Address inputs into CIDR Here are some example inputs:

1.1.1.1    
192.168.*.* #=> 192.168.0-255.0-255
192.168.1.2-20
1.1.1-10.1-100

Check if a given IP Address falls into any CIDR. This must be a very fast query, as it's a very common lookup in my web app. I'm thinking of doing something like this:

def matches?(request)
  valid = @ips.select {|cidr| cidr.contains?(request.remote_ip) }
  !valid.empty?
end

I think converting IP ranges into CIDR will let lookups be faster than what we're doing now, which is breaking the IP's into integer octets. We then index the first two sets of octets to partially match against IP's. Another option might be converting everything to ints and doing comparisons that way. I'd convert to ints with something like this IPAddr.new("1.1.1.1").to_i but then I'd need to store an upper and lower IP for each range instead of just a single CIDR.

Please let me know if I am overlooking any mainstream approaches, popular gems or repo's. Thanks!


回答1:


Well, to get the CIDR notation of a range, you need an IP and the number of network bits (calculated from the netmask).

To enumerate the addresses of a given range, you can use the NetAddr (< 2.x) gem.

p NetAddr::CIDR.create('192.168.1.0/24').enumerate
  => ['192.168.1.0', '192.168.1.1', '192.168.1.2'... '192.168.1.255']

You can also calculate the bits from the netmask on the fly:

mask_int = NetAddr.netmask_to_i('255.255.255.0')
p NetAddr.mask_to_bits(mask_int)
  => 24

And to create a range based on two IPs:

lower = NetAddr::CIDR.create('192.168.1.1')
upper = NetAddr::CIDR.create('192.168.1.10')
p NetAddr.range(lower, upper)
  => ['192.168.1.2', '192.168.1.3'... '192.168.1.9']

So now that you can create a CIDR range, you can check to see if an IP is a part of it:

cidr = NetAddr::CIDR.create('192.168.1.0/24')
p cidr.contains?('192.168.1.10')
  => true



回答2:


I suspect everything you need is in IPAddr. I use this to see if the remote IP is coming from a private network:

['127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', '192.168.10.0/8'
].none?{|block| IPAddr.new(block) === request.remote_ip}



回答3:


Maybe I'm misunderstanding the question, but it seems that one aspect of this question has not been addressed, that is, converting a range of ip addresses to one or more CIDR entries.

I use the following approach to lookup suspicious ip activity on my firewall, and if it is in a country that I'm not interested in allowing access (you know who you are) I use whois to lookup the address range, and then calculate the merged CIDRs as follows,

whois xxx.yyy.zzz.123
# find address range for this ip
range="xxx.yyy.zzz.0-xxx.yyy.zzz.255".split(/\s*-\s*/)
lower=range[0]
upper=range[1]
ip_net_range = NetAddr.range(lower, upper, :Inclusive => true, :Objectify => true)  
cidrs = NetAddr.merge(ip_net_range, :Objectify => true)

This is a example on an internal network, but it is trivial to extend to a public ip block,

whois 192.168.1.3
range="192.168.0.0 - 192.168.255.255".split(/\s*-\s*/)
upper=range[0]
lower=range[1]
ip_net_range = NetAddr.range(lower, upper, :Inclusive => true, :Objectify => true)
cidrs = NetAddr.merge(ip_net_range, :Objectify => true)
p cidrs
[192.168.0.0/16]

Then I can pass that CIDR to my firewall software (shorewall) to have it dynamically drop that cidr(s).



来源:https://stackoverflow.com/questions/13406603/ip-range-to-cidr-in-ruby-rails

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!