github.com/NebulousLabs/Sia@v1.3.7/modules/gateway/gateway.go (about)

     1  // Package gateway connects a Sia node to the Sia flood network. The flood
     2  // network is used to propagate blocks and transactions. The gateway is the
     3  // primary avenue that a node uses to hear about transactions and blocks, and
     4  // is the primary avenue used to tell the network about blocks that you have
     5  // mined or about transactions that you have created.
     6  package gateway
     7  
     8  import (
     9  	"time"
    10  )
    11  
    12  // For the user to be securely connected to the network, the user must be
    13  // connected to at least one node which will send them all of the blocks. An
    14  // attacker can trick the user into thinking that a different blockchain is the
    15  // full blockchain if the user is not connected to any nodes who are seeing +
    16  // broadcasting the real chain (and instead is connected only to attacker nodes
    17  // or to nodes that are not broadcasting). This situation is called an eclipse
    18  // attack.
    19  //
    20  // Connecting to a large number of nodes increases the resiliancy of the
    21  // network, but also puts a networking burden on the nodes and can slow down
    22  // block propagation or increase orphan rates. The gateway's job is to keep the
    23  // network efficient while also protecting the user against attacks.
    24  //
    25  // The gateway keeps a list of nodes that it knows about. It uses this list to
    26  // form connections with other nodes, and then uses those connections to
    27  // participate in the flood network. The primary vector for an attacker to
    28  // achieve an eclipse attack is node list domination. If a gateway's nodelist
    29  // is heavily dominated by attacking nodes, then when the gateway chooses to
    30  // make random connections the gateway is at risk of selecting only attacker
    31  // nodes.
    32  //
    33  // The gateway defends itself from these attacks by minimizing the amount of
    34  // control that an attacker has over the node list and peer list. The first
    35  // major defense is that the gateway maintains 8 'outbound' relationships,
    36  // which means that the gateway created those relationships instead of an
    37  // attacker. If a node forms a connection to you, that node is called
    38  // 'inbound', and because it may be an attacker node, it is not trusted.
    39  // Outbound nodes can also be attacker nodes, but they are less likely to be
    40  // attacker nodes because you chose them, instead of them choosing you.
    41  //
    42  // If the gateway forms too many connections, the gateway will allow incoming
    43  // connections by kicking an existing peer. But, to limit the amount of control
    44  // that an attacker may have, only inbound peers are selected to be kicked.
    45  // Furthermore, to increase the difficulty of attack, if a new inbound
    46  // connection shares the same IP address as an existing connection, the shared
    47  // connection is the connection that gets dropped (unless that connection is a
    48  // local or outbound connection).
    49  //
    50  // Nodes are added to a peerlist in two methods. The first method is that a
    51  // gateway will ask its outbound peers for a list of nodes. If the node list is
    52  // below a certain size (see consts.go), the gateway will repeatedly ask
    53  // outbound peers to expand the list. Nodes are also added to the nodelist
    54  // after they successfully form a connection with the gateway. To limit the
    55  // attacker's ability to add nodes to the nodelist, connections are
    56  // ratelimited. An attacker with lots of IP addresses still has the ability to
    57  // fill up the nodelist, however getting 90% dominance of the nodelist requires
    58  // forming thousands of connections, which will take hours or days. By that
    59  // time, the attacked node should already have its set of outbound peers,
    60  // limiting the amount of damage that the attacker can do.
    61  //
    62  // To limit DNS-based tomfoolry, nodes are only added to the nodelist if their
    63  // connection information takes the form of an IP address.
    64  //
    65  // Some research has been done on Bitcoin's flood networks. The more relevant
    66  // research has been listed below. The papers listed first are more relevant.
    67  //     Eclipse Attacks on Bitcoin's Peer-to-Peer Network (Heilman, Kendler, Zohar, Goldberg)
    68  //     Stubborn Mining: Generalizing Selfish Mining and Combining with an Eclipse Attack (Nayak, Kumar, Miller, Shi)
    69  //     An Overview of BGP Hijacking (https://www.bishopfox.com/blog/2015/08/an-overview-of-bgp-hijacking/)
    70  
    71  // TODO: Currently the gateway does not do much in terms of bucketing. The
    72  // gateway should make sure that it has outbound peers from a wide range of IP
    73  // addresses, and when kicking inbound peers it shouldn't just favor kicking
    74  // peers of the same IP address, it should favor kicking peers of the same ip
    75  // address range.
    76  //
    77  // TODO: There is no public key exchange, so communications cannot be
    78  // effectively encrypted or authenticated.
    79  //
    80  // TODO: Gateway hostname discovery currently has significant centralization,
    81  // namely the fallback is a single third-party website that can easily form any
    82  // response it wants. Instead, multiple TLS-protected third party websites
    83  // should be used, and the plurality answer should be accepted as the true
    84  // hostname.
    85  //
    86  // TODO: The gateway currently does hostname discovery in a non-blocking way,
    87  // which means that the first few peers that it connects to may not get the
    88  // correct hostname. This means that you may give the remote peer the wrong
    89  // hostname, which means they will not be able to dial you back, which means
    90  // they will not add you to their node list.
    91  //
    92  // TODO: The gateway should encrypt and authenticate all communications. Though
    93  // the gateway participates in a flood network, practical attacks have been
    94  // demonstrated which have been able to confuse nodes by manipulating messages
    95  // from their peers. Encryption + authentication would have made the attack
    96  // more difficult.
    97  
    98  import (
    99  	"errors"
   100  	"fmt"
   101  	"net"
   102  	"os"
   103  	"path/filepath"
   104  	"sync"
   105  
   106  	"github.com/NebulousLabs/Sia/modules"
   107  	"github.com/NebulousLabs/Sia/persist"
   108  	siasync "github.com/NebulousLabs/Sia/sync"
   109  	"github.com/NebulousLabs/fastrand"
   110  )
   111  
   112  var (
   113  	errNoPeers     = errors.New("no peers")
   114  	errUnreachable = errors.New("peer did not respond to ping")
   115  )
   116  
   117  // Gateway implements the modules.Gateway interface.
   118  type Gateway struct {
   119  	listener net.Listener
   120  	myAddr   modules.NetAddress
   121  	port     string
   122  
   123  	// handlers are the RPCs that the Gateway can handle.
   124  	//
   125  	// initRPCs are the RPCs that the Gateway calls upon connecting to a peer.
   126  	handlers map[rpcID]modules.RPCFunc
   127  	initRPCs map[string]modules.RPCFunc
   128  
   129  	// nodes is the set of all known nodes (i.e. potential peers).
   130  	//
   131  	// peers are the nodes that the gateway is currently connected to.
   132  	//
   133  	// peerTG is a special thread group for tracking peer connections, and will
   134  	// block shutdown until all peer connections have been closed out. The peer
   135  	// connections are put in a separate TG because of their unique
   136  	// requirements - they have the potential to live for the lifetime of the
   137  	// program, but also the potential to close early. Calling threads.OnStop
   138  	// for each peer could create a huge backlog of functions that do nothing
   139  	// (because most of the peers disconnected prior to shutdown). And they
   140  	// can't call threads.Add because they are potentially very long running
   141  	// and would block any threads.Flush() calls. So a second threadgroup is
   142  	// added which handles clean-shutdown for the peers, without blocking
   143  	// threads.Flush() calls.
   144  	nodes  map[modules.NetAddress]*node
   145  	peers  map[modules.NetAddress]*peer
   146  	peerTG siasync.ThreadGroup
   147  
   148  	// Utilities.
   149  	log        *persist.Logger
   150  	mu         sync.RWMutex
   151  	persistDir string
   152  	threads    siasync.ThreadGroup
   153  
   154  	// Unique ID
   155  	staticId gatewayID
   156  }
   157  
   158  type gatewayID [8]byte
   159  
   160  // managedSleep will sleep for the given period of time. If the full time
   161  // elapses, 'true' is returned. If the sleep is interrupted for shutdown,
   162  // 'false' is returned.
   163  func (g *Gateway) managedSleep(t time.Duration) (completed bool) {
   164  	select {
   165  	case <-time.After(t):
   166  		return true
   167  	case <-g.threads.StopChan():
   168  		return false
   169  	}
   170  }
   171  
   172  // Address returns the NetAddress of the Gateway.
   173  func (g *Gateway) Address() modules.NetAddress {
   174  	g.mu.RLock()
   175  	defer g.mu.RUnlock()
   176  	return g.myAddr
   177  }
   178  
   179  // Close saves the state of the Gateway and stops its listener process.
   180  func (g *Gateway) Close() error {
   181  	if err := g.threads.Stop(); err != nil {
   182  		return err
   183  	}
   184  	g.mu.Lock()
   185  	defer g.mu.Unlock()
   186  	return g.saveSync()
   187  }
   188  
   189  // New returns an initialized Gateway.
   190  func New(addr string, bootstrap bool, persistDir string) (*Gateway, error) {
   191  	// Create the directory if it doesn't exist.
   192  	err := os.MkdirAll(persistDir, 0700)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	g := &Gateway{
   198  		handlers: make(map[rpcID]modules.RPCFunc),
   199  		initRPCs: make(map[string]modules.RPCFunc),
   200  
   201  		nodes: make(map[modules.NetAddress]*node),
   202  		peers: make(map[modules.NetAddress]*peer),
   203  
   204  		persistDir: persistDir,
   205  	}
   206  
   207  	// Set Unique GatewayID
   208  	fastrand.Read(g.staticId[:])
   209  
   210  	// Create the logger.
   211  	g.log, err = persist.NewFileLogger(filepath.Join(g.persistDir, logFile))
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	// Establish the closing of the logger.
   216  	g.threads.AfterStop(func() {
   217  		if err := g.log.Close(); err != nil {
   218  			// The logger may or may not be working here, so use a println
   219  			// instead.
   220  			fmt.Println("Failed to close the gateway logger:", err)
   221  		}
   222  	})
   223  	g.log.Println("INFO: gateway created, started logging")
   224  
   225  	// Establish that the peerTG must complete shutdown before the primary
   226  	// thread group completes shutdown.
   227  	g.threads.OnStop(func() {
   228  		err = g.peerTG.Stop()
   229  		if err != nil {
   230  			g.log.Println("ERROR: peerTG experienced errors while shutting down:", err)
   231  		}
   232  	})
   233  
   234  	// Register RPCs.
   235  	g.RegisterRPC("ShareNodes", g.shareNodes)
   236  	g.RegisterRPC("DiscoverIP", g.discoverPeerIP)
   237  	g.RegisterConnectCall("ShareNodes", g.requestNodes)
   238  	// Establish the de-registration of the RPCs.
   239  	g.threads.OnStop(func() {
   240  		g.UnregisterRPC("ShareNodes")
   241  		g.UnregisterRPC("DiscoverIP")
   242  		g.UnregisterConnectCall("ShareNodes")
   243  	})
   244  
   245  	// Load the old node list. If it doesn't exist, no problem, but if it does,
   246  	// we want to know about any errors preventing us from loading it.
   247  	if loadErr := g.load(); loadErr != nil && !os.IsNotExist(loadErr) {
   248  		return nil, loadErr
   249  	}
   250  	// Spawn the thread to periodically save the gateway.
   251  	go g.threadedSaveLoop()
   252  	// Make sure that the gateway saves after shutdown.
   253  	g.threads.AfterStop(func() {
   254  		g.mu.Lock()
   255  		err = g.saveSync()
   256  		g.mu.Unlock()
   257  		if err != nil {
   258  			g.log.Println("ERROR: Unable to save gateway:", err)
   259  		}
   260  	})
   261  
   262  	// Add the bootstrap peers to the node list.
   263  	if bootstrap {
   264  		for _, addr := range modules.BootstrapPeers {
   265  			err := g.addNode(addr)
   266  			if err != nil && err != errNodeExists {
   267  				g.log.Printf("WARN: failed to add the bootstrap node '%v': %v", addr, err)
   268  			}
   269  		}
   270  	}
   271  
   272  	// Create the listener which will listen for new connections from peers.
   273  	permanentListenClosedChan := make(chan struct{})
   274  	g.listener, err = net.Listen("tcp", addr)
   275  	if err != nil {
   276  		return nil, err
   277  	}
   278  	// Automatically close the listener when g.threads.Stop() is called.
   279  	g.threads.OnStop(func() {
   280  		err := g.listener.Close()
   281  		if err != nil {
   282  			g.log.Println("WARN: closing the listener failed:", err)
   283  		}
   284  		<-permanentListenClosedChan
   285  	})
   286  	// Set the address and port of the gateway.
   287  	host, port, err := net.SplitHostPort(g.listener.Addr().String())
   288  	g.port = port
   289  	if err != nil {
   290  		return nil, err
   291  	}
   292  
   293  	if ip := net.ParseIP(host); ip.IsUnspecified() && ip != nil {
   294  		// if host is unspecified, set a dummy one for now.
   295  		host = "localhost"
   296  	}
   297  
   298  	// Set myAddr equal to the address returned by the listener. It will be
   299  	// overwritten by threadedLearnHostname later on.
   300  	g.myAddr = modules.NetAddress(net.JoinHostPort(host, port))
   301  
   302  	// Spawn the peer connection listener.
   303  	go g.permanentListen(permanentListenClosedChan)
   304  
   305  	// Spawn the peer manager and provide tools for ensuring clean shutdown.
   306  	peerManagerClosedChan := make(chan struct{})
   307  	g.threads.OnStop(func() {
   308  		<-peerManagerClosedChan
   309  	})
   310  	go g.permanentPeerManager(peerManagerClosedChan)
   311  
   312  	// Spawn the node manager and provide tools for ensuring clean shudown.
   313  	nodeManagerClosedChan := make(chan struct{})
   314  	g.threads.OnStop(func() {
   315  		<-nodeManagerClosedChan
   316  	})
   317  	go g.permanentNodeManager(nodeManagerClosedChan)
   318  
   319  	// Spawn the node purger and provide tools for ensuring clean shutdown.
   320  	nodePurgerClosedChan := make(chan struct{})
   321  	g.threads.OnStop(func() {
   322  		<-nodePurgerClosedChan
   323  	})
   324  	go g.permanentNodePurger(nodePurgerClosedChan)
   325  
   326  	// Spawn threads to take care of port forwarding and hostname discovery.
   327  	go g.threadedForwardPort(g.port)
   328  	go g.threadedLearnHostname()
   329  
   330  	return g, nil
   331  }
   332  
   333  // enforce that Gateway satisfies the modules.Gateway interface
   334  var _ modules.Gateway = (*Gateway)(nil)