github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/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/sagernet/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  	setUpNetlinkTestWithKModule(t, "nf_conntrack")
   257  	setUpNetlinkTestWithKModule(t, "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  		setUpNetlinkTestWithKModule(t, "nf_conntrack_ipv4")
   266  	}
   267  
   268  	// Creates a new namespace and bring up the loopback interface
   269  	origns, ns, h := nsCreateAndEnter(t)
   270  	defer netns.Set(*origns)
   271  	defer origns.Close()
   272  	defer ns.Close()
   273  	defer runtime.UnlockOSThread()
   274  
   275  	// Create 10 udp flows
   276  	udpFlowCreateProg(t, 5, 5000, "127.0.0.10", 6000)
   277  	udpFlowCreateProg(t, 5, 7000, "127.0.0.20", 8000)
   278  
   279  	// Fetch the conntrack table
   280  	flows, err := h.ConntrackTableList(ConntrackTable, unix.AF_INET)
   281  	CheckErrorFail(t, err)
   282  
   283  	// Check that it is able to find the 5 flows created for each group
   284  	var groupA int
   285  	var groupB int
   286  	for _, flow := range flows {
   287  		if flow.Forward.Protocol == 17 &&
   288  			flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
   289  			flow.Forward.DstPort == 6000 &&
   290  			(flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) {
   291  			groupA++
   292  		}
   293  		if flow.Forward.Protocol == 17 &&
   294  			flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) &&
   295  			flow.Forward.DstPort == 8000 &&
   296  			(flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) {
   297  			groupB++
   298  		}
   299  	}
   300  	if groupA != 5 || groupB != 5 {
   301  		t.Fatalf("Flow creation issue groupA:%d, groupB:%d", groupA, groupB)
   302  	}
   303  
   304  	// Create a filter to erase groupB flows
   305  	filter := &ConntrackFilter{}
   306  	filter.AddIP(ConntrackOrigDstIP, net.ParseIP("127.0.0.20"))
   307  	filter.AddProtocol(17)
   308  	filter.AddPort(ConntrackOrigDstPort, 8000)
   309  
   310  	// Flush entries of groupB
   311  	var deleted uint
   312  	if deleted, err = h.ConntrackDeleteFilter(ConntrackTable, unix.AF_INET, filter); err != nil {
   313  		t.Fatalf("Error during the erase: %s", err)
   314  	}
   315  	if deleted != 5 {
   316  		t.Fatalf("Error deleted a wrong number of flows:%d instead of 5", deleted)
   317  	}
   318  
   319  	// Check again the table to verify that are gone
   320  	flows, err = h.ConntrackTableList(ConntrackTable, unix.AF_INET)
   321  	CheckErrorFail(t, err)
   322  
   323  	// Check if it is able to find the 5 flows of groupA but none of groupB
   324  	groupA = 0
   325  	groupB = 0
   326  	for _, flow := range flows {
   327  		if flow.Forward.Protocol == 17 &&
   328  			flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
   329  			flow.Forward.DstPort == 6000 &&
   330  			(flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) {
   331  			groupA++
   332  		}
   333  		if flow.Forward.Protocol == 17 &&
   334  			flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) &&
   335  			flow.Forward.DstPort == 8000 &&
   336  			(flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) {
   337  			groupB++
   338  		}
   339  	}
   340  	if groupA != 5 || groupB > 0 {
   341  		t.Fatalf("Error during the erase groupA:%d, groupB:%d", groupA, groupB)
   342  	}
   343  
   344  	// Switch back to the original namespace
   345  	netns.Set(*origns)
   346  }
   347  
   348  func TestConntrackFilter(t *testing.T) {
   349  	var flowList []ConntrackFlow
   350  	flowList = append(flowList, ConntrackFlow{
   351  		FamilyType: unix.AF_INET,
   352  		Forward: ipTuple{
   353  			SrcIP:    net.ParseIP("10.0.0.1"),
   354  			DstIP:    net.ParseIP("20.0.0.1"),
   355  			SrcPort:  1000,
   356  			DstPort:  2000,
   357  			Protocol: 17,
   358  		},
   359  		Reverse: ipTuple{
   360  			SrcIP:    net.ParseIP("20.0.0.1"),
   361  			DstIP:    net.ParseIP("192.168.1.1"),
   362  			SrcPort:  2000,
   363  			DstPort:  1000,
   364  			Protocol: 17,
   365  		},
   366  	},
   367  		ConntrackFlow{
   368  			FamilyType: unix.AF_INET,
   369  			Forward: ipTuple{
   370  				SrcIP:    net.ParseIP("10.0.0.2"),
   371  				DstIP:    net.ParseIP("20.0.0.2"),
   372  				SrcPort:  5000,
   373  				DstPort:  6000,
   374  				Protocol: 6,
   375  			},
   376  			Reverse: ipTuple{
   377  				SrcIP:    net.ParseIP("20.0.0.2"),
   378  				DstIP:    net.ParseIP("192.168.1.1"),
   379  				SrcPort:  6000,
   380  				DstPort:  5000,
   381  				Protocol: 6,
   382  			},
   383  		},
   384  		ConntrackFlow{
   385  			FamilyType: unix.AF_INET6,
   386  			Forward: ipTuple{
   387  				SrcIP:    net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
   388  				DstIP:    net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
   389  				SrcPort:  1000,
   390  				DstPort:  2000,
   391  				Protocol: 132,
   392  			},
   393  			Reverse: ipTuple{
   394  				SrcIP:    net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
   395  				DstIP:    net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
   396  				SrcPort:  2000,
   397  				DstPort:  1000,
   398  				Protocol: 132,
   399  			},
   400  		})
   401  
   402  	// Empty filter
   403  	v4Match, v6Match := applyFilter(flowList, &ConntrackFilter{}, &ConntrackFilter{})
   404  	if v4Match > 0 || v6Match > 0 {
   405  		t.Fatalf("Error, empty filter cannot match, v4:%d, v6:%d", v4Match, v6Match)
   406  	}
   407  
   408  	// Filter errors
   409  
   410  	// Adding same attribute should fail
   411  	filter := &ConntrackFilter{}
   412  	err := filter.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1"))
   413  	if err != nil {
   414  		t.Fatalf("Unexpected error: %v", err)
   415  	}
   416  	if err := filter.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1")); err == nil {
   417  		t.Fatalf("Error, it should fail adding same attribute to the filter")
   418  	}
   419  	err = filter.AddProtocol(6)
   420  	if err != nil {
   421  		t.Fatalf("Unexpected error: %v", err)
   422  	}
   423  	if err := filter.AddProtocol(17); err == nil {
   424  		t.Fatalf("Error, it should fail adding same attribute to the filter")
   425  	}
   426  	filter.AddPort(ConntrackOrigSrcPort, 80)
   427  	if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
   428  		t.Fatalf("Error, it should fail adding same attribute to the filter")
   429  	}
   430  
   431  	// Can not add a Port filter without Layer 4 protocol
   432  	filter = &ConntrackFilter{}
   433  	if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
   434  		t.Fatalf("Error, it should fail adding a port filter without a protocol")
   435  	}
   436  
   437  	// Can not add a Port filter if the Layer 4 protocol does not support it
   438  	filter = &ConntrackFilter{}
   439  	err = filter.AddProtocol(47)
   440  	if err != nil {
   441  		t.Fatalf("Unexpected error: %v", err)
   442  	}
   443  	if err := filter.AddPort(ConntrackOrigSrcPort, 80); err == nil {
   444  		t.Fatalf("Error, it should fail adding a port filter with a wrong protocol")
   445  	}
   446  
   447  	// Proto filter
   448  	filterV4 := &ConntrackFilter{}
   449  	err = filterV4.AddProtocol(6)
   450  	if err != nil {
   451  		t.Fatalf("Unexpected error: %v", err)
   452  	}
   453  
   454  	filterV6 := &ConntrackFilter{}
   455  	err = filterV6.AddProtocol(132)
   456  	if err != nil {
   457  		t.Fatalf("Unexpected error: %v", err)
   458  	}
   459  
   460  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   461  	if v4Match != 1 || v6Match != 1 {
   462  		t.Fatalf("Error, there should be only 1 match for TCP:%d, UDP:%d", v4Match, v6Match)
   463  	}
   464  
   465  	// SrcIP filter
   466  	filterV4 = &ConntrackFilter{}
   467  	err = filterV4.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1"))
   468  	if err != nil {
   469  		t.Fatalf("Unexpected error: %v", err)
   470  	}
   471  
   472  	filterV6 = &ConntrackFilter{}
   473  	err = filterV6.AddIP(ConntrackOrigSrcIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"))
   474  	if err != nil {
   475  		t.Fatalf("Unexpected error: %v", err)
   476  	}
   477  
   478  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   479  	if v4Match != 1 || v6Match != 1 {
   480  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   481  	}
   482  
   483  	// DstIp filter
   484  	filterV4 = &ConntrackFilter{}
   485  	err = filterV4.AddIP(ConntrackOrigDstIP, net.ParseIP("20.0.0.1"))
   486  	if err != nil {
   487  		t.Fatalf("Unexpected error: %v", err)
   488  	}
   489  
   490  	filterV6 = &ConntrackFilter{}
   491  	err = filterV6.AddIP(ConntrackOrigDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
   492  	if err != nil {
   493  		t.Fatalf("Unexpected error: %v", err)
   494  	}
   495  
   496  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   497  	if v4Match != 1 || v6Match != 1 {
   498  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   499  	}
   500  
   501  	// SrcIP for NAT
   502  	filterV4 = &ConntrackFilter{}
   503  	err = filterV4.AddIP(ConntrackReplySrcIP, net.ParseIP("20.0.0.1"))
   504  	if err != nil {
   505  		t.Fatalf("Unexpected error: %v", err)
   506  	}
   507  
   508  	filterV6 = &ConntrackFilter{}
   509  	err = filterV6.AddIP(ConntrackReplySrcIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
   510  	if err != nil {
   511  		t.Fatalf("Unexpected error: %v", err)
   512  	}
   513  
   514  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   515  	if v4Match != 1 || v6Match != 1 {
   516  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   517  	}
   518  
   519  	// DstIP for NAT
   520  	filterV4 = &ConntrackFilter{}
   521  	err = filterV4.AddIP(ConntrackReplyDstIP, net.ParseIP("192.168.1.1"))
   522  	if err != nil {
   523  		t.Fatalf("Unexpected error: %v", err)
   524  	}
   525  
   526  	filterV6 = &ConntrackFilter{}
   527  	err = filterV6.AddIP(ConntrackReplyDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
   528  	if err != nil {
   529  		t.Fatalf("Unexpected error: %v", err)
   530  	}
   531  
   532  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   533  	if v4Match != 2 || v6Match != 0 {
   534  		t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
   535  	}
   536  
   537  	// AnyIp for Nat
   538  	filterV4 = &ConntrackFilter{}
   539  	err = filterV4.AddIP(ConntrackReplyAnyIP, net.ParseIP("192.168.1.1"))
   540  	if err != nil {
   541  		t.Fatalf("Unexpected error: %v", err)
   542  	}
   543  
   544  	filterV6 = &ConntrackFilter{}
   545  	err = filterV6.AddIP(ConntrackReplyAnyIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"))
   546  	if err != nil {
   547  		t.Fatalf("Unexpected error: %v", err)
   548  	}
   549  
   550  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   551  	if v4Match != 2 || v6Match != 1 {
   552  		t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
   553  	}
   554  
   555  	// SrcIPNet filter
   556  	filterV4 = &ConntrackFilter{}
   557  	ipNet, err := ParseIPNet("10.0.0.0/12")
   558  	if err != nil {
   559  		t.Fatalf("Unexpected error: %v", err)
   560  	}
   561  	err = filterV4.AddIPNet(ConntrackOrigSrcIP, ipNet)
   562  	if err != nil {
   563  		t.Fatalf("Unexpected error: %v", err)
   564  	}
   565  
   566  	filterV6 = &ConntrackFilter{}
   567  	ipNet, err = ParseIPNet("eeee:eeee:eeee:eeee::/64")
   568  	if err != nil {
   569  		t.Fatalf("Unexpected error: %v", err)
   570  	}
   571  	err = filterV6.AddIPNet(ConntrackOrigSrcIP, ipNet)
   572  	if err != nil {
   573  		t.Fatalf("Unexpected error: %v", err)
   574  	}
   575  
   576  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   577  	if v4Match != 2 || v6Match != 1 {
   578  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   579  	}
   580  
   581  	// DstIpNet filter
   582  	filterV4 = &ConntrackFilter{}
   583  	ipNet, err = ParseIPNet("20.0.0.0/12")
   584  	if err != nil {
   585  		t.Fatalf("Unexpected error: %v", err)
   586  	}
   587  	err = filterV4.AddIPNet(ConntrackOrigDstIP, ipNet)
   588  	if err != nil {
   589  		t.Fatalf("Unexpected error: %v", err)
   590  	}
   591  
   592  	filterV6 = &ConntrackFilter{}
   593  	ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64")
   594  	if err != nil {
   595  		t.Fatalf("Unexpected error: %v", err)
   596  	}
   597  	err = filterV6.AddIPNet(ConntrackOrigDstIP, ipNet)
   598  	if err != nil {
   599  		t.Fatalf("Unexpected error: %v", err)
   600  	}
   601  
   602  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   603  	if v4Match != 2 || v6Match != 1 {
   604  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   605  	}
   606  
   607  	// SrcIPNet for NAT
   608  	filterV4 = &ConntrackFilter{}
   609  	ipNet, err = ParseIPNet("20.0.0.0/12")
   610  	if err != nil {
   611  		t.Fatalf("Unexpected error: %v", err)
   612  	}
   613  	err = filterV4.AddIPNet(ConntrackReplySrcIP, ipNet)
   614  	if err != nil {
   615  		t.Fatalf("Unexpected error: %v", err)
   616  	}
   617  
   618  	filterV6 = &ConntrackFilter{}
   619  	ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64")
   620  	if err != nil {
   621  		t.Fatalf("Unexpected error: %v", err)
   622  	}
   623  	err = filterV6.AddIPNet(ConntrackReplySrcIP, ipNet)
   624  	if err != nil {
   625  		t.Fatalf("Unexpected error: %v", err)
   626  	}
   627  
   628  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   629  	if v4Match != 2 || v6Match != 1 {
   630  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   631  	}
   632  
   633  	// DstIPNet for NAT
   634  	filterV4 = &ConntrackFilter{}
   635  	ipNet, err = ParseIPNet("192.168.0.0/12")
   636  	if err != nil {
   637  		t.Fatalf("Unexpected error: %v", err)
   638  	}
   639  	err = filterV4.AddIPNet(ConntrackReplyDstIP, ipNet)
   640  	if err != nil {
   641  		t.Fatalf("Unexpected error: %v", err)
   642  	}
   643  
   644  	filterV6 = &ConntrackFilter{}
   645  	ipNet, err = ParseIPNet("dddd:dddd:dddd:dddd::/64")
   646  	if err != nil {
   647  		t.Fatalf("Unexpected error: %v", err)
   648  	}
   649  	err = filterV6.AddIPNet(ConntrackReplyDstIP, ipNet)
   650  	if err != nil {
   651  		t.Fatalf("Unexpected error: %v", err)
   652  	}
   653  
   654  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   655  	if v4Match != 2 || v6Match != 0 {
   656  		t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
   657  	}
   658  
   659  	// AnyIpNet for Nat
   660  	filterV4 = &ConntrackFilter{}
   661  	ipNet, err = ParseIPNet("192.168.0.0/12")
   662  	if err != nil {
   663  		t.Fatalf("Unexpected error: %v", err)
   664  	}
   665  	err = filterV4.AddIPNet(ConntrackReplyAnyIP, ipNet)
   666  	if err != nil {
   667  		t.Fatalf("Unexpected error: %v", err)
   668  	}
   669  
   670  	filterV6 = &ConntrackFilter{}
   671  	ipNet, err = ParseIPNet("eeee:eeee:eeee:eeee::/64")
   672  	if err != nil {
   673  		t.Fatalf("Unexpected error: %v", err)
   674  	}
   675  	err = filterV6.AddIPNet(ConntrackReplyAnyIP, ipNet)
   676  	if err != nil {
   677  		t.Fatalf("Unexpected error: %v", err)
   678  	}
   679  
   680  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   681  	if v4Match != 2 || v6Match != 1 {
   682  		t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
   683  	}
   684  	// SrcPort filter
   685  	filterV4 = &ConntrackFilter{}
   686  	err = filterV4.AddProtocol(6)
   687  	if err != nil {
   688  		t.Fatalf("Unexpected error: %v", err)
   689  	}
   690  	err = filterV4.AddPort(ConntrackOrigSrcPort, 5000)
   691  	if err != nil {
   692  		t.Fatalf("Unexpected error: %v", err)
   693  	}
   694  
   695  	filterV6 = &ConntrackFilter{}
   696  	err = filterV6.AddProtocol(132)
   697  	if err != nil {
   698  		t.Fatalf("Unexpected error: %v", err)
   699  	}
   700  	err = filterV6.AddPort(ConntrackOrigSrcPort, 1000)
   701  	if err != nil {
   702  		t.Fatalf("Unexpected error: %v", err)
   703  	}
   704  
   705  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   706  	if v4Match != 1 || v6Match != 1 {
   707  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   708  	}
   709  
   710  	// DstPort filter
   711  	filterV4 = &ConntrackFilter{}
   712  	err = filterV4.AddProtocol(6)
   713  	if err != nil {
   714  		t.Fatalf("Unexpected error: %v", err)
   715  	}
   716  	err = filterV4.AddPort(ConntrackOrigDstPort, 6000)
   717  	if err != nil {
   718  		t.Fatalf("Unexpected error: %v", err)
   719  	}
   720  
   721  	filterV6 = &ConntrackFilter{}
   722  	err = filterV6.AddProtocol(132)
   723  	if err != nil {
   724  		t.Fatalf("Unexpected error: %v", err)
   725  	}
   726  	err = filterV6.AddPort(ConntrackOrigDstPort, 2000)
   727  	if err != nil {
   728  		t.Fatalf("Unexpected error: %v", err)
   729  	}
   730  
   731  	v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
   732  	if v4Match != 1 || v6Match != 1 {
   733  		t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
   734  	}
   735  }
   736  
   737  func TestParseRawData(t *testing.T) {
   738  	if nl.NativeEndian() == binary.BigEndian {
   739  		t.Skip("testdata expect little-endian test executor")
   740  	}
   741  	os.Setenv("TZ", "") // print timestamps in UTC
   742  	tests := []struct {
   743  		testname         string
   744  		rawData          []byte
   745  		expConntrackFlow string
   746  	}{
   747  		{
   748  			testname: "UDP conntrack",
   749  			rawData: []byte{
   750  				/* Nfgenmsg header */
   751  				2, 0, 0, 0,
   752  				/* >> nested CTA_TUPLE_ORIG */
   753  				52, 0, 1, 128,
   754  				/* >>>> nested CTA_TUPLE_IP */
   755  				20, 0, 1, 128,
   756  				/* >>>>>> CTA_IP_V4_SRC */
   757  				8, 0, 1, 0,
   758  				192, 168, 0, 10,
   759  				/* >>>>>> CTA_IP_V4_DST */
   760  				8, 0, 2, 0,
   761  				192, 168, 0, 3,
   762  				/* >>>>>> nested proto info */
   763  				28, 0, 2, 128,
   764  				/* >>>>>>>> CTA_PROTO_NUM */
   765  				5, 0, 1, 0,
   766  				17, 0, 0, 0,
   767  				/* >>>>>>>> CTA_PROTO_SRC_PORT */
   768  				6, 0, 2, 0,
   769  				189, 1, 0, 0,
   770  				/* >>>>>>>> CTA_PROTO_DST_PORT */
   771  				6, 0, 3, 0,
   772  				0, 53, 0, 0,
   773  				/* >> CTA_TUPLE_REPLY */
   774  				52, 0, 2, 128,
   775  				/* >>>> nested CTA_TUPLE_IP */
   776  				20, 0, 1, 128,
   777  				/* >>>>>> CTA_IP_V4_SRC */
   778  				8, 0, 1, 0,
   779  				192, 168, 0, 3,
   780  				/* >>>>>> CTA_IP_V4_DST */
   781  				8, 0, 2, 0,
   782  				192, 168, 0, 10,
   783  				/* >>>>>> nested proto info */
   784  				28, 0, 2, 128,
   785  				/* >>>>>>>> CTA_PROTO_NUM */
   786  				5, 0, 1, 0,
   787  				17, 0, 0, 0,
   788  				/* >>>>>>>> CTA_PROTO_SRC_PORT */
   789  				6, 0, 2, 0,
   790  				0, 53, 0, 0,
   791  				/* >>>>>>>> CTA_PROTO_DST_PORT */
   792  				6, 0, 3, 0,
   793  				189, 1, 0, 0,
   794  				/* >> CTA_STATUS */
   795  				8, 0, 3, 0,
   796  				0, 0, 1, 138,
   797  				/* >> CTA_MARK */
   798  				8, 0, 8, 0,
   799  				0, 0, 0, 5,
   800  				/* >> CTA_ID */
   801  				8, 0, 12, 0,
   802  				81, 172, 253, 151,
   803  				/* >> CTA_USE */
   804  				8, 0, 11, 0,
   805  				0, 0, 0, 1,
   806  				/* >> CTA_TIMEOUT */
   807  				8, 0, 7, 0,
   808  				0, 0, 0, 32,
   809  				/* >> nested CTA_COUNTERS_ORIG */
   810  				28, 0, 9, 128,
   811  				/* >>>> CTA_COUNTERS_PACKETS */
   812  				12, 0, 1, 0,
   813  				0, 0, 0, 0, 0, 0, 0, 1,
   814  				/* >>>> CTA_COUNTERS_BYTES */
   815  				12, 0, 2, 0,
   816  				0, 0, 0, 0, 0, 0, 0, 55,
   817  				/* >> nested CTA_COUNTERS_REPLY */
   818  				28, 0, 10, 128,
   819  				/* >>>> CTA_COUNTERS_PACKETS */
   820  				12, 0, 1, 0,
   821  				0, 0, 0, 0, 0, 0, 0, 1,
   822  				/* >>>> CTA_COUNTERS_BYTES */
   823  				12, 0, 2, 0,
   824  				0, 0, 0, 0, 0, 0, 0, 71,
   825  				/* >> nested CTA_TIMESTAMP */
   826  				16, 0, 20, 128,
   827  				/* >>>> CTA_TIMESTAMP_START */
   828  				12, 0, 1, 0,
   829  				22, 134, 80, 142, 230, 127, 74, 166},
   830  			expConntrackFlow: "udp\t17 src=192.168.0.10 dst=192.168.0.3 sport=48385 dport=53 packets=1 bytes=55\t" +
   831  				"src=192.168.0.3 dst=192.168.0.10 sport=53 dport=48385 packets=1 bytes=71 mark=0x5 " +
   832  				"start=2021-06-07 13:41:30.39632247 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=32(sec)",
   833  		},
   834  		{
   835  			testname: "TCP conntrack",
   836  			rawData: []byte{
   837  				/* Nfgenmsg header */
   838  				2, 0, 0, 0,
   839  				/* >> nested CTA_TUPLE_ORIG */
   840  				52, 0, 1, 128,
   841  				/* >>>> nested CTA_TUPLE_IP */
   842  				20, 0, 1, 128,
   843  				/* >>>>>> CTA_IP_V4_SRC */
   844  				8, 0, 1, 0,
   845  				192, 168, 0, 10,
   846  				/* >>>>>> CTA_IP_V4_DST */
   847  				8, 0, 2, 0,
   848  				192, 168, 77, 73,
   849  				/* >>>>>> nested proto info */
   850  				28, 0, 2, 128,
   851  				/* >>>>>>>> CTA_PROTO_NUM */
   852  				5, 0, 1, 0,
   853  				6, 0, 0, 0,
   854  				/* >>>>>>>> CTA_PROTO_SRC_PORT */
   855  				6, 0, 2, 0,
   856  				166, 129, 0, 0,
   857  				/* >>>>>>>> CTA_PROTO_DST_PORT */
   858  				6, 0, 3, 0,
   859  				13, 5, 0, 0,
   860  				/* >> CTA_TUPLE_REPLY */
   861  				52, 0, 2, 128,
   862  				/* >>>> nested CTA_TUPLE_IP */
   863  				20, 0, 1, 128,
   864  				/* >>>>>> CTA_IP_V4_SRC */
   865  				8, 0, 1, 0,
   866  				192, 168, 77, 73,
   867  				/* >>>>>> CTA_IP_V4_DST */
   868  				8, 0, 2, 0,
   869  				192, 168, 0, 10,
   870  				/* >>>>>> nested proto info */
   871  				28, 0, 2, 128,
   872  				/* >>>>>>>> CTA_PROTO_NUM */
   873  				5, 0, 1, 0,
   874  				6, 0, 0, 0,
   875  				/* >>>>>>>> CTA_PROTO_SRC_PORT */
   876  				6, 0, 2, 0,
   877  				13, 5, 0, 0,
   878  				/* >>>>>>>> CTA_PROTO_DST_PORT */
   879  				6, 0, 3, 0,
   880  				166, 129, 0, 0,
   881  				/* >> CTA_STATUS */
   882  				8, 0, 3, 0,
   883  				0, 0, 1, 142,
   884  				/* >> CTA_MARK */
   885  				8, 0, 8, 0,
   886  				0, 0, 0, 5,
   887  				/* >> CTA_ID */
   888  				8, 0, 12, 0,
   889  				177, 65, 179, 133,
   890  				/* >> CTA_USE */
   891  				8, 0, 11, 0,
   892  				0, 0, 0, 1,
   893  				/* >> CTA_TIMEOUT */
   894  				8, 0, 7, 0,
   895  				0, 0, 0, 152,
   896  				/* >> CTA_PROTOINFO */
   897  				48, 0, 4, 128,
   898  				44, 0, 1, 128,
   899  				5, 0, 1, 0, 8, 0, 0, 0,
   900  				5, 0, 2, 0, 0, 0, 0, 0,
   901  				5, 0, 3, 0, 0, 0, 0, 0,
   902  				6, 0, 4, 0, 39, 0, 0, 0,
   903  				6, 0, 5, 0, 32, 0, 0, 0,
   904  				/* >> nested CTA_COUNTERS_ORIG */
   905  				28, 0, 9, 128,
   906  				/* >>>> CTA_COUNTERS_PACKETS */
   907  				12, 0, 1, 0,
   908  				0, 0, 0, 0, 0, 0, 0, 11,
   909  				/* >>>> CTA_COUNTERS_BYTES */
   910  				12, 0, 2, 0,
   911  				0, 0, 0, 0, 0, 0, 7, 122,
   912  				/* >> nested CTA_COUNTERS_REPLY */
   913  				28, 0, 10, 128,
   914  				/* >>>> CTA_COUNTERS_PACKETS */
   915  				12, 0, 1, 0,
   916  				0, 0, 0, 0, 0, 0, 0, 10,
   917  				/* >>>> CTA_COUNTERS_BYTES */
   918  				12, 0, 2, 0,
   919  				0, 0, 0, 0, 0, 0, 7, 66,
   920  				/* >> nested CTA_TIMESTAMP */
   921  				16, 0, 20, 128,
   922  				/* >>>> CTA_TIMESTAMP_START */
   923  				12, 0, 1, 0,
   924  				22, 134, 80, 175, 134, 10, 182, 221},
   925  			expConntrackFlow: "tcp\t6 src=192.168.0.10 dst=192.168.77.73 sport=42625 dport=3333 packets=11 bytes=1914\t" +
   926  				"src=192.168.77.73 dst=192.168.0.10 sport=3333 dport=42625 packets=10 bytes=1858 mark=0x5 " +
   927  				"start=2021-06-07 13:43:50.511990493 +0000 UTC stop=1970-01-01 00:00:00 +0000 UTC timeout=152(sec)",
   928  		},
   929  	}
   930  
   931  	for _, test := range tests {
   932  		t.Run(test.testname, func(t *testing.T) {
   933  			conntrackFlow := parseRawData(test.rawData)
   934  			if conntrackFlow.String() != test.expConntrackFlow {
   935  				t.Errorf("expected conntrack flow:\n\t%q\ngot conntrack flow:\n\t%q",
   936  					test.expConntrackFlow, conntrackFlow)
   937  			}
   938  		})
   939  	}
   940  }