diff options
author | Martin KaFai Lau <kafai@fb.com> | 2017-03-22 13:00:35 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-03-22 18:45:45 -0400 |
commit | fb30d4b71214aa1811e997f8f753b14b46d5b912 (patch) | |
tree | a546eefbd355792aa544c0f220b38bab4493b1aa | |
parent | bcc6b1b7ebf857a9fe56202e2be3361131588c15 (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>
-rw-r--r-- | samples/bpf/Makefile | 4 | ||||
-rw-r--r-- | samples/bpf/bpf_helpers.h | 1 | ||||
-rw-r--r-- | samples/bpf/bpf_load.c | 22 | ||||
-rw-r--r-- | samples/bpf/test_map_in_map_kern.c | 173 | ||||
-rw-r--r-- | samples/bpf/test_map_in_map_user.c | 116 | ||||
-rw-r--r-- | tools/include/uapi/linux/bpf.h | 3 | ||||
-rw-r--r-- | tools/lib/bpf/bpf.c | 17 | ||||
-rw-r--r-- | tools/lib/bpf/bpf.h | 2 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_verifier.c | 131 |
9 files changed, 451 insertions, 18 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 | |||
34 | hostprogs-y += tc_l2_redirect | 34 | hostprogs-y += tc_l2_redirect |
35 | hostprogs-y += lwt_len_hist | 35 | hostprogs-y += lwt_len_hist |
36 | hostprogs-y += xdp_tx_iptunnel | 36 | hostprogs-y += xdp_tx_iptunnel |
37 | hostprogs-y += test_map_in_map | ||
37 | 38 | ||
38 | # Libbpf dependencies | 39 | # Libbpf dependencies |
39 | LIBBPF := ../../tools/lib/bpf/bpf.o | 40 | LIBBPF := ../../tools/lib/bpf/bpf.o |
@@ -72,6 +73,7 @@ sampleip-objs := bpf_load.o $(LIBBPF) sampleip_user.o | |||
72 | tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o | 73 | tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o |
73 | lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o | 74 | lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o |
74 | xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o | 75 | xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o |
76 | test_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 |
77 | always := $(hostprogs-y) | 79 | always := $(hostprogs-y) |
@@ -105,6 +107,7 @@ always += trace_event_kern.o | |||
105 | always += sampleip_kern.o | 107 | always += sampleip_kern.o |
106 | always += lwt_len_hist_kern.o | 108 | always += lwt_len_hist_kern.o |
107 | always += xdp_tx_iptunnel_kern.o | 109 | always += xdp_tx_iptunnel_kern.o |
110 | always += test_map_in_map_kern.o | ||
108 | 111 | ||
109 | HOSTCFLAGS += -I$(objtree)/usr/include | 112 | HOSTCFLAGS += -I$(objtree)/usr/include |
110 | HOSTCFLAGS += -I$(srctree)/tools/lib/ | 113 | HOSTCFLAGS += -I$(srctree)/tools/lib/ |
@@ -139,6 +142,7 @@ HOSTLOADLIBES_sampleip += -lelf | |||
139 | HOSTLOADLIBES_tc_l2_redirect += -l elf | 142 | HOSTLOADLIBES_tc_l2_redirect += -l elf |
140 | HOSTLOADLIBES_lwt_len_hist += -l elf | 143 | HOSTLOADLIBES_lwt_len_hist += -l elf |
141 | HOSTLOADLIBES_xdp_tx_iptunnel += -lelf | 144 | HOSTLOADLIBES_xdp_tx_iptunnel += -lelf |
145 | HOSTLOADLIBES_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 | ||
85 | static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = | 86 | static 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 | ||
48 | static int populate_prog_array(const char *event, int prog_fd) | 49 | static 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 */ | ||
18 | struct 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 */ | ||
26 | struct 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 */ | ||
34 | struct 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 */ | ||
42 | struct 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 */ | ||
50 | struct 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 */ | ||
58 | struct 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 */ | ||
66 | struct 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 | |||
73 | static __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 | |||
81 | static __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 | |||
92 | static __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 | |||
103 | SEC("kprobe/sys_connect") | ||
104 | int 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 | |||
165 | done: | ||
166 | bpf_map_update_elem(®_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 | |||
172 | char _license[] SEC("license") = "GPL"; | ||
173 | u32 _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 | |||
27 | static 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 | |||
35 | static 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 | |||
56 | static 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 | |||
99 | int 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 | } | ||
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 0539a0ceef38..ce6f029ac368 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h | |||
@@ -96,6 +96,8 @@ enum bpf_map_type { | |||
96 | BPF_MAP_TYPE_LRU_HASH, | 96 | BPF_MAP_TYPE_LRU_HASH, |
97 | BPF_MAP_TYPE_LRU_PERCPU_HASH, | 97 | BPF_MAP_TYPE_LRU_PERCPU_HASH, |
98 | BPF_MAP_TYPE_LPM_TRIE, | 98 | BPF_MAP_TYPE_LPM_TRIE, |
99 | BPF_MAP_TYPE_ARRAY_OF_MAPS, | ||
100 | BPF_MAP_TYPE_HASH_OF_MAPS, | ||
99 | }; | 101 | }; |
100 | 102 | ||
101 | enum bpf_prog_type { | 103 | enum bpf_prog_type { |
@@ -152,6 +154,7 @@ union bpf_attr { | |||
152 | __u32 value_size; /* size of value in bytes */ | 154 | __u32 value_size; /* size of value in bytes */ |
153 | __u32 max_entries; /* max number of entries in a map */ | 155 | __u32 max_entries; /* max number of entries in a map */ |
154 | __u32 map_flags; /* prealloc or not */ | 156 | __u32 map_flags; /* prealloc or not */ |
157 | __u32 inner_map_fd; /* fd pointing to the inner map */ | ||
155 | }; | 158 | }; |
156 | 159 | ||
157 | struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ | 160 | struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ |
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 207c2eeddab0..9b58d20e8c93 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c | |||
@@ -69,6 +69,23 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size, | |||
69 | return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); | 69 | return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); |
70 | } | 70 | } |
71 | 71 | ||
72 | int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size, | ||
73 | int inner_map_fd, int max_entries, __u32 map_flags) | ||
74 | { | ||
75 | union bpf_attr attr; | ||
76 | |||
77 | memset(&attr, '\0', sizeof(attr)); | ||
78 | |||
79 | attr.map_type = map_type; | ||
80 | attr.key_size = key_size; | ||
81 | attr.value_size = 4; | ||
82 | attr.inner_map_fd = inner_map_fd; | ||
83 | attr.max_entries = max_entries; | ||
84 | attr.map_flags = map_flags; | ||
85 | |||
86 | return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); | ||
87 | } | ||
88 | |||
72 | int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns, | 89 | int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns, |
73 | size_t insns_cnt, const char *license, | 90 | size_t insns_cnt, const char *license, |
74 | __u32 kern_version, char *log_buf, size_t log_buf_sz) | 91 | __u32 kern_version, char *log_buf, size_t log_buf_sz) |
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 09c3dcac0496..93f021932623 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h | |||
@@ -26,6 +26,8 @@ | |||
26 | 26 | ||
27 | int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, | 27 | int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, |
28 | int max_entries, __u32 map_flags); | 28 | int max_entries, __u32 map_flags); |
29 | int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size, | ||
30 | int inner_map_fd, int max_entries, __u32 map_flags); | ||
29 | 31 | ||
30 | /* Recommend log buffer size */ | 32 | /* Recommend log buffer size */ |
31 | #define BPF_LOG_BUF_SIZE 65536 | 33 | #define BPF_LOG_BUF_SIZE 65536 |
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index d1555e4240c0..f4f43c98cf7f 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c | |||
@@ -38,6 +38,7 @@ | |||
38 | 38 | ||
39 | #define MAX_INSNS 512 | 39 | #define MAX_INSNS 512 |
40 | #define MAX_FIXUPS 8 | 40 | #define MAX_FIXUPS 8 |
41 | #define MAX_NR_MAPS 4 | ||
41 | 42 | ||
42 | struct bpf_test { | 43 | struct bpf_test { |
43 | const char *descr; | 44 | const char *descr; |
@@ -45,6 +46,7 @@ struct bpf_test { | |||
45 | int fixup_map1[MAX_FIXUPS]; | 46 | int fixup_map1[MAX_FIXUPS]; |
46 | int fixup_map2[MAX_FIXUPS]; | 47 | int fixup_map2[MAX_FIXUPS]; |
47 | int fixup_prog[MAX_FIXUPS]; | 48 | int fixup_prog[MAX_FIXUPS]; |
49 | int fixup_map_in_map[MAX_FIXUPS]; | ||
48 | const char *errstr; | 50 | const char *errstr; |
49 | const char *errstr_unpriv; | 51 | const char *errstr_unpriv; |
50 | enum { | 52 | enum { |
@@ -4452,7 +4454,76 @@ static struct bpf_test tests[] = { | |||
4452 | .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", | 4454 | .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", |
4453 | .result = REJECT, | 4455 | .result = REJECT, |
4454 | .result_unpriv = REJECT, | 4456 | .result_unpriv = REJECT, |
4455 | } | 4457 | }, |
4458 | { | ||
4459 | "map in map access", | ||
4460 | .insns = { | ||
4461 | BPF_ST_MEM(0, BPF_REG_10, -4, 0), | ||
4462 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | ||
4463 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), | ||
4464 | BPF_LD_MAP_FD(BPF_REG_1, 0), | ||
4465 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, | ||
4466 | BPF_FUNC_map_lookup_elem), | ||
4467 | BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), | ||
4468 | BPF_ST_MEM(0, BPF_REG_10, -4, 0), | ||
4469 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | ||
4470 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), | ||
4471 | BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), | ||
4472 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, | ||
4473 | BPF_FUNC_map_lookup_elem), | ||
4474 | BPF_MOV64_REG(BPF_REG_0, 0), | ||
4475 | BPF_EXIT_INSN(), | ||
4476 | }, | ||
4477 | .fixup_map_in_map = { 3 }, | ||
4478 | .result = ACCEPT, | ||
4479 | }, | ||
4480 | { | ||
4481 | "invalid inner map pointer", | ||
4482 | .insns = { | ||
4483 | BPF_ST_MEM(0, BPF_REG_10, -4, 0), | ||
4484 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | ||
4485 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), | ||
4486 | BPF_LD_MAP_FD(BPF_REG_1, 0), | ||
4487 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, | ||
4488 | BPF_FUNC_map_lookup_elem), | ||
4489 | BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), | ||
4490 | BPF_ST_MEM(0, BPF_REG_10, -4, 0), | ||
4491 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | ||
4492 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), | ||
4493 | BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), | ||
4494 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8), | ||
4495 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, | ||
4496 | BPF_FUNC_map_lookup_elem), | ||
4497 | BPF_MOV64_REG(BPF_REG_0, 0), | ||
4498 | BPF_EXIT_INSN(), | ||
4499 | }, | ||
4500 | .fixup_map_in_map = { 3 }, | ||
4501 | .errstr = "R1 type=inv expected=map_ptr", | ||
4502 | .errstr_unpriv = "R1 pointer arithmetic prohibited", | ||
4503 | .result = REJECT, | ||
4504 | }, | ||
4505 | { | ||
4506 | "forgot null checking on the inner map pointer", | ||
4507 | .insns = { | ||
4508 | BPF_ST_MEM(0, BPF_REG_10, -4, 0), | ||
4509 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | ||
4510 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), | ||
4511 | BPF_LD_MAP_FD(BPF_REG_1, 0), | ||
4512 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, | ||
4513 | BPF_FUNC_map_lookup_elem), | ||
4514 | BPF_ST_MEM(0, BPF_REG_10, -4, 0), | ||
4515 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | ||
4516 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), | ||
4517 | BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), | ||
4518 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, | ||
4519 | BPF_FUNC_map_lookup_elem), | ||
4520 | BPF_MOV64_REG(BPF_REG_0, 0), | ||
4521 | BPF_EXIT_INSN(), | ||
4522 | }, | ||
4523 | .fixup_map_in_map = { 3 }, | ||
4524 | .errstr = "R1 type=map_value_or_null expected=map_ptr", | ||
4525 | .result = REJECT, | ||
4526 | }, | ||
4456 | }; | 4527 | }; |
4457 | 4528 | ||
4458 | static int probe_filter_length(const struct bpf_insn *fp) | 4529 | static int probe_filter_length(const struct bpf_insn *fp) |
@@ -4489,42 +4560,73 @@ static int create_prog_array(void) | |||
4489 | return fd; | 4560 | return fd; |
4490 | } | 4561 | } |
4491 | 4562 | ||
4563 | static int create_map_in_map(void) | ||
4564 | { | ||
4565 | int inner_map_fd, outer_map_fd; | ||
4566 | |||
4567 | inner_map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), | ||
4568 | sizeof(int), 1, 0); | ||
4569 | if (inner_map_fd < 0) { | ||
4570 | printf("Failed to create array '%s'!\n", strerror(errno)); | ||
4571 | return inner_map_fd; | ||
4572 | } | ||
4573 | |||
4574 | outer_map_fd = bpf_create_map_in_map(BPF_MAP_TYPE_ARRAY_OF_MAPS, | ||
4575 | sizeof(int), inner_map_fd, 1, 0); | ||
4576 | if (outer_map_fd < 0) | ||
4577 | printf("Failed to create array of maps '%s'!\n", | ||
4578 | strerror(errno)); | ||
4579 | |||
4580 | close(inner_map_fd); | ||
4581 | |||
4582 | return outer_map_fd; | ||
4583 | } | ||
4584 | |||
4492 | static char bpf_vlog[32768]; | 4585 | static char bpf_vlog[32768]; |
4493 | 4586 | ||
4494 | static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, | 4587 | static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, |
4495 | int *fd_f1, int *fd_f2, int *fd_f3) | 4588 | int *map_fds) |
4496 | { | 4589 | { |
4497 | int *fixup_map1 = test->fixup_map1; | 4590 | int *fixup_map1 = test->fixup_map1; |
4498 | int *fixup_map2 = test->fixup_map2; | 4591 | int *fixup_map2 = test->fixup_map2; |
4499 | int *fixup_prog = test->fixup_prog; | 4592 | int *fixup_prog = test->fixup_prog; |
4593 | int *fixup_map_in_map = test->fixup_map_in_map; | ||
4500 | 4594 | ||
4501 | /* Allocating HTs with 1 elem is fine here, since we only test | 4595 | /* Allocating HTs with 1 elem is fine here, since we only test |
4502 | * for verifier and not do a runtime lookup, so the only thing | 4596 | * for verifier and not do a runtime lookup, so the only thing |
4503 | * that really matters is value size in this case. | 4597 | * that really matters is value size in this case. |
4504 | */ | 4598 | */ |
4505 | if (*fixup_map1) { | 4599 | if (*fixup_map1) { |
4506 | *fd_f1 = create_map(sizeof(long long), 1); | 4600 | map_fds[0] = create_map(sizeof(long long), 1); |
4507 | do { | 4601 | do { |
4508 | prog[*fixup_map1].imm = *fd_f1; | 4602 | prog[*fixup_map1].imm = map_fds[0]; |
4509 | fixup_map1++; | 4603 | fixup_map1++; |
4510 | } while (*fixup_map1); | 4604 | } while (*fixup_map1); |
4511 | } | 4605 | } |
4512 | 4606 | ||
4513 | if (*fixup_map2) { | 4607 | if (*fixup_map2) { |
4514 | *fd_f2 = create_map(sizeof(struct test_val), 1); | 4608 | map_fds[1] = create_map(sizeof(struct test_val), 1); |
4515 | do { | 4609 | do { |
4516 | prog[*fixup_map2].imm = *fd_f2; | 4610 | prog[*fixup_map2].imm = map_fds[1]; |
4517 | fixup_map2++; | 4611 | fixup_map2++; |
4518 | } while (*fixup_map2); | 4612 | } while (*fixup_map2); |
4519 | } | 4613 | } |
4520 | 4614 | ||
4521 | if (*fixup_prog) { | 4615 | if (*fixup_prog) { |
4522 | *fd_f3 = create_prog_array(); | 4616 | map_fds[2] = create_prog_array(); |
4523 | do { | 4617 | do { |
4524 | prog[*fixup_prog].imm = *fd_f3; | 4618 | prog[*fixup_prog].imm = map_fds[2]; |
4525 | fixup_prog++; | 4619 | fixup_prog++; |
4526 | } while (*fixup_prog); | 4620 | } while (*fixup_prog); |
4527 | } | 4621 | } |
4622 | |||
4623 | if (*fixup_map_in_map) { | ||
4624 | map_fds[3] = create_map_in_map(); | ||
4625 | do { | ||
4626 | prog[*fixup_map_in_map].imm = map_fds[3]; | ||
4627 | fixup_map_in_map++; | ||
4628 | } while (*fixup_map_in_map); | ||
4629 | } | ||
4528 | } | 4630 | } |
4529 | 4631 | ||
4530 | static void do_test_single(struct bpf_test *test, bool unpriv, | 4632 | static void do_test_single(struct bpf_test *test, bool unpriv, |
@@ -4533,11 +4635,15 @@ static void do_test_single(struct bpf_test *test, bool unpriv, | |||
4533 | struct bpf_insn *prog = test->insns; | 4635 | struct bpf_insn *prog = test->insns; |
4534 | int prog_len = probe_filter_length(prog); | 4636 | int prog_len = probe_filter_length(prog); |
4535 | int prog_type = test->prog_type; | 4637 | int prog_type = test->prog_type; |
4536 | int fd_f1 = -1, fd_f2 = -1, fd_f3 = -1; | 4638 | int map_fds[MAX_NR_MAPS]; |
4537 | int fd_prog, expected_ret; | 4639 | int fd_prog, expected_ret; |
4538 | const char *expected_err; | 4640 | const char *expected_err; |
4641 | int i; | ||
4642 | |||
4643 | for (i = 0; i < MAX_NR_MAPS; i++) | ||
4644 | map_fds[i] = -1; | ||
4539 | 4645 | ||
4540 | do_test_fixup(test, prog, &fd_f1, &fd_f2, &fd_f3); | 4646 | do_test_fixup(test, prog, map_fds); |
4541 | 4647 | ||
4542 | fd_prog = bpf_load_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, | 4648 | fd_prog = bpf_load_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, |
4543 | prog, prog_len, "GPL", 0, bpf_vlog, | 4649 | prog, prog_len, "GPL", 0, bpf_vlog, |
@@ -4568,9 +4674,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv, | |||
4568 | printf("OK\n"); | 4674 | printf("OK\n"); |
4569 | close_fds: | 4675 | close_fds: |
4570 | close(fd_prog); | 4676 | close(fd_prog); |
4571 | close(fd_f1); | 4677 | for (i = 0; i < MAX_NR_MAPS; i++) |
4572 | close(fd_f2); | 4678 | close(map_fds[i]); |
4573 | close(fd_f3); | ||
4574 | sched_yield(); | 4679 | sched_yield(); |
4575 | return; | 4680 | return; |
4576 | fail_log: | 4681 | fail_log: |