Skip to content

Commit bb472c5

Browse files
HoneyryderChuckmatzbot
authored andcommitted
[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). ruby/ipaddr@332ae1ffbf
1 parent 06d4c1d commit bb472c5

2 files changed

Lines changed: 84 additions & 1 deletion

File tree

lib/ipaddr.rb

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ def private?
328328
end
329329
end
330330

331-
# Returns true if the ipaddr is a link-local address. IPv4
331+
# Returns true if the ipaddr is a link-local unicast address. IPv4
332332
# addresses in 169.254.0.0/16 reserved by RFC 3927 and link-local
333333
# IPv6 Unicast Addresses in fe80::/10 reserved by RFC 4291 are
334334
# considered link-local. Link-local IPv4 addresses in the
@@ -347,6 +347,44 @@ def link_local?
347347
end
348348
end
349349

350+
alias link_local_unicast? link_local?
351+
352+
# Returns true if the ipaddr is a multicast address. IPv4
353+
# addresses in 224.0.0.0/4 and IPv6 multicast addresses
354+
# in ff00::/8 are considered multicast. Multicast IPv4 addresses in the
355+
# IPv4-mapped IPv6 address range are also considered multicast.
356+
def multicast?
357+
case @family
358+
when Socket::AF_INET
359+
@addr & 0xf0000000 == 0xe0000000 # 224.0.0.0/4
360+
when Socket::AF_INET6
361+
@addr & 0xff00_0000_0000_0000_0000_0000_0000_0000 == 0xff00_0000_0000_0000_0000_0000_0000_0000 || # ff00::/8
362+
(@addr >> 32 == 0xffff &&
363+
@addr & 0xf0000000 == 0xe0000000 # ::ffff:224.0.0.0/4
364+
)
365+
else
366+
raise AddressFamilyError, "unsupported address family"
367+
end
368+
end
369+
370+
# Returns true if the ipaddr is a link-local multicast address. IPv4
371+
# addresses in 224.0.0.0/24 and link-local IPv6 Multicast Addresses in ff02::/16
372+
# are considered link-local multicast. Link-local multicast IPv4 addresses in the
373+
# IPv4-mapped IPv6 address range are also considered link-local multicast.
374+
def link_local_multicast?
375+
case @family
376+
when Socket::AF_INET
377+
@addr & 0xffffff00 == 0xe0000000 # 224.0.0.0/24 Local Network Control Block
378+
when Socket::AF_INET6
379+
@addr & 0xffff_0000_0000_0000_0000_0000_0000_0000 == 0xff02_0000_0000_0000_0000_0000_0000_0000 || # ff02::/16
380+
(@addr >> 32 == 0xffff && (
381+
@addr & 0xffff0000 == 0xe0000000 # ::ffff:224.0.0.0/24
382+
))
383+
else
384+
raise AddressFamilyError, "unsupported address family"
385+
end
386+
end
387+
350388
# Returns true if the ipaddr is an IPv4-mapped IPv6 address.
351389
def ipv4_mapped?
352390
return ipv6? && (@addr >> 32) == 0xffff

test/test_ipaddr.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,51 @@ def test_link_local?
623623
assert_equal(false, IPAddr.new('2001:db8:1:1:0:ffff:a9fe:101').link_local?)
624624
end
625625

626+
def test_multicast?
627+
assert_equal(false, IPAddr.new('192.168.0.0').multicast?)
628+
assert_equal(false, IPAddr.new('169.254.1.1').multicast?)
629+
assert_equal(false, IPAddr.new('169.254.254.255').multicast?)
630+
631+
# notable ipv4 multicast addresses
632+
assert_equal(true, IPAddr.new('224.0.0.0').multicast?)
633+
assert_equal(true, IPAddr.new('224.0.0.1').multicast?)
634+
assert_equal(true, IPAddr.new('224.0.0.6').multicast?)
635+
assert_equal(true, IPAddr.new('224.0.1.41').multicast?)
636+
assert_equal(true, IPAddr.new('224.0.1.129').multicast?)
637+
assert_equal(true, IPAddr.new('224.0.23.12').multicast?)
638+
assert_equal(true, IPAddr.new('239.255.255.250').multicast?)
639+
assert_equal(true, IPAddr.new('239.255.255.253').multicast?)
640+
641+
assert_equal(false, IPAddr.new('::1').multicast?)
642+
assert_equal(false, IPAddr.new('::').multicast?)
643+
assert_equal(false, IPAddr.new('fb84:8bf7:e905::1').multicast?)
644+
645+
# notable ipv6 multicast addresses
646+
assert_equal(true, IPAddr.new('ff02::1').multicast?)
647+
assert_equal(true, IPAddr.new('ff02::2').multicast?)
648+
assert_equal(true, IPAddr.new('ff02::5').multicast?)
649+
assert_equal(true, IPAddr.new('ff02::6').multicast?)
650+
assert_equal(true, IPAddr.new('ff02::8').multicast?)
651+
assert_equal(true, IPAddr.new('ff02::1:2').multicast?)
652+
assert_equal(true, IPAddr.new('ff02::1:3').multicast?)
653+
assert_equal(true, IPAddr.new('ff05::101').multicast?)
654+
end
655+
656+
def test_link_local_multicast?
657+
assert_equal(false, IPAddr.new('192.168.0.0').link_local_multicast?)
658+
assert_equal(false, IPAddr.new('169.254.1.1').link_local_multicast?)
659+
assert_equal(false, IPAddr.new('169.254.254.255').link_local_multicast?)
660+
assert_equal(false, IPAddr.new('239.0.0.0').link_local_multicast?)
661+
662+
assert_equal(true, IPAddr.new('224.0.0.0').link_local_multicast?)
663+
664+
assert_equal(false, IPAddr.new('::1').link_local_multicast?)
665+
assert_equal(false, IPAddr.new('::').link_local_multicast?)
666+
assert_equal(false, IPAddr.new('ff05::1').link_local_multicast?)
667+
assert_equal(true, IPAddr.new('ff02::1').link_local_multicast?)
668+
assert_equal(true, IPAddr.new('ff02::2').link_local_multicast?)
669+
end
670+
626671
def test_hash
627672
a1 = IPAddr.new('192.168.2.0')
628673
a2 = IPAddr.new('192.168.2.0')

0 commit comments

Comments
 (0)