Resolve tracer 'self' to v6 overlay address when peer is IPv6

The packet tracer resolved 'self' to the v4 overlay address
unconditionally, causing "mixed address families" errors when tracing
v6 traffic. Pick the self address matching the peer's address family.

Add Engine.GetWgV6Addr() and rework parseAddress into
resolveTraceAddresses which parses the non-self address first to
determine the family, then resolves self accordingly.
This commit is contained in:
Viktor Liu
2026-04-10 13:28:32 +02:00
parent 4fc910031b
commit 51d1edf8b2
2 changed files with 57 additions and 11 deletions

View File

@@ -2159,6 +2159,14 @@ func (e *Engine) GetWgAddr() netip.Addr {
return e.wgInterface.Address().IP
}
// GetWgV6Addr returns the IPv6 overlay address of the WireGuard interface.
func (e *Engine) GetWgV6Addr() netip.Addr {
if e.wgInterface == nil {
return netip.Addr{}
}
return e.wgInterface.Address().IPv6
}
func (e *Engine) RenewTun(fd int) error {
e.syncMsgMux.Lock()
wgInterface := e.wgInterface

View File

@@ -24,14 +24,9 @@ func (s *Server) TracePacket(_ context.Context, req *proto.TracePacketRequest) (
return nil, err
}
srcAddr, err := s.parseAddress(req.GetSourceIp(), engine)
srcAddr, dstAddr, err := s.resolveTraceAddresses(req.GetSourceIp(), req.GetDestinationIp(), engine)
if err != nil {
return nil, fmt.Errorf("invalid source IP address: %w", err)
}
dstAddr, err := s.parseAddress(req.GetDestinationIp(), engine)
if err != nil {
return nil, fmt.Errorf("invalid destination IP address: %w", err)
return nil, err
}
protocol, err := s.parseProtocol(req.GetProtocol())
@@ -89,16 +84,59 @@ func (s *Server) getPacketTracer() (packetTracer, *internal.Engine, error) {
return tracer, engine, nil
}
func (s *Server) parseAddress(addr string, engine *internal.Engine) (netip.Addr, error) {
if addr == "self" {
return engine.GetWgAddr(), nil
// resolveTraceAddresses parses src/dst, resolving "self" to the local overlay
// address matching the peer's address family.
func (s *Server) resolveTraceAddresses(src, dst string, engine *internal.Engine) (netip.Addr, netip.Addr, error) {
srcSelf := src == "self"
dstSelf := dst == "self"
if srcSelf && dstSelf {
return netip.Addr{}, netip.Addr{}, fmt.Errorf("both source and destination cannot be 'self'")
}
var srcAddr, dstAddr netip.Addr
var err error
// Parse the non-self address first so we know the family for self resolution.
if !srcSelf {
if srcAddr, err = parseAddr(src); err != nil {
return netip.Addr{}, netip.Addr{}, fmt.Errorf("invalid source IP: %w", err)
}
}
if !dstSelf {
if dstAddr, err = parseAddr(dst); err != nil {
return netip.Addr{}, netip.Addr{}, fmt.Errorf("invalid destination IP: %w", err)
}
}
// Determine the peer address to pick the right self address.
peer := srcAddr
if srcSelf {
peer = dstAddr
}
if srcSelf {
srcAddr = selfAddr(engine, peer)
}
if dstSelf {
dstAddr = selfAddr(engine, peer)
}
return srcAddr, dstAddr, nil
}
func selfAddr(engine *internal.Engine, peer netip.Addr) netip.Addr {
if peer.Is6() {
return engine.GetWgV6Addr()
}
return engine.GetWgAddr()
}
func parseAddr(addr string) (netip.Addr, error) {
a, err := netip.ParseAddr(addr)
if err != nil {
return netip.Addr{}, err
}
return a.Unmap(), nil
}