Skip to content

Conversation

@taketo1113
Copy link
Contributor

Background

IPv4-Compatible IPv6 Addresses are within the ::/96 range, so their prefix must be at least 96.
However, the current implementation of #ipv4_compat always returns a prefix of 0, which is incorrect.
https://www.rfc-editor.org/rfc/rfc4291.html#section-2.5.5.1

Details

This Pull Request fixes the #ipv4_compat method so that the returned address has the correct prefix (>= 96).

Expected behavior

ip4 = IPAddr.new('192.168.1.1').ipv4_compat
=> #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:c0a8:0101/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
ip4.to_s
=> "::192.168.1.1"
ip4.prefix
=> 128

ip4 = IPAddr.new('192.168.0.0/16').ipv4_compat
=> #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:c0a8:0000/ffff:ffff:ffff:ffff:ffff:ffff:ffff:0000>
ip4.to_s
=> "::192.168.0.0"
ip4.prefix
112

Actual behavior

ip4 = IPAddr.new('192.168.1.1').ipv4_compat
=> #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:c0a8:0101/0000:0000:0000:0000:0000:0000:ffff:ffff>
ip4.to_s
=> "::192.168.1.1"
ip4.prefix
=> 0

ip4 = IPAddr.new('192.168.0.0/16').ipv4_compat
=> #<IPAddr: IPv6:0000:0000:0000:0000:0000:0000:c0a8:0000/0000:0000:0000:0000:0000:0000:ffff:0000>
ip4.to_s
=> "::192.168.0.0"
ip4.prefix
=> 0

Additional Information

The #ipv4_compat method is obsolete because IPv4-Compatible IPv6 Addresses were deprecated in RFC 4291.
Therefore, I believe fixing the returned prefix will not introduce compatibility issues.

#ipv4_compat still returns an inconsistent mask. As it's been deprecated (in IETF and in this library), I don't think we need to maintain it. However, this violation in object invariant ("the netmask must be consecutive 1s followed by consecutive 0s") may cause other problems and I'd propose fixing it like #ipv4_mapped if we cannot remove #ipv4_compat now.

Co-authored-by: Tietew <tietew@gmail.com>
@ioquatix
Copy link
Member

I confirmed the behaviour of this change.

require 'ipaddr'

ip4 = IPAddr.new('192.168.1.1')

# Before fix:
compat = ip4.ipv4_compat
puts compat.to_s    # => "::192.168.1.1"
puts compat.prefix  # => 0   (WRONG, means /0)

require_relative "fix"

# After fix:
compat = ip4.ipv4_compat
puts compat.to_s    # => "::192.168.1.1"
puts compat.prefix  # => 128 (CORRECT, host mask)
require 'ipaddr'

ip4net = IPAddr.new('192.168.0.0/16')

# Before fix:
compat = ip4net.ipv4_compat
puts compat.to_s    # => "::192.168.0.0"
puts compat.prefix  # => 0 (WRONG, not a valid netmask)

require_relative "fix"

# After fix:
compat = ip4net.ipv4_compat
puts compat.to_s    # => "::192.168.0.0"
puts compat.prefix  # => 112 (96 + 16)
require 'ipaddr'

ip4host = IPAddr.new("192.168.1.1")

# Before fix:
compat = ip4host.ipv4_compat
puts compat.include?(IPAddr.new("::ffff:1234:5678")) # => true (!!)
# because prefix /0 means "everything matches"

require_relative "fix"

# After fix:
compat = ip4host.ipv4_compat
puts compat.include?(IPAddr.new("::ffff:1234:5678")) # => false
# only matches "::192.168.1.1"
require 'ipaddr'

ip4net = IPAddr.new("10.0.0.0/24")

# Before fix:
# compat = ip4net.ipv4_compat
# puts compat.to_range.count  # => astronomically huge (entire IPv6 space!)

require_relative "fix"

# After fix:
compat = ip4net.ipv4_compat
puts compat.to_range.count  # => 256 (correct size of /24)

@ioquatix ioquatix merged commit 7c85bb8 into ruby:master Sep 26, 2025
29 checks passed
@hsbt
Copy link
Member

hsbt commented Sep 26, 2025

👍

@taketo1113 taketo1113 deleted the fix-ipv4_compat-prefix branch September 26, 2025 23:30
@taketo1113
Copy link
Contributor Author

Thank you for reviewing and confirming the behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

#native results in incorrect mask, breaking #prefix

3 participants