github.com/cilium/cilium@v1.16.2/pkg/monitor/datapath_trace.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package monitor 5 6 import ( 7 "bufio" 8 "encoding/json" 9 "fmt" 10 "net" 11 "os" 12 "unsafe" 13 14 "github.com/cilium/cilium/pkg/byteorder" 15 "github.com/cilium/cilium/pkg/hubble/parser/getters" 16 "github.com/cilium/cilium/pkg/identity" 17 "github.com/cilium/cilium/pkg/monitor/api" 18 "github.com/cilium/cilium/pkg/types" 19 ) 20 21 const ( 22 // traceNotifyCommonLen is the minimum length required to determine the version of the TN event. 23 traceNotifyCommonLen = 16 24 // traceNotifyV0Len is the amount of packet data provided in a trace notification v0. 25 traceNotifyV0Len = 32 26 // traceNotifyV1Len is the amount of packet data provided in a trace notification v1. 27 traceNotifyV1Len = 48 28 ) 29 30 const ( 31 // TraceNotifyFlagIsIPv6 is set in TraceNotify.Flags when the 32 // notification refers to an IPv6 flow 33 TraceNotifyFlagIsIPv6 uint8 = 1 34 ) 35 36 const ( 37 TraceNotifyVersion0 = iota 38 TraceNotifyVersion1 39 ) 40 41 // TraceNotifyV0 is the common message format for versions 0 and 1. 42 // This struct needs to be kept in sync with the decodeTraceNotifyVersion0 43 // func. 44 type TraceNotifyV0 struct { 45 Type uint8 46 ObsPoint uint8 47 Source uint16 48 Hash uint32 49 OrigLen uint32 50 CapLen uint16 51 Version uint16 52 SrcLabel identity.NumericIdentity 53 DstLabel identity.NumericIdentity 54 DstID uint16 55 Reason uint8 56 Flags uint8 57 Ifindex uint32 58 // data 59 } 60 61 // decodeTraceNotifyVersion0 decodes the trace notify message in 'data' into 62 // the struct. This function needs to be kept in sync with the TraceNotifyV0 63 // struct. 64 func (tn *TraceNotifyV0) decodeTraceNotifyVersion0(data []byte) error { 65 // This eliminates the bounds check in the accesses to `data` below. 66 if l := len(data); l < traceNotifyV0Len { 67 return fmt.Errorf("unexpected TraceNotifyV0 data length, expected %d but got %d", traceNotifyV0Len, l) 68 } 69 70 tn.Type = data[0] 71 tn.ObsPoint = data[1] 72 tn.Source = byteorder.Native.Uint16(data[2:4]) 73 tn.Hash = byteorder.Native.Uint32(data[4:8]) 74 tn.OrigLen = byteorder.Native.Uint32(data[8:12]) 75 tn.CapLen = byteorder.Native.Uint16(data[12:14]) 76 tn.Version = byteorder.Native.Uint16(data[14:16]) 77 tn.SrcLabel = identity.NumericIdentity(byteorder.Native.Uint32(data[16:20])) 78 tn.DstLabel = identity.NumericIdentity(byteorder.Native.Uint32(data[20:24])) 79 tn.DstID = byteorder.Native.Uint16(data[24:26]) 80 tn.Reason = data[26] 81 tn.Flags = data[27] 82 tn.Ifindex = byteorder.Native.Uint32(data[28:32]) 83 84 return nil 85 } 86 87 // IsEncrypted returns true when the notification has the encrypt flag set, 88 // false otherwise. 89 func (n *TraceNotifyV0) IsEncrypted() bool { 90 return (n.Reason & TraceReasonEncryptMask) != 0 91 } 92 93 // TraceReason returns the trace reason for this notification, see the 94 // TraceReason* constants. 95 func (n *TraceNotifyV0) TraceReason() uint8 { 96 return n.Reason & ^TraceReasonEncryptMask 97 } 98 99 // TraceReasonIsKnown returns false when the trace reason is unknown, true 100 // otherwise. 101 func (n *TraceNotifyV0) TraceReasonIsKnown() bool { 102 return n.TraceReason() != TraceReasonUnknown 103 } 104 105 // TraceReasonIsReply returns true when the trace reason is TraceReasonCtReply, 106 // false otherwise. 107 func (n *TraceNotifyV0) TraceReasonIsReply() bool { 108 return n.TraceReason() == TraceReasonCtReply 109 } 110 111 // TraceReasonIsEncap returns true when the trace reason is encapsulation 112 // related, false otherwise. 113 func (n *TraceNotifyV0) TraceReasonIsEncap() bool { 114 switch n.TraceReason() { 115 case TraceReasonSRv6Encap, TraceReasonEncryptOverlay: 116 return true 117 } 118 return false 119 } 120 121 // TraceReasonIsDecap returns true when the trace reason is decapsulation 122 // related, false otherwise. 123 func (n *TraceNotifyV0) TraceReasonIsDecap() bool { 124 switch n.TraceReason() { 125 case TraceReasonSRv6Decap: 126 return true 127 } 128 return false 129 } 130 131 // TraceNotifyV1 is the version 1 message format. This struct needs to be kept 132 // in sync with the decodeTraceNotifyVersion1 func. 133 type TraceNotifyV1 struct { 134 TraceNotifyV0 135 OrigIP types.IPv6 136 // data 137 } 138 139 // decodeTraceNotifyVersion1 decodes the trace notify message in 'data' into 140 // the struct. This function needs to be kept in sync with the TraceNotifyV1 141 // struct. 142 func (tn *TraceNotifyV1) decodeTraceNotifyVersion1(data []byte) error { 143 if l := len(data); l < traceNotifyV1Len { 144 return fmt.Errorf("unexpected TraceNotifyV1 data length, expected %d but got %d", traceNotifyV1Len, l) 145 } 146 147 if err := tn.decodeTraceNotifyVersion0(data); err != nil { 148 return err 149 } 150 151 copy(tn.OrigIP[:], data[32:48]) 152 return nil 153 } 154 155 // TraceNotify is the message format of a trace notification in the BPF ring buffer 156 type TraceNotify TraceNotifyV1 157 158 var ( 159 traceNotifyLength = map[uint16]uint{ 160 TraceNotifyVersion0: traceNotifyV0Len, 161 TraceNotifyVersion1: traceNotifyV1Len, 162 } 163 ) 164 165 /* Reasons for forwarding a packet, keep in sync with api/v1/flow/flow.proto */ 166 const ( 167 TraceReasonPolicy = iota 168 TraceReasonCtEstablished 169 TraceReasonCtReply 170 TraceReasonCtRelated 171 TraceReasonCtDeprecatedReopened 172 TraceReasonUnknown 173 TraceReasonSRv6Encap 174 TraceReasonSRv6Decap 175 TraceReasonEncryptOverlay 176 // TraceReasonEncryptMask is the bit used to indicate encryption or not. 177 TraceReasonEncryptMask = uint8(0x80) 178 ) 179 180 /* keep in sync with api/v1/flow/flow.proto */ 181 var traceReasons = map[uint8]string{ 182 TraceReasonPolicy: "new", 183 TraceReasonCtEstablished: "established", 184 TraceReasonCtReply: "reply", 185 TraceReasonCtRelated: "related", 186 TraceReasonCtDeprecatedReopened: "reopened", 187 TraceReasonUnknown: "unknown", 188 TraceReasonSRv6Encap: "srv6-encap", 189 TraceReasonSRv6Decap: "srv6-decap", 190 TraceReasonEncryptOverlay: "encrypt-overlay", 191 } 192 193 // DecodeTraceNotify will decode 'data' into the provided TraceNotify structure 194 func DecodeTraceNotify(data []byte, tn *TraceNotify) error { 195 if len(data) < traceNotifyCommonLen { 196 return fmt.Errorf("Unknown trace event") 197 } 198 199 offset := unsafe.Offsetof(tn.Version) 200 length := unsafe.Sizeof(tn.Version) 201 version := byteorder.Native.Uint16(data[offset : offset+length]) 202 203 switch version { 204 case TraceNotifyVersion0: 205 return tn.decodeTraceNotifyVersion0(data) 206 case TraceNotifyVersion1: 207 return ((*TraceNotifyV1)(tn)).decodeTraceNotifyVersion1(data) 208 } 209 return fmt.Errorf("Unrecognized trace event (version %d)", version) 210 } 211 212 // dumpIdentity dumps the source and destination identities in numeric or 213 // human-readable format. 214 func (n *TraceNotify) dumpIdentity(buf *bufio.Writer, numeric DisplayFormat) { 215 if numeric { 216 fmt.Fprintf(buf, ", identity %d->%d", n.SrcLabel, n.DstLabel) 217 } else { 218 fmt.Fprintf(buf, ", identity %s->%s", n.SrcLabel, n.DstLabel) 219 } 220 } 221 222 func (n *TraceNotify) encryptReasonString() string { 223 if n.IsEncrypted() { 224 return "encrypted " 225 } 226 return "" 227 } 228 229 func (n *TraceNotify) traceReasonString() string { 230 if str, ok := traceReasons[n.TraceReason()]; ok { 231 return str 232 } 233 // NOTE: show the underlying datapath trace reason without excluding the 234 // encrypt mask. 235 return fmt.Sprintf("%d", n.Reason) 236 } 237 238 func (n *TraceNotify) traceSummary() string { 239 switch n.ObsPoint { 240 case api.TraceToLxc: 241 return fmt.Sprintf("-> endpoint %d", n.DstID) 242 case api.TraceToProxy: 243 pp := "" 244 if n.DstID != 0 { 245 pp = fmt.Sprintf(" port %d", n.DstID) 246 } 247 return "-> proxy" + pp 248 case api.TraceToHost: 249 return "-> host from" 250 case api.TraceToStack: 251 return "-> stack" 252 case api.TraceToOverlay: 253 return "-> overlay" 254 case api.TraceToNetwork: 255 return "-> network" 256 case api.TraceFromLxc: 257 return fmt.Sprintf("<- endpoint %d", n.Source) 258 case api.TraceFromProxy: 259 return "<- proxy" 260 case api.TraceFromHost: 261 return "<- host" 262 case api.TraceFromStack: 263 return "<- stack" 264 case api.TraceFromOverlay: 265 return "<- overlay" 266 case api.TraceFromNetwork: 267 return "<- network" 268 default: 269 return "unknown trace" 270 } 271 } 272 273 // OriginalIP returns the original source IP if reverse NAT was performed on 274 // the flow 275 func (n *TraceNotify) OriginalIP() net.IP { 276 if (n.Flags & TraceNotifyFlagIsIPv6) != 0 { 277 return n.OrigIP[:] 278 } 279 return n.OrigIP[:4] 280 } 281 282 // DataOffset returns the offset from the beginning of TraceNotify where the 283 // trace notify data begins. 284 // 285 // Returns zero for invalid or unknown TraceNotify messages. 286 func (n *TraceNotify) DataOffset() uint { 287 return traceNotifyLength[n.Version] 288 } 289 290 // DumpInfo prints a summary of the trace messages. 291 func (n *TraceNotify) DumpInfo(data []byte, numeric DisplayFormat, linkMonitor getters.LinkGetter) { 292 buf := bufio.NewWriter(os.Stdout) 293 hdrLen := n.DataOffset() 294 if enc := n.encryptReasonString(); enc != "" { 295 fmt.Fprintf(buf, "%s %s flow %#x ", 296 n.traceSummary(), enc, n.Hash) 297 } else { 298 fmt.Fprintf(buf, "%s flow %#x ", n.traceSummary(), n.Hash) 299 } 300 n.dumpIdentity(buf, numeric) 301 ifname := linkMonitor.Name(n.Ifindex) 302 fmt.Fprintf(buf, " state %s ifindex %s orig-ip %s: %s\n", n.traceReasonString(), 303 ifname, n.OriginalIP().String(), GetConnectionSummary(data[hdrLen:])) 304 buf.Flush() 305 } 306 307 // DumpVerbose prints the trace notification in human readable form 308 func (n *TraceNotify) DumpVerbose(dissect bool, data []byte, prefix string, numeric DisplayFormat, linkMonitor getters.LinkGetter) { 309 buf := bufio.NewWriter(os.Stdout) 310 fmt.Fprintf(buf, "%s MARK %#x FROM %d %s: %d bytes (%d captured), state %s", 311 prefix, n.Hash, n.Source, api.TraceObservationPoint(n.ObsPoint), n.OrigLen, n.CapLen, n.traceReasonString()) 312 313 if n.Ifindex != 0 { 314 ifname := linkMonitor.Name(n.Ifindex) 315 fmt.Fprintf(buf, ", interface %s", ifname) 316 } 317 318 if n.SrcLabel != 0 || n.DstLabel != 0 { 319 fmt.Fprintf(buf, ", ") 320 n.dumpIdentity(buf, numeric) 321 } 322 323 fmt.Fprintf(buf, ", orig-ip %s", n.OriginalIP().String()) 324 325 if n.DstID != 0 { 326 dst := "endpoint" 327 if n.ObsPoint == api.TraceToProxy { 328 dst = "proxy-port" 329 } 330 fmt.Fprintf(buf, ", to %s %d\n", dst, n.DstID) 331 } else { 332 fmt.Fprintf(buf, "\n") 333 } 334 335 hdrLen := n.DataOffset() 336 if n.CapLen > 0 && len(data) > int(hdrLen) { 337 Dissect(dissect, data[hdrLen:]) 338 } 339 buf.Flush() 340 } 341 342 func (n *TraceNotify) getJSON(data []byte, cpuPrefix string, linkMonitor getters.LinkGetter) (string, error) { 343 v := TraceNotifyToVerbose(n, linkMonitor) 344 v.CPUPrefix = cpuPrefix 345 hdrLen := n.DataOffset() 346 if n.CapLen > 0 && len(data) > int(hdrLen) { 347 v.Summary = GetDissectSummary(data[hdrLen:]) 348 } 349 350 ret, err := json.Marshal(v) 351 return string(ret), err 352 } 353 354 // DumpJSON prints notification in json format 355 func (n *TraceNotify) DumpJSON(data []byte, cpuPrefix string, linkMonitor getters.LinkGetter) { 356 resp, err := n.getJSON(data, cpuPrefix, linkMonitor) 357 if err == nil { 358 fmt.Println(resp) 359 } 360 } 361 362 // TraceNotifyVerbose represents a json notification printed by monitor 363 type TraceNotifyVerbose struct { 364 CPUPrefix string `json:"cpu,omitempty"` 365 Type string `json:"type,omitempty"` 366 Mark string `json:"mark,omitempty"` 367 Ifindex string `json:"ifindex,omitempty"` 368 State string `json:"state,omitempty"` 369 ObservationPoint string `json:"observationPoint"` 370 TraceSummary string `json:"traceSummary"` 371 372 Source uint16 `json:"source"` 373 Bytes uint32 `json:"bytes"` 374 SrcLabel identity.NumericIdentity `json:"srcLabel"` 375 DstLabel identity.NumericIdentity `json:"dstLabel"` 376 DstID uint16 `json:"dstID"` 377 378 Summary *DissectSummary `json:"summary,omitempty"` 379 } 380 381 // TraceNotifyToVerbose creates verbose notification from base TraceNotify 382 func TraceNotifyToVerbose(n *TraceNotify, linkMonitor getters.LinkGetter) TraceNotifyVerbose { 383 ifname := linkMonitor.Name(n.Ifindex) 384 return TraceNotifyVerbose{ 385 Type: "trace", 386 Mark: fmt.Sprintf("%#x", n.Hash), 387 Ifindex: ifname, 388 State: n.traceReasonString(), 389 ObservationPoint: api.TraceObservationPoint(n.ObsPoint), 390 TraceSummary: n.traceSummary(), 391 Source: n.Source, 392 Bytes: n.OrigLen, 393 SrcLabel: n.SrcLabel, 394 DstLabel: n.DstLabel, 395 DstID: n.DstID, 396 } 397 }