2023-11-30 03:02:08 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
* Copyright Red Hat
|
|
|
|
* Author: David Gibson <david@gibson.dropbear.id.au>
|
|
|
|
*
|
|
|
|
* Tracking for logical "flows" of packets.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdint.h>
|
2023-12-24 17:56:51 +01:00
|
|
|
#include <stdio.h>
|
2023-11-30 03:02:09 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
2023-11-30 03:02:08 +01:00
|
|
|
|
2023-11-30 03:02:09 +01:00
|
|
|
#include "util.h"
|
|
|
|
#include "passt.h"
|
|
|
|
#include "siphash.h"
|
|
|
|
#include "inany.h"
|
2023-11-30 03:02:08 +01:00
|
|
|
#include "flow.h"
|
2023-11-30 03:02:09 +01:00
|
|
|
#include "flow_table.h"
|
2023-11-30 03:02:08 +01:00
|
|
|
|
|
|
|
const char *flow_type_str[] = {
|
|
|
|
[FLOW_TYPE_NONE] = "<none>",
|
|
|
|
[FLOW_TCP] = "TCP connection",
|
|
|
|
[FLOW_TCP_SPLICE] = "TCP connection (spliced)",
|
|
|
|
};
|
|
|
|
static_assert(ARRAY_SIZE(flow_type_str) == FLOW_NUM_TYPES,
|
|
|
|
"flow_type_str[] doesn't match enum flow_type");
|
2023-11-30 03:02:09 +01:00
|
|
|
|
|
|
|
/* Global Flow Table */
|
2024-01-16 01:50:40 +01:00
|
|
|
unsigned flow_count;
|
2023-11-30 03:02:09 +01:00
|
|
|
union flow flowtab[FLOW_MAX];
|
2023-11-30 03:02:12 +01:00
|
|
|
|
2024-01-16 01:50:36 +01:00
|
|
|
/* Last time the flow timers ran */
|
|
|
|
static struct timespec flow_timer_run;
|
|
|
|
|
2024-01-16 01:50:39 +01:00
|
|
|
/** flow_log_ - Log flow-related message
|
|
|
|
* @f: flow the message is related to
|
|
|
|
* @pri: Log priority
|
|
|
|
* @fmt: Format string
|
|
|
|
* @...: printf-arguments
|
|
|
|
*/
|
|
|
|
void flow_log_(const struct flow_common *f, int pri, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
char msg[BUFSIZ];
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
(void)vsnprintf(msg, sizeof(msg), fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
logmsg(pri, "Flow %u (%s): %s", flow_idx(f), FLOW_TYPE(f), msg);
|
|
|
|
}
|
|
|
|
|
2024-01-16 01:50:41 +01:00
|
|
|
/**
|
|
|
|
* flow_alloc() - Allocate a new flow
|
|
|
|
*
|
|
|
|
* Return: pointer to an unused flow entry, or NULL if the table is full
|
|
|
|
*/
|
|
|
|
union flow *flow_alloc(void)
|
|
|
|
{
|
|
|
|
if (flow_count >= FLOW_MAX)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return &flowtab[flow_count++];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* flow_alloc_cancel() - Free a newly allocated flow
|
|
|
|
* @flow: Flow to deallocate
|
|
|
|
*
|
|
|
|
* @flow must be the last flow allocated by flow_alloc()
|
|
|
|
*/
|
|
|
|
void flow_alloc_cancel(union flow *flow)
|
|
|
|
{
|
|
|
|
ASSERT(FLOW_IDX(flow) == flow_count - 1);
|
|
|
|
memset(flow, 0, sizeof(*flow));
|
|
|
|
flow_count--;
|
|
|
|
}
|
|
|
|
|
2023-11-30 03:02:12 +01:00
|
|
|
/**
|
|
|
|
* flow_table_compact() - Perform compaction on flow table
|
|
|
|
* @c: Execution context
|
|
|
|
* @hole: Pointer to recently closed flow
|
|
|
|
*/
|
2024-01-16 01:50:42 +01:00
|
|
|
static void flow_table_compact(const struct ctx *c, union flow *hole)
|
2023-11-30 03:02:12 +01:00
|
|
|
{
|
|
|
|
union flow *from;
|
|
|
|
|
2024-01-16 01:50:40 +01:00
|
|
|
if (FLOW_IDX(hole) == --flow_count) {
|
2023-11-30 03:02:12 +01:00
|
|
|
debug("flow: table compaction: maximum index was %u (%p)",
|
|
|
|
FLOW_IDX(hole), (void *)hole);
|
|
|
|
memset(hole, 0, sizeof(*hole));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-01-16 01:50:40 +01:00
|
|
|
from = flowtab + flow_count;
|
2023-11-30 03:02:12 +01:00
|
|
|
memcpy(hole, from, sizeof(*hole));
|
|
|
|
|
|
|
|
switch (from->f.type) {
|
|
|
|
case FLOW_TCP:
|
|
|
|
tcp_tap_conn_update(c, &from->tcp, &hole->tcp);
|
|
|
|
break;
|
|
|
|
case FLOW_TCP_SPLICE:
|
|
|
|
tcp_splice_conn_update(c, &hole->tcp_splice);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
die("Unexpected %s in tcp_table_compact()",
|
|
|
|
FLOW_TYPE(&from->f));
|
|
|
|
}
|
|
|
|
|
|
|
|
debug("flow: table compaction (%s): old index %u, new index %u, "
|
|
|
|
"from: %p, to: %p",
|
|
|
|
FLOW_TYPE(&from->f), FLOW_IDX(from), FLOW_IDX(hole),
|
|
|
|
(void *)from, (void *)hole);
|
|
|
|
|
|
|
|
memset(from, 0, sizeof(*from));
|
|
|
|
}
|
2023-11-30 03:02:13 +01:00
|
|
|
|
2024-01-16 01:50:35 +01:00
|
|
|
/**
|
2024-01-16 01:50:36 +01:00
|
|
|
* flow_defer_handler() - Handler for per-flow deferred and timed tasks
|
2024-01-16 01:50:35 +01:00
|
|
|
* @c: Execution context
|
2024-01-16 01:50:36 +01:00
|
|
|
* @now: Current timestamp
|
2024-01-16 01:50:35 +01:00
|
|
|
*/
|
2024-01-16 01:50:40 +01:00
|
|
|
void flow_defer_handler(const struct ctx *c, const struct timespec *now)
|
2024-01-16 01:50:35 +01:00
|
|
|
{
|
2024-01-16 01:50:36 +01:00
|
|
|
bool timer = false;
|
2024-01-16 01:50:35 +01:00
|
|
|
union flow *flow;
|
|
|
|
|
2024-01-16 01:50:36 +01:00
|
|
|
if (timespec_diff_ms(now, &flow_timer_run) >= FLOW_TIMER_INTERVAL) {
|
|
|
|
timer = true;
|
|
|
|
flow_timer_run = *now;
|
|
|
|
}
|
|
|
|
|
2024-01-16 01:50:40 +01:00
|
|
|
for (flow = flowtab + flow_count - 1; flow >= flowtab; flow--) {
|
2024-01-16 01:50:42 +01:00
|
|
|
bool closed = false;
|
|
|
|
|
2024-01-16 01:50:35 +01:00
|
|
|
switch (flow->f.type) {
|
|
|
|
case FLOW_TCP:
|
2024-01-16 01:50:42 +01:00
|
|
|
closed = tcp_flow_defer(flow);
|
2024-01-16 01:50:35 +01:00
|
|
|
break;
|
|
|
|
case FLOW_TCP_SPLICE:
|
2024-01-16 01:50:42 +01:00
|
|
|
closed = tcp_splice_flow_defer(flow);
|
|
|
|
if (!closed && timer)
|
2024-01-16 01:50:36 +01:00
|
|
|
tcp_splice_timer(c, flow);
|
2024-01-16 01:50:35 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Assume other flow types don't need any handling */
|
|
|
|
;
|
|
|
|
}
|
2024-01-16 01:50:42 +01:00
|
|
|
|
|
|
|
if (closed)
|
|
|
|
flow_table_compact(c, flow);
|
2024-01-16 01:50:35 +01:00
|
|
|
}
|
|
|
|
}
|