def _open(self, map_fd: ct.c_int, func: Callable, ctx: ct.c_void_p = None) -> None: """ Open a new ringbuf with @func as a callback. """ # Cast func as _RINGBUF_CB_TYPE func = _RINGBUF_CB_TYPE(func) # Handle case where we don't have a manager yet if not self._skel._ringbuf_mgr: self._skel._ringbuf_mgr = Lib.ring_buffer_new( map_fd, func, ctx, None) if not self._skel._ringbuf_mgr: raise Exception( f'Failed to create new ring buffer manager: {cerr()}') # Handle case where we already have a manager else: ret = Lib.ring_buffer_add(self._skel._ringbuf_mgr, map_fd, func, ctx) if ret != 0: raise Exception( f'Failed to add ringbuf to ring buffer manager: {cerr(ret)}' ) # Keep a refcnt so that our function doesn't get cleaned up self._cb = func
def generate_progs(bpf_obj: ct.c_void_p): progs = {} for prog in Lib.obj_programs(bpf_obj): if not prog: continue prog_fd = Lib.bpf_program_fd(prog) prog_name = Lib.bpf_program_name(prog).decode(FILESYSTEMENCODING) prog_type = Lib.bpf_program_type(prog) progs[prog_name] = create_prog(prog, prog_name, prog_type, prog_fd) return progs
def next(self, key): """ Returns the next map key. """ next_key = self.KeyType() if key is None: ret = Lib.bpf_map_get_next_key(self._map_fd, None, ct.byref(next_key)) else: ret = Lib.bpf_map_get_next_key(self._map_fd, ct.byref(key), ct.byref(next_key)) if ret < 0: raise StopIteration() return next_key
def __delitem__(self, key): try: key = self.KeyType(key) except TypeError: pass ret = Lib.bpf_map_delete_elem(self._map_fd, ct.byref(key)) if ret < 0: raise KeyError(f'Unable to delete item item: {cerr(ret)}')
def attach(self): """ Attach the BPF program. """ if self._link: return self._link = Lib.bpf_program_attach(self._prog) if not self._link: raise Exception(f'Failed to attach BPF program {self._name}: {cerr()}')
def peek(self): """ Peek an element from the map. """ value = self.ValueType() ret = Lib.bpf_map_lookup_elem(self._map_fd, None, ct.byref(value)) if ret < 0: raise KeyError(f'Unable to peek value: {cerr(ret)}') return value
def pop(self) -> self.ValueType: """ Pop an element from the map. """ value = self.ValueType() ret = Lib.bpf_map_lookup_and_delete_elem(self._map_fd, None, ct.byref(value)) if ret < 0: raise KeyError(f'Unable to pop value: {cerr(ret)}') return value
def __getitem__(self, key): value = self.ValueType() try: key = self.KeyType(key) except TypeError: pass ret = Lib.bpf_map_lookup_elem(self._map_fd, ct.byref(key), ct.byref(value)) if ret < 0: raise KeyError(f'Unable to fetch item: {cerr(ret)}') return value
def push(self, value: self.ValueType, flags: int = 0): """ Push an element onto the map. """ try: value = self.ValueType(value) except TypeError: pass ret = Lib.bpf_map_update_elem(self._map_fd, None, ct.byref(value), flags) if ret < 0: raise KeyError(f'Unable to push value: {cerr(ret)}')
def __init__(self, *args, **kwargs): self._num_cpus = Lib.num_possible_cpus() try: alignment = self._vsize % 8 except AttributeError: raise Exception('PerCpuMixin without value size?!') # Force aligned value size if alignment != 0: self._vsize += (8 - alignment) # Make sure we are now aligned alignment = self._vsize % 8 assert alignment == 0
def generate_maps(skel, bpf_obj: ct.c_void_p): maps = {} for _map in Lib.obj_maps(bpf_obj): if not _map: continue map_fd = Lib.bpf_map_fd(_map) map_name = Lib.bpf_map_name(_map).decode(FILESYSTEMENCODING) map_entries = Lib.bpf_map_max_entries(_map) map_ksize = Lib.bpf_map_key_size(_map) map_vsize = Lib.bpf_map_value_size(_map) map_type = Lib.bpf_map_type(_map) maps[map_name] = create_map(skel, _map, map_fd, map_type, map_ksize, map_vsize, map_entries) return maps
def update(self, key: self.KeyType, value: self.ValueType, flags: int): """ Update a map value, operating according to specified flags. This provides more control than the traditional map[key] = value method. """ try: key = self.KeyType(key) except TypeError: pass try: value = self.ValueType(value) except TypeError: pass ret = Lib.bpf_map_update_elem(self._map_fd, ct.byref(key), ct.byref(value), flags) if ret < 0: raise KeyError(f'Unable to update item: {cerr(ret)}')
def remove_xdp(self, *ifnames: str): """ Remove all XDP programs from interfaces with names @ifnames. """ try: from pyroute2 import IPRoute except ImportError: raise ImportError('pyroute2 must be installed before removing XDP programs') from None ipr = IPRoute() for ifname in ifnames: try: ifindex = ipr.link_lookup(ifname=ifname)[0] except IndexError: raise KeyError(f'No such interface "{ifname}"') from None retval = Lib.bpf_set_link_xdp_fd(ifindex, -1, 0) if retval < 0: raise Exception(f'Failed to remove XDP program {self._name} to interface "{ifname}" ({ifindex}): {cerr(retval)}')
def invoke(self, data: ct.Structure = None): """ Invoke the BPF program once and capture and return its return value. If the program is of type BPF_PROG_TYPE_USER, you can pass it data using a ctypes struct @data. """ if data == None: data_p = None data_size = ct.c_uint32(0) else: data_p = ct.addressof(data) data_size = ct.sizeof(data) # Make this signed so that we can express negative return values, # should be okay to pass to the unsigned retval pointer bpf_ret = ct.c_uint32() retval = Lib.bpf_prog_test_run(self._prog_fd, ct.c_int(1), data_p, data_size, None, ct.c_uint32(0), ct.byref(bpf_ret), None) if retval < 0: raise Exception(f'Failed to invoke BPF program {self._name}: {cerr(retval)}') bpf_retval = ct.c_int16(bpf_ret.value) return bpf_retval.value
def open_bpf_object(bpf_obj_path: str) -> ct.c_void_p: bpf_obj_path = force_bytes(bpf_obj_path) res = Lib.bpf_object_open(bpf_obj_path) if not res: raise Exception(f'Failed to open BPF object: {cerr()}') return res
def close_bpf_object(bpf_obj: ct.c_void_p): if not bpf_obj: return Lib.bpf_object_close(bpf_obj)
def load_bpf_object(bpf_obj: ct.c_void_p): res = Lib.bpf_object_load(bpf_obj) if res < 0: raise Exception(f'Failed to load BPF object: {cerr()}') return res