Skip to content

Commit

Permalink
calling: update TurnCallRouter to reduce returned options
Browse files Browse the repository at this point in the history
  • Loading branch information
adel-signal committed Mar 5, 2024
1 parent 84c6731 commit 8f100a7
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.whispersystems.textsecuregcm.util.Util;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.*;
import java.util.function.Supplier;
Expand Down Expand Up @@ -47,7 +48,9 @@ public TurnCallRouter(
}

/**
* Gets Turn Instance addresses. Returns both the IPv4 and IPv6 addresses. Prioritizes V4 connections.
* Gets Turn Instance addresses. Returns both the IPv4 and IPv6 addresses. Prefers to match the IP protocol of the
* client address in datacenter selection. Returns 2 instance options of the preferred protocol for every one instance
* of the other.
* @param aci aci of client
* @param clientAddress IP address to base routing on
* @param instanceLimit max instances to return options for
Expand Down Expand Up @@ -110,29 +113,42 @@ TurnServerOptions getRoutingForInner(
subdivision
);
}
List<String> urlsWithIps = getUrlsForInstances(selectInstances(datacenters, instanceLimit));
return new TurnServerOptions(hostname, urlsWithIps, this.configTurnRouter.randomUrls());

List<String> urlsWithIps = getUrlsForInstances(
selectInstances(
datacenters,
instanceLimit,
(clientAddress.get() instanceof Inet6Address)
));
return new TurnServerOptions(hostname, urlsWithIps, minimalRandomUrls());
}

// Includes only the udp options in the randomUrls
private List<String> minimalRandomUrls(){
return this.configTurnRouter.randomUrls().stream()
.filter(s -> s.startsWith("turn:") && !s.endsWith("transport=tcp"))
.toList();
}

private List<String> selectInstances(List<String> datacenters, int limit) {
private List<String> selectInstances(List<String> datacenters, int limit, boolean preferV6) {
if(datacenters.isEmpty() || limit == 0) {
return Collections.emptyList();
}
int numV6 = preferV6 ? (limit - limit / 3) : limit / 3;
int numV4 = limit - numV6;

CallDnsRecords dnsRecords = this.callDnsRecords.get();
List<InetAddress> ipv4Selection = datacenters.stream()
.flatMap(dc -> Util.randomNOfStable(dnsRecords.aByRegion().get(dc), 2).stream())
.flatMap(dc -> Util.randomNOfStable(dnsRecords.aByRegion().get(dc), limit).stream())
.toList();
List<InetAddress> ipv6Selection = datacenters.stream()
.flatMap(dc -> Util.randomNOfStable(dnsRecords.aaaaByRegion().get(dc), 2).stream())
.flatMap(dc -> Util.randomNOfStable(dnsRecords.aaaaByRegion().get(dc), limit).stream())
.toList();
if (ipv4Selection.size() < ipv6Selection.size()) {
ipv4Selection = ipv4Selection.stream().limit(limit / 2).toList();
ipv6Selection = ipv6Selection.stream().limit(limit - ipv4Selection.size()).toList();
} else {
ipv6Selection = ipv6Selection.stream().limit(limit / 2).toList();
ipv4Selection = ipv4Selection.stream().limit(limit - ipv6Selection.size()).toList();
}

// increase numV4 if not enough v6 options. vice-versa is also true
numV4 = Math.max(numV4, limit - ipv6Selection.size());
ipv4Selection = ipv4Selection.stream().limit(numV4).toList();
ipv6Selection = ipv6Selection.stream().limit(limit - ipv4Selection.size()).toList();

return Stream.concat(
ipv4Selection.stream().map(InetAddress::getHostAddress),
Expand All @@ -143,7 +159,6 @@ private List<String> selectInstances(List<String> datacenters, int limit) {

private static List<String> getUrlsForInstances(List<String> instanceIps) {
return instanceIps.stream().flatMap(ip -> Stream.of(
String.format("stun:%s", ip),
String.format("turn:%s", ip),
String.format("turn:%s:80?transport=tcp", ip),
String.format("turns:%s:443?transport=tcp", ip)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
@io.swagger.v3.oas.annotations.tags.Tag(name = "Calling")
public class CallRoutingController {

private static final int TURN_INSTANCE_LIMIT = 6;
private static final int TURN_INSTANCE_LIMIT = 3;
private static final Counter INVALID_IP_COUNTER = Metrics.counter(name(CallRoutingController.class, "invalidIP"));
private static final Logger log = LoggerFactory.getLogger(CallRoutingController.class);
private final RateLimiters rateLimiters;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ public class TurnCallRouterTest {

private final static String TEST_HOSTNAME = "subdomain.example.org";
private final static List<String> TEST_URLS_WITH_HOSTS = List.of(
"one.example.com",
"two.example.com",
"three.example.com"
"stun:one.example.com",
"turn:two.example.com",
"turn:three.example.com?transport=tcp"
);
private final static List<String> EXPECTED_TEST_URLS_WITH_HOSTS = List.of(
"turn:two.example.com"
);

private CallRoutingTable performanceTable;
Expand Down Expand Up @@ -113,7 +116,7 @@ TurnServerOptions optionsWithUrls(List<String> urls) {
return new TurnServerOptions(
TEST_HOSTNAME,
urls,
TEST_URLS_WITH_HOSTS
EXPECTED_TEST_URLS_WITH_HOSTS
);
}

Expand All @@ -140,7 +143,11 @@ public void testRandomizes() throws UnknownHostException {
.thenReturn(true);

assertThat(router().getRoutingFor(aci, Optional.of(InetAddress.getByName("0.0.0.1")), 10))
.isEqualTo(optionsWithUrls(null));
.isEqualTo(new TurnServerOptions(
TEST_HOSTNAME,
null,
TEST_URLS_WITH_HOSTS
));
}

@Test
Expand All @@ -150,32 +157,26 @@ public void testOrderedByPerformance() throws UnknownHostException {

assertThat(router().getRoutingFor(aci, Optional.of(InetAddress.getByName("0.0.0.1")), 10))
.isEqualTo(optionsWithUrls(List.of(
"stun:9.9.9.3",
"turn:9.9.9.3",
"turn:9.9.9.3:80?transport=tcp",
"turns:9.9.9.3:443?transport=tcp",

"stun:9.9.9.1",
"turn:9.9.9.1",
"turn:9.9.9.1:80?transport=tcp",
"turns:9.9.9.1:443?transport=tcp",

"stun:9.9.9.2",
"turn:9.9.9.2",
"turn:9.9.9.2:80?transport=tcp",
"turns:9.9.9.2:443?transport=tcp",

"stun:[2222:1111:0:abc2:0:0:0:0]",
"turn:[2222:1111:0:abc2:0:0:0:0]",
"turn:[2222:1111:0:abc2:0:0:0:0]:80?transport=tcp",
"turns:[2222:1111:0:abc2:0:0:0:0]:443?transport=tcp",

"stun:[2222:1111:0:abc0:0:0:0:0]",
"turn:[2222:1111:0:abc0:0:0:0:0]",
"turn:[2222:1111:0:abc0:0:0:0:0]:80?transport=tcp",
"turns:[2222:1111:0:abc0:0:0:0:0]:443?transport=tcp",

"stun:[2222:1111:0:abc1:0:0:0:0]",
"turn:[2222:1111:0:abc1:0:0:0:0]",
"turn:[2222:1111:0:abc1:0:0:0:0]:80?transport=tcp",
"turns:[2222:1111:0:abc1:0:0:0:0]:443?transport=tcp"
Expand All @@ -191,54 +192,49 @@ public void testPrioritizesManualRecords() throws UnknownHostException {

assertThat(router().getRoutingFor(aci, Optional.of(InetAddress.getByName("0.0.0.1")), 10))
.isEqualTo(optionsWithUrls(List.of(
"stun:1.1.1.1",
"turn:1.1.1.1",
"turn:1.1.1.1:80?transport=tcp",
"turns:1.1.1.1:443?transport=tcp",

"stun:[2222:1111:0:dead:0:0:0:0]",
"turn:[2222:1111:0:dead:0:0:0:0]",
"turn:[2222:1111:0:dead:0:0:0:0]:80?transport=tcp",
"turns:[2222:1111:0:dead:0:0:0:0]:443?transport=tcp"
)));
}

@Test
public void testLimitReturnsHalfIpv4AndPrioritizesPerformance() throws UnknownHostException {
public void testLimitReturnsPreferredProtocolAndPrioritizesPerformance() throws UnknownHostException {
when(performanceTable.getDatacentersFor(any(), any(), any(), any()))
.thenReturn(List.of("dc-performance3", "dc-performance2", "dc-performance1"));

assertThat(router().getRoutingFor(aci, Optional.of(InetAddress.getByName("0.0.0.1")), 6))
assertThat(router().getRoutingFor(aci, Optional.of(InetAddress.getByName("0.0.0.1")), 3))
.isEqualTo(optionsWithUrls(List.of(
"stun:9.9.9.4",
"turn:9.9.9.4",
"turn:9.9.9.4:80?transport=tcp",
"turns:9.9.9.4:443?transport=tcp",

"stun:9.9.9.3",
"turn:9.9.9.3",
"turn:9.9.9.3:80?transport=tcp",
"turns:9.9.9.3:443?transport=tcp",

"stun:9.9.9.1",
"turn:9.9.9.1",
"turn:9.9.9.1:80?transport=tcp",
"turns:9.9.9.1:443?transport=tcp",
"turn:[2222:1111:0:abc3:0:0:0:0]",
"turn:[2222:1111:0:abc3:0:0:0:0]:80?transport=tcp",
"turns:[2222:1111:0:abc3:0:0:0:0]:443?transport=tcp"
)));

assertThat(router().getRoutingFor(aci, Optional.of(InetAddress.getByName("2222:1111:0:abc2:0:0:0:1")), 3))
.isEqualTo(optionsWithUrls(List.of(
"turn:9.9.9.4",
"turn:9.9.9.4:80?transport=tcp",
"turns:9.9.9.4:443?transport=tcp",

"stun:[2222:1111:0:abc3:0:0:0:0]",
"turn:[2222:1111:0:abc3:0:0:0:0]",
"turn:[2222:1111:0:abc3:0:0:0:0]:80?transport=tcp",
"turns:[2222:1111:0:abc3:0:0:0:0]:443?transport=tcp",

"stun:[2222:1111:0:abc2:0:0:0:0]",
"turn:[2222:1111:0:abc2:0:0:0:0]",
"turn:[2222:1111:0:abc2:0:0:0:0]:80?transport=tcp",
"turns:[2222:1111:0:abc2:0:0:0:0]:443?transport=tcp",

"stun:[2222:1111:0:abc0:0:0:0:0]",
"turn:[2222:1111:0:abc0:0:0:0:0]",
"turn:[2222:1111:0:abc0:0:0:0:0]:80?transport=tcp",
"turns:[2222:1111:0:abc0:0:0:0:0]:443?transport=tcp"
"turns:[2222:1111:0:abc2:0:0:0:0]:443?transport=tcp"
)));
}

Expand Down

0 comments on commit 8f100a7

Please sign in to comment.