quic: debug logging of packets
Log every packet sent and received to stdout when
GODEBUG=quiclogpackets=1.
For golang/go#58547
Change-Id: I904336017ea646ad6459557b44702bfe4c732ba9
Reviewed-on: https://go-review.googlesource.com/c/net/+/513439
Reviewed-by: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Damien Neil <dneil@google.com>
diff --git a/internal/quic/conn.go b/internal/quic/conn.go
index 8130c54..ff03bd7 100644
--- a/internal/quic/conn.go
+++ b/internal/quic/conn.go
@@ -116,6 +116,10 @@
return c, nil
}
+func (c *Conn) String() string {
+ return fmt.Sprintf("quic.Conn(%v,->%v)", c.side, c.peerAddr)
+}
+
// confirmHandshake is called when the handshake is confirmed.
// https://www.rfc-editor.org/rfc/rfc9001#section-4.1.2
func (c *Conn) confirmHandshake(now time.Time) {
diff --git a/internal/quic/conn_recv.go b/internal/quic/conn_recv.go
index 7eb03e7..3baa79a 100644
--- a/internal/quic/conn_recv.go
+++ b/internal/quic/conn_recv.go
@@ -60,6 +60,9 @@
return n
}
+ if logPackets {
+ logInboundLongPacket(c, p)
+ }
c.connIDState.handlePacket(c.side, p.ptype, p.srcConnID)
ackEliciting := c.handleFrames(now, ptype, space, p.payload)
c.acks[space].receive(now, space, p.num, ackEliciting)
@@ -96,6 +99,9 @@
return len(buf)
}
+ if logPackets {
+ logInboundShortPacket(c, p)
+ }
ackEliciting := c.handleFrames(now, packetType1RTT, appDataSpace, p.payload)
c.acks[appDataSpace].receive(now, appDataSpace, p.num, ackEliciting)
return len(buf)
diff --git a/internal/quic/conn_send.go b/internal/quic/conn_send.go
index 71d24e6..62c9b62 100644
--- a/internal/quic/conn_send.go
+++ b/internal/quic/conn_send.go
@@ -59,6 +59,9 @@
}
c.w.startProtectedLongHeaderPacket(pnumMaxAcked, p)
c.appendFrames(now, initialSpace, pnum, limit)
+ if logPackets {
+ logSentPacket(c, packetTypeInitial, pnum, p.srcConnID, p.dstConnID, c.w.payload())
+ }
sentInitial = c.w.finishProtectedLongHeaderPacket(pnumMaxAcked, k, p)
if sentInitial != nil {
// Client initial packets need to be sent in a datagram padded to
@@ -83,6 +86,9 @@
}
c.w.startProtectedLongHeaderPacket(pnumMaxAcked, p)
c.appendFrames(now, handshakeSpace, pnum, limit)
+ if logPackets {
+ logSentPacket(c, packetTypeHandshake, pnum, p.srcConnID, p.dstConnID, c.w.payload())
+ }
if sent := c.w.finishProtectedLongHeaderPacket(pnumMaxAcked, k, p); sent != nil {
c.loss.packetSent(now, handshakeSpace, sent)
if c.side == clientSide {
@@ -108,6 +114,9 @@
c.w.appendPaddingTo(minimumClientInitialDatagramSize)
pad = false
}
+ if logPackets {
+ logSentPacket(c, packetType1RTT, pnum, nil, dstConnID, c.w.payload())
+ }
if sent := c.w.finish1RTTPacket(pnum, pnumMaxAcked, dstConnID, k); sent != nil {
c.loss.packetSent(now, appDataSpace, sent)
}
diff --git a/internal/quic/log.go b/internal/quic/log.go
new file mode 100644
index 0000000..d724834
--- /dev/null
+++ b/internal/quic/log.go
@@ -0,0 +1,69 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+package quic
+
+import (
+ "fmt"
+ "os"
+ "strings"
+)
+
+var logPackets bool
+
+// Parse GODEBUG settings.
+//
+// GODEBUG=quiclogpackets=1 -- log every packet sent and received.
+func init() {
+ s := os.Getenv("GODEBUG")
+ for len(s) > 0 {
+ var opt string
+ opt, s, _ = strings.Cut(s, ",")
+ switch opt {
+ case "quiclogpackets=1":
+ logPackets = true
+ }
+ }
+}
+
+func logInboundLongPacket(c *Conn, p longPacket) {
+ if !logPackets {
+ return
+ }
+ prefix := c.String()
+ fmt.Printf("%v recv %v %v\n", prefix, p.ptype, p.num)
+ logFrames(prefix+" <- ", p.payload)
+}
+
+func logInboundShortPacket(c *Conn, p shortPacket) {
+ if !logPackets {
+ return
+ }
+ prefix := c.String()
+ fmt.Printf("%v recv 1-RTT %v\n", prefix, p.num)
+ logFrames(prefix+" <- ", p.payload)
+}
+
+func logSentPacket(c *Conn, ptype packetType, pnum packetNumber, src, dst, payload []byte) {
+ if !logPackets || len(payload) == 0 {
+ return
+ }
+ prefix := c.String()
+ fmt.Printf("%v send %v %v\n", prefix, ptype, pnum)
+ logFrames(prefix+" -> ", payload)
+}
+
+func logFrames(prefix string, payload []byte) {
+ for len(payload) > 0 {
+ f, n := parseDebugFrame(payload)
+ if n < 0 {
+ fmt.Printf("%vBAD DATA\n", prefix)
+ break
+ }
+ payload = payload[n:]
+ fmt.Printf("%v%v\n", prefix, f)
+ }
+}