mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-09-15 21:00:27 +00:00
Improve OpenVPN & OpenConnect interoperability
This commit is contained in:
@@ -104,7 +104,7 @@ func (d *baseDevice) SetPacketWriter(writer PacketWriter) {
|
||||
func (d *baseDevice) writeOutbound(packetBuffers []*buf.Buffer) error {
|
||||
if d.packetWriter == nil {
|
||||
buf.ReleaseMulti(packetBuffers)
|
||||
return E.New("missing OpenConnect packet writer")
|
||||
return E.New("missing packet writer")
|
||||
}
|
||||
return d.packetWriter(packetBuffers)
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ package openconnect
|
||||
import E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
func newStackDevice(options DeviceOptions) (Device, error) {
|
||||
return nil, E.New("OpenConnect system:false requires the with_gvisor build tag")
|
||||
return nil, E.New("system:false requires the with_gvisor build tag")
|
||||
}
|
||||
|
||||
func newSystemStackDevice(options DeviceOptions) (Device, error) {
|
||||
return nil, E.New("OpenConnect system stack requires the with_gvisor build tag")
|
||||
return nil, E.New("system stack requires the with_gvisor build tag")
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sync"
|
||||
"syscall"
|
||||
@@ -14,7 +13,6 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing-tun/gtcpip/header"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -96,30 +94,21 @@ func (d *systemDevice) buildTunOptions() tun.Options {
|
||||
d.inet4Address = inet4Address
|
||||
d.inet6Address = inet6Address
|
||||
inet4Addresses, inet6Addresses := splitPrefixes(d.options.Configuration.Addresses)
|
||||
inet4Routes, inet6Routes := splitPrefixes(common.Map(d.options.Configuration.Routes, func(route Route) netip.Prefix { return route.Prefix }))
|
||||
inet4ExcludedRoutes, inet6ExcludedRoutes := splitPrefixes(common.Map(d.options.Configuration.ExcludedRoutes, func(route Route) netip.Prefix { return route.Prefix }))
|
||||
networkManager := service.FromContext[adapter.NetworkManager](d.options.Context)
|
||||
tunOptions := tun.Options{
|
||||
Name: d.options.Name,
|
||||
Inet4Address: inet4Addresses,
|
||||
Inet6Address: inet6Addresses,
|
||||
MTU: d.options.MTU,
|
||||
GSO: true,
|
||||
InterfaceScope: true,
|
||||
DNSAddress: d.options.Configuration.DNS,
|
||||
Inet4RouteAddress: inet4Routes,
|
||||
Inet6RouteAddress: inet6Routes,
|
||||
Inet4RouteExcludeAddress: inet4ExcludedRoutes,
|
||||
Inet6RouteExcludeAddress: inet6ExcludedRoutes,
|
||||
InterfaceMonitor: nil,
|
||||
InterfaceFinder: nil,
|
||||
Logger: d.options.Logger,
|
||||
IPRoute2TableIndex: tun.DefaultIPRoute2TableIndex,
|
||||
IPRoute2RuleIndex: tun.DefaultIPRoute2RuleIndex,
|
||||
EXP_DisableDNSHijack: true,
|
||||
}
|
||||
if runtime.GOOS == "darwin" {
|
||||
tunOptions.AutoRoute = true
|
||||
Name: d.options.Name,
|
||||
Inet4Address: inet4Addresses,
|
||||
Inet6Address: inet6Addresses,
|
||||
MTU: d.options.MTU,
|
||||
GSO: true,
|
||||
InterfaceScope: true,
|
||||
DNSMode: tun.DNSModeDisabled,
|
||||
InterfaceMonitor: nil,
|
||||
InterfaceFinder: nil,
|
||||
Logger: d.options.Logger,
|
||||
IPRoute2TableIndex: tun.DefaultIPRoute2TableIndex,
|
||||
IPRoute2RuleIndex: tun.DefaultIPRoute2RuleIndex,
|
||||
EXP_DisableDNSHijack: true,
|
||||
}
|
||||
if networkManager != nil {
|
||||
tunOptions.InterfaceMonitor = networkManager.InterfaceMonitor()
|
||||
@@ -252,13 +241,12 @@ func (d *systemDevice) UpdateConfiguration(configuration Configuration) error {
|
||||
return nil
|
||||
}
|
||||
if !slices.Equal(previousConfiguration.Addresses, configuration.Addresses) ||
|
||||
previousMTU != updatedMTU ||
|
||||
!slices.Equal(previousConfiguration.DNS, configuration.DNS) {
|
||||
previousMTU != updatedMTU {
|
||||
d.device.Close()
|
||||
d.device = nil
|
||||
return d.startLocked()
|
||||
}
|
||||
return d.device.UpdateRouteOptions(d.buildTunOptions())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *systemDevice) WriteInboundBuffers(packetBuffers []*buf.Buffer) error {
|
||||
@@ -270,7 +258,7 @@ func (d *systemDevice) writeBuffers(packetBuffers []*buf.Buffer) error {
|
||||
tunInterface := d.device
|
||||
d.stateAccess.RUnlock()
|
||||
if tunInterface == nil {
|
||||
return E.New("OpenConnect system device is not ready")
|
||||
return E.New("system device is not ready")
|
||||
}
|
||||
linuxTUN, isLinuxTUN := tunInterface.(tun.LinuxTUN)
|
||||
if isLinuxTUN {
|
||||
@@ -313,7 +301,7 @@ func (d *systemDevice) writePacket(packet []byte) error {
|
||||
tunInterface := d.device
|
||||
d.stateAccess.RUnlock()
|
||||
if tunInterface == nil {
|
||||
return E.New("OpenConnect system device is not ready")
|
||||
return E.New("system device is not ready")
|
||||
}
|
||||
if tun.PacketOffset == 0 {
|
||||
_, err := tunInterface.Write(packet)
|
||||
|
||||
+21
-37
@@ -3,7 +3,6 @@ package openvpn
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -54,13 +53,17 @@ type DeviceOptions struct {
|
||||
}
|
||||
|
||||
type Configuration struct {
|
||||
MTU uint32
|
||||
Address []netip.Prefix
|
||||
Routes []Route
|
||||
DNS []netip.Addr
|
||||
Topology string
|
||||
Interface string
|
||||
BlockIPv6 bool
|
||||
MTU uint32
|
||||
Address []netip.Prefix
|
||||
Routes []Route
|
||||
ExcludedRoutes []Route
|
||||
DNS []netip.Addr
|
||||
DNSServers []DNSServer
|
||||
SearchDomains []string
|
||||
DNSRoutes []string
|
||||
Topology string
|
||||
Interface string
|
||||
BlockIPv6 bool
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
@@ -69,6 +72,15 @@ type Route struct {
|
||||
Metric int
|
||||
}
|
||||
|
||||
type DNSServer struct {
|
||||
Priority int
|
||||
Addresses []netip.AddrPort
|
||||
ResolveDomains []string
|
||||
DNSSEC string
|
||||
Transport string
|
||||
SNI string
|
||||
}
|
||||
|
||||
func NewDevice(options DeviceOptions) (Device, error) {
|
||||
if !options.System {
|
||||
return newStackDevice(options)
|
||||
@@ -91,7 +103,7 @@ func (d *baseDevice) SetPacketWriter(writer PacketWriter) {
|
||||
func (d *baseDevice) writeOutbound(packetBuffers []*buf.Buffer) error {
|
||||
if d.packetWriter == nil {
|
||||
buf.ReleaseMulti(packetBuffers)
|
||||
return E.New("missing OpenVPN packet writer")
|
||||
return E.New("missing packet writer")
|
||||
}
|
||||
return d.packetWriter(packetBuffers)
|
||||
}
|
||||
@@ -192,34 +204,6 @@ func splitPrefixes(prefixes []netip.Prefix) ([]netip.Prefix, []netip.Prefix) {
|
||||
return inet4Prefixes, inet6Prefixes
|
||||
}
|
||||
|
||||
func splitRoutes(routes []Route) ([]netip.Prefix, []netip.Prefix) {
|
||||
var inet4Prefixes []netip.Prefix
|
||||
var inet6Prefixes []netip.Prefix
|
||||
for _, route := range routes {
|
||||
if route.Prefix.Addr().Is4() {
|
||||
inet4Prefixes = append(inet4Prefixes, route.Prefix)
|
||||
} else {
|
||||
inet6Prefixes = append(inet6Prefixes, route.Prefix)
|
||||
}
|
||||
}
|
||||
return inet4Prefixes, inet6Prefixes
|
||||
}
|
||||
|
||||
func routesWithBlockIPv6(configuration Configuration) []Route {
|
||||
routes := configuration.Routes
|
||||
if !configuration.BlockIPv6 {
|
||||
return routes
|
||||
}
|
||||
inet6DefaultRoute := netip.PrefixFrom(netip.IPv6Unspecified(), 0)
|
||||
for _, route := range routes {
|
||||
if route.Prefix == inet6DefaultRoute {
|
||||
return routes
|
||||
}
|
||||
}
|
||||
routes = append(slices.Clone(routes), Route{Prefix: inet6DefaultRoute})
|
||||
return routes
|
||||
}
|
||||
|
||||
func hasRouteOptions(routes []Route) bool {
|
||||
for _, route := range routes {
|
||||
if route.Gateway.IsValid() || route.Metric != 0 {
|
||||
|
||||
@@ -102,7 +102,7 @@ func (d *stackDevice) UpdateConfiguration(configuration Configuration) error {
|
||||
d.stateAccess.Lock()
|
||||
defer d.stateAccess.Unlock()
|
||||
if d.logRouteOptions && hasRouteOptions(configuration.Routes) {
|
||||
d.options.Logger.Debug("OpenVPN route gateway and metric options are not representable by the gVisor stack device; routes are installed by prefix")
|
||||
d.options.Logger.Debug("route gateway and metric options are not representable by the gVisor stack device; routes are installed by prefix")
|
||||
d.logRouteOptions = false
|
||||
}
|
||||
if configuration.MTU != 0 {
|
||||
@@ -184,7 +184,7 @@ func (d *stackDevice) writeBuffers(packetBuffers []*buf.Buffer) error {
|
||||
|
||||
func (d *stackDevice) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
if destination.IsIPv6() && d.blockIPv6Enabled() {
|
||||
return nil, E.New("IPv6 blocked by pushed OpenVPN block-ipv6")
|
||||
return nil, E.New("IPv6 blocked by pushed block-ipv6")
|
||||
}
|
||||
inet4Address, inet6Address := d.PortAddresses()
|
||||
address := tcpip.FullAddress{
|
||||
@@ -221,7 +221,7 @@ func (d *stackDevice) DialContext(ctx context.Context, network string, destinati
|
||||
|
||||
func (d *stackDevice) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if destination.IsIPv6() && d.blockIPv6Enabled() {
|
||||
return nil, E.New("IPv6 blocked by pushed OpenVPN block-ipv6")
|
||||
return nil, E.New("IPv6 blocked by pushed block-ipv6")
|
||||
}
|
||||
inet4Address, inet6Address := d.PortAddresses()
|
||||
bind := tcpip.FullAddress{
|
||||
|
||||
@@ -5,9 +5,9 @@ package openvpn
|
||||
import E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
func newStackDevice(options DeviceOptions) (Device, error) {
|
||||
return nil, E.New("OpenVPN system:false requires the with_gvisor build tag")
|
||||
return nil, E.New("system:false requires the with_gvisor build tag")
|
||||
}
|
||||
|
||||
func newSystemStackDevice(options DeviceOptions) (Device, error) {
|
||||
return nil, E.New("OpenVPN system stack requires the with_gvisor build tag")
|
||||
return nil, E.New("system stack requires the with_gvisor build tag")
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sync"
|
||||
"syscall"
|
||||
@@ -30,14 +29,13 @@ const (
|
||||
|
||||
type systemDevice struct {
|
||||
baseDevice
|
||||
stateAccess sync.RWMutex
|
||||
options DeviceOptions
|
||||
dialer N.Dialer
|
||||
device tun.Tun
|
||||
inet4Address netip.Addr
|
||||
inet6Address netip.Addr
|
||||
logRouteOptions bool
|
||||
closed bool
|
||||
stateAccess sync.RWMutex
|
||||
options DeviceOptions
|
||||
dialer N.Dialer
|
||||
device tun.Tun
|
||||
inet4Address netip.Addr
|
||||
inet6Address netip.Addr
|
||||
closed bool
|
||||
}
|
||||
|
||||
func newSystemDevice(options DeviceOptions) (*systemDevice, error) {
|
||||
@@ -55,11 +53,10 @@ func newSystemDevice(options DeviceOptions) (*systemDevice, error) {
|
||||
}
|
||||
inet4Address, inet6Address := firstAddresses(options.Configuration.Address)
|
||||
return &systemDevice{
|
||||
options: options,
|
||||
dialer: interfaceDialer,
|
||||
inet4Address: inet4Address,
|
||||
inet6Address: inet6Address,
|
||||
logRouteOptions: true,
|
||||
options: options,
|
||||
dialer: interfaceDialer,
|
||||
inet4Address: inet4Address,
|
||||
inet6Address: inet6Address,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -97,13 +94,6 @@ func (d *systemDevice) buildTunOptions() tun.Options {
|
||||
d.inet4Address = inet4Address
|
||||
d.inet6Address = inet6Address
|
||||
inet4Addresses, inet6Addresses := splitPrefixes(d.options.Configuration.Address)
|
||||
if d.options.Configuration.BlockIPv6 && len(inet6Addresses) == 0 {
|
||||
inet6Addresses = append(inet6Addresses, netip.MustParsePrefix("fddd:1194:1194:1194::2/64"))
|
||||
}
|
||||
routes := routesWithBlockIPv6(d.options.Configuration)
|
||||
inet4Routes, inet6Routes := splitRoutes(routes)
|
||||
inet4Gateway, _ := systemRouteGateway(routes, true)
|
||||
inet6Gateway, _ := systemRouteGateway(routes, false)
|
||||
networkManager := service.FromContext[adapter.NetworkManager](d.options.Context)
|
||||
tunOptions := tun.Options{
|
||||
Name: d.options.Name,
|
||||
@@ -112,11 +102,7 @@ func (d *systemDevice) buildTunOptions() tun.Options {
|
||||
MTU: d.options.MTU,
|
||||
GSO: true,
|
||||
InterfaceScope: true,
|
||||
DNSAddress: d.options.Configuration.DNS,
|
||||
Inet4Gateway: inet4Gateway,
|
||||
Inet6Gateway: inet6Gateway,
|
||||
Inet4RouteAddress: inet4Routes,
|
||||
Inet6RouteAddress: inet6Routes,
|
||||
DNSMode: tun.DNSModeDisabled,
|
||||
InterfaceMonitor: nil,
|
||||
InterfaceFinder: nil,
|
||||
Logger: d.options.Logger,
|
||||
@@ -124,9 +110,6 @@ func (d *systemDevice) buildTunOptions() tun.Options {
|
||||
IPRoute2RuleIndex: tun.DefaultIPRoute2RuleIndex,
|
||||
EXP_DisableDNSHijack: true,
|
||||
}
|
||||
if runtime.GOOS == "darwin" {
|
||||
tunOptions.AutoRoute = true
|
||||
}
|
||||
if networkManager != nil {
|
||||
tunOptions.InterfaceMonitor = networkManager.InterfaceMonitor()
|
||||
tunOptions.InterfaceFinder = networkManager.InterfaceFinder()
|
||||
@@ -134,43 +117,6 @@ func (d *systemDevice) buildTunOptions() tun.Options {
|
||||
return tunOptions
|
||||
}
|
||||
|
||||
func systemRouteGateway(routes []Route, ipv4 bool) (netip.Addr, bool) {
|
||||
var gateway netip.Addr
|
||||
var hasGateway bool
|
||||
var hasMissingGateway bool
|
||||
var gatewayUnrepresentable bool
|
||||
var metricUnrepresentable bool
|
||||
for _, route := range routes {
|
||||
if route.Prefix.Addr().Is4() != ipv4 {
|
||||
continue
|
||||
}
|
||||
if route.Metric != 0 {
|
||||
metricUnrepresentable = true
|
||||
}
|
||||
if !route.Gateway.IsValid() {
|
||||
hasMissingGateway = true
|
||||
continue
|
||||
}
|
||||
if route.Gateway.Is4() != ipv4 {
|
||||
gatewayUnrepresentable = true
|
||||
continue
|
||||
}
|
||||
if !hasGateway {
|
||||
gateway = route.Gateway
|
||||
hasGateway = true
|
||||
} else if gateway != route.Gateway {
|
||||
gatewayUnrepresentable = true
|
||||
}
|
||||
}
|
||||
if hasGateway && hasMissingGateway {
|
||||
gatewayUnrepresentable = true
|
||||
}
|
||||
if gatewayUnrepresentable {
|
||||
gateway = netip.Addr{}
|
||||
}
|
||||
return gateway, gatewayUnrepresentable || metricUnrepresentable
|
||||
}
|
||||
|
||||
func (d *systemDevice) readLoop(tunInterface tun.Tun, mtu int) {
|
||||
linuxTUN, isLinuxTUN := tunInterface.(tun.LinuxTUN)
|
||||
if isLinuxTUN && linuxTUN.BatchSize() > 1 {
|
||||
@@ -295,13 +241,6 @@ func (d *systemDevice) readLoopDarwin(tunInterface tun.DarwinTUN) {
|
||||
func (d *systemDevice) UpdateConfiguration(configuration Configuration) error {
|
||||
d.stateAccess.Lock()
|
||||
defer d.stateAccess.Unlock()
|
||||
routes := routesWithBlockIPv6(configuration)
|
||||
_, hasUnrepresentableInet4RouteOptions := systemRouteGateway(routes, true)
|
||||
_, hasUnrepresentableInet6RouteOptions := systemRouteGateway(routes, false)
|
||||
if d.logRouteOptions && (hasUnrepresentableInet4RouteOptions || hasUnrepresentableInet6RouteOptions) {
|
||||
d.options.Logger.Debug("some OpenVPN route gateway or metric options are not representable by the system device; routes are installed by prefix")
|
||||
d.logRouteOptions = false
|
||||
}
|
||||
previousConfiguration := d.options.Configuration
|
||||
previousMTU := d.options.MTU
|
||||
updatedMTU := d.options.MTU
|
||||
@@ -317,14 +256,12 @@ func (d *systemDevice) UpdateConfiguration(configuration Configuration) error {
|
||||
return nil
|
||||
}
|
||||
if !slices.Equal(previousConfiguration.Address, configuration.Address) ||
|
||||
previousMTU != updatedMTU ||
|
||||
!slices.Equal(previousConfiguration.DNS, configuration.DNS) ||
|
||||
previousConfiguration.BlockIPv6 != configuration.BlockIPv6 {
|
||||
previousMTU != updatedMTU {
|
||||
d.device.Close()
|
||||
d.device = nil
|
||||
return d.startLocked()
|
||||
}
|
||||
return d.device.UpdateRouteOptions(d.buildTunOptions())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *systemDevice) blockIPv6Enabled() bool {
|
||||
@@ -342,7 +279,7 @@ func (d *systemDevice) writeBuffers(packetBuffers []*buf.Buffer) error {
|
||||
tunInterface := d.device
|
||||
d.stateAccess.RUnlock()
|
||||
if tunInterface == nil {
|
||||
return E.New("OpenVPN system device is not ready")
|
||||
return E.New("system device is not ready")
|
||||
}
|
||||
linuxTUN, isLinuxTUN := tunInterface.(tun.LinuxTUN)
|
||||
if isLinuxTUN {
|
||||
@@ -385,7 +322,7 @@ func (d *systemDevice) writePacket(packet []byte) error {
|
||||
tunInterface := d.device
|
||||
d.stateAccess.RUnlock()
|
||||
if tunInterface == nil {
|
||||
return E.New("OpenVPN system device is not ready")
|
||||
return E.New("system device is not ready")
|
||||
}
|
||||
if tun.PacketOffset == 0 {
|
||||
_, err := tunInterface.Write(packet)
|
||||
|
||||
Reference in New Issue
Block a user