diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 0dabb0d81302..f3075d3bbf4f 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -8695,6 +8695,15 @@ value: true mirror: always +# Manually enter the NAT64 prefix that will be used if IPv4 is unavailable. +# The value is formatted as IPv6 with the least significant bits to be dropped. +# For example, 64:ff9b:: is a common prefix. This will not disable +# the NAT64 check, although the value of this pref will be prioritized. +- name: network.connectivity-service.nat64-prefix + type: String + value: "" + mirror: never + # Whether to enable echconfig. - name: network.dns.echconfig.enabled type: RelaxedAtomicBool diff --git a/netwerk/base/NetworkConnectivityService.cpp b/netwerk/base/NetworkConnectivityService.cpp index 497f547e6eb0..96f7465aa1a5 100644 --- a/netwerk/base/NetworkConnectivityService.cpp +++ b/netwerk/base/NetworkConnectivityService.cpp @@ -125,6 +125,30 @@ already_AddRefed NetworkConnectivityService::MapNAT64IPs( return builder.Finish(); } +// Returns true if a prefix was read and saved to the argument +static inline bool NAT64PrefixFromPref(NetAddr* prefix) { + nsAutoCString nat64PrefixPref; + PRNetAddr prAddr{}; + + nsresult rv = Preferences::GetCString( + "network.connectivity-service.nat64-prefix", nat64PrefixPref); + if (NS_FAILED(rv) || nat64PrefixPref.IsEmpty() || + PR_StringToNetAddr(nat64PrefixPref.get(), &prAddr) != PR_SUCCESS || + prAddr.raw.family != PR_AF_INET6) { + return false; + } + + PRNetAddrToNetAddr(&prAddr, prefix); + return true; +} + +static inline bool NAT64PrefixCompare(const NetAddr& prefix1, + const NetAddr& prefix2) { + // Compare the first 96 bits as 64 + 32 + return prefix1.inet6.ip.u64[0] == prefix2.inet6.ip.u64[0] && + prefix1.inet6.ip.u32[2] == prefix2.inet6.ip.u32[2]; +} + void NetworkConnectivityService::PerformChecks() { mDNSv4 = UNKNOWN; mDNSv6 = UNKNOWN; @@ -137,6 +161,17 @@ void NetworkConnectivityService::PerformChecks() { { MutexAutoLock lock(mLock); mNAT64Prefixes.Clear(); + + // NAT64 checks might be disabled. + // Since We can't guarantee a DNS response, we should set up + // NAT64 manually now if needed. + + NetAddr priorityPrefix{}; + bool havePrefix = NAT64PrefixFromPref(&priorityPrefix); + if (havePrefix) { + mNAT64Prefixes.AppendElement(priorityPrefix); + mNAT64 = OK; + } } RecheckDNS(); @@ -153,8 +188,17 @@ void NetworkConnectivityService::SaveNAT64Prefixes(nsIDNSRecord* aRecord) { MutexAutoLock lock(mLock); mNAT64Prefixes.Clear(); + NetAddr priorityPrefix{}; + bool havePrefix = NAT64PrefixFromPref(&priorityPrefix); + if (havePrefix) { + mNAT64 = OK; + mNAT64Prefixes.AppendElement(priorityPrefix); + } + if (!rec) { - mNAT64 = NOT_AVAILABLE; + if (!havePrefix) { + mNAT64 = NOT_AVAILABLE; + } return; } @@ -199,7 +243,7 @@ void NetworkConnectivityService::SaveNAT64Prefixes(nsIDNSRecord* aRecord) { NetAddr prev = mNAT64Prefixes[0]; for (size_t i = 1; i < length; i++) { - if (mNAT64Prefixes[i] == prev) { + if (NAT64PrefixCompare(prev, mNAT64Prefixes[i])) { mNAT64Prefixes.RemoveElementAt(i); i--; length--; @@ -208,6 +252,18 @@ void NetworkConnectivityService::SaveNAT64Prefixes(nsIDNSRecord* aRecord) { } } + // The prioritized address might also appear in the record we received. + + if (havePrefix) { + for (size_t i = 1; i < length; i++) { + if (NAT64PrefixCompare(priorityPrefix, mNAT64Prefixes[i])) { + mNAT64Prefixes.RemoveElementAt(i); + // It wouldn't appear more than once. + break; + } + } + } + mNAT64 = OK; } @@ -261,6 +317,7 @@ NetworkConnectivityService::RecheckDNS() { nsIDNSService::RESOLVE_DISABLE_IPV4 | nsIDNSService::RESOLVE_DISABLE_TRR, nullptr, this, NS_GetCurrentThread(), attrs, getter_AddRefs(mDNSv6Request)); + NS_ENSURE_SUCCESS(rv, rv); if (StaticPrefs::network_connectivity_service_nat64_check()) { rv = dns->AsyncResolveNative("ipv4only.arpa"_ns, @@ -269,6 +326,7 @@ NetworkConnectivityService::RecheckDNS() { nsIDNSService::RESOLVE_DISABLE_TRR, nullptr, this, NS_GetCurrentThread(), attrs, getter_AddRefs(mNAT64Request)); + NS_ENSURE_SUCCESS(rv, rv); } return rv; } diff --git a/netwerk/test/unit/test_trr_nat64.js b/netwerk/test/unit/test_trr_nat64.js index 7204594b18e5..1f1373d30139 100644 --- a/netwerk/test/unit/test_trr_nat64.js +++ b/netwerk/test/unit/test_trr_nat64.js @@ -63,12 +63,17 @@ add_task(async function test_add_nat64_prefix_to_trr() { let [req, resp] = await channelOpenPromise(chan); equal(resp, "

404 Path not found: /test?bla=some

"); dns.clearCache(true); - override.addIPOverride("ipv4only.arpa", "fe80::6a99:9b2b:c000:00aa"); + override.addIPOverride("ipv4only.arpa", "fe80::9b2b:c000:00aa"); + Services.prefs.setCharPref( + "network.connectivity-service.nat64-prefix", + "ae80::3b1b:c343:1133" + ); - Services.obs.notifyObservers(null, "network:captive-portal-connectivity"); - await promiseObserverNotification( + let notification = promiseObserverNotification( "network:connectivity-service:dns-checks-complete" ); + Services.obs.notifyObservers(null, "network:captive-portal-connectivity"); + await notification; Services.prefs.setIntPref("network.trr.mode", 2); Services.prefs.setCharPref( @@ -90,10 +95,21 @@ add_task(async function test_add_nat64_prefix_to_trr() { }); inRecord.QueryInterface(Ci.nsIDNSAddrRecord); - inRecord.getNextAddrAsString(); Assert.equal( inRecord.getNextAddrAsString(), - "fe80::6a99:9b2b:102:304", + "1.2.3.4", + `Checking that native IPv4 addresses have higher priority.` + ); + + Assert.equal( + inRecord.getNextAddrAsString(), + "ae80::3b1b:102:304", + `Checking the manually entered NAT64-prefixed address is in the middle.` + ); + + Assert.equal( + inRecord.getNextAddrAsString(), + "fe80::9b2b:102:304", `Checking that the NAT64-prefixed address is appended at the back.` );