Add and implement VPN preference
This patch adds a vp preference field to RTCConfig. DEFAULT, // No VPN preference. ONLY_USE_VPN, // only use VPN connections. NEVER_USE_VPN, // never use VPN connections PREFER_VPN, // use a VPN connection if possible, i.e VPN connections sorts higher than all other connections. AVOID_VPN, // only use VPN if there is no other connections, i.e VPN connections sorts last. Bug: webrtc:13097 Change-Id: I3f95bdfa9134e082c7d389f803bd08facfb70262 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/229591 Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Jonas Oreland <jonaso@webrtc.org> Cr-Commit-Position: refs/heads/main@{#34842}
This commit is contained in:
parent
c2113a3fef
commit
c8fa1eeb75
@ -653,6 +653,14 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface {
|
||||
// The ping interval (ms) when the connection is stable and writable. This
|
||||
// parameter overrides the default value in the ICE implementation if set.
|
||||
absl::optional<int> stable_writable_connection_ping_interval_ms;
|
||||
|
||||
// Whether this PeerConnection will avoid VPNs (kAvoidVpn), prefer VPNs
|
||||
// (kPreferVpn), only work over VPN (kOnlyUseVpn) or only work over non-VPN
|
||||
// (kNeverUseVpn) interfaces. This controls which local interfaces the
|
||||
// PeerConnection will prefer to connect over. Since VPN detection is not
|
||||
// perfect, adherence to this preference cannot be guaranteed.
|
||||
VpnPreference vpn_preference = VpnPreference::kDefault;
|
||||
|
||||
//
|
||||
// Don't forget to update operator== if adding something.
|
||||
//
|
||||
|
||||
@ -34,6 +34,16 @@ enum PortPrunePolicy {
|
||||
// on the same network.
|
||||
};
|
||||
|
||||
enum class VpnPreference {
|
||||
kDefault, // No VPN preference.
|
||||
kOnlyUseVpn, // only use VPN connections.
|
||||
kNeverUseVpn, // never use VPN connections
|
||||
kPreferVpn, // use a VPN connection if possible,
|
||||
// i.e VPN connections sorts first.
|
||||
kAvoidVpn, // only use VPN if there is no other connections,
|
||||
// i.e VPN connections sorts last.
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // API_TRANSPORT_ENUMS_H_
|
||||
|
||||
@ -739,6 +739,31 @@ int BasicIceController::CompareCandidatePairNetworks(
|
||||
return compare_a_b_by_network_preference;
|
||||
}
|
||||
|
||||
bool a_vpn = a->network()->IsVpn();
|
||||
bool b_vpn = b->network()->IsVpn();
|
||||
switch (config_.vpn_preference) {
|
||||
case webrtc::VpnPreference::kDefault:
|
||||
break;
|
||||
case webrtc::VpnPreference::kOnlyUseVpn:
|
||||
case webrtc::VpnPreference::kPreferVpn:
|
||||
if (a_vpn && !b_vpn) {
|
||||
return a_is_better;
|
||||
} else if (!a_vpn && b_vpn) {
|
||||
return b_is_better;
|
||||
}
|
||||
break;
|
||||
case webrtc::VpnPreference::kNeverUseVpn:
|
||||
case webrtc::VpnPreference::kAvoidVpn:
|
||||
if (a_vpn && !b_vpn) {
|
||||
return b_is_better;
|
||||
} else if (!a_vpn && b_vpn) {
|
||||
return a_is_better;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t a_cost = a->ComputeNetworkCost();
|
||||
uint32_t b_cost = b->ComputeNetworkCost();
|
||||
// Prefer lower network cost.
|
||||
|
||||
@ -175,6 +175,8 @@ struct IceConfig {
|
||||
|
||||
absl::optional<rtc::AdapterType> network_preference;
|
||||
|
||||
webrtc::VpnPreference vpn_preference = webrtc::VpnPreference::kDefault;
|
||||
|
||||
IceConfig();
|
||||
IceConfig(int receiving_timeout_ms,
|
||||
int backup_connection_ping_interval,
|
||||
|
||||
@ -796,6 +796,9 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
|
||||
config_.regather_on_failed_networks_interval_or_default();
|
||||
regathering_controller_->SetConfig(regathering_config);
|
||||
|
||||
config_.vpn_preference = config.vpn_preference;
|
||||
allocator_->SetVpnPreference(config_.vpn_preference);
|
||||
|
||||
ice_controller_->SetIceConfig(config_);
|
||||
|
||||
RTC_DCHECK(ValidateIceConfig(config_).ok());
|
||||
|
||||
@ -529,9 +529,11 @@ class P2PTransportChannelTestBase : public ::testing::Test,
|
||||
void AddAddress(int endpoint,
|
||||
const SocketAddress& addr,
|
||||
const std::string& ifname,
|
||||
rtc::AdapterType adapter_type) {
|
||||
GetEndpoint(endpoint)->network_manager_.AddInterface(addr, ifname,
|
||||
adapter_type);
|
||||
rtc::AdapterType adapter_type,
|
||||
absl::optional<rtc::AdapterType> underlying_vpn_adapter_type =
|
||||
absl::nullopt) {
|
||||
GetEndpoint(endpoint)->network_manager_.AddInterface(
|
||||
addr, ifname, adapter_type, underlying_vpn_adapter_type);
|
||||
}
|
||||
void RemoveAddress(int endpoint, const SocketAddress& addr) {
|
||||
GetEndpoint(endpoint)->network_manager_.RemoveInterface(addr);
|
||||
@ -3169,6 +3171,119 @@ TEST_F(P2PTransportChannelMultihomedTest, TestRestoreBackupConnection) {
|
||||
DestroyChannels();
|
||||
}
|
||||
|
||||
TEST_F(P2PTransportChannelMultihomedTest, TestVpnDefault) {
|
||||
rtc::ScopedFakeClock clock;
|
||||
AddAddress(0, kPublicAddrs[0], "eth0", rtc::ADAPTER_TYPE_ETHERNET);
|
||||
AddAddress(0, kAlternateAddrs[0], "vpn0", rtc::ADAPTER_TYPE_VPN);
|
||||
AddAddress(1, kPublicAddrs[1]);
|
||||
|
||||
IceConfig config;
|
||||
CreateChannels(config, config, false);
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
CheckConnected(ep1_ch1(), ep2_ch1()) &&
|
||||
!ep1_ch1()->selected_connection()->network()->IsVpn(),
|
||||
kDefaultTimeout, clock);
|
||||
}
|
||||
|
||||
TEST_F(P2PTransportChannelMultihomedTest, TestVpnPreferVpn) {
|
||||
rtc::ScopedFakeClock clock;
|
||||
AddAddress(0, kPublicAddrs[0], "eth0", rtc::ADAPTER_TYPE_ETHERNET);
|
||||
AddAddress(0, kAlternateAddrs[0], "vpn0", rtc::ADAPTER_TYPE_VPN,
|
||||
rtc::ADAPTER_TYPE_CELLULAR);
|
||||
AddAddress(1, kPublicAddrs[1]);
|
||||
|
||||
IceConfig config;
|
||||
config.vpn_preference = webrtc::VpnPreference::kPreferVpn;
|
||||
RTC_LOG(LS_INFO) << "KESO: config.vpn_preference: " << config.vpn_preference;
|
||||
CreateChannels(config, config, false);
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
CheckConnected(ep1_ch1(), ep2_ch1()) &&
|
||||
ep1_ch1()->selected_connection()->network()->IsVpn(),
|
||||
kDefaultTimeout, clock);
|
||||
|
||||
// Block VPN.
|
||||
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kAlternateAddrs[0]);
|
||||
|
||||
// Check that it switches to non-VPN
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
CheckConnected(ep1_ch1(), ep2_ch1()) &&
|
||||
!ep1_ch1()->selected_connection()->network()->IsVpn(),
|
||||
kDefaultTimeout, clock);
|
||||
}
|
||||
|
||||
TEST_F(P2PTransportChannelMultihomedTest, TestVpnAvoidVpn) {
|
||||
rtc::ScopedFakeClock clock;
|
||||
AddAddress(0, kPublicAddrs[0], "eth0", rtc::ADAPTER_TYPE_CELLULAR);
|
||||
AddAddress(0, kAlternateAddrs[0], "vpn0", rtc::ADAPTER_TYPE_VPN,
|
||||
rtc::ADAPTER_TYPE_ETHERNET);
|
||||
AddAddress(1, kPublicAddrs[1]);
|
||||
|
||||
IceConfig config;
|
||||
config.vpn_preference = webrtc::VpnPreference::kAvoidVpn;
|
||||
CreateChannels(config, config, false);
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
CheckConnected(ep1_ch1(), ep2_ch1()) &&
|
||||
!ep1_ch1()->selected_connection()->network()->IsVpn(),
|
||||
kDefaultTimeout, clock);
|
||||
|
||||
// Block non-VPN.
|
||||
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[0]);
|
||||
|
||||
// Check that it switches to VPN
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
CheckConnected(ep1_ch1(), ep2_ch1()) &&
|
||||
ep1_ch1()->selected_connection()->network()->IsVpn(),
|
||||
kDefaultTimeout, clock);
|
||||
}
|
||||
|
||||
TEST_F(P2PTransportChannelMultihomedTest, TestVpnNeverVpn) {
|
||||
rtc::ScopedFakeClock clock;
|
||||
AddAddress(0, kPublicAddrs[0], "eth0", rtc::ADAPTER_TYPE_CELLULAR);
|
||||
AddAddress(0, kAlternateAddrs[0], "vpn0", rtc::ADAPTER_TYPE_VPN,
|
||||
rtc::ADAPTER_TYPE_ETHERNET);
|
||||
AddAddress(1, kPublicAddrs[1]);
|
||||
|
||||
IceConfig config;
|
||||
config.vpn_preference = webrtc::VpnPreference::kNeverUseVpn;
|
||||
CreateChannels(config, config, false);
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
CheckConnected(ep1_ch1(), ep2_ch1()) &&
|
||||
!ep1_ch1()->selected_connection()->network()->IsVpn(),
|
||||
kDefaultTimeout, clock);
|
||||
|
||||
// Block non-VPN.
|
||||
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[0]);
|
||||
|
||||
// Check that it does not switches to VPN
|
||||
clock.AdvanceTime(webrtc::TimeDelta::Millis(kDefaultTimeout));
|
||||
EXPECT_TRUE_SIMULATED_WAIT(!CheckConnected(ep1_ch1(), ep2_ch1()),
|
||||
kDefaultTimeout, clock);
|
||||
}
|
||||
|
||||
TEST_F(P2PTransportChannelMultihomedTest, TestVpnOnlyVpn) {
|
||||
rtc::ScopedFakeClock clock;
|
||||
AddAddress(0, kPublicAddrs[0], "eth0", rtc::ADAPTER_TYPE_CELLULAR);
|
||||
AddAddress(0, kAlternateAddrs[0], "vpn0", rtc::ADAPTER_TYPE_VPN,
|
||||
rtc::ADAPTER_TYPE_ETHERNET);
|
||||
AddAddress(1, kPublicAddrs[1]);
|
||||
|
||||
IceConfig config;
|
||||
config.vpn_preference = webrtc::VpnPreference::kOnlyUseVpn;
|
||||
CreateChannels(config, config, false);
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
CheckConnected(ep1_ch1(), ep2_ch1()) &&
|
||||
ep1_ch1()->selected_connection()->network()->IsVpn(),
|
||||
kDefaultTimeout, clock);
|
||||
|
||||
// Block VPN.
|
||||
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kAlternateAddrs[0]);
|
||||
|
||||
// Check that it does not switch to non-VPN
|
||||
clock.AdvanceTime(webrtc::TimeDelta::Millis(kDefaultTimeout));
|
||||
EXPECT_TRUE_SIMULATED_WAIT(!CheckConnected(ep1_ch1(), ep2_ch1()),
|
||||
kDefaultTimeout, clock);
|
||||
}
|
||||
|
||||
// A collection of tests which tests a single P2PTransportChannel by sending
|
||||
// pings.
|
||||
class P2PTransportChannelPingTest : public ::testing::Test,
|
||||
|
||||
@ -400,6 +400,12 @@ class RTC_EXPORT PortAllocator : public sigslot::has_slots<> {
|
||||
// loopback interfaces.
|
||||
virtual void SetNetworkIgnoreMask(int network_ignore_mask) = 0;
|
||||
|
||||
// Set whether VPN connections should be preferred, avoided, mandated or
|
||||
// blocked.
|
||||
virtual void SetVpnPreference(webrtc::VpnPreference preference) {
|
||||
vpn_preference_ = preference;
|
||||
}
|
||||
|
||||
std::unique_ptr<PortAllocatorSession> CreateSession(
|
||||
const std::string& content_name,
|
||||
int component,
|
||||
@ -638,6 +644,7 @@ class RTC_EXPORT PortAllocator : public sigslot::has_slots<> {
|
||||
uint32_t candidate_filter_;
|
||||
std::string origin_;
|
||||
webrtc::SequenceChecker thread_checker_;
|
||||
webrtc::VpnPreference vpn_preference_ = webrtc::VpnPreference::kDefault;
|
||||
|
||||
private:
|
||||
ServerAddresses stun_servers_;
|
||||
|
||||
@ -213,6 +213,22 @@ void BasicPortAllocator::SetNetworkIgnoreMask(int network_ignore_mask) {
|
||||
network_ignore_mask_ = network_ignore_mask;
|
||||
}
|
||||
|
||||
int BasicPortAllocator::GetNetworkIgnoreMask() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
int mask = network_ignore_mask_;
|
||||
switch (vpn_preference_) {
|
||||
case webrtc::VpnPreference::kOnlyUseVpn:
|
||||
mask |= ~static_cast<int>(rtc::ADAPTER_TYPE_VPN);
|
||||
break;
|
||||
case webrtc::VpnPreference::kNeverUseVpn:
|
||||
mask |= static_cast<int>(rtc::ADAPTER_TYPE_VPN);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
PortAllocatorSession* BasicPortAllocator::CreateSessionInternal(
|
||||
const std::string& content_name,
|
||||
int component,
|
||||
@ -708,7 +724,7 @@ std::vector<rtc::Network*> BasicPortAllocatorSession::GetNetworks() {
|
||||
// costly networks" flag.
|
||||
NetworkFilter ignored_filter(
|
||||
[this](rtc::Network* network) {
|
||||
return allocator_->network_ignore_mask() & network->type();
|
||||
return allocator_->GetNetworkIgnoreMask() & network->type();
|
||||
},
|
||||
"ignored");
|
||||
FilterNetworks(&networks, ignored_filter);
|
||||
@ -731,6 +747,7 @@ std::vector<rtc::Network*> BasicPortAllocatorSession::GetNetworks() {
|
||||
"costly");
|
||||
FilterNetworks(&networks, costly_filter);
|
||||
}
|
||||
|
||||
// Lastly, if we have a limit for the number of IPv6 network interfaces (by
|
||||
// default, it's 5), remove networks to ensure that limit is satisfied.
|
||||
//
|
||||
|
||||
@ -46,10 +46,7 @@ class RTC_EXPORT BasicPortAllocator : public PortAllocator {
|
||||
|
||||
// Set to kDefaultNetworkIgnoreMask by default.
|
||||
void SetNetworkIgnoreMask(int network_ignore_mask) override;
|
||||
int network_ignore_mask() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return network_ignore_mask_;
|
||||
}
|
||||
int GetNetworkIgnoreMask() const;
|
||||
|
||||
rtc::NetworkManager* network_manager() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
|
||||
@ -336,6 +336,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
|
||||
absl::optional<bool> allow_codec_switching;
|
||||
absl::optional<int> report_usage_pattern_delay_ms;
|
||||
absl::optional<int> stable_writable_connection_ping_interval_ms;
|
||||
webrtc::VpnPreference vpn_preference;
|
||||
};
|
||||
static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this),
|
||||
"Did you add something to RTCConfiguration and forget to "
|
||||
@ -397,7 +398,8 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
|
||||
allow_codec_switching == o.allow_codec_switching &&
|
||||
report_usage_pattern_delay_ms == o.report_usage_pattern_delay_ms &&
|
||||
stable_writable_connection_ping_interval_ms ==
|
||||
o.stable_writable_connection_ping_interval_ms;
|
||||
o.stable_writable_connection_ping_interval_ms &&
|
||||
vpn_preference == o.vpn_preference;
|
||||
}
|
||||
|
||||
bool PeerConnectionInterface::RTCConfiguration::operator!=(
|
||||
|
||||
@ -36,7 +36,12 @@ class FakeNetworkManager : public NetworkManagerBase,
|
||||
public:
|
||||
FakeNetworkManager() {}
|
||||
|
||||
typedef std::vector<std::pair<SocketAddress, AdapterType>> IfaceList;
|
||||
struct Iface {
|
||||
SocketAddress socket_address;
|
||||
AdapterType adapter_type;
|
||||
absl::optional<AdapterType> underlying_vpn_adapter_type;
|
||||
};
|
||||
typedef std::vector<Iface> IfaceList;
|
||||
|
||||
void AddInterface(const SocketAddress& iface) {
|
||||
// Ensure a unique name for the interface if its name is not given.
|
||||
@ -47,18 +52,20 @@ class FakeNetworkManager : public NetworkManagerBase,
|
||||
AddInterface(iface, if_name, ADAPTER_TYPE_UNKNOWN);
|
||||
}
|
||||
|
||||
void AddInterface(const SocketAddress& iface,
|
||||
const std::string& if_name,
|
||||
AdapterType type) {
|
||||
void AddInterface(
|
||||
const SocketAddress& iface,
|
||||
const std::string& if_name,
|
||||
AdapterType type,
|
||||
absl::optional<AdapterType> underlying_vpn_adapter_type = absl::nullopt) {
|
||||
SocketAddress address(if_name, 0);
|
||||
address.SetResolvedIP(iface.ipaddr());
|
||||
ifaces_.push_back(std::make_pair(address, type));
|
||||
ifaces_.push_back({address, type, underlying_vpn_adapter_type});
|
||||
DoUpdateNetworks();
|
||||
}
|
||||
|
||||
void RemoveInterface(const SocketAddress& iface) {
|
||||
for (IfaceList::iterator it = ifaces_.begin(); it != ifaces_.end(); ++it) {
|
||||
if (it->first.EqualIPs(iface)) {
|
||||
if (it->socket_address.EqualIPs(iface)) {
|
||||
ifaces_.erase(it);
|
||||
break;
|
||||
}
|
||||
@ -112,17 +119,20 @@ class FakeNetworkManager : public NetworkManagerBase,
|
||||
std::vector<Network*> networks;
|
||||
for (IfaceList::iterator it = ifaces_.begin(); it != ifaces_.end(); ++it) {
|
||||
int prefix_length = 0;
|
||||
if (it->first.ipaddr().family() == AF_INET) {
|
||||
if (it->socket_address.ipaddr().family() == AF_INET) {
|
||||
prefix_length = kFakeIPv4NetworkPrefixLength;
|
||||
} else if (it->first.ipaddr().family() == AF_INET6) {
|
||||
} else if (it->socket_address.ipaddr().family() == AF_INET6) {
|
||||
prefix_length = kFakeIPv6NetworkPrefixLength;
|
||||
}
|
||||
IPAddress prefix = TruncateIP(it->first.ipaddr(), prefix_length);
|
||||
std::unique_ptr<Network> net(new Network(it->first.hostname(),
|
||||
it->first.hostname(), prefix,
|
||||
prefix_length, it->second));
|
||||
IPAddress prefix = TruncateIP(it->socket_address.ipaddr(), prefix_length);
|
||||
std::unique_ptr<Network> net(new Network(
|
||||
it->socket_address.hostname(), it->socket_address.hostname(), prefix,
|
||||
prefix_length, it->adapter_type));
|
||||
if (it->underlying_vpn_adapter_type.has_value()) {
|
||||
net->set_underlying_type_for_vpn(*it->underlying_vpn_adapter_type);
|
||||
}
|
||||
net->set_default_local_address_provider(this);
|
||||
net->AddIP(it->first.ipaddr());
|
||||
net->AddIP(it->socket_address.ipaddr());
|
||||
networks.push_back(net.release());
|
||||
}
|
||||
bool changed;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user