Skip to content

Instantly share code, notes, and snippets.

@danielsdeleo
Created October 3, 2009 02:51
Show Gist options
  • Select an option

  • Save danielsdeleo/200358 to your computer and use it in GitHub Desktop.

Select an option

Save danielsdeleo/200358 to your computer and use it in GitHub Desktop.
require "ipaddr"
require "ip_error"
module IpAddrsHelper
def self.convert_port_string(port)
port = port.to_s
case port
when /\A\d+\s*:\s*\d+\Z/
Range.new(port.split(':').first.to_i, port.split(':').last.to_i)
when /\A\d+[\s+\d,]+\d+\Z/
port.split(',').collect {|port| port.strip.to_i }
when /\A\d+\Z/
port.to_i
else
nil #makes validation fail if model validates_presence_of (which is a good thing)
end
end
def self.normalize_ip_address(string)
begin
ip_addr = assert_valid_ip_addr(string)
ip_addr.to_string_with_cidr
rescue IpError::InvalidAddressString
nil # makes validation fail if model validates_presence_of
end
end
# raises an ArgumentError if +string+ is not a valid IP Address,
# returns a new IPAddr object otherwise
def self.assert_valid_ip_addr(string)
begin
IPAddr.new(string)
rescue ArgumentError
raise IpError::InvalidAddressString, "IP address was not valid"
end
end
module IpAddrInstanceMethods
#returns the IP Address as a string in CIDR notation, such as: "192.168.123.0/24"
def to_string_with_cidr
return "#{self.to_s}/#{self.cidr_mask}"
end
#returns the CIDR format mask as an integer between 0 and 32 for IPv4,
#between 0 and 128 for IPv6.
def cidr_mask
if cidr_mask = @mask_addr.to_s(2).index('0')
return cidr_mask
else
eval_for_ip4_ip6(Proc.new {32}, Proc.new {128})
end
end
def host_digits
eval_for_ip4_ip6(Proc.new {32 - cidr_mask}, Proc.new {128 - cidr_mask})
end
def network_name_as_int
self.to_i - self.to_i.modulo(host_digits)
end
def network_name
eval_for_ip4_ip6(Proc.new {network_name_as_int.to_ip4_addr}, Proc.new {network_name_as_int.to_ip6_addr})
end
def broadcast_address_as_int
network_name_as_int + ((2 ** host_digits) -1)
end
def broadcast_address
eval_for_ip4_ip6(Proc.new {broadcast_address_as_int.to_ip4_addr}, Proc.new {broadcast_address_as_int.to_ip6_addr})
end
# use instead of IPAddr's include? method, which incorrectly returns true for things like:
# IPAddr.new("10.0.0.0/24").include?(IPAddr.new("10.0.0.0/13"))
def include_network?(other_ip_addr)
(self.network_name_as_int <= other_ip_addr.network_name_as_int) && (self.broadcast_address_as_int >= other_ip_addr.network_name_as_int)
end
def overlaps_network?(other_ip_addr)
(self.network_name_as_int <= other_ip_addr.network_name_as_int) && (self.broadcast_address_as_int >= other_ip_addr.network_name_as_int) ||
(self.network_name_as_int <= other_ip_addr.broadcast_address_as_int) && (self.broadcast_address_as_int >= other_ip_addr.network_name_as_int)
end
private
# takes two procs as arguments, calling the first for IPv4 addresses and the second for IPv6
def eval_for_ip4_ip6(ip4_proc, ip6_proc)
case @family
when ::Socket::AF_INET
ip4_proc.call
when ::Socket::AF_INET6
ip6_proc.call
else
raise ArgumentError, "Unsupported address family"
end
end
end
module StringInstanceMethods
def to_ip4_range
ip_string_pieces = self.split("/")
lower_bound = IPAddr.new(ip_string_pieces.first).to_i
if ip_string_pieces.length == 2
ip_range = (2 ** (32 - ip_string_pieces.last.to_i)) - 1 # we subtract 1 b/c 10.0.0.0/24 = 10.0.0.0 to 10.0.0.255
upper_bound = lower_bound + ip_range
else
upper_bound = lower_bound
end
return (lower_bound..upper_bound)
end
end
module IntegerInstanceMethods
def to_ip6_addr
octet_array = Array.new #not really octets
for i in 1..8
octet = (self.modulo(16 ** (4 * i))) / (16 ** ((i - 1) * 4))
octet_array.unshift(octet.to_s(16))
end
# this needs a regexp to turn dead:0:0:beef to dead::beef, best implemented as an extension to IPAddr
return IPAddr.new(octet_array.join(":")).to_s
end
def to_ip4_addr
octet_array = Array.new
for i in 1..4
octet_array.unshift((self.modulo(16 ** (2 * i))) / (16 ** ((i - 1) * 2)))
end
return octet_array.join(".")
end
end
end
IPAddr.send(:include, IpAddrsHelper::IpAddrInstanceMethods)
String.send(:include, IpAddrsHelper::StringInstanceMethods)
Integer.send(:include, IpAddrsHelper::IntegerInstanceMethods)
module IpError
class InvalidAddressString < StandardError
end
class InvalidRoute < StandardError
end
class InvalidGateway < StandardError
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment