1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <stdint.h> |
3 | #include <stdio.h> |
4 | #include <stdlib.h> |
5 | #include <ctype.h> |
6 | #include <time.h> |
7 | #include <errno.h> |
8 | #include <unistd.h> |
9 | #include <string.h> |
10 | #include <sched.h> |
11 | #include <limits.h> |
12 | #include <assert.h> |
13 | |
14 | #include <sys/socket.h> |
15 | |
16 | #include <linux/filter.h> |
17 | #include <linux/bpf.h> |
18 | #include <linux/if_alg.h> |
19 | |
20 | #include <bpf/bpf.h> |
21 | |
22 | #include "../../../include/linux/filter.h" |
23 | #include "testing_helpers.h" |
24 | |
25 | static struct bpf_insn prog[BPF_MAXINSNS]; |
26 | |
27 | static void bpf_gen_imm_prog(unsigned int insns, int fd_map) |
28 | { |
29 | int i; |
30 | |
31 | srand(time(NULL)); |
32 | for (i = 0; i < insns; i++) |
33 | prog[i] = BPF_ALU64_IMM(BPF_MOV, i % BPF_REG_10, rand()); |
34 | prog[i - 1] = BPF_EXIT_INSN(); |
35 | } |
36 | |
37 | static void bpf_gen_map_prog(unsigned int insns, int fd_map) |
38 | { |
39 | int i, j = 0; |
40 | |
41 | for (i = 0; i + 1 < insns; i += 2) { |
42 | struct bpf_insn tmp[] = { |
43 | BPF_LD_MAP_FD(j++ % BPF_REG_10, fd_map) |
44 | }; |
45 | |
46 | memcpy(&prog[i], tmp, sizeof(tmp)); |
47 | } |
48 | if (insns % 2 == 0) |
49 | prog[insns - 2] = BPF_ALU64_IMM(BPF_MOV, i % BPF_REG_10, 42); |
50 | prog[insns - 1] = BPF_EXIT_INSN(); |
51 | } |
52 | |
53 | static int bpf_try_load_prog(int insns, int fd_map, |
54 | void (*bpf_filler)(unsigned int insns, |
55 | int fd_map)) |
56 | { |
57 | int fd_prog; |
58 | |
59 | bpf_filler(insns, fd_map); |
60 | fd_prog = bpf_test_load_program(type: BPF_PROG_TYPE_SCHED_CLS, insns: prog, insns_cnt: insns, license: "" , kern_version: 0, |
61 | NULL, log_buf_sz: 0); |
62 | assert(fd_prog > 0); |
63 | if (fd_map > 0) |
64 | bpf_filler(insns, 0); |
65 | return fd_prog; |
66 | } |
67 | |
68 | static int __hex2bin(char ch) |
69 | { |
70 | if ((ch >= '0') && (ch <= '9')) |
71 | return ch - '0'; |
72 | ch = tolower(ch); |
73 | if ((ch >= 'a') && (ch <= 'f')) |
74 | return ch - 'a' + 10; |
75 | return -1; |
76 | } |
77 | |
78 | static int hex2bin(uint8_t *dst, const char *src, size_t count) |
79 | { |
80 | while (count--) { |
81 | int hi = __hex2bin(ch: *src++); |
82 | int lo = __hex2bin(ch: *src++); |
83 | |
84 | if ((hi < 0) || (lo < 0)) |
85 | return -1; |
86 | *dst++ = (hi << 4) | lo; |
87 | } |
88 | return 0; |
89 | } |
90 | |
91 | static void tag_from_fdinfo(int fd_prog, uint8_t *tag, uint32_t len) |
92 | { |
93 | const int prefix_len = sizeof("prog_tag:\t" ) - 1; |
94 | char buff[256]; |
95 | int ret = -1; |
96 | FILE *fp; |
97 | |
98 | snprintf(buf: buff, size: sizeof(buff), fmt: "/proc/%d/fdinfo/%d" , getpid(), |
99 | fd_prog); |
100 | fp = fopen(buff, "r" ); |
101 | assert(fp); |
102 | |
103 | while (fgets(buff, sizeof(buff), fp)) { |
104 | if (strncmp(buff, "prog_tag:\t" , prefix_len)) |
105 | continue; |
106 | ret = hex2bin(dst: tag, src: buff + prefix_len, count: len); |
107 | break; |
108 | } |
109 | |
110 | fclose(fp); |
111 | assert(!ret); |
112 | } |
113 | |
114 | static void tag_from_alg(int insns, uint8_t *tag, uint32_t len) |
115 | { |
116 | static const struct sockaddr_alg alg = { |
117 | .salg_family = AF_ALG, |
118 | .salg_type = "hash" , |
119 | .salg_name = "sha1" , |
120 | }; |
121 | int fd_base, fd_alg, ret; |
122 | ssize_t size; |
123 | |
124 | fd_base = socket(AF_ALG, SOCK_SEQPACKET, 0); |
125 | assert(fd_base > 0); |
126 | |
127 | ret = bind(fd_base, (struct sockaddr *)&alg, sizeof(alg)); |
128 | assert(!ret); |
129 | |
130 | fd_alg = accept(fd_base, NULL, 0); |
131 | assert(fd_alg > 0); |
132 | |
133 | insns *= sizeof(struct bpf_insn); |
134 | size = write(fd_alg, prog, insns); |
135 | assert(size == insns); |
136 | |
137 | size = read(fd_alg, tag, len); |
138 | assert(size == len); |
139 | |
140 | close(fd_alg); |
141 | close(fd_base); |
142 | } |
143 | |
144 | static void tag_dump(const char *prefix, uint8_t *tag, uint32_t len) |
145 | { |
146 | int i; |
147 | |
148 | printf("%s" , prefix); |
149 | for (i = 0; i < len; i++) |
150 | printf("%02x" , tag[i]); |
151 | printf("\n" ); |
152 | } |
153 | |
154 | static void tag_exit_report(int insns, int fd_map, uint8_t *ftag, |
155 | uint8_t *atag, uint32_t len) |
156 | { |
157 | printf("Program tag mismatch for %d insns%s!\n" , insns, |
158 | fd_map < 0 ? "" : " with map" ); |
159 | |
160 | tag_dump(prefix: " fdinfo result: " , tag: ftag, len); |
161 | tag_dump(prefix: " af_alg result: " , tag: atag, len); |
162 | exit(1); |
163 | } |
164 | |
165 | static void do_test(uint32_t *tests, int start_insns, int fd_map, |
166 | void (*bpf_filler)(unsigned int insns, int fd)) |
167 | { |
168 | int i, fd_prog; |
169 | |
170 | for (i = start_insns; i <= BPF_MAXINSNS; i++) { |
171 | uint8_t ftag[8], atag[sizeof(ftag)]; |
172 | |
173 | fd_prog = bpf_try_load_prog(insns: i, fd_map, bpf_filler); |
174 | tag_from_fdinfo(fd_prog, tag: ftag, len: sizeof(ftag)); |
175 | tag_from_alg(insns: i, tag: atag, len: sizeof(atag)); |
176 | if (memcmp(p: ftag, q: atag, size: sizeof(ftag))) |
177 | tag_exit_report(insns: i, fd_map, ftag, atag, len: sizeof(ftag)); |
178 | |
179 | close(fd_prog); |
180 | sched_yield(); |
181 | (*tests)++; |
182 | } |
183 | } |
184 | |
185 | int main(void) |
186 | { |
187 | LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_NO_PREALLOC); |
188 | uint32_t tests = 0; |
189 | int i, fd_map; |
190 | |
191 | /* Use libbpf 1.0 API mode */ |
192 | libbpf_set_strict_mode(LIBBPF_STRICT_ALL); |
193 | |
194 | fd_map = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(int), |
195 | sizeof(int), 1, &opts); |
196 | assert(fd_map > 0); |
197 | |
198 | for (i = 0; i < 5; i++) { |
199 | do_test(tests: &tests, start_insns: 2, fd_map: -1, bpf_filler: bpf_gen_imm_prog); |
200 | do_test(tests: &tests, start_insns: 3, fd_map, bpf_filler: bpf_gen_map_prog); |
201 | } |
202 | |
203 | printf("test_tag: OK (%u tests)\n" , tests); |
204 | close(fd_map); |
205 | return 0; |
206 | } |
207 | |