Skip to content

Commit 37b4aa8

Browse files
committed
pping: WIP - improve XDP and tc attach/detach process
Attempt to make it a bit safer (checking we detach the correct programs) and also adding slightly better cleanup for tc (will remove qdisc if created and failed initial setup, or if created and force as given as a parameter on shutdown) Also start using libbpf_strerror instead of strerror. Signed-off-by: Simon Sundberg <[email protected]>
1 parent 411625c commit 37b4aa8

File tree

1 file changed

+162
-46
lines changed

1 file changed

+162
-46
lines changed

pping/pping.c

Lines changed: 162 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,13 @@ struct pping_config {
7676
char *event_map;
7777
int xdp_flags;
7878
int ifindex;
79+
int ingress_prog_id;
80+
int egress_prog_id;
7981
char ifname[IF_NAMESIZE];
8082
bool json_format;
8183
bool ppviz_format;
8284
bool force;
85+
bool created_tc_hook;
8386
};
8487

8588
static volatile int keep_running = 1;
@@ -207,6 +210,7 @@ static int parse_arguments(int argc, char *argv[], struct pping_config *config)
207210
break;
208211
case 'f':
209212
config->force = true;
213+
config->xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
210214
break;
211215
case 'h':
212216
printf("HELP:\n");
@@ -242,16 +246,44 @@ static int set_rlimit(long int lim)
242246
return !setrlimit(RLIMIT_MEMLOCK, &rlim) ? 0 : -errno;
243247
}
244248

245-
static int xdp_detach(int ifindex, __u32 xdp_flags)
249+
/*
250+
* Simple convenience wrapper around libbpf_strerror for which you don't have
251+
* to provide a buffer. Instead uses its own static buffer and returns a pointer
252+
* to it.
253+
*
254+
* This of course comes with the tradeoff that it is no longer thread safe and
255+
* later invocations overwrite previous results.
256+
*/
257+
static const char *get_libbpf_strerror(int err)
246258
{
247-
return bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
259+
static char buf[200];
260+
libbpf_strerror(err, buf, sizeof(buf));
261+
return buf;
262+
}
263+
264+
static int init_rodata(struct bpf_object *obj, void *src, size_t size)
265+
{
266+
struct bpf_map *map = NULL;
267+
bpf_object__for_each_map(map, obj) {
268+
if (strstr(bpf_map__name(map), ".rodata"))
269+
return bpf_map__set_initial_value(map, src, size);
270+
}
271+
272+
// No .rodata map found
273+
return -EINVAL;
248274
}
249275

276+
/*
277+
* Attempt to attach program in section sec of obj to ifindex.
278+
* If sucessful, will return the positive program id of the attached.
279+
* On failure, will return a negative error code.
280+
*/
250281
static int xdp_attach(struct bpf_object *obj, const char *sec, int ifindex,
251-
__u32 xdp_flags, bool force)
282+
__u32 xdp_flags)
252283
{
253284
struct bpf_program *prog;
254-
int prog_fd;
285+
int prog_fd, err;
286+
__u32 prog_id;
255287

256288
if (sec)
257289
prog = bpf_object__find_program_by_title(obj, sec);
@@ -262,57 +294,127 @@ static int xdp_attach(struct bpf_object *obj, const char *sec, int ifindex,
262294
if (prog_fd < 0)
263295
return prog_fd;
264296

265-
if (force) // detach current (if any) xdp-program first
266-
xdp_detach(ifindex, xdp_flags);
297+
err = bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags);
298+
if (err)
299+
return err;
300+
301+
err = bpf_get_link_xdp_id(ifindex, &prog_id, xdp_flags);
302+
if (err) {
303+
bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
304+
return err;
305+
}
267306

268-
return bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags);
307+
return prog_id;
269308
}
270309

271-
static int init_rodata(struct bpf_object *obj, void *src, size_t size)
310+
static int xdp_detach(int ifindex, __u32 xdp_flags, __u32 expected_prog_id)
272311
{
273-
struct bpf_map *map = NULL;
274-
bpf_object__for_each_map(map, obj) {
275-
if (strstr(bpf_map__name(map), ".rodata"))
276-
return bpf_map__set_initial_value(map, src, size);
312+
__u32 curr_prog_id;
313+
int err;
314+
315+
err = bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags);
316+
if (err)
317+
return err;
318+
319+
if (!curr_prog_id) {
320+
return 0; // No current prog on interface
277321
}
278322

279-
// No .rodata map found
280-
return -EINVAL;
323+
if (expected_prog_id && curr_prog_id != expected_prog_id)
324+
return -ENOENT;
325+
326+
return bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
281327
}
282328

329+
/*
330+
* Will attempt to attach program at section sec in obj to ifindex at
331+
* attach_point.
332+
* On success, will fill in the passed opts, optionally set new_hook depending
333+
* if it created a new hook or not, and return the id of the attached program.
334+
* On failure it will return a negative hook.
335+
*/
283336
static int tc_attach(struct bpf_object *obj, int ifindex,
284337
enum bpf_tc_attach_point attach_point,
285-
const char *prog_title, struct bpf_tc_opts *opts)
338+
const char *sec, struct bpf_tc_opts *opts,
339+
bool *new_hook)
286340
{
287341
int err;
288342
int prog_fd;
343+
bool created_hook = true;
289344
DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = ifindex,
290345
.attach_point = attach_point);
291346

292347
err = bpf_tc_hook_create(&hook);
293-
if (err && err != -EEXIST)
348+
if (err == -EEXIST)
349+
created_hook = false;
350+
else if (err)
294351
return err;
295352

296353
prog_fd = bpf_program__fd(
297-
bpf_object__find_program_by_title(obj, prog_title));
298-
if (prog_fd < 0)
299-
return prog_fd;
354+
bpf_object__find_program_by_title(obj, sec));
355+
if (prog_fd < 0) {
356+
err = prog_fd;
357+
goto err_after_hook;
358+
}
300359

301360
opts->prog_fd = prog_fd;
302361
opts->prog_id = 0;
303-
return bpf_tc_attach(&hook, opts);
362+
err = bpf_tc_attach(&hook, opts);
363+
if (err)
364+
goto err_after_hook;
365+
366+
if (new_hook)
367+
*new_hook = created_hook;
368+
return opts->prog_id;
369+
370+
err_after_hook:
371+
/*
372+
* Destroy hook if it created it.
373+
* This is slightly racy, as some other program may still have been
374+
* attached to the hook between its creation and this error cleanup.
375+
*/
376+
if (created_hook) {
377+
hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
378+
bpf_tc_hook_destroy(&hook);
379+
}
380+
return err;
304381
}
305382

306383
static int tc_detach(int ifindex, enum bpf_tc_attach_point attach_point,
307-
struct bpf_tc_opts *opts)
384+
const struct bpf_tc_opts *opts, bool destroy_hook)
308385
{
386+
int err;
387+
int hook_err = 0;
309388
DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = ifindex,
310389
.attach_point = attach_point);
311-
opts->prog_fd = 0;
312-
opts->prog_id = 0;
313-
opts->flags = 0;
390+
DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_info, .handle = opts->handle,
391+
.priority = opts->priority);
314392

315-
return bpf_tc_detach(&hook, opts);
393+
// Check we are removing the correct program
394+
err = bpf_tc_query(&hook, &opts_info);
395+
if (err)
396+
return err;
397+
if (opts->prog_id != opts_info.prog_id)
398+
return -ENOENT;
399+
400+
// Attempt to detach program
401+
opts_info.prog_fd = 0;
402+
opts_info.prog_id = 0;
403+
opts_info.flags = 0;
404+
err = bpf_tc_detach(&hook, &opts_info);
405+
406+
/*
407+
* Attempt to destroy hook regardsless if detach succeded.
408+
* If the hook is destroyed sucessfully, program should
409+
* also be detached.
410+
*/
411+
if (destroy_hook) {
412+
hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
413+
hook_err = bpf_tc_hook_destroy(&hook);
414+
}
415+
416+
err = destroy_hook ? hook_err : err;
417+
return err;
316418
}
317419

318420
/*
@@ -699,34 +801,43 @@ static int load_attach_bpfprogs(struct bpf_object **obj,
699801
}
700802

701803
// Attach egress prog
702-
err = tc_attach(*obj, config->ifindex, BPF_TC_EGRESS,
703-
config->egress_sec, &config->tc_egress_opts);
704-
if (err) {
804+
config->egress_prog_id =
805+
tc_attach(*obj, config->ifindex, BPF_TC_EGRESS,
806+
config->egress_sec, &config->tc_egress_opts,
807+
&config->created_tc_hook);
808+
if (config->egress_prog_id < 0) {
705809
fprintf(stderr,
706810
"Failed attaching egress BPF program on interface %s: %s\n",
707-
config->ifname, strerror(-err));
708-
return err;
811+
config->ifname,
812+
get_libbpf_strerror(config->egress_prog_id));
813+
return config->egress_prog_id;
709814
}
710815

711816
// Attach ingress prog
712817
if (strcmp(config->ingress_sec, SEC_INGRESS_XDP) == 0)
713-
err = xdp_attach(*obj, config->ingress_sec, config->ifindex,
714-
config->xdp_flags, config->force);
818+
config->ingress_prog_id =
819+
xdp_attach(*obj, config->ingress_sec, config->ifindex,
820+
config->xdp_flags);
715821
else
716-
err = tc_attach(*obj, config->ifindex, BPF_TC_INGRESS,
717-
config->ingress_sec, &config->tc_ingress_opts);
718-
if (err) {
822+
config->ingress_prog_id =
823+
tc_attach(*obj, config->ifindex, BPF_TC_INGRESS,
824+
config->ingress_sec,
825+
&config->tc_ingress_opts, NULL);
826+
if (config->ingress_prog_id < 0) {
719827
fprintf(stderr,
720828
"Failed attaching ingress BPF program on interface %s: %s\n",
721829
config->ifname, strerror(-err));
830+
err = config->ingress_prog_id;
831+
// TODO - print XDP error hints (see xpd-tutorial/common/common_user_bpf_xdp.c)
722832
goto ingress_err;
723833
}
724834

725835
return 0;
726836

727837
ingress_err:
728-
detach_err = tc_detach(config->ifindex, BPF_TC_EGRESS,
729-
&config->tc_egress_opts);
838+
detach_err =
839+
tc_detach(config->ifindex, BPF_TC_EGRESS,
840+
&config->tc_egress_opts, config->created_tc_hook);
730841
if (detach_err)
731842
fprintf(stderr, "Failed detaching tc program from %s: %s\n",
732843
config->ifname, strerror(-detach_err));
@@ -878,22 +989,27 @@ int main(int argc, char *argv[])
878989
perf_buffer__free(pb);
879990

880991
cleanup_attached_progs:
881-
detach_err = tc_detach(config.ifindex, BPF_TC_EGRESS,
882-
&config.tc_egress_opts);
883-
if (detach_err)
884-
fprintf(stderr,
885-
"Failed removing egress program from interface %s: %s\n",
886-
config.ifname, strerror(-detach_err));
992+
printf("ingress prog id: %d\n", config.ingress_prog_id);
993+
printf("egress prog id: %d\n", config.egress_prog_id);
887994

888995
if (strcmp(config.ingress_sec, SEC_INGRESS_XDP) == 0)
889-
detach_err = xdp_detach(config.ifindex, config.xdp_flags);
996+
detach_err = xdp_detach(config.ifindex, config.xdp_flags,
997+
config.ingress_prog_id);
890998
else
891999
detach_err = tc_detach(config.ifindex, BPF_TC_INGRESS,
892-
&config.tc_ingress_opts);
1000+
&config.tc_ingress_opts, false);
8931001
if (detach_err)
8941002
fprintf(stderr,
8951003
"Failed removing ingress program from interface %s: %s\n",
896-
config.ifname, strerror(-detach_err));
1004+
config.ifname, get_libbpf_strerror(detach_err));
1005+
1006+
detach_err =
1007+
tc_detach(config.ifindex, BPF_TC_EGRESS, &config.tc_egress_opts,
1008+
config.force && config.created_tc_hook);
1009+
if (detach_err)
1010+
fprintf(stderr,
1011+
"Failed removing egress program from interface %s: %s\n",
1012+
config.ifname, get_libbpf_strerror(detach_err));
8971013

8981014
return (err != 0 && keep_running) || detach_err != 0;
8991015
}

0 commit comments

Comments
 (0)