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  }