github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/nat/pmp.go (about)

     1  package nat
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"time"
     8  
     9  	"github.com/jackpal/gateway"
    10  	natpmp "github.com/jackpal/go-nat-pmp"
    11  	"github.com/mit-dci/lit/logging"
    12  )
    13  
    14  var (
    15  	// private24BitBlock contains the set of private IPv4 addresses within
    16  	// the 10.0.0.0/8 adddress space.
    17  	private24BitBlock *net.IPNet
    18  
    19  	// private20BitBlock contains the set of private IPv4 addresses within
    20  	// the 172.16.0.0/12 address space.
    21  	private20BitBlock *net.IPNet
    22  
    23  	// private16BitBlock contains the set of private IPv4 addresses within
    24  	// the 192.168.0.0/16 address space.
    25  	private16BitBlock *net.IPNet
    26  
    27  	// ErrMultipleNAT is an error returned when multiple NATs have been
    28  	// detected.
    29  	ErrMultipleNAT = errors.New("multiple NATs detected")
    30  )
    31  
    32  func init() {
    33  	_, private24BitBlock, _ = net.ParseCIDR("10.0.0.0/8")
    34  	_, private20BitBlock, _ = net.ParseCIDR("172.16.0.0/12")
    35  	_, private16BitBlock, _ = net.ParseCIDR("192.168.0.0/16")
    36  }
    37  
    38  // ExternalIP returns the external IP address of the NAT-PMP enabled device.
    39  // cant define this as a method of natpmp without definign a seaprate s truct
    40  func ExternalIP(p *natpmp.Client) (net.IP, error) {
    41  	res, err := p.GetExternalAddress()
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	ip := net.IP(res.ExternalIPAddress[:])
    47  	if isPrivateIP(ip) {
    48  		return nil, fmt.Errorf("multiple NATs detected")
    49  	}
    50  
    51  	return ip, nil
    52  }
    53  
    54  // isPrivateIP determines if the IP is private.
    55  func isPrivateIP(ip net.IP) bool {
    56  	return private24BitBlock.Contains(ip) ||
    57  		private20BitBlock.Contains(ip) || private16BitBlock.Contains(ip)
    58  }
    59  
    60  // within the given timeout.
    61  func SetupPmp(timeout time.Duration, port uint16) (*natpmp.Client, error) {
    62  	var err error
    63  	// Retrieve the gateway IP address of the local network.
    64  	gatewayIP, err := gateway.DiscoverGateway()
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	pmp := natpmp.NewClientWithTimeout(gatewayIP, timeout)
    70  
    71  	// We'll then attempt to retrieve the external IP address of this
    72  	// device to ensure it is not behind multiple NATs.
    73  
    74  	ip, err := ExternalIP(pmp)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	logging.Infof("Your external IP is %s", ip)
    79  	_, err = pmp.AddPortMapping("tcp", int(port), int(port), 0)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	return pmp, nil
    85  }