github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/p2p/host/basic/natmgr.go (about) 1 package basichost 2 3 import ( 4 "sync" 5 6 ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" 7 goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess" 8 context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" 9 10 inat "github.com/ipfs/go-ipfs/p2p/nat" 11 inet "github.com/ipfs/go-ipfs/p2p/net" 12 lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables" 13 ) 14 15 // natManager takes care of adding + removing port mappings to the nat. 16 // Initialized with the host if it has a NATPortMap option enabled. 17 // natManager receives signals from the network, and check on nat mappings: 18 // * natManager listens to the network and adds or closes port mappings 19 // as the network signals Listen() or ListenClose(). 20 // * closing the natManager closes the nat and its mappings. 21 type natManager struct { 22 host *BasicHost 23 natmu sync.RWMutex // guards nat (ready could obviate this mutex, but safety first.) 24 nat *inat.NAT 25 26 ready chan struct{} // closed once the nat is ready to process port mappings 27 proc goprocess.Process // natManager has a process + children. can be closed. 28 } 29 30 func newNatManager(host *BasicHost) *natManager { 31 nmgr := &natManager{ 32 host: host, 33 ready: make(chan struct{}), 34 proc: goprocess.WithParent(host.proc), 35 } 36 37 // teardown 38 nmgr.proc = goprocess.WithTeardown(func() error { 39 // on closing, unregister from network notifications. 40 host.Network().StopNotify((*nmgrNetNotifiee)(nmgr)) 41 return nil 42 }) 43 44 // host is our parent. close when host closes. 45 host.proc.AddChild(nmgr.proc) 46 47 // discover the nat. 48 nmgr.discoverNAT() 49 return nmgr 50 } 51 52 // Close closes the natManager, closing the underlying nat 53 // and unregistering from network events. 54 func (nmgr *natManager) Close() error { 55 return nmgr.proc.Close() 56 } 57 58 // Ready returns a channel which will be closed when the NAT has been found 59 // and is ready to be used, or the search process is done. 60 func (nmgr *natManager) Ready() <-chan struct{} { 61 return nmgr.ready 62 } 63 64 func (nmgr *natManager) discoverNAT() { 65 66 nmgr.proc.Go(func(worker goprocess.Process) { 67 // inat.DiscoverNAT blocks until the nat is found or a timeout 68 // is reached. we unfortunately cannot specify timeouts-- the 69 // library we're using just blocks. 70 // 71 // Note: on early shutdown, there may be a case where we're trying 72 // to close before DiscoverNAT() returns. Since we cant cancel it 73 // (library) we can choose to (1) drop the result and return early, 74 // or (2) wait until it times out to exit. For now we choose (2), 75 // to avoid leaking resources in a non-obvious way. the only case 76 // this affects is when the daemon is being started up and _immediately_ 77 // asked to close. other services are also starting up, so ok to wait. 78 discoverdone := make(chan struct{}) 79 var nat *inat.NAT 80 go func() { 81 defer close(discoverdone) 82 nat = inat.DiscoverNAT() 83 }() 84 85 // by this point -- after finding the NAT -- we may have already 86 // be closing. if so, just exit. 87 select { 88 case <-worker.Closing(): 89 return 90 case <-discoverdone: 91 if nat == nil { // no nat, or failed to get it. 92 return 93 } 94 } 95 96 // wire up the nat to close when nmgr closes. 97 // nmgr.proc is our parent, and waiting for us. 98 nmgr.proc.AddChild(nat.Process()) 99 100 // set the nat. 101 nmgr.natmu.Lock() 102 nmgr.nat = nat 103 nmgr.natmu.Unlock() 104 105 // signal that we're ready to process nat mappings: 106 close(nmgr.ready) 107 108 // sign natManager up for network notifications 109 // we need to sign up here to avoid missing some notifs 110 // before the NAT has been found. 111 nmgr.host.Network().Notify((*nmgrNetNotifiee)(nmgr)) 112 113 // if any interfaces were brought up while we were setting up 114 // the nat, now is the time to setup port mappings for them. 115 // we release ready, then grab them to avoid losing any. adding 116 // a port mapping is idempotent, so its ok to add the same twice. 117 addrs := nmgr.host.Network().ListenAddresses() 118 for _, addr := range addrs { 119 // we do it async because it's slow and we may want to close beforehand 120 go addPortMapping(nmgr, addr) 121 } 122 }) 123 } 124 125 // NAT returns the natManager's nat object. this may be nil, if 126 // (a) the search process is still ongoing, or (b) the search process 127 // found no nat. Clients must check whether the return value is nil. 128 func (nmgr *natManager) NAT() *inat.NAT { 129 nmgr.natmu.Lock() 130 defer nmgr.natmu.Unlock() 131 return nmgr.nat 132 } 133 134 func addPortMapping(nmgr *natManager, intaddr ma.Multiaddr) { 135 nat := nmgr.NAT() 136 if nat == nil { 137 panic("natManager addPortMapping called without a nat.") 138 } 139 140 // first, check if the port mapping already exists. 141 for _, mapping := range nat.Mappings() { 142 if mapping.InternalAddr().Equal(intaddr) { 143 return // it exists! return. 144 } 145 } 146 147 ctx := context.TODO() 148 lm := make(lgbl.DeferredMap) 149 lm["internalAddr"] = func() interface{} { return intaddr.String() } 150 151 defer log.EventBegin(ctx, "natMgrAddPortMappingWait", lm).Done() 152 153 select { 154 case <-nmgr.proc.Closing(): 155 lm["outcome"] = "cancelled" 156 return // no use. 157 case <-nmgr.ready: // wait until it's ready. 158 } 159 160 // actually start the port map (sub-event because waiting may take a while) 161 defer log.EventBegin(ctx, "natMgrAddPortMapping", lm).Done() 162 163 // get the nat 164 m, err := nat.NewMapping(intaddr) 165 if err != nil { 166 lm["outcome"] = "failure" 167 lm["error"] = err 168 return 169 } 170 171 extaddr, err := m.ExternalAddr() 172 if err != nil { 173 lm["outcome"] = "failure" 174 lm["error"] = err 175 return 176 } 177 178 lm["outcome"] = "success" 179 lm["externalAddr"] = func() interface{} { return extaddr.String() } 180 log.Infof("established nat port mapping: %s <--> %s", intaddr, extaddr) 181 } 182 183 func rmPortMapping(nmgr *natManager, intaddr ma.Multiaddr) { 184 nat := nmgr.NAT() 185 if nat == nil { 186 panic("natManager rmPortMapping called without a nat.") 187 } 188 189 // list the port mappings (it may be gone on it's own, so we need to 190 // check this list, and not store it ourselves behind the scenes) 191 192 // close mappings for this internal address. 193 for _, mapping := range nat.Mappings() { 194 if mapping.InternalAddr().Equal(intaddr) { 195 mapping.Close() 196 } 197 } 198 } 199 200 // nmgrNetNotifiee implements the network notification listening part 201 // of the natManager. this is merely listening to Listen() and ListenClose() 202 // events. 203 type nmgrNetNotifiee natManager 204 205 func (nn *nmgrNetNotifiee) natManager() *natManager { 206 return (*natManager)(nn) 207 } 208 209 func (nn *nmgrNetNotifiee) Listen(n inet.Network, addr ma.Multiaddr) { 210 if nn.natManager().NAT() == nil { 211 return // not ready or doesnt exist. 212 } 213 214 addPortMapping(nn.natManager(), addr) 215 } 216 217 func (nn *nmgrNetNotifiee) ListenClose(n inet.Network, addr ma.Multiaddr) { 218 if nn.natManager().NAT() == nil { 219 return // not ready or doesnt exist. 220 } 221 222 rmPortMapping(nn.natManager(), addr) 223 } 224 225 func (nn *nmgrNetNotifiee) Connected(inet.Network, inet.Conn) {} 226 func (nn *nmgrNetNotifiee) Disconnected(inet.Network, inet.Conn) {} 227 func (nn *nmgrNetNotifiee) OpenedStream(inet.Network, inet.Stream) {} 228 func (nn *nmgrNetNotifiee) ClosedStream(inet.Network, inet.Stream) {}