feat(punktfunk/1): cross-VLAN/NAT video via data-plane hole-punching
ci / web (push) Successful in 29s
ci / rust (push) Failing after 38s
ci / docs-site (push) Successful in 30s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 6s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6s
apple / swift (push) Successful in 1m17s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 6s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 6s
deb / build-publish (push) Successful in 3m6s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 4m58s
docker / deploy-docs (push) Successful in 18s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 4m17s
ci / web (push) Successful in 29s
ci / rust (push) Failing after 38s
ci / docs-site (push) Successful in 30s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 6s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 6s
apple / swift (push) Successful in 1m17s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 6s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 6s
deb / build-publish (push) Successful in 3m6s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 4m58s
docker / deploy-docs (push) Successful in 18s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 4m17s
The video data plane is a raw UDP socket separate from the QUIC control connection. On a flat LAN the host can send straight to the client, but across NAT or a stateful inter-VLAN firewall the unsolicited host→client video is rejected (ICMP port-unreachable → the session dies immediately, while control/audio/input keep working since they ride the client-initiated QUIC). Observed live: a client on 192.168.6.2 streaming from a host on 192.168.1.48. Fix: client-initiated hole-punching. The client sends PUNCH_MAGIC datagrams from its data socket to the host's advertised data port (Welcome.udp_port); that opens the firewall/NAT return path and lets the host learn the client's OBSERVED source (the NAT-translated address, not the client's reported private one). The host (UdpTransport::connect_via_punch) waits ≤2.5s for the first punch and streams there, falling back to the client-reported address for clients that don't punch (flat-LAN behaviour unchanged). The client keeps a low-rate keepalive so a stateful firewall's idle timeout can't close the path during a static, low-bitrate scene. Wired into client-rs and the NativeClient connector (covers the Linux + Apple clients; the Apple app needs an xcframework rebuild to pick up the new core). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -788,9 +788,23 @@ async fn serve_session(
|
||||
let stop_stream = stop.clone();
|
||||
let result: Result<()> = async {
|
||||
tokio::task::spawn_blocking(move || -> Result<()> {
|
||||
let transport =
|
||||
UdpTransport::connect(&format!("0.0.0.0:{udp_port}"), &client_udp.to_string())
|
||||
.context("bind data plane")?;
|
||||
// Wait briefly for the client to hole-punch our data port, then stream to its OBSERVED
|
||||
// source — so video traverses a NAT / stateful inter-VLAN firewall (the client and host
|
||||
// can be on different subnets; control + side planes ride the client-initiated QUIC, but
|
||||
// the raw video UDP needs the client to open the path first). Falls back to the
|
||||
// client-reported address for clients that don't punch (flat-LAN, unchanged).
|
||||
let (transport, punched) = UdpTransport::connect_via_punch(
|
||||
&format!("0.0.0.0:{udp_port}"),
|
||||
&client_udp.to_string(),
|
||||
std::time::Duration::from_millis(2500),
|
||||
)
|
||||
.context("bind data plane")?;
|
||||
tracing::info!(
|
||||
%client_udp,
|
||||
punched,
|
||||
"data plane bound (punched=true → streaming to the client's observed source; \
|
||||
false → no hole-punch seen, using the reported address)"
|
||||
);
|
||||
let mut session = Session::new(cfg, Box::new(transport))
|
||||
.map_err(|e| anyhow!("host session: {e:?}"))?;
|
||||
match source {
|
||||
|
||||
Reference in New Issue
Block a user