namespace MoShiro.Protocol; /// /// Kind 目录。新增一种采集方法 = 在这里加一个静态字段并放进 ; /// 参数一律用 声明默认值和上下限。 /// public static class Kinds { private static readonly string[] s_directions = ["fwd", "rev", "bidir"]; /// 这个任务可能达到的最高探测频率(Hz):自适应时是 rate_max_hz,否则是固定的 rate_hz。预算、限速和准入都按它算。 public static long PeakRateHz(string kindId, ParamValues values) => (kindId is "udp_echo" or "tcp_echo") && 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 UdpEcho = new( "udp_echo", "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("udp_echo", p), p.GetInt("size"))); public static readonly KindSpec UdpTrain = new( "udp_train", "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 TcpEcho = new( "tcp_echo", "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_echo", p), p.GetInt("max"))); public static readonly KindSpec TcpBulk = new( "tcp_bulk", "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 Connect = new( "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 Hold = new( "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 IcmpEcho = new( "icmp_echo", "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 All = [UdpEcho, UdpTrain, TcpEcho, TcpBulk, Connect, Hold, IcmpEcho]; public static KindSpec? Find(string id) { foreach (KindSpec kind in All) { if (kind.Id == id) { return kind; } } return null; } }