mirror of
https://github.com/apernet/hysteria.git
synced 2025-04-03 20:47:38 +03:00
init: tun support with sing-tun
This commit is contained in:
parent
5c423d16fe
commit
f10805dc13
6 changed files with 404 additions and 20 deletions
62
app/internal/tun/log.go
Normal file
62
app/internal/tun/log.go
Normal 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
211
app/internal/tun/server.go
Normal 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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue