Skip to main content
Version: v0.30.0

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 and struct 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 from bpf_ktime_get_boot_ns().
  • typedef __u32 gadget_kernel_stack: symbolize the kernel stack from gadget_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:

  1. void *gadget_reserve_buf(void *map, __u64 size): Reserves memory in the corresponding buffer.
  2. long gadget_submit_buf(void *ctx, void *map, void *buf, __u64 size): Writes the previously reserved memory in the corresponding buffer.
  3. void gadget_discard_buf(void *buf): Discards the previously reserved buffer. This is needed to avoid wasting memory.
  4. 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 calling gadget_reserve_buf() and gadget_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
}