pkg/elfparser/elf.go (881 lines of code) (raw):
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
//limitations under the License.
package elfparser
import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"syscall"
"golang.org/x/sys/unix"
"github.com/aws/aws-ebpf-sdk-go/pkg/cache"
constdef "github.com/aws/aws-ebpf-sdk-go/pkg/constants"
"github.com/aws/aws-ebpf-sdk-go/pkg/logger"
ebpf_maps "github.com/aws/aws-ebpf-sdk-go/pkg/maps"
ebpf_progs "github.com/aws/aws-ebpf-sdk-go/pkg/progs"
"github.com/aws/aws-ebpf-sdk-go/pkg/utils"
)
var (
bpfInsDefSize = (binary.Size(utils.BPFInsn{}) - 1)
bpfMapDefSize = binary.Size(ebpf_maps.BpfMapDef{})
probeProgParams = 1
kprobeProgParams = 2
tracepointProgParams = 3
)
var log = logger.Get()
var sdkCache = cache.Get()
type BpfSDKClient interface {
IncreaseRlimit() error
LoadBpfFile(path, customizedPinPath string) (map[string]BpfData, map[string]ebpf_maps.BpfMap, error)
LoadBpfFileWithCustomData(inputData BpfCustomData) (map[string]BpfData, map[string]ebpf_maps.BpfMap, error)
RecoverGlobalMaps() (map[string]ebpf_maps.BpfMap, error)
RecoverAllBpfProgramsAndMaps() (map[string]BpfData, error)
GetAllBpfProgramsAndMaps() (map[string]BpfData, error)
}
type BpfData struct {
Program ebpf_progs.BpfProgram // Return the program
Maps map[string]ebpf_maps.BpfMap // List of associated maps
}
type BpfCustomData struct {
FilePath string // Filepath for the BPF program
CustomPinPath string // PinPath
CustomMapSize map[string]int // Map of bpfMaps with custom size
}
type bpfSDKClient struct {
mapApi ebpf_maps.BpfMapAPIs
progApi ebpf_progs.BpfProgAPIs
}
type relocationEntry struct {
relOffset int
symbol elf.Symbol
}
type progEntry struct {
progSection *elf.Section
progType string
subSystem string
subProgType string
}
type elfLoader struct {
elfFile *elf.File
customizedPinPath string
bpfMapApi ebpf_maps.BpfMapAPIs
bpfProgApi ebpf_progs.BpfProgAPIs
license string
mapSection *elf.Section
mapSectionIndex int
reloSectionMap map[uint32]*elf.Section
progSectionMap map[uint32]progEntry
}
func New() BpfSDKClient {
return &bpfSDKClient{
mapApi: &ebpf_maps.BpfMap{},
progApi: &ebpf_progs.BpfProgram{},
}
}
var _ BpfSDKClient = &bpfSDKClient{}
// This is not needed 5.11 kernel onwards because per-cgroup mem limits
// https://lore.kernel.org/bpf/20201201215900.3569844-1-guro@fb.com/
func (b *bpfSDKClient) IncreaseRlimit() error {
err := unix.Setrlimit(unix.RLIMIT_MEMLOCK, &unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY})
if err != nil {
log.Infof("Failed to bump up the rlimit")
return err
}
return nil
}
func newElfLoader(elfFile *elf.File, bpfmapapi ebpf_maps.BpfMapAPIs, bpfprogapi ebpf_progs.BpfProgAPIs, customizedpinPath string) *elfLoader {
elfloader := &elfLoader{
elfFile: elfFile,
bpfMapApi: bpfmapapi,
bpfProgApi: bpfprogapi,
customizedPinPath: customizedpinPath,
reloSectionMap: make(map[uint32]*elf.Section),
progSectionMap: make(map[uint32]progEntry),
}
return elfloader
}
func (b *bpfSDKClient) LoadBpfFile(path, customizedPinPath string) (map[string]BpfData, map[string]ebpf_maps.BpfMap, error) {
bpfFile, err := os.Open(path)
if err != nil {
log.Errorf("LoadBpfFile failed to open")
return nil, nil, err
}
defer bpfFile.Close()
elfFile, err := elf.NewFile(bpfFile)
if err != nil {
return nil, nil, err
}
elfLoader := newElfLoader(elfFile, b.mapApi, b.progApi, customizedPinPath)
bpfLoadedProg, bpfLoadedMaps, err := elfLoader.doLoadELF(BpfCustomData{})
if err != nil {
return nil, nil, err
}
return bpfLoadedProg, bpfLoadedMaps, nil
}
func (b *bpfSDKClient) LoadBpfFileWithCustomData(inputData BpfCustomData) (map[string]BpfData, map[string]ebpf_maps.BpfMap, error) {
bpfFile, err := os.Open(inputData.FilePath)
if err != nil {
log.Errorf("LoadBpfFileWithCustomData failed to open")
return nil, nil, err
}
defer bpfFile.Close()
elfFile, err := elf.NewFile(bpfFile)
if err != nil {
return nil, nil, err
}
elfLoader := newElfLoader(elfFile, b.mapApi, b.progApi, inputData.CustomPinPath)
bpfLoadedProg, bpfLoadedMaps, err := elfLoader.doLoadELF(inputData)
if err != nil {
return nil, nil, err
}
return bpfLoadedProg, bpfLoadedMaps, nil
}
func (e *elfLoader) loadMap(parsedMapData []ebpf_maps.CreateEBPFMapInput) (map[string]ebpf_maps.BpfMap, error) {
programmedMaps := make(map[string]ebpf_maps.BpfMap)
log.Infof("Total maps found - %d", len(parsedMapData))
for index := 0; index < len(parsedMapData); index++ {
log.Infof("Loading maps")
loadedMaps := parsedMapData[index]
//Get Pinning info
mapNameStr := loadedMaps.Name
if len(e.customizedPinPath) != 0 {
mapNameStr = e.customizedPinPath + "_" + mapNameStr
}
pinPath := constdef.MAP_BPF_FS + mapNameStr
loadedMaps.PinOptions.PinPath = pinPath
bpfMap, err := (e.bpfMapApi).CreateBPFMap(loadedMaps)
if err != nil {
log.Errorf("failed to create map %v", err)
return nil, err
}
//Fill ID
if loadedMaps.PinOptions.Type == constdef.PIN_NONE.Index() {
mapInfo, err := (e.bpfMapApi).GetBPFmapInfo(bpfMap.MapFD)
if err != nil {
return nil, fmt.Errorf("failed to get map info '%s'", mapNameStr)
}
bpfMap.MapID = uint32(mapInfo.Id)
} else {
mapInfo, err := (e.bpfMapApi).GetMapFromPinPath(pinPath)
if err != nil {
return nil, fmt.Errorf("map '%s' doesn't exist", mapNameStr)
}
mapID := uint32(mapInfo.Id)
bpfMap.MapID = mapID
}
programmedMaps[loadedMaps.Name] = bpfMap
if IsMapGlobal(pinPath) {
//Add to global cache
sdkCache.Set(loadedMaps.Name, int(bpfMap.MapFD))
log.Infof("Added map Name %s and FD %d to SDK cache", loadedMaps.Name, bpfMap.MapFD)
}
}
return programmedMaps, nil
}
func (e *elfLoader) parseRelocationSection(reloSection *elf.Section, elfFile *elf.File) ([]relocationEntry, error) {
var result []relocationEntry
symbols, err := elfFile.Symbols()
if err != nil {
return nil, fmt.Errorf("unable to load symbols(): %v", err)
}
// Read section data
data, err := reloSection.Data()
if err != nil {
return nil, fmt.Errorf("unable to read data from section '%s': %v", reloSection.Name, err)
}
reader := bytes.NewReader(data)
for {
var err error
var offset, index int
switch elfFile.Class {
case elf.ELFCLASS64:
var relocEntry elf.Rel64
err = binary.Read(reader, elfFile.ByteOrder, &relocEntry)
index = int(elf.R_SYM64(relocEntry.Info)) - 1
offset = int(relocEntry.Off)
case elf.ELFCLASS32:
var relocEntry elf.Rel32
err = binary.Read(reader, elfFile.ByteOrder, &relocEntry)
index = int(elf.R_SYM32(relocEntry.Info)) - 1
offset = int(relocEntry.Off)
default:
return nil, fmt.Errorf("unsupported arch %v", elfFile.Class)
}
if err != nil {
// EOF. Nothing more to do.
if err == io.EOF {
return result, nil
}
return nil, err
}
// Validate the derived index value
if index >= len(symbols) {
return nil, fmt.Errorf("invalid Relocation section entry'%v': index %v does not exist",
reloSection, index)
}
log.Infof("Relocation section entry: %s @ %v", symbols[index].Name, offset)
result = append(result, relocationEntry{
relOffset: offset,
symbol: symbols[index],
})
}
}
func (e *elfLoader) loadProg(loadedProgData map[string]ebpf_progs.CreateEBPFProgInput, loadedMaps map[string]ebpf_maps.BpfMap) (map[string]BpfData, error) {
loadedprog := make(map[string]BpfData)
for _, pgmInput := range loadedProgData {
bpfData := BpfData{}
progFD, errno := e.bpfProgApi.LoadProg(pgmInput)
if progFD == -1 {
log.Infof("Failed to load prog", "error", errno)
return nil, fmt.Errorf("failed to Load the prog")
}
log.Infof("loaded prog with %d", progFD)
//Fill ID
progInfo, newProgFD, err := e.bpfProgApi.GetProgFromPinPath(pgmInput.PinPath)
if err != nil {
return nil, fmt.Errorf("failed to get ProgID")
}
unix.Close(int(newProgFD))
progID := int(progInfo.ID)
bpfData.Program = ebpf_progs.BpfProgram{
ProgID: progID,
ProgFD: progFD,
PinPath: pgmInput.PinPath,
ProgType: pgmInput.ProgType,
SubSystem: pgmInput.SubSystem,
SubProgType: pgmInput.SubProgType,
}
loadedprog[pgmInput.PinPath] = bpfData
}
return loadedprog, nil
}
func isProgTypeSupported(progType string) bool {
if progType != "xdp" && progType != "tc_cls" && progType != "tc_act" && progType != "kprobe" && progType != "tracepoint" && progType != "kretprobe" {
return false
}
return true
}
func parseProgType(splitProgType []string) (string, string, error) {
retrievedProgParams := len(splitProgType)
if retrievedProgParams != probeProgParams && retrievedProgParams != kprobeProgParams && retrievedProgParams != tracepointProgParams {
return "", "", fmt.Errorf("unsupported prog params")
}
var progEntrySubSystem string
var subProgEntryType string
if retrievedProgParams == kprobeProgParams {
subProgEntryType = strings.ToLower(splitProgType[1])
log.Infof("Found subprog type %s", subProgEntryType)
}
if retrievedProgParams == tracepointProgParams {
progEntrySubSystem = strings.ToLower(splitProgType[1])
subProgEntryType = strings.ToLower(splitProgType[2])
log.Infof("Found subprog type %s/%s", subProgEntryType, progEntrySubSystem)
}
return subProgEntryType, progEntrySubSystem, nil
}
func (e *elfLoader) parseSection() error {
for index, section := range e.elfFile.Sections {
if section.Name == "license" {
data, err := section.Data()
if err != nil {
return fmt.Errorf("failed to read data for section %s", section.Name)
}
e.license = string(data)
log.Infof("Found license - %s", e.license)
} else if section.Name == "maps" {
log.Infof("Found maps Section at Index %v", index)
e.mapSection = section
e.mapSectionIndex = index
} else if section.Type == elf.SHT_PROGBITS {
log.Infof("Found PROG Section at Index %v and Name %s", index, section.Name)
splitProgType := strings.Split(section.Name, "/")
progEntryType := strings.ToLower(splitProgType[0])
subProgEntryType, progEntrySubSystem, err := parseProgType(splitProgType)
if err != nil {
log.Info("Invalid prog type and subtype, supported is progtype such as tc or kprobe/progName or tracepoint/progType/progName")
return fmt.Errorf("invalid progType or subType")
}
log.Infof("Found the progType %s", progEntryType)
if !isProgTypeSupported(progEntryType) {
log.Infof("Not supported program %s", progEntryType)
continue
}
pEntry := progEntry{
progType: progEntryType,
subSystem: progEntrySubSystem,
subProgType: subProgEntryType,
progSection: section,
}
e.progSectionMap[uint32(index)] = pEntry
} else if section.Type == elf.SHT_REL {
log.Infof("Found a relocation section; Info:%v; Name: %s, Type: %s; Size: %v", section.Info,
section.Name, section.Type, section.Size)
e.reloSectionMap[section.Info] = section
}
}
if len(e.license) == 0 {
return fmt.Errorf("license missing in elf file")
}
return nil
}
func (e *elfLoader) getLicense() string {
return e.license
}
func (e *elfLoader) parseMap(customData BpfCustomData) ([]ebpf_maps.CreateEBPFMapInput, error) {
mapDefinitionSize := bpfMapDefSize
parsedMapData := []ebpf_maps.CreateEBPFMapInput{}
if e.mapSection == nil {
log.Infof("Bpf file has no map section so skipping parse")
return nil, nil
}
data, err := e.mapSection.Data()
if err != nil {
log.Infof("Error while loading section")
return nil, fmt.Errorf("error while loading section : %w", err)
}
if len(data) == 0 {
log.Infof("Missing data in mapsection")
return nil, fmt.Errorf("missing data in map section")
}
symbols, err := e.elfFile.Symbols()
if err != nil {
log.Infof("Get symbol failed")
return nil, fmt.Errorf("get symbols: %w", err)
}
for offset := 0; offset < len(data); offset += mapDefinitionSize {
mapData := ebpf_maps.CreateEBPFMapInput{
Type: uint32(binary.LittleEndian.Uint32(data[offset : offset+4])),
KeySize: uint32(binary.LittleEndian.Uint32(data[offset+4 : offset+8])),
ValueSize: uint32(binary.LittleEndian.Uint32(data[offset+8 : offset+12])),
MaxEntries: uint32(binary.LittleEndian.Uint32(data[offset+12 : offset+16])),
Flags: uint32(binary.LittleEndian.Uint32(data[offset+16 : offset+20])),
}
pinOptions := ebpf_maps.BpfMapPinOptions{
Type: uint32(binary.LittleEndian.Uint32(data[offset+20 : offset+24])),
}
mapData.PinOptions = &pinOptions
for _, sym := range symbols {
if int(sym.Section) == e.mapSectionIndex && int(sym.Value) == offset {
mapName := path.Base(sym.Name)
mapData.Name = mapName
}
}
log.Infof("Found map name %s", mapData.Name)
if len(customData.CustomMapSize) != 0 {
//Update the MaxEntries
if customSize, ok := customData.CustomMapSize[mapData.Name]; ok {
mapData.MaxEntries = uint32(customSize)
}
}
parsedMapData = append(parsedMapData, mapData)
}
return parsedMapData, nil
}
func (e *elfLoader) parseAndApplyRelocSection(progIndex uint32, loadedMaps map[string]ebpf_maps.BpfMap) ([]byte, map[int]string, error) {
progEntry := e.progSectionMap[progIndex]
reloSection := e.reloSectionMap[progIndex]
data, err := progEntry.progSection.Data()
if err != nil {
return nil, nil, err
}
log.Infof("Loading Program with relocation section; Info:%v; Name: %s, Type: %s; Size: %v", reloSection.Info,
reloSection.Name, reloSection.Type, reloSection.Size)
relocationEntries, err := e.parseRelocationSection(reloSection, e.elfFile)
if err != nil || len(relocationEntries) == 0 {
return nil, nil, fmt.Errorf("unable to parse relocation entries....")
}
log.Infof("Applying Relocations..")
associatedMaps := make(map[int]string)
for _, relocationEntry := range relocationEntries {
if relocationEntry.relOffset >= len(data) {
return nil, nil, fmt.Errorf("invalid offset for the relocation entry %d", relocationEntry.relOffset)
}
//eBPF has one 16-byte instruction: BPF_LD | BPF_DW | BPF_IMM which consists
//of two consecutive 'struct bpf_insn' 8-byte blocks and interpreted as single
//instruction that loads 64-bit immediate value into a dst_reg.
ebpfInstruction := &utils.BPFInsn{
Code: data[relocationEntry.relOffset],
DstReg: data[relocationEntry.relOffset+1] & 0xf,
SrcReg: data[relocationEntry.relOffset+1] >> 4,
Off: int16(binary.LittleEndian.Uint16(data[relocationEntry.relOffset+2:])),
Imm: int32(binary.LittleEndian.Uint32(data[relocationEntry.relOffset+4:])),
}
log.Infof("BPF Instruction code: %s; offset: %d; imm: %d", ebpfInstruction.Code, ebpfInstruction.Off, ebpfInstruction.Imm)
//Validate for Invalid BPF instructions
if ebpfInstruction.Code != (unix.BPF_LD | unix.BPF_IMM | unix.BPF_DW) {
return nil, nil, fmt.Errorf("invalid BPF instruction (at %d): %d",
relocationEntry.relOffset, ebpfInstruction.Code)
}
// Point BPF instruction to the FD of the map referenced. Update the last 4 bytes of
// instruction (immediate constant) with the map's FD.
// BPF_MEM | <size> | BPF_STX: *(size *) (dst_reg + off) = src_reg
// BPF_MEM | <size> | BPF_ST: *(size *) (dst_reg + off) = imm32
mapName := relocationEntry.symbol.Name
log.Infof("Map to be relocated; Name: %s", mapName)
var mapFD int
var map_id int
// Relocated maps can be defined in the same BPF file or defined elsewhere but
// using it here. So during relocation we search if it is a local map or
// it is a global map.
if progMap, ok := loadedMaps[mapName]; ok {
map_id = int(progMap.MapID)
associatedMaps[map_id] = mapName
mapFD = int(progMap.MapFD)
} else if globalMapFd, ok := sdkCache.Get(mapName); ok {
log.Infof("Found FD %d in SDK cache", globalMapFd)
mapFD = globalMapFd
} else {
return nil, nil, fmt.Errorf("failed to get map FD '%s' doesn't exist", mapName)
}
log.Infof("Map found. Replace the offset with corresponding Map FD: %v", mapFD)
ebpfInstruction.SrcReg = 1 //dummy value for now
ebpfInstruction.Imm = int32(mapFD)
copy(data[relocationEntry.relOffset:relocationEntry.relOffset+8], ebpfInstruction.ConvertBPFInstructionToByteStream())
log.Infof("From data: BPF Instruction code: %d; offset: %d; imm: %d",
uint8(data[relocationEntry.relOffset]),
uint16(binary.LittleEndian.Uint16(data[relocationEntry.relOffset+2:relocationEntry.relOffset+4])),
uint32(binary.LittleEndian.Uint32(data[relocationEntry.relOffset+4:relocationEntry.relOffset+8])))
}
return data, associatedMaps, nil
}
func (e *elfLoader) parseProg(loadedMaps map[string]ebpf_maps.BpfMap) (map[string]ebpf_progs.CreateEBPFProgInput, error) {
//Get prog data
var pgmList = make(map[string]ebpf_progs.CreateEBPFProgInput)
for progIndex, progEntry := range e.progSectionMap {
dataProg := progEntry.progSection
data, err := progEntry.progSection.Data()
if err != nil {
return nil, fmt.Errorf("failed to get progEntry Data")
}
if len(data) == 0 {
log.Infof("Missing data in prog Section")
return nil, fmt.Errorf("missing data in prog section")
}
var linkedMaps map[int]string
//Apply relocation
if e.reloSectionMap[progIndex] == nil {
log.Infof("Relocation is not needed")
} else {
progData, associatedMaps, err := e.parseAndApplyRelocSection(progIndex, loadedMaps)
if err != nil {
return nil, fmt.Errorf("failed to apply relocation: %v", err)
}
//Replace data with relocated data
data = progData
linkedMaps = associatedMaps
}
symbolTable, err := e.elfFile.Symbols()
if err != nil {
log.Infof("Get symbol failed")
return nil, fmt.Errorf("get symbols: %w", err)
}
// Iterate over the symbols in the symbol table
for _, symbol := range symbolTable {
// Check if the symbol is a function
if elf.ST_TYPE(symbol.Info) == elf.STT_FUNC {
// Check if sectionIndex matches
if uint32(symbol.Section) == uint32(progIndex) && elf.ST_BIND(symbol.Info) == elf.STB_GLOBAL {
// Check if the symbol's value (offset) is within the range of the section data
progSize := symbol.Size
secOff := symbol.Value
ProgName := symbol.Name
if secOff+progSize > dataProg.Size {
log.Infof("Section out of bound secOff %d - progSize %d for name %s and data size %d", progSize, secOff, ProgName, dataProg.Size)
return nil, fmt.Errorf("failed to Load the prog")
}
log.Infof("Sec '%s': found program '%s' at insn offset %d (%d bytes), code size %d insns (%d bytes)\n",
progEntry.progType, ProgName, secOff/uint64(bpfInsDefSize), secOff, progSize/uint64(bpfInsDefSize), progSize)
if symbol.Value >= dataProg.Addr && symbol.Value < dataProg.Addr+dataProg.Size {
dataStart := (symbol.Value - dataProg.Addr)
dataEnd := dataStart + progSize
programData := make([]byte, progSize)
copy(programData, data[dataStart:dataEnd])
pinLocation := ProgName
if len(e.customizedPinPath) != 0 {
pinLocation = e.customizedPinPath + "_" + ProgName
}
pinPath := constdef.PROG_BPF_FS + pinLocation
progMetaData := ebpf_progs.CreateEBPFProgInput{
ProgType: progEntry.progType,
SubSystem: progEntry.subSystem,
SubProgType: progEntry.subProgType,
ProgData: programData,
LicenseStr: e.license,
PinPath: pinPath,
InsDefSize: bpfInsDefSize,
AssociatedMaps: linkedMaps,
}
pgmList[pinPath] = progMetaData
} else {
log.Infof("Invalid ELF file\n")
return nil, fmt.Errorf("failed to Load the prog")
}
}
}
}
}
return pgmList, nil
}
func (e *elfLoader) doLoadELF(inputData BpfCustomData) (map[string]BpfData, map[string]ebpf_maps.BpfMap, error) {
var err error
//Parse all sections
if err := e.parseSection(); err != nil {
fmt.Println(err)
return nil, nil, fmt.Errorf("failed to parse sections in elf file")
}
//Parse Map
parsedMapData, err := e.parseMap(inputData)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse maps")
}
//Load Map
loadedMapData, err := e.loadMap(parsedMapData)
if err != nil {
return nil, nil, fmt.Errorf("failed to load maps")
}
//Parse Prog, need to pass loadedMapData for applying relocation
parsedProgData, err := e.parseProg(loadedMapData)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse prog")
}
//Load prog
loadedProgData, err := e.loadProg(parsedProgData, loadedMapData)
if err != nil {
return nil, nil, fmt.Errorf("failed to load progs")
}
//Link loaded map with prog
for pgmPinPath := range loadedProgData {
progMaps := make(map[string]ebpf_maps.BpfMap)
if pgmEntry, ok := loadedProgData[pgmPinPath]; ok {
associatedmapIDs, err := e.bpfProgApi.GetBPFProgAssociatedMapsIDs(pgmEntry.Program.ProgFD)
if err != nil {
log.Infof("Failed to load prog")
return nil, nil, fmt.Errorf("failed to Load the prog, get associatedmapIDs failed")
}
//walk thru all mapIDs and get loaded FDs and fill BPFData
for mapInfoIdx := 0; mapInfoIdx < len(associatedmapIDs); mapInfoIdx++ {
mapID := associatedmapIDs[mapInfoIdx]
if mapName, ok := parsedProgData[pgmPinPath].AssociatedMaps[int(mapID)]; ok {
progMaps[mapName] = loadedMapData[mapName]
log.Infof("Found %s with ID %d and FD %d", mapName, progMaps[mapName].MapID, progMaps[mapName].MapFD)
}
}
pgmEntry.Maps = progMaps
loadedProgData[pgmPinPath] = pgmEntry
}
}
return loadedProgData, loadedMapData, nil
}
func GetMapNameFromBPFPinPath(pinPath string) (string, string) {
splittedPinPath := strings.Split(pinPath, "/")
lastSegment := splittedPinPath[len(splittedPinPath)-1]
// Split at the first occurrence of "_"
mapNamespace, mapName, _ := strings.Cut(lastSegment, "_")
log.Infof("Found Identified - %s : %s", mapNamespace, mapName)
if mapName == "ingress_map" || mapName == "egress_map" || mapName == "ingress_pod_state_map" || mapName == "egress_pod_state_map" {
log.Infof("Adding %s -> %s", mapName, mapNamespace)
return mapName, mapNamespace
}
//This is global map, we cannot use global since there are multiple maps
log.Infof("Adding GLOBAL %s -> %s", mapName, mapName)
return mapName, mapName
}
func IsMapGlobal(pinPath string) bool {
mapName, _ := GetMapNameFromBPFPinPath(pinPath)
if mapName == "ingress_map" || mapName == "egress_map" || mapName == "ingress_pod_state_map" || mapName == "egress_pod_state_map" {
return false
}
return true
}
func (b *bpfSDKClient) RecoverGlobalMaps() (map[string]ebpf_maps.BpfMap, error) {
_, err := os.Stat(constdef.BPF_DIR_MNT)
if err != nil {
log.Infof("BPF FS director is not present")
return nil, fmt.Errorf("BPF directory is not present %v", err)
}
loadedGlobalMaps := make(map[string]ebpf_maps.BpfMap)
var statfs syscall.Statfs_t
if err := syscall.Statfs(constdef.BPF_DIR_MNT, &statfs); err == nil && statfs.Type == unix.BPF_FS_MAGIC {
if err := filepath.Walk(constdef.MAP_BPF_FS, func(pinPath string, fsinfo os.FileInfo, err error) error {
if err != nil {
return err
}
if !fsinfo.IsDir() {
log.Infof("Dumping pinpaths - ", pinPath)
if IsMapGlobal(pinPath) {
log.Infof("Found global pinpaths - ", pinPath)
bpfMapInfo, err := b.mapApi.GetMapFromPinPath(pinPath)
if err != nil {
log.Errorf("error getting mapInfo for Global pin path, this shouldn't happen")
return err
}
mapID := bpfMapInfo.Id
log.Infof("Got ID %d", mapID)
//Get map name
mapName, _ := GetMapNameFromBPFPinPath(pinPath)
log.Infof("Adding ID %d to name %s", mapID, mapName)
recoveredBpfMap := ebpf_maps.BpfMap{}
//Fill BPF map
recoveredBpfMap.MapID = uint32(mapID)
//Fill New FD since old FDs will be deleted on recovery
mapFD, err := utils.GetMapFDFromID(int(mapID))
if err != nil {
log.Infof("Unable to GetFDfromID and ret %d and err %s", int(mapFD), err)
return fmt.Errorf("unable to get FD: %s", err)
}
recoveredBpfMap.MapFD = uint32(mapFD)
log.Infof("Recovered map Name %s and FD %d", mapName, mapFD)
sdkCache.Set(mapName, mapFD)
//Fill BPF map metadata
recoveredBpfMapMetaData := ebpf_maps.CreateEBPFMapInput{
Type: bpfMapInfo.Type,
KeySize: bpfMapInfo.KeySize,
ValueSize: bpfMapInfo.ValueSize,
MaxEntries: bpfMapInfo.MaxEntries,
Flags: bpfMapInfo.MapFlags,
Name: mapName,
}
recoveredBpfMap.MapMetaData = recoveredBpfMapMetaData
loadedGlobalMaps[pinPath] = recoveredBpfMap
}
}
return nil
}); err != nil {
log.Infof("Error walking bpf map directory:", err)
return nil, fmt.Errorf("error walking the bpfdirectory %v", err)
}
} else {
log.Infof("error checking BPF FS, please make sure it is mounted %v", err)
return nil, fmt.Errorf("error checking BPF FS, please make sure it is mounted")
}
return loadedGlobalMaps, nil
}
func (b *bpfSDKClient) RecoverAllBpfProgramsAndMaps() (map[string]BpfData, error) {
_, err := os.Stat(constdef.BPF_DIR_MNT)
if err != nil {
log.Infof("BPF FS directory is not present")
return nil, fmt.Errorf("eBPF FS directory is not present %v", err)
}
var statfs syscall.Statfs_t
//Pass DS here
loadedPrograms := make(map[string]BpfData)
mapIDsToNames := make(map[int]string)
mapPodSelector := make(map[string]map[int]string)
mapIDsToFDs := make(map[int]int)
mapsDirExists := true
progsDirExists := true
_, err = os.Stat(constdef.MAP_BPF_FS)
if err != nil {
mapsDirExists = false
}
_, err = os.Stat(constdef.PROG_BPF_FS)
if err != nil {
progsDirExists = false
}
if err := syscall.Statfs(constdef.BPF_DIR_MNT, &statfs); err == nil && statfs.Type == unix.BPF_FS_MAGIC {
if mapsDirExists {
if err := filepath.Walk(constdef.MAP_BPF_FS, func(pinPath string, fsinfo os.FileInfo, err error) error {
if err != nil {
return err
}
if !fsinfo.IsDir() {
log.Infof("Dumping pinpaths - ", pinPath)
bpfMapInfo, err := b.mapApi.GetMapFromPinPath(pinPath)
if err != nil {
log.Infof("error getting mapInfo for pin path, this shouldn't happen")
return err
}
mapID := bpfMapInfo.Id
log.Infof("Got ID %d", mapID)
//Get map name
mapName, mapNamespace := GetMapNameFromBPFPinPath(pinPath)
mapIDsToNames[int(mapID)] = mapName
if IsMapGlobal(pinPath) {
return nil
}
//Fill New FD since old FDs will be deleted on recovery
mapFD, err := utils.GetMapFDFromID(int(mapID))
if err != nil {
log.Infof("Unable to GetFDfromID and ret %d and err %s", int(mapFD), err)
return fmt.Errorf("unable to get FD: %s", err)
}
log.Infof("Got FD %d", mapFD)
mapIDsToFDs[int(mapID)] = mapFD
log.Infof("Adding ID %d to name %s and NS %s", mapID, mapName, mapNamespace)
mapPodSelector[mapNamespace] = mapIDsToNames
}
return nil
}); err != nil {
log.Infof("Error walking bpf map directory:", err)
return nil, fmt.Errorf("failed walking the bpfdirectory %v", err)
}
}
if progsDirExists {
if err := filepath.Walk(constdef.PROG_BPF_FS, func(pinPath string, fsinfo os.FileInfo, err error) error {
if err != nil {
return err
}
if !fsinfo.IsDir() {
log.Infof("Dumping pinpaths - ", pinPath)
pgmData := ebpf_progs.BpfProgram{
PinPath: pinPath,
}
splittedPinPath := strings.Split(pinPath, "/")
podIdentifier := strings.SplitN(splittedPinPath[len(splittedPinPath)-1], "_", 2)
log.Infof("Found Identified - %s : %s", podIdentifier[0], podIdentifier[1])
progNamespace := podIdentifier[0]
if progNamespace == "global" {
log.Infof("Skipping global progs")
return nil
}
bpfProgInfo, progFD, err := (b.progApi).GetProgFromPinPath(pinPath)
if err != nil {
log.Infof("Failed to progInfo for pinPath %s", pinPath)
return err
}
pgmData.ProgFD = progFD
recoveredMapData := make(map[string]ebpf_maps.BpfMap)
if bpfProgInfo.NrMapIDs > 0 {
log.Infof("Have associated maps to link")
associatedBpfMapList, associatedBPFMapIDs, err := ebpf_progs.BpfGetMapInfoFromProgInfo(progFD, bpfProgInfo.NrMapIDs)
if err != nil {
log.Infof("Failed to get associated maps")
return err
}
for mapInfoIdx := 0; mapInfoIdx < len(associatedBpfMapList); mapInfoIdx++ {
bpfMapInfo := associatedBpfMapList[mapInfoIdx]
newMapID := associatedBPFMapIDs[mapInfoIdx]
recoveredBpfMap := ebpf_maps.BpfMap{}
//Fill BPF map
recoveredBpfMap.MapID = uint32(newMapID)
mapIds, ok := mapPodSelector[progNamespace]
if !ok {
log.Infof("Failed to get ID for %s", progNamespace)
return fmt.Errorf("failed to get err")
}
mapName := mapIds[int(recoveredBpfMap.MapID)]
var mapFD int
//Check in global cache for global maps
globalMapFd, ok := sdkCache.Get(mapName)
if ok {
log.Infof("Found FD %d in SDK cache", globalMapFd)
mapFD = globalMapFd
} else {
//Fill New FD since old FDs will be deleted on recovery
localMapFD, ok := mapIDsToFDs[int(newMapID)]
if !ok {
log.Infof("Unable to get FD from ID %d", int(newMapID))
return fmt.Errorf("unable to get FD")
}
mapFD = localMapFD
}
recoveredBpfMap.MapFD = uint32(mapFD)
log.Infof("Mapinfo MapName - %v", bpfMapInfo.Name)
//Fill BPF map metadata
recoveredBpfMapMetaData := ebpf_maps.CreateEBPFMapInput{
Type: bpfMapInfo.Type,
KeySize: bpfMapInfo.KeySize,
ValueSize: bpfMapInfo.ValueSize,
MaxEntries: bpfMapInfo.MaxEntries,
Flags: bpfMapInfo.MapFlags,
Name: mapName,
}
recoveredBpfMap.MapMetaData = recoveredBpfMapMetaData
recoveredMapData[mapName] = recoveredBpfMap
}
}
recoveredBPFdata := BpfData{
Program: pgmData,
Maps: recoveredMapData,
}
loadedPrograms[pinPath] = recoveredBPFdata
}
return nil
}); err != nil {
log.Infof("Error walking bpf prog directory:", err)
return nil, fmt.Errorf("failed walking the bpfdirectory %v", err)
}
}
} else {
log.Infof("error checking BPF FS, please make sure it is mounted %v", err)
return nil, fmt.Errorf("error checking BPF FS, please make sure it is mounted")
}
//Return DS here
return loadedPrograms, nil
}
func (b *bpfSDKClient) GetAllBpfProgramsAndMaps() (map[string]BpfData, error) {
_, err := os.Stat(constdef.BPF_DIR_MNT)
if err != nil {
log.Infof("BPF FS directory is not present")
return nil, fmt.Errorf("eBPF FS directory is not present %v", err)
}
var statfs syscall.Statfs_t
//Pass DS here
loadedPrograms := make(map[string]BpfData)
mapIDsToNames := make(map[int]string)
mapPodSelector := make(map[string]map[int]string)
mapsDirExists := true
progsDirExists := true
_, err = os.Stat(constdef.MAP_BPF_FS)
if err != nil {
mapsDirExists = false
}
_, err = os.Stat(constdef.PROG_BPF_FS)
if err != nil {
progsDirExists = false
}
if err := syscall.Statfs(constdef.BPF_DIR_MNT, &statfs); err == nil && statfs.Type == unix.BPF_FS_MAGIC {
if mapsDirExists {
if err := filepath.Walk(constdef.MAP_BPF_FS, func(pinPath string, fsinfo os.FileInfo, err error) error {
if err != nil {
return err
}
if !fsinfo.IsDir() {
log.Infof("Dumping pinpaths - ", pinPath)
bpfMapInfo, err := b.mapApi.GetMapFromPinPath(pinPath)
if err != nil {
log.Infof("error getting mapInfo for pin path, this shouldn't happen")
return err
}
mapID := bpfMapInfo.Id
log.Infof("Got ID %d", mapID)
//Get map name
mapName, mapNamespace := GetMapNameFromBPFPinPath(pinPath)
mapIDsToNames[int(mapID)] = mapName
log.Infof("Adding ID %d to name %s and NS %s", mapID, mapName, mapNamespace)
mapPodSelector[mapNamespace] = mapIDsToNames
}
return nil
}); err != nil {
log.Infof("Error walking bpfdirectory:", err)
return nil, fmt.Errorf("failed walking the bpfdirectory %v", err)
}
}
if progsDirExists {
if err := filepath.Walk(constdef.PROG_BPF_FS, func(pinPath string, fsinfo os.FileInfo, err error) error {
if err != nil {
return err
}
if !fsinfo.IsDir() {
log.Infof("Dumping pinpaths - ", pinPath)
pgmData := ebpf_progs.BpfProgram{
PinPath: pinPath,
}
splittedPinPath := strings.Split(pinPath, "/")
podIdentifier := strings.SplitN(splittedPinPath[len(splittedPinPath)-1], "_", 2)
log.Infof("Found Identified - %s : %s", podIdentifier[0], podIdentifier[1])
mapNamespace := podIdentifier[0]
if mapNamespace == "global" {
log.Infof("Skipping global progs")
return nil
}
bpfProgInfo, progFD, err := (b.progApi).GetProgFromPinPath(pinPath)
if err != nil {
log.Infof("Failed to progInfo for pinPath %s", pinPath)
return err
}
pgmData.ProgID = int(bpfProgInfo.ID)
//Conv type to string here
recoveredMapData := make(map[string]ebpf_maps.BpfMap)
if bpfProgInfo.NrMapIDs > 0 {
log.Infof("Have associated maps to link")
associatedBpfMapList, associatedBPFMapIDs, err := ebpf_progs.BpfGetMapInfoFromProgInfo(progFD, bpfProgInfo.NrMapIDs)
if err != nil {
log.Infof("Failed to get associated maps")
return err
}
//Close progFD..we don't need it
unix.Close(progFD)
for mapInfoIdx := 0; mapInfoIdx < len(associatedBpfMapList); mapInfoIdx++ {
bpfMapInfo := associatedBpfMapList[mapInfoIdx]
newMapID := associatedBPFMapIDs[mapInfoIdx]
recoveredBpfMap := ebpf_maps.BpfMap{}
//Fill BPF map
recoveredBpfMap.MapID = uint32(newMapID)
mapIds, ok := mapPodSelector[mapNamespace]
if !ok {
log.Infof("Failed to ID for %s", mapNamespace)
return fmt.Errorf("failed to get err")
}
mapName := mapIds[int(recoveredBpfMap.MapID)]
recoveredBpfMap.MapFD = 0
log.Infof("Mapinfo MapName - %v", bpfMapInfo.Name)
//Fill BPF map metadata
recoveredBpfMapMetaData := ebpf_maps.CreateEBPFMapInput{
Type: bpfMapInfo.Type,
KeySize: bpfMapInfo.KeySize,
ValueSize: bpfMapInfo.ValueSize,
MaxEntries: bpfMapInfo.MaxEntries,
Flags: bpfMapInfo.MapFlags,
Name: mapName,
}
recoveredBpfMap.MapMetaData = recoveredBpfMapMetaData
recoveredMapData[mapName] = recoveredBpfMap
}
}
recoveredBPFdata := BpfData{
Program: pgmData,
Maps: recoveredMapData,
}
loadedPrograms[pinPath] = recoveredBPFdata
}
return nil
}); err != nil {
log.Infof("Error walking bpfdirectory:", err)
return nil, fmt.Errorf("failed walking the bpfdirectory %v", err)
}
}
} else {
log.Infof("error checking BPF FS, please make sure it is mounted %v", err)
return nil, fmt.Errorf("error checking BPF FS, please make sure it is mounted")
}
//Return DS here
return loadedPrograms, nil
}