Skip to content

Commit 811ad5b

Browse files
committed
pping: Only consider flow opened on reply
Wait with sending a flow open message until a reply has been seen for the flow. Likewise, only emit a flow closing event if the flow has first been opened (that is, a reply has been seen). Concerns with this commit: - Will no longer report any RTT for packets that close the flow (will delete the relevant flow_state before attempting to match the packet). - Introduces potential concurrency issues for flow opening/closing messages (although I deem them unlikely). The README has been updated to describe these. Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
1 parent 3d2d2eb commit 811ad5b

File tree

4 files changed

+84
-44
lines changed

4 files changed

+84
-44
lines changed

pping/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,20 @@ estimate anyways (may never calculate RTT for packet with the true minimum
253253
RTT). And even without sampling there is some inherent sampling due to TCP
254254
timestamps only being updated at a limited rate (1000 Hz).
255255

256+
#### Outputting flow opening/closing events
257+
A flow is not considered opened until a reply has been seen for it. The
258+
`flow_state` map keeps information about if the flow has been opened or not,
259+
which is checked and updated for each reply. The check and update of this
260+
information is not performed atomically, which may result in multiple replies
261+
thinking they are the first, emitting multiple flow-opened events, in case they
262+
are processed concurrently.
263+
264+
Likewise, when flows are closed it checks if the flow has been opened to
265+
determine if a flow closing message should be sent. If multiple replies are
266+
processed concurrently, it's possible one of them will update the flow-open
267+
information and emit a flow opening message, but another reply closing the flow
268+
without thinking it's ever been opened, thus not sending a flow closing message.
269+
256270
## Similar projects
257271
Passively measuring the RTT for TCP traffic is not a novel concept, and there
258272
exists a number of other tools that can do so. A good overview of how passive

pping/pping.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -473,10 +473,11 @@ static bool packet_ts_timeout(void *key_ptr, void *val_ptr, __u64 now)
473473
static bool flow_timeout(void *key_ptr, void *val_ptr, __u64 now)
474474
{
475475
struct flow_event fe;
476-
__u64 ts = ((struct flow_state *)val_ptr)->last_timestamp;
476+
struct flow_state *f_state = val_ptr;
477477

478-
if (now > ts && now - ts > FLOW_LIFETIME) {
479-
if (print_event_func) {
478+
if (now > f_state->last_timestamp &&
479+
now - f_state->last_timestamp > FLOW_LIFETIME) {
480+
if (print_event_func && f_state->has_opened) {
480481
fe.event_type = EVENT_TYPE_FLOW;
481482
fe.timestamp = now;
482483
reverse_flow(&fe.flow, key_ptr);

pping/pping.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ struct flow_state {
8080
__u64 rec_pkts;
8181
__u64 rec_bytes;
8282
__u32 last_id;
83-
__u32 reserved;
83+
bool has_opened;
84+
enum flow_event_reason opening_reason;
85+
__u16 reserved;
8486
};
8587

8688
struct packet_id {

pping/pping_kern.c

+63-40
Original file line numberDiff line numberDiff line change
@@ -417,59 +417,105 @@ static void fill_flow_event(struct flow_event *fe, struct packet_info *p_info,
417417
}
418418

419419
/*
420-
* Attempt to create a new flow-state and push flow-opening message
420+
* Attempt to create a new flow-state.
421421
* Returns a pointer to the flow_state if successful, NULL otherwise
422422
*/
423-
static struct flow_state *create_flow(struct network_tuple *flow, void *ctx,
424-
struct packet_info *p_info,
425-
struct flow_event *fe)
423+
static struct flow_state *create_flow(struct packet_info *p_info)
426424
{
427425
struct flow_state new_state = { 0 };
428-
429426
new_state.last_timestamp = p_info->time;
427+
new_state.opening_reason = p_info->event_type == FLOW_EVENT_OPENING ?
428+
p_info->event_reason :
429+
EVENT_REASON_FIRST_OBS_PCKT;
430+
430431
if (bpf_map_update_elem(&flow_state, &p_info->pid.flow, &new_state,
431432
BPF_NOEXIST) != 0)
432433
return NULL;
433434

434-
if (p_info->event_type != FLOW_EVENT_OPENING) {
435-
p_info->event_type = FLOW_EVENT_OPENING;
436-
p_info->event_reason = EVENT_REASON_FIRST_OBS_PCKT;
437-
}
438-
fill_flow_event(fe, p_info, true);
439-
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, fe, sizeof(*fe));
440-
441435
return bpf_map_lookup_elem(&flow_state, &p_info->pid.flow);
442436
}
443437

444438
static struct flow_state *update_flow(void *ctx, struct packet_info *p_info,
445439
struct flow_event *fe, bool *new_flow)
446440
{
447441
struct flow_state *f_state;
442+
bool has_opened;
448443
*new_flow = false;
449444

450445
f_state = bpf_map_lookup_elem(&flow_state, &p_info->pid.flow);
446+
447+
// Flow is closing - attempt to delete state if it exists
448+
if (p_info->event_type == FLOW_EVENT_CLOSING ||
449+
p_info->event_type == FLOW_EVENT_CLOSING_BOTH) {
450+
if (!f_state)
451+
return NULL;
452+
453+
has_opened = f_state->has_opened;
454+
if (bpf_map_delete_elem(&flow_state, &p_info->pid.flow) == 0 &&
455+
has_opened) {
456+
fill_flow_event(fe, p_info, true);
457+
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
458+
fe, sizeof(*fe));
459+
}
460+
return NULL;
461+
}
462+
463+
// Attempt to create flow if it does not exist
451464
if (!f_state && p_info->pid_valid) {
452465
*new_flow = true;
453-
f_state = create_flow(&p_info->pid.flow, ctx, p_info, fe);
466+
f_state = create_flow(p_info);
454467
}
455468

456469
if (!f_state)
457470
return NULL;
458471

472+
// Update flow state
459473
f_state->sent_pkts++;
460474
f_state->sent_bytes += p_info->payload;
461475

462476
return f_state;
463477
}
464478

465-
static struct flow_state *update_rev_flow(struct packet_info *p_info)
479+
static struct flow_state *update_rev_flow(void *ctx, struct packet_info *p_info,
480+
struct flow_event *fe)
466481
{
467482
struct flow_state *f_state;
483+
bool has_opened;
468484

469485
f_state = bpf_map_lookup_elem(&flow_state, &p_info->reply_pid.flow);
486+
470487
if (!f_state)
471488
return NULL;
472489

490+
// Close reverse flow
491+
if (p_info->event_type == FLOW_EVENT_CLOSING_BOTH) {
492+
493+
has_opened = f_state->has_opened;
494+
if (bpf_map_delete_elem(&flow_state, &p_info->reply_pid.flow) ==
495+
0 && has_opened) {
496+
fill_flow_event(fe, p_info, false);
497+
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
498+
fe, sizeof(*fe));
499+
}
500+
return NULL;
501+
}
502+
503+
// Is a new flow, push opening flow message
504+
if (!f_state->has_opened) {
505+
f_state->has_opened = true;
506+
507+
fe->event_type = EVENT_TYPE_FLOW;
508+
fe->flow = p_info->pid.flow;
509+
fe->timestamp = f_state->last_timestamp;
510+
fe->flow_event_type = FLOW_EVENT_OPENING;
511+
fe->reason = f_state->opening_reason;
512+
fe->source = EVENT_SOURCE_PKT_DEST;
513+
fe->reserved = 0;
514+
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, fe,
515+
sizeof(*fe));
516+
}
517+
518+
// Update flow state
473519
f_state->rec_pkts++;
474520
f_state->rec_bytes += p_info->payload;
475521

@@ -607,34 +653,11 @@ static void pping(void *ctx, struct parsing_context *pctx)
607653
if (parse_packet_identifier(pctx, &p_info) < 0)
608654
return;
609655

610-
if (p_info.event_type != FLOW_EVENT_CLOSING &&
611-
p_info.event_type != FLOW_EVENT_CLOSING_BOTH) {
612-
f_state = update_flow(ctx, &p_info, &fe, &new_flow);
613-
pping_timestamp_packet(f_state, ctx, pctx, &p_info, new_flow);
614-
}
656+
f_state = update_flow(ctx, &p_info, &fe, &new_flow);
657+
pping_timestamp_packet(f_state, ctx, pctx, &p_info, new_flow);
615658

616-
f_state = update_rev_flow(&p_info);
659+
f_state = update_rev_flow(ctx, &p_info, &fe);
617660
pping_match_packet(f_state, ctx, pctx, &p_info);
618-
619-
// Flow closing - try to delete flow state and push closing-event
620-
if (p_info.event_type == FLOW_EVENT_CLOSING ||
621-
p_info.event_type == FLOW_EVENT_CLOSING_BOTH) {
622-
if (bpf_map_delete_elem(&flow_state, &p_info.pid.flow) == 0) {
623-
fill_flow_event(&fe, &p_info, true);
624-
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
625-
&fe, sizeof(fe));
626-
}
627-
}
628-
629-
// Also close reverse flow
630-
if (p_info.event_type == FLOW_EVENT_CLOSING_BOTH) {
631-
if (bpf_map_delete_elem(&flow_state, &p_info.reply_pid.flow) ==
632-
0) {
633-
fill_flow_event(&fe, &p_info, false);
634-
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
635-
&fe, sizeof(fe));
636-
}
637-
}
638661
}
639662

640663
// Programs

0 commit comments

Comments
 (0)