class IPAddr
IPAddr
provides a set of methods to manipulate an IP address. Both IPv4 and IPv6 are supported.
Example¶ ↑
require 'ipaddr' ipaddr1 = IPAddr.new "3ffe:505:2::1" p ipaddr1 #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff> p ipaddr1.to_s #=> "3ffe:505:2::1" ipaddr2 = ipaddr1.mask(48) #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000> p ipaddr2.to_s #=> "3ffe:505:2::" ipaddr3 = IPAddr.new "192.168.2.0/24" p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
Constants
- IN4MASK
-
32 bit mask for IPv4
- IN6FORMAT
-
Format string for IPv6
- IN6MASK
-
128 bit mask for IPv6
- RE_IPV4ADDRLIKE
-
Regexp
internally used for parsing IPv4 address. - RE_IPV6ADDRLIKE_COMPRESSED
-
Regexp
internally used for parsing IPv6 address. - RE_IPV6ADDRLIKE_FULL
-
Regexp
internally used for parsing IPv6 address. - VERSION
Attributes
Returns the address family of this IP address.
Public Class Methods
Source
# File lib/ipaddr.rb, line 643 def initialize(addr = '::', family = Socket::AF_UNSPEC) @mask_addr = nil if !addr.kind_of?(String) case family when Socket::AF_INET, Socket::AF_INET6 set(addr.to_i, family) @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK return when Socket::AF_UNSPEC raise AddressFamilyError, "address family must be specified" else raise AddressFamilyError, "unsupported address family: #{family}" end end prefix, prefixlen = addr.split('/', 2) if prefix =~ /\A\[(.*)\]\z/i prefix = $1 family = Socket::AF_INET6 end if prefix =~ /\A(.*)(%\w+)\z/ prefix = $1 zone_id = $2 family = Socket::AF_INET6 end # It seems AI_NUMERICHOST doesn't do the job. #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil, # Socket::AI_NUMERICHOST) @addr = @family = nil if family == Socket::AF_UNSPEC || family == Socket::AF_INET @addr = in_addr(prefix) if @addr @family = Socket::AF_INET end end if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6) @addr = in6_addr(prefix) @family = Socket::AF_INET6 end @zone_id = zone_id if family != Socket::AF_UNSPEC && @family != family raise AddressFamilyError, "address family mismatch" end if prefixlen mask!(prefixlen) else @mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK end end
Creates a new ipaddr object either from a human readable IP address representation in string, or from a packed in_addr
value followed by an address family.
In the former case, the following are the valid formats that will be recognized: “address”, “address/prefixlen” and “address/mask”, where IPv6 address may be enclosed in square brackets (‘[’ and ‘]’). If a prefixlen or a mask is specified, it returns a masked IP address. Although the address family is determined automatically from a specified string, you can specify one explicitly by the optional second argument.
Otherwise an IP address is generated from a packed in_addr
value and an address family.
The IPAddr
class defines many methods and operators, and some of those, such as &, |, include? and ==, accept a string, or a packed in_addr
value instead of an IPAddr
object.
Source
# File lib/ipaddr.rb, line 107 def self.new_ntoh(addr) return new(ntop(addr)) end
Creates a new ipaddr containing the given network byte ordered string form of an IP address.
Source
# File lib/ipaddr.rb, line 114 def self.ntop(addr) if addr.is_a?(String) && addr.encoding != Encoding::BINARY raise InvalidAddressError, "invalid encoding (given #{addr.encoding}, expected BINARY)" end case addr.bytesize when 4 addr.unpack('C4').join('.') when 16 IN6FORMAT % addr.unpack('n8') else raise AddressFamilyError, "unsupported address family" end end
Convert a network byte ordered string form of an IP address into human readable form. It expects the string to be encoded in Encoding::ASCII_8BIT (BINARY).
Public Instance Methods
Source
# File lib/ipaddr.rb, line 130 def &(other) return self.clone.set(@addr & coerce_other(other).to_i) end
Returns a new ipaddr built by bitwise AND.
Source
# File lib/ipaddr.rb, line 145 def <<(num) return self.clone.set(addr_mask(@addr << num)) end
Returns a new ipaddr built by bitwise left shift.
Source
# File lib/ipaddr.rb, line 422 def <=>(other) other = coerce_other(other) rescue nil else @addr <=> other.to_i if other.family == @family end
Compares the ipaddr with another.
Source
# File lib/ipaddr.rb, line 155 def ==(other) other = coerce_other(other) rescue false else @family == other.family && @addr == other.to_i end
Returns true if two ipaddrs are equal.
Source
# File lib/ipaddr.rb, line 140 def >>(num) return self.clone.set(@addr >> num) end
Returns a new ipaddr built by bitwise right-shift.
Source
# File lib/ipaddr.rb, line 231 def as_json(*) if ipv4? && prefix == 32 to_s elsif ipv6? && prefix == 128 to_s else cidr end end
Returns a string containing the IP address representation with prefix.
Source
# File lib/ipaddr.rb, line 248 def cidr "#{to_s}/#{prefix}" end
Returns a string containing the IP address representation in cidr notation
Source
# File lib/ipaddr.rb, line 432 def eql?(other) return self.class == other.class && self.hash == other.hash && self == other end
Checks equality used by Hash
.
Source
Source
# File lib/ipaddr.rb, line 253 def hton case @family when Socket::AF_INET return [@addr].pack('N') when Socket::AF_INET6 return (0..7).map { |i| (@addr >> (112 - 16 * i)) & 0xffff }.pack('n8') else raise AddressFamilyError, "unsupported address family" end end
Returns a network byte ordered string form of the IP address.
Source
# File lib/ipaddr.rb, line 181 def include?(other) other = coerce_other(other) return false unless other.family == family begin_addr <= other.begin_addr && end_addr >= other.end_addr end
Returns true if the given ipaddr is in the range.
e.g.:
require 'ipaddr' net1 = IPAddr.new("192.168.2.0/24") net2 = IPAddr.new("192.168.2.100") net3 = IPAddr.new("192.168.3.0") net4 = IPAddr.new("192.168.2.0/16") p net1.include?(net2) #=> true p net1.include?(net3) #=> false p net1.include?(net4) #=> false p net4.include?(net1) #=> true
Source
# File lib/ipaddr.rb, line 477 def inspect case @family when Socket::AF_INET af = "IPv4" when Socket::AF_INET6 af = "IPv6" zone_id = @zone_id.to_s else raise AddressFamilyError, "unsupported address family" end return sprintf("#<%s: %s:%s%s/%s>", self.class.name, af, _to_string(@addr), zone_id, _to_string(@mask_addr)) end
Returns a string containing a human-readable representation of the ipaddr. (“#<IPAddr: family:address/mask>”)
Source
# File lib/ipaddr.rb, line 401 def ip6_arpa if !ipv6? raise InvalidAddressError, "not an IPv6 address: #{@addr}" end return _reverse + ".ip6.arpa" end
Returns a string for DNS reverse lookup compatible with RFC3172.
Source
# File lib/ipaddr.rb, line 409 def ip6_int if !ipv6? raise InvalidAddressError, "not an IPv6 address: #{@addr}" end return _reverse + ".ip6.int" end
Returns a string for DNS reverse lookup compatible with RFC1886.
Source
# File lib/ipaddr.rb, line 267 def ipv4? return @family == Socket::AF_INET end
Returns true if the ipaddr is an IPv4 address.
Source
# File lib/ipaddr.rb, line 369 def ipv4_compat warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE if !ipv4? raise InvalidAddressError, "not an IPv4 address: #{@addr}" end return self.clone.set(@addr, Socket::AF_INET6) end
Returns a new ipaddr built by converting the native IPv4 address into an IPv4-compatible IPv6 address.
Source
# File lib/ipaddr.rb, line 341 def ipv4_compat? warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE _ipv4_compat? end
Returns true if the ipaddr is an IPv4-compatible IPv6 address.
Source
# File lib/ipaddr.rb, line 358 def ipv4_mapped if !ipv4? raise InvalidAddressError, "not an IPv4 address: #{@addr}" end clone = self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6) clone.instance_variable_set(:@mask_addr, @mask_addr | 0xffffffffffffffffffffffff00000000) clone end
Returns a new ipaddr built by converting the native IPv4 address into an IPv4-mapped IPv6 address.
Source
# File lib/ipaddr.rb, line 336 def ipv4_mapped? return ipv6? && (@addr >> 32) == 0xffff end
Returns true if the ipaddr is an IPv4-mapped IPv6 address.
Source
# File lib/ipaddr.rb, line 272 def ipv6? return @family == Socket::AF_INET6 end
Returns true if the ipaddr is an IPv6 address.
Source
# File lib/ipaddr.rb, line 321 def link_local? case @family when Socket::AF_INET @addr & 0xffff0000 == 0xa9fe0000 # 169.254.0.0/16 when Socket::AF_INET6 @addr & 0xffc0_0000_0000_0000_0000_0000_0000_0000 == 0xfe80_0000_0000_0000_0000_0000_0000_0000 || # fe80::/10 (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && ( @addr & 0xffff0000 == 0xa9fe0000 # ::ffff:169.254.0.0/16 )) else raise AddressFamilyError, "unsupported address family" end end
Returns true if the ipaddr is a link-local address. IPv4 addresses in 169.254.0.0/16 reserved by RFC 3927 and link-local IPv6 Unicast Addresses in fe80::/10 reserved by RFC 4291 are considered link-local. Link-local IPv4 addresses in the IPv4-mapped IPv6 address range are also considered link-local.
Source
# File lib/ipaddr.rb, line 279 def loopback? case @family when Socket::AF_INET @addr & 0xff000000 == 0x7f000000 # 127.0.0.1/8 when Socket::AF_INET6 @addr == 1 || # ::1 (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && ( @addr & 0xff000000 == 0x7f000000 # ::ffff:127.0.0.1/8 )) else raise AddressFamilyError, "unsupported address family" end end
Returns true if the ipaddr is a loopback address. Loopback IPv4 addresses in the IPv4-mapped IPv6 address range are also considered as loopback addresses.
Source
# File lib/ipaddr.rb, line 165 def mask(prefixlen) return self.clone.mask!(prefixlen) end
Returns a new ipaddr built by masking IP address with the given prefixlen/netmask. (e.g. 8, 64, “255.255.255.0”, etc.)
Source
# File lib/ipaddr.rb, line 380 def native if !ipv4_mapped? && !_ipv4_compat? return self end return self.clone.set(@addr & IN4MASK, Socket::AF_INET) end
Returns a new ipaddr built by converting the IPv6 address into a native IPv4 address. If the IP address is not an IPv4-mapped or IPv4-compatible IPv6 address, returns self.
Source
# File lib/ipaddr.rb, line 492 def netmask _to_string(@mask_addr) end
Returns the netmask in string format e.g. 255.255.0.0
Source
# File lib/ipaddr.rb, line 447 def prefix case @family when Socket::AF_INET n = IN4MASK ^ @mask_addr i = 32 when Socket::AF_INET6 n = IN6MASK ^ @mask_addr i = 128 else raise AddressFamilyError, "unsupported address family" end while n.positive? n >>= 1 i -= 1 end i end
Returns the prefix length in bits for the ipaddr.
Source
# File lib/ipaddr.rb, line 466 def prefix=(prefix) case prefix when Integer mask!(prefix) else raise InvalidPrefixError, "prefix must be an integer" end end
Sets the prefix length in bits
Source
# File lib/ipaddr.rb, line 298 def private? case @family when Socket::AF_INET @addr & 0xff000000 == 0x0a000000 || # 10.0.0.0/8 @addr & 0xfff00000 == 0xac100000 || # 172.16.0.0/12 @addr & 0xffff0000 == 0xc0a80000 # 192.168.0.0/16 when Socket::AF_INET6 @addr & 0xfe00_0000_0000_0000_0000_0000_0000_0000 == 0xfc00_0000_0000_0000_0000_0000_0000_0000 || (@addr & 0xffff_0000_0000 == 0xffff_0000_0000 && ( @addr & 0xff000000 == 0x0a000000 || # ::ffff:10.0.0.0/8 @addr & 0xfff00000 == 0xac100000 || # ::ffff::172.16.0.0/12 @addr & 0xffff0000 == 0xc0a80000 # ::ffff::192.168.0.0/16 )) else raise AddressFamilyError, "unsupported address family" end end
Returns true if the ipaddr is a private address. IPv4 addresses in 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16 as defined in RFC 1918 and IPv6 Unique Local Addresses in fc00::/7 as defined in RFC 4193 are considered private. Private IPv4 addresses in the IPv4-mapped IPv6 address range are also considered private.
Source
# File lib/ipaddr.rb, line 389 def reverse case @family when Socket::AF_INET return _reverse + ".in-addr.arpa" when Socket::AF_INET6 return ip6_arpa else raise AddressFamilyError, "unsupported address family" end end
Returns a string for DNS reverse lookup. It returns a string in RFC3172 form for an IPv6 address.
Source
# File lib/ipaddr.rb, line 417 def succ return self.clone.set(@addr + 1, @family) end
Returns the successor to the ipaddr.
Source
# File lib/ipaddr.rb, line 189 def to_i return @addr end
Returns the integer representation of the ipaddr.
Source
# File lib/ipaddr.rb, line 242 def to_json(*a) %Q{"#{as_json(*a)}"} end
Returns a json string containing the IP address representation.
Source
# File lib/ipaddr.rb, line 442 def to_range self.class.new(begin_addr, @family)..self.class.new(end_addr, @family) end
Creates a Range
object for the network address.
Source
# File lib/ipaddr.rb, line 194 def to_s str = to_string return str if ipv4? str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1') loop do break if str.sub!(/\A0:0:0:0:0:0:0:0\z/, '::') break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':') break if str.sub!(/\b0:0:0:0:0:0\b/, ':') break if str.sub!(/\b0:0:0:0:0\b/, ':') break if str.sub!(/\b0:0:0:0\b/, ':') break if str.sub!(/\b0:0:0\b/, ':') break if str.sub!(/\b0:0\b/, ':') break end str.sub!(/:{3,}/, '::') if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\z/i =~ str str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256) end str end
Returns a string containing the IP address representation.
Source
# File lib/ipaddr.rb, line 220 def to_string str = _to_string(@addr) if @family == Socket::AF_INET6 str << zone_id.to_s end return str end
Returns a string containing the IP address representation in canonical form.
Source
# File lib/ipaddr.rb, line 497 def wildcard_mask case @family when Socket::AF_INET mask = IN4MASK ^ @mask_addr when Socket::AF_INET6 mask = IN6MASK ^ @mask_addr else raise AddressFamilyError, "unsupported address family" end _to_string(mask) end
Returns the wildcard mask in string format e.g. 0.0.255.255
Source
# File lib/ipaddr.rb, line 512 def zone_id if @family == Socket::AF_INET6 @zone_id else raise InvalidAddressError, "not an IPv6 address" end end
Returns the IPv6 zone identifier, if present. Raises InvalidAddressError
if not an IPv6 address.
Source
# File lib/ipaddr.rb, line 522 def zone_id=(zid) if @family == Socket::AF_INET6 case zid when nil, /\A%(\w+)\z/ @zone_id = zid else raise InvalidAddressError, "invalid zone identifier for address" end else raise InvalidAddressError, "not an IPv6 address" end end
Returns the IPv6 zone identifier, if present. Raises InvalidAddressError
if not an IPv6 address.
Source
# File lib/ipaddr.rb, line 135 def |(other) return self.clone.set(@addr | coerce_other(other).to_i) end
Returns a new ipaddr built by bitwise OR.
Source
# File lib/ipaddr.rb, line 150 def ~ return self.clone.set(addr_mask(~@addr)) end
Returns a new ipaddr built by bitwise negation.
Protected Instance Methods
Source
# File lib/ipaddr.rb, line 541 def end_addr case @family when Socket::AF_INET @addr | (IN4MASK ^ @mask_addr) when Socket::AF_INET6 @addr | (IN6MASK ^ @mask_addr) else raise AddressFamilyError, "unsupported address family" end end
Source
# File lib/ipaddr.rb, line 579 def mask!(mask) case mask when String case mask when /\A(0|[1-9]+\d*)\z/ prefixlen = mask.to_i when /\A\d+\z/ raise InvalidPrefixError, "leading zeros in prefix" else m = IPAddr.new(mask) if m.family != @family raise InvalidPrefixError, "address family is not same" end @mask_addr = m.to_i n = @mask_addr ^ m.instance_variable_get(:@mask_addr) unless ((n + 1) & n).zero? raise InvalidPrefixError, "invalid mask #{mask}" end @addr &= @mask_addr return self end else prefixlen = mask end case @family when Socket::AF_INET if prefixlen < 0 || prefixlen > 32 raise InvalidPrefixError, "invalid length" end masklen = 32 - prefixlen @mask_addr = ((IN4MASK >> masklen) << masklen) when Socket::AF_INET6 if prefixlen < 0 || prefixlen > 128 raise InvalidPrefixError, "invalid length" end masklen = 128 - prefixlen @mask_addr = ((IN6MASK >> masklen) << masklen) else raise AddressFamilyError, "unsupported address family" end @addr = ((@addr >> masklen) << masklen) return self end
Set
current netmask to given mask.
Source
# File lib/ipaddr.rb, line 555 def set(addr, *family) case family[0] ? family[0] : @family when Socket::AF_INET if addr < 0 || addr > IN4MASK raise InvalidAddressError, "invalid address: #{addr}" end when Socket::AF_INET6 if addr < 0 || addr > IN6MASK raise InvalidAddressError, "invalid address: #{addr}" end else raise AddressFamilyError, "unsupported address family" end @addr = addr if family[0] @family = family[0] if @family == Socket::AF_INET @mask_addr &= IN4MASK end end return self end
Set
+@addr+, the internal stored ip address, to given addr
. The parameter addr
is validated using the first family
member, which is Socket::AF_INET
or Socket::AF_INET6
.
Private Instance Methods
Source
# File lib/ipaddr.rb, line 346 def _ipv4_compat? if !ipv6? || (@addr >> 32) != 0 return false end a = (@addr & IN4MASK) return a != 0 && a != 1 end
Source
# File lib/ipaddr.rb, line 766 def _reverse case @family when Socket::AF_INET return (0..3).map { |i| (@addr >> (8 * i)) & 0xff }.join('.') when Socket::AF_INET6 return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.') else raise AddressFamilyError, "unsupported address family" end end
Source
# File lib/ipaddr.rb, line 779 def _to_string(addr) case @family when Socket::AF_INET return (0..3).map { |i| (addr >> (24 - 8 * i)) & 0xff }.join('.') when Socket::AF_INET6 return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:')) else raise AddressFamilyError, "unsupported address family" end end
Source
# File lib/ipaddr.rb, line 755 def addr_mask(addr) case @family when Socket::AF_INET return addr & IN4MASK when Socket::AF_INET6 return addr & IN6MASK else raise AddressFamilyError, "unsupported address family" end end
Source
# File lib/ipaddr.rb, line 692 def coerce_other(other) case other when IPAddr other when String self.class.new(other) else self.class.new(other, @family) end end
Source
# File lib/ipaddr.rb, line 718 def in6_addr(left) case left when RE_IPV6ADDRLIKE_FULL if $2 addr = in_addr($~[2,4]) left = $1 + ':' else addr = 0 end right = '' when RE_IPV6ADDRLIKE_COMPRESSED if $4 left.count(':') <= 6 or raise InvalidAddressError, "invalid address: #{@addr}" addr = in_addr($~[4,4]) left = $1 right = $3 + '0:0' else left.count(':') <= ($1.empty? || $2.empty? ? 8 : 7) or raise InvalidAddressError, "invalid address: #{@addr}" left = $1 right = $2 addr = 0 end else raise InvalidAddressError, "invalid address: #{@addr}" end l = left.split(':') r = right.split(':') rest = 8 - l.size - r.size if rest < 0 return nil end (l + Array.new(rest, '0') + r).inject(0) { |i, s| i << 16 | s.hex } | addr end
Source
# File lib/ipaddr.rb, line 703 def in_addr(addr) case addr when Array octets = addr else RE_IPV4ADDRLIKE.match?(addr) or return nil octets = addr.split('.') end octets.inject(0) { |i, s| (n = s.to_i) < 256 or raise InvalidAddressError, "invalid address: #{@addr}" (s != '0') && s.start_with?('0') and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous: #{@addr}" i << 8 | n } end