aboutsummaryrefslogtreecommitdiffstats
path: root/samples
diff options
context:
space:
mode:
authorMartin KaFai Lau <kafai@fb.com>2017-03-22 13:00:35 -0400
committerDavid S. Miller <davem@davemloft.net>2017-03-22 18:45:45 -0400
commitfb30d4b71214aa1811e997f8f753b14b46d5b912 (patch)
treea546eefbd355792aa544c0f220b38bab4493b1aa /samples
parentbcc6b1b7ebf857a9fe56202e2be3361131588c15 (diff)
bpf: Add tests for map-in-map
Test cases for array of maps and hash of maps. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'samples')
-rw-r--r--samples/bpf/Makefile4
-rw-r--r--samples/bpf/bpf_helpers.h1
-rw-r--r--samples/bpf/bpf_load.c22
-rw-r--r--samples/bpf/test_map_in_map_kern.c173
-rw-r--r--samples/bpf/test_map_in_map_user.c116
5 files changed, 311 insertions, 5 deletions
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 09e9d535bd74..91c1d616d975 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -34,6 +34,7 @@ hostprogs-y += sampleip
34hostprogs-y += tc_l2_redirect 34hostprogs-y += tc_l2_redirect
35hostprogs-y += lwt_len_hist 35hostprogs-y += lwt_len_hist
36hostprogs-y += xdp_tx_iptunnel 36hostprogs-y += xdp_tx_iptunnel
37hostprogs-y += test_map_in_map
37 38
38# Libbpf dependencies 39# Libbpf dependencies
39LIBBPF := ../../tools/lib/bpf/bpf.o 40LIBBPF := ../../tools/lib/bpf/bpf.o
@@ -72,6 +73,7 @@ sampleip-objs := bpf_load.o $(LIBBPF) sampleip_user.o
72tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o 73tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o
73lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o 74lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o
74xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o 75xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o
76test_map_in_map-objs := bpf_load.o $(LIBBPF) test_map_in_map_user.o
75 77
76# Tell kbuild to always build the programs 78# Tell kbuild to always build the programs
77always := $(hostprogs-y) 79always := $(hostprogs-y)
@@ -105,6 +107,7 @@ always += trace_event_kern.o
105always += sampleip_kern.o 107always += sampleip_kern.o
106always += lwt_len_hist_kern.o 108always += lwt_len_hist_kern.o
107always += xdp_tx_iptunnel_kern.o 109always += xdp_tx_iptunnel_kern.o
110always += test_map_in_map_kern.o
108 111
109HOSTCFLAGS += -I$(objtree)/usr/include 112HOSTCFLAGS += -I$(objtree)/usr/include
110HOSTCFLAGS += -I$(srctree)/tools/lib/ 113HOSTCFLAGS += -I$(srctree)/tools/lib/
@@ -139,6 +142,7 @@ HOSTLOADLIBES_sampleip += -lelf
139HOSTLOADLIBES_tc_l2_redirect += -l elf 142HOSTLOADLIBES_tc_l2_redirect += -l elf
140HOSTLOADLIBES_lwt_len_hist += -l elf 143HOSTLOADLIBES_lwt_len_hist += -l elf
141HOSTLOADLIBES_xdp_tx_iptunnel += -lelf 144HOSTLOADLIBES_xdp_tx_iptunnel += -lelf
145HOSTLOADLIBES_test_map_in_map += -lelf
142 146
143# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: 147# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
144# make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang 148# make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h
index faaffe2e139a..52de9d88c021 100644
--- a/samples/bpf/bpf_helpers.h
+++ b/samples/bpf/bpf_helpers.h
@@ -80,6 +80,7 @@ struct bpf_map_def {
80 unsigned int value_size; 80 unsigned int value_size;
81 unsigned int max_entries; 81 unsigned int max_entries;
82 unsigned int map_flags; 82 unsigned int map_flags;
83 unsigned int inner_map_idx;
83}; 84};
84 85
85static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = 86static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) =
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index b86ee54da2d1..dcdce1270d38 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -43,6 +43,7 @@ struct bpf_map_def {
43 unsigned int value_size; 43 unsigned int value_size;
44 unsigned int max_entries; 44 unsigned int max_entries;
45 unsigned int map_flags; 45 unsigned int map_flags;
46 unsigned int inner_map_idx;
46}; 47};
47 48
48static int populate_prog_array(const char *event, int prog_fd) 49static int populate_prog_array(const char *event, int prog_fd)
@@ -198,11 +199,22 @@ static int load_maps(struct bpf_map_def *maps, int len)
198 199
199 for (i = 0; i < len / sizeof(struct bpf_map_def); i++) { 200 for (i = 0; i < len / sizeof(struct bpf_map_def); i++) {
200 201
201 map_fd[i] = bpf_create_map(maps[i].type, 202 if (maps[i].type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
202 maps[i].key_size, 203 maps[i].type == BPF_MAP_TYPE_HASH_OF_MAPS) {
203 maps[i].value_size, 204 int inner_map_fd = map_fd[maps[i].inner_map_idx];
204 maps[i].max_entries, 205
205 maps[i].map_flags); 206 map_fd[i] = bpf_create_map_in_map(maps[i].type,
207 maps[i].key_size,
208 inner_map_fd,
209 maps[i].max_entries,
210 maps[i].map_flags);
211 } else {
212 map_fd[i] = bpf_create_map(maps[i].type,
213 maps[i].key_size,
214 maps[i].value_size,
215 maps[i].max_entries,
216 maps[i].map_flags);
217 }
206 if (map_fd[i] < 0) { 218 if (map_fd[i] < 0) {
207 printf("failed to create a map: %d %s\n", 219 printf("failed to create a map: %d %s\n",
208 errno, strerror(errno)); 220 errno, strerror(errno));
diff --git a/samples/bpf/test_map_in_map_kern.c b/samples/bpf/test_map_in_map_kern.c
new file mode 100644
index 000000000000..42c44d091dd1
--- /dev/null
+++ b/samples/bpf/test_map_in_map_kern.c
@@ -0,0 +1,173 @@
1/*
2 * Copyright (c) 2017 Facebook
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU General Public
6 * License as published by the Free Software Foundation.
7 */
8#define KBUILD_MODNAME "foo"
9#include <linux/ptrace.h>
10#include <linux/version.h>
11#include <uapi/linux/bpf.h>
12#include <uapi/linux/in6.h>
13#include "bpf_helpers.h"
14
15#define MAX_NR_PORTS 65536
16
17/* map #0 */
18struct bpf_map_def SEC("maps") port_a = {
19 .type = BPF_MAP_TYPE_ARRAY,
20 .key_size = sizeof(u32),
21 .value_size = sizeof(int),
22 .max_entries = MAX_NR_PORTS,
23};
24
25/* map #1 */
26struct bpf_map_def SEC("maps") port_h = {
27 .type = BPF_MAP_TYPE_HASH,
28 .key_size = sizeof(u32),
29 .value_size = sizeof(int),
30 .max_entries = 1,
31};
32
33/* map #2 */
34struct bpf_map_def SEC("maps") reg_result_h = {
35 .type = BPF_MAP_TYPE_HASH,
36 .key_size = sizeof(u32),
37 .value_size = sizeof(int),
38 .max_entries = 1,
39};
40
41/* map #3 */
42struct bpf_map_def SEC("maps") inline_result_h = {
43 .type = BPF_MAP_TYPE_HASH,
44 .key_size = sizeof(u32),
45 .value_size = sizeof(int),
46 .max_entries = 1,
47};
48
49/* map #4 */ /* Test case #0 */
50struct bpf_map_def SEC("maps") a_of_port_a = {
51 .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
52 .key_size = sizeof(u32),
53 .inner_map_idx = 0, /* map_fd[0] is port_a */
54 .max_entries = MAX_NR_PORTS,
55};
56
57/* map #5 */ /* Test case #1 */
58struct bpf_map_def SEC("maps") h_of_port_a = {
59 .type = BPF_MAP_TYPE_HASH_OF_MAPS,
60 .key_size = sizeof(u32),
61 .inner_map_idx = 0, /* map_fd[0] is port_a */
62 .max_entries = 1,
63};
64
65/* map #6 */ /* Test case #2 */
66struct bpf_map_def SEC("maps") h_of_port_h = {
67 .type = BPF_MAP_TYPE_HASH_OF_MAPS,
68 .key_size = sizeof(u32),
69 .inner_map_idx = 1, /* map_fd[1] is port_h */
70 .max_entries = 1,
71};
72
73static __always_inline int do_reg_lookup(void *inner_map, u32 port)
74{
75 int *result;
76
77 result = bpf_map_lookup_elem(inner_map, &port);
78 return result ? *result : -ENOENT;
79}
80
81static __always_inline int do_inline_array_lookup(void *inner_map, u32 port)
82{
83 int *result;
84
85 if (inner_map != &port_a)
86 return -EINVAL;
87
88 result = bpf_map_lookup_elem(&port_a, &port);
89 return result ? *result : -ENOENT;
90}
91
92static __always_inline int do_inline_hash_lookup(void *inner_map, u32 port)
93{
94 int *result;
95
96 if (inner_map != &port_h)
97 return -EINVAL;
98
99 result = bpf_map_lookup_elem(&port_h, &port);
100 return result ? *result : -ENOENT;
101}
102
103SEC("kprobe/sys_connect")
104int trace_sys_connect(struct pt_regs *ctx)
105{
106 struct sockaddr_in6 *in6;
107 u16 test_case, port, dst6[8];
108 int addrlen, ret, inline_ret, ret_key = 0;
109 u32 port_key;
110 void *outer_map, *inner_map;
111 bool inline_hash = false;
112
113 in6 = (struct sockaddr_in6 *)PT_REGS_PARM2(ctx);
114 addrlen = (int)PT_REGS_PARM3(ctx);
115
116 if (addrlen != sizeof(*in6))
117 return 0;
118
119 ret = bpf_probe_read(dst6, sizeof(dst6), &in6->sin6_addr);
120 if (ret) {
121 inline_ret = ret;
122 goto done;
123 }
124
125 if (dst6[0] != 0xdead || dst6[1] != 0xbeef)
126 return 0;
127
128 test_case = dst6[7];
129
130 ret = bpf_probe_read(&port, sizeof(port), &in6->sin6_port);
131 if (ret) {
132 inline_ret = ret;
133 goto done;
134 }
135
136 port_key = port;
137
138 ret = -ENOENT;
139 if (test_case == 0) {
140 outer_map = &a_of_port_a;
141 } else if (test_case == 1) {
142 outer_map = &h_of_port_a;
143 } else if (test_case == 2) {
144 outer_map = &h_of_port_h;
145 } else {
146 ret = __LINE__;
147 inline_ret = ret;
148 goto done;
149 }
150
151 inner_map = bpf_map_lookup_elem(outer_map, &port_key);
152 if (!inner_map) {
153 ret = __LINE__;
154 inline_ret = ret;
155 goto done;
156 }
157
158 ret = do_reg_lookup(inner_map, port_key);
159
160 if (test_case == 0 || test_case == 1)
161 inline_ret = do_inline_array_lookup(inner_map, port_key);
162 else
163 inline_ret = do_inline_hash_lookup(inner_map, port_key);
164
165done:
166 bpf_map_update_elem(&reg_result_h, &ret_key, &ret, BPF_ANY);
167 bpf_map_update_elem(&inline_result_h, &ret_key, &inline_ret, BPF_ANY);
168
169 return 0;
170}
171
172char _license[] SEC("license") = "GPL";
173u32 _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/samples/bpf/test_map_in_map_user.c b/samples/bpf/test_map_in_map_user.c
new file mode 100644
index 000000000000..f62fdc2bd428
--- /dev/null
+++ b/samples/bpf/test_map_in_map_user.c
@@ -0,0 +1,116 @@
1/*
2 * Copyright (c) 2017 Facebook
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU General Public
6 * License as published by the Free Software Foundation.
7 */
8#include <sys/resource.h>
9#include <sys/socket.h>
10#include <arpa/inet.h>
11#include <stdint.h>
12#include <assert.h>
13#include <errno.h>
14#include <stdlib.h>
15#include <stdio.h>
16#include "libbpf.h"
17#include "bpf_load.h"
18
19#define PORT_A (map_fd[0])
20#define PORT_H (map_fd[1])
21#define REG_RESULT_H (map_fd[2])
22#define INLINE_RESULT_H (map_fd[3])
23#define A_OF_PORT_A (map_fd[4]) /* Test case #0 */
24#define H_OF_PORT_A (map_fd[5]) /* Test case #1 */
25#define H_OF_PORT_H (map_fd[6]) /* Test case #2 */
26
27static const char * const test_names[] = {
28 "Array of Array",
29 "Hash of Array",
30 "Hash of Hash",
31};
32
33#define NR_TESTS (sizeof(test_names) / sizeof(*test_names))
34
35static void populate_map(uint32_t port_key, int magic_result)
36{
37 int ret;
38
39 ret = bpf_map_update_elem(PORT_A, &port_key, &magic_result, BPF_ANY);
40 assert(!ret);
41
42 ret = bpf_map_update_elem(PORT_H, &port_key, &magic_result,
43 BPF_NOEXIST);
44 assert(!ret);
45
46 ret = bpf_map_update_elem(A_OF_PORT_A, &port_key, &PORT_A, BPF_ANY);
47 assert(!ret);
48
49 ret = bpf_map_update_elem(H_OF_PORT_A, &port_key, &PORT_A, BPF_NOEXIST);
50 assert(!ret);
51
52 ret = bpf_map_update_elem(H_OF_PORT_H, &port_key, &PORT_H, BPF_NOEXIST);
53 assert(!ret);
54}
55
56static void test_map_in_map(void)
57{
58 struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 };
59 uint32_t result_key = 0, port_key;
60 int result, inline_result;
61 int magic_result = 0xfaceb00c;
62 int ret;
63 int i;
64
65 port_key = rand() & 0x00FF;
66 populate_map(port_key, magic_result);
67
68 in6.sin6_addr.s6_addr16[0] = 0xdead;
69 in6.sin6_addr.s6_addr16[1] = 0xbeef;
70 in6.sin6_port = port_key;
71
72 for (i = 0; i < NR_TESTS; i++) {
73 printf("%s: ", test_names[i]);
74
75 in6.sin6_addr.s6_addr16[7] = i;
76 ret = connect(-1, (struct sockaddr *)&in6, sizeof(in6));
77 assert(ret == -1 && errno == EBADF);
78
79 ret = bpf_map_lookup_elem(REG_RESULT_H, &result_key, &result);
80 assert(!ret);
81
82 ret = bpf_map_lookup_elem(INLINE_RESULT_H, &result_key,
83 &inline_result);
84 assert(!ret);
85
86 if (result != magic_result || inline_result != magic_result) {
87 printf("Error. result:%d inline_result:%d\n",
88 result, inline_result);
89 exit(1);
90 }
91
92 bpf_map_delete_elem(REG_RESULT_H, &result_key);
93 bpf_map_delete_elem(INLINE_RESULT_H, &result_key);
94
95 printf("Pass\n");
96 }
97}
98
99int main(int argc, char **argv)
100{
101 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
102 char filename[256];
103
104 assert(!setrlimit(RLIMIT_MEMLOCK, &r));
105
106 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
107
108 if (load_bpf_file(filename)) {
109 printf("%s", bpf_log_buf);
110 return 1;
111 }
112
113 test_map_in_map();
114
115 return 0;
116}