Gadget helper API
The gadget helper API is a set of features exposed by Inspektor Gadget that is accessible from eBPF code.
Container enrichment
To make use of container enrichment, gadgets must include gadget/mntns.h:
#include <gadget/mntns.h>
Inspektor Gadget will automatically enrich events with container information when the events include the mount namespace in this way:
struct event {
gadget_mntns_id mntns_id;
/* other fields */
}
An eBPF program can look up the mount namespace with the following:
u64 mntns_id;
mntns_id = gadget_get_mntns_id();
eBPF programs of type socket filter cannot use gadget_get_mntns_id()
, but instead
use socket enrichment to find the mount namespace.
Container filtering
To make use of container filtering, gadgets must include gadget/mntns_filter.h:
#include <gadget/mntns_filter.h>
eBPF programs should stop processing an event when it does not originate from a container the user is interested in.
if (gadget_should_discard_mntns_id(mntns_id))
return 0;
Socket enrichment
To make use of socket enrichment, gadgets must include gadget/sockets-map.h.
For eBPF programs of type socket filter:
#define GADGET_TYPE_NETWORKING
#include <gadget/sockets-map.h>
For eBPF programs of other kinds:
#define GADGET_TYPE_TRACING
#include <gadget/sockets-map.h>
This will define the following struct:
#define TASK_COMM_LEN 16
struct sockets_value {
__u64 mntns;
__u64 pid_tgid;
__u64 uid_gid;
char task[TASK_COMM_LEN];
/* other private fields */
};
Then, an eBPF can find additional details about a socket with
gadget_socket_lookup()
. There are two different definitions of this function
depending on the eBPF program type. The developer must define either
GADGET_TYPE_NETWORKING
or GADGET_TYPE_TRACING
to define the right one.
It can return NULL if the socket is not found.
For eBPF programs of type socket filter:
struct sockets_value *skb_val = gadget_socket_lookup(skb);
if (skb_val != NULL) {
/* Access skb_val->mntns and other fields */
}
For eBPF programs of other kinds:
struct sockets_value *skb_val = gadget_socket_lookup(sk, netns);
if (skb_val != NULL) {
/* Access skb_val->mntns and other fields */
}
Enriched types
When a gadget emits an event with one of the following fields, it will be automatically enriched.
struct event {
struct gadget_l3endpoint_t field1;
struct gadget_l4endpoint_t field2;
gadget_mntns_id field3;
gadget_timestamp field4;
gadget_kernel_stack field5;
}
struct gadget_l3endpoint_t
andstruct gadget_l4endpoint_t
: enrich with the Kubernetes endpoint. TODO: add details.typedef __u64 gadget_mntns_id
: container enrichment (see #container-enrichment)typedef __u64 gadget_timestamp
: add human-readable timestamp frombpf_ktime_get_boot_ns()
.typedef __u32 gadget_kernel_stack
: symbolize the kernel stack fromgadget_get_kernel_stack(ctx)
(see #kernel-stack-maps).
Buffer API
There are two kind of eBPF maps used to send events to userspace: (a) perf ring buffer or (b) eBPF
ring buffer. The later is more efficient and flexible, however it's only available from kernel 5.8.
Check https://nakryiko.com/posts/bpf-ringbuf/ to get more details about the differences.
<gadget/buffer.h>
provides an abstraction to automatically use the right buffer according to the
kernel features.
First, you need to declare the buffer with the following macro:
GADGET_TRACER_MAP(events, 1024 * 256);
Then, you can interact with the buffer using these functions:
void *gadget_reserve_buf(void *map, __u64 size)
: Reserves memory in the corresponding buffer.long gadget_submit_buf(void *ctx, void *map, void *buf, __u64 size)
: Writes the previously reserved memory in the corresponding buffer.void gadget_discard_buf(void *buf)
: Discards the previously reserved buffer. This is needed to avoid wasting memory.long gadget_output_buf(void *ctx, void *map, void *buf, __u64 size)
: Reserves and writes the buffer in the corresponding map. This is equivalent to callinggadget_reserve_buf()
andgadget_submit_buf()
.
The following snippet demonstrates how to use the code available in <gadget/buffer.h>
, it is taken from trace_open
:
#include <gadget/buffer.h>
/* ... */
GADGET_TRACER_MAP(events, 1024 * 256);
GADGET_TRACER(exit, events, event);
/* ... */
static __always_inline int trace_exit(struct syscall_trace_exit *ctx)
{
struct event *event;
/* ... */
event = gadget_reserve_buf(&events, sizeof(*event));
if (!event)
goto cleanup;
/* fill the event here */
/* ... */
gadget_submit_buf(ctx, &events, event, sizeof(*event));
/* ... */
}
If you know you will not make use of gadget_reserve_buf()/gadget_submit_buf()
and only rely on gadget_output_buf()
, you can define the GADGET_NO_BUF_RESERVE
macro before including <gadget/buffer.h>
.
This will not declare the map associated with gadget_reserve_buf()
as well as the other functions.
Here is an example, taken from trace_exec
, of using gadget_output_buf()
:
#define GADGET_NO_BUF_RESERVE
#include <gadget/buffer.h>
/* ... */
GADGET_TRACER_MAP(events, 1024 * 256);
GADGET_TRACER(exec, events, event);
/* ... */
SEC("tracepoint/syscalls/sys_exit_execve")
int ig_execve_x(struct syscall_trace_exit *ctx)
{
/* ... */
struct event *event;
/* ... */
event = bpf_map_lookup_elem(&execs, &pid);
if (!event)
return 0;
/* ... */
if (len <= sizeof(*event))
gadget_output_buf(ctx, &events, event, len);
/* ... */
}
Kernel stack maps
To make use of kernel stack maps, gadgets must include gadget/kernel_stack_map.h.
#include <gadget/kernel_stack_map.h>
This will define the following struct:
#define PERF_MAX_STACK_DEPTH 127
#define MAX_ENTRIES 10000
struct {
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
__uint(key_size, sizeof(u32));
__uint(value_size, PERF_MAX_STACK_DEPTH * sizeof(u64));
__uint(max_entries, MAX_ENTRIES);
} ig_kstack SEC(".maps");
Then, add a field in the event structure with the type of gadget_kernel_stack
,
designated for storing the stack id. gadget_get_kernel_stack(ctx)
could be used
to populate this field, this helper function will store the kernel stack into
ig_kstack
and returns the stack id (positive or null) as the key. It will return
a negative value on failure.
struct event {
gadget_kernel_stack kstack;
/* other fields */
};
struct event *event;
long kernel_stack_id;
event = gadget_reserve_buf(&events, sizeof(*event));
kernel_stack_id = gadget_get_kernel_stack(ctx);
if (kernel_stack_id >= 0) {
event->kstack = kernel_stack_id;
} else {
// get kernel stack failed
}