blob: 30796f65a8690e54cba84a8366c9461088910be3 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/dns/loopback_only.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "build/build_config.h"
#include "net/base/network_change_notifier.h"
#include "net/base/network_interfaces.h"
#include "net/base/sys_addrinfo.h"
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
#include <net/if.h>
#if BUILDFLAG(IS_ANDROID)
#include "net/android/network_library.h"
#else // BUILDFLAG(IS_ANDROID)
#include <ifaddrs.h>
#endif // BUILDFLAG(IS_ANDROID)
#endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
#if BUILDFLAG(IS_LINUX)
#include <linux/rtnetlink.h>
#include "net/base/address_map_linux.h"
#include "net/base/address_tracker_linux.h"
#include "net/base/network_interfaces_linux.h"
#endif
namespace net {
namespace {
#if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)) || BUILDFLAG(IS_FUCHSIA)
bool HaveOnlyLoopbackAddressesUsingGetifaddrs() {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
struct ifaddrs* interface_addr = nullptr;
int rv = getifaddrs(&interface_addr);
if (rv != 0) {
DVPLOG(1) << "getifaddrs() failed";
return false;
}
bool result = true;
for (struct ifaddrs* interface = interface_addr; interface != nullptr;
interface = interface->ifa_next) {
if (!(IFF_UP & interface->ifa_flags)) {
continue;
}
if (IFF_LOOPBACK & interface->ifa_flags) {
continue;
}
const struct sockaddr* addr = interface->ifa_addr;
if (!addr) {
continue;
}
if (addr->sa_family == AF_INET6) {
// Safe cast since this is AF_INET6.
const struct sockaddr_in6* addr_in6 =
reinterpret_cast<const struct sockaddr_in6*>(addr);
const struct in6_addr* sin6_addr = &addr_in6->sin6_addr;
if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_LINKLOCAL(sin6_addr)) {
continue;
}
}
if (addr->sa_family != AF_INET6 && addr->sa_family != AF_INET) {
continue;
}
result = false;
break;
}
freeifaddrs(interface_addr);
return result;
}
#endif // (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)) ||
// BUILDFLAG(IS_FUCHSIA)
// This implementation will always be posted to a thread pool.
bool HaveOnlyLoopbackAddressesSlow() {
#if BUILDFLAG(IS_WIN)
// TODO(wtc): implement with the GetAdaptersAddresses function.
NOTIMPLEMENTED();
return false;
#elif BUILDFLAG(IS_ANDROID)
return android::HaveOnlyLoopbackAddresses();
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
return HaveOnlyLoopbackAddressesUsingGetifaddrs();
#endif // defined(various platforms)
}
#if BUILDFLAG(IS_LINUX)
// This implementation can run on the main thread as it will not block.
bool HaveOnlyLoopbackAddressesFast(AddressMapOwnerLinux* address_map_owner) {
// The AddressMapOwnerLinux has already cached all the information necessary
// to determine if only loopback addresses exist.
AddressMapOwnerLinux::AddressMap address_map =
address_map_owner->GetAddressMap();
std::unordered_set<int> online_links = address_map_owner->GetOnlineLinks();
for (const auto& [address, ifaddrmsg] : address_map) {
// If there is an online link that isn't loopback or IPv6 link-local, return
// false.
// `online_links` shouldn't ever contain a loopback address, but keep the
// check as it is clearer and harmless.
//
// NOTE(2023-05-26): `online_links` only contains links with *both*
// IFF_LOWER_UP and IFF_UP, which is stricter than the
// HaveOnlyLoopbackAddressesUsingGetifaddrs() check above. LOWER_UP means
// the physical link layer is up and IFF_UP means the interface is
// administratively up. This new behavior might even be desirable, but if
// this causes issues it will need to be reverted.
if (online_links.contains(ifaddrmsg.ifa_index) && !address.IsLoopback() &&
!(address.IsIPv6() && address.IsLinkLocal())) {
return false;
}
}
return true;
}
#endif // BUILDFLAG(IS_LINUX)
} // namespace
void RunHaveOnlyLoopbackAddressesJob(
base::OnceCallback<void(bool)> finished_cb) {
#if BUILDFLAG(IS_LINUX)
// On Linux, this check can be fast if it accesses only network information
// that's cached by NetworkChangeNotifier, so there's no need to post this
// task to a thread pool. If HaveOnlyLoopbackAddressesFast() *is* posted to a
// different thread, it can cause a TSAN error when also setting a mock
// NetworkChangeNotifier in tests. So it's important to not run off the main
// thread if using cached, global information.
AddressMapOwnerLinux* address_map_owner =
NetworkChangeNotifier::GetAddressMapOwner();
if (address_map_owner) {
// Post `finished_cb` to avoid the bug-prone sometimes-synchronous behavior,
// which is only useful in latency-sensitive situations.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(finished_cb),
HaveOnlyLoopbackAddressesFast(address_map_owner)));
return;
}
#endif // BUILDFLAG(IS_LINUX)
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&HaveOnlyLoopbackAddressesSlow), std::move(finished_cb));
}
} // namespace net