Skip to content

Commit 32bdf11

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). This introduces potential (but unlikely) concurrency issues for flow opening/closing messages which are further described in the README. Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
1 parent 8a8f538 commit 32bdf11

File tree

4 files changed

+87
-30
lines changed

4 files changed

+87
-30
lines changed

pping/README.md

Lines changed: 14 additions & 0 deletions
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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -500,10 +500,11 @@ static bool packet_ts_timeout(void *key_ptr, void *val_ptr, __u64 now)
500500
static bool flow_timeout(void *key_ptr, void *val_ptr, __u64 now)
501501
{
502502
struct flow_event fe;
503-
__u64 ts = ((struct flow_state *)val_ptr)->last_timestamp;
503+
struct flow_state *f_state = val_ptr;
504504

505-
if (now > ts && now - ts > FLOW_LIFETIME) {
506-
if (print_event_func) {
505+
if (now > f_state->last_timestamp &&
506+
now - f_state->last_timestamp > FLOW_LIFETIME) {
507+
if (print_event_func && f_state->has_opened) {
507508
fe.event_type = EVENT_TYPE_FLOW;
508509
fe.timestamp = now;
509510
reverse_flow(&fe.flow, key_ptr);

pping/pping.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ struct flow_state {
8282
__u64 rec_pkts;
8383
__u64 rec_bytes;
8484
__u32 last_id;
85-
__u32 reserved;
85+
bool has_opened;
86+
enum flow_event_reason opening_reason;
87+
__u16 reserved;
8688
};
8789

8890
struct packet_id {

pping/pping_kern.c

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,28 @@ static bool is_rate_limited(__u64 now, __u64 last_ts, __u64 rtt)
390390
return now - last_ts < config.rate_limit;
391391
}
392392

393+
/*
394+
* Send a flow opening event through the perf-buffer.
395+
* As these events are only sent upon receiving a reply, need to access state
396+
* of the reverse flow to get reason flow was opened and when the original
397+
* packet opening the flow was sent.
398+
*/
399+
static void send_flow_open_event(void *ctx, struct packet_info *p_info,
400+
struct flow_state *rev_flow)
401+
{
402+
struct flow_event fe = {
403+
.event_type = EVENT_TYPE_FLOW,
404+
.flow_event_type = FLOW_EVENT_OPENING,
405+
.source = EVENT_SOURCE_PKT_DEST,
406+
.flow = p_info->pid.flow,
407+
.reason = rev_flow->opening_reason,
408+
.timestamp = rev_flow->last_timestamp,
409+
.reserved = 0,
410+
};
411+
412+
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &fe, sizeof(fe));
413+
}
414+
393415
/*
394416
* Sends a flow-event message based on p_info.
395417
*
@@ -420,24 +442,22 @@ static void send_flow_event(void *ctx, struct packet_info *p_info,
420442
}
421443

422444
/*
423-
* Attempt to create a new flow-state and push flow-opening message
445+
* Attempt to create a new flow-state.
424446
* Returns a pointer to the flow_state if successful, NULL otherwise
425447
*/
426-
static struct flow_state *create_flow(void *ctx, struct packet_info *p_info)
448+
static struct flow_state *create_flow(struct packet_info *p_info)
427449
{
428450
struct flow_state new_state = { 0 };
429451

430452
new_state.last_timestamp = p_info->time;
453+
new_state.opening_reason = p_info->event_type == FLOW_EVENT_OPENING ?
454+
p_info->event_reason :
455+
EVENT_REASON_FIRST_OBS_PCKT;
456+
431457
if (bpf_map_update_elem(&flow_state, &p_info->pid.flow, &new_state,
432458
BPF_NOEXIST) != 0)
433459
return NULL;
434460

435-
if (p_info->event_type != FLOW_EVENT_OPENING) {
436-
p_info->event_type = FLOW_EVENT_OPENING;
437-
p_info->event_reason = EVENT_REASON_FIRST_OBS_PCKT;
438-
}
439-
send_flow_event(ctx, p_info, false);
440-
441461
return bpf_map_lookup_elem(&flow_state, &p_info->pid.flow);
442462
}
443463

@@ -448,46 +468,68 @@ static struct flow_state *update_flow(void *ctx, struct packet_info *p_info,
448468
*new_flow = false;
449469

450470
f_state = bpf_map_lookup_elem(&flow_state, &p_info->pid.flow);
451-
if (!f_state && p_info->pid_valid) {
471+
472+
// Attempt to create flow if it does not exist
473+
if (!f_state && p_info->pid_valid &&
474+
!(p_info->event_type == FLOW_EVENT_CLOSING ||
475+
p_info->event_type == FLOW_EVENT_CLOSING_BOTH)) {
452476
*new_flow = true;
453-
f_state = create_flow(ctx, p_info);
477+
f_state = create_flow(p_info);
454478
}
455479

456480
if (!f_state)
457481
return NULL;
458482

483+
// Update flow state
459484
f_state->sent_pkts++;
460485
f_state->sent_bytes += p_info->payload;
461486

462487
return f_state;
463488
}
464489

465-
static struct flow_state *update_rev_flow(struct packet_info *p_info)
490+
static struct flow_state *update_rev_flow(void *ctx, struct packet_info *p_info)
466491
{
467492
struct flow_state *f_state;
468493

469494
f_state = bpf_map_lookup_elem(&flow_state, &p_info->reply_pid.flow);
470495
if (!f_state)
471496
return NULL;
472497

498+
// Is a new flow, push opening flow message
499+
if (!f_state->has_opened &&
500+
p_info->event_type != FLOW_EVENT_CLOSING_BOTH) {
501+
f_state->has_opened = true;
502+
send_flow_open_event(ctx, p_info, f_state);
503+
}
504+
505+
// Update flow state
473506
f_state->rec_pkts++;
474507
f_state->rec_bytes += p_info->payload;
475508

476509
return f_state;
477510
}
478511

479-
static void delete_closed_flows(void *ctx, struct packet_info *p_info)
512+
static void delete_closed_flows(void *ctx, struct packet_info *p_info,
513+
struct flow_state *flow,
514+
struct flow_state *rev_flow)
480515
{
516+
bool has_opened;
517+
481518
// Flow closing - try to delete flow state and push closing-event
482-
if (p_info->event_type == FLOW_EVENT_CLOSING ||
483-
p_info->event_type == FLOW_EVENT_CLOSING_BOTH) {
484-
if (!bpf_map_delete_elem(&flow_state, &p_info->pid.flow))
519+
if (flow && (p_info->event_type == FLOW_EVENT_CLOSING ||
520+
p_info->event_type == FLOW_EVENT_CLOSING_BOTH)) {
521+
has_opened = flow->has_opened;
522+
if (!bpf_map_delete_elem(&flow_state, &p_info->pid.flow) &&
523+
has_opened)
485524
send_flow_event(ctx, p_info, false);
486525
}
487526

488527
// Also close reverse flow
489-
if (p_info->event_type == FLOW_EVENT_CLOSING_BOTH) {
490-
if (!bpf_map_delete_elem(&flow_state, &p_info->reply_pid.flow))
528+
if (rev_flow && p_info->event_type == FLOW_EVENT_CLOSING_BOTH) {
529+
has_opened = rev_flow->has_opened;
530+
if (!bpf_map_delete_elem(&flow_state,
531+
&p_info->reply_pid.flow) &&
532+
has_opened)
491533
send_flow_event(ctx, p_info, true);
492534
}
493535
}
@@ -617,22 +659,20 @@ static void pping_match_packet(struct flow_state *f_state, void *ctx,
617659
static void pping(void *ctx, struct parsing_context *pctx)
618660
{
619661
struct packet_info p_info = { 0 };
620-
struct flow_state *f_state;
662+
struct flow_state *flow, *rev_flow;;
621663
bool new_flow;
622664

623665
if (parse_packet_identifier(pctx, &p_info) < 0)
624666
return;
625667

626-
if (p_info.event_type != FLOW_EVENT_CLOSING &&
627-
p_info.event_type != FLOW_EVENT_CLOSING_BOTH) {
628-
f_state = update_flow(ctx, &p_info, &new_flow);
629-
pping_timestamp_packet(f_state, ctx, pctx, &p_info, new_flow);
630-
}
668+
flow = update_flow(ctx, &p_info, &new_flow);
669+
pping_timestamp_packet(flow, ctx, pctx, &p_info, new_flow);
670+
671+
rev_flow = update_rev_flow(ctx, &p_info);
672+
pping_match_packet(rev_flow, ctx, pctx, &p_info);
631673

632-
f_state = update_rev_flow(&p_info);
633-
pping_match_packet(f_state, ctx, pctx, &p_info);
674+
delete_closed_flows(ctx, &p_info, flow, rev_flow);
634675

635-
delete_closed_flows(ctx, &p_info);
636676
}
637677

638678
// Programs

0 commit comments

Comments
 (0)