github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/nat/mapping/port_mapping.go (about) 1 /* 2 * Copyright (C) 2019 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU 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 * This program 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 General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package mapping 19 20 import ( 21 "errors" 22 "net" 23 "time" 24 25 portmap "github.com/ethereum/go-ethereum/p2p/nat" 26 "github.com/mysteriumnetwork/node/eventbus" 27 "github.com/mysteriumnetwork/node/nat/event" 28 "github.com/rs/zerolog/log" 29 ) 30 31 // StageName is used to indicate port mapping NAT traversal stage 32 const StageName = "port_mapping" 33 34 // DefaultConfig returns default port mapping config. 35 func DefaultConfig() *Config { 36 return &Config{ 37 MapInterface: portmap.Any(), 38 MapLifetime: 20 * time.Minute, 39 MapUpdateInterval: 15 * time.Minute, 40 } 41 } 42 43 // Config represents port mapping config. 44 type Config struct { 45 MapInterface portmap.Interface 46 MapLifetime time.Duration 47 MapUpdateInterval time.Duration 48 } 49 50 // PortMapper tries to map port using router's uPnP or NAT-PMP depending on given config map interface. 51 type PortMapper interface { 52 // Map maps port for given protocol. It returns release func which 53 // must be called when port no longer needed and ok which is true if 54 // port mapping was successful. 55 Map(id, protocol string, port int, name string) (release func(), ok bool) 56 } 57 58 // NewPortMapper returns port mapper instance. 59 func NewPortMapper(config *Config, publisher eventbus.Publisher) PortMapper { 60 return &portMapper{ 61 config: config, 62 publisher: publisher, 63 } 64 } 65 66 type portMapper struct { 67 config *Config 68 publisher eventbus.Publisher 69 } 70 71 func (p *portMapper) Map(id, protocol string, port int, name string) (release func(), ok bool) { 72 if !p.routerIPPublic() { 73 err := errors.New("failed to find router public IP") 74 log.Info().Err(err).Msg("Port mapping is useless, skipping it.") 75 p.notify(id, err) 76 77 return nil, false 78 } 79 80 // Try add mapping first to determine if it is supported and 81 // if permanent lease only is supported. 82 permanent, err := p.addMapping(protocol, port, port, name) 83 p.notify(id, err) 84 if err != nil { 85 return nil, false 86 } 87 88 // If only permanent lease is supported we don't need to update it in intervals. 89 if permanent { 90 return func() { p.deleteMapping(protocol, port, port) }, true 91 } 92 93 stopUpdate := make(chan struct{}) 94 go func() { 95 for { 96 select { 97 case <-stopUpdate: 98 return 99 case <-time.After(p.config.MapUpdateInterval): 100 _, err := p.addMapping(protocol, port, port, name) 101 p.notify(id, err) 102 } 103 } 104 }() 105 106 return func() { 107 p.deleteMapping(protocol, port, port) 108 close(stopUpdate) 109 }, true 110 } 111 112 func (p *portMapper) routerIPPublic() bool { 113 ip, err := p.config.MapInterface.ExternalIP() 114 if err != nil { 115 log.Warn().Err(err).Msg("Couldn't detect router IP address") 116 return false 117 } 118 119 log.Debug().Msgf("Detected router public IP address: %s", ip) 120 121 for _, s := range []string{ 122 "10.0.0.0/8", 123 "172.16.0.0/12", 124 "192.168.0.0/16", 125 "100.64.0.0/10", 126 "127.0.0.0/8", 127 "169.254.0.0/16", 128 } { 129 _, subnet, _ := net.ParseCIDR(s) 130 if subnet.Contains(ip) { 131 return false 132 } 133 } 134 135 return true 136 } 137 138 func (p *portMapper) notify(id string, err error) { 139 if err != nil { 140 p.publisher.Publish(event.AppTopicTraversal, event.BuildFailureEvent(id, StageName, err)) 141 } else { 142 p.publisher.Publish(event.AppTopicTraversal, event.BuildSuccessfulEvent(id, StageName)) 143 } 144 } 145 146 func (p *portMapper) addMapping(protocol string, extPort, intPort int, name string) (permanent bool, err error) { 147 if _, err := p.config.MapInterface.AddMapping(protocol, extPort, intPort, name, p.config.MapLifetime); err != nil { 148 log.Warn().Err(err).Msgf("Couldn't add port mapping for port %d: retrying with permanent lease", extPort) 149 if _, err := p.config.MapInterface.AddMapping(protocol, extPort, intPort, name, 0); err != nil { 150 // some gateways support only permanent leases 151 log.Warn().Err(err).Msgf("Couldn't add port mapping for port %d", extPort) 152 return false, err 153 } 154 return true, nil 155 } 156 log.Info().Msgf("Mapped network port: %d", extPort) 157 return false, nil 158 } 159 160 func (p *portMapper) deleteMapping(protocol string, extPort, intPort int) { 161 log.Debug().Msgf("Deleting port mapping for port: %d", extPort) 162 if err := p.config.MapInterface.DeleteMapping(protocol, extPort, intPort); err != nil { 163 log.Warn().Err(err).Msg("Couldn't delete port mapping") 164 } 165 }