@@ -4,7 +4,7 @@
#
userprogs := bpfilter_umh
-bpfilter_umh-objs := main.o logger.o
+bpfilter_umh-objs := main.o logger.o map-common.o
userccflags += -I $(srctree)/tools/include/ -I $(srctree)/tools/include/uapi
ifeq ($(CONFIG_BPFILTER_UMH), y)
new file mode 100644
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Telegram FZ-LLC
+ * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
+ */
+
+#include "map-common.h"
+
+#include <linux/err.h>
+
+#include <errno.h>
+#include <string.h>
+
+int create_map(struct hsearch_data *htab, size_t nelem)
+{
+ memset(htab, 0, sizeof(*htab));
+ if (!hcreate_r(nelem, htab))
+ return -errno;
+
+ return 0;
+}
+
+void *map_find(struct hsearch_data *htab, const char *key)
+{
+ const ENTRY needle = { .key = (char *)key };
+ ENTRY *found;
+
+ if (!hsearch_r(needle, FIND, &found, htab))
+ return ERR_PTR(-ENOENT);
+
+ return found->data;
+}
+
+int map_upsert(struct hsearch_data *htab, const char *key, void *value)
+{
+ const ENTRY needle = { .key = (char *)key, .data = value };
+ ENTRY *found;
+
+ if (!hsearch_r(needle, ENTER, &found, htab))
+ return -errno;
+
+ found->key = (char *)key;
+ found->data = value;
+
+ return 0;
+}
+
+void free_map(struct hsearch_data *htab)
+{
+ hdestroy_r(htab);
+}
new file mode 100644
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Telegram FZ-LLC
+ * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
+ */
+
+#ifndef NET_BPFILTER_MAP_COMMON_H
+#define NET_BPFILTER_MAP_COMMON_H
+
+#define _GNU_SOURCE
+
+#include <search.h>
+
+int create_map(struct hsearch_data *htab, size_t nelem);
+void *map_find(struct hsearch_data *htab, const char *key);
+int map_upsert(struct hsearch_data *htab, const char *key, void *value);
+void free_map(struct hsearch_data *htab);
+
+#endif // NET_BPFILTER_MAP_COMMON_H
new file mode 100644
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+test_map
new file mode 100644
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+
+top_srcdir = ../../../../..
+TOOLSDIR := $(abspath ../../../../)
+TOOLSINCDIR := $(TOOLSDIR)/include
+APIDIR := $(TOOLSINCDIR)/uapi
+BPFILTERSRCDIR := $(top_srcdir)/net/bpfilter
+
+CFLAGS += -Wall -g -pthread -I$(TOOLSINCDIR) -I$(APIDIR) -I$(BPFILTERSRCDIR)
+
+TEST_GEN_PROGS += test_map
+
+KSFT_KHDR_INSTALL := 1
+
+include ../../lib.mk
+
+BPFILTER_MAP_SRCS := $(BPFILTERSRCDIR)/map-common.c
+
+$(OUTPUT)/test_map: test_map.c $(BPFILTER_MAP_SRCS)
new file mode 100644
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "map-common.h"
+
+#include <linux/err.h>
+
+#include "../../kselftest_harness.h"
+
+FIXTURE(test_map)
+{
+ struct hsearch_data map;
+ const char *key;
+ void *expected;
+ void *actual;
+};
+
+FIXTURE_SETUP(test_map)
+{
+ const int max_nelements = 100;
+
+ create_map(&self->map, max_nelements);
+ self->key = "key";
+ self->expected = "expected";
+ self->actual = "actual";
+}
+
+FIXTURE_TEARDOWN(test_map)
+{
+ free_map(&self->map);
+}
+
+TEST_F(test_map, upsert_and_find)
+{
+ void *found;
+
+ found = map_find(&self->map, self->key);
+ ASSERT_TRUE(IS_ERR(found))
+ ASSERT_EQ(-ENOENT, PTR_ERR(found))
+
+ ASSERT_EQ(0, map_upsert(&self->map, self->key, self->expected));
+ ASSERT_EQ(0, map_upsert(&self->map, self->key, self->expected));
+ ASSERT_EQ(0, map_upsert(&self->map, self->key, self->actual));
+
+ found = map_find(&self->map, self->key);
+
+ ASSERT_FALSE(IS_ERR(found));
+ ASSERT_STREQ(self->actual, found);
+}
+
+TEST_F(test_map, update)
+{
+ void *found;
+
+ ASSERT_EQ(0, map_upsert(&self->map, self->key, self->actual));
+ ASSERT_EQ(0, map_upsert(&self->map, self->key, self->expected));
+
+ found = map_find(&self->map, self->key);
+
+ ASSERT_FALSE(IS_ERR(found));
+ ASSERT_STREQ(self->expected, found);
+}
+
+TEST_HARNESS_MAIN