RTP source port change after RE-INVITE followed by 488

62 views
Skip to first unread message

Matthew H

unread,
Aug 4, 2026, 5:48:07 AMAug 4
to Sipwise rtpengine
Hi,

I'm using rtpengine with kamaillio. The rtpengine logic is simply:

KSR.rtpengine.rtpengine_manage("SIP-source-address replace-origin RTP allow-asymmetric-codecs")

We've observed that if the caller RE-INVITEs to T.38 (with a connection different port) but the called side rejects it with a 488, rtpengine then sends RTP to the called party from a new source port, which is dropped by firewalls / NAT as it's now asymmetric. 

I suspect this is a configuration error on my side, perhaps I need to delete the rejected session? Any advice is appreciated.

Thanks
Matthew


Richard Fuchs

unread,
Aug 4, 2026, 7:27:28 AMAug 4
to rtpe...@googlegroups.com
On 04/08/2026 05.48, Matthew H wrote:
> KSR.rtpengine.rtpengine_manage("SIP-source-address replace-origin RTP
> allow-asymmetric-codecs")
>
> We've observed that if the caller RE-INVITEs to T.38 (with a
> connection different port) but the called side rejects it with a 488,
> rtpengine then sends RTP to the called party from a new source port,
> which is dropped by firewalls / NAT as it's now asymmetric.
>
> I suspect this is a configuration error on my side, perhaps I need to
> delete the rejected session? Any advice is appreciated.

This is not something that is supported at the moment, unfortunately.
Running an "offer" through rtpengine (which `rtpengine_manage` does)
implies that the offer is or will be accepted. Doing this to an already
established session overwrites the previous parameters, and there's no
way to revert in case of a rejected offer.

The workaround is to intercept this in your Kamailio script and then
replay the previous offer/answer to rtpengine, to reset the session to
its previous state.

Alternatively you can play tricks with the from/to tags, to pretend to
rtpengine that this is a brand new session, so that the existing session
is left unmodified in case of a rejection.

Cheers

Daniel Donoghue

unread,
Aug 18, 2026, 5:44:45 AMAug 18
to Sipwise rtpengine
Hi Richard,

This is very timely for me. I've actually been working on this very issue in my own stack, where I actually took the first of the 2 suggestions (intercepting the failure and replaying the previous answer/offer). I've also made a modification to kamailio's rtpengine module that I am planning to submit, so that rtpengine_manage() can work for in-dialog handling, too.

However, I did find one class of renegotiation where that doesnt seem to work and I dont think it can be fixed client-side: if the failed offer renegotiated ICE, replaying the previously accepted offer isnt just incomplete -  it can make matters worse since ice_is_restart() compares the incoming credentials against the stored remote ones which the rejected offer has already replaced. It might also be possible (though I've never observed it) that it could also break SDES in a similar way if the failed renegotiation changed the suite selection, transport or media type.

I also looked at the tag manipulation and, whilst it does protect the established session, it means the media path moves on every successful renegotiation and could double the port consumption for the duration of the transaction, splits recording, statistics and call-id to node mapping across 2 sessions.

I would like to propose adding a new "rollback" message to the NG protocol. this would include the call-id, from-tag, to-tag and, optionally, the via-branch and a generation; it would restore the media state to the last completed offer/answer exchange without deleting the call. This would be optional, per-call, via flag on the offer so it doesnt add an additional cost for calls that dont use it. An offer would snapshot before applying and a successful answer commits and then discards the snapshot. This would be a no-op if there's nothing outstanding so it's fail-safe without a client needing to track anything. This has the appeal, for me, that rtpengine can restore the things a replay can't, such as ICE agent and DTLS associations and not just what the SDP happens to describe and nothing sensitive, such as crypto keys, leave the daemon to make it work.

I have been working in a branch, with a view to creating a PR, if that's something you'd be interested in?

Many thanks,
--
Daniel

Richard Fuchs

unread,
Aug 18, 2026, 9:32:41 AMAug 18
to rtpe...@googlegroups.com
On 18/08/2026 05.44, Daniel Donoghue wrote:
> However, I did find one class of renegotiation where that doesnt seem
> to work and I dont think it can be fixed client-side: if the failed
> offer renegotiated ICE, replaying the previously accepted offer isnt
> just incomplete -  it can make matters worse since ice_is_restart()
> compares the incoming credentials against the stored remote ones which
> the rejected offer has already replaced. It might also be possible
> (though I've never observed it) that it could also break SDES in a
> similar way if the failed renegotiation changed the suite selection,
> transport or media type.

That is very true. SDES I think should be fine (except for some
intermittent loss of media), but DTLS would probably be in the same boat
as ICE.

ICE might be able to recover, as a full renegotiation should(?) be
possible at any time, but DTLS could get permanently broken.

There are some conceivable workarounds, e.g. instead of simply resetting
the ICE/DTLS/SDES states, keep multiple working states alive at the same
time, and attempt to guess which one is the one currently considered the
active one by the peer, but this would be far from optimal, and for DTLS
especially would be very difficult to even do.

> I would like to propose adding a new "rollback" message to the NG
> protocol. this would include the call-id, from-tag, to-tag and,
> optionally, the via-branch and a generation; it would restore the
> media state to the last completed offer/answer exchange without
> deleting the call. This would be optional, per-call, via flag on the
> offer so it doesnt add an additional cost for calls that dont use it.
> An offer would snapshot before applying and a successful answer
> commits and then discards the snapshot. This would be a no-op if
> there's nothing outstanding so it's fail-safe without a client needing
> to track anything. This has the appeal, for me, that rtpengine can
> restore the things a replay can't, such as ICE agent and DTLS
> associations and not just what the SDP happens to describe and nothing
> sensitive, such as crypto keys, leave the daemon to make it work.
>
> I have been working in a branch, with a view to creating a PR, if
> that's something you'd be interested in?

That sounds like a very good addition to the project, and I'm sure lots
of users would appreciate this feature.

Looking forward to the PR!

Cheers

Reply all
Reply to author
Forward
0 new messages