github.com/theQRL/go-zond@v0.1.1/p2p/server_nat.go (about) 1 // Copyright 2023 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 17 package p2p 18 19 import ( 20 "net" 21 "time" 22 23 "github.com/theQRL/go-zond/common/mclock" 24 "github.com/theQRL/go-zond/log" 25 "github.com/theQRL/go-zond/p2p/enr" 26 "github.com/theQRL/go-zond/p2p/nat" 27 ) 28 29 const ( 30 portMapDuration = 10 * time.Minute 31 portMapRefreshInterval = 8 * time.Minute 32 portMapRetryInterval = 5 * time.Minute 33 extipRetryInterval = 2 * time.Minute 34 ) 35 36 type portMapping struct { 37 protocol string 38 name string 39 port int 40 41 // for use by the portMappingLoop goroutine: 42 extPort int // the mapped port returned by the NAT interface 43 nextTime mclock.AbsTime 44 } 45 46 // setupPortMapping starts the port mapping loop if necessary. 47 // Note: this needs to be called after the LocalNode instance has been set on the server. 48 func (srv *Server) setupPortMapping() { 49 // portMappingRegister will receive up to two values: one for the TCP port if 50 // listening is enabled, and one more for enabling UDP port mapping if discovery is 51 // enabled. We make it buffered to avoid blocking setup while a mapping request is in 52 // progress. 53 srv.portMappingRegister = make(chan *portMapping, 2) 54 55 switch srv.NAT.(type) { 56 case nil: 57 // No NAT interface configured. 58 srv.loopWG.Add(1) 59 go srv.consumePortMappingRequests() 60 61 case nat.ExtIP: 62 // ExtIP doesn't block, set the IP right away. 63 ip, _ := srv.NAT.ExternalIP() 64 srv.localnode.SetStaticIP(ip) 65 srv.loopWG.Add(1) 66 go srv.consumePortMappingRequests() 67 68 default: 69 srv.loopWG.Add(1) 70 go srv.portMappingLoop() 71 } 72 } 73 74 func (srv *Server) consumePortMappingRequests() { 75 defer srv.loopWG.Done() 76 for { 77 select { 78 case <-srv.quit: 79 return 80 case <-srv.portMappingRegister: 81 } 82 } 83 } 84 85 // portMappingLoop manages port mappings for UDP and TCP. 86 func (srv *Server) portMappingLoop() { 87 defer srv.loopWG.Done() 88 89 newLogger := func(p string, e int, i int) log.Logger { 90 return log.New("proto", p, "extport", e, "intport", i, "interface", srv.NAT) 91 } 92 93 var ( 94 mappings = make(map[string]*portMapping, 2) 95 refresh = mclock.NewAlarm(srv.clock) 96 extip = mclock.NewAlarm(srv.clock) 97 lastExtIP net.IP 98 ) 99 extip.Schedule(srv.clock.Now()) 100 defer func() { 101 refresh.Stop() 102 extip.Stop() 103 for _, m := range mappings { 104 if m.extPort != 0 { 105 log := newLogger(m.protocol, m.extPort, m.port) 106 log.Debug("Deleting port mapping") 107 srv.NAT.DeleteMapping(m.protocol, m.extPort, m.port) 108 } 109 } 110 }() 111 112 for { 113 // Schedule refresh of existing mappings. 114 for _, m := range mappings { 115 refresh.Schedule(m.nextTime) 116 } 117 118 select { 119 case <-srv.quit: 120 return 121 122 case <-extip.C(): 123 extip.Schedule(srv.clock.Now().Add(extipRetryInterval)) 124 ip, err := srv.NAT.ExternalIP() 125 if err != nil { 126 log.Debug("Couldn't get external IP", "err", err, "interface", srv.NAT) 127 } else if !ip.Equal(lastExtIP) { 128 log.Debug("External IP changed", "ip", extip, "interface", srv.NAT) 129 } else { 130 return 131 } 132 // Here, we either failed to get the external IP, or it has changed. 133 lastExtIP = ip 134 srv.localnode.SetStaticIP(ip) 135 // Ensure port mappings are refreshed in case we have moved to a new network. 136 for _, m := range mappings { 137 m.nextTime = srv.clock.Now() 138 } 139 140 case m := <-srv.portMappingRegister: 141 if m.protocol != "TCP" && m.protocol != "UDP" { 142 panic("unknown NAT protocol name: " + m.protocol) 143 } 144 mappings[m.protocol] = m 145 m.nextTime = srv.clock.Now() 146 147 case <-refresh.C(): 148 for _, m := range mappings { 149 if srv.clock.Now() < m.nextTime { 150 continue 151 } 152 153 external := m.port 154 if m.extPort != 0 { 155 external = m.extPort 156 } 157 log := newLogger(m.protocol, external, m.port) 158 159 log.Trace("Attempting port mapping") 160 p, err := srv.NAT.AddMapping(m.protocol, external, m.port, m.name, portMapDuration) 161 if err != nil { 162 log.Debug("Couldn't add port mapping", "err", err) 163 m.extPort = 0 164 m.nextTime = srv.clock.Now().Add(portMapRetryInterval) 165 continue 166 } 167 // It was mapped! 168 m.extPort = int(p) 169 m.nextTime = srv.clock.Now().Add(portMapRefreshInterval) 170 if external != m.extPort { 171 log = newLogger(m.protocol, m.extPort, m.port) 172 log.Info("NAT mapped alternative port") 173 } else { 174 log.Info("NAT mapped port") 175 } 176 177 // Update port in local ENR. 178 switch m.protocol { 179 case "TCP": 180 srv.localnode.Set(enr.TCP(m.extPort)) 181 case "UDP": 182 srv.localnode.SetFallbackUDP(m.extPort) 183 } 184 } 185 } 186 } 187 }