github.com/pure-x-eth/consensus_tm@v0.0.0-20230502163723-e3c2ff987250/p2p/transport.go (about) 1 package p2p 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "time" 8 9 "golang.org/x/net/netutil" 10 11 "github.com/gogo/protobuf/proto" 12 "github.com/pure-x-eth/consensus_tm/crypto" 13 "github.com/pure-x-eth/consensus_tm/libs/protoio" 14 "github.com/pure-x-eth/consensus_tm/p2p/conn" 15 tmp2p "github.com/pure-x-eth/consensus_tm/proto/tendermint/p2p" 16 ) 17 18 const ( 19 defaultDialTimeout = time.Second 20 defaultFilterTimeout = 5 * time.Second 21 defaultHandshakeTimeout = 3 * time.Second 22 ) 23 24 // IPResolver is a behaviour subset of net.Resolver. 25 type IPResolver interface { 26 LookupIPAddr(context.Context, string) ([]net.IPAddr, error) 27 } 28 29 // accept is the container to carry the upgraded connection and NodeInfo from an 30 // asynchronously running routine to the Accept method. 31 type accept struct { 32 netAddr *NetAddress 33 conn net.Conn 34 nodeInfo NodeInfo 35 err error 36 } 37 38 // peerConfig is used to bundle data we need to fully setup a Peer with an 39 // MConn, provided by the caller of Accept and Dial (currently the Switch). This 40 // a temporary measure until reactor setup is less dynamic and we introduce the 41 // concept of PeerBehaviour to communicate about significant Peer lifecycle 42 // events. 43 // TODO(xla): Refactor out with more static Reactor setup and PeerBehaviour. 44 type peerConfig struct { 45 chDescs []*conn.ChannelDescriptor 46 onPeerError func(Peer, interface{}) 47 outbound bool 48 // isPersistent allows you to set a function, which, given socket address 49 // (for outbound peers) OR self-reported address (for inbound peers), tells 50 // if the peer is persistent or not. 51 isPersistent func(*NetAddress) bool 52 reactorsByCh map[byte]Reactor 53 msgTypeByChID map[byte]proto.Message 54 metrics *Metrics 55 mlc *metricsLabelCache 56 } 57 58 // Transport emits and connects to Peers. The implementation of Peer is left to 59 // the transport. Each transport is also responsible to filter establishing 60 // peers specific to its domain. 61 type Transport interface { 62 // Listening address. 63 NetAddress() NetAddress 64 65 // Accept returns a newly connected Peer. 66 Accept(peerConfig) (Peer, error) 67 68 // Dial connects to the Peer for the address. 69 Dial(NetAddress, peerConfig) (Peer, error) 70 71 // Cleanup any resources associated with Peer. 72 Cleanup(Peer) 73 } 74 75 // transportLifecycle bundles the methods for callers to control start and stop 76 // behaviour. 77 type transportLifecycle interface { 78 Close() error 79 Listen(NetAddress) error 80 } 81 82 // ConnFilterFunc to be implemented by filter hooks after a new connection has 83 // been established. The set of exisiting connections is passed along together 84 // with all resolved IPs for the new connection. 85 type ConnFilterFunc func(ConnSet, net.Conn, []net.IP) error 86 87 // ConnDuplicateIPFilter resolves and keeps all ips for an incoming connection 88 // and refuses new ones if they come from a known ip. 89 func ConnDuplicateIPFilter() ConnFilterFunc { 90 return func(cs ConnSet, c net.Conn, ips []net.IP) error { 91 for _, ip := range ips { 92 if cs.HasIP(ip) { 93 return ErrRejected{ 94 conn: c, 95 err: fmt.Errorf("ip<%v> already connected", ip), 96 isDuplicate: true, 97 } 98 } 99 } 100 101 return nil 102 } 103 } 104 105 // MultiplexTransportOption sets an optional parameter on the 106 // MultiplexTransport. 107 type MultiplexTransportOption func(*MultiplexTransport) 108 109 // MultiplexTransportConnFilters sets the filters for rejection new connections. 110 func MultiplexTransportConnFilters( 111 filters ...ConnFilterFunc, 112 ) MultiplexTransportOption { 113 return func(mt *MultiplexTransport) { mt.connFilters = filters } 114 } 115 116 // MultiplexTransportFilterTimeout sets the timeout waited for filter calls to 117 // return. 118 func MultiplexTransportFilterTimeout( 119 timeout time.Duration, 120 ) MultiplexTransportOption { 121 return func(mt *MultiplexTransport) { mt.filterTimeout = timeout } 122 } 123 124 // MultiplexTransportResolver sets the Resolver used for ip lokkups, defaults to 125 // net.DefaultResolver. 126 func MultiplexTransportResolver(resolver IPResolver) MultiplexTransportOption { 127 return func(mt *MultiplexTransport) { mt.resolver = resolver } 128 } 129 130 // MultiplexTransportMaxIncomingConnections sets the maximum number of 131 // simultaneous connections (incoming). Default: 0 (unlimited) 132 func MultiplexTransportMaxIncomingConnections(n int) MultiplexTransportOption { 133 return func(mt *MultiplexTransport) { mt.maxIncomingConnections = n } 134 } 135 136 // MultiplexTransport accepts and dials tcp connections and upgrades them to 137 // multiplexed peers. 138 type MultiplexTransport struct { 139 netAddr NetAddress 140 listener net.Listener 141 maxIncomingConnections int // see MaxIncomingConnections 142 143 acceptc chan accept 144 closec chan struct{} 145 146 // Lookup table for duplicate ip and id checks. 147 conns ConnSet 148 connFilters []ConnFilterFunc 149 150 dialTimeout time.Duration 151 filterTimeout time.Duration 152 handshakeTimeout time.Duration 153 nodeInfo NodeInfo 154 nodeKey NodeKey 155 resolver IPResolver 156 157 // TODO(xla): This config is still needed as we parameterise peerConn and 158 // peer currently. All relevant configuration should be refactored into options 159 // with sane defaults. 160 mConfig conn.MConnConfig 161 } 162 163 // Test multiplexTransport for interface completeness. 164 var _ Transport = (*MultiplexTransport)(nil) 165 var _ transportLifecycle = (*MultiplexTransport)(nil) 166 167 // NewMultiplexTransport returns a tcp connected multiplexed peer. 168 func NewMultiplexTransport( 169 nodeInfo NodeInfo, 170 nodeKey NodeKey, 171 mConfig conn.MConnConfig, 172 ) *MultiplexTransport { 173 return &MultiplexTransport{ 174 acceptc: make(chan accept), 175 closec: make(chan struct{}), 176 dialTimeout: defaultDialTimeout, 177 filterTimeout: defaultFilterTimeout, 178 handshakeTimeout: defaultHandshakeTimeout, 179 mConfig: mConfig, 180 nodeInfo: nodeInfo, 181 nodeKey: nodeKey, 182 conns: NewConnSet(), 183 resolver: net.DefaultResolver, 184 } 185 } 186 187 // NetAddress implements Transport. 188 func (mt *MultiplexTransport) NetAddress() NetAddress { 189 return mt.netAddr 190 } 191 192 // Accept implements Transport. 193 func (mt *MultiplexTransport) Accept(cfg peerConfig) (Peer, error) { 194 select { 195 // This case should never have any side-effectful/blocking operations to 196 // ensure that quality peers are ready to be used. 197 case a := <-mt.acceptc: 198 if a.err != nil { 199 return nil, a.err 200 } 201 202 cfg.outbound = false 203 204 return mt.wrapPeer(a.conn, a.nodeInfo, cfg, a.netAddr), nil 205 case <-mt.closec: 206 return nil, ErrTransportClosed{} 207 } 208 } 209 210 // Dial implements Transport. 211 func (mt *MultiplexTransport) Dial( 212 addr NetAddress, 213 cfg peerConfig, 214 ) (Peer, error) { 215 c, err := addr.DialTimeout(mt.dialTimeout) 216 if err != nil { 217 return nil, err 218 } 219 220 // TODO(xla): Evaluate if we should apply filters if we explicitly dial. 221 if err := mt.filterConn(c); err != nil { 222 return nil, err 223 } 224 225 secretConn, nodeInfo, err := mt.upgrade(c, &addr) 226 if err != nil { 227 return nil, err 228 } 229 230 cfg.outbound = true 231 232 p := mt.wrapPeer(secretConn, nodeInfo, cfg, &addr) 233 234 return p, nil 235 } 236 237 // Close implements transportLifecycle. 238 func (mt *MultiplexTransport) Close() error { 239 close(mt.closec) 240 241 if mt.listener != nil { 242 return mt.listener.Close() 243 } 244 245 return nil 246 } 247 248 // Listen implements transportLifecycle. 249 func (mt *MultiplexTransport) Listen(addr NetAddress) error { 250 ln, err := net.Listen("tcp", addr.DialString()) 251 if err != nil { 252 return err 253 } 254 255 if mt.maxIncomingConnections > 0 { 256 ln = netutil.LimitListener(ln, mt.maxIncomingConnections) 257 } 258 259 mt.netAddr = addr 260 mt.listener = ln 261 262 go mt.acceptPeers() 263 264 return nil 265 } 266 267 // AddChannel registers a channel to nodeInfo. 268 // NOTE: NodeInfo must be of type DefaultNodeInfo else channels won't be updated 269 // This is a bit messy at the moment but is cleaned up in the following version 270 // when NodeInfo changes from an interface to a concrete type 271 func (mt *MultiplexTransport) AddChannel(chID byte) { 272 if ni, ok := mt.nodeInfo.(DefaultNodeInfo); ok { 273 if !ni.HasChannel(chID) { 274 ni.Channels = append(ni.Channels, chID) 275 } 276 mt.nodeInfo = ni 277 } 278 } 279 280 func (mt *MultiplexTransport) acceptPeers() { 281 for { 282 c, err := mt.listener.Accept() 283 if err != nil { 284 // If Close() has been called, silently exit. 285 select { 286 case _, ok := <-mt.closec: 287 if !ok { 288 return 289 } 290 default: 291 // Transport is not closed 292 } 293 294 mt.acceptc <- accept{err: err} 295 return 296 } 297 298 // Connection upgrade and filtering should be asynchronous to avoid 299 // Head-of-line blocking[0]. 300 // Reference: https://github.com/pure-x-eth/consensus_tm/issues/2047 301 // 302 // [0] https://en.wikipedia.org/wiki/Head-of-line_blocking 303 go func(c net.Conn) { 304 defer func() { 305 if r := recover(); r != nil { 306 err := ErrRejected{ 307 conn: c, 308 err: fmt.Errorf("recovered from panic: %v", r), 309 isAuthFailure: true, 310 } 311 select { 312 case mt.acceptc <- accept{err: err}: 313 case <-mt.closec: 314 // Give up if the transport was closed. 315 _ = c.Close() 316 return 317 } 318 } 319 }() 320 321 var ( 322 nodeInfo NodeInfo 323 secretConn *conn.SecretConnection 324 netAddr *NetAddress 325 ) 326 327 err := mt.filterConn(c) 328 if err == nil { 329 secretConn, nodeInfo, err = mt.upgrade(c, nil) 330 if err == nil { 331 addr := c.RemoteAddr() 332 id := PubKeyToID(secretConn.RemotePubKey()) 333 netAddr = NewNetAddress(id, addr) 334 } 335 } 336 337 select { 338 case mt.acceptc <- accept{netAddr, secretConn, nodeInfo, err}: 339 // Make the upgraded peer available. 340 case <-mt.closec: 341 // Give up if the transport was closed. 342 _ = c.Close() 343 return 344 } 345 }(c) 346 } 347 } 348 349 // Cleanup removes the given address from the connections set and 350 // closes the connection. 351 func (mt *MultiplexTransport) Cleanup(p Peer) { 352 mt.conns.RemoveAddr(p.RemoteAddr()) 353 _ = p.CloseConn() 354 } 355 356 func (mt *MultiplexTransport) cleanup(c net.Conn) error { 357 mt.conns.Remove(c) 358 359 return c.Close() 360 } 361 362 func (mt *MultiplexTransport) filterConn(c net.Conn) (err error) { 363 defer func() { 364 if err != nil { 365 _ = c.Close() 366 } 367 }() 368 369 // Reject if connection is already present. 370 if mt.conns.Has(c) { 371 return ErrRejected{conn: c, isDuplicate: true} 372 } 373 374 // Resolve ips for incoming conn. 375 ips, err := resolveIPs(mt.resolver, c) 376 if err != nil { 377 return err 378 } 379 380 errc := make(chan error, len(mt.connFilters)) 381 382 for _, f := range mt.connFilters { 383 go func(f ConnFilterFunc, c net.Conn, ips []net.IP, errc chan<- error) { 384 errc <- f(mt.conns, c, ips) 385 }(f, c, ips, errc) 386 } 387 388 for i := 0; i < cap(errc); i++ { 389 select { 390 case err := <-errc: 391 if err != nil { 392 return ErrRejected{conn: c, err: err, isFiltered: true} 393 } 394 case <-time.After(mt.filterTimeout): 395 return ErrFilterTimeout{} 396 } 397 398 } 399 400 mt.conns.Set(c, ips) 401 402 return nil 403 } 404 405 func (mt *MultiplexTransport) upgrade( 406 c net.Conn, 407 dialedAddr *NetAddress, 408 ) (secretConn *conn.SecretConnection, nodeInfo NodeInfo, err error) { 409 defer func() { 410 if err != nil { 411 _ = mt.cleanup(c) 412 } 413 }() 414 415 secretConn, err = upgradeSecretConn(c, mt.handshakeTimeout, mt.nodeKey.PrivKey) 416 if err != nil { 417 return nil, nil, ErrRejected{ 418 conn: c, 419 err: fmt.Errorf("secret conn failed: %v", err), 420 isAuthFailure: true, 421 } 422 } 423 424 // For outgoing conns, ensure connection key matches dialed key. 425 connID := PubKeyToID(secretConn.RemotePubKey()) 426 if dialedAddr != nil { 427 if dialedID := dialedAddr.ID; connID != dialedID { 428 return nil, nil, ErrRejected{ 429 conn: c, 430 id: connID, 431 err: fmt.Errorf( 432 "conn.ID (%v) dialed ID (%v) mismatch", 433 connID, 434 dialedID, 435 ), 436 isAuthFailure: true, 437 } 438 } 439 } 440 441 nodeInfo, err = handshake(secretConn, mt.handshakeTimeout, mt.nodeInfo) 442 if err != nil { 443 return nil, nil, ErrRejected{ 444 conn: c, 445 err: fmt.Errorf("handshake failed: %v", err), 446 isAuthFailure: true, 447 } 448 } 449 450 if err := nodeInfo.Validate(); err != nil { 451 return nil, nil, ErrRejected{ 452 conn: c, 453 err: err, 454 isNodeInfoInvalid: true, 455 } 456 } 457 458 // Ensure connection key matches self reported key. 459 if connID != nodeInfo.ID() { 460 return nil, nil, ErrRejected{ 461 conn: c, 462 id: connID, 463 err: fmt.Errorf( 464 "conn.ID (%v) NodeInfo.ID (%v) mismatch", 465 connID, 466 nodeInfo.ID(), 467 ), 468 isAuthFailure: true, 469 } 470 } 471 472 // Reject self. 473 if mt.nodeInfo.ID() == nodeInfo.ID() { 474 return nil, nil, ErrRejected{ 475 addr: *NewNetAddress(nodeInfo.ID(), c.RemoteAddr()), 476 conn: c, 477 id: nodeInfo.ID(), 478 isSelf: true, 479 } 480 } 481 482 if err := mt.nodeInfo.CompatibleWith(nodeInfo); err != nil { 483 return nil, nil, ErrRejected{ 484 conn: c, 485 err: err, 486 id: nodeInfo.ID(), 487 isIncompatible: true, 488 } 489 } 490 491 return secretConn, nodeInfo, nil 492 } 493 494 func (mt *MultiplexTransport) wrapPeer( 495 c net.Conn, 496 ni NodeInfo, 497 cfg peerConfig, 498 socketAddr *NetAddress, 499 ) Peer { 500 501 persistent := false 502 if cfg.isPersistent != nil { 503 if cfg.outbound { 504 persistent = cfg.isPersistent(socketAddr) 505 } else { 506 selfReportedAddr, err := ni.NetAddress() 507 if err == nil { 508 persistent = cfg.isPersistent(selfReportedAddr) 509 } 510 } 511 } 512 513 peerConn := newPeerConn( 514 cfg.outbound, 515 persistent, 516 c, 517 socketAddr, 518 ) 519 520 p := newPeer( 521 peerConn, 522 mt.mConfig, 523 ni, 524 cfg.reactorsByCh, 525 cfg.msgTypeByChID, 526 cfg.chDescs, 527 cfg.onPeerError, 528 cfg.mlc, 529 PeerMetrics(cfg.metrics), 530 ) 531 532 return p 533 } 534 535 func handshake( 536 c net.Conn, 537 timeout time.Duration, 538 nodeInfo NodeInfo, 539 ) (NodeInfo, error) { 540 if err := c.SetDeadline(time.Now().Add(timeout)); err != nil { 541 return nil, err 542 } 543 544 var ( 545 errc = make(chan error, 2) 546 547 pbpeerNodeInfo tmp2p.DefaultNodeInfo 548 peerNodeInfo DefaultNodeInfo 549 ourNodeInfo = nodeInfo.(DefaultNodeInfo) 550 ) 551 552 go func(errc chan<- error, c net.Conn) { 553 _, err := protoio.NewDelimitedWriter(c).WriteMsg(ourNodeInfo.ToProto()) 554 errc <- err 555 }(errc, c) 556 go func(errc chan<- error, c net.Conn) { 557 protoReader := protoio.NewDelimitedReader(c, MaxNodeInfoSize()) 558 _, err := protoReader.ReadMsg(&pbpeerNodeInfo) 559 errc <- err 560 }(errc, c) 561 562 for i := 0; i < cap(errc); i++ { 563 err := <-errc 564 if err != nil { 565 return nil, err 566 } 567 } 568 569 peerNodeInfo, err := DefaultNodeInfoFromToProto(&pbpeerNodeInfo) 570 if err != nil { 571 return nil, err 572 } 573 574 return peerNodeInfo, c.SetDeadline(time.Time{}) 575 } 576 577 func upgradeSecretConn( 578 c net.Conn, 579 timeout time.Duration, 580 privKey crypto.PrivKey, 581 ) (*conn.SecretConnection, error) { 582 if err := c.SetDeadline(time.Now().Add(timeout)); err != nil { 583 return nil, err 584 } 585 586 sc, err := conn.MakeSecretConnection(c, privKey) 587 if err != nil { 588 return nil, err 589 } 590 591 return sc, sc.SetDeadline(time.Time{}) 592 } 593 594 func resolveIPs(resolver IPResolver, c net.Conn) ([]net.IP, error) { 595 host, _, err := net.SplitHostPort(c.RemoteAddr().String()) 596 if err != nil { 597 return nil, err 598 } 599 600 addrs, err := resolver.LookupIPAddr(context.Background(), host) 601 if err != nil { 602 return nil, err 603 } 604 605 ips := []net.IP{} 606 607 for _, addr := range addrs { 608 ips = append(ips, addr.IP) 609 } 610 611 return ips, nil 612 }