mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-04-02 19:37:40 +03:00
Init commit
This commit is contained in:
commit
4f7247190c
16 changed files with 573 additions and 0 deletions
6
.github/update_dependencies.sh
vendored
Executable file
6
.github/update_dependencies.sh
vendored
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
PROJECTS=$(dirname "$0")/../..
|
||||||
|
|
||||||
|
go get -x github.com/sagernet/sing@$(git -C $PROJECTS/sing rev-parse HEAD)
|
||||||
|
go mod tidy
|
40
.github/workflows/debug.yml
vendored
Normal file
40
.github/workflows/debug.yml
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
name: Debug build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '.github/**'
|
||||||
|
- '!.github/workflows/debug.yml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Debug build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Get latest go version
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ steps.version.outputs.go_version }}
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
version=`git rev-parse HEAD`
|
||||||
|
mkdir build
|
||||||
|
pushd build
|
||||||
|
go mod init build
|
||||||
|
go get -v github.com/sagernet/sing-tun@$version
|
||||||
|
popd
|
||||||
|
go build -v ./...
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/.idea/
|
||||||
|
/vendor/
|
16
.golangci.yml
Normal file
16
.golangci.yml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- gofumpt
|
||||||
|
- govet
|
||||||
|
- gci
|
||||||
|
- staticcheck
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
gci:
|
||||||
|
sections:
|
||||||
|
- standard
|
||||||
|
- prefix(github.com/sagernet/sing)
|
||||||
|
- default
|
||||||
|
staticcheck:
|
||||||
|
go: '1.18'
|
14
LICENSE
Normal file
14
LICENSE
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Copyright (C) 2022 by nekohasekai <contact-sagernet@sekai.icu>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
24
README.md
Normal file
24
README.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# sing-tun
|
||||||
|
|
||||||
|
Simple transparent proxy library.
|
||||||
|
|
||||||
|
Currently only for linux.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
```
|
||||||
|
Copyright (C) 2022 by nekohasekai <contact-sagernet@sekai.icu>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
```
|
7
format.go
Normal file
7
format.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package tun
|
||||||
|
|
||||||
|
//go:generate go install -v mvdan.cc/gofumpt@latest
|
||||||
|
//go:generate go install -v github.com/daixiang0/gci@latest
|
||||||
|
//go:generate gofumpt -l -w .
|
||||||
|
//go:generate gofmt -s -w .k
|
||||||
|
//go:generate gci write -s "standard,prefix(github.com/sagernet/),default" .
|
16
go.mod
Normal file
16
go.mod
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
module github.com/sagernet/sing-tun
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/sagernet/sing v0.0.0-20220711062652-4394f7cbbae1
|
||||||
|
github.com/vishvananda/netlink v1.1.0
|
||||||
|
gvisor.dev/gvisor v0.0.0-20220711011657-cecae2f4234d
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/btree v1.0.1 // indirect
|
||||||
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||||
|
)
|
17
go.sum
Normal file
17
go.sum
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||||
|
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||||
|
github.com/sagernet/sing v0.0.0-20220711062652-4394f7cbbae1 h1:gssTBQKiiXd1zALSOzQFZl3qwzCy4O76eSH0YY9A+Po=
|
||||||
|
github.com/sagernet/sing v0.0.0-20220711062652-4394f7cbbae1/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
|
||||||
|
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
|
||||||
|
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||||
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||||
|
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/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
|
||||||
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20220711011657-cecae2f4234d h1:KjI6i6P1ib9DiNdNIN8pb2TXfBewpKHf3O58cjj9vw4=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20220711011657-cecae2f4234d/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
139
gvisor.go
Normal file
139
gvisor.go
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||||
|
"gvisor.dev/gvisor/pkg/waiter"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/bufio"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultNIC tcpip.NICID = 1
|
||||||
|
|
||||||
|
type GVisorTun struct {
|
||||||
|
ctx context.Context
|
||||||
|
tunFd uintptr
|
||||||
|
tunMtu uint32
|
||||||
|
handler Handler
|
||||||
|
stack *stack.Stack
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGVisor(ctx context.Context, tunFd uintptr, tunMtu uint32, handler Handler) *GVisorTun {
|
||||||
|
return &GVisorTun{
|
||||||
|
ctx: ctx,
|
||||||
|
tunFd: tunFd,
|
||||||
|
tunMtu: tunMtu,
|
||||||
|
handler: handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *GVisorTun) Start() error {
|
||||||
|
linkEndpoint, err := NewEndpoint(t.tunFd, t.tunMtu)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ipStack := stack.New(stack.Options{
|
||||||
|
NetworkProtocols: []stack.NetworkProtocolFactory{
|
||||||
|
ipv4.NewProtocol,
|
||||||
|
ipv6.NewProtocol,
|
||||||
|
},
|
||||||
|
TransportProtocols: []stack.TransportProtocolFactory{
|
||||||
|
tcp.NewProtocol,
|
||||||
|
udp.NewProtocol,
|
||||||
|
icmp.NewProtocol4,
|
||||||
|
icmp.NewProtocol6,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
tErr := ipStack.CreateNIC(defaultNIC, linkEndpoint)
|
||||||
|
if tErr != nil {
|
||||||
|
return E.New("create nic: ", tErr)
|
||||||
|
}
|
||||||
|
ipStack.SetRouteTable([]tcpip.Route{
|
||||||
|
{Destination: header.IPv4EmptySubnet, NIC: defaultNIC},
|
||||||
|
{Destination: header.IPv6EmptySubnet, NIC: defaultNIC},
|
||||||
|
})
|
||||||
|
ipStack.SetSpoofing(defaultNIC, true)
|
||||||
|
ipStack.SetPromiscuousMode(defaultNIC, true)
|
||||||
|
bufSize := 20 * 1024
|
||||||
|
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPReceiveBufferSizeRangeOption{
|
||||||
|
Min: 1,
|
||||||
|
Default: bufSize,
|
||||||
|
Max: bufSize,
|
||||||
|
})
|
||||||
|
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPSendBufferSizeRangeOption{
|
||||||
|
Min: 1,
|
||||||
|
Default: bufSize,
|
||||||
|
Max: bufSize,
|
||||||
|
})
|
||||||
|
sOpt := tcpip.TCPSACKEnabled(true)
|
||||||
|
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &sOpt)
|
||||||
|
mOpt := tcpip.TCPModerateReceiveBufferOption(true)
|
||||||
|
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &mOpt)
|
||||||
|
tcpForwarder := tcp.NewForwarder(ipStack, 0, 1024, func(r *tcp.ForwarderRequest) {
|
||||||
|
var wq waiter.Queue
|
||||||
|
endpoint, err := r.CreateEndpoint(&wq)
|
||||||
|
if err != nil {
|
||||||
|
r.Complete(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Complete(false)
|
||||||
|
endpoint.SocketOptions().SetKeepAlive(true)
|
||||||
|
tcpConn := gonet.NewTCPConn(&wq, endpoint)
|
||||||
|
lAddr := tcpConn.RemoteAddr()
|
||||||
|
rAddr := tcpConn.LocalAddr()
|
||||||
|
if lAddr == nil || rAddr == nil {
|
||||||
|
tcpConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
var metadata M.Metadata
|
||||||
|
metadata.Source = M.SocksaddrFromNet(lAddr)
|
||||||
|
metadata.Destination = M.SocksaddrFromNet(rAddr)
|
||||||
|
t.handler.NewConnection(t.ctx, tcpConn, metadata)
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, func(id stack.TransportEndpointID, buffer *stack.PacketBuffer) bool {
|
||||||
|
return tcpForwarder.HandlePacket(id, buffer)
|
||||||
|
})
|
||||||
|
udpForwarder := udp.NewForwarder(ipStack, func(request *udp.ForwarderRequest) {
|
||||||
|
var wq waiter.Queue
|
||||||
|
endpoint, err := request.CreateEndpoint(&wq)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
udpConn := gonet.NewUDPConn(ipStack, &wq, endpoint)
|
||||||
|
lAddr := udpConn.RemoteAddr()
|
||||||
|
rAddr := udpConn.LocalAddr()
|
||||||
|
if lAddr == nil || rAddr == nil {
|
||||||
|
udpConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
var metadata M.Metadata
|
||||||
|
metadata.Source = M.SocksaddrFromNet(lAddr)
|
||||||
|
metadata.Destination = M.SocksaddrFromNet(rAddr)
|
||||||
|
t.handler.NewPacketConnection(t.ctx, bufio.NewPacketConn(&bufio.UnbindPacketConn{ExtendedConn: bufio.NewExtendedConn(udpConn), Addr: M.SocksaddrFromNet(rAddr)}), metadata)
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
||||||
|
t.stack = ipStack
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *GVisorTun) Close() error {
|
||||||
|
return common.Close(
|
||||||
|
common.PtrOrNil(t.stack),
|
||||||
|
)
|
||||||
|
}
|
22
gvisor_linux.go
Normal file
22
gvisor_linux.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewEndpoint(tunFd uintptr, tunMtu uint32) (stack.LinkEndpoint, error) {
|
||||||
|
var packetDispatchMode fdbased.PacketDispatchMode
|
||||||
|
if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" {
|
||||||
|
packetDispatchMode = fdbased.PacketMMap
|
||||||
|
} else {
|
||||||
|
packetDispatchMode = fdbased.RecvMMsg
|
||||||
|
}
|
||||||
|
return fdbased.New(&fdbased.Options{
|
||||||
|
FDs: []int{int(tunFd)},
|
||||||
|
MTU: tunMtu,
|
||||||
|
PacketDispatchMode: packetDispatchMode,
|
||||||
|
})
|
||||||
|
}
|
9
gvisor_nonlinux.go
Normal file
9
gvisor_nonlinux.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import "gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
|
||||||
|
func NewEndpoint(tunFd uintptr, tunMtu uint32) (stack.LinkEndpoint, error) {
|
||||||
|
return NewPosixEndpoint(tunFd, tunMtu)
|
||||||
|
}
|
118
gvisor_posix.go
Normal file
118
gvisor_posix.go
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
gBuffer "gvisor.dev/gvisor/pkg/buffer"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ stack.LinkEndpoint = (*PosixEndpoint)(nil)
|
||||||
|
|
||||||
|
type PosixEndpoint struct {
|
||||||
|
fd uintptr
|
||||||
|
mtu uint32
|
||||||
|
file *os.File
|
||||||
|
dispatcher stack.NetworkDispatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPosixEndpoint(tunFd uintptr, tunMtu uint32) (stack.LinkEndpoint, error) {
|
||||||
|
return &PosixEndpoint{
|
||||||
|
fd: tunFd,
|
||||||
|
mtu: tunMtu,
|
||||||
|
file: os.NewFile(tunFd, "tun"),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PosixEndpoint) MTU() uint32 {
|
||||||
|
return e.mtu
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PosixEndpoint) MaxHeaderLength() uint16 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PosixEndpoint) LinkAddress() tcpip.LinkAddress {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PosixEndpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||||
|
return stack.CapabilityNone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PosixEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||||
|
if dispatcher == nil && e.dispatcher != nil {
|
||||||
|
e.dispatcher = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if dispatcher != nil && e.dispatcher == nil {
|
||||||
|
e.dispatcher = dispatcher
|
||||||
|
go e.dispatchLoop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PosixEndpoint) dispatchLoop() {
|
||||||
|
_buffer := buf.StackNewPacket()
|
||||||
|
defer common.KeepAlive(_buffer)
|
||||||
|
buffer := common.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
for {
|
||||||
|
n, err := e.file.Read(buffer.FreeBytes())
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var view gBuffer.View
|
||||||
|
view.Append(buffer.To(n))
|
||||||
|
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||||
|
Payload: view,
|
||||||
|
IsForwardedPacket: true,
|
||||||
|
})
|
||||||
|
defer pkt.DecRef()
|
||||||
|
var p tcpip.NetworkProtocolNumber
|
||||||
|
ipHeader, ok := pkt.Data().PullUp(1)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch header.IPVersion(ipHeader) {
|
||||||
|
case header.IPv4Version:
|
||||||
|
p = header.IPv4ProtocolNumber
|
||||||
|
case header.IPv6Version:
|
||||||
|
p = header.IPv6ProtocolNumber
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
e.dispatcher.DeliverNetworkPacket(p, pkt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PosixEndpoint) IsAttached() bool {
|
||||||
|
return e.dispatcher != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PosixEndpoint) Wait() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PosixEndpoint) ARPHardwareType() header.ARPHardwareType {
|
||||||
|
return header.ARPHardwareNone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PosixEndpoint) AddHeader(buffer *stack.PacketBuffer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PosixEndpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) {
|
||||||
|
var n int
|
||||||
|
for _, packet := range pkts.AsSlice() {
|
||||||
|
_, err := rw.WriteV(e.fd, packet.Slices())
|
||||||
|
if err != nil {
|
||||||
|
return n, &tcpip.ErrAborted{}
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
10
tun.go
Normal file
10
tun.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
N.TCPConnectionHandler
|
||||||
|
N.UDPConnectionHandler
|
||||||
|
}
|
113
tun_linux.go
Normal file
113
tun_linux.go
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/link/tun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Open(name string) (uintptr, error) {
|
||||||
|
tunFd, err := tun.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uintptr(tunFd), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Configure(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32, autoRoute bool) error {
|
||||||
|
tunLink, err := netlink.LinkByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if inet4Address.IsValid() {
|
||||||
|
addr4, _ := netlink.ParseAddr(inet4Address.String())
|
||||||
|
err = netlink.AddrAdd(tunLink, addr4)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if inet6Address.IsValid() {
|
||||||
|
addr6, _ := netlink.ParseAddr(inet6Address.String())
|
||||||
|
err = netlink.AddrAdd(tunLink, addr6)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = netlink.LinkSetMTU(tunLink, int(mtu))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = netlink.LinkSetUp(tunLink)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if autoRoute {
|
||||||
|
if inet4Address.IsValid() {
|
||||||
|
err = netlink.RouteAdd(&netlink.Route{
|
||||||
|
Dst: &net.IPNet{
|
||||||
|
IP: net.IPv4zero,
|
||||||
|
Mask: net.CIDRMask(0, 32),
|
||||||
|
},
|
||||||
|
LinkIndex: tunLink.Attrs().Index,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if inet6Address.IsValid() {
|
||||||
|
err = netlink.RouteAdd(&netlink.Route{
|
||||||
|
Dst: &net.IPNet{
|
||||||
|
IP: net.IPv6zero,
|
||||||
|
Mask: net.CIDRMask(0, 128),
|
||||||
|
},
|
||||||
|
LinkIndex: tunLink.Attrs().Index,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnConfigure(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, autoRoute bool) error {
|
||||||
|
if autoRoute {
|
||||||
|
tunLink, err := netlink.LinkByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if inet4Address.IsValid() {
|
||||||
|
err = netlink.RouteDel(&netlink.Route{
|
||||||
|
Dst: &net.IPNet{
|
||||||
|
IP: net.IPv4zero,
|
||||||
|
Mask: net.CIDRMask(0, 32),
|
||||||
|
},
|
||||||
|
LinkIndex: tunLink.Attrs().Index,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if inet6Address.IsValid() {
|
||||||
|
err = netlink.RouteDel(&netlink.Route{
|
||||||
|
Dst: &net.IPNet{
|
||||||
|
IP: net.IPv6zero,
|
||||||
|
Mask: net.CIDRMask(0, 128),
|
||||||
|
},
|
||||||
|
LinkIndex: tunLink.Attrs().Index,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
20
tun_other.go
Normal file
20
tun_other.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Open(name string) (uintptr, error) {
|
||||||
|
return 0, os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func Configure(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32, autoRoute bool) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnConfigure(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, autoRoute bool) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue