Handling IP address change
This article describes some issues and their corresponding solutions related to access point disconnection, reconnection, IP address change, and how to handle these events in your PJSIP applications, specifically
for PJSIP version 2.7 or later. This wiki will focus on the new API pjsua_handle_ip_change().
Problem description
IP address change and/or access point disconnection and reconnection are scenarios that need to be handled in mobile applications. Few issues or scenarios related to this for example are:
user moves outside the range of a Wi-Fi access point (AP) and lost the connection
user moves outside the range of one AP and reconnect to another
the handset may get new IP address if user reconnects to different AP
API: pjsua_handle_ip_change()
Since 2.7, pjsua API introduce a new API (pjsua_handle_ip_change()) to handle IP address change. This way, application only needs to detect for IP address change event, and let the library
handle the IP address change based on the configuration.
pjsua_handle_ip_change() flow
When invoked, the stack will:
Restart the SIP transport listener
This will restart TCP/TLS listener no matter whether they are enabled or not when the transport were created. If you don’t have any use of the listener, you can disable this. However, if you do need this, then on some platform (e.g: on iOS), some delay is needed when restarting the listener.
ref:
pjsua_ip_change_param::restart_listenerandpjsua_ip_change_param::restart_lis_delay.Shutdown the SIP transport used by account registration
On some platform (e.g: iOS), it is necessary to shutdown the transport used by registration, since presumably the socket is already in a bad state.
ref:
ip_change_cfg.shutdown_tpinpjsua_acc_config.Update contact URI by sending re-Registration
The server needs to be updated of the new Contact URI when the IP address changed. Set it to PJ_TRUE to allow the stack update contact URI to the server.
ref:
pjsua_acc_config::allow_contact_rewriteandpjsua_acc_config::contact_rewrite_method.Hangup active calls or continue the call by sending re-INVITE
You can either hangup or maintain the ongoing/active calls. If you intend to maintain the active calls, updating dialog’s contact URI is required. This can be done by specifying
PJSUA_CALL_UPDATE_CONTACTto the reinvite flags. Note that, hanging up calls might be inevitable on some cases, please see Network change to the same IP address type. (IPv4 to IPv4) or (IPv6 to IPv6) section below.ref:
pjsua_ip_change_acc_cfg::hangup_callsandpjsua_ip_change_acc_cfg::reinvite_flagsinpjsua_acc_config::ip_change_cfg
Notes and limitations
To monitor the progress of IP change handling, application can use pjsua_callback::on_ip_change_progress callback. The callback will notify application of these events:
SIP transport listener restart,
SIP transport shutdown,
contact update (re-registration process), and
calls hangup or retry (re-INVITE).
Related to maintaining a call during IP change, there are some scenarios that are currently not implemented by IP change mechanism, so application needs to handle manually: If IP change occurs during SDP negotiation (and it is not completed yet, so there cannot be another SDP offer), updating such call needs to be done in two steps:
Update Contact header, so remote endpoint can send its SDP answer to our new contact address, i.e: use UPDATE without SDP offer (
PJSUA_CALL_NO_SDP_OFFERflag). Note that, not every endpoint supports UPDATE. Contact is used by remote to resolve target before sending new requests. If proxy is used, then you can probably skip this.Update local media transport after SDP answer is received, by sending UPDATE/re-INVITE with
PJSUA_CALL_REINIT_MEDIAflag.
- If IP change occurs before a call is confirmed:
For outgoing call, the call will be disconnected and reported to application via
pjsua_callback::on_call_state.For incoming call however, it will continue to be active. Application can manually hangup the call if desired.
IP change scenarios
Network change to the same IP address type. (IPv4 to IPv4) or (IPv6 to IPv6)
Update contact process (re-Registration) and call handling (hang-up or continue the call) should be handled by the API (pjsua_handle_ip_change()) without any special treatment from the application.
Network change to a different IP address type. (IPv4 to IPv6) or (IPv6 to IPv4)
IPv6 needs specific account configuration — see IPv6 modes and defaults in the IPv6 and NAT64 guide for the mode reference. On the case of IP address type change, additional steps are required from the application:
Once application detects a network with IP address type change, a new transport might need to be created.
Once the transport is available, modify the account’s IP version preferences if necessary by calling
pjsua_acc_modify(), and then callpjsua_handle_ip_change().
PJSUA-LIB:
static void ip_change_to_ip6()
{
...
// Create new IPv6 transport, if it's not yet available. e.g: TLS6
status = pjsua_transport_create(PJSIP_TRANSPORT_TLS6,
&tp_cfg, &transport_id);
...
// For PJSIP earlier than 2.14
// Bind account to IPv6 transport
// pjsua_acc_set_transport(acc_id, transport_id);
// Modify account configuration
pjsua_acc_get_config(acc_id, app_config.pool, &acc_cfg);
// ******************************************************
// ** For PJSIP 2.14 and above:
acc_cfg.ipv6_sip_use = PJSUA_IPV6_ENABLED_USE_IPV6_ONLY;
acc_cfg.ipv6_media_use = PJSUA_IPV6_ENABLED_USE_IPV6_ONLY;
// ** For PJSIP earlier than 2.14:
// acc_cfg.ipv6_media_use = PJSUA_IPV6_ENABLED;
// ******************************************************
// acc_cfg.ip_change_cfg.hangup_calls = PJ_TRUE;
// Available since #3910, prevents pjsua_acc_modify() from
// prematurely sending a REGISTER on the old (dead) transport.
acc_cfg.disable_reg_on_modify = PJ_TRUE;
pjsua_acc_modify(acc_id, &acc_cfg);
...
// Handle ip change
pjsua_ip_change_param_default(¶m);
pjsua_handle_ip_change(param);
}
PJSUA2 (keep your own AccountConfig around since the class doesn’t
expose a getter):
void ip_change_to_ip6(Account *acc, AccountConfig &acc_cfg)
{
// Create new IPv6 transport if needed; e.g. TLS6
Endpoint &ep = Endpoint::instance();
TransportConfig tp_cfg;
ep.transportCreate(PJSIP_TRANSPORT_TLS6, tp_cfg);
acc_cfg.sipConfig.ipv6Use = PJSUA_IPV6_ENABLED_USE_IPV6_ONLY;
acc_cfg.mediaConfig.ipv6Use = PJSUA_IPV6_ENABLED_USE_IPV6_ONLY;
// Prevent modify() from sending a REGISTER on the old transport.
acc_cfg.regConfig.disableRegOnModify = true;
acc->modify(acc_cfg);
IpChangeParam param;
ep.handleIpChange(param);
}
Note
The example forces USE_IPV6_ONLY to tear down existing IPv4
state entirely. If you set ipv6_sip_use = PREFER_IPV6 instead,
the account is dual-stack and existing calls that were negotiated
over IPv4 continue to run over IPv4 — the new preference only
affects subsequent outgoing offers/requests. Choose
USE_IPV6_ONLY when the old family is truly gone.
IP address change detection
iOS
Have a look at Reachability API.
Android
Have a look at ConnectivityManager.