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