diff --git a/AUTHORS b/AUTHORS index 00b6b44f16..6ac0fa95e6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -78,6 +78,7 @@ Telenor Digital AS <*@telenor.com> Temasys Communications <*@temasys.io> The Chromium Authors <*@chromium.org> The WebRTC Authors <*@webrtc.org> +Videxio AS <*@videxio.com> Vonage Holdings Corp. <*@vonage.com> Wire Swiss GmbH <*@wire.com> Miguel Paris diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h index 0125455ee4..4a36af9ca4 100644 --- a/api/peerconnectioninterface.h +++ b/api/peerconnectioninterface.h @@ -380,6 +380,10 @@ class PeerConnectionInterface : public rtc::RefCountInterface { // Can be set to INT_MAX to effectively disable the limit. int max_ipv6_networks = cricket::kDefaultMaxIPv6Networks; + // Exclude link-local network interfaces + // from considertaion for gathering ICE candidates. + bool disable_link_local_networks = false; + // If set to true, use RTP data channels instead of SCTP. // TODO(deadbeef): Remove this. We no longer commit to supporting RTP data // channels, though some applications are still working on moving off of diff --git a/p2p/base/portallocator.h b/p2p/base/portallocator.h index f8e813f502..1081448918 100644 --- a/p2p/base/portallocator.h +++ b/p2p/base/portallocator.h @@ -87,6 +87,10 @@ enum { // the application to work in a wider variety of environments, at the expense // of having to allocate additional candidates. PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS = 0x8000, + + // Exclude link-local network interfaces + // from considertaion after adapter enumeration. + PORTALLOCATOR_DISABLE_LINK_LOCAL_NETWORKS = 0x10000, }; // Defines various reasons that have caused ICE regathering. diff --git a/p2p/client/basicportallocator.cc b/p2p/client/basicportallocator.cc index 339e007766..86cec8fb34 100644 --- a/p2p/client/basicportallocator.cc +++ b/p2p/client/basicportallocator.cc @@ -633,6 +633,14 @@ std::vector BasicPortAllocatorSession::GetNetworks() { network_manager->GetAnyAddressNetworks(&networks); } } + // Filter out link-local networks if needed. + if (flags() & PORTALLOCATOR_DISABLE_LINK_LOCAL_NETWORKS) { + networks.erase(std::remove_if(networks.begin(), networks.end(), + [](rtc::Network* network) { + return IPIsLinkLocal(network->prefix()); + }), + networks.end()); + } // Do some more filtering, depending on the network ignore mask and "disable // costly networks" flag. networks.erase(std::remove_if(networks.begin(), networks.end(), diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index 87bef82f15..40f3ff473b 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -583,6 +583,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( bool disable_ipv6; bool disable_ipv6_on_wifi; int max_ipv6_networks; + bool disable_link_local_networks; bool enable_rtp_data_channel; rtc::Optional screencast_min_bitrate; rtc::Optional combined_audio_video_bwe; @@ -628,6 +629,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( media_config == o.media_config && disable_ipv6 == o.disable_ipv6 && disable_ipv6_on_wifi == o.disable_ipv6_on_wifi && max_ipv6_networks == o.max_ipv6_networks && + disable_link_local_networks == o.disable_link_local_networks && enable_rtp_data_channel == o.enable_rtp_data_channel && screencast_min_bitrate == o.screencast_min_bitrate && combined_audio_video_bwe == o.combined_audio_video_bwe && @@ -4240,6 +4242,11 @@ bool PeerConnection::InitializePortAllocator_n( RTC_LOG(LS_INFO) << "Do not gather candidates on high-cost networks"; } + if (configuration.disable_link_local_networks) { + portallocator_flags |= cricket::PORTALLOCATOR_DISABLE_LINK_LOCAL_NETWORKS; + RTC_LOG(LS_INFO) << "Disable candidates on link-local network interfaces."; + } + port_allocator_->set_flags(portallocator_flags); // No step delay is used while allocating ports. port_allocator_->set_step_delay(cricket::kMinimumStepDelay); diff --git a/rtc_base/ipaddress.cc b/rtc_base/ipaddress.cc index a31680d851..d441f075f8 100644 --- a/rtc_base/ipaddress.cc +++ b/rtc_base/ipaddress.cc @@ -45,8 +45,10 @@ static const in6_addr k6To4Prefix = {{{0x20, 0x02, 0}}}; static const in6_addr kTeredoPrefix = {{{0x20, 0x01, 0x00, 0x00}}}; static const in6_addr kV4CompatibilityPrefix = {{{0}}}; static const in6_addr k6BonePrefix = {{{0x3f, 0xfe, 0}}}; +static const in6_addr kPrivateNetworkPrefix = {{{0xFD}}}; -static bool IsPrivateV4(uint32_t ip); +static bool IPIsHelper(const IPAddress& ip, + const in6_addr& tomatch, int length); static in_addr ExtractMappedAddress(const in6_addr& addr); uint32_t IPAddress::v4AddressAsHostOrderInteger() const { @@ -223,12 +225,27 @@ std::ostream& operator<<(std::ostream& os, const InterfaceAddress& ip) { return os; } -bool IsPrivateV4(uint32_t ip_in_host_order) { - return ((ip_in_host_order >> 24) == 127) || - ((ip_in_host_order >> 24) == 10) || +static bool IPIsPrivateNetworkV4(const IPAddress& ip) { + uint32_t ip_in_host_order = ip.v4AddressAsHostOrderInteger(); + return ((ip_in_host_order >> 24) == 10) || ((ip_in_host_order >> 20) == ((172 << 4) | 1)) || - ((ip_in_host_order >> 16) == ((192 << 8) | 168)) || - ((ip_in_host_order >> 16) == ((169 << 8) | 254)); + ((ip_in_host_order >> 16) == ((192 << 8) | 168)); +} + +static bool IPIsPrivateNetworkV6(const IPAddress& ip) { + return IPIsHelper(ip, kPrivateNetworkPrefix, 8); +} + +bool IPIsPrivateNetwork(const IPAddress& ip) { + switch (ip.family()) { + case AF_INET: { + return IPIsPrivateNetworkV4(ip); + } + case AF_INET6: { + return IPIsPrivateNetworkV6(ip); + } + } + return false; } in_addr ExtractMappedAddress(const in6_addr& in6) { @@ -294,43 +311,29 @@ bool IPIsAny(const IPAddress& ip) { return false; } +static bool IPIsLoopbackV4(const IPAddress& ip) { + uint32_t ip_in_host_order = ip.v4AddressAsHostOrderInteger(); + return ((ip_in_host_order >> 24) == 127); +} + +static bool IPIsLoopbackV6(const IPAddress& ip) { + return ip == IPAddress(in6addr_loopback); +} + bool IPIsLoopback(const IPAddress& ip) { switch (ip.family()) { case AF_INET: { - return (ip.v4AddressAsHostOrderInteger() >> 24) == 127; + return IPIsLoopbackV4(ip); } case AF_INET6: { - return ip == IPAddress(in6addr_loopback); - } - } - return false; -} - -bool IPIsLinkLocal(const IPAddress& ip) { - switch (ip.family()) { - case AF_INET: { - uint32_t ip_in_host_order = ip.v4AddressAsHostOrderInteger(); - return (ip_in_host_order >> 16) == ((169 << 8) | 254); - } - case AF_INET6: { - // Can't use the helper because the prefix is 10 bits. - in6_addr addr = ip.ipv6_address(); - return addr.s6_addr[0] == 0xFE && addr.s6_addr[1] == 0x80; + return IPIsLoopbackV6(ip); } } return false; } bool IPIsPrivate(const IPAddress& ip) { - switch (ip.family()) { - case AF_INET: { - return IsPrivateV4(ip.v4AddressAsHostOrderInteger()); - } - case AF_INET6: { - return IPIsLinkLocal(ip) || IPIsLoopback(ip); - } - } - return false; + return IPIsLinkLocal(ip) || IPIsLoopback(ip) || IPIsPrivateNetwork(ip); } bool IPIsUnspec(const IPAddress& ip) { @@ -458,6 +461,29 @@ bool IPIs6To4(const IPAddress& ip) { return IPIsHelper(ip, k6To4Prefix, 16); } +static bool IPIsLinkLocalV4(const IPAddress& ip) { + uint32_t ip_in_host_order = ip.v4AddressAsHostOrderInteger(); + return ((ip_in_host_order >> 16) == ((169 << 8) | 254)); +} + +static bool IPIsLinkLocalV6(const IPAddress& ip) { + // Can't use the helper because the prefix is 10 bits. + in6_addr addr = ip.ipv6_address(); + return (addr.s6_addr[0] == 0xFE) && ((addr.s6_addr[1] & 0xC0) == 0x80); +} + +bool IPIsLinkLocal(const IPAddress& ip) { + switch (ip.family()) { + case AF_INET: { + return IPIsLinkLocalV4(ip); + } + case AF_INET6: { + return IPIsLinkLocalV6(ip); + } + } + return false; +} + // According to http://www.ietf.org/rfc/rfc2373.txt, Appendix A, page 19. An // address which contains MAC will have its 11th and 12th bytes as FF:FE as well // as the U/L bit as 1. diff --git a/rtc_base/ipaddress.h b/rtc_base/ipaddress.h index 2e06baf99c..4ef7d085b6 100644 --- a/rtc_base/ipaddress.h +++ b/rtc_base/ipaddress.h @@ -155,6 +155,11 @@ bool IPFromString(const std::string& str, int flags, bool IPIsAny(const IPAddress& ip); bool IPIsLoopback(const IPAddress& ip); bool IPIsLinkLocal(const IPAddress& ip); +// Identify a private network address like "192.168.111.222" +// (see https://en.wikipedia.org/wiki/Private_network ) +bool IPIsPrivateNetwork(const IPAddress& ip); +// Identify if an IP is "private", that is a loopback +// or an address belonging to a link-local or a private network. bool IPIsPrivate(const IPAddress& ip); bool IPIsUnspec(const IPAddress& ip); size_t HashIP(const IPAddress& ip); diff --git a/rtc_base/ipaddress_unittest.cc b/rtc_base/ipaddress_unittest.cc index 5d7f2a95cf..90c9559120 100644 --- a/rtc_base/ipaddress_unittest.cc +++ b/rtc_base/ipaddress_unittest.cc @@ -17,6 +17,7 @@ static const unsigned int kIPv4AddrSize = 4; static const unsigned int kIPv6AddrSize = 16; static const unsigned int kIPv4RFC1918Addr = 0xC0A80701; static const unsigned int kIPv4PublicAddr = 0x01020304; +static const unsigned int kIPv4LinkLocalAddr = 0xA9FE10C1; // 169.254.16.193 static const in6_addr kIPv6LinkLocalAddr = {{{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x30, 0x5b, 0xff, @@ -581,6 +582,28 @@ TEST(IPAddressTest, TestIsLoopback) { EXPECT_TRUE(IPIsLoopback(IPAddress(in6addr_loopback))); } +TEST(IPAddressTest, TestIsLinkLocal) { + // "any" addresses + EXPECT_FALSE(IPIsLinkLocal(IPAddress(INADDR_ANY))); + EXPECT_FALSE(IPIsLinkLocal(IPAddress(in6addr_any))); + // loopback addresses + EXPECT_FALSE(IPIsLinkLocal(IPAddress(INADDR_LOOPBACK))); + EXPECT_FALSE(IPIsLinkLocal(IPAddress(in6addr_loopback))); + // public addresses + EXPECT_FALSE(IPIsLinkLocal(IPAddress(kIPv4PublicAddr))); + EXPECT_FALSE(IPIsLinkLocal(IPAddress(kIPv6PublicAddr))); + // private network addresses + EXPECT_FALSE(IPIsLinkLocal(IPAddress(kIPv4RFC1918Addr))); + // mapped addresses + EXPECT_FALSE(IPIsLinkLocal(IPAddress(kIPv4MappedAnyAddr))); + EXPECT_FALSE(IPIsLinkLocal(IPAddress(kIPv4MappedPublicAddr))); + EXPECT_FALSE(IPIsLinkLocal(IPAddress(kIPv4MappedRFC1918Addr))); + + // link-local network addresses + EXPECT_TRUE(IPIsLinkLocal(IPAddress(kIPv4LinkLocalAddr))); + EXPECT_TRUE(IPIsLinkLocal(IPAddress(kIPv6LinkLocalAddr))); +} + // Verify that IPIsAny catches all cases of "any" address. TEST(IPAddressTest, TestIsAny) { IPAddress addr; diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm b/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm index faf34d63d0..92b334bad7 100644 --- a/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm +++ b/sdk/objc/Framework/Classes/PeerConnection/RTCConfiguration.mm @@ -30,6 +30,7 @@ @synthesize candidateNetworkPolicy = _candidateNetworkPolicy; @synthesize continualGatheringPolicy = _continualGatheringPolicy; @synthesize maxIPv6Networks = _maxIPv6Networks; +@synthesize disableLinkLocalNetworks = _disableLinkLocalNetworks; @synthesize audioJitterBufferMaxPackets = _audioJitterBufferMaxPackets; @synthesize audioJitterBufferFastAccelerate = _audioJitterBufferFastAccelerate; @synthesize iceConnectionReceivingTimeout = _iceConnectionReceivingTimeout; @@ -75,6 +76,7 @@ _continualGatheringPolicy = [[self class] continualGatheringPolicyForNativePolicy:nativePolicy]; _maxIPv6Networks = config.max_ipv6_networks; + _disableLinkLocalNetworks = config.disable_link_local_networks; _audioJitterBufferMaxPackets = config.audio_jitter_buffer_max_packets; _audioJitterBufferFastAccelerate = config.audio_jitter_buffer_fast_accelerate; _iceConnectionReceivingTimeout = config.ice_connection_receiving_timeout; @@ -100,10 +102,11 @@ } - (NSString *)description { + static NSString *formatString = @"RTCConfiguration: " + @"{\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%@\n%@\n%d\n%d\n}\n"; + return - [NSString stringWithFormat: - @"RTCConfiguration: " - @"{\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%d\n%d\n%d\n%d\n%d\n%d\n%d\n%@\n%@\n%d\n}\n", + [NSString stringWithFormat:formatString, _iceServers, [[self class] stringForTransportPolicy:_iceTransportPolicy], [[self class] stringForBundlePolicy:_bundlePolicy], @@ -120,6 +123,7 @@ _shouldPresumeWritableWhenFullyRelayed, _iceCheckMinInterval, _iceRegatherIntervalRange, + _disableLinkLocalNetworks, _maxIPv6Networks]; } @@ -147,6 +151,7 @@ nativeConfig->continual_gathering_policy = [[self class] nativeContinualGatheringPolicyForPolicy:_continualGatheringPolicy]; nativeConfig->max_ipv6_networks = _maxIPv6Networks; + nativeConfig->disable_link_local_networks = _disableLinkLocalNetworks; nativeConfig->audio_jitter_buffer_max_packets = _audioJitterBufferMaxPackets; nativeConfig->audio_jitter_buffer_fast_accelerate = _audioJitterBufferFastAccelerate ? true : false; diff --git a/sdk/objc/Framework/Headers/WebRTC/RTCConfiguration.h b/sdk/objc/Framework/Headers/WebRTC/RTCConfiguration.h index fcb62b9416..f3a8d3fc99 100644 --- a/sdk/objc/Framework/Headers/WebRTC/RTCConfiguration.h +++ b/sdk/objc/Framework/Headers/WebRTC/RTCConfiguration.h @@ -93,6 +93,12 @@ RTC_EXPORT */ @property(nonatomic, assign) int maxIPv6Networks; +/** Exclude link-local network interfaces + * from considertaion for gathering ICE candidates. + * Defaults to NO. + */ +@property(nonatomic, assign) BOOL disableLinkLocalNetworks; + @property(nonatomic, assign) int audioJitterBufferMaxPackets; @property(nonatomic, assign) BOOL audioJitterBufferFastAccelerate; @property(nonatomic, assign) int iceConnectionReceivingTimeout;