github.com/cilium/cilium@v1.16.2/pkg/hubble/parser/threefour/parser.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Hubble
     3  
     4  package threefour
     5  
     6  import (
     7  	"fmt"
     8  	"net/netip"
     9  	"strings"
    10  
    11  	"github.com/google/gopacket"
    12  	"github.com/google/gopacket/layers"
    13  	"github.com/sirupsen/logrus"
    14  	"google.golang.org/protobuf/types/known/wrapperspb"
    15  
    16  	pb "github.com/cilium/cilium/api/v1/flow"
    17  	"github.com/cilium/cilium/pkg/byteorder"
    18  	"github.com/cilium/cilium/pkg/hubble/parser/common"
    19  	"github.com/cilium/cilium/pkg/hubble/parser/errors"
    20  	"github.com/cilium/cilium/pkg/hubble/parser/getters"
    21  	ippkg "github.com/cilium/cilium/pkg/ip"
    22  	"github.com/cilium/cilium/pkg/lock"
    23  	"github.com/cilium/cilium/pkg/monitor"
    24  	monitorAPI "github.com/cilium/cilium/pkg/monitor/api"
    25  	"github.com/cilium/cilium/pkg/policy/correlation"
    26  )
    27  
    28  // Parser is a parser for L3/L4 payloads
    29  type Parser struct {
    30  	log            logrus.FieldLogger
    31  	endpointGetter getters.EndpointGetter
    32  	identityGetter getters.IdentityGetter
    33  	dnsGetter      getters.DNSGetter
    34  	ipGetter       getters.IPGetter
    35  	serviceGetter  getters.ServiceGetter
    36  	linkGetter     getters.LinkGetter
    37  
    38  	epResolver *common.EndpointResolver
    39  
    40  	// TODO: consider using a pool of these
    41  	packet *packet
    42  }
    43  
    44  // re-usable packet to avoid reallocating gopacket datastructures
    45  type packet struct {
    46  	lock.Mutex
    47  	decLayer *gopacket.DecodingLayerParser
    48  	Layers   []gopacket.LayerType
    49  	layers.Ethernet
    50  	layers.IPv4
    51  	layers.IPv6
    52  	layers.ICMPv4
    53  	layers.ICMPv6
    54  	layers.TCP
    55  	layers.UDP
    56  	layers.SCTP
    57  }
    58  
    59  // New returns a new L3/L4 parser
    60  func New(
    61  	log logrus.FieldLogger,
    62  	endpointGetter getters.EndpointGetter,
    63  	identityGetter getters.IdentityGetter,
    64  	dnsGetter getters.DNSGetter,
    65  	ipGetter getters.IPGetter,
    66  	serviceGetter getters.ServiceGetter,
    67  	linkGetter getters.LinkGetter,
    68  ) (*Parser, error) {
    69  	packet := &packet{}
    70  	packet.decLayer = gopacket.NewDecodingLayerParser(
    71  		layers.LayerTypeEthernet, &packet.Ethernet,
    72  		&packet.IPv4, &packet.IPv6,
    73  		&packet.ICMPv4, &packet.ICMPv6,
    74  		&packet.TCP, &packet.UDP, &packet.SCTP)
    75  	// Let packet.decLayer.DecodeLayers return a nil error when it
    76  	// encounters a layer it doesn't have a parser for, instead of returning
    77  	// an UnsupportedLayerType error.
    78  	packet.decLayer.IgnoreUnsupported = true
    79  
    80  	return &Parser{
    81  		log:            log,
    82  		dnsGetter:      dnsGetter,
    83  		endpointGetter: endpointGetter,
    84  		identityGetter: identityGetter,
    85  		ipGetter:       ipGetter,
    86  		serviceGetter:  serviceGetter,
    87  		linkGetter:     linkGetter,
    88  		epResolver:     common.NewEndpointResolver(log, endpointGetter, identityGetter, ipGetter),
    89  		packet:         packet,
    90  	}, nil
    91  }
    92  
    93  // Decode decodes the data from 'data' into 'decoded'
    94  func (p *Parser) Decode(data []byte, decoded *pb.Flow) error {
    95  	if len(data) == 0 {
    96  		return errors.ErrEmptyData
    97  	}
    98  
    99  	eventType := data[0]
   100  
   101  	var packetOffset int
   102  	var dn *monitor.DropNotify
   103  	var tn *monitor.TraceNotify
   104  	var pvn *monitor.PolicyVerdictNotify
   105  	var dbg *monitor.DebugCapture
   106  	var eventSubType uint8
   107  	var authType pb.AuthType
   108  
   109  	switch eventType {
   110  	case monitorAPI.MessageTypeDrop:
   111  		packetOffset = monitor.DropNotifyLen
   112  		dn = &monitor.DropNotify{}
   113  		if err := monitor.DecodeDropNotify(data, dn); err != nil {
   114  			return fmt.Errorf("failed to parse drop: %w", err)
   115  		}
   116  		eventSubType = dn.SubType
   117  	case monitorAPI.MessageTypeTrace:
   118  		tn = &monitor.TraceNotify{}
   119  		if err := monitor.DecodeTraceNotify(data, tn); err != nil {
   120  			return fmt.Errorf("failed to parse trace: %w", err)
   121  		}
   122  		eventSubType = tn.ObsPoint
   123  
   124  		if tn.ObsPoint != 0 {
   125  			decoded.TraceObservationPoint = pb.TraceObservationPoint(tn.ObsPoint)
   126  		} else {
   127  			// specifically handle the zero value in the observation enum so the json
   128  			// export and the API don't carry extra meaning with the zero value
   129  			decoded.TraceObservationPoint = pb.TraceObservationPoint_TO_ENDPOINT
   130  		}
   131  
   132  		packetOffset = (int)(tn.DataOffset())
   133  	case monitorAPI.MessageTypePolicyVerdict:
   134  		pvn = &monitor.PolicyVerdictNotify{}
   135  		if err := monitor.DecodePolicyVerdictNotify(data, pvn); err != nil {
   136  			return fmt.Errorf("failed to parse policy verdict: %w", err)
   137  		}
   138  		eventSubType = pvn.SubType
   139  		packetOffset = monitor.PolicyVerdictNotifyLen
   140  		authType = pb.AuthType(pvn.GetAuthType())
   141  	case monitorAPI.MessageTypeCapture:
   142  		dbg = &monitor.DebugCapture{}
   143  		if err := monitor.DecodeDebugCapture(data, dbg); err != nil {
   144  			return fmt.Errorf("failed to parse debug capture: %w", err)
   145  		}
   146  		eventSubType = dbg.SubType
   147  		packetOffset = monitor.DebugCaptureLen
   148  	default:
   149  		return errors.NewErrInvalidType(eventType)
   150  	}
   151  
   152  	if len(data) < packetOffset {
   153  		return fmt.Errorf("not enough bytes to decode %d", data)
   154  	}
   155  
   156  	p.packet.Lock()
   157  	defer p.packet.Unlock()
   158  
   159  	// Since v1.1.18, DecodeLayers returns a non-nil error for an empty packet, see
   160  	// https://github.com/google/gopacket/issues/846
   161  	// TODO: reconsider this check if the issue is fixed upstream
   162  	if len(data[packetOffset:]) > 0 {
   163  		err := p.packet.decLayer.DecodeLayers(data[packetOffset:], &p.packet.Layers)
   164  		if err != nil {
   165  			return err
   166  		}
   167  	} else {
   168  		// Truncate layers to avoid accidental re-use.
   169  		p.packet.Layers = p.packet.Layers[:0]
   170  	}
   171  
   172  	ether, ip, l4, srcIP, dstIP, srcPort, dstPort, summary := decodeLayers(p.packet)
   173  	if tn != nil && ip != nil {
   174  		if !tn.OriginalIP().IsUnspecified() {
   175  			// Ignore invalid IP - getters will handle invalid value.
   176  			srcIP, _ = ippkg.AddrFromIP(tn.OriginalIP())
   177  			// On SNAT the trace notification has OrigIP set to the pre
   178  			// translation IP and the source IP parsed from the header is the
   179  			// post translation IP. The check is here because sometimes we get
   180  			// trace notifications with OrigIP set to the header's IP
   181  			// (pre-translation events?)
   182  			if ip.GetSource() != srcIP.String() {
   183  				ip.SourceXlated = ip.GetSource()
   184  				ip.Source = srcIP.String()
   185  			}
   186  		}
   187  
   188  		ip.Encrypted = tn.IsEncrypted()
   189  	}
   190  
   191  	srcLabelID, dstLabelID := decodeSecurityIdentities(dn, tn, pvn)
   192  	datapathContext := common.DatapathContext{
   193  		SrcIP:                 srcIP,
   194  		SrcLabelID:            srcLabelID,
   195  		DstIP:                 dstIP,
   196  		DstLabelID:            dstLabelID,
   197  		TraceObservationPoint: decoded.TraceObservationPoint,
   198  	}
   199  	srcEndpoint := p.epResolver.ResolveEndpoint(srcIP, srcLabelID, datapathContext)
   200  	dstEndpoint := p.epResolver.ResolveEndpoint(dstIP, dstLabelID, datapathContext)
   201  	var sourceService, destinationService *pb.Service
   202  	if p.serviceGetter != nil {
   203  		sourceService = p.serviceGetter.GetServiceByAddr(srcIP, srcPort)
   204  		destinationService = p.serviceGetter.GetServiceByAddr(dstIP, dstPort)
   205  	}
   206  
   207  	decoded.Verdict = decodeVerdict(dn, tn, pvn)
   208  	decoded.AuthType = authType
   209  	decoded.DropReason = decodeDropReason(dn, pvn)
   210  	decoded.DropReasonDesc = pb.DropReason(decoded.DropReason)
   211  	decoded.Ethernet = ether
   212  	decoded.IP = ip
   213  	decoded.L4 = l4
   214  	decoded.Source = srcEndpoint
   215  	decoded.Destination = dstEndpoint
   216  	decoded.Type = pb.FlowType_L3_L4
   217  	decoded.SourceNames = p.resolveNames(dstEndpoint.ID, srcIP)
   218  	decoded.DestinationNames = p.resolveNames(srcEndpoint.ID, dstIP)
   219  	decoded.L7 = nil
   220  	decoded.IsReply = decodeIsReply(tn, pvn)
   221  	decoded.Reply = decoded.GetIsReply().GetValue() // false if GetIsReply() is nil
   222  	decoded.TrafficDirection = decodeTrafficDirection(srcEndpoint.ID, dn, tn, pvn)
   223  	decoded.EventType = decodeCiliumEventType(eventType, eventSubType)
   224  	decoded.TraceReason = decodeTraceReason(tn)
   225  	decoded.SourceService = sourceService
   226  	decoded.DestinationService = destinationService
   227  	decoded.PolicyMatchType = decodePolicyMatchType(pvn)
   228  	decoded.DebugCapturePoint = decodeDebugCapturePoint(dbg)
   229  	decoded.Interface = p.decodeNetworkInterface(tn, dbg)
   230  	decoded.ProxyPort = decodeProxyPort(dbg, tn)
   231  	decoded.Summary = summary
   232  
   233  	if p.endpointGetter != nil {
   234  		correlation.CorrelatePolicy(p.endpointGetter, decoded)
   235  	}
   236  
   237  	return nil
   238  }
   239  
   240  func (p *Parser) resolveNames(epID uint32, ip netip.Addr) (names []string) {
   241  	if p.dnsGetter != nil {
   242  		return p.dnsGetter.GetNamesOf(epID, ip)
   243  	}
   244  
   245  	return nil
   246  }
   247  
   248  func decodeLayers(packet *packet) (
   249  	ethernet *pb.Ethernet,
   250  	ip *pb.IP,
   251  	l4 *pb.Layer4,
   252  	sourceIP, destinationIP netip.Addr,
   253  	sourcePort, destinationPort uint16,
   254  	summary string) {
   255  	for _, typ := range packet.Layers {
   256  		summary = typ.String()
   257  		switch typ {
   258  		case layers.LayerTypeEthernet:
   259  			ethernet = decodeEthernet(&packet.Ethernet)
   260  		case layers.LayerTypeIPv4:
   261  			ip, sourceIP, destinationIP = decodeIPv4(&packet.IPv4)
   262  		case layers.LayerTypeIPv6:
   263  			ip, sourceIP, destinationIP = decodeIPv6(&packet.IPv6)
   264  		case layers.LayerTypeTCP:
   265  			l4, sourcePort, destinationPort = decodeTCP(&packet.TCP)
   266  			summary = "TCP Flags: " + getTCPFlags(packet.TCP)
   267  		case layers.LayerTypeUDP:
   268  			l4, sourcePort, destinationPort = decodeUDP(&packet.UDP)
   269  		case layers.LayerTypeSCTP:
   270  			l4, sourcePort, destinationPort = decodeSCTP(&packet.SCTP)
   271  		case layers.LayerTypeICMPv4:
   272  			l4 = decodeICMPv4(&packet.ICMPv4)
   273  			summary = "ICMPv4 " + packet.ICMPv4.TypeCode.String()
   274  		case layers.LayerTypeICMPv6:
   275  			l4 = decodeICMPv6(&packet.ICMPv6)
   276  			summary = "ICMPv6 " + packet.ICMPv6.TypeCode.String()
   277  		}
   278  	}
   279  
   280  	return
   281  }
   282  
   283  func decodeVerdict(dn *monitor.DropNotify, tn *monitor.TraceNotify, pvn *monitor.PolicyVerdictNotify) pb.Verdict {
   284  	switch {
   285  	case dn != nil:
   286  		return pb.Verdict_DROPPED
   287  	case tn != nil:
   288  		return pb.Verdict_FORWARDED
   289  	case pvn != nil:
   290  		if pvn.Verdict < 0 {
   291  			return pb.Verdict_DROPPED
   292  		}
   293  		if pvn.Verdict > 0 {
   294  			return pb.Verdict_REDIRECTED
   295  		}
   296  		if pvn.IsTrafficAudited() {
   297  			return pb.Verdict_AUDIT
   298  		}
   299  		return pb.Verdict_FORWARDED
   300  	}
   301  	return pb.Verdict_VERDICT_UNKNOWN
   302  }
   303  
   304  func decodeDropReason(dn *monitor.DropNotify, pvn *monitor.PolicyVerdictNotify) uint32 {
   305  	switch {
   306  	case dn != nil:
   307  		return uint32(dn.SubType)
   308  	case pvn != nil && pvn.Verdict < 0:
   309  		// if the flow was dropped, verdict equals the negative of the drop reason
   310  		return uint32(-pvn.Verdict)
   311  	}
   312  	return 0
   313  }
   314  
   315  func decodePolicyMatchType(pvn *monitor.PolicyVerdictNotify) uint32 {
   316  	if pvn != nil {
   317  		return uint32((pvn.Flags & monitor.PolicyVerdictNotifyFlagMatchType) >>
   318  			monitor.PolicyVerdictNotifyFlagMatchTypeBitOffset)
   319  	}
   320  	return 0
   321  }
   322  
   323  func decodeEthernet(ethernet *layers.Ethernet) *pb.Ethernet {
   324  	return &pb.Ethernet{
   325  		Source:      ethernet.SrcMAC.String(),
   326  		Destination: ethernet.DstMAC.String(),
   327  	}
   328  }
   329  
   330  func decodeIPv4(ipv4 *layers.IPv4) (ip *pb.IP, src, dst netip.Addr) {
   331  	// Ignore invalid IPs - getters will handle invalid values.
   332  	// IPs can be empty for Ethernet-only packets.
   333  	src, _ = ippkg.AddrFromIP(ipv4.SrcIP)
   334  	dst, _ = ippkg.AddrFromIP(ipv4.DstIP)
   335  	return &pb.IP{
   336  		Source:      ipv4.SrcIP.String(),
   337  		Destination: ipv4.DstIP.String(),
   338  		IpVersion:   pb.IPVersion_IPv4,
   339  	}, src, dst
   340  }
   341  
   342  func decodeIPv6(ipv6 *layers.IPv6) (ip *pb.IP, src, dst netip.Addr) {
   343  	// Ignore invalid IPs - getters will handle invalid values.
   344  	// IPs can be empty for Ethernet-only packets.
   345  	src, _ = ippkg.AddrFromIP(ipv6.SrcIP)
   346  	dst, _ = ippkg.AddrFromIP(ipv6.DstIP)
   347  	return &pb.IP{
   348  		Source:      ipv6.SrcIP.String(),
   349  		Destination: ipv6.DstIP.String(),
   350  		IpVersion:   pb.IPVersion_IPv6,
   351  	}, src, dst
   352  }
   353  
   354  func decodeTCP(tcp *layers.TCP) (l4 *pb.Layer4, src, dst uint16) {
   355  	return &pb.Layer4{
   356  		Protocol: &pb.Layer4_TCP{
   357  			TCP: &pb.TCP{
   358  				SourcePort:      uint32(tcp.SrcPort),
   359  				DestinationPort: uint32(tcp.DstPort),
   360  				Flags: &pb.TCPFlags{
   361  					FIN: tcp.FIN, SYN: tcp.SYN, RST: tcp.RST,
   362  					PSH: tcp.PSH, ACK: tcp.ACK, URG: tcp.URG,
   363  					ECE: tcp.ECE, CWR: tcp.CWR, NS: tcp.NS,
   364  				},
   365  			},
   366  		},
   367  	}, uint16(tcp.SrcPort), uint16(tcp.DstPort)
   368  }
   369  
   370  func decodeSCTP(sctp *layers.SCTP) (l4 *pb.Layer4, src, dst uint16) {
   371  	return &pb.Layer4{
   372  		Protocol: &pb.Layer4_SCTP{
   373  			SCTP: &pb.SCTP{
   374  				SourcePort:      uint32(sctp.SrcPort),
   375  				DestinationPort: uint32(sctp.DstPort),
   376  			},
   377  		},
   378  	}, uint16(sctp.SrcPort), uint16(sctp.DstPort)
   379  }
   380  
   381  func decodeUDP(udp *layers.UDP) (l4 *pb.Layer4, src, dst uint16) {
   382  	return &pb.Layer4{
   383  		Protocol: &pb.Layer4_UDP{
   384  			UDP: &pb.UDP{
   385  				SourcePort:      uint32(udp.SrcPort),
   386  				DestinationPort: uint32(udp.DstPort),
   387  			},
   388  		},
   389  	}, uint16(udp.SrcPort), uint16(udp.DstPort)
   390  }
   391  
   392  func decodeICMPv4(icmp *layers.ICMPv4) *pb.Layer4 {
   393  	return &pb.Layer4{
   394  		Protocol: &pb.Layer4_ICMPv4{ICMPv4: &pb.ICMPv4{
   395  			Type: uint32(icmp.TypeCode.Type()),
   396  			Code: uint32(icmp.TypeCode.Code()),
   397  		}},
   398  	}
   399  }
   400  
   401  func decodeICMPv6(icmp *layers.ICMPv6) *pb.Layer4 {
   402  	return &pb.Layer4{
   403  		Protocol: &pb.Layer4_ICMPv6{ICMPv6: &pb.ICMPv6{
   404  			Type: uint32(icmp.TypeCode.Type()),
   405  			Code: uint32(icmp.TypeCode.Code()),
   406  		}},
   407  	}
   408  }
   409  
   410  func decodeIsReply(tn *monitor.TraceNotify, pvn *monitor.PolicyVerdictNotify) *wrapperspb.BoolValue {
   411  	switch {
   412  	case tn != nil && tn.TraceReasonIsKnown():
   413  		if tn.TraceReasonIsEncap() || tn.TraceReasonIsDecap() {
   414  			return nil
   415  		}
   416  		// Reason was specified by the datapath, just reuse it.
   417  		return &wrapperspb.BoolValue{
   418  			Value: tn.TraceReasonIsReply(),
   419  		}
   420  	case pvn != nil && pvn.Verdict >= 0:
   421  		// Forwarded PolicyVerdictEvents are emitted for the first packet of
   422  		// connection, therefore we statically assume that they are not reply
   423  		// packets
   424  		return &wrapperspb.BoolValue{Value: false}
   425  	default:
   426  		// For other events, such as drops, we simply do not know if they were
   427  		// replies or not.
   428  		return nil
   429  	}
   430  }
   431  
   432  func decodeCiliumEventType(eventType, eventSubType uint8) *pb.CiliumEventType {
   433  	return &pb.CiliumEventType{
   434  		Type:    int32(eventType),
   435  		SubType: int32(eventSubType),
   436  	}
   437  }
   438  
   439  func decodeTraceReason(tn *monitor.TraceNotify) pb.TraceReason {
   440  	if tn == nil {
   441  		return pb.TraceReason_TRACE_REASON_UNKNOWN
   442  	}
   443  	// The Hubble protobuf enum values aren't 1:1 mapped with Cilium's datapath
   444  	// because we want pb.TraceReason_TRACE_REASON_UNKNOWN = 0 while in
   445  	// datapath monitor.TraceReasonUnknown = 5. The mapping works as follow:
   446  	switch {
   447  	// monitor.TraceReasonUnknown is mapped to pb.TraceReason_TRACE_REASON_UNKNOWN
   448  	case tn.TraceReason() == monitor.TraceReasonUnknown:
   449  		return pb.TraceReason_TRACE_REASON_UNKNOWN
   450  	// values before monitor.TraceReasonUnknown are "offset by one", e.g.
   451  	// TraceReasonCtEstablished = 1 → TraceReason_ESTABLISHED = 2 to make room
   452  	// for the zero value.
   453  	case tn.TraceReason() < monitor.TraceReasonUnknown:
   454  		return pb.TraceReason(tn.TraceReason()) + 1
   455  	// all values greater than monitor.TraceReasonUnknown are mapped 1:1 with
   456  	// the datapath values.
   457  	default:
   458  		return pb.TraceReason(tn.TraceReason())
   459  	}
   460  }
   461  
   462  func decodeSecurityIdentities(dn *monitor.DropNotify, tn *monitor.TraceNotify, pvn *monitor.PolicyVerdictNotify) (
   463  	sourceSecurityIdentiy, destinationSecurityIdentity uint32,
   464  ) {
   465  	switch {
   466  	case dn != nil:
   467  		sourceSecurityIdentiy = uint32(dn.SrcLabel)
   468  		destinationSecurityIdentity = uint32(dn.DstLabel)
   469  	case tn != nil:
   470  		sourceSecurityIdentiy = uint32(tn.SrcLabel)
   471  		destinationSecurityIdentity = uint32(tn.DstLabel)
   472  	case pvn != nil:
   473  		if pvn.IsTrafficIngress() {
   474  			sourceSecurityIdentiy = uint32(pvn.RemoteLabel)
   475  		} else {
   476  			destinationSecurityIdentity = uint32(pvn.RemoteLabel)
   477  		}
   478  	}
   479  
   480  	return
   481  }
   482  
   483  func decodeTrafficDirection(srcEP uint32, dn *monitor.DropNotify, tn *monitor.TraceNotify, pvn *monitor.PolicyVerdictNotify) pb.TrafficDirection {
   484  	if dn != nil && dn.Source != 0 {
   485  		// If the local endpoint at which the drop occurred is the same as the
   486  		// source of the dropped packet, we assume it was an egress flow. This
   487  		// implies that we also assume that dropped packets are not dropped
   488  		// reply packets of an ongoing connection.
   489  		if dn.Source == uint16(srcEP) {
   490  			return pb.TrafficDirection_EGRESS
   491  		}
   492  		return pb.TrafficDirection_INGRESS
   493  	}
   494  	if tn != nil && tn.Source != 0 {
   495  		// For trace events, we assume that packets may be reply packets of an
   496  		// ongoing connection. Therefore, we want to access the connection
   497  		// tracking result from the `Reason` field to invert the direction for
   498  		// reply packets. The datapath currently populates the `Reason` field
   499  		// with CT information for some observation points.
   500  		if tn.TraceReasonIsKnown() {
   501  			// true if the traffic source is the local endpoint, i.e. egress
   502  			isSourceEP := tn.Source == uint16(srcEP)
   503  			// when OrigIP is set, then the packet was SNATed
   504  			isSNATed := !tn.OriginalIP().IsUnspecified()
   505  			// true if the packet is a reply, i.e. reverse direction
   506  			isReply := tn.TraceReasonIsReply()
   507  
   508  			switch {
   509  			// Although technically the corresponding packet is ingressing the
   510  			// stack (TraceReasonEncryptOverlay traces are TraceToStack), it is
   511  			// ultimately originating from the local node and destinated to a
   512  			// remote node, so egress make more sense to expose at a high
   513  			// level.
   514  			case tn.TraceReason() == monitor.TraceReasonEncryptOverlay:
   515  				return pb.TrafficDirection_EGRESS
   516  			// isSourceEP != isReply ==
   517  			//  (isSourceEP && !isReply) || (!isSourceEP && isReply)
   518  			case isSourceEP != isReply:
   519  				return pb.TrafficDirection_EGRESS
   520  			case isSNATed:
   521  				return pb.TrafficDirection_EGRESS
   522  			}
   523  			return pb.TrafficDirection_INGRESS
   524  		}
   525  	}
   526  	if pvn != nil {
   527  		if pvn.IsTrafficIngress() {
   528  			return pb.TrafficDirection_INGRESS
   529  		}
   530  		return pb.TrafficDirection_EGRESS
   531  	}
   532  	return pb.TrafficDirection_TRAFFIC_DIRECTION_UNKNOWN
   533  }
   534  
   535  func getTCPFlags(tcp layers.TCP) string {
   536  	const (
   537  		syn         = "SYN"
   538  		ack         = "ACK"
   539  		rst         = "RST"
   540  		fin         = "FIN"
   541  		psh         = "PSH"
   542  		urg         = "URG"
   543  		ece         = "ECE"
   544  		cwr         = "CWR"
   545  		ns          = "NS"
   546  		maxTCPFlags = 9
   547  		comma       = ", "
   548  	)
   549  
   550  	info := make([]string, 0, maxTCPFlags)
   551  
   552  	if tcp.SYN {
   553  		info = append(info, syn)
   554  	}
   555  
   556  	if tcp.ACK {
   557  		info = append(info, ack)
   558  	}
   559  
   560  	if tcp.RST {
   561  		info = append(info, rst)
   562  	}
   563  
   564  	if tcp.FIN {
   565  		info = append(info, fin)
   566  	}
   567  
   568  	if tcp.PSH {
   569  		info = append(info, psh)
   570  	}
   571  
   572  	if tcp.URG {
   573  		info = append(info, urg)
   574  	}
   575  
   576  	if tcp.ECE {
   577  		info = append(info, ece)
   578  	}
   579  
   580  	if tcp.CWR {
   581  		info = append(info, cwr)
   582  	}
   583  
   584  	if tcp.NS {
   585  		info = append(info, ns)
   586  	}
   587  
   588  	return strings.Join(info, comma)
   589  }
   590  
   591  func decodeDebugCapturePoint(dbg *monitor.DebugCapture) pb.DebugCapturePoint {
   592  	if dbg == nil {
   593  		return pb.DebugCapturePoint_DBG_CAPTURE_POINT_UNKNOWN
   594  	}
   595  	return pb.DebugCapturePoint(dbg.SubType)
   596  }
   597  
   598  func (p *Parser) decodeNetworkInterface(tn *monitor.TraceNotify, dbg *monitor.DebugCapture) *pb.NetworkInterface {
   599  	ifIndex := uint32(0)
   600  	if tn != nil {
   601  		ifIndex = tn.Ifindex
   602  	} else if dbg != nil {
   603  		switch dbg.SubType {
   604  		case monitor.DbgCaptureDelivery,
   605  			monitor.DbgCaptureFromLb,
   606  			monitor.DbgCaptureAfterV46,
   607  			monitor.DbgCaptureAfterV64,
   608  			monitor.DbgCaptureSnatPre,
   609  			monitor.DbgCaptureSnatPost:
   610  			ifIndex = dbg.Arg1
   611  		}
   612  	}
   613  
   614  	if ifIndex == 0 {
   615  		return nil
   616  	}
   617  
   618  	var name string
   619  	if p.linkGetter != nil {
   620  		// if the interface is not found, `name` will be an empty string and thus
   621  		// omitted in the protobuf message
   622  		name, _ = p.linkGetter.GetIfNameCached(int(ifIndex))
   623  	}
   624  	return &pb.NetworkInterface{
   625  		Index: ifIndex,
   626  		Name:  name,
   627  	}
   628  }
   629  
   630  func decodeProxyPort(dbg *monitor.DebugCapture, tn *monitor.TraceNotify) uint32 {
   631  	if tn != nil && tn.ObsPoint == monitorAPI.TraceToProxy {
   632  		return uint32(tn.DstID)
   633  	} else if dbg != nil {
   634  		switch dbg.SubType {
   635  		case monitor.DbgCaptureProxyPre,
   636  			monitor.DbgCaptureProxyPost:
   637  			return byteorder.NetworkToHost32(dbg.Arg1)
   638  		}
   639  	}
   640  
   641  	return 0
   642  }