mirror of
https://github.com/netbirdio/ice.git
synced 2026-05-22 17:10:58 -07:00
Move localInterfaces and listenUDPInPortRange
Move more code out of the agent that doesn't actually require any state from the ICE Agent. Relates to #118
This commit is contained in:
@@ -2,12 +2,10 @@ package ice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/transport/vnet"
|
||||
"github.com/pion/turn/v2"
|
||||
)
|
||||
|
||||
@@ -15,106 +13,6 @@ const (
|
||||
stunGatherTimeout = time.Second * 5
|
||||
)
|
||||
|
||||
func (a *Agent) localInterfaces(networkTypes []NetworkType) ([]net.IP, error) {
|
||||
ips := []net.IP{}
|
||||
ifaces, err := a.net.Interfaces()
|
||||
if err != nil {
|
||||
return ips, err
|
||||
}
|
||||
|
||||
var IPv4Requested, IPv6Requested bool
|
||||
for _, typ := range networkTypes {
|
||||
if typ.IsIPv4() {
|
||||
IPv4Requested = true
|
||||
}
|
||||
|
||||
if typ.IsIPv6() {
|
||||
IPv6Requested = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue // interface down
|
||||
}
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
continue // loopback interface
|
||||
}
|
||||
|
||||
if a.interfaceFilter != nil && !a.interfaceFilter(iface.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch addr := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = addr.IP
|
||||
case *net.IPAddr:
|
||||
ip = addr.IP
|
||||
}
|
||||
if ip == nil || ip.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
|
||||
if ipv4 := ip.To4(); ipv4 == nil {
|
||||
if !IPv6Requested {
|
||||
continue
|
||||
} else if !isSupportedIPv6(ip) {
|
||||
continue
|
||||
}
|
||||
} else if !IPv4Requested {
|
||||
continue
|
||||
}
|
||||
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func (a *Agent) listenUDP(portMax, portMin int, network string, laddr *net.UDPAddr) (vnet.UDPPacketConn, error) {
|
||||
if (laddr.Port != 0) || ((portMin == 0) && (portMax == 0)) {
|
||||
return a.net.ListenUDP(network, laddr)
|
||||
}
|
||||
var i, j int
|
||||
i = portMin
|
||||
if i == 0 {
|
||||
i = 1
|
||||
}
|
||||
j = portMax
|
||||
if j == 0 {
|
||||
j = 0xFFFF
|
||||
}
|
||||
if i > j {
|
||||
return nil, ErrPort
|
||||
}
|
||||
|
||||
portStart := rand.Intn(j-i+1) + i
|
||||
portCurrent := portStart
|
||||
for {
|
||||
laddr = &net.UDPAddr{IP: laddr.IP, Port: portCurrent}
|
||||
c, e := a.net.ListenUDP(network, laddr)
|
||||
if e == nil {
|
||||
return c, e
|
||||
}
|
||||
a.log.Debugf("failed to listen %s: %v", laddr.String(), e)
|
||||
portCurrent++
|
||||
if portCurrent > j {
|
||||
portCurrent = i
|
||||
}
|
||||
if portCurrent == portStart {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil, ErrPort
|
||||
}
|
||||
|
||||
// GatherCandidates initiates the trickle based gathering process.
|
||||
func (a *Agent) GatherCandidates() error {
|
||||
gatherErrChan := make(chan error, 1)
|
||||
@@ -183,7 +81,7 @@ func (a *Agent) gatherCandidatesLocal(networkTypes []NetworkType) {
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
localIPs, err := a.localInterfaces(networkTypes)
|
||||
localIPs, err := localInterfaces(a.net, a.interfaceFilter, networkTypes)
|
||||
if err != nil {
|
||||
a.log.Warnf("failed to iterate local interfaces, host candidates will not be gathered %s", err)
|
||||
return
|
||||
@@ -203,7 +101,7 @@ func (a *Agent) gatherCandidatesLocal(networkTypes []NetworkType) {
|
||||
for _, network := range supportedNetworks {
|
||||
go func(network string, ip, mappedIP net.IP) {
|
||||
defer wg.Done()
|
||||
conn, err := a.listenUDP(int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: ip, Port: 0})
|
||||
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)
|
||||
return
|
||||
@@ -274,7 +172,7 @@ func (a *Agent) gatherCandidatesSrflx(urls []*URL, networkTypes []NetworkType) {
|
||||
continue
|
||||
}
|
||||
|
||||
conn, err := a.listenUDP(int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: nil, Port: 0})
|
||||
conn, err := listenUDPInPortRange(a.net, a.log, int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: nil, Port: 0})
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to listen for %s: %v\n", serverAddr.String(), err)
|
||||
continue
|
||||
@@ -318,7 +216,7 @@ func (a *Agent) gatherCandidatesSrflx(urls []*URL, networkTypes []NetworkType) {
|
||||
}
|
||||
}
|
||||
} else if a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeServerReflexive {
|
||||
conn, err := a.listenUDP(int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: nil, Port: 0})
|
||||
conn, err := listenUDPInPortRange(a.net, a.log, int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: nil, Port: 0})
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to listen %s: %v\n", network, err)
|
||||
continue
|
||||
|
||||
+6
-6
@@ -16,20 +16,20 @@ func TestListenUDP(t *testing.T) {
|
||||
a, err := NewAgent(&AgentConfig{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
localIPs, err := a.localInterfaces([]NetworkType{NetworkTypeUDP4})
|
||||
localIPs, err := localInterfaces(a.net, a.interfaceFilter, []NetworkType{NetworkTypeUDP4})
|
||||
assert.NotEqual(t, len(localIPs), 0, "localInterfaces found no interfaces, unable to test")
|
||||
assert.NoError(t, err)
|
||||
|
||||
ip := localIPs[0]
|
||||
|
||||
conn, err := a.listenUDP(0, 0, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
conn, err := listenUDPInPortRange(a.net, a.log, 0, 0, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
assert.NoError(t, err, "listenUDP error with no port restriction")
|
||||
assert.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn")
|
||||
|
||||
_, err = a.listenUDP(4999, 5000, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
_, err = listenUDPInPortRange(a.net, a.log, 4999, 5000, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
assert.Equal(t, err, ErrPort, "listenUDP with invalid port range did not return ErrPort")
|
||||
|
||||
conn, err = a.listenUDP(5000, 5000, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
conn, err = listenUDPInPortRange(a.net, a.log, 5000, 5000, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
assert.NoError(t, err, "listenUDP error with no port restriction")
|
||||
assert.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn")
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestListenUDP(t *testing.T) {
|
||||
result := make([]int, 0, total)
|
||||
portRange := make([]int, 0, total)
|
||||
for i := 0; i < total; i++ {
|
||||
conn, err = a.listenUDP(portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
conn, err = listenUDPInPortRange(a.net, a.log, portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
assert.NoError(t, err, "listenUDP error with no port restriction")
|
||||
assert.NotNil(t, conn, "listenUDP error with no port restriction return a nil conn")
|
||||
|
||||
@@ -65,7 +65,7 @@ func TestListenUDP(t *testing.T) {
|
||||
if !reflect.DeepEqual(result, portRange) {
|
||||
t.Fatalf("listenUDP with port restriction [%d, %d], got:%v, want:%v", portMin, portMax, result, portRange)
|
||||
}
|
||||
_, err = a.listenUDP(portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
_, err = listenUDPInPortRange(a.net, a.log, portMax, portMin, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
assert.Equal(t, err, ErrPort, "listenUDP with port restriction [%d, %d], did not return ErrPort", portMin, portMax)
|
||||
|
||||
assert.NoError(t, a.Close())
|
||||
|
||||
+8
-8
@@ -26,7 +26,7 @@ func TestVNetGather(t *testing.T) {
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
localIPs, err := a.localInterfaces([]NetworkType{NetworkTypeUDP4})
|
||||
localIPs, err := localInterfaces(a.net, a.interfaceFilter, []NetworkType{NetworkTypeUDP4})
|
||||
if len(localIPs) > 0 {
|
||||
t.Fatal("should return no local IP")
|
||||
} else if err != nil {
|
||||
@@ -66,7 +66,7 @@ func TestVNetGather(t *testing.T) {
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
localIPs, err := a.localInterfaces([]NetworkType{NetworkTypeUDP4})
|
||||
localIPs, err := localInterfaces(a.net, a.interfaceFilter, []NetworkType{NetworkTypeUDP4})
|
||||
if len(localIPs) == 0 {
|
||||
t.Fatal("should have one local IP")
|
||||
} else if err != nil {
|
||||
@@ -109,7 +109,7 @@ func TestVNetGather(t *testing.T) {
|
||||
t.Fatalf("Failed to create agent: %s", err)
|
||||
}
|
||||
|
||||
localIPs, err := a.localInterfaces([]NetworkType{NetworkTypeUDP4})
|
||||
localIPs, err := localInterfaces(a.net, a.interfaceFilter, []NetworkType{NetworkTypeUDP4})
|
||||
if len(localIPs) == 0 {
|
||||
t.Fatal("localInterfaces found no interfaces, unable to test")
|
||||
} else if err != nil {
|
||||
@@ -118,7 +118,7 @@ func TestVNetGather(t *testing.T) {
|
||||
|
||||
ip := localIPs[0]
|
||||
|
||||
conn, err := a.listenUDP(0, 0, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
conn, err := listenUDPInPortRange(a.net, a.log, 0, 0, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
if err != nil {
|
||||
t.Fatalf("listenUDP error with no port restriction %v", err)
|
||||
} else if conn == nil {
|
||||
@@ -129,12 +129,12 @@ func TestVNetGather(t *testing.T) {
|
||||
t.Fatalf("failed to close conn")
|
||||
}
|
||||
|
||||
_, err = a.listenUDP(4999, 5000, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
_, err = listenUDPInPortRange(a.net, a.log, 4999, 5000, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
if err != ErrPort {
|
||||
t.Fatal("listenUDP with invalid port range did not return ErrPort")
|
||||
}
|
||||
|
||||
conn, err = a.listenUDP(5000, 5000, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
conn, err = listenUDPInPortRange(a.net, a.log, 5000, 5000, udp, &net.UDPAddr{IP: ip, Port: 0})
|
||||
if err != nil {
|
||||
t.Fatalf("listenUDP error with no port restriction %v", err)
|
||||
} else if conn == nil {
|
||||
@@ -384,7 +384,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) {
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
localIPs, err := a.localInterfaces([]NetworkType{NetworkTypeUDP4})
|
||||
localIPs, err := localInterfaces(a.net, a.interfaceFilter, []NetworkType{NetworkTypeUDP4})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(localIPs) != 0 {
|
||||
@@ -404,7 +404,7 @@ func TestVNetGatherWithInterfaceFilter(t *testing.T) {
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
localIPs, err := a.localInterfaces([]NetworkType{NetworkTypeUDP4})
|
||||
localIPs, err := localInterfaces(a.net, a.interfaceFilter, []NetworkType{NetworkTypeUDP4})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(localIPs) == 0 {
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/stun"
|
||||
"github.com/pion/transport/vnet"
|
||||
)
|
||||
|
||||
type atomicError struct{ v atomic.Value }
|
||||
@@ -144,3 +146,103 @@ func stunRequest(read func([]byte) (int, error), write func([]byte) (int, error)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func localInterfaces(vnet *vnet.Net, interfaceFilter func(string) bool, networkTypes []NetworkType) ([]net.IP, error) {
|
||||
ips := []net.IP{}
|
||||
ifaces, err := vnet.Interfaces()
|
||||
if err != nil {
|
||||
return ips, err
|
||||
}
|
||||
|
||||
var IPv4Requested, IPv6Requested bool
|
||||
for _, typ := range networkTypes {
|
||||
if typ.IsIPv4() {
|
||||
IPv4Requested = true
|
||||
}
|
||||
|
||||
if typ.IsIPv6() {
|
||||
IPv6Requested = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue // interface down
|
||||
}
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
continue // loopback interface
|
||||
}
|
||||
|
||||
if interfaceFilter != nil && !interfaceFilter(iface.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch addr := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = addr.IP
|
||||
case *net.IPAddr:
|
||||
ip = addr.IP
|
||||
}
|
||||
if ip == nil || ip.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
|
||||
if ipv4 := ip.To4(); ipv4 == nil {
|
||||
if !IPv6Requested {
|
||||
continue
|
||||
} else if !isSupportedIPv6(ip) {
|
||||
continue
|
||||
}
|
||||
} else if !IPv4Requested {
|
||||
continue
|
||||
}
|
||||
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func listenUDPInPortRange(vnet *vnet.Net, log logging.LeveledLogger, portMax, portMin int, network string, laddr *net.UDPAddr) (vnet.UDPPacketConn, error) {
|
||||
if (laddr.Port != 0) || ((portMin == 0) && (portMax == 0)) {
|
||||
return vnet.ListenUDP(network, laddr)
|
||||
}
|
||||
var i, j int
|
||||
i = portMin
|
||||
if i == 0 {
|
||||
i = 1
|
||||
}
|
||||
j = portMax
|
||||
if j == 0 {
|
||||
j = 0xFFFF
|
||||
}
|
||||
if i > j {
|
||||
return nil, ErrPort
|
||||
}
|
||||
|
||||
portStart := rand.Intn(j-i+1) + i
|
||||
portCurrent := portStart
|
||||
for {
|
||||
laddr = &net.UDPAddr{IP: laddr.IP, Port: portCurrent}
|
||||
c, e := vnet.ListenUDP(network, laddr)
|
||||
if e == nil {
|
||||
return c, e
|
||||
}
|
||||
log.Debugf("failed to listen %s: %v", laddr.String(), e)
|
||||
portCurrent++
|
||||
if portCurrent > j {
|
||||
portCurrent = i
|
||||
}
|
||||
if portCurrent == portStart {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil, ErrPort
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user