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