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

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Hubble
     3  
     4  package sock
     5  
     6  import (
     7  	"fmt"
     8  	"net"
     9  	"net/netip"
    10  	"strings"
    11  
    12  	"github.com/sirupsen/logrus"
    13  
    14  	flowpb "github.com/cilium/cilium/api/v1/flow"
    15  	"github.com/cilium/cilium/pkg/hubble/parser/common"
    16  	"github.com/cilium/cilium/pkg/hubble/parser/errors"
    17  	"github.com/cilium/cilium/pkg/hubble/parser/getters"
    18  	ippkg "github.com/cilium/cilium/pkg/ip"
    19  	"github.com/cilium/cilium/pkg/logging/logfields"
    20  	"github.com/cilium/cilium/pkg/monitor"
    21  	monitorAPI "github.com/cilium/cilium/pkg/monitor/api"
    22  	"github.com/cilium/cilium/pkg/option"
    23  )
    24  
    25  // Parser is a parser for SockTraceNotify payloads
    26  type Parser struct {
    27  	log            logrus.FieldLogger
    28  	endpointGetter getters.EndpointGetter
    29  	identityGetter getters.IdentityGetter
    30  	dnsGetter      getters.DNSGetter
    31  	ipGetter       getters.IPGetter
    32  	serviceGetter  getters.ServiceGetter
    33  	cgroupGetter   getters.PodMetadataGetter
    34  	epResolver     *common.EndpointResolver
    35  
    36  	skipUnknownCGroupIDs bool
    37  }
    38  
    39  // New creates a new parser
    40  func New(log logrus.FieldLogger,
    41  	endpointGetter getters.EndpointGetter,
    42  	identityGetter getters.IdentityGetter,
    43  	dnsGetter getters.DNSGetter,
    44  	ipGetter getters.IPGetter,
    45  	serviceGetter getters.ServiceGetter,
    46  	cgroupGetter getters.PodMetadataGetter,
    47  ) (*Parser, error) {
    48  	return &Parser{
    49  		log:                  log,
    50  		endpointGetter:       endpointGetter,
    51  		identityGetter:       identityGetter,
    52  		dnsGetter:            dnsGetter,
    53  		ipGetter:             ipGetter,
    54  		serviceGetter:        serviceGetter,
    55  		cgroupGetter:         cgroupGetter,
    56  		epResolver:           common.NewEndpointResolver(log, endpointGetter, identityGetter, ipGetter),
    57  		skipUnknownCGroupIDs: option.Config.HubbleSkipUnknownCGroupIDs,
    58  	}, nil
    59  }
    60  
    61  // Decode takes a raw trace sock event payload obtained from the perf event ring
    62  // buffer and decodes it into a flow
    63  func (p *Parser) Decode(data []byte, decoded *flowpb.Flow) error {
    64  	if len(data) == 0 {
    65  		return errors.ErrEmptyData
    66  	}
    67  
    68  	eventType := data[0]
    69  	if eventType != monitorAPI.MessageTypeTraceSock {
    70  		return errors.NewErrInvalidType(eventType)
    71  	}
    72  
    73  	sock := &monitor.TraceSockNotify{}
    74  	if err := monitor.DecodeTraceSockNotify(data, sock); err != nil {
    75  		return fmt.Errorf("failed to parse sock trace event: %w", err)
    76  	}
    77  
    78  	ipVersion := decodeIPVersion(sock.Flags)
    79  	epIP, ok := p.decodeEndpointIP(sock.CgroupId, ipVersion)
    80  	if !ok && p.skipUnknownCGroupIDs {
    81  		// Skip events for which we cannot determine the endpoint ip based on
    82  		// the numeric cgroup id, since those events do not provide much value
    83  		// to end users.
    84  		return errors.ErrEventSkipped
    85  	}
    86  
    87  	// Ignore invalid IPs - getters will handle invalid values.
    88  	// IPs can be empty for Ethernet-only packets.
    89  	dstIP, _ := ippkg.AddrFromIP(sock.IP())
    90  	dstPort := sock.DstPort
    91  	srcIP, _ := ippkg.AddrFromIP(epIP)
    92  	srcPort := uint16(0) // source port is not known for TraceSock events
    93  
    94  	datapathContext := common.DatapathContext{
    95  		SrcIP:      srcIP,
    96  		SrcLabelID: 0,
    97  		DstIP:      dstIP,
    98  		DstLabelID: 0,
    99  	}
   100  	srcEndpoint := p.epResolver.ResolveEndpoint(srcIP, 0, datapathContext)
   101  	dstEndpoint := p.epResolver.ResolveEndpoint(dstIP, 0, datapathContext)
   102  
   103  	// On the reverse path, source and destination IP of the packet are reversed
   104  	isRevNat := decodeRevNat(sock.XlatePoint)
   105  	if isRevNat {
   106  		srcIP, dstIP = dstIP, srcIP
   107  		srcPort, dstPort = dstPort, srcPort
   108  		srcEndpoint, dstEndpoint = dstEndpoint, srcEndpoint
   109  	}
   110  
   111  	decoded.Verdict = decodeVerdict(sock.XlatePoint)
   112  	decoded.IP = decodeL3(srcIP, dstIP, ipVersion)
   113  	decoded.L4 = decodeL4(sock.L4Proto, srcPort, dstPort)
   114  	decoded.Source = srcEndpoint
   115  	decoded.SourceNames = p.resolveNames(dstEndpoint.GetID(), srcIP)
   116  	decoded.SourceService = p.decodeService(srcIP, srcPort)
   117  	decoded.Destination = dstEndpoint
   118  	decoded.DestinationService = p.decodeService(dstIP, dstPort)
   119  	decoded.DestinationNames = p.resolveNames(srcEndpoint.GetID(), dstIP)
   120  	decoded.Type = flowpb.FlowType_SOCK
   121  	decoded.EventType = decodeCiliumEventType(sock.Type, sock.XlatePoint)
   122  	decoded.SockXlatePoint = flowpb.SocketTranslationPoint(sock.XlatePoint)
   123  	decoded.SocketCookie = sock.SockCookie
   124  	decoded.CgroupId = sock.CgroupId
   125  	decoded.Summary = decodeSummary(sock)
   126  	return nil
   127  }
   128  
   129  func decodeIPVersion(flags uint8) flowpb.IPVersion {
   130  	if (flags & monitor.TraceSockNotifyFlagIPv6) != 0 {
   131  		return flowpb.IPVersion_IPv6
   132  	}
   133  	return flowpb.IPVersion_IPv4
   134  }
   135  
   136  func (p *Parser) decodeEndpointIP(cgroupId uint64, ipVersion flowpb.IPVersion) (ip net.IP, ok bool) {
   137  	if p.cgroupGetter != nil {
   138  		if m := p.cgroupGetter.GetPodMetadataForContainer(cgroupId); m != nil {
   139  			scopedLog := p.log.WithFields(logrus.Fields{
   140  				logfields.CGroupID:     cgroupId,
   141  				logfields.K8sPodName:   m.Name,
   142  				logfields.K8sNamespace: m.Namespace,
   143  			})
   144  
   145  			for _, podIP := range m.IPs {
   146  				isIPv6 := strings.Contains(podIP, ":")
   147  				if isIPv6 && ipVersion == flowpb.IPVersion_IPv6 ||
   148  					!isIPv6 && ipVersion == flowpb.IPVersion_IPv4 {
   149  					ip = net.ParseIP(podIP)
   150  					if ip == nil {
   151  						scopedLog.WithField(logfields.IPAddr, podIP).Debug("failed to parse pod IP")
   152  						return nil, false
   153  					}
   154  
   155  					return ip, true
   156  				}
   157  			}
   158  			scopedLog.Debug("no matching IP for pod")
   159  		}
   160  	}
   161  
   162  	return nil, false
   163  }
   164  
   165  func decodeL3(srcIP, dstIP netip.Addr, ipVersion flowpb.IPVersion) *flowpb.IP {
   166  	var srcIPStr, dstIPStr string
   167  	if srcIP.IsValid() {
   168  		srcIPStr = srcIP.String()
   169  	}
   170  	if dstIP.IsValid() {
   171  		dstIPStr = dstIP.String()
   172  	}
   173  
   174  	return &flowpb.IP{
   175  		Source:      srcIPStr,
   176  		Destination: dstIPStr,
   177  		IpVersion:   ipVersion,
   178  	}
   179  }
   180  
   181  func decodeL4(proto uint8, srcPort, dstPort uint16) *flowpb.Layer4 {
   182  	switch proto {
   183  	case monitor.L4ProtocolTCP:
   184  		return &flowpb.Layer4{
   185  			Protocol: &flowpb.Layer4_TCP{
   186  				TCP: &flowpb.TCP{
   187  					SourcePort:      uint32(srcPort),
   188  					DestinationPort: uint32(dstPort),
   189  				},
   190  			},
   191  		}
   192  	case monitor.L4ProtocolUDP:
   193  		return &flowpb.Layer4{
   194  			Protocol: &flowpb.Layer4_UDP{
   195  				UDP: &flowpb.UDP{
   196  					SourcePort:      uint32(srcPort),
   197  					DestinationPort: uint32(dstPort),
   198  				},
   199  			},
   200  		}
   201  	}
   202  
   203  	return nil
   204  }
   205  
   206  func (p *Parser) resolveNames(epID uint32, ip netip.Addr) (names []string) {
   207  	if p.dnsGetter != nil {
   208  		return p.dnsGetter.GetNamesOf(epID, ip)
   209  	}
   210  
   211  	return nil
   212  }
   213  
   214  func (p *Parser) decodeService(ip netip.Addr, port uint16) *flowpb.Service {
   215  	if p.serviceGetter != nil {
   216  		return p.serviceGetter.GetServiceByAddr(ip, port)
   217  	}
   218  
   219  	return nil
   220  }
   221  
   222  func decodeVerdict(xlatePoint uint8) flowpb.Verdict {
   223  	switch xlatePoint {
   224  	case monitor.XlatePointPreDirectionFwd,
   225  		monitor.XlatePointPreDirectionRev:
   226  		return flowpb.Verdict_TRACED
   227  	case monitor.XlatePointPostDirectionFwd,
   228  		monitor.XlatePointPostDirectionRev:
   229  		return flowpb.Verdict_TRANSLATED
   230  	}
   231  
   232  	return flowpb.Verdict_VERDICT_UNKNOWN
   233  }
   234  
   235  func decodeRevNat(xlatePoint uint8) bool {
   236  	switch xlatePoint {
   237  	case monitor.XlatePointPreDirectionRev,
   238  		monitor.XlatePointPostDirectionRev:
   239  		return true
   240  	}
   241  
   242  	return false
   243  }
   244  
   245  func decodeCiliumEventType(eventType, subtype uint8) *flowpb.CiliumEventType {
   246  	return &flowpb.CiliumEventType{
   247  		Type:    int32(eventType),
   248  		SubType: int32(subtype),
   249  	}
   250  }
   251  
   252  func decodeSummary(sock *monitor.TraceSockNotify) string {
   253  	switch sock.L4Proto {
   254  	case monitor.L4ProtocolTCP:
   255  		return "TCP"
   256  	case monitor.L4ProtocolUDP:
   257  		return "UDP"
   258  	default:
   259  		return "Unknown"
   260  	}
   261  }