forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Test cases for array of maps and hash of maps. Signed-off-by: Martin KaFai Lau <[email protected]> Acked-by: Alexei Starovoitov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
- Loading branch information
Showing
9 changed files
with
451 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
/* | ||
* Copyright (c) 2017 Facebook | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of version 2 of the GNU General Public | ||
* License as published by the Free Software Foundation. | ||
*/ | ||
#define KBUILD_MODNAME "foo" | ||
#include <linux/ptrace.h> | ||
#include <linux/version.h> | ||
#include <uapi/linux/bpf.h> | ||
#include <uapi/linux/in6.h> | ||
#include "bpf_helpers.h" | ||
|
||
#define MAX_NR_PORTS 65536 | ||
|
||
/* map #0 */ | ||
struct bpf_map_def SEC("maps") port_a = { | ||
.type = BPF_MAP_TYPE_ARRAY, | ||
.key_size = sizeof(u32), | ||
.value_size = sizeof(int), | ||
.max_entries = MAX_NR_PORTS, | ||
}; | ||
|
||
/* map #1 */ | ||
struct bpf_map_def SEC("maps") port_h = { | ||
.type = BPF_MAP_TYPE_HASH, | ||
.key_size = sizeof(u32), | ||
.value_size = sizeof(int), | ||
.max_entries = 1, | ||
}; | ||
|
||
/* map #2 */ | ||
struct bpf_map_def SEC("maps") reg_result_h = { | ||
.type = BPF_MAP_TYPE_HASH, | ||
.key_size = sizeof(u32), | ||
.value_size = sizeof(int), | ||
.max_entries = 1, | ||
}; | ||
|
||
/* map #3 */ | ||
struct bpf_map_def SEC("maps") inline_result_h = { | ||
.type = BPF_MAP_TYPE_HASH, | ||
.key_size = sizeof(u32), | ||
.value_size = sizeof(int), | ||
.max_entries = 1, | ||
}; | ||
|
||
/* map #4 */ /* Test case #0 */ | ||
struct bpf_map_def SEC("maps") a_of_port_a = { | ||
.type = BPF_MAP_TYPE_ARRAY_OF_MAPS, | ||
.key_size = sizeof(u32), | ||
.inner_map_idx = 0, /* map_fd[0] is port_a */ | ||
.max_entries = MAX_NR_PORTS, | ||
}; | ||
|
||
/* map #5 */ /* Test case #1 */ | ||
struct bpf_map_def SEC("maps") h_of_port_a = { | ||
.type = BPF_MAP_TYPE_HASH_OF_MAPS, | ||
.key_size = sizeof(u32), | ||
.inner_map_idx = 0, /* map_fd[0] is port_a */ | ||
.max_entries = 1, | ||
}; | ||
|
||
/* map #6 */ /* Test case #2 */ | ||
struct bpf_map_def SEC("maps") h_of_port_h = { | ||
.type = BPF_MAP_TYPE_HASH_OF_MAPS, | ||
.key_size = sizeof(u32), | ||
.inner_map_idx = 1, /* map_fd[1] is port_h */ | ||
.max_entries = 1, | ||
}; | ||
|
||
static __always_inline int do_reg_lookup(void *inner_map, u32 port) | ||
{ | ||
int *result; | ||
|
||
result = bpf_map_lookup_elem(inner_map, &port); | ||
return result ? *result : -ENOENT; | ||
} | ||
|
||
static __always_inline int do_inline_array_lookup(void *inner_map, u32 port) | ||
{ | ||
int *result; | ||
|
||
if (inner_map != &port_a) | ||
return -EINVAL; | ||
|
||
result = bpf_map_lookup_elem(&port_a, &port); | ||
return result ? *result : -ENOENT; | ||
} | ||
|
||
static __always_inline int do_inline_hash_lookup(void *inner_map, u32 port) | ||
{ | ||
int *result; | ||
|
||
if (inner_map != &port_h) | ||
return -EINVAL; | ||
|
||
result = bpf_map_lookup_elem(&port_h, &port); | ||
return result ? *result : -ENOENT; | ||
} | ||
|
||
SEC("kprobe/sys_connect") | ||
int trace_sys_connect(struct pt_regs *ctx) | ||
{ | ||
struct sockaddr_in6 *in6; | ||
u16 test_case, port, dst6[8]; | ||
int addrlen, ret, inline_ret, ret_key = 0; | ||
u32 port_key; | ||
void *outer_map, *inner_map; | ||
bool inline_hash = false; | ||
|
||
in6 = (struct sockaddr_in6 *)PT_REGS_PARM2(ctx); | ||
addrlen = (int)PT_REGS_PARM3(ctx); | ||
|
||
if (addrlen != sizeof(*in6)) | ||
return 0; | ||
|
||
ret = bpf_probe_read(dst6, sizeof(dst6), &in6->sin6_addr); | ||
if (ret) { | ||
inline_ret = ret; | ||
goto done; | ||
} | ||
|
||
if (dst6[0] != 0xdead || dst6[1] != 0xbeef) | ||
return 0; | ||
|
||
test_case = dst6[7]; | ||
|
||
ret = bpf_probe_read(&port, sizeof(port), &in6->sin6_port); | ||
if (ret) { | ||
inline_ret = ret; | ||
goto done; | ||
} | ||
|
||
port_key = port; | ||
|
||
ret = -ENOENT; | ||
if (test_case == 0) { | ||
outer_map = &a_of_port_a; | ||
} else if (test_case == 1) { | ||
outer_map = &h_of_port_a; | ||
} else if (test_case == 2) { | ||
outer_map = &h_of_port_h; | ||
} else { | ||
ret = __LINE__; | ||
inline_ret = ret; | ||
goto done; | ||
} | ||
|
||
inner_map = bpf_map_lookup_elem(outer_map, &port_key); | ||
if (!inner_map) { | ||
ret = __LINE__; | ||
inline_ret = ret; | ||
goto done; | ||
} | ||
|
||
ret = do_reg_lookup(inner_map, port_key); | ||
|
||
if (test_case == 0 || test_case == 1) | ||
inline_ret = do_inline_array_lookup(inner_map, port_key); | ||
else | ||
inline_ret = do_inline_hash_lookup(inner_map, port_key); | ||
|
||
done: | ||
bpf_map_update_elem(®_result_h, &ret_key, &ret, BPF_ANY); | ||
bpf_map_update_elem(&inline_result_h, &ret_key, &inline_ret, BPF_ANY); | ||
|
||
return 0; | ||
} | ||
|
||
char _license[] SEC("license") = "GPL"; | ||
u32 _version SEC("version") = LINUX_VERSION_CODE; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* | ||
* Copyright (c) 2017 Facebook | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of version 2 of the GNU General Public | ||
* License as published by the Free Software Foundation. | ||
*/ | ||
#include <sys/resource.h> | ||
#include <sys/socket.h> | ||
#include <arpa/inet.h> | ||
#include <stdint.h> | ||
#include <assert.h> | ||
#include <errno.h> | ||
#include <stdlib.h> | ||
#include <stdio.h> | ||
#include "libbpf.h" | ||
#include "bpf_load.h" | ||
|
||
#define PORT_A (map_fd[0]) | ||
#define PORT_H (map_fd[1]) | ||
#define REG_RESULT_H (map_fd[2]) | ||
#define INLINE_RESULT_H (map_fd[3]) | ||
#define A_OF_PORT_A (map_fd[4]) /* Test case #0 */ | ||
#define H_OF_PORT_A (map_fd[5]) /* Test case #1 */ | ||
#define H_OF_PORT_H (map_fd[6]) /* Test case #2 */ | ||
|
||
static const char * const test_names[] = { | ||
"Array of Array", | ||
"Hash of Array", | ||
"Hash of Hash", | ||
}; | ||
|
||
#define NR_TESTS (sizeof(test_names) / sizeof(*test_names)) | ||
|
||
static void populate_map(uint32_t port_key, int magic_result) | ||
{ | ||
int ret; | ||
|
||
ret = bpf_map_update_elem(PORT_A, &port_key, &magic_result, BPF_ANY); | ||
assert(!ret); | ||
|
||
ret = bpf_map_update_elem(PORT_H, &port_key, &magic_result, | ||
BPF_NOEXIST); | ||
assert(!ret); | ||
|
||
ret = bpf_map_update_elem(A_OF_PORT_A, &port_key, &PORT_A, BPF_ANY); | ||
assert(!ret); | ||
|
||
ret = bpf_map_update_elem(H_OF_PORT_A, &port_key, &PORT_A, BPF_NOEXIST); | ||
assert(!ret); | ||
|
||
ret = bpf_map_update_elem(H_OF_PORT_H, &port_key, &PORT_H, BPF_NOEXIST); | ||
assert(!ret); | ||
} | ||
|
||
static void test_map_in_map(void) | ||
{ | ||
struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 }; | ||
uint32_t result_key = 0, port_key; | ||
int result, inline_result; | ||
int magic_result = 0xfaceb00c; | ||
int ret; | ||
int i; | ||
|
||
port_key = rand() & 0x00FF; | ||
populate_map(port_key, magic_result); | ||
|
||
in6.sin6_addr.s6_addr16[0] = 0xdead; | ||
in6.sin6_addr.s6_addr16[1] = 0xbeef; | ||
in6.sin6_port = port_key; | ||
|
||
for (i = 0; i < NR_TESTS; i++) { | ||
printf("%s: ", test_names[i]); | ||
|
||
in6.sin6_addr.s6_addr16[7] = i; | ||
ret = connect(-1, (struct sockaddr *)&in6, sizeof(in6)); | ||
assert(ret == -1 && errno == EBADF); | ||
|
||
ret = bpf_map_lookup_elem(REG_RESULT_H, &result_key, &result); | ||
assert(!ret); | ||
|
||
ret = bpf_map_lookup_elem(INLINE_RESULT_H, &result_key, | ||
&inline_result); | ||
assert(!ret); | ||
|
||
if (result != magic_result || inline_result != magic_result) { | ||
printf("Error. result:%d inline_result:%d\n", | ||
result, inline_result); | ||
exit(1); | ||
} | ||
|
||
bpf_map_delete_elem(REG_RESULT_H, &result_key); | ||
bpf_map_delete_elem(INLINE_RESULT_H, &result_key); | ||
|
||
printf("Pass\n"); | ||
} | ||
} | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; | ||
char filename[256]; | ||
|
||
assert(!setrlimit(RLIMIT_MEMLOCK, &r)); | ||
|
||
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); | ||
|
||
if (load_bpf_file(filename)) { | ||
printf("%s", bpf_log_buf); | ||
return 1; | ||
} | ||
|
||
test_map_in_map(); | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.