File Index
Files and directories
Sign in
FILE

ftrace-tcp.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/net/tcp_ao/lib/ftrace-tcp.c C
  1// SPDX-License-Identifier: GPL-2.0
  2#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		else
 94			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		else
154			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 ? "F" : "", e->syn ? "S" : "", e->rst ? "R" : "",
431		   e->psh ? "P" : "", e->ack ? "." : "",
432		   e->keyid, e->rnext, e->maclen, e->sne, e->matched);
433}
434
435static void print_match_stats(bool unexpected_events)
436{
437	size_t matches_per_type[__MAX_TRACE_EVENTS] = {};
438	bool expected_but_none = false;
439	size_t i, total_matched = 0;
440	char *stat_line = NULL;
441
442	for (i = 0; i < exp_tps_nr; i++) {
443		struct expected_trace_point *e = &exp_tps[i];
444
445		total_matched += e->matched;
446		matches_per_type[e->type] += e->matched;
447		if (!e->matched)
448			expected_but_none = true;
449	}
450	for (i = 0; i < __MAX_TRACE_EVENTS; i++) {
451		if (!matches_per_type[i])
452			continue;
453		stat_line = test_sprintf("%s%s[%zu] ", stat_line ?: "",
454					 trace_event_names[i],
455					 matches_per_type[i]);
456		if (!stat_line)
457			test_error("test_sprintf()");
458	}
459
460	if (unexpected_events || expected_but_none) {
461		for (i = 0; i < exp_tps_nr; i++)
462			dump_trace_event(&exp_tps[i]);
463	}
464
465	if (unexpected_events)
466		return;
467
468	if (expected_but_none)
469		test_fail("Some trace events were expected, but didn't occur");
470	else if (total_matched)
471		test_ok("Trace events matched expectations: %zu %s",
472			total_matched, stat_line);
473	else
474		test_ok("No unexpected trace events during the test run");
475}
476
477#define dump_events(fmt, ...)                           \
478	__test_print(__test_msg, fmt, ##__VA_ARGS__)
479static void check_free_events(struct test_ftracer *tracer)
480{
481	const char **lines;
482	size_t nr;
483
484	if (!kernel_config_has(KCONFIG_FTRACE)) {
485		test_skip("kernel config doesn't have ftrace - no checks");
486		return;
487	}
488
489	nr = tracer_get_savedlines_nr(tracer);
490	lines = tracer_get_savedlines(tracer);
491	print_match_stats(!!nr);
492	if (!nr)
493		return;
494
495	errno = 0;
496	test_xfail("Trace events [%zu] were not expected:", nr);
497	while (nr)
498		dump_events("\t%s", lines[--nr]);
499}
500
501static int setup_tcp_trace_events(struct test_ftracer *tracer)
502{
503	char *filter;
504	size_t i;
505	int ret;
506
507	filter = test_sprintf("net_cookie == %zu || net_cookie == %zu",
508			      ns_cookie1, ns_cookie2);
509	if (!filter)
510		return -ENOMEM;
511
512	for (i = 0; i < __MAX_TRACE_EVENTS; i++) {
513		char *event_name = test_sprintf("tcp/%s", trace_event_names[i]);
514
515		if (!event_name) {
516			ret = -ENOMEM;
517			break;
518		}
519		ret = setup_trace_event(tracer, event_name, filter);
520		free(event_name);
521		if (ret)
522			break;
523	}
524
525	free(filter);
526	return ret;
527}
528
529static void aolib_tracer_destroy(struct test_ftracer *tracer)
530{
531	check_free_events(tracer);
532	free_expected_events();
533}
534
535static bool aolib_tracer_expecting_more(void)
536{
537	size_t i;
538
539	for (i = 0; i < exp_tps_nr; i++)
540		if (!exp_tps[i].matched)
541			return true;
542	return false;
543}
544
545int setup_aolib_ftracer(void)
546{
547	struct test_ftracer *f;
548
549	f = create_ftracer("aolib", aolib_tracer_process_event,
550			   aolib_tracer_destroy, aolib_tracer_expecting_more,
551			   DEFAULT_FTRACE_BUFFER_KB, DEFAULT_TRACER_LINES_ARR);
552	if (!f)
553		return -1;
554
555	return setup_tcp_trace_events(f);
556}
~/ Read-only file preview.