Skip to content

Commit

Permalink
libbpf-tools: Add support for IPv6 to tcprtt.
Browse files Browse the repository at this point in the history
Signed-off-by: Francis Laniel <[email protected]>
  • Loading branch information
eiffel-fl committed Jul 10, 2023
1 parent 5c92af6 commit 6534868
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 20 deletions.
93 changes: 82 additions & 11 deletions libbpf-tools/tcprtt.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,120 @@
#include "bits.bpf.h"
#include "maps.bpf.h"

/* Taken from kernel include/linux/socket.h. */
#define AF_INET 2 /* IP version 4 */
#define AF_INET6 10 /* IP version 6 */


const volatile bool targ_laddr_hist = false;
const volatile bool targ_raddr_hist = false;
const volatile bool targ_show_ext = false;
const volatile __u16 targ_sport = 0;
const volatile __u16 targ_dport = 0;
const volatile __u32 targ_saddr = 0;
const volatile __u32 targ_daddr = 0;
const volatile __u8 targ_saddr_v6[IPV6_LEN] = {};
const volatile __u8 targ_daddr_v6[IPV6_LEN] = {};
const volatile bool targ_ms = false;

#define MAX_ENTRIES 10240

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_ENTRIES);
__type(key, u64);
__type(key, struct hist_key);
__type(value, struct hist);
} hists SEC(".maps");

static struct hist zero;

/*
* We cannot use the following:
* __builtin_memcmp(targ_*addr_v6, *, sizeof(targ_*addr_v6));
* Indeed, by using the builtin, we would discard the volatile qualifier of
* targ_*addr_v6, so the compiler would optimize it and replaces the call
* with 0.
* So, using the volatile qualifier ensures this function is called at runtime.
*/
static bool inline ipv6_is_not_zero(const volatile __u8 addr[IPV6_LEN])
{
for (int i = 0; i < IPV6_LEN; i++)
if (addr[i])
return true;
return false;
}

static bool inline ipv6_are_different(const volatile __u8 a[IPV6_LEN], const __u8 b[IPV6_LEN])
{
for (int i = 0; i < IPV6_LEN; i++)
if (a[i] != b[i])
return true;
return false;
}

static int handle_tcp_rcv_established(struct sock *sk)
{
const struct inet_sock *inet = (struct inet_sock *)(sk);
struct tcp_sock *ts;
struct hist *histp;
u64 key, slot;
struct hist_key key = {};
u64 slot;
u32 srtt;

if (targ_sport && targ_sport != BPF_CORE_READ(inet, inet_sport))
return 0;
if (targ_dport && targ_dport != BPF_CORE_READ(sk, __sk_common.skc_dport))
return 0;
if (targ_saddr && targ_saddr != BPF_CORE_READ(inet, inet_saddr))
return 0;
if (targ_daddr && targ_daddr != BPF_CORE_READ(sk, __sk_common.skc_daddr))

key.family = BPF_CORE_READ(sk, __sk_common.skc_family);
switch (key.family) {
case AF_INET:
/* If we set any of IPv6 address, we do not care about IPv4 ones. */
if (ipv6_is_not_zero(targ_saddr_v6) || ipv6_is_not_zero(targ_daddr_v6))
return 0;

if (targ_saddr && targ_saddr != BPF_CORE_READ(inet, inet_saddr))
return 0;

if (targ_daddr && targ_daddr != BPF_CORE_READ(sk, __sk_common.skc_daddr))
return 0;

break;
case AF_INET6:
/*
* Reciprocal of the above: if we set any of IPv4 address, we do not care
* about IPv6 ones.
*/
if (targ_saddr || targ_daddr)
return 0;

if (ipv6_is_not_zero(targ_saddr_v6)
&& ipv6_are_different(targ_saddr_v6, BPF_CORE_READ(inet, pinet6, saddr.in6_u.u6_addr8)))
return 0;

if (ipv6_is_not_zero(targ_daddr_v6)
&& ipv6_are_different(targ_daddr_v6, BPF_CORE_READ(sk, __sk_common.skc_v6_daddr.in6_u.u6_addr8)))
return 0;

break;
default:
return 0;
}

if (targ_laddr_hist) {
if (key.family == AF_INET6)
bpf_probe_read_kernel(key.addr, sizeof(key.addr), BPF_CORE_READ(inet, pinet6, saddr.in6_u.u6_addr8));
else
bpf_probe_read_kernel(key.addr, sizeof(inet->inet_saddr), &inet->inet_saddr);
} else if (targ_raddr_hist) {
if (key.family == AF_INET6)
bpf_probe_read_kernel(&key.addr, sizeof(key.addr), BPF_CORE_READ(sk, __sk_common.skc_v6_daddr.in6_u.u6_addr8));
else
bpf_probe_read_kernel(&key.addr, sizeof(inet->sk.__sk_common.skc_daddr), &inet->sk.__sk_common.skc_daddr);
} else {
key.family = 0;
}

if (targ_laddr_hist)
key = BPF_CORE_READ(inet, inet_saddr);
else if (targ_raddr_hist)
key = BPF_CORE_READ(inet, sk.__sk_common.skc_daddr);
else
key = 0;
histp = bpf_map_lookup_or_try_init(&hists, &key, &zero);
if (!histp)
return 0;
Expand Down
59 changes: 50 additions & 9 deletions libbpf-tools/tcprtt.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ static struct env {
__u16 rport;
__u32 laddr;
__u32 raddr;
__u8 laddr_v6[IPV6_LEN];
__u8 raddr_v6[IPV6_LEN];
bool milliseconds;
time_t duration;
time_t interval;
Expand Down Expand Up @@ -64,6 +66,8 @@ static const struct argp_option opts[] = {
{ "rport", 'P', "RPORT", 0, "filter for remote port" },
{ "laddr", 'a', "LADDR", 0, "filter for local address" },
{ "raddr", 'A', "RADDR", 0, "filter for remote address" },
{ "laddr-v6", 'c', "LADDR_V6", 0, "filter for local IPv6 address" },
{ "raddr-v6", 'C', "RADDR_V6", 0, "filter for remote IPv6 address" },
{ "byladdr", 'b', NULL, 0,
"show sockets histogram by local address" },
{ "byraddr", 'B', NULL, 0,
Expand All @@ -77,6 +81,7 @@ static const struct argp_option opts[] = {
static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
struct in_addr addr;
struct in6_addr addr_v6;

switch (key) {
case 'h':
Expand Down Expand Up @@ -139,6 +144,20 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
}
env.raddr = addr.s_addr;
break;
case 'c':
if (inet_pton(AF_INET6, arg, &addr_v6) < 1) {
fprintf(stderr, "invalid local IPv6 address: %s\n", arg);
argp_usage(state);
}
memcpy(env.laddr_v6, &addr_v6, sizeof(env.laddr_v6));
break;
case 'C':
if (inet_pton(AF_INET6, arg, &addr_v6) < 1) {
fprintf(stderr, "invalid remote IPv6 address: %s\n", arg);
argp_usage(state);
}
memcpy(env.raddr_v6, &addr_v6, sizeof(env.raddr_v6));
break;
case 'b':
env.laddr_hist = true;
break;
Expand Down Expand Up @@ -169,39 +188,52 @@ static void sig_handler(int sig)
static int print_map(struct bpf_map *map)
{
const char *units = env.milliseconds ? "msecs" : "usecs";
__u64 lookup_key = -1, next_key;
struct hist_key *lookup_key = NULL, next_key;
int err, fd = bpf_map__fd(map);
struct hist hist;

while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
while (!bpf_map_get_next_key(fd, lookup_key, &next_key)) {
err = bpf_map_lookup_elem(fd, &next_key, &hist);
if (err < 0) {
fprintf(stderr, "failed to lookup infos: %d\n", err);
return -1;
}

struct in_addr addr = {.s_addr = next_key };

if (env.laddr_hist)
printf("Local Address = %s ", inet_ntoa(addr));
printf("Local Address = ");
else if (env.raddr_hist)
printf("Remote Address = %s ", inet_ntoa(addr));
printf("Remote Address = ");
else
printf("All Addresses = ****** ");

if (env.laddr_hist || env.raddr_hist) {
__u16 family = next_key.family;
char str[INET6_ADDRSTRLEN];

if (!inet_ntop(family, next_key.addr, str, sizeof(str))) {
perror("converting IP to string:");
return -1;
}

printf("%s ", str);
}

if (env.extended)
printf("[AVG %llu]", hist.latency / hist.cnt);
printf("\n");
print_log2_hist(hist.slots, MAX_SLOTS, units);
lookup_key = next_key;
lookup_key = &next_key;
}

lookup_key = -1;
while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
lookup_key = NULL;
while (!bpf_map_get_next_key(fd, lookup_key, &next_key)) {
err = bpf_map_delete_elem(fd, &next_key);
if (err < 0) {
fprintf(stderr, "failed to cleanup infos: %d\n", err);
return -1;
}
lookup_key = next_key;
lookup_key = &next_key;
}

return 0;
Expand All @@ -214,6 +246,7 @@ int main(int argc, char **argv)
.parser = parse_arg,
.doc = argp_program_doc,
};
__u8 zero_addr_v6[IPV6_LEN] = {};
struct tcprtt_bpf *obj;
__u64 time_end = 0;
struct tm *tm;
Expand All @@ -225,6 +258,12 @@ int main(int argc, char **argv)
if (err)
return err;

if ((env.laddr || env.raddr)
&& (memcmp(env.laddr_v6, zero_addr_v6, sizeof(env.laddr_v6)) || memcmp(env.raddr_v6, zero_addr_v6, sizeof(env.raddr_v6)))) {
fprintf(stderr, "It is not permitted to filter by both IPv4 and IPv6\n");
return 1;
}

libbpf_set_print(libbpf_print_fn);

obj = tcprtt_bpf__open();
Expand All @@ -240,6 +279,8 @@ int main(int argc, char **argv)
obj->rodata->targ_dport = env.rport;
obj->rodata->targ_saddr = env.laddr;
obj->rodata->targ_daddr = env.raddr;
memcpy(obj->rodata->targ_saddr_v6, env.laddr_v6, sizeof(obj->rodata->targ_saddr_v6));
memcpy(obj->rodata->targ_daddr_v6, env.raddr_v6, sizeof(obj->rodata->targ_daddr_v6));
obj->rodata->targ_ms = env.milliseconds;

if (fentry_can_attach("tcp_rcv_established", NULL))
Expand Down
6 changes: 6 additions & 0 deletions libbpf-tools/tcprtt.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
#define __TCPRTT_H

#define MAX_SLOTS 27
#define IPV6_LEN 16

struct hist {
__u64 latency;
__u64 cnt;
__u32 slots[MAX_SLOTS];
};

struct hist_key {
__u16 family;
__u8 addr[IPV6_LEN];
};

#endif /* __TCPRTT_H */

0 comments on commit 6534868

Please sign in to comment.