pping: Add JSON output format

Add the option to output in JSON format by passing '-j' or '--json' to
pping. Include the protocol in the JSON format, and fix so kernel-side
actually stores the protocol in the flow_address struct.

Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
This commit is contained in:
Simon Sundberg
2021-04-30 11:36:41 +02:00
parent b4a810b09b
commit 0ed39800d0
2 changed files with 81 additions and 13 deletions

View File

@@ -67,10 +67,6 @@ struct map_cleanup_args {
struct pping_config { struct pping_config {
struct bpf_config bpf_config; struct bpf_config bpf_config;
__u64 cleanup_interval; __u64 cleanup_interval;
int xdp_flags;
int ifindex;
char ifname[IF_NAMESIZE];
bool force;
char *object_path; char *object_path;
char *ingress_sec; char *ingress_sec;
char *egress_sec; char *egress_sec;
@@ -78,9 +74,15 @@ struct pping_config {
char *packet_map; char *packet_map;
char *flow_map; char *flow_map;
char *rtt_map; char *rtt_map;
int xdp_flags;
int ifindex;
char ifname[IF_NAMESIZE];
bool json_format;
bool force;
}; };
static volatile int keep_running = 1; static volatile int keep_running = 1;
static bool json_started = false;
static const struct option long_options[] = { static const struct option long_options[] = {
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
@@ -88,6 +90,7 @@ static const struct option long_options[] = {
{ "rate-limit", required_argument, NULL, 'r' }, // Sampling rate-limit in ms { "rate-limit", required_argument, NULL, 'r' }, // Sampling rate-limit in ms
{ "force", no_argument, NULL, 'f' }, // Detach any existing XDP program on interface { "force", no_argument, NULL, 'f' }, // Detach any existing XDP program on interface
{ "cleanup-interval", required_argument, NULL, 'c' }, // Map cleaning interval in s { "cleanup-interval", required_argument, NULL, 'c' }, // Map cleaning interval in s
{ "json", no_argument, NULL, 'j' }, // Output in JSON format
{ 0, 0, NULL, 0 } { 0, 0, NULL, 0 }
}; };
@@ -139,7 +142,7 @@ static int parse_arguments(int argc, char *argv[], struct pping_config *config)
config->ifindex = 0; config->ifindex = 0;
while ((opt = getopt_long(argc, argv, "hfi:r:c:", long_options, while ((opt = getopt_long(argc, argv, "hfji:r:c:", long_options,
NULL)) != -1) { NULL)) != -1) {
switch (opt) { switch (opt) {
case 'i': case 'i':
@@ -176,6 +179,9 @@ static int parse_arguments(int argc, char *argv[], struct pping_config *config)
config->cleanup_interval = config->cleanup_interval =
cleanup_interval_s * NS_PER_SECOND; cleanup_interval_s * NS_PER_SECOND;
break; break;
case 'j':
config->json_format = true;
break;
case 'f': case 'f':
config->force = true; config->force = true;
break; break;
@@ -416,7 +422,8 @@ static int clean_map(int map_fd, size_t key_size, size_t value_size,
} }
#ifdef DEBUG #ifdef DEBUG
duration = get_time_ns(CLOCK_MONOTONIC) - now_nsec; duration = get_time_ns(CLOCK_MONOTONIC) - now_nsec;
printf("%d: Gone through %d entries and removed %d of them in %llu.%09llu s\n", fprintf(stderr,
"%d: Gone through %d entries and removed %d of them in %llu.%09llu s\n",
map_fd, entries, removed, duration / NS_PER_SECOND, map_fd, entries, removed, duration / NS_PER_SECOND,
duration % NS_PER_SECOND); duration % NS_PER_SECOND);
#endif #endif
@@ -481,7 +488,25 @@ static int format_ip_address(int af, const struct in6_addr *addr, char *buf,
return -EINVAL; return -EINVAL;
} }
static void handle_rtt_event(void *ctx, int cpu, void *data, __u32 data_size) static char *proto_to_str(__u16 proto)
{
static char buf[8];
switch (proto) {
case IPPROTO_TCP:
return "TCP";
case IPPROTO_ICMP:
return "ICMP";
case IPPROTO_ICMPV6:
return "ICMPv6";
default:
snprintf(buf, sizeof(buf), "%d", proto);
return buf;
}
}
static void print_rtt_event_standard(void *ctx, int cpu, void *data,
__u32 data_size)
{ {
const struct rtt_event *e = data; const struct rtt_event *e = data;
char saddr[INET6_ADDRSTRLEN]; char saddr[INET6_ADDRSTRLEN];
@@ -500,6 +525,42 @@ static void handle_rtt_event(void *ctx, int cpu, void *data, __u32 data_size)
ntohs(e->flow.saddr.port), daddr, ntohs(e->flow.daddr.port)); ntohs(e->flow.saddr.port), daddr, ntohs(e->flow.daddr.port));
} }
static void print_rtt_event_json(void *ctx, int cpu, void *data,
__u32 data_size)
{
const struct rtt_event *e = data;
char saddr[INET6_ADDRSTRLEN];
char daddr[INET6_ADDRSTRLEN];
__u64 time = convert_monotonic_to_realtime(e->timestamp);
format_ip_address(e->flow.ipv, &e->flow.saddr.ip, saddr, sizeof(saddr));
format_ip_address(e->flow.ipv, &e->flow.daddr.ip, daddr, sizeof(daddr));
if (json_started) {
printf(",");
} else {
printf("[");
json_started = true;
}
printf("\n{\"timestamp\":%llu.%09llu, \"rtt\":%llu.%09llu, "
"\"min_rtt\":%llu.%09llu, \"src_ip\":\"%s\", \"src_port\":%d, "
"\"dest_ip\":\"%s\", \"dest_port\":%d, \"protocol\":\"%s\"}",
time / NS_PER_SECOND, time % NS_PER_SECOND,
e->rtt / NS_PER_SECOND, e->rtt % NS_PER_SECOND,
e->min_rtt / NS_PER_SECOND, e->min_rtt % NS_PER_SECOND, saddr,
ntohs(e->flow.saddr.port), daddr, ntohs(e->flow.daddr.port),
proto_to_str(e->flow.proto));
}
static void end_json_output(void)
{
if (json_started)
printf("\n]\n");
else
printf("[]\n");
}
static void handle_missed_rtt_event(void *ctx, int cpu, __u64 lost_cnt) static void handle_missed_rtt_event(void *ctx, int cpu, __u64 lost_cnt)
{ {
fprintf(stderr, "Lost %llu RTT events on CPU %d\n", lost_cnt, cpu); fprintf(stderr, "Lost %llu RTT events on CPU %d\n", lost_cnt, cpu);
@@ -625,12 +686,13 @@ int main(int argc, char *argv[])
.flow_map = "flow_state", .flow_map = "flow_state",
.rtt_map = "rtt_events", .rtt_map = "rtt_events",
.xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST, .xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST,
.json_format = false,
.force = false, .force = false,
}; };
struct perf_buffer *pb = NULL; struct perf_buffer *pb = NULL;
struct perf_buffer_opts pb_opts = { struct perf_buffer_opts pb_opts = {
.sample_cb = handle_rtt_event, .sample_cb = print_rtt_event_standard,
.lost_cb = handle_missed_rtt_event, .lost_cb = handle_missed_rtt_event,
}; };
@@ -656,6 +718,9 @@ int main(int argc, char *argv[])
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (config.json_format)
pb_opts.sample_cb = print_rtt_event_json;
err = load_attach_bpfprogs(&obj, &config, &tc_attached, &xdp_attached); err = load_attach_bpfprogs(&obj, &config, &tc_attached, &xdp_attached);
if (err) { if (err) {
fprintf(stderr, fprintf(stderr,
@@ -700,6 +765,9 @@ int main(int argc, char *argv[])
cleanup: cleanup:
perf_buffer__free(pb); perf_buffer__free(pb);
if (config.json_format)
end_json_output();
if (xdp_attached) { if (xdp_attached) {
err = xdp_detach(config.ifindex, config.xdp_flags); err = xdp_detach(config.ifindex, config.xdp_flags);
if (err) if (err)

View File

@@ -197,16 +197,16 @@ static int parse_packet_identifier(struct parsing_context *ctx,
// Parse IPv4/6 header // Parse IPv4/6 header
if (proto == bpf_htons(ETH_P_IP)) { if (proto == bpf_htons(ETH_P_IP)) {
p_id->flow.ipv = AF_INET; p_id->flow.ipv = AF_INET;
proto = parse_iphdr(&ctx->nh, ctx->data_end, &iph); p_id->flow.proto = parse_iphdr(&ctx->nh, ctx->data_end, &iph);
} else if (proto == bpf_htons(ETH_P_IPV6)) { } else if (proto == bpf_htons(ETH_P_IPV6)) {
p_id->flow.ipv = AF_INET6; p_id->flow.ipv = AF_INET6;
proto = parse_ip6hdr(&ctx->nh, ctx->data_end, &ip6h); p_id->flow.proto = parse_ip6hdr(&ctx->nh, ctx->data_end, &ip6h);
} else { } else {
return -1; return -1;
} }
// Add new protocols here // Add new protocols here
if (proto == IPPROTO_TCP) { if (p_id->flow.proto == IPPROTO_TCP) {
err = parse_tcp_identifier(ctx, &saddr->port, &daddr->port, err = parse_tcp_identifier(ctx, &saddr->port, &daddr->port,
flow_closing, &p_id->identifier); flow_closing, &p_id->identifier);
if (err) if (err)