Search code examples
networkingebpfkprobebpftrace

Comparing ip addresses in bpftrace?


I am writing some bpftrace code in which I would like to compare an IP address (stored as a 32 bit integer) against the string representation of an address. That is, I want to do something like this:

kprobe:netif_receive_skb {
  $skb = (struct sk_buff *)arg0;
  $dev = $skb->dev;
  $name = $dev->name;
  $ipheader = ((struct iphdr *) ($skb->head + $skb->network_header));

  // This pseudocode conditional is what I am trying to figure out
  if ($ipheader->daddr == "8.8.8.8") {
    printf("%s %s %s -> %s\n",
        func, $name, ntop($ipheader->saddr), ntop($ipheader->daddr))
  }
}

In the above, $ipheader is a struct iphdr *, and struct iphdr looks like this:

struct iphdr {
  ...
  __struct_group(/* no tag */, addrs, /* no attrs */,
    __be32  saddr;
    __be32  daddr;
  );
};

So both saddr and daddr are unsigned 32-bit integers.

I thought perhaps I could use the pton() function, like this:

if ($ipheader->daddr == pton("8.8.8.8")) {

But unfortunately pton() returns an int8[4] array instead of a single 32 bit integer, so this fails with:

traceit.bpf:8:5-41: ERROR: Type mismatch for '==': comparing 'unsigned int32' with 'unsigned int8[4]'
    if ($ipheader->daddr == pton("8.8.8.8")) {
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I next tried to see if I could normalize both values into int8[4] arrays, like this:

$target = pton("8.8.8.8");
$daddr = pton(ntop($ipheader->daddr));
if ($target == $daddr) {

But this fails because ntop() doesn't actually return a string, so we get:

ERROR: Expected string literal, got inet
traceit.bpf:9:10-38: ERROR: pton() expects an string argument of an IPv4/IPv6 address, got
$daddr = pton(ntop($ipheader->daddr));

I realize I could just specify the target as an integer myself:

if ($ipheader->daddr == 0x08080808) {

But while that's easy to interpret for a contrived address like 8.8.8.8, it's opaque when the hex form of the address is something like 0xacd9004e.

Is it possible to compare a 32-bit integer representation of an address with the string representation of an address using bpftrace (and if so, what's the canonical way of doing so)?


Solution

  • This has been fixed in version 0.17 and 0.19:

    • 0.17 added support for comparing int arrays
    • 0.19 added support for casting int to int array

    Both of these should allow comparing IP addresses by casting them to a common type.