从从属控制器观察 PacketIn
我想要做: 跑一个只读监控程序,在不占用主控权的前提下订阅 PacketIn,而主控控制器继续管理设备的转发状态。
模式
java
import io.github.zhh2001.jp4.P4Switch;
import io.github.zhh2001.jp4.ElectionId;
import io.github.zhh2001.jp4.entity.PacketIn;
import java.util.concurrent.Flow;
import java.util.concurrent.atomic.AtomicInteger;
try (P4Switch monitor = P4Switch.connect("127.0.0.1:50051")
.electionId(ElectionId.of(1)) // low id — primary holds something higher
.asSecondary()) {
// Secondaries can't bindPipeline (it's a write), but loadPipeline is a
// read-only RPC that fetches the device's currently-installed P4Info
// into the local switch. PacketIn parsing requires this schema.
monitor.loadPipeline();
AtomicInteger counter = new AtomicInteger();
monitor.packetInStream().subscribe(new Flow.Subscriber<PacketIn>() {
@Override public void onSubscribe(Flow.Subscription s) {
s.request(Long.MAX_VALUE);
}
@Override public void onNext(PacketIn p) {
int port = p.metadataInt("ingress_port");
int bytes = p.payload().toByteArray().length;
System.out.printf("[#%d] port=%d bytes=%d%n",
counter.incrementAndGet(), port, bytes);
}
@Override public void onError(Throwable t) { /* log + reconnect logic */ }
@Override public void onComplete() { /* device closed stream */ }
});
// ... block until shutdown signal ...
}实际使用: network-monitor.
走读
- 用
asSecondary()加低 election id 连接。 id 必须低于现有主控的。如果设备回复Acquired(你成了主控),检查运维侧的 election id 协调 ——asSecondary()是请求,不是保证。 loadPipeline()是bindPipeline的读侧对应物。 它发出GetForwardingPipelineConfig、填充本地P4Info、然后返回。没有它,packetInStream()抛P4PipelineException("no pipeline bound; ..."),因为入站解析没有元数据 schema 可以解码。packetInStream()返回Flow.Publisher<PacketIn>(JDK 9+,无额外依赖)。每个订阅者看到每个报文。要做背压,请求Long.MAX_VALUE以下的数;要接入响应式栈,用JdkFlowAdapter.flowPublisherToFlux(...)(Reactor)或FlowAdapters.toPublisher(...)(Reactive Streams)适配。onNext跑在 jp4 回调执行器。 与onPacketIn同样的 FIFO 契约 —— 慢消费者拖慢后续分发,但永远不阻塞 gRPC 入站线程。
重点: BMv2 PacketIn 投递的实现细节
P4Runtime 规范 §16.1 规定 PacketIn 必须 投递给主控客户端,应当 投递给从属。BMv2 只实现了 MUST。BMv2 上的从属连接会成功注册并加载流水线,但 不会 通过 packetInStream() 收到 PacketIn —— 设备只把它们扇出给主控。
广播给从属的目标设备(部分 Tofino / Stratum 构建)上,同样的订阅者代码原样挂上去即可。Java 代码与目标无关。
如果你在 BMv2 上测试这个 recipe 并发现收不到报文,这是规范 MUST/SHOULD 的差距,不是 jp4 的 bug —— 见 故障排查: PacketIn 处理器从未触发。
参见
- 连接与仲裁 —— 主控 vs 从属、election id、主控权语义。
- 报文 I/O ——
packetInStream()机制与三种消费风格。 - 线程模型 ——
Flow.Subscriber.onNext跑在哪个执行器。 network-monitor示例 —— 本 recipe 提取自该示例。