Skip to content

Commit 84949c9

Browse files
committed
ebpf: replace the existing program on update
on a container update, make sure the existing eBPF program is completely replaced. Signed-off-by: Giuseppe Scrivano <[email protected]>
1 parent f08509e commit 84949c9

File tree

1 file changed

+84
-13
lines changed

1 file changed

+84
-13
lines changed

src/libcrun/ebpf.c

+84-13
Original file line numberDiff line numberDiff line change
@@ -293,15 +293,90 @@ bpf_program_complete_dev (struct bpf_program *program, libcrun_error_t *err arg_
293293
return program;
294294
}
295295

296+
static int
297+
ebpf_attach_program (int fd, int dirfd, libcrun_error_t *err)
298+
{
299+
const int MAX_ATTEMPTS = 20;
300+
int attempt;
301+
302+
for (attempt = 0;; attempt++)
303+
{
304+
cleanup_close int replacefd = -1;
305+
union bpf_attr attr;
306+
uint32_t progs[2];
307+
int ret;
308+
309+
memset (&attr, 0, sizeof (attr));
310+
attr.query.target_fd = dirfd;
311+
attr.query.attach_type = BPF_CGROUP_DEVICE;
312+
attr.query.prog_cnt = sizeof (progs) / sizeof (progs[0]);
313+
attr.query.prog_ids = (uint64_t) &progs;
314+
315+
ret = bpf (BPF_PROG_QUERY, &attr, sizeof (attr));
316+
if (UNLIKELY (ret < 0))
317+
return crun_make_error (err, errno, "bpf query");
318+
319+
if (attr.query.prog_cnt > 1)
320+
return crun_make_error (err, 0, "invalid device cgroup state, more than one program installed");
321+
322+
if (attr.query.prog_cnt == 1)
323+
{
324+
#ifdef BPF_F_REPLACE
325+
memset (&attr, 0, sizeof (attr));
326+
attr.prog_id = progs[0];
327+
replacefd = bpf (BPF_PROG_GET_FD_BY_ID, &attr, sizeof (attr));
328+
if (UNLIKELY (replacefd < 0))
329+
{
330+
if (errno == ENOENT && attempt < MAX_ATTEMPTS)
331+
{
332+
/* Another update might have raced and updated, try again. */
333+
continue;
334+
}
335+
return crun_make_error (err, errno, "cannot open existing eBPF program");
336+
}
337+
#else
338+
return crun_make_error (err, 0, "eBPF program already configured");
339+
#endif
340+
}
341+
342+
memset (&attr, 0, sizeof (attr));
343+
attr.attach_type = BPF_CGROUP_DEVICE;
344+
attr.target_fd = dirfd;
345+
attr.attach_bpf_fd = fd;
346+
#ifdef BPF_F_REPLACE
347+
attr.attach_flags = BPF_F_ALLOW_MULTI | ((replacefd >= 0) ? BPF_F_REPLACE : 0);
348+
attr.replace_bpf_fd = replacefd;
349+
#else
350+
attr.attach_flags = BPF_F_ALLOW_MULTI;
351+
#endif
352+
353+
ret = bpf (BPF_PROG_ATTACH, &attr, sizeof (attr));
354+
if (UNLIKELY (ret < 0))
355+
{
356+
if (errno == ENOENT && replacefd >= 0 && attempt < MAX_ATTEMPTS)
357+
{
358+
/* Another update might have already updated the cgroup, try again. */
359+
continue;
360+
}
361+
if (errno == EINVAL && replacefd >= 0)
362+
return crun_make_error (err, errno, "bpf attach. The kernel might not support BPF_F_REPLACE");
363+
return crun_make_error (err, errno, "bpf attach");
364+
}
365+
366+
return 0;
367+
}
368+
}
369+
296370
int
297371
libcrun_ebpf_load (struct bpf_program *program, int dirfd, const char *pin, libcrun_error_t *err)
298372
{
299373
#ifndef HAVE_EBPF
300374
return crun_make_error (err, 0, "eBPF not supported");
301375
#else
302-
int fd, ret;
376+
cleanup_close int fd = -1;
303377
union bpf_attr attr;
304378
struct rlimit limit;
379+
int ret;
305380

306381
limit.rlim_cur = RLIM_INFINITY;
307382
limit.rlim_max = RLIM_INFINITY;
@@ -329,30 +404,26 @@ libcrun_ebpf_load (struct bpf_program *program, int dirfd, const char *pin, libc
329404

330405
fd = bpf (BPF_PROG_LOAD, &attr, sizeof (attr));
331406
if (fd < 0)
332-
return crun_make_error (err, errno, "bpf create %s", log);
407+
return crun_make_error (err, errno, "bpf create `%s`", log);
333408
}
334409

335-
memset (&attr, 0, sizeof (attr));
336-
attr.attach_type = BPF_CGROUP_DEVICE;
337-
attr.target_fd = dirfd;
338-
attr.attach_bpf_fd = fd;
339-
attr.attach_flags = BPF_F_ALLOW_MULTI;
340-
341-
ret = bpf (BPF_PROG_ATTACH, &attr, sizeof (attr));
342-
if (ret < 0)
343-
return crun_make_error (err, errno, "bpf attach");
410+
ret = ebpf_attach_program (fd, dirfd, err);
411+
if (UNLIKELY (ret < 0))
412+
return ret;
344413

345414
/* Optionally pin the program to the specified path. */
346415
if (pin)
347416
{
417+
unlink (pin);
418+
348419
memset (&attr, 0, sizeof (attr));
349420
attr.pathname = (uint64_t) pin;
350421
attr.bpf_fd = fd;
351422
ret = bpf (BPF_OBJ_PIN, &attr, sizeof (attr));
352423
if (ret < 0)
353-
return crun_make_error (err, errno, "bpf pin to %s", pin);
424+
return crun_make_error (err, errno, "bpf pin to `%s`", pin);
354425
}
355426

356-
return fd;
427+
return 0;
357428
#endif
358429
}

0 commit comments

Comments
 (0)