跳转至

非对称链路

一台交换机上的两台主机,每条链路只在单一方向上加延迟,反向不 整形。最终的 ping RTT 等于两条单向延迟之和。

你将看到什么

h1 ping h2 报告大约 220ms 的 RTT,抖动在亚毫秒级别——集成测试 中实测的 RTT 是 min/avg/max/mdev = 220.981/221.288/222.048/0.396 ms

拓扑

examples/asymmetric_link/topology.py

"""Two hosts plus one switch with asymmetric link delays.

The h1↔s1 link adds 200 ms egress delay at h1 (h1→s1 direction); the
h2↔s1 link adds 20 ms egress delay at s1 (s1→h2 direction). End-to-end
one-way h1→h2 delay is therefore ~220 ms; reverse h2→h1 is ~0 ms; ping
RTT from h1 to h2 is ~220 ms.

Run with:

    sudo p4net examples/asymmetric_link/topology.py

Then in the shell:

    h1 ping h2 count=10 timeout=2
"""

from __future__ import annotations

from pathlib import Path

from p4net import Network
from p4net.topo import Topology

HERE = Path(__file__).resolve().parent

topology = Topology()
h1 = topology.add_host("h1", ip="10.0.0.1/24", mac="00:00:00:00:00:01")
h2 = topology.add_host("h2", ip="10.0.0.2/24", mac="00:00:00:00:00:02")
s1 = topology.add_switch("s1", p4_src=HERE / "asymmetric.p4")
# h1↔s1: shape only the h1→s1 direction (egress at h1).
topology.add_link(h1, s1, port_b=1, delay_a_to_b="200ms")
# h2↔s1: shape only the s1→h2 direction. With a=h2 and b=s1, "b→a" =
# "s1→h2", so delay_b_to_a applies in that direction.
topology.add_link(h2, s1, port_b=2, delay_b_to_a="20ms")


def setup(net: Network) -> None:
    """Pre-seed static ARP."""
    h1 = net.host("h1")
    h2 = net.host("h2")
    h1.exec(
        [
            "ip",
            "neigh",
            "replace",
            "10.0.0.2",
            "lladdr",
            "00:00:00:00:00:02",
            "dev",
            "h1-eth0",
            "nud",
            "permanent",
        ]
    )
    h2.exec(
        [
            "ip",
            "neigh",
            "replace",
            "10.0.0.1",
            "lladdr",
            "00:00:00:00:00:01",
            "dev",
            "h2-eth0",
            "nud",
            "permanent",
        ]
    )


if __name__ == "__main__":
    from p4net.cli.main import main

    raise SystemExit(main([__file__]))

h1↔s1 链路上的 delay_a_to_b="200ms" 应用在 a 侧(h1 命名 空间)。h2↔s1 链路上的 delay_b_to_a="20ms" 应用在 b 侧 (root 命名空间里 s1 朝向 h2 的 veth)。

方向 路径 整形延迟
h1 → s1 h1 出向 veth 200ms
s1 → h2 s1 出向 veth 20ms
h2 → s1 (无) 0ms
s1 → h1 (无) 0ms

端到端单向 h1→h2:200 + 20 = 220ms。反向 h2→h1:0ms。 ping RTT(h1→h2 echo + h2→h1 reply):220ms。

P4 程序

examples/asymmetric_link/asymmetric.p4

#include <core.p4>
#include <v1model.p4>

header ethernet_t {
    bit<48> dstAddr;
    bit<48> srcAddr;
    bit<16> etherType;
}

struct headers {
    ethernet_t ethernet;
}

struct metadata {}

parser MyParser(packet_in pkt, out headers hdr, inout metadata meta,
                inout standard_metadata_t std) {
    state start {
        pkt.extract(hdr.ethernet);
        transition accept;
    }
}

control MyVerifyChecksum(inout headers hdr, inout metadata meta) { apply {} }

control MyIngress(inout headers hdr, inout metadata meta,
                  inout standard_metadata_t std) {
    apply {
        if (std.ingress_port == 1) {
            std.egress_spec = 2;
        } else if (std.ingress_port == 2) {
            std.egress_spec = 1;
        } else {
            mark_to_drop(std);
        }
    }
}

control MyEgress(inout headers hdr, inout metadata meta,
                 inout standard_metadata_t std) { apply {} }

control MyComputeChecksum(inout headers hdr, inout metadata meta) { apply {} }

control MyDeparser(packet_out pkt, in headers hdr) {
    apply {
        pkt.emit(hdr.ethernet);
    }
}

V1Switch(MyParser(), MyVerifyChecksum(), MyIngress(), MyEgress(),
         MyComputeChecksum(), MyDeparser()) main;

快速上手相同的端口翻转流水线。非对称完全 依赖链路损伤实现,与数据平面无关。

运行

sudo p4net examples/asymmetric_link/topology.py
p4net> h1 ping h2 5 3
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=222 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=221 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=221 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=221 ms
64 bytes from 10.0.0.2: icmp_seq=5 ttl=64 time=221 ms

--- 10.0.0.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4126ms
rtt min/avg/max/mdev = 220.981/221.288/222.048/0.396 ms

(这是 phase-12 验证时实测的输出。)

在 h1 命名空间执行 tc qdisc show 可以确认 netem 队列在哪一侧:

p4net> h1 cmd tc qdisc show dev h1-eth0
qdisc netem 8001: root ... delay 200ms

关键设计点

  • 方向语义由 (a, b) 顺序决定Link(h1, s1, delay_a_to_b="200ms") 整形 h1→s1。如果交换写法 为 Link(s1, h1, ...),同样的 delay_a_to_b 整形的将是 s1→h1。add_link(a, b, ...) 的参数顺序就是基准。
  • 同一参数同时给对称值与非对称值会被构造期拒绝。不能既写 delay="50ms" 又写 delay_a_to_b="100ms"——同一个参数只能 二选一。但跨参数自由组合是允许的(例如对称 bandwidth 加 非对称 delay)。

可尝试的变体

  • 改用 loss_pct_a_to_b=50.0 替代延迟,观察 pingall 矩阵呈 50% 丢包。
  • delay_a_to_b="200ms"delay_b_to_a="200ms" 同时设上, 恢复对称 400ms RTT。
  • 用非对称延迟搭配对称带宽整形(bandwidth="1mbit")模拟典型的 家用宽带链路。