Accept aggressive nomination

Add config for accept aggressive nomination, it is useful for
compatible with chrome or other clients use aggressive nominate
to update nominate candidate pairs.
This commit is contained in:
cnderrauber
2022-04-21 15:18:01 +08:00
committed by cnderrauber
parent 1bbdb40185
commit 18f7a214aa
5 changed files with 136 additions and 1 deletions
+1
View File
@@ -14,6 +14,7 @@ backkem <mail@backkem.me>
buptczq <czq@qq.com>
cgojin <gongjin21@hotmail.com>
Chao Yuan <yuanchao0310@163.com>
cnderrauber <zengjie9004@gmail.com>
David Hamilton <davidhamiltron@gmail.com>
David Zhao <david@davidzhao.com>
Henry <cryptix@riseup.net>
+4
View File
@@ -133,6 +133,8 @@ type Agent struct {
insecureSkipVerify bool
proxyDialer proxy.Dialer
acceptAggressiveNomination bool
}
type task struct {
@@ -314,6 +316,8 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit
interfaceFilter: config.InterfaceFilter,
insecureSkipVerify: config.InsecureSkipVerify,
acceptAggressiveNomination: config.AcceptAggressiveNomination,
}
a.tcpMux = config.TCPMux
+3
View File
@@ -159,6 +159,9 @@ type AgentConfig struct {
// Proxy Dialer is a dialer that should be implemented by the user based on golang.org/x/net/proxy
// dial interface in order to support corporate proxies
ProxyDialer proxy.Dialer
// Accept aggressive nomination in RFC 5245 for compatible with chrome and other browsers
AcceptAggressiveNomination bool
}
// initWithDefaults populates an agent and falls back to defaults if fields are unset
+124
View File
@@ -1,3 +1,4 @@
//go:build !js
// +build !js
package ice
@@ -1763,3 +1764,126 @@ func TestGetSelectedCandidatePair(t *testing.T) {
assert.NoError(t, aAgent.Close())
assert.NoError(t, bAgent.Close())
}
func TestAcceptAggressiveNomination(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
lim := test.TimeOut(time.Second * 30)
defer lim.Stop()
// Create a network with two interfaces
wan, err := vnet.NewRouter(&vnet.RouterConfig{
CIDR: "0.0.0.0/0",
LoggerFactory: logging.NewDefaultLoggerFactory(),
})
assert.NoError(t, err)
net0 := vnet.NewNet(&vnet.NetConfig{
StaticIPs: []string{"192.168.0.1"},
})
assert.NoError(t, wan.AddNet(net0))
net1 := vnet.NewNet(&vnet.NetConfig{
StaticIPs: []string{"192.168.0.2", "192.168.0.3", "192.168.0.4"},
})
assert.NoError(t, wan.AddNet(net1))
assert.NoError(t, wan.Start())
aNotifier, aConnected := onConnected()
bNotifier, bConnected := onConnected()
KeepaliveInterval := time.Hour
cfg0 := &AgentConfig{
NetworkTypes: supportedNetworkTypes(),
MulticastDNSMode: MulticastDNSModeDisabled,
Net: net0,
KeepaliveInterval: &KeepaliveInterval,
CheckInterval: &KeepaliveInterval,
AcceptAggressiveNomination: true,
}
var aAgent, bAgent *Agent
aAgent, err = NewAgent(cfg0)
require.NoError(t, err)
require.NoError(t, aAgent.OnConnectionStateChange(aNotifier))
cfg1 := &AgentConfig{
NetworkTypes: supportedNetworkTypes(),
MulticastDNSMode: MulticastDNSModeDisabled,
Net: net1,
KeepaliveInterval: &KeepaliveInterval,
CheckInterval: &KeepaliveInterval,
}
bAgent, err = NewAgent(cfg1)
require.NoError(t, err)
require.NoError(t, bAgent.OnConnectionStateChange(bNotifier))
aConn, bConn := connect(aAgent, bAgent)
// Ensure pair selected
// Note: this assumes ConnectionStateConnected is thrown after selecting the final pair
<-aConnected
<-bConnected
// Send new USE-CANDIDATE message with higher priority to update the selected pair
buildMsg := func(class stun.MessageClass, username, key string, priority uint32) *stun.Message {
msg, err1 := stun.Build(stun.NewType(stun.MethodBinding, class), stun.TransactionID,
stun.NewUsername(username),
stun.NewShortTermIntegrity(key),
UseCandidate(),
PriorityAttr(priority),
stun.Fingerprint,
)
if err1 != nil {
t.Fatal(err1)
}
return msg
}
selectedCh := make(chan Candidate, 1)
var expectNewSelectedCandidate Candidate
err = aAgent.OnSelectedCandidatePairChange(func(_, remote Candidate) {
selectedCh <- remote
})
require.NoError(t, err)
var bcandidates []Candidate
bcandidates, err = bAgent.GetLocalCandidates()
require.NoError(t, err)
for _, c := range bcandidates {
if c != bAgent.getSelectedPair().Local {
if expectNewSelectedCandidate == nil {
incr_priority:
for _, candidates := range aAgent.remoteCandidates {
for _, candidate := range candidates {
if candidate.Equal(c) {
candidate.(*CandidateHost).priorityOverride += 1000
break incr_priority
}
}
}
expectNewSelectedCandidate = c
}
_, err = c.writeTo(buildMsg(stun.ClassRequest, aAgent.localUfrag+":"+aAgent.remoteUfrag, aAgent.localPwd, c.Priority()).Raw, bAgent.getSelectedPair().Remote)
require.NoError(t, err)
}
}
time.Sleep(1 * time.Second)
select {
case selected := <-selectedCh:
assert.True(t, selected.Equal(expectNewSelectedCandidate))
default:
t.Fatal("No selected candidate pair")
}
assert.NoError(t, wan.Stop())
if !closePipe(t, aConn, bConn) {
return
}
}
+4 -1
View File
@@ -252,8 +252,11 @@ func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote
// previously sent by this pair produced a successful response and
// generated a valid pair (Section 7.2.5.3.2). The agent sets the
// nominated flag value of the valid pair to true.
if selectedPair := s.agent.getSelectedPair(); selectedPair == nil {
if selectedPair := s.agent.getSelectedPair(); selectedPair == nil ||
(s.agent.acceptAggressiveNomination && selectedPair.priority() < p.priority()) {
s.agent.setSelectedPair(p)
} else if selectedPair != p {
s.log.Tracef("ignore nominate new pair %s, already nominated pair %s", p, selectedPair)
}
} else {
// If the received Binding request triggered a new check to be