Expose a link-local network interfaces enumeration option

The bug 8432 is caused by trying to connect through a
"link-local" interface (IP address 169.254.0.x/16),
which is listed among the iPhone network interfaces.
The bug is not happening if the link-local network interfaces
are skipped in the ICE candidate gethering process.

To control this behaviour an option - disable_link_local_networks -
is added inside the RTCConfiguration.
It is used to set the new BasicPortAllocatorSession flag -
PORTALLOCATOR_DISABLE_LINK_LOCAL_NETWORKS.
The port allocator flag is added if the configuration option is set.

IPIsLinkLocal IPAddress function and its friends (IPIsLoopback, IPIsPrivate)
are refactored to work on both IPv4 and IPv6.
Unit test IPIsLinkLocal.

Bonus: fix a bug in IPIsLinkLocalV6:
take into account just 10 network mask bits instead of 16.

Bug: webrtc:8432
Change-Id: Ibe8f677a36098057b7fcad5c798380727b23359b
Reviewed-on: https://webrtc-review.googlesource.com/36380
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Reviewed-by: Zhi Huang <zhihuang@webrtc.org>
Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21922}
diff --git a/AUTHORS b/AUTHORS
index 00b6b44..6ac0fa9 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -78,6 +78,7 @@
 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 <mparisdiaz@gmail.com>
diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h
index 0125455..4a36af9 100644
--- a/api/peerconnectioninterface.h
+++ b/api/peerconnectioninterface.h
@@ -380,6 +380,10 @@
     // 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 f8e813f..1081448 100644
--- a/p2p/base/portallocator.h
+++ b/p2p/base/portallocator.h
@@ -87,6 +87,10 @@
   // 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 339e007..86cec8f 100644
--- a/p2p/client/basicportallocator.cc
+++ b/p2p/client/basicportallocator.cc
@@ -633,6 +633,14 @@
       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 87bef82..40f3ff4 100644
--- a/pc/peerconnection.cc
+++ b/pc/peerconnection.cc
@@ -583,6 +583,7 @@
     bool disable_ipv6;
     bool disable_ipv6_on_wifi;
     int max_ipv6_networks;
+    bool disable_link_local_networks;
     bool enable_rtp_data_channel;
     rtc::Optional<int> screencast_min_bitrate;
     rtc::Optional<bool> combined_audio_video_bwe;
@@ -628,6 +629,7 @@
          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 @@
     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 a31680d..d441f07 100644
--- a/rtc_base/ipaddress.cc
+++ b/rtc_base/ipaddress.cc
@@ -45,8 +45,10 @@
 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 @@
   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 @@
   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 @@
   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 2e06baf..4ef7d08 100644
--- a/rtc_base/ipaddress.h
+++ b/rtc_base/ipaddress.h
@@ -155,6 +155,11 @@
 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 5d7f2a9..90c9559 100644
--- a/rtc_base/ipaddress_unittest.cc
+++ b/rtc_base/ipaddress_unittest.cc
@@ -17,6 +17,7 @@
 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 @@
   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 faf34d6..92b334b 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 fcb62b9..f3a8d3f 100644
--- a/sdk/objc/Framework/Headers/WebRTC/RTCConfiguration.h
+++ b/sdk/objc/Framework/Headers/WebRTC/RTCConfiguration.h
@@ -93,6 +93,12 @@
  */
 @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;