-
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.
bpf: add prog tag test case to bpf selftests
Add the test case used to compare the results from fdinfo with af_alg's output on the tag. Tests are from min to max sized programs, with and without maps included. # ./test_tag test_tag: OK (40945 tests) Tested on x86_64 and s390x. Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
- Loading branch information
Daniel Borkmann
authored and
David S. Miller
committed
Jan 24, 2017
1 parent
d1b662a
commit 62b6466
Showing
2 changed files
with
204 additions
and
2 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <ctype.h> | ||
#include <time.h> | ||
#include <errno.h> | ||
#include <unistd.h> | ||
#include <string.h> | ||
#include <sched.h> | ||
#include <limits.h> | ||
#include <assert.h> | ||
|
||
#include <sys/socket.h> | ||
#include <sys/resource.h> | ||
|
||
#include <linux/filter.h> | ||
#include <linux/bpf.h> | ||
#include <linux/if_alg.h> | ||
|
||
#include "../../../include/linux/filter.h" | ||
|
||
#include "bpf_sys.h" | ||
|
||
static struct bpf_insn prog[BPF_MAXINSNS]; | ||
|
||
static void bpf_gen_imm_prog(unsigned int insns, int fd_map) | ||
{ | ||
int i; | ||
|
||
srand(time(NULL)); | ||
for (i = 0; i < insns; i++) | ||
prog[i] = BPF_ALU64_IMM(BPF_MOV, i % BPF_REG_10, rand()); | ||
prog[i - 1] = BPF_EXIT_INSN(); | ||
} | ||
|
||
static void bpf_gen_map_prog(unsigned int insns, int fd_map) | ||
{ | ||
int i, j = 0; | ||
|
||
for (i = 0; i + 1 < insns; i += 2) { | ||
struct bpf_insn tmp[] = { | ||
BPF_LD_MAP_FD(j++ % BPF_REG_10, fd_map) | ||
}; | ||
|
||
memcpy(&prog[i], tmp, sizeof(tmp)); | ||
} | ||
if (insns % 2 == 0) | ||
prog[insns - 2] = BPF_ALU64_IMM(BPF_MOV, i % BPF_REG_10, 42); | ||
prog[insns - 1] = BPF_EXIT_INSN(); | ||
} | ||
|
||
static int bpf_try_load_prog(int insns, int fd_map, | ||
void (*bpf_filler)(unsigned int insns, | ||
int fd_map)) | ||
{ | ||
int fd_prog; | ||
|
||
bpf_filler(insns, fd_map); | ||
fd_prog = bpf_prog_load(BPF_PROG_TYPE_SCHED_CLS, prog, insns * | ||
sizeof(struct bpf_insn), "", NULL, 0); | ||
assert(fd_prog > 0); | ||
if (fd_map > 0) | ||
bpf_filler(insns, 0); | ||
return fd_prog; | ||
} | ||
|
||
static int __hex2bin(char ch) | ||
{ | ||
if ((ch >= '0') && (ch <= '9')) | ||
return ch - '0'; | ||
ch = tolower(ch); | ||
if ((ch >= 'a') && (ch <= 'f')) | ||
return ch - 'a' + 10; | ||
return -1; | ||
} | ||
|
||
static int hex2bin(uint8_t *dst, const char *src, size_t count) | ||
{ | ||
while (count--) { | ||
int hi = __hex2bin(*src++); | ||
int lo = __hex2bin(*src++); | ||
|
||
if ((hi < 0) || (lo < 0)) | ||
return -1; | ||
*dst++ = (hi << 4) | lo; | ||
} | ||
return 0; | ||
} | ||
|
||
static void tag_from_fdinfo(int fd_prog, uint8_t *tag, uint32_t len) | ||
{ | ||
const int prefix_len = sizeof("prog_tag:\t") - 1; | ||
char buff[256]; | ||
int ret = -1; | ||
FILE *fp; | ||
|
||
snprintf(buff, sizeof(buff), "/proc/%d/fdinfo/%d", getpid(), | ||
fd_prog); | ||
fp = fopen(buff, "r"); | ||
assert(fp); | ||
|
||
while (fgets(buff, sizeof(buff), fp)) { | ||
if (strncmp(buff, "prog_tag:\t", len)) | ||
continue; | ||
ret = hex2bin(tag, buff + prefix_len, len); | ||
break; | ||
} | ||
|
||
fclose(fp); | ||
assert(!ret); | ||
} | ||
|
||
static void tag_from_alg(int insns, uint8_t *tag, uint32_t len) | ||
{ | ||
static const struct sockaddr_alg alg = { | ||
.salg_family = AF_ALG, | ||
.salg_type = "hash", | ||
.salg_name = "sha1", | ||
}; | ||
int fd_base, fd_alg, ret; | ||
ssize_t size; | ||
|
||
fd_base = socket(AF_ALG, SOCK_SEQPACKET, 0); | ||
assert(fd_base > 0); | ||
|
||
ret = bind(fd_base, (struct sockaddr *)&alg, sizeof(alg)); | ||
assert(!ret); | ||
|
||
fd_alg = accept(fd_base, NULL, 0); | ||
assert(fd_alg > 0); | ||
|
||
insns *= sizeof(struct bpf_insn); | ||
size = write(fd_alg, prog, insns); | ||
assert(size == insns); | ||
|
||
size = read(fd_alg, tag, len); | ||
assert(size == len); | ||
|
||
close(fd_alg); | ||
close(fd_base); | ||
} | ||
|
||
static void tag_dump(const char *prefix, uint8_t *tag, uint32_t len) | ||
{ | ||
int i; | ||
|
||
printf("%s", prefix); | ||
for (i = 0; i < len; i++) | ||
printf("%02x", tag[i]); | ||
printf("\n"); | ||
} | ||
|
||
static void tag_exit_report(int insns, int fd_map, uint8_t *ftag, | ||
uint8_t *atag, uint32_t len) | ||
{ | ||
printf("Program tag mismatch for %d insns%s!\n", insns, | ||
fd_map < 0 ? "" : " with map"); | ||
|
||
tag_dump(" fdinfo result: ", ftag, len); | ||
tag_dump(" af_alg result: ", atag, len); | ||
exit(1); | ||
} | ||
|
||
static void do_test(uint32_t *tests, int start_insns, int fd_map, | ||
void (*bpf_filler)(unsigned int insns, int fd)) | ||
{ | ||
int i, fd_prog; | ||
|
||
for (i = start_insns; i <= BPF_MAXINSNS; i++) { | ||
uint8_t ftag[8], atag[sizeof(ftag)]; | ||
|
||
fd_prog = bpf_try_load_prog(i, fd_map, bpf_filler); | ||
tag_from_fdinfo(fd_prog, ftag, sizeof(ftag)); | ||
tag_from_alg(i, atag, sizeof(atag)); | ||
if (memcmp(ftag, atag, sizeof(ftag))) | ||
tag_exit_report(i, fd_map, ftag, atag, sizeof(ftag)); | ||
|
||
close(fd_prog); | ||
sched_yield(); | ||
(*tests)++; | ||
} | ||
} | ||
|
||
int main(void) | ||
{ | ||
struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; | ||
uint32_t tests = 0; | ||
int i, fd_map; | ||
|
||
setrlimit(RLIMIT_MEMLOCK, &rinf); | ||
fd_map = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(int), | ||
sizeof(int), 1, BPF_F_NO_PREALLOC); | ||
assert(fd_map > 0); | ||
|
||
for (i = 0; i < 5; i++) { | ||
do_test(&tests, 2, -1, bpf_gen_imm_prog); | ||
do_test(&tests, 3, fd_map, bpf_gen_map_prog); | ||
} | ||
|
||
printf("test_tag: OK (%u tests)\n", tests); | ||
close(fd_map); | ||
return 0; | ||
} |