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>
32 bit mask for IPv4
128 bit mask for IPv6
Format string for IPv6
Regexp
internally used for parsing IPv4 address.
Regexp
internally used for parsing IPv6 address.
Regexp
internally used for parsing IPv6 address.
Returns the address family of this IP address.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 557
def initialize(addr = '::', family = Socket::AF_UNSPEC)
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('/')
if prefix =~ /\A\[(.*)\]\z/i
prefix = $1
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
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
rescue InvalidAddressError => e
raise e.class, "#{e.message}: #{addr}"
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 106
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 112
def self.ntop(addr)
case addr.size
when 4
s = addr.unpack('C4').join('.')
when 16
s = IN6FORMAT % addr.unpack('n8')
else
raise AddressFamilyError, "unsupported address family"
end
return s
end
Convert a network byte ordered string form of an IP address into human readable form.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 125
def &(other)
return self.clone.set(@addr & coerce_other(other).to_i)
end
Returns a new ipaddr built by bitwise AND.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 140
def <<(num)
return self.clone.set(addr_mask(@addr << num))
end
Returns a new ipaddr built by bitwise left shift.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 391
def <=>(other)
other = coerce_other(other)
rescue
nil
else
@addr <=> other.to_i if other.family == @family
end
Compares the ipaddr with another.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 150
def ==(other)
other = coerce_other(other)
rescue
false
else
@family == other.family && @addr == other.to_i
end
Returns true if two ipaddrs are equal.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 135
def >>(num)
return self.clone.set(@addr >> num)
end
Returns a new ipaddr built by bitwise right-shift.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 317
def _ipv4_compat?
if !ipv6? || (@addr >> 32) != 0
return false
end
a = (@addr & IN4MASK)
return a != 0 && a != 1
end
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 675
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
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 688
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
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 664
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
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 601
def coerce_other(other)
case other
when IPAddr
other
when String
self.class.new(other)
else
self.class.new(other, @family)
end
end
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 401
def eql?(other)
return self.class == other.class && self.hash == other.hash && self == other
end
Checks equality used by Hash
.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 406
def hash
return ([@addr, @mask_addr].hash << 1) | (ipv4? ? 0 : 1)
end
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 239
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 627
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 = in_addr($~[4,4])
left = $1
right = $3 + '0:0'
else
left.count(':') <= ($1.empty? || $2.empty? ? 8 : 7) or
raise InvalidAddressError, "invalid address"
left = $1
right = $2
addr = 0
end
else
raise InvalidAddressError, "invalid address"
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
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 612
def in_addr(addr)
case addr
when Array
octets = addr
else
m = RE_IPV4ADDRLIKE.match(addr) or return nil
octets = m.captures
end
octets.inject(0) { |i, s|
(n = s.to_i) < 256 or raise InvalidAddressError, "invalid address"
s.match(/\A0./) and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous"
i << 8 | n
}
end
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 173
def include?(other)
other = coerce_other(other)
if ipv4_mapped?
if (@mask_addr >> 32) != 0xffffffffffffffffffffffff
return false
end
mask_addr = (@mask_addr & IN4MASK)
addr = (@addr & IN4MASK)
family = Socket::AF_INET
else
mask_addr = @mask_addr
addr = @addr
family = @family
end
if other.ipv4_mapped?
other_addr = (other.to_i & IN4MASK)
other_family = Socket::AF_INET
else
other_addr = other.to_i
other_family = other.family
end
if family != other_family
return false
end
return ((addr & mask_addr) == (other_addr & mask_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") p net1.include?(net2) #=> true p net1.include?(net3) #=> false
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 457
def inspect
case @family
when Socket::AF_INET
af = "IPv4"
when Socket::AF_INET6
af = "IPv6"
else
raise AddressFamilyError, "unsupported address family"
end
return sprintf("#<%s: %s:%s/%s>", self.class.name,
af, _to_string(@addr), _to_string(@mask_addr))
end
Returns a string containing a human-readable representation of the ipaddr. (“#<IPAddr: family:address/mask>”)
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 370
def ip6_arpa
if !ipv6?
raise InvalidAddressError, "not an IPv6 address"
end
return _reverse + ".ip6.arpa"
end
Returns a string for DNS reverse lookup compatible with RFC3172.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 378
def ip6_int
if !ipv6?
raise InvalidAddressError, "not an IPv6 address"
end
return _reverse + ".ip6.int"
end
Returns a string for DNS reverse lookup compatible with RFC1886.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 253
def ipv4?
return @family == Socket::AF_INET
end
Returns true if the ipaddr is an IPv4 address.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 338
def ipv4_compat
warn "IPAddr\##{__callee__} is obsolete", uplevel: 1 if $VERBOSE
if !ipv4?
raise InvalidAddressError, "not an IPv4 address"
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 312
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 329
def ipv4_mapped
if !ipv4?
raise InvalidAddressError, "not an IPv4 address"
end
return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
end
Returns a new ipaddr built by converting the native IPv4 address into an IPv4-mapped IPv6 address.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 307
def ipv4_mapped?
return ipv6? && (@addr >> 32) == 0xffff
end
Returns true if the ipaddr is an IPv4-mapped IPv6 address.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 258
def ipv6?
return @family == Socket::AF_INET6
end
Returns true if the ipaddr is an IPv6 address.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 295
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
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 263
def loopback?
case @family
when Socket::AF_INET
@addr & 0xff000000 == 0x7f000000
when Socket::AF_INET6
@addr == 1
else
raise AddressFamilyError, "unsupported address family"
end
end
Returns true if the ipaddr is a loopback address.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 160
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.)
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 496
def mask!(mask)
case mask
when String
if mask =~ /\A\d+\z/
prefixlen = mask.to_i
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 349
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 427
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 446
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
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 278
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
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 358
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 475
def set(addr, *family)
case family[0] ? family[0] : @family
when Socket::AF_INET
if addr < 0 || addr > IN4MASK
raise InvalidAddressError, "invalid address"
end
when Socket::AF_INET6
if addr < 0 || addr > IN6MASK
raise InvalidAddressError, "invalid address"
end
else
raise AddressFamilyError, "unsupported address family"
end
@addr = addr
if family[0]
@family = family[0]
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
.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 386
def succ
return self.clone.set(@addr + 1, @family)
end
Returns the successor to the ipaddr.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 203
def to_i
return @addr
end
Returns the integer representation of the ipaddr.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 411
def to_range
begin_addr = (@addr & @mask_addr)
case @family
when Socket::AF_INET
end_addr = (@addr | (IN4MASK ^ @mask_addr))
when Socket::AF_INET6
end_addr = (@addr | (IN6MASK ^ @mask_addr))
else
raise AddressFamilyError, "unsupported address family"
end
return clone.set(begin_addr, @family)..clone.set(end_addr, @family)
end
Creates a Range
object for the network address.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 208
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.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 234
def to_string
return _to_string(@addr)
end
Returns a string containing the IP address representation in canonical form.
# File tmp/rubies/ruby-2.7.6/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 OR.
# File tmp/rubies/ruby-2.7.6/lib/ipaddr.rb, line 145
def ~
return self.clone.set(addr_mask(~@addr))
end
Returns a new ipaddr built by bitwise negation.