From bc33cd563061da24e5cee04cfe213a6893a08fb7 Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Tue, 12 Apr 2022 00:31:04 +0800 Subject: [PATCH] Refactor: fakeip pool use netip.Prefix, supports ipv6 range --- component/fakeip/cachefile.go | 33 ++++--- component/fakeip/memory.go | 37 ++++---- component/fakeip/pool.go | 167 ++++++++++++++++++---------------- component/fakeip/pool_test.go | 118 +++++++++++++++--------- config/config.go | 4 +- dns/enhancer.go | 22 +++-- dns/middleware.go | 10 +- dns/util.go | 17 ++++ listener/tun/tun_adapter.go | 4 +- 9 files changed, 240 insertions(+), 172 deletions(-) diff --git a/component/fakeip/cachefile.go b/component/fakeip/cachefile.go index 9e5f22f4..c31d751f 100644 --- a/component/fakeip/cachefile.go +++ b/component/fakeip/cachefile.go @@ -1,7 +1,7 @@ package fakeip import ( - "net" + "net/netip" "github.com/Dreamacro/clash/component/profile/cachefile" ) @@ -11,22 +11,27 @@ type cachefileStore struct { } // GetByHost implements store.GetByHost -func (c *cachefileStore) GetByHost(host string) (net.IP, bool) { +func (c *cachefileStore) GetByHost(host string) (netip.Addr, bool) { elm := c.cache.GetFakeip([]byte(host)) if elm == nil { - return nil, false + return netip.Addr{}, false + } + + if len(elm) == 4 { + return netip.AddrFrom4(*(*[4]byte)(elm)), true + } else { + return netip.AddrFrom16(*(*[16]byte)(elm)), true } - return net.IP(elm), true } // PutByHost implements store.PutByHost -func (c *cachefileStore) PutByHost(host string, ip net.IP) { - c.cache.PutFakeip([]byte(host), ip) +func (c *cachefileStore) PutByHost(host string, ip netip.Addr) { + c.cache.PutFakeip([]byte(host), ip.AsSlice()) } // GetByIP implements store.GetByIP -func (c *cachefileStore) GetByIP(ip net.IP) (string, bool) { - elm := c.cache.GetFakeip(ip.To4()) +func (c *cachefileStore) GetByIP(ip netip.Addr) (string, bool) { + elm := c.cache.GetFakeip(ip.AsSlice()) if elm == nil { return "", false } @@ -34,18 +39,18 @@ func (c *cachefileStore) GetByIP(ip net.IP) (string, bool) { } // PutByIP implements store.PutByIP -func (c *cachefileStore) PutByIP(ip net.IP, host string) { - c.cache.PutFakeip(ip.To4(), []byte(host)) +func (c *cachefileStore) PutByIP(ip netip.Addr, host string) { + c.cache.PutFakeip(ip.AsSlice(), []byte(host)) } // DelByIP implements store.DelByIP -func (c *cachefileStore) DelByIP(ip net.IP) { - ip = ip.To4() - c.cache.DelFakeipPair(ip, c.cache.GetFakeip(ip.To4())) +func (c *cachefileStore) DelByIP(ip netip.Addr) { + addr := ip.AsSlice() + c.cache.DelFakeipPair(addr, c.cache.GetFakeip(addr)) } // Exist implements store.Exist -func (c *cachefileStore) Exist(ip net.IP) bool { +func (c *cachefileStore) Exist(ip netip.Addr) bool { _, exist := c.GetByIP(ip) return exist } diff --git a/component/fakeip/memory.go b/component/fakeip/memory.go index 2568b1d9..5566ce48 100644 --- a/component/fakeip/memory.go +++ b/component/fakeip/memory.go @@ -1,35 +1,35 @@ package fakeip import ( - "net" + "net/netip" "github.com/Dreamacro/clash/common/cache" ) type memoryStore struct { - cacheIP *cache.LruCache[string, net.IP] - cacheHost *cache.LruCache[uint32, string] + cacheIP *cache.LruCache[string, netip.Addr] + cacheHost *cache.LruCache[netip.Addr, string] } // GetByHost implements store.GetByHost -func (m *memoryStore) GetByHost(host string) (net.IP, bool) { +func (m *memoryStore) GetByHost(host string) (netip.Addr, bool) { if ip, exist := m.cacheIP.Get(host); exist { // ensure ip --> host on head of linked list - m.cacheHost.Get(ipToUint(ip.To4())) + m.cacheHost.Get(ip) return ip, true } - return nil, false + return netip.Addr{}, false } // PutByHost implements store.PutByHost -func (m *memoryStore) PutByHost(host string, ip net.IP) { +func (m *memoryStore) PutByHost(host string, ip netip.Addr) { m.cacheIP.Set(host, ip) } // GetByIP implements store.GetByIP -func (m *memoryStore) GetByIP(ip net.IP) (string, bool) { - if host, exist := m.cacheHost.Get(ipToUint(ip.To4())); exist { +func (m *memoryStore) GetByIP(ip netip.Addr) (string, bool) { + if host, exist := m.cacheHost.Get(ip); exist { // ensure host --> ip on head of linked list m.cacheIP.Get(host) return host, true @@ -39,22 +39,21 @@ func (m *memoryStore) GetByIP(ip net.IP) (string, bool) { } // PutByIP implements store.PutByIP -func (m *memoryStore) PutByIP(ip net.IP, host string) { - m.cacheHost.Set(ipToUint(ip.To4()), host) +func (m *memoryStore) PutByIP(ip netip.Addr, host string) { + m.cacheHost.Set(ip, host) } // DelByIP implements store.DelByIP -func (m *memoryStore) DelByIP(ip net.IP) { - ipNum := ipToUint(ip.To4()) - if host, exist := m.cacheHost.Get(ipNum); exist { +func (m *memoryStore) DelByIP(ip netip.Addr) { + if host, exist := m.cacheHost.Get(ip); exist { m.cacheIP.Delete(host) } - m.cacheHost.Delete(ipNum) + m.cacheHost.Delete(ip) } // Exist implements store.Exist -func (m *memoryStore) Exist(ip net.IP) bool { - return m.cacheHost.Exist(ipToUint(ip.To4())) +func (m *memoryStore) Exist(ip netip.Addr) bool { + return m.cacheHost.Exist(ip) } // CloneTo implements store.CloneTo @@ -74,7 +73,7 @@ func (m *memoryStore) FlushFakeIP() error { func newMemoryStore(size int) *memoryStore { return &memoryStore{ - cacheIP: cache.NewLRUCache[string, net.IP](cache.WithSize[string, net.IP](size)), - cacheHost: cache.NewLRUCache[uint32, string](cache.WithSize[uint32, string](size)), + cacheIP: cache.NewLRUCache[string, netip.Addr](cache.WithSize[string, netip.Addr](size)), + cacheHost: cache.NewLRUCache[netip.Addr, string](cache.WithSize[netip.Addr, string](size)), } } diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index afc1691b..2b887fc3 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -2,39 +2,52 @@ package fakeip import ( "errors" - "net" + "math/bits" + "net/netip" "sync" + _ "unsafe" "github.com/Dreamacro/clash/component/profile/cachefile" "github.com/Dreamacro/clash/component/trie" ) +//go:linkname beUint64 net/netip.beUint64 +func beUint64(b []byte) uint64 + +//go:linkname bePutUint64 net/netip.bePutUint64 +func bePutUint64(b []byte, v uint64) + +type uint128 struct { + hi uint64 + lo uint64 +} + type store interface { - GetByHost(host string) (net.IP, bool) - PutByHost(host string, ip net.IP) - GetByIP(ip net.IP) (string, bool) - PutByIP(ip net.IP, host string) - DelByIP(ip net.IP) - Exist(ip net.IP) bool + GetByHost(host string) (netip.Addr, bool) + PutByHost(host string, ip netip.Addr) + GetByIP(ip netip.Addr) (string, bool) + PutByIP(ip netip.Addr, host string) + DelByIP(ip netip.Addr) + Exist(ip netip.Addr) bool CloneTo(store) FlushFakeIP() error } // Pool is a implementation about fake ip generator without storage type Pool struct { - max uint32 - min uint32 - gateway uint32 - broadcast uint32 - offset uint32 - mux sync.Mutex - host *trie.DomainTrie[bool] - ipnet *net.IPNet - store store + gateway netip.Addr + first netip.Addr + last netip.Addr + offset netip.Addr + cycle bool + mux sync.Mutex + host *trie.DomainTrie[bool] + ipnet *netip.Prefix + store store } // Lookup return a fake ip with host -func (p *Pool) Lookup(host string) net.IP { +func (p *Pool) Lookup(host string) netip.Addr { p.mux.Lock() defer p.mux.Unlock() if ip, exist := p.store.GetByHost(host); exist { @@ -47,14 +60,10 @@ func (p *Pool) Lookup(host string) net.IP { } // LookBack return host with the fake ip -func (p *Pool) LookBack(ip net.IP) (string, bool) { +func (p *Pool) LookBack(ip netip.Addr) (string, bool) { p.mux.Lock() defer p.mux.Unlock() - if ip = ip.To4(); ip == nil { - return "", false - } - return p.store.GetByIP(ip) } @@ -67,29 +76,25 @@ func (p *Pool) ShouldSkipped(domain string) bool { } // Exist returns if given ip exists in fake-ip pool -func (p *Pool) Exist(ip net.IP) bool { +func (p *Pool) Exist(ip netip.Addr) bool { p.mux.Lock() defer p.mux.Unlock() - if ip = ip.To4(); ip == nil { - return false - } - return p.store.Exist(ip) } // Gateway return gateway ip -func (p *Pool) Gateway() net.IP { - return uintToIP(p.gateway) +func (p *Pool) Gateway() netip.Addr { + return p.gateway } -// Broadcast return broadcast ip -func (p *Pool) Broadcast() net.IP { - return uintToIP(p.broadcast) +// Broadcast return the last ip +func (p *Pool) Broadcast() netip.Addr { + return p.last } // IPNet return raw ipnet -func (p *Pool) IPNet() *net.IPNet { +func (p *Pool) IPNet() *netip.Prefix { return p.ipnet } @@ -98,46 +103,28 @@ func (p *Pool) CloneFrom(o *Pool) { o.store.CloneTo(p.store) } -func (p *Pool) get(host string) net.IP { - current := p.offset - for { - p.offset = (p.offset + 1) % (p.max - p.min) - // Avoid infinite loops - if p.offset == current { - p.offset = (p.offset + 1) % (p.max - p.min) - ip := uintToIP(p.min + p.offset - 1) - p.store.DelByIP(ip) - break - } +func (p *Pool) get(host string) netip.Addr { + p.offset = p.offset.Next() - ip := uintToIP(p.min + p.offset - 1) - if !p.store.Exist(ip) { - break - } + if !p.offset.Less(p.last) { + p.cycle = true + p.offset = p.first } - ip := uintToIP(p.min + p.offset - 1) - p.store.PutByIP(ip, host) - return ip + + if p.cycle { + p.store.DelByIP(p.offset) + } + + p.store.PutByIP(p.offset, host) + return p.offset } func (p *Pool) FlushFakeIP() error { return p.store.FlushFakeIP() } -func ipToUint(ip net.IP) uint32 { - v := uint32(ip[0]) << 24 - v += uint32(ip[1]) << 16 - v += uint32(ip[2]) << 8 - v += uint32(ip[3]) - return v -} - -func uintToIP(v uint32) net.IP { - return net.IP{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} -} - type Options struct { - IPNet *net.IPNet + IPNet *netip.Prefix Host *trie.DomainTrie[bool] // Size sets the maximum number of entries in memory @@ -151,23 +138,25 @@ type Options struct { // New return Pool instance func New(options Options) (*Pool, error) { - min := ipToUint(options.IPNet.IP) + 3 + var ( + hostAddr = options.IPNet.Masked().Addr() + gateway = hostAddr.Next() + first = gateway.Next().Next() + last = add(hostAddr, 1<