github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/p2p/nat/natpmp.go (about) 1 package nat 2 3 import ( 4 "fmt" 5 "net" 6 "strings" 7 "time" 8 9 "github.com/jackpal/go-nat-pmp" 10 ) 11 12 // natPMPClient adapts the NAT-PMP protocol implementation so it conforms to 13 // the common interface. 14 type pmp struct { 15 gw net.IP 16 c *natpmp.Client 17 } 18 19 func (n *pmp) String() string { 20 return fmt.Sprintf("NAT-PMP(%v)", n.gw) 21 } 22 23 func (n *pmp) ExternalIP() (net.IP, error) { 24 response, err := n.c.GetExternalAddress() 25 if err != nil { 26 return nil, err 27 } 28 return response.ExternalIPAddress[:], nil 29 } 30 31 func (n *pmp) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error { 32 if lifetime <= 0 { 33 return fmt.Errorf("lifetime must not be <= 0") 34 } 35 // Note order of port arguments is switched between our 36 // AddMapping and the client's AddPortMapping. 37 _, err := n.c.AddPortMapping(strings.ToLower(protocol), intport, extport, int(lifetime/time.Second)) 38 return err 39 } 40 41 func (n *pmp) DeleteMapping(protocol string, extport, intport int) (err error) { 42 // To destroy a mapping, send an add-port with an internalPort of 43 // the internal port to destroy, an external port of zero and a 44 // time of zero. 45 _, err = n.c.AddPortMapping(strings.ToLower(protocol), intport, 0, 0) 46 return err 47 } 48 49 func discoverPMP() Interface { 50 // run external address lookups on all potential gateways 51 gws := potentialGateways() 52 found := make(chan *pmp, len(gws)) 53 for i := range gws { 54 gw := gws[i] 55 go func() { 56 c := natpmp.NewClient(gw) 57 if _, err := c.GetExternalAddress(); err != nil { 58 found <- nil 59 } else { 60 found <- &pmp{gw, c} 61 } 62 }() 63 } 64 // return the one that responds first. 65 // discovery needs to be quick, so we stop caring about 66 // any responses after a very short timeout. 67 timeout := time.NewTimer(1 * time.Second) 68 defer timeout.Stop() 69 for _ = range gws { 70 select { 71 case c := <-found: 72 if c != nil { 73 return c 74 } 75 case <-timeout.C: 76 return nil 77 } 78 } 79 return nil 80 } 81 82 var ( 83 // LAN IP ranges 84 _, lan10, _ = net.ParseCIDR("10.0.0.0/8") 85 _, lan176, _ = net.ParseCIDR("172.16.0.0/12") 86 _, lan192, _ = net.ParseCIDR("192.168.0.0/16") 87 ) 88 89 // TODO: improve this. We currently assume that (on most networks) 90 // the router is X.X.X.1 in a local LAN range. 91 func potentialGateways() (gws []net.IP) { 92 ifaces, err := net.Interfaces() 93 if err != nil { 94 return nil 95 } 96 for _, iface := range ifaces { 97 ifaddrs, err := iface.Addrs() 98 if err != nil { 99 return gws 100 } 101 for _, addr := range ifaddrs { 102 switch x := addr.(type) { 103 case *net.IPNet: 104 if lan10.Contains(x.IP) || lan176.Contains(x.IP) || lan192.Contains(x.IP) { 105 ip := x.IP.Mask(x.Mask).To4() 106 if ip != nil { 107 ip[3] = ip[3] | 0x01 108 gws = append(gws, ip) 109 } 110 } 111 } 112 } 113 } 114 return gws 115 }