nat.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // Copyright 2015 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. // Package nat provides access to common network port mapping protocols.
  17. package nat
  18. import (
  19. "errors"
  20. "fmt"
  21. "net"
  22. "strings"
  23. "sync"
  24. "time"
  25. "blockchain-go/common/gopool"
  26. "blockchain-go/log"
  27. natpmp "github.com/jackpal/go-nat-pmp"
  28. )
  29. // An implementation of nat.Interface can map local ports to ports
  30. // accessible from the Internet.
  31. type Interface interface {
  32. // These methods manage a mapping between a port on the local
  33. // machine to a port that can be connected to from the internet.
  34. //
  35. // protocol is "UDP" or "TCP". Some implementations allow setting
  36. // a display name for the mapping. The mapping may be removed by
  37. // the gateway when its lifetime ends.
  38. AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error
  39. DeleteMapping(protocol string, extport, intport int) error
  40. // This method should return the external (Internet-facing)
  41. // address of the gateway device.
  42. ExternalIP() (net.IP, error)
  43. // Should return name of the method. This is used for logging.
  44. String() string
  45. }
  46. // Parse parses a NAT interface description.
  47. // The following formats are currently accepted.
  48. // Note that mechanism names are not case-sensitive.
  49. //
  50. // "" or "none" return nil
  51. // "extip:77.12.33.4" will assume the local machine is reachable on the given IP
  52. // "any" uses the first auto-detected mechanism
  53. // "upnp" uses the Universal Plug and Play protocol
  54. // "pmp" uses NAT-PMP with an auto-detected gateway address
  55. // "pmp:192.168.0.1" uses NAT-PMP with the given gateway address
  56. func Parse(spec string) (Interface, error) {
  57. var (
  58. parts = strings.SplitN(spec, ":", 2)
  59. mech = strings.ToLower(parts[0])
  60. ip net.IP
  61. )
  62. if len(parts) > 1 {
  63. ip = net.ParseIP(parts[1])
  64. if ip == nil {
  65. return nil, errors.New("invalid IP address")
  66. }
  67. }
  68. switch mech {
  69. case "", "none", "off":
  70. return nil, nil
  71. case "any", "auto", "on":
  72. return Any(), nil
  73. case "extip", "ip":
  74. if ip == nil {
  75. return nil, errors.New("missing IP address")
  76. }
  77. return ExtIP(ip), nil
  78. case "upnp":
  79. return UPnP(), nil
  80. case "pmp", "natpmp", "nat-pmp":
  81. return PMP(ip), nil
  82. default:
  83. return nil, fmt.Errorf("unknown mechanism %q", parts[0])
  84. }
  85. }
  86. const (
  87. mapTimeout = 10 * time.Minute
  88. )
  89. // Map adds a port mapping on m and keeps it alive until c is closed.
  90. // This function is typically invoked in its own goroutine.
  91. func Map(m Interface, c <-chan struct{}, protocol string, extport, intport int, name string) {
  92. log := log.New("proto", protocol, "extport", extport, "intport", intport, "interface", m)
  93. refresh := time.NewTimer(mapTimeout)
  94. defer func() {
  95. refresh.Stop()
  96. log.Debug("Deleting port mapping")
  97. m.DeleteMapping(protocol, extport, intport)
  98. }()
  99. if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil {
  100. log.Debug("Couldn't add port mapping", "err", err)
  101. } else {
  102. log.Info("Mapped network port")
  103. }
  104. for {
  105. select {
  106. case _, ok := <-c:
  107. if !ok {
  108. return
  109. }
  110. case <-refresh.C:
  111. log.Trace("Refreshing port mapping")
  112. if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil {
  113. log.Debug("Couldn't add port mapping", "err", err)
  114. }
  115. refresh.Reset(mapTimeout)
  116. }
  117. }
  118. }
  119. // ExtIP assumes that the local machine is reachable on the given
  120. // external IP address, and that any required ports were mapped manually.
  121. // Mapping operations will not return an error but won't actually do anything.
  122. type ExtIP net.IP
  123. func (n ExtIP) ExternalIP() (net.IP, error) { return net.IP(n), nil }
  124. func (n ExtIP) String() string { return fmt.Sprintf("ExtIP(%v)", net.IP(n)) }
  125. // These do nothing.
  126. func (ExtIP) AddMapping(string, int, int, string, time.Duration) error { return nil }
  127. func (ExtIP) DeleteMapping(string, int, int) error { return nil }
  128. // Any returns a port mapper that tries to discover any supported
  129. // mechanism on the local network.
  130. func Any() Interface {
  131. // TODO: attempt to discover whether the local machine has an
  132. // Internet-class address. Return ExtIP in this case.
  133. return startautodisc("UPnP or NAT-PMP", func() Interface {
  134. found := make(chan Interface, 2)
  135. gopool.Submit(func() { found <- discoverUPnP() })
  136. gopool.Submit(func() { found <- discoverPMP() })
  137. for i := 0; i < cap(found); i++ {
  138. if c := <-found; c != nil {
  139. return c
  140. }
  141. }
  142. return nil
  143. })
  144. }
  145. // UPnP returns a port mapper that uses UPnP. It will attempt to
  146. // discover the address of your router using UDP broadcasts.
  147. func UPnP() Interface {
  148. return startautodisc("UPnP", discoverUPnP)
  149. }
  150. // PMP returns a port mapper that uses NAT-PMP. The provided gateway
  151. // address should be the IP of your router. If the given gateway
  152. // address is nil, PMP will attempt to auto-discover the router.
  153. func PMP(gateway net.IP) Interface {
  154. if gateway != nil {
  155. return &pmp{gw: gateway, c: natpmp.NewClient(gateway)}
  156. }
  157. return startautodisc("NAT-PMP", discoverPMP)
  158. }
  159. // autodisc represents a port mapping mechanism that is still being
  160. // auto-discovered. Calls to the Interface methods on this type will
  161. // wait until the discovery is done and then call the method on the
  162. // discovered mechanism.
  163. //
  164. // This type is useful because discovery can take a while but we
  165. // want return an Interface value from UPnP, PMP and Auto immediately.
  166. type autodisc struct {
  167. what string // type of interface being autodiscovered
  168. once sync.Once
  169. doit func() Interface
  170. mu sync.Mutex
  171. found Interface
  172. }
  173. func startautodisc(what string, doit func() Interface) Interface {
  174. // TODO: monitor network configuration and rerun doit when it changes.
  175. return &autodisc{what: what, doit: doit}
  176. }
  177. func (n *autodisc) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error {
  178. if err := n.wait(); err != nil {
  179. return err
  180. }
  181. return n.found.AddMapping(protocol, extport, intport, name, lifetime)
  182. }
  183. func (n *autodisc) DeleteMapping(protocol string, extport, intport int) error {
  184. if err := n.wait(); err != nil {
  185. return err
  186. }
  187. return n.found.DeleteMapping(protocol, extport, intport)
  188. }
  189. func (n *autodisc) ExternalIP() (net.IP, error) {
  190. if err := n.wait(); err != nil {
  191. return nil, err
  192. }
  193. return n.found.ExternalIP()
  194. }
  195. func (n *autodisc) String() string {
  196. n.mu.Lock()
  197. defer n.mu.Unlock()
  198. if n.found == nil {
  199. return n.what
  200. }
  201. return n.found.String()
  202. }
  203. // wait blocks until auto-discovery has been performed.
  204. func (n *autodisc) wait() error {
  205. n.once.Do(func() {
  206. n.mu.Lock()
  207. n.found = n.doit()
  208. n.mu.Unlock()
  209. })
  210. if n.found == nil {
  211. return fmt.Errorf("no %s router discovered", n.what)
  212. }
  213. return nil
  214. }