Add ping proxy support
This commit is contained in:
parent
4a56d47035
commit
933bd2b2d5
16 changed files with 542 additions and 19 deletions
207
stack_gvisor_icmp.go
Normal file
207
stack_gvisor_icmp.go
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
//go:build with_gvisor
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv4"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv6"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
type ICMPForwarder struct {
|
||||
ctx context.Context
|
||||
stack *stack.Stack
|
||||
handler Handler
|
||||
directNat *RouteMapping
|
||||
}
|
||||
|
||||
func NewICMPForwarder(ctx context.Context, stack *stack.Stack, handler Handler, timeout time.Duration) *ICMPForwarder {
|
||||
return &ICMPForwarder{
|
||||
ctx: ctx,
|
||||
stack: stack,
|
||||
handler: handler,
|
||||
directNat: NewRouteMapping(timeout),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *ICMPForwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
||||
if pkt.NetworkProtocolNumber == header.IPv4ProtocolNumber {
|
||||
ipHdr := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
icmpHdr := header.ICMPv4(pkt.TransportHeader().Slice())
|
||||
if icmpHdr.Type() != header.ICMPv4Echo || icmpHdr.Code() != 0 {
|
||||
return false
|
||||
}
|
||||
sourceAddr := M.AddrFromIP(ipHdr.SourceAddressSlice())
|
||||
destinationAddr := M.AddrFromIP(ipHdr.DestinationAddressSlice())
|
||||
action, err := f.directNat.Lookup(DirectRouteSession{Source: sourceAddr, Destination: destinationAddr}, func() (DirectRouteDestination, error) {
|
||||
return f.handler.PrepareConnection(
|
||||
N.NetworkICMPv4,
|
||||
M.SocksaddrFrom(sourceAddr, 0),
|
||||
M.SocksaddrFrom(destinationAddr, 0),
|
||||
&ICMPBackWriter{
|
||||
stack: f.stack,
|
||||
packet: pkt,
|
||||
source: ipHdr.SourceAddress(),
|
||||
sourceNetwork: header.IPv4ProtocolNumber,
|
||||
},
|
||||
)
|
||||
})
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
if action != nil {
|
||||
// TODO: handle error
|
||||
pkt.IncRef()
|
||||
_ = action.WritePacketBuffer(pkt)
|
||||
return true
|
||||
}
|
||||
icmpHdr.SetType(header.ICMPv4EchoReply)
|
||||
sourceAddress := ipHdr.SourceAddress()
|
||||
ipHdr.SetSourceAddress(ipHdr.DestinationAddress())
|
||||
ipHdr.SetDestinationAddress(sourceAddress)
|
||||
icmpHdr.SetChecksum(0)
|
||||
icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr[:header.ICMPv4MinimumSize], pkt.Data().Checksum()))
|
||||
ipHdr.SetChecksum(0)
|
||||
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
||||
outgoingEP, gErr := f.stack.GetNetworkEndpoint(DefaultNIC, header.IPv4ProtocolNumber)
|
||||
if gErr != nil {
|
||||
// TODO: log error
|
||||
return true
|
||||
}
|
||||
route, gErr := f.stack.FindRoute(
|
||||
DefaultNIC,
|
||||
id.LocalAddress,
|
||||
id.RemoteAddress,
|
||||
header.IPv6ProtocolNumber,
|
||||
false,
|
||||
)
|
||||
if gErr != nil {
|
||||
// TODO: log error
|
||||
return true
|
||||
}
|
||||
defer route.Release()
|
||||
outgoingEP.(ipv4.ExportedEndpoint).WritePacketDirect(route, pkt)
|
||||
return true
|
||||
} else {
|
||||
ipHdr := header.IPv6(pkt.NetworkHeader().Slice())
|
||||
icmpHdr := header.ICMPv6(pkt.TransportHeader().Slice())
|
||||
if icmpHdr.Type() != header.ICMPv6EchoRequest || icmpHdr.Code() != 0 {
|
||||
return false
|
||||
}
|
||||
sourceAddr := M.AddrFromIP(ipHdr.SourceAddressSlice())
|
||||
destinationAddr := M.AddrFromIP(ipHdr.DestinationAddressSlice())
|
||||
action, err := f.directNat.Lookup(DirectRouteSession{Source: sourceAddr, Destination: destinationAddr}, func() (DirectRouteDestination, error) {
|
||||
return f.handler.PrepareConnection(
|
||||
N.NetworkICMPv6,
|
||||
M.SocksaddrFrom(sourceAddr, 0),
|
||||
M.SocksaddrFrom(destinationAddr, 0),
|
||||
&ICMPBackWriter{
|
||||
stack: f.stack,
|
||||
packet: pkt,
|
||||
source: ipHdr.SourceAddress(),
|
||||
sourceNetwork: header.IPv6ProtocolNumber,
|
||||
},
|
||||
)
|
||||
})
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
if action != nil {
|
||||
// TODO: handle error
|
||||
pkt.IncRef()
|
||||
_ = action.WritePacketBuffer(pkt)
|
||||
return true
|
||||
}
|
||||
icmpHdr.SetType(header.ICMPv6EchoReply)
|
||||
sourceAddress := ipHdr.SourceAddress()
|
||||
ipHdr.SetSourceAddress(ipHdr.DestinationAddress())
|
||||
ipHdr.SetDestinationAddress(sourceAddress)
|
||||
icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmpHdr,
|
||||
Src: ipHdr.SourceAddress(),
|
||||
Dst: ipHdr.DestinationAddress(),
|
||||
}))
|
||||
outgoingEP, gErr := f.stack.GetNetworkEndpoint(DefaultNIC, header.IPv4ProtocolNumber)
|
||||
if gErr != nil {
|
||||
// TODO: log error
|
||||
return true
|
||||
}
|
||||
route, gErr := f.stack.FindRoute(
|
||||
DefaultNIC,
|
||||
id.LocalAddress,
|
||||
id.RemoteAddress,
|
||||
header.IPv6ProtocolNumber,
|
||||
false,
|
||||
)
|
||||
if gErr != nil {
|
||||
// TODO: log error
|
||||
return true
|
||||
}
|
||||
defer route.Release()
|
||||
outgoingEP.(ipv6.ExportedEndpoint).WritePacketDirect(route, pkt)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
type ICMPBackWriter struct {
|
||||
access sync.Mutex
|
||||
stack *stack.Stack
|
||||
packet *stack.PacketBuffer
|
||||
source tcpip.Address
|
||||
sourceNetwork tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
func (w *ICMPBackWriter) WritePacket(p []byte) error {
|
||||
if w.sourceNetwork == header.IPv4ProtocolNumber {
|
||||
route, err := w.stack.FindRoute(
|
||||
DefaultNIC,
|
||||
header.IPv4(p).SourceAddress(),
|
||||
w.source,
|
||||
w.sourceNetwork,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
return gonet.TranslateNetstackError(err)
|
||||
}
|
||||
defer route.Release()
|
||||
packet := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: buffer.MakeWithData(p),
|
||||
})
|
||||
defer packet.DecRef()
|
||||
err = route.WriteHeaderIncludedPacket(packet)
|
||||
if err != nil {
|
||||
return gonet.TranslateNetstackError(err)
|
||||
}
|
||||
} else {
|
||||
route, err := w.stack.FindRoute(
|
||||
DefaultNIC,
|
||||
header.IPv6(p).SourceAddress(),
|
||||
w.source,
|
||||
w.sourceNetwork,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
return gonet.TranslateNetstackError(err)
|
||||
}
|
||||
defer route.Release()
|
||||
packet := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: buffer.MakeWithData(p),
|
||||
})
|
||||
defer packet.DecRef()
|
||||
err = route.WriteHeaderIncludedPacket(packet)
|
||||
if err != nil {
|
||||
return gonet.TranslateNetstackError(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue