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  }