diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 0417e20c..53928696 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -88,7 +88,18 @@ func (p *Pool) IPNet() netip.Prefix { // CloneFrom clone cache from old pool func (p *Pool) CloneFrom(o *Pool) { + p.mux.Lock() + defer p.mux.Unlock() + + o.mux.Lock() + defer o.mux.Unlock() + o.store.CloneTo(p.store) + + // the allocation state only matches the cloned entries if both pools cover the same range + if o.first == p.first && o.last == p.last { + p.offset, p.cycle = o.offset, o.cycle + } } func (p *Pool) get(host string) netip.Addr { diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index c445807a..fe478e48 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -212,6 +212,33 @@ func TestPool_Clone(t *testing.T) { assert.True(t, lastExist) } +func TestPool_CloneKeepsAllocationState(t *testing.T) { + ipnet := netip.MustParsePrefix("192.168.0.1/24") + pool, _ := New(Options{ + IPNet: ipnet, + Size: 10, + }) + + foo := pool.Lookup("foo.com") + bar := pool.Lookup("bar.com") + + newPool, _ := New(Options{ + IPNet: ipnet, + Size: 10, + }) + newPool.CloneFrom(pool) + + // a fresh allocation must not overwrite the retained mappings + baz := newPool.Lookup("baz.com") + assert.NotEqual(t, foo, baz) + assert.NotEqual(t, bar, baz) + assert.Equal(t, foo, newPool.Lookup("foo.com")) + assert.Equal(t, bar, newPool.Lookup("bar.com")) + host, ok := newPool.LookBack(foo) + assert.True(t, ok) + assert.Equal(t, "foo.com", host) +} + func TestPool_Error(t *testing.T) { ipnet := netip.MustParsePrefix("192.168.0.1/31") _, err := New(Options{