Skip to main content
4 min read

In SIP, what's negotiated isn't what's supported

Three trunks that refused to place a call between two vendors. In all three, a peer signaled one thing and did another.

sipsbcinteropaudiocodestelecom

A SIP trunk that works in the lab can break in production as soon as it joins two devices from different vendors. Every time this happened to me, the cause was the same: a peer that signals a capability, a role, or a format, and then does something else. Both implementations can be defensible and still diverge.

The three cases below are real, and in all three the trunk refused to place a call. The fix was on the SBC every time, never on the misbehaving device across the trunk.

What's negotiatedWhat the peer actually doesResult
UPDATE sentUPDATE out of sequence, offer not yet resolvedIgnored, retransmitted in a loop, no calls
refresher=uacThe UAC never refreshesCall dropped when the timer expires
Early Offer expectedThe SBC sends Delayed OfferCall rejected

Case 1: an UPDATE no one was allowed to send

Across the trunk I had a Cisco CUBE, and on my side an AudioCodes SBC. The symptom was easy enough to observe, since no call went through at all.

The trace gave the answer: the CUBE was sending an UPDATE while the SDP offer from the initial INVITE had not yet received its answer. RFC 3311 forbids it, because you do not start a new offer/answer negotiation while the first one is unresolved. AudioCodes was doing exactly what the RFC asks, and ignored the UPDATE without replying.

The CUBE, which therefore never got an answer, retransmitted in a loop. The behavior repeated on every call and the trunk stayed down.

The fix was to disable UPDATE on the SBC: the initial negotiation then completes normally and the call goes through.

The compliant peer in this case was AudioCodes, which followed the RFC to the letter, and that did not keep the trunk up. What brought it down was not a fault in the protocol sense, but the gap between what the CUBE sent and what AudioCodes tolerated.

Case 2: a refresher that never refreshes

My SBC was facing an Alcatel-Lucent OXE, the OmniPCX Enterprise, and calls kept dropping systematically, always at the same point.

RFC 4028 session timers exist to clear dead calls. Both sides negotiate a Session-Expires and name a refresher, the one that will periodically send a re-INVITE or an UPDATE to signal the call is alive.

Session-Expires: 1800;refresher=uac

The designated refresher was the UAC, and that UAC never refreshed. When the timer expired no one had shown a sign of life, so the call was declared dead and cut while media was flowing both ways.

The fix was to move the refresh to the UAS side, so the role went to the party that actually performs it, and calls stopped dropping.

Negotiating a role did not guarantee it would be honored: refresher=uac was a promise the UAC did not keep, and the SBC reassigned it to a peer that did.

Case 3: Early Offer, or nothing

An AudioCodes SBC was sending Delayed Offer while the IPBX across the trunk required Early Offer, and the call was rejected on the spot.

Two ways to present the SDP:

Early Offer   : INVITE + SDP
Delayed Offer : INVITE without SDP, then SDP in the 200 OK

Both forms are valid, but not every device handles both. This IPBX wanted the offer in the INVITE and refused an INVITE with no SDP.

The fix was to reconfigure the SBC to send Early Offer, using the Delayed to Early Offer conversion on AudioCodes. The IPBX then got what it expected and the call set up.

Valid per the RFC did not mean accepted by the peer, since the Delayed Offer was legitimate and rejected anyway. The SBC adapted the format to what the other side could read.

What these three cases share

In all three, one side signaled one thing and did another: an UPDATE out of sequence, a refresher named but idle, an Early Offer expected against a Delayed Offer sent. Two compliant implementations can still refuse to talk.

I take two reflexes out of this.

The first is that I don't fix the vendor across the trunk. I patched neither the CUBE, nor the OXE, nor the IPBX, and all three fixes were configurations on the SBC, which is the layer where you absorb the peer's reality.

The second is that I trust the trace. The vendor doc says what the device is supposed to do, the SIP capture says what it does, and the two don't always agree. Facing a trunk that refuses to work, I capture rather than read a datasheet, I compare what's negotiated to what happens, and I adapt on the SBC.

A trunk breaking between two vendors, an SBC migration, a go-live stuck on signaling? Get in touch.

Field note by qaryon

Nicolas Marxer

Enterprise communications solution architect focused on operator, integrator, and B2B deployments.

Need a field view on your communications architecture?

Audit, scoping, or deployment: I work directly on UCaaS, CCaaS, IPBX, SBC, automation, and applied AI.

Discuss a telecom project