nat.go 7.2 KB

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