Skip to content

Apply the egress sequence offset in userspace passthrough - #3

Open
nickakt wants to merge 1 commit into
masterfrom
fix/passthrough-sequence-offset
Open

nickakt wants to merge 1 commit into
masterfrom
fix/passthrough-sequence-offset

Conversation

@nickakt

@nickakt nickakt commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

The kernel module adds the egress sequence offset to every packet it forwards;
userspace passthrough did not, unless a fixed egress SSRC was set. Decoding for
an injection mix lowers the offset by one per packet. After inject stop the
first packets left userspace with their original numbers and the kernel then
applied the offset, so the receiver saw the sequence jump forward and back.
SRTP derives the packet index from the sequence number, and a receiver discards
packets behind its replay window: a WebRTC client, whose window is 1024 packets,
went silent for 20 seconds after a 40-second injection.

Userspace passthrough now applies the offset, on a copy of the whole packet
since other sinks share it, for handlers the kernel may forward. A stream
forwarded under the sender's own SRTP key and SSRC keeps the sender's numbering
instead, whether or not it is re-encrypted on the way: a renumbered packet would
repeat an index the sender has used, so the offset is dropped for that stream,
in userspace and in the kernel alike, and the receiver sees a gap. A fixed
egress SSRC, echo, transcoding and DTMF injection number the stream themselves
and keep the offset.

Verification:

  • t/auto-daemon-tests-inject.pl gains nine sequence scenarios (RTP, SRTP, the
    sender's key with and without recrypt, a 65535 crossing, repeated
    injections, a fixed egress SSRC across a source change); the first eight fail
    on master and pass with the change.
  • make check with RTPENGINE_EXTENDED_TESTS=1: the same results as master.
  • Through the kernel module (6.8): fixed egress SSRC with a source change, and
    DTMF injection, under SDES keys shared end to end, three runs each against
    master and this change: the sequence stays continuous and the kernel keeps
    forwarding while the daemon is stopped with SIGSTOP.

@nickakt
nickakt force-pushed the fix/passthrough-sequence-offset branch 5 times, most recently from 89c3677 to 2a2f51e Compare October 13, 2026 15:21
@nickakt

nickakt commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner Author

Kernel-path check of this revision, with the module built from this tree on kernel 6.8, SDES keys shared end to end, three runs per build and scenario (all three identical). For 25 packets of the observed phase the daemon is stopped with SIGSTOP; 25/25 arriving means the kernel forwarded them. Re-kernelization after the event is in the daemon log, no No support for kernel packet forwarding lines, no SRTP authentication failures.

build fixed egress SSRC + recrypt, source changes SSRC DTMF injection (telephone-event)
master d590a62 10009 → 10010 → 10011 … contiguous contiguous
89c3677 (previous revision) 10010 from userspace, then 501 from the kernel: 149 packets behind a 1024-packet replay window one backward step 10017 → 10016, 2 repeated indexes
3c7a736 (this revision) contiguous, as master contiguous, as master

Userspace: the nine inject-suite scenarios pass (0 authentication failures of 812), three more times under AddressSanitizer without a report; make check with RTPENGINE_EXTENDED_TESTS=1 matches master.

3c7a736 differs from 0538522 in the ninth test only: the fixed egress scenario changes the source without an injection. With an injection, the first packet of the player and the last passed-through packet can leave with the same sequence number; master does the same (2 of 3 runs under AddressSanitizer), so that is not this change.

@nickakt
nickakt force-pushed the fix/passthrough-sequence-offset branch 2 times, most recently from 0538522 to 3c7a736 Compare October 13, 2026 17:48
@nickakt

nickakt commented Oct 13, 2026

Copy link
Copy Markdown
Owner Author

Upstream: sipwise#2209

@nickakt
nickakt force-pushed the fix/passthrough-sequence-offset branch from 3c7a736 to 88c7ab7 Compare October 16, 2026 04:59
The kernel module adds the egress sequence offset to every packet it forwards;
userspace passthrough did not, unless a fixed egress SSRC was set. Decoding for
an injection mix lowers the offset by one per packet. After `inject stop` the
first packets left userspace with their original numbers and the kernel then
applied the offset, so the receiver saw the sequence jump forward and back.
SRTP derives the packet index from the sequence number, and a receiver discards
packets behind its replay window: a WebRTC client, whose window is 1024 packets,
went silent for 20 seconds after a 40-second injection.

Userspace passthrough now applies the offset in place, as the kernel module
does. A stream forwarded under the sender's own SRTP master key and SSRC keeps
the sender's numbering instead, whether or not it is re-encrypted on the way: a
renumbered packet would repeat, under that key, an index the sender has already
used for another packet, so the offset is dropped for that stream, in userspace
and in the kernel alike, and the receiver sees a gap the length of the
injection. Only the master key and salt the stream arrives and leaves under
decide it: an MKI, a session parameter such as unencrypted SRTCP, or a re-invite
that replaces the key of the other direction leave that keystream as it is.
Echo, transcoding and a fixed egress SSRC send under an SSRC of rtpengine's own
and keep the offset, as does a stream that leaves under another key. DTMF
injection keeps it as well, since it inserts packets into the sender's stream;
with a key passed through unchanged it still repeats indices, as before. The
same happens when a sender answers a re-invite with a new key during an
injection: rtpengine goes on sending under the former key and, after the
injection, numbers the sender's later packets back into indices it used under
that key.

A sink that substitutes the SSRC rewrites the header the sender authenticated,
so it re-encrypts the packet. A fixed egress SSRC on a stream forwarded under
the sender's key used to reach the receiver with the sender's authentication
tag, and the receiver dropped every packet.

The handlers rewrite the packet in place while every sink of the stream is
handed the same received buffer, so a fixed egress SSRC, the offset and an
injected DTMF event used to reach the next sink too, and after an event the
next sink read a header on the handler's stack. The sink loop now starts every
sink from the packet as received and gives each one its own copy unless it is
the last, as it already did for RTCP.
@nickakt
nickakt force-pushed the fix/passthrough-sequence-offset branch from 88c7ab7 to 380e3ec Compare October 16, 2026 14:07
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.

1 participant