Building an SMSC¶
A store-and-forward SMSC does two jobs over SS7: it terminates
mobile-originated SMS (MO-ForwardSM) and it originates mobile-terminated
delivery (SRI-SM to the HLR, then MT-ForwardSM to the serving MSC). This recipe
is
examples/smsc.py,
walked section by section.
MSC ──MO-ForwardSM──▶ ┌──────────────────┐
│ this SMSC │ ──SRI-SM──▶ HLR
MSC ◀─MT-ForwardSM─── │ (siphon-sigtran) │ ◀routing──
└──────────────────┘ ──MT-ForwardSM──▶ serving MSC
siphon-sigtran owns the MAP and TCAP layers: the dialogue, the transaction ids,
moreMessagesToSend, and the wire encoding. It deliberately does not touch
the SMS content. The sm_rp_ui field of a ForwardSM is an opaque octet string,
the SMS transfer-layer PDU. To read or build that (SMS-SUBMIT / SMS-DELIVER per
3GPP TS 23.040, GSM 7-bit packing per TS 23.038, the User-Data-Header for
concatenation, TON/NPI addresses), a script uses the sibling
tpdu crate, which also ships as a Python
wheel (pip install tpdu).
The layer split
MAP transaction, dialogue, moreMessagesToSend, addressing at SCCP: this
crate. SMS-SUBMIT / SMS-DELIVER, GSM-7 packing, UDH concatenation: the
tpdu crate. Keeping them apart is deliberate; siphon-sigtran handles
the signalling, tpdu handles the message bytes.
We own SSN 8¶
The config declares the SMSC's subsystem so inbound MO SMS terminates locally:
node:
point_code: 1000
variant: itu
associations:
- { id: msc-1, adaptation: m3ua, role: server, addrs: [10.1.0.12], port: 2905 }
application_servers:
- { name: msc, traffic_mode: override, routing_context: 101, asps: [msc-1] }
mtp3_routes:
- { dpc: 2002, as: msc, priority: 1 }
sccp:
local_ssns: [8] # we own SSN 8; MO-ForwardSM to it terminates here
gtt:
- { match: {gt_prefix: "1555"}, to: {dpc: 2000, ssn: 6} } # SRI-SM toward the HLR
Terminate mobile-originated SMS¶
Register a handler for MO-ForwardSM. The engine hands you the
Dialogue and the decoded
IncomingOp; arg.sm_rp_oa, arg.sm_rp_da and
arg.sm_rp_ui are the raw originating address, destination address, and TPDU
bytes.
import tpdu
from siphon import ss7, gsm_map
OUR_GT = "15550100" # our E.164 address (+1 555 0100)
@gsm_map.on_operation("mo-forward-sm")
async def on_mo(dlg, arg):
# arg.sm_rp_ui is the opaque RP-DATA carrying an SMS-SUBMIT. tpdu decodes it.
rp = tpdu.parse_rp_data(arg.sm_rp_ui)
submit = rp.sms_submit
dest = submit.tp_destination_address.to_e164() # the recipient MSISDN
text = submit.text() # decoded for GSM-7 / UCS-2 DCS
await spool(sender=rp.rp_originator_address, dest=dest, text=text)
dlg.reply(gsm_map.mo_forward_sm_res()) # returnResultLast, in a closing End
dlg.end()
The moment you have the decoded TPDU, the message is yours: spool it, route it,
hand it to another transport. The MAP side is two lines, reply then end. A
TPDU that arrives without the RP wrapper (an SMPP submit_sm, or a bare
sm-RP-UI) parses just as well with tpdu.parse_sms_submit(...) /
tpdu.destination_from_tpdu(...).
Mobile-terminated delivery¶
Terminating mobile-originated SMS (above) runs on the wire today. Delivering a
mobile-terminated message is the other half of a store-and-forward SMSC, and
it originates dialogues: an SRI-SM to the HLR to learn the subscriber's IMSI
and serving MSC, then one MT-ForwardSM dialogue to that MSC held open across the
segments of a concatenated message. The node opens a dialogue it initiates and
awaits the peer's response over SCTP with gsm_map.begin(...) for a single
request/response, or node.originate(...) with an on_reply(dlg, peer) callback
for a multi-leg delivery (segment 1 with moreMessagesToSend, the ack, segment
2, the closing End). A terminate-only SMSC front end (decode, screen, spool, ack
the MO leg) works equally well.
The SMS-DELIVER TPDUs, including the User-Data-Header that ties the segments
together, are built with tpdu (TS 23.040 / TS 23.038 / TS 24.011);
moreMessagesToSend and the dialogue lifetime are siphon-sigtran's job.
siphon-sigtran never inspects the SMS bytes; it carries them and sequences the
dialogue.
From here to production¶
- Spool and retry. Back the store between MO and MT with a durable queue;
retry MT delivery on failure or on an
alertthat the subscriber is reachable again. - DLR handling. A delivery report comes back as its own MAP operation; parse
the status with
tpduand correlate it to the original submission. - Screening. If this SMSC also fronts transit SMS, screen SRI-SM origins at the content layer (GSMA FS.11); see Building an STP.
- Deploy it. Deployment and Kubernetes & scaling.