Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IP Range to CIDR in Ruby/Rails?

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!

like image 728
John Avatar asked Nov 15 '12 21:11

John


People also ask

How do I find the CIDR from the range of an IP address?

The formula to calculate the number of assignable IP address to CIDR networks is similar to classful networking. Subtract the number of network bits from 32. Raise 2 to that power and subtract 2 for the network and broadcast addresses. For example, a /24 network has 232-24 - 2 addresses available for host assignment.

What is CIDR for all IP addresses?

CIDR addresses are made up of two sets of numbers: a prefix, which is the binary representation of the network address -- similar to what would be seen in a normal IP address -- and a suffix, which declares the total number of bits in the entire address. For example, CIDR notation may look like: 192.168.


2 Answers

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
like image 190
alkalinecoffee Avatar answered Oct 02 '22 05:10

alkalinecoffee


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}
like image 20
Philip Hallstrom Avatar answered Oct 02 '22 06:10

Philip Hallstrom