diff --git a/ping/destination_gvisor.go b/ping/destination_gvisor.go index 2a2d0e5..0bac803 100644 --- a/ping/destination_gvisor.go +++ b/ping/destination_gvisor.go @@ -27,7 +27,7 @@ type GVisorDestination struct { logger logger.ContextLogger endpoint tcpip.Endpoint conn *gonet.TCPConn - rewriter *Rewriter + rewriter *SourceRewriter timeout time.Duration } @@ -76,7 +76,7 @@ func ConnectGVisor( return nil, gonet.TranslateNetstackError(gErr) } endpoint.SocketOptions().SetHeaderIncluded(true) - rewriter := NewRewriter(ctx, logger, bindAddress4, bindAddress6) + rewriter := NewSourceRewriter(ctx, logger, bindAddress4, bindAddress6) rewriter.CreateSession(tun.DirectRouteSession{Source: sourceAddress, Destination: destinationAddress}, routeContext) destination := &GVisorDestination{ ctx: ctx, diff --git a/ping/destination_rewriter.go b/ping/destination_rewriter.go new file mode 100644 index 0000000..a61e155 --- /dev/null +++ b/ping/destination_rewriter.go @@ -0,0 +1,79 @@ +package ping + +import ( + "net/netip" + + "github.com/sagernet/sing-tun" + "github.com/sagernet/sing-tun/internal/gtcpip/header" + "github.com/sagernet/sing/common/buf" +) + +type DestinationWriter struct { + tun.DirectRouteDestination + destination netip.Addr +} + +func NewDestinationWriter(routeDestination tun.DirectRouteDestination, destination netip.Addr) *DestinationWriter { + return &DestinationWriter{routeDestination, destination} +} + +func (w *DestinationWriter) WritePacket(packet *buf.Buffer) error { + var ipHdr header.Network + switch header.IPVersion(packet.Bytes()) { + case header.IPv4Version: + ipHdr = header.IPv4(packet.Bytes()) + case header.IPv6Version: + ipHdr = header.IPv6(packet.Bytes()) + default: + return w.DirectRouteDestination.WritePacket(packet) + } + ipHdr.SetDestinationAddr(w.destination) + if ipHdr4, isIPv4 := ipHdr.(header.IPv4); isIPv4 { + ipHdr4.SetChecksum(^ipHdr4.CalculateChecksum()) + } + if ipHdr.TransportProtocol() == header.ICMPv6ProtocolNumber { + icmpHdr := header.ICMPv6(ipHdr.Payload()) + icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ + Header: icmpHdr, + Src: ipHdr.SourceAddressSlice(), + Dst: ipHdr.DestinationAddressSlice(), + })) + } + return w.DirectRouteDestination.WritePacket(packet) +} + +type ContextDestinationWriter struct { + tun.DirectRouteContext + destination netip.Addr +} + +func NewContextDestinationWriter(context tun.DirectRouteContext, destination netip.Addr) *ContextDestinationWriter { + return &ContextDestinationWriter{ + context, destination, + } +} + +func (w *ContextDestinationWriter) WritePacket(packet []byte) error { + var ipHdr header.Network + switch header.IPVersion(packet) { + case header.IPv4Version: + ipHdr = header.IPv4(packet) + case header.IPv6Version: + ipHdr = header.IPv6(packet) + default: + return w.DirectRouteContext.WritePacket(packet) + } + ipHdr.SetSourceAddr(w.destination) + if ipHdr4, isIPv4 := ipHdr.(header.IPv4); isIPv4 { + ipHdr4.SetChecksum(^ipHdr4.CalculateChecksum()) + } + if ipHdr.TransportProtocol() == header.ICMPv6ProtocolNumber { + icmpHdr := header.ICMPv6(ipHdr.Payload()) + icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ + Header: icmpHdr, + Src: ipHdr.SourceAddressSlice(), + Dst: ipHdr.DestinationAddressSlice(), + })) + } + return w.DirectRouteContext.WritePacket(packet) +} diff --git a/ping/rewriter.go b/ping/source_rewriter.go similarity index 89% rename from ping/rewriter.go rename to ping/source_rewriter.go index 4d0dcd8..480c6a7 100644 --- a/ping/rewriter.go +++ b/ping/source_rewriter.go @@ -10,7 +10,7 @@ import ( "github.com/sagernet/sing/common/logger" ) -type Rewriter struct { +type SourceRewriter struct { ctx context.Context logger logger.ContextLogger access sync.RWMutex @@ -20,8 +20,8 @@ type Rewriter struct { inet6Address netip.Addr } -func NewRewriter(ctx context.Context, logger logger.ContextLogger, inet4Address netip.Addr, inet6Address netip.Addr) *Rewriter { - return &Rewriter{ +func NewSourceRewriter(ctx context.Context, logger logger.ContextLogger, inet4Address netip.Addr, inet6Address netip.Addr) *SourceRewriter { + return &SourceRewriter{ ctx: ctx, logger: logger, sessions: make(map[tun.DirectRouteSession]tun.DirectRouteContext), @@ -31,19 +31,19 @@ func NewRewriter(ctx context.Context, logger logger.ContextLogger, inet4Address } } -func (m *Rewriter) CreateSession(session tun.DirectRouteSession, context tun.DirectRouteContext) { +func (m *SourceRewriter) CreateSession(session tun.DirectRouteSession, context tun.DirectRouteContext) { m.access.Lock() m.sessions[session] = context m.access.Unlock() } -func (m *Rewriter) DeleteSession(session tun.DirectRouteSession) { +func (m *SourceRewriter) DeleteSession(session tun.DirectRouteSession) { m.access.Lock() delete(m.sessions, session) m.access.Unlock() } -func (m *Rewriter) RewritePacket(packet []byte) { +func (m *SourceRewriter) RewritePacket(packet []byte) { var ipHdr header.Network var bindAddr netip.Addr switch header.IPVersion(packet) { @@ -82,7 +82,7 @@ func (m *Rewriter) RewritePacket(packet []byte) { } } -func (m *Rewriter) WriteBack(packet []byte) (bool, error) { +func (m *SourceRewriter) WriteBack(packet []byte) (bool, error) { var ipHdr header.Network var routeSession tun.DirectRouteSession switch header.IPVersion(packet) {