Skip to content

Commit c2e3eec

Browse files
Add ConnectionMode to AcquireOutboundConnection
1 parent 5d2e86d commit c2e3eec

File tree

13 files changed

+193
-27
lines changed

13 files changed

+193
-27
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!--
2+
A new scriv changelog fragment.
3+
4+
Uncomment the section that is right (remove the HTML comment wrapper).
5+
For top level release notes, leave all the headers commented out.
6+
-->
7+
8+
### Breaking
9+
10+
- Modified `establishPeerConnection` in `Test.Cardano.Network.PeerSelection.MockEnvironment`:
11+
- Now only creates a new connection if no inbound connection is found and `ConnectionMode` allows it.
12+
- Added tracing for newly created connections.
13+
14+
### Non-Breaking
15+
16+
- Added a new tracer: `TraceEnvNewConnCreated`.
17+
- Added a property test to verify that the node never connects to peers behind a firewall.

cardano-diffusion/tests/lib/Test/Cardano/Network/PeerSelection.hs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ tests =
154154
prop_governor_target_active_local_below
155155
, testProperty "progresses towards active target (from above)"
156156
prop_governor_target_active_local_above
157+
, testProperty "never connect to peers behind a firewall"
158+
prop_governor_never_connect_peer_behind_firewall
157159
]
158160

159161
, testGroup "big ledger peers"
@@ -799,6 +801,7 @@ envEventCredits (TraceEnvSetLocalRoots peers) = LocalRootPeers.size peers
799801
envEventCredits (TraceEnvSetPublicRoots peers) = PublicRootPeers.size Cardano.ExtraPeers.size peers
800802
envEventCredits TraceEnvRequestPublicRootPeers = 0
801803
envEventCredits TraceEnvRequestBigLedgerPeers = 0
804+
envEventCredits (TraceEnvNewConnCreated _) = 0
802805
envEventCredits TraceEnvPublicRootTTL = 60
803806
envEventCredits TraceEnvBigLedgerPeersTTL = 60
804807

@@ -3206,6 +3209,56 @@ prop_governor_target_established_above (MaxTime maxTime) env =
32063209
<*> govInProgressIneligibleSig
32073210
<*> demotionOpportunitiesIgnoredTooLong)
32083211

3212+
-- | Avoid connecting to root peers marked as behind a firewall and without inbound connection.
3213+
prop_governor_never_connect_peer_behind_firewall :: MaxTime -> GovernorMockEnvironment -> Property
3214+
prop_governor_never_connect_peer_behind_firewall (MaxTime maxTime) env =
3215+
let events = Signal.eventsFromListUpToTime maxTime
3216+
. selectPeerSelectionTraceEvents
3217+
@Cardano.ExtraState
3218+
@PeerTrustable
3219+
@(Cardano.ExtraPeers PeerAddr)
3220+
@(Cardano.ExtraPeerSelectionSetsWithSizes PeerAddr)
3221+
@Cardano.ExtraTrace
3222+
. runGovernorInMockEnvironment
3223+
$ env
3224+
3225+
govLocalRootPeersSig :: Signal (LocalRootPeers PeerTrustable PeerAddr)
3226+
govLocalRootPeersSig =
3227+
selectGovState Governor.localRootPeers
3228+
(Cardano.ExtraState.empty (consensusMode env) (NumberOfBigLedgerPeers 0)) Cardano.ExtraPeers.empty
3229+
events
3230+
3231+
govUnreachablePeersSig :: Signal (Set PeerAddr)
3232+
govUnreachablePeersSig =
3233+
(\local ->
3234+
let
3235+
isUnreachablePeer (LocalRootConfig {localRootBehindFirewall}) = localRootBehindFirewall
3236+
3237+
unreachablePeers =
3238+
Map.keysSet
3239+
$ Map.filter isUnreachablePeer
3240+
$ LocalRootPeers.toMap local
3241+
in
3242+
unreachablePeers
3243+
) <$> govLocalRootPeersSig
3244+
3245+
newConnectionSig :: Signal (Maybe PeerAddr)
3246+
newConnectionSig =
3247+
Signal.fromEvents
3248+
. Signal.selectEvents
3249+
(\case TraceEnvNewConnCreated addr -> Just $! addr
3250+
_other -> Nothing)
3251+
. selectEnvEvents
3252+
$ events
3253+
3254+
in counterexample
3255+
"\nSignal key: (local root peers, unreachable local root peers, new connection)" $
3256+
3257+
signalProperty 100 show
3258+
(\(_,unreachablePeers,newConnection) -> maybe True (not . flip Set.member unreachablePeers) newConnection)
3259+
((,,) <$> govLocalRootPeersSig
3260+
<*> govUnreachablePeersSig
3261+
<*> newConnectionSig)
32093262

32103263
-- | Like 'prop_governor_target_established_above' but for big ledger peers.
32113264
--
@@ -3560,7 +3613,7 @@ prop_governor_target_established_local (MaxTime maxTime) env =
35603613
promotionOpportunitiesIgnoredTooLong :: Signal (Set PeerAddr)
35613614
promotionOpportunitiesIgnoredTooLong =
35623615
Signal.keyedTimeout
3563-
1 -- seconds
3616+
2 -- seconds -- cabal run cardano-diffusion:cardano-diffusion-sim-tests -- --quickcheck-replay="(SMGen 13722084053961804625 14989040016076027617,42)" -p '/local root peers/&&/progresses towards established target/'
35643617
id
35653618
promotionOpportunities
35663619

cardano-diffusion/tests/lib/Test/Cardano/Network/PeerSelection/MockEnvironment.hs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import Data.IP (toIPv4w)
4848

4949
import Control.Concurrent.Class.MonadSTM
5050
import Control.Concurrent.Class.MonadSTM.Strict qualified as StrictTVar
51-
import Control.Exception (throw)
51+
import Control.Exception (throw, ErrorCall (..))
5252
import Control.Monad (forM, when)
5353
import Control.Monad.Class.MonadAsync
5454
import Control.Monad.Class.MonadFork
@@ -62,6 +62,7 @@ import Control.Monad.IOSim
6262
import Control.Tracer (Tracer (..), contramap, traceWith)
6363

6464
import Ouroboros.Network.BlockFetch (FetchMode (..), PraosFetchMode (..))
65+
import Ouroboros.Network.ConnectionManager.Types (ConnectionMode (..))
6566
import Ouroboros.Network.DiffusionMode
6667
import Ouroboros.Network.ExitPolicy
6768
import Ouroboros.Network.PeerSelection hiding (requestPublicRootPeers)
@@ -330,6 +331,7 @@ data TraceMockEnv = TraceEnvAddPeers !PeerGraph
330331
| TraceEnvSetTargets !PeerSelectionTargets
331332
| TraceEnvPeersDemote !AsyncDemotion !PeerAddr
332333
| TraceEnvEstablishConn !PeerAddr
334+
| TraceEnvNewConnCreated !PeerAddr
333335
| TraceEnvActivatePeer !PeerAddr
334336
| TraceEnvDeactivatePeer !PeerAddr
335337
| TraceEnvCloseConn !PeerAddr
@@ -549,20 +551,34 @@ mockPeerSelectionActions' tracer
549551
threadDelay (interpretPeerShareTime time)
550552
traceWith tracer (TraceEnvPeerShareResult addr peeraddrs)
551553
return (PeerSharingResult peeraddrs)
552-
553-
establishPeerConnection :: IsBigLedgerPeer -> DiffusionMode -> PeerAddr -> m (PeerConn m)
554-
establishPeerConnection _ _ peeraddr = do
554+
establishPeerConnection :: IsBigLedgerPeer -> DiffusionMode -> PeerAddr -> ConnectionMode -> m (PeerConn m)
555+
establishPeerConnection _ _ peeraddr connMode = do
555556
--TODO: add support for variable delays and synchronous failure
556557
traceWith tracer (TraceEnvEstablishConn peeraddr)
557558
threadDelay 1
558559
let Just (_, peerSharingScript, connectScript) = Map.lookup peeraddr scripts
559-
conn@(PeerConn _ _ v) <- atomically $ do
560-
conn <- newTVar PeerWarm
560+
(conn@(PeerConn _ _ v), newConnCreated) <- atomically $ do
561561
conns <- readTVar connsVar
562-
let !conns' = Map.insert peeraddr conn conns
562+
(conn, conns', newConnCreated) <- maybe
563+
( case connMode of
564+
CreateNewIfNoInbound -> do
565+
conn <- newTVar PeerWarm
566+
pure (conn, conns, True)
567+
RequireInbound -> do
568+
throwSTM (ErrorCall "No inbound connection found.")
569+
)
570+
(\conn -> do
571+
pure (conn, Map.insert peeraddr conn conns, False)
572+
)
573+
(Map.lookup peeraddr conns)
574+
563575
writeTVar connsVar conns'
564576
remotePeerSharing <- stepScriptSTM peerSharingScript
565-
return (PeerConn peeraddr (peerSharingFlag <> remotePeerSharing) conn)
577+
return (PeerConn peeraddr (peerSharingFlag <> remotePeerSharing) conn, newConnCreated)
578+
579+
when newConnCreated $
580+
traceWith tracer (TraceEnvNewConnCreated peeraddr)
581+
566582
_ <- async $
567583
-- monitoring loop which does asynchronous demotions. It will terminate
568584
-- as soon as either of the events:
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<!--
2+
A new scriv changelog fragment.
3+
4+
Uncomment the section that is right (remove the HTML comment wrapper).
5+
For top level release notes, leave all the headers commented out.
6+
-->
7+
8+
### Breaking
9+
10+
- Changed the type of `localRoots` to `LocalRoots`.
11+
- Modified `AcquireOutboundConnection` to include an additional parameter: `ConnectionMode`.
12+
- `acquireOutboundConnectionImpl` only creates a new connection if the `ConnectionMode` function permits it.
13+
- `jobPromoteColdPeer` only creates a new connection if no inbound connection is found and the peer is not behind a firewall.
14+
15+
### Non-Breaking
16+
17+
- Added `LocalRoots` type in `Ouroboros.Network.PeerSelection.State.LocalRootPeers` with the following fields:
18+
- `rootConfig` of type `RootConfig`
19+
- `behindFirewall` of type `Bool`
20+
- Added `localRootBehindFirewall` field to `LocalRootConfig`.
21+
- Added a new sum type: `ConnectionMode`.
22+
- Added a new constructor `InboundConnectionNotFound` for `ConnectionManagerError`.

ouroboros-network/demo/connection-manager.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -569,9 +569,9 @@ bidirectionalExperiment
569569
(HandlerError
570570
UnversionedProtocol))
571571
connect n cm | n <= 1 =
572-
acquireOutboundConnection cm InitiatorAndResponderDiffusionMode remoteAddr
572+
acquireOutboundConnection cm InitiatorAndResponderDiffusionMode remoteAddr CreateNewIfNoInbound
573573
connect n cm =
574-
acquireOutboundConnection cm InitiatorAndResponderDiffusionMode remoteAddr
574+
acquireOutboundConnection cm InitiatorAndResponderDiffusionMode remoteAddr CreateNewIfNoInbound
575575
`catch` \(_ :: IOException) -> threadDelay 1
576576
>> connect (pred n) cm
577577
`catch` \(_ :: Mux.Error) -> threadDelay 1

ouroboros-network/framework/lib/Ouroboros/Network/ConnectionManager/Core.hs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1338,8 +1338,9 @@ with args@Arguments {
13381338
-> ConnectionHandlerFn handlerTrace socket peerAddr handle handleError version versionData m
13391339
-> DiffusionMode
13401340
-> peerAddr
1341+
-> ConnectionMode
13411342
-> m (Connected peerAddr handle handleError)
1342-
acquireOutboundConnectionImpl stateVar stdGenVar handler diffusionMode peerAddr = do
1343+
acquireOutboundConnectionImpl stateVar stdGenVar handler diffusionMode peerAddr connectionMode = do
13431344
let provenance = Outbound
13441345
traceWith tracer (TrIncludeConnection provenance peerAddr)
13451346
(trace, mutableConnState@MutableConnState { connVar, connStateId }
@@ -1486,6 +1487,10 @@ with args@Arguments {
14861487

14871488
-- connection manager does not have a connection with @peerAddr@.
14881489
Right Nowhere -> do
1490+
-- Only proceed if creating a new connection is allowed
1491+
when (inboundRequired connectionMode) $
1492+
throwIO (withCallStack $ InboundConnectionNotFound peerAddr)
1493+
14891494
(reader, writer) <- newEmptyPromiseIO
14901495

14911496
(connId, connThread) <-

ouroboros-network/framework/lib/Ouroboros/Network/ConnectionManager/Types.hs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ module Ouroboros.Network.ConnectionManager.Types
114114
, resultInState
115115
, DemotedToColdRemoteTr (..)
116116
, AcquireOutboundConnection
117+
, ConnectionMode (..)
118+
, inboundRequired
117119
, IncludeInboundConnection
118120
-- *** Outbound side
119121
, acquireOutboundConnection
@@ -497,9 +499,23 @@ data Connected peerAddr handle handleError =
497499
--
498500
| Disconnected !(ConnectionId peerAddr) !(DisconnectionException handleError)
499501

502+
-- | Describes the behavior for handling connections when no inbound connection
503+
-- is found.
504+
-- - 'CreateNewIfNoInbound': If no inbound connection exists, create a new
505+
-- conection.
506+
-- - 'RequireInbound': Strictly require an inbound connection; fail if none
507+
-- exists.
508+
data ConnectionMode
509+
= CreateNewIfNoInbound
510+
| RequireInbound
511+
512+
inboundRequired :: ConnectionMode -> Bool
513+
inboundRequired RequireInbound = True
514+
inboundRequired _other = False
500515

501516
type AcquireOutboundConnection peerAddr handle handleError m
502-
= DiffusionMode -> peerAddr -> m (Connected peerAddr handle handleError)
517+
= DiffusionMode -> peerAddr -> ConnectionMode -> m (Connected peerAddr handle handleError)
518+
503519
type IncludeInboundConnection socket peerAddr handle handleError m
504520
= Word32
505521
-- ^ inbound connections hard limit.
@@ -723,6 +739,11 @@ data ConnectionManagerError peerAddr
723739
--
724740
| ForbiddenConnection !(ConnectionId peerAddr) !CallStack
725741

742+
-- | No matching inbound connection found and creating new connection is
743+
-- not allowed.
744+
--
745+
| InboundConnectionNotFound !peerAddr !CallStack
746+
726747
-- | Connections that would be forbidden by the kernel (@TCP@ semantics).
727748
--
728749
| ImpossibleConnection !(ConnectionId peerAddr) !CallStack
@@ -774,6 +795,12 @@ instance ( Show peerAddr
774795
, "\n"
775796
, prettyCallStack cs
776797
]
798+
displayException (InboundConnectionNotFound peerAddr cs) =
799+
concat [ "No matching inbound connection found and creating new connection is not allowed with peer "
800+
, show peerAddr
801+
, "\n"
802+
, prettyCallStack cs
803+
]
777804
displayException (ConnectionTerminating connId cs) =
778805
concat [ "Connection terminating "
779806
, show connId

ouroboros-network/framework/sim-tests/Test/Ouroboros/Network/ConnectionManager.hs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@ prop_valid_transitions (Fixed rnd) (SkewedBool bindToLocalAddress) scheduleMap =
817817
-- handshake negotiation.
818818
timeout (1 + 5 + testTimeWaitTimeout)
819819
(acquireOutboundConnection
820-
connectionManager InitiatorAndResponderDiffusionMode addr))
820+
connectionManager InitiatorAndResponderDiffusionMode addr CreateNewIfNoInbound))
821821
`catches`
822822
[ Handler $ \(e :: IOException) -> return (Left (toException e))
823823
, Handler $ \(e :: SomeConnectionManagerError) ->
@@ -983,10 +983,11 @@ prop_valid_transitions (Fixed rnd) (SkewedBool bindToLocalAddress) scheduleMap =
983983
Just (SomeConnectionManagerError e@UnknownPeer {}) -> throwIO e
984984

985985
-- If 'ForbiddenConnection' is thrown we let the test continue.
986-
Just (SomeConnectionManagerError ForbiddenConnection {}) -> pure ()
987-
Just (SomeConnectionManagerError ConnectionExists {}) -> pure ()
988-
Just (SomeConnectionManagerError ConnectionTerminating {}) -> pure ()
989-
Just (SomeConnectionManagerError ConnectionTerminated {}) -> pure ()
986+
Just (SomeConnectionManagerError ForbiddenConnection {}) -> pure ()
987+
Just (SomeConnectionManagerError ConnectionExists {}) -> pure ()
988+
Just (SomeConnectionManagerError ConnectionTerminating {}) -> pure ()
989+
Just (SomeConnectionManagerError ConnectionTerminated {}) -> pure ()
990+
Just (SomeConnectionManagerError InboundConnectionNotFound {}) -> pure ()
990991

991992

992993
-- | This includes the @Overwritten@ transition.

ouroboros-network/framework/sim-tests/Test/Ouroboros/Network/Server/Sim.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,7 @@ multinodeExperiment inboundTrTracer trTracer inboundTracer debugTracer cmTracer
879879
case fromException e of
880880
Just SomeAsyncException {} -> Nothing
881881
_ -> Just e)
882-
$ acquireOutboundConnection cm InitiatorAndResponderDiffusionMode remoteAddr
882+
$ acquireOutboundConnection cm InitiatorAndResponderDiffusionMode remoteAddr CreateNewIfNoInbound
883883
case connHandle of
884884
Left _ ->
885885
go connMap

ouroboros-network/framework/tests-lib/Test/Ouroboros/Network/ConnectionManager/Experiments.hs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ unidirectionalExperiment stdGen timeouts snocket makeBearer confSock socket clie
770770
replicateM
771771
(numberOfRounds clientAndServerData)
772772
(bracket
773-
(acquireOutboundConnection connectionManager InitiatorOnlyDiffusionMode serverAddr)
773+
(acquireOutboundConnection connectionManager InitiatorOnlyDiffusionMode serverAddr CreateNewIfNoInbound)
774774
(\case
775775
Connected connId _ _ -> releaseOutboundConnection connectionManager connId
776776
Disconnected {} -> error "unidirectionalExperiment: impossible happened")
@@ -876,7 +876,8 @@ bidirectionalExperiment
876876
(acquireOutboundConnection
877877
connectionManager0
878878
InitiatorAndResponderDiffusionMode
879-
localAddr1))
879+
localAddr1
880+
CreateNewIfNoInbound))
880881
(\case
881882
Connected connId _ _ ->
882883
releaseOutboundConnection
@@ -902,7 +903,8 @@ bidirectionalExperiment
902903
(acquireOutboundConnection
903904
connectionManager1
904905
InitiatorAndResponderDiffusionMode
905-
localAddr0))
906+
localAddr0
907+
CreateNewIfNoInbound))
906908
(\case
907909
Connected connId _ _ ->
908910
releaseOutboundConnection

0 commit comments

Comments
 (0)