1

I want to inspect the packet data for incoming packets on a specific cgroup. I found the BPF_PROG_TYPE_CGROUP_SKB program type for the BPF_CGROUP_INET_INGRESS attach type can be used to operate over the __sk_buff struct. However, it seems that the __sk_buff struct only exposes the packet headers, and not the data?

For reference, this is my bpf program:

#include <linux/in.h>
#include <linux/tcp.h>

#include <linux/bpf.h>
#include <sys/socket.h>

#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>

char LICENSE[] SEC("license") = "GPL";

SEC("cgroup_skb/ingress")
int bpf_print_cgroup_skb(struct __sk_buff *skb) {
  long err, data_len;
  char skb_str[100];
  long len = skb->len;

  // Get length of the packet
  if (len > 0) {
    data_len = skb->data_end - skb->data;
    bpf_printk("Packet length: %ld Data length: %ld\n", len, data_len);
    
    if (len > 100) {
      err = bpf_skb_load_bytes(skb, 0, &skb_str, 100);
      if (err < 0) {
        bpf_printk("ERR: bpf_skb_load_bytes failed: %ld\n", err);
        return SK_PASS;
      }
      bpf_printk("Message: %s\n", skb_str);
    }
  }

  return SK_PASS;
}

And this is how I load the program on a cgroup using the userspace program:

static const char *__doc__ = "User space program to load the cgskb bpf program to register sockets\n";

#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#include "../common/common_params.h"
#include "../common/common_user_bpf_xdp.h"

const char *cgroup_dir = "/sys/fs/cgroup/unified";
const char *cgroup_name = "<cgroup_name_here>";

int main(int argc, char **argv) {
  int err, len;
  int cgroup_fd;
  char cgroup_filename[PATH_MAX];
  const char *cgskb_file = "bpf_cgroup_skb.o";
  struct bpf_object *cgskb_obj;

  // Open the cgroup fd -- needed for both attach and detach operations.
  len = snprintf(cgroup_filename, PATH_MAX, "%s/%s", cgroup_dir, cgroup_name);
 
  fprintf(stdout, "Opening cgroup file %s\n", cgroup_filename);
  cgroup_fd = open(cgroup_filename, O_RDONLY);

  // Open, load and attach cgskb_obj if not already attached
  cgskb_obj = bpf_object__open_file(cgskb_file, NULL);
 
  struct bpf_program *cgskb_prog = bpf_object__find_program_by_name(cgskb_obj, "bpf_print_cgroup_skb");
 
  // Load the cgskb program
  err = bpf_object__load(cgskb_obj);
 
  // Attach the cgskb program
  // Using core BPF API as libbpf doesn't support cgskb yet.
  err = bpf_prog_attach(bpf_program__fd(cgskb_prog), cgroup_fd, BPF_CGROUP_INET_INGRESS, 0);
 
  fprintf(stdout, "Successfully loaded BPF program.\n");

  return 0;

exit_cgroup:
  close(cgroup_fd);

fail:
  return -1;
}

Is there any other recommend eBPF program type to get the ingress packet / socket message data.

diviquery
  • 569
  • 5
  • 19

1 Answers1

1

It indeed seems like reading data other than the headers has been purposefully disallowed. The original commit to add the program type https://github.com/torvalds/linux/commit/0e33661de493db325435d565a4a722120ae4cbf3 states:

This program type is similar to BPF_PROG_TYPE_SOCKET_FILTER, except that it does not allow BPF_LD_[ABS|IND] instructions and hooks up the bpf_skb_load_bytes() helper.

Programs of this type will be attached to cgroups for network filtering and accounting.

So loading packet data via the legacy method (before direct-data access) was blocked at its inception.

A later commit adds direct-data access https://github.com/torvalds/linux/commit/b39b5f411dcfce28ff954e5d6acb2c11be3cb0ec. But its message states:

BPF programs of BPF_PROG_TYPE_CGROUP_SKB need to access headers in the skb. This patch enables direct access of skb for these programs.

Two helper functions bpf_compute_and_save_data_end() and bpf_restore_data_end() are introduced. There are used in __cgroup_bpf_run_filter_skb(), to compute proper data_end for the BPF program, and restore original data afterwards.

And this bpf_compute_and_save_data_end() function limits the data_end to only include the headers, never the body:

cb->data_end = skb->data + skb_headlen(skb);

I have yet to find out what the reason behind this limitation is.

Dylan Reimerink
  • 5,874
  • 2
  • 15
  • 21
  • The helper doc from your website suggests it can be used for `BPF_PROG_TYPE_SK_SKB `. Can it also be used for `BPF_PROG_TYPE_CGROUP_SKB`? I tried attaching the program and it gave a `unknown func bpf_skb_pull_data#39` error while loading the program. – diviquery Jun 12 '23 at 18:22
  • Another question: what is the source of these docs? Is this available somewhere within the linux' repo as well? – diviquery Jun 12 '23 at 19:56
  • 1
    Sorry I did not check for the CGROUP_SKB program type, `bpf_skb_pull_data` is indeed not available for this program type. I will update the answer. As to where I get the info, its from the sources. Each program type has a `const struct bpf_verifier_ops` declaration containing a field called `get_func_proto`. If you then lookup the function it will list all helpers that program type is allowed to use. – Dylan Reimerink Jun 13 '23 at 11:00
  • Thanks for the information. I was trying to use `bpf_skb_load_bytes` but the issue is it does not read above 52B - I believe the reason is because of the non-linear socket buffer as you mentioned. Do you think reading using `bpf_skb_load_bytes` is possible from offsets that do not fall within the linear region? For example, reading from offset 53 in my case? – diviquery Jun 13 '23 at 13:10
  • 1
    I did a bit of digging, it seems like kernel authors made an explicit decision to make it impossible for CGROUP_SKB programs to even access the data beyond the headers given the commit message in https://github.com/torvalds/linux/commit/0e33661de493db325435d565a4a722120ae4cbf3 and https://github.com/torvalds/linux/commit/b39b5f411dcfce28ff954e5d6acb2c11be3cb0ec. I have yet been able to find why even reading isn't allowed. I suspect it is an oversight in an attempt to avoid manipulation of certain parts of the SKB due to assumptions made by the kernel at the hook point. – Dylan Reimerink Jun 13 '23 at 14:14
  • Hi @dylan-reimerink, can you share the link to your website's ebpf-docs? It was helpful in reading about program types and attach types at one place. – diviquery Jun 15 '23 at 18:56
  • https://ebpf-docs.dylanreimerink.nl/ – Dylan Reimerink Jun 16 '23 at 08:04