mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-02-04 07:20:40 +00:00
Add auto_route and auto_detect_interface for linux
This commit is contained in:
parent
4432cc2253
commit
638f8a52d1
|
@ -23,6 +23,9 @@ type Router interface {
|
|||
Exchange(ctx context.Context, message *dnsmessage.Message) (*dnsmessage.Message, error)
|
||||
Lookup(ctx context.Context, domain string, strategy C.DomainStrategy) ([]netip.Addr, error)
|
||||
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
|
||||
AutoDetectInterface() bool
|
||||
DefaultInterfaceName() string
|
||||
DefaultInterfaceIndex() int
|
||||
}
|
||||
|
||||
type Rule interface {
|
||||
|
|
23
common/dialer/auto_linux.go
Normal file
23
common/dialer/auto_linux.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package dialer
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func BindToInterface(router adapter.Router) control.Func {
|
||||
return func(network, address string, conn syscall.RawConn) error {
|
||||
interfaceName := router.DefaultInterfaceName()
|
||||
if interfaceName == "" {
|
||||
return nil
|
||||
}
|
||||
var innerErr error
|
||||
err := conn.Control(func(fd uintptr) {
|
||||
innerErr = syscall.BindToDevice(int(fd), interfaceName)
|
||||
})
|
||||
return E.Errors(innerErr, err)
|
||||
}
|
||||
}
|
12
common/dialer/auto_other.go
Normal file
12
common/dialer/auto_other.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
//go:build !linux
|
||||
|
||||
package dialer
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
)
|
||||
|
||||
func BindToInterface(router adapter.Router) control.Func {
|
||||
return nil
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
|
@ -18,12 +19,15 @@ type DefaultDialer struct {
|
|||
net.ListenConfig
|
||||
}
|
||||
|
||||
func NewDefault(options option.DialerOptions) *DefaultDialer {
|
||||
func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDialer {
|
||||
var dialer net.Dialer
|
||||
var listener net.ListenConfig
|
||||
if options.BindInterface != "" {
|
||||
dialer.Control = control.Append(dialer.Control, control.BindToInterface(options.BindInterface))
|
||||
listener.Control = control.Append(listener.Control, control.BindToInterface(options.BindInterface))
|
||||
} else if router.AutoDetectInterface() {
|
||||
dialer.Control = BindToInterface(router)
|
||||
listener.Control = BindToInterface(router)
|
||||
}
|
||||
if options.RoutingMark != 0 {
|
||||
dialer.Control = control.Append(dialer.Control, control.RoutingMark(options.RoutingMark))
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
func New(router adapter.Router, options option.DialerOptions) N.Dialer {
|
||||
if options.Detour == "" {
|
||||
return NewDefault(options)
|
||||
return NewDefault(router, options)
|
||||
} else {
|
||||
return NewDetour(router, options.Detour)
|
||||
}
|
||||
|
|
9
common/iffmonitor/monitor.go
Normal file
9
common/iffmonitor/monitor.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package iffmonitor
|
||||
|
||||
import "github.com/sagernet/sing-box/adapter"
|
||||
|
||||
type InterfaceMonitor interface {
|
||||
adapter.Service
|
||||
DefaultInterfaceName() string
|
||||
DefaultInterfaceIndex() int
|
||||
}
|
100
common/iffmonitor/monitor_linux.go
Normal file
100
common/iffmonitor/monitor_linux.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package iffmonitor
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
var _ InterfaceMonitor = (*monitor)(nil)
|
||||
|
||||
type monitor struct {
|
||||
logger log.Logger
|
||||
defaultInterfaceName string
|
||||
defaultInterfaceIndex int
|
||||
update chan netlink.RouteUpdate
|
||||
close chan struct{}
|
||||
}
|
||||
|
||||
func New(logger log.Logger) (InterfaceMonitor, error) {
|
||||
return &monitor{
|
||||
logger: logger,
|
||||
update: make(chan netlink.RouteUpdate, 2),
|
||||
close: make(chan struct{}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *monitor) Start() error {
|
||||
err := netlink.RouteSubscribe(m.update, m.close)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = m.checkUpdate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *monitor) loopUpdate() {
|
||||
for {
|
||||
select {
|
||||
case <-m.close:
|
||||
return
|
||||
case <-m.update:
|
||||
err := m.checkUpdate()
|
||||
if err != nil {
|
||||
m.logger.Error(E.Cause(err, "check default interface"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *monitor) checkUpdate() error {
|
||||
routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, route := range routes {
|
||||
if route.Dst != nil {
|
||||
continue
|
||||
}
|
||||
var link netlink.Link
|
||||
link, err = netlink.LinkByIndex(route.LinkIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if link.Type() == "tuntap" {
|
||||
continue
|
||||
}
|
||||
|
||||
m.defaultInterfaceName = link.Attrs().Name
|
||||
m.defaultInterfaceIndex = link.Attrs().Index
|
||||
|
||||
m.logger.Info("updated default interface ", m.defaultInterfaceName, ", index ", m.defaultInterfaceIndex)
|
||||
return nil
|
||||
}
|
||||
return E.New("no route to internet")
|
||||
}
|
||||
|
||||
func (m *monitor) Close() error {
|
||||
select {
|
||||
case <-m.close:
|
||||
return os.ErrClosed
|
||||
default:
|
||||
}
|
||||
close(m.close)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *monitor) DefaultInterfaceName() string {
|
||||
return m.defaultInterfaceName
|
||||
}
|
||||
|
||||
func (m *monitor) DefaultInterfaceIndex() int {
|
||||
return m.defaultInterfaceIndex
|
||||
}
|
13
common/iffmonitor/monitor_other.go
Normal file
13
common/iffmonitor/monitor_other.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
//go:build !linux
|
||||
|
||||
package iffmonitor
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
)
|
||||
|
||||
func New(logger log.Logger) (InterfaceMonitor, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package tun
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
|
@ -15,7 +16,7 @@ func Open(name string) (uintptr, error) {
|
|||
return uintptr(tunFd), nil
|
||||
}
|
||||
|
||||
func Configure(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32) error {
|
||||
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
|
||||
|
@ -47,5 +48,66 @@ func Configure(name string, inet4Address netip.Prefix, inet6Address netip.Prefix
|
|||
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
|
||||
}
|
||||
|
|
|
@ -3,9 +3,18 @@
|
|||
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
|
||||
}
|
||||
|
|
|
@ -236,7 +236,7 @@ func (a *myInboundAdapter) NewError(ctx context.Context, err error) {
|
|||
|
||||
func NewError(logger log.Logger, ctx context.Context, err error) {
|
||||
common.Close(err)
|
||||
if E.IsClosed(err) {
|
||||
if E.IsClosed(err) || E.IsCanceled(err) {
|
||||
logger.WithContext(ctx).Debug("connection closed")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -32,8 +32,9 @@ type Tun struct {
|
|||
logger log.Logger
|
||||
options option.TunInboundOptions
|
||||
|
||||
tunFd uintptr
|
||||
tun *tun.GVisorTun
|
||||
tunName string
|
||||
tunFd uintptr
|
||||
tun *tun.GVisorTun
|
||||
}
|
||||
|
||||
func NewTun(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.TunInboundOptions) (*Tun, error) {
|
||||
|
@ -70,10 +71,11 @@ func (t *Tun) Start() error {
|
|||
if err != nil {
|
||||
return E.Cause(err, "create tun interface")
|
||||
}
|
||||
err = tun.Configure(tunName, netip.Prefix(t.options.Inet4Address), netip.Prefix(t.options.Inet6Address), mtu)
|
||||
err = tun.Configure(tunName, netip.Prefix(t.options.Inet4Address), netip.Prefix(t.options.Inet6Address), mtu, t.options.AutoRoute)
|
||||
if err != nil {
|
||||
return E.Cause(err, "configure tun interface")
|
||||
}
|
||||
t.tunName = tunName
|
||||
t.tunFd = tunFd
|
||||
t.tun = tun.NewGVisor(t.ctx, tunFd, mtu, t)
|
||||
err = t.tun.Start()
|
||||
|
@ -85,6 +87,10 @@ func (t *Tun) Start() error {
|
|||
}
|
||||
|
||||
func (t *Tun) Close() error {
|
||||
err := tun.UnConfigure(t.tunName, netip.Prefix(t.options.Inet4Address), netip.Prefix(t.options.Inet6Address), t.options.AutoRoute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return E.Errors(
|
||||
t.tun.Close(),
|
||||
os.NewFile(t.tunFd, "tun").Close(),
|
||||
|
@ -99,6 +105,9 @@ func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata
|
|||
metadata.Network = C.NetworkTCP
|
||||
metadata.Source = upstreamMetadata.Source
|
||||
metadata.Destination = upstreamMetadata.Destination
|
||||
metadata.SniffEnabled = t.options.SniffEnabled
|
||||
metadata.SniffOverrideDestination = t.options.SniffOverrideDestination
|
||||
metadata.DomainStrategy = C.DomainStrategy(t.options.DomainStrategy)
|
||||
return t.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
|
@ -110,6 +119,9 @@ func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstre
|
|||
metadata.Network = C.NetworkUDP
|
||||
metadata.Source = upstreamMetadata.Source
|
||||
metadata.Destination = upstreamMetadata.Destination
|
||||
metadata.SniffEnabled = t.options.SniffEnabled
|
||||
metadata.SniffOverrideDestination = t.options.SniffOverrideDestination
|
||||
metadata.DomainStrategy = C.DomainStrategy(t.options.DomainStrategy)
|
||||
return t.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
|
|
|
@ -83,16 +83,20 @@ func (h *Inbound) UnmarshalJSON(bytes []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type ListenOptions struct {
|
||||
Listen ListenAddress `json:"listen"`
|
||||
ListenPort uint16 `json:"listen_port"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||
type InboundOptions struct {
|
||||
SniffEnabled bool `json:"sniff,omitempty"`
|
||||
SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
}
|
||||
|
||||
type ListenOptions struct {
|
||||
Listen ListenAddress `json:"listen"`
|
||||
ListenPort uint16 `json:"listen_port"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||
InboundOptions
|
||||
}
|
||||
|
||||
type SimpleInboundOptions struct {
|
||||
ListenOptions
|
||||
Users []auth.User `json:"users,omitempty"`
|
||||
|
@ -144,4 +148,6 @@ type TunInboundOptions struct {
|
|||
MTU uint32 `json:"mtu,omitempty"`
|
||||
Inet4Address ListenPrefix `json:"inet4_address"`
|
||||
Inet6Address ListenPrefix `json:"inet6_address"`
|
||||
AutoRoute bool `json:"auto_route"`
|
||||
InboundOptions
|
||||
}
|
||||
|
|
|
@ -9,10 +9,11 @@ import (
|
|||
)
|
||||
|
||||
type RouteOptions struct {
|
||||
GeoIP *GeoIPOptions `json:"geoip,omitempty"`
|
||||
Geosite *GeositeOptions `json:"geosite,omitempty"`
|
||||
Rules []Rule `json:"rules,omitempty"`
|
||||
Final string `json:"final,omitempty"`
|
||||
GeoIP *GeoIPOptions `json:"geoip,omitempty"`
|
||||
Geosite *GeositeOptions `json:"geosite,omitempty"`
|
||||
Rules []Rule `json:"rules,omitempty"`
|
||||
Final string `json:"final,omitempty"`
|
||||
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
|
||||
}
|
||||
|
||||
func (o RouteOptions) Equals(other RouteOptions) bool {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
@ -77,7 +78,12 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
|
|||
}
|
||||
|
||||
func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return NewConnection(ctx, h, conn, metadata)
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
outConn, err := h.DialContext(ctx, C.NetworkTCP, metadata.Destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bufio.CopyConn(ctx, conn, outConn)
|
||||
}
|
||||
|
||||
func (h *Direct) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/geoip"
|
||||
"github.com/sagernet/sing-box/common/geosite"
|
||||
"github.com/sagernet/sing-box/common/iffmonitor"
|
||||
"github.com/sagernet/sing-box/common/sniff"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
|
@ -62,6 +63,9 @@ type Router struct {
|
|||
defaultTransport adapter.DNSTransport
|
||||
transports []adapter.DNSTransport
|
||||
transportMap map[string]adapter.DNSTransport
|
||||
|
||||
autoDetectInterface bool
|
||||
interfaceMonitor iffmonitor.InterfaceMonitor
|
||||
}
|
||||
|
||||
func NewRouter(ctx context.Context, logger log.Logger, options option.RouteOptions, dnsOptions option.DNSOptions) (*Router, error) {
|
||||
|
@ -80,6 +84,7 @@ func NewRouter(ctx context.Context, logger log.Logger, options option.RouteOptio
|
|||
defaultDetour: options.Final,
|
||||
dnsClient: dns.NewClient(dnsOptions.DNSClientOptions),
|
||||
defaultDomainStrategy: C.DomainStrategy(dnsOptions.Strategy),
|
||||
autoDetectInterface: options.AutoDetectInterface,
|
||||
}
|
||||
for i, ruleOptions := range options.Rules {
|
||||
routeRule, err := NewRule(router, logger, ruleOptions)
|
||||
|
@ -181,6 +186,14 @@ func NewRouter(ctx context.Context, logger log.Logger, options option.RouteOptio
|
|||
router.defaultTransport = defaultTransport
|
||||
router.transports = transports
|
||||
router.transportMap = transportMap
|
||||
|
||||
if options.AutoDetectInterface {
|
||||
monitor, err := iffmonitor.New(router.logger)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create default interface monitor")
|
||||
}
|
||||
router.interfaceMonitor = monitor
|
||||
}
|
||||
return router, nil
|
||||
}
|
||||
|
||||
|
@ -303,6 +316,12 @@ func (r *Router) Start() error {
|
|||
r.geositeCache = nil
|
||||
r.geositeReader = nil
|
||||
}
|
||||
if r.interfaceMonitor != nil {
|
||||
err := r.interfaceMonitor.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -321,6 +340,7 @@ func (r *Router) Close() error {
|
|||
}
|
||||
return common.Close(
|
||||
common.PtrOrNil(r.geoIPReader),
|
||||
r.interfaceMonitor,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -399,7 +419,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||
}
|
||||
|
||||
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
if metadata.SniffEnabled {
|
||||
if metadata.SniffEnabled && metadata.Destination.Port == 443 {
|
||||
_buffer := buf.StackNewPacket()
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
|
@ -417,9 +437,9 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
|||
metadata.Destination.Fqdn = metadata.Domain
|
||||
}
|
||||
if metadata.Domain != "" {
|
||||
r.logger.WithContext(ctx).Info("sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
|
||||
r.logger.WithContext(ctx).Info("sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
|
||||
} else {
|
||||
r.logger.WithContext(ctx).Info("sniffed protocol: ", metadata.Protocol)
|
||||
r.logger.WithContext(ctx).Info("sniffed packet protocol: ", metadata.Protocol)
|
||||
}
|
||||
}
|
||||
conn = bufio.NewCachedPacketConn(conn, buffer, originDestination)
|
||||
|
@ -485,6 +505,24 @@ func (r *Router) matchDNS(ctx context.Context) adapter.DNSTransport {
|
|||
return r.defaultTransport
|
||||
}
|
||||
|
||||
func (r *Router) AutoDetectInterface() bool {
|
||||
return r.autoDetectInterface
|
||||
}
|
||||
|
||||
func (r *Router) DefaultInterfaceName() string {
|
||||
if r.interfaceMonitor == nil {
|
||||
return ""
|
||||
}
|
||||
return r.interfaceMonitor.DefaultInterfaceName()
|
||||
}
|
||||
|
||||
func (r *Router) DefaultInterfaceIndex() int {
|
||||
if r.interfaceMonitor == nil {
|
||||
return 0
|
||||
}
|
||||
return r.interfaceMonitor.DefaultInterfaceIndex()
|
||||
}
|
||||
|
||||
func hasGeoRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool {
|
||||
for _, rule := range rules {
|
||||
switch rule.Type {
|
||||
|
|
Loading…
Reference in a new issue