xref: /linux/tools/testing/selftests/net/tcp_ao/lib/ftrace-tcp.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <inttypes.h>
3 #include <pthread.h>
4 #include "aolib.h"
5 
6 static 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 
26 struct 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 
52 static struct expected_trace_point *exp_tps;
53 static size_t exp_tps_nr;
54 static size_t exp_tps_size;
55 static pthread_mutex_t exp_tps_mutex = PTHREAD_MUTEX_INITIALIZER;
56 
57 int __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++;
105 out:
106 	pthread_mutex_unlock(&exp_tps_mutex);
107 	return ret;
108 }
109 
110 static 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 
118 struct 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 
138 static 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 
193 static 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 
208 static 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 
227 static 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 
266 static 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 
289 static 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 
397 out_free:
398 	free(src);
399 	free(dst);
400 	free(family);
401 	return ret;
402 }
403 
404 static 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 
419 static 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 
438 static 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 	else
477 		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__)
482 static 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 
504 static 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 
532 static void aolib_tracer_destroy(struct test_ftracer *tracer)
533 {
534 	check_free_events(tracer);
535 	free_expected_events();
536 }
537 
538 static 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 
548 int 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