diff --git a/README.md b/README.md
index bd0d3bfb1f510ea44b913b10a476c7781a577165..7d94c135b063895cbd6ab8f027da3314ea25198c 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 [![Go Report Card](https://goreportcard.com/badge/github.com/netobserv/netobserv-ebpf-agent)](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)
 }