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) {}