github.com/cilium/cilium@v1.16.2/pkg/monitor/format/format.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package format 5 6 import ( 7 "bytes" 8 "encoding/binary" 9 "encoding/gob" 10 "fmt" 11 12 "github.com/cilium/cilium/pkg/byteorder" 13 "github.com/cilium/cilium/pkg/hubble/parser/getters" 14 "github.com/cilium/cilium/pkg/monitor" 15 monitorAPI "github.com/cilium/cilium/pkg/monitor/api" 16 "github.com/cilium/cilium/pkg/monitor/payload" 17 ) 18 19 // Verbosity levels for formatting output. 20 type Verbosity uint8 21 22 const ( 23 msgSeparator = "------------------------------------------------------------------------------" 24 25 // INFO is the level of verbosity in which summaries of Drop and Capture 26 // messages are printed out when the monitor is invoked 27 INFO Verbosity = iota + 1 28 // DEBUG is the level of verbosity in which more information about packets 29 // is printed than in INFO mode. Debug, Drop, and Capture messages are printed. 30 DEBUG 31 // VERBOSE is the level of verbosity in which the most information possible 32 // about packets is printed out. Currently is not utilized. 33 VERBOSE 34 // JSON is the level of verbosity in which event information is printed out in json format 35 JSON 36 ) 37 38 // MonitorFormatter filters and formats monitor messages from a buffer. 39 type MonitorFormatter struct { 40 EventTypes monitorAPI.MessageTypeFilter 41 FromSource Uint16Flags 42 ToDst Uint16Flags 43 Related Uint16Flags 44 Hex bool 45 JSONOutput bool 46 Verbosity Verbosity 47 Numeric bool 48 49 linkMonitor getters.LinkGetter 50 } 51 52 // NewMonitorFormatter returns a new formatter with default configuration. 53 func NewMonitorFormatter(verbosity Verbosity, linkMonitor getters.LinkGetter) *MonitorFormatter { 54 return &MonitorFormatter{ 55 Hex: false, 56 EventTypes: monitorAPI.MessageTypeFilter{}, 57 FromSource: Uint16Flags{}, 58 ToDst: Uint16Flags{}, 59 Related: Uint16Flags{}, 60 JSONOutput: false, 61 Verbosity: verbosity, 62 Numeric: bool(monitor.DisplayLabel), 63 linkMonitor: linkMonitor, 64 } 65 } 66 67 // match checks if the event type, from endpoint and / or to endpoint match 68 // when they are supplied. The either part of from and to endpoint depends on 69 // related to, which can match on both. If either one of them is less than or 70 // equal to zero, then it is assumed user did not use them. 71 func (m *MonitorFormatter) match(messageType int, src uint16, dst uint16) bool { 72 if len(m.EventTypes) > 0 && !m.EventTypes.Contains(messageType) { 73 return false 74 } else if len(m.FromSource) > 0 && !m.FromSource.Has(src) { 75 return false 76 } else if len(m.ToDst) > 0 && !m.ToDst.Has(dst) { 77 return false 78 } else if len(m.Related) > 0 && !m.Related.Has(src) && !m.Related.Has(dst) { 79 return false 80 } 81 82 return true 83 } 84 85 // dropEvents prints out all the received drop notifications. 86 func (m *MonitorFormatter) dropEvents(prefix string, data []byte) { 87 dn := monitor.DropNotify{} 88 89 if err := binary.Read(bytes.NewReader(data), byteorder.Native, &dn); err != nil { 90 fmt.Printf("Error while parsing drop notification message: %s\n", err) 91 } 92 if m.match(monitorAPI.MessageTypeDrop, dn.Source, uint16(dn.DstID)) { 93 switch m.Verbosity { 94 case INFO, DEBUG: 95 dn.DumpInfo(data, monitor.DisplayFormat(m.Numeric)) 96 case JSON: 97 dn.DumpJSON(data, prefix) 98 default: 99 fmt.Println(msgSeparator) 100 dn.DumpVerbose(!m.Hex, data, prefix, monitor.DisplayFormat(m.Numeric)) 101 } 102 } 103 } 104 105 // traceEvents prints out all the received trace notifications. 106 func (m *MonitorFormatter) traceEvents(prefix string, data []byte) { 107 tn := monitor.TraceNotify{} 108 109 if err := monitor.DecodeTraceNotify(data, &tn); err != nil { 110 fmt.Printf("Error while parsing trace notification message: %s\n", err) 111 } 112 if m.match(monitorAPI.MessageTypeTrace, tn.Source, tn.DstID) { 113 switch m.Verbosity { 114 case INFO, DEBUG: 115 tn.DumpInfo(data, monitor.DisplayFormat(m.Numeric), m.linkMonitor) 116 case JSON: 117 tn.DumpJSON(data, prefix, m.linkMonitor) 118 default: 119 fmt.Println(msgSeparator) 120 tn.DumpVerbose(!m.Hex, data, prefix, monitor.DisplayFormat(m.Numeric), m.linkMonitor) 121 } 122 } 123 } 124 125 func (m *MonitorFormatter) traceSockEvents(prefix string, data []byte) { 126 tn := monitor.TraceSockNotify{} 127 128 if err := binary.Read(bytes.NewReader(data), byteorder.Native, &tn); err != nil { 129 fmt.Printf("Error while parsing socket trace notification message: %s\n", err) 130 } 131 // Currently only printed with the debug option. Extend it to info and json. 132 // GH issue: https://github.com/cilium/cilium/issues/21510 133 if m.Verbosity == DEBUG { 134 tn.DumpDebug(prefix) 135 } 136 } 137 138 func (m *MonitorFormatter) policyVerdictEvents(prefix string, data []byte) { 139 pn := monitor.PolicyVerdictNotify{} 140 141 if err := binary.Read(bytes.NewReader(data), byteorder.Native, &pn); err != nil { 142 fmt.Printf("Error while parsing policy notification message: %s\n", err) 143 } 144 145 if m.match(monitorAPI.MessageTypePolicyVerdict, pn.Source, uint16(pn.RemoteLabel)) { 146 pn.DumpInfo(data, monitor.DisplayFormat(m.Numeric)) 147 } 148 } 149 150 func (m *MonitorFormatter) recorderCaptureEvents(prefix string, data []byte) { 151 rc := monitor.RecorderCapture{} 152 153 if err := binary.Read(bytes.NewReader(data), byteorder.Native, &rc); err != nil { 154 fmt.Printf("Error while parsing capture record: %s\n", err) 155 } 156 157 if m.match(monitorAPI.MessageTypeRecCapture, 0, 0) { 158 rc.DumpInfo(data) 159 } 160 } 161 162 // debugEvents prints out all the debug messages. 163 func (m *MonitorFormatter) debugEvents(prefix string, data []byte) { 164 dm := monitor.DebugMsg{} 165 166 if err := binary.Read(bytes.NewReader(data), byteorder.Native, &dm); err != nil { 167 fmt.Printf("Error while parsing debug message: %s\n", err) 168 } 169 if m.match(monitorAPI.MessageTypeDebug, dm.Source, 0) { 170 switch m.Verbosity { 171 case INFO: 172 dm.DumpInfo(data) 173 case JSON: 174 dm.DumpJSON(prefix, m.linkMonitor) 175 default: 176 dm.Dump(prefix, m.linkMonitor) 177 } 178 } 179 } 180 181 // captureEvents prints out all the capture messages. 182 func (m *MonitorFormatter) captureEvents(prefix string, data []byte) { 183 dc := monitor.DebugCapture{} 184 185 if err := binary.Read(bytes.NewReader(data), byteorder.Native, &dc); err != nil { 186 fmt.Printf("Error while parsing debug capture message: %s\n", err) 187 } 188 if m.match(monitorAPI.MessageTypeCapture, dc.Source, 0) { 189 switch m.Verbosity { 190 case INFO, DEBUG: 191 dc.DumpInfo(data, m.linkMonitor) 192 case JSON: 193 dc.DumpJSON(data, prefix, m.linkMonitor) 194 default: 195 fmt.Println(msgSeparator) 196 dc.DumpVerbose(!m.Hex, data, prefix) 197 } 198 } 199 } 200 201 // logRecordEvents prints out LogRecord events 202 func (m *MonitorFormatter) logRecordEvents(prefix string, data []byte) { 203 buf := bytes.NewBuffer(data[1:]) 204 dec := gob.NewDecoder(buf) 205 206 lr := monitor.LogRecordNotify{} 207 if err := dec.Decode(&lr); err != nil { 208 fmt.Printf("Error while decoding LogRecord notification message: %s\n", err) 209 } 210 211 if m.match(monitorAPI.MessageTypeAccessLog, uint16(lr.SourceEndpoint.ID), uint16(lr.DestinationEndpoint.ID)) { 212 if m.Verbosity == JSON { 213 lr.DumpJSON() 214 } else { 215 lr.DumpInfo() 216 } 217 } 218 } 219 220 // agentEvents prints out agent events 221 func (m *MonitorFormatter) agentEvents(prefix string, data []byte) { 222 buf := bytes.NewBuffer(data[1:]) 223 dec := gob.NewDecoder(buf) 224 225 an := monitorAPI.AgentNotify{} 226 if err := dec.Decode(&an); err != nil { 227 fmt.Printf("Error while decoding agent notification message: %s\n", err) 228 } 229 230 if m.match(monitorAPI.MessageTypeAgent, 0, 0) { 231 if m.Verbosity == JSON { 232 an.DumpJSON() 233 } else { 234 an.DumpInfo() 235 } 236 } 237 } 238 239 // FormatSample prints an event from the provided raw data slice to stdout. 240 // 241 // For most monitor event types, 'data' corresponds to the 'data' field in 242 // bpf.PerfEventSample. Exceptions are MessageTypeAccessLog and 243 // MessageTypeAgent. 244 func (m *MonitorFormatter) FormatSample(data []byte, cpu int) { 245 prefix := fmt.Sprintf("CPU %02d:", cpu) 246 messageType := data[0] 247 248 switch messageType { 249 case monitorAPI.MessageTypeDrop: 250 m.dropEvents(prefix, data) 251 case monitorAPI.MessageTypeDebug: 252 m.debugEvents(prefix, data) 253 case monitorAPI.MessageTypeCapture: 254 m.captureEvents(prefix, data) 255 case monitorAPI.MessageTypeTrace: 256 m.traceEvents(prefix, data) 257 case monitorAPI.MessageTypeAccessLog: 258 m.logRecordEvents(prefix, data) 259 case monitorAPI.MessageTypeAgent: 260 m.agentEvents(prefix, data) 261 case monitorAPI.MessageTypePolicyVerdict: 262 m.policyVerdictEvents(prefix, data) 263 case monitorAPI.MessageTypeRecCapture: 264 m.recorderCaptureEvents(prefix, data) 265 case monitorAPI.MessageTypeTraceSock: 266 m.traceSockEvents(prefix, data) 267 default: 268 fmt.Printf("%s Unknown event: %+v\n", prefix, data) 269 } 270 } 271 272 // LostEvent formats a lost event using the specified payload parameters. 273 func LostEvent(lost uint64, cpu int) { 274 fmt.Printf("CPU %02d: Lost %d events\n", cpu, lost) 275 } 276 277 // FormatEvent formats an event from the specified payload to stdout. 278 // 279 // Returns true if the event was successfully printed, false otherwise. 280 func (m *MonitorFormatter) FormatEvent(pl *payload.Payload) bool { 281 switch pl.Type { 282 case payload.EventSample: 283 m.FormatSample(pl.Data, pl.CPU) 284 case payload.RecordLost: 285 LostEvent(pl.Lost, pl.CPU) 286 default: 287 return false 288 } 289 290 return true 291 }