Bug 1219557 - don't pair candidates from different reserved networks. r=mt r=bwc
authorNils Ohlmeier [:drno] <drno@ohlmeier.org>
Fri, 04 Dec 2015 20:43:15 -0800
changeset 310333 357f32e43e16bb48330596cfb301d8333192a7f6
parent 310332 00f88afb10ac4cdb0b75b0dc3d5e13180df09751
child 310334 a3bb1bf0b330812382d7833312cb7ecd9188a435
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmt, bwc
bugs1219557
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1219557 - don't pair candidates from different reserved networks. r=mt r=bwc
media/mtransport/test/ice_unittest.cpp
media/mtransport/third_party/nICEr/src/ice/ice_component.c
media/mtransport/third_party/nICEr/src/net/transport_addr.c
media/mtransport/third_party/nICEr/src/net/transport_addr.h
--- a/media/mtransport/test/ice_unittest.cpp
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -90,16 +90,34 @@ enum TrickleMode { TRICKLE_NONE, TRICKLE
 const unsigned int ICE_TEST_PEER_OFFERER = (1 << 0);
 const unsigned int ICE_TEST_PEER_ALLOW_LOOPBACK = (1 << 1);
 const unsigned int ICE_TEST_PEER_ENABLED_TCP = (1 << 2);
 const unsigned int ICE_TEST_PEER_ALLOW_LINK_LOCAL = (1 << 3);
 const unsigned int ICE_TEST_PEER_HIDE_NON_DEFAULT = (1 << 4);
 
 typedef std::string (*CandidateFilter)(const std::string& candidate);
 
+std::vector<std::string> split(const std::string &s, char delim) {
+  std::vector<std::string> elems;
+  std::stringstream ss(s);
+  std::string item;
+  while (std::getline(ss, item, delim)) {
+    elems.push_back(item);
+  }
+  return elems;
+}
+
+static std::string IsSrflxCandidate(const std::string& candidate) {
+  std::vector<std::string> tokens = split(candidate, ' ');
+  if ((tokens.at(6) == "typ") && (tokens.at(7) == "srflx")) {
+    return candidate;
+  }
+  return std::string();
+}
+
 static std::string IsRelayCandidate(const std::string& candidate) {
   if (candidate.find("typ relay") != std::string::npos) {
     return candidate;
   }
   return std::string();
 }
 
 static std::string IsTcpCandidate(const std::string& candidate) {
@@ -118,16 +136,24 @@ static std::string IsTcpSoCandidate(cons
 
 static std::string IsLoopbackCandidate(const std::string& candidate) {
   if (candidate.find("127.0.0.") != std::string::npos) {
     return candidate;
   }
   return std::string();
 }
 
+static std::string IsIpv4Candidate(const std::string& candidate) {
+  std::vector<std::string> tokens = split(candidate, ' ');
+  if (tokens.at(4).find(":") == std::string::npos) {
+    return candidate;
+  }
+  return std::string();
+}
+
 static std::string SabotageHostCandidateAndDropReflexive(
     const std::string& candidate) {
   if (candidate.find("typ srflx") != std::string::npos) {
     return std::string();
   }
 
   if (candidate.find("typ host") != std::string::npos) {
     return kUnreachableHostIceCandidate;
@@ -443,17 +469,17 @@ class IceTestPeer : public sigslot::has_
   std::vector<std::string> GetGlobalAttributes() {
     std::vector<std::string> attrs(ice_ctx_->GetGlobalAttributes());
     if (simulate_ice_lite_) {
       attrs.push_back("ice-lite");
     }
     return attrs;
   }
 
-   std::vector<std::string> GetCandidates(size_t stream) {
+  std::vector<std::string> GetCandidates(size_t stream) {
     std::vector<std::string> v;
 
     RUN_ON_THREAD(
         test_utils->sts_target(),
         WrapRunnableRet(&v, this, &IceTestPeer::GetCandidates_s, stream));
 
     return v;
   }
@@ -495,16 +521,42 @@ class IceTestPeer : public sigslot::has_
     expected_local_transport_ = local_transport;
     expected_remote_type_ = remote;
   }
 
   void SetExpectedRemoteCandidateAddr(const std::string& addr) {
     expected_remote_addr_ = addr;
   }
 
+  int GetCandidatesPrivateIpv4Range(size_t stream) {
+    std::vector<std::string> candidates = GetCandidates(stream);
+
+    int host_net = 0;
+    for (auto c : candidates) {
+      if (c.find("typ host") != std::string::npos) {
+        nr_transport_addr addr;
+        std::vector<std::string> tokens = split(c, ' ');
+        int r = nr_str_port_to_transport_addr(tokens.at(4).c_str(), 0, IPPROTO_UDP, &addr);
+        MOZ_ASSERT(!r);
+        if (!r && (addr.ip_version == NR_IPV4)) {
+          int n = nr_transport_addr_get_private_addr_range(&addr);
+          if (n) {
+            if (host_net) {
+              // TODO: add support for multiple private interfaces
+              std::cerr << "This test doesn't support multiple private interfaces";
+              return -1;
+            }
+            host_net = n;
+          }
+        }
+      }
+    }
+    return host_net;
+  }
+
   bool gathering_complete() { return gathering_complete_; }
   int ready_ct() { return ready_ct_; }
   bool is_ready_s(size_t stream) {
     if (!streams_[stream]) {
       EXPECT_TRUE(false) << "No such stream " << stream;
       return false;
     }
     return streams_[stream]->state() == NrIceMediaStream::ICE_OPEN;
@@ -2461,16 +2513,50 @@ void DelayRelayCandidates(
     if ((*i)->IsRelay()) {
       (*i)->Schedule(ms);
     } else {
       (*i)->Schedule(0);
     }
   }
 }
 
+void AddNonPairableCandidates(
+    std::vector<SchedulableTrickleCandidate*>& candidates,
+    IceTestPeer *peer, size_t stream, int net_type) {
+  for (int i=1; i<5; i++) {
+    if (net_type == i)
+      continue;
+    switch (i) {
+      case 1:
+        candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+                   "candidate:0 1 UDP 2113601790 10.0.0.1 12345 typ host"));
+        break;
+      case 2:
+        candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+                   "candidate:0 1 UDP 2113601791 172.16.1.1 12345 typ host"));
+        break;
+      case 3:
+        candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+                   "candidate:0 1 UDP 2113601792 192.168.0.1 12345 typ host"));
+        break;
+      case 4:
+        candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+                   "candidate:0 1 UDP 2113601793 100.64.1.1 12345 typ host"));
+        break;
+      default:
+        UNIMPLEMENTED;
+    }
+  }
+
+  for (auto i = candidates.rbegin(); i != candidates.rend(); ++i) {
+    std::cerr << "Scheduling candidate: " << (*i)->Candidate().c_str() << std::endl;
+    (*i)->Schedule(0);
+  }
+}
+
 void DropTrickleCandidates(
     std::vector<SchedulableTrickleCandidate*>& candidates) {
 }
 
 TEST_F(IceConnectTest, TestConnectTrickleAddStreamDuringICE) {
   AddStream("first", 1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
@@ -2857,16 +2943,86 @@ TEST_F(IceConnectTest, TestPollCandPairs
 
   r = p2_->GetCandidatePairs(0, &pairs);
   ASSERT_EQ(NS_OK, r);
   ASSERT_NE(0U, pairs.size());
   ASSERT_TRUE(p2_->CandidatePairsPriorityDescending(pairs));
   ASSERT_TRUE(ContainsSucceededPair(pairs));
 }
 
+TEST_F(IceConnectTest, TestHostCandPairingFilter) {
+  AddStream("first", 1);
+  ASSERT_TRUE(Gather(kDefaultTimeout, false));
+  SetCandidateFilter(IsIpv4Candidate);
+
+  int host_net = p1_->GetCandidatesPrivateIpv4Range(0);
+  if (host_net <= 0) {
+    // TODO bug 1226838: make this work with multiple private IPs
+    FAIL() << "This test needs exactly one private IPv4 host candidate to work" << std::endl;
+  }
+
+  ConnectTrickle();
+  AddNonPairableCandidates(p1_->ControlTrickle(0), p1_, 0, host_net);
+  AddNonPairableCandidates(p2_->ControlTrickle(0), p2_, 0, host_net);
+
+  std::vector<NrIceCandidatePair> pairs;
+  p1_->GetCandidatePairs(0, &pairs);
+  for (auto p : pairs) {
+    std::cerr << "Verifying pair:" << std::endl;
+    p1_->DumpCandidatePair(p);
+    nr_transport_addr addr;
+    nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == host_net);
+    nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == host_net);
+  }
+}
+
+TEST_F(IceConnectTest, TestSrflxCandPairingFilter) {
+  if (g_stun_server_address.empty()) {
+    return;
+  }
+
+  AddStream("first", 1);
+  ASSERT_TRUE(Gather(kDefaultTimeout));
+  SetCandidateFilter(IsSrflxCandidate);
+
+  if (p1_->GetCandidatesPrivateIpv4Range(0) <= 0) {
+    // TODO bug 1226838: make this work with public IP addresses
+    std::cerr << "Don't run this test at IETF meetings!" << std::endl;
+    FAIL() << "This test needs one private IPv4 host candidate to work" << std::endl;
+  }
+
+  ConnectTrickle();
+  SimulateTrickleP1(0);
+  SimulateTrickleP2(0);
+
+  std::vector<NrIceCandidatePair> pairs;
+  p1_->GetCandidatePairs(0, &pairs);
+  for (auto p : pairs) {
+    std::cerr << "Verifying P1 pair:" << std::endl;
+    p1_->DumpCandidatePair(p);
+    nr_transport_addr addr;
+    nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) != 0);
+    nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == 0);
+  }
+  p2_->GetCandidatePairs(0, &pairs);
+  for (auto p : pairs) {
+    std::cerr << "Verifying P2 pair:" << std::endl;
+    p2_->DumpCandidatePair(p);
+    nr_transport_addr addr;
+    nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) != 0);
+    nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == 0);
+  }
+}
+
 TEST_F(IceConnectTest, TestPollCandPairsDuringConnect) {
   AddStream("first", 1);
   ASSERT_TRUE(Gather());
 
   p2_->Connect(p1_, TRICKLE_NONE, false);
   p1_->Connect(p2_, TRICKLE_NONE, false);
 
   std::vector<NrIceCandidatePair> pairs1;
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -943,16 +943,56 @@ int nr_ice_component_service_pre_answer_
         nr_ice_pre_answer_request_destroy(&r1);
       }
     }
 
     _status=0;
      return(_status);
   }
 
+int nr_ice_component_can_candidate_tcptype_pair(nr_socket_tcp_type left, nr_socket_tcp_type right)
+  {
+    if (left && !right)
+      return(0);
+    if (!left && right)
+      return(0);
+    if (left == TCP_TYPE_ACTIVE && right != TCP_TYPE_PASSIVE)
+      return(0);
+    if (left == TCP_TYPE_SO && right != TCP_TYPE_SO)
+      return(0);
+    if (left == TCP_TYPE_PASSIVE)
+      return(0);
+
+    return(1);
+  }
+
+/* local vs. remote matters here because we allow private -> public pairing,
+ * but discourage public -> private pairing. */
+int nr_ice_component_can_candidate_addr_pair(nr_transport_addr *local, nr_transport_addr *remote)
+  {
+    int remote_range;
+
+    if(local->ip_version != remote->ip_version)
+      return(0);
+    if(nr_transport_addr_is_link_local(local) !=
+       nr_transport_addr_is_link_local(remote))
+      return(0);
+    /* This prevents our ice_unittest (or broken clients) from pairing a
+     * loopback with a host candidate. */
+    if(nr_transport_addr_is_loopback(local) !=
+       nr_transport_addr_is_loopback(remote))
+      return(0);
+    remote_range = nr_transport_addr_get_private_addr_range(remote);
+    if(remote_range && (nr_transport_addr_get_private_addr_range(local) !=
+       remote_range))
+      return(0);
+
+    return(1);
+  }
+
 int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote)
   {
     int r, _status;
     nr_ice_candidate *pcand;
     nr_ice_cand_pair *pair=0;
     char codeword[5];
 
     nr_ice_compute_codeword(lcand->label,strlen(lcand->label),codeword);
@@ -970,35 +1010,19 @@ int nr_ice_component_pair_candidate(nr_i
         break;
       default:
         assert(0);
         ABORT(R_INTERNAL);
         break;
     }
 
     TAILQ_FOREACH(pcand, &pcomp->candidates, entry_comp){
-      if (lcand->tcp_type && !pcand->tcp_type)
-        continue;
-      if (!lcand->tcp_type && pcand->tcp_type)
-        continue;
-      if (lcand->tcp_type == TCP_TYPE_ACTIVE && pcand->tcp_type != TCP_TYPE_PASSIVE)
-        continue;
-      if (lcand->tcp_type == TCP_TYPE_SO && pcand->tcp_type != TCP_TYPE_SO)
-        continue;
-      if (lcand->tcp_type == TCP_TYPE_PASSIVE)
+      if(!nr_ice_component_can_candidate_addr_pair(&lcand->addr, &pcand->addr))
         continue;
-      if(pcand->addr.ip_version != lcand->addr.ip_version)
-        continue;
-      /* This prevents our ice_unittest from pairing a loopback with a host
-       * candidate. */
-      if(nr_transport_addr_is_loopback(&lcand->addr) &&
-         !nr_transport_addr_is_loopback(&pcand->addr))
-        continue;
-      if(!nr_transport_addr_is_loopback(&lcand->addr) &&
-         nr_transport_addr_is_loopback(&pcand->addr))
+      if(!nr_ice_component_can_candidate_tcptype_pair(lcand->tcp_type, pcand->tcp_type))
         continue;
 
       /*
         Two modes, depending on |pair_all_remote|
 
         1. Pair remote candidates which have not been paired
            (used in initial pairing or in processing the other side's
            trickle candidates).
--- a/media/mtransport/third_party/nICEr/src/net/transport_addr.c
+++ b/media/mtransport/third_party/nICEr/src/net/transport_addr.c
@@ -390,21 +390,31 @@ int nr_transport_addr_is_loopback(nr_tra
         UNIMPLEMENTED;
     }
 
     return(0);
   }
 
 int nr_transport_addr_is_link_local(nr_transport_addr *addr)
   {
-    if(addr->ip_version == NR_IPV6){
-      UINT4* addrTop = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr);
-      return ((*addrTop & htonl(0xFFC00000)) == htonl(0xFE800000));
-    } else {
-      assert(0);
+    switch(addr->ip_version){
+      case NR_IPV4:
+        /* RFC3927: 169.254/16 */
+        if ((ntohl(addr->u.addr4.sin_addr.s_addr) & 0xFFFF0000) == 0xA9FE0000)
+          return(1);
+        break;
+      case NR_IPV6:
+        {
+          UINT4* addrTop = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr);
+          if ((*addrTop & htonl(0xFFC00000)) == htonl(0xFE800000))
+            return(2);
+        }
+        break;
+      default:
+        UNIMPLEMENTED;
     }
 
     return(0);
   }
 
 int nr_transport_addr_is_wildcard(nr_transport_addr *addr)
   {
     switch(addr->ip_version){
@@ -421,8 +431,42 @@ int nr_transport_addr_is_wildcard(nr_tra
           return(1);
         break;
       default:
         UNIMPLEMENTED;
     }
 
     return(0);
   }
+
+nr_transport_addr_mask nr_private_ipv4_addrs[] = {
+  /* RFC1918: 10/8 */
+  {0x0A000000, 0xFF000000},
+  /* RFC1918: 172.16/12 */
+  {0xAC100000, 0xFFF00000},
+  /* RFC1918: 192.168/16 */
+  {0xC0A80000, 0xFFFF0000},
+  /* RFC6598: 100.64/10 */
+  {0x64400000, 0xFFC00000}
+};
+
+int nr_transport_addr_get_private_addr_range(nr_transport_addr *addr)
+  {
+    switch(addr->ip_version){
+      case NR_IPV4:
+        {
+          UINT4 ip = ntohl(addr->u.addr4.sin_addr.s_addr);
+          for (int i=0; i<(sizeof(nr_private_ipv4_addrs)/sizeof(nr_transport_addr_mask)); i++) {
+            if ((ip & nr_private_ipv4_addrs[i].mask) == nr_private_ipv4_addrs[i].addr)
+              return i + 1;
+          }
+        }
+        break;
+      case NR_IPV6:
+        return(0);
+      default:
+        UNIMPLEMENTED;
+    }
+
+    return(0);
+  }
+
+
--- a/media/mtransport/third_party/nICEr/src/net/transport_addr.h
+++ b/media/mtransport/third_party/nICEr/src/net/transport_addr.h
@@ -66,16 +66,21 @@ typedef struct nr_transport_addr_ {
     struct sockaddr_in6 addr6;
   } u;
   char ifname[MAXIFNAME];
   /* A string version.
      56 = 5 ("IP6:[") + 39 (ipv6 address) + 2 ("]:") + 5 (port) + 4 (/UDP) + 1 (null) */
   char as_string[56];
 } nr_transport_addr;
 
+typedef struct nr_transport_addr_mask_ {
+  UINT4 addr;
+  UINT4 mask;
+} nr_transport_addr_mask;
+
 int nr_sockaddr_to_transport_addr(struct sockaddr *saddr, int protocol, int keep, nr_transport_addr *addr);
 
 // addresses, ports in local byte order
 int nr_ip4_port_to_transport_addr(UINT4 ip4, UINT2 port, int protocol, nr_transport_addr *addr);
 int nr_str_port_to_transport_addr(const char *str, UINT2 port, int protocol, nr_transport_addr *addr);
 int nr_ip6_port_to_transport_addr(struct in6_addr* addr6, UINT2 port, int protocol, nr_transport_addr *addr);
 
 int nr_transport_addr_get_addrstring(const nr_transport_addr *addr, char *str, int maxlen);
@@ -83,16 +88,17 @@ int nr_transport_addr_get_port(nr_transp
 int nr_transport_addr_cmp(nr_transport_addr *addr1,nr_transport_addr *addr2,int mode);
 #define NR_TRANSPORT_ADDR_CMP_MODE_VERSION   1
 #define NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL  2
 #define NR_TRANSPORT_ADDR_CMP_MODE_ADDR      3
 #define NR_TRANSPORT_ADDR_CMP_MODE_ALL       4
 
 int nr_transport_addr_is_wildcard(nr_transport_addr *addr);
 int nr_transport_addr_is_loopback(nr_transport_addr *addr);
+int nr_transport_addr_get_private_addr_range(nr_transport_addr *addr);
 int nr_transport_addr_is_link_local(nr_transport_addr *addr);
 int nr_transport_addr_copy(nr_transport_addr *to, nr_transport_addr *from);
 int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, nr_transport_addr *from);
 int nr_transport_addr_fmt_addr_string(nr_transport_addr *addr);
 int nr_transport_addr_fmt_ifname_addr_string(const nr_transport_addr *addr, char *buf, int len);
 int nr_transport_addr_set_port(nr_transport_addr *addr, int port);
 
 #endif