Repository: libp2p/go-libp2p
Pull request #1823 (2022-10-12 15:40:03)

noise: add an option to allow unknown peer ID in SecureOutbound 
Allows the `SecureOutbound` call to be called with an unknown peer ID (empty string). This is required for https://github.com/libp2p/specs/pull/412#discussion_r977698490

marten-seemann: Can we make this a separate function, that has a very clear warning of the risks. Dialing nodes without a peer ID makes you susceptible to MITM attacks, see discussion in https://github.com/libp2p/specs/issues/458.
ckousik: @marten-seemann Could you take another look at this?
ckousik: > This seems quite complicated. Have you considered my suggestion of making this a separate method, as I suggested in my last review?

@marten-seemann I considered it but wanted to adhere strictly to the `SecureTransport` interface. Adding that will not be difficult since `noise.Transport` is already exposed.
ckousik: @marten-seemann I want to keep the interface consistent between `SessionTransport` and `Transport`. We also use `SessionTransport` in webrtc for the noise prologue. 

Patch:
diff --git a/core/peer/addrinfo.go b/core/peer/addrinfo.go
index 19b07a4b..b479df9e 100644
--- a/core/peer/addrinfo.go
+++ b/core/peer/addrinfo.go
@@ -86,7 +86,6 @@ func AddrInfoFromP2pAddr(m ma.Multiaddr) (*AddrInfo, error) {
 
 // AddrInfoToP2pAddrs converts an AddrInfo to a list of Multiaddrs.
 func AddrInfoToP2pAddrs(pi *AddrInfo) ([]ma.Multiaddr, error) {
-	var addrs []ma.Multiaddr
 	p2ppart, err := ma.NewComponent("p2p", Encode(pi.ID))
 	if err != nil {
 		return nil, err
@@ -94,6 +93,7 @@ func AddrInfoToP2pAddrs(pi *AddrInfo) ([]ma.Multiaddr, error) {
 	if len(pi.Addrs) == 0 {
 		return []ma.Multiaddr{p2ppart}, nil
 	}
+	addrs := make([]ma.Multiaddr, 0, len(pi.Addrs))
 	for _, addr := range pi.Addrs {
 		addrs = append(addrs, addr.Encapsulate(p2ppart))
 	}
diff --git a/core/peer/record.go b/core/peer/record.go
index 9f8861af..b502c8dd 100644
--- a/core/peer/record.go
+++ b/core/peer/record.go
@@ -231,7 +231,7 @@ func (r *PeerRecord) ToProtobuf() (*pb.PeerRecord, error) {
 }
 
 func addrsFromProtobuf(addrs []*pb.PeerRecord_AddressInfo) []ma.Multiaddr {
-	var out []ma.Multiaddr
+	out := make([]ma.Multiaddr, 0, len(addrs))
 	for _, addr := range addrs {
 		a, err := ma.NewMultiaddrBytes(addr.Multiaddr)
 		if err != nil {
@@ -243,7 +243,7 @@ func addrsFromProtobuf(addrs []*pb.PeerRecord_AddressInfo) []ma.Multiaddr {
 }
 
 func addrsToProtobuf(addrs []ma.Multiaddr) []*pb.PeerRecord_AddressInfo {
-	var out []*pb.PeerRecord_AddressInfo
+	out := make([]*pb.PeerRecord_AddressInfo, 0, len(addrs))
 	for _, addr := range addrs {
 		out = append(out, &pb.PeerRecord_AddressInfo{Multiaddr: addr.Bytes()})
 	}
diff --git a/core/peerstore/peerstore.go b/core/peerstore/peerstore.go
index 213513bc..7561f32b 100644
--- a/core/peerstore/peerstore.go
+++ b/core/peerstore/peerstore.go
@@ -25,11 +25,6 @@ var (
 	// TempAddrTTL is the ttl used for a short lived address.
 	TempAddrTTL = time.Minute * 2
 
-	// ProviderAddrTTL is the TTL of an address we've received from a provider.
-	// This is also a temporary address, but lasts longer. After this expires,
-	// the records we return will require an extra lookup.
-	ProviderAddrTTL = time.Minute * 30
-
 	// RecentlyConnectedAddrTTL is used when we recently connected to a peer.
 	// It means that we are reasonably certain of the peer's address.
 	RecentlyConnectedAddrTTL = time.Minute * 30
diff --git a/options.go b/options.go
index 3509c61d..5d54fe75 100644
--- a/options.go
+++ b/options.go
@@ -245,7 +245,9 @@ func EnableRelayService(opts ...relayv2.Option) Option {
 //
 // Dependencies:
 //   - Relay (enabled by default)
-//   - Routing (to find relays), or StaticRelays/DefaultStaticRelays.
+//   - Either:
+//     1. A list of static relays
+//     2. A PeerSource function that provides a chan of relays. See `autorelay.WithPeerSource`
 //
 // This subsystem performs automatic address rewriting to advertise relay addresses when it
 // detects that the node is publicly unreachable (e.g. behind a NAT).
diff --git a/p2p/discovery/util/util.go b/p2p/discovery/util/util.go
index 74175261..b21a676a 100644
--- a/p2p/discovery/util/util.go
+++ b/p2p/discovery/util/util.go
@@ -14,13 +14,13 @@ var log = logging.Logger("discovery-util")
 
 // FindPeers is a utility function that synchronously collects peers from a Discoverer.
 func FindPeers(ctx context.Context, d discovery.Discoverer, ns string, opts ...discovery.Option) ([]peer.AddrInfo, error) {
-	var res []peer.AddrInfo
 
 	ch, err := d.FindPeers(ctx, ns, opts...)
 	if err != nil {
 		return nil, err
 	}
 
+	res := make([]peer.AddrInfo, 0, len(ch))
 	for pi := range ch {
 		res = append(res, pi)
 	}
diff --git a/p2p/host/autorelay/relay_finder.go b/p2p/host/autorelay/relay_finder.go
index 4760511b..533c748b 100644
--- a/p2p/host/autorelay/relay_finder.go
+++ b/p2p/host/autorelay/relay_finder.go
@@ -83,6 +83,10 @@ type relayFinder struct {
 }
 
 func newRelayFinder(host *basic.BasicHost, peerSource func(context.Context, int) <-chan peer.AddrInfo, conf *config) *relayFinder {
+	if peerSource == nil && len(conf.staticRelays) == 0 {
+		panic("Can not create a new relayFinder. Need a Peer Source fn or a list of static relays. Refer to the documentation around `libp2p.EnableAutoRelay`")
+	}
+
 	return &relayFinder{
 		bootTime:                   conf.clock.Now(),
 		host:                       host,
diff --git a/p2p/host/basic/basic_host.go b/p2p/host/basic/basic_host.go
index 19fff7d2..82e29a37 100644
--- a/p2p/host/basic/basic_host.go
+++ b/p2p/host/basic/basic_host.go
@@ -413,7 +413,7 @@ func (h *BasicHost) newStreamHandler(s network.Stream) {
 		return
 	}
 
-	log.Debugf("protocol negotiation took %s", took)
+	log.Debugf("negotiated: %s (took %s)", protoID, took)
 
 	go handle(protoID, s)
 }
diff --git a/p2p/host/routed/routed.go b/p2p/host/routed/routed.go
index 55c8d347..8f00c25b 100644
--- a/p2p/host/routed/routed.go
+++ b/p2p/host/routed/routed.go
@@ -107,7 +107,39 @@ func (rh *RoutedHost) Connect(ctx context.Context, pi peer.AddrInfo) error {
 
 	// if we're here, we got some addrs. let's use our wrapped host to connect.
 	pi.Addrs = addrs
-	return rh.host.Connect(ctx, pi)
+	err := rh.host.Connect(ctx, pi)
+	if err != nil {
+		// We couldn't connect. Let's check if we have the most
+		// up-to-date addresses for the given peer. If there
+		// are addresses we didn't know about previously, we
+		// try to connect again.
+		newAddrs, err := rh.findPeerAddrs(ctx, pi.ID)
+		if err != nil {
+			return fmt.Errorf("failed to find peers: %w", err)
+		}
+
+		// Build lookup map
+		lookup := make(map[ma.Multiaddr]struct{}, len(addrs))
+		for _, addr := range addrs {
+			lookup[addr] = struct{}{}
+		}
+
+		// if there's any address that's not in the previous set
+		// of addresses, try to connect again. If all addresses
+		// where known previously we return the original error.
+		for _, newAddr := range newAddrs {
+			if _, found := lookup[newAddr]; found {
+				continue
+			}
+
+			pi.Addrs = newAddrs
+			return rh.host.Connect(ctx, pi)
+		}
+
+		return err
+	}
+
+	return nil
 }
 
 func (rh *RoutedHost) findPeerAddrs(ctx context.Context, id peer.ID) ([]ma.Multiaddr, error) {
diff --git a/p2p/net/connmgr/connmgr.go b/p2p/net/connmgr/connmgr.go
index 3a1acf0d..c124c6bb 100644
--- a/p2p/net/connmgr/connmgr.go
+++ b/p2p/net/connmgr/connmgr.go
@@ -62,14 +62,21 @@ type segment struct {
 	peers map[peer.ID]*peerInfo
 }
 
-type segments [256]*segment
+type segments struct {
+	// bucketsMu is used to prevent deadlocks when concurrent processes try to
+	// grab multiple segment locks at once. If you need multiple segment locks
+	// at once, you should grab this lock first. You may release this lock once
+	// you have the segment locks.
+	bucketsMu sync.Mutex
+	buckets   [256]*segment
+}
 
 func (ss *segments) get(p peer.ID) *segment {
-	return ss[byte(p[len(p)-1])]
+	return ss.buckets[byte(p[len(p)-1])]
 }
 
 func (ss *segments) countPeers() (count int) {
-	for _, seg := range ss {
+	for _, seg := range ss.buckets {
 		seg.Lock()
 		count += len(seg.peers)
 		seg.Unlock()
@@ -122,15 +129,15 @@ func NewConnManager(low, hi int, opts ...Option) (*BasicConnMgr, error) {
 		cfg:       cfg,
 		clock:     cfg.clock,
 		protected: make(map[peer.ID]map[string]struct{}, 16),
-		segments: func() (ret segments) {
-			for i := range ret {
-				ret[i] = &segment{
-					peers: make(map[peer.ID]*peerInfo),
-				}
-			}
-			return ret
-		}(),
+		segments:  segments{},
+	}
+
+	for i := range cm.segments.buckets {
+		cm.segments.buckets[i] = &segment{
+			peers: make(map[peer.ID]*peerInfo),
+		}
 	}
+
 	cm.ctx, cm.cancel = context.WithCancel(context.Background())
 
 	if cfg.emergencyTrim {
@@ -246,15 +253,32 @@ type peerInfo struct {
 	firstSeen time.Time // timestamp when we began tracking this peer.
 }
 
-type peerInfos []peerInfo
+type peerInfos []*peerInfo
 
 // SortByValueAndStreams sorts peerInfos by their value and stream count. It
 // will sort peers with no streams before those with streams (all else being
 // equal). If `sortByMoreStreams` is true it will sort peers with more streams
 // before those with fewer streams. This is useful to prioritize freeing memory.
-func (p peerInfos) SortByValueAndStreams(sortByMoreStreams bool) {
+func (p peerInfos) SortByValueAndStreams(segments *segments, sortByMoreStreams bool) {
 	sort.Slice(p, func(i, j int) bool {
 		left, right := p[i], p[j]
+
+		// Grab this lock so that we can grab both segment locks below without deadlocking.
+		segments.bucketsMu.Lock()
+
+		// lock this to protect from concurrent modifications from connect/disconnect events
+		leftSegment := segments.get(left.id)
+		leftSegment.Lock()
+		defer leftSegment.Unlock()
+
+		rightSegment := segments.get(right.id)
+		if leftSegment != rightSegment {
+			// These two peers are not in the same segment, lets get the lock
+			rightSegment.Lock()
+			defer rightSegment.Unlock()
+		}
+		segments.bucketsMu.Unlock()
+
 		// temporary peers are preferred for pruning.
 		if left.temp != right.temp {
 			return left.temp
@@ -360,31 +384,34 @@ func (cm *BasicConnMgr) getConnsToCloseEmergency(target int) []network.Conn {
 	candidates := make(peerInfos, 0, cm.segments.countPeers())
 
 	cm.plk.RLock()
-	for _, s := range cm.segments {
+	for _, s := range cm.segments.buckets {
 		s.Lock()
 		for id, inf := range s.peers {
 			if _, ok := cm.protected[id]; ok {
 				// skip over protected peer.
 				continue
 			}
-			candidates = append(candidates, *inf)
+			candidates = append(candidates, inf)
 		}
 		s.Unlock()
 	}
 	cm.plk.RUnlock()
 
 	// Sort peers according to their value.
-	candidates.SortByValueAndStreams(true)
+	candidates.SortByValueAndStreams(&cm.segments, true)
 
 	selected := make([]network.Conn, 0, target+10)
 	for _, inf := range candidates {
 		if target <= 0 {
 			break
 		}
+		s := cm.segments.get(inf.id)
+		s.Lock()
 		for c := range inf.conns {
 			selected = append(selected, c)
 		}
 		target -= len(inf.conns)
+		s.Unlock()
 	}
 	if len(selected) >= target {
 		// We found enough connections that were not protected.
@@ -395,24 +422,28 @@ func (cm *BasicConnMgr) getConnsToCloseEmergency(target int) []network.Conn {
 	// We have no choice but to kill some protected connections.
 	candidates = candidates[:0]
 	cm.plk.RLock()
-	for _, s := range cm.segments {
+	for _, s := range cm.segments.buckets {
 		s.Lock()
 		for _, inf := range s.peers {
-			candidates = append(candidates, *inf)
+			candidates = append(candidates, inf)
 		}
 		s.Unlock()
 	}
 	cm.plk.RUnlock()
 
-	candidates.SortByValueAndStreams(true)
+	candidates.SortByValueAndStreams(&cm.segments, true)
 	for _, inf := range candidates {
 		if target <= 0 {
 			break
 		}
+		// lock this to protect from concurrent modifications from connect/disconnect events
+		s := cm.segments.get(inf.id)
+		s.Lock()
 		for c := range inf.conns {
 			selected = append(selected, c)
 		}
 		target -= len(inf.conns)
+		s.Unlock()
 	}
 	return selected
 }
@@ -435,7 +466,7 @@ func (cm *BasicConnMgr) getConnsToClose() []network.Conn {
 	gracePeriodStart := cm.clock.Now().Add(-cm.cfg.gracePeriod)
 
 	cm.plk.RLock()
-	for _, s := range cm.segments {
+	for _, s := range cm.segments.buckets {
 		s.Lock()
 		for id, inf := range s.peers {
 			if _, ok := cm.protected[id]; ok {
@@ -448,7 +479,7 @@ func (cm *BasicConnMgr) getConnsToClose() []network.Conn {
 			}
 			// note that we're copying the entry here,
 			// but since inf.conns is a map, it will still point to the original object
-			candidates = append(candidates, *inf)
+			candidates = append(candidates, inf)
 			ncandidates += len(inf.conns)
 		}
 		s.Unlock()
@@ -465,7 +496,7 @@ func (cm *BasicConnMgr) getConnsToClose() []network.Conn {
 	}
 
 	// Sort peers according to their value.
-	candidates.SortByValueAndStreams(false)
+	candidates.SortByValueAndStreams(&cm.segments, false)
 
 	target := ncandidates - cm.cfg.lowWater
 
diff --git a/p2p/net/connmgr/decay.go b/p2p/net/connmgr/decay.go
index 0819bd21..c10214cb 100644
--- a/p2p/net/connmgr/decay.go
+++ b/p2p/net/connmgr/decay.go
@@ -177,7 +177,7 @@ func (d *decayer) process() {
 			d.tagsMu.Unlock()
 
 			// Visit each peer, and decay tags that need to be decayed.
-			for _, s := range d.mgr.segments {
+			for _, s := range d.mgr.segments.buckets {
 				s.Lock()
 
 				// Entered a segment that contains peers. Process each peer.
@@ -261,7 +261,7 @@ func (d *decayer) process() {
 			d.tagsMu.Unlock()
 
 			// Remove the tag from all peers that had it in the connmgr.
-			for _, s := range d.mgr.segments {
+			for _, s := range d.mgr.segments.buckets {
 				// visit all segments, and attempt to remove the tag from all the peers it stores.
 				s.Lock()
 				for _, p := range s.peers {
diff --git a/p2p/net/mock/mock_peernet.go b/p2p/net/mock/mock_peernet.go
index 3fb07013..85e63678 100644
--- a/p2p/net/mock/mock_peernet.go
+++ b/p2p/net/mock/mock_peernet.go
@@ -255,7 +255,7 @@ func (pn *peernet) ConnsToPeer(p peer.ID) []network.Conn {
 		return nil
 	}
 
-	var cs2 []network.Conn
+	cs2 := make([]network.Conn, 0, len(cs))
 	for c := range cs {
 		cs2 = append(cs2, c)
 	}
diff --git a/p2p/net/swarm/swarm_dial.go b/p2p/net/swarm/swarm_dial.go
index f5e5cba8..1efde8c4 100644
--- a/p2p/net/swarm/swarm_dial.go
+++ b/p2p/net/swarm/swarm_dial.go
@@ -334,7 +334,7 @@ func (s *Swarm) addrsForDial(ctx context.Context, p peer.ID) ([]ma.Multiaddr, er
 
 	s.peers.AddAddrs(p, goodAddrs, peerstore.TempAddrTTL)
 
-	return resolved, nil
+	return goodAddrs, nil
 }
 
 func (s *Swarm) resolveAddrs(ctx context.Context, pi peer.AddrInfo) ([]ma.Multiaddr, error) {
diff --git a/p2p/protocol/circuitv2/util/pbconv.go b/p2p/protocol/circuitv2/util/pbconv.go
index ae1b9b50..4a884351 100644
--- a/p2p/protocol/circuitv2/util/pbconv.go
+++ b/p2p/protocol/circuitv2/util/pbconv.go
@@ -36,10 +36,7 @@ func PeerToPeerInfoV1(p *pbv1.CircuitRelay_Peer) (peer.AddrInfo, error) {
 }
 
 func PeerInfoToPeerV1(pi peer.AddrInfo) *pbv1.CircuitRelay_Peer {
-	var addrs [][]byte
-	if len(pi.Addrs) > 0 {
-		addrs = make([][]byte, 0, len(pi.Addrs))
-	}
+	addrs := make([][]byte, 0, len(pi.Addrs))
 
 	for _, addr := range pi.Addrs {
 		addrs = append(addrs, addr.Bytes())
@@ -62,10 +59,7 @@ func PeerToPeerInfoV2(p *pbv2.Peer) (peer.AddrInfo, error) {
 		return peer.AddrInfo{}, err
 	}
 
-	var addrs []ma.Multiaddr
-	if len(p.Addrs) > 0 {
-		addrs = make([]ma.Multiaddr, 0, len(p.Addrs))
-	}
+	addrs := make([]ma.Multiaddr, 0, len(p.Addrs))
 
 	for _, addrBytes := range p.Addrs {
 		a, err := ma.NewMultiaddrBytes(addrBytes)
@@ -78,11 +72,7 @@ func PeerToPeerInfoV2(p *pbv2.Peer) (peer.AddrInfo, error) {
 }
 
 func PeerInfoToPeerV2(pi peer.AddrInfo) *pbv2.Peer {
-	var addrs [][]byte
-
-	if len(pi.Addrs) > 0 {
-		addrs = make([][]byte, 0, len(pi.Addrs))
-	}
+	addrs := make([][]byte, 0, len(pi.Addrs))
 
 	for _, addr := range pi.Addrs {
 		addrs = append(addrs, addr.Bytes())
diff --git a/p2p/protocol/ping/ping.go b/p2p/protocol/ping/ping.go
index 583eea5c..6ff5c3fb 100644
--- a/p2p/protocol/ping/ping.go
+++ b/p2p/protocol/ping/ping.go
@@ -71,7 +71,7 @@ func (p *PingService) PingHandler(s network.Stream) {
 				log.Error("ping loop failed without error")
 			}
 		}
-		s.Reset()
+		s.Close()
 	}()
 
 	for {
diff --git a/p2p/security/noise/handshake.go b/p2p/security/noise/handshake.go
index 6e119cb5..4dd481d6 100644
--- a/p2p/security/noise/handshake.go
+++ b/p2p/security/noise/handshake.go
@@ -272,11 +272,8 @@ func (s *secureSession) handleRemoteHandshakePayload(payload []byte, remoteStati
 		return nil, err
 	}
 
-	// check the peer ID for:
-	// * all outbound connection
-	// * inbound connections, if we know which peer we want to connect to (SecureInbound called with a peer ID)
-	if (s.initiator && s.remoteID != id) || (!s.initiator && s.remoteID != "" && s.remoteID != id) {
-		// use Pretty() as it produces the full b58-encoded string, rather than abbreviated forms.
+	// check the peer ID if enabled
+	if s.checkPeerID && s.remoteID != id {
 		return nil, fmt.Errorf("peer id mismatch: expected %s, but remote key matches %s", s.remoteID.Pretty(), id.Pretty())
 	}
 
diff --git a/p2p/security/noise/session.go b/p2p/security/noise/session.go
index ce8d97cd..f7e75e64 100644
--- a/p2p/security/noise/session.go
+++ b/p2p/security/noise/session.go
@@ -15,7 +15,8 @@ import (
 )
 
 type secureSession struct {
-	initiator bool
+	initiator   bool
+	checkPeerID bool
 
 	localID   peer.ID
 	localKey  crypto.PrivKey
@@ -47,7 +48,7 @@ type secureSession struct {
 
 // newSecureSession creates a Noise session over the given insecureConn Conn, using
 // the libp2p identity keypair from the given Transport.
-func newSecureSession(tpt *Transport, ctx context.Context, insecure net.Conn, remote peer.ID, prologue []byte, initiatorEDH, responderEDH EarlyDataHandler, initiator bool) (*secureSession, error) {
+func newSecureSession(tpt *Transport, ctx context.Context, insecure net.Conn, remote peer.ID, prologue []byte, initiatorEDH, responderEDH EarlyDataHandler, initiator, checkPeerID bool) (*secureSession, error) {
 	s := &secureSession{
 		insecureConn:              insecure,
 		insecureReader:            bufio.NewReader(insecure),
@@ -58,6 +59,7 @@ func newSecureSession(tpt *Transport, ctx context.Context, insecure net.Conn, re
 		prologue:                  prologue,
 		initiatorEarlyDataHandler: initiatorEDH,
 		responderEarlyDataHandler: responderEDH,
+		checkPeerID:               checkPeerID,
 	}
 
 	// the go-routine we create to run the handshake will
diff --git a/p2p/security/noise/session_transport.go b/p2p/security/noise/session_transport.go
index 7b468edb..c42271bd 100644
--- a/p2p/security/noise/session_transport.go
+++ b/p2p/security/noise/session_transport.go
@@ -50,6 +50,17 @@ func EarlyData(initiator, responder EarlyDataHandler) SessionOption {
 	}
 }
 
+// DisablePeerIDCheck disables checking the remote peer ID for a noise connection.
+// For outbound connections, this is the equivalent of calling `SecureInbound` with an empty
+// peer ID. This is susceptible to MITM attacks since we do not verify the identity of the remote
+// peer.
+func DisablePeerIDCheck() SessionOption {
+	return func(s *SessionTransport) error {
+		s.disablePeerIDCheck = true
+		return nil
+	}
+}
+
 var _ sec.SecureTransport = &SessionTransport{}
 
 // SessionTransport can be used
@@ -57,7 +68,8 @@ var _ sec.SecureTransport = &SessionTransport{}
 type SessionTransport struct {
 	t *Transport
 	// options
-	prologue []byte
+	prologue           []byte
+	disablePeerIDCheck bool
 
 	initiatorEarlyDataHandler, responderEarlyDataHandler EarlyDataHandler
 }
@@ -65,7 +77,8 @@ type SessionTransport struct {
 // SecureInbound runs the Noise handshake as the responder.
 // If p is empty, connections from any peer are accepted.
 func (i *SessionTransport) SecureInbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
-	c, err := newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false)
+	checkPeerID := !i.disablePeerIDCheck && p != ""
+	c, err := newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, false, checkPeerID)
 	if err != nil {
 		addr, maErr := manet.FromNetAddr(insecure.RemoteAddr())
 		if maErr == nil {
@@ -77,5 +90,5 @@ func (i *SessionTransport) SecureInbound(ctx context.Context, insecure net.Conn,
 
 // SecureOutbound runs the Noise handshake as the initiator.
 func (i *SessionTransport) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
-	return newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true)
+	return newSecureSession(i.t, ctx, insecure, p, i.prologue, i.initiatorEarlyDataHandler, i.responderEarlyDataHandler, true, !i.disablePeerIDCheck)
 }
diff --git a/p2p/security/noise/transport.go b/p2p/security/noise/transport.go
index 7ae4d7de..68789295 100644
--- a/p2p/security/noise/transport.go
+++ b/p2p/security/noise/transport.go
@@ -54,7 +54,7 @@ func New(privkey crypto.PrivKey, muxers []protocol.ID) (*Transport, error) {
 // If p is empty, connections from any peer are accepted.
 func (t *Transport) SecureInbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
 	responderEDH := newTransportEDH(t)
-	c, err := newSecureSession(t, ctx, insecure, p, nil, nil, responderEDH, false)
+	c, err := newSecureSession(t, ctx, insecure, p, nil, nil, responderEDH, false, p != "")
 	if err != nil {
 		addr, maErr := manet.FromNetAddr(insecure.RemoteAddr())
 		if maErr == nil {
@@ -67,14 +67,14 @@ func (t *Transport) SecureInbound(ctx context.Context, insecure net.Conn, p peer
 // SecureOutbound runs the Noise handshake as the initiator.
 func (t *Transport) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
 	initiatorEDH := newTransportEDH(t)
-	c, err := newSecureSession(t, ctx, insecure, p, nil, initiatorEDH, nil, true)
+	c, err := newSecureSession(t, ctx, insecure, p, nil, initiatorEDH, nil, true, true)
 	if err != nil {
 		return c, err
 	}
 	return SessionWithConnState(c, initiatorEDH.MatchMuxers(true)), err
 }
 
-func (t *Transport) WithSessionOptions(opts ...SessionOption) (sec.SecureTransport, error) {
+func (t *Transport) WithSessionOptions(opts ...SessionOption) (*SessionTransport, error) {
 	st := &SessionTransport{t: t}
 	for _, opt := range opts {
 		if err := opt(st); err != nil {
diff --git a/p2p/security/tls/crypto.go b/p2p/security/tls/crypto.go
index aa16b334..b8f23f39 100644
--- a/p2p/security/tls/crypto.go
+++ b/p2p/security/tls/crypto.go
@@ -16,8 +16,6 @@ import (
 	"runtime/debug"
 	"time"
 
-	"golang.org/x/sys/cpu"
-
 	ic "github.com/libp2p/go-libp2p/core/crypto"
 	"github.com/libp2p/go-libp2p/core/peer"
 )
@@ -75,11 +73,10 @@ func NewIdentity(privKey ic.PrivKey, opts ...IdentityOption) (*Identity, error)
 	}
 	return &Identity{
 		config: tls.Config{
-			MinVersion:               tls.VersionTLS13,
-			PreferServerCipherSuites: preferServerCipherSuites(),
-			InsecureSkipVerify:       true, // This is not insecure here. We will verify the cert chain ourselves.
-			ClientAuth:               tls.RequireAnyClientCert,
-			Certificates:             []tls.Certificate{*cert},
+			MinVersion:         tls.VersionTLS13,
+			InsecureSkipVerify: true, // This is not insecure here. We will verify the cert chain ourselves.
+			ClientAuth:         tls.RequireAnyClientCert,
+			Certificates:       []tls.Certificate{*cert},
 			VerifyPeerCertificate: func(_ [][]byte, _ [][]*x509.Certificate) error {
 				panic("tls config not specialized for peer")
 			},
@@ -271,25 +268,3 @@ func certTemplate() (*x509.Certificate, error) {
 		Subject: pkix.Name{SerialNumber: subjectSN.String()},
 	}, nil
 }
-
-// We want nodes without AES hardware (e.g. ARM) support to always use ChaCha.
-// Only if both nodes have AES hardware support (e.g. x86), AES should be used.
-// x86->x86: AES, ARM->x86: ChaCha, x86->ARM: ChaCha and ARM->ARM: Chacha
-// This function returns true if we don't have AES hardware support, and false otherwise.
-// Thus, ARM servers will always use their own cipher suite preferences (ChaCha first),
-// and x86 servers will always use the client's cipher suite preferences.
-func preferServerCipherSuites() bool {
-	// Copied from the Go TLS implementation.
-
-	// Check the cpu flags for each platform that has optimized GCM implementations.
-	// Worst case, these variables will just all be false.
-	var (
-		hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
-		hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
-		// Keep in sync with crypto/aes/cipher_s390x.go.
-		hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
-
-		hasGCMAsm = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X
-	)
-	return !hasGCMAsm
-}


Test patch:
diff --git a/p2p/discovery/routing/routing_test.go b/p2p/discovery/routing/routing_test.go
index ed8ab25d..6e28c06f 100644
--- a/p2p/discovery/routing/routing_test.go
+++ b/p2p/discovery/routing/routing_test.go
@@ -135,7 +135,7 @@ func TestDiscoveryRouting(t *testing.T) {
 
 	pch := r2.FindProvidersAsync(ctx, c, 20)
 
-	var allAIs []peer.AddrInfo
+	allAIs := make([]peer.AddrInfo, 0, len(pch))
 	for ai := range pch {
 		allAIs = append(allAIs, ai)
 	}
diff --git a/p2p/host/autorelay/autorelay_test.go b/p2p/host/autorelay/autorelay_test.go
index e7ac6d99..bbdec41d 100644
--- a/p2p/host/autorelay/autorelay_test.go
+++ b/p2p/host/autorelay/autorelay_test.go
@@ -444,3 +444,15 @@ func TestMaxAge(t *testing.T) {
 	}
 	require.Contains(t, ids, relays[0])
 }
+
+func TestIncorrectInit(t *testing.T) {
+	// Check if we panic if we do not correctly initialize the autorelay system.
+	// Common since it's easy to initialize without passing in the correct options: https://github.com/libp2p/go-libp2p/issues/1852
+
+	defer func() {
+		if r := recover(); r == nil {
+			t.Errorf("Expected to panic")
+		}
+	}()
+	_ = newPrivateNode(t)
+}
diff --git a/p2p/host/routed/routed_test.go b/p2p/host/routed/routed_test.go
new file mode 100644
index 00000000..ccae2fdb
--- /dev/null
+++ b/p2p/host/routed/routed_test.go
@@ -0,0 +1,73 @@
+package routedhost
+
+import (
+	"context"
+	"testing"
+
+	"github.com/libp2p/go-libp2p/core/peer"
+	basic "github.com/libp2p/go-libp2p/p2p/host/basic"
+	swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing"
+	ma "github.com/multiformats/go-multiaddr"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+var _ Routing = (*mockRouting)(nil)
+
+type mockRouting struct {
+	callCount  int
+	findPeerFn func(ctx context.Context, id peer.ID) (peer.AddrInfo, error)
+}
+
+func (m *mockRouting) FindPeer(ctx context.Context, pid peer.ID) (peer.AddrInfo, error) {
+	m.callCount++
+	return m.findPeerFn(ctx, pid)
+}
+
+func TestRoutedHostConnectToObsoleteAddresses(t *testing.T) {
+	ctx := context.Background()
+
+	h1, err := basic.NewHost(swarmt.GenSwarm(t), nil)
+	require.NoError(t, err)
+	defer h1.Close()
+
+	h2, err := basic.NewHost(swarmt.GenSwarm(t), nil)
+	require.NoError(t, err)
+	defer h2.Close()
+
+	// construct a wrong multi address for host 2, so that
+	// the initial connection attempt will fail
+	// (we have obsolete, old multi address information)
+	maddr, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1234")
+	require.NoError(t, err)
+
+	// assemble the AddrInfo struct to use for the connection attempt
+	pi := peer.AddrInfo{
+		ID:    h2.ID(),
+		Addrs: []ma.Multiaddr{maddr},
+	}
+
+	// Build mock routing module and replace the FindPeer function.
+	// Now, that function will return the correct multi addresses for host 2
+	// (we have fetched the most up-to-date data from the DHT)
+	mr := &mockRouting{
+		findPeerFn: func(ctx context.Context, pi peer.ID) (peer.AddrInfo, error) {
+			return peer.AddrInfo{
+				ID:    h2.ID(),
+				Addrs: h2.Addrs(),
+			}, nil
+		},
+	}
+
+	// Build routed host
+	rh := Wrap(h1, mr)
+
+	// Try to connect
+	err = rh.Connect(ctx, pi)
+
+	// Connection establishment should have worked without an error
+	assert.NoError(t, err)
+
+	// The mocked FindPeer function should have been called
+	assert.Equal(t, 1, mr.callCount)
+}
diff --git a/p2p/net/connmgr/connmgr_test.go b/p2p/net/connmgr/connmgr_test.go
index 2053e3e6..39db7b54 100644
--- a/p2p/net/connmgr/connmgr_test.go
+++ b/p2p/net/connmgr/connmgr_test.go
@@ -808,36 +808,57 @@ func (m mockConn) GetStreams() []network.Stream                          { panic
 func (m mockConn) Scope() network.ConnScope                              { panic("implement me") }
 func (m mockConn) ConnState() network.ConnectionState                    { return network.ConnectionState{} }
 
+func makeSegmentsWithPeerInfos(peerInfos peerInfos) *segments {
+	var s = func() *segments {
+		ret := segments{}
+		for i := range ret.buckets {
+			ret.buckets[i] = &segment{
+				peers: make(map[peer.ID]*peerInfo),
+			}
+		}
+		return &ret
+	}()
+
+	for _, pi := range peerInfos {
+		segment := s.get(pi.id)
+		segment.Lock()
+		segment.peers[pi.id] = pi
+		segment.Unlock()
+	}
+
+	return s
+}
+
 func TestPeerInfoSorting(t *testing.T) {
 	t.Run("starts with temporary connections", func(t *testing.T) {
-		p1 := peerInfo{id: peer.ID("peer1")}
-		p2 := peerInfo{id: peer.ID("peer2"), temp: true}
+		p1 := &peerInfo{id: peer.ID("peer1")}
+		p2 := &peerInfo{id: peer.ID("peer2"), temp: true}
 		pis := peerInfos{p1, p2}
-		pis.SortByValueAndStreams(false)
+		pis.SortByValueAndStreams(makeSegmentsWithPeerInfos(pis), false)
 		require.Equal(t, pis, peerInfos{p2, p1})
 	})
 
 	t.Run("starts with low-value connections", func(t *testing.T) {
-		p1 := peerInfo{id: peer.ID("peer1"), value: 40}
-		p2 := peerInfo{id: peer.ID("peer2"), value: 20}
+		p1 := &peerInfo{id: peer.ID("peer1"), value: 40}
+		p2 := &peerInfo{id: peer.ID("peer2"), value: 20}
 		pis := peerInfos{p1, p2}
-		pis.SortByValueAndStreams(false)
+		pis.SortByValueAndStreams(makeSegmentsWithPeerInfos(pis), false)
 		require.Equal(t, pis, peerInfos{p2, p1})
 	})
 
 	t.Run("prefer peers with no streams", func(t *testing.T) {
-		p1 := peerInfo{id: peer.ID("peer1"),
+		p1 := &peerInfo{id: peer.ID("peer1"),
 			conns: map[network.Conn]time.Time{
 				&mockConn{stats: network.ConnStats{NumStreams: 0}}: time.Now(),
 			},
 		}
-		p2 := peerInfo{id: peer.ID("peer2"),
+		p2 := &peerInfo{id: peer.ID("peer2"),
 			conns: map[network.Conn]time.Time{
 				&mockConn{stats: network.ConnStats{NumStreams: 1}}: time.Now(),
 			},
 		}
 		pis := peerInfos{p2, p1}
-		pis.SortByValueAndStreams(false)
+		pis.SortByValueAndStreams(makeSegmentsWithPeerInfos(pis), false)
 		require.Equal(t, pis, peerInfos{p1, p2})
 	})
 
@@ -849,27 +870,27 @@ func TestPeerInfoSorting(t *testing.T) {
 
 		outgoingSomeStreams := network.ConnStats{Stats: network.Stats{Direction: network.DirOutbound}, NumStreams: 1}
 		outgoingMoreStreams := network.ConnStats{Stats: network.Stats{Direction: network.DirOutbound}, NumStreams: 2}
-		p1 := peerInfo{
+		p1 := &peerInfo{
 			id: peer.ID("peer1"),
 			conns: map[network.Conn]time.Time{
 				&mockConn{stats: outgoingSomeStreams}: time.Now(),
 			},
 		}
-		p2 := peerInfo{
+		p2 := &peerInfo{
 			id: peer.ID("peer2"),
 			conns: map[network.Conn]time.Time{
 				&mockConn{stats: outgoingSomeStreams}: time.Now(),
 				&mockConn{stats: incoming}:            time.Now(),
 			},
 		}
-		p3 := peerInfo{
+		p3 := &peerInfo{
 			id: peer.ID("peer3"),
 			conns: map[network.Conn]time.Time{
 				&mockConn{stats: outgoing}: time.Now(),
 				&mockConn{stats: incoming}: time.Now(),
 			},
 		}
-		p4 := peerInfo{
+		p4 := &peerInfo{
 			id: peer.ID("peer4"),
 			conns: map[network.Conn]time.Time{
 				&mockConn{stats: outgoingMoreStreams}: time.Now(),
@@ -877,7 +898,7 @@ func TestPeerInfoSorting(t *testing.T) {
 			},
 		}
 		pis := peerInfos{p1, p2, p3, p4}
-		pis.SortByValueAndStreams(true)
+		pis.SortByValueAndStreams(makeSegmentsWithPeerInfos(pis), true)
 		// p3 is first because it is inactive (no streams).
 		// p4 is second because it has the most streams and we priortize killing
 		// connections with the higher number of streams.
@@ -885,13 +906,13 @@ func TestPeerInfoSorting(t *testing.T) {
 	})
 
 	t.Run("in a memory emergency, starts with connections that have many streams", func(t *testing.T) {
-		p1 := peerInfo{
+		p1 := &peerInfo{
 			id: peer.ID("peer1"),
 			conns: map[network.Conn]time.Time{
 				&mockConn{stats: network.ConnStats{NumStreams: 100}}: time.Now(),
 			},
 		}
-		p2 := peerInfo{
+		p2 := &peerInfo{
 			id: peer.ID("peer2"),
 			conns: map[network.Conn]time.Time{
 				&mockConn{stats: network.ConnStats{NumStreams: 80}}: time.Now(),
@@ -899,7 +920,49 @@ func TestPeerInfoSorting(t *testing.T) {
 			},
 		}
 		pis := peerInfos{p1, p2}
-		pis.SortByValueAndStreams(true)
+		pis.SortByValueAndStreams(makeSegmentsWithPeerInfos(pis), true)
 		require.Equal(t, pis, peerInfos{p2, p1})
 	})
 }
+
+func TestSafeConcurrency(t *testing.T) {
+	t.Run("Safe Concurrency", func(t *testing.T) {
+		cl := clock.NewMock()
+
+		p1 := &peerInfo{id: peer.ID("peer1"), conns: map[network.Conn]time.Time{}}
+		p2 := &peerInfo{id: peer.ID("peer2"), conns: map[network.Conn]time.Time{}}
+		pis := peerInfos{p1, p2}
+
+		ss := makeSegmentsWithPeerInfos(pis)
+
+		const runs = 10
+		const concurrency = 10
+		var wg sync.WaitGroup
+		for i := 0; i < concurrency; i++ {
+			wg.Add(1)
+			go func() {
+				// add conns. This mimics new connection events
+				pis := peerInfos{p1, p2}
+				for i := 0; i < runs; i++ {
+					pi := pis[i%len(pis)]
+					s := ss.get(pi.id)
+					s.Lock()
+					s.peers[pi.id].conns[randConn(t, nil)] = cl.Now()
+					s.Unlock()
+				}
+				wg.Done()
+			}()
+
+			wg.Add(1)
+			go func() {
+				pis := peerInfos{p1, p2}
+				for i := 0; i < runs; i++ {
+					pis.SortByValueAndStreams(ss, false)
+				}
+				wg.Done()
+			}()
+		}
+
+		wg.Wait()
+	})
+}
diff --git a/p2p/security/noise/crypto_test.go b/p2p/security/noise/crypto_test.go
index 1ca11476..5c40a5ef 100644
--- a/p2p/security/noise/crypto_test.go
+++ b/p2p/security/noise/crypto_test.go
@@ -93,7 +93,7 @@ func TestCryptoFailsIfHandshakeIncomplete(t *testing.T) {
 	init, resp := net.Pipe()
 	_ = resp.Close()
 
-	session, _ := newSecureSession(initTransport, context.TODO(), init, "remote-peer", nil, nil, nil, true)
+	session, _ := newSecureSession(initTransport, context.TODO(), init, "remote-peer", nil, nil, nil, true, true)
 	_, err := session.encrypt(nil, []byte("hi"))
 	if err == nil {
 		t.Error("expected encryption error when handshake incomplete")
diff --git a/p2p/security/noise/transport_test.go b/p2p/security/noise/transport_test.go
index c3b18047..15b46bc9 100644
--- a/p2p/security/noise/transport_test.go
+++ b/p2p/security/noise/transport_test.go
@@ -232,6 +232,44 @@ func TestPeerIDMismatchInboundFailsHandshake(t *testing.T) {
 	<-done
 }
 
+func TestPeerIDInboundCheckDisabled(t *testing.T) {
+	initTransport := newTestTransport(t, crypto.Ed25519, 2048)
+	respTransport := newTestTransport(t, crypto.Ed25519, 2048)
+	init, resp := newConnPair(t)
+
+	initSessionTransport, err := initTransport.WithSessionOptions(DisablePeerIDCheck())
+	require.NoError(t, err)
+	errChan := make(chan error)
+	go func() {
+		_, err := initSessionTransport.SecureInbound(context.Background(), init, "test")
+		errChan <- err
+	}()
+	_, err = respTransport.SecureOutbound(context.Background(), resp, initTransport.localID)
+	require.NoError(t, err)
+	initErr := <-errChan
+	require.NoError(t, initErr)
+}
+
+func TestPeerIDOutboundNoCheck(t *testing.T) {
+	initTransport := newTestTransport(t, crypto.Ed25519, 2048)
+	respTransport := newTestTransport(t, crypto.Ed25519, 2048)
+	init, resp := newConnPair(t)
+
+	initSessionTransport, err := initTransport.WithSessionOptions(DisablePeerIDCheck())
+	require.NoError(t, err)
+
+	errChan := make(chan error)
+	go func() {
+		_, err := initSessionTransport.SecureOutbound(context.Background(), init, "test")
+		errChan <- err
+	}()
+
+	_, err = respTransport.SecureInbound(context.Background(), resp, "")
+	require.NoError(t, err)
+	initErr := <-errChan
+	require.NoError(t, initErr)
+}
+
 func makeLargePlaintext(size int) []byte {
 	buf := make([]byte, size)
 	rand.Read(buf)