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  }