p2p/nat: retry UPnP several times before resetting port to 0

Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com>
This commit is contained in:
Csaba Kiraly 2025-04-03 09:32:16 +02:00
parent fe9056f29f
commit 78930c11fb
No known key found for this signature in database
GPG key ID: 0FE274EE8C95166E

View file

@ -31,12 +31,14 @@ const (
portMapRefreshInterval = 8 * time.Minute portMapRefreshInterval = 8 * time.Minute
portMapRetryInterval = 5 * time.Minute portMapRetryInterval = 5 * time.Minute
extipRetryInterval = 2 * time.Minute extipRetryInterval = 2 * time.Minute
maxRetries = 5 // max number of failed attempts to refresh the mapping
) )
type portMapping struct { type portMapping struct {
protocol string protocol string
name string name string
port int port int
retries int // number of failed attempts to refresh the mapping
// for use by the portMappingLoop goroutine: // for use by the portMappingLoop goroutine:
extPort int // the mapped port returned by the NAT interface extPort int // the mapped port returned by the NAT interface
@ -158,17 +160,26 @@ func (srv *Server) portMappingLoop() {
if m.extPort == 0 { if m.extPort == 0 {
log.Debug("Couldn't add port mapping", "err", err) log.Debug("Couldn't add port mapping", "err", err)
} else { } else {
// Since UPnP implementation are often buggy, // Failed refresh. Since UPnP implementation are often buggy, and lifetime is
// and lifetime is larger than the retry interval, this does not mean we lost our // larger than the retry interval, this does not mean we lost our existing
// existing mapping. We do not reset the external port, as it is still our best chance, // mapping. We do not reset the external port, as it is still our best chance,
// but we do retry soon. // but we do retry soon.
// TODO: we could check the error code, but again, UPnP implementations are buggy. // TODO: we could check the error code, but again, UPnP implementations are buggy.
log.Debug("Couldn't refresh port mapping", "err", err) log.Debug("Couldn't refresh port mapping", "err", err)
m.retries++
if m.retries > maxRetries {
m.retries = 0
err := srv.NAT.DeleteMapping(m.protocol, m.extPort, m.port)
log.Debug("Couldn't refresh port mapping, trying to delete it:", "err", err)
m.extPort = 0
}
} }
m.nextTime = srv.clock.Now().Add(portMapRetryInterval) m.nextTime = srv.clock.Now().Add(portMapRetryInterval)
continue continue //TODO: this means we never reset the ENR. Is that what we want?
} }
// It was mapped! // It was mapped!
m.retries = 0
log = newLogger(m.protocol, int(p), m.port) log = newLogger(m.protocol, int(p), m.port)
if int(p) != m.extPort { if int(p) != m.extPort {
m.extPort = int(p) m.extPort = int(p)