ruby-ip library
This is a library for manipulating IP addresses.
<img src=“travis-ci.org/G7M3/ruby-ip2.svg?branch=master” alt=“Build Status” /> <img src=“coveralls.io/repos/github/G7M3/ruby-ip2/badge.svg?branch=master” alt=“Coverage Status” />
Installation
- Gem
-
gem install ruby-ip - Source
-
git clone git://github.com/G7M3/ruby-ip2.git
Feature overview
-
Create from string and to string
require 'ip' ip = IP.new("192.0.2.53/24") ip.to_s # "192.0.2.53/24" ip.to_i # 3221226037 ip.to_b # 11000000000000000000001000110101 ip.to_hex # "c0000235" ip.to_addr # "192.0.2.53" ip.to_arpa # "53.2.0.192.in-addr.arpa." ip.pfxlen # 24 -
Convert from IPAddr to IP and back to IPAddr
# new IPAddr ipaddr = IPAddr.new('192.168.2.1') # => #<IPAddr: IPv4:192.168.2.1/255.255.255.255> # convert it to IP ip_from_ipaddr = IP.new_ntoh(ipaddr.hton) # => <IP::V4 192.168.2.1> # and back to IPAddr ipaddr_from_ip = IPAddr.new_ntoh(ip_from_ipaddr.hton) # => #<IPAddr: IPv4:192.168.2.1/255.255.255.255> -
Qualify IP address with “routing context” (VRF)
ip = IP.new("192.0.2.53/24@cust1") ip.to_s # "192.0.2.53/24@cust1" ip.to_addrlen # "192.0.2.53/24" ip.ctx # "cust1" -
Clean implementation of IP::V4 and IP::V6 as subclasses of IP
ip1 = IP.new("192.0.2.53/24") #<IP::V4 192.0.2.53/24> ip2 = IP.new("2001:db8:be00::/48") #<IP::V6 2001:db8:be00::/48> ip1.is_a?(IP::V4) # true -
Create directly from integers or hex
ip = IP::V4.new(3221226037, 24, "cust1") ip = IP::V4.new("c0000235", 24, "cust1") -
Netmask manipulation
ip = IP.new("192.0.2.53/24") ip.network #<IP::V4 192.0.2.0/24> ip.network(1) #<IP::V4 192.0.2.1/24> ip.broadcast #<IP::V4 192.0.2.255/24> ip.broadcast(-1) #<IP::V4 192.0.2.254/24> ip.mask # 255 ip.size # 256 ip.netmask.to_s # "255.255.255.0" ip.wildmask.to_s # "0.0.0.255" -
Address masking
ip = IP.new("192.0.2.53/24") ip.offset? # true ip.offset # 53 ip.mask! ip.to_s # "192.0.2.0/24" ip.offset? # false -
Simple IP arithmetic
ip = IP.new("192.0.2.53/24") ip + 4 #<IP::V4 192.0.2.57/24> ip | 7 #<IP::V4 192.0.2.55/24> ip ^ 7 #<IP::V4 192.0.2.50/24> ~ip #<IP::V4 63.255.253.202/24> -
Advanced Subnet Operations
sn = IP.new('192.168.0.0/24') ip = IP.new('192.168.0.48/32') sn.split [#<IP::V4 192.168.0.0/25>, #<IP::V4 192.168.0.128/25>] (2 evenly divided subnets) sn.divide_by_subnets(3) [#<IP::V4 192.168.0.0/26>, #<IP::V4 192.168.0.64/26>, #<IP::V4 192.168.0.128/26>, #<IP::V4 192.168.0.192/26>] (4 evenly divided subnets) #keep in mind this always takes into account a network and broadcast address sn.divide_by_hosts(100) [#<IP::V4 192.168.0.0/25>, #<IP::V4 192.168.0.128/25>] (128 hosts each) ip = IP.new('192.168.0.48/32') ip.is_in?(sn) => true -
Deaggregate subnets from range
IP.new('1.2.0.0').deaggregate(IP.new('1.3.255.255')) => [#<IP::V4 1.2.0.0/15>] IP.new('1.2.0.0').deaggregate(IP.new('1.4.255.255')) => [#<IP::V4 1.2.0.0/15>, #<IP::V4 1.4.0.0/16>] IP.new('2001:db8:85a3:8d3::').deaggregate(IP.new('2001:0db8:85a3:08d3:ffff:ffff:ffff:ffff')) => [#<IP::V6 2001:db8:85a3:8d3::/64>] IP.new('2001:db8:85a3:8d3::').deaggregate(IP.new('2001:db8:85a3:8d3:1::')) => [#<IP::V6 2001:db8:85a3:8d3::/80>, #<IP::V6 2001:db8:85a3:8d3:1::>] -
Convert to and from a compact Array representation
ip1 = IP.new("192.0.2.53/24@cust1") ip1.to_a # ["v4", 3221226037, 24, "cust1"] ip2 = IP.new(["v4", 3221226037, 24, "cust1"]) ip1 == ip2 # true -
Hex array representation, useful when talking to languages which don’t have Bignum support
ip1 = IP.new("2001:db8:be00::/48@cust1") ip1.to_ah # ["v6", "20010db8be0000000000000000000000", 48, "cust1"] ip2 = IP.new(["v6", "20010db8be0000000000000000000000", 48, "cust1"]) ip1 == ip2 # true -
Addresses are Comparable, sortable, and can be used as Hash keys
-
Addresses can be stored in a VARBINARY(16) database column as string of bytes
# == Schema Information # # Table name: ipaddresses # # id :integer not null, primary key # address :binary(16) # pfxlen :integer # created_at :datetime not null # updated_at :datetime not null # # Indexes # # index_ipaddresses_on_address (address) # index_ipaddresses_on_pfxlen (pfxlen) # require 'ip' class IpAddress < ActiveRecord::Base validates_uniqueness_of :address, scope: [:pfxlen] validates_presence_of :address, :pfxlen # IpAddress.last.address returns an IP object def address IP.new("#{IP.new_ntoh(super).to_s}/#{pfxlen}") end # Address can be set with string: # ipaddress = IpAddress.new({address: '2001:db8:be00::/128'}) # Or IP object: # ip = IP.new('2001:db8:be00::/128') # ipaddress = IpAddress.new({address: ip}) def address=(arg) ip = IP.new(arg) self.pfxlen = ip.pfxlen super(ip.hton) end end
Why not IPAddr?
Ruby bundles an IPAddr class (ipaddr.rb). However there are a number of serious problems with this library.
-
Given an IP address with a netmask or prefix (e.g. 192.0.2.0/24) it’s very hard to get access to the netmask part. It involves digging around instance variables.
-
It’s impossible to deal with an off-base address with prefix, because IPAddr forcibly masks it to the base. e.g. 192.0.2.53/24 is stored as 192.0.2.0/24
-
IPAddr uses calls to the socket library to validate IP addresses, and this can trigger spurious DNS lookups when given an invalid IP address. ruby-ip does not depend on the socket library at all, unless you require ‘ip/socket’ to have access to the Socket::AF_INET and Socket::AF_INET6 constants.
Copying
You may use, distribute and modify this software under the same terms as ruby itself (see LICENSE.txt and COPYING.txt)