Skip to content

Commit e00aba6

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 e00aba6

File tree

1 file changed

+66
-5
lines changed

1 file changed

+66
-5
lines changed

src/libcrun/ebpf.c

+66-5
Original file line numberDiff line numberDiff line change
@@ -299,9 +299,14 @@ libcrun_ebpf_load (struct bpf_program *program, int dirfd, const char *pin, libc
299299
#ifndef HAVE_EBPF
300300
return crun_make_error (err, 0, "eBPF not supported");
301301
#else
302-
int fd, ret;
302+
cleanup_close int replacefd = -1;
303+
cleanup_close int fd = -1;
304+
union bpf_attr attr_open;
305+
int attempts_left = 20;
303306
union bpf_attr attr;
304307
struct rlimit limit;
308+
uint32_t progs[2];
309+
int ret;
305310

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

330335
fd = bpf (BPF_PROG_LOAD, &attr, sizeof (attr));
331336
if (fd < 0)
332-
return crun_make_error (err, errno, "bpf create %s", log);
337+
return crun_make_error (err, errno, "bpf create `%s`", log);
338+
}
339+
340+
repeat:
341+
memset (&attr_open, 0, sizeof (attr_open));
342+
attr_open.query.target_fd = dirfd;
343+
attr_open.query.attach_type = BPF_CGROUP_DEVICE;
344+
attr_open.query.prog_cnt = sizeof (progs) / sizeof (progs[0]);
345+
attr_open.query.prog_ids = (uint64_t) &progs;
346+
347+
ret = bpf (BPF_PROG_QUERY, &attr_open, sizeof (attr_open));
348+
if (UNLIKELY (ret < 0))
349+
return crun_make_error (err, errno, "bpf query");
350+
351+
if (attr_open.query.prog_cnt > 1)
352+
return crun_make_error (err, 0, "invalid device cgroup state, more than one program installed");
353+
354+
if (attr_open.query.prog_cnt == 1)
355+
{
356+
#ifdef BPF_F_REPLACE
357+
memset (&attr_open, 0, sizeof (attr_open));
358+
attr_open.prog_id = progs[0];
359+
replacefd = bpf (BPF_PROG_GET_FD_BY_ID, &attr_open, sizeof (attr_open));
360+
if (UNLIKELY (replacefd < 0))
361+
{
362+
if (errno == ENOENT && attempts_left > 0)
363+
{
364+
/* Another update might have raced and updated, try again. */
365+
close_and_reset (&replacefd);
366+
367+
attempts_left--;
368+
goto repeat;
369+
}
370+
return crun_make_error (err, errno, "cannot open existing eBPF program");
371+
}
372+
#else
373+
return crun_make_error (err, 0, "eBPF program already configured");
374+
#endif
333375
}
334376

335377
memset (&attr, 0, sizeof (attr));
336378
attr.attach_type = BPF_CGROUP_DEVICE;
337379
attr.target_fd = dirfd;
338380
attr.attach_bpf_fd = fd;
381+
#ifdef BPF_F_REPLACE
382+
attr.attach_flags = BPF_F_ALLOW_MULTI | ((replacefd >= 0) ? BPF_F_REPLACE : 0);
383+
attr.replace_bpf_fd = replacefd;
384+
#else
339385
attr.attach_flags = BPF_F_ALLOW_MULTI;
386+
#endif
340387

341388
ret = bpf (BPF_PROG_ATTACH, &attr, sizeof (attr));
342-
if (ret < 0)
343-
return crun_make_error (err, errno, "bpf attach");
389+
if (UNLIKELY (ret < 0))
390+
{
391+
if (errno == ENOENT && replacefd >= 0 && attempts_left > 0)
392+
{
393+
/* Another update might have already updated the cgroup, try again. */
394+
close_and_reset (&replacefd);
395+
396+
attempts_left--;
397+
goto repeat;
398+
}
399+
if (errno == EINVAL && replacefd >= 0)
400+
return crun_make_error (err, errno, "bpf attach. The kernel might not support BPF_F_REPLACE");
401+
return crun_make_error (err, errno, "bpf attach");
402+
}
344403

345404
/* Optionally pin the program to the specified path. */
346405
if (pin)
347406
{
407+
unlink (pin);
408+
348409
memset (&attr, 0, sizeof (attr));
349410
attr.pathname = (uint64_t) pin;
350411
attr.bpf_fd = fd;
351412
ret = bpf (BPF_OBJ_PIN, &attr, sizeof (attr));
352413
if (ret < 0)
353-
return crun_make_error (err, errno, "bpf pin to %s", pin);
414+
return crun_make_error (err, errno, "bpf pin to `%s`", pin);
354415
}
355416

356417
return fd;

0 commit comments

Comments
 (0)