github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/nat/nat.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:41</date>
    10  //</624450105004527616>
    11  
    12  
    13  //包NAT提供对公共网络端口映射协议的访问。
    14  package nat
    15  
    16  import (
    17  	"errors"
    18  	"fmt"
    19  	"net"
    20  	"strings"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/log"
    25  	"github.com/jackpal/go-nat-pmp"
    26  )
    27  
    28  //一个nat.interface的实现可以将本地端口映射到端口
    29  //可从Internet访问。
    30  type Interface interface {
    31  //这些方法管理本地端口之间的映射
    32  //机器到可以从Internet连接到的端口。
    33  //
    34  //协议是“udp”或“tcp”。某些实现允许设置
    35  //映射的显示名称。映射可以通过
    36  //网关的生命周期结束时。
    37  	AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error
    38  	DeleteMapping(protocol string, extport, intport int) error
    39  
    40  //此方法应返回外部(面向Internet)
    41  //网关设备的地址。
    42  	ExternalIP() (net.IP, error)
    43  
    44  //应返回方法的名称。这用于日志记录。
    45  	String() string
    46  }
    47  
    48  //解析解析一个NAT接口描述。
    49  //当前接受以下格式。
    50  //请注意,机制名称不区分大小写。
    51  //
    52  //“”或“无”返回零
    53  //“extip:77.12.33.4”将假定在给定的IP上可以访问本地计算机。
    54  //“任意”使用第一个自动检测机制
    55  //“UPNP”使用通用即插即用协议
    56  //“pmp”使用具有自动检测到的网关地址的nat-pmp
    57  //“pmp:192.168.0.1”使用具有给定网关地址的nat-pmp
    58  func Parse(spec string) (Interface, error) {
    59  	var (
    60  		parts = strings.SplitN(spec, ":", 2)
    61  		mech  = strings.ToLower(parts[0])
    62  		ip    net.IP
    63  	)
    64  	if len(parts) > 1 {
    65  		ip = net.ParseIP(parts[1])
    66  		if ip == nil {
    67  			return nil, errors.New("invalid IP address")
    68  		}
    69  	}
    70  	switch mech {
    71  	case "", "none", "off":
    72  		return nil, nil
    73  	case "any", "auto", "on":
    74  		return Any(), nil
    75  	case "extip", "ip":
    76  		if ip == nil {
    77  			return nil, errors.New("missing IP address")
    78  		}
    79  		return ExtIP(ip), nil
    80  	case "upnp":
    81  		return UPnP(), nil
    82  	case "pmp", "natpmp", "nat-pmp":
    83  		return PMP(ip), nil
    84  	default:
    85  		return nil, fmt.Errorf("unknown mechanism %q", parts[0])
    86  	}
    87  }
    88  
    89  const (
    90  	mapTimeout        = 20 * time.Minute
    91  	mapUpdateInterval = 15 * time.Minute
    92  )
    93  
    94  //map在m上添加了一个端口映射,并在c关闭之前保持它的活动状态。
    95  //此函数通常在自己的goroutine中调用。
    96  func Map(m Interface, c chan struct{}, protocol string, extport, intport int, name string) {
    97  	log := log.New("proto", protocol, "extport", extport, "intport", intport, "interface", m)
    98  	refresh := time.NewTimer(mapUpdateInterval)
    99  	defer func() {
   100  		refresh.Stop()
   101  		log.Debug("Deleting port mapping")
   102  		m.DeleteMapping(protocol, extport, intport)
   103  	}()
   104  	if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil {
   105  		log.Debug("Couldn't add port mapping", "err", err)
   106  	} else {
   107  		log.Info("Mapped network port")
   108  	}
   109  	for {
   110  		select {
   111  		case _, ok := <-c:
   112  			if !ok {
   113  				return
   114  			}
   115  		case <-refresh.C:
   116  			log.Trace("Refreshing port mapping")
   117  			if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil {
   118  				log.Debug("Couldn't add port mapping", "err", err)
   119  			}
   120  			refresh.Reset(mapUpdateInterval)
   121  		}
   122  	}
   123  }
   124  
   125  //extip假定在给定的
   126  //外部IP地址,以及手动映射所需的任何端口。
   127  //映射操作不会返回错误,但实际上不会执行任何操作。
   128  type ExtIP net.IP
   129  
   130  func (n ExtIP) ExternalIP() (net.IP, error) { return net.IP(n), nil }
   131  func (n ExtIP) String() string              { return fmt.Sprintf("ExtIP(%v)", net.IP(n)) }
   132  
   133  //这些什么都不做。
   134  
   135  func (ExtIP) AddMapping(string, int, int, string, time.Duration) error { return nil }
   136  func (ExtIP) DeleteMapping(string, int, int) error                     { return nil }
   137  
   138  //any返回一个端口映射器,该映射器尝试发现任何支持的
   139  //本地网络上的机制。
   140  func Any() Interface {
   141  //TODO:尝试发现本地计算机是否具有
   142  //Internet类地址。在这种情况下返回extip。
   143  	return startautodisc("UPnP or NAT-PMP", func() Interface {
   144  		found := make(chan Interface, 2)
   145  		go func() { found <- discoverUPnP() }()
   146  		go func() { found <- discoverPMP() }()
   147  		for i := 0; i < cap(found); i++ {
   148  			if c := <-found; c != nil {
   149  				return c
   150  			}
   151  		}
   152  		return nil
   153  	})
   154  }
   155  
   156  //UPNP返回使用UPNP的端口映射器。它将试图
   157  //使用UDP广播发现路由器的地址。
   158  func UPnP() Interface {
   159  	return startautodisc("UPnP", discoverUPnP)
   160  }
   161  
   162  //pmp返回使用nat-pmp的端口映射器。提供的网关
   163  //地址应该是路由器的IP。如果给定的网关
   164  //地址为零,PMP将尝试自动发现路由器。
   165  func PMP(gateway net.IP) Interface {
   166  	if gateway != nil {
   167  		return &pmp{gw: gateway, c: natpmp.NewClient(gateway)}
   168  	}
   169  	return startautodisc("NAT-PMP", discoverPMP)
   170  }
   171  
   172  //自动发现表示仍在
   173  //自动发现。调用此类型上的接口方法将
   174  //等待发现完成,然后在
   175  //发现了机制。
   176  //
   177  //这种类型很有用,因为发现可能需要一段时间,但是我们
   178  //要立即从upnp、pmp和auto返回接口值。
   179  type autodisc struct {
   180  what string //正在自动发现的接口类型
   181  	once sync.Once
   182  	doit func() Interface
   183  
   184  	mu    sync.Mutex
   185  	found Interface
   186  }
   187  
   188  func startautodisc(what string, doit func() Interface) Interface {
   189  //TODO:监视网络配置,并在其更改时重新运行DOIT。
   190  	return &autodisc{what: what, doit: doit}
   191  }
   192  
   193  func (n *autodisc) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error {
   194  	if err := n.wait(); err != nil {
   195  		return err
   196  	}
   197  	return n.found.AddMapping(protocol, extport, intport, name, lifetime)
   198  }
   199  
   200  func (n *autodisc) DeleteMapping(protocol string, extport, intport int) error {
   201  	if err := n.wait(); err != nil {
   202  		return err
   203  	}
   204  	return n.found.DeleteMapping(protocol, extport, intport)
   205  }
   206  
   207  func (n *autodisc) ExternalIP() (net.IP, error) {
   208  	if err := n.wait(); err != nil {
   209  		return nil, err
   210  	}
   211  	return n.found.ExternalIP()
   212  }
   213  
   214  func (n *autodisc) String() string {
   215  	n.mu.Lock()
   216  	defer n.mu.Unlock()
   217  	if n.found == nil {
   218  		return n.what
   219  	} else {
   220  		return n.found.String()
   221  	}
   222  }
   223  
   224  //等待块,直到执行自动发现。
   225  func (n *autodisc) wait() error {
   226  	n.once.Do(func() {
   227  		n.mu.Lock()
   228  		n.found = n.doit()
   229  		n.mu.Unlock()
   230  	})
   231  	if n.found == nil {
   232  		return fmt.Errorf("no %s router discovered", n.what)
   233  	}
   234  	return nil
   235  }
   236