mirror of
https://github.com/apernet/hysteria.git
synced 2025-04-03 04:27:39 +03:00
234 lines
5.2 KiB
Go
234 lines
5.2 KiB
Go
package tun
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/netip"
|
|
|
|
tun "github.com/apernet/sing-tun"
|
|
"github.com/sagernet/sing/common/buf"
|
|
"github.com/sagernet/sing/common/control"
|
|
"github.com/sagernet/sing/common/metadata"
|
|
"github.com/sagernet/sing/common/network"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/apernet/hysteria/core/v2/client"
|
|
)
|
|
|
|
type Server struct {
|
|
HyClient client.Client
|
|
EventLogger EventLogger
|
|
|
|
// for debugging
|
|
Logger *zap.Logger
|
|
|
|
IfName string
|
|
MTU uint32
|
|
Timeout int64 // in seconds, also applied to TCP in system stack
|
|
|
|
// required by system stack
|
|
Inet4Address []netip.Prefix
|
|
Inet6Address []netip.Prefix
|
|
|
|
// auto route
|
|
AutoRoute bool
|
|
StructRoute bool
|
|
Inet4RouteAddress []netip.Prefix
|
|
Inet6RouteAddress []netip.Prefix
|
|
Inet4RouteExcludeAddress []netip.Prefix
|
|
Inet6RouteExcludeAddress []netip.Prefix
|
|
}
|
|
|
|
type EventLogger interface {
|
|
TCPRequest(addr, reqAddr string)
|
|
TCPError(addr, reqAddr string, err error)
|
|
UDPRequest(addr string)
|
|
UDPError(addr string, err error)
|
|
}
|
|
|
|
func (s *Server) Serve() error {
|
|
if !isIPv6Supported() {
|
|
s.Logger.Warn("tun-pre-check", zap.String("msg", "IPv6 is not supported or enabled on this system, TUN device is created without IPv6 support."))
|
|
s.Inet6Address = nil
|
|
}
|
|
tunOpts := tun.Options{
|
|
Name: s.IfName,
|
|
Inet4Address: s.Inet4Address,
|
|
Inet6Address: s.Inet6Address,
|
|
MTU: s.MTU,
|
|
GSO: true,
|
|
AutoRoute: s.AutoRoute,
|
|
StrictRoute: s.StructRoute,
|
|
Inet4RouteAddress: s.Inet4RouteAddress,
|
|
Inet6RouteAddress: s.Inet6RouteAddress,
|
|
Inet4RouteExcludeAddress: s.Inet4RouteExcludeAddress,
|
|
Inet6RouteExcludeAddress: s.Inet6RouteExcludeAddress,
|
|
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)
|
|
}
|
|
defer tunIf.Close()
|
|
|
|
tunStack, err := tun.NewSystem(tun.StackOptions{
|
|
Context: context.Background(),
|
|
Tun: tunIf,
|
|
TunOptions: tunOpts,
|
|
UDPTimeout: s.Timeout,
|
|
Handler: &tunHandler{s},
|
|
Logger: &singLogger{
|
|
tag: "tun-stack",
|
|
zapLogger: s.Logger,
|
|
},
|
|
ForwarderBindInterface: true,
|
|
InterfaceFinder: &interfaceFinder{},
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create tun stack: %w", err)
|
|
}
|
|
defer tunStack.Close()
|
|
return tunStack.(tun.StackRunner).Run()
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
type interfaceFinder struct{}
|
|
|
|
var _ control.InterfaceFinder = (*interfaceFinder)(nil)
|
|
|
|
func (f *interfaceFinder) InterfaceIndexByName(name string) (int, error) {
|
|
ifce, err := net.InterfaceByName(name)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
return ifce.Index, nil
|
|
}
|
|
|
|
func (f *interfaceFinder) InterfaceNameByIndex(index int) (string, error) {
|
|
ifce, err := net.InterfaceByIndex(index)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return ifce.Name, nil
|
|
}
|