info packetInfo // only valid if the contained IP address is valid
}
+type receivedPacketWithDatagramID struct {
+ receivedPacket
+ datagramID qlog.DatagramID
+}
+
func (p *receivedPacket) Size() protocol.ByteCount { return protocol.ByteCount(len(p.data)) }
func (p *receivedPacket) Clone() *receivedPacket {
ctxCancel context.CancelCauseFunc
handshakeCompleteChan chan struct{}
- undecryptablePackets []receivedPacket // undecryptable packets, waiting for a change in encryption level
- undecryptablePacketsToProcess []receivedPacket
+ undecryptablePackets []receivedPacketWithDatagramID // undecryptable packets, waiting for a change in encryption level
+ undecryptablePacketsToProcess []receivedPacketWithDatagramID
earlyConnReadyChan chan struct{}
sentFirstPacket bool
queue := c.undecryptablePacketsToProcess
c.undecryptablePacketsToProcess = nil
for _, p := range queue {
- processed, err := c.handleOnePacket(p)
+ processed, err := c.handleOnePacket(p.receivedPacket, p.datagramID)
if err != nil {
c.setCloseError(&closeError{err: err})
break runLoop
hasMorePackets = !c.receivedPackets.Empty()
c.receivedPacketMx.Unlock()
- processed, err := c.handleOnePacket(p)
+ var datagramID qlog.DatagramID
+ if c.qlogger != nil && wire.IsLongHeaderPacket(p.data[0]) {
+ datagramID = qlog.CalculateDatagramID(p.data)
+ }
+ processed, err := c.handleOnePacket(p, datagramID)
if err != nil {
return false, err
}
return wasProcessed, nil
}
-func (c *Conn) handleOnePacket(rp receivedPacket) (wasProcessed bool, _ error) {
+func (c *Conn) handleOnePacket(rp receivedPacket, datagramID qlog.DatagramID) (wasProcessed bool, _ error) {
c.sentPacketHandler.ReceivedBytes(rp.Size(), rp.rcvTime)
if wire.IsVersionNegotiationPacket(rp.data) {
if err != nil {
if c.qlogger != nil {
c.qlogger.RecordEvent(qlog.PacketDropped{
- Raw: qlog.RawInfo{Length: len(data)},
- Trigger: qlog.PacketDropHeaderParseError,
+ Raw: qlog.RawInfo{Length: len(data)},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropHeaderParseError,
})
}
c.logger.Debugf("error parsing packet, couldn't parse connection ID: %s", err)
if destConnID != lastConnID {
if c.qlogger != nil {
c.qlogger.RecordEvent(qlog.PacketDropped{
- Header: qlog.PacketHeader{DestConnectionID: destConnID},
- Raw: qlog.RawInfo{Length: len(data)},
- Trigger: qlog.PacketDropUnknownConnectionID,
+ Header: qlog.PacketHeader{DestConnectionID: destConnID},
+ Raw: qlog.RawInfo{Length: len(data)},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropUnknownConnectionID,
})
}
c.logger.Debugf("coalesced packet has different destination connection ID: %s, expected %s", destConnID, lastConnID)
if c.qlogger != nil {
if err == wire.ErrUnsupportedVersion {
c.qlogger.RecordEvent(qlog.PacketDropped{
- Header: qlog.PacketHeader{Version: hdr.Version},
- Raw: qlog.RawInfo{Length: len(data)},
- Trigger: qlog.PacketDropUnsupportedVersion,
+ Header: qlog.PacketHeader{Version: hdr.Version},
+ Raw: qlog.RawInfo{Length: len(data)},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropUnsupportedVersion,
})
} else {
c.qlogger.RecordEvent(qlog.PacketDropped{
- Raw: qlog.RawInfo{Length: len(data)},
- Trigger: qlog.PacketDropHeaderParseError,
+ Raw: qlog.RawInfo{Length: len(data)},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropHeaderParseError,
})
}
}
if hdr.Version != c.version {
if c.qlogger != nil {
c.qlogger.RecordEvent(qlog.PacketDropped{
- Raw: qlog.RawInfo{Length: len(data)},
- Trigger: qlog.PacketDropUnexpectedVersion,
+ Raw: qlog.RawInfo{Length: len(data)},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropUnexpectedVersion,
})
}
c.logger.Debugf("Dropping packet with version %x. Expected %x.", hdr.Version, c.version)
p.data = packetData
- processed, err := c.handleLongHeaderPacket(p, hdr)
+ processed, err := c.handleLongHeaderPacket(p, hdr, datagramID)
if err != nil {
return false, err
}
if counter > 0 {
p.buffer.Split()
}
- processed, err := c.handleShortHeaderPacket(p, counter > 0)
+ processed, err := c.handleShortHeaderPacket(p, counter > 0, datagramID)
if err != nil {
return false, err
}
return wasProcessed, nil
}
-func (c *Conn) handleShortHeaderPacket(p receivedPacket, isCoalesced bool) (wasProcessed bool, _ error) {
+func (c *Conn) handleShortHeaderPacket(
+ p receivedPacket,
+ isCoalesced bool,
+ datagramID qlog.DatagramID, // only for logging
+) (wasProcessed bool, _ error) {
var wasQueued bool
defer func() {
PacketType: qlog.PacketType1RTT,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: len(p.data)},
- Trigger: qlog.PacketDropHeaderParseError,
+ Raw: qlog.RawInfo{Length: len(p.data)},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropHeaderParseError,
})
return false, nil
}
return false, &StatelessResetError{}
}
}
- wasQueued, err = c.handleUnpackError(err, p, qlog.PacketType1RTT)
+ wasQueued, err = c.handleUnpackError(err, p, qlog.PacketType1RTT, datagramID)
return false, err
}
c.largestRcvdAppData = max(c.largestRcvdAppData, pn)
PacketType: qlog.PacketType1RTT,
PacketNumber: pn,
},
- Raw: qlog.RawInfo{Length: int(p.Size())},
- Trigger: qlog.PacketDropDuplicate,
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropDuplicate,
})
}
return false, nil
Length: int(p.Size()),
PayloadLength: int(p.Size() - wire.ShortHeaderLen(destConnID, pnLen)),
},
- Frames: frames,
- ECN: toQlogECN(p.ecn),
+ DatagramID: datagramID,
+ Frames: frames,
+ ECN: toQlogECN(p.ecn),
})
}
}
return true, err
}
c.logger.Debugf("sending path probe packet to %s", p.remoteAddr)
- c.logShortHeaderPacket(probe.DestConnID, probe.Ack, probe.Frames, probe.StreamFrames, probe.PacketNumber, probe.PacketNumberLen, probe.KeyPhase, protocol.ECNNon, buf.Len(), false)
+ c.logShortHeaderPacketWithDatagramID(probe, protocol.ECNNon, buf.Len(), false, datagramID)
c.registerPackedShortHeaderPacket(probe, protocol.ECNNon, p.rcvTime)
c.sendQueue.SendProbe(buf, p.remoteAddr)
}
return true, nil
}
-func (c *Conn) handleLongHeaderPacket(p receivedPacket, hdr *wire.Header) (wasProcessed bool, _ error) {
+func (c *Conn) handleLongHeaderPacket(p receivedPacket, hdr *wire.Header, datagramID qlog.DatagramID) (wasProcessed bool, _ error) {
var wasQueued bool
defer func() {
PacketType: qlog.PacketTypeInitial,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: int(p.Size())},
- Trigger: qlog.PacketDropUnknownConnectionID,
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropUnknownConnectionID,
})
}
c.logger.Debugf("Dropping Initial packet (%d bytes) with unexpected source connection ID: %s (expected %s)", p.Size(), hdr.SrcConnectionID, c.handshakeDestConnID)
PacketType: qlog.PacketType0RTT,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: int(p.Size())},
- Trigger: qlog.PacketDropUnexpectedPacket,
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropUnexpectedPacket,
})
}
return false, nil
packet, err := c.unpacker.UnpackLongHeader(hdr, p.data)
if err != nil {
- wasQueued, err = c.handleUnpackError(err, p, toQlogPacketType(hdr.Type))
+ wasQueued, err = c.handleUnpackError(err, p, toQlogPacketType(hdr.Type), datagramID)
return false, err
}
PacketNumber: pn,
Version: packet.hdr.Version,
},
- Raw: qlog.RawInfo{Length: int(p.Size()), PayloadLength: int(packet.hdr.Length)},
- Trigger: qlog.PacketDropDuplicate,
+ Raw: qlog.RawInfo{Length: int(p.Size()), PayloadLength: int(packet.hdr.Length)},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropDuplicate,
})
}
return false, nil
}
- if err := c.handleUnpackedLongHeaderPacket(packet, p.ecn, p.rcvTime, p.Size()); err != nil {
+ if err := c.handleUnpackedLongHeaderPacket(packet, p.ecn, p.rcvTime, datagramID, p.Size()); err != nil {
return false, err
}
return true, nil
}
-func (c *Conn) handleUnpackError(err error, p receivedPacket, pt qlog.PacketType) (wasQueued bool, _ error) {
+func (c *Conn) handleUnpackError(err error, p receivedPacket, pt qlog.PacketType, datagramID qlog.DatagramID) (wasQueued bool, _ error) {
switch err {
case handshake.ErrKeysDropped:
if c.qlogger != nil {
DestConnectionID: connID,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: int(p.Size())},
- Trigger: qlog.PacketDropKeyUnavailable,
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropKeyUnavailable,
})
}
c.logger.Debugf("Dropping %s packet (%d bytes) because we already dropped the keys.", pt, p.Size())
case handshake.ErrKeysNotYetAvailable:
// Sealer for this encryption level not yet available.
// Try again later.
- c.tryQueueingUndecryptablePacket(p, pt)
+ c.tryQueueingUndecryptablePacket(p, pt, datagramID)
return true, nil
case wire.ErrInvalidReservedBits:
return false, &qerr.TransportError{
DestConnectionID: connID,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: int(p.Size())},
- Trigger: qlog.PacketDropPayloadDecryptError,
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropPayloadDecryptError,
})
}
c.logger.Debugf("Dropping %s packet (%d bytes) that could not be unpacked. Error: %s", pt, p.Size(), err)
DestConnectionID: connID,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: int(p.Size())},
- Trigger: qlog.PacketDropHeaderParseError,
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropHeaderParseError,
})
}
c.logger.Debugf("Dropping %s packet (%d bytes) for which we couldn't unpack the header. Error: %s", pt, p.Size(), err)
packet *unpackedPacket,
ecn protocol.ECN,
rcvTime monotime.Time,
+ datagramID qlog.DatagramID, // only for logging
packetSize protocol.ByteCount, // only for logging
) error {
if !c.receivedFirstPacket {
Length: int(packetSize),
PayloadLength: int(packet.hdr.Length),
},
- Frames: frames,
- ECN: toQlogECN(ecn),
+ DatagramID: datagramID,
+ Frames: frames,
+ ECN: toQlogECN(ecn),
})
}
}
// the channel size, protocol.MaxConnUnprocessedPackets
if c.receivedPackets.Len() >= protocol.MaxConnUnprocessedPackets {
if c.qlogger != nil {
+ var datagramID qlog.DatagramID
+ if wire.IsLongHeaderPacket(p.data[0]) {
+ datagramID = qlog.CalculateDatagramID(p.data)
+ }
c.qlogger.RecordEvent(qlog.PacketDropped{
- Raw: qlog.RawInfo{Length: int(p.Size())},
- Trigger: qlog.PacketDropDOSPrevention,
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropDOSPrevention,
})
}
c.receivedPacketMx.Unlock()
return err
}
c.logger.Debugf("sending path probe packet from %s", c.LocalAddr())
- c.logShortHeaderPacket(probe.DestConnID, probe.Ack, probe.Frames, probe.StreamFrames, probe.PacketNumber, probe.PacketNumberLen, probe.KeyPhase, protocol.ECNNon, buf.Len(), false)
+ c.logShortHeaderPacket(probe, protocol.ECNNon, buf.Len())
c.registerPackedShortHeaderPacket(probe, protocol.ECNNon, now)
tr.WriteTo(buf.Data, c.conn.RemoteAddr())
// There's (likely) more data to send. Loop around again.
return err
}
ecn := c.sentPacketHandler.ECNMode(true)
- c.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false)
+ c.logShortHeaderPacket(p, ecn, buf.Len())
c.registerPackedShortHeaderPacket(p, ecn, now)
c.sendQueue.Send(buf, 0, ecn)
// There's (likely) more data to send. Loop around again.
}
return err
}
- c.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false)
+ c.logShortHeaderPacket(p, ecn, buf.Len())
c.registerPackedShortHeaderPacket(p, ecn, now)
c.sendQueue.Send(buf, 0, ecn)
return nil
return 0, err
}
size := buf.Len() - startLen
- c.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, size, false)
+ c.logShortHeaderPacket(p, ecn, size)
c.registerPackedShortHeaderPacket(p, ecn, now)
return size, nil
}
// tryQueueingUndecryptablePacket queues a packet for which we're missing the decryption keys.
// The qlogevents.PacketType is only used for logging purposes.
-func (c *Conn) tryQueueingUndecryptablePacket(p receivedPacket, pt qlog.PacketType) {
+func (c *Conn) tryQueueingUndecryptablePacket(p receivedPacket, pt qlog.PacketType, datagramID qlog.DatagramID) {
if c.handshakeComplete {
panic("shouldn't queue undecryptable packets after handshake completion")
}
PacketType: pt,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: int(p.Size())},
- Trigger: qlog.PacketDropDOSPrevention,
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: datagramID,
+ Trigger: qlog.PacketDropDOSPrevention,
})
}
c.logger.Infof("Dropping undecryptable packet (%d bytes). Undecryptable packet queue full.", p.Size())
PacketType: pt,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: int(p.Size())},
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: datagramID,
})
}
- c.undecryptablePackets = append(c.undecryptablePackets, p)
+ c.undecryptablePackets = append(c.undecryptablePackets, receivedPacketWithDatagramID{receivedPacket: p, datagramID: datagramID})
}
func (c *Conn) queueControlFrame(f wire.Frame) {
"net/netip"
"slices"
- "github.com/quic-go/quic-go/internal/ackhandler"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/wire"
"github.com/quic-go/quic-go/qlog"
return ack
}
-func (c *Conn) logLongHeaderPacket(p *longHeaderPacket, ecn protocol.ECN) {
+func (c *Conn) logLongHeaderPacket(p *longHeaderPacket, ecn protocol.ECN, datagramID qlog.DatagramID) {
// quic-go logging
if c.logger.Debug() {
p.header.Log(c.logger)
Length: int(p.length),
PayloadLength: int(p.header.Length),
},
- Frames: frames,
- ECN: toQlogECN(ecn),
+ DatagramID: datagramID,
+ Frames: frames,
+ ECN: toQlogECN(ecn),
})
}
}
-func (c *Conn) logShortHeaderPacket(
- destConnID protocol.ConnectionID,
- ackFrame *wire.AckFrame,
- frames []ackhandler.Frame,
- streamFrames []ackhandler.StreamFrame,
- pn protocol.PacketNumber,
- pnLen protocol.PacketNumberLen,
- kp protocol.KeyPhaseBit,
- ecn protocol.ECN,
- size protocol.ByteCount,
- isCoalesced bool,
-) {
+func (c *Conn) logShortHeaderPacket(p shortHeaderPacket, ecn protocol.ECN, size protocol.ByteCount) {
+ c.logShortHeaderPacketWithDatagramID(p, ecn, size, false, 0)
+}
+
+func (c *Conn) logShortHeaderPacketWithDatagramID(p shortHeaderPacket, ecn protocol.ECN, size protocol.ByteCount, isCoalesced bool, datagramID qlog.DatagramID) {
if c.logger.Debug() && !isCoalesced {
- c.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, 1-RTT (ECN: %s)", pn, size, c.logID, ecn)
+ c.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, 1-RTT (ECN: %s)", p.PacketNumber, size, c.logID, ecn)
}
// quic-go logging
if c.logger.Debug() {
- wire.LogShortHeader(c.logger, destConnID, pn, pnLen, kp)
- if ackFrame != nil {
- wire.LogFrame(c.logger, ackFrame, true)
+ wire.LogShortHeader(c.logger, p.DestConnID, p.PacketNumber, p.PacketNumberLen, p.KeyPhase)
+ if p.Ack != nil {
+ wire.LogFrame(c.logger, p.Ack, true)
}
- for _, f := range frames {
+ for _, f := range p.Frames {
wire.LogFrame(c.logger, f.Frame, true)
}
- for _, f := range streamFrames {
+ for _, f := range p.StreamFrames {
wire.LogFrame(c.logger, f.Frame, true)
}
}
// tracing
if c.qlogger != nil {
- numFrames := len(frames) + len(streamFrames)
- if ackFrame != nil {
+ numFrames := len(p.Frames) + len(p.StreamFrames)
+ if p.Ack != nil {
numFrames++
}
fs := make([]qlog.Frame, 0, numFrames)
- if ackFrame != nil {
- fs = append(fs, toQlogFrame(ackFrame))
+ if p.Ack != nil {
+ fs = append(fs, toQlogFrame(p.Ack))
}
- for _, f := range frames {
+ for _, f := range p.Frames {
fs = append(fs, toQlogFrame(f.Frame))
}
- for _, f := range streamFrames {
+ for _, f := range p.StreamFrames {
fs = append(fs, toQlogFrame(f.Frame))
}
c.qlogger.RecordEvent(qlog.PacketSent{
Header: qlog.PacketHeader{
PacketType: qlog.PacketType1RTT,
- KeyPhaseBit: kp,
- PacketNumber: pn,
+ KeyPhaseBit: p.KeyPhase,
+ PacketNumber: p.PacketNumber,
Version: c.version,
- DestConnectionID: destConnID,
+ DestConnectionID: p.DestConnID,
},
Raw: qlog.RawInfo{
Length: int(size),
- PayloadLength: int(size - wire.ShortHeaderLen(destConnID, pnLen)),
+ PayloadLength: int(size - wire.ShortHeaderLen(p.DestConnID, p.PacketNumberLen)),
},
- Frames: fs,
- ECN: toQlogECN(ecn),
+ DatagramID: datagramID,
+ Frames: fs,
+ ECN: toQlogECN(ecn),
})
}
}
func (c *Conn) logCoalescedPacket(packet *coalescedPacket, ecn protocol.ECN) {
+ var datagramID qlog.DatagramID
+ if c.qlogger != nil {
+ datagramID = qlog.CalculateDatagramID(packet.buffer.Data)
+ }
if c.logger.Debug() {
// There's a short period between dropping both Initial and Handshake keys and completion of the handshake,
// during which we might call PackCoalescedPacket but just pack a short header packet.
if len(packet.longHdrPackets) == 0 && packet.shortHdrPacket != nil {
- c.logShortHeaderPacket(
- packet.shortHdrPacket.DestConnID,
- packet.shortHdrPacket.Ack,
- packet.shortHdrPacket.Frames,
- packet.shortHdrPacket.StreamFrames,
- packet.shortHdrPacket.PacketNumber,
- packet.shortHdrPacket.PacketNumberLen,
- packet.shortHdrPacket.KeyPhase,
+ c.logShortHeaderPacketWithDatagramID(
+ *packet.shortHdrPacket,
ecn,
packet.shortHdrPacket.Length,
false,
+ datagramID,
)
return
}
}
}
for _, p := range packet.longHdrPackets {
- c.logLongHeaderPacket(p, ecn)
+ c.logLongHeaderPacket(p, ecn, datagramID)
}
if p := packet.shortHdrPacket; p != nil {
- c.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, p.Length, true)
+ c.logShortHeaderPacketWithDatagramID(*p, ecn, p.Length, true, datagramID)
}
}
}},
make([]byte, 16), /* Retry integrity tag */
)
- wasProcessed, err := tc.conn.handleOnePacket(p)
+ wasProcessed, err := tc.conn.handleOnePacket(p, 0)
require.NoError(t, err)
require.False(t, wasProcessed)
require.Equal(t,
protocol.ArbitraryLenConnectionID(tc.conn.origDestConnID.Bytes()),
[]Version{Version1},
)
- wasProcessed, err := tc.conn.handleOnePacket(receivedPacket{data: b, buffer: getPacketBuffer()})
+ wasProcessed, err := tc.conn.handleOnePacket(receivedPacket{data: b, buffer: getPacketBuffer()}, 0)
require.NoError(t, err)
require.False(t, wasProcessed)
require.Equal(t,
},
nil,
)
- wasProcessed, err := tc.conn.handleOnePacket(p)
+ wasProcessed, err := tc.conn.handleOnePacket(p, 42)
require.NoError(t, err)
require.False(t, wasProcessed)
require.Equal(t,
[]qlogwriter.Event{
qlog.PacketDropped{
- Header: qlog.PacketHeader{Version: 1234},
- Raw: qlog.RawInfo{Length: int(p.Size())},
- Trigger: qlog.PacketDropUnsupportedVersion,
+ Header: qlog.PacketHeader{Version: 1234},
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: 42,
+ Trigger: qlog.PacketDropUnsupportedVersion,
},
},
eventRecorder.Events(qlog.PacketDropped{}),
nil,
)
p.data[0] ^= 0x40 // unset the QUIC bit
- wasProcessed, err := tc.conn.handleOnePacket(p)
+ wasProcessed, err := tc.conn.handleOnePacket(p, 42)
require.NoError(t, err)
require.False(t, wasProcessed)
require.Equal(t,
[]qlogwriter.Event{
qlog.PacketDropped{
- Header: qlog.PacketHeader{},
- Raw: qlog.RawInfo{Length: int(p.Size())},
- Trigger: qlog.PacketDropHeaderParseError,
+ Header: qlog.PacketHeader{},
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: 42,
+ Trigger: qlog.PacketDropHeaderParseError,
},
},
eventRecorder.Events(qlog.PacketDropped{}),
},
nil,
)
- wasProcessed, err := tc.conn.handleOnePacket(p)
+ wasProcessed, err := tc.conn.handleOnePacket(p, 1234)
require.NoError(t, err)
require.False(t, wasProcessed)
require.Equal(t,
PacketType: qlog.PacketType0RTT,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: int(p.Size())},
- Trigger: qlog.PacketDropUnexpectedPacket,
+ Raw: qlog.RawInfo{Length: int(p.Size())},
+ DatagramID: 1234,
+ Trigger: qlog.PacketDropUnexpectedPacket,
},
},
eventRecorder.Events(qlog.PacketDropped{}),
rph.EXPECT().ReceivedPacket(protocol.PacketNumber(0x1337), protocol.ECNCE, protocol.EncryptionInitial, rcvTime, false),
)
- wasProcessed, err := tc.conn.handleOnePacket(packet)
+ wasProcessed, err := tc.conn.handleOnePacket(packet, 42)
require.NoError(t, err)
require.True(t, wasProcessed)
require.Equal(t,
PacketNumber: protocol.PacketNumber(0x1337),
Version: protocol.Version1,
},
- Frames: []qlog.Frame{},
- ECN: qlog.ECNCE,
- Raw: qlog.RawInfo{Length: int(packet.Size()), PayloadLength: 1},
+ Frames: []qlog.Frame{},
+ ECN: qlog.ECNCE,
+ Raw: qlog.RawInfo{Length: int(packet.Size()), PayloadLength: 1},
+ DatagramID: 42,
},
},
eventRecorder.Events(qlog.PacketReceived{}, qlog.PacketDropped{}),
hdr: &unpackedHdr,
data: []byte{0}, // one PADDING frame
}, nil)
- wasProcessed, err = tc.conn.handleOnePacket(packet)
+ wasProcessed, err = tc.conn.handleOnePacket(packet, 43)
require.NoError(t, err)
require.False(t, wasProcessed)
require.Equal(t,
PacketNumber: protocol.PacketNumber(0x1337),
Version: protocol.Version1,
},
- Raw: qlog.RawInfo{Length: int(packet.Size()), PayloadLength: 1},
- Trigger: qlog.PacketDropDuplicate,
+ Raw: qlog.RawInfo{Length: int(packet.Size()), PayloadLength: 1},
+ DatagramID: 43,
+ Trigger: qlog.PacketDropDuplicate,
},
},
eventRecorder.Events(qlog.PacketReceived{}, qlog.PacketDropped{}),
unpacker.EXPECT().UnpackShortHeader(gomock.Any(), gomock.Any()).Return(
protocol.PacketNumber(0x1337), protocol.PacketNumberLen2, protocol.KeyPhaseZero, []byte{0} /* PADDING */, nil,
)
- wasProcessed, err = tc.conn.handleOnePacket(packet)
+ wasProcessed, err = tc.conn.handleOnePacket(packet, 0)
require.NoError(t, err)
require.Equal(t,
[]qlogwriter.Event{
rph.EXPECT().ReceivedPacket(protocol.PacketNumber(1338), protocol.ECT1, protocol.EncryptionHandshake, rcvTime, true),
)
rph.EXPECT().DropPackets(protocol.EncryptionInitial)
- wasProcessed, err := tc.conn.handleOnePacket(packet)
+ wasProcessed, err := tc.conn.handleOnePacket(packet, 42)
require.NoError(t, err)
require.True(t, wasProcessed)
PacketNumber: protocol.PacketNumber(1337),
Version: protocol.Version1,
},
- Raw: qlog.RawInfo{Length: int(firstPacketLen), PayloadLength: 1},
- Frames: []qlog.Frame{},
- ECN: qlog.ECT1,
+ Raw: qlog.RawInfo{Length: int(firstPacketLen), PayloadLength: 1},
+ DatagramID: 42,
+ Frames: []qlog.Frame{},
+ ECN: qlog.ECT1,
},
qlog.PacketReceived{
Header: qlog.PacketHeader{
PacketNumber: protocol.PacketNumber(1338),
Version: protocol.Version1,
},
- Raw: qlog.RawInfo{Length: int(packet2.Size()), PayloadLength: 1},
- Frames: []qlog.Frame{{Frame: &wire.PingFrame{}}},
- ECN: qlog.ECT1,
+ Raw: qlog.RawInfo{Length: int(packet2.Size()), PayloadLength: 1},
+ DatagramID: 42,
+ Frames: []qlog.Frame{{Frame: &wire.PingFrame{}}},
+ ECN: qlog.ECT1,
},
qlog.PacketDropped{
- Header: qlog.PacketHeader{DestConnectionID: incorrectSrcConnID},
- Raw: qlog.RawInfo{Length: int(packet3.Size())},
- Trigger: qlog.PacketDropUnknownConnectionID,
+ Header: qlog.PacketHeader{DestConnectionID: incorrectSrcConnID},
+ Raw: qlog.RawInfo{Length: int(packet3.Size())},
+ DatagramID: 42,
+ Trigger: qlog.PacketDropUnknownConnectionID,
},
},
eventRecorder.Events(qlog.PacketReceived{}, qlog.PacketDropped{}),
hdrs := make(map[string]*wire.ExtendedHeader)
packet1 := getLongHeaderPacket(t, tc.remoteAddr, &hdr1, []byte("packet1"))
+ datagramID1 := qlog.CalculateDatagramID(packet1.data)
hdrs["packet1"] = &hdr1
tc.conn.handlePacket(packet1)
packet2 := getLongHeaderPacket(t, tc.remoteAddr, &hdr2, []byte("packet2"))
- tc.conn.handlePacket(packet2)
+ datagramID2 := qlog.CalculateDatagramID(packet2.data)
hdrs["packet2"] = &hdr2
-
+ tc.conn.handlePacket(packet2)
synctest.Wait()
require.Equal(t,
PacketType: qlog.PacketTypeHandshake,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: int(packet1.Size())},
+ Raw: qlog.RawInfo{Length: int(packet1.Size())},
+ DatagramID: datagramID1,
},
qlog.PacketBuffered{
Header: qlog.PacketHeader{
PacketType: qlog.PacketTypeHandshake,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: int(packet2.Size())},
+ Raw: qlog.RawInfo{Length: int(packet2.Size())},
+ DatagramID: datagramID2,
},
},
eventRecorder.Events(qlog.PacketBuffered{}),
)
packet3 := getLongHeaderPacket(t, tc.remoteAddr, &hdr3, []byte("packet3"))
+ datagramID3 := qlog.CalculateDatagramID(packet3.data)
tc.conn.handlePacket(packet3)
synctest.Wait()
PacketNumber: 3,
Version: protocol.Version1,
},
- Raw: qlog.RawInfo{Length: int(packet3.Size()), PayloadLength: 8},
- Frames: []qlog.Frame{{Frame: &qlog.CryptoFrame{Length: 6}}},
+ Raw: qlog.RawInfo{Length: int(packet3.Size()), PayloadLength: 8},
+ DatagramID: datagramID3,
+ Frames: []qlog.Frame{{Frame: &qlog.CryptoFrame{Length: 6}}},
},
qlog.PacketReceived{
Header: qlog.PacketHeader{
PacketNumber: 1,
Version: protocol.Version1,
},
- Raw: qlog.RawInfo{Length: int(packet1.Size()), PayloadLength: 8},
- Frames: []qlog.Frame{},
+ Raw: qlog.RawInfo{Length: int(packet1.Size()), PayloadLength: 8},
+ DatagramID: datagramID1,
+ Frames: []qlog.Frame{},
},
qlog.PacketReceived{
Header: qlog.PacketHeader{
PacketNumber: 2,
Version: protocol.Version1,
},
- Raw: qlog.RawInfo{Length: int(packet1.Size()), PayloadLength: 8},
- Frames: []qlog.Frame{},
+ Raw: qlog.RawInfo{Length: int(packet1.Size()), PayloadLength: 8},
+ DatagramID: datagramID2,
+ Frames: []qlog.Frame{},
},
},
eventRecorder.Events(qlog.PacketReceived{}, qlog.PacketBuffered{}),
tc.srcConnID,
[]protocol.Version{1234, protocol.Version1},
)
- wasProcessed, err := tc.conn.handleOnePacket(vnp)
+ wasProcessed, err := tc.conn.handleOnePacket(vnp, 0)
require.NoError(t, err)
require.False(t, wasProcessed)
require.Equal(t,
// unparseable, since it's missing 2 bytes
vnp.data = vnp.data[:len(vnp.data)-2]
- wasProcessed, err = tc.conn.handleOnePacket(vnp)
+ wasProcessed, err = tc.conn.handleOnePacket(vnp, 0)
require.NoError(t, err)
require.False(t, wasProcessed)
require.Equal(t,
// invalid integrity tag
retry := getRetryPacket(t, newConnID, tc.srcConnID, tc.destConnID, []byte("foobar"))
retry.data[len(retry.data)-1]++
- wasProcessed, err := tc.conn.handleOnePacket(retry)
+ wasProcessed, err := tc.conn.handleOnePacket(retry, 0)
require.NoError(t, err)
require.False(t, wasProcessed)
require.Equal(t,
// receive a retry that doesn't change the connection ID
retry = getRetryPacket(t, tc.destConnID, tc.srcConnID, tc.destConnID, []byte("foobar"))
- wasProcessed, err = tc.conn.handleOnePacket(retry)
+ wasProcessed, err = tc.conn.handleOnePacket(retry, 0)
require.NoError(t, err)
require.False(t, wasProcessed)
require.Equal(t,
buffer: getPacketBuffer(),
rcvTime: monotime.Now(),
remoteAddr: tc.remoteAddr,
- })
+ }, 0)
require.NoError(t, err)
require.True(t, wasProcessed)
// receive a retry
retry := getRetryPacket(t, tc.destConnID, tc.srcConnID, tc.destConnID, []byte("foobar"))
- wasProcessed, err = tc.conn.handleOnePacket(retry)
+ wasProcessed, err = tc.conn.handleOnePacket(retry, 0)
require.NoError(t, err)
require.False(t, wasProcessed)
PacketNumber: 1,
Version: protocol.Version1,
},
- Raw: qlog.RawInfo{Length: int(packet1.Size()), PayloadLength: int(hdr1.Length)},
- Frames: []qlog.Frame{},
+ Raw: qlog.RawInfo{Length: int(packet1.Size()), PayloadLength: int(hdr1.Length)},
+ DatagramID: qlog.CalculateDatagramID(packet1.data),
+ Frames: []qlog.Frame{},
},
},
eventRecorder.Events(qlog.PacketReceived{}, qlog.PacketDropped{}),
PacketType: qlog.PacketTypeInitial,
PacketNumber: protocol.InvalidPacketNumber,
},
- Raw: qlog.RawInfo{Length: int(packet2.Size())},
- Trigger: qlog.PacketDropUnknownConnectionID,
+ Raw: qlog.RawInfo{Length: int(packet2.Size())},
+ DatagramID: qlog.CalculateDatagramID(packet2.data),
+ Trigger: qlog.PacketDropUnknownConnectionID,
},
},
eventRecorder.Events(qlog.PacketDropped{}, qlog.PacketReceived{}),
type PacketSent struct {
Header PacketHeader
Raw RawInfo
+ DatagramID DatagramID
Frames []Frame
ECN ECN
IsCoalesced bool
if err := e.Raw.encode(enc); err != nil {
return err
}
+ if e.DatagramID != 0 {
+ h.WriteToken(jsontext.String("datagram_id"))
+ h.WriteToken(jsontext.Uint(uint64(e.DatagramID)))
+ }
if len(e.Frames) > 0 {
h.WriteToken(jsontext.String("frames"))
if err := frames(e.Frames).encode(enc); err != nil {
type PacketReceived struct {
Header PacketHeader
Raw RawInfo
+ DatagramID DatagramID
Frames []Frame
ECN ECN
IsCoalesced bool
if err := e.Raw.encode(enc); err != nil {
return err
}
+ if e.DatagramID != 0 {
+ h.WriteToken(jsontext.String("datagram_id"))
+ h.WriteToken(jsontext.Uint(uint64(e.DatagramID)))
+ }
if len(e.Frames) > 0 {
h.WriteToken(jsontext.String("frames"))
if err := frames(e.Frames).encode(enc); err != nil {
}
type PacketBuffered struct {
- Header PacketHeader
- Raw RawInfo
+ Header PacketHeader
+ Raw RawInfo
+ DatagramID DatagramID
}
func (e PacketBuffered) Name() string { return "transport:packet_buffered" }
if err := e.Raw.encode(enc); err != nil {
return err
}
+ if e.DatagramID != 0 {
+ h.WriteToken(jsontext.String("datagram_id"))
+ h.WriteToken(jsontext.Uint(uint64(e.DatagramID)))
+ }
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String("keys_unavailable"))
h.WriteToken(jsontext.EndObject)
// PacketDropped is the transport:packet_dropped event.
type PacketDropped struct {
- Header PacketHeader
- Raw RawInfo
- Trigger PacketDropReason
+ Header PacketHeader
+ Raw RawInfo
+ DatagramID DatagramID
+ Trigger PacketDropReason
}
func (e PacketDropped) Name() string { return "transport:packet_dropped" }
if err := e.Raw.encode(enc); err != nil {
return err
}
+ if e.DatagramID != 0 {
+ h.WriteToken(jsontext.String("datagram_id"))
+ h.WriteToken(jsontext.Uint(uint64(e.DatagramID)))
+ }
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String(string(e.Trigger)))
h.WriteToken(jsontext.EndObject)
require.Equal(t, "transport:packet_sent", name)
require.Contains(t, ev, "raw")
raw := ev["raw"].(map[string]any)
+ require.NotContains(t, ev, "datagram_id")
require.Equal(t, float64(987), raw["length"])
require.Equal(t, float64(1337), raw["payload_length"])
require.Contains(t, ev, "header")
require.Equal(t, "stream", frames[1].(map[string]any)["frame_type"])
}
-func TestPacketSentShort(t *testing.T) {
+func TestPacketSent1RTT(t *testing.T) {
+ t.Run("with datagram ID", func(t *testing.T) {
+ testPacketSent1RTT(t, 1337)
+ })
+
+ t.Run("without datagram ID", func(t *testing.T) {
+ testPacketSent1RTT(t, 0)
+ })
+}
+
+func testPacketSent1RTT(t *testing.T, datagramID DatagramID) {
name, ev := testEventEncoding(t, &PacketSent{
Header: PacketHeader{
PacketType: PacketType1RTT,
{Frame: &AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 10}}}},
{Frame: &MaxDataFrame{MaximumData: 987}},
},
- ECN: ECNUnsupported,
+ ECN: ECNUnsupported,
+ DatagramID: datagramID,
})
require.Equal(t, "transport:packet_sent", name)
require.Len(t, frames, 2)
require.Equal(t, "ack", frames[0].(map[string]any)["frame_type"])
require.Equal(t, "max_data", frames[1].(map[string]any)["frame_type"])
+ if datagramID != 0 {
+ require.Contains(t, ev, "datagram_id")
+ require.Equal(t, float64(datagramID), ev["datagram_id"])
+ } else {
+ require.NotContains(t, ev, "datagram_id")
+ }
}
func TestPacketReceived(t *testing.T) {
{Frame: &MaxStreamDataFrame{StreamID: 42, MaximumStreamData: 987}},
{Frame: &StreamFrame{StreamID: 123, Offset: 1234, Length: 6, Fin: true}},
},
- ECN: ECT0,
+ ECN: ECT0,
+ DatagramID: 42,
})
require.Equal(t, "transport:packet_received", name)
require.Equal(t, "deadbeef", token["data"])
require.Contains(t, ev, "frames")
require.Len(t, ev["frames"].([]any), 2)
+ require.Contains(t, ev, "datagram_id")
+ require.Equal(t, float64(42), ev["datagram_id"])
}
func TestPacketReceived1RTT(t *testing.T) {
+ t.Run("with datagram ID", func(t *testing.T) {
+ testPacketReceived1RTT(t, 1337)
+ })
+
+ t.Run("without datagram ID", func(t *testing.T) {
+ testPacketReceived1RTT(t, 0)
+ })
+}
+
+func testPacketReceived1RTT(t *testing.T, datagramID DatagramID) {
name, ev := testEventEncoding(t, &PacketReceived{
Header: PacketHeader{
PacketType: PacketType1RTT,
{Frame: &MaxStreamDataFrame{StreamID: 42, MaximumStreamData: 987}},
{Frame: &StreamFrame{StreamID: 123, Offset: 1234, Length: 6, Fin: true}},
},
- ECN: ECT1,
+ ECN: ECT1,
+ DatagramID: datagramID,
})
require.Equal(t, "transport:packet_received", name)
hdr := ev["header"].(map[string]any)
require.Equal(t, "1RTT", hdr["packet_type"])
require.Equal(t, float64(1337), hdr["packet_number"])
- require.Equal(t, "0", hdr["key_phase_bit"])
require.Contains(t, ev, "frames")
require.Len(t, ev["frames"].([]any), 2)
+ if datagramID != 0 {
+ require.Contains(t, ev, "datagram_id")
+ require.Equal(t, float64(datagramID), ev["datagram_id"])
+ } else {
+ require.NotContains(t, ev, "datagram_id")
+ }
}
func TestPacketReceivedRetry(t *testing.T) {
import (
"fmt"
+ "hash/crc32"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/qerr"
// StatelessReset indicates the connection was closed due to receiving a stateless reset from the peer
ConnectionCloseTriggerStatelessReset ConnectionCloseTrigger = "stateless_reset"
)
+
+// DatagramID is a unique identifier for a datagram
+type DatagramID uint32
+
+// CalculateDatagramID computes a DatagramID for a given packet
+func CalculateDatagramID(packet []byte) DatagramID {
+ return DatagramID(crc32.ChecksumIEEE(packet))
+}
require.Equal(t, "0RTT", string(EncryptionLevelToPacketType(protocol.Encryption0RTT)))
require.Equal(t, "1RTT", string(EncryptionLevelToPacketType(protocol.Encryption1RTT)))
}
+
+func TestCalculateDatagramID(t *testing.T) {
+ require.Equal(t, DatagramID(0xcbf43926), CalculateDatagramID([]byte("123456789")))
+}