cache: add dns cache in udp packet sender

reduce the cost of re-resolving DNS for each packet received and prevent the target IP from jumping between multiple resolution results
This commit is contained in:
wwqgtxx 2024-09-26 22:21:59 +08:00
parent 4fa15c6334
commit 43cb48231a
3 changed files with 31 additions and 18 deletions

View File

@ -298,7 +298,11 @@ type PacketSender interface {
// Send will send PacketAdapter nonblocking // Send will send PacketAdapter nonblocking
// the implement must call UDPPacket.Drop() inside Send // the implement must call UDPPacket.Drop() inside Send
Send(PacketAdapter) Send(PacketAdapter)
// Process is a blocking loop to send PacketAdapter to PacketConn and update the WriteBackProxy
Process(PacketConn, WriteBackProxy) Process(PacketConn, WriteBackProxy)
// ResolveUDP do a local resolve UDP dns blocking if metadata is not resolved
ResolveUDP(*Metadata) error
// Close stop the Process loop
Close() Close()
} }

View File

@ -7,7 +7,9 @@ import (
"net/netip" "net/netip"
"time" "time"
"github.com/metacubex/mihomo/common/lru"
N "github.com/metacubex/mihomo/common/net" N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/resolver"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
) )
@ -16,6 +18,7 @@ type packetSender struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
ch chan C.PacketAdapter ch chan C.PacketAdapter
cache *lru.LruCache[string, netip.Addr]
} }
// newPacketSender return a chan based C.PacketSender // newPacketSender return a chan based C.PacketSender
@ -27,6 +30,7 @@ func newPacketSender() C.PacketSender {
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
ch: ch, ch: ch,
cache: lru.New[string, netip.Addr](lru.WithSize[string, netip.Addr](senderCapacity)),
} }
} }
@ -39,7 +43,11 @@ func (s *packetSender) Process(pc C.PacketConn, proxy C.WriteBackProxy) {
if proxy != nil { if proxy != nil {
proxy.UpdateWriteBack(packet) proxy.UpdateWriteBack(packet)
} }
_ = handleUDPToRemote(packet, pc, packet.Metadata()) if err := s.ResolveUDP(packet.Metadata()); err != nil {
log.Warnln("[UDP] Resolve Ip error: %s", err)
} else {
_ = handleUDPToRemote(packet, pc, packet.Metadata())
}
packet.Drop() packet.Drop()
} }
} }
@ -79,11 +87,24 @@ func (s *packetSender) Close() {
s.dropAll() s.dropAll()
} }
func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error { func (s *packetSender) ResolveUDP(metadata *C.Metadata) (err error) {
if err := resolveUDP(metadata); err != nil { // local resolve UDP dns
return err if !metadata.Resolved() {
} ip, ok := s.cache.Get(metadata.Host)
if !ok {
ip, err = resolver.ResolveIP(s.ctx, metadata.Host)
if err != nil {
return err
}
s.cache.Set(metadata.Host, ip)
}
metadata.DstIP = ip
}
return nil
}
func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error {
addr := metadata.UDPAddr() addr := metadata.UDPAddr()
if addr == nil { if addr == nil {
return errors.New("udp addr invalid") return errors.New("udp addr invalid")

View File

@ -346,18 +346,6 @@ func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err erro
return return
} }
func resolveUDP(metadata *C.Metadata) error {
// local resolve UDP dns
if !metadata.Resolved() {
ip, err := resolver.ResolveIP(context.Background(), metadata.Host)
if err != nil {
return err
}
metadata.DstIP = ip
}
return nil
}
// processUDP starts a loop to handle udp packet // processUDP starts a loop to handle udp packet
func processUDP(queue chan C.PacketAdapter) { func processUDP(queue chan C.PacketAdapter) {
for conn := range queue { for conn := range queue {
@ -398,7 +386,7 @@ func handleUDPConn(packet C.PacketAdapter) {
sender, loaded := natTable.GetOrCreate(key, newPacketSender) sender, loaded := natTable.GetOrCreate(key, newPacketSender)
if !loaded { if !loaded {
dial := func() (C.PacketConn, C.WriteBackProxy, error) { dial := func() (C.PacketConn, C.WriteBackProxy, error) {
if err := resolveUDP(metadata); err != nil { if err := sender.ResolveUDP(metadata); err != nil {
log.Warnln("[UDP] Resolve Ip error: %s", err) log.Warnln("[UDP] Resolve Ip error: %s", err)
return nil, nil, err return nil, nil, err
} }