Skip to content

Commit 5b2e30d

Browse files
committed
Implement TCP candidate priority with offsets
By default it is UDP one minus 27 (except relay). It is can be configured using AgentConfig.
1 parent 656a328 commit 5b2e30d

6 files changed

+81
-23
lines changed

agent.go

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ type Agent struct {
7373
prflxAcceptanceMinWait time.Duration
7474
relayAcceptanceMinWait time.Duration
7575

76+
tcpPriorityOffset uint16
77+
7678
portMin uint16
7779
portMax uint16
7880

agent_config.go

+16
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ const (
4141
// defaultMaxBindingRequests is the maximum number of binding requests before considering a pair failed
4242
defaultMaxBindingRequests = 7
4343

44+
// TCPPriorityOffset is a number which is subtracted from the default (UDP) candidate type preference
45+
// for host, srflx and prfx candidate types.
46+
defaultTCPPriorityOffset = 27
47+
4448
// maxBufferSize is the number of bytes that can be buffered before we start to error
4549
maxBufferSize = 1000 * 1000 // 1MB
4650

@@ -177,6 +181,12 @@ type AgentConfig struct {
177181

178182
// Include loopback addresses in the candidate list.
179183
IncludeLoopback bool
184+
185+
// TCPPriorityOffset is a number which is subtracted from the default (UDP) candidate type preference
186+
// for host, srflx and prfx candidate types. It helps to configure relative preference of UDP candidates
187+
// against TCP ones. Relay candidates for TCP and UDP are always 0 and not affected by this setting.
188+
// When this is nil, defaultTCPPriorityOffset is used.
189+
TCPPriorityOffset *uint16
180190
}
181191

182192
// initWithDefaults populates an agent and falls back to defaults if fields are unset
@@ -211,6 +221,12 @@ func (config *AgentConfig) initWithDefaults(a *Agent) {
211221
a.relayAcceptanceMinWait = *config.RelayAcceptanceMinWait
212222
}
213223

224+
if config.TCPPriorityOffset == nil {
225+
a.tcpPriorityOffset = defaultTCPPriorityOffset
226+
} else {
227+
a.tcpPriorityOffset = *config.TCPPriorityOffset
228+
}
229+
214230
if config.DisconnectedTimeout == nil {
215231
a.disconnectedTimeout = defaultDisconnectedTimeout
216232
} else {

candidate_base.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,13 @@ func (c *candidateBase) Priority() uint32 {
355355
// candidates for a particular component for a particular data stream
356356
// that have the same type, the local preference MUST be unique for each
357357
// one.
358-
return (1<<24)*uint32(c.Type().Preference(c.networkType)) +
358+
359+
var tcpPriorityOffset uint16 = defaultTCPPriorityOffset
360+
if c.agent() != nil {
361+
tcpPriorityOffset = c.agent().tcpPriorityOffset
362+
}
363+
364+
return (1<<24)*uint32(c.Type().Preference(c.networkType, tcpPriorityOffset)) +
359365
(1<<8)*uint32(c.LocalPreference()) +
360366
uint32(256-c.Component())
361367
}

candidate_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func TestCandidatePriority(t *testing.T) {
7878
tcpType: TCPTypeSimultaneousOpen,
7979
},
8080
},
81-
WantPriority: 1658847231,
81+
WantPriority: 1407188991,
8282
},
8383
{
8484
Candidate: &CandidatePeerReflexive{
@@ -89,7 +89,7 @@ func TestCandidatePriority(t *testing.T) {
8989
tcpType: TCPTypeActive,
9090
},
9191
},
92-
WantPriority: 1654652927,
92+
WantPriority: 1402994687,
9393
},
9494
{
9595
Candidate: &CandidatePeerReflexive{
@@ -100,7 +100,7 @@ func TestCandidatePriority(t *testing.T) {
100100
tcpType: TCPTypePassive,
101101
},
102102
},
103-
WantPriority: 1650458623,
103+
WantPriority: 1398800383,
104104
},
105105
{
106106
Candidate: &CandidateServerReflexive{

candidatetype.go

+11-19
Original file line numberDiff line numberDiff line change
@@ -38,32 +38,24 @@ func (c CandidateType) String() string {
3838
// The RECOMMENDED values are 126 for host candidates, 100
3939
// for server reflexive candidates, 110 for peer reflexive candidates,
4040
// and 0 for relayed candidates.
41-
func (c CandidateType) Preference(networkType NetworkType) uint16 {
42-
if networkType == NetworkTypeTCP4 || networkType == NetworkTypeTCP6 {
43-
switch c {
44-
case CandidateTypeHost:
45-
return 99
46-
case CandidateTypePeerReflexive:
47-
return 98
48-
case CandidateTypeServerReflexive:
49-
return 97
50-
case CandidateTypeRelay, CandidateTypeUnspecified:
51-
return 0
52-
}
53-
}
54-
55-
// UDP candidates
41+
func (c CandidateType) Preference(networkType NetworkType, tcpOffset uint16) uint16 {
42+
var result uint16
5643
switch c {
5744
case CandidateTypeHost:
58-
return 126
45+
result = 126
5946
case CandidateTypePeerReflexive:
60-
return 110
47+
result = 110
6148
case CandidateTypeServerReflexive:
62-
return 100
49+
result = 100
6350
case CandidateTypeRelay, CandidateTypeUnspecified:
6451
return 0
52+
default:
53+
return 0
54+
}
55+
if networkType.IsTCP() {
56+
return result - tcpOffset
6557
}
66-
return 0
58+
return result
6759
}
6860

6961
func containsCandidateType(candidateType CandidateType, candidateTypeList []CandidateType) bool {

candidatetype_test.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
2+
// SPDX-License-Identifier: MIT
3+
4+
package ice
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestCandidateTypePreference(t *testing.T) {
13+
r := require.New(t)
14+
15+
hostDefaultPreference := uint16(126)
16+
prflxDefaultPreference := uint16(110)
17+
srflxDefaultPreference := uint16(100)
18+
relayDefaultPreference := uint16(0)
19+
unspecifiedDefaultPreference := uint16(0)
20+
21+
tcpOffsets := []uint16{0, 10}
22+
23+
for _, tcpOffset := range tcpOffsets {
24+
for _, networkType := range supportedNetworkTypes() {
25+
if networkType.IsTCP() {
26+
r.Equal(hostDefaultPreference-tcpOffset, CandidateTypeHost.Preference(networkType, tcpOffset))
27+
r.Equal(prflxDefaultPreference-tcpOffset, CandidateTypePeerReflexive.Preference(networkType, tcpOffset))
28+
r.Equal(srflxDefaultPreference-tcpOffset, CandidateTypeServerReflexive.Preference(networkType, tcpOffset))
29+
} else {
30+
r.Equal(hostDefaultPreference, CandidateTypeHost.Preference(networkType, tcpOffset))
31+
r.Equal(prflxDefaultPreference, CandidateTypePeerReflexive.Preference(networkType, tcpOffset))
32+
r.Equal(srflxDefaultPreference, CandidateTypeServerReflexive.Preference(networkType, tcpOffset))
33+
}
34+
}
35+
}
36+
for _, tcpOffset := range tcpOffsets {
37+
for _, networkType := range supportedNetworkTypes() {
38+
r.Equal(relayDefaultPreference, CandidateTypeRelay.Preference(networkType, tcpOffset))
39+
r.Equal(unspecifiedDefaultPreference, CandidateTypeUnspecified.Preference(networkType, tcpOffset))
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)