162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2017 Facebook
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <sys/socket.h>
662306a36Sopenharmony_ci#include <arpa/inet.h>
762306a36Sopenharmony_ci#include <stdint.h>
862306a36Sopenharmony_ci#include <assert.h>
962306a36Sopenharmony_ci#include <errno.h>
1062306a36Sopenharmony_ci#include <stdlib.h>
1162306a36Sopenharmony_ci#include <stdio.h>
1262306a36Sopenharmony_ci#include <bpf/bpf.h>
1362306a36Sopenharmony_ci#include <bpf/libbpf.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "bpf_util.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic int map_fd[7];
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define PORT_A		(map_fd[0])
2062306a36Sopenharmony_ci#define PORT_H		(map_fd[1])
2162306a36Sopenharmony_ci#define REG_RESULT_H	(map_fd[2])
2262306a36Sopenharmony_ci#define INLINE_RESULT_H	(map_fd[3])
2362306a36Sopenharmony_ci#define A_OF_PORT_A	(map_fd[4]) /* Test case #0 */
2462306a36Sopenharmony_ci#define H_OF_PORT_A	(map_fd[5]) /* Test case #1 */
2562306a36Sopenharmony_ci#define H_OF_PORT_H	(map_fd[6]) /* Test case #2 */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const char * const test_names[] = {
2862306a36Sopenharmony_ci	"Array of Array",
2962306a36Sopenharmony_ci	"Hash of Array",
3062306a36Sopenharmony_ci	"Hash of Hash",
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define NR_TESTS ARRAY_SIZE(test_names)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic void check_map_id(int inner_map_fd, int map_in_map_fd, uint32_t key)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct bpf_map_info info = {};
3862306a36Sopenharmony_ci	uint32_t info_len = sizeof(info);
3962306a36Sopenharmony_ci	int ret, id;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	ret = bpf_map_get_info_by_fd(inner_map_fd, &info, &info_len);
4262306a36Sopenharmony_ci	assert(!ret);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	ret = bpf_map_lookup_elem(map_in_map_fd, &key, &id);
4562306a36Sopenharmony_ci	assert(!ret);
4662306a36Sopenharmony_ci	assert(id == info.id);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void populate_map(uint32_t port_key, int magic_result)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	int ret;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	ret = bpf_map_update_elem(PORT_A, &port_key, &magic_result, BPF_ANY);
5462306a36Sopenharmony_ci	assert(!ret);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	ret = bpf_map_update_elem(PORT_H, &port_key, &magic_result,
5762306a36Sopenharmony_ci				  BPF_NOEXIST);
5862306a36Sopenharmony_ci	assert(!ret);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	ret = bpf_map_update_elem(A_OF_PORT_A, &port_key, &PORT_A, BPF_ANY);
6162306a36Sopenharmony_ci	assert(!ret);
6262306a36Sopenharmony_ci	check_map_id(PORT_A, A_OF_PORT_A, port_key);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	ret = bpf_map_update_elem(H_OF_PORT_A, &port_key, &PORT_A, BPF_NOEXIST);
6562306a36Sopenharmony_ci	assert(!ret);
6662306a36Sopenharmony_ci	check_map_id(PORT_A, H_OF_PORT_A, port_key);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	ret = bpf_map_update_elem(H_OF_PORT_H, &port_key, &PORT_H, BPF_NOEXIST);
6962306a36Sopenharmony_ci	assert(!ret);
7062306a36Sopenharmony_ci	check_map_id(PORT_H, H_OF_PORT_H, port_key);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void test_map_in_map(void)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 };
7662306a36Sopenharmony_ci	uint32_t result_key = 0, port_key;
7762306a36Sopenharmony_ci	int result, inline_result;
7862306a36Sopenharmony_ci	int magic_result = 0xfaceb00c;
7962306a36Sopenharmony_ci	int ret;
8062306a36Sopenharmony_ci	int i;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	port_key = rand() & 0x00FF;
8362306a36Sopenharmony_ci	populate_map(port_key, magic_result);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	in6.sin6_addr.s6_addr16[0] = 0xdead;
8662306a36Sopenharmony_ci	in6.sin6_addr.s6_addr16[1] = 0xbeef;
8762306a36Sopenharmony_ci	in6.sin6_port = port_key;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	for (i = 0; i < NR_TESTS; i++) {
9062306a36Sopenharmony_ci		printf("%s: ", test_names[i]);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		in6.sin6_addr.s6_addr16[7] = i;
9362306a36Sopenharmony_ci		ret = connect(-1, (struct sockaddr *)&in6, sizeof(in6));
9462306a36Sopenharmony_ci		assert(ret == -1 && errno == EBADF);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		ret = bpf_map_lookup_elem(REG_RESULT_H, &result_key, &result);
9762306a36Sopenharmony_ci		assert(!ret);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		ret = bpf_map_lookup_elem(INLINE_RESULT_H, &result_key,
10062306a36Sopenharmony_ci					  &inline_result);
10162306a36Sopenharmony_ci		assert(!ret);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		if (result != magic_result || inline_result != magic_result) {
10462306a36Sopenharmony_ci			printf("Error. result:%d inline_result:%d\n",
10562306a36Sopenharmony_ci			       result, inline_result);
10662306a36Sopenharmony_ci			exit(1);
10762306a36Sopenharmony_ci		}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		bpf_map_delete_elem(REG_RESULT_H, &result_key);
11062306a36Sopenharmony_ci		bpf_map_delete_elem(INLINE_RESULT_H, &result_key);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		printf("Pass\n");
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ciint main(int argc, char **argv)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct bpf_link *link = NULL;
11962306a36Sopenharmony_ci	struct bpf_program *prog;
12062306a36Sopenharmony_ci	struct bpf_object *obj;
12162306a36Sopenharmony_ci	char filename[256];
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
12462306a36Sopenharmony_ci	obj = bpf_object__open_file(filename, NULL);
12562306a36Sopenharmony_ci	if (libbpf_get_error(obj)) {
12662306a36Sopenharmony_ci		fprintf(stderr, "ERROR: opening BPF object file failed\n");
12762306a36Sopenharmony_ci		return 0;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	prog = bpf_object__find_program_by_name(obj, "trace_sys_connect");
13162306a36Sopenharmony_ci	if (!prog) {
13262306a36Sopenharmony_ci		printf("finding a prog in obj file failed\n");
13362306a36Sopenharmony_ci		goto cleanup;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* load BPF program */
13762306a36Sopenharmony_ci	if (bpf_object__load(obj)) {
13862306a36Sopenharmony_ci		fprintf(stderr, "ERROR: loading BPF object file failed\n");
13962306a36Sopenharmony_ci		goto cleanup;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	map_fd[0] = bpf_object__find_map_fd_by_name(obj, "port_a");
14362306a36Sopenharmony_ci	map_fd[1] = bpf_object__find_map_fd_by_name(obj, "port_h");
14462306a36Sopenharmony_ci	map_fd[2] = bpf_object__find_map_fd_by_name(obj, "reg_result_h");
14562306a36Sopenharmony_ci	map_fd[3] = bpf_object__find_map_fd_by_name(obj, "inline_result_h");
14662306a36Sopenharmony_ci	map_fd[4] = bpf_object__find_map_fd_by_name(obj, "a_of_port_a");
14762306a36Sopenharmony_ci	map_fd[5] = bpf_object__find_map_fd_by_name(obj, "h_of_port_a");
14862306a36Sopenharmony_ci	map_fd[6] = bpf_object__find_map_fd_by_name(obj, "h_of_port_h");
14962306a36Sopenharmony_ci	if (map_fd[0] < 0 || map_fd[1] < 0 || map_fd[2] < 0 ||
15062306a36Sopenharmony_ci	    map_fd[3] < 0 || map_fd[4] < 0 || map_fd[5] < 0 || map_fd[6] < 0) {
15162306a36Sopenharmony_ci		fprintf(stderr, "ERROR: finding a map in obj file failed\n");
15262306a36Sopenharmony_ci		goto cleanup;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	link = bpf_program__attach(prog);
15662306a36Sopenharmony_ci	if (libbpf_get_error(link)) {
15762306a36Sopenharmony_ci		fprintf(stderr, "ERROR: bpf_program__attach failed\n");
15862306a36Sopenharmony_ci		link = NULL;
15962306a36Sopenharmony_ci		goto cleanup;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	test_map_in_map();
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cicleanup:
16562306a36Sopenharmony_ci	bpf_link__destroy(link);
16662306a36Sopenharmony_ci	bpf_object__close(obj);
16762306a36Sopenharmony_ci	return 0;
16862306a36Sopenharmony_ci}
169