This commit is contained in:
Hongbo Zhang 2020-06-12 10:07:17 -04:00
parent aaa00b3488
commit 96cfcc0b5b
3 changed files with 124 additions and 20 deletions

View File

@ -22,7 +22,10 @@ type NATRouter interface {
func GetNATRouter() NATRouter {
//TODO other protocol
return getUPnPRouter()
if r := getUPnPRouter(); r != nil {
return r
}
return nil
}
type Router struct {
@ -61,13 +64,13 @@ func (dev *Router) mapPort(protocol string, intport, extport uint16, desc string
}()
if err := dev.r.MapPort(protocol, intport, extport, desc, mapTimeout); err != nil {
dev.log.Warn("Map port failed. Protocol %s Internal %d External %d. %s",
dev.log.Error("Map port failed. Protocol %s Internal %d External %d. %s",
protocol, intport, extport, err)
dev.errLock.Lock()
dev.errs.Add(err)
dev.errLock.Unlock()
} else {
dev.log.Info("Mapped Protocol %s Internal %d External %d. %s", protocol,
dev.log.Info("Mapped Protocol %s Internal %d External %d.", protocol,
intport, extport)
}
@ -75,14 +78,16 @@ func (dev *Router) mapPort(protocol string, intport, extport uint16, desc string
select {
case <-updater.C:
if err := dev.r.MapPort(protocol, intport, extport, desc, mapTimeout); err != nil {
dev.log.Warn("Renew port mapping failed. Protocol %s Internal %d External %d. %s",
dev.log.Error("Renew port mapping failed. Protocol %s Internal %d External %d. %s",
protocol, intport, extport, err)
} else {
dev.log.Info("Renew port mapping Protocol %s Internal %d External %d. %s", protocol,
dev.log.Info("Renew port mapping Protocol %s Internal %d External %d.", protocol,
intport, extport)
}
updater.Reset(mapUpdate)
case _, _ = <-dev.closer:
return
}
}
}
@ -90,5 +95,6 @@ func (dev *Router) mapPort(protocol string, intport, extport uint16, desc string
func (dev *Router) UnmapAllPorts() error {
close(dev.closer)
dev.wg.Wait()
dev.log.Info("Unmapped all ports")
return dev.errs.Err
}

77
nat/nat_test.go Normal file
View File

@ -0,0 +1,77 @@
package nat
// go test -run 'HTTP'
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"strconv"
"testing"
"time"
"github.com/ava-labs/gecko/utils/logging"
)
const (
externalPort = 9876
localPort = 8080
)
func TestHTTP(t *testing.T) {
config, err := logging.DefaultConfig()
if err != nil {
return
}
factory := logging.NewFactory(config)
defer factory.Close()
log, err := factory.Make()
if err != nil {
return
}
defer log.Stop()
defer log.StopOnPanic()
log.Info("Logger Initialized")
n := GetNATRouter()
if n == nil {
log.Error("Unable to get UPnP Device")
return
}
ip, err := n.ExternalIP()
if err != nil {
log.Error("Unable to get external IP: %v", err)
return
}
log.Info("External Address %s:%d", ip.String(), externalPort)
r := NewRouter(log, n)
defer r.UnmapAllPorts()
r.Map("TCP", localPort, externalPort, "AVA UPnP Test")
log.Info("Starting HTTP Service")
server := &http.Server{Addr: ":" + strconv.Itoa(localPort)}
http.HandleFunc("/", hello)
go func() {
server.ListenAndServe()
}()
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt)
<-stop
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
server.Shutdown(ctx)
}
func hello(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "AVA UPnP Test\n")
}

View File

@ -10,7 +10,10 @@ import (
"github.com/huin/goupnp/dcps/internetgateway2"
)
const soapRequestTimeout = 3 * time.Second
const (
soapRequestTimeout = 3 * time.Second
mapRetry = 20
)
// upnpClient is the interface used by goupnp for their client implementations
type upnpClient interface {
@ -79,7 +82,6 @@ func (r *upnpRouter) localIP() (net.IP, error) {
}
}
return nil, fmt.Errorf("couldn't find the local address in the same network as %s", deviceIP)
}
func (r *upnpRouter) ExternalIP() (net.IP, error) {
@ -95,14 +97,26 @@ func (r *upnpRouter) ExternalIP() (net.IP, error) {
return ip, nil
}
func (r *upnpRouter) MapPort(protocol string, intport, extport uint16, desc string, duration time.Duration) error {
func (r *upnpRouter) MapPort(protocol string, intport, extport uint16,
desc string, duration time.Duration) error {
ip, err := r.localIP()
if err != nil {
return nil
}
lifetime := uint32(duration / time.Second)
r.UnmapPort(protocol, extport)
return r.client.AddPortMapping("", extport, protocol, intport, ip.String(), true, desc, lifetime)
for i := 0; i < mapRetry; i++ {
externalPort := extport + uint16(i)
err = r.client.AddPortMapping("", externalPort, protocol, intport,
ip.String(), true, desc, lifetime)
if err == nil {
fmt.Printf("Mapped external port %d to local %s:%d\n", externalPort,
ip.String(), intport)
return nil
}
fmt.Printf("Unable to map port, retry with port %d\n", externalPort+1)
}
return err
}
func (r *upnpRouter) UnmapPort(protocol string, extport uint16) error {
@ -184,17 +198,24 @@ func discover(target string) *upnpRouter {
}
func getUPnPRouter() *upnpRouter {
r := discover(internetgateway1.URN_WANConnectionDevice_1)
if r != nil {
return r
targets := []string{
internetgateway1.URN_WANConnectionDevice_1,
internetgateway2.URN_WANConnectionDevice_2,
}
return discover(internetgateway2.URN_WANConnectionDevice_2)
}
func GetUPnP() *upnpRouter {
r := discover(internetgateway1.URN_WANConnectionDevice_1)
if r != nil {
return r
routers := make(chan *upnpRouter, len(targets))
for _, urn := range targets {
go func(urn string) {
routers <- discover(urn)
}(urn)
}
return discover(internetgateway2.URN_WANConnectionDevice_2)
for i := 0; i < len(targets); i++ {
if r := <-routers; r != nil {
return r
}
}
return nil
}