Files

207 lines
9.9 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace MoShiro.Protocol;
/// <summary>
/// Kind 目录。新增一种采集方法 = 在这里加一个静态字段并放进 <see cref="All"/>;
/// 参数一律用 <see cref="P"/> 声明默认值和上下限。
/// </summary>
public static class Kinds
{
private static readonly string[] s_directions = ["fwd", "rev", "bidir"];
/// <summary>这个任务可能达到的最高探测频率(Hz):自适应时是 <c>rate_max_hz</c>,否则是固定的 <c>rate_hz</c>。预算、限速和准入都按它算。</summary>
public static long PeakRateHz(string kindId, ParamValues values) =>
(kindId is "quic_ping" or "tcp_ping") && values.GetBool("adaptive") ? Math.Max(values.GetInt("rate_max_hz"), values.GetInt("rate_min_hz")) : values.GetInt("rate_hz");
private static readonly StreamSpec s_echoMsg = new(
"msg",
[
Col.Seq("seq"), Col.U16("size"), Col.U16("w"),
Col.TsDialer("t1"), Col.TsListener("t2", nullable: true), Col.TsListener("t3", nullable: true), Col.TsDialer("t4", nullable: true),
],
Side.Dialer,
"seq");
private static readonly StreamSpec s_tcpInfo = new(
"tcp_info",
[
Col.TsLocal("ts"), Col.U8("side"), Col.U32("srtt_us"), Col.U32("rttvar_us"), Col.U32("cwnd"),
Col.U32("retrans_total"), Col.U32("segs_out"), Col.U64("bytes_acked"),
new ColumnSpec("conn", ColumnType.U64, ColumnEnc.Plain, true),
],
Union: true);
public static readonly KindSpec QuicPing = new(
"quic_ping",
"QUIC Datagram echo",
"Fixed-rate QUIC Datagram echo with four timestamps per packet.",
Scope.Line,
new BlockSet("quic_datagram", "fixed", "fixed", "echo", []),
[
P.Bool("adaptive", true, "Adapt the probe rate to the loss rate: low when the line is quiet, up to rate_max_hz when it loses packets, exponential back-off when it is unreachable."),
P.Int("rate_hz", 10, 1, 200, "Hz", "Fixed rate, used when adaptive is false."),
P.Int("rate_min_hz", 2, 1, 200, "Hz"),
P.Int("rate_max_hz", 10, 1, 200, "Hz", "Highest rate, and the reference grid for sample weights."),
P.Number("tau_pct", 0.2, 0.01, 5, "%", "Loss rate a quiet line has to be able to prove below, at 95% confidence."),
P.Number("rtt_tol_pct", 2, 0.1, 20, "%", "Half-width of the 95% confidence interval of the RTT p99 a quiet line has to keep, as a share of p99 (never below 0.3 ms)."),
P.Int("size", 64, 44, 1400, "B"),
P.Int("port", 7443, 1, 65535),
],
[s_echoMsg],
p => Bandwidth.UdpEcho(PeakRateHz("quic_ping", p), p.GetInt("size")));
public static readonly KindSpec QuicStream = new(
"quic_stream",
"QUIC Datagram train",
"Fixed-rate or laddered one-way QUIC Datagram stream (Agent to 拨号方) for loss and delay variation under load.",
Scope.Line,
new BlockSet("quic_datagram", "ladder", "fixed", "sink", []),
[
P.Number("mbps", 10, 0.1, 1000, "Mbit/s"),
P.Int("size", 1250, 200, 1400, "B"),
P.Int("duration_s", 10, 1, 120, "s"),
P.Enum("direction", "rev", s_directions),
P.Int("port", 7443, 1, 65535),
],
[
new StreamSpec("pkt", [Col.Seq("seq"), Col.U16("size"), Col.TsListener("t_send"), Col.TsDialer("t_recv", nullable: true)], Side.Listener, "seq"),
new StreamSpec("step", [Col.U16("index"), Col.U32("mbps"), Col.TsDialer("start_ts"), Col.TsDialer("end_ts")]),
],
p => (long)(p.GetNumber("mbps") * 1_000_000));
public static readonly KindSpec TcpPing = new(
"tcp_ping",
"TCP echo",
"Small TCP messages echoed with four timestamps, plus periodic kernel tcp_info samples.",
Scope.Line,
new BlockSet("tcp", "fixed", "uniform", "echo", ["tcp_info"]),
[
P.Bool("adaptive", true, "Adapt the echo rate: as low as the loss bound and the RTT percentile precision allow when the line is quiet, the maximum when it is not, back-off when it stalls."),
P.Int("rate_hz", 20, 1, 200, "Hz", "Fixed rate, used when adaptive is false."),
P.Int("rate_min_hz", 2, 1, 200, "Hz"),
P.Int("rate_max_hz", 20, 1, 200, "Hz", "Highest rate, and the reference grid for sample weights."),
P.Number("tau_pct", 0.2, 0.01, 5, "%", "Share of retransmitted segments a quiet line has to be able to prove below, at 95% confidence."),
P.Number("rtt_tol_pct", 2, 0.1, 20, "%", "Half-width of the 95% confidence interval of the RTT p99 a quiet line has to keep, as a share of p99 (never below 0.3 ms)."),
P.Int("min", 32, 16, 1400, "B"),
P.Int("max", 64, 16, 1400, "B"),
P.Int("tcp_info_ms", 1000, 10, 1000, "ms"),
P.Int("port", 7444, 1, 65535),
P.Bool("tls", false),
],
[s_echoMsg, s_tcpInfo],
p => Bandwidth.TcpEcho(PeakRateHz("tcp_ping", p), p.GetInt("max")));
public static readonly KindSpec TcpTransfer = new(
"tcp_transfer",
"TCP bulk",
"Bulk TCP transfer on several streams with periodic byte counters and tcp_info.",
Scope.Line,
new BlockSet("tcp", "fixed", "fixed", "source", ["tcp_info"]),
[
P.Int("streams", 4, 1, 16),
P.Int("duration_s", 4, 1, 120, "s"),
P.Enum("direction", "rev", s_directions),
P.Int("sample_ms", 100, 20, 1000, "ms"),
P.Int("port", 7444, 1, 65535),
P.Bool("tls", true),
],
[
new StreamSpec("bytes", [Col.TsDialer("ts"), Col.U8("conn"), Col.U64("bytes")]),
s_tcpInfo,
],
_ => 0);
public static readonly KindSpec TcpConnect = new(
"tcp_connect",
"Connect",
"Repeated TCP and TLS connection establishment with per-phase timings and outcome.",
Scope.Line | Scope.Target,
new BlockSet("tcp", "fixed", "fixed", "connect", []),
[
P.Int("per_minute", 6, 1, 60),
P.Bool("tls", true),
P.Int("timeout_ms", 5000, 500, 30000, "ms"),
P.Int("port", 7444, 1, 65535),
],
[
new StreamSpec(
"conn",
[Col.Seq("attempt"), Col.TsDialer("ts"), Col.U32("tcp_us", nullable: true), Col.U32("tls_us", nullable: true), Col.U8("outcome")]),
],
p => p.GetInt("per_minute") * 4000 / 60);
public static readonly KindSpec TlsHold = new(
"tls_hold",
"Hold",
"A long-lived TLS connection with periodic heartbeats; records resets and lifetime.",
Scope.Line,
new BlockSet("tls", "fixed", "fixed", "hold", []),
[
P.Int("heartbeat_s", 30, 5, 300, "s"),
P.Int("port", 7444, 1, 65535),
],
[
new StreamSpec("hold", [Col.TsDialer("ts"), Col.U8("event"), Col.U32("lifetime_ms")]),
],
p => 2000 / Math.Max(1, p.GetInt("heartbeat_s")) * 8);
public static readonly KindSpec HttpRequest = new(
"http_request",
"HTTP request",
"One Agent requests a URL over HTTP/1.1, HTTP/2 or HTTP/3 and records DNS, TCP, time to first byte, total time and status. Target is the URL.",
Scope.Target,
new BlockSet("http", "fixed", "fixed", "request", []),
[
P.Enum("version", "h2", ["h1", "h2", "h3"], "HTTP version; the request fails instead of falling back."),
P.Enum("method", "GET", ["GET", "HEAD"]),
P.Bool("adaptive", true, "Adapt the request rate to the failure rate: low while the target answers, up to rate_max_per_min when it fails, exponential back-off while it is unreachable."),
P.Int("per_minute", 6, 1, 120, "/min", "Fixed rate, used when adaptive is false."),
P.Int("rate_min_per_min", 1, 1, 120, "/min"),
P.Int("rate_max_per_min", 12, 1, 120, "/min", "Highest rate, and the reference grid for sample weights."),
P.Number("tau_pct", 5, 0.1, 50, "%", "Failure rate a quiet target has to be able to prove below, at 95% confidence."),
P.Int("timeout_ms", 10000, 500, 60000, "ms"),
P.Int("read_kb", 16, 0, 1024, "KiB", "Body bytes to read before the request counts as finished (0: headers only)."),
P.Int("ok_status", 0, 0, 599, null, "Status that counts as success; 0 means any status below 400."),
P.Bool("tls_verify", true),
],
[
new StreamSpec(
"req",
[
Col.Seq("attempt"), Col.TsDialer("ts"), Col.U16("w"),
Col.U32("dns_us", nullable: true), Col.U32("tcp_us", nullable: true), Col.U32("ttfb_us", nullable: true), Col.U32("total_us", nullable: true),
Col.U16("status", nullable: true), Col.U8("proto", nullable: true), Col.U8("outcome"),
]),
],
p => p.GetInt("rate_max_per_min") * (2000 + p.GetInt("read_kb") * 1024) * 8 / 60);
public static readonly KindSpec IcmpPing = new(
"icmp_ping",
"ICMP echo",
"拨号方 or Agent side ICMP echo to a target address.",
Scope.Target,
new BlockSet("icmp", "fixed", "fixed", "echo", []),
[
P.Int("rate_hz", 1, 1, 20, "Hz"),
P.Int("size", 64, 44, 1400, "B"),
],
[
new StreamSpec("msg", [Col.Seq("seq"), Col.U16("size"), Col.TsLocal("t1"), new ColumnSpec("t4", ColumnType.I64, ColumnEnc.Delta, true, ColumnRole.TsLocal)]),
],
p => Bandwidth.UdpEcho(p.GetInt("rate_hz"), p.GetInt("size")));
public static readonly IReadOnlyList<KindSpec> All = [QuicPing, QuicStream, TcpPing, TcpTransfer, TcpConnect, TlsHold, HttpRequest, IcmpPing];
public static KindSpec? Find(string id)
{
foreach (KindSpec kind in All)
{
if (kind.Id == id)
{
return kind;
}
}
return null;
}
}