p2p/nat: use rateLimiter instead of extra timeout

Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com>
This commit is contained in:
Csaba Kiraly 2025-04-03 14:18:43 +02:00
parent 54d124ba46
commit 2a517dab64
No known key found for this signature in database
GPG key ID: 0FE274EE8C95166E

View file

@ -35,9 +35,8 @@ import (
const ( const (
soapRequestTimeout = 3 * time.Second soapRequestTimeout = 3 * time.Second
rateLimit = 200 * time.Millisecond rateLimit = 200 * time.Millisecond
retryInterval = 1 * time.Second // time to wait between retries retryCount = 3 // number of retries after a failed AddPortMapping
retryCount = 3 // number of retries after a failed AddPortMapping randomCount = 3 // number of random ports to try
randomCount = 3 // number of random ports to try
) )
type upnp struct { type upnp struct {
@ -101,30 +100,26 @@ func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, li
} }
// Try to add port mapping, preferring the specified external port. // Try to add port mapping, preferring the specified external port.
err = n.withRateLimit(func() error { return n.addAnyPortMapping(protocol, extport, intport, ip, desc, lifetimeS)
p, err := n.addAnyPortMapping(protocol, extport, intport, ip, desc, lifetimeS)
if err == nil {
extport = int(p)
}
return err
})
return uint16(extport), err
} }
// addAnyPortMapping tries to add a port mapping with the specified external port. // addAnyPortMapping tries to add a port mapping with the specified external port.
// If the external port is already in use, it will try to assign another port. // If the external port is already in use, it will try to assign another port.
func (n *upnp) addAnyPortMapping(protocol string, extport, intport int, ip net.IP, desc string, lifetimeS uint32) (uint16, error) { func (n *upnp) addAnyPortMapping(protocol string, extport, intport int, ip net.IP, desc string, lifetimeS uint32) (uint16, error) {
if client, ok := n.client.(*internetgateway2.WANIPConnection2); ok { if client, ok := n.client.(*internetgateway2.WANIPConnection2); ok {
return client.AddAnyPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) return n.portWithRateLimit(func() (uint16, error) {
return client.AddAnyPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
})
} }
// For IGDv1 and v1 services we should first try to add with extport. // For IGDv1 and v1 services we should first try to add with extport.
for i := 0; i < retryCount+1; i++ { for i := 0; i < retryCount+1; i++ {
err := n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) err := n.withRateLimit(func() error {
return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
})
if err == nil { if err == nil {
return uint16(extport), nil return uint16(extport), nil
} }
log.Trace("Failed to add port mapping", "protocol", protocol, "extport", extport, "intport", intport, "err", err) log.Trace("Failed to add port mapping", "protocol", protocol, "extport", extport, "intport", intport, "err", err)
time.Sleep(retryInterval)
} }
// If above fails, we retry with a random port. // If above fails, we retry with a random port.
@ -132,12 +127,13 @@ func (n *upnp) addAnyPortMapping(protocol string, extport, intport int, ip net.I
var err error var err error
for i := 0; i < randomCount; i++ { for i := 0; i < randomCount; i++ {
extport = n.randomPort() extport = n.randomPort()
err = n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS) err := n.withRateLimit(func() error {
return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
})
if err == nil { if err == nil {
return uint16(extport), nil return uint16(extport), nil
} }
log.Trace("Failed to add random port mapping", "protocol", protocol, "extport", extport, "intport", intport, "err", err) log.Trace("Failed to add random port mapping", "protocol", protocol, "extport", extport, "intport", intport, "err", err)
time.Sleep(retryInterval)
} }
return 0, err return 0, err
} }
@ -182,6 +178,17 @@ func (n *upnp) String() string {
return "UPNP " + n.service return "UPNP " + n.service
} }
func (n *upnp) portWithRateLimit(pfn func() (uint16, error)) (uint16, error) {
var port uint16
var err error
fn := func() error {
port, err = pfn()
return err
}
n.withRateLimit(fn)
return port, err
}
func (n *upnp) withRateLimit(fn func() error) error { func (n *upnp) withRateLimit(fn func() error) error {
n.mu.Lock() n.mu.Lock()
defer n.mu.Unlock() defer n.mu.Unlock()