mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-04-01 19:07:38 +03:00
Export interface for WireGuard
This commit is contained in:
parent
8a18f0c99e
commit
4ebeb2fa86
11 changed files with 269 additions and 49 deletions
2
go.mod
2
go.mod
|
@ -9,7 +9,7 @@ require (
|
||||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3
|
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4
|
github.com/sagernet/nftables v0.3.0-beta.4
|
||||||
github.com/sagernet/sing v0.6.0-alpha.11
|
github.com/sagernet/sing v0.6.0-alpha.18
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
|
||||||
golang.org/x/net v0.26.0
|
golang.org/x/net v0.26.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -22,8 +22,8 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||||
github.com/sagernet/sing v0.6.0-alpha.11 h1:ZcZlA0/vdDeiipAbjK73x9VabGJ/RRcAJgWhOo/OoBk=
|
github.com/sagernet/sing v0.6.0-alpha.18 h1:ih4CurU8KvbhfagYjSqVrE2LR0oBSXSZTNH2sAGPGiM=
|
||||||
github.com/sagernet/sing v0.6.0-alpha.11/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.0-alpha.18/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
|
|
136
internal/gtcpip/header/interfaces.go
Normal file
136
internal/gtcpip/header/interfaces.go
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
// Copyright 2018 The gVisor Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package header
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
tcpip "github.com/sagernet/sing-tun/internal/gtcpip"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxIPPacketSize is the maximum supported IP packet size, excluding
|
||||||
|
// jumbograms. The maximum IPv4 packet size is 64k-1 (total size must fit
|
||||||
|
// in 16 bits). For IPv6, the payload max size (excluding jumbograms) is
|
||||||
|
// 64k-1 (also needs to fit in 16 bits). So we use 64k - 1 + 2 * m, where
|
||||||
|
// m is the minimum IPv6 header size; we leave room for some potential
|
||||||
|
// IP options.
|
||||||
|
MaxIPPacketSize = 0xffff + 2*IPv6MinimumSize
|
||||||
|
)
|
||||||
|
|
||||||
|
// Transport offers generic methods to query and/or update the fields of the
|
||||||
|
// header of a transport protocol buffer.
|
||||||
|
type Transport interface {
|
||||||
|
// SourcePort returns the value of the "source port" field.
|
||||||
|
SourcePort() uint16
|
||||||
|
|
||||||
|
// Destination returns the value of the "destination port" field.
|
||||||
|
DestinationPort() uint16
|
||||||
|
|
||||||
|
// Checksum returns the value of the "checksum" field.
|
||||||
|
Checksum() uint16
|
||||||
|
|
||||||
|
// SetSourcePort sets the value of the "source port" field.
|
||||||
|
SetSourcePort(uint16)
|
||||||
|
|
||||||
|
// SetDestinationPort sets the value of the "destination port" field.
|
||||||
|
SetDestinationPort(uint16)
|
||||||
|
|
||||||
|
// SetChecksum sets the value of the "checksum" field.
|
||||||
|
SetChecksum(uint16)
|
||||||
|
|
||||||
|
// Payload returns the data carried in the transport buffer.
|
||||||
|
Payload() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChecksummableTransport is a Transport that supports checksumming.
|
||||||
|
type ChecksummableTransport interface {
|
||||||
|
Transport
|
||||||
|
|
||||||
|
// SetSourcePortWithChecksumUpdate sets the source port and updates
|
||||||
|
// the checksum.
|
||||||
|
//
|
||||||
|
// The receiver's checksum must be a fully calculated checksum.
|
||||||
|
SetSourcePortWithChecksumUpdate(port uint16)
|
||||||
|
|
||||||
|
// SetDestinationPortWithChecksumUpdate sets the destination port and updates
|
||||||
|
// the checksum.
|
||||||
|
//
|
||||||
|
// The receiver's checksum must be a fully calculated checksum.
|
||||||
|
SetDestinationPortWithChecksumUpdate(port uint16)
|
||||||
|
|
||||||
|
// UpdateChecksumPseudoHeaderAddress updates the checksum to reflect an
|
||||||
|
// updated address in the pseudo header.
|
||||||
|
//
|
||||||
|
// If fullChecksum is true, the receiver's checksum field is assumed to hold a
|
||||||
|
// fully calculated checksum. Otherwise, it is assumed to hold a partially
|
||||||
|
// calculated checksum which only reflects the pseudo header.
|
||||||
|
UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullChecksum bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network offers generic methods to query and/or update the fields of the
|
||||||
|
// header of a network protocol buffer.
|
||||||
|
type Network interface {
|
||||||
|
// SourceAddress returns the value of the "source address" field.
|
||||||
|
SourceAddress() tcpip.Address
|
||||||
|
|
||||||
|
// DestinationAddress returns the value of the "destination address"
|
||||||
|
// field.
|
||||||
|
DestinationAddress() tcpip.Address
|
||||||
|
|
||||||
|
DestinationAddr() netip.Addr
|
||||||
|
|
||||||
|
// Checksum returns the value of the "checksum" field.
|
||||||
|
Checksum() uint16
|
||||||
|
|
||||||
|
// SetSourceAddress sets the value of the "source address" field.
|
||||||
|
SetSourceAddress(tcpip.Address)
|
||||||
|
|
||||||
|
// SetDestinationAddress sets the value of the "destination address"
|
||||||
|
// field.
|
||||||
|
SetDestinationAddress(tcpip.Address)
|
||||||
|
|
||||||
|
SetDestinationAddr(addr netip.Addr)
|
||||||
|
|
||||||
|
// SetChecksum sets the value of the "checksum" field.
|
||||||
|
SetChecksum(uint16)
|
||||||
|
|
||||||
|
// TransportProtocol returns the number of the transport protocol
|
||||||
|
// stored in the payload.
|
||||||
|
TransportProtocol() tcpip.TransportProtocolNumber
|
||||||
|
|
||||||
|
// Payload returns a byte slice containing the payload of the network
|
||||||
|
// packet.
|
||||||
|
Payload() []byte
|
||||||
|
|
||||||
|
// TOS returns the values of the "type of service" and "flow label" fields.
|
||||||
|
TOS() (uint8, uint32)
|
||||||
|
|
||||||
|
// SetTOS sets the values of the "type of service" and "flow label" fields.
|
||||||
|
SetTOS(t uint8, l uint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChecksummableNetwork is a Network that supports checksumming.
|
||||||
|
type ChecksummableNetwork interface {
|
||||||
|
Network
|
||||||
|
|
||||||
|
// SetSourceAddressAndChecksum sets the source address and updates the
|
||||||
|
// checksum to reflect the new address.
|
||||||
|
SetSourceAddressWithChecksumUpdate(tcpip.Address)
|
||||||
|
|
||||||
|
// SetDestinationAddressAndChecksum sets the destination address and
|
||||||
|
// updates the checksum to reflect the new address.
|
||||||
|
SetDestinationAddressWithChecksumUpdate(tcpip.Address)
|
||||||
|
}
|
|
@ -19,13 +19,11 @@ import (
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const WithGVisor = true
|
const WithGVisor = true
|
||||||
|
|
||||||
const defaultNIC tcpip.NICID = 1
|
const DefaultNIC tcpip.NICID = 1
|
||||||
|
|
||||||
type GVisor struct {
|
type GVisor struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
@ -68,28 +66,11 @@ func (t *GVisor) Start() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
linkEndpoint = &LinkEndpointFilter{linkEndpoint, t.broadcastAddr, t.tun}
|
linkEndpoint = &LinkEndpointFilter{linkEndpoint, t.broadcastAddr, t.tun}
|
||||||
ipStack, err := newGVisorStack(linkEndpoint)
|
ipStack, err := NewGVisorStack(linkEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tcpForwarder := tcp.NewForwarder(ipStack, 0, 1024, func(r *tcp.ForwarderRequest) {
|
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, NewTCPForwarder(t.ctx, ipStack, t.handler).HandlePacket)
|
||||||
source := M.SocksaddrFrom(AddrFromAddress(r.ID().RemoteAddress), r.ID().RemotePort)
|
|
||||||
destination := M.SocksaddrFrom(AddrFromAddress(r.ID().LocalAddress), r.ID().LocalPort)
|
|
||||||
pErr := t.handler.PrepareConnection(N.NetworkTCP, source, destination)
|
|
||||||
if pErr != nil {
|
|
||||||
r.Complete(pErr != ErrDrop)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
conn := &gLazyConn{
|
|
||||||
parentCtx: t.ctx,
|
|
||||||
stack: t.stack,
|
|
||||||
request: r,
|
|
||||||
localAddr: source.TCPAddr(),
|
|
||||||
remoteAddr: destination.TCPAddr(),
|
|
||||||
}
|
|
||||||
go t.handler.NewConnectionEx(t.ctx, conn, source, destination, nil)
|
|
||||||
})
|
|
||||||
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
|
||||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, NewUDPForwarder(t.ctx, ipStack, t.handler, t.udpTimeout).HandlePacket)
|
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, NewUDPForwarder(t.ctx, ipStack, t.handler, t.udpTimeout).HandlePacket)
|
||||||
t.stack = ipStack
|
t.stack = ipStack
|
||||||
t.endpoint = linkEndpoint
|
t.endpoint = linkEndpoint
|
||||||
|
@ -124,7 +105,7 @@ func AddrFromAddress(address tcpip.Address) netip.Addr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGVisorStack(ep stack.LinkEndpoint) (*stack.Stack, error) {
|
func NewGVisorStack(ep stack.LinkEndpoint) (*stack.Stack, error) {
|
||||||
ipStack := stack.New(stack.Options{
|
ipStack := stack.New(stack.Options{
|
||||||
NetworkProtocols: []stack.NetworkProtocolFactory{
|
NetworkProtocols: []stack.NetworkProtocolFactory{
|
||||||
ipv4.NewProtocol,
|
ipv4.NewProtocol,
|
||||||
|
@ -137,19 +118,19 @@ func newGVisorStack(ep stack.LinkEndpoint) (*stack.Stack, error) {
|
||||||
icmp.NewProtocol6,
|
icmp.NewProtocol6,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
err := ipStack.CreateNIC(defaultNIC, ep)
|
err := ipStack.CreateNIC(DefaultNIC, ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gonet.TranslateNetstackError(err)
|
return nil, gonet.TranslateNetstackError(err)
|
||||||
}
|
}
|
||||||
ipStack.SetRouteTable([]tcpip.Route{
|
ipStack.SetRouteTable([]tcpip.Route{
|
||||||
{Destination: header.IPv4EmptySubnet, NIC: defaultNIC},
|
{Destination: header.IPv4EmptySubnet, NIC: DefaultNIC},
|
||||||
{Destination: header.IPv6EmptySubnet, NIC: defaultNIC},
|
{Destination: header.IPv6EmptySubnet, NIC: DefaultNIC},
|
||||||
})
|
})
|
||||||
err = ipStack.SetSpoofing(defaultNIC, true)
|
err = ipStack.SetSpoofing(DefaultNIC, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gonet.TranslateNetstackError(err)
|
return nil, gonet.TranslateNetstackError(err)
|
||||||
}
|
}
|
||||||
err = ipStack.SetPromiscuousMode(defaultNIC, true)
|
err = ipStack.SetPromiscuousMode(DefaultNIC, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gonet.TranslateNetstackError(err)
|
return nil, gonet.TranslateNetstackError(err)
|
||||||
}
|
}
|
||||||
|
|
51
stack_gvisor_tcp.go
Normal file
51
stack_gvisor_tcp.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
//go:build with_gvisor
|
||||||
|
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||||
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TCPForwarder struct {
|
||||||
|
ctx context.Context
|
||||||
|
stack *stack.Stack
|
||||||
|
handler Handler
|
||||||
|
forwarder *tcp.Forwarder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTCPForwarder(ctx context.Context, stack *stack.Stack, handler Handler) *TCPForwarder {
|
||||||
|
forwarder := &TCPForwarder{
|
||||||
|
ctx: ctx,
|
||||||
|
stack: stack,
|
||||||
|
handler: handler,
|
||||||
|
}
|
||||||
|
forwarder.forwarder = tcp.NewForwarder(stack, 0, 1024, forwarder.Forward)
|
||||||
|
return forwarder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TCPForwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
||||||
|
return f.forwarder.HandlePacket(id, pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TCPForwarder) Forward(r *tcp.ForwarderRequest) {
|
||||||
|
source := M.SocksaddrFrom(AddrFromAddress(r.ID().RemoteAddress), r.ID().RemotePort)
|
||||||
|
destination := M.SocksaddrFrom(AddrFromAddress(r.ID().LocalAddress), r.ID().LocalPort)
|
||||||
|
pErr := f.handler.PrepareConnection(N.NetworkTCP, source, destination)
|
||||||
|
if pErr != nil {
|
||||||
|
r.Complete(pErr != ErrDrop)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn := &gLazyConn{
|
||||||
|
parentCtx: f.ctx,
|
||||||
|
stack: f.stack,
|
||||||
|
request: r,
|
||||||
|
localAddr: source.TCPAddr(),
|
||||||
|
remoteAddr: destination.TCPAddr(),
|
||||||
|
}
|
||||||
|
go f.handler.NewConnectionEx(f.ctx, conn, source, destination, nil)
|
||||||
|
}
|
|
@ -123,7 +123,7 @@ func (w *UDPBackWriter) WritePacket(packetBuffer *buf.Buffer, destination M.Sock
|
||||||
defer packetBuffer.Release()
|
defer packetBuffer.Release()
|
||||||
|
|
||||||
route, err := w.stack.FindRoute(
|
route, err := w.stack.FindRoute(
|
||||||
defaultNIC,
|
DefaultNIC,
|
||||||
AddressFromAddr(destination.Addr),
|
AddressFromAddr(destination.Addr),
|
||||||
w.source,
|
w.source,
|
||||||
w.sourceNetwork,
|
w.sourceNetwork,
|
||||||
|
|
|
@ -38,7 +38,7 @@ func (m *Mixed) Start() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
endpoint := channel.New(1024, uint32(m.mtu), "")
|
endpoint := channel.New(1024, uint32(m.mtu), "")
|
||||||
ipStack, err := newGVisorStack(endpoint)
|
ipStack, err := NewGVisorStack(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ func (m *Mixed) batchLoop(linuxTUN LinuxTUN, batchSize int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(writeBuffers) > 0 {
|
if len(writeBuffers) > 0 {
|
||||||
err = linuxTUN.BatchWrite(writeBuffers, m.frontHeadroom)
|
_, err = linuxTUN.BatchWrite(writeBuffers, m.frontHeadroom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Trace(E.Cause(err, "batch write packet"))
|
m.logger.Trace(E.Cause(err, "batch write packet"))
|
||||||
}
|
}
|
||||||
|
@ -151,10 +151,10 @@ func (m *Mixed) processPacket(packet []byte) bool {
|
||||||
writeBack bool
|
writeBack bool
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
switch ipVersion := packet[0] >> 4; ipVersion {
|
switch ipVersion := header.IPVersion(packet); ipVersion {
|
||||||
case 4:
|
case header.IPv4Version:
|
||||||
writeBack, err = m.processIPv4(packet)
|
writeBack, err = m.processIPv4(packet)
|
||||||
case 6:
|
case header.IPv6Version:
|
||||||
writeBack, err = m.processIPv6(packet)
|
writeBack, err = m.processIPv6(packet)
|
||||||
default:
|
default:
|
||||||
err = E.New("ip: unknown version: ", ipVersion)
|
err = E.New("ip: unknown version: ", ipVersion)
|
||||||
|
|
|
@ -419,7 +419,7 @@ func (s *System) resetIPv4TCP(origIPHdr header.IPv4, origTCPHdr header.TCP) erro
|
||||||
ipHdr.SetChecksum(0)
|
ipHdr.SetChecksum(0)
|
||||||
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
||||||
if PacketOffset > 0 {
|
if PacketOffset > 0 {
|
||||||
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET
|
PacketFillHeader(newPacket.ExtendHeader(PacketOffset), header.IPv4Version)
|
||||||
} else {
|
} else {
|
||||||
newPacket.Advance(-s.frontHeadroom)
|
newPacket.Advance(-s.frontHeadroom)
|
||||||
}
|
}
|
||||||
|
@ -502,7 +502,7 @@ func (s *System) resetIPv6TCP(origIPHdr header.IPv6, origTCPHdr header.TCP) erro
|
||||||
tcpHdr.SetChecksum(^tcpHdr.CalculateChecksum(header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), header.TCPMinimumSize)))
|
tcpHdr.SetChecksum(^tcpHdr.CalculateChecksum(header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipHdr.SourceAddressSlice(), ipHdr.DestinationAddressSlice(), header.TCPMinimumSize)))
|
||||||
}
|
}
|
||||||
if PacketOffset > 0 {
|
if PacketOffset > 0 {
|
||||||
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET6
|
PacketFillHeader(newPacket.ExtendHeader(PacketOffset), header.IPv6Version)
|
||||||
} else {
|
} else {
|
||||||
newPacket.Advance(-s.frontHeadroom)
|
newPacket.Advance(-s.frontHeadroom)
|
||||||
}
|
}
|
||||||
|
@ -684,7 +684,7 @@ func (s *System) rejectIPv6WithICMP(ipHdr header.IPv6, code header.ICMPv6Code) e
|
||||||
}))
|
}))
|
||||||
copy(icmpHdr.Payload(), payload)
|
copy(icmpHdr.Payload(), payload)
|
||||||
if PacketOffset > 0 {
|
if PacketOffset > 0 {
|
||||||
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET6
|
PacketFillHeader(newPacket.ExtendHeader(PacketOffset), header.IPv6Version)
|
||||||
} else {
|
} else {
|
||||||
newPacket.Advance(-s.frontHeadroom)
|
newPacket.Advance(-s.frontHeadroom)
|
||||||
}
|
}
|
||||||
|
@ -724,7 +724,7 @@ func (w *systemUDPPacketWriter4) WritePacket(buffer *buf.Buffer, destination M.S
|
||||||
ipHdr.SetChecksum(0)
|
ipHdr.SetChecksum(0)
|
||||||
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
ipHdr.SetChecksum(^ipHdr.CalculateChecksum())
|
||||||
if PacketOffset > 0 {
|
if PacketOffset > 0 {
|
||||||
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET
|
PacketFillHeader(newPacket.ExtendHeader(PacketOffset), header.IPv4Version)
|
||||||
} else {
|
} else {
|
||||||
newPacket.Advance(-w.frontHeadroom)
|
newPacket.Advance(-w.frontHeadroom)
|
||||||
}
|
}
|
||||||
|
@ -763,7 +763,7 @@ func (w *systemUDPPacketWriter6) WritePacket(buffer *buf.Buffer, destination M.S
|
||||||
udpHdr.SetChecksum(0)
|
udpHdr.SetChecksum(0)
|
||||||
}
|
}
|
||||||
if PacketOffset > 0 {
|
if PacketOffset > 0 {
|
||||||
newPacket.ExtendHeader(PacketOffset)[3] = syscall.AF_INET6
|
PacketFillHeader(newPacket.ExtendHeader(PacketOffset), header.IPv6Version)
|
||||||
} else {
|
} else {
|
||||||
newPacket.Advance(-w.frontHeadroom)
|
newPacket.Advance(-w.frontHeadroom)
|
||||||
}
|
}
|
||||||
|
|
34
stack_system_packet.go
Normal file
34
stack_system_packet.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PacketIPVersion(packet []byte) int {
|
||||||
|
return header.IPVersion(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PacketFillHeader(packet []byte, ipVersion int) {
|
||||||
|
if PacketOffset > 0 {
|
||||||
|
switch ipVersion {
|
||||||
|
case header.IPv4Version:
|
||||||
|
packet[3] = syscall.AF_INET
|
||||||
|
case header.IPv6Version:
|
||||||
|
packet[3] = syscall.AF_INET6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PacketDestination(packet []byte) netip.Addr {
|
||||||
|
switch ipVersion := header.IPVersion(packet); ipVersion {
|
||||||
|
case header.IPv4Version:
|
||||||
|
return header.IPv4(packet).DestinationAddr()
|
||||||
|
case header.IPv6Version:
|
||||||
|
return header.IPv6(packet).DestinationAddr()
|
||||||
|
default:
|
||||||
|
return netip.Addr{}
|
||||||
|
}
|
||||||
|
}
|
3
tun.go
3
tun.go
|
@ -1,6 +1,7 @@
|
||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/sagernet/sing/common/control"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
@ -54,6 +55,7 @@ type Options struct {
|
||||||
MTU uint32
|
MTU uint32
|
||||||
GSO bool
|
GSO bool
|
||||||
AutoRoute bool
|
AutoRoute bool
|
||||||
|
InterfaceScope bool
|
||||||
Inet4Gateway netip.Addr
|
Inet4Gateway netip.Addr
|
||||||
Inet6Gateway netip.Addr
|
Inet6Gateway netip.Addr
|
||||||
DNSServers []netip.Addr
|
DNSServers []netip.Addr
|
||||||
|
@ -74,6 +76,7 @@ type Options struct {
|
||||||
IncludeAndroidUser []int
|
IncludeAndroidUser []int
|
||||||
IncludePackage []string
|
IncludePackage []string
|
||||||
ExcludePackage []string
|
ExcludePackage []string
|
||||||
|
InterfaceFinder control.InterfaceFinder
|
||||||
InterfaceMonitor DefaultInterfaceMonitor
|
InterfaceMonitor DefaultInterfaceMonitor
|
||||||
FileDescriptor int
|
FileDescriptor int
|
||||||
Logger logger.Logger
|
Logger logger.Logger
|
||||||
|
|
|
@ -3,6 +3,7 @@ package tun
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/sagernet/sing-tun/internal/gtcpip/header"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
|
@ -96,9 +97,10 @@ var (
|
||||||
|
|
||||||
func (t *NativeTun) WriteVectorised(buffers []*buf.Buffer) error {
|
func (t *NativeTun) WriteVectorised(buffers []*buf.Buffer) error {
|
||||||
var packetHeader []byte
|
var packetHeader []byte
|
||||||
if buffers[0].Byte(0)>>4 == 4 {
|
switch header.IPVersion(buffers[0].Bytes()) {
|
||||||
|
case header.IPv4Version:
|
||||||
packetHeader = packetHeader4[:]
|
packetHeader = packetHeader4[:]
|
||||||
} else {
|
case header.IPv6Version:
|
||||||
packetHeader = packetHeader6[:]
|
packetHeader = packetHeader6[:]
|
||||||
}
|
}
|
||||||
return t.tunWriter.WriteVectorised(append([]*buf.Buffer{buf.As(packetHeader)}, buffers...))
|
return t.tunWriter.WriteVectorised(append([]*buf.Buffer{buf.As(packetHeader)}, buffers...))
|
||||||
|
@ -250,6 +252,7 @@ func configure(tunFd int, ifIndex int, name string, options Options) error {
|
||||||
|
|
||||||
func (t *NativeTun) setRoutes() error {
|
func (t *NativeTun) setRoutes() error {
|
||||||
if t.options.AutoRoute && t.options.FileDescriptor == 0 {
|
if t.options.AutoRoute && t.options.FileDescriptor == 0 {
|
||||||
|
|
||||||
routeRanges, err := t.options.BuildAutoRouteRanges(false)
|
routeRanges, err := t.options.BuildAutoRouteRanges(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -262,14 +265,22 @@ func (t *NativeTun) setRoutes() error {
|
||||||
} else {
|
} else {
|
||||||
gateway = gateway6
|
gateway = gateway6
|
||||||
}
|
}
|
||||||
err = execRoute(unix.RTM_ADD, destination, gateway)
|
var interfaceIndex int
|
||||||
|
if t.options.InterfaceScope {
|
||||||
|
iff, err := t.options.InterfaceFinder.ByName(t.options.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
interfaceIndex = iff.Index
|
||||||
|
}
|
||||||
|
err = execRoute(unix.RTM_ADD, t.options.InterfaceScope, interfaceIndex, destination, gateway)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, unix.EEXIST) {
|
if errors.Is(err, unix.EEXIST) {
|
||||||
err = execRoute(unix.RTM_DELETE, destination, gateway)
|
err = execRoute(unix.RTM_DELETE, false, 0, destination, gateway)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "remove existing route: ", destination)
|
return E.Cause(err, "remove existing route: ", destination)
|
||||||
}
|
}
|
||||||
err = execRoute(unix.RTM_ADD, destination, gateway)
|
err = execRoute(unix.RTM_ADD, t.options.InterfaceScope, interfaceIndex, destination, gateway)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "re-add route: ", destination)
|
return E.Cause(err, "re-add route: ", destination)
|
||||||
}
|
}
|
||||||
|
@ -300,7 +311,7 @@ func (t *NativeTun) unsetRoutes() error {
|
||||||
} else {
|
} else {
|
||||||
gateway = gateway6
|
gateway = gateway6
|
||||||
}
|
}
|
||||||
err = execRoute(unix.RTM_DELETE, destination, gateway)
|
err = execRoute(unix.RTM_DELETE, false, 0, destination, gateway)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = E.Errors(err, E.Cause(err, "delete route: ", destination))
|
err = E.Errors(err, E.Cause(err, "delete route: ", destination))
|
||||||
}
|
}
|
||||||
|
@ -317,7 +328,7 @@ func useSocket(domain, typ, proto int, block func(socketFd int) error) error {
|
||||||
return block(socketFd)
|
return block(socketFd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func execRoute(rtmType int, destination netip.Prefix, gateway netip.Addr) error {
|
func execRoute(rtmType int, interfaceScope bool, interfaceIndex int, destination netip.Prefix, gateway netip.Addr) error {
|
||||||
routeMessage := route.RouteMessage{
|
routeMessage := route.RouteMessage{
|
||||||
Type: rtmType,
|
Type: rtmType,
|
||||||
Version: unix.RTM_VERSION,
|
Version: unix.RTM_VERSION,
|
||||||
|
@ -326,6 +337,10 @@ func execRoute(rtmType int, destination netip.Prefix, gateway netip.Addr) error
|
||||||
}
|
}
|
||||||
if rtmType == unix.RTM_ADD {
|
if rtmType == unix.RTM_ADD {
|
||||||
routeMessage.Flags |= unix.RTF_UP
|
routeMessage.Flags |= unix.RTF_UP
|
||||||
|
if interfaceScope {
|
||||||
|
routeMessage.Flags |= unix.RTF_IFSCOPE
|
||||||
|
routeMessage.Index = interfaceIndex
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if gateway.Is4() {
|
if gateway.Is4() {
|
||||||
routeMessage.Addrs = []route.Addr{
|
routeMessage.Addrs = []route.Addr{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue