2022 lines · 43.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0// Copyright (c) 2017-2018 Covalent IO, Inc. http://covalent.io#include <stdio.h>#include <stdlib.h>#include <sys/socket.h>#include <sys/ioctl.h>#include <sys/select.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <stdbool.h>#include <signal.h>#include <fcntl.h>#include <sys/wait.h>#include <time.h>#include <sched.h> #include <sys/time.h>#include <sys/types.h>#include <sys/sendfile.h> #include <linux/netlink.h>#include <linux/socket.h>#include <linux/sock_diag.h>#include <linux/bpf.h>#include <linux/if_link.h>#include <assert.h>#include <libgen.h> #include <getopt.h> #include <bpf/bpf.h>#include <bpf/libbpf.h> #include "bpf_util.h"#include "cgroup_helpers.h" int running;static void running_handler(int a); /* randomly selected ports for testing on lo */#define S1_PORT 10000#define S2_PORT 10001 #define BPF_SOCKMAP_FILENAME  "test_sockmap_kern.bpf.o"#define BPF_SOCKHASH_FILENAME "test_sockhash_kern.bpf.o"#define CG_PATH "/sockmap" #define EDATAINTEGRITY 2001 /* global sockets */int s1, s2, c1, c2, p1, p2;int test_cnt;int passed;int failed;int map_fd[8];struct bpf_map *maps[8];struct bpf_program *progs[8];struct bpf_link *links[8]; int txmsg_pass;int txmsg_redir;int txmsg_drop;int txmsg_apply;int txmsg_cork;int txmsg_start;int txmsg_end;int txmsg_start_push;int txmsg_end_push;int txmsg_start_pop;int txmsg_pop;int txmsg_ingress;int txmsg_redir_skb;int peek_flag;int skb_use_parser;int txmsg_omit_skb_parser;int verify_push_start;int verify_push_len;int verify_pop_start;int verify_pop_len; static const struct option long_options[] = {	{"help",	no_argument,		NULL, 'h' },	{"cgroup",	required_argument,	NULL, 'c' },	{"rate",	required_argument,	NULL, 'r' },	{"verbose",	optional_argument,	NULL, 'v' },	{"iov_count",	required_argument,	NULL, 'i' },	{"length",	required_argument,	NULL, 'l' },	{"test",	required_argument,	NULL, 't' },	{"data_test",   no_argument,		NULL, 'd' },	{"txmsg",		no_argument,	&txmsg_pass,  1  },	{"txmsg_redir",		no_argument,	&txmsg_redir, 1  },	{"txmsg_drop",		no_argument,	&txmsg_drop, 1 },	{"txmsg_apply",	required_argument,	NULL, 'a'},	{"txmsg_cork",	required_argument,	NULL, 'k'},	{"txmsg_start", required_argument,	NULL, 's'},	{"txmsg_end",	required_argument,	NULL, 'e'},	{"txmsg_start_push", required_argument,	NULL, 'p'},	{"txmsg_end_push",   required_argument,	NULL, 'q'},	{"txmsg_start_pop",  required_argument,	NULL, 'w'},	{"txmsg_pop",	     required_argument,	NULL, 'x'},	{"txmsg_ingress", no_argument,		&txmsg_ingress, 1 },	{"txmsg_redir_skb", no_argument,	&txmsg_redir_skb, 1 },	{"peek", no_argument,			&peek_flag, 1 },	{"txmsg_omit_skb_parser", no_argument,      &txmsg_omit_skb_parser, 1},	{"whitelist", required_argument,	NULL, 'n' },	{"blacklist", required_argument,	NULL, 'b' },	{0, 0, NULL, 0 }}; struct test_env {	const char *type;	const char *subtest;	const char *prepend; 	int test_num;	int subtest_num; 	int succ_cnt;	int fail_cnt;	int fail_last;}; struct test_env env; struct sockmap_options {	int verbose;	bool base;	bool sendpage;	bool data_test;	bool drop_expected;	bool check_recved_len;	bool tx_wait_mem;	int iov_count;	int iov_length;	int rate;	char *map;	char *whitelist;	char *blacklist;	char *prepend;}; struct _test {	char *title;	void (*tester)(int cg_fd, struct sockmap_options *opt);}; static void test_start(void){	env.subtest_num++;} static void test_fail(void){	env.fail_cnt++;} static void test_pass(void){	env.succ_cnt++;} static void test_reset(void){	txmsg_start = txmsg_end = 0;	txmsg_start_pop = txmsg_pop = 0;	txmsg_start_push = txmsg_end_push = 0;	txmsg_pass = txmsg_drop = txmsg_redir = 0;	txmsg_apply = txmsg_cork = 0;	txmsg_ingress = txmsg_redir_skb = 0;	txmsg_omit_skb_parser = 0;	skb_use_parser = 0;} static int test_start_subtest(const struct _test *t, struct sockmap_options *o){	env.type = o->map;	env.subtest = t->title;	env.prepend = o->prepend;	env.test_num++;	env.subtest_num = 0;	env.fail_last = env.fail_cnt;	test_reset();	return 0;} static void test_end_subtest(void){	int error = env.fail_cnt - env.fail_last;	int type = strcmp(env.type, BPF_SOCKMAP_FILENAME); 	if (!error)		test_pass(); 	fprintf(stdout, "#%2d/%2d %8s:%s:%s:%s\n",		env.test_num, env.subtest_num,		!type ? "sockmap" : "sockhash",		env.prepend ? : "",		env.subtest, error ? "FAIL" : "OK");} static void test_print_results(void){	fprintf(stdout, "Pass: %d Fail: %d\n",		env.succ_cnt, env.fail_cnt);} static void usage(char *argv[]){	int i; 	printf(" Usage: %s --cgroup <cgroup_path>\n", argv[0]);	printf(" options:\n");	for (i = 0; long_options[i].name != 0; i++) {		printf(" --%-12s", long_options[i].name);		if (long_options[i].flag != NULL)			printf(" flag (internal value:%d)\n",				*long_options[i].flag);		else			printf(" -%c\n", long_options[i].val);	}	printf("\n");} static int sockmap_init_sockets(int verbose){	int i, err, one = 1;	struct sockaddr_in addr;	int *fds[4] = {&s1, &s2, &c1, &c2}; 	s1 = s2 = p1 = p2 = c1 = c2 = 0; 	/* Init sockets */	for (i = 0; i < 4; i++) {		*fds[i] = socket(AF_INET, SOCK_STREAM, 0);		if (*fds[i] < 0) {			perror("socket s1 failed()");			return errno;		}	} 	/* Allow reuse */	for (i = 0; i < 2; i++) {		err = setsockopt(*fds[i], SOL_SOCKET, SO_REUSEADDR,				 (char *)&one, sizeof(one));		if (err) {			perror("setsockopt failed()");			return errno;		}	} 	/* Non-blocking sockets */	for (i = 0; i < 2; i++) {		err = ioctl(*fds[i], FIONBIO, (char *)&one);		if (err < 0) {			perror("ioctl s1 failed()");			return errno;		}	} 	/* Bind server sockets */	memset(&addr, 0, sizeof(struct sockaddr_in));	addr.sin_family = AF_INET;	addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 	addr.sin_port = htons(S1_PORT);	err = bind(s1, (struct sockaddr *)&addr, sizeof(addr));	if (err < 0) {		perror("bind s1 failed()");		return errno;	} 	addr.sin_port = htons(S2_PORT);	err = bind(s2, (struct sockaddr *)&addr, sizeof(addr));	if (err < 0) {		perror("bind s2 failed()");		return errno;	} 	/* Listen server sockets */	addr.sin_port = htons(S1_PORT);	err = listen(s1, 32);	if (err < 0) {		perror("listen s1 failed()");		return errno;	} 	addr.sin_port = htons(S2_PORT);	err = listen(s2, 32);	if (err < 0) {		perror("listen s1 failed()");		return errno;	} 	/* Initiate Connect */	addr.sin_port = htons(S1_PORT);	err = connect(c1, (struct sockaddr *)&addr, sizeof(addr));	if (err < 0 && errno != EINPROGRESS) {		perror("connect c1 failed()");		return errno;	} 	addr.sin_port = htons(S2_PORT);	err = connect(c2, (struct sockaddr *)&addr, sizeof(addr));	if (err < 0 && errno != EINPROGRESS) {		perror("connect c2 failed()");		return errno;	} else if (err < 0) {		err = 0;	} 	/* Accept Connecrtions */	p1 = accept(s1, NULL, NULL);	if (p1 < 0) {		perror("accept s1 failed()");		return errno;	} 	p2 = accept(s2, NULL, NULL);	if (p2 < 0) {		perror("accept s1 failed()");		return errno;	} 	if (verbose > 1) {		printf("connected sockets: c1 <-> p1, c2 <-> p2\n");		printf("cgroups binding: c1(%i) <-> s1(%i) - - - c2(%i) <-> s2(%i)\n",			c1, s1, c2, s2);	}	return 0;} struct msg_stats {	size_t bytes_sent;	size_t bytes_recvd;	struct timespec start;	struct timespec end;}; static int msg_loop_sendpage(int fd, int iov_length, int cnt,			     struct msg_stats *s,			     struct sockmap_options *opt){	bool drop = opt->drop_expected;	unsigned char k = 0;	int i, j, fp;	FILE *file; 	file = tmpfile();	if (!file) {		perror("create file for sendpage");		return 1;	}	for (i = 0; i < cnt; i++, k = 0) {		for (j = 0; j < iov_length; j++, k++)			fwrite(&k, sizeof(char), 1, file);	}	fflush(file);	fseek(file, 0, SEEK_SET); 	fp = fileno(file); 	clock_gettime(CLOCK_MONOTONIC, &s->start);	for (i = 0; i < cnt; i++) {		int sent; 		errno = 0;		sent = sendfile(fd, fp, NULL, iov_length); 		if (!drop && sent < 0) {			perror("sendpage loop error");			fclose(file);			return sent;		} else if (drop && sent >= 0) {			printf("sendpage loop error expected: %i errno %i\n",			       sent, errno);			fclose(file);			return -EIO;		} 		if (sent > 0)			s->bytes_sent += sent;	}	clock_gettime(CLOCK_MONOTONIC, &s->end);	fclose(file);	return 0;} static void msg_free_iov(struct msghdr *msg){	int i; 	for (i = 0; i < msg->msg_iovlen; i++)		free(msg->msg_iov[i].iov_base);	free(msg->msg_iov);	msg->msg_iov = NULL;	msg->msg_iovlen = 0;} static int msg_alloc_iov(struct msghdr *msg,			 int iov_count, int iov_length,			 bool data, bool xmit){	unsigned char k = 0;	struct iovec *iov;	int i; 	iov = calloc(iov_count, sizeof(struct iovec));	if (!iov)		return errno; 	for (i = 0; i < iov_count; i++) {		unsigned char *d = calloc(iov_length, sizeof(char)); 		if (!d) {			fprintf(stderr, "iov_count %i/%i OOM\n", i, iov_count);			goto unwind_iov;		}		iov[i].iov_base = d;		iov[i].iov_len = iov_length; 		if (data && xmit) {			int j; 			for (j = 0; j < iov_length; j++)				d[j] = k++;		}	} 	msg->msg_iov = iov;	msg->msg_iovlen = iov_count; 	return 0;unwind_iov:	for (i--; i >= 0 ; i--)		free(iov[i].iov_base);	free(iov);	return -ENOMEM;} /* In push or pop test, we need to do some calculations for msg_verify_data */static void msg_verify_date_prep(void){	int push_range_end = txmsg_start_push + txmsg_end_push - 1;	int pop_range_end = txmsg_start_pop + txmsg_pop - 1; 	if (txmsg_end_push && txmsg_pop &&	    txmsg_start_push <= pop_range_end && txmsg_start_pop <= push_range_end) {		/* The push range and the pop range overlap */		int overlap_len; 		verify_push_start = txmsg_start_push;		verify_pop_start = txmsg_start_pop;		if (txmsg_start_push < txmsg_start_pop)			overlap_len = min(push_range_end - txmsg_start_pop + 1, txmsg_pop);		else			overlap_len = min(pop_range_end - txmsg_start_push + 1, txmsg_end_push);		verify_push_len = max(txmsg_end_push - overlap_len, 0);		verify_pop_len = max(txmsg_pop - overlap_len, 0);	} else {		/* Otherwise */		verify_push_start = txmsg_start_push;		verify_pop_start = txmsg_start_pop;		verify_push_len = txmsg_end_push;		verify_pop_len = txmsg_pop;	}} static int msg_verify_data(struct msghdr *msg, int size, int chunk_sz,			   unsigned char *k_p, int *bytes_cnt_p,			   int *check_cnt_p, int *push_p){	int bytes_cnt = *bytes_cnt_p, check_cnt = *check_cnt_p, push = *push_p;	unsigned char k = *k_p;	int i, j; 	for (i = 0, j = 0; i < msg->msg_iovlen && size; i++, j = 0) {		unsigned char *d = msg->msg_iov[i].iov_base; 		for (; j < msg->msg_iov[i].iov_len && size; j++) {			if (push > 0 &&			    check_cnt == verify_push_start + verify_push_len - push) {				int skipped;revisit_push:				skipped = push;				if (j + push >= msg->msg_iov[i].iov_len)					skipped = msg->msg_iov[i].iov_len - j;				push -= skipped;				size -= skipped;				j += skipped - 1;				check_cnt += skipped;				continue;			} 			if (verify_pop_len > 0 && check_cnt == verify_pop_start) {				bytes_cnt += verify_pop_len;				check_cnt += verify_pop_len;				k += verify_pop_len;

Lines 1–500 of 2022.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t