IPAddr#ipv4_compat incorrect behavior
To ease transition from IPv4 to IPv6, there exist "ipv4-compatible" and "ipv4-mapped" addresses, which are ipv6 addresses that embed an ipv4 address inside them.
Ruby's IPAddr defines several helper functions related to this:
IPAddr#ipv4_mapped? -> return true if the ipaddr is an ipv4-mapped ipv6 address
IPAddr#ipv4_compat? -> return true if the ipaddr is an ipv4-compatible ipv6 address
These 2 formats are defined in RFC4291 section 2.5.5, here:
Notably for ipv4-compatible addresses, it says the following:
The "IPv4-Compatible IPv6 address" was defined to assist in the IPv6 transition. The format of the "IPv4-Compatible IPv6 address" is as follows: | 80 bits | 16 | 32 bits | +--------------------------------------+--------------------------+ |0000..............................0000|0000| IPv4 address | +--------------------------------------+----+---------------------+ Note: The IPv4 address used in the "IPv4-Compatible IPv6 address" must be a globally-unique IPv4 unicast address.
But this is not the behavior of IPAddr#ipv4_compat?, defined here:
Given an ipv6 address it checks that the top 96 bits are 0, but then also that the last 32 bits are not equal to 0 or 1, so we have:
2.2.1 :002 > IPAddr.new('::0.0.0.0').ipv4_compat? => false 2.2.1 :003 > IPAddr.new('::0.0.0.1').ipv4_compat? => false
It seems like those should return true.
Perhaps this is related to the last sentence of the RFC: "The IPv4 address used in the "IPv4-Compatible IPv6 address must be a globally-unique IPv4 unicast address.". Since 0.0.0.0 and 0.0.0.1 are not globally-unique unicast addresses, that might justify the false return value. Under this reasoning the function is still wrong in that it only returns false for those 2 IPv4 addresses, when there are many other non-globally-unique unicast addresses - see here for some:
It's not clear why 0.0.0.0 and 0.0.0.1 are given this special treatment. The commit was made in 2002 and predates Ruby 1.9.3:
Fix the issue reference to [Bug #13769], handled in r60270
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60272 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Updated by Glass_saga (Masaki Matsushita) about 2 years ago
- Target version set to 2.5
RFC4291 says: '"IPv4-Compatible IPv6 address" is now deprecated.'
We can remove #ipv4_compat? or mark it as deprecated.
The "IPv4-Compatible IPv6 address" is now deprecated because the
current IPv6 transition mechanisms no longer use these addresses.
New or updated implementations are not required to support this
Updated by arkadiyt (Arkadiy Tetelman) about 2 years ago
I'd vote for marking it deprecated & fixing the bug in the original ticket. Despite the fact that the addresses are now deprecated, it may be useful for ruby code to interact with or check for legacy situations - that's how I found the bug
Updated by knu (Akinori MUSHA) about 2 years ago
::1 are the only exceptions listed because they are the only IPv6 addresses with the 80+16 bit zero prefix that already have special, conflicting meanings as IPv6 address, so I consider it was practically reasonable enough not to bother with all the other possible non-public IPv4 addresses.
Updated by knu (Akinori MUSHA) almost 2 years ago
While I do understand the need for dealing with legacy situations, changing the current behavior can cause a subtle bug with rarely maintained code without being noticed immediately.
I'd rather just remove it so that breakage is noticeable because one would soon get a NoMethodError.