Update gVisor to release-20230605.0-21-g457c1c36d

This commit is contained in:
世界 2023-05-20 12:09:41 +08:00
parent b02f252916
commit e881f21013
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
20 changed files with 71 additions and 550 deletions

6
go.mod
View file

@ -9,9 +9,9 @@ require (
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
github.com/sagernet/sing v0.2.4
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9
golang.org/x/net v0.9.0
golang.org/x/sys v0.7.0
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523
golang.org/x/net v0.10.0
golang.org/x/sys v0.8.0
gvisor.dev/gvisor v0.0.0-20230609002524-f143e1baf0bb
)
require (

12
go.sum
View file

@ -15,15 +15,15 @@ github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523 h1:zUQYeyyPLnSR6yMvLSOmLH37xDWCZ7BqlpE69fE5K3Q=
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523/go.mod h1:pzr6sy8gDLfVmDAg8OYrlKvGEHw5C3PGTiBXBTCx76Q=
gvisor.dev/gvisor v0.0.0-20230609002524-f143e1baf0bb h1:A5Zr25mHIiXEZUjN92wAopvMv2XL4jTbl2/+9D4ATgE=
gvisor.dev/gvisor v0.0.0-20230609002524-f143e1baf0bb/go.mod h1:sQuqOkxbfJq/GS2uSnqHphtXclHyk/ZrAGhZBxxsq6g=

102
gvisor.go
View file

@ -4,8 +4,7 @@ package tun
import (
"context"
"net"
"syscall"
"net/netip"
"time"
"github.com/sagernet/sing/common/bufio"
@ -36,12 +35,10 @@ type GVisor struct {
tunMtu uint32
endpointIndependentNat bool
udpTimeout int64
router Router
handler Handler
logger logger.Logger
stack *stack.Stack
endpoint stack.LinkEndpoint
routeMapping *RouteMapping
}
type GVisorTun interface {
@ -63,13 +60,9 @@ func NewGVisor(
tunMtu: options.MTU,
endpointIndependentNat: options.EndpointIndependentNat,
udpTimeout: options.UDPTimeout,
router: options.Router,
handler: options.Handler,
logger: options.Logger,
}
if gStack.router != nil {
gStack.routeMapping = NewRouteMapping(options.UDPTimeout)
}
return gStack, nil
}
@ -155,44 +148,7 @@ func (t *GVisor) Start() error {
}
}()
})
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, func(id stack.TransportEndpointID, buffer stack.PacketBufferPtr) bool {
if t.router != nil {
var routeSession RouteSession
routeSession.Network = syscall.IPPROTO_TCP
var ipHdr header.Network
if buffer.NetworkProtocolNumber == header.IPv4ProtocolNumber {
routeSession.IPVersion = 4
ipHdr = header.IPv4(buffer.NetworkHeader().Slice())
} else {
routeSession.IPVersion = 6
ipHdr = header.IPv6(buffer.NetworkHeader().Slice())
}
tcpHdr := header.TCP(buffer.TransportHeader().Slice())
routeSession.Source = M.AddrPortFrom(net.IP(ipHdr.SourceAddress()), tcpHdr.SourcePort())
routeSession.Destination = M.AddrPortFrom(net.IP(ipHdr.DestinationAddress()), tcpHdr.DestinationPort())
action := t.routeMapping.Lookup(routeSession, func() RouteAction {
if routeSession.IPVersion == 4 {
return t.router.RouteConnection(routeSession, &systemTCPDirectPacketWriter4{t.tun, routeSession.Source})
} else {
return t.router.RouteConnection(routeSession, &systemTCPDirectPacketWriter6{t.tun, routeSession.Source})
}
})
switch actionType := action.(type) {
case *ActionBlock:
// TODO: send icmp unreachable
return true
case *ActionDirect:
buffer.IncRef()
err = actionType.WritePacketBuffer(buffer)
if err != nil {
t.logger.Trace("route gvisor tcp packet: ", err)
}
return true
}
}
return tcpForwarder.HandlePacket(id, buffer)
})
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
if !t.endpointIndependentNat {
udpForwarder := udp.NewForwarder(ipStack, func(request *udp.ForwarderRequest) {
var wq waiter.Queue
@ -218,43 +174,7 @@ func (t *GVisor) Start() error {
}
}()
})
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, func(id stack.TransportEndpointID, buffer stack.PacketBufferPtr) bool {
if t.router != nil {
var routeSession RouteSession
routeSession.Network = syscall.IPPROTO_UDP
var ipHdr header.Network
if buffer.NetworkProtocolNumber == header.IPv4ProtocolNumber {
routeSession.IPVersion = 4
ipHdr = header.IPv4(buffer.NetworkHeader().Slice())
} else {
routeSession.IPVersion = 6
ipHdr = header.IPv6(buffer.NetworkHeader().Slice())
}
udpHdr := header.UDP(buffer.TransportHeader().Slice())
routeSession.Source = M.AddrPortFrom(net.IP(ipHdr.SourceAddress()), udpHdr.SourcePort())
routeSession.Destination = M.AddrPortFrom(net.IP(ipHdr.DestinationAddress()), udpHdr.DestinationPort())
action := t.routeMapping.Lookup(routeSession, func() RouteAction {
if routeSession.IPVersion == 4 {
return t.router.RouteConnection(routeSession, &systemUDPDirectPacketWriter4{t.tun, routeSession.Source})
} else {
return t.router.RouteConnection(routeSession, &systemUDPDirectPacketWriter6{t.tun, routeSession.Source})
}
})
switch actionType := action.(type) {
case *ActionBlock:
// TODO: send icmp unreachable
return true
case *ActionDirect:
buffer.IncRef()
err = actionType.WritePacketBuffer(buffer)
if err != nil {
t.logger.Trace("route gvisor udp packet: ", err)
}
return true
}
}
return udpForwarder.HandlePacket(id, buffer)
})
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
} else {
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, NewUDPForwarder(t.ctx, ipStack, t.handler, t.udpTimeout).HandlePacket)
}
@ -272,3 +192,19 @@ func (t *GVisor) Close() error {
}
return nil
}
func AddressFromAddr(destination netip.Addr) tcpip.Address {
if destination.Is6() {
return tcpip.AddrFrom16(destination.As16())
} else {
return tcpip.AddrFrom4(destination.As4())
}
}
func AddrFromAddress(address tcpip.Address) netip.Addr {
if address.Len() == 16 {
return netip.AddrFrom16(address.As16())
} else {
return netip.AddrFrom4(address.As4())
}
}

View file

@ -5,7 +5,6 @@ package tun
import (
"context"
"math"
"net"
"net/netip"
"github.com/sagernet/sing/common/buf"
@ -13,7 +12,7 @@ import (
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/udpnat"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
"gvisor.dev/gvisor/pkg/tcpip/header"
@ -36,8 +35,8 @@ func NewUDPForwarder(ctx context.Context, stack *stack.Stack, handler Handler, u
func (f *UDPForwarder) HandlePacket(id stack.TransportEndpointID, pkt stack.PacketBufferPtr) bool {
var upstreamMetadata M.Metadata
upstreamMetadata.Source = M.SocksaddrFrom(M.AddrFromIP(net.IP(id.RemoteAddress)), id.RemotePort)
upstreamMetadata.Destination = M.SocksaddrFrom(M.AddrFromIP(net.IP(id.LocalAddress)), id.LocalPort)
upstreamMetadata.Source = M.SocksaddrFrom(AddrFromAddress(id.RemoteAddress), id.RemotePort)
upstreamMetadata.Destination = M.SocksaddrFrom(AddrFromAddress(id.LocalAddress), id.LocalPort)
var netProto tcpip.NetworkProtocolNumber
if upstreamMetadata.Source.IsIPv4() {
netProto = header.IPv4ProtocolNumber
@ -63,12 +62,12 @@ type UDPBackWriter struct {
sourceNetwork tcpip.NetworkProtocolNumber
}
func (w *UDPBackWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
func (w *UDPBackWriter) WritePacket(packetBuffer *buf.Buffer, destination M.Socksaddr) error {
defer packetBuffer.Release()
route, err := w.stack.FindRoute(
defaultNIC,
tcpip.Address(destination.Addr.AsSlice()),
AddressFromAddr(destination.Addr),
w.source,
w.sourceNetwork,
false,
@ -80,7 +79,7 @@ func (w *UDPBackWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr)
packet := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: header.UDPMinimumSize + int(route.MaxHeaderLength()),
Payload: bufferv2.MakeWithData(buffer.Bytes()),
Payload: buffer.MakeWithData(packetBuffer.Bytes()),
})
defer packet.DecRef()

View file

@ -1,3 +1,3 @@
# fdbased
Version: release-20230417.0
Version: release-20230605.0-21-g457c1c36d

View file

@ -45,7 +45,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
@ -545,7 +545,7 @@ func (e *endpoint) writePacket(pkt stack.PacketBufferPtr) tcpip.Error {
vnetHdr.csumStart = header.EthernetMinimumSize + pkt.GSOOptions.L3HdrLen
vnetHdr.csumOffset = pkt.GSOOptions.CsumOffset
}
if pkt.GSOOptions.Type != stack.GSONone && uint16(pkt.Data().Size()) > pkt.GSOOptions.MSS {
if uint16(pkt.Data().Size()) > pkt.GSOOptions.MSS {
switch pkt.GSOOptions.Type {
case stack.GSOTCPv4:
vnetHdr.gsoType = _VIRTIO_NET_HDR_GSO_TCPV4
@ -732,7 +732,7 @@ func (e *endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error)
}
// InjectOutbound implements stack.InjectableEndpoint.InjectOutbound.
func (e *endpoint) InjectOutbound(dest tcpip.Address, packet *bufferv2.View) tcpip.Error {
func (e *endpoint) InjectOutbound(dest tcpip.Address, packet *buffer.View) tcpip.Error {
return rawfile.NonBlockingWrite(e.fds[0].fd, packet.AsSlice())
}
@ -756,7 +756,7 @@ func (e *endpoint) GSOMaxSize() uint32 {
return e.gsoMaxSize
}
// SupportsHWGSO implements stack.GSOEndpoint.
// SupportedGSO implements stack.GSOEndpoint.
func (e *endpoint) SupportedGSO() stack.SupportedGSO {
return e.gsoKind
}

View file

@ -24,7 +24,7 @@ import (
"github.com/sagernet/sing-tun/internal/fdbased/stopfd"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
@ -135,7 +135,7 @@ type packetMMapDispatcher struct {
func (*packetMMapDispatcher) release() {}
func (d *packetMMapDispatcher) readMMappedPacket() (*bufferv2.View, bool, tcpip.Error) {
func (d *packetMMapDispatcher) readMMappedPacket() (*buffer.View, bool, tcpip.Error) {
hdr := tPacketHdr(d.ringBuffer[d.ringOffset*tpFrameSize:])
for hdr.tpStatus()&tpStatusUser == 0 {
stopped, errno := rawfile.BlockingPollUntilStopped(d.EFD, d.fd, unix.POLLIN|unix.POLLERR)
@ -159,7 +159,7 @@ func (d *packetMMapDispatcher) readMMappedPacket() (*bufferv2.View, bool, tcpip.
}
// Copy out the packet from the mmapped frame to a locally owned buffer.
pkt := bufferv2.NewView(int(hdr.tpSnapLen()))
pkt := buffer.NewView(int(hdr.tpSnapLen()))
pkt.Write(hdr.Payload())
// Release packet to kernel.
hdr.setTPStatus(tpStatusKernel)
@ -191,7 +191,7 @@ func (d *packetMMapDispatcher) dispatch() (bool, tcpip.Error) {
}
pbuf := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: bufferv2.MakeWithView(pkt),
Payload: buffer.MakeWithView(pkt),
})
defer pbuf.DecRef()
if d.e.hdrSize > 0 {

View file

@ -21,7 +21,7 @@ import (
"github.com/sagernet/sing-tun/internal/fdbased/stopfd"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
@ -35,7 +35,7 @@ type iovecBuffer struct {
// buffer is the actual buffer that holds the packet contents. Some contents
// are reused across calls to pullBuffer if number of requested bytes is
// smaller than the number of bytes allocated in the buffer.
views []*bufferv2.View
views []*buffer.View
// iovecs are initialized with base pointers/len of the corresponding
// entries in the views defined above, except when GSO is enabled
@ -59,7 +59,7 @@ type iovecBuffer struct {
func newIovecBuffer(sizes []int, skipsVnetHdr bool) *iovecBuffer {
b := &iovecBuffer{
views: make([]*bufferv2.View, len(sizes)),
views: make([]*buffer.View, len(sizes)),
sizes: sizes,
skipsVnetHdr: skipsVnetHdr,
}
@ -87,7 +87,7 @@ func (b *iovecBuffer) nextIovecs() []unix.Iovec {
if b.views[i] != nil {
break
}
v := bufferv2.NewViewSize(b.sizes[i])
v := buffer.NewViewSize(b.sizes[i])
b.views[i] = v
b.iovecs[i+vnetHdrOff] = unix.Iovec{Base: v.BasePtr()}
b.iovecs[i+vnetHdrOff].SetLen(v.Size())
@ -100,14 +100,14 @@ func (b *iovecBuffer) nextIovecs() []unix.Iovec {
// that holds the storage, and updates pulledIndex to indicate which part
// of b.buffer's storage must be reallocated during the next call to
// nextIovecs.
func (b *iovecBuffer) pullBuffer(n int) bufferv2.Buffer {
var views []*bufferv2.View
func (b *iovecBuffer) pullBuffer(n int) buffer.Buffer {
var views []*buffer.View
c := 0
if b.skipsVnetHdr {
c += virtioNetHdrSize
if c >= n {
// Nothing in the packet.
return bufferv2.Buffer{}
return buffer.Buffer{}
}
}
// Remove the used views from the buffer.
@ -126,7 +126,7 @@ func (b *iovecBuffer) pullBuffer(n int) bufferv2.Buffer {
// Exclude the size of the vnet header.
n -= virtioNetHdrSize
}
pulled := bufferv2.Buffer{}
pulled := buffer.Buffer{}
for _, v := range views {
pulled.Append(v)
}

View file

@ -1,6 +0,0 @@
// automatically generated by stateify.
//go:build (linux && amd64) || (linux && arm64)
// +build linux,amd64 linux,arm64
package stopfd

View file

@ -1,92 +0,0 @@
package tun
import (
"net/netip"
E "github.com/sagernet/sing/common/exceptions"
)
type ActionType = uint8
const (
ActionTypeUnknown ActionType = iota
ActionTypeReturn
ActionTypeBlock
ActionTypeDirect
)
func ParseActionType(action string) (ActionType, error) {
switch action {
case "return":
return ActionTypeReturn, nil
case "block":
return ActionTypeBlock, nil
case "direct":
return ActionTypeDirect, nil
default:
return 0, E.New("unknown action: ", action)
}
}
func ActionTypeName(actionType ActionType) (string, error) {
switch actionType {
case ActionTypeUnknown:
return "", nil
case ActionTypeReturn:
return "return", nil
case ActionTypeBlock:
return "block", nil
case ActionTypeDirect:
return "direct", nil
default:
return "", E.New("unknown action: ", actionType)
}
}
type RouteSession struct {
IPVersion uint8
Network uint8
Source netip.AddrPort
Destination netip.AddrPort
}
type RouteContext interface {
WritePacket(packet []byte) error
}
type Router interface {
RouteConnection(session RouteSession, context RouteContext) RouteAction
}
type RouteAction interface {
ActionType() ActionType
Timeout() bool
}
type ActionReturn struct{}
func (r *ActionReturn) ActionType() ActionType {
return ActionTypeReturn
}
func (r *ActionReturn) Timeout() bool {
return false
}
type ActionBlock struct{}
func (r *ActionBlock) ActionType() ActionType {
return ActionTypeBlock
}
func (r *ActionBlock) Timeout() bool {
return false
}
type ActionDirect struct {
DirectDestination
}
func (r *ActionDirect) ActionType() ActionType {
return ActionTypeDirect
}

View file

@ -1,16 +0,0 @@
//go:build with_gvisor
package tun
import (
"github.com/sagernet/sing/common/buf"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
type DirectDestination interface {
WritePacket(buffer *buf.Buffer) error
WritePacketBuffer(buffer stack.PacketBufferPtr) error
Close() error
Timeout() bool
}

View file

@ -1,32 +0,0 @@
package tun
import (
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/cache"
)
type RouteMapping struct {
status *cache.LruCache[RouteSession, RouteAction]
}
func NewRouteMapping(maxAge int64) *RouteMapping {
return &RouteMapping{
status: cache.New(
cache.WithAge[RouteSession, RouteAction](maxAge),
cache.WithUpdateAgeOnGet[RouteSession, RouteAction](),
cache.WithEvict[RouteSession, RouteAction](func(key RouteSession, conn RouteAction) {
common.Close(conn)
}),
),
}
}
func (m *RouteMapping) Lookup(session RouteSession, constructor func() RouteAction) RouteAction {
action, _ := m.status.LoadOrStore(session, constructor)
if action.Timeout() {
common.Close(action)
action = constructor()
m.status.Store(session, action)
}
return action
}

View file

@ -1,119 +0,0 @@
package tun
import (
"net/netip"
"sync"
"github.com/sagernet/sing-tun/internal/clashtcpip"
)
type NatMapping struct {
access sync.RWMutex
sessions map[RouteSession]RouteContext
ipRewrite bool
}
func NewNatMapping(ipRewrite bool) *NatMapping {
return &NatMapping{
sessions: make(map[RouteSession]RouteContext),
ipRewrite: ipRewrite,
}
}
func (m *NatMapping) CreateSession(session RouteSession, context RouteContext) {
if m.ipRewrite {
session.Source = netip.AddrPort{}
}
m.access.Lock()
m.sessions[session] = context
m.access.Unlock()
}
func (m *NatMapping) DeleteSession(session RouteSession) {
if m.ipRewrite {
session.Source = netip.AddrPort{}
}
m.access.Lock()
delete(m.sessions, session)
m.access.Unlock()
}
func (m *NatMapping) WritePacket(packet []byte) (bool, error) {
var routeSession RouteSession
var ipHdr clashtcpip.IP
switch ipVersion := packet[0] >> 4; ipVersion {
case 4:
routeSession.IPVersion = 4
ipHdr = clashtcpip.IPv4Packet(packet)
case 6:
routeSession.IPVersion = 6
ipHdr = clashtcpip.IPv6Packet(packet)
default:
return false, nil
}
routeSession.Network = ipHdr.Protocol()
switch routeSession.Network {
case clashtcpip.TCP:
tcpHdr := clashtcpip.TCPPacket(ipHdr.Payload())
routeSession.Destination = netip.AddrPortFrom(ipHdr.SourceIP(), tcpHdr.SourcePort())
if !m.ipRewrite {
routeSession.Source = netip.AddrPortFrom(ipHdr.DestinationIP(), tcpHdr.DestinationPort())
}
case clashtcpip.UDP:
udpHdr := clashtcpip.UDPPacket(ipHdr.Payload())
routeSession.Destination = netip.AddrPortFrom(ipHdr.SourceIP(), udpHdr.SourcePort())
if !m.ipRewrite {
routeSession.Source = netip.AddrPortFrom(ipHdr.DestinationIP(), udpHdr.DestinationPort())
}
default:
routeSession.Destination = netip.AddrPortFrom(ipHdr.SourceIP(), 0)
if !m.ipRewrite {
routeSession.Source = netip.AddrPortFrom(ipHdr.DestinationIP(), 0)
}
}
m.access.RLock()
context, loaded := m.sessions[routeSession]
m.access.RUnlock()
if !loaded {
return false, nil
}
return true, context.WritePacket(packet)
}
type NatWriter struct {
inet4Address netip.Addr
inet6Address netip.Addr
}
func NewNatWriter(inet4Address netip.Addr, inet6Address netip.Addr) *NatWriter {
return &NatWriter{
inet4Address: inet4Address,
inet6Address: inet6Address,
}
}
func (w *NatWriter) RewritePacket(packet []byte) {
var ipHdr clashtcpip.IP
var bindAddr netip.Addr
switch ipVersion := packet[0] >> 4; ipVersion {
case 4:
ipHdr = clashtcpip.IPv4Packet(packet)
bindAddr = w.inet4Address
case 6:
ipHdr = clashtcpip.IPv6Packet(packet)
bindAddr = w.inet6Address
default:
return
}
ipHdr.SetSourceIP(bindAddr)
switch ipHdr.Protocol() {
case clashtcpip.TCP:
tcpHdr := clashtcpip.TCPPacket(ipHdr.Payload())
tcpHdr.ResetChecksum(ipHdr.PseudoSum())
case clashtcpip.UDP:
udpHdr := clashtcpip.UDPPacket(ipHdr.Payload())
udpHdr.ResetChecksum(ipHdr.PseudoSum())
default:
}
ipHdr.ResetChecksum()
}

View file

@ -1,41 +0,0 @@
//go:build with_gvisor
package tun
import (
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
func (w *NatWriter) RewritePacketBuffer(packetBuffer stack.PacketBufferPtr) {
var bindAddr tcpip.Address
if packetBuffer.NetworkProtocolNumber == header.IPv4ProtocolNumber {
bindAddr = tcpip.Address(w.inet4Address.AsSlice())
} else {
bindAddr = tcpip.Address(w.inet6Address.AsSlice())
}
var ipHdr header.Network
switch packetBuffer.NetworkProtocolNumber {
case header.IPv4ProtocolNumber:
ipHdr = header.IPv4(packetBuffer.NetworkHeader().Slice())
case header.IPv6ProtocolNumber:
ipHdr = header.IPv6(packetBuffer.NetworkHeader().Slice())
default:
return
}
oldAddr := ipHdr.SourceAddress()
if checksumHdr, needChecksum := ipHdr.(header.ChecksummableNetwork); needChecksum {
checksumHdr.SetSourceAddressWithChecksumUpdate(bindAddr)
} else {
ipHdr.SetSourceAddress(bindAddr)
}
switch packetBuffer.TransportProtocolNumber {
case header.TCPProtocolNumber:
tcpHdr := header.TCP(packetBuffer.TransportHeader().Slice())
tcpHdr.UpdateChecksumPseudoHeaderAddress(oldAddr, bindAddr, true)
case header.UDPProtocolNumber:
udpHdr := header.UDP(packetBuffer.TransportHeader().Slice())
udpHdr.UpdateChecksumPseudoHeaderAddress(oldAddr, bindAddr, true)
}
}

View file

@ -1,11 +0,0 @@
//go:build !with_gvisor
package tun
import "github.com/sagernet/sing/common/buf"
type DirectDestination interface {
WritePacket(buffer *buf.Buffer) error
Close() error
Timeout() bool
}

View file

@ -23,7 +23,6 @@ type StackOptions struct {
Inet6Address []netip.Prefix
EndpointIndependentNat bool
UDPTimeout int64
Router Router
Handler Handler
Logger logger.Logger
ForwarderBindInterface bool

View file

@ -23,7 +23,6 @@ type System struct {
tun Tun
tunName string
mtu uint32
router Router
handler Handler
logger logger.Logger
inet4Prefixes []netip.Prefix
@ -39,7 +38,6 @@ type System struct {
tcpPort6 uint16
tcpNat *TCPNat
udpNat *udpnat.Service[netip.AddrPort]
routeMapping *RouteMapping
bindInterface bool
interfaceFinder control.InterfaceFinder
}
@ -58,7 +56,6 @@ func NewSystem(options StackOptions) (Stack, error) {
tunName: options.Name,
mtu: options.MTU,
udpTimeout: options.UDPTimeout,
router: options.Router,
handler: options.Handler,
logger: options.Logger,
inet4Prefixes: options.Inet4Address,
@ -66,9 +63,6 @@ func NewSystem(options StackOptions) (Stack, error) {
bindInterface: options.ForwarderBindInterface,
interfaceFinder: options.InterfaceFinder,
}
if stack.router != nil {
stack.routeMapping = NewRouteMapping(options.UDPTimeout)
}
if len(options.Inet4Address) > 0 {
if options.Inet4Address[0].Bits() == 32 {
return nil, E.New("need one more IPv4 address in first prefix for system stack")
@ -275,21 +269,6 @@ func (s *System) processIPv4TCP(packet clashtcpip.IPv4Packet, header clashtcpip.
packet.SetDestinationIP(session.Source.Addr())
header.SetDestinationPort(session.Source.Port())
} else {
if s.router != nil {
session := RouteSession{4, syscall.IPPROTO_TCP, source, destination}
action := s.routeMapping.Lookup(session, func() RouteAction {
return s.router.RouteConnection(session, &systemTCPDirectPacketWriter4{s.tun, source})
})
switch actionType := action.(type) {
case *ActionBlock:
// TODO: send ICMP unreachable
return nil
case *ActionDirect:
return E.Append(nil, actionType.WritePacket(buf.As(packet).ToOwned()), func(err error) error {
return E.Cause(err, "route ipv4 tcp packet")
})
}
}
natPort := s.tcpNat.Lookup(source, destination)
packet.SetSourceIP(s.inet4Address)
header.SetSourcePort(natPort)
@ -316,21 +295,6 @@ func (s *System) processIPv6TCP(packet clashtcpip.IPv6Packet, header clashtcpip.
packet.SetDestinationIP(session.Source.Addr())
header.SetDestinationPort(session.Source.Port())
} else {
if s.router != nil {
session := RouteSession{6, syscall.IPPROTO_TCP, source, destination}
action := s.routeMapping.Lookup(session, func() RouteAction {
return s.router.RouteConnection(session, &systemTCPDirectPacketWriter6{s.tun, source})
})
switch actionType := action.(type) {
case *ActionBlock:
// TODO: send RST
return nil
case *ActionDirect:
return E.Append(nil, actionType.WritePacket(buf.As(packet).ToOwned()), func(err error) error {
return E.Cause(err, "route ipv6 tcp packet")
})
}
}
natPort := s.tcpNat.Lookup(source, destination)
packet.SetSourceIP(s.inet6Address)
header.SetSourcePort(natPort)
@ -354,21 +318,6 @@ func (s *System) processIPv4UDP(packet clashtcpip.IPv4Packet, header clashtcpip.
if !destination.Addr().IsGlobalUnicast() {
return common.Error(s.tun.Write(packet))
}
if s.router != nil {
routeSession := RouteSession{4, syscall.IPPROTO_UDP, source, destination}
action := s.routeMapping.Lookup(routeSession, func() RouteAction {
return s.router.RouteConnection(routeSession, &systemUDPDirectPacketWriter4{s.tun, source})
})
switch actionType := action.(type) {
case *ActionBlock:
// TODO: send icmp unreachable
return nil
case *ActionDirect:
return E.Append(nil, actionType.WritePacket(buf.As(packet).ToOwned()), func(err error) error {
return E.Cause(err, "route ipv4 udp packet")
})
}
}
data := buf.As(header.Payload())
if data.Len() == 0 {
return nil
@ -392,21 +341,6 @@ func (s *System) processIPv6UDP(packet clashtcpip.IPv6Packet, header clashtcpip.
if !destination.Addr().IsGlobalUnicast() {
return common.Error(s.tun.Write(packet))
}
if s.router != nil {
routeSession := RouteSession{6, syscall.IPPROTO_UDP, source, destination}
action := s.routeMapping.Lookup(routeSession, func() RouteAction {
return s.router.RouteConnection(routeSession, &systemUDPDirectPacketWriter6{s.tun, source})
})
switch actionType := action.(type) {
case *ActionBlock:
// TODO: send icmp unreachable
return nil
case *ActionDirect:
return E.Append(nil, actionType.WritePacket(buf.As(packet).ToOwned()), func(err error) error {
return E.Cause(err, "route ipv6 udp packet")
})
}
}
data := buf.As(header.Payload())
if data.Len() == 0 {
return nil
@ -425,21 +359,6 @@ func (s *System) processIPv6UDP(packet clashtcpip.IPv6Packet, header clashtcpip.
}
func (s *System) processIPv4ICMP(packet clashtcpip.IPv4Packet, header clashtcpip.ICMPPacket) error {
if s.router != nil {
routeSession := RouteSession{4, clashtcpip.ICMP, netip.AddrPortFrom(packet.SourceIP(), 0), netip.AddrPortFrom(packet.DestinationIP(), 0)}
action := s.routeMapping.Lookup(routeSession, func() RouteAction {
return s.router.RouteConnection(routeSession, &systemICMPDirectPacketWriter4{s.tun, packet.SourceIP()})
})
switch actionType := action.(type) {
case *ActionBlock:
// TODO: send icmp unreachable
return nil
case *ActionDirect:
return E.Append(nil, actionType.WritePacket(buf.As(packet).ToOwned()), func(err error) error {
return E.Cause(err, "route ipv4 icmp packet")
})
}
}
if header.Type() != clashtcpip.ICMPTypePingRequest || header.Code() != 0 {
return nil
}
@ -453,21 +372,6 @@ func (s *System) processIPv4ICMP(packet clashtcpip.IPv4Packet, header clashtcpip
}
func (s *System) processIPv6ICMP(packet clashtcpip.IPv6Packet, header clashtcpip.ICMPv6Packet) error {
if s.router != nil {
routeSession := RouteSession{6, clashtcpip.ICMPv6, netip.AddrPortFrom(packet.SourceIP(), 0), netip.AddrPortFrom(packet.DestinationIP(), 0)}
action := s.routeMapping.Lookup(routeSession, func() RouteAction {
return s.router.RouteConnection(routeSession, &systemICMPDirectPacketWriter6{s.tun, packet.SourceIP()})
})
switch actionType := action.(type) {
case *ActionBlock:
// TODO: send icmp unreachable
return nil
case *ActionDirect:
return E.Append(nil, actionType.WritePacket(buf.As(packet).ToOwned()), func(err error) error {
return E.Cause(err, "route ipv6 icmp packet")
})
}
}
if header.Type() != clashtcpip.ICMPv6EchoRequest || header.Code() != 0 {
return nil
}

View file

@ -25,8 +25,8 @@ type NativeTun struct {
tunFile *os.File
tunWriter N.VectorisedWriter
mtu uint32
inet4Address string
inet6Address string
inet4Address [4]byte
inet6Address [16]byte
}
func New(options Options) (Tun, error) {
@ -57,10 +57,10 @@ func New(options Options) (Tun, error) {
mtu: options.MTU,
}
if len(options.Inet4Address) > 0 {
nativeTun.inet4Address = string(options.Inet4Address[0].Addr().AsSlice())
nativeTun.inet4Address = options.Inet4Address[0].Addr().As4()
}
if len(options.Inet6Address) > 0 {
nativeTun.inet6Address = string(options.Inet6Address[0].Addr().AsSlice())
nativeTun.inet6Address = options.Inet6Address[0].Addr().As16()
}
var ok bool
nativeTun.tunWriter, ok = bufio.CreateVectorisedWriter(nativeTun.tunFile)

View file

@ -7,7 +7,7 @@ import (
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
@ -56,9 +56,9 @@ func (e *DarwinEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
func (e *DarwinEndpoint) dispatchLoop() {
_buffer := buf.StackNewSize(int(e.tun.mtu) + 4)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
data := buffer.FreeBytes()
packetBuffer := common.Dup(_buffer)
defer packetBuffer.Release()
data := packetBuffer.FreeBytes()
for {
n, err := e.tun.tunFile.Read(data)
if err != nil {
@ -69,13 +69,13 @@ func (e *DarwinEndpoint) dispatchLoop() {
switch header.IPVersion(packet) {
case header.IPv4Version:
networkProtocol = header.IPv4ProtocolNumber
if header.IPv4(packet).DestinationAddress() == tcpip.Address(e.tun.inet4Address) {
if header.IPv4(packet).DestinationAddress().As4() == e.tun.inet4Address {
e.tun.tunFile.Write(data[:n])
continue
}
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
if header.IPv6(packet).DestinationAddress() == tcpip.Address(e.tun.inet6Address) {
if header.IPv6(packet).DestinationAddress().As16() == e.tun.inet6Address {
e.tun.tunFile.Write(data[:n])
continue
}
@ -84,7 +84,7 @@ func (e *DarwinEndpoint) dispatchLoop() {
continue
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: bufferv2.MakeWithData(data[4:n]),
Payload: buffer.MakeWithData(data[4:n]),
IsForwardedPacket: true,
})
pkt.NetworkProtocolNumber = networkProtocol

View file

@ -3,7 +3,7 @@
package tun
import (
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
@ -51,16 +51,16 @@ func (e *WintunEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
func (e *WintunEndpoint) dispatchLoop() {
for {
var buffer bufferv2.Buffer
var packetBuffer buffer.Buffer
err := e.tun.ReadFunc(func(b []byte) {
buffer = bufferv2.MakeWithData(b)
packetBuffer = buffer.MakeWithData(b)
})
if err != nil {
break
}
ihl, ok := buffer.PullUp(0, 1)
ihl, ok := packetBuffer.PullUp(0, 1)
if !ok {
buffer.Release()
packetBuffer.Release()
continue
}
var networkProtocol tcpip.NetworkProtocolNumber
@ -70,12 +70,12 @@ func (e *WintunEndpoint) dispatchLoop() {
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
default:
e.tun.Write(buffer.Flatten())
buffer.Release()
e.tun.Write(packetBuffer.Flatten())
packetBuffer.Release()
continue
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: buffer,
Payload: packetBuffer,
IsForwardedPacket: true,
})
dispatcher := e.dispatcher