Add support for manually configuring subnets as VPN

This patch adds support for manually setting subnets that
should be handled as VPN, i.e be subject to VpnPreference,
in case webrtc fails to auto-detect VPNs.

Bug: webrtc:13097
Change-Id: I42514f0677a35cfe30ad053570fa9c2a5b4a856b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/230122
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#34852}
This commit is contained in:
Jonas Oreland 2021-08-25 15:43:02 +02:00 committed by WebRTC LUCI CQ
parent c5cb7f1fad
commit 2ee0e64696
9 changed files with 134 additions and 1 deletions

View File

@ -661,6 +661,10 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface {
// perfect, adherence to this preference cannot be guaranteed.
VpnPreference vpn_preference = VpnPreference::kDefault;
// List of address/length subnets that should be treated like
// VPN (in case webrtc fails to auto detect them).
std::vector<rtc::NetworkMask> vpn_list;
//
// Don't forget to update operator== if adding something.
//

View File

@ -406,6 +406,10 @@ class RTC_EXPORT PortAllocator : public sigslot::has_slots<> {
vpn_preference_ = preference;
}
// Set list of <ipaddress, mask> that shall be categorized as VPN.
// Implemented by BasicPortAllocator.
virtual void SetVpnList(const std::vector<rtc::NetworkMask>& vpn_list) {}
std::unique_ptr<PortAllocatorSession> CreateSession(
const std::string& content_name,
int component,

View File

@ -1228,6 +1228,11 @@ void BasicPortAllocatorSession::PrunePortsAndRemoveCandidates(
}
}
void BasicPortAllocator::SetVpnList(
const std::vector<rtc::NetworkMask>& vpn_list) {
network_manager_->set_vpn_list(vpn_list);
}
// AllocationSequence
AllocationSequence::AllocationSequence(

View File

@ -74,6 +74,8 @@ class RTC_EXPORT BasicPortAllocator : public PortAllocator {
return relay_port_factory_;
}
void SetVpnList(const std::vector<rtc::NetworkMask>& vpn_list) override;
private:
void OnIceRegathering(PortAllocatorSession* session,
IceRegatheringReason reason);

View File

@ -337,6 +337,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
absl::optional<int> report_usage_pattern_delay_ms;
absl::optional<int> stable_writable_connection_ping_interval_ms;
webrtc::VpnPreference vpn_preference;
std::vector<rtc::NetworkMask> vpn_list;
};
static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this),
"Did you add something to RTCConfiguration and forget to "
@ -399,7 +400,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
report_usage_pattern_delay_ms == o.report_usage_pattern_delay_ms &&
stable_writable_connection_ping_interval_ms ==
o.stable_writable_connection_ping_interval_ms &&
vpn_preference == o.vpn_preference;
vpn_preference == o.vpn_preference && vpn_list == o.vpn_list;
}
bool PeerConnectionInterface::RTCConfiguration::operator!=(

View File

@ -231,6 +231,7 @@ PeerConnectionFactory::CreatePeerConnectionOrError(
}
dependencies.allocator->SetNetworkIgnoreMask(options().network_ignore_mask);
dependencies.allocator->SetVpnList(configuration.vpn_list);
std::unique_ptr<RtcEventLog> event_log =
worker_thread()->Invoke<std::unique_ptr<RtcEventLog>>(

View File

@ -541,6 +541,7 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces,
continue;
}
// Convert to InterfaceAddress.
// TODO(webrtc:13114): Convert ConvertIfAddrs to use rtc::Netmask.
if (!ifaddrs_converter->ConvertIfAddrsToIPAddress(cursor, &ip, &mask)) {
continue;
}
@ -576,8 +577,16 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces,
vpn_underlying_adapter_type =
network_monitor_->GetVpnUnderlyingAdapterType(cursor->ifa_name);
}
int prefix_length = CountIPMaskBits(mask);
prefix = TruncateIP(ip, prefix_length);
if (adapter_type != ADAPTER_TYPE_VPN &&
IsConfiguredVpn(prefix, prefix_length)) {
vpn_underlying_adapter_type = adapter_type;
adapter_type = ADAPTER_TYPE_VPN;
}
std::string key =
MakeNetworkKey(std::string(cursor->ifa_name), prefix, prefix_length);
auto iter = current_networks.find(key);
@ -766,8 +775,15 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored,
adapter_type = ADAPTER_TYPE_UNKNOWN;
break;
}
auto vpn_underlying_adapter_type = ADAPTER_TYPE_UNKNOWN;
if (adapter_type != ADAPTER_TYPE_VPN &&
IsConfiguredVpn(prefix, prefix_length)) {
vpn_underlying_adapter_type = adapter_type;
adapter_type = ADAPTER_TYPE_VPN;
}
std::unique_ptr<Network> network(new Network(
name, description, prefix, prefix_length, adapter_type));
network->set_underlying_type_for_vpn(vpn_underlying_adapter_type);
network->set_default_local_address_provider(this);
network->set_mdns_responder_provider(this);
network->set_scope_id(scope_id);
@ -1118,4 +1134,26 @@ std::string Network::ToString() const {
return ss.Release();
}
void BasicNetworkManager::set_vpn_list(const std::vector<NetworkMask>& vpn) {
if (thread_ == nullptr) {
vpn_ = vpn;
} else {
thread_->Invoke<void>(RTC_FROM_HERE, [this, vpn] { vpn_ = vpn; });
}
}
bool BasicNetworkManager::IsConfiguredVpn(IPAddress prefix,
int prefix_length) const {
RTC_DCHECK_RUN_ON(thread_);
for (const auto& vpn : vpn_) {
if (prefix_length >= vpn.prefix_length()) {
auto copy = TruncateIP(prefix, vpn.prefix_length());
if (copy == vpn.address()) {
return true;
}
}
}
return false;
}
} // namespace rtc

View File

@ -79,6 +79,25 @@ class MdnsResponderProvider {
virtual webrtc::MdnsResponderInterface* GetMdnsResponder() const = 0;
};
// Network/mask in CIDR representation.
class NetworkMask {
public:
NetworkMask(const IPAddress& addr, int prefix_length)
: address_(addr), prefix_length_(prefix_length) {}
const IPAddress& address() const { return address_; }
int prefix_length() const { return prefix_length_; }
bool operator==(const NetworkMask& o) const {
return address_ == o.address_ && prefix_length_ == o.prefix_length_;
}
private:
IPAddress address_;
// Length of valid bits in address_ (for ipv4 valid range is 0-32)
int prefix_length_;
};
// Generic network manager interface. It provides list of local
// networks.
//
@ -158,6 +177,8 @@ class RTC_EXPORT NetworkManager : public DefaultLocalAddressProvider,
// MdnsResponderProvider interface.
webrtc::MdnsResponderInterface* GetMdnsResponder() const override;
virtual void set_vpn_list(const std::vector<NetworkMask>& vpn) {}
};
// Base class for NetworkManager implementations.
@ -249,6 +270,12 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase,
network_ignore_list_ = list;
}
// Set a list of manually configured VPN's.
void set_vpn_list(const std::vector<NetworkMask>& vpn) override;
// Check if |prefix| is configured as VPN.
bool IsConfiguredVpn(IPAddress prefix, int prefix_length) const;
// Bind a socket to interface that ip address belong to.
// Implementation look up interface name and calls
// BindSocketToNetwork on NetworkMonitor.
@ -305,6 +332,8 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase,
RTC_GUARDED_BY(thread_);
bool allow_mac_based_ipv6_ RTC_GUARDED_BY(thread_) = false;
bool bind_using_ifname_ RTC_GUARDED_BY(thread_) = false;
std::vector<NetworkMask> vpn_;
};
// Represents a Unix-type network interface, with a name and single address.

View File

@ -44,6 +44,12 @@ namespace rtc {
namespace {
IPAddress IPFromString(const std::string& str) {
IPAddress ip;
RTC_CHECK(IPFromString(str, &ip));
return ip;
}
class FakeNetworkMonitor : public NetworkMonitorInterface {
public:
void Start() override { started_ = true; }
@ -1385,4 +1391,47 @@ TEST_F(NetworkTest, NetworkCostVpn_VpnMoreExpensive) {
delete net2;
}
TEST_F(NetworkTest, VpnList) {
{
BasicNetworkManager manager;
manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 16)});
manager.StartUpdating();
EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.1.1"), 32));
EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.12.1"), 24));
EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.0.0"), 16));
EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.0.0"), 24));
EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.133.1.1"), 32));
EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.133.0.0"), 16));
EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.168.0.0"), 15));
}
{
BasicNetworkManager manager;
manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 24)});
manager.StartUpdating();
EXPECT_FALSE(manager.IsConfiguredVpn(IPFromString("192.168.1.1"), 32));
EXPECT_TRUE(manager.IsConfiguredVpn(IPFromString("192.168.0.1"), 32));
}
}
#if defined(WEBRTC_POSIX)
// TODO(webrtc:13114): Implement the InstallIpv4Network for windows.
TEST_F(NetworkTest, VpnListOverrideAdapterType) {
BasicNetworkManager manager;
manager.set_vpn_list({NetworkMask(IPFromString("192.168.0.0"), 16)});
manager.StartUpdating();
char if_name[20] = "eth0";
auto addr_list =
InstallIpv4Network(if_name, "192.168.1.23", "255.255.255.255", manager);
BasicNetworkManager::NetworkList list;
manager.GetNetworks(&list);
ASSERT_EQ(1u, list.size());
EXPECT_EQ(ADAPTER_TYPE_VPN, list[0]->type());
EXPECT_EQ(ADAPTER_TYPE_ETHERNET, list[0]->underlying_type_for_vpn());
ClearNetworks(manager);
ReleaseIfAddrs(addr_list);
}
#endif // defined(WEBRTC_POSIX)
} // namespace rtc