diff --git a/README.md b/README.md index bd0d3bfb1f510ea44b913b10a476c7781a577165..7d94c135b063895cbd6ab8f027da3314ea25198c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [](https://goreportcard.com/report/github.com/netobserv/netobserv-ebpf-agent) The Network Observability eBPF Agent allows collecting and aggregating all the ingress and -egress flows on a Linux host (required a Kernel 4.18+ with eBPF enabled). +egress flows on a Linux host (required a Kernel 5.8+ with eBPF enabled). * [How to build](#how-to-build) * [How to configure](#how-to-configure) diff --git a/pkg/ebpf/tracer.go b/pkg/ebpf/tracer.go index dc6986295a3f32b7cab9f505e6190b48e38e826d..530e8729e9a58e314253ac326b718e2098741927 100644 --- a/pkg/ebpf/tracer.go +++ b/pkg/ebpf/tracer.go @@ -45,6 +45,7 @@ const ( pcaRecordsMap = "packet_record" tcEgressFilterName = "tc/tc_egress_flow_parse" tcIngressFilterName = "tc/tc_ingress_flow_parse" + tcpFentryHook = "tcp_rcv_fentry" ) var log = logrus.WithField("component", "ebpf.FlowFetcher") @@ -85,6 +86,7 @@ type FlowFetcherConfig struct { FilterConfig *FilterConfig } +// nolint:cyclop func NewFlowFetcher(cfg *FlowFetcherConfig) (*FlowFetcher, error) { if err := rlimit.RemoveMemlock(); err != nil { log.WithError(err). @@ -168,19 +170,26 @@ func NewFlowFetcher(cfg *FlowFetcherConfig) (*FlowFetcher, error) { var rttFentryLink, rttKprobeLink link.Link if cfg.EnableRTT { - rttFentryLink, err = link.AttachTracing(link.TracingOptions{ - Program: objects.BpfPrograms.TcpRcvFentry, - }) - if err != nil { - log.Warningf("failed to attach the BPF program to tcpReceiveFentry: %v fallback to use kprobe", err) - // try to use kprobe for older kernels - rttKprobeLink, err = link.Kprobe("tcp_rcv_established", objects.TcpRcvKprobe, nil) + if !oldKernel { + rttFentryLink, err = link.AttachTracing(link.TracingOptions{ + Program: objects.BpfPrograms.TcpRcvFentry, + }) + if err == nil { + goto next + } if err != nil { - return nil, fmt.Errorf("failed to attach the BPF program to tcpReceiveKprobe: %w", err) + log.Warningf("failed to attach the BPF program to tcpReceiveFentry: %v fallback to use kprobe", err) + // Fall through to use kprobe } } + // try to use kprobe for older kernels + rttKprobeLink, err = link.Kprobe("tcp_rcv_established", objects.TcpRcvKprobe, nil) + if err != nil { + log.Warningf("failed to attach the BPF program to kprobe: %v", err) + return nil, fmt.Errorf("failed to attach the BPF program to tcpReceiveKprobe: %w", err) + } } - +next: // read events from igress+egress ringbuffer flows, err := ringbuf.NewReader(objects.DirectFlows) if err != nil { @@ -717,7 +726,6 @@ func kernelSpecificLoadAndAssign(oldKernel bool, spec *ebpf.CollectionSpec) (Bpf TcIngressPcaParse *ebpf.Program `ebpf:"tc_ingress_pca_parse"` TcxEgressPcaParse *ebpf.Program `ebpf:"tcx_egress_pca_parse"` TcxIngressPcaParse *ebpf.Program `ebpf:"tcx_ingress_pca_parse"` - TCPRcvFentry *ebpf.Program `ebpf:"tcp_rcv_fentry"` TCPRcvKprobe *ebpf.Program `ebpf:"tcp_rcv_kprobe"` } type NewBpfObjects struct { @@ -727,6 +735,8 @@ func kernelSpecificLoadAndAssign(oldKernel bool, spec *ebpf.CollectionSpec) (Bpf var newObjects NewBpfObjects // remove pktdrop hook from the spec delete(spec.Programs, pktDropHook) + // remove fentry hook from the spec + delete(spec.Programs, tcpFentryHook) newObjects.NewBpfPrograms = NewBpfPrograms{} if err := spec.LoadAndAssign(&newObjects, nil); err != nil { var ve *ebpf.VerifierError @@ -752,8 +762,8 @@ func kernelSpecificLoadAndAssign(oldKernel bool, spec *ebpf.CollectionSpec) (Bpf objects.TcIngressPcaParse = newObjects.TcIngressPcaParse objects.TcxEgressPcaParse = newObjects.TcxEgressPcaParse objects.TcxIngressPcaParse = newObjects.TcxIngressPcaParse - objects.TcpRcvFentry = newObjects.TCPRcvFentry objects.TcpRcvKprobe = newObjects.TCPRcvKprobe + objects.TcpRcvFentry = nil objects.KfreeSkb = nil } else { if err := spec.LoadAndAssign(&objects, nil); err != nil { diff --git a/pkg/pbflow/flow_grpc.pb.go b/pkg/pbflow/flow_grpc.pb.go index f9a4eb466ddc8314218821f97d135fb89e10abb2..ac00dda6d17c52850425c73da86bfb4ce5520881 100644 --- a/pkg/pbflow/flow_grpc.pb.go +++ b/pkg/pbflow/flow_grpc.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.1 // - protoc v3.19.4 // source: proto/flow.proto @@ -15,8 +15,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( Collector_Send_FullMethodName = "/pbflow.Collector/Send" @@ -49,20 +49,24 @@ func (c *collectorClient) Send(ctx context.Context, in *Records, opts ...grpc.Ca // CollectorServer is the server API for Collector service. // All implementations must embed UnimplementedCollectorServer -// for forward compatibility +// for forward compatibility. type CollectorServer interface { Send(context.Context, *Records) (*CollectorReply, error) mustEmbedUnimplementedCollectorServer() } -// UnimplementedCollectorServer must be embedded to have forward compatible implementations. -type UnimplementedCollectorServer struct { -} +// UnimplementedCollectorServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedCollectorServer struct{} func (UnimplementedCollectorServer) Send(context.Context, *Records) (*CollectorReply, error) { return nil, status.Errorf(codes.Unimplemented, "method Send not implemented") } func (UnimplementedCollectorServer) mustEmbedUnimplementedCollectorServer() {} +func (UnimplementedCollectorServer) testEmbeddedByValue() {} // UnsafeCollectorServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to CollectorServer will @@ -72,6 +76,13 @@ type UnsafeCollectorServer interface { } func RegisterCollectorServer(s grpc.ServiceRegistrar, srv CollectorServer) { + // If the following call pancis, it indicates UnimplementedCollectorServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Collector_ServiceDesc, srv) } diff --git a/pkg/pbpacket/packet_grpc.pb.go b/pkg/pbpacket/packet_grpc.pb.go index 5aa9685d9cccfae9f76d2fa5847cdf88038c5e6f..fce168986087e4284796661e07d9b7c329b1ddcb 100644 --- a/pkg/pbpacket/packet_grpc.pb.go +++ b/pkg/pbpacket/packet_grpc.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.4.0 +// - protoc-gen-go-grpc v1.5.1 // - protoc v3.19.4 // source: proto/packet.proto @@ -15,8 +15,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 const ( Collector_Send_FullMethodName = "/pbpacket.Collector/Send" @@ -49,20 +49,24 @@ func (c *collectorClient) Send(ctx context.Context, in *Packet, opts ...grpc.Cal // CollectorServer is the server API for Collector service. // All implementations must embed UnimplementedCollectorServer -// for forward compatibility +// for forward compatibility. type CollectorServer interface { Send(context.Context, *Packet) (*CollectorReply, error) mustEmbedUnimplementedCollectorServer() } -// UnimplementedCollectorServer must be embedded to have forward compatible implementations. -type UnimplementedCollectorServer struct { -} +// UnimplementedCollectorServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedCollectorServer struct{} func (UnimplementedCollectorServer) Send(context.Context, *Packet) (*CollectorReply, error) { return nil, status.Errorf(codes.Unimplemented, "method Send not implemented") } func (UnimplementedCollectorServer) mustEmbedUnimplementedCollectorServer() {} +func (UnimplementedCollectorServer) testEmbeddedByValue() {} // UnsafeCollectorServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to CollectorServer will @@ -72,6 +76,13 @@ type UnsafeCollectorServer interface { } func RegisterCollectorServer(s grpc.ServiceRegistrar, srv CollectorServer) { + // If the following call pancis, it indicates UnimplementedCollectorServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Collector_ServiceDesc, srv) }