Skip to content

Commit

Permalink
IPv4 to IPv6 address synthesis described in RFC6052.
Browse files Browse the repository at this point in the history
Implement IPv4 to IPv6 address synthesis (RFC6052) to utilize the NAT64
on an IPv6 only network to connect to an IPv4 address literal.

Currently, Chrome cannot access an IPv4 literal when the network is IPv6
only (such as when an IPv4 address is directly written in the Omnibox)
because DNS64 cannot be used for IP literals.
In order for the packets to use NAT64 in the network, Chrome will now
append the IPv4 to IPv6 translation prefix (Pref64::/n) to the IPv4
address.

Also add feature kUseNAT64ForIPv4Literal (default: enabled) in order to
be able to disable this feature for debugging purposes.

Bug: 915087
Change-Id: I7fc4324c6d5ac245dfa88e326cd6cedcf3296c64
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3691238
Commit-Queue: Momoka Yamamoto <momoka.my6@gmail.com>
Reviewed-by: Takashi Toyoshima <toyoshim@chromium.org>
Reviewed-by: Tsuyoshi Horo <horo@chromium.org>
Reviewed-by: Adam Rice <ricea@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1058599}
  • Loading branch information
momoka0122y authored and Chromium LUCI CQ committed Oct 13, 2022
1 parent f313c28 commit f1accfc
Show file tree
Hide file tree
Showing 16 changed files with 839 additions and 17 deletions.
5 changes: 5 additions & 0 deletions chrome/browser/about_flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9742,6 +9742,11 @@ const FeatureEntry kFeatureEntries[] = {
FEATURE_VALUE_TYPE(ash::features::kEnable16Desks)},
#endif

{"use-nat64-for-ipv4-literal",
flag_descriptions::kUseNAT64ForIPv4LiteralName,
flag_descriptions::kUseNAT64ForIPv4LiteralDescription, kOsAll,
FEATURE_VALUE_TYPE(net::features::kUseNAT64ForIPv4Literal)},

// NOTE: Adding a new flag requires adding a corresponding entry to enum
// "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
// Histograms" in tools/metrics/histograms/README.md (run the
Expand Down
5 changes: 5 additions & 0 deletions chrome/browser/flag-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -6809,6 +6809,11 @@
"owners": [ "khaslett", "//components/viz/OWNERS" ],
"expiry_milestone": 110
},
{
"name": "use-nat64-for-ipv4-literal",
"owners": [ "horo", "net-dev" ],
"expiry_milestone": 120
},
{
"name": "use-passthrough-command-decoder",
"owners": [ "//third_party/angle/OWNERS" ],
Expand Down
6 changes: 6 additions & 0 deletions chrome/browser/flag_descriptions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3076,6 +3076,12 @@ const char kUnsafeFastJSCallsDescription[] =
const char kUiPartialSwapName[] = "Partial swap";
const char kUiPartialSwapDescription[] = "Sets partial swap behavior.";

const char kUseNAT64ForIPv4LiteralName[] =
"Use NAT64 translation for IPv4 literals";
const char kUseNAT64ForIPv4LiteralDescription[] =
"Enables IPv4 to IPv6 address translation for IPv4 literals when chrome is "
"on an IPv6 only network";

const char kUsernameFirstFlowName[] = "Username first flow voting";
const char kUsernameFirstFlowDescription[] =
"Support of sending votes on username first flow i.e. login "
Expand Down
3 changes: 3 additions & 0 deletions chrome/browser/flag_descriptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -1739,6 +1739,9 @@ extern const char kUnsafeFastJSCallsDescription[];
extern const char kUiPartialSwapName[];
extern const char kUiPartialSwapDescription[];

extern const char kUseNAT64ForIPv4LiteralName[];
extern const char kUseNAT64ForIPv4LiteralDescription[];

extern const char kUseSearchClickForRightClickName[];
extern const char kUseSearchClickForRightClickDescription[];

Expand Down
4 changes: 4 additions & 0 deletions net/base/features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -361,4 +361,8 @@ BASE_FEATURE(kEnableWebsocketsOverHttp3,
"EnableWebsocketsOverHttp3",
base::FEATURE_DISABLED_BY_DEFAULT);

BASE_FEATURE(kUseNAT64ForIPv4Literal,
"UseNAT64ForIPv4Literal",
base::FEATURE_ENABLED_BY_DEFAULT);

} // namespace net::features
3 changes: 3 additions & 0 deletions net/base/features.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,9 @@ NET_EXPORT BASE_DECLARE_FEATURE(kCaseInsensitiveCookiePrefix);

NET_EXPORT BASE_DECLARE_FEATURE(kEnableWebsocketsOverHttp3);

// Whether to do IPv4 to IPv6 address translation for IPv4 literals.
NET_EXPORT BASE_DECLARE_FEATURE(kUseNAT64ForIPv4Literal);

} // namespace net::features

#endif // NET_BASE_FEATURES_H_
144 changes: 144 additions & 0 deletions net/base/ip_address.cc
Original file line number Diff line number Diff line change
Expand Up @@ -490,4 +490,148 @@ size_t MaskPrefixLength(const IPAddress& mask) {
IPAddress(all_ones->data(), all_ones->size()));
}

Dns64PrefixLength ExtractPref64FromIpv4onlyArpaAAAA(const IPAddress& address) {
DCHECK(address.IsIPv6());
IPAddress ipv4onlyarpa0(192, 0, 0, 170);
IPAddress ipv4onlyarpa1(192, 0, 0, 171);
if (std::equal(ipv4onlyarpa0.bytes().begin(), ipv4onlyarpa0.bytes().end(),
address.bytes().begin() + 12u) ||
std::equal(ipv4onlyarpa1.bytes().begin(), ipv4onlyarpa1.bytes().end(),
address.bytes().begin() + 12u)) {
return Dns64PrefixLength::k96bit;
} else if (std::equal(ipv4onlyarpa0.bytes().begin(),
ipv4onlyarpa0.bytes().end(),
address.bytes().begin() + 9u) ||
std::equal(ipv4onlyarpa1.bytes().begin(),
ipv4onlyarpa1.bytes().end(),
address.bytes().begin() + 9u)) {
return Dns64PrefixLength::k64bit;
} else if ((std::equal(ipv4onlyarpa0.bytes().begin(),
ipv4onlyarpa0.bytes().begin() + 1u,
address.bytes().begin() + 7u) &&
std::equal(ipv4onlyarpa0.bytes().begin() + 1u,
ipv4onlyarpa0.bytes().end(),
address.bytes().begin() + 9u)) ||
(std::equal(ipv4onlyarpa1.bytes().begin(),
ipv4onlyarpa1.bytes().begin() + 1u,
address.bytes().begin() + 7u) &&
std::equal(ipv4onlyarpa1.bytes().begin() + 1u,
ipv4onlyarpa1.bytes().end(),
address.bytes().begin() + 9u))) {
return Dns64PrefixLength::k56bit;
} else if ((std::equal(ipv4onlyarpa0.bytes().begin(),
ipv4onlyarpa0.bytes().begin() + 2u,
address.bytes().begin() + 6u) &&
std::equal(ipv4onlyarpa0.bytes().begin() + 2u,
ipv4onlyarpa0.bytes().end(),
address.bytes().begin() + 9u)) ||
((std::equal(ipv4onlyarpa1.bytes().begin(),
ipv4onlyarpa1.bytes().begin() + 2u,
address.bytes().begin() + 6u) &&
std::equal(ipv4onlyarpa1.bytes().begin() + 2u,
ipv4onlyarpa1.bytes().end(),
address.bytes().begin() + 9u)))) {
return Dns64PrefixLength::k48bit;
} else if ((std::equal(ipv4onlyarpa0.bytes().begin(),
ipv4onlyarpa0.bytes().begin() + 3u,
address.bytes().begin() + 5u) &&
std::equal(ipv4onlyarpa0.bytes().begin() + 3u,
ipv4onlyarpa0.bytes().end(),
address.bytes().begin() + 9u)) ||
(std::equal(ipv4onlyarpa1.bytes().begin(),
ipv4onlyarpa1.bytes().begin() + 3u,
address.bytes().begin() + 5u) &&
std::equal(ipv4onlyarpa1.bytes().begin() + 3u,
ipv4onlyarpa1.bytes().end(),
address.bytes().begin() + 9u))) {
return Dns64PrefixLength::k40bit;
} else if (std::equal(ipv4onlyarpa0.bytes().begin(),
ipv4onlyarpa0.bytes().end(),
address.bytes().begin() + 4u) ||
std::equal(ipv4onlyarpa1.bytes().begin(),
ipv4onlyarpa1.bytes().end(),
address.bytes().begin() + 4u)) {
return Dns64PrefixLength::k32bit;
} else {
// if ipv4onlyarpa address is not found return 0
return Dns64PrefixLength::kInvalid;
}
}

IPAddress ConvertIPv4ToIPv4EmbeddedIPv6(const IPAddress& ipv4_address,
const IPAddress& ipv6_address,
Dns64PrefixLength prefix_length) {
DCHECK(ipv4_address.IsIPv4());
DCHECK(ipv6_address.IsIPv6());

base::StackVector<uint8_t, 16> bytes;

uint8_t zero_bits[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

switch (prefix_length) {
case Dns64PrefixLength::k96bit:
bytes->insert(bytes->end(), ipv6_address.bytes().begin(),
ipv6_address.bytes().begin() + 12u);
bytes->insert(bytes->end(), ipv4_address.bytes().begin(),
ipv4_address.bytes().end());
return IPAddress(bytes->data(), bytes->size());
case Dns64PrefixLength::k64bit:
bytes->insert(bytes->end(), ipv6_address.bytes().begin(),
ipv6_address.bytes().begin() + 8u);
bytes->insert(bytes->end(), std::begin(zero_bits),
std::begin(zero_bits) + 1u);
bytes->insert(bytes->end(), ipv4_address.bytes().begin(),
ipv4_address.bytes().end());
bytes->insert(bytes->end(), std::begin(zero_bits),
std::begin(zero_bits) + 3u);
return IPAddress(bytes->data(), bytes->size());
case Dns64PrefixLength::k56bit:
bytes->insert(bytes->end(), ipv6_address.bytes().begin(),
ipv6_address.bytes().begin() + 7u);
bytes->insert(bytes->end(), ipv4_address.bytes().begin(),
ipv4_address.bytes().begin() + 1u);
bytes->insert(bytes->end(), std::begin(zero_bits),
std::begin(zero_bits) + 1u);
bytes->insert(bytes->end(), ipv4_address.bytes().begin() + 1u,
ipv4_address.bytes().end());
bytes->insert(bytes->end(), std::begin(zero_bits),
std::begin(zero_bits) + 4u);
return IPAddress(bytes->data(), bytes->size());
case Dns64PrefixLength::k48bit:
bytes->insert(bytes->end(), ipv6_address.bytes().begin(),
ipv6_address.bytes().begin() + 6u);
bytes->insert(bytes->end(), ipv4_address.bytes().begin(),
ipv4_address.bytes().begin() + 2u);
bytes->insert(bytes->end(), std::begin(zero_bits),
std::begin(zero_bits) + 1u);
bytes->insert(bytes->end(), ipv4_address.bytes().begin() + 2u,
ipv4_address.bytes().end());
bytes->insert(bytes->end(), std::begin(zero_bits),
std::begin(zero_bits) + 5u);
return IPAddress(bytes->data(), bytes->size());
case Dns64PrefixLength::k40bit:
bytes->insert(bytes->end(), ipv6_address.bytes().begin(),
ipv6_address.bytes().begin() + 5u);
bytes->insert(bytes->end(), ipv4_address.bytes().begin(),
ipv4_address.bytes().begin() + 3u);
bytes->insert(bytes->end(), std::begin(zero_bits),
std::begin(zero_bits) + 1u);
bytes->insert(bytes->end(), ipv4_address.bytes().begin() + 3u,
ipv4_address.bytes().end());
bytes->insert(bytes->end(), std::begin(zero_bits),
std::begin(zero_bits) + 6u);
return IPAddress(bytes->data(), bytes->size());
case Dns64PrefixLength::k32bit:
bytes->insert(bytes->end(), ipv6_address.bytes().begin(),
ipv6_address.bytes().begin() + 4u);
bytes->insert(bytes->end(), ipv4_address.bytes().begin(),
ipv4_address.bytes().end());
bytes->insert(bytes->end(), std::begin(zero_bits),
std::begin(zero_bits) + 8u);
return IPAddress(bytes->data(), bytes->size());
case Dns64PrefixLength::kInvalid:
return ipv4_address;
}
}

} // namespace net
47 changes: 47 additions & 0 deletions net/base/ip_address.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,53 @@ bool IPAddressStartsWith(const IPAddress& address, const uint8_t (&prefix)[N]) {
return std::equal(prefix, prefix + N, address.bytes().begin());
}

// According to RFC6052 Section 2.2 IPv4-Embedded IPv6 Address Format.
// https://www.rfc-editor.org/rfc/rfc6052#section-2.2
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |PL| 0-------------32--40--48--56--64--72--80--88--96--104---------|
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |32| prefix |v4(32) | u | suffix |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |40| prefix |v4(24) | u |(8)| suffix |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |48| prefix |v4(16) | u | (16) | suffix |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |56| prefix |(8)| u | v4(24) | suffix |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |64| prefix | u | v4(32) | suffix |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// |96| prefix | v4(32) |
// +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
// The NAT64/DNS64 translation prefixes has one of the following lengths.
enum class Dns64PrefixLength {
k32bit,
k40bit,
k48bit,
k56bit,
k64bit,
k96bit,
kInvalid
};

// Extracts the NAT64 translation prefix from the IPv6 address using the well
// known address ipv4only.arpa 192.0.0.170 and 192.0.0.171.
// Returns prefix length on success, or Dns64PrefixLength::kInvalid on failure
// (when the ipv4only.arpa IPv4 address is not found)
NET_EXPORT Dns64PrefixLength
ExtractPref64FromIpv4onlyArpaAAAA(const IPAddress& address);

// Converts an IPv4 address to an IPv4-embedded IPv6 address using the given
// prefix. For example 192.168.0.1 and 64:ff9b::/96 would be converted to
// 64:ff9b::192.168.0.1
// Returns converted IPv6 address when prefix_length is not
// Dns64PrefixLength::kInvalid, and returns the original IPv4 address when
// prefix_length is Dns64PrefixLength::kInvalid.
NET_EXPORT IPAddress
ConvertIPv4ToIPv4EmbeddedIPv6(const IPAddress& ipv4_address,
const IPAddress& ipv6_address,
Dns64PrefixLength prefix_length);

} // namespace net

#endif // NET_BASE_IP_ADDRESS_H_
Loading

0 comments on commit f1accfc

Please sign in to comment.