brintos

brintos / linux-shallow public Read only

0
0
Text · 13.1 KiB · 24380c6 Raw
560 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <inttypes.h>3#include <pthread.h>4#include "aolib.h"5 6static const char *trace_event_names[__MAX_TRACE_EVENTS] = {7	/* TCP_HASH_EVENT */8	"tcp_hash_bad_header",9	"tcp_hash_md5_required",10	"tcp_hash_md5_unexpected",11	"tcp_hash_md5_mismatch",12	"tcp_hash_ao_required",13	/* TCP_AO_EVENT */14	"tcp_ao_handshake_failure",15	"tcp_ao_wrong_maclen",16	"tcp_ao_mismatch",17	"tcp_ao_key_not_found",18	"tcp_ao_rnext_request",19	/* TCP_AO_EVENT_SK */20	"tcp_ao_synack_no_key",21	/* TCP_AO_EVENT_SNE */22	"tcp_ao_snd_sne_update",23	"tcp_ao_rcv_sne_update"24};25 26struct expected_trace_point {27	/* required */28	enum trace_events type;29	int family;30	union tcp_addr src;31	union tcp_addr dst;32 33	/* optional */34	int src_port;35	int dst_port;36	int L3index;37 38	int fin;39	int syn;40	int rst;41	int psh;42	int ack;43 44	int keyid;45	int rnext;46	int maclen;47	int sne;48 49	size_t matched;50};51 52static struct expected_trace_point *exp_tps;53static size_t exp_tps_nr;54static size_t exp_tps_size;55static pthread_mutex_t exp_tps_mutex = PTHREAD_MUTEX_INITIALIZER;56 57int __trace_event_expect(enum trace_events type, int family,58			 union tcp_addr src, union tcp_addr dst,59			 int src_port, int dst_port, int L3index,60			 int fin, int syn, int rst, int psh, int ack,61			 int keyid, int rnext, int maclen, int sne)62{63	struct expected_trace_point new_tp = {64		.type           = type,65		.family         = family,66		.src            = src,67		.dst            = dst,68		.src_port       = src_port,69		.dst_port       = dst_port,70		.L3index        = L3index,71		.fin            = fin,72		.syn            = syn,73		.rst            = rst,74		.psh            = psh,75		.ack            = ack,76		.keyid          = keyid,77		.rnext          = rnext,78		.maclen         = maclen,79		.sne            = sne,80		.matched        = 0,81	};82	int ret = 0;83 84	if (!kernel_config_has(KCONFIG_FTRACE))85		return 0;86 87	pthread_mutex_lock(&exp_tps_mutex);88	if (exp_tps_nr == exp_tps_size) {89		struct expected_trace_point *tmp;90 91		if (exp_tps_size == 0)92			exp_tps_size = 10;93		else94			exp_tps_size = exp_tps_size * 1.6;95 96		tmp = reallocarray(exp_tps, exp_tps_size, sizeof(exp_tps[0]));97		if (!tmp) {98			ret = -ENOMEM;99			goto out;100		}101		exp_tps = tmp;102	}103	exp_tps[exp_tps_nr] = new_tp;104	exp_tps_nr++;105out:106	pthread_mutex_unlock(&exp_tps_mutex);107	return ret;108}109 110static void free_expected_events(void)111{112	/* We're from the process destructor - not taking the mutex */113	exp_tps_size = 0;114	exp_tps = NULL;115	free(exp_tps);116}117 118struct trace_point {119	int family;120	union tcp_addr src;121	union tcp_addr dst;122	unsigned int src_port;123	unsigned int dst_port;124	int L3index;125	unsigned int fin:1,126		     syn:1,127		     rst:1,128		     psh:1,129		     ack:1;130 131	unsigned int keyid;132	unsigned int rnext;133	unsigned int maclen;134 135	unsigned int sne;136};137 138static bool lookup_expected_event(int event_type, struct trace_point *e)139{140	size_t i;141 142	pthread_mutex_lock(&exp_tps_mutex);143	for (i = 0; i < exp_tps_nr; i++) {144		struct expected_trace_point *p = &exp_tps[i];145		size_t sk_size;146 147		if (p->type != event_type)148			continue;149		if (p->family != e->family)150			continue;151		if (p->family == AF_INET)152			sk_size = sizeof(p->src.a4);153		else154			sk_size = sizeof(p->src.a6);155		if (memcmp(&p->src, &e->src, sk_size))156			continue;157		if (memcmp(&p->dst, &e->dst, sk_size))158			continue;159		if (p->src_port >= 0 && p->src_port != e->src_port)160			continue;161		if (p->dst_port >= 0 && p->dst_port != e->dst_port)162			continue;163		if (p->L3index >= 0 && p->L3index != e->L3index)164			continue;165 166		if (p->fin >= 0 && p->fin != e->fin)167			continue;168		if (p->syn >= 0 && p->syn != e->syn)169			continue;170		if (p->rst >= 0 && p->rst != e->rst)171			continue;172		if (p->psh >= 0 && p->psh != e->psh)173			continue;174		if (p->ack >= 0 && p->ack != e->ack)175			continue;176 177		if (p->keyid >= 0 && p->keyid != e->keyid)178			continue;179		if (p->rnext >= 0 && p->rnext != e->rnext)180			continue;181		if (p->maclen >= 0 && p->maclen != e->maclen)182			continue;183		if (p->sne >= 0 && p->sne != e->sne)184			continue;185		p->matched++;186		pthread_mutex_unlock(&exp_tps_mutex);187		return true;188	}189	pthread_mutex_unlock(&exp_tps_mutex);190	return false;191}192 193static int check_event_type(const char *line)194{195	size_t i;196 197	/*198	 * This should have been a set or hashmap, but it's a selftest,199	 * so... KISS.200	 */201	for (i = 0; i < __MAX_TRACE_EVENTS; i++) {202		if (!strncmp(trace_event_names[i], line, strlen(trace_event_names[i])))203			return i;204	}205	return -1;206}207 208static bool event_has_flags(enum trace_events event)209{210	switch (event) {211	case TCP_HASH_BAD_HEADER:212	case TCP_HASH_MD5_REQUIRED:213	case TCP_HASH_MD5_UNEXPECTED:214	case TCP_HASH_MD5_MISMATCH:215	case TCP_HASH_AO_REQUIRED:216	case TCP_AO_HANDSHAKE_FAILURE:217	case TCP_AO_WRONG_MACLEN:218	case TCP_AO_MISMATCH:219	case TCP_AO_KEY_NOT_FOUND:220	case TCP_AO_RNEXT_REQUEST:221		return true;222	default:223		return false;224	}225}226 227static int tracer_ip_split(int family, char *src, char **addr, char **port)228{229	char *p;230 231	if (family == AF_INET) {232		/* fomat is <addr>:port, i.e.: 10.0.254.1:7015 */233		*addr = src;234		p = strchr(src, ':');235		if (!p) {236			test_print("Couldn't parse trace event addr:port %s", src);237			return -EINVAL;238		}239		*p++ = '\0';240		*port = p;241		return 0;242	}243	if (family != AF_INET6)244		return -EAFNOSUPPORT;245 246	/* format is [<addr>]:port, i.e.: [2001:db8:254::1]:7013 */247	*addr = strchr(src, '[');248	p = strchr(src, ']');249 250	if (!p || !*addr) {251		test_print("Couldn't parse trace event [addr]:port %s", src);252		return -EINVAL;253	}254 255	*addr = *addr + 1;      /* '[' */256	*p++ = '\0';            /* ']' */257	if (*p != ':') {258		test_print("Couldn't parse trace event :port %s", p);259		return -EINVAL;260	}261	*p++ = '\0';            /* ':' */262	*port = p;263	return 0;264}265 266static int tracer_scan_address(int family, char *src,267			       union tcp_addr *dst, unsigned int *port)268{269	char *addr, *port_str;270	int ret;271 272	ret = tracer_ip_split(family, src, &addr, &port_str);273	if (ret)274		return ret;275 276	if (inet_pton(family, addr, dst) != 1) {277		test_print("Couldn't parse trace event addr %s", addr);278		return -EINVAL;279	}280	errno = 0;281	*port = (unsigned int)strtoul(port_str, NULL, 10);282	if (errno != 0) {283		test_print("Couldn't parse trace event port %s", port_str);284		return -errno;285	}286	return 0;287}288 289static int tracer_scan_event(const char *line, enum trace_events event,290			     struct trace_point *out)291{292	char *src = NULL, *dst = NULL, *family = NULL;293	char fin, syn, rst, psh, ack;294	int nr_matched, ret = 0;295	uint64_t netns_cookie;296 297	switch (event) {298	case TCP_HASH_BAD_HEADER:299	case TCP_HASH_MD5_REQUIRED:300	case TCP_HASH_MD5_UNEXPECTED:301	case TCP_HASH_MD5_MISMATCH:302	case TCP_HASH_AO_REQUIRED: {303		nr_matched = sscanf(line, "%*s net=%" PRIu64 " state%*s family=%ms src=%ms dest=%ms L3index=%d [%c%c%c%c%c]",304				    &netns_cookie, &family,305				    &src, &dst, &out->L3index,306				    &fin, &syn, &rst, &psh, &ack);307		if (nr_matched != 10)308			test_print("Couldn't parse trace event, matched = %d/10",309				   nr_matched);310		break;311	}312	case TCP_AO_HANDSHAKE_FAILURE:313	case TCP_AO_WRONG_MACLEN:314	case TCP_AO_MISMATCH:315	case TCP_AO_KEY_NOT_FOUND:316	case TCP_AO_RNEXT_REQUEST: {317		nr_matched = sscanf(line, "%*s net=%" PRIu64 " state%*s family=%ms src=%ms dest=%ms L3index=%d [%c%c%c%c%c] keyid=%u rnext=%u maclen=%u",318				    &netns_cookie, &family,319				    &src, &dst, &out->L3index,320				    &fin, &syn, &rst, &psh, &ack,321				    &out->keyid, &out->rnext, &out->maclen);322		if (nr_matched != 13)323			test_print("Couldn't parse trace event, matched = %d/13",324				   nr_matched);325		break;326	}327	case TCP_AO_SYNACK_NO_KEY: {328		nr_matched = sscanf(line, "%*s net=%" PRIu64 " state%*s family=%ms src=%ms dest=%ms keyid=%u rnext=%u",329				    &netns_cookie, &family,330				    &src, &dst, &out->keyid, &out->rnext);331		if (nr_matched != 6)332			test_print("Couldn't parse trace event, matched = %d/6",333				   nr_matched);334		break;335	}336	case TCP_AO_SND_SNE_UPDATE:337	case TCP_AO_RCV_SNE_UPDATE: {338		nr_matched = sscanf(line, "%*s net=%" PRIu64 " state%*s family=%ms src=%ms dest=%ms sne=%u",339				    &netns_cookie, &family,340				    &src, &dst, &out->sne);341		if (nr_matched != 5)342			test_print("Couldn't parse trace event, matched = %d/5",343				   nr_matched);344		break;345	}346	default:347		return -1;348	}349 350	if (family) {351		if (!strcmp(family, "AF_INET")) {352			out->family = AF_INET;353		} else if (!strcmp(family, "AF_INET6")) {354			out->family = AF_INET6;355		} else {356			test_print("Couldn't parse trace event family %s", family);357			ret = -EINVAL;358			goto out_free;359		}360	}361 362	if (event_has_flags(event)) {363		out->fin = (fin == 'F');364		out->syn = (syn == 'S');365		out->rst = (rst == 'R');366		out->psh = (psh == 'P');367		out->ack = (ack == '.');368 369		if ((fin != 'F' && fin != ' ') ||370		    (syn != 'S' && syn != ' ') ||371		    (rst != 'R' && rst != ' ') ||372		    (psh != 'P' && psh != ' ') ||373		    (ack != '.' && ack != ' ')) {374			test_print("Couldn't parse trace event flags %c%c%c%c%c",375				   fin, syn, rst, psh, ack);376			ret = -EINVAL;377			goto out_free;378		}379	}380 381	if (src && tracer_scan_address(out->family, src, &out->src, &out->src_port)) {382		ret = -EINVAL;383		goto out_free;384	}385 386	if (dst && tracer_scan_address(out->family, dst, &out->dst, &out->dst_port)) {387		ret = -EINVAL;388		goto out_free;389	}390 391	if (netns_cookie != ns_cookie1 && netns_cookie != ns_cookie2) {392		test_print("Net namespace filter for trace event didn't work: %" PRIu64 " != %" PRIu64 " OR %" PRIu64,393			   netns_cookie, ns_cookie1, ns_cookie2);394		ret = -EINVAL;395	}396 397out_free:398	free(src);399	free(dst);400	free(family);401	return ret;402}403 404static enum ftracer_op aolib_tracer_process_event(const char *line)405{406	int event_type = check_event_type(line);407	struct trace_point tmp = {};408 409	if (event_type < 0)410		return FTRACER_LINE_PRESERVE;411 412	if (tracer_scan_event(line, event_type, &tmp))413		return FTRACER_LINE_PRESERVE;414 415	return lookup_expected_event(event_type, &tmp) ?416		FTRACER_LINE_DISCARD : FTRACER_LINE_PRESERVE;417}418 419static void dump_trace_event(struct expected_trace_point *e)420{421	char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN];422 423	if (!inet_ntop(e->family, &e->src, src, INET6_ADDRSTRLEN))424		test_error("inet_ntop()");425	if (!inet_ntop(e->family, &e->dst, dst, INET6_ADDRSTRLEN))426		test_error("inet_ntop()");427	test_print("trace event filter %s [%s:%d => %s:%d, L3index %d, flags: %s%s%s%s%s, keyid: %d, rnext: %d, maclen: %d, sne: %d] = %zu",428		   trace_event_names[e->type],429		   src, e->src_port, dst, e->dst_port, e->L3index,430		   (e->fin > 0) ? "F" : (e->fin == 0) ? "!F" : "",431		   (e->syn > 0) ? "S" : (e->syn == 0) ? "!S" : "",432		   (e->rst > 0) ? "R" : (e->rst == 0) ? "!R" : "",433		   (e->psh > 0) ? "P" : (e->psh == 0) ? "!P" : "",434		   (e->ack > 0) ? "." : (e->ack == 0) ? "!." : "",435		   e->keyid, e->rnext, e->maclen, e->sne, e->matched);436}437 438static void print_match_stats(bool unexpected_events)439{440	size_t matches_per_type[__MAX_TRACE_EVENTS] = {};441	bool expected_but_none = false;442	size_t i, total_matched = 0;443	char *stat_line = NULL;444 445	for (i = 0; i < exp_tps_nr; i++) {446		struct expected_trace_point *e = &exp_tps[i];447 448		total_matched += e->matched;449		matches_per_type[e->type] += e->matched;450		if (!e->matched)451			expected_but_none = true;452	}453	for (i = 0; i < __MAX_TRACE_EVENTS; i++) {454		if (!matches_per_type[i])455			continue;456		stat_line = test_sprintf("%s%s[%zu] ", stat_line ?: "",457					 trace_event_names[i],458					 matches_per_type[i]);459		if (!stat_line)460			test_error("test_sprintf()");461	}462 463	if (unexpected_events || expected_but_none) {464		for (i = 0; i < exp_tps_nr; i++)465			dump_trace_event(&exp_tps[i]);466	}467 468	if (unexpected_events)469		return;470 471	if (expected_but_none)472		test_fail("Some trace events were expected, but didn't occur");473	else if (total_matched)474		test_ok("Trace events matched expectations: %zu %s",475			total_matched, stat_line);476	else477		test_ok("No unexpected trace events during the test run");478}479 480#define dump_events(fmt, ...)                           \481	__test_print(__test_msg, fmt, ##__VA_ARGS__)482static void check_free_events(struct test_ftracer *tracer)483{484	const char **lines;485	size_t nr;486 487	if (!kernel_config_has(KCONFIG_FTRACE)) {488		test_skip("kernel config doesn't have ftrace - no checks");489		return;490	}491 492	nr = tracer_get_savedlines_nr(tracer);493	lines = tracer_get_savedlines(tracer);494	print_match_stats(!!nr);495	if (!nr)496		return;497 498	errno = 0;499	test_xfail("Trace events [%zu] were not expected:", nr);500	while (nr)501		dump_events("\t%s", lines[--nr]);502}503 504static int setup_tcp_trace_events(struct test_ftracer *tracer)505{506	char *filter;507	size_t i;508	int ret;509 510	filter = test_sprintf("net_cookie == %zu || net_cookie == %zu",511			      ns_cookie1, ns_cookie2);512	if (!filter)513		return -ENOMEM;514 515	for (i = 0; i < __MAX_TRACE_EVENTS; i++) {516		char *event_name = test_sprintf("tcp/%s", trace_event_names[i]);517 518		if (!event_name) {519			ret = -ENOMEM;520			break;521		}522		ret = setup_trace_event(tracer, event_name, filter);523		free(event_name);524		if (ret)525			break;526	}527 528	free(filter);529	return ret;530}531 532static void aolib_tracer_destroy(struct test_ftracer *tracer)533{534	check_free_events(tracer);535	free_expected_events();536}537 538static bool aolib_tracer_expecting_more(void)539{540	size_t i;541 542	for (i = 0; i < exp_tps_nr; i++)543		if (!exp_tps[i].matched)544			return true;545	return false;546}547 548int setup_aolib_ftracer(void)549{550	struct test_ftracer *f;551 552	f = create_ftracer("aolib", aolib_tracer_process_event,553			   aolib_tracer_destroy, aolib_tracer_expecting_more,554			   DEFAULT_FTRACE_BUFFER_KB, DEFAULT_TRACER_LINES_ARR);555	if (!f)556		return -1;557 558	return setup_tcp_trace_events(f);559}560