github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/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 12:09:44</date> 10 //</624342658696876032> 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 func ExtIP(ip net.IP) Interface { 129 if ip == nil { 130 panic("IP must not be nil") 131 } 132 return extIP(ip) 133 } 134 135 type extIP net.IP 136 137 func (n extIP) ExternalIP() (net.IP, error) { return net.IP(n), nil } 138 func (n extIP) String() string { return fmt.Sprintf("ExtIP(%v)", net.IP(n)) } 139 140 //这些什么都不做。 141 func (extIP) AddMapping(string, int, int, string, time.Duration) error { return nil } 142 func (extIP) DeleteMapping(string, int, int) error { return nil } 143 144 //any返回一个端口映射器,该映射器尝试发现任何支持的 145 //本地网络上的机制。 146 func Any() Interface { 147 //TODO:尝试发现本地计算机是否具有 148 //Internet类地址。在这种情况下返回extip。 149 return startautodisc("UPnP or NAT-PMP", func() Interface { 150 found := make(chan Interface, 2) 151 go func() { found <- discoverUPnP() }() 152 go func() { found <- discoverPMP() }() 153 for i := 0; i < cap(found); i++ { 154 if c := <-found; c != nil { 155 return c 156 } 157 } 158 return nil 159 }) 160 } 161 162 //UPNP返回使用UPNP的端口映射器。它将试图 163 //使用UDP广播发现路由器的地址。 164 func UPnP() Interface { 165 return startautodisc("UPnP", discoverUPnP) 166 } 167 168 //pmp返回使用nat-pmp的端口映射器。提供的网关 169 //地址应该是路由器的IP。如果给定的网关 170 //地址为零,PMP将尝试自动发现路由器。 171 func PMP(gateway net.IP) Interface { 172 if gateway != nil { 173 return &pmp{gw: gateway, c: natpmp.NewClient(gateway)} 174 } 175 return startautodisc("NAT-PMP", discoverPMP) 176 } 177 178 //自动发现表示仍在 179 //自动发现。调用此类型上的接口方法将 180 //等待发现完成,然后在 181 //发现了机制。 182 // 183 //这种类型很有用,因为发现可能需要一段时间,但是我们 184 //要立即从upnp、pmp和auto返回接口值。 185 type autodisc struct { 186 what string //正在自动发现的接口类型 187 once sync.Once 188 doit func() Interface 189 190 mu sync.Mutex 191 found Interface 192 } 193 194 func startautodisc(what string, doit func() Interface) Interface { 195 //TODO:监视网络配置,并在其更改时重新运行DOIT。 196 return &autodisc{what: what, doit: doit} 197 } 198 199 func (n *autodisc) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error { 200 if err := n.wait(); err != nil { 201 return err 202 } 203 return n.found.AddMapping(protocol, extport, intport, name, lifetime) 204 } 205 206 func (n *autodisc) DeleteMapping(protocol string, extport, intport int) error { 207 if err := n.wait(); err != nil { 208 return err 209 } 210 return n.found.DeleteMapping(protocol, extport, intport) 211 } 212 213 func (n *autodisc) ExternalIP() (net.IP, error) { 214 if err := n.wait(); err != nil { 215 return nil, err 216 } 217 return n.found.ExternalIP() 218 } 219 220 func (n *autodisc) String() string { 221 n.mu.Lock() 222 defer n.mu.Unlock() 223 if n.found == nil { 224 return n.what 225 } else { 226 return n.found.String() 227 } 228 } 229 230 //等待块,直到执行自动发现。 231 func (n *autodisc) wait() error { 232 n.once.Do(func() { 233 n.mu.Lock() 234 n.found = n.doit() 235 n.mu.Unlock() 236 }) 237 if n.found == nil { 238 return fmt.Errorf("no %s router discovered", n.what) 239 } 240 return nil 241 } 242