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