Skip to content

Commit c79c4e8

Browse files
committed
pping: Eliminate flow creation/deletion concurrency issue
Only push flow events for opening/closing flows if the creation/deletion of the flow-state was successful (as indicated by the bpf_map_*_elem() return value). This should avoid outputting several flow creation/deletion messages in case multiple instances are trying to create/delete a flow concurrently, as could theoretically occur previously. Also set the last_timestamp value before creating a new flow, to avoid a race condition where the userspace cleanup might incorrectly determine that a flow is old before the last_timestamp value can be set. Explicitly skip the rate-limit for the first packet of a new flow to avoid it failing the rate-limit. This also fixes an issue where the first packet of a new flow would previously fail the rate-limit if the rate-limit was higher than current time uptime (CLOCK_MONOTONIC). Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
1 parent 1cadbe0 commit c79c4e8

File tree

1 file changed

+21
-15
lines changed

1 file changed

+21
-15
lines changed

pping/pping_kern.c

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ static void pping_egress(void *ctx, struct parsing_context *pctx)
350350
struct flow_event fe;
351351
struct flow_state *f_state;
352352
struct flow_state new_state = { 0 };
353+
bool new_flow = false;
353354
__u64 now;
354355

355356
if (parse_packet_identifier(pctx, &p_id, &fe.event_info) < 0)
@@ -372,20 +373,25 @@ static void pping_egress(void *ctx, struct parsing_context *pctx)
372373

373374
// No previous state - attempt to create it and push flow-opening event
374375
if (!f_state) {
375-
bpf_map_update_elem(&flow_state, &p_id.flow, &new_state,
376-
BPF_NOEXIST);
377-
f_state = bpf_map_lookup_elem(&flow_state, &p_id.flow);
376+
new_state.last_timestamp = now;
377+
if (bpf_map_update_elem(&flow_state, &p_id.flow, &new_state,
378+
BPF_NOEXIST) == 0) {
379+
new_flow = true;
380+
381+
if (fe.event_info.event != FLOW_EVENT_OPENING) {
382+
fe.event_info.event = FLOW_EVENT_OPENING;
383+
fe.event_info.reason =
384+
EVENT_REASON_FIRST_OBS_PCKT;
385+
}
386+
fill_flow_event(&fe, now, &p_id.flow,
387+
EVENT_SOURCE_EGRESS);
388+
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
389+
&fe, sizeof(fe));
390+
}
378391

392+
f_state = bpf_map_lookup_elem(&flow_state, &p_id.flow);
379393
if (!f_state) // Creation failed
380394
return;
381-
382-
if (fe.event_info.event != FLOW_EVENT_OPENING) {
383-
fe.event_info.event = FLOW_EVENT_OPENING;
384-
fe.event_info.reason = EVENT_REASON_FIRST_OBS_PCKT;
385-
}
386-
fill_flow_event(&fe, now, &p_id.flow, EVENT_SOURCE_EGRESS);
387-
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &fe,
388-
sizeof(fe));
389395
}
390396

391397
f_state->sent_pkts++;
@@ -397,8 +403,8 @@ static void pping_egress(void *ctx, struct parsing_context *pctx)
397403
f_state->last_id = p_id.identifier;
398404

399405
// Check rate-limit
400-
if (now < f_state->last_timestamp ||
401-
now - f_state->last_timestamp < config.rate_limit)
406+
if (!new_flow && (now < f_state->last_timestamp ||
407+
now - f_state->last_timestamp < config.rate_limit))
402408
return;
403409

404410
/*
@@ -463,8 +469,8 @@ static void pping_ingress(void *ctx, struct parsing_context *pctx)
463469

464470
validflow_out:
465471
// Wait with deleting flow until having pushed final RTT message
466-
if (fe.event_info.event == FLOW_EVENT_CLOSING && f_state) {
467-
bpf_map_delete_elem(&flow_state, &p_id.flow);
472+
if (fe.event_info.event == FLOW_EVENT_CLOSING &&
473+
bpf_map_delete_elem(&flow_state, &p_id.flow) == 0) {
468474
fill_flow_event(&fe, now, &p_id.flow, EVENT_SOURCE_INGRESS);
469475
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &fe,
470476
sizeof(fe));

0 commit comments

Comments
 (0)