forked from quic-go/quic-go
qlog: use PathEndpointInfo in connection_started (#5368)
* qlog: use PathEndpointInfo in connection_started * correctly deal with dual-stack addresses
This commit is contained in:
@@ -413,12 +413,7 @@ var newClientConnection = func(
|
||||
if addr, ok := conn.RemoteAddr().(*net.UDPAddr); ok {
|
||||
destAddr = addr
|
||||
}
|
||||
s.qlogger.RecordEvent(qlog.StartedConnection{
|
||||
SrcAddr: srcAddr,
|
||||
DestAddr: destAddr,
|
||||
SrcConnectionID: srcConnID,
|
||||
DestConnectionID: destConnID,
|
||||
})
|
||||
s.qlogger.RecordEvent(startedConnectionEvent(srcAddr, destAddr))
|
||||
}
|
||||
s.connIDManager = newConnIDManager(
|
||||
destConnID,
|
||||
@@ -1658,12 +1653,7 @@ func (c *Conn) handleUnpackedLongHeaderPacket(
|
||||
if addr, ok := c.conn.RemoteAddr().(*net.UDPAddr); ok {
|
||||
destAddr = addr
|
||||
}
|
||||
c.qlogger.RecordEvent(qlog.StartedConnection{
|
||||
SrcAddr: srcAddr,
|
||||
DestAddr: destAddr,
|
||||
SrcConnectionID: packet.hdr.SrcConnectionID,
|
||||
DestConnectionID: packet.hdr.DestConnectionID,
|
||||
})
|
||||
c.qlogger.RecordEvent(startedConnectionEvent(srcAddr, destAddr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"slices"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/ackhandler"
|
||||
@@ -268,3 +270,53 @@ func toQlogPacketType(pt protocol.PacketType) qlog.PacketType {
|
||||
}
|
||||
return qpt
|
||||
}
|
||||
|
||||
func toPathEndpointInfo(addr *net.UDPAddr) qlog.PathEndpointInfo {
|
||||
if addr == nil {
|
||||
return qlog.PathEndpointInfo{}
|
||||
}
|
||||
|
||||
var info qlog.PathEndpointInfo
|
||||
if addr.IP == nil || addr.IP.To4() != nil {
|
||||
addrPort := netip.AddrPortFrom(netip.AddrFrom4([4]byte(addr.IP.To4())), uint16(addr.Port))
|
||||
if addrPort.IsValid() {
|
||||
info.IPv4 = addrPort
|
||||
}
|
||||
} else {
|
||||
addrPort := netip.AddrPortFrom(netip.AddrFrom16([16]byte(addr.IP.To16())), uint16(addr.Port))
|
||||
if addrPort.IsValid() {
|
||||
info.IPv6 = addrPort
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// startedConnectionEvent builds a StartedConnection event using consistent logic
|
||||
// for both endpoints. If the local address is unspecified (e.g., dual-stack
|
||||
// listener), it selects the family based on the remote address and uses the
|
||||
// unspecified address of that family with the local port.
|
||||
func startedConnectionEvent(local, remote *net.UDPAddr) qlog.StartedConnection {
|
||||
var localInfo, remoteInfo qlog.PathEndpointInfo
|
||||
if remote != nil {
|
||||
remoteInfo = toPathEndpointInfo(remote)
|
||||
}
|
||||
if local != nil {
|
||||
if local.IP == nil || local.IP.IsUnspecified() {
|
||||
// Choose local family based on the remote address family.
|
||||
if remote != nil && remote.IP.To4() != nil {
|
||||
ap := netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), uint16(local.Port))
|
||||
if ap.IsValid() {
|
||||
localInfo.IPv4 = ap
|
||||
}
|
||||
} else if remote != nil && remote.IP.To16() != nil && remote.IP.To4() == nil {
|
||||
ap := netip.AddrPortFrom(netip.AddrFrom16([16]byte{}), uint16(local.Port))
|
||||
if ap.IsValid() {
|
||||
localInfo.IPv6 = ap
|
||||
}
|
||||
}
|
||||
} else {
|
||||
localInfo = toPathEndpointInfo(local)
|
||||
}
|
||||
}
|
||||
return qlog.StartedConnection{Local: localInfo, Remote: remoteInfo}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/wire"
|
||||
@@ -70,3 +72,72 @@ func TestConnectionLoggingOtherFrames(t *testing.T) {
|
||||
f := toQlogFrame(&wire.MaxDataFrame{MaximumData: 1234})
|
||||
require.Equal(t, &qlog.MaxDataFrame{MaximumData: 1234}, f.Frame)
|
||||
}
|
||||
|
||||
func TestConnectionLoggingStartedConnectionEvent(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
local *net.UDPAddr
|
||||
remote *net.UDPAddr
|
||||
wantLocalIP string
|
||||
wantLocalPort uint16
|
||||
wantRemote netip.AddrPort
|
||||
}{
|
||||
{
|
||||
name: "unspecified local, remote IPv4 -> 0.0.0.0",
|
||||
local: &net.UDPAddr{Port: 58451},
|
||||
remote: &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 6121},
|
||||
wantLocalIP: "0.0.0.0",
|
||||
wantLocalPort: 58451,
|
||||
wantRemote: netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), 6121),
|
||||
},
|
||||
{
|
||||
name: "unspecified local, remote IPv6 -> ::",
|
||||
local: &net.UDPAddr{Port: 4242},
|
||||
remote: &net.UDPAddr{IP: net.ParseIP("2001:db8::1"), Port: 6121},
|
||||
wantLocalIP: "::",
|
||||
wantLocalPort: 4242,
|
||||
wantRemote: func() netip.AddrPort { a, _ := netip.ParseAddr("2001:db8::1"); return netip.AddrPortFrom(a, 6121) }(),
|
||||
},
|
||||
{
|
||||
name: "specified local IPv4",
|
||||
local: &net.UDPAddr{IP: net.IPv4(192, 168, 1, 10), Port: 9999},
|
||||
remote: &net.UDPAddr{IP: net.IPv4(10, 0, 0, 1), Port: 1234},
|
||||
wantLocalIP: "192.168.1.10",
|
||||
wantLocalPort: 9999,
|
||||
wantRemote: netip.AddrPortFrom(netip.AddrFrom4([4]byte{10, 0, 0, 1}), 1234),
|
||||
},
|
||||
{
|
||||
name: "specified local IPv6",
|
||||
local: &net.UDPAddr{IP: net.ParseIP("fe80::1"), Port: 999},
|
||||
remote: &net.UDPAddr{IP: net.ParseIP("2001:db8::1"), Port: 6121},
|
||||
wantLocalIP: "fe80::1",
|
||||
wantLocalPort: 999,
|
||||
wantRemote: func() netip.AddrPort { a, _ := netip.ParseAddr("2001:db8::1"); return netip.AddrPortFrom(a, 6121) }(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ev := startedConnectionEvent(tc.local, tc.remote)
|
||||
var gotIP string
|
||||
var gotPort uint16
|
||||
if ev.Local.IPv4.IsValid() {
|
||||
gotIP = ev.Local.IPv4.Addr().String()
|
||||
gotPort = ev.Local.IPv4.Port()
|
||||
} else if ev.Local.IPv6.IsValid() {
|
||||
gotIP = ev.Local.IPv6.Addr().String()
|
||||
gotPort = ev.Local.IPv6.Port()
|
||||
}
|
||||
require.Equal(t, tc.wantLocalIP, gotIP)
|
||||
require.Equal(t, tc.wantLocalPort, gotPort)
|
||||
|
||||
var gotRemote netip.AddrPort
|
||||
if ev.Remote.IPv4.IsValid() {
|
||||
gotRemote = ev.Remote.IPv4
|
||||
} else if ev.Remote.IPv6.IsValid() {
|
||||
gotRemote = ev.Remote.IPv6
|
||||
}
|
||||
require.Equal(t, tc.wantRemote, gotRemote)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package qlog
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
@@ -56,11 +55,33 @@ func (i RawInfo) encode(enc *jsontext.Encoder) error {
|
||||
return h.err
|
||||
}
|
||||
|
||||
type PathEndpointInfo struct {
|
||||
IPv4 netip.AddrPort
|
||||
IPv6 netip.AddrPort
|
||||
}
|
||||
|
||||
func (p PathEndpointInfo) encode(enc *jsontext.Encoder) error {
|
||||
h := encoderHelper{enc: enc}
|
||||
h.WriteToken(jsontext.BeginObject)
|
||||
if p.IPv4.IsValid() {
|
||||
h.WriteToken(jsontext.String("ip_v4"))
|
||||
h.WriteToken(jsontext.String(p.IPv4.Addr().String()))
|
||||
h.WriteToken(jsontext.String("port_v4"))
|
||||
h.WriteToken(jsontext.Int(int64(p.IPv4.Port())))
|
||||
}
|
||||
if p.IPv6.IsValid() {
|
||||
h.WriteToken(jsontext.String("ip_v6"))
|
||||
h.WriteToken(jsontext.String(p.IPv6.Addr().String()))
|
||||
h.WriteToken(jsontext.String("port_v6"))
|
||||
h.WriteToken(jsontext.Int(int64(p.IPv6.Port())))
|
||||
}
|
||||
h.WriteToken(jsontext.EndObject)
|
||||
return h.err
|
||||
}
|
||||
|
||||
type StartedConnection struct {
|
||||
SrcAddr *net.UDPAddr
|
||||
DestAddr *net.UDPAddr
|
||||
SrcConnectionID ConnectionID
|
||||
DestConnectionID ConnectionID
|
||||
Local PathEndpointInfo
|
||||
Remote PathEndpointInfo
|
||||
}
|
||||
|
||||
func (e StartedConnection) Name() string { return "transport:connection_started" }
|
||||
@@ -68,25 +89,14 @@ func (e StartedConnection) Name() string { return "transport:connection_started"
|
||||
func (e StartedConnection) Encode(enc *jsontext.Encoder, _ time.Time) error {
|
||||
h := encoderHelper{enc: enc}
|
||||
h.WriteToken(jsontext.BeginObject)
|
||||
if e.SrcAddr.IP.To4() != nil {
|
||||
h.WriteToken(jsontext.String("ip_version"))
|
||||
h.WriteToken(jsontext.String("ipv4"))
|
||||
} else {
|
||||
h.WriteToken(jsontext.String("ip_version"))
|
||||
h.WriteToken(jsontext.String("ipv6"))
|
||||
h.WriteToken(jsontext.String("local"))
|
||||
if err := e.Local.encode(enc); err != nil {
|
||||
return err
|
||||
}
|
||||
h.WriteToken(jsontext.String("remote"))
|
||||
if err := e.Remote.encode(enc); err != nil {
|
||||
return err
|
||||
}
|
||||
h.WriteToken(jsontext.String("src_ip"))
|
||||
h.WriteToken(jsontext.String(e.SrcAddr.IP.String()))
|
||||
h.WriteToken(jsontext.String("src_port"))
|
||||
h.WriteToken(jsontext.Int(int64(e.SrcAddr.Port)))
|
||||
h.WriteToken(jsontext.String("dst_ip"))
|
||||
h.WriteToken(jsontext.String(e.DestAddr.IP.String()))
|
||||
h.WriteToken(jsontext.String("dst_port"))
|
||||
h.WriteToken(jsontext.Int(int64(e.DestAddr.Port)))
|
||||
h.WriteToken(jsontext.String("src_cid"))
|
||||
h.WriteToken(jsontext.String(e.SrcConnectionID.String()))
|
||||
h.WriteToken(jsontext.String("dst_cid"))
|
||||
h.WriteToken(jsontext.String(e.DestConnectionID.String()))
|
||||
h.WriteToken(jsontext.EndObject)
|
||||
return h.err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package qlog
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -58,21 +57,28 @@ func decode(t *testing.T, data string) (string, map[string]any) {
|
||||
}
|
||||
|
||||
func TestStartedConnection(t *testing.T) {
|
||||
var localInfo, remoteInfo PathEndpointInfo
|
||||
localInfo.IPv4 = netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 13, 37}), 42)
|
||||
ip, err := netip.ParseAddr("2001:db8::1")
|
||||
require.NoError(t, err)
|
||||
remoteInfo.IPv6 = netip.AddrPortFrom(ip, 24)
|
||||
|
||||
name, ev := testEventEncoding(t, &StartedConnection{
|
||||
SrcAddr: &net.UDPAddr{IP: net.IPv4(192, 168, 13, 37), Port: 42},
|
||||
DestAddr: &net.UDPAddr{IP: net.IPv4(192, 168, 12, 34), Port: 24},
|
||||
SrcConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
|
||||
DestConnectionID: protocol.ParseConnectionID([]byte{5, 6, 7, 8}),
|
||||
Local: localInfo,
|
||||
Remote: remoteInfo,
|
||||
})
|
||||
|
||||
require.Equal(t, "transport:connection_started", name)
|
||||
require.Equal(t, "ipv4", ev["ip_version"])
|
||||
require.Equal(t, "192.168.13.37", ev["src_ip"])
|
||||
require.Equal(t, float64(42), ev["src_port"])
|
||||
require.Equal(t, "192.168.12.34", ev["dst_ip"])
|
||||
require.Equal(t, float64(24), ev["dst_port"])
|
||||
require.Equal(t, "01020304", ev["src_cid"])
|
||||
require.Equal(t, "05060708", ev["dst_cid"])
|
||||
|
||||
local, ok := ev["local"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "192.168.13.37", local["ip_v4"])
|
||||
require.Equal(t, float64(42), local["port_v4"])
|
||||
|
||||
remote, ok := ev["remote"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "2001:db8::1", remote["ip_v6"])
|
||||
require.Equal(t, float64(24), remote["port_v6"])
|
||||
}
|
||||
|
||||
func TestVersionInformation(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user