github.com/cilium/cilium@v1.16.2/pkg/hubble/parser/sock/parser_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Hubble 3 4 package sock 5 6 import ( 7 "bytes" 8 "encoding/binary" 9 "errors" 10 "fmt" 11 "net" 12 "net/netip" 13 "testing" 14 15 "github.com/sirupsen/logrus" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 19 flowpb "github.com/cilium/cilium/api/v1/flow" 20 "github.com/cilium/cilium/pkg/byteorder" 21 cgroupManager "github.com/cilium/cilium/pkg/cgroups/manager" 22 parserErrors "github.com/cilium/cilium/pkg/hubble/parser/errors" 23 "github.com/cilium/cilium/pkg/hubble/parser/getters" 24 "github.com/cilium/cilium/pkg/hubble/testutils" 25 "github.com/cilium/cilium/pkg/identity" 26 "github.com/cilium/cilium/pkg/ipcache" 27 "github.com/cilium/cilium/pkg/labels" 28 "github.com/cilium/cilium/pkg/monitor" 29 monitorAPI "github.com/cilium/cilium/pkg/monitor/api" 30 "github.com/cilium/cilium/pkg/types" 31 ) 32 33 func mustParseIP(s string) (res types.IPv6) { 34 ip := net.ParseIP(s) 35 if ip == nil { 36 panic(fmt.Sprintf("failed to parse ip %q", s)) 37 } 38 if v4 := ip.To4(); v4 != nil { 39 copy(res[:4], v4) 40 } else { 41 copy(res[:], ip) 42 } 43 return res 44 } 45 46 func TestDecodeSockEvent(t *testing.T) { 47 const ( 48 xwingIPv4 = "192.168.10.10" 49 xwingIPv6 = "f00d::a10:0:0:10" 50 xwingCgroupId = 101010 51 xwingIdentity = 1234 52 xwingEndpoint = 110 53 xwingPodName = "xwing" 54 xwingPodNamespace = "default" 55 deathstarIPv4 = "192.168.20.20" 56 deathstarIPv6 = "f00d::20:20" 57 deathstarServiceV4 = "10.10.20.20" 58 deathstarServiceV6 = "f00c::20:20" 59 deathstarIdentity = 5678 60 deathstarEndpoint = 220 61 deathstarServicePort = 8080 62 deathstarTargetPort = 80 63 deathstarPodName = "deathstar-1" 64 deathstarPodNamespace = "default" 65 deathstarServiceName = "deathstar" 66 deathstarServiceNamespace = "default" 67 deathstarServiceDomain = "deathstar.default.svc.cluster.local" 68 deathstarAltIPv4 = "192.168.20.21" 69 deathstarAltIPv6 = "f00d::20:21" 70 deathstarAltPodName = "deathstar-2" 71 deathstarAltPodNamespace = "default" 72 ) 73 var ( 74 xwingLabels = []string{"k8s:org=alliance"} 75 deathstarLabels = []string{"k8s:org=empire"} 76 ) 77 78 endpointGetter := &testutils.FakeEndpointGetter{ 79 OnGetEndpointInfo: func(ip netip.Addr) (endpoint getters.EndpointInfo, ok bool) { 80 switch ip.String() { 81 case xwingIPv4, xwingIPv6: 82 return &testutils.FakeEndpointInfo{ 83 ID: xwingEndpoint, 84 Identity: xwingIdentity, 85 IPv4: net.ParseIP(xwingIPv4), 86 IPv6: net.ParseIP(xwingIPv6), 87 Labels: xwingLabels, 88 PodName: xwingPodName, 89 PodNamespace: xwingPodNamespace, 90 }, true 91 case deathstarIPv4, deathstarIPv6: 92 return &testutils.FakeEndpointInfo{ 93 ID: deathstarEndpoint, 94 Identity: deathstarIdentity, 95 IPv4: net.ParseIP(deathstarIPv4), 96 IPv6: net.ParseIP(deathstarIPv6), 97 Labels: deathstarLabels, 98 PodName: deathstarPodName, 99 PodNamespace: deathstarPodNamespace, 100 }, true 101 } 102 return nil, false 103 }, 104 } 105 identityGetter := &testutils.FakeIdentityGetter{ 106 OnGetIdentity: func(securityIdentity uint32) (*identity.Identity, error) { 107 switch securityIdentity { 108 case xwingIdentity: 109 return &identity.Identity{ 110 ID: xwingIdentity, 111 Labels: labels.NewLabelsFromModel(xwingLabels), 112 }, nil 113 case deathstarIdentity: 114 return &identity.Identity{ 115 ID: deathstarIdentity, 116 Labels: labels.NewLabelsFromModel(deathstarLabels), 117 }, nil 118 } 119 return nil, errors.New("identity not found") 120 }, 121 } 122 dnsGetter := &testutils.FakeFQDNCache{ 123 OnGetNamesOf: func(epID uint32, ip netip.Addr) (names []string) { 124 switch epID { 125 case xwingEndpoint: 126 switch ip.String() { 127 case deathstarServiceV4, deathstarServiceV6: 128 return []string{deathstarServiceDomain} 129 } 130 } 131 return nil 132 }, 133 } 134 ipGetter := &testutils.FakeIPGetter{ 135 OnGetK8sMetadata: func(ip netip.Addr) *ipcache.K8sMetadata { 136 switch ip.String() { 137 case xwingIPv4, xwingIPv6: 138 return &ipcache.K8sMetadata{ 139 PodName: xwingPodName, 140 Namespace: xwingPodNamespace, 141 } 142 case deathstarIPv4, deathstarIPv6: 143 return &ipcache.K8sMetadata{ 144 PodName: deathstarPodName, 145 Namespace: deathstarPodNamespace, 146 } 147 case deathstarAltIPv4, deathstarAltIPv6: 148 return &ipcache.K8sMetadata{ 149 PodName: deathstarAltPodName, 150 Namespace: deathstarAltPodNamespace, 151 } 152 } 153 return nil 154 }, 155 OnLookupSecIDByIP: func(ip netip.Addr) (ipcache.Identity, bool) { 156 switch ip.String() { 157 case xwingIPv4, xwingIPv6: 158 return ipcache.Identity{ 159 ID: xwingIdentity, 160 }, true 161 case deathstarIPv4, deathstarIPv6, deathstarAltIPv4, deathstarAltIPv6: 162 return ipcache.Identity{ 163 ID: deathstarIdentity, 164 }, true 165 } 166 return ipcache.Identity{}, false 167 }, 168 } 169 serviceGetter := &testutils.FakeServiceGetter{ 170 OnGetServiceByAddr: func(ip netip.Addr, port uint16) *flowpb.Service { 171 switch ip.String() { 172 case deathstarServiceV4, deathstarServiceV6: 173 if port == deathstarServicePort { 174 return &flowpb.Service{ 175 Name: deathstarServiceName, 176 Namespace: deathstarServiceNamespace, 177 } 178 } 179 } 180 return nil 181 }, 182 } 183 cgroupGetter := &testutils.FakePodMetadataGetter{ 184 OnGetPodMetadataForContainer: func(cgroupId uint64) *cgroupManager.PodMetadata { 185 switch cgroupId { 186 case xwingCgroupId: 187 return &cgroupManager.PodMetadata{ 188 Name: xwingPodName, 189 Namespace: xwingPodNamespace, 190 IPs: []string{xwingIPv4, xwingIPv6}, 191 } 192 } 193 return nil 194 }, 195 } 196 tt := []struct { 197 name string 198 msg monitor.TraceSockNotify 199 200 skipUnknownCGroupIDs bool 201 202 rawMsg []byte 203 flow *flowpb.Flow 204 errMsg string 205 }{ 206 { 207 name: "empty buffer", 208 rawMsg: []byte{}, 209 errMsg: parserErrors.ErrEmptyData.Error(), 210 }, 211 { 212 name: "invalid buffer", 213 rawMsg: []byte{monitorAPI.MessageTypeTraceSock}, 214 errMsg: "failed to parse sock trace event", 215 }, 216 { 217 name: "empty event", 218 msg: monitor.TraceSockNotify{}, 219 errMsg: parserErrors.NewErrInvalidType(0).Error(), 220 }, 221 { 222 name: "invalid cgroup id", 223 msg: monitor.TraceSockNotify{ 224 Type: monitorAPI.MessageTypeTraceSock, 225 XlatePoint: monitor.XlatePointPreDirectionFwd, 226 DstIP: mustParseIP("10.10.10.10"), 227 DstPort: 8080, 228 L4Proto: monitor.L4ProtocolUDP, 229 SockCookie: 0xc0ffee, 230 CgroupId: 1234, 231 }, 232 skipUnknownCGroupIDs: true, 233 errMsg: parserErrors.ErrEventSkipped.Error(), 234 }, 235 { 236 name: "minimal", 237 msg: monitor.TraceSockNotify{ 238 Type: monitorAPI.MessageTypeTraceSock, 239 XlatePoint: monitor.XlatePointPreDirectionFwd, 240 DstIP: mustParseIP("10.10.10.10"), 241 DstPort: 8080, 242 L4Proto: monitor.L4ProtocolUDP, 243 SockCookie: 0xc0ffee, 244 }, 245 skipUnknownCGroupIDs: false, 246 flow: &flowpb.Flow{ 247 Type: flowpb.FlowType_SOCK, 248 Verdict: flowpb.Verdict_TRACED, 249 IP: &flowpb.IP{ 250 Destination: "10.10.10.10", 251 IpVersion: flowpb.IPVersion_IPv4, 252 }, 253 L4: &flowpb.Layer4{Protocol: &flowpb.Layer4_UDP{UDP: &flowpb.UDP{ 254 DestinationPort: 8080, 255 }}}, 256 Source: &flowpb.Endpoint{}, 257 Destination: &flowpb.Endpoint{}, 258 EventType: &flowpb.CiliumEventType{ 259 Type: monitorAPI.MessageTypeTraceSock, 260 SubType: monitor.XlatePointPreDirectionFwd, 261 }, 262 SockXlatePoint: monitor.XlatePointPreDirectionFwd, 263 SocketCookie: 0xc0ffee, 264 Summary: "UDP", 265 }, 266 }, 267 { 268 name: "pre-translate v4 xwing to service ip", 269 msg: monitor.TraceSockNotify{ 270 Type: monitorAPI.MessageTypeTraceSock, 271 XlatePoint: monitor.XlatePointPreDirectionFwd, 272 DstIP: mustParseIP(deathstarServiceV4), 273 DstPort: deathstarServicePort, 274 CgroupId: xwingCgroupId, 275 L4Proto: monitor.L4ProtocolTCP, 276 }, 277 skipUnknownCGroupIDs: true, 278 flow: &flowpb.Flow{ 279 Type: flowpb.FlowType_SOCK, 280 Verdict: flowpb.Verdict_TRACED, 281 CgroupId: xwingCgroupId, 282 IP: &flowpb.IP{ 283 Source: xwingIPv4, 284 Destination: deathstarServiceV4, 285 IpVersion: flowpb.IPVersion_IPv4, 286 }, 287 L4: &flowpb.Layer4{Protocol: &flowpb.Layer4_TCP{TCP: &flowpb.TCP{ 288 DestinationPort: deathstarServicePort, 289 }}}, 290 Source: &flowpb.Endpoint{ 291 ID: xwingEndpoint, 292 Identity: xwingIdentity, 293 PodName: xwingPodName, 294 Namespace: xwingPodNamespace, 295 Labels: xwingLabels, 296 }, 297 Destination: &flowpb.Endpoint{}, 298 DestinationNames: []string{deathstarServiceDomain}, 299 DestinationService: &flowpb.Service{ 300 Name: deathstarServiceName, 301 Namespace: deathstarServiceNamespace, 302 }, 303 EventType: &flowpb.CiliumEventType{ 304 Type: monitorAPI.MessageTypeTraceSock, 305 SubType: monitor.XlatePointPreDirectionFwd, 306 }, 307 SockXlatePoint: monitor.XlatePointPreDirectionFwd, 308 Summary: "TCP", 309 }, 310 }, 311 { 312 name: "post-translate v4 xwing to remote pod ip", 313 msg: monitor.TraceSockNotify{ 314 Type: monitorAPI.MessageTypeTraceSock, 315 XlatePoint: monitor.XlatePointPostDirectionFwd, 316 DstIP: mustParseIP(deathstarAltIPv4), 317 DstPort: deathstarTargetPort, 318 CgroupId: xwingCgroupId, 319 L4Proto: monitor.L4ProtocolTCP, 320 }, 321 skipUnknownCGroupIDs: true, 322 flow: &flowpb.Flow{ 323 Type: flowpb.FlowType_SOCK, 324 Verdict: flowpb.Verdict_TRANSLATED, 325 CgroupId: xwingCgroupId, 326 IP: &flowpb.IP{ 327 Source: xwingIPv4, 328 Destination: deathstarAltIPv4, 329 IpVersion: flowpb.IPVersion_IPv4, 330 }, 331 L4: &flowpb.Layer4{Protocol: &flowpb.Layer4_TCP{TCP: &flowpb.TCP{ 332 DestinationPort: deathstarTargetPort, 333 }}}, 334 Source: &flowpb.Endpoint{ 335 ID: xwingEndpoint, 336 Identity: xwingIdentity, 337 PodName: xwingPodName, 338 Namespace: xwingPodNamespace, 339 Labels: xwingLabels, 340 }, 341 Destination: &flowpb.Endpoint{ 342 Identity: deathstarIdentity, 343 PodName: deathstarAltPodName, 344 Namespace: deathstarAltPodNamespace, 345 Labels: deathstarLabels, 346 }, 347 EventType: &flowpb.CiliumEventType{ 348 Type: monitorAPI.MessageTypeTraceSock, 349 SubType: monitor.XlatePointPostDirectionFwd, 350 }, 351 SockXlatePoint: monitor.XlatePointPostDirectionFwd, 352 Summary: "TCP", 353 }, 354 }, 355 { 356 name: "post-translate rev v6 xwing from service ip", 357 msg: monitor.TraceSockNotify{ 358 Type: monitorAPI.MessageTypeTraceSock, 359 XlatePoint: monitor.XlatePointPostDirectionRev, 360 DstIP: mustParseIP(deathstarServiceV6), 361 DstPort: deathstarServicePort, 362 CgroupId: xwingCgroupId, 363 L4Proto: monitor.L4ProtocolTCP, 364 Flags: monitor.TraceSockNotifyFlagIPv6, 365 }, 366 skipUnknownCGroupIDs: true, 367 flow: &flowpb.Flow{ 368 Type: flowpb.FlowType_SOCK, 369 Verdict: flowpb.Verdict_TRANSLATED, 370 CgroupId: xwingCgroupId, 371 IP: &flowpb.IP{ 372 Source: deathstarServiceV6, 373 Destination: xwingIPv6, 374 IpVersion: flowpb.IPVersion_IPv6, 375 }, 376 L4: &flowpb.Layer4{Protocol: &flowpb.Layer4_TCP{TCP: &flowpb.TCP{ 377 SourcePort: deathstarServicePort, 378 }}}, 379 Source: &flowpb.Endpoint{}, 380 SourceNames: []string{deathstarServiceDomain}, 381 SourceService: &flowpb.Service{ 382 Name: deathstarServiceName, 383 Namespace: deathstarServiceNamespace, 384 }, 385 Destination: &flowpb.Endpoint{ 386 ID: xwingEndpoint, 387 Identity: xwingIdentity, 388 PodName: xwingPodName, 389 Namespace: xwingPodNamespace, 390 Labels: xwingLabels, 391 }, 392 EventType: &flowpb.CiliumEventType{ 393 Type: monitorAPI.MessageTypeTraceSock, 394 SubType: monitor.XlatePointPostDirectionRev, 395 }, 396 SockXlatePoint: monitor.XlatePointPostDirectionRev, 397 Summary: "TCP", 398 }, 399 }, 400 } 401 402 p, err := New(logrus.New(), endpointGetter, identityGetter, dnsGetter, ipGetter, serviceGetter, cgroupGetter) 403 assert.Nil(t, err) 404 405 for _, tc := range tt { 406 t.Run(tc.name, func(t *testing.T) { 407 p.skipUnknownCGroupIDs = tc.skipUnknownCGroupIDs 408 data := tc.rawMsg 409 if data == nil { 410 buf := &bytes.Buffer{} 411 err := binary.Write(buf, byteorder.Native, &tc.msg) 412 assert.Nil(t, err) 413 data = buf.Bytes() 414 } 415 flow := &flowpb.Flow{} 416 err = p.Decode(data, flow) 417 if tc.errMsg != "" { 418 assert.ErrorContains(t, err, tc.errMsg) 419 } else { 420 assert.Nil(t, err) 421 require.EqualValues(t, tc.flow, flow) 422 } 423 }) 424 } 425 }