Skip to content

rtp_relay: SIP→WebRTC transcoding regression tests (GH-3902)Feat/rtp relay tests - #33

Merged
razvancrainea merged 2 commits into
OpenSIPS:mainfrom
NormB:feat/rtp_relay-tests
Jul 31, 2026
Merged

rtp_relay: SIP→WebRTC transcoding regression tests (GH-3902)Feat/rtp relay tests#33
razvancrainea merged 2 commits into
OpenSIPS:mainfrom
NormB:feat/rtp_relay-tests

Conversation

@NormB

@NormB NormB commented Jun 26, 2026

Copy link
Copy Markdown
Member

Summary

Adds a new rtp_relay test set — four sipssert scenarios that are regression tests for OpenSIPS issue #3902 ("rtp_relay: callee/WebRTC profile lost on the offer for a transcoding call").

On a transcoding SIP → WebRTC call the caller leg is plain RTP/AVP (set in the request route) and the callee/WebRTC leg (UDP/TLS/RTP/SAVPF, ICE=force) is provisioned in a branch_route via $rtp_relay(...). On affected OpenSIPS the branch-provisioned callee leg is not linked into sess->legs[CALLEE] before the offer, so rtp_relay asks rtpengine for plain RTP/AVP — the WebRTC transport-protocol and ICE are dropped and a real WebRTC UA replies 488 Not Acceptable Here.

⚠️ These assert the fixed behaviour, so they are RED on current master and turn GREEN once the #3902 fix is merged and the sipssert image is rebuilt — i.e. this is the companion test PR to the opensips fix.

Changes

  • New rtp_relay/ test set (4 scenarios, see table below).
  • run.yml — registers the set (- rtp_relay).
  • Root config.yml — pins the bridge device names (device: osbr0 / osbr1) so a host-networked capture sidecar can tcpdump -i osbr0. Inert for every other set.
  • Root defines.yml — adds the rtp_relay vars (rtpengine_ip, rtpengine_ng_port, rtpengine_image, capture_image, checker_image). Unused by the other sets.
  • rtp_relay/config.yml and rtp_relay/defines.yml are symlinks to the suite root (../config.yml / ../defines.yml), matching dialog/, b2b/, auth/, and every other set.

What the scenarios assert

# Test dir Path Checker axis
01 01.initial-offer-savpf/ initial SDP-in-INVITE offer OFFER carries UDP/TLS/RTP/SAVPF + ICE=force
02 02.indialog-reinvite-hold-unhold/ in-dialog re-INVITE (hold + unhold) ≥3 OFFERs, every one keeps the profile
03 03.update-failover-reanchor/ rtp_relay_update (MI) node-failover re-anchor re-anchor OFFER/ANSWER after the MI keeps the profile
04 04.late-negotiation/ no-SDP INVITE / delayed offer ANSWER axis keeps the profile

The assertion is made against the rtpengine ng control message (captured on the ng UDP port), not against media or a real endpoint — so no live DTLS/ICE peer is needed; the checker reads exactly what OpenSIPS asks rtpengine to do. The 488 is therefore inferred from the missing transport profile, not observed.

scripts/ng_checker.py (one copy per test dir) is dependency-free — it parses the libpcap capture and decodes the bencode ng messages with the Python standard library only (no tshark/scapy/pip), so it runs in any python3 image.

rtpengine service container

The suite has no rtpengine task yet, so this set adds one (type: generic). Rather than depend on a third-party image (there is no official sipwise/rtpengine image), it installs the official Debian rtpengine-daemon package on a stock
debian:trixie-slim base at container start, run with default values:

rtpengine --table=-1 --interface={{ rtpengine_ip }} --listen-ng={{ rtpengine_ip }}:{{ rtpengine_ng_port }} --foreground --log-stderr

--table=-1 ⇒ pure userspace (no kernel module, no NET_ADMIN, no privileged). The cold apt-install pulls a sizeable ffmpeg/codec tree (~1–3 min on a cold cache), so dependents gate on a Docker healthcheck (retries: 120, ~4 min window) via require: { healthy: rtpengine }, and the per-scenario timeout is 300s (360s for 03's MI re-anchor). If a prebuilt image is later preferred, point rtpengine_image at it and drop the apt-install from the task command.

The checker needs the OpenSIPS↔rtpengine ng packets, which flow between two other containers, so each scenario runs a host-networked capture sidecar (network: host) that tcpdump -i osbr0s the bridge device into a shared volume
the checker then reads.

Image modules — no .opensips.modules change

Every module these tests load (rtp_relay.so, rtpengine.so, mi_datagram.so, plus dialog/tm/sl/rr/maxfwd/sipmsgops/proto_udp) ships in the base opensips packagedebian/control has no rtp/datagram split-out package, and the RPM spec lists them under the main %files. The stock ghcr.io/opensips/opensips:sipssert image already contains them.

Validation

Run end-to-end through sipssert against two OpenSIPS images identical except for the rtp_relay.so (#3902-fixed vs. stock), spinning up the full stack (OpenSIPS + rtpengine + sipp UAC/UAS + host-net ng-capture + checker, plus the opensips-mi datagram task for 03):

test fixed .so pristine .so
01.initial-offer-savpf PASS FAIL
02.indialog-reinvite-hold-unhold PASS FAIL
03.update-failover-reanchor PASS FAIL
04.late-negotiation PASS FAIL

i.e. the tests catch the bug (pristine → all FAIL) and confirm the fix (all PASS) across all four paths.

Gaps / caveats

  • No real WebRTC UA — the callee is a trivial sipp UAS; the 488 is inferred from the missing transport profile, not observed (intentional — the checker is endpoint-independent).
  • Single rtpengine node for 03 — rtp_relay_update re-anchors onto the same node (fired via MI, not a real watchdog node-down); still exercises the self-generated re-INVITE / profile-carry path, which is the defect.
  • GREEN requires the fix — these are regression tests for the fixed behaviour; on master they are RED until the #3902 fix merges. Path 04 needs the link in both initial_cb branches; 01–03 also pass with the narrower REQUEST_FWDED-only link.
  • Readiness window (retries: 120, timeout 300/360s) is sized for a cold apt-install on a slow-apt host; CI with a warm mirror finishes well inside it.
  • Capture image (nicolaka/netshoot) is a pragmatic tcpdump carrier — trivially swappable via capture_image.

NormB added 2 commits June 25, 2026 15:00
Add an rtp_relay test set covering OpenSIPS issue #3902: the callee/WebRTC
profile (UDP/TLS/RTP/SAVPF + ICE=force) provisioned in a branch_route is not
linked into sess->legs[CALLEE] before the offer, so rtp_relay asks rtpengine
for plain RTP/AVP and a WebRTC UA replies 488.

Four scenarios, each asserting via the rtpengine ng control stream (no live
WebRTC UA needed) that the profile survives:

  01.initial-offer-savpf            initial SDP-in-INVITE offer
  02.indialog-reinvite-hold-unhold  in-dialog hold/unhold re-INVITE (issue title)
  03.update-failover-reanchor       rtp_relay_update node-failover re-anchor
  04.late-negotiation               no-SDP INVITE / delayed offer

The checker (scripts/ng_checker.py) is dependency-free: it parses the libpcap
capture and decodes the bencode ng messages with the standard library only.
A host-networked capture sidecar sniffs the bridge so it sees the inter-container
OpenSIPS<->rtpengine ng traffic.

rtpengine runs from the official Debian rtpengine-daemon package (sipwise
upstream, in Debian main) installed on a pinned debian:trixie-slim base at
container start -- no third-party rtpengine image. It runs userspace-only
(--table=-1, no kernel dkms, no NET_ADMIN). A Docker healthcheck on the ng
socket lets dependents gate on `healthy: rtpengine` so the apt-install delay
cannot race the proxy startup ping. Registered in run.yml.

All required OpenSIPS modules (rtp_relay, rtpengine, mi_datagram, dialog,
proto_udp) ship in the base opensips package, so no .opensips.modules change
is needed. These are regression tests for the fixed behavior: RED on current
master, GREEN once rtp_relay_reinvite_tag_v4 is merged and the image rebuilt.
- rtp_relay/config.yml and defines.yml are now symlinks to the suite-root
  files, matching every other test set. The set-specific extras moved up
  into the root: bridge device pins (config.yml) and the rtpengine/capture/
  checker vars (defines.yml).
- rtpengine image: drop the pinned digest, use plain debian:trixie-slim and
  run with default flags (no --log-level override).
- widen the rtpengine readiness window (healthcheck retries 30->120, scenario
  timeout 90->300, and 360 for 03's MI re-anchor step) so the cold apt-install
  of rtpengine-daemon reliably completes on slower-apt hosts.

Verified locally: rtp_relay 4/4 PASS against the fixed OpenSIPS image, and
RED via the ng checker on the affected build (#3902).
@razvancrainea
razvancrainea merged commit f276148 into OpenSIPS:main Jul 31, 2026
13 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants