PBX · OpenSIPS 3.x

Use DIDHub as an OpenSIPS dispatcher upstream

OpenSIPS 3.x ships with the same dispatcher primitive as Kamailio but with different syntax (DB-driven by default, OPTIONS keepalive built in). This tutorial wires up DIDHub as a load-balanced upstream with primary + secondary PoPs and digest auth.

/etc/opensips/opensips.cfg (relevant sections)

loadmodule "dispatcher.so"
loadmodule "db_text.so"     # or db_mysql / db_postgres
loadmodule "uac.so"
loadmodule "uac_auth.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "sl.so"

modparam("dispatcher", "db_url", "text:///etc/opensips/dbtext")
modparam("dispatcher", "ds_ping_interval", 30)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3)
modparam("dispatcher", "ds_probing_mode", 1)   # probe all gateways

# uac_auth challenges — needed when DIDHub runs in digest mode (not IP ACL)
modparam("uac_auth", "credential", "trunk_xxxxx:sip.didhub.io:<PASSWORD>")

/etc/opensips/dbtext/dispatcher

# id(int)setid(int)destination(string)flags(int)priority(int)attrs(string)description(string)
1:1:sip:sip.didhub.io;transport=udp:0:100:weight=70:didhub-primary
2:1:sip:sip-bk.didhub.io;transport=udp:0:50:weight=30:didhub-secondary
3:2:sip:sip-eu.didhub.io;transport=udp:0:100:weight=100:didhub-eu-fallback

Outbound route

route[OUTBOUND_PSTN] {
    # Pick best alive destination from set 1, weight-based
    if (!ds_select_dst("1", "4")) {       # 4 = weight + alive flags
        send_reply(503, "All upstreams down");
        exit;
    }

    # Strip leading + and re-add for E.164 normalization
    if ($rU =~ "^\+") {
        $rU = $(rU{s.substr,1,0});
    }
    $ru = "sip:+" + $rU + "@" + $du;

    t_on_failure("DISPATCH_FAILOVER");
    t_relay();
}

failure_route[DISPATCH_FAILOVER] {
    # Retry next upstream on 5xx or timeout
    if (t_was_cancelled()) exit;
    if (t_check_status("(408|5[0-9][0-9])")) {
        if (ds_next_dst()) {
            t_on_failure("DISPATCH_FAILOVER");
            t_relay();
        }
    }
    if (t_check_status("401|407")) {
        # Digest challenge from DIDHub
        uac_auth();
        t_relay();
    }
}

Trigger the route from your main script

route {
    if (!mf_process_maxfwd_header("10")) {
        send_reply(483, "Too Many Hops");
        exit;
    }
    if (has_totag()) {
        loose_route();
        t_relay();
        exit;
    }
    record_route();

    # Route +E.164 to PSTN upstream
    if ($rU =~ "^\+?[0-9]{6,15}$") {
        route(OUTBOUND_PSTN);
        exit;
    }

    send_reply(404, "Not Found");
}

Verify dispatcher health

opensips-cli -x mi ds_list

# Expected output:
SET:: 1
  DEST:: sip:sip.didhub.io;transport=udp
    PRIORITY:: 100
    FLAGS:: AP   # A=Active P=Probing
  DEST:: sip:sip-bk.didhub.io;transport=udp
    PRIORITY:: 50
    FLAGS:: AP

Real failover test

  1. Place an outbound call. Confirm it routes via sip.didhub.io (the higher-weighted destination).
  2. Block sip.didhub.io at your firewall.
  3. Within ~90 seconds (3 probes × 30s) OpenSIPS marks it inactive (FLAGS:: P).
  4. Subsequent calls automatically use sip-bk.didhub.io.
  5. Unblock; OpenSIPS re-marks active on the next successful OPTIONS response.

Tips

Compare to Kamailio: see the Kamailio dispatcher tutorial. The two configs are almost identical at the routing-logic level, but OpenSIPS 3.x has cleaner built-in probing semantics and DB-backed dispatcher tables out of the box.

Ready to get a number?

Pick a DID in 130+ countries from $1.99/month. Activates instantly on most numbers.