ping: Add gVisor destination
This commit is contained in:
parent
12c9fb6a5d
commit
86d96064d5
9 changed files with 332 additions and 221 deletions
113
ping/destination_gvisor.go
Normal file
113
ping/destination_gvisor.go
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
//go:build with_gvisor
|
||||
|
||||
package ping
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
|
||||
"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/stack"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
var _ tun.DirectRouteDestination = (*GVisorDestination)(nil)
|
||||
|
||||
type GVisorDestination struct {
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
conn *gonet.TCPConn
|
||||
rewriter *Rewriter
|
||||
}
|
||||
|
||||
func ConnectGVisor(
|
||||
ctx context.Context, logger logger.ContextLogger,
|
||||
sourceAddress, destinationAddress netip.Addr,
|
||||
routeContext tun.DirectRouteContext,
|
||||
stack *stack.Stack,
|
||||
bindAddress4, bindAddress6 netip.Addr,
|
||||
) (*GVisorDestination, error) {
|
||||
var (
|
||||
bindAddress tcpip.Address
|
||||
wq waiter.Queue
|
||||
endpoint tcpip.Endpoint
|
||||
gErr tcpip.Error
|
||||
)
|
||||
if !destinationAddress.Is6() {
|
||||
if !bindAddress4.IsValid() {
|
||||
return nil, E.New("missing IPv4 interface address")
|
||||
}
|
||||
bindAddress = tun.AddressFromAddr(bindAddress4)
|
||||
endpoint, gErr = stack.NewRawEndpoint(header.ICMPv4ProtocolNumber, header.IPv4ProtocolNumber, &wq, true)
|
||||
} else {
|
||||
if !bindAddress6.IsValid() {
|
||||
return nil, E.New("missing IPv6 interface address")
|
||||
}
|
||||
bindAddress = tun.AddressFromAddr(bindAddress6)
|
||||
endpoint, gErr = stack.NewRawEndpoint(header.ICMPv6ProtocolNumber, header.IPv6ProtocolNumber, &wq, true)
|
||||
}
|
||||
if gErr != nil {
|
||||
return nil, gonet.TranslateNetstackError(gErr)
|
||||
}
|
||||
gErr = endpoint.Bind(tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: bindAddress,
|
||||
})
|
||||
if gErr != nil {
|
||||
return nil, gonet.TranslateNetstackError(gErr)
|
||||
}
|
||||
gErr = endpoint.Connect(tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tun.AddressFromAddr(destinationAddress),
|
||||
})
|
||||
if gErr != nil {
|
||||
return nil, gonet.TranslateNetstackError(gErr)
|
||||
}
|
||||
endpoint.SocketOptions().SetHeaderIncluded(true)
|
||||
rewriter := NewRewriter(bindAddress4, bindAddress6)
|
||||
rewriter.CreateSession(tun.DirectRouteSession{Source: sourceAddress, Destination: destinationAddress}, routeContext)
|
||||
destination := &GVisorDestination{
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
conn: gonet.NewTCPConn(&wq, endpoint),
|
||||
rewriter: rewriter,
|
||||
}
|
||||
go destination.loopRead()
|
||||
return destination, nil
|
||||
}
|
||||
|
||||
func (d *GVisorDestination) loopRead() {
|
||||
for {
|
||||
buffer := buf.NewPacket()
|
||||
n, err := d.conn.Read(buffer.FreeBytes())
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
if !E.IsClosed(err) {
|
||||
d.logger.ErrorContext(d.ctx, E.Cause(err, "receive ICMP echo reply"))
|
||||
}
|
||||
return
|
||||
}
|
||||
buffer.Truncate(n)
|
||||
_, err = d.rewriter.WriteBack(buffer.Bytes())
|
||||
if err != nil {
|
||||
d.logger.ErrorContext(d.ctx, E.Cause(err, "write ICMP echo reply"))
|
||||
}
|
||||
buffer.Release()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *GVisorDestination) WritePacket(packet *buf.Buffer) error {
|
||||
d.rewriter.RewritePacket(packet.Bytes())
|
||||
return common.Error(d.conn.Write(packet.Bytes()))
|
||||
}
|
||||
|
||||
func (d *GVisorDestination) Close() error {
|
||||
return d.conn.Close()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue