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  }