@@ -47,6 +47,11 @@ const (
4747 notransport = "NoTransport"
4848
4949 maxiter = 100 // max number alg/nat evict iterations
50+
51+ // stale threshold for retaining expired ips from merge
52+ staleXipsThres = 24 * time .Hour
53+ // stale threshold for retaining expired domains from merge
54+ staleDomainThres = 24 * time .Hour
5055)
5156
5257type iptype int
@@ -134,7 +139,7 @@ func (a expaddr) sizes() (alive, tot int) {
134139 if a .ips == nil {
135140 return
136141 }
137- return len (a .get ( xalive )), len (a .ips )
142+ return len (a .alive ( )), len (a .ips )
138143}
139144
140145func (a expaddr ) all () []netip.Addr {
@@ -194,12 +199,14 @@ func expaddrDobSorter(a, b expaddr) int {
194199}
195200
196201type xips struct {
197- // protects pri, aux
202+ // protects pri, aux, past
198203 pmu sync.RWMutex
199204 // resolved expaddr(v6 or v4) by tid; may be nil
200205 pri map [string ]expaddr
201206 // resolved expaddr(v6 or v4) by secondary tid for a uid; may be nil
202207 aux map [string ]expaddr
208+ // recently expired ips within staleXipsThres captured from merge; tid+uid -> expaddr
209+ past map [string ]expaddr
203210}
204211
205212type xaddrstatus bool
@@ -247,8 +254,9 @@ func NewXips(tid, uid string, pri, sec []netip.Addr, ttl time.Time) *xips {
247254 }
248255
249256 x := & xips {
250- pri : make (map [string ]expaddr ),
251- aux : make (map [string ]expaddr ), // sec may be nil
257+ pri : make (map [string ]expaddr ),
258+ aux : make (map [string ]expaddr ), // sec may be nil
259+ past : make (map [string ]expaddr ),
252260 }
253261 now := time .Now ()
254262 // id == "" for dnsx.NoDNS, in which case pri should be empty
@@ -281,7 +289,7 @@ func (p *xips) String() string {
281289 }
282290 p .pmu .RLock ()
283291 defer p .pmu .RUnlock ()
284- return fmt .Sprintf ("xips: pri(%v) sec(%v)" , p .pri , p .aux )
292+ return fmt .Sprintf ("xips: pri(%v) sec(%v) past(%v) " , p .pri , p .aux , p . past )
285293}
286294
287295// secondary ips for all tids+uids
@@ -309,10 +317,17 @@ func (p *xips) allips(t xaddrtyp, s xaddrstatus) (out []netip.Addr) {
309317 }
310318 const all = 0
311319 addrs := append ([]expaddr {}, vals (g , all )... )
312-
313320 for _ , v := range core .Sort (addrs , expaddrDobSorter ) {
314321 out = append (out , v .get (s )... )
315322 }
323+ // staleXipsThres only applies to pastips, not pri/aux entries
324+ if s == xall && t == xpri {
325+ for _ , v := range core .Sort (vals (p .past , all ), expaddrDobSorter ) {
326+ if ! v .dob .IsZero () && time .Since (v .dob ) <= staleXipsThres {
327+ out = copyUniq (out , v .ips )
328+ }
329+ }
330+ }
316331 return
317332}
318333
@@ -486,6 +501,19 @@ func (p *xips) merge(q *xips) (szprialiv, szpri, szsecaliv, szsec int) {
486501 for qk , qv := range q .pri {
487502 pv := p .pri [qk ]
488503 if _ , y := pv .fresh (); ! y {
504+ // pv is not fresh; capture its stale ips before replacement
505+ if len (pv .ips ) > 0 && ! pv .dob .IsZero () && time .Since (pv .dob ) <= staleXipsThres {
506+ if existing , ok := p .past [qk ]; ok && time .Since (existing .dob ) <= staleXipsThres {
507+ existing .ips = copyUniq (existing .ips , pv .ips )
508+ if pv .dob .Before (existing .dob ) {
509+ existing .dob = pv .dob
510+ }
511+ p .past [qk ] = existing
512+ } else {
513+ p .past [qk ] = expaddr {ips : copyUniq (nil , pv .ips ), ttl : pv .ttl , dob : pv .dob }
514+ }
515+ }
516+
489517 p .pri [qk ] = qv // copy v from q into p
490518 szqaliv , szqpri := qv .sizes ()
491519 szprialiv += szqaliv
@@ -502,6 +530,19 @@ func (p *xips) merge(q *xips) (szprialiv, szpri, szsecaliv, szsec int) {
502530 // pv is younger, so pv's ips should come first (youngest first ordering)
503531 ips = copyUniq (pv .alive (), qv .alive ())
504532 }
533+ // capture stale (expired) ips from qv that would otherwise be lost
534+ if len (qv .ips ) > 0 && ! qv .dob .IsZero () && time .Since (qv .dob ) <= staleXipsThres {
535+ if existing , ok := p .past [qk ]; ok && time .Since (existing .dob ) <= staleXipsThres {
536+ existing .ips = copyUniq (existing .ips , qv .ips )
537+ if qv .dob .Before (existing .dob ) {
538+ existing .dob = qv .dob
539+ }
540+ p .past [qk ] = existing
541+ } else {
542+ p .past [qk ] = expaddr {ips : copyUniq (nil , qv .ips ), ttl : qv .ttl , dob : qv .dob }
543+ }
544+ }
545+
505546 if ! pv .fresherThan (qv ) {
506547 // ips from aa & v both get assigned the latest ttl
507548 // which is strictly incorrect, but for accounting
@@ -540,7 +581,7 @@ func (p *xips) merge(q *xips) (szprialiv, szpri, szsecaliv, szsec int) {
540581 if ! pv .fresherThan (qv ) {
541582 ttl = qv .ttl
542583 }
543- v := expaddr {ips , ttl , dob }
584+ v := expaddr {ips : ips , ttl : ttl , dob : dob }
544585 p .aux [qk ] = v
545586 szqaliv , szqsec := v .sizes ()
546587 szsecaliv += szqaliv
@@ -619,8 +660,9 @@ func expdomainsDobSorter(a, b expdomains) int {
619660
620661// xdomains tracks domains per tid+uid; there is no secondary (aux) store.
621662type xdomains struct {
622- pmu * sync.RWMutex
623- pri map [string ]expdomains // tid+uid -> domains
663+ pmu * sync.RWMutex
664+ pri map [string ]expdomains // tid+uid -> domains
665+ past map [string ]expdomains // recently expired domains within staleDomainThres captured from merge; tid+uid -> expdomains
624666}
625667
626668// NewXdomains returns a new xdomains object keyed by tid+uid.
@@ -634,8 +676,9 @@ func NewXdomains(tid, uid string, pri []string, ttl time.Time) *xdomains {
634676 }
635677
636678 x := & xdomains {
637- pmu : new (sync.RWMutex ),
638- pri : make (map [string ]expdomains ),
679+ pmu : new (sync.RWMutex ),
680+ pri : make (map [string ]expdomains ),
681+ past : make (map [string ]expdomains ),
639682 }
640683
641684 if len (pri ) > 0 { // pri may be nil
@@ -650,7 +693,7 @@ func (p *xdomains) String() string {
650693 }
651694 p .pmu .RLock ()
652695 defer p .pmu .RUnlock ()
653- return fmt .Sprintf ("xdomains: pri(%v)" , p .pri )
696+ return fmt .Sprintf ("xdomains: pri(%v) past(%v) " , p .pri , p . past )
654697}
655698
656699func (p * xdomains ) domainsFor (tid , uid string , forIP netip.Addr , s xaddrstatus ) (out []string ) {
@@ -682,11 +725,36 @@ func (p *xdomains) domainsFor(tid, uid string, forIP netip.Addr, s xaddrstatus)
682725 }
683726 out = append (out , v .get (s )... )
684727 }
685- } else if v , ok := p .pri [key ]; ok {
686- if settings .Debug {
687- ttls = append (ttls , v .ttl )
728+ // staleDomainThres only applies to pastdomains, not pri entries
729+ if s == xall {
730+ past := make ([]expdomains , 0 )
731+ for k , v := range p .past {
732+ if ! strings .HasSuffix (k , uid ) {
733+ continue
734+ }
735+ past = append (past , v )
736+ }
737+ for _ , v := range core .Sort (past , expdomainsDobSorter ) {
738+ if ! v .dob .IsZero () && time .Since (v .dob ) <= staleDomainThres {
739+ out = copyUniq (out , v .domains )
740+ }
741+ }
742+ }
743+ } else {
744+ if v , ok := p .pri [key ]; ok {
745+ if settings .Debug {
746+ ttls = append (ttls , v .ttl )
747+ }
748+ out = v .get (s )
749+ }
750+ // staleDomainThres only applies to pastdomains, not pri entries
751+ if s == xall {
752+ if v , ok := p .past [key ]; ok {
753+ if ! v .dob .IsZero () && time .Since (v .dob ) <= staleDomainThres {
754+ out = copyUniq (out , v .domains )
755+ }
756+ }
688757 }
689- out = v .get (s )
690758 }
691759
692760 if settings .Debug {
@@ -740,6 +808,19 @@ func (p *xdomains) merge(q *xdomains) (szprialiv, szpri int) {
740808 for qk , qv := range q .pri {
741809 pv := p .pri [qk ]
742810 if _ , y := pv .fresh (); ! y {
811+ // pv is not fresh; capture its stale domains before replacement
812+ if len (pv .domains ) > 0 && ! pv .dob .IsZero () && time .Since (pv .dob ) <= staleDomainThres {
813+ if existing , ok := p .past [qk ]; ok && time .Since (existing .dob ) <= staleDomainThres {
814+ existing .domains = copyUniq (existing .domains , pv .domains )
815+ if pv .dob .Before (existing .dob ) {
816+ existing .dob = pv .dob
817+ }
818+ p .past [qk ] = existing
819+ } else {
820+ p .past [qk ] = expdomains {domains : copyUniq (nil , pv .domains ), ttl : pv .ttl , dob : pv .dob }
821+ }
822+ }
823+
743824 p .pri [qk ] = qv // copy v from q into p
744825 szqaliv , szqpri := qv .sizes ()
745826 szprialiv += szqaliv
@@ -756,6 +837,19 @@ func (p *xdomains) merge(q *xdomains) (szprialiv, szpri int) {
756837 // pv is younger, so pv's domains should come first (youngest first ordering)
757838 doms = copyUniq (pv .get (xalive ), qv .get (xalive ))
758839 }
840+ // capture stale (expired) domains from qv that would otherwise be lost
841+ if len (qv .domains ) > 0 && ! qv .dob .IsZero () && time .Since (qv .dob ) <= staleDomainThres {
842+ if existing , ok := p .past [qk ]; ok && time .Since (existing .dob ) <= staleDomainThres {
843+ existing .domains = copyUniq (existing .domains , qv .domains )
844+ if qv .dob .Before (existing .dob ) {
845+ existing .dob = qv .dob
846+ }
847+ p .past [qk ] = existing
848+ } else {
849+ p .past [qk ] = expdomains {domains : copyUniq (nil , qv .domains ), ttl : qv .ttl , dob : qv .dob }
850+ }
851+ }
852+
759853 if ! pv .fresherThan (qv ) {
760854 pv .ttl = qv .ttl
761855 }
0 commit comments