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!
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.
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.
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}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With