Nested Class Summary
Modifier and TypeClassDescriptionprivate classPeriodically drops temp-bans whose time has elapsed and resets the per-dest refusal window, so a ban always expires (24h default) and the refusal counter doesn't grow unbounded.private classTimer event that removes a pending ping from the map and notifies the caller on timeout.static interfaceThe callback interface for a pong.private static classHolds the state for a pending ping request, including optional payload and notification callback.private static classWrapper for a pooled connection with metadata.private static classEncapsulates a connection rejection reason with an optional Retry-After duration in seconds.(package private) static enumOutcome of evaluating one SYN burst-gate trip against the dest's recorded strike.(package private) static final classA recorded SYN-burst strike, and how many distinct burst windows this dest has tripped inside the current strike window.Field Summary
Modifier and TypeFieldDescriptionprivate final ReentrantReadWriteLockAdmission barrier: readers hold it across a whole reserve-to-bind span (tryReserveStream through assignReceiveStreamId) so a reservation can never straddledisconnectAllHard()'s ledger clear.private booleanCached autoban switch (PROP_AUTOBAN), refreshed by the BanExpiry sweeper like the other hot-path config.private ConnectionManager.BanExpiryThe sweeper owned by this manager.private final ByteCachePooled buffers for packet payloads.private final ConcurrentHashMap<Long, Connection> Inbound stream ID (Long) to Connection map.private final ConcurrentHashMap<Long, Connection> Outbound stream ID (Long) to Connection map — reverse index for O(1) lookup.private final IncomingConnectionFilterDecides whether to accept inbound connections.private final ConnectionHandlerRuns the connection state machines.private final ConnectionPacketHandlerHandles packets for one connection.private final I2PAppContextThe app context.private final ObjectLock for interruptible cooldown wait inconnect(Destination, ConnectionOptions, I2PSession)— notified on shutdown.private final ConcurrentHashMap<Hash, Long> When we last logged the "Delaying connect" cooldown WARN per destination.private ConnThrottlerLimits inbound connections per day.private final ConnectionOptionsOptions used for new connections.private final ConcurrentHashMap<Hash, Long> Per-destination cooldown to avoid hammering unreachable peers.Destinations blacklisted across all managers.private ConnThrottlerLimits inbound connections per hour.private final LogLogger for the streaming session.private final MessageHandlerHandles the session's messages.private ConnThrottlerLimits inbound connections per minute.private final AtomicIntegerThreads waiting for an inbound connection slot.private final PacketQueueQueue of outbound packets.private final PacketHandlerProcesses inbound packets.private final ConcurrentHashMap<Long, ConnectionManager.PingRequest> Ping ID (Long) to PingRequest.Recently closed connections, to reject quick reopens.private final ConcurrentHashMap<Hash, long[]> Per-destination burst state for the sub-second SYN gate: value is a two-element array {burstStartMs, count}, updated at most once per validated SYN via compare-and-swap (replace).private final ObjectCounter<Hash> Rolling refusals per destination, incremented each time the MAXIMUM streams gate refuses a SYN from that dest.private final SchedulerChooserChooses the outbound packet scheduler.private final I2PSessionThe session carrying the packets.private longSocket read timeout in ms.private final ConcurrentHashMap<Hash, ConcurrentLinkedDeque<ConnectionManager.PooledConnection>> Stream connection pool — reuses established streams to the same destination.private final ConcurrentHashMap<Long, Hash> Reservation tokens: receive stream ID → the dest whose budget the stream was admitted under.private final ConcurrentHashMap<Hash, AtomicInteger> Live concurrent-stream count keyed by remote destination.private final ConcurrentHashMap<Hash, Integer> Reconciliation memory: whatConnectionManager.BanExpirylast observed for a dest whose ledger count exceeded the live connection table.private intSYNs in a burst that trigger a temp ban.private final ConcurrentHashMap<Hash, ConnectionManager.SynStrike> Each dest's most recent SYN-burst strikes: the start of the burst window that crossed the threshold, the time that strike was recorded, and how many distinct windows have tripped inside the current strike window.private longCached sub-second burst config, refreshed by the BanExpiry sweeper rather than re-read from the property store on every validated SYN (hot path).private final TCBShareShares the congestion window between connections.private longCached autoban duration (ms) and refusal threshold, refreshed by the BanExpiry sweeper so the hot path (isTempBanned / refusal latch) avoids per-SYN property-store reads.private final ConcurrentHashMap<Hash, String> Trigger reason for an active temp-ban, kept alongside _tempBanUntil so the enforcement path can tell a client *why* it was banned (e.g.private longRefusals that trigger a temp ban.private intCached strikes-to-ban count, refreshed by the BanExpiry sweeper.private final ConcurrentHashMap<Hash, Long> Temporary bans keyed by destination hash -> bannedUntil epoch ms.private booleanWhether the throttlers have been created.private final RetransmissionTimerThe retransmission timer for this manager.private static StringCache of the property to detect changes.private static final intNegative means no permanent autoban.private static final booleanPong delay is off by default.private static final longThe default temp ban lasts 5 minutes.private static final longThe default SYN rate window is 1 second.private static final longDefault refused connections that trigger a temp ban.private static final intTemp bans before a permanent ban.private static final intDefault SYNs in a burst that trigger a temp ban.private static final ObjectPlaceholder value in the recently-closed map.(package private) static final long[]Sample periods for the send-event counters whose ratio the streaming ceiling reads.private static final StringThe http 429 response sent when over the limit.private static final intLongest artificial pong delay, in ms.private static final intLargest pong payload accepted, in bytes.private static final intLargest ban length accepted, effectively forever.static final StringAutoban property: whether autoban enforcement is enabled.static final StringBlacklist property for streaming.private static final StringConfig key enabling the PONG response delay.(package private) static final StringProperty: maximum pong delay in ms.static final StringAutoban property: a dest is temporarily banned until this many minutes after it first trips a flood threshold.static final StringAutoban property: a dest sending more than tempBanSynBurst SYNs within a tempBanSynRate-ms rolling window is auto-banned.static final StringAutoban property: refusals from a single dest within a one-minute window (as counted by _refusalCounter) at which the dest is auto-banned.static final StringAutoban property: how many DISTINCT burst windows insideSTRIKE_WINDOW_MSa dest must trip before the SYN-burst gate autobans it.static final StringAutoban property: sub-second burst threshold (SYNs within rate window).private static final long[]Rate stat periods for the throttler stats.(package private) static final long[]Sample periods for the stream-close retransmission ratios.(package private) static final longStrike window for the multi-strike SYN-burst gate: strikes more than this many ms apart are forgiven, so the required count has to be met inside one rolling window.Constructor Summary
ConstructorsConstructorDescriptionConnectionManager(I2PAppContext context, I2PSession session, ConnectionOptions defaultOptions, IncomingConnectionFilter connectionFilter) Manage all conns for this session.Method Summary
Modifier and TypeMethodDescriptionprivate ConnectionEverything about a new inbound connection up to the point where its payload is handed over: the SYN burst gates, the admission reservation, the options and MTU negotiation, and the connection's construction.private ConnectionacquireFromPool(Destination peer) Try to acquire a pooled connection for the given destination.booleanDo we respond to pings that aren't on an existing connection?(package private) static ConnectionManager.SynBurstActionapplySynBurstStrike(ConcurrentHashMap<Hash, ConnectionManager.SynStrike> strikes, Hash h, long windowStart, long now, long strikeWindowMs, int requiredStrikes) Atomically evaluate and apply a burst-gate trip againsth's recorded strike.private longPick a new random stream ID for a ping and assign it, taking care to avoid duplicates, and return it.private voidassignReceiveStreamId(Connection con, Hash destHash) Pick a new random stream ID for the con and assign it, taking care to avoid duplicates, and put it in the connection table.private longPick a new random stream ID that we are rejecting, taking care to avoid duplicates, and return it.(package private) static longawaitCooldown(Object lock, long delay, Clock clock) Wait out the remainder of a destination's connect cooldown.(package private) static booleanWhether a bannedUntil time is still in the future.(package private) static booleanbanIsLonger(Long existing, Long candidate) Whether a new ban end time should replace an existing one: only when it is strictly longer.(package private) booleanBan a dest for the configured duration.(package private) static booleancanReserveStream(int held, int max) Pure admission decision for the per-dest stream budget: one more stream fits while the dest's live count is strictly below the ceiling.(package private) booleancheckInboundSynFlood(Hash h, long now) Package-visible flood gate for the retransmit-SYN path inConnectionHandler.receiveNewSyn(Packet).private longcheckSynBurst(Hash h, long now, boolean autoban) Whether a SYN from a dest tripped the sub-second burst gate.(package private) voidPeriodic pool cleanup — evicts stale connections, then drops the buckets they leave behind.connect(Destination peer, ConnectionOptions opts, I2PSession session) Build a new connection to the given peer.(package private) static HashconsumeReservation(ConcurrentHashMap<Long, Hash> tokens, ConcurrentHashMap<Hash, AtomicInteger> ledger, long receiveStreamId) Consume the reservation token bound to a receive stream ID, releasing the dest budget slot it owns.voidDrop every connection, sending a RESET instead of a CLOSE handshake.private ConnectionfinalizeConnection(Connection con, Destination peer, ConnectionOptions opts, long connectStart, boolean fromPool) Common post-creation setup for both pooled and new connections.booleanWhether incoming connections are accepted.(package private) ConnectiongetConnectionByInboundId(long id) Look up a connection by its inbound stream ID.(package private) ConnectiongetConnectionByOutboundId(long id) Look up a connection by its outbound stream ID.Connection handler for this manager.private longDefault stream delay maximum when no connect timeout is set.private longCooldown between connection attempts to the same failed destination.private intOnce over throttle limits, respond this many times before just dropping.private intThe concurrent-stream cap to enforce right now.private intThe cap to report in refusal log messages: the Tuner's current override when it has armed one, otherwise the user-configured ceiling.private longMaximum ping timeout.Message handler for this manager.Packet handler for this manager.Outbound packet queue.private longMax idle time before a pooled stream is closed and evicted.private intMax pooled streams per destination.This is the primary session only.longSocket accept() timeout.private longReturns the temp ban length from the config.private booleanWhether stream pooling is enabled.(package private) booleanisTempBanned(Hash h, long now) Whether the dest is currently temp-banned.(package private) static StringlimitResponse(String limitAction, int retryAfter, boolean sslPort) The payload to send in a limit-response SYN/CLOSE, or null for a RESET.Connections currently managed.private booleannoteSynBurstStrike(Hash h, long windowStart, long now) Record a SYN-burst strike forhand decide whether this trip autobans (seesynBurstStrikeAction(ConnectionManager.SynStrike, long, long, long, int)).Live connection count per dest, preferring the reservation token (the dest that was actually charged at admission) and falling back to the connection's remote peer for anything bound outside the protocol.booleanping(Destination peer, int fromPort, int toPort, long timeoutMs) Ping the destination and wait for a pong.booleanping(Destination peer, int fromPort, int toPort, long timeoutMs, boolean blocking) Ping the destination, optionally waiting for a pong.booleanping(Destination peer, int fromPort, int toPort, long timeoutMs, boolean blocking, ConnectionManager.PingNotifier notifier) Ping the destination, optionally waiting for a pong.byte[]ping(Destination peer, int fromPort, int toPort, long timeoutMs, byte[] payload) Ping the destination with a payload and wait for the pong.booleanping(Destination peer, long timeoutMs) Ping the destination and wait for a pong.booleanping(Destination peer, I2PSession session, int fromPort, int toPort, long timeoutMs) Ping the destination through a specific subsession, waiting for the pong.booleanping(Destination peer, I2PSession session, int fromPort, int toPort, long timeoutMs, boolean blocking) Ping the destination through a specific subsession, optionally blocking.byte[]ping(Destination peer, I2PSession session, int fromPort, int toPort, long timeoutMs, byte[] payload) Ping the destination with a payload through a specific subsession.(package private) static intpruneEmptyPoolBuckets(Map<Hash, ? extends Collection<?>> pools) Remove destination buckets whose connection deque has gone empty, so a destination that is no longer contacted does not keep a (now-empty) pool entry alive forever.(package private) voidPublish a closing connection's lifetime counters.receiveConnection(Packet synPacket) Create a new connection based on the SYN packet we received.booleanreceivePing(Connection con, Packet ping) Process a ping by checking for throttling, etc., then sending a pong.(package private) voidreceivePong(long pingId, ByteArray payload) Process a received pong response.(package private) static intreconcileStreamCount(int held, int observed, Integer candidate, int boundTokens) Pure reconciliation step for one dest: how much of a ledger count that exceeds the live connection table should survive this sweep.(package private) static voidreconcileStreamSlots(ConcurrentHashMap<Hash, AtomicInteger> ledger, Map<Hash, Integer> observed, ConcurrentHashMap<Hash, Integer> candidates, Map<Hash, Integer> boundTokens) Reconcile the per-dest stream ledger against the live connection table.(package private) static booleanrefusalThresholdMet(long refusals, long threshold) Whether a dest has tripped the autoban refusal threshold.(package private) voidRegister outbound id.(package private) static voidRegister the stream-close retransmission ratio stats, including the five-minute period the router's congestion tuner samples.private HashRelease whatever this connection still holds against a dest budget: first its reservation token, and only if no token was bound, its remote peer (a connection built outside the reservation protocol, so the slot must still be drained or the budget leaks forever).private HashreleaseReservation(long receiveStreamId) Consume the reservation bound to a receive stream ID, releasing its slot.private voidReturn a stream slot taken outside the token protocol (a SYN rejected after admission, or a connection built before a token was bound).(package private) static voidreleaseStreamSlot(ConcurrentHashMap<Hash, AtomicInteger> ledger, Hash h) Return one stream slot to a dest's budget.(package private) static intreleaseWaiting(AtomicInteger waiting) Decrement a connect-attempt waiter count without ever going negative.voidRemove a connection from the manager.(package private) static booleanreserveStreamSlot(ConcurrentHashMap<Hash, AtomicInteger> ledger, Hash h, int max) Atomically take one stream slot from a dest's budget: the ceiling check and the increment happen inside a single per-keycompute(), so two SYNs racing the gate can no longer both observe "room left" and admit past the ceiling (the check-then-add this replaced).private voidreturnToPool(Connection con) Return a connection to the pool if healthy.private voidsendRejectResponse(Packet synPacket, Destination from, Hash fromHash, int retryAfter) Send the configured response for a rejected SYN (RESET, HTTP-style Retry-After payload, or custom payload), subject to the peer's throttler status.voidsetAllowIncomingConnections(boolean allow) Enable or disable acceptance of incoming connections.voidsetSoTimeout(long x) Socket accept() timeout.(package private) static booleanshouldLogCooldownWarn(long lastWarnTime, long now, long cooldownMs) Whether to log another cooldown-delay WARN for a destination.private ConnectionManager.ReasonCheck if a connection should be rejected based on blacklists, access lists, throttling, and the connection filter.voidshutdown()Kill all connections and the timers.(package private) static intstreamSlotCount(ConcurrentHashMap<Hash, AtomicInteger> ledger, Hash h) Read the stream count a dest is actually holding in the ledger, or 0 if it holds none.private static booleanstrikeForgiven(ConnectionManager.SynStrike last, long now, long strikeWindowMs) Whetherlasthas aged out of the forgiveness window (or was stamped in the future by a clock adjustment, which is treated the same way so a skewed clock can never ban).(package private) static ConnectionManager.SynBurstActionsynBurstStrikeAction(ConnectionManager.SynStrike last, long windowStart, long now, long strikeWindowMs, int requiredStrikes) Pure decision for the multi-strike SYN-burst gate.(package private) static booleansynBurstTripped(Long burstStartMs, int count, long now, long windowMs, int burst) Pure decision for the sub-second SYN-burst gate: whether a dest has sent more thanburstSYNs within the lastwindowMsms.(package private) static booleansynNacksMatch(long[] nacks, Hash localHash) Whether a SYN's 8 NACK words match the local destination hash.private StringThe stored trigger description for an active temp-ban, if any.Bound reservation tokens per dest: the ABA floor forreconcileStreamCount(int, int, Integer, int).(package private) static booleantooManyStreamsForDest(int streamCount, int max) Pure decision for the per-destination stream budget: a dest is over budget once its live concurrent-stream count reaches the per-dest ceiling.toString()Connection summary for log messages.private voidRemove expired entries from_destFailuresand_cooldownWarned.private booleantryReserveStream(Hash h, int max) Take a stream slot from this dest's budget.voidUpdate the throttler options.voidUpdate opts from share.voidUpdate share opts.booleanwasRecentlyClosed(long inboundID) Was this conn recently closed?
Field Details
_context
The app context._log
Logger for the streaming session._session
The session carrying the packets._messageHandler
Handles the session's messages._packetHandler
Processes inbound packets._connectionHandler
Runs the connection state machines._outboundQueue
Queue of outbound packets._schedulerChooser
Chooses the outbound packet scheduler._conPacketHandler
Handles packets for one connection._connectionFilter
Decides whether to accept inbound connections._connectionByInboundId
Inbound stream ID (Long) to Connection map._connectionByOutboundId
Outbound stream ID (Long) to Connection map — reverse index for O(1) lookup._pendingPings
Ping ID (Long) to PingRequest._throttlersInitialized
private volatile boolean _throttlersInitializedWhether the throttlers have been created._defaultOptions
Options used for new connections._numWaiting
Threads waiting for an inbound connection slot._soTimeout
private long _soTimeoutSocket read timeout in ms._minuteThrottler
Limits inbound connections per minute._hourThrottler
Limits inbound connections per hour._dayThrottler
Limits inbound connections per day._timer
The retransmission timer for this manager._recentlyClosed
_cache
Pooled buffers for packet payloads.DUMMY
Placeholder value in the recently-closed map._cooldownLock
Lock for interruptible cooldown wait inconnect(Destination, ConnectionOptions, I2PSession)— notified on shutdown._destFailures
Per-destination cooldown to avoid hammering unreachable peers. Key is destination Hash, value is timestamp of last failed connect._cooldownWarned
When we last logged the "Delaying connect" cooldown WARN per destination. Complements_destFailures: a dest that keeps failing re-enters cooldown (and thus arms the WARN) on every attempt, so without this the message would fire once per connect() against an unreachable peer. The key is pruned together with_destFailuresusing the same cutoff._streamPools
private final ConcurrentHashMap<Hash, ConcurrentLinkedDeque<ConnectionManager.PooledConnection>> _streamPoolsStream connection pool — reuses established streams to the same destination. Key: destination Hash, Value: deque of pooled connections.RATES
private static final long[] RATESRate stat periods for the throttler stats.RTX_RATIO_RATES
static final long[] RTX_RATIO_RATESSample periods for the stream-close retransmission ratios. The five minute period is mandatory, not cosmetic:Tuner.getAdditionalStat5Min()reads exactly FIVE_MINUTES from both stats to measure routing congestion, and a RateStat without that period makes addRateData() silently drop those samples, leaving the tuner with NaN forever.EVENT_RATES
static final long[] EVENT_RATESSample periods for the send-event counters whose ratio the streaming ceiling reads. The five-minute period is required so the event-count ratio can be compared againstRTX_RATIO_RATESon the same window — without it the two signals live on different clocks and every comparison mixes a 60 s event figure with a 5 min byte figure.currentBlacklist
Cache of the property to detect changes._globalBlacklist
_tempBanUntil
Temporary bans keyed by destination hash -> bannedUntil epoch ms. Autoban hammering dests for 24h (ori2p.streaming.tempBanMinutes). Enforced inshouldRejectConnection(Packet)before the port/budget counters so a banned dest no longer consumes its per-dest budget or per-peer throttlers. Expired entries are removed by BanExpiry._tempBanReason
Trigger reason for an active temp-ban, kept alongside _tempBanUntil so the enforcement path can tell a client *why* it was banned (e.g. the per-minute limit it tripped). Written on ban, removed on expiry by BanExpiry._refusalCounter
Rolling refusals per destination, incremented each time the MAXIMUM streams gate refuses a SYN from that dest. Empties when a dest is banned or when BanExpiry clears the window._streamsByDest
Live concurrent-stream count keyed by remote destination. Each remote dest gets its own stream budget (captured from the same effective max as the old global gate), so a flood from one dest can no longer starve legitimate clients sharing the listener. Slots are only ever taken byreserveStreamSlot(ConcurrentHashMap, Hash, int)(an atomic check-and-reserve, so two SYNs racing the gate cannot both observe "room left") and released through_streamReservationsso a teardown cannot double-count. Shrunk toward the live table byConnectionManager.BanExpiryonly after the same excess is seen twice. @since 0.9.71+._streamReservations
Reservation tokens: receive stream ID → the dest whose budget the stream was admitted under. Bound insideassignReceiveStreamId(Connection, Hash)in the same critical section that publishes the ID, and consumed exactly once byreleaseReservation(long)when that ID leaves the manager. The token, not the connection's remote peer, is the source of truth on teardown, so a connection torn down before its peer was ever learned (or one whose dest hash is expensive to recompute) still returns its slot to the right budget. @since 0.9.71+._streamShrinkCandidates
Reconciliation memory: whatConnectionManager.BanExpirylast observed for a dest whose ledger count exceeded the live connection table. The excess is only rebased downward when it repeats on a consecutive sweep, which rules out an in-flight reservation (accepted, ID not yet published)._admissionLock
Admission barrier: readers hold it across a whole reserve-to-bind span (tryReserveStream through assignReceiveStreamId) so a reservation can never straddledisconnectAllHard()'s ledger clear. Without the barrier a SYN admitted mid-clear would bind a token against an erased count and its teardown would then decrement a DIFFERENT stream's fresh count. The write side wraps only the hard-disconnect loop and the clears, so the rare teardown pays for quiescing the (microsecond) admission spans._banExpiry
The sweeper owned by this manager. Held soshutdown()can stop it: a fired-and-cancelled TimedEvent would otherwise re-arm itself from insideConnectionManager.BanExpiry.timeReached(), outliving the manager it reads._recentSyns
Per-destination burst state for the sub-second SYN gate: value is a two-element array {burstStartMs, count}, updated at most once per validated SYN via compare-and-swap (replace). A dest that quiesces naturally falls out of the next window. No per-dest entries accumulate beyond one array, so this is bounded and cheap on the hot path. Reset per-dest by the SYN-rate sweeper. @since 0.9.71+._synBurstStrikes
Each dest's most recent SYN-burst strikes: the start of the burst window that crossed the threshold, the time that strike was recorded, and how many distinct windows have tripped inside the current strike window. Forgiveness ages from the strike time — window start can precede the strike by up to a whole burst window, and aging from it would forgive a later window's trip early. A trip from a DIFFERENT burst window withinSTRIKE_WINDOW_MSof the last strike adds one, and reachingPROP_TEMP_BAN_STRIKESof them autobans; repeat trips inside the window that already struck are ignored, so a single unlucky page-load burst never bans no matter how far over threshold it goes. After the strike window the entry is swept by BanExpiry._synRateMs
private volatile long _synRateMsCached sub-second burst config, refreshed by the BanExpiry sweeper rather than re-read from the property store on every validated SYN (hot path)._synBurst
private volatile int _synBurstSYNs in a burst that trigger a temp ban._tempBanStrikes
private volatile int _tempBanStrikesCached strikes-to-ban count, refreshed by the BanExpiry sweeper. 0 or 1 is raised to 2 at the decision point, so no setting can let a single burst window ban on its own._tempBanMs
private volatile long _tempBanMsCached autoban duration (ms) and refusal threshold, refreshed by the BanExpiry sweeper so the hot path (isTempBanned / refusal latch) avoids per-SYN property-store reads. Snapshot-at-mark-time still holds: a ban uses the duration current when it was placed._tempBanRefusals
private volatile long _tempBanRefusalsRefusals that trigger a temp ban._autobanEnabled
private volatile boolean _autobanEnabledCached autoban switch (PROP_AUTOBAN), refreshed by the BanExpiry sweeper like the other hot-path config. When false, banPeer() is a no-op, the SYN burst gates do not count or strike, and no SYN is dropped for a would-ban trip. Bans placed while it was enabled still expire naturally.PROP_BLACKLIST
Blacklist property for streaming.- See Also:
PROP_TEMP_BAN_MINUTES
Autoban property: a dest is temporarily banned until this many minutes after it first trips a flood threshold. Default 5 minutes. Short enough that a legitimate client's retry window (30s connect timeout) overlaps with the ban expiry, so retries succeed on the next attempt. The previous 24-hour default was far too aggressive: a burst of page-load SYNs from a browser (local or remote) could ban a peer for an entire day. Tunable viai2p.streaming.tempBanMinutes. 0 disables autoban. @since 0.9.71+.- See Also:
DEFAULT_TEMP_BAN_MINUTES
private static final long DEFAULT_TEMP_BAN_MINUTESThe default temp ban lasts 5 minutes.- See Also:
PROP_AUTOBAN
Autoban property: whether autoban enforcement is enabled. 0 disables (no new bans are recorded and the SYN flood gates do not strike or drop); nonzero enables. Default on. The BanExpiry sweeper runs regardless, so per-dest gate state stays bounded and the cached autoban config keeps refreshing while enforcement is off. @since 0.9.71+.- See Also:
DEFAULT_AUTOBAN
private static final int DEFAULT_AUTOBANNegative means no permanent autoban.- See Also:
PROP_TEMP_BAN_REFUSALS
Autoban property: refusals from a single dest within a one-minute window (as counted by _refusalCounter) at which the dest is auto-banned. Tunable viai2p.streaming.tempBanRefusals. @since 0.9.71+.- See Also:
DEFAULT_TEMP_BAN_REFUSALS
private static final long DEFAULT_TEMP_BAN_REFUSALSDefault refused connections that trigger a temp ban.- See Also:
PROP_TEMP_BAN_RATE_MS
Autoban property: a dest sending more than tempBanSynBurst SYNs within a tempBanSynRate-ms rolling window is auto-banned. This is the sub-second rate gate: a legit client (e.g. a BitTorrent announce on a timer) never bursts tens of SYNs within a few hundred ms, but the observed tracker flood does (~25 SYNs in 259ms). Catches the burst before it consumes the stream budget.- See Also:
DEFAULT_TEMP_BAN_RATE_MS
private static final long DEFAULT_TEMP_BAN_RATE_MSThe default SYN rate window is 1 second.- See Also:
PROP_TEMP_BAN_SYN_BURST
Autoban property: sub-second burst threshold (SYNs within rate window). Default 40 SYNs per 1s = 40 req/s instantaneous. A browser page load can fire 15-20 parallel connections, each with SYN retransmits; empty-response retry dual-race adds up to 4 SYNs/s per dest. The threshold must sit well above legitimate bursts so only a serious abuser trips it. First trip is a strike (no ban); a second trip withinSTRIKE_WINDOW_MSautobans.- See Also:
DEFAULT_TEMP_BAN_SYN_BURST
private static final int DEFAULT_TEMP_BAN_SYN_BURSTDefault SYNs in a burst that trigger a temp ban.- See Also:
PROP_TEMP_BAN_STRIKES
Autoban property: how many DISTINCT burst windows insideSTRIKE_WINDOW_MSa dest must trip before the SYN-burst gate autobans it. Default 3.A single burst window is not abuse: a browser page load, a client with a high concurrent-stream ceiling, or a peer resuming after a pause can all legitimately cross the sub-second threshold. Two was low enough that a merely busy peer could lose five minutes of connectivity, so the default demands sustained tripping of the gate. Raise it further on a high-legitimate-traffic router, or set
PROP_AUTOBAN0 to turn off autoban entirely (which also disables the refusal-based ban).Values below 2 are raised to 2: a single window must never ban alone. Tunable via
i2p.streaming.tempBanStrikes.- See Also:
DEFAULT_TEMP_BAN_STRIKES
private static final int DEFAULT_TEMP_BAN_STRIKESTemp bans before a permanent ban.- See Also:
STRIKE_WINDOW_MS
static final long STRIKE_WINDOW_MSStrike window for the multi-strike SYN-burst gate: strikes more than this many ms apart are forgiven, so the required count has to be met inside one rolling window. A dest that trips once and then quiesces for the window is forgiven (single unlucky page-load burst).- See Also:
MAX_PONG_PAYLOAD
private static final int MAX_PONG_PAYLOADLargest pong payload accepted, in bytes.- See Also:
PROP_ENABLE_PONG_DELAY
Config key enabling the PONG response delay.- See Also:
DEFAULT_ENABLE_PONG_DELAY
private static final boolean DEFAULT_ENABLE_PONG_DELAYPong delay is off by default.- See Also:
MAX_PONG_DELAY
private static final int MAX_PONG_DELAYLongest artificial pong delay, in ms.- See Also:
PROP_MAX_PONG_DELAY
Property: maximum pong delay in ms.- See Also:
LIMIT_HTTP_RESPONSE
The http 429 response sent when over the limit. See also: RFC 6585 (additional HTTP status codes) See also: rate limit responses- See Also:
MAX_TIME
private static final int MAX_TIMELargest ban length accepted, effectively forever.- See Also:
Constructor Details
ConnectionManager
public ConnectionManager(I2PAppContext context, I2PSession session, ConnectionOptions defaultOptions, IncomingConnectionFilter connectionFilter) Manage all conns for this session.- Parameters:
context- the I2P app contextsession- the primary session, packets may come in on subsessions alsodefaultOptions- the default connection optionsconnectionFilter- the incoming connection filter
Method Details
shouldLogCooldownWarn
static boolean shouldLogCooldownWarn(long lastWarnTime, long now, long cooldownMs) Whether to log another cooldown-delay WARN for a destination. Rate-limits to one warning per cooldown window per destination so a peer that keeps failing to connect cannot turn every retry into a log line. A value of 0 (never warned, or pruned) always logs.- Parameters:
lastWarnTime- last timestamp the WARN was emitted for this dest, 0 if nevernow- router clock time in millisecondscooldownMs- length of the cooldown window that arms the WARN- Returns:
- true if the WARN should be emitted
awaitCooldown
Wait out the remainder of a destination's connect cooldown. Re-arms after every wakeup until the deadline passes, so a notify - the one shutdown sends, or a spurious one - cannot cut the cooldown short and let a connect through to a peer we just failed to reach. The deadline is fixed at entry, so an early wakeup costs one more short wait, never a longer cooldown. An interrupt does end the wait, since that is a deliberate cancellation rather than a stray signal.- Parameters:
lock- the monitor to wait on, notified on shutdowndelay- milliseconds of cooldown left to sit out, non-positive to skipclock- the clock the deadline is measured against- Returns:
- milliseconds actually waited
getPoolMaxIdleMs
private long getPoolMaxIdleMs()Max idle time before a pooled stream is closed and evicted. Default 20s — balances reuse vs staleness. Tunable viai2p.streaming.poolMaxIdleMs.- Returns:
- the pool max idle ms
getPoolMaxPerDestination
private int getPoolMaxPerDestination()Max pooled streams per destination. Default 8 — limits memory per peer. Tunable viai2p.streaming.poolMaxPerDestination.- Returns:
- the pool max per destination
isPoolEnabled
private boolean isPoolEnabled()Whether stream pooling is enabled. Disabled by default — pooled connections retain stale remote state that causes zero-data-flow after reuse. Enable only with proper connection state reset. Tunable viai2p.streaming.streamPool.enabled(default: false).- Returns:
- whether pool enabled
getDestCooldownMs
private long getDestCooldownMs()Cooldown between connection attempts to the same failed destination. Tunable viai2p.streaming.destinationCooldownMs(default: 5000).- Returns:
- the dest cooldown ms
trimDestFailures
private void trimDestFailures()Remove expired entries from_destFailuresand_cooldownWarned. Called on every connect failure to prevent unbounded growth. Uses the cooldown window as the eviction threshold.registerRtxRatioStats
Register the stream-close retransmission ratio stats, including the five-minute period the router's congestion tuner samples. Separated from the constructor so tests can register against a fresh StatManager without building a whole manager.- Parameters:
sm- stat manager to register with, non-null
banPeer
Ban a dest for the configured duration. Idempotent; an existing longer ban is left in place so repeated abuse can't shrink it. No-op when autoban is disabled (PROP_AUTOBAN0) or the duration is 0.- Parameters:
h- dest hash to banwhy- human-readable trigger, e.g. "exceeded max 50 conns/minute"now- current clock time- Returns:
- true if the dest is banned after this call (newly banned, extended, or already banned); false if bans are disabled
canReserveStream
static boolean canReserveStream(int held, int max) Pure admission decision for the per-dest stream budget: one more stream fits while the dest's live count is strictly below the ceiling. A non-positive ceiling disables the gate entirely (no cap enforced).- Parameters:
held- streams currently reserved by the destmax- the per-dest concurrent stream ceiling- Returns:
- true if a further stream would stay under the ceiling
tooManyStreamsForDest
static boolean tooManyStreamsForDest(int streamCount, int max) Pure decision for the per-destination stream budget: a dest is over budget once its live concurrent-stream count reaches the per-dest ceiling. A non-positive ceiling disables the gate entirely.- Parameters:
streamCount- live streams currently held by the destmax- the per-dest concurrent stream ceiling- Returns:
- true if the dest is at or over its own budget
reserveStreamSlot
Atomically take one stream slot from a dest's budget: the ceiling check and the increment happen inside a single per-keycompute(), so two SYNs racing the gate can no longer both observe "room left" and admit past the ceiling (the check-then-add this replaced).Refusal leaves the counter untouched, so a rejected SYN never costs the dest anything. An unknown dest (null hash) is admitted without accounting, matching the old gate's "no hash, no budget" behavior.
- Parameters:
ledger- the per-dest stream ledger, non-nullh- remote dest hash, may be nullmax- the per-dest concurrent stream ceiling; ≤ 0 disables the gate- Returns:
- true if the slot was taken (the caller now owns a slot and must either bind it to a reservation token or release it)
releaseStreamSlot
Return one stream slot to a dest's budget. The count is clamped at zero and the entry is dropped once drained, so a mismatched teardown can never drive a budget negative (which would hand out free slots) and an idle dest leaves no entry behind.- Parameters:
ledger- the per-dest stream ledger, non-nullh- remote dest hash, null-safe
streamSlotCount
Read the stream count a dest is actually holding in the ledger, or 0 if it holds none.- Parameters:
ledger- the per-dest stream ledger, non-nullh- remote dest hash, non-null- Returns:
- the reserved stream count for the dest, never negative
consumeReservation
static Hash consumeReservation(ConcurrentHashMap<Long, Hash> tokens, ConcurrentHashMap<Hash, AtomicInteger> ledger, long receiveStreamId) Consume the reservation token bound to a receive stream ID, releasing the dest budget slot it owns. A token is removed first and released after, so concurrent teardowns of the same ID cannot both return the slot; a missing token is a no-op rather than a guess about which dest to debit.- Parameters:
tokens- receive stream ID → dest hash, non-nullledger- the per-dest stream ledger, non-nullreceiveStreamId- the connection's receive stream ID- Returns:
- the dest hash whose slot was released, or null if no token was bound
releaseWaiting
Decrement a connect-attempt waiter count without ever going negative.Waiters are released from a
finallyaround the wait loop, so an abandoned or failed attempt always drains its own increment exactly once and a double release can only stop at zero.- Parameters:
waiting- the waiter counter, non-null- Returns:
- the value after the release, or the unchanged value if it was already zero
reconcileStreamCount
Pure reconciliation step for one dest: how much of a ledger count that exceeds the live connection table should survive this sweep.The excess is only dropped when the SAME excess was recorded by the previous sweep (the candidate), which means it persisted across a whole sweep interval and therefore cannot be an in-flight reservation admitted moments before the first sweep. Counts at or below the table are never raised: the ledger only ever shrinks toward observed reality, so a sweep can free leaked slots but can never hand out new ones.
Even a repeated candidate cannot shrink below the bound-token floor. A release followed by a re-reserve can return the count to the same value while the observed snapshot still shows the old, smaller table (ABA): shrinking to observed there would erase brand-new reservations. Tokens are bound one-per-admission, so
max(observed, boundTokens)is never below the live floor and a genuine leak (no token behind the excess) still shrinks to observed.- Parameters:
held- the count currently in the ledgerobserved- the count of live connections for the destcandidate- the excess last sweep recorded for this dest, or nullboundTokens- reservation tokens bound for this dest right now- Returns:
- the count to keep in the ledger this sweep
reconcileStreamSlots
static void reconcileStreamSlots(ConcurrentHashMap<Hash, AtomicInteger> ledger, Map<Hash, Integer> observed, ConcurrentHashMap<Hash, Integer> candidates, Map<Hash, Integer> boundTokens) Reconcile the per-dest stream ledger against the live connection table. SeereconcileStreamCount(int, int, Integer, int)for the policy; this walks the ledger, records or clears per-dest candidates, and prunes candidates for dests that have left the ledger.A rebase is applied with a compare-and-set on the count we read, so a reserve or release landing mid-sweep makes us skip that dest rather than overwrite the newer value.
- Parameters:
ledger- the per-dest stream ledger, non-nullobserved- live connection count per dest, non-nullcandidates- sweep-over-sweep observation memory, non-nullboundTokens- reservation tokens bound per dest, non-null
tryReserveStream
Take a stream slot from this dest's budget. SeereserveStreamSlot(ConcurrentHashMap, Hash, int).- Parameters:
h- remote dest hash, non-null on every real pathmax- the per-dest concurrent stream ceiling- Returns:
- true if the slot was taken
releaseStream
Return a stream slot taken outside the token protocol (a SYN rejected after admission, or a connection built before a token was bound).- Parameters:
h- remote dest hash, null-safe
releaseReservation
Consume the reservation bound to a receive stream ID, releasing its slot.- Parameters:
receiveStreamId- the connection's receive stream ID- Returns:
- the dest hash released, or null if no token was bound
releaseConnectionReservation
Release whatever this connection still holds against a dest budget: first its reservation token, and only if no token was bound, its remote peer (a connection built outside the reservation protocol, so the slot must still be drained or the budget leaks forever).The release is claimed on the connection first: any path may reach here for a connection whose slot was already returned (a hard-disconnect sweep racing an async teardown, or a teardown after the sweeper released it), and the peer fallback would then decrement a DIFFERENT stream's count. The claim is re-armed by
assignReceiveStreamId(Connection, Hash)when a pooled connection starts a new generation.- Parameters:
con- the connection leaving the manager, non-null- Returns:
- the dest hash released, or null if nothing was held
tempBanReason
banIsLonger
Whether a new ban end time should replace an existing one: only when it is strictly longer. Prevents a late-arriving shorter ban from shrinking an active ban under repeated abuse.- Parameters:
existing- current bannedUntil (aged), null if nonecandidate- proposed new bannedUntil- Returns:
- true if candidate exceeds existing
isTempBanned
Whether the dest is currently temp-banned.- Parameters:
h- dest hash to checknow- current clock time- Returns:
- true if temp-banned and not yet expired
banActive
Whether a bannedUntil time is still in the future.- Parameters:
bannedUntil- epoch ms; null treated as not bannednow- current clock time- Returns:
- true if bannedUntil is non-null and greater than now
refusalThresholdMet
static boolean refusalThresholdMet(long refusals, long threshold) Whether a dest has tripped the autoban refusal threshold.- Parameters:
refusals- count of refusals seen for the dest in the current windowthreshold- configured refusals-to-ban threshold- Returns:
- true when refusals exceed threshold
synBurstTripped
Pure decision for the sub-second SYN-burst gate: whether a dest has sent more thanburstSYNs within the lastwindowMsms.- Parameters:
burstStartMs- epoch ms of the first SYN in the current burst window (the ''oldest'' still counted), null if nonecount- number of SYNs attributed to the open windownow- current clock timewindowMs- rolling window lengthburst- SYNs-per-window that constitutes an abusive burst- Returns:
- true if the dest tripped the burst gate
checkSynBurst
Whether a SYN from a dest tripped the sub-second burst gate. Side effects: records the SYN in the per-dest rolling window. Allocation-free on the hot path: an existing window is bumped in place rather than replaced, and the window/burst limits are read from cached volatile fields refreshed by the BanExpiry sweeper. Call only for a validated SYN source.The autoban policy is a parameter, snapshotted once per SYN by the caller, so one SYN can never be counted under one policy and then recorded (or not) under a different one that changed mid-evaluation.
- Parameters:
h- dest hash to record againstnow- current clock timeautoban- the autoban policy snapshotted when this SYN was evaluated- Returns:
- the window start of the burst window that tripped (used to key the strike to a distinct burst), or -1 if this SYN did not trip the gate (below threshold, window just re-armed, gate disabled, or autoban enforcement off)
strikeForgiven
private static boolean strikeForgiven(ConnectionManager.SynStrike last, long now, long strikeWindowMs) Whetherlasthas aged out of the forgiveness window (or was stamped in the future by a clock adjustment, which is treated the same way so a skewed clock can never ban).Single source of truth: the decision function uses it to choose BAN vs RECORD, and the recorder uses it to decide whether a RECORD extends the running strike count or restarts it at 1. Sharing it keeps those two from drifting, which would otherwise let a dest accumulate strikes across unrelated hours of traffic.
- Parameters:
last- recorded strike to age-check, non-nullnow- wall-clock ms to measure the strike's age againststrikeWindowMs- strike forgiveness window (passSTRIKE_WINDOW_MS)- Returns:
- true if the strike is old enough (or future-stamped) to be forgiven, false while it still counts toward a ban
synBurstStrikeAction
static ConnectionManager.SynBurstAction synBurstStrikeAction(ConnectionManager.SynStrike last, long windowStart, long now, long strikeWindowMs, int requiredStrikes) Pure decision for the multi-strike SYN-burst gate. Strikes mean DISTINCT burst windows: repeat trips inside the window that recorded the most recent strike are IGNOREd, so a single unlucky page-load burst can never cost a 5-minute outage however far over threshold it goes. Only when a dest has trippedrequiredStrikesdistinct windows insideSTRIKE_WINDOW_MSof each other is this trip a BAN, i.e. the demonstrable pattern of sustained abuse. After the window the count is forgiven and a new one RECORDs instead.Forgiveness ages from the most recent strike time, so the window is rolling and the clock starts when the strike was recorded, not when the (millisecond-scale) burst window began.
- Parameters:
last- recorded strike for this dest, or null if this dest has nonewindowStart- window start of the burst window that just trippednow- current clock timestrikeWindowMs- strike forgiveness window (passSTRIKE_WINDOW_MS)requiredStrikes- distinct burst windows needed to ban; values below 2 are raised to 2 so a single window can never ban on its own- Returns:
- the action to take for this trip
applySynBurstStrike
static ConnectionManager.SynBurstAction applySynBurstStrike(ConcurrentHashMap<Hash, ConnectionManager.SynStrike> strikes, Hash h, long windowStart, long now, long strikeWindowMs, int requiredStrikes) Atomically evaluate and apply a burst-gate trip againsth's recorded strike. The read-modify-write runs underConcurrentHashMap.compute(K, BiFunction)so concurrent trips from one dest can neither both miss a just-recorded first strike (double-counting a single burst as two strikes) nor lose it to a racing put (letting a sustained flood escape the ban).- Parameters:
strikes- per-dest strike maph- remote dest hash, non-nullwindowStart- window start of the burst window that just trippednow- current clock timestrikeWindowMs- strike forgiveness window (passSTRIKE_WINDOW_MS)requiredStrikes- distinct burst windows needed to autoban- Returns:
- the action taken
noteSynBurstStrike
Record a SYN-burst strike forhand decide whether this trip autobans (seesynBurstStrikeAction(ConnectionManager.SynStrike, long, long, long, int)). First trip in a window (or a trip after the strike window): store the strike, return false. Repeat trips in an already-struck window: no-op, return false. Second distinct window inside the strike window: leave the original strike in place (the ban supersedes further strikes), return true.Autoban is re-checked live here (the callers already gate
checkSynBurst(Hash, long, boolean)on a per-SYN snapshot): if enforcement is switched off between the trip and this record, no strike may be stored, or the map would keep evidence recorded under a policy the operator has since disabled. Callers' snapshot decides BAN for the trip in flight; this check only gates the record.- Parameters:
h- remote dest hash, non-nullwindowStart- window start of the burst window that just trippednow- current clock time- Returns:
- true if this trip should autoban
checkInboundSynFlood
Package-visible flood gate for the retransmit-SYN path inConnectionHandler.receiveNewSyn(Packet). A retransmitted SYN carries the stream IDs of a connection that already exists in the manager, so it never reachesreceiveConnection(Packet)(and thus never passes thecheckSynBurst(Hash, long, boolean)gate at the top of that method). An attacker exploits that by planting a handful of half-open connections and then blasting retransmitted SYNs against them. Without this gate every hit makesConnectionHandler.resendSynAckmint and enqueue a fresh SYN-ACK, consuming CPU and egress, and the connection never establishes.This routes the retransmit through the same per-destination sub-second burst window as fresh SYNs, so a dest that crosses the burst threshold in
PROP_TEMP_BAN_STRIKESDISTINCT windows within the strike window — the demonstrable pattern of sustained abuse — is autobanned and dropped. A single burst only strikes, and with autoban disabled (PROP_AUTOBAN0) nothing is recorded or dropped here. Once banned, subsequent calls returntrue(drop) immediately.- Parameters:
h- remote dest hash, non-nullnow- current clock time- Returns:
- true if this SYN should be dropped (dest already temp-banned, or this SYN is a second distinct burst strike and just banned it)
getTempBanMinutes
private long getTempBanMinutes()Returns the temp ban length from the config.- Returns:
- the ban length in minutes
getEffectiveMaxStreams
private int getEffectiveMaxStreams()The concurrent-stream cap to enforce right now. Normally this is the value captured into_defaultOptionsat init (the user-configuredi2p.streaming.maxConcurrentStreams); if the router's Tuner has armed an override viaI2PSocketManagerFull.setMaxStreamsOverride(int), the effective cap is the lower of the two so a user ceiling is never exceeded a Tuner. A volatile read; no config lookup per call.- Returns:
- the cap; ≤ 0 means no cap is enforced
getLogMaxStreams
private int getLogMaxStreams()The cap to report in refusal log messages: the Tuner's current override when it has armed one, otherwise the user-configured ceiling.This deliberately differs from
getEffectiveMaxStreams(), which min's the override against the user ceiling and is the real enforcement limit at the per-destination stream budget gate (reserveStreamSlot(ConcurrentHashMap, Hash, int)). When the Tuner relaxes a healthy host back up toward its own max (e.g. 512) that is higher than a user's configured ceiling (e.g. 256), the enforcement limit stays 256 while the operator reading the log wants to see the Tuner's current target (512).- Returns:
- the tuner override when armed, else the configured cap
getMaxPingTimeout
private long getMaxPingTimeout()Maximum ping timeout. Tunable viai2p.streaming.maxPingTimeout(default: 300000).- Returns:
- the max ping timeout
getDropOverLimit
private int getDropOverLimit()Once over throttle limits, respond this many times before just dropping. Tunable viai2p.streaming.dropOverLimit(default: 3).- Returns:
- the drop over limit
getConnectionByInboundId
Look up a connection by its inbound stream ID.- Parameters:
id- the inbound stream ID- Returns:
- the connection, or null if not found
getConnectionByOutboundId
Look up a connection by its outbound stream ID. Not guaranteed to be unique, but in case we receive more than one packet on an inbound connection that we haven't ack'ed yet...- Parameters:
id- the outbound stream ID- Returns:
- the connection, or null if not found
wasRecentlyClosed
public boolean wasRecentlyClosed(long inboundID) Was this conn recently closed?- Parameters:
inboundID- the inbound stream ID to look up in the recently-closed LRU- Returns:
- true if the connection was closed recently and is still being tracked
setSoTimeout
public void setSoTimeout(long x) Socket accept() timeout.- Parameters:
x- accept timeout in milliseconds, passed through to the listener's accept()
getSoTimeout
public long getSoTimeout()Socket accept() timeout.- Returns:
- accept timeout in ms.
setAllowIncomingConnections
public void setAllowIncomingConnections(boolean allow) Enable or disable acceptance of incoming connections. When first enabled, initializes throttlers if needed.- Parameters:
allow- true to accept incoming connections
updateOptions
public void updateOptions()Update the throttler options.getAllowIncomingConnections
public boolean getAllowIncomingConnections()Whether incoming connections are accepted.- Returns:
- if we should accept connections
receiveConnection
Create a new connection based on the SYN packet we received.Ownership of the packet's pooled payload is decided here:
acceptSyn(Packet)never touches it, so every exit from that stage - all of the rejections, and any throw - returns the buffer. ThereceivePacket()call below takes ownership, and nothing after it may release the payload, its own verification-failure path included.- Parameters:
synPacket- SYN packet to process- Returns:
- created Connection with the packet's data already delivered to it, or null if the syn's streamId was already taken, or if the connection was rejected
acceptSyn
Everything about a new inbound connection up to the point where its payload is handed over: the SYN burst gates, the admission reservation, the options and MTU negotiation, and the connection's construction. Leaves the packet's payload alone - the caller owns it and returns it on every exit from here.- Parameters:
synPacket- the SYN packet to process- Returns:
- the connection with its receive slot bound, or null if the SYN was dropped or refused
sendRejectResponse
Send the configured response for a rejected SYN (RESET, HTTP-style Retry-After payload, or custom payload), subject to the peer's throttler status. Only called when the SYN was already judged rejectable; extracted fromreceiveConnection(Packet)to bound its complexity.- Parameters:
synPacket- the incoming SYN being rejectedfrom- the peer destinationfromHash-from.calculateHash()(already computed by the caller)retryAfter- seconds for the Retry-After header;MAX_TIMEmeans never retry (silent drop)
limitResponse
The payload to send in a limit-response SYN/CLOSE, or null for a RESET.Pure decision helper (extracted from
receiveConnection): the "reset" limit action, an empty action, and port 443 (the synth HTTPS over I2P port, which must not be tricked into a text payload) all produce a plain RESET. "http" produces an HTTP Retry-After body; anything else is echoed verbatim after normalizing\r/\code{n} escape sequences. HTTPS (443) always stacks the port check too, per RFC-compliant servers that disregard limit responses on that port.- Parameters:
limitAction- the configuredi2p.streaming.limitActionvalue, may be nullretryAfter- seconds for the Retry-After header, or 0 to omit itsslPort- true if the SYN arrived on local port 443- Returns:
- the response payload, or null for a RESET
synNacksMatch
Whether a SYN's 8 NACK words match the local destination hash. A NACK that does not match the local hash means the sender is wrong about who we are; extracted so the word-split comparison is unit-testable.- Parameters:
nacks- the packet's NACK values, or nulllocalHash- the local destination hash to compare against- Returns:
- true if the NACKs are absent or all 8 words match
receivePing
Process a ping by checking for throttling, etc., then sending a pong.- Parameters:
con- null if unknownping- Ping packet to process, must have From and Sig fields, with signature already verified, only ifanswerPings()returned true- Returns:
- true if we sent a pong
assignReceiveStreamId
Pick a new random stream ID for the con and assign it, taking care to avoid duplicates, and put it in the connection table.Binds this stream's budget reservation here, inside the same critical section that publishes the ID, so the token becomes the source of truth for the slot from the moment the ID is visible to teardown paths. An earlier id the con may have held is deliberately NOT re-released: a pooled connection hands back its previous slot when it leaves the manager, and releasing here as well would double-count.
- Parameters:
con- the connection to assign an ID todestHash- the remote dest whose budget admitted this stream; the reservation token for the new ID points here
assignPingId
Pick a new random stream ID for a ping and assign it, taking care to avoid duplicates, and return it.- Parameters:
req- the ping request to register under the new ID- Returns:
- the fresh receive stream ID the ping is pending under
assignRejectId
private long assignRejectId()Pick a new random stream ID that we are rejecting, taking care to avoid duplicates, and return it.- Returns:
- the fresh stream ID recorded in the recently-closed set
getDefaultStreamDelayMax
private long getDefaultStreamDelayMax()Default stream delay maximum when no connect timeout is set. Tunable viai2p.streaming.defaultStreamDelayMax(default: 10000).- Returns:
- the default stream delay max
connect
Build a new connection to the given peer. This blocks if there is no connection delay, otherwise it returns immediately.- Parameters:
peer- Destination to contact, non-nullopts- connection options, never nullsession- generally the session from the constructor, but could be a subsession- Returns:
- new connection, or null if we have exceeded our limit
finalizeConnection
private Connection finalizeConnection(Connection con, Destination peer, ConnectionOptions opts, long connectStart, boolean fromPool) Common post-creation setup for both pooled and new connections. Handles cooldown, waitForConnect, stats recording.- Parameters:
con- the new or pooled connection being set up, non-nullpeer- the remote destination, used for the per-dest cooldown and failure bookkeepingopts- options supplying the connect delay and timeout passed to waitForConnectconnectStart- wall-clock ms when the connect attempt began, for the elapsed-time statsfromPool- if true, skip cooldown delay (connection already established)- Returns:
- the same con, after waitForConnect; a failed connect is reported through getConnectionError(), not a null return
shouldRejectConnection
Check if a connection should be rejected based on blacklists, access lists, throttling, and the connection filter.- Parameters:
syn- the incoming SYN packet- Returns:
- a Reason with seconds for Retry-After header; MAX_TIME for drop, 0 if unknown; or null if not rejected
getMessageHandler
Message handler for this manager.- Returns:
- the message handler
getPacketHandler
getSession
This is the primary session only.- Returns:
- the primary session passed to the constructor, not a subsession
getConnectionHandler
Connection handler for this manager.- Returns:
- the handler running the connection state machines
getPacketQueue
Outbound packet queue.- Returns:
- the queue of packets waiting to be sent
answerPings
public boolean answerPings()Do we respond to pings that aren't on an existing connection?- Returns:
- true if we answer pings
disconnectAllHard
public void disconnectAllHard()Drop every connection, sending a RESET instead of a CLOSE handshake. This will not close the ServerSocket. This will not kill the timer threads.CAN continue to use the manager.
shutdown
public void shutdown()Kill all connections and the timers. Don't bother sending close packets. As of 0.9.17, this will close the ServerSocket, killing one thread in accept().CANNOT continue to use the manager or restart.
acquireFromPool
Try to acquire a pooled connection for the given destination. Returns a valid connection or null if none available.- Parameters:
peer- the destination whose pool bucket is polled, non-null- Returns:
- an established, healthy pooled connection, or null if the bucket is empty or every entry was stale or unhealthy
returnToPool
Return a connection to the pool if healthy. Called fromremoveConnection(Connection).- Parameters:
con- the connection being retired; skipped unless it is established, healthy and has clean streams, null is ignored
cleanupPool
void cleanupPool()Periodic pool cleanup — evicts stale connections, then drops the buckets they leave behind. Without the second step the_streamPoolskey set grows with every destination the manager has ever pooled a stream to: an emptied deque is still a map entry, and a destination that is never requested again is never drained byacquireFromPool(Destination).pruneEmptyPoolBuckets
Remove destination buckets whose connection deque has gone empty, so a destination that is no longer contacted does not keep a (now-empty) pool entry alive forever.returnToPool(Connection)recreates a bucket on demand viacomputeIfAbsent, so dropping an empty one is safe. Weakly-consistent iteration, so it never blocks a concurrent pool acquire/return. Generic over the value type so the prune is testable without constructing aPooledConnection.- Parameters:
pools- the per-destination pool map to prune in place- Returns:
- the number of empty buckets removed
registerOutboundId
Register outbound id.- Parameters:
con- the connection whose send stream ID is bound to it, ignored when <= 0
removeConnection
Remove a connection from the manager.- Parameters:
con- Connection to drop.
publishLifetimeStats
Publish a closing connection's lifetime counters. Called once per stream fromConnection.disconnectComplete(), which is the single funnel every teardown goes through — deliberately not gated on whether the dispatch table still held the connection. A hard-killed or already-unlinked stream would otherwise silently lose every lifetime stat, and with them the retransmission ratios the Tuner reads for its loss decisions.RTT and send-window are the exception: they are only published once a real sample exists, since a connection killed during connect holds class defaults rather than observations.
- Parameters:
con- Connection that is done; never null
listConnections
Connections currently managed.- Returns:
- set of Connection objects
ping
Ping the destination and wait for a pong.- Parameters:
peer- the resolved destination to pingtimeoutMs- greater than zero- Returns:
- true if pong received
ping
Ping the destination and wait for a pong.- Parameters:
peer- the resolved destination to pingfromPort- the source porttoPort- the destination porttimeoutMs- greater than zero- Returns:
- true if pong received
ping
Ping the destination, optionally waiting for a pong.- Parameters:
peer- the resolved destination to pingfromPort- the source porttoPort- the destination porttimeoutMs- greater than zeroblocking- true to block until pong- Returns:
- true if blocking and pong received
ping
public boolean ping(Destination peer, int fromPort, int toPort, long timeoutMs, boolean blocking, ConnectionManager.PingNotifier notifier) Ping the destination, optionally waiting for a pong.- Parameters:
peer- the resolved destination to pingfromPort- the source porttoPort- the destination porttimeoutMs- greater than zeroblocking- true to block until pongnotifier- may be null- Returns:
- true if blocking and pong received
ping
Ping the destination with a payload and wait for the pong.- Parameters:
peer- the resolved destination to pingfromPort- the source porttoPort- the destination porttimeoutMs- greater than zeropayload- non-null, include in packet, up to 32 bytes may be returned in pong not copied, do not modify- Returns:
- the payload received in the pong, zero-length if none, null on failure or timeout
ping
Ping the destination through a specific subsession, waiting for the pong.- Parameters:
peer- the resolved destination to pingsession- the subsession to use (must not be null)fromPort- the source porttoPort- the destination porttimeoutMs- greater than zero- Returns:
- true if pong received
ping
public boolean ping(Destination peer, I2PSession session, int fromPort, int toPort, long timeoutMs, boolean blocking) Ping the destination through a specific subsession, optionally blocking.- Parameters:
peer- the resolved destination to pingsession- the subsession to use (must not be null)fromPort- the source porttoPort- the destination porttimeoutMs- greater than zeroblocking- true to block until pong- Returns:
- true if blocking and pong received
ping
public byte[] ping(Destination peer, I2PSession session, int fromPort, int toPort, long timeoutMs, byte[] payload) Ping the destination with a payload through a specific subsession.- Parameters:
peer- the resolved destination to pingsession- the subsession to use (must not be null)fromPort- the source porttoPort- the destination porttimeoutMs- greater than zeropayload- non-null, include in packet, up to 32 bytes may be returned in pong- Returns:
- the payload received in the pong, zero-length if none, null on failure or timeout
observedStreamCounts
Live connection count per dest, preferring the reservation token (the dest that was actually charged at admission) and falling back to the connection's remote peer for anything bound outside the protocol. Built per sweep. A dest with no live connection is simply absent.- Returns:
- observed stream count per dest, never null
tokenStreamCounts
Bound reservation tokens per dest: the ABA floor forreconcileStreamCount(int, int, Integer, int). A token exists for exactly the admissions that own a slot (it is bound after the connection is published and released together with the count), so a stale shrink candidate can never claim slots that are genuinely held.- Returns:
- tokens per dest; empty when nothing is bound, never null
receivePong
Process a received pong response.- Parameters:
pingId- the ping stream ID to matchpayload- the pong payload, may be null
toString