katran/lib/BpfAdapter.h (191 lines of code) (raw):
/* Copyright (C) 2018-present, Facebook, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <folly/Function.h>
#include <string>
#include <unordered_map>
#include <vector>
#include "BpfLoader.h"
extern "C" {
#include <bpf/bpf.h>
#include <linux/perf_event.h>
}
namespace katran {
constexpr int TC_INGRESS = 0xfffffff2;
constexpr int TC_EGRESS = 0xfffffff3;
// from bpf.h (list could be outdated)
constexpr unsigned int kBpfMapTypeUnspec = 0;
constexpr unsigned int kBpfMapTypeHash = 1;
constexpr unsigned int kBpfMapTypeArray = 2;
constexpr unsigned int kBpfMapTypeProgArray = 3;
constexpr unsigned int kBpfMapTypePerfEventArray = 4;
constexpr unsigned int kBpfMapTypePercpuHash = 5;
constexpr unsigned int kBpfMapTypePercpuArray = 6;
constexpr unsigned int kBpfMapTypeStackTrace = 7;
constexpr unsigned int kBpfMapTypeCgroupArray = 8;
constexpr unsigned int kBpfMapTypeLruHash = 9;
constexpr unsigned int kBpfMapTypeLruPercpuHash = 10;
constexpr unsigned int kBpfMapTypeLpmTrie = 11;
constexpr unsigned int kBpfMapTypeArrayOfMaps = 12;
constexpr unsigned int kBpfMapTypeHashOfMaps = 13;
/**
* This class implements API to work with bpf programs (such as load program,
* update/lookup maps etc), as well as some helper functions (resolve ifindex).
* This class is not thread safe.
*/
class BpfAdapter {
public:
explicit BpfAdapter(bool set_limits = true);
// BpfAdapter is not thread safe. Discourage unsafe use by disabling copy
// construction/assignment.
BpfAdapter(BpfAdapter const&) = delete;
BpfAdapter& operator=(BpfAdapter const&) = delete;
/**
* @param string bpf_prog path to bpf program
* @param bpf_prog_type type of bpf prog to load
* @param bool use_names flag to mark if names for maps/progs should be loaded
* @return int result 0 in case of success, other val otherwise
*
* helper function to load bpf program into the kernel
* loader could either deduct type from prog's name
* (supported xdp- and cls- prefixes) or by using option
* bpf_prog_type hint
*/
int loadBpfProg(
const std::string& bpf_prog,
const bpf_prog_type type = BPF_PROG_TYPE_UNSPEC,
bool use_names = false);
/**
* @param string bpf_prog path to bpf program
* @param bpf_prog_type type of bpf prog to load
* @return int result 0 in case of success, other val otherwise
*
* helper function to reload bpf program into the kernel
* loader could either deduct type from prog's name
* (supported xdp- and cls- prefixes) or by using option
* bpf_prog_type hint
*/
int reloadBpfProg(
const std::string& bpf_prog,
const bpf_prog_type type = BPF_PROG_TYPE_UNSPEC);
/**
* @param char* ptr to buffer with bpf's elf object
* @param int size of the buffer
* @param bpf_prog_type type of bpf prog to load
* @param bool use_names flag to mark if names for maps/progs should be loaded
* @return int result 0 in case of success, other val otherwise
*
* helper function to load bpf program into the kernel from buffer
* loader could either deduct type from prog's name
* (supported xdp- and cls- prefixes) or by using option
* bpf_prog_type hint
*/
int loadBpfProg(
const char* buf,
int buf_size,
const bpf_prog_type type = BPF_PROG_TYPE_UNSPEC,
bool use_names = false);
/**
* @param string name of the map (as in bpf's .c file)
* @return int bpf's map descriptor
*
* helper function which return's map descriptor for map
* w/ specified name
* on error return's -1
* note: positive return value doesn't mean map is in the current prog, just
* that it's opened
*/
int getMapFdByName(const std::string& name);
/**
* @param string name of the prog
* @param string name of the map (as in bpf's .c file)
* @return bool whether the map is present in the current prog
*
* helper function to check if a map is in current prog
*/
bool isMapInProg(const std::string& progName, const std::string& name);
/**
* @param unsigned int type of map to create
* @param unsigned int key_size size of the key in a map
* @param unsigned int value_size size of the map's value
* @param unsigned int max_entries maximum entries in the map
* @param unsigned int map_flags map's specific flags
* @param int numa_node where mem is going to be allocated
* @return int -1 on error, map's fd otherwise
*
* cpp wrapper around bpf_create_map helper
* current list of map types could be retrieved from bph.h
* some common types are specified as constants in this header.
* default behavior for numa_node is to allocate mem on the
* same node as userspace process is running.
*/
static int createBpfMap(
unsigned int type,
unsigned int key_size,
unsigned int value_size,
unsigned int max_entries,
unsigned int map_flags,
int numa_node = -1);
/**
* @param string name of the map
* @param unsigned int type of map to create
* @param unsigned int key_size size of the key in a map
* @param unsigned int value_size size of the map's value
* @param unsigned int max_entries maximum entries in the map
* @param unsigned int map_flags map's specific flags
* @param int numa_node where mem is going to be allocated
* @return int -1 on error, map's fd otherwise
*
* cpp wrapper around bpf_create_map_name helper
* current list of map types could be retrieved from bph.h
* some common types are specified as constants in this header.
* default behavior for numa_node is to allocate mem on the
* same node as userspace process is running.
*/
static int createNamedBpfMap(
const std::string& name,
unsigned int type,
unsigned int key_size,
unsigned int value_size,
unsigned int max_entries,
unsigned int map_flags,
int numa_node = -1);
/**
* @param string name of map-in-map w/ specified fd as prototype
* @param int map_fd fd of the prototype map
* @return 0 on success, -1 on failure
*
* helper function to set prototype for map-in-map
*/
int setInnerMapPrototype(const std::string& name, int map_fd);
/**
* @param string name of the prog's section (as SEC("name") in bpf)
* @return int bpf's prog descriptor
*
* helper function which returns program's descriptor for prog
* which section's name is equal to specified one
* on error returns -1
*/
int getProgFdByName(const std::string& name);
/**
* @param int fd of the object (e.g. map)
* @param string path where we want our obj to pin
* @return int 0 on success; non-zero othewise
*
* helper function to pin bpf's object to specified location
*/
static int pinBpfObject(int fd, const std::string& path);
/**
* @param string path where bpf's obj is pinned to
* @return fd on success; negative on failure
*
* helper function to get fd of pinned bpf's object
*/
static int getPinnedBpfObject(const std::string& path);
/**
* @param int fd of the object (e.g. map)
* @param bpf_map_info* pointer of pre-allocated bpf map info to populate
* @return int 0 on success; non-zero otherwise
*
* helper function to get the metadata of a bpf map
*/
static int getBpfMapInfo(const int& fd, struct bpf_map_info* info);
/**
* @param string name of the bpf map
* @return int >=0 on success; negative on failure
*
* helper function to get the max number of entries in a bpf map
*/
int getBpfMapMaxSize(const std::string& name);
/**
* @param string name of the bpf map
* @return int >=0 on success; negative on failure
*
* helper function to get the current number of entries in a bpf map
* O(N) on the map size -- this walks every key in the map
*/
int getBpfMapUsedSize(const std::string& name);
/**
* @param const string& interface name
* @return int interface index, or 0 if interface can't be found
*
* helper function to resolve interface name to interface index
*/
static int getInterfaceIndex(const std::string& interface_name);
/**
* @param int prog_fd descriptor of the program
* @param const string& interface name
* @param int direction ingress or egress
* @param const string& name of bpf program (will be visiable in tc output)
* @return int 0 on success, other val otherwise
*
* helper function which attach bpf program to tc's hook
* on specified interface in specified direction
*/
static int attachBpfProgToTc(
const int prog_fd,
const std::string& interface_name,
const int direction = TC_INGRESS,
const std::string& bpf_name = "tc-bpf",
const uint32_t priority = 2307);
/**
* @param int prog_fd descriptor of the program
* @param string interface_name to attach
* @param uint32_t flags xdp flags for attaching xdp prog. 0 is default
* @return int 0 on success
*
* helper function to attach bpf prog to specified interface
*/
static int attachXdpProg(
const int prog_fd,
const std::string& interface_name,
const uint32_t flags = 0);
/**
* @param string interface_name from which we want to detach xdp prog
* @param uint32_t flags xdp flags used for attaching xdp prog. 0 is default
* @return int 0 on success
*
* helper function to detach bpf prog from interface
*/
static int detachXdpProg(
const std::string& interface_name,
const uint32_t flags = 0);
/**
* @param int interface ifindex
* @param uint32_t flags xdp flags used for attaching xdp prog. 0 is default
* @return int 0 on success
*
* helper function to detach bpf prog from interface w/ specified index
*/
static int detachXdpProg(const int ifindex, const uint32_t flags = 0);
/**
* @param int map_fd file descriptor of map to update
* @param void* key pointer to map's key which value we want to update
* @param void* value pointer to new value
* @param usinged long long flags
* @return int 0 on success, other val otherwise
*
* helper function to update (and/or create; depends on container)
* value inside bpf map
*/
static int bpfUpdateMap(
int map_fd,
void* key,
void* value,
unsigned long long flags = 0);
/**
* @param int map_fd file descriptor of map to update
* @param void* key pointer to map's key which value we want to get
* @param void* value pointer where we will write value from map
* @return int 0 on success, other val otherwise
*
* helper function to update (and/or create; depends on container)
* value inside bpf map
*/
static int bpfMapLookupElement(int map_fd, void* key, void* value);
/**
* @param int map_fd file descriptor of bpf map
* @param void* key pointer to key, which we are going to delete
* @return int 0 on sucess, other val otherwise
*
* helper function to delete element for bpf map, which key is equal to *key
*/
static int bpfMapDeleteElement(int map_fd, void* key);
/**
* @param int map_fd file descriptor of bpf map
* @param void* key pointer to key, void* next_key pointer to the next key
* @return int 0 on success, -1 or otherval otherwise
*
* helper function to iterate through the keys of a map
*/
static int bpfMapGetNextKey(int map_fd, void* key, void* next_key);
/**
* @param int outer_map_fd file descriptor of the map-in-map
* @param void* key pointer to the key, for looking up the associated value
* @return int fd of inner map on success, -1 on failure
*
* Returns FD of the inner bpf map associated with the given
* key in the given map-in-map with fd=outer_map_fd
*
* NOTE: bpf internally increments the reference count of the inner map after
* successfully looking up by it's id. Thus, the userspace program
* must close the FD to avoid leaks.
*/
static int bpfMapGetFdOfInnerMap(int outer_map_fd, void* key);
/**
* @param uint32_t map_id valid id of a bpf map
* @return int fd of the map if success, -1 on failure
*
* Given id of a bpf map, returns it's file descriptor (fd)
*
* NOTE: bpf internally increments the reference count of the map after
* successfully looking up by it's id. Thus, the userspace program
* must close the FD to avoid leaks.
*/
static int bpfMapGetFdById(uint32_t map_id);
/**
* @param uint32_t prog_id valid id of a bpf prog
* @return int fd of the prog if success, -1 on failure
*
* Given id of a bpf prog, returns it's file descriptor (fd)
*
* NOTE: bpf internally increments the reference count of the prog after
* successfully looking up by it's id. Thus, the userspace program
* must close the FD to avoid leaks.
*/
static int bpfProgGetFdById(uint32_t prog_id);
/**
* @param int prog_fd descriptor of bpf program
* @param unsigned int ifindex - index of the interface
* @param const string& bpf_name name of program
* @param uint32_t& priority priority of tc's filter
* @return int 0 on success, other val otherwise
*
* helper function to attach bpf to specified interface (thru ifindex)
* and w/ specified priority. if there is already filter w/ specified
* priority, this call will fails (return non 0);
*/
static int addTcBpfFilter(
const int prog_fd,
const unsigned int ifindex,
const std::string& bpf_name,
uint32_t priority,
const int direction = TC_INGRESS);
/**
* @param int prog_fd descriptor of the program
* @param usigned int ifindex of interface
* @param uint32_t flags optional xdp flags
* @return int 0 on success
*
* helper function to add or delete (by specifying prog_fd = -1) xdp
* prog
*/
static int modifyXdpProg(
const int prog_fd,
const unsigned int ifindex,
const uint32_t flags = 0);
/**
* @param int prog_fd descriptor of bpf program
* @param unsigned int ifindex - index of the interface
* @param const string& bpf_name name of program
* @param uint32_t& priority priority of tc's filter
* @return int 0 on success, other val otherwise
*
* helper function to replace bpf filter on specified interface
* (thru ifindex) and w/ specified priority. if there is no filter
* with specified priority this call will create a new one
*/
static int replaceTcBpfFilter(
const int prog_fd,
const unsigned int ifindex,
const std::string& bpf_name,
const uint32_t priority,
const int direction = TC_INGRESS);
/**
* @param int prog_fd descriptor of bpf program
* @param unsigned int ifindex - index of the interface
* @param const string& bpf_name name of program
* @param uint32_t& priority priority of tc's filter
* @return int 0 on success, other val otherwise
*
* helper function to delete bpf prog (filter) from specified interface.
* to delete specified filter priority must be specified
*/
static int deleteTcBpfFilter(
const int prog_fd,
const unsigned int ifindex,
const std::string& bpf_name,
const uint32_t priority,
const int direction = TC_INGRESS);
/**
* @param int prog_fd descriptor of bpf program
* @param int repeat how many times repeat a test run
* @param void* data pointer to the input packet
* @param uint32_t data_size size of the packet
* @param void* data_out pointer to the output (where we store a result)
* @param uint32_t* size_out size of the output packet. optional
* @param uint32_t* retval return value of the program
* @param uint32_t* duration how long did it take to run a test
* @param void* ctx_in optional pointer to context
* @param uint32_t ctx_size_in size of the context
* @param void* ctx_out optional pointer to output context
* @param uint32_t* ctx_size_out pointer to the size of ctx after test run
* @return int result of the test. 0 on success, non 0 otherwise
*
* helper function which allow user to test xdp program by specifying
* program_fd and input value (ptr to packet and it's size)
* it will return modified (if program modifies it) packet
* and xdp's return code.
*/
static int testXdpProg(
const int prog_fd,
const int repeat,
void* data,
uint32_t data_size,
void* data_out,
uint32_t* size_out = nullptr,
uint32_t* retval = nullptr,
uint32_t* duration = nullptr,
void* ctx_in = nullptr,
uint32_t ctx_size_in = 0,
void* ctx_out = nullptr,
uint32_t* ctx_size_out = nullptr);
/**
* @param int prog_fd descriptor of the program
* @param string path to cgroup directory
* @param enum bpf_attach_type type of attachment
* @param unsigned int flags
* @return int 0 on success, non 0 otherwise
*
* helper function to attach bpf prog to specified cgroup
*/
static int attachCgroupProg(
int prog_fd,
const std::string& cgroup,
enum bpf_attach_type type,
unsigned int flags);
/**
* @param string path to cgroup directory
* @param enum bpf_attach_type type of attachment
* @return int 0 on success, non 0 otherwise
*
* helper function to detach all bpf progs from specified cgroup
*/
static int detachCgroupProg(
const std::string& cgroup,
enum bpf_attach_type type);
/**
* @param string path to cgroup directory
* @param enum bpf_attach_type type of attachment
* @param string progPrefix Prefix to match with each bpf-program name in
* the specified group before detaching the program
* @return int 0 on success, non 0 otherwise
*
* helper function to detach all bpf progs with matching program name
* prefix from specified cgroup
*/
static int detachCgroupProgByPrefix(
const std::string& cgroup,
enum bpf_attach_type type,
const std::string& progPrefix);
/**
* @param int bpf prog fd
* @param string path to cgroup directory
* @param enum bpf_attach_type type of attachment
* @return int 0 on success, non 0 otherwise
*
* helper function to detach specified (by fd) bpf progs
* from specified cgroup
*/
static int detachCgroupProg(
int prog_fd,
const std::string& cgroup,
enum bpf_attach_type type);
/**
* @param string path to cgroup directory
* @param enum bpf_attach_type type of attachment
* @return vector int of fds of attached bpf progs
*
* helper function to get a list of fds of all attached bpf progs
* with specified type to specified cgroup
*/
static std::vector<uint32_t> getCgroupProgsIds(
const std::string& cgroup,
enum bpf_attach_type type);
/**
* @param int Fd of a valid bpf program
* @param bpf_prog_info info object to be populated with result
* @return 0 if successful
*
* helper function to get info about a valid bpf program
*/
static int getBpfProgInfo(int progFd, ::bpf_prog_info& info);
/**
* @param int Fd of a valid bpf program
* @return bpf_prog_info object with info about the given program
* @throws std::runtime_exception on error
*
* helper function to get info about a valid bpf program
*/
static bpf_prog_info getBpfProgInfo(int progFd);
/**
* @param string name of the shared map
* @param int fd of the shared map
* @return 0 on success
*
* helper function to update shared map's dictionary
*/
int updateSharedMap(const std::string& name, int fd);
/**
* @return number of possible cpus used for percpu maps
*
* helper function which retrieves number of cpus which have allocated
* resources in running system. return -1 on failure, number of CPUs
* otherwise.
*/
static int getPossibleCpus();
/**
* @param struct perf_event_mmap_page* header ptr to mmaped memory
* @param int pages size of mmap region in pages
* @return true on success
*
* helper function to unmap previously mmaped pages for bpf_perf_event
*/
static bool perfEventUnmmap(struct perf_event_mmap_page** header, int pages);
/**
* @param int cpu where perf event needs to be attached
* @param int map_fd descriptor of bpf's perf event map
* @param int wakeUpNumEvents sampling rate. wake up every wakeUpNumEvents
* @param struct perf_event_mmap_page** header of mmaped memory region
* @param int& event_fd descriptor of new perf event
* @return true on success
*
* helper function to mmap memory region and create a new perf event which is
* going to use it on specified cpu w/ specified sampling rate.
* header and event_fd params are going to be used to store allocated values
*/
static bool openPerfEvent(
/* input */
int cpu,
int map_fd,
int wakeUpNumEvents,
int pages,
/* output */
struct perf_event_mmap_page** header,
int& event_fd);
/**
* @param Function eventHandler cb to run on received perf event
* @param perf_event_mmap_page header ptr to mmape memory region of perf event
* @param string& buffer to copy perf event data to
* @param int pageSize size of a single page
* @param int pages size in pages of mmaped memory region
* @param int cpu cpu to get handle event from
*
* helper function to handle perf event from specified cpu
* and call specified helper
*/
static void handlePerfEvent(
folly::Function<void(const char* data, size_t size)> eventHandler,
struct perf_event_mmap_page* header,
std::string& buffer,
int pageSize,
int pages,
int cpu);
/**
* @param path path to the .o object file
* @param mapName name of map to check
*
* stateless helper function to check for the presence of a map in an
* unloaded bpf file
*/
static bool isMapInBpfObject(
const std::string& path,
const std::string& mapName);
private:
/**
* helper function to modify (add/delete/replace) tc's bpf prog.
* this is lowlvl function which would be used by all other public wrappers
*/
static int modifyTcBpfFilter(
const int cmd,
const unsigned int flags,
const uint32_t priority,
const int prog_fd,
const unsigned int ifindex,
const std::string& bpf_name,
const int direction = TC_INGRESS);
/**
* helper function to add clsact qdisk to interface for healthchecking
*/
static int addClsActQD(const unsigned int ifindex);
/**
* Generic wrapper to add bpf prog to tc.
*/
static int genericAttachBpfProgToTc(
const int prog_fd,
const unsigned int ifindex,
const std::string& bpf_name,
uint32_t priority,
const int direction = TC_INGRESS);
/**
* helper function which open specified dir and returns it's fd.
* used to get cgroup's fd from path
* returns -1 on failure
*/
static int getDirFd(const std::string& path);
/**
* helper function to mmap pages for bpf_perf_event
*/
static struct perf_event_mmap_page* perfEventMmap(int event_fd, int pages);
/**
* object file loader
*/
BpfLoader loader_;
};
} // namespace katran