[ruby/ipaddr] extra multicast/unicast predicates

This adds the following predicate methods to IPAddr objects:

* `#multicast?`: whether the ip address is multicast
* `#link_local_multicast?`: whether the ip address is a link-local
  multicast

it also alias `IPAddr#link_local?` as `IPAddr#link_local_unicast?`,
which is more accurate (whether the original should be deprecated is
probably a separate discussion).

https://github.com/ruby/ipaddr/commit/332ae1ffbf
This commit is contained in:
HoneyryderChuck authored and git committed 2026-07-15 13:12:23 +00:00
1 parent 06d4c1d1a5
commit bb472c530a
2 files changed
+84 -1

No files matched your search

+39 -1
View File
@@ -328,7 +328,7 @@ class IPAddr
end
end
# Returns true if the ipaddr is a link-local address. IPv4
# Returns true if the ipaddr is a link-local unicast 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
@@ -347,6 +347,44 @@ class IPAddr
end
end
alias link_local_unicast? link_local?
# Returns true if the ipaddr is a multicast address. IPv4
# addresses in 224.0.0.0/4 and IPv6 multicast addresses
# in ff00::/8 are considered multicast. Multicast IPv4 addresses in the
# IPv4-mapped IPv6 address range are also considered multicast.
def multicast?
case @family
when Socket::AF_INET
@addr & 0xf0000000 == 0xe0000000 # 224.0.0.0/4
when Socket::AF_INET6
@addr & 0xff00_0000_0000_0000_0000_0000_0000_0000 == 0xff00_0000_0000_0000_0000_0000_0000_0000 || # ff00::/8
(@addr >> 32 == 0xffff &&
@addr & 0xf0000000 == 0xe0000000 # ::ffff:224.0.0.0/4
)
else
raise AddressFamilyError, "unsupported address family"
end
end
# Returns true if the ipaddr is a link-local multicast address. IPv4
# addresses in 224.0.0.0/24 and link-local IPv6 Multicast Addresses in ff02::/16
# are considered link-local multicast. Link-local multicast IPv4 addresses in the
# IPv4-mapped IPv6 address range are also considered link-local multicast.
def link_local_multicast?
case @family
when Socket::AF_INET
@addr & 0xffffff00 == 0xe0000000 # 224.0.0.0/24 Local Network Control Block
when Socket::AF_INET6
@addr & 0xffff_0000_0000_0000_0000_0000_0000_0000 == 0xff02_0000_0000_0000_0000_0000_0000_0000 || # ff02::/16
(@addr >> 32 == 0xffff && (
@addr & 0xffff0000 == 0xe0000000 # ::ffff:224.0.0.0/24
))
else
raise AddressFamilyError, "unsupported address family"
end
end
# Returns true if the ipaddr is an IPv4-mapped IPv6 address.
def ipv4_mapped?
return ipv6? && (@addr >> 32) == 0xffff
+45
View File
@@ -623,6 +623,51 @@ class TC_Operator < Test::Unit::TestCase
assert_equal(false, IPAddr.new('2001:db8:1:1:0:ffff:a9fe:101').link_local?)
end
def test_multicast?
assert_equal(false, IPAddr.new('192.168.0.0').multicast?)
assert_equal(false, IPAddr.new('169.254.1.1').multicast?)
assert_equal(false, IPAddr.new('169.254.254.255').multicast?)
# notable ipv4 multicast addresses
assert_equal(true, IPAddr.new('224.0.0.0').multicast?)
assert_equal(true, IPAddr.new('224.0.0.1').multicast?)
assert_equal(true, IPAddr.new('224.0.0.6').multicast?)
assert_equal(true, IPAddr.new('224.0.1.41').multicast?)
assert_equal(true, IPAddr.new('224.0.1.129').multicast?)
assert_equal(true, IPAddr.new('224.0.23.12').multicast?)
assert_equal(true, IPAddr.new('239.255.255.250').multicast?)
assert_equal(true, IPAddr.new('239.255.255.253').multicast?)
assert_equal(false, IPAddr.new('::1').multicast?)
assert_equal(false, IPAddr.new('::').multicast?)
assert_equal(false, IPAddr.new('fb84:8bf7:e905::1').multicast?)
# notable ipv6 multicast addresses
assert_equal(true, IPAddr.new('ff02::1').multicast?)
assert_equal(true, IPAddr.new('ff02::2').multicast?)
assert_equal(true, IPAddr.new('ff02::5').multicast?)
assert_equal(true, IPAddr.new('ff02::6').multicast?)
assert_equal(true, IPAddr.new('ff02::8').multicast?)
assert_equal(true, IPAddr.new('ff02::1:2').multicast?)
assert_equal(true, IPAddr.new('ff02::1:3').multicast?)
assert_equal(true, IPAddr.new('ff05::101').multicast?)
end
def test_link_local_multicast?
assert_equal(false, IPAddr.new('192.168.0.0').link_local_multicast?)
assert_equal(false, IPAddr.new('169.254.1.1').link_local_multicast?)
assert_equal(false, IPAddr.new('169.254.254.255').link_local_multicast?)
assert_equal(false, IPAddr.new('239.0.0.0').link_local_multicast?)
assert_equal(true, IPAddr.new('224.0.0.0').link_local_multicast?)
assert_equal(false, IPAddr.new('::1').link_local_multicast?)
assert_equal(false, IPAddr.new('::').link_local_multicast?)
assert_equal(false, IPAddr.new('ff05::1').link_local_multicast?)
assert_equal(true, IPAddr.new('ff02::1').link_local_multicast?)
assert_equal(true, IPAddr.new('ff02::2').link_local_multicast?)
end
def test_hash
a1 = IPAddr.new('192.168.2.0')
a2 = IPAddr.new('192.168.2.0')