Skip to content

Commit

Permalink
Single port handling via UDPMux
Browse files Browse the repository at this point in the history
Allows for ICE to handle connections on a single UDP port
  • Loading branch information
davidzhao committed Apr 13, 2021
1 parent 6e44037 commit 86d69d6
Show file tree
Hide file tree
Showing 12 changed files with 627 additions and 9 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu
* [Assad Obaid](https://github.com/assadobaid)
* [Antoine Baché](https://github.com/Antonito)
* [Will Forcey](https://github.com/wawesomeNOGUI)
* [David Zhao](https://github.com/davidzhao)

### License
MIT License - see [LICENSE](LICENSE) for full text
5 changes: 5 additions & 0 deletions agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ type Agent struct {

net *vnet.Net
tcpMux TCPMux
udpMux UDPMux

interfaceFilter func(string) bool

Expand Down Expand Up @@ -314,6 +315,7 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit
if a.tcpMux == nil {
a.tcpMux = newInvalidTCPMux()
}
a.udpMux = config.UDPMux

if a.net == nil {
a.net = vnet.NewNet(nil)
Expand Down Expand Up @@ -897,6 +899,9 @@ func (a *Agent) Close() error {
a.err.Store(ErrClosed)

a.tcpMux.RemoveConnByUfrag(a.localUfrag)
if a.udpMux != nil {
a.udpMux.RemoveConnByUfrag(a.localUfrag)
}

close(a.done)

Expand Down
5 changes: 5 additions & 0 deletions agent_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ type AgentConfig struct {
// experimental and the API might change in the future.
TCPMux TCPMux

// UDPMux is used for multiplexing multiple incoming UDP connections on a single port
// when this is set, the agent ignores PortMin and PortMax configurations and will
// defer to UDPMux for incoming connections
UDPMux UDPMux

// 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
Expand Down
67 changes: 67 additions & 0 deletions agent_udpmux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// +build !js

package ice

import (
"testing"

"github.com/pion/logging"
"github.com/pion/transport/test"
"github.com/stretchr/testify/require"
)

// TestMuxAgent is an end to end test over UDP mux, ensuring two agents could connect over mux
func TestMuxAgent(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
loggerFactory := logging.NewDefaultLoggerFactory()

udpMux := NewUDPMuxDefault(UDPMuxParams{
Logger: loggerFactory.NewLogger("ice"),
ReadBufferSize: 20,
})
muxPort := 7686
require.NoError(t, udpMux.Start(muxPort))

muxedA, err := NewAgent(&AgentConfig{
UDPMux: udpMux,
CandidateTypes: []CandidateType{CandidateTypeHost},
NetworkTypes: supportedNetworkTypes(),
})
require.NoError(t, err)

a, err := NewAgent(&AgentConfig{
CandidateTypes: []CandidateType{CandidateTypeHost},
NetworkTypes: supportedNetworkTypes(),
})
require.NoError(t, err)

conn, muxedConn := connect(a, muxedA)

pair := muxedA.getSelectedPair()
require.NotNil(t, pair)
require.Equal(t, muxPort, pair.Local.Port())

// send a packet to Mux
data := []byte("hello world")
_, err = conn.Write(data)
require.NoError(t, err)

buffer := make([]byte, 1024)
n, err := muxedConn.Read(buffer)
require.NoError(t, err)
require.Equal(t, data, buffer[:n])

// send a packet from Mux
_, err = muxedConn.Write(data)
require.NoError(t, err)

n, err = conn.Read(buffer)
require.NoError(t, err)
require.Equal(t, data, buffer[:n])

// close it down
require.NoError(t, conn.Close())
require.NoError(t, muxedConn.Close())
require.NoError(t, udpMux.Close())
}
3 changes: 3 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ var (
// ErrTCPRemoteAddrAlreadyExists indicates we already have the connection with same remote addr.
ErrTCPRemoteAddrAlreadyExists = errors.New("conn with same remote addr already exists")

// ErrMuxNotStarted indicates the Mux has not been started prior to use
ErrMuxNotStarted = errors.New("mux must be started first")

errSendPacket = errors.New("failed to send packet")
errAttributeTooShortICECandidate = errors.New("attribute not long enough to be ICE candidate")
errParseComponent = errors.New("could not parse component")
Expand Down
17 changes: 12 additions & 5 deletions gather.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ
switch network {
case tcp:
// Handle ICE TCP passive mode

a.log.Debugf("GetConn by ufrag: %s\n", a.localUfrag)
conn, err = a.tcpMux.GetConnByUfrag(a.localUfrag)
if err != nil {
Expand All @@ -178,10 +177,18 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ
// is there a way to verify that the listen address is even
// accessible from the current interface.
case udp:
conn, err = listenUDPInPortRange(a.net, a.log, int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: ip, Port: 0})
if err != nil {
a.log.Warnf("could not listen %s %s\n", network, ip)
continue
if a.udpMux != nil {
conn, err = a.udpMux.GetConnByUfrag(a.localUfrag)
if err != nil {
a.log.Warnf("could not get udp muxed connection: %v\n", err)
continue
}
} else {
conn, err = listenUDPInPortRange(a.net, a.log, int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: ip, Port: 0})
if err != nil {
a.log.Warnf("could not listen %s %s\n", network, ip)
continue
}
}

port = conn.LocalAddr().(*net.UDPAddr).Port
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.13

require (
github.com/google/uuid v1.2.0
github.com/kr/pretty v0.1.0 // indirect
github.com/pion/dtls/v2 v2.0.9
github.com/pion/logging v0.2.2
github.com/pion/mdns v0.0.5
Expand All @@ -13,4 +14,5 @@ require (
github.com/pion/turn/v2 v2.0.5
github.com/stretchr/testify v1.7.0
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)
8 changes: 7 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pion/dtls/v2 v2.0.9 h1:7Ow+V++YSZQMYzggI0P9vLJz/hUFcffsfGMfT/Qy+u8=
github.com/pion/dtls/v2 v2.0.9/go.mod h1:O0Wr7si/Zj5/EBFlDzDd6UtVxx25CE1r7XM7BQKYQho=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
Expand Down Expand Up @@ -50,7 +55,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
5 changes: 2 additions & 3 deletions tcp_mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ type TCPMux interface {
RemoveConnByUfrag(ufrag string)
}

// invalidTCPMux is an implementation of TCPMux that always returns ErroTCPMuxNotInitialized.
type invalidTCPMux struct {
}
// invalidTCPMux is an implementation of TCPMux that always returns ErrTCPMuxNotInitialized.
type invalidTCPMux struct{}

func newInvalidTCPMux() *invalidTCPMux {
return &invalidTCPMux{}
Expand Down
Loading

0 comments on commit 86d69d6

Please sign in to comment.