Skip to content

Commit 09be5cb

Browse files
committed
feat: tun support auto-redirect, route-address-set and route-exclude-address-set
1 parent 0738e18 commit 09be5cb

File tree

28 files changed

+746
-248
lines changed

28 files changed

+746
-248
lines changed

common/utils/callback.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package utils
2+
3+
import (
4+
"io"
5+
"sync"
6+
7+
list "github.com/bahlo/generic-list-go"
8+
)
9+
10+
type Callback[T any] struct {
11+
list list.List[func(T)]
12+
mutex sync.RWMutex
13+
}
14+
15+
func NewCallback[T any]() *Callback[T] {
16+
return &Callback[T]{}
17+
}
18+
19+
func (c *Callback[T]) Register(item func(T)) io.Closer {
20+
c.mutex.RLock()
21+
defer c.mutex.RUnlock()
22+
element := c.list.PushBack(item)
23+
return &callbackCloser[T]{
24+
element: element,
25+
callback: c,
26+
}
27+
}
28+
29+
func (c *Callback[T]) Emit(item T) {
30+
c.mutex.RLock()
31+
defer c.mutex.RUnlock()
32+
for element := c.list.Front(); element != nil; element = element.Next() {
33+
go element.Value(item)
34+
}
35+
}
36+
37+
type callbackCloser[T any] struct {
38+
element *list.Element[func(T)]
39+
callback *Callback[T]
40+
once sync.Once
41+
}
42+
43+
func (c *callbackCloser[T]) Close() error {
44+
c.once.Do(func() {
45+
c.callback.mutex.Lock()
46+
defer c.callback.mutex.Unlock()
47+
c.callback.list.Remove(c.element)
48+
})
49+
return nil
50+
}

component/cidr/ipcidr_set.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ func (set *IpCidrSet) IsContainForString(ipString string) bool {
4343
}
4444

4545
func (set *IpCidrSet) IsContain(ip netip.Addr) bool {
46-
return set.toIPSet().Contains(ip.WithZone(""))
46+
return set.ToIPSet().Contains(ip.WithZone(""))
4747
}
4848

4949
func (set *IpCidrSet) Merge() error {
5050
var b netipx.IPSetBuilder
51-
b.AddSet(set.toIPSet())
51+
b.AddSet(set.ToIPSet())
5252
i, err := b.IPSet()
5353
if err != nil {
5454
return err
@@ -57,7 +57,9 @@ func (set *IpCidrSet) Merge() error {
5757
return nil
5858
}
5959

60-
func (set *IpCidrSet) toIPSet() *netipx.IPSet {
60+
// ToIPSet not safe convert to *netipx.IPSet
61+
// be careful, must be used after Merge
62+
func (set *IpCidrSet) ToIPSet() *netipx.IPSet {
6163
return (*netipx.IPSet)(unsafe.Pointer(set))
6264
}
6365

component/iface/iface.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import (
1111

1212
type Interface struct {
1313
Index int
14+
MTU int
1415
Name string
15-
Addrs []netip.Prefix
16+
Addresses []netip.Prefix
1617
HardwareAddr net.HardwareAddr
1718
}
1819

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

6263
r[iface.Name] = &Interface{
6364
Index: iface.Index,
65+
MTU: iface.MTU,
6466
Name: iface.Name,
65-
Addrs: ipNets,
67+
Addresses: ipNets,
6668
HardwareAddr: iface.HardwareAddr,
6769
}
6870
}
@@ -92,7 +94,7 @@ func IsLocalIp(ip netip.Addr) (bool, error) {
9294
return false, err
9395
}
9496
for _, iface := range ifaces {
95-
for _, addr := range iface.Addrs {
97+
for _, addr := range iface.Addresses {
9698
if addr.Contains(ip) {
9799
return true, nil
98100
}
@@ -120,7 +122,7 @@ func (iface *Interface) PickIPv6Addr(destination netip.Addr) (netip.Prefix, erro
120122
func (iface *Interface) pickIPAddr(destination netip.Addr, accept func(addr netip.Prefix) bool) (netip.Prefix, error) {
121123
var fallback netip.Prefix
122124

123-
for _, addr := range iface.Addrs {
125+
for _, addr := range iface.Addresses {
124126
if !accept(addr) {
125127
continue
126128
}

config/config.go

+65-44
Original file line numberDiff line numberDiff line change
@@ -246,31 +246,39 @@ type RawTun struct {
246246
DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"`
247247
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
248248
AutoDetectInterface bool `yaml:"auto-detect-interface"`
249-
RedirectToTun []string `yaml:"-" json:"-"`
250249

251250
MTU uint32 `yaml:"mtu" json:"mtu,omitempty"`
252251
GSO bool `yaml:"gso" json:"gso,omitempty"`
253252
GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"`
254253
//Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4_address,omitempty"`
255-
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"`
256-
StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"`
254+
Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"`
255+
IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"`
256+
IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"`
257+
AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"`
258+
AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"`
259+
AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"`
260+
StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"`
261+
RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"`
262+
RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"`
263+
RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"`
264+
RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"`
265+
IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"`
266+
ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
267+
IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"`
268+
IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"`
269+
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"`
270+
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"`
271+
IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"`
272+
IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"`
273+
ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"`
274+
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"`
275+
UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"`
276+
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
277+
257278
Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4_route_address,omitempty"`
258279
Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6_route_address,omitempty"`
259280
Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4_route_exclude_address,omitempty"`
260281
Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6_route_exclude_address,omitempty"`
261-
IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"`
262-
ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
263-
IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"`
264-
IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"`
265-
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"`
266-
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"`
267-
IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"`
268-
IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"`
269-
ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"`
270-
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"`
271-
UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"`
272-
FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
273-
TableIndex int `yaml:"table-index" json:"table-index"`
274282
}
275283

276284
type RawTuicServer struct {
@@ -564,13 +572,13 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
564572
}
565573
config.RuleProviders = ruleProviders
566574

567-
subRules, err := parseSubRules(rawCfg, proxies)
575+
subRules, err := parseSubRules(rawCfg, proxies, ruleProviders)
568576
if err != nil {
569577
return nil, err
570578
}
571579
config.SubRules = subRules
572580

573-
rules, err := parseRules(rawCfg.Rule, proxies, subRules, "rules")
581+
rules, err := parseRules(rawCfg.Rule, proxies, ruleProviders, subRules, "rules")
574582
if err != nil {
575583
return nil, err
576584
}
@@ -666,7 +674,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
666674
updater.ExternalUIURL = cfg.ExternalUIURL
667675
}
668676

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

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

856864
ruleProviders[name] = rp
857-
RP.SetRuleProvider(rp)
858865
}
859866
return
860867
}
861868

862-
func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[string][]C.Rule, err error) {
869+
func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy, ruleProviders map[string]providerTypes.RuleProvider) (subRules map[string][]C.Rule, err error) {
863870
subRules = map[string][]C.Rule{}
864871
for name := range cfg.SubRules {
865872
subRules[name] = make([]C.Rule, 0)
@@ -869,7 +876,7 @@ func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[str
869876
return nil, fmt.Errorf("sub-rule name is empty")
870877
}
871878
var rules []C.Rule
872-
rules, err = parseRules(rawRules, proxies, subRules, fmt.Sprintf("sub-rules[%s]", name))
879+
rules, err = parseRules(rawRules, proxies, ruleProviders, subRules, fmt.Sprintf("sub-rules[%s]", name))
873880
if err != nil {
874881
return nil, err
875882
}
@@ -922,7 +929,7 @@ func verifySubRuleCircularReferences(n string, subRules map[string][]C.Rule, arr
922929
return nil
923930
}
924931

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

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

981+
for _, name := range parsed.ProviderNames() {
982+
if _, ok := ruleProviders[name]; !ok {
983+
return nil, fmt.Errorf("%s[%d] [%s] error: rule set [%s] not found", format, idx, line, name)
984+
}
985+
}
986+
974987
rules = append(rules, parsed)
975988
}
976989

@@ -1455,31 +1468,39 @@ func parseTun(rawTun RawTun, general *General) error {
14551468
DNSHijack: rawTun.DNSHijack,
14561469
AutoRoute: rawTun.AutoRoute,
14571470
AutoDetectInterface: rawTun.AutoDetectInterface,
1458-
RedirectToTun: rawTun.RedirectToTun,
1459-
1460-
MTU: rawTun.MTU,
1461-
GSO: rawTun.GSO,
1462-
GSOMaxSize: rawTun.GSOMaxSize,
1463-
Inet4Address: []netip.Prefix{tunAddressPrefix},
1464-
Inet6Address: rawTun.Inet6Address,
1465-
StrictRoute: rawTun.StrictRoute,
1471+
1472+
MTU: rawTun.MTU,
1473+
GSO: rawTun.GSO,
1474+
GSOMaxSize: rawTun.GSOMaxSize,
1475+
Inet4Address: []netip.Prefix{tunAddressPrefix},
1476+
Inet6Address: rawTun.Inet6Address,
1477+
IPRoute2TableIndex: rawTun.IPRoute2TableIndex,
1478+
IPRoute2RuleIndex: rawTun.IPRoute2RuleIndex,
1479+
AutoRedirect: rawTun.AutoRedirect,
1480+
AutoRedirectInputMark: rawTun.AutoRedirectInputMark,
1481+
AutoRedirectOutputMark: rawTun.AutoRedirectOutputMark,
1482+
StrictRoute: rawTun.StrictRoute,
1483+
RouteAddress: rawTun.RouteAddress,
1484+
RouteAddressSet: rawTun.RouteAddressSet,
1485+
RouteExcludeAddress: rawTun.RouteExcludeAddress,
1486+
RouteExcludeAddressSet: rawTun.RouteExcludeAddressSet,
1487+
IncludeInterface: rawTun.IncludeInterface,
1488+
ExcludeInterface: rawTun.ExcludeInterface,
1489+
IncludeUID: rawTun.IncludeUID,
1490+
IncludeUIDRange: rawTun.IncludeUIDRange,
1491+
ExcludeUID: rawTun.ExcludeUID,
1492+
ExcludeUIDRange: rawTun.ExcludeUIDRange,
1493+
IncludeAndroidUser: rawTun.IncludeAndroidUser,
1494+
IncludePackage: rawTun.IncludePackage,
1495+
ExcludePackage: rawTun.ExcludePackage,
1496+
EndpointIndependentNat: rawTun.EndpointIndependentNat,
1497+
UDPTimeout: rawTun.UDPTimeout,
1498+
FileDescriptor: rawTun.FileDescriptor,
1499+
14661500
Inet4RouteAddress: rawTun.Inet4RouteAddress,
14671501
Inet6RouteAddress: rawTun.Inet6RouteAddress,
14681502
Inet4RouteExcludeAddress: rawTun.Inet4RouteExcludeAddress,
14691503
Inet6RouteExcludeAddress: rawTun.Inet6RouteExcludeAddress,
1470-
IncludeInterface: rawTun.IncludeInterface,
1471-
ExcludeInterface: rawTun.ExcludeInterface,
1472-
IncludeUID: rawTun.IncludeUID,
1473-
IncludeUIDRange: rawTun.IncludeUIDRange,
1474-
ExcludeUID: rawTun.ExcludeUID,
1475-
ExcludeUIDRange: rawTun.ExcludeUIDRange,
1476-
IncludeAndroidUser: rawTun.IncludeAndroidUser,
1477-
IncludePackage: rawTun.IncludePackage,
1478-
ExcludePackage: rawTun.ExcludePackage,
1479-
EndpointIndependentNat: rawTun.EndpointIndependentNat,
1480-
UDPTimeout: rawTun.UDPTimeout,
1481-
FileDescriptor: rawTun.FileDescriptor,
1482-
TableIndex: rawTun.TableIndex,
14831504
}
14841505

14851506
return nil

constant/provider/interface.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ type RuleProvider interface {
8484
Match(*constant.Metadata) bool
8585
ShouldResolveIP() bool
8686
ShouldFindProcess() bool
87-
AsRule(adaptor string) constant.Rule
87+
Strategy() any
8888
}
8989

9090
// Rule Behavior
@@ -127,3 +127,9 @@ func (rf RuleFormat) String() string {
127127
return "Unknown"
128128
}
129129
}
130+
131+
type Tunnel interface {
132+
Providers() map[string]ProxyProvider
133+
RuleProviders() map[string]RuleProvider
134+
RuleUpdateCallback() *utils.Callback[RuleProvider]
135+
}

constant/rule.go

+1
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,5 @@ type Rule interface {
116116
Payload() string
117117
ShouldResolveIP() bool
118118
ShouldFindProcess() bool
119+
ProviderNames() []string
119120
}

dns/policy.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,17 @@ func (p geositePolicy) Match(domain string) []dnsClient {
3737
}
3838

3939
type domainSetPolicy struct {
40-
domainSetProvider provider.RuleProvider
41-
dnsClients []dnsClient
40+
tunnel provider.Tunnel
41+
name string
42+
dnsClients []dnsClient
4243
}
4344

4445
func (p domainSetPolicy) Match(domain string) []dnsClient {
45-
metadata := &C.Metadata{Host: domain}
46-
if ok := p.domainSetProvider.Match(metadata); ok {
47-
return p.dnsClients
46+
if ruleProvider, ok := p.tunnel.RuleProviders()[p.name]; ok {
47+
metadata := &C.Metadata{Host: domain}
48+
if ok := ruleProvider.Match(metadata); ok {
49+
return p.dnsClients
50+
}
4851
}
4952
return nil
5053
}

dns/resolver.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ type Config struct {
414414
Pool *fakeip.Pool
415415
Hosts *trie.DomainTrie[resolver.HostValue]
416416
Policy *orderedmap.OrderedMap[string, []NameServer]
417-
RuleProviders map[string]provider.RuleProvider
417+
Tunnel provider.Tunnel
418418
CacheAlgorithm string
419419
}
420420

@@ -502,11 +502,12 @@ func NewResolver(config Config) *Resolver {
502502
key := temp[1]
503503
switch prefix {
504504
case "rule-set":
505-
if p, ok := config.RuleProviders[key]; ok {
505+
if _, ok := config.Tunnel.RuleProviders()[key]; ok {
506506
log.Debugln("Adding rule-set policy: %s ", key)
507507
insertPolicy(domainSetPolicy{
508-
domainSetProvider: p,
509-
dnsClients: cacheTransform(nameserver),
508+
tunnel: config.Tunnel,
509+
name: key,
510+
dnsClients: cacheTransform(nameserver),
510511
})
511512
continue
512513
} else {

0 commit comments

Comments
 (0)