gitlab.com/jokerrs1/Sia@v1.3.2/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 id 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.id[:]) 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.RegisterConnectCall("ShareNodes", g.requestNodes) 237 // Establish the de-registration of the RPCs. 238 g.threads.OnStop(func() { 239 g.UnregisterRPC("ShareNodes") 240 g.UnregisterConnectCall("ShareNodes") 241 }) 242 243 // Load the old node list. If it doesn't exist, no problem, but if it does, 244 // we want to know about any errors preventing us from loading it. 245 if loadErr := g.load(); loadErr != nil && !os.IsNotExist(loadErr) { 246 return nil, loadErr 247 } 248 // Spawn the thread to periodically save the gateway. 249 go g.threadedSaveLoop() 250 // Make sure that the gateway saves after shutdown. 251 g.threads.AfterStop(func() { 252 g.mu.Lock() 253 err = g.saveSync() 254 g.mu.Unlock() 255 if err != nil { 256 g.log.Println("ERROR: Unable to save gateway:", err) 257 } 258 }) 259 260 // Add the bootstrap peers to the node list. 261 if bootstrap { 262 for _, addr := range modules.BootstrapPeers { 263 err := g.addNode(addr) 264 if err != nil && err != errNodeExists { 265 g.log.Printf("WARN: failed to add the bootstrap node '%v': %v", addr, err) 266 } 267 } 268 } 269 270 // Create the listener which will listen for new connections from peers. 271 permanentListenClosedChan := make(chan struct{}) 272 g.listener, err = net.Listen("tcp", addr) 273 if err != nil { 274 return nil, err 275 } 276 // Automatically close the listener when g.threads.Stop() is called. 277 g.threads.OnStop(func() { 278 err := g.listener.Close() 279 if err != nil { 280 g.log.Println("WARN: closing the listener failed:", err) 281 } 282 <-permanentListenClosedChan 283 }) 284 // Set the address and port of the gateway. 285 host, port, err := net.SplitHostPort(g.listener.Addr().String()) 286 g.port = port 287 if err != nil { 288 return nil, err 289 } 290 291 if ip := net.ParseIP(host); ip.IsUnspecified() && ip != nil { 292 // if host is unspecified, set a dummy one for now. 293 host = "localhost" 294 } 295 296 // Set myAddr equal to the address returned by the listener. It will be 297 // overwritten by threadedLearnHostname later on. 298 g.myAddr = modules.NetAddress(net.JoinHostPort(host, port)) 299 300 // Spawn the peer connection listener. 301 go g.permanentListen(permanentListenClosedChan) 302 303 // Spawn the peer manager and provide tools for ensuring clean shutdown. 304 peerManagerClosedChan := make(chan struct{}) 305 g.threads.OnStop(func() { 306 <-peerManagerClosedChan 307 }) 308 go g.permanentPeerManager(peerManagerClosedChan) 309 310 // Spawn the node manager and provide tools for ensuring clean shudown. 311 nodeManagerClosedChan := make(chan struct{}) 312 g.threads.OnStop(func() { 313 <-nodeManagerClosedChan 314 }) 315 go g.permanentNodeManager(nodeManagerClosedChan) 316 317 // Spawn the node purger and provide tools for ensuring clean shutdown. 318 nodePurgerClosedChan := make(chan struct{}) 319 g.threads.OnStop(func() { 320 <-nodePurgerClosedChan 321 }) 322 go g.permanentNodePurger(nodePurgerClosedChan) 323 324 // Spawn threads to take care of port forwarding and hostname discovery. 325 go g.threadedForwardPort(g.port) 326 go g.threadedLearnHostname() 327 328 return g, nil 329 } 330 331 // enforce that Gateway satisfies the modules.Gateway interface 332 var _ modules.Gateway = (*Gateway)(nil)