Issue #13769 has been updated by arkadiyt (Arkadiy Tetelman).


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

----------------------------------------
Bug #13769: IPAddr#ipv4_compat incorrect behavior
https://bugs.ruby-lang.org/issues/13769#change-66313

* Author: arkadiyt (Arkadiy Tetelman)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 2.5
* ruby -v: ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-darwin14]
* Backport: 2.2: UNKNOWN, 2.3: UNKNOWN, 2.4: UNKNOWN
----------------------------------------
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:
https://tools.ietf.org/html/rfc4291#section-2.5.5

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:
https://github.com/ruby/ruby/blob/trunk/lib/ipaddr.rb#L267-L274

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.

Or:

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:
https://en.wikipedia.org/wiki/IPv4#Special-use_addresses

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:
https://github.com/ruby/ruby/commit/9ec0a96ad4235f2054976eab6c04efbe62b3c703




-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>