github.com/jayanthvn/pure-gobpf@v0.0.0-20230623131354-8d1d959d9e0b/pkg/ebpf_progs/prog_loader.go (about)

     1  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License").
     4  // You may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  //limitations under the License.
    14  
    15  package ebpf_progs
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  	"runtime"
    22  	"syscall"
    23  	"unsafe"
    24  
    25  	"github.com/jayanthvn/pure-gobpf/pkg/ebpf_maps"
    26  	"github.com/jayanthvn/pure-gobpf/pkg/logger"
    27  	"github.com/jayanthvn/pure-gobpf/pkg/utils"
    28  	"github.com/vishvananda/netlink"
    29  	"golang.org/x/sys/unix"
    30  )
    31  
    32  type BpfProgAPIs interface {
    33  	PinProg(progFD uint32, pinPath string) error
    34  	UnPinProg(pinPath string) error
    35  	LoadProg(progType string, data []byte, licenseStr string, pinPath string, insDefSize int) (int, error)
    36  	BpfGetProgFromPinPath(pinPath string) (BpfProgInfo, int, error)
    37  	GetBPFProgAssociatedMapsIDs(progFD int) ([]uint32, error)
    38  }
    39  
    40  var log = logger.Get()
    41  
    42  type BPFProgram struct {
    43  	// return program name, prog FD and pinPath
    44  	ProgID      int
    45  	ProgFD      int
    46  	PinPath     string
    47  	ProgType    string
    48  	SubSystem   string
    49  	SubProgType string
    50  }
    51  
    52  type BpfProgInfo struct {
    53  	Type                 uint32
    54  	ID                   uint32
    55  	Tag                  [utils.BPFTagSize]byte
    56  	JitedProgLen         uint32
    57  	XlatedProgLen        uint32
    58  	JitedProgInsns       uint64
    59  	XlatedProgInsns      uint64
    60  	LoadTime             int64
    61  	CreatedByUID         uint32
    62  	NrMapIDs             uint32
    63  	MapIDs               uint64
    64  	Name                 [utils.BPFObjNameLen]byte
    65  	IfIndex              uint32
    66  	GPLCompatible        uint32 `strcut:"bitfield"`
    67  	Pad                  uint32 `strcut:"pad"`
    68  	NetnsDev             uint64
    69  	NetnsIno             uint64
    70  	NrJitedKsyms         uint32
    71  	NrJitedFuncLens      uint32
    72  	JitedKsyms           uint64
    73  	JitedFuncLens        uint64
    74  	BTFID                uint32
    75  	FuncInfoRecSize      uint32
    76  	FuncInfo             uint64
    77  	NrFuncInfo           uint32
    78  	NrLineInfo           uint32
    79  	LineInfo             uint64
    80  	JitedLineInfo        uint64
    81  	NrJitedLineInfo      uint32
    82  	LineInfoRecSize      uint32
    83  	JitedLineInfoRecSize uint32
    84  	NrProgTags           uint32
    85  	ProgTags             uint64
    86  	RunTimeNS            uint64
    87  	RunCnt               uint64
    88  }
    89  
    90  type BpfProgAttr struct {
    91  	prog_id    uint32
    92  	next_id    uint32
    93  	open_flags uint32
    94  }
    95  
    96  /*
    97   * struct { anonymous struct used by BPF_OBJ_GET_INFO_BY_FD
    98   *	__u32		bpf_fd;
    99   *	__u32		info_len;
   100   *	__aligned_u64	info;
   101   * } info;
   102  *
   103  */
   104  type BpfObjGetInfo struct {
   105  	bpf_fd   uint32
   106  	info_len uint32
   107  	info     uintptr
   108  }
   109  
   110  /*
   111   *	struct { anonymous struct used by BPF_OBJ_* commands
   112   *	__aligned_u64	pathname;
   113   *	__u32		bpf_fd;
   114   *	__u32		file_flags;
   115   * };
   116   */
   117  type BpfObjGet struct {
   118  	pathname   uintptr
   119  	bpf_fd     uint32
   120  	file_flags uint32
   121  }
   122  
   123  func mount_bpf_fs() error {
   124  	//var log = logger.Get()
   125  	log.Infof("Let's mount BPF FS")
   126  	err := syscall.Mount("bpf", utils.BPF_DIR_MNT, "bpf", 0, "mode=0700")
   127  	if err != nil {
   128  		log.Errorf("error mounting bpffs: %v", err)
   129  	}
   130  	return err
   131  }
   132  
   133  func (m *BPFProgram) PinProg(progFD uint32, pinPath string) error {
   134  	//var log = logger.Get()
   135  
   136  	var err error
   137  	if utils.IsfileExists(pinPath) {
   138  		log.Infof("Found file %s so deleting the path", pinPath)
   139  		err = utils.UnPinObject(pinPath)
   140  		if err != nil {
   141  			log.Infof("Failed to UnPinObject during pinning")
   142  			return err
   143  		}
   144  	}
   145  
   146  	err = os.MkdirAll(filepath.Dir(pinPath), 0755)
   147  	if err != nil {
   148  		log.Infof("error creating directory %q: %v", filepath.Dir(pinPath), err)
   149  		return fmt.Errorf("error creating directory %q: %v", filepath.Dir(pinPath), err)
   150  	}
   151  	_, err = os.Stat(pinPath)
   152  	if err == nil {
   153  		log.Infof("aborting, found file at %q", pinPath)
   154  		return fmt.Errorf("aborting, found file at %q", pinPath)
   155  	}
   156  	if err != nil && !os.IsNotExist(err) {
   157  		log.Infof("failed to stat %q: %v", pinPath, err)
   158  		return fmt.Errorf("failed to stat %q: %v", pinPath, err)
   159  	}
   160  
   161  	return utils.PinObject(progFD, pinPath)
   162  }
   163  
   164  func (m *BPFProgram) UnPinProg(pinPath string) error {
   165  	//var log = logger.Get()
   166  	err := utils.UnPinObject(pinPath)
   167  	if err != nil {
   168  		log.Infof("Failed to unpin prog")
   169  		return err
   170  	}
   171  	if m.ProgFD <= 0 {
   172  		log.Infof("FD is invalid or closed %d", m.ProgFD)
   173  		return nil
   174  	}
   175  	return unix.Close(int(m.ProgFD))
   176  }
   177  
   178  func (m *BPFProgram) LoadProg(progType string, data []byte, licenseStr string, pinPath string, insDefSize int) (int, error) {
   179  	//var log = logger.Get()
   180  
   181  	var prog_type uint32
   182  	switch progType {
   183  	case "xdp":
   184  		prog_type = uint32(netlink.BPF_PROG_TYPE_XDP)
   185  	case "tc_cls":
   186  		prog_type = uint32(netlink.BPF_PROG_TYPE_SCHED_CLS)
   187  	case "tc_act":
   188  		prog_type = uint32(netlink.BPF_PROG_TYPE_SCHED_ACT)
   189  	case "kprobe":
   190  		prog_type = uint32(netlink.BPF_PROG_TYPE_KPROBE)
   191  	case "kretprobe":
   192  		prog_type = uint32(netlink.BPF_PROG_TYPE_KPROBE)
   193  	case "tracepoint":
   194  		prog_type = uint32(netlink.BPF_PROG_TYPE_TRACEPOINT)
   195  	default:
   196  		prog_type = uint32(netlink.BPF_PROG_TYPE_UNSPEC)
   197  	}
   198  
   199  	logBuf := make([]byte, 65535)
   200  	program := netlink.BPFAttr{
   201  		ProgType: prog_type,
   202  		LogBuf:   uintptr(unsafe.Pointer(&logBuf[0])),
   203  		LogSize:  uint32(cap(logBuf) - 1),
   204  		LogLevel: 1,
   205  	}
   206  
   207  	program.Insns = uintptr(unsafe.Pointer(&data[0]))
   208  	program.InsnCnt = uint32(len(data) / insDefSize)
   209  
   210  	license := []byte(licenseStr)
   211  	program.License = uintptr(unsafe.Pointer(&license[0]))
   212  
   213  	fd, _, errno := unix.Syscall(unix.SYS_BPF,
   214  		utils.BPF_PROG_LOAD,
   215  		uintptr(unsafe.Pointer(&program)),
   216  		unsafe.Sizeof(program))
   217  	runtime.KeepAlive(data)
   218  	runtime.KeepAlive(license)
   219  
   220  	log.Infof("Load prog done with fd : %d", int(fd))
   221  	if errno != 0 {
   222  		log.Infof(string(logBuf))
   223  		return -1, errno
   224  	}
   225  
   226  	//Pin the prog
   227  	err := m.PinProg(uint32(fd), pinPath)
   228  	if err != nil {
   229  		log.Infof("pin prog failed %v", err)
   230  		return -1, err
   231  	}
   232  	return int(fd), nil
   233  }
   234  
   235  func (attr *BpfProgAttr) isBpfProgGetNextID() bool {
   236  	//var log = logger.Get()
   237  	ret, _, errno := unix.Syscall(
   238  		unix.SYS_BPF,
   239  		utils.BPF_PROG_GET_NEXT_ID,
   240  		uintptr(unsafe.Pointer(attr)),
   241  		unsafe.Sizeof(*attr),
   242  	)
   243  	if errno != 0 {
   244  		log.Infof("Done get_next_id for Prog - ret %d and err %s", int(ret), errno)
   245  		return false
   246  	}
   247  
   248  	attr.prog_id = attr.next_id
   249  	return true
   250  }
   251  
   252  func (attr *BpfProgAttr) BpfProgGetFDbyID() (int, error) {
   253  	//var log = logger.Get()
   254  	ret, _, errno := unix.Syscall(
   255  		unix.SYS_BPF,
   256  		utils.BPF_PROG_GET_FD_BY_ID,
   257  		uintptr(unsafe.Pointer(attr)),
   258  		unsafe.Sizeof(*attr),
   259  	)
   260  	if errno != 0 {
   261  		log.Infof("Failed to get Prog FD - ret %d and err %s", int(ret), errno)
   262  		return 0, errno
   263  	}
   264  	return int(ret), nil
   265  }
   266  
   267  func (objattr *BpfObjGetInfo) BpfGetProgramInfoForFD() error {
   268  	//var log = logger.Get()
   269  	ret, _, errno := unix.Syscall(
   270  		unix.SYS_BPF,
   271  		utils.BPF_OBJ_GET_INFO_BY_FD,
   272  		uintptr(unsafe.Pointer(objattr)),
   273  		unsafe.Sizeof(*objattr),
   274  	)
   275  	if errno != 0 {
   276  		log.Infof("Failed to get object info by FD - ret %d and err %s", int(ret), errno)
   277  		return errno
   278  	}
   279  	//TODO maybe get info here itself
   280  	return nil
   281  }
   282  
   283  func GetBPFprogInfo(progFD int) (BpfProgInfo, error) {
   284  	//var log = logger.Get()
   285  	var bpfProgInfo BpfProgInfo
   286  	objInfo := BpfObjGetInfo{
   287  		bpf_fd:   uint32(progFD),
   288  		info_len: uint32(unsafe.Sizeof(bpfProgInfo)),
   289  		info:     uintptr(unsafe.Pointer(&bpfProgInfo)),
   290  	}
   291  
   292  	err := objInfo.BpfGetProgramInfoForFD()
   293  	if err != nil {
   294  		log.Infof("Failed to get program Info for FD - ", progFD)
   295  		return BpfProgInfo{}, err
   296  	}
   297  
   298  	log.Infof("TYPE - %d", bpfProgInfo.Type)
   299  	log.Infof("Prog Name - %s", string(bpfProgInfo.Name[:]))
   300  	log.Infof("Maps linked - %d", bpfProgInfo.NrMapIDs)
   301  
   302  	return bpfProgInfo, nil
   303  }
   304  
   305  func (m *BPFProgram) GetBPFProgAssociatedMapsIDs(progFD int) ([]uint32, error) {
   306  	//var log = logger.Get()
   307  	bpfProgInfo, err := GetBPFprogInfo(progFD)
   308  
   309  	if bpfProgInfo.NrMapIDs <= 0 {
   310  		return nil, nil
   311  	}
   312  	numMaps := bpfProgInfo.NrMapIDs
   313  
   314  	associatedMaps := make([]uint32, numMaps)
   315  	newBpfProgInfo := BpfProgInfo{
   316  		NrMapIDs: numMaps,
   317  		MapIDs:   uint64(uintptr(unsafe.Pointer(&associatedMaps[0]))),
   318  	}
   319  	objInfo := BpfObjGetInfo{
   320  		bpf_fd:   uint32(progFD),
   321  		info_len: uint32(unsafe.Sizeof(newBpfProgInfo)),
   322  		info:     uintptr(unsafe.Pointer(&newBpfProgInfo)),
   323  	}
   324  
   325  	err = objInfo.BpfGetProgramInfoForFD()
   326  	if err != nil {
   327  		log.Infof("Failed to get program Info for FD - ", progFD)
   328  		return nil, err
   329  	}
   330  	return associatedMaps, nil
   331  }
   332  
   333  func BpfGetMapInfoFromProgInfo(progFD int, numMaps uint32) (BpfProgInfo, []ebpf_maps.BpfMapInfo, []int, []int, error) {
   334  	//var log = logger.Get()
   335  	associatedMaps := make([]uint32, numMaps)
   336  	newBpfProgInfo := BpfProgInfo{
   337  		NrMapIDs: numMaps,
   338  		MapIDs:   uint64(uintptr(unsafe.Pointer(&associatedMaps[0]))),
   339  	}
   340  
   341  	objInfo := BpfObjGetInfo{
   342  		bpf_fd:   uint32(progFD),
   343  		info_len: uint32(unsafe.Sizeof(newBpfProgInfo)),
   344  		info:     uintptr(unsafe.Pointer(&newBpfProgInfo)),
   345  	}
   346  
   347  	err := objInfo.BpfGetProgramInfoForFD()
   348  	if err != nil {
   349  		log.Infof("Failed to get program Info for FD - ", progFD)
   350  		return BpfProgInfo{}, nil, nil, nil, err
   351  	}
   352  
   353  	log.Infof("TYPE - %d", newBpfProgInfo.Type)
   354  	log.Infof("Prog Name - %s", unix.ByteSliceToString(newBpfProgInfo.Name[:]))
   355  	log.Infof("Maps linked - %d", newBpfProgInfo.NrMapIDs)
   356  	//Printing associated maps
   357  	loadedMaps := []ebpf_maps.BpfMapInfo{}
   358  	loadedMapsFDs := make([]int, 0)
   359  	loadedMapsIDs := make([]int, 0)
   360  	for mapIdx := 0; mapIdx < len(associatedMaps); mapIdx++ {
   361  		log.Infof("MAP ID - %d", associatedMaps[mapIdx])
   362  
   363  		mapfd, err := utils.GetMapFDFromID(int(associatedMaps[mapIdx]))
   364  		if err != nil {
   365  			log.Infof("Failed to get map Info")
   366  			return BpfProgInfo{}, nil, nil, nil, err
   367  		}
   368  		/*
   369  			fileAttr := ebpf_maps.BpfMapShowAttr{
   370  				Map_id: associatedMaps[mapIdx],
   371  			}
   372  			mapfd, err := fileAttr.BpfMapGetFDFromID()
   373  			if err != nil {
   374  				log.Infof("Failed to get map Info")
   375  				return BpfProgInfo{}, nil, nil, nil, err
   376  			}
   377  		*/
   378  		log.Infof("Found map FD - %d", mapfd)
   379  
   380  		bpfMapInfo, err := ebpf_maps.GetBPFmapInfo(mapfd)
   381  		if err != nil {
   382  			log.Infof("Failed to get map Info for FD", mapfd)
   383  			return BpfProgInfo{}, nil, nil, nil, err
   384  		}
   385  		loadedMaps = append(loadedMaps, bpfMapInfo)
   386  		loadedMapsFDs = append(loadedMapsFDs, mapfd)
   387  		loadedMapsIDs = append(loadedMapsIDs, int(associatedMaps[mapIdx]))
   388  	}
   389  	return newBpfProgInfo, loadedMaps, loadedMapsFDs, loadedMapsIDs, nil
   390  }
   391  
   392  func BpfGetAllProgramInfo() ([]BpfProgInfo, error) {
   393  	//var log = logger.Get()
   394  	loadedPrograms := []BpfProgInfo{}
   395  	attr := BpfProgAttr{}
   396  	log.Infof("In get all prog info")
   397  	for attr.isBpfProgGetNextID() {
   398  		log.Infof("Got ID - %d", attr.next_id)
   399  
   400  		progfd, err := utils.GetProgFDFromID(int(attr.next_id))
   401  		if err != nil {
   402  			log.Infof("Failed to get program Info")
   403  			return nil, err
   404  		}
   405  		log.Infof("Found prog FD - %d", progfd)
   406  		bpfProgInfo, err := GetBPFprogInfo(progfd)
   407  		if err != nil {
   408  			log.Infof("Failed to get program Info for FD", progfd)
   409  			return nil, err
   410  		}
   411  		unix.Close(progfd)
   412  
   413  		loadedPrograms = append(loadedPrograms, bpfProgInfo)
   414  	}
   415  	log.Infof("Done all prog info!!!")
   416  	return loadedPrograms, nil
   417  }
   418  
   419  func (attr *BpfObjGet) BpfGetObject() (int, error) {
   420  	//var log = logger.Get()
   421  	ret, _, errno := unix.Syscall(
   422  		unix.SYS_BPF,
   423  		utils.BPF_OBJ_GET,
   424  		uintptr(unsafe.Pointer(attr)),
   425  		unsafe.Sizeof(*attr),
   426  	)
   427  	if errno != 0 {
   428  		log.Infof("Failed to get Prog FD - ret %d and err %s", int(ret), errno)
   429  		return 0, errno
   430  	}
   431  	return int(ret), nil
   432  }
   433  
   434  func (m *BPFProgram) BpfGetProgFromPinPath(pinPath string) (BpfProgInfo, int, error) {
   435  	//var log = logger.Get()
   436  	log.Infof("Printing pinpath - %s ", pinPath)
   437  	if len(pinPath) == 0 {
   438  		return BpfProgInfo{}, -1, fmt.Errorf("Invalid pinPath")
   439  	}
   440  
   441  	cPath := []byte(pinPath + "\x00")
   442  	objInfo := BpfObjGet{
   443  		pathname: uintptr(unsafe.Pointer(&cPath[0])),
   444  	}
   445  
   446  	progFD, err := objInfo.BpfGetObject()
   447  	if err != nil {
   448  		log.Infof("Failed to get object")
   449  		return BpfProgInfo{}, -1, err
   450  
   451  	}
   452  
   453  	log.Infof("Got progFD - %d", progFD)
   454  	bpfProgInfo, err := GetBPFprogInfo(progFD)
   455  	if err != nil {
   456  		log.Infof("Failed to get program Info for FD - %d", progFD)
   457  		return bpfProgInfo, -1, err
   458  	}
   459  
   460  	return bpfProgInfo, progFD, nil
   461  }