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 }