Skip to content

FlushSet is unreliable, occasionally elements remain in the set #305

@marten-seemann

Description

@marten-seemann

I'm running into occasional errors when working with sets / maps. I narrowed this down to the FlushSet function, which sometimes doesn't actually flush the set, i.e. the set sometimes is not empty after FlushSet.

It usually works as expected, but in 5-10% of the cases, the existing elements remain in the set.

I observed this behavior both with v0.3.0, as well as with the current state of the master branch.
Kernel information:

❯ uname -a
Linux linode-sg 5.15.0-131-generic #141-Ubuntu SMP Fri Jan 10 21:18:28 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux

Here's a minimal example to reproduce the bug. It does the following:

  1. It creates a table and a set.
  2. Adds some elements to the set.
  3. Flushes the set using FlushSet.
  4. Checks that there are 0 elements remaining.
package loadbalancer

import (
	"testing"

	"github.com/google/nftables"
	"github.com/google/nftables/binaryutil"

	"github.com/stretchr/testify/require"
)

func TestFlushSet(t *testing.T) {
	conn := &nftables.Conn{}
	table := &nftables.Table{
		Name:   "test_table",
		Family: nftables.TableFamilyIPv4,
	}
	conn.AddTable(table)

	t.Cleanup(func() {
		conn.DelTable(table)
		require.NoError(t, conn.Flush(), "failed to delete table")
	})

	set := &nftables.Set{
		Name:    "test_set",
		Table:   table,
		IsMap:   false,
		KeyType: nftables.TypeInetService, // 2-byte key (e.g., ports)
	}
	require.NoError(t, conn.AddSet(set, nil), "failed to add set")

	elements := []nftables.SetElement{
		{Key: binaryutil.BigEndian.PutUint16(80)},   // Port 80
		{Key: binaryutil.BigEndian.PutUint16(443)},  // Port 443
		{Key: binaryutil.BigEndian.PutUint16(8080)}, // Port 8080
	}
	require.NoError(t, conn.SetAddElements(set, elements), "failed to add elements")
	require.NoError(t, conn.Flush(), "failed to flush added elements")
	conn.FlushSet(set)
	require.NoError(t, conn.Flush(), "failed to flush the flush")

	// check that the set is empty
	elems, err := conn.GetSetElements(set)
	require.NoError(t, err, "failed to get set elements after flush")
	require.Empty(t, elems, "set not empty after flush")
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions