Bug 1189041 - Add option to only gather addresses for default route. r=bwc a=sylvestre
authorEKR <ekr@rtfm.com>
Wed, 12 Aug 2015 10:53:15 -0400
changeset 288818 ffdc61617a74ff24f5b3a7b5446445d15eece9d9
parent 288817 1c9bbc9454dbeff074cd3a542e291fa058f7ab06
child 288819 d8d62e273a8cf840ec0b843d7f81960ea661393f
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbwc, sylvestre
bugs1189041
milestone42.0a2
Bug 1189041 - Add option to only gather addresses for default route. r=bwc a=sylvestre
media/mtransport/nr_socket_prsock.cpp
media/mtransport/nricectx.cpp
media/mtransport/nricectx.h
media/mtransport/test/ice_unittest.cpp
media/mtransport/test/stunserver.cpp
media/mtransport/test/turn_unittest.cpp
media/mtransport/test_nr_socket.cpp
media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
media/mtransport/third_party/nICEr/src/ice/ice_component.c
media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
media/mtransport/third_party/nICEr/src/net/transport_addr.h
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
modules/libpref/init/all.js
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -733,34 +733,49 @@ void NrSocket::close() {
   mCondition = NS_BASE_STREAM_CLOSED;
 }
 
 
 int NrSocket::connect(nr_transport_addr *addr) {
   ASSERT_ON_THREAD(ststhread_);
   int r,_status;
   PRNetAddr naddr;
-  int32_t status;
+  int32_t connect_status, getsockname_status;
 
   if ((r=nr_transport_addr_to_praddr(addr, &naddr)))
     ABORT(r);
 
   if(!fd_)
     ABORT(R_EOD);
 
   // Note: this just means we tried to connect, not that we
   // are actually live.
   connect_invoked_ = true;
-  status = PR_Connect(fd_, &naddr, PR_INTERVAL_NO_WAIT);
+  connect_status = PR_Connect(fd_, &naddr, PR_INTERVAL_NO_WAIT);
+  if (connect_status != PR_SUCCESS) {
+    if (PR_GetError() != PR_IN_PROGRESS_ERROR)
+      ABORT(R_IO_ERROR);
+  }
 
-  if (status != PR_SUCCESS) {
-    if (PR_GetError() == PR_IN_PROGRESS_ERROR)
-      ABORT(R_WOULDBLOCK);
+  // If our local address is wildcard, then fill in the
+  // address now.
+  if(nr_transport_addr_is_wildcard(&my_addr_)){
+    getsockname_status = PR_GetSockName(fd_, &naddr);
+    if (getsockname_status != PR_SUCCESS){
+      r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
+      ABORT(R_INTERNAL);
+    }
 
-    ABORT(R_IO_ERROR);
+    if((r=nr_praddr_to_transport_addr(&naddr,&my_addr_,addr->protocol,1)))
+      ABORT(r);
+  }
+
+  // Now return the WOULDBLOCK if needed.
+  if (connect_status != PR_SUCCESS) {
+    ABORT(R_WOULDBLOCK);
   }
 
   _status=0;
 abort:
   return(_status);
 }
 
 
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -377,19 +377,19 @@ void NrIceCtx::trickle_cb(void *arg, nr_
 }
 
 RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& name,
                                   bool offerer,
                                   bool set_interface_priorities,
                                   bool allow_loopback,
                                   bool tcp_enabled,
                                   bool allow_link_local,
+                                  bool hide_non_default,
                                   Policy policy) {
-
-  RefPtr<NrIceCtx> ctx = new NrIceCtx(name, offerer, policy);
+   RefPtr<NrIceCtx> ctx = new NrIceCtx(name, offerer, policy);
 
   // Initialize the crypto callbacks and logging stuff
   if (!initialized) {
     NR_reg_init(NR_REG_MODE_LOCAL);
     RLogRingBuffer::CreateInstance();
     nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl;
     initialized = true;
 
@@ -494,16 +494,19 @@ RefPtr<NrIceCtx> NrIceCtx::Create(const 
 
   UINT4 flags = offerer ? NR_ICE_CTX_FLAGS_OFFERER:
       NR_ICE_CTX_FLAGS_ANSWERER;
   flags |= NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION;
   if (policy == ICE_POLICY_RELAY) {
     flags |= NR_ICE_CTX_FLAGS_RELAY_ONLY;
   }
 
+  if (hide_non_default)
+    flags |= NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS;
+
   r = nr_ice_ctx_create(const_cast<char *>(name.c_str()), flags,
                         &ctx->ctx_);
   if (r) {
     MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name << "'");
     return nullptr;
   }
 
 #ifdef USE_INTERFACE_PRIORITIZER
--- a/media/mtransport/nricectx.h
+++ b/media/mtransport/nricectx.h
@@ -208,22 +208,24 @@ class NrIceCtx {
                      ICE_CONTROLLED
   };
 
   enum Policy { ICE_POLICY_NONE,
                 ICE_POLICY_RELAY,
                 ICE_POLICY_ALL
   };
 
+  // TODO(ekr@rtfm.com): Too many bools here. Bug 1193437.
   static RefPtr<NrIceCtx> Create(const std::string& name,
                                  bool offerer,
                                  bool set_interface_priorities = true,
                                  bool allow_loopback = false,
                                  bool tcp_enabled = true,
                                  bool allow_link_local = false,
+                                 bool hide_non_default = false,
                                  Policy policy = ICE_POLICY_ALL);
 
   // Deinitialize all ICE global state. Used only for testing.
   static void internal_DeinitializeGlobal();
 
 
   nr_ice_ctx *ctx() { return ctx_; }
   nr_ice_peer_ctx *peer() { return peer_; }
--- a/media/mtransport/test/ice_unittest.cpp
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -245,22 +245,24 @@ class SchedulableTrickleCandidate {
     std::string candidate_;
     void *timer_handle_;
 
     DISALLOW_COPY_ASSIGN(SchedulableTrickleCandidate);
 };
 
 class IceTestPeer : public sigslot::has_slots<> {
  public:
-
+  // TODO(ekr@rtfm.com): Convert to flags when NrIceCtx::Create() does.
+  // Bug 1193437.
   IceTestPeer(const std::string& name, bool offerer, bool set_priorities,
-              bool allow_loopback = false, bool enable_tcp = true) :
+              bool allow_loopback = false, bool enable_tcp = true,
+              bool allow_link_local = false, bool hide_non_default = false) :
       name_(name),
       ice_ctx_(NrIceCtx::Create(name, offerer, set_priorities, allow_loopback,
-                                enable_tcp)),
+                                enable_tcp, allow_link_local, hide_non_default)),
       streams_(),
       candidates_(),
       gathering_complete_(false),
       ready_ct_(0),
       ice_complete_(false),
       ice_reached_checking_(false),
       received_(0),
       sent_(0),
@@ -1056,16 +1058,32 @@ class IceTestPeer : public sigslot::has_
     ice_ctx_->peer()->tiebreaker = tiebreaker;
   }
 
   void SimulateIceLite() {
     simulate_ice_lite_ = true;
     SetControlling(NrIceCtx::ICE_CONTROLLED);
   }
 
+  nsresult GetDefaultCandidate(unsigned int stream, NrIceCandidate* cand) {
+    nsresult rv;
+
+    test_utils->sts_target()->Dispatch(
+        WrapRunnableRet(&rv, this,
+                        &IceTestPeer::GetDefaultCandidate_s,
+                        stream, cand),
+        NS_DISPATCH_SYNC);
+
+    return rv;
+  }
+
+  nsresult GetDefaultCandidate_s(unsigned int stream, NrIceCandidate* cand) {
+    return streams_[stream]->GetDefaultCandidate(1, cand);
+  }
+
  private:
   std::string name_;
   nsRefPtr<NrIceCtx> ice_ctx_;
   std::vector<mozilla::RefPtr<NrIceMediaStream> > streams_;
   std::map<std::string, std::vector<std::string> > candidates_;
   // Maps from stream id to list of remote trickle candidates
   std::map<size_t, std::vector<SchedulableTrickleCandidate*> >
     controlled_trickle_candidates_;
@@ -1240,16 +1258,27 @@ class IceGatherTest : public ::testing::
             std::string::npos != candidates[c].find(match2)) {
           return true;
         }
       }
     }
     return false;
   }
 
+  void DumpCandidates(unsigned int stream) {
+    std::vector<std::string> candidates = peer_->GetCandidates(stream);
+
+    std::cerr << "Candidates for stream " << stream << "->"
+              << candidates.size() << std::endl;
+
+    for (auto c : candidates) {
+      std::cerr << "Candidate: " << c << std::endl;
+    }
+  }
+
  protected:
   mozilla::ScopedDeletePtr<IceTestPeer> peer_;
 };
 
 class IceConnectTest : public ::testing::Test {
  public:
   IceConnectTest() :
     initted_(false),
@@ -1283,49 +1312,53 @@ class IceConnectTest : public ::testing:
     p2_->AddStream(components);
   }
 
   void RemoveStream(size_t index) {
     p1_->RemoveStream(index);
     p2_->RemoveStream(index);
   }
 
-  void Init(bool set_priorities, bool allow_loopback, bool enable_tcp) {
+  void Init(bool set_priorities, bool allow_loopback, bool enable_tcp,
+            bool default_only = false) {
     if (!initted_) {
       p1_ = new IceTestPeer("P1", true, set_priorities, allow_loopback,
-                            enable_tcp);
+                            enable_tcp, false, default_only);
       p2_ = new IceTestPeer("P2", false, set_priorities, allow_loopback,
-                            enable_tcp);
+                            enable_tcp, false, default_only);
     }
     initted_ = true;
   }
 
-  bool Gather(unsigned int waitTime = kDefaultTimeout) {
+  bool Gather(unsigned int waitTime = kDefaultTimeout,
+              bool setupStunServers = true) {
     Init(false, false, false);
     if (use_nat_) {
       // If we enable nat simulation, but still use a real STUN server somewhere
       // on the internet, we will see failures if there is a real NAT in
       // addition to our simulated one, particularly if it disallows
       // hairpinning.
-      UseTestStunServer();
+      if (setupStunServers) {
+        UseTestStunServer();
+      }
       p1_->UseNat();
       p2_->UseNat();
       p1_->SetFilteringType(filtering_type_);
       p2_->SetFilteringType(filtering_type_);
       p1_->SetMappingType(mapping_type_);
       p2_->SetMappingType(mapping_type_);
       p1_->SetBlockUdp(block_udp_);
       p2_->SetBlockUdp(block_udp_);
-    } else {
+    } else if (setupStunServers) {
       std::vector<NrIceStunServer> stun_servers;
 
       stun_servers.push_back(*NrIceStunServer::Create(g_stun_server_address,
-        kDefaultStunServerPort, kNrIceTransportUdp));
+                                                      kDefaultStunServerPort, kNrIceTransportUdp));
       stun_servers.push_back(*NrIceStunServer::Create(g_stun_server_address,
-        kDefaultStunServerPort, kNrIceTransportTcp));
+                                                      kDefaultStunServerPort, kNrIceTransportTcp));
 
       p1_->SetStunServers(stun_servers);
       p2_->SetStunServers(stun_servers);
     }
 
     p1_->Gather();
     p2_->Gather();
 
@@ -1640,16 +1673,29 @@ TEST_F(IceGatherTest, TestGatherFakeStun
   }
 
   EnsurePeer();
   peer_->SetStunServer(g_stun_server_address, kDefaultStunServerPort);
   peer_->SetFakeResolver();
   Gather();
 }
 
+TEST_F(IceGatherTest, TestGatherStunServerIpAddressDefaultRouteOnly) {
+  if (g_stun_server_address.empty()) {
+    return;
+  }
+
+  peer_ = new IceTestPeer("P1", true, false, false, false, false, true);
+  peer_->AddStream(1);
+  peer_->SetStunServer(g_stun_server_address, kDefaultStunServerPort);
+  peer_->SetFakeResolver();
+  Gather();
+  ASSERT_FALSE(StreamHasMatchingCandidate(0, " host "));
+}
+
 TEST_F(IceGatherTest, TestGatherFakeStunServerHostname) {
   if (g_stun_server_hostname.empty()) {
     return;
   }
 
   EnsurePeer();
   peer_->SetStunServer(g_stun_server_hostname, kDefaultStunServerPort);
   peer_->SetFakeResolver();
@@ -1763,16 +1809,24 @@ TEST_F(IceGatherTest, TestGatherDNSStunB
 TEST_F(IceGatherTest, TestGatherDNSStunBogusHostnameTcp) {
   EnsurePeer();
   peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort,
     kNrIceTransportTcp);
   peer_->SetDNSResolver();
   Gather();
 }
 
+TEST_F(IceGatherTest, TestDefaultCandidate) {
+  EnsurePeer();
+  peer_->SetStunServer(g_stun_server_hostname, kDefaultStunServerPort);
+  Gather();
+  NrIceCandidate default_candidate;
+  ASSERT_TRUE(NS_SUCCEEDED(peer_->GetDefaultCandidate(0, &default_candidate)));
+}
+
 TEST_F(IceGatherTest, TestGatherTurn) {
   EnsurePeer();
   if (g_turn_server.empty())
     return;
   peer_->SetTurnServer(g_turn_server, kDefaultStunServerPort,
                        g_turn_user, g_turn_password, kNrIceTransportUdp);
   Gather();
 }
@@ -1916,16 +1970,47 @@ TEST_F(IceGatherTest, TestStunServerTric
   TestStunServer::GetInstance(AF_INET)->SetActive(false);
   Gather(0);
   ASSERT_FALSE(StreamHasMatchingCandidate(0, "192.0.2.1"));
   TestStunServer::GetInstance(AF_INET)->SetActive(true);
   WaitForGather();
   ASSERT_TRUE(StreamHasMatchingCandidate(0, "192.0.2.1"));
 }
 
+// Test default route only with our fake STUN server and
+// apparently NATted.
+TEST_F(IceGatherTest, TestFakeStunServerNatedDefaultRouteOnly) {
+  peer_ = new IceTestPeer("P1", true, false, false, false, false, true);
+  peer_->AddStream(1);
+  UseFakeStunUdpServerWithResponse("192.0.2.1", 3333);
+  Gather(0);
+  WaitForGather();
+  DumpCandidates(0);
+  ASSERT_FALSE(StreamHasMatchingCandidate(0, "host"));
+  ASSERT_TRUE(StreamHasMatchingCandidate(0, "srflx"));
+  NrIceCandidate default_candidate;
+  nsresult rv = peer_->GetDefaultCandidate(0, &default_candidate);
+  if (NS_SUCCEEDED(rv)) {
+    ASSERT_NE(NrIceCandidate::ICE_HOST, default_candidate.type);
+  }
+}
+
+// Test default route only with our fake STUN server and
+// apparently non-NATted.
+TEST_F(IceGatherTest, TestFakeStunServerNoNatDefaultRouteOnly) {
+  peer_ = new IceTestPeer("P1", true, false, false, false, false, true);
+  peer_->AddStream(1);
+  UseTestStunServer();
+  Gather(0);
+  WaitForGather();
+  DumpCandidates(0);
+  ASSERT_FALSE(StreamHasMatchingCandidate(0, "host"));
+  ASSERT_TRUE(StreamHasMatchingCandidate(0, "srflx"));
+}
+
 TEST_F(IceGatherTest, TestStunTcpServerTrickle) {
   UseFakeStunTcpServerWithResponse("192.0.3.1", 3333);
   TestStunTcpServer::GetInstance(AF_INET)->SetActive(false);
   Gather(0);
   ASSERT_FALSE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype "));
   TestStunTcpServer::GetInstance(AF_INET)->SetActive(true);
   WaitForGather();
   ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype "));
@@ -1986,26 +2071,35 @@ TEST_F(IceConnectTest, DISABLED_TestConn
   AddStream("first", 1);
   ASSERT_TRUE(Gather());
   SetCandidateFilter(IsTcpSoCandidate);
   SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
     NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp);
   Connect();
 }
 
+// Disabled because this breaks with hairpinning.
+TEST_F(IceConnectTest, DISABLED_TestConnectDefaultRouteOnly) {
+  Init(false, false, false, true);
+  AddStream("first", 1);
+  ASSERT_TRUE(Gather());
+  SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
+    NrIceCandidate::Type::ICE_SERVER_REFLEXIVE, kNrIceTransportTcp);
+  Connect();
+}
+
 TEST_F(IceConnectTest, TestLoopbackOnlySortOf) {
   Init(false, true, false);
   AddStream("first", 1);
   SetCandidateFilter(IsLoopbackCandidate);
   ASSERT_TRUE(Gather());
   SetExpectedRemoteCandidateAddr("127.0.0.1");
   Connect();
 }
 
-
 TEST_F(IceConnectTest, TestConnectBothControllingP1Wins) {
   AddStream("first", 1);
   p1_->SetTiebreaker(1);
   p2_->SetTiebreaker(0);
   ASSERT_TRUE(Gather());
   p1_->SetControlling(NrIceCtx::ICE_CONTROLLING);
   p2_->SetControlling(NrIceCtx::ICE_CONTROLLING);
   Connect();
@@ -2091,16 +2185,41 @@ TEST_F(IceConnectTest, TestConnectFullCo
   SetFilteringType(TestNat::ENDPOINT_INDEPENDENT);
   SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
   SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
                    NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
   ASSERT_TRUE(Gather());
   Connect();
 }
 
+TEST_F(IceConnectTest, TestConnectNoNatRouteOnly) {
+  Init(false, false, false, true);
+  AddStream("first", 1);
+  UseTestStunServer();
+  // Because we are connecting from our host candidate to the
+  // other side's apparent srflx (which is also their host)
+  // we see a host/srflx pair.
+  SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
+                   NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
+  ASSERT_TRUE(Gather(kDefaultTimeout, false));
+  Connect();
+}
+
+TEST_F(IceConnectTest, TestConnectFullConeDefaultRouteOnly) {
+  Init(false, false, false, true);
+  AddStream("first", 1);
+  UseNat();
+  SetFilteringType(TestNat::ENDPOINT_INDEPENDENT);
+  SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
+  SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
+                   NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
+  ASSERT_TRUE(Gather());
+  Connect();
+}
+
 TEST_F(IceConnectTest, TestGatherAddressRestrictedCone) {
   AddStream("first", 1);
   UseNat();
   SetFilteringType(TestNat::ADDRESS_DEPENDENT);
   SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
   ASSERT_TRUE(Gather());
 }
 
--- a/media/mtransport/test/stunserver.cpp
+++ b/media/mtransport/test/stunserver.cpp
@@ -534,17 +534,17 @@ int TestStunTcpServer::TryOpenListenSock
   addr->addr.protocol=IPPROTO_TCP;
 
   int r = SetInternalPort(addr, port);
 
   if (r)
     return r;
 
   if (ice_ctx_ == NULL)
-    ice_ctx_ = NrIceCtx::Create("stun", true);
+    ice_ctx_ = NrIceCtx::Create("stun", false, false, false, false, false, false);
 
   //TODO (nils@mozilla.com) can we replace this with a more basic TCP socket
   // alternative which would allow us to remove the framing argument from the
   // nr_socket_multi_tcp_create() call?
   if(nr_socket_multi_tcp_create(ice_ctx_->ctx(),
      &addr->addr, TCP_TYPE_PASSIVE, 0, 0, 2048,
      &listen_sock_)) {
      MOZ_MTLOG(ML_ERROR, "Couldn't create listen socket");
--- a/media/mtransport/test/turn_unittest.cpp
+++ b/media/mtransport/test/turn_unittest.cpp
@@ -499,17 +499,17 @@ int main(int argc, char **argv)
   NSS_NoDB_Init(nullptr);
   NSS_SetDomesticPolicy();
 
   // Set up the ICE registry, etc.
   // TODO(ekr@rtfm.com): Clean up
   std::string dummy("dummy");
   RUN_ON_THREAD(test_utils->sts_target(),
                 WrapRunnableNM(&NrIceCtx::Create,
-                               dummy, false, false, false, false, false,
+                               dummy, false, false, false, false, false, false,
                                NrIceCtx::ICE_POLICY_ALL),
                 NS_DISPATCH_SYNC);
 
   // Start the tests
   ::testing::InitGoogleTest(&argc, argv);
 
   int rv = RUN_ALL_TESTS();
   delete test_utils;
--- a/media/mtransport/test_nr_socket.cpp
+++ b/media/mtransport/test_nr_socket.cpp
@@ -375,17 +375,21 @@ bool TestNrSocket::allow_ingress(const n
 int TestNrSocket::connect(nr_transport_addr *addr) {
   ASSERT_ON_THREAD(ststhread_);
 
   if (connect_invoked_ || !port_mappings_.empty()) {
     MOZ_CRASH("TestNrSocket::connect() called more than once!");
     return R_INTERNAL;
   }
 
-  if (!nat_->enabled_ || nat_->is_an_internal_tuple(*addr)) {
+  if (!nat_->enabled_
+      || addr->protocol==IPPROTO_UDP  // Horrible hack to allow default address
+                                      // discovery to work. Only works because
+                                      // we don't normally connect on UDP.
+      || nat_->is_an_internal_tuple(*addr)) {
     // This will set connect_invoked_
     return NrSocket::connect(addr);
   }
 
   nsRefPtr<NrSocket> external_socket(create_external_socket(*addr));
   if (!external_socket) {
     return R_INTERNAL;
   }
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
@@ -936,29 +936,29 @@ int nr_ice_format_candidate_attribute(nr
       port=9;
     snprintf(attr,maxlen,"candidate:%s %d %s %u %s %d typ %s",
       cand->foundation, cand->component_id, cand->addr.protocol==IPPROTO_UDP?"UDP":"TCP",cand->priority, addr, port,
       nr_ctype_name(cand->type));
 
     len=strlen(attr); attr+=len; maxlen-=len;
 
     /* raddr, rport */
-    raddr = (cand->stream->ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) ?
+    raddr = (cand->stream->ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY |
+             NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS) ?
       &cand->addr : &cand->base;
 
     switch(cand->type){
       case HOST:
         break;
       case SERVER_REFLEXIVE:
       case PEER_REFLEXIVE:
         if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr)))
           ABORT(r);
         if(r=nr_transport_addr_get_port(raddr,&port))
           ABORT(r);
-
         snprintf(attr,maxlen," raddr %s rport %d",addr,port);
         break;
       case RELAYED:
         // comes from XorMappedAddress via AllocateResponse
         if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr)))
           ABORT(r);
         if(r=nr_transport_addr_get_port(raddr,&port))
           ABORT(r);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -662,18 +662,19 @@ int nr_ice_component_maybe_prune_candida
     c2 = TAILQ_FIRST(&comp->candidates);
     while(c2){
       if((c1 != c2) &&
          (c2->state == NR_ICE_CAND_STATE_INITIALIZED) &&
          !nr_transport_addr_cmp(&c1->base,&c2->base,NR_TRANSPORT_ADDR_CMP_MODE_ALL) &&
          !nr_transport_addr_cmp(&c1->addr,&c2->addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
 
         if((c1->type == c2->type) ||
-           (c1->type==HOST && c2->type == SERVER_REFLEXIVE) ||
-           (c2->type==HOST && c1->type == SERVER_REFLEXIVE)){
+           (!(ctx->flags & NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS) &&
+            ((c1->type==HOST && c2->type == SERVER_REFLEXIVE) ||
+             (c2->type==HOST && c1->type == SERVER_REFLEXIVE)))){
 
           /*
              These are redundant. Remove the lower pri one, or if pairing has
              already occurred, remove the newest one.
 
              Since this algorithmis run whenever a new candidate
              is initialized, there should at most one duplicate.
            */
@@ -1358,17 +1359,17 @@ int nr_ice_component_get_default_candida
 
     /* We have the component. Now find the "best" candidate, making
        use of the fact that more "reliable" candidate types have
        higher numbers. So, we sort by type and then priority within
        type
     */
     cand=TAILQ_FIRST(&comp->candidates);
     while(cand){
-      if (cand->state == NR_ICE_CAND_STATE_INITIALIZED &&
+      if (!nr_ice_ctx_hide_candidate(comp->ctx, cand) &&
           cand->addr.ip_version == ip_version) {
         if (!best_cand) {
           best_cand = cand;
         }
         else if (best_cand->type < cand->type) {
           best_cand = cand;
         } else if (best_cand->type == cand->type &&
                    best_cand->priority < cand->priority) {
--- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
@@ -1,8 +1,9 @@
+
 /*
 Copyright (c) 2007, Adobe Systems, Incorporated
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
 
@@ -508,17 +509,18 @@ void nr_ice_gather_finished_cb(NR_SOCKET
 
       if (r=nr_ice_component_maybe_prune_candidate(ctx, cand->component,
                                                    cand, &was_pruned)) {
           r_log(LOG_ICE, LOG_NOTICE, "ICE(%s): Problem pruning candidates",ctx->label);
       }
 
       /* If we are initialized, the candidate wasn't pruned,
          and we have a trickle ICE callback fire the callback */
-      if (ctx->trickle_cb && !was_pruned) {
+      if (ctx->trickle_cb && !was_pruned &&
+          !nr_ice_ctx_hide_candidate(ctx, cand)) {
         ctx->trickle_cb(ctx->trickle_cb_arg, ctx, cand->stream, cand->component_id, cand);
 
         if (nr_ice_ctx_pair_new_trickle_candidates(ctx, cand)) {
           r_log(LOG_ICE,LOG_ERR, "ICE(%s): All could not pair new trickle candidate",ctx->label);
           /* But continue */
         }
       }
     }
@@ -553,27 +555,100 @@ static int nr_ice_ctx_pair_new_trickle_c
       pctx=STAILQ_NEXT(pctx,entry);
     }
 
     _status=0;
  abort:
     return(_status);
   }
 
+/* Get the default address by doing a connect to a known public IP address,
+   in this case Google public DNS:
 
-int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg)
+   IPv4: 8.8.8.8
+   IPv6: 2001:4860:4860::8888
+
+   Then we can do getsockname to get the address. No packets get sent
+   since this is UDP. It's just a way to get the address.
+*/
+static int nr_ice_get_default_address(nr_ice_ctx *ctx, int ip_version, nr_transport_addr* addrp)
   {
     int r,_status;
-    nr_ice_media_stream *stream;
-    nr_local_addr addrs[MAXADDRS];
+    nr_transport_addr addr;
+    nr_transport_addr remote_addr;
+    nr_socket *sock=0;
+
+    switch(ip_version) {
+      case NR_IPV4:
+        if ((r=nr_str_port_to_transport_addr("0.0.0.0", 0, IPPROTO_UDP, &addr)))
+          ABORT(r);
+        if ((r=nr_str_port_to_transport_addr("8.8.8.8", 53, IPPROTO_UDP, &remote_addr)))
+          ABORT(r);
+        break;
+      case NR_IPV6:
+        if ((r=nr_str_port_to_transport_addr("::0", 0, IPPROTO_UDP, &addr)))
+          ABORT(r);
+        if ((r=nr_str_port_to_transport_addr("2001:4860:4860::8888", 53, IPPROTO_UDP, &remote_addr)))
+          ABORT(r);
+        break;
+      default:
+        assert(0);
+        ABORT(R_INTERNAL);
+    }
+
+    if ((r=nr_socket_factory_create_socket(ctx->socket_factory, &addr, &sock)))
+      ABORT(r);
+    if ((r=nr_socket_connect(sock, &remote_addr)))
+      ABORT(r);
+    if ((r=nr_socket_getaddr(sock, addrp)))
+      ABORT(r);
+
+    _status=0;
+  abort:
+    nr_socket_destroy(&sock);
+    return(_status);
+  }
+
+static int nr_ice_get_default_local_address(nr_ice_ctx *ctx, int ip_version, nr_local_addr* addrs, int addr_ct, nr_local_addr *addrp)
+  {
+    int r,_status;
+    nr_transport_addr default_addr;
+    int i;
+
+    if ((r=nr_ice_get_default_address(ctx, ip_version, &default_addr)))
+        ABORT(r);
+
+    for(i=0; i<addr_ct; ++i) {
+      if (!nr_transport_addr_cmp(&default_addr, &addrs[i].addr,
+                                 NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) {
+        if ((r=nr_local_addr_copy(addrp, &addrs[i])))
+          ABORT(r);
+        break;
+      }
+    }
+    if (i==addr_ct)
+      ABORT(R_NOT_FOUND);
+
+    _status=0;
+  abort:
+    return(_status);
+  }
+
+static int nr_ice_get_local_addresses(nr_ice_ctx *ctx)
+  {
+    int r,_status;
+    nr_local_addr local_addrs[MAXADDRS];
+    nr_local_addr *addrs = 0;
     int i,addr_ct;
+    nr_local_addr default_addrs[2];
+    int default_addr_ct = 0;
 
     if (!ctx->local_addrs) {
       /* First, gather all the local addresses we have */
-      if(r=nr_stun_find_local_addresses(addrs,MAXADDRS,&addr_ct)) {
+      if((r=nr_stun_find_local_addresses(local_addrs,MAXADDRS,&addr_ct))) {
         r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to find local addresses",ctx->label);
         ABORT(r);
       }
 
       if (ctx->force_net_interface[0]) {
         /* Limit us to only addresses on a single interface */
         int force_addr_ct = 0;
         for(i=0;i<addr_ct;i++){
@@ -621,16 +696,29 @@ int nr_ice_gather(nr_ice_ctx *ctx, NR_as
         }
       }
 
       if (r=nr_ice_ctx_set_local_addrs(ctx,addrs,addr_ct)) {
         ABORT(r);
       }
     }
 
+    _status=0;
+  abort:
+    return(_status);
+  }
+
+int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg)
+  {
+    int r,_status;
+    nr_ice_media_stream *stream;
+
+    if ((r=nr_ice_get_local_addresses(ctx)))
+      ABORT(r);
+
     if(STAILQ_EMPTY(&ctx->streams)) {
       r_log(LOG_ICE,LOG_ERR,"ICE(%s): Missing streams to initialize",ctx->label);
       ABORT(R_BAD_ARGS);
     }
 
     r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Initializing candidates",ctx->label);
     ctx->done_cb=done_cb;
     ctx->cb_arg=cb_arg;
--- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
@@ -155,16 +155,17 @@ struct nr_ice_ctx_ {
 };
 
 int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp);
 #define NR_ICE_CTX_FLAGS_OFFERER                           1
 #define NR_ICE_CTX_FLAGS_ANSWERER                          (1<<1)
 #define NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION             (1<<2)
 #define NR_ICE_CTX_FLAGS_LITE                              (1<<3)
 #define NR_ICE_CTX_FLAGS_RELAY_ONLY                        (1<<4)
+#define NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS                (1<<5)
 
 int nr_ice_ctx_destroy(nr_ice_ctx **ctxp);
 int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg);
 int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand);
 void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg);
 int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp);
 int nr_ice_remove_media_stream(nr_ice_ctx *ctx,nr_ice_media_stream **streamp);
 int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp);
@@ -174,16 +175,17 @@ int nr_ice_ctx_remember_id(nr_ice_ctx *c
 int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx);
 int nr_ice_ctx_set_stun_servers(nr_ice_ctx *ctx,nr_ice_stun_server *servers, int ct);
 int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers, int ct);
 int nr_ice_ctx_set_resolver(nr_ice_ctx *ctx, nr_resolver *resolver);
 int nr_ice_ctx_set_interface_prioritizer(nr_ice_ctx *ctx, nr_interface_prioritizer *prioritizer);
 int nr_ice_ctx_set_turn_tcp_socket_wrapper(nr_ice_ctx *ctx, nr_socket_wrapper_factory *wrapper);
 void nr_ice_ctx_set_socket_factory(nr_ice_ctx *ctx, nr_socket_factory *factory);
 int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg);
+int nr_ice_ctx_hide_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand);
 
 #define NR_ICE_MAX_ATTRIBUTE_SIZE 256
 
 extern int LOG_ICE;
 
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
--- a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
@@ -135,16 +135,17 @@ int nr_ice_media_stream_initialize(nr_ic
       comp=STAILQ_NEXT(comp,entry);
     }
 
     _status=0;
   abort:
     return(_status);
   }
 
+
 int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp, int *attrctp)
   {
     int attrct=0;
     nr_ice_component *comp;
     char **attrs=0;
     int index=0;
     nr_ice_candidate *cand;
     int r,_status;
@@ -152,17 +153,17 @@ int nr_ice_media_stream_get_attributes(n
     *attrctp=0;
 
     /* First find out how many attributes we need */
     comp=STAILQ_FIRST(&stream->components);
     while(comp){
       if (comp->state != NR_ICE_COMPONENT_DISABLED) {
         cand = TAILQ_FIRST(&comp->candidates);
         while(cand){
-          if (cand->state == NR_ICE_CAND_STATE_INITIALIZED) {
+          if (!nr_ice_ctx_hide_candidate(stream->ctx, cand)) {
             ++attrct;
           }
 
           cand = TAILQ_NEXT(cand, entry_comp);
         }
       }
       comp=STAILQ_NEXT(comp,entry);
     }
@@ -184,17 +185,17 @@ int nr_ice_media_stream_get_attributes(n
     /* Now format the attributes */
     comp=STAILQ_FIRST(&stream->components);
     while(comp){
       if (comp->state != NR_ICE_COMPONENT_DISABLED) {
         nr_ice_candidate *cand;
 
         cand=TAILQ_FIRST(&comp->candidates);
         while(cand){
-          if (cand->state == NR_ICE_CAND_STATE_INITIALIZED) {
+          if (!nr_ice_ctx_hide_candidate(stream->ctx, cand)) {
             assert(index < attrct);
 
             if (index >= attrct)
               ABORT(R_INTERNAL);
 
             if(r=nr_ice_format_candidate_attribute(cand, attrs[index],NR_ICE_MAX_ATTRIBUTE_SIZE))
               ABORT(r);
 
--- a/media/mtransport/third_party/nICEr/src/net/transport_addr.h
+++ b/media/mtransport/third_party/nICEr/src/net/transport_addr.h
@@ -70,17 +70,17 @@ typedef struct nr_transport_addr_ {
      56 = 5 ("IP6:[") + 39 (ipv6 address) + 2 ("]:") + 5 (port) + 4 (/UDP) + 1 (null) */
   char as_string[56];
 } nr_transport_addr;
 
 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 *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(nr_transport_addr *addr, char *str, int maxlen);
 int nr_transport_addr_get_port(nr_transport_addr *addr, int *port);
 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
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -299,28 +299,33 @@ nsresult PeerConnectionMedia::Init(const
   if (NS_FAILED(rv)) {
     CSFLogError(logTag, "%s: Failed to resolve protocol proxy: %d", __FUNCTION__, (int)rv);
     return NS_ERROR_FAILURE;
   }
 #endif // defined(MOZILLA_XPCOMRT_API)
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   bool ice_tcp = Preferences::GetBool("media.peerconnection.ice.tcp", false);
+  bool default_address_only = Preferences::GetBool(
+    "media.peerconnection.ice.default_address_only", false);
 #else
   bool ice_tcp = false;
+  bool default_address_only = false;
 #endif
 
+
   // TODO(ekr@rtfm.com): need some way to set not offerer later
   // Looks like a bug in the NrIceCtx API.
   mIceCtx = NrIceCtx::Create("PC:" + mParentName,
                              true, // Offerer
                              true, // Explicitly set priorities
                              mParent->GetAllowIceLoopback(),
                              ice_tcp,
                              mParent->GetAllowIceLinkLocal(),
+                             default_address_only,
                              policy);
   if(!mIceCtx) {
     CSFLogError(logTag, "%s: Failed to create Ice Context", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
 
   if (NS_FAILED(rv = mIceCtx->SetStunServers(stun_servers))) {
     CSFLogError(logTag, "%s: Failed to set stun servers", __FUNCTION__);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -400,16 +400,18 @@ pref("media.peerconnection.ice.tcp", fal
 pref("media.peerconnection.ice.link_local", false); // Set only for testing IPV6 in networks that don't assign IPV6 addresses
 pref("media.peerconnection.ice.force_interface", ""); // Limit to only a single interface
 pref("media.peerconnection.ice.relay_only", false); // Limit candidates to TURN
 pref("media.peerconnection.use_document_iceservers", true);
 pref("media.peerconnection.identity.enabled", true);
 pref("media.peerconnection.identity.timeout", 10000);
 pref("media.peerconnection.ice.stun_client_maximum_transmits", 7);
 pref("media.peerconnection.ice.trickle_grace_period", 5000);
+pref("media.peerconnection.ice.default_address_only", false);
+
 // These values (aec, agc, and noice) are from media/webrtc/trunk/webrtc/common_types.h
 // kXxxUnchanged = 0, kXxxDefault = 1, and higher values are specific to each
 // setting (for Xxx = Ec, Agc, or Ns).  Defaults are all set to kXxxDefault here.
 pref("media.peerconnection.turn.disable", false);
 #if defined(MOZ_WEBRTC_HARDWARE_AEC_NS)
 pref("media.getusermedia.aec_enabled", false);
 pref("media.getusermedia.noise_enabled", false);
 #else