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