Bug 1219557 - don't pair candidates from different reserved networks. r=mt r=bwc

This commit is contained in:
Nils Ohlmeier [:drno] 2015-12-04 20:43:15 -08:00
parent 49bf8d2d47
commit 041b342a19
4 changed files with 254 additions and 24 deletions

View File

@ -95,6 +95,24 @@ 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;
@ -123,6 +141,14 @@ static std::string IsLoopbackCandidate(const std::string& 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) {
@ -448,7 +474,7 @@ class IceTestPeer : public sigslot::has_slots<> {
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(
@ -500,6 +526,32 @@ class IceTestPeer : public sigslot::has_slots<> {
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) {
@ -2466,6 +2518,40 @@ void DelayRelayCandidates(
}
}
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) {
}
@ -2862,6 +2948,76 @@ TEST_F(IceConnectTest, TestPollCandPairsAfterConnect) {
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());

View File

@ -948,6 +948,46 @@ int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_c
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;
@ -975,25 +1015,9 @@ int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pco
}
TAILQ_FOREACH(pcand, &pcomp->candidates, entry_comp){
if (lcand->tcp_type && !pcand->tcp_type)
if(!nr_ice_component_can_candidate_addr_pair(&lcand->addr, &pcand->addr))
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)
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;
/*

View File

@ -395,11 +395,21 @@ int nr_transport_addr_is_loopback(nr_transport_addr *addr)
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);
@ -426,3 +436,37 @@ int nr_transport_addr_is_wildcard(nr_transport_addr *addr)
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);
}

View File

@ -71,6 +71,11 @@ typedef struct nr_transport_addr_ {
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
@ -88,6 +93,7 @@ int nr_transport_addr_cmp(nr_transport_addr *addr1,nr_transport_addr *addr2,int
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);