Skip to content

Commit

Permalink
ebpf: replace the existing program on update
Browse files Browse the repository at this point in the history
on a container update, make sure the existing eBPF program is
completely replaced.

Signed-off-by: Giuseppe Scrivano <[email protected]>
  • Loading branch information
giuseppe committed May 10, 2020
1 parent f08509e commit e00aba6
Showing 1 changed file with 66 additions and 5 deletions.
71 changes: 66 additions & 5 deletions src/libcrun/ebpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,14 @@ libcrun_ebpf_load (struct bpf_program *program, int dirfd, const char *pin, libc
#ifndef HAVE_EBPF
return crun_make_error (err, 0, "eBPF not supported");
#else
int fd, ret;
cleanup_close int replacefd = -1;
cleanup_close int fd = -1;
union bpf_attr attr_open;
int attempts_left = 20;
union bpf_attr attr;
struct rlimit limit;
uint32_t progs[2];
int ret;

limit.rlim_cur = RLIM_INFINITY;
limit.rlim_max = RLIM_INFINITY;
Expand Down Expand Up @@ -329,28 +334,84 @@ libcrun_ebpf_load (struct bpf_program *program, int dirfd, const char *pin, libc

fd = bpf (BPF_PROG_LOAD, &attr, sizeof (attr));
if (fd < 0)
return crun_make_error (err, errno, "bpf create %s", log);
return crun_make_error (err, errno, "bpf create `%s`", log);
}

repeat:
memset (&attr_open, 0, sizeof (attr_open));
attr_open.query.target_fd = dirfd;
attr_open.query.attach_type = BPF_CGROUP_DEVICE;
attr_open.query.prog_cnt = sizeof (progs) / sizeof (progs[0]);
attr_open.query.prog_ids = (uint64_t) &progs;

ret = bpf (BPF_PROG_QUERY, &attr_open, sizeof (attr_open));
if (UNLIKELY (ret < 0))
return crun_make_error (err, errno, "bpf query");

if (attr_open.query.prog_cnt > 1)
return crun_make_error (err, 0, "invalid device cgroup state, more than one program installed");

if (attr_open.query.prog_cnt == 1)
{
#ifdef BPF_F_REPLACE
memset (&attr_open, 0, sizeof (attr_open));
attr_open.prog_id = progs[0];
replacefd = bpf (BPF_PROG_GET_FD_BY_ID, &attr_open, sizeof (attr_open));
if (UNLIKELY (replacefd < 0))
{
if (errno == ENOENT && attempts_left > 0)
{
/* Another update might have raced and updated, try again. */
close_and_reset (&replacefd);

attempts_left--;
goto repeat;
}
return crun_make_error (err, errno, "cannot open existing eBPF program");
}
#else
return crun_make_error (err, 0, "eBPF program already configured");
#endif
}

memset (&attr, 0, sizeof (attr));
attr.attach_type = BPF_CGROUP_DEVICE;
attr.target_fd = dirfd;
attr.attach_bpf_fd = fd;
#ifdef BPF_F_REPLACE
attr.attach_flags = BPF_F_ALLOW_MULTI | ((replacefd >= 0) ? BPF_F_REPLACE : 0);
attr.replace_bpf_fd = replacefd;
#else
attr.attach_flags = BPF_F_ALLOW_MULTI;
#endif

ret = bpf (BPF_PROG_ATTACH, &attr, sizeof (attr));
if (ret < 0)
return crun_make_error (err, errno, "bpf attach");
if (UNLIKELY (ret < 0))
{
if (errno == ENOENT && replacefd >= 0 && attempts_left > 0)
{
/* Another update might have already updated the cgroup, try again. */
close_and_reset (&replacefd);

attempts_left--;
goto repeat;
}
if (errno == EINVAL && replacefd >= 0)
return crun_make_error (err, errno, "bpf attach. The kernel might not support BPF_F_REPLACE");
return crun_make_error (err, errno, "bpf attach");
}

/* Optionally pin the program to the specified path. */
if (pin)
{
unlink (pin);

memset (&attr, 0, sizeof (attr));
attr.pathname = (uint64_t) pin;
attr.bpf_fd = fd;
ret = bpf (BPF_OBJ_PIN, &attr, sizeof (attr));
if (ret < 0)
return crun_make_error (err, errno, "bpf pin to %s", pin);
return crun_make_error (err, errno, "bpf pin to `%s`", pin);
}

return fd;
Expand Down

0 comments on commit e00aba6

Please sign in to comment.