github.com/klaytn/klaytn@v1.12.1/networks/p2p/nat/natpmp.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from p2p/nat/natpmp.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package nat
    22  
    23  import (
    24  	"fmt"
    25  	"net"
    26  	"strings"
    27  	"time"
    28  
    29  	natpmp "github.com/jackpal/go-nat-pmp"
    30  )
    31  
    32  // natPMPClient adapts the NAT-PMP protocol implementation so it conforms to
    33  // the common interface.
    34  type pmp struct {
    35  	gw net.IP
    36  	c  *natpmp.Client
    37  }
    38  
    39  func (n *pmp) String() string {
    40  	return fmt.Sprintf("NAT-PMP(%v)", n.gw)
    41  }
    42  
    43  func (n *pmp) ExternalIP() (net.IP, error) {
    44  	response, err := n.c.GetExternalAddress()
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	return response.ExternalIPAddress[:], nil
    49  }
    50  
    51  func (n *pmp) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error {
    52  	if lifetime <= 0 {
    53  		return fmt.Errorf("lifetime must not be <= 0")
    54  	}
    55  	// Note order of port arguments is switched between our
    56  	// AddMapping and the client's AddPortMapping.
    57  	_, err := n.c.AddPortMapping(strings.ToLower(protocol), intport, extport, int(lifetime/time.Second))
    58  	return err
    59  }
    60  
    61  func (n *pmp) DeleteMapping(protocol string, extport, intport int) (err error) {
    62  	// To destroy a mapping, send an add-port with an internalPort of
    63  	// the internal port to destroy, an external port of zero and a
    64  	// time of zero.
    65  	_, err = n.c.AddPortMapping(strings.ToLower(protocol), intport, 0, 0)
    66  	return err
    67  }
    68  
    69  func discoverPMP() Interface {
    70  	// run external address lookups on all potential gateways
    71  	gws := potentialGateways()
    72  	found := make(chan *pmp, len(gws))
    73  	for i := range gws {
    74  		gw := gws[i]
    75  		go func() {
    76  			c := natpmp.NewClient(gw)
    77  			if _, err := c.GetExternalAddress(); err != nil {
    78  				found <- nil
    79  			} else {
    80  				found <- &pmp{gw, c}
    81  			}
    82  		}()
    83  	}
    84  	// return the one that responds first.
    85  	// discovery needs to be quick, so we stop caring about
    86  	// any responses after a very short timeout.
    87  	timeout := time.NewTimer(1 * time.Second)
    88  	defer timeout.Stop()
    89  	for range gws {
    90  		select {
    91  		case c := <-found:
    92  			if c != nil {
    93  				return c
    94  			}
    95  		case <-timeout.C:
    96  			return nil
    97  		}
    98  	}
    99  	return nil
   100  }
   101  
   102  var (
   103  	// LAN IP ranges
   104  	_, lan10, _  = net.ParseCIDR("10.0.0.0/8")
   105  	_, lan176, _ = net.ParseCIDR("172.16.0.0/12")
   106  	_, lan192, _ = net.ParseCIDR("192.168.0.0/16")
   107  )
   108  
   109  // TODO: improve this. We currently assume that (on most networks)
   110  // the router is X.X.X.1 in a local LAN range.
   111  func potentialGateways() (gws []net.IP) {
   112  	ifaces, err := net.Interfaces()
   113  	if err != nil {
   114  		return nil
   115  	}
   116  	for _, iface := range ifaces {
   117  		ifaddrs, err := iface.Addrs()
   118  		if err != nil {
   119  			return gws
   120  		}
   121  		for _, addr := range ifaddrs {
   122  			switch x := addr.(type) {
   123  			case *net.IPNet:
   124  				if lan10.Contains(x.IP) || lan176.Contains(x.IP) || lan192.Contains(x.IP) {
   125  					ip := x.IP.Mask(x.Mask).To4()
   126  					if ip != nil {
   127  						ip[3] = ip[3] | 0x01
   128  						gws = append(gws, ip)
   129  					}
   130  				}
   131  			}
   132  		}
   133  	}
   134  	return gws
   135  }