Skip to content

Commit

Permalink
feat: tun support auto-redirect, route-address-set and `route-exc…
Browse files Browse the repository at this point in the history
…lude-address-set`
  • Loading branch information
wwqgtxx committed Jun 17, 2024
1 parent 0738e18 commit 1067254
Show file tree
Hide file tree
Showing 28 changed files with 745 additions and 248 deletions.
50 changes: 50 additions & 0 deletions common/utils/callback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package utils

import (
"io"
"sync"

list "github.com/bahlo/generic-list-go"
)

type Callback[T any] struct {
list list.List[func(T)]
mutex sync.RWMutex
}

func NewCallback[T any]() *Callback[T] {
return &Callback[T]{}
}

func (c *Callback[T]) Register(item func(T)) io.Closer {
c.mutex.RLock()
defer c.mutex.RUnlock()
element := c.list.PushBack(item)
return &callbackCloser[T]{
element: element,
callback: c,
}
}

func (c *Callback[T]) Emit(item T) {
c.mutex.RLock()
defer c.mutex.RUnlock()
for element := c.list.Front(); element != nil; element = element.Next() {
go element.Value(item)
}
}

type callbackCloser[T any] struct {
element *list.Element[func(T)]
callback *Callback[T]
once sync.Once
}

func (c *callbackCloser[T]) Close() error {
c.once.Do(func() {
c.callback.mutex.Lock()
defer c.callback.mutex.Unlock()
c.callback.list.Remove(c.element)
})
return nil
}
8 changes: 5 additions & 3 deletions component/cidr/ipcidr_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ func (set *IpCidrSet) IsContainForString(ipString string) bool {
}

func (set *IpCidrSet) IsContain(ip netip.Addr) bool {
return set.toIPSet().Contains(ip.WithZone(""))
return set.ToIPSet().Contains(ip.WithZone(""))
}

func (set *IpCidrSet) Merge() error {
var b netipx.IPSetBuilder
b.AddSet(set.toIPSet())
b.AddSet(set.ToIPSet())
i, err := b.IPSet()
if err != nil {
return err
Expand All @@ -57,7 +57,9 @@ func (set *IpCidrSet) Merge() error {
return nil
}

func (set *IpCidrSet) toIPSet() *netipx.IPSet {
// ToIPSet not safe convert to *netipx.IPSet
// be careful, must be used after Merge
func (set *IpCidrSet) ToIPSet() *netipx.IPSet {
return (*netipx.IPSet)(unsafe.Pointer(set))
}

Expand Down
10 changes: 6 additions & 4 deletions component/iface/iface.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import (

type Interface struct {
Index int
MTU int
Name string
Addrs []netip.Prefix
Addresses []netip.Prefix
HardwareAddr net.HardwareAddr
}

Expand Down Expand Up @@ -61,8 +62,9 @@ func Interfaces() (map[string]*Interface, error) {

r[iface.Name] = &Interface{
Index: iface.Index,
MTU: iface.MTU,
Name: iface.Name,
Addrs: ipNets,
Addresses: ipNets,
HardwareAddr: iface.HardwareAddr,
}
}
Expand Down Expand Up @@ -92,7 +94,7 @@ func IsLocalIp(ip netip.Addr) (bool, error) {
return false, err
}
for _, iface := range ifaces {
for _, addr := range iface.Addrs {
for _, addr := range iface.Addresses {
if addr.Contains(ip) {
return true, nil
}
Expand Down Expand Up @@ -120,7 +122,7 @@ func (iface *Interface) PickIPv6Addr(destination netip.Addr) (netip.Prefix, erro
func (iface *Interface) pickIPAddr(destination netip.Addr, accept func(addr netip.Prefix) bool) (netip.Prefix, error) {
var fallback netip.Prefix

for _, addr := range iface.Addrs {
for _, addr := range iface.Addresses {
if !accept(addr) {
continue
}
Expand Down
109 changes: 65 additions & 44 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,31 +246,39 @@ type RawTun struct {
DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"`
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
AutoDetectInterface bool `yaml:"auto-detect-interface"`
RedirectToTun []string `yaml:"-" json:"-"`

MTU uint32 `yaml:"mtu" json:"mtu,omitempty"`
GSO bool `yaml:"gso" json:"gso,omitempty"`
GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"`
//Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4_address,omitempty"`
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"`
StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"`
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"`
IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"`
IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"`
AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"`
AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"`
AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"`
StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"`
RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"`
RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"`
RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"`
RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"`
IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"`
ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"`
IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"`
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"`
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"`
IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"`
IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"`
ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"`
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"`
UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"`
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`

Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4_route_address,omitempty"`
Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6_route_address,omitempty"`
Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4_route_exclude_address,omitempty"`
Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6_route_exclude_address,omitempty"`
IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"`
ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"`
IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"`
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"`
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"`
IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"`
IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"`
ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"`
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"`
UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"`
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
TableIndex int `yaml:"table-index" json:"table-index"`
}

type RawTuicServer struct {
Expand Down Expand Up @@ -564,13 +572,13 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
}
config.RuleProviders = ruleProviders

subRules, err := parseSubRules(rawCfg, proxies)
subRules, err := parseSubRules(rawCfg, proxies, ruleProviders)
if err != nil {
return nil, err
}
config.SubRules = subRules

rules, err := parseRules(rawCfg.Rule, proxies, subRules, "rules")
rules, err := parseRules(rawCfg.Rule, proxies, ruleProviders, subRules, "rules")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -666,7 +674,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
updater.ExternalUIURL = cfg.ExternalUIURL
}

cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun
return &General{
Inbound: Inbound{
Port: cfg.Port,
Expand Down Expand Up @@ -845,6 +852,7 @@ func parseListeners(cfg *RawConfig) (listeners map[string]C.InboundListener, err
}

func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes.RuleProvider, err error) {
RP.SetTunnel(T.Tunnel)
ruleProviders = map[string]providerTypes.RuleProvider{}
// parse rule provider
for name, mapping := range cfg.RuleProvider {
Expand All @@ -854,12 +862,11 @@ func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes.
}

ruleProviders[name] = rp
RP.SetRuleProvider(rp)
}
return
}

func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[string][]C.Rule, err error) {
func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy, ruleProviders map[string]providerTypes.RuleProvider) (subRules map[string][]C.Rule, err error) {
subRules = map[string][]C.Rule{}
for name := range cfg.SubRules {
subRules[name] = make([]C.Rule, 0)
Expand All @@ -869,7 +876,7 @@ func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[str
return nil, fmt.Errorf("sub-rule name is empty")
}
var rules []C.Rule
rules, err = parseRules(rawRules, proxies, subRules, fmt.Sprintf("sub-rules[%s]", name))
rules, err = parseRules(rawRules, proxies, ruleProviders, subRules, fmt.Sprintf("sub-rules[%s]", name))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -922,7 +929,7 @@ func verifySubRuleCircularReferences(n string, subRules map[string][]C.Rule, arr
return nil
}

func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[string][]C.Rule, format string) ([]C.Rule, error) {
func parseRules(rulesConfig []string, proxies map[string]C.Proxy, ruleProviders map[string]providerTypes.RuleProvider, subRules map[string][]C.Rule, format string) ([]C.Rule, error) {
var rules []C.Rule

// parse rules
Expand Down Expand Up @@ -971,6 +978,12 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s
return nil, fmt.Errorf("%s[%d] [%s] error: %s", format, idx, line, parseErr.Error())
}

for _, name := range parsed.ProviderNames() {
if _, ok := ruleProviders[name]; !ok {
return nil, fmt.Errorf("%s[%d] [%s] error: rule set [%s] not found", format, idx, line, name)
}
}

rules = append(rules, parsed)
}

Expand Down Expand Up @@ -1455,31 +1468,39 @@ func parseTun(rawTun RawTun, general *General) error {
DNSHijack: rawTun.DNSHijack,
AutoRoute: rawTun.AutoRoute,
AutoDetectInterface: rawTun.AutoDetectInterface,
RedirectToTun: rawTun.RedirectToTun,

MTU: rawTun.MTU,
GSO: rawTun.GSO,
GSOMaxSize: rawTun.GSOMaxSize,
Inet4Address: []netip.Prefix{tunAddressPrefix},
Inet6Address: rawTun.Inet6Address,
StrictRoute: rawTun.StrictRoute,

MTU: rawTun.MTU,
GSO: rawTun.GSO,
GSOMaxSize: rawTun.GSOMaxSize,
Inet4Address: []netip.Prefix{tunAddressPrefix},
Inet6Address: rawTun.Inet6Address,
IPRoute2TableIndex: rawTun.IPRoute2TableIndex,
IPRoute2RuleIndex: rawTun.IPRoute2RuleIndex,
AutoRedirect: rawTun.AutoRedirect,
AutoRedirectInputMark: rawTun.AutoRedirectInputMark,
AutoRedirectOutputMark: rawTun.AutoRedirectOutputMark,
StrictRoute: rawTun.StrictRoute,
RouteAddress: rawTun.RouteAddress,
RouteAddressSet: rawTun.RouteAddressSet,
RouteExcludeAddress: rawTun.RouteExcludeAddress,
RouteExcludeAddressSet: rawTun.RouteExcludeAddressSet,
IncludeInterface: rawTun.IncludeInterface,
ExcludeInterface: rawTun.ExcludeInterface,
IncludeUID: rawTun.IncludeUID,
IncludeUIDRange: rawTun.IncludeUIDRange,
ExcludeUID: rawTun.ExcludeUID,
ExcludeUIDRange: rawTun.ExcludeUIDRange,
IncludeAndroidUser: rawTun.IncludeAndroidUser,
IncludePackage: rawTun.IncludePackage,
ExcludePackage: rawTun.ExcludePackage,
EndpointIndependentNat: rawTun.EndpointIndependentNat,
UDPTimeout: rawTun.UDPTimeout,
FileDescriptor: rawTun.FileDescriptor,

Inet4RouteAddress: rawTun.Inet4RouteAddress,
Inet6RouteAddress: rawTun.Inet6RouteAddress,
Inet4RouteExcludeAddress: rawTun.Inet4RouteExcludeAddress,
Inet6RouteExcludeAddress: rawTun.Inet6RouteExcludeAddress,
IncludeInterface: rawTun.IncludeInterface,
ExcludeInterface: rawTun.ExcludeInterface,
IncludeUID: rawTun.IncludeUID,
IncludeUIDRange: rawTun.IncludeUIDRange,
ExcludeUID: rawTun.ExcludeUID,
ExcludeUIDRange: rawTun.ExcludeUIDRange,
IncludeAndroidUser: rawTun.IncludeAndroidUser,
IncludePackage: rawTun.IncludePackage,
ExcludePackage: rawTun.ExcludePackage,
EndpointIndependentNat: rawTun.EndpointIndependentNat,
UDPTimeout: rawTun.UDPTimeout,
FileDescriptor: rawTun.FileDescriptor,
TableIndex: rawTun.TableIndex,
}

return nil
Expand Down
8 changes: 7 additions & 1 deletion constant/provider/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ type RuleProvider interface {
Match(*constant.Metadata) bool
ShouldResolveIP() bool
ShouldFindProcess() bool
AsRule(adaptor string) constant.Rule
Strategy() any
}

// Rule Behavior
Expand Down Expand Up @@ -127,3 +127,9 @@ func (rf RuleFormat) String() string {
return "Unknown"
}
}

type Tunnel interface {
Providers() map[string]ProxyProvider
RuleProviders() map[string]RuleProvider
RuleUpdateCallback() *utils.Callback[RuleProvider]
}
1 change: 1 addition & 0 deletions constant/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,5 @@ type Rule interface {
Payload() string
ShouldResolveIP() bool
ShouldFindProcess() bool
ProviderNames() []string
}
13 changes: 8 additions & 5 deletions dns/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,17 @@ func (p geositePolicy) Match(domain string) []dnsClient {
}

type domainSetPolicy struct {
domainSetProvider provider.RuleProvider
dnsClients []dnsClient
tunnel provider.Tunnel
name string
dnsClients []dnsClient
}

func (p domainSetPolicy) Match(domain string) []dnsClient {
metadata := &C.Metadata{Host: domain}
if ok := p.domainSetProvider.Match(metadata); ok {
return p.dnsClients
if ruleProvider, ok := p.tunnel.RuleProviders()[p.name]; ok {
metadata := &C.Metadata{Host: domain}
if ok := ruleProvider.Match(metadata); ok {
return p.dnsClients
}
}
return nil
}
9 changes: 5 additions & 4 deletions dns/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ type Config struct {
Pool *fakeip.Pool
Hosts *trie.DomainTrie[resolver.HostValue]
Policy *orderedmap.OrderedMap[string, []NameServer]
RuleProviders map[string]provider.RuleProvider
Tunnel provider.Tunnel
CacheAlgorithm string
}

Expand Down Expand Up @@ -502,11 +502,12 @@ func NewResolver(config Config) *Resolver {
key := temp[1]
switch prefix {
case "rule-set":
if p, ok := config.RuleProviders[key]; ok {
if _, ok := config.Tunnel.RuleProviders()[key]; ok {
log.Debugln("Adding rule-set policy: %s ", key)
insertPolicy(domainSetPolicy{
domainSetProvider: p,
dnsClients: cacheTransform(nameserver),
tunnel: config.Tunnel,
name: key,
dnsClients: cacheTransform(nameserver),
})
continue
} else {
Expand Down
Loading

0 comments on commit 1067254

Please sign in to comment.