github.com/cilium/cilium@v1.16.2/pkg/hubble/parser/threefour/parser_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Hubble
     3  
     4  package threefour
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/binary"
     9  	"fmt"
    10  	"io"
    11  	"net"
    12  	"net/netip"
    13  	"testing"
    14  
    15  	"github.com/google/go-cmp/cmp"
    16  	"github.com/google/gopacket"
    17  	"github.com/google/gopacket/layers"
    18  	"github.com/sirupsen/logrus"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  	"google.golang.org/protobuf/testing/protocmp"
    22  
    23  	flowpb "github.com/cilium/cilium/api/v1/flow"
    24  	"github.com/cilium/cilium/pkg/byteorder"
    25  	"github.com/cilium/cilium/pkg/hubble/parser/common"
    26  	"github.com/cilium/cilium/pkg/hubble/parser/errors"
    27  	"github.com/cilium/cilium/pkg/hubble/parser/getters"
    28  	"github.com/cilium/cilium/pkg/hubble/testutils"
    29  	"github.com/cilium/cilium/pkg/identity"
    30  	"github.com/cilium/cilium/pkg/ipcache"
    31  	"github.com/cilium/cilium/pkg/k8s/apis/cilium.io/utils"
    32  	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1"
    33  	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    34  	"github.com/cilium/cilium/pkg/labels"
    35  	"github.com/cilium/cilium/pkg/monitor"
    36  	monitorAPI "github.com/cilium/cilium/pkg/monitor/api"
    37  	"github.com/cilium/cilium/pkg/policy/trafficdirection"
    38  	policyTypes "github.com/cilium/cilium/pkg/policy/types"
    39  	"github.com/cilium/cilium/pkg/source"
    40  	"github.com/cilium/cilium/pkg/types"
    41  	"github.com/cilium/cilium/pkg/u8proto"
    42  )
    43  
    44  var log *logrus.Logger
    45  
    46  func init() {
    47  	log = logrus.New()
    48  	log.SetOutput(io.Discard)
    49  }
    50  
    51  func TestL34DecodeEmpty(t *testing.T) {
    52  	parser, err := New(log, &testutils.NoopEndpointGetter, &testutils.NoopIdentityGetter,
    53  		&testutils.NoopDNSGetter, &testutils.NoopIPGetter, &testutils.NoopServiceGetter,
    54  		&testutils.NoopLinkGetter)
    55  	require.NoError(t, err)
    56  
    57  	var d []byte
    58  	f := &flowpb.Flow{}
    59  	err = parser.Decode(d, f)
    60  	assert.Equal(t, err, errors.ErrEmptyData)
    61  }
    62  
    63  func TestL34Decode(t *testing.T) {
    64  	//SOURCE          					DESTINATION           TYPE   SUMMARY
    65  	//192.168.60.11:6443(sun-sr-https)  10.16.236.178:54222   L3/4   TCP Flags: ACK
    66  	d := []byte{
    67  		4, 7, 0, 0, 7, 124, 26, 57, 66, 0, 0, 0, 66, 0, 0, 0, // NOTIFY_CAPTURE_HDR
    68  		1, 0, 0, 0, // source labels
    69  		0, 0, 0, 0, // destination labels
    70  		0, 0, // destination ID
    71  		0x81,       // "established" trace reason with the encrypt bit set
    72  		0,          // flags
    73  		0, 0, 0, 0, // ifindex
    74  		246, 141, 178, 45, 33, 217, 246, 141, 178,
    75  		45, 33, 217, 8, 0, 69, 0, 0, 52, 234, 28, 64, 0, 64, 6, 120, 49, 192,
    76  		168, 60, 11, 10, 16, 236, 178, 25, 43, 211, 206, 42, 239, 210, 28, 180,
    77  		152, 129, 103, 128, 16, 1, 152, 216, 156, 0, 0, 1, 1, 8, 10, 0, 90, 176,
    78  		98, 0, 90, 176, 97, 0, 0}
    79  
    80  	endpointGetter := &testutils.FakeEndpointGetter{
    81  		OnGetEndpointInfo: func(ip netip.Addr) (endpoint getters.EndpointInfo, ok bool) {
    82  			if ip == netip.MustParseAddr("10.16.236.178") {
    83  				return &testutils.FakeEndpointInfo{
    84  					ID:           1234,
    85  					Identity:     5678,
    86  					PodName:      "pod-10.16.236.178",
    87  					PodNamespace: "default",
    88  					Pod: &slim_corev1.Pod{
    89  						ObjectMeta: slim_metav1.ObjectMeta{
    90  							OwnerReferences: []slim_metav1.OwnerReference{
    91  								{
    92  									Kind: "ReplicaSet",
    93  									Name: "pod",
    94  								},
    95  							},
    96  						},
    97  					},
    98  				}, true
    99  			}
   100  			return nil, false
   101  		},
   102  	}
   103  	dnsGetter := &testutils.FakeFQDNCache{
   104  		OnGetNamesOf: func(epID uint32, ip netip.Addr) (names []string) {
   105  			if epID == 1234 {
   106  				switch {
   107  				case ip.String() == "192.168.60.11":
   108  					return []string{"host-192.168.60.11"}
   109  				}
   110  			}
   111  			return nil
   112  		},
   113  	}
   114  	ipGetter := &testutils.FakeIPGetter{
   115  		OnGetK8sMetadata: func(ip netip.Addr) *ipcache.K8sMetadata {
   116  			if ip == netip.MustParseAddr("192.168.60.11") {
   117  				return &ipcache.K8sMetadata{
   118  					Namespace: "remote",
   119  					PodName:   "pod-192.168.60.11",
   120  				}
   121  			}
   122  			return nil
   123  		},
   124  		OnLookupSecIDByIP: func(ip netip.Addr) (ipcache.Identity, bool) {
   125  			// pretend IP belongs to a pod on a remote node
   126  			if ip == netip.MustParseAddr("192.168.60.11") {
   127  				// This numeric identity will be ignored because the above
   128  				// TraceNotify event already contains the source identity
   129  				return ipcache.Identity{
   130  					ID:     1234,
   131  					Source: source.Unspec,
   132  				}, true
   133  			}
   134  			return ipcache.Identity{}, false
   135  		},
   136  	}
   137  	serviceGetter := &testutils.FakeServiceGetter{
   138  		OnGetServiceByAddr: func(ip netip.Addr, port uint16) *flowpb.Service {
   139  			if ip == netip.MustParseAddr("192.168.60.11") && (port == 6443) {
   140  				return &flowpb.Service{
   141  					Name:      "service-1234",
   142  					Namespace: "remote",
   143  				}
   144  			}
   145  			if ip == netip.MustParseAddr("10.16.236.178") && (port == 54222) {
   146  				return &flowpb.Service{
   147  					Name:      "service-4321",
   148  					Namespace: "default",
   149  				}
   150  			}
   151  			return nil
   152  		},
   153  	}
   154  	identityCache := &testutils.NoopIdentityGetter
   155  	parser, err := New(log, endpointGetter, identityCache, dnsGetter, ipGetter, serviceGetter, &testutils.NoopLinkGetter)
   156  	require.NoError(t, err)
   157  
   158  	f := &flowpb.Flow{}
   159  	err = parser.Decode(d, f)
   160  	require.NoError(t, err)
   161  
   162  	assert.Equal(t, []string{"host-192.168.60.11"}, f.GetSourceNames())
   163  	assert.Equal(t, "192.168.60.11", f.GetIP().GetSource())
   164  	assert.Equal(t, "", f.GetIP().GetSourceXlated())
   165  	assert.Equal(t, flowpb.TraceReason_ESTABLISHED, f.GetTraceReason())
   166  	assert.True(t, f.GetIP().GetEncrypted())
   167  	assert.Equal(t, uint32(6443), f.L4.GetTCP().GetSourcePort())
   168  	assert.Equal(t, "pod-192.168.60.11", f.GetSource().GetPodName())
   169  	assert.Equal(t, "remote", f.GetSource().GetNamespace())
   170  	assert.Equal(t, "service-1234", f.GetSourceService().GetName())
   171  	assert.Equal(t, "remote", f.GetSourceService().GetNamespace())
   172  	assert.Equal(t, uint32(1), f.GetSource().GetIdentity())
   173  
   174  	assert.Equal(t, []string(nil), f.GetDestinationNames())
   175  	assert.Equal(t, "10.16.236.178", f.GetIP().GetDestination())
   176  	assert.Equal(t, uint32(54222), f.L4.GetTCP().GetDestinationPort())
   177  	assert.Equal(t, "pod-10.16.236.178", f.GetDestination().GetPodName())
   178  	assert.Equal(t, "default", f.GetDestination().GetNamespace())
   179  	assert.Equal(t, "service-4321", f.GetDestinationService().GetName())
   180  	assert.Equal(t, "default", f.GetDestinationService().GetNamespace())
   181  	assert.Equal(t, uint32(5678), f.GetDestination().GetIdentity())
   182  
   183  	assert.Equal(t, int32(monitorAPI.MessageTypeTrace), f.GetEventType().GetType())
   184  	assert.Equal(t, int32(monitorAPI.TraceFromHost), f.GetEventType().GetSubType())
   185  	assert.Equal(t, flowpb.Verdict_FORWARDED, f.GetVerdict())
   186  	assert.Equal(t, &flowpb.TCPFlags{ACK: true}, f.L4.GetTCP().GetFlags())
   187  
   188  	assert.Equal(t, flowpb.TraceObservationPoint_FROM_HOST, f.GetTraceObservationPoint())
   189  
   190  	nilParser, err := New(log, nil, nil, nil, nil, nil, nil)
   191  	require.NoError(t, err)
   192  	err = nilParser.Decode(d, f)
   193  	require.NoError(t, err)
   194  
   195  	// ICMP packet so no ports until that support is merged into master
   196  	//
   197  	//SOURCE              DESTINATION          TYPE   SUMMARY
   198  	//ff02::1:ff00:b3e5   f00d::a10:0:0:9195   L3/4
   199  	d2 := []byte{
   200  		4, 5, 168, 11, 95, 22, 242, 184, 86, 0, 0, 0, 86, 0, 0, 0, 104, 0, 0, 0,
   201  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 51, 255, 0, 179, 229, 18, 145,
   202  		6, 226, 34, 26, 134, 221, 96, 0, 0, 0, 0, 32, 58, 255, 255, 2, 0, 0, 0,
   203  		0, 0, 0, 0, 0, 0, 1, 255, 0, 179, 229, 240, 13, 0, 0, 0, 0, 0, 0, 10,
   204  		16, 0, 0, 0, 0, 145, 149, 135, 0, 80, 117, 0, 0, 0, 0, 240, 13, 0, 0, 0,
   205  		0, 0, 0, 10, 16, 0, 0, 0, 0, 179, 229, 1, 1, 18, 145, 6, 226, 34, 26, 0,
   206  		0, 0, 0, 0, 0}
   207  
   208  	endpointGetter = &testutils.FakeEndpointGetter{
   209  		OnGetEndpointInfo: func(ip netip.Addr) (endpoint getters.EndpointInfo, ok bool) {
   210  			if ip == netip.MustParseAddr("ff02::1:ff00:b3e5") {
   211  				return &testutils.FakeEndpointInfo{
   212  					ID: 1234,
   213  				}, true
   214  			}
   215  			return nil, false
   216  		},
   217  	}
   218  	dnsGetter = &testutils.FakeFQDNCache{
   219  		OnGetNamesOf: func(epID uint32, ip netip.Addr) (names []string) {
   220  			if epID == 1234 {
   221  				switch {
   222  				case ip.String() == "f00d::a10:0:0:9195":
   223  					return []string{"host-f00d::a10:0:0:9195"}
   224  				}
   225  			}
   226  			return nil
   227  		},
   228  	}
   229  	ipGetter = &testutils.NoopIPGetter
   230  	serviceGetter = &testutils.NoopServiceGetter
   231  	parser, err = New(log, endpointGetter, identityCache, dnsGetter, ipGetter, serviceGetter, &testutils.NoopLinkGetter)
   232  	require.NoError(t, err)
   233  
   234  	err = parser.Decode(d2, f)
   235  	require.NoError(t, err)
   236  
   237  	// second packet is ICMPv6 and the flags should be totally wiped out
   238  	assert.Equal(t, []string(nil), f.GetSourceNames())
   239  	assert.Equal(t, "ff02::1:ff00:b3e5", f.GetIP().GetSource())
   240  	assert.Equal(t, "", f.GetIP().GetSourceXlated())
   241  	assert.Equal(t, &flowpb.ICMPv6{Type: 135}, f.L4.GetICMPv6())
   242  	assert.Equal(t, "", f.GetSource().GetPodName())
   243  	assert.Equal(t, "", f.GetSource().GetNamespace())
   244  
   245  	assert.Equal(t, []string{"host-f00d::a10:0:0:9195"}, f.GetDestinationNames())
   246  	assert.Equal(t, "f00d::a10:0:0:9195", f.GetIP().GetDestination())
   247  	assert.Equal(t, "", f.GetDestination().GetPodName())
   248  	assert.Equal(t, "", f.GetDestination().GetNamespace())
   249  
   250  	assert.Equal(t, int32(monitorAPI.MessageTypeTrace), f.GetEventType().GetType())
   251  	assert.Equal(t, int32(monitorAPI.TraceFromLxc), f.GetEventType().GetSubType())
   252  	assert.Equal(t, flowpb.Verdict_FORWARDED, f.GetVerdict())
   253  	assert.Equal(t, (*flowpb.TCPFlags)(nil), f.L4.GetTCP().GetFlags())
   254  
   255  	assert.Equal(t, flowpb.TraceObservationPoint_FROM_ENDPOINT, f.GetTraceObservationPoint())
   256  
   257  	err = nilParser.Decode(d, f)
   258  	require.NoError(t, err)
   259  }
   260  
   261  func BenchmarkL34Decode(b *testing.B) {
   262  	d := []byte{4, 7, 0, 0, 7, 124, 26, 57, 66, 0, 0, 0, 66, 0, 0, 0, 1, 0, 0, 0,
   263  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 141, 178, 45, 33, 217, 246, 141,
   264  		178, 45, 33, 217, 8, 0, 69, 0, 0, 52, 234, 28, 64, 0, 64, 6, 120, 49, 192,
   265  		168, 60, 11, 10, 16, 236, 178, 25, 43, 211, 206, 42, 239, 210, 28, 180, 152,
   266  		129, 103, 128, 16, 1, 152, 216, 156, 0, 0, 1, 1, 8, 10, 0, 90, 176, 98, 0,
   267  		90, 176, 97, 0, 0}
   268  
   269  	endpointGetter := &testutils.NoopEndpointGetter
   270  	dnsGetter := &testutils.NoopDNSGetter
   271  	ipGetter := &testutils.NoopIPGetter
   272  	serviceGetter := &testutils.NoopServiceGetter
   273  	identityCache := &testutils.NoopIdentityGetter
   274  	parser, err := New(log, endpointGetter, identityCache, dnsGetter, ipGetter, serviceGetter, &testutils.NoopLinkGetter)
   275  	require.NoError(b, err)
   276  
   277  	f := &flowpb.Flow{}
   278  	b.ReportAllocs()
   279  	b.ResetTimer()
   280  	for i := 0; i < b.N; i++ {
   281  		_ = parser.Decode(d, f)
   282  	}
   283  }
   284  
   285  func TestDecodeTraceNotify(t *testing.T) {
   286  	buf := &bytes.Buffer{}
   287  	tn := monitor.TraceNotifyV0{
   288  		Type:     byte(monitorAPI.MessageTypeTrace),
   289  		SrcLabel: 123,
   290  		DstLabel: 456,
   291  	}
   292  	err := binary.Write(buf, byteorder.Native, &tn)
   293  	require.NoError(t, err)
   294  	buffer := gopacket.NewSerializeBuffer()
   295  	err = gopacket.SerializeLayers(buffer,
   296  		gopacket.SerializeOptions{},
   297  		&layers.Ethernet{
   298  			SrcMAC: net.HardwareAddr{1, 2, 3, 4, 5, 6},
   299  			DstMAC: net.HardwareAddr{1, 2, 3, 4, 5, 6},
   300  		},
   301  		&layers.IPv4{
   302  			SrcIP: net.IPv4(1, 2, 3, 4),
   303  			DstIP: net.IPv4(1, 2, 3, 4),
   304  		},
   305  	)
   306  	require.NoError(t, err)
   307  	buf.Write(buffer.Bytes())
   308  	require.NoError(t, err)
   309  	identityGetter := &testutils.FakeIdentityGetter{OnGetIdentity: func(securityIdentity uint32) (*identity.Identity, error) {
   310  		if securityIdentity == uint32(tn.SrcLabel) {
   311  			return &identity.Identity{Labels: labels.NewLabelsFromModel([]string{"k8s:src=label", "k8s:io.cilium.k8s.policy.cluster=cluster-name"})}, nil
   312  		} else if securityIdentity == uint32(tn.DstLabel) {
   313  			return &identity.Identity{Labels: labels.NewLabelsFromModel([]string{"k8s:dst=label", "k8s:io.cilium.k8s.policy.cluster=cluster-name"})}, nil
   314  		}
   315  		return nil, fmt.Errorf("identity not found for %d", securityIdentity)
   316  	}}
   317  
   318  	parser, err := New(log, &testutils.NoopEndpointGetter, identityGetter, &testutils.NoopDNSGetter, &testutils.NoopIPGetter, &testutils.NoopServiceGetter, &testutils.NoopLinkGetter)
   319  	require.NoError(t, err)
   320  
   321  	f := &flowpb.Flow{}
   322  	err = parser.Decode(buf.Bytes(), f)
   323  	require.NoError(t, err)
   324  	assert.Equal(t, []string{"k8s:io.cilium.k8s.policy.cluster=cluster-name", "k8s:src=label"}, f.GetSource().GetLabels())
   325  	assert.Equal(t, []string{"k8s:dst=label", "k8s:io.cilium.k8s.policy.cluster=cluster-name"}, f.GetDestination().GetLabels())
   326  	assert.Equal(t, "cluster-name", f.GetSource().GetClusterName())
   327  	assert.Equal(t, "cluster-name", f.GetDestination().GetClusterName())
   328  }
   329  
   330  func TestDecodeDropNotify(t *testing.T) {
   331  	buf := &bytes.Buffer{}
   332  	dn := monitor.DropNotify{
   333  		Type:     byte(monitorAPI.MessageTypeDrop),
   334  		SrcLabel: 123,
   335  		DstLabel: 456,
   336  	}
   337  	err := binary.Write(buf, byteorder.Native, &dn)
   338  	require.NoError(t, err)
   339  	buffer := gopacket.NewSerializeBuffer()
   340  	err = gopacket.SerializeLayers(buffer,
   341  		gopacket.SerializeOptions{},
   342  		&layers.Ethernet{
   343  			SrcMAC: net.HardwareAddr{1, 2, 3, 4, 5, 6},
   344  			DstMAC: net.HardwareAddr{1, 2, 3, 4, 5, 6},
   345  		},
   346  		&layers.IPv4{
   347  			SrcIP: net.IPv4(1, 2, 3, 4),
   348  			DstIP: net.IPv4(1, 2, 3, 4),
   349  		},
   350  	)
   351  	require.NoError(t, err)
   352  	buf.Write(buffer.Bytes())
   353  	require.NoError(t, err)
   354  	identityGetter := &testutils.FakeIdentityGetter{
   355  		OnGetIdentity: func(securityIdentity uint32) (*identity.Identity, error) {
   356  			if securityIdentity == uint32(dn.SrcLabel) {
   357  				return &identity.Identity{Labels: labels.NewLabelsFromModel([]string{"k8s:src=label"})}, nil
   358  			} else if securityIdentity == uint32(dn.DstLabel) {
   359  				return &identity.Identity{Labels: labels.NewLabelsFromModel([]string{"k8s:dst=label"})}, nil
   360  			}
   361  			return nil, fmt.Errorf("identity not found for %d", securityIdentity)
   362  		},
   363  	}
   364  
   365  	parser, err := New(log, &testutils.NoopEndpointGetter, identityGetter, &testutils.NoopDNSGetter, &testutils.NoopIPGetter, &testutils.NoopServiceGetter, &testutils.NoopLinkGetter)
   366  	require.NoError(t, err)
   367  
   368  	f := &flowpb.Flow{}
   369  	err = parser.Decode(buf.Bytes(), f)
   370  	require.NoError(t, err)
   371  	assert.Equal(t, []string{"k8s:src=label"}, f.GetSource().GetLabels())
   372  	assert.Equal(t, []string{"k8s:dst=label"}, f.GetDestination().GetLabels())
   373  }
   374  
   375  func TestDecodePolicyVerdictNotify(t *testing.T) {
   376  	localIP := "1.2.3.4"
   377  	localID := uint64(12)
   378  	localIdentity := uint64(1234)
   379  	remoteIP := "5.6.7.8"
   380  	remoteIdentity := uint64(5678)
   381  	dstPort := uint32(443)
   382  
   383  	identityGetter := &testutils.FakeIdentityGetter{
   384  		OnGetIdentity: func(securityIdentity uint32) (*identity.Identity, error) {
   385  			if securityIdentity == uint32(remoteIdentity) {
   386  				return &identity.Identity{ID: identity.NumericIdentity(remoteIdentity), Labels: labels.NewLabelsFromModel([]string{"k8s:dst=label"})}, nil
   387  			}
   388  			return nil, fmt.Errorf("identity not found for %d", securityIdentity)
   389  		},
   390  	}
   391  
   392  	policyLabel := utils.GetPolicyLabels("foo-namespace", "web-policy", "1234-5678", utils.ResourceTypeCiliumNetworkPolicy)
   393  	policyKey := policyTypes.Key{
   394  		Identity:         uint32(remoteIdentity),
   395  		DestPort:         uint16(dstPort),
   396  		Nexthdr:          uint8(u8proto.TCP),
   397  		TrafficDirection: trafficdirection.Egress.Uint8(),
   398  	}
   399  	ep := &testutils.FakeEndpointInfo{
   400  		ID:           localID,
   401  		Identity:     identity.NumericIdentity(localIdentity),
   402  		IPv4:         net.ParseIP(localIP),
   403  		PodName:      "xwing",
   404  		PodNamespace: "default",
   405  		Labels:       []string{"a", "b", "c"},
   406  		PolicyMap: map[policyTypes.Key]labels.LabelArrayList{
   407  			policyKey: {policyLabel},
   408  		},
   409  		PolicyRevision: 1,
   410  	}
   411  	endpointGetter := &testutils.FakeEndpointGetter{
   412  		OnGetEndpointInfo: func(ip netip.Addr) (endpoint getters.EndpointInfo, ok bool) {
   413  			if ip == netip.MustParseAddr(localIP) {
   414  				return ep, true
   415  			}
   416  			return nil, false
   417  		},
   418  		OnGetEndpointInfoByID: func(id uint16) (endpoint getters.EndpointInfo, ok bool) {
   419  			if uint64(id) == ep.ID {
   420  				return ep, true
   421  			}
   422  			return nil, false
   423  		},
   424  	}
   425  
   426  	parser, err := New(log, endpointGetter, identityGetter, &testutils.NoopDNSGetter, &testutils.NoopIPGetter, &testutils.NoopServiceGetter, &testutils.NoopLinkGetter)
   427  	require.NoError(t, err)
   428  
   429  	// PolicyVerdictNotify for forwarded flow
   430  	var flags uint8
   431  	flags |= monitorAPI.PolicyEgress
   432  	flags |= monitorAPI.PolicyMatchL3L4 << monitor.PolicyVerdictNotifyFlagMatchTypeBitOffset
   433  	pvn := monitor.PolicyVerdictNotify{
   434  		Type:        byte(monitorAPI.MessageTypePolicyVerdict),
   435  		SubType:     0,
   436  		Flags:       flags,
   437  		RemoteLabel: identity.NumericIdentity(remoteIdentity),
   438  		Verdict:     0, // CTX_ACT_OK
   439  		Source:      uint16(localID),
   440  	}
   441  	eth := layers.Ethernet{
   442  		EthernetType: layers.EthernetTypeIPv4,
   443  		SrcMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
   444  		DstMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
   445  	}
   446  	ip := layers.IPv4{
   447  		SrcIP:    net.ParseIP(localIP),
   448  		DstIP:    net.ParseIP(remoteIP),
   449  		Protocol: layers.IPProtocolTCP,
   450  	}
   451  	tcp := layers.TCP{
   452  		DstPort: layers.TCPPort(dstPort),
   453  	}
   454  	data, err := testutils.CreateL3L4Payload(pvn, &eth, &ip, &tcp)
   455  	require.NoError(t, err)
   456  
   457  	f := &flowpb.Flow{}
   458  	err = parser.Decode(data, f)
   459  	require.NoError(t, err)
   460  
   461  	assert.Equal(t, int32(monitorAPI.MessageTypePolicyVerdict), f.GetEventType().GetType())
   462  	assert.Equal(t, flowpb.TrafficDirection_EGRESS, f.GetTrafficDirection())
   463  	assert.Equal(t, uint32(monitorAPI.PolicyMatchL3L4), f.GetPolicyMatchType())
   464  	assert.Equal(t, flowpb.Verdict_FORWARDED, f.GetVerdict())
   465  	assert.Equal(t, []string{"k8s:dst=label"}, f.GetDestination().GetLabels())
   466  
   467  	expectedPolicy := []*flowpb.Policy{
   468  		{
   469  			Name:      "web-policy",
   470  			Namespace: "foo-namespace",
   471  			Labels: []string{
   472  				"k8s:io.cilium.k8s.policy.derived-from=CiliumNetworkPolicy",
   473  				"k8s:io.cilium.k8s.policy.name=web-policy",
   474  				"k8s:io.cilium.k8s.policy.namespace=foo-namespace",
   475  				"k8s:io.cilium.k8s.policy.uid=1234-5678",
   476  			},
   477  			Revision: 1,
   478  		},
   479  	}
   480  	if diff := cmp.Diff(expectedPolicy, f.GetEgressAllowedBy(), protocmp.Transform()); diff != "" {
   481  		t.Errorf("not equal (-want +got):\n%s", diff)
   482  	}
   483  
   484  	// PolicyVerdictNotify for dropped flow
   485  	flags = monitorAPI.PolicyIngress
   486  	pvn = monitor.PolicyVerdictNotify{
   487  		Type:        byte(monitorAPI.MessageTypePolicyVerdict),
   488  		SubType:     0,
   489  		Flags:       flags,
   490  		RemoteLabel: identity.NumericIdentity(remoteIdentity),
   491  		Verdict:     -151, // drop reason: Stale or unroutable IP
   492  	}
   493  	data, err = testutils.CreateL3L4Payload(pvn)
   494  	require.NoError(t, err)
   495  
   496  	f.Reset()
   497  	err = parser.Decode(data, f)
   498  	require.NoError(t, err)
   499  
   500  	assert.Equal(t, int32(monitorAPI.MessageTypePolicyVerdict), f.GetEventType().GetType())
   501  	assert.Equal(t, flowpb.TrafficDirection_INGRESS, f.GetTrafficDirection())
   502  	assert.Equal(t, uint32(151), f.GetDropReason())
   503  	assert.Equal(t, flowpb.DropReason(151), f.GetDropReasonDesc())
   504  	assert.Equal(t, flowpb.Verdict_DROPPED, f.GetVerdict())
   505  	assert.Equal(t, []string{"k8s:dst=label"}, f.GetSource().GetLabels())
   506  }
   507  
   508  func TestDecodeDropReason(t *testing.T) {
   509  	reason := uint8(130)
   510  	dn := monitor.DropNotify{
   511  		Type:    byte(monitorAPI.MessageTypeDrop),
   512  		SubType: reason,
   513  	}
   514  	data, err := testutils.CreateL3L4Payload(dn)
   515  	require.NoError(t, err)
   516  
   517  	parser, err := New(log, nil, nil, nil, nil, nil, nil)
   518  	require.NoError(t, err)
   519  
   520  	f := &flowpb.Flow{}
   521  	err = parser.Decode(data, f)
   522  	require.NoError(t, err)
   523  
   524  	assert.Equal(t, uint32(reason), f.GetDropReason())
   525  	assert.Equal(t, flowpb.DropReason(reason), f.GetDropReasonDesc())
   526  }
   527  
   528  func TestDecodeTraceReason(t *testing.T) {
   529  	parser, err := New(log, nil, nil, nil, nil, nil, nil)
   530  	require.NoError(t, err)
   531  	parseFlow := func(event interface{}, srcIPv4, dstIPv4 string) *flowpb.Flow {
   532  		data, err := testutils.CreateL3L4Payload(event,
   533  			&layers.Ethernet{
   534  				SrcMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
   535  				DstMAC:       net.HardwareAddr{7, 8, 9, 0, 1, 2},
   536  				EthernetType: layers.EthernetTypeIPv4,
   537  			},
   538  			&layers.IPv4{SrcIP: net.ParseIP(srcIPv4), DstIP: net.ParseIP(dstIPv4)})
   539  		require.NoError(t, err)
   540  		f := &flowpb.Flow{}
   541  		err = parser.Decode(data, f)
   542  		require.NoError(t, err)
   543  		return f
   544  	}
   545  
   546  	var tt = []struct {
   547  		name   string
   548  		reason uint8
   549  		want   flowpb.TraceReason
   550  	}{
   551  		{
   552  			name:   "unknown",
   553  			reason: monitor.TraceReasonUnknown,
   554  			want:   flowpb.TraceReason_TRACE_REASON_UNKNOWN,
   555  		},
   556  		{
   557  			name:   "new",
   558  			reason: monitor.TraceReasonPolicy,
   559  			want:   flowpb.TraceReason_NEW,
   560  		},
   561  		{
   562  			name:   "established",
   563  			reason: monitor.TraceReasonCtEstablished,
   564  			want:   flowpb.TraceReason_ESTABLISHED,
   565  		},
   566  		{
   567  			name:   "reply",
   568  			reason: monitor.TraceReasonCtReply,
   569  			want:   flowpb.TraceReason_REPLY,
   570  		},
   571  		{
   572  			name:   "related",
   573  			reason: monitor.TraceReasonCtRelated,
   574  			want:   flowpb.TraceReason_RELATED,
   575  		},
   576  		{
   577  			// "reopened" is deprecated, as the datapath no longer returns it
   578  			name:   "reopened",
   579  			reason: monitor.TraceReasonCtDeprecatedReopened,
   580  			want:   flowpb.TraceReason_REOPENED,
   581  		},
   582  		{
   583  			name:   "srv6-encap",
   584  			reason: monitor.TraceReasonSRv6Encap,
   585  			want:   flowpb.TraceReason_SRV6_ENCAP,
   586  		},
   587  		{
   588  			name:   "srv6-decap",
   589  			reason: monitor.TraceReasonSRv6Decap,
   590  			want:   flowpb.TraceReason_SRV6_DECAP,
   591  		},
   592  		{
   593  			name:   "encrypt-overlay",
   594  			reason: monitor.TraceReasonEncryptOverlay,
   595  			want:   flowpb.TraceReason_ENCRYPT_OVERLAY,
   596  		},
   597  	}
   598  
   599  	for _, tc := range tt {
   600  		t.Run(tc.name, func(t *testing.T) {
   601  			tn := monitor.TraceNotifyV0{
   602  				Type:   byte(monitorAPI.MessageTypeTrace),
   603  				Reason: tc.reason,
   604  			}
   605  			f := parseFlow(tn, "1.2.3.4", "5.6.7.8")
   606  			assert.Equal(t, tc.want, f.GetTraceReason())
   607  			assert.False(t, f.GetIP().GetEncrypted())
   608  		})
   609  		t.Run(tc.name+" encrypted", func(t *testing.T) {
   610  			tn := monitor.TraceNotifyV0{
   611  				Type:   byte(monitorAPI.MessageTypeTrace),
   612  				Reason: tc.reason | monitor.TraceReasonEncryptMask,
   613  			}
   614  			f := parseFlow(tn, "1.2.3.4", "5.6.7.8")
   615  			assert.Equal(t, tc.want, f.GetTraceReason())
   616  			assert.True(t, f.GetIP().GetEncrypted())
   617  		})
   618  	}
   619  }
   620  
   621  func TestDecodeLocalIdentity(t *testing.T) {
   622  	tn := monitor.TraceNotifyV0{
   623  		Type:     byte(monitorAPI.MessageTypeTrace),
   624  		SrcLabel: 123 | identity.IdentityScopeLocal,
   625  		DstLabel: 456 | identity.IdentityScopeLocal,
   626  	}
   627  	data, err := testutils.CreateL3L4Payload(tn)
   628  	require.NoError(t, err)
   629  	identityGetter := &testutils.FakeIdentityGetter{
   630  		OnGetIdentity: func(securityIdentity uint32) (*identity.Identity, error) {
   631  			return &identity.Identity{Labels: labels.NewLabelsFromModel([]string{"unspec:some=label", "cidr:1.2.3.4/12", "cidr:1.2.3.4/11"})}, nil
   632  		},
   633  	}
   634  
   635  	parser, err := New(log, nil, identityGetter, nil, nil, nil, nil)
   636  	require.NoError(t, err)
   637  
   638  	f := &flowpb.Flow{}
   639  	err = parser.Decode(data, f)
   640  	require.NoError(t, err)
   641  
   642  	assert.Equal(t, []string{"cidr:1.2.3.4/12", "unspec:some=label"}, f.GetSource().GetLabels())
   643  	assert.Equal(t, []string{"cidr:1.2.3.4/12", "unspec:some=label"}, f.GetDestination().GetLabels())
   644  }
   645  
   646  func TestDecodeTrafficDirection(t *testing.T) {
   647  	localIP := netip.MustParseAddr("1.2.3.4")
   648  	localEP := uint16(1234)
   649  	hostEP := uint16(0x1092)
   650  	remoteIP := netip.MustParseAddr("5.6.7.8")
   651  	remoteID := uint32(5678)
   652  
   653  	directionFromProto := func(direction flowpb.TrafficDirection) trafficdirection.TrafficDirection {
   654  		switch direction {
   655  		case flowpb.TrafficDirection_INGRESS:
   656  			return trafficdirection.Ingress
   657  		case flowpb.TrafficDirection_EGRESS:
   658  			return trafficdirection.Egress
   659  		}
   660  		return trafficdirection.Invalid
   661  	}
   662  
   663  	policyLabel := labels.LabelArrayList{labels.ParseLabelArray("foo=bar")}
   664  	policyKey := policyTypes.Key{
   665  		Identity:         remoteID,
   666  		DestPort:         0,
   667  		InvertedPortMask: 0xffff, // this is a wildcard
   668  		Nexthdr:          0,
   669  		TrafficDirection: trafficdirection.Egress.Uint8(),
   670  	}
   671  
   672  	endpointGetter := &testutils.FakeEndpointGetter{
   673  		OnGetEndpointInfo: func(ip netip.Addr) (endpoint getters.EndpointInfo, ok bool) {
   674  			if ip == localIP {
   675  				return &testutils.FakeEndpointInfo{
   676  					ID: uint64(localEP),
   677  					PolicyMap: map[policyTypes.Key]labels.LabelArrayList{
   678  						policyKey: policyLabel,
   679  					},
   680  					PolicyRevision: 1,
   681  				}, true
   682  			}
   683  			return nil, false
   684  		},
   685  	}
   686  
   687  	parser, err := New(log, endpointGetter, nil, nil, nil, nil, nil)
   688  	require.NoError(t, err)
   689  	parseFlow := func(event any, srcIPv4, dstIPv4 netip.Addr) *flowpb.Flow {
   690  		data, err := testutils.CreateL3L4Payload(event,
   691  			&layers.Ethernet{
   692  				SrcMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
   693  				DstMAC:       net.HardwareAddr{7, 8, 9, 0, 1, 2},
   694  				EthernetType: layers.EthernetTypeIPv4,
   695  			},
   696  			&layers.IPv4{SrcIP: srcIPv4.AsSlice(), DstIP: dstIPv4.AsSlice()})
   697  		require.NoError(t, err)
   698  		f := &flowpb.Flow{}
   699  		err = parser.Decode(data, f)
   700  		require.NoError(t, err)
   701  		return f
   702  	}
   703  
   704  	// DROP at unknown endpoint
   705  	dn := monitor.DropNotify{
   706  		Type: byte(monitorAPI.MessageTypeDrop),
   707  	}
   708  	f := parseFlow(dn, localIP, remoteIP)
   709  	assert.Equal(t, flowpb.TrafficDirection_TRAFFIC_DIRECTION_UNKNOWN, f.GetTrafficDirection())
   710  	assert.Equal(t, uint32(localEP), f.GetSource().GetID())
   711  
   712  	// DROP Egress
   713  	dn = monitor.DropNotify{
   714  		Type:   byte(monitorAPI.MessageTypeDrop),
   715  		Source: localEP,
   716  	}
   717  	f = parseFlow(dn, localIP, remoteIP)
   718  	assert.Equal(t, flowpb.TrafficDirection_EGRESS, f.GetTrafficDirection())
   719  	assert.Equal(t, uint32(localEP), f.GetSource().GetID())
   720  
   721  	// DROP Ingress
   722  	dn = monitor.DropNotify{
   723  		Type:   byte(monitorAPI.MessageTypeDrop),
   724  		Source: localEP,
   725  	}
   726  	f = parseFlow(dn, remoteIP, localIP)
   727  	assert.Equal(t, flowpb.TrafficDirection_INGRESS, f.GetTrafficDirection())
   728  	assert.Equal(t, uint32(localEP), f.GetDestination().GetID())
   729  
   730  	// TRACE_TO_LXC at unknown endpoint
   731  	tn := monitor.TraceNotifyV0{
   732  		Type:     byte(monitorAPI.MessageTypeTrace),
   733  		ObsPoint: monitorAPI.TraceToLxc,
   734  	}
   735  	f = parseFlow(tn, localIP, remoteIP)
   736  	assert.Equal(t, flowpb.TrafficDirection_TRAFFIC_DIRECTION_UNKNOWN, f.GetTrafficDirection())
   737  	assert.Equal(t, uint32(localEP), f.GetSource().GetID())
   738  
   739  	// TRACE_TO_LXC Egress
   740  	tn = monitor.TraceNotifyV0{
   741  		Type:     byte(monitorAPI.MessageTypeTrace),
   742  		Source:   localEP,
   743  		ObsPoint: monitorAPI.TraceToLxc,
   744  	}
   745  	f = parseFlow(tn, localIP, remoteIP)
   746  	assert.Equal(t, flowpb.TrafficDirection_EGRESS, f.GetTrafficDirection())
   747  	assert.Equal(t, uint32(localEP), f.GetSource().GetID())
   748  
   749  	// TO_NETWORK Egress (SNAT)
   750  	tnv1 := monitor.TraceNotifyV1{
   751  		TraceNotifyV0: monitor.TraceNotifyV0{
   752  			Type:     byte(monitorAPI.MessageTypeTrace),
   753  			Source:   hostEP,
   754  			ObsPoint: monitorAPI.TraceToNetwork,
   755  			Version:  monitor.TraceNotifyVersion1,
   756  		},
   757  		OrigIP: types.IPv6{1, 2, 3, 4}, // localIP
   758  	}
   759  	f = parseFlow(tnv1, netip.MustParseAddr("10.11.12.13"), remoteIP)
   760  	assert.Equal(t, flowpb.TrafficDirection_EGRESS, f.GetTrafficDirection())
   761  	assert.Equal(t, uint32(localEP), f.GetSource().GetID())
   762  
   763  	// TRACE_TO_LXC Egress, reversed by CT_REPLY
   764  	tn = monitor.TraceNotifyV0{
   765  		Type:     byte(monitorAPI.MessageTypeTrace),
   766  		Source:   localEP,
   767  		ObsPoint: monitorAPI.TraceToLxc,
   768  		Reason:   monitor.TraceReasonCtReply,
   769  	}
   770  	f = parseFlow(tn, localIP, remoteIP)
   771  	assert.Equal(t, flowpb.TrafficDirection_INGRESS, f.GetTrafficDirection())
   772  	assert.Equal(t, uint32(localEP), f.GetSource().GetID())
   773  
   774  	// TRACE_TO_HOST Ingress
   775  	tn = monitor.TraceNotifyV0{
   776  		Type:     byte(monitorAPI.MessageTypeTrace),
   777  		Source:   localEP,
   778  		ObsPoint: monitorAPI.TraceToHost,
   779  	}
   780  	f = parseFlow(tn, remoteIP, localIP)
   781  	assert.Equal(t, flowpb.TrafficDirection_INGRESS, f.GetTrafficDirection())
   782  	assert.Equal(t, uint32(localEP), f.GetDestination().GetID())
   783  
   784  	// TRACE_TO_HOST Ingress, reversed by CT_REPLY
   785  	tn = monitor.TraceNotifyV0{
   786  		Type:     byte(monitorAPI.MessageTypeTrace),
   787  		Source:   localEP,
   788  		ObsPoint: monitorAPI.TraceToHost,
   789  		Reason:   monitor.TraceReasonCtReply,
   790  	}
   791  	f = parseFlow(tn, remoteIP, localIP)
   792  	assert.Equal(t, flowpb.TrafficDirection_EGRESS, f.GetTrafficDirection())
   793  	assert.Equal(t, uint32(localEP), f.GetDestination().GetID())
   794  
   795  	// TRACE_FROM_LXC unknown
   796  	tn = monitor.TraceNotifyV0{
   797  		Type:     byte(monitorAPI.MessageTypeTrace),
   798  		Source:   localEP,
   799  		ObsPoint: monitorAPI.TraceFromLxc,
   800  		Reason:   monitor.TraceReasonUnknown,
   801  	}
   802  	f = parseFlow(tn, localIP, remoteIP)
   803  	assert.Equal(t, flowpb.TrafficDirection_TRAFFIC_DIRECTION_UNKNOWN, f.GetTrafficDirection())
   804  	assert.Equal(t, uint32(localEP), f.GetSource().GetID())
   805  
   806  	// TRACE_FROM_LXC unknown (encrypted)
   807  	tn = monitor.TraceNotifyV0{
   808  		Type:     byte(monitorAPI.MessageTypeTrace),
   809  		Source:   localEP,
   810  		ObsPoint: monitorAPI.TraceFromLxc,
   811  		Reason:   monitor.TraceReasonUnknown | monitor.TraceReasonEncryptMask,
   812  	}
   813  	f = parseFlow(tn, localIP, remoteIP)
   814  	assert.Equal(t, flowpb.TrafficDirection_TRAFFIC_DIRECTION_UNKNOWN, f.GetTrafficDirection())
   815  	assert.Equal(t, uint32(localEP), f.GetSource().GetID())
   816  
   817  	// TRACE_TO_STACK Encrypt Overlay
   818  	tn = monitor.TraceNotifyV0{
   819  		Type:     byte(monitorAPI.MessageTypeTrace),
   820  		Source:   hostEP,
   821  		ObsPoint: monitorAPI.TraceToStack,
   822  		Reason:   monitor.TraceReasonEncryptOverlay,
   823  	}
   824  	f = parseFlow(tn, localIP, remoteIP)
   825  	assert.Equal(t, flowpb.TrafficDirection_EGRESS, f.GetTrafficDirection())
   826  	assert.Equal(t, uint32(localEP), f.GetSource().GetID())
   827  
   828  	// TRACE_TO_STACK SRV6 decap Ingress
   829  	tn = monitor.TraceNotifyV0{
   830  		Type:     byte(monitorAPI.MessageTypeTrace),
   831  		Source:   hostEP,
   832  		ObsPoint: monitorAPI.TraceToStack,
   833  		Reason:   monitor.TraceReasonSRv6Decap,
   834  	}
   835  	f = parseFlow(tn, remoteIP, localIP)
   836  	assert.Equal(t, flowpb.TrafficDirection_INGRESS, f.GetTrafficDirection())
   837  	assert.Equal(t, uint32(localEP), f.GetDestination().GetID())
   838  
   839  	// TRACE_TO_STACK SRV6 encap Egress
   840  	tn = monitor.TraceNotifyV0{
   841  		Type:     byte(monitorAPI.MessageTypeTrace),
   842  		Source:   localEP,
   843  		ObsPoint: monitorAPI.TraceToStack,
   844  		Reason:   monitor.TraceReasonSRv6Encap,
   845  	}
   846  	f = parseFlow(tn, localIP, remoteIP)
   847  	assert.Equal(t, flowpb.TrafficDirection_EGRESS, f.GetTrafficDirection())
   848  	assert.Equal(t, uint32(localEP), f.GetSource().GetID())
   849  
   850  	// PolicyVerdictNotify Egress
   851  	pvn := monitor.PolicyVerdictNotify{
   852  		Type:        byte(monitorAPI.MessageTypePolicyVerdict),
   853  		Source:      localEP,
   854  		Flags:       monitorAPI.PolicyEgress,
   855  		RemoteLabel: identity.NumericIdentity(remoteID),
   856  	}
   857  	f = parseFlow(pvn, localIP, remoteIP)
   858  	assert.Equal(t, flowpb.TrafficDirection_EGRESS, f.GetTrafficDirection())
   859  	assert.Equal(t, uint32(localEP), f.GetSource().GetID())
   860  
   861  	ep, ok := endpointGetter.GetEndpointInfo(localIP)
   862  	assert.Equal(t, true, ok)
   863  	lbls, rev, ok := ep.GetRealizedPolicyRuleLabelsForKey(policyTypes.Key{
   864  		Identity:         f.GetDestination().GetIdentity(),
   865  		InvertedPortMask: 0xffff, // this is a wildcard
   866  		TrafficDirection: directionFromProto(f.GetTrafficDirection()).Uint8(),
   867  	})
   868  	assert.Equal(t, true, ok)
   869  	assert.Equal(t, lbls, policyLabel)
   870  	assert.Equal(t, uint64(1), rev)
   871  
   872  	// PolicyVerdictNotify Ingress
   873  	pvn = monitor.PolicyVerdictNotify{
   874  		Type:   byte(monitorAPI.MessageTypePolicyVerdict),
   875  		Source: localEP,
   876  		Flags:  monitorAPI.PolicyIngress,
   877  	}
   878  	f = parseFlow(pvn, remoteIP, localIP)
   879  	assert.Equal(t, flowpb.TrafficDirection_INGRESS, f.GetTrafficDirection())
   880  	assert.Equal(t, uint32(localEP), f.GetDestination().GetID())
   881  }
   882  
   883  func TestDecodeIsReply(t *testing.T) {
   884  	localIP := net.ParseIP("1.2.3.4")
   885  	localEP := uint16(1234)
   886  	hostEP := uint16(0x1092)
   887  	remoteIP := net.ParseIP("5.6.7.8")
   888  
   889  	parser, err := New(log, nil, nil, nil, nil, nil, nil)
   890  	require.NoError(t, err)
   891  	parseFlow := func(event interface{}, srcIPv4, dstIPv4 net.IP) *flowpb.Flow {
   892  		data, err := testutils.CreateL3L4Payload(event,
   893  			&layers.Ethernet{
   894  				SrcMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
   895  				DstMAC:       net.HardwareAddr{7, 8, 9, 0, 1, 2},
   896  				EthernetType: layers.EthernetTypeIPv4,
   897  			},
   898  			&layers.IPv4{SrcIP: srcIPv4, DstIP: dstIPv4})
   899  		require.NoError(t, err)
   900  		f := &flowpb.Flow{}
   901  		err = parser.Decode(data, f)
   902  		require.NoError(t, err)
   903  		return f
   904  	}
   905  
   906  	// TRACE_TO_LXC
   907  	tn := monitor.TraceNotifyV0{
   908  		Type:     byte(monitorAPI.MessageTypeTrace),
   909  		ObsPoint: monitorAPI.TraceToLxc,
   910  		Reason:   monitor.TraceReasonCtReply,
   911  	}
   912  	f := parseFlow(tn, localIP, remoteIP)
   913  	assert.NotNil(t, f.GetIsReply())
   914  	assert.Equal(t, true, f.GetIsReply().GetValue())
   915  	assert.Equal(t, true, f.GetReply())
   916  
   917  	// TRACE_FROM_LXC
   918  	tn = monitor.TraceNotifyV0{
   919  		Type:     byte(monitorAPI.MessageTypeTrace),
   920  		ObsPoint: monitorAPI.TraceFromLxc,
   921  		Reason:   monitor.TraceReasonUnknown,
   922  	}
   923  	f = parseFlow(tn, localIP, remoteIP)
   924  	assert.Nil(t, f.GetIsReply())
   925  	assert.Equal(t, false, f.GetReply())
   926  
   927  	// TRACE_FROM_LXC encrypted
   928  	tn = monitor.TraceNotifyV0{
   929  		Type:     byte(monitorAPI.MessageTypeTrace),
   930  		ObsPoint: monitorAPI.TraceFromLxc,
   931  		Reason:   monitor.TraceReasonUnknown | monitor.TraceReasonEncryptMask,
   932  	}
   933  	f = parseFlow(tn, localIP, remoteIP)
   934  	assert.Nil(t, f.GetIsReply())
   935  	assert.Equal(t, false, f.GetReply())
   936  
   937  	// TRACE_TO_STACK srv6-decap
   938  	tn = monitor.TraceNotifyV0{
   939  		Type:     byte(monitorAPI.MessageTypeTrace),
   940  		Source:   hostEP,
   941  		ObsPoint: monitorAPI.TraceToStack,
   942  		Reason:   monitor.TraceReasonSRv6Decap,
   943  	}
   944  	f = parseFlow(tn, remoteIP, localIP)
   945  	assert.Nil(t, f.GetIsReply())
   946  	assert.Equal(t, false, f.GetReply())
   947  
   948  	// TRACE_TO_STACK srv6-decap (encrypted)
   949  	tn = monitor.TraceNotifyV0{
   950  		Type:     byte(monitorAPI.MessageTypeTrace),
   951  		Source:   hostEP,
   952  		ObsPoint: monitorAPI.TraceToStack,
   953  		Reason:   monitor.TraceReasonSRv6Decap | monitor.TraceReasonEncryptMask,
   954  	}
   955  	f = parseFlow(tn, remoteIP, localIP)
   956  	assert.Nil(t, f.GetIsReply())
   957  	assert.Equal(t, false, f.GetReply())
   958  
   959  	// TRACE_TO_STACK srv6-encap
   960  	tn = monitor.TraceNotifyV0{
   961  		Type:     byte(monitorAPI.MessageTypeTrace),
   962  		Source:   localEP,
   963  		ObsPoint: monitorAPI.TraceToStack,
   964  		Reason:   monitor.TraceReasonSRv6Encap,
   965  	}
   966  	f = parseFlow(tn, localIP, remoteIP)
   967  	assert.Nil(t, f.GetIsReply())
   968  	assert.Equal(t, false, f.GetReply())
   969  
   970  	// TRACE_TO_STACK srv6-encap (encrypted)
   971  	tn = monitor.TraceNotifyV0{
   972  		Type:     byte(monitorAPI.MessageTypeTrace),
   973  		Source:   localEP,
   974  		ObsPoint: monitorAPI.TraceToStack,
   975  		Reason:   monitor.TraceReasonSRv6Encap | monitor.TraceReasonEncryptMask,
   976  	}
   977  	f = parseFlow(tn, localIP, remoteIP)
   978  	assert.Nil(t, f.GetIsReply())
   979  	assert.Equal(t, false, f.GetReply())
   980  
   981  	// TRACE_TO_STACK Encrypted Overlay
   982  	tn = monitor.TraceNotifyV0{
   983  		Type:     byte(monitorAPI.MessageTypeTrace),
   984  		Source:   hostEP,
   985  		ObsPoint: monitorAPI.TraceToStack,
   986  		Reason:   monitor.TraceReasonEncryptOverlay,
   987  	}
   988  	f = parseFlow(tn, localIP, remoteIP)
   989  	assert.Nil(t, f.GetIsReply())
   990  	assert.Equal(t, false, f.GetReply())
   991  
   992  	// PolicyVerdictNotify forward statically assumes is_reply=false
   993  	pvn := monitor.PolicyVerdictNotify{
   994  		Type:    byte(monitorAPI.MessageTypePolicyVerdict),
   995  		Verdict: 0,
   996  	}
   997  	f = parseFlow(pvn, localIP, remoteIP)
   998  	assert.NotNil(t, f.GetIsReply())
   999  	assert.Equal(t, false, f.GetIsReply().GetValue())
  1000  	assert.Equal(t, false, f.GetReply())
  1001  
  1002  	// PolicyVerdictNotify drop statically assumes is_reply=unknown
  1003  	pvn = monitor.PolicyVerdictNotify{
  1004  		Type:    byte(monitorAPI.MessageTypePolicyVerdict),
  1005  		Verdict: -151, // drop reason: Stale or unroutable IP
  1006  	}
  1007  	f = parseFlow(pvn, localIP, remoteIP)
  1008  	assert.Nil(t, f.GetIsReply())
  1009  	assert.Equal(t, false, f.GetReply())
  1010  
  1011  	// DropNotify statically assumes is_reply=unknown
  1012  	dn := monitor.DropNotify{
  1013  		Type: byte(monitorAPI.MessageTypeDrop),
  1014  	}
  1015  	f = parseFlow(dn, localIP, remoteIP)
  1016  	assert.Nil(t, f.GetIsReply())
  1017  	assert.Equal(t, false, f.GetReply())
  1018  }
  1019  
  1020  func Test_filterCIDRLabels(t *testing.T) {
  1021  	type args struct {
  1022  		labels []string
  1023  	}
  1024  	tests := []struct {
  1025  		name string
  1026  		args args
  1027  		want []string
  1028  	}{
  1029  		{
  1030  			name: "mixed",
  1031  			args: args{
  1032  				labels: []string{
  1033  					"b",
  1034  					"cidr:1.1.1.1/23",
  1035  					"a",
  1036  					"d",
  1037  					"cidr:1.1.1.1/24",
  1038  				},
  1039  			},
  1040  			want: []string{"b", "a", "d", "cidr:1.1.1.1/24"},
  1041  		}, {
  1042  			name: "mixed, IPv6",
  1043  			args: args{
  1044  				labels: []string{
  1045  					"b",
  1046  					"cidr:2a00-1450-400a-800--0/85", // - is used instead of : in the address because labels cannot contain :
  1047  					"a",
  1048  					"d",
  1049  					"cidr:2a00-1450-400a-800--0/107",
  1050  				},
  1051  			},
  1052  			want: []string{"b", "a", "d", "cidr:2a00-1450-400a-800--0/107"},
  1053  		}, {
  1054  			name: "no-cidr",
  1055  			args: args{
  1056  				labels: []string{"b", "c", "a"},
  1057  			},
  1058  			want: []string{"b", "c", "a"},
  1059  		}, {
  1060  			name: "cidr-only",
  1061  			args: args{
  1062  				labels: []string{
  1063  					"cidr:1.1.1.1/0",
  1064  					"cidr:1.1.1.1/32",
  1065  					"cidr:1.1.1.1/16",
  1066  				},
  1067  			},
  1068  			want: []string{"cidr:1.1.1.1/32"},
  1069  		}, {
  1070  			name: "cidr-only, IPv6",
  1071  			args: args{
  1072  				labels: []string{
  1073  					"cidr:2a00-1450-400a-800--0/85", // - is used instead of : in the address because labels cannot contain :
  1074  					"cidr:2a00-1450-400a-800--0/95",
  1075  					"cidr:2a00-1450-400a-800--0/107",
  1076  				},
  1077  			},
  1078  			want: []string{"cidr:2a00-1450-400a-800--0/107"},
  1079  		}, {
  1080  			name: "empty",
  1081  			args: args{
  1082  				labels: []string{},
  1083  			},
  1084  			want: nil,
  1085  		},
  1086  	}
  1087  	for _, tt := range tests {
  1088  		t.Run(tt.name, func(t *testing.T) {
  1089  			got := common.FilterCIDRLabels(log, tt.args.labels)
  1090  			assert.Equal(t, tt.want, got)
  1091  		})
  1092  	}
  1093  }
  1094  
  1095  func TestTraceNotifyOriginalIP(t *testing.T) {
  1096  	f := &flowpb.Flow{}
  1097  	parser, err := New(log, &testutils.NoopEndpointGetter, nil, &testutils.NoopDNSGetter, &testutils.NoopIPGetter, &testutils.NoopServiceGetter, &testutils.NoopLinkGetter)
  1098  	require.NoError(t, err)
  1099  
  1100  	v0 := monitor.TraceNotifyV0{
  1101  		Type:    byte(monitorAPI.MessageTypeTrace),
  1102  		Version: monitor.TraceNotifyVersion0,
  1103  	}
  1104  	eth := layers.Ethernet{
  1105  		EthernetType: layers.EthernetTypeIPv4,
  1106  		SrcMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
  1107  		DstMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
  1108  	}
  1109  	ip := layers.IPv4{
  1110  		SrcIP: net.ParseIP("10.0.0.2"),
  1111  		DstIP: net.ParseIP("10.0.0.3"),
  1112  	}
  1113  	data, err := testutils.CreateL3L4Payload(v0, &eth, &ip, &layers.TCP{})
  1114  	require.NoError(t, err)
  1115  
  1116  	err = parser.Decode(data, f)
  1117  	require.NoError(t, err)
  1118  	assert.Equal(t, f.IP.Source, "10.0.0.2")
  1119  	assert.Empty(t, f.IP.SourceXlated)
  1120  
  1121  	v1 := monitor.TraceNotifyV1{
  1122  		TraceNotifyV0: monitor.TraceNotifyV0{
  1123  			Type:    byte(monitorAPI.MessageTypeTrace),
  1124  			Version: monitor.TraceNotifyVersion1,
  1125  		},
  1126  		OrigIP: [16]byte{1, 1, 1, 1},
  1127  	}
  1128  	data, err = testutils.CreateL3L4Payload(v1, &eth, &ip, &layers.TCP{})
  1129  	require.NoError(t, err)
  1130  	err = parser.Decode(data, f)
  1131  	require.NoError(t, err)
  1132  	assert.Equal(t, f.IP.Source, "1.1.1.1")
  1133  	assert.Equal(t, f.IP.SourceXlated, "10.0.0.2")
  1134  
  1135  	v1 = monitor.TraceNotifyV1{
  1136  		TraceNotifyV0: monitor.TraceNotifyV0{
  1137  			Type:    byte(monitorAPI.MessageTypeTrace),
  1138  			Version: monitor.TraceNotifyVersion1,
  1139  		},
  1140  		OrigIP: [16]byte{10, 0, 0, 2},
  1141  	}
  1142  	data, err = testutils.CreateL3L4Payload(v1, &eth, &ip, &layers.TCP{})
  1143  	require.NoError(t, err)
  1144  	err = parser.Decode(data, f)
  1145  	require.NoError(t, err)
  1146  	assert.Equal(t, f.IP.Source, "10.0.0.2")
  1147  	assert.Empty(t, f.IP.SourceXlated)
  1148  }
  1149  
  1150  func TestICMP(t *testing.T) {
  1151  	parser, err := New(log, &testutils.NoopEndpointGetter, nil, &testutils.NoopDNSGetter, &testutils.NoopIPGetter, &testutils.NoopServiceGetter, &testutils.NoopLinkGetter)
  1152  	require.NoError(t, err)
  1153  	message := monitor.TraceNotifyV1{
  1154  		TraceNotifyV0: monitor.TraceNotifyV0{
  1155  			Type:    byte(monitorAPI.MessageTypeTrace),
  1156  			Version: monitor.TraceNotifyVersion1,
  1157  		},
  1158  	}
  1159  
  1160  	// icmpv4
  1161  	eth := layers.Ethernet{
  1162  		EthernetType: layers.EthernetTypeIPv4,
  1163  		SrcMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
  1164  		DstMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
  1165  	}
  1166  	ip := layers.IPv4{
  1167  		SrcIP:    net.ParseIP("10.0.0.2"),
  1168  		DstIP:    net.ParseIP("10.0.0.3"),
  1169  		Protocol: layers.IPProtocolICMPv4,
  1170  	}
  1171  	icmpv4 := layers.ICMPv4{
  1172  		TypeCode: layers.CreateICMPv4TypeCode(1, 2),
  1173  	}
  1174  	v4data, err := testutils.CreateL3L4Payload(message, &eth, &ip, &icmpv4)
  1175  	require.NoError(t, err)
  1176  	v4flow := &flowpb.Flow{}
  1177  	err = parser.Decode(v4data, v4flow)
  1178  	require.NoError(t, err)
  1179  	assert.Equal(t, uint32(1), v4flow.GetL4().GetICMPv4().Type)
  1180  	assert.Equal(t, uint32(2), v4flow.GetL4().GetICMPv4().Code)
  1181  
  1182  	// icmpv4
  1183  	ethv6 := layers.Ethernet{
  1184  		EthernetType: layers.EthernetTypeIPv6,
  1185  		SrcMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
  1186  		DstMAC:       net.HardwareAddr{2, 3, 4, 5, 6, 7},
  1187  	}
  1188  	ipv6 := layers.IPv6{
  1189  		Version:    0x6,
  1190  		NextHeader: 0x3a,
  1191  		SrcIP:      net.ParseIP("::"),
  1192  		DstIP:      net.ParseIP("::"),
  1193  	}
  1194  	icmpv6 := layers.ICMPv6{
  1195  		TypeCode: layers.CreateICMPv6TypeCode(3, 4),
  1196  	}
  1197  	v6data, err := testutils.CreateL3L4Payload(message, &ethv6, &ipv6, &icmpv6)
  1198  	require.NoError(t, err)
  1199  	v6flow := &flowpb.Flow{}
  1200  	err = parser.Decode(v6data, v6flow)
  1201  	require.NoError(t, err)
  1202  	assert.Equal(t, uint32(3), v6flow.GetL4().GetICMPv6().Type)
  1203  	assert.Equal(t, uint32(4), v6flow.GetL4().GetICMPv6().Code)
  1204  }
  1205  
  1206  func TestTraceNotifyLocalEndpoint(t *testing.T) {
  1207  	f := &flowpb.Flow{}
  1208  
  1209  	ep := &testutils.FakeEndpointInfo{
  1210  		ID:           1234,
  1211  		Identity:     4567,
  1212  		IPv4:         net.ParseIP("1.1.1.1"),
  1213  		PodName:      "xwing",
  1214  		PodNamespace: "default",
  1215  		Labels:       []string{"a", "b", "c"},
  1216  	}
  1217  	endpointGetter := &testutils.FakeEndpointGetter{
  1218  		OnGetEndpointInfo: func(ip netip.Addr) (endpoint getters.EndpointInfo, ok bool) {
  1219  			return ep, true
  1220  		},
  1221  	}
  1222  
  1223  	parser, err := New(log, endpointGetter, nil, &testutils.NoopDNSGetter, &testutils.NoopIPGetter, &testutils.NoopServiceGetter, &testutils.NoopLinkGetter)
  1224  	require.NoError(t, err)
  1225  
  1226  	v0 := monitor.TraceNotifyV0{
  1227  		Type:     byte(monitorAPI.MessageTypeTrace),
  1228  		SrcLabel: 456, // takes precedence over ep.Identity
  1229  		Version:  monitor.TraceNotifyVersion0,
  1230  	}
  1231  
  1232  	eth := layers.Ethernet{
  1233  		EthernetType: layers.EthernetTypeIPv4,
  1234  		SrcMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
  1235  		DstMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
  1236  	}
  1237  	ip := layers.IPv4{
  1238  		SrcIP:    net.ParseIP("10.0.0.2"),
  1239  		DstIP:    net.ParseIP("10.0.0.3"),
  1240  		Protocol: layers.IPProtocolTCP,
  1241  	}
  1242  	data, err := testutils.CreateL3L4Payload(v0, &eth, &ip, &layers.TCP{})
  1243  	require.NoError(t, err)
  1244  
  1245  	err = parser.Decode(data, f)
  1246  	require.NoError(t, err)
  1247  
  1248  	assert.Equal(t, uint32(ep.ID), f.Source.ID)
  1249  	assert.Equal(t, uint32(v0.SrcLabel), f.Source.Identity)
  1250  	assert.Equal(t, ep.PodNamespace, f.Source.Namespace)
  1251  	assert.Equal(t, ep.Labels, f.Source.Labels)
  1252  	assert.Equal(t, ep.PodName, f.Source.PodName)
  1253  }
  1254  
  1255  func TestDebugCapture(t *testing.T) {
  1256  	f := &flowpb.Flow{}
  1257  
  1258  	parser, err := New(log, &testutils.NoopEndpointGetter, &testutils.NoopIdentityGetter, &testutils.NoopDNSGetter, &testutils.NoopIPGetter, &testutils.NoopServiceGetter, &testutils.NoopLinkGetter)
  1259  	require.NoError(t, err)
  1260  
  1261  	// The testutils.NoopLinkGetter above will mock out the device name
  1262  	// lookup to always return 'lo', so we can just hardcode it here and
  1263  	// check that the events below get decoded with this link name.
  1264  	loIfName := "lo"
  1265  	loIfIndex := uint32(1)
  1266  
  1267  	dbg := monitor.DebugCapture{
  1268  		Type:    monitorAPI.MessageTypeCapture,
  1269  		SubType: monitor.DbgCaptureDelivery,
  1270  		Arg1:    loIfIndex,
  1271  	}
  1272  
  1273  	eth := layers.Ethernet{
  1274  		EthernetType: layers.EthernetTypeIPv4,
  1275  		SrcMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
  1276  		DstMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
  1277  	}
  1278  	ip := layers.IPv4{
  1279  		SrcIP:    net.ParseIP("10.0.0.2"),
  1280  		DstIP:    net.ParseIP("10.0.0.3"),
  1281  		Protocol: layers.IPProtocolTCP,
  1282  	}
  1283  	data, err := testutils.CreateL3L4Payload(dbg, &eth, &ip, &layers.TCP{})
  1284  	require.NoError(t, err)
  1285  
  1286  	err = parser.Decode(data, f)
  1287  	require.NoError(t, err)
  1288  
  1289  	assert.Equal(t, int32(dbg.Type), f.EventType.Type)
  1290  	assert.Equal(t, int32(dbg.SubType), f.EventType.SubType)
  1291  	assert.Equal(t, flowpb.DebugCapturePoint_DBG_CAPTURE_DELIVERY, f.DebugCapturePoint)
  1292  	assert.Equal(t, ip.SrcIP.String(), f.IP.Source)
  1293  	assert.Equal(t, ip.DstIP.String(), f.IP.Destination)
  1294  	assert.NotNil(t, f.L4.GetTCP())
  1295  
  1296  	assert.Equal(t, &flowpb.NetworkInterface{
  1297  		Index: loIfIndex,
  1298  		Name:  loIfName,
  1299  	}, f.Interface)
  1300  
  1301  	nilParser, err := New(log, nil, nil, nil, nil, nil, nil)
  1302  	require.NoError(t, err)
  1303  	err = nilParser.Decode(data, f)
  1304  	require.NoError(t, err)
  1305  
  1306  	dbg = monitor.DebugCapture{
  1307  		Type:    monitorAPI.MessageTypeCapture,
  1308  		SubType: monitor.DbgCaptureProxyPost,
  1309  		Arg1:    byteorder.HostToNetwork32(1234),
  1310  	}
  1311  	data, err = testutils.CreateL3L4Payload(dbg)
  1312  	require.NoError(t, err)
  1313  
  1314  	err = parser.Decode(data, f)
  1315  	require.NoError(t, err)
  1316  
  1317  	assert.Equal(t, int32(dbg.Type), f.EventType.Type)
  1318  	assert.Equal(t, int32(dbg.SubType), f.EventType.SubType)
  1319  	assert.Equal(t, flowpb.DebugCapturePoint_DBG_CAPTURE_PROXY_POST, f.DebugCapturePoint)
  1320  	assert.Equal(t, uint32(1234), f.ProxyPort)
  1321  
  1322  	err = nilParser.Decode(data, f)
  1323  	require.NoError(t, err)
  1324  }
  1325  
  1326  func TestTraceNotifyProxyPort(t *testing.T) {
  1327  	f := &flowpb.Flow{}
  1328  	parser, err := New(log, &testutils.NoopEndpointGetter, nil, &testutils.NoopDNSGetter, &testutils.NoopIPGetter, &testutils.NoopServiceGetter, &testutils.NoopLinkGetter)
  1329  	require.NoError(t, err)
  1330  
  1331  	v0 := monitor.TraceNotifyV0{
  1332  		Type:     byte(monitorAPI.MessageTypeTrace),
  1333  		Version:  monitor.TraceNotifyVersion0,
  1334  		ObsPoint: monitorAPI.TraceToProxy,
  1335  		DstID:    uint16(1234),
  1336  	}
  1337  	eth := layers.Ethernet{
  1338  		EthernetType: layers.EthernetTypeIPv4,
  1339  		SrcMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
  1340  		DstMAC:       net.HardwareAddr{1, 2, 3, 4, 5, 6},
  1341  	}
  1342  	ip := layers.IPv4{
  1343  		SrcIP: net.ParseIP("10.0.0.2"),
  1344  		DstIP: net.ParseIP("10.0.0.3"),
  1345  	}
  1346  	data, err := testutils.CreateL3L4Payload(v0, &eth, &ip, &layers.TCP{})
  1347  	require.NoError(t, err)
  1348  
  1349  	err = parser.Decode(data, f)
  1350  	require.NoError(t, err)
  1351  	assert.Equal(t, f.ProxyPort, uint32(1234))
  1352  
  1353  	v1 := monitor.TraceNotifyV1{
  1354  		TraceNotifyV0: monitor.TraceNotifyV0{
  1355  			Type:     byte(monitorAPI.MessageTypeTrace),
  1356  			Version:  monitor.TraceNotifyVersion1,
  1357  			ObsPoint: monitorAPI.TraceToProxy,
  1358  			DstID:    uint16(4321),
  1359  		},
  1360  		OrigIP: [16]byte{1, 1, 1, 1},
  1361  	}
  1362  	data, err = testutils.CreateL3L4Payload(v1, &eth, &ip, &layers.TCP{})
  1363  	require.NoError(t, err)
  1364  	err = parser.Decode(data, f)
  1365  	require.NoError(t, err)
  1366  	assert.Equal(t, f.ProxyPort, uint32(4321))
  1367  }