summaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/bpf/prog_tests/bpftool_maps_access.c
blob: e0eb869cb1b47c1255ef99ac5eadc7db15221822 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
// SPDX-License-Identifier: GPL-2.0-only

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <linux/bpf.h>
#include <bpf/libbpf.h>
#include <bpftool_helpers.h>
#include <test_progs.h>
#include <bpf/bpf.h>
#include "security_bpf_map.skel.h"

#define PROTECTED_MAP_NAME	"prot_map"
#define UNPROTECTED_MAP_NAME	"not_prot_map"
#define BPF_ITER_FILE		"bpf_iter_map_elem.bpf.o"
#define BPFFS_PIN_DIR		"/sys/fs/bpf/test_bpftool_map"
#define INNER_MAP_NAME		"inner_map_tt"
#define OUTER_MAP_NAME		"outer_map_tt"

#define MAP_NAME_MAX_LEN	64
#define PATH_MAX_LEN		128

enum map_protection {
	PROTECTED,
	UNPROTECTED
};

struct test_desc {
	char *name;
	enum map_protection protection;
	struct bpf_map *map;
	char *map_name;
	bool pinned;
	char pin_path[PATH_MAX_LEN];
	bool write_must_fail;
};

static struct security_bpf_map *general_setup(void)
{
	struct security_bpf_map *skel;
	uint32_t key, value;
	int ret, i;

	skel = security_bpf_map__open_and_load();
	if (!ASSERT_OK_PTR(skel, "open and load skeleton"))
		goto end;

	struct bpf_map *maps[] = {skel->maps.prot_map, skel->maps.not_prot_map};

	ret = security_bpf_map__attach(skel);
	if (!ASSERT_OK(ret, "attach maps security programs"))
		goto end_destroy;

	for (i = 0; i < sizeof(maps)/sizeof(struct bpf_map *); i++) {
		for (key = 0; key < 2; key++) {
			int ret = bpf_map__update_elem(maps[i], &key,
					sizeof(key), &key, sizeof(key),
					0);
			if (!ASSERT_OK(ret, "set initial map value"))
				goto end_destroy;
		}
	}

	key = 0;
	value = 1;
	ret = bpf_map__update_elem(skel->maps.prot_status_map, &key,
			sizeof(key), &value, sizeof(value), 0);
	if (!ASSERT_OK(ret, "configure map protection"))
		goto end_destroy;

	if (!ASSERT_OK(mkdir(BPFFS_PIN_DIR, S_IFDIR), "create bpffs pin dir"))
		goto end_destroy;

	return skel;
end_destroy:
	security_bpf_map__destroy(skel);
end:
	return NULL;
}

static void general_cleanup(struct security_bpf_map *skel)
{
	rmdir(BPFFS_PIN_DIR);
	security_bpf_map__destroy(skel);
}

static void update_test_desc(struct security_bpf_map *skel,
			      struct test_desc *test)
{
	/* Now that the skeleton is loaded, update all missing fields to
	 * have the subtest properly configured
	 */
	if (test->protection == PROTECTED) {
		test->map = skel->maps.prot_map;
		test->map_name = PROTECTED_MAP_NAME;
	} else {
		test->map = skel->maps.not_prot_map;
		test->map_name = UNPROTECTED_MAP_NAME;
	}
}

static int test_setup(struct security_bpf_map *skel, struct test_desc *desc)
{
	int ret;

	update_test_desc(skel, desc);

	if (desc->pinned) {
		ret = snprintf(desc->pin_path, PATH_MAX_LEN, "%s/%s", BPFFS_PIN_DIR,
				desc->name);
		if (!ASSERT_GT(ret, 0, "format pin path"))
			return 1;
		ret = bpf_map__pin(desc->map, desc->pin_path);
		if (!ASSERT_OK(ret, "pin map"))
			return 1;
	}

	return 0;
}

static void test_cleanup(struct test_desc *desc)
{
	if (desc->pinned)
		bpf_map__unpin(desc->map, NULL);
}

static int lookup_map_value(char *map_handle)
{
	char cmd[MAX_BPFTOOL_CMD_LEN];
	int ret = 0;

	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "map lookup %s key 0 0 0 0",
			map_handle);
	if (!ASSERT_GT(ret, 0, "format map lookup cmd"))
		return 1;
	return run_bpftool_command(cmd);
}

static int read_map_btf_data(char *map_handle)
{
	char cmd[MAX_BPFTOOL_CMD_LEN];
	int ret = 0;

	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "btf dump map %s",
			map_handle);
	if (!ASSERT_GT(ret, 0, "format map btf dump cmd"))
		return 1;
	return run_bpftool_command(cmd);
}

static int write_map_value(char *map_handle)
{
	char cmd[MAX_BPFTOOL_CMD_LEN];
	int ret = 0;

	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN,
		       "map update %s key 0 0 0 0 value 1 1 1 1", map_handle);
	if (!ASSERT_GT(ret, 0, "format value write cmd"))
		return 1;
	return run_bpftool_command(cmd);
}

static int delete_map_value(char *map_handle)
{
	char cmd[MAX_BPFTOOL_CMD_LEN];
	int ret = 0;

	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN,
		       "map delete %s key 0 0 0 0", map_handle);
	if (!ASSERT_GT(ret, 0, "format value deletion cmd"))
		return 1;
	return run_bpftool_command(cmd);
}

static int iterate_on_map_values(char *map_handle, char *iter_pin_path)
{
	char cmd[MAX_BPFTOOL_CMD_LEN];
	int ret = 0;


	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "iter pin %s %s map %s",
		       BPF_ITER_FILE, iter_pin_path, map_handle);
	if (!ASSERT_GT(ret, 0, "format iterator creation cmd"))
		return 1;
	ret = run_bpftool_command(cmd);
	if (ret)
		return ret;
	ret = snprintf(cmd, MAP_NAME_MAX_LEN, "cat %s", iter_pin_path);
	if (ret < 0)
		goto cleanup;
	ret = system(cmd);

cleanup:
	unlink(iter_pin_path);
	return ret;
}

static int create_inner_map(void)
{
	char cmd[MAX_BPFTOOL_CMD_LEN];
	int ret = 0;

	ret = snprintf(
		cmd, MAX_BPFTOOL_CMD_LEN,
		"map create %s/%s type array key 4 value 4 entries 4 name %s",
		BPFFS_PIN_DIR, INNER_MAP_NAME, INNER_MAP_NAME);
	if (!ASSERT_GT(ret, 0, "format inner map create cmd"))
		return 1;
	return run_bpftool_command(cmd);
}

static int create_outer_map(void)
{
	char cmd[MAX_BPFTOOL_CMD_LEN];
	int ret = 0;

	ret = snprintf(
		cmd, MAX_BPFTOOL_CMD_LEN,
		"map create %s/%s type hash_of_maps key 4 value 4 entries 2 name %s inner_map name %s",
		BPFFS_PIN_DIR, OUTER_MAP_NAME, OUTER_MAP_NAME, INNER_MAP_NAME);
	if (!ASSERT_GT(ret, 0, "format outer map create cmd"))
		return 1;
	return run_bpftool_command(cmd);
}

static void delete_pinned_map(char *map_name)
{
	char pin_path[PATH_MAX_LEN];
	int ret;

	ret = snprintf(pin_path, PATH_MAX_LEN, "%s/%s", BPFFS_PIN_DIR,
		       map_name);
	if (ret >= 0)
		unlink(pin_path);
}

static int add_outer_map_entry(int key)
{
	char cmd[MAX_BPFTOOL_CMD_LEN];
	int ret = 0;

	ret = snprintf(
		cmd, MAX_BPFTOOL_CMD_LEN,
		"map update pinned %s/%s key %d 0 0 0 value name %s",
		BPFFS_PIN_DIR, OUTER_MAP_NAME, key, INNER_MAP_NAME);
	if (!ASSERT_GT(ret, 0, "format outer map value addition cmd"))
		return 1;
	return run_bpftool_command(cmd);
}

static void test_basic_access(struct test_desc *desc)
{
	char map_handle[MAP_NAME_MAX_LEN];
	char iter_pin_path[PATH_MAX_LEN];
	int ret;

	if (desc->pinned)
		ret = snprintf(map_handle, MAP_NAME_MAX_LEN, "pinned %s",
			       desc->pin_path);
	else
		ret = snprintf(map_handle, MAP_NAME_MAX_LEN, "name %s",
			       desc->map_name);
	if (!ASSERT_GT(ret, 0, "format map handle"))
		return;

	ret = lookup_map_value(map_handle);
	ASSERT_OK(ret, "read map value");

	ret = read_map_btf_data(map_handle);
	ASSERT_OK(ret, "read map btf data");

	ret = write_map_value(map_handle);
	ASSERT_OK(desc->write_must_fail ? !ret : ret, "write map value");

	ret = delete_map_value(map_handle);
	ASSERT_OK(desc->write_must_fail ? !ret : ret, "delete map value");
	/* Restore deleted value */
	if (!ret)
		write_map_value(map_handle);

	ret = snprintf(iter_pin_path, PATH_MAX_LEN, "%s/iter", BPFFS_PIN_DIR);
	if (ASSERT_GT(ret, 0, "format iter pin path")) {
		ret = iterate_on_map_values(map_handle, iter_pin_path);
		ASSERT_OK(ret, "iterate on map values");
	}
}

static void test_create_nested_maps(void)
{
	if (!ASSERT_OK(create_inner_map(), "create inner map"))
		return;
	if (!ASSERT_OK(create_outer_map(), "create outer map"))
		goto end_cleanup_inner;
	ASSERT_OK(add_outer_map_entry(0), "add a first entry in outer map");
	ASSERT_OK(add_outer_map_entry(1), "add a second entry in outer map");
	ASSERT_NEQ(add_outer_map_entry(2), 0, "add a third entry in outer map");

	delete_pinned_map(OUTER_MAP_NAME);
end_cleanup_inner:
	delete_pinned_map(INNER_MAP_NAME);
}

static void test_btf_list(void)
{
	ASSERT_OK(run_bpftool_command("btf list"), "list btf data");
}

static struct test_desc tests[] = {
	{
		.name = "unprotected_unpinned",
		.protection = UNPROTECTED,
		.map_name = UNPROTECTED_MAP_NAME,
		.pinned = false,
		.write_must_fail = false,
	},
	{
		.name = "unprotected_pinned",
		.protection = UNPROTECTED,
		.map_name = UNPROTECTED_MAP_NAME,
		.pinned = true,
		.write_must_fail = false,
	},
	{
		.name = "protected_unpinned",
		.protection = PROTECTED,
		.map_name = UNPROTECTED_MAP_NAME,
		.pinned = false,
		.write_must_fail = true,
	},
	{
		.name = "protected_pinned",
		.protection = PROTECTED,
		.map_name = UNPROTECTED_MAP_NAME,
		.pinned = true,
		.write_must_fail = true,
	}
};

static const size_t tests_count = ARRAY_SIZE(tests);

void test_bpftool_maps_access(void)
{
	struct security_bpf_map *skel;
	struct test_desc *current;
	int i;

	skel = general_setup();
	if (!ASSERT_OK_PTR(skel, "prepare programs"))
		goto cleanup;

	for (i = 0; i < tests_count; i++) {
		current = &tests[i];
		if (!test__start_subtest(current->name))
			continue;
		if (ASSERT_OK(test_setup(skel, current), "subtest setup")) {
			test_basic_access(current);
			test_cleanup(current);
		}
	}
	if (test__start_subtest("nested_maps"))
		test_create_nested_maps();
	if (test__start_subtest("btf_list"))
		test_btf_list();

cleanup:
	general_cleanup(skel);
}