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!
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
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}
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