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 }