Break out UPnP port mapping into a service

This commit is contained in:
Jakob Borg
2015-04-25 20:52:07 +09:00
parent 50d377d9fe
commit 0642402449
6 changed files with 124 additions and 126 deletions

View File

@@ -35,7 +35,6 @@ import (
"github.com/syncthing/syncthing/internal/osutil"
"github.com/syncthing/syncthing/internal/symlinks"
"github.com/syncthing/syncthing/internal/upgrade"
"github.com/syncthing/syncthing/internal/upnp"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/opt"
@@ -109,8 +108,6 @@ var (
readRateLimit *ratelimit.Bucket
stop = make(chan int)
discoverer *discover.Discoverer
externalPort int
igd *upnp.IGD
cert tls.Certificate
lans []*net.IPNet
)
@@ -573,18 +570,20 @@ func syncthingMain() {
if err != nil {
l.Fatalln("Bad listen address:", err)
}
externalPort = addr.Port
// UPnP
igd = nil
// Start discovery
localPort := addr.Port
discoverer = discovery(localPort)
// Start UPnP. The UPnP service will restart global discovery if the
// external port changes.
if opts.UPnPEnabled {
setupUPnP()
upnpSvc := newUPnPSvc(cfg, localPort)
mainSvc.Add(upnpSvc)
}
// Routine to connect out to configured devices
discoverer = discovery(externalPort)
connectionSvc := newConnectionSvc(cfg, myID, m, tlsCfg)
mainSvc.Add(connectionSvc)
@@ -765,110 +764,6 @@ func generatePingEvents() {
}
}
func setupUPnP() {
if opts := cfg.Options(); len(opts.ListenAddress) == 1 {
_, portStr, err := net.SplitHostPort(opts.ListenAddress[0])
if err != nil {
l.Warnln("Bad listen address:", err)
} else {
// Set up incoming port forwarding, if necessary and possible
port, _ := strconv.Atoi(portStr)
igds := upnp.Discover(time.Duration(cfg.Options().UPnPTimeoutS) * time.Second)
if len(igds) > 0 {
// Configure the first discovered IGD only. This is a work-around until we have a better mechanism
// for handling multiple IGDs, which will require changes to the global discovery service
igd = &igds[0]
externalPort = setupExternalPort(igd, port)
if externalPort == 0 {
l.Warnln("Failed to create UPnP port mapping")
} else {
l.Infof("Created UPnP port mapping for external port %d on UPnP device %s.", externalPort, igd.FriendlyIdentifier())
if opts.UPnPRenewalM > 0 {
go renewUPnP(port)
}
}
}
}
} else {
l.Warnln("Multiple listening addresses; not attempting UPnP port mapping")
}
}
func setupExternalPort(igd *upnp.IGD, port int) int {
if igd == nil {
return 0
}
for i := 0; i < 10; i++ {
r := 1024 + predictableRandom.Intn(65535-1024)
err := igd.AddPortMapping(upnp.TCP, r, port, fmt.Sprintf("syncthing-%d", r), cfg.Options().UPnPLeaseM*60)
if err == nil {
return r
}
}
return 0
}
func renewUPnP(port int) {
for {
opts := cfg.Options()
time.Sleep(time.Duration(opts.UPnPRenewalM) * time.Minute)
// Some values might have changed while we were sleeping
opts = cfg.Options()
// Make sure our IGD reference isn't nil
if igd == nil {
if debugNet {
l.Debugln("Undefined IGD during UPnP port renewal. Re-discovering...")
}
igds := upnp.Discover(time.Duration(opts.UPnPTimeoutS) * time.Second)
if len(igds) > 0 {
// Configure the first discovered IGD only. This is a work-around until we have a better mechanism
// for handling multiple IGDs, which will require changes to the global discovery service
igd = &igds[0]
} else {
if debugNet {
l.Debugln("Failed to discover IGD during UPnP port mapping renewal.")
}
continue
}
}
// Just renew the same port that we already have
if externalPort != 0 {
err := igd.AddPortMapping(upnp.TCP, externalPort, port, "syncthing", opts.UPnPLeaseM*60)
if err != nil {
l.Warnf("Error renewing UPnP port mapping for external port %d on device %s: %s", externalPort, igd.FriendlyIdentifier(), err.Error())
} else if debugNet {
l.Debugf("Renewed UPnP port mapping for external port %d on device %s.", externalPort, igd.FriendlyIdentifier())
}
continue
}
// Something strange has happened. We didn't have an external port before?
// Or perhaps the gateway has changed?
// Retry the same port sequence from the beginning.
if debugNet {
l.Debugln("No UPnP port mapping defined, updating...")
}
forwardedPort := setupExternalPort(igd, port)
if forwardedPort != 0 {
externalPort = forwardedPort
discoverer.StopGlobal()
discoverer.StartGlobal(opts.GlobalAnnServers, uint16(forwardedPort))
if debugNet {
l.Debugf("Updated UPnP port mapping for external port %d on device %s.", forwardedPort, igd.FriendlyIdentifier())
}
} else {
l.Warnf("Failed to update UPnP port mapping for external port on device " + igd.FriendlyIdentifier() + ".")
}
}
}
func resetDB() error {
return os.RemoveAll(locations[locDatabase])
}