Move ComputeFoundation to Candidate.
Move code from P2PTransportChannel to Candidate, where we set the
foundation value for remote prflx candidates.
Bug: none
Change-Id: I7dbcb85bca35dca7297136b0706092dd8d2b153c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/339902
Reviewed-by: Philipp Hancke <phancke@microsoft.com>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41774}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index 0c982a2..9ced1f2 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -183,7 +183,9 @@
"candidate.h",
]
deps = [
+ "../p2p:p2p_constants",
"../rtc_base:checks",
+ "../rtc_base:crc32",
"../rtc_base:ip_address",
"../rtc_base:logging",
"../rtc_base:network_constants",
diff --git a/api/candidate.cc b/api/candidate.cc
index 7718f94..06b3156 100644
--- a/api/candidate.cc
+++ b/api/candidate.cc
@@ -11,6 +11,8 @@
#include "api/candidate.h"
#include "absl/base/attributes.h"
+#include "p2p/base/p2p_constants.h"
+#include "rtc_base/crc32.h"
#include "rtc_base/helpers.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/logging.h"
@@ -25,7 +27,7 @@
Candidate::Candidate()
: id_(rtc::CreateRandomString(8)),
- component_(0),
+ component_(ICE_CANDIDATE_COMPONENT_DEFAULT),
priority_(0),
type_(LOCAL_PORT_TYPE),
network_type_(rtc::ADAPTER_TYPE_UNKNOWN),
@@ -209,6 +211,43 @@
return copy;
}
+void Candidate::ComputeFoundation(const rtc::SocketAddress& base_address,
+ uint64_t tie_breaker) {
+ // https://www.rfc-editor.org/rfc/rfc5245#section-4.1.1.3
+ // The foundation is an identifier, scoped within a session. Two candidates
+ // MUST have the same foundation ID when all of the following are true:
+ //
+ // o they are of the same type.
+ // o their bases have the same IP address (the ports can be different).
+ // o for reflexive and relayed candidates, the STUN or TURN servers used to
+ // obtain them have the same IP address.
+ // o they were obtained using the same transport protocol (TCP, UDP, etc.).
+ //
+ // Similarly, two candidates MUST have different foundations if their
+ // types are different, their bases have different IP addresses, the STUN or
+ // TURN servers used to obtain them have different IP addresses, or their
+ // transport protocols are different.
+
+ rtc::StringBuilder sb;
+ sb << type_ << base_address.ipaddr().ToString() << protocol_
+ << relay_protocol_;
+
+ // https://www.rfc-editor.org/rfc/rfc5245#section-5.2
+ // [...] it is possible for both agents to mistakenly believe they are
+ // controlled or controlling. To resolve this, each agent MUST select a random
+ // number, called the tie-breaker, uniformly distributed between 0 and (2**64)
+ // - 1 (that is, a 64-bit positive integer). This number is used in
+ // connectivity checks to detect and repair this case [...]
+ sb << rtc::ToString(tie_breaker);
+ foundation_ = rtc::ToString(rtc::ComputeCrc32(sb.Release()));
+}
+
+void Candidate::ComputePrflxFoundation() {
+ RTC_DCHECK(is_prflx());
+ RTC_DCHECK(!id_.empty());
+ foundation_ = rtc::ToString(rtc::ComputeCrc32(id_));
+}
+
void Candidate::Assign(std::string& s, absl::string_view view) {
// Assigning via a temporary object, like s = std::string(view), results in
// binary size bloat. To avoid that, extract pointer and size from the
diff --git a/api/candidate.h b/api/candidate.h
index 0aa75aa..77774ed 100644
--- a/api/candidate.h
+++ b/api/candidate.h
@@ -74,10 +74,18 @@
void set_component(int component) { component_ = component; }
const std::string& protocol() const { return protocol_; }
+
+ // Valid protocol values are:
+ // UDP_PROTOCOL_NAME, TCP_PROTOCOL_NAME, SSLTCP_PROTOCOL_NAME,
+ // TLS_PROTOCOL_NAME.
void set_protocol(absl::string_view protocol) { Assign(protocol_, protocol); }
// The protocol used to talk to relay.
const std::string& relay_protocol() const { return relay_protocol_; }
+
+ // Valid protocol values are:
+ // UDP_PROTOCOL_NAME, TCP_PROTOCOL_NAME, SSLTCP_PROTOCOL_NAME,
+ // TLS_PROTOCOL_NAME.
void set_relay_protocol(absl::string_view protocol) {
Assign(relay_protocol_, protocol);
}
@@ -169,7 +177,15 @@
uint16_t network_id() const { return network_id_; }
void set_network_id(uint16_t network_id) { network_id_ = network_id; }
+ // From RFC 5245, section-7.2.1.3:
+ // The foundation of the candidate is set to an arbitrary value, different
+ // from the foundation for all other remote candidates.
+ // Note: Use ComputeFoundation to populate this value.
const std::string& foundation() const { return foundation_; }
+
+ // TODO(tommi): Deprecate in favor of ComputeFoundation.
+ // For situations where serializing/deserializing a candidate is needed,
+ // the constructor can be used to inject a value for the foundation.
void set_foundation(absl::string_view foundation) {
Assign(foundation_, foundation);
}
@@ -221,6 +237,25 @@
Candidate ToSanitizedCopy(bool use_hostname_address,
bool filter_related_address) const;
+ // Computes and populates the `foundation()` field.
+ // Foundation: An arbitrary string that is the same for two candidates
+ // that have the same type, base IP address, protocol (UDP, TCP,
+ // etc.), and STUN or TURN server. If any of these are different,
+ // then the foundation will be different. Two candidate pairs with
+ // the same foundation pairs are likely to have similar network
+ // characteristics. Foundations are used in the frozen algorithm.
+ // A session wide (peerconnection) tie-breaker is applied to the foundation,
+ // adds additional randomness and must be the same for all candidates.
+ void ComputeFoundation(const rtc::SocketAddress& base_address,
+ uint64_t tie_breaker);
+
+ // https://www.rfc-editor.org/rfc/rfc5245#section-7.2.1.3
+ // Call to populate the foundation field for a new peer reflexive remote
+ // candidate. The type of the candidate must be "prflx".
+ // The foundation of the candidate is set to an arbitrary value, different
+ // from the foundation for all other remote candidates.
+ void ComputePrflxFoundation();
+
private:
// TODO(bugs.webrtc.org/13220): With C++17, we get a std::string assignment
// operator accepting any object implicitly convertible to std::string_view,
diff --git a/api/candidate_unittest.cc b/api/candidate_unittest.cc
index e52d448..a74afdc 100644
--- a/api/candidate_unittest.cc
+++ b/api/candidate_unittest.cc
@@ -29,9 +29,9 @@
TEST(CandidateTest, Component) {
Candidate c;
- EXPECT_EQ(c.component(), 0);
- c.set_component(ICE_CANDIDATE_COMPONENT_DEFAULT);
EXPECT_EQ(c.component(), ICE_CANDIDATE_COMPONENT_DEFAULT);
+ c.set_component(ICE_CANDIDATE_COMPONENT_RTCP);
+ EXPECT_EQ(c.component(), ICE_CANDIDATE_COMPONENT_RTCP);
}
TEST(CandidateTest, TypeName) {
@@ -53,4 +53,48 @@
EXPECT_EQ(c.type(), RELAY_PORT_TYPE);
}
+TEST(CandidateTest, Foundation) {
+ Candidate c;
+ EXPECT_TRUE(c.foundation().empty());
+ c.set_protocol("udp");
+ c.set_relay_protocol("udp");
+
+ rtc::SocketAddress address("99.99.98.1", 1024);
+ c.set_address(address);
+ c.ComputeFoundation(c.address(), 1);
+ std::string foundation1 = c.foundation();
+ EXPECT_FALSE(foundation1.empty());
+
+ // Change the tiebreaker.
+ c.ComputeFoundation(c.address(), 2);
+ std::string foundation2 = c.foundation();
+ EXPECT_NE(foundation1, foundation2);
+
+ // Provide a different base address.
+ address.SetIP("100.100.100.1");
+ c.ComputeFoundation(address, 1); // Same tiebreaker as for foundation1.
+ foundation2 = c.foundation();
+ EXPECT_NE(foundation1, foundation2);
+
+ // Consistency check (just in case the algorithm ever changes to random!).
+ c.ComputeFoundation(c.address(), 1);
+ foundation2 = c.foundation();
+ EXPECT_EQ(foundation1, foundation2);
+
+ // Changing the protocol should affect the foundation.
+ auto prev_protocol = c.protocol();
+ c.set_protocol("tcp");
+ ASSERT_NE(prev_protocol, c.protocol());
+ c.ComputeFoundation(c.address(), 1);
+ EXPECT_NE(foundation1, c.foundation());
+ c.set_protocol(prev_protocol);
+
+ // Changing the relay protocol should affect the foundation.
+ prev_protocol = c.relay_protocol();
+ c.set_relay_protocol("tcp");
+ ASSERT_NE(prev_protocol, c.relay_protocol());
+ c.ComputeFoundation(c.address(), 1);
+ EXPECT_NE(foundation1, c.foundation());
+}
+
} // namespace cricket