1

I want to print IP addresses of packets parsed by an XDP program I am testing with. I using bpf_trace_printk() to print details about packets parsed by my program. How can I print IP addresses with bpf_trace_print()?

I tried using this suggestion to print the IP, but I get this error when trying to use bpf_trace_printk()

/virtual/main.c:99:52: warning: cannot use more than 3 conversion specifiers
            bpf_trace_printk("\n- src_ip: %d.%d.%d.%d\n", src_ipaddr[3],src_ipaddr[2],src_ipaddr[1],src_ipaddr[0]);
                                                   ^
6 warnings generated.
error: /virtual/main.c:111:59: in function filter i32 (%struct.xdp_md*): too many args to 0x5b6de28: i64 = Constant<6>

Its not clear to my why I am getting this error.

Dave
  • 727
  • 1
  • 9
  • 20
  • 1
    Just a guess: `bpf_trace_printk` is _not_ as general as `printk` or `printf`. Based on the diagnostic, its limit for format specifiers is 3, so you can only have (e.g.) `%d.%d.%d`. I'm not too familar with BPF, but you could [probably] loop to get what you want: `bpf_trace_printk("\n- src_ip:"); for (int idx = 3; idx >= 0; --idx) bpf_trace_printk("%c%d",(idx == 0) ? ' ' : '.',src_ipaddr[idx]); bpf_trace_printk("\n");` – Craig Estey Feb 21 '22 at 21:11
  • Is `src_ipaddr` an IPv6 or an IPv4 address? – pchaigno Feb 22 '22 at 09:19
  • src_ipaddr is neither, its an unsigned character pointer array. – Dave Feb 22 '22 at 18:47

3 Answers3

1

There are printk format specifiers for IP addr structs. You can just do this:

bpf_printk("%pI6", &ip6hdr->saddr);

And the result:

12:46 $ sudo cat /sys/kernel/debug/tracing/trace_pipe
            ping-648648  [001] d.s11 191981.300511: bpf_trace_printk: 2001:0569:7c1b:8500:2587:6500:1975:5649
            ping-648648  [001] d.s11 191982.318736: bpf_trace_printk: 2001:0569:7c1b:8500:2587:6500:1975:5649
            ping-648684  [000] d.s11 191983.422204: bpf_trace_printk: 0000:0000:0000:0000:0000:0000:0000:0001
            ping-648684  [002] d.s11 191984.430712: bpf_trace_printk: 0000:0000:0000:0000:0000:0000:0000:0001
Arthur M
  • 447
  • 5
  • 21
0

bpf_trace_printk is meant for debugging only. It will print a large warning in your system logs when you use it. If you're at the stage where you want to pretty-print IP addresses, then you're probably not debugging anymore.

The proper alternative is to use the bpf_perf_event_output BPF helper. See https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md#lesson-7-hello_perf_outputpy for an example with bcc. That will allow you to send arbitrary data to userspace, where you can pretty-print the IP addresses with Python.

pchaigno
  • 11,313
  • 2
  • 29
  • 54
-1

Linux kernel provides BPF helper, bpf_trace_printk(), with the following definition:

long bpf_trace_printk(const char *fmt, __u32 fmt_size, ...);

So you need to define size of format string before your arguments.

A hard limitation is that bpf_trace_printk() can accept only up to 5 input arguments in total. You must define fmt and fmt_size, which means you limit to use just 3 other arguments. So 4 specifiers is not allowed in format string. This is quite often pretty limiting and you might need to use multiple bpf_trace_printk() invocations to log all the data.

for(int i=0; i<4; i++)
   bpf_trace_printk("IP section %d [%d]", strlen("IP section %d [%d]"),i,src_ipaddr[i]);

or use @craig-estey method

bpf_trace_printk("\n- src_ip:"); 
for (int idx = 3;  idx >= 0;  --idx) 
    bpf_trace_printk("%c%d",(idx == 0) ? ' ' : '.',src_ipaddr[idx]); 

bpf_trace_printk("\n");

I'm not sure but you may use sprintf

char part1Ip[32] = {0};
char part2Ip[32] = {0};
char wholeIp[32] = {0};
sprintf(part1Ip, "%d.%d", src_ipaddr[3],src_ipaddr[2]);
sprintf(part2Ip, "%d.%d", src_ipaddr[1],src_ipaddr[0]);
sprintf(wholeIp, "%s.%s", part1Ip,part2Ip);

bpf_trace_printk("\n- src_ip: %s\n", wholeIp);

For more information see BPF tips & tricks.

Majid Hajibaba
  • 3,105
  • 6
  • 23
  • 55