2

I have a list of network segments, and would like my website to recognize what network segment users are connected to based on their IP address. This is so I can select their location automatically on my site vs them having to choose manually. Whats a good way or determining this?

So far I have the below.

To find the client IP address:

local_ip = UDPSocket.open {|s| s.connect('64.233.187.99', 1); s.addr.last }

My array of Network Segments:

network_segments = [
{"name"=>"Site 1", "starting_address"=>"192.168.168.1", "ending_address"=>"192.168.175.254"},
{"name"=>"Site 2", "starting_address"=>"192.168.184.1", "ending_address"=>"192.168.191.254"},
{"name"=>"Site 3", "starting_address"=>"192.168.176.1", "ending_address"=>"192.168.183.254"}
]

I've tried removing the periods from the IPs and looping through each network segment to see if local_ip is > starting_address and < the ending_address, but this doesn't seem accurate. It should only return one name, and it returns all of them.

Is there a better way to see if the local_ip is in between any of my network segment IPs?

ip = local_ip.delete('.').to_i

network_segments.each do |seg|
   start_addr = seg['starting_address'].delete('.').to_i
   end_addr = seg['ending_address'].delete('.').to_i

    if ip > start_addr and ip < end_addr
      puts seg['name']
    end
end
Devin
  • 1,011
  • 2
  • 14
  • 30
  • There are gems that do this. I think this is one of them: https://github.com/alexreisner/geocoder – Taryn East Mar 19 '15 at 23:36
  • thanks but seems more geared toward finding geographical location. i need to determine location based within a set of specific Network Segment IP addresses. the IPs are static, internal, and set by a Network Engineer. I don't need location based on public address. – Devin Mar 19 '15 at 23:40
  • 1
    Consider changing the title of your question, it seems to imply geographic location. – maerics Mar 19 '15 at 23:45
  • title updated, thanks – Devin Mar 19 '15 at 23:48
  • ah internally set ip addresses right. The geolocator does actually work on ip-address - it takes the given IP and determines where you are located (not just a physical address). but if you're setting them yourself, then that's a different problem. – Taryn East Mar 20 '15 at 00:35

2 Answers2

1

Use the IPAddr class (represents an IP Address) instead of a string with dots removed. IPAddr provides comparison operators which understand which addresses are greater/less than another.

require 'ipaddr'
# ...
local_ip = IPAddr.new(local_ip)
network_segments.each do |seg|
  if local_ip > IPAddr.new(seg['starting_address']) and local_ip < IPAddr.new(seg['ending_address'])
    puts seg['name']
  end
end

edit: Apart from the fact that your ranges go from 1..254 instead of 0..255, you could implement the checks using a netmask range, eg:

IPAddr.new("192.168.168.1/21").to_range.first.to_s       # => "192.168.168.0"
IPAddr.new("192.168.168.1/21").to_range.last.to_s       # => "192.168.175.255"
IPAddr.new("192.168.168.1/21").include?("192.168.170.1") # => true
IPAddr.new("192.168.168.1/21").include?("192.168.176.1") # => false
Tim Peters
  • 4,034
  • 2
  • 21
  • 27
0

Note that IP addresses aren't reliably lexicographically comparable (i.e. as strings), consider using the standard IPAddr module:

require 'ipaddr'

a1 = '10.1.1.1'
a2 = '2.1.1.1'
a1 < a2 # => true (nope!)

i1 = IPAddr.new(a1)
i2 = IPAddr.new(a2)
i1 < i2 # => false (ok)

Also, note that is it a generally accepted best practice to use && instead of and for boolean logic.

Community
  • 1
  • 1
maerics
  • 151,642
  • 46
  • 269
  • 291