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
25static struct bpf_insn prog[BPF_MAXINSNS];
26
27static 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
37static 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
53static 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
68static 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
78static 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
91static 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
114static 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
144static 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
154static 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
165static 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
185int 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

source code of linux/tools/testing/selftests/bpf/test_tag.c