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 }