github.com/vishvananda/netlink@v1.3.0/conntrack_test.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  package netlink
     5  
     6  import (
     7  	"encoding/binary"
     8  	"fmt"
     9  	"net"
    10  	"os"
    11  	"runtime"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/vishvananda/netlink/nl"
    16  	"github.com/vishvananda/netns"
    17  	"golang.org/x/sys/unix"
    18  )
    19  
    20  func CheckErrorFail(t *testing.T, err error) {
    21  	if err != nil {
    22  		t.Fatalf("Fatal Error: %s", err)
    23  	}
    24  }
    25  func CheckError(t *testing.T, err error) {
    26  	if err != nil {
    27  		t.Errorf("Error: %s", err)
    28  	}
    29  }
    30  
    31  func udpFlowCreateProg(t *testing.T, flows, srcPort int, dstIP string, dstPort int) {
    32  	for i := 0; i < flows; i++ {
    33  		ServerAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", dstIP, dstPort))
    34  		CheckError(t, err)
    35  
    36  		LocalAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", srcPort+i))
    37  		CheckError(t, err)
    38  
    39  		Conn, err := net.DialUDP("udp", LocalAddr, ServerAddr)
    40  		CheckError(t, err)
    41  
    42  		Conn.Write([]byte("Hello World"))
    43  		Conn.Close()
    44  	}
    45  }
    46  
    47  func nsCreateAndEnter(t *testing.T) (*netns.NsHandle, *netns.NsHandle, *Handle) {
    48  	// Lock the OS Thread so we don't accidentally switch namespaces
    49  	runtime.LockOSThread()
    50  
    51  	// Save the current network namespace
    52  	origns, _ := netns.Get()
    53  
    54  	ns, err := netns.New()
    55  	CheckErrorFail(t, err)
    56  
    57  	h, err := NewHandleAt(ns)
    58  	CheckErrorFail(t, err)
    59  
    60  	// Enter the new namespace
    61  	netns.Set(ns)
    62  
    63  	// Bing up loopback
    64  	link, _ := h.LinkByName("lo")
    65  	h.LinkSetUp(link)
    66  
    67  	return &origns, &ns, h
    68  }
    69  
    70  func applyFilter(flowList []ConntrackFlow, ipv4Filter *ConntrackFilter, ipv6Filter *ConntrackFilter) (ipv4Match, ipv6Match uint) {
    71  	for _, flow := range flowList {
    72  		if ipv4Filter.MatchConntrackFlow(&flow) == true {
    73  			ipv4Match++
    74  		}
    75  		if ipv6Filter.MatchConntrackFlow(&flow) == true {
    76  			ipv6Match++
    77  		}
    78  	}
    79  	return ipv4Match, ipv6Match
    80  }
    81  
    82  // TestConntrackSocket test the opening of a NETFILTER family socket
    83  func TestConntrackSocket(t *testing.T) {
    84  	skipUnlessRoot(t)
    85  	setUpNetlinkTestWithKModule(t, "nf_conntrack")
    86  	setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
    87  
    88  	h, err := NewHandle(unix.NETLINK_NETFILTER)
    89  	CheckErrorFail(t, err)
    90  
    91  	if h.SupportsNetlinkFamily(unix.NETLINK_NETFILTER) != true {
    92  		t.Fatal("ERROR not supporting the NETFILTER family")
    93  	}
    94  }
    95  
    96  // TestConntrackTableList test the conntrack table list
    97  // Creates some flows and checks that they are correctly fetched from the conntrack table
    98  func TestConntrackTableList(t *testing.T) {
    99  	if os.Getenv("CI") == "true" {
   100  		t.Skipf("Fails in CI: Flow creation fails")
   101  	}
   102  	skipUnlessRoot(t)
   103  	k, m, err := KernelVersion()
   104  	if err != nil {
   105  		t.Fatal(err)
   106  	}
   107  	// conntrack l3proto was unified since 4.19
   108  	// https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
   109  	if k < 4 || k == 4 && m < 19 {
   110  		setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
   111  		setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv6")
   112  	}
   113  	setUpNetlinkTestWithKModule(t, "nf_conntrack")
   114  	setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
   115  
   116  	// Creates a new namespace and bring up the loopback interface
   117  	origns, ns, h := nsCreateAndEnter(t)
   118  	defer netns.Set(*origns)
   119  	defer origns.Close()
   120  	defer ns.Close()
   121  	defer runtime.UnlockOSThread()
   122  
   123  	setUpF(t, "/proc/sys/net/netfilter/nf_conntrack_acct", "1")
   124  	setUpF(t, "/proc/sys/net/netfilter/nf_conntrack_timestamp", "1")
   125  	setUpF(t, "/proc/sys/net/netfilter/nf_conntrack_udp_timeout", "45")
   126  
   127  	// Flush the table to start fresh
   128  	err = h.ConntrackTableFlush(ConntrackTable)
   129  	CheckErrorFail(t, err)
   130  
   131  	// Create 5 udp
   132  	startTime := time.Now()
   133  	udpFlowCreateProg(t, 5, 2000, "127.0.0.10", 3000)
   134  
   135  	// Fetch the conntrack table
   136  	flows, err := h.ConntrackTableList(ConntrackTable, unix.AF_INET)
   137  	CheckErrorFail(t, err)
   138  
   139  	// Check that it is able to find the 5 flows created
   140  	var found int
   141  	for _, flow := range flows {
   142  		if flow.Forward.Protocol == 17 &&
   143  			flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
   144  			flow.Forward.DstPort == 3000 &&
   145  			(flow.Forward.SrcPort >= 2000 && flow.Forward.SrcPort <= 2005) {
   146  			found++
   147  			flowStart := time.Unix(0, int64(flow.TimeStart))
   148  			if flowStart.Before(startTime) || flowStart.Sub(startTime) > time.Second {
   149  				t.Error("Invalid conntrack entry start timestamp")
   150  			}
   151  			if flow.TimeStop != 0 {
   152  				t.Error("Invalid conntrack entry stop timestamp")
   153  			}
   154  			// Expect at most one second to have already passed from the configured UDP timeout of 45secs.
   155  			if flow.TimeOut < 44 || flow.TimeOut > 45 {
   156  				t.Error("Invalid conntrack entry timeout")
   157  			}
   158  		}
   159  
   160  		if flow.Forward.Bytes == 0 && flow.Forward.Packets == 0 && flow.Reverse.Bytes == 0 && flow.Reverse.Packets == 0 {
   161  			t.Error("No traffic statistics are collected")
   162  		}
   163  	}
   164  	if found != 5 {
   165  		t.Fatalf("Found only %d flows over 5", found)
   166  	}
   167  
   168  	// Give a try also to the IPv6 version
   169  	_, err = h.ConntrackTableList(ConntrackTable, unix.AF_INET6)
   170  	CheckErrorFail(t, err)
   171  
   172  	// Switch back to the original namespace
   173  	netns.Set(*origns)
   174  }
   175  
   176  // TestConntrackTableFlush test the conntrack table flushing
   177  // Creates some flows and then call the table flush
   178  func TestConntrackTableFlush(t *testing.T) {
   179  	if os.Getenv("CI") == "true" {
   180  		t.Skipf("Fails in CI: Flow creation fails")
   181  	}
   182  	skipUnlessRoot(t)
   183  	setUpNetlinkTestWithKModule(t, "nf_conntrack")
   184  	setUpNetlinkTestWithKModule(t, "nf_conntrack_netlink")
   185  	k, m, err := KernelVersion()
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  	// conntrack l3proto was unified since 4.19
   190  	// https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
   191  	if k < 4 || k == 4 && m < 19 {
   192  		setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
   193  	}
   194  	setUpNetlinkTestWithKModule(t, "nf_conntrack")
   195  	// Creates a new namespace and bring up the loopback interface
   196  	origns, ns, h := nsCreateAndEnter(t)
   197  	defer netns.Set(*origns)
   198  	defer origns.Close()
   199  	defer ns.Close()
   200  	defer runtime.UnlockOSThread()
   201  
   202  	// Create 5 udp flows using netcat
   203  	udpFlowCreateProg(t, 5, 3000, "127.0.0.10", 4000)
   204  
   205  	// Fetch the conntrack table
   206  	flows, err := h.ConntrackTableList(ConntrackTable, unix.AF_INET)
   207  	CheckErrorFail(t, err)
   208  
   209  	// Check that it is able to find the 5 flows created
   210  	var found int
   211  	for _, flow := range flows {
   212  		if flow.Forward.Protocol == 17 &&
   213  			flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
   214  			flow.Forward.DstPort == 4000 &&
   215  			(flow.Forward.SrcPort >= 3000 && flow.Forward.SrcPort <= 3005) {
   216  			found++
   217  		}
   218  	}
   219  	if found != 5 {
   220  		t.Fatalf("Found only %d flows over 5", found)
   221  	}
   222  
   223  	// Flush the table
   224  	err = h.ConntrackTableFlush(ConntrackTable)
   225  	CheckErrorFail(t, err)
   226  
   227  	// Fetch again the flows to validate the flush
   228  	flows, err = h.ConntrackTableList(ConntrackTable, unix.AF_INET)
   229  	CheckErrorFail(t, err)
   230  
   231  	// Check if it is still able to find the 5 flows created
   232  	found = 0
   233  	for _, flow := range flows {
   234  		if flow.Forward.Protocol == 17 &&
   235  			flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
   236  			flow.Forward.DstPort == 4000 &&
   237  			(flow.Forward.SrcPort >= 3000 && flow.Forward.SrcPort <= 3005) {
   238  			found++
   239  		}
   240  	}
   241  	if found > 0 {
   242  		t.Fatalf("Found %d flows, they should had been flushed", found)
   243  	}
   244  
   245  	// Switch back to the original namespace
   246  	netns.Set(*origns)
   247  }
   248  
   249  // TestConntrackTableDelete tests the deletion with filter
   250  // Creates 2 group of flows then deletes only one group and validates the result
   251  func TestConntrackTableDelete(t *testing.T) {
   252  	if os.Getenv("CI") == "true" {
   253  		t.Skipf("Fails in CI: Flow creation fails")
   254  	}
   255  	skipUnlessRoot(t)
   256  
   257  	requiredModules := []string{"nf_conntrack", "nf_conntrack_netlink"}
   258  	k, m, err := KernelVersion()
   259  	if err != nil {
   260  		t.Fatal(err)
   261  	}
   262  	// conntrack l3proto was unified since 4.19
   263  	// https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
   264  	if k < 4 || k == 4 && m < 19 {
   265  		requiredModules = append(requiredModules, "nf_conntrack_ipv4")
   266  	}
   267  
   268  	setUpNetlinkTestWithKModule(t, requiredModules...)
   269  
   270  	// Creates a new namespace and bring up the loopback interface
   271  	origns, ns, h := nsCreateAndEnter(t)
   272  	defer netns.Set(*origns)
   273  	defer origns.Close()
   274  	defer ns.Close()
   275  	defer runtime.UnlockOSThread()
   276  
   277  	// Create 10 udp flows
   278  	udpFlowCreateProg(t, 5, 5000, "127.0.0.10", 6000)
   279  	udpFlowCreateProg(t, 5, 7000, "127.0.0.20", 8000)
   280  
   281  	// Fetch the conntrack table
   282  	flows, err := h.ConntrackTableList(ConntrackTable, unix.AF_INET)
   283  	CheckErrorFail(t, err)
   284  
   285  	// Check that it is able to find the 5 flows created for each group
   286  	var groupA int
   287  	var groupB int
   288  	for _, flow := range flows {
   289  		if flow.Forward.Protocol == 17 &&
   290  			flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
   291  			flow.Forward.DstPort == 6000 &&
   292  			(flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) {
   293  			groupA++
   294  		}
   295  		if flow.Forward.Protocol == 17 &&
   296  			flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) &&
   297  			flow.Forward.DstPort == 8000 &&
   298  			(flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) {
   299  			groupB++
   300  		}
   301  	}
   302  	if groupA != 5 || groupB != 5 {
   303  		t.Fatalf("Flow creation issue groupA:%d, groupB:%d", groupA, groupB)
   304  	}
   305  
   306  	// Create a filter to erase groupB flows
   307  	filter := &ConntrackFilter{}
   308  	filter.AddIP(ConntrackOrigDstIP, net.ParseIP("127.0.0.20"))
   309  	filter.AddProtocol(17)
   310  	filter.AddPort(ConntrackOrigDstPort, 8000)
   311  
   312  	// Flush entries of groupB
   313  	var deleted uint
   314  	if deleted, err = h.ConntrackDeleteFilters(ConntrackTable, unix.AF_INET, filter); err != nil {
   315  		t.Fatalf("Error during the erase: %s", err)
   316  	}
   317  	if deleted != 5 {
   318  		t.Fatalf("Error deleted a wrong number of flows:%d instead of 5", deleted)
   319  	}
   320  
   321  	// Check again the table to verify that are gone
   322  	flows, err = h.ConntrackTableList(ConntrackTable, unix.AF_INET)
   323  	CheckErrorFail(t, err)
   324  
   325  	// Check if it is able to find the 5 flows of groupA but none of groupB
   326  	groupA = 0
   327  	groupB = 0
   328  	for _, flow := range flows {
   329  		if flow.Forward.Protocol == 17 &&
   330  			flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
   331  			flow.Forward.DstPort == 6000 &&
   332  			(flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) {
   333  			groupA++
   334  		}
   335  		if flow.Forward.Protocol == 17 &&
   336  			flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) &&
   337  			flow.Forward.DstPort == 8000 &&
   338  			(flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) {
   339  			groupB++
   340  		}
   341  	}
   342  	if groupA != 5 || groupB > 0 {
   343  		t.Fatalf("Error during the erase groupA:%d, groupB:%d", groupA, groupB)
   344  	}
   345  
   346  	// Switch back to the original namespace
   347  	netns.Set(*origns)
   348  }
   349  
   350  func TestConntrackFilter(t *testing.T) {
   351  	var flowList []ConntrackFlow
   352  	flowList = append(flowList, ConntrackFlow{
   353  			FamilyType: unix.AF_INET,
   354  			Forward: IPTuple{
   355  				SrcIP:    net.ParseIP("10.0.0.1"),
   356  				DstIP:    net.ParseIP("20.0.0.1"),
   357  				SrcPort:  1000,
   358  				DstPort:  2000,
   359  				Protocol: 17,
   360  			},
   361  			Reverse: IPTuple{
   362  				SrcIP:    net.ParseIP("20.0.0.1"),
   363  				DstIP:    net.ParseIP("192.168.1.1"),
   364  				SrcPort:  2000,
   365  				DstPort:  1000,
   366  				Protocol: 17,
   367  			},
   368  		},
   369  		ConntrackFlow{
   370  			FamilyType: unix.AF_INET,
   371  			Forward: IPTuple{
   372  				SrcIP:    net.ParseIP("10.0.0.2"),
   373  				DstIP:    net.ParseIP("20.0.0.2"),
   374  				SrcPort:  5000,
   375  				DstPort:  6000,
   376  				Protocol: 6,
   377  			},
   378  			Reverse: IPTuple{
   379  				SrcIP:    net.ParseIP("20.0.0.2"),
   380  				DstIP:    net.ParseIP("192.168.1.1"),
   381  				SrcPort:  6000,
   382  				DstPort:  5000,
   383  				Protocol: 6,
   384  			},
   385  			Labels: []byte{0, 0, 0, 0, 3, 4, 61, 141, 207, 170, 2, 0, 0, 0, 0, 0},
   386  			Zone:   200,
   387  		},
   388  		ConntrackFlow{
   389  			FamilyType: unix.AF_INET6,
   390  			Forward: IPTuple{
   391  				SrcIP:    net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
   392  				DstIP:    net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
   393  				SrcPort:  1000,
   394  				DstPort:  2000,
   395  				Protocol: 132,
   396  			},
   397  			Reverse: IPTuple{
   398  				SrcIP:    net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
   399  				DstIP:    net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
   400  				SrcPort:  2000,
   401  				DstPort:  1000,
   402  				Protocol: 132,
   403  			},
   404  			Zone: 200,
   405  		})
   406  
   407  	// Empty filter
   408  	v4Match, v6Match := applyFilter(flowList, &ConntrackFilter{}, &ConntrackFilter{})
   409  	if v4Match > 0 || v6Match > 0 {
   410  		t.Fatalf("Error, empty filter cannot match, v4:%d, v6:%d", v4Match, v6Match)
   411  	}
   412  
   413  	// Filter errors
   414  
   415  	// Adding same attribute should fail
   416  	filter := &ConntrackFilter{}
   417  	err := filter.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1"))
   418  	if err != nil {
   419  		t.Fatalf("Unexpected error: %v", err)
   420  	}
   421  	if err := filter.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1")); err == nil {
   422  		t.Fatalf("Error, it should fail adding same attribute to the filter")
   423  	}
   424  	err = filter.AddProtocol(6)
   425  	if err != nil {
   426  		t.Fatalf("Unexpected error: %v", err)
   427  	}
   428  	if err := filter.AddProtocol(17); err == nil {
   429  		t.Fatalf("Error, it should fail adding same attribute to the filter")
   430  	}
   431  	filter.AddPort(ConntrackOrigSrcPort, 80)
   432  	if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
   433  		t.Fatalf("Error, it should fail adding same attribute to the filter")
   434  	}
   435  
   436  	// Can not add a Port filter without Layer 4 protocol
   437  	filter = &ConntrackFilter{}
   438  	if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
   439  		t.Fatalf("Error, it should fail adding a port filter without a protocol")
   440  	}
   441  
   442  	// Can not add a Port filter if the Layer 4 protocol does not support it
   443  	filter = &ConntrackFilter{}
   444  	err = filter.AddProtocol(47)
   445  	if err != nil {
   446  		t.Fatalf("Unexpected error: %v", err)
   447  	}
   448  	if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
   449  		t.Fatalf("Error, it should fail adding a port filter with a wrong protocol")
   450  	}
   451  
   452  	// Proto filter
   453  	filterV4 := &ConntrackFilter{}
   454  	err = filterV4.AddProtocol(6)
   455  	if err != nil {
   456  		t.Fatalf("Unexpected error: %v", err)
   457  	}
   458  
   459  	filterV6 := &ConntrackFilter{}
   460  	err = filterV6.AddProtocol(132)
   461  	if err != nil {
   462  		t.Fatalf("Unexpected error: %v", err)
   463  	}
   464  
   465  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   466  	if v4Match != 1 || v6Match != 1 {
   467  		t.Fatalf("Error, there should be only 1 match for TCP:%d, UDP:%d", v4Match, v6Match)
   468  	}
   469  
   470  	// SrcIP filter
   471  	filterV4 = &ConntrackFilter{}
   472  	err = filterV4.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1"))
   473  	if err != nil {
   474  		t.Fatalf("Unexpected error: %v", err)
   475  	}
   476  
   477  	filterV6 = &ConntrackFilter{}
   478  	err = filterV6.AddIP(ConntrackOrigSrcIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"))
   479  	if err != nil {
   480  		t.Fatalf("Unexpected error: %v", err)
   481  	}
   482  
   483  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   484  	if v4Match != 1 || v6Match != 1 {
   485  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   486  	}
   487  
   488  	// DstIp filter
   489  	filterV4 = &ConntrackFilter{}
   490  	err = filterV4.AddIP(ConntrackOrigDstIP, net.ParseIP("20.0.0.1"))
   491  	if err != nil {
   492  		t.Fatalf("Unexpected error: %v", err)
   493  	}
   494  
   495  	filterV6 = &ConntrackFilter{}
   496  	err = filterV6.AddIP(ConntrackOrigDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
   497  	if err != nil {
   498  		t.Fatalf("Unexpected error: %v", err)
   499  	}
   500  
   501  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   502  	if v4Match != 1 || v6Match != 1 {
   503  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   504  	}
   505  
   506  	// SrcIP for NAT
   507  	filterV4 = &ConntrackFilter{}
   508  	err = filterV4.AddIP(ConntrackReplySrcIP, net.ParseIP("20.0.0.1"))
   509  	if err != nil {
   510  		t.Fatalf("Unexpected error: %v", err)
   511  	}
   512  
   513  	filterV6 = &ConntrackFilter{}
   514  	err = filterV6.AddIP(ConntrackReplySrcIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
   515  	if err != nil {
   516  		t.Fatalf("Unexpected error: %v", err)
   517  	}
   518  
   519  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   520  	if v4Match != 1 || v6Match != 1 {
   521  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   522  	}
   523  
   524  	// DstIP for NAT
   525  	filterV4 = &ConntrackFilter{}
   526  	err = filterV4.AddIP(ConntrackReplyDstIP, net.ParseIP("192.168.1.1"))
   527  	if err != nil {
   528  		t.Fatalf("Unexpected error: %v", err)
   529  	}
   530  
   531  	filterV6 = &ConntrackFilter{}
   532  	err = filterV6.AddIP(ConntrackReplyDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
   533  	if err != nil {
   534  		t.Fatalf("Unexpected error: %v", err)
   535  	}
   536  
   537  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   538  	if v4Match != 2 || v6Match != 0 {
   539  		t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
   540  	}
   541  
   542  	// AnyIp for Nat
   543  	filterV4 = &ConntrackFilter{}
   544  	err = filterV4.AddIP(ConntrackReplyAnyIP, net.ParseIP("192.168.1.1"))
   545  	if err != nil {
   546  		t.Fatalf("Unexpected error: %v", err)
   547  	}
   548  
   549  	filterV6 = &ConntrackFilter{}
   550  	err = filterV6.AddIP(ConntrackReplyAnyIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"))
   551  	if err != nil {
   552  		t.Fatalf("Unexpected error: %v", err)
   553  	}
   554  
   555  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   556  	if v4Match != 2 || v6Match != 1 {
   557  		t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
   558  	}
   559  
   560  	// SrcIPNet filter
   561  	filterV4 = &ConntrackFilter{}
   562  	ipNet, err := ParseIPNet("10.0.0.0/12")
   563  	if err != nil {
   564  		t.Fatalf("Unexpected error: %v", err)
   565  	}
   566  	err = filterV4.AddIPNet(ConntrackOrigSrcIP, ipNet)
   567  	if err != nil {
   568  		t.Fatalf("Unexpected error: %v", err)
   569  	}
   570  
   571  	filterV6 = &ConntrackFilter{}
   572  	ipNet, err = ParseIPNet("eeee:eeee:eeee:eeee::/64")
   573  	if err != nil {
   574  		t.Fatalf("Unexpected error: %v", err)
   575  	}
   576  	err = filterV6.AddIPNet(ConntrackOrigSrcIP, ipNet)
   577  	if err != nil {
   578  		t.Fatalf("Unexpected error: %v", err)
   579  	}
   580  
   581  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   582  	if v4Match != 2 || v6Match != 1 {
   583  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   584  	}
   585  
   586  	// DstIpNet filter
   587  	filterV4 = &ConntrackFilter{}
   588  	ipNet, err = ParseIPNet("20.0.0.0/12")
   589  	if err != nil {
   590  		t.Fatalf("Unexpected error: %v", err)
   591  	}
   592  	err = filterV4.AddIPNet(ConntrackOrigDstIP, ipNet)
   593  	if err != nil {
   594  		t.Fatalf("Unexpected error: %v", err)
   595  	}
   596  
   597  	filterV6 = &ConntrackFilter{}
   598  	ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64")
   599  	if err != nil {
   600  		t.Fatalf("Unexpected error: %v", err)
   601  	}
   602  	err = filterV6.AddIPNet(ConntrackOrigDstIP, ipNet)
   603  	if err != nil {
   604  		t.Fatalf("Unexpected error: %v", err)
   605  	}
   606  
   607  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   608  	if v4Match != 2 || v6Match != 1 {
   609  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   610  	}
   611  
   612  	// SrcIPNet for NAT
   613  	filterV4 = &ConntrackFilter{}
   614  	ipNet, err = ParseIPNet("20.0.0.0/12")
   615  	if err != nil {
   616  		t.Fatalf("Unexpected error: %v", err)
   617  	}
   618  	err = filterV4.AddIPNet(ConntrackReplySrcIP, ipNet)
   619  	if err != nil {
   620  		t.Fatalf("Unexpected error: %v", err)
   621  	}
   622  
   623  	filterV6 = &ConntrackFilter{}
   624  	ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64")
   625  	if err != nil {
   626  		t.Fatalf("Unexpected error: %v", err)
   627  	}
   628  	err = filterV6.AddIPNet(ConntrackReplySrcIP, ipNet)
   629  	if err != nil {
   630  		t.Fatalf("Unexpected error: %v", err)
   631  	}
   632  
   633  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   634  	if v4Match != 2 || v6Match != 1 {
   635  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   636  	}
   637  
   638  	// DstIPNet for NAT
   639  	filterV4 = &ConntrackFilter{}
   640  	ipNet, err = ParseIPNet("192.168.0.0/12")
   641  	if err != nil {
   642  		t.Fatalf("Unexpected error: %v", err)
   643  	}
   644  	err = filterV4.AddIPNet(ConntrackReplyDstIP, ipNet)
   645  	if err != nil {
   646  		t.Fatalf("Unexpected error: %v", err)
   647  	}
   648  
   649  	filterV6 = &ConntrackFilter{}
   650  	ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64")
   651  	if err != nil {
   652  		t.Fatalf("Unexpected error: %v", err)
   653  	}
   654  	err = filterV6.AddIPNet(ConntrackReplyDstIP, ipNet)
   655  	if err != nil {
   656  		t.Fatalf("Unexpected error: %v", err)
   657  	}
   658  
   659  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   660  	if v4Match != 2 || v6Match != 0 {
   661  		t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
   662  	}
   663  
   664  	// AnyIpNet for Nat
   665  	filterV4 = &ConntrackFilter{}
   666  	ipNet, err = ParseIPNet("192.168.0.0/12")
   667  	if err != nil {
   668  		t.Fatalf("Unexpected error: %v", err)
   669  	}
   670  	err = filterV4.AddIPNet(ConntrackReplyAnyIP, ipNet)
   671  	if err != nil {
   672  		t.Fatalf("Unexpected error: %v", err)
   673  	}
   674  
   675  	filterV6 = &ConntrackFilter{}
   676  	ipNet, err = ParseIPNet("eeee:eeee:eeee:eeee::/64")
   677  	if err != nil {
   678  		t.Fatalf("Unexpected error: %v", err)
   679  	}
   680  	err = filterV6.AddIPNet(ConntrackReplyAnyIP, ipNet)
   681  	if err != nil {
   682  		t.Fatalf("Unexpected error: %v", err)
   683  	}
   684  
   685  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   686  	if v4Match != 2 || v6Match != 1 {
   687  		t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
   688  	}
   689  	// SrcPort filter
   690  	filterV4 = &ConntrackFilter{}
   691  	err = filterV4.AddProtocol(6)
   692  	if err != nil {
   693  		t.Fatalf("Unexpected error: %v", err)
   694  	}
   695  	err = filterV4.AddPort(ConntrackOrigSrcPort, 5000)
   696  	if err != nil {
   697  		t.Fatalf("Unexpected error: %v", err)
   698  	}
   699  
   700  	filterV6 = &ConntrackFilter{}
   701  	err = filterV6.AddProtocol(132)
   702  	if err != nil {
   703  		t.Fatalf("Unexpected error: %v", err)
   704  	}
   705  	err = filterV6.AddPort(ConntrackOrigSrcPort, 1000)
   706  	if err != nil {
   707  		t.Fatalf("Unexpected error: %v", err)
   708  	}
   709  
   710  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   711  	if v4Match != 1 || v6Match != 1 {
   712  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   713  	}
   714  
   715  	// DstPort filter
   716  	filterV4 = &ConntrackFilter{}
   717  	err = filterV4.AddProtocol(6)
   718  	if err != nil {
   719  		t.Fatalf("Unexpected error: %v", err)
   720  	}
   721  	err = filterV4.AddPort(ConntrackOrigDstPort, 6000)
   722  	if err != nil {
   723  		t.Fatalf("Unexpected error: %v", err)
   724  	}
   725  
   726  	filterV6 = &ConntrackFilter{}
   727  	err = filterV6.AddProtocol(132)
   728  	if err != nil {
   729  		t.Fatalf("Unexpected error: %v", err)
   730  	}
   731  	err = filterV6.AddPort(ConntrackOrigDstPort, 2000)
   732  	if err != nil {
   733  		t.Fatalf("Unexpected error: %v", err)
   734  	}
   735  
   736  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   737  	if v4Match != 1 || v6Match != 1 {
   738  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   739  	}
   740  
   741  	// Labels filter
   742  	filterV4 = &ConntrackFilter{}
   743  	var labels [][]byte
   744  	labels = append(labels, []byte{3, 4, 61, 141, 207, 170})
   745  	labels = append(labels, []byte{0x2})
   746  	err = filterV4.AddLabels(ConntrackMatchLabels, labels)
   747  	if err != nil {
   748  		t.Fatalf("Unexpected error: %v", err)
   749  	}
   750  
   751  	filterV6 = &ConntrackFilter{}
   752  	err = filterV6.AddLabels(ConntrackUnmatchLabels, labels)
   753  	if err != nil {
   754  		t.Fatalf("Unexpected error: %v", err)
   755  	}
   756  
   757  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   758  	if v4Match != 1 || v6Match != 0 {
   759  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   760  	}
   761  
   762  	filterV4 = &ConntrackFilter{}
   763  	err = filterV4.AddZone(200)
   764  	if err != nil {
   765  		t.Fatalf("Unexpected error: %v", err)
   766  	}
   767  	filterV6 = &ConntrackFilter{}
   768  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   769  	if v4Match != 2 || v6Match != 0 {
   770  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   771  	}
   772  }
   773  
   774  func TestParseRawData(t *testing.T) {
   775  	if nl.NativeEndian() == binary.BigEndian {
   776  		t.Skip("testdata expect little-endian test executor")
   777  	}
   778  	os.Setenv("TZ", "") // print timestamps in UTC
   779  	tests := []struct {
   780  		testname         string
   781  		rawData          []byte
   782  		expConntrackFlow string
   783  	}{
   784  		{
   785  			testname: "UDP conntrack",
   786  			rawData: []byte{
   787  				/* Nfgenmsg header */
   788  				2, 0, 0, 0,
   789  				/* >> nested CTA_TUPLE_ORIG */
   790  				52, 0, 1, 128,
   791  				/* >>>> nested CTA_TUPLE_IP */
   792  				20, 0, 1, 128,
   793  				/* >>>>>> CTA_IP_V4_SRC */
   794  				8, 0, 1, 0,
   795  				192, 168, 0, 10,
   796  				/* >>>>>> CTA_IP_V4_DST */
   797  				8, 0, 2, 0,
   798  				192, 168, 0, 3,
   799  				/* >>>>>> nested proto info */
   800  				28, 0, 2, 128,
   801  				/* >>>>>>>> CTA_PROTO_NUM */
   802  				5, 0, 1, 0,
   803  				17, 0, 0, 0,
   804  				/* >>>>>>>> CTA_PROTO_SRC_PORT */
   805  				6, 0, 2, 0,
   806  				189, 1, 0, 0,
   807  				/* >>>>>>>> CTA_PROTO_DST_PORT */
   808  				6, 0, 3, 0,
   809  				0, 53, 0, 0,
   810  				/* >> CTA_TUPLE_REPLY */
   811  				52, 0, 2, 128,
   812  				/* >>>> nested CTA_TUPLE_IP */
   813  				20, 0, 1, 128,
   814  				/* >>>>>> CTA_IP_V4_SRC */
   815  				8, 0, 1, 0,
   816  				192, 168, 0, 3,
   817  				/* >>>>>> CTA_IP_V4_DST */
   818  				8, 0, 2, 0,
   819  				192, 168, 0, 10,
   820  				/* >>>>>> nested proto info */
   821  				28, 0, 2, 128,
   822  				/* >>>>>>>> CTA_PROTO_NUM */
   823  				5, 0, 1, 0,
   824  				17, 0, 0, 0,
   825  				/* >>>>>>>> CTA_PROTO_SRC_PORT */
   826  				6, 0, 2, 0,
   827  				0, 53, 0, 0,
   828  				/* >>>>>>>> CTA_PROTO_DST_PORT */
   829  				6, 0, 3, 0,
   830  				189, 1, 0, 0,
   831  				/* >> CTA_STATUS */
   832  				8, 0, 3, 0,
   833  				0, 0, 1, 138,
   834  				/* >> CTA_MARK */
   835  				8, 0, 8, 0,
   836  				0, 0, 0, 5,
   837  				/* >> CTA_ID */
   838  				8, 0, 12, 0,
   839  				81, 172, 253, 151,
   840  				/* >> CTA_USE */
   841  				8, 0, 11, 0,
   842  				0, 0, 0, 1,
   843  				/* >> CTA_TIMEOUT */
   844  				8, 0, 7, 0,
   845  				0, 0, 0, 32,
   846  				/* >> nested CTA_COUNTERS_ORIG */
   847  				28, 0, 9, 128,
   848  				/* >>>> CTA_COUNTERS_PACKETS */
   849  				12, 0, 1, 0,
   850  				0, 0, 0, 0, 0, 0, 0, 1,
   851  				/* >>>> CTA_COUNTERS_BYTES */
   852  				12, 0, 2, 0,
   853  				0, 0, 0, 0, 0, 0, 0, 55,
   854  				/* >> nested CTA_COUNTERS_REPLY */
   855  				28, 0, 10, 128,
   856  				/* >>>> CTA_COUNTERS_PACKETS */
   857  				12, 0, 1, 0,
   858  				0, 0, 0, 0, 0, 0, 0, 1,
   859  				/* >>>> CTA_COUNTERS_BYTES */
   860  				12, 0, 2, 0,
   861  				0, 0, 0, 0, 0, 0, 0, 71,
   862  				/* >> nested CTA_TIMESTAMP */
   863  				16, 0, 20, 128,
   864  				/* >>>> CTA_TIMESTAMP_START */
   865  				12, 0, 1, 0,
   866  				22, 134, 80, 142, 230, 127, 74, 166,
   867  				/* >> CTA_LABELS */
   868  				20, 0, 22, 0,
   869  				0, 0, 0, 0, 5, 0, 18, 172, 66, 2, 1, 0, 0, 0, 0, 0},
   870  			expConntrackFlow: "udp\t17 src=192.168.0.10 dst=192.168.0.3 sport=48385 dport=53 packets=1 bytes=55\t" +
   871  				"src=192.168.0.3 dst=192.168.0.10 sport=53 dport=48385 packets=1 bytes=71 mark=0x5 labels=0x00000000050012ac4202010000000000 " +
   872  				"start=2021-06-07 13:41:30.39632247 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=32(sec)",
   873  		},
   874  		{
   875  			testname: "TCP conntrack",
   876  			rawData: []byte{
   877  				/* Nfgenmsg header */
   878  				2, 0, 0, 0,
   879  				/* >> nested CTA_TUPLE_ORIG */
   880  				52, 0, 1, 128,
   881  				/* >>>> nested CTA_TUPLE_IP */
   882  				20, 0, 1, 128,
   883  				/* >>>>>> CTA_IP_V4_SRC */
   884  				8, 0, 1, 0,
   885  				192, 168, 0, 10,
   886  				/* >>>>>> CTA_IP_V4_DST */
   887  				8, 0, 2, 0,
   888  				192, 168, 77, 73,
   889  				/* >>>>>> nested proto info */
   890  				28, 0, 2, 128,
   891  				/* >>>>>>>> CTA_PROTO_NUM */
   892  				5, 0, 1, 0,
   893  				6, 0, 0, 0,
   894  				/* >>>>>>>> CTA_PROTO_SRC_PORT */
   895  				6, 0, 2, 0,
   896  				166, 129, 0, 0,
   897  				/* >>>>>>>> CTA_PROTO_DST_PORT */
   898  				6, 0, 3, 0,
   899  				13, 5, 0, 0,
   900  				/* >> CTA_TUPLE_REPLY */
   901  				52, 0, 2, 128,
   902  				/* >>>> nested CTA_TUPLE_IP */
   903  				20, 0, 1, 128,
   904  				/* >>>>>> CTA_IP_V4_SRC */
   905  				8, 0, 1, 0,
   906  				192, 168, 77, 73,
   907  				/* >>>>>> CTA_IP_V4_DST */
   908  				8, 0, 2, 0,
   909  				192, 168, 0, 10,
   910  				/* >>>>>> nested proto info */
   911  				28, 0, 2, 128,
   912  				/* >>>>>>>> CTA_PROTO_NUM */
   913  				5, 0, 1, 0,
   914  				6, 0, 0, 0,
   915  				/* >>>>>>>> CTA_PROTO_SRC_PORT */
   916  				6, 0, 2, 0,
   917  				13, 5, 0, 0,
   918  				/* >>>>>>>> CTA_PROTO_DST_PORT */
   919  				6, 0, 3, 0,
   920  				166, 129, 0, 0,
   921  				/* >> CTA_STATUS */
   922  				8, 0, 3, 0,
   923  				0, 0, 1, 142,
   924  				/* >> CTA_MARK */
   925  				8, 0, 8, 0,
   926  				0, 0, 0, 5,
   927  				/* >> CTA_ID */
   928  				8, 0, 12, 0,
   929  				177, 65, 179, 133,
   930  				/* >> CTA_USE */
   931  				8, 0, 11, 0,
   932  				0, 0, 0, 1,
   933  				/* >> CTA_TIMEOUT */
   934  				8, 0, 7, 0,
   935  				0, 0, 0, 152,
   936  				/* >> CTA_PROTOINFO */
   937  				48, 0, 4, 128,
   938  				44, 0, 1, 128,
   939  				5, 0, 1, 0, 8, 0, 0, 0,
   940  				5, 0, 2, 0, 0, 0, 0, 0,
   941  				5, 0, 3, 0, 0, 0, 0, 0,
   942  				6, 0, 4, 0, 39, 0, 0, 0,
   943  				6, 0, 5, 0, 32, 0, 0, 0,
   944  				/* >> nested CTA_COUNTERS_ORIG */
   945  				28, 0, 9, 128,
   946  				/* >>>> CTA_COUNTERS_PACKETS */
   947  				12, 0, 1, 0,
   948  				0, 0, 0, 0, 0, 0, 0, 11,
   949  				/* >>>> CTA_COUNTERS_BYTES */
   950  				12, 0, 2, 0,
   951  				0, 0, 0, 0, 0, 0, 7, 122,
   952  				/* >> nested CTA_COUNTERS_REPLY */
   953  				28, 0, 10, 128,
   954  				/* >>>> CTA_COUNTERS_PACKETS */
   955  				12, 0, 1, 0,
   956  				0, 0, 0, 0, 0, 0, 0, 10,
   957  				/* >>>> CTA_COUNTERS_BYTES */
   958  				12, 0, 2, 0,
   959  				0, 0, 0, 0, 0, 0, 7, 66,
   960  				/* >> CTA_ZONE */
   961  				8, 0, 18, 0,
   962  				0, 100, 0, 0,
   963  				/* >> nested CTA_TIMESTAMP */
   964  				16, 0, 20, 128,
   965  				/* >>>> CTA_TIMESTAMP_START */
   966  				12, 0, 1, 0,
   967  				22, 134, 80, 175, 134, 10, 182, 221},
   968  			expConntrackFlow: "tcp\t6 src=192.168.0.10 dst=192.168.77.73 sport=42625 dport=3333 packets=11 bytes=1914\t" +
   969  				"src=192.168.77.73 dst=192.168.0.10 sport=3333 dport=42625 packets=10 bytes=1858 mark=0x5 zone=100 " +
   970  				"start=2021-06-07 13:43:50.511990493 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=152(sec)",
   971  		},
   972  	}
   973  
   974  	for _, test := range tests {
   975  		t.Run(test.testname, func(t *testing.T) {
   976  			conntrackFlow := parseRawData(test.rawData)
   977  			if conntrackFlow.String() != test.expConntrackFlow {
   978  				t.Errorf("expected conntrack flow:\n\t%q\ngot conntrack flow:\n\t%q",
   979  					test.expConntrackFlow, conntrackFlow)
   980  			}
   981  		})
   982  	}
   983  }
   984  
   985  // TestConntrackUpdateV4 first tries to update a non-existant IPv4 conntrack and asserts that an error occurs.
   986  // It then creates a conntrack entry using and adjacent API method (ConntrackCreate), and attempts to update the value of the created conntrack.
   987  func TestConntrackUpdateV4(t *testing.T) {
   988  	// Print timestamps in UTC
   989  	os.Setenv("TZ", "")
   990  
   991  	requiredModules := []string{"nf_conntrack", "nf_conntrack_netlink"}
   992  	k, m, err := KernelVersion()
   993  	if err != nil {
   994  		t.Fatal(err)
   995  	}
   996  	// Conntrack l3proto was unified since 4.19
   997  	// https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
   998  	if k < 4 || k == 4 && m < 19 {
   999  		requiredModules = append(requiredModules, "nf_conntrack_ipv4")
  1000  	}
  1001  	// Implicitly skips test if not root:
  1002  	nsStr, teardown := setUpNamedNetlinkTestWithKModule(t, requiredModules...)
  1003  	defer teardown()
  1004  
  1005  	ns, err := netns.GetFromName(nsStr)
  1006  	if err != nil {
  1007  		t.Fatalf("couldn't get handle to generated namespace: %s", err)
  1008  	}
  1009  
  1010  	h, err := NewHandleAt(ns, nl.FAMILY_V4)
  1011  	if err != nil {
  1012  		t.Fatalf("failed to create netlink handle: %s", err)
  1013  	}
  1014  
  1015  	flow := ConntrackFlow{
  1016  		FamilyType: FAMILY_V4,
  1017  		Forward: IPTuple{
  1018  			SrcIP: net.IP{234,234,234,234},
  1019  			DstIP: net.IP{123,123,123,123},
  1020  			SrcPort: 48385,
  1021  			DstPort: 53,
  1022  			Protocol: unix.IPPROTO_TCP,
  1023  		},
  1024  		Reverse: IPTuple{
  1025  			SrcIP: net.IP{123,123,123,123},
  1026  			DstIP: net.IP{234,234,234,234},
  1027  			SrcPort: 53,
  1028  			DstPort: 48385,
  1029  			Protocol: unix.IPPROTO_TCP,
  1030  		},
  1031  		// No point checking equivalence of timeout, but value must
  1032  		// be reasonable to allow for a potentially slow subsequent read.
  1033  		TimeOut:   100,
  1034  		Mark: 12,
  1035  		ProtoInfo: &ProtoInfoTCP{
  1036  			State: nl.TCP_CONNTRACK_SYN_SENT2,
  1037  		},
  1038  	}
  1039  
  1040  	err = h.ConntrackUpdate(ConntrackTable, nl.FAMILY_V4, &flow)
  1041  	if err == nil {
  1042  		t.Fatalf("expected an error to occur when trying to update a non-existant conntrack: %+v", flow)
  1043  	}
  1044  
  1045  	err = h.ConntrackCreate(ConntrackTable, nl.FAMILY_V4, &flow)
  1046  	if err != nil {
  1047  		t.Fatalf("failed to insert conntrack: %s", err)
  1048  	}
  1049  
  1050  	flows, err := h.ConntrackTableList(ConntrackTable, nl.FAMILY_V4)
  1051  	if err != nil {
  1052  		t.Fatalf("failed to list conntracks following successful insert: %s", err)
  1053  	}
  1054  
  1055  	filter := ConntrackFilter{
  1056  		ipNetFilter: map[ConntrackFilterType]*net.IPNet{
  1057  			ConntrackOrigSrcIP: NewIPNet(flow.Forward.SrcIP),
  1058  			ConntrackOrigDstIP: NewIPNet(flow.Forward.DstIP),
  1059  			ConntrackReplySrcIP: NewIPNet(flow.Reverse.SrcIP),
  1060  			ConntrackReplyDstIP: NewIPNet(flow.Reverse.DstIP),
  1061  		},
  1062  		portFilter: map[ConntrackFilterType]uint16{
  1063  			ConntrackOrigSrcPort: flow.Forward.SrcPort,
  1064  			ConntrackOrigDstPort: flow.Forward.DstPort,
  1065  		},
  1066  		protoFilter:unix.IPPROTO_TCP,
  1067  	}
  1068  
  1069  	var match *ConntrackFlow
  1070  	for _, f := range flows {
  1071  		if filter.MatchConntrackFlow(f) {
  1072  			match = f
  1073  			break
  1074  		}
  1075  	}
  1076  
  1077  	if match == nil {
  1078  		t.Fatalf("Didn't find any matching conntrack entries for original flow: %+v\n Filter used: %+v", flow, filter)
  1079  	} else {
  1080  		t.Logf("Found entry in conntrack table matching original flow: %+v labels=%+v", match, match.Labels)
  1081  	}
  1082  	checkFlowsEqual(t, &flow, match)
  1083  	checkProtoInfosEqual(t, flow.ProtoInfo, match.ProtoInfo)
  1084  
  1085  	// Change the conntrack and update the kernel entry.
  1086  	flow.Mark = 10
  1087  	flow.ProtoInfo = &ProtoInfoTCP{
  1088  		State: nl.TCP_CONNTRACK_ESTABLISHED,
  1089  	}
  1090  	err = h.ConntrackUpdate(ConntrackTable, nl.FAMILY_V4, &flow)
  1091  	if err != nil {
  1092  		t.Fatalf("failed to update conntrack with new mark: %s", err)
  1093  	}
  1094  
  1095  	// Look for updated conntrack.
  1096  	flows, err = h.ConntrackTableList(ConntrackTable, nl.FAMILY_V4)
  1097  	if err != nil {
  1098  		t.Fatalf("failed to list conntracks following successful update: %s", err)
  1099  	}
  1100  
  1101  	var updatedMatch *ConntrackFlow
  1102  	for _, f := range flows {
  1103  		if filter.MatchConntrackFlow(f) {
  1104  			updatedMatch = f
  1105  			break
  1106  		}
  1107  	}
  1108  	if updatedMatch == nil {
  1109  		t.Fatalf("Didn't find any matching conntrack entries for updated flow: %+v\n Filter used: %+v", flow, filter)
  1110  	} else {
  1111  		t.Logf("Found entry in conntrack table matching updated flow: %+v labels=%+v", updatedMatch, updatedMatch.Labels)
  1112  	}
  1113  
  1114  	checkFlowsEqual(t, &flow, updatedMatch)
  1115  	checkProtoInfosEqual(t, flow.ProtoInfo, updatedMatch.ProtoInfo)
  1116  }
  1117  
  1118  // TestConntrackUpdateV6 first tries to update a non-existant IPv6 conntrack and asserts that an error occurs.
  1119  // It then creates a conntrack entry using and adjacent API method (ConntrackCreate), and attempts to update the value of the created conntrack.
  1120  func TestConntrackUpdateV6(t *testing.T) {
  1121  	// Print timestamps in UTC
  1122  	os.Setenv("TZ", "")
  1123  
  1124  	requiredModules := []string{"nf_conntrack", "nf_conntrack_netlink"}
  1125  	k, m, err := KernelVersion()
  1126  	if err != nil {
  1127  		t.Fatal(err)
  1128  	}
  1129  	// Conntrack l3proto was unified since 4.19
  1130  	// https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
  1131  	if k < 4 || k == 4 && m < 19 {
  1132  		requiredModules = append(requiredModules, "nf_conntrack_ipv4")
  1133  	}
  1134  	// Implicitly skips test if not root:
  1135  	nsStr, teardown := setUpNamedNetlinkTestWithKModule(t, requiredModules...)
  1136  	defer teardown()
  1137  
  1138  	ns, err := netns.GetFromName(nsStr)
  1139  	if err != nil {
  1140  		t.Fatalf("couldn't get handle to generated namespace: %s", err)
  1141  	}
  1142  
  1143  	h, err := NewHandleAt(ns, nl.FAMILY_V6)
  1144  	if err != nil {
  1145  		t.Fatalf("failed to create netlink handle: %s", err)
  1146  	}
  1147  
  1148  	flow := ConntrackFlow{
  1149  		FamilyType: FAMILY_V6,
  1150  		Forward: IPTuple{
  1151  			SrcIP: net.ParseIP("2001:db8::68"),
  1152  			DstIP: net.ParseIP("2001:db9::32"),
  1153  			SrcPort: 48385,
  1154  			DstPort: 53,
  1155  			Protocol: unix.IPPROTO_TCP,
  1156  		},
  1157  		Reverse: IPTuple{
  1158  			SrcIP: net.ParseIP("2001:db9::32"),
  1159  			DstIP: net.ParseIP("2001:db8::68"),
  1160  			SrcPort: 53,
  1161  			DstPort: 48385,
  1162  			Protocol: unix.IPPROTO_TCP,
  1163  		},
  1164  		// No point checking equivalence of timeout, but value must
  1165  		// be reasonable to allow for a potentially slow subsequent read.
  1166  		TimeOut:   100,
  1167  		Mark: 12,
  1168  		ProtoInfo: &ProtoInfoTCP{
  1169  			State: nl.TCP_CONNTRACK_SYN_SENT2,
  1170  		},
  1171  	}
  1172  
  1173  	err = h.ConntrackUpdate(ConntrackTable, nl.FAMILY_V6, &flow)
  1174  	if err == nil {
  1175  		t.Fatalf("expected an error to occur when trying to update a non-existant conntrack: %+v", flow)
  1176  	}
  1177  
  1178  	err = h.ConntrackCreate(ConntrackTable, nl.FAMILY_V6, &flow)
  1179  	if err != nil {
  1180  		t.Fatalf("failed to insert conntrack: %s", err)
  1181  	}
  1182  
  1183  	flows, err := h.ConntrackTableList(ConntrackTable, nl.FAMILY_V6)
  1184  	if err != nil {
  1185  		t.Fatalf("failed to list conntracks following successful insert: %s", err)
  1186  	}
  1187  
  1188  	filter := ConntrackFilter{
  1189  		ipNetFilter: map[ConntrackFilterType]*net.IPNet{
  1190  			ConntrackOrigSrcIP: NewIPNet(flow.Forward.SrcIP),
  1191  			ConntrackOrigDstIP: NewIPNet(flow.Forward.DstIP),
  1192  			ConntrackReplySrcIP: NewIPNet(flow.Reverse.SrcIP),
  1193  			ConntrackReplyDstIP: NewIPNet(flow.Reverse.DstIP),
  1194  		},
  1195  		portFilter: map[ConntrackFilterType]uint16{
  1196  			ConntrackOrigSrcPort: flow.Forward.SrcPort,
  1197  			ConntrackOrigDstPort: flow.Forward.DstPort,
  1198  		},
  1199  		protoFilter:unix.IPPROTO_TCP,
  1200  	}
  1201  
  1202  	var match *ConntrackFlow
  1203  	for _, f := range flows {
  1204  		if filter.MatchConntrackFlow(f) {
  1205  			match = f
  1206  			break
  1207  		}
  1208  	}
  1209  
  1210  	if match == nil {
  1211  		t.Fatalf("didn't find any matching conntrack entries for original flow: %+v\n Filter used: %+v", flow, filter)
  1212  	} else {
  1213  		t.Logf("found entry in conntrack table matching original flow: %+v labels=%+v", match, match.Labels)
  1214  	}
  1215  	checkFlowsEqual(t, &flow, match)
  1216  	checkProtoInfosEqual(t, flow.ProtoInfo, match.ProtoInfo)
  1217  
  1218  	// Change the conntrack and update the kernel entry.
  1219  	flow.Mark = 10
  1220  	flow.ProtoInfo = &ProtoInfoTCP{
  1221  		State: nl.TCP_CONNTRACK_ESTABLISHED,
  1222  	}
  1223  	err = h.ConntrackUpdate(ConntrackTable, nl.FAMILY_V6, &flow)
  1224  	if err != nil {
  1225  		t.Fatalf("failed to update conntrack with new mark: %s", err)
  1226  	}
  1227  
  1228  	// Look for updated conntrack.
  1229  	flows, err = h.ConntrackTableList(ConntrackTable, nl.FAMILY_V6)
  1230  	if err != nil {
  1231  		t.Fatalf("failed to list conntracks following successful update: %s", err)
  1232  	}
  1233  
  1234  	var updatedMatch *ConntrackFlow
  1235  	for _, f := range flows {
  1236  		if filter.MatchConntrackFlow(f) {
  1237  			updatedMatch = f
  1238  			break
  1239  		}
  1240  	}
  1241  	if updatedMatch == nil {
  1242  		t.Fatalf("didn't find any matching conntrack entries for updated flow: %+v\n Filter used: %+v", flow, filter)
  1243  	} else {
  1244  		t.Logf("found entry in conntrack table matching updated flow: %+v labels=%+v", updatedMatch, updatedMatch.Labels)
  1245  	}
  1246  
  1247  	checkFlowsEqual(t, &flow, updatedMatch)
  1248  	checkProtoInfosEqual(t, flow.ProtoInfo, updatedMatch.ProtoInfo)
  1249  }
  1250  
  1251  func TestConntrackCreateV4(t *testing.T) {
  1252  	// Print timestamps in UTC
  1253  	os.Setenv("TZ", "")
  1254  
  1255  	requiredModules := []string{"nf_conntrack", "nf_conntrack_netlink"}
  1256  	k, m, err := KernelVersion()
  1257  	if err != nil {
  1258  		t.Fatal(err)
  1259  	}
  1260  	// Conntrack l3proto was unified since 4.19
  1261  	// https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
  1262  	if k < 4 || k == 4 && m < 19 {
  1263  		requiredModules = append(requiredModules, "nf_conntrack_ipv4")
  1264  	}
  1265  	// Implicitly skips test if not root:
  1266  	nsStr, teardown := setUpNamedNetlinkTestWithKModule(t, requiredModules...)
  1267  	defer teardown()
  1268  
  1269  	ns, err := netns.GetFromName(nsStr)
  1270  	if err != nil {
  1271  		t.Fatalf("couldn't get handle to generated namespace: %s", err)
  1272  	}
  1273  
  1274  	h, err := NewHandleAt(ns, nl.FAMILY_V4)
  1275  	if err != nil {
  1276  		t.Fatalf("failed to create netlink handle: %s", err)
  1277  	}
  1278  
  1279  	flow := ConntrackFlow{
  1280  		FamilyType: FAMILY_V4,
  1281  		Forward: IPTuple{
  1282  			SrcIP: net.IP{234,234,234,234},
  1283  			DstIP: net.IP{123,123,123,123},
  1284  			SrcPort: 48385,
  1285  			DstPort: 53,
  1286  			Protocol: unix.IPPROTO_TCP,
  1287  		},
  1288  		Reverse: IPTuple{
  1289  			SrcIP: net.IP{123,123,123,123},
  1290  			DstIP: net.IP{234,234,234,234},
  1291  			SrcPort: 53,
  1292  			DstPort: 48385,
  1293  			Protocol: unix.IPPROTO_TCP,
  1294  		},
  1295  		// No point checking equivalence of timeout, but value must
  1296  		// be reasonable to allow for a potentially slow subsequent read.
  1297  		TimeOut:   100,
  1298  		Mark: 12,
  1299  		ProtoInfo: &ProtoInfoTCP{
  1300  			State: nl.TCP_CONNTRACK_ESTABLISHED,
  1301  		},
  1302  	}
  1303  
  1304  	err = h.ConntrackCreate(ConntrackTable, nl.FAMILY_V4, &flow)
  1305  	if err != nil {
  1306  		t.Fatalf("failed to insert conntrack: %s", err)
  1307  	}
  1308  
  1309  	flows, err := h.ConntrackTableList(ConntrackTable, nl.FAMILY_V4)
  1310  	if err != nil {
  1311  		t.Fatalf("failed to list conntracks following successful insert: %s", err)
  1312  	}
  1313  
  1314  	filter := ConntrackFilter{
  1315  		ipNetFilter: map[ConntrackFilterType]*net.IPNet{
  1316  			ConntrackOrigSrcIP: NewIPNet(flow.Forward.SrcIP),
  1317  			ConntrackOrigDstIP: NewIPNet(flow.Forward.DstIP),
  1318  			ConntrackReplySrcIP: NewIPNet(flow.Reverse.SrcIP),
  1319  			ConntrackReplyDstIP: NewIPNet(flow.Reverse.DstIP),
  1320  		},
  1321  		portFilter: map[ConntrackFilterType]uint16{
  1322  			ConntrackOrigSrcPort: flow.Forward.SrcPort,
  1323  			ConntrackOrigDstPort: flow.Forward.DstPort,
  1324  		},
  1325  		protoFilter:unix.IPPROTO_TCP,
  1326  	}
  1327  
  1328  	var match *ConntrackFlow
  1329  	for _, f := range flows {
  1330  		if filter.MatchConntrackFlow(f) {
  1331  			match = f
  1332  			break
  1333  		}
  1334  	}
  1335  
  1336  	if match == nil {
  1337  		t.Fatalf("didn't find any matching conntrack entries for original flow: %+v\n Filter used: %+v", flow, filter)
  1338  	} else {
  1339  		t.Logf("Found entry in conntrack table matching original flow: %+v labels=%+v", match, match.Labels)
  1340  	}
  1341  
  1342  	checkFlowsEqual(t, &flow, match)
  1343  	checkProtoInfosEqual(t, flow.ProtoInfo, match.ProtoInfo)
  1344  }
  1345  
  1346  func TestConntrackCreateV6(t *testing.T) {
  1347  	// Print timestamps in UTC
  1348  	os.Setenv("TZ", "")
  1349  
  1350  	requiredModules := []string{"nf_conntrack", "nf_conntrack_netlink"}
  1351  	k, m, err := KernelVersion()
  1352  	if err != nil {
  1353  		t.Fatal(err)
  1354  	}
  1355  	// Conntrack l3proto was unified since 4.19
  1356  	// https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
  1357  	if k < 4 || k == 4 && m < 19 {
  1358  		requiredModules = append(requiredModules, "nf_conntrack_ipv4")
  1359  	}
  1360  	// Implicitly skips test if not root:
  1361  	nsStr, teardown := setUpNamedNetlinkTestWithKModule(t, requiredModules...)
  1362  	defer teardown()
  1363  
  1364  	ns, err := netns.GetFromName(nsStr)
  1365  	if err != nil {
  1366  		t.Fatalf("couldn't get handle to generated namespace: %s", err)
  1367  	}
  1368  
  1369  	h, err := NewHandleAt(ns, nl.FAMILY_V6)
  1370  	if err != nil {
  1371  		t.Fatalf("failed to create netlink handle: %s", err)
  1372  	}
  1373  
  1374  	flow := ConntrackFlow{
  1375  		FamilyType: FAMILY_V6,
  1376  		Forward: IPTuple{
  1377  			SrcIP: net.ParseIP("2001:db8::68"),
  1378  			DstIP: net.ParseIP("2001:db9::32"),
  1379  			SrcPort: 48385,
  1380  			DstPort: 53,
  1381  			Protocol: unix.IPPROTO_TCP,
  1382  		},
  1383  		Reverse: IPTuple{
  1384  			SrcIP: net.ParseIP("2001:db9::32"),
  1385  			DstIP: net.ParseIP("2001:db8::68"),
  1386  			SrcPort: 53,
  1387  			DstPort: 48385,
  1388  			Protocol: unix.IPPROTO_TCP,
  1389  		},
  1390  		// No point checking equivalence of timeout, but value must
  1391  		// be reasonable to allow for a potentially slow subsequent read.
  1392  		TimeOut:    100,
  1393  		Mark: 12,
  1394  		ProtoInfo: &ProtoInfoTCP{
  1395  			State: nl.TCP_CONNTRACK_ESTABLISHED,
  1396  		},
  1397  	}
  1398  
  1399  	err = h.ConntrackCreate(ConntrackTable, nl.FAMILY_V6, &flow)
  1400  	if err != nil {
  1401  		t.Fatalf("failed to insert conntrack: %s", err)
  1402  	}
  1403  
  1404  	flows, err := h.ConntrackTableList(ConntrackTable, nl.FAMILY_V6)
  1405  	if err != nil {
  1406  		t.Fatalf("failed to list conntracks following successful insert: %s", err)
  1407  	}
  1408  
  1409  	filter := ConntrackFilter{
  1410  		ipNetFilter: map[ConntrackFilterType]*net.IPNet{
  1411  			ConntrackOrigSrcIP: NewIPNet(flow.Forward.SrcIP),
  1412  			ConntrackOrigDstIP: NewIPNet(flow.Forward.DstIP),
  1413  			ConntrackReplySrcIP: NewIPNet(flow.Reverse.SrcIP),
  1414  			ConntrackReplyDstIP: NewIPNet(flow.Reverse.DstIP),
  1415  		},
  1416  		portFilter: map[ConntrackFilterType]uint16{
  1417  			ConntrackOrigSrcPort: flow.Forward.SrcPort,
  1418  			ConntrackOrigDstPort: flow.Forward.DstPort,
  1419  		},
  1420  		protoFilter:unix.IPPROTO_TCP,
  1421  	}
  1422  
  1423  	var match *ConntrackFlow
  1424  	for _, f := range flows {
  1425  		if filter.MatchConntrackFlow(f) {
  1426  			match = f
  1427  			break
  1428  		}
  1429  	}
  1430  
  1431  	if match == nil {
  1432  		t.Fatalf("Didn't find any matching conntrack entries for original flow: %+v\n Filter used: %+v", flow, filter)
  1433  	} else {
  1434  		t.Logf("Found entry in conntrack table matching original flow: %+v labels=%+v", match, match.Labels)
  1435  	}
  1436  
  1437  	// Other fields are implicitly correct due to the filter/match logic.
  1438  	if match.Mark != flow.Mark {
  1439  		t.Logf("Matched kernel entry did not have correct mark. Kernel: %d, Expected: %d", flow.Mark, match.Mark)
  1440  		t.Fail()
  1441  	}
  1442  	checkProtoInfosEqual(t, flow.ProtoInfo, match.ProtoInfo)
  1443  }
  1444  
  1445  // TestConntrackFlowToNlData generates a serialized representation of a
  1446  // ConntrackFlow and runs the resulting bytes back through `parseRawData` to validate.
  1447  func TestConntrackFlowToNlData(t *testing.T) {
  1448  	flowV4 := ConntrackFlow{
  1449  		FamilyType: FAMILY_V4,
  1450  		Forward: IPTuple{
  1451  			SrcIP: net.IP{234,234,234,234},
  1452  			DstIP: net.IP{123,123,123,123},
  1453  			SrcPort: 48385,
  1454  			DstPort: 53,
  1455  			Protocol: unix.IPPROTO_TCP,
  1456  		},
  1457  		Reverse: IPTuple{
  1458  			SrcIP: net.IP{123,123,123,123},
  1459  			DstIP: net.IP{234,234,234,234},
  1460  			SrcPort: 53,
  1461  			DstPort: 48385,
  1462  			Protocol: unix.IPPROTO_TCP,
  1463  		},
  1464  		Mark: 5,
  1465  		TimeOut:    10,
  1466  		ProtoInfo: &ProtoInfoTCP{
  1467  			State: nl.TCP_CONNTRACK_ESTABLISHED,
  1468  		},
  1469  	}
  1470  	flowV6 := ConntrackFlow	{
  1471  		FamilyType: FAMILY_V6,
  1472  		Forward: IPTuple{
  1473  				SrcIP: net.ParseIP("2001:db8::68"),
  1474  				DstIP: net.ParseIP("2001:db9::32"),
  1475  				SrcPort: 48385,
  1476  				DstPort: 53,
  1477  				Protocol: unix.IPPROTO_TCP,
  1478  		},
  1479  		Reverse: IPTuple{
  1480  			SrcIP: net.ParseIP("2001:db9::32"),
  1481  			DstIP: net.ParseIP("2001:db8::68"),
  1482  			SrcPort: 53,
  1483  			DstPort: 48385,
  1484  			Protocol: unix.IPPROTO_TCP,
  1485  		},
  1486  		Mark: 5,
  1487  		TimeOut:    10,
  1488  		ProtoInfo: &ProtoInfoTCP{
  1489  			State: nl.TCP_CONNTRACK_ESTABLISHED,
  1490  		},
  1491  	}
  1492  
  1493  	var bytesV4, bytesV6 []byte
  1494  
  1495  	attrsV4, err := flowV4.toNlData()
  1496  	if err != nil {
  1497  		t.Fatalf("Error converting ConntrackFlow to netlink messages: %s", err)
  1498  	}
  1499  	// Mock nfgenmsg header
  1500  	bytesV4 = append(bytesV4, flowV4.FamilyType,0,0,0)
  1501  	for _, a := range attrsV4 {
  1502  		bytesV4 = append(bytesV4, a.Serialize()...)
  1503  	}
  1504  
  1505  	attrsV6, err := flowV6.toNlData()
  1506  	if err != nil {
  1507  		t.Fatalf("Error converting ConntrackFlow to netlink messages: %s", err)
  1508  	}
  1509  	// Mock nfgenmsg header
  1510  	bytesV6 = append(bytesV6, flowV6.FamilyType,0,0,0)
  1511  	for _, a := range attrsV6 {
  1512  		bytesV6 = append(bytesV6, a.Serialize()...)
  1513  	}
  1514  
  1515  	parsedFlowV4 := parseRawData(bytesV4)
  1516  	checkFlowsEqual(t, &flowV4, parsedFlowV4)
  1517  	checkProtoInfosEqual(t, flowV4.ProtoInfo, parsedFlowV4.ProtoInfo)
  1518  
  1519  	parsedFlowV6 := parseRawData(bytesV6)
  1520  	checkFlowsEqual(t, &flowV6, parsedFlowV6)
  1521  	checkProtoInfosEqual(t, flowV6.ProtoInfo, parsedFlowV6.ProtoInfo)
  1522  }
  1523  
  1524  func checkFlowsEqual(t *testing.T, f1, f2 *ConntrackFlow) {
  1525  	// No point checking timeout as it will differ between reads.
  1526  	// Timestart and timestop may also differ.
  1527  	if f1.FamilyType != f2.FamilyType {
  1528  		t.Logf("Conntrack flow FamilyTypes differ. Tuple1: %d, Tuple2: %d.\n", f1.FamilyType, f2.FamilyType)
  1529  		t.Fail()
  1530  	}
  1531  	if f1.Mark != f2.Mark {
  1532  		t.Logf("Conntrack flow Marks differ. Tuple1: %d, Tuple2: %d.\n", f1.Mark, f2.Mark)
  1533  		t.Fail()
  1534  	}
  1535  	if !tuplesEqual(f1.Forward, f2.Forward) {
  1536  		t.Logf("Forward tuples mismatch. Tuple1 forward flow: %+v, Tuple2 forward flow: %+v.\n", f1.Forward, f2.Forward)
  1537  		t.Fail()
  1538  	}
  1539  	if !tuplesEqual(f1.Reverse, f2.Reverse) {
  1540  		t.Logf("Reverse tuples mismatch. Tuple1 reverse flow: %+v, Tuple2 reverse flow: %+v.\n", f1.Reverse, f2.Reverse)
  1541  		t.Fail()
  1542  	}
  1543  }
  1544  
  1545  func checkProtoInfosEqual(t *testing.T, p1, p2 ProtoInfo) {
  1546  	t.Logf("Checking protoinfo fields equal:\n\t p1: %+v\n\t p2: %+v", p1, p2)
  1547  	if !protoInfosEqual(p1, p2) {
  1548  		t.Logf("Protoinfo structs differ: P1: %+v, P2: %+v", p1, p2)
  1549  		t.Fail()
  1550  	}
  1551  }
  1552  
  1553  func protoInfosEqual(p1, p2 ProtoInfo) bool {
  1554  	if p1 == nil {
  1555  		return p2 == nil
  1556  	} else if p2 != nil {
  1557  		return p1.Protocol() == p2.Protocol()
  1558  	}
  1559  
  1560  	return false
  1561  }
  1562  
  1563  func tuplesEqual(t1, t2 IPTuple) bool {
  1564  	if t1.Bytes != t2.Bytes {
  1565  		return false
  1566  	}
  1567  
  1568  	if !t1.DstIP.Equal(t2.DstIP) {
  1569  		return false
  1570  	}
  1571  
  1572  	if !t1.SrcIP.Equal(t2.SrcIP) {
  1573  		return false
  1574  	}
  1575  
  1576  	if t1.DstPort != t2.DstPort {
  1577  		return false
  1578  	}
  1579  
  1580  	if t1.SrcPort != t2.SrcPort {
  1581  		return false
  1582  	}
  1583  
  1584  	if t1.Packets != t2.Packets {
  1585  		return false
  1586  	}
  1587  
  1588  	if t1.Protocol != t2.Protocol {
  1589  		return false
  1590  	}
  1591  
  1592  	return true
  1593  }