2020-06-15 12:16:24 -07:00
|
|
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
|
2020-06-07 17:02:29 -07:00
|
|
|
package nat
|
|
|
|
|
|
|
|
import (
|
2020-06-24 08:41:22 -07:00
|
|
|
"errors"
|
2020-06-07 17:02:29 -07:00
|
|
|
"net"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ava-labs/gecko/utils/logging"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2020-06-15 12:16:24 -07:00
|
|
|
mapTimeout = 30 * time.Second
|
2020-06-13 12:29:28 -07:00
|
|
|
mapUpdateTimeout = mapTimeout / 2
|
|
|
|
maxRetries = 20
|
2020-06-07 17:02:29 -07:00
|
|
|
)
|
|
|
|
|
2020-06-17 19:33:41 -07:00
|
|
|
// Router describes the functionality that a network device must support to be
|
|
|
|
// able to open ports to an external IP.
|
2020-06-16 08:29:50 -07:00
|
|
|
type Router interface {
|
2020-06-14 13:08:09 -07:00
|
|
|
MapPort(protocol string, intPort, extPort uint16, desc string, duration time.Duration) error
|
2020-06-15 12:16:24 -07:00
|
|
|
UnmapPort(protocol string, intPort, extPort uint16) error
|
2020-06-07 17:02:29 -07:00
|
|
|
ExternalIP() (net.IP, error)
|
2020-06-14 13:08:09 -07:00
|
|
|
GetPortMappingEntry(extPort uint16, protocol string) (
|
2020-06-14 00:51:31 -07:00
|
|
|
InternalIP string,
|
|
|
|
InternalPort uint16,
|
|
|
|
Description string,
|
|
|
|
err error,
|
|
|
|
)
|
2020-06-07 17:02:29 -07:00
|
|
|
}
|
|
|
|
|
2020-06-17 19:33:41 -07:00
|
|
|
// GetRouter returns a router on the current network.
|
2020-06-16 08:29:50 -07:00
|
|
|
func GetRouter() Router {
|
2020-06-13 12:29:28 -07:00
|
|
|
if r := getUPnPRouter(); r != nil {
|
|
|
|
return r
|
2020-06-12 07:07:17 -07:00
|
|
|
}
|
2020-06-15 12:16:24 -07:00
|
|
|
if r := getPMPRouter(); r != nil {
|
|
|
|
return r
|
|
|
|
}
|
2020-06-12 14:40:41 -07:00
|
|
|
|
2020-06-13 12:29:28 -07:00
|
|
|
return NewNoRouter()
|
2020-06-07 17:02:29 -07:00
|
|
|
}
|
|
|
|
|
2020-06-17 19:33:41 -07:00
|
|
|
// Mapper attempts to open a set of ports on a router
|
2020-06-14 00:51:31 -07:00
|
|
|
type Mapper struct {
|
2020-06-17 14:13:35 -07:00
|
|
|
log logging.Logger
|
|
|
|
r Router
|
|
|
|
closer chan struct{}
|
|
|
|
wg sync.WaitGroup
|
2020-06-07 17:02:29 -07:00
|
|
|
}
|
|
|
|
|
2020-06-17 19:33:41 -07:00
|
|
|
// NewPortMapper returns an initialized mapper
|
2020-06-16 08:29:50 -07:00
|
|
|
func NewPortMapper(log logging.Logger, r Router) Mapper {
|
2020-06-14 00:51:31 -07:00
|
|
|
return Mapper{
|
2020-06-07 17:02:29 -07:00
|
|
|
log: log,
|
|
|
|
r: r,
|
|
|
|
closer: make(chan struct{}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 12:29:28 -07:00
|
|
|
// Map sets up port mapping using given protocol, internal and external ports
|
|
|
|
// and returns the final port mapped. It returns 0 if mapping failed after the
|
|
|
|
// maximun number of retries
|
2020-06-24 08:41:22 -07:00
|
|
|
func (dev *Mapper) Map(protocol string, intPort uint16, desc string) (uint16, error) {
|
2020-06-13 12:29:28 -07:00
|
|
|
mappedPort := make(chan uint16)
|
|
|
|
|
2020-06-16 08:29:50 -07:00
|
|
|
go dev.keepPortMapping(mappedPort, protocol, intPort, desc)
|
2020-06-13 12:29:28 -07:00
|
|
|
|
2020-06-24 08:41:22 -07:00
|
|
|
port := <-mappedPort
|
|
|
|
if port == 0 {
|
|
|
|
return 0, errors.New("failed to map port")
|
|
|
|
}
|
|
|
|
return port, nil
|
2020-06-07 17:02:29 -07:00
|
|
|
}
|
|
|
|
|
2020-06-13 12:29:28 -07:00
|
|
|
// keepPortMapping runs in the background to keep a port mapped. It renews the
|
|
|
|
// the port mapping in mapUpdateTimeout.
|
2020-06-14 00:51:31 -07:00
|
|
|
func (dev *Mapper) keepPortMapping(mappedPort chan<- uint16, protocol string,
|
2020-06-16 08:29:50 -07:00
|
|
|
intPort uint16, desc string) {
|
2020-06-13 12:29:28 -07:00
|
|
|
updateTimer := time.NewTimer(mapUpdateTimeout)
|
|
|
|
|
2020-06-14 13:08:09 -07:00
|
|
|
for i := 0; i <= maxRetries; i++ {
|
2020-06-16 08:29:50 -07:00
|
|
|
extPort := intPort + uint16(i)
|
|
|
|
if intaddr, intPort, desc, err := dev.r.GetPortMappingEntry(extPort, protocol); err == nil {
|
|
|
|
dev.log.Debug("Port %d is taken by %s:%d: %s, retry with the next port",
|
|
|
|
extPort, intaddr, intPort, desc)
|
2020-06-17 14:13:35 -07:00
|
|
|
} else if err := dev.r.MapPort(protocol, intPort, extPort, desc, mapTimeout); err != nil {
|
|
|
|
dev.log.Debug("Map port failed. Protocol %s Internal %d External %d. %s",
|
2020-06-16 08:29:50 -07:00
|
|
|
protocol, intPort, extPort, err)
|
2020-06-13 12:29:28 -07:00
|
|
|
} else {
|
|
|
|
dev.log.Info("Mapped Protocol %s Internal %d External %d.", protocol,
|
2020-06-16 08:29:50 -07:00
|
|
|
intPort, extPort)
|
2020-06-07 17:02:29 -07:00
|
|
|
|
2020-06-14 13:08:09 -07:00
|
|
|
dev.wg.Add(1)
|
|
|
|
|
2020-06-16 08:29:50 -07:00
|
|
|
mappedPort <- extPort
|
|
|
|
|
|
|
|
defer func(extPort uint16) {
|
2020-06-14 13:08:09 -07:00
|
|
|
updateTimer.Stop()
|
|
|
|
|
2020-06-16 08:29:50 -07:00
|
|
|
dev.log.Debug("Unmap protocol %s external port %d", protocol, extPort)
|
|
|
|
dev.r.UnmapPort(protocol, intPort, extPort)
|
2020-06-14 13:08:09 -07:00
|
|
|
|
|
|
|
dev.wg.Done()
|
2020-06-16 08:29:50 -07:00
|
|
|
}(extPort)
|
2020-06-14 13:08:09 -07:00
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-updateTimer.C:
|
2020-06-16 08:29:50 -07:00
|
|
|
if err := dev.r.MapPort(protocol, intPort, extPort, desc, mapTimeout); err != nil {
|
|
|
|
dev.log.Error("Renewing port mapping from external port %d to internal port %d failed with %s",
|
|
|
|
intPort, extPort, err)
|
2020-06-14 13:08:09 -07:00
|
|
|
} else {
|
2020-06-24 07:09:36 -07:00
|
|
|
dev.log.Debug("Renewed port mapping from external port %d to internal port %d.",
|
2020-06-16 08:29:50 -07:00
|
|
|
intPort, extPort)
|
2020-06-14 13:08:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
updateTimer.Reset(mapUpdateTimeout)
|
|
|
|
case _, _ = <-dev.closer:
|
|
|
|
return
|
|
|
|
}
|
2020-06-07 17:02:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-14 13:08:09 -07:00
|
|
|
|
2020-06-16 08:29:50 -07:00
|
|
|
dev.log.Debug("Unable to map port %d~%d", intPort, intPort+maxRetries)
|
2020-06-14 13:08:09 -07:00
|
|
|
mappedPort <- 0
|
2020-06-07 17:02:29 -07:00
|
|
|
}
|
|
|
|
|
2020-06-17 19:33:41 -07:00
|
|
|
// UnmapAllPorts stops mapping all ports from this mapper and attempts to unmap
|
|
|
|
// them.
|
2020-06-17 14:13:35 -07:00
|
|
|
func (dev *Mapper) UnmapAllPorts() {
|
2020-06-07 17:02:29 -07:00
|
|
|
close(dev.closer)
|
|
|
|
dev.wg.Wait()
|
2020-06-12 07:07:17 -07:00
|
|
|
dev.log.Info("Unmapped all ports")
|
2020-06-07 17:02:29 -07:00
|
|
|
}
|