init: tun support with sing-tun

This commit is contained in:
Haruue 2024-03-19 02:13:50 +08:00
parent 5c423d16fe
commit f10805dc13
No known key found for this signature in database
GPG key ID: F6083B28CBCBC148
6 changed files with 404 additions and 20 deletions

62
app/internal/tun/log.go Normal file
View file

@ -0,0 +1,62 @@
package tun
import (
"github.com/sagernet/sing/common/logger"
"go.uber.org/zap"
)
var _ logger.Logger = (*singLogger)(nil)
type singLogger struct {
tag string
zapLogger *zap.Logger
}
func (l *singLogger) Trace(args ...any) {
if l.zapLogger == nil {
return
}
l.zapLogger.Debug(l.tag, zap.Any("args", args))
}
func (l *singLogger) Debug(args ...any) {
if l.zapLogger == nil {
return
}
l.zapLogger.Debug(l.tag, zap.Any("args", args))
}
func (l *singLogger) Info(args ...any) {
if l.zapLogger == nil {
return
}
l.zapLogger.Info(l.tag, zap.Any("args", args))
}
func (l *singLogger) Warn(args ...any) {
if l.zapLogger == nil {
return
}
l.zapLogger.Warn(l.tag, zap.Any("args", args))
}
func (l *singLogger) Error(args ...any) {
if l.zapLogger == nil {
return
}
l.zapLogger.Error(l.tag, zap.Any("args", args))
}
func (l *singLogger) Fatal(args ...any) {
if l.zapLogger == nil {
return
}
l.zapLogger.Fatal(l.tag, zap.Any("args", args))
}
func (l *singLogger) Panic(args ...any) {
if l.zapLogger == nil {
return
}
l.zapLogger.Panic(l.tag, zap.Any("args", args))
}

211
app/internal/tun/server.go Normal file
View file

@ -0,0 +1,211 @@
package tun
import (
"context"
"errors"
"fmt"
"io"
"net"
"net/netip"
"github.com/apernet/hysteria/core/client"
tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/network"
"go.uber.org/zap"
)
type Server struct {
HyClient client.Client
EventLogger EventLogger
// for debugging
Logger *zap.Logger
IfName string
MTU uint32
UDPTimeout int64 // in seconds
// required by system stack
Inet4Address []netip.Prefix
Inet6Address []netip.Prefix
tunIf tun.Tun
tunStack tun.Stack
}
type EventLogger interface {
TCPRequest(addr, reqAddr string)
TCPError(addr, reqAddr string, err error)
UDPRequest(addr string)
UDPError(addr string, err error)
}
func (s *Server) Start() error {
tunOpts := tun.Options{
Name: s.IfName,
Inet4Address: s.Inet4Address,
Inet6Address: s.Inet6Address,
MTU: s.MTU,
GSO: true,
Logger: &singLogger{
tag: "tun",
zapLogger: s.Logger,
},
}
tunIf, err := tun.New(tunOpts)
if err != nil {
return fmt.Errorf("failed to create tun interface: %w", err)
}
s.tunIf = tunIf
tunStack, err := tun.NewStack("system", tun.StackOptions{
Context: context.Background(),
Tun: tunIf,
TunOptions: tunOpts,
UDPTimeout: s.UDPTimeout,
Handler: &tunHandler{s},
Logger: &singLogger{
tag: "tun-stack",
zapLogger: s.Logger,
},
})
if err != nil {
return fmt.Errorf("failed to create tun stack: %w", err)
}
s.tunStack = tunStack
err = tunStack.Start()
if err != nil {
return fmt.Errorf("failed to start tun stack: %w", err)
}
return nil
}
func (s *Server) Close() error {
var ifErr, stackErr error
if s.tunIf != nil {
ifErr = s.tunIf.Close()
}
if s.tunStack != nil {
stackErr = s.tunStack.Close()
}
return errors.Join(ifErr, stackErr)
}
type tunHandler struct {
*Server
}
var _ tun.Handler = (*tunHandler)(nil)
func (t *tunHandler) NewConnection(ctx context.Context, conn net.Conn, m metadata.Metadata) error {
addr := m.Source.String()
reqAddr := m.Destination.String()
if t.EventLogger != nil {
t.EventLogger.TCPRequest(addr, reqAddr)
}
var closeErr error
defer func() {
if t.EventLogger != nil {
t.EventLogger.TCPError(addr, reqAddr, closeErr)
}
}()
rc, err := t.HyClient.TCP(reqAddr)
if err != nil {
closeErr = err
// the returned err is ignored by caller
return nil
}
defer rc.Close()
// start forwarding
copyErrChan := make(chan error, 3)
go func() {
<-ctx.Done()
copyErrChan <- ctx.Err()
}()
go func() {
_, copyErr := io.Copy(rc, conn)
copyErrChan <- copyErr
}()
go func() {
_, copyErr := io.Copy(conn, rc)
copyErrChan <- copyErr
}()
closeErr = <-copyErrChan
return nil
}
func (t *tunHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, m metadata.Metadata) error {
addr := m.Source.String()
if t.EventLogger != nil {
t.EventLogger.UDPRequest(addr)
}
var closeErr error
defer func() {
if t.EventLogger != nil {
t.EventLogger.UDPError(addr, closeErr)
}
}()
rc, err := t.HyClient.UDP()
if err != nil {
closeErr = err
// the returned err is simply called into NewError again
return nil
}
defer rc.Close()
// start forwarding
copyErrChan := make(chan error, 3)
go func() {
<-ctx.Done()
copyErrChan <- ctx.Err()
}()
// local <- remote
go func() {
for {
bs, from, err := rc.Receive()
if err != nil {
copyErrChan <- err
return
}
var fromAddr metadata.Socksaddr
if ap, perr := netip.ParseAddrPort(from); perr == nil {
fromAddr = metadata.SocksaddrFromNetIP(ap)
} else {
fromAddr.Fqdn = from
}
err = conn.WritePacket(buf.As(bs), fromAddr)
if err != nil {
copyErrChan <- err
return
}
}
}()
// local -> remote
go func() {
buffer := buf.NewPacket()
defer buffer.Release()
for {
buffer.Reset()
addr, err := conn.ReadPacket(buffer)
if err != nil {
copyErrChan <- err
return
}
err = rc.Send(buffer.Bytes(), addr.String())
if err != nil {
copyErrChan <- err
return
}
}
}()
closeErr = <-copyErrChan
return nil
}
func (t *tunHandler) NewError(ctx context.Context, err error) {
// unused
}