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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.