github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/profile/tcprtt/tracer/tracer_test.go (about) 1 // Copyright 2024 The Inspektor Gadget authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build linux 16 // +build linux 17 18 package tracer 19 20 import ( 21 "context" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "net" 26 "strconv" 27 "sync" 28 "testing" 29 "time" 30 31 log "github.com/sirupsen/logrus" 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 35 utilstest "github.com/inspektor-gadget/inspektor-gadget/internal/test" 36 gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context" 37 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/profile/tcprtt/types" 38 "github.com/inspektor-gadget/inspektor-gadget/pkg/histogram" 39 "github.com/inspektor-gadget/inspektor-gadget/pkg/params" 40 ) 41 42 func createTracer() *Tracer { 43 return &Tracer{ 44 config: &Config{}, 45 } 46 } 47 48 func TestGadgetInstantiate(t *testing.T) { 49 t.Parallel() 50 51 gadget := &GadgetDesc{} 52 tracer, err := gadget.NewInstance() 53 require.Nil(t, err, "unexpected error creating instance") 54 require.NotNil(t, tracer, "expected tracer") 55 } 56 57 func TestTracerInstallation(t *testing.T) { 58 t.Parallel() 59 60 utilstest.RequireRoot(t) 61 62 tracer := createTracer() 63 err := tracer.install() 64 require.Nil(t, err, "unexpected error installing tracer") 65 66 tracer.close() 67 } 68 69 func TestTracerCloseIdempotent(t *testing.T) { 70 t.Parallel() 71 72 utilstest.RequireRoot(t) 73 74 tracer := createTracer() 75 err := tracer.install() 76 require.Nil(t, err, "unexpected error installing tracer") 77 78 // Check that a double stop doesn't cause issues 79 tracer.close() 80 tracer.close() 81 } 82 83 func TestParseParams(t *testing.T) { 84 t.Parallel() 85 86 type expected struct { 87 err bool 88 config *Config 89 } 90 91 gadget := &GadgetDesc{} 92 93 testTable := []struct { 94 description string 95 getGadgetParams func() *params.Params 96 expected expected 97 }{ 98 { 99 description: "milliseconds", 100 getGadgetParams: func() *params.Params { 101 params := gadget.ParamDescs().ToParams() 102 params.Get(ParamMilliseconds).Set("true") 103 return params 104 }, 105 expected: expected{ 106 config: &Config{ 107 useMilliseconds: true, 108 }, 109 }, 110 }, 111 { 112 description: "by_local_address", 113 getGadgetParams: func() *params.Params { 114 params := gadget.ParamDescs().ToParams() 115 params.Get(ParamByLocalAddress).Set("true") 116 return params 117 }, 118 expected: expected{ 119 config: &Config{ 120 localAddrHist: true, 121 }, 122 }, 123 }, 124 { 125 description: "by_remote_address", 126 getGadgetParams: func() *params.Params { 127 params := gadget.ParamDescs().ToParams() 128 params.Get(ParamByRemoteAddress).Set("true") 129 return params 130 }, 131 expected: expected{ 132 config: &Config{ 133 remoteAddrHist: true, 134 }, 135 }, 136 }, 137 { 138 description: "by_local_and_remote_address_err", 139 getGadgetParams: func() *params.Params { 140 params := gadget.ParamDescs().ToParams() 141 params.Get(ParamByRemoteAddress).Set("true") 142 params.Get(ParamByLocalAddress).Set("true") 143 return params 144 }, 145 expected: expected{ 146 err: true, 147 }, 148 }, 149 { 150 description: "filter_by_local_port", 151 getGadgetParams: func() *params.Params { 152 params := gadget.ParamDescs().ToParams() 153 params.Get(ParamFilterLocalPort).Set("42") 154 return params 155 }, 156 expected: expected{ 157 config: &Config{ 158 filterLocalPort: uint16(42), 159 }, 160 }, 161 }, 162 { 163 description: "filter_by_remote_port", 164 getGadgetParams: func() *params.Params { 165 params := gadget.ParamDescs().ToParams() 166 params.Get(ParamFilterRemotePort).Set("42") 167 return params 168 }, 169 expected: expected{ 170 config: &Config{ 171 filterRemotePort: uint16(42), 172 }, 173 }, 174 }, 175 { 176 description: "filter_by_local_address", 177 getGadgetParams: func() *params.Params { 178 params := gadget.ParamDescs().ToParams() 179 params.Get(ParamFilterLocalAddress).Set("1.2.3.4") 180 return params 181 }, 182 expected: expected{ 183 config: &Config{ 184 filterLocalAddress: 0x04030201, 185 }, 186 }, 187 }, 188 { 189 description: "filter_by_remote_address", 190 getGadgetParams: func() *params.Params { 191 params := gadget.ParamDescs().ToParams() 192 params.Get(ParamFilterRemoteAddress).Set("192.168.0.1") 193 return params 194 }, 195 expected: expected{ 196 config: &Config{ 197 filterRemoteAddress: 0x0100A8C0, 198 }, 199 }, 200 }, 201 { 202 description: "filter_by_local_v6_address", 203 getGadgetParams: func() *params.Params { 204 params := gadget.ParamDescs().ToParams() 205 params.Get(ParamFilterLocalAddressV6).Set("::ffff:c0a8:0001") 206 return params 207 }, 208 expected: expected{ 209 config: &Config{ 210 filterLocalAddressV6: [16]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc0, 0xa8, 0x0, 0x1}, 211 }, 212 }, 213 }, 214 { 215 description: "filter_by_remote_v6_address", 216 getGadgetParams: func() *params.Params { 217 params := gadget.ParamDescs().ToParams() 218 params.Get(ParamFilterRemoteAddressV6).Set("::ffff:c0a8:0001") 219 return params 220 }, 221 expected: expected{ 222 config: &Config{ 223 filterRemoteAddressV6: [16]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc0, 0xa8, 0x0, 0x1}, 224 }, 225 }, 226 }, 227 { 228 description: "filter_by_remote_and_filter_by_local_address", 229 getGadgetParams: func() *params.Params { 230 params := gadget.ParamDescs().ToParams() 231 params.Get(ParamFilterLocalAddress).Set("1.2.3.4") 232 params.Get(ParamFilterRemoteAddress).Set("192.168.0.1") 233 return params 234 }, 235 expected: expected{ 236 config: &Config{ 237 filterLocalAddress: 0x04030201, 238 filterRemoteAddress: 0x0100A8C0, 239 }, 240 }, 241 }, 242 { 243 description: "filter_by_remote_v4_and_filter_by_remote_v6_address", 244 getGadgetParams: func() *params.Params { 245 params := gadget.ParamDescs().ToParams() 246 params.Get(ParamFilterRemoteAddress).Set("192.168.0.1") 247 params.Get(ParamFilterRemoteAddressV6).Set("::ffff:c0a8:0001") 248 return params 249 }, 250 expected: expected{ 251 err: true, 252 }, 253 }, 254 { 255 description: "filter_by_local_v4_and_filter_by_local_v6_address", 256 getGadgetParams: func() *params.Params { 257 params := gadget.ParamDescs().ToParams() 258 params.Get(ParamFilterLocalAddress).Set("192.168.0.1") 259 params.Get(ParamFilterLocalAddressV6).Set("::ffff:c0a8:0001") 260 return params 261 }, 262 expected: expected{ 263 err: true, 264 }, 265 }, 266 { 267 description: "filter_by_remote_and_filter_by_local_address_sorted_by_local", 268 getGadgetParams: func() *params.Params { 269 params := gadget.ParamDescs().ToParams() 270 params.Get(ParamFilterLocalAddress).Set("1.2.3.4") 271 params.Get(ParamFilterRemoteAddress).Set("192.168.0.1") 272 params.Get(ParamByLocalAddress).Set("true") 273 return params 274 }, 275 expected: expected{ 276 config: &Config{ 277 filterLocalAddress: 0x04030201, 278 filterRemoteAddress: 0x0100A8C0, 279 localAddrHist: true, 280 }, 281 }, 282 }, 283 { 284 description: "filter_by_remote_and_filter_by_local_address_sorted_by_remote", 285 getGadgetParams: func() *params.Params { 286 params := gadget.ParamDescs().ToParams() 287 params.Get(ParamFilterLocalAddress).Set("1.2.3.4") 288 params.Get(ParamFilterRemoteAddress).Set("192.168.0.1") 289 params.Get(ParamByRemoteAddress).Set("true") 290 return params 291 }, 292 expected: expected{ 293 config: &Config{ 294 filterLocalAddress: 0x04030201, 295 filterRemoteAddress: 0x0100A8C0, 296 remoteAddrHist: true, 297 }, 298 }, 299 }, 300 { 301 description: "filter_by_remote_v6_and_filter_by_local_v6_address_sorted_by_local", 302 getGadgetParams: func() *params.Params { 303 params := gadget.ParamDescs().ToParams() 304 params.Get(ParamFilterLocalAddressV6).Set("::ffff:c0a8:0001") 305 params.Get(ParamFilterRemoteAddressV6).Set("::ffff:c0a8:0001") 306 params.Get(ParamByLocalAddress).Set("true") 307 return params 308 }, 309 expected: expected{ 310 config: &Config{ 311 filterLocalAddressV6: [16]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc0, 0xa8, 0x0, 0x1}, 312 filterRemoteAddressV6: [16]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc0, 0xa8, 0x0, 0x1}, 313 localAddrHist: true, 314 }, 315 }, 316 }, 317 { 318 description: "filter_by_remote_v6_and_filter_by_local_v6_address_sorted_by_remote", 319 getGadgetParams: func() *params.Params { 320 params := gadget.ParamDescs().ToParams() 321 params.Get(ParamFilterLocalAddressV6).Set("::ffff:c0a8:0001") 322 params.Get(ParamFilterRemoteAddressV6).Set("::ffff:c0a8:0001") 323 params.Get(ParamByRemoteAddress).Set("true") 324 return params 325 }, 326 expected: expected{ 327 config: &Config{ 328 filterLocalAddressV6: [16]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc0, 0xa8, 0x0, 0x1}, 329 filterRemoteAddressV6: [16]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc0, 0xa8, 0x0, 0x1}, 330 remoteAddrHist: true, 331 }, 332 }, 333 }, 334 } 335 336 for _, test := range testTable { 337 test := test 338 t.Run(test.description, func(t *testing.T) { 339 t.Parallel() 340 341 tracer := createTracer() 342 tracer.logger = log.StandardLogger() 343 err := tracer.parseParams(test.getGadgetParams()) 344 345 if test.expected.err { 346 require.NotNil(t, err, "expected error parsing params") 347 return 348 } 349 350 require.Nil(t, err, "unexpected error parsing params") 351 require.Equal(t, tracer.config, test.expected.config) 352 }) 353 } 354 } 355 356 type expectedResult struct { 357 err bool 358 exactHistograms int 359 minHistograms int 360 useMilliseconds bool // true if the unit is milliseconds, false if microseconds (gadget default) 361 byRemoteAddr string 362 byLocalAddr string 363 localPort uint16 364 remotePort uint16 365 } 366 367 type testDefinition struct { 368 getGadgetParams func() *params.Params 369 timeout time.Duration 370 expectedResult expectedResult 371 } 372 373 func testRunWithResult(t *testing.T, serverPort int, serverIP, clientIP net.IP, ipVersion int) { 374 startTCPServer(t, serverIP, serverPort, ipVersion) 375 376 runnerConfig := &utilstest.RunnerConfig{ 377 // This gadget works at host level not at network namespace 378 // level, so we don't need to generate the events in an isolated 379 // network namespace. 380 HostNetwork: true, 381 } 382 383 var paramFilterLocalAddress, paramFilterRemoteAddress string 384 switch ipVersion { 385 case 4: 386 paramFilterLocalAddress = ParamFilterLocalAddress 387 paramFilterRemoteAddress = ParamFilterRemoteAddress 388 case 6: 389 paramFilterLocalAddress = ParamFilterLocalAddressV6 390 paramFilterRemoteAddress = ParamFilterRemoteAddressV6 391 default: 392 t.Fatalf("IP version %d is not valid, expected 4 or 6", ipVersion) 393 } 394 395 gadget := &GadgetDesc{} 396 defs := map[string]testDefinition{ 397 "with_default_params": { 398 getGadgetParams: func() *params.Params { 399 return gadget.ParamDescs().ToParams() 400 }, 401 expectedResult: expectedResult{ 402 minHistograms: 1, 403 }, 404 }, 405 "with_default_params_and_timeout": { 406 timeout: 5 * time.Second, 407 getGadgetParams: func() *params.Params { 408 return gadget.ParamDescs().ToParams() 409 }, 410 expectedResult: expectedResult{ 411 minHistograms: 1, 412 }, 413 }, 414 "with_milliseconds": { 415 getGadgetParams: func() *params.Params { 416 params := gadget.ParamDescs().ToParams() 417 params.Get(ParamMilliseconds).Set("true") 418 return params 419 }, 420 expectedResult: expectedResult{ 421 minHistograms: 1, 422 useMilliseconds: true, 423 }, 424 }, 425 "by_local_addr": { 426 getGadgetParams: func() *params.Params { 427 params := gadget.ParamDescs().ToParams() 428 params.Get(ParamByLocalAddress).Set("true") 429 return params 430 }, 431 expectedResult: expectedResult{ 432 minHistograms: 1, 433 byLocalAddr: clientIP.String(), 434 }, 435 }, 436 "by_remote_addr": { 437 getGadgetParams: func() *params.Params { 438 params := gadget.ParamDescs().ToParams() 439 params.Get(ParamByRemoteAddress).Set("true") 440 return params 441 }, 442 expectedResult: expectedResult{ 443 minHistograms: 1, 444 byRemoteAddr: serverIP.String(), 445 }, 446 }, 447 "filter_by_laddr": { 448 getGadgetParams: func() *params.Params { 449 params := gadget.ParamDescs().ToParams() 450 params.Get(paramFilterLocalAddress).Set(clientIP.String()) 451 return params 452 }, 453 expectedResult: expectedResult{ 454 exactHistograms: 1, 455 }, 456 }, 457 "filter_by_non_existing_laddr": { 458 getGadgetParams: func() *params.Params { 459 params := gadget.ParamDescs().ToParams() 460 params.Get(paramFilterLocalAddress).Set("0.1.0.1") 461 return params 462 }, 463 expectedResult: expectedResult{ 464 err: true, 465 }, 466 }, 467 "filter_by_raddr": { 468 getGadgetParams: func() *params.Params { 469 params := gadget.ParamDescs().ToParams() 470 params.Get(paramFilterRemoteAddress).Set(serverIP.String()) 471 return params 472 }, 473 expectedResult: expectedResult{ 474 exactHistograms: 1, 475 }, 476 }, 477 "filter_by_non_existing_raddr": { 478 getGadgetParams: func() *params.Params { 479 params := gadget.ParamDescs().ToParams() 480 params.Get(ParamFilterRemoteAddress).Set("0.1.0.1") 481 return params 482 }, 483 expectedResult: expectedResult{ 484 err: true, 485 }, 486 }, 487 "filter_by_laddr_and_raddr": { 488 getGadgetParams: func() *params.Params { 489 params := gadget.ParamDescs().ToParams() 490 params.Get(paramFilterLocalAddress).Set(clientIP.String()) 491 params.Get(paramFilterRemoteAddress).Set(serverIP.String()) 492 return params 493 }, 494 expectedResult: expectedResult{ 495 exactHistograms: 1, 496 }, 497 }, 498 "filter_by_laddr_and_raddr_msec": { 499 getGadgetParams: func() *params.Params { 500 params := gadget.ParamDescs().ToParams() 501 params.Get(paramFilterLocalAddress).Set(clientIP.String()) 502 params.Get(paramFilterRemoteAddress).Set(serverIP.String()) 503 params.Get(ParamMilliseconds).Set("true") 504 return params 505 }, 506 expectedResult: expectedResult{ 507 exactHistograms: 1, 508 useMilliseconds: true, 509 }, 510 }, 511 "filter_by_invalid_laddr_and_raddr": { 512 getGadgetParams: func() *params.Params { 513 params := gadget.ParamDescs().ToParams() 514 params.Get(ParamFilterLocalAddress).Set("1.0.0.1") 515 params.Get(paramFilterRemoteAddress).Set(serverIP.String()) 516 return params 517 }, 518 expectedResult: expectedResult{ 519 err: true, 520 }, 521 }, 522 "filter_by_laddr_and_invalid_raddr": { 523 getGadgetParams: func() *params.Params { 524 params := gadget.ParamDescs().ToParams() 525 params.Get(paramFilterLocalAddress).Set(clientIP.String()) 526 params.Get(paramFilterRemoteAddress).Set("1.0.0.1") 527 return params 528 }, 529 expectedResult: expectedResult{ 530 err: true, 531 }, 532 }, 533 "filter_by_invalid_laddr_and_invalid_raddr": { 534 getGadgetParams: func() *params.Params { 535 params := gadget.ParamDescs().ToParams() 536 params.Get(ParamFilterLocalAddress).Set("1.0.0.1") 537 params.Get(paramFilterRemoteAddress).Set("1.0.0.1") 538 return params 539 }, 540 expectedResult: expectedResult{ 541 err: true, 542 }, 543 }, 544 "mix_by_local_and_filter_by_raddr": { 545 getGadgetParams: func() *params.Params { 546 params := gadget.ParamDescs().ToParams() 547 params.Get(ParamByLocalAddress).Set("true") 548 params.Get(paramFilterRemoteAddress).Set(serverIP.String()) 549 return params 550 }, 551 expectedResult: expectedResult{ 552 exactHistograms: 1, 553 byLocalAddr: clientIP.String(), 554 }, 555 }, 556 "mix_by_local_and_filter_by_laddr": { 557 getGadgetParams: func() *params.Params { 558 params := gadget.ParamDescs().ToParams() 559 params.Get(ParamByLocalAddress).Set("true") 560 params.Get(paramFilterLocalAddress).Set(clientIP.String()) 561 return params 562 }, 563 expectedResult: expectedResult{ 564 exactHistograms: 1, 565 byLocalAddr: clientIP.String(), 566 }, 567 }, 568 "mix_by_remote_and_filter_by_laddr": { 569 getGadgetParams: func() *params.Params { 570 params := gadget.ParamDescs().ToParams() 571 params.Get(ParamByRemoteAddress).Set("true") 572 params.Get(paramFilterLocalAddress).Set(clientIP.String()) 573 return params 574 }, 575 expectedResult: expectedResult{ 576 exactHistograms: 1, 577 byRemoteAddr: serverIP.String(), 578 }, 579 }, 580 "mix_by_remote_and_filter_by_raddr": { 581 getGadgetParams: func() *params.Params { 582 params := gadget.ParamDescs().ToParams() 583 params.Get(ParamByRemoteAddress).Set("true") 584 params.Get(paramFilterRemoteAddress).Set(serverIP.String()) 585 return params 586 }, 587 expectedResult: expectedResult{ 588 exactHistograms: 1, 589 byRemoteAddr: serverIP.String(), 590 }, 591 }, 592 "filter_by_lport": { 593 getGadgetParams: func() *params.Params { 594 params := gadget.ParamDescs().ToParams() 595 params.Get(ParamFilterLocalPort).Set(strconv.Itoa(serverPort)) 596 return params 597 }, 598 expectedResult: expectedResult{ 599 exactHistograms: 1, 600 localPort: uint16(serverPort), 601 }, 602 }, 603 "mix_by_local_addr_and_filter_by_lport": { 604 getGadgetParams: func() *params.Params { 605 params := gadget.ParamDescs().ToParams() 606 params.Get(ParamByLocalAddress).Set("true") 607 params.Get(ParamFilterLocalPort).Set(strconv.Itoa(serverPort)) 608 return params 609 }, 610 expectedResult: expectedResult{ 611 minHistograms: 1, 612 byLocalAddr: serverIP.String(), 613 localPort: uint16(serverPort), 614 }, 615 }, 616 "mix_by_remote_addr_and_filter_by_lport": { 617 getGadgetParams: func() *params.Params { 618 params := gadget.ParamDescs().ToParams() 619 params.Get(ParamByRemoteAddress).Set("true") 620 params.Get(ParamFilterLocalPort).Set(strconv.Itoa(serverPort)) 621 return params 622 }, 623 expectedResult: expectedResult{ 624 minHistograms: 1, 625 byRemoteAddr: clientIP.String(), 626 localPort: uint16(serverPort), 627 }, 628 }, 629 "filter_by_rport": { 630 getGadgetParams: func() *params.Params { 631 params := gadget.ParamDescs().ToParams() 632 params.Get(ParamFilterRemotePort).Set(strconv.Itoa(serverPort)) 633 return params 634 }, 635 expectedResult: expectedResult{ 636 exactHistograms: 1, 637 remotePort: uint16(serverPort), 638 }, 639 }, 640 "mix_by_local_addr_and_filter_by_rport": { 641 getGadgetParams: func() *params.Params { 642 params := gadget.ParamDescs().ToParams() 643 params.Get(ParamByLocalAddress).Set("true") 644 params.Get(ParamFilterRemotePort).Set(strconv.Itoa(serverPort)) 645 return params 646 }, 647 expectedResult: expectedResult{ 648 minHistograms: 1, 649 byLocalAddr: clientIP.String(), 650 remotePort: uint16(serverPort), 651 }, 652 }, 653 "mix_by_remote_addr_and_filter_by_rport": { 654 getGadgetParams: func() *params.Params { 655 params := gadget.ParamDescs().ToParams() 656 params.Get(ParamByRemoteAddress).Set("true") 657 params.Get(ParamFilterRemotePort).Set(strconv.Itoa(serverPort)) 658 return params 659 }, 660 expectedResult: expectedResult{ 661 minHistograms: 1, 662 byRemoteAddr: serverIP.String(), 663 remotePort: uint16(serverPort), 664 }, 665 }, 666 "filter_by_laddr_and_rport": { 667 getGadgetParams: func() *params.Params { 668 params := gadget.ParamDescs().ToParams() 669 params.Get(paramFilterLocalAddress).Set(clientIP.String()) 670 params.Get(ParamFilterRemotePort).Set(strconv.Itoa(serverPort)) 671 return params 672 }, 673 expectedResult: expectedResult{ 674 exactHistograms: 1, 675 remotePort: uint16(serverPort), 676 }, 677 }, 678 "filter_by_raddr_and_lport": { 679 getGadgetParams: func() *params.Params { 680 params := gadget.ParamDescs().ToParams() 681 params.Get(paramFilterRemoteAddress).Set(clientIP.String()) 682 params.Get(ParamFilterLocalPort).Set(strconv.Itoa(serverPort)) 683 return params 684 }, 685 expectedResult: expectedResult{ 686 exactHistograms: 1, 687 localPort: uint16(serverPort), 688 }, 689 }, 690 // TODO: Test mix cases with clients and servers with different IPs 691 } 692 693 for name, test := range defs { 694 test := test 695 696 t.Run(name, func(t *testing.T) { 697 t.Parallel() 698 699 // Create context 700 gadgetCtx := newGadgetCtx(test.getGadgetParams(), test.timeout) 701 defer gadgetCtx.Cancel() 702 703 // Run the tracer in a goroutine 704 tracer := createTracer() 705 type output struct { 706 result []byte 707 err error 708 } 709 out := make(chan output) 710 go func() { 711 r, err := tracer.RunWithResult(gadgetCtx) 712 out <- output{r, err} 713 }() 714 715 // Wait for the tracer to be ready 716 time.Sleep(2 * time.Second) 717 718 // Generate the events 719 runner := utilstest.NewRunnerWithTest(t, runnerConfig) 720 utilstest.RunWithRunner(t, runner, func() error { 721 connectTCPClient(t, clientIP, serverIP, serverPort, ipVersion) 722 return nil 723 }) 724 725 // If needed, stop the tracer. Otherwise, simply wait for the result 726 if test.timeout == 0 { 727 // Wait for the tracer to capture the events 728 time.Sleep(2 * time.Second) 729 730 // Stop the tracer 731 gadgetCtx.Cancel() 732 } 733 734 // Wait for the tracer to finish and produce a result 735 ret := <-out 736 737 // Validate errors 738 if test.expectedResult.err { 739 require.NotNil(t, ret.err, "expected error running tracer") 740 require.Nil(t, ret.result, "available data when error occurred") 741 return 742 } 743 assert.Nil(t, ret.err, "not expected error running tracer") 744 745 // Unmarshal the result 746 var result types.Report 747 err := json.Unmarshal(ret.result, &result) 748 require.Nil(t, err, "unmarshalling report") 749 lHis := len(result.Histograms) 750 751 // Dump the result 752 for i, l := range result.Histograms { 753 t.Logf("Result [%d/%d]:", i+1, lHis) 754 t.Logf("AddrType %s - Addr %s - LocalPort %d - RemotePort %d - Avg %f", l.AddressType, l.Address, l.LocalPort, l.RemotePort, l.Average) 755 t.Logf("Histogram: %s", l.Histogram) 756 } 757 758 // Validate the number of histograms 759 if test.expectedResult.exactHistograms != 0 { 760 require.Equal(t, test.expectedResult.exactHistograms, lHis, 761 "invalid number of histograms") 762 } else { 763 // When we don't filter by address, we can't know how many 764 // histograms will be generated. This is because the number of 765 // histograms depends on the number of connections that are 766 // established in the host during the test. Therefore, we can 767 // only check that the number of histograms is greater or equal 768 // than the minimum number of histograms we expect. 769 require.GreaterOrEqual(t, lHis, test.expectedResult.minHistograms, 770 "wrong number of histograms") 771 } 772 773 lPortFound := false 774 rPortFound := false 775 lAddrFound := false 776 rAddrFound := false 777 for _, h := range result.Histograms { 778 // Validate unit 779 var expectedUnit histogram.Unit 780 if test.expectedResult.useMilliseconds { 781 expectedUnit = histogram.UnitMilliseconds 782 } else { 783 expectedUnit = histogram.UnitMicroseconds 784 } 785 require.Equal(t, expectedUnit, h.Unit, "wrong unit for histogram") 786 787 // Validate histogram contains at least one interval 788 require.Greater(t, len(h.Intervals), int(0), 789 "expecting at least one interval") 790 791 // Validate histogram computed the average value 792 // TODO: Verify this also when using milliseconds once the BPF 793 // program will be able to report the total latencies between 0 794 // and 1. Otherwise, the test will fail because the average 795 // latency will always be 0 milliseconds, so there is no way to 796 // verify that it was computed correctly. 797 if !test.expectedResult.useMilliseconds { 798 require.Greater(t, h.Average, float64(0)) 799 } 800 801 // Validate histogram contains the expected port information 802 if test.expectedResult.localPort != 0 && h.LocalPort == test.expectedResult.localPort { 803 lPortFound = true 804 } 805 if test.expectedResult.remotePort != 0 && h.RemotePort == test.expectedResult.remotePort { 806 rPortFound = true 807 } 808 809 // Validate histogram contains the expected address information 810 if test.expectedResult.byLocalAddr != "" { 811 require.Equal(t, types.AddressTypeLocal, h.AddressType) 812 813 if h.Address == test.expectedResult.byLocalAddr { 814 lAddrFound = true 815 } 816 } else if test.expectedResult.byRemoteAddr != "" { 817 require.Equal(t, types.AddressTypeRemote, h.AddressType) 818 819 if h.Address == test.expectedResult.byRemoteAddr { 820 rAddrFound = true 821 } 822 } else { 823 require.Equal(t, types.AddressTypeAll, h.AddressType) 824 require.Equal(t, types.WildcardAddress, h.Address) 825 } 826 } 827 828 if test.expectedResult.localPort != 0 { 829 require.True(t, lPortFound, 830 "expected to find histogram with local port %d", test.expectedResult.localPort) 831 } 832 if test.expectedResult.remotePort != 0 { 833 require.True(t, rPortFound, 834 "expected to find histogram with local port %d", test.expectedResult.remotePort) 835 } 836 837 if test.expectedResult.byLocalAddr != "" { 838 require.True(t, lAddrFound, 839 "expected to find histogram with local address %s", test.expectedResult.byLocalAddr) 840 } else if test.expectedResult.byRemoteAddr != "" { 841 require.True(t, rAddrFound, 842 "expected to find histogram with remote address %s", test.expectedResult.byRemoteAddr) 843 } 844 }) 845 } 846 } 847 848 func TestRunWithResultV4(t *testing.T) { 849 t.Parallel() 850 851 utilstest.RequireRoot(t) 852 853 serverIP := net.IPv4(127, 80, 80, 80) 854 clientIP := net.IPv4(127, 127, 127, 127) 855 856 // TODO: Use random IPs. 857 testRunWithResult(t, 8080, serverIP, clientIP, 4) 858 } 859 860 func TestRunWithResultV6(t *testing.T) { 861 t.Parallel() 862 863 utilstest.RequireRoot(t) 864 865 clientIP := net.ParseIP("::1") 866 serverIP := net.ParseIP("::1") 867 868 testRunWithResult(t, 8080, serverIP, clientIP, 6) 869 } 870 871 func verifyNetError(t *testing.T, err error) { 872 if err == nil { 873 return 874 } 875 876 // If possible, get more detailed information about the error 877 var oPErr *net.OpError 878 if errors.As(err, &oPErr) { 879 require.Nil(t, oPErr.Err) 880 } 881 882 // Make test fail anyway 883 require.Nil(t, err) 884 } 885 886 func startTCPServer(t *testing.T, serverIP net.IP, serverPort, family int) { 887 t.Helper() 888 889 // "[]" are required for IPv6 addresses. 890 l, err := net.Listen(fmt.Sprintf("tcp%d", family), fmt.Sprintf("[%s]:%d", serverIP, serverPort)) 891 verifyNetError(t, err) 892 require.NotNil(t, l, "expected listener") 893 t.Cleanup(func() { 894 l.Close() 895 }) 896 897 go func() { 898 for { 899 conn, err := l.Accept() 900 if err != nil { 901 require.Contains(t, err.Error(), net.ErrClosed.Error()) 902 return 903 } 904 905 go func(conn net.Conn) { 906 defer conn.Close() 907 908 // Read the data from the connection 909 buf := make([]byte, 1024) 910 n, err := conn.Read(buf) 911 if err != nil { 912 require.Contains(t, err.Error(), net.ErrClosed.Error()) 913 return 914 } 915 require.Greater(t, n, 0) 916 917 // Write the data back to the connection 918 response := fmt.Sprintf("Echo: %s", string(buf)) 919 n, err = conn.Write([]byte(response)) 920 if err != nil { 921 require.Contains(t, err.Error(), net.ErrClosed.Error()) 922 return 923 } 924 require.Equal(t, len(response), n) 925 }(conn) 926 } 927 }() 928 } 929 930 func connectTCPClient(t *testing.T, clientIP net.IP, remoteIP net.IP, remotePort, family int) { 931 t.Helper() 932 933 const ( 934 // Number of parallel connections to establish 935 parallelConnections = 5 936 ) 937 938 // Connect multiple times to the server to generate more traffic 939 wg := sync.WaitGroup{} 940 for i := 0; i < parallelConnections; i++ { 941 wg.Add(1) 942 943 go func(i int) { 944 defer wg.Done() 945 946 // ResolveTCPAddr will assign a random port to the client 947 tcpClient, err := net.ResolveTCPAddr(fmt.Sprintf("tcp%d", family), fmt.Sprintf("[%s]:0", clientIP.String())) 948 verifyNetError(t, err) 949 require.NotNil(t, tcpClient) 950 951 tcpRemote := &net.TCPAddr{ 952 IP: remoteIP, 953 Port: remotePort, 954 } 955 956 conn, err := net.DialTCP(fmt.Sprintf("tcp%d", family), tcpClient, tcpRemote) 957 verifyNetError(t, err) 958 require.NotNil(t, conn, "expected connection") 959 defer conn.Close() 960 961 // Send dummy messages to the server to generate traffic 962 msg := fmt.Sprintf("Hello %d from %s:%d", i, tcpClient.IP, tcpClient.Port) 963 n, err := conn.Write([]byte(msg)) 964 require.Nil(t, err) 965 require.Equal(t, len(msg), n) 966 967 // Read the response from the server 968 received := make([]byte, 1024) 969 n, err = conn.Read(received) 970 require.Nil(t, err) 971 require.Greater(t, n, 0) 972 }(i) 973 } 974 975 // Wait for all connections to be closed 976 wg.Wait() 977 } 978 979 func newGadgetCtx(gadgetParams *params.Params, timeout time.Duration) *gadgetcontext.GadgetContext { 980 return gadgetcontext.NewBuiltIn( 981 context.Background(), 982 "", 983 nil, 984 nil, 985 nil, 986 gadgetParams, 987 nil, 988 nil, 989 nil, 990 log.StandardLogger(), 991 timeout, 992 ) 993 }