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, ð, &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, ð, &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, ð, &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, ð, &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, ð, &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, ðv6, &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, ð, &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, ð, &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, ð, &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, ð, &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 }