In network programming, we always perform some operations on IP addresses. Following are some examples.
- Calculating network prefix of an IP address from IP address and subnet mask.
- Calculating the host part of an IP address from IP address and subnet mask.
- Calculating the number of hosts in a subnet.
- Check whether an IP address belongs to a subnet or not.
- Converting subnet mask from dot-decimal notation to integer.
Ruby provides class(IPAddr) for basic operations on IP address that can be used to perform all operations mentioned above.
require 'ipaddr'
ip = IPAddr.new("192.34.56.54/24")
A simple mask method call will give us the network prefix part of IP address. It is simply a bitwise mask of IP address with subnet mask.
require 'ipaddr'
ip = IPAddr.new(ARGV[0])
network_prefix = ip.mask(ARGV[1])
puts network_prefix
Run it
ruby ip_example.rb 192.168.5.130 24
# Returns
192.168.5.0
calculating the host part is not as trivial as the network part of the IP address. We first calculate the complement of subnet mask.
Subnet(24) : 11111111.11111111.11111111.00000000
neg_subnet(24) : 00000000.00000000.00000000.11111111
we used negation(~) and mask method to calculate complement of subnet mask then simply performed a bitwise AND between the IP and complement of subnet
require 'ipaddr'
ip = IPAddr.new(ARGV[0])
neg_subnet = ~(IPAddr.new("255.255.255.255").mask(ARGV[1]))
host = ip & neg_subnet
puts host
Run it
ruby ip_example.rb 192.168.5.130 24
# Returns
0.0.0.130
We used to_range method to create a range of all the IPs then count method to count the IPs in range. We reduced the number by two to exclude the gateway and broadcast IP address.
require 'ipaddr'
ip=IPAddr.new("0.0.0.0/#{ARGV[0]}")
puts ip.to_range.count-2
Run it
ruby ip_example.rb 24
254
===
is an alias of include? which returns true if ip address belongs to the range otherwise it returns false.
require 'ipaddr'
net=IPAddr.new("#{ARG[0]}/#{ARGV[1]}")
puts net === ARGV[2]
Run it
ruby ip_example.rb 192.168.5.128 24 192.168.5.93
true
ruby ip_example.rb 192.168.5.128 24 192.168.6.93
false
We treated subnet mask as ip address and converted it into an integer by using to_i
then used to_s(2)
to convert the integer into binary form. Once we had the binary we counted the number of occurrence of digit 1 with count("1")
.
require 'ipaddr'
subnet_mask = IPAddr.new(ARGV[0])
puts subnet_mask.to_i.to_s(2).count("1").to_s
Run it
ruby ip_example.rb 255.255.255.0
24
require 'ipaddr'
IPAddr.new(3232236159, Socket::AF_INET).to_s
or
[3232236159].pack('N').unpack('C4').join('.')
require 'ipaddr'
IPAddr.new('192.168.2.127').to_i
This part has been pretty quoted from IP address Operations in Ruby topic
you may need to know more information about IP location due attack investigation or any other reason.
The special thing about geoip lib is that it's an API for offline database you download from www.maxmind.com. There are few free databases from MaxMind whoever you can have a subscription database version though.
-
Download one of the free GeoLite country, city or ASN databases
-
Install geoip gem
gem install geoip
-
Usage
#!/usr/bin/env ruby
ip = ARGV[0]
geoip = GeoIP.new('GeoLiteCity.dat')
geoinfo = geoip.country(ip).to_hash
puts "IP address:\t" + geoinfo[:ip]
puts "Country:\t" + geoinfo[:country_name]
puts "Country code:\t" + geoinfo[:country_code2]
puts "City name:\t" + geoinfo[:city_name]
puts "Latitude:\t" + geoinfo[:latitude]
puts "Longitude:\t" + geoinfo[:longitude]
puts "Time zone:\t" + geoinfo[:timezone]
-> ruby ip2location.rb 108.168.255.243
IP address: 108.168.255.243
Country: United States
Country code: US
City name: Dallas
Latitude: 32.9299
Longitude: -96.8353
Time zone: America/Chicago