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  }