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 }