SIP Train Blog - SBCs and carrier engineering
SBC Topology Hiding: What Actually Gets Rewritten, and Why
Ask ten VoIP engineers what an SBC does and eight will say "topology hiding" somewhere in the answer. Ask the same ten engineers exactly which fields get rewritten, in which direction, and what happens to a dialog when one of them is rewritten wrong, and the answers thin out fast. Topology hiding lives in the marketing slide deck of every SBC vendor and in the muscle memory of every senior carrier engineer, but it rarely gets written down precisely.
This post fixes that. We'll walk every header and SDP field that an SBC operating as a back-to-back user agent (B2BUA) typically rewrites between the access and core legs, why each one is rewritten, and the three most common ways topology hiding goes wrong in production.
Why topology hiding exists at all
The simple framing, "we don't want our internal IPs to leak", is correct but undersells the engineering. There are four operational reasons an SBC rewrites SIP signalling at the boundary, and only one of them is about secrecy.
Security comes first: hiding the IPs and software versions of internal SIP elements
removes a free reconnaissance step for an attacker. A scanner that can read
User-Agent: Asterisk PBX 18.6.0 and a private Contact URI on
10.20.30.40 has been handed your topology and a known CVE list in one
round trip.
NAT and addressing come second: the access side faces public internet endpoints while
the core talks to a private softswitch fabric, so without rewriting Via, Contact, and
SDP c=/m= lines no response or media packet would route back
correctly. Transport translation is third: an SBC routinely terminates UDP on one side
and originates TCP or TLS on the other, terminates SRTP and originates plain RTP, and
translates between IPv4 and IPv6, and each translation forces a SIP-layer rewrite
because the original transport, address, or media keying is no longer valid downstream.
Policy is fourth: codec lists get filtered, P-Asserted-Identity gets inserted or
stripped, Diversion headers get normalised, and SDP gets reshaped to match what the
downstream peer accepts.
A pure proxy cannot do all four reliably. That is why every production SBC is, in implementation terms, a B2BUA: it terminates the inbound INVITE, runs policy, and originates a fresh outbound INVITE with new transactions and (usually) new dialogs on each side.
What gets rewritten, line by line
Think of a B2BUA SBC as two independent SIP user agents glued together by a call-control core that maps one leg's events onto the other's. From that vantage point, almost every SIP and SDP field gets rewritten between the legs. Here is the working set.
Via. The SBC strips every inbound Via and inserts a single Via of its
own on the outbound INVITE, pointing to its core-facing signalling interface. The
branch parameter is freshly generated per RFC 3261 §8.1.1.7, beginning with the magic
cookie z9hG4bK. The outbound transaction is not the inbound transaction;
reusing the inbound branch would break transaction matching on any downstream stateful
element.
Contact. Rewritten to the SBC's own address on the outbound side.
The original UAC's Contact never crosses the SBC. This is the field that breaks more
SBC deployments than any other, because the 2xx ACK and every in-dialog request after
answer are routed using Contact modified by the dialog's Route set. Get it wrong
(wrong IP, wrong port, missing transport parameter, missing ;ob for
outbound flow tokens) and the call sets up cleanly then dies with a BYE from the UAS
at the 32-second mark when no ACK arrives.
Record-Route. The SBC inserts itself into the Route set so all in-dialog requests are forced back through it. RFC 3261 §16.6 defines the rules, but the operational subtlety is that an SBC straddling two transports or two address families needs to insert two Record-Route headers, one for each leg's signalling address: the "double Record-Route". A single entry pointing at the access-side address will route core-originated re-INVITEs and BYEs to an interface that cannot reach the originating peer.
Call-ID, From-tag, To-tag. A full B2BUA generates fresh values on the outbound leg. These three identify the dialog, and a B2BUA runs two independent dialogs bridged in the call-control layer. Lighter SBCs preserve Call-ID across the bridge, convenient for end-to-end trace correlation, but it leaks the UAC's Call-ID generation pattern (often a hash of MAC and timestamp) and partially defeats topology hiding.
CSeq, Max-Forwards. CSeq starts independently on the outbound leg (typically at 1 or random). Max-Forwards is reset to the SBC's configured value (typically 70), not decremented from the inbound: the SBC originates the outbound INVITE, so the count starts again.
User-Agent and Server. Stripped or replaced with a generic banner. The cheapest topology-hiding win, and the one most often left disabled by default in vendor configurations. Check yours.
Allow, Supported, Require. Filtered to advertise only the methods
and option tags the SBC will actually relay. Common pitfall: stripping
Supported: 100rel on the way through breaks PRACK negotiation
end-to-end, even if both endpoints would have supported it. If the SBC does not
itself implement PRACK relay, dropping the option tag is correct; if it does,
dropping it silently breaks early-media reliability.
P-Asserted-Identity, Diversion, History-Info. Policy-driven.
Access-to-core, these are usually stripped (the access leg is untrusted) and the SBC
inserts its own asserted identity from registration credentials. Core-to-access, they
are usually stripped entirely for privacy unless an explicit Privacy:
header allows them through.
SDP: o=, c=, m=,
a=rtcp. The originating IP in o=, the connection
IP in c=, and the port in each m= line are all rewritten to
point at the SBC's media interface and the dynamically allocated RTP/RTCP port pair
on the corresponding side; a=rtcp is rewritten in step. The codec list
(a=rtpmap, a=fmtp) may be filtered by policy. SRTP keys
(a=crypto) are decrypted on one side and re-encrypted on the other,
which means the SBC sees the plaintext media on every call, an architectural fact
with compliance implications worth knowing.
What to look for in a trace
Capture on both sides of the SBC simultaneously. The legitimate fingerprint of correct
topology hiding is straightforward: every field above differs between the two captures,
Call-IDs and branch parameters do not match, Contact URIs point at the SBC's two
interfaces, and the SDP c=/m= lines on each side point at
the SBC's two media interfaces with no overlap.
The three most common pathologies all show up cleanly in a paired trace. The first is the missing-Record-Route case: setup completes, but when either side sends a BYE or re-INVITE it goes nowhere. The in-dialog request from the core leaves the SBC's access interface destined for the access UA's original Contact, bypassing the SBC's anchored media path. The SBC inserted only one Record-Route header instead of two.
The second is the Contact-rewrite case described above: 200 OK retransmits five times on the access leg, then a BYE arrives from the UAS at 32 seconds. The UAC's ACK is going to the original Contact rather than the SBC's, either because Contact was not rewritten in the 200 OK or because Record-Route was missing so the ACK bypassed the SBC entirely.
The third is the silent-PRACK case: the access UA advertised
Supported: 100rel, the SBC stripped it, and the core UAS sent unreliable
183s with SDP. Early media is lost intermittently, the call answers cleanly, and the
customer complaint is "the announcement didn't play". No PRACK is ever sent in either
direction, and the 183 with SDP retransmits, exactly the fingerprint described in
our post on the INVITE lifecycle.
Closing
Topology hiding is not a feature you toggle. It is the cumulative effect of a dozen header-rewrite rules and an SDP rewrite, each of which has to be configured to match the topology, transport, and trust boundary of the deployment. Get them right and the SBC is invisible to attackers and reliable to operators; get any one of them wrong and the failure mode is "the call sets up and then mysteriously dies", the kind of bug that takes hours of paired-capture work to corner.
Go deeper on SBCs
SIPT-202: Session Border Controllers covers the full topology-hiding ruleset, double-Record-Route patterns, media anchoring decisions, and the operational playbook for paired-capture diagnosis. In production for Q3 2026 launch.
See the course catalog