@@ -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
8588static 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+ */
250281static 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+ */
283336static 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
306383static 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
727837ingress_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
880991cleanup_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