Follow BOLT1 handling for "no reply" pings: ignore, don't warn. (#3278)
What changed, and why it matters
This change updates how Eclair handles oversized ping messages from other Lightning nodes. Previously, Eclair would log a warning and send a 'Warning' message back. Now it silently ignores them. This aligns with the Lightning protocol spec (BOLT 1), which says large ping requests are a legitimate way to ask for no reply, often used for cover traffic. The old behavior was not a security vulnerability, but it was slightly noisy and could be used to generate warning logs or messages.
No urgent action needed. This is a protocol-compliance and log-noise reduction change. Operators may appreciate fewer spurious warnings. Review whether any monitoring depends on the removed Warning message.
Security signals we found
Behavior change from warning+reply to silent ignore for non-standard ping sizes
Removes a Warning message that could be triggered by a peer
Aligns with BOLT 1 spec interpretation for cover traffic
No memory corruption, crash, or authorization bypass evident
Evidence from the diff
In PeerConnection.scala, the handler for Ping messages no longer logs a warning or sends a Warning message when pongLength exceeds 65532. Instead, it simply does not reply. The test is updated to expect no further message after the ReadAck. This follows BOLT 1’s allowance for ‘unacceptable’ pong lengths to mean ‘no reply desired.’
Changed components
eclair-core/src/main/scala/fr/acinq/eclair/io/PeerConnection.scalaeclair-core/src/test/scala/fr/acinq/eclair/io/PeerConnectionSpec.scalaInspect captured patch +4 / −6
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/io/PeerConnection.scala b/eclair-core/src/main/scala/fr/acinq/eclair/io/PeerConnection.scala
index 2ee93d0..4ecd648 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/io/PeerConnection.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/io/PeerConnection.scala
@@ -243,11 +243,9 @@ class PeerConnection(keyPair: KeyPair, conf: PeerConnection.Conf, switchboard: A
case Event(ping@Ping(pongLength, _, _), d: ConnectedData) =>
d.transport ! TransportHandler.ReadAck(ping)
if (pongLength <= 65532) {
- // see BOLT 1: we reply only if requested pong length is acceptable
+ // See BOLT 1: we reply only if requested pong length is acceptable.
+ // Senders may use unacceptable pong length when they don't want a response (to generate cover traffic).
d.transport ! Pong(ByteVector.fill(pongLength)(0.toByte))
- } else {
- log.warning(s"ignoring invalid ping with pongLength=${ping.pongLength}")
- d.transport ! Warning(s"invalid pong length (${ping.pongLength})")
}
stay()
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerConnectionSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerConnectionSpec.scala
index 26aaccb..c6d6451 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerConnectionSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerConnectionSpec.scala
@@ -250,11 +250,11 @@ class PeerConnectionSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wi
test("ignore malicious ping") { f =>
import f._
connect(nodeParams, remoteNodeId, switchboard, router, connection, transport, peerConnection, peer)
- // huge requested pong length
+ // Huge requested pong length.
val ping = Ping(Int.MaxValue, randomBytes(127))
transport.send(peerConnection, ping)
transport.expectMsg(TransportHandler.ReadAck(ping))
- assert(transport.expectMsgType[Warning].channelId == Peer.CHANNELID_ZERO)
+ // We simply ignore the message without sending pong back.
transport.expectNoMessage()
}
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.