Skip to content
Snippets Groups Projects
Unverified Commit de8537f0 authored by Mohamed S. Mahmoud's avatar Mohamed S. Mahmoud Committed by GitHub
Browse files

NETOBSERV-1129: Add kerenl version chk to disable unsupported hooks (#144)


Signed-off-by: default avatarmsherif1234 <mmahmoud@redhat.com>
parent c0510ba4
No related branches found
No related tags found
No related merge requests found
No preview for this file type
No preview for this file type
...@@ -6,12 +6,14 @@ import ( ...@@ -6,12 +6,14 @@ import (
"io/fs" "io/fs"
"strings" "strings"
"github.com/netobserv/netobserv-ebpf-agent/pkg/ifaces"
"github.com/netobserv/netobserv-ebpf-agent/pkg/utils"
"github.com/cilium/ebpf" "github.com/cilium/ebpf"
"github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/btf"
"github.com/cilium/ebpf/link" "github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/ringbuf" "github.com/cilium/ebpf/ringbuf"
"github.com/cilium/ebpf/rlimit" "github.com/cilium/ebpf/rlimit"
"github.com/netobserv/netobserv-ebpf-agent/pkg/ifaces"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
...@@ -29,6 +31,8 @@ const ( ...@@ -29,6 +31,8 @@ const (
constSampling = "sampling" constSampling = "sampling"
constTraceMessages = "trace_messages" constTraceMessages = "trace_messages"
constEnableRtt = "enable_rtt" constEnableRtt = "enable_rtt"
tcpDropHook = "kfree_skb"
dnsTraceHook = "net_dev_queue"
) )
var log = logrus.WithField("component", "ebpf.FlowFetcher") var log = logrus.WithField("component", "ebpf.FlowFetcher")
...@@ -105,14 +109,53 @@ func NewFlowFetcher(cfg *FlowFetcherConfig) (*FlowFetcher, error) { ...@@ -105,14 +109,53 @@ func NewFlowFetcher(cfg *FlowFetcherConfig) (*FlowFetcher, error) {
return nil, fmt.Errorf("rewriting BPF constants definition: %w", err) return nil, fmt.Errorf("rewriting BPF constants definition: %w", err)
} }
if err := spec.LoadAndAssign(&objects, nil); err != nil { oldKernel := utils.IskernelOlderthan514()
var ve *ebpf.VerifierError
if errors.As(err, &ve) { // For older kernel (< 5.14) kfree_sbk drop hook doesn't exists
// Using %+v will print the whole verifier error, not just the last if oldKernel {
// few lines. // Here we define another structure similar to the bpf2go created one but w/o the hooks that does not exist in older kernel
log.Infof("Verifier error: %+v", ve) // Note: if new hooks are added in the future we need to update the following structures manually
type NewBpfPrograms struct {
EgressFlowParse *ebpf.Program `ebpf:"egress_flow_parse"`
IngressFlowParse *ebpf.Program `ebpf:"ingress_flow_parse"`
TraceNetPackets *ebpf.Program `ebpf:"trace_net_packets"`
}
type NewBpfObjects struct {
NewBpfPrograms
BpfMaps
}
var newObjects NewBpfObjects
// remove tcpdrop hook from the spec
delete(spec.Programs, tcpDropHook)
newObjects.NewBpfPrograms = NewBpfPrograms{}
if err := spec.LoadAndAssign(&newObjects, nil); err != nil {
var ve *ebpf.VerifierError
if errors.As(err, &ve) {
// Using %+v will print the whole verifier error, not just the last
// few lines.
log.Infof("Verifier error: %+v", ve)
}
return nil, fmt.Errorf("loading and assigning BPF objects: %w", err)
}
// Manually assign maps and programs to the original objects variable
// Note for any future maps or programs make sure to copy them manually here
objects.DirectFlows = newObjects.DirectFlows
objects.AggregatedFlows = newObjects.AggregatedFlows
objects.FlowSequences = newObjects.FlowSequences
objects.EgressFlowParse = newObjects.EgressFlowParse
objects.IngressFlowParse = newObjects.IngressFlowParse
objects.TraceNetPackets = newObjects.TraceNetPackets
objects.KfreeSkb = nil
} else {
if err := spec.LoadAndAssign(&objects, nil); err != nil {
var ve *ebpf.VerifierError
if errors.As(err, &ve) {
// Using %+v will print the whole verifier error, not just the last
// few lines.
log.Infof("Verifier error: %+v", ve)
}
return nil, fmt.Errorf("loading and assigning BPF objects: %w", err)
} }
return nil, fmt.Errorf("loading and assigning BPF objects: %w", err)
} }
/* /*
...@@ -123,8 +166,8 @@ func NewFlowFetcher(cfg *FlowFetcherConfig) (*FlowFetcher, error) { ...@@ -123,8 +166,8 @@ func NewFlowFetcher(cfg *FlowFetcherConfig) (*FlowFetcher, error) {
btf.FlushKernelSpec() btf.FlushKernelSpec()
var tcpDropsLink link.Link var tcpDropsLink link.Link
if cfg.TCPDrops { if cfg.TCPDrops && !oldKernel {
tcpDropsLink, err = link.Tracepoint("skb", "kfree_skb", objects.KfreeSkb, nil) tcpDropsLink, err = link.Tracepoint("skb", tcpDropHook, objects.KfreeSkb, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to attach the BPF program to kfree_skb tracepoint: %w", err) return nil, fmt.Errorf("failed to attach the BPF program to kfree_skb tracepoint: %w", err)
} }
...@@ -132,7 +175,7 @@ func NewFlowFetcher(cfg *FlowFetcherConfig) (*FlowFetcher, error) { ...@@ -132,7 +175,7 @@ func NewFlowFetcher(cfg *FlowFetcherConfig) (*FlowFetcher, error) {
var dnsTrackerLink link.Link var dnsTrackerLink link.Link
if cfg.DNSTracker { if cfg.DNSTracker {
dnsTrackerLink, err = link.Tracepoint("net", "net_dev_queue", objects.TraceNetPackets, nil) dnsTrackerLink, err = link.Tracepoint("net", dnsTraceHook, objects.TraceNetPackets, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to attach the BPF program to trace_net_packets: %w", err) return nil, fmt.Errorf("failed to attach the BPF program to trace_net_packets: %w", err)
} }
......
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.30.0 // protoc-gen-go v1.25.0-devel
// protoc v3.19.4 // protoc v3.14.0
// source: proto/flow.proto // source: proto/flow.proto
package pbflow package pbflow
......
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v3.19.4
// source: proto/flow.proto
package pbflow package pbflow
...@@ -18,10 +14,6 @@ import ( ...@@ -18,10 +14,6 @@ import (
// Requires gRPC-Go v1.32.0 or later. // Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7 const _ = grpc.SupportPackageIsVersion7
const (
Collector_Send_FullMethodName = "/pbflow.Collector/Send"
)
// CollectorClient is the client API for Collector service. // CollectorClient is the client API for Collector service.
// //
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
...@@ -39,7 +31,7 @@ func NewCollectorClient(cc grpc.ClientConnInterface) CollectorClient { ...@@ -39,7 +31,7 @@ func NewCollectorClient(cc grpc.ClientConnInterface) CollectorClient {
func (c *collectorClient) Send(ctx context.Context, in *Records, opts ...grpc.CallOption) (*CollectorReply, error) { func (c *collectorClient) Send(ctx context.Context, in *Records, opts ...grpc.CallOption) (*CollectorReply, error) {
out := new(CollectorReply) out := new(CollectorReply)
err := c.cc.Invoke(ctx, Collector_Send_FullMethodName, in, out, opts...) err := c.cc.Invoke(ctx, "/pbflow.Collector/Send", in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -84,7 +76,7 @@ func _Collector_Send_Handler(srv interface{}, ctx context.Context, dec func(inte ...@@ -84,7 +76,7 @@ func _Collector_Send_Handler(srv interface{}, ctx context.Context, dec func(inte
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: Collector_Send_FullMethodName, FullMethod: "/pbflow.Collector/Send",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CollectorServer).Send(ctx, req.(*Records)) return srv.(CollectorServer).Send(ctx, req.(*Records))
......
...@@ -3,6 +3,17 @@ package utils ...@@ -3,6 +3,17 @@ package utils
import ( import (
"fmt" "fmt"
"net" "net"
"regexp"
"strconv"
"strings"
"syscall"
"github.com/sirupsen/logrus"
)
var (
getCurrentKernelVersion = currentKernelVersion
log = logrus.WithField("component", "utils")
) )
// GetSocket returns socket string in the correct format based on address family // GetSocket returns socket string in the correct format based on address family
...@@ -14,3 +25,69 @@ func GetSocket(hostIP string, hostPort int) string { ...@@ -14,3 +25,69 @@ func GetSocket(hostIP string, hostPort int) string {
} }
return socket return socket
} }
func IskernelOlderthan514() bool {
kernelVersion514, err := kernelVersionFromReleaseString("5.14.0")
if err != nil {
log.Warnf("failed to get kernel version from release string: %v", err)
return false
}
currentVersion, err := getCurrentKernelVersion()
if err != nil {
log.Warnf("failed to get current kernel version: %v", err)
return false
}
if currentVersion < kernelVersion514 {
log.Infof("older kernel version not all hooks will be supported")
return true
}
return false
}
var versionRegex = regexp.MustCompile(`^(\d+)\.(\d+).(\d+).*$`)
// kernelVersionFromReleaseString converts a release string with format
// 4.4.2[-1] to a kernel version number in LINUX_VERSION_CODE format.
// That is, for kernel "a.b.c", the version number will be (a<<16 + b<<8 + c)
func kernelVersionFromReleaseString(releaseString string) (uint32, error) {
versionParts := versionRegex.FindStringSubmatch(releaseString)
if len(versionParts) != 4 {
return 0, fmt.Errorf("got invalid release version %q (expected format '4.3.2-1')", releaseString)
}
major, err := strconv.Atoi(versionParts[1])
if err != nil {
return 0, err
}
minor, err := strconv.Atoi(versionParts[2])
if err != nil {
return 0, err
}
patch, err := strconv.Atoi(versionParts[3])
if err != nil {
return 0, err
}
out := major*256*256 + minor*256 + patch
return uint32(out), nil
}
func currentKernelVersion() (uint32, error) {
var buf syscall.Utsname
if err := syscall.Uname(&buf); err != nil {
return 0, err
}
releaseString := strings.Trim(utsnameStr(buf.Release[:]), "\x00")
return kernelVersionFromReleaseString(releaseString)
}
func utsnameStr(in []int8) string {
out := make([]byte, len(in))
for i := 0; i < len(in); i++ {
if in[i] == 0 {
break
}
out = append(out, byte(in[i]))
}
return string(out)
}
package utils
import (
"errors"
"testing"
)
func TestIskernelOlderthan514(t *testing.T) {
tests := []struct {
name string
mockKernelVersion func() (uint32, error)
want bool
}{
{
name: "Kernel version < 5.14.0",
mockKernelVersion: func() (uint32, error) {
ver, _ := kernelVersionFromReleaseString("5.13.0")
return ver, nil
},
want: true,
},
{
name: "Kernel version = 5.14.0",
mockKernelVersion: func() (uint32, error) {
ver, _ := kernelVersionFromReleaseString("5.14.0")
return ver, nil
},
want: false,
},
{
name: "Kernel version > 5.14.0",
mockKernelVersion: func() (uint32, error) {
ver, _ := kernelVersionFromReleaseString("5.15.0")
return ver, nil
},
want: false,
},
{
name: "Error getting kernel version",
mockKernelVersion: func() (uint32, error) {
return 0, errors.New("error")
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
getCurrentKernelVersion = tt.mockKernelVersion
got := IskernelOlderthan514()
if got != tt.want {
t.Errorf("%s: IskernelOlderthan514() = %v, want %v", tt.name, got, tt.want)
}
})
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment